]> git.sur5r.net Git - freertos/blobdiff - FreeRTOS/Demo/lwIP_MCF5235_GCC/lwip/src/core/ipv4/ip_frag.c
Add FreeRTOS-Plus directory.
[freertos] / FreeRTOS / Demo / lwIP_MCF5235_GCC / lwip / src / core / ipv4 / ip_frag.c
diff --git a/FreeRTOS/Demo/lwIP_MCF5235_GCC/lwip/src/core/ipv4/ip_frag.c b/FreeRTOS/Demo/lwIP_MCF5235_GCC/lwip/src/core/ipv4/ip_frag.c
new file mode 100644 (file)
index 0000000..a233674
--- /dev/null
@@ -0,0 +1,366 @@
+/* @file\r
+ * \r
+ * This is the IP packet segmentation and reassembly implementation.\r
+ *\r
+ */\r
+\r
+/*\r
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.\r
+ * All rights reserved. \r
+ * \r
+ * Redistribution and use in source and binary forms, with or without modification, \r
+ * are permitted provided that the following conditions are met:\r
+ *\r
+ * 1. Redistributions of source code must retain the above copyright notice,\r
+ *    this list of conditions and the following disclaimer.\r
+ * 2. Redistributions in binary form must reproduce the above copyright notice,\r
+ *    this list of conditions and the following disclaimer in the documentation\r
+ *    and/or other materials provided with the distribution.\r
+ * 3. The name of the author may not be used to endorse or promote products\r
+ *    derived from this software without specific prior written permission. \r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED \r
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF \r
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT \r
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, \r
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT \r
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS \r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN \r
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING \r
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY \r
+ * OF SUCH DAMAGE.\r
+ *\r
+ * This file is part of the lwIP TCP/IP stack.\r
+ * \r
+ * Author: Jani Monoses <jani@iv.ro> \r
+ * original reassembly code by Adam Dunkels <adam@sics.se>\r
+ * \r
+ */\r
+\r
+#include <string.h>\r
+\r
+#include "lwip/opt.h"\r
+/* #include "lwip/sys.h" */\r
+#include "lwip/ip.h"\r
+#include "lwip/ip_frag.h"\r
+#include "lwip/netif.h"\r
+#include "lwip/stats.h"\r
+\r
+\r
+/*\r
+ * Copy len bytes from offset in pbuf to buffer \r
+ *\r
+ * helper used by both ip_reass and ip_frag\r
+ */\r
+static struct pbuf *\r
+copy_from_pbuf(struct pbuf *p, u16_t * offset,\r
+           u8_t * buffer, u16_t len)\r
+{\r
+  u16_t l;\r
+\r
+  p->payload = (u8_t *)p->payload + *offset;\r
+  p->len -= *offset;\r
+  while (len) {\r
+    l = len < p->len ? len : p->len;\r
+    memcpy(buffer, p->payload, l);\r
+    buffer += l;\r
+    len -= l;\r
+    if (len)\r
+      p = p->next;\r
+    else\r
+      *offset = l;\r
+  }\r
+  return p;\r
+}\r
+\r
+#define IP_REASS_BUFSIZE 5760\r
+#define IP_REASS_MAXAGE 30\r
+#define IP_REASS_TMO 1000\r
+\r
+static u8_t ip_reassbuf[IP_HLEN + IP_REASS_BUFSIZE];\r
+static u8_t ip_reassbitmap[IP_REASS_BUFSIZE / (8 * 8) + 1];\r
+static const u8_t bitmap_bits[8] = { 0xff, 0x7f, 0x3f, 0x1f,\r
+  0x0f, 0x07, 0x03, 0x01\r
+};\r
+static u16_t ip_reasslen;\r
+static u8_t ip_reassflags;\r
+#define IP_REASS_FLAG_LASTFRAG 0x01\r
+\r
+static u8_t ip_reasstmr;\r
+\r
+/**\r
+ * Reassembly timer base function\r
+ * for both NO_SYS == 0 and 1 (!).\r
+ *\r
+ * Should be called every 1000 msec.\r
+ */\r
+void\r
+ip_reass_tmr(void)\r
+{\r
+  if (ip_reasstmr > 0) {\r
+    ip_reasstmr--;\r
+  }\r
+}\r
+\r
+/**\r
+ * Reassembles incoming IP fragments into an IP datagram.\r
+ *\r
+ * @param p points to a pbuf chain of the fragment\r
+ * @return NULL if reassembly is incomplete, ? otherwise\r
+ */\r
+struct pbuf *\r
+ip_reass(struct pbuf *p)\r
+{\r
+  struct pbuf *q;\r
+  struct ip_hdr *fraghdr, *iphdr;\r
+  u16_t offset, len;\r
+  u16_t i;\r
+\r
+  IPFRAG_STATS_INC(ip_frag.recv);\r
+\r
+  iphdr = (struct ip_hdr *) ip_reassbuf;\r
+  fraghdr = (struct ip_hdr *) p->payload;\r
+  /* If ip_reasstmr is zero, no packet is present in the buffer, so we\r
+     write the IP header of the fragment into the reassembly\r
+     buffer. The timer is updated with the maximum age. */\r
+  if (ip_reasstmr == 0) {\r
+    LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: new packet\n"));\r
+    memcpy(iphdr, fraghdr, IP_HLEN);\r
+    ip_reasstmr = IP_REASS_MAXAGE;\r
+    ip_reassflags = 0;\r
+    /* Clear the bitmap. */\r
+    memset(ip_reassbitmap, 0, sizeof(ip_reassbitmap));\r
+  }\r
+\r
+  /* Check if the incoming fragment matches the one currently present\r
+     in the reasembly buffer. If so, we proceed with copying the\r
+     fragment into the buffer. */\r
+  if (ip_addr_cmp(&iphdr->src, &fraghdr->src) &&\r
+      ip_addr_cmp(&iphdr->dest, &fraghdr->dest) &&\r
+      IPH_ID(iphdr) == IPH_ID(fraghdr)) {\r
+    LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n",\r
+      ntohs(IPH_ID(fraghdr))));\r
+    IPFRAG_STATS_INC(ip_frag.cachehit);\r
+    /* Find out the offset in the reassembly buffer where we should\r
+       copy the fragment. */\r
+    len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;\r
+    offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;\r
+\r
+    /* If the offset or the offset + fragment length overflows the\r
+       reassembly buffer, we discard the entire packet. */\r
+    if (offset > IP_REASS_BUFSIZE || offset + len > IP_REASS_BUFSIZE) {\r
+      LWIP_DEBUGF(IP_REASS_DEBUG,\r
+       ("ip_reass: fragment outside of buffer (%"S16_F":%"S16_F"/%"S16_F").\n", offset,\r
+        offset + len, IP_REASS_BUFSIZE));\r
+      ip_reasstmr = 0;\r
+      goto nullreturn;\r
+    }\r
+\r
+    /* Copy the fragment into the reassembly buffer, at the right\r
+       offset. */\r
+    LWIP_DEBUGF(IP_REASS_DEBUG,\r
+     ("ip_reass: copying with offset %"S16_F" into %"S16_F":%"S16_F"\n", offset,\r
+      IP_HLEN + offset, IP_HLEN + offset + len));\r
+    i = IPH_HL(fraghdr) * 4;\r
+    copy_from_pbuf(p, &i, &ip_reassbuf[IP_HLEN + offset], len);\r
+\r
+    /* Update the bitmap. */\r
+    if (offset / (8 * 8) == (offset + len) / (8 * 8)) {\r
+      LWIP_DEBUGF(IP_REASS_DEBUG,\r
+       ("ip_reass: updating single byte in bitmap.\n"));\r
+      /* If the two endpoints are in the same byte, we only update that byte. */\r
+      LWIP_ASSERT("offset / (8 * 8) < sizeof(ip_reassbitmap)",\r
+                   offset / (8 * 8) < sizeof(ip_reassbitmap));\r
+      ip_reassbitmap[offset / (8 * 8)] |=\r
+        bitmap_bits[(offset / 8) & 7] &\r
+        ~bitmap_bits[((offset + len) / 8) & 7];\r
+    } else {\r
+      /* If the two endpoints are in different bytes, we update the\r
+         bytes in the endpoints and fill the stuff inbetween with\r
+         0xff. */\r
+      LWIP_ASSERT("offset / (8 * 8) < sizeof(ip_reassbitmap)",\r
+                   offset / (8 * 8) < sizeof(ip_reassbitmap));\r
+      ip_reassbitmap[offset / (8 * 8)] |= bitmap_bits[(offset / 8) & 7];\r
+      LWIP_DEBUGF(IP_REASS_DEBUG,\r
+       ("ip_reass: updating many bytes in bitmap (%"S16_F":%"S16_F").\n",\r
+        1 + offset / (8 * 8), (offset + len) / (8 * 8)));\r
+      for (i = 1 + offset / (8 * 8); i < (offset + len) / (8 * 8); ++i) {\r
+        ip_reassbitmap[i] = 0xff;\r
+      }\r
+      LWIP_ASSERT("(offset + len) / (8 * 8) < sizeof(ip_reassbitmap)",\r
+                   (offset + len) / (8 * 8) < sizeof(ip_reassbitmap));\r
+      ip_reassbitmap[(offset + len) / (8 * 8)] |=\r
+        ~bitmap_bits[((offset + len) / 8) & 7];\r
+    }\r
+\r
+    /* If this fragment has the More Fragments flag set to zero, we\r
+       know that this is the last fragment, so we can calculate the\r
+       size of the entire packet. We also set the\r
+       IP_REASS_FLAG_LASTFRAG flag to indicate that we have received\r
+       the final fragment. */\r
+\r
+    if ((ntohs(IPH_OFFSET(fraghdr)) & IP_MF) == 0) {\r
+      ip_reassflags |= IP_REASS_FLAG_LASTFRAG;\r
+      ip_reasslen = offset + len;\r
+      LWIP_DEBUGF(IP_REASS_DEBUG,\r
+       ("ip_reass: last fragment seen, total len %"S16_F"\n",\r
+        ip_reasslen));\r
+    }\r
+\r
+    /* Finally, we check if we have a full packet in the buffer. We do\r
+       this by checking if we have the last fragment and if all bits\r
+       in the bitmap are set. */\r
+    if (ip_reassflags & IP_REASS_FLAG_LASTFRAG) {\r
+      /* Check all bytes up to and including all but the last byte in\r
+         the bitmap. */\r
+      LWIP_ASSERT("ip_reasslen / (8 * 8) - 1 < sizeof(ip_reassbitmap)",\r
+                   ip_reasslen / (8 * 8) - 1 < sizeof(ip_reassbitmap));\r
+      for (i = 0; i < ip_reasslen / (8 * 8) - 1; ++i) {\r
+        if (ip_reassbitmap[i] != 0xff) {\r
+          LWIP_DEBUGF(IP_REASS_DEBUG,\r
+           ("ip_reass: last fragment seen, bitmap %"S16_F"/%"S16_F" failed (%"X16_F")\n",\r
+            i, ip_reasslen / (8 * 8) - 1, ip_reassbitmap[i]));\r
+          goto nullreturn;\r
+        }\r
+      }\r
+      /* Check the last byte in the bitmap. It should contain just the\r
+         right amount of bits. */\r
+      LWIP_ASSERT("ip_reasslen / (8 * 8) < sizeof(ip_reassbitmap)",\r
+                   ip_reasslen / (8 * 8) < sizeof(ip_reassbitmap));\r
+      if (ip_reassbitmap[ip_reasslen / (8 * 8)] !=\r
+        (u8_t) ~ bitmap_bits[ip_reasslen / 8 & 7]) {\r
+         LWIP_DEBUGF(IP_REASS_DEBUG,\r
+          ("ip_reass: last fragment seen, bitmap %"S16_F" didn't contain %"X16_F" (%"X16_F")\n",\r
+        ip_reasslen / (8 * 8), ~bitmap_bits[ip_reasslen / 8 & 7],\r
+        ip_reassbitmap[ip_reasslen / (8 * 8)]));\r
+        goto nullreturn;\r
+      }\r
+\r
+      /* Pretend to be a "normal" (i.e., not fragmented) IP packet\r
+         from now on. */\r
+      ip_reasslen += IP_HLEN;\r
+\r
+      IPH_LEN_SET(iphdr, htons(ip_reasslen));\r
+      IPH_OFFSET_SET(iphdr, 0);\r
+      IPH_CHKSUM_SET(iphdr, 0);\r
+      IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));\r
+\r
+      /* If we have come this far, we have a full packet in the\r
+         buffer, so we allocate a pbuf and copy the packet into it. We\r
+         also reset the timer. */\r
+      ip_reasstmr = 0;\r
+      pbuf_free(p);\r
+      p = pbuf_alloc(PBUF_LINK, ip_reasslen, PBUF_POOL);\r
+      if (p != NULL) {\r
+        i = 0;\r
+        for (q = p; q != NULL; q = q->next) {\r
+          /* Copy enough bytes to fill this pbuf in the chain. The\r
+             available data in the pbuf is given by the q->len variable. */\r
+          LWIP_DEBUGF(IP_REASS_DEBUG,\r
+           ("ip_reass: memcpy from %p (%"S16_F") to %p, %"S16_F" bytes\n",\r
+            (void *)&ip_reassbuf[i], i, q->payload,\r
+            q->len > ip_reasslen - i ? ip_reasslen - i : q->len));\r
+          memcpy(q->payload, &ip_reassbuf[i],\r
+            q->len > ip_reasslen - i ? ip_reasslen - i : q->len);\r
+          i += q->len;\r
+        }\r
+        IPFRAG_STATS_INC(ip_frag.fw);\r
+      } else {\r
+        IPFRAG_STATS_INC(ip_frag.memerr);\r
+      }\r
+      LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: p %p\n", (void*)p));\r
+      return p;\r
+    }\r
+  }\r
+\r
+nullreturn:\r
+  IPFRAG_STATS_INC(ip_frag.drop);\r
+  pbuf_free(p);\r
+  return NULL;\r
+}\r
+\r
+#define MAX_MTU 1500\r
+static u8_t buf[MEM_ALIGN_SIZE(MAX_MTU)];\r
+\r
+/**\r
+ * Fragment an IP datagram if too large for the netif.\r
+ *\r
+ * Chop the datagram in MTU sized chunks and send them in order\r
+ * by using a fixed size static memory buffer (PBUF_ROM)\r
+ */\r
+err_t \r
+ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest)\r
+{\r
+  struct pbuf *rambuf;\r
+  struct pbuf *header;\r
+  struct ip_hdr *iphdr;\r
+  u16_t nfb = 0;\r
+  u16_t left, cop;\r
+  u16_t mtu = netif->mtu;\r
+  u16_t ofo, omf;\r
+  u16_t last;\r
+  u16_t poff = IP_HLEN;\r
+  u16_t tmp;\r
+\r
+  /* Get a RAM based MTU sized pbuf */\r
+  rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);\r
+  if (rambuf == NULL) {\r
+    return ERR_MEM;\r
+  }\r
+  rambuf->tot_len = rambuf->len = mtu;\r
+  rambuf->payload = MEM_ALIGN((void *)buf);\r
+\r
+  /* Copy the IP header in it */\r
+  iphdr = rambuf->payload;\r
+  memcpy(iphdr, p->payload, IP_HLEN);\r
+\r
+  /* Save original offset */\r
+  tmp = ntohs(IPH_OFFSET(iphdr));\r
+  ofo = tmp & IP_OFFMASK;\r
+  omf = tmp & IP_MF;\r
+\r
+  left = p->tot_len - IP_HLEN;\r
+\r
+  while (left) {\r
+    last = (left <= mtu - IP_HLEN);\r
+\r
+    /* Set new offset and MF flag */\r
+    ofo += nfb;\r
+    tmp = omf | (IP_OFFMASK & (ofo));\r
+    if (!last)\r
+      tmp = tmp | IP_MF;\r
+    IPH_OFFSET_SET(iphdr, htons(tmp));\r
+\r
+    /* Fill this fragment */\r
+    nfb = (mtu - IP_HLEN) / 8;\r
+    cop = last ? left : nfb * 8;\r
+\r
+    p = copy_from_pbuf(p, &poff, (u8_t *) iphdr + IP_HLEN, cop);\r
+\r
+    /* Correct header */\r
+    IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));\r
+    IPH_CHKSUM_SET(iphdr, 0);\r
+    IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));\r
+\r
+    if (last)\r
+      pbuf_realloc(rambuf, left + IP_HLEN);\r
+    /* This part is ugly: we alloc a RAM based pbuf for \r
+     * the link level header for each chunk and then \r
+     * free it.A PBUF_ROM style pbuf for which pbuf_header\r
+     * worked would make things simpler.\r
+     */\r
+    header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);\r
+    if (header != NULL) {\r
+      pbuf_chain(header, rambuf);\r
+      netif->output(netif, header, dest);\r
+      IPFRAG_STATS_INC(ip_frag.xmit);\r
+      pbuf_free(header);\r
+    } else {\r
+      pbuf_free(rambuf);      \r
+      return ERR_MEM;    \r
+    }\r
+    left -= cop;\r
+  }\r
+  pbuf_free(rambuf);\r
+  return ERR_OK;\r
+}\r