[PATCH][odhcpd] router: Add PREF64 (RFC 8781) RA option support

Ondřej Caletka ondrej at caletka.cz
Wed Mar 29 05:27:03 PDT 2023


This adds support for RA option that signals NAT64 prefix used
in the network.

Signed-off-by: Ondřej Caletka <ondrej at caletka.cz>
---
 README       |  2 ++
 src/config.c | 20 +++++++++++++++
 src/odhcpd.h |  5 ++++
 src/router.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 96 insertions(+), 1 deletion(-)

diff --git a/README b/README
index ef8758e..24e57d4 100644
--- a/README
+++ b/README
@@ -142,6 +142,8 @@ ra_mtu			integer -			MTU to be advertised in
 							RA messages
 ra_dns			bool	1			Announce DNS configuration in
 							RA messages (RFC8106)
+ra_pref64		string				Announce PREF64 option
+			[IPv6 prefix]			for NAT64 prefix (RFC8781)
 ndproxy_routing		bool	1			Learn routes from NDP
 ndproxy_slave		bool	0			NDProxy external slave
 prefix_filter		string	::/0			Only advertise on-link prefixes within
diff --git a/src/config.c b/src/config.c
index 27e7f03..7d54319 100644
--- a/src/config.c
+++ b/src/config.c
@@ -82,6 +82,7 @@ enum {
 	IFACE_ATTR_RA_HOPLIMIT,
 	IFACE_ATTR_RA_MTU,
 	IFACE_ATTR_RA_DNS,
+	IFACE_ATTR_RA_PREF64,
 	IFACE_ATTR_PD_MANAGER,
 	IFACE_ATTR_PD_CER,
 	IFACE_ATTR_NDPROXY_ROUTING,
@@ -135,6 +136,7 @@ static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = {
 	[IFACE_ATTR_RA_HOPLIMIT] = { .name = "ra_hoplimit", .type = BLOBMSG_TYPE_INT32 },
 	[IFACE_ATTR_RA_MTU] = { .name = "ra_mtu", .type = BLOBMSG_TYPE_INT32 },
 	[IFACE_ATTR_RA_DNS] = { .name = "ra_dns", .type = BLOBMSG_TYPE_BOOL },
+	[IFACE_ATTR_RA_PREF64] = { .name = "ra_pref64", .type = BLOBMSG_TYPE_STRING },
 	[IFACE_ATTR_NDPROXY_ROUTING] = { .name = "ndproxy_routing", .type = BLOBMSG_TYPE_BOOL },
 	[IFACE_ATTR_NDPROXY_SLAVE] = { .name = "ndproxy_slave", .type = BLOBMSG_TYPE_BOOL },
 	[IFACE_ATTR_PREFIX_FILTER] = { .name = "prefix_filter", .type = BLOBMSG_TYPE_STRING },
@@ -953,6 +955,24 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr
 	if ((c = tb[IFACE_ATTR_RA_DNS]))
 		iface->ra_dns = blobmsg_get_bool(c);
 
+	if ((c = tb[IFACE_ATTR_RA_PREF64])) {
+		const char *str = blobmsg_get_string(c);
+		char *astr = malloc(strlen(str) + 1);
+		char *delim;
+		int l;
+
+		if (!astr || !strcpy(astr, str) ||
+				(delim = strchr(astr, '/')) == NULL || (*(delim++) = 0) ||
+				sscanf(delim, "%i", &l) == 0 || l > 128 ||
+				inet_pton(AF_INET6, astr, &iface->pref64_addr) == 0)
+			iface->pref64_length = 0;
+		else
+			iface->pref64_length = l;
+
+		if (astr)
+			free(astr);
+	}
+
 	if ((c = tb[IFACE_ATTR_RA_PREFERENCE])) {
 		const char *prio = blobmsg_get_string(c);
 
diff --git a/src/odhcpd.h b/src/odhcpd.h
index 0550bc2..5dd35f8 100644
--- a/src/odhcpd.h
+++ b/src/odhcpd.h
@@ -34,6 +34,9 @@
 #define ND_OPT_RECURSIVE_DNS 25
 #define ND_OPT_DNS_SEARCH 31
 
+// RFC 8781 defines PREF64 option
+#define ND_OPT_PREF64 38
+
 #define INFINITE_VALID(x) ((x) == 0)
 
 #define _unused __attribute__((unused))
@@ -300,6 +303,8 @@ struct interface {
 	bool ra_advrouter;
 	bool ra_useleasetime;
 	bool ra_dns;
+	uint8_t pref64_length;
+	struct in6_addr pref64_addr;
 	bool no_dynamic_dhcp;
 	bool have_link_local;
 	uint8_t pio_filter_length;
diff --git a/src/router.c b/src/router.c
index eca0bf7..4422eb9 100644
--- a/src/router.c
+++ b/src/router.c
@@ -390,6 +390,7 @@ enum {
 	IOV_RA_ROUTES,
 	IOV_RA_DNS,
 	IOV_RA_SEARCH,
+	IOV_RA_PREF64,
 	IOV_RA_ADV_INTERVAL,
 	IOV_RA_TOTAL,
 };
@@ -427,6 +428,13 @@ struct nd_opt_route_info {
 	uint32_t addr[4];
 };
 
+struct nd_opt_pref64_info {
+	uint8_t type;
+	uint8_t len;
+	uint16_t lifetime_plc;
+	uint32_t addr[3];
+};
+
 /* Router Advert server mode */
 static int send_router_advert(struct interface *iface, const struct in6_addr *from)
 {
@@ -437,10 +445,12 @@ static int send_router_advert(struct interface *iface, const struct in6_addr *fr
 	struct nd_opt_dns_server *dns = NULL;
 	struct nd_opt_search_list *search = NULL;
 	struct nd_opt_route_info *routes = NULL;
+	struct nd_opt_pref64_info *pref64 = NULL;
 	struct nd_opt_adv_interval adv_interval;
 	struct iovec iov[IOV_RA_TOTAL];
 	struct sockaddr_in6 dest;
-	size_t dns_sz = 0, search_sz = 0, pfxs_cnt = 0, routes_cnt = 0;
+	size_t dns_sz = 0, search_sz = 0, pref64_sz = 0;
+	size_t pfxs_cnt = 0, routes_cnt = 0;
 	ssize_t valid_addr_cnt = 0, invalid_addr_cnt = 0;
 	uint32_t minvalid = UINT32_MAX, maxival, lifetime;
 	int msecs, mtu = iface->ra_mtu, hlim = iface->ra_hoplimit;
@@ -698,6 +708,64 @@ static int send_router_advert(struct interface *iface, const struct in6_addr *fr
 	iov[IOV_RA_SEARCH].iov_base = (char *)search;
 	iov[IOV_RA_SEARCH].iov_len = search_sz;
 
+	if (iface->pref64_length) {
+		/* RFC 8781 § 4.1 rounding up lifetime to multiply of 8 */
+		uint16_t pref64_lifetime = lifetime < (UINT16_MAX - 7) ? lifetime + 7 : UINT16_MAX;
+		uint8_t prefix_length_code = 0;
+		uint32_t mask_a1 = 0xffffffff;
+		uint32_t mask_a2 = 0xffffffff;
+
+		switch (iface->pref64_length) {
+		case 96:
+			prefix_length_code = 0;
+			break;
+		case 64:
+			prefix_length_code = 1;
+			mask_a2 = 0;
+			break;
+		case 56:
+			prefix_length_code = 2;
+			mask_a1 = htonl(0xffffff00);
+			mask_a2 = 0;
+			break;
+		case 48:
+			prefix_length_code = 3;
+			mask_a1 = htonl(0xffff0000);
+			mask_a2 = 0;
+			break;
+		case 40:
+			prefix_length_code = 4;
+			mask_a1 = htonl(0xff000000);
+			mask_a2 = 0;
+			break;
+		case 32:
+			prefix_length_code = 5;
+			mask_a1 = 0;
+			mask_a2 = 0;
+			break;
+		default:
+			syslog(LOG_WARNING, "Invalid PREF64 prefix size (%d), "
+					"ignoring ra_pref64 option!", iface->pref64_length);
+			goto pref64_out;
+			break;
+		}
+
+		pref64_sz = sizeof(*pref64);
+		pref64 = alloca(pref64_sz);
+		memset(pref64, 0, pref64_sz);
+		pref64->type = ND_OPT_PREF64;
+		pref64->len = 2;
+		pref64->lifetime_plc = htons((0xfff8 & pref64_lifetime) |
+						(0x7 & prefix_length_code));
+		pref64->addr[0] = iface->pref64_addr.s6_addr32[0];
+		pref64->addr[1] = iface->pref64_addr.s6_addr32[1] & mask_a1;
+		pref64->addr[2] = iface->pref64_addr.s6_addr32[2] & mask_a2;
+	}
+
+pref64_out:
+	iov[IOV_RA_PREF64].iov_base = (char *)pref64;
+	iov[IOV_RA_PREF64].iov_len = pref64_sz;
+
 	/*
 	 * RFC7084 § 4.3 :
 	 *    L-3:   An IPv6 CE router MUST advertise itself as a router for the
-- 
2.39.2




More information about the openwrt-devel mailing list