* Ralf Horstmann <[email protected]> [2020-11-06 18:18]:
> Why exactly the write filter wants to drop these packets is unclear, the
> dhcpd BPF program looks like it should accept them.
It's more clear now :-) When dhcpd sends a response, BPF is actually called
twice, since dhcpd installs both a BPF write and read filter:
1. bpfwrite -> bpf_movein -> bpf_filter -> _bpf_filter
this call of _bpf_filter processes the bd_wfilter of the writing BPF socket
2. carp_transmit -> bpf_mtap_ether -> bpf_mtap -> _bpf_mtap -> bpf_mfilter ->
_bpf_filter
this call of _bpf_filter processes all bp_rfilter on the sending interface
>From this I would understand that the second BPF call in carp_transmit is
intended to feed back outbound packets to all BPF listeners on that interface.
And not to make a decision whether a packet should be dropped or permitted. So
the return value of bpf_mtap_ether should be ignored in carp_transmit. And
hence I think my initial patch is correct:
Index: sys/netinet/ip_carp.c
===================================================================
RCS file: /home/cvs/src/sys/netinet/ip_carp.c,v
retrieving revision 1.349
diff -u -u -r1.349 ip_carp.c
--- sys/netinet/ip_carp.c 28 Jul 2020 16:44:34 -0000 1.349
+++ sys/netinet/ip_carp.c 4 Nov 2020 22:13:42 -0000
@@ -2282,10 +2282,8 @@
#if NBPFILTER > 0
{
caddr_t if_bpf = ifp->if_bpf;
- if (if_bpf) {
- if (bpf_mtap_ether(if_bpf, m, BPF_DIRECTION_OUT))
- m_freem(m);
- }
+ if (if_bpf)
+ bpf_mtap_ether(if_bpf, m, BPF_DIRECTION_OUT);
}
#endif /* NBPFILTER > 0 */
I've been running this in production in a 6.8 kernel for a week now, no
problems observed so far.
Here is a scapy script to reproduce the kernel panic with dhcpd running on a
carp interface (IP addresses likely need to be adjusted to the dhcpd
configuration):
#!/usr/bin/env python2
from scapy.all import *
dhcp_server = '192.168.0.111'
dhcp_gateway = '192.168.0.222'
ether = Ether(dst='00:00:5e:00:01:01')
ip = IP(src=dhcp_gateway, dst=dhcp_server)
udp = UDP(sport=67, dport=67)
bootp = BOOTP(chaddr='00:01:02:03:04:05', giaddr=dhcp_gateway)
dhcp = DHCP(options=[("message-type","discover")])
dhcp_request = ether/ip/udp/bootp/dhcp
sendp(dhcp_request)
Regards,
Ralf