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 for zero-copy RX:
37  @code{.c}
38typedef struct my_custom_pbuf
39{
40   struct pbuf_custom p;
41   void* dma_descriptor;
42} my_custom_pbuf_t;
43
44LWIP_MEMPOOL_DECLARE(RX_POOL, 10, sizeof(my_custom_pbuf_t), "Zero-copy RX PBUF pool");
45
46void my_pbuf_free_custom(void* p)
47{
48  my_custom_pbuf_t* my_puf = (my_custom_pbuf_t*)p;
49
50  LOCK_INTERRUPTS();
51  free_rx_dma_descriptor(my_pbuf->dma_descriptor);
52  LWIP_MEMPOOL_FREE(RX_POOL, my_pbuf);
53  UNLOCK_INTERRUPTS();
54}
55
56void eth_rx_irq()
57{
58  dma_descriptor*   dma_desc = get_RX_DMA_descriptor_from_ethernet();
59  my_custom_pbuf_t* my_pbuf  = (my_custom_pbuf_t*)LWIP_MEMPOOL_ALLOC(RX_POOL);
60
61  my_pbuf->p.custom_free_function = my_pbuf_free_custom;
62  my_pbuf->dma_descriptor         = dma_desc;
63
64  invalidate_cpu_cache(dma_desc->rx_data, dma_desc->rx_length);
65
66  struct pbuf* p = pbuf_alloced_custom(PBUF_RAW,
67     dma_desc->rx_length,
68     PBUF_REF,
69     &my_pbuf->p,
70     dma_desc->rx_data,
71     dma_desc->max_buffer_size);
72
73  if(netif->input(p, netif) != ERR_OK) {
74    pbuf_free(p);
75  }
76}
77  @endcode
78 */
79
80/*
81 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
82 * All rights reserved.
83 *
84 * Redistribution and use in source and binary forms, with or without modification,
85 * are permitted provided that the following conditions are met:
86 *
87 * 1. Redistributions of source code must retain the above copyright notice,
88 *    this list of conditions and the following disclaimer.
89 * 2. Redistributions in binary form must reproduce the above copyright notice,
90 *    this list of conditions and the following disclaimer in the documentation
91 *    and/or other materials provided with the distribution.
92 * 3. The name of the author may not be used to endorse or promote products
93 *    derived from this software without specific prior written permission.
94 *
95 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
96 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
97 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
98 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
99 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
100 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
101 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
102 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
103 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
104 * OF SUCH DAMAGE.
105 *
106 * This file is part of the lwIP TCP/IP stack.
107 *
108 * Author: Adam Dunkels <adam@sics.se>
109 *
110 */
111
112#include "lwip/opt.h"
113
114#include "lwip/stats.h"
115#include "lwip/def.h"
116#include "lwip/mem.h"
117#include "lwip/memp.h"
118#include "lwip/pbuf.h"
119#include "lwip/sys.h"
120#if LWIP_TCP && TCP_QUEUE_OOSEQ
121#include "lwip/priv/tcp_priv.h"
122#endif
123#if LWIP_CHECKSUM_ON_COPY
124#include "lwip/inet_chksum.h"
125#endif
126
127#include <string.h>
128
129#define SIZEOF_STRUCT_PBUF        LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf))
130/* Since the pool is created in memp, PBUF_POOL_BUFSIZE will be automatically
131   aligned there. Therefore, PBUF_POOL_BUFSIZE_ALIGNED can be used here. */
132#define PBUF_POOL_BUFSIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE)
133
134
135
136
137/**
138 * Adjusts the payload pointer to hide or reveal headers in the payload.
139 * @see pbuf_header.
140 *
141 * @param p pbuf to change the header size.
142 * @param header_size_increment Number of bytes to increment header size.
143 * @param force Allow 'header_size_increment > 0' for PBUF_REF/PBUF_ROM types
144 *
145 * @return non-zero on failure, zero on success.
146 *
147 */
148static u8_t
149pbuf_header_impl(struct pbuf *p, s16_t header_size_increment, u8_t force)
150{
151  u16_t type;
152  void *payload;
153  u16_t increment_magnitude;
154
155  LWIP_ASSERT("p != NULL", p != NULL);
156  if ((header_size_increment == 0) || (p == NULL)) {
157    return 0;
158  }
159
160  if (header_size_increment < 0) {
161    increment_magnitude = (u16_t)-header_size_increment;
162    /* Check that we aren't going to move off the end of the pbuf */
163    LWIP_ERROR("increment_magnitude <= p->len", (increment_magnitude <= p->len), return 1;);
164  } else {
165    increment_magnitude = (u16_t)header_size_increment;
166#if 0
167    /* Can't assert these as some callers speculatively call
168         pbuf_header() to see if it's OK.  Will return 1 below instead. */
169    /* Check that we've got the correct type of pbuf to work with */
170    LWIP_ASSERT("p->type == PBUF_RAM || p->type == PBUF_POOL",
171                p->type == PBUF_RAM || p->type == PBUF_POOL);
172    /* Check that we aren't going to move off the beginning of the pbuf */
173    LWIP_ASSERT("p->payload - increment_magnitude >= p + SIZEOF_STRUCT_PBUF",
174                (u8_t *)p->payload - increment_magnitude >= (u8_t *)p + SIZEOF_STRUCT_PBUF);
175#endif
176  }
177
178  type = p->type;
179  /* remember current payload pointer */
180  payload = p->payload;
181
182  /* pbuf types containing payloads? */
183  if (type == PBUF_RAM || type == PBUF_POOL) {
184    /* set new payload pointer */
185    p->payload = (u8_t *)p->payload - header_size_increment;
186    /* boundary check fails? */
187    if ((u8_t *)p->payload < (u8_t *)p + SIZEOF_STRUCT_PBUF) {
188      LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE,
189        ("pbuf_header: failed as %p < %p (not enough space for new header size)\n",
190        (void *)p->payload, (void *)((u8_t *)p + SIZEOF_STRUCT_PBUF)));
191      /* restore old payload pointer */
192      p->payload = payload;
193      /* bail out unsuccessfully */
194      return 1;
195    }
196  /* pbuf types referring to external payloads? */
197  } else if (type == PBUF_REF || type == PBUF_ROM) {
198    /* hide a header in the payload? */
199    if ((header_size_increment < 0) && (increment_magnitude <= p->len)) {
200      /* increase payload pointer */
201      p->payload = (u8_t *)p->payload - header_size_increment;
202    } else if ((header_size_increment > 0) && force) {
203      p->payload = (u8_t *)p->payload - header_size_increment;
204    } else {
205      /* cannot expand payload to front (yet!)
206       * bail out unsuccessfully */
207      return 1;
208    }
209  } else {
210    /* Unknown type */
211    LWIP_ASSERT("bad pbuf type", 0);
212    return 1;
213  }
214  /* modify pbuf length fields */
215  p->len += header_size_increment;
216  p->tot_len += header_size_increment;
217
218  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_header: old %p new %p (%"S16_F")\n",
219    (void *)payload, (void *)p->payload, header_size_increment));
220
221  return 0;
222}
223
224/**
225 * Adjusts the payload pointer to hide or reveal headers in the payload.
226 *
227 * Adjusts the ->payload pointer so that space for a header
228 * (dis)appears in the pbuf payload.
229 *
230 * The ->payload, ->tot_len and ->len fields are adjusted.
231 *
232 * @param p pbuf to change the header size.
233 * @param header_size_increment Number of bytes to increment header size which
234 * increases the size of the pbuf. New space is on the front.
235 * (Using a negative value decreases the header size.)
236 * If hdr_size_inc is 0, this function does nothing and returns successful.
237 *
238 * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so
239 * the call will fail. A check is made that the increase in header size does
240 * not move the payload pointer in front of the start of the buffer.
241 * @return non-zero on failure, zero on success.
242 *
243 */
244u8_t
245pbuf_header(struct pbuf *p, s16_t header_size_increment)
246{
247   return pbuf_header_impl(p, header_size_increment, 1);
248}
249
250/**
251 * Same as pbuf_header but does not check if 'header_size > 0' is allowed.
252 * This is used internally only, to allow PBUF_REF for RX.
253 */
254u8_t
255pbuf_header_force(struct pbuf *p, s16_t header_size_increment)
256{
257   return pbuf_header_impl(p, header_size_increment, 1);
258}
259
260
261
262/**
263 * Count number of pbufs in a chain
264 *
265 * @param p first pbuf of chain
266 * @return the number of pbufs in a chain
267 */
268u16_t
269pbuf_clen(const struct pbuf *p)
270{
271  u16_t len;
272
273  len = 0;
274  while (p != NULL) {
275    ++len;
276    p = p->next;
277  }
278  return len;
279}
280
281/**
282 * @ingroup pbuf
283 * Increment the reference count of the pbuf.
284 *
285 * @param p pbuf to increase reference counter of
286 *
287 */
288void
289pbuf_ref(struct pbuf *p)
290{
291  /* pbuf given? */
292  if (p != NULL) {
293    SYS_ARCH_INC(p->ref, 1);
294    LWIP_ASSERT("pbuf ref overflow", p->ref > 0);
295  }
296}
297
298/**
299 * @ingroup pbuf
300 * Concatenate two pbufs (each may be a pbuf chain) and take over
301 * the caller's reference of the tail pbuf.
302 *
303 * @note The caller MAY NOT reference the tail pbuf afterwards.
304 * Use pbuf_chain() for that purpose.
305 *
306 * @see pbuf_chain()
307 */
308void
309pbuf_cat(struct pbuf *h, struct pbuf *t)
310{
311  struct pbuf *p;
312
313  LWIP_ERROR("(h != NULL) && (t != NULL) (programmer violates API)",
314             ((h != NULL) && (t != NULL)), return;);
315
316  /* proceed to last pbuf of chain */
317  for (p = h; p->next != NULL; p = p->next) {
318    /* add total length of second chain to all totals of first chain */
319    p->tot_len += t->tot_len;
320  }
321  /* { p is last pbuf of first h chain, p->next == NULL } */
322  LWIP_ASSERT("p->tot_len == p->len (of last pbuf in chain)", p->tot_len == p->len);
323  LWIP_ASSERT("p->next == NULL", p->next == NULL);
324  /* add total length of second chain to last pbuf total of first chain */
325  p->tot_len += t->tot_len;
326  /* chain last pbuf of head (p) with first of tail (t) */
327  p->next = t;
328  /* p->next now references t, but the caller will drop its reference to t,
329   * so netto there is no change to the reference count of t.
330   */
331}
332
333/**
334 * @ingroup pbuf
335 * Chain two pbufs (or pbuf chains) together.
336 *
337 * The caller MUST call pbuf_free(t) once it has stopped
338 * using it. Use pbuf_cat() instead if you no longer use t.
339 *
340 * @param h head pbuf (chain)
341 * @param t tail pbuf (chain)
342 * @note The pbufs MUST belong to the same packet.
343 * @note MAY NOT be called on a packet queue.
344 *
345 * The ->tot_len fields of all pbufs of the head chain are adjusted.
346 * The ->next field of the last pbuf of the head chain is adjusted.
347 * The ->ref field of the first pbuf of the tail chain is adjusted.
348 *
349 */
350void
351pbuf_chain(struct pbuf *h, struct pbuf *t)
352{
353  pbuf_cat(h, t);
354  /* t is now referenced by h */
355  pbuf_ref(t);
356  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_chain: %p references %p\n", (void *)h, (void *)t));
357}
358
359/**
360 * Dechains the first pbuf from its succeeding pbufs in the chain.
361 *
362 * Makes p->tot_len field equal to p->len.
363 * @param p pbuf to dechain
364 * @return remainder of the pbuf chain, or NULL if it was de-allocated.
365 * @note May not be called on a packet queue.
366 */
367struct pbuf *
368pbuf_dechain(struct pbuf *p)
369{
370  struct pbuf *q;
371  u8_t tail_gone = 1;
372  /* tail */
373  q = p->next;
374  /* pbuf has successor in chain? */
375  if (q != NULL) {
376    /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */
377    LWIP_ASSERT("p->tot_len == p->len + q->tot_len", q->tot_len == p->tot_len - p->len);
378    /* enforce invariant if assertion is disabled */
379    q->tot_len = p->tot_len - p->len;
380    /* decouple pbuf from remainder */
381    p->next = NULL;
382    /* total length of pbuf p is its own length only */
383    p->tot_len = p->len;
384    /* q is no longer referenced by p, free it */
385    LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_dechain: unreferencing %p\n", (void *)q));
386    tail_gone = pbuf_free(q);
387    if (tail_gone > 0) {
388      LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE,
389                  ("pbuf_dechain: deallocated %p (as it is no longer referenced)\n", (void *)q));
390    }
391    /* return remaining tail or NULL if deallocated */
392  }
393  /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */
394  LWIP_ASSERT("p->tot_len == p->len", p->tot_len == p->len);
395  return ((tail_gone > 0) ? NULL : q);
396}
397
398/**
399 * @ingroup pbuf
400 * Create PBUF_RAM copies of pbufs.
401 *
402 * Used to queue packets on behalf of the lwIP stack, such as
403 * ARP based queueing.
404 *
405 * @note You MUST explicitly use p = pbuf_take(p);
406 *
407 * @note Only one packet is copied, no packet queue!
408 *
409 * @param p_to pbuf destination of the copy
410 * @param p_from pbuf source of the copy
411 *
412 * @return ERR_OK if pbuf was copied
413 *         ERR_ARG if one of the pbufs is NULL or p_to is not big
414 *                 enough to hold p_from
415 */
416err_t
417pbuf_copy(struct pbuf *p_to, const struct pbuf *p_from)
418{
419  u16_t offset_to=0, offset_from=0, len;
420
421  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy(%p, %p)\n",
422    (const void*)p_to, (const void*)p_from));
423
424  /* is the target big enough to hold the source? */
425  LWIP_ERROR("pbuf_copy: target not big enough to hold source", ((p_to != NULL) &&
426             (p_from != NULL) && (p_to->tot_len >= p_from->tot_len)), return ERR_ARG;);
427
428  /* iterate through pbuf chain */
429  do
430  {
431    /* copy one part of the original chain */
432    if ((p_to->len - offset_to) >= (p_from->len - offset_from)) {
433      /* complete current p_from fits into current p_to */
434      len = p_from->len - offset_from;
435    } else {
436      /* current p_from does not fit into current p_to */
437      len = p_to->len - offset_to;
438    }
439    MEMCPY((u8_t*)p_to->payload + offset_to, (u8_t*)p_from->payload + offset_from, len);
440    offset_to += len;
441    offset_from += len;
442    LWIP_ASSERT("offset_to <= p_to->len", offset_to <= p_to->len);
443    LWIP_ASSERT("offset_from <= p_from->len", offset_from <= p_from->len);
444    if (offset_from >= p_from->len) {
445      /* on to next p_from (if any) */
446      offset_from = 0;
447      p_from = p_from->next;
448    }
449    if (offset_to == p_to->len) {
450      /* on to next p_to (if any) */
451      offset_to = 0;
452      p_to = p_to->next;
453      LWIP_ERROR("p_to != NULL", (p_to != NULL) || (p_from == NULL) , return ERR_ARG;);
454    }
455
456    if ((p_from != NULL) && (p_from->len == p_from->tot_len)) {
457      /* don't copy more than one packet! */
458      LWIP_ERROR("pbuf_copy() does not allow packet queues!",
459                 (p_from->next == NULL), return ERR_VAL;);
460    }
461    if ((p_to != NULL) && (p_to->len == p_to->tot_len)) {
462      /* don't copy more than one packet! */
463      LWIP_ERROR("pbuf_copy() does not allow packet queues!",
464                  (p_to->next == NULL), return ERR_VAL;);
465    }
466  } while (p_from);
467  LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy: end of chain reached.\n"));
468  return ERR_OK;
469}
470
471/**
472 * @ingroup pbuf
473 * Copy (part of) the contents of a packet buffer
474 * to an application supplied buffer.
475 *
476 * @param buf the pbuf from which to copy data
477 * @param dataptr the application supplied buffer
478 * @param len length of data to copy (dataptr must be big enough). No more
479 * than buf->tot_len will be copied, irrespective of len
480 * @param offset offset into the packet buffer from where to begin copying len bytes
481 * @return the number of bytes copied, or 0 on failure
482 */
483u16_t
484pbuf_copy_partial(const struct pbuf *buf, void *dataptr, u16_t len, u16_t offset)
485{
486  const struct pbuf *p;
487  u16_t left;
488  u16_t buf_copy_len;
489  u16_t copied_total = 0;
490
491  LWIP_ERROR("pbuf_copy_partial: invalid buf", (buf != NULL), return 0;);
492  LWIP_ERROR("pbuf_copy_partial: invalid dataptr", (dataptr != NULL), return 0;);
493
494  left = 0;
495
496  if ((buf == NULL) || (dataptr == NULL)) {
497    return 0;
498  }
499
500  /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */
501  for (p = buf; len != 0 && p != NULL; p = p->next) {
502    if ((offset != 0) && (offset >= p->len)) {
503      /* don't copy from this buffer -> on to the next */
504      offset -= p->len;
505    } else {
506      /* copy from this buffer. maybe only partially. */
507      buf_copy_len = p->len - offset;
508      if (buf_copy_len > len) {
509        buf_copy_len = len;
510      }
511      /* copy the necessary parts of the buffer */
512      MEMCPY(&((char*)dataptr)[left], &((char*)p->payload)[offset], buf_copy_len);
513      copied_total += buf_copy_len;
514      left += buf_copy_len;
515      len -= buf_copy_len;
516      offset = 0;
517    }
518  }
519  return copied_total;
520}
521
522#if LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE
523/**
524 * This method modifies a 'pbuf chain', so that its total length is
525 * smaller than 64K. The remainder of the original pbuf chain is stored
526 * in *rest.
527 * This function never creates new pbufs, but splits an existing chain
528 * in two parts. The tot_len of the modified packet queue will likely be
529 * smaller than 64K.
530 * 'packet queues' are not supported by this function.
531 *
532 * @param p the pbuf queue to be split
533 * @param rest pointer to store the remainder (after the first 64K)
534 */
535void pbuf_split_64k(struct pbuf *p, struct pbuf **rest)
536{
537  *rest = NULL;
538  if ((p != NULL) && (p->next != NULL)) {
539    u16_t tot_len_front = p->len;
540    struct pbuf *i = p;
541    struct pbuf *r = p->next;
542
543    /* continue until the total length (summed up as u16_t) overflows */
544    while ((r != NULL) && ((u16_t)(tot_len_front + r->len) > tot_len_front)) {
545      tot_len_front += r->len;
546      i = r;
547      r = r->next;
548    }
549    /* i now points to last packet of the first segment. Set next
550       pointer to NULL */
551    i->next = NULL;
552
553    if (r != NULL) {
554      /* Update the tot_len field in the first part */
555      for (i = p; i != NULL; i = i->next) {
556        i->tot_len -= r->tot_len;
557        LWIP_ASSERT("tot_len/len mismatch in last pbuf",
558                    (i->next != NULL) || (i->tot_len == i->len));
559      }
560      if (p->flags & PBUF_FLAG_TCP_FIN) {
561        r->flags |= PBUF_FLAG_TCP_FIN;
562      }
563
564      /* tot_len field in rest does not need modifications */
565      /* reference counters do not need modifications */
566      *rest = r;
567    }
568  }
569}
570#endif /* LWIP_TCP && TCP_QUEUE_OOSEQ && LWIP_WND_SCALE */
571
572/* Actual implementation of pbuf_skip() but returning const pointer... */
573static const struct pbuf*
574pbuf_skip_const(const struct pbuf* in, u16_t in_offset, u16_t* out_offset)
575{
576  u16_t offset_left = in_offset;
577  const struct pbuf* q = in;
578
579  /* get the correct pbuf */
580  while ((q != NULL) && (q->len <= offset_left)) {
581    offset_left -= q->len;
582    q = q->next;
583  }
584  if (out_offset != NULL) {
585    *out_offset = offset_left;
586  }
587  return q;
588}
589
590/**
591 * @ingroup pbuf
592 * Skip a number of bytes at the start of a pbuf
593 *
594 * @param in input pbuf
595 * @param in_offset offset to skip
596 * @param out_offset resulting offset in the returned pbuf
597 * @return the pbuf in the queue where the offset is
598 */
599struct pbuf*
600pbuf_skip(struct pbuf* in, u16_t in_offset, u16_t* out_offset)
601{
602  const struct pbuf* out = pbuf_skip_const(in, in_offset, out_offset);
603  return LWIP_CONST_CAST(struct pbuf*, out);
604}
605
606/**
607 * @ingroup pbuf
608 * Copy application supplied data into a pbuf.
609 * This function can only be used to copy the equivalent of buf->tot_len data.
610 *
611 * @param buf pbuf to fill with data
612 * @param dataptr application supplied data buffer
613 * @param len length of the application supplied data buffer
614 *
615 * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough
616 */
617err_t
618pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len)
619{
620  struct pbuf *p;
621  u16_t buf_copy_len;
622  u16_t total_copy_len = len;
623  u16_t copied_total = 0;
624
625  LWIP_ERROR("pbuf_take: invalid buf", (buf != NULL), return ERR_ARG;);
626  LWIP_ERROR("pbuf_take: invalid dataptr", (dataptr != NULL), return ERR_ARG;);
627  LWIP_ERROR("pbuf_take: buf not large enough", (buf->tot_len >= len), return ERR_MEM;);
628
629  if ((buf == NULL) || (dataptr == NULL) || (buf->tot_len < len)) {
630    return ERR_ARG;
631  }
632
633  /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */
634  for (p = buf; total_copy_len != 0; p = p->next) {
635    LWIP_ASSERT("pbuf_take: invalid pbuf", p != NULL);
636    buf_copy_len = total_copy_len;
637    if (buf_copy_len > p->len) {
638      /* this pbuf cannot hold all remaining data */
639      buf_copy_len = p->len;
640    }
641    /* copy the necessary parts of the buffer */
642    MEMCPY(p->payload, &((const char*)dataptr)[copied_total], buf_copy_len);
643    total_copy_len -= buf_copy_len;
644    copied_total += buf_copy_len;
645  }
646  LWIP_ASSERT("did not copy all data", total_copy_len == 0 && copied_total == len);
647  return ERR_OK;
648}
649
650/**
651 * @ingroup pbuf
652 * Same as pbuf_take() but puts data at an offset
653 *
654 * @param buf pbuf to fill with data
655 * @param dataptr application supplied data buffer
656 * @param len length of the application supplied data buffer
657 * @param offset offset in pbuf where to copy dataptr to
658 *
659 * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough
660 */
661err_t
662pbuf_take_at(struct pbuf *buf, const void *dataptr, u16_t len, u16_t offset)
663{
664  u16_t target_offset;
665  struct pbuf* q = pbuf_skip(buf, offset, &target_offset);
666
667  /* return requested data if pbuf is OK */
668  if ((q != NULL) && (q->tot_len >= target_offset + len)) {
669    u16_t remaining_len = len;
670    const u8_t* src_ptr = (const u8_t*)dataptr;
671    /* copy the part that goes into the first pbuf */
672    u16_t first_copy_len = LWIP_MIN(q->len - target_offset, len);
673    MEMCPY(((u8_t*)q->payload) + target_offset, dataptr, first_copy_len);
674    remaining_len -= first_copy_len;
675    src_ptr += first_copy_len;
676    if (remaining_len > 0) {
677      return pbuf_take(q->next, src_ptr, remaining_len);
678    }
679    return ERR_OK;
680  }
681  return ERR_MEM;
682}
683
684/**
685 * @ingroup pbuf
686 * Creates a single pbuf out of a queue of pbufs.
687 *
688 * @remark: Either the source pbuf 'p' is freed by this function or the original
689 *          pbuf 'p' is returned, therefore the caller has to check the result!
690 *
691 * @param p the source pbuf
692 * @param layer pbuf_layer of the new pbuf
693 *
694 * @return a new, single pbuf (p->next is NULL)
695 *         or the old pbuf if allocation fails
696 */
697struct pbuf*
698pbuf_coalesce(struct pbuf *p, pbuf_layer layer)
699{
700  struct pbuf *q;
701  err_t err;
702  if (p->next == NULL) {
703    return p;
704  }
705  q = pbuf_alloc(layer, p->tot_len, PBUF_RAM);
706  if (q == NULL) {
707    /* @todo: what do we do now? */
708    return p;
709  }
710  err = pbuf_copy(q, p);
711  LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
712  LWIP_ASSERT("pbuf_copy failed", err == ERR_OK);
713  pbuf_free(p);
714  return q;
715}
716
717#if LWIP_CHECKSUM_ON_COPY
718/**
719 * Copies data into a single pbuf (*not* into a pbuf queue!) and updates
720 * the checksum while copying
721 *
722 * @param p the pbuf to copy data into
723 * @param start_offset offset of p->payload where to copy the data to
724 * @param dataptr data to copy into the pbuf
725 * @param len length of data to copy into the pbuf
726 * @param chksum pointer to the checksum which is updated
727 * @return ERR_OK if successful, another error if the data does not fit
728 *         within the (first) pbuf (no pbuf queues!)
729 */
730err_t
731pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr,
732                 u16_t len, u16_t *chksum)
733{
734  u32_t acc;
735  u16_t copy_chksum;
736  char *dst_ptr;
737  LWIP_ASSERT("p != NULL", p != NULL);
738  LWIP_ASSERT("dataptr != NULL", dataptr != NULL);
739  LWIP_ASSERT("chksum != NULL", chksum != NULL);
740  LWIP_ASSERT("len != 0", len != 0);
741
742  if ((start_offset >= p->len) || (start_offset + len > p->len)) {
743    return ERR_ARG;
744  }
745
746  dst_ptr = ((char*)p->payload) + start_offset;
747  copy_chksum = LWIP_CHKSUM_COPY(dst_ptr, dataptr, len);
748  if ((start_offset & 1) != 0) {
749    copy_chksum = SWAP_BYTES_IN_WORD(copy_chksum);
750  }
751  acc = *chksum;
752  acc += copy_chksum;
753  *chksum = FOLD_U32T(acc);
754  return ERR_OK;
755}
756#endif /* LWIP_CHECKSUM_ON_COPY */
757
758/**
759 * @ingroup pbuf
760 * Get one byte from the specified position in a pbuf
761 * WARNING: returns zero for offset >= p->tot_len
762 *
763 * @param p pbuf to parse
764 * @param offset offset into p of the byte to return
765 * @return byte at an offset into p OR ZERO IF 'offset' >= p->tot_len
766 */
767u8_t
768pbuf_get_at(const struct pbuf* p, u16_t offset)
769{
770  int ret = pbuf_try_get_at(p, offset);
771  if (ret >= 0) {
772    return (u8_t)ret;
773  }
774  return 0;
775}
776
777/**
778 * @ingroup pbuf
779 * Get one byte from the specified position in a pbuf
780 *
781 * @param p pbuf to parse
782 * @param offset offset into p of the byte to return
783 * @return byte at an offset into p [0..0xFF] OR negative if 'offset' >= p->tot_len
784 */
785int
786pbuf_try_get_at(const struct pbuf* p, u16_t offset)
787{
788  u16_t q_idx;
789  const struct pbuf* q = pbuf_skip_const(p, offset, &q_idx);
790
791  /* return requested data if pbuf is OK */
792  if ((q != NULL) && (q->len > q_idx)) {
793    return ((u8_t*)q->payload)[q_idx];
794  }
795  return -1;
796}
797
798/**
799 * @ingroup pbuf
800 * Put one byte to the specified position in a pbuf
801 * WARNING: silently ignores offset >= p->tot_len
802 *
803 * @param p pbuf to fill
804 * @param offset offset into p of the byte to write
805 * @param data byte to write at an offset into p
806 */
807void
808pbuf_put_at(struct pbuf* p, u16_t offset, u8_t data)
809{
810  u16_t q_idx;
811  struct pbuf* q = pbuf_skip(p, offset, &q_idx);
812
813  /* write requested data if pbuf is OK */
814  if ((q != NULL) && (q->len > q_idx)) {
815    ((u8_t*)q->payload)[q_idx] = data;
816  }
817}
818
819/**
820 * @ingroup pbuf
821 * Compare pbuf contents at specified offset with memory s2, both of length n
822 *
823 * @param p pbuf to compare
824 * @param offset offset into p at which to start comparing
825 * @param s2 buffer to compare
826 * @param n length of buffer to compare
827 * @return zero if equal, nonzero otherwise
828 *         (0xffff if p is too short, diffoffset+1 otherwise)
829 */
830u16_t
831pbuf_memcmp(const struct pbuf* p, u16_t offset, const void* s2, u16_t n)
832{
833  u16_t start = offset;
834  const struct pbuf* q = p;
835  u16_t i;
836
837  /* pbuf long enough to perform check? */
838  if(p->tot_len < (offset + n)) {
839    return 0xffff;
840  }
841
842  /* get the correct pbuf from chain. We know it succeeds because of p->tot_len check above. */
843  while ((q != NULL) && (q->len <= start)) {
844    start -= q->len;
845    q = q->next;
846  }
847
848  /* return requested data if pbuf is OK */
849  for (i = 0; i < n; i++) {
850    /* We know pbuf_get_at() succeeds because of p->tot_len check above. */
851    u8_t a = pbuf_get_at(q, start + i);
852    u8_t b = ((const u8_t*)s2)[i];
853    if (a != b) {
854      return i+1;
855    }
856  }
857  return 0;
858}
859
860/**
861 * @ingroup pbuf
862 * Find occurrence of mem (with length mem_len) in pbuf p, starting at offset
863 * start_offset.
864 *
865 * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as
866 *        return value 'not found'
867 * @param mem search for the contents of this buffer
868 * @param mem_len length of 'mem'
869 * @param start_offset offset into p at which to start searching
870 * @return 0xFFFF if substr was not found in p or the index where it was found
871 */
872u16_t
873pbuf_memfind(const struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset)
874{
875  u16_t i;
876  u16_t max = p->tot_len - mem_len;
877  if (p->tot_len >= mem_len + start_offset) {
878    for (i = start_offset; i <= max; i++) {
879      u16_t plus = pbuf_memcmp(p, i, mem, mem_len);
880      if (plus == 0) {
881        return i;
882      }
883    }
884  }
885  return 0xFFFF;
886}
887
888/**
889 * Find occurrence of substr with length substr_len in pbuf p, start at offset
890 * start_offset
891 * WARNING: in contrast to strstr(), this one does not stop at the first \0 in
892 * the pbuf/source string!
893 *
894 * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as
895 *        return value 'not found'
896 * @param substr string to search for in p, maximum length is 0xFFFE
897 * @return 0xFFFF if substr was not found in p or the index where it was found
898 */
899u16_t
900pbuf_strstr(const struct pbuf* p, const char* substr)
901{
902  size_t substr_len;
903  if ((substr == NULL) || (substr[0] == 0) || (p->tot_len == 0xFFFF)) {
904    return 0xFFFF;
905  }
906  substr_len = strlen(substr);
907  if (substr_len >= 0xFFFF) {
908    return 0xFFFF;
909  }
910  return pbuf_memfind(p, substr, (u16_t)substr_len, 0);
911}
912