mirror of
https://github.com/lgcosta/pf-linux-port.git
synced 2026-11-12 23:38:03 -03:00
M2: live netfilter data path (pf_test per direction), pfattach bring-up, NET_LOCK reentrancy fix, static pfctl
- pfk_hooks: PRE→PF_IN, LOCAL_OUT/POST→PF_OUT v4+v6; replacement/buffer handling with NF_STOLEN + re-injection (documented divergence: re-entry) - pfk_mod: pf_status_init/pfattach + pfi attach via pfk_if event callback - pfk_kern: NET_LOCK holder reentrancy (fix deadlock when notifier fires under the write lock — observed as full-VM network hang) - tools: static pfctl link for musl VMs
This commit is contained in:
1 parent
0119e70960
commit
5323ee2c71
49 files changed
+20028
-28
No files matched your search
+36
-1
@@ -7,6 +7,8 @@
|
||||
*/
|
||||
#include <pfk.h>
|
||||
|
||||
static void pfk_if_event_call(int added, struct pfk_ifnet *ifp);
|
||||
|
||||
static DEFINE_IDR(pfk_ifnet_idr);
|
||||
static DEFINE_SPINLOCK(pfk_ifnet_lock);
|
||||
static struct pfk_ifnet *pfk_egress;
|
||||
@@ -290,6 +292,7 @@ pfk_netdev_event(struct notifier_block *nb, unsigned long ev, void *ptr)
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
spin_unlock_irqrestore(&pfk_ifnet_lock, flags);
|
||||
pfk_if_event_call(1, ifp);
|
||||
break;
|
||||
case NETDEV_UNREGISTER:
|
||||
spin_lock_irqsave(&pfk_ifnet_lock, flags);
|
||||
@@ -297,6 +300,7 @@ pfk_netdev_event(struct notifier_block *nb, unsigned long ev, void *ptr)
|
||||
if (ifp) {
|
||||
ifp->alive = 0;
|
||||
idr_remove(&pfk_ifnet_idr, dev->ifindex);
|
||||
pfk_if_event_call(0, ifp);
|
||||
call_rcu(&ifp->rcu, pfk_ifnet_release);
|
||||
}
|
||||
spin_unlock_irqrestore(&pfk_ifnet_lock, flags);
|
||||
@@ -325,6 +329,35 @@ static struct notifier_block pfk_netdev_nb = {
|
||||
.notifier_call = pfk_netdev_event,
|
||||
};
|
||||
|
||||
static void (*pfk_if_event_cb)(int added, struct pfk_ifnet *ifp);
|
||||
|
||||
static void
|
||||
pfk_if_event_call(int added, struct pfk_ifnet *ifp)
|
||||
{
|
||||
if (!pfk_if_event_cb)
|
||||
return;
|
||||
/* netdev notifiers run in process context; NET lock is sleepable */
|
||||
pfk_net_lock();
|
||||
pfk_if_event_cb(added, ifp);
|
||||
pfk_net_unlock();
|
||||
}
|
||||
|
||||
void
|
||||
pfk_if_set_event_cb(void (*cb)(int added, struct pfk_ifnet *ifp))
|
||||
{
|
||||
pfk_if_event_cb = cb;
|
||||
}
|
||||
|
||||
void
|
||||
pfk_if_iterate(void (*cb)(struct pfk_ifnet *ifp))
|
||||
{
|
||||
struct pfk_ifnet *ifp;
|
||||
int id = -1;
|
||||
|
||||
while ((ifp = pfk_if_next_from(&id)) != NULL)
|
||||
cb(ifp);
|
||||
}
|
||||
|
||||
/* -------- egress -------- */
|
||||
void
|
||||
pfk_egress_refresh(void)
|
||||
@@ -403,8 +436,10 @@ pfk_if_init(void)
|
||||
else
|
||||
ifp = NULL; /* already tracked by notifier */
|
||||
spin_unlock_irqrestore(&pfk_ifnet_lock, flags);
|
||||
if (ifp)
|
||||
if (ifp) {
|
||||
pfk_if_refresh(ifp);
|
||||
pfk_if_event_call(1, ifp);
|
||||
}
|
||||
}
|
||||
rtnl_unlock();
|
||||
pfk_egress_refresh();
|
||||
|
||||
@@ -100,10 +100,28 @@ taskq_create(const char *name, unsigned int nthreads, int ipl,
|
||||
/* ---------------- NET_LOCK ---------------- */
|
||||
static DECLARE_RWSEM(pfk_net_sem);
|
||||
static atomic_t pfk_net_readers = ATOMIC_INIT(0);
|
||||
static atomic_t pfk_net_owner = ATOMIC_INIT(0); /* task pid holding write */
|
||||
|
||||
void pfk_net_lock_init(void) { }
|
||||
void pfk_net_lock(void) { down_write(&pfk_net_sem); }
|
||||
void pfk_net_unlock(void) { up_write(&pfk_net_sem); }
|
||||
void
|
||||
pfk_net_lock(void)
|
||||
{
|
||||
/* Re-entrant for the holder: OpenBSD's NET_LOCK is friendly with
|
||||
* nested calls in the same path (e.g. our netdev notifier firing
|
||||
* while pfioctl holds the lock). */
|
||||
if (atomic_read(&pfk_net_owner) == current->pid)
|
||||
return;
|
||||
down_write(&pfk_net_sem);
|
||||
atomic_set(&pfk_net_owner, current->pid);
|
||||
}
|
||||
void
|
||||
pfk_net_unlock(void)
|
||||
{
|
||||
if (atomic_read(&pfk_net_owner) != current->pid)
|
||||
return; /* nested no-op for non-holders */
|
||||
atomic_set(&pfk_net_owner, 0);
|
||||
up_write(&pfk_net_sem);
|
||||
}
|
||||
void pfk_net_read_enter(void) { down_read(&pfk_net_sem);
|
||||
atomic_inc(&pfk_net_readers); }
|
||||
void pfk_net_read_release(void){ atomic_dec(&pfk_net_readers);
|
||||
|
||||
@@ -50,6 +50,30 @@ pfk_m_setpkthdr(struct mbuf *m)
|
||||
m->m_pkthdr.tags = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ingress wrapper for netfilter hook skbs: takes an EXTRA skb reference so
|
||||
* the wrapper can be consumed (m_freem) without disturbing netfilter's own
|
||||
* reference; fills the pkthdr the way OpenBSD's ip_input/ip_output does.
|
||||
*/
|
||||
struct mbuf *
|
||||
pfk_m_wrap_skb(struct sk_buff *skb, int af)
|
||||
{
|
||||
struct mbuf *m;
|
||||
|
||||
skb_get(skb);
|
||||
m = m_alloc_skb(skb, M_NOWAIT);
|
||||
if (!m)
|
||||
return NULL;
|
||||
m->m_pkthdr.af = af;
|
||||
m->m_pkthdr.proto = (af == AF_INET) ?
|
||||
((struct pf_ip *)skb->data)->ip_p : -1;
|
||||
m->m_pkthdr.ph_ifidx = skb->dev ? skb->dev->ifindex : 0;
|
||||
m->m_pkthdr.ifp = m->m_pkthdr.rcvif =
|
||||
skb->dev ? pfk_ifp_bydev(skb->dev) : NULL;
|
||||
m->m_pkthdr.ph_rtableid = 0;
|
||||
return m;
|
||||
}
|
||||
|
||||
struct mbuf *
|
||||
m_alloc_skb(struct sk_buff *skb, int how)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user