]> rtime.felk.cvut.cz Git - pes-rpp/rpp-lwip.git/blob - src/core/ipv4/ip_frag.c
Allow minimal unix target to build in cygwin (but not necessarily run).
[pes-rpp/rpp-lwip.git] / src / core / ipv4 / ip_frag.c
1 /*
2  * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
3  * All rights reserved. 
4  * 
5  * Redistribution and use in source and binary forms, with or without modification, 
6  * are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright notice,
11  *    this list of conditions and the following disclaimer in the documentation
12  *    and/or other materials provided with the distribution.
13  * 3. The name of the author may not be used to endorse or promote products
14  *    derived from this software without specific prior written permission. 
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
19  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
21  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
23  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
24  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
25  * OF SUCH DAMAGE.
26  *
27  * This file is part of the lwIP TCP/IP stack.
28  * 
29  * Author: Jani Monoses <jani@iv.ro> 
30  * original reassembly code by Adam Dunkels <adam@sics.se>
31  * 
32  */
33
34 /*-----------------------------------------------------------------------------------*/
35 /* ip_frag.c
36  *
37  * This is the code for IP segmentation and reassembly
38  *
39  */
40 /*-----------------------------------------------------------------------------------*/
41
42 #include "lwip/debug.h"
43 #include "lwip/sys.h"
44 #include "lwip/ip.h"
45 #include "lwip/ip_frag.h"
46 #include "lwip/netif.h"
47
48 #include "lwip/stats.h"
49
50
51 /*
52  * Copy len bytes from offset in pbuf to buffer 
53  *
54  * helper used by both ip_reass and ip_frag
55  */
56 static struct pbuf *
57 copy_from_pbuf(struct pbuf *p, u16_t * offset,
58                                    u8_t * buffer, u16_t len)
59 {
60   u16_t l;
61
62   p->payload = (u8_t *)p->payload + *offset;
63   p->len -= *offset;
64   while (len) {
65     l = len < p->len ? len : p->len;
66     memcpy(buffer, p->payload, l);
67     buffer += l;
68     len -= l;
69     if (len)
70       p = p->next;
71     else
72       *offset = l;
73   }
74   return p;
75 }
76
77 #define IP_REASS_BUFSIZE 5760
78 #define IP_REASS_MAXAGE 30
79 #define IP_REASS_TMO 1000
80
81 static u8_t ip_reassbuf[IP_HLEN + IP_REASS_BUFSIZE];
82 static u8_t ip_reassbitmap[IP_REASS_BUFSIZE / (8 * 8)];
83 static const u8_t bitmap_bits[8] = { 0xff, 0x7f, 0x3f, 0x1f,
84   0x0f, 0x07, 0x03, 0x01
85 };
86 static u16_t ip_reasslen;
87 static u8_t ip_reassflags;
88 #define IP_REASS_FLAG_LASTFRAG 0x01
89
90 static u8_t ip_reasstmr;
91
92 /* Reassembly timer */ 
93 static void 
94 ip_reass_timer(void *arg)
95 {
96   if(ip_reasstmr > 1) {
97     ip_reasstmr--;
98     sys_timeout(IP_REASS_TMO, ip_reass_timer, NULL);
99   } else if(ip_reasstmr == 1)
100         ip_reasstmr = 0;
101 }
102
103 struct pbuf *
104 ip_reass(struct pbuf *p)
105 {
106   struct pbuf *q;
107   struct ip_hdr *fraghdr, *iphdr;
108   u16_t offset, len;
109   u16_t i;
110
111 #ifdef IP_STATS
112   ++lwip_stats.ip_frag.recv;
113 #endif /* IP_STATS */
114
115   iphdr = (struct ip_hdr *) ip_reassbuf;
116   fraghdr = (struct ip_hdr *) p->payload;
117   /* If ip_reasstmr is zero, no packet is present in the buffer, so we
118      write the IP header of the fragment into the reassembly
119      buffer. The timer is updated with the maximum age. */
120   if (ip_reasstmr == 0) {
121     DEBUGF(IP_REASS_DEBUG, ("ip_reass: new packet\n"));
122     memcpy(iphdr, fraghdr, IP_HLEN);
123     ip_reasstmr = IP_REASS_MAXAGE;
124     sys_timeout(IP_REASS_TMO, ip_reass_timer, NULL);
125     ip_reassflags = 0;
126     /* Clear the bitmap. */
127     memset(ip_reassbitmap, 0, sizeof(ip_reassbitmap));
128   }
129
130   /* Check if the incoming fragment matches the one currently present
131      in the reasembly buffer. If so, we proceed with copying the
132      fragment into the buffer. */
133   if (ip_addr_cmp(&iphdr->src, &fraghdr->src) &&
134       ip_addr_cmp(&iphdr->dest, &fraghdr->dest) &&
135       IPH_ID(iphdr) == IPH_ID(fraghdr)) {
136     DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching old packet\n"));
137 #ifdef IP_STATS
138     ++lwip_stats.ip_frag.cachehit;
139 #endif /* IP_STATS */
140     /* Find out the offset in the reassembly buffer where we should
141        copy the fragment. */
142     len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
143     offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
144
145     /* If the offset or the offset + fragment length overflows the
146        reassembly buffer, we discard the entire packet. */
147     if (offset > IP_REASS_BUFSIZE || offset + len > IP_REASS_BUFSIZE) {
148       DEBUGF(IP_REASS_DEBUG,
149              ("ip_reass: fragment outside of buffer (%d:%d/%d).\n", offset,
150               offset + len, IP_REASS_BUFSIZE));
151       sys_untimeout(ip_reass_timer, NULL);
152       ip_reasstmr = 0;
153       goto nullreturn;
154     }
155
156     /* Copy the fragment into the reassembly buffer, at the right
157        offset. */
158     DEBUGF(IP_REASS_DEBUG,
159            ("ip_reass: copying with offset %d into %d:%d\n", offset,
160             IP_HLEN + offset, IP_HLEN + offset + len));
161     i = IPH_HL(fraghdr) * 4;
162     copy_from_pbuf(p, &i, &ip_reassbuf[IP_HLEN + offset], len);
163
164     /* Update the bitmap. */
165     if (offset / (8 * 8) == (offset + len) / (8 * 8)) {
166       DEBUGF(IP_REASS_DEBUG,
167              ("ip_reass: updating single byte in bitmap.\n"));
168       /* If the two endpoints are in the same byte, we only update
169          that byte. */
170       ip_reassbitmap[offset / (8 * 8)] |=
171           bitmap_bits[(offset / 8) & 7] &
172           ~bitmap_bits[((offset + len) / 8) & 7];
173     } else {
174       /* If the two endpoints are in different bytes, we update the
175          bytes in the endpoints and fill the stuff inbetween with
176          0xff. */
177       ip_reassbitmap[offset / (8 * 8)] |= bitmap_bits[(offset / 8) & 7];
178       DEBUGF(IP_REASS_DEBUG,
179              ("ip_reass: updating many bytes in bitmap (%d:%d).\n",
180               1 + offset / (8 * 8), (offset + len) / (8 * 8)));
181       for (i = 1 + offset / (8 * 8); i < (offset + len) / (8 * 8); ++i) {
182         ip_reassbitmap[i] = 0xff;
183       }
184       ip_reassbitmap[(offset + len) / (8 * 8)] |=
185           ~bitmap_bits[((offset + len) / 8) & 7];
186     }
187
188     /* If this fragment has the More Fragments flag set to zero, we
189        know that this is the last fragment, so we can calculate the
190        size of the entire packet. We also set the
191        IP_REASS_FLAG_LASTFRAG flag to indicate that we have received
192        the final fragment. */
193
194     if ((ntohs(IPH_OFFSET(fraghdr)) & IP_MF) == 0) {
195       ip_reassflags |= IP_REASS_FLAG_LASTFRAG;
196       ip_reasslen = offset + len;
197       DEBUGF(IP_REASS_DEBUG,
198              ("ip_reass: last fragment seen, total len %d\n",
199               ip_reasslen));
200     }
201
202     /* Finally, we check if we have a full packet in the buffer. We do
203        this by checking if we have the last fragment and if all bits
204        in the bitmap are set. */
205     if (ip_reassflags & IP_REASS_FLAG_LASTFRAG) {
206       /* Check all bytes up to and including all but the last byte in
207          the bitmap. */
208       for (i = 0; i < ip_reasslen / (8 * 8) - 1; ++i) {
209         if (ip_reassbitmap[i] != 0xff) {
210           DEBUGF(IP_REASS_DEBUG,
211                  ("ip_reass: last fragment seen, bitmap %d/%d failed (%x)\n",
212                   i, ip_reasslen / (8 * 8) - 1, ip_reassbitmap[i]));
213           goto nullreturn;
214         }
215       }
216       /* Check the last byte in the bitmap. It should contain just the
217          right amount of bits. */
218       if (ip_reassbitmap[ip_reasslen / (8 * 8)] !=
219           (u8_t) ~ bitmap_bits[ip_reasslen / 8 & 7]) {
220         DEBUGF(IP_REASS_DEBUG,
221                ("ip_reass: last fragment seen, bitmap %d didn't contain %x (%x)\n",
222                 ip_reasslen / (8 * 8), ~bitmap_bits[ip_reasslen / 8 & 7],
223                 ip_reassbitmap[ip_reasslen / (8 * 8)]));
224         goto nullreturn;
225       }
226
227       /* Pretend to be a "normal" (i.e., not fragmented) IP packet
228          from now on. */
229       ip_reasslen += IP_HLEN;
230
231       IPH_LEN_SET(iphdr, htons(ip_reasslen));
232       IPH_OFFSET_SET(iphdr, 0);
233       IPH_CHKSUM_SET(iphdr, 0);
234       IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
235
236       /* If we have come this far, we have a full packet in the
237          buffer, so we allocate a pbuf and copy the packet into it. We
238          also reset the timer. */
239       sys_untimeout(ip_reass_timer, NULL);
240       ip_reasstmr = 0;
241       pbuf_free(p);
242       p = pbuf_alloc(PBUF_LINK, ip_reasslen, PBUF_POOL);
243       if (p != NULL) {
244         i = 0;
245         for (q = p; q != NULL; q = q->next) {
246           /* Copy enough bytes to fill this pbuf in the chain. The
247              available data in the pbuf is given by the q->len
248              variable. */
249           DEBUGF(IP_REASS_DEBUG,
250                  ("ip_reass: memcpy from %p (%d) to %p, %d bytes\n",
251                   &ip_reassbuf[i], i, q->payload,
252                   q->len > ip_reasslen - i ? ip_reasslen - i : q->len));
253           memcpy(q->payload, &ip_reassbuf[i],
254                 q->len > ip_reasslen - i ? ip_reasslen - i : q->len);
255           i += q->len;
256         }
257 #ifdef IP_STATS
258         ++lwip_stats.ip_frag.fw;
259 #endif /* IP_STATS */
260       } else {
261 #ifdef IP_STATS
262         ++lwip_stats.ip_frag.memerr;
263 #endif /* IP_STATS */
264       }
265       DEBUGF(IP_REASS_DEBUG, ("ip_reass: p %p\n", (void*)p));
266       return p;
267     }
268   }
269
270 nullreturn:
271 #ifdef IP_STATS
272   ++lwip_stats.ip_frag.drop;
273 #endif /* IP_STATS */
274   pbuf_free(p);
275   return NULL;
276 }
277
278 #define MAX_MTU 1500
279 static u8_t buf[MAX_MTU];
280
281 /**
282  * Fragment an IP packet if too large
283  *
284  * Chop the packet in mtu sized chunks and send them in order
285  * by using a fixed size static memory buffer (PBUF_ROM)
286  */
287 err_t 
288 ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest)
289 {
290   struct pbuf *rambuf;
291   struct pbuf *header;
292   struct ip_hdr *iphdr;
293   u16_t nfb = 0;
294   u16_t left, cop;
295   u16_t mtu = netif->mtu;
296   u16_t ofo, omf;
297   u16_t last;
298   u16_t poff = IP_HLEN;
299   u16_t tmp;
300
301   /* Get a RAM based MTU sized pbuf */
302   rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_ROM);
303   rambuf->tot_len = rambuf->len = mtu;
304   rambuf->payload = buf;
305
306
307   /* Copy the IP header in it */
308   iphdr = rambuf->payload;
309   memcpy(iphdr, p->payload, IP_HLEN);
310
311   /* Save original offset */
312   tmp = ntohs(IPH_OFFSET(iphdr));
313   ofo = tmp & IP_OFFMASK;
314   omf = tmp & IP_MF;
315
316   left = p->tot_len - IP_HLEN;
317
318   while (left) {
319     last = (left <= mtu - IP_HLEN);
320
321     /* Set new offset and MF flag */
322     ofo += nfb;
323     tmp = omf | (IP_OFFMASK & (ofo));
324     if (!last)
325       tmp = tmp | IP_MF;
326     IPH_OFFSET_SET(iphdr, htons(tmp));
327
328     /* Fill this fragment */
329     nfb = (mtu - IP_HLEN) / 8;
330     cop = last ? left : nfb * 8;
331
332     p = copy_from_pbuf(p, &poff, (u8_t *) iphdr + IP_HLEN, cop);
333
334     /* Correct header */
335     IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
336     IPH_CHKSUM_SET(iphdr, 0);
337     IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
338
339     if (last)
340       pbuf_realloc(rambuf, left + IP_HLEN);
341     /* This part is ugly: we alloc a RAM based pbuf for 
342      * the link level header for each chunk and then 
343      * free it.A PBUF_ROM style pbuf for which pbuf_header
344      * worked would make things simpler.
345      */
346     header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
347     pbuf_chain(header, rambuf);
348     netif->output(netif, header, dest);
349 #ifdef IP_STATS
350     ++lwip_stats.ip_frag.xmit;
351 #endif /* IP_STATS */
352     pbuf_dechain(header);
353     pbuf_free(header);
354
355     left -= cop;
356   }
357   pbuf_free(rambuf);
358   return ERR_OK;
359 }