
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
155 lines
4.2 KiB
C
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;
|
|
}
|