Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of lwip by
ip_frag.c
00001 /** 00002 * @file 00003 * This is the IPv4 packet segmentation and reassembly implementation. 00004 * 00005 */ 00006 00007 /* 00008 * Copyright (c) 2001-2004 Swedish Institute of Computer Science. 00009 * All rights reserved. 00010 * 00011 * Redistribution and use in source and binary forms, with or without modification, 00012 * are permitted provided that the following conditions are met: 00013 * 00014 * 1. Redistributions of source code must retain the above copyright notice, 00015 * this list of conditions and the following disclaimer. 00016 * 2. Redistributions in binary form must reproduce the above copyright notice, 00017 * this list of conditions and the following disclaimer in the documentation 00018 * and/or other materials provided with the distribution. 00019 * 3. The name of the author may not be used to endorse or promote products 00020 * derived from this software without specific prior written permission. 00021 * 00022 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 00023 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 00024 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 00025 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 00026 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 00027 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 00028 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 00029 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 00030 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 00031 * OF SUCH DAMAGE. 00032 * 00033 * This file is part of the lwIP TCP/IP stack. 00034 * 00035 * Author: Jani Monoses <jani@iv.ro> 00036 * Simon Goldschmidt 00037 * original reassembly code by Adam Dunkels <adam@sics.se> 00038 * 00039 */ 00040 00041 #include "lwip/opt.h" 00042 #include "lwip/ip_frag.h" 00043 #include "lwip/def.h" 00044 #include "lwip/inet_chksum.h" 00045 #include "lwip/netif.h" 00046 #include "lwip/snmp.h" 00047 #include "lwip/stats.h" 00048 #include "lwip/icmp.h" 00049 00050 #include <string.h> 00051 00052 #if IP_REASSEMBLY 00053 /** 00054 * The IP reassembly code currently has the following limitations: 00055 * - IP header options are not supported 00056 * - fragments must not overlap (e.g. due to different routes), 00057 * currently, overlapping or duplicate fragments are thrown away 00058 * if IP_REASS_CHECK_OVERLAP=1 (the default)! 00059 * 00060 * @todo: work with IP header options 00061 */ 00062 00063 /** Setting this to 0, you can turn off checking the fragments for overlapping 00064 * regions. The code gets a little smaller. Only use this if you know that 00065 * overlapping won't occur on your network! */ 00066 #ifndef IP_REASS_CHECK_OVERLAP 00067 #define IP_REASS_CHECK_OVERLAP 1 00068 #endif /* IP_REASS_CHECK_OVERLAP */ 00069 00070 /** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is 00071 * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller. 00072 * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA 00073 * is set to 1, so one datagram can be reassembled at a time, only. */ 00074 #ifndef IP_REASS_FREE_OLDEST 00075 #define IP_REASS_FREE_OLDEST 1 00076 #endif /* IP_REASS_FREE_OLDEST */ 00077 00078 #define IP_REASS_FLAG_LASTFRAG 0x01 00079 00080 /** This is a helper struct which holds the starting 00081 * offset and the ending offset of this fragment to 00082 * easily chain the fragments. 00083 * It has the same packing requirements as the IP header, since it replaces 00084 * the IP header in memory in incoming fragments (after copying it) to keep 00085 * track of the various fragments. (-> If the IP header doesn't need packing, 00086 * this struct doesn't need packing, too.) 00087 */ 00088 #ifdef PACK_STRUCT_USE_INCLUDES 00089 # include "arch/bpstruct.h" 00090 #endif 00091 PACK_STRUCT_BEGIN 00092 struct ip_reass_helper { 00093 PACK_STRUCT_FIELD(struct pbuf *next_pbuf); 00094 PACK_STRUCT_FIELD(u16_t start); 00095 PACK_STRUCT_FIELD(u16_t end); 00096 } PACK_STRUCT_STRUCT; 00097 PACK_STRUCT_END 00098 #ifdef PACK_STRUCT_USE_INCLUDES 00099 # include "arch/epstruct.h" 00100 #endif 00101 00102 #define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \ 00103 (ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \ 00104 ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \ 00105 IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0 00106 00107 /* global variables */ 00108 static struct ip_reassdata *reassdatagrams; 00109 static u16_t ip_reass_pbufcount; 00110 00111 /* function prototypes */ 00112 static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev); 00113 static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev); 00114 00115 /** 00116 * Reassembly timer base function 00117 * for both NO_SYS == 0 and 1 (!). 00118 * 00119 * Should be called every 1000 msec (defined by IP_TMR_INTERVAL). 00120 */ 00121 void 00122 ip_reass_tmr(void) 00123 { 00124 struct ip_reassdata *r, *prev = NULL; 00125 00126 r = reassdatagrams; 00127 while (r != NULL) { 00128 /* Decrement the timer. Once it reaches 0, 00129 * clean up the incomplete fragment assembly */ 00130 if (r->timer > 0) { 00131 r->timer--; 00132 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer)); 00133 prev = r; 00134 r = r->next; 00135 } else { 00136 /* reassembly timed out */ 00137 struct ip_reassdata *tmp; 00138 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n")); 00139 tmp = r; 00140 /* get the next pointer before freeing */ 00141 r = r->next; 00142 /* free the helper struct and all enqueued pbufs */ 00143 ip_reass_free_complete_datagram(tmp, prev); 00144 } 00145 } 00146 } 00147 00148 /** 00149 * Free a datagram (struct ip_reassdata) and all its pbufs. 00150 * Updates the total count of enqueued pbufs (ip_reass_pbufcount), 00151 * SNMP counters and sends an ICMP time exceeded packet. 00152 * 00153 * @param ipr datagram to free 00154 * @param prev the previous datagram in the linked list 00155 * @return the number of pbufs freed 00156 */ 00157 static int 00158 ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) 00159 { 00160 u16_t pbufs_freed = 0; 00161 u8_t clen; 00162 struct pbuf *p; 00163 struct ip_reass_helper *iprh; 00164 00165 LWIP_ASSERT("prev != ipr", prev != ipr); 00166 if (prev != NULL) { 00167 LWIP_ASSERT("prev->next == ipr", prev->next == ipr); 00168 } 00169 00170 snmp_inc_ipreasmfails(); 00171 #if LWIP_ICMP 00172 iprh = (struct ip_reass_helper *)ipr->p->payload; 00173 if (iprh->start == 0) { 00174 /* The first fragment was received, send ICMP time exceeded. */ 00175 /* First, de-queue the first pbuf from r->p. */ 00176 p = ipr->p; 00177 ipr->p = iprh->next_pbuf; 00178 /* Then, copy the original header into it. */ 00179 SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN); 00180 icmp_time_exceeded(p, ICMP_TE_FRAG); 00181 clen = pbuf_clen(p); 00182 LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); 00183 pbufs_freed += clen; 00184 pbuf_free(p); 00185 } 00186 #endif /* LWIP_ICMP */ 00187 00188 /* First, free all received pbufs. The individual pbufs need to be released 00189 separately as they have not yet been chained */ 00190 p = ipr->p; 00191 while (p != NULL) { 00192 struct pbuf *pcur; 00193 iprh = (struct ip_reass_helper *)p->payload; 00194 pcur = p; 00195 /* get the next pointer before freeing */ 00196 p = iprh->next_pbuf; 00197 clen = pbuf_clen(pcur); 00198 LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); 00199 pbufs_freed += clen; 00200 pbuf_free(pcur); 00201 } 00202 /* Then, unchain the struct ip_reassdata from the list and free it. */ 00203 ip_reass_dequeue_datagram(ipr, prev); 00204 LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed); 00205 ip_reass_pbufcount -= pbufs_freed; 00206 00207 return pbufs_freed; 00208 } 00209 00210 #if IP_REASS_FREE_OLDEST 00211 /** 00212 * Free the oldest datagram to make room for enqueueing new fragments. 00213 * The datagram 'fraghdr' belongs to is not freed! 00214 * 00215 * @param fraghdr IP header of the current fragment 00216 * @param pbufs_needed number of pbufs needed to enqueue 00217 * (used for freeing other datagrams if not enough space) 00218 * @return the number of pbufs freed 00219 */ 00220 static int 00221 ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed) 00222 { 00223 /* @todo Can't we simply remove the last datagram in the 00224 * linked list behind reassdatagrams? 00225 */ 00226 struct ip_reassdata *r, *oldest, *prev; 00227 int pbufs_freed = 0, pbufs_freed_current; 00228 int other_datagrams; 00229 00230 /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs, 00231 * but don't free the datagram that 'fraghdr' belongs to! */ 00232 do { 00233 oldest = NULL; 00234 prev = NULL; 00235 other_datagrams = 0; 00236 r = reassdatagrams; 00237 while (r != NULL) { 00238 if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) { 00239 /* Not the same datagram as fraghdr */ 00240 other_datagrams++; 00241 if (oldest == NULL) { 00242 oldest = r; 00243 } else if (r->timer <= oldest->timer) { 00244 /* older than the previous oldest */ 00245 oldest = r; 00246 } 00247 } 00248 if (r->next != NULL) { 00249 prev = r; 00250 } 00251 r = r->next; 00252 } 00253 if (oldest != NULL) { 00254 pbufs_freed_current = ip_reass_free_complete_datagram(oldest, prev); 00255 pbufs_freed += pbufs_freed_current; 00256 } 00257 } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1)); 00258 return pbufs_freed; 00259 } 00260 #endif /* IP_REASS_FREE_OLDEST */ 00261 00262 /** 00263 * Enqueues a new fragment into the fragment queue 00264 * @param fraghdr points to the new fragments IP hdr 00265 * @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space) 00266 * @return A pointer to the queue location into which the fragment was enqueued 00267 */ 00268 static struct ip_reassdata* 00269 ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen) 00270 { 00271 struct ip_reassdata* ipr; 00272 /* No matching previous fragment found, allocate a new reassdata struct */ 00273 ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA); 00274 if (ipr == NULL) { 00275 #if IP_REASS_FREE_OLDEST 00276 if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) { 00277 ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA); 00278 } 00279 if (ipr == NULL) 00280 #endif /* IP_REASS_FREE_OLDEST */ 00281 { 00282 IPFRAG_STATS_INC(ip_frag.memerr); 00283 LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n")); 00284 return NULL; 00285 } 00286 } 00287 memset(ipr, 0, sizeof(struct ip_reassdata)); 00288 ipr->timer = IP_REASS_MAXAGE; 00289 00290 /* enqueue the new structure to the front of the list */ 00291 ipr->next = reassdatagrams; 00292 reassdatagrams = ipr; 00293 /* copy the ip header for later tests and input */ 00294 /* @todo: no ip options supported? */ 00295 SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN); 00296 return ipr; 00297 } 00298 00299 /** 00300 * Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs. 00301 * @param ipr points to the queue entry to dequeue 00302 */ 00303 static void 00304 ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) 00305 { 00306 00307 /* dequeue the reass struct */ 00308 if (reassdatagrams == ipr) { 00309 /* it was the first in the list */ 00310 reassdatagrams = ipr->next; 00311 } else { 00312 /* it wasn't the first, so it must have a valid 'prev' */ 00313 LWIP_ASSERT("sanity check linked list", prev != NULL); 00314 prev->next = ipr->next; 00315 } 00316 00317 /* now we can free the ip_reass struct */ 00318 memp_free(MEMP_REASSDATA, ipr); 00319 } 00320 00321 /** 00322 * Chain a new pbuf into the pbuf list that composes the datagram. The pbuf list 00323 * will grow over time as new pbufs are rx. 00324 * Also checks that the datagram passes basic continuity checks (if the last 00325 * fragment was received at least once). 00326 * @param root_p points to the 'root' pbuf for the current datagram being assembled. 00327 * @param new_p points to the pbuf for the current fragment 00328 * @return 0 if invalid, >0 otherwise 00329 */ 00330 static int 00331 ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p) 00332 { 00333 struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL; 00334 struct pbuf *q; 00335 u16_t offset,len; 00336 struct ip_hdr *fraghdr; 00337 int valid = 1; 00338 00339 /* Extract length and fragment offset from current fragment */ 00340 fraghdr = (struct ip_hdr*)new_p->payload; 00341 len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; 00342 offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; 00343 00344 /* overwrite the fragment's ip header from the pbuf with our helper struct, 00345 * and setup the embedded helper structure. */ 00346 /* make sure the struct ip_reass_helper fits into the IP header */ 00347 LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN", 00348 sizeof(struct ip_reass_helper) <= IP_HLEN); 00349 iprh = (struct ip_reass_helper*)new_p->payload; 00350 iprh->next_pbuf = NULL; 00351 iprh->start = offset; 00352 iprh->end = offset + len; 00353 00354 /* Iterate through until we either get to the end of the list (append), 00355 * or we find on with a larger offset (insert). */ 00356 for (q = ipr->p; q != NULL;) { 00357 iprh_tmp = (struct ip_reass_helper*)q->payload; 00358 if (iprh->start < iprh_tmp->start) { 00359 /* the new pbuf should be inserted before this */ 00360 iprh->next_pbuf = q; 00361 if (iprh_prev != NULL) { 00362 /* not the fragment with the lowest offset */ 00363 #if IP_REASS_CHECK_OVERLAP 00364 if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) { 00365 /* fragment overlaps with previous or following, throw away */ 00366 goto freepbuf; 00367 } 00368 #endif /* IP_REASS_CHECK_OVERLAP */ 00369 iprh_prev->next_pbuf = new_p; 00370 } else { 00371 /* fragment with the lowest offset */ 00372 ipr->p = new_p; 00373 } 00374 break; 00375 } else if(iprh->start == iprh_tmp->start) { 00376 /* received the same datagram twice: no need to keep the datagram */ 00377 goto freepbuf; 00378 #if IP_REASS_CHECK_OVERLAP 00379 } else if(iprh->start < iprh_tmp->end) { 00380 /* overlap: no need to keep the new datagram */ 00381 goto freepbuf; 00382 #endif /* IP_REASS_CHECK_OVERLAP */ 00383 } else { 00384 /* Check if the fragments received so far have no wholes. */ 00385 if (iprh_prev != NULL) { 00386 if (iprh_prev->end != iprh_tmp->start) { 00387 /* There is a fragment missing between the current 00388 * and the previous fragment */ 00389 valid = 0; 00390 } 00391 } 00392 } 00393 q = iprh_tmp->next_pbuf; 00394 iprh_prev = iprh_tmp; 00395 } 00396 00397 /* If q is NULL, then we made it to the end of the list. Determine what to do now */ 00398 if (q == NULL) { 00399 if (iprh_prev != NULL) { 00400 /* this is (for now), the fragment with the highest offset: 00401 * chain it to the last fragment */ 00402 #if IP_REASS_CHECK_OVERLAP 00403 LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start); 00404 #endif /* IP_REASS_CHECK_OVERLAP */ 00405 iprh_prev->next_pbuf = new_p; 00406 if (iprh_prev->end != iprh->start) { 00407 valid = 0; 00408 } 00409 } else { 00410 #if IP_REASS_CHECK_OVERLAP 00411 LWIP_ASSERT("no previous fragment, this must be the first fragment!", 00412 ipr->p == NULL); 00413 #endif /* IP_REASS_CHECK_OVERLAP */ 00414 /* this is the first fragment we ever received for this ip datagram */ 00415 ipr->p = new_p; 00416 } 00417 } 00418 00419 /* At this point, the validation part begins: */ 00420 /* If we already received the last fragment */ 00421 if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) { 00422 /* and had no wholes so far */ 00423 if (valid) { 00424 /* then check if the rest of the fragments is here */ 00425 /* Check if the queue starts with the first datagram */ 00426 if (((struct ip_reass_helper*)ipr->p->payload)->start != 0) { 00427 valid = 0; 00428 } else { 00429 /* and check that there are no wholes after this datagram */ 00430 iprh_prev = iprh; 00431 q = iprh->next_pbuf; 00432 while (q != NULL) { 00433 iprh = (struct ip_reass_helper*)q->payload; 00434 if (iprh_prev->end != iprh->start) { 00435 valid = 0; 00436 break; 00437 } 00438 iprh_prev = iprh; 00439 q = iprh->next_pbuf; 00440 } 00441 /* if still valid, all fragments are received 00442 * (because to the MF==0 already arrived */ 00443 if (valid) { 00444 LWIP_ASSERT("sanity check", ipr->p != NULL); 00445 LWIP_ASSERT("sanity check", 00446 ((struct ip_reass_helper*)ipr->p->payload) != iprh); 00447 LWIP_ASSERT("validate_datagram:next_pbuf!=NULL", 00448 iprh->next_pbuf == NULL); 00449 LWIP_ASSERT("validate_datagram:datagram end!=datagram len", 00450 iprh->end == ipr->datagram_len); 00451 } 00452 } 00453 } 00454 /* If valid is 0 here, there are some fragments missing in the middle 00455 * (since MF == 0 has already arrived). Such datagrams simply time out if 00456 * no more fragments are received... */ 00457 return valid; 00458 } 00459 /* If we come here, not all fragments were received, yet! */ 00460 return 0; /* not yet valid! */ 00461 #if IP_REASS_CHECK_OVERLAP 00462 freepbuf: 00463 ip_reass_pbufcount -= pbuf_clen(new_p); 00464 pbuf_free(new_p); 00465 return 0; 00466 #endif /* IP_REASS_CHECK_OVERLAP */ 00467 } 00468 00469 /** 00470 * Reassembles incoming IP fragments into an IP datagram. 00471 * 00472 * @param p points to a pbuf chain of the fragment 00473 * @return NULL if reassembly is incomplete, ? otherwise 00474 */ 00475 struct pbuf * 00476 ip_reass(struct pbuf *p) 00477 { 00478 struct pbuf *r; 00479 struct ip_hdr *fraghdr; 00480 struct ip_reassdata *ipr; 00481 struct ip_reass_helper *iprh; 00482 u16_t offset, len; 00483 u8_t clen; 00484 struct ip_reassdata *ipr_prev = NULL; 00485 00486 IPFRAG_STATS_INC(ip_frag.recv); 00487 snmp_inc_ipreasmreqds(); 00488 00489 fraghdr = (struct ip_hdr*)p->payload; 00490 00491 if ((IPH_HL(fraghdr) * 4) != IP_HLEN) { 00492 LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n")); 00493 IPFRAG_STATS_INC(ip_frag.err); 00494 goto nullreturn; 00495 } 00496 00497 offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; 00498 len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; 00499 00500 /* Check if we are allowed to enqueue more datagrams. */ 00501 clen = pbuf_clen(p); 00502 if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { 00503 #if IP_REASS_FREE_OLDEST 00504 if (!ip_reass_remove_oldest_datagram(fraghdr, clen) || 00505 ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS)) 00506 #endif /* IP_REASS_FREE_OLDEST */ 00507 { 00508 /* No datagram could be freed and still too many pbufs enqueued */ 00509 LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n", 00510 ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS)); 00511 IPFRAG_STATS_INC(ip_frag.memerr); 00512 /* @todo: send ICMP time exceeded here? */ 00513 /* drop this pbuf */ 00514 goto nullreturn; 00515 } 00516 } 00517 00518 /* Look for the datagram the fragment belongs to in the current datagram queue, 00519 * remembering the previous in the queue for later dequeueing. */ 00520 for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) { 00521 /* Check if the incoming fragment matches the one currently present 00522 in the reassembly buffer. If so, we proceed with copying the 00523 fragment into the buffer. */ 00524 if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) { 00525 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n", 00526 ntohs(IPH_ID(fraghdr)))); 00527 IPFRAG_STATS_INC(ip_frag.cachehit); 00528 break; 00529 } 00530 ipr_prev = ipr; 00531 } 00532 00533 if (ipr == NULL) { 00534 /* Enqueue a new datagram into the datagram queue */ 00535 ipr = ip_reass_enqueue_new_datagram(fraghdr, clen); 00536 /* Bail if unable to enqueue */ 00537 if(ipr == NULL) { 00538 goto nullreturn; 00539 } 00540 } else { 00541 if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) && 00542 ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) { 00543 /* ipr->iphdr is not the header from the first fragment, but fraghdr is 00544 * -> copy fraghdr into ipr->iphdr since we want to have the header 00545 * of the first fragment (for ICMP time exceeded and later, for copying 00546 * all options, if supported)*/ 00547 SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN); 00548 } 00549 } 00550 /* Track the current number of pbufs current 'in-flight', in order to limit 00551 the number of fragments that may be enqueued at any one time */ 00552 ip_reass_pbufcount += clen; 00553 00554 /* At this point, we have either created a new entry or pointing 00555 * to an existing one */ 00556 00557 /* check for 'no more fragments', and update queue entry*/ 00558 if ((IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0) { 00559 ipr->flags |= IP_REASS_FLAG_LASTFRAG; 00560 ipr->datagram_len = offset + len; 00561 LWIP_DEBUGF(IP_REASS_DEBUG, 00562 ("ip_reass: last fragment seen, total len %"S16_F"\n", 00563 ipr->datagram_len)); 00564 } 00565 /* find the right place to insert this pbuf */ 00566 /* @todo: trim pbufs if fragments are overlapping */ 00567 if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) { 00568 /* the totally last fragment (flag more fragments = 0) was received at least 00569 * once AND all fragments are received */ 00570 ipr->datagram_len += IP_HLEN; 00571 00572 /* save the second pbuf before copying the header over the pointer */ 00573 r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf; 00574 00575 /* copy the original ip header back to the first pbuf */ 00576 fraghdr = (struct ip_hdr*)(ipr->p->payload); 00577 SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN); 00578 IPH_LEN_SET(fraghdr, htons(ipr->datagram_len)); 00579 IPH_OFFSET_SET(fraghdr, 0); 00580 IPH_CHKSUM_SET(fraghdr, 0); 00581 /* @todo: do we need to set calculate the correct checksum? */ 00582 IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN)); 00583 00584 p = ipr->p; 00585 00586 /* chain together the pbufs contained within the reass_data list. */ 00587 while(r != NULL) { 00588 iprh = (struct ip_reass_helper*)r->payload; 00589 00590 /* hide the ip header for every succeding fragment */ 00591 pbuf_header(r, -IP_HLEN); 00592 pbuf_cat(p, r); 00593 r = iprh->next_pbuf; 00594 } 00595 /* release the sources allocate for the fragment queue entry */ 00596 ip_reass_dequeue_datagram(ipr, ipr_prev); 00597 00598 /* and adjust the number of pbufs currently queued for reassembly. */ 00599 ip_reass_pbufcount -= pbuf_clen(p); 00600 00601 /* Return the pbuf chain */ 00602 return p; 00603 } 00604 /* the datagram is not (yet?) reassembled completely */ 00605 LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount)); 00606 return NULL; 00607 00608 nullreturn: 00609 LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n")); 00610 IPFRAG_STATS_INC(ip_frag.drop); 00611 pbuf_free(p); 00612 return NULL; 00613 } 00614 #endif /* IP_REASSEMBLY */ 00615 00616 #if IP_FRAG 00617 #if IP_FRAG_USES_STATIC_BUF 00618 static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU + MEM_ALIGNMENT - 1)]; 00619 #else /* IP_FRAG_USES_STATIC_BUF */ 00620 00621 #if !LWIP_NETIF_TX_SINGLE_PBUF 00622 /** Allocate a new struct pbuf_custom_ref */ 00623 static struct pbuf_custom_ref* 00624 ip_frag_alloc_pbuf_custom_ref(void) 00625 { 00626 return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF); 00627 } 00628 00629 /** Free a struct pbuf_custom_ref */ 00630 static void 00631 ip_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p) 00632 { 00633 LWIP_ASSERT("p != NULL", p != NULL); 00634 memp_free(MEMP_FRAG_PBUF, p); 00635 } 00636 00637 /** Free-callback function to free a 'struct pbuf_custom_ref', called by 00638 * pbuf_free. */ 00639 static void 00640 ipfrag_free_pbuf_custom(struct pbuf *p) 00641 { 00642 struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p; 00643 LWIP_ASSERT("pcr != NULL", pcr != NULL); 00644 LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p); 00645 if (pcr->original != NULL) { 00646 pbuf_free(pcr->original); 00647 } 00648 ip_frag_free_pbuf_custom_ref(pcr); 00649 } 00650 #endif /* !LWIP_NETIF_TX_SINGLE_PBUF */ 00651 #endif /* IP_FRAG_USES_STATIC_BUF */ 00652 00653 /** 00654 * Fragment an IP datagram if too large for the netif. 00655 * 00656 * Chop the datagram in MTU sized chunks and send them in order 00657 * by using a fixed size static memory buffer (PBUF_REF) or 00658 * point PBUF_REFs into p (depending on IP_FRAG_USES_STATIC_BUF). 00659 * 00660 * @param p ip packet to send 00661 * @param netif the netif on which to send 00662 * @param dest destination ip address to which to send 00663 * 00664 * @return ERR_OK if sent successfully, err_t otherwise 00665 */ 00666 err_t 00667 ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest) 00668 { 00669 struct pbuf *rambuf; 00670 #if IP_FRAG_USES_STATIC_BUF 00671 struct pbuf *header; 00672 #else 00673 #if !LWIP_NETIF_TX_SINGLE_PBUF 00674 struct pbuf *newpbuf; 00675 #endif 00676 struct ip_hdr *original_iphdr; 00677 #endif 00678 struct ip_hdr *iphdr; 00679 u16_t nfb; 00680 u16_t left, cop; 00681 u16_t mtu = netif->mtu; 00682 u16_t ofo, omf; 00683 u16_t last; 00684 u16_t poff = IP_HLEN; 00685 u16_t tmp; 00686 #if !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF 00687 u16_t newpbuflen = 0; 00688 u16_t left_to_copy; 00689 #endif 00690 00691 /* Get a RAM based MTU sized pbuf */ 00692 #if IP_FRAG_USES_STATIC_BUF 00693 /* When using a static buffer, we use a PBUF_REF, which we will 00694 * use to reference the packet (without link header). 00695 * Layer and length is irrelevant. 00696 */ 00697 rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF); 00698 if (rambuf == NULL) { 00699 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n")); 00700 return ERR_MEM; 00701 } 00702 rambuf->tot_len = rambuf->len = mtu; 00703 rambuf->payload = LWIP_MEM_ALIGN((void *)buf); 00704 00705 /* Copy the IP header in it */ 00706 iphdr = (struct ip_hdr *)rambuf->payload; 00707 SMEMCPY(iphdr, p->payload, IP_HLEN); 00708 #else /* IP_FRAG_USES_STATIC_BUF */ 00709 original_iphdr = (struct ip_hdr *)p->payload; 00710 iphdr = original_iphdr; 00711 #endif /* IP_FRAG_USES_STATIC_BUF */ 00712 00713 /* Save original offset */ 00714 tmp = ntohs(IPH_OFFSET(iphdr)); 00715 ofo = tmp & IP_OFFMASK; 00716 omf = tmp & IP_MF; 00717 00718 left = p->tot_len - IP_HLEN; 00719 00720 nfb = (mtu - IP_HLEN) / 8; 00721 00722 while (left) { 00723 last = (left <= mtu - IP_HLEN); 00724 00725 /* Set new offset and MF flag */ 00726 tmp = omf | (IP_OFFMASK & (ofo)); 00727 if (!last) { 00728 tmp = tmp | IP_MF; 00729 } 00730 00731 /* Fill this fragment */ 00732 cop = last ? left : nfb * 8; 00733 00734 #if IP_FRAG_USES_STATIC_BUF 00735 poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff); 00736 #else /* IP_FRAG_USES_STATIC_BUF */ 00737 #if LWIP_NETIF_TX_SINGLE_PBUF 00738 rambuf = pbuf_alloc(PBUF_IP, cop, PBUF_RAM); 00739 if (rambuf == NULL) { 00740 return ERR_MEM; 00741 } 00742 LWIP_ASSERT("this needs a pbuf in one piece!", 00743 (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL)); 00744 poff += pbuf_copy_partial(p, rambuf->payload, cop, poff); 00745 /* make room for the IP header */ 00746 if(pbuf_header(rambuf, IP_HLEN)) { 00747 pbuf_free(rambuf); 00748 return ERR_MEM; 00749 } 00750 /* fill in the IP header */ 00751 SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN); 00752 iphdr = rambuf->payload; 00753 #else /* LWIP_NETIF_TX_SINGLE_PBUF */ 00754 /* When not using a static buffer, create a chain of pbufs. 00755 * The first will be a PBUF_RAM holding the link and IP header. 00756 * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged, 00757 * but limited to the size of an mtu. 00758 */ 00759 rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM); 00760 if (rambuf == NULL) { 00761 return ERR_MEM; 00762 } 00763 LWIP_ASSERT("this needs a pbuf in one piece!", 00764 (p->len >= (IP_HLEN))); 00765 SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN); 00766 iphdr = (struct ip_hdr *)rambuf->payload; 00767 00768 /* Can just adjust p directly for needed offset. */ 00769 p->payload = (u8_t *)p->payload + poff; 00770 p->len -= poff; 00771 00772 left_to_copy = cop; 00773 while (left_to_copy) { 00774 struct pbuf_custom_ref *pcr; 00775 newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len; 00776 /* Is this pbuf already empty? */ 00777 if (!newpbuflen) { 00778 p = p->next; 00779 continue; 00780 } 00781 pcr = ip_frag_alloc_pbuf_custom_ref(); 00782 if (pcr == NULL) { 00783 pbuf_free(rambuf); 00784 return ERR_MEM; 00785 } 00786 /* Mirror this pbuf, although we might not need all of it. */ 00787 newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen); 00788 if (newpbuf == NULL) { 00789 ip_frag_free_pbuf_custom_ref(pcr); 00790 pbuf_free(rambuf); 00791 return ERR_MEM; 00792 } 00793 pbuf_ref(p); 00794 pcr->original = p; 00795 pcr->pc.custom_free_function = ipfrag_free_pbuf_custom; 00796 00797 /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain 00798 * so that it is removed when pbuf_dechain is later called on rambuf. 00799 */ 00800 pbuf_cat(rambuf, newpbuf); 00801 left_to_copy -= newpbuflen; 00802 if (left_to_copy) { 00803 p = p->next; 00804 } 00805 } 00806 poff = newpbuflen; 00807 #endif /* LWIP_NETIF_TX_SINGLE_PBUF */ 00808 #endif /* IP_FRAG_USES_STATIC_BUF */ 00809 00810 /* Correct header */ 00811 IPH_OFFSET_SET(iphdr, htons(tmp)); 00812 IPH_LEN_SET(iphdr, htons(cop + IP_HLEN)); 00813 IPH_CHKSUM_SET(iphdr, 0); 00814 IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); 00815 00816 #if IP_FRAG_USES_STATIC_BUF 00817 if (last) { 00818 pbuf_realloc(rambuf, left + IP_HLEN); 00819 } 00820 00821 /* This part is ugly: we alloc a RAM based pbuf for 00822 * the link level header for each chunk and then 00823 * free it.A PBUF_ROM style pbuf for which pbuf_header 00824 * worked would make things simpler. 00825 */ 00826 header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM); 00827 if (header != NULL) { 00828 pbuf_chain(header, rambuf); 00829 netif->output(netif, header, dest); 00830 IPFRAG_STATS_INC(ip_frag.xmit); 00831 snmp_inc_ipfragcreates(); 00832 pbuf_free(header); 00833 } else { 00834 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n")); 00835 pbuf_free(rambuf); 00836 return ERR_MEM; 00837 } 00838 #else /* IP_FRAG_USES_STATIC_BUF */ 00839 /* No need for separate header pbuf - we allowed room for it in rambuf 00840 * when allocated. 00841 */ 00842 netif->output(netif, rambuf, dest); 00843 IPFRAG_STATS_INC(ip_frag.xmit); 00844 00845 /* Unfortunately we can't reuse rambuf - the hardware may still be 00846 * using the buffer. Instead we free it (and the ensuing chain) and 00847 * recreate it next time round the loop. If we're lucky the hardware 00848 * will have already sent the packet, the free will really free, and 00849 * there will be zero memory penalty. 00850 */ 00851 00852 pbuf_free(rambuf); 00853 #endif /* IP_FRAG_USES_STATIC_BUF */ 00854 left -= cop; 00855 ofo += nfb; 00856 } 00857 #if IP_FRAG_USES_STATIC_BUF 00858 pbuf_free(rambuf); 00859 #endif /* IP_FRAG_USES_STATIC_BUF */ 00860 snmp_inc_ipfragoks(); 00861 return ERR_OK; 00862 } 00863 #endif /* IP_FRAG */
Generated on Tue Jul 12 2022 13:44:40 by
