diff --git a/net/igmp/igmp.h b/net/igmp/igmp.h index 1f107172683..7fac4c9d361 100644 --- a/net/igmp/igmp.h +++ b/net/igmp/igmp.h @@ -117,6 +117,7 @@ struct igmp_group_s uint8_t ifindex; /* Interface index */ uint8_t flags; /* See IGMP_ flags definitions */ uint8_t msgid; /* Pending message ID (if non-zero) */ + uint8_t njoins; /* Number of joins from this host */ }; /**************************************************************************** diff --git a/net/igmp/igmp_join.c b/net/igmp/igmp_join.c index c28b10112a8..32d1e840d56 100644 --- a/net/igmp/igmp_join.c +++ b/net/igmp/igmp_join.c @@ -160,12 +160,12 @@ int igmp_joingroup(struct net_driver_s *dev, /* Add the group (MAC) address to the ether drivers MAC filter list */ igmp_addmcastmac(dev, (FAR in_addr_t *)&grpaddr->s_addr); - return OK; } - /* Return EEXIST if the address is already a member of the group */ + DEBUGASSERT(group->njoins < UINT8_MAX); + group->njoins++; - return -EEXIST; + return OK; } #endif /* CONFIG_NET_IGMP */ diff --git a/net/igmp/igmp_leave.c b/net/igmp/igmp_leave.c index e026340832d..62c57edbcba 100644 --- a/net/igmp/igmp_leave.c +++ b/net/igmp/igmp_leave.c @@ -138,6 +138,18 @@ int igmp_leavegroup(struct net_driver_s *dev, ninfo("Leaving group: %p\n", group); if (group) { + DEBUGASSERT(group->njoins > 0); + group->njoins--; + + /* Take no further actions if there are other members of this group + * on this host. + */ + + if (group->njoins > 0) + { + return OK; + } + /* Cancel the timer and discard any queued Membership Reports. * Canceling the timer will prevent any new Membership Reports from * being sent; clearing the flags will discard any pending Membership diff --git a/net/inet/ipv4_setsockopt.c b/net/inet/ipv4_setsockopt.c index 7553cdf1344..741e6168cd2 100644 --- a/net/inet/ipv4_setsockopt.c +++ b/net/inet/ipv4_setsockopt.c @@ -126,6 +126,7 @@ int ipv4_setsockopt(FAR struct socket *psock, int option, } break; +#ifdef NET_UDP_HAVE_STACK case IP_ADD_MEMBERSHIP: /* Join a multicast group */ case IP_DROP_MEMBERSHIP: /* Leave a multicast group */ { @@ -142,6 +143,8 @@ int ipv4_setsockopt(FAR struct socket *psock, int option, } else { + FAR struct udp_conn_s *conn = psock->s_conn; + /* Use the default network device is imr_interface is * INADDRY_ANY. */ @@ -166,17 +169,33 @@ int ipv4_setsockopt(FAR struct socket *psock, int option, } else if (option == IP_ADD_MEMBERSHIP) { - ret = igmp_joingroup(dev, &mrec->imr_multiaddr); + if (conn->mreq.imr_multiaddr.s_addr != 0) + { + ret = -EADDRINUSE; + } + else + { + ret = igmp_joingroup(dev, &mrec->imr_multiaddr); + if (ret == OK) + { + conn->mreq.imr_multiaddr = mrec->imr_multiaddr; + conn->mreq.imr_ifindex = dev->d_ifindex; + } + } } else { ret = igmp_leavegroup(dev, &mrec->imr_multiaddr); + if (ret == OK) + { + conn->mreq.imr_multiaddr.s_addr = 0; + conn->mreq.imr_ifindex = 0; + } } } } break; -#ifdef NET_UDP_HAVE_STACK case IP_MULTICAST_TTL: /* Set/read the time-to-live value of * outgoing multicast packets */ #endif diff --git a/net/netdev/netdev_ioctl.c b/net/netdev/netdev_ioctl.c index a7401460fa5..d1eea0c40e7 100644 --- a/net/netdev/netdev_ioctl.c +++ b/net/netdev/netdev_ioctl.c @@ -1207,6 +1207,8 @@ static int netdev_imsf_ioctl(FAR struct socket *psock, int cmd, ninfo("cmd: %d\n", cmd); + net_lock(); + /* Execute the command */ switch (cmd) @@ -1235,6 +1237,7 @@ static int netdev_imsf_ioctl(FAR struct socket *psock, int cmd, break; } + net_unlock(); return ret; } #endif diff --git a/net/udp/udp.h b/net/udp/udp.h index 354aefb8ea0..3832ec5a0a4 100644 --- a/net/udp/udp.h +++ b/net/udp/udp.h @@ -310,6 +310,28 @@ int udp_bind(FAR struct udp_conn_s *conn, FAR const struct sockaddr *addr); int udp_connect(FAR struct udp_conn_s *conn, FAR const struct sockaddr *addr); +#if defined(CONFIG_NET_IGMP) +/**************************************************************************** + * Name: udp_leavegroup + * + * Description: + * This function leaves the multicast group to which the conn belongs. + * + * Input Parameters: + * conn - A reference to UDP connection structure. A value of NULL will + * disconnect from any previously connected address. + * + * Assumptions: + * This function is called (indirectly) from user code. Interrupts may + * be enabled. + * + ****************************************************************************/ + +void udp_leavegroup(FAR struct udp_conn_s *conn); +#else +#define udp_leavegroup(c) +#endif + /**************************************************************************** * Name: udp_close * diff --git a/net/udp/udp_close.c b/net/udp/udp_close.c index 0c2368a9ab1..42ade8f897b 100644 --- a/net/udp/udp_close.c +++ b/net/udp/udp_close.c @@ -103,6 +103,8 @@ int udp_close(FAR struct socket *psock) nerr("ERROR: udp_txdrain() failed: %d\n", ret); } + udp_leavegroup(conn); + #ifdef CONFIG_NET_UDP_WRITE_BUFFERS /* Free any semi-permanent write buffer callback in place. */ diff --git a/net/udp/udp_conn.c b/net/udp/udp_conn.c index 6092584c33d..2bb4f64b228 100644 --- a/net/udp/udp_conn.c +++ b/net/udp/udp_conn.c @@ -68,6 +68,7 @@ #include "nat/nat.h" #include "netdev/netdev.h" #include "socket/socket.h" +#include "igmp/igmp.h" #include "udp/udp.h" /**************************************************************************** @@ -1080,4 +1081,35 @@ int udp_connect(FAR struct udp_conn_s *conn, FAR const struct sockaddr *addr) return OK; } +#if defined(CONFIG_NET_IGMP) +/**************************************************************************** + * Name: udp_leavegroup + * + * Description: + * This function leaves the multicast group to which the conn belongs. + * + * Input Parameters: + * conn - A reference to UDP connection structure. A value of NULL will + * disconnect from any previously connected address. + * + * Assumptions: + * This function is called (indirectly) from user code. Interrupts may + * be enabled. + * + ****************************************************************************/ + +void udp_leavegroup(FAR struct udp_conn_s *conn) +{ + if (conn->mreq.imr_multiaddr.s_addr != 0) + { + FAR struct net_driver_s *dev; + + if ((dev = netdev_findbyindex(conn->mreq.imr_ifindex)) != NULL) + { + igmp_leavegroup(dev, &conn->mreq.imr_multiaddr); + } + } +} +#endif + #endif /* CONFIG_NET && CONFIG_NET_UDP */