2 * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
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.
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
27 * This file is part of the lwIP TCP/IP stack.
29 * Author: Jani Monoses <jani@iv.ro>
30 * original reassembly code by Adam Dunkels <adam@sics.se>
34 /*-----------------------------------------------------------------------------------*/
37 * This is the code for IP segmentation and reassembly
40 /*-----------------------------------------------------------------------------------*/
42 #include "lwip/debug.h"
45 #include "lwip/ip_frag.h"
46 #include "lwip/netif.h"
48 #include "lwip/stats.h"
52 * Copy len bytes from offset in pbuf to buffer
54 * helper used by both ip_reass and ip_frag
57 copy_from_pbuf(struct pbuf *p, u16_t * offset,
58 u8_t * buffer, u16_t len)
62 p->payload = (u8_t *)p->payload + *offset;
65 l = len < p->len ? len : p->len;
66 memcpy(buffer, p->payload, l);
77 #define IP_REASS_BUFSIZE 5760
78 #define IP_REASS_MAXAGE 30
79 #define IP_REASS_TMO 1000
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
86 static u16_t ip_reasslen;
87 static u8_t ip_reassflags;
88 #define IP_REASS_FLAG_LASTFRAG 0x01
90 static u8_t ip_reasstmr;
92 /* Reassembly timer */
94 ip_reass_timer(void *arg)
98 sys_timeout(IP_REASS_TMO, ip_reass_timer, NULL);
99 } else if(ip_reasstmr == 1)
104 ip_reass(struct pbuf *p)
107 struct ip_hdr *fraghdr, *iphdr;
112 ++lwip_stats.ip_frag.recv;
113 #endif /* IP_STATS */
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);
126 /* Clear the bitmap. */
127 memset(ip_reassbitmap, 0, sizeof(ip_reassbitmap));
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"));
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;
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);
156 /* Copy the fragment into the reassembly buffer, at the right
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);
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
170 ip_reassbitmap[offset / (8 * 8)] |=
171 bitmap_bits[(offset / 8) & 7] &
172 ~bitmap_bits[((offset + len) / 8) & 7];
174 /* If the two endpoints are in different bytes, we update the
175 bytes in the endpoints and fill the stuff inbetween with
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;
184 ip_reassbitmap[(offset + len) / (8 * 8)] |=
185 ~bitmap_bits[((offset + len) / 8) & 7];
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. */
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",
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
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]));
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)]));
227 /* Pretend to be a "normal" (i.e., not fragmented) IP packet
229 ip_reasslen += IP_HLEN;
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));
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);
242 p = pbuf_alloc(PBUF_LINK, ip_reasslen, PBUF_POOL);
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
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);
258 ++lwip_stats.ip_frag.fw;
259 #endif /* IP_STATS */
262 ++lwip_stats.ip_frag.memerr;
263 #endif /* IP_STATS */
265 DEBUGF(IP_REASS_DEBUG, ("ip_reass: p %p\n", (void*)p));
272 ++lwip_stats.ip_frag.drop;
273 #endif /* IP_STATS */
279 static u8_t buf[MAX_MTU];
282 * Fragment an IP packet if too large
284 * Chop the packet in mtu sized chunks and send them in order
285 * by using a fixed size static memory buffer (PBUF_ROM)
288 ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest)
292 struct ip_hdr *iphdr;
295 u16_t mtu = netif->mtu;
298 u16_t poff = IP_HLEN;
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;
307 /* Copy the IP header in it */
308 iphdr = rambuf->payload;
309 memcpy(iphdr, p->payload, IP_HLEN);
311 /* Save original offset */
312 tmp = ntohs(IPH_OFFSET(iphdr));
313 ofo = tmp & IP_OFFMASK;
316 left = p->tot_len - IP_HLEN;
319 last = (left <= mtu - IP_HLEN);
321 /* Set new offset and MF flag */
323 tmp = omf | (IP_OFFMASK & (ofo));
326 IPH_OFFSET_SET(iphdr, htons(tmp));
328 /* Fill this fragment */
329 nfb = (mtu - IP_HLEN) / 8;
330 cop = last ? left : nfb * 8;
332 p = copy_from_pbuf(p, &poff, (u8_t *) iphdr + IP_HLEN, cop);
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));
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.
346 header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
347 pbuf_chain(header, rambuf);
348 netif->output(netif, header, dest);
350 ++lwip_stats.ip_frag.xmit;
351 #endif /* IP_STATS */
352 pbuf_dechain(header);