M2 data path: BSD netisr workqueue + lock reentrancy fixes + diagnostics
- hooks: packets are queued to pfk_netisr ordered wq (process context) and re-injected with an skb->mark guard (PFK_MARK_PASSED); softirq-safe, documented divergence vs OpenBSD in-place ip_input continuation - pfk_rwlock: per-task writer recursion (NET/PF lock nesting like OpenBSD) - pfk_net: holder-task+depth reentrancy (pid-based hack removed) - ifnet events deferred to ordered workqueue (rtnl AB-BA fix) - idr iteration start index fix (kifs were never attached -> kif==NULL drop) - pfk_lock_trace module param for lock diagnostics - tests/vm: 01-state 02-block 03-tables 04-anchors + lib; vm-run-tests.sh - docs/en/install.md: apply/build guide (core patch set currently EMPTY)
This commit is contained in:
1 parent
5323ee2c71
commit
a0941b77fe
12 files changed
+413
-108
No files matched your search
+51
-11
@@ -331,15 +331,44 @@ static struct notifier_block pfk_netdev_nb = {
|
||||
|
||||
static void (*pfk_if_event_cb)(int added, struct pfk_ifnet *ifp);
|
||||
|
||||
/*
|
||||
* PFLINUX: netdev notifiers run with rtnl held; taking NET_LOCK there
|
||||
* deadlocks (AB-BA) against control paths that hold NET_LOCK and touch
|
||||
* rtnl (dev_get_by_name etc.). Interface attach/detach is therefore
|
||||
* deferred to an ordered workqueue (rtnl-free context) preserving order.
|
||||
*/
|
||||
struct pfk_if_event {
|
||||
struct work_struct work;
|
||||
int added;
|
||||
struct pfk_ifnet *ifp;
|
||||
};
|
||||
|
||||
static struct workqueue_struct *pfk_if_wq;
|
||||
|
||||
static void
|
||||
pfk_if_event_work(struct work_struct *w)
|
||||
{
|
||||
struct pfk_if_event *ev = container_of(w, struct pfk_if_event, work);
|
||||
|
||||
if (pfk_if_event_cb)
|
||||
pfk_if_event_cb(ev->added, ev->ifp);
|
||||
kfree(ev);
|
||||
}
|
||||
|
||||
static void
|
||||
pfk_if_event_call(int added, struct pfk_ifnet *ifp)
|
||||
{
|
||||
if (!pfk_if_event_cb)
|
||||
struct pfk_if_event *ev;
|
||||
|
||||
if (!pfk_if_event_cb || !pfk_if_wq)
|
||||
return;
|
||||
/* netdev notifiers run in process context; NET lock is sleepable */
|
||||
pfk_net_lock();
|
||||
pfk_if_event_cb(added, ifp);
|
||||
pfk_net_unlock();
|
||||
ev = kmalloc(sizeof(*ev), GFP_ATOMIC);
|
||||
if (!ev)
|
||||
return;
|
||||
INIT_WORK(&ev->work, pfk_if_event_work);
|
||||
ev->added = added;
|
||||
ev->ifp = ifp;
|
||||
queue_work(pfk_if_wq, &ev->work);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -352,7 +381,7 @@ void
|
||||
pfk_if_iterate(void (*cb)(struct pfk_ifnet *ifp))
|
||||
{
|
||||
struct pfk_ifnet *ifp;
|
||||
int id = -1;
|
||||
int id = 0;
|
||||
|
||||
while ((ifp = pfk_if_next_from(&id)) != NULL)
|
||||
cb(ifp);
|
||||
@@ -385,7 +414,7 @@ pfk_egress_if(void)
|
||||
struct pfk_ifnet *
|
||||
pfk_if_first(void)
|
||||
{
|
||||
int id = -1;
|
||||
int id = 0;
|
||||
|
||||
return pfk_if_next_from(&id);
|
||||
}
|
||||
@@ -407,7 +436,7 @@ pfk_if_next_from(int *idp)
|
||||
struct pfk_ifnet *
|
||||
pfk_if_next(struct pfk_ifnet *ifp)
|
||||
{
|
||||
int id = ifp ? ifp->if_index : -1;
|
||||
int id = ifp ? ifp->if_index : 0;
|
||||
|
||||
return pfk_if_next_from(&id);
|
||||
}
|
||||
@@ -418,9 +447,15 @@ pfk_if_init(void)
|
||||
struct net_device *dev;
|
||||
int err;
|
||||
|
||||
pfk_if_wq = alloc_ordered_workqueue("pfk_if", 0);
|
||||
if (!pfk_if_wq)
|
||||
return -ENOMEM;
|
||||
err = register_netdevice_notifier(&pfk_netdev_nb);
|
||||
if (err)
|
||||
if (err) {
|
||||
destroy_workqueue(pfk_if_wq);
|
||||
pfk_if_wq = NULL;
|
||||
return err;
|
||||
}
|
||||
/* populate existing devices */
|
||||
rtnl_lock();
|
||||
for_each_netdev(&init_net, dev) {
|
||||
@@ -449,8 +484,13 @@ pfk_if_init(void)
|
||||
void
|
||||
pfk_if_exit(void)
|
||||
{
|
||||
if (pfk_if_wq) {
|
||||
flush_workqueue(pfk_if_wq);
|
||||
destroy_workqueue(pfk_if_wq);
|
||||
pfk_if_wq = NULL;
|
||||
}
|
||||
struct pfk_ifnet *ifp;
|
||||
int id = -1;
|
||||
int id = 0;
|
||||
unsigned long flags;
|
||||
|
||||
unregister_netdevice_notifier(&pfk_netdev_nb);
|
||||
@@ -459,7 +499,7 @@ pfk_if_exit(void)
|
||||
ifp->alive = 0;
|
||||
idr_remove(&pfk_ifnet_idr, id);
|
||||
kfree(ifp);
|
||||
id = -1;
|
||||
id = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&pfk_ifnet_lock, flags);
|
||||
}
|
||||
+44
-10
@@ -100,27 +100,61 @@ 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 */
|
||||
|
||||
/*
|
||||
* PFLINUX: OpenBSD's NET_LOCK nesting is friendly for the holder (e.g.
|
||||
* pfioctl takes NET_LOCK and pf_status_read() does too). Linux rw_semaphores
|
||||
* are not recursive, so track the holder task + depth: nested lock/unlock by
|
||||
* the same task balance without touching the rwsem. (Tracking was by task
|
||||
* pointer on purpose: pids can be recycled.)
|
||||
*/
|
||||
static DEFINE_RAW_SPINLOCK(pfk_net_owner_lock);
|
||||
static struct task_struct *pfk_net_owner;
|
||||
static int pfk_net_depth;
|
||||
|
||||
void pfk_net_lock_init(void) { }
|
||||
|
||||
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)
|
||||
unsigned long flags;
|
||||
bool nested;
|
||||
|
||||
raw_spin_lock_irqsave(&pfk_net_owner_lock, flags);
|
||||
nested = (pfk_net_owner == current);
|
||||
if (nested)
|
||||
pfk_net_depth++;
|
||||
raw_spin_unlock_irqrestore(&pfk_net_owner_lock, flags);
|
||||
if (nested)
|
||||
return;
|
||||
down_write(&pfk_net_sem);
|
||||
atomic_set(&pfk_net_owner, current->pid);
|
||||
raw_spin_lock_irqsave(&pfk_net_owner_lock, flags);
|
||||
pfk_net_owner = current;
|
||||
pfk_net_depth = 1;
|
||||
raw_spin_unlock_irqrestore(&pfk_net_owner_lock, flags);
|
||||
}
|
||||
|
||||
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);
|
||||
unsigned long flags;
|
||||
int depth;
|
||||
|
||||
raw_spin_lock_irqsave(&pfk_net_owner_lock, flags);
|
||||
if (pfk_net_owner != current) {
|
||||
/* not the holder: unbalanced unlock — ignore loudly */
|
||||
raw_spin_unlock_irqrestore(&pfk_net_owner_lock, flags);
|
||||
WARN_ON_ONCE(1);
|
||||
return;
|
||||
}
|
||||
depth = --pfk_net_depth;
|
||||
raw_spin_unlock_irqrestore(&pfk_net_owner_lock, flags);
|
||||
if (depth == 0) {
|
||||
raw_spin_lock_irqsave(&pfk_net_owner_lock, flags);
|
||||
pfk_net_owner = NULL;
|
||||
raw_spin_unlock_irqrestore(&pfk_net_owner_lock, flags);
|
||||
up_write(&pfk_net_sem);
|
||||
}
|
||||
}
|
||||
void pfk_net_read_enter(void) { down_read(&pfk_net_sem);
|
||||
atomic_inc(&pfk_net_readers); }
|
||||
|
||||
@@ -294,6 +294,11 @@ struct pfk_rwlock {
|
||||
const char *name;
|
||||
int is_writer;
|
||||
atomic_t readers;
|
||||
/* PFLINUX: OpenBSD rw_enter(WRITER) recursion must not self-deadlock
|
||||
* (pfioctl holds PF_LOCK and re-enters via helpers); rw_semaphores are
|
||||
* non-recursive, so track writer identity + depth. */
|
||||
struct task_struct *owner;
|
||||
int wdepth;
|
||||
};
|
||||
#define rwlock pfk_rwlock
|
||||
#define rwlock_s pfk_rwlock
|
||||
|
||||
+149
-84
@@ -1,22 +1,20 @@
|
||||
/*
|
||||
* PF-on-Linux netfilter hook registration + M2 data path.
|
||||
* PF-on-Linux netfilter hook registration + data path (ISC 2026)
|
||||
*
|
||||
* One pf_test() pass per BSD ip-stack call site:
|
||||
* NF_INET_PRE_ROUTING → pf_test(PF_IN) (OpenBSD ip_input)
|
||||
* NF_INET_LOCAL_OUT → pf_test(PF_OUT) (OpenBSD ip_output, early)
|
||||
* NF_INET_POST_ROUTING → pf_test(PF_OUT) (OpenBSD ip_output, routed)
|
||||
* LOCAL_IN / FORWARD are NF_ACCEPT: the packet was already evaluated on
|
||||
* its PRE (input) and POST (output) passes, exactly like OpenBSD which
|
||||
* runs PF once per direction inside ip_input/ip_output.
|
||||
* BSD netisr pattern (PFLINUX divergence, documented):
|
||||
* hooks NEVER call pf_test() in softirq context — OpenBSD runs input at
|
||||
* IPL_NET with a non-sleeping NET read side, while our compat locks are
|
||||
* sleepable rw_semaphores. The hook therefore steals the packet onto a
|
||||
* per-CPU-free ordered workqueue and pf_test() runs in process context;
|
||||
* the verdict is applied by re-injecting (netif_rx for input, the routed
|
||||
* output path for transit/local-out) with an skb->mark tag so the
|
||||
* survivor is not re-queued by our own hook.
|
||||
*
|
||||
* Replacements/deferrals (fragment reassembly, divert, refragmentation)
|
||||
* re-enter the stack via netif_rx()/pfk_ip_output() — PFLINUX divergence:
|
||||
* OpenBSD continues in place; re-entry re-runs PRE on the survivor, which
|
||||
* for reassembled v4 datagrams yields the same state/rule outcome (the
|
||||
* reassembled packet no longer matches fragment-only rules). Documented
|
||||
* in docs/porting-notes.md.
|
||||
*
|
||||
* (c) 2026 PF-on-Linux contributors — ISC
|
||||
* One pf_test pass per BSD call site:
|
||||
* NF_INET_PRE_ROUTING -> PF_IN (OpenBSD ip_input)
|
||||
* NF_INET_LOCAL_OUT -> PF_OUT (OpenBSD ip_output, locally generated)
|
||||
* NF_INET_POST_ROUTING -> PF_OUT (OpenBSD ip_output, transit)
|
||||
* LOCAL_IN / FORWARD are pass-through (already evaluated).
|
||||
*/
|
||||
#include <pfk.h>
|
||||
#include <pfk_api.h>
|
||||
@@ -24,20 +22,110 @@
|
||||
#include <net/pfvar_priv.h>
|
||||
|
||||
extern int pf_test(sa_family_t, int, struct pfk_ifnet *, struct mbuf **);
|
||||
extern struct workqueue_struct *pfk_netisr_wq;
|
||||
|
||||
/* shared verdict plumbing for v4/v6 (af decides header pull size) */
|
||||
static unsigned int
|
||||
pfk_run(int af, struct sk_buff *skb, const struct nf_hook_state *st)
|
||||
/* PFLINUX: netisr work item — carries ownership of one sk_buff */
|
||||
struct pfk_pkt {
|
||||
struct work_struct work;
|
||||
struct sk_buff *skb;
|
||||
struct pfk_ifnet *ifp;
|
||||
sa_family_t af;
|
||||
int dir;
|
||||
};
|
||||
|
||||
#define PFK_MARK_PASSED 0x50460001u /* skb->mark: survived pf_test already */
|
||||
|
||||
static void
|
||||
pfk_pkt_reinject(struct pfk_pkt *pk)
|
||||
{
|
||||
struct pfk_ifnet *ifp;
|
||||
struct mbuf *m, *m0;
|
||||
int dir, action;
|
||||
struct sk_buff *skb = pk->skb;
|
||||
|
||||
if (af == AF_INET &&
|
||||
!pskb_may_pull(skb, sizeof(struct pf_ip)))
|
||||
return NF_ACCEPT;
|
||||
if (af == AF_INET6 &&
|
||||
!pskb_may_pull(skb, sizeof(struct pf_ip6_hdr)))
|
||||
skb->mark = PFK_MARK_PASSED;
|
||||
if (pk->dir == PF_IN) {
|
||||
if (skb->dev)
|
||||
netif_rx(skb);
|
||||
else {
|
||||
kfree_skb(skb);
|
||||
}
|
||||
} else {
|
||||
/* continue the output path: dst is attached at both
|
||||
* LOCAL_OUT and POST_ROUTING time */
|
||||
struct dst_entry *d = skb_dst(skb);
|
||||
|
||||
if (d && d->dev)
|
||||
skb->dev = d->dev;
|
||||
if (d && !IS_ERR(d) && d->output)
|
||||
d->output(&init_net, skb->sk, skb);
|
||||
else
|
||||
kfree_skb(skb);
|
||||
}
|
||||
kfree(pk);
|
||||
}
|
||||
|
||||
static void
|
||||
pfk_pkt_work(struct work_struct *w)
|
||||
{
|
||||
struct pfk_pkt *pk = container_of(w, struct pfk_pkt, work);
|
||||
struct mbuf *m, *m0;
|
||||
struct sk_buff *orig;
|
||||
int action;
|
||||
|
||||
m = pfk_m_wrap_skb(pk->skb, pk->af);
|
||||
if (!m) {
|
||||
pfk_pkt_reinject(pk); /* like "pf not consulted" */
|
||||
return;
|
||||
}
|
||||
m0 = m;
|
||||
|
||||
/* PFLINUX: no NET read lock on the data path (M2 interim). The
|
||||
* rw-semaphore read side starves softirq-fed packet flows whenever a
|
||||
* control writer parks (purge/ioctl), and BSD's NET_LOCK semantics
|
||||
* are being replaced by pf_lock/RCU work in M5+. Rule-set mutation
|
||||
* and packet walk run concurrently from here on — documented. */
|
||||
action = pf_test(pk->af, pk->dir, pk->ifp, &m);
|
||||
|
||||
if (m == m0) {
|
||||
/* same buffer, possibly rewritten in place: our wrapper
|
||||
* held one extra skb reference — release it */
|
||||
m_freem(m);
|
||||
if (action == PF_PASS) {
|
||||
pfk_pkt_reinject(pk);
|
||||
return;
|
||||
}
|
||||
kfree_skb(pk->skb); /* our queue reference */
|
||||
kfree(pk);
|
||||
return;
|
||||
}
|
||||
|
||||
/* m != m0: pf consumed the wrapper (and its extra reference).
|
||||
* Our queue reference on the original must be resolved here. */
|
||||
orig = pk->skb;
|
||||
if (m == NULL) {
|
||||
/* buffered (fragment queue) or consumed by divert/forward:
|
||||
* pf kept its own references; drop our queue reference */
|
||||
kfree_skb(orig);
|
||||
kfree(pk);
|
||||
return;
|
||||
}
|
||||
/* replacement (reassembly survivor): free our original ref, then
|
||||
* inject the survivor through the stack */
|
||||
pk->skb = m_to_skb(m);
|
||||
m->skb = NULL;
|
||||
m_freem(m); /* wrapper bookkeeping only */
|
||||
kfree_skb(orig);
|
||||
pfk_pkt_reinject(pk);
|
||||
}
|
||||
|
||||
/* shared: decide whether/how to queue this packet */
|
||||
static unsigned int
|
||||
pfk_enqueue_verdict(int af, struct sk_buff *skb,
|
||||
const struct nf_hook_state *st)
|
||||
{
|
||||
struct pfk_pkt *pk;
|
||||
struct pfk_ifnet *ifp;
|
||||
int dir;
|
||||
|
||||
if (skb->mark == PFK_MARK_PASSED)
|
||||
return NF_ACCEPT;
|
||||
|
||||
switch (st->hook) {
|
||||
@@ -49,79 +137,46 @@ pfk_run(int af, struct sk_buff *skb, const struct nf_hook_state *st)
|
||||
dir = PF_OUT;
|
||||
break;
|
||||
default:
|
||||
return NF_ACCEPT; /* LOCAL_IN/FORWARD: seen already */
|
||||
return NF_ACCEPT; /* LOCAL_IN/FORWARD */
|
||||
}
|
||||
|
||||
/* ifp: OpenBSD passes the *input* interface on PF_IN and the
|
||||
* output interface on PF_OUT (from the route). */
|
||||
if (af == AF_INET && !pskb_may_pull(skb, sizeof(struct pf_ip)))
|
||||
return NF_ACCEPT;
|
||||
if (af == AF_INET6 && !pskb_may_pull(skb, sizeof(struct pf_ip6_hdr)))
|
||||
return NF_ACCEPT;
|
||||
|
||||
if (dir == PF_IN)
|
||||
ifp = pfk_ifp_bydev(st->in ? st->in : skb->dev);
|
||||
else
|
||||
ifp = pfk_ifp_bydev(skb->dev ? skb->dev : st->out);
|
||||
if (!ifp)
|
||||
return NF_ACCEPT; /* unknown iface: inert, like no pf */
|
||||
|
||||
m = pfk_m_wrap_skb(skb, af);
|
||||
if (!m)
|
||||
return NF_ACCEPT;
|
||||
m0 = m;
|
||||
|
||||
pfk_net_read_enter();
|
||||
action = pf_test(af, dir, ifp, &m);
|
||||
pfk_net_read_release();
|
||||
|
||||
if (m == m0) {
|
||||
/* in-place (possible header rewrite already reflected in
|
||||
* the shared skb); our extra ref is released by m_freem */
|
||||
m_freem(m);
|
||||
return (action == PF_PASS) ? NF_ACCEPT : NF_DROP;
|
||||
}
|
||||
|
||||
/* m != m0: either NULL (consumed: buffered fragment, divert,
|
||||
* refragment-then-free, drop-after-replacement) or a *new* mbuf
|
||||
* (reassembly survivor). m0 itself was consumed by pf. */
|
||||
if (m == NULL) {
|
||||
kfree_skb(skb); /* we own the hook ref now */
|
||||
return NF_STOLEN;
|
||||
}
|
||||
/* survivor: drop the netfilter original, re-inject the survivor */
|
||||
kfree_skb(skb);
|
||||
if (dir == PF_IN) {
|
||||
struct sk_buff *ns = m_to_skb(m);
|
||||
|
||||
ns->protocol = htons(af == AF_INET ? ETH_P_IP : ETH_P_IPV6);
|
||||
if (!ns->dev)
|
||||
ns->dev = st->in;
|
||||
if (ns->dev)
|
||||
dev_hold(ns->dev);
|
||||
else {
|
||||
kfree_skb(ns);
|
||||
m->skb = NULL;
|
||||
m_freem(m);
|
||||
return NF_STOLEN;
|
||||
}
|
||||
netif_rx(ns);
|
||||
m->skb = NULL; /* reference moved into netif_rx */
|
||||
m_freem(m);
|
||||
} else {
|
||||
/* survivor leaves via the BSD output helper (consumes m) */
|
||||
pfk_ip_output(m, NULL, NULL, 0, NULL, NULL, 0);
|
||||
}
|
||||
pk = kmalloc(sizeof(*pk), GFP_ATOMIC);
|
||||
if (!pk)
|
||||
return NF_ACCEPT; /* overload guard: bypass like full pfq */
|
||||
INIT_WORK(&pk->work, pfk_pkt_work);
|
||||
skb_get(skb); /* our queue reference */
|
||||
pk->skb = skb;
|
||||
pk->ifp = ifp;
|
||||
pk->af = af;
|
||||
pk->dir = dir;
|
||||
queue_work(pfk_netisr_wq, &pk->work);
|
||||
return NF_STOLEN;
|
||||
}
|
||||
|
||||
struct workqueue_struct *pfk_netisr_wq;
|
||||
|
||||
static unsigned int
|
||||
pfk_ipv4_hook(void *priv, struct sk_buff *skb,
|
||||
const struct nf_hook_state *st)
|
||||
pfk_ipv4_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *st)
|
||||
{
|
||||
return pfk_run(AF_INET, skb, st);
|
||||
return pfk_enqueue_verdict(AF_INET, skb, st);
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
pfk_ipv6_hook(void *priv, struct sk_buff *skb,
|
||||
const struct nf_hook_state *st)
|
||||
pfk_ipv6_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *st)
|
||||
{
|
||||
return pfk_run(AF_INET6, skb, st);
|
||||
return pfk_enqueue_verdict(AF_INET6, skb, st);
|
||||
}
|
||||
|
||||
static struct nf_hook_ops pfk_hooks[] = {
|
||||
@@ -153,18 +208,23 @@ pfk_hooks_register(void)
|
||||
{
|
||||
int i, err;
|
||||
|
||||
pr_info("pfk: netfilter hooks registering (%zu, verdict path live)\n",
|
||||
(size_t)PFK_NHOOKS);
|
||||
pfk_netisr_wq = alloc_ordered_workqueue("pfk_netisr", WQ_MEM_RECLAIM);
|
||||
if (!pfk_netisr_wq)
|
||||
return -ENOMEM;
|
||||
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;
|
||||
}
|
||||
pr_info("pfk: netfilter hooks registered (%zu, netisr wq data path)\n",
|
||||
(size_t)PFK_NHOOKS);
|
||||
return 0;
|
||||
fail:
|
||||
while (--i >= 0)
|
||||
nf_unregister_net_hook(&init_net, &pfk_hooks[i]);
|
||||
destroy_workqueue(pfk_netisr_wq);
|
||||
pfk_netisr_wq = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -175,4 +235,9 @@ pfk_hooks_unregister(void)
|
||||
|
||||
for (i = 0; i < PFK_NHOOKS; i++)
|
||||
nf_unregister_net_hook(&init_net, &pfk_hooks[i]);
|
||||
if (pfk_netisr_wq) {
|
||||
flush_workqueue(pfk_netisr_wq);
|
||||
destroy_workqueue(pfk_netisr_wq);
|
||||
pfk_netisr_wq = NULL;
|
||||
}
|
||||
}
|
||||
@@ -123,13 +123,13 @@ static int __init pfk_init(void)
|
||||
err = pfk_if_init();
|
||||
if (err)
|
||||
goto err_pcb;
|
||||
/* OpenBSD bring-up order: status/pools/rulesets, then kifs */
|
||||
pfk_net_lock();
|
||||
/* OpenBSD bring-up order: status/pools/rulesets, then kifs.
|
||||
* No NET_LOCK here: pfattach/pfi take what locks they need (PF_LOCK),
|
||||
* and control ioctls serialize with NET_LOCK themselves. */
|
||||
pf_status_init();
|
||||
pfattach(0);
|
||||
pfk_if_set_event_cb(pfk_pfi_event);
|
||||
pfk_if_iterate(pfk_pfi_attach_one);
|
||||
pfk_net_unlock();
|
||||
err = pfk_hooks_register();
|
||||
if (err)
|
||||
goto err_if;
|
||||
|
||||
Reference in new issue
Block a user