M1: pf.c compiles into pf.ko

This commit is contained in:
lgcosta committed 2026-11-11 14:55:20 -03:00
1 parent 1fcd7b3046
commit 0119e70960
54 files changed
+14853 -96

No files matched your search

+3 -2
View File
@@ -1811,6 +1811,9 @@ deps_compat/md5.o := \
include/compat/net/if_types.h \
include/compat/net/if_var.h \
include/compat/net/ifq.h \
include/compat/net/rtable.h \
include/compat/sys/percpu.h \
include/compat/netinet/in.h \
include/pfk_mbuf.h \
include/pfk_net.h \
include/pfk_if.h \
@@ -1820,13 +1823,11 @@ deps_compat/md5.o := \
include/compat/net/toeplitz.h \
include/net/pfvar.h \
include/compat/sys/syslimits.h \
include/compat/netinet/in.h \
include/compat/net/radix.h \
include/net/pfvar_priv.h \
include/compat/sys/rwlock.h \
include/compat/sys/mutex.h \
include/compat/sys/pclock.h \
include/compat/sys/percpu.h \
compat/md5.o: $(deps_compat/md5.o)
+3 -2
View File
@@ -1811,6 +1811,9 @@ deps_compat/pfk_if.o := \
include/compat/net/if_types.h \
include/compat/net/if_var.h \
include/compat/net/ifq.h \
include/compat/net/rtable.h \
include/compat/sys/percpu.h \
include/compat/netinet/in.h \
include/pfk_mbuf.h \
include/pfk_net.h \
include/pfk_if.h \
@@ -1820,13 +1823,11 @@ deps_compat/pfk_if.o := \
include/compat/net/toeplitz.h \
include/net/pfvar.h \
include/compat/sys/syslimits.h \
include/compat/netinet/in.h \
include/compat/net/radix.h \
include/net/pfvar_priv.h \
include/compat/sys/rwlock.h \
include/compat/sys/mutex.h \
include/compat/sys/pclock.h \
include/compat/sys/percpu.h \
compat/pfk_if.o: $(deps_compat/pfk_if.o)
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+3 -2
View File
@@ -1811,6 +1811,9 @@ deps_compat/pfk_ip6.o := \
include/compat/net/if_types.h \
include/compat/net/if_var.h \
include/compat/net/ifq.h \
include/compat/net/rtable.h \
include/compat/sys/percpu.h \
include/compat/netinet/in.h \
include/pfk_mbuf.h \
include/pfk_net.h \
include/pfk_if.h \
@@ -1820,13 +1823,11 @@ deps_compat/pfk_ip6.o := \
include/compat/net/toeplitz.h \
include/net/pfvar.h \
include/compat/sys/syslimits.h \
include/compat/netinet/in.h \
include/compat/net/radix.h \
include/net/pfvar_priv.h \
include/compat/sys/rwlock.h \
include/compat/sys/mutex.h \
include/compat/sys/pclock.h \
include/compat/sys/percpu.h \
compat/pfk_ip6.o: $(deps_compat/pfk_ip6.o)
+3 -2
View File
@@ -1811,6 +1811,9 @@ deps_compat/pfk_kern.o := \
include/compat/net/if_types.h \
include/compat/net/if_var.h \
include/compat/net/ifq.h \
include/compat/net/rtable.h \
include/compat/sys/percpu.h \
include/compat/netinet/in.h \
include/pfk_mbuf.h \
include/pfk_net.h \
include/pfk_if.h \
@@ -1820,13 +1823,11 @@ deps_compat/pfk_kern.o := \
include/compat/net/toeplitz.h \
include/net/pfvar.h \
include/compat/sys/syslimits.h \
include/compat/netinet/in.h \
include/compat/net/radix.h \
include/net/pfvar_priv.h \
include/compat/sys/rwlock.h \
include/compat/sys/mutex.h \
include/compat/sys/pclock.h \
include/compat/sys/percpu.h \
compat/pfk_kern.o: $(deps_compat/pfk_kern.o)
+3 -2
View File
@@ -1811,6 +1811,9 @@ deps_compat/pfk_mbuf.o := \
include/compat/net/if_types.h \
include/compat/net/if_var.h \
include/compat/net/ifq.h \
include/compat/net/rtable.h \
include/compat/sys/percpu.h \
include/compat/netinet/in.h \
include/pfk_mbuf.h \
include/pfk_net.h \
include/pfk_if.h \
@@ -1820,13 +1823,11 @@ deps_compat/pfk_mbuf.o := \
include/compat/net/toeplitz.h \
include/net/pfvar.h \
include/compat/sys/syslimits.h \
include/compat/netinet/in.h \
include/compat/net/radix.h \
include/net/pfvar_priv.h \
include/compat/sys/rwlock.h \
include/compat/sys/mutex.h \
include/compat/sys/pclock.h \
include/compat/sys/percpu.h \
compat/pfk_mbuf.o: $(deps_compat/pfk_mbuf.o)
File diff suppressed because it is too large. Load diff
+4 -4
View File
@@ -3,6 +3,7 @@ savedcmd_compat/pfk_rtable.o := gcc -Wp,-MMD,compat/.pfk_rtable.o.d -nostdinc -I
source_compat/pfk_rtable.o := compat/pfk_rtable.c
deps_compat/pfk_rtable.o := \
$(wildcard include/config/IPV6) \
/home/lgcosta/lab/linux_kernel/linux-7.3.4/include/linux/compiler-version.h \
$(wildcard include/config/CC_VERSION_TEXT) \
/home/lgcosta/lab/linux_kernel/linux-7.3.4/include/linux/kconfig.h \
@@ -1207,7 +1208,6 @@ deps_compat/pfk_rtable.o := \
$(wildcard include/config/DEBUG_NET_SMALL_RTNL) \
$(wildcard include/config/WIRELESS_EXT) \
$(wildcard include/config/NET_L3_MASTER_DEV) \
$(wildcard include/config/IPV6) \
$(wildcard include/config/TLS_DEVICE) \
$(wildcard include/config/VLAN_8021Q) \
$(wildcard include/config/NET_DSA) \
@@ -1811,6 +1811,9 @@ deps_compat/pfk_rtable.o := \
include/compat/net/if_types.h \
include/compat/net/if_var.h \
include/compat/net/ifq.h \
include/compat/net/rtable.h \
include/compat/sys/percpu.h \
include/compat/netinet/in.h \
include/pfk_mbuf.h \
include/pfk_net.h \
include/pfk_if.h \
@@ -1820,14 +1823,11 @@ deps_compat/pfk_rtable.o := \
include/compat/net/toeplitz.h \
include/net/pfvar.h \
include/compat/sys/syslimits.h \
include/compat/netinet/in.h \
include/compat/net/radix.h \
include/net/pfvar_priv.h \
include/compat/sys/rwlock.h \
include/compat/sys/mutex.h \
include/compat/sys/pclock.h \
include/compat/sys/percpu.h \
include/compat/net/rtable.h \
compat/pfk_rtable.o: $(deps_compat/pfk_rtable.o)
+3 -2
View File
@@ -1811,6 +1811,9 @@ deps_compat/pfk_xmit.o := \
include/compat/net/if_types.h \
include/compat/net/if_var.h \
include/compat/net/ifq.h \
include/compat/net/rtable.h \
include/compat/sys/percpu.h \
include/compat/netinet/in.h \
include/pfk_mbuf.h \
include/pfk_net.h \
include/pfk_if.h \
@@ -1820,13 +1823,11 @@ deps_compat/pfk_xmit.o := \
include/compat/net/toeplitz.h \
include/net/pfvar.h \
include/compat/sys/syslimits.h \
include/compat/netinet/in.h \
include/compat/net/radix.h \
include/net/pfvar_priv.h \
include/compat/sys/rwlock.h \
include/compat/sys/mutex.h \
include/compat/sys/pclock.h \
include/compat/sys/percpu.h \
compat/pfk_xmit.o: $(deps_compat/pfk_xmit.o)
+5 -3
View File
@@ -1,4 +1,4 @@
savedcmd_compat/radix.o := gcc -Wp,-MMD,compat/.radix.o.d -nostdinc -I/home/lgcosta/lab/linux_kernel/linux-7.3.4/arch/x86/include -I/home/lgcosta/lab/linux_kernel/linux-7.3.4/arch/x86/include/generated -I/home/lgcosta/lab/linux_kernel/linux-7.3.4/include -I/home/lgcosta/lab/linux_kernel/linux-7.3.4/include -I/home/lgcosta/lab/linux_kernel/linux-7.3.4/arch/x86/include/uapi -I/home/lgcosta/lab/linux_kernel/linux-7.3.4/arch/x86/include/generated/uapi -I/home/lgcosta/lab/linux_kernel/linux-7.3.4/include/uapi -I/home/lgcosta/lab/linux_kernel/linux-7.3.4/include/generated/uapi -include /home/lgcosta/lab/linux_kernel/linux-7.3.4/include/linux/compiler-version.h -include /home/lgcosta/lab/linux_kernel/linux-7.3.4/include/linux/kconfig.h -include /home/lgcosta/lab/linux_kernel/linux-7.3.4/include/linux/compiler_types.h -D__KERNEL__ -Werror -fshort-wchar -funsigned-char -fno-common -fno-PIE -fno-strict-aliasing -std=gnu11 -fms-extensions -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -mno-sse4a -fcf-protection=branch -fno-jump-tables -m64 -falign-jumps=1 -falign-loops=1 -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -march=x86-64 -mtune=generic -mno-red-zone -mcmodel=kernel -mstack-protector-guard-reg=gs -mstack-protector-guard-symbol=__ref_stack_chk_guard -Wno-sign-compare -fno-asynchronous-unwind-tables -mindirect-branch=thunk-extern -mindirect-branch-register -mindirect-branch-cs-prefix -mfunction-return=thunk-extern -fno-jump-tables -fpatchable-function-entry=16,16 -fno-delete-null-pointer-checks -O2 -fno-allow-store-data-races -fstack-protector-strong -fomit-frame-pointer -ftrivial-auto-var-init=zero -fzero-init-padding-bits=all -fno-stack-clash-protection -fdiagnostics-show-context=2 -fmin-function-alignment=16 -fstrict-flex-arrays=3 -fno-strict-overflow -fno-stack-check -fconserve-stack -fno-builtin-wcslen -Wall -Wextra -Wundef -Werror=implicit-function-declaration -Werror=implicit-int -Werror=return-type -Werror=strict-prototypes -Wno-format-security -Wno-trigraphs -Wno-frame-address -Wno-address-of-packed-member -Wmissing-declarations -Wmissing-prototypes -Wframe-larger-than=2048 -Wno-main -Wno-type-limits -Wno-dangling-pointer -Wvla-larger-than=1 -Wno-pointer-sign -Wcast-function-type -Wno-unterminated-string-initialization -Wno-array-bounds -Wno-stringop-overflow -Wno-alloc-size-larger-than -Wimplicit-fallthrough=5 -Werror=date-time -Werror=incompatible-pointer-types -Werror=designated-init -Wenum-conversion -Wunused -Wno-unused-but-set-variable -Wno-unused-const-variable -Wno-packed-not-aligned -Wno-format-overflow -Wno-format-truncation -Wno-stringop-truncation -Wno-override-init -Wno-missing-field-initializers -Wno-shift-negative-value -Wno-maybe-uninitialized -Wno-sign-compare -Wno-unused-parameter -I././include -I././include/compat -DPFLINUX=1 -D_KERNEL=1 -Wno-declaration-after-statement -DMODULE -DKBUILD_BASENAME='"radix"' -DKBUILD_MODNAME='"pf"' -D__KBUILD_MODNAME=pf -c -o compat/radix.o compat/radix.c
savedcmd_compat/radix.o := gcc -Wp,-MMD,compat/.radix.o.d -nostdinc -I/home/lgcosta/lab/linux_kernel/linux-7.3.4/arch/x86/include -I/home/lgcosta/lab/linux_kernel/linux-7.3.4/arch/x86/include/generated -I/home/lgcosta/lab/linux_kernel/linux-7.3.4/include -I/home/lgcosta/lab/linux_kernel/linux-7.3.4/include -I/home/lgcosta/lab/linux_kernel/linux-7.3.4/arch/x86/include/uapi -I/home/lgcosta/lab/linux_kernel/linux-7.3.4/arch/x86/include/generated/uapi -I/home/lgcosta/lab/linux_kernel/linux-7.3.4/include/uapi -I/home/lgcosta/lab/linux_kernel/linux-7.3.4/include/generated/uapi -include /home/lgcosta/lab/linux_kernel/linux-7.3.4/include/linux/compiler-version.h -include /home/lgcosta/lab/linux_kernel/linux-7.3.4/include/linux/kconfig.h -include /home/lgcosta/lab/linux_kernel/linux-7.3.4/include/linux/compiler_types.h -D__KERNEL__ -Werror -fshort-wchar -funsigned-char -fno-common -fno-PIE -fno-strict-aliasing -std=gnu11 -fms-extensions -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -mno-sse4a -fcf-protection=branch -fno-jump-tables -m64 -falign-jumps=1 -falign-loops=1 -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -march=x86-64 -mtune=generic -mno-red-zone -mcmodel=kernel -mstack-protector-guard-reg=gs -mstack-protector-guard-symbol=__ref_stack_chk_guard -Wno-sign-compare -fno-asynchronous-unwind-tables -mindirect-branch=thunk-extern -mindirect-branch-register -mindirect-branch-cs-prefix -mfunction-return=thunk-extern -fno-jump-tables -fpatchable-function-entry=16,16 -fno-delete-null-pointer-checks -O2 -fno-allow-store-data-races -fstack-protector-strong -fomit-frame-pointer -ftrivial-auto-var-init=zero -fzero-init-padding-bits=all -fno-stack-clash-protection -fdiagnostics-show-context=2 -fmin-function-alignment=16 -fstrict-flex-arrays=3 -fno-strict-overflow -fno-stack-check -fconserve-stack -fno-builtin-wcslen -Wall -Wextra -Wundef -Werror=implicit-function-declaration -Werror=implicit-int -Werror=return-type -Werror=strict-prototypes -Wno-format-security -Wno-trigraphs -Wno-frame-address -Wno-address-of-packed-member -Wmissing-declarations -Wmissing-prototypes -Wframe-larger-than=2048 -Wno-main -Wno-type-limits -Wno-dangling-pointer -Wvla-larger-than=1 -Wno-pointer-sign -Wcast-function-type -Wno-unterminated-string-initialization -Wno-array-bounds -Wno-stringop-overflow -Wno-alloc-size-larger-than -Wimplicit-fallthrough=5 -Werror=date-time -Werror=incompatible-pointer-types -Werror=designated-init -Wenum-conversion -Wunused -Wno-unused-but-set-variable -Wno-unused-const-variable -Wno-packed-not-aligned -Wno-format-overflow -Wno-format-truncation -Wno-stringop-truncation -Wno-override-init -Wno-missing-field-initializers -Wno-shift-negative-value -Wno-maybe-uninitialized -Wno-sign-compare -Wno-unused-parameter -I././include -I././include/compat -DPFLINUX=1 -D_KERNEL=1 -Wno-declaration-after-statement -Wno-implicit-fallthrough -Wno-frame-larger-than -DMODULE -DKBUILD_BASENAME='"radix"' -DKBUILD_MODNAME='"pf"' -D__KBUILD_MODNAME=pf -c -o compat/radix.o compat/radix.c
source_compat/radix.o := compat/radix.c
@@ -1810,6 +1810,10 @@ deps_compat/radix.o := \
include/compat/sys/socket.h \
include/compat/net/if_types.h \
include/compat/net/if_var.h \
include/compat/net/ifq.h \
include/compat/net/rtable.h \
include/compat/sys/percpu.h \
include/compat/netinet/in.h \
include/pfk_mbuf.h \
include/pfk_net.h \
include/pfk_if.h \
@@ -1819,13 +1823,11 @@ deps_compat/radix.o := \
include/compat/net/toeplitz.h \
include/net/pfvar.h \
include/compat/sys/syslimits.h \
include/compat/netinet/in.h \
include/compat/net/radix.h \
include/net/pfvar_priv.h \
include/compat/sys/rwlock.h \
include/compat/sys/mutex.h \
include/compat/sys/pclock.h \
include/compat/sys/percpu.h \
compat/radix.o: $(deps_compat/radix.o)
+3 -2
View File
@@ -1811,6 +1811,9 @@ deps_compat/sha2.o := \
include/compat/net/if_types.h \
include/compat/net/if_var.h \
include/compat/net/ifq.h \
include/compat/net/rtable.h \
include/compat/sys/percpu.h \
include/compat/netinet/in.h \
include/pfk_mbuf.h \
include/pfk_net.h \
include/pfk_if.h \
@@ -1820,13 +1823,11 @@ deps_compat/sha2.o := \
include/compat/net/toeplitz.h \
include/net/pfvar.h \
include/compat/sys/syslimits.h \
include/compat/netinet/in.h \
include/compat/net/radix.h \
include/net/pfvar_priv.h \
include/compat/sys/rwlock.h \
include/compat/sys/mutex.h \
include/compat/sys/pclock.h \
include/compat/sys/percpu.h \
compat/sha2.o: $(deps_compat/sha2.o)
+3 -2
View File
@@ -1811,6 +1811,9 @@ deps_compat/siphash.o := \
include/compat/net/if_types.h \
include/compat/net/if_var.h \
include/compat/net/ifq.h \
include/compat/net/rtable.h \
include/compat/sys/percpu.h \
include/compat/netinet/in.h \
include/pfk_mbuf.h \
include/pfk_net.h \
include/pfk_if.h \
@@ -1820,13 +1823,11 @@ deps_compat/siphash.o := \
include/compat/net/toeplitz.h \
include/net/pfvar.h \
include/compat/sys/syslimits.h \
include/compat/netinet/in.h \
include/compat/net/radix.h \
include/net/pfvar_priv.h \
include/compat/sys/rwlock.h \
include/compat/sys/mutex.h \
include/compat/sys/pclock.h \
include/compat/sys/percpu.h \
compat/siphash.o: $(deps_compat/siphash.o)
File diff suppressed because it is too large. Load diff
+3 -2
View File
@@ -1811,6 +1811,9 @@ deps_compat/toeplitz.o := \
include/compat/net/if_types.h \
include/compat/net/if_var.h \
include/compat/net/ifq.h \
include/compat/net/rtable.h \
include/compat/sys/percpu.h \
include/compat/netinet/in.h \
include/pfk_mbuf.h \
include/pfk_net.h \
include/pfk_if.h \
@@ -1820,13 +1823,11 @@ deps_compat/toeplitz.o := \
include/compat/net/toeplitz.h \
include/net/pfvar.h \
include/compat/sys/syslimits.h \
include/compat/netinet/in.h \
include/compat/net/radix.h \
include/net/pfvar_priv.h \
include/compat/sys/rwlock.h \
include/compat/sys/mutex.h \
include/compat/sys/pclock.h \
include/compat/sys/percpu.h \
/home/lgcosta/lab/linux_kernel/linux-7.3.4/include/crypto/hash.h \
/home/lgcosta/lab/linux_kernel/linux-7.3.4/include/linux/crypto.h \
+32
View File
@@ -30,6 +30,11 @@ pfk_ifnet_new(struct net_device *dev)
ifp->dev = dev;
ifp->alive = 1;
INIT_LIST_HEAD(&ifp->link);
/* BSD TAILQ heads are self-referential when empty (tqh_last points
* at &tqh_first); kzalloc leaves tqh_last == NULL, which the first
* TAILQ_INSERT_TAIL would dereference. */
TAILQ_INIT(&ifp->if_addrlist);
TAILQ_INIT(&ifp->if_groups);
ifp->if_type = IFT_OTHER;
switch (dev->type) {
case ARPHRD_ETHER: ifp->if_type = IFT_ETHER; break;
@@ -64,10 +69,37 @@ pfk_if_refresh(struct pfk_ifnet *ifp)
{
ifp->if_mtu = ifp->dev->mtu;
ifp->if_flags = pfk_dev_flags_bsd(ifp->dev);
if (ifp->dev->hard_header_len > max_linkhdr)
max_linkhdr = ifp->dev->hard_header_len;
/* PFLINUX: sync the BSD sysctl mirrors on device events */
pfk_sysctl_sync();
/* address snapshot: rebuild list from Linux in_ifaddr/inet6_ifaddr */
pfk_if_addrlist_refresh(ifp);
}
/*
* OpenBSD if_get/if_put: registry entries are refcounted there; the port's
* pfk_ifnet objects are freed via call_rcu on UNREGISTER, so RCU/NET_LOCK
* readers can use them without an extra count (PFLINUX: if_put is a no-op).
*/
struct pfk_ifnet *
if_get(int ifidx)
{
return pfk_ifp_byindex(ifidx);
}
struct pfk_ifnet *
if_get_smr(int ifidx)
{
return pfk_ifp_byindex(ifidx);
}
void
if_put(struct pfk_ifnet *ifp)
{
(void)ifp;
}
struct pfk_ifnet *
pfk_ifp_byindex(int idx)
{
+19 -2
View File
@@ -162,7 +162,7 @@ struct pfk_pfq {
};
static void *
pfk_pfq_alloc(struct ifnet *ifp)
pfk_pfq_alloc(struct pfk_ifnet *ifp)
{
struct pfk_pfq *pfq = kzalloc(sizeof(*pfq), GFP_KERNEL);
@@ -280,7 +280,7 @@ const struct pfq_ops * const pfq_fqcodel_ops = &pfk_priq_pfq_ops;
/* ---------------- framework ---------------- */
void
ifq_init(struct ifqueue *ifq, struct ifnet *ifp, unsigned int maxlen)
ifq_init(struct ifqueue *ifq, struct pfk_ifnet *ifp, unsigned int maxlen)
{
memset(ifq, 0, sizeof(*ifq));
ifq->ifq_if = ifp;
@@ -422,3 +422,20 @@ ifq_hdatalen(struct ifqueue *ifq)
(void)ifq;
return 0;
}
/* ---------------- scheduler back ends (milestone M5) ---------------- */
/* The staged OpenBSD hfsc.c/fq_codel.c are not in the build yet; the priq
* ops above are the active scheduler. These wrappers keep pf_ioctl's
* initialisation calls valid until M5 replaces them with the ported
* schedulers (docs/porting-notes.md). */
void
hfsc_initialize(void)
{
pr_info("pfk: hfsc back end pending M5 (priq active)\n");
}
void
fq_codel_initialize(void)
{
pr_info("pfk: fq_codel back end pending M5 (priq active)\n");
}
+540
View File
@@ -0,0 +1,540 @@
/*
* PF-on-Linux BSD IPv4/IPv6 layer used by pf.c (ISC 2026).
*
* OpenBSD's netinet entry points that PF calls directly for the AFRT
* (64<->4 translation), route-to, RST/error-return and state-ICMP paths.
* The staging seds rename ip_output/ip_forward/ip6_output to pfk_* (they
* clash with Linux declarations); ip_send/ip6_send/icmp_error/ip_fragment
* have no Linux declaration and keep their BSD names.
*
* All of these CONSUME the mbuf (OpenBSD semantics), matching the callers
* in pf.c which set pd->m = NULL right after.
*/
#include <pfk.h>
static struct rtable *
pfk_ip4_route(__be32 daddr, __be32 saddr)
{
struct flowi4 fl4 = { };
struct rtable *rt;
fl4.daddr = daddr;
fl4.saddr = saddr;
rt = ip_route_output_key(&init_net, &fl4);
if (IS_ERR(rt))
return NULL;
return rt;
}
/* one's complement sum over an mbuf range (shim: linear sk_buff) */
u_int16_t
in_cksum(struct mbuf *m, int len)
{
struct sk_buff *skb = m_to_skb(m);
if (!skb || !pskb_may_pull(skb, len))
return 0xffff;
return pfk_in_cksum(skb->data, len);
}
/*
* in4_cksum(m, nxt, off, len): transport checksum including the IPv4
* pseudo-header. off/len are relative to the packet start (the IP hdr).
* Returns the one's complement sum; a valid packet computes to 0, and a
* value to be STORED is (sum == 0 ? 0xffff : ~sum) style — callers do
* their own fixups; we return the raw folded sum like OpenBSD.
*/
u_int16_t
in4_cksum(struct mbuf *m, __u8 nxt, u_int32_t off, u_int32_t len)
{
struct sk_buff *skb = m_to_skb(m);
struct pf_ip *ip;
struct {
__be32 src, dst;
__u8 zero, proto;
__be16 len;
} ph;
__wsum base;
if (!skb || !pskb_may_pull(skb, off + len) ||
!pskb_may_pull(skb, sizeof(*ip)))
return 0xffff;
ip = (struct pf_ip *)skb->data;
ph.src = ip->ip_src.s_addr;
ph.dst = ip->ip_dst.s_addr;
ph.zero = 0;
ph.proto = nxt;
ph.len = htons(len);
base = csum_partial(&ph, sizeof(ph), 0);
return csum_fold(csum_partial(skb->data + off, len, base));
}
u_int16_t
in6_cksum(struct mbuf *m, __u8 nxt, u_int32_t off, u_int32_t len)
{
struct sk_buff *skb = m_to_skb(m);
struct pf_ip6_hdr *ip6;
__wsum base;
if (!skb || !pskb_may_pull(skb, sizeof(*ip6)) ||
!pskb_may_pull(skb, off + len))
return 0xffff;
ip6 = (struct pf_ip6_hdr *)skb->data;
base = csum_partial(ip6->ip6_src.s6_addr, sizeof(ip6->ip6_src), 0);
base = csum_add(base, csum_partial(ip6->ip6_dst.s6_addr,
sizeof(ip6->ip6_dst), 0));
base = csum_add(base, (__force __u32)htonl(len));
base = csum_add(base, (__force __u32)htonl(nxt));
return csum_fold(csum_partial(skb->data + off, len, base));
}
/* recompute the IPv4 header checksum in place */
void
in_hdr_cksum_out(struct mbuf *m, void *ro)
{
struct sk_buff *skb = m_to_skb(m);
struct pf_ip *ip;
int hlen;
(void)ro;
if (!skb || !pskb_may_pull(skb, sizeof(*ip)))
return;
ip = (struct pf_ip *)skb->data;
hlen = ip->ip_hl << 2;
if (hlen < (int)sizeof(*ip) || !pskb_may_pull(skb, hlen))
return;
ip->ip_sum = 0;
ip->ip_sum = csum_fold(csum_partial(skb->data, hlen, 0));
pfk_mbuf_refresh(m);
}
/* compute a pending transport checksum requested via csum_flags */
static void
pfk_proto_cksum_out(struct mbuf *m)
{
struct sk_buff *skb = m_to_skb(m);
struct pf_ip *ip;
int hlen;
if (!skb || !(m->m_pkthdr.csum_flags &
(M_TCP_CSUM_OUT | M_UDP_CSUM_OUT)))
return;
if (!pskb_may_pull(skb, sizeof(*ip)))
return;
ip = (struct pf_ip *)skb->data;
hlen = ip->ip_hl << 2;
if (ip->ip_p == IPPROTO_TCP) {
__be16 *sum;
u_int16_t s;
if (!pskb_may_pull(skb, hlen + 20))
return;
sum = (__be16 *)(skb->data + hlen + 16); /* th_sum */
*sum = 0;
s = in4_cksum(m, IPPROTO_TCP, hlen, ntohs(ip->ip_len) - hlen);
*sum = s == 0 ? (__force __be16)0xffff : (__force __be16)~s;
} else if (ip->ip_p == IPPROTO_UDP) {
__be16 *sum;
u_int16_t s;
if (!pskb_may_pull(skb, hlen + 8))
return;
sum = (__be16 *)(skb->data + hlen + 6); /* uh_sum */
*sum = 0;
s = in4_cksum(m, IPPROTO_UDP, hlen, ntohs(ip->ip_len) - hlen);
if (s == 0)
s = 0xffff; /* RFC 768: zero means "unused" */
*sum = (__force __be16)s;
}
pfk_mbuf_refresh(m);
}
/*
* pfk_ip_output(m, opt, ro, flags, imcb, so, flowid) — BSD ip_output().
* PFLINUX: option processing (opt/record-route), IPsec metadata (imcb) and
* socket accounting (so) are not modelled; flags IP_FORWARDING /
* IP_ALLOWBROADCAST / IP_REDIRECT / *_IPSEC are accepted. The packet is
* routed by its own destination header and injected into the stack output
* path (exactly what OpenBSD's ip_output ends up doing at if_output).
* Consumes m. Returns 0 or errno.
*/
int
pfk_ip_output(struct mbuf *m, void *opt, void *ro, u_int flags, void *imcb,
void *so, u_int flowid)
{
struct sk_buff *skb = m ? m_to_skb(m) : NULL;
struct pf_ip *ip;
struct rtable *rt;
(void)opt; (void)ro; (void)flags; (void)imcb; (void)so; (void)flowid;
if (!skb)
return EINVAL;
if (!pskb_may_pull(skb, sizeof(*ip))) {
m_freem(m);
return EMSGSIZE;
}
ip = (struct pf_ip *)skb->data;
pfk_proto_cksum_out(m);
in_hdr_cksum_out(m, NULL);
skb_reset_network_header(skb);
skb->protocol = htons(ETH_P_IP);
rt = pfk_ip4_route(ip->ip_dst.s_addr, ip->ip_src.s_addr);
if (!rt) {
/* BSD: no route while forwarding → ICMP host unreachable */
if (flags & IP_FORWARDING) {
/* re-derive a mutable copy for quoting */
struct sk_buff *q;
q = skb_copy(skb, GFP_ATOMIC);
if (q) {
icmp_error(m_dtom(q), ICMP_UNREACH,
ICMP_UNREACH_HOST, 0, 0);
}
} else {
struct sk_buff *q = skb_copy(skb, GFP_ATOMIC);
if (q)
icmp_error(m_dtom(q), ICMP_UNREACH,
ICMP_UNREACH_HOST, 0, 0);
}
m_freem(m);
pfk_if_congestion();
return EHOSTUNREACH;
}
skb_dst_set(skb, &rt->dst);
skb->dev = rt->dst.dev;
if (!netif_running(skb->dev)) {
m->skb = NULL;
m_freem(m);
kfree_skb(skb);
return ENETDOWN;
}
m->skb = NULL; /* output consumes the skb */
m_freem(m);
skb_dst(skb)->output(&init_net, NULL, skb);
return 0;
}
/*
* ip_send(m): route + transmit a complete IPv4 packet to its own
* destination (OpenBSD: used by PF to send RST/error packets).
* Consumes m.
*/
void
ip_send(struct mbuf *m)
{
if (!m)
return;
(void)pfk_ip_output(m, NULL, NULL, 0, NULL, NULL, 0);
}
/*
* pfk_ip_forward(m, ifp, tag, flags) — BSD ip_forward(): ttl-- , recheck,
* forward toward the packet's destination.
* PFLINUX: ICMP redirect generation (IP_REDIRECT flag) is NOT implemented;
* the port lets Linux routing emit redirects. IP_FORWARDING_IPSEC has no
* meaning in the port (no kernel IPsec coupling) and is ignored.
* Consumes m.
*/
void
pfk_ip_forward(struct mbuf *m, struct pfk_ifnet *ifp, void *tag, u_int flags)
{
struct sk_buff *skb = m ? m_to_skb(m) : NULL;
struct pf_ip *ip;
(void)ifp; (void)tag;
if (!skb)
return;
if (!pskb_may_pull(skb, sizeof(*ip))) {
m_freem(m);
return;
}
ip = (struct pf_ip *)skb->data;
if (skb_ensure_writable(skb, sizeof(*ip))) {
m_freem(m);
return;
}
ip = (struct pf_ip *)skb->data;
if (ip->ip_ttl <= IPTTLDEC) {
struct sk_buff *q = skb_copy(skb, GFP_ATOMIC);
if (q)
icmp_error(m_dtom(q), ICMP_TIMXCEED,
ICMP_TIMXCEED_INTRANS, 0, 0);
m_freem(m);
return;
}
ip->ip_ttl -= IPTTLDEC;
in_hdr_cksum_out(m, NULL);
(void)pfk_ip_output(m, NULL, NULL, flags | IP_FORWARDING, NULL, NULL,
0);
}
/*
* ip_fragment(m, ml, ifp, mtu): split a too-large IPv4 packet into
* MTU-sized fragments appended to ml; consumes m (OpenBSD semantics:
* always consumed). Returns 0 or errno.
*/
int
ip_fragment(struct mbuf *m, struct mbuf_list *ml, struct pfk_ifnet *ifp,
int mtu)
{
struct pf_ip *ip;
int hlen, len, dlen, flen, off, error = 0;
__be16 ipid;
int maxfacilen;
if (!m || !ml)
return EINVAL;
ip = mtod(m, struct pf_ip *);
if (m->m_len < (int)sizeof(*ip)) {
m_freem(m);
return EMSGSIZE;
}
hlen = ip->ip_hl << 2;
len = ntohs(ip->ip_len);
if (len <= mtu) {
/* single piece */
if (ml->ml_last)
ml->ml_last->m_next = m;
else
ml->ml_first = m;
ml->ml_last = m;
ml->ml_len++;
return 0;
}
dlen = len - hlen;
flen = (mtu - hlen) & ~7;
if (flen < 8) {
m_freem(m);
return EMSGSIZE;
}
ipid = ip->ip_id;
maxfacilen = dlen;
off = 0;
while (off < maxfacilen) {
struct mbuf *fm;
struct pf_ip *fi;
int thislen = maxfacilen - off;
int totlen;
if (thislen > flen)
thislen = flen;
fm = m_gethdr(M_NOWAIT, MT_HEADER);
if (!fm) {
error = ENOBUFS;
break;
}
totlen = hlen + thislen;
if (skb_put(fm->skb, totlen) == NULL) {
m_freem(fm);
error = ENOBUFS;
break;
}
memcpy(fm->m_data, m->m_data, hlen);
memcpy(fm->m_data + hlen, m->m_data + hlen + off, thislen);
fi = mtod(fm, struct pf_ip *);
fi->ip_len = htons(totlen);
fi->ip_id = ipid;
fi->ip_off = htons(off >> 3);
if (off + thislen < maxfacilen)
fi->ip_off |= htons(IP_MF);
fi->ip_sum = 0;
pfk_mbuf_refresh(fm);
in_hdr_cksum_out(fm, NULL);
if (ml->ml_last)
ml->ml_last->m_next = fm;
else
ml->ml_first = fm;
ml->ml_last = fm;
ml->ml_len++;
off += thislen;
}
m_freem(m);
return error;
}
/*
* icmp_error(n, type, code, dest, mtu): build and send an ICMP error
* quoting the offending packet n; consumes n (OpenBSD semantics).
*/
void
icmp_error(struct mbuf *n, u_int type, u_int code, u_int dest, u_int mtu)
{
struct sk_buff *skb = n ? m_to_skb(n) : NULL;
struct pf_ip *oip;
struct mbuf *m;
struct pf_ip *ip;
struct pf_icmp *ic;
int hlen, qlen, len;
__be32 rsaddr;
if (!skb || !pskb_may_pull(skb, sizeof(*oip))) {
if (n)
m_freem(n);
return;
}
oip = (struct pf_ip *)skb->data;
hlen = oip->ip_hl << 2;
/* quote: ip header + at least 8 bytes, fit into RFC 792 minimum */
qlen = min_t(int, ntohs(oip->ip_len), 576 - 20 - 8);
if (qlen < hlen + 8)
qlen = min(hlen + 8, (int)skb->len);
len = 20 + 8 + qlen;
m = m_gethdr(M_DONTWAIT, MT_HEADER);
if (!m) {
m_freem(n);
return;
}
if (skb_put(m->skb, len) == NULL) {
m_freem(m);
m_freem(n);
return;
}
ip = mtod(m, struct pf_ip *);
ic = (struct pf_icmp *)(ip + 1);
memset(ip, 0, 20);
ip->ip_v = 4;
ip->ip_hl = 5;
ip->ip_len = htons(len);
ip->ip_id = ip_randomid();
ip->ip_off = htons(atomic_load_int(&ip_mtudisc) ? IP_DF : 0);
ip->ip_ttl = atomic_load_int(&ip_defttl);
ip->ip_p = IPPROTO_ICMP;
ip->ip_dst = oip->ip_src; /* answer to the offender */
/* source: address of our route egress interface (mirrors BSD,
* which fills ip_src from the route/interface). Linux 7.3's
* struct rtable no longer caches rt_src, so consult the PF
* interface registry for the device's first IPv4 address. */
{
struct rtable *rt = pfk_ip4_route(oip->ip_src.s_addr, 0);
struct pfk_ifnet *ifp;
rsaddr = 0;
if (rt) {
ifp = rt->dst.dev ?
pfk_ifp_byindex(rt->dst.dev->ifindex) : NULL;
if (ifp) {
struct ifaddr *ifa;
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr &&
ifa->ifa_addr->sa_family == AF_INET) {
rsaddr = sintosa(ifa->ifa_addr)->sin_addr.s_addr;
break;
}
}
}
dst_release(&rt->dst);
}
}
ip->ip_src.s_addr = rsaddr;
ic->icmp_type = type;
ic->icmp_code = code;
ic->icmp_cksum = 0;
if (type == ICMP_UNREACH && code == ICMP_UNREACH_NEEDFRAG) {
ic->icmp_nextmtu = htons(((__u16)mtu));
} else {
ic->icmp_void = 0;
if (dest)
ic->icmp_gwaddr = (__force __be32)dest;
}
memcpy(ic + 1, (char *)oip, qlen);
{
__wsum w = csum_partial(ic, 8 + qlen, 0);
ic->icmp_cksum = csum_fold(w);
}
m->m_pkthdr.len = m->m_len = len;
pfk_mbuf_refresh(m);
m->m_pkthdr.pf.flags |= PF_TAG_GENERATED;
if (n)
m_freem(n);
ip_send(m);
}
/*
* pfk_ip6_output(m, opt, ro, flags, so, flowid) — BSD ip6_output() for
* IPv6 packets with a complete header. PFLINUX: same reductions as
* pfk_ip_output; consumes m; returns 0/errno.
*/
int
pfk_ip6_output(struct mbuf *m, void *opt, void *ro, u_int flags, void *so,
void *flowid)
{
struct sk_buff *skb = m ? m_to_skb(m) : NULL;
struct pf_ip6_hdr *ip6;
struct flowi6 fl6 = { };
struct dst_entry *d;
(void)opt; (void)ro; (void)flags; (void)so; (void)flowid;
if (!skb)
return EINVAL;
if (!pskb_may_pull(skb, sizeof(*ip6))) {
m_freem(m);
return EMSGSIZE;
}
ip6 = (struct pf_ip6_hdr *)skb->data;
if (m->m_pkthdr.csum_flags & (M_TCP_CSUM_OUT | M_UDP_CSUM_OUT)) {
int plen = ntohs(ip6->ip6_plen);
if (ip6->ip6_nxt == IPPROTO_TCP && plen >= 20) {
__be16 *sum = (__be16 *)(skb->data + sizeof(*ip6) + 16);
*sum = 0;
*sum = (__force __be16)in6_cksum(m, IPPROTO_TCP,
sizeof(*ip6), plen);
} else if (ip6->ip6_nxt == IPPROTO_UDP && plen >= 8) {
__be16 *sum = (__be16 *)(skb->data + sizeof(*ip6) + 6);
u_int16_t s = in6_cksum(m, IPPROTO_UDP, sizeof(*ip6),
plen);
*sum = s == 0 ? 0xffff : (__force __be16)s;
}
}
skb_reset_network_header(skb);
skb->protocol = htons(ETH_P_IPV6);
fl6.daddr = ip6->ip6_dst;
fl6.saddr = ip6->ip6_src;
d = ip6_route_output(&init_net, NULL, &fl6);
if (!d || d->error) {
if (d)
dst_release(d);
m_freem(m);
pfk_if_congestion();
return EHOSTUNREACH;
}
skb_dst_set(skb, d);
skb->dev = d->dev;
if (!netif_running(skb->dev)) {
m->skb = NULL;
m_freem(m);
kfree_skb(skb);
return ENETDOWN;
}
m->skb = NULL;
m_freem(m);
skb_dst(skb)->output(&init_net, NULL, skb);
return 0;
}
/* ip6_send(m): BSD convenience — output an IPv6 packet to its dst */
void
ip6_send(struct mbuf *m)
{
if (!m)
return;
(void)pfk_ip6_output(m, NULL, NULL, 0, NULL, 0);
}
+176 -1
View File
@@ -284,7 +284,7 @@ pfk_timeout_fire(struct timer_list *tl)
void
timeout_set(struct timeout *to, timeout_fn *fn, void *arg)
{
if (!to->fn) { /* one-time setup */
if (!to->timer.function) { /* one-time setup */
timer_setup(&to->timer, pfk_timeout_fire, 0);
INIT_WORK(&to->work, pfk_timeout_work);
to->period_ms = -1;
@@ -299,9 +299,22 @@ timeout_set_micro(struct timeout *to, timeout_fn *fn, void *arg)
timeout_set(to, fn, arg);
}
/* OpenBSD TIMEOUT_INITIALIZER() statics never see timeout_set(): lazily
* complete the Linux timer/work plumbing on the first use (PFLINUX). */
static inline void
pfk_timeout_ensure(struct timeout *to)
{
if (!to->timer.function) {
timer_setup(&to->timer, pfk_timeout_fire, 0);
INIT_WORK(&to->work, pfk_timeout_work);
to->period_ms = -1;
}
}
int
timeout_add(struct timeout *to, long ms)
{
pfk_timeout_ensure(to);
if (ms < 0)
ms = 0;
to->pending = 1;
@@ -309,6 +322,12 @@ timeout_add(struct timeout *to, long ms)
return mod_timer(&to->timer, to->deadline_jif);
}
int
timeout_add_msec(struct timeout *to, int msecs)
{
return timeout_add(to, msecs);
}
int
timeout_add_sec(struct timeout *to, int secs)
{
@@ -331,6 +350,8 @@ int
timeout_del(struct timeout *to)
{
to->pending = 0;
if (!to->timer.function)
return 0;
return timer_delete(&to->timer);
}
@@ -338,6 +359,8 @@ void
timeout_del_sync(struct timeout *to)
{
to->pending = 0;
if (!to->timer.function)
return;
timer_delete_sync(&to->timer);
cancel_work_sync(&to->work);
}
@@ -345,6 +368,8 @@ timeout_del_sync(struct timeout *to)
void
timeout_barrier(struct timeout *to, int flags)
{
if (!to->timer.function)
return;
timer_delete_sync(&to->timer);
cancel_work_sync(&to->work);
}
@@ -405,3 +430,153 @@ pfk_wakeup(void *p)
}
spin_unlock(&pfk_tsleep_lock);
}
/* ---------------- OpenBSD counters(9) over cpumem ---------------- */
/* Each "counter" slot is an array of `count` u64s per possible CPU.
* PFLINUX deviation from OpenBSD's cpumem-based counters_read(): the
* scratch argument is accepted but unused (no cpu_info walk needed). */
struct cpumem *
counters_alloc(u_int count)
{
return cpumem_malloc((size_t)count * sizeof(uint64_t), M_WAITOK);
}
void
counters_free(struct cpumem *cm)
{
cpumem_free(cm, 0);
}
void
counters_inc(struct cpumem *cm, u_int idx)
{
uint64_t *p;
if (!cm)
return;
preempt_disable();
p = (uint64_t *)cm->cm_base +
(size_t)smp_processor_id() * cm->cm_item / sizeof(uint64_t);
p[idx]++;
preempt_enable();
}
void
counters_zero(struct cpumem *cm, u_int count)
{
unsigned int cpu;
if (!cm)
return;
for_each_possible_cpu(cpu) {
uint64_t *p = (uint64_t *)cm->cm_base +
(size_t)cpu * cm->cm_item / sizeof(uint64_t);
unsigned int i;
for (i = 0; i < count; i++)
p[i] = 0;
}
}
void
counters_read(struct cpumem *cm, uint64_t *result, u_int count,
uint64_t *scratch)
{
unsigned int cpu;
(void)scratch; /* PFLINUX: unused (see header note) */
memset(result, 0, count * sizeof(*result));
if (!cm)
return;
for_each_possible_cpu(cpu) {
uint64_t *p = (uint64_t *)cm->cm_base +
(size_t)cpu * cm->cm_item / sizeof(uint64_t);
unsigned int i;
for (i = 0; i < count; i++)
result[i] += p[i];
}
}
/* ---------------- OpenBSD sysctl-backed globals ---------------- */
/* PFLINUX: these are OpenBSD kern.sysctls PF consults per-packet. The
* port keeps plain globals; ip_forwarding is synced from
* init_net.ipv4.sysctl_ip_forward on ifevents (pfk_if.c) — a runtime
* `sysctl -w net.ipv4.ip_forward` is picked up on the next event, not
* instantly (recorded deviation). */
int ip_forwarding;
int ip_directedbcast;
int ipmultipath;
int ip6_multipath;
int ip_mtudisc = 1; /* Linux enables PMTU discovery by default */
int ip_defttl = 64; /* OpenBSD IP_DEFTTL */
u_int max_linkhdr = 14; /* refreshed by pfk_if_refresh() */
tcp_seq tcp_iss; /* PFLINUX: stays 0; pf_tcp_iss() supplies
* the randomness (SHA512 + per-packet off) */
void
pfk_sysctl_sync(void)
{
/* PFLINUX: OpenBSD ip_forwarding 0/1(/2 ipsec). Linux 7.3 keeps
* the forwarding boolean in the "all" ipv4 device-configuration
* (per-net ipv4.devconf_all, the old net.ipv4.ip_forward sysctl
* storage); we mirror it as 0/1. */
int fwd = 0;
if (init_net.ipv4.devconf_all)
fwd = IPV4_DEVCONF(*init_net.ipv4.devconf_all, FORWARDING);
WRITE_ONCE(ip_forwarding, fwd ? 1 : 0);
}
/* ---------------- curcpu(9) ---------------- */
static DEFINE_PER_CPU(struct pfk_cpu_info, pfk_cpu_infos);
struct pfk_cpu_info *
pfk_curcpu(void)
{
struct pfk_cpu_info *ci;
preempt_disable();
ci = this_cpu_ptr(&pfk_cpu_infos);
ci->ci_schedstate.spc_schedflags =
need_resched() ? SPCF_SHOULDYIELD : 0;
preempt_enable();
return ci;
}
/* ---------------- if_congested(9) ---------------- */
/* PFLINUX: OpenBSD sets ifq_congestion when any ifq hits resource
* pressure; the port stamps the same instant from its xmit path
* (pfk_if_congestion(), called on ENOBUFS-class drops in pfk_xmit.c). */
static unsigned long pfk_congestion_jif;
void
pfk_if_congestion(void)
{
WRITE_ONCE(pfk_congestion_jif, jiffies);
}
int
if_congested(void)
{
unsigned long c = READ_ONCE(pfk_congestion_jif);
if (!c || time_after(jiffies, c + HZ / 100))
return 0;
return 1;
}
/* ---------------- pflog (netlink backend, milestone M4) ---------------- */
/* NPFLOG=1: the staged if_pflog.c needs the OpenBSD ifpf layer; until the
* netlink-backed pflog lands (M4), pflog_packet() consumes nothing and
* reports the missing backend at least once so logged packets are never
* silently vanished (see docs/porting-notes.md). */
int
pflog_packet(struct pf_pdesc *pd, u_int8_t reason, struct pf_rule *rm,
struct pf_rule *am, struct pf_ruleset *ruleset, struct pf_rule *cm)
{
(void)pd; (void)reason; (void)rm; (void)am; (void)ruleset; (void)cm;
pr_warn_once("pfk: pflog backend pending M4; packet not logged\n");
return (0);
}
+74
View File
@@ -187,6 +187,80 @@ m_dup(struct mbuf *m, int wait)
return n;
}
/*
* m_prepend(m, len, how): make room for `len` bytes at the front of the
* packet (contents moved, not initialised — BSD semantics).
*/
struct mbuf *
m_prepend(struct mbuf *m, int len, int how)
{
if (!m || len <= 0)
return m;
if (skb_headroom(m->skb) < len) {
int extra = ALIGN(len - skb_headroom(m->skb), 64);
gfp_t g = (how & M_WAITOK) ? GFP_ATOMIC : GFP_ATOMIC;
if ((how & (M_WAITOK|M_CANFAIL)) == M_WAITOK)
g = GFP_KERNEL;
if (pskb_expand_head(m->skb, extra, 0, g)) {
m_freem(m);
return NULL;
}
}
skb_push(m->skb, len);
pfk_mbuf_refresh(m);
if (m->m_flags & M_PKTHDR)
m->m_pkthdr.len += len;
return m;
}
/*
* m_split(m, len, flags): cut m at `len`, return the tail (second half).
* m keeps the first `len` bytes. On failure returns NULL and leaves m
* untouched (OpenBSD semantics: m unchanged).
*/
struct mbuf *
m_split(struct mbuf *m, int len, int flags)
{
struct mbuf *tail;
if (len >= m->m_len)
return NULL; /* nothing to split off */
tail = m_copym(m, len, m->m_len - len, flags);
if (!tail)
return NULL;
skb_trim(m->skb, len);
pfk_mbuf_refresh(m);
if (m->m_flags & M_PKTHDR)
m->m_pkthdr.len = len;
return tail;
}
/*
* m_dup_pkt(m, hdr, how): full duplicate of m with at least `hdr` bytes of
* headroom in front of the data (for link-layer rewriting downstream).
*/
struct mbuf *
m_dup_pkt(struct mbuf *m, u_int hdr, int how)
{
struct mbuf *n = m_dup(m, how);
if (!n)
return NULL;
if ((unsigned int)skb_headroom(n->skb) < hdr) {
int extra = ALIGN((int)hdr - skb_headroom(n->skb), 64);
if (pskb_expand_head(n->skb, extra, 0,
(how & (M_WAITOK|M_CANFAIL)) == M_WAITOK ?
GFP_KERNEL : GFP_ATOMIC)) {
m_freem(n);
return NULL;
}
pfk_mbuf_refresh(n);
}
return n;
}
void
m_adj(struct mbuf *m, int dlen)
{
+176
View File
@@ -0,0 +1,176 @@
/*
* PF-on-Linux: BSD inpcb view over Linux sockets (ISC 2026).
*
* pf.c uses OpenBSD's PCB API to (a) find the socket owning an arriving
* packet (pf_socket_lookup: uid/gid/pid match), and (b) link state keys to
* sockets (sk_inp <-> inp_pf_sk). The port creates one struct inpcb per
* successful Linux socket lookup; it holds the Linux `struct sock` and a
* BSD-ish socket view. Reference semantics follow OpenBSD: lookups return
* the pcb with one reference held; PF takes extra refs (mbufs, state keys)
* via in_pcbref()/in_pcbunref().
*
* PFLINUX deviations (docs/porting-notes.md):
* - so_egid: Linux does not store the group of the socket owner in
* struct sock; reported as (gid_t)-1 ("unknown").
* - so_cpid: Linux does not store the owning pid in struct sock
* (/proc-based matching is a userland-side feature); reported NO_PID.
* - Wildcard match: OpenBSD in_pcblookup() finds bound-with-wildcard
* sockets in one pass; here established lookups are exact and the
* wildcard is found via in_pcblookup_listen(), which is exactly the
* sequence pf_socket_lookup already follows.
*/
#include <pfk.h>
#include <net/inet_hashtables.h>
#include <net/inet6_hashtables.h>
struct inpcbtable tcbtable, udbtable, tcb6table, udb6table;
static struct inpcb *
pfk_inpcb_of_sock(struct sock *sk)
{
struct inpcb *inp;
if (!sk)
return NULL;
/* our stable reference on the Linux socket */
if (!refcount_inc_not_zero(&sk->__sk_common.skc_refcnt))
return NULL;
inp = pfk_malloc(sizeof(*inp), M_NOWAIT | M_ZERO);
if (!inp) {
sock_gen_put(sk);
return NULL;
}
refcount_set(&inp->inp_refcnt, 1);
inp->inp_sock = sk;
inp->inp_socket = &inp->inp_soinfo_storage;
inp->inp_socket->so_euid = __kuid_val(sk_uid(sk));
/* PFLINUX: Linux stores no egid/owner-pid in struct sock */
inp->inp_socket->so_egid = (gid_t)-1;
inp->inp_socket->so_cpid = NO_PID;
return inp;
}
struct inpcb *
in_pcbref(struct inpcb *inp)
{
if (inp != NULL)
refcount_inc(&inp->inp_refcnt);
return inp;
}
void
in_pcbunref(struct inpcb *inp)
{
struct sock *sk;
if (inp == NULL)
return;
if (!refcount_dec_and_test(&inp->inp_refcnt))
return;
sk = inp->inp_sock;
if (sk != NULL)
sock_gen_put(sk); /* drop our stable reference */
pfk_free(inp);
}
/*
* Established/exact 4-tuple lookup. table selects the protocol family
* (tcbtable: TCP, udbtable: UDP) — OpenBSD keeps them apart; Linux shares
* the ehash for TCP but UDP needs its own lib lookup (which also covers
* bound-wildcard matching, mirroring OpenBSD's behaviour).
*/
struct inpcb *
in_pcblookup(struct inpcbtable *table, struct in_addr faddr, u_int fport,
struct in_addr laddr, u_int lport, u_int rtableid)
{
struct sock *sk;
struct inpcb *inp;
rcu_read_lock();
if (table == &udbtable) {
sk = __udp4_lib_lookup(&init_net, faddr.s_addr, (__be16)fport,
laddr.s_addr, (__be16)lport, 0, 0, NULL);
} else {
sk = __inet_lookup_established(&init_net, faddr.s_addr,
(__be16)fport, laddr.s_addr, ntohs((__be16)lport), 0, 0);
}
inp = sk ? pfk_inpcb_of_sock(sk) : NULL;
rcu_read_unlock();
return inp;
}
struct inpcb *
in_pcblookup_listen(struct inpcbtable *table, struct in_addr laddr,
u_int lport, struct mbuf *m, u_int rtableid)
{
struct sock *sk;
struct inpcb *inp;
rcu_read_lock();
if (table == &udbtable) {
/* UDP: the lib lookup matches the wildcard bound socket */
sk = __udp4_lib_lookup(&init_net, 0, 0, laddr.s_addr,
(__be16)lport, 0, 0, NULL);
} else {
sk = __inet_lookup_listener(&init_net, NULL, 0, 0, 0,
laddr.s_addr, ntohs((__be16)lport), 0, 0);
}
inp = sk ? pfk_inpcb_of_sock(sk) : NULL;
rcu_read_unlock();
return inp;
}
struct inpcb *
in6_pcblookup(struct inpcbtable *table, const struct in6_addr *faddr,
u_int fport, const struct in6_addr *laddr, u_int lport, u_int rtableid)
{
struct sock *sk;
struct inpcb *inp;
rcu_read_lock();
if (table == &udb6table) {
sk = __udp6_lib_lookup(&init_net, faddr, (__be16)fport,
(struct in6_addr *)laddr, (__be16)lport, 0, 0, NULL);
} else {
sk = __inet6_lookup_established(&init_net, faddr,
(__be16)fport, (struct in6_addr *)laddr,
ntohs((__be16)lport), 0, 0);
}
inp = sk ? pfk_inpcb_of_sock(sk) : NULL;
rcu_read_unlock();
return inp;
}
struct inpcb *
in6_pcblookup_listen(struct inpcbtable *table, struct in6_addr *laddr,
u_int lport, struct mbuf *m, u_int rtableid)
{
static const struct in6_addr any = { };
struct sock *sk;
struct inpcb *inp;
rcu_read_lock();
if (table == &udb6table) {
sk = __udp6_lib_lookup(&init_net, &any, 0, laddr,
(__be16)lport, 0, 0, NULL);
} else {
sk = inet6_lookup_listener(&init_net, NULL, 0, &any, 0,
laddr, ntohs((__be16)lport), 0, 0);
}
inp = sk ? pfk_inpcb_of_sock(sk) : NULL;
rcu_read_unlock();
return inp;
}
int
pfk_pcb_init(void)
{
return 0;
}
void
pfk_pcb_exit(void)
{
}
+152 -4
View File
@@ -1,8 +1,14 @@
/*
* PFLINUX rtable compatibility (ISC 2026).
* PFLINUX rtable/route compatibility (ISC 2026).
* Linux has no per-rtableid FIBs: rtable_exists() accepts every id and
* rtable_iterate() is a terminal stub — the route-to implementation uses
* the Linux FIB through pfk_xmit.c (pf_route/pf_route6, plan task T14).
* rtable_iterate() stops after the first route (multipath iteration is
* not modelled; recorded deviation). The PF route-to/mb-path checks get
* their answers from the Linux FIB here; forwarding itself happens in
* pfk_ip4.c/pfk_xmit.c.
*
* struct pf_rtentry instances are small heap snapshots of the Linux
* route: egress ifindex + a BSD-shaped interface address view (needed by
* ifatoia/ifatoia6 in pf.c) + label id (always 0, labels not modelled).
*/
#include <pfk.h>
#include "compat/net/rtable.h"
@@ -17,6 +23,148 @@ rtable_exists(unsigned int rtableid)
struct pf_rtentry *
rtable_iterate(struct pf_rtentry *rt)
{
/* PFLINUX: replaced by Linux FIB walk in pfk_route_next() (T14). */
/* PFLINUX: multipath route iteration is not modelled; PF falls
* back to the single Linux FIB answer (checked once). */
(void)rt;
return NULL;
}
/* attach a BSD interface-address view for the egress device */
static void
pfk_rt_fill_ifa(struct pf_rtentry *rt, struct pfk_ifnet *ifp, int family)
{
struct pfk_in_ifaddr *ia4;
struct pfk_in6_ifaddr *ia6;
struct ifaddr *ifa;
if (!ifp)
return;
if (family == AF_INET) {
ia4 = pfk_malloc(sizeof(*ia4), M_NOWAIT | M_ZERO);
if (!ia4)
return;
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET) {
ia4->ia_addr = *sintosa(ifa->ifa_addr);
break;
}
}
rt->rt_ifa = ia4;
} else {
ia6 = pfk_malloc(sizeof(*ia6), M_NOWAIT | M_ZERO);
if (!ia6)
return;
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6) {
ia6->ia_addr = *sin6tosa(ifa->ifa_addr);
break;
}
}
rt->rt_ifa = ia6;
}
}
static struct pf_rtentry *
pfk_rtentry_from_dst(struct dst_entry *d, const struct sockaddr *dst)
{
struct pf_rtentry *rt;
struct pfk_ifnet *ifp;
rt = pfk_malloc(sizeof(*rt), M_NOWAIT | M_ZERO);
if (!rt)
return NULL;
rt->rt_addr = (void *)dst; /* borrowed; not freed */
rt->rt_ifidx = d->dev ? d->dev->ifindex : 0;
rt->rt_flags = RTF_UP;
ifp = d->dev ? pfk_ifp_byindex(d->dev->ifindex) : NULL;
pfk_rt_fill_ifa(rt, ifp, dst->sa_family);
return rt;
}
struct pf_rtentry *
rtalloc(const void *sap, int flags, unsigned int rtableid)
{
const struct sockaddr *sa = (const struct sockaddr *)sap;
struct pf_rtentry *rt = NULL;
(void)flags; (void)rtableid; /* PFLINUX: one FIB, resolve implied */
switch (sa->sa_family) {
case AF_INET: {
struct flowi4 fl4 = { };
struct rtable *r4;
fl4.daddr = sintosa(sa)->sin_addr.s_addr;
r4 = ip_route_output_key(&init_net, &fl4);
if (!IS_ERR(r4)) {
rt = pfk_rtentry_from_dst(&r4->dst, sa);
dst_release(&r4->dst);
}
break;
}
case AF_INET6: {
#if IS_ENABLED(CONFIG_IPV6)
struct flowi6 fl6 = { };
struct dst_entry *d;
fl6.daddr = sin6tosa(sa)->sin6_addr;
d = ip6_route_output(&init_net, NULL, &fl6);
if (d && !d->error) {
rt = pfk_rtentry_from_dst(d, sa);
dst_release(d);
} else if (d)
dst_release(d);
break;
#endif
}
default:
break;
}
return rt;
}
struct pf_rtentry *
rtalloc_mpath(const void *sa, uint32_t *hash, unsigned int rtableid)
{
/* PFLINUX: no multipath selection; hash hint unused. */
(void)hash;
return rtalloc(sa, RT_RESOLVE, rtableid);
}
int
rtisvalid(struct pf_rtentry *rt)
{
return rt != NULL;
}
void
rtfree(struct pf_rtentry *rt)
{
if (!rt)
return;
if (rt->rt_ifa)
pfk_free(rt->rt_ifa);
pfk_free(rt);
}
/* ---------------- route labels ---------------- */
/* Routing domains/labels are a non-goal of the port; these satisfy the
* ioctl layer and report "unavailable" instead of pretending (pfk.h). */
int
rtlabel_name2id(const char *name)
{
(void)name;
return (0); /* 0 == no label, pf_ioctl treats as failure */
}
char *
rtlabel_id2name(unsigned int id, char *buf, size_t len)
{
(void)id; (void)buf; (void)len;
return (NULL);
}
void
rtlabel_unref(unsigned int id)
{
(void)id;
}
+7 -4
View File
@@ -48,12 +48,13 @@ pfk_route6(const struct in6_addr *daddr, const struct in6_addr *saddr, int oif)
/* xmit: BSD semantics — caller hands fully-formed IP packet + dst sock.
* rt arg is OpenBSD's cached route (opaque to us). Returns 0/errno. */
int
if_output_tso(struct pfk_ifnet *ifp, struct mbuf **mp, struct sockaddr *dst,
if_output_tso(struct pfk_ifnet *ifp, struct mbuf **mp, void *dst,
void *rt, int mtu)
{
struct mbuf *m = *mp;
struct sk_buff *skb = m_to_skb(m);
struct dst_entry *d = NULL;
struct sockaddr *sa = (struct sockaddr *)dst;
int ret = 0;
if (!pskb_may_pull(skb, sizeof(struct pf_ip))) {
@@ -64,7 +65,7 @@ if_output_tso(struct pfk_ifnet *ifp, struct mbuf **mp, struct sockaddr *dst,
ret = ENOBUFS;
goto drop;
}
switch (dst->sa_family) {
switch (sa->sa_family) {
case AF_INET: {
struct pf_ip *ip = mtod(m, struct pf_ip *);
__be32 gw = 0;
@@ -129,12 +130,14 @@ drop:
m_freem(m);
*mp = NULL;
}
if (ret == ENOBUFS || ret == ENETDOWN || ret == EHOSTUNREACH)
pfk_if_congestion();
return ret;
}
int
if_output_ml(struct pfk_ifnet *ifp, struct mbuf_list *ml,
struct sockaddr *dst, void *rt)
void *dst, void *rt)
{
struct mbuf *m;
int err = 0, n;
@@ -212,7 +215,7 @@ pfk_send_icmp_error(int family, struct sk_buff *skb_in, int type, int code,
* ported PF code (route-to / refragment / divert paths).
*/
int
pfk_if_output(struct pfk_ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
pfk_if_output(struct pfk_ifnet *ifp, struct mbuf *m, void *dst,
void *rt)
{
struct mbuf *mp = m;
File diff suppressed because it is too large. Load diff
+642
View File
@@ -0,0 +1,642 @@
/* PFLINUX port of OpenBSD 8.0 kern/subr_tree.c — see docs/porting-notes.md */
#include <pfk.h>
#include <net/pfvar.h>
#include <net/pfvar_priv.h>
/* $OpenBSD: subr_tree.c,v 1.10 2018/10/09 08:28:43 dlg Exp $ */
/*
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) 2016 David Gwynne <dlg@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
static inline struct rb_entry *
rb_n2e(const struct rb_type *t, void *node)
{
unsigned long addr = (unsigned long)node;
return ((struct rb_entry *)(addr + t->t_offset));
}
static inline void *
rb_e2n(const struct rb_type *t, struct rb_entry *rbe)
{
unsigned long addr = (unsigned long)rbe;
return ((void *)(addr - t->t_offset));
}
#define RBE_LEFT(_rbe) (_rbe)->rbt_left
#define RBE_RIGHT(_rbe) (_rbe)->rbt_right
#define RBE_PARENT(_rbe) (_rbe)->rbt_parent
#define RBE_COLOR(_rbe) (_rbe)->rbt_color
#define RBH_ROOT(_rbt) (_rbt)->rbt_root
static inline void
rbe_set(struct rb_entry *rbe, struct rb_entry *parent)
{
RBE_PARENT(rbe) = parent;
RBE_LEFT(rbe) = RBE_RIGHT(rbe) = NULL;
RBE_COLOR(rbe) = RB_RED;
}
static inline void
rbe_set_blackred(struct rb_entry *black, struct rb_entry *red)
{
RBE_COLOR(black) = RB_BLACK;
RBE_COLOR(red) = RB_RED;
}
static inline void
rbe_augment(const struct rb_type *t, struct rb_entry *rbe)
{
(*t->t_augment)(rb_e2n(t, rbe));
}
static inline void
rbe_if_augment(const struct rb_type *t, struct rb_entry *rbe)
{
if (t->t_augment != NULL)
rbe_augment(t, rbe);
}
static inline void
rbe_rotate_left(const struct rb_type *t, struct rb_tree *rbt,
struct rb_entry *rbe)
{
struct rb_entry *parent;
struct rb_entry *tmp;
tmp = RBE_RIGHT(rbe);
RBE_RIGHT(rbe) = RBE_LEFT(tmp);
if (RBE_RIGHT(rbe) != NULL)
RBE_PARENT(RBE_LEFT(tmp)) = rbe;
parent = RBE_PARENT(rbe);
RBE_PARENT(tmp) = parent;
if (parent != NULL) {
if (rbe == RBE_LEFT(parent))
RBE_LEFT(parent) = tmp;
else
RBE_RIGHT(parent) = tmp;
} else
RBH_ROOT(rbt) = tmp;
RBE_LEFT(tmp) = rbe;
RBE_PARENT(rbe) = tmp;
if (t->t_augment != NULL) {
rbe_augment(t, rbe);
rbe_augment(t, tmp);
parent = RBE_PARENT(tmp);
if (parent != NULL)
rbe_augment(t, parent);
}
}
static inline void
rbe_rotate_right(const struct rb_type *t, struct rb_tree *rbt,
struct rb_entry *rbe)
{
struct rb_entry *parent;
struct rb_entry *tmp;
tmp = RBE_LEFT(rbe);
RBE_LEFT(rbe) = RBE_RIGHT(tmp);
if (RBE_LEFT(rbe) != NULL)
RBE_PARENT(RBE_RIGHT(tmp)) = rbe;
parent = RBE_PARENT(rbe);
RBE_PARENT(tmp) = parent;
if (parent != NULL) {
if (rbe == RBE_LEFT(parent))
RBE_LEFT(parent) = tmp;
else
RBE_RIGHT(parent) = tmp;
} else
RBH_ROOT(rbt) = tmp;
RBE_RIGHT(tmp) = rbe;
RBE_PARENT(rbe) = tmp;
if (t->t_augment != NULL) {
rbe_augment(t, rbe);
rbe_augment(t, tmp);
parent = RBE_PARENT(tmp);
if (parent != NULL)
rbe_augment(t, parent);
}
}
static inline void
rbe_insert_color(const struct rb_type *t, struct rb_tree *rbt,
struct rb_entry *rbe)
{
struct rb_entry *parent, *gparent, *tmp;
while ((parent = RBE_PARENT(rbe)) != NULL &&
RBE_COLOR(parent) == RB_RED) {
gparent = RBE_PARENT(parent);
if (parent == RBE_LEFT(gparent)) {
tmp = RBE_RIGHT(gparent);
if (tmp != NULL && RBE_COLOR(tmp) == RB_RED) {
RBE_COLOR(tmp) = RB_BLACK;
rbe_set_blackred(parent, gparent);
rbe = gparent;
continue;
}
if (RBE_RIGHT(parent) == rbe) {
rbe_rotate_left(t, rbt, parent);
tmp = parent;
parent = rbe;
rbe = tmp;
}
rbe_set_blackred(parent, gparent);
rbe_rotate_right(t, rbt, gparent);
} else {
tmp = RBE_LEFT(gparent);
if (tmp != NULL && RBE_COLOR(tmp) == RB_RED) {
RBE_COLOR(tmp) = RB_BLACK;
rbe_set_blackred(parent, gparent);
rbe = gparent;
continue;
}
if (RBE_LEFT(parent) == rbe) {
rbe_rotate_right(t, rbt, parent);
tmp = parent;
parent = rbe;
rbe = tmp;
}
rbe_set_blackred(parent, gparent);
rbe_rotate_left(t, rbt, gparent);
}
}
RBE_COLOR(RBH_ROOT(rbt)) = RB_BLACK;
}
static inline void
rbe_remove_color(const struct rb_type *t, struct rb_tree *rbt,
struct rb_entry *parent, struct rb_entry *rbe)
{
struct rb_entry *tmp;
while ((rbe == NULL || RBE_COLOR(rbe) == RB_BLACK) &&
rbe != RBH_ROOT(rbt)) {
if (RBE_LEFT(parent) == rbe) {
tmp = RBE_RIGHT(parent);
if (RBE_COLOR(tmp) == RB_RED) {
rbe_set_blackred(tmp, parent);
rbe_rotate_left(t, rbt, parent);
tmp = RBE_RIGHT(parent);
}
if ((RBE_LEFT(tmp) == NULL ||
RBE_COLOR(RBE_LEFT(tmp)) == RB_BLACK) &&
(RBE_RIGHT(tmp) == NULL ||
RBE_COLOR(RBE_RIGHT(tmp)) == RB_BLACK)) {
RBE_COLOR(tmp) = RB_RED;
rbe = parent;
parent = RBE_PARENT(rbe);
} else {
if (RBE_RIGHT(tmp) == NULL ||
RBE_COLOR(RBE_RIGHT(tmp)) == RB_BLACK) {
struct rb_entry *oleft;
oleft = RBE_LEFT(tmp);
if (oleft != NULL)
RBE_COLOR(oleft) = RB_BLACK;
RBE_COLOR(tmp) = RB_RED;
rbe_rotate_right(t, rbt, tmp);
tmp = RBE_RIGHT(parent);
}
RBE_COLOR(tmp) = RBE_COLOR(parent);
RBE_COLOR(parent) = RB_BLACK;
if (RBE_RIGHT(tmp))
RBE_COLOR(RBE_RIGHT(tmp)) = RB_BLACK;
rbe_rotate_left(t, rbt, parent);
rbe = RBH_ROOT(rbt);
break;
}
} else {
tmp = RBE_LEFT(parent);
if (RBE_COLOR(tmp) == RB_RED) {
rbe_set_blackred(tmp, parent);
rbe_rotate_right(t, rbt, parent);
tmp = RBE_LEFT(parent);
}
if ((RBE_LEFT(tmp) == NULL ||
RBE_COLOR(RBE_LEFT(tmp)) == RB_BLACK) &&
(RBE_RIGHT(tmp) == NULL ||
RBE_COLOR(RBE_RIGHT(tmp)) == RB_BLACK)) {
RBE_COLOR(tmp) = RB_RED;
rbe = parent;
parent = RBE_PARENT(rbe);
} else {
if (RBE_LEFT(tmp) == NULL ||
RBE_COLOR(RBE_LEFT(tmp)) == RB_BLACK) {
struct rb_entry *oright;
oright = RBE_RIGHT(tmp);
if (oright != NULL)
RBE_COLOR(oright) = RB_BLACK;
RBE_COLOR(tmp) = RB_RED;
rbe_rotate_left(t, rbt, tmp);
tmp = RBE_LEFT(parent);
}
RBE_COLOR(tmp) = RBE_COLOR(parent);
RBE_COLOR(parent) = RB_BLACK;
if (RBE_LEFT(tmp) != NULL)
RBE_COLOR(RBE_LEFT(tmp)) = RB_BLACK;
rbe_rotate_right(t, rbt, parent);
rbe = RBH_ROOT(rbt);
break;
}
}
}
if (rbe != NULL)
RBE_COLOR(rbe) = RB_BLACK;
}
static inline struct rb_entry *
rbe_remove(const struct rb_type *t, struct rb_tree *rbt, struct rb_entry *rbe)
{
struct rb_entry *child, *parent, *old = rbe;
unsigned int color;
if (RBE_LEFT(rbe) == NULL)
child = RBE_RIGHT(rbe);
else if (RBE_RIGHT(rbe) == NULL)
child = RBE_LEFT(rbe);
else {
struct rb_entry *tmp;
rbe = RBE_RIGHT(rbe);
while ((tmp = RBE_LEFT(rbe)) != NULL)
rbe = tmp;
child = RBE_RIGHT(rbe);
parent = RBE_PARENT(rbe);
color = RBE_COLOR(rbe);
if (child != NULL)
RBE_PARENT(child) = parent;
if (parent != NULL) {
if (RBE_LEFT(parent) == rbe)
RBE_LEFT(parent) = child;
else
RBE_RIGHT(parent) = child;
rbe_if_augment(t, parent);
} else
RBH_ROOT(rbt) = child;
if (RBE_PARENT(rbe) == old)
parent = rbe;
*rbe = *old;
tmp = RBE_PARENT(old);
if (tmp != NULL) {
if (RBE_LEFT(tmp) == old)
RBE_LEFT(tmp) = rbe;
else
RBE_RIGHT(tmp) = rbe;
rbe_if_augment(t, tmp);
} else
RBH_ROOT(rbt) = rbe;
RBE_PARENT(RBE_LEFT(old)) = rbe;
if (RBE_RIGHT(old))
RBE_PARENT(RBE_RIGHT(old)) = rbe;
if (t->t_augment != NULL && parent != NULL) {
tmp = parent;
do {
rbe_augment(t, tmp);
tmp = RBE_PARENT(tmp);
} while (tmp != NULL);
}
goto color;
}
parent = RBE_PARENT(rbe);
color = RBE_COLOR(rbe);
if (child != NULL)
RBE_PARENT(child) = parent;
if (parent != NULL) {
if (RBE_LEFT(parent) == rbe)
RBE_LEFT(parent) = child;
else
RBE_RIGHT(parent) = child;
rbe_if_augment(t, parent);
} else
RBH_ROOT(rbt) = child;
color:
if (color == RB_BLACK)
rbe_remove_color(t, rbt, parent, child);
return (old);
}
void *
_rb_remove(const struct rb_type *t, struct rb_tree *rbt, void *elm)
{
struct rb_entry *rbe = rb_n2e(t, elm);
struct rb_entry *old;
old = rbe_remove(t, rbt, rbe);
return (old == NULL ? NULL : rb_e2n(t, old));
}
void *
_rb_insert(const struct rb_type *t, struct rb_tree *rbt, void *elm)
{
struct rb_entry *rbe = rb_n2e(t, elm);
struct rb_entry *tmp;
struct rb_entry *parent = NULL;
void *node;
int comp = 0;
tmp = RBH_ROOT(rbt);
while (tmp != NULL) {
parent = tmp;
node = rb_e2n(t, tmp);
comp = (*t->t_compare)(elm, node);
if (comp < 0)
tmp = RBE_LEFT(tmp);
else if (comp > 0)
tmp = RBE_RIGHT(tmp);
else
return (node);
}
rbe_set(rbe, parent);
if (parent != NULL) {
if (comp < 0)
RBE_LEFT(parent) = rbe;
else
RBE_RIGHT(parent) = rbe;
rbe_if_augment(t, parent);
} else
RBH_ROOT(rbt) = rbe;
rbe_insert_color(t, rbt, rbe);
return (NULL);
}
/* Finds the node with the same key as elm */
void *
_rb_find(const struct rb_type *t, struct rb_tree *rbt, const void *key)
{
struct rb_entry *tmp = RBH_ROOT(rbt);
void *node;
int comp;
while (tmp != NULL) {
node = rb_e2n(t, tmp);
comp = (*t->t_compare)(key, node);
if (comp < 0)
tmp = RBE_LEFT(tmp);
else if (comp > 0)
tmp = RBE_RIGHT(tmp);
else
return (node);
}
return (NULL);
}
/* Finds the first node greater than or equal to the search key */
void *
_rb_nfind(const struct rb_type *t, struct rb_tree *rbt, const void *key)
{
struct rb_entry *tmp = RBH_ROOT(rbt);
void *node;
void *res = NULL;
int comp;
while (tmp != NULL) {
node = rb_e2n(t, tmp);
comp = (*t->t_compare)(key, node);
if (comp < 0) {
res = node;
tmp = RBE_LEFT(tmp);
} else if (comp > 0)
tmp = RBE_RIGHT(tmp);
else
return (node);
}
return (res);
}
void *
_rb_next(const struct rb_type *t, void *elm)
{
struct rb_entry *rbe = rb_n2e(t, elm);
if (RBE_RIGHT(rbe) != NULL) {
rbe = RBE_RIGHT(rbe);
while (RBE_LEFT(rbe) != NULL)
rbe = RBE_LEFT(rbe);
} else {
if (RBE_PARENT(rbe) &&
(rbe == RBE_LEFT(RBE_PARENT(rbe))))
rbe = RBE_PARENT(rbe);
else {
while (RBE_PARENT(rbe) &&
(rbe == RBE_RIGHT(RBE_PARENT(rbe))))
rbe = RBE_PARENT(rbe);
rbe = RBE_PARENT(rbe);
}
}
return (rbe == NULL ? NULL : rb_e2n(t, rbe));
}
void *
_rb_prev(const struct rb_type *t, void *elm)
{
struct rb_entry *rbe = rb_n2e(t, elm);
if (RBE_LEFT(rbe)) {
rbe = RBE_LEFT(rbe);
while (RBE_RIGHT(rbe))
rbe = RBE_RIGHT(rbe);
} else {
if (RBE_PARENT(rbe) &&
(rbe == RBE_RIGHT(RBE_PARENT(rbe))))
rbe = RBE_PARENT(rbe);
else {
while (RBE_PARENT(rbe) &&
(rbe == RBE_LEFT(RBE_PARENT(rbe))))
rbe = RBE_PARENT(rbe);
rbe = RBE_PARENT(rbe);
}
}
return (rbe == NULL ? NULL : rb_e2n(t, rbe));
}
void *
_rb_root(const struct rb_type *t, struct rb_tree *rbt)
{
struct rb_entry *rbe = RBH_ROOT(rbt);
return (rbe == NULL ? rbe : rb_e2n(t, rbe));
}
void *
_rb_min(const struct rb_type *t, struct rb_tree *rbt)
{
struct rb_entry *rbe = RBH_ROOT(rbt);
struct rb_entry *parent = NULL;
while (rbe != NULL) {
parent = rbe;
rbe = RBE_LEFT(rbe);
}
return (parent == NULL ? NULL : rb_e2n(t, parent));
}
void *
_rb_max(const struct rb_type *t, struct rb_tree *rbt)
{
struct rb_entry *rbe = RBH_ROOT(rbt);
struct rb_entry *parent = NULL;
while (rbe != NULL) {
parent = rbe;
rbe = RBE_RIGHT(rbe);
}
return (parent == NULL ? NULL : rb_e2n(t, parent));
}
void *
_rb_left(const struct rb_type *t, void *node)
{
struct rb_entry *rbe = rb_n2e(t, node);
rbe = RBE_LEFT(rbe);
return (rbe == NULL ? NULL : rb_e2n(t, rbe));
}
void *
_rb_right(const struct rb_type *t, void *node)
{
struct rb_entry *rbe = rb_n2e(t, node);
rbe = RBE_RIGHT(rbe);
return (rbe == NULL ? NULL : rb_e2n(t, rbe));
}
void *
_rb_parent(const struct rb_type *t, void *node)
{
struct rb_entry *rbe = rb_n2e(t, node);
rbe = RBE_PARENT(rbe);
return (rbe == NULL ? NULL : rb_e2n(t, rbe));
}
void
_rb_set_left(const struct rb_type *t, void *node, void *left)
{
struct rb_entry *rbe = rb_n2e(t, node);
struct rb_entry *rbl = (left == NULL) ? NULL : rb_n2e(t, left);
RBE_LEFT(rbe) = rbl;
}
void
_rb_set_right(const struct rb_type *t, void *node, void *right)
{
struct rb_entry *rbe = rb_n2e(t, node);
struct rb_entry *rbr = (right == NULL) ? NULL : rb_n2e(t, right);
RBE_RIGHT(rbe) = rbr;
}
void
_rb_set_parent(const struct rb_type *t, void *node, void *parent)
{
struct rb_entry *rbe = rb_n2e(t, node);
struct rb_entry *rbp = (parent == NULL) ? NULL : rb_n2e(t, parent);
RBE_PARENT(rbe) = rbp;
}
void
_rb_poison(const struct rb_type *t, void *node, unsigned long poison)
{
struct rb_entry *rbe = rb_n2e(t, node);
RBE_PARENT(rbe) = RBE_LEFT(rbe) = RBE_RIGHT(rbe) =
(struct rb_entry *)poison;
}
int
_rb_check(const struct rb_type *t, void *node, unsigned long poison)
{
struct rb_entry *rbe = rb_n2e(t, node);
return ((unsigned long)RBE_PARENT(rbe) == poison &&
(unsigned long)RBE_LEFT(rbe) == poison &&
(unsigned long)RBE_RIGHT(rbe) == poison);
}