]> rtime.felk.cvut.cz Git - l4.git/blobdiff - l4/pkg/lwip/lib/contrib/src/core/ipv4/ip_frag.c
Update
[l4.git] / l4 / pkg / lwip / lib / contrib / src / core / ipv4 / ip_frag.c
index 8d184345dff0809a51a6acdc7dffca03d8bc2ea0..306f31b7ced7d0a3bd44309a0184a2a027206813 100644 (file)
@@ -6,9 +6,9 @@
 
 /*
  * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved. 
- * 
- * Redistribution and use in source and binary forms, with or without modification, 
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *
  * 1. Redistributions of source code must retain the above copyright notice,
  *    this list of conditions and the following disclaimer in the documentation
  *    and/or other materials provided with the distribution.
  * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission. 
+ *    derived from this software without specific prior written permission.
  *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
  * OF SUCH DAMAGE.
  *
  * This file is part of the lwIP TCP/IP stack.
- * 
- * Author: Jani Monoses <jani@iv.ro> 
+ *
+ * Author: Jani Monoses <jani@iv.ro>
  *         Simon Goldschmidt
  * original reassembly code by Adam Dunkels <adam@sics.se>
- * 
+ *
  */
 
 #include "lwip/opt.h"
+
+#if LWIP_IPV4
+
 #include "lwip/ip_frag.h"
 #include "lwip/def.h"
 #include "lwip/inet_chksum.h"
 #include "lwip/netif.h"
-#include "lwip/snmp.h"
 #include "lwip/stats.h"
 #include "lwip/icmp.h"
 
@@ -100,8 +102,8 @@ PACK_STRUCT_END
 #endif
 
 #define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB)  \
-  (ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \
-   ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \
+  (ip4_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \
+   ip4_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \
    IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0
 
 /* global variables */
@@ -167,7 +169,7 @@ ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *p
     LWIP_ASSERT("prev->next == ipr", prev->next == ipr);
   }
 
-  snmp_inc_ipreasmfails();
+  MIB2_STATS_INC(mib2.ipreasmfails);
 #if LWIP_ICMP
   iprh = (struct ip_reass_helper *)ipr->p->payload;
   if (iprh->start == 0) {
@@ -185,7 +187,7 @@ ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *p
   }
 #endif /* LWIP_ICMP */
 
-  /* First, free all received pbufs.  The individual pbufs need to be released 
+  /* First, free all received pbufs.  The individual pbufs need to be released
      separately as they have not yet been chained */
   p = ipr->p;
   while (p != NULL) {
@@ -223,7 +225,7 @@ ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
   /* @todo Can't we simply remove the last datagram in the
    *       linked list behind reassdatagrams?
    */
-  struct ip_reassdata *r, *oldest, *prev;
+  struct ip_reassdata *r, *oldest, *prev, *oldest_prev;
   int pbufs_freed = 0, pbufs_freed_current;
   int other_datagrams;
 
@@ -232,6 +234,7 @@ ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
   do {
     oldest = NULL;
     prev = NULL;
+    oldest_prev = NULL;
     other_datagrams = 0;
     r = reassdatagrams;
     while (r != NULL) {
@@ -240,9 +243,11 @@ ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
         other_datagrams++;
         if (oldest == NULL) {
           oldest = r;
+          oldest_prev = prev;
         } else if (r->timer <= oldest->timer) {
           /* older than the previous oldest */
           oldest = r;
+          oldest_prev = prev;
         }
       }
       if (r->next != NULL) {
@@ -251,7 +256,7 @@ ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
       r = r->next;
     }
     if (oldest != NULL) {
-      pbufs_freed_current = ip_reass_free_complete_datagram(oldest, prev);
+      pbufs_freed_current = ip_reass_free_complete_datagram(oldest, oldest_prev);
       pbufs_freed += pbufs_freed_current;
     }
   } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1));
@@ -303,7 +308,6 @@ ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen)
 static void
 ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
 {
-  
   /* dequeue the reass struct  */
   if (reassdatagrams == ipr) {
     /* it was the first in the list */
@@ -314,7 +318,7 @@ ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
     prev->next = ipr->next;
   }
 
-  /* now we can free the ip_reass struct */
+  /* now we can free the ip_reassdata struct */
   memp_free(MEMP_REASSDATA, ipr);
 }
 
@@ -337,7 +341,7 @@ ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct
   int valid = 1;
 
   /* Extract length and fragment offset from current fragment */
-  fraghdr = (struct ip_hdr*)new_p->payload; 
+  fraghdr = (struct ip_hdr*)new_p->payload;
   len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
   offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
 
@@ -372,11 +376,11 @@ ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct
         ipr->p = new_p;
       }
       break;
-    } else if(iprh->start == iprh_tmp->start) {
+    } else if (iprh->start == iprh_tmp->start) {
       /* received the same datagram twice: no need to keep the datagram */
       goto freepbuf;
 #if IP_REASS_CHECK_OVERLAP
-    } else if(iprh->start < iprh_tmp->end) {
+    } else if (iprh->start < iprh_tmp->end) {
       /* overlap: no need to keep the new datagram */
       goto freepbuf;
 #endif /* IP_REASS_CHECK_OVERLAP */
@@ -423,7 +427,7 @@ ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct
     if (valid) {
       /* then check if the rest of the fragments is here */
       /* Check if the queue starts with the first datagram */
-      if (((struct ip_reass_helper*)ipr->p->payload)->start != 0) {
+      if ((ipr->p == NULL) || (((struct ip_reass_helper*)ipr->p->payload)->start != 0)) {
         valid = 0;
       } else {
         /* and check that there are no wholes after this datagram */
@@ -473,7 +477,7 @@ freepbuf:
  * @return NULL if reassembly is incomplete, ? otherwise
  */
 struct pbuf *
-ip_reass(struct pbuf *p)
+ip4_reass(struct pbuf *p)
 {
   struct pbuf *r;
   struct ip_hdr *fraghdr;
@@ -481,15 +485,14 @@ ip_reass(struct pbuf *p)
   struct ip_reass_helper *iprh;
   u16_t offset, len;
   u8_t clen;
-  struct ip_reassdata *ipr_prev = NULL;
 
   IPFRAG_STATS_INC(ip_frag.recv);
-  snmp_inc_ipreasmreqds();
+  MIB2_STATS_INC(mib2.ipreasmreqds);
 
   fraghdr = (struct ip_hdr*)p->payload;
 
   if ((IPH_HL(fraghdr) * 4) != IP_HLEN) {
-    LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n"));
+    LWIP_DEBUGF(IP_REASS_DEBUG,("ip4_reass: IP options currently not supported!\n"));
     IPFRAG_STATS_INC(ip_frag.err);
     goto nullreturn;
   }
@@ -506,7 +509,7 @@ ip_reass(struct pbuf *p)
 #endif /* IP_REASS_FREE_OLDEST */
     {
       /* No datagram could be freed and still too many pbufs enqueued */
-      LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n",
+      LWIP_DEBUGF(IP_REASS_DEBUG,("ip4_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n",
         ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS));
       IPFRAG_STATS_INC(ip_frag.memerr);
       /* @todo: send ICMP time exceeded here? */
@@ -522,23 +525,22 @@ ip_reass(struct pbuf *p)
        in the reassembly buffer. If so, we proceed with copying the
        fragment into the buffer. */
     if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) {
-      LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n",
+      LWIP_DEBUGF(IP_REASS_DEBUG, ("ip4_reass: matching previous fragment ID=%"X16_F"\n",
         ntohs(IPH_ID(fraghdr))));
       IPFRAG_STATS_INC(ip_frag.cachehit);
       break;
     }
-    ipr_prev = ipr;
   }
 
   if (ipr == NULL) {
   /* Enqueue a new datagram into the datagram queue */
     ipr = ip_reass_enqueue_new_datagram(fraghdr, clen);
     /* Bail if unable to enqueue */
-    if(ipr == NULL) {
+    if (ipr == NULL) {
       goto nullreturn;
     }
   } else {
-    if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) && 
+    if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) &&
       ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) {
       /* ipr->iphdr is not the header from the first fragment, but fraghdr is
        * -> copy fraghdr into ipr->iphdr since we want to have the header
@@ -547,11 +549,11 @@ ip_reass(struct pbuf *p)
       SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN);
     }
   }
-  /* Track the current number of pbufs current 'in-flight', in order to limit 
+  /* Track the current number of pbufs current 'in-flight', in order to limit
   the number of fragments that may be enqueued at any one time */
   ip_reass_pbufcount += clen;
 
-  /* At this point, we have either created a new entry or pointing 
+  /* At this point, we have either created a new entry or pointing
    * to an existing one */
 
   /* check for 'no more fragments', and update queue entry*/
@@ -559,12 +561,13 @@ ip_reass(struct pbuf *p)
     ipr->flags |= IP_REASS_FLAG_LASTFRAG;
     ipr->datagram_len = offset + len;
     LWIP_DEBUGF(IP_REASS_DEBUG,
-     ("ip_reass: last fragment seen, total len %"S16_F"\n",
+     ("ip4_reass: last fragment seen, total len %"S16_F"\n",
       ipr->datagram_len));
   }
   /* find the right place to insert this pbuf */
   /* @todo: trim pbufs if fragments are overlapping */
   if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) {
+    struct ip_reassdata *ipr_prev;
     /* the totally last fragment (flag more fragments = 0) was received at least
      * once AND all fragments are received */
     ipr->datagram_len += IP_HLEN;
@@ -578,26 +581,44 @@ ip_reass(struct pbuf *p)
     IPH_LEN_SET(fraghdr, htons(ipr->datagram_len));
     IPH_OFFSET_SET(fraghdr, 0);
     IPH_CHKSUM_SET(fraghdr, 0);
-    /* @todo: do we need to set calculate the correct checksum? */
-    IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN));
+    /* @todo: do we need to set/calculate the correct checksum? */
+#if CHECKSUM_GEN_IP
+    IF__NETIF_CHECKSUM_ENABLED(ip_current_input_netif(), NETIF_CHECKSUM_GEN_IP) {
+      IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN));
+    }
+#endif /* CHECKSUM_GEN_IP */
 
     p = ipr->p;
 
     /* chain together the pbufs contained within the reass_data list. */
-    while(r != NULL) {
+    while (r != NULL) {
       iprh = (struct ip_reass_helper*)r->payload;
 
-      /* hide the ip header for every succeding fragment */
+      /* hide the ip header for every succeeding fragment */
       pbuf_header(r, -IP_HLEN);
       pbuf_cat(p, r);
       r = iprh->next_pbuf;
     }
+
+    /* find the previous entry in the linked list */
+    if (ipr == reassdatagrams) {
+      ipr_prev = NULL;
+    } else {
+      for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr_prev->next) {
+        if (ipr_prev->next == ipr) {
+          break;
+        }
+      }
+    }
+
     /* release the sources allocate for the fragment queue entry */
     ip_reass_dequeue_datagram(ipr, ipr_prev);
 
     /* and adjust the number of pbufs currently queued for reassembly. */
     ip_reass_pbufcount -= pbuf_clen(p);
 
+    MIB2_STATS_INC(mib2.ipreasmoks);
+
     /* Return the pbuf chain */
     return p;
   }
@@ -606,7 +627,7 @@ ip_reass(struct pbuf *p)
   return NULL;
 
 nullreturn:
-  LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n"));
+  LWIP_DEBUGF(IP_REASS_DEBUG,("ip4_reass: nullreturn\n"));
   IPFRAG_STATS_INC(ip_frag.drop);
   pbuf_free(p);
   return NULL;
@@ -663,8 +684,8 @@ ipfrag_free_pbuf_custom(struct pbuf *p)
  *
  * @return ERR_OK if sent successfully, err_t otherwise
  */
-err_t 
-ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
+err_t
+ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest)
 {
   struct pbuf *rambuf;
 #if IP_FRAG_USES_STATIC_BUF
@@ -697,7 +718,7 @@ ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
   rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);
   if (rambuf == NULL) {
     LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n"));
-    return ERR_MEM;
+    goto memerr;
   }
   rambuf->tot_len = rambuf->len = mtu;
   rambuf->payload = LWIP_MEM_ALIGN((void *)buf);
@@ -737,19 +758,19 @@ ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
 #if LWIP_NETIF_TX_SINGLE_PBUF
     rambuf = pbuf_alloc(PBUF_IP, cop, PBUF_RAM);
     if (rambuf == NULL) {
-      return ERR_MEM;
+      goto memerr;
     }
     LWIP_ASSERT("this needs a pbuf in one piece!",
       (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL));
     poff += pbuf_copy_partial(p, rambuf->payload, cop, poff);
     /* make room for the IP header */
-    if(pbuf_header(rambuf, IP_HLEN)) {
+    if (pbuf_header(rambuf, IP_HLEN)) {
       pbuf_free(rambuf);
-      return ERR_MEM;
+      goto memerr;
     }
     /* fill in the IP header */
     SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
-    iphdr = rambuf->payload;
+    iphdr = (struct ip_hdr*)rambuf->payload;
 #else /* LWIP_NETIF_TX_SINGLE_PBUF */
     /* When not using a static buffer, create a chain of pbufs.
      * The first will be a PBUF_RAM holding the link and IP header.
@@ -758,7 +779,7 @@ ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
      */
     rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM);
     if (rambuf == NULL) {
-      return ERR_MEM;
+      goto memerr;
     }
     LWIP_ASSERT("this needs a pbuf in one piece!",
                 (p->len >= (IP_HLEN)));
@@ -781,14 +802,14 @@ ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
       pcr = ip_frag_alloc_pbuf_custom_ref();
       if (pcr == NULL) {
         pbuf_free(rambuf);
-        return ERR_MEM;
+        goto memerr;
       }
       /* Mirror this pbuf, although we might not need all of it. */
       newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen);
       if (newpbuf == NULL) {
         ip_frag_free_pbuf_custom_ref(pcr);
         pbuf_free(rambuf);
-        return ERR_MEM;
+        goto memerr;
       }
       pbuf_ref(p);
       pcr->original = p;
@@ -811,16 +832,20 @@ ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
     IPH_OFFSET_SET(iphdr, htons(tmp));
     IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
     IPH_CHKSUM_SET(iphdr, 0);
-    IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
+#if CHECKSUM_GEN_IP
+    IF__NETIF_CHECKSUM_ENABLED(netif, NETIF_CHECKSUM_GEN_IP) {
+      IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
+    }
+#endif /* CHECKSUM_GEN_IP */
 
 #if IP_FRAG_USES_STATIC_BUF
     if (last) {
       pbuf_realloc(rambuf, left + IP_HLEN);
     }
 
-    /* This part is ugly: we alloc a RAM based pbuf for 
-     * the link level header for each chunk and then 
-     * free it.A PBUF_ROM style pbuf for which pbuf_header
+    /* This part is ugly: we alloc a RAM based pbuf for
+     * the link level header for each chunk and then
+     * free it. A PBUF_ROM style pbuf for which pbuf_header
      * worked would make things simpler.
      */
     header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
@@ -828,12 +853,12 @@ ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
       pbuf_chain(header, rambuf);
       netif->output(netif, header, dest);
       IPFRAG_STATS_INC(ip_frag.xmit);
-      snmp_inc_ipfragcreates();
+      MIB2_STATS_INC(mib2.ipfragcreates);
       pbuf_free(header);
     } else {
       LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n"));
       pbuf_free(rambuf);
-      return ERR_MEM;
+      goto memerr;
     }
 #else /* IP_FRAG_USES_STATIC_BUF */
     /* No need for separate header pbuf - we allowed room for it in rambuf
@@ -848,7 +873,7 @@ ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
      * will have already sent the packet, the free will really free, and
      * there will be zero memory penalty.
      */
-    
+
     pbuf_free(rambuf);
 #endif /* IP_FRAG_USES_STATIC_BUF */
     left -= cop;
@@ -857,7 +882,12 @@ ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
 #if IP_FRAG_USES_STATIC_BUF
   pbuf_free(rambuf);
 #endif /* IP_FRAG_USES_STATIC_BUF */
-  snmp_inc_ipfragoks();
+  MIB2_STATS_INC(mib2.ipfragoks);
   return ERR_OK;
+memerr:
+  MIB2_STATS_INC(mib2.ipfragfails);
+  return ERR_MEM;
 }
 #endif /* IP_FRAG */
+
+#endif /* LWIP_IPV4 */