Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Release 1.0
[palacios.git] / geekos / src / lwip / core / ipv4 / ip_frag.c
1 /**
2  * @file
3  * This is the IPv4 packet segmentation and reassembly implementation.
4  *
5  */
6
7 /*
8  * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
9  * All rights reserved. 
10  * 
11  * Redistribution and use in source and binary forms, with or without modification, 
12  * are permitted provided that the following conditions are met:
13  *
14  * 1. Redistributions of source code must retain the above copyright notice,
15  *    this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright notice,
17  *    this list of conditions and the following disclaimer in the documentation
18  *    and/or other materials provided with the distribution.
19  * 3. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission. 
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
23  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
24  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
25  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
26  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
27  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
30  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
31  * OF SUCH DAMAGE.
32  *
33  * This file is part of the lwIP TCP/IP stack.
34  * 
35  * Author: Jani Monoses <jani@iv.ro> 
36  *         Simon Goldschmidt
37  * original reassembly code by Adam Dunkels <adam@sics.se>
38  * 
39  */
40
41 #include "lwip/opt.h"
42 #include "lwip/ip_frag.h"
43 #include "lwip/ip.h"
44 #include "lwip/inet.h"
45 #include "lwip/inet_chksum.h"
46 #include "lwip/netif.h"
47 #include "lwip/snmp.h"
48 #include "lwip/stats.h"
49 #include "lwip/icmp.h"
50
51 #include <string.h>
52
53 #if IP_REASSEMBLY
54 /**
55  * The IP reassembly code currently has the following limitations:
56  * - IP header options are not supported
57  * - fragments must not overlap (e.g. due to different routes),
58  *   currently, overlapping or duplicate fragments are thrown away
59  *   if IP_REASS_CHECK_OVERLAP=1 (the default)!
60  *
61  * @todo: work with IP header options
62  */
63
64 /** Setting this to 0, you can turn off checking the fragments for overlapping
65  * regions. The code gets a little smaller. Only use this if you know that
66  * overlapping won't occur on your network! */
67 #ifndef IP_REASS_CHECK_OVERLAP
68 #define IP_REASS_CHECK_OVERLAP 1
69 #endif /* IP_REASS_CHECK_OVERLAP */
70
71 /** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is
72  * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller.
73  * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA
74  * is set to 1, so one datagram can be reassembled at a time, only. */
75 #ifndef IP_REASS_FREE_OLDEST
76 #define IP_REASS_FREE_OLDEST 1
77 #endif /* IP_REASS_FREE_OLDEST */
78
79 #define IP_REASS_FLAG_LASTFRAG 0x01
80
81 /** This is a helper struct which holds the starting
82  * offset and the ending offset of this fragment to
83  * easily chain the fragments.
84  */
85 struct ip_reass_helper {
86   struct pbuf *next_pbuf;
87   u16_t start;
88   u16_t end;
89 };
90
91 #define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB)  \
92   (ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \
93    ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \
94    IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0
95
96 /* global variables */
97 static struct ip_reassdata *reassdatagrams;
98 static u16_t ip_reass_pbufcount;
99
100 /* function prototypes */
101 static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
102 static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
103
104 /**
105  * Reassembly timer base function
106  * for both NO_SYS == 0 and 1 (!).
107  *
108  * Should be called every 1000 msec (defined by IP_TMR_INTERVAL).
109  */
110 void
111 ip_reass_tmr(void)
112 {
113   struct ip_reassdata *r, *prev = NULL;
114
115   r = reassdatagrams;
116   while (r != NULL) {
117     /* Decrement the timer. Once it reaches 0,
118      * clean up the incomplete fragment assembly */
119     if (r->timer > 0) {
120       r->timer--;
121       LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer));
122       prev = r;
123       r = r->next;
124     } else {
125       /* reassembly timed out */
126       struct ip_reassdata *tmp;
127       LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n"));
128       tmp = r;
129       /* get the next pointer before freeing */
130       r = r->next;
131       /* free the helper struct and all enqueued pbufs */
132       ip_reass_free_complete_datagram(tmp, prev);
133      }
134    }
135 }
136
137 /**
138  * Free a datagram (struct ip_reassdata) and all its pbufs.
139  * Updates the total count of enqueued pbufs (ip_reass_pbufcount),
140  * SNMP counters and sends an ICMP time exceeded packet.
141  *
142  * @param ipr datagram to free
143  * @param prev the previous datagram in the linked list
144  * @return the number of pbufs freed
145  */
146 static int
147 ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
148 {
149   int pbufs_freed = 0;
150   struct pbuf *p;
151   struct ip_reass_helper *iprh;
152
153   LWIP_ASSERT("prev != ipr", prev != ipr);
154   if (prev != NULL) {
155     LWIP_ASSERT("prev->next == ipr", prev->next == ipr);
156   }
157
158   snmp_inc_ipreasmfails();
159 #if LWIP_ICMP
160   iprh = (struct ip_reass_helper *)ipr->p->payload;
161   if (iprh->start == 0) {
162     /* The first fragment was received, send ICMP time exceeded. */
163     /* First, de-queue the first pbuf from r->p. */
164     p = ipr->p;
165     ipr->p = iprh->next_pbuf;
166     /* Then, copy the original header into it. */
167     SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN);
168     icmp_time_exceeded(p, ICMP_TE_FRAG);
169     pbufs_freed += pbuf_clen(p);
170     pbuf_free(p);
171   }
172 #endif /* LWIP_ICMP */
173
174   /* First, free all received pbufs.  The individual pbufs need to be released 
175      separately as they have not yet been chained */
176   p = ipr->p;
177   while (p != NULL) {
178     struct pbuf *pcur;
179     iprh = (struct ip_reass_helper *)p->payload;
180     pcur = p;
181     /* get the next pointer before freeing */
182     p = iprh->next_pbuf;
183     pbufs_freed += pbuf_clen(pcur);
184     pbuf_free(pcur);    
185   }
186   /* Then, unchain the struct ip_reassdata from the list and free it. */
187   ip_reass_dequeue_datagram(ipr, prev);
188   LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed);
189   ip_reass_pbufcount -= pbufs_freed;
190
191   return pbufs_freed;
192 }
193
194 #if IP_REASS_FREE_OLDEST
195 /**
196  * Free the oldest datagram to make room for enqueueing new fragments.
197  * The datagram 'fraghdr' belongs to is not freed!
198  *
199  * @param fraghdr IP header of the current fragment
200  * @param pbufs_needed number of pbufs needed to enqueue
201  *        (used for freeing other datagrams if not enough space)
202  * @return the number of pbufs freed
203  */
204 static int
205 ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
206 {
207   /* @todo Can't we simply remove the last datagram in the
208    *       linked list behind reassdatagrams?
209    */
210   struct ip_reassdata *r, *oldest, *prev;
211   int pbufs_freed = 0, pbufs_freed_current;
212   int other_datagrams;
213
214   /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs,
215    * but don't free the datagram that 'fraghdr' belongs to! */
216   do {
217     oldest = NULL;
218     prev = NULL;
219     other_datagrams = 0;
220     r = reassdatagrams;
221     while (r != NULL) {
222       if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) {
223         /* Not the same datagram as fraghdr */
224         other_datagrams++;
225         if (oldest == NULL) {
226           oldest = r;
227         } else if (r->timer <= oldest->timer) {
228           /* older than the previous oldest */
229           oldest = r;
230         }
231       }
232       if (r->next != NULL) {
233         prev = r;
234       }
235       r = r->next;
236     }
237     if (oldest != NULL) {
238       pbufs_freed_current = ip_reass_free_complete_datagram(oldest, prev);
239       pbufs_freed += pbufs_freed_current;
240     }
241   } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1));
242   return pbufs_freed;
243 }
244 #endif /* IP_REASS_FREE_OLDEST */
245
246 /**
247  * Enqueues a new fragment into the fragment queue
248  * @param fraghdr points to the new fragments IP hdr
249  * @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space)
250  * @return A pointer to the queue location into which the fragment was enqueued
251  */
252 static struct ip_reassdata*
253 ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen)
254 {
255   struct ip_reassdata* ipr;
256   /* No matching previous fragment found, allocate a new reassdata struct */
257   ipr = memp_malloc(MEMP_REASSDATA);
258   if (ipr == NULL) {
259 #if IP_REASS_FREE_OLDEST
260     if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) {
261       ipr = memp_malloc(MEMP_REASSDATA);
262     }
263     if (ipr == NULL)
264 #endif /* IP_REASS_FREE_OLDEST */
265     {
266       IPFRAG_STATS_INC(ip_frag.memerr);
267       LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n"));
268       return NULL;
269     }
270   }
271   memset(ipr, 0, sizeof(struct ip_reassdata));
272   ipr->timer = IP_REASS_MAXAGE;
273
274   /* enqueue the new structure to the front of the list */
275   ipr->next = reassdatagrams;
276   reassdatagrams = ipr;
277   /* copy the ip header for later tests and input */
278   /* @todo: no ip options supported? */
279   SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN);
280   return ipr;
281 }
282
283 /**
284  * Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs.
285  * @param ipr points to the queue entry to dequeue
286  */
287 static void
288 ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
289 {
290   
291   /* dequeue the reass struct  */
292   if (reassdatagrams == ipr) {
293     /* it was the first in the list */
294     reassdatagrams = ipr->next;
295   } else {
296     /* it wasn't the first, so it must have a valid 'prev' */
297     LWIP_ASSERT("sanity check linked list", prev != NULL);
298     prev->next = ipr->next;
299   }
300
301   /* now we can free the ip_reass struct */
302   memp_free(MEMP_REASSDATA, ipr);
303 }
304
305 /**
306  * Chain a new pbuf into the pbuf list that composes the datagram.  The pbuf list
307  * will grow over time as  new pbufs are rx.
308  * Also checks that the datagram passes basic continuity checks (if the last
309  * fragment was received at least once).
310  * @param root_p points to the 'root' pbuf for the current datagram being assembled.
311  * @param new_p points to the pbuf for the current fragment
312  * @return 0 if invalid, >0 otherwise
313  */
314 static int
315 ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p)
316 {
317   struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
318   struct pbuf *q;
319   u16_t offset,len;
320   struct ip_hdr *fraghdr;
321   int valid = 1;
322
323   /* Extract length and fragment offset from current fragment */
324   fraghdr = (struct ip_hdr*)new_p->payload; 
325   len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
326   offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
327
328   /* overwrite the fragment's ip header from the pbuf with our helper struct,
329    * and setup the embedded helper structure. */
330   /* make sure the struct ip_reass_helper fits into the IP header */
331   LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN",
332               sizeof(struct ip_reass_helper) <= IP_HLEN);
333   iprh = (struct ip_reass_helper*)new_p->payload;
334   iprh->next_pbuf = NULL;
335   iprh->start = offset;
336   iprh->end = offset + len;
337
338   /* Iterate through until we either get to the end of the list (append),
339    * or we find on with a larger offset (insert). */
340   for (q = ipr->p; q != NULL;) {
341     iprh_tmp = (struct ip_reass_helper*)q->payload;
342     if (iprh->start < iprh_tmp->start) {
343       /* the new pbuf should be inserted before this */
344       iprh->next_pbuf = q;
345       if (iprh_prev != NULL) {
346         /* not the fragment with the lowest offset */
347 #if IP_REASS_CHECK_OVERLAP
348         if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) {
349           /* fragment overlaps with previous or following, throw away */
350           goto freepbuf;
351         }
352 #endif /* IP_REASS_CHECK_OVERLAP */
353         iprh_prev->next_pbuf = new_p;
354       } else {
355         /* fragment with the lowest offset */
356         ipr->p = new_p;
357       }
358       break;
359     } else if(iprh->start == iprh_tmp->start) {
360       /* received the same datagram twice: no need to keep the datagram */
361       goto freepbuf;
362 #if IP_REASS_CHECK_OVERLAP
363     } else if(iprh->start < iprh_tmp->end) {
364       /* overlap: no need to keep the new datagram */
365       goto freepbuf;
366 #endif /* IP_REASS_CHECK_OVERLAP */
367     } else {
368       /* Check if the fragments received so far have no wholes. */
369       if (iprh_prev != NULL) {
370         if (iprh_prev->end != iprh_tmp->start) {
371           /* There is a fragment missing between the current
372            * and the previous fragment */
373           valid = 0;
374         }
375       }
376     }
377     q = iprh_tmp->next_pbuf;
378     iprh_prev = iprh_tmp;
379   }
380
381   /* If q is NULL, then we made it to the end of the list. Determine what to do now */
382   if (q == NULL) {
383     if (iprh_prev != NULL) {
384       /* this is (for now), the fragment with the highest offset:
385        * chain it to the last fragment */
386 #if IP_REASS_CHECK_OVERLAP
387       LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start);
388 #endif /* IP_REASS_CHECK_OVERLAP */
389       iprh_prev->next_pbuf = new_p;
390       if (iprh_prev->end != iprh->start) {
391         valid = 0;
392       }
393     } else {
394 #if IP_REASS_CHECK_OVERLAP
395       LWIP_ASSERT("no previous fragment, this must be the first fragment!",
396         ipr->p == NULL);
397 #endif /* IP_REASS_CHECK_OVERLAP */
398       /* this is the first fragment we ever received for this ip datagram */
399       ipr->p = new_p;
400     }
401   }
402
403   /* At this point, the validation part begins: */
404   /* If we already received the last fragment */
405   if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) {
406     /* and had no wholes so far */
407     if (valid) {
408       /* then check if the rest of the fragments is here */
409       /* Check if the queue starts with the first datagram */
410       if (((struct ip_reass_helper*)ipr->p->payload)->start != 0) {
411         valid = 0;
412       } else {
413         /* and check that there are no wholes after this datagram */
414         iprh_prev = iprh;
415         q = iprh->next_pbuf;
416         while (q != NULL) {
417           iprh = (struct ip_reass_helper*)q->payload;
418           if (iprh_prev->end != iprh->start) {
419             valid = 0;
420             break;
421           }
422           iprh_prev = iprh;
423           q = iprh->next_pbuf;
424         }
425         /* if still valid, all fragments are received
426          * (because to the MF==0 already arrived */
427         if (valid) {
428           LWIP_ASSERT("sanity check", ipr->p != NULL);
429           LWIP_ASSERT("sanity check",
430             ((struct ip_reass_helper*)ipr->p->payload) != iprh);
431           LWIP_ASSERT("validate_datagram:next_pbuf!=NULL",
432             iprh->next_pbuf == NULL);
433           LWIP_ASSERT("validate_datagram:datagram end!=datagram len",
434             iprh->end == ipr->datagram_len);
435         }
436       }
437     }
438     /* If valid is 0 here, there are some fragments missing in the middle
439      * (since MF == 0 has already arrived). Such datagrams simply time out if
440      * no more fragments are received... */
441     return valid;
442   }
443   /* If we come here, not all fragments were received, yet! */
444   return 0; /* not yet valid! */
445 #if IP_REASS_CHECK_OVERLAP
446 freepbuf:
447   ip_reass_pbufcount -= pbuf_clen(new_p);
448   pbuf_free(new_p);
449   return 0;
450 #endif /* IP_REASS_CHECK_OVERLAP */
451 }
452
453 /**
454  * Reassembles incoming IP fragments into an IP datagram.
455  *
456  * @param p points to a pbuf chain of the fragment
457  * @return NULL if reassembly is incomplete, ? otherwise
458  */
459 struct pbuf *
460 ip_reass(struct pbuf *p)
461 {
462   struct pbuf *r;
463   struct ip_hdr *fraghdr;
464   struct ip_reassdata *ipr;
465   struct ip_reass_helper *iprh;
466   u16_t offset, len;
467   u8_t clen;
468   struct ip_reassdata *ipr_prev = NULL;
469
470   IPFRAG_STATS_INC(ip_frag.recv);
471   snmp_inc_ipreasmreqds();
472
473   fraghdr = (struct ip_hdr*)p->payload;
474
475   if ((IPH_HL(fraghdr) * 4) != IP_HLEN) {
476     LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n"));
477     IPFRAG_STATS_INC(ip_frag.err);
478     goto nullreturn;
479   }
480
481   offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
482   len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
483
484   /* Check if we are allowed to enqueue more datagrams. */
485   clen = pbuf_clen(p);
486   if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
487 #if IP_REASS_FREE_OLDEST
488     if (!ip_reass_remove_oldest_datagram(fraghdr, clen) ||
489         ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS))
490 #endif /* IP_REASS_FREE_OLDEST */
491     {
492       /* No datagram could be freed and still too many pbufs enqueued */
493       LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n",
494         ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS));
495       IPFRAG_STATS_INC(ip_frag.memerr);
496       /* @todo: send ICMP time exceeded here? */
497       /* drop this pbuf */
498       goto nullreturn;
499     }
500   }
501
502   /* Look for the datagram the fragment belongs to in the current datagram queue,
503    * remembering the previous in the queue for later dequeueing. */
504   for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) {
505     /* Check if the incoming fragment matches the one currently present
506        in the reassembly buffer. If so, we proceed with copying the
507        fragment into the buffer. */
508     if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) {
509       LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n",
510         ntohs(IPH_ID(fraghdr))));
511       IPFRAG_STATS_INC(ip_frag.cachehit);
512       break;
513     }
514     ipr_prev = ipr;
515   }
516
517   if (ipr == NULL) {
518   /* Enqueue a new datagram into the datagram queue */
519     ipr = ip_reass_enqueue_new_datagram(fraghdr, clen);
520     /* Bail if unable to enqueue */
521     if(ipr == NULL) {
522       goto nullreturn;
523     }
524   } else {
525     if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) && 
526       ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) {
527       /* ipr->iphdr is not the header from the first fragment, but fraghdr is
528        * -> copy fraghdr into ipr->iphdr since we want to have the header
529        * of the first fragment (for ICMP time exceeded and later, for copying
530        * all options, if supported)*/
531       SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN);
532     }
533   }
534   /* Track the current number of pbufs current 'in-flight', in order to limit 
535   the number of fragments that may be enqueued at any one time */
536   ip_reass_pbufcount += clen;
537
538   /* At this point, we have either created a new entry or pointing 
539    * to an existing one */
540
541   /* check for 'no more fragments', and update queue entry*/
542   if ((ntohs(IPH_OFFSET(fraghdr)) & IP_MF) == 0) {
543     ipr->flags |= IP_REASS_FLAG_LASTFRAG;
544     ipr->datagram_len = offset + len;
545     LWIP_DEBUGF(IP_REASS_DEBUG,
546      ("ip_reass: last fragment seen, total len %"S16_F"\n",
547       ipr->datagram_len));
548   }
549   /* find the right place to insert this pbuf */
550   /* @todo: trim pbufs if fragments are overlapping */
551   if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) {
552     /* the totally last fragment (flag more fragments = 0) was received at least
553      * once AND all fragments are received */
554     ipr->datagram_len += IP_HLEN;
555
556     /* save the second pbuf before copying the header over the pointer */
557     r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf;
558
559     /* copy the original ip header back to the first pbuf */
560     fraghdr = (struct ip_hdr*)(ipr->p->payload);
561     SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN);
562     IPH_LEN_SET(fraghdr, htons(ipr->datagram_len));
563     IPH_OFFSET_SET(fraghdr, 0);
564     IPH_CHKSUM_SET(fraghdr, 0);
565     /* @todo: do we need to set calculate the correct checksum? */
566     IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN));
567
568     p = ipr->p;
569
570     /* chain together the pbufs contained within the reass_data list. */
571     while(r != NULL) {
572       iprh = (struct ip_reass_helper*)r->payload;
573
574       /* hide the ip header for every succeding fragment */
575       pbuf_header(r, -IP_HLEN);
576       pbuf_cat(p, r);
577       r = iprh->next_pbuf;
578     }
579     /* release the sources allocate for the fragment queue entry */
580     ip_reass_dequeue_datagram(ipr, ipr_prev);
581
582     /* and adjust the number of pbufs currently queued for reassembly. */
583     ip_reass_pbufcount -= pbuf_clen(p);
584
585     /* Return the pbuf chain */
586     return p;
587   }
588   /* the datagram is not (yet?) reassembled completely */
589   LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount));
590   return NULL;
591
592 nullreturn:
593   LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n"));
594   IPFRAG_STATS_INC(ip_frag.drop);
595   pbuf_free(p);
596   return NULL;
597 }
598 #endif /* IP_REASSEMBLY */
599
600 #if IP_FRAG
601 #if IP_FRAG_USES_STATIC_BUF
602 static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU)];
603 #endif /* IP_FRAG_USES_STATIC_BUF */
604
605 /**
606  * Fragment an IP datagram if too large for the netif.
607  *
608  * Chop the datagram in MTU sized chunks and send them in order
609  * by using a fixed size static memory buffer (PBUF_REF) or
610  * point PBUF_REFs into p (depending on IP_FRAG_USES_STATIC_BUF).
611  *
612  * @param p ip packet to send
613  * @param netif the netif on which to send
614  * @param dest destination ip address to which to send
615  *
616  * @return ERR_OK if sent successfully, err_t otherwise
617  */
618 err_t 
619 ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest)
620 {
621   struct pbuf *rambuf;
622 #if IP_FRAG_USES_STATIC_BUF
623   struct pbuf *header;
624 #else
625   struct pbuf *newpbuf;
626   struct ip_hdr *original_iphdr;
627 #endif
628   struct ip_hdr *iphdr;
629   u16_t nfb;
630   u16_t left, cop;
631   u16_t mtu = netif->mtu;
632   u16_t ofo, omf;
633   u16_t last;
634   u16_t poff = IP_HLEN;
635   u16_t tmp;
636 #if !IP_FRAG_USES_STATIC_BUF
637   u16_t newpbuflen = 0;
638   u16_t left_to_copy;
639 #endif
640
641   /* Get a RAM based MTU sized pbuf */
642 #if IP_FRAG_USES_STATIC_BUF
643   /* When using a static buffer, we use a PBUF_REF, which we will
644    * use to reference the packet (without link header).
645    * Layer and length is irrelevant.
646    */
647   rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);
648   if (rambuf == NULL) {
649     LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n"));
650     return ERR_MEM;
651   }
652   rambuf->tot_len = rambuf->len = mtu;
653   rambuf->payload = LWIP_MEM_ALIGN((void *)buf);
654
655   /* Copy the IP header in it */
656   iphdr = rambuf->payload;
657   SMEMCPY(iphdr, p->payload, IP_HLEN);
658 #else /* IP_FRAG_USES_STATIC_BUF */
659   original_iphdr = p->payload;
660   iphdr = original_iphdr;
661 #endif /* IP_FRAG_USES_STATIC_BUF */
662
663   /* Save original offset */
664   tmp = ntohs(IPH_OFFSET(iphdr));
665   ofo = tmp & IP_OFFMASK;
666   omf = tmp & IP_MF;
667
668   left = p->tot_len - IP_HLEN;
669
670   nfb = (mtu - IP_HLEN) / 8;
671
672   while (left) {
673     last = (left <= mtu - IP_HLEN);
674
675     /* Set new offset and MF flag */
676     tmp = omf | (IP_OFFMASK & (ofo));
677     if (!last)
678       tmp = tmp | IP_MF;
679
680     /* Fill this fragment */
681     cop = last ? left : nfb * 8;
682
683 #if IP_FRAG_USES_STATIC_BUF
684     poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff);
685 #else /* IP_FRAG_USES_STATIC_BUF */
686     /* When not using a static buffer, create a chain of pbufs.
687      * The first will be a PBUF_RAM holding the link and IP header.
688      * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
689      * but limited to the size of an mtu.
690      */
691     rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM);
692     if (rambuf == NULL) {
693       return ERR_MEM;
694     }
695     LWIP_ASSERT("this needs a pbuf in one piece!",
696                 (p->len >= (IP_HLEN)));
697     SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
698     iphdr = rambuf->payload;
699
700     /* Can just adjust p directly for needed offset. */
701     p->payload = (u8_t *)p->payload + poff;
702     p->len -= poff;
703
704     left_to_copy = cop;
705     while (left_to_copy) {
706       newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len;
707       /* Is this pbuf already empty? */
708       if (!newpbuflen) {
709         p = p->next;
710         continue;
711       }
712       newpbuf = pbuf_alloc(PBUF_RAW, 0, PBUF_REF);
713       if (newpbuf == NULL) {
714         pbuf_free(rambuf);
715         return ERR_MEM;
716       }
717       /* Mirror this pbuf, although we might not need all of it. */
718       newpbuf->payload = p->payload;
719       newpbuf->len = newpbuf->tot_len = newpbuflen;
720       /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain
721        * so that it is removed when pbuf_dechain is later called on rambuf.
722        */
723       pbuf_cat(rambuf, newpbuf);
724       left_to_copy -= newpbuflen;
725       if (left_to_copy)
726         p = p->next;
727     }
728     poff = newpbuflen;
729 #endif /* IP_FRAG_USES_STATIC_BUF */
730
731     /* Correct header */
732     IPH_OFFSET_SET(iphdr, htons(tmp));
733     IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
734     IPH_CHKSUM_SET(iphdr, 0);
735     IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
736
737 #if IP_FRAG_USES_STATIC_BUF
738     if (last)
739       pbuf_realloc(rambuf, left + IP_HLEN);
740
741     /* This part is ugly: we alloc a RAM based pbuf for 
742      * the link level header for each chunk and then 
743      * free it.A PBUF_ROM style pbuf for which pbuf_header
744      * worked would make things simpler.
745      */
746     header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
747     if (header != NULL) {
748       pbuf_chain(header, rambuf);
749       netif->output(netif, header, dest);
750       IPFRAG_STATS_INC(ip_frag.xmit);
751       snmp_inc_ipfragcreates();
752       pbuf_free(header);
753     } else {
754       LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n"));
755       pbuf_free(rambuf);
756       return ERR_MEM;
757     }
758 #else /* IP_FRAG_USES_STATIC_BUF */
759     /* No need for separate header pbuf - we allowed room for it in rambuf
760      * when allocated.
761      */
762     netif->output(netif, rambuf, dest);
763     IPFRAG_STATS_INC(ip_frag.xmit);
764
765     /* Unfortunately we can't reuse rambuf - the hardware may still be
766      * using the buffer. Instead we free it (and the ensuing chain) and
767      * recreate it next time round the loop. If we're lucky the hardware
768      * will have already sent the packet, the free will really free, and
769      * there will be zero memory penalty.
770      */
771     
772     pbuf_free(rambuf);
773 #endif /* IP_FRAG_USES_STATIC_BUF */
774     left -= cop;
775     ofo += nfb;
776   }
777 #if IP_FRAG_USES_STATIC_BUF
778   pbuf_free(rambuf);
779 #endif /* IP_FRAG_USES_STATIC_BUF */
780   snmp_inc_ipfragoks();
781   return ERR_OK;
782 }
783 #endif /* IP_FRAG */