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

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

No files matched your search

+36 -1
View File
@@ -7,6 +7,8 @@
*/
#include <pfk.h>
static void pfk_if_event_call(int added, struct pfk_ifnet *ifp);
static DEFINE_IDR(pfk_ifnet_idr);
static DEFINE_SPINLOCK(pfk_ifnet_lock);
static struct pfk_ifnet *pfk_egress;
@@ -290,6 +292,7 @@ pfk_netdev_event(struct notifier_block *nb, unsigned long ev, void *ptr)
return NOTIFY_DONE;
}
spin_unlock_irqrestore(&pfk_ifnet_lock, flags);
pfk_if_event_call(1, ifp);
break;
case NETDEV_UNREGISTER:
spin_lock_irqsave(&pfk_ifnet_lock, flags);
@@ -297,6 +300,7 @@ pfk_netdev_event(struct notifier_block *nb, unsigned long ev, void *ptr)
if (ifp) {
ifp->alive = 0;
idr_remove(&pfk_ifnet_idr, dev->ifindex);
pfk_if_event_call(0, ifp);
call_rcu(&ifp->rcu, pfk_ifnet_release);
}
spin_unlock_irqrestore(&pfk_ifnet_lock, flags);
@@ -325,6 +329,35 @@ static struct notifier_block pfk_netdev_nb = {
.notifier_call = pfk_netdev_event,
};
static void (*pfk_if_event_cb)(int added, struct pfk_ifnet *ifp);
static void
pfk_if_event_call(int added, struct pfk_ifnet *ifp)
{
if (!pfk_if_event_cb)
return;
/* netdev notifiers run in process context; NET lock is sleepable */
pfk_net_lock();
pfk_if_event_cb(added, ifp);
pfk_net_unlock();
}
void
pfk_if_set_event_cb(void (*cb)(int added, struct pfk_ifnet *ifp))
{
pfk_if_event_cb = cb;
}
void
pfk_if_iterate(void (*cb)(struct pfk_ifnet *ifp))
{
struct pfk_ifnet *ifp;
int id = -1;
while ((ifp = pfk_if_next_from(&id)) != NULL)
cb(ifp);
}
/* -------- egress -------- */
void
pfk_egress_refresh(void)
@@ -403,8 +436,10 @@ pfk_if_init(void)
else
ifp = NULL; /* already tracked by notifier */
spin_unlock_irqrestore(&pfk_ifnet_lock, flags);
if (ifp)
if (ifp) {
pfk_if_refresh(ifp);
pfk_if_event_call(1, ifp);
}
}
rtnl_unlock();
pfk_egress_refresh();
+20 -2
View File
@@ -100,10 +100,28 @@ taskq_create(const char *name, unsigned int nthreads, int ipl,
/* ---------------- NET_LOCK ---------------- */
static DECLARE_RWSEM(pfk_net_sem);
static atomic_t pfk_net_readers = ATOMIC_INIT(0);
static atomic_t pfk_net_owner = ATOMIC_INIT(0); /* task pid holding write */
void pfk_net_lock_init(void) { }
void pfk_net_lock(void) { down_write(&pfk_net_sem); }
void pfk_net_unlock(void) { up_write(&pfk_net_sem); }
void
pfk_net_lock(void)
{
/* Re-entrant for the holder: OpenBSD's NET_LOCK is friendly with
* nested calls in the same path (e.g. our netdev notifier firing
* while pfioctl holds the lock). */
if (atomic_read(&pfk_net_owner) == current->pid)
return;
down_write(&pfk_net_sem);
atomic_set(&pfk_net_owner, current->pid);
}
void
pfk_net_unlock(void)
{
if (atomic_read(&pfk_net_owner) != current->pid)
return; /* nested no-op for non-holders */
atomic_set(&pfk_net_owner, 0);
up_write(&pfk_net_sem);
}
void pfk_net_read_enter(void) { down_read(&pfk_net_sem);
atomic_inc(&pfk_net_readers); }
void pfk_net_read_release(void){ atomic_dec(&pfk_net_readers);
+24
View File
@@ -50,6 +50,30 @@ pfk_m_setpkthdr(struct mbuf *m)
m->m_pkthdr.tags = NULL;
}
/*
* Ingress wrapper for netfilter hook skbs: takes an EXTRA skb reference so
* the wrapper can be consumed (m_freem) without disturbing netfilter's own
* reference; fills the pkthdr the way OpenBSD's ip_input/ip_output does.
*/
struct mbuf *
pfk_m_wrap_skb(struct sk_buff *skb, int af)
{
struct mbuf *m;
skb_get(skb);
m = m_alloc_skb(skb, M_NOWAIT);
if (!m)
return NULL;
m->m_pkthdr.af = af;
m->m_pkthdr.proto = (af == AF_INET) ?
((struct pf_ip *)skb->data)->ip_p : -1;
m->m_pkthdr.ph_ifidx = skb->dev ? skb->dev->ifindex : 0;
m->m_pkthdr.ifp = m->m_pkthdr.rcvif =
skb->dev ? pfk_ifp_bydev(skb->dev) : NULL;
m->m_pkthdr.ph_rtableid = 0;
return m;
}
struct mbuf *
m_alloc_skb(struct sk_buff *skb, int how)
{
+6
View File
@@ -86,6 +86,12 @@ struct pfk_ifnet *pfk_if_next(struct pfk_ifnet *ifp);
struct pfk_ifnet *pfk_if_next_from(int *idp);
struct net_device *ip_rt_get_default_dev(struct net *net);
/* Notification for PF engine (pfi attach/detach), called under NET_LOCK:
* added=1 new interface (after populate), added=0 interface going away. */
void pfk_if_set_event_cb(void (*cb)(int added, struct pfk_ifnet *ifp));
/* Iterate all live interfaces with cb(ifp) under NET_LOCK-read. */
void pfk_if_iterate(void (*cb)(struct pfk_ifnet *ifp));
/* xmit API used by pf_route/pf output (implemented in pfk_xmit.c).
* PFLINUX: `dst` is void * so BSD accessor macros (sintosa/sin6tosa,
* which keep the sockaddr_in/sockaddr_in6 type for field access) can be
+2
View File
@@ -141,6 +141,8 @@ struct mbuf {
struct mbuf *m_get(int how, short type);
struct mbuf *m_gethdr(int how, short type);
struct mbuf *m_alloc_skb(struct sk_buff *skb, int how); /* wrap borrowed skb */
struct mbuf *pfk_m_wrap_skb(struct sk_buff *skb, int af); /* ingress: extra ref */
struct mbuf *pfk_m_wrap_skb(struct sk_buff *skb, int af); /* ingress: takes extra ref */
struct mbuf *m_devget(char *buf, int totlen, int off,
struct pfk_ifnet *ifp, void (*copyin_fc)(caddr_t, int));
void pfk_m_setpkthdr(struct mbuf *m);
+126 -25
View File
@@ -1,37 +1,127 @@
/*
* PF-on-Linux netfilter hook registration + engine status surface.
* The verdict path is staged pf.c (pf_test()); hook→pf_test wiring with
* mbuf ingress is milestone M2's data-path task — until then the hooks
* accept so the module is inert once loaded (never silent: pr_info at
* registration says so). (ISC 2026)
* PF-on-Linux netfilter hook registration + M2 data path.
*
* One pf_test() pass per BSD ip-stack call site:
* NF_INET_PRE_ROUTING → pf_test(PF_IN) (OpenBSD ip_input)
* NF_INET_LOCAL_OUT → pf_test(PF_OUT) (OpenBSD ip_output, early)
* NF_INET_POST_ROUTING → pf_test(PF_OUT) (OpenBSD ip_output, routed)
* LOCAL_IN / FORWARD are NF_ACCEPT: the packet was already evaluated on
* its PRE (input) and POST (output) passes, exactly like OpenBSD which
* runs PF once per direction inside ip_input/ip_output.
*
* Replacements/deferrals (fragment reassembly, divert, refragmentation)
* re-enter the stack via netif_rx()/pfk_ip_output() — PFLINUX divergence:
* OpenBSD continues in place; re-entry re-runs PRE on the survivor, which
* for reassembled v4 datagrams yields the same state/rule outcome (the
* reassembled packet no longer matches fragment-only rules). Documented
* in docs/porting-notes.md.
*
* (c) 2026 PF-on-Linux contributors — ISC
*/
#include <pfk.h>
#include <pfk_api.h>
#include <net/pfvar.h>
#include <net/pfvar_priv.h>
static int pfk_enabled = 1; /* like pf_defaultwise/pf_enable on boot */
static struct pf_status pfk_stat;
extern int pf_test(sa_family_t, int, struct pfk_ifnet *, struct mbuf **);
void pfk_status(struct pf_status *s)
{
if (s)
*s = pfk_stat;
}
int pfk_enable(int on)
{
pfk_enabled = on ? 1 : 0;
return 0;
}
/* --- netfilter hooks: ingress/egress plumbing --- */
/* shared verdict plumbing for v4/v6 (af decides header pull size) */
static unsigned int
pfk_ipv4_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *st)
pfk_run(int af, struct sk_buff *skb, const struct nf_hook_state *st)
{
/* PFLINUX M1: pf_test() routing arrives with the M2 data-path task;
* until then the hooks accept (module inert). */
return NF_ACCEPT;
struct pfk_ifnet *ifp;
struct mbuf *m, *m0;
int dir, action;
if (af == AF_INET &&
!pskb_may_pull(skb, sizeof(struct pf_ip)))
return NF_ACCEPT;
if (af == AF_INET6 &&
!pskb_may_pull(skb, sizeof(struct pf_ip6_hdr)))
return NF_ACCEPT;
switch (st->hook) {
case NF_INET_PRE_ROUTING:
dir = PF_IN;
break;
case NF_INET_LOCAL_OUT:
case NF_INET_POST_ROUTING:
dir = PF_OUT;
break;
default:
return NF_ACCEPT; /* LOCAL_IN/FORWARD: seen already */
}
/* ifp: OpenBSD passes the *input* interface on PF_IN and the
* output interface on PF_OUT (from the route). */
if (dir == PF_IN)
ifp = pfk_ifp_bydev(st->in ? st->in : skb->dev);
else
ifp = pfk_ifp_bydev(skb->dev ? skb->dev : st->out);
if (!ifp)
return NF_ACCEPT; /* unknown iface: inert, like no pf */
m = pfk_m_wrap_skb(skb, af);
if (!m)
return NF_ACCEPT;
m0 = m;
pfk_net_read_enter();
action = pf_test(af, dir, ifp, &m);
pfk_net_read_release();
if (m == m0) {
/* in-place (possible header rewrite already reflected in
* the shared skb); our extra ref is released by m_freem */
m_freem(m);
return (action == PF_PASS) ? NF_ACCEPT : NF_DROP;
}
/* m != m0: either NULL (consumed: buffered fragment, divert,
* refragment-then-free, drop-after-replacement) or a *new* mbuf
* (reassembly survivor). m0 itself was consumed by pf. */
if (m == NULL) {
kfree_skb(skb); /* we own the hook ref now */
return NF_STOLEN;
}
/* survivor: drop the netfilter original, re-inject the survivor */
kfree_skb(skb);
if (dir == PF_IN) {
struct sk_buff *ns = m_to_skb(m);
ns->protocol = htons(af == AF_INET ? ETH_P_IP : ETH_P_IPV6);
if (!ns->dev)
ns->dev = st->in;
if (ns->dev)
dev_hold(ns->dev);
else {
kfree_skb(ns);
m->skb = NULL;
m_freem(m);
return NF_STOLEN;
}
netif_rx(ns);
m->skb = NULL; /* reference moved into netif_rx */
m_freem(m);
} else {
/* survivor leaves via the BSD output helper (consumes m) */
pfk_ip_output(m, NULL, NULL, 0, NULL, NULL, 0);
}
return NF_STOLEN;
}
static unsigned int
pfk_ipv4_hook(void *priv, struct sk_buff *skb,
const struct nf_hook_state *st)
{
return pfk_run(AF_INET, skb, st);
}
static unsigned int
pfk_ipv6_hook(void *priv, struct sk_buff *skb,
const struct nf_hook_state *st)
{
return pfk_run(AF_INET6, skb, st);
}
static struct nf_hook_ops pfk_hooks[] = {
@@ -45,6 +135,16 @@ static struct nf_hook_ops pfk_hooks[] = {
.priority = NF_IP_PRI_CONNTRACK + 1 },
{ .hook = pfk_ipv4_hook, .pf = NFPROTO_IPV4, .hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_LAST - 1 },
{ .hook = pfk_ipv6_hook, .pf = NFPROTO_IPV6, .hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP_PRI_FIRST },
{ .hook = pfk_ipv6_hook, .pf = NFPROTO_IPV6, .hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP_PRI_FIRST + 1 },
{ .hook = pfk_ipv6_hook, .pf = NFPROTO_IPV6, .hooknum = NF_INET_FORWARD,
.priority = NF_IP_PRI_FIRST },
{ .hook = pfk_ipv6_hook, .pf = NFPROTO_IPV6, .hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_CONNTRACK + 1 },
{ .hook = pfk_ipv6_hook, .pf = NFPROTO_IPV6, .hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_LAST - 1 },
};
#define PFK_NHOOKS ARRAY_SIZE(pfk_hooks)
@@ -53,7 +153,8 @@ pfk_hooks_register(void)
{
int i, err;
pr_info("pfk: netfilter hooks registered (verdict path inert until M2)\n");
pr_info("pfk: netfilter hooks registering (%zu, verdict path live)\n",
(size_t)PFK_NHOOKS);
for (i = 0; i < PFK_NHOOKS; i++) {
pfk_hooks[i].priv = NULL;
err = nf_register_net_hook(&init_net, &pfk_hooks[i]);
+25
View File
@@ -91,6 +91,24 @@ static struct miscdevice pfk_dev = {
.mode = 0600,
};
extern void pf_status_init(void);
extern int pfattach(int);
static void
pfk_pfi_event(int added, struct pfk_ifnet *ifp)
{
if (added)
pfi_attach_ifnet(ifp);
else
pfi_detach_ifnet(ifp);
}
static void
pfk_pfi_attach_one(struct pfk_ifnet *ifp)
{
pfi_attach_ifnet(ifp);
}
static int __init pfk_init(void)
{
int err;
@@ -105,6 +123,13 @@ static int __init pfk_init(void)
err = pfk_if_init();
if (err)
goto err_pcb;
/* OpenBSD bring-up order: status/pools/rulesets, then kifs */
pfk_net_lock();
pf_status_init();
pfattach(0);
pfk_if_set_event_cb(pfk_pfi_event);
pfk_if_iterate(pfk_pfi_attach_one);
pfk_net_unlock();
err = pfk_hooks_register();
if (err)
goto err_if;
+4
View File
@@ -0,0 +1,4 @@
# first M2 smoke: allow everything on lan/wan, block icmp to blackhole
pass in quick on lan
pass out quick
block drop in quick proto icmp from any to 10.0.2.99
+9
View File
@@ -0,0 +1,9 @@
# PFLINUX build products
out/
# generated copies (regenerated by `make` each build; never hand-edit)
include/net/pfvar.h
include/net/radix.h
include/net/hfsc.h
include/net/fq_codel.h
include/sys/tree.h
include/sys/queue.h
+180
View File
@@ -0,0 +1,180 @@
LDFLAGS ?= -static
# PFLINUX — build OpenBSD 8.0 pfctl userland for Linux.
#
# Layout:
# include/ compat headers (sys/ioctl.h with OpenBSD _IO encoding,
# net/{route,if,if_dl}.h, netinet/tcp_fsm.h, md5.h, ...)
# plus copies generated at build time:
# include/net/pfvar.h <- ../module/include/net/pfvar.h
# include/sys/{queue,tree}.h, include/net/{radix,hfsc,
# fq_codel}.h <- ../_vendor/upstream/sys/...
# pfctl/ pristine OpenBSD 8.0 sbin/pfctl sources + marked edits
# compat-user/ glibc missing pieces (sysctl, strtonum, md5, tcpstates)
# abitest/ ABI struct layout table (built twice: tools/include and
# module/include, compared, MD5-verified)
# out/ build products (make clean removes)
#
# Contract: `make -C tools` must work with plain make/gcc/bison.
TOP := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
ROOT := $(abspath $(TOP)/..)
MODULE := $(ROOT)/module
VENDOR := $(ROOT)/_vendor/upstream
SRC := $(TOP)/pfctl
COMPAT := $(TOP)/compat-user
INC := $(TOP)/include
OUT := $(TOP)/out
OBJ := $(OUT)/obj
GEN := $(OUT)/gen
CC ?= cc
BISON ?= bison
# PFLINUX: include order is ABI-critical. $(INC) first so sys/ioctl.h
# (OpenBSD PFKXIOC encoding, identical to module/include/pfk.h),
# net/pfvar.h, sys/{queue,tree}.h, net/{route,if,if_dl}.h,
# netinet/tcp_fsm.h and md5.h shadow the Linux system headers.
# pflinux_compat.h is force-included (attribute macros, strtonum,
# strings.h) before any source's own #includes.
CPPFLAGS += -I$(INC) -I$(GEN) -include $(INC)/pflinux_compat.h
CFLAGS ?= -O2 -g
CFLAGS += -std=gnu17 -Wall -D_GNU_SOURCE
LDLIBS += -lm
# Headers copied verbatim at build time (never edited; single source of
# truth stays in module/ and _vendor/).
GENHDRS := $(INC)/net/pfvar.h $(INC)/net/radix.h $(INC)/net/hfsc.h \
$(INC)/net/fq_codel.h $(INC)/sys/tree.h $(INC)/sys/queue.h
PF_SRCS := pfctl.c pfctl_parser.c pf_print_state.c pfctl_table.c \
pfctl_queue.c pfctl_radix.c pfctl_osfp.c pfctl_optimize.c
CB_SRCS := sysctl.c strtonum.c bsd.c md5.c rbtree.c inet_net_pton.c
PF_OBJS := $(addprefix $(OBJ)/,$(PF_SRCS:.c=.o)) $(OBJ)/parse.o
CB_OBJS := $(addprefix $(OBJ)/,$(CB_SRCS:.c=.o))
GOODTESTS := tests/01_basic.conf tests/02_translations.conf \
tests/03_queues_scrub.conf
.PHONY: all pfctl abitest test abi clean
all: pfctl abitest
pfctl: $(OUT)/pfctl
# ---------------------------------------------------------------------------
# generated (copied) headers
# ---------------------------------------------------------------------------
# Contract: userland MUST use the exact kernel-side ABI header.
$(INC)/net/pfvar.h: $(MODULE)/include/net/pfvar.h
@mkdir -p $(@D)
@cmp -s $< $@ || cp -f $< $@
$(INC)/net/radix.h: $(VENDOR)/sys/net/radix.h
@mkdir -p $(@D)
@cmp -s $< $@ || cp -f $< $@
$(INC)/net/hfsc.h: $(VENDOR)/sys/net/hfsc.h
@mkdir -p $(@D)
@cmp -s $< $@ || cp -f $< $@
$(INC)/net/fq_codel.h: $(VENDOR)/sys/net/fq_codel.h
@mkdir -p $(@D)
@cmp -s $< $@ || cp -f $< $@
$(INC)/sys/tree.h: $(VENDOR)/sys/sys/tree.h
@mkdir -p $(@D)
@cmp -s $< $@ || cp -f $< $@
$(INC)/sys/queue.h: $(VENDOR)/sys/sys/queue.h
@mkdir -p $(@D)
@cmp -s $< $@ || cp -f $< $@
# ---------------------------------------------------------------------------
# grammar: OpenBSD pfctl has a hand-written lexer inside parse.y; only
# bison is required (flex is NOT used).
# ---------------------------------------------------------------------------
$(GEN)/parse.c: $(SRC)/parse.y $(GENHDRS)
@mkdir -p $(GEN)
$(BISON) -d -o $@ $<
$(OBJ)/%.o: $(SRC)/%.c $(GENHDRS)
@mkdir -p $(OBJ)
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
$(OBJ)/parse.o: $(GEN)/parse.c $(SRC)/pfctl.h $(SRC)/pfctl_parser.h
@mkdir -p $(OBJ)
$(CC) $(CPPFLAGS) $(CFLAGS) -I$(SRC) -c $< -o $@
$(OBJ)/%.o: $(COMPAT)/%.c $(GENHDRS)
@mkdir -p $(OBJ)
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
# PFLINUX: OpenBSD 8.0 pfctl links the kernel's pf_ruleset.c userland
# branch ("Effort sponsored... Userland equivalents so we can lend code
# to pfctl") for anchor/ruleset management (pf_anchor_setup,
# pf_find_or_create_ruleset, pf_main_anchor, ...). Compiled straight
# from _vendor, unmodified.
$(OBJ)/pf_ruleset.o: $(VENDOR)/sys/net/pf_ruleset.c $(GENHDRS)
@mkdir -p $(OBJ)
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
$(OUT)/pfctl: $(PF_OBJS) $(CB_OBJS) $(OBJ)/pf_ruleset.o
@mkdir -p $(OUT)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(PF_OBJS) $(CB_OBJS) $(OBJ)/pf_ruleset.o $(LDLIBS)
# ---------------------------------------------------------------------------
# ABI table: same abitest.c compiled twice —
# out/abitest : headers from tools/include (copied pfvar.h)
# out/abitest-module : tools/include shims but net/pfvar.h taken directly
# from ../module/include (kernel ABI header)
# The two tables must be byte-identical (proof: MD5 over the table).
# The kernel-module-side check (structs compiled with the real kernel
# compat headers under -D_KERNEL) is done separately by the integrator.
# ---------------------------------------------------------------------------
abitest: $(OUT)/abitest $(OUT)/abitest-module
$(OBJ)/abitest-user.o: $(TOP)/abitest/abitest.c $(GENHDRS)
@mkdir -p $(OBJ)
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
$(OBJ)/abitest-module.o: $(TOP)/abitest/abitest.c $(GENHDRS)
@mkdir -p $(OBJ)
$(CC) -I$(MODULE)/include $(CPPFLAGS) $(CFLAGS) -c $< -o $@
$(OUT)/abitest: $(OBJ)/abitest-user.o $(OBJ)/md5.o
@mkdir -p $(OUT)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)
$(OUT)/abitest-module: $(OBJ)/abitest-module.o $(OBJ)/md5.o
@mkdir -p $(OUT)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS)
abi: abitest
@$(OUT)/abitest > $(OUT)/abi-tools.tbl
@$(OUT)/abitest-module > $(OUT)/abi-module.tbl
@diff -u $(OUT)/abi-tools.tbl $(OUT)/abi-module.tbl && \
echo "ABI OK: identical tables"
@tail -n 1 $(OUT)/abi-tools.tbl
# ---------------------------------------------------------------------------
# offline (-n) parse validation, no /dev/pf required
# ---------------------------------------------------------------------------
test: $(OUT)/pfctl
@ok=1; for c in $(GOODTESTS); do \
if $(OUT)/pfctl -n -f $$c >/dev/null 2>&1; then \
echo "PASS $$c (exit 0)"; \
else \
echo "FAIL $$c (exit $$?)"; ok=0; \
fi; \
done; \
if $(OUT)/pfctl -n -f tests/broken.conf >/dev/null 2>&1; then \
echo "FAIL tests/broken.conf accepted"; ok=0; \
else \
echo "PASS tests/broken.conf rejected (non-zero exit)"; \
fi; \
test $$ok -eq 1
clean:
rm -rf $(OUT)
rm -f $(GENHDRS)
+126
View File
@@ -0,0 +1,126 @@
# PFLINUX — OpenBSD 8.0 pfctl on Linux: upstream provenance and edits
Sources: `_vendor/upstream/sbin/pfctl/` (OpenBSD 8.0-current, see
`_vendor/upstream/IDS.txt`), ABI header: `module/include/net/pfvar.h`
(v1.548), ioctl encoding: `module/include/pfk.h`.
This document lists every deviation from the pristine vendor sources, the
compat layer added under `tools/`, and the warnings emitted by the build.
## Build-time header copies (Makefile rules, never hand-edited)
| tools/include/… | copied from |
|----------------------------|------------------------------------------|
| `net/pfvar.h` | `module/include/net/pfvar.h` (contract) |
| `sys/tree.h`, `sys/queue.h`| `_vendor/upstream/sys/sys/{tree,queue}.h`|
| `net/radix.h` | `_vendor/upstream/sys/net/radix.h` |
| `net/hfsc.h`, `net/fq_codel.h` | `_vendor/upstream/sys/net/{hfsc,fq_codel}.h` |
`make abi` compiles `abitest.c` once against each header set and diffs the
tables + MD5, proving the copy is in lockstep with `module/include`.
## Compat headers (hand-written, tools/include/)
| file | purpose |
|------|---------|
| `sys/ioctl.h` | glibc `<sys/ioctl.h>` via `#include_next`, then `_IO/_IOW/_IOR/_IOWR` redefined to the PFKXIOC (OpenBSD ioccom.h) encoding copied verbatim from `module/include/pfk.h` (NRBITS 8, TYPEBITS 8, SIZEBITS 13, DIRBITS 2, NRSHIFT 0, TYPESHIFT 8, SIZESHIFT 16, DIRSHIFT 30, NONE 0, WRITE 1, READ 2). This makes every `DIOC*` number in pfvar.h identical to what pf.ko decodes. |
| `pflinux_compat.h` | force-included (`-include`): `<sys/types.h>`, `<strings.h>` (bzero/bcopy), `offsetof`, OpenBSD cdefs fallbacks `__dead`/`__printflike`/`__packed`/`__unused`/`__inline`, `HTONL/NTOL/HTONS/NOSH` (assignment-style, `<sys/param.h>`), `AF_LINK` → `AF_PACKET` (parse.y uses it without `<net/if_dl.h>`), prototypes for `strtonum`, `fgetln`, `freezero`, `uid_from_user`, `gid_from_group`, `pledge`, `unveil`. |
| `sys/sysctl.h` | CTL_*/KERN_MAXCLUSTERS(67)/HW_PHYSMEM64(19) constants (OpenBSD values) + `sysctl()` prototype (glibc dropped `<sys/sysctl.h>`). |
| `sys/syslimits.h` | → `<limits.h>` (PATH_MAX etc. for pfvar.h). |
| `sys/_null.h` | OpenBSD NULL header needed by vendored queue.h/tree.h. |
| `sys/refcnt.h` | userland `struct refcnt { unsigned int rc; }` — same size as the module kernel compat (`refcount_t` = one int); referenced by struct pf_anchor only, never crosses ioctl. |
| `sys/timeout.h` | minimal `struct timeout` — pfvar.h embeds it in `struct pf_pktdelay`, which is kernel-internal (userland state payloads use `struct pfsync_state`). |
| `net/route.h` | userland subset pfvar.h/parse.y need: RTLABEL_LEN 32, RT_TABLEID_MAX 32 (**matches `module/include/pfk.h`**, not OpenBSD's 255), NET_RT_* numbers, `struct rt_tableinfo` (verbatim from obbsd route.h), forward `struct rtentry`. AF_ROUTE/PF_ROUTE come from glibc `<sys/socket.h>` (both are 17 there, same as OpenBSD). |
| `net/if.h` | glibc header via `#include_next` + OpenBSD interface-group ABI (`struct ifg_req`, `struct ifgroupreq`, `SIOCGIFGMEMB` with the OpenBSD number, IFQ_NQUEUES/IFQ_MAXPRIO). Linux has no interface groups: the ioctl fails ENOTTY and group names degrade to "no such group", like an OpenBSD box missing the group. |
| `net/if_dl.h` | `AF_LINK` ≡ `AF_PACKET`; `struct sockaddr_dl` laid out to mirror Linux `sockaddr_ll` so pfctl_parser.c's `sdl_index` cast reads `sll_ifindex` (offset 4). AF_LINK is userland-internal (iftab bookkeeping); it never enters ioctl payloads. |
| `netinet/tcp_fsm.h` | TCPS_* states + TCP_NSTATES 11 (obbsd values); `tcpstates[]` extern (defined in compat). |
| `netinet/ip_icmp.h`, `netinet/icmp6.h`, `netinet/ip.h` | `#include_next` + the ICMP*/ICMP6_*/MLD_*/ND_*/IPTOS_DSCP_CS* constants glibc lacks (verbatim obbsd values). |
| `md5.h` | copy of `_vendor/upstream/sys/crypto/md5.h` minus the kernel-only `__bounded__` annotations. |
## Compat sources (tools/compat-user/)
| file | purpose |
|------|---------|
| `sysctl.c` | `sysctl(3)` emulation for exactly the three MIBs pfctl uses: HW_PHYSMEM64 (sysconf), KERN_MAXCLUSTERS (physmem/2048, the OpenBSD RAM-based sizing), NET_RT_TABLE (`struct rt_tableinfo`, always "exists" for ids ≤ RT_TABLEID_MAX — mirrors module `compat/pfk_rtable.c`: Linux has no per-rtableid FIBs, `rtable_exists()` accepts every id). |
| `strtonum.c` | BSD `strtonum(3)` (glibc has none). |
| `md5.c` | vendor `_vendor/upstream/sys/crypto/md5.c`, **only** the three include lines replaced (`sys/param.h`,`sys/systm.h`,`crypto/md5.h` → `sys/types.h`,`endian.h`,`string.h`,`md5.h`); algorithm byte-identical. |
| `rbtree.c` | OpenBSD's out-of-line generic RB tree (`_rb_insert/_rb_find/_rb_remove/_rb_min/_rb_next/...`) that `sys/tree.h`'s RBT_* macros call; OpenBSD ships it in libc, glibc has none. CLRS-based, verified by a 30k-op/12-seed fuzz harness (order, parent-links, bidirectional walks, find identity). |
| `bsd.c` | `tcpstates[]` (obbsd text), `pledge`/`unveil` no-op stubs (unused by 8.0 sources, kept for future syncs), `fgetln()` over `getline()` with OpenBSD `*len` semantics, `freezero()`, `uid_from_user()`, `gid_from_group()`. |
| `inet_net_pton.c` | BSD `inet_net_pton()` for AF_INET abbreviated CIDR ("10/8", classful defaults) + AF_INET6; glibc 2.43 declares it but exports no symbol. |
`pfctl/pf_ruleset.c`: not copied — the Makefile compiles
`_vendor/upstream/sys/net/pf_ruleset.c` **unmodified** into pfctl (its
`!_KERNEL` branch exists precisely "so we can lend code to pfctl", providing
`pf_anchor_setup`, `pf_find_or_create_ruleset`, `pf_main_anchor`, …).
Verified with `cmp` against vendor on every build (file read-only here).
## Grammar toolchain
Only bison is needed: OpenBSD `parse.y` carries a hand-written `yylex()`
(`flex` unused). bison 3.8 accepts the OpenBSD yacc idioms used here —
prologue `typedef struct {union{...} v; int lineno;} YYSTYPE` plus
`%type <v.member>` dotted tags — with zero pre-processing. Duplicate
`%token RTABLE/RDOMAIN` lines produce warnings (upstream text).
## Edits to the copied pfctl sources (all marked in-file with `/* PFLINUX */`)
1. **parse.y** (declarations section): `expand_label_addr`/`expand_label`
prototypes: address-family parameter `u_int8_t` → `sa_family_t`.
Linux `sa_family_t` is `unsigned short` (OpenBSD: `unsigned char`); the
matching definitions in the same file already use `sa_family_t`.
2. **pfctl_parser.c** (`ifa_load`): dropped two
`ifa_broadaddr->sa_len != 0` / `ifa_dstaddr->sa_len != 0` guards —
glibc `struct sockaddr` has no `sa_len`; `getifaddrs(3)` only fills those
members when a broadcast/destination address actually exists.
3. **pfctl_parser.h**: `geticmptypebynumber`, `geticmptypebyname`,
`geticmpcodebynumber`, `geticmpcodebyname` prototype af parameters
`u_int8_t` → `sa_family_t` (same reason as #1).
4. **pfctl_parser.c**: `print_fromto` forward prototype, af parameter
`u_int8_t` → `sa_family_t`.
5. **pf_print_state.c** (`print_name`): removed `sin_len`/`sin6_len`
assignments; added local `socklen_t slen` set to `sizeof(*sin|*sin6)` and
passed to `getnameinfo(3)` (no `ss_len` on Linux).
6. **pfctl_table.c** (`print_ad` reverse-DNS): identical `sa_len` removal
pattern as #5.
7. **pfctl_osfp.c** (`pfctl_file_fingerprints`): a missing `/etc/pf.os`
(ENOENT) returns success instead of warn+fail — OpenBSD ships the file in
base; this port has no fingerprint packaging yet. Other open errors still
fatal. (Without this, every `-f` load exits 1 on stock Linux.)
No other lines differ from vendor; `cmp` each file against
`_vendor/upstream/sbin/pfctl/` to audit (`diff -u` shows only the sites above).
## ABI table notes (tools/abitest/abitest.c)
* The contract's `pf_state_key` is kernel-only in 8.0 (moved to
`<net/pfvar_priv.h>`, which userland MUST NOT include). The table
therefore probes its userland mirror `struct pf_state_key_cmp`
("keep synced with struct pf_state_key, used in RB_FIND") plus the
wire-format `struct pfsync_state_key` / `struct pfsync_state` used by
`DIOCGETSTATE` payloads. The kernel-side comparison of the real
`struct pf_state_key` is the integrator's job, same as for all
`-D_KERNEL` layout checks.
## Known warnings (intentional, benign)
* `-Wformat=` (×51): `%lld`/`%llu` vs `long` printf conversions. OpenBSD
prints 64-bit values with `%ll*`; LP64 glibc types differ in spelling only.
Output text is identical.
* `-Waddress-of-packed-member` (×6): `struct pfsync_state` is `__packed`
(wire format shared with pf.ko); pf_print_state.c takes addresses of its
members. Access is alignment-safe on x86-64 (no AVX requirement).
* `-Wother` (×6): bison "symbol RTABLE/RDOMAIN redeclared" — upstream
parse.y declares these tokens twice.
* `-Wpointer-sign` (×15) / `-Wformat-truncation` (×10) / `-Wdiscarded-qualifiers`
(×2): snprintf/`char*` signedness and const-preserving idioms from OpenBSD
sources (e.g. `strrchr` result into non-const, GCC's path-insensitive
truncation heuristics). Non-fatal, semantics unchanged.
## Out of scope / pending
* `pdivert` and `pflogd` — intentionally NOT ported here; owned by the
integrator. (No skeleton stubs are shipped.)
* Kernel-side ABI confirmation (same table compiled under `-D_KERNEL` with
the module compat headers) — integrator step; `out/abi-tools.tbl` /
`out/abi-module.tbl` + `out/abitest` md5 line are the reference inputs.
* Runtime paths (`-s`, `-F`, live `-f` load) require `/dev/pf` from pf.ko and
were exercised only through the `-n` (offline) code path here.
+126
View File
@@ -0,0 +1,126 @@
/*
* PFLINUX ABI table (acceptance item 3).
*
* Computes sizeof()/offsetof() of the key pf structs shared between this
* userland and pf.ko, compiled against tools/include (or, when built as
* out/abitest-module, against ../module/include/net/pfvar.h directly).
* Prints a deterministic table plus an MD5 digest of it; equal digests
* between the two builds prove the userland copy and the kernel-side
* staging header agree. The kernel-internal side of the comparison
* (structs compiled with the module compat headers under -D_KERNEL) is
* performed separately by the integrator.
*
* Note: OpenBSD 8.0 moved struct pf_state_key into <net/pfvar_priv.h>
* (kernel-only). The userland-visible mirror is struct
* pf_state_key_cmp ("keep synced with struct pf_state_key, used in
* RB_FIND") plus struct pfsync_state_key; both are tabulated below.
*/
#include <sys/types.h>
#include <net/if.h>
#include <net/pfvar.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <md5.h>
_Static_assert(sizeof(struct pf_addr) == 16, "pf_addr must be 16 bytes");
_Static_assert(sizeof(((struct pf_addr *)NULL)->addr32) == 16,
"pf_addr union must be 16 bytes");
_Static_assert(sizeof(((struct pf_status *)NULL)->ifname) == IFNAMSIZ,
"pf_status.ifname sized by IFNAMSIZ");
_Static_assert(PF_MD5_DIGEST_LENGTH == MD5_DIGEST_LENGTH,
"pfvar.h and md5.h digest length agree");
struct probe {
const char *name;
size_t size;
size_t off1;
size_t off2;
size_t off3;
};
#define P(st, a) { #st "." #a, sizeof(struct st), \
offsetof(struct st, a), \
offsetof(struct st, a), \
offsetof(struct st, a) }
#define P3(st, a, b, c) { #st "." #a "/" #b "/" #c, sizeof(struct st), \
offsetof(struct st, a), \
offsetof(struct st, b), \
offsetof(struct st, c) }
static const struct probe probes[] = {
/* struct, off1, off2, off3 */
P3(pf_addr, pfa, pfa, pfa),
P3(pf_status, counters, hostid, pf_chksum),
P3(pf_state_key_cmp, addr, port, rdomain),
P3(pfsync_state_key, addr, port, rdomain),
P3(pfsync_state, id, ifname, key),
P3(pfioc_state, state, state, state),
P3(pf_rule, src, ifname, rtableid),
P3(pf_rule, nat, rdr, route),
P3(pf_rule, evaluations, anchor, timeout),
P3(pfioc_rule, action, anchor, rule),
P3(pf_rule_addr, addr, neg, port_op),
P3(pf_addr_wrap, v, type, iflags),
P3(pf_pool, addr, key, states),
P3(pf_queuespec, qname, ifname, flowqueue),
P3(pfioc_queue, ticket, nr, queue),
P3(pfi_kif, pfik_name, pfik_packets, pfik_states),
P3(pfr_tstats, pfrts_t, pfrts_match, pfrts_cnt),
P3(pfr_addr, pfra_u, pfra_af, pfra_net),
P3(pfioc_table, pfrio_table, pfrio_buffer, pfrio_esize),
P3(pfioc_trans_e, type, anchor, ticket),
P3(pf_osfp_ioctl, fp_os, fp_wsize, fp_psize),
P3(pfr_kcounters, pfrkc_packets, pfrkc_bytes, states),
};
int
main(void)
{
char buf[4096];
size_t n = 0;
MD5_CTX mc;
u_int8_t dg[MD5_DIGEST_LENGTH];
int i;
printf("%-24s %8s %8s %8s %8s\n",
"type.member", "size", "off1", "off2", "off3");
printf("----------------------------------------"
"-----------------------------\n");
for (i = 0; i < (int)(sizeof(probes)/sizeof(probes[0])); i++) {
char line[256];
int len;
len = snprintf(line, sizeof(line), "%-24s %8zu %8zu %8zu %8zu\n",
probes[i].name, probes[i].size, probes[i].off1,
probes[i].off2, probes[i].off3);
if (len < 0 || (size_t)len >= sizeof(line)) {
fprintf(stderr, "line too long\n");
return (1);
}
fputs(line, stdout);
if (n + strlen(line) >= sizeof(buf)) {
fprintf(stderr, "table buffer overflow\n");
return (1);
}
memcpy(buf + n, line, strlen(line));
n += strlen(line);
}
printf("----------------------------------------"
"-----------------------------\n");
printf("%d entries\n", (int)(sizeof(probes)/sizeof(probes[0])));
/* MD5 of just the table body (deterministic across both builds) */
MD5Init(&mc);
MD5Update(&mc, buf, n);
MD5Final(dg, &mc);
printf("md5 ");
for (i = 0; i < MD5_DIGEST_LENGTH; i++)
printf("%02x", dg[i]);
printf("\n");
return (0);
}
+117
View File
@@ -0,0 +1,117 @@
/* PFLINUX: small bag of BSD-isms glibc lacks, referenced by the copied
* 8.0 pfctl sources (see tools/UPSTREAM.md). */
#include <sys/types.h>
#include <stdio.h>
/* pf_print_state.c: TCP state names, normally provided by OpenBSD
* <netinet/tcp_fsm.h> under TCPSTATES (array text copied verbatim from
* _vendor/openbsd/obbsd/sys/netinet/tcp_fsm.h). */
const char *const tcpstates[] = {
"CLOSED", "LISTEN", "SYN_SENT", "SYN_RCVD",
"ESTABLISHED", "CLOSE_WAIT", "FIN_WAIT_1", "CLOSING",
"LAST_ACK", "FIN_WAIT_2", "TIME_WAIT",
};
/*
* pledge(2)/unveil(2): BSD-only process sandboxes. OpenBSD 8.0 pfctl
* sources do not call them, but the no-op stubs keep future upstream
* syncs building without changes here.
*/
int
pledge(const char *promises, const char *paths[])
{
(void)promises;
(void)paths;
return (0);
}
int
unveil(const char *path, const char *permissions)
{
(void)path;
(void)permissions;
return (0);
}
/*
* fgetln(3): BSD stdio line reader (returns the line *including* the
* trailing newline, sets *len, no NUL terminator). Used by
* pfctl_osfp.c to read fingerprint files. Backed by glibc getline(3);
* the buffer is valid until the next call on the same stream, matching
* the single-file usage in pfctl.
*/
char *
fgetln(FILE *stream, size_t *len)
{
static char *buf;
static size_t bufsz;
ssize_t n;
n = getline(&buf, &bufsz, stream);
if (n == -1)
return (NULL);
/* OpenBSD fgetln(): *len INCLUDES the trailing newline (when
* present) and the buffer is NUL-terminated as an extension;
* getline() already gives exactly that. */
*len = n;
return (buf);
}
/*
* uid_from_user(3)/gid_from_group(3): OpenBSD <pwd.h>/<grp.h> helpers
* used by parse.y for the `user`/`group` filter options. OpenBSD
* returns 0 on success; on failure returns -1 and sets errno (ENOENT
* when the name is unknown).
*/
#include <pwd.h>
#include <grp.h>
#include <errno.h>
int
uid_from_user(const char *name, uid_t *uidp)
{
struct passwd *pw;
errno = 0;
if ((pw = getpwnam(name)) == NULL) {
if (errno == 0)
errno = ENOENT;
return (-1);
}
if (uidp != NULL)
*uidp = pw->pw_uid;
return (0);
}
int
gid_from_group(const char *name, gid_t *gidp)
{
struct group *gr;
errno = 0;
if ((gr = getgrnam(name)) == NULL) {
if (errno == 0)
errno = ENOENT;
return (-1);
}
if (gidp != NULL)
*gidp = gr->gr_gid;
return (0);
}
/*
* freezero(3): OpenBSD secure realloc-free. Used by pf_ruleset.c's
* userland rs_free(). (explicit_bzero is in glibc <string.h>.)
*/
#include <string.h>
void
freezero(void *ptr, size_t siz)
{
if (ptr == NULL)
return;
explicit_bzero(ptr, siz);
free(ptr);
}
+155
View File
@@ -0,0 +1,155 @@
/*
* PFLINUX: BSD inet_net_pton(3) for pfctl on glibc. glibc 2.43 declares
* the function in <arpa/inet.h> but no longer provides the symbol (the
* resolver dropped it); libc.so.6 exports neither. pfctl_parser.c uses
* it only for AF_INET abbreviated-CIDR parsing ("10/8", "10.0.0.0/255...",
* "10.1.2" classful). Semantics implemented per OpenBSD
* lib/libc/net/inet_net_pton.c:
* - returns prefix length on success, -1 with errno on failure.
* - writes the network address (masked) to pbin.
* - AF_INET6 accepts CIDR via inet_pton (RFC 5952 form), default /128.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int
netmask_bits(uint32_t mask)
{
int bits = 0;
while ((mask & 0x80000000u) != 0) {
bits++;
mask <<= 1;
}
return ((mask == 0) ? bits : -1); /* must be contiguous */
}
static int
pton4(const char *pres, struct in_addr *pin, size_t binsize)
{
char copy[128], *sp;
struct in_addr base;
uint32_t hostaddr, mask;
int bits = -1;
if (strlen(pres) >= sizeof(copy)) {
errno = EMSGSIZE;
return (-1);
}
strcpy(copy, pres);
sp = strchr(copy, '/');
if (sp == NULL) {
if (!inet_aton(copy, &base))
return (-1);
hostaddr = ntohl(base.s_addr);
/* classful defaults, matching BSD */
if (hostaddr < 0x80000000u)
bits = 8;
else if (hostaddr < 0xC0000000u)
bits = 16;
else
bits = 24;
} else {
*sp++ = '\0';
if (!inet_aton(copy, &base))
return (-1);
if (strchr(sp, '.')) {
struct in_addr m;
if (!inet_aton(sp, &m))
return (-1);
bits = netmask_bits(ntohl(m.s_addr));
if (bits < 0)
return (-1);
} else {
char *end;
long v;
if (*sp == '\0')
return (-1);
v = strtol(sp, &end, 10);
if (end == sp || *end != '\0' || v < 0 || v > 32)
return (-1);
bits = (int)v;
}
}
hostaddr = ntohl(base.s_addr);
mask = bits == 0 ? 0 : (0xffffffffu << (32 - bits));
hostaddr &= mask;
if (binsize < sizeof(base)) {
errno = EMSGSIZE;
return (-1);
}
pin->s_addr = htonl(hostaddr);
return (bits);
}
static int
pton6(const char *pres, void *pbin, size_t binsize)
{
char copy[128], *sp;
int bits = 128;
if (strlen(pres) >= sizeof(copy)) {
errno = EMSGSIZE;
return (-1);
}
strcpy(copy, pres);
sp = strchr(copy, '/');
if (sp != NULL) {
*sp++ = '\0';
if (*sp == '\0')
return (-1);
bits = (int)strtol(sp, NULL, 10);
if (bits < 0 || bits > 128)
return (-1);
}
/* glibc inet_pton accepts bare addresses; mask by prefix by hand */
if (binsize < sizeof(struct in6_addr)) {
errno = EMSGSIZE;
return (-1);
}
if (inet_pton(AF_INET6, copy, pbin) != 1)
return (-1);
if (bits < 128) {
uint8_t *a = pbin;
int i;
for (i = bits / 8; i < 16; i++)
a[i] = 0;
a[bits / 8] &= (uint8_t)(0xffu << (8 - (bits % 8)));
}
return (bits);
}
int
inet_net_pton(int af, const char *pres, void *pbin, size_t binsize)
{
if (pres == NULL || pbin == NULL) {
errno = EFAULT;
return (-1);
}
switch (af) {
case AF_INET:
return (pton4(pres, pbin, binsize));
case AF_INET6:
return (pton6(pres, pbin, binsize));
default:
errno = EAFNOSUPPORT;
return (-1);
}
}
+242
View File
@@ -0,0 +1,242 @@
/* $OpenBSD: md5.c,v 1.4 2014/12/28 10:04:35 tedu Exp $ */
/*
* This code implements the MD5 message-digest algorithm.
* The algorithm is due to Ron Rivest. This code was
* written by Colin Plumb in 1993, no copyright is claimed.
* This code is in the public domain; do with it what you wish.
*
* Equivalent code is available from RSA Data Security, Inc.
* This code has been tested against that, and is equivalent,
* except that you don't need to include two pages of legalese
* with every copy.
*
* To compute the message digest of a chunk of bytes, declare an
* MD5Context structure, pass it to MD5Init, call MD5Update as
* needed on buffers full of bytes, and then call MD5Final, which
* will fill a supplied 16-byte array with the digest.
*/
/* PFLINUX: OpenBSD kernel includes <sys/param.h>/<sys/systm.h>/<crypto/md5.h>
* replaced by their glibc/userland equivalents; rest of the file is the
* verbatim vendor MD5 (tools/include/md5.h, string.h, endian.h). */
#include <sys/types.h>
#include <endian.h>
#include <string.h>
#include <md5.h>
#define PUT_64BIT_LE(cp, value) do { \
(cp)[7] = (value) >> 56; \
(cp)[6] = (value) >> 48; \
(cp)[5] = (value) >> 40; \
(cp)[4] = (value) >> 32; \
(cp)[3] = (value) >> 24; \
(cp)[2] = (value) >> 16; \
(cp)[1] = (value) >> 8; \
(cp)[0] = (value); } while (0)
#define PUT_32BIT_LE(cp, value) do { \
(cp)[3] = (value) >> 24; \
(cp)[2] = (value) >> 16; \
(cp)[1] = (value) >> 8; \
(cp)[0] = (value); } while (0)
static u_int8_t PADDING[MD5_BLOCK_LENGTH] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/*
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
* initialization constants.
*/
void
MD5Init(MD5_CTX *ctx)
{
ctx->count = 0;
ctx->state[0] = 0x67452301;
ctx->state[1] = 0xefcdab89;
ctx->state[2] = 0x98badcfe;
ctx->state[3] = 0x10325476;
}
/*
* Update context to reflect the concatenation of another buffer full
* of bytes.
*/
void
MD5Update(MD5_CTX *ctx, const void *inputptr, size_t len)
{
const uint8_t *input = inputptr;
size_t have, need;
/* Check how many bytes we already have and how many more we need. */
have = (size_t)((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1));
need = MD5_BLOCK_LENGTH - have;
/* Update bitcount */
ctx->count += (u_int64_t)len << 3;
if (len >= need) {
if (have != 0) {
memcpy(ctx->buffer + have, input, need);
MD5Transform(ctx->state, ctx->buffer);
input += need;
len -= need;
have = 0;
}
/* Process data in MD5_BLOCK_LENGTH-byte chunks. */
while (len >= MD5_BLOCK_LENGTH) {
MD5Transform(ctx->state, input);
input += MD5_BLOCK_LENGTH;
len -= MD5_BLOCK_LENGTH;
}
}
/* Handle any remaining bytes of data. */
if (len != 0)
memcpy(ctx->buffer + have, input, len);
}
/*
* Final wrapup - pad to 64-byte boundary with the bit pattern
* 1 0* (64-bit count of bits processed, MSB-first)
*/
void
MD5Final(unsigned char digest[MD5_DIGEST_LENGTH], MD5_CTX *ctx)
{
u_int8_t count[8];
size_t padlen;
int i;
/* Convert count to 8 bytes in little endian order. */
PUT_64BIT_LE(count, ctx->count);
/* Pad out to 56 mod 64. */
padlen = MD5_BLOCK_LENGTH -
((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1));
if (padlen < 1 + 8)
padlen += MD5_BLOCK_LENGTH;
MD5Update(ctx, PADDING, padlen - 8); /* padlen - 8 <= 64 */
MD5Update(ctx, count, 8);
for (i = 0; i < 4; i++)
PUT_32BIT_LE(digest + i * 4, ctx->state[i]);
explicit_bzero(ctx, sizeof(*ctx)); /* in case it's sensitive */
}
/* The four core functions - F1 is optimized somewhat */
/* #define F1(x, y, z) (x & y | ~x & z) */
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
/* This is the central step in the MD5 algorithm. */
#define MD5STEP(f, w, x, y, z, data, s) \
( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
/*
* The core of the MD5 algorithm, this alters an existing MD5 hash to
* reflect the addition of 16 longwords of new data. MD5Update blocks
* the data and converts bytes into longwords for this routine.
*/
void
MD5Transform(u_int32_t state[4], const u_int8_t block[MD5_BLOCK_LENGTH])
{
u_int32_t a, b, c, d, in[MD5_BLOCK_LENGTH / 4];
#if BYTE_ORDER == LITTLE_ENDIAN
memcpy(in, block, sizeof(in));
#else
for (a = 0; a < MD5_BLOCK_LENGTH / 4; a++) {
in[a] = (u_int32_t)(
(u_int32_t)(block[a * 4 + 0]) |
(u_int32_t)(block[a * 4 + 1]) << 8 |
(u_int32_t)(block[a * 4 + 2]) << 16 |
(u_int32_t)(block[a * 4 + 3]) << 24);
}
#endif
a = state[0];
b = state[1];
c = state[2];
d = state[3];
MD5STEP(F1, a, b, c, d, in[ 0] + 0xd76aa478, 7);
MD5STEP(F1, d, a, b, c, in[ 1] + 0xe8c7b756, 12);
MD5STEP(F1, c, d, a, b, in[ 2] + 0x242070db, 17);
MD5STEP(F1, b, c, d, a, in[ 3] + 0xc1bdceee, 22);
MD5STEP(F1, a, b, c, d, in[ 4] + 0xf57c0faf, 7);
MD5STEP(F1, d, a, b, c, in[ 5] + 0x4787c62a, 12);
MD5STEP(F1, c, d, a, b, in[ 6] + 0xa8304613, 17);
MD5STEP(F1, b, c, d, a, in[ 7] + 0xfd469501, 22);
MD5STEP(F1, a, b, c, d, in[ 8] + 0x698098d8, 7);
MD5STEP(F1, d, a, b, c, in[ 9] + 0x8b44f7af, 12);
MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
MD5STEP(F2, a, b, c, d, in[ 1] + 0xf61e2562, 5);
MD5STEP(F2, d, a, b, c, in[ 6] + 0xc040b340, 9);
MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
MD5STEP(F2, b, c, d, a, in[ 0] + 0xe9b6c7aa, 20);
MD5STEP(F2, a, b, c, d, in[ 5] + 0xd62f105d, 5);
MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
MD5STEP(F2, b, c, d, a, in[ 4] + 0xe7d3fbc8, 20);
MD5STEP(F2, a, b, c, d, in[ 9] + 0x21e1cde6, 5);
MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
MD5STEP(F2, c, d, a, b, in[ 3] + 0xf4d50d87, 14);
MD5STEP(F2, b, c, d, a, in[ 8] + 0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, in[ 2] + 0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, in[ 7] + 0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
MD5STEP(F3, a, b, c, d, in[ 5] + 0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, in[ 8] + 0x8771f681, 11);
MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, in[ 1] + 0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, in[ 4] + 0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, in[ 7] + 0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, in[ 0] + 0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, in[ 3] + 0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, in[ 6] + 0x04881d05, 23);
MD5STEP(F3, a, b, c, d, in[ 9] + 0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, in[2 ] + 0xc4ac5665, 23);
MD5STEP(F4, a, b, c, d, in[ 0] + 0xf4292244, 6);
MD5STEP(F4, d, a, b, c, in[7 ] + 0x432aff97, 10);
MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, in[5 ] + 0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, in[3 ] + 0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, in[1 ] + 0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, in[8 ] + 0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, in[6 ] + 0xa3014314, 15);
MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, in[4 ] + 0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, in[2 ] + 0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, in[9 ] + 0xeb86d391, 21);
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
}
+511
View File
@@ -0,0 +1,511 @@
/*
* PFLINUX: userland implementation of the out-of-line generic red-black
* tree API that tools/include/sys/tree.h (copied verbatim from
* _vendor/upstream/sys/sys/tree.h) declares and the RBT_* macros call:
* _rb_insert/_rb_find/_rb_remove/_rb_min/_rb_next/... OpenBSD ships
* these as libc symbols (sys/kern/subr_rbtree.c is linked into libc);
* glibc has none, so pfctl links this file. Behavior follows OpenBSD:
* _rb_insert returns NULL on success, the existing item on duplicate.
* _rb_remove unlinks and returns the removed item.
* Colors use the tree.h RB_RED/RB_BLACK values; struct rb_entry holds
* plain parent/left/right pointers plus an unsigned color.
* t_augment (when non-NULL) is called on ancestors after mutations.
*/
#include <sys/types.h>
#include <stddef.h>
#include <errno.h>
#include <sys/tree.h>
static __inline struct rb_entry *
rbt_entry(const struct rb_type *t, void *item)
{
return ((struct rb_entry *)((char *)item + t->t_offset));
}
static __inline void *
rbt_item(const struct rb_type *t, const struct rb_entry *rbe)
{
return (rbe == NULL ? NULL :
(void *)((char *)(struct rb_entry *)rbe - t->t_offset));
}
#define RBE_PARENT(rbe) ((rbe)->rbt_parent)
#define RBE_LEFT(rbe) ((rbe)->rbt_left)
#define RBE_RIGHT(rbe) ((rbe)->rbt_right)
#define RBE_COLOR(rbe) ((rbe)->rbt_color)
static void
rbt_augment(const struct rb_type *t, struct rb_entry *rbe)
{
for (; rbe != NULL && t->t_augment != NULL; rbe = RBE_PARENT(rbe))
t->t_augment(rbt_item(t, rbe));
}
/*
* Replace the subtree rooted at `u' with `v' (CLRS TRANSPLANT).
*/
static void
rbt_transplant(struct rb_entry **root, struct rb_entry *u,
struct rb_entry *v)
{
if (RBE_PARENT(u) == NULL)
*root = v;
else if (u == RBE_LEFT(RBE_PARENT(u)))
RBE_PARENT(u)->rbt_left = v;
else
RBE_PARENT(u)->rbt_right = v;
if (v != NULL)
v->rbt_parent = RBE_PARENT(u);
}
/*
* Replace the subtree rooted at `x' with `y'; y takes x's parent link.
* (CLRS RB-INSERT-FIXUP/DELETE-FIXUP rotations.)
*/
static void
rbt_rotate_left(struct rb_entry **root, struct rb_entry *x)
{
struct rb_entry *y = RBE_RIGHT(x);
x->rbt_right = RBE_LEFT(y);
if (RBE_LEFT(y) != NULL)
RBE_LEFT(y)->rbt_parent = x;
y->rbt_parent = RBE_PARENT(x);
if (RBE_PARENT(x) == NULL)
*root = y;
else if (x == RBE_LEFT(RBE_PARENT(x)))
RBE_PARENT(x)->rbt_left = y;
else
RBE_PARENT(x)->rbt_right = y;
y->rbt_left = x;
x->rbt_parent = y;
}
static void
rbt_rotate_right(struct rb_entry **root, struct rb_entry *x)
{
struct rb_entry *y = RBE_LEFT(x);
x->rbt_left = RBE_RIGHT(y);
if (RBE_RIGHT(y) != NULL)
RBE_RIGHT(y)->rbt_parent = x;
y->rbt_parent = RBE_PARENT(x);
if (RBE_PARENT(x) == NULL)
*root = y;
else if (x == RBE_RIGHT(RBE_PARENT(x)))
RBE_PARENT(x)->rbt_right = y;
else
RBE_PARENT(x)->rbt_left = y;
y->rbt_right = x;
x->rbt_parent = y;
}
void *
_rb_insert(const struct rb_type *t, struct rb_tree *rbt, void *item)
{
struct rb_entry **root = &rbt->rbt_root;
struct rb_entry *rbe = rbt_entry(t, item);
struct rb_entry *current = *root, *parent = NULL;
/* [1] Find the insertion spot. */
while (current != NULL) {
int result = t->t_compare(item, rbt_item(t, current));
parent = current;
if (result < 0)
current = RBE_LEFT(current);
else if (result > 0)
current = RBE_RIGHT(current);
else
return (rbt_item(t, current)); /* duplicate */
}
/* [2] Insert red. */
rbe->rbt_left = NULL;
rbe->rbt_right = NULL;
rbe->rbt_color = RB_RED;
rbe->rbt_parent = parent;
if (parent == NULL)
*root = rbe;
else if (t->t_compare(item, rbt_item(t, parent)) < 0)
parent->rbt_left = rbe;
else
parent->rbt_right = rbe;
/* [3] Fix red-black violations (CLRS RB-INSERT-FIXUP). */
if (parent == NULL)
rbe->rbt_color = RB_BLACK;
else {
struct rb_entry *z = rbe, *p, *g, *u;
while ((p = RBE_PARENT(z)) != NULL &&
RBE_COLOR(p) == RB_RED) {
g = RBE_PARENT(p);
if (p == RBE_LEFT(g)) {
u = RBE_RIGHT(g);
if (u != NULL && RBE_COLOR(u) == RB_RED) {
p->rbt_color = RB_BLACK;
u->rbt_color = RB_BLACK;
g->rbt_color = RB_RED;
z = g;
} else {
if (z == RBE_RIGHT(p)) {
z = p;
rbt_rotate_left(root, p);
p = RBE_PARENT(z);
}
g = RBE_PARENT(p);
p->rbt_color = RB_BLACK;
g->rbt_color = RB_RED;
rbt_rotate_right(root, g);
}
} else {
u = RBE_LEFT(g);
if (u != NULL && RBE_COLOR(u) == RB_RED) {
p->rbt_color = RB_BLACK;
u->rbt_color = RB_BLACK;
g->rbt_color = RB_RED;
z = g;
} else {
if (z == RBE_LEFT(p)) {
z = p;
rbt_rotate_right(root, p);
p = RBE_PARENT(z);
}
g = RBE_PARENT(p);
p->rbt_color = RB_BLACK;
g->rbt_color = RB_RED;
rbt_rotate_left(root, g);
}
}
}
(*root)->rbt_color = RB_BLACK;
}
/* [4] Augment. */
rbt_augment(t, RBE_PARENT(rbe));
return (NULL);
}
/*
* CLRS RB-DELETE-FIXUP with x possibly NULL; x_parent threaded explicitly.
*/
static void
rbt_delete_fixup(struct rb_entry **root, struct rb_entry *x,
struct rb_entry *x_parent)
{
while (x != *root && (x == NULL || RBE_COLOR(x) == RB_BLACK)) {
struct rb_entry *sibling;
if (x_parent == NULL)
break;
if (x == RBE_LEFT(x_parent)) {
sibling = RBE_RIGHT(x_parent);
if (sibling != NULL &&
RBE_COLOR(sibling) == RB_RED) {
sibling->rbt_color = RB_BLACK;
x_parent->rbt_color = RB_RED;
rbt_rotate_left(root, x_parent);
sibling = RBE_RIGHT(x_parent);
}
if (sibling == NULL) {
x = x_parent;
x_parent = RBE_PARENT(x);
continue;
}
if ((RBE_LEFT(sibling) == NULL ||
RBE_COLOR(RBE_LEFT(sibling)) == RB_BLACK) &&
(RBE_RIGHT(sibling) == NULL ||
RBE_COLOR(RBE_RIGHT(sibling)) == RB_BLACK)) {
sibling->rbt_color = RB_RED;
x = x_parent;
x_parent = RBE_PARENT(x);
} else {
if (RBE_RIGHT(sibling) == NULL ||
RBE_COLOR(RBE_RIGHT(sibling)) ==
RB_BLACK) {
if (RBE_LEFT(sibling) != NULL)
RBE_LEFT(sibling)->rbt_color =
RB_BLACK;
sibling->rbt_color = RB_RED;
rbt_rotate_right(root, sibling);
sibling = RBE_RIGHT(x_parent);
}
sibling->rbt_color = RBE_COLOR(x_parent);
x_parent->rbt_color = RB_BLACK;
if (RBE_RIGHT(sibling) != NULL)
RBE_RIGHT(sibling)->rbt_color =
RB_BLACK;
rbt_rotate_left(root, x_parent);
x = *root;
}
} else {
sibling = RBE_LEFT(x_parent);
if (sibling != NULL &&
RBE_COLOR(sibling) == RB_RED) {
sibling->rbt_color = RB_BLACK;
x_parent->rbt_color = RB_RED;
rbt_rotate_right(root, x_parent);
sibling = RBE_LEFT(x_parent);
}
if (sibling == NULL) {
x = x_parent;
x_parent = RBE_PARENT(x);
continue;
}
if ((RBE_RIGHT(sibling) == NULL ||
RBE_COLOR(RBE_RIGHT(sibling)) == RB_BLACK) &&
(RBE_LEFT(sibling) == NULL ||
RBE_COLOR(RBE_LEFT(sibling)) == RB_BLACK)) {
sibling->rbt_color = RB_RED;
x = x_parent;
x_parent = RBE_PARENT(x);
} else {
if (RBE_LEFT(sibling) == NULL ||
RBE_COLOR(RBE_LEFT(sibling)) == RB_BLACK) {
if (RBE_RIGHT(sibling) != NULL)
RBE_RIGHT(sibling)->rbt_color =
RB_BLACK;
sibling->rbt_color = RB_RED;
rbt_rotate_left(root, sibling);
sibling = RBE_LEFT(x_parent);
}
sibling->rbt_color = RBE_COLOR(x_parent);
x_parent->rbt_color = RB_BLACK;
if (RBE_LEFT(sibling) != NULL)
RBE_LEFT(sibling)->rbt_color =
RB_BLACK;
rbt_rotate_right(root, x_parent);
x = *root;
}
}
}
if (x != NULL)
x->rbt_color = RB_BLACK;
}
void *
_rb_remove(const struct rb_type *t, struct rb_tree *rbt, void *item)
{
struct rb_entry **root = &rbt->rbt_root;
struct rb_entry *z = rbt_entry(t, item);
struct rb_entry *y = z, *x, *x_parent;
int y_color;
y_color = RBE_COLOR(y);
if (RBE_LEFT(z) == NULL) { /* [1] no left child */
x = RBE_RIGHT(z);
x_parent = RBE_PARENT(z);
rbt_transplant(root, z, x);
} else if (RBE_RIGHT(z) == NULL) { /* [2] no right child */
x = RBE_LEFT(z);
x_parent = RBE_PARENT(z);
rbt_transplant(root, z, x);
} else { /* [3] two children */
/* in-order successor: leftmost of z's right subtree */
for (y = RBE_RIGHT(z); RBE_LEFT(y) != NULL; y = RBE_LEFT(y))
;
y_color = RBE_COLOR(y);
x = RBE_RIGHT(y);
if (RBE_PARENT(y) == z) {
x_parent = y;
} else {
x_parent = RBE_PARENT(y);
rbt_transplant(root, y, x);
y->rbt_right = RBE_RIGHT(z);
RBE_RIGHT(z)->rbt_parent = y;
}
rbt_transplant(root, z, y);
y->rbt_left = RBE_LEFT(z);
RBE_LEFT(z)->rbt_parent = y;
y->rbt_color = RBE_COLOR(z);
}
/* detach z */
z->rbt_parent = NULL;
z->rbt_left = NULL;
z->rbt_right = NULL;
z->rbt_color = RB_BLACK;
if (y_color == RB_BLACK)
rbt_delete_fixup(root, x, x_parent);
/* augment from the changed point up to the root */
rbt_augment(t, x_parent);
return (item);
}
void *
_rb_find(const struct rb_type *t, struct rb_tree *rbt, const void *item)
{
struct rb_entry *current = rbt->rbt_root;
while (current != NULL) {
int result = t->t_compare(item, rbt_item(t, current));
if (result < 0)
current = RBE_LEFT(current);
else if (result > 0)
current = RBE_RIGHT(current);
else
return (rbt_item(t, current));
}
return (NULL);
}
void *
_rb_nfind(const struct rb_type *t, struct rb_tree *rbt, const void *item)
{
struct rb_entry *current = rbt->rbt_root;
struct rb_entry *best = NULL;
while (current != NULL) {
if (t->t_compare(item, rbt_item(t, current)) <= 0) {
best = current;
current = RBE_LEFT(current);
} else
current = RBE_RIGHT(current);
}
return (best == NULL ? NULL : rbt_item(t, best));
}
void *
_rb_root(const struct rb_type *t, struct rb_tree *rbt)
{
return (rbt_item(t, rbt->rbt_root));
}
void *
_rb_min(const struct rb_type *t, struct rb_tree *rbt)
{
struct rb_entry *current = rbt->rbt_root;
if (current == NULL)
return (NULL);
while (RBE_LEFT(current) != NULL)
current = RBE_LEFT(current);
return (rbt_item(t, current));
}
void *
_rb_max(const struct rb_type *t, struct rb_tree *rbt)
{
struct rb_entry *current = rbt->rbt_root;
if (current == NULL)
return (NULL);
while (RBE_RIGHT(current) != NULL)
current = RBE_RIGHT(current);
return (rbt_item(t, current));
}
void *
_rb_next(const struct rb_type *t, void *item)
{
struct rb_entry *rbe = rbt_entry(t, item);
struct rb_entry *parent;
if (RBE_RIGHT(rbe) != NULL) {
rbe = RBE_RIGHT(rbe);
while (RBE_LEFT(rbe) != NULL)
rbe = RBE_LEFT(rbe);
return (rbt_item(t, rbe));
}
for (parent = RBE_PARENT(rbe); parent != NULL;
parent = RBE_PARENT(parent)) {
if (rbe == RBE_LEFT(parent))
return (rbt_item(t, parent));
rbe = parent;
}
return (NULL);
}
void *
_rb_prev(const struct rb_type *t, void *item)
{
struct rb_entry *rbe = rbt_entry(t, item);
struct rb_entry *parent;
if (RBE_LEFT(rbe) != NULL) {
rbe = RBE_LEFT(rbe);
while (RBE_RIGHT(rbe) != NULL)
rbe = RBE_RIGHT(rbe);
return (rbt_item(t, rbe));
}
for (parent = RBE_PARENT(rbe); parent != NULL;
parent = RBE_PARENT(parent)) {
if (rbe == RBE_RIGHT(parent))
return (rbt_item(t, parent));
rbe = parent;
}
return (NULL);
}
void *
_rb_left(const struct rb_type *t, void *item)
{
return (rbt_item(t, RBE_LEFT(rbt_entry(t, item))));
}
void *
_rb_right(const struct rb_type *t, void *item)
{
return (rbt_item(t, RBE_RIGHT(rbt_entry(t, item))));
}
void *
_rb_parent(const struct rb_type *t, void *item)
{
return (rbt_item(t, RBE_PARENT(rbt_entry(t, item))));
}
void
_rb_set_left(const struct rb_type *t, void *item, void *left)
{
RBE_LEFT(rbt_entry(t, item)) =
left == NULL ? NULL : rbt_entry(t, left);
}
void
_rb_set_right(const struct rb_type *t, void *item, void *right)
{
RBE_RIGHT(rbt_entry(t, item)) =
right == NULL ? NULL : rbt_entry(t, right);
}
void
_rb_set_parent(const struct rb_type *t, void *item, void *parent)
{
RBE_PARENT(rbt_entry(t, item)) =
parent == NULL ? NULL : rbt_entry(t, parent);
}
void
_rb_poison(const struct rb_type *t, void *item, unsigned long poison)
{
struct rb_entry *rbe = rbt_entry(t, item);
rbe->rbt_parent = (void *)poison;
rbe->rbt_left = (void *)poison;
rbe->rbt_right = (void *)poison;
}
int
_rb_check(const struct rb_type *t, void *item, unsigned long poison)
{
struct rb_entry *rbe = rbt_entry(t, item);
if (rbe->rbt_parent == (void *)poison ||
rbe->rbt_left == (void *)poison ||
rbe->rbt_right == (void *)poison) {
errno = EINVAL;
return (-1);
}
return (0);
}
+52
View File
@@ -0,0 +1,52 @@
/* PFLINUX: BSD strtonum(3) for glibc — follows OpenBSD
* lib/libc/stdlib/strtonum.c semantics (public domain).
* Declared in tools/include/pflinux_compat.h. */
#include <sys/types.h>
#include <errno.h>
#include <inttypes.h>
#include <stdlib.h>
#define INVALID 1
#define TOO_SMALL 2
#define TOO_LARGE 3
long long
strtonum(const char *numstr, long long minval, long long maxval,
const char **errstrp)
{
long long ll = 0;
int error = 0;
struct errval {
const char *errstr;
int err;
} ev[4] = {
{ NULL, 0 },
{ "invalid", EINVAL },
{ "too small", ERANGE },
{ "too large", ERANGE },
};
ev[0].err = errno;
errno = 0;
if (minval > maxval) {
error = INVALID;
} else {
char *str;
ll = strtoll(numstr, &str, 10);
if (numstr == str || str == NULL || *str != '\0')
error = INVALID;
else if ((errno == ERANGE && ll < minval) || ll < minval)
error = TOO_SMALL;
else if ((errno == ERANGE && ll > maxval) || ll > maxval)
error = TOO_LARGE;
}
if (errstrp != NULL)
*errstrp = ev[error].errstr;
errno = ev[error].err;
if (error)
ll = 0;
return (ll);
}
+111
View File
@@ -0,0 +1,111 @@
/* PFLINUX: sysctl(3) emulation for pfctl on Linux.
*
* pfctl uses three OpenBSD MIBs; the legacy Linux sysctl(2) syscall this
* replaces them with (/proc, sysconf) covers exactly those, mirroring the
* semantics the pf.ko port provides:
*
* CTL_KERN/KERN_MAXCLUSTERS mbuf-cluster budget feeding the default
* PF_LIMIT_FRAGS limit (pfctl_init_options).
* CTL_HW/HW_PHYSMEM64 physical memory (tiny-host table limit).
* CTL_NET/PF_ROUTE/NET_RT_TABLE/<id>
* routing-table existence check used by
* parse.y lookup_rtable(). Mirrors
* module/compat/pfk_rtable.c: Linux has no
* per-rtableid FIBs, so every id is accepted
* (ids > RT_TABLEID_MAX never get here:
* callers range-check first, and ENOENT
* answers for completeness).
*/
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/socket.h> /* PF_ROUTE (glibc value, see net/route.h note) */
#include <net/route.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
/*
* Physical memory in bytes via sysconf(3); falls back to sysinfo-ish
* estimate of 0 (callers treat it as "tiny host" path).
*/
static int64_t
pflinux_physmem(void)
{
long pages = sysconf(_SC_PHYS_PAGES);
long psz = sysconf(_SC_PAGESIZE);
if (pages <= 0 || psz <= 0)
return ((int64_t)32 * 1024 * 1024);
return ((int64_t)pages * psz);
}
int
sysctl(const int *name, u_int namelen, void *old, size_t *oldlenp,
const void *new, size_t newlen)
{
(void)new;
(void)newlen;
if (old == NULL || oldlenp == NULL) {
/* read-only emulation: callers always want values */
errno = EINVAL;
return (-1);
}
if (namelen == 2 && name[0] == CTL_HW && name[1] == HW_PHYSMEM64) {
int64_t mem = pflinux_physmem();
if (*oldlenp < sizeof(mem)) {
errno = EINVAL;
return (-1);
}
memcpy(old, &mem, sizeof(mem));
*oldlenp = sizeof(mem);
return (0);
}
if (namelen == 2 && name[0] == CTL_KERN && name[1] == KERN_MAXCLUSTERS) {
/*
* PFLINUX: OpenBSD sizes nmbclusters off RAM (roughly
* mem/MCLBYTES, MCLBYTES==2048); Linux has no mbufs, so we
* hand pfctl the same RAM-derived cluster budget its
* defaults were designed against.
*/
int mcl = (int)(pflinux_physmem() / 2048);
if (*oldlenp < sizeof(mcl)) {
errno = EINVAL;
return (-1);
}
memcpy(old, &mcl, sizeof(mcl));
*oldlenp = sizeof(mcl);
return (0);
}
if (namelen == 6 && name[0] == CTL_NET && name[1] == PF_ROUTE &&
name[4] == NET_RT_TABLE) {
struct rt_tableinfo info;
if (name[5] > RT_TABLEID_MAX) {
errno = ENOENT; /* nonexistent table */
return (-1);
}
memset(&info, 0, sizeof(info));
info.rti_tableid = name[5];
info.rti_domainid = 0;
if (*oldlenp < sizeof(info)) {
errno = EINVAL;
return (-1);
}
memcpy(old, &info, sizeof(info));
*oldlenp = sizeof(info);
return (0);
}
errno = ENOTSUP;
return (-1);
}
+28
View File
@@ -0,0 +1,28 @@
/* PFLINUX: <md5.h> — userland copy of OpenBSD 8.0 <sys/crypto/md5.h>
* (from _vendor/upstream/sys/crypto/md5.h) with the kernel-only
* __bounded__ attribute annotations dropped (glibc sys/cdefs.h has no
* __bounded__) and <sys/types.h> added. Algorithm and constants are
* unchanged; PF_MD5_DIGEST_LENGTH checks in pfvar.h rely on
* MD5_DIGEST_LENGTH == 16. */
#ifndef _MD5_H_
#define _MD5_H_
#include <sys/types.h>
#define MD5_BLOCK_LENGTH 64
#define MD5_DIGEST_LENGTH 16
typedef struct MD5Context {
u_int32_t state[4]; /* state */
u_int64_t count; /* number of bits, mod 2^64 */
u_int8_t buffer[MD5_BLOCK_LENGTH]; /* input buffer */
} MD5_CTX;
__BEGIN_DECLS
void MD5Init(MD5_CTX *);
void MD5Update(MD5_CTX *, const void *, size_t);
void MD5Final(u_int8_t [MD5_DIGEST_LENGTH], MD5_CTX *);
void MD5Transform(u_int32_t [4], const u_int8_t [MD5_BLOCK_LENGTH]);
__END_DECLS
#endif /* _MD5_H_ */
+54
View File
@@ -0,0 +1,54 @@
/* PFLINUX: <net/if.h> — glibc's <net/if.h> plus OpenBSD's interface-group
* ABI (sys/sockio.h) used by pfctl_parser.c (ifa_exists/ifa_grouplookup).
* Linux has no interface groups: SIOCGIFGMEMB keeps the OpenBSD encoding
* (it is computed with our OpenBSD _IOWR) and every use of it on an AF_INET
* socket returns ENOTTY, so group lookups degrade to "not a group" exactly
* like on an OpenBSD without the group. */
#ifndef _PFLINUX_NET_IF_H
#define _PFLINUX_NET_IF_H
#include_next <net/if.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#ifndef IFNAMSIZ
#define IFNAMSIZ IF_NAMESIZE
#endif
#define IFG_ALL "all" /* group contains all interfaces */
#define IFG_EGRESS "egress" /* if(s) default route(s) point to */
struct ifg_req {
union {
char ifgrqu_group[IFNAMSIZ];
char ifgrqu_member[IFNAMSIZ];
} ifgrq_ifgrqu;
#define ifgrq_group ifgrq_ifgrqu.ifgrqu_group
#define ifgrq_member ifgrq_ifgrqu.ifgrqu_member
};
struct ifgroupreq {
char ifgr_name[IFNAMSIZ];
u_int ifgr_len;
union {
char ifgru_group[IFNAMSIZ];
struct ifg_req *ifgru_groups;
} ifgr_ifgru;
#define ifgr_group ifgr_ifgru.ifgru_group
#define ifgr_groups ifgr_ifgru.ifgru_groups
};
#ifndef SIOCGIFGMEMB
#define SIOCGIFGMEMB _IOWR('i', 138, struct ifgroupreq) /* OpenBSD value */
#endif
/* PFLINUX: OpenBSD if.h queue-priority range (parse.y `prio` check) */
#ifndef IFQ_NQUEUES
#define IFQ_NQUEUES 8
#endif
#ifndef IFQ_MAXPRIO
#define IFQ_MAXPRIO (IFQ_NQUEUES - 1)
#endif
#endif /* _PFLINUX_NET_IF_H */
+30
View File
@@ -0,0 +1,30 @@
/* PFLINUX: <net/if_dl.h> — BSD link-layer address. Linux has no AF_LINK;
* getifaddrs() reports link-layer addresses as AF_PACKET/struct sockaddr_ll.
* We therefore define AF_LINK as AF_PACKET and lay struct sockaddr_dl out
* exactly like struct sockaddr_ll, so pfctl_parser.c's
* ((struct sockaddr_dl *)ifa->ifa_addr)->sdl_index finds sll_ifindex.
* Everything stays userland-internal: af values tagged AF_LINK never cross
* /dev/pf (rules carry ifname strings, not sockaddr families). */
#ifndef _PFLINUX_NET_IF_DL_H
#define _PFLINUX_NET_IF_DL_H
#include <sys/types.h>
#include <sys/socket.h>
#ifndef AF_LINK
#define AF_LINK AF_PACKET /* PFLINUX mapping, see header note */
#endif
struct sockaddr_dl {
sa_family_t sdl_family; /* sll_family (offset 0) */
u_short sdl_protocol; /* sll_protocol (offset 2) */
int sdl_index; /* sll_ifindex (offset 4) */
u_short sdl_type; /* sll_hatype (offset 8) */
u_char sdl_pkttype; /* sll_pkttype (offset 10) */
u_char sdl_alen; /* sll_halen (offset 11) */
u_char sdl_data[8]; /* sll_addr (offset 12) */
};
#define LLADDR(s) ((caddr_t *)((s)->sdl_data))
#endif /* _PFLINUX_NET_IF_DL_H */
+32
View File
@@ -0,0 +1,32 @@
/* PFLINUX: <net/route.h> — the userland-visible subset of OpenBSD's
* <net/route.h>/<sys/socket.h> routing constants that net/pfvar.h
* (RTLABEL_LEN) and parse.y (CTL_NET route lookups) rely on. Linux'
* glibc <net/route.h> carries only struct rtentry/ioctls, so this shim
* shadows it. Values copied from OpenBSD 8.0 sys/sys/socket.h and
* sys/net/route.h; RT_TABLEID_MAX matches module/include/pfk.h (32),
* the routing-domain ceiling our pf.ko implements. */
#ifndef _PFLINUX_NET_ROUTE_H
#define _PFLINUX_NET_ROUTE_H
#include <sys/types.h>
/* PFLINUX: AF_ROUTE/PF_ROUTE (== 17) come from glibc <sys/socket.h>
* unmodified; only the routing-table constants pfvar.h/parse.y need
* live here. */
#define NET_RT_DUMP 1
#define NET_RT_FLAGS 2
#define NET_RT_IFLIST 3
#define NET_RT_TABLE 5
#define RTLABEL_LEN 32
#define RT_TABLEID_MAX 32 /* keep in sync with module/include/pfk.h */
struct rt_tableinfo {
u_short rti_tableid; /* routing table id */
u_short rti_domainid; /* routing domain id */
};
struct rtentry; /* only referenced via pointers/prototypes */
#endif /* _PFLINUX_NET_ROUTE_H */
+56
View File
@@ -0,0 +1,56 @@
/* PFLINUX: shadow <netinet/icmp6.h> — glibc's copy lacks the MLD/nodeinfo
* and ND redirect-code constants pfctl_parser.c's icmp6-type keyword
* table references. Values copied verbatim from
* _vendor/openbsd/obbsd/sys/netinet/icmp6.h (OpenBSD 8.0). */
#ifndef _PFLINUX_NETINET_ICMP6_H
#define _PFLINUX_NETINET_ICMP6_H
#include_next <netinet/icmp6.h>
#ifndef ICMP6_MEMBERSHIP_QUERY
#define ICMP6_MEMBERSHIP_QUERY 130 /* group membership query */
#endif
#ifndef ICMP6_MEMBERSHIP_REPORT
#define ICMP6_MEMBERSHIP_REPORT 131 /* group membership report */
#endif
#ifndef ICMP6_MEMBERSHIP_REDUCTION
#define ICMP6_MEMBERSHIP_REDUCTION 132 /* membership termination */
#endif
#ifndef ICMP6_WRUREQUEST
#define ICMP6_WRUREQUEST 139 /* who are you request */
#endif
#ifndef ICMP6_WRUREPLY
#define ICMP6_WRUREPLY 140 /* who are you reply */
#endif
#ifndef ICMP6_FQDN_QUERY
#define ICMP6_FQDN_QUERY 139 /* FQDN query */
#endif
#ifndef ICMP6_FQDN_REPLY
#define ICMP6_FQDN_REPLY 140 /* FQDN reply */
#endif
#ifndef ICMP6_NI_QUERY
#define ICMP6_NI_QUERY 139 /* node information request */
#endif
#ifndef ICMP6_NI_REPLY
#define ICMP6_NI_REPLY 140 /* node information reply */
#endif
#ifndef MLD_LISTENER_DONE
#define MLD_LISTENER_DONE 132 /* multicast listener done */
#endif
#ifndef MLD_MTRACE_RESP
#define MLD_MTRACE_RESP 200 /* mtrace resp (to sender) */
#endif
#ifndef MLD_MTRACE
#define MLD_MTRACE 201 /* mtrace messages */
#endif
#ifndef MLDV2_LISTENER_REPORT
#define MLDV2_LISTENER_REPORT 143 /* RFC3810 listener report */
#endif
#ifndef ND_REDIRECT_ONLINK
#define ND_REDIRECT_ONLINK 0 /* redirect to on-link node */
#endif
#ifndef ND_REDIRECT_ROUTER
#define ND_REDIRECT_ROUTER 1 /* redirect to better router */
#endif
#endif /* _PFLINUX_NETINET_ICMP6_H */
+44
View File
@@ -0,0 +1,44 @@
/* PFLINUX: shadow <netinet/ip.h> — glibc's copy lacks the IPTOS_DSCP_CS*
* class-selector codepoints parse.y's `set tos` table references (plus a
* few neighbours); values copied verbatim from
* _vendor/openbsd/obbsd/sys/netinet/ip.h (OpenBSD 8.0). */
#ifndef _PFLINUX_NETINET_IP_H
#define _PFLINUX_NETINET_IP_H
#include_next <netinet/ip.h>
#ifndef IPTOS_DSCP_CS0
#define IPTOS_DSCP_CS0 0x00
#endif
#ifndef IPTOS_DSCP_LE
#define IPTOS_DSCP_LE 0x04
#endif
#ifndef IPTOS_DSCP_CS1
#define IPTOS_DSCP_CS1 0x20
#endif
#ifndef IPTOS_DSCP_CS2
#define IPTOS_DSCP_CS2 0x40
#endif
#ifndef IPTOS_DSCP_CS3
#define IPTOS_DSCP_CS3 0x60
#endif
#ifndef IPTOS_DSCP_CS4
#define IPTOS_DSCP_CS4 0x80
#endif
#ifndef IPTOS_DSCP_CS5
#define IPTOS_DSCP_CS5 0xa0
#endif
#ifndef IPTOS_DSCP_VA
#define IPTOS_DSCP_VA 0xb0
#endif
#ifndef IPTOS_DSCP_EF
#define IPTOS_DSCP_EF 0xb8
#endif
#ifndef IPTOS_DSCP_CS6
#define IPTOS_DSCP_CS6 0xc0
#endif
#ifndef IPTOS_DSCP_CS7
#define IPTOS_DSCP_CS7 0xe0
#endif
#endif /* _PFLINUX_NETINET_IP_H */
+62
View File
@@ -0,0 +1,62 @@
/* PFLINUX: shadow <netinet/ip_icmp.h> — glibc's copy lacks the extended
* ICMP type/code constants pfctl_parser.c's icmp-type keyword table
* references. Values copied verbatim from
* _vendor/openbsd/obbsd/sys/netinet/ip_icmp.h (OpenBSD 8.0). */
#ifndef _PFLINUX_NETINET_IP_ICMP_H
#define _PFLINUX_NETINET_IP_ICMP_H
#include_next <netinet/ip_icmp.h>
#ifndef ICMP_ALTHOSTADDR
#define ICMP_ALTHOSTADDR 6 /* alternate host address */
#endif
#ifndef ICMP_TRACEROUTE
#define ICMP_TRACEROUTE 30 /* traceroute */
#endif
#ifndef ICMP_DATACONVERR
#define ICMP_DATACONVERR 31 /* data conversion error */
#endif
#ifndef ICMP_MOBILE_REDIRECT
#define ICMP_MOBILE_REDIRECT 32 /* mobile host redirect */
#endif
#ifndef ICMP_IPV6_WHEREAREYOU
#define ICMP_IPV6_WHEREAREYOU 33 /* IPv6 where-are-you */
#endif
#ifndef ICMP_IPV6_IAMHERE
#define ICMP_IPV6_IAMHERE 34 /* IPv6 i-am-here */
#endif
#ifndef ICMP_MOBILE_REGREQUEST
#define ICMP_MOBILE_REGREQUEST 35 /* mobile registration req */
#endif
#ifndef ICMP_MOBILE_REGREPLY
#define ICMP_MOBILE_REGREPLY 36 /* mobile registration reply */
#endif
#ifndef ICMP_SKIP
#define ICMP_SKIP 39 /* SKIP */
#endif
#ifndef ICMP_PHOTURIS
#define ICMP_PHOTURIS 40 /* Photuris */
#endif
#ifndef ICMP_PHOTURIS_UNKNOWN_INDEX
#define ICMP_PHOTURIS_UNKNOWN_INDEX 1 /* unknown sec index */
#endif
#ifndef ICMP_PHOTURIS_AUTH_FAILED
#define ICMP_PHOTURIS_AUTH_FAILED 2 /* auth failed */
#endif
#ifndef ICMP_PHOTURIS_DECRYPT_FAILED
#define ICMP_PHOTURIS_DECRYPT_FAILED 3 /* decrypt failed */
#endif
#ifndef ICMP_PARAMPROB_ERRATPTR
#define ICMP_PARAMPROB_ERRATPTR 0 /* req. opt. absent */
#endif
#ifndef ICMP_PARAMPROB_LENGTH
#define ICMP_PARAMPROB_LENGTH 2 /* bad length */
#endif
#ifndef ICMP_ROUTERADVERT_NORMAL
#define ICMP_ROUTERADVERT_NORMAL 0 /* normal advertisement */
#endif
#ifndef ICMP_ROUTERADVERT_NOROUTE_COMMON
#define ICMP_ROUTERADVERT_NOROUTE_COMMON 16 /* selective routing */
#endif
#endif /* _PFLINUX_NETINET_IP_ICMP_H */
+32
View File
@@ -0,0 +1,32 @@
/* PFLINUX: <netinet/tcp_fsm.h> — OpenBSD 8.0 TCP state machine header
* (glibc has no equivalent). Values copied from
* _vendor/openbsd/obbsd/sys/netinet/tcp_fsm.h. Kernel-only bodies
* (TCPOUTFLAGS/TCPSTATES arrays) dropped; tcpstates[] is declared extern
* and defined in tools/compat-user/bsd.c for pf_print_state.c. */
#ifndef _PFLINUX_NETINET_TCP_FSM_H
#define _PFLINUX_NETINET_TCP_FSM_H
#define TCP_NSTATES 11
#define TCPS_CLOSED 0 /* closed */
#define TCPS_LISTEN 1 /* listening for connection */
#define TCPS_SYN_SENT 2 /* active, have sent syn */
#define TCPS_SYN_RECEIVED 3 /* have sent and received syn */
/* states < TCPS_ESTABLISHED are those where connections not established */
#define TCPS_ESTABLISHED 4 /* established */
#define TCPS_CLOSE_WAIT 5 /* rcvd fin, waiting for close */
/* states > TCPS_CLOSE_WAIT are those where user has closed */
#define TCPS_FIN_WAIT_1 6 /* have closed, sent fin */
#define TCPS_CLOSING 7 /* closed xchd FIN; await FIN ACK */
#define TCPS_LAST_ACK 8 /* had fin and close; await FIN ACK */
/* states > TCPS_CLOSE_WAIT && < TCPS_FIN_WAIT_2 await ACK of FIN */
#define TCPS_FIN_WAIT_2 9 /* have closed, fin is acked */
#define TCPS_TIME_WAIT 10 /* in 2*msl quiet wait after close */
#define TCPS_HAVERCVDSYN(s) ((s) >= TCPS_SYN_RECEIVED)
#define TCPS_HAVEESTABLISHED(s) ((s) >= TCPS_ESTABLISHED)
#define TCPS_HAVERCVDFIN(s) ((s) >= TCPS_TIME_WAIT)
extern const char *const tcpstates[];
#endif /* _PFLINUX_NETINET_TCP_FSM_H */
+121
View File
@@ -0,0 +1,121 @@
/* PFLINUX: force-include prelude for the Linux build of OpenBSD 8.0 pfctl
* (see tools/Makefile, CPPFLAGS -include). Keeps the copied pfctl sources
* byte-identical to _vendor except for the edits listed in UPSTREAM.md. */
#ifndef _PFLINUX_PRELUDE_H
#define _PFLINUX_PRELUDE_H
#include <sys/types.h>
#include <sys/cdefs.h>
#include <stddef.h> /* offsetof for sys/tree.h RBT_GENERATE */
#include <stdint.h>
#include <stdio.h> /* fgetln(3) declaration below */
#include <stdlib.h>
#include <string.h>
#include <strings.h> /* PFLINUX: BSD bzero/bcopy; OpenBSD <string.h>
* exposes them, glibc keeps them in <strings.h> */
/* PFLINUX: resolves to tools/include/sys/ioctl.h (first -I dir), which
* redefines _IO/_IOW/_IOR/_IOWR to the OpenBSD sys/ioccom.h encoding that
* pf.ko expects (identical to module/include/pfk.h). */
#include <sys/ioctl.h>
/* PFLINUX: BSD AF_LINK. tools/include/net/if_dl.h maps it to AF_PACKET
* with a sockaddr_ll-compatible struct sockaddr_dl; parse.y compares
* n->af against AF_LINK but never includes <net/if_dl.h>, so the
* definition lives in this prelude (if_dl.h's own copy is #ifndef'd). */
#include <sys/socket.h>
#ifndef AF_LINK
#define AF_LINK AF_PACKET
#endif
/* OpenBSD sys/cdefs.h attribute macros that glibc does not provide. */
#ifndef __dead
#define __dead __attribute__((__noreturn__))
#endif
#ifndef __printflike
#define __printflike(f, a) __attribute__((__format__(__printf__, f, a)))
#endif
/* net/pfvar.h marks pfsync structs with __packed (OpenBSD cdefs); recent
* glibc does not define it. */
#ifndef __packed
#define __packed __attribute__((__packed__))
#endif
/* sys/tree.h's RBT_* / RB_* generator macros use __unused/__inline;
* recent glibc sys/cdefs.h no longer provides them. */
#ifndef __unused
#define __unused __attribute__((__unused__))
#endif
#ifndef __inline
#define __inline inline
#endif
/*
* OpenBSD <sys/endian.h> also spells the byte-order helpers
* betoh16/32/64 and letoh16/32/64 (pf_print_state.c uses betoh64);
* glibc only has
* beXXtoh/leXXtoh names.
*/
#include <endian.h>
#ifndef betoh16
#define betoh16(x) be16toh(x)
#endif
#ifndef betoh32
#define betoh32(x) be32toh(x)
#endif
#ifndef betoh64
#define betoh64(x) be64toh(x)
#endif
#ifndef letoh16
#define letoh16(x) le16toh(x)
#endif
#ifndef letoh32
#define letoh32(x) le32toh(x)
#endif
#ifndef letoh64
#define letoh64(x) le64toh(x)
#endif
/*
* OpenBSD <sys/param.h> assignment-style host<->net byte-order helpers
* (pfctl.c uses them). htonl/ntohl/htons/ntohs come from <netinet/in.h>
* which the including source pulls in.
*/
#ifndef HTONL
#define HTONL(x) ((x) = htonl(x))
#endif
#ifndef NTOL
#define NTOL(x) ((x) = ntohl(x))
#endif
#ifndef HTONS
#define HTONS(x) ((x) = htons(x))
#endif
#ifndef NOSH
#define NOSH(x) ((x) = ntohs(x))
#endif
/* strtonum(3) is BSD (lib/libc/stdlib/strtonum.c); glibc has none.
* Implemented in tools/compat-user/strtonum.c. */
long long strtonum(const char *numstr, long long minval,
long long maxval, const char **errstrp);
/* pledge(2)/unveil(2) are BSD-only. The 8.0 pfctl sources do not call
* them, but the stubs are provided (tools/compat-user/bsd.c) for future
* upstream syncs. */
int pledge(const char *promises, const char *paths[]);
int unveil(const char *path, const char *permissions);
/* fgetln(3) is BSD stdio (pfctl_osfp.c uses it); implemented in
* tools/compat-user/bsd.c on top of getline(3). */
char *fgetln(FILE *stream, size_t *len);
/* freezero(3) is BSD stdlib (pf_ruleset.c userland branch uses it);
* implemented in tools/compat-user/bsd.c. */
void freezero(void *ptr, size_t siz);
/* uid_from_user(3)/gid_from_group(3) are BSD <pwd.h>/<grp.h> helpers
* (parse.y `user`/`group` keywords); implemented in
* tools/compat-user/bsd.c on getpwnam(3)/getgrnam(3). Return 0 on
* success, non-zero with errno on failure (OpenBSD semantics). */
int uid_from_user(const char *, uid_t *);
int gid_from_group(const char *, gid_t *);
#endif /* _PFLINUX_PRELUDE_H */
+14
View File
@@ -0,0 +1,14 @@
/* PFLINUX: <sys/_null.h> — OpenBSD's NULL guard header, required by the
* vendored <sys/queue.h> and <sys/tree.h>. */
#ifndef _PFLINUX_SYS__NULL_H
#define _PFLINUX_SYS__NULL_H
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
#endif /* _PFLINUX_SYS__NULL_H */
+45
View File
@@ -0,0 +1,45 @@
/* PFLINUX: <sys/ioctl.h> — OpenBSD 8.0 ioctl encoding for userland pfctl.
*
* net/pfvar.h builds every DIOC* number with _IO/_IOW/_IOR/_IOWR. pf.ko
* (see module/include/pfk.h) interprets ioctl commands using the OpenBSD
* sys/ioccom.h bit layout, NOT Linux asm/ioctl.h, so this header must
* reproduce pfk.h's PFKXIOC_* encoding byte-for-byte. We pull in the real
* glibc <sys/ioctl.h> first (ioctl(2) prototype, Linux SIOC* constants used
* by <net/if.h>) and then redefine the _IO family.
*
* Values copied from module/include/pfk.h lines 135-152.
*/
#ifndef _PFLINUX_SYS_IOCTL_H
#define _PFLINUX_SYS_IOCTL_H
#include_next <sys/ioctl.h>
#include <sys/types.h>
#undef _IO
#undef _IOR
#undef _IOW
#undef _IOWR
#define PFKXIOC_NRBITS 8
#define PFKXIOC_TYPEBITS 8
#define PFKXIOC_SIZEBITS 13
#define PFKXIOC_DIRBITS 2
#define PFKXIOC_NRSHIFT 0
#define PFKXIOC_TYPESHIFT (PFKXIOC_NRSHIFT + PFKXIOC_NRBITS)
#define PFKXIOC_SIZESHIFT (PFKXIOC_TYPESHIFT + PFKXIOC_TYPEBITS)
#define PFKXIOC_DIRSHIFT (PFKXIOC_SIZESHIFT + PFKXIOC_SIZEBITS + 1)
#define PFKXIOC_NONE 0U
#define PFKXIOC_WRITE 1U /* OpenBSD IOC_IN (copyin) */
#define PFKXIOC_READ 2U /* OpenBSD IOC_OUT (copyout) */
#define PFKXIOC(dir,type,nr,size) \
(((dir) << PFKXIOC_DIRSHIFT) | ((type) << PFKXIOC_TYPESHIFT) | \
((nr) << PFKXIOC_NRSHIFT) | ((size) << PFKXIOC_SIZESHIFT))
#define _IO(t, n) PFKXIOC(PFKXIOC_NONE, (t), (n), 0)
#define _IOWR(t, n, s) PFKXIOC(PFKXIOC_WRITE|PFKXIOC_READ, (t), (n), sizeof(s))
#define _IOW(t, n, s) PFKXIOC(PFKXIOC_WRITE, (t), (n), sizeof(s))
#define _IOR(t, n, s) PFKXIOC(PFKXIOC_READ, (t), (n), sizeof(s))
#endif /* _PFLINUX_SYS_IOCTL_H */
+63
View File
@@ -0,0 +1,63 @@
/* PFLINUX: <sys/refcnt.h> — userland twin of
* module/include/compat/sys/refcnt.h. The kernel side is
* `struct refcnt { refcount_t rc; }` where refcount_t wraps a single
* atomic_t (4 bytes on Linux). Only struct pf_anchor embeds it and
* pf_anchor never crosses /dev/pf, but size/alignment are kept equal. */
#ifndef _PFLINUX_SYS_REFCNT_H
#define _PFLINUX_SYS_REFCNT_H
#include <sys/types.h>
struct refcnt {
unsigned int rc;
};
#define REFCNT_INITIALIZER { .rc = 1 }
static inline void
refcnt_init(struct refcnt *r, unsigned int v)
{
r->rc = v ? v : 1;
}
static inline unsigned int
refcnt_value(const struct refcnt *r)
{
return (r->rc);
}
static inline void
refcnt_take(struct refcnt *r)
{
r->rc++;
}
static inline int
refcnt_take_not0(struct refcnt *r)
{
if (r->rc == 0)
return (0);
r->rc++;
return (1);
}
static inline unsigned int
refcnt_rele(struct refcnt *r)
{
return (--r->rc);
}
static inline unsigned int
refcnt_rele_wake(struct refcnt *r)
{
return (refcnt_rele(r));
}
static inline void
refcnt_finalize(struct refcnt *r, const void *unused)
{
(void)r;
(void)unused;
}
#endif /* _PFLINUX_SYS_REFCNT_H */
+28
View File
@@ -0,0 +1,28 @@
/* PFLINUX: <sys/sysctl.h> — OpenBSD sysctl(3) surface used by pfctl.
* Linux removed both <sys/sysctl.h> and the legacy sysctl(2) syscall; the
* constants below mirror OpenBSD 8.0 (sys/sysctl.h) and sysctl() is
* implemented in tools/compat-user/sysctl.c for exactly the three MIBs
* pfctl queries. */
#ifndef _PFLINUX_SYS_SYSCTL_H
#define _PFLINUX_SYS_SYSCTL_H
#include <sys/types.h>
#include <stddef.h>
#define CTL_UNSPEC 0
#define CTL_KERN 1
#define CTL_VM 2
#define CTL_NET 4 /* PF_ROUTE etc. */
#define CTL_HW 6
#define KERN_MAXCLUSTERS 67 /* int: mbuf cluster limit */
#define HW_PHYSMEM64 19 /* int64_t: total RAM */
struct kinfo_proc; /* forward: nothing pfctl uses, avoid clash */
__BEGIN_DECLS
int sysctl(const int *name, u_int namelen, void *old, size_t *oldlenp,
const void *new, size_t newlen);
__END_DECLS
#endif /* _PFLINUX_SYS_SYSCTL_H */
+8
View File
@@ -0,0 +1,8 @@
/* PFLINUX: <sys/syslimits.h> — glibc ships no such header; PATH_MAX etc.
* live in <limits.h>. */
#ifndef _PFLINUX_SYS_SYSLIMITS_H
#define _PFLINUX_SYS_SYSLIMITS_H
#include <limits.h>
#endif /* _PFLINUX_SYS_SYSLIMITS_H */
+18
View File
@@ -0,0 +1,18 @@
/* PFLINUX: <sys/timeout.h> — pfvar.h embeds struct timeout by value inside
* struct pf_pktdelay. pf_pktdelay is kernel-internal only (it never
* appears in any ioctl payload; the kernel side uses the real
* module/include/compat/sys/timeout.h with linux/timer.h), so userland
* just needs the type to be complete for pfvar.h to compile. */
#ifndef _PFLINUX_SYS_TIMEOUT_H
#define _PFLINUX_SYS_TIMEOUT_H
#include <sys/types.h>
typedef void (timeout_fn)(void *);
struct timeout {
timeout_fn *fn;
void *arg;
};
#endif /* _PFLINUX_SYS_TIMEOUT_H */
+6548
View File
File diff suppressed because it is too large. Load diff
+381
View File
@@ -0,0 +1,381 @@
/* $OpenBSD: pf_print_state.c,v 1.69 2019/02/02 15:43:18 yasuoka Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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
* COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
*
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#define TCPSTATES
#include <netinet/in.h>
#include <netinet/tcp_fsm.h>
#include <arpa/inet.h>
#include <net/pfvar.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include "pfctl_parser.h"
#include "pfctl.h"
void print_name(struct pf_addr *, sa_family_t);
void
print_addr(struct pf_addr_wrap *addr, sa_family_t af, int verbose)
{
switch (addr->type) {
case PF_ADDR_DYNIFTL:
printf("(%s", addr->v.ifname);
if (addr->iflags & PFI_AFLAG_NETWORK)
printf(":network");
if (addr->iflags & PFI_AFLAG_BROADCAST)
printf(":broadcast");
if (addr->iflags & PFI_AFLAG_PEER)
printf(":peer");
if (addr->iflags & PFI_AFLAG_NOALIAS)
printf(":0");
if (verbose) {
if (addr->p.dyncnt <= 0)
printf(":*");
else
printf(":%d", addr->p.dyncnt);
}
printf(")");
break;
case PF_ADDR_TABLE:
if (verbose)
if (addr->p.tblcnt == -1)
printf("<%s:*>", addr->v.tblname);
else
printf("<%s:%d>", addr->v.tblname,
addr->p.tblcnt);
else
printf("<%s>", addr->v.tblname);
return;
case PF_ADDR_RANGE: {
print_addr_str(af, &addr->v.a.addr);
printf(" - ");
print_addr_str(af, &addr->v.a.mask);
break;
}
case PF_ADDR_ADDRMASK:
if (PF_AZERO(&addr->v.a.addr, AF_INET6) &&
PF_AZERO(&addr->v.a.mask, AF_INET6))
printf("any");
else
print_addr_str(af, &addr->v.a.addr);
break;
case PF_ADDR_NOROUTE:
printf("no-route");
return;
case PF_ADDR_URPFFAILED:
printf("urpf-failed");
return;
case PF_ADDR_RTLABEL:
printf("route \"%s\"", addr->v.rtlabelname);
return;
default:
printf("?");
return;
}
/* mask if not _both_ address and mask are zero */
if (addr->type != PF_ADDR_RANGE &&
!(PF_AZERO(&addr->v.a.addr, AF_INET6) &&
PF_AZERO(&addr->v.a.mask, AF_INET6))) {
int bits = unmask(&addr->v.a.mask);
if (bits < (af == AF_INET ? 32 : 128))
printf("/%d", bits);
}
}
void
print_addr_str(sa_family_t af, struct pf_addr *addr)
{
static char buf[48];
if (inet_ntop(af, addr, buf, sizeof(buf)) == NULL)
printf("?");
else
printf("%s", buf);
}
void
print_name(struct pf_addr *addr, sa_family_t af)
{
struct sockaddr_storage ss;
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
char host[NI_MAXHOST];
socklen_t slen; /* PFLINUX: sa_len replacement */
memset(&ss, 0, sizeof(ss));
ss.ss_family = af;
if (ss.ss_family == AF_INET) {
sin = (struct sockaddr_in *)&ss;
/* PFLINUX: Linux struct sockaddr(_in) has no sa_len;
* getnameinfo(3) takes an explicit length instead. */
sin->sin_addr = addr->v4;
slen = sizeof(*sin);
} else {
sin6 = (struct sockaddr_in6 *)&ss;
sin6->sin6_addr = addr->v6;
slen = sizeof(*sin6);
}
if (getnameinfo((struct sockaddr *)&ss, slen, host, sizeof(host),
NULL, 0, NI_NOFQDN) != 0)
printf("?");
else
printf("%s", host);
}
void
print_host(struct pf_addr *addr, u_int16_t port, sa_family_t af, u_int16_t rdom,
const char *proto, int opts)
{
struct pf_addr_wrap aw;
struct servent *s = NULL;
char ps[6];
if (rdom)
printf("(%u) ", ntohs(rdom));
if (opts & PF_OPT_USEDNS)
print_name(addr, af);
else {
memset(&aw, 0, sizeof(aw));
aw.v.a.addr = *addr;
memset(&aw.v.a.mask, 0xff, sizeof(aw.v.a.mask));
print_addr(&aw, af, opts & PF_OPT_VERBOSE2);
}
if (port) {
snprintf(ps, sizeof(ps), "%u", ntohs(port));
if (opts & PF_OPT_PORTNAMES)
s = getservbyport(port, proto);
if (af == AF_INET)
printf(":%s", s ? s->s_name : ps);
else
printf("[%s]", s ? s->s_name : ps);
}
}
void
print_seq(struct pfsync_state_peer *p)
{
if (p->seqdiff)
printf("[%u + %u](+%u)", ntohl(p->seqlo),
ntohl(p->seqhi) - ntohl(p->seqlo), ntohl(p->seqdiff));
else
printf("[%u + %u]", ntohl(p->seqlo),
ntohl(p->seqhi) - ntohl(p->seqlo));
}
void
print_state(struct pfsync_state *s, int opts)
{
struct pfsync_state_peer *src, *dst;
struct pfsync_state_key *sk, *nk;
struct protoent *p;
char *pn = NULL;
int min, sec;
int afto = (s->key[PF_SK_STACK].af != s->key[PF_SK_WIRE].af);
int idx;
if (s->direction == PF_OUT) {
src = &s->src;
dst = &s->dst;
sk = &s->key[PF_SK_STACK];
nk = &s->key[PF_SK_WIRE];
if (s->proto == IPPROTO_ICMP || s->proto == IPPROTO_ICMPV6)
sk->port[0] = nk->port[0];
} else {
src = &s->dst;
dst = &s->src;
sk = &s->key[PF_SK_WIRE];
nk = &s->key[PF_SK_STACK];
if (s->proto == IPPROTO_ICMP || s->proto == IPPROTO_ICMPV6)
sk->port[1] = nk->port[1];
}
printf("%s ", s->ifname);
if ((p = getprotobynumber(s->proto)) != NULL) {
pn = p->p_name;
printf("%s ", pn);
} else
printf("%u ", s->proto);
print_host(&nk->addr[1], nk->port[1], nk->af, nk->rdomain, pn, opts);
if (nk->af != sk->af || PF_ANEQ(&nk->addr[1], &sk->addr[1], nk->af) ||
nk->port[1] != sk->port[1] ||
nk->rdomain != sk->rdomain) {
idx = afto ? 0 : 1;
printf(" (");
print_host(&sk->addr[idx], sk->port[idx], sk->af,
sk->rdomain, pn, opts);
printf(")");
}
if (s->direction == PF_IN && !PF_AZERO(&s->rt_addr, sk->af)) {
printf(" {");
print_addr_str(sk->af, &s->rt_addr);
printf("}");
}
if (s->direction == PF_OUT || (afto && s->direction == PF_IN))
printf(" -> ");
else
printf(" <- ");
print_host(&nk->addr[0], nk->port[0], nk->af, nk->rdomain, pn, opts);
if (nk->af != sk->af || PF_ANEQ(&nk->addr[0], &sk->addr[0], nk->af) ||
nk->port[0] != sk->port[0] ||
nk->rdomain != sk->rdomain) {
idx = afto ? 1 : 0;
printf(" (");
print_host(&sk->addr[idx], sk->port[idx], sk->af,
sk->rdomain, pn, opts);
printf(")");
}
if (s->direction == PF_OUT && !PF_AZERO(&s->rt_addr, nk->af)) {
printf(" {");
print_addr_str(nk->af, &s->rt_addr);
printf("}");
}
printf(" ");
if (s->proto == IPPROTO_TCP) {
if (src->state <= TCPS_TIME_WAIT &&
dst->state <= TCPS_TIME_WAIT)
printf(" %s:%s\n", tcpstates[src->state],
tcpstates[dst->state]);
else if (src->state == PF_TCPS_PROXY_SRC ||
dst->state == PF_TCPS_PROXY_SRC)
printf(" PROXY:SRC\n");
else if (src->state == PF_TCPS_PROXY_DST ||
dst->state == PF_TCPS_PROXY_DST)
printf(" PROXY:DST\n");
else
printf(" <BAD STATE LEVELS %u:%u>\n",
src->state, dst->state);
if (opts & PF_OPT_VERBOSE) {
printf(" ");
print_seq(src);
if (src->wscale && dst->wscale)
printf(" wscale %u",
src->wscale & PF_WSCALE_MASK);
printf(" ");
print_seq(dst);
if (src->wscale && dst->wscale)
printf(" wscale %u",
dst->wscale & PF_WSCALE_MASK);
printf("\n");
}
} else if (s->proto == IPPROTO_UDP && src->state < PFUDPS_NSTATES &&
dst->state < PFUDPS_NSTATES) {
const char *states[] = PFUDPS_NAMES;
printf(" %s:%s\n", states[src->state], states[dst->state]);
} else if (s->proto != IPPROTO_ICMP && s->proto != IPPROTO_ICMPV6 &&
src->state < PFOTHERS_NSTATES && dst->state < PFOTHERS_NSTATES) {
/* XXX ICMP doesn't really have state levels */
const char *states[] = PFOTHERS_NAMES;
printf(" %s:%s\n", states[src->state], states[dst->state]);
} else {
printf(" %u:%u\n", src->state, dst->state);
}
if (opts & PF_OPT_VERBOSE) {
u_int64_t packets[2];
u_int64_t bytes[2];
u_int32_t creation = ntohl(s->creation);
u_int32_t expire = ntohl(s->expire);
sec = creation % 60;
creation /= 60;
min = creation % 60;
creation /= 60;
printf(" age %.2u:%.2u:%.2u", creation, min, sec);
sec = expire % 60;
expire /= 60;
min = expire % 60;
expire /= 60;
printf(", expires in %.2u:%.2u:%.2u", expire, min, sec);
bcopy(s->packets[0], &packets[0], sizeof(u_int64_t));
bcopy(s->packets[1], &packets[1], sizeof(u_int64_t));
bcopy(s->bytes[0], &bytes[0], sizeof(u_int64_t));
bcopy(s->bytes[1], &bytes[1], sizeof(u_int64_t));
printf(", %llu:%llu pkts, %llu:%llu bytes",
betoh64(packets[0]),
betoh64(packets[1]),
betoh64(bytes[0]),
betoh64(bytes[1]));
if (ntohl(s->anchor) != -1)
printf(", anchor %u", ntohl(s->anchor));
if (ntohl(s->rule) != -1)
printf(", rule %u", ntohl(s->rule));
if (ntohs(s->state_flags) & PFSTATE_SLOPPY)
printf(", sloppy");
if (ntohs(s->state_flags) & PFSTATE_PFLOW)
printf(", pflow");
if (s->sync_flags & PFSYNC_FLAG_SRCNODE)
printf(", source-track");
if (s->sync_flags & PFSYNC_FLAG_NATSRCNODE)
printf(", sticky-address");
printf("\n");
}
if (opts & PF_OPT_VERBOSE2) {
u_int64_t id;
bcopy(&s->id, &id, sizeof(u_int64_t));
printf(" id: %016llx creatorid: %08x",
betoh64(id), ntohl(s->creatorid));
printf("\n");
}
}
int
unmask(struct pf_addr *m)
{
int i = 31, j = 0, b = 0;
u_int32_t tmp;
while (j < 4 && m->addr32[j] == 0xffffffff) {
b += 32;
j++;
}
if (j < 4) {
tmp = ntohl(m->addr32[j]);
for (i = 31; tmp & (1 << i); --i)
b++;
}
return (b);
}
+3683
View File
File diff suppressed because it is too large. Load diff
+137
View File
@@ -0,0 +1,137 @@
/* $OpenBSD: pfctl.h,v 1.66 2026/02/03 10:25:28 sashan Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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
* COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
*
*/
#ifndef _PFCTL_H_
#define _PFCTL_H_
#ifdef PFCTL_DEBUG
#define DBGPRINT(...) fprintf(stderr, __VA_ARGS__)
#else
#define DBGPRINT(...) (void)(0)
#endif
enum pfctl_show { PFCTL_SHOW_RULES, PFCTL_SHOW_LABELS, PFCTL_SHOW_NOTHING };
enum { PFRB_TABLES = 1, PFRB_TSTATS, PFRB_ADDRS, PFRB_ASTATS,
PFRB_IFACES, PFRB_TRANS, PFRB_MAX };
struct pfr_buffer {
int pfrb_type; /* type of content, see enum above */
int pfrb_size; /* number of objects in buffer */
int pfrb_msize; /* maximum number of objects in buffer */
void *pfrb_caddr; /* malloc'ated memory area */
};
#define PFRB_FOREACH(var, buf) \
for ((var) = pfr_buf_next((buf), NULL); \
(var) != NULL; \
(var) = pfr_buf_next((buf), (var)))
struct pfr_anchoritem {
SLIST_ENTRY(pfr_anchoritem) pfra_sle;
char *pfra_anchorname;
};
struct pfr_uktable {
struct pfr_ktable pfrukt_kt;
struct pfr_buffer pfrukt_addrs;
int pfrukt_init_addr;
SLIST_ENTRY(pfr_uktable)
pfrukt_entry;
};
#define pfrukt_t pfrukt_kt.pfrkt_ts.pfrts_t
#define pfrukt_name pfrukt_kt.pfrkt_t.pfrt_name
#define pfrukt_anchor pfrukt_kt.pfrkt_t.pfrt_anchor
extern struct pfr_ktablehead pfr_ktables;
SLIST_HEAD(pfr_anchors, pfr_anchoritem);
struct pfctl;
int pfr_clr_tables(struct pfr_table *, int *, int);
int pfr_add_tables(struct pfr_table *, int, int *, int);
int pfr_del_tables(struct pfr_table *, int, int *, int);
int pfr_get_tables(struct pfr_table *, struct pfr_table *, int *, int);
int pfr_get_tstats(struct pfr_table *, struct pfr_tstats *, int *, int);
int pfr_clr_tstats(struct pfr_table *, int, int *, int);
int pfr_clr_astats(struct pfr_table *, struct pfr_addr *, int, int *, int);
int pfr_clr_addrs(struct pfr_table *, int *, int);
int pfr_add_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int);
int pfr_del_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int);
int pfr_set_addrs(struct pfr_table *, struct pfr_addr *, int, int *,
int *, int *, int *, int);
int pfr_get_addrs(struct pfr_table *, struct pfr_addr *, int *, int);
int pfr_get_astats(struct pfr_table *, struct pfr_astats *, int *, int);
int pfr_tst_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int);
int pfr_ina_define(struct pfr_table *, struct pfr_addr *, int, int *,
int *, int, int);
void pfr_buf_clear(struct pfr_buffer *);
int pfr_buf_add(struct pfr_buffer *, const void *);
void *pfr_buf_next(struct pfr_buffer *, const void *);
int pfr_buf_grow(struct pfr_buffer *, int);
int pfr_buf_load(struct pfr_buffer *, char *, int, int);
char *pf_strerror(int);
int pfi_get_ifaces(const char *, struct pfi_kif *, int *);
void pfctl_print_title(char *);
int pfctl_clear_tables(const char *, int);
void pfctl_show_tables(const char *, int);
int pfctl_table(int, char *[], char *, const char *, char *,
const char *, int);
void warn_duplicate_tables(const char *, const char *);
void pfctl_show_ifaces(const char *, int);
FILE *pfctl_fopen(const char *, const char *);
void print_addr(struct pf_addr_wrap *, sa_family_t, int);
void print_addr_str(sa_family_t, struct pf_addr *);
void print_host(struct pf_addr *, u_int16_t p, sa_family_t, u_int16_t, const char *, int);
void print_seq(struct pfsync_state_peer *);
void print_state(struct pfsync_state *, int);
int pfctl_cmdline_symset(char *);
int pfctl_add_trans(struct pfr_buffer *, int, const char *);
u_int32_t
pfctl_get_ticket(struct pfr_buffer *, int, const char *);
int pfctl_trans(int, struct pfr_buffer *, u_long, int);
int pfctl_show_queues(int, const char *, int, int);
void pfctl_err(int, int, const char *, ...);
void pfctl_errx(int, int, const char *, ...);
const char
*pfctl_statelim_id2name(struct pfctl *, u_int8_t);
const char
*pfctl_sourcelim_id2name(struct pfctl *, u_int8_t);
#endif /* _PFCTL_H_ */
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+346
View File
@@ -0,0 +1,346 @@
/* $OpenBSD: pfctl_parser.h,v 1.124 2026/04/09 06:10:38 dlg Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
* Copyright (c) 2002 - 2013 Henning Brauer <henning@openbsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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
* COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
*
*/
#ifndef _PFCTL_PARSER_H_
#define _PFCTL_PARSER_H_
#define PF_OSFP_FILE "/etc/pf.os"
#define PF_OPT_DISABLE 0x00001
#define PF_OPT_ENABLE 0x00002
#define PF_OPT_VERBOSE 0x00004
#define PF_OPT_NOACTION 0x00008
#define PF_OPT_QUIET 0x00010
#define PF_OPT_CLRRULECTRS 0x00020
#define PF_OPT_USEDNS 0x00040
#define PF_OPT_VERBOSE2 0x00080
#define PF_OPT_DUMMYACTION 0x00100
#define PF_OPT_DEBUG 0x00200
#define PF_OPT_SHOWALL 0x00400
#define PF_OPT_OPTIMIZE 0x00800
#define PF_OPT_NODNS 0x01000
#define PF_OPT_RECURSE 0x04000
#define PF_OPT_PORTNAMES 0x08000
#define PF_OPT_IGNFAIL 0x10000
#define PF_OPT_CALLSHOW 0x20000
#define PF_TH_ALL 0xFF
#define PF_NAT_PROXY_PORT_LOW 50001
#define PF_NAT_PROXY_PORT_HIGH 65535
#define PF_OPTIMIZE_BASIC 0x0001
#define PF_OPTIMIZE_PROFILE 0x0002
#define FCNT_NAMES { \
"searches", \
"inserts", \
"removals", \
NULL \
}
struct pfr_buffer; /* forward definition */
struct pfctl_statelim {
struct pfioc_statelim ioc;
RBT_ENTRY(pfctl_statelim) id_entry;
RBT_ENTRY(pfctl_statelim) nm_entry;
};
RBT_HEAD(pfctl_statelim_ids, pfctl_statelim);
RBT_HEAD(pfctl_statelim_nms, pfctl_statelim);
struct pfctl_sourcelim {
struct pfioc_sourcelim ioc;
RBT_ENTRY(pfctl_sourcelim) id_entry;
RBT_ENTRY(pfctl_sourcelim) nm_entry;
};
RBT_HEAD(pfctl_sourcelim_ids, pfctl_sourcelim);
RBT_HEAD(pfctl_sourcelim_nms, pfctl_sourcelim);
struct pfctl {
int dev;
int opts;
int optimize;
int asd; /* anchor stack depth */
int bn; /* brace number */
int brace;
int tdirty; /* kernel dirty */
#define PFCTL_ANCHOR_STACK_DEPTH 64
struct pf_anchor *astack[PFCTL_ANCHOR_STACK_DEPTH];
struct pfioc_queue *pqueue;
struct pfr_buffer *trans;
struct pf_anchor *anchor, *alast;
struct pfr_ktablehead pfr_ktlast;
const char *ruleset;
struct pfctl_statelim_ids statelim_ids;
struct pfctl_statelim_nms statelim_nms;
struct pfctl_sourcelim_ids sourcelim_ids;
struct pfctl_sourcelim_nms sourcelim_nms;
/* 'set foo' options */
u_int32_t timeout[PFTM_MAX];
u_int32_t limit[PF_LIMIT_MAX];
u_int32_t debug;
u_int32_t hostid;
u_int32_t reassemble;
u_int8_t syncookies;
u_int8_t syncookieswat[2]; /* lowat, hiwat */
char *ifname;
u_int8_t timeout_set[PFTM_MAX];
u_int8_t limit_set[PF_LIMIT_MAX];
u_int8_t debug_set;
u_int8_t hostid_set;
u_int8_t ifname_set;
u_int8_t reass_set;
u_int8_t syncookies_set;
u_int8_t syncookieswat_set;
};
struct node_if {
char ifname[IFNAMSIZ];
u_int8_t not;
u_int8_t dynamic; /* antispoof */
u_int8_t use_rdomain;
u_int ifa_flags;
int rdomain;
struct node_if *next;
struct node_if *tail;
};
struct node_host {
struct pf_addr_wrap addr;
struct pf_addr bcast;
struct pf_addr peer;
sa_family_t af;
u_int8_t not;
u_int32_t ifindex; /* link-local IPv6 addrs */
u_int16_t weight; /* load balancing weight */
char *ifname;
u_int ifa_flags;
struct node_host *next;
struct node_host *tail;
};
void freehostlist(struct node_host *);
struct node_os {
char *os;
pf_osfp_t fingerprint;
struct node_os *next;
struct node_os *tail;
};
struct node_queue_bw {
u_int64_t bw_absolute;
u_int16_t bw_percent;
};
struct node_hfsc_sc {
struct node_queue_bw m1; /* slope of 1st segment; bps */
u_int d; /* x-projection of m1; msec */
struct node_queue_bw m2; /* slope of 2nd segment; bps */
u_int8_t used;
};
struct node_hfsc_opts {
struct node_hfsc_sc realtime;
struct node_hfsc_sc linkshare;
struct node_hfsc_sc upperlimit;
int flags;
};
struct node_queue_opt {
int qtype;
union {
struct priq_opts priq_opts;
struct node_hfsc_opts hfsc_opts;
} data;
};
SIMPLEQ_HEAD(node_tinithead, node_tinit);
struct node_tinit { /* table initializer */
SIMPLEQ_ENTRY(node_tinit) entries;
struct node_host *host;
char *file;
};
/* optimizer created tables */
struct pf_opt_tbl {
char pt_name[PF_TABLE_NAME_SIZE];
int pt_rulecount;
int pt_generated;
u_int32_t pt_flags;
u_int32_t pt_refcnt;
struct node_tinithead pt_nodes;
struct pfr_buffer *pt_buf;
};
/* optimizer pf_rule container */
struct pf_opt_rule {
struct pf_rule por_rule;
struct pf_opt_tbl *por_src_tbl;
struct pf_opt_tbl *por_dst_tbl;
u_int64_t por_profile_count;
TAILQ_ENTRY(pf_opt_rule) por_entry;
TAILQ_ENTRY(pf_opt_rule) por_skip_entry[PF_SKIP_COUNT];
};
TAILQ_HEAD(pf_opt_queue, pf_opt_rule);
extern TAILQ_HEAD(pf_qihead, pfctl_qsitem) qspecs, rootqs;
struct pfctl_qsitem {
TAILQ_ENTRY(pfctl_qsitem) entries;
struct pf_queuespec qs;
struct pf_qihead children;
int matches;
};
struct pfctl_watermarks {
u_int32_t hi;
u_int32_t lo;
};
struct pfr_uktable;
void copy_satopfaddr(struct pf_addr *, struct sockaddr *);
int pfctl_rules(int, char *, int, int, char *, struct pfr_buffer *);
int pfctl_optimize_ruleset(struct pfctl *, struct pf_ruleset *);
int pf_opt_create_table(struct pfctl *, struct pf_opt_tbl *);
int add_opt_table(struct pfctl *, struct pf_opt_tbl **, sa_family_t,
struct pf_rule_addr *, char *);
void pfctl_add_rule(struct pfctl *, struct pf_rule *);
int pfctl_add_statelim(struct pfctl *, struct pfctl_statelim *);
struct pfctl_statelim *
pfctl_get_statelim_id(struct pfctl *, uint32_t);
struct pfctl_statelim *
pfctl_get_statelim_nm(struct pfctl *, const char *);
int pfctl_add_sourcelim(struct pfctl *, struct pfctl_sourcelim *);
struct pfctl_sourcelim *
pfctl_get_sourcelim_id(struct pfctl *, uint32_t);
struct pfctl_sourcelim *
pfctl_get_sourcelim_nm(struct pfctl *, const char *);
int pfctl_set_timeout(struct pfctl *, const char *, int, int);
int pfctl_set_reassembly(struct pfctl *, int, int);
int pfctl_set_syncookies(struct pfctl *, u_int8_t,
struct pfctl_watermarks *);
int pfctl_set_optimization(struct pfctl *, const char *);
int pfctl_set_limit(struct pfctl *, const char *, unsigned int);
int pfctl_set_logif(struct pfctl *, char *);
void pfctl_set_hostid(struct pfctl *, u_int32_t);
int pfctl_set_debug(struct pfctl *, char *);
int pfctl_set_interface_flags(struct pfctl *, char *, int, int);
int parse_config(char *, struct pfctl *);
int parse_flags(char *);
int pfctl_load_anchors(int, struct pfctl *);
int pfctl_load_queues(struct pfctl *);
int pfctl_add_queue(struct pfctl *, struct pf_queuespec *);
struct pfctl_qsitem * pfctl_find_queue(char *, struct pf_qihead *);
void print_pool(struct pf_pool *, u_int16_t, u_int16_t, sa_family_t, int, int);
void print_src_node(struct pf_src_node *, int);
void print_statelim(const struct pfioc_statelim *);
void print_sourcelim(const struct pfioc_sourcelim *);
void print_rule(struct pfctl *pf, struct pf_rule *, const char *, int);
void print_tabledef(const char *, int, int, struct node_tinithead *);
void print_status(struct pf_status *, struct pfctl_watermarks *, int);
void print_queuespec(struct pf_queuespec *);
int pfctl_define_table(char *, int, int, const char *, struct pfr_buffer *,
u_int32_t, struct pfr_uktable *);
void pfctl_expand_label_nr(struct pf_rule *, unsigned int);
void pfctl_clear_fingerprints(int, int);
int pfctl_file_fingerprints(int, int, const char *);
pf_osfp_t pfctl_get_fingerprint(const char *);
int pfctl_load_fingerprints(int, int);
char *pfctl_lookup_fingerprint(pf_osfp_t, char *, size_t);
void pfctl_show_fingerprints(int);
struct icmptypeent {
const char *name;
u_int8_t type;
};
struct icmpcodeent {
const char *name;
u_int8_t type;
u_int8_t code;
};
/* PFLINUX: last arg is the address family; the definitions below use
* sa_family_t, which on Linux is `unsigned short` (OpenBSD: u_int8_t).
* Header and definition must agree. */
const struct icmptypeent *geticmptypebynumber(u_int8_t, sa_family_t);
const struct icmptypeent *geticmptypebyname(char *, sa_family_t);
const struct icmpcodeent *geticmpcodebynumber(u_int8_t, u_int8_t, sa_family_t);
const struct icmpcodeent *geticmpcodebyname(u_long, char *, sa_family_t);
int string_to_loglevel(const char *);
const char *loglevel_to_string(int);
struct pf_timeout {
const char *name;
int timeout;
};
extern const struct pf_timeout pf_timeouts[];
void set_ipmask(struct node_host *, int);
int check_netmask(struct node_host *, sa_family_t);
int unmask(struct pf_addr *);
struct node_host *gen_dynnode(struct node_host *, sa_family_t);
void ifa_load(void);
unsigned int ifa_nametoindex(const char *);
char *ifa_indextoname(unsigned int, char *);
struct node_host *ifa_exists(const char *);
struct node_host *ifa_lookup(const char *, int);
struct node_host *host(const char *, int);
int append_addr(struct pfr_buffer *, char *, int, int);
int append_addr_host(struct pfr_buffer *,
struct node_host *, int, int);
int pfr_ktable_compare(struct pfr_ktable *,
struct pfr_ktable *);
RB_PROTOTYPE(pfr_ktablehead, pfr_ktable, pfrkt_tree, pfr_ktable_compare);
#endif /* _PFCTL_PARSER_H_ */
+287
View File
@@ -0,0 +1,287 @@
/* $OpenBSD: pfctl_queue.c,v 1.8 2024/05/19 10:39:40 jsg Exp $ */
/*
* Copyright (c) 2003 - 2013 Henning Brauer <henning@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.
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <net/pfvar.h>
#include <arpa/inet.h>
#include <err.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/hfsc.h>
#include <net/fq_codel.h>
#include "pfctl.h"
#include "pfctl_parser.h"
#define AVGN_MAX 8
#define STAT_INTERVAL 5
struct queue_stats {
union {
struct hfsc_class_stats hfsc;
struct fqcodel_stats fqc;
} data;
int avgn;
double avg_bytes;
double avg_packets;
u_int64_t prev_bytes;
u_int64_t prev_packets;
};
struct pfctl_queue_node {
TAILQ_ENTRY(pfctl_queue_node) entries;
struct pf_queuespec qs;
struct queue_stats qstats;
};
TAILQ_HEAD(qnodes, pfctl_queue_node) qnodes = TAILQ_HEAD_INITIALIZER(qnodes);
int pfctl_update_qstats(int);
void pfctl_insert_queue_node(const struct pf_queuespec,
const struct queue_stats);
struct pfctl_queue_node *pfctl_find_queue_node(const char *, const char *);
void pfctl_print_queue_node(int, struct pfctl_queue_node *,
int);
void pfctl_print_queue_nodestat(int,
const struct pfctl_queue_node *);
void update_avg(struct queue_stats *);
char *rate2str(double);
int
pfctl_show_queues(int dev, const char *iface, int opts, int verbose2)
{
struct pfctl_queue_node *node;
int nodes, dotitle = (opts & PF_OPT_SHOWALL);
if ((nodes = pfctl_update_qstats(dev)) <= 0)
return (nodes);
TAILQ_FOREACH(node, &qnodes, entries) {
if (iface != NULL && strcmp(node->qs.ifname, iface))
continue;
if (dotitle) {
pfctl_print_title("QUEUES:");
dotitle = 0;
}
pfctl_print_queue_node(dev, node, opts);
}
while (verbose2 && nodes > 0) {
printf("\n");
fflush(stdout);
sleep(STAT_INTERVAL);
if ((nodes = pfctl_update_qstats(dev)) == -1)
return (-1);
TAILQ_FOREACH(node, &qnodes, entries) {
if (iface != NULL && strcmp(node->qs.ifname, iface))
continue;
pfctl_print_queue_node(dev, node, opts);
}
}
while ((node = TAILQ_FIRST(&qnodes)) != NULL)
TAILQ_REMOVE(&qnodes, node, entries);
return (0);
}
int
pfctl_update_qstats(int dev)
{
struct pfctl_queue_node *node;
struct pfioc_queue pq;
struct pfioc_qstats pqs;
u_int32_t mnr, nr;
struct queue_stats qstats;
static u_int32_t last_ticket;
memset(&pq, 0, sizeof(pq));
memset(&pqs, 0, sizeof(pqs));
memset(&qstats, 0, sizeof(qstats));
if (ioctl(dev, DIOCGETQUEUES, &pq) == -1) {
warn("DIOCGETQUEUES");
return (-1);
}
/* if a new set is found, start over */
if (pq.ticket != last_ticket)
while ((node = TAILQ_FIRST(&qnodes)) != NULL)
TAILQ_REMOVE(&qnodes, node, entries);
last_ticket = pq.ticket;
mnr = pq.nr;
for (nr = 0; nr < mnr; ++nr) {
pqs.nr = nr;
pqs.ticket = pq.ticket;
pqs.buf = &qstats.data;
pqs.nbytes = sizeof(qstats.data);
if (ioctl(dev, DIOCGETQSTATS, &pqs) == -1) {
warn("DIOCGETQSTATS");
return (-1);
}
if ((node = pfctl_find_queue_node(pqs.queue.qname,
pqs.queue.ifname)) != NULL) {
memcpy(&node->qstats.data, &qstats.data,
sizeof(qstats.data));
update_avg(&node->qstats);
} else {
pfctl_insert_queue_node(pqs.queue, qstats);
}
}
return (mnr);
}
void
pfctl_insert_queue_node(const struct pf_queuespec qs,
const struct queue_stats qstats)
{
struct pfctl_queue_node *node;
node = calloc(1, sizeof(struct pfctl_queue_node));
if (node == NULL)
err(1, "pfctl_insert_queue_node: calloc");
memcpy(&node->qs, &qs, sizeof(qs));
memcpy(&node->qstats, &qstats, sizeof(qstats));
TAILQ_INSERT_TAIL(&qnodes, node, entries);
update_avg(&node->qstats);
}
struct pfctl_queue_node *
pfctl_find_queue_node(const char *qname, const char *ifname)
{
struct pfctl_queue_node *node;
TAILQ_FOREACH(node, &qnodes, entries)
if (!strcmp(node->qs.qname, qname)
&& !(strcmp(node->qs.ifname, ifname)))
return (node);
return (NULL);
}
void
pfctl_print_queue_node(int dev, struct pfctl_queue_node *node, int opts)
{
if (node == NULL)
return;
print_queuespec(&node->qs);
if (opts & PF_OPT_VERBOSE)
pfctl_print_queue_nodestat(dev, node);
if (opts & PF_OPT_DEBUG)
printf(" [ qid=%u parent_qid=%u ifname=%s]\n",
node->qs.qid, node->qs.parent_qid, node->qs.ifname);
}
void
pfctl_print_queue_nodestat(int dev, const struct pfctl_queue_node *node)
{
struct hfsc_class_stats *stats =
(struct hfsc_class_stats *)&node->qstats.data.hfsc;
struct fqcodel_stats *fqstats =
(struct fqcodel_stats *)&node->qstats.data.fqc;
printf(" [ pkts: %10llu bytes: %10llu "
"dropped pkts: %6llu bytes: %6llu ]\n",
(unsigned long long)stats->xmit_cnt.packets,
(unsigned long long)stats->xmit_cnt.bytes,
(unsigned long long)stats->drop_cnt.packets,
(unsigned long long)stats->drop_cnt.bytes);
if (node->qs.parent_qid == 0 && (node->qs.flags & PFQS_FLOWQUEUE) &&
!(node->qs.flags & PFQS_ROOTCLASS)) {
double avg = 0, dev = 0;
if (fqstats->flows > 0) {
avg = (double)fqstats->delaysum /
(double)fqstats->flows;
dev = sqrt(fmax(0, (double)fqstats->delaysumsq /
(double)fqstats->flows - avg * avg));
}
printf(" [ qlength: %3d/%3d avg delay: %.3fms std-dev: %.3fms"
" flows: %3d ]\n", stats->qlength, stats->qlimit,
avg / 1000, dev / 1000, fqstats->flows);
} else
printf(" [ qlength: %3d/%3d ]\n", stats->qlength,
stats->qlimit);
if (node->qstats.avgn < 2)
return;
printf(" [ measured: %7.1f packets/s, %s/s ]\n",
node->qstats.avg_packets / STAT_INTERVAL,
rate2str((8 * node->qstats.avg_bytes) / STAT_INTERVAL));
}
void
update_avg(struct queue_stats *s)
{
struct hfsc_class_stats *stats =
(struct hfsc_class_stats *)&s->data;
if (s->avgn > 0) {
if (stats->xmit_cnt.bytes >= s->prev_bytes)
s->avg_bytes = ((s->avg_bytes * (s->avgn - 1)) +
(stats->xmit_cnt.bytes - s->prev_bytes)) /
s->avgn;
if (stats->xmit_cnt.packets >= s->prev_packets)
s->avg_packets = ((s->avg_packets * (s->avgn - 1)) +
(stats->xmit_cnt.packets - s->prev_packets)) /
s->avgn;
}
s->prev_bytes = stats->xmit_cnt.bytes;
s->prev_packets = stats->xmit_cnt.packets;
if (s->avgn < AVGN_MAX)
s->avgn++;
}
#define R2S_BUFS 8
#define RATESTR_MAX 16
char *
rate2str(double rate)
{
char *buf;
static char r2sbuf[R2S_BUFS][RATESTR_MAX]; /* ring bufer */
static int idx = 0;
int i;
static const char unit[] = " KMG";
buf = r2sbuf[idx++];
if (idx == R2S_BUFS)
idx = 0;
for (i = 0; rate >= 1000 && i <= 3; i++)
rate /= 1000;
if ((int)(rate * 100) % 100)
snprintf(buf, RATESTR_MAX, "%.2f%cb", rate, unit[i]);
else
snprintf(buf, RATESTR_MAX, "%d%cb", (int)rate, unit[i]);
return (buf);
}
+598
View File
@@ -0,0 +1,598 @@
/* $OpenBSD: pfctl_radix.c,v 1.40 2024/11/20 13:57:29 kirill Exp $ */
/*
* Copyright (c) 2002 Cedric Berger
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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
* COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
*
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <net/pfvar.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <err.h>
#include "pfctl.h"
#include "pfctl_parser.h"
#define BUF_SIZE 256
extern int dev;
static int pfr_next_token(char buf[BUF_SIZE], FILE *);
struct pfr_ktablehead pfr_ktables = { 0 };
RB_GENERATE(pfr_ktablehead, pfr_ktable, pfrkt_tree, pfr_ktable_compare);
int
pfr_ktable_compare(struct pfr_ktable *p, struct pfr_ktable *q)
{
int d;
if ((d = strncmp(p->pfrkt_name, q->pfrkt_name, PF_TABLE_NAME_SIZE)))
return (d);
return (strcmp(p->pfrkt_anchor, q->pfrkt_anchor));
}
int
pfr_clr_tables(struct pfr_table *filter, int *ndel, int flags)
{
struct pfioc_table io;
bzero(&io, sizeof io);
io.pfrio_flags = flags;
if (filter != NULL)
io.pfrio_table = *filter;
if (ioctl(dev, DIOCRCLRTABLES, &io) == -1)
return (-1);
if (ndel != NULL)
*ndel = io.pfrio_ndel;
return (0);
}
int
pfr_add_tables(struct pfr_table *tbl, int size, int *nadd, int flags)
{
struct pfioc_table io;
if (size < 0 || (size && tbl == NULL)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_buffer = tbl;
io.pfrio_esize = sizeof(*tbl);
io.pfrio_size = size;
if (ioctl(dev, DIOCRADDTABLES, &io) == -1)
return (-1);
if (nadd != NULL)
*nadd = io.pfrio_nadd;
return (0);
}
int
pfr_del_tables(struct pfr_table *tbl, int size, int *ndel, int flags)
{
struct pfioc_table io;
if (size < 0 || (size && tbl == NULL)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_buffer = tbl;
io.pfrio_esize = sizeof(*tbl);
io.pfrio_size = size;
if (ioctl(dev, DIOCRDELTABLES, &io) == -1)
return (-1);
if (ndel != NULL)
*ndel = io.pfrio_ndel;
return (0);
}
int
pfr_get_tables(struct pfr_table *filter, struct pfr_table *tbl, int *size,
int flags)
{
struct pfioc_table io;
if (size == NULL || *size < 0 || (*size && tbl == NULL)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
if (filter != NULL)
io.pfrio_table = *filter;
io.pfrio_buffer = tbl;
io.pfrio_esize = sizeof(*tbl);
io.pfrio_size = *size;
if (ioctl(dev, DIOCRGETTABLES, &io) == -1)
return (-1);
*size = io.pfrio_size;
return (0);
}
int
pfr_get_tstats(struct pfr_table *filter, struct pfr_tstats *tbl, int *size,
int flags)
{
struct pfioc_table io;
if (size == NULL || *size < 0 || (*size && tbl == NULL)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
if (filter != NULL)
io.pfrio_table = *filter;
io.pfrio_buffer = tbl;
io.pfrio_esize = sizeof(*tbl);
io.pfrio_size = *size;
if (ioctl(dev, DIOCRGETTSTATS, &io) == -1)
return (-1);
*size = io.pfrio_size;
return (0);
}
int
pfr_clr_addrs(struct pfr_table *tbl, int *ndel, int flags)
{
struct pfioc_table io;
if (tbl == NULL) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_table = *tbl;
if (ioctl(dev, DIOCRCLRADDRS, &io) == -1)
return (-1);
if (ndel != NULL)
*ndel = io.pfrio_ndel;
return (0);
}
int
pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
int *nadd, int flags)
{
struct pfioc_table io;
if (tbl == NULL || size < 0 || (size && addr == NULL)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_table = *tbl;
io.pfrio_buffer = addr;
io.pfrio_esize = sizeof(*addr);
io.pfrio_size = size;
if (ioctl(dev, DIOCRADDADDRS, &io) == -1)
return (-1);
if (nadd != NULL)
*nadd = io.pfrio_nadd;
return (0);
}
int
pfr_del_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
int *ndel, int flags)
{
struct pfioc_table io;
if (tbl == NULL || size < 0 || (size && addr == NULL)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_table = *tbl;
io.pfrio_buffer = addr;
io.pfrio_esize = sizeof(*addr);
io.pfrio_size = size;
if (ioctl(dev, DIOCRDELADDRS, &io) == -1)
return (-1);
if (ndel != NULL)
*ndel = io.pfrio_ndel;
return (0);
}
int
pfr_set_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
int *size2, int *nadd, int *ndel, int *nchange, int flags)
{
struct pfioc_table io;
if (tbl == NULL || size < 0 || (size && addr == NULL)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_table = *tbl;
io.pfrio_buffer = addr;
io.pfrio_esize = sizeof(*addr);
io.pfrio_size = size;
io.pfrio_size2 = (size2 != NULL) ? *size2 : 0;
if (ioctl(dev, DIOCRSETADDRS, &io) == -1)
return (-1);
if (nadd != NULL)
*nadd = io.pfrio_nadd;
if (ndel != NULL)
*ndel = io.pfrio_ndel;
if (nchange != NULL)
*nchange = io.pfrio_nchange;
if (size2 != NULL)
*size2 = io.pfrio_size2;
return (0);
}
int
pfr_get_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int *size,
int flags)
{
struct pfioc_table io;
if (tbl == NULL || size == NULL || *size < 0 ||
(*size && addr == NULL)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_table = *tbl;
io.pfrio_buffer = addr;
io.pfrio_esize = sizeof(*addr);
io.pfrio_size = *size;
if (ioctl(dev, DIOCRGETADDRS, &io) == -1)
return (-1);
*size = io.pfrio_size;
return (0);
}
int
pfr_get_astats(struct pfr_table *tbl, struct pfr_astats *addr, int *size,
int flags)
{
struct pfioc_table io;
if (tbl == NULL || size == NULL || *size < 0 ||
(*size && addr == NULL)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_table = *tbl;
io.pfrio_buffer = addr;
io.pfrio_esize = sizeof(*addr);
io.pfrio_size = *size;
if (ioctl(dev, DIOCRGETASTATS, &io) == -1)
return (-1);
*size = io.pfrio_size;
return (0);
}
int
pfr_clr_astats(struct pfr_table *tbl, struct pfr_addr *addr, int size,
int *nzero, int flags)
{
struct pfioc_table io;
if (size < 0 || (size && !tbl) || addr == NULL) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_table = *tbl;
io.pfrio_buffer = addr;
io.pfrio_esize = sizeof(*addr);
io.pfrio_size = size;
if (ioctl(dev, DIOCRCLRASTATS, &io) == -1)
return (-1);
if (nzero)
*nzero = io.pfrio_nzero;
return (0);
}
int
pfr_clr_tstats(struct pfr_table *tbl, int size, int *nzero, int flags)
{
struct pfioc_table io;
if (size < 0 || (size && !tbl)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_buffer = tbl;
io.pfrio_esize = sizeof(*tbl);
io.pfrio_size = size;
if (ioctl(dev, DIOCRCLRTSTATS, &io) == -1)
return (-1);
if (nzero)
*nzero = io.pfrio_nzero;
return (0);
}
int
pfr_tst_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
int *nmatch, int flags)
{
struct pfioc_table io;
if (tbl == NULL || size < 0 || (size && addr == NULL)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_table = *tbl;
io.pfrio_buffer = addr;
io.pfrio_esize = sizeof(*addr);
io.pfrio_size = size;
if (ioctl(dev, DIOCRTSTADDRS, &io) == -1)
return (-1);
if (nmatch)
*nmatch = io.pfrio_nmatch;
return (0);
}
int
pfr_ina_define(struct pfr_table *tbl, struct pfr_addr *addr, int size,
int *nadd, int *naddr, int ticket, int flags)
{
struct pfioc_table io;
if (tbl == NULL || size < 0 || (size && addr == NULL)) {
DBGPRINT("%s %p %d %p\n", __func__, tbl, size, addr);
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_table = *tbl;
io.pfrio_buffer = addr;
io.pfrio_esize = sizeof(*addr);
io.pfrio_size = size;
io.pfrio_ticket = ticket;
if (ioctl(dev, DIOCRINADEFINE, &io) == -1)
return (-1);
if (nadd != NULL)
*nadd = io.pfrio_nadd;
if (naddr != NULL)
*naddr = io.pfrio_naddr;
return (0);
}
/* interface management code */
int
pfi_get_ifaces(const char *filter, struct pfi_kif *buf, int *size)
{
struct pfioc_iface io;
if (size == NULL || *size < 0 || (*size && buf == NULL)) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
if (filter != NULL)
if (strlcpy(io.pfiio_name, filter, sizeof(io.pfiio_name)) >=
sizeof(io.pfiio_name)) {
errno = EINVAL;
return (-1);
}
io.pfiio_buffer = buf;
io.pfiio_esize = sizeof(*buf);
io.pfiio_size = *size;
if (ioctl(dev, DIOCIGETIFACES, &io) == -1)
return (-1);
*size = io.pfiio_size;
return (0);
}
/* buffer management code */
size_t buf_esize[PFRB_MAX] = { 0,
sizeof(struct pfr_table), sizeof(struct pfr_tstats),
sizeof(struct pfr_addr), sizeof(struct pfr_astats),
sizeof(struct pfi_kif), sizeof(struct pfioc_trans_e)
};
/*
* add one element to the buffer
*/
int
pfr_buf_add(struct pfr_buffer *b, const void *e)
{
size_t bs;
if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX ||
e == NULL) {
errno = EINVAL;
return (-1);
}
bs = buf_esize[b->pfrb_type];
if (b->pfrb_size == b->pfrb_msize)
if (pfr_buf_grow(b, 0))
return (-1);
memcpy(((caddr_t)b->pfrb_caddr) + bs * b->pfrb_size, e, bs);
b->pfrb_size++;
return (0);
}
/*
* return next element of the buffer (or first one if prev is NULL)
* see PFRB_FOREACH macro
*/
void *
pfr_buf_next(struct pfr_buffer *b, const void *prev)
{
size_t bs;
if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX)
return (NULL);
if (b->pfrb_size == 0)
return (NULL);
if (prev == NULL)
return (b->pfrb_caddr);
bs = buf_esize[b->pfrb_type];
if ((((caddr_t)prev)-((caddr_t)b->pfrb_caddr)) / bs >= b->pfrb_size-1)
return (NULL);
return (((caddr_t)prev) + bs);
}
/*
* minsize:
* 0: make the buffer somewhat bigger
* n: make room for "n" entries in the buffer
*/
int
pfr_buf_grow(struct pfr_buffer *b, int minsize)
{
caddr_t p;
size_t bs;
if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX) {
errno = EINVAL;
return (-1);
}
if (minsize != 0 && minsize <= b->pfrb_msize)
return (0);
bs = buf_esize[b->pfrb_type];
if (!b->pfrb_msize) {
if (minsize < 64)
minsize = 64;
}
if (minsize == 0)
minsize = b->pfrb_msize * 2;
p = reallocarray(b->pfrb_caddr, minsize, bs);
if (p == NULL)
return (-1);
bzero(p + b->pfrb_msize * bs, (minsize - b->pfrb_msize) * bs);
b->pfrb_caddr = p;
b->pfrb_msize = minsize;
return (0);
}
/*
* reset buffer and free memory.
*/
void
pfr_buf_clear(struct pfr_buffer *b)
{
if (b == NULL)
return;
free(b->pfrb_caddr);
b->pfrb_caddr = NULL;
b->pfrb_size = b->pfrb_msize = 0;
}
int
pfr_buf_load(struct pfr_buffer *b, char *file, int nonetwork, int opts)
{
FILE *fp;
char buf[BUF_SIZE];
int rv;
int ev = 0;
if (file == NULL)
return (0);
if (!strcmp(file, "-"))
fp = stdin;
else {
fp = pfctl_fopen(file, "r");
if (fp == NULL)
return (-1);
}
while ((rv = pfr_next_token(buf, fp)) == 1)
if ((ev = append_addr(b, buf, nonetwork, opts)) == -1) {
rv = -1;
break;
}
if (ev == 1) /* expected further append_addr call */
rv = -1;
if (fp != stdin)
fclose(fp);
return (rv);
}
int
pfr_next_token(char buf[BUF_SIZE], FILE *fp)
{
static char next_ch = ' ';
int i = 0;
for (;;) {
/* skip spaces */
while (isspace((unsigned char)next_ch) && !feof(fp))
next_ch = fgetc(fp);
/* remove from '#' until end of line */
if (next_ch == '#')
while (!feof(fp)) {
next_ch = fgetc(fp);
if (next_ch == '\n')
break;
}
else
break;
}
if (feof(fp)) {
next_ch = ' ';
return (0);
}
do {
if (i < BUF_SIZE)
buf[i++] = next_ch;
next_ch = fgetc(fp);
} while (!feof(fp) && !isspace((unsigned char)next_ch));
if (i >= BUF_SIZE) {
errno = EINVAL;
return (-1);
}
buf[i] = '\0';
return (1);
}
+692
View File
@@ -0,0 +1,692 @@
/* $OpenBSD: pfctl_table.c,v 1.91 2024/11/20 13:57:29 kirill Exp $ */
/*
* Copyright (c) 2002 Cedric Berger
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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
* COPYRIGHT HOLDERS OR CONTRIBUTORS 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.
*
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <net/pfvar.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <netdb.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <limits.h>
#include "pfctl_parser.h"
#include "pfctl.h"
extern void usage(void);
static void print_table(struct pfr_table *, int, int);
static void print_tstats(struct pfr_tstats *, int);
static int load_addr(struct pfr_buffer *, int, char *[], char *, int, int);
static void print_addrx(struct pfr_addr *, struct pfr_addr *, int);
static void print_astats(struct pfr_astats *, int);
static void xprintf(int, const char *, ...);
static void print_iface(struct pfi_kif *, int);
static const char *stats_text[PFR_DIR_MAX][PFR_OP_TABLE_MAX] = {
{ "In/Block:", "In/Match:", "In/Pass:", "In/XPass:" },
{ "Out/Block:", "Out/Match:", "Out/Pass:", "Out/XPass:" }
};
static const char *istats_text[2][2][2] = {
{ { "In4/Pass:", "In4/Block:" }, { "Out4/Pass:", "Out4/Block:" } },
{ { "In6/Pass:", "In6/Block:" }, { "Out6/Pass:", "Out6/Block:" } }
};
#define RVTEST(fct) do { \
if ((!(opts & PF_OPT_NOACTION) || \
(opts & PF_OPT_DUMMYACTION)) && \
(fct)) { \
if ((opts & PF_OPT_RECURSE) == 0) \
warnx("%s", pf_strerror(errno)); \
goto _error; \
} \
} while (0)
#define CREATE_TABLE do { \
warn_duplicate_tables(table.pfrt_name, \
table.pfrt_anchor); \
table.pfrt_flags |= PFR_TFLAG_PERSIST; \
if ((!(opts & PF_OPT_NOACTION) || \
(opts & PF_OPT_DUMMYACTION)) && \
(pfr_add_tables(&table, 1, &nadd, flags)) && \
(errno != EPERM)) { \
warnx("%s", pf_strerror(errno)); \
goto _error; \
} \
if (nadd) { \
xprintf(opts, "%d table created", nadd); \
if (opts & PF_OPT_NOACTION) \
return (0); \
} \
table.pfrt_flags &= ~PFR_TFLAG_PERSIST; \
} while(0)
int
pfctl_clear_tables(const char *anchor, int opts)
{
int rv;
if ((rv = pfctl_table(0, NULL, NULL, "-F", NULL, anchor, opts)) == -1) {
if ((opts & PF_OPT_IGNFAIL) == 0)
exit(1);
}
return (rv);
}
void
pfctl_show_tables(const char *anchor, int opts)
{
if (pfctl_table(0, NULL, NULL, "-s", NULL, anchor, opts) == -1)
exit(1);
}
int
pfctl_table(int argc, char *argv[], char *tname, const char *command,
char *file, const char *anchor, int opts)
{
struct pfr_table table;
struct pfr_buffer b, b2;
struct pfr_addr *a, *a2;
int nadd = 0, ndel = 0, nchange = 0, nzero = 0;
int rv = 0, flags = 0, nmatch = 0;
void *p;
if (command == NULL)
usage();
if (opts & PF_OPT_NOACTION)
flags |= PFR_FLAG_DUMMY;
bzero(&b, sizeof(b));
bzero(&b2, sizeof(b2));
bzero(&table, sizeof(table));
if (tname != NULL) {
if (strlen(tname) >= PF_TABLE_NAME_SIZE)
usage();
if (strlcpy(table.pfrt_name, tname,
sizeof(table.pfrt_name)) >= sizeof(table.pfrt_name))
errx(1, "pfctl_table: strlcpy");
}
if (strlcpy(table.pfrt_anchor, anchor,
sizeof(table.pfrt_anchor)) >= sizeof(table.pfrt_anchor))
errx(1, "pfctl_table: strlcpy");
if (!strcmp(command, "-F")) {
if (argc || file != NULL)
usage();
RVTEST(pfr_clr_tables(&table, &ndel, flags));
xprintf(opts, "%d tables deleted", ndel);
} else if (!strcmp(command, "-s")) {
b.pfrb_type = (opts & PF_OPT_VERBOSE2) ?
PFRB_TSTATS : PFRB_TABLES;
if (argc || file != NULL)
usage();
for (;;) {
pfr_buf_grow(&b, b.pfrb_size);
b.pfrb_size = b.pfrb_msize;
if (opts & PF_OPT_VERBOSE2)
RVTEST(pfr_get_tstats(&table,
b.pfrb_caddr, &b.pfrb_size, flags));
else
RVTEST(pfr_get_tables(&table,
b.pfrb_caddr, &b.pfrb_size, flags));
if (b.pfrb_size <= b.pfrb_msize)
break;
}
if ((opts & PF_OPT_SHOWALL) && b.pfrb_size > 0)
pfctl_print_title("TABLES:");
PFRB_FOREACH(p, &b)
if (opts & PF_OPT_VERBOSE2)
print_tstats(p, opts & PF_OPT_DEBUG);
else
print_table(p, opts & PF_OPT_VERBOSE,
opts & PF_OPT_DEBUG);
} else if (!strcmp(command, "kill")) {
if (argc || file != NULL)
usage();
RVTEST(pfr_del_tables(&table, 1, &ndel, flags));
xprintf(opts, "%d table deleted", ndel);
} else if (!strcmp(command, "flush")) {
if (argc || file != NULL)
usage();
RVTEST(pfr_clr_addrs(&table, &ndel, flags));
xprintf(opts, "%d addresses deleted", ndel);
} else if (!strcmp(command, "add")) {
b.pfrb_type = PFRB_ADDRS;
if (load_addr(&b, argc, argv, file, 0, opts))
goto _error;
CREATE_TABLE;
if (opts & PF_OPT_VERBOSE)
flags |= PFR_FLAG_FEEDBACK;
RVTEST(pfr_add_addrs(&table, b.pfrb_caddr, b.pfrb_size,
&nadd, flags));
xprintf(opts, "%d/%d addresses added", nadd, b.pfrb_size);
if (opts & PF_OPT_VERBOSE)
PFRB_FOREACH(a, &b)
if (opts & PF_OPT_VERBOSE2 ||
a->pfra_fback != PFR_FB_NONE)
print_addrx(a, NULL,
opts & PF_OPT_USEDNS);
} else if (!strcmp(command, "delete")) {
b.pfrb_type = PFRB_ADDRS;
if (load_addr(&b, argc, argv, file, 0, opts))
goto _error;
if (opts & PF_OPT_VERBOSE)
flags |= PFR_FLAG_FEEDBACK;
RVTEST(pfr_del_addrs(&table, b.pfrb_caddr, b.pfrb_size,
&ndel, flags));
xprintf(opts, "%d/%d addresses deleted", ndel, b.pfrb_size);
if (opts & PF_OPT_VERBOSE)
PFRB_FOREACH(a, &b)
if (opts & PF_OPT_VERBOSE2 ||
a->pfra_fback != PFR_FB_NONE)
print_addrx(a, NULL,
opts & PF_OPT_USEDNS);
} else if (!strcmp(command, "replace")) {
b.pfrb_type = PFRB_ADDRS;
if (load_addr(&b, argc, argv, file, 0, opts))
goto _error;
CREATE_TABLE;
if (opts & PF_OPT_VERBOSE)
flags |= PFR_FLAG_FEEDBACK;
for (;;) {
int sz2 = b.pfrb_msize;
RVTEST(pfr_set_addrs(&table, b.pfrb_caddr, b.pfrb_size,
&sz2, &nadd, &ndel, &nchange, flags));
if (sz2 <= b.pfrb_msize) {
b.pfrb_size = sz2;
break;
} else
pfr_buf_grow(&b, sz2);
}
if (nadd)
xprintf(opts, "%d addresses added", nadd);
if (ndel)
xprintf(opts, "%d addresses deleted", ndel);
if (nchange)
xprintf(opts, "%d addresses changed", nchange);
if (!nadd && !ndel && !nchange)
xprintf(opts, "no changes");
if (opts & PF_OPT_VERBOSE)
PFRB_FOREACH(a, &b)
if (opts & PF_OPT_VERBOSE2 ||
a->pfra_fback != PFR_FB_NONE)
print_addrx(a, NULL,
opts & PF_OPT_USEDNS);
} else if (!strcmp(command, "expire")) {
const char *errstr;
u_int lifetime;
b.pfrb_type = PFRB_ASTATS;
b2.pfrb_type = PFRB_ADDRS;
if (argc != 1 || file != NULL)
usage();
lifetime = strtonum(*argv, 0, UINT_MAX, &errstr);
if (errstr)
errx(1, "expiry time: %s", errstr);
for (;;) {
pfr_buf_grow(&b, b.pfrb_size);
b.pfrb_size = b.pfrb_msize;
RVTEST(pfr_get_astats(&table, b.pfrb_caddr,
&b.pfrb_size, flags));
if (b.pfrb_size <= b.pfrb_msize)
break;
}
PFRB_FOREACH(p, &b) {
((struct pfr_astats *)p)->pfras_a.pfra_fback = PFR_FB_NONE;
if (time(NULL) - ((struct pfr_astats *)p)->pfras_tzero >
lifetime)
if (pfr_buf_add(&b2,
&((struct pfr_astats *)p)->pfras_a))
err(1, "duplicate buffer");
}
if (opts & PF_OPT_VERBOSE)
flags |= PFR_FLAG_FEEDBACK;
RVTEST(pfr_del_addrs(&table, b2.pfrb_caddr, b2.pfrb_size,
&ndel, flags));
xprintf(opts, "%d/%d addresses expired", ndel, b2.pfrb_size);
if (opts & PF_OPT_VERBOSE)
PFRB_FOREACH(a, &b2)
if (opts & PF_OPT_VERBOSE2 ||
a->pfra_fback != PFR_FB_NONE)
print_addrx(a, NULL,
opts & PF_OPT_USEDNS);
} else if (!strcmp(command, "show")) {
b.pfrb_type = (opts & PF_OPT_VERBOSE) ?
PFRB_ASTATS : PFRB_ADDRS;
if (argc || file != NULL)
usage();
for (;;) {
pfr_buf_grow(&b, b.pfrb_size);
b.pfrb_size = b.pfrb_msize;
if (opts & PF_OPT_VERBOSE)
RVTEST(pfr_get_astats(&table, b.pfrb_caddr,
&b.pfrb_size, flags));
else
RVTEST(pfr_get_addrs(&table, b.pfrb_caddr,
&b.pfrb_size, flags));
if (b.pfrb_size <= b.pfrb_msize)
break;
}
PFRB_FOREACH(p, &b)
if (opts & PF_OPT_VERBOSE)
print_astats(p, opts & PF_OPT_USEDNS);
else
print_addrx(p, NULL, opts & PF_OPT_USEDNS);
} else if (!strcmp(command, "test")) {
b.pfrb_type = PFRB_ADDRS;
b2.pfrb_type = PFRB_ADDRS;
if (load_addr(&b, argc, argv, file, 1, opts))
goto _error;
if (opts & PF_OPT_VERBOSE2) {
flags |= PFR_FLAG_REPLACE;
PFRB_FOREACH(a, &b)
if (pfr_buf_add(&b2, a))
err(1, "duplicate buffer");
}
RVTEST(pfr_tst_addrs(&table, b.pfrb_caddr, b.pfrb_size,
&nmatch, flags));
xprintf(opts, "%d/%d addresses match", nmatch, b.pfrb_size);
if ((opts & PF_OPT_VERBOSE) && !(opts & PF_OPT_VERBOSE2))
PFRB_FOREACH(a, &b)
if (a->pfra_fback == PFR_FB_MATCH)
print_addrx(a, NULL,
opts & PF_OPT_USEDNS);
if (opts & PF_OPT_VERBOSE2) {
a2 = NULL;
PFRB_FOREACH(a, &b) {
a2 = pfr_buf_next(&b2, a2);
print_addrx(a2, a, opts & PF_OPT_USEDNS);
}
}
if (nmatch < b.pfrb_size)
rv = 2;
} else if (!strcmp(command, "zero") && (argc || file != NULL)) {
b.pfrb_type = PFRB_ADDRS;
if (load_addr(&b, argc, argv, file, 0, opts))
goto _error;
if (opts & PF_OPT_VERBOSE)
flags |= PFR_FLAG_FEEDBACK;
RVTEST(pfr_clr_astats(&table, b.pfrb_caddr, b.pfrb_size,
&nzero, flags));
xprintf(opts, "%d/%d addresses cleared", nzero, b.pfrb_size);
if (opts & PF_OPT_VERBOSE)
PFRB_FOREACH(a, &b)
if (opts & PF_OPT_VERBOSE2 ||
a->pfra_fback != PFR_FB_NONE)
print_addrx(a, NULL,
opts & PF_OPT_USEDNS);
} else if (!strcmp(command, "zero")) {
flags |= PFR_FLAG_ADDRSTOO;
RVTEST(pfr_clr_tstats(&table, 1, &nzero, flags));
xprintf(opts, "%d table/stats cleared", nzero);
} else
warnx("pfctl_table: unknown command '%s'", command);
goto _cleanup;
_error:
rv = -1;
_cleanup:
pfr_buf_clear(&b);
pfr_buf_clear(&b2);
return (rv);
}
void
print_table(struct pfr_table *ta, int verbose, int debug)
{
if (!debug && !(ta->pfrt_flags & PFR_TFLAG_ACTIVE))
return;
if (verbose)
printf("%c%c%c%c%c%c%c\t",
(ta->pfrt_flags & PFR_TFLAG_CONST) ? 'c' : '-',
(ta->pfrt_flags & PFR_TFLAG_PERSIST) ? 'p' : '-',
(ta->pfrt_flags & PFR_TFLAG_ACTIVE) ? 'a' : '-',
(ta->pfrt_flags & PFR_TFLAG_INACTIVE) ? 'i' : '-',
(ta->pfrt_flags & PFR_TFLAG_REFERENCED) ? 'r' : '-',
(ta->pfrt_flags & PFR_TFLAG_REFDANCHOR) ? 'h' : '-',
(ta->pfrt_flags & PFR_TFLAG_COUNTERS) ? 'C' : '-');
printf("%s", ta->pfrt_name);
if (ta->pfrt_anchor[0] != '\0')
printf("@%s", ta->pfrt_anchor);
printf("\n");
}
void
print_tstats(struct pfr_tstats *ts, int debug)
{
time_t time = ts->pfrts_tzero;
int dir, op;
char *ct;
if (!debug && !(ts->pfrts_flags & PFR_TFLAG_ACTIVE))
return;
ct = ctime(&time);
print_table(&ts->pfrts_t, 1, debug);
printf("\tAddresses: %d\n", ts->pfrts_cnt);
if (ct)
printf("\tCleared: %s", ct);
else
printf("\tCleared: %lld\n", time);
printf("\tReferences: [ Anchors: %-18d Rules: %-18d ]\n",
ts->pfrts_refcnt[PFR_REFCNT_ANCHOR],
ts->pfrts_refcnt[PFR_REFCNT_RULE]);
printf("\tEvaluations: [ NoMatch: %-18llu Match: %-18llu ]\n",
(unsigned long long)ts->pfrts_nomatch,
(unsigned long long)ts->pfrts_match);
for (dir = 0; dir < PFR_DIR_MAX; dir++)
for (op = 0; op < PFR_OP_TABLE_MAX; op++)
printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
stats_text[dir][op],
(unsigned long long)ts->pfrts_packets[dir][op],
(unsigned long long)ts->pfrts_bytes[dir][op]);
}
int
load_addr(struct pfr_buffer *b, int argc, char *argv[], char *file,
int nonetwork, int opts)
{
int ev = 0;
while (argc--)
if ((ev = append_addr(b, *argv++, nonetwork, opts)) == -1) {
if (errno)
warn("cannot decode %s", argv[-1]);
return (-1);
}
if (ev == 1) { /* expected further append_addr call */
warnx("failed to decode %s", argv[-1]);
return (-1);
}
if (pfr_buf_load(b, file, nonetwork, opts)) {
warn("cannot load %s", file);
return (-1);
}
return (0);
}
void
print_addrx(struct pfr_addr *ad, struct pfr_addr *rad, int dns)
{
char ch, buf[256] = "{error}";
char fb[] = { ' ', 'M', 'A', 'D', 'C', 'Z', 'X', ' ', 'Y', ' ' };
unsigned int fback, hostnet;
fback = (rad != NULL) ? rad->pfra_fback : ad->pfra_fback;
ch = (fback < sizeof(fb)/sizeof(*fb)) ? fb[fback] : '?';
hostnet = (ad->pfra_af == AF_INET6) ? 128 : 32;
inet_ntop(ad->pfra_af, &ad->pfra_u, buf, sizeof(buf));
printf("%c %c%s", ch, (ad->pfra_not?'!':' '), buf);
if (ad->pfra_net < hostnet)
printf("/%d", ad->pfra_net);
if (rad != NULL && fback != PFR_FB_NONE) {
if (strlcpy(buf, "{error}", sizeof(buf)) >= sizeof(buf))
errx(1, "print_addrx: strlcpy");
inet_ntop(rad->pfra_af, &rad->pfra_u, buf, sizeof(buf));
printf("\t%c%s", (rad->pfra_not?'!':' '), buf);
if (rad->pfra_net < hostnet)
printf("/%d", rad->pfra_net);
}
if (rad != NULL && fback == PFR_FB_NONE)
printf("\t nomatch");
if (dns && ad->pfra_net == hostnet) {
char host[NI_MAXHOST];
struct sockaddr_storage ss;
socklen_t slen; /* PFLINUX: sa_len replacement */
strlcpy(host, "?", sizeof(host));
bzero(&ss, sizeof(ss));
ss.ss_family = ad->pfra_af;
if (ss.ss_family == AF_INET) {
struct sockaddr_in *sin = (struct sockaddr_in *)&ss;
/* PFLINUX: no sa_len on Linux; getnameinfo(3)
* takes an explicit length instead. */
sin->sin_addr = ad->pfra_ip4addr;
slen = sizeof(*sin);
} else {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss;
sin6->sin6_addr = ad->pfra_ip6addr;
slen = sizeof(*sin6);
}
if (getnameinfo((struct sockaddr *)&ss, slen, host,
sizeof(host), NULL, 0, NI_NAMEREQD) == 0)
printf("\t(%s)", host);
}
if (ad->pfra_ifname[0] != '\0')
printf("@%s", ad->pfra_ifname);
printf("\n");
}
void
print_astats(struct pfr_astats *as, int dns)
{
time_t time = as->pfras_tzero;
int dir, op;
char *ct;
ct = ctime(&time);
print_addrx(&as->pfras_a, NULL, dns);
if (ct)
printf("\tCleared: %s", ctime(&time));
else
printf("\tCleared: %lld\n", time);
if (as->pfras_a.pfra_states)
printf("\tActive States: %d\n", as->pfras_a.pfra_states);
if (as->pfras_a.pfra_type == PFRKE_COST)
printf("\tWeight: %d\n", as->pfras_a.pfra_weight);
if (as->pfras_a.pfra_ifname[0])
printf("\tInterface: %s\n", as->pfras_a.pfra_ifname);
if (as->pfras_a.pfra_fback == PFR_FB_NOCOUNT)
return;
for (dir = 0; dir < PFR_DIR_MAX; dir++)
for (op = 0; op < PFR_OP_ADDR_MAX; op++)
printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
stats_text[dir][op],
(unsigned long long)as->pfras_packets[dir][op],
(unsigned long long)as->pfras_bytes[dir][op]);
}
int
pfctl_define_table(char *name, int flags, int addrs, const char *anchor,
struct pfr_buffer *ab, u_int32_t ticket, struct pfr_uktable *ukt)
{
struct pfr_table tbl_buf;
struct pfr_table *tbl;
if (ukt == NULL) {
bzero(&tbl_buf, sizeof(tbl_buf));
tbl = &tbl_buf;
} else {
if (ab->pfrb_size != 0) {
/*
* copy IP addresses which come with table from
* temporal buffer to buffer attached to table.
*/
ukt->pfrukt_addrs = *ab;
ab->pfrb_size = 0;
ab->pfrb_msize = 0;
ab->pfrb_caddr = NULL;
} else
memset(&ukt->pfrukt_addrs, 0,
sizeof(struct pfr_buffer));
tbl = &ukt->pfrukt_t;
}
if (strlcpy(tbl->pfrt_name, name, sizeof(tbl->pfrt_name)) >=
sizeof(tbl->pfrt_name) || strlcpy(tbl->pfrt_anchor, anchor,
sizeof(tbl->pfrt_anchor)) >= sizeof(tbl->pfrt_anchor))
errx(1, "%s: strlcpy", __func__);
tbl->pfrt_flags = flags;
DBGPRINT("%s %s@%s [%x]\n", __func__, tbl->pfrt_name,
tbl->pfrt_anchor, tbl->pfrt_flags);
/*
* non-root anchors processed by parse.y are loaded to kernel later.
* Here we load tables, which are either created for root anchor
* or by 'pfctl -t ... -T ...' command.
*/
if (ukt != NULL)
return (0);
return pfr_ina_define(tbl, ab->pfrb_caddr, ab->pfrb_size, NULL,
NULL, ticket, addrs ? PFR_FLAG_ADDRSTOO : 0);
}
void
warn_duplicate_tables(const char *tablename, const char *anchorname)
{
struct pfr_buffer b;
struct pfr_table *t;
bzero(&b, sizeof(b));
b.pfrb_type = PFRB_TABLES;
for (;;) {
pfr_buf_grow(&b, b.pfrb_size);
b.pfrb_size = b.pfrb_msize;
if (pfr_get_tables(NULL, b.pfrb_caddr,
&b.pfrb_size, PFR_FLAG_ALLRSETS))
err(1, "pfr_get_tables");
if (b.pfrb_size <= b.pfrb_msize)
break;
}
PFRB_FOREACH(t, &b) {
if (!(t->pfrt_flags & PFR_TFLAG_ACTIVE))
continue;
if (!strcmp(anchorname, t->pfrt_anchor))
continue;
if (!strcmp(tablename, t->pfrt_name))
warnx("warning: table <%s> already defined"
" in anchor \"%s\"", tablename,
t->pfrt_anchor[0] ? t->pfrt_anchor : "/");
}
pfr_buf_clear(&b);
}
void
xprintf(int opts, const char *fmt, ...)
{
va_list args;
if (opts & PF_OPT_QUIET)
return;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (opts & PF_OPT_DUMMYACTION)
fprintf(stderr, " (dummy).\n");
else if (opts & PF_OPT_NOACTION)
fprintf(stderr, " (syntax only).\n");
else
fprintf(stderr, ".\n");
}
/* interface stuff */
void
pfctl_show_ifaces(const char *filter, int opts)
{
struct pfr_buffer b;
struct pfi_kif *p;
bzero(&b, sizeof(b));
b.pfrb_type = PFRB_IFACES;
for (;;) {
pfr_buf_grow(&b, 0);
b.pfrb_size = b.pfrb_msize;
if (pfi_get_ifaces(filter, b.pfrb_caddr, &b.pfrb_size))
errx(1, "%s", pf_strerror(errno));
if (b.pfrb_size < b.pfrb_msize)
break;
}
if (opts & PF_OPT_SHOWALL)
pfctl_print_title("INTERFACES:");
PFRB_FOREACH(p, &b)
print_iface(p, opts);
}
void
print_iface(struct pfi_kif *p, int opts)
{
time_t tzero = p->pfik_tzero;
int i, af, dir, act;
char *ct;
printf("%s", p->pfik_name);
if (opts & PF_OPT_VERBOSE) {
if (p->pfik_flags & PFI_IFLAG_SKIP)
printf(" (skip)");
}
printf("\n");
if (!(opts & PF_OPT_VERBOSE2))
return;
ct = ctime(&tzero);
if (ct)
printf("\tCleared: %s", ct);
else
printf("\tCleared: %lld\n", tzero);
printf("\tReferences: [ States: %-18d Rules: %-18d ]\n",
p->pfik_states, p->pfik_rules);
for (i = 0; i < 8; i++) {
af = (i>>2) & 1;
dir = (i>>1) &1;
act = i & 1;
printf("\t%-12s [ Packets: %-18llu Bytes: %-18llu ]\n",
istats_text[af][dir][act],
(unsigned long long)p->pfik_packets[af][dir][act],
(unsigned long long)p->pfik_bytes[af][dir][act]);
}
}
+32
View File
@@ -0,0 +1,32 @@
# PFLINUX offline parse test 1 — macros, tables, anchors, filter rules.
# Only lo is referenced so the config parses on any Linux host.
xif = "lo"
int_net = "127.0.0.0/8"
table <brutes> persist
table <blocked> const { 10.0.0.0/8, 127.0.0.1 }
table <web> const { $int_net }
set skip on lo
set block-policy return
set loginterface $xif
set ruleset-optimization basic
set state-policy if-bound
set timeout { tcp.established 3600, tcp.closed 90 }
set limit states 100000
set limit src-nodes 3000
anchor "in/*"
anchor "testsub" {
block drop in quick on $xif from <blocked> to any label "blocked-in"
pass in quick on $xif proto tcp from any to any port 22 keep state
}
load anchor "in/loaded" from "tests/anchor_loaded.conf"
pass in quick on $xif inet proto tcp from any to any port { 80 443 8080 } \
tag WEB keep state (max 1000)
pass in quick on $xif inet6 proto tcp from fe80::/10 to any port 443
pass out quick on $xif proto udp from any to any port 53
block drop in log quick on $xif from <brutes> to any
+23
View File
@@ -0,0 +1,23 @@
# PFLINUX offline parse test 2 — translations, redirections, diversion.
xif = "lo"
table <ext> const { 127.0.0.1 }
set reassemble yes no-df
set syncookies adaptive (start 25%, end 12%)
pass in quick on $xif inet proto tcp from any to any port 8080 \
rdr-to 127.0.0.1 port 80 sticky-address
pass out quick on $xif inet proto tcp from 127.0.0.0/8 to any \
nat-to <ext> static-port
pass in quick on $xif inet6 proto tcp from any to any port 8080 \
rdr-to ::1
pass in quick on $xif inet proto tcp from any to any port 1723 \
route-to 127.0.0.1
pass in quick on $xif proto tcp from any to any port 21 \
divert-to 127.0.0.1 port 7000
pass in quick on $xif proto tcp from any to any port 110 divert-packet port 2000
pass quick on $xif inet proto tcp from 127.0.0.5 to any binat-to 127.0.0.9
pass in quick on $xif inet proto tcp all af-to inet6 from ::1
match out quick on $xif inet proto tcp all set (delay 100)
block drop in quick on $xif inet from urpf-failed to any
+18
View File
@@ -0,0 +1,18 @@
# PFLINUX offline parse test 3 — HFSC/fq_codel queues, scrub, limits, delay.
xif = "lo"
queue rootq on $xif bandwidth 1G, max 900M, min 10M
queue rtq on $xif bandwidth 100M burst 200M for 50ms parent rootq qlimit 500
queue bulkq on $xif bandwidth 50M, min 5M parent rootq default
queue fqcq on $xif bandwidth 20M flows 1024 quantum 1514 parent rootq
set timeout { frag 20, interval 10 }
set limit frags 10000
match in on $xif proto tcp all scrub (random-id)
match in on $xif inet proto tcp all scrub (max-mss 1440 min-ttl 10)
pass in quick on $xif proto tcp from any to any port 80 queue rtq
pass out quick on $xif proto tcp from any to any queue bulkq
pass in quick on $xif proto udp from any to any queue fqcq
block drop in quick on $xif all
+4
View File
@@ -0,0 +1,4 @@
# PFLINUX offline parse test 1 — anchor body loaded from a file.
block drop in quick on lo proto tcp from any to any port 139
pass in quick on lo proto tcp from 127.0.0.0/8 to any port 389 tag LDAP
+5
View File
@@ -0,0 +1,5 @@
# PFLINUX offline parse test — intentionally BROKEN config.
# `port` with no port spec is a syntax error; pfctl -n must exit non-zero.
xif = "lo"
pass in quick on $xif all port