* 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

Reply via email to