phunix/minix/net/lwip/pchain.c
David van Moolenbroek ef8d499e2d Add lwip: a new lwIP-based TCP/IP service
This commit adds a new TCP/IP service to MINIX 3.  As its core, the
service uses the lwIP TCP/IP stack for maintenance reasons.  The
service aims to be compatible with NetBSD userland, including its
low-level network management utilities.  It also aims to support
modern features such as IPv6.  In summary, the new LWIP service has
support for the following main features:

- TCP, UDP, RAW sockets with mostly standard BSD API semantics;
- IPv6 support: host mode (complete) and router mode (partial);
- most of the standard BSD API socket options (SO_);
- all of the standard BSD API message flags (MSG_);
- the most used protocol-specific socket and control options;
- a default loopback interface and the ability to create one more;
- configuration-free ethernet interfaces and driver tracking;
- queuing and multiple concurrent requests to each ethernet driver;
- standard ioctl(2)-based BSD interface management;
- radix tree backed, destination-based routing;
- routing sockets for standard BSD route reporting and management;
- multicast traffic and multicast group membership tracking;
- Berkeley Packet Filter (BPF) devices;
- standard and custom sysctl(7) nodes for many internals;
- a slab allocation based, hybrid static/dynamic memory pool model.

Many of its modules come with fairly elaborate comments that cover
many aspects of what is going on.  The service is primarily a socket
driver built on top of the libsockdriver library, but for BPF devices
it is at the same time also a character driver.

Change-Id: Ib0c02736234b21143915e5fcc0fda8fe408f046f
2017-04-30 13:16:03 +00:00

155 lines
4.2 KiB
C

/* LWIP service - pchain.c - pbuf chain utility functions */
#include "lwip.h"
/*
* Allocate a chain of pbuf buffers as though it were a PBUF_POOL allocation,
* except that each buffer is of type PBUF_RAM. Return the pbuf chain on
* success, or NULL on memory allocation failure.
*/
struct pbuf *
pchain_alloc(int layer, size_t size)
{
struct pbuf *pbuf, *phead, **pnext;
size_t chunk, left;
int offset = 0;
/*
* Check for length overflow. Note that we do this before prepending
* the header, because otherwise we could never send a full-sized
* (65535-byte) IP packet. This does mean that we are generating a
* pbuf chain that has over 64KB worth of allocated space, but our
* header hiding ensures that tot_len stays under 64KB. A check in
* pbuf_header() prevents that later header adjustments end up lifting
* tot_len over this limit.
*/
if (size > UINT16_MAX)
return NULL;
/*
* Unfortunately, we have no choice but to replicate this block from
* lwIP's pbuf_alloc() code. It is however unlikely that the offsets
* change for the currently supported layer types, and we do not need
* to support any layer types that we do not use ourselves.
*/
switch (layer) {
case PBUF_TRANSPORT:
offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN +
PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN;
break;
case PBUF_IP:
offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN +
PBUF_IP_HLEN;
break;
case PBUF_LINK:
offset = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN;
break;
case PBUF_RAW_TX:
offset = PBUF_LINK_ENCAPSULATION_HLEN;
break;
case PBUF_RAW:
offset = 0;
break;
default:
panic("invalid pbuf layer: %d", layer);
}
chunk = size + offset;
if (chunk > MEMPOOL_BUFSIZE)
chunk = MEMPOOL_BUFSIZE;
if ((phead = pbuf_alloc(PBUF_RAW, chunk, PBUF_RAM)) == NULL)
return NULL;
if (offset > 0)
util_pbuf_header(phead, -offset);
phead->tot_len = size;
pnext = &phead->next;
for (left = size - (chunk - offset); left > 0; left -= chunk) {
chunk = (left < MEMPOOL_BUFSIZE) ? left : MEMPOOL_BUFSIZE;
if ((pbuf = pbuf_alloc(PBUF_RAW, chunk, PBUF_RAM)) == NULL) {
/*
* Adjust tot_len to match the actual length of the
* chain so far, just in case pbuf_free() starts caring
* about this in the future.
*/
for (pbuf = phead; pbuf != NULL; pbuf = pbuf->next)
pbuf->tot_len -= left;
pbuf_free(phead);
return NULL;
}
pbuf->tot_len = left;
*pnext = pbuf;
pnext = &pbuf->next;
}
return phead;
}
/*
* Given the (non-empty) chain of buffers 'pbuf', return a pointer to the
* 'next' field of the last buffer in the chain. This function is packet queue
* friendly. A packet queue is a queue of packet chains, where each chain is
* delimited using the 'tot_len' field. As a result, while the pointer
* returned is never NULL, the value pointed to by the returned pointer may or
* may not be NULL (and will point to the next chain if not NULL). As notable
* exception, in cases where the buffer type is a single PBUF_REF, 'tot_len'
* may be zero and 'len' may be non-zero. In such cases, the chain consists of
* that single buffer only. This function must handle that case as well.
*/
struct pbuf **
pchain_end(struct pbuf * pbuf)
{
assert(pbuf != NULL);
while (pbuf->tot_len > pbuf->len) {
pbuf = pbuf->next;
assert(pbuf != NULL);
}
return &pbuf->next;
}
/*
* Given the (non-empty) chain of buffers 'pbuf', return a byte size estimation
* of the memory used by the chain, rounded up to pool buffer sizes. This
* function is packet queue friendly.
*/
size_t
pchain_size(struct pbuf * pbuf)
{
size_t size;
assert(pbuf != NULL);
/*
* Count the first buffer separately, as its length may be seriously
* off due to header hiding. While the caller should always provide
* exactly the same pbuf chain twice if it intends to get back the same
* size twice, this also protects against accidental size differences
* due to header hiding in that case.
*/
size = MEMPOOL_BUFSIZE;
/*
* Round up the size of the rest of the chain to whole buffers.
*/
if (pbuf->tot_len > pbuf->len) {
size += pbuf->tot_len - pbuf->len + MEMPOOL_BUFSIZE - 1;
size -= size % MEMPOOL_BUFSIZE;
}
return size;
}