diff --git a/include/nuttx/net/icmp.h b/include/nuttx/net/icmp.h index 9665753590f..b2523ee7400 100644 --- a/include/nuttx/net/icmp.h +++ b/include/nuttx/net/icmp.h @@ -114,6 +114,11 @@ #define ICMP_PREC_CUTOFF 15 /* Precedence cut off */ #define NR_ICMP_UNREACH 15 /* instead of hardcoding immediate value */ +/* Codes for TIME_EXCEEDED. */ + +#define ICMP_EXC_TTL 0 /* TTL count exceeded */ +#define ICMP_EXC_FRAGTIME 1 /* Fragment Reassembly time exceeded */ + /**************************************************************************** * Public Type Definitions ****************************************************************************/ diff --git a/include/nuttx/net/icmpv6.h b/include/nuttx/net/icmpv6.h index 23a6b8a3c12..89266cf2e72 100644 --- a/include/nuttx/net/icmpv6.h +++ b/include/nuttx/net/icmpv6.h @@ -141,6 +141,11 @@ #define ICMPv6_POLICY_FAIL 5 #define ICMPv6_REJECT_ROUTE 6 +/* Codes for Time Exceeded */ + +#define ICMPV6_EXC_HOPLIMIT 0 +#define ICMPV6_EXC_FRAGTIME 1 + /**************************************************************************** * Public Type Definitions ****************************************************************************/ diff --git a/include/nuttx/net/ip.h b/include/nuttx/net/ip.h index 864a7292129..1eb599e02fe 100644 --- a/include/nuttx/net/ip.h +++ b/include/nuttx/net/ip.h @@ -217,6 +217,8 @@ struct ipv6_stats_s net_stats_t sent; /* Number of sent packets at the IP layer */ net_stats_t vhlerr; /* Number of packets dropped due to wrong * IP version or header length */ + net_stats_t fragerr; /* Number of packets dropped since they + * were IP fragments */ net_stats_t protoerr; /* Number of packets dropped since they * were neither ICMP, UDP nor TCP */ }; diff --git a/include/nuttx/net/ipv6ext.h b/include/nuttx/net/ipv6ext.h index 39550c3c1e3..110d757b752 100644 --- a/include/nuttx/net/ipv6ext.h +++ b/include/nuttx/net/ipv6ext.h @@ -124,6 +124,14 @@ #define EXTHDR_LEN(hdrlen) ((hdrlen + 1) << 3) +/* Fragment header has no length field and has a fixed size */ + +#define EXTHDR_FRAG_LEN 8 + +/* More frags flag bits in 16-bit flags in fragment header */ + +#define FRAGHDR_FRAG_MOREFRAGS 0x0001 + /* Values of the Two High-Order Bits in the Hop-to-hop Option Type Field */ #define HOPBYHOP_TYPE_MASK 0xc0 @@ -217,4 +225,24 @@ struct ipv6_router_alert_s uint8_t pad[4]; /* Pad to a multiple of 8 bytes */ }; +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: ipv6_exthdr + * + * Description: + * Check whether it is an IPv6 extension header. + * + * Input Parameters: + * The next header value extracted from an IPv6 frame. + * + * Returned Value: + * Return true if the next header value is an IPv6 extension header. + * + ****************************************************************************/ + +bool ipv6_exthdr(uint8_t nxthdr); + #endif /* __INCLUDE_NUTTX_NET_IPV6EXT_H */ diff --git a/include/nuttx/net/netdev.h b/include/nuttx/net/netdev.h index 8f78e519efe..10f55c463f8 100644 --- a/include/nuttx/net/netdev.h +++ b/include/nuttx/net/netdev.h @@ -320,6 +320,12 @@ struct net_driver_s FAR struct iob_s *d_iob; + /* Remember the outgoing fragments waiting to be sent */ + +#ifdef CONFIG_NET_IPFRAG + FAR struct iob_queue_s d_fragout; +#endif + /* The d_buf array is used to hold incoming and outgoing packets. The * device driver should place incoming data into this buffer. When sending * data, the device driver should read the link level headers and the diff --git a/net/Kconfig b/net/Kconfig index 53134fd9803..63944dbb285 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -331,6 +331,7 @@ source "net/sixlowpan/Kconfig" source "net/ipforward/Kconfig" source "net/nat/Kconfig" source "net/netfilter/Kconfig" +source "net/ipfrag/Kconfig" endmenu # Internet Protocol Selection diff --git a/net/Makefile b/net/Makefile index 7d81a4db4a3..0a8463bd7e5 100644 --- a/net/Makefile +++ b/net/Makefile @@ -28,6 +28,7 @@ NET_CSRCS = net_initialize.c include socket/Make.defs include inet/Make.defs +include ipfrag/Make.defs include netdev/Make.defs include arp/Make.defs include icmp/Make.defs diff --git a/net/README.txt b/net/README.txt index d8158171bfa..bb352d15419 100644 --- a/net/README.txt +++ b/net/README.txt @@ -11,6 +11,7 @@ Directory Structure +- arp - Address resolution protocol (IPv4) +- bluetooth - PF_BLUETOOTH socket interface +- devif - Stack/device interface layer + +- ipfrag - Fragmentation and reassembly +- icmp - Internet Control Message Protocol (IPv4) +- icmpv6 - Internet Control Message Protocol (IPv6) +- ieee802154 - PF_IEEE802154 socket interface diff --git a/net/devif/devif.h b/net/devif/devif.h index 2378b76ee1a..9c4b89656e9 100644 --- a/net/devif/devif.h +++ b/net/devif/devif.h @@ -511,6 +511,21 @@ void devif_out(FAR struct net_driver_s *dev); int devif_poll_out(FAR struct net_driver_s *dev, devif_poll_callback_t callback); +/**************************************************************************** + * Name: devif_is_loopback + * + * Description: + * The function checks the destination address of the packet to see + * whether the target of packet is ourself. + * + * Returned Value: + * true is returned if the packet need loop back to ourself, otherwise + * false is returned. + * + ****************************************************************************/ + +bool devif_is_loopback(FAR struct net_driver_s *dev); + /**************************************************************************** * Name: devif_loopback * diff --git a/net/devif/devif_iobsend.c b/net/devif/devif_iobsend.c index 2663915f607..ee7eed6878b 100644 --- a/net/devif/devif_iobsend.c +++ b/net/devif/devif_iobsend.c @@ -56,19 +56,29 @@ void devif_iob_send(FAR struct net_driver_s *dev, FAR struct iob_s *iob, unsigned int len, unsigned int offset, unsigned int target_offset) { +#ifndef CONFIG_NET_IPFRAG unsigned int limit = NETDEV_PKTSIZE(dev) - NET_LL_HDRLEN(dev) - target_offset; +#endif int ret; +#ifndef CONFIG_NET_IPFRAG if (dev == NULL || len == 0 || len > limit) +#else + if (dev == NULL || len == 0) +#endif { if (dev->d_iob == NULL) { iob_free_chain(iob); } +#ifndef CONFIG_NET_IPFRAG nerr("devif_iob_send error, %p, send len: %u, limit len: %u\n", dev, len, limit); +#else + nerr("devif_iob_send error, %p, send len: %u\n", dev, len); +#endif return; } diff --git a/net/devif/devif_loopback.c b/net/devif/devif_loopback.c index a290780b4ed..a7379aa4e53 100644 --- a/net/devif/devif_loopback.c +++ b/net/devif/devif_loopback.c @@ -32,10 +32,23 @@ #include /**************************************************************************** - * Private Functions + * Public Functions ****************************************************************************/ -static bool is_loopback(FAR struct net_driver_s *dev) +/**************************************************************************** + * Name: devif_is_loopback + * + * Description: + * The function checks the destination address of the packet to see + * whether the target of packet is ourself. + * + * Returned Value: + * true is returned if the packet need loop back to ourself, otherwise + * false is returned. + * + ****************************************************************************/ + +bool devif_is_loopback(FAR struct net_driver_s *dev) { if (dev->d_len > 0) { @@ -76,7 +89,7 @@ static bool is_loopback(FAR struct net_driver_s *dev) int devif_loopback(FAR struct net_driver_s *dev) { - if (!is_loopback(dev)) + if (!devif_is_loopback(dev)) { return 0; } diff --git a/net/devif/devif_poll.c b/net/devif/devif_poll.c index 2fdf462acb8..176657b9733 100644 --- a/net/devif/devif_poll.c +++ b/net/devif/devif_poll.c @@ -33,6 +33,7 @@ #include #include "devif/devif.h" +#include "netdev/netdev.h" #include "arp/arp.h" #include "can/can.h" #include "tcp/tcp.h" @@ -46,6 +47,7 @@ #include "mld/mld.h" #include "ipforward/ipforward.h" #include "sixlowpan/sixlowpan.h" +#include "ipfrag/ipfrag.h" /**************************************************************************** * Private Types @@ -616,6 +618,79 @@ static inline int devif_poll_tcp_connections(FAR struct net_driver_s *dev, # define devif_poll_tcp_connections(dev, callback) (0) #endif +/**************************************************************************** + * Name: devif_poll_ipfrag + * + * Description: + * Poll all ip fragments for available packets to send. + * + * Input Parameters: + * dev - NIC Device instance. + * callback - the actual sending API provided by each NIC driver. + * + * Returned Value: + * Zero indicated the polling will continue, else stop the polling. + * + * Assumptions: + * This function is called from the MAC device driver with the network + * locked. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_IPFRAG +static int devif_poll_ipfrag(FAR struct net_driver_s *dev, + devif_poll_callback_t callback) +{ + FAR struct iob_s *frag; + bool reused = false; + int bstop = false; + + while (!bstop) + { + /* Dequeue outgoing fragment from dev->d_fragout */ + + frag = iob_remove_queue(&dev->d_fragout); + if (frag == NULL) + { + break; + } + + /* Frag buffer could be reused for other protocols */ + + reused = true; + + /* Replace original iob */ + + netdev_iob_replace(dev, frag); + + /* build L2 headers */ + + devif_out(dev); + + /* Call back into the driver */ + + bstop = callback(dev); + } + + /* Notify the device driver that ip fragments is available. */ + + if (iob_peek_queue(&dev->d_fragout) != NULL) + { + netdev_txnotify_dev(dev); + } + + /* Reuse iob buffer */ + + if (!bstop && reused) + { + iob_update_pktlen(dev->d_iob, 0); + netdev_iob_prepare(dev, true, 0); + } + + return bstop; +} +#endif + /**************************************************************************** * Name: devif_poll_connections * @@ -654,10 +729,19 @@ static int devif_poll_connections(FAR struct net_driver_s *dev, * action. */ -#ifdef CONFIG_NET_ARP_SEND - /* Check for pending ARP requests */ +#ifdef CONFIG_NET_IPFRAG + /* Traverse all of ip fragments for available packets to transfer */ + + bstop = devif_poll_ipfrag(dev, callback); + if (!bstop) +#endif +#ifdef CONFIG_NET_ARP_SEND + { + /* Check for pending ARP requests */ + + bstop = arp_poll(dev, callback); + } - bstop = arp_poll(dev, callback); if (!bstop) #endif #ifdef CONFIG_NET_PKT @@ -1048,6 +1132,18 @@ int devif_poll_out(FAR struct net_driver_s *dev, if (callback) { +#ifdef CONFIG_NET_IPFRAG + if (ip_fragout(dev) != OK) + { + netdev_iob_release(dev); + return 1; + } + else if (iob_peek_queue(&dev->d_fragout) != NULL) + { + return devif_poll_ipfrag(dev, callback); + } +#endif + return callback(dev); } diff --git a/net/devif/ipv4_input.c b/net/devif/ipv4_input.c index aaf854290b8..18f96e0dce4 100644 --- a/net/devif/ipv4_input.c +++ b/net/devif/ipv4_input.c @@ -105,6 +105,7 @@ #include "ipforward/ipforward.h" #include "devif/devif.h" #include "nat/nat.h" +#include "ipfrag/ipfrag.h" #include "utils/utils.h" /**************************************************************************** @@ -219,6 +220,13 @@ static int ipv4_in(FAR struct net_driver_s *dev) if ((ipv4->ipoffset[0] & 0x3f) != 0 || ipv4->ipoffset[1] != 0) { +#ifdef CONFIG_NET_IPFRAG + if (ipv4_fragin(dev) == OK) + { + return OK; + } + +#endif #ifdef CONFIG_NET_STATISTICS g_netstats.ipv4.drop++; g_netstats.ipv4.fragerr++; @@ -434,6 +442,11 @@ static int ipv4_in(FAR struct net_driver_s *dev) (defined(CONFIG_NET_BROADCAST) && defined(NET_UDP_HAVE_STACK)) done: #endif + +#ifdef CONFIG_NET_IPFRAG + ip_fragout(dev); +#endif + devif_out(dev); /* Return and let the caller do any pending transmission. */ diff --git a/net/devif/ipv6_input.c b/net/devif/ipv6_input.c index 7b7dfa40672..7783eda841c 100644 --- a/net/devif/ipv6_input.c +++ b/net/devif/ipv6_input.c @@ -51,6 +51,7 @@ #include "ipforward/ipforward.h" #include "inet/inet.h" #include "devif/devif.h" +#include "ipfrag/ipfrag.h" /**************************************************************************** * Pre-processor Definitions @@ -62,37 +63,6 @@ * Private Functions ****************************************************************************/ -/**************************************************************************** - * Name: ipv6_exthdr - * - * Description: - * Return true if the next header value is an IPv6 extension header. - * - ****************************************************************************/ - -static bool ipv6_exthdr(uint8_t nxthdr) -{ - switch (nxthdr) - { - case NEXT_HOPBYBOT_EH: /* Hop-by-Hop Options Header */ - case NEXT_ENCAP_EH: /* Encapsulated IPv6 Header */ - case NEXT_ROUTING_EH: /* Routing Header */ - case NEXT_FRAGMENT_EH: /* Fragment Header */ - case NEXT_RRSVP_EH: /* Resource ReSerVation Protocol */ - case NEXT_ENCAPSEC_EH: /* Encapsulating Security Payload */ - case NEXT_AUTH_EH: /* Authentication Header */ - case NEXT_DESTOPT_EH: /* Destination Options Header */ - case NEXT_MOBILITY_EH: /* Mobility */ - case NEXT_HOSTID_EH: /* Host Identity Protocol */ - case NEXT_SHIM6_EH: /* Shim6 Protocol */ - return true; - - case NEXT_NOHEADER: /* No next header */ - default: - return false; - } -} - /**************************************************************************** * Name: check_dev_destipaddr * @@ -228,6 +198,9 @@ static int ipv6_in(FAR struct net_driver_s *dev) #ifdef CONFIG_NET_IPFORWARD int ret; #endif +#ifdef CONFIG_NET_IPFRAG + bool isfrag = false; +#endif /* This is where the input processing starts. */ @@ -312,7 +285,18 @@ static int ipv6_in(FAR struct net_driver_s *dev) /* Just skip over the extension header */ exthdr = (FAR struct ipv6_extension_s *)payload; - extlen = EXTHDR_LEN((unsigned int)exthdr->len); + if (nxthdr == NEXT_FRAGMENT_EH) + { + extlen = EXTHDR_FRAG_LEN; +#ifdef CONFIG_NET_IPFRAG + isfrag = true; +#endif + } + else + { + extlen = EXTHDR_LEN((unsigned int)exthdr->len); + } + payload += extlen; iphdrlen += extlen; nxthdr = exthdr->nxthdr; @@ -418,6 +402,23 @@ static int ipv6_in(FAR struct net_driver_s *dev) } #endif +#ifdef CONFIG_NET_IPFRAG + if (isfrag) + { + if (ipv6_fragin(dev) == OK) + { + return OK; + } + else + { +#ifdef CONFIG_NET_STATISTICS + g_netstats.ipv6.fragerr++; +#endif + goto drop; + } + } +#endif + /* Now process the incoming packet according to the protocol specified in * the next header IPv6 field. */ @@ -519,6 +520,11 @@ static int ipv6_in(FAR struct net_driver_s *dev) #ifdef CONFIG_NET_IPFORWARD done: #endif + +#ifdef CONFIG_NET_IPFRAG + ip_fragout(dev); +#endif + devif_out(dev); /* Return and let the caller do any pending transmission. */ @@ -541,6 +547,43 @@ drop: * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: ipv6_exthdr + * + * Description: + * Check whether it is an IPv6 extension header. + * + * Input Parameters: + * The next header value extracted from an IPv6 frame. + * + * Returned Value: + * Return true if the next header value is an IPv6 extension header. + * + ****************************************************************************/ + +bool ipv6_exthdr(uint8_t nxthdr) +{ + switch (nxthdr) + { + case NEXT_HOPBYBOT_EH: /* Hop-by-Hop Options Header */ + case NEXT_ENCAP_EH: /* Encapsulated IPv6 Header */ + case NEXT_ROUTING_EH: /* Routing Header */ + case NEXT_FRAGMENT_EH: /* Fragment Header */ + case NEXT_RRSVP_EH: /* Resource ReSerVation Protocol */ + case NEXT_ENCAPSEC_EH: /* Encapsulating Security Payload */ + case NEXT_AUTH_EH: /* Authentication Header */ + case NEXT_DESTOPT_EH: /* Destination Options Header */ + case NEXT_MOBILITY_EH: /* Mobility */ + case NEXT_HOSTID_EH: /* Host Identity Protocol */ + case NEXT_SHIM6_EH: /* Shim6 Protocol */ + return true; + + case NEXT_NOHEADER: /* No next header */ + default: + return false; + } +} + /**************************************************************************** * Name: ipv6_input * diff --git a/net/icmp/icmp_sendmsg.c b/net/icmp/icmp_sendmsg.c index 1d90e016651..d6db44b95ce 100644 --- a/net/icmp/icmp_sendmsg.c +++ b/net/icmp/icmp_sendmsg.c @@ -321,6 +321,7 @@ ssize_t icmp_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg, goto errout; } +#ifndef CONFIG_NET_IPFRAG /* Sanity check if the request len is greater than the net payload len */ if (len > NETDEV_PKTSIZE(dev) - (NET_LL_HDRLEN(dev) + IPv4_HDRLEN)) @@ -328,6 +329,7 @@ ssize_t icmp_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg, nerr("ERROR: Invalid packet length\n"); return -EINVAL; } +#endif /* If we are no longer processing the same ping ID, then flush any pending * packets from the read-ahead buffer. diff --git a/net/icmpv6/icmpv6_sendmsg.c b/net/icmpv6/icmpv6_sendmsg.c index 5af54d73177..1fdc6d2ebb5 100644 --- a/net/icmpv6/icmpv6_sendmsg.c +++ b/net/icmpv6/icmpv6_sendmsg.c @@ -308,6 +308,7 @@ ssize_t icmpv6_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg, goto errout; } +#ifndef CONFIG_NET_IPFRAG /* Sanity check if the request len is greater than the net payload len */ if (len > NETDEV_PKTSIZE(dev) - (NET_LL_HDRLEN(dev) + IPv6_HDRLEN)) @@ -315,6 +316,7 @@ ssize_t icmpv6_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg, nerr("ERROR: Invalid packet length\n"); return -EINVAL; } +#endif /* If we are no longer processing the same ping ID, then flush any pending * packets from the read-ahead buffer. diff --git a/net/ipforward/ipv4_forward.c b/net/ipforward/ipv4_forward.c index 306785a80b7..1bc3b9c3223 100644 --- a/net/ipforward/ipv4_forward.c +++ b/net/ipforward/ipv4_forward.c @@ -214,11 +214,15 @@ static int ipv4_dev_forward(FAR struct net_driver_s *dev, #endif int ret; - /* Verify that the full packet will fit within the forwarding devices MTU. - * We provide no support for fragmenting forwarded packets. + /* Verify that the full packet will fit within the forwarding device's MTU + * if DF is set. */ - if (NET_LL_HDRLEN(fwddev) + dev->d_len > NETDEV_PKTSIZE(fwddev)) + if (NET_LL_HDRLEN(fwddev) + dev->d_len > NETDEV_PKTSIZE(fwddev) +#ifdef CONFIG_NET_IPFRAG + && (ipv4->ipoffset[0] & (IP_FLAG_DONTFRAG >> 8)) +#endif + ) { nwarn("WARNING: Packet > MTU... Dropping\n"); ret = -EFBIG; @@ -490,7 +494,7 @@ drop: case -EMULTIHOP: icmp_reply_type = ICMP_TIME_EXCEEDED; - icmp_reply_code = 0; + icmp_reply_code = ICMP_EXC_TTL; goto reply; default: diff --git a/net/ipforward/ipv6_forward.c b/net/ipforward/ipv6_forward.c index 95ae9fd08c6..527f476ce6e 100644 --- a/net/ipforward/ipv6_forward.c +++ b/net/ipforward/ipv6_forward.c @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -106,6 +107,12 @@ static int ipv6_hdrsize(FAR struct ipv6_hdr_s *ipv6) break; #endif +#ifdef CONFIG_NET_IPFRAG + case NEXT_FRAGMENT_EH: + return IPv6_HDRLEN + EXTHDR_FRAG_LEN; + break; +#endif + default: nwarn("WARNING: Unrecognized proto: %u\n", ipv6->proto); return -EPROTONOSUPPORT; @@ -636,7 +643,8 @@ drop: return OK; case -EMULTIHOP: - icmpv6_reply(dev, ICMPv6_PACKET_TIME_EXCEEDED, 0, 0); + icmpv6_reply(dev, ICMPv6_PACKET_TIME_EXCEEDED, ICMPV6_EXC_HOPLIMIT, + 0); return OK; default: diff --git a/net/ipfrag/Kconfig b/net/ipfrag/Kconfig new file mode 100644 index 00000000000..f444b072bce --- /dev/null +++ b/net/ipfrag/Kconfig @@ -0,0 +1,23 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config NET_IPFRAG + bool "IP fragmentation support" + default n + depends on (NET_IPv4 || NET_IPv6) + ---help--- + Enable support IP packet fragmentation and IP packet reassembly of + fragmented IP packets. + +if NET_IPFRAG + +config NET_IPFRAG_REASS_MAXAGE + int "IP fragmentation timeout" + default 20 + ---help--- + The maximum time an IP fragment should wait in the reassembly buffer + before it is dropped. Units are deci-seconds. Default: 2 seconds. + +endif # NET_IPFRAG diff --git a/net/ipfrag/Make.defs b/net/ipfrag/Make.defs new file mode 100755 index 00000000000..8c7bf9549a5 --- /dev/null +++ b/net/ipfrag/Make.defs @@ -0,0 +1,40 @@ +############################################################################ +# net/ipfrag/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifeq ($(CONFIG_NET_IPFRAG),y) + +# fragment processing source files + +NET_CSRCS += ipfrag.c + +ifeq ($(CONFIG_NET_IPv4),y) +NET_CSRCS += ipv4_frag.c +endif + +ifeq ($(CONFIG_NET_IPv6),y) +NET_CSRCS += ipv6_frag.c +endif + +# Include fragment build support + +DEPPATH += --dep-path ipfrag +VPATH += :ipfrag + +endif # CONFIG_NET_IPFRAG diff --git a/net/ipfrag/ipfrag.c b/net/ipfrag/ipfrag.c new file mode 100755 index 00000000000..f78d836d0a3 --- /dev/null +++ b/net/ipfrag/ipfrag.c @@ -0,0 +1,1283 @@ +/**************************************************************************** + * net/ipfrag/ipfrag.c + * Handling incoming IPv4 and IPv6 fragment input + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) && \ + defined(CONFIG_NET_IPFRAG) + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "netdev/netdev.h" +#include "inet/inet.h" +#include "icmp/icmp.h" +#include "icmpv6/icmpv6.h" +#include "ipfrag.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define GOTO_IF(expression, to) \ +if (expression) \ + { \ + goto to; \ + } + +#define UPDATE_IOB(iob, off, len) \ +do \ + { \ + (iob)->io_offset = (off); \ + (iob)->io_len = (len); \ + (iob)->io_pktlen = (len); \ + } while (0); + +/* Defined the minimal timeout interval to avoid triggering timeout timer + * too frequently, default: 0.5 seconds. + */ + +#define REASSEMBLY_TIMEOUT_MINIMAL 5 + +#if CONFIG_NET_IPFRAG_REASS_MAXAGE < REASSEMBLY_TIMEOUT_MINIMAL +# define REASSEMBLY_TIMEOUT REASSEMBLY_TIMEOUT_MINIMAL +#else +# define REASSEMBLY_TIMEOUT CONFIG_NET_IPFRAG_REASS_MAXAGE +#endif + +#define REASSEMBLY_TIMEOUT_MINIMALTICKS DSEC2TICK(REASSEMBLY_TIMEOUT_MINIMAL) +#define REASSEMBLY_TIMEOUT_TICKS DSEC2TICK(REASSEMBLY_TIMEOUT) + +#define IPFRAGWORK LPWORK + +/* Helper macro to count I/O buffer count for a given I/O buffer chain */ + +#define IOBUF_CNT(ptr) (((ptr)->io_pktlen + CONFIG_IOB_BUFSIZE - 1)/ \ + CONFIG_IOB_BUFSIZE) + +/* The maximum I/O buffer occupied by fragment reassembly cache */ + +#define REASSEMBLY_MAXOCCUPYIOB CONFIG_IOB_NBUFFERS / 5 + +/* Deciding whether to fragment outgoing packets which target is to ourself */ + +#define LOOPBACK_IPFRAME_NOFRAGMENT 0 + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* A timeout timer used to start a worker which is used to check + * whether the assembly time of those fragments within one node is expired, + * if so, free all resources of this node. + */ + +static struct wdog_s g_wdfragtimeout; + +/* Reassembly timeout work */ + +static struct work_s g_wkfragtimeout; + +/* Remember the number of I/O buffers currently in reassembly cache */ + +static uint8_t g_bufoccupy; + +/* Queue header definition, it links all fragments of all NICs by ascending + * ipid. + */ + +static sq_queue_t g_assemblyhead_ipid; + +/* Queue header definition, which connects all fragments of all NICs in order + * of addition time. + */ + +static sq_queue_t g_assemblyhead_time; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/* Only one thread can access g_assemblyhead_ipid and g_assemblyhead_time + * at a time. + */ + +mutex_t g_ipfrag_lock = NXMUTEX_INITIALIZER; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static void ip_fragin_timerout_expiry(wdparm_t arg); +static void ip_fragin_timerwork(FAR void *arg); +static inline FAR struct ip_fraglink_s * +ip_fragin_freelink(FAR struct ip_fraglink_s *fraglink); +static void ip_fragin_check(FAR struct ip_fragsnode_s *fragsnode); +static void ip_fragin_cachemonitor(FAR struct ip_fragsnode_s *curnode); +static inline FAR struct iob_s * +ip_fragout_allocfragbuf(FAR struct iob_queue_s *fragq); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ip_fragin_timerout_expiry + * + * Description: + * Schedule the timeout checking and handling on the low priority work + * queue. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void ip_fragin_timerout_expiry(wdparm_t arg) +{ + assert(g_wkfragtimeout.worker == NULL); + work_queue(IPFRAGWORK, &g_wkfragtimeout, ip_fragin_timerwork, NULL, 0); +} + +/**************************************************************************** + * Name: ip_fragin_timerwork + * + * Description: + * The really work of fragment timeout checking and handling. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void ip_fragin_timerwork(FAR void *arg) +{ + clock_t curtick = clock_systime_ticks(); + sclock_t interval; + FAR sq_entry_t *entry; + FAR sq_entry_t *entrynext; + FAR struct ip_fragsnode_s *node; + + ninfo("Start reassembly work queue\n"); + + nxmutex_lock(&g_ipfrag_lock); + + /* Walk through the list, check the timetout and calculate the next timer + * interval + */ + + entry = sq_peek(&g_assemblyhead_time); + while (entry != NULL) + { + entrynext = sq_next(entry); + + node = (FAR struct ip_fragsnode_s *) + container_of(entry, FAR struct ip_fragsnode_s, flinkat); + + /* Check for timeout, be careful with the calculation formula, + * the tick counter may overflow + */ + + interval = curtick - node->tick; + + if (interval >= REASSEMBLY_TIMEOUT_TICKS) + { + /* If this timeout expires, the partially-reassembled datagram + * MUST be discarded and an ICMP Time Exceeded message sent to + * the source host (if fragment zero has been received). + */ + + ninfo("Reassembly timeout occurs!"); +#if defined(CONFIG_NET_ICMP) && !defined(CONFIG_NET_ICMP_NO_STACK) + if ((node->verifyflag & IP_FRAGVERIFY_RECVDZEROFRAG) != 0) + { + FAR struct net_driver_s *dev = node->dev; + + net_lock(); + + netdev_iob_replace(dev, node->frags->frag); + node->frags->frag = NULL; + +#ifdef CONFIG_NET_IPv4 + if (node->frags->isipv4) + { + icmp_reply(dev, ICMP_TIME_EXCEEDED, + ICMP_EXC_FRAGTIME); + } +#endif + +#ifdef CONFIG_NET_IPv6 + if (!node->frags->isipv4) + { + icmpv6_reply(dev, ICMPv6_PACKET_TIME_EXCEEDED, + ICMPV6_EXC_FRAGTIME, 0); + } +#endif + + if (iob_tryadd_queue(dev->d_iob, &dev->d_fragout) == 0) + { + netdev_iob_clear(dev); + + /* Send ICMP Time Exceeded message via dev->d_fragout + * queue + */ + + ninfo("Send Time Exceeded ICMP%s Message to source " + "host\n", node->frags->isipv4 ? "v4" : "v6"); + netdev_txnotify_dev(dev); + } + + net_unlock(); + } +#endif + + /* Remove fragments of this node */ + + if (node->frags != NULL) + { + FAR struct ip_fraglink_s *fraglink = node->frags; + + while (fraglink) + { + fraglink = ip_fragin_freelink(fraglink); + } + } + + /* Remove node from single-list and free node memory */ + + ip_frag_remnode(node); + kmm_free(node); + } + else + { + /* Because fragment nodes have been sorted to g_assemblyhead_time + * according to the added time, so enter here, we can get the + * 'interval' of the earliest time node that has not timed out. + * There is no need to continue the loop here, and use time + * REASSEMBLY_TIMEOUT_TICKS - 'interval' as the input for the next + * Timer starting. + */ + + break; + } + + entry = entrynext; + } + + /* Be sure to start the timer, if there are nodes in the linked list */ + + if (sq_peek(&g_assemblyhead_time) != NULL) + { + clock_t delay = REASSEMBLY_TIMEOUT_MINIMALTICKS; + + /* The interval for the next timer is REASSEMBLY_TIMEOUT_TICKS - + * interval, if it is less than the minimum timeout interval, + * fix it to REASSEMBLY_TIMEOUT_MINIMALTICKS + */ + + if (delay < REASSEMBLY_TIMEOUT_TICKS - interval) + { + delay = REASSEMBLY_TIMEOUT_TICKS - interval; + } + + ninfo("Reschedule reassembly work queue\n"); + wd_start(&g_wdfragtimeout, delay, ip_fragin_timerout_expiry, + (wdparm_t)NULL); + } + else + { + ninfo("Stop reassembly work queue\n"); + } + + nxmutex_unlock(&g_ipfrag_lock); +} + +/**************************************************************************** + * Name: ip_fragin_freelink + * + * Description: + * Free the I/O buffer and ip_fraglink_s buffer at the head of a + * ip_fraglink_s chain. + * + * Input Parameters: + * fraglink - node of the lower-level linked list, it maintains + * information of one fragment + * + * Returned Value: + * The link to the next ip_fraglink_s buffer in the chain. + * + ****************************************************************************/ + +static inline FAR struct ip_fraglink_s * +ip_fragin_freelink(FAR struct ip_fraglink_s *fraglink) +{ + FAR struct ip_fraglink_s *next = fraglink->flink; + + if (fraglink->frag != NULL) + { + iob_free_chain(fraglink->frag); + } + + kmm_free(fraglink); + + return next; +} + +/**************************************************************************** + * Name: ip_fragin_check + * + * Description: + * Check whether the zero fragment has been received or all fragments have + * been received. + * + * Input Parameters: + * fragsnode - node of the upper-level linked list, it maintains + * information bout all fragments belonging to an IP datagram + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void ip_fragin_check(FAR struct ip_fragsnode_s *fragsnode) +{ + uint16_t formerlen = 0; + FAR struct ip_fraglink_s *entry; + + if (fragsnode->verifyflag & IP_FRAGVERIFY_RECVDTAILFRAG) + { + entry = fragsnode->frags; + while (entry != NULL) + { + if (entry->morefrags) + { + formerlen += entry->fraglen; + } + else + { + /* Only the last entry has a 0 morefrags flag */ + + if (entry->fragoff == formerlen) + { + fragsnode->verifyflag |= IP_FRAGVERIFY_RECVDALLFRAGS; + } + } + + entry = entry->flink; + } + } +} + +/**************************************************************************** + * Name: ip_fragin_cachemonitor + * + * Description: + * Check the reassembly cache buffer size, if it exceeds the configured + * threshold, some I/O buffers need to be freed + * + * Input Parameters: + * curnode - node of the upper-level linked list, it maintains information + * about all fragments belonging to an IP datagram + * + * Returned Value: + * none + * + ****************************************************************************/ + +static void ip_fragin_cachemonitor(FAR struct ip_fragsnode_s *curnode) +{ + uint32_t cleancnt = 0; + uint32_t bufcnt; + FAR sq_entry_t *entry; + FAR sq_entry_t *entrynext; + FAR struct ip_fragsnode_s *node; + + /* Start cache cleaning if g_bufoccupy exceeds the cache threshold */ + + if (g_bufoccupy > REASSEMBLY_MAXOCCUPYIOB) + { + cleancnt = g_bufoccupy - REASSEMBLY_MAXOCCUPYIOB; + entry = sq_peek(&g_assemblyhead_time); + + while (entry != NULL && cleancnt > 0) + { + entrynext = sq_next(entry); + + node = (FAR struct ip_fragsnode_s *) + container_of(entry, FAR struct ip_fragsnode_s, flinkat); + + /* Skip specified node */ + + if (node != curnode) + { + /* Remove fragments of this node */ + + if (node->frags != NULL) + { + FAR struct ip_fraglink_s *fraglink = node->frags; + + while (fraglink != NULL) + { + fraglink = ip_fragin_freelink(fraglink); + } + } + + /* Remove node from single-list and free node memory */ + + bufcnt = ip_frag_remnode(node); + kmm_free(node); + + cleancnt = cleancnt > bufcnt ? cleancnt - bufcnt : 0; + } + + entry = entrynext; + } + } +} + +/**************************************************************************** + * Name: ip_fragout_allocfragbuf + * + * Description: + * Prepare one I/O buffer and enqueue it to a specified queue + * + * Input Parameters: + * fragq - the queue head + * + * Returned Value: + * The pointer to I/O buffer + * + ****************************************************************************/ + +static inline FAR struct iob_s * +ip_fragout_allocfragbuf(FAR struct iob_queue_s *fragq) +{ + FAR struct iob_s *iob; + + iob = iob_tryalloc(false); + if (iob != NULL) + { + if (iob_tryadd_queue(iob, fragq) < 0) + { + iob_free(iob); + iob = NULL; + } + } + + return iob; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ip_frag_remnode + * + * Description: + * free ip_fragsnode_s + * + * Input Parameters: + * node - node of the upper-level linked list, it maintains + * information about all fragments belonging to an IP datagram + * + * Returned Value: + * I/O buffer count of this node + * + ****************************************************************************/ + +uint32_t ip_frag_remnode(FAR struct ip_fragsnode_s *node) +{ + g_bufoccupy -= node->bufcnt; + assert(g_bufoccupy < CONFIG_IOB_NBUFFERS); + + sq_rem((FAR sq_entry_t *)node, &g_assemblyhead_ipid); + sq_rem((FAR sq_entry_t *)&node->flinkat, &g_assemblyhead_time); + + return node->bufcnt; +} + +/**************************************************************************** + * Name: ip_fragin_enqueue + * + * Description: + * Enqueue one fragment. + * All fragments belonging to one IP frame are organized in a linked list + * form, that is a ip_fragsnode_s node. All ip_fragsnode_s nodes are also + * organized in an upper-level linked list. + * + * Input Parameters: + * dev - NIC Device instance + * curfraglink - node of the lower-level linked list, it maintains + * information of one fragment + * + * Returned Value: + * Whether queue is empty before enqueue the new node + * + ****************************************************************************/ + +bool ip_fragin_enqueue(FAR struct net_driver_s *dev, + FAR struct ip_fraglink_s *curfraglink) +{ + FAR struct ip_fragsnode_s *node; + FAR sq_entry_t *entry; + FAR sq_entry_t *entrylast = NULL; + bool empty; + + /* The linked list is ordered by IP ID value, walk through it and try to + * find a node that has the same IP ID value, otherwise need to create a + * new node and insert it into the linked list. + */ + + entry = sq_peek(&g_assemblyhead_ipid); + empty = (entry == NULL) ? true : false; + + while (entry != NULL) + { + node = (struct ip_fragsnode_s *)entry; + + if (dev == node->dev && curfraglink->ipid <= node->ipid) + { + break; + } + + entrylast = entry; + entry = sq_next(entry); + } + + node = (FAR struct ip_fragsnode_s *)entry; + + if (node != NULL && curfraglink->ipid == node->ipid) + { + FAR struct ip_fraglink_s *fraglink; + FAR struct ip_fraglink_s *lastlink = NULL; + + /* Found a previously created ip_fragsnode_s, insert this new + * ip_fraglink_s to the subchain of this node. + */ + + fraglink = node->frags; + + /* An ip_fragsnode_s must have an ip_fraglink_s because we allocate a + * new ip_fraglink_s when caching a new ip_fraglink_s with a new IP ID + */ + + while (fraglink != NULL) + { + /* The fragment list is ordered by fragment offset value */ + + if (curfraglink->fragoff <= fraglink->fragoff) + { + break; + } + + lastlink = fraglink; + fraglink = fraglink->flink; + } + + if (fraglink == NULL) + { + /* This fragment offset is greater than the previous fragments, + * added to the last position + */ + + lastlink->flink = curfraglink; + + /* Remember I/O buffer count */ + + node->bufcnt += IOBUF_CNT(curfraglink->frag); + g_bufoccupy += IOBUF_CNT(curfraglink->frag); + } + else if (curfraglink->fragoff == fraglink->fragoff) + { + /* Fragments with same offset value contain the same data, use the + * more recently arrived copy. Refer to RFC791, Section3.2, Page29. + * Replace and removed the old packet from the fragment list + */ + + curfraglink->flink = fraglink->flink; + if (lastlink == NULL) + { + node->frags = curfraglink; + } + else + { + lastlink->flink = curfraglink; + } + + iob_free_chain(fraglink->frag); + kmm_free(fraglink); + } + else + { + /* Insert into the fragment list */ + + if (lastlink == NULL) + { + /* Insert before the first node */ + + curfraglink->flink = node->frags; + node->frags = curfraglink; + } + else + { + /* Insert this node after lastlink */ + + curfraglink->flink = lastlink->flink; + lastlink->flink = curfraglink; + } + + /* Remember I/O buffer count */ + + node->bufcnt += IOBUF_CNT(curfraglink->frag); + g_bufoccupy += IOBUF_CNT(curfraglink->frag); + } + } + else + { + /* It's a new IP ID fragment, malloc a new node and insert it into the + * linked list + */ + + node = kmm_malloc(sizeof(struct ip_fragsnode_s)); + if (node == NULL) + { + nerr("ERROR: Failed to allocate buffer.\n"); + return -ENOMEM; + } + + node->flink = NULL; + node->flinkat = NULL; + node->dev = dev; + node->ipid = curfraglink->ipid; + node->frags = curfraglink; + node->tick = clock_systime_ticks(); + node->bufcnt = IOBUF_CNT(curfraglink->frag); + g_bufoccupy += IOBUF_CNT(curfraglink->frag); + node->verifyflag = 0; + node->outgoframe = NULL; + + /* Insert this new node into linked list identified by + * g_assemblyhead_ipid with correct position + */ + + if (sq_peek(&g_assemblyhead_ipid) == NULL || entrylast == NULL) + { + sq_addfirst((FAR sq_entry_t *)node, &g_assemblyhead_ipid); + } + else + { + sq_addafter(entrylast, (FAR sq_entry_t *)node, + &g_assemblyhead_ipid); + } + + /* Add this new node to the tail of linked list identified by + * g_assemblyhead_time + */ + + sq_addlast((FAR sq_entry_t *)&node->flinkat, &g_assemblyhead_time); + } + + if (curfraglink->fragoff == 0) + { + /* Have received the zero fragment */ + + node->verifyflag |= IP_FRAGVERIFY_RECVDZEROFRAG; + } + else if (!curfraglink->morefrags) + { + /* Have received the tail fragment */ + + node->verifyflag |= IP_FRAGVERIFY_RECVDTAILFRAG; + } + + /* For indexing convenience */ + + curfraglink->fragsnode = node; + + /* Check receiving status */ + + ip_fragin_check(node); + + /* Buffer is take away, clear original pointers in NIC */ + + netdev_iob_clear(dev); + + /* Perform cache cleaning when reassembly cache size exceeds the configured + * threshold + */ + + ip_fragin_cachemonitor(node); + + return empty; +} + +/**************************************************************************** + * Name: ip_fragout_slice + * + * Description: + * According to the MTU of a given NIC, split the original data into + * multiple data pieces, and the space for filling the L3 header is + * reserved at the forefront of each piece. Each piece is stored in + * independent I/O buffer(s) and eventually forms an I/O buffer queue. + * Note: + * 1.About the 'piece' above + * 1).If MTU < CONFIG_IOB_BUFSIZE, a piece consists of an I/O buffer; + * 2).If MTU >= CONFIG_IOB_BUFSIZE, a piece consists of multiple I/O + * buffers. + * 2.This function split and gathers the incoming data into outgoing + * I/O buffers according to the MTU, but is not responsible for + * building the L3 header related to the fragmentation. + * + * Input Parameters: + * iob - The data comes from + * domain - PF_INET or PF_INET6 + * mtu - MTU of current NIC + * unfraglen - The starting position to fragmentation processing + * fragq - Those output slices + * + * Returned Value: + * Number of fragments + * + * Assumptions: + * Data length(iob->io_pktlen) is grater than the MTU of current NIC + * + ****************************************************************************/ + +int32_t ip_fragout_slice(FAR struct iob_s *iob, uint8_t domain, uint16_t mtu, + uint16_t unfraglen, FAR struct iob_queue_s *fragq) +{ + FAR uint8_t *leftstart; + uint16_t leftlen = 0; + uint16_t ncopy; + uint16_t navail; + uint32_t nfrags = 0; + bool expand = false; + FAR struct iob_s *orig = NULL; + FAR struct iob_s *reorg = NULL; + FAR struct iob_s *head = NULL; + + if (iob == NULL || fragq == NULL) + { + nerr("ERROR: Invalid parameters! iob: %p, fragq: %p\n", iob, fragq); + return 0; + } + + assert(iob->io_pktlen > mtu); + +#ifdef CONFIG_NET_IPv4 + if (domain == PF_INET) + { + uint16_t nreside; + + /* Fragmentation requires that the data length after the IP header + * must be a multiple of 8 + */ + + mtu = ((mtu - IPv4_HDRLEN) & ~0x7) + IPv4_HDRLEN; + + /* Remember the number of resident bytes */ + + nreside = mtu; + + /* For IPv4, fragmented frames and non-fragmented frames have the + * same length L3 header. So process it as follows: + * the zero fragment use the original I/O buffer and reorganize + * the non-zero fragments (copy to new I/O buffers), space for the + * L3 IP header must be reserved for all fragments + */ + + head = iob; + while (iob != NULL && nreside > iob->io_len) + { + nreside -= iob->io_len; + iob = iob->io_flink; + } + + leftstart = iob->io_data + iob->io_offset + nreside; + leftlen = iob->io_len - nreside; + + orig = iob->io_flink; + if (orig != NULL) + { + orig->io_pktlen = head->io_pktlen - (mtu + leftlen); + iob->io_flink = NULL; + } + + head->io_pktlen = mtu; + iob->io_len = nreside; + + if (iob_tryadd_queue(head, fragq) < 0) + { + goto allocfail; + } + + head = NULL; + nfrags++; + + if (leftlen == 0 && orig != NULL) + { + reorg = ip_fragout_allocfragbuf(fragq); + GOTO_IF(reorg == NULL, allocfail); + + nfrags++; + + /* This is a new fragment buffer, reserve L2&L3 header space + * in the front of this buffer + */ + + UPDATE_IOB(reorg, CONFIG_NET_LL_GUARDSIZE, unfraglen); + } + else + { + /* If the MTU is relatively small, the remaining data of the first + * I/O buffer may need to be fragmented multiple times. + * For IPv4, the first I/O Buffer is reused, which have reserved + * the L4 header space, the following fragmentation flow is only + * for non-zero fragments, so following flow does not need to + * consider the L4 header + */ + + while (leftlen > 0) + { + reorg = ip_fragout_allocfragbuf(fragq); + GOTO_IF(reorg == NULL, allocfail); + + nfrags++; + + if (leftlen + unfraglen > mtu) + { + ncopy = mtu - unfraglen; + } + else + { + ncopy = leftlen; + } + + /* This is a new fragment buffer, reserve L2&L3 header space + * in the front of this buffer + */ + + UPDATE_IOB(reorg, CONFIG_NET_LL_GUARDSIZE, unfraglen); + + /* Then copy L4 data */ + + GOTO_IF(iob_trycopyin(reorg, leftstart, ncopy, + reorg->io_pktlen, false) < 0, allocfail); + + leftstart += ncopy; + leftlen -= ncopy; + } + } + } +#ifdef CONFIG_NET_IPv6 + else +#endif +#endif +#ifdef CONFIG_NET_IPv6 + if (domain == PF_INET6) + { + unfraglen += EXTHDR_FRAG_LEN; + + /* Fragmentation requires the length field to be a multiples of 8, + * and the length of the IPv6 basic header and all extended headers + * is a multiples of 8, so here directly fix the MTU to 8-byte + * alignment. + */ + + mtu = mtu & ~0x7; + + /* For IPv6 fragment, a fragment header needs to be inserted before + * the l4 header, so all data must be reorganized, a space for IPv6 + * header and fragment external header is reserved before l4 header + * for all fragments + */ + + reorg = ip_fragout_allocfragbuf(fragq); + GOTO_IF(reorg == NULL, allocfail); + + nfrags++; + + /* Reserve L3 header space */ + + UPDATE_IOB(reorg, CONFIG_NET_LL_GUARDSIZE, unfraglen); + + /* Copy L3 header(include unfragmentable extention header if present) + * from original I/O buffer + */ + + orig = iob; + memcpy(reorg->io_data + reorg->io_offset, + orig->io_data + orig->io_offset, unfraglen - EXTHDR_FRAG_LEN); + iob_trimhead(orig, unfraglen - EXTHDR_FRAG_LEN); + } +#endif + + /* Copy data from original I/O buffer chain 'orig' to new reorganized + * I/O buffer chain 'reorg' + */ + + while (orig != NULL) + { + leftstart = orig->io_data + orig->io_offset; + leftlen = orig->io_len; + + /* For each I/O buffer data of the 'orig' chain */ + + while (leftlen > 0) + { + /* Calculate target area size */ + + navail = mtu - reorg->io_pktlen; + + if (navail > 0) + { + if (leftlen > navail) + { + /* Target area is too small, need expand the destination + * chain + */ + + expand = true; + ncopy = navail; + } + else + { + ncopy = leftlen; + } + + if (iob_trycopyin(reorg, leftstart, ncopy, + reorg->io_pktlen, false) < 0) + { + goto allocfail; + } + + leftlen -= ncopy; + leftstart += ncopy; + } + else + { + expand = true; + } + + if (expand) + { + reorg = ip_fragout_allocfragbuf(fragq); + GOTO_IF(reorg == NULL, allocfail); + + nfrags++; + + /* This is a new fragment buffer, reserve L2&L3 header space + * in the front of this buffer + */ + + UPDATE_IOB(reorg, CONFIG_NET_LL_GUARDSIZE, unfraglen); + + expand = false; + } + } + + orig = iob_free(orig); + } + + return nfrags; + +allocfail: + nerr("ERROR: Fragout fail! No I/O buffer available!"); + iob_free_chain(head); + iob_free_chain(orig); + iob_free_chain(reorg); + iob_free_queue(fragq); + + return 0; +} + +/**************************************************************************** + * Name: ip_frag_startwdog + * + * Description: + * Start the reassembly timeout timer + * + * Returned Value: + * None + * + ****************************************************************************/ + +void ip_frag_startwdog(void) +{ + if (g_wdfragtimeout.func == NULL) + { + wd_start(&g_wdfragtimeout, REASSEMBLY_TIMEOUT_TICKS, + ip_fragin_timerout_expiry, (wdparm_t)NULL); + } +} + +/**************************************************************************** + * Name: ip_frag_uninit + * + * Description: + * Uninitialize the fragment processing module. + * + * Returned Value: + * None + * + ****************************************************************************/ + +int32_t ip_frag_uninit(void) +{ + FAR struct net_driver_s *dev; + + ninfo("Uninitialize frag proccessing module\n"); + + /* Stop work queue */ + + if (!work_available(&g_wkfragtimeout)) + { + ninfo("Cancel reassembly work queue\n"); + work_cancel(IPFRAGWORK, &g_wkfragtimeout); + } + + /* Release frag processing resources of each NIC */ + + net_lock(); + for (dev = g_netdevices; dev; dev = dev->flink) + { + /* Is the interface in the "up" state? */ + + if ((dev->d_flags & IFF_UP) != 0) + { + ip_frag_stop(dev); + } + } + + net_unlock(); + + return OK; +} + +/**************************************************************************** + * Name: ip_frag_stop + * + * Description: + * Stop the fragment process function for the specified NIC. + * + * Input Parameters: + * dev - NIC Device instance which will be bring down + * + * Returned Value: + * None + * + ****************************************************************************/ + +void ip_frag_stop(FAR struct net_driver_s *dev) +{ + FAR sq_entry_t *entry = NULL; + FAR sq_entry_t *entrynext; + + ninfo("Stop frag processing for NIC:%p\n", dev); + + nxmutex_lock(&g_ipfrag_lock); + + entry = sq_peek(&g_assemblyhead_ipid); + + /* Drop those unassembled incoming fragments belonging to this NIC */ + + while (entry != NULL) + { + FAR struct ip_fragsnode_s *node = (FAR struct ip_fragsnode_s *)entry; + entrynext = sq_next(entry); + + if (dev == node->dev) + { + if (node->frags != NULL) + { + FAR struct ip_fraglink_s *fraglink = node->frags; + + while (fraglink != NULL) + { + fraglink = ip_fragin_freelink(fraglink); + } + } + + ip_frag_remnode(node); + kmm_free(entry); + } + + entry = entrynext; + } + + nxmutex_unlock(&g_ipfrag_lock); + + /* Drop those unsent outgoing fragments belonging to this NIC */ + + iob_free_queue(&dev->d_fragout); +} + +/**************************************************************************** + * Name: ip_frag_remallfrags + * + * Description: + * Release all I/O Buffers used by fragment processing module when + * I/O Buffer resources are exhausted. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void ip_frag_remallfrags(void) +{ + FAR sq_entry_t *entry = NULL; + FAR sq_entry_t *entrynext; + FAR struct net_driver_s *dev; + + nxmutex_lock(&g_ipfrag_lock); + + entry = sq_peek(&g_assemblyhead_ipid); + + /* Drop all unassembled incoming fragments */ + + while (entry != NULL) + { + FAR struct ip_fragsnode_s *node = (FAR struct ip_fragsnode_s *)entry; + entrynext = sq_next(entry); + + if (node->frags != NULL) + { + FAR struct ip_fraglink_s *fraglink = node->frags; + + while (fraglink != NULL) + { + fraglink = ip_fragin_freelink(fraglink); + } + } + + /* Because nodes managed by the two queues are the same, + * and g_assemblyhead_ipid will be cleared after this loop ends, + * so only reset g_assemblyhead_time is needed after this loop ends + */ + + sq_rem(entry, &g_assemblyhead_ipid); + kmm_free(entry); + + entry = entrynext; + } + + sq_init(&g_assemblyhead_time); + g_bufoccupy = 0; + + nxmutex_unlock(&g_ipfrag_lock); + + /* Drop all unsent outgoing fragments */ + + net_lock(); + for (dev = g_netdevices; dev; dev = dev->flink) + { + /* Is the interface in the "up" state? */ + + if ((dev->d_flags & IFF_UP) != 0) + { + iob_free_queue(&dev->d_fragout); + } + } + + net_unlock(); +} + +/**************************************************************************** + * Name: ip_fragout + * + * Description: + * Fragout processing + * + * Input Parameters: + * dev - The NIC device + * + * Returned Value: + * A non-negative value is returned on success; negative value on failure. + * + ****************************************************************************/ + +int32_t ip_fragout(FAR struct net_driver_s *dev) +{ + uint16_t mtu = dev->d_pktsize - dev->d_llhdrlen; + + if (dev->d_iob == NULL || dev->d_len == 0) + { + return -EINVAL; + } + + if (dev->d_iob->io_pktlen <= mtu) + { + return OK; + } + +#ifdef CONFIG_NET_6LOWPAN + if (dev->d_lltype == NET_LL_IEEE802154 || + dev->d_lltype == NET_LL_PKTRADIO) + { + return -EINVAL; + } +#endif + + if (devif_is_loopback(dev)) + { + return OK; + } + + ninfo("pkt size: %d, MTU: %d\n", dev->d_iob->io_pktlen, mtu); + +#ifdef CONFIG_NET_IPv4 + if (IFF_IS_IPv4(dev->d_flags)) + { + return ipv4_fragout(dev, mtu); + } +#endif + +#ifdef CONFIG_NET_IPv6 + if (IFF_IS_IPv6(dev->d_flags)) + { + return ipv6_fragout(dev, mtu); + } +#endif + + return -EINVAL; +} + +#endif /* (CONFIG_NET_IPv4 || CONFIG_NET_IPv6) && CONFIG_NET_IPFRAG */ diff --git a/net/ipfrag/ipfrag.h b/net/ipfrag/ipfrag.h new file mode 100755 index 00000000000..15ce7a00c55 --- /dev/null +++ b/net/ipfrag/ipfrag.h @@ -0,0 +1,388 @@ +/**************************************************************************** + * net/ipfrag/ipfrag.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __NET_IPFRAG_IPFRAG_H +#define __NET_IPFRAG_IPFRAG_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "devif/devif.h" + +#if defined(CONFIG_NET_IPFRAG) + +/**************************************************************************** + * Public types + ****************************************************************************/ + +enum ip_fragverify_e +{ + /* Indicates whether received all fragments */ + + IP_FRAGVERIFY_RECVDALLFRAGS = 0x01 << 0, + + /* Indicates whether received the first fragment which is used to: + * 1.construct the ICMP time exceeded msg(type=11, code=1) when reassembly + * timeout, but if the first fragment has not been received when timeout, + * no ICMP error message will be sent; + * 2.build NAT entry with the L4 port number and do forwarding. + */ + + IP_FRAGVERIFY_RECVDZEROFRAG = 0x01 << 1, + + /* Indicates whether the tail fragment is received(which morefrag flag is + * set to 0) + */ + + IP_FRAGVERIFY_RECVDTAILFRAG = 0x01 << 2, +}; + +struct ip_fraglink_s +{ + /* This link is used to maintain a single-linked list of ip_fraglink_s, + * it links all framgents with the same IP ID + */ + + FAR struct ip_fraglink_s *flink; + + FAR struct ip_fragsnode_s *fragsnode; /* Point to parent struct */ + FAR struct iob_s *frag; /* Point to fragment data */ + uint8_t isipv4; /* IPv4 or IPv6 */ + uint16_t fragoff; /* Fragment offset */ + uint16_t fraglen; /* Payload length */ + uint16_t morefrags; /* The more frag flag */ + + /* The identification field is 16 bits in IPv4 header but 32 bits in IPv6 + * fragment header + */ + + uint32_t ipid; +}; + +struct ip_fragsnode_s +{ + /* This link is used to maintain a single-linked list of ip_fragsnode_s. + * Must be the first field in the structure due to flink type casting. + */ + + FAR struct ip_fragsnode_s *flink; + + /* Another link which connects all ip_fragsnode_s in order of addition + * time + */ + + FAR sq_entry_t *flinkat; + + /* Interface understood by the network */ + + FAR struct net_driver_s *dev; + + /* IP Identification (IP ID) field defined in ipv4 header or in ipv6 + * fragment header. + */ + + uint32_t ipid; + + /* Count ticks, used by ressembly timer */ + + clock_t tick; + + /* Remember some running flags */ + + uint16_t verifyflag; + + /* Remember the total number of I/O buffers of this node */ + + uint32_t bufcnt; + + /* Linked all fragments with the same IP ID. */ + + FAR struct ip_fraglink_s *frags; + + /* Points to the reassembled outgoing IP frame */ + + FAR struct iob_s *outgoframe; +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifdef __cplusplus +# define EXTERN extern "C" +extern "C" +{ +#else +# define EXTERN extern +#endif + +/* Only one thread can access g_assemblyhead_ipid and g_assemblyhead_time + * at a time + */ + +extern mutex_t g_ipfrag_lock; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: ip_frag_remnode + * + * Description: + * free ip_fragsnode_s + * + * Input Parameters: + * node - node of the upper-level linked list, it maintains + * information about all fragments belonging to an IP datagram + * + * Returned Value: + * I/O buffer count of this node + * + ****************************************************************************/ + +uint32_t ip_frag_remnode(FAR struct ip_fragsnode_s *node); + +/**************************************************************************** + * Name: ip_fragin_enqueue + * + * Description: + * Enqueue one fragment. + * All fragments belonging to one IP frame are organized in a linked list + * form, that is a ip_fragsnode_s node. All ip_fragsnode_s nodes are also + * organized in an upper-level linked list. + * + * Input Parameters: + * dev - NIC Device instance + * curfraglink - node of the lower-level linked list, it maintains + * information of one fragment + * + * Returned Value: + * Whether queue is empty before enqueue the new node + * + ****************************************************************************/ + +bool ip_fragin_enqueue(FAR struct net_driver_s *dev, + FAR struct ip_fraglink_s *curfraglink); + +/**************************************************************************** + * Name: ipv4_fragin + * + * Description: + * Handling incoming IPv4 fragment input, the input data + * (dev->d_iob) can be an I/O buffer chain + * + * Input Parameters: + * dev - The NIC device that the fragmented data comes from + * + * Returned Value: + * ENOMEM - No memory + * OK - The input fragment is processed as expected + * + ****************************************************************************/ + +int32_t ipv4_fragin(FAR struct net_driver_s *dev); + +/**************************************************************************** + * Name: ipv6_fragin + * + * Description: + * Handling incoming IPv6 fragment input, the input data + * (dev->d_iob) can be an I/O buffer chain + * + * Input Parameters: + * dev - The NIC device that the fragmented data comes from + * + * Returned Value: + * ENOMEM - No memory + * OK - The input fragment is processed as expected + * + ****************************************************************************/ + +int32_t ipv6_fragin(FAR struct net_driver_s *dev); + +/**************************************************************************** + * Name: ip_fragout_slice + * + * Description: + * According to the MTU of a given NIC, split the original data into + * multiple data pieces, and the space for filling the L3 header is + * reserved at the forefront of each piece. Each piece is stored in + * independent I/O buffer(s) and eventually forms an I/O buffer queue. + * Note: + * 1.About the 'piece' above + * 1).If MTU < CONFIG_IOB_BUFSIZE, a piece consists of an I/O buffer; + * 2).If MTU >= CONFIG_IOB_BUFSIZE, a piece consists of multiple I/O + * buffers. + * 2.This function split and gathers the incoming data into outgoing + * I/O buffers according to the MTU, but is not responsible for + * building the L3 header related to the fragmentation. + * + * Input Parameters: + * iob - The data comes from + * domain - PF_INET or PF_INET6 + * mtu - MTU of given NIC + * unfraglen - The starting position to fragmentation processing + * fragq - Those output slices + * + * Returned Value: + * Number of fragments + * + * Assumptions: + * Data length(iob->io_pktlen) is grater than the MTU of current NIC + * + ****************************************************************************/ + +int32_t ip_fragout_slice(FAR struct iob_s *iob, uint8_t domain, uint16_t mtu, + uint16_t unfraglen, FAR struct iob_queue_s *fragq); + +/**************************************************************************** + * Name: ipv4_fragout + * + * Description: + * Execute the ipv4 fragment function. After this work is done, all + * fragments are maintained by dev->d_fragout. In order to reduce the + * cyclomatic complexity and facilitate maintenance, fragmentation is + * performed in two steps: + * 1. Reconstruct I/O Buffer according to MTU, which will reserve + * the space for the L3 header; + * 2. Fill the L3 header into the reserved space. + * + * Input Parameters: + * dev - The NIC device + * mtu - The MTU of current NIC + * + * Returned Value: + * 0 if success or a negative value if fail. + * + * Assumptions: + * Data length(dev->d_iob->io_pktlen) is grater than the MTU of + * current NIC + * + ****************************************************************************/ + +int32_t ipv4_fragout(FAR struct net_driver_s *dev, uint16_t mtu); + +/**************************************************************************** + * Name: ipv6_fragout + * + * Description: + * Execute the ipv6 fragment function. After this work is done, all + * fragments are maintained by dev->d_fragout. In order to reduce the + * cyclomatic complexity and facilitate maintenance, fragmentation is + * performed in two steps: + * 1. Reconstruct I/O Buffer according to MTU, which will reserve + * the space for the L3 header; + * 2. Fill the L3 header into the reserved space. + * + * Input Parameters: + * dev - The NIC device + * mtu - The MTU of current NIC + * + * Returned Value: + * 0 if success or a negative value if fail. + * + * Assumptions: + * Data length(dev->d_iob->io_pktlen) is grater than the MTU of + * current NIC + * + ****************************************************************************/ + +int32_t ipv6_fragout(FAR struct net_driver_s *dev, uint16_t mtu); + +/**************************************************************************** + * Name: ip_frag_startwdog + * + * Description: + * Start the reassembly timeout timer + * + * Returned Value: + * None + * + ****************************************************************************/ + +void ip_frag_startwdog(void); + +/**************************************************************************** + * Name: ip_frag_stop + * + * Description: + * Stop the fragment process function for the specified NIC. + * + * Input Parameters: + * dev - NIC Device instance which will be bring down + * + * Returned Value: + * None + * + ****************************************************************************/ + +void ip_frag_stop(FAR struct net_driver_s *dev); + +/**************************************************************************** + * Name: ip_frag_remallfrags + * + * Description: + * Release all I/O Buffers used by fragment processing module when + * I/O Buffer resources are exhausted. + * + * Returned Value: + * None + * + ****************************************************************************/ + +void ip_frag_remallfrags(void); + +/**************************************************************************** + * Name: ip_fragout + * + * Description: + * Fragout processing + * + * Input Parameters: + * dev - The NIC device + * + * Returned Value: + * A non-negative value is returned on success; negative value on failure. + * + ****************************************************************************/ + +int32_t ip_fragout(FAR struct net_driver_s *dev); + +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_NET_IPFRAG */ +#endif /* __NET_IPFRAG_IPFRAG_H */ diff --git a/net/ipfrag/ipv4_frag.c b/net/ipfrag/ipv4_frag.c new file mode 100755 index 00000000000..156493bcd30 --- /dev/null +++ b/net/ipfrag/ipv4_frag.c @@ -0,0 +1,443 @@ +/**************************************************************************** + * net/ipfrag/ipv4_frag.c + * Handling incoming IPv4 fragment input + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#if defined(CONFIG_NET_IPv4) && defined (CONFIG_NET_IPFRAG) + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "netdev/netdev.h" +#include "inet/inet.h" +#include "utils/utils.h" +#include "ipfrag.h" + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static inline int32_t +ipv4_fragin_getinfo(FAR struct iob_s *iob, + FAR struct ip_fraglink_s *fraglink); +static uint32_t ipv4_fragin_reassemble(FAR struct ip_fragsnode_s *node); +static inline void +ipv4_fragout_buildipv4header(FAR struct ipv4_hdr_s *ref, + FAR struct ipv4_hdr_s *ipv4, + uint16_t len, uint16_t ipoff); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ipv4_fragin_getinfo + * + * Description: + * Polulate fragment information from the input ipv4 packet data. + * + * Input Parameters: + * iob - An IPv4 fragment + * fraglink - node of the lower-level linked list, it maintains information + * of one fragment + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline int32_t +ipv4_fragin_getinfo(FAR struct iob_s *iob, + FAR struct ip_fraglink_s *fraglink) +{ + FAR struct ipv4_hdr_s *ipv4 = (FAR struct ipv4_hdr_s *) + (iob->io_data + iob->io_offset); + uint16_t offset; + + fraglink->flink = NULL; + fraglink->fragsnode = NULL; + fraglink->isipv4 = true; + + offset = (ipv4->ipoffset[0] << 8) + ipv4->ipoffset[1]; + fraglink->morefrags = offset & IP_FLAG_MOREFRAGS; + fraglink->fragoff = ((offset & 0x1fff) << 3); + + fraglink->fraglen = (ipv4->len[0] << 8) + ipv4->len[1] - IPv4_HDRLEN; + fraglink->ipid = (ipv4->ipid[0] << 8) + ipv4->ipid[1]; + fraglink->frag = iob; + + return OK; +} + +/**************************************************************************** + * Name: ipv4_fragin_reassemble + * + * Description: + * Reassemble all ipv4 fragments to build an IP frame. + * + * Input Parameters: + * node - node of the upper-level linked list, it maintains + * information about all fragments belonging to an IP datagram + * + * Returned Value: + * The length of the reassembled IP frame + * + ****************************************************************************/ + +static uint32_t ipv4_fragin_reassemble(FAR struct ip_fragsnode_s *node) +{ + FAR struct iob_s *head; + FAR struct ipv4_hdr_s *ipv4; + FAR struct ip_fraglink_s *fraglink; + + /* Loop to walk through the fragment list and reassemble those fragments, + * the fraglink list was ordered by fragment offset value + */ + + fraglink = node->frags; + node->frags = NULL; + + while (fraglink != NULL) + { + FAR struct ip_fraglink_s *linknext; + FAR struct iob_s *iob = fraglink->frag; + + if (fraglink->fragoff != 0) + { + uint16_t iphdrlen; + + /* Get IPv4 header length from IPv4 header (it may carry some + * IPv4 options) + */ + + ipv4 = (FAR struct ipv4_hdr_s *)(head->io_data + head->io_offset); + iphdrlen = (ipv4->vhl & IPv4_HLMASK) << 2; + + /* Just modify the offset and length of all none zero fragments */ + + iob->io_offset += iphdrlen; + iob->io_len -= iphdrlen; + iob->io_pktlen -= iphdrlen; + + /* Concatenate this iob to the reassembly chain */ + + iob_concat(head, iob); + } + else + { + /* Remember the head iob */ + + head = iob; + } + + linknext = fraglink->flink; + kmm_free(fraglink); + + fraglink = linknext; + } + + /* Remember the reassembled outgoing IP frame */ + + node->outgoframe = head; + + /* Get pointer of the new IPv4 header */ + + ipv4 = (FAR struct ipv4_hdr_s *)(head->io_data + head->io_offset); + + /* Update the length value in the IP Header */ + + ipv4->len[0] = head->io_pktlen >> 8; + ipv4->len[1] = head->io_pktlen & 0xff; + + /* Set ipoffset to zero */ + + ipv4->ipoffset[0] = 0; + ipv4->ipoffset[1] = 0; + + /* Calculate IP checksum. */ + + ipv4->ipchksum = 0; + ipv4->ipchksum = ~(ipv4_chksum(ipv4)); + + return head->io_pktlen; +} + +/**************************************************************************** + * Name: ipv4_fragout_buildipv4header + * + * Description: + * Build IPv4 header for an IPv4 fragment. + * + * Input Parameters: + * ref - The reference IPv4 Header + * ipv4 - The pointer of the newly generated IPv4 Header + * len - Total Length of this IP frame + * ipoff - Fragment offset + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void +ipv4_fragout_buildipv4header(FAR struct ipv4_hdr_s *ref, + FAR struct ipv4_hdr_s *ipv4, + uint16_t len, uint16_t ipoff) +{ + if (ref != ipv4) + { + uint32_t iphdrlen = (ref->vhl & IPv4_HLMASK) << 2; + memcpy(ipv4, ref, iphdrlen); + } + + ipv4->len[0] = len >> 8; + ipv4->len[1] = len & 0xff; + + ipv4->ipoffset[0] = ipoff >> 8; + ipv4->ipoffset[1] = ipoff & 0xff; + + /* Calculate IP checksum. */ + + ipv4->ipchksum = 0; + ipv4->ipchksum = ~(ipv4_chksum(ipv4)); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ipv4_fragin + * + * Description: + * Handling incoming IPv4 and IPv6 fragment input, the input data + * (dev->d_iob) can be an I/O buffer chain + * + * Input Parameters: + * dev - The NIC device that the fragmented data comes from + * + * Returned Value: + * ENOMEM - No memory + * OK - The input fragment is processed as expected + * + ****************************************************************************/ + +int32_t ipv4_fragin(FAR struct net_driver_s *dev) +{ + FAR struct ip_fragsnode_s *node; + FAR struct ip_fraglink_s *fraginfo; + bool restartwdog; + + if (dev->d_len != dev->d_iob->io_pktlen) + { + nerr("ERROR: Parameters error.\n"); + return -EINVAL; + } + + fraginfo = kmm_malloc(sizeof(struct ip_fraglink_s)); + if (fraginfo == NULL) + { + nerr("ERROR: Failed to allocate buffer.\n"); + return -ENOMEM; + } + + /* Polulate fragment information from input packet data */ + + ipv4_fragin_getinfo(dev->d_iob, fraginfo); + + nxmutex_lock(&g_ipfrag_lock); + + /* Need to restart reassembly worker if the original linked list is empty */ + + restartwdog = ip_fragin_enqueue(dev, fraginfo); + + node = fraginfo->fragsnode; + + if (node->verifyflag & IP_FRAGVERIFY_RECVDALLFRAGS) + { + /* Well, all fragments of an IP frame have been received, remove + * node from link list first, then reassemble and dispatch to the + * stack. + */ + + ip_frag_remnode(node); + + /* All fragments belonging to one IP frame have been separated + * from the fragment processing module, unlocks mutex as soon + * as possible + */ + + nxmutex_unlock(&g_ipfrag_lock); + + /* Reassemble fragments to one IP frame and set the resulting + * IP frame to dev->d_iob + */ + + ipv4_fragin_reassemble(node); + netdev_iob_replace(dev, node->outgoframe); + + /* Free the memory of node */ + + kmm_free(node); + + return ipv4_input(dev); + } + + nxmutex_unlock(&g_ipfrag_lock); + + if (restartwdog) + { + /* Restart the work queue for fragment processing */ + + ip_frag_startwdog(); + } + + return OK; +} + +/**************************************************************************** + * Name: ipv4_fragout + * + * Description: + * Execute the ipv4 fragment function. After this work is done, all + * fragments are maintained by dev->d_fragout. In order to reduce the + * cyclomatic complexity and facilitate maintenance, fragmentation is + * performed in two steps: + * 1. Reconstruct I/O Buffer according to MTU, which will reserve + * the space for the L3 header; + * 2. Fill the L3 header into the reserved space. + * + * Input Parameters: + * dev - The NIC device + * mtu - The MTU of current NIC + * + * Returned Value: + * 0 if success or a negative value if fail. + * + * Assumptions: + * Data length(dev->d_iob->io_pktlen) is grater than the MTU of + * current NIC + * + ****************************************************************************/ + +int32_t ipv4_fragout(FAR struct net_driver_s *dev, uint16_t mtu) +{ + uint32_t iter; + uint32_t nfrags; + uint16_t offset = 0; + uint16_t hdrlen; + FAR struct iob_s *frag; + FAR struct ipv4_hdr_s *ref; + struct iob_queue_s fragq = + { + NULL, NULL + }; + + /* Get the total length of L3 Header(if IPv4 options are present, then this + * length includes the size of all the IPv4 options) + */ + + hdrlen = (IPv4BUF->vhl & IPv4_HLMASK) << 2; + + /* Reconstruct I/O Buffer according to MTU, which will reserve + * the space for the L3 header + */ + + nfrags = ip_fragout_slice(dev->d_iob, PF_INET, mtu, hdrlen, &fragq); + assert(nfrags > 1); + netdev_iob_clear(dev); + + /* Fill the L3 header into the reserved space */ + + for (iter = 0; iter < nfrags; iter++) + { + frag = iob_remove_queue(&fragq); + + if (iter == 0) + { + ref = (FAR struct ipv4_hdr_s *)(frag->io_data + frag->io_offset); + + /* Update the IPv4 header of the first fragment */ + + ipv4_fragout_buildipv4header(ref, ref, frag->io_pktlen, + IP_FLAG_MOREFRAGS); + } + else + { + uint16_t ipoff = (offset - iter * hdrlen) >> 3; + + if (iter < nfrags - 1) + { + ipoff |= IP_FLAG_MOREFRAGS; + } + + /* Refer to the zero fragment ipv4 header to construct the ipv4 + * header of non-zero fragment + */ + + ipv4_fragout_buildipv4header(ref, + (FAR struct ipv4_hdr_s *)(frag->io_data + frag->io_offset), + frag->io_pktlen, ipoff); + } + + /* Enqueue this fragment to dev->d_fragout */ + + if (iob_tryadd_queue(frag, &dev->d_fragout) < 0) + { + goto fail; + } + + offset += frag->io_pktlen; + } + +#ifdef CONFIG_NET_STATISTICS + g_netstats.ipv4.sent += nfrags - 1; +#endif + + netdev_txnotify_dev(dev); + + return OK; + +fail: + netdev_iob_release(dev); + iob_free_chain(frag); + iob_free_queue(&fragq); + iob_free_queue(&dev->d_fragout); + return -ENOMEM; +} + +#endif /* CONFIG_NET_IPv4 && CONFIG_NET_IPFRAG */ diff --git a/net/ipfrag/ipv6_frag.c b/net/ipfrag/ipv6_frag.c new file mode 100755 index 00000000000..b39ae816872 --- /dev/null +++ b/net/ipfrag/ipv6_frag.c @@ -0,0 +1,670 @@ +/**************************************************************************** + * net/ipfrag/ipv6_frag.c + * Handling incoming IPv6 fragment input + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#if defined(CONFIG_NET_IPv6) && defined (CONFIG_NET_IPFRAG) + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "netdev/netdev.h" +#include "inet/inet.h" +#include "ipfrag.h" + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* The increasing number used for the IP ID field of IPv6 Fragment Header. */ + +static uint32_t g_ipv6id; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int32_t ipv6_fragin_getinfo(FAR struct iob_s *iob, + FAR struct ip_fraglink_s *fraglink); +static uint32_t ipv6_fragin_reassemble(FAR struct ip_fragsnode_s *node); +static inline void +ipv6_fragout_buildipv6header(FAR struct ipv6_hdr_s *ref, + FAR struct ipv6_hdr_s *ipv6, + uint16_t hdrlen, uint16_t datalen, + uint16_t nxthdroff, uint16_t nxtprot); +static inline void +ipv6_fragout_buildipv6fragheader(FAR struct ipv6_fragment_extension_s *frag, + uint8_t nxthdr, uint16_t ipoff, + uint32_t ipid); +static uint16_t ipv6_fragout_getunfraginfo(FAR struct iob_s *iob, + uint16_t *hdroff, + uint16_t *hdrtype); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ipv6_fragin_getinfo + * + * Description: + * Polulate fragment information from the input ipv6 packet data. + * + * Input Parameters: + * iob - An IPv6 fragment + * fraglink - node of the lower-level linked list, it maintains information + * of one fragment + * + * Returned Value: + * OK - Got fragment information. + * EINVAL - The input ipv6 packet is not a fragment. + * + ****************************************************************************/ + +static int32_t ipv6_fragin_getinfo(FAR struct iob_s *iob, + FAR struct ip_fraglink_s *fraglink) +{ + FAR struct ipv6_hdr_s *ipv6 = (FAR struct ipv6_hdr_s *) + (iob->io_data + iob->io_offset); + FAR struct ipv6_extension_s *exthdr; + FAR uint8_t *payload; + uint16_t paylen; + uint8_t nxthdr; + + paylen = ((uint16_t)ipv6->len[0] << 8) + (uint16_t)ipv6->len[1]; + payload = (FAR uint8_t *)(ipv6 + 1); + exthdr = (FAR struct ipv6_extension_s *)payload; + nxthdr = ipv6->proto; + + while (nxthdr != NEXT_FRAGMENT_EH && ipv6_exthdr(nxthdr)) + { + uint16_t extlen; + + exthdr = (FAR struct ipv6_extension_s *)payload; + extlen = EXTHDR_LEN((unsigned int)exthdr->len); + payload += extlen; + paylen -= extlen; + nxthdr = exthdr->nxthdr; + }; + + if (nxthdr == NEXT_FRAGMENT_EH) + { + FAR struct ipv6_fragment_extension_s *fraghdr; + + fraghdr = (FAR struct ipv6_fragment_extension_s *)exthdr; + + /* Cut the size of fragment header, notice fragment header don't has a + * length filed. + */ + + paylen -= EXTHDR_FRAG_LEN; + + fraglink->flink = NULL; + fraglink->fragsnode = NULL; + + fraglink->isipv4 = FALSE; + fraglink->fragoff = (fraghdr->msoffset << 8) + fraghdr->lsoffset; + fraglink->morefrags = fraglink->fragoff & 0x1; + fraglink->fragoff &= 0xfff8; + fraglink->fraglen = paylen; + fraglink->ipid = NTOHL((*(uint16_t *)(&fraghdr->id[0]) << 16) + + *(uint16_t *)(&fraghdr->id[2])); + + fraglink->frag = iob; + + return OK; + } + else + { + return -EINVAL; + } +} + +/**************************************************************************** + * Name: ipv6_fragin_reassemble + * + * Description: + * Reassemble all ipv6 fragments to build an IP frame. + * + * Input Parameters: + * node - node of the upper-level linked list, it maintains + * information about all fragments belonging to an IP datagram + * + * Returned Value: + * The length of the reassembled IP frame + * + ****************************************************************************/ + +static uint32_t ipv6_fragin_reassemble(FAR struct ip_fragsnode_s *node) +{ + FAR struct iob_s *head; + FAR struct ipv6_hdr_s *ipv6; + FAR struct ip_fraglink_s *fraglink; + + /* Loop to walk through the fragment list and reassemble those fragments, + * the fraglink list was ordered by fragment offset value + */ + + fraglink = node->frags; + node->frags = NULL; + + while (fraglink != NULL) + { + FAR uint8_t *payload; + uint8_t nxthdr; + FAR struct iob_s *iob; + FAR struct ip_fraglink_s *linknext; + FAR struct ipv6_extension_s *exthdr; + FAR struct ipv6_fragment_extension_s *fraghdr; + + iob = fraglink->frag; + ipv6 = (FAR struct ipv6_hdr_s *)(iob->io_data + iob->io_offset); + payload = (FAR uint8_t *)(ipv6 + 1); + exthdr = (FAR struct ipv6_extension_s *)payload; + nxthdr = ipv6->proto; + + /* Find fragment header and the front header which is close to the + * fragment header + */ + + while (nxthdr != NEXT_FRAGMENT_EH && ipv6_exthdr(nxthdr)) + { + uint16_t extlen; + + exthdr = (FAR struct ipv6_extension_s *)payload; + extlen = EXTHDR_LEN((unsigned int)exthdr->len); + payload += extlen; + nxthdr = exthdr->nxthdr; + }; + + fraghdr = (FAR struct ipv6_fragment_extension_s *)payload; + + /* Skip fragment header, notice fragment header don't has a length + * filed + */ + + payload += EXTHDR_FRAG_LEN; + + if (fraglink->fragoff == 0) + { + /* This is the zero fragment, Set the front header's next header + * filed to the next header value of the fragment header + */ + + if (ipv6->proto == NEXT_FRAGMENT_EH) + { + ipv6->proto = fraghdr->nxthdr; + } + else + { + exthdr->nxthdr = fraghdr->nxthdr; + } + + /* Remove fragment header and fix up the data length */ + + memmove(fraghdr, payload, + iob->io_len - (payload - (iob->io_data + iob->io_offset))); + iob->io_len -= EXTHDR_FRAG_LEN; + iob->io_pktlen -= EXTHDR_FRAG_LEN; + + /* Remember the head iob */ + + head = iob; + } + else + { + uint16_t new_off; + + /* Fix up the value of offset and length for this none zero + * fragment + */ + + new_off = payload - iob->io_data; + iob->io_len -= new_off - iob->io_offset; + iob->io_pktlen -= new_off - iob->io_offset; + iob->io_offset = new_off; + + /* Concatenate this iob to the reassembly chain */ + + iob_concat(head, iob); + } + + linknext = fraglink->flink; + kmm_free(fraglink); + fraglink = linknext; + } + + /* Remember the reassembled outgoing IP frame */ + + node->outgoframe = head; + + /* Adjust the length value in the IP Header */ + + ipv6 = (FAR struct ipv6_hdr_s *)(head->io_data + head->io_offset); + ipv6->len[0] = (head->io_pktlen - IPv6_HDRLEN) >> 8; + ipv6->len[1] = (head->io_pktlen - IPv6_HDRLEN) & 0xff; + + return head->io_pktlen; +} + +/**************************************************************************** + * Name: ipv6_fragout_buildipv6header + * + * Description: + * Build IPv6 header for an IPv6 fragment. + * + * Input Parameters: + * ref - The reference IPv6 Header + * ipv6 - The pointer of the newly generated IPv6 Header + * hdrlen - Including the length of IPv6 basic header and all + * extention headers + * datalen - The data length follows the IPv6 basic header + * nxthdroff - The offset of 'next header' to be updated + * nxtprot - The value of 'next header' to be updated + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void +ipv6_fragout_buildipv6header(FAR struct ipv6_hdr_s *ref, + FAR struct ipv6_hdr_s *ipv6, + uint16_t hdrlen, uint16_t datalen, + uint16_t nxthdroff, uint16_t nxtprot) +{ + if (ref != ipv6) + { + /* Copy unfragmentable header data from reference header */ + + memcpy(ipv6, ref, hdrlen); + } + + /* Update length filed */ + + ipv6->len[0] = datalen >> 8; + ipv6->len[1] = datalen & 0xff; + + /* If extension headers exist, update the Next Header field in the + * last extension header of the unfragmentable part; Otherwise update + * the Next Header field of the basic IPv6 header. + */ + + *((uint8_t *)ipv6 + nxthdroff) = nxtprot; +} + +/**************************************************************************** + * Name: ipv6_fragout_buildipv6fragheader + * + * Description: + * Build IPv6 fragment extension header for an IPv6 fragment. + * + * Input Parameters: + * frag - The pointer of the newly generated IPv6 fragment Header + * nxthdr - The first header type in the fragmentable part + * ipoff - Fragment offset + * ipid - The value of IPv6 IP ID + * + * Returned Value: + * None + * + ****************************************************************************/ + +static inline void +ipv6_fragout_buildipv6fragheader(FAR struct ipv6_fragment_extension_s *frag, + uint8_t nxthdr, uint16_t ipoff, + uint32_t ipid) +{ + frag->nxthdr = nxthdr; + frag->reserved = 0; + frag->msoffset = ipoff >> 8; + frag->lsoffset = ipoff & 0xff; + *(uint16_t *)&frag->id[0] = HTONL(ipid) & 0xffff; + *(uint16_t *)&frag->id[2] = HTONL(ipid) >> 16; +} + +/**************************************************************************** + * Name: ipv6_fragout_getunfraginfo + * + * Description: + * Get the length of Unfragmentable Part of the original ipv6 packet, + * remember the offset and value of nextheader in the last extension + * header of the unfragmentable part. + * Refer to rfc2460, section-4.1, section-4.5 + * + * Input Parameters: + * iob - Outgoing data waiting for fragment + * hdroff - The offset of the last next header position in the + * unfragmentable part + * hdrtype - The first header type in the fragmentable part + * + * Returned Value: + * Unfragmentable Part length + * + ****************************************************************************/ + +static uint16_t ipv6_fragout_getunfraginfo(FAR struct iob_s *iob, + uint16_t *hdroff, + uint16_t *hdrtype) +{ + uint32_t iter = 0; + bool destopt = false; + uint16_t delta = sizeof(struct ipv6_hdr_s); + uint16_t unfraglen = delta; + uint8_t nxthdr; + FAR struct ipv6_hdr_s *ipv6; + FAR struct ipv6_extension_s *exthdr; + FAR uint8_t *payload; + + ipv6 = (FAR struct ipv6_hdr_s *)(iob->io_data + iob->io_offset); + payload = (FAR uint8_t *)(ipv6 + 1); + exthdr = (FAR struct ipv6_extension_s *)payload; + nxthdr = ipv6->proto; + + *hdroff = offsetof(struct ipv6_hdr_s, proto); + *hdrtype = ipv6->proto; + + /* Traverse up to three extension headers, if the Destination Options + * Header appears repeatedly, ingore the secondary one and end the search. + * refer to rfc2460, section-4.1 + */ + + while (ipv6_exthdr(nxthdr) && iter++ < 3) + { + uint16_t extlen; + + exthdr = (FAR struct ipv6_extension_s *)payload; + extlen = EXTHDR_LEN((unsigned int)exthdr->len); + + switch (nxthdr) + { + case NEXT_DESTOPT_EH: + if (!destopt) + { + destopt = true; + } + else + { + /* This is the secondary Destination Options Header, + * end the search + */ + + goto done; + } + + case NEXT_ROUTING_EH: + case NEXT_HOPBYBOT_EH: + unfraglen = delta + extlen; + *hdroff = delta; + *hdrtype = exthdr->nxthdr; + } + + payload += extlen; + delta += extlen; + nxthdr = exthdr->nxthdr; + } + +done: + return unfraglen; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ipv6_fragin + * + * Description: + * Handling incoming IPv6 fragment input, the input data + * (dev->d_iob) can be an I/O buffer chain + * + * Input Parameters: + * dev - The NIC device that the fragmented data comes from + * + * Returned Value: + * ENOMEM - No memory + * OK - The input fragment is processed as expected + * + ****************************************************************************/ + +int32_t ipv6_fragin(FAR struct net_driver_s *dev) +{ + FAR struct ip_fragsnode_s *node = NULL; + FAR struct ip_fraglink_s *fraginfo = NULL; + bool restartwdog; + + if (dev->d_len != dev->d_iob->io_pktlen) + { + nerr("ERROR: Parameters error.\n"); + return -EINVAL; + } + + fraginfo = kmm_malloc(sizeof(struct ip_fraglink_s)); + if (fraginfo == NULL) + { + nerr("ERROR: Failed to allocate buffer.\n"); + return -ENOMEM; + } + + /* Polulate fragment information from input packet data */ + + ipv6_fragin_getinfo(dev->d_iob, fraginfo); + + nxmutex_lock(&g_ipfrag_lock); + + /* Need to restart reassembly worker if the original linked list is empty */ + + restartwdog = ip_fragin_enqueue(dev, fraginfo); + + node = fraginfo->fragsnode; + if (node->verifyflag & IP_FRAGVERIFY_RECVDALLFRAGS) + { + /* Well, all fragments of an IP frame have been received, remove + * node from link list first, then reassemble and dispatch to the + * stack. + */ + + ip_frag_remnode(node); + + /* All fragments belonging to one IP frame have been separated + * from the fragment processing module, unlocks mutex as soon + * as possible + */ + + nxmutex_unlock(&g_ipfrag_lock); + + /* Reassemble fragments to one IP frame and set the resulting + * IP frame to dev->d_iob + */ + + ipv6_fragin_reassemble(node); + netdev_iob_replace(dev, node->outgoframe); + + /* Free the memory of node */ + + kmm_free(node); + + return ipv6_input(dev); + } + + nxmutex_unlock(&g_ipfrag_lock); + + if (restartwdog) + { + /* Restart the work queue for fragment processing */ + + ip_frag_startwdog(); + } + + return OK; +} + +/**************************************************************************** + * Name: ipv6_fragout + * + * Description: + * Execute the ipv6 fragment function. After this work is done, all + * fragments are maintained by dev->d_fragout. In order to reduce the + * cyclomatic complexity and facilitate maintenance, fragmentation is + * performed in two steps: + * 1. Reconstruct I/O Buffer according to MTU, which will reserve + * the space for the L3 header; + * 2. Fill the L3 header into the reserved space. + * + * Input Parameters: + * dev - The NIC device + * mtu - The MTU of current NIC + * + * Returned Value: + * 0 if success or a negative value if fail. + * + * Assumptions: + * Data length(dev->d_iob->io_pktlen) is grater than the MTU of + * current NIC + * + ****************************************************************************/ + +int32_t ipv6_fragout(FAR struct net_driver_s *dev, uint16_t mtu) +{ + uint16_t unfraglen; + uint16_t offset = 0; + uint32_t ipid; + uint32_t iter; + uint32_t nfrags; + uint16_t hdroff; + uint16_t hdrtype; + FAR struct iob_s *frag; + FAR struct ipv6_hdr_s *ref; + FAR struct ipv6_fragment_extension_s *fraghdr; + struct iob_queue_s fragq = + { + NULL, NULL + }; + + /* Get the length of Unfragmentable Part of the original ipv6 packet, + * Get the offset and value of nextheader filed in the last extension + * header of the unfragmentable part. + */ + + unfraglen = ipv6_fragout_getunfraginfo(dev->d_iob, &hdroff, &hdrtype); + + /* Reconstruct I/O Buffer according to MTU, which will reserve + * the space for the L3 header + */ + + nfrags = ip_fragout_slice(dev->d_iob, PF_INET6, mtu, unfraglen, &fragq); + assert(nfrags > 1); + netdev_iob_clear(dev); + + ipid = ++g_ipv6id; + + /* Fill the L3 header into the reserved space */ + + for (iter = 0; iter < nfrags; iter++) + { + frag = iob_remove_queue(&fragq); + + if (iter == 0) + { + ref = (FAR struct ipv6_hdr_s *)(frag->io_data + frag->io_offset); + + /* Update the IPv6 header for the zero fragment */ + + ipv6_fragout_buildipv6header(ref, ref, unfraglen, + frag->io_pktlen - IPv6_HDRLEN, hdroff, NEXT_FRAGMENT_EH); + + /* Build the fragment header for the zero fragment */ + + fraghdr = (FAR struct ipv6_fragment_extension_s *) + (frag->io_data + frag->io_offset + unfraglen); + ipv6_fragout_buildipv6fragheader(fraghdr, hdrtype, + FRAGHDR_FRAG_MOREFRAGS, ipid); + } + else + { + uint16_t ipoff = offset - iter * (unfraglen + EXTHDR_FRAG_LEN); + + if (iter < nfrags - 1) + { + ipoff |= FRAGHDR_FRAG_MOREFRAGS; + } + + /* Refer to the zero fragment ipv6 header to construct the ipv6 + * header of non-zero fragment + */ + + ipv6_fragout_buildipv6header(ref, + (FAR struct ipv6_hdr_s *)(frag->io_data + frag->io_offset), + unfraglen, frag->io_pktlen - IPv6_HDRLEN, hdroff, + NEXT_FRAGMENT_EH); + + /* Build extension fragment header for non-zero fragment */ + + fraghdr = (FAR struct ipv6_fragment_extension_s *) + (frag->io_data + frag->io_offset + unfraglen); + ipv6_fragout_buildipv6fragheader(fraghdr, hdrtype, ipoff, ipid); + } + + /* Enqueue this fragment to dev->d_fragout */ + + if (iob_tryadd_queue(frag, &dev->d_fragout) < 0) + { + goto fail; + } + + offset += frag->io_pktlen; + } + +#ifdef CONFIG_NET_STATISTICS + g_netstats.ipv6.sent += nfrags - 1; +#endif + + netdev_txnotify_dev(dev); + + return OK; + +fail: + netdev_iob_release(dev); + iob_free_chain(frag); + iob_free_queue(&fragq); + iob_free_queue(&dev->d_fragout); + --g_ipv6id; + return -ENOMEM; +} + +#endif /* CONFIG_NET_IPv6 && CONFIG_NET_IPFRAG */ diff --git a/net/netdev/netdev_carrier.c b/net/netdev/netdev_carrier.c index 769ce3fd41b..37ac880c538 100644 --- a/net/netdev/netdev_carrier.c +++ b/net/netdev/netdev_carrier.c @@ -35,6 +35,7 @@ #include #include +#include "ipfrag/ipfrag.h" #include "netdev/netdev.h" #include "netlink/netlink.h" #include "arp/arp.h" @@ -92,6 +93,12 @@ int netdev_carrier_off(FAR struct net_driver_s *dev) dev->d_flags &= ~IFF_RUNNING; netlink_device_notify(dev); +#ifdef CONFIG_NET_IPFRAG + /* Clean up fragment data for this NIC (if any) */ + + ip_frag_stop(dev); +#endif + /* Notify clients that the network has been taken down */ devif_dev_event(dev, NETDEV_DOWN);