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