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