summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--README2
-rw-r--r--src/config.c20
-rw-r--r--src/odhcpd.h5
-rw-r--r--src/router.c71
4 files changed, 97 insertions, 1 deletions
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 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