BadVPN – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /** |
2 | * @file |
||
3 | * Packet buffer management |
||
4 | */ |
||
5 | |||
6 | /** |
||
7 | * @defgroup pbuf Packet buffers (PBUF) |
||
8 | * @ingroup infrastructure |
||
9 | * |
||
10 | * Packets are built from the pbuf data structure. It supports dynamic |
||
11 | * memory allocation for packet contents or can reference externally |
||
12 | * managed packet contents both in RAM and ROM. Quick allocation for |
||
13 | * incoming packets is provided through pools with fixed sized pbufs. |
||
14 | * |
||
15 | * A packet may span over multiple pbufs, chained as a singly linked |
||
16 | * list. This is called a "pbuf chain". |
||
17 | * |
||
18 | * Multiple packets may be queued, also using this singly linked list. |
||
19 | * This is called a "packet queue". |
||
20 | * |
||
21 | * So, a packet queue consists of one or more pbuf chains, each of |
||
22 | * which consist of one or more pbufs. CURRENTLY, PACKET QUEUES ARE |
||
23 | * NOT SUPPORTED!!! Use helper structs to queue multiple packets. |
||
24 | * |
||
25 | * The differences between a pbuf chain and a packet queue are very |
||
26 | * precise but subtle. |
||
27 | * |
||
28 | * The last pbuf of a packet has a ->tot_len field that equals the |
||
29 | * ->len field. It can be found by traversing the list. If the last |
||
30 | * pbuf of a packet has a ->next field other than NULL, more packets |
||
31 | * are on the queue. |
||
32 | * |
||
33 | * Therefore, looping through a pbuf of a single packet, has an |
||
34 | * loop end condition (tot_len == p->len), NOT (next == NULL). |
||
35 | * |
||
36 | * Example of custom pbuf usage: @ref zerocopyrx |
||
37 | */ |
||
38 | |||
39 | /* |
||
40 | * Copyright (c) 2001-2004 Swedish Institute of Computer Science. |
||
41 | * All rights reserved. |
||
42 | * |
||
43 | * Redistribution and use in source and binary forms, with or without modification, |
||
44 | * are permitted provided that the following conditions are met: |
||
45 | * |
||
46 | * 1. Redistributions of source code must retain the above copyright notice, |
||
47 | * this list of conditions and the following disclaimer. |
||
48 | * 2. Redistributions in binary form must reproduce the above copyright notice, |
||
49 | * this list of conditions and the following disclaimer in the documentation |
||
50 | * and/or other materials provided with the distribution. |
||
51 | * 3. The name of the author may not be used to endorse or promote products |
||
52 | * derived from this software without specific prior written permission. |
||
53 | * |
||
54 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
||
55 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
||
56 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
||
57 | * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||
58 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT |
||
59 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||
60 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||
61 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||
62 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY |
||
63 | * OF SUCH DAMAGE. |
||
64 | * |
||
65 | * This file is part of the lwIP TCP/IP stack. |
||
66 | * |
||
67 | * Author: Adam Dunkels <adam@sics.se> |
||
68 | * |
||
69 | */ |
||
70 | |||
71 | #include "lwip/opt.h" |
||
72 | |||
73 | #include "lwip/pbuf.h" |
||
74 | #include "lwip/stats.h" |
||
75 | #include "lwip/def.h" |
||
76 | #include "lwip/mem.h" |
||
77 | #include "lwip/memp.h" |
||
78 | #include "lwip/sys.h" |
||
79 | #include "lwip/netif.h" |
||
80 | #if LWIP_TCP && TCP_QUEUE_OOSEQ |
||
81 | #include "lwip/priv/tcp_priv.h" |
||
82 | #endif |
||
83 | #if LWIP_CHECKSUM_ON_COPY |
||
84 | #include "lwip/inet_chksum.h" |
||
85 | #endif |
||
86 | |||
87 | #include <string.h> |
||
88 | |||
89 | #define SIZEOF_STRUCT_PBUF LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf)) |
||
90 | /* Since the pool is created in memp, PBUF_POOL_BUFSIZE will be automatically |
||
91 | aligned there. Therefore, PBUF_POOL_BUFSIZE_ALIGNED can be used here. */ |
||
92 | #define PBUF_POOL_BUFSIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE) |
||
93 | |||
94 | #if !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ |
||
95 | #define PBUF_POOL_IS_EMPTY() |
||
96 | #else /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */ |
||
97 | |||
98 | #if !NO_SYS |
||
99 | #ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL |
||
100 | #include "lwip/tcpip.h" |
||
101 | #define PBUF_POOL_FREE_OOSEQ_QUEUE_CALL() do { \ |
||
102 | if (tcpip_try_callback(pbuf_free_ooseq_callback, NULL) != ERR_OK) { \ |
||
103 | SYS_ARCH_PROTECT(old_level); \ |
||
104 | pbuf_free_ooseq_pending = 0; \ |
||
105 | SYS_ARCH_UNPROTECT(old_level); \ |
||
106 | } } while(0) |
||
107 | #endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ |
||
108 | #endif /* !NO_SYS */ |
||
109 | |||
110 | volatile u8_t pbuf_free_ooseq_pending; |
||
111 | #define PBUF_POOL_IS_EMPTY() pbuf_pool_is_empty() |
||
112 | |||
113 | /** |
||
114 | * Attempt to reclaim some memory from queued out-of-sequence TCP segments |
||
115 | * if we run out of pool pbufs. It's better to give priority to new packets |
||
116 | * if we're running out. |
||
117 | * |
||
118 | * This must be done in the correct thread context therefore this function |
||
119 | * can only be used with NO_SYS=0 and through tcpip_callback. |
||
120 | */ |
||
121 | #if !NO_SYS |
||
122 | static |
||
123 | #endif /* !NO_SYS */ |
||
124 | void |
||
125 | pbuf_free_ooseq(void) |
||
126 | { |
||
127 | struct tcp_pcb *pcb; |
||
128 | SYS_ARCH_SET(pbuf_free_ooseq_pending, 0); |
||
129 | |||
130 | for (pcb = tcp_active_pcbs; NULL != pcb; pcb = pcb->next) { |
||
131 | if (pcb->ooseq != NULL) { |
||
132 | /** Free the ooseq pbufs of one PCB only */ |
||
133 | LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free_ooseq: freeing out-of-sequence pbufs\n")); |
||
134 | tcp_free_ooseq(pcb); |
||
135 | return; |
||
136 | } |
||
137 | } |
||
138 | } |
||
139 | |||
140 | #if !NO_SYS |
||
141 | /** |
||
142 | * Just a callback function for tcpip_callback() that calls pbuf_free_ooseq(). |
||
143 | */ |
||
144 | static void |
||
145 | pbuf_free_ooseq_callback(void *arg) |
||
146 | { |
||
147 | LWIP_UNUSED_ARG(arg); |
||
148 | pbuf_free_ooseq(); |
||
149 | } |
||
150 | #endif /* !NO_SYS */ |
||
151 | |||
152 | /** Queue a call to pbuf_free_ooseq if not already queued. */ |
||
153 | static void |
||
154 | pbuf_pool_is_empty(void) |
||
155 | { |
||
156 | #ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL |
||
157 | SYS_ARCH_SET(pbuf_free_ooseq_pending, 1); |
||
158 | #else /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ |
||
159 | u8_t queued; |
||
160 | SYS_ARCH_DECL_PROTECT(old_level); |
||
161 | SYS_ARCH_PROTECT(old_level); |
||
162 | queued = pbuf_free_ooseq_pending; |
||
163 | pbuf_free_ooseq_pending = 1; |
||
164 | SYS_ARCH_UNPROTECT(old_level); |
||
165 | |||
166 | if (!queued) { |
||
167 | /* queue a call to pbuf_free_ooseq if not already queued */ |
||
168 | PBUF_POOL_FREE_OOSEQ_QUEUE_CALL(); |
||
169 | } |
||
170 | #endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */ |
||
171 | } |
||
172 | #endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */ |
||
173 | |||
174 | /* Initialize members of struct pbuf after allocation */ |
||
175 | static void |
||
176 | pbuf_init_alloced_pbuf(struct pbuf *p, void *payload, u16_t tot_len, u16_t len, pbuf_type type, u8_t flags) |
||
177 | { |
||
178 | p->next = NULL; |
||
179 | p->payload = payload; |
||
180 | p->tot_len = tot_len; |
||
181 | p->len = len; |
||
182 | p->type_internal = (u8_t)type; |
||
183 | p->flags = flags; |
||
184 | p->ref = 1; |
||
185 | p->if_idx = NETIF_NO_INDEX; |
||
186 | } |
||
187 | |||
188 | /** |
||
189 | * @ingroup pbuf |
||
190 | * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type). |
||
191 | * |
||
192 | * The actual memory allocated for the pbuf is determined by the |
||
193 | * layer at which the pbuf is allocated and the requested size |
||
194 | * (from the size parameter). |
||
195 | * |
||
196 | * @param layer header size |
||
197 | * @param length size of the pbuf's payload |
||
198 | * @param type this parameter decides how and where the pbuf |
||
199 | * should be allocated as follows: |
||
200 | * |
||
201 | * - PBUF_RAM: buffer memory for pbuf is allocated as one large |
||
202 | * chunk. This includes protocol headers as well. |
||
203 | * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for |
||
204 | * protocol headers. Additional headers must be prepended |
||
205 | * by allocating another pbuf and chain in to the front of |
||
206 | * the ROM pbuf. It is assumed that the memory used is really |
||
207 | * similar to ROM in that it is immutable and will not be |
||
208 | * changed. Memory which is dynamic should generally not |
||
209 | * be attached to PBUF_ROM pbufs. Use PBUF_REF instead. |
||
210 | * - PBUF_REF: no buffer memory is allocated for the pbuf, even for |
||
211 | * protocol headers. It is assumed that the pbuf is only |
||
212 | * being used in a single thread. If the pbuf gets queued, |
||
213 | * then pbuf_take should be called to copy the buffer. |
||
214 | * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from |
||
215 | * the pbuf pool that is allocated during pbuf_init(). |
||
216 | * |
||
217 | * @return the allocated pbuf. If multiple pbufs where allocated, this |
||
218 | * is the first pbuf of a pbuf chain. |
||
219 | */ |
||
220 | struct pbuf * |
||
221 | pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type) |
||
222 | { |
||
223 | struct pbuf *p; |
||
224 | u16_t offset = (u16_t)layer; |
||
225 | LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length)); |
||
226 | |||
227 | switch (type) { |
||
228 | case PBUF_REF: /* fall through */ |
||
229 | case PBUF_ROM: |
||
230 | p = pbuf_alloc_reference(NULL, length, type); |
||
231 | break; |
||
232 | case PBUF_POOL: { |
||
233 | struct pbuf *q, *last; |
||
234 | u16_t rem_len; /* remaining length */ |
||
235 | p = NULL; |
||
236 | last = NULL; |
||
237 | rem_len = length; |
||
238 | do { |
||
239 | u16_t qlen; |
||
240 | q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL); |
||
241 | if (q == NULL) { |
||
242 | PBUF_POOL_IS_EMPTY(); |
||
243 | /* free chain so far allocated */ |
||
244 | if (p) { |
||
245 | pbuf_free(p); |
||
246 | } |
||
247 | /* bail out unsuccessfully */ |
||
248 | return NULL; |
||
249 | } |
||
250 | qlen = LWIP_MIN(rem_len, (u16_t)(PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset))); |
||
251 | pbuf_init_alloced_pbuf(q, LWIP_MEM_ALIGN((void *)((u8_t *)q + SIZEOF_STRUCT_PBUF + offset)), |
||
252 | rem_len, qlen, type, 0); |
||
253 | LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned", |
||
254 | ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0); |
||
255 | LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT", |
||
256 | (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 ); |
||
257 | if (p == NULL) { |
||
258 | /* allocated head of pbuf chain (into p) */ |
||
259 | p = q; |
||
260 | } else { |
||
261 | /* make previous pbuf point to this pbuf */ |
||
262 | last->next = q; |
||
263 | } |
||
264 | last = q; |
||
265 | rem_len = (u16_t)(rem_len - qlen); |
||
266 | offset = 0; |
||
267 | } while (rem_len > 0); |
||
268 | break; |
||
269 | } |
||
270 | case PBUF_RAM: { |
||
271 | u16_t payload_len = (u16_t)(LWIP_MEM_ALIGN_SIZE(offset) + LWIP_MEM_ALIGN_SIZE(length)); |
||
272 | mem_size_t alloc_len = (mem_size_t)(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF) + payload_len); |
||
273 | |||
274 | /* bug #50040: Check for integer overflow when calculating alloc_len */ |
||
275 | if ((payload_len < LWIP_MEM_ALIGN_SIZE(length)) || |
||
276 | (alloc_len < LWIP_MEM_ALIGN_SIZE(length))) { |
||
277 | return NULL; |
||
278 | } |
||
279 | |||
280 | /* If pbuf is to be allocated in RAM, allocate memory for it. */ |
||
281 | p = (struct pbuf *)mem_malloc(alloc_len); |
||
282 | if (p == NULL) { |
||
283 | return NULL; |
||
284 | } |
||
285 | pbuf_init_alloced_pbuf(p, LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset)), |
||
286 | length, length, type, 0); |
||
287 | LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned", |
||
288 | ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0); |
||
289 | break; |
||
290 | } |
||
291 | default: |
||
292 | LWIP_ASSERT("pbuf_alloc: erroneous type", 0); |
||
293 | return NULL; |
||
294 | } |
||
295 | LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p)); |
||
296 | return p; |
||
297 | } |
||
298 | |||
299 | /** |
||
300 | * @ingroup pbuf |
||
301 | * Allocates a pbuf for referenced data. |
||
302 | * Referenced data can be volatile (PBUF_REF) or long-lived (PBUF_ROM). |
||
303 | * |
||
304 | * The actual memory allocated for the pbuf is determined by the |
||
305 | * layer at which the pbuf is allocated and the requested size |
||
306 | * (from the size parameter). |
||
307 | * |
||
308 | * @param payload referenced payload |
||
309 | * @param length size of the pbuf's payload |
||
310 | * @param type this parameter decides how and where the pbuf |
||
311 | * should be allocated as follows: |
||
312 | * |
||
313 | * - PBUF_ROM: It is assumed that the memory used is really |
||
314 | * similar to ROM in that it is immutable and will not be |
||
315 | * changed. Memory which is dynamic should generally not |
||
316 | * be attached to PBUF_ROM pbufs. Use PBUF_REF instead. |
||
317 | * - PBUF_REF: It is assumed that the pbuf is only |
||
318 | * being used in a single thread. If the pbuf gets queued, |
||
319 | * then pbuf_take should be called to copy the buffer. |
||
320 | * |
||
321 | * @return the allocated pbuf. |
||
322 | */ |
||
323 | struct pbuf * |
||
324 | pbuf_alloc_reference(void *payload, u16_t length, pbuf_type type) |
||
325 | { |
||
326 | struct pbuf *p; |
||
327 | LWIP_ASSERT("invalid pbuf_type", (type == PBUF_REF) || (type == PBUF_ROM)); |
||
328 | /* only allocate memory for the pbuf structure */ |
||
329 | p = (struct pbuf *)memp_malloc(MEMP_PBUF); |
||
330 | if (p == NULL) { |
||
331 | LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, |
||
332 | ("pbuf_alloc_reference: Could not allocate MEMP_PBUF for PBUF_%s.\n", |
||
333 | (type == PBUF_ROM) ? "ROM" : "REF")); |
||
334 | return NULL; |
||
335 | } |
||
336 | pbuf_init_alloced_pbuf(p, payload, length, length, type, 0); |
||
337 | return p; |
||
338 | } |
||
339 | |||
340 | |||
341 | #if LWIP_SUPPORT_CUSTOM_PBUF |
||
342 | /** |
||
343 | * @ingroup pbuf |
||
344 | * Initialize a custom pbuf (already allocated). |
||
345 | * Example of custom pbuf usage: @ref zerocopyrx |
||
346 | * |
||
347 | * @param l header size |
||
348 | * @param length size of the pbuf's payload |
||
349 | * @param type type of the pbuf (only used to treat the pbuf accordingly, as |
||
350 | * this function allocates no memory) |
||
351 | * @param p pointer to the custom pbuf to initialize (already allocated) |
||
352 | * @param payload_mem pointer to the buffer that is used for payload and headers, |
||
353 | * must be at least big enough to hold 'length' plus the header size, |
||
354 | * may be NULL if set later. |
||
355 | * ATTENTION: The caller is responsible for correct alignment of this buffer!! |
||
356 | * @param payload_mem_len the size of the 'payload_mem' buffer, must be at least |
||
357 | * big enough to hold 'length' plus the header size |
||
358 | */ |
||
359 | struct pbuf * |
||
360 | pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_custom *p, |
||
361 | void *payload_mem, u16_t payload_mem_len) |
||
362 | { |
||
363 | u16_t offset = (u16_t)l; |
||
364 | void *payload; |
||
365 | LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloced_custom(length=%"U16_F")\n", length)); |
||
366 | |||
367 | if (LWIP_MEM_ALIGN_SIZE(offset) + length > payload_mem_len) { |
||
368 | LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloced_custom(length=%"U16_F") buffer too short\n", length)); |
||
369 | return NULL; |
||
370 | } |
||
371 | |||
372 | if (payload_mem != NULL) { |
||
373 | payload = (u8_t *)payload_mem + LWIP_MEM_ALIGN_SIZE(offset); |
||
374 | } else { |
||
375 | payload = NULL; |
||
376 | } |
||
377 | pbuf_init_alloced_pbuf(&p->pbuf, payload, length, length, type, PBUF_FLAG_IS_CUSTOM); |
||
378 | return &p->pbuf; |
||
379 | } |
||
380 | #endif /* LWIP_SUPPORT_CUSTOM_PBUF */ |
||
381 | |||
382 | /** |
||
383 | * @ingroup pbuf |
||
384 | * Shrink a pbuf chain to a desired length. |
||
385 | * |
||
386 | * @param p pbuf to shrink. |
||
387 | * @param new_len desired new length of pbuf chain |
||
388 | * |
||
389 | * Depending on the desired length, the first few pbufs in a chain might |
||
390 | * be skipped and left unchanged. The new last pbuf in the chain will be |
||
391 | * resized, and any remaining pbufs will be freed. |
||
392 | * |
||
393 | * @note If the pbuf is ROM/REF, only the ->tot_len and ->len fields are adjusted. |
||
394 | * @note May not be called on a packet queue. |
||
395 | * |
||
396 | * @note Despite its name, pbuf_realloc cannot grow the size of a pbuf (chain). |
||
397 | */ |
||
398 | void |
||
399 | pbuf_realloc(struct pbuf *p, u16_t new_len) |
||
400 | { |
||
401 | struct pbuf *q; |
||
402 | u16_t rem_len; /* remaining length */ |
||
403 | u16_t shrink; |
||
404 | |||
405 | LWIP_ASSERT("pbuf_realloc: p != NULL", p != NULL); |
||
406 | |||
407 | /* desired length larger than current length? */ |
||
408 | if (new_len >= p->tot_len) { |
||
409 | /* enlarging not yet supported */ |
||
410 | return; |
||
411 | } |
||
412 | |||
413 | /* the pbuf chain grows by (new_len - p->tot_len) bytes |
||
414 | * (which may be negative in case of shrinking) */ |
||
415 | shrink = (u16_t)(p->tot_len - new_len); |
||
416 | |||
417 | /* first, step over any pbufs that should remain in the chain */ |
||
418 | rem_len = new_len; |
||
419 | q = p; |
||
420 | /* should this pbuf be kept? */ |
||
421 | while (rem_len > q->len) { |
||
422 | /* decrease remaining length by pbuf length */ |
||
423 | rem_len = (u16_t)(rem_len - q->len); |
||
424 | /* decrease total length indicator */ |
||
425 | q->tot_len = (u16_t)(q->tot_len - shrink); |
||
426 | /* proceed to next pbuf in chain */ |
||
427 | q = q->next; |
||
428 | LWIP_ASSERT("pbuf_realloc: q != NULL", q != NULL); |
||
429 | } |
||
430 | /* we have now reached the new last pbuf (in q) */ |
||
431 | /* rem_len == desired length for pbuf q */ |
||
432 | |||
433 | /* shrink allocated memory for PBUF_RAM */ |
||
434 | /* (other types merely adjust their length fields */ |
||
435 | if (pbuf_match_allocsrc(q, PBUF_TYPE_ALLOC_SRC_MASK_STD_HEAP) && (rem_len != q->len) |
||
436 | #if LWIP_SUPPORT_CUSTOM_PBUF |
||
437 | && ((q->flags & PBUF_FLAG_IS_CUSTOM) == 0) |
||
438 | #endif /* LWIP_SUPPORT_CUSTOM_PBUF */ |
||
439 | ) { |
||
440 | /* reallocate and adjust the length of the pbuf that will be split */ |
||
441 | q = (struct pbuf *)mem_trim(q, (mem_size_t)(((u8_t *)q->payload - (u8_t *)q) + rem_len)); |
||
442 | LWIP_ASSERT("mem_trim returned q == NULL", q != NULL); |
||
443 | } |
||
444 | /* adjust length fields for new last pbuf */ |
||
445 | q->len = rem_len; |
||
446 | q->tot_len = q->len; |
||
447 | |||
448 | /* any remaining pbufs in chain? */ |
||
449 | if (q->next != NULL) { |
||
450 | /* free remaining pbufs in chain */ |
||
451 | pbuf_free(q->next); |
||
452 | } |
||
453 | /* q is last packet in chain */ |
||
454 | q->next = NULL; |
||
455 | |||
456 | } |
||
457 | |||
458 | /** |
||
459 | * Adjusts the payload pointer to reveal headers in the payload. |
||
460 | * @see pbuf_add_header. |
||
461 | * |
||
462 | * @param p pbuf to change the header size. |
||
463 | * @param header_size_increment Number of bytes to increment header size. |
||
464 | * @param force Allow 'header_size_increment > 0' for PBUF_REF/PBUF_ROM types |
||
465 | * |
||
466 | * @return non-zero on failure, zero on success. |
||
467 | * |
||
468 | */ |
||
469 | static u8_t |
||
470 | pbuf_add_header_impl(struct pbuf *p, size_t header_size_increment, u8_t force) |
||
471 | { |
||
472 | u16_t type_internal; |
||
473 | void *payload; |
||
474 | u16_t increment_magnitude; |
||
475 | |||
476 | LWIP_ASSERT("p != NULL", p != NULL); |
||
477 | if ((header_size_increment == 0) || (p == NULL) || (header_size_increment > 0xFFFF)) { |
||
478 | return 0; |
||
479 | } |
||
480 | |||
481 | increment_magnitude = (u16_t)header_size_increment; |
||
482 | /* Do not allow tot_len to wrap as a result. */ |
||
483 | if ((u16_t)(increment_magnitude + p->tot_len) < increment_magnitude) { |
||
484 | return 1; |
||
485 | } |
||
486 | |||
487 | type_internal = p->type_internal; |
||
488 | |||
489 | /* pbuf types containing payloads? */ |
||
490 | if (type_internal & PBUF_TYPE_FLAG_STRUCT_DATA_CONTIGUOUS) { |
||
491 | /* set new payload pointer */ |
||
492 | payload = (u8_t *)p->payload - header_size_increment; |
||
493 | /* boundary check fails? */ |
||
494 | if ((u8_t *)payload < (u8_t *)p + SIZEOF_STRUCT_PBUF) { |
||
495 | LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, |
||
496 | ("pbuf_add_header: failed as %p < %p (not enough space for new header size)\n", |
||
497 | (void *)payload, (void *)((u8_t *)p + SIZEOF_STRUCT_PBUF))); |
||
498 | /* bail out unsuccessfully */ |
||
499 | return 1; |
||
500 | } |
||
501 | /* pbuf types referring to external payloads? */ |
||
502 | } else { |
||
503 | /* hide a header in the payload? */ |
||
504 | if (force) { |
||
505 | payload = (u8_t *)p->payload - header_size_increment; |
||
506 | } else { |
||
507 | /* cannot expand payload to front (yet!) |
||
508 | * bail out unsuccessfully */ |
||
509 | return 1; |
||
510 | } |
||
511 | } |
||
512 | LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_add_header: old %p new %p (%"U16_F")\n", |
||
513 | (void *)p->payload, (void *)payload, increment_magnitude)); |
||
514 | |||
515 | /* modify pbuf fields */ |
||
516 | p->payload = payload; |
||
517 | p->len = (u16_t)(p->len + increment_magnitude); |
||
518 | p->tot_len = (u16_t)(p->tot_len + increment_magnitude); |
||
519 | |||
520 | |||
521 | return 0; |
||
522 | } |
||
523 | |||
524 | /** |
||
525 | * Adjusts the payload pointer to reveal headers in the payload. |
||
526 | * |
||
527 | * Adjusts the ->payload pointer so that space for a header |
||
528 | * appears in the pbuf payload. |
||
529 | * |
||
530 | * The ->payload, ->tot_len and ->len fields are adjusted. |
||
531 | * |
||
532 | * @param p pbuf to change the header size. |
||
533 | * @param header_size_increment Number of bytes to increment header size which |
||
534 | * increases the size of the pbuf. New space is on the front. |
||
535 | * If hdr_size_inc is 0, this function does nothing and returns successful. |
||
536 | * |
||
537 | * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so |
||
538 | * the call will fail. A check is made that the increase in header size does |
||
539 | * not move the payload pointer in front of the start of the buffer. |
||
540 | * |
||
541 | * @return non-zero on failure, zero on success. |
||
542 | * |
||
543 | */ |
||
544 | u8_t |
||
545 | pbuf_add_header(struct pbuf *p, size_t header_size_increment) |
||
546 | { |
||
547 | return pbuf_add_header_impl(p, header_size_increment, 0); |
||
548 | } |
||
549 | |||
550 | /** |
||
551 | * Same as @ref pbuf_add_header but does not check if 'header_size > 0' is allowed. |
||
552 | * This is used internally only, to allow PBUF_REF for RX. |
||
553 | */ |
||
554 | u8_t |
||
555 | pbuf_add_header_force(struct pbuf *p, size_t header_size_increment) |
||
556 | { |
||
557 | return pbuf_add_header_impl(p, header_size_increment, 1); |
||
558 | } |
||
559 | |||
560 | /** |
||
561 | * Adjusts the payload pointer to hide headers in the payload. |
||
562 | * |
||
563 | * Adjusts the ->payload pointer so that space for a header |
||
564 | * disappears in the pbuf payload. |
||
565 | * |
||
566 | * The ->payload, ->tot_len and ->len fields are adjusted. |
||
567 | * |
||
568 | * @param p pbuf to change the header size. |
||
569 | * @param header_size_decrement Number of bytes to decrement header size which |
||
570 | * decreases the size of the pbuf. |
||
571 | * If hdr_size_inc is 0, this function does nothing and returns successful. |
||
572 | * @return non-zero on failure, zero on success. |
||
573 | * |
||
574 | */ |
||
575 | u8_t |
||
576 | pbuf_remove_header(struct pbuf *p, size_t header_size_decrement) |
||
577 | { |
||
578 | void *payload; |
||
579 | u16_t increment_magnitude; |
||
580 | |||
581 | LWIP_ASSERT("p != NULL", p != NULL); |
||
582 | if ((header_size_decrement == 0) || (p == NULL) || (header_size_decrement > 0xFFFF)) { |
||
583 | return 0; |
||
584 | } |
||
585 | |||
586 | increment_magnitude = (u16_t)header_size_decrement; |
||
587 | /* Check that we aren't going to move off the end of the pbuf */ |
||
588 | LWIP_ERROR("increment_magnitude <= p->len", (increment_magnitude <= p->len), return 1;); |
||
589 | |||
590 | /* remember current payload pointer */ |
||
591 | payload = p->payload; |
||
592 | LWIP_UNUSED_ARG(payload); /* only used in LWIP_DEBUGF below */ |
||
593 | |||
594 | /* increase payload pointer (guarded by length check above) */ |
||
595 | p->payload = (u8_t *)p->payload + header_size_decrement; |
||
596 | /* modify pbuf length fields */ |
||
597 | p->len = (u16_t)(p->len - increment_magnitude); |
||
598 | p->tot_len = (u16_t)(p->tot_len - increment_magnitude); |
||
599 | |||
600 | LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_remove_header: old %p new %p (%"U16_F")\n", |
||
601 | (void *)payload, (void *)p->payload, increment_magnitude)); |
||
602 | |||
603 | return 0; |
||
604 | } |
||
605 | |||
606 | static u8_t |
||
607 | pbuf_header_impl(struct pbuf *p, s16_t header_size_increment, u8_t force) |
||
608 | { |
||
609 | if (header_size_increment < 0) { |
||
610 | return pbuf_remove_header(p, (size_t) - header_size_increment); |
||
611 | } else { |
||
612 | return pbuf_add_header_impl(p, (size_t)header_size_increment, force); |
||
613 | } |
||
614 | } |
||
615 | |||
616 | /** |
||
617 | * Adjusts the payload pointer to hide or reveal headers in the payload. |
||
618 | * |
||
619 | * Adjusts the ->payload pointer so that space for a header |
||
620 | * (dis)appears in the pbuf payload. |
||
621 | * |
||
622 | * The ->payload, ->tot_len and ->len fields are adjusted. |
||
623 | * |
||
624 | * @param p pbuf to change the header size. |
||
625 | * @param header_size_increment Number of bytes to increment header size which |
||
626 | * increases the size of the pbuf. New space is on the front. |
||
627 | * (Using a negative value decreases the header size.) |
||
628 | * If hdr_size_inc is 0, this function does nothing and returns successful. |
||
629 | * |
||
630 | * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so |
||
631 | * the call will fail. A check is made that the increase in header size does |
||
632 | * not move the payload pointer in front of the start of the buffer. |
||
633 | * @return non-zero on failure, zero on success. |
||
634 | * |
||
635 | */ |
||
636 | u8_t |
||
637 | pbuf_header(struct pbuf *p, s16_t header_size_increment) |
||
638 | { |
||
639 | return pbuf_header_impl(p, header_size_increment, 0); |
||
640 | } |
||
641 | |||
642 | /** |
||
643 | * Same as pbuf_header but does not check if 'header_size > 0' is allowed. |
||
644 | * This is used internally only, to allow PBUF_REF for RX. |
||
645 | */ |
||
646 | u8_t |
||
647 | pbuf_header_force(struct pbuf *p, s16_t header_size_increment) |
||
648 | { |
||
649 | return pbuf_header_impl(p, header_size_increment, 1); |
||
650 | } |
||
651 | |||
652 | /** Similar to pbuf_header(-size) but de-refs header pbufs for (size >= p->len) |
||
653 | * |
||
654 | * @param q pbufs to operate on |
||
655 | * @param size The number of bytes to remove from the beginning of the pbuf list. |
||
656 | * While size >= p->len, pbufs are freed. |
||
657 | * ATTENTION: this is the opposite direction as @ref pbuf_header, but |
||
658 | * takes an u16_t not s16_t! |
||
659 | * @return the new head pbuf |
||
660 | */ |
||
661 | struct pbuf * |
||
662 | pbuf_free_header(struct pbuf *q, u16_t size) |
||
663 | { |
||
664 | struct pbuf *p = q; |
||
665 | u16_t free_left = size; |
||
666 | while (free_left && p) { |
||
667 | if (free_left >= p->len) { |
||
668 | struct pbuf *f = p; |
||
669 | free_left = (u16_t)(free_left - p->len); |
||
670 | p = p->next; |
||
671 | f->next = 0; |
||
672 | pbuf_free(f); |
||
673 | } else { |
||
674 | pbuf_remove_header(p, free_left); |
||
675 | free_left = 0; |
||
676 | } |
||
677 | } |
||
678 | return p; |
||
679 | } |
||
680 | |||
681 | /** |
||
682 | * @ingroup pbuf |
||
683 | * Dereference a pbuf chain or queue and deallocate any no-longer-used |
||
684 | * pbufs at the head of this chain or queue. |
||
685 | * |
||
686 | * Decrements the pbuf reference count. If it reaches zero, the pbuf is |
||
687 | * deallocated. |
||
688 | * |
||
689 | * For a pbuf chain, this is repeated for each pbuf in the chain, |
||
690 | * up to the first pbuf which has a non-zero reference count after |
||
691 | * decrementing. So, when all reference counts are one, the whole |
||
692 | * chain is free'd. |
||
693 | * |
||
694 | * @param p The pbuf (chain) to be dereferenced. |
||
695 | * |
||
696 | * @return the number of pbufs that were de-allocated |
||
697 | * from the head of the chain. |
||
698 | * |
||
699 | * @note MUST NOT be called on a packet queue (Not verified to work yet). |
||
700 | * @note the reference counter of a pbuf equals the number of pointers |
||
701 | * that refer to the pbuf (or into the pbuf). |
||
702 | * |
||
703 | * @internal examples: |
||
704 | * |
||
705 | * Assuming existing chains a->b->c with the following reference |
||
706 | * counts, calling pbuf_free(a) results in: |
||
707 | * |
||
708 | * 1->2->3 becomes ...1->3 |
||
709 | * 3->3->3 becomes 2->3->3 |
||
710 | * 1->1->2 becomes ......1 |
||
711 | * 2->1->1 becomes 1->1->1 |
||
712 | * 1->1->1 becomes ....... |
||
713 | * |
||
714 | */ |
||
715 | u8_t |
||
716 | pbuf_free(struct pbuf *p) |
||
717 | { |
||
718 | u8_t alloc_src; |
||
719 | struct pbuf *q; |
||
720 | u8_t count; |
||
721 | |||
722 | if (p == NULL) { |
||
723 | LWIP_ASSERT("p != NULL", p != NULL); |
||
724 | /* if assertions are disabled, proceed with debug output */ |
||
725 | LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS, |
||
726 | ("pbuf_free(p == NULL) was called.\n")); |
||
727 | return 0; |
||
728 | } |
||
729 | LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free(%p)\n", (void *)p)); |
||
730 | |||
731 | PERF_START; |
||
732 | |||
733 | count = 0; |
||
734 | /* de-allocate all consecutive pbufs from the head of the chain that |
||
735 | * obtain a zero reference count after decrementing*/ |
||
736 | while (p != NULL) { |
||
737 | LWIP_PBUF_REF_T ref; |
||
738 | SYS_ARCH_DECL_PROTECT(old_level); |
||
739 | /* Since decrementing ref cannot be guaranteed to be a single machine operation |
||
740 | * we must protect it. We put the new ref into a local variable to prevent |
||
741 | * further protection. */ |
||
742 | SYS_ARCH_PROTECT(old_level); |
||
743 | /* all pbufs in a chain are referenced at least once */ |
||
744 | LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0); |
||
745 | /* decrease reference count (number of pointers to pbuf) */ |
||
746 | ref = --(p->ref); |
||
747 | SYS_ARCH_UNPROTECT(old_level); |
||
748 | /* this pbuf is no longer referenced to? */ |
||
749 | if (ref == 0) { |
||
750 | /* remember next pbuf in chain for next iteration */ |
||
751 | q = p->next; |
||
752 | LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: deallocating %p\n", (void *)p)); |
||
753 | alloc_src = pbuf_get_allocsrc(p); |
||
754 | #if LWIP_SUPPORT_CUSTOM_PBUF |
||
755 | /* is this a custom pbuf? */ |
||
756 | if ((p->flags & PBUF_FLAG_IS_CUSTOM) != 0) { |
||
757 | struct pbuf_custom *pc = (struct pbuf_custom *)p; |
||
758 | LWIP_ASSERT("pc->custom_free_function != NULL", pc->custom_free_function != NULL); |
||
759 | pc->custom_free_function(p); |
||
760 | } else |
||
761 | #endif /* LWIP_SUPPORT_CUSTOM_PBUF */ |
||
762 | { |
||
763 | /* is this a pbuf from the pool? */ |
||
764 | if (alloc_src == PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF_POOL) { |
||
765 | memp_free(MEMP_PBUF_POOL, p); |
||
766 | /* is this a ROM or RAM referencing pbuf? */ |
||
767 | } else if (alloc_src == PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF) { |
||
768 | memp_free(MEMP_PBUF, p); |
||
769 | /* type == PBUF_RAM */ |
||
770 | } else if (alloc_src == PBUF_TYPE_ALLOC_SRC_MASK_STD_HEAP) { |
||
771 | mem_free(p); |
||
772 | } else { |
||
773 | /* @todo: support freeing other types */ |
||
774 | LWIP_ASSERT("invalid pbuf type", 0); |
||
775 | } |
||
776 | } |
||
777 | count++; |
||
778 | /* proceed to next pbuf */ |
||
779 | p = q; |
||
780 | /* p->ref > 0, this pbuf is still referenced to */ |
||
781 | /* (and so the remaining pbufs in chain as well) */ |
||
782 | } else { |
||
783 | LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: %p has ref %"U16_F", ending here.\n", (void *)p, ref)); |
||
784 | /* stop walking through the chain */ |
||
785 | p = NULL; |
||
786 | } |
||
787 | } |
||
788 | PERF_STOP("pbuf_free"); |
||
789 | /* return number of de-allocated pbufs */ |
||
790 | return count; |
||
791 | } |
||
792 | |||
793 | /** |
||
794 | * Count number of pbufs in a chain |
||
795 | * |
||
796 | * @param p first pbuf of chain |
||
797 | * @return the number of pbufs in a chain |
||
798 | */ |
||
799 | u16_t |
||
800 | pbuf_clen(const struct pbuf *p) |
||
801 | { |
||
802 | u16_t len; |
||
803 | |||
804 | len = 0; |
||
805 | while (p != NULL) { |
||
806 | ++len; |
||
807 | p = p->next; |
||
808 | } |
||
809 | return len; |
||
810 | } |
||
811 | |||
812 | /** |
||
813 | * @ingroup pbuf |
||
814 | * Increment the reference count of the pbuf. |
||
815 | * |
||
816 | * @param p pbuf to increase reference counter of |
||
817 | * |
||
818 | */ |
||
819 | void |
||
820 | pbuf_ref(struct pbuf *p) |
||
821 | { |
||
822 | /* pbuf given? */ |
||
823 | if (p != NULL) { |
||
824 | SYS_ARCH_SET(p->ref, (u8_t)(p->ref + 1)); |
||
825 | LWIP_ASSERT("pbuf ref overflow", p->ref > 0); |
||
826 | } |
||
827 | } |
||
828 | |||
829 | /** |
||
830 | * @ingroup pbuf |
||
831 | * Concatenate two pbufs (each may be a pbuf chain) and take over |
||
832 | * the caller's reference of the tail pbuf. |
||
833 | * |
||
834 | * @note The caller MAY NOT reference the tail pbuf afterwards. |
||
835 | * Use pbuf_chain() for that purpose. |
||
836 | * |
||
837 | * This function explicitly does not check for tot_len overflow to prevent |
||
838 | * failing to queue too long pbufs. This can produce invalid pbufs, so |
||
839 | * handle with care! |
||
840 | * |
||
841 | * @see pbuf_chain() |
||
842 | */ |
||
843 | void |
||
844 | pbuf_cat(struct pbuf *h, struct pbuf *t) |
||
845 | { |
||
846 | struct pbuf *p; |
||
847 | |||
848 | LWIP_ERROR("(h != NULL) && (t != NULL) (programmer violates API)", |
||
849 | ((h != NULL) && (t != NULL)), return;); |
||
850 | |||
851 | /* proceed to last pbuf of chain */ |
||
852 | for (p = h; p->next != NULL; p = p->next) { |
||
853 | /* add total length of second chain to all totals of first chain */ |
||
854 | p->tot_len = (u16_t)(p->tot_len + t->tot_len); |
||
855 | } |
||
856 | /* { p is last pbuf of first h chain, p->next == NULL } */ |
||
857 | LWIP_ASSERT("p->tot_len == p->len (of last pbuf in chain)", p->tot_len == p->len); |
||
858 | LWIP_ASSERT("p->next == NULL", p->next == NULL); |
||
859 | /* add total length of second chain to last pbuf total of first chain */ |
||
860 | p->tot_len = (u16_t)(p->tot_len + t->tot_len); |
||
861 | /* chain last pbuf of head (p) with first of tail (t) */ |
||
862 | p->next = t; |
||
863 | /* p->next now references t, but the caller will drop its reference to t, |
||
864 | * so netto there is no change to the reference count of t. |
||
865 | */ |
||
866 | } |
||
867 | |||
868 | /** |
||
869 | * @ingroup pbuf |
||
870 | * Chain two pbufs (or pbuf chains) together. |
||
871 | * |
||
872 | * The caller MUST call pbuf_free(t) once it has stopped |
||
873 | * using it. Use pbuf_cat() instead if you no longer use t. |
||
874 | * |
||
875 | * @param h head pbuf (chain) |
||
876 | * @param t tail pbuf (chain) |
||
877 | * @note The pbufs MUST belong to the same packet. |
||
878 | * @note MAY NOT be called on a packet queue. |
||
879 | * |
||
880 | * The ->tot_len fields of all pbufs of the head chain are adjusted. |
||
881 | * The ->next field of the last pbuf of the head chain is adjusted. |
||
882 | * The ->ref field of the first pbuf of the tail chain is adjusted. |
||
883 | * |
||
884 | */ |
||
885 | void |
||
886 | pbuf_chain(struct pbuf *h, struct pbuf *t) |
||
887 | { |
||
888 | pbuf_cat(h, t); |
||
889 | /* t is now referenced by h */ |
||
890 | pbuf_ref(t); |
||
891 | LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_chain: %p references %p\n", (void *)h, (void *)t)); |
||
892 | } |
||
893 | |||
894 | /** |
||
895 | * Dechains the first pbuf from its succeeding pbufs in the chain. |
||
896 | * |
||
897 | * Makes p->tot_len field equal to p->len. |
||
898 | * @param p pbuf to dechain |
||
899 | * @return remainder of the pbuf chain, or NULL if it was de-allocated. |
||
900 | * @note May not be called on a packet queue. |
||
901 | */ |
||
902 | struct pbuf * |
||
903 | pbuf_dechain(struct pbuf *p) |
||
904 | { |
||
905 | struct pbuf *q; |
||
906 | u8_t tail_gone = 1; |
||
907 | /* tail */ |
||
908 | q = p->next; |
||
909 | /* pbuf has successor in chain? */ |
||
910 | if (q != NULL) { |
||
911 | /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */ |
||
912 | LWIP_ASSERT("p->tot_len == p->len + q->tot_len", q->tot_len == p->tot_len - p->len); |
||
913 | /* enforce invariant if assertion is disabled */ |
||
914 | q->tot_len = (u16_t)(p->tot_len - p->len); |
||
915 | /* decouple pbuf from remainder */ |
||
916 | p->next = NULL; |
||
917 | /* total length of pbuf p is its own length only */ |
||
918 | p->tot_len = p->len; |
||
919 | /* q is no longer referenced by p, free it */ |
||
920 | LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_dechain: unreferencing %p\n", (void *)q)); |
||
921 | tail_gone = pbuf_free(q); |
||
922 | if (tail_gone > 0) { |
||
923 | LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, |
||
924 | ("pbuf_dechain: deallocated %p (as it is no longer referenced)\n", (void *)q)); |
||
925 | } |
||
926 | /* return remaining tail or NULL if deallocated */ |
||
927 | } |
||
928 | /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */ |
||
929 | LWIP_ASSERT("p->tot_len == p->len", p->tot_len == p->len); |
||
930 | return ((tail_gone > 0) ? NULL : q); |
||
931 | } |
||
932 | |||
933 | /** |
||
934 | * @ingroup pbuf |
||
935 | * Create PBUF_RAM copies of pbufs. |
||
936 | * |
||
937 | * Used to queue packets on behalf of the lwIP stack, such as |
||
938 | * ARP based queueing. |
||
939 | * |
||
940 | * @note You MUST explicitly use p = pbuf_take(p); |
||
941 | * |
||
942 | * @note Only one packet is copied, no packet queue! |
||
943 | * |
||
944 | * @param p_to pbuf destination of the copy |
||
945 | * @param p_from pbuf source of the copy |
||
946 | * |
||
947 | * @return ERR_OK if pbuf was copied |
||
948 | * ERR_ARG if one of the pbufs is NULL or p_to is not big |
||
949 | * enough to hold p_from |
||
950 | */ |
||
951 | err_t |
||
952 | pbuf_copy(struct pbuf *p_to, const struct pbuf *p_from) |
||
953 | { |
||
954 | size_t offset_to = 0, offset_from = 0, len; |
||
955 | |||
956 | LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy(%p, %p)\n", |
||
957 | (const void *)p_to, (const void *)p_from)); |
||
958 | |||
959 | /* is the target big enough to hold the source? */ |
||
960 | LWIP_ERROR("pbuf_copy: target not big enough to hold source", ((p_to != NULL) && |
||
961 | (p_from != NULL) && (p_to->tot_len >= p_from->tot_len)), return ERR_ARG;); |
||
962 | |||
963 | /* iterate through pbuf chain */ |
||
964 | do { |
||
965 | /* copy one part of the original chain */ |
||
966 | if ((p_to->len - offset_to) >= (p_from->len - offset_from)) { |
||
967 | /* complete current p_from fits into current p_to */ |
||
968 | len = p_from->len - offset_from; |
||
969 | } else { |
||
970 | /* current p_from does not fit into current p_to */ |
||
971 | len = p_to->len - offset_to; |
||
972 | } |
||
973 | MEMCPY((u8_t *)p_to->payload + offset_to, (u8_t *)p_from->payload + offset_from, len); |
||
974 | offset_to += len; |
||
975 | offset_from += len; |
||
976 | LWIP_ASSERT("offset_to <= p_to->len", offset_to <= p_to->len); |
||
977 | LWIP_ASSERT("offset_from <= p_from->len", offset_from <= p_from->len); |
||
978 | if (offset_from >= p_from->len) { |
||
979 | /* on to next p_from (if any) */ |
||
980 | offset_from = 0; |
||
981 | p_from = p_from->next; |
||
982 | } |
||
983 | if (offset_to == p_to->len) { |
||
984 | /* on to next p_to (if any) */ |
||
985 | offset_to = 0; |
||
986 | p_to = p_to->next; |
||
987 | LWIP_ERROR("p_to != NULL", (p_to != NULL) || (p_from == NULL), return ERR_ARG;); |
||
988 | } |
||
989 | |||
990 | if ((p_from != NULL) && (p_from->len == p_from->tot_len)) { |
||
991 | /* don't copy more than one packet! */ |
||
992 | LWIP_ERROR("pbuf_copy() does not allow packet queues!", |
||
993 | (p_from->next == NULL), return ERR_VAL;); |
||
994 | } |
||
995 | if ((p_to != NULL) && (p_to->len == p_to->tot_len)) { |
||
996 | /* don't copy more than one packet! */ |
||
997 | LWIP_ERROR("pbuf_copy() does not allow packet queues!", |
||
998 | (p_to->next == NULL), return ERR_VAL;); |
||
999 | } |
||
1000 | } while (p_from); |
||
1001 | LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy: end of chain reached.\n")); |
||
1002 | return ERR_OK; |
||
1003 | } |
||
1004 | |||
1005 | /** |
||
1006 | * @ingroup pbuf |
||
1007 | * Copy (part of) the contents of a packet buffer |
||
1008 | * to an application supplied buffer. |
||
1009 | * |
||
1010 | * @param buf the pbuf from which to copy data |
||
1011 | * @param dataptr the application supplied buffer |
||
1012 | * @param len length of data to copy (dataptr must be big enough). No more |
||
1013 | * than buf->tot_len will be copied, irrespective of len |
||
1014 | * @param offset offset into the packet buffer from where to begin copying len bytes |
||
1015 | * @return the number of bytes copied, or 0 on failure |
||
1016 | */ |
||
1017 | u16_t |
||
1018 | pbuf_copy_partial(const struct pbuf *buf, void *dataptr, u16_t len, u16_t offset) |
||
1019 | { |
||
1020 | const struct pbuf *p; |
||
1021 | u16_t left = 0; |
||
1022 | u16_t buf_copy_len; |
||
1023 | u16_t copied_total = 0; |
||
1024 | |||
1025 | LWIP_ERROR("pbuf_copy_partial: invalid buf", (buf != NULL), return 0;); |
||
1026 | LWIP_ERROR("pbuf_copy_partial: invalid dataptr", (dataptr != NULL), return 0;); |
||
1027 | |||
1028 | /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */ |
||
1029 | for (p = buf; len != 0 && p != NULL; p = p->next) { |
||
1030 | if ((offset != 0) && (offset >= p->len)) { |
||
1031 | /* don't copy from this buffer -> on to the next */ |
||
1032 | offset = (u16_t)(offset - p->len); |
||
1033 | } else { |
||
1034 | /* copy from this buffer. maybe only partially. */ |
||
1035 | buf_copy_len = (u16_t)(p->len - offset); |
||
1036 | if (buf_copy_len > len) { |
||
1037 | buf_copy_len = len; |
||
1038 | } |
||
1039 | /* copy the necessary parts of the buffer */ |
||
1040 | MEMCPY(&((char *)dataptr)[left], &((char *)p->payload)[offset], buf_copy_len); |
||
1041 | copied_total = (u16_t)(copied_total + buf_copy_len); |
||
1042 | left = (u16_t)(left + buf_copy_len); |
||
1043 | len = (u16_t)(len - buf_copy_len); |
||
1044 | offset = 0; |
||
1045 | } |
||
1046 | } |
||
1047 | return copied_total; |
||
1048 | } |
||
1049 | |||
1050 | /** |
||
1051 | * @ingroup pbuf |
||
1052 | * Get part of a pbuf's payload as contiguous memory. The returned memory is |
||
1053 | * either a pointer into the pbuf's payload or, if split over multiple pbufs, |
||
1054 | * a copy into the user-supplied buffer. |
||
1055 | * |
||
1056 | * @param p the pbuf from which to copy data |
||
1057 | * @param buffer the application supplied buffer |
||
1058 | * @param bufsize size of the application supplied buffer |
||
1059 | * @param len length of data to copy (dataptr must be big enough). No more |
||
1060 | * than buf->tot_len will be copied, irrespective of len |
||
1061 | * @param offset offset into the packet buffer from where to begin copying len bytes |
||
1062 | * @return the number of bytes copied, or 0 on failure |
||
1063 | */ |
||
1064 | void * |
||
1065 | pbuf_get_contiguous(const struct pbuf *p, void *buffer, size_t bufsize, u16_t len, u16_t offset) |
||
1066 | { |
||
1067 | const struct pbuf *q; |
||
1068 | |||
1069 | LWIP_ERROR("pbuf_get_contiguous: invalid buf", (p != NULL), return NULL;); |
||
1070 | LWIP_ERROR("pbuf_get_contiguous: invalid dataptr", (buffer != NULL), return NULL;); |
||
1071 | LWIP_ERROR("pbuf_get_contiguous: invalid dataptr", (bufsize >= len), return NULL;); |
||
1072 | |||
1073 | for (q = p; q != NULL; q = q->next) { |
||
1074 | if ((offset != 0) && (offset >= q->len)) { |
||
1075 | /* don't copy from this buffer -> on to the next */ |
||
1076 | offset = (u16_t)(offset - q->len); |
||
1077 | } else { |
||
1078 | if (q->len >= (offset + len)) { |
||
1079 | /* all data in this pbuf, return zero-copy */ |
||
1080 | return (u8_t *)q->payload + offset; |
||
1081 | } |
||
1082 | /* need to copy */ |
||
1083 | if (pbuf_copy_partial(q, buffer, len, offset) != len) { |
||
1084 | /* copying failed: pbuf is too short */ |
||
1085 | return NULL; |
||
1086 | } |
||
1087 | return buffer; |
||
1088 | } |
||
1089 | } |
||
1090 | /* pbuf is too short (offset does not fit in) */ |
||
1091 | return NULL; |
||
1092 | } |
||
1093 | |||
1094 | #if LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE |
||
1095 | /** |
||
1096 | * This method modifies a 'pbuf chain', so that its total length is |
||
1097 | * smaller than 64K. The remainder of the original pbuf chain is stored |
||
1098 | * in *rest. |
||
1099 | * This function never creates new pbufs, but splits an existing chain |
||
1100 | * in two parts. The tot_len of the modified packet queue will likely be |
||
1101 | * smaller than 64K. |
||
1102 | * 'packet queues' are not supported by this function. |
||
1103 | * |
||
1104 | * @param p the pbuf queue to be split |
||
1105 | * @param rest pointer to store the remainder (after the first 64K) |
||
1106 | */ |
||
1107 | void pbuf_split_64k(struct pbuf *p, struct pbuf **rest) |
||
1108 | { |
||
1109 | *rest = NULL; |
||
1110 | if ((p != NULL) && (p->next != NULL)) { |
||
1111 | u16_t tot_len_front = p->len; |
||
1112 | struct pbuf *i = p; |
||
1113 | struct pbuf *r = p->next; |
||
1114 | |||
1115 | /* continue until the total length (summed up as u16_t) overflows */ |
||
1116 | while ((r != NULL) && ((u16_t)(tot_len_front + r->len) >= tot_len_front)) { |
||
1117 | tot_len_front = (u16_t)(tot_len_front + r->len); |
||
1118 | i = r; |
||
1119 | r = r->next; |
||
1120 | } |
||
1121 | /* i now points to last packet of the first segment. Set next |
||
1122 | pointer to NULL */ |
||
1123 | i->next = NULL; |
||
1124 | |||
1125 | if (r != NULL) { |
||
1126 | /* Update the tot_len field in the first part */ |
||
1127 | for (i = p; i != NULL; i = i->next) { |
||
1128 | i->tot_len = (u16_t)(i->tot_len - r->tot_len); |
||
1129 | LWIP_ASSERT("tot_len/len mismatch in last pbuf", |
||
1130 | (i->next != NULL) || (i->tot_len == i->len)); |
||
1131 | } |
||
1132 | if (p->flags & PBUF_FLAG_TCP_FIN) { |
||
1133 | r->flags |= PBUF_FLAG_TCP_FIN; |
||
1134 | } |
||
1135 | |||
1136 | /* tot_len field in rest does not need modifications */ |
||
1137 | /* reference counters do not need modifications */ |
||
1138 | *rest = r; |
||
1139 | } |
||
1140 | } |
||
1141 | } |
||
1142 | #endif /* LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */ |
||
1143 | |||
1144 | /* Actual implementation of pbuf_skip() but returning const pointer... */ |
||
1145 | static const struct pbuf * |
||
1146 | pbuf_skip_const(const struct pbuf *in, u16_t in_offset, u16_t *out_offset) |
||
1147 | { |
||
1148 | u16_t offset_left = in_offset; |
||
1149 | const struct pbuf *q = in; |
||
1150 | |||
1151 | /* get the correct pbuf */ |
||
1152 | while ((q != NULL) && (q->len <= offset_left)) { |
||
1153 | offset_left = (u16_t)(offset_left - q->len); |
||
1154 | q = q->next; |
||
1155 | } |
||
1156 | if (out_offset != NULL) { |
||
1157 | *out_offset = offset_left; |
||
1158 | } |
||
1159 | return q; |
||
1160 | } |
||
1161 | |||
1162 | /** |
||
1163 | * @ingroup pbuf |
||
1164 | * Skip a number of bytes at the start of a pbuf |
||
1165 | * |
||
1166 | * @param in input pbuf |
||
1167 | * @param in_offset offset to skip |
||
1168 | * @param out_offset resulting offset in the returned pbuf |
||
1169 | * @return the pbuf in the queue where the offset is |
||
1170 | */ |
||
1171 | struct pbuf * |
||
1172 | pbuf_skip(struct pbuf *in, u16_t in_offset, u16_t *out_offset) |
||
1173 | { |
||
1174 | const struct pbuf *out = pbuf_skip_const(in, in_offset, out_offset); |
||
1175 | return LWIP_CONST_CAST(struct pbuf *, out); |
||
1176 | } |
||
1177 | |||
1178 | /** |
||
1179 | * @ingroup pbuf |
||
1180 | * Copy application supplied data into a pbuf. |
||
1181 | * This function can only be used to copy the equivalent of buf->tot_len data. |
||
1182 | * |
||
1183 | * @param buf pbuf to fill with data |
||
1184 | * @param dataptr application supplied data buffer |
||
1185 | * @param len length of the application supplied data buffer |
||
1186 | * |
||
1187 | * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough |
||
1188 | */ |
||
1189 | err_t |
||
1190 | pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len) |
||
1191 | { |
||
1192 | struct pbuf *p; |
||
1193 | size_t buf_copy_len; |
||
1194 | size_t total_copy_len = len; |
||
1195 | size_t copied_total = 0; |
||
1196 | |||
1197 | LWIP_ERROR("pbuf_take: invalid buf", (buf != NULL), return ERR_ARG;); |
||
1198 | LWIP_ERROR("pbuf_take: invalid dataptr", (dataptr != NULL), return ERR_ARG;); |
||
1199 | LWIP_ERROR("pbuf_take: buf not large enough", (buf->tot_len >= len), return ERR_MEM;); |
||
1200 | |||
1201 | if ((buf == NULL) || (dataptr == NULL) || (buf->tot_len < len)) { |
||
1202 | return ERR_ARG; |
||
1203 | } |
||
1204 | |||
1205 | /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */ |
||
1206 | for (p = buf; total_copy_len != 0; p = p->next) { |
||
1207 | LWIP_ASSERT("pbuf_take: invalid pbuf", p != NULL); |
||
1208 | buf_copy_len = total_copy_len; |
||
1209 | if (buf_copy_len > p->len) { |
||
1210 | /* this pbuf cannot hold all remaining data */ |
||
1211 | buf_copy_len = p->len; |
||
1212 | } |
||
1213 | /* copy the necessary parts of the buffer */ |
||
1214 | MEMCPY(p->payload, &((const char *)dataptr)[copied_total], buf_copy_len); |
||
1215 | total_copy_len -= buf_copy_len; |
||
1216 | copied_total += buf_copy_len; |
||
1217 | } |
||
1218 | LWIP_ASSERT("did not copy all data", total_copy_len == 0 && copied_total == len); |
||
1219 | return ERR_OK; |
||
1220 | } |
||
1221 | |||
1222 | /** |
||
1223 | * @ingroup pbuf |
||
1224 | * Same as pbuf_take() but puts data at an offset |
||
1225 | * |
||
1226 | * @param buf pbuf to fill with data |
||
1227 | * @param dataptr application supplied data buffer |
||
1228 | * @param len length of the application supplied data buffer |
||
1229 | * @param offset offset in pbuf where to copy dataptr to |
||
1230 | * |
||
1231 | * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough |
||
1232 | */ |
||
1233 | err_t |
||
1234 | pbuf_take_at(struct pbuf *buf, const void *dataptr, u16_t len, u16_t offset) |
||
1235 | { |
||
1236 | u16_t target_offset; |
||
1237 | struct pbuf *q = pbuf_skip(buf, offset, &target_offset); |
||
1238 | |||
1239 | /* return requested data if pbuf is OK */ |
||
1240 | if ((q != NULL) && (q->tot_len >= target_offset + len)) { |
||
1241 | u16_t remaining_len = len; |
||
1242 | const u8_t *src_ptr = (const u8_t *)dataptr; |
||
1243 | /* copy the part that goes into the first pbuf */ |
||
1244 | u16_t first_copy_len; |
||
1245 | LWIP_ASSERT("chekc pbuf_skip result", target_offset < q->len); |
||
1246 | first_copy_len = (u16_t)LWIP_MIN(q->len - target_offset, len); |
||
1247 | MEMCPY(((u8_t *)q->payload) + target_offset, dataptr, first_copy_len); |
||
1248 | remaining_len = (u16_t)(remaining_len - first_copy_len); |
||
1249 | src_ptr += first_copy_len; |
||
1250 | if (remaining_len > 0) { |
||
1251 | return pbuf_take(q->next, src_ptr, remaining_len); |
||
1252 | } |
||
1253 | return ERR_OK; |
||
1254 | } |
||
1255 | return ERR_MEM; |
||
1256 | } |
||
1257 | |||
1258 | /** |
||
1259 | * @ingroup pbuf |
||
1260 | * Creates a single pbuf out of a queue of pbufs. |
||
1261 | * |
||
1262 | * @remark: Either the source pbuf 'p' is freed by this function or the original |
||
1263 | * pbuf 'p' is returned, therefore the caller has to check the result! |
||
1264 | * |
||
1265 | * @param p the source pbuf |
||
1266 | * @param layer pbuf_layer of the new pbuf |
||
1267 | * |
||
1268 | * @return a new, single pbuf (p->next is NULL) |
||
1269 | * or the old pbuf if allocation fails |
||
1270 | */ |
||
1271 | struct pbuf * |
||
1272 | pbuf_coalesce(struct pbuf *p, pbuf_layer layer) |
||
1273 | { |
||
1274 | struct pbuf *q; |
||
1275 | if (p->next == NULL) { |
||
1276 | return p; |
||
1277 | } |
||
1278 | q = pbuf_clone(layer, PBUF_RAM, p); |
||
1279 | if (q == NULL) { |
||
1280 | /* @todo: what do we do now? */ |
||
1281 | return p; |
||
1282 | } |
||
1283 | pbuf_free(p); |
||
1284 | return q; |
||
1285 | } |
||
1286 | |||
1287 | /** |
||
1288 | * @ingroup pbuf |
||
1289 | * Allocates a new pbuf of same length (via pbuf_alloc()) and copies the source |
||
1290 | * pbuf into this new pbuf (using pbuf_copy()). |
||
1291 | * |
||
1292 | * @param layer pbuf_layer of the new pbuf |
||
1293 | * @param type this parameter decides how and where the pbuf should be allocated |
||
1294 | * (@see pbuf_alloc()) |
||
1295 | * @param p the source pbuf |
||
1296 | * |
||
1297 | * @return a new pbuf or NULL if allocation fails |
||
1298 | */ |
||
1299 | struct pbuf * |
||
1300 | pbuf_clone(pbuf_layer layer, pbuf_type type, struct pbuf *p) |
||
1301 | { |
||
1302 | struct pbuf *q; |
||
1303 | err_t err; |
||
1304 | q = pbuf_alloc(layer, p->tot_len, type); |
||
1305 | if (q == NULL) { |
||
1306 | return NULL; |
||
1307 | } |
||
1308 | err = pbuf_copy(q, p); |
||
1309 | LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */ |
||
1310 | LWIP_ASSERT("pbuf_copy failed", err == ERR_OK); |
||
1311 | return q; |
||
1312 | } |
||
1313 | |||
1314 | #if LWIP_CHECKSUM_ON_COPY |
||
1315 | /** |
||
1316 | * Copies data into a single pbuf (*not* into a pbuf queue!) and updates |
||
1317 | * the checksum while copying |
||
1318 | * |
||
1319 | * @param p the pbuf to copy data into |
||
1320 | * @param start_offset offset of p->payload where to copy the data to |
||
1321 | * @param dataptr data to copy into the pbuf |
||
1322 | * @param len length of data to copy into the pbuf |
||
1323 | * @param chksum pointer to the checksum which is updated |
||
1324 | * @return ERR_OK if successful, another error if the data does not fit |
||
1325 | * within the (first) pbuf (no pbuf queues!) |
||
1326 | */ |
||
1327 | err_t |
||
1328 | pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr, |
||
1329 | u16_t len, u16_t *chksum) |
||
1330 | { |
||
1331 | u32_t acc; |
||
1332 | u16_t copy_chksum; |
||
1333 | char *dst_ptr; |
||
1334 | LWIP_ASSERT("p != NULL", p != NULL); |
||
1335 | LWIP_ASSERT("dataptr != NULL", dataptr != NULL); |
||
1336 | LWIP_ASSERT("chksum != NULL", chksum != NULL); |
||
1337 | LWIP_ASSERT("len != 0", len != 0); |
||
1338 | |||
1339 | if ((start_offset >= p->len) || (start_offset + len > p->len)) { |
||
1340 | return ERR_ARG; |
||
1341 | } |
||
1342 | |||
1343 | dst_ptr = ((char *)p->payload) + start_offset; |
||
1344 | copy_chksum = LWIP_CHKSUM_COPY(dst_ptr, dataptr, len); |
||
1345 | if ((start_offset & 1) != 0) { |
||
1346 | copy_chksum = SWAP_BYTES_IN_WORD(copy_chksum); |
||
1347 | } |
||
1348 | acc = *chksum; |
||
1349 | acc += copy_chksum; |
||
1350 | *chksum = FOLD_U32T(acc); |
||
1351 | return ERR_OK; |
||
1352 | } |
||
1353 | #endif /* LWIP_CHECKSUM_ON_COPY */ |
||
1354 | |||
1355 | /** |
||
1356 | * @ingroup pbuf |
||
1357 | * Get one byte from the specified position in a pbuf |
||
1358 | * WARNING: returns zero for offset >= p->tot_len |
||
1359 | * |
||
1360 | * @param p pbuf to parse |
||
1361 | * @param offset offset into p of the byte to return |
||
1362 | * @return byte at an offset into p OR ZERO IF 'offset' >= p->tot_len |
||
1363 | */ |
||
1364 | u8_t |
||
1365 | pbuf_get_at(const struct pbuf *p, u16_t offset) |
||
1366 | { |
||
1367 | int ret = pbuf_try_get_at(p, offset); |
||
1368 | if (ret >= 0) { |
||
1369 | return (u8_t)ret; |
||
1370 | } |
||
1371 | return 0; |
||
1372 | } |
||
1373 | |||
1374 | /** |
||
1375 | * @ingroup pbuf |
||
1376 | * Get one byte from the specified position in a pbuf |
||
1377 | * |
||
1378 | * @param p pbuf to parse |
||
1379 | * @param offset offset into p of the byte to return |
||
1380 | * @return byte at an offset into p [0..0xFF] OR negative if 'offset' >= p->tot_len |
||
1381 | */ |
||
1382 | int |
||
1383 | pbuf_try_get_at(const struct pbuf *p, u16_t offset) |
||
1384 | { |
||
1385 | u16_t q_idx; |
||
1386 | const struct pbuf *q = pbuf_skip_const(p, offset, &q_idx); |
||
1387 | |||
1388 | /* return requested data if pbuf is OK */ |
||
1389 | if ((q != NULL) && (q->len > q_idx)) { |
||
1390 | return ((u8_t *)q->payload)[q_idx]; |
||
1391 | } |
||
1392 | return -1; |
||
1393 | } |
||
1394 | |||
1395 | /** |
||
1396 | * @ingroup pbuf |
||
1397 | * Put one byte to the specified position in a pbuf |
||
1398 | * WARNING: silently ignores offset >= p->tot_len |
||
1399 | * |
||
1400 | * @param p pbuf to fill |
||
1401 | * @param offset offset into p of the byte to write |
||
1402 | * @param data byte to write at an offset into p |
||
1403 | */ |
||
1404 | void |
||
1405 | pbuf_put_at(struct pbuf *p, u16_t offset, u8_t data) |
||
1406 | { |
||
1407 | u16_t q_idx; |
||
1408 | struct pbuf *q = pbuf_skip(p, offset, &q_idx); |
||
1409 | |||
1410 | /* write requested data if pbuf is OK */ |
||
1411 | if ((q != NULL) && (q->len > q_idx)) { |
||
1412 | ((u8_t *)q->payload)[q_idx] = data; |
||
1413 | } |
||
1414 | } |
||
1415 | |||
1416 | /** |
||
1417 | * @ingroup pbuf |
||
1418 | * Compare pbuf contents at specified offset with memory s2, both of length n |
||
1419 | * |
||
1420 | * @param p pbuf to compare |
||
1421 | * @param offset offset into p at which to start comparing |
||
1422 | * @param s2 buffer to compare |
||
1423 | * @param n length of buffer to compare |
||
1424 | * @return zero if equal, nonzero otherwise |
||
1425 | * (0xffff if p is too short, diffoffset+1 otherwise) |
||
1426 | */ |
||
1427 | u16_t |
||
1428 | pbuf_memcmp(const struct pbuf *p, u16_t offset, const void *s2, u16_t n) |
||
1429 | { |
||
1430 | u16_t start = offset; |
||
1431 | const struct pbuf *q = p; |
||
1432 | u16_t i; |
||
1433 | |||
1434 | /* pbuf long enough to perform check? */ |
||
1435 | if (p->tot_len < (offset + n)) { |
||
1436 | return 0xffff; |
||
1437 | } |
||
1438 | |||
1439 | /* get the correct pbuf from chain. We know it succeeds because of p->tot_len check above. */ |
||
1440 | while ((q != NULL) && (q->len <= start)) { |
||
1441 | start = (u16_t)(start - q->len); |
||
1442 | q = q->next; |
||
1443 | } |
||
1444 | |||
1445 | /* return requested data if pbuf is OK */ |
||
1446 | for (i = 0; i < n; i++) { |
||
1447 | /* We know pbuf_get_at() succeeds because of p->tot_len check above. */ |
||
1448 | u8_t a = pbuf_get_at(q, (u16_t)(start + i)); |
||
1449 | u8_t b = ((const u8_t *)s2)[i]; |
||
1450 | if (a != b) { |
||
1451 | return (u16_t)LWIP_MIN(i + 1, 0xFFFF); |
||
1452 | } |
||
1453 | } |
||
1454 | return 0; |
||
1455 | } |
||
1456 | |||
1457 | /** |
||
1458 | * @ingroup pbuf |
||
1459 | * Find occurrence of mem (with length mem_len) in pbuf p, starting at offset |
||
1460 | * start_offset. |
||
1461 | * |
||
1462 | * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as |
||
1463 | * return value 'not found' |
||
1464 | * @param mem search for the contents of this buffer |
||
1465 | * @param mem_len length of 'mem' |
||
1466 | * @param start_offset offset into p at which to start searching |
||
1467 | * @return 0xFFFF if substr was not found in p or the index where it was found |
||
1468 | */ |
||
1469 | u16_t |
||
1470 | pbuf_memfind(const struct pbuf *p, const void *mem, u16_t mem_len, u16_t start_offset) |
||
1471 | { |
||
1472 | u16_t i; |
||
1473 | u16_t max_cmp_start = (u16_t)(p->tot_len - mem_len); |
||
1474 | if (p->tot_len >= mem_len + start_offset) { |
||
1475 | for (i = start_offset; i <= max_cmp_start; i++) { |
||
1476 | u16_t plus = pbuf_memcmp(p, i, mem, mem_len); |
||
1477 | if (plus == 0) { |
||
1478 | return i; |
||
1479 | } |
||
1480 | } |
||
1481 | } |
||
1482 | return 0xFFFF; |
||
1483 | } |
||
1484 | |||
1485 | /** |
||
1486 | * Find occurrence of substr with length substr_len in pbuf p, start at offset |
||
1487 | * start_offset |
||
1488 | * WARNING: in contrast to strstr(), this one does not stop at the first \0 in |
||
1489 | * the pbuf/source string! |
||
1490 | * |
||
1491 | * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as |
||
1492 | * return value 'not found' |
||
1493 | * @param substr string to search for in p, maximum length is 0xFFFE |
||
1494 | * @return 0xFFFF if substr was not found in p or the index where it was found |
||
1495 | */ |
||
1496 | u16_t |
||
1497 | pbuf_strstr(const struct pbuf *p, const char *substr) |
||
1498 | { |
||
1499 | size_t substr_len; |
||
1500 | if ((substr == NULL) || (substr[0] == 0) || (p->tot_len == 0xFFFF)) { |
||
1501 | return 0xFFFF; |
||
1502 | } |
||
1503 | substr_len = strlen(substr); |
||
1504 | if (substr_len >= 0xFFFF) { |
||
1505 | return 0xFFFF; |
||
1506 | } |
||
1507 | return pbuf_memfind(p, substr, (u16_t)substr_len, 0); |
||
1508 | } |