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