Index | Thread | Search

From:
Florian Obser <florian@openbsd.org>
Subject:
dhcp6leased(8): Install reject route for prefix delegation.
To:
tech <tech@openbsd.org>
Date:
Wed, 17 Sep 2025 17:19:30 +0200

Download raw body.

Thread
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.