M2: live netfilter data path (pf_test per direction), pfattach bring-up, NET_LOCK reentrancy fix, static pfctl

- pfk_hooks: PRE→PF_IN, LOCAL_OUT/POST→PF_OUT v4+v6; replacement/buffer
  handling with NF_STOLEN + re-injection (documented divergence: re-entry)
- pfk_mod: pf_status_init/pfattach + pfi attach via pfk_if event callback
- pfk_kern: NET_LOCK holder reentrancy (fix deadlock when notifier fires
  under the write lock — observed as full-VM network hang)
- tools: static pfctl link for musl VMs
This commit is contained in:
lgcosta committed 2026-11-11 15:20:01 -03:00
1 parent 0119e70960
commit 5323ee2c71
49 files changed
+20028 -28

No files matched your search

+117
View File
@@ -0,0 +1,117 @@
/* PFLINUX: small bag of BSD-isms glibc lacks, referenced by the copied
* 8.0 pfctl sources (see tools/UPSTREAM.md). */
#include <sys/types.h>
#include <stdio.h>
/* pf_print_state.c: TCP state names, normally provided by OpenBSD
* <netinet/tcp_fsm.h> under TCPSTATES (array text copied verbatim from
* _vendor/openbsd/obbsd/sys/netinet/tcp_fsm.h). */
const char *const tcpstates[] = {
"CLOSED", "LISTEN", "SYN_SENT", "SYN_RCVD",
"ESTABLISHED", "CLOSE_WAIT", "FIN_WAIT_1", "CLOSING",
"LAST_ACK", "FIN_WAIT_2", "TIME_WAIT",
};
/*
* pledge(2)/unveil(2): BSD-only process sandboxes. OpenBSD 8.0 pfctl
* sources do not call them, but the no-op stubs keep future upstream
* syncs building without changes here.
*/
int
pledge(const char *promises, const char *paths[])
{
(void)promises;
(void)paths;
return (0);
}
int
unveil(const char *path, const char *permissions)
{
(void)path;
(void)permissions;
return (0);
}
/*
* fgetln(3): BSD stdio line reader (returns the line *including* the
* trailing newline, sets *len, no NUL terminator). Used by
* pfctl_osfp.c to read fingerprint files. Backed by glibc getline(3);
* the buffer is valid until the next call on the same stream, matching
* the single-file usage in pfctl.
*/
char *
fgetln(FILE *stream, size_t *len)
{
static char *buf;
static size_t bufsz;
ssize_t n;
n = getline(&buf, &bufsz, stream);
if (n == -1)
return (NULL);
/* OpenBSD fgetln(): *len INCLUDES the trailing newline (when
* present) and the buffer is NUL-terminated as an extension;
* getline() already gives exactly that. */
*len = n;
return (buf);
}
/*
* uid_from_user(3)/gid_from_group(3): OpenBSD <pwd.h>/<grp.h> helpers
* used by parse.y for the `user`/`group` filter options. OpenBSD
* returns 0 on success; on failure returns -1 and sets errno (ENOENT
* when the name is unknown).
*/
#include <pwd.h>
#include <grp.h>
#include <errno.h>
int
uid_from_user(const char *name, uid_t *uidp)
{
struct passwd *pw;
errno = 0;
if ((pw = getpwnam(name)) == NULL) {
if (errno == 0)
errno = ENOENT;
return (-1);
}
if (uidp != NULL)
*uidp = pw->pw_uid;
return (0);
}
int
gid_from_group(const char *name, gid_t *gidp)
{
struct group *gr;
errno = 0;
if ((gr = getgrnam(name)) == NULL) {
if (errno == 0)
errno = ENOENT;
return (-1);
}
if (gidp != NULL)
*gidp = gr->gr_gid;
return (0);
}
/*
* freezero(3): OpenBSD secure realloc-free. Used by pf_ruleset.c's
* userland rs_free(). (explicit_bzero is in glibc <string.h>.)
*/
#include <string.h>
void
freezero(void *ptr, size_t siz)
{
if (ptr == NULL)
return;
explicit_bzero(ptr, siz);
free(ptr);
}
+155
View File
@@ -0,0 +1,155 @@
/*
* PFLINUX: BSD inet_net_pton(3) for pfctl on glibc. glibc 2.43 declares
* the function in <arpa/inet.h> but no longer provides the symbol (the
* resolver dropped it); libc.so.6 exports neither. pfctl_parser.c uses
* it only for AF_INET abbreviated-CIDR parsing ("10/8", "10.0.0.0/255...",
* "10.1.2" classful). Semantics implemented per OpenBSD
* lib/libc/net/inet_net_pton.c:
* - returns prefix length on success, -1 with errno on failure.
* - writes the network address (masked) to pbin.
* - AF_INET6 accepts CIDR via inet_pton (RFC 5952 form), default /128.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int
netmask_bits(uint32_t mask)
{
int bits = 0;
while ((mask & 0x80000000u) != 0) {
bits++;
mask <<= 1;
}
return ((mask == 0) ? bits : -1); /* must be contiguous */
}
static int
pton4(const char *pres, struct in_addr *pin, size_t binsize)
{
char copy[128], *sp;
struct in_addr base;
uint32_t hostaddr, mask;
int bits = -1;
if (strlen(pres) >= sizeof(copy)) {
errno = EMSGSIZE;
return (-1);
}
strcpy(copy, pres);
sp = strchr(copy, '/');
if (sp == NULL) {
if (!inet_aton(copy, &base))
return (-1);
hostaddr = ntohl(base.s_addr);
/* classful defaults, matching BSD */
if (hostaddr < 0x80000000u)
bits = 8;
else if (hostaddr < 0xC0000000u)
bits = 16;
else
bits = 24;
} else {
*sp++ = '\0';
if (!inet_aton(copy, &base))
return (-1);
if (strchr(sp, '.')) {
struct in_addr m;
if (!inet_aton(sp, &m))
return (-1);
bits = netmask_bits(ntohl(m.s_addr));
if (bits < 0)
return (-1);
} else {
char *end;
long v;
if (*sp == '\0')
return (-1);
v = strtol(sp, &end, 10);
if (end == sp || *end != '\0' || v < 0 || v > 32)
return (-1);
bits = (int)v;
}
}
hostaddr = ntohl(base.s_addr);
mask = bits == 0 ? 0 : (0xffffffffu << (32 - bits));
hostaddr &= mask;
if (binsize < sizeof(base)) {
errno = EMSGSIZE;
return (-1);
}
pin->s_addr = htonl(hostaddr);
return (bits);
}
static int
pton6(const char *pres, void *pbin, size_t binsize)
{
char copy[128], *sp;
int bits = 128;
if (strlen(pres) >= sizeof(copy)) {
errno = EMSGSIZE;
return (-1);
}
strcpy(copy, pres);
sp = strchr(copy, '/');
if (sp != NULL) {
*sp++ = '\0';
if (*sp == '\0')
return (-1);
bits = (int)strtol(sp, NULL, 10);
if (bits < 0 || bits > 128)
return (-1);
}
/* glibc inet_pton accepts bare addresses; mask by prefix by hand */
if (binsize < sizeof(struct in6_addr)) {
errno = EMSGSIZE;
return (-1);
}
if (inet_pton(AF_INET6, copy, pbin) != 1)
return (-1);
if (bits < 128) {
uint8_t *a = pbin;
int i;
for (i = bits / 8; i < 16; i++)
a[i] = 0;
a[bits / 8] &= (uint8_t)(0xffu << (8 - (bits % 8)));
}
return (bits);
}
int
inet_net_pton(int af, const char *pres, void *pbin, size_t binsize)
{
if (pres == NULL || pbin == NULL) {
errno = EFAULT;
return (-1);
}
switch (af) {
case AF_INET:
return (pton4(pres, pbin, binsize));
case AF_INET6:
return (pton6(pres, pbin, binsize));
default:
errno = EAFNOSUPPORT;
return (-1);
}
}
+242
View File
@@ -0,0 +1,242 @@
/* $OpenBSD: md5.c,v 1.4 2014/12/28 10:04:35 tedu Exp $ */
/*
* This code implements the MD5 message-digest algorithm.
* The algorithm is due to Ron Rivest. This code was
* written by Colin Plumb in 1993, no copyright is claimed.
* This code is in the public domain; do with it what you wish.
*
* Equivalent code is available from RSA Data Security, Inc.
* This code has been tested against that, and is equivalent,
* except that you don't need to include two pages of legalese
* with every copy.
*
* To compute the message digest of a chunk of bytes, declare an
* MD5Context structure, pass it to MD5Init, call MD5Update as
* needed on buffers full of bytes, and then call MD5Final, which
* will fill a supplied 16-byte array with the digest.
*/
/* PFLINUX: OpenBSD kernel includes <sys/param.h>/<sys/systm.h>/<crypto/md5.h>
* replaced by their glibc/userland equivalents; rest of the file is the
* verbatim vendor MD5 (tools/include/md5.h, string.h, endian.h). */
#include <sys/types.h>
#include <endian.h>
#include <string.h>
#include <md5.h>
#define PUT_64BIT_LE(cp, value) do { \
(cp)[7] = (value) >> 56; \
(cp)[6] = (value) >> 48; \
(cp)[5] = (value) >> 40; \
(cp)[4] = (value) >> 32; \
(cp)[3] = (value) >> 24; \
(cp)[2] = (value) >> 16; \
(cp)[1] = (value) >> 8; \
(cp)[0] = (value); } while (0)
#define PUT_32BIT_LE(cp, value) do { \
(cp)[3] = (value) >> 24; \
(cp)[2] = (value) >> 16; \
(cp)[1] = (value) >> 8; \
(cp)[0] = (value); } while (0)
static u_int8_t PADDING[MD5_BLOCK_LENGTH] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/*
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
* initialization constants.
*/
void
MD5Init(MD5_CTX *ctx)
{
ctx->count = 0;
ctx->state[0] = 0x67452301;
ctx->state[1] = 0xefcdab89;
ctx->state[2] = 0x98badcfe;
ctx->state[3] = 0x10325476;
}
/*
* Update context to reflect the concatenation of another buffer full
* of bytes.
*/
void
MD5Update(MD5_CTX *ctx, const void *inputptr, size_t len)
{
const uint8_t *input = inputptr;
size_t have, need;
/* Check how many bytes we already have and how many more we need. */
have = (size_t)((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1));
need = MD5_BLOCK_LENGTH - have;
/* Update bitcount */
ctx->count += (u_int64_t)len << 3;
if (len >= need) {
if (have != 0) {
memcpy(ctx->buffer + have, input, need);
MD5Transform(ctx->state, ctx->buffer);
input += need;
len -= need;
have = 0;
}
/* Process data in MD5_BLOCK_LENGTH-byte chunks. */
while (len >= MD5_BLOCK_LENGTH) {
MD5Transform(ctx->state, input);
input += MD5_BLOCK_LENGTH;
len -= MD5_BLOCK_LENGTH;
}
}
/* Handle any remaining bytes of data. */
if (len != 0)
memcpy(ctx->buffer + have, input, len);
}
/*
* Final wrapup - pad to 64-byte boundary with the bit pattern
* 1 0* (64-bit count of bits processed, MSB-first)
*/
void
MD5Final(unsigned char digest[MD5_DIGEST_LENGTH], MD5_CTX *ctx)
{
u_int8_t count[8];
size_t padlen;
int i;
/* Convert count to 8 bytes in little endian order. */
PUT_64BIT_LE(count, ctx->count);
/* Pad out to 56 mod 64. */
padlen = MD5_BLOCK_LENGTH -
((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1));
if (padlen < 1 + 8)
padlen += MD5_BLOCK_LENGTH;
MD5Update(ctx, PADDING, padlen - 8); /* padlen - 8 <= 64 */
MD5Update(ctx, count, 8);
for (i = 0; i < 4; i++)
PUT_32BIT_LE(digest + i * 4, ctx->state[i]);
explicit_bzero(ctx, sizeof(*ctx)); /* in case it's sensitive */
}
/* The four core functions - F1 is optimized somewhat */
/* #define F1(x, y, z) (x & y | ~x & z) */
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
/* This is the central step in the MD5 algorithm. */
#define MD5STEP(f, w, x, y, z, data, s) \
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
/*
* The core of the MD5 algorithm, this alters an existing MD5 hash to
* reflect the addition of 16 longwords of new data. MD5Update blocks
* the data and converts bytes into longwords for this routine.
*/
void
MD5Transform(u_int32_t state[4], const u_int8_t block[MD5_BLOCK_LENGTH])
{
u_int32_t a, b, c, d, in[MD5_BLOCK_LENGTH / 4];
#if BYTE_ORDER == LITTLE_ENDIAN
memcpy(in, block, sizeof(in));
#else
for (a = 0; a < MD5_BLOCK_LENGTH / 4; a++) {
in[a] = (u_int32_t)(
(u_int32_t)(block[a * 4 + 0]) |
(u_int32_t)(block[a * 4 + 1]) << 8 |
(u_int32_t)(block[a * 4 + 2]) << 16 |
(u_int32_t)(block[a * 4 + 3]) << 24);
}
#endif
a = state[0];
b = state[1];
c = state[2];
d = state[3];
MD5STEP(F1, a, b, c, d, in[ 0] + 0xd76aa478, 7);
MD5STEP(F1, d, a, b, c, in[ 1] + 0xe8c7b756, 12);
MD5STEP(F1, c, d, a, b, in[ 2] + 0x242070db, 17);
MD5STEP(F1, b, c, d, a, in[ 3] + 0xc1bdceee, 22);
MD5STEP(F1, a, b, c, d, in[ 4] + 0xf57c0faf, 7);
MD5STEP(F1, d, a, b, c, in[ 5] + 0x4787c62a, 12);
MD5STEP(F1, c, d, a, b, in[ 6] + 0xa8304613, 17);
MD5STEP(F1, b, c, d, a, in[ 7] + 0xfd469501, 22);
MD5STEP(F1, a, b, c, d, in[ 8] + 0x698098d8, 7);
MD5STEP(F1, d, a, b, c, in[ 9] + 0x8b44f7af, 12);
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
MD5STEP(F2, a, b, c, d, in[ 1] + 0xf61e2562, 5);
MD5STEP(F2, d, a, b, c, in[ 6] + 0xc040b340, 9);
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
MD5STEP(F2, b, c, d, a, in[ 0] + 0xe9b6c7aa, 20);
MD5STEP(F2, a, b, c, d, in[ 5] + 0xd62f105d, 5);
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
MD5STEP(F2, b, c, d, a, in[ 4] + 0xe7d3fbc8, 20);
MD5STEP(F2, a, b, c, d, in[ 9] + 0x21e1cde6, 5);
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
MD5STEP(F2, c, d, a, b, in[ 3] + 0xf4d50d87, 14);
MD5STEP(F2, b, c, d, a, in[ 8] + 0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, in[ 2] + 0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, in[ 7] + 0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
MD5STEP(F3, a, b, c, d, in[ 5] + 0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, in[ 8] + 0x8771f681, 11);
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, in[ 1] + 0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, in[ 4] + 0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, in[ 7] + 0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, in[ 0] + 0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, in[ 3] + 0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, in[ 6] + 0x04881d05, 23);
MD5STEP(F3, a, b, c, d, in[ 9] + 0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, in[2 ] + 0xc4ac5665, 23);
MD5STEP(F4, a, b, c, d, in[ 0] + 0xf4292244, 6);
MD5STEP(F4, d, a, b, c, in[7 ] + 0x432aff97, 10);
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, in[5 ] + 0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, in[3 ] + 0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, in[1 ] + 0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, in[8 ] + 0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, in[6 ] + 0xa3014314, 15);
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, in[4 ] + 0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, in[2 ] + 0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, in[9 ] + 0xeb86d391, 21);
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
}
+511
View File
@@ -0,0 +1,511 @@
/*
* PFLINUX: userland implementation of the out-of-line generic red-black
* tree API that tools/include/sys/tree.h (copied verbatim from
* _vendor/upstream/sys/sys/tree.h) declares and the RBT_* macros call:
* _rb_insert/_rb_find/_rb_remove/_rb_min/_rb_next/... OpenBSD ships
* these as libc symbols (sys/kern/subr_rbtree.c is linked into libc);
* glibc has none, so pfctl links this file. Behavior follows OpenBSD:
* _rb_insert returns NULL on success, the existing item on duplicate.
* _rb_remove unlinks and returns the removed item.
* Colors use the tree.h RB_RED/RB_BLACK values; struct rb_entry holds
* plain parent/left/right pointers plus an unsigned color.
* t_augment (when non-NULL) is called on ancestors after mutations.
*/
#include <sys/types.h>
#include <stddef.h>
#include <errno.h>
#include <sys/tree.h>
static __inline struct rb_entry *
rbt_entry(const struct rb_type *t, void *item)
{
return ((struct rb_entry *)((char *)item + t->t_offset));
}
static __inline void *
rbt_item(const struct rb_type *t, const struct rb_entry *rbe)
{
return (rbe == NULL ? NULL :
(void *)((char *)(struct rb_entry *)rbe - t->t_offset));
}
#define RBE_PARENT(rbe) ((rbe)->rbt_parent)
#define RBE_LEFT(rbe) ((rbe)->rbt_left)
#define RBE_RIGHT(rbe) ((rbe)->rbt_right)
#define RBE_COLOR(rbe) ((rbe)->rbt_color)
static void
rbt_augment(const struct rb_type *t, struct rb_entry *rbe)
{
for (; rbe != NULL && t->t_augment != NULL; rbe = RBE_PARENT(rbe))
t->t_augment(rbt_item(t, rbe));
}
/*
* Replace the subtree rooted at `u' with `v' (CLRS TRANSPLANT).
*/
static void
rbt_transplant(struct rb_entry **root, struct rb_entry *u,
struct rb_entry *v)
{
if (RBE_PARENT(u) == NULL)
*root = v;
else if (u == RBE_LEFT(RBE_PARENT(u)))
RBE_PARENT(u)->rbt_left = v;
else
RBE_PARENT(u)->rbt_right = v;
if (v != NULL)
v->rbt_parent = RBE_PARENT(u);
}
/*
* Replace the subtree rooted at `x' with `y'; y takes x's parent link.
* (CLRS RB-INSERT-FIXUP/DELETE-FIXUP rotations.)
*/
static void
rbt_rotate_left(struct rb_entry **root, struct rb_entry *x)
{
struct rb_entry *y = RBE_RIGHT(x);
x->rbt_right = RBE_LEFT(y);
if (RBE_LEFT(y) != NULL)
RBE_LEFT(y)->rbt_parent = x;
y->rbt_parent = RBE_PARENT(x);
if (RBE_PARENT(x) == NULL)
*root = y;
else if (x == RBE_LEFT(RBE_PARENT(x)))
RBE_PARENT(x)->rbt_left = y;
else
RBE_PARENT(x)->rbt_right = y;
y->rbt_left = x;
x->rbt_parent = y;
}
static void
rbt_rotate_right(struct rb_entry **root, struct rb_entry *x)
{
struct rb_entry *y = RBE_LEFT(x);
x->rbt_left = RBE_RIGHT(y);
if (RBE_RIGHT(y) != NULL)
RBE_RIGHT(y)->rbt_parent = x;
y->rbt_parent = RBE_PARENT(x);
if (RBE_PARENT(x) == NULL)
*root = y;
else if (x == RBE_RIGHT(RBE_PARENT(x)))
RBE_PARENT(x)->rbt_right = y;
else
RBE_PARENT(x)->rbt_left = y;
y->rbt_right = x;
x->rbt_parent = y;
}
void *
_rb_insert(const struct rb_type *t, struct rb_tree *rbt, void *item)
{
struct rb_entry **root = &rbt->rbt_root;
struct rb_entry *rbe = rbt_entry(t, item);
struct rb_entry *current = *root, *parent = NULL;
/* [1] Find the insertion spot. */
while (current != NULL) {
int result = t->t_compare(item, rbt_item(t, current));
parent = current;
if (result < 0)
current = RBE_LEFT(current);
else if (result > 0)
current = RBE_RIGHT(current);
else
return (rbt_item(t, current)); /* duplicate */
}
/* [2] Insert red. */
rbe->rbt_left = NULL;
rbe->rbt_right = NULL;
rbe->rbt_color = RB_RED;
rbe->rbt_parent = parent;
if (parent == NULL)
*root = rbe;
else if (t->t_compare(item, rbt_item(t, parent)) < 0)
parent->rbt_left = rbe;
else
parent->rbt_right = rbe;
/* [3] Fix red-black violations (CLRS RB-INSERT-FIXUP). */
if (parent == NULL)
rbe->rbt_color = RB_BLACK;
else {
struct rb_entry *z = rbe, *p, *g, *u;
while ((p = RBE_PARENT(z)) != NULL &&
RBE_COLOR(p) == RB_RED) {
g = RBE_PARENT(p);
if (p == RBE_LEFT(g)) {
u = RBE_RIGHT(g);
if (u != NULL && RBE_COLOR(u) == RB_RED) {
p->rbt_color = RB_BLACK;
u->rbt_color = RB_BLACK;
g->rbt_color = RB_RED;
z = g;
} else {
if (z == RBE_RIGHT(p)) {
z = p;
rbt_rotate_left(root, p);
p = RBE_PARENT(z);
}
g = RBE_PARENT(p);
p->rbt_color = RB_BLACK;
g->rbt_color = RB_RED;
rbt_rotate_right(root, g);
}
} else {
u = RBE_LEFT(g);
if (u != NULL && RBE_COLOR(u) == RB_RED) {
p->rbt_color = RB_BLACK;
u->rbt_color = RB_BLACK;
g->rbt_color = RB_RED;
z = g;
} else {
if (z == RBE_LEFT(p)) {
z = p;
rbt_rotate_right(root, p);
p = RBE_PARENT(z);
}
g = RBE_PARENT(p);
p->rbt_color = RB_BLACK;
g->rbt_color = RB_RED;
rbt_rotate_left(root, g);
}
}
}
(*root)->rbt_color = RB_BLACK;
}
/* [4] Augment. */
rbt_augment(t, RBE_PARENT(rbe));
return (NULL);
}
/*
* CLRS RB-DELETE-FIXUP with x possibly NULL; x_parent threaded explicitly.
*/
static void
rbt_delete_fixup(struct rb_entry **root, struct rb_entry *x,
struct rb_entry *x_parent)
{
while (x != *root && (x == NULL || RBE_COLOR(x) == RB_BLACK)) {
struct rb_entry *sibling;
if (x_parent == NULL)
break;
if (x == RBE_LEFT(x_parent)) {
sibling = RBE_RIGHT(x_parent);
if (sibling != NULL &&
RBE_COLOR(sibling) == RB_RED) {
sibling->rbt_color = RB_BLACK;
x_parent->rbt_color = RB_RED;
rbt_rotate_left(root, x_parent);
sibling = RBE_RIGHT(x_parent);
}
if (sibling == NULL) {
x = x_parent;
x_parent = RBE_PARENT(x);
continue;
}
if ((RBE_LEFT(sibling) == NULL ||
RBE_COLOR(RBE_LEFT(sibling)) == RB_BLACK) &&
(RBE_RIGHT(sibling) == NULL ||
RBE_COLOR(RBE_RIGHT(sibling)) == RB_BLACK)) {
sibling->rbt_color = RB_RED;
x = x_parent;
x_parent = RBE_PARENT(x);
} else {
if (RBE_RIGHT(sibling) == NULL ||
RBE_COLOR(RBE_RIGHT(sibling)) ==
RB_BLACK) {
if (RBE_LEFT(sibling) != NULL)
RBE_LEFT(sibling)->rbt_color =
RB_BLACK;
sibling->rbt_color = RB_RED;
rbt_rotate_right(root, sibling);
sibling = RBE_RIGHT(x_parent);
}
sibling->rbt_color = RBE_COLOR(x_parent);
x_parent->rbt_color = RB_BLACK;
if (RBE_RIGHT(sibling) != NULL)
RBE_RIGHT(sibling)->rbt_color =
RB_BLACK;
rbt_rotate_left(root, x_parent);
x = *root;
}
} else {
sibling = RBE_LEFT(x_parent);
if (sibling != NULL &&
RBE_COLOR(sibling) == RB_RED) {
sibling->rbt_color = RB_BLACK;
x_parent->rbt_color = RB_RED;
rbt_rotate_right(root, x_parent);
sibling = RBE_LEFT(x_parent);
}
if (sibling == NULL) {
x = x_parent;
x_parent = RBE_PARENT(x);
continue;
}
if ((RBE_RIGHT(sibling) == NULL ||
RBE_COLOR(RBE_RIGHT(sibling)) == RB_BLACK) &&
(RBE_LEFT(sibling) == NULL ||
RBE_COLOR(RBE_LEFT(sibling)) == RB_BLACK)) {
sibling->rbt_color = RB_RED;
x = x_parent;
x_parent = RBE_PARENT(x);
} else {
if (RBE_LEFT(sibling) == NULL ||
RBE_COLOR(RBE_LEFT(sibling)) == RB_BLACK) {
if (RBE_RIGHT(sibling) != NULL)
RBE_RIGHT(sibling)->rbt_color =
RB_BLACK;
sibling->rbt_color = RB_RED;
rbt_rotate_left(root, sibling);
sibling = RBE_LEFT(x_parent);
}
sibling->rbt_color = RBE_COLOR(x_parent);
x_parent->rbt_color = RB_BLACK;
if (RBE_LEFT(sibling) != NULL)
RBE_LEFT(sibling)->rbt_color =
RB_BLACK;
rbt_rotate_right(root, x_parent);
x = *root;
}
}
}
if (x != NULL)
x->rbt_color = RB_BLACK;
}
void *
_rb_remove(const struct rb_type *t, struct rb_tree *rbt, void *item)
{
struct rb_entry **root = &rbt->rbt_root;
struct rb_entry *z = rbt_entry(t, item);
struct rb_entry *y = z, *x, *x_parent;
int y_color;
y_color = RBE_COLOR(y);
if (RBE_LEFT(z) == NULL) { /* [1] no left child */
x = RBE_RIGHT(z);
x_parent = RBE_PARENT(z);
rbt_transplant(root, z, x);
} else if (RBE_RIGHT(z) == NULL) { /* [2] no right child */
x = RBE_LEFT(z);
x_parent = RBE_PARENT(z);
rbt_transplant(root, z, x);
} else { /* [3] two children */
/* in-order successor: leftmost of z's right subtree */
for (y = RBE_RIGHT(z); RBE_LEFT(y) != NULL; y = RBE_LEFT(y))
;
y_color = RBE_COLOR(y);
x = RBE_RIGHT(y);
if (RBE_PARENT(y) == z) {
x_parent = y;
} else {
x_parent = RBE_PARENT(y);
rbt_transplant(root, y, x);
y->rbt_right = RBE_RIGHT(z);
RBE_RIGHT(z)->rbt_parent = y;
}
rbt_transplant(root, z, y);
y->rbt_left = RBE_LEFT(z);
RBE_LEFT(z)->rbt_parent = y;
y->rbt_color = RBE_COLOR(z);
}
/* detach z */
z->rbt_parent = NULL;
z->rbt_left = NULL;
z->rbt_right = NULL;
z->rbt_color = RB_BLACK;
if (y_color == RB_BLACK)
rbt_delete_fixup(root, x, x_parent);
/* augment from the changed point up to the root */
rbt_augment(t, x_parent);
return (item);
}
void *
_rb_find(const struct rb_type *t, struct rb_tree *rbt, const void *item)
{
struct rb_entry *current = rbt->rbt_root;
while (current != NULL) {
int result = t->t_compare(item, rbt_item(t, current));
if (result < 0)
current = RBE_LEFT(current);
else if (result > 0)
current = RBE_RIGHT(current);
else
return (rbt_item(t, current));
}
return (NULL);
}
void *
_rb_nfind(const struct rb_type *t, struct rb_tree *rbt, const void *item)
{
struct rb_entry *current = rbt->rbt_root;
struct rb_entry *best = NULL;
while (current != NULL) {
if (t->t_compare(item, rbt_item(t, current)) <= 0) {
best = current;
current = RBE_LEFT(current);
} else
current = RBE_RIGHT(current);
}
return (best == NULL ? NULL : rbt_item(t, best));
}
void *
_rb_root(const struct rb_type *t, struct rb_tree *rbt)
{
return (rbt_item(t, rbt->rbt_root));
}
void *
_rb_min(const struct rb_type *t, struct rb_tree *rbt)
{
struct rb_entry *current = rbt->rbt_root;
if (current == NULL)
return (NULL);
while (RBE_LEFT(current) != NULL)
current = RBE_LEFT(current);
return (rbt_item(t, current));
}
void *
_rb_max(const struct rb_type *t, struct rb_tree *rbt)
{
struct rb_entry *current = rbt->rbt_root;
if (current == NULL)
return (NULL);
while (RBE_RIGHT(current) != NULL)
current = RBE_RIGHT(current);
return (rbt_item(t, current));
}
void *
_rb_next(const struct rb_type *t, void *item)
{
struct rb_entry *rbe = rbt_entry(t, item);
struct rb_entry *parent;
if (RBE_RIGHT(rbe) != NULL) {
rbe = RBE_RIGHT(rbe);
while (RBE_LEFT(rbe) != NULL)
rbe = RBE_LEFT(rbe);
return (rbt_item(t, rbe));
}
for (parent = RBE_PARENT(rbe); parent != NULL;
parent = RBE_PARENT(parent)) {
if (rbe == RBE_LEFT(parent))
return (rbt_item(t, parent));
rbe = parent;
}
return (NULL);
}
void *
_rb_prev(const struct rb_type *t, void *item)
{
struct rb_entry *rbe = rbt_entry(t, item);
struct rb_entry *parent;
if (RBE_LEFT(rbe) != NULL) {
rbe = RBE_LEFT(rbe);
while (RBE_RIGHT(rbe) != NULL)
rbe = RBE_RIGHT(rbe);
return (rbt_item(t, rbe));
}
for (parent = RBE_PARENT(rbe); parent != NULL;
parent = RBE_PARENT(parent)) {
if (rbe == RBE_RIGHT(parent))
return (rbt_item(t, parent));
rbe = parent;
}
return (NULL);
}
void *
_rb_left(const struct rb_type *t, void *item)
{
return (rbt_item(t, RBE_LEFT(rbt_entry(t, item))));
}
void *
_rb_right(const struct rb_type *t, void *item)
{
return (rbt_item(t, RBE_RIGHT(rbt_entry(t, item))));
}
void *
_rb_parent(const struct rb_type *t, void *item)
{
return (rbt_item(t, RBE_PARENT(rbt_entry(t, item))));
}
void
_rb_set_left(const struct rb_type *t, void *item, void *left)
{
RBE_LEFT(rbt_entry(t, item)) =
left == NULL ? NULL : rbt_entry(t, left);
}
void
_rb_set_right(const struct rb_type *t, void *item, void *right)
{
RBE_RIGHT(rbt_entry(t, item)) =
right == NULL ? NULL : rbt_entry(t, right);
}
void
_rb_set_parent(const struct rb_type *t, void *item, void *parent)
{
RBE_PARENT(rbt_entry(t, item)) =
parent == NULL ? NULL : rbt_entry(t, parent);
}
void
_rb_poison(const struct rb_type *t, void *item, unsigned long poison)
{
struct rb_entry *rbe = rbt_entry(t, item);
rbe->rbt_parent = (void *)poison;
rbe->rbt_left = (void *)poison;
rbe->rbt_right = (void *)poison;
}
int
_rb_check(const struct rb_type *t, void *item, unsigned long poison)
{
struct rb_entry *rbe = rbt_entry(t, item);
if (rbe->rbt_parent == (void *)poison ||
rbe->rbt_left == (void *)poison ||
rbe->rbt_right == (void *)poison) {
errno = EINVAL;
return (-1);
}
return (0);
}
+52
View File
@@ -0,0 +1,52 @@
/* PFLINUX: BSD strtonum(3) for glibc — follows OpenBSD
* lib/libc/stdlib/strtonum.c semantics (public domain).
* Declared in tools/include/pflinux_compat.h. */
#include <sys/types.h>
#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#define INVALID 1
#define TOO_SMALL 2
#define TOO_LARGE 3
long long
strtonum(const char *numstr, long long minval, long long maxval,
const char **errstrp)
{
long long ll = 0;
int error = 0;
struct errval {
const char *errstr;
int err;
} ev[4] = {
{ NULL, 0 },
{ "invalid", EINVAL },
{ "too small", ERANGE },
{ "too large", ERANGE },
};
ev[0].err = errno;
errno = 0;
if (minval > maxval) {
error = INVALID;
} else {
char *str;
ll = strtoll(numstr, &str, 10);
if (numstr == str || str == NULL || *str != '\0')
error = INVALID;
else if ((errno == ERANGE && ll < minval) || ll < minval)
error = TOO_SMALL;
else if ((errno == ERANGE && ll > maxval) || ll > maxval)
error = TOO_LARGE;
}
if (errstrp != NULL)
*errstrp = ev[error].errstr;
errno = ev[error].err;
if (error)
ll = 0;
return (ll);
}
+111
View File
@@ -0,0 +1,111 @@
/* PFLINUX: sysctl(3) emulation for pfctl on Linux.
*
* pfctl uses three OpenBSD MIBs; the legacy Linux sysctl(2) syscall this
* replaces them with (/proc, sysconf) covers exactly those, mirroring the
* semantics the pf.ko port provides:
*
* CTL_KERN/KERN_MAXCLUSTERS mbuf-cluster budget feeding the default
* PF_LIMIT_FRAGS limit (pfctl_init_options).
* CTL_HW/HW_PHYSMEM64 physical memory (tiny-host table limit).
* CTL_NET/PF_ROUTE/NET_RT_TABLE/<id>
* routing-table existence check used by
* parse.y lookup_rtable(). Mirrors
* module/compat/pfk_rtable.c: Linux has no
* per-rtableid FIBs, so every id is accepted
* (ids > RT_TABLEID_MAX never get here:
* callers range-check first, and ENOENT
* answers for completeness).
*/
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/socket.h> /* PF_ROUTE (glibc value, see net/route.h note) */
#include <net/route.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
/*
* Physical memory in bytes via sysconf(3); falls back to sysinfo-ish
* estimate of 0 (callers treat it as "tiny host" path).
*/
static int64_t
pflinux_physmem(void)
{
long pages = sysconf(_SC_PHYS_PAGES);
long psz = sysconf(_SC_PAGESIZE);
if (pages <= 0 || psz <= 0)
return ((int64_t)32 * 1024 * 1024);
return ((int64_t)pages * psz);
}
int
sysctl(const int *name, u_int namelen, void *old, size_t *oldlenp,
const void *new, size_t newlen)
{
(void)new;
(void)newlen;
if (old == NULL || oldlenp == NULL) {
/* read-only emulation: callers always want values */
errno = EINVAL;
return (-1);
}
if (namelen == 2 && name[0] == CTL_HW && name[1] == HW_PHYSMEM64) {
int64_t mem = pflinux_physmem();
if (*oldlenp < sizeof(mem)) {
errno = EINVAL;
return (-1);
}
memcpy(old, &mem, sizeof(mem));
*oldlenp = sizeof(mem);
return (0);
}
if (namelen == 2 && name[0] == CTL_KERN && name[1] == KERN_MAXCLUSTERS) {
/*
* PFLINUX: OpenBSD sizes nmbclusters off RAM (roughly
* mem/MCLBYTES, MCLBYTES==2048); Linux has no mbufs, so we
* hand pfctl the same RAM-derived cluster budget its
* defaults were designed against.
*/
int mcl = (int)(pflinux_physmem() / 2048);
if (*oldlenp < sizeof(mcl)) {
errno = EINVAL;
return (-1);
}
memcpy(old, &mcl, sizeof(mcl));
*oldlenp = sizeof(mcl);
return (0);
}
if (namelen == 6 && name[0] == CTL_NET && name[1] == PF_ROUTE &&
name[4] == NET_RT_TABLE) {
struct rt_tableinfo info;
if (name[5] > RT_TABLEID_MAX) {
errno = ENOENT; /* nonexistent table */
return (-1);
}
memset(&info, 0, sizeof(info));
info.rti_tableid = name[5];
info.rti_domainid = 0;
if (*oldlenp < sizeof(info)) {
errno = EINVAL;
return (-1);
}
memcpy(old, &info, sizeof(info));
*oldlenp = sizeof(info);
return (0);
}
errno = ENOTSUP;
return (-1);
}