00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041 #include "lwip/opt.h"
00042 #include "lwip/ip_frag.h"
00043 #include "lwip/ip.h"
00044 #include "lwip/inet.h"
00045 #include "lwip/inet_chksum.h"
00046 #include "lwip/netif.h"
00047 #include "lwip/snmp.h"
00048 #include "lwip/stats.h"
00049 #include "lwip/icmp.h"
00050
00051 #include <string.h>
00052
00053 #if IP_REASSEMBLY
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067 #ifndef IP_REASS_CHECK_OVERLAP
00068 #define IP_REASS_CHECK_OVERLAP 1
00069 #endif
00070
00071
00072
00073
00074
00075 #ifndef IP_REASS_FREE_OLDEST
00076 #define IP_REASS_FREE_OLDEST 1
00077 #endif
00078
00079 #define IP_REASS_FLAG_LASTFRAG 0x01
00080
00081
00082
00083
00084
00085
00086 #ifdef PACK_STRUCT_USE_INCLUDES
00087 # include "arch/bpstruct.h"
00088 #endif
00089 PACK_STRUCT_BEGIN
00090 struct ip_reass_helper {
00091 PACK_STRUCT_FIELD(struct pbuf *next_pbuf);
00092 PACK_STRUCT_FIELD(u16_t start);
00093 PACK_STRUCT_FIELD(u16_t end);
00094 } PACK_STRUCT_STRUCT;
00095 PACK_STRUCT_END
00096 #ifdef PACK_STRUCT_USE_INCLUDES
00097 # include "arch/epstruct.h"
00098 #endif
00099
00100 #define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \
00101 (ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \
00102 ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \
00103 IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0
00104
00105
00106 static struct ip_reassdata *reassdatagrams;
00107 static u16_t ip_reass_pbufcount;
00108
00109
00110 static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
00111 static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
00112
00113
00114
00115
00116
00117
00118
00119 void
00120 ip_reass_tmr(void)
00121 {
00122 struct ip_reassdata *r, *prev = NULL;
00123
00124 r = reassdatagrams;
00125 while (r != NULL) {
00126
00127
00128 if (r->timer > 0) {
00129 r->timer--;
00130 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer));
00131 prev = r;
00132 r = r->next;
00133 } else {
00134
00135 struct ip_reassdata *tmp;
00136 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n"));
00137 tmp = r;
00138
00139 r = r->next;
00140
00141 ip_reass_free_complete_datagram(tmp, prev);
00142 }
00143 }
00144 }
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155 static int
00156 ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
00157 {
00158 int pbufs_freed = 0;
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
00172
00173 p = ipr->p;
00174 ipr->p = iprh->next_pbuf;
00175
00176 SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN);
00177 icmp_time_exceeded(p, ICMP_TE_FRAG);
00178 pbufs_freed += pbuf_clen(p);
00179 pbuf_free(p);
00180 }
00181 #endif
00182
00183
00184
00185 p = ipr->p;
00186 while (p != NULL) {
00187 struct pbuf *pcur;
00188 iprh = (struct ip_reass_helper *)p->payload;
00189 pcur = p;
00190
00191 p = iprh->next_pbuf;
00192 pbufs_freed += pbuf_clen(pcur);
00193 pbuf_free(pcur);
00194 }
00195
00196 ip_reass_dequeue_datagram(ipr, prev);
00197 LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed);
00198 ip_reass_pbufcount -= pbufs_freed;
00199
00200 return pbufs_freed;
00201 }
00202
00203 #if IP_REASS_FREE_OLDEST
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213 static int
00214 ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
00215 {
00216
00217
00218
00219 struct ip_reassdata *r, *oldest, *prev;
00220 int pbufs_freed = 0, pbufs_freed_current;
00221 int other_datagrams;
00222
00223
00224
00225 do {
00226 oldest = NULL;
00227 prev = NULL;
00228 other_datagrams = 0;
00229 r = reassdatagrams;
00230 while (r != NULL) {
00231 if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) {
00232
00233 other_datagrams++;
00234 if (oldest == NULL) {
00235 oldest = r;
00236 } else if (r->timer <= oldest->timer) {
00237
00238 oldest = r;
00239 }
00240 }
00241 if (r->next != NULL) {
00242 prev = r;
00243 }
00244 r = r->next;
00245 }
00246 if (oldest != NULL) {
00247 pbufs_freed_current = ip_reass_free_complete_datagram(oldest, prev);
00248 pbufs_freed += pbufs_freed_current;
00249 }
00250 } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1));
00251 return pbufs_freed;
00252 }
00253 #endif
00254
00255
00256
00257
00258
00259
00260
00261 static struct ip_reassdata*
00262 ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen)
00263 {
00264 struct ip_reassdata* ipr;
00265
00266 ipr = memp_malloc(MEMP_REASSDATA);
00267 if (ipr == NULL) {
00268 #if IP_REASS_FREE_OLDEST
00269 if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) {
00270 ipr = memp_malloc(MEMP_REASSDATA);
00271 }
00272 if (ipr == NULL)
00273 #endif
00274 {
00275 IPFRAG_STATS_INC(ip_frag.memerr);
00276 LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n"));
00277 return NULL;
00278 }
00279 }
00280 memset(ipr, 0, sizeof(struct ip_reassdata));
00281 ipr->timer = IP_REASS_MAXAGE;
00282
00283
00284 ipr->next = reassdatagrams;
00285 reassdatagrams = ipr;
00286
00287
00288 SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN);
00289 return ipr;
00290 }
00291
00292
00293
00294
00295
00296 static void
00297 ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
00298 {
00299
00300
00301 if (reassdatagrams == ipr) {
00302
00303 reassdatagrams = ipr->next;
00304 } else {
00305
00306 LWIP_ASSERT("sanity check linked list", prev != NULL);
00307 prev->next = ipr->next;
00308 }
00309
00310
00311 memp_free(MEMP_REASSDATA, ipr);
00312 }
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323 static int
00324 ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p)
00325 {
00326 struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
00327 struct pbuf *q;
00328 u16_t offset,len;
00329 struct ip_hdr *fraghdr;
00330 int valid = 1;
00331
00332
00333 fraghdr = (struct ip_hdr*)new_p->payload;
00334 len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
00335 offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
00336
00337
00338
00339
00340 LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN",
00341 sizeof(struct ip_reass_helper) <= IP_HLEN);
00342 iprh = (struct ip_reass_helper*)new_p->payload;
00343 iprh->next_pbuf = NULL;
00344 iprh->start = offset;
00345 iprh->end = offset + len;
00346
00347
00348
00349 for (q = ipr->p; q != NULL;) {
00350 iprh_tmp = (struct ip_reass_helper*)q->payload;
00351 if (iprh->start < iprh_tmp->start) {
00352
00353 iprh->next_pbuf = q;
00354 if (iprh_prev != NULL) {
00355
00356 #if IP_REASS_CHECK_OVERLAP
00357 if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) {
00358
00359 goto freepbuf;
00360 }
00361 #endif
00362 iprh_prev->next_pbuf = new_p;
00363 } else {
00364
00365 ipr->p = new_p;
00366 }
00367 break;
00368 } else if(iprh->start == iprh_tmp->start) {
00369
00370 goto freepbuf;
00371 #if IP_REASS_CHECK_OVERLAP
00372 } else if(iprh->start < iprh_tmp->end) {
00373
00374 goto freepbuf;
00375 #endif
00376 } else {
00377
00378 if (iprh_prev != NULL) {
00379 if (iprh_prev->end != iprh_tmp->start) {
00380
00381
00382 valid = 0;
00383 }
00384 }
00385 }
00386 q = iprh_tmp->next_pbuf;
00387 iprh_prev = iprh_tmp;
00388 }
00389
00390
00391 if (q == NULL) {
00392 if (iprh_prev != NULL) {
00393
00394
00395 #if IP_REASS_CHECK_OVERLAP
00396 LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start);
00397 #endif
00398 iprh_prev->next_pbuf = new_p;
00399 if (iprh_prev->end != iprh->start) {
00400 valid = 0;
00401 }
00402 } else {
00403 #if IP_REASS_CHECK_OVERLAP
00404 LWIP_ASSERT("no previous fragment, this must be the first fragment!",
00405 ipr->p == NULL);
00406 #endif
00407
00408 ipr->p = new_p;
00409 }
00410 }
00411
00412
00413
00414 if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) {
00415
00416 if (valid) {
00417
00418
00419 if (((struct ip_reass_helper*)ipr->p->payload)->start != 0) {
00420 valid = 0;
00421 } else {
00422
00423 iprh_prev = iprh;
00424 q = iprh->next_pbuf;
00425 while (q != NULL) {
00426 iprh = (struct ip_reass_helper*)q->payload;
00427 if (iprh_prev->end != iprh->start) {
00428 valid = 0;
00429 break;
00430 }
00431 iprh_prev = iprh;
00432 q = iprh->next_pbuf;
00433 }
00434
00435
00436 if (valid) {
00437 LWIP_ASSERT("sanity check", ipr->p != NULL);
00438 LWIP_ASSERT("sanity check",
00439 ((struct ip_reass_helper*)ipr->p->payload) != iprh);
00440 LWIP_ASSERT("validate_datagram:next_pbuf!=NULL",
00441 iprh->next_pbuf == NULL);
00442 LWIP_ASSERT("validate_datagram:datagram end!=datagram len",
00443 iprh->end == ipr->datagram_len);
00444 }
00445 }
00446 }
00447
00448
00449
00450 return valid;
00451 }
00452
00453 return 0;
00454 #if IP_REASS_CHECK_OVERLAP
00455 freepbuf:
00456 ip_reass_pbufcount -= pbuf_clen(new_p);
00457 pbuf_free(new_p);
00458 return 0;
00459 #endif
00460 }
00461
00462
00463
00464
00465
00466
00467
00468 struct pbuf *
00469 ip_reass(struct pbuf *p)
00470 {
00471 struct pbuf *r;
00472 struct ip_hdr *fraghdr;
00473 struct ip_reassdata *ipr;
00474 struct ip_reass_helper *iprh;
00475 u16_t offset, len;
00476 u8_t clen;
00477 struct ip_reassdata *ipr_prev = NULL;
00478
00479 IPFRAG_STATS_INC(ip_frag.recv);
00480 snmp_inc_ipreasmreqds();
00481
00482 fraghdr = (struct ip_hdr*)p->payload;
00483
00484 if ((IPH_HL(fraghdr) * 4) != IP_HLEN) {
00485 LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n"));
00486 IPFRAG_STATS_INC(ip_frag.err);
00487 goto nullreturn;
00488 }
00489
00490 offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
00491 len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
00492
00493
00494 clen = pbuf_clen(p);
00495 if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
00496 #if IP_REASS_FREE_OLDEST
00497 if (!ip_reass_remove_oldest_datagram(fraghdr, clen) ||
00498 ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS))
00499 #endif
00500 {
00501
00502 LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n",
00503 ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS));
00504 IPFRAG_STATS_INC(ip_frag.memerr);
00505
00506
00507 goto nullreturn;
00508 }
00509 }
00510
00511
00512
00513 for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) {
00514
00515
00516
00517 if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) {
00518 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n",
00519 ntohs(IPH_ID(fraghdr))));
00520 IPFRAG_STATS_INC(ip_frag.cachehit);
00521 break;
00522 }
00523 ipr_prev = ipr;
00524 }
00525
00526 if (ipr == NULL) {
00527
00528 ipr = ip_reass_enqueue_new_datagram(fraghdr, clen);
00529
00530 if(ipr == NULL) {
00531 goto nullreturn;
00532 }
00533 } else {
00534 if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) &&
00535 ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) {
00536
00537
00538
00539
00540 SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN);
00541 }
00542 }
00543
00544
00545 ip_reass_pbufcount += clen;
00546
00547
00548
00549
00550
00551 if ((ntohs(IPH_OFFSET(fraghdr)) & IP_MF) == 0) {
00552 ipr->flags |= IP_REASS_FLAG_LASTFRAG;
00553 ipr->datagram_len = offset + len;
00554 LWIP_DEBUGF(IP_REASS_DEBUG,
00555 ("ip_reass: last fragment seen, total len %"S16_F"\n",
00556 ipr->datagram_len));
00557 }
00558
00559
00560 if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) {
00561
00562
00563 ipr->datagram_len += IP_HLEN;
00564
00565
00566 r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf;
00567
00568
00569 fraghdr = (struct ip_hdr*)(ipr->p->payload);
00570 SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN);
00571 IPH_LEN_SET(fraghdr, htons(ipr->datagram_len));
00572 IPH_OFFSET_SET(fraghdr, 0);
00573 IPH_CHKSUM_SET(fraghdr, 0);
00574
00575 IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN));
00576
00577 p = ipr->p;
00578
00579
00580 while(r != NULL) {
00581 iprh = (struct ip_reass_helper*)r->payload;
00582
00583
00584 pbuf_header(r, -IP_HLEN);
00585 pbuf_cat(p, r);
00586 r = iprh->next_pbuf;
00587 }
00588
00589 ip_reass_dequeue_datagram(ipr, ipr_prev);
00590
00591
00592 ip_reass_pbufcount -= pbuf_clen(p);
00593
00594
00595 return p;
00596 }
00597
00598 LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount));
00599 return NULL;
00600
00601 nullreturn:
00602 LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n"));
00603 IPFRAG_STATS_INC(ip_frag.drop);
00604 pbuf_free(p);
00605 return NULL;
00606 }
00607 #endif
00608
00609 #if IP_FRAG
00610 #if IP_FRAG_USES_STATIC_BUF
00611 static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU + MEM_ALIGNMENT - 1)];
00612 #endif
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627 err_t
00628 ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest)
00629 {
00630 struct pbuf *rambuf;
00631 #if IP_FRAG_USES_STATIC_BUF
00632 struct pbuf *header;
00633 #else
00634 struct pbuf *newpbuf;
00635 struct ip_hdr *original_iphdr;
00636 #endif
00637 struct ip_hdr *iphdr;
00638 u16_t nfb;
00639 u16_t left, cop;
00640 u16_t mtu = netif->mtu;
00641 u16_t ofo, omf;
00642 u16_t last;
00643 u16_t poff = IP_HLEN;
00644 u16_t tmp;
00645 #if !IP_FRAG_USES_STATIC_BUF
00646 u16_t newpbuflen = 0;
00647 u16_t left_to_copy;
00648 #endif
00649
00650
00651 #if IP_FRAG_USES_STATIC_BUF
00652
00653
00654
00655
00656 rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);
00657 if (rambuf == NULL) {
00658 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n"));
00659 return ERR_MEM;
00660 }
00661 rambuf->tot_len = rambuf->len = mtu;
00662 rambuf->payload = LWIP_MEM_ALIGN((void *)buf);
00663
00664
00665 iphdr = rambuf->payload;
00666 SMEMCPY(iphdr, p->payload, IP_HLEN);
00667 #else
00668 original_iphdr = p->payload;
00669 iphdr = original_iphdr;
00670 #endif
00671
00672
00673 tmp = ntohs(IPH_OFFSET(iphdr));
00674 ofo = tmp & IP_OFFMASK;
00675 omf = tmp & IP_MF;
00676
00677 left = p->tot_len - IP_HLEN;
00678
00679 nfb = (mtu - IP_HLEN) / 8;
00680
00681 while (left) {
00682 last = (left <= mtu - IP_HLEN);
00683
00684
00685 tmp = omf | (IP_OFFMASK & (ofo));
00686 if (!last)
00687 tmp = tmp | IP_MF;
00688
00689
00690 cop = last ? left : nfb * 8;
00691
00692 #if IP_FRAG_USES_STATIC_BUF
00693 poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff);
00694 #else
00695
00696
00697
00698
00699
00700 rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM);
00701 if (rambuf == NULL) {
00702 return ERR_MEM;
00703 }
00704 LWIP_ASSERT("this needs a pbuf in one piece!",
00705 (p->len >= (IP_HLEN)));
00706 SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
00707 iphdr = rambuf->payload;
00708
00709
00710 p->payload = (u8_t *)p->payload + poff;
00711 p->len -= poff;
00712
00713 left_to_copy = cop;
00714 while (left_to_copy) {
00715 newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len;
00716
00717 if (!newpbuflen) {
00718 p = p->next;
00719 continue;
00720 }
00721 newpbuf = pbuf_alloc(PBUF_RAW, 0, PBUF_REF);
00722 if (newpbuf == NULL) {
00723 pbuf_free(rambuf);
00724 return ERR_MEM;
00725 }
00726
00727 newpbuf->payload = p->payload;
00728 newpbuf->len = newpbuf->tot_len = newpbuflen;
00729
00730
00731
00732 pbuf_cat(rambuf, newpbuf);
00733 left_to_copy -= newpbuflen;
00734 if (left_to_copy)
00735 p = p->next;
00736 }
00737 poff = newpbuflen;
00738 #endif
00739
00740
00741 IPH_OFFSET_SET(iphdr, htons(tmp));
00742 IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
00743 IPH_CHKSUM_SET(iphdr, 0);
00744 IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
00745
00746 #if IP_FRAG_USES_STATIC_BUF
00747 if (last)
00748 pbuf_realloc(rambuf, left + IP_HLEN);
00749
00750
00751
00752
00753
00754
00755 header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
00756 if (header != NULL) {
00757 pbuf_chain(header, rambuf);
00758 netif->output(netif, header, dest);
00759 IPFRAG_STATS_INC(ip_frag.xmit);
00760 snmp_inc_ipfragcreates();
00761 pbuf_free(header);
00762 } else {
00763 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n"));
00764 pbuf_free(rambuf);
00765 return ERR_MEM;
00766 }
00767 #else
00768
00769
00770
00771 netif->output(netif, rambuf, dest);
00772 IPFRAG_STATS_INC(ip_frag.xmit);
00773
00774
00775
00776
00777
00778
00779
00780
00781 pbuf_free(rambuf);
00782 #endif
00783 left -= cop;
00784 ofo += nfb;
00785 }
00786 #if IP_FRAG_USES_STATIC_BUF
00787 pbuf_free(rambuf);
00788 #endif
00789 snmp_inc_ipfragoks();
00790 return ERR_OK;
00791 }
00792 #endif