Warning, cross-references for /kernel/lwip/core/ipv4/ip_frag.c need to be fixed.
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041 #include "lwip/opt.h"
0042 #include "lwip/ip_frag.h"
0043 #include "lwip/ip.h"
0044 #include "lwip/inet.h"
0045 #include "lwip/inet_chksum.h"
0046 #include "lwip/netif.h"
0047 #include "lwip/snmp.h"
0048 #include "lwip/stats.h"
0049 #include "lwip/icmp.h"
0050
0051 #include <string.h>
0052
0053 #if IP_REASSEMBLY
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067 #ifndef IP_REASS_CHECK_OVERLAP
0068 #define IP_REASS_CHECK_OVERLAP 1
0069 #endif
0070
0071
0072
0073
0074
0075 #ifndef IP_REASS_FREE_OLDEST
0076 #define IP_REASS_FREE_OLDEST 1
0077 #endif
0078
0079 #define IP_REASS_FLAG_LASTFRAG 0x01
0080
0081
0082
0083
0084
0085
0086 #ifdef PACK_STRUCT_USE_INCLUDES
0087 # include "arch/bpstruct.h"
0088 #endif
0089 PACK_STRUCT_BEGIN
0090 struct ip_reass_helper {
0091 PACK_STRUCT_FIELD(struct pbuf *next_pbuf);
0092 PACK_STRUCT_FIELD(u16_t start);
0093 PACK_STRUCT_FIELD(u16_t end);
0094 } PACK_STRUCT_STRUCT;
0095 PACK_STRUCT_END
0096 #ifdef PACK_STRUCT_USE_INCLUDES
0097 # include "arch/epstruct.h"
0098 #endif
0099
0100 #define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \
0101 (ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \
0102 ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \
0103 IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0
0104
0105
0106 static struct ip_reassdata *reassdatagrams;
0107 static u16_t ip_reass_pbufcount;
0108
0109
0110 static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
0111 static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
0112
0113
0114
0115
0116
0117
0118
0119 void
0120 ip_reass_tmr(void)
0121 {
0122 struct ip_reassdata *r, *prev = NULL;
0123
0124 r = reassdatagrams;
0125 while (r != NULL) {
0126
0127
0128 if (r->timer > 0) {
0129 r->timer--;
0130 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer));
0131 prev = r;
0132 r = r->next;
0133 } else {
0134
0135 struct ip_reassdata *tmp;
0136 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n"));
0137 tmp = r;
0138
0139 r = r->next;
0140
0141 ip_reass_free_complete_datagram(tmp, prev);
0142 }
0143 }
0144 }
0145
0146
0147
0148
0149
0150
0151
0152
0153
0154
0155 static int
0156 ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
0157 {
0158 int pbufs_freed = 0;
0159 struct pbuf *p;
0160 struct ip_reass_helper *iprh;
0161
0162 LWIP_ASSERT("prev != ipr", prev != ipr);
0163 if (prev != NULL) {
0164 LWIP_ASSERT("prev->next == ipr", prev->next == ipr);
0165 }
0166
0167 snmp_inc_ipreasmfails();
0168 #if LWIP_ICMP
0169 iprh = (struct ip_reass_helper *)ipr->p->payload;
0170 if (iprh->start == 0) {
0171
0172
0173 p = ipr->p;
0174 ipr->p = iprh->next_pbuf;
0175
0176 SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN);
0177 icmp_time_exceeded(p, ICMP_TE_FRAG);
0178 pbufs_freed += pbuf_clen(p);
0179 pbuf_free(p);
0180 }
0181 #endif
0182
0183
0184
0185 p = ipr->p;
0186 while (p != NULL) {
0187 struct pbuf *pcur;
0188 iprh = (struct ip_reass_helper *)p->payload;
0189 pcur = p;
0190
0191 p = iprh->next_pbuf;
0192 pbufs_freed += pbuf_clen(pcur);
0193 pbuf_free(pcur);
0194 }
0195
0196 ip_reass_dequeue_datagram(ipr, prev);
0197 LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed);
0198 ip_reass_pbufcount -= pbufs_freed;
0199
0200 return pbufs_freed;
0201 }
0202
0203 #if IP_REASS_FREE_OLDEST
0204
0205
0206
0207
0208
0209
0210
0211
0212
0213 static int
0214 ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
0215 {
0216
0217
0218
0219 struct ip_reassdata *r, *oldest, *prev;
0220 int pbufs_freed = 0, pbufs_freed_current;
0221 int other_datagrams;
0222
0223
0224
0225 do {
0226 oldest = NULL;
0227 prev = NULL;
0228 other_datagrams = 0;
0229 r = reassdatagrams;
0230 while (r != NULL) {
0231 if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) {
0232
0233 other_datagrams++;
0234 if (oldest == NULL) {
0235 oldest = r;
0236 } else if (r->timer <= oldest->timer) {
0237
0238 oldest = r;
0239 }
0240 }
0241 if (r->next != NULL) {
0242 prev = r;
0243 }
0244 r = r->next;
0245 }
0246 if (oldest != NULL) {
0247 pbufs_freed_current = ip_reass_free_complete_datagram(oldest, prev);
0248 pbufs_freed += pbufs_freed_current;
0249 }
0250 } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1));
0251 return pbufs_freed;
0252 }
0253 #endif
0254
0255
0256
0257
0258
0259
0260
0261 static struct ip_reassdata*
0262 ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen)
0263 {
0264 struct ip_reassdata* ipr;
0265
0266 ipr = memp_malloc(MEMP_REASSDATA);
0267 if (ipr == NULL) {
0268 #if IP_REASS_FREE_OLDEST
0269 if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) {
0270 ipr = memp_malloc(MEMP_REASSDATA);
0271 }
0272 if (ipr == NULL)
0273 #endif
0274 {
0275 IPFRAG_STATS_INC(ip_frag.memerr);
0276 LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n"));
0277 return NULL;
0278 }
0279 }
0280 memset(ipr, 0, sizeof(struct ip_reassdata));
0281 ipr->timer = IP_REASS_MAXAGE;
0282
0283
0284 ipr->next = reassdatagrams;
0285 reassdatagrams = ipr;
0286
0287
0288 SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN);
0289 return ipr;
0290 }
0291
0292
0293
0294
0295
0296 static void
0297 ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
0298 {
0299
0300
0301 if (reassdatagrams == ipr) {
0302
0303 reassdatagrams = ipr->next;
0304 } else {
0305
0306 LWIP_ASSERT("sanity check linked list", prev != NULL);
0307 prev->next = ipr->next;
0308 }
0309
0310
0311 memp_free(MEMP_REASSDATA, ipr);
0312 }
0313
0314
0315
0316
0317
0318
0319
0320
0321
0322
0323 static int
0324 ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p)
0325 {
0326 struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
0327 struct pbuf *q;
0328 u16_t offset,len;
0329 struct ip_hdr *fraghdr;
0330 int valid = 1;
0331
0332
0333 fraghdr = (struct ip_hdr*)new_p->payload;
0334 len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
0335 offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
0336
0337
0338
0339
0340 LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN",
0341 sizeof(struct ip_reass_helper) <= IP_HLEN);
0342 iprh = (struct ip_reass_helper*)new_p->payload;
0343 iprh->next_pbuf = NULL;
0344 iprh->start = offset;
0345 iprh->end = offset + len;
0346
0347
0348
0349 for (q = ipr->p; q != NULL;) {
0350 iprh_tmp = (struct ip_reass_helper*)q->payload;
0351 if (iprh->start < iprh_tmp->start) {
0352
0353 iprh->next_pbuf = q;
0354 if (iprh_prev != NULL) {
0355
0356 #if IP_REASS_CHECK_OVERLAP
0357 if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) {
0358
0359 goto freepbuf;
0360 }
0361 #endif
0362 iprh_prev->next_pbuf = new_p;
0363 } else {
0364
0365 ipr->p = new_p;
0366 }
0367 break;
0368 } else if(iprh->start == iprh_tmp->start) {
0369
0370 goto freepbuf;
0371 #if IP_REASS_CHECK_OVERLAP
0372 } else if(iprh->start < iprh_tmp->end) {
0373
0374 goto freepbuf;
0375 #endif
0376 } else {
0377
0378 if (iprh_prev != NULL) {
0379 if (iprh_prev->end != iprh_tmp->start) {
0380
0381
0382 valid = 0;
0383 }
0384 }
0385 }
0386 q = iprh_tmp->next_pbuf;
0387 iprh_prev = iprh_tmp;
0388 }
0389
0390
0391 if (q == NULL) {
0392 if (iprh_prev != NULL) {
0393
0394
0395 #if IP_REASS_CHECK_OVERLAP
0396 LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start);
0397 #endif
0398 iprh_prev->next_pbuf = new_p;
0399 if (iprh_prev->end != iprh->start) {
0400 valid = 0;
0401 }
0402 } else {
0403 #if IP_REASS_CHECK_OVERLAP
0404 LWIP_ASSERT("no previous fragment, this must be the first fragment!",
0405 ipr->p == NULL);
0406 #endif
0407
0408 ipr->p = new_p;
0409 }
0410 }
0411
0412
0413
0414 if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) {
0415
0416 if (valid) {
0417
0418
0419 if (((struct ip_reass_helper*)ipr->p->payload)->start != 0) {
0420 valid = 0;
0421 } else {
0422
0423 iprh_prev = iprh;
0424 q = iprh->next_pbuf;
0425 while (q != NULL) {
0426 iprh = (struct ip_reass_helper*)q->payload;
0427 if (iprh_prev->end != iprh->start) {
0428 valid = 0;
0429 break;
0430 }
0431 iprh_prev = iprh;
0432 q = iprh->next_pbuf;
0433 }
0434
0435
0436 if (valid) {
0437 LWIP_ASSERT("sanity check", ipr->p != NULL);
0438 LWIP_ASSERT("sanity check",
0439 ((struct ip_reass_helper*)ipr->p->payload) != iprh);
0440 LWIP_ASSERT("validate_datagram:next_pbuf!=NULL",
0441 iprh->next_pbuf == NULL);
0442 LWIP_ASSERT("validate_datagram:datagram end!=datagram len",
0443 iprh->end == ipr->datagram_len);
0444 }
0445 }
0446 }
0447
0448
0449
0450 return valid;
0451 }
0452
0453 return 0;
0454 #if IP_REASS_CHECK_OVERLAP
0455 freepbuf:
0456 ip_reass_pbufcount -= pbuf_clen(new_p);
0457 pbuf_free(new_p);
0458 return 0;
0459 #endif
0460 }
0461
0462
0463
0464
0465
0466
0467
0468 struct pbuf *
0469 ip_reass(struct pbuf *p)
0470 {
0471 struct pbuf *r;
0472 struct ip_hdr *fraghdr;
0473 struct ip_reassdata *ipr;
0474 struct ip_reass_helper *iprh;
0475 u16_t offset, len;
0476 u8_t clen;
0477 struct ip_reassdata *ipr_prev = NULL;
0478
0479 IPFRAG_STATS_INC(ip_frag.recv);
0480 snmp_inc_ipreasmreqds();
0481
0482 fraghdr = (struct ip_hdr*)p->payload;
0483
0484 if ((IPH_HL(fraghdr) * 4) != IP_HLEN) {
0485 LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n"));
0486 IPFRAG_STATS_INC(ip_frag.err);
0487 goto nullreturn;
0488 }
0489
0490 offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
0491 len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
0492
0493
0494 clen = pbuf_clen(p);
0495 if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
0496 #if IP_REASS_FREE_OLDEST
0497 if (!ip_reass_remove_oldest_datagram(fraghdr, clen) ||
0498 ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS))
0499 #endif
0500 {
0501
0502 LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n",
0503 ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS));
0504 IPFRAG_STATS_INC(ip_frag.memerr);
0505
0506
0507 goto nullreturn;
0508 }
0509 }
0510
0511
0512
0513 for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) {
0514
0515
0516
0517 if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) {
0518 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n",
0519 ntohs(IPH_ID(fraghdr))));
0520 IPFRAG_STATS_INC(ip_frag.cachehit);
0521 break;
0522 }
0523 ipr_prev = ipr;
0524 }
0525
0526 if (ipr == NULL) {
0527
0528 ipr = ip_reass_enqueue_new_datagram(fraghdr, clen);
0529
0530 if(ipr == NULL) {
0531 goto nullreturn;
0532 }
0533 } else {
0534 if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) &&
0535 ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) {
0536
0537
0538
0539
0540 SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN);
0541 }
0542 }
0543
0544
0545 ip_reass_pbufcount += clen;
0546
0547
0548
0549
0550
0551 if ((ntohs(IPH_OFFSET(fraghdr)) & IP_MF) == 0) {
0552 ipr->flags |= IP_REASS_FLAG_LASTFRAG;
0553 ipr->datagram_len = offset + len;
0554 LWIP_DEBUGF(IP_REASS_DEBUG,
0555 ("ip_reass: last fragment seen, total len %"S16_F"\n",
0556 ipr->datagram_len));
0557 }
0558
0559
0560 if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) {
0561
0562
0563 ipr->datagram_len += IP_HLEN;
0564
0565
0566 r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf;
0567
0568
0569 fraghdr = (struct ip_hdr*)(ipr->p->payload);
0570 SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN);
0571 IPH_LEN_SET(fraghdr, htons(ipr->datagram_len));
0572 IPH_OFFSET_SET(fraghdr, 0);
0573 IPH_CHKSUM_SET(fraghdr, 0);
0574
0575 IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN));
0576
0577 p = ipr->p;
0578
0579
0580 while(r != NULL) {
0581 iprh = (struct ip_reass_helper*)r->payload;
0582
0583
0584 pbuf_header(r, -IP_HLEN);
0585 pbuf_cat(p, r);
0586 r = iprh->next_pbuf;
0587 }
0588
0589 ip_reass_dequeue_datagram(ipr, ipr_prev);
0590
0591
0592 ip_reass_pbufcount -= pbuf_clen(p);
0593
0594
0595 return p;
0596 }
0597
0598 LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount));
0599 return NULL;
0600
0601 nullreturn:
0602 LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n"));
0603 IPFRAG_STATS_INC(ip_frag.drop);
0604 pbuf_free(p);
0605 return NULL;
0606 }
0607 #endif
0608
0609 #if IP_FRAG
0610 #if IP_FRAG_USES_STATIC_BUF
0611 static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU + MEM_ALIGNMENT - 1)];
0612 #endif
0613
0614
0615
0616
0617
0618
0619
0620
0621
0622
0623
0624
0625
0626
0627 err_t
0628 ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest)
0629 {
0630 struct pbuf *rambuf;
0631 #if IP_FRAG_USES_STATIC_BUF
0632 struct pbuf *header;
0633 #else
0634 struct pbuf *newpbuf;
0635 struct ip_hdr *original_iphdr;
0636 #endif
0637 struct ip_hdr *iphdr;
0638 u16_t nfb;
0639 u16_t left, cop;
0640 u16_t mtu = netif->mtu;
0641 u16_t ofo, omf;
0642 u16_t last;
0643 u16_t poff = IP_HLEN;
0644 u16_t tmp;
0645 #if !IP_FRAG_USES_STATIC_BUF
0646 u16_t newpbuflen = 0;
0647 u16_t left_to_copy;
0648 #endif
0649
0650
0651 #if IP_FRAG_USES_STATIC_BUF
0652
0653
0654
0655
0656 rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);
0657 if (rambuf == NULL) {
0658 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n"));
0659 return ERR_MEM;
0660 }
0661 rambuf->tot_len = rambuf->len = mtu;
0662 rambuf->payload = LWIP_MEM_ALIGN((void *)buf);
0663
0664
0665 iphdr = rambuf->payload;
0666 SMEMCPY(iphdr, p->payload, IP_HLEN);
0667 #else
0668 original_iphdr = p->payload;
0669 iphdr = original_iphdr;
0670 #endif
0671
0672
0673 tmp = ntohs(IPH_OFFSET(iphdr));
0674 ofo = tmp & IP_OFFMASK;
0675 omf = tmp & IP_MF;
0676
0677 left = p->tot_len - IP_HLEN;
0678
0679 nfb = (mtu - IP_HLEN) / 8;
0680
0681 while (left) {
0682 last = (left <= mtu - IP_HLEN);
0683
0684
0685 tmp = omf | (IP_OFFMASK & (ofo));
0686 if (!last)
0687 tmp = tmp | IP_MF;
0688
0689
0690 cop = last ? left : nfb * 8;
0691
0692 #if IP_FRAG_USES_STATIC_BUF
0693 poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff);
0694 #else
0695
0696
0697
0698
0699
0700 rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM);
0701 if (rambuf == NULL) {
0702 return ERR_MEM;
0703 }
0704 LWIP_ASSERT("this needs a pbuf in one piece!",
0705 (p->len >= (IP_HLEN)));
0706 SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
0707 iphdr = rambuf->payload;
0708
0709
0710 p->payload = (u8_t *)p->payload + poff;
0711 p->len -= poff;
0712
0713 left_to_copy = cop;
0714 while (left_to_copy) {
0715 newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len;
0716
0717 if (!newpbuflen) {
0718 p = p->next;
0719 continue;
0720 }
0721 newpbuf = pbuf_alloc(PBUF_RAW, 0, PBUF_REF);
0722 if (newpbuf == NULL) {
0723 pbuf_free(rambuf);
0724 return ERR_MEM;
0725 }
0726
0727 newpbuf->payload = p->payload;
0728 newpbuf->len = newpbuf->tot_len = newpbuflen;
0729
0730
0731
0732 pbuf_cat(rambuf, newpbuf);
0733 left_to_copy -= newpbuflen;
0734 if (left_to_copy)
0735 p = p->next;
0736 }
0737 poff = newpbuflen;
0738 #endif
0739
0740
0741 IPH_OFFSET_SET(iphdr, htons(tmp));
0742 IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
0743 IPH_CHKSUM_SET(iphdr, 0);
0744 IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
0745
0746 #if IP_FRAG_USES_STATIC_BUF
0747 if (last)
0748 pbuf_realloc(rambuf, left + IP_HLEN);
0749
0750
0751
0752
0753
0754
0755 header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
0756 if (header != NULL) {
0757 pbuf_chain(header, rambuf);
0758 netif->output(netif, header, dest);
0759 IPFRAG_STATS_INC(ip_frag.xmit);
0760 snmp_inc_ipfragcreates();
0761 pbuf_free(header);
0762 } else {
0763 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n"));
0764 pbuf_free(rambuf);
0765 return ERR_MEM;
0766 }
0767 #else
0768
0769
0770
0771 netif->output(netif, rambuf, dest);
0772 IPFRAG_STATS_INC(ip_frag.xmit);
0773
0774
0775
0776
0777
0778
0779
0780
0781 pbuf_free(rambuf);
0782 #endif
0783 left -= cop;
0784 ofo += nfb;
0785 }
0786 #if IP_FRAG_USES_STATIC_BUF
0787 pbuf_free(rambuf);
0788 #endif
0789 snmp_inc_ipfragoks();
0790 return ERR_OK;
0791 }
0792 #endif