3 * This is the IP packet segmentation and reassembly implementation.
\r
8 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
\r
9 * All rights reserved.
\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
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
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
33 * This file is part of the lwIP TCP/IP stack.
\r
35 * Author: Jani Monoses <jani@iv.ro>
\r
36 * original reassembly code by Adam Dunkels <adam@sics.se>
\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
46 #include "lwip/stats.h"
\r
51 * Copy len bytes from offset in pbuf to buffer
\r
53 * helper used by both ip_reass and ip_frag
\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
61 p->payload = (u8_t *)p->payload + *offset;
\r
64 l = len < p->len ? len : p->len;
\r
65 memcpy(buffer, p->payload, l);
\r
76 #define IP_REASS_BUFSIZE 5760
\r
77 #define IP_REASS_MAXAGE 30
\r
78 #define IP_REASS_TMO 1000
\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
85 static u16_t ip_reasslen;
\r
86 static u8_t ip_reassflags;
\r
87 #define IP_REASS_FLAG_LASTFRAG 0x01
\r
89 static u8_t ip_reasstmr;
\r
91 /* Reassembly timer */
\r
93 ip_reass_timer(void *arg)
\r
96 if (ip_reasstmr > 1) {
\r
98 sys_timeout(IP_REASS_TMO, ip_reass_timer, NULL);
\r
99 } else if (ip_reasstmr == 1)
\r
104 ip_reass(struct pbuf *p)
\r
107 struct ip_hdr *fraghdr, *iphdr;
\r
111 IPFRAG_STATS_INC(ip_frag.recv);
\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
124 /* Clear the bitmap. */
\r
125 memset(ip_reassbitmap, 0, sizeof(ip_reassbitmap));
\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
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
152 /* Copy the fragment into the reassembly buffer, at the right
\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
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
166 ip_reassbitmap[offset / (8 * 8)] |=
\r
167 bitmap_bits[(offset / 8) & 7] &
\r
168 ~bitmap_bits[((offset + len) / 8) & 7];
\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
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
180 ip_reassbitmap[(offset + len) / (8 * 8)] |=
\r
181 ~bitmap_bits[((offset + len) / 8) & 7];
\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
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
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
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
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
223 /* Pretend to be a "normal" (i.e., not fragmented) IP packet
\r
225 ip_reasslen += IP_HLEN;
\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
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
238 p = pbuf_alloc(PBUF_LINK, ip_reasslen, PBUF_POOL);
\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
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
253 IPFRAG_STATS_INC(ip_frag.fw);
\r
255 IPFRAG_STATS_INC(ip_frag.memerr);
\r
257 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: p %p\n", (void*)p));
\r
263 IPFRAG_STATS_INC(ip_frag.drop);
\r
268 #define MAX_MTU 1500
\r
269 static u8_t buf[MEM_ALIGN_SIZE(MAX_MTU)];
\r
272 * Fragment an IP packet if too large
\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
278 ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest)
\r
280 struct pbuf *rambuf;
\r
281 struct pbuf *header;
\r
282 struct ip_hdr *iphdr;
\r
285 u16_t mtu = netif->mtu;
\r
288 u16_t poff = IP_HLEN;
\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
296 /* Copy the IP header in it */
\r
297 iphdr = rambuf->payload;
\r
298 memcpy(iphdr, p->payload, IP_HLEN);
\r
300 /* Save original offset */
\r
301 tmp = ntohs(IPH_OFFSET(iphdr));
\r
302 ofo = tmp & IP_OFFMASK;
\r
305 left = p->tot_len - IP_HLEN;
\r
308 last = (left <= mtu - IP_HLEN);
\r
310 /* Set new offset and MF flag */
\r
312 tmp = omf | (IP_OFFMASK & (ofo));
\r
315 IPH_OFFSET_SET(iphdr, htons(tmp));
\r
317 /* Fill this fragment */
\r
318 nfb = (mtu - IP_HLEN) / 8;
\r
319 cop = last ? left : nfb * 8;
\r
321 p = copy_from_pbuf(p, &poff, (u8_t *) iphdr + IP_HLEN, cop);
\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
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
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