dhcp.c: Remove temporary default route properly

DHCP is implemented in propagator in a rather lazy way:
to dynamically configure a network interface, say `eno1',
instead of slinging raw Ethernet frames around like everyone else,
it sets up a (temporary) default null route through that interface
and throws UDP messages with actual bootp payload.

`ip' utility describes such a route as `default dev eno1 scope link'

The idea behind this presumably was to avoid generating IP and UDP
headers manually while the null route is sufficient to send a couple of
link-wide (maybe broadcast) messages and generic enough to work anywhere.

To this date propagator didn't bother to remove that route,
so *in some networks* it had persisted even after the initramfs
(and propagator itself) was long gone, and in other networks did not,
having been luckily replaced by the default route to DHCP gateway.

In the cases where it did the real problem showed itself
after the real userspace (for example, in a live distro)
eventually tried to reconfigure the network (because why not?).
The pesky null route was most often left untouched by the DHCP client
(ALT live distros mostly use dhcpcd), managing to squeeze its way
up the routing table and effectively preventing the host
from network access beyond a router.

If the NFS root is behind a couple routers, any attempt to access
the file system gets stuck — the system keeps tirelessly looking for
the NFS host via ARP.
This commit is contained in:
Arseny Maslennikov 2017-12-14 16:21:28 +03:00
parent dfa3713d0f
commit 6ce39ab483

32
dhcp.c
View File

@ -212,6 +212,37 @@ static int initial_setup_interface(char * device, int s) {
return 0; return 0;
} }
static int interface_cleanup(char * device, int s) {
/*
* Removing the auxiliary route
* made by initial_setup_interface()
*/
struct rtentry route;
struct sockaddr_in* address;
memset(&route, 0, sizeof(route));
route.rt_dev = device;
route.rt_flags = RTF_UP | RTF_GATEWAY;
route.rt_metric = 0;
address = (struct sockaddr_in*) &route.rt_dst;
address->sin_family = AF_INET;
address = (struct sockaddr_in*) &route.rt_gateway;
address->sin_family = AF_INET;
address = (struct sockaddr_in*) &route.rt_genmask;
address->sin_family = AF_INET;
if (ioctl(s, SIOCDELRT, &route)) {
close(s);
log_perror("SIOCDELRT");
return -1;
}
return 0;
}
void set_missing_ip_info(struct interface_info * intf) void set_missing_ip_info(struct interface_info * intf)
{ {
@ -665,6 +696,7 @@ enum return_type perform_dhcp(struct interface_info * intf)
} }
lease = ntohl(lease); lease = ntohl(lease);
interface_cleanup(intf->device, s);
close(s); close(s);
intf->netmask.s_addr = 0; intf->netmask.s_addr = 0;