Dear readers, since 5.8 i ve been carrying around patches to manage : * crazy server sending hostname like "crazy ISP name with space" ( in one case the ignore or supersede failed to workaround the problem ), it is a bit hard to test, and it looks like some improvement was made to crash fatal on all invalid network information, * and a bridging case, which is more realistic :
Assuming the EGRESS is Bridged to a vether, a pair or something to serve the same LAN to a VM or something else, the first dhclient will eat the paquet in the BPF filter because MAC are ignored. MAC are ignored for some obscure historic case where the dhcp server responded with broadcast or something. To see the problem , assuming em0 is egress like : # ifconfig em0 em0: flags=808b43<UP,BROADCAST,RUNNING,PROMISC,ALLMULTI,SIMPLEX,MULTICAST,AUTOCONF4> mtu 1500 lladdr 00:ff:18:0b:4a:ee index 1 priority 0 llprio 3 groups: egress media: Ethernet autoselect (100baseTX full-duplex,rxpause,txpause) status: active inet 172.16.1.4 netmask 0xffffff00 broadcast 172.16.1.255 Bridge it to something ifconfig vether0 create ifconfig vether0 rdomain 1 ifconfig bridge0 create ifconfig bridge0 add em0 ifconfig bridge0 add vether0 #safety net echo <<< EOF >> /etc/dhclient interface "vether1" { send host-name "bridged-sub-host-1"; send dhcp-lease-time 3600; ignore domain-name-servers,routers; require subnet-mask,domain-name-servers; } #testing ifconfig bridge0 up route -T 1 exec dhclient vether0 No lease ! because dhclient on em0 is actually filtering them at bpf level A year ago the hw_addr was kept inside the daemon so i am not sure how to apply my bpf filter. To not break something the path add a -m option to dhclient that enable mac bpf filter awareness, In configure_bpf_sock, I added a ` uint8_t * ether_addr_octet` param that is not null and setup in case -m is passed to fill in a slightly different filter. /* Set up the bpf filter program structure. */ p.bf_len = dhcp_bpf_mac_filter_len; p.bf_insns = dhcp_bpf_mac_filter; dhcp_bpf_mac_filter[8].k = LOCAL_PORT; memcpy(&bits16, ether_addr_octet, sizeof(bits16)); dhcp_bpf_mac_filter[10].k = ntohs(bits16); memcpy(&bits, ether_addr_octet + 2, sizeof(bits)); dhcp_bpf_mac_filter[12].k = ntohl(bits); with the filter : struct bpf_insn dhcp_bpf_mac_filter[] = { /* Make sure this is an IP packet. */ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8), /* Make sure it's a UDP packet. */ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6), /* Make sure this isn't a fragment. */ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20), BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 8, 0), /* Get the IP header length. */ BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14), /* Make sure it's to the right port. */ BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1), /* patch */ /* check bootp.hw.addr 2 bytes */ BPF_STMT(BPF_LD + BPF_H + BPF_IND, 50), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0000, 0, 3), /* patch */ /* check bootp.hw.addr 4 bytes */ BPF_STMT(BPF_LD + BPF_W + BPF_IND, 52), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x00000000, 0, 1), /* patch */ /* If we passed all the tests, ask for the whole packet. */ BPF_STMT(BPF_RET+BPF_K, (unsigned int)-1), /* Otherwise, drop it. */ BPF_STMT(BPF_RET+BPF_K, 0), }; int dhcp_bpf_filter_len = sizeof(dhcp_bpf_filter) / sizeof(struct bpf_insn); int dhcp_bpf_mac_filter_len = sizeof(dhcp_bpf_mac_filter) / sizeof(struct bpf_insn); I would gladly test an officially made diff because this is becoming hard to maintain, and there should be use cases out there. The only workaround I know is to put egress in a vether behind the bridges, certainly something that could break anyway. Thanks for reading up to there ! -- -- --------------------------------------------------------------------------------------------------------------------- Knowing is not enough; we must apply. Willing is not enough; we must do