3 * This is the IP packet segmentation and reassembly implementation.
8 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
11 * Redistribution and use in source and binary forms, with or without modification,
12 * are permitted provided that the following conditions are met:
14 * 1. Redistributions of source code must retain the above copyright notice,
15 * this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright notice,
17 * this list of conditions and the following disclaimer in the documentation
18 * and/or other materials provided with the distribution.
19 * 3. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
23 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
24 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
27 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
30 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
33 * This file is part of the lwIP TCP/IP stack.
35 * Author: Jani Monoses <jani@iv.ro>
36 * original reassembly code by Adam Dunkels <adam@sics.se>
43 /* #include "lwip/sys.h" */
45 #include "lwip/ip_frag.h"
46 #include "lwip/netif.h"
47 #include "lwip/stats.h"
51 * Copy len bytes from offset in pbuf to buffer
53 * helper used by both ip_reass and ip_frag
56 copy_from_pbuf(struct pbuf *p, u16_t * offset,
57 u8_t * buffer, u16_t len)
61 p->payload = (u8_t *)p->payload + *offset;
64 l = len < p->len ? len : p->len;
65 memcpy(buffer, p->payload, l);
76 #define IP_REASS_BUFSIZE 5760
77 #define IP_REASS_MAXAGE 30
78 #define IP_REASS_TMO 1000
80 static u8_t ip_reassbuf[IP_HLEN + IP_REASS_BUFSIZE];
81 static u8_t ip_reassbitmap[IP_REASS_BUFSIZE / (8 * 8) + 1];
82 static const u8_t bitmap_bits[8] = { 0xff, 0x7f, 0x3f, 0x1f,
83 0x0f, 0x07, 0x03, 0x01
85 static u16_t ip_reasslen;
86 static u8_t ip_reassflags;
87 #define IP_REASS_FLAG_LASTFRAG 0x01
89 static u8_t ip_reasstmr;
92 * Reassembly timer base function
93 * for both NO_SYS == 0 and 1 (!).
95 * Should be called every 1000 msec.
100 if (ip_reasstmr > 0) {
106 * Reassembles incoming IP fragments into an IP datagram.
108 * @param p points to a pbuf chain of the fragment
109 * @return NULL if reassembly is incomplete, ? otherwise
112 ip_reass(struct pbuf *p)
115 struct ip_hdr *fraghdr, *iphdr;
119 IPFRAG_STATS_INC(ip_frag.recv);
121 iphdr = (struct ip_hdr *) ip_reassbuf;
122 fraghdr = (struct ip_hdr *) p->payload;
123 /* If ip_reasstmr is zero, no packet is present in the buffer, so we
124 write the IP header of the fragment into the reassembly
125 buffer. The timer is updated with the maximum age. */
126 if (ip_reasstmr == 0) {
127 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: new packet\n"));
128 memcpy(iphdr, fraghdr, IP_HLEN);
129 ip_reasstmr = IP_REASS_MAXAGE;
131 /* Clear the bitmap. */
132 memset(ip_reassbitmap, 0, sizeof(ip_reassbitmap));
135 /* Check if the incoming fragment matches the one currently present
136 in the reasembly buffer. If so, we proceed with copying the
137 fragment into the buffer. */
138 if (ip_addr_cmp(&iphdr->src, &fraghdr->src) &&
139 ip_addr_cmp(&iphdr->dest, &fraghdr->dest) &&
140 IPH_ID(iphdr) == IPH_ID(fraghdr)) {
141 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n",
142 ntohs(IPH_ID(fraghdr))));
143 IPFRAG_STATS_INC(ip_frag.cachehit);
144 /* Find out the offset in the reassembly buffer where we should
145 copy the fragment. */
146 len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
147 offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
149 /* If the offset or the offset + fragment length overflows the
150 reassembly buffer, we discard the entire packet. */
151 if (offset > IP_REASS_BUFSIZE || offset + len > IP_REASS_BUFSIZE) {
152 LWIP_DEBUGF(IP_REASS_DEBUG,
153 ("ip_reass: fragment outside of buffer (%"S16_F":%"S16_F"/%"S16_F").\n", offset,
154 offset + len, IP_REASS_BUFSIZE));
159 /* Copy the fragment into the reassembly buffer, at the right
161 LWIP_DEBUGF(IP_REASS_DEBUG,
162 ("ip_reass: copying with offset %"S16_F" into %"S16_F":%"S16_F"\n", offset,
163 IP_HLEN + offset, IP_HLEN + offset + len));
164 i = IPH_HL(fraghdr) * 4;
165 copy_from_pbuf(p, &i, &ip_reassbuf[IP_HLEN + offset], len);
167 /* Update the bitmap. */
168 if (offset / (8 * 8) == (offset + len) / (8 * 8)) {
169 LWIP_DEBUGF(IP_REASS_DEBUG,
170 ("ip_reass: updating single byte in bitmap.\n"));
171 /* If the two endpoints are in the same byte, we only update that byte. */
172 LWIP_ASSERT("offset / (8 * 8) < sizeof(ip_reassbitmap)",
173 offset / (8 * 8) < sizeof(ip_reassbitmap));
174 ip_reassbitmap[offset / (8 * 8)] |=
175 bitmap_bits[(offset / 8) & 7] &
176 ~bitmap_bits[((offset + len) / 8) & 7];
178 /* If the two endpoints are in different bytes, we update the
179 bytes in the endpoints and fill the stuff inbetween with
181 LWIP_ASSERT("offset / (8 * 8) < sizeof(ip_reassbitmap)",
182 offset / (8 * 8) < sizeof(ip_reassbitmap));
183 ip_reassbitmap[offset / (8 * 8)] |= bitmap_bits[(offset / 8) & 7];
184 LWIP_DEBUGF(IP_REASS_DEBUG,
185 ("ip_reass: updating many bytes in bitmap (%"S16_F":%"S16_F").\n",
186 1 + offset / (8 * 8), (offset + len) / (8 * 8)));
187 for (i = 1 + offset / (8 * 8); i < (offset + len) / (8 * 8); ++i) {
188 ip_reassbitmap[i] = 0xff;
190 LWIP_ASSERT("(offset + len) / (8 * 8) < sizeof(ip_reassbitmap)",
191 (offset + len) / (8 * 8) < sizeof(ip_reassbitmap));
192 ip_reassbitmap[(offset + len) / (8 * 8)] |=
193 ~bitmap_bits[((offset + len) / 8) & 7];
196 /* If this fragment has the More Fragments flag set to zero, we
197 know that this is the last fragment, so we can calculate the
198 size of the entire packet. We also set the
199 IP_REASS_FLAG_LASTFRAG flag to indicate that we have received
200 the final fragment. */
202 if ((ntohs(IPH_OFFSET(fraghdr)) & IP_MF) == 0) {
203 ip_reassflags |= IP_REASS_FLAG_LASTFRAG;
204 ip_reasslen = offset + len;
205 LWIP_DEBUGF(IP_REASS_DEBUG,
206 ("ip_reass: last fragment seen, total len %"S16_F"\n",
210 /* Finally, we check if we have a full packet in the buffer. We do
211 this by checking if we have the last fragment and if all bits
212 in the bitmap are set. */
213 if (ip_reassflags & IP_REASS_FLAG_LASTFRAG) {
214 /* Check all bytes up to and including all but the last byte in
216 LWIP_ASSERT("ip_reasslen / (8 * 8) - 1 < sizeof(ip_reassbitmap)",
217 ip_reasslen / (8 * 8) - 1 < sizeof(ip_reassbitmap));
218 for (i = 0; i < ip_reasslen / (8 * 8) - 1; ++i) {
219 if (ip_reassbitmap[i] != 0xff) {
220 LWIP_DEBUGF(IP_REASS_DEBUG,
221 ("ip_reass: last fragment seen, bitmap %"S16_F"/%"S16_F" failed (%"X16_F")\n",
222 i, ip_reasslen / (8 * 8) - 1, ip_reassbitmap[i]));
226 /* Check the last byte in the bitmap. It should contain just the
227 right amount of bits. */
228 LWIP_ASSERT("ip_reasslen / (8 * 8) < sizeof(ip_reassbitmap)",
229 ip_reasslen / (8 * 8) < sizeof(ip_reassbitmap));
230 if (ip_reassbitmap[ip_reasslen / (8 * 8)] !=
231 (u8_t) ~ bitmap_bits[ip_reasslen / 8 & 7]) {
232 LWIP_DEBUGF(IP_REASS_DEBUG,
233 ("ip_reass: last fragment seen, bitmap %"S16_F" didn't contain %"X16_F" (%"X16_F")\n",
234 ip_reasslen / (8 * 8), ~bitmap_bits[ip_reasslen / 8 & 7],
235 ip_reassbitmap[ip_reasslen / (8 * 8)]));
239 /* Pretend to be a "normal" (i.e., not fragmented) IP packet
241 ip_reasslen += IP_HLEN;
243 IPH_LEN_SET(iphdr, htons(ip_reasslen));
244 IPH_OFFSET_SET(iphdr, 0);
245 IPH_CHKSUM_SET(iphdr, 0);
246 IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
248 /* If we have come this far, we have a full packet in the
249 buffer, so we allocate a pbuf and copy the packet into it. We
250 also reset the timer. */
253 p = pbuf_alloc(PBUF_LINK, ip_reasslen, PBUF_POOL);
256 for (q = p; q != NULL; q = q->next) {
257 /* Copy enough bytes to fill this pbuf in the chain. The
258 available data in the pbuf is given by the q->len variable. */
259 LWIP_DEBUGF(IP_REASS_DEBUG,
260 ("ip_reass: memcpy from %p (%"S16_F") to %p, %"S16_F" bytes\n",
261 (void *)&ip_reassbuf[i], i, q->payload,
262 q->len > ip_reasslen - i ? ip_reasslen - i : q->len));
263 memcpy(q->payload, &ip_reassbuf[i],
264 q->len > ip_reasslen - i ? ip_reasslen - i : q->len);
267 IPFRAG_STATS_INC(ip_frag.fw);
269 IPFRAG_STATS_INC(ip_frag.memerr);
271 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: p %p\n", (void*)p));
277 IPFRAG_STATS_INC(ip_frag.drop);
283 static u8_t buf[MEM_ALIGN_SIZE(MAX_MTU)];
286 * Fragment an IP datagram if too large for the netif.
288 * Chop the datagram in MTU sized chunks and send them in order
289 * by using a fixed size static memory buffer (PBUF_ROM)
292 ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest)
296 struct ip_hdr *iphdr;
299 u16_t mtu = netif->mtu;
302 u16_t poff = IP_HLEN;
305 /* Get a RAM based MTU sized pbuf */
306 rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);
307 if (rambuf == NULL) {
310 rambuf->tot_len = rambuf->len = mtu;
311 rambuf->payload = MEM_ALIGN((void *)buf);
313 /* Copy the IP header in it */
314 iphdr = rambuf->payload;
315 memcpy(iphdr, p->payload, IP_HLEN);
317 /* Save original offset */
318 tmp = ntohs(IPH_OFFSET(iphdr));
319 ofo = tmp & IP_OFFMASK;
322 left = p->tot_len - IP_HLEN;
325 last = (left <= mtu - IP_HLEN);
327 /* Set new offset and MF flag */
329 tmp = omf | (IP_OFFMASK & (ofo));
332 IPH_OFFSET_SET(iphdr, htons(tmp));
334 /* Fill this fragment */
335 nfb = (mtu - IP_HLEN) / 8;
336 cop = last ? left : nfb * 8;
338 p = copy_from_pbuf(p, &poff, (u8_t *) iphdr + IP_HLEN, cop);
341 IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
342 IPH_CHKSUM_SET(iphdr, 0);
343 IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
346 pbuf_realloc(rambuf, left + IP_HLEN);
347 /* This part is ugly: we alloc a RAM based pbuf for
348 * the link level header for each chunk and then
349 * free it.A PBUF_ROM style pbuf for which pbuf_header
350 * worked would make things simpler.
352 header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
353 if (header != NULL) {
354 pbuf_chain(header, rambuf);
355 netif->output(netif, header, dest);
356 IPFRAG_STATS_INC(ip_frag.xmit);