From eba16af7ba9eb3b56c3352b9ca35099f5cd9e705 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Mon, 3 Jul 2017 16:22:57 -0600 Subject: [PATCH] Adds a little more IP foward framework --- net/devif/ipv6_forward.c | 280 ++++++++++++++++++++++++++++++++++++--- net/tcp/Make.defs | 6 + net/tcp/tcp.h | 28 ++++ net/tcp/tcp_forward.c | 101 ++++++++++++++ 4 files changed, 398 insertions(+), 17 deletions(-) create mode 100644 net/tcp/tcp_forward.c diff --git a/net/devif/ipv6_forward.c b/net/devif/ipv6_forward.c index b84891e4da..a8c69d24ba 100644 --- a/net/devif/ipv6_forward.c +++ b/net/devif/ipv6_forward.c @@ -42,16 +42,52 @@ #include #include +#include #include #include #include #include "netdev/netdev.h" #include "sixlowpan/sixlowpan.h" +#include "tcp/tcp.h" #include "devif/devif.h" #if defined(CONFIG_NET_IPFORWARD) && defined(CONFIG_NET_IPv6) +/**************************************************************************** + * Private Types + ****************************************************************************/ + +#if defined(CONFIG_NETDEV_MULTINIC) && \ + (defined(CONFIG_NET_UDP) || defined(CONFIG_NET_ICMPv6)) + +/* IPv6 + UDP or ICMPv6 header */ + +struct ipv6l3_hdr_s +{ + struct ipv6_hdr_s ipv6; + union + { +#ifdef CONFIG_NET_UDP + struct udp_hdr_s udp; +#endif +#ifdef CONFIG_NET_ICMPv6 + struct icmpv6_iphdr_s icmp; +#endif + } u; +}; + +/* This is the send state structure */ + +struct forward_s +{ + FAR struct net_driver_s *dev; /* Forwarding device */ + struct ipv6l3_hdr_s hdr; /* Copy of origin L2+L3 headers */ + FAR struct iob_queue_s iobq; /* IOBs contained the data payload */ +}; + +#endif /* CONFIG_NETDEV_MULTINIC && (CONFIG_NET_UDP || CONFIG_NET_ICMPv6) */ + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -118,6 +154,162 @@ static int ipv6_packet_conversion(FAR struct net_driver_s *dev, # define ipv6_packet_conversion(dev, ipv6) #endif /* CONFIG_NET_6LOWPAN */ +/**************************************************************************** + * Name: ipv6_hdrsize + * + * Description: + * Return the size of the IPv6 header and the following. + * + * Input Parameters: + * ipv6 - A pointer to the IPv6 header in within the IPv6 packet. This + * is immeidately followed by the L3 header which may be TCP, UDP, + * or ICMPv6. + * + * Returned Value: + * The size of the combined L2 + L3 headers is returned on success. An + * error is returned only if the prototype is not supported. + * + ****************************************************************************/ + +#ifdef CONFIG_NETDEV_MULTINIC +static int ipv6_hdrsize(FAR struct ipv6_hdr_s *ipv6) +{ + /* Size is determined by the following protocol header, */ + + switch (ipv6->proto) + { +#ifdef CONFIG_NET_TCP + case IP_PROTO_TCP: + { + FAR struct tcp_hdr_s *tcp = + (FAR struct tcp_hdr_s *)((FAR uintptr_t *)ipv6 + IPv6_HDRLEN); + unsigned int tcpsize; + + /* The TCP header length is encoded in the top 4 bits of the + * tcpoffset field (in units of 32-bit words). + */ + + tcpsize = ((uint16_t)tcp->tcpoffset >> 4) << 2; + return IPv6_HDRLEN + tcpsize; + } + break; +#endif + +#ifdef CONFIG_NET_UDP + case IP_PROTO_UDP: + return IPv6_HDRLEN + UDP_HDRLEN; + break; +#endif + +#ifdef CONFIG_NET_ICMPv6 + case IP_PROTO_ICMP6: + return IPv6_HDRLEN + ICMPv6_HDRLEN; + break; +#endif + + default: + nwarn("WARNING: Unrecognized proto: %u\n", ipv6->proto); + return -EPROTONOSUPPORT; + } +} +#endif + +/**************************************************************************** + * Name: ipv6_dev_forward + * + * Description: + * Set up to forward the UDP or ICMPv6 packet on the specified device. + * This function will set up a send "interrupt" handler that will perform + * the actual send asynchronously and must return without waiting for the + * send to complete. + * + * Input Parameters: + * dev - The device on which the packet should be forwarded. + * ipv6 - A pointer to the IPv6 header in within the IPv6 packet. This + * is immeidately followed by the L3 header which may be UDP or + * ICMPv6. + * iob - A list of IOBs containing the data payload to be sent. + * + * Returned Value: + * Zero is returned if the packet was successfully forwarded; A negated + * errno value is returned if the packet is not forwardable. In that + * latter case, the caller should free the IOB list and drop the packet. + * + ****************************************************************************/ + +#if defined(CONFIG_NETDEV_MULTINIC) && \ + (defined(CONFIG_NET_UDP) || defined(CONFIG_NET_ICMPv6)) +static int ipv6_dev_forward(FAR struct net_driver_s *dev, + FAR struct ipv6_hdr_s *ipv6, + FAR struct iob_s *iob) +{ + /* Notify the forwarding device that TX data is available */ + + /* Set up to send the packet when the selected device polls for TX data. */ + +#warning Missing logic + + /* REVISIT: For Ethernet we may have to fix up the Ethernet header: + * - source MAC, the MAC of the current device. + * - dest MAC, the MAC associated with the destination IPv6 adress. + * This will involve ICMPv6 and Neighbor Discovery. + */ + + nwarn("WARNING: UPD/ICMPv6 packet forwarding not yet supported\n"); + return -ENOSYS; +} +#else +# define ipv6_dev_forward(dev,ipv6,iob) -EPROTONOSUPPORT +#endif + +/**************************************************************************** + * Name: ipv6_dropstats + * + * Description: + * Update statistics for a droped 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 + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -187,35 +379,89 @@ int ipv6_forward(FAR struct net_driver_s *dev, FAR struct ipv6_hdr_s *ipv6) ret = ipv6_packet_conversion(dev, fwddev, ipv6); if (ret < 0) { - /* Extract the IPv6 + L3 header; Move the data payload to an IOB - * chain. + FAR struct iob_s *iob = NULL; + FAR uint8_t *payload; + unsigned int paysize; + int hdrsize; + + /* Get the size of the IPv6 + L3 header. Use this to determine + * start of the data payload. + * + * Remember that the size of the L1 header has already been + * subtracted from dev->d_len. */ - /* Notify the forwarding device that TX data is available */ + hdrsize = ipv6_hdrsize(ipv6); + if (hdrsize < 0) + { + goto drop; + } - /* Set up to send the packet when the selected device polls for TX - * data. - */ + payload = (FAR uint8_t *)ipv6 + hdrsize; + paysize = dev->d_len - hdrsize; - /* REVISIT: For Ethernet we may have to fix up the Ethernet header: - * - source MAC, the MAC of the current device. - * - dest MAC, the MAC associated with the destination IPv6 adress. - * This will involve ICMPv6 and Neighbor Discovery. - */ + if (paysize > 0) + { + /* Try to allocate the head of an IOB chain. If this fails, + * the the packet will be dropped; we are not operating in a + * context where waiting for an IOB is a good idea + */ - /* Return success with dev->d_len = 0 */ + iob = iob_tryalloc(false); + if (iob == NULL) + { + ret = -ENOMEM; + goto errout_with_iob; + } -# warning Missing logic - nwarn("WARNING: Packet forwarding not yet supported " - "across different devices\n"); + /* Copy the packet data payload into an IOB chain. + * iob_trycopin() will not wait, but will fail there are no + * available IOBs. + */ + + ret = iob_trycopyin(iob, payload, paysize, 0, false); + if (ret < 0) + { + goto errout_with_iob; + } + } + + /* Then set up to forward the packet */ + +#ifdef CONFIG_NET_TCP + if (ipv6->proto == IP_PROTO_TCP) + { + /* Forward a TCP packet, handling ACKs, windowing, etc. */ + + ret = tcp_ipv6_forward(fwddev, ipv6, iob); + } + else +#endif + { + /* Forward a UDP or ICMPv6 packet */ + + ret = ipv6_dev_forward(fwddev, ipv6, iob); + } + + if (ret >= 0) + { + dev->d_len = 0; + return OK; + } + +errout_with_iob: + iob_free_chain(iob); + +drop: + ipv6_dropstats(ipv6); + dev->d_len = 0; return -ENOSYS; } } else #endif /* CONFIG_NETDEV_MULTINIC */ -#if defined(CONFIG_NET_6LOWPAN) /* REVISIT: Currently only suport for - * 6LoWPAN */ +#if defined(CONFIG_NET_6LOWPAN) /* REVISIT: Currently only suport for 6LoWPAN */ { /* Single network device */ diff --git a/net/tcp/Make.defs b/net/tcp/Make.defs index 29b6167a65..f9e7c89388 100644 --- a/net/tcp/Make.defs +++ b/net/tcp/Make.defs @@ -60,6 +60,12 @@ NET_CSRCS += tcp_conn.c tcp_seqno.c tcp_devpoll.c tcp_finddev.c tcp_timer.c NET_CSRCS += tcp_send.c tcp_input.c tcp_appsend.c tcp_listen.c NET_CSRCS += tcp_callback.c tcp_backlog.c tcp_ipselect.c +# IP forwarding + +ifeq ($(CONFIG_NET_IPFORWARD),y) +NET_CSRCS += tcp_forward.c +endif + # TCP write buffering ifeq ($(CONFIG_NET_TCP_WRITE_BUFFERS),y) diff --git a/net/tcp/tcp.h b/net/tcp/tcp.h index 83891b1024..a56068b147 100644 --- a/net/tcp/tcp.h +++ b/net/tcp/tcp.h @@ -794,6 +794,34 @@ int tcp_accept_connection(FAR struct net_driver_s *dev, void tcp_send(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn, uint16_t flags, uint16_t len); +/**************************************************************************** + * Name: tcp_ipv6_forward + * + * Description: + * Set up to forward the TCP packet on the specified device. This + * function will set up a send "interrupt" handler that will perform + * the actual send asynchronously and must return without waiting for the + * send to complete. + * + * Input Parameters: + * dev - The device on which the packet should be forwarded. + * ipv6 - A pointer to the IPv6 header in within the IPv6 packet. This + * is immeidately followed by the TCP header. + * iob - A list of IOBs containing the data payload to be sent. + * + * Returned Value: + * Zero is returned if the packet was successfully forwarded; A negated + * errno value is returned if the packet is not forwardable. In that + * latter case, the caller should free the IOB list and drop the packet. + * + ****************************************************************************/ + +#if defined(CONFIG_NET_IPFORWARD) && defined(CONFIG_NET_IPv6) && \ + defined(CONFIG_NETDEV_MULTINIC) +int tcp_ipv6_forward(FAR struct net_driver_s *dev, + FAR struct ipv6_hdr_s *ipv6, FAR struct iob_s *iob); +#endif + /**************************************************************************** * Name: tcp_reset * diff --git a/net/tcp/tcp_forward.c b/net/tcp/tcp_forward.c new file mode 100644 index 0000000000..1197224229 --- /dev/null +++ b/net/tcp/tcp_forward.c @@ -0,0 +1,101 @@ +/**************************************************************************** + * net/tcp/tcp_forward.c + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include +#include +#include + +#include "tcp/tcp.h" + +#if defined(CONFIG_NET) && defined(CONFIG_NET_IPFORWARD) && defined(CONFIG_NET_TCP) + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: tcp_ipv6_forward + * + * Description: + * Set up to forward the TCP packet on the specified device. This + * function will set up a send "interrupt" handler that will perform + * the actual send asynchronously and must return without waiting for the + * send to complete. + * + * Input Parameters: + * dev - The device on which the packet should be forwarded. + * ipv6 - A pointer to the IPv6 header in within the IPv6 packet. This + * is immeidately followed by the TCP header. + * iob - A list of IOBs containing the data payload to be sent. + * + * Returned Value: + * Zero is returned if the packet was successfully forwarded; A negated + * errno value is returned if the packet is not forwardable. In that + * latter case, the caller should free the IOB list and drop the packet. + * + ****************************************************************************/ + +#if defined(CONFIG_NET_IPv6) && defined(CONFIG_NETDEV_MULTINIC) +int tcp_ipv6_forward(FAR struct net_driver_s *dev, + FAR struct ipv6_hdr_s *ipv6, FAR struct iob_s *iob) +{ + /* Notify the forwarding device that TX data is available */ + + /* Set up to send the packet when the selected device polls for TX data. + * TCP packets must obey ACK and windowing rules. + */ + +#warning Missing logic + + /* REVISIT: For Ethernet we may have to fix up the Ethernet header: + * - source MAC, the MAC of the current device. + * - dest MAC, the MAC associated with the destination IPv6 adress. + * This will involve ICMPv6 and Neighbor Discovery. + * - Because of TCP window, the packet may have to be sent in smaller + * pieces. + */ + + nwarn("WARNING: TCP packet forwarding not yet supported\n"); + return -ENOSYS; +} +#endif /* CONFIG_NET_IPv6 && CONFIG_NETDEV_MULTINIC */ +#endif /* CONFIG_NET && CONFIG_NET_IPFORWARD && CONFIG_NET_TCP */