I am playing around with rumpkernel and NetBSD with BRIDGE_IPF enabled. I have a bridge with 2 interfaces dpdk0 and dpdk1.
NPF rules are such that all stateful filtering happens on dpdk0 interface only. dpdk1 has a single rule NPF_RULE_IN | NPF_RULE_OUT | NPF_RULE_PASS | NPF_RULE_FINAL.
PFIL_IN direction (packets from bridge_forward) works just fine - npf hooks are called and npf connection tracking entries are created as expected.
PFIL_OUT direction (packets from bridge_enqueue) does not work - npf hook is called but mbuf chain is not what npf code expects.
Let me explain what happens. Please refer to bridge_ipf() code (sys/net/if_bridge.c).
If you look at bridge_ipf(), it strips ethernet header, calls pfil hooks and restores ethernet header by adding it back with M_PREPEND(). Even though mbuf has leading space (we just removed same amount of bytes with m_adj()), it cannot be used because mbuf is not writable. Thus M_PREPEND() causes adding of new mbuf.
So on first pass (PFIL_IN) everything is fine, but on second pass (PFIL_OUT) we end up with mbuf chain where first mbuf is empty before call to pfil hooks.
Normally this wouldn't be a problem but npf code (specifically npf_cache_ip()) seems to assume that data has been pulled up. It ends up checking IP version from first byte of L2 header and fails.
Current code in bridge_ipf() calls bridge_ip_checkbasic()/bridge_ip6_checkbasic() that does pullup. But it only calls it for PFIL_IN direction. Why is it not done for both directions?
Long story short, attached patch fixed the issue for me.
Thanks,
Krists