diff --git a/0115-support-igmpv3.patch b/0115-support-igmpv3.patch new file mode 100644 index 0000000000000000000000000000000000000000..245a0f2dea4337615af3e7a7f0b858c62a28cd71 --- /dev/null +++ b/0115-support-igmpv3.patch @@ -0,0 +1,2386 @@ +diff -Nur lwip-2.2.0init/src/api/api_msg.c lwip-2.2.0/src/api/api_msg.c +--- lwip-2.2.0init/src/api/api_msg.c 2024-03-14 19:14:54.616020996 +0800 ++++ lwip-2.2.0/src/api/api_msg.c 2024-03-14 19:15:36.789020996 +0800 +@@ -192,9 +192,19 @@ + + buf->p = q; + buf->ptr = q; +- ip_addr_copy(buf->addr, *ip_current_src_addr()); ++ ip_addr_copy(buf->addr, *addr); + buf->port = pcb->protocol; + ++#if LWIP_NETBUF_RECVINFO ++ if (conn->flags & NETCONN_FLAG_PKTINFO) { ++ buf->flags = NETBUF_FLAG_DESTADDR; ++ /* If we use mroute this packet maybe a mroute fake packet so we must get ipv4/ipv6 header */ ++ if (IP_IS_V4(addr)) { ++ struct ip_hdr *iphdr = (struct ip_hdr *)p->payload; ++ ip_addr_copy_from_ip4(buf->toaddr, iphdr->dest); ++ } else {} ++#endif /* LWIP_NETBUF_RECVINFO */ ++ + len = q->tot_len; + if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) { + netbuf_delete(buf); +@@ -2071,6 +2081,7 @@ + + msg->err = ERR_CONN; + if (msg->conn->pcb.tcp != NULL) { ++#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 +@@ -2082,25 +2093,33 @@ + 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 ++ } else { + #endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ +- { +-#if LWIP_IGMP +- if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { +- msg->err = igmp_joingroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)), +- ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr))); +- } else { +- msg->err = igmp_leavegroup(ip_2_ip4(API_EXPR_REF(msg->msg.jl.netif_addr)), +- ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr))); +- } +-#endif /* LWIP_IGMP */ ++#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 */ +-#if (LWIP_TCP || LWIP_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_group(&msg->conn->pcb.raw->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.raw->ipmc, API_EXPR_REF(msg->msg.jl.netif_addr), API_EXPR_REF(msg->msg.jl.multiaddr), NULL); ++ } ++#else ++ msg->err = ERR_VAL; ++#endif /* LWIP_RAW */ + } else { + msg->err = ERR_VAL; +-#endif /* (LWIP_TCP || LWIP_RAW) */ + } ++#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ + } + TCPIP_APIMSG_ACK(msg); + } +@@ -2124,6 +2143,7 @@ + + msg->err = ERR_CONN; + if (msg->conn->pcb.tcp != NULL) { ++#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 +@@ -2137,23 +2157,32 @@ + } + } else + #endif /* LWIP_IPV6 && LWIP_IPV6_MLD */ +- { +-#if LWIP_IGMP +- if (msg->msg.jl.join_or_leave == NETCONN_JOIN) { +- msg->err = igmp_joingroup_netif(netif, +- ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr))); +- } else { +- msg->err = igmp_leavegroup_netif(netif, +- ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr))); +- } +-#endif /* LWIP_IGMP */ ++ { ++#if LWIP_IGMP ++ 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 */ +-#if (LWIP_TCP || LWIP_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); ++ } else { ++ msg->err = mcast_leave_netif(&msg->conn->pcb.raw->ipmc, netif, API_EXPR_REF(msg->msg.jl.multiaddr), NULL); ++ } ++#else ++ msg->err = ERR_VAL; ++#endif /* LWIP_RAW */ + } else { + msg->err = ERR_VAL; +-#endif /* (LWIP_TCP || LWIP_RAW) */ + } ++#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */ + } + + done: +diff -Nur lwip-2.2.0init/src/api/sockets.c lwip-2.2.0/src/api/sockets.c +--- lwip-2.2.0init/src/api/sockets.c 2024-03-14 19:14:54.621020996 +0800 ++++ lwip-2.2.0/src/api/sockets.c 2024-03-14 19:15:36.790020996 +0800 +@@ -293,16 +293,16 @@ + struct lwip_socket_multicast_pair { + /** the socket */ + struct lwip_sock *sock; +- /** the interface address */ +- ip4_addr_t if_addr; ++ /** the interface index */ ++ u8_t if_idx; + /** the group address */ + ip4_addr_t multi_addr; + }; + + static struct lwip_socket_multicast_pair socket_ipv4_multicast_memberships[LWIP_SOCKET_MAX_MEMBERSHIPS]; + +-static int lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr); +-static void lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr); ++static int lwip_socket_register_membership(int s, unsigned int if_idx, const ip4_addr_t *multi_addr); ++static void lwip_socket_unregister_membership(int s, unsigned int if_idx, const ip4_addr_t *multi_addr); + static void lwip_socket_drop_registered_memberships(int s); + #endif /* LWIP_IGMP */ + +@@ -3841,32 +3841,24 @@ + #endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP */ + #if LWIP_IGMP + case IP_ADD_MEMBERSHIP: +- case IP_DROP_MEMBERSHIP: { ++ case IP_DROP_MEMBERSHIP: + /* If this is a TCP or a RAW socket, ignore these options. */ +- err_t igmp_err; +- const struct ip_mreq *imr = (const struct ip_mreq *)optval; +- ip4_addr_t if_addr; +- ip4_addr_t multi_addr; +- LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ip_mreq, NETCONN_UDP); +- inet_addr_to_ip4addr(&if_addr, &imr->imr_interface); +- inet_addr_to_ip4addr(&multi_addr, &imr->imr_multiaddr); +- if (optname == IP_ADD_MEMBERSHIP) { +- if (!lwip_socket_register_membership(s, &if_addr, &multi_addr)) { +- /* cannot track membership (out of memory) */ +- err = ENOMEM; +- igmp_err = ERR_OK; +- } else { +- igmp_err = igmp_joingroup(&if_addr, &multi_addr); +- } +- } else { +- igmp_err = igmp_leavegroup(&if_addr, &multi_addr); +- lwip_socket_unregister_membership(s, &if_addr, &multi_addr); +- } +- if (igmp_err != ERR_OK) { +- err = EADDRNOTAVAIL; +- } +- } +- break; ++ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, struct ip_mreq); ++#if LWIP_UDP ++ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_UDP) { ++ err = mcast_sock_add_drop_membership(s, &sock->conn->pcb.udp->ipmc, optname, (const struct ip_mreq *)optval); ++ } else ++#endif /* LWIP_UDP */ ++#if LWIP_RAW ++ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_RAW) { ++ err = mcast_sock_add_drop_membership(s, &sock->conn->pcb.raw->ipmc, optname, (const struct ip_mreq *)optval); ++ } else ++#endif /* LWIP_RAW */ ++ { ++ done_socket(sock); ++ return ENOPROTOOPT; ++ } ++ break; + #endif /* LWIP_IGMP */ + default: + LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", +@@ -4349,7 +4341,7 @@ + * @return 1 on success, 0 on failure + */ + static int +-lwip_socket_register_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr) ++lwip_socket_register_membership(int s, unsigned int if_idx, const ip4_addr_t *multi_addr) + { + struct lwip_sock *sock = get_socket(s); + int i; +@@ -4359,9 +4351,15 @@ + } + + for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) { ++ if ((socket_ipv4_multicast_memberships[i].sock == sock) && ++ (socket_ipv4_multicast_memberships[i].if_idx == if_idx) && ++ ip4_addr_cmp(&socket_ipv4_multicast_memberships[i].multi_addr, multi_addr)) { ++ done_socket(sock); ++ return 1; ++ } + if (socket_ipv4_multicast_memberships[i].sock == NULL) { + socket_ipv4_multicast_memberships[i].sock = sock; +- ip4_addr_copy(socket_ipv4_multicast_memberships[i].if_addr, *if_addr); ++ socket_ipv4_multicast_memberships[i].if_idx = if_idx; + ip4_addr_copy(socket_ipv4_multicast_memberships[i].multi_addr, *multi_addr); + done_socket(sock); + return 1; +@@ -4377,7 +4375,7 @@ + * ATTENTION: this function is called from tcpip_thread (or under CORE_LOCK). + */ + static void +-lwip_socket_unregister_membership(int s, const ip4_addr_t *if_addr, const ip4_addr_t *multi_addr) ++lwip_socket_unregister_membership(int s, unsigned int if_idx, const ip4_addr_t *multi_addr) + { + struct lwip_sock *sock = get_socket(s); + int i; +@@ -4388,10 +4386,10 @@ + + for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) { + if ((socket_ipv4_multicast_memberships[i].sock == sock) && +- ip4_addr_eq(&socket_ipv4_multicast_memberships[i].if_addr, if_addr) && +- ip4_addr_eq(&socket_ipv4_multicast_memberships[i].multi_addr, multi_addr)) { ++ (socket_ipv4_multicast_memberships[i].if_idx == if_idx) && ++ ip4_addr_cmp(&socket_ipv4_multicast_memberships[i].multi_addr, multi_addr)) { + socket_ipv4_multicast_memberships[i].sock = NULL; +- ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr); ++ socket_ipv4_multicast_memberships[i].if_idx = NETIF_NO_INDEX; + ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr); + break; + } +@@ -4415,14 +4413,16 @@ + + for (i = 0; i < LWIP_SOCKET_MAX_MEMBERSHIPS; i++) { + if (socket_ipv4_multicast_memberships[i].sock == sock) { +- ip_addr_t multi_addr, if_addr; ++ ip_addr_t multi_addr; ++ u8_t if_idx; + ip_addr_copy_from_ip4(multi_addr, socket_ipv4_multicast_memberships[i].multi_addr); +- ip_addr_copy_from_ip4(if_addr, socket_ipv4_multicast_memberships[i].if_addr); ++ if_idx = socket_ipv4_multicast_memberships[i].if_idx; ++ ++ socket_ipv4_multicast_memberships[i].if_idx = NETIF_NO_INDEX; + socket_ipv4_multicast_memberships[i].sock = NULL; +- ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].if_addr); + ip4_addr_set_zero(&socket_ipv4_multicast_memberships[i].multi_addr); + +- netconn_join_leave_group(sock->conn, &multi_addr, &if_addr, NETCONN_LEAVE); ++ netconn_join_leave_group_netif(sock->conn, &multi_addr, if_idx, NETCONN_LEAVE); + } + } + done_socket(sock); +@@ -4447,6 +4447,13 @@ + } + + 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; ++ } ++ + 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; +@@ -4477,7 +4484,7 @@ + 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_eq(&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); +@@ -4520,6 +4527,154 @@ + } + #endif /* LWIP_IPV6_MLD */ + ++/** Remove multicast filter when pcb remove ++ * ++ * @param ipmc multicast filter control block ++ */ ++void ++mcast_pcb_remove(struct ip_mc *ipmc) ++{ ++#if LWIP_IPV4 && LWIP_IGMP ++ { ++ struct ip4_mc *mc, *next; ++ ++ mc = ipmc->mc4; ++ while (mc) { ++ next = mc->next; ++ mcast_ip4_mc_src_remove(mc->src); ++ igmp_leavegroup(&mc->if_addr, &mc->multi_addr); ++ mem_free(mc); ++ mc = next; ++ } ++ } ++#endif /* LWIP_IPV4 && LWIP_IGMP */ ++} ++ ++/** Common code to see if the current input multicast packet matches the pcb ++ * (current input packet is accessed via ip(4/6)_current_* macros) ++ * ++ * @param ipmc multicast filter control block ++ * @param inp network interface on which the datagram was received (only used for IPv4) ++ * @return 1 on match, 0 otherwise ++ */ ++u8_t ++mcast_input_local_match(struct ip_mc *ipmc, struct netif *inp) ++{ ++#if LWIP_IPV4 && LWIP_IGMP ++ if (!ip_current_is_v6()) { ++ struct igmp_src *src; ++ struct ip4_mc *mc; ++ ip4_addr_t *multi_addr = ip4_current_dest_addr(); ++ ip4_addr_t *src_addr = ip4_current_src_addr(); ++ ++ IP4_MC_FOREACH(ipmc, mc) { ++ if (ip4_addr_cmp(&mc->if_addr, netif_ip4_addr(inp)) && ++ ip4_addr_cmp(&mc->multi_addr, multi_addr)) { ++ if (mc->fmode == MCAST_EXCLUDE) { ++ IP4_MC_SRC_FOREACH(mc, src) { ++ if (ip4_addr_cmp(&src->src_addr, src_addr)) { ++ return 0; ++ } ++ } ++ return 1; /* MCAST_EXCLUDE src_addr must not in src list */ ++ ++ } else { /* MCAST_INCLUDE */ ++ IP4_MC_SRC_FOREACH(mc, src) { ++ if (ip4_addr_cmp(&src->src_addr, src_addr)) { ++ return 1; ++ } ++ } ++ return 0; /* MCAST_INCLUDE src_addr must in src list */ ++ } ++ } ++ } ++ } else {} ++#endif /* LWIP_IPV4 && LWIP_IGMP */ ++ return 0; ++} ++ ++#if (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) ++/** ++ * setsockopt() IP_ADD_MEMBERSHIP / IP_DROP_MEMBERSHIP command ++ */ ++int ++mcast_sock_add_drop_membership(int s, struct ip_mc *ipmc, int optname, const struct ip_mreq *imr) ++{ ++ struct netif *netif; ++ ip_addr_t if_addr; ++ ip_addr_t multi_addr; ++ err_t err; ++ u8_t if_idx; ++ ++ inet_addr_to_ip4addr(ip_2_ip4(&if_addr), &imr->imr_interface); ++ inet_addr_to_ip4addr(ip_2_ip4(&multi_addr), &imr->imr_multiaddr); ++ IP_SET_TYPE_VAL(if_addr, IPADDR_TYPE_V4); ++ IP_SET_TYPE_VAL(multi_addr, IPADDR_TYPE_V4); ++ ++ if (!ip4_addr_ismulticast(ip_2_ip4(&multi_addr))) { ++ return EADDRNOTAVAIL; ++ } ++ ++ if (ip4_addr_isany(ip_2_ip4(&if_addr))) { /* To all network interface */ ++ if (optname == IP_ADD_MEMBERSHIP) { ++ NETIF_FOREACH(netif) { ++ if_idx = netif_get_index(netif); ++ if (!lwip_socket_register_membership(s, if_idx, ip_2_ip4(&multi_addr))) { ++ /* cannot track membership (out of memory) */ ++ err = ENOMEM; ++ goto out; ++ } ++ } ++ ++ err = mcast_join_group(ipmc, &if_addr, &multi_addr, NULL); ++ } else { ++ err = mcast_leave_group(ipmc, &if_addr, &multi_addr, NULL); ++ NETIF_FOREACH(netif) { ++ if_idx = netif_get_index(netif); ++ lwip_socket_unregister_membership(s, if_idx, ip_2_ip4(&multi_addr)); ++ } ++ } ++ ++ } else { /* To specified network interface */ ++ if (!(ip_2_ip4(&if_addr)->addr & PP_HTONL(IP_CLASSA_NET))) { /* IS a BSD style index ? */ ++ u8_t idx = (u8_t)PP_NTOHL(ip_2_ip4(&if_addr)->addr); ++ netif = netif_get_by_index(idx); ++ if (netif == NULL) { ++ return ENXIO; ++ } ++ *ip_2_ip4(&if_addr) = *netif_ip4_addr(netif); ++ ++ } else { ++ NETIF_FOREACH(netif) { ++ if (ip4_addr_cmp(ip_2_ip4(&if_addr), netif_ip4_addr(netif))) { ++ break; ++ } ++ } ++ if (netif == NULL) { ++ return ENXIO; ++ } ++ } ++ ++ if (optname == IP_ADD_MEMBERSHIP) { ++ if_idx = netif_get_index(netif); ++ if (!lwip_socket_register_membership(s, if_idx, ip_2_ip4(&multi_addr))) { ++ /* cannot track membership (out of memory) */ ++ err = ENOMEM; ++ goto out; ++ } ++ err = mcast_join_netif(ipmc, netif, &multi_addr, NULL); ++ } else { ++ err = mcast_leave_netif(ipmc, netif, &multi_addr, NULL); ++ if_idx = netif_get_index(netif); ++ lwip_socket_unregister_membership(s, if_idx, ip_2_ip4(&multi_addr)); ++ } ++ } ++ ++out: ++ return (u8_t)err_to_errno(err); ++} ++#endif /* (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD)*/ ++ + #if GAZELLE_ENABLE + void lwip_sock_init(void) + { +diff -Nur lwip-2.2.0init/src/core/dir.mk lwip-2.2.0/src/core/dir.mk +--- lwip-2.2.0init/src/core/dir.mk 2024-03-14 19:14:54.544020996 +0800 ++++ lwip-2.2.0/src/core/dir.mk 2024-03-14 19:15:36.790020996 +0800 +@@ -3,6 +3,6 @@ + ipv4/icmp.c ipv4/ip4_addr.c ipv4/ip4_frag.c ipv4/etharp.c \ + ipv4/ip4.c ipv4/igmp.c ipv6/icmp6.c ipv6/ip6_addr.c ipv6/ip6_frag.c \ + ipv6/ethip6.c ipv6/ip6.c ipv6/dhcp6.c ipv6/inet6.c \ +- ipv6/mld6.c ipv6/nd6.c ++ ipv6/mld6.c ipv6/nd6.c mcast.c + + $(eval $(call register_dir, core, $(SRC))) +diff -Nur lwip-2.2.0init/src/core/ipv4/igmp.c lwip-2.2.0/src/core/ipv4/igmp.c +--- lwip-2.2.0init/src/core/ipv4/igmp.c 2024-03-14 19:14:53.317020996 +0800 ++++ lwip-2.2.0/src/core/ipv4/igmp.c 2024-03-14 19:17:36.739020996 +0800 +@@ -93,6 +93,7 @@ + #include "lwip/netif.h" + #include "lwip/stats.h" + #include "lwip/prot/igmp.h" ++#include "lwip/sockets.h" + + #include + +@@ -104,9 +105,21 @@ + static err_t igmp_ip_output_if(struct pbuf *p, const ip4_addr_t *src, const ip4_addr_t *dest, struct netif *netif); + static void igmp_send(struct netif *netif, struct igmp_group *group, u8_t type); + ++#if LWIP_IGMP_V3 ++static void igmp_v3_timeout(struct netif *netif, struct igmp_group *group); ++static void igmp_v3_start_timer(struct igmp_group *group, u8_t max_time); ++static void igmp_v3_delaying_member(struct igmp_group *group, u8_t maxresp); ++static void igmp_v3_send(struct netif *netif, struct igmp_group *group, u8_t type); ++static void igmp_v3_send_allgroups(struct netif *netif); ++#endif /* LWIP_IGMP_V3 */ ++ + static ip4_addr_t allsystems; + static ip4_addr_t allrouters; + ++#if LWIP_IGMP_V3 ++static ip4_addr_t v3_routers; ++#endif /* LWIP_IGMP_V3 */ ++ + /** + * Initialize the IGMP module + */ +@@ -117,6 +130,10 @@ + + IP4_ADDR(&allsystems, 224, 0, 0, 1); + IP4_ADDR(&allrouters, 224, 0, 0, 2); ++ ++#if LWIP_IGMP_V3 ++ IP4_ADDR(&v3_routers, 224, 0, 0, 22); ++#endif /* LWIP_IGMP_V3 */ + } + + /** +@@ -136,6 +153,9 @@ + if (group != NULL) { + group->group_state = IGMP_GROUP_IDLE_MEMBER; + group->use++; ++#if LWIP_IGMP_V3 ++ group->v3_group_state = IGMP_GROUP_IDLE_MEMBER; ++#endif /* LWIP_IGMP_V3 */ + + /* Allow the igmp messages at the MAC level */ + if (netif->igmp_mac_filter != NULL) { +@@ -204,6 +224,15 @@ + igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR); + group = group->next; + } ++ ++#if LWIP_IGMP_V3 ++ group = netif_igmp_data(netif); ++ ++ if (group) { ++ /* We use first group to determine report all groups */ ++ igmp_v3_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR); ++ } ++#endif /* LWIP_IGMP_V3 */ + } + + /** +@@ -261,6 +290,12 @@ + group->group_state = IGMP_GROUP_NON_MEMBER; + group->last_reporter_flag = 0; + group->use = 0; ++#if LWIP_IGMP_V3 ++ group->v3_fmode = IGMP_FMODE_INIT; /* Non mode with init-stat */ ++ group->v3_timer = 0; /* Not running */ ++ group->v3_group_state = IGMP_GROUP_NON_MEMBER; ++ group->v3_last_reporter_flag = 0; ++#endif /* LWIP_IGMP_V3 */ + + /* Ensure allsystems group is always first in list */ + if (list_head == NULL) { +@@ -365,6 +400,71 @@ + /* NOW ACT ON THE INCOMING MESSAGE TYPE... */ + switch (igmp->igmp_msgtype) { + case IGMP_MEMB_QUERY: ++#if LWIP_IGMP_V3 ++ if (p->len >= IGMP_V3_MINLEN) { /* this is a igmp v3 query packet */ ++ struct igmp_v3_query *igmp_v3 = (struct igmp_v3_query *)p->payload; ++ ++ if ((ip4_addr_cmp(dest, &allsystems)) && ip4_addr_isany(&igmp_v3->igmp_v3_group_address)) { ++ /* THIS IS THE GENERAL QUERY */ ++ groupref = netif_igmp_data(inp); ++ ++ if (groupref) { ++ /* We use first group to determine report all groups */ ++ igmp_v3_delaying_member(groupref, igmp_v3->igmp_v3_maxresp); ++ } ++ ++ } else { ++ /* IGMP_MEMB_QUERY to a specific group ? */ ++ if (!ip4_addr_isany(&igmp_v3->igmp_v3_group_address)) { ++ u16_t src_cnt = PP_NTOHS(igmp_v3->igmp_v3_srccnt); ++ u16_t src_buf_size = src_cnt << 2; ++ u8_t need_free; ++ ip4_addr_p_t *src_buf; ++ ++ if (p->tot_len < (IGMP_V3_QUERY_HLEN + src_buf_size)) { ++ /* packet len error */ ++ pbuf_free(p); ++ IGMP_STATS_INC(igmp.lenerr); ++ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n")); ++ return; ++ } ++ ++ if (src_cnt) { /* Seach interest */ ++ if (p->len < (IGMP_V3_QUERY_HLEN + src_buf_size)) { /* Unfortunately! the source address memory is not contiguous */ ++ src_buf = (ip4_addr_p_t *)mem_malloc(src_buf_size); ++ if (src_buf == NULL) { ++ pbuf_free(p); ++ IGMP_STATS_INC(igmp.memerr); ++ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: not enough memory for igmp_input\n")); ++ return; ++ } ++ need_free = 1; ++ pbuf_copy_partial(p, src_buf, src_buf_size, IGMP_V3_QUERY_HLEN); ++ ++ } else { ++ need_free = 0; ++ src_buf = (ip4_addr_p_t *)((u8_t *)p->payload + IGMP_V3_QUERY_HLEN); ++ } ++ ++ LWIP_ASSERT("igmp_v3_query packet source address array not aligned!", !((mem_ptr_t)src_buf & 0x3)); ++ ip4_addr_t igmp_v3_group_address; ++ memcpy(&igmp_v3_group_address, &igmp_v3->igmp_v3_group_address, sizeof(igmp_v3_group_address)); ++ if (mcast_ip4_filter_interest(inp, (const ip4_addr_t *)&igmp_v3_group_address, src_buf, src_cnt)) { ++ /* We interest! */ ++ igmp_v3_delaying_member(group, igmp_v3->igmp_v3_maxresp); ++ } ++ ++ if (need_free) { ++ mem_free(src_buf); ++ } ++ ++ } else { /* Report! */ ++ igmp_v3_delaying_member(group, igmp_v3->igmp_v3_maxresp); ++ } ++ } ++ } ++ } ++#endif /* LWIP_IGMP_V3 */ + /* IGMP_MEMB_QUERY to the "all systems" address ? */ + if ((ip4_addr_eq(dest, &allsystems)) && ip4_addr_isany(&igmp->igmp_group_address)) { + /* THIS IS THE GENERAL QUERY */ +@@ -523,6 +623,16 @@ + + /* Need to work out where this timer comes from */ + group->group_state = IGMP_GROUP_DELAYING_MEMBER; ++ ++ ++#if LWIP_IGMP_V3 ++ igmp_v3_send(netif, group, IGMP_V3_MEMB_REPORT); ++ ++ igmp_v3_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR); ++ ++ /* Need to work out where this timer comes from */ ++ group->v3_group_state = IGMP_GROUP_DELAYING_MEMBER; ++#endif /* LWIP_IGMP_V3 */ + } + /* Increment group use */ + group->use++; +@@ -612,6 +722,13 @@ + igmp_send(netif, group, IGMP_LEAVE_GROUP); + } + ++#if LWIP_IGMP_V3 ++ /* If we are the last reporter for this group */ ++ if (group->v3_last_reporter_flag) { ++ igmp_v3_send(netif, group, IGMP_LEAVE_GROUP); ++ } ++#endif /* LWIP_IGMP_V3 */ ++ + /* Disable the group at the MAC level */ + if (netif->igmp_mac_filter != NULL) { + LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup_netif: igmp_mac_filter(DEL ")); +@@ -654,6 +771,19 @@ + } + group = group->next; + } ++ ++#if LWIP_IGMP_V3 ++ group = netif_igmp_data(netif); ++ while (group != NULL) { ++ if (group->v3_timer > 0) { ++ group->v3_timer--; ++ if (group->v3_timer == 0) { ++ igmp_v3_timeout(netif, group); ++ } ++ } ++ group = group->next; ++ } ++#endif /* LWIP_IGMP_V3 */ + } + } + +@@ -681,6 +811,36 @@ + } + } + ++#if LWIP_IGMP_V3 ++/** ++ * Called if a timeout for one group is reached. ++ * Sends a report for this group. ++ * ++ * @param group an igmp_group for which a timeout is reached ++ */ ++static void ++igmp_v3_timeout(struct netif *netif, struct igmp_group *group) ++{ ++ /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group ++ (if it is the allsystems group to all groups) */ ++ if (group->v3_group_state == IGMP_GROUP_DELAYING_MEMBER) { ++ group->group_state = IGMP_GROUP_IDLE_MEMBER; ++ if (ip4_addr_cmp(&(group->group_address), &allsystems)) { ++ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_v3_timeout: report all membership\n")); ++ IGMP_STATS_INC(igmp.tx_report); ++ igmp_v3_send_allgroups(netif); ++ ++ } else { ++ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_v3_timeout: report membership for group with address ")); ++ ip4_addr_debug_print(IGMP_DEBUG, &(group->group_address)); ++ LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", (void *)netif)); ++ IGMP_STATS_INC(igmp.tx_report); ++ igmp_v3_send(netif, group, IGMP_V3_MEMB_REPORT); ++ } ++ } ++} ++#endif /* LWIP_IGMP_V3 */ ++ + /** + * Start a timer for an igmp group + * +@@ -703,6 +863,51 @@ + } + } + ++#if LWIP_IGMP_V3 ++/** ++ * Start a timer for an igmp group ++ * ++ * @param group the igmp_group for which to start a timer ++ * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with ++ * every call to igmp_tmr()) ++ */ ++static void ++igmp_v3_start_timer(struct igmp_group *group, u8_t max_time) ++{ ++ /* ++ * If Max Resp Code < 128, Max Resp Time = Max Resp Code ++ * ++ * If Max Resp Code >= 128, Max Resp Code represents a floating-point ++ * value as follows: ++ * ++ * 0 1 2 3 4 5 6 7 ++ * +-+-+-+-+-+-+-+-+ ++ * |1| exp | mant | ++ * +-+-+-+-+-+-+-+-+ ++ * ++ * Max Resp Time = (mant | 0x10) << (exp + 3) ++ */ ++ u16_t delay; ++ ++ if (max_time < 128) { ++ delay = max_time; ++ } else { ++ delay = ((max_time & 0xf) | 0x10) << (((max_time >> 4) & 7) + 3); ++ } ++ ++#ifdef LWIP_RAND ++ group->v3_timer = (u16_t)(delay > 2 ? (LWIP_RAND() % delay) : 1); ++#else /* LWIP_RAND */ ++ /* ATTENTION: use this only if absolutely necessary! */ ++ group->v3_timer = delay / 2; ++#endif /* LWIP_RAND */ ++ ++ if (group->v3_timer == 0) { ++ group->v3_timer = 1; ++ } ++} ++#endif /* LWIP_IGMP_V3 */ ++ + /** + * Delaying membership report for a group if necessary + * +@@ -721,6 +926,33 @@ + } + + ++#if LWIP_IGMP_V3 ++/** ++ * Delaying membership report for a group if necessary ++ * ++ * @param group the igmp_group for which "delaying" membership report ++ * @param maxresp query delay ++ */ ++static void ++igmp_v3_delaying_member(struct igmp_group *group, u8_t maxresp) ++{ ++ u16_t delay; ++ ++ if (maxresp < 128) { ++ delay = maxresp; ++ } else { ++ delay = ((maxresp & 0xf) | 0x10) << (((maxresp >> 4) & 7) + 3); ++ } ++ ++ if ((group->v3_group_state == IGMP_GROUP_IDLE_MEMBER) || ++ ((group->v3_group_state == IGMP_GROUP_DELAYING_MEMBER) && ++ ((group->v3_timer == 0) || (delay < group->timer)))) { ++ igmp_v3_start_timer(group, maxresp); ++ group->v3_group_state = IGMP_GROUP_DELAYING_MEMBER; ++ } ++} ++#endif /* LWIP_IGMP_V3 */ ++ + /** + * Sends an IP packet on a network interface. This function constructs the IP header + * and calculates the IP header checksum. If the source IP address is NULL, +@@ -798,4 +1030,220 @@ + } + } + ++#if LWIP_IGMP_V3 ++/** ++ * Build a igmp v3 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 ++igmp_v3_build_record(struct igmp_v3_record *rec, struct igmp_group *group, u8_t fmode, ip4_addr_p_t *src_array, u32_t src_cnt) ++{ ++ ip4_addr_p_t *src_copy; ++ ++ if (fmode == MCAST_EXCLUDE) { ++ if (group->v3_fmode != MCAST_EXCLUDE) { ++ group->v3_fmode = MCAST_EXCLUDE; ++ rec->igmp_v3_rc_type = IGMP_V3_REC_TO_EX; ++ } else { ++ rec->igmp_v3_rc_type = IGMP_V3_REC_IS_EX; ++ } ++ ++ } else { ++ if (group->v3_fmode != MCAST_INCLUDE) { ++ group->v3_fmode = MCAST_INCLUDE; ++ rec->igmp_v3_rc_type = IGMP_V3_REC_TO_IN; ++ } else { ++ rec->igmp_v3_rc_type = IGMP_V3_REC_IS_IN; ++ } ++ } ++ ++ rec->igmp_v3_rc_auxlen = 0; ++ rec->igmp_v3_rc_srccnt = PP_HTONS(src_cnt); ++ ip4_addr_set(&rec->igmp_v3_rc_group_address, &group->group_address); ++ ++ if (src_cnt) { ++ src_copy = (ip4_addr_p_t *)(rec + 1); ++ MEMCPY(src_copy, src_array, (src_cnt << 2)); ++ } ++} ++ ++/** ++ * Send an igmp v3 packet to a specific group. ++ * ++ * @param group the group to which to send the packet ++ * @param type the type of igmp packet to send ++ */ ++static void ++igmp_v3_send(struct netif *netif, struct igmp_group *group, u8_t type) ++{ ++ struct igmp_v3_report *rep; ++ struct igmp_v3_record *rec; ++ ip4_addr_t src; ++ struct pbuf *p; ++ ++ ip4_addr_copy(src, *netif_ip4_addr(netif)); ++ ++ if (type == IGMP_LEAVE_GROUP) { /* Leave from a group */ ++ p = pbuf_alloc(PBUF_TRANSPORT, IGMP_V3_REPORT_HLEN + IGMP_V3_RECORD_LEN(0), PBUF_RAM); ++ if (p == NULL) { ++ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_v3_send: not enough memory for igmp_v3_send\n")); ++ IGMP_STATS_INC(igmp.memerr); ++ return; ++ } ++ ++ rep = (struct igmp_v3_report *)p->payload; ++ rep->igmp_v3_msgtype = IGMP_V3_MEMB_REPORT; ++ rep->igmp_v3_reserve1 = 0; ++ rep->igmp_v3_checksum = 0; ++ rep->igmp_v3_reserve2 = 0; ++ rep->igmp_v3_reccnt = PP_HTONS(1); ++ ++ rec = (struct igmp_v3_record *)(rep + 1); ++ if (group->v3_fmode == MCAST_EXCLUDE) { ++ rec->igmp_v3_rc_type = IGMP_V3_REC_TO_IN; ++ } else { ++ rec->igmp_v3_rc_type = IGMP_V3_REC_IS_IN; ++ } ++ rec->igmp_v3_rc_auxlen = 0; ++ rec->igmp_v3_rc_srccnt = 0; /* IS_IN (NULL) mean drop group */ ++ ip4_addr_set(&rec->igmp_v3_rc_group_address, &group->group_address); ++ rep->igmp_v3_checksum = inet_chksum(rep, IGMP_V3_REPORT_HLEN + IGMP_V3_RECORD_LEN(0)); ++ ++ } else { /* Report a group */ ++ ip4_addr_p_t src_array[LWIP_MCAST_SRC_TBL_SIZE]; ++ u16_t src_cnt; ++ u8_t fmode; ++ ++ src_cnt = mcast_ip4_filter_info(netif, &group->group_address, src_array, LWIP_MCAST_SRC_TBL_SIZE, &fmode); ++ LWIP_ASSERT("igmp_v3_send: multicast filter error!", !(!src_cnt && (fmode == MCAST_INCLUDE))); ++ ++ p = pbuf_alloc(PBUF_TRANSPORT, IGMP_V3_REPORT_HLEN + IGMP_V3_RECORD_LEN(src_cnt), PBUF_RAM); ++ if (p == NULL) { ++ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_v3_send: not enough memory for igmp_v3_send\n")); ++ IGMP_STATS_INC(igmp.memerr); ++ return; ++ } ++ ++ rep = (struct igmp_v3_report *)p->payload; ++ rep->igmp_v3_msgtype = IGMP_V3_MEMB_REPORT; ++ rep->igmp_v3_reserve1 = 0; ++ rep->igmp_v3_checksum = 0; ++ rep->igmp_v3_reserve2 = 0; ++ rep->igmp_v3_reccnt = PP_HTONS(1); ++ ++ rec = (struct igmp_v3_record *)(rep + 1); ++ igmp_v3_build_record(rec, group, fmode, src_array, src_cnt); ++ rep->igmp_v3_checksum = inet_chksum(rep, IGMP_V3_REPORT_HLEN + IGMP_V3_RECORD_LEN(src_cnt)); ++ group->v3_last_reporter_flag = 1; /* Remember we were the last to report */ ++ } ++ ++ igmp_ip_output_if(p, &src, &v3_routers, netif); ++ pbuf_free(p); ++} ++ ++/** ++ * Send igmp v3 packet to report all groups. ++ */ ++static void ++igmp_v3_send_allgroups(struct netif *netif) ++{ ++ struct igmp_group *group; ++ struct igmp_v3_report *rep; ++ struct igmp_v3_record *rec; ++ struct pbuf *p, *p_rec; ++ ip4_addr_t src; ++ ip4_addr_p_t src_array[LWIP_MCAST_SRC_TBL_SIZE]; ++ u16_t src_cnt; ++ u16_t max_pkt_len = (u16_t)(netif->mtu ? netif->mtu : 0xffff); ++ u8_t fmode; ++ ++ ip4_addr_copy(src, *netif_ip4_addr(netif)); ++ ++ group = netif_igmp_data(netif); ++ if (group) { ++ group = group->next; /* do not report allsystem group */ ++ } ++ ++ if (!group) { ++ return; /* no group */ ++ } ++ ++ src_cnt = mcast_ip4_filter_info(netif, &group->group_address, src_array, LWIP_MCAST_SRC_TBL_SIZE, &fmode); ++ LWIP_ASSERT("igmp_v3_send: multicast filter error!", !(!src_cnt && (fmode == MCAST_INCLUDE))); ++ ++ while (group) { ++ p = pbuf_alloc(PBUF_TRANSPORT, IGMP_V3_REPORT_HLEN + IGMP_V3_RECORD_LEN(src_cnt), PBUF_RAM); ++ if (p == NULL) { ++ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_v3_send_allgroups: not enough memory for igmp_v3_send_allgroups\n")); ++ IGMP_STATS_INC(igmp.memerr); ++ return; ++ } ++ ++ rep = (struct igmp_v3_report *)p->payload; ++ rep->igmp_v3_msgtype = IGMP_V3_MEMB_REPORT; ++ rep->igmp_v3_reserve1 = 0; ++ rep->igmp_v3_checksum = 0; ++ rep->igmp_v3_reserve2 = 0; ++ rep->igmp_v3_reccnt = PP_HTONS(1); ++ ++ rec = (struct igmp_v3_record *)(rep + 1); ++ igmp_v3_build_record(rec, group, fmode, src_array, src_cnt); ++ group->v3_last_reporter_flag = 1; /* Remember we were the last to report */ ++ ++cat_rec: ++ group = group->next; /* the next group */ ++ if (group) { ++ src_cnt = mcast_ip4_filter_info(netif, &group->group_address, src_array, LWIP_MCAST_SRC_TBL_SIZE, &fmode); ++ LWIP_ASSERT("igmp_v3_send: multicast filter error!", !(!src_cnt && (fmode == MCAST_INCLUDE))); ++ ++ if (p->tot_len + IGMP_V3_RECORD_LEN(src_cnt) < max_pkt_len) { /* can add a record? */ ++ p_rec = pbuf_alloc(PBUF_RAW, IGMP_V3_RECORD_LEN(src_cnt), PBUF_RAM); ++ if (p_rec) { ++ rep->igmp_v3_reccnt = PP_HTONS(PP_NTOHS(rep->igmp_v3_reccnt) + 1); /* add a record */ ++ ++ rec = (struct igmp_v3_record *)p_rec->payload; ++ igmp_v3_build_record(rec, group, fmode, src_array, src_cnt); ++ group->v3_last_reporter_flag = 1; /* Remember we were the last to report */ ++ pbuf_cat(p, p_rec); /* cat to tail */ ++ goto cat_rec; ++ ++ } else { ++ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_v3_send_allgroups: not enough memory for igmp_v3_send_allgroups\n")); ++ IGMP_STATS_INC(igmp.memerr); ++ } ++ } ++ } ++ ++ rep->igmp_v3_checksum = inet_chksum_pbuf(p); ++ igmp_ip_output_if(p, &src, &v3_routers, netif); ++ pbuf_free(p); ++ } ++} ++ ++/** ++ * igmp v3 report trigger. ++ */ ++void ++igmp_v3_trigger(struct netif *netif, const ip4_addr_t *groupaddr) ++{ ++ struct igmp_group *group; ++ ++ /* find group */ ++ group = igmp_lookfor_group(netif, groupaddr); ++ if (group) { ++ igmp_v3_send(netif, group, IGMP_V3_MEMB_REPORT); ++ ++ igmp_v3_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR); ++ ++ /* Need to work out where this timer comes from */ ++ group->v3_group_state = IGMP_GROUP_DELAYING_MEMBER; ++ } ++} ++ ++#endif /* LWIP_IGMP_V3 */ + #endif /* LWIP_IPV4 && LWIP_IGMP */ +diff -Nur lwip-2.2.0init/src/core/mcast.c lwip-2.2.0/src/core/mcast.c +--- lwip-2.2.0init/src/core/mcast.c 1970-01-01 08:00:00.000000000 +0800 ++++ lwip-2.2.0/src/core/mcast.c 2024-03-14 19:16:11.610020996 +0800 +@@ -0,0 +1,603 @@ ++/** ++ * @file ++ * Multicast filter module\n ++ */ ++ ++/* ++ * Copyright (c) 2018 ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without modification, ++ * are permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright notice, ++ * this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright notice, ++ * this list of conditions and the following disclaimer in the documentation ++ * and/or other materials provided with the distribution. ++ * 3. The name of the author may not be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED ++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT ++ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT ++ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING ++ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY ++ * OF SUCH DAMAGE. ++ * ++ * This file is part of the lwIP TCP/IP stack. ++ * ++ * ++ */ ++ ++#include "lwip/opt.h" ++ ++#if LWIP_UDP || LWIP_RAW /* don't build if not configured for use in lwipopts.h */ ++ ++#include "lwip/err.h" ++#include "lwip/udp.h" ++#include "lwip/raw.h" ++#include "lwip/def.h" ++#include "lwip/mem.h" ++#include "lwip/netif.h" ++#include "lwip/errno.h" ++#include "lwip/ip_addr.h" ++#include "lwip/sockets.h" ++#include "lwip/mcast.h" ++ ++#include /* We need offsetof() */ ++ ++#if LWIP_IPV4 && LWIP_IGMP ++/** ++ * ipv4 multicast filter find ++ */ ++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) ++{ ++ struct ip4_mc *prev = NULL; ++ struct ip4_mc *mc; ++ ++ IP4_MC_FOREACH(ipmc, mc) { ++ if (ip4_addr_cmp(&mc->if_addr, netif_ip4_addr(netif)) && ++ ip4_addr_cmp(&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! */ ++} ++ ++/** ++ * ipv4 multicast filter find source ++ */ ++static struct igmp_src * ++mcast_ip4_mc_src_find(struct ip4_mc *mc, const ip4_addr_t *src_addr, struct igmp_src **src_prev) ++{ ++ struct igmp_src *prev = NULL; ++ struct igmp_src *src; ++ ++ IP4_MC_SRC_FOREACH(mc, src) { ++ if (ip4_addr_cmp(&src->src_addr, src_addr)) { /* check source address */ ++ if (src_prev) { ++ *src_prev = prev; ++ } ++ return src; /* found! */ ++ } ++ prev = src; ++ } ++ ++ return NULL; /* not found! */ ++} ++ ++/** ++ * ipv4 multicast filter remove all source ++ */ ++void ++mcast_ip4_mc_src_remove(struct igmp_src *src) ++{ ++ struct igmp_src *next; ++ ++ while (src) { ++ next = src->next; ++ mem_free(src); ++ src = next; ++ } ++} ++ ++#if LWIP_IGMP_V3 ++/** ++ * ipv4 multicast filter group source infomation (use ip4_addr_p_t for IGMPv3 speedup) ++ */ ++u16_t ++mcast_ip4_filter_info(struct netif *netif, const ip4_addr_t *multi_addr, ip4_addr_p_t addr_array[], u16_t arr_cnt, u8_t *fmode) ++{ ++ static ip4_addr_p_t in_tbl[LWIP_MCAST_SRC_TBL_SIZE]; ++ static ip4_addr_p_t ex_tbl[LWIP_MCAST_SRC_TBL_SIZE]; ++ ++ struct ip4_mc *mc; ++ struct igmp_src *src; ++ ip4_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_IP4_MC_GET(pcb, pcbs, type, tbl, c) \ ++ for ((pcb) = (pcbs); (pcb) != NULL; (pcb) = (pcb)->next) { \ ++ mc = mcast_ip4_mc_find(&(pcb)->ipmc, netif, multi_addr, NULL); \ ++ if ((mc == NULL) || (mc->fmode != (type))) { \ ++ continue; \ ++ } \ ++ match = 1; /* group matched */ \ ++ IP4_MC_SRC_FOREACH(mc, src) { \ ++ if ((c) < max_cnt) { \ ++ ip4_addr_set(&(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_IP4_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_IP4_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_IP4_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_IP4_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++) { ++ ip4_addr_set(&addr, &ex_tbl[i]); ++ for (j = 0; j < in_cnt; j++) { ++ if (ip4_addr_cmp(&addr, &in_tbl[j])) { /* check exclude conflict with include table */ ++ ip4_addr_set_any(&ex_tbl[i]); /* remove from exclude table */ ++ break; ++ } ++ } ++ } ++ ++ for (i = 0, cnt = 0; i < ex_cnt; i++) { ++ if (!ip4_addr_isany(&ex_tbl[i])) { ++ ip4_addr_set(&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++) { ++ ip4_addr_set(&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); ++} ++ ++/** ++ * ipv4 multicast filter source address interest (use ip4_addr_p_t for IGMPv3 speedup) ++ */ ++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) ++{ ++ static ip4_addr_p_t ip_tbl[LWIP_MCAST_SRC_TBL_SIZE]; ++ u16_t i, j, cnt; ++ u8_t fmode; ++ ++ cnt = mcast_ip4_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 (ip4_addr_cmp(&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 (ip4_addr_cmp(&src_addr[j], &ip_tbl[i])) { ++ return 1; ++ } ++ } ++ } ++ return 0; ++ } ++} ++#endif /* LWIP_IGMP_V3 */ ++#endif /* LWIP_IPV4 && LWIP_IGMP */ ++ ++#if (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) ++/** Join a multicast group (Can with a source specified) ++ * ++ * @param ipmc multicast filter control block ++ * @param netif the network interface which should join a new group. ++ * @param multi_addr the ipv6 address of the group to join ++ * @param src_addr multicast source address (can be NULL) ++ * @return lwIP error definitions ++ */ ++err_t ++mcast_join_netif(struct ip_mc *ipmc, struct netif *netif, const ip_addr_t *multi_addr, const ip_addr_t *src_addr) ++{ ++#if LWIP_UDP ++ if (ipmc->proto == IPPROTO_UDP) { ++ err_t err; ++ struct udp_pcb *pcb; ++ /* prepare UDP pcb to udp_pcbs list */ ++ pcb = (struct udp_pcb *)((u8_t *)ipmc - offsetof(struct udp_pcb, ipmc)); ++ if (pcb->local_port == 0) { ++ err = udp_bind(pcb, &pcb->local_ip, pcb->local_port); ++ if (err != ERR_OK) { ++ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("mcast_join_netif: forced port bind failed\n")); ++ return err; ++ } ++ } ++ } ++#endif /* LWIP_UDP */ ++ ++#if LWIP_IPV4 && LWIP_IGMP ++ if (IP_IS_V4(multi_addr)) { ++ struct ip4_mc *mc; ++ struct igmp_src *src; ++ ++ mc = mcast_ip4_mc_find(ipmc, netif, ip_2_ip4(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_ip4_mc_src_find(mc, ip_2_ip4(src_addr), NULL); ++ if (src) { ++ return ERR_ALREADY; /* already in source list */ ++ } ++ ++ src = (struct igmp_src *)mem_malloc(sizeof(struct igmp_src)); ++ if (src == NULL) { ++ return ERR_MEM; /* no memory */ ++ } ++ ip4_addr_set(&src->src_addr, ip_2_ip4(src_addr)); ++ src->next = mc->src; ++ 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; ++ } ++ ++ mc = (struct ip4_mc *)mem_malloc(sizeof(struct ip4_mc)); /* Make a new mc */ ++ if (mc == NULL) { ++ igmp_leavegroup_netif(netif, ip_2_ip4(multi_addr)); ++ return ERR_MEM; /* no memory */ ++ } ++ ip4_addr_set(&mc->if_addr, netif_ip4_addr(netif)); ++ ip4_addr_set(&mc->multi_addr, ip_2_ip4(multi_addr)); ++ ++ if (src_addr) { /* have a source specified */ ++ mc->fmode = MCAST_INCLUDE; ++ src = (struct igmp_src *)mem_malloc(sizeof(struct igmp_src)); ++ if (src == NULL) { ++ mem_free(mc); ++ return ERR_MEM; /* no memory */ ++ } ++ ip4_addr_set(&src->src_addr, ip_2_ip4(src_addr)); ++ src->next = NULL; ++ mc->src = src; ++ ++ } else { ++ mc->fmode = MCAST_EXCLUDE; /* no source specified */ ++ mc->src = NULL; ++ } ++ ++ mc->next = ipmc->mc4; ++ ipmc->mc4 = mc; ++ igmp_joingroup_netif(netif, ip_2_ip4(multi_addr)); ++ } else {} ++#endif /* LWIP_IPV4 && LWIP_IGMP */ ++ return ERR_OK; ++} ++ ++/** Join a multicast group (Can with a source specified) ++ * ++ * @param ipmc multicast filter control block ++ * @param if_addr the network interface address. ++ * @param multi_addr the ipv6 address of the group to join ++ * @param src_addr multicast source address (can be NULL) ++ * @return lwIP error definitions ++ */ ++err_t ++mcast_join_group(struct ip_mc *ipmc, const ip_addr_t *if_addr, const ip_addr_t *multi_addr, const ip_addr_t *src_addr) ++{ ++ err_t err = ERR_VAL; /* no matching interface */ ++ ++#if LWIP_IPV4 && LWIP_IGMP ++ if (IP_IS_V4(multi_addr)) { ++ struct netif *netif; ++ ++ NETIF_FOREACH(netif) { ++ if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ip_2_ip4(if_addr)) || ip4_addr_cmp(netif_ip4_addr(netif), ip_2_ip4(if_addr))))) { ++ err = mcast_join_netif(ipmc, netif, multi_addr, src_addr); ++ if (err != ERR_OK) { ++ return (err); ++ } ++ } ++ } ++ } else {} ++#endif /* LWIP_IPV4 && LWIP_IGMP */ ++ return err; ++} ++ ++/** Leave or drop a source from group on a network interface. ++ * ++ * @param ipmc multicast filter control block ++ * @param netif the network interface which should leave group. ++ * @param multi_addr the address of the group to leave ++ * @param src_addr multicast source address ++ * @return lwIP error definitions ++ */ ++err_t ++mcast_leave_netif(struct ip_mc *ipmc, struct netif *netif, const ip_addr_t *multi_addr, const ip_addr_t *src_addr) ++{ ++#if LWIP_IPV4 && LWIP_IGMP ++ if (IP_IS_V4(multi_addr)) { ++ struct ip4_mc *mc_prev; ++ struct ip4_mc *mc; ++ struct igmp_src *src_prev; ++ struct igmp_src *src; ++ ++ mc = mcast_ip4_mc_find(ipmc, netif, ip_2_ip4(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_ip4_mc_src_find(mc, ip_2_ip4(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) { ++ IP4_MC_TRIGGER_CALL(netif, ip_2_ip4(multi_addr)); /* trigger a report */ ++ return ERR_OK; ++ } ++ } else { /* we want drop this group */ ++ mcast_ip4_mc_src_remove(mc->src); ++ } ++ ++ igmp_leavegroup_netif(netif, ip_2_ip4(multi_addr)); ++ if (mc_prev) { ++ mc_prev->next = mc->next; ++ } else { ++ ipmc->mc4 = mc->next; ++ } ++ mem_free(mc); ++ } else {} ++#endif /* LWIP_IPV4 && LWIP_IGMP */ ++ return ERR_OK; ++} ++ ++/** Leave or drop a source from group on a network interface. ++ * ++ * @param ipmc multicast filter control block ++ * @param if_addr the network interface address. ++ * @param multi_addr the address of the group to leave ++ * @param src_addr multicast source address ++ * @return lwIP error definitions ++ */ ++err_t ++mcast_leave_group(struct ip_mc *ipmc, const ip_addr_t *if_addr, const ip_addr_t *multi_addr, const ip_addr_t *src_addr) ++{ ++ err_t res, err = ERR_VAL; /* no matching interface */ ++ ++#if LWIP_IPV4 && LWIP_IGMP ++ if (IP_IS_V4(multi_addr)) { ++ struct netif *netif; ++ ++ NETIF_FOREACH(netif) { ++ if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ip_2_ip4(if_addr)) || ip4_addr_cmp(netif_ip4_addr(netif), ip_2_ip4(if_addr))))) { ++ res = mcast_leave_netif(ipmc, netif, multi_addr, src_addr); ++ if (err != ERR_OK) { ++ err = res; ++ } ++ } ++ } ++ } else {} ++#endif /* LWIP_IPV4 && LWIP_IGMP */ ++ return err; ++} ++ ++/** Add a block source address to a multicast group ++ * ++ * @param ipmc multicast filter control block ++ * @param netif the network interface which group we already join. ++ * @param multi_addr the address of the group to add source ++ * @param blk_addr block multicast source address ++ * @return lwIP error definitions ++ */ ++err_t ++mcast_block_netif(struct ip_mc *ipmc, struct netif *netif, const ip_addr_t *multi_addr, const ip_addr_t *blk_addr) ++{ ++#if LWIP_IPV4 && LWIP_IGMP ++ if (IP_IS_V4(multi_addr)) { ++ struct ip4_mc *mc; ++ struct igmp_src *src; ++ ++ mc = mcast_ip4_mc_find(ipmc, netif, ip_2_ip4(multi_addr), NULL); ++ if (mc == NULL) { ++ return ERR_VAL; ++ } ++ if (mc->fmode != MCAST_EXCLUDE) { /* we must in exclude mode */ ++ return ERR_VAL; ++ } ++ ++ src = mcast_ip4_mc_src_find(mc, ip_2_ip4(blk_addr), NULL); ++ if (src == NULL) { ++ src = (struct igmp_src *)mem_malloc(sizeof(struct igmp_src)); ++ if (src == NULL) { ++ return ERR_MEM; ++ } ++ ip4_addr_set(&src->src_addr, ip_2_ip4(blk_addr)); ++ src->next = mc->src; ++ mc->src = src; ++ IP4_MC_TRIGGER_CALL(netif, ip_2_ip4(multi_addr)); /* trigger a report */ ++ } ++ } else {} ++#endif /* LWIP_IPV4 && LWIP_IGMP */ ++ return ERR_OK; ++} ++ ++/** Add a block source address to a multicast group ++ * ++ * @param ipmc multicast filter control block ++ * @param if_addr the network interface address. ++ * @param multi_addr the address of the group to add source ++ * @param blk_addr block multicast source address ++ * @return lwIP error definitions ++ */ ++err_t ++mcast_block_group(struct ip_mc *ipmc, const ip_addr_t *if_addr, const ip_addr_t *multi_addr, const ip_addr_t *blk_addr) ++{ ++ err_t err = ERR_VAL; /* no matching interface */ ++ ++#if LWIP_IPV4 && LWIP_IGMP ++ if (IP_IS_V4(multi_addr)) { ++ struct netif *netif; ++ ++ NETIF_FOREACH(netif) { ++ if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ip_2_ip4(if_addr)) || ip4_addr_cmp(netif_ip4_addr(netif), ip_2_ip4(if_addr))))) { ++ err = mcast_block_netif(ipmc, netif, multi_addr, blk_addr); ++ if (err != ERR_OK) { ++ return (err); ++ } ++ } ++ } ++ } else {} ++#endif /* LWIP_IPV4 && LWIP_IGMP */ ++ return err; ++} ++ ++/** Remove a block source address from a multicast group ++ * ++ * @param ipmc multicast filter control block ++ * @param netif the network interface which group we already join. ++ * @param multi_addr the address of the group to add source ++ * @param unblk_addr unblock multicast source address ++ * @return lwIP error definitions ++ */ ++err_t ++mcast_unblock_netif(struct ip_mc *ipmc, struct netif *netif, const ip_addr_t *multi_addr, const ip_addr_t *unblk_addr) ++{ ++#if LWIP_IPV4 && LWIP_IGMP ++ if (IP_IS_V4(multi_addr)) { ++ struct ip4_mc *mc; ++ struct igmp_src *src_prev; ++ struct igmp_src *src; ++ ++ mc = mcast_ip4_mc_find(ipmc, netif, ip_2_ip4(multi_addr), NULL); ++ if (mc == NULL) { ++ return ERR_VAL; ++ } ++ if (mc->fmode != MCAST_EXCLUDE) { /* we must in exclude mode */ ++ return ERR_VAL; ++ } ++ ++ src = mcast_ip4_mc_src_find(mc, ip_2_ip4(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); ++ IP4_MC_TRIGGER_CALL(netif, ip_2_ip4(multi_addr)); /* trigger a report */ ++ } else {} ++#endif /* LWIP_IPV4 && LWIP_IGMP */ ++ return ERR_OK; ++} ++ ++/** Remove a block source address from a multicast group ++ * ++ * @param ipmc multicast filter control block ++ * @param if_addr the network interface address. ++ * @param multi_addr the address of the group to add source ++ * @param unblk_addr unblock multicast source address ++ * @return lwIP error definitions ++ */ ++err_t ++mcast_unblock_group(struct ip_mc *ipmc, const ip_addr_t *if_addr, const ip_addr_t *multi_addr, const ip_addr_t *unblk_addr) ++{ ++ err_t res, err = ERR_VAL; /* no matching interface */ ++ ++#if LWIP_IPV4 && LWIP_IGMP ++ if (IP_IS_V4(multi_addr)) { ++ struct netif *netif; ++ ++ NETIF_FOREACH(netif) { ++ if ((netif->flags & NETIF_FLAG_IGMP) && ((ip4_addr_isany(ip_2_ip4(if_addr)) || ip4_addr_cmp(netif_ip4_addr(netif), ip_2_ip4(if_addr))))) { ++ res = mcast_unblock_netif(ipmc, netif, multi_addr, unblk_addr); ++ if (err != ERR_OK) { ++ err = res; ++ } ++ } ++ } ++ } else {} ++#endif /* LWIP_IPV4 && LWIP_IGMP */ ++ return err; ++} ++ ++#endif /* (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) */ ++ ++#endif /* LWIP_UDP || LWIP_RAW */ +diff -Nur lwip-2.2.0init/src/core/raw.c lwip-2.2.0/src/core/raw.c +--- lwip-2.2.0init/src/core/raw.c 2024-03-14 19:14:54.550020996 +0800 ++++ lwip-2.2.0/src/core/raw.c 2024-03-14 19:15:36.791020996 +0800 +@@ -63,10 +63,10 @@ + #include + + /** The list of RAW PCBs */ +-static struct raw_pcb *raw_pcbs; ++struct raw_pcb *raw_pcbs; + + static u8_t +-raw_input_local_match(struct raw_pcb *pcb, u8_t broadcast) ++raw_input_local_match(struct raw_pcb *pcb, u8_t broadcast, struct netif *inp) + { + LWIP_UNUSED_ARG(broadcast); /* in IPv6 only case */ + +@@ -88,6 +88,12 @@ + } + #endif /* LWIP_IPV4 && LWIP_IPV6 */ + ++#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 +@@ -164,9 +170,20 @@ + /* loop through all raw pcbs until the packet is eaten by one */ + /* this allows multiple pcbs to match against the packet by design */ + while (pcb != NULL) { +- if ((pcb->protocol == proto) && raw_input_local_match(pcb, broadcast) && ++ if ((pcb->protocol == proto) && raw_input_local_match(pcb, broadcast, inp) && + (((pcb->flags & RAW_FLAGS_CONNECTED) == 0) || + ip_addr_eq(&pcb->remote_ip, ip_current_src_addr()))) { ++ if (pcb->min_ttl) { ++ if (ip_current_is_v6()) { ++ if (pcb->min_ttl > IP6H_HOPLIM(ip6_current_header())) { ++ continue; ++ } ++ } else { ++ if (pcb->min_ttl > IPH_TTL(ip4_current_header())) { ++ continue; ++ } ++ } ++ } + /* receive callback function available? */ + if (pcb->recv != NULL) { + u8_t eaten; +@@ -371,7 +388,26 @@ + /* For multicast-destined packets, use the user-provided interface index to + * determine the outgoing interface, if an interface index is set and a + * matching netif can be found. Otherwise, fall back to regular routing. */ +- netif = netif_get_by_index(pcb->mcast_ifindex); ++ if (pcb->mcast_ifindex != NETIF_NO_INDEX) { ++ netif = netif_get_by_index(pcb->mcast_ifindex); ++ } ++#if LWIP_IPV4 ++ else ++#if LWIP_IPV6 ++ if (IP_IS_V4(ipaddr)) ++#endif /* LWIP_IPV6 */ ++ { ++ /* IPv4 does not use source-based routing by default, so we use an ++ administratively selected interface for multicast by default. ++ However, this can be overridden by setting an interface address ++ in pcb->mcast_ip4 that is used for routing. If this routing lookup ++ fails, we try regular routing as though no override was set. */ ++ if (!ip4_addr_isany_val(pcb->mcast_ip4) && ++ !ip4_addr_cmp(&pcb->mcast_ip4, IP4_ADDR_BROADCAST)) { ++ netif = ip4_route_src(ip_2_ip4(&pcb->local_ip), &pcb->mcast_ip4); ++ } ++ } ++#endif /* LWIP_IPV4 */ + } + + if (netif == NULL) +@@ -450,6 +486,16 @@ + if (p->len < header_size) { + return ERR_VAL; + } ++#if CHECKSUM_GEN_IP ++ if (IP_IS_V4(dst_ip)) { ++ struct ip_hdr *iphdr = (struct ip_hdr *)p->payload; ++ if (IPH_CHKSUM(iphdr) == 0) { ++ IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) { ++ IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IPH_HL(iphdr) << 2)); ++ } ++ } ++ } ++#endif /* CHECKSUM_GEN_IP */ + /* @todo multicast loop support, if at all desired for this scenario.. */ + NETIF_SET_HINTS(netif, &pcb->netif_hints); + err = ip_output_if_hdrincl(p, src_ip, dst_ip, netif); +@@ -563,6 +609,11 @@ + { + struct raw_pcb *pcb2; + LWIP_ASSERT_CORE_LOCKED(); ++ ++#if (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) ++ mcast_pcb_remove(&pcb->ipmc); ++#endif /* (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) */ ++ + /* pcb to be removed is first in list? */ + if (raw_pcbs == pcb) { + /* make list start at 2nd pcb */ +@@ -611,6 +662,9 @@ + raw_set_multicast_ttl(pcb, RAW_TTL); + #endif /* LWIP_MULTICAST_TX_OPTIONS */ + pcb_tci_init(pcb); ++#if (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) ++ pcb->ipmc.proto = IPPROTO_RAW; ++#endif /* (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) */ + pcb->next = raw_pcbs; + raw_pcbs = pcb; + } +diff -Nur lwip-2.2.0init/src/core/udp.c lwip-2.2.0/src/core/udp.c +--- lwip-2.2.0init/src/core/udp.c 2024-03-14 19:14:54.623020996 +0800 ++++ lwip-2.2.0/src/core/udp.c 2024-03-14 19:15:36.791020996 +0800 +@@ -205,6 +205,12 @@ + 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 +@@ -347,6 +353,21 @@ + if ((pcb->remote_port == src) && + (ip_addr_isany_val(pcb->remote_ip) || + ip_addr_eq(&pcb->remote_ip, ip_current_src_addr()))) { ++ if (pcb->min_ttl) { ++ if (ip_current_is_v6()) { ++ if (pcb->min_ttl > IP6H_HOPLIM(ip6_current_header())) { ++ UDP_STATS_INC(udp.drop); ++ pbuf_free(p); ++ goto end; ++ } ++ } else { ++ if (pcb->min_ttl > IPH_TTL(ip4_current_header())) { ++ UDP_STATS_INC(udp.drop); ++ pbuf_free(p); ++ goto end; ++ } ++ } ++ } + /* the first fully matching PCB */ + if (prev != NULL) { + /* move the pcb to the front of udp_pcbs so that is +@@ -1321,6 +1342,10 @@ + + LWIP_ERROR("udp_remove: invalid pcb", pcb != NULL, return); + ++#if (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) ++ mcast_pcb_remove(&pcb->ipmc); ++#endif /* (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) */ ++ + mib2_udp_unbind(pcb); + /* pcb to be removed is first in list? */ + if (udp_pcbs == pcb) { +@@ -1375,6 +1400,9 @@ + udp_set_multicast_ttl(pcb, UDP_TTL); + #endif /* LWIP_MULTICAST_TX_OPTIONS */ + pcb_tci_init(pcb); ++#if (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) ++ pcb->ipmc.proto = IPPROTO_UDP; ++#endif /* (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) */ + } + return pcb; + } +diff -Nur lwip-2.2.0init/src/include/lwip/api.h lwip-2.2.0/src/include/lwip/api.h +--- lwip-2.2.0init/src/include/lwip/api.h 2024-03-14 19:14:54.586020996 +0800 ++++ lwip-2.2.0/src/include/lwip/api.h 2024-03-14 19:15:36.791020996 +0800 +@@ -89,6 +89,8 @@ + #if LWIP_NETBUF_RECVINFO + /** Received packet info will be recorded for this netconn */ + #define NETCONN_FLAG_PKTINFO 0x40 ++/** Received hoplim will be recorded for this netconn */ ++#define NETCONN_FLAG_HOPLIM 0x100 + #endif /* LWIP_NETBUF_RECVINFO */ + /** A FIN has been received but not passed to the application yet */ + #define NETCONN_FIN_RX_PENDING 0x80 +@@ -309,7 +311,7 @@ + s16_t linger; + #endif /* LWIP_SO_LINGER */ + /** flags holding more netconn-internal state, see NETCONN_FLAG_* defines */ +- u8_t flags; ++ u16_t flags; + #if LWIP_TCP + /** TCP: when data passed to netconn_write doesn't fit into the send buffer, + this temporarily stores the message. +diff -Nur lwip-2.2.0init/src/include/lwip/igmp.h lwip-2.2.0/src/include/lwip/igmp.h +--- lwip-2.2.0init/src/include/lwip/igmp.h 2024-03-14 19:14:53.946020996 +0800 ++++ lwip-2.2.0/src/include/lwip/igmp.h 2024-03-14 19:15:36.791020996 +0800 +@@ -51,6 +51,13 @@ + extern "C" { + #endif + ++/* Multicast filter support */ ++struct igmp_src { ++ struct igmp_src *next; ++ /** the source address */ ++ ip4_addr_t src_addr; ++}; ++ + /* IGMP timer */ + #define IGMP_TMR_INTERVAL 100 /* Milliseconds */ + #define IGMP_V1_DELAYING_MEMBER_TMR (1000/IGMP_TMR_INTERVAL) +@@ -60,6 +67,10 @@ + #define IGMP_DEL_MAC_FILTER NETIF_DEL_MAC_FILTER + #define IGMP_ADD_MAC_FILTER NETIF_ADD_MAC_FILTER + ++#if LWIP_IGMP_V3 ++#define IGMP_FMODE_INIT 2 /* Not MCAST_INCLUDE or MCAST_EXCLUDE */ ++#endif /* LWIP_IGMP_V3 */ ++ + /** + * igmp group structure - there is + * a list of groups for each interface +@@ -84,6 +95,16 @@ + u16_t timer; + /** counter of simultaneous uses */ + u8_t use; ++#if LWIP_IGMP_V3 ++ /** Last report fmode */ ++ u8_t v3_fmode; ++ /** signifies we were the last person to report */ ++ u8_t v3_last_reporter_flag; ++ /** current state of the group */ ++ u8_t v3_group_state; ++ /** timer for reporting, negative is OFF */ ++ u16_t v3_timer; ++#endif /* LWIP_IGMP_V3 */ + }; + + /* Prototypes */ +@@ -106,6 +127,20 @@ + */ + #define netif_igmp_data(netif) ((struct igmp_group *)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP)) + ++/** ++ * ipv4 multicast filter trigger function ++ */ ++#if LWIP_IGMP_V3 ++ void igmp_v3_trigger(struct netif *netif, const ip4_addr_t *groupaddr); ++ ++ /* IGMPv3 must install a trigger function to report multicast filter changes */ ++ ++#define IP4_MC_TRIGGER_CALL(netif, multi_addr) igmp_v3_trigger(netif, multi_addr) ++#else ++#define IP4_MC_TRIGGER_CALL(netif, multi_addr) ++#endif /* !LWIP_IGMP_V3 */ ++ ++ + #ifdef __cplusplus + } + #endif +diff -Nur lwip-2.2.0init/src/include/lwip/ip.h lwip-2.2.0/src/include/lwip/ip.h +--- lwip-2.2.0init/src/include/lwip/ip.h 2024-03-14 19:14:54.550020996 +0800 ++++ lwip-2.2.0/src/include/lwip/ip.h 2024-03-14 19:15:36.791020996 +0800 +@@ -85,7 +85,9 @@ + /* Type Of Service */ \ + u8_t tos; \ + /* Time To Live */ \ +- u8_t ttl \ ++ u8_t ttl; \ ++ /* min TTL */ \ ++ u8_t min_ttl \ + /* link layer address resolution hint */ \ + IP_PCB_NETIFHINT + +diff -Nur lwip-2.2.0init/src/include/lwip/mcast.h lwip-2.2.0/src/include/lwip/mcast.h +--- lwip-2.2.0init/src/include/lwip/mcast.h 1970-01-01 08:00:00.000000000 +0800 ++++ lwip-2.2.0/src/include/lwip/mcast.h 2024-03-14 19:16:35.017020996 +0800 +@@ -0,0 +1,116 @@ ++/** ++ * @file ++ * Multicast filter module\n ++ */ ++ ++/* ++ * Copyright (c) 2018 ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without modification, ++ * are permitted provided that the following conditions are met: ++ * ++ * 1. Redistributions of source code must retain the above copyright notice, ++ * this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright notice, ++ * this list of conditions and the following disclaimer in the documentation ++ * and/or other materials provided with the distribution. ++ * 3. The name of the author may not be used to endorse or promote products ++ * derived from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED ++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT ++ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT ++ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING ++ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY ++ * OF SUCH DAMAGE. ++ * ++ * This file is part of the lwIP TCP/IP stack. ++ * ++ * ++ */ ++#ifndef LWIP_HDR_MCAST_H ++#define LWIP_HDR_MCAST_H ++ ++#include "lwip/opt.h" ++ ++#if LWIP_UDP || LWIP_RAW /* don't build if not configured for use in lwipopts.h */ ++ ++#include "lwip/def.h" ++#include "lwip/ip.h" ++#include "lwip/ip_addr.h" ++#include "lwip/ip6_addr.h" ++#include "lwip/netif.h" ++#include "lwip/igmp.h" ++#include "lwip/mld6.h" ++#include "lwip/prot/ip4.h" ++#include "lwip/prot/ip6.h" ++ ++ ++#if LWIP_IPV4 && LWIP_IGMP ++/** the IPv4 multicast filter */ ++struct ip4_mc { ++ struct ip4_mc *next; ++ /** the interface address */ ++ ip4_addr_t if_addr; ++ /** the group address */ ++ ip4_addr_t multi_addr; ++ /** the source address list filter mode 0: EXCLUDE 1: INCLUDE */ ++ u8_t fmode; ++ /** the source address list */ ++ struct igmp_src *src; ++}; ++ ++/** The list of ip4_mc. */ ++#define IP4_MC_FOREACH(ipmc, mc) \ ++ for ((mc) = (ipmc)->mc4; (mc) != NULL; (mc) = (mc)->next) ++#define IP4_MC_SRC_FOREACH(mc, src) \ ++ for ((src) = (mc)->src; (src) != NULL; (src) = (src)->next) ++#endif /* LWIP_IPV4 && LWIP_IGMP */ ++ ++ ++#if (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) ++/* multicast filter control block */ ++struct ip_mc { ++#if !LWIP_SOCKET ++#define IPPROTO_UDP 17 ++#define IPPROTO_RAW 255 ++#endif /* !LWIP_SOCKET */ ++ u8_t proto; /* IPPROTO_UDP or IPPROTO_RAW */ ++#if LWIP_IPV4 && LWIP_IGMP ++ struct ip4_mc *mc4; ++#endif /* LWIP_IPV4 && LWIP_IGMP */ ++}; ++ ++#if LWIP_IPV4 && LWIP_IGMP && LWIP_IGMP_V3 ++/* IGMPv3 use the following function to get specified group of total multicast filter source address array */ ++u16_t mcast_ip4_filter_info(struct netif *netif, const ip4_addr_t *multi_addr, ip4_addr_p_t addr_array[], u16_t arr_cnt, u8_t *fmode); ++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 */ ++ ++/* 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); ++ ++/* The following functions is for Non-Socket API user */ ++err_t mcast_join_netif(struct ip_mc *ipmc, struct netif *netif, const ip_addr_t *multi_addr, const ip_addr_t *src_addr); ++err_t mcast_join_group(struct ip_mc *ipmc, const ip_addr_t *if_addr, const ip_addr_t *multi_addr, const ip_addr_t *src_addr); ++err_t mcast_leave_netif(struct ip_mc *ipmc, struct netif *netif, const ip_addr_t *multi_addr, const ip_addr_t *src_addr); ++err_t mcast_leave_group(struct ip_mc *ipmc, const ip_addr_t *if_addr, const ip_addr_t *multi_addr, const ip_addr_t *src_addr); ++err_t mcast_block_netif(struct ip_mc *ipmc, struct netif *netif, const ip_addr_t *multi_addr, const ip_addr_t *blk_addr); ++err_t mcast_block_group(struct ip_mc *ipmc, const ip_addr_t *if_addr, const ip_addr_t *multi_addr, const ip_addr_t *blk_addr); ++err_t mcast_unblock_netif(struct ip_mc *ipmc, struct netif *netif, const ip_addr_t *multi_addr, const ip_addr_t *unblk_addr); ++err_t mcast_unblock_group(struct ip_mc *ipmc, const ip_addr_t *if_addr, const ip_addr_t *multi_addr, const ip_addr_t *unblk_addr); ++ ++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); ++ ++#endif /* (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) */ ++ ++#endif /* LWIP_UDP || LWIP_RAW */ ++ ++#endif /* LWIP_HDR_MCAST_H */ +diff -Nur lwip-2.2.0init/src/include/lwip/netbuf.h lwip-2.2.0/src/include/lwip/netbuf.h +--- lwip-2.2.0init/src/include/lwip/netbuf.h 2024-03-14 19:14:53.959020996 +0800 ++++ lwip-2.2.0/src/include/lwip/netbuf.h 2024-03-14 19:15:36.791020996 +0800 +@@ -55,6 +55,8 @@ + #define NETBUF_FLAG_DESTADDR 0x01 + /** This netbuf includes a checksum */ + #define NETBUF_FLAG_CHKSUM 0x02 ++/** This netbuf has hoplim set */ ++#define NETBUF_FLAG_HOPLIM 0x80 + + /** "Network buffer" - contains data and addressing info */ + struct netbuf { +diff -Nur lwip-2.2.0init/src/include/lwip/opt.h lwip-2.2.0/src/include/lwip/opt.h +--- lwip-2.2.0init/src/include/lwip/opt.h 2024-03-14 19:14:54.586020996 +0800 ++++ lwip-2.2.0/src/include/lwip/opt.h 2024-03-14 19:15:36.792020996 +0800 +@@ -1126,9 +1126,19 @@ + #if !defined LWIP_IGMP || defined __DOXYGEN__ + #define LWIP_IGMP 1 + #endif ++ ++/** ++ * LWIP_IGMP_V3==1: Turn on IGMPv3 module. ++ */ ++#if !defined LWIP_IGMP_V3 || defined __DOXYGEN__ ++#define LWIP_IGMP_V3 1 ++#endif ++ + #if !LWIP_IPV4 + #undef LWIP_IGMP + #define LWIP_IGMP 0 ++#undef LWIP_IGMP_V3 ++#define LWIP_IGMP_V3 0 + #endif + /** + * @} +diff -Nur lwip-2.2.0init/src/include/lwip/prot/igmp.h lwip-2.2.0/src/include/lwip/prot/igmp.h +--- lwip-2.2.0init/src/include/lwip/prot/igmp.h 2024-03-14 19:14:53.979020996 +0800 ++++ lwip-2.2.0/src/include/lwip/prot/igmp.h 2024-03-14 19:15:36.792020996 +0800 +@@ -49,6 +49,7 @@ + */ + #define IGMP_TTL 1 + #define IGMP_MINLEN 8 ++#define IGMP_V3_MINLEN 12 + #define ROUTER_ALERT 0x9404U + #define ROUTER_ALERTLEN 4 + +@@ -59,6 +60,7 @@ + #define IGMP_V1_MEMB_REPORT 0x12 /* Ver. 1 membership report */ + #define IGMP_V2_MEMB_REPORT 0x16 /* Ver. 2 membership report */ + #define IGMP_LEAVE_GROUP 0x17 /* Leave-group message */ ++#define IGMP_V3_MEMB_REPORT 0x22 /* Ver. 3 membership report */ + + /* Group membership states */ + #define IGMP_GROUP_NON_MEMBER 0 +@@ -83,6 +85,79 @@ + # include "arch/epstruct.h" + #endif + ++#if LWIP_IGMP_V3 /* RFC 3367 */ ++/** ++ * IGMPv3 query packet format. ++ */ ++#ifdef PACK_STRUCT_USE_INCLUDES ++# include "arch/bpstruct.h" ++#endif ++PACK_STRUCT_BEGIN ++struct igmp_v3_query { ++ PACK_STRUCT_FLD_8(u8_t igmp_v3_msgtype); ++ PACK_STRUCT_FLD_8(u8_t igmp_v3_maxresp); ++ PACK_STRUCT_FIELD(u16_t igmp_v3_checksum); ++ PACK_STRUCT_FLD_S(ip4_addr_p_t igmp_v3_group_address); ++ PACK_STRUCT_FLD_8(u8_t igmp_v3_sqrv); ++ PACK_STRUCT_FLD_8(u8_t igmp_v3_qqic); ++ PACK_STRUCT_FIELD(u16_t igmp_v3_srccnt); ++} PACK_STRUCT_STRUCT; ++PACK_STRUCT_END ++#ifdef PACK_STRUCT_USE_INCLUDES ++# include "arch/epstruct.h" ++#endif ++ ++#define IGMP_V3_QUERY_HLEN sizeof(struct igmp_v3_query) ++ ++/** ++ * IGMPv3 report packet header format. ++ */ ++#ifdef PACK_STRUCT_USE_INCLUDES ++# include "arch/bpstruct.h" ++#endif ++PACK_STRUCT_BEGIN ++struct igmp_v3_report { ++ PACK_STRUCT_FLD_8(u8_t igmp_v3_msgtype); ++ PACK_STRUCT_FLD_8(u8_t igmp_v3_reserve1); ++ PACK_STRUCT_FIELD(u16_t igmp_v3_checksum); ++ PACK_STRUCT_FIELD(u16_t igmp_v3_reserve2); ++ PACK_STRUCT_FIELD(u16_t igmp_v3_reccnt); ++} PACK_STRUCT_STRUCT; ++PACK_STRUCT_END ++#ifdef PACK_STRUCT_USE_INCLUDES ++# include "arch/epstruct.h" ++#endif ++ ++#define IGMP_V3_REPORT_HLEN sizeof(struct igmp_v3_report) ++ ++#define IGMP_V3_REC_IS_IN 0x01 /* Type MODE_IS_INCLUDE */ ++#define IGMP_V3_REC_IS_EX 0x02 /* Type MODE_IS_EXCLUDE */ ++#define IGMP_V3_REC_TO_IN 0x03 /* Type CHANGE_TO_INCLUDE_MODE */ ++#define IGMP_V3_REC_TO_EX 0x04 /* Type CHANGE_TO_EXCLUDE_MODE */ ++#define IGMP_V3_REC_ALLOW 0x05 /* Type ALLOW_NEW_SOURCES */ ++#define IGMP_V3_REC_BLOCK 0x06 /* Type BLOCK_OLD_SOURCES */ ++ ++/** ++ * IGMPv3 report packet group record format. ++ */ ++#ifdef PACK_STRUCT_USE_INCLUDES ++# include "arch/bpstruct.h" ++#endif ++PACK_STRUCT_BEGIN ++struct igmp_v3_record { ++ PACK_STRUCT_FLD_8(u8_t igmp_v3_rc_type); ++ PACK_STRUCT_FLD_8(u8_t igmp_v3_rc_auxlen); ++ PACK_STRUCT_FIELD(u16_t igmp_v3_rc_srccnt); ++ PACK_STRUCT_FLD_S(ip4_addr_p_t igmp_v3_rc_group_address); ++} PACK_STRUCT_STRUCT; ++PACK_STRUCT_END ++#ifdef PACK_STRUCT_USE_INCLUDES ++# include "arch/epstruct.h" ++#endif ++ ++#define IGMP_V3_RECORD_LEN(src_cnt) (sizeof(struct igmp_v3_record) + (src_cnt << 2)) ++#endif /* LWIP_IGMP_V3 */ ++ + #ifdef __cplusplus + } + #endif +diff -Nur lwip-2.2.0init/src/include/lwip/raw.h lwip-2.2.0/src/include/lwip/raw.h +--- lwip-2.2.0init/src/include/lwip/raw.h 2024-03-14 19:14:53.984020996 +0800 ++++ lwip-2.2.0/src/include/lwip/raw.h 2024-03-14 19:15:36.792020996 +0800 +@@ -47,6 +47,7 @@ + #include "lwip/ip.h" + #include "lwip/ip_addr.h" + #include "lwip/ip6_addr.h" ++#include "lwip/mcast.h" + + #ifdef __cplusplus + extern "C" { +@@ -82,12 +83,20 @@ + u8_t flags; + + #if LWIP_MULTICAST_TX_OPTIONS ++#if LWIP_IPV4 ++ /** outgoing network interface for multicast packets, by IPv4 address (if not 'any') */ ++ ip4_addr_t mcast_ip4; ++#endif /* LWIP_IPV4 */ + /** outgoing network interface for multicast packets, by interface index (if nonzero) */ + u8_t mcast_ifindex; + /** TTL for outgoing multicast packets */ + u8_t mcast_ttl; + #endif /* LWIP_MULTICAST_TX_OPTIONS */ + ++#if (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) ++ struct ip_mc ipmc; ++#endif /* (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) */ ++ + /** receive callback function */ + raw_recv_fn recv; + /* user-supplied argument for the recv callback */ +@@ -98,6 +107,7 @@ + u8_t chksum_reqd; + #endif + }; ++extern struct raw_pcb *raw_pcbs; + + /* The following functions is the application layer interface to the + RAW code. */ +@@ -128,6 +138,10 @@ + #define raw_new_ip6(proto) raw_new_ip_type(IPADDR_TYPE_V6, proto) + + #if LWIP_MULTICAST_TX_OPTIONS ++#if LWIP_IPV4 ++#define raw_set_multicast_netif_addr(pcb, ip4addr) ip4_addr_copy((pcb)->mcast_ip4, *(ip4addr)) ++#define raw_get_multicast_netif_addr(pcb) (&(pcb)->mcast_ip4) ++#endif /* LWIP_IPV4 */ + #define raw_set_multicast_netif_index(pcb, idx) ((pcb)->mcast_ifindex = (idx)) + #define raw_get_multicast_netif_index(pcb) ((pcb)->mcast_ifindex) + #define raw_set_multicast_ttl(pcb, value) ((pcb)->mcast_ttl = (value)) +diff -Nur lwip-2.2.0init/src/include/lwip/sockets.h lwip-2.2.0/src/include/lwip/sockets.h +--- lwip-2.2.0init/src/include/lwip/sockets.h 2024-03-14 19:14:54.624020996 +0800 ++++ lwip-2.2.0/src/include/lwip/sockets.h 2024-03-14 19:15:36.792020996 +0800 +@@ -53,7 +53,7 @@ + #include "lwip/inet.h" + #include "lwip/errno.h" + #include "lwip/api.h" +- ++#include "lwip/mcast.h" + #include + + #ifdef __cplusplus +@@ -304,6 +304,10 @@ + #define IP_TOS 1 + #define IP_TTL 2 + #define IP_PKTINFO 8 ++#define IP_MINTTL 99 ++#define IP_HDRINCL 100 ++#define IP_OPTIONS 101 ++ + + #if LWIP_TCP + /* +@@ -357,23 +361,6 @@ + #endif /* GAZELLE_UDP_ENABLE */ + #endif /* LWIP_MULTICAST_TX_OPTIONS */ + +-#if LWIP_IGMP +-#if GAZELLE_UDP_ENABLE +-#define IP_ADD_MEMBERSHIP 35 +-#define IP_DROP_MEMBERSHIP 36 +-#else +-/* +- * Options and types related to multicast membership +- */ +-#define IP_ADD_MEMBERSHIP 3 +-#define IP_DROP_MEMBERSHIP 4 +-#endif /* GAZELLE_UDP_ENABLE */ +- +-typedef struct ip_mreq { +- struct in_addr imr_multiaddr; /* IP multicast address of group */ +- struct in_addr imr_interface; /* local IP address of interface */ +-} ip_mreq; +-#endif /* LWIP_IGMP */ + + #if LWIP_IPV4 + struct in_pktinfo { +@@ -716,6 +703,111 @@ + int lwip_fcntl(int s, int cmd, int val); + #endif /* GAZELLE_ENABLE */ + ++#if (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) ++/* Protocol Independent Multicast API [RFC3678] */ ++#define MCAST_JOIN_GROUP 42 /* join an any-source group */ ++#define MCAST_BLOCK_SOURCE 43 /* block a source */ ++#define MCAST_UNBLOCK_SOURCE 44 /* unblock a source */ ++#define MCAST_LEAVE_GROUP 45 /* leave all sources for group */ ++#define MCAST_JOIN_SOURCE_GROUP 46 /* join a source-specific group */ ++#define MCAST_LEAVE_SOURCE_GROUP 47 /* leave a single source */ ++#define MCAST_MSFILTER 48 ++ ++#define LWIP_MCAST_SRC_TBL_SIZE 32 ++ ++ ++#if (LWIP_IPV4 && LWIP_IGMP) ++#if GAZELLE_UDP_ENABLE ++#define IP_ADD_MEMBERSHIP 35 ++#define IP_DROP_MEMBERSHIP 36 ++#else ++/* ++ * Options and types related to multicast membership ++ */ ++#define IP_ADD_MEMBERSHIP 3 ++#define IP_DROP_MEMBERSHIP 4 ++#endif /* GAZELLE_UDP_ENABLE */ ++ ++typedef struct ip_mreq { ++ struct in_addr imr_multiaddr; /* IP multicast address of group */ ++ struct in_addr imr_interface; /* local IP address of interface */ ++} ip_mreq; ++ ++/* IPv4 Source Filter Multicast API [RFC3678] */ ++#define IP_UNBLOCK_SOURCE 37 /* unblock a source */ ++#define IP_BLOCK_SOURCE 38 /* block a source */ ++#define IP_ADD_SOURCE_MEMBERSHIP 39 /* join a source-specific group */ ++#define IP_DROP_SOURCE_MEMBERSHIP 40 /* drop a single source */ ++#define IP_MSFILTER 41 /* set/get msfilter */ ++/* ++ * Argument structure for IPv4 Multicast Source Filter APIs. [RFC3678] ++ */ ++struct ip_mreq_source { ++ struct in_addr imr_multiaddr; /* IP multicast address of group */ ++ struct in_addr imr_sourceaddr; /* IP address of source */ ++ struct in_addr imr_interface; /* local IP address of interface */ ++}; ++ ++#define IP_MSFILTER_SIZE(numsrc) \ ++ (sizeof(struct ip_msfilter) - sizeof(struct in_addr) \ ++ + (numsrc) * sizeof(struct in_addr)) ++ ++struct ip_msfilter { ++ struct in_addr imsf_multiaddr; ++ struct in_addr imsf_interface; ++ u32_t imsf_fmode; ++#define MCAST_EXCLUDE 0 ++#define MCAST_INCLUDE 1 ++ u32_t imsf_numsrc; ++ struct in_addr imsf_slist[1]; ++}; ++#endif /*(LWIP_IPV4 && LWIP_IGMP)*/ ++ ++/* +++ * Argument structures for Protocol-Independent Multicast Source +++ * Filter APIs. [RFC3678] +++ */ ++struct group_req { ++ u32_t gr_interface; /* interface index */ ++ struct sockaddr_storage gr_group; /* group address */ ++}; ++ ++struct group_source_req { ++ u32_t gsr_interface; /* interface index */ ++ struct sockaddr_storage gsr_group; /* group address */ ++ struct sockaddr_storage gsr_source; /* source address */ ++}; ++ ++#define GROUP_FILTER_SIZE(numsrc) \ ++ (sizeof(struct group_filter) - sizeof(struct sockaddr_storage) \ ++ + (numsrc) * sizeof(struct sockaddr_storage)) ++ ++struct group_filter { ++ u32_t gf_interface; /* interface index */ ++ struct sockaddr_storage gf_group; /* multicast address */ ++ u32_t gf_fmode; /* filter mode */ ++ u32_t gf_numsrc; /* number of sources */ ++ struct sockaddr_storage gf_slist[1]; /* interface index */ ++}; ++ ++err_t mcast_set_msfilter_netif(struct ip_mc *ipmc, struct netif *netif, const ip_addr_t *multi_addr, const struct ip_msfilter *imsf); ++err_t mcast_set_groupfilter_netif(struct ip_mc *ipmc, struct netif *netif, const ip_addr_t *multi_addr, const struct group_filter *gf); ++err_t mcast_get_msfilter_netif(struct ip_mc *ipmc, struct netif *netif, const ip_addr_t *multi_addr, struct ip_msfilter *imsf, socklen_t *bufsz); ++err_t mcast_get_groupfilter_netif(struct ip_mc *ipmc, struct netif *netif, const ip_addr_t *multi_addr, struct group_filter *gf, socklen_t *bufsz); ++ ++int mcast_sock_add_drop_membership(int s, struct ip_mc *ipmc, int optname, const struct ip_mreq *imr); ++int mcast_sock_add_drop_source_membership(int s, struct ip_mc *ipmc, int optname, const struct ip_mreq_source *imr); ++int mcast_sock_block_unblock_source(struct ip_mc *ipmc, int optname, const struct ip_mreq_source *imr); ++int mcast_sock_set_msfilter(struct ip_mc *ipmc, int optname, const struct ip_msfilter *imsf); ++int mcast_sock_get_msfilter(struct ip_mc *ipmc, int optname, struct ip_msfilter *imsf, socklen_t *size); ++int mcast_sock_join_leave_group(int s, struct ip_mc *ipmc, int optname, const struct group_req *gr); ++int mcast_sock_join_leave_source_group(int s, struct ip_mc *ipmc, int optname, const struct group_source_req *gsr); ++int mcast_sock_block_unblock_source_group(struct ip_mc *ipmc, int optname, const struct group_source_req *gsr); ++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); ++ ++#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); + int lwip_inet_pton(int af, const char *src, void *dst); + +diff -Nur lwip-2.2.0init/src/include/lwip/udp.h lwip-2.2.0/src/include/lwip/udp.h +--- lwip-2.2.0init/src/include/lwip/udp.h 2024-03-14 19:14:54.472020996 +0800 ++++ lwip-2.2.0/src/include/lwip/udp.h 2024-03-14 19:15:36.792020996 +0800 +@@ -48,6 +48,7 @@ + #include "lwip/ip.h" + #include "lwip/ip6_addr.h" + #include "lwip/prot/udp.h" ++#include "lwip/mcast.h" + + #ifdef __cplusplus + extern "C" { +@@ -101,6 +102,10 @@ + u8_t mcast_ttl; + #endif /* LWIP_MULTICAST_TX_OPTIONS */ + ++#if (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) ++ struct ip_mc ipmc; ++#endif /* (LWIP_IPV4 && LWIP_IGMP) || (LWIP_IPV6 && LWIP_IPV6_MLD) */ ++ + #if LWIP_UDPLITE + /** used for UDP_LITE only */ + u16_t chksum_len_rx, chksum_len_tx; diff --git a/lwip.spec b/lwip.spec index 21484d55dbc4cfba6fa202603deeaee4f209534f..a014f0c23643c3ec4cb1cee5571abce6e213d54a 100644 --- a/lwip.spec +++ b/lwip.spec @@ -4,7 +4,7 @@ Summary: lwip is a small independent implementation of the TCP/IP protocol suite Name: lwip Version: 2.2.0 -Release: 10 +Release: 11 License: BSD URL: http://savannah.nongnu.org/projects/lwip/ Source0: http://download.savannah.nongnu.org/releases/lwip/%{name}-%{version}.zip @@ -130,6 +130,8 @@ Patch9114: 0115-enable-LWIP_SO_RCVTIMEO-to-support-recv-accept-timeo.patch Patch9115: 0116-transfer-pbuf-timestamp-in-ip-frag.patch Patch9116: 0117-udp-muticast-loop.patch +Patch9117: 0115-support-igmpv3.patch + BuildRequires: gcc-c++ dos2unix dpdk-devel #Requires: @@ -158,6 +160,9 @@ cd %{_builddir}/%{name}-%{version}/src %{_libdir}/liblwip.a %changelog +* Thu Mar 14 2024 zhangylulong - 2.2.0-11 +- add igmp v3 support + * Fri Mar 22 2024 zhujunhao - 2.2.0-10 - add udp muticast loop