The current Debian bootpc and bootpd (from bootp) do not appear to actually work together in today's Linux kernel environment. There seems to be a 🐔/🥚 problem; bootpd sends the replies as unicast UDP packets to the as-yet-unconfigured IP address. The client's kernel then drops them w/out delivering to the bootpc client's socket, b/c that IP address is not (yet) a valid local address on the host.
How did this ever work?
- Is there a kernel parameter or other modification that can get the kernel to give these packets to the
bootpcprocess? - Is there a configuration for
bootpdthat will get it to use all 1's or all 0's destination IP addresses instead of the as-yet-unconfigured client IP unicast address?
We have a prospective customer with a large bootp non-DHCP infrastructure. Old-school bootp support is one of their requirements.
Issue Details
bootpdsends its reply packets as unicast packets to the bootpc client's MAC address, using a destination IP address value of the address that it is also in the payload telling the client to configure itself with.- The kernel then drops these packets, rather than deliver them to the
bootpcprocess that requested them. bootpchas correctly opened a listening socket for0.0.0.0:68 0.0.0.0:*(see netstat output below)- I've verified this analysis in several ways:
- I've run
tcpdumpand I can see the replies getting to the NIC - I've run
dropwatchand see the packets getting dropped with a reason ofIPINADDRERROR, which basically means "invalid IP address" - I can trick
bootpdinto using 0.0.0.0 as the destination IP address by omitting and actual IP assignment; when I do this,bootpcgets the responses and processes them. However this doesn't help b/c then the client doesn't get an IP address - I've tried adding the IP address to the interface while
bootpcis making requests. Once I add it, the next reply gets to the process.
- I've run
- I've also tried the
bootpc --serverbcastoption. This fails for a similar reason:bootpdsends the replies to the subnet broadcast address (e.g. 10.0.43.255)- Since the IP address & subnet mask aren't configured on an interface yet, the kernel has no reason to consider this a valid address for itself.
- Here is our current
bootptabconfiguration:
.vs-default:\ :sm=255.255.254.0:\ :gw=10.0.42.1:\ :ds=10.0.42.1:\ :hn: client-ad02-vs:\ ht=1:\ ha=0xea4a1fad0002:\ ip=10.0.42.31:\ tc=.vs-default: Netstat output showing bootpc's listening socket:
$ sudo netstat -unlp Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name udp 0 0 0.0.0.0:68 0.0.0.0:* 2295/bootpc Hacky Workaround
I've come up with a work-around that feels hacky. In particular, I think it leverages an unintended behavior, maybe we can't count on it to work in the future:
- the iptables conntrack module has a match
ctstatewith a value ofDNAT. - This can be used to accept packets that have an unrecognized destination IP address
- I don't think it is intended to match arbitrary DST IP's:
- It can only be used with packets that were NAT'd by the same host's kernel
- I think its purpose is to match packets that have had their address rewritten (which we are not doing), but it is a "cheap" implementation that ignores the NAT mapping table instead of verifying that there is an entry that matches this specific packet.
- I'm concerned this will either be removed, or re-written to be less promiscuous in the future
- To get this to work, I have to port-map these packets to their existing DST port, just to get NAT connection state data onto the packet, otherwise
--ctstate DNATdoesn't apply to the packet.
Background/Due Diligence
- Our testbed is using is Debian 12 on Linux 6.1.0.
- Our testbed hosts are from UTM Debian 12 off-the-shelf VM images
bootpdhas very few cli options, none of which involve replies, ports or addresses. The man page doesn't mention "broadcast" or "unicast", and references to "address" or "destination" are few & not relevant.- Our actual product is Debian 12 on a custom Linux 5.19.9 kernel (and behaves the same way)
- IP connectivity is fully working between the testbed client and server
bootpcrefuses to send requests unless the following are true:- There is no routable IP address already on the interface (169.254/16 is allowed, although I've tried it without too)
- There is a 0/0 default route pointing to the interface where the bootp server is expected
- Otherwise it says
network unreachable - Note: specifying
--dev <iface>on thebootpccommand line doesn't help with this
- Otherwise it says
- I'm still working on evaluating
bootptabconfiguration options to see if there are any that affect this. Haven't found a good reference for them, the manbootptabman page is very terse about them.
Alternate Workaround
I've been able to get this to work with a less convoluted iptables solution: I added a NAT rule that matches UDP port 68 (bootpc), and maps the destination IP address to 255.255.255.255 (and keeps the same UDP port).
This works, but I consider this hacky. Clearly this isn't how the protocol is meant to work, so I'd prefer a "real" solution if possible.