From ab8119360e6a1759f29321ed7c98a08e2cffaf06 Mon Sep 17 00:00:00 2001 From: David van Moolenbroek Date: Mon, 6 Feb 2017 14:12:37 +0000 Subject: [PATCH] udp: add core-level multicast support for IPv6 So far, the UDP core module implemented only IPv4 multicast support. This patch extends the module with the features necessary for socket layers on top to implement IPv6 multicast support as well: o If a UDP PCB is bound to an IPv6 multicast address, a unicast source address is selected and used to send the packet instead, as is required (and was the case for IPv4 multicast already). o Unlike IPv4's IP_MULTICAST_IF socket option, which takes a source IPv4 address, the IPV6_MULTICAST_IF socket option (from RFC 3493) takes an interface identifier to denote the interface to use for outgoing multicast-destined packets. A new pair of UDP PCB API calls, udp_[gs]et_multicast_netif_index(), are added to support this. The new definition "NETIF_NO_INDEX" may be used to indicate that lwIP should pick an interface instead. IPv4 socket implementations may now also choose to map the given source address to an interface index immediately and use the new facility instead of the old udp_[gs]et_multicast_netif_addr() one. A side effect of limiting the old facility to IPv4 is that for dual- stack configurations with multicast support, the UDP PCB size is reduced by (up to) 16 bytes. o For configurations that enable loopback interface support, the IPv6 code now also supports multicast loopback (IPV6_MULTICAST_LOOP). o The LWIP_MULTICAST_TX_OPTIONS opt.h setting now covers both IPv4 and IPv6, and as such is no longer strictly linked to IGMP. It is therefore placed in its own lwIP options subgroup in opt.h. The IPV6_MULTICAST_HOPS socket option can already be implemented using the existing IP_MULTICAST_TTL support, and thus requires no additional changes. Overall, this patch should not break any existing code. --- src/api/sockets.c | 8 +++--- src/core/init.c | 5 +--- src/core/ipv6/ip6.c | 5 ++++ src/core/udp.c | 62 ++++++++++++++++++++++++++++------------ src/include/lwip/netif.h | 1 + src/include/lwip/opt.h | 32 +++++++++++++++------ src/include/lwip/udp.h | 18 ++++++++---- 7 files changed, 90 insertions(+), 41 deletions(-) diff --git a/src/api/sockets.c b/src/api/sockets.c index f67162ab..27d8bc76 100644 --- a/src/api/sockets.c +++ b/src/api/sockets.c @@ -2020,7 +2020,7 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n", s, *(int *)optval)); break; -#if LWIP_MULTICAST_TX_OPTIONS +#if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS case IP_MULTICAST_TTL: LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t); if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) { @@ -2049,7 +2049,7 @@ lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *opt LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_LOOP) = %d\n", s, *(int *)optval)); break; -#endif /* LWIP_MULTICAST_TX_OPTIONS */ +#endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS */ default: LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n", s, optname)); @@ -2389,7 +2389,7 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %d\n", s, sock->conn->pcb.ip->tos)); break; -#if LWIP_MULTICAST_TX_OPTIONS +#if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS case IP_MULTICAST_TTL: LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP); udp_set_multicast_ttl(sock->conn->pcb.udp, (u8_t)(*(const u8_t*)optval)); @@ -2410,7 +2410,7 @@ lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_ udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_MULTICAST_LOOP); } break; -#endif /* LWIP_MULTICAST_TX_OPTIONS */ +#endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS */ #if LWIP_IGMP case IP_ADD_MEMBERSHIP: case IP_DROP_MEMBERSHIP: diff --git a/src/core/init.c b/src/core/init.c index ee415a97..1f89997e 100644 --- a/src/core/init.c +++ b/src/core/init.c @@ -96,7 +96,7 @@ PACK_STRUCT_END #error "If you want to use DHCP, you have to define LWIP_UDP=1 in your lwipopts.h" #endif #if (!LWIP_UDP && LWIP_MULTICAST_TX_OPTIONS) - #error "If you want to use IGMP/LWIP_MULTICAST_TX_OPTIONS, you have to define LWIP_UDP=1 in your lwipopts.h" + #error "If you want to use LWIP_MULTICAST_TX_OPTIONS, you have to define LWIP_UDP=1 in your lwipopts.h" #endif #if (!LWIP_UDP && LWIP_DNS) #error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h" @@ -123,9 +123,6 @@ PACK_STRUCT_END #if (LWIP_IGMP && !LWIP_IPV4) #error "IGMP needs LWIP_IPV4 enabled in your lwipopts.h" #endif -#if (LWIP_MULTICAST_TX_OPTIONS && !LWIP_IPV4) - #error "LWIP_MULTICAST_TX_OPTIONS needs LWIP_IPV4 enabled in your lwipopts.h" -#endif #if ((LWIP_NETCONN || LWIP_SOCKET) && (MEMP_NUM_TCPIP_MSG_API<=0)) #error "If you want to use Sequential API, you have to define MEMP_NUM_TCPIP_MSG_API>=1 in your lwipopts.h" #endif diff --git a/src/core/ipv6/ip6.c b/src/core/ipv6/ip6.c index 55567073..59d112d9 100644 --- a/src/core/ipv6/ip6.c +++ b/src/core/ipv6/ip6.c @@ -1028,6 +1028,11 @@ ip6_output_if_src(struct pbuf *p, const ip6_addr_t *src, const ip6_addr_t *dest, } } } +#if LWIP_MULTICAST_TX_OPTIONS + if ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) { + netif_loop_output(netif, p); + } +#endif /* LWIP_MULTICAST_TX_OPTIONS */ #endif /* ENABLE_LOOPBACK */ #if LWIP_IPV6_FRAG /* don't fragment if interface has mtu set to 0 [loopif] */ diff --git a/src/core/udp.c b/src/core/udp.c index 5bdb3cd1..25a04ade 100644 --- a/src/core/udp.c +++ b/src/core/udp.c @@ -514,7 +514,7 @@ udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, { #endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */ struct netif *netif; - const ip_addr_t *dst_ip_route = dst_ip; + const ip_addr_t *src_ip_route; if ((pcb == NULL) || (dst_ip == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) { return ERR_VAL; @@ -522,8 +522,31 @@ udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send\n")); -#if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS - if (ip_addr_ismulticast(dst_ip_route)) { + if (IP_IS_ANY_TYPE_VAL(pcb->local_ip)) { + /* Don't call ip_route() with IP_ANY_TYPE */ + src_ip_route = IP46_ADDR_ANY(IP_GET_TYPE(dst_ip)); + } else { + src_ip_route = &pcb->local_ip; + } + +#if LWIP_MULTICAST_TX_OPTIONS + netif = NULL; + if (ip_addr_ismulticast(dst_ip)) { + /* For IPv6, the interface to use for packets with a multicast destination + * is specified using an interface index. The same approach may be used for + * IPv4 as well, in which case it overrides the IPv4 multicast override + * address below. Here we have to look up the netif by going through the + * list, but by doing so we skip a route lookup. If the interface index has + * gone stale, we fall through and do the regular route lookup after all. */ + if (pcb->mcast_ifindex != NETIF_NO_INDEX) { + for (netif = netif_list; netif != NULL; netif = netif->next) { + if (pcb->mcast_ifindex == netif_num_to_index(netif)) { + break; /* found! */ + } + } + } +#if LWIP_IPV4 + else #if LWIP_IPV6 if (IP_IS_V4(dst_ip)) #endif /* LWIP_IPV6 */ @@ -531,21 +554,21 @@ udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, /* 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->multicast_ip that is used for routing. */ - if (!ip_addr_isany_val(pcb->multicast_ip) && - !ip4_addr_cmp(ip_2_ip4(&pcb->multicast_ip), IP4_ADDR_BROADCAST)) { - dst_ip_route = &pcb->multicast_ip; + 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(src_ip_route), &pcb->mcast_ip4); } } +#endif /* LWIP_IPV4 */ } -#endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS */ - /* find the outgoing network interface for this packet */ - if(IP_IS_ANY_TYPE_VAL(pcb->local_ip)) { - /* Don't call ip_route() with IP_ANY_TYPE */ - netif = ip_route(IP46_ADDR_ANY(IP_GET_TYPE(dst_ip_route)), dst_ip_route); - } else { - netif = ip_route(&pcb->local_ip, dst_ip_route); + if (netif == NULL) +#endif /* LWIP_MULTICAST_TX_OPTIONS */ + { + /* find the outgoing network interface for this packet */ + netif = ip_route(src_ip_route, dst_ip); } /* no outgoing network interface could be found? */ @@ -604,10 +627,11 @@ udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_i return ERR_VAL; } - /* PCB local address is IP_ANY_ADDR? */ + /* PCB local address is IP_ANY_ADDR or multicast? */ #if LWIP_IPV6 if (IP_IS_V6(dst_ip)) { - if (ip6_addr_isany(ip_2_ip6(&pcb->local_ip))) { + if (ip6_addr_isany(ip_2_ip6(&pcb->local_ip)) || + ip6_addr_ismulticast(ip_2_ip6(&pcb->local_ip))) { src_ip = ip6_select_source_address(netif, ip_2_ip6(dst_ip)); if (src_ip == NULL) { /* No suitable source address was found. */ @@ -737,11 +761,11 @@ udp_sendto_if_src_chksum(struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *d udphdr->chksum = 0x0000; /* Multicast Loop? */ -#if (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS) || (LWIP_IPV6 && LWIP_IPV6_MLD) +#if LWIP_MULTICAST_TX_OPTIONS if (((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) && ip_addr_ismulticast(dst_ip)) { q->flags |= PBUF_FLAG_MCASTLOOP; } -#endif /* (LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS) || (LWIP_IPV6 && LWIP_IPV6_MLD) */ +#endif /* LWIP_MULTICAST_TX_OPTIONS */ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"\n", q->tot_len)); @@ -917,7 +941,7 @@ udp_bind(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port) * This is legacy support: scope-aware callers should always provide properly * zoned source addresses. Do the zone selection before the address-in-use * check below; as such we have to make a temporary copy of the address. */ - if (IP_IS_V6(ipaddr) && ip6_addr_lacks_zone(ip_2_ip6(ipaddr), IP6_UNICAST)) { + if (IP_IS_V6(ipaddr) && ip6_addr_lacks_zone(ip_2_ip6(ipaddr), IP6_UNKNOWN)) { ip_addr_copy(zoned_ipaddr, *ipaddr); ip6_addr_select_zone(ip_2_ip6(&zoned_ipaddr), ip_2_ip6(&zoned_ipaddr)); ipaddr = &zoned_ipaddr; diff --git a/src/include/lwip/netif.h b/src/include/lwip/netif.h index 7241b42a..0b47ec06 100644 --- a/src/include/lwip/netif.h +++ b/src/include/lwip/netif.h @@ -504,6 +504,7 @@ char * netif_index_to_name(u8_t idx, char *name); /* Interface indexes always start at 1 per RFC 3493, section 4, num starts at 0 */ #define netif_num_to_index(netif) ((netif)->num + 1) #define netif_index_to_num(index) ((index) - 1) +#define NETIF_NO_INDEX (0) #ifdef __cplusplus } diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h index eb911e92..1c4f2919 100644 --- a/src/include/lwip/opt.h +++ b/src/include/lwip/opt.h @@ -972,7 +972,29 @@ /* ---------------------------------- - ----- Multicast/IGMP options ----- + -------- Multicast options ------- + ---------------------------------- +*/ +/** + * @defgroup lwip_opts_multicast Multicast + * @ingroup lwip_opts_infrastructure + * @{ + */ +/** + * LWIP_MULTICAST_TX_OPTIONS==1: Enable multicast TX support like the socket options + * IP_MULTICAST_TTL/IP_MULTICAST_IF/IP_MULTICAST_LOOP, as well as (currently only) + * core support for the corresponding IPv6 options. + */ +#if !defined LWIP_MULTICAST_TX_OPTIONS || defined __DOXYGEN__ +#define LWIP_MULTICAST_TX_OPTIONS ((LWIP_IGMP || LWIP_IPV6_MLD) && LWIP_UDP) +#endif +/** + * @} + */ + +/* + ---------------------------------- + ---------- IGMP options ---------- ---------------------------------- */ /** @@ -990,14 +1012,6 @@ #undef LWIP_IGMP #define LWIP_IGMP 0 #endif - -/** - * LWIP_MULTICAST_TX_OPTIONS==1: Enable multicast TX support like the socket options - * IP_MULTICAST_TTL/IP_MULTICAST_IF/IP_MULTICAST_LOOP - */ -#if !defined LWIP_MULTICAST_TX_OPTIONS || defined __DOXYGEN__ -#define LWIP_MULTICAST_TX_OPTIONS (LWIP_IGMP && LWIP_UDP) -#endif /** * @} */ diff --git a/src/include/lwip/udp.h b/src/include/lwip/udp.h index b9299073..30cfb332 100644 --- a/src/include/lwip/udp.h +++ b/src/include/lwip/udp.h @@ -91,8 +91,12 @@ struct udp_pcb { u16_t local_port, remote_port; #if LWIP_MULTICAST_TX_OPTIONS - /** outgoing network interface for multicast packets */ - ip_addr_t multicast_ip; +#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 */ @@ -159,9 +163,13 @@ void udp_init (void); #define udp_new_ip6() udp_new_ip_type(IPADDR_TYPE_V6) #if LWIP_MULTICAST_TX_OPTIONS -#define udp_set_multicast_netif_addr(pcb, ip4addr) ip_addr_copy_from_ip4((pcb)->multicast_ip, *(ip4addr)) -#define udp_get_multicast_netif_addr(pcb) ip_2_ip4(&(pcb)->multicast_ip) -#define udp_set_multicast_ttl(pcb, value) do { (pcb)->mcast_ttl = value; } while(0) +#if LWIP_IPV4 +#define udp_set_multicast_netif_addr(pcb, ip4addr) ip4_addr_copy((pcb)->mcast_ip4, *(ip4addr)) +#define udp_get_multicast_netif_addr(pcb) (&(pcb)->mcast_ip4) +#endif /* LWIP_IPV4 */ +#define udp_set_multicast_netif_index(pcb, idx) ((pcb)->mcast_ifindex = (idx)) +#define udp_get_multicast_netif_index(pcb) ((pcb)->mcast_ifindex) +#define udp_set_multicast_ttl(pcb, value) ((pcb)->mcast_ttl = (value)) #define udp_get_multicast_ttl(pcb) ((pcb)->mcast_ttl) #endif /* LWIP_MULTICAST_TX_OPTIONS */