]> git.sur5r.net Git - freertos/blob - Demo/Common/ethernet/lwIP/core/ipv4/ip_frag.c
git-svn-id: https://svn.code.sf.net/p/freertos/code/trunk@82 1d2547de-c912-0410-9cb9...
[freertos] / Demo / Common / ethernet / lwIP / core / ipv4 / ip_frag.c
1 /* @file\r
2  * \r
3  * This is the IP 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  * original reassembly code by Adam Dunkels <adam@sics.se>\r
37  * \r
38  */\r
39 \r
40 #include <string.h>\r
41 \r
42 #include "lwip/opt.h"\r
43 #include "lwip/ip.h"\r
44 #include "lwip/ip_frag.h"\r
45 #include "lwip/netif.h"\r
46 #include "lwip/snmp.h"\r
47 #include "lwip/stats.h"\r
48 \r
49 static u8_t ip_reassbuf[IP_HLEN + IP_REASS_BUFSIZE];\r
50 static u8_t ip_reassbitmap[IP_REASS_BUFSIZE / (8 * 8) + 1];\r
51 static const u8_t bitmap_bits[8] = { 0xff, 0x7f, 0x3f, 0x1f,\r
52   0x0f, 0x07, 0x03, 0x01\r
53 };\r
54 static u16_t ip_reasslen;\r
55 static u8_t ip_reassflags;\r
56 #define IP_REASS_FLAG_LASTFRAG 0x01\r
57 \r
58 static u8_t ip_reasstmr;\r
59 \r
60 /*\r
61  * Copy len bytes from offset in pbuf to buffer \r
62  *\r
63  * helper used by both ip_reass and ip_frag\r
64  */\r
65 static struct pbuf *\r
66 copy_from_pbuf(struct pbuf *p, u16_t * offset,\r
67            u8_t * buffer, u16_t len)\r
68 {\r
69   u16_t l;\r
70 \r
71   p->payload = (u8_t *)p->payload + *offset;\r
72   p->len -= *offset;\r
73   while (len) {\r
74     l = len < p->len ? len : p->len;\r
75     memcpy(buffer, p->payload, l);\r
76     buffer += l;\r
77     len -= l;\r
78     if (len)\r
79       p = p->next;\r
80     else\r
81       *offset = l;\r
82   }\r
83   return p;\r
84 }\r
85 \r
86 \r
87 /**\r
88  * Initializes IP reassembly and fragmentation states.\r
89  */\r
90 void\r
91 ip_frag_init(void)\r
92 {\r
93   ip_reasstmr = 0;\r
94   ip_reassflags = 0;\r
95   ip_reasslen = 0;\r
96   memset(ip_reassbitmap, 0, sizeof(ip_reassbitmap));\r
97 }\r
98 \r
99 /**\r
100  * Reassembly timer base function\r
101  * for both NO_SYS == 0 and 1 (!).\r
102  *\r
103  * Should be called every 1000 msec.\r
104  */\r
105 void\r
106 ip_reass_tmr(void)\r
107 {\r
108   if (ip_reasstmr > 0) {\r
109     ip_reasstmr--;\r
110     LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)ip_reasstmr));\r
111     if (ip_reasstmr == 0) {\r
112       /* reassembly timed out */\r
113       snmp_inc_ipreasmfails();\r
114     }\r
115   }\r
116 }\r
117 \r
118 /**\r
119  * Reassembles incoming IP fragments into an IP datagram.\r
120  *\r
121  * @param p points to a pbuf chain of the fragment\r
122  * @return NULL if reassembly is incomplete, ? otherwise\r
123  */\r
124 struct pbuf *\r
125 ip_reass(struct pbuf *p)\r
126 {\r
127   struct pbuf *q;\r
128   struct ip_hdr *fraghdr, *iphdr;\r
129   u16_t offset, len;\r
130   u16_t i;\r
131 \r
132   IPFRAG_STATS_INC(ip_frag.recv);\r
133   snmp_inc_ipreasmreqds();\r
134 \r
135   iphdr = (struct ip_hdr *) ip_reassbuf;\r
136   fraghdr = (struct ip_hdr *) p->payload;\r
137   /* If ip_reasstmr is zero, no packet is present in the buffer, so we\r
138      write the IP header of the fragment into the reassembly\r
139      buffer. The timer is updated with the maximum age. */\r
140   if (ip_reasstmr == 0) {\r
141     LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: new packet\n"));\r
142     memcpy(iphdr, fraghdr, IP_HLEN);\r
143     ip_reasstmr = IP_REASS_MAXAGE;\r
144     ip_reassflags = 0;\r
145     /* Clear the bitmap. */\r
146     memset(ip_reassbitmap, 0, sizeof(ip_reassbitmap));\r
147   }\r
148 \r
149   /* Check if the incoming fragment matches the one currently present\r
150      in the reasembly buffer. If so, we proceed with copying the\r
151      fragment into the buffer. */\r
152   if (ip_addr_cmp(&iphdr->src, &fraghdr->src) &&\r
153       ip_addr_cmp(&iphdr->dest, &fraghdr->dest) &&\r
154       IPH_ID(iphdr) == IPH_ID(fraghdr)) {\r
155     LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n",\r
156       ntohs(IPH_ID(fraghdr))));\r
157     IPFRAG_STATS_INC(ip_frag.cachehit);\r
158     /* Find out the offset in the reassembly buffer where we should\r
159        copy the fragment. */\r
160     len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;\r
161     offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;\r
162 \r
163     /* If the offset or the offset + fragment length overflows the\r
164        reassembly buffer, we discard the entire packet. */\r
165     if ((offset > IP_REASS_BUFSIZE) || ((offset + len) > IP_REASS_BUFSIZE)) {\r
166       LWIP_DEBUGF(IP_REASS_DEBUG,\r
167        ("ip_reass: fragment outside of buffer (%"S16_F":%"S16_F"/%"S16_F").\n", offset,\r
168         offset + len, IP_REASS_BUFSIZE));\r
169       ip_reasstmr = 0;\r
170       snmp_inc_ipreasmfails();\r
171       goto nullreturn;\r
172     }\r
173 \r
174     /* Copy the fragment into the reassembly buffer, at the right\r
175        offset. */\r
176     LWIP_DEBUGF(IP_REASS_DEBUG,\r
177      ("ip_reass: copying with offset %"S16_F" into %"S16_F":%"S16_F"\n", offset,\r
178       IP_HLEN + offset, IP_HLEN + offset + len));\r
179     i = IPH_HL(fraghdr) * 4;\r
180     copy_from_pbuf(p, &i, &ip_reassbuf[IP_HLEN + offset], len);\r
181 \r
182     /* Update the bitmap. */\r
183     if (offset / (8 * 8) == (offset + len) / (8 * 8)) {\r
184       LWIP_DEBUGF(IP_REASS_DEBUG,\r
185        ("ip_reass: updating single byte in bitmap.\n"));\r
186       /* If the two endpoints are in the same byte, we only update that byte. */\r
187       LWIP_ASSERT("offset / (8 * 8) < sizeof(ip_reassbitmap)",\r
188                    offset / (8 * 8) < sizeof(ip_reassbitmap));\r
189       ip_reassbitmap[offset / (8 * 8)] |=\r
190         bitmap_bits[(offset / 8) & 7] &\r
191         ~bitmap_bits[((offset + len) / 8) & 7];\r
192     } else {\r
193       /* If the two endpoints are in different bytes, we update the\r
194          bytes in the endpoints and fill the stuff inbetween with\r
195          0xff. */\r
196       LWIP_ASSERT("offset / (8 * 8) < sizeof(ip_reassbitmap)",\r
197                    offset / (8 * 8) < sizeof(ip_reassbitmap));\r
198       ip_reassbitmap[offset / (8 * 8)] |= bitmap_bits[(offset / 8) & 7];\r
199       LWIP_DEBUGF(IP_REASS_DEBUG,\r
200        ("ip_reass: updating many bytes in bitmap (%"S16_F":%"S16_F").\n",\r
201         1 + offset / (8 * 8), (offset + len) / (8 * 8)));\r
202       for (i = 1 + offset / (8 * 8); i < (offset + len) / (8 * 8); ++i) {\r
203         ip_reassbitmap[i] = 0xff;\r
204       }\r
205       LWIP_ASSERT("(offset + len) / (8 * 8) < sizeof(ip_reassbitmap)",\r
206                    (offset + len) / (8 * 8) < sizeof(ip_reassbitmap));\r
207       ip_reassbitmap[(offset + len) / (8 * 8)] |=\r
208         ~bitmap_bits[((offset + len) / 8) & 7];\r
209     }\r
210 \r
211     /* If this fragment has the More Fragments flag set to zero, we\r
212        know that this is the last fragment, so we can calculate the\r
213        size of the entire packet. We also set the\r
214        IP_REASS_FLAG_LASTFRAG flag to indicate that we have received\r
215        the final fragment. */\r
216 \r
217     if ((ntohs(IPH_OFFSET(fraghdr)) & IP_MF) == 0) {\r
218       ip_reassflags |= IP_REASS_FLAG_LASTFRAG;\r
219       ip_reasslen = offset + len;\r
220       LWIP_DEBUGF(IP_REASS_DEBUG,\r
221        ("ip_reass: last fragment seen, total len %"S16_F"\n",\r
222         ip_reasslen));\r
223     }\r
224 \r
225     /* Finally, we check if we have a full packet in the buffer. We do\r
226        this by checking if we have the last fragment and if all bits\r
227        in the bitmap are set. */\r
228     if (ip_reassflags & IP_REASS_FLAG_LASTFRAG) {\r
229       /* Check all bytes up to and including all but the last byte in\r
230          the bitmap. */\r
231       LWIP_ASSERT("ip_reasslen / (8 * 8) - 1 < sizeof(ip_reassbitmap)",\r
232                    ip_reasslen / (8 * 8) - 1 < ((u16_t) sizeof(ip_reassbitmap)));\r
233       for (i = 0; i < ip_reasslen / (8 * 8) - 1; ++i) {\r
234         if (ip_reassbitmap[i] != 0xff) {\r
235           LWIP_DEBUGF(IP_REASS_DEBUG,\r
236            ("ip_reass: last fragment seen, bitmap %"S16_F"/%"S16_F" failed (%"X16_F")\n",\r
237             i, ip_reasslen / (8 * 8) - 1, ip_reassbitmap[i]));\r
238           goto nullreturn;\r
239         }\r
240       }\r
241       /* Check the last byte in the bitmap. It should contain just the\r
242          right amount of bits. */\r
243       LWIP_ASSERT("ip_reasslen / (8 * 8) < sizeof(ip_reassbitmap)",\r
244                    ip_reasslen / (8 * 8) < sizeof(ip_reassbitmap));\r
245       if (ip_reassbitmap[ip_reasslen / (8 * 8)] !=\r
246         (u8_t) ~ bitmap_bits[ip_reasslen / 8 & 7]) {\r
247          LWIP_DEBUGF(IP_REASS_DEBUG,\r
248           ("ip_reass: last fragment seen, bitmap %"S16_F" didn't contain %"X16_F" (%"X16_F")\n",\r
249         ip_reasslen / (8 * 8), ~bitmap_bits[ip_reasslen / 8 & 7],\r
250         ip_reassbitmap[ip_reasslen / (8 * 8)]));\r
251         goto nullreturn;\r
252       }\r
253 \r
254       /* Pretend to be a "normal" (i.e., not fragmented) IP packet\r
255          from now on. */\r
256       ip_reasslen += IP_HLEN;\r
257 \r
258       IPH_LEN_SET(iphdr, htons(ip_reasslen));\r
259       IPH_OFFSET_SET(iphdr, 0);\r
260       IPH_CHKSUM_SET(iphdr, 0);\r
261       IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));\r
262 \r
263       /* If we have come this far, we have a full packet in the\r
264          buffer, so we allocate a pbuf and copy the packet into it. We\r
265          also reset the timer. */\r
266       ip_reasstmr = 0;\r
267       pbuf_free(p);\r
268       p = pbuf_alloc(PBUF_LINK, ip_reasslen, PBUF_POOL);\r
269       if (p != NULL) {\r
270         i = 0;\r
271         for (q = p; q != NULL; q = q->next) {\r
272           /* Copy enough bytes to fill this pbuf in the chain. The\r
273              available data in the pbuf is given by the q->len variable. */\r
274           LWIP_DEBUGF(IP_REASS_DEBUG,\r
275            ("ip_reass: memcpy from %p (%"S16_F") to %p, %"S16_F" bytes\n",\r
276             (void *)&ip_reassbuf[i], i, q->payload,\r
277             q->len > ip_reasslen - i ? ip_reasslen - i : q->len));\r
278           memcpy(q->payload, &ip_reassbuf[i],\r
279             q->len > ip_reasslen - i ? ip_reasslen - i : q->len);\r
280           i += q->len;\r
281         }\r
282         IPFRAG_STATS_INC(ip_frag.fw);\r
283         snmp_inc_ipreasmoks();\r
284       } else {\r
285         LWIP_DEBUGF(IP_REASS_DEBUG,\r
286           ("ip_reass: pbuf_alloc(PBUF_LINK, ip_reasslen=%"U16_F", PBUF_POOL) failed\n", ip_reasslen));\r
287         IPFRAG_STATS_INC(ip_frag.memerr);\r
288         snmp_inc_ipreasmfails();\r
289       }\r
290       LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: p %p\n", (void*)p));\r
291       return p;\r
292     }\r
293   }\r
294 \r
295 nullreturn:\r
296   IPFRAG_STATS_INC(ip_frag.drop);\r
297   pbuf_free(p);\r
298   return NULL;\r
299 }\r
300 \r
301 static u8_t buf[MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU)];\r
302 \r
303 /**\r
304  * Fragment an IP datagram if too large for the netif.\r
305  *\r
306  * Chop the datagram in MTU sized chunks and send them in order\r
307  * by using a fixed size static memory buffer (PBUF_ROM)\r
308  */\r
309 err_t \r
310 ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest)\r
311 {\r
312   struct pbuf *rambuf;\r
313   struct pbuf *header;\r
314   struct ip_hdr *iphdr;\r
315   u16_t nfb = 0;\r
316   u16_t left, cop;\r
317   u16_t mtu = netif->mtu;\r
318   u16_t ofo, omf;\r
319   u16_t last;\r
320   u16_t poff = IP_HLEN;\r
321   u16_t tmp;\r
322 \r
323   /* Get a RAM based MTU sized pbuf */\r
324   rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);\r
325   if (rambuf == NULL) {\r
326     LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n"));\r
327     return ERR_MEM;\r
328   }\r
329   rambuf->tot_len = rambuf->len = mtu;\r
330   rambuf->payload = MEM_ALIGN((void *)buf);\r
331 \r
332   /* Copy the IP header in it */\r
333   iphdr = rambuf->payload;\r
334   memcpy(iphdr, p->payload, IP_HLEN);\r
335 \r
336   /* Save original offset */\r
337   tmp = ntohs(IPH_OFFSET(iphdr));\r
338   ofo = tmp & IP_OFFMASK;\r
339   omf = tmp & IP_MF;\r
340 \r
341   left = p->tot_len - IP_HLEN;\r
342 \r
343   while (left) {\r
344     last = (left <= mtu - IP_HLEN);\r
345 \r
346     /* Set new offset and MF flag */\r
347     ofo += nfb;\r
348     tmp = omf | (IP_OFFMASK & (ofo));\r
349     if (!last)\r
350       tmp = tmp | IP_MF;\r
351     IPH_OFFSET_SET(iphdr, htons(tmp));\r
352 \r
353     /* Fill this fragment */\r
354     nfb = (mtu - IP_HLEN) / 8;\r
355     cop = last ? left : nfb * 8;\r
356 \r
357     p = copy_from_pbuf(p, &poff, (u8_t *) iphdr + IP_HLEN, cop);\r
358 \r
359     /* Correct header */\r
360     IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));\r
361     IPH_CHKSUM_SET(iphdr, 0);\r
362     IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));\r
363 \r
364     if (last)\r
365       pbuf_realloc(rambuf, left + IP_HLEN);\r
366     /* This part is ugly: we alloc a RAM based pbuf for \r
367      * the link level header for each chunk and then \r
368      * free it.A PBUF_ROM style pbuf for which pbuf_header\r
369      * worked would make things simpler.\r
370      */\r
371     header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);\r
372     if (header != NULL) {\r
373       pbuf_chain(header, rambuf);\r
374       netif->output(netif, header, dest);\r
375       IPFRAG_STATS_INC(ip_frag.xmit);\r
376       snmp_inc_ipfragcreates();\r
377       pbuf_free(header);\r
378     } else {\r
379       LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n"));\r
380       pbuf_free(rambuf);      \r
381       return ERR_MEM;    \r
382     }\r
383     left -= cop;\r
384   }\r
385   pbuf_free(rambuf);\r
386   snmp_inc_ipfragoks();\r
387   return ERR_OK;\r
388 }\r