1*ef8d499eSDavid van Moolenbroek /* LWIP service - pchain.c - pbuf chain utility functions */
2*ef8d499eSDavid van Moolenbroek
3*ef8d499eSDavid van Moolenbroek #include "lwip.h"
4*ef8d499eSDavid van Moolenbroek
5*ef8d499eSDavid van Moolenbroek /*
6*ef8d499eSDavid van Moolenbroek * Allocate a chain of pbuf buffers as though it were a PBUF_POOL allocation,
7*ef8d499eSDavid van Moolenbroek * except that each buffer is of type PBUF_RAM. Return the pbuf chain on
8*ef8d499eSDavid van Moolenbroek * success, or NULL on memory allocation failure.
9*ef8d499eSDavid van Moolenbroek */
10*ef8d499eSDavid van Moolenbroek struct pbuf *
pchain_alloc(int layer,size_t size)11*ef8d499eSDavid van Moolenbroek pchain_alloc(int layer, size_t size)
12*ef8d499eSDavid van Moolenbroek {
13*ef8d499eSDavid van Moolenbroek struct pbuf *pbuf, *phead, **pnext;
14*ef8d499eSDavid van Moolenbroek size_t chunk, left;
15*ef8d499eSDavid van Moolenbroek int offset = 0;
16*ef8d499eSDavid van Moolenbroek
17*ef8d499eSDavid van Moolenbroek /*
18*ef8d499eSDavid van Moolenbroek * Check for length overflow. Note that we do this before prepending
19*ef8d499eSDavid van Moolenbroek * the header, because otherwise we could never send a full-sized
20*ef8d499eSDavid van Moolenbroek * (65535-byte) IP packet. This does mean that we are generating a
21*ef8d499eSDavid van Moolenbroek * pbuf chain that has over 64KB worth of allocated space, but our
22*ef8d499eSDavid van Moolenbroek * header hiding ensures that tot_len stays under 64KB. A check in
23*ef8d499eSDavid van Moolenbroek * pbuf_header() prevents that later header adjustments end up lifting
24*ef8d499eSDavid van Moolenbroek * tot_len over this limit.
25*ef8d499eSDavid van Moolenbroek */
26*ef8d499eSDavid van Moolenbroek if (size > UINT16_MAX)
27*ef8d499eSDavid van Moolenbroek return NULL;
28*ef8d499eSDavid van Moolenbroek
29*ef8d499eSDavid van Moolenbroek /*
30*ef8d499eSDavid van Moolenbroek * Unfortunately, we have no choice but to replicate this block from
31*ef8d499eSDavid van Moolenbroek * lwIP's pbuf_alloc() code. It is however unlikely that the offsets
32*ef8d499eSDavid van Moolenbroek * change for the currently supported layer types, and we do not need
33*ef8d499eSDavid van Moolenbroek * to support any layer types that we do not use ourselves.
34*ef8d499eSDavid van Moolenbroek */
35*ef8d499eSDavid van Moolenbroek switch (layer) {
36*ef8d499eSDavid van Moolenbroek case PBUF_TRANSPORT:
37*ef8d499eSDavid van Moolenbroek offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN +
38*ef8d499eSDavid van Moolenbroek PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN;
39*ef8d499eSDavid van Moolenbroek break;
40*ef8d499eSDavid van Moolenbroek case PBUF_IP:
41*ef8d499eSDavid van Moolenbroek offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN +
42*ef8d499eSDavid van Moolenbroek PBUF_IP_HLEN;
43*ef8d499eSDavid van Moolenbroek break;
44*ef8d499eSDavid van Moolenbroek case PBUF_LINK:
45*ef8d499eSDavid van Moolenbroek offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN;
46*ef8d499eSDavid van Moolenbroek break;
47*ef8d499eSDavid van Moolenbroek case PBUF_RAW_TX:
48*ef8d499eSDavid van Moolenbroek offset = PBUF_LINK_ENCAPSULATION_HLEN;
49*ef8d499eSDavid van Moolenbroek break;
50*ef8d499eSDavid van Moolenbroek case PBUF_RAW:
51*ef8d499eSDavid van Moolenbroek offset = 0;
52*ef8d499eSDavid van Moolenbroek break;
53*ef8d499eSDavid van Moolenbroek default:
54*ef8d499eSDavid van Moolenbroek panic("invalid pbuf layer: %d", layer);
55*ef8d499eSDavid van Moolenbroek }
56*ef8d499eSDavid van Moolenbroek
57*ef8d499eSDavid van Moolenbroek chunk = size + offset;
58*ef8d499eSDavid van Moolenbroek if (chunk > MEMPOOL_BUFSIZE)
59*ef8d499eSDavid van Moolenbroek chunk = MEMPOOL_BUFSIZE;
60*ef8d499eSDavid van Moolenbroek
61*ef8d499eSDavid van Moolenbroek if ((phead = pbuf_alloc(PBUF_RAW, chunk, PBUF_RAM)) == NULL)
62*ef8d499eSDavid van Moolenbroek return NULL;
63*ef8d499eSDavid van Moolenbroek
64*ef8d499eSDavid van Moolenbroek if (offset > 0)
65*ef8d499eSDavid van Moolenbroek util_pbuf_header(phead, -offset);
66*ef8d499eSDavid van Moolenbroek
67*ef8d499eSDavid van Moolenbroek phead->tot_len = size;
68*ef8d499eSDavid van Moolenbroek
69*ef8d499eSDavid van Moolenbroek pnext = &phead->next;
70*ef8d499eSDavid van Moolenbroek
71*ef8d499eSDavid van Moolenbroek for (left = size - (chunk - offset); left > 0; left -= chunk) {
72*ef8d499eSDavid van Moolenbroek chunk = (left < MEMPOOL_BUFSIZE) ? left : MEMPOOL_BUFSIZE;
73*ef8d499eSDavid van Moolenbroek
74*ef8d499eSDavid van Moolenbroek if ((pbuf = pbuf_alloc(PBUF_RAW, chunk, PBUF_RAM)) == NULL) {
75*ef8d499eSDavid van Moolenbroek /*
76*ef8d499eSDavid van Moolenbroek * Adjust tot_len to match the actual length of the
77*ef8d499eSDavid van Moolenbroek * chain so far, just in case pbuf_free() starts caring
78*ef8d499eSDavid van Moolenbroek * about this in the future.
79*ef8d499eSDavid van Moolenbroek */
80*ef8d499eSDavid van Moolenbroek for (pbuf = phead; pbuf != NULL; pbuf = pbuf->next)
81*ef8d499eSDavid van Moolenbroek pbuf->tot_len -= left;
82*ef8d499eSDavid van Moolenbroek
83*ef8d499eSDavid van Moolenbroek pbuf_free(phead);
84*ef8d499eSDavid van Moolenbroek
85*ef8d499eSDavid van Moolenbroek return NULL;
86*ef8d499eSDavid van Moolenbroek }
87*ef8d499eSDavid van Moolenbroek
88*ef8d499eSDavid van Moolenbroek pbuf->tot_len = left;
89*ef8d499eSDavid van Moolenbroek
90*ef8d499eSDavid van Moolenbroek *pnext = pbuf;
91*ef8d499eSDavid van Moolenbroek pnext = &pbuf->next;
92*ef8d499eSDavid van Moolenbroek }
93*ef8d499eSDavid van Moolenbroek
94*ef8d499eSDavid van Moolenbroek return phead;
95*ef8d499eSDavid van Moolenbroek }
96*ef8d499eSDavid van Moolenbroek
97*ef8d499eSDavid van Moolenbroek /*
98*ef8d499eSDavid van Moolenbroek * Given the (non-empty) chain of buffers 'pbuf', return a pointer to the
99*ef8d499eSDavid van Moolenbroek * 'next' field of the last buffer in the chain. This function is packet queue
100*ef8d499eSDavid van Moolenbroek * friendly. A packet queue is a queue of packet chains, where each chain is
101*ef8d499eSDavid van Moolenbroek * delimited using the 'tot_len' field. As a result, while the pointer
102*ef8d499eSDavid van Moolenbroek * returned is never NULL, the value pointed to by the returned pointer may or
103*ef8d499eSDavid van Moolenbroek * may not be NULL (and will point to the next chain if not NULL). As notable
104*ef8d499eSDavid van Moolenbroek * exception, in cases where the buffer type is a single PBUF_REF, 'tot_len'
105*ef8d499eSDavid van Moolenbroek * may be zero and 'len' may be non-zero. In such cases, the chain consists of
106*ef8d499eSDavid van Moolenbroek * that single buffer only. This function must handle that case as well.
107*ef8d499eSDavid van Moolenbroek */
108*ef8d499eSDavid van Moolenbroek struct pbuf **
pchain_end(struct pbuf * pbuf)109*ef8d499eSDavid van Moolenbroek pchain_end(struct pbuf * pbuf)
110*ef8d499eSDavid van Moolenbroek {
111*ef8d499eSDavid van Moolenbroek
112*ef8d499eSDavid van Moolenbroek assert(pbuf != NULL);
113*ef8d499eSDavid van Moolenbroek
114*ef8d499eSDavid van Moolenbroek while (pbuf->tot_len > pbuf->len) {
115*ef8d499eSDavid van Moolenbroek pbuf = pbuf->next;
116*ef8d499eSDavid van Moolenbroek
117*ef8d499eSDavid van Moolenbroek assert(pbuf != NULL);
118*ef8d499eSDavid van Moolenbroek }
119*ef8d499eSDavid van Moolenbroek
120*ef8d499eSDavid van Moolenbroek return &pbuf->next;
121*ef8d499eSDavid van Moolenbroek }
122*ef8d499eSDavid van Moolenbroek
123*ef8d499eSDavid van Moolenbroek /*
124*ef8d499eSDavid van Moolenbroek * Given the (non-empty) chain of buffers 'pbuf', return a byte size estimation
125*ef8d499eSDavid van Moolenbroek * of the memory used by the chain, rounded up to pool buffer sizes. This
126*ef8d499eSDavid van Moolenbroek * function is packet queue friendly.
127*ef8d499eSDavid van Moolenbroek */
128*ef8d499eSDavid van Moolenbroek size_t
pchain_size(struct pbuf * pbuf)129*ef8d499eSDavid van Moolenbroek pchain_size(struct pbuf * pbuf)
130*ef8d499eSDavid van Moolenbroek {
131*ef8d499eSDavid van Moolenbroek size_t size;
132*ef8d499eSDavid van Moolenbroek
133*ef8d499eSDavid van Moolenbroek assert(pbuf != NULL);
134*ef8d499eSDavid van Moolenbroek
135*ef8d499eSDavid van Moolenbroek /*
136*ef8d499eSDavid van Moolenbroek * Count the first buffer separately, as its length may be seriously
137*ef8d499eSDavid van Moolenbroek * off due to header hiding. While the caller should always provide
138*ef8d499eSDavid van Moolenbroek * exactly the same pbuf chain twice if it intends to get back the same
139*ef8d499eSDavid van Moolenbroek * size twice, this also protects against accidental size differences
140*ef8d499eSDavid van Moolenbroek * due to header hiding in that case.
141*ef8d499eSDavid van Moolenbroek */
142*ef8d499eSDavid van Moolenbroek size = MEMPOOL_BUFSIZE;
143*ef8d499eSDavid van Moolenbroek
144*ef8d499eSDavid van Moolenbroek /*
145*ef8d499eSDavid van Moolenbroek * Round up the size of the rest of the chain to whole buffers.
146*ef8d499eSDavid van Moolenbroek */
147*ef8d499eSDavid van Moolenbroek if (pbuf->tot_len > pbuf->len) {
148*ef8d499eSDavid van Moolenbroek size += pbuf->tot_len - pbuf->len + MEMPOOL_BUFSIZE - 1;
149*ef8d499eSDavid van Moolenbroek
150*ef8d499eSDavid van Moolenbroek size -= size % MEMPOOL_BUFSIZE;
151*ef8d499eSDavid van Moolenbroek }
152*ef8d499eSDavid van Moolenbroek
153*ef8d499eSDavid van Moolenbroek return size;
154*ef8d499eSDavid van Moolenbroek }
155