From 7d978b637ce33eb186d8a1c2dcaa9475ab2e883f Mon Sep 17 00:00:00 2001
From: David van Moolenbroek <david@minix3.org>
Date: Mon, 6 Feb 2017 17:10:45 +0000
Subject: [PATCH 2/2] 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 1f89997..0cc90a8 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 2577be6..d357051 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 5fe34dd..0fb32f9 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 644ba1a..1235986 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
-- 
2.5.2

