1868 lines
59 KiB
Diff
1868 lines
59 KiB
Diff
From bd5283f4d24be4b982652b38a680264b4a3efdf4 Mon Sep 17 00:00:00 2001
|
|
From: wanfeng <wanfeng@kylinos.cn>
|
|
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 <string.h>
|
|
|
|
@@ -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
|
|
|