You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by GitBox <gi...@apache.org> on 2023/01/09 14:04:10 UTC

[GitHub] [nuttx] luojun1234 opened a new pull request, #8059: Support fragmentation and reassembly

luojun1234 opened a new pull request, #8059:
URL: https://github.com/apache/nuttx/pull/8059

   Signed-off-by: luojun1 <lu...@xiaomi.com>
   
   ## Summary
   Add fragment reassembly function
   
   ## Impact
   1. Support IPv4 and IPv6 networks;
   2. There is no limit to the number of sockets,  supports multiple network card environments;
   3. Support IOB offload feature, try to avoid and reduce data copying in the process of fragmentation and reassembly;
   4. Reassembly cache management. When the Buffer Size used by the cached fragmented data packets exceeds the configured threshold, part of the cache buffer will be released;
   5. Support fragment L3 forwarding.
   
   ## Testing
   1. ping and ping6 test(with a payload larger than MTU)
      ping -s 4000 -c 100 IPAddr(the ipv4 address of another nuttx simulator)
      ping6 -s 3000 -c 100 IPAddr(the ipv6 address of another nuttx simulator)
   3. Iperf2 test
   Simulator 1: iperf2 -u -s -i 1 -V
   Simulator 2: iperf2 -u -c fc00::2 -V -l 4000 -t 5 -i 1 -d
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] xiaoxiang781216 merged pull request #8059: Support fragmentation and reassembly

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 merged PR #8059:
URL: https://github.com/apache/nuttx/pull/8059


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] luojun1234 commented on a diff in pull request #8059: Support fragmentation and reassembly

Posted by GitBox <gi...@apache.org>.
luojun1234 commented on code in PR #8059:
URL: https://github.com/apache/nuttx/pull/8059#discussion_r1070183874


##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * 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 *

Review Comment:
   > can we avoid inline?
   
   The purpose of using inline is to save the time cost of function calls.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] luojun1234 commented on a diff in pull request #8059: Support fragmentation and reassembly

Posted by GitBox <gi...@apache.org>.
luojun1234 commented on code in PR #8059:
URL: https://github.com/apache/nuttx/pull/8059#discussion_r1065357238


##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1257 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * Name: ip_fragin_freelink
+ *
+ * Description:
+ *   Free the I/O buffer and ip_fraglink_s buffer at the head of a
+ *   ip_fraglink_s chain.
+ *
+ * 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:
+ *   Audit whether the fragment zero has been received or all fragments have
+ *   been received.
+ *
+ * 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)
+        {
+          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
+ *
+ * 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 && 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)
+                    {
+                      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
+ *
+ * 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
+ *
+ * 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.
+ *
+ * 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 IPID value, walk through it and try to
+   * find a node that has the same IPID 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)
+    {
+      node = (struct ip_fragsnode_s *)entry;
+
+      if (dev == node->dev && curfraglink->ipid <= node->ipid)
+        {
+          break;
+        }
+
+      entrylast = entry;
+      entry = sq_next(entry);
+    }
+
+  node = (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;
+
+      /* A ip_fragsnode_s must have a ip_fraglink_s because we allocate a new
+       * ip_fraglink_s when caching a new ip_fraglink_s with a new IPID
+       */
+
+      while (fraglink)
+        {
+          /* The fragment list is ordered by fragment offset value */
+
+          if (curfraglink->fragoff <= fraglink->fragoff)
+            {
+              break;

Review Comment:
   > Please include an Info/Warn message saying the that fragment is out of order
   
   Because these packets are received from the network, the sequence cannot be guaranteed due to network fluctuations,this is a normal phenomenon, so i don't think warning message is necessary.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] xiaoxiang781216 commented on a diff in pull request #8059: Support fragmentation and reassembly

Posted by GitBox <gi...@apache.org>.
xiaoxiang781216 commented on code in PR #8059:
URL: https://github.com/apache/nuttx/pull/8059#discussion_r1070221573


##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);

Review Comment:
   After https://github.com/apache/nuttx/pull/6965, all places should be changed to use mutex.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] acassis commented on a diff in pull request #8059: Support fragmentation and reassembly

Posted by GitBox <gi...@apache.org>.
acassis commented on code in PR #8059:
URL: https://github.com/apache/nuttx/pull/8059#discussion_r1064721602


##########
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 Reass time exceeded */

Review Comment:
   ```suggestion
   #define ICMP_EXC_FRAGTIME            1    /* Fragment Reassembly time exceeded */



##########
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 fragment 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 fragment timeout"

Review Comment:
   ```suggestion
   	int "IP fragmentation timeout"



##########
net/devif/devif_poll.c:
##########
@@ -616,6 +618,72 @@ 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.
+ *
+ * Assumptions:
+ *   This function is called from the MAC device driver with the network
+ *   locked.
+ *

Review Comment:
   Please include Parameters and return info



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1257 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * Name: ip_fragin_freelink
+ *
+ * Description:
+ *   Free the I/O buffer and ip_fraglink_s buffer at the head of a
+ *   ip_fraglink_s chain.
+ *
+ * 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:
+ *   Audit whether the fragment zero has been received or all fragments have
+ *   been received.
+ *
+ * Returned Value:
+ *   None
+ *

Review Comment:
   Ditto



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1257 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * Name: ip_fragin_freelink
+ *
+ * Description:
+ *   Free the I/O buffer and ip_fraglink_s buffer at the head of a
+ *   ip_fraglink_s chain.
+ *
+ * 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:
+ *   Audit whether the fragment zero has been received or all fragments have
+ *   been received.
+ *
+ * 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)
+        {
+          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
+ *
+ * Returned Value:
+ *   none
+ *

Review Comment:
   Ditto



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1257 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * Name: ip_fragin_freelink
+ *
+ * Description:
+ *   Free the I/O buffer and ip_fraglink_s buffer at the head of a
+ *   ip_fraglink_s chain.
+ *
+ * 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:
+ *   Audit whether the fragment zero has been received or all fragments have
+ *   been received.
+ *
+ * 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)
+        {
+          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
+ *
+ * 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 && 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)
+                    {
+                      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
+ *
+ * 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
+ *
+ * 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.
+ *
+ * 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 IPID value, walk through it and try to
+   * find a node that has the same IPID 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)
+    {
+      node = (struct ip_fragsnode_s *)entry;
+
+      if (dev == node->dev && curfraglink->ipid <= node->ipid)
+        {
+          break;
+        }
+
+      entrylast = entry;
+      entry = sq_next(entry);
+    }
+
+  node = (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;
+
+      /* A ip_fragsnode_s must have a ip_fraglink_s because we allocate a new
+       * ip_fraglink_s when caching a new ip_fraglink_s with a new IPID
+       */
+
+      while (fraglink)
+        {
+          /* The fragment list is ordered by fragment offset value */
+
+          if (curfraglink->fragoff <= fraglink->fragoff)
+            {
+              break;

Review Comment:
   Please include an Info/Warn message saying the that fragment is out of order



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1257 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * Name: ip_fragin_freelink
+ *
+ * Description:
+ *   Free the I/O buffer and ip_fraglink_s buffer at the head of a
+ *   ip_fraglink_s chain.
+ *
+ * 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:
+ *   Audit whether the fragment zero has been received or all fragments have
+ *   been received.
+ *
+ * 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)
+        {
+          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
+ *
+ * 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 && 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)
+                    {
+                      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
+ *
+ * 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
+ *
+ * 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.
+ *
+ * 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 IPID value, walk through it and try to
+   * find a node that has the same IPID 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)
+    {
+      node = (struct ip_fragsnode_s *)entry;
+
+      if (dev == node->dev && curfraglink->ipid <= node->ipid)
+        {
+          break;
+        }
+
+      entrylast = entry;
+      entry = sq_next(entry);
+    }
+
+  node = (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;
+
+      /* A ip_fragsnode_s must have a ip_fraglink_s because we allocate a new
+       * ip_fraglink_s when caching a new ip_fraglink_s with a new IPID
+       */
+
+      while (fraglink)
+        {
+          /* 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 IPID 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) >> 3 << 3) + 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);

Review Comment:
   ```suggestion
                         reorg->io_pktlen, false) < 0, allocfail);



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1257 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * Name: ip_fragin_freelink
+ *
+ * Description:
+ *   Free the I/O buffer and ip_fraglink_s buffer at the head of a
+ *   ip_fraglink_s chain.
+ *
+ * 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:
+ *   Audit whether the fragment zero has been received or all fragments have
+ *   been received.
+ *
+ * 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)
+        {
+          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
+ *
+ * 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 && 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)
+                    {
+                      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
+ *
+ * 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
+ *
+ * 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.
+ *
+ * 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 IPID value, walk through it and try to
+   * find a node that has the same IPID 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)
+    {
+      node = (struct ip_fragsnode_s *)entry;
+
+      if (dev == node->dev && curfraglink->ipid <= node->ipid)
+        {
+          break;
+        }
+
+      entrylast = entry;
+      entry = sq_next(entry);
+    }
+
+  node = (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;
+
+      /* A ip_fragsnode_s must have a ip_fraglink_s because we allocate a new
+       * ip_fraglink_s when caching a new ip_fraglink_s with a new IPID
+       */
+
+      while (fraglink)
+        {
+          /* 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 IPID 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) >> 3 << 3) + 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 >> 3 << 3;

Review Comment:
   Ditto: Could it work replacing ">> 3 << 3" with "((mtu - IPv4_HDRLEN) & ~0x7)" ?



##########
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 fragment support"

Review Comment:
   ```suggestion
   	bool "IP fragmentation support"



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1257 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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:

Review Comment:
   Ditto



##########
include/nuttx/net/ipv6ext.h:
##########
@@ -217,4 +225,18 @@ struct ipv6_router_alert_s
   uint8_t pad[4];     /* Pad to a multiple of 8 bytes */
 };
 
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ipv6_exthdr
+ *
+ * Description:
+ *   Return true if the next header value is an IPv6 extension header.
+ *

Review Comment:
   Please incluse Parameter and Return information



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1257 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * Name: ip_fragin_freelink
+ *
+ * Description:
+ *   Free the I/O buffer and ip_fraglink_s buffer at the head of a
+ *   ip_fraglink_s chain.
+ *
+ * Returned Value:

Review Comment:
   Please include Parameter info



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1257 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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:

Review Comment:
   Please include Parameter info



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1257 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * Name: ip_fragin_freelink
+ *
+ * Description:
+ *   Free the I/O buffer and ip_fraglink_s buffer at the head of a
+ *   ip_fraglink_s chain.
+ *
+ * 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:
+ *   Audit whether the fragment zero has been received or all fragments have
+ *   been received.
+ *
+ * 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)
+        {
+          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
+ *
+ * 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 && 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)
+                    {
+                      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
+ *
+ * Returned Value:
+ *   The pointer to I/O buffer
+ *

Review Comment:
   Ditto



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1257 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * Name: ip_fragin_freelink
+ *
+ * Description:
+ *   Free the I/O buffer and ip_fraglink_s buffer at the head of a
+ *   ip_fraglink_s chain.
+ *
+ * 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:
+ *   Audit whether the fragment zero has been received or all fragments have
+ *   been received.
+ *
+ * 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)
+        {
+          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
+ *
+ * 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 && 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)
+                    {
+                      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
+ *
+ * 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
+ *
+ * Returned Value:
+ *   I/O buffer count of this node
+ *

Review Comment:
   Ditto



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1257 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * Name: ip_fragin_freelink
+ *
+ * Description:
+ *   Free the I/O buffer and ip_fraglink_s buffer at the head of a
+ *   ip_fraglink_s chain.
+ *
+ * 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:
+ *   Audit whether the fragment zero has been received or all fragments have
+ *   been received.
+ *
+ * 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)
+        {
+          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
+ *
+ * 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 && 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)
+                    {
+                      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
+ *
+ * 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
+ *
+ * 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.
+ *
+ * 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 IPID value, walk through it and try to
+   * find a node that has the same IPID 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)
+    {
+      node = (struct ip_fragsnode_s *)entry;
+
+      if (dev == node->dev && curfraglink->ipid <= node->ipid)
+        {
+          break;
+        }
+
+      entrylast = entry;
+      entry = sq_next(entry);
+    }
+
+  node = (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;
+
+      /* A ip_fragsnode_s must have a ip_fraglink_s because we allocate a new

Review Comment:
   ```suggestion
         /* An ip_fragsnode_s must have an ip_fraglink_s because we allocate a new



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1257 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * Name: ip_fragin_freelink
+ *
+ * Description:
+ *   Free the I/O buffer and ip_fraglink_s buffer at the head of a
+ *   ip_fraglink_s chain.
+ *
+ * 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:
+ *   Audit whether the fragment zero has been received or all fragments have
+ *   been received.
+ *
+ * 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)
+        {
+          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
+ *
+ * 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 && 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)
+                    {
+                      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
+ *
+ * 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
+ *
+ * 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.
+ *
+ * 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 IPID value, walk through it and try to
+   * find a node that has the same IPID 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)
+    {
+      node = (struct ip_fragsnode_s *)entry;
+
+      if (dev == node->dev && curfraglink->ipid <= node->ipid)
+        {
+          break;
+        }
+
+      entrylast = entry;
+      entry = sq_next(entry);
+    }
+
+  node = (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;
+
+      /* A ip_fragsnode_s must have a ip_fraglink_s because we allocate a new
+       * ip_fraglink_s when caching a new ip_fraglink_s with a new IPID
+       */
+
+      while (fraglink)
+        {
+          /* 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 IPID fragment, malloc a new node and insert it into the

Review Comment:
   ```suggestion
         /* It's a new IP ID fragment, malloc a new node and insert it into the



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1257 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * Name: ip_fragin_freelink
+ *
+ * Description:
+ *   Free the I/O buffer and ip_fraglink_s buffer at the head of a
+ *   ip_fraglink_s chain.
+ *
+ * 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:
+ *   Audit whether the fragment zero has been received or all fragments have
+ *   been received.
+ *
+ * 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)
+        {
+          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
+ *
+ * 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 && 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)
+                    {
+                      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
+ *
+ * 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
+ *
+ * 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.
+ *
+ * Returned Value:
+ *   Whether queue is empty before enqueue the new node
+ *

Review Comment:
   Please include dev and curfraglink Parameters info



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1257 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * Name: ip_fragin_freelink
+ *
+ * Description:
+ *   Free the I/O buffer and ip_fraglink_s buffer at the head of a
+ *   ip_fraglink_s chain.
+ *
+ * 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:
+ *   Audit whether the fragment zero has been received or all fragments have
+ *   been received.
+ *
+ * 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)
+        {
+          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
+ *
+ * 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 && 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)
+                    {
+                      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
+ *
+ * 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
+ *
+ * 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.
+ *
+ * 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 IPID value, walk through it and try to
+   * find a node that has the same IPID 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)
+    {
+      node = (struct ip_fragsnode_s *)entry;
+
+      if (dev == node->dev && curfraglink->ipid <= node->ipid)
+        {
+          break;
+        }
+
+      entrylast = entry;
+      entry = sq_next(entry);
+    }
+
+  node = (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;
+
+      /* A ip_fragsnode_s must have a ip_fraglink_s because we allocate a new
+       * ip_fraglink_s when caching a new ip_fraglink_s with a new IPID
+       */
+
+      while (fraglink)
+        {
+          /* 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 IPID 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) >> 3 << 3) + IPv4_HDRLEN;

Review Comment:
   Could it work replacing ">> 3 << 3" with "((mtu - IPv4_HDRLEN) & ~0x7)" ?



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1257 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * Name: ip_fragin_freelink
+ *
+ * Description:
+ *   Free the I/O buffer and ip_fraglink_s buffer at the head of a
+ *   ip_fraglink_s chain.
+ *
+ * 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:
+ *   Audit whether the fragment zero has been received or all fragments have
+ *   been received.
+ *
+ * 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)
+        {
+          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
+ *
+ * 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 && 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)
+                    {
+                      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
+ *
+ * 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
+ *
+ * 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.
+ *
+ * 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 IPID value, walk through it and try to
+   * find a node that has the same IPID 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)
+    {
+      node = (struct ip_fragsnode_s *)entry;
+
+      if (dev == node->dev && curfraglink->ipid <= node->ipid)
+        {
+          break;
+        }
+
+      entrylast = entry;
+      entry = sq_next(entry);
+    }
+
+  node = (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;
+
+      /* A ip_fragsnode_s must have a ip_fraglink_s because we allocate a new
+       * ip_fraglink_s when caching a new ip_fraglink_s with a new IPID
+       */
+
+      while (fraglink)
+        {
+          /* 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 IPID 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) >> 3 << 3) + 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 >> 3 << 3;
+
+      /* 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)
+    {
+      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);
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  entry = sq_peek(&g_assemblyhead_ipid);
+
+  /* Drop those unassembled incoming fragments belonging to this NIC */
+
+  while (entry)
+    {
+      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)
+                {
+                  fraglink = ip_fragin_freelink(fraglink);
+                }
+            }
+
+          ip_frag_remnode(node);
+          kmm_free(entry);
+        }
+
+      entry = entrynext;
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+
+  /* 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;
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  entry = sq_peek(&g_assemblyhead_ipid);
+
+  /* Drop all unassembled incoming fragments */
+
+  while (entry)
+    {
+      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)
+            {
+              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;
+
+  nxsem_post(&g_ipfrag_mutex);
+
+  /* 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
+ *

Review Comment:
   Please include Return value info



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] pkarashchenko commented on a diff in pull request #8059: Support fragmentation and reassembly

Posted by GitBox <gi...@apache.org>.
pkarashchenko commented on code in PR #8059:
URL: https://github.com/apache/nuttx/pull/8059#discussion_r1069229971


##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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)/ \

Review Comment:
   ```suggestion
   #define IOBUF_CNT(ptr)    (((ptr)->io_pktlen + CONFIG_IOB_BUFSIZE - 1)/ \
   ```



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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);  \

Review Comment:
   ```suggestion
     } while (0);
   ```



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)

Review Comment:
   Optional
   ```suggestion
     while (entry != NULL)
   ```



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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;

Review Comment:
   either align all or remove extra spaces



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * 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)

Review Comment:
   Optional
   ```suggestion
     if ((fragsnode->verifyflag & IP_FRAGVERIFY_RECVDTAILFRAG) != 0)
   ```



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * 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)

Review Comment:
   Optional
   ```suggestion
         while (entry != NULL)
   ```



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * 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)
+        {
+          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 && 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)
+                    {
+                      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;

Review Comment:
   ```suggestion
     empty = (entry == NULL);
   ```



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * 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)
+        {
+          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 && 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)
+                    {
+                      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)

Review Comment:
   Optional
   ```suggestion
     while (entry != NULL)
   ```



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);

Review Comment:
   can we use `nxmutex`?



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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;  \
+  } \

Review Comment:
   ```suggestion
     }
   ```



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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);

Review Comment:
   ```suggestion
         wd_start(&g_wdfragtimeout, delay, ip_fragin_timerout_expiry,
                  (wdparm_t)NULL);
   ```



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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)

Review Comment:
   Optional
   ```suggestion
                 while (fraglink != NULL)
   ```



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)

Review Comment:
   Optional
   ```suggestion
             if ((node->verifyflag & IP_FRAGVERIFY_RECVDZEROFRAG) != 0)
   ```



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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; \

Review Comment:
   ```suggestion
       (iob)->io_offset = (off); \
       (iob)->io_len    = (len); \
       (iob)->io_pktlen = (len); \
   ```



##########
net/devif/devif_poll.c:
##########
@@ -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 inline int devif_poll_ipfrag(FAR struct net_driver_s *dev,

Review Comment:
   can we avoid inline?



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * 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)
+        {
+          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 && 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)
+                    {
+                      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)
+    {
+      node = (struct ip_fragsnode_s *)entry;
+
+      if (dev == node->dev && curfraglink->ipid <= node->ipid)
+        {
+          break;
+        }
+
+      entrylast = entry;
+      entry = sq_next(entry);
+    }
+
+  node = (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)
+        {
+          /* 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)

Review Comment:
   Optional
   ```suggestion
     while (orig != NULL)
   ```



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * 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 *

Review Comment:
   can we avoid inline?



##########
net/ipfrag/ipfrag.h:
##########
@@ -0,0 +1,387 @@
+/****************************************************************************
+ * 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_H
+#define __NET_IPFRAG_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <assert.h>
+
+#include <nuttx/queue.h>
+#include <nuttx/mm/iob.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/net.h>
+#include <nuttx/net/netdev.h>
+
+#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 sem_t g_ipfrag_mutex;
+
+/****************************************************************************
+ * 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_H */

Review Comment:
   ```suggestion
   #endif /* __NET_IPFRAG_IPFRAG_H */
   ```



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * 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)
+        {
+          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 && 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)
+                    {
+                      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)
+    {
+      node = (struct ip_fragsnode_s *)entry;
+
+      if (dev == node->dev && curfraglink->ipid <= node->ipid)
+        {
+          break;
+        }
+
+      entrylast = entry;
+      entry = sq_next(entry);
+    }
+
+  node = (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)
+        {
+          /* 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)
+    {
+      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);
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  entry = sq_peek(&g_assemblyhead_ipid);
+
+  /* Drop those unassembled incoming fragments belonging to this NIC */
+
+  while (entry)
+    {
+      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)
+                {
+                  fraglink = ip_fragin_freelink(fraglink);
+                }
+            }
+
+          ip_frag_remnode(node);
+          kmm_free(entry);
+        }
+
+      entry = entrynext;
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+
+  /* 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;
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  entry = sq_peek(&g_assemblyhead_ipid);
+
+  /* Drop all unassembled incoming fragments */
+
+  while (entry)
+    {
+      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)

Review Comment:
   Optional
   ```suggestion
             while (fraglink != NULL)
   ```



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * 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)
+        {
+          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 && cleancnt > 0)

Review Comment:
   Optional
   ```suggestion
         while (entry != NULL && cleancnt > 0)
   ```



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * 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)
+        {
+          if (entry->morefrags)

Review Comment:
   Optional
   ```suggestion
             if (entry->morefrags != NULL)
   ```



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * 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)
+        {
+          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;

Review Comment:
   either align all or remove extra spaces



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * 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)
+        {
+          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 && 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)
+                    {
+                      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)
+    {
+      node = (struct ip_fragsnode_s *)entry;
+
+      if (dev == node->dev && curfraglink->ipid <= node->ipid)
+        {
+          break;
+        }
+
+      entrylast = entry;
+      entry = sq_next(entry);
+    }
+
+  node = (struct ip_fragsnode_s *)entry;

Review Comment:
   ```suggestion
     node = (FAR struct ip_fragsnode_s *)entry;
   ```



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * 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)
+        {
+          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 && 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)

Review Comment:
   Optional
   ```suggestion
                     while (fraglink != NULL)
   ```



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * 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)
+        {
+          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 && 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)
+                    {
+                      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)
+    {
+      node = (struct ip_fragsnode_s *)entry;
+
+      if (dev == node->dev && curfraglink->ipid <= node->ipid)
+        {
+          break;
+        }
+
+      entrylast = entry;
+      entry = sq_next(entry);
+    }
+
+  node = (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)

Review Comment:
   Optional
   ```suggestion
         while (fraglink != NULL)
   ```



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * 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)
+        {
+          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 && 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)
+                    {
+                      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)
+    {
+      node = (struct ip_fragsnode_s *)entry;

Review Comment:
   ```suggestion
         node = (FAR struct ip_fragsnode_s *)entry;
   ```



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * 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)
+        {
+          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 && 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)
+                    {
+                      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)
+    {
+      node = (struct ip_fragsnode_s *)entry;
+
+      if (dev == node->dev && curfraglink->ipid <= node->ipid)
+        {
+          break;
+        }
+
+      entrylast = entry;
+      entry = sq_next(entry);
+    }
+
+  node = (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)
+        {
+          /* 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

Review Comment:
   ```suggestion
    *   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
   ```



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * 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)
+        {
+          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 && 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)
+                    {
+                      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;

Review Comment:
   either align all or remove extra spaces



##########
net/ipfrag/ipfrag.h:
##########
@@ -0,0 +1,387 @@
+/****************************************************************************
+ * 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_H
+#define __NET_IPFRAG_H

Review Comment:
   ```suggestion
   #ifndef __NET_IPFRAG_IPFRAG_H
   #define __NET_IPFRAG_IPFRAG_H
   ```



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * 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)
+        {
+          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 && 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)
+                    {
+                      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)
+    {
+      node = (struct ip_fragsnode_s *)entry;
+
+      if (dev == node->dev && curfraglink->ipid <= node->ipid)
+        {
+          break;
+        }
+
+      entrylast = entry;
+      entry = sq_next(entry);
+    }
+
+  node = (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)
+        {
+          /* 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;

Review Comment:
   either align all or remove extra spaces



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * 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)
+        {
+          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 && 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)
+                    {
+                      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)
+    {
+      node = (struct ip_fragsnode_s *)entry;
+
+      if (dev == node->dev && curfraglink->ipid <= node->ipid)
+        {
+          break;
+        }
+
+      entrylast = entry;
+      entry = sq_next(entry);
+    }
+
+  node = (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)
+        {
+          /* 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)
+    {
+      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);
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  entry = sq_peek(&g_assemblyhead_ipid);
+
+  /* Drop those unassembled incoming fragments belonging to this NIC */
+
+  while (entry)
+    {
+      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)

Review Comment:
   Optional
   ```suggestion
                 while (fraglink != NULL)
   ```



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * 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)
+        {
+          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 && 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)
+                    {
+                      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)
+    {
+      node = (struct ip_fragsnode_s *)entry;
+
+      if (dev == node->dev && curfraglink->ipid <= node->ipid)
+        {
+          break;
+        }
+
+      entrylast = entry;
+      entry = sq_next(entry);
+    }
+
+  node = (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)
+        {
+          /* 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)
+    {
+      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);
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  entry = sq_peek(&g_assemblyhead_ipid);
+
+  /* Drop those unassembled incoming fragments belonging to this NIC */
+
+  while (entry)

Review Comment:
   Optional
   ```suggestion
     while (entry != NULL)
   ```



##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);
+
+/****************************************************************************
+ * 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");
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  /* Walk through the list, check the timetout and calculate the next timer
+   * interval
+   */
+
+  entry = sq_peek(&g_assemblyhead_time);
+  while (entry)
+    {
+      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)
+            {
+              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");
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+}
+
+/****************************************************************************
+ * 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)
+        {
+          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 && 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)
+                    {
+                      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)
+    {
+      node = (struct ip_fragsnode_s *)entry;
+
+      if (dev == node->dev && curfraglink->ipid <= node->ipid)
+        {
+          break;
+        }
+
+      entrylast = entry;
+      entry = sq_next(entry);
+    }
+
+  node = (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)
+        {
+          /* 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)
+    {
+      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);
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  entry = sq_peek(&g_assemblyhead_ipid);
+
+  /* Drop those unassembled incoming fragments belonging to this NIC */
+
+  while (entry)
+    {
+      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)
+                {
+                  fraglink = ip_fragin_freelink(fraglink);
+                }
+            }
+
+          ip_frag_remnode(node);
+          kmm_free(entry);
+        }
+
+      entry = entrynext;
+    }
+
+  nxsem_post(&g_ipfrag_mutex);
+
+  /* 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;
+
+  nxsem_wait_uninterruptible(&g_ipfrag_mutex);
+
+  entry = sq_peek(&g_assemblyhead_ipid);
+
+  /* Drop all unassembled incoming fragments */
+
+  while (entry)

Review Comment:
   Optional
   ```suggestion
     while (entry != NULL)
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


[GitHub] [nuttx] luojun1234 commented on a diff in pull request #8059: Support fragmentation and reassembly

Posted by GitBox <gi...@apache.org>.
luojun1234 commented on code in PR #8059:
URL: https://github.com/apache/nuttx/pull/8059#discussion_r1070184308


##########
net/ipfrag/ipfrag.c:
##########
@@ -0,0 +1,1284 @@
+/****************************************************************************
+ * 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 <nuttx/config.h>
+#if (defined(CONFIG_NET_IPv4) || defined(CONFIG_NET_IPv6)) &&   \
+    defined(CONFIG_NET_IPFRAG)
+
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <debug.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <net/if.h>
+
+#include <nuttx/nuttx.h>
+#include <nuttx/wqueue.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/semaphore.h>
+#include <nuttx/net/netconfig.h>
+#include <nuttx/net/netdev.h>
+#include <nuttx/net/netstats.h>
+#include <nuttx/net/ip.h>
+#include <nuttx/net/ipv6ext.h>
+
+#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.
+ */
+
+sem_t                 g_ipfrag_mutex = SEM_INITIALIZER(1);

Review Comment:
   Many places in the nuttx network protocol stack use semaphores instead of mutex, must it be changed to mutex here?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: commits-unsubscribe@nuttx.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org