From 6546c316644dd1c04088242f3274ef6a1579e3c4 Mon Sep 17 00:00:00 2001
From: wanfeng <wanfeng@kylinos.cn>
Date: Fri, 28 Jun 2024 10:56:51 +0800
Subject: [PATCH] add MCAST_MSFILTER to setsockopt for IGMPv3

---
 src/api/sockets.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 137 insertions(+)

diff --git a/src/api/sockets.c b/src/api/sockets.c
index a030f4b..7a67650 100644
--- a/src/api/sockets.c
+++ b/src/api/sockets.c
@@ -3940,6 +3940,27 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_
           if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_RAW) {
             err = mcast_sock_block_unblock_source_group(&sock->conn->pcb.raw->ipmc, optname, (const struct group_source_req *)optval);
           } else
+#endif /* LWIP_RAW */
+          {
+            done_socket(sock);
+            return ENOPROTOOPT;
+          }
+          break;
+        case MCAST_MSFILTER:
+          LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, struct group_filter);
+          if (((const struct group_filter *)optval)->gf_group.ss_family != AF_INET) {
+            done_socket(sock);
+            return EINVAL;
+          }
+#if LWIP_UDP
+          if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_UDP) {
+            err = mcast_sock_set_groupfilter(&sock->conn->pcb.udp->ipmc, optname, (const struct group_filter *)optval);
+          } else
+#endif /* LWIP_UDP */
+#if LWIP_RAW
+          if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_RAW) {
+            err = mcast_sock_set_groupfilter(&sock->conn->pcb.raw->ipmc, optname, (const struct group_filter *)optval);
+          } else
 #endif /* LWIP_RAW */
           {
             done_socket(sock);
@@ -4729,6 +4750,78 @@ lwip_socket_drop_registered_mld6_memberships(int s)
 }
 #endif /* LWIP_IPV6_MLD */
 
+/** Set filter to a multicast group (group_filter)
+ *
+ * @param ipmc multicast filter control block
+ * @param netif the network interface which group we already join.
+ * @param multi_addr the address of the group to add source
+ * @param gf filter
+ * @return lwIP error definitions
+ */
+err_t
+mcast_set_groupfilter_netif(struct ip_mc *ipmc, struct netif *netif, const ip_addr_t *multi_addr, const struct group_filter *gf)
+{
+#if LWIP_IPV4 && LWIP_IGMP
+  if (IP_IS_V4(multi_addr)) {
+    struct ip4_mc *mc;
+    struct igmp_src *src, *head = NULL;
+    u32_t i;
+
+    mc = mcast_ip4_mc_find(ipmc, netif, ip_2_ip4(multi_addr), NULL);
+    if (mc == NULL) {
+      return ERR_VAL;
+    }
+    mc->num_src = 0;
+    for (i = 0; i < gf->gf_numsrc; i++) {
+      src = (struct igmp_src *)mem_malloc(sizeof(struct igmp_src));
+      if (src == NULL) {
+        mcast_ip4_mc_src_remove(head);
+        return ERR_MEM;
+      }
+      inet_addr_to_ip4addr(&src->src_addr, &(((struct sockaddr_in *)&(gf->gf_slist[i]))->sin_addr));
+      src->next = head;
+      head = src;
+      mc->num_src++;
+    }
+
+    mc->fmode = (u8_t)gf->gf_fmode;
+    mcast_ip4_mc_src_remove(mc->src);
+    mc->src = head;
+    IP4_MC_TRIGGER_CALL(netif, ip_2_ip4(multi_addr)); /* trigger a report */
+  } else
+#endif /* LWIP_IPV4 && LWIP_IGMP */
+  {
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+    struct ip6_mc *mc;
+    struct mld6_src *src, *head = NULL;
+    u32_t i;
+
+    mc = mcast_ip6_mc_find(ipmc, netif, ip_2_ip6(multi_addr), NULL);
+    if (mc == NULL) {
+      return ERR_VAL;
+    }
+    mc->num_src = 0;
+    for (i = 0; i < gf->gf_numsrc; i++) {
+      src = (struct mld6_src *)mem_malloc(sizeof(struct mld6_src));
+      if (src == NULL) {
+        mcast_ip6_mc_src_remove(head);
+        return ERR_MEM;
+      }
+      inet6_addr_to_ip6addr(&src->src_addr, &(((struct sockaddr_in6 *)&(gf->gf_slist[i]))->sin6_addr));
+      src->next = head;
+      head = src;
+      mc->num_src++;
+    }
+
+    mc->fmode = (u8_t)gf->gf_fmode;
+    mcast_ip6_mc_src_remove(mc->src);
+    mc->src = head;
+    IP6_MC_TRIGGER_CALL(netif, ip_2_ip6(multi_addr)); /* trigger a report */
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+  }
+  return ERR_OK;
+}
+
 /** Remove multicast filter when pcb remove
  *
  * @param ipmc multicast filter control block
@@ -5199,6 +5292,50 @@ mcast_sock_block_unblock_source_group(struct ip_mc *ipmc, int optname, const str
   return err_to_errno(err);
 }
 
+/**
+ * setsockopt() MCAST_MSFILTER command
+ */
+int
+mcast_sock_set_groupfilter(struct ip_mc *ipmc, int optname, const struct group_filter *gf)
+{
+  struct netif *netif;
+  ip_addr_t multi_addr;
+
+  LWIP_UNUSED_ARG(optname);
+
+  if (gf->gf_numsrc > LWIP_MCAST_SRC_TBL_SIZE) {
+    return ERR_VAL;
+  }
+  if (gf->gf_group.ss_family == AF_INET) {
+    inet_addr_to_ip4addr(ip_2_ip4(&multi_addr), &(((struct sockaddr_in *)&(gf->gf_group))->sin_addr));
+    IP_SET_TYPE_VAL(multi_addr, IPADDR_TYPE_V4);
+    if (!ip4_addr_ismulticast(ip_2_ip4(&multi_addr))) {
+      return EADDRNOTAVAIL;
+    }
+
+  } else if (gf->gf_group.ss_family == AF_INET6) {
+    inet6_addr_to_ip6addr(ip_2_ip6(&multi_addr), &(((struct sockaddr_in6 *)&(gf->gf_group))->sin6_addr));
+    IP_SET_TYPE_VAL(multi_addr, IPADDR_TYPE_V6);
+    if (!ip6_addr_ismulticast(ip_2_ip6(&multi_addr))) {
+      return EADDRNOTAVAIL;
+    }
+
+  } else {
+    return EADDRNOTAVAIL;
+  }
+
+  if (gf->gf_interface) {
+    netif = netif_get_by_index((u8_t)gf->gf_interface);
+  } else {
+    netif = netif_default; /* To default network interface */
+  }
+  if (netif == NULL) {
+    return ENXIO;
+  }
+
+  return err_to_errno(mcast_set_groupfilter_netif(ipmc, netif, &multi_addr, gf));
+}
+
 /**
  * setsockopt() IPV6_JOIN_GROUP / IPV6_LEAVE_GROUP command
  */
-- 
2.25.1

