Some quick code to use UDP-only (no TCP) with mBed. Echos received packets and sends packets when a button is pressed

Dependencies:   mbed

Committer:
pehrhovey
Date:
Sun Mar 14 00:54:12 2010 +0000
Revision:
0:a548a085de55

        

Who changed what in which revision?

UserRevisionLine numberNew contents of line
pehrhovey 0:a548a085de55 1 /**
pehrhovey 0:a548a085de55 2 * @file
pehrhovey 0:a548a085de55 3 * This is the IPv4 packet segmentation and reassembly implementation.
pehrhovey 0:a548a085de55 4 *
pehrhovey 0:a548a085de55 5 */
pehrhovey 0:a548a085de55 6
pehrhovey 0:a548a085de55 7 /*
pehrhovey 0:a548a085de55 8 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
pehrhovey 0:a548a085de55 9 * All rights reserved.
pehrhovey 0:a548a085de55 10 *
pehrhovey 0:a548a085de55 11 * Redistribution and use in source and binary forms, with or without modification,
pehrhovey 0:a548a085de55 12 * are permitted provided that the following conditions are met:
pehrhovey 0:a548a085de55 13 *
pehrhovey 0:a548a085de55 14 * 1. Redistributions of source code must retain the above copyright notice,
pehrhovey 0:a548a085de55 15 * this list of conditions and the following disclaimer.
pehrhovey 0:a548a085de55 16 * 2. Redistributions in binary form must reproduce the above copyright notice,
pehrhovey 0:a548a085de55 17 * this list of conditions and the following disclaimer in the documentation
pehrhovey 0:a548a085de55 18 * and/or other materials provided with the distribution.
pehrhovey 0:a548a085de55 19 * 3. The name of the author may not be used to endorse or promote products
pehrhovey 0:a548a085de55 20 * derived from this software without specific prior written permission.
pehrhovey 0:a548a085de55 21 *
pehrhovey 0:a548a085de55 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
pehrhovey 0:a548a085de55 23 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
pehrhovey 0:a548a085de55 24 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
pehrhovey 0:a548a085de55 25 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
pehrhovey 0:a548a085de55 26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
pehrhovey 0:a548a085de55 27 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
pehrhovey 0:a548a085de55 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
pehrhovey 0:a548a085de55 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
pehrhovey 0:a548a085de55 30 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
pehrhovey 0:a548a085de55 31 * OF SUCH DAMAGE.
pehrhovey 0:a548a085de55 32 *
pehrhovey 0:a548a085de55 33 * This file is part of the lwIP TCP/IP stack.
pehrhovey 0:a548a085de55 34 *
pehrhovey 0:a548a085de55 35 * Author: Jani Monoses <jani@iv.ro>
pehrhovey 0:a548a085de55 36 * Simon Goldschmidt
pehrhovey 0:a548a085de55 37 * original reassembly code by Adam Dunkels <adam@sics.se>
pehrhovey 0:a548a085de55 38 *
pehrhovey 0:a548a085de55 39 */
pehrhovey 0:a548a085de55 40
pehrhovey 0:a548a085de55 41 #include "lwip/opt.h"
pehrhovey 0:a548a085de55 42 #include "lwip/ip_frag.h"
pehrhovey 0:a548a085de55 43 #include "lwip/ip.h"
pehrhovey 0:a548a085de55 44 #include "lwip/inet.h"
pehrhovey 0:a548a085de55 45 #include "lwip/inet_chksum.h"
pehrhovey 0:a548a085de55 46 #include "lwip/netif.h"
pehrhovey 0:a548a085de55 47 #include "lwip/snmp.h"
pehrhovey 0:a548a085de55 48 #include "lwip/stats.h"
pehrhovey 0:a548a085de55 49 #include "lwip/icmp.h"
pehrhovey 0:a548a085de55 50
pehrhovey 0:a548a085de55 51 #include <string.h>
pehrhovey 0:a548a085de55 52
pehrhovey 0:a548a085de55 53 #if IP_REASSEMBLY
pehrhovey 0:a548a085de55 54 /**
pehrhovey 0:a548a085de55 55 * The IP reassembly code currently has the following limitations:
pehrhovey 0:a548a085de55 56 * - IP header options are not supported
pehrhovey 0:a548a085de55 57 * - fragments must not overlap (e.g. due to different routes),
pehrhovey 0:a548a085de55 58 * currently, overlapping or duplicate fragments are thrown away
pehrhovey 0:a548a085de55 59 * if IP_REASS_CHECK_OVERLAP=1 (the default)!
pehrhovey 0:a548a085de55 60 *
pehrhovey 0:a548a085de55 61 * @todo: work with IP header options
pehrhovey 0:a548a085de55 62 */
pehrhovey 0:a548a085de55 63
pehrhovey 0:a548a085de55 64 /** Setting this to 0, you can turn off checking the fragments for overlapping
pehrhovey 0:a548a085de55 65 * regions. The code gets a little smaller. Only use this if you know that
pehrhovey 0:a548a085de55 66 * overlapping won't occur on your network! */
pehrhovey 0:a548a085de55 67 #ifndef IP_REASS_CHECK_OVERLAP
pehrhovey 0:a548a085de55 68 #define IP_REASS_CHECK_OVERLAP 1
pehrhovey 0:a548a085de55 69 #endif /* IP_REASS_CHECK_OVERLAP */
pehrhovey 0:a548a085de55 70
pehrhovey 0:a548a085de55 71 /** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is
pehrhovey 0:a548a085de55 72 * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller.
pehrhovey 0:a548a085de55 73 * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA
pehrhovey 0:a548a085de55 74 * is set to 1, so one datagram can be reassembled at a time, only. */
pehrhovey 0:a548a085de55 75 #ifndef IP_REASS_FREE_OLDEST
pehrhovey 0:a548a085de55 76 #define IP_REASS_FREE_OLDEST 1
pehrhovey 0:a548a085de55 77 #endif /* IP_REASS_FREE_OLDEST */
pehrhovey 0:a548a085de55 78
pehrhovey 0:a548a085de55 79 #define IP_REASS_FLAG_LASTFRAG 0x01
pehrhovey 0:a548a085de55 80
pehrhovey 0:a548a085de55 81 /** This is a helper struct which holds the starting
pehrhovey 0:a548a085de55 82 * offset and the ending offset of this fragment to
pehrhovey 0:a548a085de55 83 * easily chain the fragments.
pehrhovey 0:a548a085de55 84 * It has to be packed since it has to fit inside the IP header.
pehrhovey 0:a548a085de55 85 */
pehrhovey 0:a548a085de55 86 // XXX: For armcc
pehrhovey 0:a548a085de55 87 struct ip_reass_helper {
pehrhovey 0:a548a085de55 88 struct pbuf *next_pbuf;
pehrhovey 0:a548a085de55 89 u16_t start;
pehrhovey 0:a548a085de55 90 u16_t end;
pehrhovey 0:a548a085de55 91 };
pehrhovey 0:a548a085de55 92
pehrhovey 0:a548a085de55 93 /*
pehrhovey 0:a548a085de55 94 #ifdef PACK_STRUCT_USE_INCLUDES
pehrhovey 0:a548a085de55 95 # include "arch/bpstruct.h"
pehrhovey 0:a548a085de55 96 #endif
pehrhovey 0:a548a085de55 97 PACK_STRUCT_BEGIN
pehrhovey 0:a548a085de55 98 struct ip_reass_helper {
pehrhovey 0:a548a085de55 99 PACK_STRUCT_FIELD(struct pbuf *next_pbuf);
pehrhovey 0:a548a085de55 100 PACK_STRUCT_FIELD(u16_t start);
pehrhovey 0:a548a085de55 101 PACK_STRUCT_FIELD(u16_t end);
pehrhovey 0:a548a085de55 102 } PACK_STRUCT_STRUCT;
pehrhovey 0:a548a085de55 103 PACK_STRUCT_END
pehrhovey 0:a548a085de55 104 #ifdef PACK_STRUCT_USE_INCLUDES
pehrhovey 0:a548a085de55 105 # include "arch/epstruct.h"
pehrhovey 0:a548a085de55 106 #endif
pehrhovey 0:a548a085de55 107 */
pehrhovey 0:a548a085de55 108 #define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \
pehrhovey 0:a548a085de55 109 (ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \
pehrhovey 0:a548a085de55 110 ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \
pehrhovey 0:a548a085de55 111 IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0
pehrhovey 0:a548a085de55 112
pehrhovey 0:a548a085de55 113 /* global variables */
pehrhovey 0:a548a085de55 114 static struct ip_reassdata *reassdatagrams;
pehrhovey 0:a548a085de55 115 static u16_t ip_reass_pbufcount;
pehrhovey 0:a548a085de55 116
pehrhovey 0:a548a085de55 117 /* function prototypes */
pehrhovey 0:a548a085de55 118 static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
pehrhovey 0:a548a085de55 119 static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
pehrhovey 0:a548a085de55 120
pehrhovey 0:a548a085de55 121 /**
pehrhovey 0:a548a085de55 122 * Reassembly timer base function
pehrhovey 0:a548a085de55 123 * for both NO_SYS == 0 and 1 (!).
pehrhovey 0:a548a085de55 124 *
pehrhovey 0:a548a085de55 125 * Should be called every 1000 msec (defined by IP_TMR_INTERVAL).
pehrhovey 0:a548a085de55 126 */
pehrhovey 0:a548a085de55 127 void
pehrhovey 0:a548a085de55 128 ip_reass_tmr(void)
pehrhovey 0:a548a085de55 129 {
pehrhovey 0:a548a085de55 130 struct ip_reassdata *r, *prev = NULL;
pehrhovey 0:a548a085de55 131
pehrhovey 0:a548a085de55 132 r = reassdatagrams;
pehrhovey 0:a548a085de55 133 while (r != NULL) {
pehrhovey 0:a548a085de55 134 /* Decrement the timer. Once it reaches 0,
pehrhovey 0:a548a085de55 135 * clean up the incomplete fragment assembly */
pehrhovey 0:a548a085de55 136 if (r->timer > 0) {
pehrhovey 0:a548a085de55 137 r->timer--;
pehrhovey 0:a548a085de55 138 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer));
pehrhovey 0:a548a085de55 139 prev = r;
pehrhovey 0:a548a085de55 140 r = r->next;
pehrhovey 0:a548a085de55 141 } else {
pehrhovey 0:a548a085de55 142 /* reassembly timed out */
pehrhovey 0:a548a085de55 143 struct ip_reassdata *tmp;
pehrhovey 0:a548a085de55 144 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n"));
pehrhovey 0:a548a085de55 145 tmp = r;
pehrhovey 0:a548a085de55 146 /* get the next pointer before freeing */
pehrhovey 0:a548a085de55 147 r = r->next;
pehrhovey 0:a548a085de55 148 /* free the helper struct and all enqueued pbufs */
pehrhovey 0:a548a085de55 149 ip_reass_free_complete_datagram(tmp, prev);
pehrhovey 0:a548a085de55 150 }
pehrhovey 0:a548a085de55 151 }
pehrhovey 0:a548a085de55 152 }
pehrhovey 0:a548a085de55 153
pehrhovey 0:a548a085de55 154 /**
pehrhovey 0:a548a085de55 155 * Free a datagram (struct ip_reassdata) and all its pbufs.
pehrhovey 0:a548a085de55 156 * Updates the total count of enqueued pbufs (ip_reass_pbufcount),
pehrhovey 0:a548a085de55 157 * SNMP counters and sends an ICMP time exceeded packet.
pehrhovey 0:a548a085de55 158 *
pehrhovey 0:a548a085de55 159 * @param ipr datagram to free
pehrhovey 0:a548a085de55 160 * @param prev the previous datagram in the linked list
pehrhovey 0:a548a085de55 161 * @return the number of pbufs freed
pehrhovey 0:a548a085de55 162 */
pehrhovey 0:a548a085de55 163 static int
pehrhovey 0:a548a085de55 164 ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
pehrhovey 0:a548a085de55 165 {
pehrhovey 0:a548a085de55 166 int pbufs_freed = 0;
pehrhovey 0:a548a085de55 167 struct pbuf *p;
pehrhovey 0:a548a085de55 168 struct ip_reass_helper *iprh;
pehrhovey 0:a548a085de55 169
pehrhovey 0:a548a085de55 170 LWIP_ASSERT("prev != ipr", prev != ipr);
pehrhovey 0:a548a085de55 171 if (prev != NULL) {
pehrhovey 0:a548a085de55 172 LWIP_ASSERT("prev->next == ipr", prev->next == ipr);
pehrhovey 0:a548a085de55 173 }
pehrhovey 0:a548a085de55 174
pehrhovey 0:a548a085de55 175 snmp_inc_ipreasmfails();
pehrhovey 0:a548a085de55 176 #if LWIP_ICMP
pehrhovey 0:a548a085de55 177 iprh = (struct ip_reass_helper *)ipr->p->payload;
pehrhovey 0:a548a085de55 178 if (iprh->start == 0) {
pehrhovey 0:a548a085de55 179 /* The first fragment was received, send ICMP time exceeded. */
pehrhovey 0:a548a085de55 180 /* First, de-queue the first pbuf from r->p. */
pehrhovey 0:a548a085de55 181 p = ipr->p;
pehrhovey 0:a548a085de55 182 ipr->p = iprh->next_pbuf;
pehrhovey 0:a548a085de55 183 /* Then, copy the original header into it. */
pehrhovey 0:a548a085de55 184 SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN);
pehrhovey 0:a548a085de55 185 icmp_time_exceeded(p, ICMP_TE_FRAG);
pehrhovey 0:a548a085de55 186 pbufs_freed += pbuf_clen(p);
pehrhovey 0:a548a085de55 187 pbuf_free(p);
pehrhovey 0:a548a085de55 188 }
pehrhovey 0:a548a085de55 189 #endif /* LWIP_ICMP */
pehrhovey 0:a548a085de55 190
pehrhovey 0:a548a085de55 191 /* First, free all received pbufs. The individual pbufs need to be released
pehrhovey 0:a548a085de55 192 separately as they have not yet been chained */
pehrhovey 0:a548a085de55 193 p = ipr->p;
pehrhovey 0:a548a085de55 194 while (p != NULL) {
pehrhovey 0:a548a085de55 195 struct pbuf *pcur;
pehrhovey 0:a548a085de55 196 iprh = (struct ip_reass_helper *)p->payload;
pehrhovey 0:a548a085de55 197 pcur = p;
pehrhovey 0:a548a085de55 198 /* get the next pointer before freeing */
pehrhovey 0:a548a085de55 199 p = iprh->next_pbuf;
pehrhovey 0:a548a085de55 200 pbufs_freed += pbuf_clen(pcur);
pehrhovey 0:a548a085de55 201 pbuf_free(pcur);
pehrhovey 0:a548a085de55 202 }
pehrhovey 0:a548a085de55 203 /* Then, unchain the struct ip_reassdata from the list and free it. */
pehrhovey 0:a548a085de55 204 ip_reass_dequeue_datagram(ipr, prev);
pehrhovey 0:a548a085de55 205 LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed);
pehrhovey 0:a548a085de55 206 ip_reass_pbufcount -= pbufs_freed;
pehrhovey 0:a548a085de55 207
pehrhovey 0:a548a085de55 208 return pbufs_freed;
pehrhovey 0:a548a085de55 209 }
pehrhovey 0:a548a085de55 210
pehrhovey 0:a548a085de55 211 #if IP_REASS_FREE_OLDEST
pehrhovey 0:a548a085de55 212 /**
pehrhovey 0:a548a085de55 213 * Free the oldest datagram to make room for enqueueing new fragments.
pehrhovey 0:a548a085de55 214 * The datagram 'fraghdr' belongs to is not freed!
pehrhovey 0:a548a085de55 215 *
pehrhovey 0:a548a085de55 216 * @param fraghdr IP header of the current fragment
pehrhovey 0:a548a085de55 217 * @param pbufs_needed number of pbufs needed to enqueue
pehrhovey 0:a548a085de55 218 * (used for freeing other datagrams if not enough space)
pehrhovey 0:a548a085de55 219 * @return the number of pbufs freed
pehrhovey 0:a548a085de55 220 */
pehrhovey 0:a548a085de55 221 static int
pehrhovey 0:a548a085de55 222 ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
pehrhovey 0:a548a085de55 223 {
pehrhovey 0:a548a085de55 224 /* @todo Can't we simply remove the last datagram in the
pehrhovey 0:a548a085de55 225 * linked list behind reassdatagrams?
pehrhovey 0:a548a085de55 226 */
pehrhovey 0:a548a085de55 227 struct ip_reassdata *r, *oldest, *prev;
pehrhovey 0:a548a085de55 228 int pbufs_freed = 0, pbufs_freed_current;
pehrhovey 0:a548a085de55 229 int other_datagrams;
pehrhovey 0:a548a085de55 230
pehrhovey 0:a548a085de55 231 /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs,
pehrhovey 0:a548a085de55 232 * but don't free the datagram that 'fraghdr' belongs to! */
pehrhovey 0:a548a085de55 233 do {
pehrhovey 0:a548a085de55 234 oldest = NULL;
pehrhovey 0:a548a085de55 235 prev = NULL;
pehrhovey 0:a548a085de55 236 other_datagrams = 0;
pehrhovey 0:a548a085de55 237 r = reassdatagrams;
pehrhovey 0:a548a085de55 238 while (r != NULL) {
pehrhovey 0:a548a085de55 239 if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) {
pehrhovey 0:a548a085de55 240 /* Not the same datagram as fraghdr */
pehrhovey 0:a548a085de55 241 other_datagrams++;
pehrhovey 0:a548a085de55 242 if (oldest == NULL) {
pehrhovey 0:a548a085de55 243 oldest = r;
pehrhovey 0:a548a085de55 244 } else if (r->timer <= oldest->timer) {
pehrhovey 0:a548a085de55 245 /* older than the previous oldest */
pehrhovey 0:a548a085de55 246 oldest = r;
pehrhovey 0:a548a085de55 247 }
pehrhovey 0:a548a085de55 248 }
pehrhovey 0:a548a085de55 249 if (r->next != NULL) {
pehrhovey 0:a548a085de55 250 prev = r;
pehrhovey 0:a548a085de55 251 }
pehrhovey 0:a548a085de55 252 r = r->next;
pehrhovey 0:a548a085de55 253 }
pehrhovey 0:a548a085de55 254 if (oldest != NULL) {
pehrhovey 0:a548a085de55 255 pbufs_freed_current = ip_reass_free_complete_datagram(oldest, prev);
pehrhovey 0:a548a085de55 256 pbufs_freed += pbufs_freed_current;
pehrhovey 0:a548a085de55 257 }
pehrhovey 0:a548a085de55 258 } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1));
pehrhovey 0:a548a085de55 259 return pbufs_freed;
pehrhovey 0:a548a085de55 260 }
pehrhovey 0:a548a085de55 261 #endif /* IP_REASS_FREE_OLDEST */
pehrhovey 0:a548a085de55 262
pehrhovey 0:a548a085de55 263 /**
pehrhovey 0:a548a085de55 264 * Enqueues a new fragment into the fragment queue
pehrhovey 0:a548a085de55 265 * @param fraghdr points to the new fragments IP hdr
pehrhovey 0:a548a085de55 266 * @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space)
pehrhovey 0:a548a085de55 267 * @return A pointer to the queue location into which the fragment was enqueued
pehrhovey 0:a548a085de55 268 */
pehrhovey 0:a548a085de55 269 static struct ip_reassdata*
pehrhovey 0:a548a085de55 270 ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen)
pehrhovey 0:a548a085de55 271 {
pehrhovey 0:a548a085de55 272 struct ip_reassdata* ipr;
pehrhovey 0:a548a085de55 273 /* No matching previous fragment found, allocate a new reassdata struct */
pehrhovey 0:a548a085de55 274 ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
pehrhovey 0:a548a085de55 275 if (ipr == NULL) {
pehrhovey 0:a548a085de55 276 #if IP_REASS_FREE_OLDEST
pehrhovey 0:a548a085de55 277 if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) {
pehrhovey 0:a548a085de55 278 ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
pehrhovey 0:a548a085de55 279 }
pehrhovey 0:a548a085de55 280 if (ipr == NULL)
pehrhovey 0:a548a085de55 281 #endif /* IP_REASS_FREE_OLDEST */
pehrhovey 0:a548a085de55 282 {
pehrhovey 0:a548a085de55 283 IPFRAG_STATS_INC(ip_frag.memerr);
pehrhovey 0:a548a085de55 284 LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n"));
pehrhovey 0:a548a085de55 285 return NULL;
pehrhovey 0:a548a085de55 286 }
pehrhovey 0:a548a085de55 287 }
pehrhovey 0:a548a085de55 288 memset(ipr, 0, sizeof(struct ip_reassdata));
pehrhovey 0:a548a085de55 289 ipr->timer = IP_REASS_MAXAGE;
pehrhovey 0:a548a085de55 290
pehrhovey 0:a548a085de55 291 /* enqueue the new structure to the front of the list */
pehrhovey 0:a548a085de55 292 ipr->next = reassdatagrams;
pehrhovey 0:a548a085de55 293 reassdatagrams = ipr;
pehrhovey 0:a548a085de55 294 /* copy the ip header for later tests and input */
pehrhovey 0:a548a085de55 295 /* @todo: no ip options supported? */
pehrhovey 0:a548a085de55 296 SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN);
pehrhovey 0:a548a085de55 297 return ipr;
pehrhovey 0:a548a085de55 298 }
pehrhovey 0:a548a085de55 299
pehrhovey 0:a548a085de55 300 /**
pehrhovey 0:a548a085de55 301 * Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs.
pehrhovey 0:a548a085de55 302 * @param ipr points to the queue entry to dequeue
pehrhovey 0:a548a085de55 303 */
pehrhovey 0:a548a085de55 304 static void
pehrhovey 0:a548a085de55 305 ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
pehrhovey 0:a548a085de55 306 {
pehrhovey 0:a548a085de55 307
pehrhovey 0:a548a085de55 308 /* dequeue the reass struct */
pehrhovey 0:a548a085de55 309 if (reassdatagrams == ipr) {
pehrhovey 0:a548a085de55 310 /* it was the first in the list */
pehrhovey 0:a548a085de55 311 reassdatagrams = ipr->next;
pehrhovey 0:a548a085de55 312 } else {
pehrhovey 0:a548a085de55 313 /* it wasn't the first, so it must have a valid 'prev' */
pehrhovey 0:a548a085de55 314 LWIP_ASSERT("sanity check linked list", prev != NULL);
pehrhovey 0:a548a085de55 315 prev->next = ipr->next;
pehrhovey 0:a548a085de55 316 }
pehrhovey 0:a548a085de55 317
pehrhovey 0:a548a085de55 318 /* now we can free the ip_reass struct */
pehrhovey 0:a548a085de55 319 memp_free(MEMP_REASSDATA, ipr);
pehrhovey 0:a548a085de55 320 }
pehrhovey 0:a548a085de55 321
pehrhovey 0:a548a085de55 322 /**
pehrhovey 0:a548a085de55 323 * Chain a new pbuf into the pbuf list that composes the datagram. The pbuf list
pehrhovey 0:a548a085de55 324 * will grow over time as new pbufs are rx.
pehrhovey 0:a548a085de55 325 * Also checks that the datagram passes basic continuity checks (if the last
pehrhovey 0:a548a085de55 326 * fragment was received at least once).
pehrhovey 0:a548a085de55 327 * @param root_p points to the 'root' pbuf for the current datagram being assembled.
pehrhovey 0:a548a085de55 328 * @param new_p points to the pbuf for the current fragment
pehrhovey 0:a548a085de55 329 * @return 0 if invalid, >0 otherwise
pehrhovey 0:a548a085de55 330 */
pehrhovey 0:a548a085de55 331 static int
pehrhovey 0:a548a085de55 332 ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p)
pehrhovey 0:a548a085de55 333 {
pehrhovey 0:a548a085de55 334 struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
pehrhovey 0:a548a085de55 335 struct pbuf *q;
pehrhovey 0:a548a085de55 336 u16_t offset,len;
pehrhovey 0:a548a085de55 337 struct ip_hdr *fraghdr;
pehrhovey 0:a548a085de55 338 int valid = 1;
pehrhovey 0:a548a085de55 339
pehrhovey 0:a548a085de55 340 /* Extract length and fragment offset from current fragment */
pehrhovey 0:a548a085de55 341 fraghdr = (struct ip_hdr*)new_p->payload;
pehrhovey 0:a548a085de55 342 len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
pehrhovey 0:a548a085de55 343 offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
pehrhovey 0:a548a085de55 344
pehrhovey 0:a548a085de55 345 /* overwrite the fragment's ip header from the pbuf with our helper struct,
pehrhovey 0:a548a085de55 346 * and setup the embedded helper structure. */
pehrhovey 0:a548a085de55 347 /* make sure the struct ip_reass_helper fits into the IP header */
pehrhovey 0:a548a085de55 348 LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN",
pehrhovey 0:a548a085de55 349 sizeof(struct ip_reass_helper) <= IP_HLEN);
pehrhovey 0:a548a085de55 350 iprh = (struct ip_reass_helper*)new_p->payload;
pehrhovey 0:a548a085de55 351 iprh->next_pbuf = NULL;
pehrhovey 0:a548a085de55 352 iprh->start = offset;
pehrhovey 0:a548a085de55 353 iprh->end = offset + len;
pehrhovey 0:a548a085de55 354
pehrhovey 0:a548a085de55 355 /* Iterate through until we either get to the end of the list (append),
pehrhovey 0:a548a085de55 356 * or we find on with a larger offset (insert). */
pehrhovey 0:a548a085de55 357 for (q = ipr->p; q != NULL;) {
pehrhovey 0:a548a085de55 358 iprh_tmp = (struct ip_reass_helper*)q->payload;
pehrhovey 0:a548a085de55 359 if (iprh->start < iprh_tmp->start) {
pehrhovey 0:a548a085de55 360 /* the new pbuf should be inserted before this */
pehrhovey 0:a548a085de55 361 iprh->next_pbuf = q;
pehrhovey 0:a548a085de55 362 if (iprh_prev != NULL) {
pehrhovey 0:a548a085de55 363 /* not the fragment with the lowest offset */
pehrhovey 0:a548a085de55 364 #if IP_REASS_CHECK_OVERLAP
pehrhovey 0:a548a085de55 365 if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) {
pehrhovey 0:a548a085de55 366 /* fragment overlaps with previous or following, throw away */
pehrhovey 0:a548a085de55 367 goto freepbuf;
pehrhovey 0:a548a085de55 368 }
pehrhovey 0:a548a085de55 369 #endif /* IP_REASS_CHECK_OVERLAP */
pehrhovey 0:a548a085de55 370 iprh_prev->next_pbuf = new_p;
pehrhovey 0:a548a085de55 371 } else {
pehrhovey 0:a548a085de55 372 /* fragment with the lowest offset */
pehrhovey 0:a548a085de55 373 ipr->p = new_p;
pehrhovey 0:a548a085de55 374 }
pehrhovey 0:a548a085de55 375 break;
pehrhovey 0:a548a085de55 376 } else if(iprh->start == iprh_tmp->start) {
pehrhovey 0:a548a085de55 377 /* received the same datagram twice: no need to keep the datagram */
pehrhovey 0:a548a085de55 378 goto freepbuf;
pehrhovey 0:a548a085de55 379 #if IP_REASS_CHECK_OVERLAP
pehrhovey 0:a548a085de55 380 } else if(iprh->start < iprh_tmp->end) {
pehrhovey 0:a548a085de55 381 /* overlap: no need to keep the new datagram */
pehrhovey 0:a548a085de55 382 goto freepbuf;
pehrhovey 0:a548a085de55 383 #endif /* IP_REASS_CHECK_OVERLAP */
pehrhovey 0:a548a085de55 384 } else {
pehrhovey 0:a548a085de55 385 /* Check if the fragments received so far have no wholes. */
pehrhovey 0:a548a085de55 386 if (iprh_prev != NULL) {
pehrhovey 0:a548a085de55 387 if (iprh_prev->end != iprh_tmp->start) {
pehrhovey 0:a548a085de55 388 /* There is a fragment missing between the current
pehrhovey 0:a548a085de55 389 * and the previous fragment */
pehrhovey 0:a548a085de55 390 valid = 0;
pehrhovey 0:a548a085de55 391 }
pehrhovey 0:a548a085de55 392 }
pehrhovey 0:a548a085de55 393 }
pehrhovey 0:a548a085de55 394 q = iprh_tmp->next_pbuf;
pehrhovey 0:a548a085de55 395 iprh_prev = iprh_tmp;
pehrhovey 0:a548a085de55 396 }
pehrhovey 0:a548a085de55 397
pehrhovey 0:a548a085de55 398 /* If q is NULL, then we made it to the end of the list. Determine what to do now */
pehrhovey 0:a548a085de55 399 if (q == NULL) {
pehrhovey 0:a548a085de55 400 if (iprh_prev != NULL) {
pehrhovey 0:a548a085de55 401 /* this is (for now), the fragment with the highest offset:
pehrhovey 0:a548a085de55 402 * chain it to the last fragment */
pehrhovey 0:a548a085de55 403 #if IP_REASS_CHECK_OVERLAP
pehrhovey 0:a548a085de55 404 LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start);
pehrhovey 0:a548a085de55 405 #endif /* IP_REASS_CHECK_OVERLAP */
pehrhovey 0:a548a085de55 406 iprh_prev->next_pbuf = new_p;
pehrhovey 0:a548a085de55 407 if (iprh_prev->end != iprh->start) {
pehrhovey 0:a548a085de55 408 valid = 0;
pehrhovey 0:a548a085de55 409 }
pehrhovey 0:a548a085de55 410 } else {
pehrhovey 0:a548a085de55 411 #if IP_REASS_CHECK_OVERLAP
pehrhovey 0:a548a085de55 412 LWIP_ASSERT("no previous fragment, this must be the first fragment!",
pehrhovey 0:a548a085de55 413 ipr->p == NULL);
pehrhovey 0:a548a085de55 414 #endif /* IP_REASS_CHECK_OVERLAP */
pehrhovey 0:a548a085de55 415 /* this is the first fragment we ever received for this ip datagram */
pehrhovey 0:a548a085de55 416 ipr->p = new_p;
pehrhovey 0:a548a085de55 417 }
pehrhovey 0:a548a085de55 418 }
pehrhovey 0:a548a085de55 419
pehrhovey 0:a548a085de55 420 /* At this point, the validation part begins: */
pehrhovey 0:a548a085de55 421 /* If we already received the last fragment */
pehrhovey 0:a548a085de55 422 if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) {
pehrhovey 0:a548a085de55 423 /* and had no wholes so far */
pehrhovey 0:a548a085de55 424 if (valid) {
pehrhovey 0:a548a085de55 425 /* then check if the rest of the fragments is here */
pehrhovey 0:a548a085de55 426 /* Check if the queue starts with the first datagram */
pehrhovey 0:a548a085de55 427 if (((struct ip_reass_helper*)ipr->p->payload)->start != 0) {
pehrhovey 0:a548a085de55 428 valid = 0;
pehrhovey 0:a548a085de55 429 } else {
pehrhovey 0:a548a085de55 430 /* and check that there are no wholes after this datagram */
pehrhovey 0:a548a085de55 431 iprh_prev = iprh;
pehrhovey 0:a548a085de55 432 q = iprh->next_pbuf;
pehrhovey 0:a548a085de55 433 while (q != NULL) {
pehrhovey 0:a548a085de55 434 iprh = (struct ip_reass_helper*)q->payload;
pehrhovey 0:a548a085de55 435 if (iprh_prev->end != iprh->start) {
pehrhovey 0:a548a085de55 436 valid = 0;
pehrhovey 0:a548a085de55 437 break;
pehrhovey 0:a548a085de55 438 }
pehrhovey 0:a548a085de55 439 iprh_prev = iprh;
pehrhovey 0:a548a085de55 440 q = iprh->next_pbuf;
pehrhovey 0:a548a085de55 441 }
pehrhovey 0:a548a085de55 442 /* if still valid, all fragments are received
pehrhovey 0:a548a085de55 443 * (because to the MF==0 already arrived */
pehrhovey 0:a548a085de55 444 if (valid) {
pehrhovey 0:a548a085de55 445 LWIP_ASSERT("sanity check", ipr->p != NULL);
pehrhovey 0:a548a085de55 446 LWIP_ASSERT("sanity check",
pehrhovey 0:a548a085de55 447 ((struct ip_reass_helper*)ipr->p->payload) != iprh);
pehrhovey 0:a548a085de55 448 LWIP_ASSERT("validate_datagram:next_pbuf!=NULL",
pehrhovey 0:a548a085de55 449 iprh->next_pbuf == NULL);
pehrhovey 0:a548a085de55 450 LWIP_ASSERT("validate_datagram:datagram end!=datagram len",
pehrhovey 0:a548a085de55 451 iprh->end == ipr->datagram_len);
pehrhovey 0:a548a085de55 452 }
pehrhovey 0:a548a085de55 453 }
pehrhovey 0:a548a085de55 454 }
pehrhovey 0:a548a085de55 455 /* If valid is 0 here, there are some fragments missing in the middle
pehrhovey 0:a548a085de55 456 * (since MF == 0 has already arrived). Such datagrams simply time out if
pehrhovey 0:a548a085de55 457 * no more fragments are received... */
pehrhovey 0:a548a085de55 458 return valid;
pehrhovey 0:a548a085de55 459 }
pehrhovey 0:a548a085de55 460 /* If we come here, not all fragments were received, yet! */
pehrhovey 0:a548a085de55 461 return 0; /* not yet valid! */
pehrhovey 0:a548a085de55 462 #if IP_REASS_CHECK_OVERLAP
pehrhovey 0:a548a085de55 463 freepbuf:
pehrhovey 0:a548a085de55 464 ip_reass_pbufcount -= pbuf_clen(new_p);
pehrhovey 0:a548a085de55 465 pbuf_free(new_p);
pehrhovey 0:a548a085de55 466 return 0;
pehrhovey 0:a548a085de55 467 #endif /* IP_REASS_CHECK_OVERLAP */
pehrhovey 0:a548a085de55 468 }
pehrhovey 0:a548a085de55 469
pehrhovey 0:a548a085de55 470 /**
pehrhovey 0:a548a085de55 471 * Reassembles incoming IP fragments into an IP datagram.
pehrhovey 0:a548a085de55 472 *
pehrhovey 0:a548a085de55 473 * @param p points to a pbuf chain of the fragment
pehrhovey 0:a548a085de55 474 * @return NULL if reassembly is incomplete, ? otherwise
pehrhovey 0:a548a085de55 475 */
pehrhovey 0:a548a085de55 476 struct pbuf *
pehrhovey 0:a548a085de55 477 ip_reass(struct pbuf *p)
pehrhovey 0:a548a085de55 478 {
pehrhovey 0:a548a085de55 479 struct pbuf *r;
pehrhovey 0:a548a085de55 480 struct ip_hdr *fraghdr;
pehrhovey 0:a548a085de55 481 struct ip_reassdata *ipr;
pehrhovey 0:a548a085de55 482 struct ip_reass_helper *iprh;
pehrhovey 0:a548a085de55 483 u16_t offset, len;
pehrhovey 0:a548a085de55 484 u8_t clen;
pehrhovey 0:a548a085de55 485 struct ip_reassdata *ipr_prev = NULL;
pehrhovey 0:a548a085de55 486
pehrhovey 0:a548a085de55 487 IPFRAG_STATS_INC(ip_frag.recv);
pehrhovey 0:a548a085de55 488 snmp_inc_ipreasmreqds();
pehrhovey 0:a548a085de55 489
pehrhovey 0:a548a085de55 490 fraghdr = (struct ip_hdr*)p->payload;
pehrhovey 0:a548a085de55 491
pehrhovey 0:a548a085de55 492 if ((IPH_HL(fraghdr) * 4) != IP_HLEN) {
pehrhovey 0:a548a085de55 493 LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n"));
pehrhovey 0:a548a085de55 494 IPFRAG_STATS_INC(ip_frag.err);
pehrhovey 0:a548a085de55 495 goto nullreturn;
pehrhovey 0:a548a085de55 496 }
pehrhovey 0:a548a085de55 497
pehrhovey 0:a548a085de55 498 offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
pehrhovey 0:a548a085de55 499 len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
pehrhovey 0:a548a085de55 500
pehrhovey 0:a548a085de55 501 /* Check if we are allowed to enqueue more datagrams. */
pehrhovey 0:a548a085de55 502 clen = pbuf_clen(p);
pehrhovey 0:a548a085de55 503 if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
pehrhovey 0:a548a085de55 504 #if IP_REASS_FREE_OLDEST
pehrhovey 0:a548a085de55 505 if (!ip_reass_remove_oldest_datagram(fraghdr, clen) ||
pehrhovey 0:a548a085de55 506 ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS))
pehrhovey 0:a548a085de55 507 #endif /* IP_REASS_FREE_OLDEST */
pehrhovey 0:a548a085de55 508 {
pehrhovey 0:a548a085de55 509 /* No datagram could be freed and still too many pbufs enqueued */
pehrhovey 0:a548a085de55 510 LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n",
pehrhovey 0:a548a085de55 511 ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS));
pehrhovey 0:a548a085de55 512 IPFRAG_STATS_INC(ip_frag.memerr);
pehrhovey 0:a548a085de55 513 /* @todo: send ICMP time exceeded here? */
pehrhovey 0:a548a085de55 514 /* drop this pbuf */
pehrhovey 0:a548a085de55 515 goto nullreturn;
pehrhovey 0:a548a085de55 516 }
pehrhovey 0:a548a085de55 517 }
pehrhovey 0:a548a085de55 518
pehrhovey 0:a548a085de55 519 /* Look for the datagram the fragment belongs to in the current datagram queue,
pehrhovey 0:a548a085de55 520 * remembering the previous in the queue for later dequeueing. */
pehrhovey 0:a548a085de55 521 for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) {
pehrhovey 0:a548a085de55 522 /* Check if the incoming fragment matches the one currently present
pehrhovey 0:a548a085de55 523 in the reassembly buffer. If so, we proceed with copying the
pehrhovey 0:a548a085de55 524 fragment into the buffer. */
pehrhovey 0:a548a085de55 525 if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) {
pehrhovey 0:a548a085de55 526 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n",
pehrhovey 0:a548a085de55 527 ntohs(IPH_ID(fraghdr))));
pehrhovey 0:a548a085de55 528 IPFRAG_STATS_INC(ip_frag.cachehit);
pehrhovey 0:a548a085de55 529 break;
pehrhovey 0:a548a085de55 530 }
pehrhovey 0:a548a085de55 531 ipr_prev = ipr;
pehrhovey 0:a548a085de55 532 }
pehrhovey 0:a548a085de55 533
pehrhovey 0:a548a085de55 534 if (ipr == NULL) {
pehrhovey 0:a548a085de55 535 /* Enqueue a new datagram into the datagram queue */
pehrhovey 0:a548a085de55 536 ipr = ip_reass_enqueue_new_datagram(fraghdr, clen);
pehrhovey 0:a548a085de55 537 /* Bail if unable to enqueue */
pehrhovey 0:a548a085de55 538 if(ipr == NULL) {
pehrhovey 0:a548a085de55 539 goto nullreturn;
pehrhovey 0:a548a085de55 540 }
pehrhovey 0:a548a085de55 541 } else {
pehrhovey 0:a548a085de55 542 if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) &&
pehrhovey 0:a548a085de55 543 ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) {
pehrhovey 0:a548a085de55 544 /* ipr->iphdr is not the header from the first fragment, but fraghdr is
pehrhovey 0:a548a085de55 545 * -> copy fraghdr into ipr->iphdr since we want to have the header
pehrhovey 0:a548a085de55 546 * of the first fragment (for ICMP time exceeded and later, for copying
pehrhovey 0:a548a085de55 547 * all options, if supported)*/
pehrhovey 0:a548a085de55 548 SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN);
pehrhovey 0:a548a085de55 549 }
pehrhovey 0:a548a085de55 550 }
pehrhovey 0:a548a085de55 551 /* Track the current number of pbufs current 'in-flight', in order to limit
pehrhovey 0:a548a085de55 552 the number of fragments that may be enqueued at any one time */
pehrhovey 0:a548a085de55 553 ip_reass_pbufcount += clen;
pehrhovey 0:a548a085de55 554
pehrhovey 0:a548a085de55 555 /* At this point, we have either created a new entry or pointing
pehrhovey 0:a548a085de55 556 * to an existing one */
pehrhovey 0:a548a085de55 557
pehrhovey 0:a548a085de55 558 /* check for 'no more fragments', and update queue entry*/
pehrhovey 0:a548a085de55 559 if ((ntohs(IPH_OFFSET(fraghdr)) & IP_MF) == 0) {
pehrhovey 0:a548a085de55 560 ipr->flags |= IP_REASS_FLAG_LASTFRAG;
pehrhovey 0:a548a085de55 561 ipr->datagram_len = offset + len;
pehrhovey 0:a548a085de55 562 LWIP_DEBUGF(IP_REASS_DEBUG,
pehrhovey 0:a548a085de55 563 ("ip_reass: last fragment seen, total len %"S16_F"\n",
pehrhovey 0:a548a085de55 564 ipr->datagram_len));
pehrhovey 0:a548a085de55 565 }
pehrhovey 0:a548a085de55 566 /* find the right place to insert this pbuf */
pehrhovey 0:a548a085de55 567 /* @todo: trim pbufs if fragments are overlapping */
pehrhovey 0:a548a085de55 568 if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) {
pehrhovey 0:a548a085de55 569 /* the totally last fragment (flag more fragments = 0) was received at least
pehrhovey 0:a548a085de55 570 * once AND all fragments are received */
pehrhovey 0:a548a085de55 571 ipr->datagram_len += IP_HLEN;
pehrhovey 0:a548a085de55 572
pehrhovey 0:a548a085de55 573 /* save the second pbuf before copying the header over the pointer */
pehrhovey 0:a548a085de55 574 r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf;
pehrhovey 0:a548a085de55 575
pehrhovey 0:a548a085de55 576 /* copy the original ip header back to the first pbuf */
pehrhovey 0:a548a085de55 577 fraghdr = (struct ip_hdr*)(ipr->p->payload);
pehrhovey 0:a548a085de55 578 SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN);
pehrhovey 0:a548a085de55 579 IPH_LEN_SET(fraghdr, htons(ipr->datagram_len));
pehrhovey 0:a548a085de55 580 IPH_OFFSET_SET(fraghdr, 0);
pehrhovey 0:a548a085de55 581 IPH_CHKSUM_SET(fraghdr, 0);
pehrhovey 0:a548a085de55 582 /* @todo: do we need to set calculate the correct checksum? */
pehrhovey 0:a548a085de55 583 IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN));
pehrhovey 0:a548a085de55 584
pehrhovey 0:a548a085de55 585 p = ipr->p;
pehrhovey 0:a548a085de55 586
pehrhovey 0:a548a085de55 587 /* chain together the pbufs contained within the reass_data list. */
pehrhovey 0:a548a085de55 588 while(r != NULL) {
pehrhovey 0:a548a085de55 589 iprh = (struct ip_reass_helper*)r->payload;
pehrhovey 0:a548a085de55 590
pehrhovey 0:a548a085de55 591 /* hide the ip header for every succeding fragment */
pehrhovey 0:a548a085de55 592 pbuf_header(r, -IP_HLEN);
pehrhovey 0:a548a085de55 593 pbuf_cat(p, r);
pehrhovey 0:a548a085de55 594 r = iprh->next_pbuf;
pehrhovey 0:a548a085de55 595 }
pehrhovey 0:a548a085de55 596 /* release the sources allocate for the fragment queue entry */
pehrhovey 0:a548a085de55 597 ip_reass_dequeue_datagram(ipr, ipr_prev);
pehrhovey 0:a548a085de55 598
pehrhovey 0:a548a085de55 599 /* and adjust the number of pbufs currently queued for reassembly. */
pehrhovey 0:a548a085de55 600 ip_reass_pbufcount -= pbuf_clen(p);
pehrhovey 0:a548a085de55 601
pehrhovey 0:a548a085de55 602 /* Return the pbuf chain */
pehrhovey 0:a548a085de55 603 return p;
pehrhovey 0:a548a085de55 604 }
pehrhovey 0:a548a085de55 605 /* the datagram is not (yet?) reassembled completely */
pehrhovey 0:a548a085de55 606 LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount));
pehrhovey 0:a548a085de55 607 return NULL;
pehrhovey 0:a548a085de55 608
pehrhovey 0:a548a085de55 609 nullreturn:
pehrhovey 0:a548a085de55 610 LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n"));
pehrhovey 0:a548a085de55 611 IPFRAG_STATS_INC(ip_frag.drop);
pehrhovey 0:a548a085de55 612 pbuf_free(p);
pehrhovey 0:a548a085de55 613 return NULL;
pehrhovey 0:a548a085de55 614 }
pehrhovey 0:a548a085de55 615 #endif /* IP_REASSEMBLY */
pehrhovey 0:a548a085de55 616
pehrhovey 0:a548a085de55 617 #if IP_FRAG
pehrhovey 0:a548a085de55 618 #if IP_FRAG_USES_STATIC_BUF
pehrhovey 0:a548a085de55 619 static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU + MEM_ALIGNMENT - 1)];
pehrhovey 0:a548a085de55 620 #endif /* IP_FRAG_USES_STATIC_BUF */
pehrhovey 0:a548a085de55 621
pehrhovey 0:a548a085de55 622 /**
pehrhovey 0:a548a085de55 623 * Fragment an IP datagram if too large for the netif.
pehrhovey 0:a548a085de55 624 *
pehrhovey 0:a548a085de55 625 * Chop the datagram in MTU sized chunks and send them in order
pehrhovey 0:a548a085de55 626 * by using a fixed size static memory buffer (PBUF_REF) or
pehrhovey 0:a548a085de55 627 * point PBUF_REFs into p (depending on IP_FRAG_USES_STATIC_BUF).
pehrhovey 0:a548a085de55 628 *
pehrhovey 0:a548a085de55 629 * @param p ip packet to send
pehrhovey 0:a548a085de55 630 * @param netif the netif on which to send
pehrhovey 0:a548a085de55 631 * @param dest destination ip address to which to send
pehrhovey 0:a548a085de55 632 *
pehrhovey 0:a548a085de55 633 * @return ERR_OK if sent successfully, err_t otherwise
pehrhovey 0:a548a085de55 634 */
pehrhovey 0:a548a085de55 635 err_t
pehrhovey 0:a548a085de55 636 ip_frag(struct pbuf *p, struct netif *netif, struct ip_addr *dest)
pehrhovey 0:a548a085de55 637 {
pehrhovey 0:a548a085de55 638 struct pbuf *rambuf;
pehrhovey 0:a548a085de55 639 #if IP_FRAG_USES_STATIC_BUF
pehrhovey 0:a548a085de55 640 struct pbuf *header;
pehrhovey 0:a548a085de55 641 #else
pehrhovey 0:a548a085de55 642 struct pbuf *newpbuf;
pehrhovey 0:a548a085de55 643 struct ip_hdr *original_iphdr;
pehrhovey 0:a548a085de55 644 #endif
pehrhovey 0:a548a085de55 645 struct ip_hdr *iphdr;
pehrhovey 0:a548a085de55 646 u16_t nfb;
pehrhovey 0:a548a085de55 647 u16_t left, cop;
pehrhovey 0:a548a085de55 648 u16_t mtu = netif->mtu;
pehrhovey 0:a548a085de55 649 u16_t ofo, omf;
pehrhovey 0:a548a085de55 650 u16_t last;
pehrhovey 0:a548a085de55 651 u16_t poff = IP_HLEN;
pehrhovey 0:a548a085de55 652 u16_t tmp;
pehrhovey 0:a548a085de55 653 #if !IP_FRAG_USES_STATIC_BUF
pehrhovey 0:a548a085de55 654 u16_t newpbuflen = 0;
pehrhovey 0:a548a085de55 655 u16_t left_to_copy;
pehrhovey 0:a548a085de55 656 #endif
pehrhovey 0:a548a085de55 657
pehrhovey 0:a548a085de55 658 /* Get a RAM based MTU sized pbuf */
pehrhovey 0:a548a085de55 659 #if IP_FRAG_USES_STATIC_BUF
pehrhovey 0:a548a085de55 660 /* When using a static buffer, we use a PBUF_REF, which we will
pehrhovey 0:a548a085de55 661 * use to reference the packet (without link header).
pehrhovey 0:a548a085de55 662 * Layer and length is irrelevant.
pehrhovey 0:a548a085de55 663 */
pehrhovey 0:a548a085de55 664 rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);
pehrhovey 0:a548a085de55 665 if (rambuf == NULL) {
pehrhovey 0:a548a085de55 666 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n"));
pehrhovey 0:a548a085de55 667 return ERR_MEM;
pehrhovey 0:a548a085de55 668 }
pehrhovey 0:a548a085de55 669 rambuf->tot_len = rambuf->len = mtu;
pehrhovey 0:a548a085de55 670 rambuf->payload = LWIP_MEM_ALIGN((void *)buf);
pehrhovey 0:a548a085de55 671
pehrhovey 0:a548a085de55 672 /* Copy the IP header in it */
pehrhovey 0:a548a085de55 673 iphdr = rambuf->payload;
pehrhovey 0:a548a085de55 674 SMEMCPY(iphdr, p->payload, IP_HLEN);
pehrhovey 0:a548a085de55 675 #else /* IP_FRAG_USES_STATIC_BUF */
pehrhovey 0:a548a085de55 676 original_iphdr = (struct ip_hdr *)p->payload;
pehrhovey 0:a548a085de55 677 iphdr = original_iphdr;
pehrhovey 0:a548a085de55 678 #endif /* IP_FRAG_USES_STATIC_BUF */
pehrhovey 0:a548a085de55 679
pehrhovey 0:a548a085de55 680 /* Save original offset */
pehrhovey 0:a548a085de55 681 tmp = ntohs(IPH_OFFSET(iphdr));
pehrhovey 0:a548a085de55 682 ofo = tmp & IP_OFFMASK;
pehrhovey 0:a548a085de55 683 omf = tmp & IP_MF;
pehrhovey 0:a548a085de55 684
pehrhovey 0:a548a085de55 685 left = p->tot_len - IP_HLEN;
pehrhovey 0:a548a085de55 686
pehrhovey 0:a548a085de55 687 nfb = (mtu - IP_HLEN) / 8;
pehrhovey 0:a548a085de55 688
pehrhovey 0:a548a085de55 689 while (left) {
pehrhovey 0:a548a085de55 690 last = (left <= mtu - IP_HLEN);
pehrhovey 0:a548a085de55 691
pehrhovey 0:a548a085de55 692 /* Set new offset and MF flag */
pehrhovey 0:a548a085de55 693 tmp = omf | (IP_OFFMASK & (ofo));
pehrhovey 0:a548a085de55 694 if (!last)
pehrhovey 0:a548a085de55 695 tmp = tmp | IP_MF;
pehrhovey 0:a548a085de55 696
pehrhovey 0:a548a085de55 697 /* Fill this fragment */
pehrhovey 0:a548a085de55 698 cop = last ? left : nfb * 8;
pehrhovey 0:a548a085de55 699
pehrhovey 0:a548a085de55 700 #if IP_FRAG_USES_STATIC_BUF
pehrhovey 0:a548a085de55 701 poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff);
pehrhovey 0:a548a085de55 702 #else /* IP_FRAG_USES_STATIC_BUF */
pehrhovey 0:a548a085de55 703 /* When not using a static buffer, create a chain of pbufs.
pehrhovey 0:a548a085de55 704 * The first will be a PBUF_RAM holding the link and IP header.
pehrhovey 0:a548a085de55 705 * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
pehrhovey 0:a548a085de55 706 * but limited to the size of an mtu.
pehrhovey 0:a548a085de55 707 */
pehrhovey 0:a548a085de55 708 rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM);
pehrhovey 0:a548a085de55 709 if (rambuf == NULL) {
pehrhovey 0:a548a085de55 710 return ERR_MEM;
pehrhovey 0:a548a085de55 711 }
pehrhovey 0:a548a085de55 712 LWIP_ASSERT("this needs a pbuf in one piece!",
pehrhovey 0:a548a085de55 713 (p->len >= (IP_HLEN)));
pehrhovey 0:a548a085de55 714 SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
pehrhovey 0:a548a085de55 715 iphdr = (struct ip_hdr *)rambuf->payload;
pehrhovey 0:a548a085de55 716
pehrhovey 0:a548a085de55 717 /* Can just adjust p directly for needed offset. */
pehrhovey 0:a548a085de55 718 p->payload = (u8_t *)p->payload + poff;
pehrhovey 0:a548a085de55 719 p->len -= poff;
pehrhovey 0:a548a085de55 720
pehrhovey 0:a548a085de55 721 left_to_copy = cop;
pehrhovey 0:a548a085de55 722 while (left_to_copy) {
pehrhovey 0:a548a085de55 723 newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len;
pehrhovey 0:a548a085de55 724 /* Is this pbuf already empty? */
pehrhovey 0:a548a085de55 725 if (!newpbuflen) {
pehrhovey 0:a548a085de55 726 p = p->next;
pehrhovey 0:a548a085de55 727 continue;
pehrhovey 0:a548a085de55 728 }
pehrhovey 0:a548a085de55 729 newpbuf = pbuf_alloc(PBUF_RAW, 0, PBUF_REF);
pehrhovey 0:a548a085de55 730 if (newpbuf == NULL) {
pehrhovey 0:a548a085de55 731 pbuf_free(rambuf);
pehrhovey 0:a548a085de55 732 return ERR_MEM;
pehrhovey 0:a548a085de55 733 }
pehrhovey 0:a548a085de55 734 /* Mirror this pbuf, although we might not need all of it. */
pehrhovey 0:a548a085de55 735 newpbuf->payload = p->payload;
pehrhovey 0:a548a085de55 736 newpbuf->len = newpbuf->tot_len = newpbuflen;
pehrhovey 0:a548a085de55 737 /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain
pehrhovey 0:a548a085de55 738 * so that it is removed when pbuf_dechain is later called on rambuf.
pehrhovey 0:a548a085de55 739 */
pehrhovey 0:a548a085de55 740 pbuf_cat(rambuf, newpbuf);
pehrhovey 0:a548a085de55 741 left_to_copy -= newpbuflen;
pehrhovey 0:a548a085de55 742 if (left_to_copy)
pehrhovey 0:a548a085de55 743 p = p->next;
pehrhovey 0:a548a085de55 744 }
pehrhovey 0:a548a085de55 745 poff = newpbuflen;
pehrhovey 0:a548a085de55 746 #endif /* IP_FRAG_USES_STATIC_BUF */
pehrhovey 0:a548a085de55 747
pehrhovey 0:a548a085de55 748 /* Correct header */
pehrhovey 0:a548a085de55 749 IPH_OFFSET_SET(iphdr, htons(tmp));
pehrhovey 0:a548a085de55 750 IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
pehrhovey 0:a548a085de55 751 IPH_CHKSUM_SET(iphdr, 0);
pehrhovey 0:a548a085de55 752 IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
pehrhovey 0:a548a085de55 753
pehrhovey 0:a548a085de55 754 #if IP_FRAG_USES_STATIC_BUF
pehrhovey 0:a548a085de55 755 if (last)
pehrhovey 0:a548a085de55 756 pbuf_realloc(rambuf, left + IP_HLEN);
pehrhovey 0:a548a085de55 757
pehrhovey 0:a548a085de55 758 /* This part is ugly: we alloc a RAM based pbuf for
pehrhovey 0:a548a085de55 759 * the link level header for each chunk and then
pehrhovey 0:a548a085de55 760 * free it.A PBUF_ROM style pbuf for which pbuf_header
pehrhovey 0:a548a085de55 761 * worked would make things simpler.
pehrhovey 0:a548a085de55 762 */
pehrhovey 0:a548a085de55 763 header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
pehrhovey 0:a548a085de55 764 if (header != NULL) {
pehrhovey 0:a548a085de55 765 pbuf_chain(header, rambuf);
pehrhovey 0:a548a085de55 766 netif->output(netif, header, dest);
pehrhovey 0:a548a085de55 767 IPFRAG_STATS_INC(ip_frag.xmit);
pehrhovey 0:a548a085de55 768 snmp_inc_ipfragcreates();
pehrhovey 0:a548a085de55 769 pbuf_free(header);
pehrhovey 0:a548a085de55 770 } else {
pehrhovey 0:a548a085de55 771 LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n"));
pehrhovey 0:a548a085de55 772 pbuf_free(rambuf);
pehrhovey 0:a548a085de55 773 return ERR_MEM;
pehrhovey 0:a548a085de55 774 }
pehrhovey 0:a548a085de55 775 #else /* IP_FRAG_USES_STATIC_BUF */
pehrhovey 0:a548a085de55 776 /* No need for separate header pbuf - we allowed room for it in rambuf
pehrhovey 0:a548a085de55 777 * when allocated.
pehrhovey 0:a548a085de55 778 */
pehrhovey 0:a548a085de55 779 netif->output(netif, rambuf, dest);
pehrhovey 0:a548a085de55 780 IPFRAG_STATS_INC(ip_frag.xmit);
pehrhovey 0:a548a085de55 781
pehrhovey 0:a548a085de55 782 /* Unfortunately we can't reuse rambuf - the hardware may still be
pehrhovey 0:a548a085de55 783 * using the buffer. Instead we free it (and the ensuing chain) and
pehrhovey 0:a548a085de55 784 * recreate it next time round the loop. If we're lucky the hardware
pehrhovey 0:a548a085de55 785 * will have already sent the packet, the free will really free, and
pehrhovey 0:a548a085de55 786 * there will be zero memory penalty.
pehrhovey 0:a548a085de55 787 */
pehrhovey 0:a548a085de55 788
pehrhovey 0:a548a085de55 789 pbuf_free(rambuf);
pehrhovey 0:a548a085de55 790 #endif /* IP_FRAG_USES_STATIC_BUF */
pehrhovey 0:a548a085de55 791 left -= cop;
pehrhovey 0:a548a085de55 792 ofo += nfb;
pehrhovey 0:a548a085de55 793 }
pehrhovey 0:a548a085de55 794 #if IP_FRAG_USES_STATIC_BUF
pehrhovey 0:a548a085de55 795 pbuf_free(rambuf);
pehrhovey 0:a548a085de55 796 #endif /* IP_FRAG_USES_STATIC_BUF */
pehrhovey 0:a548a085de55 797 snmp_inc_ipfragoks();
pehrhovey 0:a548a085de55 798 return ERR_OK;
pehrhovey 0:a548a085de55 799 }
pehrhovey 0:a548a085de55 800 #endif /* IP_FRAG */