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-unsubscr...@nuttx.apache.org

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

Reply via email to