From 6ce9a01c3e1b8134a24da0098ae6f38786818a50 Mon Sep 17 00:00:00 2001 From: David van Moolenbroek Date: Mon, 6 Feb 2017 17:10:45 +0000 Subject: [PATCH] raw: add core support for multicast TX options The patch simply copies the relevant bits from the UDP implementation. Perhaps most notably, the patch does *not* copy the IPv4-only UDP support for IP_MULTICAST_IF, because that option can also be implemented using the interface index based approach. Largely thanks to this omission, at least on 32-bit platforms, this patch does not increase the RAW PCB size at all. --- src/core/init.c | 4 ++-- src/core/raw.c | 52 +++++++++++++++++++++++++++++++++++------- src/include/lwip/opt.h | 2 +- src/include/lwip/raw.h | 19 +++++++++++++-- 4 files changed, 64 insertions(+), 13 deletions(-) diff --git a/src/core/init.c b/src/core/init.c index 1f89997e..0cc90a88 100644 --- a/src/core/init.c +++ b/src/core/init.c @@ -95,8 +95,8 @@ PACK_STRUCT_END #if (!LWIP_UDP && LWIP_DHCP) #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 LWIP_MULTICAST_TX_OPTIONS, you have to define LWIP_UDP=1 in your lwipopts.h" +#if (!LWIP_UDP && !LWIP_RAW && LWIP_MULTICAST_TX_OPTIONS) + #error "If you want to use LWIP_MULTICAST_TX_OPTIONS, you have to define LWIP_UDP=1 and/or LWIP_RAW=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" diff --git a/src/core/raw.c b/src/core/raw.c index 2577be64..d357051d 100644 --- a/src/core/raw.c +++ b/src/core/raw.c @@ -220,7 +220,7 @@ raw_bind(struct raw_pcb *pcb, const ip_addr_t *ipaddr) * This is legacy support: scope-aware callers should always provide properly * zoned source addresses. */ if (IP_IS_V6(&pcb->local_ip) && - ip6_addr_lacks_zone(ip_2_ip6(&pcb->local_ip), IP6_UNICAST)) { + ip6_addr_lacks_zone(ip_2_ip6(&pcb->local_ip), IP6_UNKNOWN)) { ip6_addr_select_zone(ip_2_ip6(&pcb->local_ip), ip_2_ip6(&pcb->local_ip)); } #endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */ @@ -325,11 +325,28 @@ raw_sendto(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *ipaddr) LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n")); - 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(ipaddr)), ipaddr); - } else { - netif = ip_route(&pcb->local_ip, ipaddr); +#if LWIP_MULTICAST_TX_OPTIONS + netif = NULL; + if (ip_addr_ismulticast(ipaddr) && (pcb->mcast_ifindex != NETIF_NO_INDEX)) { + /* 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. */ + for (netif = netif_list; netif != NULL; netif = netif->next) { + if (pcb->mcast_ifindex == netif_num_to_index(netif)) { + break; /* found! */ + } + } + } + + if (netif == NULL) +#endif /* LWIP_MULTICAST_TX_OPTIONS */ + { + 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(ipaddr)), ipaddr); + } else { + netif = ip_route(&pcb->local_ip, ipaddr); + } } if (netif == NULL) { @@ -338,7 +355,7 @@ raw_sendto(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *ipaddr) return ERR_RTE; } - if (ip_addr_isany(&pcb->local_ip)) { + if (ip_addr_isany(&pcb->local_ip) || ip_addr_ismulticast(&pcb->local_ip)) { /* use outgoing network interface IP address as source address */ src_ip = ip_netif_get_local_ip(netif, ipaddr); #if LWIP_IPV6 @@ -374,6 +391,7 @@ raw_sendto_if_src(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, err_t err; struct pbuf *q; /* q will be sent down the stack */ s16_t header_size; + u8_t ttl; if ((pcb == NULL) || (dst_ip == NULL) || (netif == NULL) || (src_ip == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, src_ip) || !IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) { @@ -397,6 +415,7 @@ raw_sendto_if_src(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, if (p->len < header_size) { return ERR_VAL; } + /* @todo multicast loop support, if at all desired for this scenario.. */ NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint); err = ip_output_if_hdrincl(p, src_ip, dst_ip, netif); NETIF_SET_HWADDRHINT(netif, NULL); @@ -446,6 +465,13 @@ raw_sendto_if_src(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, } #endif /* IP_SOF_BROADCAST */ + /* Multicast Loop? */ +#if LWIP_MULTICAST_TX_OPTIONS + if (((pcb->flags & RAW_FLAGS_MULTICAST_LOOP) != 0) && ip_addr_ismulticast(dst_ip)) { + q->flags |= PBUF_FLAG_MCASTLOOP; + } +#endif /* LWIP_MULTICAST_TX_OPTIONS */ + #if LWIP_IPV6 /* If requested, based on the IPV6_CHECKSUM socket option per RFC3542, compute the checksum and update the checksum in the payload. */ @@ -456,8 +482,15 @@ raw_sendto_if_src(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, } #endif + /* Determine TTL to use */ +#if LWIP_MULTICAST_TX_OPTIONS + ttl = (ip_addr_ismulticast(dst_ip) ? raw_get_multicast_ttl(pcb) : pcb->ttl); +#else /* LWIP_MULTICAST_TX_OPTIONS */ + ttl = pcb->ttl; +#endif /* LWIP_MULTICAST_TX_OPTIONS */ + NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint); - err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, pcb->protocol, netif); + err = ip_output_if(q, src_ip, dst_ip, ttl, pcb->tos, pcb->protocol, netif); NETIF_SET_HWADDRHINT(netif, NULL); /* did we chain a header earlier? */ @@ -538,6 +571,9 @@ raw_new(u8_t proto) memset(pcb, 0, sizeof(struct raw_pcb)); pcb->protocol = proto; pcb->ttl = RAW_TTL; +#if LWIP_MULTICAST_TX_OPTIONS + raw_set_multicast_ttl(pcb, RAW_TTL); +#endif /* LWIP_MULTICAST_TX_OPTIONS */ pcb->next = raw_pcbs; raw_pcbs = pcb; } diff --git a/src/include/lwip/opt.h b/src/include/lwip/opt.h index 1c4f2919..4d98bdd3 100644 --- a/src/include/lwip/opt.h +++ b/src/include/lwip/opt.h @@ -986,7 +986,7 @@ * 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) +#define LWIP_MULTICAST_TX_OPTIONS ((LWIP_IGMP || LWIP_IPV6_MLD) && (LWIP_UDP || LWIP_RAW)) #endif /** * @} diff --git a/src/include/lwip/raw.h b/src/include/lwip/raw.h index 644ba1a8..1235986b 100644 --- a/src/include/lwip/raw.h +++ b/src/include/lwip/raw.h @@ -52,8 +52,9 @@ extern "C" { #endif -#define RAW_FLAGS_CONNECTED 0x01U -#define RAW_FLAGS_HDRINCL 0x02U +#define RAW_FLAGS_CONNECTED 0x01U +#define RAW_FLAGS_HDRINCL 0x02U +#define RAW_FLAGS_MULTICAST_LOOP 0x04U struct raw_pcb; @@ -80,6 +81,13 @@ struct raw_pcb { u8_t protocol; u8_t flags; +#if LWIP_MULTICAST_TX_OPTIONS + /** 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 */ + /** receive callback function */ raw_recv_fn recv; /* user-supplied argument for the recv callback */ @@ -118,6 +126,13 @@ void raw_netif_ip_addr_changed(const ip_addr_t* old_addr, const ip_addr_t* new_a /* for compatibility with older implementation */ #define raw_new_ip6(proto) raw_new_ip_type(IPADDR_TYPE_V6, proto) +#if LWIP_MULTICAST_TX_OPTIONS +#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)) +#define raw_get_multicast_ttl(pcb) ((pcb)->mcast_ttl) +#endif /* LWIP_MULTICAST_TX_OPTIONS */ + #ifdef __cplusplus } #endif