From: Florian Obser Subject: dhcp6leased(8): Install reject route for prefix delegation. To: tech Date: Wed, 17 Sep 2025 17:19:30 +0200 This prevents routing loops in case only parts of the delegated prefix are configured on interfaces. OK? diff --git dhcp6leased.c dhcp6leased.c index ae29f5a9436..90333592b95 100644 --- dhcp6leased.c +++ dhcp6leased.c @@ -76,6 +76,7 @@ void main_dispatch_engine(int, short, void *); void open_udpsock(uint32_t); void configure_address(struct imsg_configure_address *); void deconfigure_address(struct imsg_configure_address *); +void configure_reject_route(struct imsg_configure_reject_route *, uint8_t); void read_lease_file(struct imsg_ifinfo *); uint8_t *get_uuid(void); void write_lease_file(struct imsg_lease_info *); @@ -553,6 +554,26 @@ main_dispatch_engine(int fd, short event, void *bula) deconfigure_address(&imsg_configure_address); break; } + case IMSG_CONFIGURE_REJECT_ROUTE: { + struct imsg_configure_reject_route imsg_crr; + if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_crr)) + fatalx("%s: IMSG_CONFIGURE_REJECT_ROUTE wrong " + "length: %lu", __func__, + IMSG_DATA_SIZE(imsg)); + memcpy(&imsg_crr, imsg.data, sizeof(imsg_crr)); + configure_reject_route(&imsg_crr, RTM_ADD); + break; + } + case IMSG_DECONFIGURE_REJECT_ROUTE: { + struct imsg_configure_reject_route imsg_crr; + if (IMSG_DATA_SIZE(imsg) != sizeof(imsg_crr)) + fatalx("%s: IMSG_CONFIGURE_REJECT_ROUTE wrong " + "length: %lu", __func__, + IMSG_DATA_SIZE(imsg)); + memcpy(&imsg_crr, imsg.data, sizeof(imsg_crr)); + configure_reject_route(&imsg_crr, RTM_DELETE); + break; + } case IMSG_WRITE_LEASE: { struct imsg_lease_info imsg_lease_info; if (IMSG_DATA_SIZE(imsg) != @@ -771,6 +792,99 @@ deconfigure_address(struct imsg_configure_address *address) log_warn("%s: cannot remove address", __func__); } +#define ROUNDUP(a) \ + (((a) & (sizeof(long) - 1)) ? (1 + ((a) | (sizeof(long) - 1))) : (a)) + +void +configure_reject_route(struct imsg_configure_reject_route *reject_route, + uint8_t rtm_type) +{ + struct rt_msghdr rtm; + struct sockaddr_rtlabel rl; + struct sockaddr_in6 dst, gw, mask; + struct iovec iov[10]; + long pad = 0; + int iovcnt = 0, padlen; + + memset(&rtm, 0, sizeof(rtm)); + + rtm.rtm_version = RTM_VERSION; + rtm.rtm_type = rtm_type; + rtm.rtm_msglen = sizeof(rtm); + rtm.rtm_tableid = reject_route->rdomain; + rtm.rtm_index = reject_route->if_index; + rtm.rtm_seq = ++rtm_seq; + rtm.rtm_priority = RTP_DEFAULT; + rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_LABEL; + rtm.rtm_flags = RTF_UP | RTF_REJECT | RTF_GATEWAY | RTF_STATIC; + + iov[iovcnt].iov_base = &rtm; + iov[iovcnt++].iov_len = sizeof(rtm); + + memset(&dst, 0, sizeof(dst)); + dst.sin6_family = AF_INET6; + dst.sin6_len = sizeof(struct sockaddr_in6); + memcpy(&dst.sin6_addr, &reject_route->prefix, sizeof(dst.sin6_addr)); + + iov[iovcnt].iov_base = &dst; + iov[iovcnt++].iov_len = sizeof(dst); + rtm.rtm_msglen += sizeof(dst); + padlen = ROUNDUP(sizeof(dst)) - sizeof(dst); + if (padlen > 0) { + iov[iovcnt].iov_base = &pad; + iov[iovcnt++].iov_len = padlen; + rtm.rtm_msglen += padlen; + } + + memset(&gw, 0, sizeof(gw)); + gw.sin6_family = AF_INET6; + gw.sin6_len = sizeof(struct sockaddr_in6); + memcpy(&gw.sin6_addr, &in6addr_loopback, sizeof(gw.sin6_addr)); + + iov[iovcnt].iov_base = &gw; + iov[iovcnt++].iov_len = sizeof(gw); + rtm.rtm_msglen += sizeof(gw); + padlen = ROUNDUP(sizeof(gw)) - sizeof(gw); + if (padlen > 0) { + iov[iovcnt].iov_base = &pad; + iov[iovcnt++].iov_len = padlen; + rtm.rtm_msglen += padlen; + } + + memset(&mask, 0, sizeof(mask)); + mask.sin6_family = AF_INET6; + mask.sin6_len = sizeof(struct sockaddr_in6); + memcpy(&mask.sin6_addr, &reject_route->mask, sizeof(mask.sin6_addr)); + + iov[iovcnt].iov_base = &mask; + iov[iovcnt++].iov_len = sizeof(mask); + rtm.rtm_msglen += sizeof(mask); + padlen = ROUNDUP(sizeof(mask)) - sizeof(mask); + if (padlen > 0) { + iov[iovcnt].iov_base = &pad; + iov[iovcnt++].iov_len = padlen; + rtm.rtm_msglen += padlen; + } + + memset(&rl, 0, sizeof(rl)); + rl.sr_len = sizeof(rl); + rl.sr_family = AF_UNSPEC; + (void)snprintf(rl.sr_label, sizeof(rl.sr_label), "%s", + DHCP6LEASED_RTA_LABEL); + iov[iovcnt].iov_base = &rl; + iov[iovcnt++].iov_len = sizeof(rl); + rtm.rtm_msglen += sizeof(rl); + padlen = ROUNDUP(sizeof(rl)) - sizeof(rl); + if (padlen > 0) { + iov[iovcnt].iov_base = &pad; + iov[iovcnt++].iov_len = padlen; + rtm.rtm_msglen += padlen; + } + + if (writev(routesock, iov, iovcnt) == -1) + log_warn("failed to send route message"); +} + const char* sin6_to_str(struct sockaddr_in6 *sin6) { diff --git dhcp6leased.h dhcp6leased.h index 977a4cbfaee..c229e95a7ef 100644 --- dhcp6leased.h +++ dhcp6leased.h @@ -171,6 +171,8 @@ enum imsg_type { IMSG_DHCP, IMSG_CONFIGURE_ADDRESS, IMSG_DECONFIGURE_ADDRESS, + IMSG_CONFIGURE_REJECT_ROUTE, + IMSG_DECONFIGURE_REJECT_ROUTE, IMSG_REQUEST_REBOOT, IMSG_WRITE_LEASE, }; diff --git engine.c engine.c index 6b660983818..98784ca9b90 100644 --- engine.c +++ engine.c @@ -139,6 +139,9 @@ void deprecate_interfaces(struct dhcp6leased_iface *); int prefixcmp(struct prefix *, struct prefix *, int); void send_reconfigure_interface(struct iface_pd_conf *, struct prefix *, enum reconfigure_action); +void send_reconfigure_reject_route( + struct dhcp6leased_iface *, struct in6_addr *, + uint8_t, enum reconfigure_action); int engine_imsg_compose_main(int, pid_t, void *, uint16_t); const char *dhcp_option_type2str(int); const char *dhcp_duid2str(int, uint8_t *); @@ -1305,7 +1308,6 @@ request_dhcp_request(struct dhcp6leased_iface *iface) } } -/* XXX we need to install a reject route for the delegated prefix */ void configure_interfaces(struct dhcp6leased_iface *iface) { @@ -1336,6 +1338,9 @@ configure_interfaces(struct dhcp6leased_iface *iface) "server %s", i, inet_ntop(AF_INET6, &pd->prefix, ntopbuf, INET6_ADDRSTRLEN), pd->prefix_len, if_name, dhcp_duid2str(iface->serverid_len, iface->serverid)); + + send_reconfigure_reject_route(iface, &pd->prefix, + pd->prefix_len, CONFIGURE); } SIMPLEQ_FOREACH(ia_conf, &iface_conf->iface_ia_list, entry) { @@ -1402,6 +1407,8 @@ deconfigure_interfaces(struct dhcp6leased_iface *iface) "server %s", i, inet_ntop(AF_INET6, &pd->prefix, ntopbuf, INET6_ADDRSTRLEN), pd->prefix_len, if_name, dhcp_duid2str(iface->serverid_len, iface->serverid)); + send_reconfigure_reject_route(iface, &pd->prefix, + pd->prefix_len, DECONFIGURE); } SIMPLEQ_FOREACH(ia_conf, &iface_conf->iface_ia_list, entry) { @@ -1531,6 +1538,27 @@ send_reconfigure_interface(struct iface_pd_conf *pd_conf, struct prefix *pd, sizeof(address)); } +void +send_reconfigure_reject_route(struct dhcp6leased_iface *iface, + struct in6_addr *prefix, uint8_t prefix_len, enum reconfigure_action action) +{ + struct imsg_configure_reject_route imsg; + + memset(&imsg, 0, sizeof(imsg)); + + imsg.if_index = iface->if_index; + imsg.rdomain = iface->rdomain; + memcpy(&imsg.prefix, prefix, sizeof(imsg.prefix)); + in6_prefixlen2mask(&imsg.mask, prefix_len); + + if (action == CONFIGURE) + engine_imsg_compose_main(IMSG_CONFIGURE_REJECT_ROUTE, 0, &imsg, + sizeof(imsg)); + else + engine_imsg_compose_main(IMSG_DECONFIGURE_REJECT_ROUTE, 0, + &imsg, sizeof(imsg)); +} + const char * dhcp_message_type2str(int type) { diff --git engine.h engine.h index 61c0fd15f7a..add40126924 100644 --- engine.h +++ engine.h @@ -24,5 +24,12 @@ struct imsg_configure_address { uint32_t pltime; }; +struct imsg_configure_reject_route { + uint32_t if_index; + int rdomain; + struct in6_addr prefix; + struct in6_addr mask; +}; + void engine(int, int); int engine_imsg_compose_frontend(int, pid_t, void *, uint16_t); -- In my defence, I have been left unsupervised.