VirtualBox

Changeset 50023 in vbox


Ignore:
Timestamp:
Jan 6, 2014 2:15:53 AM (11 years ago)
Author:
vboxsync
Message:

Rewrite IPv6 reassembly: https://savannah.nongnu.org/bugs/?41009

Original code overwrote fragment header with a helper structure, but
unfortunately on a 64-bit machine the helper structure was larger than
the fragment header.

Store helper structure in the IPv6 header instead. While here
simplify the check for reassembly completion.

Location:
trunk/src/VBox/Devices/Network/lwip-new/src
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Network/lwip-new/src/core/ipv6/ip6_frag.c

    r47886 r50023  
    7676 * offset and the ending offset of this fragment to
    7777 * easily chain the fragments.
    78  * It has the same packing requirements as the IPv6 header, since it replaces
    79  * the Fragment Header in memory in incoming fragments to keep
    80  * track of the various fragments.
    8178 */
    82 #ifdef PACK_STRUCT_USE_INCLUDES
    83 #  include "arch/bpstruct.h"
    84 #endif
    85 PACK_STRUCT_BEGIN
    8679struct ip6_reass_helper {
    87   PACK_STRUCT_FIELD(struct pbuf *next_pbuf);
    88   PACK_STRUCT_FIELD(u16_t start);
    89   PACK_STRUCT_FIELD(u16_t end);
    90 } PACK_STRUCT_STRUCT;
    91 PACK_STRUCT_END
    92 #ifdef PACK_STRUCT_USE_INCLUDES
    93 #  include "arch/epstruct.h"
    94 #endif
     80  struct ip6_reass_helper *next;
     81  struct pbuf *p;
     82  u16_t start;
     83  u16_t end;
     84};
    9585
    9686/* static variables */
     
    137127ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr)
    138128{
    139   struct ip6_reassdata *prev;
     129  struct ip6_reassdata **pipr;
    140130  u16_t pbufs_freed = 0;
    141131  u8_t clen;
     
    143133  struct ip6_reass_helper *iprh;
    144134
     135  /* First, free all received pbufs.  The individual pbufs need to be released
     136     separately as they have not yet been chained */
     137  iprh = ipr->iprh;
     138  while (iprh != NULL) {
     139    struct ip6_reass_helper *next = iprh->next;
     140    p = iprh->p;
     141
    145142#if LWIP_ICMP6
    146   iprh = (struct ip6_reass_helper *)ipr->p->payload;
    147   if (iprh->start == 0) {
    148     /* The first fragment was received, send ICMP time exceeded. */
    149     /* First, de-queue the first pbuf from r->p. */
    150     p = ipr->p;
    151     ipr->p = iprh->next_pbuf;
    152     /* Then, move back to the original header (we are now pointing to Fragment header). */
    153     if (pbuf_header(p, (u8_t*)p->payload - (u8_t*)ipr->iphdr)) {
    154       LWIP_ASSERT("ip6_reass_free: moving p->payload to ip6 header failed\n", 0);
    155     }
    156     else {
    157       icmp6_time_exceeded(p, ICMP6_TE_FRAG);
    158     }
     143    /* If the first fragment was received, send ICMP time exceeded. */
     144    if (iprh->start == 0) {
     145      SMEMCPY(ipr->iphdr0, &ipr->iphdr, IP6_HLEN);
     146      if (pbuf_header(p, (u8_t *)p->payload - (u8_t *)ipr->iphdr0) == 0) {
     147        icmp6_time_exceeded(p, ICMP6_TE_FRAG);
     148      }
     149      else {
     150        LWIP_ASSERT("ip6_reass_free: moving p->payload to ip6 header failed\n", 0);
     151      }
     152    }
     153#endif /* LWIP_ICMP6 */
     154
    159155    clen = pbuf_clen(p);
    160156    LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
    161157    pbufs_freed += clen;
    162158    pbuf_free(p);
    163   }
    164 #endif /* LWIP_ICMP6 */
    165 
    166   /* First, free all received pbufs.  The individual pbufs need to be released
    167      separately as they have not yet been chained */
    168   p = ipr->p;
    169   while (p != NULL) {
    170     struct pbuf *pcur;
    171     iprh = (struct ip6_reass_helper *)p->payload;
    172     pcur = p;
    173     /* get the next pointer before freeing */
    174     p = iprh->next_pbuf;
    175     clen = pbuf_clen(pcur);
    176     LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
    177     pbufs_freed += clen;
    178     pbuf_free(pcur);
     159
     160    iprh = next;
    179161  }
    180162
    181163  /* Then, unchain the struct ip6_reassdata from the list and free it. */
    182   if (ipr == reassdatagrams) {
    183     reassdatagrams = ipr->next;
    184   } else {
    185     prev = reassdatagrams;
    186     while (prev != NULL) {
    187       if (prev->next == ipr) {
    188         break;
    189       }
    190       prev = prev->next;
    191     }
    192     if (prev != NULL) {
    193       prev->next = ipr->next;
     164  for (pipr = &reassdatagrams; *pipr != NULL; pipr = &(*pipr)->next) {
     165    if (*pipr == ipr) {
     166      (*pipr) = ipr->next;
     167      break;
    194168    }
    195169  }
     
    246220ip6_reass(struct pbuf *p)
    247221{
    248   struct ip6_reassdata *ipr, *ipr_prev;
    249   struct ip6_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
     222  struct ip6_reassdata *ipr, **pipr;
     223  struct ip6_reass_helper *iprh, *iprh_tmp;
     224  struct ip6_reass_helper **pnext;
    250225  struct ip6_frag_hdr * frag_hdr;
    251   u16_t offset, len;
    252   u8_t clen, valid = 1;
    253   struct pbuf *q;
     226  size_t unfrag_len;
     227  u16_t offset, len, start, end, validlen;
     228  u8_t clen;
    254229
    255230  IP6_FRAG_STATS_INC(ip6_frag.recv);
     
    268243  len -= IP6_FRAG_HLEN;
    269244
     245  start = (offset & IP6_FRAG_OFFSET_MASK);
     246  end = start + len;
     247
     248
    270249  /* Look for the datagram the fragment belongs to in the current datagram queue,
    271250   * remembering the previous in the queue for later dequeueing. */
    272   for (ipr = reassdatagrams, ipr_prev = NULL; ipr != NULL; ipr = ipr->next) {
     251  for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) {
    273252    /* Check if the incoming fragment matches the one currently present
    274253       in the reassembly buffer. If so, we proceed with copying the
    275254       fragment into the buffer. */
    276255    if ((frag_hdr->_identification == ipr->identification) &&
    277         ip6_addr_cmp(ip6_current_src_addr(), &(ipr->iphdr->src)) &&
    278         ip6_addr_cmp(ip6_current_dest_addr(), &(ipr->iphdr->dest))) {
     256        ip6_addr_cmp(ip6_current_src_addr(), &(ipr->iphdr.src)) &&
     257        ip6_addr_cmp(ip6_current_dest_addr(), &(ipr->iphdr.dest))) {
    279258      IP6_FRAG_STATS_INC(ip6_frag.cachehit);
    280259      break;
    281260    }
    282     ipr_prev = ipr;
    283261  }
    284262
     
    310288     * Eventually, we will replace it when we get the first fragment
    311289     * (it might be this one, in any case, it is done later). */
    312     ipr->iphdr = (struct ip6_hdr *)ip6_current_header();
     290    SMEMCPY(&ipr->iphdr, ip6_current_header(), IP6_HLEN);
     291    if (start == 0) {
     292      ipr->iphdr0 = (struct ip6_hdr *)ip6_current_header();
     293    }
    313294
    314295    /* copy the fragmented packet id. */
    315296    ipr->identification = frag_hdr->_identification;
    316 
    317     /* copy the nexth field */
    318     ipr->nexth = frag_hdr->_nexth;
     297  }
     298
     299  /* If this is the last fragment, save total packet length. */
     300  if ((offset & IP6_FRAG_MORE_FLAG) == 0) {
     301#if IP_REASS_CHECK_OVERLAP
     302    if (ipr->datagram_len != 0) {
     303      IP6_FRAG_STATS_INC(ip6_frag.proterr);
     304      IP6_FRAG_STATS_INC(ip6_frag.drop);
     305      goto nullreturn;
     306    }
     307#endif /* IP_REASS_CHECK_OVERLAP */
     308    ipr->datagram_len = end;
     309  }
     310
     311  /* find the place to insert this pbuf */
     312  validlen = 0;
     313  for (pnext = &ipr->iprh; *pnext != NULL; pnext = &(*pnext)->next) {
     314    iprh_tmp = *pnext;
     315
     316    if (start < iprh_tmp->start) {
     317      /* the new pbuf should be inserted before this */
     318#if IP_REASS_CHECK_OVERLAP
     319      if (end > iprh_tmp->start) {
     320        /* fragment overlaps with following, throw away */
     321        IP6_FRAG_STATS_INC(ip6_frag.proterr);
     322        IP6_FRAG_STATS_INC(ip6_frag.drop);
     323        goto nullreturn;
     324      }
     325#endif /* IP_REASS_CHECK_OVERLAP */
     326      break;
     327    }
     328    else if (start == iprh_tmp->start) {
     329      /* received the same datagram twice: no need to keep the datagram */
     330      IP6_FRAG_STATS_INC(ip6_frag.drop);
     331      goto nullreturn;
     332    }
     333#if IP_REASS_CHECK_OVERLAP
     334    else if (start < iprh_tmp->end) {
     335      /* overlap: no need to keep the new datagram */
     336      IP6_FRAG_STATS_INC(ip6_frag.proterr);
     337      IP6_FRAG_STATS_INC(ip6_frag.drop);
     338      goto nullreturn;
     339    }
     340#endif /* IP_REASS_CHECK_OVERLAP */
     341    else {
     342      /* Check if the fragments received so far have no gaps. */
     343      if (validlen == iprh_tmp->start) {
     344        validlen = iprh_tmp->end;
     345      }
     346      else {
     347        validlen = 0;
     348      }
     349    }
    319350  }
    320351
     
    334365  }
    335366
    336   /* Overwrite Fragment Header with our own helper struct. */
    337   iprh = (struct ip6_reass_helper *)p->payload;
    338   iprh->next_pbuf = NULL;
    339   iprh->start = (offset & IP6_FRAG_OFFSET_MASK);
    340   iprh->end = (offset & IP6_FRAG_OFFSET_MASK) + len;
    341 
    342   /* find the right place to insert this pbuf */
    343   /* Iterate through until we either get to the end of the list (append),
    344    * or we find on with a larger offset (insert). */
    345   for (q = ipr->p; q != NULL;) {
    346     iprh_tmp = (struct ip6_reass_helper*)q->payload;
    347     if (iprh->start < iprh_tmp->start) {
    348 #if IP_REASS_CHECK_OVERLAP
    349       if (iprh->end > iprh_tmp->start) {
    350         /* fragment overlaps with following, throw away */
    351         IP6_FRAG_STATS_INC(ip6_frag.proterr);
    352         IP6_FRAG_STATS_INC(ip6_frag.drop);
    353         goto nullreturn;
    354       }
    355       if (iprh_prev != NULL) {
    356         if (iprh->start < iprh_prev->end) {
    357           /* fragment overlaps with previous, throw away */
    358           IP6_FRAG_STATS_INC(ip6_frag.proterr);
    359           IP6_FRAG_STATS_INC(ip6_frag.drop);
    360           goto nullreturn;
    361         }
    362       }
    363 #endif /* IP_REASS_CHECK_OVERLAP */
    364       /* the new pbuf should be inserted before this */
    365       iprh->next_pbuf = q;
    366       if (iprh_prev != NULL) {
    367         /* not the fragment with the lowest offset */
    368         iprh_prev->next_pbuf = p;
    369       } else {
    370         /* fragment with the lowest offset */
    371         ipr->p = p;
    372       }
    373       break;
    374     } else if(iprh->start == iprh_tmp->start) {
    375       /* received the same datagram twice: no need to keep the datagram */
    376       IP6_FRAG_STATS_INC(ip6_frag.drop);
    377       goto nullreturn;
    378 #if IP_REASS_CHECK_OVERLAP
    379     } else if(iprh->start < iprh_tmp->end) {
    380       /* overlap: no need to keep the new datagram */
    381       IP6_FRAG_STATS_INC(ip6_frag.proterr);
    382       IP6_FRAG_STATS_INC(ip6_frag.drop);
    383       goto nullreturn;
    384 #endif /* IP_REASS_CHECK_OVERLAP */
    385     } else {
    386       /* Check if the fragments received so far have no gaps. */
    387       if (iprh_prev != NULL) {
    388         if (iprh_prev->end != iprh_tmp->start) {
    389           /* There is a fragment missing between the current
    390            * and the previous fragment */
    391           valid = 0;
    392         }
    393       }
    394     }
    395     q = iprh_tmp->next_pbuf;
    396     iprh_prev = iprh_tmp;
    397   }
    398 
    399   /* If q is NULL, then we made it to the end of the list. Determine what to do now */
    400   if (q == NULL) {
    401     if (iprh_prev != NULL) {
    402       /* this is (for now), the fragment with the highest offset:
    403        * chain it to the last fragment */
    404 #if IP_REASS_CHECK_OVERLAP
    405       LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start);
    406 #endif /* IP_REASS_CHECK_OVERLAP */
    407       iprh_prev->next_pbuf = p;
    408       if (iprh_prev->end != iprh->start) {
    409         valid = 0;
    410       }
    411     } else {
    412 #if IP_REASS_CHECK_OVERLAP
    413       LWIP_ASSERT("no previous fragment, this must be the first fragment!",
    414         ipr->p == NULL);
    415 #endif /* IP_REASS_CHECK_OVERLAP */
    416       /* this is the first fragment we ever received for this ip datagram */
    417       ipr->p = p;
    418     }
    419   }
     367  if (start == 0 && ipr->iphdr0 == NULL) {
     368    /*
     369     * We've got the fragment with offset 0 out of order, remember its
     370     * IPv6 header location (in the hidden part of the current pbuf)
     371     * and update the copy in ip6_reassdata::iphdr.  We don't need to
     372     * copy complete header since src and dest are the same as in the
     373     * first fragment we received.
     374     */
     375    ipr->iphdr0 = (struct ip6_hdr *)ip6_current_header();
     376    SMEMCPY(&ipr->iphdr, ip6_current_header(),
     377            IP6_HLEN - 2 * sizeof(ip_addr_p_t));
     378  }
     379
     380  /* Overwrite IPv6 Header with our own helper struct (aligned). */
     381  iprh = (struct ip6_reass_helper *)
     382    (((uintptr_t)(u8_t *)ip6_current_header() + sizeof(void *) - 1)
     383     & ~(sizeof(void *) - 1));
     384  iprh->p = p;
     385  iprh->start = start;
     386  iprh->end = end;
     387
     388  /* insert it into the list */
     389  iprh->next = *pnext;
     390  *pnext = iprh;
    420391
    421392  /* Track the current number of pbufs current 'in-flight', in order to limit
     
    423394  ip6_reass_pbufcount += clen;
    424395
    425   /* Remember IPv6 header if this is the first fragment. */
    426   if (iprh->start == 0) {
    427     ipr->iphdr = (struct ip6_hdr *)ip6_current_header();
    428   }
    429 
    430   /* If this is the last fragment, calculate total packet length. */
    431   if ((offset & IP6_FRAG_MORE_FLAG) == 0) {
    432     ipr->datagram_len = iprh->end;
    433   }
    434 
    435   /* Additional validity tests: we have received first and last fragment. */
    436   iprh_tmp = (struct ip6_reass_helper*)ipr->p->payload;
    437   if (iprh_tmp->start != 0) {
    438     valid = 0;
    439   }
    440396  if (ipr->datagram_len == 0) {
    441     valid = 0;
    442   }
    443 
    444   /* Final validity test: no gaps between current and last fragment. */
    445   iprh_prev = iprh;
    446   q = iprh->next_pbuf;
    447   while ((q != NULL) && valid) {
    448     iprh = (struct ip6_reass_helper*)q->payload;
    449     if (iprh_prev->end != iprh->start) {
    450       valid = 0;
     397    /* We still don't have the last fragment. */
     398    return NULL;
     399  }
     400
     401  if (validlen == start) {
     402    validlen = end;
     403  }
     404  else {
     405    /* There are gaps before this fragment. */
     406    return NULL;
     407  }
     408
     409  if (validlen != 0) {
     410    /*
     411     * We know we have all the data up to the end of this fragment and
     412     * we know the total length.  Check if the reassembly is complete.
     413     */
     414    for (iprh_tmp = iprh->next; iprh_tmp != NULL; iprh_tmp = iprh_tmp->next) {
     415      if (validlen == iprh_tmp->start) {
     416        validlen = iprh_tmp->end;
     417      }
     418      else {
     419        validlen = 0;
     420        break;
     421      }
     422    }
     423
     424    if (validlen != ipr->datagram_len) {
     425      /* the datagram is not yet reassembled completely */
     426      return NULL;
     427    }
     428  }
     429
     430  /*
     431   * All fragments have been received.  Reassemble original datagram
     432   * and return it to ip6_input() to be processed instead of the final
     433   * fragment that completed the reassembly.
     434   */
     435
     436  /* chain together the pbufs contained within the ip6_reassdata list. */
     437  p = NULL;
     438  for (iprh = ipr->iprh; iprh != NULL; iprh = iprh->next) {
     439    if (p == NULL) {
     440      p = iprh->p;
     441    }
     442    else {
     443      /* hide the fragment header for every succeeding fragment */
     444      pbuf_header(iprh->p, -IP6_FRAG_HLEN);
     445      pbuf_cat(p, iprh->p);
     446    }
     447  }
     448
     449  /* Adjust datagram length by adding preceding header lengths. */
     450  unfrag_len = (u8_t *)p->payload - (u8_t *)ipr->iphdr0;
     451  ipr->datagram_len += unfrag_len - IP6_HLEN + IP6_FRAG_HLEN;
     452
     453  /* Set payload length in ip header. */
     454  ipr->iphdr._plen = htons(ipr->datagram_len);
     455
     456  /* restore IPv6 header (overwritten with ip6_reass_helper) */
     457  SMEMCPY(ipr->iphdr0, &ipr->iphdr, IP6_HLEN);
     458
     459  /* Mark as "single fragment" packet (see caller). */
     460  frag_hdr = (struct ip6_frag_hdr *) p->payload;
     461  frag_hdr->_fragment_offset = 0;
     462
     463  /* Unlink from the reassdatagrams list */
     464  for (pipr = &reassdatagrams; *pipr != NULL; pipr = &(*pipr)->next) {
     465    if (*pipr == ipr) {
     466      (*pipr) = ipr->next;
    451467      break;
    452468    }
    453     iprh_prev = iprh;
    454     q = iprh->next_pbuf;
    455   }
    456 
    457   if (valid) {
    458     /* All fragments have been received */
    459 
    460     /* chain together the pbufs contained within the ip6_reassdata list. */
    461     iprh = (struct ip6_reass_helper*) ipr->p->payload;
    462     while(iprh != NULL) {
    463 
    464       if (iprh->next_pbuf != NULL) {
    465         /* Save next helper struct (will be hidden in next step). */
    466         iprh_tmp = (struct ip6_reass_helper*) iprh->next_pbuf->payload;
    467 
    468         /* hide the fragment header for every succeding fragment */
    469         pbuf_header(iprh->next_pbuf, -IP6_FRAG_HLEN);
    470         pbuf_cat(ipr->p, iprh->next_pbuf);
    471       }
    472       else {
    473         iprh_tmp = NULL;
    474       }
    475 
    476       iprh = iprh_tmp;
    477     }
    478 
    479     /* Adjust datagram length by adding header lengths. */
    480     ipr->datagram_len += ((u8_t*)ipr->p->payload - (u8_t*)ipr->iphdr)
    481                          + IP6_FRAG_HLEN
    482                          - IP6_HLEN ;
    483 
    484     /* Set payload length in ip header. */
    485     ipr->iphdr->_plen = htons(ipr->datagram_len);
    486 
    487     /* Get the furst pbuf. */
    488     p = ipr->p;
    489 
    490     /* Restore Fragment Header in first pbuf. Mark as "single fragment"
    491      * packet. Restore nexth. */
    492     frag_hdr = (struct ip6_frag_hdr *) p->payload;
    493     frag_hdr->_nexth = ipr->nexth;
    494     frag_hdr->reserved = 0;
    495     frag_hdr->_fragment_offset = 0;
    496     frag_hdr->_identification = 0;
    497 
    498     /* release the sources allocate for the fragment queue entry */
    499     if (reassdatagrams == ipr) {
    500       /* it was the first in the list */
    501       reassdatagrams = ipr->next;
    502     } else {
    503       /* it wasn't the first, so it must have a valid 'prev' */
    504       LWIP_ASSERT("sanity check linked list", ipr_prev != NULL);
    505       ipr_prev->next = ipr->next;
    506     }
    507     memp_free(MEMP_IP6_REASSDATA, ipr);
    508 
    509     /* adjust the number of pbufs currently queued for reassembly. */
    510     ip6_reass_pbufcount -= pbuf_clen(p);
    511 
    512     /* Move pbuf back to IPv6 header. */
    513     if (pbuf_header(p, (u8_t*)p->payload - (u8_t*)ipr->iphdr)) {
    514       LWIP_ASSERT("ip6_reass: moving p->payload to ip6 header failed\n", 0);
    515       pbuf_free(p);
    516       return NULL;
    517     }
    518 
    519     /* Return the pbuf chain */
    520     return p;
    521   }
    522   /* the datagram is not (yet?) reassembled completely */
    523   return NULL;
     469  }
     470  memp_free(MEMP_IP6_REASSDATA, ipr);
     471
     472  /* adjust the number of pbufs currently queued for reassembly. */
     473  ip6_reass_pbufcount -= pbuf_clen(p);
     474
     475  /* Move pbuf back to IPv6 header. */
     476  if (pbuf_header(p, unfrag_len) != 0) {
     477    LWIP_ASSERT("ip6_reass: moving p->payload to ip6 header failed\n", 0);
     478    goto nullreturn;
     479  }
     480
     481  /* Return the pbuf chain */
     482  return p;
    524483
    525484nullreturn:
     
    528487}
    529488
    530 #endif /* LWIP_IPV6 ^^ LWIP_IPV6_REASS */
     489#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */
    531490
    532491#if LWIP_IPV6 && LWIP_IPV6_FRAG
  • trunk/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/ip6_frag.h

    r47886 r50023  
    4444#include "lwip/opt.h"
    4545#include "lwip/pbuf.h"
     46#include "lwip/ip6.h"
    4647#include "lwip/ip6_addr.h"
    4748#include "lwip/netif.h"
     
    5758#define IP6_REASS_TMR_INTERVAL 1000
    5859
    59 /* IPv6 reassembly helper struct.
     60struct ip6_reass_helper;
     61
     62/* IPv6 reassembly struct.
    6063 * This is exported because memp needs to know the size.
    6164 */
    6265struct ip6_reassdata {
    6366  struct ip6_reassdata *next;
    64   struct pbuf *p;
    65   struct ip6_hdr * iphdr;
     67  struct ip6_reass_helper *iprh;
     68  struct ip6_hdr *iphdr0;
     69  struct ip6_hdr iphdr;
    6670  u32_t identification;
    6771  u16_t datagram_len;
    68   u8_t nexth;
    6972  u8_t timer;
    7073};
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette