diff options
author | Ondřej Caletka <ondrej@caletka.cz> | 2022-06-04 23:42:59 +0200 |
---|---|---|
committer | Christian Marangi <ansuelsmth@gmail.com> | 2023-06-23 14:09:59 +0200 |
commit | c6bff6f1c0fbb37a21a7f54e393615bad22a72d9 (patch) | |
tree | c6228ad688e168e0d52abde6e365fcacc933ffa8 /src | |
parent | 40ab806bf4c8698ac925d094ce11ce1d57ae2e46 (diff) |
router: Add PREF64 (RFC 8781) support
This option of IPv6 Router Advertisements allows devices connected to
a IPv6-only network to discover IPv6 prefix of the NAT64 gateway.
Devices can use this information for instance to setup client translator
(CLAT) from IPv4 to IPv6 in 464XLAT (RFC 6877) scenario or to handle
IPv4 address literal on application level.
To enable PREF64 option, a new uci parameter ra_pref64 has to contain
the NAT64 prefix, including prefix length. Only lengths of 96, 64, 56,
48, 40 and 32 bits are supported. For example, to annonce the Well-Known
Prefix:
config dhcp 'lan'
…
option ra_pref64 '64:ff9b::/96'
Fixes: #182
Signed-off-by: Ondřej Caletka <ondrej@caletka.cz>
[ remove extra space for Fixes tag ]
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/config.c | 20 | ||||
-rw-r--r-- | src/odhcpd.h | 5 | ||||
-rw-r--r-- | src/router.c | 71 |
3 files changed, 95 insertions, 1 deletions
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 |