Example program with HTTPServer and sensor data streaming over TCPSockets, using Donatien Garnier's Net APIs and services code on top of LWIP. Files StreamServer.h and .cpp encapsulate streaming over TCPSockets. Broadcast is done by sendToAll(), and all incoming data is echoed back to the client. Echo code can be replaced with some remote control of the streaming interface. See main() that shows how to periodically send some data to all subscribed clients. To subscribe, a client should open a socket at <mbed_ip> port 123. I used few lines in TCL code to set up a quick sink for the data. HTTP files are served on port 80 concurrently to the streaming.
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 to be packed since it has to fit inside the IP header. 00084 */ 00085 #ifdef PACK_STRUCT_USE_INCLUDES 00086 # include "arch/bpstruct.h" 00087 #endif 00088 PACK_STRUCT_BEGIN 00089 struct ip_reass_helper { 00090 PACK_STRUCT_FIELD(struct pbuf *next_pbuf); 00091 PACK_STRUCT_FIELD(u16_t start); 00092 PACK_STRUCT_FIELD(u16_t end); 00093 } PACK_STRUCT_STRUCT; 00094 PACK_STRUCT_END 00095 #ifdef PACK_STRUCT_USE_INCLUDES 00096 # include "arch/epstruct.h" 00097 #endif 00098 00099 #define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \ 00100 (ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \ 00101 ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \ 00102 IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0 00103 00104 /* global variables */ 00105 static struct ip_reassdata *reassdatagrams; 00106 static u16_t ip_reass_pbufcount; 00107 00108 /* function prototypes */ 00109 static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev); 00110 static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev); 00111 00112 /** 00113 * Reassembly timer base function 00114 * for both NO_SYS == 0 and 1 (!). 00115 * 00116 * Should be called every 1000 msec (defined by IP_TMR_INTERVAL). 00117 */ 00118 void 00119 ip_reass_tmr(void) 00120 { 00121 struct ip_reassdata *r, *prev = NULL; 00122 00123 r = reassdatagrams; 00124 while (r != NULL) { 00125 /* Decrement the timer. Once it reaches 0, 00126 * clean up the incomplete fragment assembly */ 00127 if (r->timer > 0) { 00128 r->timer--; 00129 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer)); 00130 prev = r; 00131 r = r->next; 00132 } else { 00133 /* reassembly timed out */ 00134 struct ip_reassdata *tmp; 00135 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n")); 00136 tmp = r; 00137 /* get the next pointer before freeing */ 00138 r = r->next; 00139 /* free the helper struct and all enqueued pbufs */ 00140 ip_reass_free_complete_datagram(tmp, prev); 00141 } 00142 } 00143 } 00144 00145 /** 00146 * Free a datagram (struct ip_reassdata) and all its pbufs. 00147 * Updates the total count of enqueued pbufs (ip_reass_pbufcount), 00148 * SNMP counters and sends an ICMP time exceeded packet. 00149 * 00150 * @param ipr datagram to free 00151 * @param prev the previous datagram in the linked list 00152 * @return the number of pbufs freed 00153 */ 00154 static int 00155 ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) 00156 { 00157 u16_t pbufs_freed = 0; 00158 u8_t clen; 00159 struct pbuf *p; 00160 struct ip_reass_helper *iprh; 00161 00162 LWIP_ASSERT("prev != ipr", prev != ipr); 00163 if (prev != NULL) { 00164 LWIP_ASSERT("prev->next == ipr", prev->next == ipr); 00165 } 00166 00167 snmp_inc_ipreasmfails(); 00168 #if LWIP_ICMP 00169 iprh = (struct ip_reass_helper *)ipr->p->payload; 00170 if (iprh->start == 0) { 00171 /* The first fragment was received, send ICMP time exceeded. */ 00172 /* First, de-queue the first pbuf from r->p. */ 00173 p = ipr->p; 00174 ipr->p = iprh->next_pbuf; 00175 /* Then, copy the original header into it. */ 00176 SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN); 00177 icmp_time_exceeded(p, ICMP_TE_FRAG); 00178 clen = pbuf_clen(p); 00179 LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); 00180 pbufs_freed += clen; 00181 pbuf_free(p); 00182 } 00183 #endif /* LWIP_ICMP */ 00184 00185 /* First, free all received pbufs. The individual pbufs need to be released 00186 separately as they have not yet been chained */ 00187 p = ipr->p; 00188 while (p != NULL) { 00189 struct pbuf *pcur; 00190 iprh = (struct ip_reass_helper *)p->payload; 00191 pcur = p; 00192 /* get the next pointer before freeing */ 00193 p = iprh->next_pbuf; 00194 clen = pbuf_clen(pcur); 00195 LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); 00196 pbufs_freed += clen; 00197 pbuf_free(pcur); 00198 } 00199 /* Then, unchain the struct ip_reassdata from the list and free it. */ 00200 ip_reass_dequeue_datagram(ipr, prev); 00201 LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed); 00202 ip_reass_pbufcount -= pbufs_freed; 00203 00204 return pbufs_freed; 00205 } 00206 00207 #if IP_REASS_FREE_OLDEST 00208 /** 00209 * Free the oldest datagram to make room for enqueueing new fragments. 00210 * The datagram 'fraghdr' belongs to is not freed! 00211 * 00212 * @param fraghdr IP header of the current fragment 00213 * @param pbufs_needed number of pbufs needed to enqueue 00214 * (used for freeing other datagrams if not enough space) 00215 * @return the number of pbufs freed 00216 */ 00217 static int 00218 ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed) 00219 { 00220 /* @todo Can't we simply remove the last datagram in the 00221 * linked list behind reassdatagrams? 00222 */ 00223 struct ip_reassdata *r, *oldest, *prev; 00224 int pbufs_freed = 0, pbufs_freed_current; 00225 int other_datagrams; 00226 00227 /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs, 00228 * but don't free the datagram that 'fraghdr' belongs to! */ 00229 do { 00230 oldest = NULL; 00231 prev = NULL; 00232 other_datagrams = 0; 00233 r = reassdatagrams; 00234 while (r != NULL) { 00235 if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) { 00236 /* Not the same datagram as fraghdr */ 00237 other_datagrams++; 00238 if (oldest == NULL) { 00239 oldest = r; 00240 } else if (r->timer <= oldest->timer) { 00241 /* older than the previous oldest */ 00242 oldest = r; 00243 } 00244 } 00245 if (r->next != NULL) { 00246 prev = r; 00247 } 00248 r = r->next; 00249 } 00250 if (oldest != NULL) { 00251 pbufs_freed_current = ip_reass_free_complete_datagram(oldest, prev); 00252 pbufs_freed += pbufs_freed_current; 00253 } 00254 } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1)); 00255 return pbufs_freed; 00256 } 00257 #endif /* IP_REASS_FREE_OLDEST */ 00258 00259 /** 00260 * Enqueues a new fragment into the fragment queue 00261 * @param fraghdr points to the new fragments IP hdr 00262 * @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space) 00263 * @return A pointer to the queue location into which the fragment was enqueued 00264 */ 00265 static struct ip_reassdata* 00266 ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen) 00267 { 00268 struct ip_reassdata* ipr; 00269 /* No matching previous fragment found, allocate a new reassdata struct */ 00270 ipr = (struct ip_reassdata*) memp_malloc(MEMP_REASSDATA); 00271 if (ipr == NULL) { 00272 #if IP_REASS_FREE_OLDEST 00273 if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) { 00274 ipr = (struct ip_reassdata*) memp_malloc(MEMP_REASSDATA); 00275 } 00276 if (ipr == NULL) 00277 #endif /* IP_REASS_FREE_OLDEST */ 00278 { 00279 IPFRAG_STATS_INC(ip_frag.memerr); 00280 LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n")); 00281 return NULL; 00282 } 00283 } 00284 memset(ipr, 0, sizeof(struct ip_reassdata)); 00285 ipr->timer = IP_REASS_MAXAGE; 00286 00287 /* enqueue the new structure to the front of the list */ 00288 ipr->next = reassdatagrams; 00289 reassdatagrams = ipr; 00290 /* copy the ip header for later tests and input */ 00291 /* @todo: no ip options supported? */ 00292 SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN); 00293 return ipr; 00294 } 00295 00296 /** 00297 * Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs. 00298 * @param ipr points to the queue entry to dequeue 00299 */ 00300 static void 00301 ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) 00302 { 00303 00304 /* dequeue the reass struct */ 00305 if (reassdatagrams == ipr) { 00306 /* it was the first in the list */ 00307 reassdatagrams = ipr->next; 00308 } else { 00309 /* it wasn't the first, so it must have a valid 'prev' */ 00310 LWIP_ASSERT("sanity check linked list", prev != NULL); 00311 prev->next = ipr->next; 00312 } 00313 00314 /* now we can free the ip_reass struct */ 00315 memp_free(MEMP_REASSDATA, ipr); 00316 } 00317 00318 /** 00319 * Chain a new pbuf into the pbuf list that composes the datagram. The pbuf list 00320 * will grow over time as new pbufs are rx. 00321 * Also checks that the datagram passes basic continuity checks (if the last 00322 * fragment was received at least once). 00323 * @param root_p points to the 'root' pbuf for the current datagram being assembled. 00324 * @param new_p points to the pbuf for the current fragment 00325 * @return 0 if invalid, >0 otherwise 00326 */ 00327 static int 00328 ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p) 00329 { 00330 struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL; 00331 struct pbuf *q; 00332 u16_t offset,len; 00333 struct ip_hdr *fraghdr; 00334 int valid = 1; 00335 00336 /* Extract length and fragment offset from current fragment */ 00337 fraghdr = (struct ip_hdr*)new_p->payload; 00338 len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; 00339 offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; 00340 00341 /* overwrite the fragment's ip header from the pbuf with our helper struct, 00342 * and setup the embedded helper structure. */ 00343 /* make sure the struct ip_reass_helper fits into the IP header */ 00344 LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN", 00345 sizeof(struct ip_reass_helper) <= IP_HLEN); 00346 iprh = (struct ip_reass_helper*)new_p->payload; 00347 iprh->next_pbuf = NULL; 00348 iprh->start = offset; 00349 iprh->end = offset + len; 00350 00351 /* Iterate through until we either get to the end of the list (append), 00352 * or we find on with a larger offset (insert). */ 00353 for (q = ipr->p; q != NULL;) { 00354 iprh_tmp = (struct ip_reass_helper*)q->payload; 00355 if (iprh->start < iprh_tmp->start) { 00356 /* the new pbuf should be inserted before this */ 00357 iprh->next_pbuf = q; 00358 if (iprh_prev != NULL) { 00359 /* not the fragment with the lowest offset */ 00360 #if IP_REASS_CHECK_OVERLAP 00361 if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) { 00362 /* fragment overlaps with previous or following, throw away */ 00363 goto freepbuf; 00364 } 00365 #endif /* IP_REASS_CHECK_OVERLAP */ 00366 iprh_prev->next_pbuf = new_p; 00367 } else { 00368 /* fragment with the lowest offset */ 00369 ipr->p = new_p; 00370 } 00371 break; 00372 } else if(iprh->start == iprh_tmp->start) { 00373 /* received the same datagram twice: no need to keep the datagram */ 00374 goto freepbuf; 00375 #if IP_REASS_CHECK_OVERLAP 00376 } else if(iprh->start < iprh_tmp->end) { 00377 /* overlap: no need to keep the new datagram */ 00378 goto freepbuf; 00379 #endif /* IP_REASS_CHECK_OVERLAP */ 00380 } else { 00381 /* Check if the fragments received so far have no wholes. */ 00382 if (iprh_prev != NULL) { 00383 if (iprh_prev->end != iprh_tmp->start) { 00384 /* There is a fragment missing between the current 00385 * and the previous fragment */ 00386 valid = 0; 00387 } 00388 } 00389 } 00390 q = iprh_tmp->next_pbuf; 00391 iprh_prev = iprh_tmp; 00392 } 00393 00394 /* If q is NULL, then we made it to the end of the list. Determine what to do now */ 00395 if (q == NULL) { 00396 if (iprh_prev != NULL) { 00397 /* this is (for now), the fragment with the highest offset: 00398 * chain it to the last fragment */ 00399 #if IP_REASS_CHECK_OVERLAP 00400 LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start); 00401 #endif /* IP_REASS_CHECK_OVERLAP */ 00402 iprh_prev->next_pbuf = new_p; 00403 if (iprh_prev->end != iprh->start) { 00404 valid = 0; 00405 } 00406 } else { 00407 #if IP_REASS_CHECK_OVERLAP 00408 LWIP_ASSERT("no previous fragment, this must be the first fragment!", 00409 ipr->p == NULL); 00410 #endif /* IP_REASS_CHECK_OVERLAP */ 00411 /* this is the first fragment we ever received for this ip datagram */ 00412 ipr->p = new_p; 00413 } 00414 } 00415 00416 /* At this point, the validation part begins: */ 00417 /* If we already received the last fragment */ 00418 if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) { 00419 /* and had no wholes so far */ 00420 if (valid) { 00421 /* then check if the rest of the fragments is here */ 00422 /* Check if the queue starts with the first datagram */ 00423 if (((struct ip_reass_helper*)ipr->p->payload)->start != 0) { 00424 valid = 0; 00425 } else { 00426 /* and check that there are no wholes after this datagram */ 00427 iprh_prev = iprh; 00428 q = iprh->next_pbuf; 00429 while (q != NULL) { 00430 iprh = (struct ip_reass_helper*)q->payload; 00431 if (iprh_prev->end != iprh->start) { 00432 valid = 0; 00433 break; 00434 } 00435 iprh_prev = iprh; 00436 q = iprh->next_pbuf; 00437 } 00438 /* if still valid, all fragments are received 00439 * (because to the MF==0 already arrived */ 00440 if (valid) { 00441 LWIP_ASSERT("sanity check", ipr->p != NULL); 00442 LWIP_ASSERT("sanity check", 00443 ((struct ip_reass_helper*)ipr->p->payload) != iprh); 00444 LWIP_ASSERT("validate_datagram:next_pbuf!=NULL", 00445 iprh->next_pbuf == NULL); 00446 LWIP_ASSERT("validate_datagram:datagram end!=datagram len", 00447 iprh->end == ipr->datagram_len); 00448 } 00449 } 00450 } 00451 /* If valid is 0 here, there are some fragments missing in the middle 00452 * (since MF == 0 has already arrived). Such datagrams simply time out if 00453 * no more fragments are received... */ 00454 return valid; 00455 } 00456 /* If we come here, not all fragments were received, yet! */ 00457 return 0; /* not yet valid! */ 00458 #if IP_REASS_CHECK_OVERLAP 00459 freepbuf: 00460 ip_reass_pbufcount -= pbuf_clen(new_p); 00461 pbuf_free(new_p); 00462 return 0; 00463 #endif /* IP_REASS_CHECK_OVERLAP */ 00464 } 00465 00466 /** 00467 * Reassembles incoming IP fragments into an IP datagram. 00468 * 00469 * @param p points to a pbuf chain of the fragment 00470 * @return NULL if reassembly is incomplete, ? otherwise 00471 */ 00472 struct pbuf * 00473 ip_reass(struct pbuf *p) 00474 { 00475 struct pbuf *r; 00476 struct ip_hdr *fraghdr; 00477 struct ip_reassdata *ipr; 00478 struct ip_reass_helper *iprh; 00479 u16_t offset, len; 00480 u8_t clen; 00481 struct ip_reassdata *ipr_prev = NULL; 00482 00483 IPFRAG_STATS_INC(ip_frag.recv); 00484 snmp_inc_ipreasmreqds(); 00485 00486 fraghdr = (struct ip_hdr*)p->payload; 00487 00488 if ((IPH_HL(fraghdr) * 4) != IP_HLEN) { 00489 LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n")); 00490 IPFRAG_STATS_INC(ip_frag.err); 00491 goto nullreturn; 00492 } 00493 00494 offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8; 00495 len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4; 00496 00497 /* Check if we are allowed to enqueue more datagrams. */ 00498 clen = pbuf_clen(p); 00499 if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) { 00500 #if IP_REASS_FREE_OLDEST 00501 if (!ip_reass_remove_oldest_datagram(fraghdr, clen) || 00502 ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS)) 00503 #endif /* IP_REASS_FREE_OLDEST */ 00504 { 00505 /* No datagram could be freed and still too many pbufs enqueued */ 00506 LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n", 00507 ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS)); 00508 IPFRAG_STATS_INC(ip_frag.memerr); 00509 /* @todo: send ICMP time exceeded here? */ 00510 /* drop this pbuf */ 00511 goto nullreturn; 00512 } 00513 } 00514 00515 /* Look for the datagram the fragment belongs to in the current datagram queue, 00516 * remembering the previous in the queue for later dequeueing. */ 00517 for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) { 00518 /* Check if the incoming fragment matches the one currently present 00519 in the reassembly buffer. If so, we proceed with copying the 00520 fragment into the buffer. */ 00521 if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) { 00522 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n", 00523 ntohs(IPH_ID(fraghdr)))); 00524 IPFRAG_STATS_INC(ip_frag.cachehit); 00525 break; 00526 } 00527 ipr_prev = ipr; 00528 } 00529 00530 if (ipr == NULL) { 00531 /* Enqueue a new datagram into the datagram queue */ 00532 ipr = ip_reass_enqueue_new_datagram(fraghdr, clen); 00533 /* Bail if unable to enqueue */ 00534 if(ipr == NULL) { 00535 goto nullreturn; 00536 } 00537 } else { 00538 if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) && 00539 ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) { 00540 /* ipr->iphdr is not the header from the first fragment, but fraghdr is 00541 * -> copy fraghdr into ipr->iphdr since we want to have the header 00542 * of the first fragment (for ICMP time exceeded and later, for copying 00543 * all options, if supported)*/ 00544 SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN); 00545 } 00546 } 00547 /* Track the current number of pbufs current 'in-flight', in order to limit 00548 the number of fragments that may be enqueued at any one time */ 00549 ip_reass_pbufcount += clen; 00550 00551 /* At this point, we have either created a new entry or pointing 00552 * to an existing one */ 00553 00554 /* check for 'no more fragments', and update queue entry*/ 00555 if ((ntohs(IPH_OFFSET(fraghdr)) & IP_MF) == 0) { 00556 ipr->flags |= IP_REASS_FLAG_LASTFRAG; 00557 ipr->datagram_len = offset + len; 00558 LWIP_DEBUGF(IP_REASS_DEBUG, 00559 ("ip_reass: last fragment seen, total len %"S16_F"\n", 00560 ipr->datagram_len)); 00561 } 00562 /* find the right place to insert this pbuf */ 00563 /* @todo: trim pbufs if fragments are overlapping */ 00564 if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) { 00565 /* the totally last fragment (flag more fragments = 0) was received at least 00566 * once AND all fragments are received */ 00567 ipr->datagram_len += IP_HLEN; 00568 00569 /* save the second pbuf before copying the header over the pointer */ 00570 r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf; 00571 00572 /* copy the original ip header back to the first pbuf */ 00573 fraghdr = (struct ip_hdr*)(ipr->p->payload); 00574 SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN); 00575 IPH_LEN_SET(fraghdr, htons(ipr->datagram_len)); 00576 IPH_OFFSET_SET(fraghdr, 0); 00577 IPH_CHKSUM_SET(fraghdr, 0); 00578 /* @todo: do we need to set calculate the correct checksum? */ 00579 IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN)); 00580 00581 p = ipr->p; 00582 00583 /* chain together the pbufs contained within the reass_data list. */ 00584 while(r != NULL) { 00585 iprh = (struct ip_reass_helper*)r->payload; 00586 00587 /* hide the ip header for every succeding fragment */ 00588 pbuf_header(r, -IP_HLEN); 00589 pbuf_cat(p, r); 00590 r = iprh->next_pbuf; 00591 } 00592 /* release the sources allocate for the fragment queue entry */ 00593 ip_reass_dequeue_datagram(ipr, ipr_prev); 00594 00595 /* and adjust the number of pbufs currently queued for reassembly. */ 00596 ip_reass_pbufcount -= pbuf_clen(p); 00597 00598 /* Return the pbuf chain */ 00599 return p; 00600 } 00601 /* the datagram is not (yet?) reassembled completely */ 00602 LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount)); 00603 return NULL; 00604 00605 nullreturn: 00606 LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n")); 00607 IPFRAG_STATS_INC(ip_frag.drop); 00608 pbuf_free(p); 00609 return NULL; 00610 } 00611 #endif /* IP_REASSEMBLY */ 00612 00613 #if IP_FRAG 00614 #if IP_FRAG_USES_STATIC_BUF 00615 static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU + MEM_ALIGNMENT - 1)]; 00616 #endif /* IP_FRAG_USES_STATIC_BUF */ 00617 00618 /** 00619 * Fragment an IP datagram if too large for the netif. 00620 * 00621 * Chop the datagram in MTU sized chunks and send them in order 00622 * by using a fixed size static memory buffer (PBUF_REF) or 00623 * point PBUF_REFs into p (depending on IP_FRAG_USES_STATIC_BUF). 00624 * 00625 * @param p ip packet to send 00626 * @param netif the netif on which to send 00627 * @param dest destination ip address to which to send 00628 * 00629 * @return ERR_OK if sent successfully, err_t otherwise 00630 */ 00631 err_t 00632 ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest) 00633 { 00634 struct pbuf *rambuf; 00635 #if IP_FRAG_USES_STATIC_BUF 00636 struct pbuf *header; 00637 #else 00638 struct pbuf *newpbuf; 00639 struct ip_hdr *original_iphdr; 00640 #endif 00641 struct ip_hdr *iphdr; 00642 u16_t nfb; 00643 u16_t left, cop; 00644 u16_t mtu = netif->mtu; 00645 u16_t ofo, omf; 00646 u16_t last; 00647 u16_t poff = IP_HLEN; 00648 u16_t tmp; 00649 #if !IP_FRAG_USES_STATIC_BUF 00650 u16_t newpbuflen = 0; 00651 u16_t left_to_copy; 00652 #endif 00653 00654 /* Get a RAM based MTU sized pbuf */ 00655 #if IP_FRAG_USES_STATIC_BUF 00656 /* When using a static buffer, we use a PBUF_REF, which we will 00657 * use to reference the packet (without link header). 00658 * Layer and length is irrelevant. 00659 */ 00660 rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF); 00661 if (rambuf == NULL) { 00662 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n")); 00663 return ERR_MEM; 00664 } 00665 rambuf->tot_len = rambuf->len = mtu; 00666 rambuf->payload = LWIP_MEM_ALIGN((void *)buf); 00667 00668 /* Copy the IP header in it */ 00669 iphdr = (struct ip_hdr*) rambuf->payload; 00670 SMEMCPY(iphdr, p->payload, IP_HLEN); 00671 #else /* IP_FRAG_USES_STATIC_BUF */ 00672 original_iphdr = (struct ip_hdr*) p->payload; 00673 iphdr = original_iphdr; 00674 #endif /* IP_FRAG_USES_STATIC_BUF */ 00675 00676 /* Save original offset */ 00677 tmp = ntohs(IPH_OFFSET(iphdr)); 00678 ofo = tmp & IP_OFFMASK; 00679 omf = tmp & IP_MF; 00680 00681 left = p->tot_len - IP_HLEN; 00682 00683 nfb = (mtu - IP_HLEN) / 8; 00684 00685 while (left) { 00686 last = (left <= mtu - IP_HLEN); 00687 00688 /* Set new offset and MF flag */ 00689 tmp = omf | (IP_OFFMASK & (ofo)); 00690 if (!last) { 00691 tmp = tmp | IP_MF; 00692 } 00693 00694 /* Fill this fragment */ 00695 cop = last ? left : nfb * 8; 00696 00697 #if IP_FRAG_USES_STATIC_BUF 00698 poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff); 00699 #else /* IP_FRAG_USES_STATIC_BUF */ 00700 /* When not using a static buffer, create a chain of pbufs. 00701 * The first will be a PBUF_RAM holding the link and IP header. 00702 * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged, 00703 * but limited to the size of an mtu. 00704 */ 00705 rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM); 00706 if (rambuf == NULL) { 00707 return ERR_MEM; 00708 } 00709 LWIP_ASSERT("this needs a pbuf in one piece!", 00710 (p->len >= (IP_HLEN))); 00711 SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN); 00712 iphdr = (struct ip_hdr*) rambuf->payload; 00713 00714 /* Can just adjust p directly for needed offset. */ 00715 p->payload = (u8_t *)p->payload + poff; 00716 p->len -= poff; 00717 00718 left_to_copy = cop; 00719 while (left_to_copy) { 00720 newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len; 00721 /* Is this pbuf already empty? */ 00722 if (!newpbuflen) { 00723 p = p->next; 00724 continue; 00725 } 00726 newpbuf = pbuf_alloc(PBUF_RAW, 0, PBUF_REF); 00727 if (newpbuf == NULL) { 00728 pbuf_free(rambuf); 00729 return ERR_MEM; 00730 } 00731 /* Mirror this pbuf, although we might not need all of it. */ 00732 newpbuf->payload = p->payload; 00733 newpbuf->len = newpbuf->tot_len = newpbuflen; 00734 /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain 00735 * so that it is removed when pbuf_dechain is later called on rambuf. 00736 */ 00737 pbuf_cat(rambuf, newpbuf); 00738 left_to_copy -= newpbuflen; 00739 if (left_to_copy) { 00740 p = p->next; 00741 } 00742 } 00743 poff = newpbuflen; 00744 #endif /* IP_FRAG_USES_STATIC_BUF */ 00745 00746 /* Correct header */ 00747 IPH_OFFSET_SET(iphdr, htons(tmp)); 00748 IPH_LEN_SET(iphdr, htons(cop + IP_HLEN)); 00749 IPH_CHKSUM_SET(iphdr, 0); 00750 IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); 00751 00752 #if IP_FRAG_USES_STATIC_BUF 00753 if (last) { 00754 pbuf_realloc(rambuf, left + IP_HLEN); 00755 } 00756 00757 /* This part is ugly: we alloc a RAM based pbuf for 00758 * the link level header for each chunk and then 00759 * free it.A PBUF_ROM style pbuf for which pbuf_header 00760 * worked would make things simpler. 00761 */ 00762 header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM); 00763 if (header != NULL) { 00764 pbuf_chain(header, rambuf); 00765 netif->output(netif, header, dest); 00766 IPFRAG_STATS_INC(ip_frag.xmit); 00767 snmp_inc_ipfragcreates(); 00768 pbuf_free(header); 00769 } else { 00770 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n")); 00771 pbuf_free(rambuf); 00772 return ERR_MEM; 00773 } 00774 #else /* IP_FRAG_USES_STATIC_BUF */ 00775 /* No need for separate header pbuf - we allowed room for it in rambuf 00776 * when allocated. 00777 */ 00778 netif->output(netif, rambuf, dest); 00779 IPFRAG_STATS_INC(ip_frag.xmit); 00780 00781 /* Unfortunately we can't reuse rambuf - the hardware may still be 00782 * using the buffer. Instead we free it (and the ensuing chain) and 00783 * recreate it next time round the loop. If we're lucky the hardware 00784 * will have already sent the packet, the free will really free, and 00785 * there will be zero memory penalty. 00786 */ 00787 00788 pbuf_free(rambuf); 00789 #endif /* IP_FRAG_USES_STATIC_BUF */ 00790 left -= cop; 00791 ofo += nfb; 00792 } 00793 #if IP_FRAG_USES_STATIC_BUF 00794 pbuf_free(rambuf); 00795 #endif /* IP_FRAG_USES_STATIC_BUF */ 00796 snmp_inc_ipfragoks(); 00797 return ERR_OK; 00798 } 00799 #endif /* IP_FRAG */
Generated on Tue Jul 12 2022 21:10:25 by 1.7.2