This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit fb50ff08264786acae393af351de8e4f40d73113 Author: Marco Casaroli <[email protected]> AuthorDate: Wed Oct 28 22:07:00 2026 +0100 net/inet: Support MCAST_JOIN_GROUP and MCAST_LEAVE_GROUP. Join and leave a multicast group with the RFC 3678 options. At the level IPPROTO_IP the group is AF_INET and the options use the code of IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP. At the level IPPROTO_IPV6 the group is AF_INET6 and the options use the code of IPV6_JOIN_GROUP and IPV6_LEAVE_GROUP. gr_interface is an interface index; 0 selects the default device. As on Linux, a short option length gives EINVAL, and a group of the other family gives EINVAL at IPPROTO_IP and EADDRNOTAVAIL at IPPROTO_IPV6. IGMP is version 2 and MLD keeps no source lists, so the source options MCAST_BLOCK_SOURCE, MCAST_UNBLOCK_SOURCE, MCAST_JOIN_SOURCE_GROUP and MCAST_LEAVE_SOURCE_GROUP give ENOSYS, as IP_BLOCK_SOURCE and the other IPv4 source options do. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <[email protected]> --- net/inet/ipv4_setsockopt.c | 94 ++++++++++++++++++++++++++++++++-------------- net/inet/ipv6_setsockopt.c | 40 +++++++++++++++++++- 2 files changed, 105 insertions(+), 29 deletions(-) diff --git a/net/inet/ipv4_setsockopt.c b/net/inet/ipv4_setsockopt.c index 5b2179a7cd2..13b2a654770 100644 --- a/net/inet/ipv4_setsockopt.c +++ b/net/inet/ipv4_setsockopt.c @@ -132,21 +132,55 @@ int ipv4_setsockopt(FAR struct socket *psock, int option, #ifdef NET_UDP_HAVE_STACK case IP_ADD_MEMBERSHIP: /* Join a multicast group */ case IP_DROP_MEMBERSHIP: /* Leave a multicast group */ + case MCAST_JOIN_GROUP: /* Join a multicast group (RFC 3678) */ + case MCAST_LEAVE_GROUP: /* Leave a multicast group (RFC 3678) */ { - FAR const struct ip_mreq *mrec; + FAR struct udp_conn_s *conn = psock->s_conn; FAR struct net_driver_s *dev; + struct in_addr grpaddr; /* REVISIT: This is not a proper implementation of IP_MSGFILTER */ - mrec = (FAR const struct ip_mreq *)value; - if (mrec == NULL || value_len < sizeof(struct ip_mreq)) + if (option == MCAST_JOIN_GROUP || option == MCAST_LEAVE_GROUP) { - nerr("ERROR: Bad value or value_len\n"); - ret = -EINVAL; + FAR const struct group_req *greq = + (FAR const struct group_req *)value; + + if (greq == NULL || value_len < sizeof(struct group_req) || + greq->gr_group.ss_family != AF_INET) + { + nerr("ERROR: Bad value or value_len\n"); + ret = -EINVAL; + break; + } + + grpaddr = ((FAR const struct sockaddr_in *) + &greq->gr_group)->sin_addr; + + /* Use the default network device if gr_interface is 0 */ + + if (greq->gr_interface == 0) + { + dev = netdev_default(); + } + else + { + dev = netdev_findbyindex(greq->gr_interface); + } } else { - FAR struct udp_conn_s *conn = psock->s_conn; + FAR const struct ip_mreq *mrec = + (FAR const struct ip_mreq *)value; + + if (mrec == NULL || value_len < sizeof(struct ip_mreq)) + { + nerr("ERROR: Bad value or value_len\n"); + ret = -EINVAL; + break; + } + + grpaddr = mrec->imr_multiaddr; /* Use the default network device is imr_interface is * INADDRY_ANY. @@ -164,38 +198,38 @@ int ipv4_setsockopt(FAR struct socket *psock, int option, dev = netdev_findby_lipv4addr(mrec->imr_interface.s_addr); } + } - if (dev == NULL) + if (dev == NULL) + { + nwarn("WARNING: Could not find device\n"); + ret = -ENODEV; + } + else if (option == IP_ADD_MEMBERSHIP || option == MCAST_JOIN_GROUP) + { + if (conn->mreq.imr_multiaddr.s_addr != 0) { - nwarn("WARNING: Could not find device\n"); - ret = -ENODEV; - } - else if (option == IP_ADD_MEMBERSHIP) - { - 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; - } - } + ret = -EADDRINUSE; } else { - ret = igmp_leavegroup(dev, &mrec->imr_multiaddr); + ret = igmp_joingroup(dev, &grpaddr); if (ret == OK) { - conn->mreq.imr_multiaddr.s_addr = 0; - conn->mreq.imr_ifindex = 0; + conn->mreq.imr_multiaddr = grpaddr; + conn->mreq.imr_ifindex = dev->d_ifindex; } } } + else + { + ret = igmp_leavegroup(dev, &grpaddr); + if (ret == OK) + { + conn->mreq.imr_multiaddr.s_addr = 0; + conn->mreq.imr_ifindex = 0; + } + } } break; @@ -320,6 +354,10 @@ int ipv4_setsockopt(FAR struct socket *psock, int option, case IP_MULTICAST_ALL: /* Modify the delivery policy of * multicast messages bound to * INADDR_ANY */ + case MCAST_BLOCK_SOURCE: /* Same as IP_BLOCK_SOURCE */ + case MCAST_UNBLOCK_SOURCE: /* Same as IP_UNBLOCK_SOURCE */ + case MCAST_JOIN_SOURCE_GROUP: /* Same as IP_ADD_SOURCE_MEMBERSHIP */ + case MCAST_LEAVE_SOURCE_GROUP: /* Same as IP_DROP_SOURCE_MEMBERSHIP */ /* #warning Missing logic */ diff --git a/net/inet/ipv6_setsockopt.c b/net/inet/ipv6_setsockopt.c index 881c508f1e2..d08958855ff 100644 --- a/net/inet/ipv6_setsockopt.c +++ b/net/inet/ipv6_setsockopt.c @@ -99,6 +99,40 @@ int ipv6_setsockopt(FAR struct socket *psock, int option, ret = mld_leavegroup(value); break; + case MCAST_JOIN_GROUP: /* Join a multicast group (RFC 3678) */ + case MCAST_LEAVE_GROUP: /* Leave a multicast group (RFC 3678) */ + { + FAR const struct group_req *greq = + (FAR const struct group_req *)value; + struct ipv6_mreq mrec; + + if (value_len < sizeof(struct group_req)) + { + ret = -EINVAL; + break; + } + + if (greq->gr_group.ss_family != AF_INET6) + { + ret = -EADDRNOTAVAIL; + break; + } + + mrec.ipv6mr_multiaddr = ((FAR const struct sockaddr_in6 *) + &greq->gr_group)->sin6_addr; + mrec.ipv6mr_interface = greq->gr_interface; + + if (option == MCAST_JOIN_GROUP) + { + ret = mld_joingroup(&mrec); + } + else + { + ret = mld_leavegroup(&mrec); + } + } + break; + case IPV6_MULTICAST_HOPS: /* Multicast hop limit */ { FAR struct socket_conn_s *conn = psock->s_conn; @@ -142,10 +176,14 @@ int ipv6_setsockopt(FAR struct socket *psock, int option, break; } #endif /* NET_UDP_HAVE_STACK */ -#endif /* CONFIG_NET_MLD */ /* The following IPv6 socket options are defined, but not implemented */ + case MCAST_BLOCK_SOURCE: + case MCAST_UNBLOCK_SOURCE: + case MCAST_JOIN_SOURCE_GROUP: + case MCAST_LEAVE_SOURCE_GROUP: +#endif /* CONFIG_NET_MLD */ case IPV6_V6ONLY: /* Restrict AF_INET6 socket to IPv6 * communications only */ nwarn("WARNING: Unimplemented IPv6 option: %d\n", option);
