IP forwading: Add optional support to forward broadcast and multicast packets.

This commit is contained in:
Gregory Nutt 2017-07-06 07:26:01 -06:00
parent 24e9647156
commit 04716a65a5
7 changed files with 563 additions and 248 deletions

View File

@ -293,10 +293,18 @@ config NET_IPFORWARD
that are not supported by this platform will be forwarded to the that are not supported by this platform will be forwarded to the
appropriate network device. Routing table support may be required. 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 config NET_IPFORWARD_NSTRUCT
int "Number of pre-allocated forwarding structures" int "Number of pre-allocated forwarding structures"
default 4 default 4
depends on NET_IPFORWARD && CONFIG_NETDEV_MULTINIC depends on NET_IPFORWARD && NETDEV_MULTINIC
---help--- ---help---
When packets are forwarded from on device to another, a structure When packets are forwarded from on device to another, a structure
must be allocated to hold the state of forwarding across several must be allocated to hold the state of forwarding across several

View File

@ -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 devif_dev_event(FAR struct net_driver_s *dev, void *pvconn,
uint16_t flags); 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. * Send data on the current connection.
* *

View File

@ -55,7 +55,13 @@
#include "icmpv6/icmpv6.h" #include "icmpv6/icmpv6.h"
#undef HAVE_FWDALLOC #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 * Pre-processor Definitions
@ -205,6 +211,68 @@ FAR struct forward_s *ip_forward_alloc(void);
void ip_forward_free(FAR struct forward_s *fwd); 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 * Name: devif_forward
* *
@ -225,5 +293,67 @@ void ip_forward_free(FAR struct forward_s *fwd);
void devif_forward(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 */ #endif /* __NET_DEVIF_IP_FORWARD_H */

View File

@ -49,6 +49,7 @@
#include <nuttx/net/netstats.h> #include <nuttx/net/netstats.h>
#include "netdev/netdev.h" #include "netdev/netdev.h"
#include "utils/utils.h"
#include "sixlowpan/sixlowpan.h" #include "sixlowpan/sixlowpan.h"
#include "udp/udp.h" #include "udp/udp.h"
#include "tcp/tcp.h" #include "tcp/tcp.h"
@ -122,6 +123,119 @@ static int ipv4_hdrsize(FAR struct ipv4_hdr_s *ipv4)
} }
#endif #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 * Name: ipv4_dev_forward
* *
@ -210,9 +324,22 @@ static int ipv4_dev_forward(FAR struct net_driver_s *dev,
goto errout_with_fwd; goto errout_with_fwd;
} }
memcpy(&fwd->f_hdr, ipv4, hdrsize); memcpy(&fwd->f_hdr.ipv4, ipv4, hdrsize);
fwd->f_hdrsize = 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 /* Use the L2 + L3 header size to determine start and size of the data
* payload. * payload.
* *
@ -317,103 +444,59 @@ errout:
#endif /* CONFIG_NETDEV_MULTINIC */ #endif /* CONFIG_NETDEV_MULTINIC */
/**************************************************************************** /****************************************************************************
* Name: ipv4_decr_ttl * Name: ipv4_forward_callback
* *
* Description: * Description:
* Decrement the IPv4 TTL (time to live value). TTL field is set by the * This function is a callback from netdev_foreach. It implements the
* sender of the packet and reduced by every router on the route to its * the broadcase forwarding action for each network device (other than, of
* destination. If the TTL field reaches zero before the datagram arrives * course, the device that received the packet).
* 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: * Input Parameters:
* ipv4 - A pointer to the IPv4 header in within the IPv4 packet to be * dev - The device on which the packet was received and which contains
* forwarded. * the IPv4 packet.
* ipv4 - A convenience pointer to the IPv4 header in within the IPv4
* packet
* *
* Returned Value: * Returned Value:
* The new TTL value is returned. A value <= 0 means the hop limit has * Typically returns zero (meaning to continue the enumeration), but will
* expired. * 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) DEBUGASSERT(fwddev != NULL && dev != NULL && dev->d_buf != NULL);
{
#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 */ /* Check if we are forwarding on the same device that we received the
* packet from.
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.
*/ */
return ttl; if (fwddev != dev)
}
/****************************************************************************
* 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 /* Recover the pointer to the IPv4 header in the receiving device's
case IP_PROTO_TCP: * d_buf.
g_netstats.tcp.drop++; */
break;
#endif
#ifdef CONFIG_NET_UDP ipv4 = (FAR struct ipv4_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)];
case IP_PROTO_UDP:
g_netstats.udp.drop++;
break;
#endif
#ifdef CONFIG_NET_ICMP /* Send the packet asynchrously on the forwarding device. */
case IP_PROTO_ICMP:
g_netstats.icmp.drop++;
break;
#endif
default: ret = ipv4_dev_forward(dev, fwddev, ipv4);
break; 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 #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; FAR struct net_driver_s *fwddev;
int ret; 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 for a device that can forward this packet. This is a trivial
* search if there is only a single network device (CONFIG_NETDEV_MULTINIC * search if there is only a single network device (CONFIG_NETDEV_MULTINIC
* not defined). But netdev_findby_ipv4addr() will still assure * 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); ret = ipv4_dev_forward(dev, fwddev, ipv4);
if (ret < 0) if (ret < 0)
{ {
nwarn("WARNING: ipv4_dev_forward faield: %d\n", ret); nwarn("WARNING: ipv4_dev_forward failed: %d\n", ret);
goto drop; goto drop;
} }
} }
@ -550,4 +615,48 @@ drop:
return ret; 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 */ #endif /* CONFIG_NET_IPFORWARD && CONFIG_NET_IPv4 */

View File

@ -99,6 +99,7 @@
#include "icmp/icmp.h" #include "icmp/icmp.h"
#include "igmp/igmp.h" #include "igmp/igmp.h"
#include "devif/ip_forward.h"
#include "devif/devif.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), net_ipv4addr_cmp(net_ip4addr_conv32(ipv4->destipaddr),
INADDR_BROADCAST)) INADDR_BROADCAST))
{ {
#ifdef CONFIG_NET_IPFORWARD_BROADCAST
/* Forward broadcast packets */
ipv4_forward_broadcast(dev, ipv4);
#endif
return udp_ipv4_input(dev); return udp_ipv4_input(dev);
} }
@ -430,7 +436,15 @@ int ipv4_input(FAR struct net_driver_s *dev)
#ifdef CONFIG_NET_IGMP #ifdef CONFIG_NET_IGMP
in_addr_t destip = net_ip4addr_conv32(ipv4->destipaddr); 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 #endif
{ {
/* No.. The packet is not destined for us. */ /* No.. The packet is not destined for us. */

View File

@ -186,6 +186,106 @@ static int ipv6_hdrsize(FAR struct ipv6_hdr_s *ipv6)
} }
#endif #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 * Name: ipv6_dev_forward
* *
@ -280,9 +380,22 @@ static int ipv6_dev_forward(FAR struct net_driver_s *dev,
goto errout_with_fwd; goto errout_with_fwd;
} }
memcpy(&fwd->f_hdr, ipv6, hdrsize); memcpy(&fwd->f_hdr.ipv6, ipv6, hdrsize);
fwd->f_hdrsize = 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 /* Use the L2 + L3 header size to determine start and size of the data
* payload. * payload.
* *
@ -398,103 +511,59 @@ errout:
#endif /* CONFIG_NETDEV_MULTINIC */ #endif /* CONFIG_NETDEV_MULTINIC */
/**************************************************************************** /****************************************************************************
* Name: ipv6_decr_ttl * Name: ipv6_forward_callback
* *
* Description: * Description:
* Decrement the IPv6 TTL (time to live value). TTL field is set by the * This function is a callback from netdev_foreach. It implements the
* sender of the packet and reduced by every router on the route to its * the broadcase forwarding action for each network device (other than, of
* destination. If the TTL field reaches zero before the datagram arrives * course, the device that received the packet).
* 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: * Input Parameters:
* ipv6 - A pointer to the IPv6 header in within the IPv6 packet to be * dev - The device on which the packet was received and which contains
* forwarded. * the IPv6 packet.
* ipv6 - A convenience pointer to the IPv6 header in within the IPv6
* packet
* *
* Returned Value: * Returned Value:
* The new TTL value is returned. A value <= 0 means the hop limit has * Typically returns zero (meaning to continue the enumeration), but will
* expired. * 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) DEBUGASSERT(fwddev != NULL && dev != NULL && dev->d_buf != NULL);
{
#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 */ /* Check if we are forwarding on the same device that we received the
* packet from.
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; if (fwddev != dev)
}
/****************************************************************************
* 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 /* Recover the pointer to the IPv6 header in the receiving device's
case IP_PROTO_TCP: * d_buf.
g_netstats.tcp.drop++; */
break;
#endif
#ifdef CONFIG_NET_UDP ipv6 = (FAR struct ipv6_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)];
case IP_PROTO_UDP:
g_netstats.udp.drop++;
break;
#endif
#ifdef CONFIG_NET_ICMPv6 /* Send the packet asynchrously on the forwarding device. */
case IP_PROTO_ICMP6:
g_netstats.icmpv6.drop++;
break;
#endif
default: ret = ipv6_dev_forward(dev, fwddev, ipv6);
break; 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 #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; FAR struct net_driver_s *fwddev;
int ret; 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 for a device that can forward this packet. This is a trivial
* search if there is only a single network device (CONFIG_NETDEV_MULTINIC * search if there is only a single network device (CONFIG_NETDEV_MULTINIC
* not defined). But netdev_findby_ipv6addr() will still assure * 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); ret = ipv6_dev_forward(dev, fwddev, ipv6);
if (ret < 0) if (ret < 0)
{ {
nwarn("WARNING: ipv6_dev_forward faield: %d\n", ret); nwarn("WARNING: ipv6_dev_forward failed: %d\n", ret);
goto drop; goto drop;
} }
} }
@ -641,4 +700,48 @@ drop:
return ret; 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 */ #endif /* CONFIG_NET_IPFORWARD && CONFIG_NET_IPv6 */

View File

@ -100,6 +100,7 @@
#include "icmpv6/icmpv6.h" #include "icmpv6/icmpv6.h"
#include "netdev/netdev.h" #include "netdev/netdev.h"
#include "devif/ip_forward.h"
#include "devif/devif.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)) if (ipv6->destipaddr[0] == HTONS(0xff02))
{ {
#ifdef CONFIG_NET_IPFORWARD_BROADCAST
/* Forward multicast packets */
ipv6_forward_broadcast(dev, ipv6);
#endif
return true; return true;
} }
@ -322,6 +328,11 @@ int ipv6_input(FAR struct net_driver_s *dev)
if (ipv6->proto == IP_PROTO_UDP && if (ipv6->proto == IP_PROTO_UDP &&
net_ipv6addr_cmp(ipv6->destipaddr, g_ipv6_alloneaddr)) 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); return udp_ipv6_input(dev);
} }