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