M1: compat layer + pf.ko skeleton builds against Linux 7.3.4

- pfk.h: BSD types/ioctl encoding (OpenBSD bit layout), locks (NET_LOCK=rwsem),
  pools, timeouts, mbuf shim over sk_buff, ifnet registry, toeplitz/siphash
- pfk_mod/pfk_hooks: misc /dev/pf (0600), IPv4 netfilter hook skeleton,
  DIOCGETSTATUS answered (M1 skeleton; engine ioctls land with pf_ioctl.c)
- vendorize/stage-port scripts: deterministic upstream staging with seds
  (struct renames, IFF_*->PFIFF_*, sa_len drops) logged to docs/porting-notes
- fixes: modules_prepare+Module.symvers for out-of-tree builds
This commit is contained in:
lgcosta committed 2026-11-11 12:09:34 -03:00
1 parent ba81284bd3
commit da2e9b679a
87 files changed
+47256 -3

No files matched your search

File diff suppressed because it is too large. Load diff
+1
View File
@@ -0,0 +1 @@
savedcmd_Module.symvers := /home/lgcosta/lab/linux_kernel/linux-7.3.4/scripts/mod/modpost -M -o Module.symvers -n -T modules.order -i /home/lgcosta/lab/linux_kernel/linux-7.3.4/Module.symvers -e
+1
View File
@@ -0,0 +1 @@
savedcmd_modules.order := { echo pf.o; :; } > modules.order
+1
View File
@@ -0,0 +1 @@
savedcmd_pf.ko := ld -r -m elf_x86_64 --fatal-warnings -z noexecstack --no-warn-rwx-segments --build-id=sha1 --force-group-allocation -T /home/lgcosta/lab/linux_kernel/linux-7.3.4/scripts/module.lds -o pf.ko pf.o pf.mod.o .module-common.o
+1
View File
@@ -0,0 +1 @@
savedcmd_pf.mod := printf '%s\n' pfsrc/pfk_mod.o pfsrc/pfk_hooks.o compat/pfk_mbuf.o compat/pfk_kern.o compat/pfk_if.o compat/pfk_xmit.o compat/siphash.o compat/toeplitz.o | awk '!x[$$0]++ { print("./"$$0) }' > pf.mod
+1117
View File
File diff suppressed because it is too large. Load diff
+3
View File
@@ -0,0 +1,3 @@
savedcmd_pf.o := ld -m elf_x86_64 --fatal-warnings -z noexecstack --no-warn-rwx-segments -r -o pf.o @pf.mod ; /home/lgcosta/lab/linux_kernel/linux-7.3.4/tools/objtool/objtool --hacks=jump_label --hacks=noinstr --hacks=skylake --ibt --prefix=16 --orc --retpoline --rethunk --static-call --uaccess --link --module pf.o
pf.o: $(wildcard /home/lgcosta/lab/linux_kernel/linux-7.3.4/tools/objtool/objtool)
+17
View File
@@ -0,0 +1,17 @@
# PF-on-Linux kernel module build (out-of-tree)
# Build: make -C /path/to/linux-7.3.4 M=$PWD (or scripts/build-module.sh)
ccflags-y := -I$(src)/include -I$(src)/include/compat -DPFLINUX=1 \
-Wno-declaration-after-statement
# M1 skeleton set — staged engine files (pf.c, pf_ioctl.c, ...) are added
# per plan tasks T7b/T10/T11/T12; keep list explicit (no wildcards).
pf-objs := pfsrc/pfk_mod.o pfsrc/pfk_hooks.o \
compat/pfk_mbuf.o compat/pfk_kern.o compat/pfk_if.o compat/pfk_xmit.o \
compat/siphash.o compat/toeplitz.o
# staged BSD sources (uncomment as ported):
# pf-objs += compat/sha2.o compat/sha1.o compat/siphash.o compat/toeplitz.o
# pf-objs += compat/radix.o compat/rtable.o
# pf-objs += pfsrc/pf.o pfsrc/pf_ioctl.o pfsrc/pf_norm.o pfsrc/pf_if.o pfsrc/pf_lb.o
# pf-objs += pfsrc/pf_ruleset.o pfsrc/pf_table.o pfsrc/pf_osfp.o pfsrc/pf_syncookies.o
obj-m := pf.o
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
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
+387
View File
@@ -0,0 +1,387 @@
/*
* PF-on-Linux ifnet/address registry (ISC 2026).
* Maintains BSD-flavored struct pfk_ifnet views over net_device, refreshed
* address snapshots, egress tracking, and name/index lookups. All ops under
* NET_LOCK (read for lookups, write for list mutation) except notifier which
* uses its own spinlock + work.
*/
#include <pfk.h>
static DEFINE_IDR(pfk_ifnet_idr);
static DEFINE_SPINLOCK(pfk_ifnet_lock);
static struct pfk_ifnet *pfk_egress;
static void pfk_ifnet_release(struct rcu_head *rhp)
{
struct pfk_ifnet *ifp = container_of(rhp, struct pfk_ifnet, rcu);
pfk_if_free(ifp);
}
static struct pfk_ifnet *
pfk_ifnet_new(struct net_device *dev)
{
struct pfk_ifnet *ifp = kzalloc(sizeof(*ifp), GFP_KERNEL);
if (!ifp)
return NULL;
strscpy(ifp->if_xname, dev->name, sizeof(ifp->if_xname));
ifp->if_index = dev->ifindex;
ifp->dev = dev;
ifp->alive = 1;
INIT_LIST_HEAD(&ifp->link);
ifp->if_type = IFT_OTHER;
switch (dev->type) {
case ARPHRD_ETHER: ifp->if_type = IFT_ETHER; break;
case ARPHRD_LOOPBACK: ifp->if_type = IFT_LOOP; break;
case ARPHRD_PPP: ifp->if_type = IFT_PPPOINT; break;
case ARPHRD_SIT: case ARPHRD_TUNNEL: case ARPHRD_IP6GRE:
ifp->if_type = IFT_GIF; break;
}
return ifp;
}
static int
pfk_dev_flags_bsd(struct net_device *dev)
{
int f = 0;
if (dev->flags & IFF_UP) f |= PFIFF_UP;
if (dev->flags & IFF_BROADCAST) f |= PFIFF_BROADCAST;
if (dev->flags & IFF_DEBUG) f |= PFIFF_DEBUG;
if (dev->flags & IFF_LOOPBACK) f |= PFIFF_LOOPBACK;
if (dev->flags & IFF_POINTOPOINT) f |= PFIFF_POINTOPOINT;
if (netif_running(dev)) f |= PFIFF_RUNNING;
if (dev->flags & IFF_PROMISC) f |= PFIFF_PROMISC;
if (dev->flags & IFF_ALLMULTI) f |= PFIFF_ALLMULTI;
if (dev->flags & IFF_MULTICAST) f |= PFIFF_MULTICAST;
if (!netif_running(dev)) f |= PFIFF_RESOLVE;
return f;
}
void
pfk_if_refresh(struct pfk_ifnet *ifp)
{
ifp->if_mtu = ifp->dev->mtu;
ifp->if_flags = pfk_dev_flags_bsd(ifp->dev);
/* address snapshot: rebuild list from Linux in_ifaddr/inet6_ifaddr */
pfk_if_addrlist_refresh(ifp);
}
struct pfk_ifnet *
pfk_ifp_byindex(int idx)
{
struct pfk_ifnet *ifp;
if (idx <= 0)
return NULL;
ifp = idr_find(&pfk_ifnet_idr, idx);
return ifp && ifp->alive ? ifp : NULL;
}
struct pfk_ifnet *
pfk_ifp_bydev(struct net_device *dev)
{
return dev ? pfk_ifp_byindex(dev->ifindex) : NULL;
}
struct pfk_ifnet *
pfk_ifp_byname(const char *name)
{
struct net_device *dev;
struct pfk_ifnet *ifp;
dev = dev_get_by_name(&init_net, name);
if (!dev)
return NULL;
ifp = pfk_ifp_bydev(dev);
dev_put(dev);
return ifp;
}
void
pfk_if_stats(struct pfk_ifnet *ifp, struct pfk_if_data *out)
{
memset(out, 0, sizeof(*out));
out->ifi_ibytes = ifp->dev->stats.rx_bytes;
out->ifi_obytes = ifp->dev->stats.tx_bytes;
}
/* -------- address snapshots -------- */
static struct pfk_ifaddr *
pfkifa_new(struct pfk_ifnet *ifp, int family)
{
struct pfk_ifaddr *ifa = kzalloc(sizeof(*ifa), GFP_KERNEL);
if (!ifa)
return NULL;
ifa->ifa_ifp = ifp;
ifa->dev = ifp->dev;
ifa->family = family;
ifa->ifa_refcnt = 1;
ifa->ifa_addr = (struct sockaddr *)&ifa->ifa_addr_storage;
return ifa;
}
int
pfk_if_addrlist_refresh(struct pfk_ifnet *ifp)
{
struct pfk_ifaddr *ifa, *next;
struct net_device *dev = ifp->dev;
/* free old */
for (ifa = ifp->if_addrlist; ifa; ifa = next) {
next = ifa->ifa_next;
kfree(ifa);
}
ifp->if_addrlist = NULL;
rcu_read_lock();
/* IPv4 */
{
struct in_device *idev = __in_dev_get_rcu(dev);
struct in_ifaddr *ifa4;
if (idev)
for (ifa4 = rcu_dereference(idev->ifa_list); ifa4;
ifa4 = rcu_dereference(ifa4->ifa_next)) {
ifa = pfkifa_new(ifp, AF_INET);
if (!ifa)
continue;
ifa->addr4 = ifa4->ifa_address;
ifa->ifa_addr->sa_family = AF_INET;
{
struct sockaddr_in *sin =
sintosa(ifa->ifa_addr);
sin->sin_addr.s_addr = ifa4->ifa_address;
sin->sin_family = AF_INET;
}
ifa->ifa_next = ifp->if_addrlist;
ifp->if_addrlist = ifa;
}
}
/* IPv6 */
#if IS_ENABLED(CONFIG_IPV6)
{
struct inet6_dev *idev6 = __in6_dev_get(dev);
struct inet6_ifaddr *ifa6;
if (idev6)
list_for_each_entry(ifa6, &idev6->addr_list, if_list) {
ifa = pfkifa_new(ifp, AF_INET6);
if (!ifa)
continue;
ifa->addr6 = ifa6->addr;
ifa->scope = ifa6->scope;
ifa->ifa_addr->sa_family = AF_INET6;
{
struct sockaddr_in6 *sin6 =
sin6tosa(ifa->ifa_addr);
sin6->sin6_family = AF_INET6;
sin6->sin6_addr = ifa6->addr;
}
ifa->ifa_next = ifp->if_addrlist;
ifp->if_addrlist = ifa;
}
}
#endif
rcu_read_unlock();
return 0;
}
struct pfk_ifaddr *
pfk_ifwithaddr(const struct sockaddr *sa)
{
struct pfk_ifnet *ifp;
struct pfk_ifaddr *ifa;
for (ifp = pfk_if_first(); ifp; ifp = pfk_if_next(ifp)) {
for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
if (ifa->family != sa->sa_family)
continue;
if (sa->sa_family == AF_INET &&
ifa->addr4 == sintosa(sa)->sin_addr.s_addr)
return ifa;
if (sa->sa_family == AF_INET6 &&
!memcmp(&ifa->addr6, &sin6tosa(sa)->sin6_addr,
sizeof(struct in6_addr)))
return ifa;
}
}
return NULL;
}
void
pfk_if_free(struct pfk_ifnet *ifp)
{
struct pfk_ifaddr *ifa, *next;
for (ifa = ifp->if_addrlist; ifa; ifa = next) {
next = ifa->ifa_next;
kfree(ifa);
}
kfree(ifp);
}
/* -------- netdev events -------- */
static int
pfk_netdev_event(struct notifier_block *nb, unsigned long ev, void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct pfk_ifnet *ifp;
unsigned long flags;
switch (ev) {
case NETDEV_REGISTER:
ifp = pfk_ifnet_new(dev);
if (!ifp)
return NOTIFY_DONE;
pfk_if_refresh(ifp);
spin_lock_irqsave(&pfk_ifnet_lock, flags);
if (idr_find(&pfk_ifnet_idr, dev->ifindex))
idr_remove(&pfk_ifnet_idr, dev->ifindex);
if (idr_alloc(&pfk_ifnet_idr, ifp, dev->ifindex,
dev->ifindex + 1, GFP_ATOMIC) < 0) {
spin_unlock_irqrestore(&pfk_ifnet_lock, flags);
kfree(ifp);
return NOTIFY_DONE;
}
spin_unlock_irqrestore(&pfk_ifnet_lock, flags);
break;
case NETDEV_UNREGISTER:
spin_lock_irqsave(&pfk_ifnet_lock, flags);
ifp = idr_find(&pfk_ifnet_idr, dev->ifindex);
if (ifp) {
ifp->alive = 0;
idr_remove(&pfk_ifnet_idr, dev->ifindex);
call_rcu(&ifp->rcu, pfk_ifnet_release);
}
spin_unlock_irqrestore(&pfk_ifnet_lock, flags);
break;
case NETDEV_CHANGE:
case NETDEV_CHANGENAME:
case NETDEV_CHANGEMTU:
ifp = pfk_ifp_bydev(dev);
if (ifp)
pfk_if_refresh(ifp);
break;
case NETDEV_DOWN:
case NETDEV_UP: {
ifp = pfk_ifp_bydev(dev);
if (ifp)
pfk_if_refresh(ifp);
pfk_egress_refresh();
break;
}
}
pfk_egress_refresh();
return NOTIFY_DONE;
}
static struct notifier_block pfk_netdev_nb = {
.notifier_call = pfk_netdev_event,
};
/* -------- egress -------- */
void
pfk_egress_refresh(void)
{
struct pfk_ifnet *ifp = NULL;
struct net_device *dev;
/* PFLINUX: default route lookup for 0.0.0.0 via FIB */
rcu_read_lock();
dev = ip_rt_get_default_dev(&init_net); /* helper, P1 export */
rcu_read_unlock();
ifp = pfk_ifp_bydev(dev);
if (ifp)
ifp->if_xflags |= 0x00000001; /* BSD IFFX_EGRESS value */
pfk_egress = ifp;
}
struct pfk_ifnet *
pfk_egress_if(void)
{
return pfk_egress;
}
/* -------- iteration -------- */
struct pfk_ifnet *
pfk_if_first(void)
{
int id = -1;
return pfk_if_next_from(&id);
}
/* callers keep id on stack; helper returns next or NULL */
struct pfk_ifnet *
pfk_if_next_from(int *idp)
{
struct pfk_ifnet *ifp;
unsigned long flags;
spin_lock_irqsave(&pfk_ifnet_lock, flags);
ifp = idr_get_next(&pfk_ifnet_idr, idp);
spin_unlock_irqrestore(&pfk_ifnet_lock, flags);
if (ifp)
(*idp)++;
return ifp && ifp->alive ? ifp : (ifp ? pfk_if_next_from(idp) : NULL);
}
struct pfk_ifnet *
pfk_if_next(struct pfk_ifnet *ifp)
{
int id = ifp ? ifp->if_index : -1;
return pfk_if_next_from(&id);
}
int
pfk_if_init(void)
{
struct net_device *dev;
int err;
err = register_netdevice_notifier(&pfk_netdev_nb);
if (err)
return err;
/* populate existing devices */
rtnl_lock();
for_each_netdev(&init_net, dev) {
struct pfk_ifnet *ifp = pfk_ifnet_new(dev);
unsigned long flags;
if (!ifp)
continue;
spin_lock_irqsave(&pfk_ifnet_lock, flags);
if (!idr_find(&pfk_ifnet_idr, dev->ifindex))
idr_alloc(&pfk_ifnet_idr, ifp, dev->ifindex,
dev->ifindex + 1, GFP_KERNEL);
else
ifp = NULL; /* already tracked by notifier */
spin_unlock_irqrestore(&pfk_ifnet_lock, flags);
if (ifp)
pfk_if_refresh(ifp);
}
rtnl_unlock();
pfk_egress_refresh();
return 0;
}
void
pfk_if_exit(void)
{
struct pfk_ifnet *ifp;
int id = -1;
unsigned long flags;
unregister_netdevice_notifier(&pfk_netdev_nb);
spin_lock_irqsave(&pfk_ifnet_lock, flags);
while ((ifp = idr_get_next(&pfk_ifnet_idr, &id)) != NULL) {
ifp->alive = 0;
idr_remove(&pfk_ifnet_idr, id);
kfree(ifp);
id = -1;
}
spin_unlock_irqrestore(&pfk_ifnet_lock, flags);
}
+332
View File
@@ -0,0 +1,332 @@
/*
* PF-on-Linux kernel API compatibility (log/lock/malloc/pool/timeout/tsleep)
* ISC (c) 2026 PF-on-Linux contributors
*/
#include <pfk.h>
/* ---------------- logging ---------------- */
void
pfk_log(int level, const char *fmt, ...)
{
char buf[512];
va_list ap;
if (level < 0 || level > LOG_DEBUG)
level = LOG_INFO;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
switch (level) {
case LOG_EMERG: pr_emerg("%s\n", buf); break;
case LOG_ALERT: pr_alert("%s\n", buf); break;
case LOG_CRIT: pr_crit("%s\n", buf); break;
case LOG_ERR: pr_err("%s\n", buf); break;
case LOG_WARNING: pr_warn("%s\n", buf); break;
case LOG_NOTICE: pr_notice("%s\n", buf); break;
default:
pr_info_ratelimited("%s\n", buf); break;
}
}
int
pfk_printk_compat(const char *fmt, ...)
{
char buf[512];
va_list ap;
int n;
va_start(ap, fmt);
n = vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
if (n > 0)
pr_info("%s", buf);
return n;
}
/* ---------------- NET_LOCK ---------------- */
static DECLARE_RWSEM(pfk_net_sem);
static atomic_t pfk_net_readers = ATOMIC_INIT(0);
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_read_enter(void) { down_read(&pfk_net_sem);
atomic_inc(&pfk_net_readers); }
void pfk_net_read_release(void){ atomic_dec(&pfk_net_readers);
up_read(&pfk_net_sem); }
void
rw_init(struct rwlock *l, const char *name)
{
init_rwsem(&l->sem);
l->name = name;
l->is_writer = 0;
atomic_set(&l->readers, 0);
}
void
rw_enter(struct rwlock *l, int mode)
{
if (mode == RW_WRITER) {
down_write(&l->sem);
l->is_writer = 1;
} else {
down_read(&l->sem);
atomic_inc(&l->readers);
}
}
void
rw_exit(struct rwlock *l)
{
if (l->is_writer) {
l->is_writer = 0;
up_write(&l->sem);
} else {
atomic_dec(&l->readers);
up_read(&l->sem);
}
}
int
rw_status(struct rwlock *l)
{
return l->is_writer ? RW_WRITER :
atomic_read(&l->readers) ? RW_READER : RW_NAPUTS;
}
/* ---------------- malloc ---------------- */
void *
pfk_malloc(size_t sz, int flags)
{
gfp_t g = (flags & M_WAITOK) ? GFP_KERNEL : GFP_ATOMIC;
if (flags & M_ZERO)
return kzalloc(sz, g);
return kmalloc(sz, g);
}
void
pfk_free(void *p)
{
kfree(p);
}
/* ---------------- pools ---------------- */
static atomic64_t pfk_pool_id = ATOMIC64_INIT(0);
int
pool_init(struct pool *pl, size_t size, int align, int ipl, const char *use,
void *p, int flags, void (*ih)(void *, void *), void *arg,
struct srcptr_static *sp)
{
memset(pl, 0, sizeof(*pl));
pl->p_size = size;
return 0; /* slab lazily created on first pool_get */
}
static int
pool_get_cache(struct pool *pl)
{
char nm[64];
struct kmem_cache *c;
snprintf(nm, sizeof(nm), "pfk_pool_%llu",
(unsigned long long)atomic64_inc_return(&pfk_pool_id));
c = kmem_cache_create_usercopy(nm, pl->p_size, 0, SLAB_HWCACHE_ALIGN,
0, pl->p_size, NULL);
pl->slab = c;
return c ? 0 : -ENOMEM;
}
void
pool_destroy(struct pool *pl)
{
if (pl->slab) {
kmem_cache_destroy(pl->slab);
pl->slab = NULL;
}
}
void *
pool_get(struct pool *pl, int flags)
{
void *v;
if (!pl->slab && pool_get_cache(pl))
return NULL;
v = kmem_cache_alloc(pl->slab,
(flags & PR_WAITOK) ? GFP_KERNEL :
(flags & PR_NOWAIT) ? GFP_ATOMIC :
__GFP_RETRY_MAYFAIL | GFP_KERNEL);
if (v) {
pl->p_used++;
if (flags & PR_ZERO)
memset(v, 0, pl->p_size);
}
return v;
}
void
pool_put(struct pool *pl, void *v)
{
if (pl->slab && v) {
kmem_cache_free(pl->slab, v);
if (pl->p_used)
pl->p_used--;
}
}
void
pool_sethardlimit(struct pool *pl, unsigned long lim, const char *msg, int lvl)
{
pl->p_hardlimit = lim;
}
/* ---------------- timeout ---------------- */
static void
pfk_timeout_work(struct work_struct *w)
{
struct timeout *to = container_of(w, struct timeout, work);
to->running = 1;
to->pending = 0;
if (to->fn)
to->fn(to->arg);
to->running = 0;
if (to->period_ms >= 0)
mod_timer(&to->timer, jiffies + msecs_to_jiffies(to->period_ms));
}
static void
pfk_timeout_fire(struct timer_list *tl)
{
struct timeout *to = container_of(tl, struct timeout, timer);
schedule_work(&to->work);
}
void
timeout_set(struct timeout *to, timeout_fn *fn, void *arg)
{
if (!to->fn) { /* one-time setup */
timer_setup(&to->timer, pfk_timeout_fire, 0);
INIT_WORK(&to->work, pfk_timeout_work);
to->period_ms = -1;
}
to->fn = fn;
to->arg = arg;
}
void
timeout_set_micro(struct timeout *to, timeout_fn *fn, void *arg)
{
timeout_set(to, fn, arg);
}
int
timeout_add(struct timeout *to, long ms)
{
if (ms < 0)
ms = 0;
to->pending = 1;
to->deadline_jif = jiffies + msecs_to_jiffies(ms);
return mod_timer(&to->timer, to->deadline_jif);
}
int
timeout_add_sec(struct timeout *to, int secs)
{
return timeout_add(to, (long)secs * 1000);
}
int
timeout_add_usec(struct timeout *to, int us)
{
return timeout_add(to, us > 1000 ? us / 1000 : (us ? 1 : 0));
}
int
timeout_pending(struct timeout *to)
{
return to->pending;
}
int
timeout_del(struct timeout *to)
{
to->pending = 0;
return timer_delete(&to->timer);
}
void
timeout_del_sync(struct timeout *to)
{
to->pending = 0;
timer_delete_sync(&to->timer);
cancel_work_sync(&to->work);
}
void
timeout_barrier(struct timeout *to, int flags)
{
timer_delete_sync(&to->timer);
cancel_work_sync(&to->work);
}
/* ---------------- tsleep/wakeup (control path only) ---------------- */
struct pfk_wait {
struct list_head link;
struct wait_queue_head wq;
int done;
};
static DEFINE_SPINLOCK(pfk_tsleep_lock);
/* PFLINUX: BSD queue.h shadows LIST_HEAD (2-arg) — use raw initializer */
static struct list_head pfk_tsleep_list = {
.next = &pfk_tsleep_list,
.prev = &pfk_tsleep_list,
};
int
tsleep(void *p, int pr, const char *w, int timo)
{
struct pfk_wait *pw;
long left;
pw = kmalloc(sizeof(*pw), GFP_KERNEL);
if (!pw)
return EINTR;
init_waitqueue_head(&pw->wq);
pw->done = 0;
spin_lock(&pfk_tsleep_lock);
list_add_tail(&pw->link, &pfk_tsleep_list);
pw->link.next = (void *)p; /* stash ident */
spin_unlock(&pfk_tsleep_lock);
left = wait_event_interruptible_timeout(pw->wq, pw->done,
timo ? msecs_to_jiffies(timo) : MAX_JIFFY_OFFSET);
spin_lock(&pfk_tsleep_lock);
list_del(&pw->link);
spin_unlock(&pfk_tsleep_lock);
kfree(pw);
if (!left)
return EWOULDBLOCK;
if (left == -ERESTARTSYS)
return EINTR;
return 0;
}
void
pfk_wakeup(void *p)
{
struct pfk_wait *pw;
spin_lock(&pfk_tsleep_lock);
list_for_each_entry(pw, &pfk_tsleep_list, link) {
if (pw->link.next == (void *)p) {
pw->done = 1;
wake_up_interruptible(&pw->wq);
}
}
spin_unlock(&pfk_tsleep_lock);
}
+462
View File
@@ -0,0 +1,462 @@
/*
* PF-on-Linux mbuf shim implementation (ISC, (c) 2026 PF-on-Linux contributors)
* See pfk_mbuf.h for the invariants. All functions run under pfk NET_LOCK
* (read side) — never in hardirq context.
*/
#include <pfk.h>
static struct kmem_cache *pfk_mbuf_cache;
struct mbuf *
m_get(int how, short type)
{
return m_gethdr(how, type); /* PF always uses pkthdr packets */
}
struct mbuf *
m_gethdr(int how, short type)
{
struct mbuf *m;
struct sk_buff *skb;
m = kmem_cache_zalloc(pfk_mbuf_cache,
(how & (M_WAITOK|M_CANFAIL)) == M_WAITOK ? GFP_KERNEL : GFP_ATOMIC);
if (!m)
return NULL;
skb = alloc_skb(256 + NET_SKB_PAD + NET_IP_ALIGN,
(how & (M_WAITOK|M_CANFAIL)) == M_WAITOK ? GFP_KERNEL : GFP_ATOMIC);
if (!skb) {
kmem_cache_free(pfk_mbuf_cache, m);
return NULL;
}
skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
m->skb = skb;
m->m_data = skb->data;
m->m_len = skb->len;
m->m_type = type;
m->m_flags = M_PKTHDR;
m->m_refcnt = 1;
m->m_maxsize = skb_tailroom(skb);
pfk_m_setpkthdr(m);
return m;
}
void
pfk_m_setpkthdr(struct mbuf *m)
{
m->m_flags |= M_PKTHDR;
m->m_pkthdr.len = m->m_len;
m->m_pkthdr.pf.tag = NULL;
m->m_pkthdr.tags = NULL;
}
struct mbuf *
m_alloc_skb(struct sk_buff *skb, int how)
{
struct mbuf *m;
m = kmem_cache_zalloc(pfk_mbuf_cache,
(how & (M_WAITOK|M_CANFAIL)) == M_WAITOK ? GFP_KERNEL : GFP_ATOMIC);
if (!m) {
kfree_skb(skb);
return NULL;
}
m->skb = skb;
m->m_data = skb->data;
m->m_len = skb->len;
m->m_type = MT_HEADER;
m->m_refcnt = 1;
pfk_m_setpkthdr(m);
return m;
}
/* reverse map: stash mbuf ptr in skb->cb (documented exclusivity in spec §4.1) */
struct pfk_skb_cb {
struct mbuf *m;
};
#define PFK_CB(skb) ((struct pfk_skb_cb *)((skb)->cb))
struct mbuf *
m_dtom(struct sk_buff *skb)
{
return PFK_CB(skb)->m;
}
struct mbuf *
m_devget(char *buf, int totlen, int off, struct pfk_ifnet *ifp,
void (*copyin_fc)(caddr_t, int))
{
struct mbuf *m;
if (off) {
buf += off;
totlen -= off;
}
if (totlen < 0)
return NULL;
m = m_gethdr(M_NOWAIT, MT_DATA);
if (!m)
return NULL;
if (skb_put(m->skb, totlen) == NULL) {
m_freem(m);
return NULL;
}
if (copyin_fc)
copyin_fc(mtod(m, caddr_t), totlen);
else
memcpy(m->skb->data, buf, totlen);
pfk_mbuf_refresh(m);
return m;
}
int
pfk_mbuf_linearize(struct mbuf *m)
{
if (skb_is_nonlinear(m->skb)) {
struct sk_buff *ns;
ns = skb_copy(m->skb, GFP_ATOMIC);
if (!ns)
return ENOBUFS;
consume_skb(m->skb);
m->skb = ns;
}
return 0;
}
void
pfk_mbuf_refresh(struct mbuf *m)
{
m->m_data = m->skb->data;
m->m_len = m->skb->len;
if (m->m_flags & M_PKTHDR)
m->m_pkthdr.len = m->m_len;
}
struct mbuf *
m_copym(struct mbuf *m, int off, int len, int wait)
{
struct mbuf *n;
struct sk_buff *ns;
if (len == 0)
len = m->m_len - off;
if (off > m->m_len || len > m->m_len - off)
return NULL;
ns = alloc_skb(len + NET_SKB_PAD + NET_IP_ALIGN,
wait == M_WAITOK ? GFP_KERNEL : GFP_ATOMIC);
if (!ns)
return NULL;
skb_reserve(ns, NET_SKB_PAD + NET_IP_ALIGN);
if (skb_copy_bits(m->skb, off, skb_put(ns, len), len)) {
kfree_skb(ns);
return NULL;
}
n = m_alloc_skb(ns, wait);
if (!n)
return NULL;
if (m->m_flags & M_PKTHDR) {
n->m_pkthdr = m->m_pkthdr;
n->m_pkthdr.len = len;
n->m_pkthdr.tags = NULL;
if (m_tag_copy_chain(n, m, wait) != 0) {
m_freem(n);
return NULL;
}
}
return n;
}
struct mbuf *
m_dup(struct mbuf *m, int wait)
{
struct mbuf *n = m_copym(m, 0, m->m_len, wait);
if (n) {
struct sk_buff *ns = n->skb;
int need = 256 - skb_headroom(ns);
if (need > 0 &&
pskb_expand_head(ns, ALIGN(need, 64), 0,
wait == M_WAITOK ? GFP_KERNEL : GFP_ATOMIC)) {
m_freem(n);
return NULL;
}
pfk_mbuf_refresh(n);
}
return n;
}
void
m_adj(struct mbuf *m, int dlen)
{
int off = dlen;
if (off < 0) { /* trim tail */
skb_trim(m->skb, m->skb->len + off);
} else if (off > 0) {
pskb_pull(m->skb, off > m->skb->len ? m->skb->len : off);
}
pfk_mbuf_refresh(m);
}
void
m_trim(struct mbuf *m, int dlen)
{
skb_trim(m->skb, dlen);
pfk_mbuf_refresh(m);
}
int
m_cat(struct mbuf *to, struct mbuf *from)
{
int len = from->m_len;
if (skb_tailroom(to->skb) < len) {
if (pskb_expand_head(to->skb, 0, ALIGN(len, 64), GFP_ATOMIC))
return EINVAL;
pfk_mbuf_refresh(to);
}
memcpy(skb_put(to->skb, len), from->m_data, len);
pfk_mbuf_refresh(to);
m_freem(from);
return 0;
}
caddr_t
m_copyback(struct mbuf *m, int off, int len, const void *copied)
{
int grow = off + len - m->m_len;
caddr_t p;
if (off < 0 || len < 0)
return mtod(m, caddr_t) + off; /* BSD clamps; PF never does this */
if (grow > 0) {
if (skb_tailroom(m->skb) < grow) {
if (pskb_expand_head(m->skb, 0, ALIGN(grow, 64), GFP_ATOMIC))
return NULL;
pfk_mbuf_refresh(m);
}
memset(m->m_data + m->m_len, 0, grow);
skb_put(m->skb, grow);
pfk_mbuf_refresh(m);
}
p = m->m_data + off;
memcpy(p, copied, len);
return p;
}
void
m_copydata(const struct mbuf *m, int off, int len, caddr_t dest)
{
if (len <= 0)
return;
if (skb_copy_bits(m->skb, off, dest, len))
memcpy(dest, m->m_data + off,
len > m->m_len - off ? m->m_len - off : len);
}
caddr_t
m_getptr(struct mbuf *m, int loc, int *offp)
{
*offp = 0;
if (loc < 0 || loc >= m->m_len)
return NULL;
return m->m_data + loc;
}
int
m_clump(struct mbuf **m0p, int len, int *mp_off, int *mp_len, caddr_t *doff)
{
struct mbuf *m = *m0p;
if (!pskb_may_pull(m->skb, *mp_off + len))
return -1;
pfk_mbuf_refresh(m);
*mp_len = len;
*doff = m->m_data + *mp_off;
*m0p = m;
return 0;
}
struct mbuf *
m_free(struct mbuf *m)
{
struct mbuf *n = m->m_next;
m_tag_delete_chain(m, NULL);
if (m->skb) {
if (PFK_CB(m->skb)->m == m)
PFK_CB(m->skb)->m = NULL;
kfree_skb(m->skb);
}
kmem_cache_free(pfk_mbuf_cache, m);
return n;
}
void
m_freem(struct mbuf *m)
{
while (m)
m = m_free(m);
}
void
m_free_ext(struct mbuf *m)
{
/* PF never attaches external mbuf refs; no-op like upstream ext free */
}
/* ---------------- m_tag ---------------- */
struct m_tag *
m_tag_alloc(int type, int len, int wait, caddr_t *align_data)
{
struct m_tag *t;
t = kmalloc(sizeof(*t) + len + 4,
wait == M_WAITOK ? GFP_KERNEL : GFP_ATOMIC);
if (!t)
return NULL;
t->m_tag_next = NULL;
t->m_tag_type = type;
t->m_tag_len = len;
if (align_data)
*align_data = PFK_PTR_ALIGN(&t[1]);
return t;
}
void
m_tag_free(struct m_tag *t)
{
kfree(t);
}
void
m_tag_attach(struct mbuf *m, struct m_tag *t)
{
t->m_tag_next = NULL;
if (m->m_pkthdr.tags) {
struct m_tag *e = m->m_pkthdr.tags;
while (e->m_tag_next)
e = e->m_tag_next;
e->m_tag_next = t;
} else
m->m_pkthdr.tags = t;
}
void
m_tag_prepend(struct mbuf *m, struct m_tag *t)
{
t->m_tag_next = m->m_pkthdr.tags;
m->m_pkthdr.tags = t;
}
void
m_tag_prepend_packet(struct mbuf *m, struct m_tag *t)
{
if (m->m_flags & M_PKTHDR)
m_tag_prepend(m, t);
else
m_tag_free(t);
}
struct m_tag *
m_tag_find(struct mbuf *m, int type, struct m_tag *start)
{
struct m_tag *t;
if (start == NULL) {
if (!(m->m_flags & M_PKTHDR))
return NULL;
t = m->m_pkthdr.tags;
} else
t = start->m_tag_next;
for (; t; t = t->m_tag_next)
if (t->m_tag_type == type)
break;
return t;
}
void
m_tag_delete(struct mbuf *m, struct m_tag *t)
{
struct m_tag **pp = &m->m_pkthdr.tags, *e;
for (e = m->m_pkthdr.tags; e; e = e->m_tag_next) {
if (e == t) {
*pp = t->m_tag_next;
m_tag_free(t);
return;
}
pp = &e->m_tag_next;
}
}
struct m_tag *
m_tag_copy(struct m_tag *tag, int how)
{
struct m_tag *t;
caddr_t d;
t = m_tag_alloc(tag->m_tag_type, tag->m_tag_len, how, &d);
if (t)
memcpy(d, tag->m_tag_data, tag->m_tag_len);
return t;
}
int
m_tag_copy_chain(struct mbuf *to, struct mbuf *from, int how)
{
struct m_tag *t;
for (t = m_tag_first(from); t; t = m_tag_next(from, t)) {
struct m_tag *n = m_tag_copy(t, how);
if (!n)
return ENOBUFS;
m_tag_attach(to, n);
}
return 0;
}
void
m_tag_delete_chain(struct mbuf *m, struct m_tag *end)
{
struct m_tag *t, *n;
if (!(m->m_flags & M_PKTHDR))
return;
for (t = m->m_pkthdr.tags; t && t != end; t = n) {
n = t->m_tag_next;
m_tag_free(t);
}
m->m_pkthdr.tags = end;
}
struct m_tag *
m_tag_first(struct mbuf *m)
{
return (m->m_flags & M_PKTHDR) ? m->m_pkthdr.tags : NULL;
}
struct m_tag *
m_tag_next(struct mbuf *m, struct m_tag *t)
{
return t->m_tag_next;
}
int
pfk_mbuf_init(void)
{
pfk_mbuf_cache = KMEM_CACHE(mbuf, 0);
if (!pfk_mbuf_cache)
return -ENOMEM;
return 0;
}
void
pfk_mbuf_exit(void)
{
kmem_cache_destroy(pfk_mbuf_cache);
}
+208
View File
@@ -0,0 +1,208 @@
/*
* PF-on-Linux transmit + return-packet helpers (ISC 2026).
* if_output_tso/ml + if_enqueue mirror OpenBSD calls; route-to uses them
* with a gateway dst (see pf_route.c, T14). Return errors use Linux
* ipstack helpers (exported: icmp_send; P1 exports ip_route_output_key,
* ip6_route_output, tcp_v4_send_reset if needed).
*/
#include <pfk.h>
static struct dst_entry *
pfk_route4(__be32 daddr, __be32 saddr, int oif)
{
struct flowi4 fl4 = { };
struct rtable *rt;
fl4.daddr = daddr;
fl4.saddr = saddr;
if (oif)
fl4.flowi4_oif = oif;
rt = ip_route_output_key(&init_net, &fl4);
if (IS_ERR(rt))
return NULL;
return &rt->dst;
}
#if IS_ENABLED(CONFIG_IPV6)
static struct dst_entry *
pfk_route6(const struct in6_addr *daddr, const struct in6_addr *saddr, int oif)
{
struct flowi6 fl6 = { };
struct dst_entry *d;
fl6.daddr = *daddr;
if (saddr)
fl6.saddr = *saddr;
if (oif)
fl6.flowi6_oif = oif;
d = ip6_route_output(&init_net, NULL, &fl6);
if (!d || d->error) {
if (d)
dst_release(d);
return NULL;
}
return d;
}
#endif
/* xmit: BSD semantics — caller hands fully-formed IP packet + dst sock.
* rt arg is OpenBSD's cached route (opaque to us). Returns 0/errno. */
int
if_output_tso(struct pfk_ifnet *ifp, struct mbuf **mp, struct sockaddr *dst,
void *rt, int mtu)
{
struct mbuf *m = *mp;
struct sk_buff *skb = m_to_skb(m);
struct dst_entry *d = NULL;
int ret = 0;
if (!pskb_may_pull(skb, sizeof(struct pf_ip))) {
ret = EINVAL;
goto drop;
}
if (skb_ensure_writable(skb, 0)) {
ret = ENOBUFS;
goto drop;
}
switch (dst->sa_family) {
case AF_INET: {
struct pf_ip *ip = mtod(m, struct pf_ip *);
__be32 gw = 0;
/* if route was forced (route-to), dst holds gw sockaddr
* stored in sin_addr by pf_route; normal case dst==final */
gw = sintosa(dst)->sin_addr.s_addr;
d = pfk_route4(gw, ip->ip_src.s_addr, ifp->if_index);
if (!d) {
ret = EHOSTUNREACH;
goto drop;
}
skb_dst_set(skb, d);
break;
}
#if IS_ENABLED(CONFIG_IPV6)
case AF_INET6: {
struct pf_ip6_hdr *ip6 = mtod(m, struct pf_ip6_hdr *);
const struct in6_addr *gw = &sin6tosa(dst)->sin6_addr;
if (!d)
d = pfk_route6(gw,
(struct in6_addr *)&ip6->ip6_src,
ifp->if_index);
if (!d) {
ret = EHOSTUNREACH;
goto drop;
}
skb_dst_set(skb, d);
skb->protocol = htons(ETH_P_IPV6);
break;
}
#endif
default:
ret = EAFNOSUPPORT;
goto drop;
}
skb->dev = skb_dst(skb)->dev;
skb_reset_network_header(skb);
if (skb->protocol == 0)
skb->protocol = htons(ETH_P_IP);
/* push through network layer exactly like OpenBSD's if_output:
* dst->output = ip_output/ip6_output (headers already built) */
if (!netif_running(skb->dev)) {
ret = ENETDOWN;
goto drop_dst_nofree;
}
ret = skb_dst(skb)->output(&init_net, NULL, skb);
/* output consumed skb (and wrapper); avoid double free */
if (ret == 0) {
m->skb = NULL; /* ownership moved */
m_freem(m);
*mp = NULL;
}
return ret;
drop_dst_nofree:
ret = ENETDOWN;
drop:
if (m) {
m_freem(m);
*mp = NULL;
}
return ret;
}
int
if_output_ml(struct pfk_ifnet *ifp, struct mbuf_list *ml,
struct sockaddr *dst, void *rt)
{
struct mbuf *m;
int err = 0, n;
while ((m = ml->ml_first) != NULL) {
ml->ml_first = m->m_next;
m->m_next = NULL;
n = if_output_tso(ifp, &m, dst, rt, ifp->if_mtu);
if (n)
err = n;
}
ml->ml_len = 0;
return err;
}
int
if_enqueue(struct pfk_ifnet *ifp, struct mbuf *m)
{
/* PFLINUX M1-M4: no qdisc attached → direct xmit via same path;
* M5 replaces this with the pfq enqueue (spec §4.3). */
struct sockaddr sa = { .sa_family = AF_INET };
struct mbuf *mp = m;
if (m_to_skb(m)->protocol == htons(ETH_P_IPV6))
sa.sa_family = AF_INET6;
if_output_tso(ifp, &mp, &sa, NULL, ifp->if_mtu);
if (mp == NULL)
return 0;
m_freem(mp);
return ENOBUFS;
}
/* default-route egress device helper for pfk_egress_refresh() */
struct net_device *
ip_rt_get_default_dev(struct net *net)
{
struct dst_entry *d = pfk_route4(htonl(0x08080808), 0, 0);
struct net_device *dev = NULL;
if (d) {
dev = d->dev;
if (dev)
dev_hold(dev);
dst_release(d);
}
return dev;
}
/* ---------------- return errors ---------------- */
u_int16_t
pfk_in_cksum(const void *data, int len)
{
return csum_fold(csum_partial(data, len, 0));
}
void
pfk_send_icmp_error(int family, struct sk_buff *skb_in, int type, int code,
__be32 info)
{
if (family == AF_INET) {
icmp_send(skb_in, type, code, info);
return;
}
#if IS_ENABLED(CONFIG_IPV6)
pr_warn_once("pfk: ICMPv6 error path pending P1 export (type %d)\n", type);
#endif
}
/* NOTE: TCP RST generation is done by PF itself (pf.c builds the segment
* and calls if_output_tso), so no kernel tcp_v4_send_reset export is
* needed — matches OpenBSD's pf_send_rst() design. */
+157
View File
@@ -0,0 +1,157 @@
/*
* SipHash-2-4 implementation (PFLINUX) — reference algorithm by Jean-Philippe
* Aumasson & Daniel J. Bernstein, public domain reference semantics.
* ISC (c) 2026 PF-on-Linux contributors.
*/
#include <pfk.h>
#include "compat/crypto/siphash.h"
#define ROTL64(x, b) (u64)(((x) << (b)) | ((x) >> (64 - (b))))
#define SIPROUND \
do { \
v0 += v1; v1 = ROTL64(v1, 13); v1 ^= v0; v0 = ROTL64(v0, 32); \
v2 += v3; v3 = ROTL64(v3, 16); v3 ^= v2; \
v0 += v3; v3 = ROTL64(v3, 21); v3 ^= v0; \
v2 += v1; v1 = ROTL64(v1, 17); v1 ^= v2; v2 = ROTL64(v2, 32); \
} while (0)
#define U8TO64_LE(p) \
(((u64)((p)[0])) | ((u64)((p)[1]) << 8) | ((u64)((p)[2]) << 16) | \
((u64)((p)[3]) << 24) | ((u64)((p)[4]) << 32) | ((u64)((p)[5]) << 40) | \
((u64)((p)[6]) << 48) | ((u64)((p)[7]) << 56))
#define U64TO8_LE(p, v) \
do { \
(p)[0] = (u8)((v) & 0xff); (p)[1] = (u8)((v) >> 8); \
(p)[2] = (u8)((v) >> 16); (p)[3] = (u8)((v) >> 24); \
(p)[4] = (u8)((v) >> 32); (p)[5] = (u8)((v) >> 40); \
(p)[6] = (u8)((v) >> 48); (p)[7] = (u8)((v) >> 56); \
} while (0)
void
SipHash24_Init(SIPHASH_CTX *ctx, const SIPHASH_KEY *key)
{
ctx->v[0] = 0x736f6d6570736575ULL ^ key->v[0];
ctx->v[1] = 0x646f72616e646f6dULL ^ key->v[1];
ctx->v[2] = 0x6c7967656e657261ULL ^ key->v[0];
ctx->v[3] = 0x7465646279746573ULL ^ key->v[1];
ctx->total_len = 0;
ctx->block_len = 0;
memset(ctx->block, 0, sizeof(ctx->block));
}
void
SipHash24_Update(SIPHASH_CTX *ctx, const void *in, __u64 inlen)
{
const unsigned char *ni = in;
u64 total = ctx->total_len + inlen;
int left = ctx->block_len;
u64 i;
ctx->total_len = total;
if (left) {
unsigned int take = 8 - left;
if (take > inlen)
take = inlen;
memcpy(ctx->block + left, ni, take);
left += take;
ni += take;
inlen -= take;
if (left == 8) {
u64 m = U8TO64_LE(ctx->block);
u64 v0 = ctx->v[0], v1 = ctx->v[1];
u64 v2 = ctx->v[2], v3 = ctx->v[3];
v3 ^= m;
SIPROUND; SIPROUND;
v0 ^= m;
ctx->v[0] = v0; ctx->v[1] = v1;
ctx->v[2] = v2; ctx->v[3] = v3;
ctx->block_len = 0;
}
}
while (inlen >= 8) {
u64 m = U8TO64_LE(ni);
u64 v0 = ctx->v[0], v1 = ctx->v[1];
u64 v2 = ctx->v[2], v3 = ctx->v[3];
v3 ^= m;
SIPROUND; SIPROUND;
v0 ^= m;
ctx->v[0] = v0; ctx->v[1] = v1;
ctx->v[2] = v2; ctx->v[3] = v3;
ni += 8; inlen -= 8;
}
if (inlen) {
memcpy(ctx->block, ni, inlen);
ctx->block_len = inlen;
}
(void)i;
}
void
SipHash24_Final(SIPHASH_CTX *ctx, unsigned char *out)
{
u64 b = ((u64)ctx->total_len) << 56;
u64 v0 = ctx->v[0], v1 = ctx->v[1], v2 = ctx->v[2], v3 = ctx->v[3];
unsigned char t[8];
int i;
U64TO8_LE(t, b);
for (i = 0; i < ctx->block_len; i++)
t[i] = ctx->block[i];
v3 ^= b;
SIPROUND; SIPROUND;
v0 ^= b;
v2 ^= 0xff;
SIPROUND; SIPROUND; SIPROUND; SIPROUND;
U64TO8_LE(out, v0 ^ v1 ^ v2 ^ v3);
/* wipe */
ctx->v[0] = ctx->v[1] = ctx->v[2] = ctx->v[3] = 0;
}
void
SipHash24_Generate(const SIPHASH_KEY *key, const void *in, __u64 inlen,
unsigned char *out)
{
u64 k0 = key->v[0], k1 = key->v[1];
u64 v0, v1, v2, v3;
v0 = 0x736f6d6570736575ULL ^ k0;
v1 = 0x646f72616e646f6dULL ^ k1;
v2 = 0x6c7967656e657261ULL ^ k0;
v3 = 0x7465646279746573ULL ^ k1;
while (inlen >= 8) {
u64 m = U8TO64_LE((const unsigned char *)in);
v3 ^= m;
SIPROUND; SIPROUND;
v0 ^= m;
in = (const unsigned char *)in + 8;
inlen -= 8;
}
{
unsigned char t[8];
u64 b = ((u64)inlen) << 56;
int i;
memset(t, 0, sizeof(t));
memcpy(t, in, inlen);
for (i = 0; i < 8; i++)
t[i] ^= (unsigned char)(b >> (8 * i));
{
u64 m = U8TO64_LE(t);
v3 ^= m;
SIPROUND; SIPROUND;
v0 ^= m;
}
v2 ^= 0xff;
SIPROUND; SIPROUND; SIPROUND; SIPROUND;
U64TO8_LE(out, v0 ^ v1 ^ v2 ^ v3);
}
}
+168
View File
@@ -0,0 +1,168 @@
/*
* Software Toeplitz hash (RFC 3305) for PF flow ids — PFLINUX.
* The key window is SHA256(key) || key; the 16-bit result is the XOR of
* the 16-bit windows selected by the set bits of the input.
* ISC (c) 2026 PF-on-Linux contributors.
*/
#include <pfk.h>
#include <crypto/hash.h>
#include "compat/net/toeplitz.h"
struct stoeplitz_cache *stoeplitz_cache;
static int
toeplitz_keyhash(const u8 *key, u8 *out /*32*/)
{
struct crypto_shash *tfm;
char descbuf[sizeof(struct shash_desc) + 512];
struct shash_desc *desc = (struct shash_desc *)descbuf;
int err;
tfm = crypto_alloc_shash("sha256", 0, 0);
if (!tfm)
return -ENOMEM;
desc->tfm = tfm;
err = crypto_shash_digest(desc, key, TOEPLITZ_KEY_LEN, out);
crypto_free_shash(tfm);
return err;
}
/* core: hash `inlen` bytes of `in` (MSB-first bit order) into 16 bits */
static uint16_t
toeplitz_bits(const struct stoeplitz_cache *sc, const u8 *in, int inlen)
{
u8 window[64 + 32]; /* keyhash(32) || key(40) */
int i, bit, k;
uint16_t res = 0;
memcpy(window, sc->keyhash, 32);
memcpy(window + 32, sc->key, TOEPLITZ_KEY_LEN);
for (i = 0; i < inlen; i++) {
for (bit = 0; bit < 8; bit++) {
if (!(in[i] & (0x80 >> bit)))
continue;
for (k = 0; k < 16; k++) {
int pos = i * 8 + bit + k;
int byte = pos / 8;
int b = 7 - (pos % 8);
res ^= (uint16_t)((window[byte] >> b) & 1) << k;
}
}
}
return res;
}
void
stoeplitz_cache_init(struct stoeplitz_cache *sc, const u8 *key)
{
memset(sc, 0, sizeof(*sc));
memcpy(sc->key, key, TOEPLITZ_KEY_LEN);
toeplitz_keyhash(key, sc->keyhash);
sc->initialized = 1;
}
void
stoeplitz_init(void)
{
static u8 key[TOEPLITZ_KEY_LEN];
int i;
if (stoeplitz_cache && stoeplitz_cache->initialized)
return;
for (i = 0; i < TOEPLITZ_KEY_LEN; i++)
key[i] = get_random_u8();
stoeplitz_cache = kzalloc(sizeof(*stoeplitz_cache), GFP_KERNEL);
if (!stoeplitz_cache)
return;
stoeplitz_cache_init(stoeplitz_cache, key);
}
uint16_t
stoeplitz_hash_n16(const struct stoeplitz_cache *sc, u16 n16)
{
u8 b[2] = { (u8)(n16 >> 8), (u8)n16 };
return toeplitz_bits(sc, b, sizeof(b));
}
uint16_t
stoeplitz_hash_n32(const struct stoeplitz_cache *sc, u32 n32)
{
return stoeplitz_hash_n16(sc, (u16)(n32 ^ (n32 >> 16)));
}
uint16_t
stoeplitz_hash_h16(const struct stoeplitz_cache *sc, u16 h16)
{
u8 b[2] = { (u8)(h16 >> 8), (u8)h16 };
return toeplitz_bits(sc, b, sizeof(b));
}
uint16_t
stoeplitz_hash_h32(const struct stoeplitz_cache *sc, u32 h32)
{
return stoeplitz_hash_h16(sc, (u16)(h32 ^ (h32 >> 16)));
}
uint16_t
stoeplitz_hash_h64(const struct stoeplitz_cache *sc, u64 h64)
{
return stoeplitz_hash_h32(sc, (u32)(h64 ^ (h64 >> 32)));
}
uint16_t
stoeplitz_hash_ip4(const struct stoeplitz_cache *sc,
const struct in_addr *sa, const struct in_addr *da)
{
u8 buf[8];
memcpy(buf, &sa->s_addr, 4);
memcpy(buf + 4, &da->s_addr, 4);
return toeplitz_bits(sc, buf, sizeof(buf));
}
uint16_t
stoeplitz_hash_ip4port(const struct stoeplitz_cache *sc,
const struct in_addr *sa, const struct in_addr *da, u16 sp, u16 dp)
{
u8 buf[12];
memcpy(buf, &sa->s_addr, 4);
memcpy(buf + 4, &da->s_addr, 4);
buf[8] = (u8)(sp >> 8); buf[9] = (u8)sp;
buf[10] = (u8)(dp >> 8); buf[11] = (u8)dp;
return toeplitz_bits(sc, buf, sizeof(buf));
}
uint16_t
stoeplitz_hash_ip6(const struct stoeplitz_cache *sc,
const struct in6_addr *sa, const struct in6_addr *da)
{
u8 buf[32];
memcpy(buf, sa, 16);
memcpy(buf + 16, da, 16);
return toeplitz_bits(sc, buf, sizeof(buf));
}
uint16_t
stoeplitz_hash_ip6port(const struct stoeplitz_cache *sc,
const struct in6_addr *sa, const struct in6_addr *da, u16 sp, u16 dp)
{
u8 buf[36];
memcpy(buf, sa, 16);
memcpy(buf + 16, da, 16);
buf[32] = (u8)(sp >> 8); buf[33] = (u8)sp;
buf[34] = (u8)(dp >> 8); buf[35] = (u8)dp;
return toeplitz_bits(sc, buf, sizeof(buf));
}
uint16_t
stoeplitz_hash_eaddr(const struct stoeplitz_cache *sc, const u8 *ea)
{
return toeplitz_bits(sc, ea, 6);
}
+83
View File
@@ -0,0 +1,83 @@
/* $OpenBSD: sha2.h,v 1.5 2014/11/16 17:39:09 tedu Exp $ */
/*
* FILE: sha2.h
* AUTHOR: Aaron D. Gifford <me@aarongifford.com>
*
* Copyright (c) 2000-2001, Aaron D. Gifford
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) 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.
*
* $From: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $
*/
#ifndef _SHA2_H
#define _SHA2_H
/*** SHA-256/384/512 Various Length Definitions ***********************/
#define SHA256_BLOCK_LENGTH 64
#define SHA256_DIGEST_LENGTH 32
#define SHA256_DIGEST_STRING_LENGTH (SHA256_DIGEST_LENGTH * 2 + 1)
#define SHA384_BLOCK_LENGTH 128
#define SHA384_DIGEST_LENGTH 48
#define SHA384_DIGEST_STRING_LENGTH (SHA384_DIGEST_LENGTH * 2 + 1)
#define SHA512_BLOCK_LENGTH 128
#define SHA512_DIGEST_LENGTH 64
#define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1)
/*** SHA-256/384/512 Context Structure *******************************/
typedef struct _SHA2_CTX {
union {
u_int32_t st32[8];
u_int64_t st64[8];
} state;
u_int64_t bitcount[2];
u_int8_t buffer[SHA512_BLOCK_LENGTH];
} SHA2_CTX;
__BEGIN_DECLS
void SHA256Init(SHA2_CTX *);
void SHA256Update(SHA2_CTX *, const void *, size_t)
__attribute__((__bounded__(__string__,2,3)));
void SHA256Final(u_int8_t[SHA256_DIGEST_LENGTH], SHA2_CTX *)
__attribute__((__bounded__(__minbytes__,1,SHA256_DIGEST_LENGTH)));
void SHA384Init(SHA2_CTX *);
void SHA384Update(SHA2_CTX *, const void *, size_t)
__attribute__((__bounded__(__string__,2,3)));
void SHA384Final(u_int8_t[SHA384_DIGEST_LENGTH], SHA2_CTX *)
__attribute__((__bounded__(__minbytes__,1,SHA384_DIGEST_LENGTH)));
void SHA512Init(SHA2_CTX *);
void SHA512Update(SHA2_CTX *, const void *, size_t)
__attribute__((__bounded__(__string__,2,3)));
void SHA512Final(u_int8_t[SHA512_DIGEST_LENGTH], SHA2_CTX *)
__attribute__((__bounded__(__minbytes__,1,SHA512_DIGEST_LENGTH)));
__END_DECLS
#endif /* _SHA2_H */
+27
View File
@@ -0,0 +1,27 @@
/* PFLINUX compat: <crypto/siphash.h> — SipHash-2-4 (Aumasson–Bernstein)
* API compatible with OpenBSD's SIPHASH_CTX/KEY usage in pf_syncookies.c.
* Implementation: compat/siphash.c (ISC 2026). */
#ifndef _PFK_SIPHASH_H
#define _PFK_SIPHASH_H
#include <linux/types.h>
#define SIPHASH_KEY_LENGTH 16
typedef struct {
__u64 v[2];
} SIPHASH_KEY;
typedef struct {
__u64 v[3];
__u64 total_len;
unsigned char block[8];
unsigned char block_len;
} SIPHASH_CTX;
void SipHash24_Init(SIPHASH_CTX *ctx, const SIPHASH_KEY *key);
void SipHash24_Update(SIPHASH_CTX *ctx, const void *in, __u64 inlen);
void SipHash24_Final(SIPHASH_CTX *ctx, unsigned char *out);
void SipHash24_Generate(const SIPHASH_KEY *key, const void *in, __u64 inlen,
unsigned char *out);
#endif
+72
View File
@@ -0,0 +1,72 @@
/* $OpenBSD: if_pflog.h,v 1.29 2021/01/13 09:13:30 mvs Exp $ */
/*
* Copyright 2001 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _NET_IF_PFLOG_H_
#define _NET_IF_PFLOG_H_
#include <sys/queue.h>
#include <net/pfvar.h>
#define PFLOG_RULESET_NAME_SIZE 16
struct pfloghdr {
u_int8_t length;
sa_family_t af;
u_int8_t action;
u_int8_t reason;
char ifname[IFNAMSIZ];
char ruleset[PFLOG_RULESET_NAME_SIZE];
u_int32_t rulenr;
u_int32_t subrulenr;
uid_t uid;
pid_t pid;
uid_t rule_uid;
pid_t rule_pid;
u_int8_t dir;
u_int8_t rewritten;
sa_family_t naf;
u_int8_t pad[1];
struct pf_addr saddr;
struct pf_addr daddr;
u_int16_t sport;
u_int16_t dport;
};
#define PFLOG_HDRLEN sizeof(struct pfloghdr)
/* used to be minus pad, also used as a signature */
#define PFLOG_REAL_HDRLEN PFLOG_HDRLEN
#define PFLOG_OLD_HDRLEN offsetof(struct pfloghdr, pad)
#ifdef _KERNEL
struct pflog_softc {
LIST_ENTRY(pflog_softc) sc_entry;
struct ifnet sc_if; /* the interface */
int sc_unit;
};
#endif /* _KERNEL */
#endif /* _NET_IF_PFLOG_H_ */
+16
View File
@@ -0,0 +1,16 @@
/* PFLINUX compat: <net/if_types.h> — OpenBSD IFT_* constants
* (values must match OpenBSD: they travel in pf_rule/pfk_if ioctls). */
#ifndef _PFK_IF_TYPES_H
#define _PFK_IF_TYPES_H
#define IFT_OTHER 0x1
#define IFT_ETHER 0x6
#define IFT_TOKENRING 0xf
#define IFT_FDDI 0x12
#define IFT_LOOP 0x18
#define IFT_SLIP 0x1f
#define IFT_PPPOINT 0x1e
#define IFT_GIF 0xe6
#define IFT_PFLOG 0xf0
#define IFT_PFSYNC 0xf1
#define IFT_CARP 0xf8
#endif
+5
View File
@@ -0,0 +1,5 @@
/* PFLINUX compat: <net/if_var.h> — ifnet view comes from pfk_if.h */
#ifndef _PFK_IF_VAR_H
#define _PFK_IF_VAR_H
/* intentionally minimal; struct pfk_ifnet is the ifnet */
#endif
+507
View File
@@ -0,0 +1,507 @@
/* $OpenBSD: ifq.h,v 1.44 2025/03/04 01:13:37 dlg Exp $ */
/*
* Copyright (c) 2015 David Gwynne <dlg@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _NET_IFQ_H_
#define _NET_IFQ_H_
struct ifnet;
struct kstat;
struct ifq_ops;
struct ifqueue {
struct ifnet *ifq_if;
struct taskq *ifq_softnet;
union {
void *_ifq_softc;
/*
* a rings sndq is found by looking up an array of pointers.
* by default we only have one sndq and the default drivers
* dont use ifq_softc, so we can borrow it for the map until
* we need to allocate a proper map.
*/
struct ifqueue *_ifq_ifqs[1];
} _ifq_ptr;
#define ifq_softc _ifq_ptr._ifq_softc
#define ifq_ifqs _ifq_ptr._ifq_ifqs
/* mbuf handling */
struct mutex ifq_mtx;
const struct ifq_ops *ifq_ops;
void *ifq_q;
struct mbuf_list ifq_free;
unsigned int ifq_len;
unsigned int ifq_oactive;
/* statistics */
uint64_t ifq_packets;
uint64_t ifq_bytes;
uint64_t ifq_qdrops;
uint64_t ifq_errors;
uint64_t ifq_mcasts;
uint32_t ifq_oactives;
struct kstat *ifq_kstat;
/* work serialisation */
struct mutex ifq_task_mtx;
struct task_list ifq_task_list;
void *ifq_serializer;
struct task ifq_bundle;
/* work to be serialised */
struct task ifq_start;
struct task ifq_restart;
/* properties */
unsigned int ifq_maxlen;
unsigned int ifq_idx;
};
struct ifiqueue {
struct ifnet *ifiq_if;
caddr_t *ifiq_bpfp;
struct taskq *ifiq_softnet;
union {
void *_ifiq_softc;
struct ifiqueue *_ifiq_ifiqs[1];
} _ifiq_ptr;
#define ifiq_softc _ifiq_ptr._ifiq_softc
#define ifiq_ifiqs _ifiq_ptr._ifiq_ifiqs
struct mutex ifiq_mtx;
struct mbuf_list ifiq_ml;
struct task ifiq_task;
unsigned int ifiq_pressure;
/* counters */
uint64_t ifiq_packets;
uint64_t ifiq_bytes;
uint64_t ifiq_fdrops;
uint64_t ifiq_qdrops;
uint64_t ifiq_errors;
uint64_t ifiq_mcasts;
uint64_t ifiq_noproto;
/* number of times a list of packets were put on ifiq_ml */
uint64_t ifiq_enqueues;
/* number of times a list of packets were pulled off ifiq_ml */
uint64_t ifiq_dequeues;
struct kstat *ifiq_kstat;
/* properties */
unsigned int ifiq_idx;
};
#ifdef _KERNEL
#define IFQ_MAXLEN 256
/*
*
* Interface Send Queues
*
* struct ifqueue sits between the network stack and a drivers
* transmission of packets. The high level view is that when the stack
* has finished generating a packet it hands it to a driver for
* transmission. It does this by queueing the packet on an ifqueue and
* notifying the driver to start transmission of the queued packets.
*
* A network device may have multiple contexts for the transmission
* of packets, ie, independent transmit rings. Such a network device,
* represented by a struct ifnet, would then have multiple ifqueue
* structures, each of which maps to an independent transmit ring.
*
* struct ifqueue also provides the point where conditioning of
* traffic (ie, priq and hfsc) is implemented, and provides some
* infrastructure to assist in the implementation of network drivers.
*
* = ifq API
*
* The ifq API provides functions for three distinct consumers:
*
* 1. The network stack
* 2. Traffic QoS/conditioning implementations
* 3. Network drivers
*
* == Network Stack API
*
* The network stack is responsible for initialising and destroying
* the ifqueue structures, changing the traffic conditioner on an
* interface, enqueuing packets for transmission, and notifying
* the driver to start transmission of a particular ifqueue.
*
* === ifq_init()
*
* During if_attach(), the network stack calls ifq_init to initialise
* the ifqueue structure. By default it configures the priq traffic
* conditioner.
*
* === ifq_destroy()
*
* The network stack calls ifq_destroy() during if_detach to tear down
* the ifqueue structure. It frees the traffic conditioner state, and
* frees any mbufs that were left queued.
*
* === ifq_attach()
*
* ifq_attach() is used to replace the current traffic conditioner on
* the ifqueue. All the pending mbufs are removed from the previous
* conditioner and requeued on the new.
*
* === ifq_idx()
*
* ifq_idx() selects a specific ifqueue from the current ifnet
* structure for use in the transmission of the mbuf.
*
* === ifq_enqueue()
*
* ifq_enqueue() attempts to fit an mbuf onto the ifqueue. The
* current traffic conditioner may drop a packet to make space on the
* queue.
*
* === ifq_start()
*
* Once a packet has been successfully queued with ifq_enqueue(),
* the network card is notified with a call to ifq_start().
* Calls to ifq_start() run in the ifqueue serialisation context,
* guaranteeing that only one instance of ifp->if_qstart() will be
* running on behalf of a specific ifqueue in the system at any point
* in time.
*
* == Traffic conditioners API
*
* The majority of interaction between struct ifqueue and a traffic
* conditioner occurs via the callbacks a traffic conditioner provides
* in an instance of struct ifq_ops.
*
* XXX document ifqop_*
*
* The ifqueue API implements the locking on behalf of the conditioning
* implementations so conditioners only have to reject or keep mbufs.
* If something needs to inspect a conditioners internals, the queue lock
* needs to be taken to allow for a consistent or safe view. The queue
* lock may be taken and released with ifq_q_enter() and ifq_q_leave().
*
* === ifq_q_enter()
*
* Code wishing to access a conditioners internals may take the queue
* lock with ifq_q_enter(). The caller must pass a reference to the
* conditioners ifq_ops structure so the infrastructure can ensure the
* caller is able to understand the internals. ifq_q_enter() returns
* a pointer to the conditioners internal structures, or NULL if the
* ifq_ops did not match the current conditioner.
*
* === ifq_q_leave()
*
* The queue lock acquired with ifq_q_enter() is released with
* ifq_q_leave().
*
* === ifq_mfreem() and ifq_mfreeml()
*
* A goal of the API is to avoid freeing an mbuf while mutexes are
* held. Because the ifq API manages the lock on behalf of the backend
* ifqops, the backend should not directly free mbufs. If a conditioner
* backend needs to drop a packet during the handling of ifqop_deq_begin,
* it may free it by calling ifq_mfreem(). This accounts for the drop,
* and schedules the free of the mbuf outside the hold of ifq_mtx.
* ifq_mfreeml() takes an mbuf list as an argument instead.
*
*
* == Network Driver API
*
* The API used by network drivers is mostly documented in the
* ifq_dequeue(9) manpage except for ifq_serialize().
*
* === ifq_serialize()
*
* A driver may run arbitrary work in the ifqueue serialiser context
* via ifq_serialize(). The work to be done is represented by a task
* that has been prepared with task_set.
*
* The work will be run in series with any other work dispatched by
* ifq_start(), ifq_restart(), or other ifq_serialize() calls.
*
* Because the work may be run on another CPU, the lifetime of the
* task and the work it represents can extend beyond the end of the
* call to ifq_serialize() that dispatched it.
*
*
* = ifqueue work serialisation
*
* ifqueues provide a mechanism to dispatch work to be run in a single
* context. Work in this mechanism is represented by task structures.
*
* The tasks are run in a context similar to a taskq serviced by a
* single kernel thread, except the work is run immediately by the
* first CPU that dispatches work. If a second CPU attempts to dispatch
* additional tasks while the first is still running, it will be queued
* to be run by the first CPU. The second CPU will return immediately.
*
* = MP Safe Network Drivers
*
* An MP safe network driver is one in which its start routine can be
* called by the network stack without holding the big kernel lock.
*
* == Attach
*
* A driver advertises its ability to run its start routine without
* the kernel lock by setting the IFXF_MPSAFE flag in ifp->if_xflags
* before calling if_attach(). Advertising an MPSAFE start routine
* also implies that the driver understands that a network card can
* have multiple rings or transmit queues, and therefore provides
* if_qstart function (which takes an ifqueue pointer) instead of an
* if_start function (which takes an ifnet pointer).
*
* If the hardware supports multiple transmit rings, it advertises
* support for multiple rings to the network stack with if_attach_queues()
* after the call to if_attach(). if_attach_queues allocates a struct
* ifqueue for each hardware ring, which can then be initialised by
* the driver with data for each ring.
*
* void drv_start(struct ifqueue *);
*
* void
* drv_attach()
* {
* ...
* ifp->if_xflags = IFXF_MPSAFE;
* ifp->if_qstart = drv_start;
* if_attach(ifp);
*
* if_attach_queues(ifp, DRV_NUM_TX_RINGS);
* for (i = 0; i < DRV_NUM_TX_RINGS; i++) {
* struct ifqueue *ifq = ifp->if_ifqs[i];
* struct drv_tx_ring *ring = &sc->sc_tx_rings[i];
*
* ifq->ifq_softc = ring;
* ring->ifq = ifq;
* }
* }
*
* The network stack will then call ifp->if_qstart via ifq_start()
* to guarantee there is only one instance of that function running
* for each ifq in the system, and to serialise it with other work
* the driver may provide.
*
* == Initialise
*
* When the stack requests an interface be brought up (ie, drv_ioctl()
* is called to handle SIOCSIFFLAGS with IFF_UP set in ifp->if_flags)
* drivers should set IFF_RUNNING in ifp->if_flags, and then call
* ifq_clr_oactive() against each ifq.
*
* == if_start
*
* ifq_start() checks that IFF_RUNNING is set in ifp->if_flags, that
* ifq_is_oactive() does not return true, and that there are pending
* packets to transmit via a call to ifq_len(). Therefore, drivers are
* no longer responsible for doing this themselves.
*
* If a driver should not transmit packets while its link is down, use
* ifq_purge() to flush pending packets from the transmit queue.
*
* Drivers for hardware should use the following pattern to transmit
* packets:
*
* void
* drv_start(struct ifqueue *ifq)
* {
* struct drv_tx_ring *ring = ifq->ifq_softc;
* struct ifnet *ifp = ifq->ifq_if;
* struct drv_softc *sc = ifp->if_softc;
* struct mbuf *m;
* int kick = 0;
*
* if (NO_LINK) {
* ifq_purge(ifq);
* return;
* }
*
* for (;;) {
* if (NO_SPACE(ring)) {
* ifq_set_oactive(ifq);
* break;
* }
*
* m = ifq_dequeue(ifq);
* if (m == NULL)
* break;
*
* if (drv_encap(sc, ring, m) != 0) { // map and fill ring
* m_freem(m);
* continue;
* }
*
* bpf_mtap();
* }
*
* drv_kick(ring); // notify hw of new descriptors on the ring
* }
*
* == Transmission completion
*
* The following pattern should be used for transmit queue interrupt
* processing:
*
* void
* drv_txeof(struct drv_tx_ring *ring)
* {
* struct ifqueue *ifq = ring->ifq;
*
* while (COMPLETED_PKTS(ring)) {
* // unmap packets, m_freem() the mbufs.
* }
*
* if (ifq_is_oactive(ifq))
* ifq_restart(ifq);
* }
*
* == Stop
*
* Bringing an interface down (ie, IFF_UP was cleared in ifp->if_flags)
* should clear IFF_RUNNING in ifp->if_flags, and guarantee the start
* routine is not running before freeing any resources it uses:
*
* void
* drv_down(struct drv_softc *sc)
* {
* struct ifnet *ifp = &sc->sc_if;
* struct ifqueue *ifq;
* int i;
*
* CLR(ifp->if_flags, IFF_RUNNING);
* DISABLE_INTERRUPTS();
*
* for (i = 0; i < sc->sc_num_queues; i++) {
* ifq = ifp->if_ifqs[i];
* ifq_barrier(ifq);
* }
*
* intr_barrier(sc->sc_ih);
*
* FREE_RESOURCES();
*
* for (i = 0; i < sc->sc_num_queues; i++) {
* ifq = ifp->if_ifqs[i];
* ifq_clr_oactive(ifq);
* }
* }
*
*/
struct ifq_ops {
unsigned int (*ifqop_idx)(unsigned int,
const struct mbuf *);
struct mbuf *(*ifqop_enq)(struct ifqueue *, struct mbuf *);
struct mbuf *(*ifqop_deq_begin)(struct ifqueue *, void **);
void (*ifqop_deq_commit)(struct ifqueue *,
struct mbuf *, void *);
void (*ifqop_purge)(struct ifqueue *,
struct mbuf_list *);
void *(*ifqop_alloc)(unsigned int, void *);
void (*ifqop_free)(unsigned int, void *);
};
extern const struct ifq_ops * const ifq_priq_ops;
/*
* Interface send queues.
*/
void ifq_init(struct ifqueue *, struct ifnet *, unsigned int);
void ifq_attach(struct ifqueue *, const struct ifq_ops *, void *);
void ifq_destroy(struct ifqueue *);
void ifq_add_data(struct ifqueue *, struct if_data *);
int ifq_enqueue(struct ifqueue *, struct mbuf *);
void ifq_start(struct ifqueue *);
struct mbuf *ifq_deq_begin(struct ifqueue *);
void ifq_deq_commit(struct ifqueue *, struct mbuf *);
void ifq_deq_rollback(struct ifqueue *, struct mbuf *);
struct mbuf *ifq_dequeue(struct ifqueue *);
int ifq_hdatalen(struct ifqueue *);
void ifq_init_maxlen(struct ifqueue *, unsigned int);
void ifq_mfreem(struct ifqueue *, struct mbuf *);
void ifq_mfreeml(struct ifqueue *, struct mbuf_list *);
unsigned int ifq_purge(struct ifqueue *);
void *ifq_q_enter(struct ifqueue *, const struct ifq_ops *);
void ifq_q_leave(struct ifqueue *, void *);
void ifq_serialize(struct ifqueue *, struct task *);
void ifq_barrier(struct ifqueue *);
void ifq_set_oactive(struct ifqueue *);
void ifq_deq_set_oactive(struct ifqueue *);
int ifq_deq_sleep(struct ifqueue *, struct mbuf **, int, int,
const char *, volatile unsigned int *,
volatile unsigned int *);
#define ifq_len(_ifq) READ_ONCE((_ifq)->ifq_len)
#define ifq_empty(_ifq) (ifq_len(_ifq) == 0)
static inline int
ifq_is_priq(struct ifqueue *ifq)
{
return (ifq->ifq_ops == ifq_priq_ops);
}
static inline void
ifq_clr_oactive(struct ifqueue *ifq)
{
ifq->ifq_oactive = 0;
}
static inline unsigned int
ifq_is_oactive(struct ifqueue *ifq)
{
return (ifq->ifq_oactive);
}
static inline void
ifq_restart(struct ifqueue *ifq)
{
ifq_serialize(ifq, &ifq->ifq_restart);
}
static inline unsigned int
ifq_idx(struct ifqueue *ifq, unsigned int nifqs, const struct mbuf *m)
{
return ((*ifq->ifq_ops->ifqop_idx)(nifqs, m));
}
/* ifiq */
void ifiq_init(struct ifiqueue *, struct ifnet *, unsigned int);
void ifiq_destroy(struct ifiqueue *);
int ifiq_input(struct ifiqueue *, struct mbuf_list *);
int ifiq_enqueue_qlim(struct ifiqueue *, struct mbuf *,
unsigned int);
void ifiq_add_data(struct ifiqueue *, struct if_data *);
#define ifiq_len(_ifiq) READ_ONCE(ml_len(&(_ifiq)->ifiq_ml))
#define ifiq_empty(_ifiq) (ifiq_len(_ifiq) == 0)
static inline int
ifiq_enqueue(struct ifiqueue *ifiq, struct mbuf *m)
{
return ifiq_enqueue_qlim(ifiq, m, 0);
}
#endif /* _KERNEL */
#endif /* _NET_IFQ_H_ */
+75
View File
@@ -0,0 +1,75 @@
/* PFLINUX compat: <net/radix.h> — OpenBSD 8.0 radix structures/API
* (rn_*), cleaned of BSD-only syntax. Implementation: compat/radix.c
* (ported from OpenBSD 8.0 sys/net/radix.c, task T10). */
#ifndef _PFK_RADIX_H
#define _PFK_RADIX_H
#include <linux/types.h>
struct radix_mask;
struct radix_node {
struct radix_mask *rn_mklist;
struct radix_node *rn_p;
short rn_b;
char rn_bmask;
u_char rn_flags;
#define RNF_NORMAL 1
#define RNF_ROOT 2
#define RNF_ACTIVE 4
union {
struct {
caddr_t rn_Key;
caddr_t rn_Mask;
struct radix_node *rn_Dupedkey;
} rn_leaf;
struct {
int rn_Off;
struct radix_node *rn_L;
struct radix_node *rn_R;
} rn_node;
} rn_u;
};
#define rn_dupedkey rn_u.rn_leaf.rn_Dupedkey
#define rn_key rn_u.rn_leaf.rn_Key
#define rn_mask rn_u.rn_leaf.rn_Mask
#define rn_off rn_u.rn_node.rn_Off
#define rn_l rn_u.rn_node.rn_L
#define rn_r rn_u.rn_node.rn_R
struct radix_mask {
short rm_b;
char rm_unused;
u_char rm_flags;
struct radix_mask *rm_mklist;
union {
caddr_t rmu_mask;
struct radix_node *rmu_leaf;
} rm_rmu;
int rm_refs;
};
#define rm_mask rm_rmu.rmu_mask
#define rm_leaf rm_rmu.rmu_leaf
struct radix_node_head {
struct radix_node *rnh_treetop;
int rnh_addrsize;
int rnh_pktsize;
struct radix_node rnh_nodes[3];
u_int rnh_rtableid;
};
void rn_init(unsigned int);
int rn_inithead(void **, int);
int rn_walktree(struct radix_node_head *,
int (*)(struct radix_node *, void *, u_int), void *);
struct radix_node *rn_addroute(void *, void *, struct radix_node_head *,
struct radix_node [2], u_int8_t);
struct radix_node *rn_delete(void *, void *, struct radix_node_head *,
struct radix_node *);
struct radix_node *rn_lookup(void *, void *, struct radix_node_head *);
struct radix_node *rn_match(void *, struct radix_node_head *);
#endif /* _PFK_RADIX_H_ */
+82
View File
@@ -0,0 +1,82 @@
/* $OpenBSD: rtable.h,v 1.36 2025/07/15 09:55:49 dlg Exp $ */
/*
* Copyright (c) 2014-2016 Martin Pieuchot
*
* 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.
*/
#ifndef _NET_RTABLE_H_
#define _NET_RTABLE_H_
#include <sys/rwlock.h>
struct art;
/*
* Locks used to protect struct members in this file:
* I immutable after creation
* N net lock
*/
struct rtable {
struct rwlock r_lock;
struct art *r_art; /* [I] */
unsigned int r_off; /* [I] Offset of key in bytes */
struct sockaddr *r_source; /* [N] use optional src addr */
};
/*
* Newer routing table implementation based on ART (Allotment Routing
* Table).
*/
#define rt_key(rt) ((rt)->rt_dest)
#define rt_plen(rt) ((rt)->rt_plen)
#define RT_ROOT(rt) (0)
int rtable_satoplen(sa_family_t, const struct sockaddr *);
void rtable_init(void);
int rtable_exists(unsigned int);
int rtable_empty(unsigned int);
int rtable_add(unsigned int);
unsigned int rtable_l2(unsigned int);
unsigned int rtable_loindex(unsigned int);
void rtable_l2set(unsigned int, unsigned int, unsigned int);
int rtable_setsource(unsigned int, int, struct sockaddr *);
struct sockaddr *rtable_getsource(unsigned int, int);
void rtable_clearsource(unsigned int, struct sockaddr *);
struct rtentry *rtable_lookup(unsigned int, const struct sockaddr *,
const struct sockaddr *, const struct sockaddr *, uint8_t);
struct rtentry *rtable_match(unsigned int, const struct sockaddr *,
uint32_t *);
struct rtentry *rtable_iterate(struct rtentry *);
int rtable_insert(unsigned int, struct sockaddr *,
const struct sockaddr *, const struct sockaddr *, uint8_t,
struct rtentry *);
int rtable_delete(unsigned int, const struct sockaddr *,
const struct sockaddr *, struct rtentry *);
int rtable_walk(unsigned int, sa_family_t, struct rtentry **,
int (*)(struct rtentry *, void *, unsigned int), void *);
int rtable_read(unsigned int, sa_family_t,
int (*)(const struct rtentry *, void *, unsigned int),
void *);
int rtable_mpath_capable(unsigned int, sa_family_t);
int rtable_mpath_reprio(unsigned int, struct sockaddr *, int,
uint8_t, struct rtentry *);
#endif /* _NET_RTABLE_H_ */
+53
View File
@@ -0,0 +1,53 @@
/* PFLINUX compat: <net/toeplitz.h> — software Toeplitz hash (RFC 3305)
* with the OpenBSD API shape used by PF (global cache + stoeplitz_* macros).
* Implementation: compat/toeplitz.c (uses kernel crypto_shash "sha256"). */
#ifndef _PFK_TOEPLITZ_H
#define _PFK_TOEPLITZ_H
#include <linux/types.h>
#define TOEPLITZ_KEY_LEN 40
struct stoeplitz_cache {
u8 key[TOEPLITZ_KEY_LEN];
u8 keyhash[32]; /* SHA256(key) */
int initialized;
};
extern struct stoeplitz_cache *stoeplitz_cache;
void stoeplitz_init(void);
void stoeplitz_cache_init(struct stoeplitz_cache *, const u8 *key);
uint16_t stoeplitz_hash_ip4(const struct stoeplitz_cache *,
const struct in_addr *, const struct in_addr *);
uint16_t stoeplitz_hash_ip4port(const struct stoeplitz_cache *,
const struct in_addr *, const struct in_addr *,
u16, u16);
uint16_t stoeplitz_hash_ip6(const struct stoeplitz_cache *,
const struct in6_addr *, const struct in6_addr *);
uint16_t stoeplitz_hash_ip6port(const struct stoeplitz_cache *,
const struct in6_addr *, const struct in6_addr *,
u16, u16);
uint16_t stoeplitz_hash_eaddr(const struct stoeplitz_cache *,
const u8 *);
uint16_t stoeplitz_hash_n16(const struct stoeplitz_cache *, u16);
uint16_t stoeplitz_hash_n32(const struct stoeplitz_cache *, u32);
uint16_t stoeplitz_hash_h16(const struct stoeplitz_cache *, u16);
uint16_t stoeplitz_hash_h32(const struct stoeplitz_cache *, u32);
uint16_t stoeplitz_hash_h64(const struct stoeplitz_cache *, u64);
#define stoeplitz_n32(_n32) stoeplitz_hash_n32(stoeplitz_cache, (_n32))
#define stoeplitz_h16(_h16) stoeplitz_hash_h16(stoeplitz_cache, (_h16))
#define stoeplitz_h32(_h32) stoeplitz_hash_h32(stoeplitz_cache, (_h32))
#define stoeplitz_h64(_h64) stoeplitz_hash_h64(stoeplitz_cache, (_h64))
#define stoeplitz_ip4(_sa4, _da4) \
stoeplitz_hash_ip4(stoeplitz_cache, (_sa4), (_da4))
#define stoeplitz_ip4port(_sa4, _da4, _sp, _dp) \
stoeplitz_hash_ip4port(stoeplitz_cache, (_sa4), (_da4), (_sp), (_dp))
#define stoeplitz_ip6(_sa6, _da6) \
stoeplitz_hash_ip6(stoeplitz_cache, (_sa6), (_da6))
#define stoeplitz_ip6port(_sa6, _da6, _sp, _dp) \
stoeplitz_hash_ip6port(stoeplitz_cache, (_sa6), (_da6), (_sp), (_dp))
#define stoeplitz_eaddr(_ea) \
stoeplitz_hash_eaddr(stoeplitz_cache, (_ea))
#endif
+13
View File
@@ -0,0 +1,13 @@
/* PFLINUX compat: <netinet/in.h> */
#ifndef _PFK_NETINET_IN_H
#define _PFK_NETINET_IN_H
#include <linux/in.h>
#include <linux/in6.h>
/* BSD IP-level option numbers (Linux uapi uses different values) */
#define PF_IP_OPTIONS 1
#define PF_IP_RECORD_ROUTE 7
#define PF_IP_TIMESTAMP 68
#ifndef INADDR_MULTICAST
#define INADDR_MULTICAST 0xe0000000U
#endif
#endif
+179
View File
@@ -0,0 +1,179 @@
/* $OpenBSD: _endian.h,v 1.8 2018/01/11 23:13:37 dlg Exp $ */
/*-
* Copyright (c) 1997 Niklas Hallqvist. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Internal endianness macros. This pulls in <machine/endian.h> to
* get the correct setting direction for the platform and sets internal
* ('__' prefix) macros appropriately.
*/
#ifndef _SYS__ENDIAN_H_
#define _SYS__ENDIAN_H_
#include <sys/_types.h>
#define __FROM_SYS__ENDIAN
#include <machine/endian.h>
#undef __FROM_SYS__ENDIAN
#define _LITTLE_ENDIAN 1234
#define _BIG_ENDIAN 4321
#define _PDP_ENDIAN 3412
/* Note that these macros evaluate their arguments several times. */
#define __swap16gen(x) \
(__uint16_t)(((__uint16_t)(x) & 0xffU) << 8 | ((__uint16_t)(x) & 0xff00U) >> 8)
#define __swap32gen(x) \
(__uint32_t)(((__uint32_t)(x) & 0xff) << 24 | \
((__uint32_t)(x) & 0xff00) << 8 | ((__uint32_t)(x) & 0xff0000) >> 8 |\
((__uint32_t)(x) & 0xff000000) >> 24)
#define __swap64gen(x) \
(__uint64_t)((((__uint64_t)(x) & 0xff) << 56) | \
((__uint64_t)(x) & 0xff00ULL) << 40 | \
((__uint64_t)(x) & 0xff0000ULL) << 24 | \
((__uint64_t)(x) & 0xff000000ULL) << 8 | \
((__uint64_t)(x) & 0xff00000000ULL) >> 8 | \
((__uint64_t)(x) & 0xff0000000000ULL) >> 24 | \
((__uint64_t)(x) & 0xff000000000000ULL) >> 40 | \
((__uint64_t)(x) & 0xff00000000000000ULL) >> 56)
#ifndef __HAVE_MD_SWAP
static __inline __uint16_t
__swap16md(__uint16_t x)
{
return (__swap16gen(x));
}
static __inline __uint32_t
__swap32md(__uint32_t x)
{
return (__swap32gen(x));
}
static __inline __uint64_t
__swap64md(__uint64_t x)
{
return (__swap64gen(x));
}
#endif
#define __swap16(x) \
(__uint16_t)(__builtin_constant_p(x) ? __swap16gen(x) : __swap16md(x))
#define __swap32(x) \
(__uint32_t)(__builtin_constant_p(x) ? __swap32gen(x) : __swap32md(x))
#define __swap64(x) \
(__uint64_t)(__builtin_constant_p(x) ? __swap64gen(x) : __swap64md(x))
#if _BYTE_ORDER == _LITTLE_ENDIAN
#define _QUAD_HIGHWORD 1
#define _QUAD_LOWWORD 0
#define __htobe16 __swap16
#define __htobe32 __swap32
#define __htobe64 __swap64
#define __htole16(x) ((__uint16_t)(x))
#define __htole32(x) ((__uint32_t)(x))
#define __htole64(x) ((__uint64_t)(x))
#ifdef _KERNEL
#ifdef __HAVE_MD_SWAPIO
#define __bemtoh16(_x) __mswap16(_x)
#define __bemtoh32(_x) __mswap32(_x)
#define __bemtoh64(_x) __mswap64(_x)
#define __htobem16(_x, _v) __swapm16((_x), (_v))
#define __htobem32(_x, _v) __swapm32((_x), (_v))
#define __htobem64(_x, _v) __swapm64((_x), (_v))
#endif /* __HAVE_MD_SWAPIO */
#endif /* _KERNEL */
#endif /* _BYTE_ORDER == _LITTLE_ENDIAN */
#if _BYTE_ORDER == _BIG_ENDIAN
#define _QUAD_HIGHWORD 0
#define _QUAD_LOWWORD 1
#define __htobe16(x) ((__uint16_t)(x))
#define __htobe32(x) ((__uint32_t)(x))
#define __htobe64(x) ((__uint64_t)(x))
#define __htole16 __swap16
#define __htole32 __swap32
#define __htole64 __swap64
#ifdef _KERNEL
#ifdef __HAVE_MD_SWAPIO
#define __lemtoh16(_x) __mswap16(_x)
#define __lemtoh32(_x) __mswap32(_x)
#define __lemtoh64(_x) __mswap64(_x)
#define __htolem16(_x, _v) __swapm16((_x), (_v))
#define __htolem32(_x, _v) __swapm32((_x), (_v))
#define __htolem64(_x, _v) __swapm64((_x), (_v))
#endif /* __HAVE_MD_SWAPIO */
#endif /* _KERNEL */
#endif /* _BYTE_ORDER == _BIG_ENDIAN */
#ifdef _KERNEL
/*
* Fill in the __hto[bl]em{16,32,64} and __[bl]emtoh{16,32,64} macros
* that haven't been defined yet
*/
#ifndef __bemtoh16
#define __bemtoh16(_x) __htobe16(*(__uint16_t *)(_x))
#define __bemtoh32(_x) __htobe32(*(__uint32_t *)(_x))
#define __bemtoh64(_x) __htobe64(*(__uint64_t *)(_x))
#endif
#ifndef __htobem16
#define __htobem16(_x, _v) (*(__uint16_t *)(_x) = __htobe16(_v))
#define __htobem32(_x, _v) (*(__uint32_t *)(_x) = __htobe32(_v))
#define __htobem64(_x, _v) (*(__uint64_t *)(_x) = __htobe64(_v))
#endif
#ifndef __lemtoh16
#define __lemtoh16(_x) __htole16(*(__uint16_t *)(_x))
#define __lemtoh32(_x) __htole32(*(__uint32_t *)(_x))
#define __lemtoh64(_x) __htole64(*(__uint64_t *)(_x))
#endif
#ifndef __htolem16
#define __htolem16(_x, _v) (*(__uint16_t *)(_x) = __htole16(_v))
#define __htolem32(_x, _v) (*(__uint32_t *)(_x) = __htole32(_v))
#define __htolem64(_x, _v) (*(__uint64_t *)(_x) = __htole64(_v))
#endif
#endif /* _KERNEL */
#endif /* _SYS__ENDIAN_H_ */
+7
View File
@@ -0,0 +1,7 @@
/* PFLINUX compat: <sys/_null.h> */
#ifndef _SYS__NULL_H_
#define _SYS__NULL_H_
#ifndef NULL
#define NULL 0L
#endif
#endif
+34
View File
@@ -0,0 +1,34 @@
/* PFLINUX compat: <sys/cdefs.h> minimal for queue.h/tree.h */
#ifndef _PFK_CDEFS_H
#define _PFK_CDEFS_H
#include <linux/compiler.h>
#define __BEGIN_DECLS
#define __END_DECLS
#ifndef __aligned
#define __aligned(x) __attribute__((__aligned__(x)))
#endif
#ifndef __packed
#define __packed __attribute__((__packed__))
#endif
#ifndef __unused
#define __unused __attribute__((__unused__))
#endif
#ifndef __dead
#define __dead __attribute__((__noreturn__))
#endif
#ifndef __predict_false
#define __predict_false(x) unlikely(x)
#endif
#ifndef __predict_true
#define __predict_true(x) likely(x)
#endif
#ifndef __arraycount
#define __arraycount(a) (sizeof(a)/sizeof((a)[0]))
#endif
#ifndef __predict
#define __predict(x, y) ((y) ? likely(x) : unlikely(x))
#endif
#ifndef __restrict
#define __restrict
#endif
#endif
+6
View File
@@ -0,0 +1,6 @@
/* PFLINUX compat: <sys/endian.h> */
#ifndef _PFK_SYS_ENDIAN_H
#define _PFK_SYS_ENDIAN_H
#include <linux/byteorder/generic.h>
#include <linux/byteorder/little_endian.h>
#endif
+10
View File
@@ -0,0 +1,10 @@
/* PFLINUX compat: <sys/param.h> */
#ifndef _PFK_PARAM_H
#define _PFK_PARAM_H
#include <linux/limits.h>
#define MAXHOSTNAMELEN 64
#define NOFILE 256
#define BSD 20260101
#define BSD_VERSION BSD
#define NETBSD 0
#endif
+633
View File
@@ -0,0 +1,633 @@
/* $OpenBSD: queue.h,v 1.47 2026/06/12 01:04:42 millert Exp $ */
/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
/*
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
*
* @(#)queue.h 8.5 (Berkeley) 8/20/94
*/
#ifndef _SYS_QUEUE_H_
#define _SYS_QUEUE_H_
#include <sys/_null.h>
/*
* This file defines five types of data structures: singly-linked lists,
* lists, simple queues, tail queues and XOR simple queues.
*
*
* A singly-linked list is headed by a single forward pointer. The elements
* are singly linked for minimum space and pointer manipulation overhead at
* the expense of O(n) removal for arbitrary elements. New elements can be
* added to the list after an existing element or at the head of the list.
* Elements being removed from the head of the list should use the explicit
* macro for this purpose for optimum efficiency. A singly-linked list may
* only be traversed in the forward direction. Singly-linked lists are ideal
* for applications with large datasets and few or no removals or for
* implementing a LIFO queue.
*
* A list is headed by a single forward pointer (or an array of forward
* pointers for a hash table header). The elements are doubly linked
* so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before
* or after an existing element or at the head of the list. A list
* may only be traversed in the forward direction.
*
* A simple queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are singly
* linked to save space, so elements can only be removed from the
* head of the list. New elements can be added to the list before or after
* an existing element, at the head of the list, or at the end of the
* list. A simple queue may only be traversed in the forward direction.
*
* A tail queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or
* after an existing element, at the head of the list, or at the end of
* the list. A tail queue may be traversed in either direction.
*
* An XOR simple queue is used in the same way as a regular simple queue.
* The difference is that the head structure also includes a "cookie" that
* is XOR'd with the queue pointer (first, last or next) to generate the
* real pointer value.
*
* For details on the use of these macros, see the queue(3) manual page.
*/
#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
#define _Q_INVALID ((void *)-1)
#define _Q_INVALIDATE(a) (a) = _Q_INVALID
#else
#define _Q_INVALIDATE(a)
#endif
/*
* Singly-linked List definitions.
*/
#define SLIST_HEAD(name, type) \
struct name { \
struct type *slh_first; /* first element */ \
}
#define SLIST_HEAD_INITIALIZER(head) \
{ NULL }
#define SLIST_ENTRY(type) \
struct { \
struct type *sle_next; /* next element */ \
}
/*
* Singly-linked List access methods.
*/
#define SLIST_FIRST(head) ((head)->slh_first)
#define SLIST_END(head) NULL
#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
#define SLIST_FOREACH(var, head, field) \
for((var) = SLIST_FIRST(head); \
(var) != SLIST_END(head); \
(var) = SLIST_NEXT(var, field))
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = SLIST_FIRST(head); \
(var) && ((tvar) = SLIST_NEXT(var, field), 1); \
(var) = (tvar))
/*
* Singly-linked List functions.
*/
#define SLIST_INIT(head) { \
SLIST_FIRST(head) = SLIST_END(head); \
}
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
(elm)->field.sle_next = (slistelm)->field.sle_next; \
(slistelm)->field.sle_next = (elm); \
} while (0)
#define SLIST_INSERT_HEAD(head, elm, field) do { \
(elm)->field.sle_next = (head)->slh_first; \
(head)->slh_first = (elm); \
} while (0)
#define SLIST_REMOVE_AFTER(elm, field) do { \
(elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
} while (0)
#define SLIST_REMOVE_HEAD(head, field) do { \
(head)->slh_first = (head)->slh_first->field.sle_next; \
} while (0)
#define SLIST_REMOVE(head, elm, type, field) do { \
if ((head)->slh_first == (elm)) { \
SLIST_REMOVE_HEAD((head), field); \
} else { \
struct type *curelm = (head)->slh_first; \
\
while (curelm->field.sle_next != (elm)) \
curelm = curelm->field.sle_next; \
curelm->field.sle_next = \
curelm->field.sle_next->field.sle_next; \
} \
_Q_INVALIDATE((elm)->field.sle_next); \
} while (0)
/*
* List definitions.
*/
#define LIST_HEAD(name, type) \
struct name { \
struct type *lh_first; /* first element */ \
}
#define LIST_HEAD_INITIALIZER(head) \
{ NULL }
#define LIST_ENTRY(type) \
struct { \
struct type *le_next; /* next element */ \
struct type **le_prev; /* address of previous next element */ \
}
/*
* List access methods.
*/
#define LIST_FIRST(head) ((head)->lh_first)
#define LIST_END(head) NULL
#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
#define LIST_FOREACH(var, head, field) \
for((var) = LIST_FIRST(head); \
(var)!= LIST_END(head); \
(var) = LIST_NEXT(var, field))
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = LIST_FIRST(head); \
(var) && ((tvar) = LIST_NEXT(var, field), 1); \
(var) = (tvar))
/*
* List functions.
*/
#define LIST_INIT(head) do { \
LIST_FIRST(head) = LIST_END(head); \
} while (0)
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
(listelm)->field.le_next->field.le_prev = \
&(elm)->field.le_next; \
(listelm)->field.le_next = (elm); \
(elm)->field.le_prev = &(listelm)->field.le_next; \
} while (0)
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.le_prev = (listelm)->field.le_prev; \
(elm)->field.le_next = (listelm); \
*(listelm)->field.le_prev = (elm); \
(listelm)->field.le_prev = &(elm)->field.le_next; \
} while (0)
#define LIST_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
(head)->lh_first = (elm); \
(elm)->field.le_prev = &(head)->lh_first; \
} while (0)
#define LIST_REMOVE(elm, field) do { \
if ((elm)->field.le_next != NULL) \
(elm)->field.le_next->field.le_prev = \
(elm)->field.le_prev; \
*(elm)->field.le_prev = (elm)->field.le_next; \
_Q_INVALIDATE((elm)->field.le_prev); \
_Q_INVALIDATE((elm)->field.le_next); \
} while (0)
#define LIST_REPLACE(elm, elm2, field) do { \
if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
(elm2)->field.le_next->field.le_prev = \
&(elm2)->field.le_next; \
(elm2)->field.le_prev = (elm)->field.le_prev; \
*(elm2)->field.le_prev = (elm2); \
_Q_INVALIDATE((elm)->field.le_prev); \
_Q_INVALIDATE((elm)->field.le_next); \
} while (0)
/*
* Simple queue definitions.
*/
#define SIMPLEQ_HEAD(name, type) \
struct name { \
struct type *sqh_first; /* first element */ \
struct type **sqh_last; /* addr of last next element */ \
}
#define SIMPLEQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).sqh_first }
#define SIMPLEQ_ENTRY(type) \
struct { \
struct type *sqe_next; /* next element */ \
}
/*
* Simple queue access methods.
*/
#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
#define SIMPLEQ_END(head) NULL
#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
#define SIMPLEQ_FOREACH(var, head, field) \
for((var) = SIMPLEQ_FIRST(head); \
(var) != SIMPLEQ_END(head); \
(var) = SIMPLEQ_NEXT(var, field))
#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = SIMPLEQ_FIRST(head); \
(var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \
(var) = (tvar))
/*
* Simple queue functions.
*/
#define SIMPLEQ_INIT(head) do { \
(head)->sqh_first = NULL; \
(head)->sqh_last = &(head)->sqh_first; \
} while (0)
#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
(head)->sqh_last = &(elm)->field.sqe_next; \
(head)->sqh_first = (elm); \
} while (0)
#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.sqe_next = NULL; \
*(head)->sqh_last = (elm); \
(head)->sqh_last = &(elm)->field.sqe_next; \
} while (0)
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
(head)->sqh_last = &(elm)->field.sqe_next; \
(listelm)->field.sqe_next = (elm); \
} while (0)
#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
(head)->sqh_last = &(head)->sqh_first; \
} while (0)
#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
== NULL) \
(head)->sqh_last = &(elm)->field.sqe_next; \
} while (0)
#define SIMPLEQ_CONCAT(head1, head2) do { \
if (!SIMPLEQ_EMPTY((head2))) { \
*(head1)->sqh_last = (head2)->sqh_first; \
(head1)->sqh_last = (head2)->sqh_last; \
SIMPLEQ_INIT((head2)); \
} \
} while (0)
/*
* XOR Simple queue definitions.
*/
#define XSIMPLEQ_HEAD(name, type) \
struct name { \
struct type *sqx_first; /* first element */ \
struct type **sqx_last; /* addr of last next element */ \
unsigned long sqx_cookie; \
}
#define XSIMPLEQ_ENTRY(type) \
struct { \
struct type *sqx_next; /* next element */ \
}
/*
* XOR Simple queue access methods.
*/
#define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \
(unsigned long)(ptr)))
#define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first))
#define XSIMPLEQ_END(head) NULL
#define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head))
#define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next))
#define XSIMPLEQ_FOREACH(var, head, field) \
for ((var) = XSIMPLEQ_FIRST(head); \
(var) != XSIMPLEQ_END(head); \
(var) = XSIMPLEQ_NEXT(head, var, field))
#define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = XSIMPLEQ_FIRST(head); \
(var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \
(var) = (tvar))
/*
* XOR Simple queue functions.
*/
#define XSIMPLEQ_INIT(head) do { \
arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \
(head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
} while (0)
#define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.sqx_next = (head)->sqx_first) == \
XSIMPLEQ_XOR(head, NULL)) \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
(head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \
} while (0)
#define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \
*(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
} while (0)
#define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \
XSIMPLEQ_XOR(head, NULL)) \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
(listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \
} while (0)
#define XSIMPLEQ_REMOVE_HEAD(head, field) do { \
if (((head)->sqx_first = XSIMPLEQ_XOR(head, \
(head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
} while (0)
#define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \
(elm)->field.sqx_next)->field.sqx_next) \
== XSIMPLEQ_XOR(head, NULL)) \
(head)->sqx_last = \
XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
} while (0)
/*
* Tail queue definitions.
*/
#define TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; /* first element */ \
struct type **tqh_last; /* addr of last next element */ \
}
#define TAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).tqh_first }
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
}
/*
* Tail queue access methods.
*/
#define TAILQ_FIRST(head) ((head)->tqh_first)
#define TAILQ_END(head) NULL
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define TAILQ_LAST(head, headname) \
(*(((struct headname *)((head)->tqh_last))->tqh_last))
/* XXX */
#define TAILQ_PREV(elm, headname, field) \
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
#define TAILQ_EMPTY(head) \
(TAILQ_FIRST(head) == TAILQ_END(head))
#define TAILQ_FOREACH(var, head, field) \
for((var) = TAILQ_FIRST(head); \
(var) != TAILQ_END(head); \
(var) = TAILQ_NEXT(var, field))
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = TAILQ_FIRST(head); \
(var) != TAILQ_END(head) && \
((tvar) = TAILQ_NEXT(var, field), 1); \
(var) = (tvar))
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
for((var) = TAILQ_LAST(head, headname); \
(var) != TAILQ_END(head); \
(var) = TAILQ_PREV(var, headname, field))
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
for ((var) = TAILQ_LAST(head, headname); \
(var) != TAILQ_END(head) && \
((tvar) = TAILQ_PREV(var, headname, field), 1); \
(var) = (tvar))
/*
* Tail queue functions.
*/
#define TAILQ_INIT(head) do { \
(head)->tqh_first = NULL; \
(head)->tqh_last = &(head)->tqh_first; \
} while (0)
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
(head)->tqh_first->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(head)->tqh_first = (elm); \
(elm)->field.tqe_prev = &(head)->tqh_first; \
} while (0)
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.tqe_next = NULL; \
(elm)->field.tqe_prev = (head)->tqh_last; \
*(head)->tqh_last = (elm); \
(head)->tqh_last = &(elm)->field.tqe_next; \
} while (0)
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
(elm)->field.tqe_next->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(listelm)->field.tqe_next = (elm); \
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
} while (0)
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
(elm)->field.tqe_next = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
} while (0)
#define TAILQ_REMOVE(head, elm, field) do { \
if (((elm)->field.tqe_next) != NULL) \
(elm)->field.tqe_next->field.tqe_prev = \
(elm)->field.tqe_prev; \
else \
(head)->tqh_last = (elm)->field.tqe_prev; \
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
_Q_INVALIDATE((elm)->field.tqe_prev); \
_Q_INVALIDATE((elm)->field.tqe_next); \
} while (0)
#define TAILQ_REPLACE(head, elm, elm2, field) do { \
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
(elm2)->field.tqe_next->field.tqe_prev = \
&(elm2)->field.tqe_next; \
else \
(head)->tqh_last = &(elm2)->field.tqe_next; \
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
*(elm2)->field.tqe_prev = (elm2); \
_Q_INVALIDATE((elm)->field.tqe_prev); \
_Q_INVALIDATE((elm)->field.tqe_next); \
} while (0)
#define TAILQ_CONCAT(head1, head2, field) do { \
if (!TAILQ_EMPTY(head2)) { \
*(head1)->tqh_last = (head2)->tqh_first; \
(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
(head1)->tqh_last = (head2)->tqh_last; \
TAILQ_INIT((head2)); \
} \
} while (0)
/*
* Singly-linked Tail queue declarations.
*/
#define STAILQ_HEAD(name, type) \
struct name { \
struct type *stqh_first; /* first element */ \
struct type **stqh_last; /* addr of last next element */ \
}
#define STAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).stqh_first }
#define STAILQ_ENTRY(type) \
struct { \
struct type *stqe_next; /* next element */ \
}
/*
* Singly-linked Tail queue access methods.
*/
#define STAILQ_FIRST(head) ((head)->stqh_first)
#define STAILQ_END(head) NULL
#define STAILQ_EMPTY(head) (STAILQ_FIRST(head) == STAILQ_END(head))
#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
#define STAILQ_FOREACH(var, head, field) \
for ((var) = STAILQ_FIRST(head); \
(var) != STAILQ_END(head); \
(var) = STAILQ_NEXT(var, field))
#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = STAILQ_FIRST(head); \
(var) && ((tvar) = STAILQ_NEXT(var, field), 1); \
(var) = (tvar))
/*
* Singly-linked Tail queue functions.
*/
#define STAILQ_INIT(head) do { \
STAILQ_FIRST((head)) = NULL; \
(head)->stqh_last = &STAILQ_FIRST((head)); \
} while (0)
#define STAILQ_INSERT_HEAD(head, elm, field) do { \
if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
STAILQ_FIRST((head)) = (elm); \
} while (0)
#define STAILQ_INSERT_TAIL(head, elm, field) do { \
STAILQ_NEXT((elm), field) = NULL; \
*(head)->stqh_last = (elm); \
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
} while (0)
#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((listelm), field)) == NULL)\
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
STAILQ_NEXT((listelm), field) = (elm); \
} while (0)
#define STAILQ_REMOVE_HEAD(head, field) do { \
if ((STAILQ_FIRST((head)) = \
STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
(head)->stqh_last = &STAILQ_FIRST((head)); \
} while (0)
#define STAILQ_REMOVE_AFTER(head, elm, field) do { \
if ((STAILQ_NEXT(elm, field) = \
STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
} while (0)
#define STAILQ_REMOVE(head, elm, type, field) do { \
if (STAILQ_FIRST((head)) == (elm)) { \
STAILQ_REMOVE_HEAD((head), field); \
} else { \
struct type *curelm = (head)->stqh_first; \
while (STAILQ_NEXT(curelm, field) != (elm)) \
curelm = STAILQ_NEXT(curelm, field); \
STAILQ_REMOVE_AFTER(head, curelm, field); \
} \
} while (0)
#define STAILQ_CONCAT(head1, head2) do { \
if (!STAILQ_EMPTY((head2))) { \
*(head1)->stqh_last = (head2)->stqh_first; \
(head1)->stqh_last = (head2)->stqh_last; \
STAILQ_INIT((head2)); \
} \
} while (0)
#define STAILQ_LAST(head, type, field) \
(STAILQ_EMPTY((head)) ? NULL : \
((struct type *)(void *) \
((char *)((head)->stqh_last) - offsetof(struct type, field))))
#endif /* !_SYS_QUEUE_H_ */
+38
View File
@@ -0,0 +1,38 @@
/* PFLINUX compat: <sys/refcnt.h> — refcount_t wrapper with OpenBSD API */
#ifndef _PFK_REFCNT_H
#define _PFK_REFCNT_H
#include <linux/refcount.h>
struct refcnt { refcount_t rc; };
#define REFCNT_INITIALIZER { .rc = REFCNT_INIT }
static inline void refcnt_init(struct refcnt *r, unsigned int v)
{
refcount_set(&r->rc, v ? (int)v : 1);
}
static inline unsigned int refcnt_value(const struct refcnt *r)
{
return refcount_read((refcount_t *)&r->rc);
}
static inline void refcnt_take(struct refcnt *r)
{
refcount_inc(&r->rc);
}
/* returns true if ref was valid and taken (OpenBSD refcnt_take_if) */
static inline bool refcnt_take_not0(struct refcnt *r)
{
return refcount_inc_not_zero(&r->rc);
}
static inline unsigned int refcnt_rele(struct refcnt *r)
{
return (unsigned int)refcount_dec_and_test(&r->rc) ?
refcnt_value(r) : refcnt_value(r) + 1;
}
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)
{
}
#endif
+46
View File
@@ -0,0 +1,46 @@
/* PFLINUX compat: <sys/socket.h> — BSD sockaddrs stay Linux-layout;
* sa_len fields are removed by the staging sed (see PORTING.md).
* Provides BSD sockaddr accessor macros + address family constants. */
#ifndef _PFK_SOCKET_H
#define _PFK_SOCKET_H
#include <linux/types.h>
struct pfk_sockaddr_union {
struct sockaddr_storage ss;
};
#define sintosa(x) ((struct sockaddr_in *)(void *)(x))
#define sin6tosa(x) ((struct sockaddr_in6 *)(void *)(x))
#define sa_tosa(x) ((struct sockaddr *)(void *)(x))
#define satosa(x) ((struct sockaddr *)(void *)(x))
#define satoxp(x) ((void *)(x)) /* unused; keep symbol */
#define SA_LEN(x) (((x)->sa_family == AF_INET) ? \
sizeof(struct sockaddr_in) : \
((x)->sa_family == AF_INET6) ? \
sizeof(struct sockaddr_in6) : 16)
#define PFK_SA(x) ((struct sockaddr *)(x))
#define PFK_INA(x) (&((struct sockaddr_in *)(x))->sin_addr)
#define PFK_SA6(x) ((struct sockaddr_in6 *)(x))
/* BSD sin_port/sin_addr == Linux: both network byte order fields */
#ifndef SIN_IS_PRIVATE
#define SIN_IS_PRIVATE 1
#endif
/* AF_INET6 == 10 on both sides */
#ifndef AF_LINK
#define AF_LINK 18 /* OpenBSD value; used by pf for link sa */
#endif
static inline int pfk_sa_cmp(const struct sockaddr *a, const struct sockaddr *b)
{
if (a->sa_family != b->sa_family)
return 1;
switch (a->sa_family) {
case AF_INET:
return memcmp(sintosa(a), sintosa(b), sizeof(struct sockaddr_in));
case AF_INET6:
return memcmp(sin6tosa(a), sin6tosa(b), sizeof(struct sockaddr_in6));
}
return 1;
}
#endif
+9
View File
@@ -0,0 +1,9 @@
/* PFLINUX compat: <sys/syslimits.h> */
#ifndef _PFK_SYSLIMITS_H
#define _PFK_SYSLIMITS_H
#include <linux/limits.h>
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif
#define MAXSYMLINKS 8
#endif
+17
View File
@@ -0,0 +1,17 @@
/* PFLINUX compat: <sys/timeout.h> */
#ifndef _PFK_TIMEOUT_H
#define _PFK_TIMEOUT_H
#include <linux/timer.h>
#include <linux/workqueue.h>
typedef void (timeout_fn)(void *);
struct timeout {
struct timer_list timer;
struct work_struct work;
timeout_fn *fn;
void *arg;
unsigned long deadline_jif;
int period_ms;
int pending;
int running;
};
#endif
File diff suppressed because it is too large. Load diff
+6
View File
@@ -0,0 +1,6 @@
/* PFLINUX compat: <sys/types.h> */
#ifndef _PFK_SYS_TYPES_H
#define _PFK_SYS_TYPES_H
#include <linux/types.h>
typedef unsigned long u_long;
#endif
File diff suppressed because it is too large. Load diff
+689
View File
@@ -0,0 +1,689 @@
/* $OpenBSD: pfvar_priv.h,v 1.43 2026/09/10 12:28:04 deraadt Exp $ */
/*
* Copyright (c) 2001 Daniel Hartmeier
* Copyright (c) 2002 - 2013 Henning Brauer <henning@openbsd.org>
* Copyright (c) 2016 Alexander Bluhm <bluhm@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 _NET_PFVAR_PRIV_H_
#define _NET_PFVAR_PRIV_H_
#ifdef _KERNEL
#include <sys/rwlock.h>
#include <sys/mutex.h>
#include <sys/pclock.h>
#include <sys/percpu.h>
/*
* Locks used to protect struct members in this file:
* L pf_inp_mtx link pf to inp mutex
*/
struct pfsync_deferral;
struct kstat;
/*
* PF state links
*
* This is used to augment a struct pf_state so it can be
* tracked/referenced by the state and source address limiter things.
* Each limiter maintains a list of the states they "own", and these
* state links are what the limiters use to wire a state into their
* lists.
*
* Without PF state links, the pf_state struct would have to grow
* a lot to support a feature that may not be used.
*
* pfl_entry is used by the pools to add states to their list.
* pfl_state allows the pools to get from their list of states to
* the states themselves.
*
* pfl_link allows operations on states (well, delete) to be able
* to quickly locate the pf_state_link struct so they can be unwired
* from the pools.
*/
#define PF_STATE_LINK_TYPE_STATELIM 1
#define PF_STATE_LINK_TYPE_SOURCELIM 2
struct pf_state_link {
/* used by source/state pools to get to states */
TAILQ_ENTRY(pf_state_link) pfl_link;
/* used by pf_state to get to source/state pools */
SLIST_ENTRY(pf_state_link) pfl_linkage;
struct pf_state *pfl_state;
unsigned int pfl_type;
};
TAILQ_HEAD(pf_state_link_list, pf_state_link);
SLIST_HEAD(pf_state_linkage, pf_state_link);
/*
* pf state items - links from pf_state_key to pf_states
*/
struct pf_state_item {
TAILQ_ENTRY(pf_state_item)
si_entry;
struct pf_state *si_st;
};
TAILQ_HEAD(pf_statelisthead, pf_state_item);
/*
* pf state keys - look up states by address
*/
struct pf_state_key {
struct pf_addr addr[2];
u_int16_t port[2];
u_int16_t rdomain;
u_int16_t hash;
sa_family_t af;
u_int8_t proto;
RBT_ENTRY(pf_state_key) sk_entry;
struct pf_statelisthead sk_states;
struct pf_state_key *sk_reverse;
struct inpcb *sk_inp; /* [L] */
pf_refcnt_t sk_refcnt;
u_int8_t sk_removed;
};
RBT_HEAD(pf_state_tree, pf_state_key);
RBT_PROTOTYPE(pf_state_tree, pf_state_key, sk_entry, pf_state_compare_key);
#define PF_REVERSED_KEY(key, family) \
((key[PF_SK_WIRE]->af != key[PF_SK_STACK]->af) && \
(key[PF_SK_WIRE]->af != (family)))
/*
* pf state
*
* Protection/ownership of pf_state members:
* I immutable after pf_state_insert()
* M pf_state mtx
* P PF_STATE_LOCK
* S pfsync
* L pf_state_list
* g pf_purge gc
*/
struct pf_state {
u_int64_t id; /* [I] */
u_int32_t creatorid; /* [I] */
u_int8_t direction; /* [I] */
u_int8_t pad[3];
TAILQ_ENTRY(pf_state) sync_list; /* [S] */
struct pfsync_deferral *sync_defer; /* [S] */
TAILQ_ENTRY(pf_state) entry_list; /* [L] */
SLIST_ENTRY(pf_state) gc_list; /* [g] */
RBT_ENTRY(pf_state) entry_id; /* [P] */
struct pf_state_peer src;
struct pf_state_peer dst;
struct pf_rule_slist match_rules; /* [I] */
union pf_rule_ptr rule; /* [I] */
union pf_rule_ptr anchor; /* [I] */
union pf_rule_ptr natrule; /* [I] */
struct pf_addr rt_addr; /* [I] */
struct pf_sn_head src_nodes; /* [I] */
struct pf_state_key *key[2]; /* [I] stack and wire */
struct pfi_kif *kif; /* [I] */
struct mutex mtx;
pf_refcnt_t refcnt;
u_int64_t packets[2];
u_int64_t bytes[2];
int32_t creation; /* [I] */
int32_t expire;
int32_t pfsync_time; /* [S] */
int rtableid[2]; /* [I] stack and wire */
u_int16_t qid; /* [I] */
u_int16_t pqid; /* [I] */
u_int16_t tag; /* [I] */
u_int16_t state_flags; /* [M] */
u_int8_t log; /* [I] */
u_int8_t timeout;
u_int8_t sync_state; /* [S] PFSYNC_S_x */
u_int8_t sync_updates; /* [S] */
u_int8_t min_ttl; /* [I] */
u_int8_t set_tos; /* [I] */
u_int8_t set_prio[2]; /* [I] */
u_int16_t max_mss; /* [I] */
u_int16_t if_index_in; /* [I] */
u_int16_t if_index_out; /* [I] */
u_int16_t delay; /* [I] */
u_int8_t rt; /* [I] */
uint8_t statelim;
uint8_t sourcelim;
struct pf_state_linkage linkage;
};
RBT_HEAD(pf_state_tree_id, pf_state);
RBT_PROTOTYPE(pf_state_tree_id, pf_state, entry_id, pf_state_compare_id);
extern struct pf_state_tree_id tree_id;
/*
* states are linked into a global list to support the following
* functionality:
*
* - garbage collection
* - pfsync bulk send operations
* - bulk state fetches via the DIOCGETSTATES ioctl
* - bulk state clearing via the DIOCCLRSTATES ioctl
*
* states are inserted into the global pf_state_list once it has also
* been successfully added to the various trees that make up the state
* table. states are only removed from the pf_state_list by the garbage
* collection process.
*
* the pf_state_list head and tail pointers (ie, the pfs_list TAILQ_HEAD
* structure) and the pointers between the entries on the pf_state_list
* are locked separately. at a high level, this allows for insertion
* of new states into the pf_state_list while other contexts (eg, the
* ioctls) are traversing the state items in the list. for garbage
* collection to remove items from the pf_state_list, it has to exclude
* both modifications to the list head and tail pointers, and traversal
* of the links between the states.
*
* the head and tail pointers are protected by a mutex. the pointers
* between states are protected by an rwlock.
*
* because insertions are only made to the end of the list, if we get
* a snapshot of the head and tail of the list and prevent modifications
* to the links between states, we can safely traverse between the
* head and tail entries. subsequent insertions can add entries after
* our view of the tail, but we don't look past our view.
*
* if both locks must be taken, the rwlock protecting the links between
* states is taken before the mutex protecting the head and tail
* pointer.
*
* insertion into the list follows this pattern:
*
* // serialise list head/tail modifications
* mtx_enter(&pf_state_list.pfs_mtx);
* TAILQ_INSERT_TAIL(&pf_state_list.pfs_list, state, entry_list);
* mtx_leave(&pf_state_list.pfs_mtx);
*
* traversal of the list:
*
* // lock against the gc removing an item from the list
* rw_enter_read(&pf_state_list.pfs_rwl);
*
* // get a snapshot view of the ends of the list
* mtx_enter(&pf_state_list.pfs_mtx);
* head = TAILQ_FIRST(&pf_state_list.pfs_list);
* tail = TAILQ_LAST(&pf_state_list.pfs_list, pf_state_queue);
* mtx_leave(&pf_state_list.pfs_mtx);
*
* state = NULL;
* next = head;
*
* while (state != tail) {
* state = next;
* next = TAILQ_NEXT(state, entry_list);
*
* // look at the state
* }
*
* rw_exit_read(&pf_state_list.pfs_rwl);
*
* removing an item from the list:
*
* // wait for iterators (readers) to get out
* rw_enter_write(&pf_state_list.pfs_rwl);
*
* // serialise list head/tail modifications
* mtx_enter(&pf_state_list.pfs_mtx);
* TAILQ_REMOVE(&pf_state_list.pfs_list, state, entry_list);
* mtx_leave(&pf_state_list.pfs_mtx);
*
* rw_exit_write(&pf_state_list.pfs_rwl);
*
* the lock ordering for pf_state_list locks and the rest of the pf
* locks are:
*
* 1. KERNEL_LOCK
* 2. NET_LOCK
* 3. pf_state_list.pfs_rwl
* 4. PF_LOCK
* 5. PF_STATE_LOCK
* 6. pf_state_list.pfs_mtx
*/
struct pf_state_list {
/* the list of states in the system */
struct pf_state_queue pfs_list;
/* serialise pfs_list head/tail access */
struct mutex pfs_mtx;
/* serialise access to pointers between pfs_list entries */
struct rwlock pfs_rwl;
};
#define PF_STATE_LIST_INITIALIZER(_pfs) { \
.pfs_list = TAILQ_HEAD_INITIALIZER(_pfs.pfs_list), \
.pfs_mtx = MUTEX_INITIALIZER(IPL_SOFTNET), \
.pfs_rwl = RWLOCK_INITIALIZER("pfstates"), \
}
/*
* State limiter
*/
struct pf_statelim {
RBT_ENTRY(pf_statelim) pfstlim_id_tree;
RBT_ENTRY(pf_statelim) pfstlim_nm_tree;
TAILQ_ENTRY(pf_statelim) pfstlim_list;
struct kstat *pfstlim_ks;
uint32_t pfstlim_id;
char pfstlim_nm[PF_STATELIM_NAME_LEN];
/* config */
unsigned int pfstlim_limit;
struct {
unsigned int limit;
unsigned int seconds;
} pfstlim_rate;
/* run state */
struct pc_lock pfstlim_lock;
/* rate limiter */
uint64_t pfstlim_rate_ts;
uint64_t pfstlim_rate_token;
uint64_t pfstlim_rate_bucket;
unsigned int pfstlim_inuse;
struct pf_state_link_list pfstlim_states;
/* counters */
struct {
uint64_t admitted;
uint64_t hardlimited;
uint64_t ratelimited;
} pfstlim_counters;
struct {
time_t created;
time_t updated;
time_t cleared;
} pfstlim_timestamps;
};
RBT_HEAD(pf_statelim_id_tree, pf_statelim);
RBT_PROTOTYPE(pf_statelim_id_tree, pf_statelim, pfstlim_id_tree, cmp);
RBT_HEAD(pf_statelim_nm_tree, pf_statelim);
RBT_PROTOTYPE(pf_statelim_nm_tree, pf_statelim, pfstlim_nm_tree, cmp);
TAILQ_HEAD(pf_statelim_list, pf_statelim);
extern struct pf_statelim_id_tree pf_statelim_id_tree_active;
extern struct pf_statelim_list pf_statelim_list_active;
extern struct pf_statelim_id_tree pf_statelim_id_tree_inactive;
extern struct pf_statelim_nm_tree pf_statelim_nm_tree_inactive;
extern struct pf_statelim_list pf_statelim_list_inactive;
static inline unsigned int
pf_statelim_enter(struct pf_statelim *pfstlim)
{
return (pc_sprod_enter(&pfstlim->pfstlim_lock));
}
static inline void
pf_statelim_leave(struct pf_statelim *pfstlim, unsigned int gen)
{
pc_sprod_leave(&pfstlim->pfstlim_lock, gen);
}
/*
* Source address pools
*/
struct pf_sourcelim;
struct pf_source {
RBT_ENTRY(pf_source) pfsr_tree;
RBT_ENTRY(pf_source) pfsr_ioc_tree;
struct pf_sourcelim *pfsr_parent;
sa_family_t pfsr_af;
u_int16_t pfsr_rdomain;
struct pf_addr pfsr_addr;
/* run state */
unsigned int pfsr_inuse;
unsigned int pfsr_intable;
struct pf_state_link_list pfsr_states;
time_t pfsr_empty_ts;
TAILQ_ENTRY(pf_source) pfsr_empty_gc;
/* rate limiter */
uint64_t pfsr_rate_ts;
struct {
uint64_t admitted;
uint64_t hardlimited;
uint64_t ratelimited;
} pfsr_counters;
};
RBT_HEAD(pf_source_tree, pf_source);
RBT_PROTOTYPE(pf_source_tree, pf_source, pfsr_tree, cmp);
RBT_HEAD(pf_source_ioc_tree, pf_source);
RBT_PROTOTYPE(pf_source_ioc_tree, pf_source, pfsr_ioc_tree, cmp);
TAILQ_HEAD(pf_source_list, pf_source);
struct pf_sourcelim {
RBT_ENTRY(pf_sourcelim) pfsrlim_id_tree;
RBT_ENTRY(pf_sourcelim) pfsrlim_nm_tree;
TAILQ_ENTRY(pf_sourcelim) pfsrlim_list;
struct kstat *pfsrlim_ks;
uint32_t pfsrlim_id;
char pfsrlim_nm[PF_SOURCELIM_NAME_LEN];
unsigned int pfsrlim_disabled;
/* config */
unsigned int pfsrlim_entries;
unsigned int pfsrlim_limit;
unsigned int pfsrlim_ipv4_prefix;
unsigned int pfsrlim_ipv6_prefix;
struct {
unsigned int limit;
unsigned int seconds;
} pfsrlim_rate;
struct {
char name[PF_TABLE_NAME_SIZE];
unsigned int hwm;
unsigned int lwm;
struct pfr_ktable *table;
} pfsrlim_overload;
/* run state */
struct pc_lock pfsrlim_lock;
struct pf_addr pfsrlim_ipv4_mask;
struct pf_addr pfsrlim_ipv6_mask;
uint64_t pfsrlim_rate_token;
uint64_t pfsrlim_rate_bucket;
/* number of pf_sources */
unsigned int pfsrlim_nsources;
struct pf_source_tree pfsrlim_sources;
struct pf_source_ioc_tree pfsrlim_ioc_sources;
struct {
/* number of times pf_source was allocated */
uint64_t addrallocs;
/* state was rejected because the address limit was hit */
uint64_t addrlimited;
/* no memory to create address thing */
uint64_t addrnomem;
/* sum of pf_source inuse gauges */
uint64_t inuse;
/* sum of pf_source admitted counters */
uint64_t admitted;
/* sum of pf_source hardlimited counters */
uint64_t hardlimited;
/* sum of pf_source ratelimited counters */
uint64_t ratelimited;
} pfsrlim_counters;
};
RBT_HEAD(pf_sourcelim_id_tree, pf_sourcelim);
RBT_PROTOTYPE(pf_sourcelim_id_tree, pf_sourcelim, pfsrlim_id_tree, cmp);
RBT_HEAD(pf_sourcelim_nm_tree, pf_sourcelim);
RBT_PROTOTYPE(pf_sourcelim_nm_tree, pf_sourcelim, pfsrlim_nm_tree, cmp);
TAILQ_HEAD(pf_sourcelim_list, pf_sourcelim);
extern struct pf_sourcelim_id_tree pf_sourcelim_id_tree_active;
extern struct pf_sourcelim_list pf_sourcelim_list_active;
extern struct pf_sourcelim_id_tree pf_sourcelim_id_tree_inactive;
extern struct pf_sourcelim_nm_tree pf_sourcelim_nm_tree_inactive;
extern struct pf_sourcelim_list pf_sourcelim_list_inactive;
static inline unsigned int
pf_sourcelim_enter(struct pf_sourcelim *pfsrlim)
{
return (pc_sprod_enter(&pfsrlim->pfsrlim_lock));
}
static inline void
pf_sourcelim_leave(struct pf_sourcelim *pfsrlim, unsigned int gen)
{
pc_sprod_leave(&pfsrlim->pfsrlim_lock, gen);
}
/*
* pf internals
*/
extern struct rwlock pf_lock;
struct pf_pdesc {
struct {
int done;
uid_t uid;
gid_t gid;
pid_t pid;
} lookup;
u_int64_t tot_len; /* Make Mickey money */
struct pf_addr nsaddr; /* src address after NAT */
struct pf_addr ndaddr; /* dst address after NAT */
struct pfi_kif *kif; /* incoming interface */
struct mbuf *m; /* mbuf containing the packet */
struct pf_addr *src; /* src address */
struct pf_addr *dst; /* dst address */
u_int16_t *pcksum; /* proto cksum */
u_int16_t *sport;
u_int16_t *dport;
u_int16_t osport;
u_int16_t odport;
u_int16_t hash;
u_int16_t nsport; /* src port after NAT */
u_int16_t ndport; /* dst port after NAT */
u_int32_t off; /* protocol header offset */
u_int32_t hdrlen; /* protocol header length */
u_int32_t p_len; /* length of protocol payload */
u_int32_t extoff; /* extension header offset */
u_int32_t fragoff; /* fragment header offset */
u_int32_t jumbolen; /* length from v6 jumbo header */
u_int32_t badopts; /* v4 options or v6 routing headers */
#define PF_OPT_OTHER 0x0001
#define PF_OPT_JUMBO 0x0002
#define PF_OPT_ROUTER_ALERT 0x0004
u_int16_t rdomain; /* original routing domain */
u_int16_t virtual_proto;
#define PF_VPROTO_FRAGMENT 256
sa_family_t af;
sa_family_t naf;
u_int8_t proto;
u_int8_t tos;
u_int8_t ttl;
u_int8_t dir; /* direction */
u_int8_t sidx; /* key index for source */
u_int8_t didx; /* key index for destination */
u_int8_t destchg; /* flag set when destination changed */
u_int8_t pflog; /* flags for packet logging */
union {
struct tcphdr tcp;
struct udphdr udp;
struct icmp icmp;
#ifdef INET6
struct icmp6_hdr icmp6;
struct mld_hdr mld;
struct nd_neighbor_solicit nd_ns;
#endif /* INET6 */
} hdr;
};
struct pf_anchor_stackframe {
struct pf_ruleset *sf_rs;
struct pf_rule *sf_anchor;
union {
struct pf_rule *u_r;
struct pf_anchor_stackframe *u_stack_top;
} u;
struct pf_anchor *sf_child;
int sf_jump_target;
};
#define sf_r u.u_r
#define sf_stack_top u.u_stack_top
enum {
PF_NEXT_RULE,
PF_NEXT_CHILD
};
extern struct cpumem *pf_anchor_stack;
enum pf_trans_type {
PF_TRANS_NONE,
PF_TRANS_GETRULE,
PF_TRANS_MAX
};
struct pf_trans {
LIST_ENTRY(pf_trans) pft_entry;
uint32_t pft_unit; /* process id */
uint64_t pft_ticket;
enum pf_trans_type pft_type;
union {
struct {
u_int32_t gr_version;
struct pf_anchor *gr_anchor;
struct pf_rule *gr_rule;
} u_getrule;
} u;
};
#define pftgr_version u.u_getrule.gr_version
#define pftgr_anchor u.u_getrule.gr_anchor
#define pftgr_rule u.u_getrule.gr_rule
extern struct timeout pf_purge_states_to;
extern struct task pf_purge_task;
extern struct timeout pf_purge_to;
struct pf_state *pf_state_ref(struct pf_state *);
void pf_state_unref(struct pf_state *);
extern struct rwlock pf_lock;
extern struct rwlock pf_state_lock;
extern struct mutex pf_frag_mtx;
extern struct mutex pf_inp_mtx;
#define PF_LOCK() do { \
rw_enter_write(&pf_lock); \
} while (0)
#define PF_UNLOCK() do { \
PF_ASSERT_LOCKED(); \
rw_exit_write(&pf_lock); \
} while (0)
#define PF_ASSERT_LOCKED() do { \
if (rw_status(&pf_lock) != RW_WRITE) \
splassert_fail(RW_WRITE, \
rw_status(&pf_lock),__func__);\
} while (0)
#define PF_ASSERT_UNLOCKED() do { \
if (rw_status(&pf_lock) == RW_WRITE) \
splassert_fail(0, rw_status(&pf_lock), __func__);\
} while (0)
#define PF_STATE_ENTER_READ() do { \
rw_enter_read(&pf_state_lock); \
} while (0)
#define PF_STATE_EXIT_READ() do { \
rw_exit_read(&pf_state_lock); \
} while (0)
#define PF_STATE_ENTER_WRITE() do { \
rw_enter_write(&pf_state_lock); \
} while (0)
#define PF_STATE_EXIT_WRITE() do { \
PF_STATE_ASSERT_LOCKED(); \
rw_exit_write(&pf_state_lock); \
} while (0)
#define PF_STATE_ASSERT_LOCKED() do { \
if (rw_status(&pf_state_lock) != RW_WRITE)\
splassert_fail(RW_WRITE, \
rw_status(&pf_state_lock), __func__);\
} while (0)
#define PF_FRAG_LOCK() mtx_enter(&pf_frag_mtx)
#define PF_FRAG_UNLOCK() mtx_leave(&pf_frag_mtx)
/* for copies to/from network byte order */
void pf_state_peer_hton(const struct pf_state_peer *,
struct pfsync_state_peer *);
void pf_state_peer_ntoh(const struct pfsync_state_peer *,
struct pf_state_peer *);
u_int16_t pf_pkt_hash(sa_family_t, uint8_t,
const struct pf_addr *, const struct pf_addr *,
uint16_t, uint16_t);
void pf_status_init(void);
void pf_status_clear(void);
int pf_status_read(struct pf_status *);
#endif /* _KERNEL */
#endif /* _NET_PFVAR_PRIV_H_ */
+338
View File
@@ -0,0 +1,338 @@
/*
* PF-on-Linux: master compatibility header (pfk).
*
* Every ported OpenBSD file includes <pfk.h> INSTEAD of the original
* #include block. pfk.h pulls Linux headers first, then provides the
* OpenBSD-flavored types/APIs the PF code expects, so the ported logic
* stays line-proximate to upstream (ISC, (c) 2026 PF-on-Linux contributors;
* ported code: OpenBSD project, see LICENSE).
*/
#ifndef _PFK_H
#define _PFK_H
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/spinlock.h>
#include <linux/rwsem.h>
#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/jiffies.h>
#include <linux/ktime.h>
#include <linux/timekeeping.h>
#include <linux/random.h>
#include <linux/prandom.h>
#include <linux/percpu.h>
#include <linux/preempt.h>
#include <linux/cpumask.h>
#include <linux/net.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/if.h>
#include <linux/if_ether.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/icmp.h>
#include <linux/icmpv6.h>
#include <linux/icmpv6.h>
#include <linux/inetdevice.h>
#include <linux/if_addr.h>
#include <linux/neighbour.h>
#include <net/route.h>
#include <net/ipv6.h>
#include <net/ip6_route.h>
#include <net/ip.h>
#include <net/tcp.h>
#include <net/udp.h>
#include <net/icmp.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>
#include <net/dst.h>
#include <net/neighbour.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/workqueue.h>
#include <linux/timer.h>
#include <linux/highuid.h>
#include <linux/atomic.h>
#include <linux/hashtable.h>
#include <linux/jhash.h>
#include <linux/byteorder/generic.h>
#include <linux/ratelimit.h>
/* ------------------------------------------------------------ basic types */
#ifndef u_int8_t
typedef __u8 u_int8_t;
#endif
#ifndef u_int16_t
typedef __u16 u_int16_t;
#endif
#ifndef u_int32_t
typedef __u32 u_int32_t;
#endif
#ifndef u_int64_t
typedef __u64 u_int64_t;
#endif
#ifndef u64
typedef __u64 u64;
#endif
#ifndef u_int
typedef unsigned int u_int;
#endif
#ifndef u_char
typedef unsigned char u_char;
#endif
/* caddr_t/time_t/timeval: kernel provides caddr_t; BSD time types below */
#ifndef u_short
typedef unsigned short u_short;
#endif
typedef __be16 pf_in_port_t;
typedef __be32 pf_in_addr_t;
/* uapi linux/in6.h already provides s6_addr/s6_addr16/s6_addr32 macros
* matching OpenBSD usage on the same union layout. */
/* roundup/MIN/MAX/rounddown/NULL/PTR_ALIGN are already provided by the
* Linux kernel headers — use those. BSD-only extras kept under unique names. */
#define roundup2(x, y) (((x)+((y)-1))&(~((y)-1)))
#define howmany(x, y) (((x)+((y)-1))/(y))
#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))
#define PFK_PTR_ALIGN(p) ((caddr_t)(((unsigned long)(p) + 3) & ~3UL))
#define P2ALIGN(x, y) ((x) & -(y))
/* OpenBSD rwlock name; the kernel has its own debug `struct rwlock`, so the
* BSD-visible struct is pfk_rwlock + alias macro (no further kernel headers
* are included after pfk.h, so the alias cannot leak). */
struct pfk_rwlock {
struct rw_semaphore sem;
const char *name;
int is_writer;
atomic_t readers;
};
#define rwlock pfk_rwlock
#define rwlock_s pfk_rwlock
/* OpenBSD ioctl encoding: OUR kernel-side and our Linux pfctl use EXACTLY
* these macros so _IOR/_IOW direction bits stay bit-compatible with
* OpenBSD (Linux asm/ioctl.h differs). Ported TUs must include NO further
* kernel headers after pfk.h (see porting-notes). */
#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))
#define PFKIOC_TYPE(nr) (((nr) >> PFKXIOC_TYPESHIFT) & ((1U<<PFKXIOC_TYPEBITS)-1))
#define PFKIOC_NR(nr) (((nr) >> PFKXIOC_NRSHIFT) & ((1U<<PFKXIOC_NRBITS)-1))
#define PFKIOC_DIR(nr) (((nr) >> PFKXIOC_DIRSHIFT) & ((1U<<PFKXIOC_DIRBITS)-1))
/* OpenBSD endian names htobe64/htole64/beXXXtoh and ntoh/hton are already
* provided by linux/byteorder/generic.h — do NOT redefine (Werror). */
/* pfvar.h pulls <net/route.h> → resolves to the kernel's net/route.h;
* BSD-only bits it expects must be provided here. */
#define RTLABEL_LEN 32
/* syslog levels (BSD numbering == Linux numbering for these) */
#define LOG_EMERG 0
#define LOG_ALERT 1
#define LOG_CRIT 2
#define LOG_ERR 3
#define LOG_WARNING 4
#define LOG_NOTICE 5
#define LOG_INFO 6
#define LOG_DEBUG 7
void pfk_log(int level, const char *fmt, ...);
#define log pfk_log
#define printf pfk_printk_compat
int pfk_printk_compat(const char *fmt, ...);
#define panic(x...) do { pr_emerg("pfk panic: " x); BUG(); } while (0)
/* time */
typedef long time_t;
struct timeval { long tv_sec; long tv_usec; }; /* BSD layout (uapi may clash) */
static inline __u64 pfk_time_sec(void) { return ktime_get_seconds(); }
static inline __u64 pfk_time_ms(void) { return ktime_get_ns() / 1000000ULL; }
struct pfk_timeval { __s64 tv_sec; __s64 tv_usec; };
static inline void pfk_getmicrotime(struct pfk_timeval *tv)
{
struct timespec64 ts;
ktime_get_real_ts64(&ts);
tv->tv_sec = ts.tv_sec;
tv->tv_usec = ts.tv_nsec / 1000;
}
static inline __s64 pfk_ntptime_delta(void) { return 0; } /* PFLINUX: ntpd adj not modeled */
/* MALLOC / pool */
#define M_TEMP 0x10000
#define M_PF 0x10001
#define M_DEVBUF 0x10002
#define M_IFADDR 0x10003
#define M_PFX 0x10004
#define M_TAG 0x10005
#define M_PFADDR 0x10006
#define M_PFRWP 0x10007
#define M_PFTOC 0x10008
#define M_PFRTABLE 0x10009
#define M_IFMADDR 0x1000a
#define M_PFXSRC 0x1000b
#define M_ZOMBIE 0x1000c
#define M_PCR 0x1000d
#define M_PRoute 0x1000e
#define M_NOWAIT 0x01
#define M_WAITOK 0x02
#define M_CANFAIL 0x04
#define M_ZERO 0x08
void *pfk_malloc(size_t sz, int flags);
void pfk_free(void *p);
#define malloc(sz, t, f) pfk_malloc((sz), (f))
#define free(p, t) pfk_free(p)
/*
* pools: struct pool backed by kmem_cache when size fixed (pool_init),
* lazily created on first get for simplicity of many small pools.
*/
struct pool {
struct kmem_cache *slab;
size_t p_size;
int p_flags; /* PR_WAITOK semantics via M_* on init */
unsigned long p_alloc;
unsigned long p_used;
unsigned long p_hardlimit;
struct list_head p_objects; /* for zeroed reuse semantics if needed */
};
#define PR_WAITOK 0x01
#define PR_NOWAIT 0x02
#define PR_ZERO 0x08
struct srcptr_static { const char *file; int line; };
#define SRCPOINT() (&(struct srcptr_static){__FILE__, __LINE__})
int pool_init(struct pool *pl, size_t size, int align, int ipl,
const char *use, void *p, int flags,
void (*ih)(void *, void *), void *arg, struct srcptr_static *sp);
void pool_destroy(struct pool *pl);
void *pool_get(struct pool *pl, int flags);
void pool_put(struct pool *pl, void *v);
void pool_sethardlimit(struct pool *pl, unsigned long lim, const char *msg, int msglevel);
/* timeout.h: OpenBSD embeds `struct timeout` in statics/globals → fixed layout */
#include "compat/sys/timeout.h"
void timeout_set(struct timeout *to, timeout_fn *fn, void *arg);
void timeout_set_micro(struct timeout *to, timeout_fn *fn, void *arg);
int timeout_add(struct timeout *to, long ms);
int timeout_add_sec(struct timeout *to, int secs);
int timeout_add_usec(struct timeout *to, int us);
int timeout_del(struct timeout *to);
void timeout_barrier(struct timeout *to, int flags);
void timeout_del_sync(struct timeout *to);
int timeout_pending(struct timeout *to);
int tsleep(void *p, int pr, const char *w, int timo); /* PFLINUX: control-path only */
#define wakeup pfk_wakeup
void pfk_wakeup(void *p);
/* refcnt.h */
#include "compat/sys/refcnt.h"
/* rnd / random */
static inline u_int32_t arc4random(void) { return get_random_u32(); }
static inline void arc4random_buf(void *b, size_t l) { get_random_bytes(b, l); }
static inline u_int32_t arc4random_uniform(u_int32_t n) { return get_random_u32_below(n); }
static inline u_int32_t rnd(void) { return get_random_u32(); }
static inline u_int32_t random(void) { return get_random_u32(); }
#define karc4random() get_random_u32()
#define karc4random_uniform(n) get_random_u32_below(n)
/* copyin/copyout */
#define copyin(u, k, l) (copy_from_user((k), (u), (l)) ? EFAULT : 0)
#define copyout(k, u, l) (copy_to_user((u), (k), (l)) ? EFAULT : 0)
/* assert-ish */
#define KASSERT(cond) WARN_ON(!(cond))
#define __arraycount(a) (sizeof(a) / sizeof((a)[0]))
#define __predictfalse(x) unlikely(x)
#define __predicttrue(x) likely(x)
#define __predict(x, y) ((y) ? likely(x) : unlikely(x))
#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
/* NET_LOCK: OpenBSD global network rwlock. PF data path runs under NET_READ;
* ioctls take NET_LOCK (write). We map to a global rw_semaphore. */
void pfk_net_lock_init(void);
void pfk_net_lock(void); /* write */
void pfk_net_unlock(void);
void pfk_net_read_enter(void); /* read */
void pfk_net_read_release(void);
#define NET_LOCK() pfk_net_lock()
#define NET_UNLOCK() pfk_net_unlock()
#define NET_READ_ENTER() pfk_net_read_enter()
#define NET_READ_RELEASE() pfk_net_read_release()
void rw_init(struct rwlock_s *l, const char *name);
void rw_enter(struct rwlock_s *l, int mode);
void rw_exit(struct rwlock_s *l);
int rw_status(struct rwlock_s *l);
#define RW_READER 1
#define RW_WRITER 2
#define RW_NAPUTS 0
#define rw_assert(l, m) do { } while (0) /* PFLINUX: advisory */
/* BSD queue.h/tree.h/staged helpers — the Linux kernel's list.h/rbtree.h
* define a few identically-named macros; ported TUs never use Linux's
* list APIs via those names, so we clear and override. */
#undef LIST_HEAD
#undef LIST_HEAD_INIT
#undef RB_ROOT
#undef RB_CLEAR_NODE
#include "compat/sys/queue.h"
#include "compat/sys/tree.h"
#include "compat/sys/socket.h"
#include "compat/net/if_types.h"
#include "compat/net/if_var.h"
/* mbuf + BSD wire structs + pf ifnet registry */
#include "pfk_mbuf.h"
#include "pfk_net.h"
#include "pfk_if.h"
/* OpenBSD digest + toeplitz helpers (vendored into compat/crypto, compat/net) */
#include "compat/net/toeplitz.h"
#include "compat/crypto/siphash.h"
/* pf headers resolve via -I module/include */
#define NPFSYNC 0
#define NPFLOG 1
#define NPFLOW 0
#define NCARP 0
#define NPF 1
void pfk_mod_init_done(void);
#endif /* _PFK_H */
+13
View File
@@ -0,0 +1,13 @@
/* PFLINUX internal API (module-internal prototypes) */
#ifndef _PFK_API_H
#define _PFK_API_H
#include <pfk.h>
#include <net/pfvar.h>
long pfk_ioctl(struct file *f, unsigned int cmd, unsigned long addr);
int pfk_enable(int on);
void pfk_status(struct pf_status *s);
int pfk_hooks_register(void);
void pfk_hooks_unregister(void);
#endif
+102
View File
@@ -0,0 +1,102 @@
/*
* PF-on-Linux: ifnet/address registry — OpenBSD `struct ifnet`/`in_ifaddr`
* views over Linux net_device + in_ifaddr lists (ISC 2026).
*/
#ifndef _PFK_IF_H
#define _PFK_IF_H
struct pfk_ifaddr {
struct pfk_ifaddr *ifa_next;
struct pfk_ifnet *ifa_ifp;
int ifa_refcnt;
int ifa_flags; /* BSD IFA_* */
struct sockaddr_storage ifa_addr_storage; /* BSD-ish sockaddr */
struct sockaddr *ifa_addr; /* -> storage */
struct sockaddr *ifa_mask;
struct net_device *dev;
__be32 addr4;
struct in6_addr addr6;
int family; /* AF_INET/AF_INET6 */
int scope; /* link/site for v6 */
};
struct pfk_ifqueue; /* M5 */
struct pfk_ifnet {
char if_xname[IFNAMSIZ];
int if_index;
struct net_device *dev;
int if_flags; /* BSD-mapped IFF_* */
int if_xflags; /* BSD IFFX_* */
int if_type; /* IFT_* */
int if_mtu;
void *if_softc;
struct pfk_ifaddr *if_addrlist; /* refreshed snapshot */
struct pf_kif *if_pf_kif; /* pf_if glue */
struct pfk_ifqueue *if_snd; /* M5 */
void *if_pf_rtable; /* unused (rt tables NG) */
int if_rdomain; /* always 0 (NG), kept for ABI */
struct rcu_head rcu;
struct list_head link;
int alive;
};
#define if_output pfk_if_output_placeholder
struct pfk_ifnet *pfk_ifp_byindex(int idx); /* NULL if gone */
struct pfk_ifnet *pfk_ifp_byname(const char *name);
struct pfk_ifnet *pfk_ifp_bydev(struct net_device *dev);
int pfk_if_addrlist_refresh(struct pfk_ifnet *ifp); /* NET_LOCK held */
void pfk_if_free(struct pfk_ifnet *ifp);
/* BSD IFF_* bits (OpenBSD values) — distinct from Linux! */
#define PFIFF_UP 0x00000001
#define PFIFF_BROADCAST 0x00000002
#define PFIFF_DEBUG 0x00000004
#define PFIFF_LOOPBACK 0x00000008
#define PFIFF_POINTOPOINT 0x00000010
#define PFIFF_RUNNING 0x00000040
#define PFIFF_PROMISC 0x00000100
#define PFIFF_ALLMULTI 0x00000200
#define PFIFF_MULTICAST 0x00008000
#define PFIFF_RESOLVE 0x02000000
#define PFIFF_IPV4 0x10000000
#define PFIFF_IPV6 0x20000000
/* NOTE: BSD IFF_* bit values are PFIFF_*; staged port files are sed'ed
* IFF_* → PFIFF_* (IFF_ macros must NEVER alias linux netdev flags).
* BSD IFA_* */
#define IFA_BROADCAST_ 0x02
#define IFA_PRIMARY_ 0x04
#define IFA_SECONDARY_ 0x08
struct mbuf_list { struct mbuf *ml_first, *ml_last; int ml_len; };
/* egress interface (default-route dev), maintained on fib/net events */
struct pfk_ifnet *pfk_egress_if(void);
void pfk_egress_refresh(void);
int pfk_if_init(void);
void pfk_if_exit(void);
void pfk_if_refresh(struct pfk_ifnet *ifp);
struct pfk_ifnet *pfk_if_first(void);
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);
/* xmit API used by pf_route/pf output (implemented in pfk_xmit.c) */
int if_output_tso(struct pfk_ifnet *ifp, struct mbuf **mp,
struct sockaddr *dst, void *rt, int mtu);
int if_output_ml(struct pfk_ifnet *ifp, struct mbuf_list *ml,
struct sockaddr *dst, void *rt);
int if_enqueue(struct pfk_ifnet *ifp, struct mbuf *m);
/* addr lookups (ifa_ifwithaddr, in_ifwithaddr etc.) */
struct pfk_ifaddr *pfk_ifwithaddr(const struct sockaddr *sa);
struct pfk_ifaddr *pfk_ifwithroute(int family, const struct sockaddr *dst,
const struct sockaddr *gate, struct pfk_ifnet **ifp_out);
/* mib-ish status for pfctl DIOCGIFSTATS */
struct pfk_if_data { unsigned long long ifi_ibytes, ifi_obytes; };
void pfk_if_stats(struct pfk_ifnet *ifp, struct pfk_if_data *out);
#endif /* _PFK_IF_H */
+166
View File
@@ -0,0 +1,166 @@
/*
* PF-on-Linux mbuf shim: OpenBSD `struct mbuf` over Linux `sk_buff`.
*
* Design (see spec §4.1): PF-facing mbufs are ALWAYS single-segment
* (linearized on ingress). `m_data`/`m_len` mirror `skb->data`/`skb->len`.
* `m_pkthdr` carries PF's per-packet metadata (pf.* bits, ph.* aliases).
* Chains (m_next) are used only by our own concatenation (m_cat → copy);
* upstream PF never builds multi-segment chains it would hand back to Linux.
*
* (c) 2026 PF-on-Linux contributors — ISC.
*/
#ifndef _PFK_MBUF_H
#define _PFK_MBUF_H
#include <linux/skbuff.h>
struct pfk_ifnet; /* forward (pfk_if.h) */
struct pf_state_key;
struct inpcb;
struct pf_tag;
struct m_tag {
struct m_tag *m_tag_next;
int m_tag_type;
u_int m_tag_len; /* opaque: byte count of data[] */
u_char m_tag_data[];
};
/* mbuf types (OpenBSD numbering) */
#define MT_FREE 0
#define MT_DATA 1
#define MT_HEADER 2
#define MT_SONAME 3
#define MT_SBUF 4
#define MT_ZOMBIE 5
#define MT_USER 6
#define MT_RTABLE 7
#define MT_IFADDR 8
#define MT_EXT 9
#define MT_RTMSG 10
#define MT_SOCKET 11
#define MT_PFDESC 12
#define MT_TAG 13
#define MT_OFDESC 14
#define MT_TAGV 10
#define MT_PFTOC TAG_PF
#define MT_PF TAG_PF
#define TAG_PF 0x50460000
#define TAG_PF_RR (TAG_PF + 1)
#define MT_PFTOC TAG_PF
struct pkthdr_pf {
u_int flags;
struct pf_state_key *statekey;
struct inpcb *inp;
u_int32_t qid;
int16_t prio;
u_int8_t delay;
u_int8_t routed;
u_int8_t dir;
u_int8_t reason;
u_int16_t ruleset;
struct pfk_tag *tag;
};
struct pkthdr {
int len;
struct pkthdr_pf pf;
u_int csum_flags;
void *ph_cookie;
u_int32_t ph_flowid;
u_int ph_ifidx;
u_int ph_rtableid;
struct pfk_timeval ph_timestamp;
struct pfk_ifnet *ifp;
struct pfk_ifnet *rcvif;
u_short ph; /* tso segs: unused, kept as pad */
u_int8_t af;
u_int16_t proto;
u_int hdr_len;
struct m_tag *tags;
int pkthdr_len; /* == len mirror */
};
#define M_EXT 0x0001
#define M_PKTHDR 0x0002
#define M_EOR 0x0004
#define M_RO 0x0010
#define M_BCAST 0x0020
#define M_MCAST 0x0040
#define M_LOOP 0x0080
#define M_SKIP_FIREWALL 0x0100
#define M_BPF_MCAST 0x0200
#define M_PROMISC 0x0400
#define M_READONLY 0x0800
#define M_CANTFIND (M_READONLY)
#define M_DONTFRAG 0x1000
struct mbuf {
struct mbuf *m_next;
struct mbuf *m_nextout;
u_short m_flags;
u_short m_len;
caddr_t m_data;
u_short m_type;
u_short m_maxsize;
int m_refcnt;
struct sk_buff *skb; /* canonical storage (linear) */
struct pkthdr m_pkthdr; /* valid iff M_PKTHDR */
};
#define MGET(m, f, t) do { (m) = m_get((f), (t)); } while (0)
#define MGETHDR(m, f, t) do { (m) = m_gethdr((f), (t)); } while (0)
#define mtod(m, t) ((t)((m)->m_data))
#define MTOU(m, t) ((t)((m)->m_data))
#define MFREE(m, n) ((n) = m_free((m)))
/* lifecycle */
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 *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);
struct mbuf *m_copym(struct mbuf *m, int off, int len, int wait);
struct mbuf *m_dup(struct mbuf *m, int wait); /* full dup + headroom */
#define m_copypacket(m, w) m_copym((m), 0, 0, (w))
void m_adj(struct mbuf *m, int dlen);
void m_trim(struct mbuf *m, int dlen);
int m_cat(struct mbuf *to, struct mbuf *from); /* copies+consumes from */
caddr_t m_copyback(struct mbuf *m, int off, int len, const void *copied);
void m_copydata(const struct mbuf *m, int off, int len, caddr_t dest);
caddr_t m_getptr(struct mbuf *m, int loc, int *off);
int m_clump(struct mbuf **m0p, int len, int *mp_off, int *mp_len, caddr_t *doff);
void m_free_ext(struct mbuf *m);
struct mbuf *m_free(struct mbuf *m); /* returns next */
void m_freem(struct mbuf *m);
struct mbuf *m_dtom(struct sk_buff *skb); /* reverse map via skb->cb */
static inline struct sk_buff *m_to_skb(struct mbuf *m) { return m->skb; }
/* linearize: PF ingress guarantee */
int pfk_mbuf_linearize(struct mbuf *m); /* pskb_expand if fragged */
void pfk_mbuf_refresh(struct mbuf *m); /* resync m_len/pkthdr from skb */
/* m_tag */
struct m_tag *m_tag_alloc(int type, int len, int wait, caddr_t *align_data);
void m_tag_free(struct m_tag *m);
void m_tag_prepend(struct mbuf *m, struct m_tag *tag);
void m_tag_prepend_packet(struct mbuf *m, struct m_tag *tag);
void m_tag_delete(struct mbuf *m, struct m_tag *tag);
struct m_tag *m_tag_find(struct mbuf *m, int type, struct m_tag *start);
struct m_tag *m_tag_copy(struct m_tag *tag, int how);
int m_tag_copy_chain(struct mbuf *to, struct mbuf *from, int how);
void m_tag_delete_chain(struct mbuf *m, struct m_tag *end);
struct m_tag *m_tag_first(struct mbuf *m);
struct m_tag *m_tag_next(struct mbuf *m, struct m_tag *tag);
void m_tag_attach(struct mbuf *m, struct m_tag *t); /* append order */
/* PF flowid/toeplitz ingress hook + sysctl-ish knobs */
int pfk_mbuf_init(void);
void pfk_mbuf_exit(void);
#endif /* _PFK_MBUF_H */
+190
View File
@@ -0,0 +1,190 @@
/*
* PF-on-Linux BSD wire-protocol structs + checksum helpers (ISC 2026).
* Field names match OpenBSD headers; staging seds rename `struct ip` etc.
* to the pf_* names defined here so they never collide with Linux uapi.
* Byte layouts below are the little-endian variants (x86/arm64); the
* bitfield orderings replicate OpenBSD netinet headers exactly.
*/
#ifndef _PFK_NET_H
#define _PFK_NET_H
/* ---- IPv4 header (OpenBSD <netinet/ip.h> layout, LE bitfield order) ---- */
struct pf_ip {
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 ip_hl:4, ip_v:4;
#else
__u8 ip_v:4, ip_hl:4;
#endif
__u8 ip_tos;
__be16 ip_len;
__be16 ip_id;
__be16 ip_off;
#define IP_RF 0x8000
#define IP_OFFLP 0x3fff
__u8 ip_ttl;
__u8 ip_p;
__be16 ip_sum;
struct in_addr ip_src, ip_dst;
};
#define IP_MAXPACKET 65535
#define IP_DEFAULT_TTL 64
#define IPV4_TTL 64
/* ---- TCP (OpenBSD <netinet/tcp.h> names) ---- */
struct pf_tcphdr {
__be16 th_sport, th_dport;
__be32 th_seq, th_ack;
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 th_x2:4, th_off:4;
#else
__u8 th_off:4, th_x2:4;
#endif
__u8 th_flags;
__be16 th_win;
__be16 th_sum;
__be16 th_urp;
};
#define TH_FIN 0x01
#define TH_SYN 0x02
#define TH_RST 0x04
#define TH_PUSH 0x08
#define TH_ACK 0x10
#define TH_URG 0x20
#define TH_ECE 0x40
#define TH_CWR 0x80
#define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
/* tcp_fsm.h states (same numeric as Linux TCP_* enum) */
#define TCPS_CLOSED 0
#define TCPS_LISTEN 1
#define TCPS_SYN_SENT 2
#define TCPS_SYN_RECEIVED 3
#define TCPS_ESTABLISHED 4
#define TCPS_CLOSE_WAIT 5
#define TCPS_FIN_WAIT_1 6
#define TCPS_CLOSING 7
#define TCPS_LAST_ACK 8
#define TCPS_FIN_WAIT_2 9
#define TCPS_TIME_WAIT 10
/* ---- UDP ---- */
struct pf_udphdr {
__be16 uh_sport, uh_dport;
__be16 uh_ulen;
__be16 uh_sum;
};
/* ---- ICMP (OpenBSD <netinet/ip_icmp.h> names/values) ---- */
struct pf_icmp {
__u8 icmp_type;
__u8 icmp_code;
__be16 icmp_cksum;
union {
__be16 ih_id[2]; /* id, seq */
struct { __u8 type, code; __be16 cksum; } ih_type_hdr;
__be32 ih_gwaddr;
struct ih_frags { __be16 ih_rrechk; __be16 ih_length; } ih_frag;
} icmp_hun;
#define icmp_id icmp_hun.ih_id[0]
#define icmp_seq icmp_hun.ih_id[1]
#define icmp_cksum_alt icmp_hun.ih_type_hdr.cksum
#define icmp_gwaddr icmp_hun.ih_gwaddr
union {
__s8 id_pad[8];
__be16 id_ipid[2];
struct pf_ip id_ip;
struct ih_pmtu { __be16 reserved; __be16 mtu; } ih_pmtu;
struct ih_rtradv { __u8 irt_num_addrs, irt_wpa;
__be16 lifetime; } ih_rtradv;
} icmp_data;
#define icmp_rip icmp_data.id_ip
#define icmp_ppsmtu icmp_data.ih_pmtu.mtu
};
#define ICMP_ECHOREPLY 0
#define ICMP_UNREACH 3
#define ICMP_UNREACH_NET 0
#define ICMP_UNREACH_HOST 1
#define ICMP_UNREACH_PROTOCOL 2
#define ICMP_UNREACH_PORT 3
#define ICMP_UNREACH_NEEDFRAG 4
#define ICMP_UNREACH_SRCFAIL 5
#define ICMP_UNREACH_NET_UNKNOWN 6
#define ICMP_UNREACH_HOST_UNKNOWN 7
#define ICMP_UNREACH_ISOLATED 8
#define ICMP_UNREACH_NET_PROHIB 9
#define ICMP_UNREACH_HOST_PROHIB 10
#define ICMP_UNREACH_TOSNET 11
#define ICMP_UNREACH_TOSHOST 12
#define ICMP_UNREACH_FILTER_PROHIB 13
#define ICMP_UNREACH_HOST_PRECEDENCE 14
#define ICMP_UNREACH_PRECEDENCE_CUTOFF 15
#define ICMP_SOURCEQUENCH 4
#define ICMP_REDIRECT 5
#define ICMP_ALTHOSTADDR 6
#define ICMP_ECHO 8
#define ICMP_ROUTERADVERT 9
#define ICMP_ROUTERSOLICIT 10
#define ICMP_TIMXCEED 11
#define ICMP_TIMXCEED_INTRANS 0
#define ICMP_TIMXCEED_REASS 1
#define ICMP_PARAMPROB 12
#define ICMP_TSTAMP 13
#define ICMP_TSTAMPREPLY 14
#define ICMP_INFO_REQUEST 15
#define ICMP_INFO_REPLY 16
#define ICMP_ADDRESS 17
#define ICMP_ADDRESSREPLY 18
#define ICMP_MAXTYPE 18
/* ---- IPv6 (OpenBSD <netinet/ip6.h> ctl-union + macros) ---- */
struct pf_ip6_hdr {
union {
struct ip6_hdrctl {
__u32 ip6_un1_flow;
__be16 ip6_un1_plen;
__u8 ip6_un1_nxt;
__u8 ip6_un1_hlim;
} ip6_un1;
} ip6_ctlun;
struct in6_addr ip6_src;
struct in6_addr ip6_dst;
};
#define ip6_flow ip6_ctlun.ip6_un1->ip6_un1_flow
#define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen
#define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt
#define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim
#define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim
/* NOTE ip6_flow above is a pointer trick in OpenBSD; PF reads/writes
* it via explicit helpers only — provide the plain accessor instead: */
#undef ip6_flow
#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow
#define IPV6_VERSION 0x60
#define IPV6_VERSION_MASK 0xf0
#define v_hl i_vhl
#define traffic_class i_tc1, i_tc2
/* flow macros (OpenBSD) */
static inline __u32 PF6_FLOWlabel(const struct pf_ip6_hdr *h)
{ return be32_to_cpu(h->ip6_flow) & 0x000fffff; }
static inline __u8 PF6_VERSION(const struct pf_ip6_hdr *h)
{ return be32_to_cpu(h->ip6_flow) >> 28; }
static inline __u8 PF6_CLASS(const struct pf_ip6_hdr *h)
{ return (be32_to_cpu(h->ip6_flow) >> 20) & 0xff; }
/* ---- ICMPv6: PF code's field names (icmp6_type/code/cksum/identifier/
* sequence/mtu) are provided by Linux's own <linux/icmpv6.h> struct
* icmp6hdr + its member macros; staging seds `struct icmp6_hdr` →
* `struct icmp6hdr` (see stage-port.sh). No private definition needed. */
/* BSD IPPROTO_ / IP levels (identical numerics to Linux; aliases kept) */
#define IPPROTO_MAX_ IPPROTO_MAX
/* ---- checksum helpers (ported in_cksum etc.) ---- */
u_int16_t pfk_in_cksum(const void *data, int len); /* one's complement sum */
__sum16 pfk_icmp6_cksum(const struct pf_ip6_hdr *ip6,
const struct icmp6hdr *ic6, int len);
void pfk_send_icmp_error(int family, struct sk_buff *skb_in, int type,
int code, __be32 info);
#endif /* _PFK_NET_H */
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+871
View File
@@ -0,0 +1,871 @@
/* PFLINUX port of OpenBSD 8.0 net/fq_codel.c — see docs/porting-notes.md */
#include <pfk.h>
/* #define FQCODEL_DEBUG 1 */
#ifdef FQCODEL_DEBUG
#define DPRINTF(x...) printf(x)
#else
#define DPRINTF(x...)
#endif
struct codel {
struct mbuf_list q;
unsigned int dropping:1; /* Dropping state */
unsigned int backlog:31; /* Number of bytes in the queue */
unsigned short drops; /* Free running counter of drops */
unsigned short ldrops; /* Value from the previous run */
int64_t start; /* The moment queue was above target */
int64_t next; /* Next interval */
int64_t delay; /* Delay incurred by the last packet */
};
struct codel_params {
int64_t target;
int64_t interval;
int64_t grace;
int quantum;
uint32_t *intervals;
};
void codel_initparams(struct codel_params *, unsigned int,
unsigned int, int);
void codel_freeparams(struct codel_params *);
void codel_enqueue(struct codel *, int64_t, struct mbuf *);
struct mbuf *codel_dequeue(struct codel *, struct codel_params *, int64_t,
struct mbuf_list *, uint64_t *, uint64_t *);
struct mbuf *codel_commit(struct codel *, struct mbuf *);
void codel_purge(struct codel *, struct mbuf_list *);
struct flow {
struct codel cd;
int active:1;
int deficit:31;
#ifdef FQCODEL_DEBUG
uint16_t id;
#endif
SIMPLEQ_ENTRY(flow) flowentry;
};
SIMPLEQ_HEAD(flowq, flow);
struct fqcodel {
struct flowq newq;
struct flowq oldq;
struct flow *flows;
unsigned int qlength;
struct ifnet *ifp;
struct codel_params cparams;
unsigned int nflows;
unsigned int qlimit;
int quantum;
unsigned int flags;
#define FQCF_FIXED_QUANTUM 0x1
/* stats */
struct fqcodel_pktcntr xmit_cnt;
struct fqcodel_pktcntr drop_cnt;
struct mbuf_list pending_drops;
};
unsigned int fqcodel_idx(unsigned int, const struct mbuf *);
void *fqcodel_alloc(unsigned int, void *);
void fqcodel_free(unsigned int, void *);
struct mbuf *fqcodel_if_enq(struct ifqueue *, struct mbuf *);
struct mbuf *fqcodel_if_deq_begin(struct ifqueue *, void **);
void fqcodel_if_deq_commit(struct ifqueue *, struct mbuf *, void *);
void fqcodel_if_purge(struct ifqueue *, struct mbuf_list *);
struct mbuf *fqcodel_enq(struct fqcodel *, struct mbuf *);
struct mbuf *fqcodel_deq_begin(struct fqcodel *, void **,
struct mbuf_list *);
void fqcodel_deq_commit(struct fqcodel *, struct mbuf *, void *);
void fqcodel_purge(struct fqcodel *, struct mbuf_list *);
/*
* ifqueue glue.
*/
static const struct ifq_ops fqcodel_ops = {
fqcodel_idx,
fqcodel_if_enq,
fqcodel_if_deq_begin,
fqcodel_if_deq_commit,
fqcodel_if_purge,
fqcodel_alloc,
fqcodel_free
};
const struct ifq_ops *const ifq_fqcodel_ops = &fqcodel_ops;
void *fqcodel_pf_alloc(struct ifnet *);
int fqcodel_pf_addqueue(void *, struct pf_queuespec *);
void fqcodel_pf_free(void *);
int fqcodel_pf_qstats(struct pf_queuespec *, void *, int *);
unsigned int fqcodel_pf_qlength(void *);
struct mbuf *fqcodel_pf_enqueue(void *, struct mbuf *);
struct mbuf *fqcodel_pf_deq_begin(void *, void **, struct mbuf_list *);
void fqcodel_pf_deq_commit(void *, struct mbuf *, void *);
void fqcodel_pf_purge(void *, struct mbuf_list *);
/*
* pf queue glue.
*/
static const struct pfq_ops fqcodel_pf_ops = {
fqcodel_pf_alloc,
fqcodel_pf_addqueue,
fqcodel_pf_free,
fqcodel_pf_qstats,
fqcodel_pf_qlength,
fqcodel_pf_enqueue,
fqcodel_pf_deq_begin,
fqcodel_pf_deq_commit,
fqcodel_pf_purge
};
const struct pfq_ops *const pfq_fqcodel_ops = &fqcodel_pf_ops;
/* Default aggregate queue depth */
static const unsigned int fqcodel_qlimit = 1024;
/*
* CoDel implementation
*/
/* Delay target, 5ms */
static const int64_t codel_target = 5000000;
/* First 399 "100 / sqrt(x)" intervals, ns precision */
static const uint32_t codel_intervals[] = {
100000000, 70710678, 57735027, 50000000, 44721360, 40824829, 37796447,
35355339, 33333333, 31622777, 30151134, 28867513, 27735010, 26726124,
25819889, 25000000, 24253563, 23570226, 22941573, 22360680, 21821789,
21320072, 20851441, 20412415, 20000000, 19611614, 19245009, 18898224,
18569534, 18257419, 17960530, 17677670, 17407766, 17149859, 16903085,
16666667, 16439899, 16222142, 16012815, 15811388, 15617376, 15430335,
15249857, 15075567, 14907120, 14744196, 14586499, 14433757, 14285714,
14142136, 14002801, 13867505, 13736056, 13608276, 13483997, 13363062,
13245324, 13130643, 13018891, 12909944, 12803688, 12700013, 12598816,
12500000, 12403473, 12309149, 12216944, 12126781, 12038585, 11952286,
11867817, 11785113, 11704115, 11624764, 11547005, 11470787, 11396058,
11322770, 11250879, 11180340, 11111111, 11043153, 10976426, 10910895,
10846523, 10783277, 10721125, 10660036, 10599979, 10540926, 10482848,
10425721, 10369517, 10314212, 10259784, 10206207, 10153462, 10101525,
10050378, 10000000, 9950372, 9901475, 9853293, 9805807, 9759001,
9712859, 9667365, 9622504, 9578263, 9534626, 9491580, 9449112,
9407209, 9365858, 9325048, 9284767, 9245003, 9205746, 9166985,
9128709, 9090909, 9053575, 9016696, 8980265, 8944272, 8908708,
8873565, 8838835, 8804509, 8770580, 8737041, 8703883, 8671100,
8638684, 8606630, 8574929, 8543577, 8512565, 8481889, 8451543,
8421519, 8391814, 8362420, 8333333, 8304548, 8276059, 8247861,
8219949, 8192319, 8164966, 8137885, 8111071, 8084521, 8058230,
8032193, 8006408, 7980869, 7955573, 7930516, 7905694, 7881104,
7856742, 7832604, 7808688, 7784989, 7761505, 7738232, 7715167,
7692308, 7669650, 7647191, 7624929, 7602859, 7580980, 7559289,
7537784, 7516460, 7495317, 7474351, 7453560, 7432941, 7412493,
7392213, 7372098, 7352146, 7332356, 7312724, 7293250, 7273930,
7254763, 7235746, 7216878, 7198158, 7179582, 7161149, 7142857,
7124705, 7106691, 7088812, 7071068, 7053456, 7035975, 7018624,
7001400, 6984303, 6967330, 6950480, 6933752, 6917145, 6900656,
6884284, 6868028, 6851887, 6835859, 6819943, 6804138, 6788442,
6772855, 6757374, 6741999, 6726728, 6711561, 6696495, 6681531,
6666667, 6651901, 6637233, 6622662, 6608186, 6593805, 6579517,
6565322, 6551218, 6537205, 6523281, 6509446, 6495698, 6482037,
6468462, 6454972, 6441566, 6428243, 6415003, 6401844, 6388766,
6375767, 6362848, 6350006, 6337243, 6324555, 6311944, 6299408,
6286946, 6274558, 6262243, 6250000, 6237829, 6225728, 6213698,
6201737, 6189845, 6178021, 6166264, 6154575, 6142951, 6131393,
6119901, 6108472, 6097108, 6085806, 6074567, 6063391, 6052275,
6041221, 6030227, 6019293, 6008418, 5997601, 5986843, 5976143,
5965500, 5954913, 5944383, 5933908, 5923489, 5913124, 5902813,
5892557, 5882353, 5872202, 5862104, 5852057, 5842062, 5832118,
5822225, 5812382, 5802589, 5792844, 5783149, 5773503, 5763904,
5754353, 5744850, 5735393, 5725983, 5716620, 5707301, 5698029,
5688801, 5679618, 5670480, 5661385, 5652334, 5643326, 5634362,
5625440, 5616560, 5607722, 5598925, 5590170, 5581456, 5572782,
5564149, 5555556, 5547002, 5538488, 5530013, 5521576, 5513178,
5504819, 5496497, 5488213, 5479966, 5471757, 5463584, 5455447,
5447347, 5439283, 5431254, 5423261, 5415304, 5407381, 5399492,
5391639, 5383819, 5376033, 5368281, 5360563, 5352877, 5345225,
5337605, 5330018, 5322463, 5314940, 5307449, 5299989, 5292561,
5285164, 5277798, 5270463, 5263158, 5255883, 5248639, 5241424,
5234239, 5227084, 5219958, 5212860, 5205792, 5198752, 5191741,
5184758, 5177804, 5170877, 5163978, 5157106, 5150262, 5143445,
5136655, 5129892, 5123155, 5116445, 5109761, 5103104, 5096472,
5089866, 5083286, 5076731, 5070201, 5063697, 5057217, 5050763,
5044333, 5037927, 5031546, 5025189, 5018856, 5012547, 5006262
};
void
codel_initparams(struct codel_params *cp, unsigned int target,
unsigned int interval, int quantum)
{
uint64_t mult;
unsigned int i;
/*
* Update observation intervals table according to the configured
* initial interval value.
*/
if (interval > codel_intervals[0]) {
/*
* Select either specified target or 5% of an interval
* (RFC 8289, section 4.3).
*/
cp->target = MAX(target, interval / 20);
cp->interval = interval;
/* The coefficient is scaled up by a 1000 */
mult = ((uint64_t)cp->interval * 1000) / codel_intervals[0];
/* Prepare table of intervals */
cp->intervals = mallocarray(nitems(codel_intervals),
sizeof(codel_intervals[0]), M_DEVBUF, M_WAITOK | M_ZERO);
for (i = 0; i < nitems(codel_intervals); i++)
cp->intervals[i] = ((uint64_t)codel_intervals[i] *
mult) / 1000;
} else {
cp->target = MAX(target, codel_target);
cp->interval = codel_intervals[0];
cp->intervals = (uint32_t *)codel_intervals;
}
cp->quantum = quantum;
/* Grace period for delta reuse (RFC 8289, section 5.5) */
cp->grace = 16 * cp->interval;
}
void
codel_freeparams(struct codel_params *cp)
{
if (cp->intervals != codel_intervals)
free(cp->intervals, M_DEVBUF, nitems(codel_intervals) *
sizeof(codel_intervals[0]));
cp->intervals = NULL;
}
static inline unsigned int
codel_backlog(struct codel *cd)
{
return (cd->backlog);
}
static inline unsigned int
codel_qlength(struct codel *cd)
{
return (ml_len(&cd->q));
}
static inline int64_t
codel_delay(struct codel *cd)
{
return (cd->delay);
}
void
codel_enqueue(struct codel *cd, int64_t now, struct mbuf *m)
{
m->m_pkthdr.ph_timestamp = now;
ml_enqueue(&cd->q, m);
cd->backlog += m->m_pkthdr.len;
}
/*
* Select the next interval according to the number of drops
* in the current one relative to the provided timestamp.
*/
static inline void
control_law(struct codel *cd, struct codel_params *cp, int64_t rts)
{
unsigned int idx;
KASSERT(cd->drops > 0);
idx = min(cd->drops - 1, nitems(codel_intervals) - 1);
cd->next = rts + cp->intervals[idx];
}
/*
* Pick the next enqueued packet and determine the queueing delay
* as well as whether or not it's a good candidate for dropping
* from the queue.
*
* The decision whether to drop the packet or not is made based
* on the queueing delay target of 5ms and on the current queue
* length in bytes which shouldn't be less than the amount of data
* that arrives in a typical interarrival time (MTU-sized packets
* arriving spaced by the amount of time it takes to send such a
* packet on the bottleneck).
*/
static inline struct mbuf *
codel_next_packet(struct codel *cd, struct codel_params *cp, int64_t now,
int *drop)
{
struct mbuf *m;
*drop = 0;
m = MBUF_LIST_FIRST(&cd->q);
if (m == NULL) {
KASSERT(cd->backlog == 0);
/* Empty queue, reset interval */
cd->start = 0;
return (NULL);
}
KASSERT(cd->backlog >= m->m_pkthdr.len);
if (now - m->m_pkthdr.ph_timestamp < cp->target ||
cd->backlog - m->m_pkthdr.len <= cp->quantum) {
/*
* The minimum delay decreased below the target, reset
* the current observation interval.
*/
cd->start = 0;
return (m);
}
if (cd->start == 0) {
/*
* This is the first packet to be delayed for more than
* the target, start the first observation interval after
* a single RTT and see if the minimum delay goes below
* the target within the interval, otherwise punish the
* next packet.
*/
cd->start = now + cp->interval;
} else if (now >= cd->start) {
*drop = 1;
}
return (m);
}
enum { INITIAL, ACCEPTING, FIRSTDROP, DROPPING, CONTROL, RECOVERY };
static inline int
codel_state_change(struct codel *cd, int64_t now, struct mbuf *m, int drop,
int state)
{
if (state == FIRSTDROP)
return (ACCEPTING);
if (cd->dropping) {
if (!drop)
return (RECOVERY);
else if (now >= cd->next)
return (state == DROPPING ? CONTROL : DROPPING);
} else if (drop)
return (FIRSTDROP);
if (m == NULL)
return (RECOVERY);
return (ACCEPTING);
}
struct mbuf *
codel_dequeue(struct codel *cd, struct codel_params *cp, int64_t now,
struct mbuf_list *free_ml, uint64_t *dpkts, uint64_t *dbytes)
{
struct mbuf *m;
unsigned short delta;
int drop, state, done = 0;
state = INITIAL;
while (!done) {
m = codel_next_packet(cd, cp, now, &drop);
state = codel_state_change(cd, now, m, drop, state);
switch (state) {
case FIRSTDROP:
m = codel_commit(cd, m);
ml_enqueue(free_ml, m);
*dpkts += 1;
*dbytes += m->m_pkthdr.len;
cd->dropping = 1;
/*
* If we're still within the grace period and not
* meeting our minimal delay target we treat this
* as a continuation of the previous observation
* interval and shrink it further. Otherwise we
* start from the initial one.
*/
delta = cd->drops - cd->ldrops;
if (delta > 1 && (now < cd->next ||
now - cd->next < cp->grace))
cd->drops = delta;
else
cd->drops = 1;
control_law(cd, cp, now);
cd->ldrops = cd->drops;
/* fetches the next packet and goes to ACCEPTING */
break;
case DROPPING:
m = codel_commit(cd, m);
ml_enqueue(free_ml, m);
cd->drops++;
*dpkts += 1;
*dbytes += m->m_pkthdr.len;
/* fetches the next packet and goes to CONTROL */
break;
case CONTROL:
if (drop) {
control_law(cd, cp, cd->next);
continue;
}
/* FALLTHROUGH */
case RECOVERY:
cd->dropping = 0;
/* FALLTHROUGH */
case ACCEPTING:
done = 1;
break;
}
}
if (m != NULL)
cd->delay = now - m->m_pkthdr.ph_timestamp;
return (m);
}
struct mbuf *
codel_commit(struct codel *cd, struct mbuf *m)
{
struct mbuf *n;
n = ml_dequeue(&cd->q);
if (m)
KASSERT(n == m);
KASSERT(n != NULL);
KASSERT(cd->backlog >= n->m_pkthdr.len);
cd->backlog -= n->m_pkthdr.len;
return (n);
}
void
codel_purge(struct codel *cd, struct mbuf_list *ml)
{
ml_enlist(ml, &cd->q);
cd->backlog = 0;
}
/*
* FQ-CoDel implementation
*/
static inline struct flow *
classify_flow(struct fqcodel *fqc, struct mbuf *m)
{
unsigned int index = 0;
if (m->m_pkthdr.csum_flags & M_FLOWID)
index = m->m_pkthdr.ph_flowid % fqc->nflows;
DPRINTF("%s: %u\n", __func__, index);
return (&fqc->flows[index]);
}
struct mbuf *
fqcodel_enq(struct fqcodel *fqc, struct mbuf *m)
{
struct flow *flow;
unsigned int backlog = 0;
int64_t now;
int i, ndrop;
flow = classify_flow(fqc, m);
if (flow == NULL)
return (m);
now = nsecuptime();
codel_enqueue(&flow->cd, now, m);
fqc->qlength++;
if (!flow->active) {
SIMPLEQ_INSERT_TAIL(&fqc->newq, flow, flowentry);
flow->deficit = fqc->quantum;
flow->active = 1;
DPRINTF("%s: flow %u active deficit %d\n", __func__,
flow->id, flow->deficit);
}
/*
* Flush pending_drops first to let PF account them individually.
* When batch dropping (below), we queue multiple packets but can
* only return one per enqueue call to maintain the interface
* contract.
*/
if (!ml_empty(&fqc->pending_drops))
return (ml_dequeue(&fqc->pending_drops));
/*
* If total queue length exceeds the limit, find the flow with the
* largest backlog and drop up to half of its packets, with a
* maximum of 64, from the head. Implements RFC 8290, section 4.1
* batch drop to handle overload efficiently. Dropped packets are
* queued in pending_drops.
*/
if (fqc->qlength > fqc->qlimit) {
for (i = 0; i < fqc->nflows; i++) {
if (codel_backlog(&fqc->flows[i].cd) > backlog) {
flow = &fqc->flows[i];
backlog = codel_backlog(&flow->cd);
}
}
KASSERT(flow != NULL);
ndrop = MIN(MAX(codel_qlength(&flow->cd) / 2, 1), 64);
for (i = 0; i < ndrop; i++) {
m = codel_commit(&flow->cd, NULL);
if (m == NULL)
break;
fqc->drop_cnt.packets++;
fqc->drop_cnt.bytes += m->m_pkthdr.len;
fqc->qlength--;
ml_enqueue(&fqc->pending_drops, m);
}
DPRINTF("%s: batch-dropped %d/%d pkts from flow %u\n", __func__,
i, ndrop, flow->id);
return (ml_dequeue(&fqc->pending_drops));
}
return (NULL);
}
static inline struct flowq *
select_queue(struct fqcodel *fqc)
{
struct flowq *fq = NULL;
if (!SIMPLEQ_EMPTY(&fqc->newq))
fq = &fqc->newq;
else if (!SIMPLEQ_EMPTY(&fqc->oldq))
fq = &fqc->oldq;
return (fq);
}
static inline struct flow *
first_flow(struct fqcodel *fqc, struct flowq **fq)
{
struct flow *flow;
while ((*fq = select_queue(fqc)) != NULL) {
while ((flow = SIMPLEQ_FIRST(*fq)) != NULL) {
if (flow->deficit <= 0) {
flow->deficit += fqc->quantum;
SIMPLEQ_REMOVE_HEAD(*fq, flowentry);
SIMPLEQ_INSERT_TAIL(&fqc->oldq, flow,
flowentry);
DPRINTF("%s: flow %u deficit %d\n", __func__,
flow->id, flow->deficit);
} else
return (flow);
}
}
return (NULL);
}
static inline struct flow *
next_flow(struct fqcodel *fqc, struct flow *flow, struct flowq **fq)
{
SIMPLEQ_REMOVE_HEAD(*fq, flowentry);
if (*fq == &fqc->newq && !SIMPLEQ_EMPTY(&fqc->oldq)) {
/* A packet was dropped, starve the queue */
SIMPLEQ_INSERT_TAIL(&fqc->oldq, flow, flowentry);
DPRINTF("%s: flow %u ->oldq deficit %d\n", __func__,
flow->id, flow->deficit);
} else {
/* A packet was dropped on a starved queue, disable it */
flow->active = 0;
DPRINTF("%s: flow %u inactive deficit %d\n", __func__,
flow->id, flow->deficit);
}
return (first_flow(fqc, fq));
}
struct mbuf *
fqcodel_deq_begin(struct fqcodel *fqc, void **cookiep,
struct mbuf_list *free_ml)
{
struct mbuf_list ml = MBUF_LIST_INITIALIZER();
struct flowq *fq;
struct flow *flow;
struct mbuf *m;
int64_t now;
if ((fqc->flags & FQCF_FIXED_QUANTUM) == 0)
fqc->quantum = fqc->ifp->if_mtu + max_linkhdr;
now = nsecuptime();
for (flow = first_flow(fqc, &fq); flow != NULL;
flow = next_flow(fqc, flow, &fq)) {
m = codel_dequeue(&flow->cd, &fqc->cparams, now, &ml,
&fqc->drop_cnt.packets, &fqc->drop_cnt.bytes);
KASSERT(fqc->qlength >= ml_len(&ml));
fqc->qlength -= ml_len(&ml);
ml_enlist(free_ml, &ml);
if (m != NULL) {
flow->deficit -= m->m_pkthdr.len;
DPRINTF("%s: flow %u deficit %d\n", __func__,
flow->id, flow->deficit);
*cookiep = flow;
return (m);
}
}
return (NULL);
}
void
fqcodel_deq_commit(struct fqcodel *fqc, struct mbuf *m, void *cookie)
{
struct flow *flow = cookie;
KASSERT(fqc->qlength > 0);
fqc->qlength--;
fqc->xmit_cnt.packets++;
fqc->xmit_cnt.bytes += m->m_pkthdr.len;
(void)codel_commit(&flow->cd, m);
}
void
fqcodel_purge(struct fqcodel *fqc, struct mbuf_list *ml)
{
unsigned int i;
for (i = 0; i < fqc->nflows; i++)
codel_purge(&fqc->flows[i].cd, ml);
ml_enlist(ml, &fqc->pending_drops);
fqc->qlength = 0;
}
struct mbuf *
fqcodel_if_enq(struct ifqueue *ifq, struct mbuf *m)
{
return fqcodel_enq(ifq->ifq_q, m);
}
struct mbuf *
fqcodel_if_deq_begin(struct ifqueue *ifq, void **cookiep)
{
struct mbuf_list free_ml = MBUF_LIST_INITIALIZER();
struct mbuf *m;
m = fqcodel_deq_begin(ifq->ifq_q, cookiep, &free_ml);
ifq_mfreeml(ifq, &free_ml);
return (m);
}
void
fqcodel_if_deq_commit(struct ifqueue *ifq, struct mbuf *m, void *cookie)
{
return fqcodel_deq_commit(ifq->ifq_q, m, cookie);
}
void
fqcodel_if_purge(struct ifqueue *ifq, struct mbuf_list *ml)
{
return fqcodel_purge(ifq->ifq_q, ml);
}
void *
fqcodel_pf_alloc(struct ifnet *ifp)
{
struct fqcodel *fqc;
fqc = malloc(sizeof(struct fqcodel), M_DEVBUF, M_WAITOK | M_ZERO);
SIMPLEQ_INIT(&fqc->newq);
SIMPLEQ_INIT(&fqc->oldq);
ml_init(&fqc->pending_drops);
return (fqc);
}
int
fqcodel_pf_addqueue(void *arg, struct pf_queuespec *qs)
{
struct ifnet *ifp = qs->kif->pfik_ifp;
struct fqcodel *fqc = arg;
if (qs->flowqueue.flows == 0 || qs->flowqueue.flows > 0xffff)
return (EINVAL);
fqc->nflows = qs->flowqueue.flows;
fqc->quantum = qs->flowqueue.quantum;
if (qs->qlimit > 0)
fqc->qlimit = qs->qlimit;
else
fqc->qlimit = fqcodel_qlimit;
if (fqc->quantum > 0)
fqc->flags |= FQCF_FIXED_QUANTUM;
else
fqc->quantum = ifp->if_mtu + max_linkhdr;
codel_initparams(&fqc->cparams, qs->flowqueue.target,
qs->flowqueue.interval, fqc->quantum);
fqc->flows = mallocarray(fqc->nflows, sizeof(struct flow),
M_DEVBUF, M_WAITOK | M_ZERO);
#ifdef FQCODEL_DEBUG
{
unsigned int i;
for (i = 0; i < fqc->nflows; i++)
fqc->flows[i].id = i;
}
#endif
fqc->ifp = ifp;
DPRINTF("fq-codel on %s: %d queues %d deep, quantum %d target %llums "
"interval %llums\n", ifp->if_xname, fqc->nflows, fqc->qlimit,
fqc->quantum, fqc->cparams.target / 1000000,
fqc->cparams.interval / 1000000);
return (0);
}
void
fqcodel_pf_free(void *arg)
{
struct fqcodel *fqc = arg;
ml_purge(&fqc->pending_drops);
codel_freeparams(&fqc->cparams);
free(fqc->flows, M_DEVBUF, fqc->nflows * sizeof(struct flow));
free(fqc, M_DEVBUF, sizeof(struct fqcodel));
}
int
fqcodel_pf_qstats(struct pf_queuespec *qs, void *ubuf, int *nbytes)
{
struct ifnet *ifp = qs->kif->pfik_ifp;
struct fqcodel_stats stats;
struct fqcodel *fqc;
int64_t delay;
unsigned int i;
int error = 0;
if (ifp == NULL)
return (EBADF);
if (*nbytes < sizeof(stats))
return (EINVAL);
memset(&stats, 0, sizeof(stats));
/* XXX: multi-q? */
fqc = ifq_q_enter(&ifp->if_snd, ifq_fqcodel_ops);
if (fqc == NULL)
return (EBADF);
stats.xmit_cnt = fqc->xmit_cnt;
stats.drop_cnt = fqc->drop_cnt;
stats.qlength = ifq_len(&ifp->if_snd);
stats.qlimit = fqc->qlimit;
stats.flows = 0;
stats.delaysum = stats.delaysumsq = 0;
for (i = 0; i < fqc->nflows; i++) {
if (codel_qlength(&fqc->flows[i].cd) == 0)
continue;
/* Scale down to microseconds to avoid overflows */
delay = codel_delay(&fqc->flows[i].cd) / 1000;
stats.delaysum += delay;
stats.delaysumsq += delay * delay;
stats.flows++;
}
ifq_q_leave(&ifp->if_snd, fqc);
if ((error = copyout((caddr_t)&stats, ubuf, sizeof(stats))) != 0)
return (error);
*nbytes = sizeof(stats);
return (0);
}
unsigned int
fqcodel_pf_qlength(void *fqc)
{
return ((struct fqcodel *)fqc)->qlength;
}
struct mbuf *
fqcodel_pf_enqueue(void *fqc, struct mbuf *m)
{
return fqcodel_enq(fqc, m);
}
struct mbuf *
fqcodel_pf_deq_begin(void *fqc, void **cookiep, struct mbuf_list *free_ml)
{
return fqcodel_deq_begin(fqc, cookiep, free_ml);
}
void
fqcodel_pf_deq_commit(void *fqc, struct mbuf *m, void *cookie)
{
return fqcodel_deq_commit(fqc, m, cookie);
}
void
fqcodel_pf_purge(void *fqc, struct mbuf_list *ml)
{
return fqcodel_purge(fqc, ml);
}
unsigned int
fqcodel_idx(unsigned int nqueues, const struct mbuf *m)
{
return (0);
}
void *
fqcodel_alloc(unsigned int idx, void *arg)
{
/* Allocation is done in fqcodel_pf_alloc */
return (arg);
}
void
fqcodel_free(unsigned int idx, void *arg)
{
fqcodel_pf_free(arg);
}
+1667
View File
File diff suppressed because it is too large. Load diff
+193
View File
@@ -0,0 +1,193 @@
/* PFLINUX port of OpenBSD 8.0 net/if_pflog.c — see docs/porting-notes.md */
#include <pfk.h>
#define PFLOGMTU (32768 + MHLEN + MLEN)
#ifdef PFLOGDEBUG
#define DPRINTF(x) do { if (pflogdebug) printf x ; } while (0)
#else
#define DPRINTF(x)
#endif
void pflogattach(int);
int pflogoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
struct rtentry *);
int pflogioctl(struct ifnet *, u_long, caddr_t);
int pflog_clone_create(struct if_clone *, int);
int pflog_clone_destroy(struct ifnet *);
struct pflog_softc *pflog_getif(int);
struct if_clone pflog_cloner =
IF_CLONE_INITIALIZER("pflog", pflog_clone_create, pflog_clone_destroy);
LIST_HEAD(, pflog_softc) pflog_ifs = LIST_HEAD_INITIALIZER(pflog_ifs);
void
pflogattach(int npflog)
{
if_clone_attach(&pflog_cloner);
}
int
pflog_clone_create(struct if_clone *ifc, int unit)
{
struct ifnet *ifp;
struct pflog_softc *pflogif;
pflogif = malloc(sizeof(*pflogif), M_DEVBUF, M_WAITOK|M_ZERO);
pflogif->sc_unit = unit;
ifp = &pflogif->sc_if;
snprintf(ifp->if_xname, sizeof ifp->if_xname, "pflog%d", unit);
ifp->if_softc = pflogif;
ifp->if_mtu = PFLOGMTU;
ifp->if_ioctl = pflogioctl;
ifp->if_output = pflogoutput;
ifp->if_xflags = IFXF_CLONED;
ifp->if_type = IFT_PFLOG;
ifp->if_hdrlen = PFLOG_HDRLEN;
if_attach(ifp);
if_alloc_sadl(ifp);
#if NBPFILTER > 0
bpfattach(&pflogif->sc_if.if_bpf, ifp, DLT_PFLOG, PFLOG_HDRLEN);
#endif
NET_LOCK();
LIST_INSERT_HEAD(&pflog_ifs, pflogif, sc_entry);
NET_UNLOCK();
return (0);
}
int
pflog_clone_destroy(struct ifnet *ifp)
{
struct pflog_softc *pflogif = ifp->if_softc;
NET_LOCK();
LIST_REMOVE(pflogif, sc_entry);
NET_UNLOCK();
if_detach(ifp);
free(pflogif, M_DEVBUF, sizeof(*pflogif));
return (0);
}
int
pflogoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
struct rtentry *rt)
{
m_freem(m); /* drop packet */
return (EAFNOSUPPORT);
}
int
pflogioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
switch (cmd) {
case SIOCSIFFLAGS:
if (ifp->if_flags & PFIFF_UP)
ifp->if_flags |= PFIFF_RUNNING;
else
ifp->if_flags &= ~PFIFF_RUNNING;
break;
default:
return (ENOTTY);
}
return (0);
}
struct pflog_softc *
pflog_getif(int unit)
{
struct pflog_softc *pflogif;
NET_ASSERT_LOCKED();
LIST_FOREACH(pflogif, &pflog_ifs, sc_entry) {
if (pflogif->sc_unit == unit)
break;
}
return pflogif;
}
int
pflog_packet(struct pf_pdesc *pd, u_int8_t reason, struct pf_rule *rm,
struct pf_rule *am, struct pf_ruleset *ruleset, struct pf_rule *trigger)
{
#if NBPFILTER > 0
struct pflog_softc *pflogif;
struct ifnet *ifn;
caddr_t if_bpf;
struct pfloghdr hdr;
if (rm == NULL || pd == NULL || pd->kif == NULL || pd->m == NULL)
return (-1);
if (trigger == NULL)
trigger = rm;
pflogif = pflog_getif(trigger->logif);
if (pflogif == NULL)
return (0);
ifn = &pflogif->sc_if;
if_bpf = ifn->if_bpf;
if (!if_bpf)
return (0);
bzero(&hdr, sizeof(hdr));
hdr.length = PFLOG_REAL_HDRLEN;
/* Default rule does not pass packets dropped for other reasons. */
hdr.action = (rm->nr == (u_int32_t)-1 && reason != PFRES_MATCH) ?
PF_DROP : rm->action;
hdr.reason = reason;
memcpy(hdr.ifname, pd->kif->pfik_name, sizeof(hdr.ifname));
if (am == NULL) {
hdr.rulenr = htonl(rm->nr);
hdr.subrulenr = -1;
} else {
hdr.rulenr = htonl(am->nr);
hdr.subrulenr = htonl(rm->nr);
if (ruleset != NULL && ruleset->anchor != NULL)
strlcpy(hdr.ruleset, ruleset->anchor->name,
sizeof(hdr.ruleset));
}
if (trigger->log & PF_LOG_USER && !pd->lookup.done)
pd->lookup.done = pf_socket_lookup(pd);
if (trigger->log & PF_LOG_USER && pd->lookup.done > 0) {
hdr.uid = pd->lookup.uid;
hdr.pid = pd->lookup.pid;
} else {
hdr.uid = -1;
hdr.pid = NO_PID;
}
hdr.rule_uid = rm->cuid;
hdr.rule_pid = rm->cpid;
hdr.dir = pd->dir;
hdr.af = pd->af;
if (pd->src != NULL && pd->dst != NULL) {
if (pd->af != pd->naf ||
pf_addr_compare(pd->src, &pd->nsaddr, pd->naf) != 0 ||
pf_addr_compare(pd->dst, &pd->ndaddr, pd->naf) != 0 ||
pd->osport != pd->nsport ||
pd->odport != pd->ndport) {
hdr.rewritten = 1;
}
}
hdr.naf = pd->naf;
pf_addrcpy(&hdr.saddr, &pd->nsaddr, pd->naf);
pf_addrcpy(&hdr.daddr, &pd->ndaddr, pd->naf);
hdr.sport = pd->nsport;
hdr.dport = pd->ndport;
ifn->if_opackets++;
ifn->if_obytes += pd->m->m_pkthdr.len;
bpf_mtap_hdr(if_bpf, &hdr, sizeof(hdr), pd->m, BPF_DIRECTION_OUT);
#endif
return (0);
}
+983
View File
@@ -0,0 +1,983 @@
/* PFLINUX port of OpenBSD 8.0 net/ifq.c — see docs/porting-notes.md */
#include <pfk.h>
#endif
/*
* priq glue
*/
unsigned int priq_idx(unsigned int, const struct mbuf *);
struct mbuf *priq_enq(struct ifqueue *, struct mbuf *);
struct mbuf *priq_deq_begin(struct ifqueue *, void **);
void priq_deq_commit(struct ifqueue *, struct mbuf *, void *);
void priq_purge(struct ifqueue *, struct mbuf_list *);
void *priq_alloc(unsigned int, void *);
void priq_free(unsigned int, void *);
const struct ifq_ops priq_ops = {
priq_idx,
priq_enq,
priq_deq_begin,
priq_deq_commit,
priq_purge,
priq_alloc,
priq_free,
};
const struct ifq_ops * const ifq_priq_ops = &priq_ops;
/*
* priq internal structures
*/
struct priq {
struct mbuf_list pq_lists[IFQ_NQUEUES];
};
/*
* ifqueue serialiser
*/
void ifq_start_task(void *);
void ifq_restart_task(void *);
void ifq_bundle_task(void *);
static inline void
ifq_run_start(struct ifqueue *ifq)
{
ifq_serialize(ifq, &ifq->ifq_start);
}
void
ifq_serialize(struct ifqueue *ifq, struct task *t)
{
struct task work;
if (ISSET(t->t_flags, TASK_ONQUEUE))
return;
mtx_enter(&ifq->ifq_task_mtx);
if (!ISSET(t->t_flags, TASK_ONQUEUE)) {
SET(t->t_flags, TASK_ONQUEUE);
TAILQ_INSERT_TAIL(&ifq->ifq_task_list, t, t_entry);
}
if (ifq->ifq_serializer == NULL) {
ifq->ifq_serializer = curcpu();
while ((t = TAILQ_FIRST(&ifq->ifq_task_list)) != NULL) {
TAILQ_REMOVE(&ifq->ifq_task_list, t, t_entry);
CLR(t->t_flags, TASK_ONQUEUE);
work = *t; /* copy to caller to avoid races */
mtx_leave(&ifq->ifq_task_mtx);
(*work.t_func)(work.t_arg);
mtx_enter(&ifq->ifq_task_mtx);
}
ifq->ifq_serializer = NULL;
}
mtx_leave(&ifq->ifq_task_mtx);
}
void
ifq_start(struct ifqueue *ifq)
{
if (ifq_len(ifq) >= min(ifq->ifq_if->if_txmit, ifq->ifq_maxlen)) {
task_del(ifq->ifq_softnet, &ifq->ifq_bundle);
ifq_run_start(ifq);
} else
task_add(ifq->ifq_softnet, &ifq->ifq_bundle);
}
void
ifq_start_task(void *p)
{
struct ifqueue *ifq = p;
struct ifnet *ifp = ifq->ifq_if;
if (!ISSET(ifp->if_flags, PFIFF_RUNNING) ||
ifq_empty(ifq) || ifq_is_oactive(ifq))
return;
ifp->if_qstart(ifq);
}
void
ifq_set_oactive(struct ifqueue *ifq)
{
if (ifq->ifq_oactive)
return;
mtx_enter(&ifq->ifq_mtx);
if (!ifq->ifq_oactive) {
ifq->ifq_oactive = 1;
ifq->ifq_oactives++;
}
mtx_leave(&ifq->ifq_mtx);
}
void
ifq_deq_set_oactive(struct ifqueue *ifq)
{
MUTEX_ASSERT_LOCKED(&ifq->ifq_mtx);
if (!ifq->ifq_oactive) {
ifq->ifq_oactive = 1;
ifq->ifq_oactives++;
}
}
void
ifq_restart_task(void *p)
{
struct ifqueue *ifq = p;
struct ifnet *ifp = ifq->ifq_if;
ifq_clr_oactive(ifq);
ifp->if_qstart(ifq);
}
void
ifq_bundle_task(void *p)
{
struct ifqueue *ifq = p;
ifq_run_start(ifq);
}
void
ifq_barrier(struct ifqueue *ifq)
{
struct cond c = COND_INITIALIZER();
struct task t = TASK_INITIALIZER(cond_signal_handler, &c);
task_del(ifq->ifq_softnet, &ifq->ifq_bundle);
if (ifq->ifq_serializer == NULL)
return;
ifq_serialize(ifq, &t);
cond_wait(&c, "ifqbar");
}
/*
* ifqueue mbuf queue API
*/
#if NKSTAT > 0
struct ifq_kstat_data {
struct kstat_kv kd_packets;
struct kstat_kv kd_bytes;
struct kstat_kv kd_qdrops;
struct kstat_kv kd_errors;
struct kstat_kv kd_qlen;
struct kstat_kv kd_maxqlen;
struct kstat_kv kd_oactive;
struct kstat_kv kd_oactives;
};
static const struct ifq_kstat_data ifq_kstat_tpl = {
KSTAT_KV_UNIT_INITIALIZER("packets",
KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS),
KSTAT_KV_UNIT_INITIALIZER("bytes",
KSTAT_KV_T_COUNTER64, KSTAT_KV_U_BYTES),
KSTAT_KV_UNIT_INITIALIZER("qdrops",
KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS),
KSTAT_KV_UNIT_INITIALIZER("errors",
KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS),
KSTAT_KV_UNIT_INITIALIZER("qlen",
KSTAT_KV_T_UINT32, KSTAT_KV_U_PACKETS),
KSTAT_KV_UNIT_INITIALIZER("maxqlen",
KSTAT_KV_T_UINT32, KSTAT_KV_U_PACKETS),
KSTAT_KV_INITIALIZER("oactive", KSTAT_KV_T_BOOL),
KSTAT_KV_INITIALIZER("oactives", KSTAT_KV_T_COUNTER32),
};
int
ifq_kstat_copy(struct kstat *ks, void *dst)
{
struct ifqueue *ifq = ks->ks_softc;
struct ifq_kstat_data *kd = dst;
*kd = ifq_kstat_tpl;
kstat_kv_u64(&kd->kd_packets) = ifq->ifq_packets;
kstat_kv_u64(&kd->kd_bytes) = ifq->ifq_bytes;
kstat_kv_u64(&kd->kd_qdrops) = ifq->ifq_qdrops;
kstat_kv_u64(&kd->kd_errors) = ifq->ifq_errors;
kstat_kv_u32(&kd->kd_qlen) = ifq->ifq_len;
kstat_kv_u32(&kd->kd_maxqlen) = ifq->ifq_maxlen;
kstat_kv_bool(&kd->kd_oactive) = ifq->ifq_oactive;
kstat_kv_u32(&kd->kd_oactives) = ifq->ifq_oactives;
return (0);
}
#endif
void
ifq_init(struct ifqueue *ifq, struct ifnet *ifp, unsigned int idx)
{
ifq->ifq_if = ifp;
ifq->ifq_softnet = net_tq(idx);
ifq->ifq_softc = NULL;
mtx_init(&ifq->ifq_mtx, IPL_NET);
/* default to priq */
ifq->ifq_ops = &priq_ops;
ifq->ifq_q = priq_ops.ifqop_alloc(idx, NULL);
ml_init(&ifq->ifq_free);
ifq->ifq_len = 0;
ifq->ifq_packets = 0;
ifq->ifq_bytes = 0;
ifq->ifq_qdrops = 0;
ifq->ifq_errors = 0;
ifq->ifq_mcasts = 0;
mtx_init(&ifq->ifq_task_mtx, IPL_NET);
TAILQ_INIT(&ifq->ifq_task_list);
ifq->ifq_serializer = NULL;
task_set(&ifq->ifq_bundle, ifq_bundle_task, ifq);
task_set(&ifq->ifq_start, ifq_start_task, ifq);
task_set(&ifq->ifq_restart, ifq_restart_task, ifq);
if (ifq->ifq_maxlen == 0)
ifq_init_maxlen(ifq, IFQ_MAXLEN);
ifq->ifq_idx = idx;
#if NKSTAT > 0
/* XXX xname vs driver name and unit */
ifq->ifq_kstat = kstat_create(ifp->if_xname, 0,
"txq", ifq->ifq_idx, KSTAT_T_KV, 0);
KASSERT(ifq->ifq_kstat != NULL);
kstat_set_mutex(ifq->ifq_kstat, &ifq->ifq_mtx);
ifq->ifq_kstat->ks_softc = ifq;
ifq->ifq_kstat->ks_datalen = sizeof(ifq_kstat_tpl);
ifq->ifq_kstat->ks_copy = ifq_kstat_copy;
kstat_install(ifq->ifq_kstat);
#endif
}
void
ifq_attach(struct ifqueue *ifq, const struct ifq_ops *newops, void *opsarg)
{
struct mbuf_list ml = MBUF_LIST_INITIALIZER();
struct mbuf_list free_ml = MBUF_LIST_INITIALIZER();
struct mbuf *m;
const struct ifq_ops *oldops;
void *newq, *oldq;
newq = newops->ifqop_alloc(ifq->ifq_idx, opsarg);
mtx_enter(&ifq->ifq_mtx);
ifq->ifq_ops->ifqop_purge(ifq, &ml);
ifq->ifq_len = 0;
oldops = ifq->ifq_ops;
oldq = ifq->ifq_q;
ifq->ifq_ops = newops;
ifq->ifq_q = newq;
while ((m = ml_dequeue(&ml)) != NULL) {
m = ifq->ifq_ops->ifqop_enq(ifq, m);
if (m != NULL) {
ifq->ifq_qdrops++;
ml_enqueue(&free_ml, m);
} else
ifq->ifq_len++;
}
mtx_leave(&ifq->ifq_mtx);
oldops->ifqop_free(ifq->ifq_idx, oldq);
ml_purge(&free_ml);
}
void
ifq_destroy(struct ifqueue *ifq)
{
struct mbuf_list ml = MBUF_LIST_INITIALIZER();
#if NKSTAT > 0
kstat_destroy(ifq->ifq_kstat);
#endif
NET_ASSERT_UNLOCKED();
taskq_del_barrier(ifq->ifq_softnet, &ifq->ifq_bundle);
/* don't need to lock because this is the last use of the ifq */
ifq->ifq_ops->ifqop_purge(ifq, &ml);
ifq->ifq_ops->ifqop_free(ifq->ifq_idx, ifq->ifq_q);
ml_purge(&ml);
}
void
ifq_add_data(struct ifqueue *ifq, struct if_data *data)
{
mtx_enter(&ifq->ifq_mtx);
data->ifi_opackets += ifq->ifq_packets;
data->ifi_obytes += ifq->ifq_bytes;
data->ifi_oqdrops += ifq->ifq_qdrops;
data->ifi_omcasts += ifq->ifq_mcasts;
/* ifp->if_data.ifi_oerrors */
mtx_leave(&ifq->ifq_mtx);
}
int
ifq_enqueue(struct ifqueue *ifq, struct mbuf *m)
{
struct mbuf *dm;
mtx_enter(&ifq->ifq_mtx);
dm = ifq->ifq_ops->ifqop_enq(ifq, m);
if (dm != m) {
ifq->ifq_packets++;
ifq->ifq_bytes += m->m_pkthdr.len;
if (ISSET(m->m_flags, M_MCAST))
ifq->ifq_mcasts++;
}
if (dm == NULL)
ifq->ifq_len++;
else
ifq->ifq_qdrops++;
mtx_leave(&ifq->ifq_mtx);
if (dm != NULL)
m_freem(dm);
return (dm == m ? ENOBUFS : 0);
}
static inline void
ifq_deq_enter(struct ifqueue *ifq)
{
mtx_enter(&ifq->ifq_mtx);
}
static inline void
ifq_deq_leave(struct ifqueue *ifq)
{
struct mbuf_list ml;
ml = ifq->ifq_free;
ml_init(&ifq->ifq_free);
mtx_leave(&ifq->ifq_mtx);
if (!ml_empty(&ml))
ml_purge(&ml);
}
struct mbuf *
ifq_deq_begin(struct ifqueue *ifq)
{
struct mbuf *m = NULL;
void *cookie;
ifq_deq_enter(ifq);
if (ifq->ifq_len == 0 ||
(m = ifq->ifq_ops->ifqop_deq_begin(ifq, &cookie)) == NULL) {
ifq_deq_leave(ifq);
return (NULL);
}
m->m_pkthdr.ph_cookie = cookie;
return (m);
}
void
ifq_deq_commit(struct ifqueue *ifq, struct mbuf *m)
{
void *cookie;
KASSERT(m != NULL);
cookie = m->m_pkthdr.ph_cookie;
ifq->ifq_ops->ifqop_deq_commit(ifq, m, cookie);
ifq->ifq_len--;
ifq_deq_leave(ifq);
}
void
ifq_deq_rollback(struct ifqueue *ifq, struct mbuf *m)
{
KASSERT(m != NULL);
ifq_deq_leave(ifq);
}
struct mbuf *
ifq_dequeue(struct ifqueue *ifq)
{
struct mbuf *m;
m = ifq_deq_begin(ifq);
if (m == NULL)
return (NULL);
ifq_deq_commit(ifq, m);
return (m);
}
int
ifq_deq_sleep(struct ifqueue *ifq, struct mbuf **mp, int nbio, int priority,
const char *wmesg, volatile unsigned int *sleeping,
volatile unsigned int *alive)
{
struct mbuf *m;
void *cookie;
int error = 0;
ifq_deq_enter(ifq);
if (ifq->ifq_len == 0 && nbio)
error = EWOULDBLOCK;
else {
for (;;) {
m = ifq->ifq_ops->ifqop_deq_begin(ifq, &cookie);
if (m != NULL) {
ifq->ifq_ops->ifqop_deq_commit(ifq, m, cookie);
ifq->ifq_len--;
*mp = m;
break;
}
(*sleeping)++;
error = msleep_nsec(ifq, &ifq->ifq_mtx,
priority, wmesg, INFSLP);
(*sleeping)--;
if (error != 0)
break;
if (!(*alive)) {
error = EIO;
break;
}
}
}
ifq_deq_leave(ifq);
return (error);
}
int
ifq_hdatalen(struct ifqueue *ifq)
{
struct mbuf *m;
int len = 0;
if (ifq_empty(ifq))
return (0);
m = ifq_deq_begin(ifq);
if (m != NULL) {
len = m->m_pkthdr.len;
ifq_deq_rollback(ifq, m);
}
return (len);
}
void
ifq_init_maxlen(struct ifqueue *ifq, unsigned int maxlen)
{
/* this is not MP safe, use only during attach */
ifq->ifq_maxlen = maxlen;
}
unsigned int
ifq_purge(struct ifqueue *ifq)
{
struct mbuf_list ml = MBUF_LIST_INITIALIZER();
unsigned int rv;
mtx_enter(&ifq->ifq_mtx);
ifq->ifq_ops->ifqop_purge(ifq, &ml);
rv = ifq->ifq_len;
ifq->ifq_len = 0;
ifq->ifq_qdrops += rv;
mtx_leave(&ifq->ifq_mtx);
KASSERT(rv == ml_len(&ml));
ml_purge(&ml);
return (rv);
}
void *
ifq_q_enter(struct ifqueue *ifq, const struct ifq_ops *ops)
{
mtx_enter(&ifq->ifq_mtx);
if (ifq->ifq_ops == ops)
return (ifq->ifq_q);
mtx_leave(&ifq->ifq_mtx);
return (NULL);
}
void
ifq_q_leave(struct ifqueue *ifq, void *q)
{
KASSERT(q == ifq->ifq_q);
mtx_leave(&ifq->ifq_mtx);
}
void
ifq_mfreem(struct ifqueue *ifq, struct mbuf *m)
{
MUTEX_ASSERT_LOCKED(&ifq->ifq_mtx);
ifq->ifq_len--;
ifq->ifq_qdrops++;
ml_enqueue(&ifq->ifq_free, m);
}
void
ifq_mfreeml(struct ifqueue *ifq, struct mbuf_list *ml)
{
MUTEX_ASSERT_LOCKED(&ifq->ifq_mtx);
ifq->ifq_len -= ml_len(ml);
ifq->ifq_qdrops += ml_len(ml);
ml_enlist(&ifq->ifq_free, ml);
}
/*
* ifiq
*/
#if NKSTAT > 0
struct ifiq_kstat_data {
struct kstat_kv kd_packets;
struct kstat_kv kd_bytes;
struct kstat_kv kd_fdrops;
struct kstat_kv kd_qdrops;
struct kstat_kv kd_errors;
struct kstat_kv kd_qlen;
struct kstat_kv kd_enqueues;
struct kstat_kv kd_dequeues;
};
static const struct ifiq_kstat_data ifiq_kstat_tpl = {
KSTAT_KV_UNIT_INITIALIZER("packets",
KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS),
KSTAT_KV_UNIT_INITIALIZER("bytes",
KSTAT_KV_T_COUNTER64, KSTAT_KV_U_BYTES),
KSTAT_KV_UNIT_INITIALIZER("fdrops",
KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS),
KSTAT_KV_UNIT_INITIALIZER("qdrops",
KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS),
KSTAT_KV_UNIT_INITIALIZER("errors",
KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS),
KSTAT_KV_UNIT_INITIALIZER("qlen",
KSTAT_KV_T_UINT32, KSTAT_KV_U_PACKETS),
KSTAT_KV_INITIALIZER("enqueues",
KSTAT_KV_T_COUNTER64),
KSTAT_KV_INITIALIZER("dequeues",
KSTAT_KV_T_COUNTER64),
};
int
ifiq_kstat_copy(struct kstat *ks, void *dst)
{
struct ifiqueue *ifiq = ks->ks_softc;
struct ifiq_kstat_data *kd = dst;
*kd = ifiq_kstat_tpl;
kstat_kv_u64(&kd->kd_packets) = ifiq->ifiq_packets;
kstat_kv_u64(&kd->kd_bytes) = ifiq->ifiq_bytes;
kstat_kv_u64(&kd->kd_fdrops) = ifiq->ifiq_fdrops;
kstat_kv_u64(&kd->kd_qdrops) = ifiq->ifiq_qdrops;
kstat_kv_u64(&kd->kd_errors) = ifiq->ifiq_errors;
kstat_kv_u32(&kd->kd_qlen) = ml_len(&ifiq->ifiq_ml);
kstat_kv_u64(&kd->kd_enqueues) = ifiq->ifiq_enqueues;
kstat_kv_u64(&kd->kd_dequeues) = ifiq->ifiq_dequeues;
return (0);
}
#endif
static void ifiq_process(void *);
void
ifiq_init(struct ifiqueue *ifiq, struct ifnet *ifp, unsigned int idx)
{
ifiq->ifiq_if = ifp;
ifiq->ifiq_bpfp = NULL;
ifiq->ifiq_softnet = net_tq(idx);
ifiq->ifiq_softc = NULL;
mtx_init(&ifiq->ifiq_mtx, IPL_NET);
ml_init(&ifiq->ifiq_ml);
task_set(&ifiq->ifiq_task, ifiq_process, ifiq);
ifiq->ifiq_pressure = 0;
ifiq->ifiq_packets = 0;
ifiq->ifiq_bytes = 0;
ifiq->ifiq_fdrops = 0;
ifiq->ifiq_qdrops = 0;
ifiq->ifiq_errors = 0;
ifiq->ifiq_idx = idx;
#if NKSTAT > 0
/* XXX xname vs driver name and unit */
ifiq->ifiq_kstat = kstat_create(ifp->if_xname, 0,
"rxq", ifiq->ifiq_idx, KSTAT_T_KV, 0);
KASSERT(ifiq->ifiq_kstat != NULL);
kstat_set_mutex(ifiq->ifiq_kstat, &ifiq->ifiq_mtx);
ifiq->ifiq_kstat->ks_softc = ifiq;
ifiq->ifiq_kstat->ks_datalen = sizeof(ifiq_kstat_tpl);
ifiq->ifiq_kstat->ks_copy = ifiq_kstat_copy;
kstat_install(ifiq->ifiq_kstat);
#endif
}
void
ifiq_destroy(struct ifiqueue *ifiq)
{
#if NKSTAT > 0
kstat_destroy(ifiq->ifiq_kstat);
#endif
NET_ASSERT_UNLOCKED();
taskq_del_barrier(ifiq->ifiq_softnet, &ifiq->ifiq_task);
/* don't need to lock because this is the last use of the ifiq */
ml_purge(&ifiq->ifiq_ml);
}
unsigned int ifiq_maxlen_drop = 2048 * 5;
unsigned int ifiq_maxlen_return = 2048 * 3;
int
ifiq_input(struct ifiqueue *ifiq, struct mbuf_list *ml)
{
struct ifnet *ifp = ifiq->ifiq_if;
struct mbuf *m;
uint64_t packets;
uint64_t bytes = 0;
uint64_t fdrops = 0;
unsigned int len;
#if NBPFILTER > 0
caddr_t *ifiq_bpfp, ifiq_bpf = NULL, if_bpf;
#endif
if (ml_empty(ml))
return (0);
MBUF_LIST_FOREACH(ml, m) {
m->m_pkthdr.ph_ifidx = ifp->if_index;
m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
bytes += m->m_pkthdr.len;
}
packets = ml_len(ml);
#if NBPFILTER > 0
ifiq_bpfp = READ_ONCE(ifiq->ifiq_bpfp);
if (ifiq_bpfp != NULL)
ifiq_bpf = READ_ONCE(*ifiq_bpfp);
if_bpf = READ_ONCE(ifp->if_bpf);
if (ifiq_bpf || if_bpf) {
struct mbuf_list ml0 = *ml;
ml_init(ml);
while ((m = ml_dequeue(&ml0)) != NULL) {
int drop = 0;
if (ifiq_bpf &&
(*ifp->if_bpf_mtap)(ifiq_bpf, m, BPF_DIRECTION_IN))
drop = 1;
if (if_bpf &&
(*ifp->if_bpf_mtap)(if_bpf, m, BPF_DIRECTION_IN))
drop = 1;
if (drop) {
m_freem(m);
fdrops++;
} else
ml_enqueue(ml, m);
}
if (ml_empty(ml)) {
mtx_enter(&ifiq->ifiq_mtx);
ifiq->ifiq_packets += packets;
ifiq->ifiq_bytes += bytes;
ifiq->ifiq_fdrops += fdrops;
mtx_leave(&ifiq->ifiq_mtx);
return (0);
}
}
#endif
mtx_enter(&ifiq->ifiq_mtx);
ifiq->ifiq_packets += packets;
ifiq->ifiq_bytes += bytes;
ifiq->ifiq_fdrops += fdrops;
len = ml_len(&ifiq->ifiq_ml);
if (__predict_true(!ISSET(ifp->if_xflags, IFXF_MONITOR))) {
if (len > ifiq_maxlen_drop)
ifiq->ifiq_qdrops += ml_len(ml);
else {
ifiq->ifiq_enqueues++;
ml_enlist(&ifiq->ifiq_ml, ml);
}
}
mtx_leave(&ifiq->ifiq_mtx);
if (ml_empty(ml))
task_add(ifiq->ifiq_softnet, &ifiq->ifiq_task);
else
ml_purge(ml);
return (len > ifiq_maxlen_return);
}
void
ifiq_add_data(struct ifiqueue *ifiq, struct if_data *data)
{
mtx_enter(&ifiq->ifiq_mtx);
data->ifi_ipackets += ifiq->ifiq_packets;
data->ifi_ibytes += ifiq->ifiq_bytes;
data->ifi_iqdrops += ifiq->ifiq_qdrops;
mtx_leave(&ifiq->ifiq_mtx);
}
int
ifiq_enqueue_qlim(struct ifiqueue *ifiq, struct mbuf *m, unsigned int qlim)
{
struct ifnet *ifp = ifiq->ifiq_if;
unsigned int len;
#if NBPFILTER > 0
caddr_t if_bpf = ifp->if_bpf;
#endif
m->m_pkthdr.ph_ifidx = ifp->if_index;
m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
#if NBPFILTER > 0
if_bpf = ifp->if_bpf;
if (if_bpf) {
if ((*ifp->if_bpf_mtap)(if_bpf, m, BPF_DIRECTION_IN)) {
mtx_enter(&ifiq->ifiq_mtx);
ifiq->ifiq_packets++;
ifiq->ifiq_bytes += m->m_pkthdr.len;
ifiq->ifiq_fdrops++;
mtx_leave(&ifiq->ifiq_mtx);
m_freem(m);
return (0);
}
}
#endif
mtx_enter(&ifiq->ifiq_mtx);
ifiq->ifiq_packets++;
ifiq->ifiq_bytes += m->m_pkthdr.len;
if (qlim && ((len = ml_len(&ifiq->ifiq_ml) >= qlim))) {
ifiq->ifiq_qdrops++;
} else {
ifiq->ifiq_enqueues++;
ml_enqueue(&ifiq->ifiq_ml, m);
m = NULL;
}
mtx_leave(&ifiq->ifiq_mtx);
if (m) {
m_freem(m);
return (0);
}
task_add(ifiq->ifiq_softnet, &ifiq->ifiq_task);
return (0);
}
static void
ifiq_process(void *arg)
{
struct ifiqueue *ifiq = arg;
struct mbuf_list ml;
if (ifiq_empty(ifiq))
return;
mtx_enter(&ifiq->ifiq_mtx);
ifiq->ifiq_dequeues++;
ml = ifiq->ifiq_ml;
ml_init(&ifiq->ifiq_ml);
mtx_leave(&ifiq->ifiq_mtx);
if_input_process(ifiq->ifiq_if, &ml, ifiq->ifiq_idx);
}
#ifndef SMALL_KERNEL
int
net_ifiq_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
void *newp, size_t newlen)
{
int error = EOPNOTSUPP;
/* pressure is disabled for 6.6-release */
#if 0
int val;
if (namelen != 1)
return (EISDIR);
switch (name[0]) {
case NET_LINK_IFRXQ_PRESSURE_RETURN:
val = ifiq_pressure_return;
error = sysctl_int(oldp, oldlenp, newp, newlen, &val);
if (error != 0)
return (error);
if (val < 1 || val > ifiq_pressure_drop)
return (EINVAL);
ifiq_pressure_return = val;
break;
case NET_LINK_IFRXQ_PRESSURE_DROP:
val = ifiq_pressure_drop;
error = sysctl_int(oldp, oldlenp, newp, newlen, &val);
if (error != 0)
return (error);
if (ifiq_pressure_return > val)
return (EINVAL);
ifiq_pressure_drop = val;
break;
default:
error = EOPNOTSUPP;
break;
}
#endif
return (error);
}
#endif /* SMALL_KERNEL */
/*
* priq implementation
*/
unsigned int
priq_idx(unsigned int nqueues, const struct mbuf *m)
{
unsigned int flow = 0;
if (ISSET(m->m_pkthdr.csum_flags, M_FLOWID))
flow = m->m_pkthdr.ph_flowid;
return (flow % nqueues);
}
void *
priq_alloc(unsigned int idx, void *null)
{
struct priq *pq;
int i;
pq = malloc(sizeof(struct priq), M_DEVBUF, M_WAITOK);
for (i = 0; i < IFQ_NQUEUES; i++)
ml_init(&pq->pq_lists[i]);
return (pq);
}
void
priq_free(unsigned int idx, void *pq)
{
free(pq, M_DEVBUF, sizeof(struct priq));
}
struct mbuf *
priq_enq(struct ifqueue *ifq, struct mbuf *m)
{
struct priq *pq;
struct mbuf_list *pl;
struct mbuf *n = NULL;
unsigned int prio;
pq = ifq->ifq_q;
KASSERT(m->m_pkthdr.pf.prio <= IFQ_MAXPRIO);
/* Find a lower priority queue to drop from */
if (ifq_len(ifq) >= ifq->ifq_maxlen) {
for (prio = 0; prio < m->m_pkthdr.pf.prio; prio++) {
pl = &pq->pq_lists[prio];
if (ml_len(pl) > 0) {
n = ml_dequeue(pl);
goto enqueue;
}
}
/*
* There's no lower priority queue that we can
* drop from so don't enqueue this one.
*/
return (m);
}
enqueue:
pl = &pq->pq_lists[m->m_pkthdr.pf.prio];
ml_enqueue(pl, m);
return (n);
}
struct mbuf *
priq_deq_begin(struct ifqueue *ifq, void **cookiep)
{
struct priq *pq = ifq->ifq_q;
struct mbuf_list *pl;
unsigned int prio = nitems(pq->pq_lists);
struct mbuf *m;
do {
pl = &pq->pq_lists[--prio];
m = MBUF_LIST_FIRST(pl);
if (m != NULL) {
*cookiep = pl;
return (m);
}
} while (prio > 0);
return (NULL);
}
void
priq_deq_commit(struct ifqueue *ifq, struct mbuf *m, void *cookie)
{
struct mbuf_list *pl = cookie;
KASSERT(MBUF_LIST_FIRST(pl) == m);
ml_dequeue(pl);
}
void
priq_purge(struct ifqueue *ifq, struct mbuf_list *ml)
{
struct priq *pq = ifq->ifq_q;
struct mbuf_list *pl;
unsigned int prio = nitems(pq->pq_lists);
do {
pl = &pq->pq_lists[--prio];
ml_enlist(ml, pl);
} while (prio > 0);
}
+318
View File
@@ -0,0 +1,318 @@
/* PFLINUX port of OpenBSD 8.0 netinet/ip_divert.c — see docs/porting-notes.md */
#include <pfk.h>
/*
* Locks used to protect data:
* a atomic
*/
struct inpcbtable divbtable;
struct cpumem *divcounters;
u_int divert_sendspace = DIVERT_SENDSPACE; /* [a] */
u_int divert_recvspace = DIVERT_RECVSPACE; /* [a] */
const struct sysctl_bounded_args divertctl_vars[] = {
{ DIVERTCTL_RECVSPACE, &divert_recvspace, 0, SB_MAX },
{ DIVERTCTL_SENDSPACE, &divert_sendspace, 0, SB_MAX },
};
const struct pr_usrreqs divert_usrreqs = {
.pru_attach = divert_attach,
.pru_detach = divert_detach,
.pru_bind = divert_bind,
.pru_shutdown = divert_shutdown,
.pru_send = divert_send,
.pru_control = in_control,
.pru_sockaddr = in_sockaddr,
.pru_peeraddr = in_peeraddr,
};
int divert_output(struct inpcb *, struct mbuf *, struct mbuf *,
struct mbuf *);
void
divert_init(void)
{
in_pcbinit(&divbtable, DIVERT_HASHSIZE);
divcounters = counters_alloc(divs_ncounters);
}
int
divert_output(struct inpcb *inp, struct mbuf *m, struct mbuf *nam,
struct mbuf *control)
{
struct sockaddr_in *sin;
int error, min_hdrlen, off, dir;
struct pf_ip *ip;
m_freem(control);
if ((error = in_nam2sin(nam, &sin)))
goto fail;
if (m->m_pkthdr.len > IP_MAXPACKET) {
error = EMSGSIZE;
goto fail;
}
m = rip_chkhdr(m, NULL);
if (m == NULL) {
error = EINVAL;
goto fail;
}
ip = mtod(m, struct pf_ip *);
off = ip->ip_hl << 2;
dir = (sin->sin_addr.s_addr == INADDR_ANY ? PF_OUT : PF_IN);
switch (ip->ip_p) {
case IPPROTO_TCP:
min_hdrlen = sizeof(struct pf_tcphdr);
m->m_pkthdr.csum_flags |= M_TCP_CSUM_OUT;
break;
case IPPROTO_UDP:
min_hdrlen = sizeof(struct pf_udphdr);
m->m_pkthdr.csum_flags |= M_UDP_CSUM_OUT;
break;
case IPPROTO_ICMP:
min_hdrlen = ICMP_MINLEN;
m->m_pkthdr.csum_flags |= M_ICMP_CSUM_OUT;
break;
default:
min_hdrlen = 0;
break;
}
if (min_hdrlen && m->m_pkthdr.len < off + min_hdrlen) {
error = EINVAL;
goto fail;
}
m->m_pkthdr.pf.flags |= PF_TAG_DIVERTED_PACKET;
if (dir == PF_IN) {
struct rtentry *rt;
struct ifnet *ifp;
rt = rtalloc(sintosa(sin), 0, inp->inp_rtableid);
if (!rtisvalid(rt) || !ISSET(rt->rt_flags, RTF_LOCAL)) {
rtfree(rt);
error = EADDRNOTAVAIL;
goto fail;
}
m->m_pkthdr.ph_ifidx = rt->rt_ifidx;
rtfree(rt);
/*
* Recalculate IP and protocol checksums for the inbound packet
* since the userspace application may have modified the packet
* prior to reinjection.
*/
in_hdr_cksum_out(m, NULL);
in_proto_cksum_out(m, NULL);
ifp = if_get(m->m_pkthdr.ph_ifidx);
if (ifp == NULL) {
error = ENETDOWN;
goto fail;
}
ipv4_input(ifp, m, NULL);
if_put(ifp);
} else {
m->m_pkthdr.ph_rtableid = inp->inp_rtableid;
error = ip_output(m, NULL, &inp->inp_route,
IP_ALLOWBROADCAST | IP_RAWOUTPUT, NULL, NULL, 0);
}
divstat_inc(divs_opackets);
return (error);
fail:
m_freem(m);
divstat_inc(divs_errors);
return (error);
}
void
divert_packet(struct mbuf *m, int dir, u_int16_t divert_port)
{
struct inpcb *inp = NULL;
struct socket *so;
struct sockaddr_in sin;
u_int rdomain;
divstat_inc(divs_ipackets);
if (m->m_len < sizeof(struct pf_ip) &&
(m = m_pullup(m, sizeof(struct pf_ip))) == NULL) {
divstat_inc(divs_errors);
goto bad;
}
rdomain = rtable_l2(m->m_pkthdr.ph_rtableid);
mtx_enter(&divbtable.inpt_mtx);
TAILQ_FOREACH(inp, &divbtable.inpt_queue, inp_queue) {
if (inp->inp_lport != divert_port ||
rtable_l2(inp->inp_rtableid) != rdomain) {
continue;
}
in_pcbref(inp);
break;
}
mtx_leave(&divbtable.inpt_mtx);
if (inp == NULL) {
divstat_inc(divs_noport);
goto bad;
}
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin./* PFLINUX sin_len */;
if (dir == PF_IN) {
struct ifaddr *ifa;
struct ifnet *ifp;
ifp = if_get(m->m_pkthdr.ph_ifidx);
if (ifp == NULL) {
divstat_inc(divs_errors);
goto bad;
}
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr->sa_family != AF_INET)
continue;
sin.sin_addr = satosin(ifa->ifa_addr)->sin_addr;
break;
}
if_put(ifp);
} else {
/*
* Calculate IP and protocol checksums for outbound packet
* diverted to userland. pf rule diverts before cksum offload.
*/
in_hdr_cksum_out(m, NULL);
in_proto_cksum_out(m, NULL);
}
so = inp->inp_socket;
mtx_enter(&so->so_rcv.sb_mtx);
if (sbappendaddr(&so->so_rcv, sintosa(&sin), m, NULL) == 0) {
mtx_leave(&so->so_rcv.sb_mtx);
divstat_inc(divs_fullsock);
goto bad;
}
mtx_leave(&so->so_rcv.sb_mtx);
sorwakeup(so);
in_pcbunref(inp);
return;
bad:
in_pcbunref(inp);
m_freem(m);
}
int
divert_attach(struct socket *so, int proto, int wait)
{
int error;
if (so->so_pcb != NULL)
return EINVAL;
if ((so->so_state & SS_PRIV) == 0)
return EACCES;
error = soreserve(so, atomic_load_int(&divert_sendspace),
atomic_load_int(&divert_recvspace));
if (error)
return error;
error = in_pcballoc(so, &divbtable, wait);
if (error)
return error;
sotoinpcb(so)->inp_flags |= INP_HDRINCL;
return (0);
}
int
divert_detach(struct socket *so)
{
struct inpcb *inp = sotoinpcb(so);
soassertlocked(so);
if (inp == NULL)
return (EINVAL);
in_pcbdetach(inp);
return (0);
}
int
divert_bind(struct socket *so, struct mbuf *addr, struct proc *p)
{
struct inpcb *inp = sotoinpcb(so);
soassertlocked(so);
return in_pcbbind(inp, addr, p);
}
int
divert_shutdown(struct socket *so)
{
soassertlocked(so);
socantsendmore(so);
return (0);
}
int
divert_send(struct socket *so, struct mbuf *m, struct mbuf *addr,
struct mbuf *control)
{
struct inpcb *inp = sotoinpcb(so);
soassertlocked(so);
return (divert_output(inp, m, addr, control));
}
int
divert_sysctl_divstat(void *oldp, size_t *oldlenp, void *newp)
{
uint64_t counters[divs_ncounters];
struct divstat divstat;
u_long *words = (u_long *)&divstat;
int i;
CTASSERT(sizeof(divstat) == (nitems(counters) * sizeof(u_long)));
memset(&divstat, 0, sizeof divstat);
counters_read(divcounters, counters, nitems(counters), NULL);
for (i = 0; i < nitems(counters); i++)
words[i] = (u_long)counters[i];
return (sysctl_rdstruct(oldp, oldlenp, newp,
&divstat, sizeof(divstat)));
}
/*
* Sysctl for divert variables.
*/
int
divert_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen)
{
/* All sysctl names at this level are terminal. */
if (namelen != 1)
return (ENOTDIR);
switch (name[0]) {
case DIVERTCTL_STATS:
return (divert_sysctl_divstat(oldp, oldlenp, newp));
default:
return (sysctl_bounded_arr(divertctl_vars,
nitems(divertctl_vars), name, namelen, oldp, oldlenp,
newp, newlen));
}
/* NOTREACHED */
}
+9101
View File
File diff suppressed because it is too large. Load diff
+910
View File
@@ -0,0 +1,910 @@
/* PFLINUX port of OpenBSD 8.0 net/pf_if.c — see docs/porting-notes.md */
#include <pfk.h>
#define isupper(c) ((c) >= 'A' && (c) <= 'Z')
#define islower(c) ((c) >= 'a' && (c) <= 'z')
#define isalpha(c) (isupper(c)||islower(c))
struct pfi_kif *pfi_all = NULL;
struct pool pfi_addr_pl;
struct pfi_ifhead pfi_ifs;
long pfi_update = 1;
struct pfr_addr *pfi_buffer;
int pfi_buffer_cnt;
int pfi_buffer_max;
void pfi_kif_update(struct pfi_kif *);
void pfi_dynaddr_update(struct pfi_dynaddr *dyn);
void pfi_table_update(struct pfr_ktable *, struct pfi_kif *,
u_int8_t, int);
void pfi_kifaddr_update(void *);
void pfi_instance_add(struct ifnet *, u_int8_t, int);
void pfi_address_add(struct sockaddr *, sa_family_t, u_int8_t);
int pfi_if_compare(struct pfi_kif *, struct pfi_kif *);
int pfi_skip_if(const char *, struct pfi_kif *);
int pfi_unmask(void *);
void pfi_group_change(const char *);
RB_PROTOTYPE(pfi_ifhead, pfi_kif, pfik_tree, pfi_if_compare);
RB_GENERATE(pfi_ifhead, pfi_kif, pfik_tree, pfi_if_compare);
#define PFI_BUFFER_MAX 0x10000
#define PFI_MTYPE M_PF
struct pfi_kif *
pfi_kif_alloc(const char *kif_name, int mflags)
{
struct pfi_kif *kif;
kif = malloc(sizeof(*pfi_all), PFI_MTYPE, mflags|M_ZERO);
if (kif == NULL)
return (NULL);
strlcpy(kif->pfik_name, kif_name, sizeof(kif->pfik_name));
kif->pfik_tzero = gettime();
TAILQ_INIT(&kif->pfik_dynaddrs);
if (!strcmp(kif->pfik_name, "any")) {
/* both so it works in the ioctl and the regular case */
kif->pfik_flags |= PFI_IFLAG_ANY;
kif->pfik_flags_new |= PFI_IFLAG_ANY;
}
return (kif);
}
void
pfi_kif_free(struct pfi_kif *kif)
{
if (kif == NULL)
return;
if (kif->pfik_rules || kif->pfik_states || kif->pfik_routes ||
kif->pfik_srcnodes || kif->pfik_flagrefs)
panic("kif is still alive");
free(kif, PFI_MTYPE, sizeof(*kif));
}
void
pfi_initialize(void)
{
/*
* The first time we arrive here is during kernel boot,
* when if_attachsetup() for the first time. No locking
* is needed in this case, because it's granted there
* is a single thread, which sets pfi_all global var.
*/
if (pfi_all != NULL) /* already initialized */
return;
pool_init(&pfi_addr_pl, sizeof(struct pfi_dynaddr), 0, IPL_SOFTNET, 0,
"pfiaddrpl", NULL);
pfi_buffer_max = 64;
pfi_buffer = mallocarray(pfi_buffer_max, sizeof(*pfi_buffer),
PFI_MTYPE, M_WAITOK);
pfi_all = pfi_kif_alloc(IFG_ALL, M_WAITOK);
if (RB_INSERT(pfi_ifhead, &pfi_ifs, pfi_all) != NULL)
panic("IFG_ALL kif found already");
}
struct pfi_kif *
pfi_kif_find(const char *kif_name)
{
struct pfi_kif_cmp s;
PF_ASSERT_LOCKED();
memset(&s, 0, sizeof(s));
strlcpy(s.pfik_name, kif_name, sizeof(s.pfik_name));
return (RB_FIND(pfi_ifhead, &pfi_ifs, (struct pfi_kif *)&s));
}
struct pfi_kif *
pfi_kif_get(const char *kif_name, struct pfi_kif **prealloc)
{
struct pfi_kif *kif;
PF_ASSERT_LOCKED();
if ((kif = pfi_kif_find(kif_name)))
return (kif);
/* create new one */
if ((prealloc == NULL) || (*prealloc == NULL)) {
kif = pfi_kif_alloc(kif_name, M_NOWAIT);
if (kif == NULL)
return (NULL);
} else {
kif = *prealloc;
*prealloc = NULL;
}
RB_INSERT(pfi_ifhead, &pfi_ifs, kif);
return (kif);
}
void
pfi_kif_ref(struct pfi_kif *kif, enum pfi_kif_refs what)
{
PF_ASSERT_LOCKED();
switch (what) {
case PFI_KIF_REF_RULE:
kif->pfik_rules++;
break;
case PFI_KIF_REF_STATE:
kif->pfik_states++;
break;
case PFI_KIF_REF_ROUTE:
kif->pfik_routes++;
break;
case PFI_KIF_REF_SRCNODE:
kif->pfik_srcnodes++;
break;
case PFI_KIF_REF_FLAG:
kif->pfik_flagrefs++;
break;
default:
panic("pfi_kif_ref with unknown type");
}
}
void
pfi_kif_unref(struct pfi_kif *kif, enum pfi_kif_refs what)
{
if (kif == NULL)
return;
PF_ASSERT_LOCKED();
switch (what) {
case PFI_KIF_REF_NONE:
break;
case PFI_KIF_REF_RULE:
if (kif->pfik_rules <= 0) {
DPFPRINTF(LOG_ERR,
"pfi_kif_unref (%s): rules refcount <= 0",
kif->pfik_name);
return;
}
kif->pfik_rules--;
break;
case PFI_KIF_REF_STATE:
if (kif->pfik_states <= 0) {
DPFPRINTF(LOG_ERR,
"pfi_kif_unref (%s): state refcount <= 0",
kif->pfik_name);
return;
}
kif->pfik_states--;
break;
case PFI_KIF_REF_ROUTE:
if (kif->pfik_routes <= 0) {
DPFPRINTF(LOG_ERR,
"pfi_kif_unref (%s): route refcount <= 0",
kif->pfik_name);
return;
}
kif->pfik_routes--;
break;
case PFI_KIF_REF_SRCNODE:
if (kif->pfik_srcnodes <= 0) {
DPFPRINTF(LOG_ERR,
"pfi_kif_unref (%s): src-node refcount <= 0",
kif->pfik_name);
return;
}
kif->pfik_srcnodes--;
break;
case PFI_KIF_REF_FLAG:
if (kif->pfik_flagrefs <= 0) {
DPFPRINTF(LOG_ERR,
"pfi_kif_unref (%s): flags refcount <= 0",
kif->pfik_name);
return;
}
kif->pfik_flagrefs--;
break;
default:
panic("pfi_kif_unref (%s) with unknown type", kif->pfik_name);
}
if (kif->pfik_ifp != NULL || kif->pfik_group != NULL || kif == pfi_all)
return;
if (kif->pfik_rules || kif->pfik_states || kif->pfik_routes ||
kif->pfik_srcnodes || kif->pfik_flagrefs)
return;
RB_REMOVE(pfi_ifhead, &pfi_ifs, kif);
free(kif, PFI_MTYPE, sizeof(*kif));
}
int
pfi_kif_match(struct pfi_kif *rule_kif, struct pfi_kif *packet_kif)
{
struct ifg_list *p;
if (rule_kif == NULL || rule_kif == packet_kif)
return (1);
if (rule_kif->pfik_group != NULL)
TAILQ_FOREACH(p, &packet_kif->pfik_ifp->if_groups, ifgl_next)
if (p->ifgl_group == rule_kif->pfik_group)
return (1);
if (rule_kif->pfik_flags & PFI_IFLAG_ANY && packet_kif->pfik_ifp &&
!(packet_kif->pfik_ifp->if_flags & PFIFF_LOOPBACK))
return (1);
return (0);
}
void
pfi_attach_ifnet(struct ifnet *ifp)
{
struct pfi_kif *kif;
struct task *t;
PF_LOCK();
pfi_initialize();
pfi_update++;
if ((kif = pfi_kif_get(ifp->if_xname, NULL)) == NULL)
panic("%s: pfi_kif_get failed", __func__);
kif->pfik_ifp = ifp;
ifp->if_pf_kif = (caddr_t)kif;
t = malloc(sizeof(*t), PFI_MTYPE, M_WAITOK);
task_set(t, pfi_kifaddr_update, kif);
if_addrhook_add(ifp, t);
kif->pfik_ah_cookie = t;
pfi_kif_update(kif);
PF_UNLOCK();
}
void
pfi_detach_ifnet(struct ifnet *ifp)
{
struct pfi_kif *kif;
struct task *t;
if ((kif = (struct pfi_kif *)ifp->if_pf_kif) == NULL)
return;
PF_LOCK();
pfi_update++;
t = kif->pfik_ah_cookie;
kif->pfik_ah_cookie = NULL;
if_addrhook_del(ifp, t);
free(t, PFI_MTYPE, sizeof(*t));
pfi_kif_update(kif);
kif->pfik_ifp = NULL;
ifp->if_pf_kif = NULL;
pfi_kif_unref(kif, PFI_KIF_REF_NONE);
PF_UNLOCK();
}
void
pfi_attach_ifgroup(struct ifg_group *ifg)
{
struct pfi_kif *kif;
PF_LOCK();
pfi_initialize();
pfi_update++;
if ((kif = pfi_kif_get(ifg->ifg_group, NULL)) == NULL)
panic("%s: pfi_kif_get failed", __func__);
kif->pfik_group = ifg;
ifg->ifg_pf_kif = (caddr_t)kif;
PF_UNLOCK();
}
void
pfi_detach_ifgroup(struct ifg_group *ifg)
{
struct pfi_kif *kif;
if ((kif = (struct pfi_kif *)ifg->ifg_pf_kif) == NULL)
return;
PF_LOCK();
pfi_update++;
kif->pfik_group = NULL;
ifg->ifg_pf_kif = NULL;
pfi_kif_unref(kif, PFI_KIF_REF_NONE);
PF_UNLOCK();
}
void
pfi_group_change(const char *group)
{
struct pfi_kif *kif;
pfi_update++;
if ((kif = pfi_kif_get(group, NULL)) == NULL)
panic("%s: pfi_kif_get failed", __func__);
pfi_kif_update(kif);
}
void
pfi_group_delmember(const char *group)
{
PF_LOCK();
pfi_group_change(group);
pfi_xcommit();
PF_UNLOCK();
}
void
pfi_group_addmember(const char *group)
{
PF_LOCK();
pfi_group_change(group);
pfi_xcommit();
PF_UNLOCK();
}
int
pfi_match_addr(struct pfi_dynaddr *dyn, struct pf_addr *a, sa_family_t af)
{
switch (af) {
case AF_INET:
switch (dyn->pfid_acnt4) {
case 0:
return (0);
case 1:
return (pf_match_addr(0, &dyn->pfid_addr4,
&dyn->pfid_mask4, a, AF_INET));
default:
return (pfr_match_addr(dyn->pfid_kt, a, AF_INET));
}
break;
#ifdef INET6
case AF_INET6:
switch (dyn->pfid_acnt6) {
case 0:
return (0);
case 1:
return (pf_match_addr(0, &dyn->pfid_addr6,
&dyn->pfid_mask6, a, AF_INET6));
default:
return (pfr_match_addr(dyn->pfid_kt, a, AF_INET6));
}
break;
#endif /* INET6 */
default:
return (0);
}
}
int
pfi_dynaddr_setup(struct pf_addr_wrap *aw, sa_family_t af, int wait)
{
struct pfi_dynaddr *dyn;
char tblname[PF_TABLE_NAME_SIZE];
struct pf_ruleset *ruleset = NULL;
int rv = 0;
if (aw->type != PF_ADDR_DYNIFTL)
return (0);
if ((dyn = pool_get(&pfi_addr_pl, wait|PR_LIMITFAIL|PR_ZERO)) == NULL)
return (1);
if (!strcmp(aw->v.ifname, "self"))
dyn->pfid_kif = pfi_kif_get(IFG_ALL, NULL);
else
dyn->pfid_kif = pfi_kif_get(aw->v.ifname, NULL);
if (dyn->pfid_kif == NULL) {
rv = 1;
goto _bad;
}
pfi_kif_ref(dyn->pfid_kif, PFI_KIF_REF_RULE);
dyn->pfid_net = pfi_unmask(&aw->v.a.mask);
if (af == AF_INET && dyn->pfid_net == 32)
dyn->pfid_net = 128;
strlcpy(tblname, aw->v.ifname, sizeof(tblname));
if (aw->iflags & PFI_AFLAG_NETWORK)
strlcat(tblname, ":network", sizeof(tblname));
if (aw->iflags & PFI_AFLAG_BROADCAST)
strlcat(tblname, ":broadcast", sizeof(tblname));
if (aw->iflags & PFI_AFLAG_PEER)
strlcat(tblname, ":peer", sizeof(tblname));
if (aw->iflags & PFI_AFLAG_NOALIAS)
strlcat(tblname, ":0", sizeof(tblname));
if (dyn->pfid_net != 128)
snprintf(tblname + strlen(tblname),
sizeof(tblname) - strlen(tblname), "/%d", dyn->pfid_net);
if ((ruleset = pf_find_or_create_ruleset(PF_RESERVED_ANCHOR)) == NULL) {
rv = 1;
goto _bad;
}
if ((dyn->pfid_kt = pfr_attach_table(ruleset, tblname, wait)) == NULL) {
rv = 1;
goto _bad;
}
dyn->pfid_kt->pfrkt_flags |= PFR_TFLAG_ACTIVE;
dyn->pfid_iflags = aw->iflags;
dyn->pfid_af = af;
TAILQ_INSERT_TAIL(&dyn->pfid_kif->pfik_dynaddrs, dyn, entry);
aw->p.dyn = dyn;
pfi_kif_update(dyn->pfid_kif);
return (0);
_bad:
if (dyn->pfid_kt != NULL)
pfr_detach_table(dyn->pfid_kt);
if (ruleset != NULL)
pf_remove_if_empty_ruleset(ruleset);
if (dyn->pfid_kif != NULL)
pfi_kif_unref(dyn->pfid_kif, PFI_KIF_REF_RULE);
pool_put(&pfi_addr_pl, dyn);
return (rv);
}
void
pfi_kif_update(struct pfi_kif *kif)
{
struct ifg_list *ifgl;
struct pfi_dynaddr *p;
/* update all dynaddr */
TAILQ_FOREACH(p, &kif->pfik_dynaddrs, entry)
pfi_dynaddr_update(p);
/* again for all groups kif is member of */
if (kif->pfik_ifp != NULL)
TAILQ_FOREACH(ifgl, &kif->pfik_ifp->if_groups, ifgl_next)
pfi_kif_update((struct pfi_kif *)
ifgl->ifgl_group->ifg_pf_kif);
}
void
pfi_dynaddr_update(struct pfi_dynaddr *dyn)
{
struct pfi_kif *kif;
struct pfr_ktable *kt;
if (dyn == NULL || dyn->pfid_kif == NULL || dyn->pfid_kt == NULL)
panic("pfi_dynaddr_update");
kif = dyn->pfid_kif;
kt = dyn->pfid_kt;
if (kt->pfrkt_larg != pfi_update) {
/* this table needs to be brought up-to-date */
pfi_table_update(kt, kif, dyn->pfid_net, dyn->pfid_iflags);
kt->pfrkt_larg = pfi_update;
}
pfr_dynaddr_update(kt, dyn);
}
void
pfi_table_update(struct pfr_ktable *kt, struct pfi_kif *kif, u_int8_t net, int flags)
{
int e, size2 = 0;
struct ifg_member *ifgm;
pfi_buffer_cnt = 0;
if (kif->pfik_ifp != NULL)
pfi_instance_add(kif->pfik_ifp, net, flags);
else if (kif->pfik_group != NULL)
TAILQ_FOREACH(ifgm, &kif->pfik_group->ifg_members, ifgm_next)
pfi_instance_add(ifgm->ifgm_ifp, net, flags);
if ((e = pfr_set_addrs(&kt->pfrkt_t, pfi_buffer, pfi_buffer_cnt, &size2,
NULL, NULL, NULL, 0, PFR_TFLAG_ALLMASK)))
DPFPRINTF(LOG_ERR,
"pfi_table_update: cannot set %d new addresses "
"into table %s: %d", pfi_buffer_cnt, kt->pfrkt_name, e);
}
void
pfi_instance_add(struct ifnet *ifp, u_int8_t net, int flags)
{
struct ifaddr *ifa;
int got4 = 0, got6 = 0;
int net2, af;
if (ifp == NULL)
return;
TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
if (ifa->ifa_addr == NULL)
continue;
af = ifa->ifa_addr->sa_family;
if (af != AF_INET && af != AF_INET6)
continue;
if ((flags & PFI_AFLAG_BROADCAST) && af == AF_INET6)
continue;
if ((flags & PFI_AFLAG_BROADCAST) &&
!(ifp->if_flags & PFIFF_BROADCAST))
continue;
if ((flags & PFI_AFLAG_PEER) &&
!(ifp->if_flags & PFIFF_POINTOPOINT))
continue;
if ((flags & PFI_AFLAG_NETWORK) && af == AF_INET6 &&
IN6_IS_ADDR_LINKLOCAL(
&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr))
continue;
if (flags & PFI_AFLAG_NOALIAS) {
if (af == AF_INET && got4)
continue;
if (af == AF_INET6 && got6)
continue;
}
if (af == AF_INET)
got4 = 1;
else if (af == AF_INET6)
got6 = 1;
net2 = net;
if (net2 == 128 && (flags & PFI_AFLAG_NETWORK)) {
if (af == AF_INET)
net2 = pfi_unmask(&((struct sockaddr_in *)
ifa->ifa_netmask)->sin_addr);
else if (af == AF_INET6)
net2 = pfi_unmask(&((struct sockaddr_in6 *)
ifa->ifa_netmask)->sin6_addr);
}
if (af == AF_INET && net2 > 32)
net2 = 32;
if (flags & PFI_AFLAG_BROADCAST)
pfi_address_add(ifa->ifa_broadaddr, af, net2);
else if (flags & PFI_AFLAG_PEER)
pfi_address_add(ifa->ifa_dstaddr, af, net2);
else
pfi_address_add(ifa->ifa_addr, af, net2);
}
}
void
pfi_address_add(struct sockaddr *sa, sa_family_t af, u_int8_t net)
{
struct pfr_addr *p;
int i;
if (pfi_buffer_cnt >= pfi_buffer_max) {
int new_max = pfi_buffer_max * 2;
if (new_max > PFI_BUFFER_MAX) {
DPFPRINTF(LOG_ERR,
"pfi_address_add: address buffer full (%d/%d)",
pfi_buffer_cnt, PFI_BUFFER_MAX);
return;
}
p = mallocarray(new_max, sizeof(*pfi_buffer), PFI_MTYPE,
M_DONTWAIT);
if (p == NULL) {
DPFPRINTF(LOG_ERR,
"pfi_address_add: no memory to grow buffer "
"(%d/%d)", pfi_buffer_cnt, PFI_BUFFER_MAX);
return;
}
memcpy(p, pfi_buffer, pfi_buffer_max * sizeof(*pfi_buffer));
/* no need to zero buffer */
free(pfi_buffer, PFI_MTYPE, pfi_buffer_max * sizeof(*pfi_buffer));
pfi_buffer = p;
pfi_buffer_max = new_max;
}
if (af == AF_INET && net > 32)
net = 128;
p = pfi_buffer + pfi_buffer_cnt++;
memset(p, 0, sizeof(*p));
p->pfra_af = af;
p->pfra_net = net;
if (af == AF_INET)
p->pfra_ip4addr = ((struct sockaddr_in *)sa)->sin_addr;
else if (af == AF_INET6) {
p->pfra_ip6addr = ((struct sockaddr_in6 *)sa)->sin6_addr;
if (IN6_IS_SCOPE_EMBED(&p->pfra_ip6addr))
p->pfra_ip6addr.s6_addr16[1] = 0;
}
/* mask network address bits */
if (net < 128)
((caddr_t)p)[p->pfra_net/8] &= ~(0xFF >> (p->pfra_net%8));
for (i = (p->pfra_net+7)/8; i < sizeof(p->pfra_u); i++)
((caddr_t)p)[i] = 0;
}
void
pfi_dynaddr_remove(struct pf_addr_wrap *aw)
{
if (aw->type != PF_ADDR_DYNIFTL || aw->p.dyn == NULL ||
aw->p.dyn->pfid_kif == NULL || aw->p.dyn->pfid_kt == NULL)
return;
TAILQ_REMOVE(&aw->p.dyn->pfid_kif->pfik_dynaddrs, aw->p.dyn, entry);
pfi_kif_unref(aw->p.dyn->pfid_kif, PFI_KIF_REF_RULE);
aw->p.dyn->pfid_kif = NULL;
pfr_detach_table(aw->p.dyn->pfid_kt);
aw->p.dyn->pfid_kt = NULL;
pool_put(&pfi_addr_pl, aw->p.dyn);
aw->p.dyn = NULL;
}
void
pfi_dynaddr_copyout(struct pf_addr_wrap *aw)
{
if (aw->type != PF_ADDR_DYNIFTL || aw->p.dyn == NULL ||
aw->p.dyn->pfid_kif == NULL)
return;
aw->p.dyncnt = aw->p.dyn->pfid_acnt4 + aw->p.dyn->pfid_acnt6;
}
void
pfi_kifaddr_update(void *v)
{
struct pfi_kif *kif = (struct pfi_kif *)v;
NET_ASSERT_LOCKED();
PF_LOCK();
pfi_update++;
pfi_kif_update(kif);
PF_UNLOCK();
}
int
pfi_if_compare(struct pfi_kif *p, struct pfi_kif *q)
{
return (strncmp(p->pfik_name, q->pfik_name, IFNAMSIZ));
}
void
pfi_update_status(const char *name, struct pf_status *pfs)
{
struct pfi_kif *p;
struct pfi_kif_cmp key;
struct ifg_member p_member, *ifgm;
TAILQ_HEAD(, ifg_member) ifg_members;
int i, j, k;
if (*name == '\0' && pfs == NULL) {
RB_FOREACH(p, pfi_ifhead, &pfi_ifs) {
memset(p->pfik_packets, 0, sizeof(p->pfik_packets));
memset(p->pfik_bytes, 0, sizeof(p->pfik_bytes));
p->pfik_tzero = gettime();
}
return;
}
strlcpy(key.pfik_name, name, sizeof(key.pfik_name));
p = RB_FIND(pfi_ifhead, &pfi_ifs, (struct pfi_kif *)&key);
if (p == NULL) {
return;
}
if (p->pfik_group != NULL) {
memcpy(&ifg_members, &p->pfik_group->ifg_members,
sizeof(ifg_members));
} else {
/* build a temporary list for p only */
memset(&p_member, 0, sizeof(p_member));
p_member.ifgm_ifp = p->pfik_ifp;
TAILQ_INIT(&ifg_members);
TAILQ_INSERT_TAIL(&ifg_members, &p_member, ifgm_next);
}
if (pfs) {
memset(pfs->pcounters, 0, sizeof(pfs->pcounters));
memset(pfs->bcounters, 0, sizeof(pfs->bcounters));
}
TAILQ_FOREACH(ifgm, &ifg_members, ifgm_next) {
if (ifgm->ifgm_ifp == NULL)
continue;
p = (struct pfi_kif *)ifgm->ifgm_ifp->if_pf_kif;
/* just clear statistics */
if (pfs == NULL) {
memset(p->pfik_packets, 0, sizeof(p->pfik_packets));
memset(p->pfik_bytes, 0, sizeof(p->pfik_bytes));
p->pfik_tzero = gettime();
continue;
}
for (i = 0; i < 2; i++)
for (j = 0; j < 2; j++)
for (k = 0; k < 2; k++) {
pfs->pcounters[i][j][k] +=
p->pfik_packets[i][j][k];
pfs->bcounters[i][j] +=
p->pfik_bytes[i][j][k];
}
}
}
void
pfi_get_ifaces(const char *name, struct pfi_kif *buf, int *size)
{
struct pfi_kif *p;
int n = 0;
RB_FOREACH(p, pfi_ifhead, &pfi_ifs) {
if (pfi_skip_if(name, p))
continue;
if (n >= *size)
break;
n++;
if (!p->pfik_tzero)
p->pfik_tzero = gettime();
memcpy(buf++, p, sizeof(*buf));
}
*size = n;
}
int
pfi_skip_if(const char *filter, struct pfi_kif *p)
{
struct ifg_list *i;
int n;
PF_ASSERT_LOCKED();
if (filter == NULL || !*filter)
return (0);
if (!strcmp(p->pfik_name, filter))
return (0); /* exact match */
n = strlen(filter);
if (n < 1 || n >= IFNAMSIZ)
return (1); /* sanity check */
if (filter[n-1] >= '0' && filter[n-1] <= '9')
return (1); /* group names may not end in a digit */
if (p->pfik_ifp != NULL)
TAILQ_FOREACH(i, &p->pfik_ifp->if_groups, ifgl_next)
if (!strncmp(i->ifgl_group->ifg_group, filter, IFNAMSIZ))
return (0); /* iface is in group "filter" */
return (1);
}
int
pfi_set_flags(const char *name, int flags)
{
struct pfi_kif *p;
size_t n;
PF_ASSERT_LOCKED();
if (name != NULL && name[0] != '\0') {
p = pfi_kif_find(name);
if (p == NULL) {
n = strlen(name);
if (n < 1 || n >= IFNAMSIZ)
return (EINVAL);
if (!isalpha(name[0]))
return (EINVAL);
p = pfi_kif_get(name, NULL);
if (p != NULL) {
p->pfik_flags_new = p->pfik_flags | flags;
/*
* We use pfik_flagrefs counter as an
* indication whether the kif has been created
* on behalf of 'pfi_set_flags()' or not.
*/
KASSERT(p->pfik_flagrefs == 0);
if (ISSET(p->pfik_flags_new, PFI_IFLAG_SKIP))
pfi_kif_ref(p, PFI_KIF_REF_FLAG);
} else
panic("%s pfi_kif_get() returned NULL\n",
__func__);
} else {
/*
* pf.conf may accidentally contain two set skip on ...
* statements. For example:
* set skip lo
* set skip lo
* We need to grab reference only when skip flag is
* set to avoid tripping assert pfi_clear_flags()
*/
if (ISSET(flags, PFI_IFLAG_SKIP) &&
!ISSET(p->pfik_flags_new, PFI_IFLAG_SKIP) &&
!ISSET(p->pfik_flags, PFI_IFLAG_SKIP))
pfi_kif_ref(p, PFI_KIF_REF_FLAG);
p->pfik_flags_new = p->pfik_flags | flags;
}
} else {
RB_FOREACH(p, pfi_ifhead, &pfi_ifs)
p->pfik_flags_new = p->pfik_flags | flags;
}
return (0);
}
int
pfi_clear_flags(const char *name, int flags)
{
struct pfi_kif *p, *w;
PF_ASSERT_LOCKED();
if (name != NULL && name[0] != '\0') {
p = pfi_kif_find(name);
if (p != NULL) {
p->pfik_flags_new = p->pfik_flags & ~flags;
KASSERT((p->pfik_flagrefs == 0) ||
(p->pfik_flagrefs == 1));
if (!ISSET(p->pfik_flags_new, PFI_IFLAG_SKIP) &&
(p->pfik_flagrefs == 1))
pfi_kif_unref(p, PFI_KIF_REF_FLAG);
} else
return (ESRCH);
} else
RB_FOREACH_SAFE(p, pfi_ifhead, &pfi_ifs, w) {
p->pfik_flags_new = p->pfik_flags & ~flags;
KASSERT((p->pfik_flagrefs == 0) ||
(p->pfik_flagrefs == 1));
if (!ISSET(p->pfik_flags_new, PFI_IFLAG_SKIP) &&
(p->pfik_flagrefs == 1))
pfi_kif_unref(p, PFI_KIF_REF_FLAG);
}
return (0);
}
void
pfi_xcommit(void)
{
struct pfi_kif *p, *gkif;
struct ifg_list *g;
struct ifnet *ifp;
size_t n;
PF_ASSERT_LOCKED();
RB_FOREACH(p, pfi_ifhead, &pfi_ifs) {
p->pfik_flags = p->pfik_flags_new;
n = strlen(p->pfik_name);
ifp = p->pfik_ifp;
/*
* if kif is backed by existing interface, then we must use
* skip flags found in groups. We use pfik_flags_new, otherwise
* we would need to do two RB_FOREACH() passes: the first to
* commit group changes the second to commit flag changes for
* interfaces.
*/
if (ifp != NULL)
TAILQ_FOREACH(g, &ifp->if_groups, ifgl_next) {
gkif =
(struct pfi_kif *)g->ifgl_group->ifg_pf_kif;
KASSERT(gkif != NULL);
p->pfik_flags |= gkif->pfik_flags_new;
}
}
}
/* from pf_print_state.c */
int
pfi_unmask(void *addr)
{
struct pf_addr *m = addr;
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);
}
+218
View File
@@ -0,0 +1,218 @@
/* PFLINUX port of OpenBSD 8.0 net/pf_ioctl.c — see docs/porting-notes.md */
#include <pfk.h>
struct pf_statelim_kstat {
struct kstat_kv name;
struct kstat_kv inuse;
struct kstat_kv limit;
struct kstat_kv admitted;
struct kstat_kv hardlimited;
struct kstat_kv ratelimited;
};
static int
pf_statelim_kstat_read(struct kstat *ks)
{
struct pf_statelim *pfstlim = ks->ks_softc;
struct pf_statelim_kstat *d = ks->ks_data;
unsigned int gen;
strlcpy(kstat_kv_istr(&d->name), pfstlim->pfstlim_nm,
sizeof(kstat_kv_istr(&d->name)));
kstat_kv_u32(&d->limit) = pfstlim->pfstlim_limit;
pc_cons_enter(&pfstlim->pfstlim_lock, &gen);
do {
kstat_kv_u32(&d->inuse) = pfstlim->pfstlim_inuse;
kstat_kv_u64(&d->admitted) =
pfstlim->pfstlim_counters.admitted;
kstat_kv_u64(&d->hardlimited) =
pfstlim->pfstlim_counters.hardlimited;
kstat_kv_u64(&d->ratelimited) =
pfstlim->pfstlim_counters.ratelimited;
} while (pc_cons_leave(&pfstlim->pfstlim_lock, &gen) != 0);
nanouptime(&ks->ks_updated);
return (0);
};
static void
pf_statelim_kstat_attach(struct pf_statelim *pfstlim)
{
struct kstat *ks = pfstlim->pfstlim_ks;
struct pf_statelim_kstat *d;
if (ks != NULL)
return;
d = malloc(sizeof(*d), M_DEVBUF, M_WAITOK|M_CANFAIL|M_ZERO);
if (d == NULL) {
printf("pf: unable to allocate state-limiter kstat\n");
return;
}
ks = kstat_create("pf", 0, "state-limiter", pfstlim->pfstlim_id,
KSTAT_T_KV, 0);
if (ks == NULL) {
printf("pf: unable to create state-limiter kstat\n");
free(d, M_DEVBUF, sizeof(*d));
return;
}
kstat_kv_init(&d->name, "name", KSTAT_KV_T_ISTR);
kstat_kv_init(&d->inuse, "in-use", KSTAT_KV_T_UINT32);
kstat_kv_init(&d->limit, "limit", KSTAT_KV_T_UINT32);
kstat_kv_init(&d->admitted, "admitted", KSTAT_KV_T_COUNTER64);
kstat_kv_init(&d->hardlimited, "hard-limited", KSTAT_KV_T_COUNTER64);
kstat_kv_init(&d->ratelimited, "rate-limited", KSTAT_KV_T_COUNTER64);
ks->ks_softc = pfstlim;
ks->ks_data = d;
ks->ks_datalen = sizeof(*d);
ks->ks_read = pf_statelim_kstat_read;
kstat_install(ks);
pfstlim->pfstlim_ks = ks;
}
static void
pf_statelim_kstat_detach(struct pf_statelim *pfstlim)
{
struct kstat *ks = pfstlim->pfstlim_ks;
struct pf_statelim_kstat *d;
if (ks == NULL)
return;
kstat_remove(ks);
d = ks->ks_data;
kstat_destroy(ks);
free(d, M_DEVBUF, sizeof(*d));
}
struct pf_sourcelim_kstat {
struct kstat_kv name;
/* pf_source/address/prefix counters */
struct kstat_kv sourceinuse;
struct kstat_kv sourcelimit;
struct kstat_kv sourceallocs;
struct kstat_kv sourcelimited;
struct kstat_kv sourcenomem;
/* state counters */
struct kstat_kv inuse;
struct kstat_kv limit;
struct kstat_kv admitted;
struct kstat_kv hardlimited;
struct kstat_kv ratelimited;
};
static int
pf_sourcelim_kstat_read(struct kstat *ks)
{
struct pf_sourcelim *pfsrlim = ks->ks_softc;
struct pf_sourcelim_kstat *d = ks->ks_data;
unsigned int gen;
strlcpy(kstat_kv_istr(&d->name), pfsrlim->pfsrlim_nm,
sizeof(kstat_kv_istr(&d->name)));
kstat_kv_u32(&d->sourcelimit) = pfsrlim->pfsrlim_entries;
kstat_kv_u32(&d->limit) = pfsrlim->pfsrlim_limit;
pc_cons_enter(&pfsrlim->pfsrlim_lock, &gen);
do {
kstat_kv_u32(&d->sourceinuse) =
pfsrlim->pfsrlim_nsources;
kstat_kv_u64(&d->sourceallocs) =
pfsrlim->pfsrlim_counters.addrallocs;
kstat_kv_u64(&d->sourcelimited) =
pfsrlim->pfsrlim_counters.addrlimited;
kstat_kv_u64(&d->sourcenomem) =
pfsrlim->pfsrlim_counters.addrnomem;
kstat_kv_u32(&d->inuse) =
pfsrlim->pfsrlim_counters.inuse;
kstat_kv_u64(&d->admitted) =
pfsrlim->pfsrlim_counters.admitted;
kstat_kv_u64(&d->hardlimited) =
pfsrlim->pfsrlim_counters.hardlimited;
kstat_kv_u64(&d->ratelimited) =
pfsrlim->pfsrlim_counters.ratelimited;
} while (pc_cons_leave(&pfsrlim->pfsrlim_lock, &gen) != 0);
nanouptime(&ks->ks_updated);
return (0);
};
static void
pf_sourcelim_kstat_attach(struct pf_sourcelim *pfsrlim)
{
struct kstat *ks = pfsrlim->pfsrlim_ks;
struct pf_sourcelim_kstat *d;
if (ks != NULL)
return;
d = malloc(sizeof(*d), M_DEVBUF, M_WAITOK|M_CANFAIL|M_ZERO);
if (d == NULL) {
printf("pf: unable to allocate source-limiter kstat\n");
return;
}
ks = kstat_create("pf", 0, "source-limiter", pfsrlim->pfsrlim_id,
KSTAT_T_KV, 0);
if (ks == NULL) {
printf("pf: unable to create source-limiter kstat\n");
free(d, M_DEVBUF, sizeof(*d));
return;
}
kstat_kv_init(&d->name, "name", KSTAT_KV_T_ISTR);
kstat_kv_init(&d->sourceinuse, "source-in-use", KSTAT_KV_T_UINT32);
kstat_kv_init(&d->sourcelimit, "source-limit", KSTAT_KV_T_UINT32);
kstat_kv_init(&d->sourceallocs, "source-allocs",
KSTAT_KV_T_COUNTER64);
kstat_kv_init(&d->sourcelimited, "source-limited",
KSTAT_KV_T_COUNTER64);
kstat_kv_init(&d->sourcenomem, "source-nomem",
KSTAT_KV_T_COUNTER64);
kstat_kv_init(&d->inuse, "in-use", KSTAT_KV_T_UINT32);
kstat_kv_init(&d->limit, "limit", KSTAT_KV_T_UINT32);
kstat_kv_init(&d->admitted, "admitted", KSTAT_KV_T_COUNTER64);
kstat_kv_init(&d->hardlimited, "hard-limited", KSTAT_KV_T_COUNTER64);
kstat_kv_init(&d->ratelimited, "rate-limited", KSTAT_KV_T_COUNTER64);
ks->ks_softc = pfsrlim;
ks->ks_data = d;
ks->ks_datalen = sizeof(*d);
ks->ks_read = pf_sourcelim_kstat_read;
kstat_install(ks);
pfsrlim->pfsrlim_ks = ks;
}
static void
pf_sourcelim_kstat_detach(struct pf_sourcelim *pfsrlim)
{
struct kstat *ks = pfsrlim->pfsrlim_ks;
struct pf_sourcelim_kstat *d;
if (ks == NULL)
return;
kstat_remove(ks);
d = ks->ks_data;
kstat_destroy(ks);
free(d, M_DEVBUF, sizeof(*d));
}
#endif /* NKSTAT > 0 */
+945
View File
@@ -0,0 +1,945 @@
/* PFLINUX port of OpenBSD 8.0 net/pf_lb.c — see docs/porting-notes.md */
#include <pfk.h>
u_int64_t pf_hash(struct pf_addr *, struct pf_addr *,
struct pf_poolhashkey *, sa_family_t);
int pf_get_sport(struct pf_pdesc *, struct pf_rule *,
struct pf_addr *, u_int16_t *, u_int16_t,
u_int16_t, struct pf_src_node **);
int pf_map_addr_states_increase(sa_family_t,
struct pf_pool *, struct pf_addr *);
int pf_get_transaddr_af(struct pf_rule *,
struct pf_pdesc *, struct pf_src_node **);
int pf_map_addr_sticky(sa_family_t, struct pf_rule *,
struct pf_addr *, struct pf_addr *,
struct pf_src_node **, struct pf_pool *,
enum pf_sn_types);
int pf_pool_states_decrease_addr(struct pf_pool *, int,
struct pf_addr *);
u_int64_t
pf_hash(struct pf_addr *inaddr, struct pf_addr *hash,
struct pf_poolhashkey *key, sa_family_t af)
{
uint64_t res = 0;
#ifdef INET6
union {
uint64_t hash64;
uint32_t hash32[2];
} h;
#endif /* INET6 */
switch (af) {
case AF_INET:
res = SipHash24((SIPHASH_KEY *)key,
&inaddr->addr32[0], sizeof(inaddr->addr32[0]));
hash->addr32[0] = res;
break;
#ifdef INET6
case AF_INET6:
res = SipHash24((SIPHASH_KEY *)key, &inaddr->addr32[0],
4 * sizeof(inaddr->addr32[0]));
h.hash64 = res;
hash->addr32[0] = h.hash32[0];
hash->addr32[1] = h.hash32[1];
/*
* siphash isn't big enough, but flipping it around is
* good enough here.
*/
hash->addr32[2] = ~h.hash32[1];
hash->addr32[3] = ~h.hash32[0];
break;
#endif /* INET6 */
default:
unhandled_af(af);
}
return (res);
}
int
pf_get_sport(struct pf_pdesc *pd, struct pf_rule *r,
struct pf_addr *naddr, u_int16_t *nport, u_int16_t low, u_int16_t high,
struct pf_src_node **sn)
{
struct pf_state_key_cmp key;
struct pf_addr init_addr;
u_int16_t cut;
int dir = (pd->dir == PF_IN) ? PF_OUT : PF_IN;
int sidx = pd->sidx;
int didx = pd->didx;
memset(&init_addr, 0, sizeof(init_addr));
if (pf_map_addr(pd->naf, r, &pd->nsaddr, naddr, &init_addr, sn, &r->nat,
PF_SN_NAT))
return (1);
if (pd->proto == IPPROTO_ICMP) {
if (pd->ndport == htons(ICMP_ECHO)) {
low = 1;
high = 65535;
} else
return (0); /* Don't try to modify non-echo ICMP */
}
#ifdef INET6
if (pd->proto == IPPROTO_ICMPV6) {
if (pd->ndport == htons(ICMP6_ECHO_REQUEST)) {
low = 1;
high = 65535;
} else
return (0); /* Don't try to modify non-echo ICMP */
}
#endif /* INET6 */
do {
key.af = pd->naf;
key.proto = pd->proto;
key.rdomain = pd->rdomain;
pf_addrcpy(&key.addr[didx], &pd->ndaddr, key.af);
pf_addrcpy(&key.addr[sidx], naddr, key.af);
key.port[didx] = pd->ndport;
/*
* port search; start random, step;
* similar 2 portloop in in_pcbbind
*/
if (!(pd->proto == IPPROTO_TCP || pd->proto == IPPROTO_UDP ||
pd->proto == IPPROTO_ICMP || pd->proto == IPPROTO_ICMPV6)) {
/* XXX bug: icmp states dont use the id on both
* XXX sides (traceroute -I through nat) */
key.port[sidx] = pd->nsport;
key.hash = pf_pkt_hash(key.af, key.proto, &key.addr[0],
&key.addr[1], key.port[0], key.port[1]);
if (pf_find_state_all(&key, dir, NULL) == NULL) {
*nport = pd->nsport;
return (0);
}
} else if (low == 0 && high == 0) {
key.port[sidx] = pd->nsport;
key.hash = pf_pkt_hash(key.af, key.proto, &key.addr[0],
&key.addr[1], key.port[0], key.port[1]);
if (pf_find_state_all(&key, dir, NULL) == NULL) {
*nport = pd->nsport;
return (0);
}
} else if (low == high) {
key.port[sidx] = htons(low);
key.hash = pf_pkt_hash(key.af, key.proto, &key.addr[0],
&key.addr[1], key.port[0], key.port[1]);
if (pf_find_state_all(&key, dir, NULL) == NULL) {
*nport = htons(low);
return (0);
}
} else {
u_int32_t tmp;
if (low > high) {
tmp = low;
low = high;
high = tmp;
}
/* low < high */
cut = arc4random_uniform(1 + high - low) + low;
/* low <= cut <= high */
for (tmp = cut; tmp <= high && tmp <= 0xffff; ++tmp) {
key.port[sidx] = htons(tmp);
key.hash = pf_pkt_hash(key.af, key.proto,
&key.addr[0], &key.addr[1], key.port[0],
key.port[1]);
if (pf_find_state_all(&key, dir, NULL) ==
NULL && !in_baddynamic(tmp, pd->proto)) {
*nport = htons(tmp);
return (0);
}
}
tmp = cut;
for (tmp -= 1; tmp >= low && tmp <= 0xffff; --tmp) {
key.port[sidx] = htons(tmp);
key.hash = pf_pkt_hash(key.af, key.proto,
&key.addr[0], &key.addr[1], key.port[0],
key.port[1]);
if (pf_find_state_all(&key, dir, NULL) ==
NULL && !in_baddynamic(tmp, pd->proto)) {
*nport = htons(tmp);
return (0);
}
}
}
switch (r->nat.opts & PF_POOL_TYPEMASK) {
case PF_POOL_RANDOM:
case PF_POOL_ROUNDROBIN:
case PF_POOL_LEASTSTATES:
/*
* pick a different source address since we're out
* of free port choices for the current one.
*/
if (pf_map_addr(pd->naf, r, &pd->nsaddr, naddr,
&init_addr, sn, &r->nat, PF_SN_NAT))
return (1);
break;
case PF_POOL_NONE:
case PF_POOL_SRCHASH:
case PF_POOL_BITMASK:
default:
return (1);
}
} while (! PF_AEQ(&init_addr, naddr, pd->naf) );
return (1); /* none available */
}
int
pf_map_addr_sticky(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr,
struct pf_addr *naddr, struct pf_src_node **sns, struct pf_pool *rpool,
enum pf_sn_types type)
{
struct pf_addr *raddr, *rmask, *cached;
struct pf_state *s;
struct pf_src_node k;
int valid;
k.af = af;
k.type = type;
pf_addrcpy(&k.addr, saddr, af);
k.rule.ptr = r;
pf_status.scounters[SCNT_SRC_NODE_SEARCH]++;
sns[type] = RB_FIND(pf_src_tree, &tree_src_tracking, &k);
if (sns[type] == NULL)
return (-1);
/* check if the cached entry is still valid */
cached = &(sns[type])->raddr;
valid = 0;
if (PF_AZERO(cached, af)) {
valid = 1;
} else if (rpool->addr.type == PF_ADDR_DYNIFTL) {
if (pfr_kentry_byaddr(rpool->addr.p.dyn->pfid_kt, cached,
af, 0))
valid = 1;
} else if (rpool->addr.type == PF_ADDR_TABLE) {
if (pfr_kentry_byaddr(rpool->addr.p.tbl, cached, af, 0))
valid = 1;
} else if (rpool->addr.type != PF_ADDR_NOROUTE) {
raddr = &rpool->addr.v.a.addr;
rmask = &rpool->addr.v.a.mask;
valid = pf_match_addr(0, raddr, rmask, cached, af);
}
if (!valid) {
if (pf_status.debug >= LOG_DEBUG) {
log(LOG_DEBUG, "pf: pf_map_addr: "
"stale src tracking (%u) ", type);
pf_print_host(&k.addr, 0, af);
addlog(" to ");
pf_print_host(cached, 0, af);
addlog("\n");
}
if (sns[type]->states != 0) {
/* XXX expensive */
RBT_FOREACH(s, pf_state_tree_id, &tree_id)
pf_state_rm_src_node(s, sns[type]);
}
sns[type]->expire = 1;
pf_remove_src_node(sns[type]);
sns[type] = NULL;
return (-1);
}
if (!PF_AZERO(cached, af)) {
pf_addrcpy(naddr, cached, af);
if ((rpool->opts & PF_POOL_TYPEMASK) == PF_POOL_LEASTSTATES &&
pf_map_addr_states_increase(af, rpool, cached) == -1)
return (-1);
}
if (pf_status.debug >= LOG_DEBUG) {
log(LOG_DEBUG, "pf: pf_map_addr: "
"src tracking (%u) maps ", type);
pf_print_host(&k.addr, 0, af);
addlog(" to ");
pf_print_host(naddr, 0, af);
addlog("\n");
}
if (sns[type]->kif != NULL)
rpool->kif = sns[type]->kif;
return (0);
}
uint32_t
pf_rand_addr(uint32_t mask)
{
uint32_t addr;
mask = ~ntohl(mask);
addr = arc4random_uniform(mask + 1);
return (htonl(addr));
}
int
pf_map_addr(sa_family_t af, struct pf_rule *r, struct pf_addr *saddr,
struct pf_addr *naddr, struct pf_addr *init_addr, struct pf_src_node **sns,
struct pf_pool *rpool, enum pf_sn_types type)
{
struct pf_addr hash;
struct pf_addr faddr;
struct pf_addr *raddr = &rpool->addr.v.a.addr;
struct pf_addr *rmask = &rpool->addr.v.a.mask;
struct pfr_ktable *kt;
struct pfi_kif *kif;
u_int64_t states;
u_int16_t weight;
u_int64_t load;
u_int64_t cload;
u_int64_t hashidx;
int cnt;
if (sns[type] == NULL && rpool->opts & PF_POOL_STICKYADDR &&
(rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE &&
pf_map_addr_sticky(af, r, saddr, naddr, sns, rpool, type) == 0)
return (0);
if (rpool->addr.type == PF_ADDR_NOROUTE)
return (1);
if (rpool->addr.type == PF_ADDR_DYNIFTL) {
switch (af) {
case AF_INET:
if (rpool->addr.p.dyn->pfid_acnt4 < 1 &&
!PF_POOL_DYNTYPE(rpool->opts))
return (1);
raddr = &rpool->addr.p.dyn->pfid_addr4;
rmask = &rpool->addr.p.dyn->pfid_mask4;
break;
#ifdef INET6
case AF_INET6:
if (rpool->addr.p.dyn->pfid_acnt6 < 1 &&
!PF_POOL_DYNTYPE(rpool->opts))
return (1);
raddr = &rpool->addr.p.dyn->pfid_addr6;
rmask = &rpool->addr.p.dyn->pfid_mask6;
break;
#endif /* INET6 */
default:
unhandled_af(af);
}
} else if (rpool->addr.type == PF_ADDR_TABLE) {
if (!PF_POOL_DYNTYPE(rpool->opts))
return (1); /* unsupported */
} else {
raddr = &rpool->addr.v.a.addr;
rmask = &rpool->addr.v.a.mask;
}
switch (rpool->opts & PF_POOL_TYPEMASK) {
case PF_POOL_NONE:
pf_addrcpy(naddr, raddr, af);
break;
case PF_POOL_BITMASK:
pf_poolmask(naddr, raddr, rmask, saddr, af);
break;
case PF_POOL_RANDOM:
if (rpool->addr.type == PF_ADDR_TABLE ||
rpool->addr.type == PF_ADDR_DYNIFTL) {
if (rpool->addr.type == PF_ADDR_TABLE)
kt = rpool->addr.p.tbl;
else
kt = rpool->addr.p.dyn->pfid_kt;
kt = pfr_ktable_select_active(kt);
if (kt == NULL)
return (1);
cnt = kt->pfrkt_cnt;
if (cnt == 0)
rpool->tblidx = 0;
else
rpool->tblidx = (int)arc4random_uniform(cnt);
memset(&rpool->counter, 0, sizeof(rpool->counter));
if (pfr_pool_get(rpool, &raddr, &rmask, af))
return (1);
pf_addrcpy(naddr, &rpool->counter, af);
} else if (init_addr != NULL && PF_AZERO(init_addr, af)) {
switch (af) {
case AF_INET:
rpool->counter.addr32[0] = pf_rand_addr(
rmask->addr32[0]);
break;
#ifdef INET6
case AF_INET6:
if (rmask->addr32[3] != 0xffffffff)
rpool->counter.addr32[3] = pf_rand_addr(
rmask->addr32[3]);
else
break;
if (rmask->addr32[2] != 0xffffffff)
rpool->counter.addr32[2] = pf_rand_addr(
rmask->addr32[2]);
else
break;
if (rmask->addr32[1] != 0xffffffff)
rpool->counter.addr32[1] = pf_rand_addr(
rmask->addr32[1]);
else
break;
if (rmask->addr32[0] != 0xffffffff)
rpool->counter.addr32[0] = pf_rand_addr(
rmask->addr32[0]);
break;
#endif /* INET6 */
default:
unhandled_af(af);
}
pf_poolmask(naddr, raddr, rmask, &rpool->counter, af);
pf_addrcpy(init_addr, naddr, af);
} else {
pf_addr_inc(&rpool->counter, af);
pf_poolmask(naddr, raddr, rmask, &rpool->counter, af);
}
break;
case PF_POOL_SRCHASH:
hashidx = pf_hash(saddr, &hash, &rpool->key, af);
if (rpool->addr.type == PF_ADDR_TABLE ||
rpool->addr.type == PF_ADDR_DYNIFTL) {
if (rpool->addr.type == PF_ADDR_TABLE)
kt = rpool->addr.p.tbl;
else
kt = rpool->addr.p.dyn->pfid_kt;
kt = pfr_ktable_select_active(kt);
if (kt == NULL)
return (1);
cnt = kt->pfrkt_cnt;
if (cnt == 0)
rpool->tblidx = 0;
else
rpool->tblidx = (int)(hashidx % cnt);
memset(&rpool->counter, 0, sizeof(rpool->counter));
if (pfr_pool_get(rpool, &raddr, &rmask, af))
return (1);
pf_addrcpy(naddr, &rpool->counter, af);
} else {
pf_poolmask(naddr, raddr, rmask, &hash, af);
}
break;
case PF_POOL_ROUNDROBIN:
if (rpool->addr.type == PF_ADDR_TABLE ||
rpool->addr.type == PF_ADDR_DYNIFTL) {
if (pfr_pool_get(rpool, &raddr, &rmask, af)) {
/*
* reset counter in case its value
* has been removed from the pool.
*/
memset(&rpool->counter, 0,
sizeof(rpool->counter));
if (pfr_pool_get(rpool, &raddr, &rmask, af))
return (1);
}
} else if (PF_AZERO(&rpool->counter, af)) {
/*
* fall back to POOL_NONE if there is a single host
* address in pool.
*/
if (af == AF_INET &&
rmask->addr32[0] == INADDR_BROADCAST) {
pf_addrcpy(naddr, raddr, af);
break;
}
#ifdef INET6
if (af == AF_INET6 &&
IN6_ARE_ADDR_EQUAL(&rmask->v6, &in6mask128)) {
pf_addrcpy(naddr, raddr, af);
break;
}
#endif
} else if (pf_match_addr(0, raddr, rmask, &rpool->counter, af))
return (1);
/* iterate over table if it contains entries which are weighted */
if ((rpool->addr.type == PF_ADDR_TABLE &&
rpool->addr.p.tbl->pfrkt_refcntcost > 0) ||
(rpool->addr.type == PF_ADDR_DYNIFTL &&
rpool->addr.p.dyn->pfid_kt->pfrkt_refcntcost > 0)) {
do {
if (rpool->addr.type == PF_ADDR_TABLE ||
rpool->addr.type == PF_ADDR_DYNIFTL) {
if (pfr_pool_get(rpool,
&raddr, &rmask, af))
return (1);
} else {
log(LOG_ERR, "pf: pf_map_addr: "
"weighted RR failure");
return (1);
}
if (rpool->weight >= rpool->curweight)
break;
pf_addr_inc(&rpool->counter, af);
} while (1);
weight = rpool->weight;
}
pf_poolmask(naddr, raddr, rmask, &rpool->counter, af);
if (init_addr != NULL && PF_AZERO(init_addr, af))
pf_addrcpy(init_addr, &rpool->counter, af);
pf_addr_inc(&rpool->counter, af);
break;
case PF_POOL_LEASTSTATES:
/* retrieve an address first */
if (rpool->addr.type == PF_ADDR_TABLE ||
rpool->addr.type == PF_ADDR_DYNIFTL) {
if (pfr_pool_get(rpool, &raddr, &rmask, af)) {
/* see PF_POOL_ROUNDROBIN */
memset(&rpool->counter, 0,
sizeof(rpool->counter));
if (pfr_pool_get(rpool, &raddr, &rmask, af))
return (1);
}
} else if (pf_match_addr(0, raddr, rmask, &rpool->counter, af))
return (1);
states = rpool->states;
weight = rpool->weight;
kif = rpool->kif;
if ((rpool->addr.type == PF_ADDR_TABLE &&
rpool->addr.p.tbl->pfrkt_refcntcost > 0) ||
(rpool->addr.type == PF_ADDR_DYNIFTL &&
rpool->addr.p.dyn->pfid_kt->pfrkt_refcntcost > 0))
load = ((UINT16_MAX * rpool->states) / rpool->weight);
else
load = states;
pf_addrcpy(&faddr, &rpool->counter, af);
pf_addrcpy(naddr, &rpool->counter, af);
if (init_addr != NULL && PF_AZERO(init_addr, af))
pf_addrcpy(init_addr, naddr, af);
/*
* iterate *once* over whole table and find destination with
* least connection
*/
do {
pf_addr_inc(&rpool->counter, af);
if (rpool->addr.type == PF_ADDR_TABLE ||
rpool->addr.type == PF_ADDR_DYNIFTL) {
if (pfr_pool_get(rpool, &raddr, &rmask, af))
return (1);
} else if (pf_match_addr(0, raddr, rmask,
&rpool->counter, af))
return (1);
if ((rpool->addr.type == PF_ADDR_TABLE &&
rpool->addr.p.tbl->pfrkt_refcntcost > 0) ||
(rpool->addr.type == PF_ADDR_DYNIFTL &&
rpool->addr.p.dyn->pfid_kt->pfrkt_refcntcost > 0))
cload = ((UINT16_MAX * rpool->states)
/ rpool->weight);
else
cload = rpool->states;
/* find lc minimum */
if (cload < load) {
states = rpool->states;
weight = rpool->weight;
kif = rpool->kif;
load = cload;
pf_addrcpy(naddr, &rpool->counter, af);
if (init_addr != NULL &&
PF_AZERO(init_addr, af))
pf_addrcpy(init_addr, naddr, af);
}
} while (pf_match_addr(1, &faddr, rmask, &rpool->counter, af) &&
(states > 0));
if (pf_map_addr_states_increase(af, rpool, naddr) == -1)
return (1);
/* revert the kif which was set by pfr_pool_get() */
rpool->kif = kif;
break;
}
if (rpool->opts & PF_POOL_STICKYADDR) {
if (sns[type] != NULL) {
pf_remove_src_node(sns[type]);
sns[type] = NULL;
}
if (pf_insert_src_node(&sns[type], r, type, af, saddr, naddr,
rpool->kif))
return (1);
}
if (pf_status.debug >= LOG_INFO &&
(rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) {
log(LOG_INFO, "pf: pf_map_addr: selected address ");
pf_print_host(naddr, 0, af);
if ((rpool->opts & PF_POOL_TYPEMASK) ==
PF_POOL_LEASTSTATES)
addlog(" with state count %llu", states);
if ((rpool->addr.type == PF_ADDR_TABLE &&
rpool->addr.p.tbl->pfrkt_refcntcost > 0) ||
(rpool->addr.type == PF_ADDR_DYNIFTL &&
rpool->addr.p.dyn->pfid_kt->pfrkt_refcntcost > 0))
addlog(" with weight %u", weight);
addlog("\n");
}
return (0);
}
int
pf_map_addr_states_increase(sa_family_t af, struct pf_pool *rpool,
struct pf_addr *naddr)
{
if (rpool->addr.type == PF_ADDR_TABLE) {
if (pfr_states_increase(rpool->addr.p.tbl,
naddr, af) == -1) {
if (pf_status.debug >= LOG_DEBUG) {
log(LOG_DEBUG,
"pf: pf_map_addr_states_increase: "
"selected address ");
pf_print_host(naddr, 0, af);
addlog(". Failed to increase count!\n");
}
return (-1);
}
} else if (rpool->addr.type == PF_ADDR_DYNIFTL) {
if (pfr_states_increase(rpool->addr.p.dyn->pfid_kt,
naddr, af) == -1) {
if (pf_status.debug >= LOG_DEBUG) {
log(LOG_DEBUG,
"pf: pf_map_addr_states_increase: "
"selected address ");
pf_print_host(naddr, 0, af);
addlog(". Failed to increase count!\n");
}
return (-1);
}
}
return (0);
}
int
pf_get_transaddr(struct pf_rule *r, struct pf_pdesc *pd,
struct pf_src_node **sns, struct pf_rule **nr)
{
struct pf_addr naddr;
u_int16_t nport;
#ifdef INET6
if (pd->af != pd->naf)
return (pf_get_transaddr_af(r, pd, sns));
#endif /* INET6 */
if (r->nat.addr.type != PF_ADDR_NONE) {
/* XXX is this right? what if rtable is changed at the same
* XXX time? where do I need to figure out the sport? */
nport = 0;
if (pf_get_sport(pd, r, &naddr, &nport,
r->nat.proxy_port[0], r->nat.proxy_port[1], sns)) {
DPFPRINTF(LOG_NOTICE,
"pf: NAT proxy port allocation (%u-%u) failed",
r->nat.proxy_port[0],
r->nat.proxy_port[1]);
return (-1);
}
/* decrease least-connection state counter of the previous */
if ((*nr) != NULL && (*nr)->nat.addr.type != PF_ADDR_NONE &&
((*nr)->nat.opts & PF_POOL_TYPEMASK) ==
PF_POOL_LEASTSTATES)
pf_pool_states_decrease_addr(&(*nr)->nat, pd->af,
&pd->nsaddr);
*nr = r;
pf_addrcpy(&pd->nsaddr, &naddr, pd->af);
pd->nsport = nport;
}
if (r->rdr.addr.type != PF_ADDR_NONE) {
if (pf_map_addr(pd->af, r, &pd->nsaddr, &naddr, NULL, sns,
&r->rdr, PF_SN_RDR))
return (-1);
if ((r->rdr.opts & PF_POOL_TYPEMASK) == PF_POOL_BITMASK)
pf_poolmask(&naddr, &naddr, &r->rdr.addr.v.a.mask,
&pd->ndaddr, pd->af);
nport = 0;
if (r->rdr.proxy_port[1]) {
u_int32_t tmp_nport;
u_int16_t div;
div = r->rdr.proxy_port[1] - r->rdr.proxy_port[0] + 1;
div = (div == 0) ? 1 : div;
tmp_nport = ((ntohs(pd->ndport) - ntohs(r->dst.port[0])) % div) +
r->rdr.proxy_port[0];
/* wrap around if necessary */
if (tmp_nport > 65535)
tmp_nport -= 65535;
nport = htons((u_int16_t)tmp_nport);
} else if (r->rdr.proxy_port[0])
nport = htons(r->rdr.proxy_port[0]);
/* decrease least-connection state counter of the previous */
if ((*nr) != NULL && (*nr)->rdr.addr.type != PF_ADDR_NONE &&
((*nr)->rdr.opts & PF_POOL_TYPEMASK) ==
PF_POOL_LEASTSTATES)
pf_pool_states_decrease_addr(&(*nr)->rdr, pd->af,
&pd->ndaddr);
*nr = r;
pf_addrcpy(&pd->ndaddr, &naddr, pd->af);
if (nport)
pd->ndport = nport;
}
return (0);
}
#ifdef INET6
int
pf_get_transaddr_af(struct pf_rule *r, struct pf_pdesc *pd,
struct pf_src_node **sns)
{
struct pf_addr ndaddr, nsaddr, naddr;
u_int16_t nport;
int prefixlen = 96;
if (pf_status.debug >= LOG_INFO) {
log(LOG_INFO, "pf: af-to %s %s, ",
pd->naf == AF_INET ? "inet" : "inet6",
r->rdr.addr.type == PF_ADDR_NONE ? "nat" : "rdr");
pf_print_host(&pd->nsaddr, pd->nsport, pd->af);
addlog(" -> ");
pf_print_host(&pd->ndaddr, pd->ndport, pd->af);
addlog("\n");
}
if (r->nat.addr.type == PF_ADDR_NONE)
panic("pf_get_transaddr_af: no nat pool for source address");
/* get source address and port */
nport = 0;
if (pf_get_sport(pd, r, &nsaddr, &nport,
r->nat.proxy_port[0], r->nat.proxy_port[1], sns)) {
DPFPRINTF(LOG_NOTICE,
"pf: af-to NAT proxy port allocation (%u-%u) failed",
r->nat.proxy_port[0],
r->nat.proxy_port[1]);
return (-1);
}
pd->nsport = nport;
if (pd->proto == IPPROTO_ICMPV6 && pd->naf == AF_INET) {
if (pd->dir == PF_IN) {
pd->ndport = ntohs(pd->ndport);
if (pd->ndport == ICMP6_ECHO_REQUEST)
pd->ndport = ICMP_ECHO;
else if (pd->ndport == ICMP6_ECHO_REPLY)
pd->ndport = ICMP_ECHOREPLY;
pd->ndport = htons(pd->ndport);
} else {
pd->nsport = ntohs(pd->nsport);
if (pd->nsport == ICMP6_ECHO_REQUEST)
pd->nsport = ICMP_ECHO;
else if (pd->nsport == ICMP6_ECHO_REPLY)
pd->nsport = ICMP_ECHOREPLY;
pd->nsport = htons(pd->nsport);
}
} else if (pd->proto == IPPROTO_ICMP && pd->naf == AF_INET6) {
if (pd->dir == PF_IN) {
pd->ndport = ntohs(pd->ndport);
if (pd->ndport == ICMP_ECHO)
pd->ndport = ICMP6_ECHO_REQUEST;
else if (pd->ndport == ICMP_ECHOREPLY)
pd->ndport = ICMP6_ECHO_REPLY;
pd->ndport = htons(pd->ndport);
} else {
pd->nsport = ntohs(pd->nsport);
if (pd->nsport == ICMP_ECHO)
pd->nsport = ICMP6_ECHO_REQUEST;
else if (pd->nsport == ICMP_ECHOREPLY)
pd->nsport = ICMP6_ECHO_REPLY;
pd->nsport = htons(pd->nsport);
}
}
/* get the destination address and port */
if (r->rdr.addr.type != PF_ADDR_NONE) {
if (pf_map_addr(pd->naf, r, &nsaddr, &naddr, NULL, sns,
&r->rdr, PF_SN_RDR))
return (-1);
if (r->rdr.proxy_port[0])
pd->ndport = htons(r->rdr.proxy_port[0]);
if (pd->naf == AF_INET) {
/* The prefix is the IPv4 rdr address */
prefixlen = in_mask2len((struct in_addr *)
&r->rdr.addr.v.a.mask);
inet_nat46(pd->naf, &pd->ndaddr,
&ndaddr, &naddr, prefixlen);
} else {
/* The prefix is the IPv6 rdr address */
prefixlen =
in6_mask2len((struct in6_addr *)
&r->rdr.addr.v.a.mask, NULL);
inet_nat64(pd->naf, &pd->ndaddr,
&ndaddr, &naddr, prefixlen);
}
} else {
if (pd->naf == AF_INET) {
/* The prefix is the IPv6 dst address */
prefixlen =
in6_mask2len((struct in6_addr *)
&r->dst.addr.v.a.mask, NULL);
if (prefixlen < 32)
prefixlen = 96;
inet_nat64(pd->naf, &pd->ndaddr,
&ndaddr, &pd->ndaddr, prefixlen);
} else {
/*
* The prefix is the IPv6 nat address
* (that was stored in pd->nsaddr)
*/
prefixlen = in6_mask2len((struct in6_addr *)
&r->nat.addr.v.a.mask, NULL);
if (prefixlen > 96)
prefixlen = 96;
inet_nat64(pd->naf, &pd->ndaddr,
&ndaddr, &nsaddr, prefixlen);
}
}
pf_addrcpy(&pd->nsaddr, &nsaddr, pd->naf);
pf_addrcpy(&pd->ndaddr, &ndaddr, pd->naf);
if (pf_status.debug >= LOG_INFO) {
log(LOG_INFO, "pf: af-to %s %s done, prefixlen %d, ",
pd->naf == AF_INET ? "inet" : "inet6",
r->rdr.addr.type == PF_ADDR_NONE ? "nat" : "rdr",
prefixlen);
pf_print_host(&pd->nsaddr, pd->nsport, pd->naf);
addlog(" -> ");
pf_print_host(&pd->ndaddr, pd->ndport, pd->naf);
addlog("\n");
}
return (0);
}
#endif /* INET6 */
int
pf_postprocess_addr(struct pf_state *cur)
{
struct pf_state_key *sks;
struct pf_addr *addr;
struct pf_pool *rpool, *natpl = NULL, *rdrpl = NULL;
struct pf_rule_item *ri;
int ret = 0;
/* decrease the states counter in pool for "least-states" */
if (cur->natrule.ptr != NULL) { /* this is the final */
if (cur->natrule.ptr->nat.addr.type != PF_ADDR_NONE)
natpl = &cur->natrule.ptr->nat;
if (cur->natrule.ptr->rdr.addr.type != PF_ADDR_NONE)
rdrpl = &cur->natrule.ptr->rdr;
}
if (natpl == NULL || rdrpl == NULL) {
/* nat or rdr might be done by a previous "match" */
SLIST_FOREACH(ri, &cur->match_rules, entry) {
/* first match since the list order is reversed */
if (natpl == NULL &&
ri->r->nat.addr.type != PF_ADDR_NONE)
natpl = &ri->r->nat;
if (rdrpl == NULL &&
ri->r->rdr.addr.type != PF_ADDR_NONE)
rdrpl = &ri->r->rdr;
if (natpl != NULL && rdrpl != NULL)
break;
}
}
if (natpl != NULL &&
(natpl->opts & PF_POOL_TYPEMASK) == PF_POOL_LEASTSTATES) {
/* nat-to <table> least-state */
if (cur->direction == PF_IN) {
sks = cur->key[PF_SK_STACK];
addr = &sks->addr[0];
} else {
sks = cur->key[PF_SK_WIRE];
addr = &sks->addr[1];
}
if (pf_pool_states_decrease_addr(natpl, sks->af, addr) != 0)
ret = 1;
}
if (rdrpl != NULL &&
(rdrpl->opts & PF_POOL_TYPEMASK) == PF_POOL_LEASTSTATES) {
/* rdr <table> least-state */
if (cur->direction == PF_IN) {
sks = cur->key[PF_SK_STACK];
addr = &sks->addr[1];
} else {
sks = cur->key[PF_SK_WIRE];
addr = &sks->addr[0];
}
if (pf_pool_states_decrease_addr(rdrpl, sks->af, addr) != 0)
ret = 1;
}
if (cur->rule.ptr != NULL) {
/* route-to <table> least-state */
rpool = &cur->rule.ptr->route;
if (rpool->addr.type != PF_ADDR_NONE &&
(rpool->opts & PF_POOL_TYPEMASK) == PF_POOL_LEASTSTATES) {
sks = cur->key[(cur->direction == PF_IN)
? PF_SK_STACK : PF_SK_WIRE];
addr = &cur->rt_addr;
KASSERT(sks != NULL);
if (pf_pool_states_decrease_addr(rpool, sks->af, addr)
!= 0)
ret = 1;
}
}
return (ret);
}
int
pf_pool_states_decrease_addr(struct pf_pool *rpool, int af,
struct pf_addr *addr)
{
int slbcount = -1;
if (rpool->addr.type == PF_ADDR_TABLE) {
if ((slbcount = pfr_states_decrease(
rpool->addr.p.tbl, addr, af)) == -1) {
if (pf_status.debug >= LOG_DEBUG) {
log(LOG_DEBUG, "pf: %s: selected address ",
__func__);
pf_print_host(addr, 0, af);
addlog(". Failed to "
"decrease count!\n");
}
return (1);
}
} else if (rpool->addr.type == PF_ADDR_DYNIFTL) {
if ((slbcount = pfr_states_decrease(
rpool->addr.p.dyn->pfid_kt, addr, af)) == -1) {
if (pf_status.debug >= LOG_DEBUG) {
log(LOG_DEBUG, "pf: %s: selected address ",
__func__);
pf_print_host(addr, 0, af);
addlog(". Failed to "
"decrease count!\n");
}
return (1);
}
}
if (slbcount > -1) {
if (pf_status.debug >= LOG_INFO) {
log(LOG_INFO, "pf: %s: selected address ", __func__);
pf_print_host(addr, 0, af);
addlog(" decreased state count to %u\n",
slbcount);
}
}
return (0);
}
File diff suppressed because it is too large. Load diff
+560
View File
@@ -0,0 +1,560 @@
/* PFLINUX port of OpenBSD 8.0 net/pf_osfp.c — see docs/porting-notes.md */
#include <pfk.h>
#endif /* PFDEBUG */
#endif /* _KERNEL */
/*
* Protection/ownership:
* I immutable after pf_osfp_initialize()
* p pf_lock
*/
SLIST_HEAD(pf_osfp_list, pf_os_fingerprint) pf_osfp_list =
SLIST_HEAD_INITIALIZER(pf_osfp_list); /* [p] */
pool_t pf_osfp_entry_pl; /* [I] */
pool_t pf_osfp_pl; /* [I] */
struct pf_os_fingerprint *pf_osfp_find(struct pf_os_fingerprint *,
u_int8_t);
struct pf_os_fingerprint *pf_osfp_find_exact(struct pf_os_fingerprint *);
void pf_osfp_insert(struct pf_os_fingerprint *);
#ifdef _KERNEL
/*
* Passively fingerprint the OS of the host (IPv4 TCP SYN packets only)
* Returns the list of possible OSes.
*/
struct pf_osfp_enlist *
pf_osfp_fingerprint(struct pf_pdesc *pd)
{
struct pf_tcphdr *th = &pd->hdr.tcp;
struct pf_ip *ip = NULL;
struct pf_ip6_hdr *ip6 = NULL;
char hdr[60];
if (pd->proto != IPPROTO_TCP)
return (NULL);
switch (pd->af) {
case AF_INET:
ip = mtod(pd->m, struct pf_ip *);
break;
case AF_INET6:
ip6 = mtod(pd->m, struct pf_ip6_hdr *);
break;
}
if (!pf_pull_hdr(pd->m, pd->off, hdr, th->th_off << 2, NULL, pd->af))
return (NULL);
return (pf_osfp_fingerprint_hdr(ip, ip6, (struct pf_tcphdr *)hdr));
}
#endif /* _KERNEL */
struct pf_osfp_enlist *
pf_osfp_fingerprint_hdr(const struct pf_ip *ip, const struct pf_ip6_hdr *ip6,
const struct pf_tcphdr *tcp)
{
struct pf_os_fingerprint fp, *fpresult;
int cnt, optlen = 0;
const u_int8_t *optp;
#ifdef _KERNEL
char srcname[128];
#else /* !_KERNEL */
char srcname[NI_MAXHOST];
#endif /* _KERNEL */
if ((tcp->th_flags & (TH_SYN|TH_ACK)) != TH_SYN)
return (NULL);
if (ip) {
if ((ip->ip_off & htons(IP_OFFMASK)) != 0)
return (NULL);
}
memset(&fp, 0, sizeof(fp));
if (ip) {
#ifndef _KERNEL
struct sockaddr_in sin;
#endif /* _KERNEL */
fp.fp_psize = ntohs(ip->ip_len);
fp.fp_ttl = ip->ip_ttl;
if (ip->ip_off & htons(IP_DF))
fp.fp_flags |= PF_OSFP_DF;
#ifdef _KERNEL
inet_ntop(AF_INET, &ip->ip_src, srcname, sizeof(srcname));
#else /* !_KERNEL */
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin./* PFLINUX sin_len */;
sin.sin_addr = ip->ip_src;
(void)getnameinfo((struct sockaddr *)&sin,
sizeof(struct sockaddr_in), srcname, sizeof(srcname),
NULL, 0, NI_NUMERICHOST);
#endif /* _KERNEL */
}
#ifdef INET6
else if (ip6) {
#ifndef _KERNEL
struct sockaddr_in6 sin6;
#endif /* !_KERNEL */
/* jumbo payload? */
fp.fp_psize = sizeof(struct pf_ip6_hdr) + ntohs(ip6->ip6_plen);
fp.fp_ttl = ip6->ip6_hlim;
fp.fp_flags |= PF_OSFP_DF;
fp.fp_flags |= PF_OSFP_INET6;
#ifdef _KERNEL
inet_ntop(AF_INET6, &ip6->ip6_src, srcname, sizeof(srcname));
#else /* !_KERNEL */
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
sin6.sin6_len = sizeof(struct sockaddr_in6);
sin6.sin6_addr = ip6->ip6_src;
(void)getnameinfo((struct sockaddr *)&sin6,
sizeof(struct sockaddr_in6), srcname, sizeof(srcname),
NULL, 0, NI_NUMERICHOST);
#endif /* !_KERNEL */
}
#endif /* INET6 */
else
return (NULL);
fp.fp_wsize = ntohs(tcp->th_win);
cnt = (tcp->th_off << 2) - sizeof(*tcp);
optp = (const u_int8_t *)((const char *)tcp + sizeof(*tcp));
for (; cnt > 0; cnt -= optlen, optp += optlen) {
if (*optp == TCPOPT_EOL)
break;
fp.fp_optcnt++;
if (*optp == TCPOPT_NOP) {
fp.fp_tcpopts = (fp.fp_tcpopts << PF_OSFP_TCPOPT_BITS) |
PF_OSFP_TCPOPT_NOP;
optlen = 1;
} else {
if (cnt < 2)
return (NULL);
optlen = optp[1];
if (optlen > cnt || optlen < 2)
return (NULL);
switch (*optp) {
case TCPOPT_MAXSEG:
if (optlen >= TCPOLEN_MAXSEG)
memcpy(&fp.fp_mss, &optp[2],
sizeof(fp.fp_mss));
fp.fp_tcpopts = (fp.fp_tcpopts <<
PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_MSS;
fp.fp_mss = ntohs(fp.fp_mss);
break;
case TCPOPT_WINDOW:
if (optlen >= TCPOLEN_WINDOW)
memcpy(&fp.fp_wscale, &optp[2],
sizeof(fp.fp_wscale));
fp.fp_tcpopts = (fp.fp_tcpopts <<
PF_OSFP_TCPOPT_BITS) |
PF_OSFP_TCPOPT_WSCALE;
break;
case TCPOPT_SACK_PERMITTED:
fp.fp_tcpopts = (fp.fp_tcpopts <<
PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_SACK;
break;
case TCPOPT_TIMESTAMP:
if (optlen >= TCPOLEN_TIMESTAMP) {
u_int32_t ts;
memcpy(&ts, &optp[2], sizeof(ts));
if (ts == 0)
fp.fp_flags |= PF_OSFP_TS0;
}
fp.fp_tcpopts = (fp.fp_tcpopts <<
PF_OSFP_TCPOPT_BITS) | PF_OSFP_TCPOPT_TS;
break;
default:
return (NULL);
}
}
optlen = MAX(optlen, 1); /* paranoia */
}
DPFPRINTF(LOG_INFO,
"fingerprinted %s:%d %d:%d:%d:%d:%llx (%d) "
"(TS=%s,M=%s%d,W=%s%d)",
srcname, ntohs(tcp->th_sport),
fp.fp_wsize, fp.fp_ttl, (fp.fp_flags & PF_OSFP_DF) != 0,
fp.fp_psize, (long long int)fp.fp_tcpopts, fp.fp_optcnt,
(fp.fp_flags & PF_OSFP_TS0) ? "0" : "",
(fp.fp_flags & PF_OSFP_MSS_MOD) ? "%" :
(fp.fp_flags & PF_OSFP_MSS_DC) ? "*" : "",
fp.fp_mss,
(fp.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" :
(fp.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "",
fp.fp_wscale);
if ((fpresult = pf_osfp_find(&fp, PF_OSFP_MAXTTL_OFFSET)))
return (&fpresult->fp_oses);
return (NULL);
}
/* Match a fingerprint ID against a list of OSes */
int
pf_osfp_match(struct pf_osfp_enlist *list, pf_osfp_t os)
{
struct pf_osfp_entry *entry;
int os_class, os_version, os_subtype;
int en_class, en_version, en_subtype;
if (os == PF_OSFP_ANY)
return (1);
if (list == NULL) {
DPFPRINTF(LOG_INFO, "osfp no match against %x", os);
return (os == PF_OSFP_UNKNOWN);
}
PF_OSFP_UNPACK(os, os_class, os_version, os_subtype);
SLIST_FOREACH(entry, list, fp_entry) {
PF_OSFP_UNPACK(entry->fp_os, en_class, en_version, en_subtype);
if ((os_class == PF_OSFP_ANY || en_class == os_class) &&
(os_version == PF_OSFP_ANY || en_version == os_version) &&
(os_subtype == PF_OSFP_ANY || en_subtype == os_subtype)) {
DPFPRINTF(LOG_INFO,
"osfp matched %s %s %s %x==%x",
entry->fp_class_nm, entry->fp_version_nm,
entry->fp_subtype_nm, os, entry->fp_os);
return (1);
}
}
DPFPRINTF(LOG_INFO, "fingerprint 0x%x didn't match", os);
return (0);
}
/* Initialize the OS fingerprint system */
void
pf_osfp_initialize(void)
{
pool_init(&pf_osfp_entry_pl, sizeof(struct pf_osfp_entry), 0,
IPL_NONE, PR_WAITOK, "pfosfpen", NULL);
pool_init(&pf_osfp_pl, sizeof(struct pf_os_fingerprint), 0,
IPL_NONE, PR_WAITOK, "pfosfp", NULL);
}
/* Flush the fingerprint list */
void
pf_osfp_flush(void)
{
struct pf_os_fingerprint *fp;
struct pf_osfp_entry *entry;
PF_LOCK();
while ((fp = SLIST_FIRST(&pf_osfp_list))) {
SLIST_REMOVE_HEAD(&pf_osfp_list, fp_next);
while ((entry = SLIST_FIRST(&fp->fp_oses))) {
SLIST_REMOVE_HEAD(&fp->fp_oses, fp_entry);
pool_put(&pf_osfp_entry_pl, entry);
}
pool_put(&pf_osfp_pl, fp);
}
PF_UNLOCK();
}
/* Add a fingerprint */
int
pf_osfp_add(struct pf_osfp_ioctl *fpioc)
{
struct pf_os_fingerprint *fp, *fp_prealloc, fpadd;
struct pf_osfp_entry *entry;
if (strnlen(fpioc->fp_os.fp_class_nm, sizeof(fpioc->fp_os.fp_class_nm)) >=
sizeof(fpioc->fp_os.fp_class_nm))
return ENAMETOOLONG;
if (strnlen(fpioc->fp_os.fp_version_nm, sizeof(fpioc->fp_os.fp_version_nm)) >=
sizeof(fpioc->fp_os.fp_version_nm))
return ENAMETOOLONG;
if (strnlen(fpioc->fp_os.fp_subtype_nm, sizeof(fpioc->fp_os.fp_subtype_nm)) >=
sizeof(fpioc->fp_os.fp_subtype_nm))
return ENAMETOOLONG;
memset(&fpadd, 0, sizeof(fpadd));
fpadd.fp_tcpopts = fpioc->fp_tcpopts;
fpadd.fp_wsize = fpioc->fp_wsize;
fpadd.fp_psize = fpioc->fp_psize;
fpadd.fp_mss = fpioc->fp_mss;
fpadd.fp_flags = fpioc->fp_flags;
fpadd.fp_optcnt = fpioc->fp_optcnt;
fpadd.fp_wscale = fpioc->fp_wscale;
fpadd.fp_ttl = fpioc->fp_ttl;
DPFPRINTF(LOG_DEBUG,
"adding osfp %s %s %s = %s%d:%d:%d:%s%d:0x%llx %d "
"(TS=%s,M=%s%d,W=%s%d) %x",
fpioc->fp_os.fp_class_nm, fpioc->fp_os.fp_version_nm,
fpioc->fp_os.fp_subtype_nm,
(fpadd.fp_flags & PF_OSFP_WSIZE_MOD) ? "%" :
(fpadd.fp_flags & PF_OSFP_WSIZE_MSS) ? "S" :
(fpadd.fp_flags & PF_OSFP_WSIZE_MTU) ? "T" :
(fpadd.fp_flags & PF_OSFP_WSIZE_DC) ? "*" : "",
fpadd.fp_wsize,
fpadd.fp_ttl,
(fpadd.fp_flags & PF_OSFP_DF) ? 1 : 0,
(fpadd.fp_flags & PF_OSFP_PSIZE_MOD) ? "%" :
(fpadd.fp_flags & PF_OSFP_PSIZE_DC) ? "*" : "",
fpadd.fp_psize,
(long long int)fpadd.fp_tcpopts, fpadd.fp_optcnt,
(fpadd.fp_flags & PF_OSFP_TS0) ? "0" : "",
(fpadd.fp_flags & PF_OSFP_MSS_MOD) ? "%" :
(fpadd.fp_flags & PF_OSFP_MSS_DC) ? "*" : "",
fpadd.fp_mss,
(fpadd.fp_flags & PF_OSFP_WSCALE_MOD) ? "%" :
(fpadd.fp_flags & PF_OSFP_WSCALE_DC) ? "*" : "",
fpadd.fp_wscale,
fpioc->fp_os.fp_os);
entry = pool_get(&pf_osfp_entry_pl, PR_WAITOK|PR_LIMITFAIL);
if (entry == NULL)
return (ENOMEM);
fp_prealloc = pool_get(&pf_osfp_pl, PR_WAITOK|PR_ZERO|PR_LIMITFAIL);
if (fp_prealloc == NULL) {
pool_put(&pf_osfp_entry_pl, entry);
return (ENOMEM);
}
PF_LOCK();
if ((fp = pf_osfp_find_exact(&fpadd))) {
struct pf_osfp_entry *tentry;
SLIST_FOREACH(tentry, &fp->fp_oses, fp_entry) {
if (PF_OSFP_ENTRY_EQ(tentry, &fpioc->fp_os)) {
PF_UNLOCK();
pool_put(&pf_osfp_entry_pl, entry);
pool_put(&pf_osfp_pl, fp_prealloc);
return (EEXIST);
}
}
} else {
fp = fp_prealloc;
fp_prealloc = NULL;
fp->fp_tcpopts = fpioc->fp_tcpopts;
fp->fp_wsize = fpioc->fp_wsize;
fp->fp_psize = fpioc->fp_psize;
fp->fp_mss = fpioc->fp_mss;
fp->fp_flags = fpioc->fp_flags;
fp->fp_optcnt = fpioc->fp_optcnt;
fp->fp_wscale = fpioc->fp_wscale;
fp->fp_ttl = fpioc->fp_ttl;
SLIST_INIT(&fp->fp_oses);
pf_osfp_insert(fp);
}
memcpy(entry, &fpioc->fp_os, sizeof(*entry));
SLIST_INSERT_HEAD(&fp->fp_oses, entry, fp_entry);
PF_UNLOCK();
#ifdef PFDEBUG
if ((fp = pf_osfp_validate()))
DPFPRINTF(LOG_NOTICE,
"Invalid fingerprint list");
#endif /* PFDEBUG */
if (fp_prealloc != NULL)
pool_put(&pf_osfp_pl, fp_prealloc);
return (0);
}
/* Find a fingerprint in the list */
struct pf_os_fingerprint *
pf_osfp_find(struct pf_os_fingerprint *find, u_int8_t ttldiff)
{
struct pf_os_fingerprint *f;
PF_ASSERT_LOCKED();
#define MATCH_INT(_MOD, _DC, _field) \
if ((f->fp_flags & _DC) == 0) { \
if ((f->fp_flags & _MOD) == 0) { \
if (f->_field != find->_field) \
continue; \
} else { \
if (f->_field == 0 || find->_field % f->_field) \
continue; \
} \
}
SLIST_FOREACH(f, &pf_osfp_list, fp_next) {
if (f->fp_tcpopts != find->fp_tcpopts ||
f->fp_optcnt != find->fp_optcnt ||
f->fp_ttl < find->fp_ttl ||
f->fp_ttl - find->fp_ttl > ttldiff ||
(f->fp_flags & (PF_OSFP_DF|PF_OSFP_TS0)) !=
(find->fp_flags & (PF_OSFP_DF|PF_OSFP_TS0)))
continue;
MATCH_INT(PF_OSFP_PSIZE_MOD, PF_OSFP_PSIZE_DC, fp_psize)
MATCH_INT(PF_OSFP_MSS_MOD, PF_OSFP_MSS_DC, fp_mss)
MATCH_INT(PF_OSFP_WSCALE_MOD, PF_OSFP_WSCALE_DC, fp_wscale)
if ((f->fp_flags & PF_OSFP_WSIZE_DC) == 0) {
if (f->fp_flags & PF_OSFP_WSIZE_MSS) {
if (find->fp_mss == 0)
continue;
/* Some "smart" NAT devices and DSL routers will tweak the MSS size and
* will set it to whatever is suitable for the link type.
*/
#define SMART_MSS 1460
if ((find->fp_wsize % find->fp_mss ||
find->fp_wsize / find->fp_mss !=
f->fp_wsize) &&
(find->fp_wsize % SMART_MSS ||
find->fp_wsize / SMART_MSS !=
f->fp_wsize))
continue;
} else if (f->fp_flags & PF_OSFP_WSIZE_MTU) {
if (find->fp_mss == 0)
continue;
#define MTUOFF (sizeof(struct pf_ip) + sizeof(struct pf_tcphdr))
#define SMART_MTU (SMART_MSS + MTUOFF)
if ((find->fp_wsize % (find->fp_mss + MTUOFF) ||
find->fp_wsize / (find->fp_mss + MTUOFF) !=
f->fp_wsize) &&
(find->fp_wsize % SMART_MTU ||
find->fp_wsize / SMART_MTU !=
f->fp_wsize))
continue;
} else if (f->fp_flags & PF_OSFP_WSIZE_MOD) {
if (f->fp_wsize == 0 || find->fp_wsize %
f->fp_wsize)
continue;
} else {
if (f->fp_wsize != find->fp_wsize)
continue;
}
}
return (f);
}
return (NULL);
}
/* Find an exact fingerprint in the list */
struct pf_os_fingerprint *
pf_osfp_find_exact(struct pf_os_fingerprint *find)
{
struct pf_os_fingerprint *f;
PF_ASSERT_LOCKED();
SLIST_FOREACH(f, &pf_osfp_list, fp_next) {
if (f->fp_tcpopts == find->fp_tcpopts &&
f->fp_wsize == find->fp_wsize &&
f->fp_psize == find->fp_psize &&
f->fp_mss == find->fp_mss &&
f->fp_flags == find->fp_flags &&
f->fp_optcnt == find->fp_optcnt &&
f->fp_wscale == find->fp_wscale &&
f->fp_ttl == find->fp_ttl)
return (f);
}
return (NULL);
}
/* Insert a fingerprint into the list */
void
pf_osfp_insert(struct pf_os_fingerprint *ins)
{
struct pf_os_fingerprint *f, *prev = NULL;
PF_ASSERT_LOCKED();
/* XXX need to go semi tree based. can key on tcp options */
SLIST_FOREACH(f, &pf_osfp_list, fp_next)
prev = f;
if (prev)
SLIST_INSERT_AFTER(prev, ins, fp_next);
else
SLIST_INSERT_HEAD(&pf_osfp_list, ins, fp_next);
}
/* Fill a fingerprint by its number (from an ioctl) */
int
pf_osfp_get(struct pf_osfp_ioctl *fpioc)
{
struct pf_os_fingerprint *fp;
struct pf_osfp_entry *entry;
int num = fpioc->fp_getnum;
int i = 0;
memset(fpioc, 0, sizeof(*fpioc));
PF_LOCK();
SLIST_FOREACH(fp, &pf_osfp_list, fp_next) {
SLIST_FOREACH(entry, &fp->fp_oses, fp_entry) {
if (i++ == num) {
fpioc->fp_mss = fp->fp_mss;
fpioc->fp_wsize = fp->fp_wsize;
fpioc->fp_flags = fp->fp_flags;
fpioc->fp_psize = fp->fp_psize;
fpioc->fp_ttl = fp->fp_ttl;
fpioc->fp_wscale = fp->fp_wscale;
fpioc->fp_getnum = num;
memcpy(&fpioc->fp_os, entry,
sizeof(fpioc->fp_os));
PF_UNLOCK();
return (0);
}
}
}
PF_UNLOCK();
return (EBUSY);
}
/* Validate that each signature is reachable */
struct pf_os_fingerprint *
pf_osfp_validate(void)
{
struct pf_os_fingerprint *f, *f2, find;
PF_ASSERT_LOCKED();
SLIST_FOREACH(f, &pf_osfp_list, fp_next) {
memcpy(&find, f, sizeof(find));
/* We do a few MSS/th_win percolations to make things unique */
if (find.fp_mss == 0)
find.fp_mss = 128;
if (f->fp_flags & PF_OSFP_WSIZE_MSS)
find.fp_wsize *= find.fp_mss;
else if (f->fp_flags & PF_OSFP_WSIZE_MTU)
find.fp_wsize *= (find.fp_mss + 40);
else if (f->fp_flags & PF_OSFP_WSIZE_MOD)
find.fp_wsize *= 2;
if (f != (f2 = pf_osfp_find(&find, 0))) {
if (f2)
DPFPRINTF(LOG_NOTICE,
"Found \"%s %s %s\" instead of "
"\"%s %s %s\"\n",
SLIST_FIRST(&f2->fp_oses)->fp_class_nm,
SLIST_FIRST(&f2->fp_oses)->fp_version_nm,
SLIST_FIRST(&f2->fp_oses)->fp_subtype_nm,
SLIST_FIRST(&f->fp_oses)->fp_class_nm,
SLIST_FIRST(&f->fp_oses)->fp_version_nm,
SLIST_FIRST(&f->fp_oses)->fp_subtype_nm);
else
DPFPRINTF(LOG_NOTICE,
"Couldn't find \"%s %s %s\"\n",
SLIST_FIRST(&f->fp_oses)->fp_class_nm,
SLIST_FIRST(&f->fp_oses)->fp_version_nm,
SLIST_FIRST(&f->fp_oses)->fp_subtype_nm);
return (f);
}
}
return (NULL);
}
+380
View File
@@ -0,0 +1,380 @@
/* PFLINUX port of OpenBSD 8.0 net/pf_ruleset.c — see docs/porting-notes.md */
#include <pfk.h>
#endif /* PFDEBUG */
#endif /* _KERNEL */
struct pf_anchor_global pf_anchors;
struct pf_anchor pf_main_anchor;
static __inline int pf_anchor_compare(struct pf_anchor *, struct pf_anchor *);
RB_GENERATE(pf_anchor_global, pf_anchor, entry_global, pf_anchor_compare);
RB_GENERATE(pf_anchor_node, pf_anchor, entry_node, pf_anchor_compare);
static __inline int
pf_anchor_compare(struct pf_anchor *a, struct pf_anchor *b)
{
int c = strcmp(a->path, b->path);
return (c ? (c < 0 ? -1 : 1) : 0);
}
void
pf_init_ruleset(struct pf_ruleset *ruleset)
{
memset(ruleset, 0, sizeof(struct pf_ruleset));
TAILQ_INIT(&ruleset->rules.queues[0]);
TAILQ_INIT(&ruleset->rules.queues[1]);
ruleset->rules.active.ptr = &ruleset->rules.queues[0];
ruleset->rules.inactive.ptr = &ruleset->rules.queues[1];
}
struct pf_anchor *
pf_find_anchor(const char *path)
{
struct pf_anchor *key, *found;
key = rs_malloc(sizeof(*key));
if (key == NULL)
return (NULL);
strlcpy(key->path, path, sizeof(key->path));
found = RB_FIND(pf_anchor_global, &pf_anchors, key);
rs_free(key, sizeof(*key));
return (found);
}
struct pf_ruleset *
pf_find_ruleset(const char *path)
{
struct pf_anchor *anchor;
while (*path == '/')
path++;
if (!*path)
return (&pf_main_ruleset);
anchor = pf_find_anchor(path);
if (anchor == NULL)
return (NULL);
else
return (&anchor->ruleset);
}
struct pf_ruleset *
pf_get_leaf_ruleset(char *path, char **path_remainder)
{
struct pf_ruleset *ruleset;
char *leaf, *p;
int i = 0;
p = path;
while (*p == '/')
p++;
ruleset = pf_find_ruleset(p);
leaf = p;
while (ruleset == NULL) {
leaf = strrchr(p, '/');
if (leaf != NULL) {
*leaf = '\0';
i++;
ruleset = pf_find_ruleset(p);
} else {
leaf = path;
/*
* if no path component exists, then main ruleset is
* our parent.
*/
ruleset = &pf_main_ruleset;
}
}
if (path_remainder != NULL)
*path_remainder = leaf;
/* restore slashes in path. */
while (i != 0) {
while (*leaf != '\0')
leaf++;
*leaf = '/';
i--;
}
return (ruleset);
}
struct pf_anchor *
pf_create_anchor(struct pf_anchor *parent, const char *aname)
{
struct pf_anchor *anchor, *dup;
if (!*aname || (strlen(aname) >= PF_ANCHOR_NAME_SIZE) ||
((parent != NULL) && (strlen(parent->path) >= PF_ANCHOR_MAXPATH)))
return (NULL);
anchor = rs_pool_get_anchor();
if (anchor == NULL)
return (NULL);
RB_INIT(&anchor->children);
strlcpy(anchor->name, aname, sizeof(anchor->name));
if (parent != NULL) {
/*
* Make sure path for levels 2, 3, ... is terminated by '/':
* 1/2/3/...
*/
strlcpy(anchor->path, parent->path, sizeof(anchor->path));
strlcat(anchor->path, "/", sizeof(anchor->path));
}
strlcat(anchor->path, anchor->name, sizeof(anchor->path));
if ((dup = RB_INSERT(pf_anchor_global, &pf_anchors, anchor)) != NULL) {
DPFPRINTF(LOG_NOTICE,
"%s: RB_INSERT to global '%s' '%s' collides with '%s' '%s'",
__func__, anchor->path, anchor->name, dup->path, dup->name);
rs_pool_put_anchor(anchor);
return (NULL);
}
if (parent != NULL) {
anchor->parent = parent;
dup = RB_INSERT(pf_anchor_node, &parent->children, anchor);
if (dup != NULL) {
DPFPRINTF(LOG_NOTICE,
"%s: RB_INSERT to parent '%s' '%s' collides with "
"'%s' '%s'", __func__, anchor->path, anchor->name,
dup->path, dup->name);
RB_REMOVE(pf_anchor_global, &pf_anchors,
anchor);
rs_pool_put_anchor(anchor);
return (NULL);
}
}
pf_init_ruleset(&anchor->ruleset);
anchor->ruleset.anchor = anchor;
#ifdef _KERNEL
refcnt_init(&anchor->ref);
#endif
return (anchor);
}
struct pf_ruleset *
pf_find_or_create_ruleset(const char *path)
{
char *p, *aname, *r;
struct pf_ruleset *ruleset;
struct pf_anchor *anchor;
if (path[0] == 0)
return (&pf_main_ruleset);
while (*path == '/')
path++;
ruleset = pf_find_ruleset(path);
if (ruleset != NULL)
return (ruleset);
p = rs_malloc(MAXPATHLEN);
if (p == NULL)
return (NULL);
strlcpy(p, path, MAXPATHLEN);
ruleset = pf_get_leaf_ruleset(p, &aname);
anchor = ruleset->anchor;
while (*aname == '/')
aname++;
/*
* aname is a path remainder, which contains nodes we must create. We
* process the aname path from left to right, effectively descending
* from parents to children.
*/
while ((r = strchr(aname, '/')) != NULL || *aname) {
if (r != NULL)
*r = 0;
anchor = pf_create_anchor(anchor, aname);
if (anchor == NULL) {
rs_free(p, MAXPATHLEN);
return (NULL);
}
if (r == NULL)
break;
else
aname = r + 1;
}
rs_free(p, MAXPATHLEN);
return (&anchor->ruleset);
}
void
pf_remove_if_empty_ruleset(struct pf_ruleset *ruleset)
{
struct pf_anchor *parent;
while (ruleset != NULL) {
if (ruleset == &pf_main_ruleset ||
!RB_EMPTY(&ruleset->anchor->children) ||
ruleset->anchor->refcnt > 0 || ruleset->tables > 0 ||
ruleset->topen)
return;
if (!TAILQ_EMPTY(ruleset->rules.active.ptr) ||
!TAILQ_EMPTY(ruleset->rules.inactive.ptr) ||
ruleset->rules.inactive.open)
return;
RB_REMOVE(pf_anchor_global, &pf_anchors, ruleset->anchor);
if ((parent = ruleset->anchor->parent) != NULL)
RB_REMOVE(pf_anchor_node, &parent->children,
ruleset->anchor);
pf_anchor_rele(ruleset->anchor);
if (parent == NULL)
return;
ruleset = &parent->ruleset;
}
}
int
pf_anchor_setup(struct pf_rule *r, const struct pf_ruleset *s,
const char *name)
{
char *p, *path;
struct pf_ruleset *ruleset;
r->anchor = NULL;
r->anchor_relative = 0;
r->anchor_wildcard = 0;
if (!name[0])
return (0);
path = rs_malloc(MAXPATHLEN);
if (path == NULL)
return (1);
if (name[0] == '/')
strlcpy(path, name + 1, MAXPATHLEN);
else {
/* relative path */
r->anchor_relative = 1;
if (s->anchor == NULL || !s->anchor->path[0])
path[0] = 0;
else
strlcpy(path, s->anchor->path, MAXPATHLEN);
while (name[0] == '.' && name[1] == '.' && name[2] == '/') {
if (!path[0]) {
DPFPRINTF(LOG_NOTICE,
"pf_anchor_setup: .. beyond root");
rs_free(path, MAXPATHLEN);
return (1);
}
if ((p = strrchr(path, '/')) != NULL)
*p = 0;
else
path[0] = 0;
r->anchor_relative++;
name += 3;
}
if (path[0])
strlcat(path, "/", MAXPATHLEN);
strlcat(path, name, MAXPATHLEN);
}
if ((p = strrchr(path, '/')) != NULL && !strcmp(p, "/*")) {
r->anchor_wildcard = 1;
*p = 0;
}
ruleset = pf_find_or_create_ruleset(path);
rs_free(path, MAXPATHLEN);
if (ruleset == NULL || ruleset == &pf_main_ruleset) {
DPFPRINTF(LOG_NOTICE,
"pf_anchor_setup: ruleset");
return (1);
}
r->anchor = ruleset->anchor;
r->anchor->refcnt++;
return (0);
}
int
pf_anchor_copyout(const struct pf_ruleset *rs, const struct pf_rule *r,
struct pfioc_rule *pr)
{
pr->anchor_call[0] = 0;
if (r->anchor == NULL)
return (0);
if (!r->anchor_relative) {
strlcpy(pr->anchor_call, "/", sizeof(pr->anchor_call));
strlcat(pr->anchor_call, r->anchor->path,
sizeof(pr->anchor_call));
} else {
char *a, *p;
int i;
a = rs_malloc(MAXPATHLEN);
if (a == NULL)
return (1);
if (rs == &pf_main_ruleset)
a[0] = 0;
else
strlcpy(a, rs->anchor->path, MAXPATHLEN);
for (i = 1; i < r->anchor_relative; ++i) {
if ((p = strrchr(a, '/')) == NULL)
p = a;
*p = 0;
strlcat(pr->anchor_call, "../",
sizeof(pr->anchor_call));
}
if (strncmp(a, r->anchor->path, strlen(a))) {
DPFPRINTF(LOG_NOTICE,
"pf_anchor_copyout: '%s' '%s'", a,
r->anchor->path);
rs_free(a, MAXPATHLEN);
return (1);
}
if (strlen(r->anchor->path) > strlen(a))
strlcat(pr->anchor_call, r->anchor->path + (a[0] ?
strlen(a) + 1 : 0), sizeof(pr->anchor_call));
rs_free(a, MAXPATHLEN);
}
if (r->anchor_wildcard)
strlcat(pr->anchor_call, pr->anchor_call[0] ? "/*" : "*",
sizeof(pr->anchor_call));
return (0);
}
void
pf_remove_anchor(struct pf_rule *r)
{
if (r->anchor == NULL)
return;
if (r->anchor->refcnt <= 0)
DPFPRINTF(LOG_NOTICE, "pf_remove_anchor: broken refcount");
else if (!--r->anchor->refcnt)
pf_remove_if_empty_ruleset(&r->anchor->ruleset);
r->anchor = NULL;
}
void
pf_anchor_rele(struct pf_anchor *anchor)
{
if ((anchor == NULL) || (anchor == &pf_main_anchor))
return;
#ifdef _KERNEL
if (refcnt_rele(&anchor->ref))
rs_pool_put_anchor(anchor);
#else
rs_pool_put_anchor(anchor);
#endif
}
struct pf_anchor *
pf_anchor_take(struct pf_anchor *anchor)
{
#ifdef _KERNEL
if (anchor != NULL && anchor != &pf_main_anchor)
refcnt_take(&anchor->ref);
#endif
return (anchor);
}
+294
View File
@@ -0,0 +1,294 @@
/* PFLINUX port of OpenBSD 8.0 net/pf_syncookies.c — see docs/porting-notes.md */
#include <pfk.h>
union pf_syncookie {
uint8_t cookie;
struct {
uint8_t oddeven:1,
sack_ok:1,
wscale_idx:3,
mss_idx:3;
} flags;
};
#define PF_SYNCOOKIE_SECRET_SIZE SIPHASH_KEY_LENGTH
#define PF_SYNCOOKIE_SECRET_LIFETIME 15 /* seconds */
static struct {
struct timeout keytimeout;
volatile uint oddeven;
SIPHASH_KEY key[2];
uint32_t hiwat; /* absolute; # of states */
uint32_t lowat;
} pf_syncookie_status;
void pf_syncookie_rotate(void *);
void pf_syncookie_newkey(void);
uint32_t pf_syncookie_mac(struct pf_pdesc *, union pf_syncookie,
uint32_t);
uint32_t pf_syncookie_generate(struct pf_pdesc *, uint16_t);
void
pf_syncookies_init(void)
{
timeout_set(&pf_syncookie_status.keytimeout,
pf_syncookie_rotate, NULL);
pf_syncookie_status.hiwat = PFSTATE_HIWAT * PF_SYNCOOKIES_HIWATPCT/100;
pf_syncookie_status.lowat = PFSTATE_HIWAT * PF_SYNCOOKIES_LOWATPCT/100;
pf_syncookies_setmode(PF_SYNCOOKIES_NEVER);
}
int
pf_syncookies_setmode(u_int8_t mode)
{
if (mode > PF_SYNCOOKIES_MODE_MAX)
return (EINVAL);
if (pf_status.syncookies_mode == mode)
return (0);
pf_status.syncookies_mode = mode;
if (pf_status.syncookies_mode == PF_SYNCOOKIES_ALWAYS) {
pf_syncookie_newkey();
pf_status.syncookies_active = 1;
}
return (0);
}
int
pf_syncookies_setwats(u_int32_t hiwat, u_int32_t lowat)
{
if (lowat > hiwat)
return (EINVAL);
pf_syncookie_status.hiwat = hiwat;
pf_syncookie_status.lowat = lowat;
return (0);
}
int
pf_syncookies_getwats(struct pfioc_synflwats *wats)
{
wats->hiwat = pf_syncookie_status.hiwat;
wats->lowat = pf_syncookie_status.lowat;
return (0);
}
int
pf_synflood_check(struct pf_pdesc *pd)
{
KASSERT (pd->proto == IPPROTO_TCP);
if (pd->m && (pd->m->m_pkthdr.pf.tag & PF_TAG_SYNCOOKIE_RECREATED))
return (0);
if (pf_status.syncookies_mode != PF_SYNCOOKIES_ADAPTIVE)
return (pf_status.syncookies_mode);
if (!pf_status.syncookies_active &&
pf_status.states_halfopen > pf_syncookie_status.hiwat) {
pf_syncookie_newkey();
pf_status.syncookies_active = 1;
DPFPRINTF(LOG_WARNING,
"synflood detected, enabling syncookies");
pf_status.lcounters[LCNT_SYNFLOODS]++;
}
return (pf_status.syncookies_active);
}
void
pf_syncookie_send(struct pf_pdesc *pd, u_short *reason)
{
uint16_t mss, mssdflt;
uint32_t iss;
mssdflt = atomic_load_int(&tcp_mssdflt);
mss = max(pf_get_mss(pd, mssdflt), mssdflt);
iss = pf_syncookie_generate(pd, mss);
pf_send_tcp(NULL, pd->af, pd->dst, pd->src, *pd->dport, *pd->sport,
iss, ntohl(pd->hdr.tcp.th_seq) + 1, TH_SYN|TH_ACK, 0, mss,
0, 1, 0, pd->rdomain, reason);
pf_status.syncookies_inflight[pf_syncookie_status.oddeven]++;
pf_status.lcounters[LCNT_SYNCOOKIES_SENT]++;
}
uint8_t
pf_syncookie_validate(struct pf_pdesc *pd)
{
uint32_t hash, ack, seq;
union pf_syncookie cookie;
KASSERT(pd->proto == IPPROTO_TCP);
seq = ntohl(pd->hdr.tcp.th_seq) - 1;
ack = ntohl(pd->hdr.tcp.th_ack) - 1;
cookie.cookie = (ack & 0xff) ^ (ack >> 24);
/* we don't know oddeven before setting the cookie (union) */
if (pf_status.syncookies_inflight[cookie.flags.oddeven] == 0)
return (0);
hash = pf_syncookie_mac(pd, cookie, seq);
if ((ack & ~0xff) != (hash & ~0xff))
return (0);
pf_status.syncookies_inflight[cookie.flags.oddeven]--;
pf_status.lcounters[LCNT_SYNCOOKIES_VALID]++;
return (1);
}
/*
* all following functions private
*/
void
pf_syncookie_rotate(void *arg)
{
/* do we want to disable syncookies? */
if (pf_status.syncookies_active &&
((pf_status.syncookies_mode == PF_SYNCOOKIES_ADAPTIVE &&
pf_status.states_halfopen + pf_status.syncookies_inflight[0] +
pf_status.syncookies_inflight[1] < pf_syncookie_status.lowat) ||
pf_status.syncookies_mode == PF_SYNCOOKIES_NEVER)) {
pf_status.syncookies_active = 0;
DPFPRINTF(LOG_WARNING, "syncookies disabled");
}
/* nothing in flight any more? delete keys and return */
if (!pf_status.syncookies_active &&
pf_status.syncookies_inflight[0] == 0 &&
pf_status.syncookies_inflight[1] == 0) {
memset(&pf_syncookie_status.key[0], 0,
PF_SYNCOOKIE_SECRET_SIZE);
memset(&pf_syncookie_status.key[1], 0,
PF_SYNCOOKIE_SECRET_SIZE);
return;
}
/* new key, including timeout */
pf_syncookie_newkey();
}
void
pf_syncookie_newkey(void)
{
pf_syncookie_status.oddeven = (pf_syncookie_status.oddeven + 1) & 0x1;
pf_status.syncookies_inflight[pf_syncookie_status.oddeven] = 0;
arc4random_buf(&pf_syncookie_status.key[pf_syncookie_status.oddeven],
PF_SYNCOOKIE_SECRET_SIZE);
timeout_add_sec(&pf_syncookie_status.keytimeout,
PF_SYNCOOKIE_SECRET_LIFETIME);
}
/*
* Distribution and probability of certain MSS values. Those in between are
* rounded down to the next lower one.
* [An Analysis of TCP Maximum Segment Sizes, S. Alcock and R. Nelson, 2011]
* .2% .3% 5% 7% 7% 20% 15% 45%
*/
static int pf_syncookie_msstab[] =
{ 216, 536, 1200, 1360, 1400, 1440, 1452, 1460 };
/*
* Distribution and probability of certain WSCALE values.
* The absence of the WSCALE option is encoded with index zero.
* [WSCALE values histograms, Allman, 2012]
* X 10 10 35 5 6 14 10% by host
* X 11 4 5 5 18 49 3% by connections
*/
static int pf_syncookie_wstab[] = { 0, 0, 1, 2, 4, 6, 7, 8 };
uint32_t
pf_syncookie_mac(struct pf_pdesc *pd, union pf_syncookie cookie, uint32_t seq)
{
SIPHASH_CTX ctx;
uint32_t siphash[2];
KASSERT(pd->proto == IPPROTO_TCP);
SipHash24_Init(&ctx, &pf_syncookie_status.key[cookie.flags.oddeven]);
switch (pd->af) {
case AF_INET:
SipHash24_Update(&ctx, pd->src, sizeof(pd->src->v4));
SipHash24_Update(&ctx, pd->dst, sizeof(pd->dst->v4));
break;
case AF_INET6:
SipHash24_Update(&ctx, pd->src, sizeof(pd->src->v6));
SipHash24_Update(&ctx, pd->dst, sizeof(pd->dst->v6));
break;
default:
panic("unknown address family");
}
SipHash24_Update(&ctx, pd->sport, sizeof(*pd->sport));
SipHash24_Update(&ctx, pd->dport, sizeof(*pd->dport));
SipHash24_Update(&ctx, &seq, sizeof(seq));
SipHash24_Update(&ctx, &cookie, sizeof(cookie));
SipHash24_Final((uint8_t *)&siphash, &ctx);
return (siphash[0] ^ siphash[1]);
}
uint32_t
pf_syncookie_generate(struct pf_pdesc *pd, uint16_t mss)
{
uint8_t i, wscale;
uint32_t iss, hash;
union pf_syncookie cookie;
cookie.cookie = 0;
/* map MSS */
for (i = nitems(pf_syncookie_msstab) - 1;
pf_syncookie_msstab[i] > mss && i > 0; i--)
/* nada */;
cookie.flags.mss_idx = i;
/* map WSCALE */
wscale = pf_get_wscale(pd);
for (i = nitems(pf_syncookie_wstab) - 1;
pf_syncookie_wstab[i] > wscale && i > 0; i--)
/* nada */;
cookie.flags.wscale_idx = i;
cookie.flags.sack_ok = 0; /* XXX */
cookie.flags.oddeven = pf_syncookie_status.oddeven;
hash = pf_syncookie_mac(pd, cookie, ntohl(pd->hdr.tcp.th_seq));
/*
* Put the flags into the hash and XOR them to get better ISS number
* variance. This doesn't enhance the cryptographic strength and is
* done to prevent the 8 cookie bits from showing up directly on the
* wire.
*/
iss = hash & ~0xff;
iss |= cookie.cookie ^ (hash >> 24);
return (iss);
}
struct mbuf *
pf_syncookie_recreate_syn(struct pf_pdesc *pd, u_short *reason)
{
uint8_t wscale;
uint16_t mss;
uint32_t ack, seq;
union pf_syncookie cookie;
seq = ntohl(pd->hdr.tcp.th_seq) - 1;
ack = ntohl(pd->hdr.tcp.th_ack) - 1;
cookie.cookie = (ack & 0xff) ^ (ack >> 24);
if (cookie.flags.mss_idx >= nitems(pf_syncookie_msstab) ||
cookie.flags.wscale_idx >= nitems(pf_syncookie_wstab))
return (NULL);
mss = pf_syncookie_msstab[cookie.flags.mss_idx];
wscale = pf_syncookie_wstab[cookie.flags.wscale_idx];
return (pf_build_tcp(NULL, pd->af, pd->src, pd->dst, *pd->sport,
*pd->dport, seq, 0, TH_SYN, wscale, mss, pd->ttl, 0,
PF_TAG_SYNCOOKIE_RECREATED, cookie.flags.sack_ok, pd->rdomain,
reason));
}
File diff suppressed because it is too large. Load diff
+92
View File
@@ -0,0 +1,92 @@
/*
* PF-on-Linux netfilter hook registration + M1 ioctl skeleton.
* The real verdict logic arrives from staged pf.c (T12); this file owns
* hook lifecycle and /dev/pf dispatch. (ISC 2026)
*/
#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;
/* --- temporary M1 control surface (replaced by staged pf_ioctl.c, T7b) --- */
static int pfk_ioctl_min(struct file *f, unsigned int cmd, unsigned long addr)
{
struct pf_status s;
switch (cmd) {
case DIOCGETSTATUS:
memset(&s, 0, sizeof(s));
s.running = pfk_enabled;
strscpy(s.ifname, "pf", sizeof(s.ifname));
if (copy_to_user((void __user *)addr, &s, sizeof(s)))
return EFAULT;
return 0;
default:
/* Rule/state/table ioctls arrive with the staged pf_ioctl.c
* (plan T7b); until then they are rejected (not silently
* accepted) so behaviour is explicit. */
return EINVAL;
}
}
long pfk_ioctl(struct file *f, unsigned int cmd, unsigned long addr)
{
return pfk_ioctl_min(f, cmd, addr);
}
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 (real work lands in T12) --- */
static unsigned int
pfk_ipv4_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *st)
{
/* PFLINUX M1: accept-everything placeholder; T12 routes to pf_test() */
return NF_ACCEPT;
}
static struct nf_hook_ops pfk_hooks[] = {
{ .hook = pfk_ipv4_hook, .pf = NFPROTO_IPV4, .hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP_PRI_FIRST },
{ .hook = pfk_ipv4_hook, .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP_PRI_FIRST + 1 },
{ .hook = pfk_ipv4_hook, .pf = NFPROTO_IPV4, .hooknum = NF_INET_FORWARD,
.priority = NF_IP_PRI_FIRST },
{ .hook = pfk_ipv4_hook, .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_CONNTRACK + 1 },
{ .hook = pfk_ipv4_hook, .pf = NFPROTO_IPV4, .hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_LAST - 1 },
};
#define PFK_NHOOKS ARRAY_SIZE(pfk_hooks)
int pfk_hooks_register(void)
{
int i, err;
for (i = 0; i < PFK_NHOOKS; i++) {
pfk_hooks[i].priv = NULL;
err = nf_register_net_hook(&init_net, &pfk_hooks[i]);
if (err)
goto fail;
}
return 0;
fail:
while (--i >= 0)
nf_unregister_net_hook(&init_net, &pfk_hooks[i]);
return err;
}
void pfk_hooks_unregister(void)
{
int i;
for (i = 0; i < PFK_NHOOKS; i++)
nf_unregister_net_hook(&init_net, &pfk_hooks[i]);
}
+86
View File
@@ -0,0 +1,86 @@
/*
* PF-on-Linux: pfk module core — load/exit, /dev/pf misc device,
* NET_LOCK bring-up, staged upstream bring-up hooks. (ISC 2026)
*/
#include <pfk.h>
#include <pfk_api.h>
#include <net/pfvar.h>
#include <net/pfvar_priv.h>
extern void pfk_mbuf_exit(void);
extern int pfk_if_init(void);
extern void pfk_if_exit(void);
static int pfk_open(struct inode *inode, struct file *filp)
{
if (filp->f_mode & FMODE_WRITE)
return capable(CAP_NET_ADMIN) ? 0 : -EPERM;
return 0;
}
static long pfk_ioctl_io(struct file *filp, unsigned int cmd,
unsigned long arg)
{
if (!capable(CAP_NET_ADMIN))
return -EPERM;
return pfk_ioctl(filp, cmd, arg);
}
static const struct file_operations pfk_fops = {
.owner = THIS_MODULE,
.open = pfk_open,
.unlocked_ioctl = pfk_ioctl_io,
.compat_ioctl = compat_ptr_ioctl,
};
static struct miscdevice pfk_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "pf",
.fops = &pfk_fops,
.mode = 0600,
};
static int __init pfk_init(void)
{
int err;
pfk_net_lock_init();
err = pfk_mbuf_init();
if (err)
return err;
err = pfk_if_init();
if (err)
goto err_mbuf;
err = pfk_hooks_register();
if (err)
goto err_if;
err = misc_register(&pfk_dev);
if (err)
goto err_hooks;
pr_info("pfk: PF-on-Linux engine loaded (OpenBSD 8.0 base)\n");
return 0;
err_hooks:
pfk_hooks_unregister();
err_if:
pfk_if_exit();
err_mbuf:
pfk_mbuf_exit();
return err;
}
static void __exit pfk_exit(void)
{
misc_deregister(&pfk_dev);
pfk_hooks_unregister();
pfk_if_exit();
pfk_mbuf_exit();
pr_info("pfk: unloaded\n");
}
module_init(pfk_init);
module_exit(pfk_exit);
MODULE_LICENSE("Dual MIT/GPL"); /* ISC/BSD code; Dual MIT/GPL is the kernel-
* recognised tag for permissive BSD-licensed
* modules linking GPL exports (see LICENSE). */
MODULE_DESCRIPTION("PF packet filter (ported from OpenBSD 8.0) for Linux");
MODULE_AUTHOR("PF-on-Linux contributors");