From 04716a65a57334a6a063e1167da55106a0415545 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Thu, 6 Jul 2017 07:26:01 -0600 Subject: [PATCH] IP forwading: Add optional support to forward broadcast and multicast packets. --- net/Kconfig | 10 +- net/devif/devif.h | 60 -------- net/devif/ip_forward.h | 134 ++++++++++++++++- net/devif/ipv4_forward.c | 301 ++++++++++++++++++++++++++------------- net/devif/ipv4_input.c | 16 ++- net/devif/ipv6_forward.c | 279 ++++++++++++++++++++++++------------ net/devif/ipv6_input.c | 11 ++ 7 files changed, 563 insertions(+), 248 deletions(-) diff --git a/net/Kconfig b/net/Kconfig index 0a0d9830b6..663e159aea 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -293,10 +293,18 @@ config NET_IPFORWARD that are not supported by this platform will be forwarded to the appropriate network device. Routing table support may be required. +config NET_IPFORWARD_BROADCAST + bool "Forward broadcast/multicast packets" + default n + depends on NET_IPFORWARD && NETDEV_MULTINIC + ---help--- + If selected, broadcast packets received on one network device will + be forwarded though other network devices. + config NET_IPFORWARD_NSTRUCT int "Number of pre-allocated forwarding structures" default 4 - depends on NET_IPFORWARD && CONFIG_NETDEV_MULTINIC + depends on NET_IPFORWARD && NETDEV_MULTINIC ---help--- When packets are forwarded from on device to another, a structure must be allocated to hold the state of forwarding across several diff --git a/net/devif/devif.h b/net/devif/devif.h index efacd00714..e90377dd58 100644 --- a/net/devif/devif.h +++ b/net/devif/devif.h @@ -425,66 +425,6 @@ uint16_t devif_conn_event(FAR struct net_driver_s *dev, FAR void *pvconn, uint16_t devif_dev_event(FAR struct net_driver_s *dev, void *pvconn, uint16_t flags); -/**************************************************************************** - * Name: ipv4_forward - * - * Description: - * This function is called from ipv4_input when a packet is received that - * is not destined for us. In this case, the packet may need to be - * forwarded to another device (or sent back out the same device) - * depending configuration, routing table information, and the IPv4 - * networks served by various network devices. - * - * Input Parameters: - * dev - The device on which the packet was received and which contains - * the IPv4 packet. - * ipv4 - A convenience pointer to the IPv4 header in within the IPv4 - * packet - * - * On input: - * - dev->d_buf holds the received packet. - * - dev->d_len holds the length of the received packet MINUS the - * size of the L1 header. That was subtracted out by ipv4_input. - * - ipv4 points to the IPv4 header with dev->d_buf. - * - * Returned Value: - * Zero is returned if the packet was successfully forward; A negated - * errno value is returned if the packet is not forwardable. In that - * latter case, the caller (ipv4_input()) should drop the packet. - * - ****************************************************************************/ - -#if defined(CONFIG_NET_IPFORWARD) && defined(CONFIG_NET_IPv4) -int ipv4_forward(FAR struct net_driver_s *dev, FAR struct ipv4_hdr_s *ipv4); -#endif - -/**************************************************************************** - * Name: ipv6_forward - * - * Description: - * This function is called from ipv6_input when a packet is received that - * is not destined for us. In this case, the packet may need to be - * forwarded to another device (or sent back out the same device) - * depending configuration, routing table information, and the IPv6 - * networks served by various network devices. - * - * Input Parameters: - * dev - The device on which the packet was received and which contains - * the IPv6 packet. - * ipv6 - A convenience pointer to the IPv6 header in within the IPv6 - * packet - * - * Returned Value: - * Zero is returned if the packet was successfully forward; A negated - * errno value is returned if the packet is not forwardable. In that - * latter case, the caller (ipv6_input()) should drop the packet. - * - ****************************************************************************/ - -#if defined(CONFIG_NET_IPFORWARD) && defined(CONFIG_NET_IPv6) -int ipv6_forward(FAR struct net_driver_s *dev, FAR struct ipv6_hdr_s *ipv6); -#endif - /**************************************************************************** * Send data on the current connection. * diff --git a/net/devif/ip_forward.h b/net/devif/ip_forward.h index f7b88b4e2e..5eed4d8bb3 100644 --- a/net/devif/ip_forward.h +++ b/net/devif/ip_forward.h @@ -55,7 +55,13 @@ #include "icmpv6/icmpv6.h" #undef HAVE_FWDALLOC -#if defined(CONFIG_NET_IPFORWARD) && defined(CONFIG_NETDEV_MULTINIC) +#ifdef CONFIG_NET_IPFORWARD + +/* Must of the logic in this header file applies only for configurations + * will multiple network devices. + */ + +#ifdef CONFIG_NETDEV_MULTINIC /**************************************************************************** * Pre-processor Definitions @@ -205,6 +211,68 @@ FAR struct forward_s *ip_forward_alloc(void); void ip_forward_free(FAR struct forward_s *fwd); +/**************************************************************************** + * Name: ipv4_forward_broadcast + * + * Description: + * This function is called from ipv4_input when a broadcast or multicast + * packet is received. If CONFIG_NET_IPFORWARD_BROADCAST is enabled, this + * function will forward the broadcast packet to other networks through + * other network devices. + * + * Input Parameters: + * dev - The device on which the packet was received and which contains + * the IPv4 packet. + * ipv4 - A convenience pointer to the IPv4 header in within the IPv4 + * packet + * + * On input: + * - dev->d_buf holds the received packet. + * - dev->d_len holds the length of the received packet MINUS the + * size of the L1 header. That was subtracted out by ipv4_input. + * - ipv4 points to the IPv4 header with dev->d_buf. + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_NET_IPFORWARD_BROADCAST +void ipv4_forward_broadcast(FAR struct net_driver_s *dev, + FAR struct ipv4_hdr_s *ipv4); +#endif + +/**************************************************************************** + * Name: ipv6_forward_broadcast + * + * Description: + * This function is called from ipv6_input when a broadcast or multicast + * packet is received. If CONFIG_NET_IPFORWARD_BROADCAST is enabled, this + * function will forward the broadcast packet to other networks through + * other network devices. + * + * Input Parameters: + * dev - The device on which the packet was received and which contains + * the IPv6 packet. + * ipv6 - A convenience pointer to the IPv6 header in within the IPv6 + * packet + * + * On input: + * - dev->d_buf holds the received packet. + * - dev->d_len holds the length of the received packet MINUS the + * size of the L1 header. That was subtracted out by ipv6_input. + * - ipv6 points to the IPv6 header with dev->d_buf. + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_NET_IPFORWARD_BROADCAST +void ipv6_forward_broadcast(FAR struct net_driver_s *dev, + FAR struct ipv6_hdr_s *ipv6); +#endif + /**************************************************************************** * Name: devif_forward * @@ -225,5 +293,67 @@ void ip_forward_free(FAR struct forward_s *fwd); void devif_forward(FAR struct forward_s *fwd); -#endif /* CONFIG_NET_IPFORWARD && CONFIG_NETDEV_MULTINIC */ +#endif /* CONFIG_NETDEV_MULTINIC */ + +/**************************************************************************** + * Name: ipv4_forward + * + * Description: + * This function is called from ipv4_input when a packet is received that + * is not destined for us. In this case, the packet may need to be + * forwarded to another device (or sent back out the same device) + * depending configuration, routing table information, and the IPv4 + * networks served by various network devices. + * + * Input Parameters: + * dev - The device on which the packet was received and which contains + * the IPv4 packet. + * ipv4 - A convenience pointer to the IPv4 header in within the IPv4 + * packet + * + * On input: + * - dev->d_buf holds the received packet. + * - dev->d_len holds the length of the received packet MINUS the + * size of the L1 header. That was subtracted out by ipv4_input. + * - ipv4 points to the IPv4 header with dev->d_buf. + * + * Returned Value: + * Zero is returned if the packet was successfully forward; A negated + * errno value is returned if the packet is not forwardable. In that + * latter case, the caller (ipv4_input()) should drop the packet. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_IPv4 +int ipv4_forward(FAR struct net_driver_s *dev, FAR struct ipv4_hdr_s *ipv4); +#endif + +/**************************************************************************** + * Name: ipv6_forward + * + * Description: + * This function is called from ipv6_input when a packet is received that + * is not destined for us. In this case, the packet may need to be + * forwarded to another device (or sent back out the same device) + * depending configuration, routing table information, and the IPv6 + * networks served by various network devices. + * + * Input Parameters: + * dev - The device on which the packet was received and which contains + * the IPv6 packet. + * ipv6 - A convenience pointer to the IPv6 header in within the IPv6 + * packet + * + * Returned Value: + * Zero is returned if the packet was successfully forward; A negated + * errno value is returned if the packet is not forwardable. In that + * latter case, the caller (ipv6_input()) should drop the packet. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_IPv6 +int ipv6_forward(FAR struct net_driver_s *dev, FAR struct ipv6_hdr_s *ipv6); +#endif + +#endif /* CONFIG_NET_IPFORWARD */ #endif /* __NET_DEVIF_IP_FORWARD_H */ diff --git a/net/devif/ipv4_forward.c b/net/devif/ipv4_forward.c index ef8e8f971f..500e040f19 100644 --- a/net/devif/ipv4_forward.c +++ b/net/devif/ipv4_forward.c @@ -49,6 +49,7 @@ #include #include "netdev/netdev.h" +#include "utils/utils.h" #include "sixlowpan/sixlowpan.h" #include "udp/udp.h" #include "tcp/tcp.h" @@ -122,6 +123,119 @@ static int ipv4_hdrsize(FAR struct ipv4_hdr_s *ipv4) } #endif +/**************************************************************************** + * Name: ipv4_decr_ttl + * + * Description: + * Decrement the IPv4 TTL (time to live value). TTL field is set by the + * sender of the packet and reduced by every router on the route to its + * destination. If the TTL field reaches zero before the datagram arrives + * at its destination, then the datagram is discarded and an ICMP error + * packet (11 - Time Exceeded) is sent back to the sender. + * + * The purpose of the TTL field is to avoid a situation in which an + * undeliverable datagram keeps circulating on an Internet system, and + * such a system eventually becoming swamped by such "immortals". + * + * Input Parameters: + * ipv4 - A pointer to the IPv4 header in within the IPv4 packet to be + * forwarded. + * + * Returned Value: + * The new TTL value is returned. A value <= 0 means the hop limit has + * expired. + * + ****************************************************************************/ + +static int ipv4_decr_ttl(FAR struct ipv4_hdr_s *ipv4) +{ + uint16_t sum; + int ttl = (int)ipv4->ttl - 1; + + if (ttl <= 0) + { +#ifdef CONFIG_NET_ICMP + /* Return an ICMP error packet back to the sender. */ +#warning Missing logic +#endif + + /* Return zero which must cause the packet to be dropped */ + + return 0; + } + + /* Save the updated TTL value */ + + ipv4->ttl = ttl; + + /* Re-calculate the IPv4 checksum. This checksum is the Internet checksum + * of the 20 bytes of the IPv4 header. This checksum will be different + * because we just modify the IPv4 TTL. + */ + + ipv4->ipchksum = 0; + sum = chksum(0, (FAR const uint8_t *)ipv4, IPv4_HDRLEN); + if (sum == 0) + { + sum = 0xffff; + } + else + { + sum = htons(sum); + } + + ipv4->ipchksum = ~sum; + return ttl; +} + +/**************************************************************************** + * Name: ipv4_dropstats + * + * Description: + * Update statistics for a dropped packet. + * + * Input Parameters: + * ipv4 - A convenience pointer to the IPv4 header in within the IPv4 + * packet to be dropped. + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_NET_STATISTICS +static void ipv4_dropstats(FAR struct ipv4_hdr_s *ipv4) +{ + switch (ipv4->proto) + { +#ifdef CONFIG_NET_TCP + case IP_PROTO_TCP: + g_netstats.tcp.drop++; + break; +#endif + +#ifdef CONFIG_NET_UDP + case IP_PROTO_UDP: + g_netstats.udp.drop++; + break; +#endif + +#ifdef CONFIG_NET_ICMP + case IP_PROTO_ICMP: + g_netstats.icmp.drop++; + break; +#endif + + default: + break; + } + + g_netstats.ipv4.drop++; +} +#else +# define ipv4_dropstats(ipv4) +#endif + /**************************************************************************** * Name: ipv4_dev_forward * @@ -210,9 +324,22 @@ static int ipv4_dev_forward(FAR struct net_driver_s *dev, goto errout_with_fwd; } - memcpy(&fwd->f_hdr, ipv4, hdrsize); + memcpy(&fwd->f_hdr.ipv4, ipv4, hdrsize); fwd->f_hdrsize = hdrsize; + /* Decrement the TTL in the copy of the IPv4 header (retaining the + * original TTL in the source). If it decrements to zero, then drop + * the packet. + */ + + ret = ipv4_decr_ttl(&fwd->f_hdr.ipv4.l2); + if (ret < 1) + { + nwarn("WARNING: Hop limit exceeded... Dropping!\n"); + ret = -EMULTIHOP; + goto errout_with_fwd; + } + /* Use the L2 + L3 header size to determine start and size of the data * payload. * @@ -317,103 +444,59 @@ errout: #endif /* CONFIG_NETDEV_MULTINIC */ /**************************************************************************** - * Name: ipv4_decr_ttl + * Name: ipv4_forward_callback * * Description: - * Decrement the IPv4 TTL (time to live value). TTL field is set by the - * sender of the packet and reduced by every router on the route to its - * destination. If the TTL field reaches zero before the datagram arrives - * at its destination, then the datagram is discarded and an ICMP error - * packet (11 - Time Exceeded) is sent back to the sender. - * - * The purpose of the TTL field is to avoid a situation in which an - * undeliverable datagram keeps circulating on an Internet system, and - * such a system eventually becoming swamped by such "immortals". + * This function is a callback from netdev_foreach. It implements the + * the broadcase forwarding action for each network device (other than, of + * course, the device that received the packet). * * Input Parameters: - * ipv4 - A pointer to the IPv4 header in within the IPv4 packet to be - * forwarded. + * dev - The device on which the packet was received and which contains + * the IPv4 packet. + * ipv4 - A convenience pointer to the IPv4 header in within the IPv4 + * packet * * Returned Value: - * The new TTL value is returned. A value <= 0 means the hop limit has - * expired. + * Typically returns zero (meaning to continue the enumeration), but will + * return a non-zero to stop the enumeration if an error occurs. * ****************************************************************************/ -static int ipv4_decr_ttl(FAR struct ipv4_hdr_s *ipv4) +#if defined(CONFIG_NET_IPFORWARD_BROADCAST) && \ + defined(CONFIG_NETDEV_MULTINIC) +int ipv4_forward_callback(FAR struct net_driver_s *fwddev, FAR void *arg) { - int ttl = (int)ipv4->ttl - 1; + FAR struct net_driver_s *dev = (FAR struct net_driver_s *)arg; + FAR struct ipv4_hdr_s *ipv4; + int ret; - if (ttl <= 0) - { -#ifdef CONFIG_NET_ICMP - /* Return an ICMP error packet back to the sender. */ -#warning Missing logic -#endif + DEBUGASSERT(fwddev != NULL && dev != NULL && dev->d_buf != NULL); - /* Return zero which must cause the packet to be dropped */ - - return 0; - } - - /* Save the updated TTL value */ - - ipv4->ttl = ttl; - - /* NOTE: We do not have to recalculate the IPv4 checksum because (1) the - * IPv4 header does not include a checksum itself and (2) the TTL is not - * included in the sum for the TCP and UDP headers. + /* Check if we are forwarding on the same device that we received the + * packet from. */ - return ttl; -} - -/**************************************************************************** - * Name: ipv4_dropstats - * - * Description: - * Update statistics for a dropped packet. - * - * Input Parameters: - * ipv4 - A convenience pointer to the IPv4 header in within the IPv4 - * packet to be dropped. - * - * Returned Value: - * None - * - ****************************************************************************/ - -#ifdef CONFIG_NET_STATISTICS -static void ipv4_dropstats(FAR struct ipv4_hdr_s *ipv4) -{ - switch (ipv4->proto) + if (fwddev != dev) { -#ifdef CONFIG_NET_TCP - case IP_PROTO_TCP: - g_netstats.tcp.drop++; - break; -#endif + /* Recover the pointer to the IPv4 header in the receiving device's + * d_buf. + */ -#ifdef CONFIG_NET_UDP - case IP_PROTO_UDP: - g_netstats.udp.drop++; - break; -#endif + ipv4 = (FAR struct ipv4_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]; -#ifdef CONFIG_NET_ICMP - case IP_PROTO_ICMP: - g_netstats.icmp.drop++; - break; -#endif + /* Send the packet asynchrously on the forwarding device. */ - default: - break; + ret = ipv4_dev_forward(dev, fwddev, ipv4); + if (ret < 0) + { + nwarn("WARNING: ipv4_dev_forward failed: %d\n", ret); + return ret; + } } - g_netstats.ipv4.drop++; + return OK; } -#else -# define ipv4_dropstats(ipv4) #endif /**************************************************************************** @@ -458,24 +541,6 @@ int ipv4_forward(FAR struct net_driver_s *dev, FAR struct ipv4_hdr_s *ipv4) FAR struct net_driver_s *fwddev; int ret; - /* Decrement the TTL. If it decrements to zero, then drop the packet */ - - ret = ipv4_decr_ttl(ipv4); - if (ret < 1) - { - nwarn("WARNING: Hop limit exceeded... Dropping!\n"); - ret = -EMULTIHOP; - goto drop; - } - - /* Re-calculate the IPv4 checksum. This checksum is the Internet checksum - * of the 20 bytes of the IPv4 header. This checksum will be different - * because we just modify the IPv4 TTL. - */ - - ipv4->ipchksum = 0; - ipv4->ipchksum = ~ipv4_chksum(dev); - /* Search for a device that can forward this packet. This is a trivial * search if there is only a single network device (CONFIG_NETDEV_MULTINIC * not defined). But netdev_findby_ipv4addr() will still assure @@ -508,7 +573,7 @@ int ipv4_forward(FAR struct net_driver_s *dev, FAR struct ipv4_hdr_s *ipv4) ret = ipv4_dev_forward(dev, fwddev, ipv4); if (ret < 0) { - nwarn("WARNING: ipv4_dev_forward faield: %d\n", ret); + nwarn("WARNING: ipv4_dev_forward failed: %d\n", ret); goto drop; } } @@ -550,4 +615,48 @@ drop: return ret; } +/**************************************************************************** + * Name: ipv4_forward_broadcast + * + * Description: + * This function is called from ipv4_input when a broadcast or multicast + * packet is received. If CONFIG_NET_IPFORWARD_BROADCAST is enabled, this + * function will forward the broadcast packet to other networks through + * other network devices. + * + * Input Parameters: + * dev - The device on which the packet was received and which contains + * the IPv4 packet. + * ipv4 - A convenience pointer to the IPv4 header in within the IPv4 + * packet + * + * On input: + * - dev->d_buf holds the received packet. + * - dev->d_len holds the length of the received packet MINUS the + * size of the L1 header. That was subtracted out by ipv4_input. + * - ipv4 points to the IPv4 header with dev->d_buf. + * + * Returned Value: + * None + * + ****************************************************************************/ + +#if defined(CONFIG_NET_IPFORWARD_BROADCAST) && \ + defined(CONFIG_NETDEV_MULTINIC) +void ipv4_forward_broadcast(FAR struct net_driver_s *dev, + FAR struct ipv4_hdr_s *ipv4) +{ + /* Don't bother if the TTL would expire */ + + if (ipv4->ttl > 1) + { + /* Forward the the broadcast/multicast packet to all devices except, + * of course, the device that received the packet. + */ + + (void)netdev_foreach(ipv4_forward_callback, dev); + } +} +#endif + #endif /* CONFIG_NET_IPFORWARD && CONFIG_NET_IPv4 */ diff --git a/net/devif/ipv4_input.c b/net/devif/ipv4_input.c index c2f9fcb2ca..018f747dc0 100644 --- a/net/devif/ipv4_input.c +++ b/net/devif/ipv4_input.c @@ -99,6 +99,7 @@ #include "icmp/icmp.h" #include "igmp/igmp.h" +#include "devif/ip_forward.h" #include "devif/devif.h" /**************************************************************************** @@ -403,6 +404,11 @@ int ipv4_input(FAR struct net_driver_s *dev) net_ipv4addr_cmp(net_ip4addr_conv32(ipv4->destipaddr), INADDR_BROADCAST)) { +#ifdef CONFIG_NET_IPFORWARD_BROADCAST + /* Forward broadcast packets */ + + ipv4_forward_broadcast(dev, ipv4); +#endif return udp_ipv4_input(dev); } @@ -430,7 +436,15 @@ int ipv4_input(FAR struct net_driver_s *dev) #ifdef CONFIG_NET_IGMP in_addr_t destip = net_ip4addr_conv32(ipv4->destipaddr); - if (igmp_grpfind(dev, &destip) == NULL) + if (igmp_grpfind(dev, &destip) != NULL) + { +#ifdef CONFIG_NET_IPFORWARD_BROADCAST + /* Forward multicast packets */ + + ipv4_forward_broadcast(dev, ipv4); +#endif + } + else #endif { /* No.. The packet is not destined for us. */ diff --git a/net/devif/ipv6_forward.c b/net/devif/ipv6_forward.c index 222099e5a7..17fb197c93 100644 --- a/net/devif/ipv6_forward.c +++ b/net/devif/ipv6_forward.c @@ -186,6 +186,106 @@ static int ipv6_hdrsize(FAR struct ipv6_hdr_s *ipv6) } #endif +/**************************************************************************** + * Name: ipv6_decr_ttl + * + * Description: + * Decrement the IPv6 TTL (time to live value). TTL field is set by the + * sender of the packet and reduced by every router on the route to its + * destination. If the TTL field reaches zero before the datagram arrives + * at its destination, then the datagram is discarded and an ICMP error + * packet (11 - Time Exceeded) is sent back to the sender. + * + * The purpose of the TTL field is to avoid a situation in which an + * undeliverable datagram keeps circulating on an Internet system, and + * such a system eventually becoming swamped by such "immortals". + * + * Input Parameters: + * ipv6 - A pointer to the IPv6 header in within the IPv6 packet to be + * forwarded. + * + * Returned Value: + * The new TTL value is returned. A value <= 0 means the hop limit has + * expired. + * + ****************************************************************************/ + +static int ipv6_decr_ttl(FAR struct ipv6_hdr_s *ipv6) +{ + int ttl = (int)ipv6->ttl - 1; + + if (ttl <= 0) + { +#ifdef CONFIG_NET_ICMPv6 + /* Return an ICMPv6 error packet back to the sender. */ +#warning Missing logic +#endif + + /* Return zero which must cause the packet to be dropped */ + + return 0; + } + + /* Save the updated TTL value */ + + ipv6->ttl = ttl; + + /* NOTE: We do not have to recalculate the IPv6 checksum because (1) the + * IPv6 header does not include a checksum itself and (2) the TTL is not + * included in the sum for the TCP and UDP headers. + */ + + return ttl; +} + +/**************************************************************************** + * Name: ipv6_dropstats + * + * Description: + * Update statistics for a dropped packet. + * + * Input Parameters: + * ipv6 - A convenience pointer to the IPv6 header in within the IPv6 + * packet to be dropped. + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_NET_STATISTICS +static void ipv6_dropstats(FAR struct ipv6_hdr_s *ipv6) +{ + switch (ipv6->proto) + { +#ifdef CONFIG_NET_TCP + case IP_PROTO_TCP: + g_netstats.tcp.drop++; + break; +#endif + +#ifdef CONFIG_NET_UDP + case IP_PROTO_UDP: + g_netstats.udp.drop++; + break; +#endif + +#ifdef CONFIG_NET_ICMPv6 + case IP_PROTO_ICMP6: + g_netstats.icmpv6.drop++; + break; +#endif + + default: + break; + } + + g_netstats.ipv6.drop++; +} +#else +# define ipv6_dropstats(ipv6) +#endif + /**************************************************************************** * Name: ipv6_dev_forward * @@ -280,9 +380,22 @@ static int ipv6_dev_forward(FAR struct net_driver_s *dev, goto errout_with_fwd; } - memcpy(&fwd->f_hdr, ipv6, hdrsize); + memcpy(&fwd->f_hdr.ipv6, ipv6, hdrsize); fwd->f_hdrsize = hdrsize; + /* Decrement the TTL in the copy of the IPv6 header (retaining the + * original TTL in the source). If it decrements to zero, then drop + * the packet. + */ + + ret = ipv6_decr_ttl(&fwd->f_hdr.ipv6.l2); + if (ret < 1) + { + nwarn("WARNING: Hop limit exceeded... Dropping!\n"); + ret = -EMULTIHOP; + goto errout_with_fwd; + } + /* Use the L2 + L3 header size to determine start and size of the data * payload. * @@ -398,103 +511,59 @@ errout: #endif /* CONFIG_NETDEV_MULTINIC */ /**************************************************************************** - * Name: ipv6_decr_ttl + * Name: ipv6_forward_callback * * Description: - * Decrement the IPv6 TTL (time to live value). TTL field is set by the - * sender of the packet and reduced by every router on the route to its - * destination. If the TTL field reaches zero before the datagram arrives - * at its destination, then the datagram is discarded and an ICMP error - * packet (11 - Time Exceeded) is sent back to the sender. - * - * The purpose of the TTL field is to avoid a situation in which an - * undeliverable datagram keeps circulating on an Internet system, and - * such a system eventually becoming swamped by such "immortals". + * This function is a callback from netdev_foreach. It implements the + * the broadcase forwarding action for each network device (other than, of + * course, the device that received the packet). * * Input Parameters: - * ipv6 - A pointer to the IPv6 header in within the IPv6 packet to be - * forwarded. + * dev - The device on which the packet was received and which contains + * the IPv6 packet. + * ipv6 - A convenience pointer to the IPv6 header in within the IPv6 + * packet * * Returned Value: - * The new TTL value is returned. A value <= 0 means the hop limit has - * expired. + * Typically returns zero (meaning to continue the enumeration), but will + * return a non-zero to stop the enumeration if an error occurs. * ****************************************************************************/ -static int ipv6_decr_ttl(FAR struct ipv6_hdr_s *ipv6) +#if defined(CONFIG_NET_IPFORWARD_BROADCAST) && \ + defined(CONFIG_NETDEV_MULTINIC) +int ipv6_forward_callback(FAR struct net_driver_s *fwddev, FAR void *arg) { - int ttl = (int)ipv6->ttl - 1; + FAR struct net_driver_s *dev = (FAR struct net_driver_s *)arg; + FAR struct ipv6_hdr_s *ipv6; + int ret; - if (ttl <= 0) - { -#ifdef CONFIG_NET_ICMPv6 - /* Return an ICMPv6 error packet back to the sender. */ -#warning Missing logic -#endif + DEBUGASSERT(fwddev != NULL && dev != NULL && dev->d_buf != NULL); - /* Return zero which must cause the packet to be dropped */ - - return 0; - } - - /* Save the updated TTL value */ - - ipv6->ttl = ttl; - - /* NOTE: We do not have to recalculate the IPv6 checksum because (1) the - * IPv6 header does not include a checksum itself and (2) the TTL is not - * included in the sum for the TCP and UDP headers. + /* Check if we are forwarding on the same device that we received the + * packet from. */ - return ttl; -} - -/**************************************************************************** - * Name: ipv6_dropstats - * - * Description: - * Update statistics for a dropped packet. - * - * Input Parameters: - * ipv6 - A convenience pointer to the IPv6 header in within the IPv6 - * packet to be dropped. - * - * Returned Value: - * None - * - ****************************************************************************/ - -#ifdef CONFIG_NET_STATISTICS -static void ipv6_dropstats(FAR struct ipv6_hdr_s *ipv6) -{ - switch (ipv6->proto) + if (fwddev != dev) { -#ifdef CONFIG_NET_TCP - case IP_PROTO_TCP: - g_netstats.tcp.drop++; - break; -#endif + /* Recover the pointer to the IPv6 header in the receiving device's + * d_buf. + */ -#ifdef CONFIG_NET_UDP - case IP_PROTO_UDP: - g_netstats.udp.drop++; - break; -#endif + ipv6 = (FAR struct ipv6_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)]; -#ifdef CONFIG_NET_ICMPv6 - case IP_PROTO_ICMP6: - g_netstats.icmpv6.drop++; - break; -#endif + /* Send the packet asynchrously on the forwarding device. */ - default: - break; + ret = ipv6_dev_forward(dev, fwddev, ipv6); + if (ret < 0) + { + nwarn("WARNING: ipv6_dev_forward failed: %d\n", ret); + return ret; + } } - g_netstats.ipv6.drop++; + return OK; } -#else -# define ipv6_dropstats(ipv6) #endif /**************************************************************************** @@ -535,16 +604,6 @@ int ipv6_forward(FAR struct net_driver_s *dev, FAR struct ipv6_hdr_s *ipv6) FAR struct net_driver_s *fwddev; int ret; - /* Decrement the TTL. If it decrements to zero, then drop the packet */ - - ret = ipv6_decr_ttl(ipv6); - if (ret < 1) - { - nwarn("WARNING: Hop limit exceeded... Dropping!\n"); - ret = -EMULTIHOP; - goto drop; - } - /* Search for a device that can forward this packet. This is a trivial * search if there is only a single network device (CONFIG_NETDEV_MULTINIC * not defined). But netdev_findby_ipv6addr() will still assure @@ -574,7 +633,7 @@ int ipv6_forward(FAR struct net_driver_s *dev, FAR struct ipv6_hdr_s *ipv6) ret = ipv6_dev_forward(dev, fwddev, ipv6); if (ret < 0) { - nwarn("WARNING: ipv6_dev_forward faield: %d\n", ret); + nwarn("WARNING: ipv6_dev_forward failed: %d\n", ret); goto drop; } } @@ -641,4 +700,48 @@ drop: return ret; } +/**************************************************************************** + * Name: ipv6_forward_broadcast + * + * Description: + * This function is called from ipv6_input when a broadcast or multicast + * packet is received. If CONFIG_NET_IPFORWARD_BROADCAST is enabled, this + * function will forward the broadcast packet to other networks through + * other network devices. + * + * Input Parameters: + * dev - The device on which the packet was received and which contains + * the IPv6 packet. + * ipv6 - A convenience pointer to the IPv6 header in within the IPv6 + * packet + * + * On input: + * - dev->d_buf holds the received packet. + * - dev->d_len holds the length of the received packet MINUS the + * size of the L1 header. That was subtracted out by ipv6_input. + * - ipv6 points to the IPv6 header with dev->d_buf. + * + * Returned Value: + * None + * + ****************************************************************************/ + +#if defined(CONFIG_NET_IPFORWARD_BROADCAST) && \ + defined(CONFIG_NETDEV_MULTINIC) +void ipv6_forward_broadcast(FAR struct net_driver_s *dev, + FAR struct ipv6_hdr_s *ipv6) +{ + /* Don't bother if the TTL would expire */ + + if (ipv6->ttl > 1) + { + /* Forward the the broadcast/multicast packet to all devices except, + * of course, the device that received the packet. + */ + + (void)netdev_foreach(ipv6_forward_callback, dev); + } +} +#endif + #endif /* CONFIG_NET_IPFORWARD && CONFIG_NET_IPv6 */ diff --git a/net/devif/ipv6_input.c b/net/devif/ipv6_input.c index 4d656f3749..b74f104677 100644 --- a/net/devif/ipv6_input.c +++ b/net/devif/ipv6_input.c @@ -100,6 +100,7 @@ #include "icmpv6/icmpv6.h" #include "netdev/netdev.h" +#include "devif/ip_forward.h" #include "devif/devif.h" /**************************************************************************** @@ -183,6 +184,11 @@ static bool check_destipaddr(FAR struct net_driver_s *dev, if (ipv6->destipaddr[0] == HTONS(0xff02)) { +#ifdef CONFIG_NET_IPFORWARD_BROADCAST + /* Forward multicast packets */ + + ipv6_forward_broadcast(dev, ipv6); +#endif return true; } @@ -322,6 +328,11 @@ int ipv6_input(FAR struct net_driver_s *dev) if (ipv6->proto == IP_PROTO_UDP && net_ipv6addr_cmp(ipv6->destipaddr, g_ipv6_alloneaddr)) { +#ifdef CONFIG_NET_IPFORWARD_BROADCAST + /* Forward broadcast packets */ + + ipv6_forward_broadcast(dev, ipv6); +#endif return udp_ipv6_input(dev); }