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