Files
pf-linux-port/module/compat/pfk_mbuf.c
T
lgcosta da2e9b679a 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
2026-11-11 12:09:34 -03:00

463 lines
8.4 KiB
C

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