From 310fa748277fb768b54d03b4efc9d72c782649dc Mon Sep 17 00:00:00 2001 From: wanfeng Date: Mon, 8 Apr 2024 11:05:21 +0800 Subject: [PATCH] support mldv2 (cherry picked from commit 7947f444bad18e8aee16530ec3c2f833e7527f97) --- 0122-support-mldv2.patch | 1867 ++++++++++++++++++++++++++++++++++++++ lwip.spec | 6 +- 2 files changed, 1872 insertions(+), 1 deletion(-) create mode 100644 0122-support-mldv2.patch diff --git a/0122-support-mldv2.patch b/0122-support-mldv2.patch new file mode 100644 index 0000000..3a92755 --- /dev/null +++ b/0122-support-mldv2.patch @@ -0,0 +1,1867 @@ +From bd5283f4d24be4b982652b38a680264b4a3efdf4 Mon Sep 17 00:00:00 2001 +From: wanfeng +Date: Mon, 8 Apr 2024 10:31:20 +0800 +Subject: [PATCH] support mldv2 + +--- + src/api/api_msg.c | 60 +++-- + src/api/sockets.c | 262 +++++++++++++++++--- + src/core/ipv6/icmp6.c | 3 + + src/core/ipv6/mld6.c | 360 +++++++++++++++++++++++++++- + src/core/mcast.c | 438 +++++++++++++++++++++++++++++++++- + src/core/udp.c | 12 +- + src/include/lwip/api.h | 1 + + src/include/lwip/mcast.h | 35 +++ + src/include/lwip/mld6.h | 30 +++ + src/include/lwip/netbuf.h | 3 + + src/include/lwip/opt.h | 8 + + src/include/lwip/prot/icmp6.h | 2 + + src/include/lwip/prot/mld6.h | 79 ++++++ + src/include/lwip/sockets.h | 45 +++- + src/include/lwipopts.h | 5 + + 15 files changed, 1249 insertions(+), 94 deletions(-) + +diff --git a/src/api/api_msg.c b/src/api/api_msg.c +index 7cae567..f0a3e0b 100644 +--- a/src/api/api_msg.c ++++ b/src/api/api_msg.c +@@ -202,7 +202,23 @@ recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p, + if (IP_IS_V4(addr)) { + struct ip_hdr *iphdr = (struct ip_hdr *)p->payload; + ip_addr_copy_from_ip4(buf->toaddr, iphdr->dest); +- } else {} ++ } else ++#if LWIP_IPV6 ++ { ++ struct ip6_hdr *ip6hdr = (struct ip6_hdr *)p->payload; ++ ip_addr_copy_from_ip6_packed(buf->toaddr, ip6hdr->dest); ++ } ++#endif /* LWIP_IPV6 */ ++ } ++#if LWIP_IPV6 ++ if (conn->flags & NETCONN_FLAG_HOPLIM) { /* IPV6_HOPLIMIT */ ++ if (IP_IS_V6(addr)) { ++ struct ip6_hdr *ip6hdr = (struct ip6_hdr *)p->payload; ++ buf->flags |= NETBUF_FLAG_HOPLIM; /* IPV6_RECVHOPLIMIT (Hop limit) */ ++ buf->hoplim = IP6H_HOPLIM(ip6hdr); ++ } ++ } ++#endif /* LWIP_IPV6 */ + #endif /* LWIP_NETBUF_RECVINFO */ + + len = q->tot_len; +@@ -290,6 +306,15 @@ recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p, + ip_addr_set(&buf->toaddr, ip_current_dest_addr()); + buf->toport_chksum = udphdr->dest; + } ++#if LWIP_IPV6 ++ if (conn->flags & NETCONN_FLAG_HOPLIM) { /* IPV6_HOPLIMIT */ ++ if (IP_IS_V6(addr)) { ++ struct ip6_hdr *ip6hdr = (struct ip6_hdr *)ip6_current_header(); ++ buf->flags |= NETBUF_FLAG_HOPLIM; /* IPV6_RECVHOPLIMIT (Hop limit) */ ++ buf->hoplim = IP6H_HOPLIM(ip6hdr); ++ } ++ } ++#endif /* LWIP_IPV6 */ + #endif /* LWIP_NETBUF_RECVINFO */ + } + +@@ -2084,25 +2109,11 @@ lwip_netconn_do_join_leave_group(void *m) + #if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) { + #if LWIP_UDP +-#if LWIP_IPV6 && LWIP_IPV6_MLD +- if (NETCONNTYPE_ISIPV6(msg->conn->type)) { +- if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { +- msg->err = mld6_joingroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)), +- ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr))); +- } else { +- msg->err = mld6_leavegroup(ip_2_ip6(API_EXPR_REF(msg->msg.jl.netif_addr)), +- ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr))); +- } +- } else { +-#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ +-#if LWIP_IGMP + if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { + msg->err = mcast_join_group(&msg->conn->pcb.udp->ipmc, API_EXPR_REF(msg->msg.jl.netif_addr), API_EXPR_REF(msg->msg.jl.multiaddr), NULL); + } else { + msg->err = mcast_leave_group(&msg->conn->pcb.udp->ipmc, API_EXPR_REF(msg->msg.jl.netif_addr), API_EXPR_REF(msg->msg.jl.multiaddr), NULL); + } +-#endif /*LWIP_IGMP*/ +- } + #else + msg->err = ERR_VAL; + #endif /* LWIP_UDP */ +@@ -2146,30 +2157,15 @@ lwip_netconn_do_join_leave_group_netif(void *m) + #if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) + if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) { + #if LWIP_UDP +-#if LWIP_IPV6 && LWIP_IPV6_MLD +- if (NETCONNTYPE_ISIPV6(msg->conn->type)) { +- if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { +- msg->err = mld6_joingroup_netif(netif, +- ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr))); +- } else { +- msg->err = mld6_leavegroup_netif(netif, +- ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr))); +- } +- } else +-#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ +- { +-#if LWIP_IGMP +- if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { ++ if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { + msg->err = mcast_join_netif(&msg->conn->pcb.udp->ipmc, netif, API_EXPR_REF(msg->msg.jl.multiaddr), NULL); + } else { + msg->err = mcast_leave_netif(&msg->conn->pcb.udp->ipmc, netif, API_EXPR_REF(msg->msg.jl.multiaddr), NULL); + } +-#endif /*LWIP_IGMP*/ + #else + msg->err = ERR_VAL; + #endif /* LWIP_UDP */ +- } +- } else if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_RAW) { ++ } else if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_RAW) { + #if LWIP_RAW + if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { + msg->err = mcast_join_netif(&msg->conn->pcb.raw->ipmc, netif, API_EXPR_REF(msg->msg.jl.multiaddr), NULL); +diff --git a/src/api/sockets.c b/src/api/sockets.c +index 0caebdd..6b2f5ee 100644 +--- a/src/api/sockets.c ++++ b/src/api/sockets.c +@@ -3936,34 +3936,43 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_ + #if LWIP_IPV6_MLD + case IPV6_JOIN_GROUP: + case IPV6_LEAVE_GROUP: { +- /* If this is a TCP or a RAW socket, ignore these options. */ +- err_t mld6_err; +- struct netif *netif; +- ip6_addr_t multi_addr; +- const struct ipv6_mreq *imr = (const struct ipv6_mreq *)optval; +- LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ipv6_mreq, NETCONN_UDP); +- inet6_addr_to_ip6addr(&multi_addr, &imr->ipv6mr_multiaddr); +- LWIP_ASSERT("Invalid netif index", imr->ipv6mr_interface <= 0xFFu); +- netif = netif_get_by_index((u8_t)imr->ipv6mr_interface); +- if (netif == NULL) { +- err = EADDRNOTAVAIL; +- break; ++ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, struct ipv6_mreq); ++#if LWIP_UDP ++ if (NETCONNTYPE_GROUPV6(netconn_type(sock->conn)) == NETCONN_UDP_IPV6) { ++ err = mcast_sock_ipv6_add_drop_membership(s, &sock->conn->pcb.udp->ipmc, optname, (const struct ipv6_mreq *)optval); ++ } else ++#endif /* LWIP_UDP */ ++#if LWIP_RAW ++ if (NETCONNTYPE_GROUPV6(netconn_type(sock->conn)) == NETCONN_RAW_IPV6) { ++ err = mcast_sock_ipv6_add_drop_membership(s, &sock->conn->pcb.raw->ipmc, optname, (const struct ipv6_mreq *)optval); ++ } else ++#endif /* LWIP_RAW */ ++ { ++ done_socket(sock); ++ return ENOPROTOOPT; + } +- +- if (optname == IPV6_JOIN_GROUP) { +- if (!lwip_socket_register_mld6_membership(s, imr->ipv6mr_interface, &multi_addr)) { +- /* cannot track membership (out of memory) */ +- err = ENOMEM; +- mld6_err = ERR_OK; +- } else { +- mld6_err = mld6_joingroup_netif(netif, &multi_addr); +- } +- } else { +- mld6_err = mld6_leavegroup_netif(netif, &multi_addr); +- lwip_socket_unregister_mld6_membership(s, imr->ipv6mr_interface, &multi_addr); ++ } ++ break; ++ case MCAST_JOIN_GROUP: ++ case MCAST_LEAVE_GROUP: { ++ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, struct group_req); ++ if (((const struct group_req *)optval)->gr_group.ss_family != AF_INET6) { ++ done_socket(sock); ++ return EINVAL; + } +- if (mld6_err != ERR_OK) { +- err = EADDRNOTAVAIL; ++#if LWIP_UDP ++ if (NETCONNTYPE_GROUPV6(netconn_type(sock->conn)) == NETCONN_UDP_IPV6) { ++ err = mcast_sock_join_leave_group(s, &sock->conn->pcb.udp->ipmc, optname, (const struct group_req *)optval); ++ } else ++#endif /* LWIP_UDP */ ++#if LWIP_RAW ++ if (NETCONNTYPE_GROUPV6(netconn_type(sock->conn)) == NETCONN_RAW_IPV6) { ++ err = mcast_sock_join_leave_group(s, &sock->conn->pcb.raw->ipmc, optname, (const struct group_req *)optval); ++ } else ++#endif /* LWIP_RAW */ ++ { ++ done_socket(sock); ++ return ENOPROTOOPT; + } + } + break; +@@ -4486,17 +4495,13 @@ lwip_socket_register_mld6_membership(int s, unsigned int if_idx, const ip6_addr_ + if (!sock) { + return 0; + } +- + SYS_ARCH_DECL_PROTECT(lev); + SYS_ARCH_PROTECT(lev); ++#if GAZELLE_ENABLE ++ i = s; ++#else + for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) { +- if ((socket_ipv6_multicast_memberships[i].sock == sock) && +- (socket_ipv6_multicast_memberships[i].if_idx == if_idx) && +- ip6_addr_cmp(&socket_ipv6_multicast_memberships[i].multi_addr, multi_addr)) { +- done_socket(sock); +- return 1; +- } +- ++#endif + if (socket_ipv6_multicast_memberships[i].sock == NULL) { + socket_ipv6_multicast_memberships[i].sock = sock; + socket_ipv6_multicast_memberships[i].if_idx = (u8_t)if_idx; +@@ -4504,7 +4509,9 @@ lwip_socket_register_mld6_membership(int s, unsigned int if_idx, const ip6_addr_ + done_socket(sock); + return 1; + } ++#if !GAZELLE_ENABLE + } ++#endif + SYS_ARCH_UNPROTECT(lev); + done_socket(sock); + return 0; +@@ -4524,19 +4531,26 @@ lwip_socket_unregister_mld6_membership(int s, unsigned int if_idx, const ip6_add + if (!sock) { + return; + } +- + SYS_ARCH_DECL_PROTECT(lev); + SYS_ARCH_PROTECT(lev); ++#if GAZELLE_ENABLE ++ i = s; ++#else + for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) { ++#endif + if ((socket_ipv6_multicast_memberships[i].sock == sock) && + (socket_ipv6_multicast_memberships[i].if_idx == if_idx) && +- ip6_addr_cmp(&socket_ipv6_multicast_memberships[i].multi_addr, multi_addr)) { ++ ip6_addr_cmp(&socket_ipv6_multicast_memberships[i].multi_addr, multi_addr)) { + socket_ipv6_multicast_memberships[i].sock = NULL; + socket_ipv6_multicast_memberships[i].if_idx = NETIF_NO_INDEX; + ip6_addr_set_zero(&socket_ipv6_multicast_memberships[i].multi_addr); ++#if !GAZELLE_ENABLE + break; ++#endif + } ++#if !GAZELLE_ENABLE + } ++#endif + SYS_ARCH_UNPROTECT(lev); + done_socket(sock); + } +@@ -4556,7 +4570,11 @@ lwip_socket_drop_registered_mld6_memberships(int s) + } + SYS_ARCH_DECL_PROTECT(lev); + SYS_ARCH_PROTECT(lev); ++#if GAZELLE_ENABLE ++ i = s; ++#else + for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) { ++#endif + if (socket_ipv6_multicast_memberships[i].sock == sock) { + ip_addr_t multi_addr; + u8_t if_idx; +@@ -4570,7 +4588,9 @@ lwip_socket_drop_registered_mld6_memberships(int s) + + netconn_join_leave_group_netif(sock->conn, &multi_addr, if_idx, NETCONN_LEAVE); + } ++#if !GAZELLE_ENABLE + } ++#endif + SYS_ARCH_UNPROTECT(lev); + done_socket(sock); + } +@@ -4597,6 +4617,25 @@ mcast_pcb_remove(struct ip_mc *ipmc) + } + } + #endif /* LWIP_IPV4 && LWIP_IGMP */ ++ ++#if LWIP_IPV6 && LWIP_IPV6_MLD ++ { ++ struct ip6_mc *mc, *next; ++ struct netif *netif; ++ ++ mc = ipmc->mc6; ++ while (mc) { ++ next = mc->next; ++ mcast_ip6_mc_src_remove(mc->src); ++ netif = netif_get_by_index(mc->if_idx); ++ if (netif) { ++ mld6_leavegroup_netif(netif, &mc->multi_addr); ++ } ++ mem_free(mc); ++ mc = next; ++ } ++ } ++#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + } + + /** Common code to see if the current input multicast packet matches the pcb +@@ -4637,8 +4676,38 @@ mcast_input_local_match(struct ip_mc *ipmc, struct netif *inp) + } + } + } +- } else {} ++ } else + #endif /* LWIP_IPV4 && LWIP_IGMP */ ++ { ++#if LWIP_IPV6 && LWIP_IPV6_MLD ++ struct mld6_src *src; ++ struct ip6_mc *mc; ++ ip6_addr_t *multi_addr = ip6_current_dest_addr(); ++ ip6_addr_t *src_addr = ip6_current_src_addr(); ++ ++ IP6_MC_FOREACH(ipmc, mc) { ++ if (((mc->if_idx == NETIF_NO_INDEX) || (mc->if_idx == netif_get_index(inp))) && ++ ip6_addr_cmp_zoneless(&mc->multi_addr, multi_addr)) { ++ if (mc->fmode == MCAST_EXCLUDE) { ++ IP6_MC_SRC_FOREACH(mc, src) { ++ if (ip6_addr_cmp_zoneless(&src->src_addr, src_addr)) { ++ return 0; ++ } ++ } ++ return 1; /* MCAST_EXCLUDE src_addr must not in src list */ ++ ++ } else { /* MCAST_INCLUDE */ ++ IP6_MC_SRC_FOREACH(mc, src) { ++ if (ip6_addr_cmp_zoneless(&src->src_addr, src_addr)) { ++ return 1; ++ } ++ } ++ return 0; /* MCAST_INCLUDE src_addr must in src list */ ++ } ++ } ++ } ++#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ ++ } + return 0; + } + +@@ -4722,6 +4791,125 @@ mcast_sock_add_drop_membership(int s, struct ip_mc *ipmc, int optname, const str + out: + return (u8_t)err_to_errno(err); + } ++ ++/** ++ * setsockopt() MCAST_JOIN_GROUP / MCAST_LEAVE_GROUP command ++ */ ++int ++mcast_sock_join_leave_group(int s, struct ip_mc *ipmc, int optname, const struct group_req *gr) ++{ ++ struct netif *netif; ++ ip_addr_t multi_addr; ++ err_t err; ++ u8_t if_idx; ++ ++ if (gr->gr_group.ss_family == AF_INET) { ++ inet_addr_to_ip4addr(ip_2_ip4(&multi_addr), &(((struct sockaddr_in *)&(gr->gr_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 (gr->gr_group.ss_family == AF_INET6) { ++ inet6_addr_to_ip6addr(ip_2_ip6(&multi_addr), &(((struct sockaddr_in6 *)&(gr->gr_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 (gr->gr_interface) { ++ netif = netif_get_by_index((u8_t)gr->gr_interface); ++ } else { ++ netif = netif_default; /* To default network interface */ ++ } ++ if (netif == NULL) { ++ return ENXIO; ++ } ++ ++ if_idx = netif_get_index(netif); ++ if (optname == MCAST_JOIN_GROUP) { ++#if LWIP_IPV4 && LWIP_IGMP ++ if (IP_IS_V4(&multi_addr)) { ++ if (!lwip_socket_register_membership(s, if_idx, ip_2_ip4(&multi_addr))) { ++ /* cannot track membership (out of memory) */ ++ err = ENOMEM; ++ goto out; ++ } ++ } else ++#endif ++ { ++#if LWIP_IPV6 && LWIP_IPV6_MLD ++ if (!lwip_socket_register_mld6_membership(s, if_idx, ip_2_ip6(&multi_addr))) { ++ /* cannot track membership (out of memory) */ ++ err = ENOMEM; ++ goto out; ++ } ++#endif ++ } ++ ++ err = mcast_join_netif(ipmc, netif, &multi_addr, NULL); ++ } else { ++ err = mcast_leave_netif(ipmc, netif, &multi_addr, NULL); ++#if LWIP_IPV4 && LWIP_IGMP ++ if (IP_IS_V4(&multi_addr)) { ++ lwip_socket_unregister_membership(s, if_idx, ip_2_ip4(&multi_addr)); ++ } else ++#endif ++ { ++#if LWIP_IPV6 && LWIP_IPV6_MLD ++ lwip_socket_unregister_mld6_membership(s, if_idx, ip_2_ip6(&multi_addr)); ++#endif ++ } ++ } ++out: ++ return err_to_errno(err); ++} ++ ++/** ++ * setsockopt() IPV6_JOIN_GROUP / IPV6_LEAVE_GROUP command ++ */ ++int ++mcast_sock_ipv6_add_drop_membership(int s, struct ip_mc *ipmc, int optname, const struct ipv6_mreq *imr) ++{ ++ struct netif *netif; ++ ip_addr_t multi_addr; ++ err_t err; ++ ++ inet6_addr_to_ip6addr(ip_2_ip6(&multi_addr), &imr->ipv6mr_multiaddr); ++ IP_SET_TYPE_VAL(multi_addr, IPADDR_TYPE_V6); ++ ++ if (!ip6_addr_ismulticast(ip_2_ip6(&multi_addr))) { ++ return EADDRNOTAVAIL; ++ } ++ ++ if (imr->ipv6mr_interface) { ++ netif = netif_get_by_index((u8_t)imr->ipv6mr_interface); ++ } else { ++ netif = netif_default; /* To default network interface */ ++ } ++ if (netif == NULL) { ++ return ENXIO; ++ } ++ ++ u8_t if_idx = netif_get_index(netif); ++ if (optname == IPV6_JOIN_GROUP) { ++ if (!lwip_socket_register_mld6_membership(s, if_idx, ip_2_ip6(&multi_addr))) { ++ /* cannot track membership (out of memory) */ ++ err = ENOMEM; ++ goto out; ++ } ++ err = mcast_join_netif(ipmc, netif, &multi_addr, NULL); ++ } else { ++ err = mcast_leave_netif(ipmc, netif, &multi_addr, NULL); ++ lwip_socket_unregister_mld6_membership(s, if_idx, ip_2_ip6(&multi_addr)); ++ } ++out: ++ return err_to_errno(err); ++} + #endif /* (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD)*/ + + #if GAZELLE_ENABLE +diff --git a/src/core/ipv6/icmp6.c b/src/core/ipv6/icmp6.c +index ed0bd7b..143cb20 100644 +--- a/src/core/ipv6/icmp6.c ++++ b/src/core/ipv6/icmp6.c +@@ -129,6 +129,9 @@ icmp6_input(struct pbuf *p, struct netif *inp) + case ICMP6_TYPE_MLQ: + case ICMP6_TYPE_MLR: + case ICMP6_TYPE_MLD: ++#if LWIP_IPV6_MLD_V2 ++ case ICMP6_TYPE_MLR2: ++#endif + mld6_input(p, inp); + return; + #endif +diff --git a/src/core/ipv6/mld6.c b/src/core/ipv6/mld6.c +index ac4fb01..29c45bc 100644 +--- a/src/core/ipv6/mld6.c ++++ b/src/core/ipv6/mld6.c +@@ -66,6 +66,7 @@ + #include "lwip/netif.h" + #include "lwip/memp.h" + #include "lwip/stats.h" ++#include "lwip/sockets.h" + + #include + +@@ -80,12 +81,22 @@ + #define MLD6_GROUP_DELAYING_MEMBER 1 + #define MLD6_GROUP_IDLE_MEMBER 2 + ++#define MLD6_MINLEN 12 ++#define MLD6_V2_MINLEN 16 ++ + /* Forward declarations. */ + static struct mld_group *mld6_new_group(struct netif *ifp, const ip6_addr_t *addr); + static err_t mld6_remove_group(struct netif *netif, struct mld_group *group); + static void mld6_delayed_report(struct mld_group *group, u16_t maxresp); + static void mld6_send(struct netif *netif, struct mld_group *group, u8_t type); ++static void mld6_timeout(struct netif *netif, struct mld_group *group); ++ + ++#if LWIP_IPV6_MLD_V2 ++static void mld6_v2_timeout(struct netif *netif, struct mld_group *group); ++static void mld6_v2_delayed_report(struct mld_group *group, u16_t maxresp_in); ++static void mld6_v2_send(struct netif *netif, struct mld_group *group, u8_t type); ++#endif + + /** + * Stop MLD processing on interface +@@ -128,6 +139,9 @@ mld6_report_groups(struct netif *netif) + + while (group != NULL) { + mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS); ++#if LWIP_IPV6_MLD_V2 ++ mld6_v2_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS); ++#endif + group = group->next; + } + } +@@ -177,6 +191,12 @@ mld6_new_group(struct netif *ifp, const ip6_addr_t *addr) + group->last_reporter_flag = 0; + group->use = 0; + group->next = netif_mld6_data(ifp); ++#if LWIP_IPV6_MLD_V2 ++ group->v2_fmode = MLD6_FMODE_INIT; ++ group->v2_last_reporter_flag = 0; ++ group->v2_group_state = MLD6_GROUP_IDLE_MEMBER; ++ group->v2_timer = 0; ++#endif + + netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, group); + } +@@ -244,6 +264,58 @@ mld6_input(struct pbuf *p, struct netif *inp) + + switch (mld_hdr->type) { + case ICMP6_TYPE_MLQ: /* Multicast listener query. */ ++#if LWIP_IPV6_MLD_V2 ++ if (p->len >= MLD6_V2_MINLEN) { ++ struct mld_v2_query *mld_v2_hdr = (struct mld_v2_query *)p->payload; ++ ++ /* Is it a general query? */ ++ if (ip6_addr_isallnodes_linklocal(ip6_current_dest_addr()) && ++ ip6_addr_isany(&(mld_v2_hdr->multicast_address))) { ++ MLD6_STATS_INC(mld6.rx_general); ++ /* Report all groups, except all nodes group, and if-local groups. */ ++ group = netif_mld6_data(inp); ++ while (group != NULL) { ++ if ((!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) && ++ (!(ip6_addr_isallnodes_linklocal(&(group->group_address))))) { ++ mld6_v2_delayed_report(group, lwip_ntohs(mld_hdr->max_resp_delay)); ++ } ++ group = group->next; ++ } ++ } else { /* query to a specific group ? */ ++ if (!ip6_addr_isany(&(mld_v2_hdr->multicast_address))) { ++ u16_t src_num = PP_NTOHS(mld_v2_hdr->src_num); ++ ip6_addr_p_t *src_buf; ++ u16_t src_buf_size = src_num << 4; ++ ip6_addr_t group_addr; ++ ++ MLD6_STATS_INC(mld6.rx_group); ++ ip6_addr_copy(group_addr, mld_v2_hdr->multicast_address); ++ group = mld6_lookfor_group(inp, &group_addr); ++ ++ if (0 == src_num) { ++ if (group != NULL) { ++ /* Schedule a report. */ ++ mld6_v2_delayed_report(group, lwip_ntohs(mld_v2_hdr->max_resp_delay)); ++ } ++ } else { ++ src_buf = (ip6_addr_p_t *)mem_malloc(src_buf_size); ++ if (src_buf == NULL) { ++ pbuf_free(p); ++ MLD6_STATS_INC(mld6.memerr); ++ LWIP_DEBUGF(MLD6_DEBUG, ("mld6_input: not enough memory for mld6_input\n")); ++ return; ++ } ++ pbuf_copy_partial(p, src_buf, src_buf_size, MLD_V2_QUERY_HLEN); ++ if (mcast_ip6_filter_interest(inp, (const ip6_addr_t *)&group_addr, src_buf, src_num)) { ++ /* We interest! */ ++ mld6_v2_delayed_report(group, lwip_ntohs(mld_v2_hdr->max_resp_delay)); ++ } ++ } ++ } ++ } ++ ++ } ++#endif + /* Is it a general query? */ + if (ip6_addr_isallnodes_linklocal(ip6_current_dest_addr()) && + ip6_addr_isany(&(mld_hdr->multicast_address))) { +@@ -381,6 +453,10 @@ mld6_joingroup_netif(struct netif *netif, const ip6_addr_t *groupaddr) + MLD6_STATS_INC(mld6.tx_report); + mld6_send(netif, group, ICMP6_TYPE_MLR); + mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS); ++#if LWIP_IPV6_MLD_V2 ++ mld6_v2_send(netif, group, ICMP6_TYPE_MLR2); ++ mld6_v2_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS); ++#endif + } + + /* Increment group use */ +@@ -464,6 +540,13 @@ mld6_leavegroup_netif(struct netif *netif, const ip6_addr_t *groupaddr) + MLD6_STATS_INC(mld6.tx_leave); + mld6_send(netif, group, ICMP6_TYPE_MLD); + } ++#if LWIP_IPV6_MLD_V2 ++ /* If we are the last reporter for this group */ ++ if (group->v2_last_reporter_flag) { ++ MLD6_STATS_INC(mld6.tx_leave); ++ mld6_v2_send(netif, group, ICMP6_TYPE_MLD); ++ } ++#endif + + /* Disable the group at the MAC level */ + if (netif->mld_mac_filter != NULL) { +@@ -485,6 +568,51 @@ mld6_leavegroup_netif(struct netif *netif, const ip6_addr_t *groupaddr) + return ERR_VAL; + } + ++/** ++ * Called if a timeout for one group is reached. ++ * Sends a report for this group. ++ * ++ * @param group an mld_group for which a timeout is reached ++ */ ++static void ++mld6_timeout(struct netif *netif, struct mld_group *group) ++{ ++ /* If the state is MLD6_GROUP_DELAYING_MEMBER then we send a report for this group */ ++ if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) { ++ LWIP_DEBUGF(MLD6_DEBUG, ("mld6_timeout: report membership for group with address ")); ++ ip6_addr_debug_print_val(MLD6_DEBUG, group->group_address); ++ LWIP_DEBUGF(MLD6_DEBUG, (" on if %p\n", (void *)netif)); ++ ++ group->group_state = MLD6_GROUP_IDLE_MEMBER; ++ ++ MLD6_STATS_INC(mld6.tx_report); ++ mld6_send(netif, group, ICMP6_TYPE_MLR); ++ } ++} ++ ++#if LWIP_IPV6_MLD_V2 ++/** ++ * Called if a timeout for one group is reached. ++ * Sends a report for this group. ++ * ++ * @param group an mld_group for which a timeout is reached ++ */ ++static void ++mld6_v2_timeout(struct netif *netif, struct mld_group *group) ++{ ++ /* If the state is MLD6_GROUP_DELAYING_MEMBER then we send a report for this group */ ++ if (group->v2_group_state == MLD6_GROUP_DELAYING_MEMBER) { ++ LWIP_DEBUGF(MLD6_DEBUG, ("mld6_v2_timeout: report membership for group with address ")); ++ ip6_addr_debug_print_val(MLD6_DEBUG, group->group_address); ++ LWIP_DEBUGF(MLD6_DEBUG, (" on if %p\n", (void *)netif)); ++ ++ group->v2_group_state = MLD6_GROUP_IDLE_MEMBER; ++ ++ MLD6_STATS_INC(mld6.tx_report); ++ mld6_v2_send(netif, group, ICMP6_TYPE_MLR2); ++ } ++} ++#endif /* LWIP_IPV6_MLD_V2 */ + + /** + * Periodic timer for mld processing. Must be called every +@@ -504,14 +632,17 @@ mld6_tmr(void) + if (group->timer > 0) { + group->timer--; + if (group->timer == 0) { +- /* If the state is MLD6_GROUP_DELAYING_MEMBER then we send a report for this group */ +- if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) { +- MLD6_STATS_INC(mld6.tx_report); +- mld6_send(netif, group, ICMP6_TYPE_MLR); +- group->group_state = MLD6_GROUP_IDLE_MEMBER; +- } ++ mld6_timeout(netif, group); ++ } ++ } ++#if LWIP_IPV6_MLD_V2 ++ if (group->v2_timer > 0) { ++ group->v2_timer--; ++ if (group->v2_timer == 0) { ++ mld6_v2_timeout(netif, group); + } + } ++#endif + group = group->next; + } + } +@@ -550,6 +681,46 @@ mld6_delayed_report(struct mld_group *group, u16_t maxresp_in) + } + } + ++#if LWIP_IPV6_MLD_V2 ++/** ++ * Schedule a delayed membership report for a group ++ * ++ * @param group the mld_group for which "delaying" membership report ++ * should be sent ++ * @param maxresp_in the max resp delay provided in the query ++ */ ++static void ++mld6_v2_delayed_report(struct mld_group *group, u16_t maxresp_in) ++{ ++ /* Convert maxresp from milliseconds to tmr ticks */ ++ u16_t maxresp = maxresp_in / MLD6_TMR_INTERVAL; ++ if (maxresp >= 32768) { ++ maxresp = ((maxresp & 0x0fff) | 0x1000) << (((maxresp >> 12) & 7) + 3);; ++ } ++ if (maxresp == 0) { ++ maxresp = 1; ++ } ++ ++#ifdef LWIP_RAND ++ /* Randomize maxresp. (if LWIP_RAND is supported) */ ++ maxresp = (u16_t)(LWIP_RAND() % maxresp); ++ if (maxresp == 0) { ++ maxresp = 1; ++ } ++#endif /* LWIP_RAND */ ++ ++ /* Apply timer value if no report has been scheduled already. */ ++ if ((group->v2_group_state == MLD6_GROUP_IDLE_MEMBER) || ++ ((group->v2_group_state == MLD6_GROUP_DELAYING_MEMBER) && ++ ((group->v2_timer == 0) || (maxresp < group->v2_timer)))) { ++ group->v2_timer = maxresp; ++ group->v2_group_state = MLD6_GROUP_DELAYING_MEMBER; ++ } ++} ++ ++#endif ++ ++ + /** + * Send a MLD message (report or done). + * +@@ -623,4 +794,181 @@ mld6_send(struct netif *netif, struct mld_group *group, u8_t type) + pbuf_free(p); + } + ++#if LWIP_IPV6_MLD_V2 ++/** ++ * Build a mld v2 record to a specific group. ++ * ++ * @param rec record to build ++ * @param group the group to which to send the packet ++ * @param fmode filter mode ++ * @param src_array source addr array ++ * @param src_cnt source addr cnt ++ */ ++static void ++mld_v2_build_record(struct mld_v2_record *rec, struct mld_group *group, u8_t fmode, ip6_addr_p_t *src_array, u32_t src_cnt) ++{ ++ ip6_addr_p_t *src_copy; ++ ++ if (fmode == MCAST_EXCLUDE) { ++ if (group->v2_fmode != MCAST_EXCLUDE) { ++ group->v2_fmode = MCAST_EXCLUDE; ++ rec->type = MLD2_CHANGE_TO_EXCLUDE; ++ } else { ++ rec->type = MLD2_MODE_IS_EXCLUDE; ++ } ++ ++ } else { ++ if (group->v2_fmode != MCAST_INCLUDE) { ++ group->v2_fmode = MCAST_INCLUDE; ++ rec->type = MLD2_CHANGE_TO_INCLUDE; ++ } else { ++ rec->type = MLD2_MODE_IS_INCLUDE; ++ } ++ } ++ ++ rec->aux_len = 0; ++ rec->src_num = PP_HTONS(src_cnt); ++ ip6_addr_copy_to_packed(rec->multicast_address, group->group_address); ++ ++ if (src_cnt) { ++ src_copy = (ip6_addr_p_t *)(rec + 1); ++ MEMCPY(src_copy, src_array, (src_cnt << 4)); ++ } ++} ++ ++/** ++ * Send a MLD message (report). ++ * ++ * An IPv6 hop-by-hop options header with a router alert option ++ * is prepended. ++ * ++ * @param group the group to report or quit ++ * @param type ICMP6_TYPE_MLR (report) or ICMP6_TYPE_MLD (done) ++ */ ++static void ++mld6_v2_send(struct netif *netif, struct mld_group *group, u8_t type) ++{ ++ struct mld_v2_report *mld_hdr; ++ struct mld_v2_record *rec; ++ struct pbuf *p; ++ const ip6_addr_t *src_addr; ++ ++ /* Select our source address. */ ++ if (!ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) { ++ /* This is a special case, when we are performing duplicate address detection. ++ * We must join the multicast group, but we don't have a valid address yet. */ ++ src_addr = IP6_ADDR_ANY6; ++ } else { ++ /* Use link-local address as source address. */ ++ src_addr = netif_ip6_addr(netif, 0); ++ } ++ ++ if (ICMP6_TYPE_MLD == type) { ++ p = pbuf_alloc(PBUF_IP, MLD_V2_REPORT_HLEN + MLD_V2_RECORD_LEN(0) + MLD6_HBH_HLEN, PBUF_RAM); ++ if (p == NULL) { ++ LWIP_DEBUGF(MLD6_DEBUG, ("mld6_v2_send: not enough memory for mld6_v2_send\n")); ++ IGMP_STATS_INC(mld6.memerr); ++ return; ++ } ++ ++ /* Move to make room for Hop-by-hop options header. */ ++ if (pbuf_remove_header(p, MLD6_HBH_HLEN)) { ++ pbuf_free(p); ++ MLD6_STATS_INC(mld6.lenerr); ++ return; ++ } ++ ++ mld_hdr = (struct mld_v2_report *)p->payload; ++ mld_hdr->type = ICMP6_TYPE_MLR2; ++ mld_hdr->reserved1 = 0; ++ mld_hdr->chksum = 0; ++ mld_hdr->reserved2 = 0; ++ mld_hdr->mrec_num = PP_HTONS(1); ++ ++ rec = (struct mld_v2_record *)(mld_hdr + 1); ++ if (group->v2_fmode == MCAST_EXCLUDE) { ++ rec->type = MLD2_CHANGE_TO_INCLUDE; ++ } else { ++ rec->type = MLD2_MODE_IS_INCLUDE; ++ } ++ rec->aux_len = 0; ++ rec->src_num = 0; /* IS_IN (NULL) mean drop group */ ++ ip6_addr_copy_to_packed(rec->multicast_address, group->group_address); ++ } else {/* Report a group */ ++ ip6_addr_p_t src_array[LWIP_MCAST_SRC_TBL_SIZE]; ++ u16_t src_cnt; ++ u8_t fmode; ++ ++ src_cnt = mcast_ip6_filter_info(netif, &group->group_address, src_array, LWIP_MCAST_SRC_TBL_SIZE, &fmode); ++ LWIP_ASSERT("mld6_v2_send: multicast filter error!", !(!src_cnt && (fmode == MCAST_INCLUDE))); ++ ++ p = pbuf_alloc(PBUF_IP, MLD_V2_REPORT_HLEN + MLD_V2_RECORD_LEN(src_cnt) + MLD6_HBH_HLEN, PBUF_RAM); ++ if (p == NULL) { ++ LWIP_DEBUGF(MLD6_DEBUG, ("mld6_v2_send: not enough memory for mld6_v2_send\n")); ++ IGMP_STATS_INC(mld6.memerr); ++ return; ++ } ++ ++ mld_hdr = (struct mld_v2_report *)p->payload; ++ mld_hdr->type = ICMP6_TYPE_MLR2; ++ mld_hdr->reserved1 = 0; ++ mld_hdr->chksum = 0; ++ mld_hdr->reserved2 = 0; ++ mld_hdr->mrec_num = PP_HTONS(1); ++ ++ rec = (struct mld_v2_record *)(mld_hdr + 1); ++ mld_v2_build_record(rec, group, fmode, src_array, src_cnt); ++ group->v2_last_reporter_flag = 1;/* Remember we were the last to report */ ++ } ++ ++ #if CHECKSUM_GEN_ICMP6 ++ IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_ICMP6) { ++ mld_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, ++ src_addr, &(group->group_address)); ++ } ++#endif /* CHECKSUM_GEN_ICMP6 */ ++ ++ /* Add hop-by-hop headers options: router alert with MLD value. */ ++ ip6_options_add_hbh_ra(p, IP6_NEXTH_ICMP6, IP6_ROUTER_ALERT_VALUE_MLD); ++ ++ /* Send the packet out. */ ++ MLD6_STATS_INC(mld6.xmit); ++ ip6_output_if(p, (ip6_addr_isany(src_addr)) ? NULL : src_addr, &(group->group_address), ++ MLD6_HL, 0, IP6_NEXTH_HOPBYHOP, netif); ++ pbuf_free(p); ++} ++ ++/** ++ * mld v2 report trigger. ++ */ ++void ++mld6_v2_trigger(struct netif *netif, const ip6_addr_t *groupaddr) ++{ ++ struct mld_group *group; ++#if LWIP_IPV6_SCOPES ++ ip6_addr_t ip6addr; ++ ++ /* If the address has a particular scope but no zone set, use the netif to ++ * set one now. Within the mld6 module, all addresses are properly zoned. */ ++ if (ip6_addr_lacks_zone(groupaddr, IP6_MULTICAST)) { ++ ip6_addr_set(&ip6addr, groupaddr); ++ ip6_addr_assign_zone(&ip6addr, IP6_MULTICAST, netif); ++ groupaddr = &ip6addr; ++ } ++ IP6_ADDR_ZONECHECK_NETIF(groupaddr, netif); ++#endif /* LWIP_IPV6_SCOPES */ ++ ++ /* find group */ ++ group = mld6_lookfor_group(netif, groupaddr); ++ if (group) { ++ mld6_v2_send(netif, group, ICMP6_TYPE_MLR2); ++ ++ mld6_v2_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS); ++ ++ /* Need to work out where this timer comes from */ ++ group->v2_group_state = MLD6_GROUP_DELAYING_MEMBER; ++ } ++} ++#endif ++ + #endif /* LWIP_IPV6 */ +diff --git a/src/core/mcast.c b/src/core/mcast.c +index 5e3463b..fa46f10 100644 +--- a/src/core/mcast.c ++++ b/src/core/mcast.c +@@ -250,6 +250,204 @@ mcast_ip4_filter_interest(struct netif *netif, const ip4_addr_t *multi_addr, con + #endif /* LWIP_IGMP_V3 */ + #endif /* LWIP_IPV4 && LWIP_IGMP */ + ++#if LWIP_IPV6 && LWIP_IPV6_MLD ++/** ++ * ipv6 multicast filter find ++ */ ++struct ip6_mc * ++mcast_ip6_mc_find(struct ip_mc *ipmc, struct netif *netif, const ip6_addr_t *multi_addr, struct ip6_mc **mc_prev) ++{ ++ struct ip6_mc *prev = NULL; ++ struct ip6_mc *mc; ++ ++ IP6_MC_FOREACH(ipmc, mc) { ++ if (((mc->if_idx == NETIF_NO_INDEX) || (mc->if_idx == netif_get_index(netif))) && ++ ip6_addr_cmp_zoneless(&mc->multi_addr, multi_addr)) { /* check interface and multicast address */ ++ if (mc_prev) { ++ *mc_prev = prev; ++ } ++ return mc; /* found! */ ++ } ++ prev = mc; ++ } ++ ++ return NULL; /* not found! */ ++} ++ ++/** ++ * ipv6 multicast filter find source ++ */ ++static struct mld6_src * ++mcast_ip6_mc_src_find(struct ip6_mc *mc, const ip6_addr_t *src_addr, struct mld6_src **src_prev) ++{ ++ struct mld6_src *prev = NULL; ++ struct mld6_src *src; ++ ++ IP6_MC_SRC_FOREACH(mc, src) { ++ if (ip6_addr_cmp_zoneless(&src->src_addr, src_addr)) { /* check source address */ ++ if (src_prev) { ++ *src_prev = prev; ++ } ++ return src; /* found! */ ++ } ++ prev = src; ++ } ++ ++ return NULL; /* not found! */ ++} ++ ++/** ++ * ipv6 multicast filter remove all source ++ */ ++void ++mcast_ip6_mc_src_remove(struct mld6_src *src) ++{ ++ struct mld6_src *next; ++ ++ while (src) { ++ next = src->next; ++ mem_free(src); ++ src = next; ++ } ++} ++ ++#if LWIP_IPV6_MLD_V2 ++/** ++ * ipv6 multicast filter group source infomation (use ip6_addr_p_t for MLDv2 speedup) ++ */ ++u16_t ++mcast_ip6_filter_info(struct netif *netif, const ip6_addr_t *multi_addr, ip6_addr_p_t addr_array[], u16_t arr_cnt, u8_t *fmode) ++{ ++ static ip6_addr_p_t in_tbl[LWIP_MCAST_SRC_TBL_SIZE]; ++ static ip6_addr_p_t ex_tbl[LWIP_MCAST_SRC_TBL_SIZE]; ++ ++ struct ip6_mc *mc; ++ struct mld6_src *src; ++ ip6_addr_t addr; /* for compare speed */ ++ u16_t i, j; ++ u16_t cnt, in_cnt = 0, ex_cnt = 0; ++ u16_t max_cnt = (u16_t)((arr_cnt > LWIP_MCAST_SRC_TBL_SIZE) ? LWIP_MCAST_SRC_TBL_SIZE : arr_cnt); ++ u8_t match = 0; ++ ++#define LWIP_IP6_MC_GET(pcb, pcbs, type, tbl, c) \ ++ for ((pcb) = (pcbs); (pcb) != NULL; (pcb) = (pcb)->next) { \ ++ mc = mcast_ip6_mc_find(&(pcb)->ipmc, netif, multi_addr, NULL); \ ++ if ((mc == NULL) || (mc->fmode != (type))) { \ ++ continue; \ ++ } \ ++ match = 1; \ ++ IP6_MC_SRC_FOREACH(mc, src) { \ ++ if ((c) < max_cnt) { \ ++ ip6_addr_copy_to_packed((tbl)[(c)], src->src_addr); /* save a source */ \ ++ (c)++; \ ++ } else { \ ++ *fmode = MCAST_EXCLUDE; /* table overflow, we need all this group packet */ \ ++ return (0); \ ++ } \ ++ } \ ++ } ++ ++#if LWIP_UDP ++ { ++ struct udp_pcb *pcb; ++ LWIP_IP6_MC_GET(pcb, udp_pcbs, MCAST_INCLUDE, in_tbl, in_cnt); /* copy all udp include source address to in_tbl[] */ ++ } ++#endif /* LWIP_UDP */ ++ ++#if LWIP_RAW ++ { ++ struct raw_pcb *pcb; ++ LWIP_IP6_MC_GET(pcb, raw_pcbs, MCAST_INCLUDE, in_tbl, in_cnt); /* copy all raw include source address to in_tbl[] */ ++ } ++#endif /* LWIP_UDP */ ++ ++#if LWIP_UDP ++ { ++ struct udp_pcb *pcb; ++ LWIP_IP6_MC_GET(pcb, udp_pcbs, MCAST_EXCLUDE, ex_tbl, ex_cnt); /* copy all udp exclude source address to ex_tbl[] */ ++ } ++#endif /* LWIP_UDP */ ++ ++#if LWIP_RAW ++ { ++ struct raw_pcb *pcb; ++ LWIP_IP6_MC_GET(pcb, raw_pcbs, MCAST_EXCLUDE, ex_tbl, ex_cnt); /* copy all raw exclude source address to ex_tbl[] */ ++ } ++#endif /* LWIP_UDP */ ++ ++ if (ex_cnt) { /* at least have one exclude source address */ ++ *fmode = MCAST_EXCLUDE; ++ for (i = 0; i < ex_cnt; i++) { ++ ip6_addr_copy_from_packed(addr, ex_tbl[i]); ++ for (j = 0; j < in_cnt; j++) { ++ if (ip6_addr_cmp_zoneless(&addr, &in_tbl[j])) { /* check exclude conflict with include table */ ++ ip6_addr_copy_to_packed(ex_tbl[i], *IP6_ADDR_ANY6); /* remove from exclude table */ ++ break; ++ } ++ } ++ } ++ ++ for (i = 0, cnt = 0; i < ex_cnt; i++) { ++ if (!ip6_addr_isany(&ex_tbl[i])) { ++ ip6_addr_copy_to_packed(addr_array[cnt], ex_tbl[i]); ++ cnt++; ++ } ++ } ++ ++ } else if (in_cnt) { /* at least have one include source address */ ++ *fmode = MCAST_INCLUDE; ++ for (i = 0; i < in_cnt; i++) { ++ ip6_addr_copy_to_packed(addr_array[i], in_tbl[i]); ++ } ++ cnt = i; ++ ++ } else { ++ if (match) { /* at least have one pcb matched */ ++ *fmode = MCAST_EXCLUDE; ++ } else { /* no match! */ ++ *fmode = MCAST_INCLUDE; ++ } ++ cnt = 0; ++ } ++ ++ return (cnt); ++} ++ ++/** ++ * ipv6 multicast filter source address interest (use ip6_addr_p_t for MLDv2 speedup) ++ */ ++u8_t ++mcast_ip6_filter_interest(struct netif *netif, const ip6_addr_t *multi_addr, const ip6_addr_p_t src_addr[], u16_t arr_cnt) ++{ ++ static ip6_addr_p_t ip_tbl[LWIP_MCAST_SRC_TBL_SIZE]; ++ u16_t i, j, cnt; ++ u8_t fmode; ++ ++ cnt = mcast_ip6_filter_info(netif, multi_addr, ip_tbl, LWIP_MCAST_SRC_TBL_SIZE, &fmode); ++ if (fmode == MCAST_EXCLUDE) { ++ for (i = 0; i < cnt; i++) { ++ for (j = 0; j < arr_cnt; j++) { ++ if (ip6_addr_cmp_zoneless(&src_addr[j], &ip_tbl[i])) { ++ return 0; ++ } ++ } ++ } ++ return 1; ++ ++ } else { ++ for (i = 0; i < cnt; i++) { ++ for (j = 0; j < arr_cnt; j++) { ++ if (ip6_addr_cmp_zoneless(&src_addr[j], &ip_tbl[i])) { ++ return 1; ++ } ++ } ++ } ++ return 0; ++ } ++} ++#endif /* LWIP_IPV6_MLD_V2 */ ++#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ ++ + #if (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) + /** Join a multicast group (Can with a source specified) + * +@@ -303,8 +501,9 @@ mcast_join_netif(struct ip_mc *ipmc, struct netif *netif, const ip_addr_t *multi + mc->src = src; + mc->fmode = MCAST_INCLUDE; /* change to include mode */ + IP4_MC_TRIGGER_CALL(netif, ip_2_ip4(multi_addr)); /* trigger a report */ ++ return ERR_OK; + } +- return ERR_OK; ++ return EADDRINUSE; + } + + mc = (struct ip4_mc *)mem_malloc(sizeof(struct ip4_mc)); /* Make a new mc */ +@@ -334,8 +533,68 @@ mcast_join_netif(struct ip_mc *ipmc, struct netif *netif, const ip_addr_t *multi + mc->next = ipmc->mc4; + ipmc->mc4 = mc; + igmp_joingroup_netif(netif, ip_2_ip4(multi_addr)); +- } else {} ++ } else + #endif /* LWIP_IPV4 && LWIP_IGMP */ ++ { ++#if LWIP_IPV6 && LWIP_IPV6_MLD ++ struct ip6_mc *mc; ++ struct mld6_src *src; ++ ++ mc = mcast_ip6_mc_find(ipmc, netif, ip_2_ip6(multi_addr), NULL); ++ if (mc) { ++ if (src_addr) { ++ if ((mc->fmode == MCAST_EXCLUDE) && (mc->src)) { ++ return ERR_VAL; /* filter mode not include mode */ ++ } ++ src = mcast_ip6_mc_src_find(mc, ip_2_ip6(src_addr), NULL); ++ if (src) { ++ return ERR_ALREADY; /* already in source list */ ++ } ++ ++ src = (struct mld6_src *)mem_malloc(sizeof(struct mld6_src)); ++ if (src == NULL) { ++ return ERR_MEM; /* no memory */ ++ } ++ ip6_addr_set(&src->src_addr, ip_2_ip6(src_addr)); ++ src->next = mc->src; ++ mc->src = src; ++ mc->fmode = MCAST_INCLUDE; /* change to include mode */ ++ IP6_MC_TRIGGER_CALL(netif, ip_2_ip6(multi_addr)); /* trigger a report */ ++ return ERR_OK; ++ } ++ return EADDRINUSE; ++ } ++ ++ mc = (struct ip6_mc *)mem_malloc(sizeof(struct ip6_mc)); /* Make a new mc */ ++ if (mc == NULL) { ++ mld6_leavegroup_netif(netif, ip_2_ip6(multi_addr)); ++ return ERR_MEM; /* no memory */ ++ } ++ mc->if_idx = netif_get_index(netif); ++ ip6_addr_set(&mc->multi_addr, ip_2_ip6(multi_addr)); ++ ++ if (src_addr) { ++ mc->fmode = MCAST_INCLUDE; ++ src = (struct mld6_src *)mem_malloc(sizeof(struct mld6_src)); ++ if (src == NULL) { ++ mld6_leavegroup_netif(netif, ip_2_ip6(multi_addr)); ++ mem_free(mc); ++ return ERR_MEM; /* no memory */ ++ } ++ ip6_addr_set(&src->src_addr, ip_2_ip6(src_addr)); ++ src->next = NULL; ++ mc->src = src; ++ ++ } else { ++ mc->fmode = MCAST_EXCLUDE; /* no source specified */ ++ mc->src = NULL; ++ } ++ ++ mc->next = ipmc->mc6; ++ ipmc->mc6 = mc; ++ mld6_joingroup_netif(netif, ip_2_ip6(multi_addr)); ++#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ ++ } + return ERR_OK; + } + +@@ -364,8 +623,23 @@ mcast_join_group(struct ip_mc *ipmc, const ip_addr_t *if_addr, const ip_addr_t * + } + } + } +- } else {} ++ } else + #endif /* LWIP_IPV4 && LWIP_IGMP */ ++ { ++#if LWIP_IPV6 && LWIP_IPV6_MLD ++ struct netif *netif; ++ ++ NETIF_FOREACH(netif) { ++ if (ip6_addr_isany(ip_2_ip6(if_addr)) || ++ netif_get_ip6_addr_match(netif, ip_2_ip6(if_addr)) >= 0) { ++ err = mcast_join_netif(ipmc, netif, multi_addr, src_addr); ++ if (err != ERR_OK) { ++ return (err); ++ } ++ } ++ } ++#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ ++ } + return err; + } + +@@ -423,8 +697,53 @@ mcast_leave_netif(struct ip_mc *ipmc, struct netif *netif, const ip_addr_t *mult + ipmc->mc4 = mc->next; + } + mem_free(mc); +- } else {} ++ } else + #endif /* LWIP_IPV4 && LWIP_IGMP */ ++ { ++#if LWIP_IPV6 && LWIP_IPV6_MLD ++ struct ip6_mc *mc_prev; ++ struct ip6_mc *mc; ++ struct mld6_src *src_prev; ++ struct mld6_src *src; ++ ++ mc = mcast_ip6_mc_find(ipmc, netif, ip_2_ip6(multi_addr), &mc_prev); ++ if (mc == NULL) { ++ return ERR_VAL; ++ } ++ ++ if (src_addr) { ++ if ((mc->fmode == MCAST_EXCLUDE) && (mc->src)) { ++ return ERR_VAL; /* drop source membership must in include mode */ ++ } ++ ++ src = mcast_ip6_mc_src_find(mc, ip_2_ip6(src_addr), &src_prev); ++ if (src) { ++ if (src_prev) { ++ src_prev->next = src->next; ++ } else { ++ mc->src = src->next; ++ } ++ mem_free(src); ++ } else { ++ return ERR_VAL; ++ } ++ if (mc->src) { ++ IP6_MC_TRIGGER_CALL(netif, ip_2_ip6(multi_addr)); /* trigger a report */ ++ return ERR_OK; ++ } ++ } else { /* we want drop this group */ ++ mcast_ip6_mc_src_remove(mc->src); ++ } ++ ++ mld6_leavegroup_netif(netif, ip_2_ip6(multi_addr)); ++ if (mc_prev) { ++ mc_prev->next = mc->next; ++ } else { ++ ipmc->mc6 = mc->next; ++ } ++ mem_free(mc); ++#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ ++ } + return ERR_OK; + } + +@@ -453,8 +772,23 @@ mcast_leave_group(struct ip_mc *ipmc, const ip_addr_t *if_addr, const ip_addr_t + } + } + } +- } else {} ++ } else + #endif /* LWIP_IPV4 && LWIP_IGMP */ ++ { ++#if LWIP_IPV6 && LWIP_IPV6_MLD ++ struct netif *netif; ++ ++ NETIF_FOREACH(netif) { ++ if (ip6_addr_isany(ip_2_ip6(if_addr)) || ++ netif_get_ip6_addr_match(netif, ip_2_ip6(if_addr)) >= 0) { ++ res = mcast_leave_netif(ipmc, netif, multi_addr, src_addr); ++ if (err != ERR_OK) { ++ err = res; ++ } ++ } ++ } ++#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ ++ } + return err; + } + +@@ -492,11 +826,36 @@ mcast_block_netif(struct ip_mc *ipmc, struct netif *netif, const ip_addr_t *mult + src->next = mc->src; + mc->src = src; + IP4_MC_TRIGGER_CALL(netif, ip_2_ip4(multi_addr)); /* trigger a report */ +- return ERR_OK; + } +- } else {} ++ } else + #endif /* LWIP_IPV4 && LWIP_IGMP */ +- return EADDRINUSE; ++ { ++#if LWIP_IPV6 && LWIP_IPV6_MLD ++ struct ip6_mc *mc; ++ struct mld6_src *src; ++ ++ mc = mcast_ip6_mc_find(ipmc, netif, ip_2_ip6(multi_addr), NULL); ++ if (mc == NULL) { ++ return ERR_VAL; ++ } ++ if (mc->fmode != MCAST_EXCLUDE) { /* we must in exclude mode */ ++ return ERR_VAL; ++ } ++ ++ src = mcast_ip6_mc_src_find(mc, ip_2_ip6(blk_addr), NULL); ++ if (src == NULL) { ++ src = (struct mld6_src *)mem_malloc(sizeof(struct mld6_src)); ++ if (src == NULL) { ++ return ERR_MEM; ++ } ++ ip6_addr_set(&src->src_addr, ip_2_ip6(blk_addr)); ++ src->next = mc->src; ++ mc->src = src; ++ IP6_MC_TRIGGER_CALL(netif, ip_2_ip6(multi_addr)); /* trigger a report */ ++ } ++#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ ++ } ++ return ERR_OK; + } + + /** Add a block source address to a multicast group +@@ -524,8 +883,23 @@ mcast_block_group(struct ip_mc *ipmc, const ip_addr_t *if_addr, const ip_addr_t + } + } + } +- } else {} ++ } else + #endif /* LWIP_IPV4 && LWIP_IGMP */ ++ { ++#if LWIP_IPV6 && LWIP_IPV6_MLD ++ struct netif *netif; ++ ++ NETIF_FOREACH(netif) { ++ if (ip6_addr_isany(ip_2_ip6(if_addr)) || ++ netif_get_ip6_addr_match(netif, ip_2_ip6(if_addr)) >= 0) { ++ err = mcast_block_netif(ipmc, netif, multi_addr, blk_addr); ++ if (err != ERR_OK) { ++ return (err); ++ } ++ } ++ } ++#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ ++ } + return err; + } + +@@ -565,8 +939,35 @@ mcast_unblock_netif(struct ip_mc *ipmc, struct netif *netif, const ip_addr_t *mu + } + mem_free(src); + IP4_MC_TRIGGER_CALL(netif, ip_2_ip4(multi_addr)); /* trigger a report */ +- } else {} ++ } else + #endif /* LWIP_IPV4 && LWIP_IGMP */ ++ { ++#if LWIP_IPV6 && LWIP_IPV6_MLD ++ struct ip6_mc *mc; ++ struct mld6_src *src_prev; ++ struct mld6_src *src; ++ ++ mc = mcast_ip6_mc_find(ipmc, netif, ip_2_ip6(multi_addr), NULL); ++ if (mc == NULL) { ++ return ERR_VAL; ++ } ++ if (mc->fmode != MCAST_EXCLUDE) { /* we must in exclude mode */ ++ return ERR_VAL; ++ } ++ ++ src = mcast_ip6_mc_src_find(mc, ip_2_ip6(unblk_addr), &src_prev); ++ if (src == NULL) { ++ return ERR_VAL; ++ } ++ if (src_prev) { ++ src_prev->next = src->next; ++ } else { ++ mc->src = src->next; ++ } ++ mem_free(src); ++ IP6_MC_TRIGGER_CALL(netif, ip_2_ip6(multi_addr)); /* trigger a report */ ++#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ ++ } + return ERR_OK; + } + +@@ -595,8 +996,23 @@ mcast_unblock_group(struct ip_mc *ipmc, const ip_addr_t *if_addr, const ip_addr_ + } + } + } +- } else {} ++ } else + #endif /* LWIP_IPV4 && LWIP_IGMP */ ++ { ++#if LWIP_IPV6 && LWIP_IPV6_MLD ++ struct netif *netif; ++ ++ NETIF_FOREACH(netif) { ++ if (ip6_addr_isany(ip_2_ip6(if_addr)) || ++ netif_get_ip6_addr_match(netif, ip_2_ip6(if_addr)) >= 0) { ++ res = mcast_unblock_netif(ipmc, netif, multi_addr, unblk_addr); ++ if (err != ERR_OK) { ++ err = res; ++ } ++ } ++ } ++#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ ++ } + return err; + } + +diff --git a/src/core/udp.c b/src/core/udp.c +index e1f9e1b..4a47e99 100644 +--- a/src/core/udp.c ++++ b/src/core/udp.c +@@ -195,6 +195,12 @@ udp_input_local_match(struct udp_pcb *pcb, struct netif *inp, u8_t broadcast) + return 0; + } + ++#if (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) ++ if (ip_addr_ismulticast(ip_current_dest_addr())) { ++ return mcast_input_local_match(&pcb->ipmc, inp); ++ } ++#endif /* (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) */ ++ + /* Dual-stack: PCBs listening to any IP type also listen to any IP address */ + if (IP_IS_ANY_TYPE_VAL(pcb->local_ip)) { + #if LWIP_IPV4 && IP_SOF_BROADCAST_RECV +@@ -205,12 +211,6 @@ udp_input_local_match(struct udp_pcb *pcb, struct netif *inp, u8_t broadcast) + return 1; + } + +-#if (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) +- if (ip_addr_ismulticast(ip_current_dest_addr())) { +- return mcast_input_local_match(&pcb->ipmc, inp); +- } +-#endif /* (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) */ +- + /* Only need to check PCB if incoming IP version matches PCB IP version */ + if (IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ip_current_dest_addr())) { + #if LWIP_IPV4 +diff --git a/src/include/lwip/api.h b/src/include/lwip/api.h +index d5783dd..319ee09 100644 +--- a/src/include/lwip/api.h ++++ b/src/include/lwip/api.h +@@ -103,6 +103,7 @@ extern "C" { + #define NETCONNTYPE_ISIPV6(t) (((t)&NETCONN_TYPE_IPV6) != 0) + #define NETCONNTYPE_ISUDPLITE(t) (((t)&0xF3) == NETCONN_UDPLITE) + #define NETCONNTYPE_ISUDPNOCHKSUM(t) (((t)&0xF3) == NETCONN_UDPNOCHKSUM) ++#define NETCONNTYPE_GROUPV6(t) ((t)&0xFF) + #else /* LWIP_IPV6 */ + #define NETCONNTYPE_ISIPV6(t) (0) + #define NETCONNTYPE_ISUDPLITE(t) ((t) == NETCONN_UDPLITE) +diff --git a/src/include/lwip/mcast.h b/src/include/lwip/mcast.h +index c7b195d..16a27ae 100644 +--- a/src/include/lwip/mcast.h ++++ b/src/include/lwip/mcast.h +@@ -74,6 +74,28 @@ struct ip4_mc { + #endif /* LWIP_IPV4 && LWIP_IGMP */ + + ++#if LWIP_IPV6 && LWIP_IPV6_MLD ++/** the IPv6 multicast filter */ ++struct ip6_mc { ++ struct ip6_mc *next; ++ /** the interface index */ ++ u8_t if_idx; ++ /** the group address */ ++ ip6_addr_t multi_addr; ++ /** the source address list filter mode 0: EXCLUDE 1: INCLUDE */ ++ u8_t fmode; ++ /** the source address list */ ++ struct mld6_src *src; ++}; ++ ++/** The list of ip6_mc. */ ++#define IP6_MC_FOREACH(ipmc, mc) \ ++ for ((mc) = (ipmc)->mc6; (mc) != NULL; (mc) = (mc)->next) ++#define IP6_MC_SRC_FOREACH(mc, src) \ ++ for ((src) = (mc)->src; (src) != NULL; (src) = (src)->next) ++#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ ++ ++ + #if (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) + /* multicast filter control block */ + struct ip_mc { +@@ -85,6 +107,10 @@ struct ip_mc { + #if LWIP_IPV4 && LWIP_IGMP + struct ip4_mc *mc4; + #endif /* LWIP_IPV4 && LWIP_IGMP */ ++ ++#if LWIP_IPV6 && LWIP_IPV6_MLD ++ struct ip6_mc *mc6; ++#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ + }; + + #if LWIP_IPV4 && LWIP_IGMP && LWIP_IGMP_V3 +@@ -93,6 +119,13 @@ u16_t mcast_ip4_filter_info(struct netif *netif, const ip4_addr_t *multi_addr, + u8_t mcast_ip4_filter_interest(struct netif *netif, const ip4_addr_t *multi_addr, const ip4_addr_p_t src_addr[], u16_t arr_cnt); + #endif /* LWIP_IPV4 && LWIP_IGMP && LWIP_IGMP_V3 */ + ++#if LWIP_IPV6 && LWIP_IPV6_MLD && LWIP_IPV6_MLD_V2 ++/* MLDv2 use the following function to get specified group of total multicast filter source address array */ ++u16_t mcast_ip6_filter_info(struct netif *netif, const ip6_addr_t *multi_addr, ip6_addr_p_t addr_array[], u16_t arr_cnt, u8_t *fmode); ++u8_t mcast_ip6_filter_interest(struct netif *netif, const ip6_addr_t *multi_addr, const ip6_addr_p_t src_addr[], u16_t arr_cnt); ++#endif /* LWIP_IPV6 && LWIP_IPV6_MLD && LWIP_IPV6_MLD_V2 */ ++ ++ + /* UDP or RAW use the following functions */ + void mcast_pcb_remove(struct ip_mc *ipmc); + u8_t mcast_input_local_match(struct ip_mc *ipmc, struct netif *inp); +@@ -109,6 +142,8 @@ err_t mcast_unblock_group(struct ip_mc *ipmc, const ip_addr_t *if_addr, const + + struct ip4_mc *mcast_ip4_mc_find(struct ip_mc *ipmc, struct netif *netif, const ip4_addr_t *multi_addr, struct ip4_mc **mc_prev); + void mcast_ip4_mc_src_remove(struct igmp_src *src); ++struct ip6_mc *mcast_ip6_mc_find(struct ip_mc *ipmc, struct netif *netif, const ip6_addr_t *multi_addr, struct ip6_mc **mc_prev); ++void mcast_ip6_mc_src_remove(struct mld6_src *src); + + #endif /* (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) */ + +diff --git a/src/include/lwip/mld6.h b/src/include/lwip/mld6.h +index 2764fdd..7819551 100644 +--- a/src/include/lwip/mld6.h ++++ b/src/include/lwip/mld6.h +@@ -54,6 +54,13 @@ + extern "C" { + #endif + ++/* Multicast filter support */ ++struct mld6_src { ++ struct mld6_src *next; ++ /** the source address */ ++ ip6_addr_t src_addr; ++}; ++ + /** MLD group */ + struct mld_group { + /** next link */ +@@ -68,6 +75,16 @@ struct mld_group { + u16_t timer; + /** counter of simultaneous uses */ + u8_t use; ++ #if LWIP_IPV6_MLD_V2 ++ /** Last report fmode */ ++ u8_t v2_fmode; ++ /** signifies we were the last person to report */ ++ u8_t v2_last_reporter_flag; ++ /** current state of the group */ ++ u8_t v2_group_state; ++ /** timer for reporting, negative is OFF */ ++ u16_t v2_timer; ++#endif /* LWIP_IPV6_MLD_V2 */ + }; + + #define MLD6_TMR_INTERVAL 100 /* Milliseconds */ +@@ -90,6 +107,19 @@ err_t mld6_leavegroup_netif(struct netif *netif, const ip6_addr_t *groupaddr); + */ + #define netif_mld6_data(netif) ((struct mld_group *)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6)) + ++/** ++ * ipv6 multicast filter trigger function ++ */ ++#if LWIP_IPV6_MLD_V2 ++ ++#define MLD6_FMODE_INIT 2 /* Not MCAST_INCLUDE or MCAST_EXCLUDE */ ++void mld6_v2_trigger(struct netif *netif, const ip6_addr_t *groupaddr); ++#define IP6_MC_TRIGGER_CALL(netif, multi_addr) mld6_v2_trigger(netif, multi_addr) ++#else ++#define IP6_MC_TRIGGER_CALL(netif, multi_addr) ++#endif /* !LWIP_IPV6_MLD_V2 */ ++ ++ + #ifdef __cplusplus + } + #endif +diff --git a/src/include/lwip/netbuf.h b/src/include/lwip/netbuf.h +index e32c9bd..e7f0713 100644 +--- a/src/include/lwip/netbuf.h ++++ b/src/include/lwip/netbuf.h +@@ -68,6 +68,9 @@ struct netbuf { + u16_t toport_chksum; + #if LWIP_NETBUF_RECVINFO + ip_addr_t toaddr; ++#if LWIP_IPV6 ++ u8_t hoplim; ++#endif /* LWIP_IPV6 */ + #endif /* LWIP_NETBUF_RECVINFO */ + #endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */ + }; +diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h +index 06303f4..9a1a32d 100644 +--- a/src/include/lwip/opt.h ++++ b/src/include/lwip/opt.h +@@ -3584,6 +3584,14 @@ + #if !defined DHCP6_DEBUG || defined __DOXYGEN__ + #define DHCP6_DEBUG LWIP_DBG_OFF + #endif ++ ++/** ++ * MLD6_DEBUG: Enable debugging in mld6.c. ++ */ ++#if !defined MLD6_DEBUG || defined __DOXYGEN__ ++#define MLD6_DEBUG LWIP_DBG_OFF ++#endif ++ + /** + * @} + */ +diff --git a/src/include/lwip/prot/icmp6.h b/src/include/lwip/prot/icmp6.h +index 36989f6..17186b6 100644 +--- a/src/include/lwip/prot/icmp6.h ++++ b/src/include/lwip/prot/icmp6.h +@@ -70,6 +70,8 @@ enum icmp6_type { + ICMP6_TYPE_MLR = 131, + /** Multicast listener done */ + ICMP6_TYPE_MLD = 132, ++ /** Multicast listener report v2*/ ++ ICMP6_TYPE_MLR2 = 143, + /** Router solicitation */ + ICMP6_TYPE_RS = 133, + /** Router advertisement */ +diff --git a/src/include/lwip/prot/mld6.h b/src/include/lwip/prot/mld6.h +index 71f1dcb..40f1ca6 100644 +--- a/src/include/lwip/prot/mld6.h ++++ b/src/include/lwip/prot/mld6.h +@@ -64,6 +64,85 @@ PACK_STRUCT_END + # include "arch/epstruct.h" + #endif + ++#if LWIP_IPV6_MLD_V2 /* RFC 3810 */ ++/** ++ * MLDv2 query packet format. ++ */ ++#ifdef PACK_STRUCT_USE_INCLUDES ++# include "arch/bpstruct.h" ++#endif ++ PACK_STRUCT_BEGIN ++ struct mld_v2_query { ++ PACK_STRUCT_FLD_8(u8_t type); ++ PACK_STRUCT_FLD_8(u8_t code); ++ PACK_STRUCT_FIELD(u16_t chksum); ++ PACK_STRUCT_FIELD(u16_t max_resp_delay); ++ PACK_STRUCT_FIELD(u16_t reserved); ++ PACK_STRUCT_FLD_S(ip6_addr_t multicast_address); ++ PACK_STRUCT_FLD_8(u8_t resv_s_qrv); ++ PACK_STRUCT_FLD_8(u8_t qqic); ++ PACK_STRUCT_FIELD(u16_t src_num); ++ } PACK_STRUCT_STRUCT; ++ PACK_STRUCT_END ++#ifdef PACK_STRUCT_USE_INCLUDES ++# include "arch/epstruct.h" ++#endif ++ ++#define MLD_V2_QUERY_HLEN sizeof(struct mld_v2_query) ++ ++/** ++ * MLDv2 report packet header format. ++ */ ++#ifdef PACK_STRUCT_USE_INCLUDES ++# include "arch/bpstruct.h" ++#endif ++ PACK_STRUCT_BEGIN ++ struct mld_v2_report { ++ PACK_STRUCT_FLD_8(u8_t type); ++ PACK_STRUCT_FLD_8(u8_t reserved1); ++ PACK_STRUCT_FIELD(u16_t chksum); ++ PACK_STRUCT_FIELD(u16_t reserved2); ++ PACK_STRUCT_FIELD(u16_t mrec_num); ++ } PACK_STRUCT_STRUCT; ++ PACK_STRUCT_END ++#ifdef PACK_STRUCT_USE_INCLUDES ++# include "arch/epstruct.h" ++#endif ++ ++#define MLD_V2_REPORT_HLEN sizeof(struct mld_v2_report) ++ ++/* ++ * Definitions for MLDv2 ++ */ ++#define MLD2_MODE_IS_INCLUDE 1 ++#define MLD2_MODE_IS_EXCLUDE 2 ++#define MLD2_CHANGE_TO_INCLUDE 3 ++#define MLD2_CHANGE_TO_EXCLUDE 4 ++#define MLD2_ALLOW_NEW_SOURCES 5 ++#define MLD2_BLOCK_OLD_SOURCES 6 ++ ++/** ++ * MLDv2 report packet group record format. ++ */ ++#ifdef PACK_STRUCT_USE_INCLUDES ++# include "arch/bpstruct.h" ++#endif ++ PACK_STRUCT_BEGIN ++ struct mld_v2_record { ++ PACK_STRUCT_FLD_8(u8_t type); ++ PACK_STRUCT_FLD_8(u8_t aux_len); ++ PACK_STRUCT_FIELD(u16_t src_num); ++ PACK_STRUCT_FLD_S(ip6_addr_p_t multicast_address); ++ } PACK_STRUCT_STRUCT; ++ PACK_STRUCT_END ++#ifdef PACK_STRUCT_USE_INCLUDES ++# include "arch/epstruct.h" ++#endif ++ ++#define MLD_V2_RECORD_LEN(src_cnt) (sizeof(struct mld_v2_record) + (src_cnt << 4)) ++#endif /* LWIP_IPV6_MLD_V2 */ ++ ++ + #ifdef __cplusplus + } + #endif +diff --git a/src/include/lwip/sockets.h b/src/include/lwip/sockets.h +index 00aca53..f2a1b59 100644 +--- a/src/include/lwip/sockets.h ++++ b/src/include/lwip/sockets.h +@@ -114,8 +114,11 @@ struct sockaddr { + char sa_data[14]; + }; + ++#ifndef LWIP_IPV6_MLD_V2 + struct sockaddr_storage { ++#if !GAZELLE_ENABLE + u8_t s2_len; ++#endif + sa_family_t ss_family; + char s2_data1[2]; + u32_t s2_data2[3]; +@@ -123,6 +126,27 @@ struct sockaddr_storage { + u32_t s2_data3[3]; + #endif /* LWIP_IPV6 */ + }; ++#else ++#ifndef _SS_SIZE ++#define _SS_SIZE 128 ++#endif //_SS_SIZE ++#ifndef __SOCKADDR_COMMON_SIZE ++#define __SOCKADDR_COMMON_SIZE (sizeof (unsigned short int)) ++#endif //__SOCKADDR_COMMON_SIZE ++#ifndef __ss_aligntype ++#define __ss_aligntype unsigned long int ++#endif //__ss_aligntype ++#ifndef _SS_PADSIZE ++#define _SS_PADSIZE \ ++ (_SS_SIZE - __SOCKADDR_COMMON_SIZE - sizeof (__ss_aligntype)) ++#endif //_SS_PADSIZE ++struct sockaddr_storage ++ { ++ sa_family_t ss_family; /* Address family, etc. */ ++ char __ss_padding[_SS_PADSIZE]; ++ __ss_aligntype __ss_align; /* Force desired alignment. */ ++ }; ++#endif //LWIP_IPV6_MLD_V2 + + /* If your port already typedef's socklen_t, define SOCKLEN_T_DEFINED + to prevent this code from redefining it. */ +@@ -335,6 +359,11 @@ struct linger { + */ + #define IPV6_CHECKSUM 7 /* RFC3542: calculate and insert the ICMPv6 checksum for raw sockets. */ + #define IPV6_V6ONLY 27 /* RFC3493: boolean control to restrict AF_INET6 sockets to IPv6 communications only. */ ++#define IPV6_RECVPKTINFO 49 /* Recv IPV6_PKTINFO message */ ++#define IPV6_PKTINFO 50 ++#define IPV6_RECVHOPLIMIT 51 /* Recv IPV6_HOPLIMIT message */ ++#define IPV6_HOPLIMIT 52 ++#define IPV6_MINHOPCNT 99 + #endif /* LWIP_IPV6 */ + + #if LWIP_UDP && LWIP_UDPLITE +@@ -369,13 +398,24 @@ struct in_pktinfo { + }; + #endif /* LWIP_IPV4 */ + ++#if LWIP_IPV6 ++struct in6_pktinfo { ++ struct in6_addr ipi6_addr; ++ int ipi6_ifindex; ++}; ++#endif /* LWIP_IPV6 */ ++ + #if LWIP_IPV6_MLD ++#define IPV6_UNICAST_HOPS 16 /* IPv6 ttl */ ++#define IPV6_MULTICAST_IF 17 /* Set mcast ifindex, (The argument is a pointer to an interface index) */ ++#define IPV6_MULTICAST_HOPS 18 /* IPv6 mcast ttl */ ++#define IPV6_MULTICAST_LOOP 19 /* Mcast loop */ + /* + * Options and types related to IPv6 multicast membership + */ +-#define IPV6_JOIN_GROUP 12 ++#define IPV6_JOIN_GROUP 20 + #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +-#define IPV6_LEAVE_GROUP 13 ++#define IPV6_LEAVE_GROUP 21 + #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP + + typedef struct ipv6_mreq { +@@ -807,6 +847,7 @@ int mcast_sock_block_unblock_source_group(struct ip_mc *ipmc, int optname, c + int mcast_sock_set_groupfilter(struct ip_mc *ipmc, int optname, const struct group_filter *gf); + int mcast_sock_get_groupfilter(struct ip_mc *ipmc, int optname, struct group_filter *gf, socklen_t *size); + ++int mcast_sock_ipv6_add_drop_membership(int s, struct ip_mc *ipmc, int optname, const struct ipv6_mreq *imr); + #endif /*(LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD)*/ + + const char *lwip_inet_ntop(int af, const void *src, char *dst, socklen_t size); +diff --git a/src/include/lwipopts.h b/src/include/lwipopts.h +index ab36688..396bcdf 100644 +--- a/src/include/lwipopts.h ++++ b/src/include/lwipopts.h +@@ -292,4 +292,9 @@ + */ + #define LWIP_IGMP_V3 1 + ++ /** ++ * LWIP_IPV6_MLD_V2==1: Enable multicast listener discovery protocol v2. ++ */ ++#define LWIP_IPV6_MLD_V2 1 ++ + #endif /* __LWIPOPTS_H__ */ +-- +2.25.1 + diff --git a/lwip.spec b/lwip.spec index de0b9fe..226752a 100644 --- a/lwip.spec +++ b/lwip.spec @@ -4,7 +4,7 @@ Summary: lwip is a small independent implementation of the TCP/IP protocol suite Name: lwip Version: 2.2.0 -Release: 14 +Release: 15 License: BSD URL: http://savannah.nongnu.org/projects/lwip/ Source0: http://download.savannah.nongnu.org/releases/lwip/%{name}-%{version}.zip @@ -133,6 +133,7 @@ Patch9117: 0118-support-querying-udp-multicast-addresses.patch Patch9118: 0119-support-igmpv3.patch Patch9119: 0120-enable-SO_REUSE_RXTOALL.patch Patch9120: 0121-add-vlan-filter.patch +Patch9121: 0122-support-mldv2.patch BuildRequires: gcc-c++ dos2unix dpdk-devel @@ -162,6 +163,9 @@ cd %{_builddir}/%{name}-%{version}/src %{_libdir}/liblwip.a %changelog +* Tue Apr 09 2024 wanfeng - 2.2.0-15 +- add ipv6 mldv2 multicast protocol + * Mon Apr 08 2024 zhengjiebing - 2.2.0-14 - add vlan filter