xref: /minix3/minix/net/lwip/pchain.c (revision ef8d499e2d2af900e9b2ab297171d7b088652482)
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