From 50833670dbebaa12337b3ce909ab95c55d5d8eca Mon Sep 17 00:00:00 2001
From: wanfeng <wanfeng@kylinos.cn>
Date: Wed, 19 Jun 2024 17:04:35 +0800
Subject: [PATCH] add IP_ADD_SOURCE_MEMBERSHIP to setsockopt for igmpv3

modify ip_mreq_source

add IP_ADD_SOURCE_MEMBERSHIP to setsockopt for igmpv3

add IP_ADD_SOURCE_MEMBERSHIP to setsockopt for igmpv3
---
 src/api/sockets.c          | 93 ++++++++++++++++++++++++++++++++++++++
 src/include/lwip/sockets.h |  2 +-
 2 files changed, 94 insertions(+), 1 deletion(-)

diff --git a/src/api/sockets.c b/src/api/sockets.c
index 5a72b62..a030f4b 100644
--- a/src/api/sockets.c
+++ b/src/api/sockets.c
@@ -3859,6 +3859,24 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
             return ENOPROTOOPT;
            }
 	break;
+        case IP_ADD_SOURCE_MEMBERSHIP:
+        case IP_DROP_SOURCE_MEMBERSHIP:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, struct ip_mreq_source);
+#if LWIP_UDP
+          if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_UDP) {
+            err = mcast_sock_add_drop_source_membership(s, &sock->conn->pcb.udp->ipmc, optname, (const struct ip_mreq_source *)optval);
+          } else
+#endif /* LWIP_UDP */
+#if LWIP_RAW
+          if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_RAW) {
+            err = mcast_sock_add_drop_source_membership(s, &sock->conn->pcb.raw->ipmc, optname, (const struct ip_mreq_source *)optval);
+          } else
+#endif /* LWIP_RAW */
+          {
+            done_socket(sock);
+            return ENOPROTOOPT;
+          }
+          break;
 	case MCAST_JOIN_GROUP:
         case MCAST_LEAVE_GROUP: {
           LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, struct group_req);
@@ -4907,6 +4925,81 @@ out:
   return (u8_t)err_to_errno(err);
 }
 
+/**
+ * setsockopt() IP_ADD_SOURCE_MEMBERSHIP / IP_DROP_SOURCE_MEMBERSHIP command
+ */
+int
+mcast_sock_add_drop_source_membership(int s, struct ip_mc *ipmc, int optname, const struct ip_mreq_source *imr)
+{
+  struct netif *netif;
+  ip_addr_t if_addr;
+  ip_addr_t multi_addr;
+  ip_addr_t src_addr;
+  err_t err;
+  u8_t if_idx;
+
+  inet_addr_to_ip4addr(ip_2_ip4(&if_addr), &imr->imr_interface);
+  inet_addr_to_ip4addr(ip_2_ip4(&multi_addr), &imr->imr_multiaddr);
+  inet_addr_to_ip4addr(ip_2_ip4(&src_addr), &imr->imr_sourceaddr);
+  IP_SET_TYPE_VAL(if_addr, IPADDR_TYPE_V4);
+  IP_SET_TYPE_VAL(multi_addr, IPADDR_TYPE_V4);
+  IP_SET_TYPE_VAL(src_addr, IPADDR_TYPE_V4);
+
+  if (!ip4_addr_ismulticast(ip_2_ip4(&multi_addr)) || ip4_addr_isany(ip_2_ip4(&src_addr))) {
+    return EADDRNOTAVAIL;
+  }
+
+  if (ip4_addr_isany(ip_2_ip4(&if_addr))) { /* To all network interface */
+    if (optname == IP_ADD_SOURCE_MEMBERSHIP) {
+      NETIF_FOREACH(netif) {
+        if_idx = netif_get_index(netif);
+        if (!lwip_socket_register_membership(s, if_idx, ip_2_ip4(&multi_addr))) {
+          /* cannot track membership (out of memory) */
+          err = ENOMEM;
+          goto out;
+        }
+      }
+      err = mcast_join_group(ipmc, &if_addr, &multi_addr, &src_addr);
+    } else {
+      err = mcast_leave_group(ipmc, &if_addr, &multi_addr, &src_addr);
+    }
+
+  } else { /* To specified network interface */
+    if (!(ip_2_ip4(&if_addr)->addr & PP_HTONL(IP_CLASSA_NET))) { /* IS a BSD style index ? */
+      u8_t idx = (u8_t)PP_NTOHL(ip_2_ip4(&if_addr)->addr);
+      netif = netif_get_by_index(idx);
+      if (netif == NULL) {
+        return ENXIO;
+      }
+      *ip_2_ip4(&if_addr) = *netif_ip4_addr(netif);
+
+    } else {
+      NETIF_FOREACH(netif) {
+        if (ip4_addr_cmp(ip_2_ip4(&if_addr), netif_ip4_addr(netif))) {
+          break;
+        }
+      }
+      if (netif == NULL) {
+        return ENXIO;
+      }
+    }
+
+    if (optname == IP_ADD_SOURCE_MEMBERSHIP) {
+      if_idx = netif_get_index(netif);
+      if (!lwip_socket_register_membership(s, if_idx, ip_2_ip4(&multi_addr))) {
+        /* cannot track membership (out of memory) */
+        err = ENOMEM;
+        goto out;
+      }
+      err = mcast_join_netif(ipmc, netif, &multi_addr, &src_addr);
+    } else {
+      err = mcast_leave_netif(ipmc, netif, &multi_addr, &src_addr);
+    }
+  }
+out:
+  return err_to_errno(err);
+}
+
 /**
  * setsockopt() MCAST_JOIN_GROUP / MCAST_LEAVE_GROUP command
  */
diff --git a/src/include/lwip/sockets.h b/src/include/lwip/sockets.h
index f2a1b59..038afba 100644
--- a/src/include/lwip/sockets.h
+++ b/src/include/lwip/sockets.h
@@ -785,8 +785,8 @@ typedef struct ip_mreq {
  */
 struct ip_mreq_source {
   struct in_addr imr_multiaddr;   /* IP multicast address of group */
-  struct in_addr imr_sourceaddr;  /* IP address of source */
   struct in_addr imr_interface;   /* local IP address of interface */
+  struct in_addr imr_sourceaddr;  /* IP address of source */
 };
 
 #define IP_MSFILTER_SIZE(numsrc) \
-- 
2.25.1

