Files
pf-linux-port/tools/compat-user/rbtree.c
T
lgcosta 5323ee2c71 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
2026-11-11 15:20:01 -03:00

512 lines
12 KiB
C

/*
* 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);
}