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:
1 parent
0119e70960
commit
5323ee2c71
49 files changed
+20028
-28
No files matched your search
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
Reference in new issue
Block a user