diff options
-rw-r--r-- | README | 2 | ||||
-rw-r--r-- | src/config.c | 20 | ||||
-rw-r--r-- | src/odhcpd.h | 5 | ||||
-rw-r--r-- | src/router.c | 71 |
4 files changed, 97 insertions, 1 deletions
@@ -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 30fdc30..d7cd5dd 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 }, @@ -958,6 +960,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 420d2a7..8ab51dc 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..d5ef7f8 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,65 @@ 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; + uint32_t mask_a1, mask_a2; + + switch (iface->pref64_length) { + case 96: + prefix_length_code = 0; + mask_a1 = 0xffffffff; + mask_a2 = 0xffffffff; + break; + case 64: + prefix_length_code = 1; + mask_a1 = 0xffffffff; + mask_a2 = 0x00000000; + break; + case 56: + prefix_length_code = 2; + mask_a1 = 0xffffff00; + mask_a2 = 0x00000000; + break; + case 48: + prefix_length_code = 3; + mask_a1 = 0xffff0000; + mask_a2 = 0x00000000; + break; + case 40: + prefix_length_code = 4; + mask_a1 = 0xff000000; + mask_a2 = 0x00000000; + break; + case 32: + prefix_length_code = 5; + mask_a1 = 0x00000000; + mask_a2 = 0x00000000; + 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] & htonl(mask_a1); + pref64->addr[2] = iface->pref64_addr.s6_addr32[2] & htonl(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 |