I hear there are circuses out there where the dhcp server hands us a /32 and so the default gateway is not reachable.
The comment in sbin/dhclient/kroute.c suggests that the Google Clown Platform operates in this way. I seem to recall mumblings that Hetzner does something similar on their VPS. Currently dhcpleased(8) will not work in such setups, I think this diff fixes it. Could someone tests this please in the real world? (This is currently two diffs in one, a bunch of deck chair shuffling and fixing the actual problem. I'll work on classes static routes option next to see if the deck chairs have been shuffled sufficiently. Not asking for OKs just yet.) diff --git dhcpleased.c dhcpleased.c index 46685012402..14bd26eb01b 100644 --- dhcpleased.c +++ dhcpleased.c @@ -77,7 +77,9 @@ void open_bpfsock(uint32_t); void configure_interface(struct imsg_configure_interface *); void deconfigure_interface(struct imsg_configure_interface *); void propose_rdns(struct imsg_propose_rdns *); -void configure_gateway(struct imsg_configure_interface *, uint8_t); +void configure_routes(uint8_t, struct imsg_configure_interface *); +void configure_route(uint8_t, uint32_t, int, struct sockaddr_in *, struct + sockaddr_in *, struct sockaddr_in *, struct sockaddr_in *, int); void read_lease_file(struct imsg_ifinfo *); static int main_imsg_send_ipc_sockets(struct imsgbuf *, struct imsgbuf *); @@ -683,9 +685,8 @@ configure_interface(struct imsg_configure_interface *imsg) if (ioctl(ioctl_sock, SIOCAIFADDR, &ifaliasreq) == -1) fatal("SIOCAIFADDR"); - /* XXX check weird shit in dhclient/kroute.c set_routes() */ if (imsg->router.s_addr != INADDR_ANY) - configure_gateway(imsg, RTM_ADD); + configure_routes(RTM_ADD, imsg); } req_sin_addr->sin_port = ntohs(CLIENT_PORT); if ((udpsock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { @@ -774,7 +775,7 @@ deconfigure_interface(struct imsg_configure_interface *imsg) memset(&ifaliasreq, 0, sizeof(ifaliasreq)); if (imsg->router.s_addr != INADDR_ANY) - configure_gateway(imsg, RTM_DELETE); + configure_routes(RTM_DELETE, imsg); if (if_indextoname(imsg->if_index, ifaliasreq.ifra_name) == NULL) { log_warnx("%s: cannot find interface %d", __func__, @@ -795,14 +796,66 @@ deconfigure_interface(struct imsg_configure_interface *imsg) } } +/* XXX check weird shit in dhclient/kroute.c set_routes() */ +void +configure_routes(uint8_t rtm_type, struct imsg_configure_interface *imsg) +{ + struct sockaddr_in dst, gw, mask, ifa; + in_addr_t addrnet, gwnet; + + memset(&ifa, 0, sizeof(ifa)); + memcpy(&ifa.sin_addr, &imsg->addr, sizeof(ifa.sin_addr)); + ifa.sin_family = AF_INET; + ifa.sin_len = sizeof(struct sockaddr_in); + + addrnet = imsg->addr.s_addr & imsg->mask.s_addr; + gwnet = imsg->router.s_addr & imsg->mask.s_addr; + if (addrnet != gwnet) { + /* + The gateway for the default route is outside the configured + prefix. Install a direct cloning route for the gateway to + make the default route reachable. + */ + memset(&dst, 0, sizeof(dst)); + memcpy(&dst.sin_addr, &imsg->router, sizeof(gw.sin_addr)); + dst.sin_family = AF_INET; + dst.sin_len = sizeof(struct sockaddr_in); + + memset(&mask, 0, sizeof(mask)); + mask.sin_family = AF_INET; + mask.sin_addr.s_addr = 0xffffffff; + mask.sin_len = sizeof(struct sockaddr_in); + + configure_route(rtm_type, imsg->if_index, imsg->rdomain, &dst, + &ifa, &mask, &ifa, RTF_CLONING); + } + + memset(&dst, 0, sizeof(dst)); + dst.sin_family = AF_INET; + dst.sin_len = sizeof(struct sockaddr_in); + + memset(&gw, 0, sizeof(gw)); + memcpy(&gw.sin_addr, &imsg->router, sizeof(gw.sin_addr)); + gw.sin_family = AF_INET; + gw.sin_len = sizeof(struct sockaddr_in); + + memset(&mask, 0, sizeof(mask)); + mask.sin_family = AF_INET; + mask.sin_len = sizeof(struct sockaddr_in); + + configure_route(rtm_type, imsg->if_index, imsg->rdomain, &dst, &gw, + &mask, &ifa, RTF_GATEWAY); +} + #define ROUNDUP(a) \ (((a) & (sizeof(long) - 1)) ? (1 + ((a) | (sizeof(long) - 1))) : (a)) void -configure_gateway(struct imsg_configure_interface *imsg, uint8_t rtm_type) +configure_route(uint8_t rtm_type, uint32_t if_index, int rdomain, struct + sockaddr_in *dst, struct sockaddr_in *gw, struct sockaddr_in *mask, struct + sockaddr_in *ifa, int rtm_flags) { struct rt_msghdr rtm; struct sockaddr_rtlabel rl; - struct sockaddr_in dst, gw, mask, ifa; struct iovec iov[12]; long pad = 0; int iovcnt = 0, padlen; @@ -812,66 +865,51 @@ configure_gateway(struct imsg_configure_interface *imsg, uint8_t rtm_type) rtm.rtm_version = RTM_VERSION; rtm.rtm_type = rtm_type; rtm.rtm_msglen = sizeof(rtm); - rtm.rtm_tableid = imsg->rdomain; - rtm.rtm_index = imsg->if_index; + rtm.rtm_index = if_index; + rtm.rtm_tableid = rdomain; rtm.rtm_seq = ++rtm_seq; rtm.rtm_priority = RTP_NONE; rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_IFA | RTA_LABEL; - rtm.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC | RTF_MPATH; + rtm.rtm_flags = RTF_UP | RTF_STATIC | RTF_MPATH | rtm_flags; iov[iovcnt].iov_base = &rtm; iov[iovcnt++].iov_len = sizeof(rtm); - memset(&dst, 0, sizeof(dst)); - dst.sin_family = AF_INET; - dst.sin_len = sizeof(struct sockaddr_in); - - iov[iovcnt].iov_base = &dst; - iov[iovcnt++].iov_len = sizeof(dst); - rtm.rtm_msglen += sizeof(dst); - padlen = ROUNDUP(sizeof(dst)) - sizeof(dst); + iov[iovcnt].iov_base = dst; + iov[iovcnt++].iov_len = dst->sin_len; + rtm.rtm_msglen += dst->sin_len; + padlen = ROUNDUP(dst->sin_len) - dst->sin_len; if (padlen > 0) { iov[iovcnt].iov_base = &pad; iov[iovcnt++].iov_len = padlen; rtm.rtm_msglen += padlen; } - memset(&gw, 0, sizeof(gw)); - memcpy(&gw.sin_addr, &imsg->router, sizeof(gw.sin_addr)); - gw.sin_family = AF_INET; - gw.sin_len = sizeof(struct sockaddr_in); - iov[iovcnt].iov_base = &gw; - iov[iovcnt++].iov_len = sizeof(gw); - rtm.rtm_msglen += sizeof(gw); - padlen = ROUNDUP(sizeof(gw)) - sizeof(gw); + iov[iovcnt].iov_base = gw; + iov[iovcnt++].iov_len = gw->sin_len; + rtm.rtm_msglen += gw->sin_len; + padlen = ROUNDUP(gw->sin_len) - gw->sin_len; if (padlen > 0) { iov[iovcnt].iov_base = &pad; iov[iovcnt++].iov_len = padlen; rtm.rtm_msglen += padlen; } - memset(&mask, 0, sizeof(mask)); - mask.sin_family = AF_INET; - mask.sin_len = sizeof(struct sockaddr_in); - iov[iovcnt].iov_base = &mask; - iov[iovcnt++].iov_len = sizeof(mask); - rtm.rtm_msglen += sizeof(mask); - padlen = ROUNDUP(sizeof(mask)) - sizeof(mask); + iov[iovcnt].iov_base = mask; + iov[iovcnt++].iov_len = mask->sin_len; + rtm.rtm_msglen += mask->sin_len; + padlen = ROUNDUP(mask->sin_len) - mask->sin_len; if (padlen > 0) { iov[iovcnt].iov_base = &pad; iov[iovcnt++].iov_len = padlen; rtm.rtm_msglen += padlen; } - memset(&ifa, 0, sizeof(ifa)); - memcpy(&ifa.sin_addr, &imsg->addr, sizeof(ifa.sin_addr)); - ifa.sin_family = AF_INET; - ifa.sin_len = sizeof(struct sockaddr_in); - iov[iovcnt].iov_base = &ifa; - iov[iovcnt++].iov_len = sizeof(ifa); - rtm.rtm_msglen += sizeof(ifa); - padlen = ROUNDUP(sizeof(ifa)) - sizeof(ifa); + iov[iovcnt].iov_base = ifa; + iov[iovcnt++].iov_len = ifa->sin_len; + rtm.rtm_msglen += ifa->sin_len; + padlen = ROUNDUP(ifa->sin_len) - ifa->sin_len; if (padlen > 0) { iov[iovcnt].iov_base = &pad; iov[iovcnt++].iov_len = padlen; -- I'm not entirely sure you are real.