summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMikael Magnusson <mikma@users.sourceforge.net>2021-07-22 00:07:28 +0200
committerMikael Magnusson <mikma@users.sourceforge.net>2021-09-21 22:27:25 +0200
commit60f8f6eda17b868216e8e58fd370505502cd80ac (patch)
treebfda8bb6abd1baf036f53168cf2d76ff49513f57
parentd28760aea75c8da6ebe23775b1b81318729516c1 (diff)
dhcpv6-ia: implement temporary addresses (IA_TA)
-rw-r--r--src/config.c8
-rw-r--r--src/dhcpv6-ia.c114
-rw-r--r--src/dhcpv6.h1
-rw-r--r--src/odhcpd.h4
-rw-r--r--src/ubus.c2
5 files changed, 88 insertions, 41 deletions
diff --git a/src/config.c b/src/config.c
index bbac334..824b30d 100644
--- a/src/config.c
+++ b/src/config.c
@@ -39,7 +39,7 @@ struct config config = {.legacy = false, .main_dhcpv4 = false,
#define HOSTID_LEN_MAX 64
#define HOSTID_LEN_DEFAULT HOSTID_LEN_MIN
-#define OAF_DHCPV6 (OAF_DHCPV6_NA | OAF_DHCPV6_PD)
+#define OAF_DHCPV6 (OAF_DHCPV6_NA | OAF_DHCPV6_PD | OAF_DHCPV6_TA)
enum {
IFACE_ATTR_INTERFACE,
@@ -65,6 +65,7 @@ enum {
IFACE_ATTR_DHCPV6_ASSIGNALL,
IFACE_ATTR_DHCPV6_PD,
IFACE_ATTR_DHCPV6_NA,
+ IFACE_ATTR_DHCPV6_TA,
IFACE_ATTR_DHCPV6_HOSTID_LEN,
IFACE_ATTR_RA_DEFAULT,
IFACE_ATTR_RA_MANAGEMENT,
@@ -115,6 +116,7 @@ static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = {
[IFACE_ATTR_DHCPV6_ASSIGNALL] = { .name ="dhcpv6_assignall", .type = BLOBMSG_TYPE_BOOL },
[IFACE_ATTR_DHCPV6_PD] = { .name = "dhcpv6_pd", .type = BLOBMSG_TYPE_BOOL },
[IFACE_ATTR_DHCPV6_NA] = { .name = "dhcpv6_na", .type = BLOBMSG_TYPE_BOOL },
+ [IFACE_ATTR_DHCPV6_TA] = { .name = "dhcpv6_ta", .type = BLOBMSG_TYPE_BOOL },
[IFACE_ATTR_DHCPV6_HOSTID_LEN] = { .name = "dhcpv6_hostidlength", .type = BLOBMSG_TYPE_INT32 },
[IFACE_ATTR_PD_MANAGER] = { .name = "pd_manager", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_PD_CER] = { .name = "pd_cer", .type = BLOBMSG_TYPE_STRING },
@@ -213,6 +215,7 @@ static void set_interface_defaults(struct interface *iface)
iface->dhcpv6_assignall = true;
iface->dhcpv6_pd = true;
iface->dhcpv6_na = true;
+ iface->dhcpv6_ta = true;
iface->dhcpv6_hostid_len = HOSTID_LEN_DEFAULT;
iface->dns_service = true;
iface->ra_flags = ND_RA_FLAG_OTHER;
@@ -768,6 +771,9 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr
if ((c = tb[IFACE_ATTR_DHCPV6_NA]))
iface->dhcpv6_na = blobmsg_get_bool(c);
+ if ((c = tb[IFACE_ATTR_DHCPV6_TA]))
+ iface->dhcpv6_ta = blobmsg_get_bool(c);
+
if ((c = tb[IFACE_ATTR_DHCPV6_HOSTID_LEN])) {
uint32_t hostid_len = blobmsg_get_u32(c);
diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c
index bd4b6e1..bb13b9f 100644
--- a/src/dhcpv6-ia.c
+++ b/src/dhcpv6-ia.c
@@ -53,6 +53,7 @@ static struct uloop_timeout valid_until_timeout = {.cb = valid_until_cb};
static uint32_t serial = 0;
static uint8_t statemd5[16];
static prng_context_t *prng;
+static uint64_t ta_seed = 0; /* TODO initialize from stable storage, or with random value. */
int dhcpv6_ia_init(void)
{
@@ -248,12 +249,12 @@ void dhcpv6_ia_enum_addrs(struct interface *iface, struct dhcp_assignment *c,
pref = addrs[i].preferred;
valid = addrs[i].valid;
- if (c->flags & OAF_DHCPV6_NA) {
+ if (c->flags & (OAF_DHCPV6_NA | OAF_DHCPV6_TA)) {
if (!ADDR_ENTRY_VALID_IA_ADDR(iface, i, m, addrs))
continue;
- addr.s6_addr32[2] = htonl(c->assigned_host_id >> 32);
- addr.s6_addr32[3] = htonl(c->assigned_host_id & UINT32_MAX);
+ addr.s6_addr32[2] |= htonl(c->assigned_host_id >> 32);
+ addr.s6_addr32[3] |= htonl(c->assigned_host_id & UINT32_MAX);
} else {
if (!valid_prefix_length(c, addrs[i].prefix))
continue;
@@ -299,7 +300,7 @@ static void dhcpv6_write_ia_addr(struct in6_addr *addr, int prefix, _unused uint
inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf) - 1);
- if ((ctxt->c->flags & OAF_DHCPV6_NA) && ctxt->c->hostname &&
+ if ((ctxt->c->flags & (OAF_DHCPV6_NA | OAF_DHCPV6_TA)) && ctxt->c->hostname &&
!(ctxt->c->flags & OAF_BROKEN_HOSTNAME)) {
fputs(ipbuf, ctxt->fp);
@@ -376,7 +377,7 @@ void dhcpv6_ia_write_statefile(void)
(int64_t)(ctxt.c->valid_until - now + wall_time) :
(INFINITE_VALID(ctxt.c->valid_until) ? -1 : 0)));
- if (ctxt.c->flags & OAF_DHCPV6_NA)
+ if (ctxt.c->flags & (OAF_DHCPV6_NA | OAF_DHCPV6_TA))
ctxt.buf_idx += snprintf(ctxt.buf + ctxt.buf_idx, ctxt.buf_len - ctxt.buf_idx,
"%" PRIx64" %u ", ctxt.c->assigned_host_id, (unsigned)ctxt.c->length);
else
@@ -460,7 +461,7 @@ void dhcpv6_ia_write_statefile(void)
static void __apply_lease(struct dhcp_assignment *a,
struct odhcpd_ipaddr *addrs, ssize_t addr_len, bool add)
{
- if (a->flags & OAF_DHCPV6_NA)
+ if (a->flags & (OAF_DHCPV6_NA | OAF_DHCPV6_TA))
return;
for (ssize_t i = 0; i < addr_len; ++i) {
@@ -640,7 +641,7 @@ static bool assign_pd(struct interface *iface, struct dhcp_assignment *assign)
uint32_t current = 1, asize = (1 << (64 - assign->length)) - 1;
if (assign->assigned_subnet_id) {
list_for_each_entry(c, &iface->ia_assignments, head) {
- if (c->flags & OAF_DHCPV6_NA)
+ if (c->flags & (OAF_DHCPV6_NA | OAF_DHCPV6_TA))
continue;
if (assign->assigned_subnet_id >= current && assign->assigned_subnet_id + asize < c->assigned_subnet_id) {
@@ -659,7 +660,7 @@ static bool assign_pd(struct interface *iface, struct dhcp_assignment *assign)
/* Fallback to a variable assignment */
current = 1;
list_for_each_entry(c, &iface->ia_assignments, head) {
- if (c->flags & OAF_DHCPV6_NA)
+ if (c->flags & (OAF_DHCPV6_NA | OAF_DHCPV6_TA))
continue;
current = (current + asize) & (~asize);
@@ -701,14 +702,14 @@ static bool is_reserved_ipv6_iid(uint64_t iid)
return false;
}
-static bool assign_na(struct interface *iface, struct dhcp_assignment *a)
+static bool assign_na(struct interface *iface, struct dhcp_assignment *a, bool is_ta)
{
struct dhcp_assignment *c;
/* Preconfigured assignment by static lease */
if (a->assigned_host_id) {
list_for_each_entry(c, &iface->ia_assignments, head) {
- if (!(c->flags & OAF_DHCPV6_NA) || c->assigned_host_id > a->assigned_host_id ) {
+ if (!(c->flags & (OAF_DHCPV6_NA | OAF_DHCPV6_TA)) || c->assigned_host_id > a->assigned_host_id ) {
list_add_tail(&a->head, &c->head);
return true;
} else if (c->assigned_host_id == a->assigned_host_id)
@@ -729,12 +730,25 @@ static bool assign_na(struct interface *iface, struct dhcp_assignment *a)
uint64_t try = 0;
uint32_t counter_be = htonl(counter);
- prng_starts(prng);
- prng_update(prng, a->clid_data, a->clid_len);
- prng_update(prng, (const uint8_t*)&a->iaid, sizeof(a->iaid));
- prng_update(prng, (const uint8_t*)&counter_be, sizeof(counter_be));
- prng_update(prng, (const uint8_t*)&config.secret_key, sizeof(config.secret_key));
- prng_finish(prng, (uint8_t*)&try);
+ if (is_ta) {
+ prng_starts(prng);
+ prng_update(prng, (const uint8_t*)&ta_seed, sizeof(ta_seed));
+ prng_update(prng, a->clid_data, a->clid_len);
+ prng_update(prng, (const uint8_t*)&is_ta, sizeof(is_ta));
+ prng_update(prng, (const uint8_t*)&a->iaid, sizeof(a->iaid));
+ prng_finish(prng, (uint8_t*)&try, (uint8_t*)&ta_seed);
+ /* TODO set leftmost 64-bits of the MD5 digest
+ and set bit 6 (the leftmost bit is numbered
+ 0) to zero. */
+ } else {
+ prng_starts(prng);
+ prng_update(prng, a->clid_data, a->clid_len);
+ prng_update(prng, (const uint8_t*)&a->iaid, sizeof(a->iaid));
+ prng_update(prng, (const uint8_t*)&is_ta, sizeof(is_ta));
+ prng_update(prng, (const uint8_t*)&counter_be, sizeof(counter_be));
+ prng_update(prng, (const uint8_t*)&config.secret_key, sizeof(config.secret_key));
+ prng_finish(prng, (uint8_t*)&try, NULL);
+ }
try &= mask;
@@ -750,7 +764,7 @@ static bool assign_na(struct interface *iface, struct dhcp_assignment *a)
continue;
list_for_each_entry(c, &iface->ia_assignments, head) {
- if (!(c->flags & OAF_DHCPV6_NA) || c->assigned_host_id > try) {
+ if (!(c->flags & (OAF_DHCPV6_NA | OAF_DHCPV6_TA)) || c->assigned_host_id > try) {
a->assigned_host_id = try;
list_add_tail(&a->head, &c->head);
return true;
@@ -867,16 +881,24 @@ static size_t build_ia(uint8_t *buf, size_t buflen, uint16_t status,
const struct dhcpv6_ia_hdr *ia, struct dhcp_assignment *a,
struct interface *iface, bool request)
{
+ bool is_ta = ia->type == htons(DHCPV6_OPT_IA_TA);
struct dhcpv6_ia_hdr o_ia = {
.type = ia->type,
.len = 0,
.iaid = ia->iaid,
- .t1 = 0,
- .t2 = 0,
+ .t1 = 0, /* not in IA_TA */
+ .t2 = 0, /* not in IA_TA */
};
- size_t ia_len = sizeof(o_ia);
+ size_t ia_hdr_len;
time_t now = odhcpd_time();
+ if (is_ta)
+ ia_hdr_len = sizeof(o_ia) - sizeof(o_ia.t1) - sizeof(o_ia.t2);
+ else
+ ia_hdr_len = sizeof(o_ia);
+
+ size_t ia_len = ia_hdr_len;
+
if (buflen < ia_len)
return 0;
@@ -895,7 +917,7 @@ static size_t build_ia(uint8_t *buf, size_t buflen, uint16_t status,
ia_len += sizeof(o_status);
o_ia.len = htons(ia_len - 4);
- memcpy(buf, &o_ia, sizeof(o_ia));
+ memcpy(buf, &o_ia, ia_hdr_len);
return ia_len;
}
@@ -973,7 +995,7 @@ static size_t build_ia(uint8_t *buf, size_t buflen, uint16_t status,
ia_len += sizeof(o_ia_p);
}
- if (a->flags & OAF_DHCPV6_NA) {
+ if (a->flags & (OAF_DHCPV6_NA | OAF_DHCPV6_TA)) {
struct dhcpv6_ia_addr o_ia_a = {
.type = htons(DHCPV6_OPT_IA_ADDR),
.len = htons(sizeof(o_ia_a) - 4),
@@ -982,8 +1004,8 @@ static size_t build_ia(uint8_t *buf, size_t buflen, uint16_t status,
.valid = htonl(prefix_valid)
};
- o_ia_a.addr.s6_addr32[2] = htonl(a->assigned_host_id >> 32);
- o_ia_a.addr.s6_addr32[3] = htonl(a->assigned_host_id & UINT32_MAX);
+ o_ia_a.addr.s6_addr32[2] |= htonl(a->assigned_host_id >> 32);
+ o_ia_a.addr.s6_addr32[3] |= htonl(a->assigned_host_id & UINT32_MAX);
if (!ADDR_ENTRY_VALID_IA_ADDR(iface, i, m, addrs))
continue;
@@ -1061,8 +1083,8 @@ static size_t build_ia(uint8_t *buf, size_t buflen, uint16_t status,
ia_p->prefix == ((a->managed) ? addrs[i].prefix : a->length))
found = true;
} else {
- addr.s6_addr32[2] = htonl(a->assigned_host_id >> 32);
- addr.s6_addr32[3] = htonl(a->assigned_host_id & UINT32_MAX);
+ addr.s6_addr32[2] |= htonl(a->assigned_host_id >> 32);
+ addr.s6_addr32[3] |= htonl(a->assigned_host_id & UINT32_MAX);
if (!memcmp(&ia_a->addr, &addr, sizeof(addr)))
found = true;
@@ -1106,7 +1128,7 @@ static size_t build_ia(uint8_t *buf, size_t buflen, uint16_t status,
}
o_ia.len = htons(ia_len - 4);
- memcpy(buf, &o_ia, sizeof(o_ia));
+ memcpy(buf, &o_ia, ia_hdr_len);
return ia_len;
}
@@ -1128,10 +1150,11 @@ static void dhcpv6_log_ia_addr(struct in6_addr *addr, int prefix, _unused uint32
}
static void dhcpv6_log(uint8_t msgtype, struct interface *iface, time_t now,
- const char *duidbuf, bool is_pd, struct dhcp_assignment *a, int code)
+ const char *duidbuf, uint16_t otype, struct dhcp_assignment *a, int code)
{
const char *type = "UNKNOWN";
const char *status = "UNKNOWN";
+ const char *lease_type = "UNKNOWN";
switch (msgtype) {
case DHCPV6_MSG_SOLICIT:
@@ -1175,6 +1198,18 @@ static void dhcpv6_log(uint8_t msgtype, struct interface *iface, time_t now,
break;
}
+ switch (otype) {
+ case DHCPV6_OPT_IA_NA:
+ lease_type = "IA_NA";
+ break;
+ case DHCPV6_OPT_IA_TA:
+ lease_type = "IA_TA";
+ break;
+ case DHCPV6_OPT_IA_PD:
+ lease_type = "IA_PD";
+ break;
+ }
+
char leasebuf[256] = "";
if (a) {
@@ -1185,7 +1220,7 @@ static void dhcpv6_log(uint8_t msgtype, struct interface *iface, time_t now,
dhcpv6_ia_enum_addrs(iface, a, now, dhcpv6_log_ia_addr, &ctxt);
}
- syslog(LOG_INFO, "DHCPV6 %s %s from %s on %s: %s %s", type, (is_pd) ? "IA_PD" : "IA_NA",
+ syslog(LOG_INFO, "DHCPV6 %s %s from %s on %s: %s %s", type, lease_type,
duidbuf, iface->name, status, leasebuf);
}
@@ -1282,8 +1317,9 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
dhcpv6_for_each_option(start, end, otype, olen, odata) {
bool is_pd = (otype == DHCPV6_OPT_IA_PD);
bool is_na = (otype == DHCPV6_OPT_IA_NA);
+ bool is_ta = (otype == DHCPV6_OPT_IA_TA);
bool ia_addr_present = false;
- if (!is_pd && !is_na)
+ if (!is_pd && !is_na && !is_ta)
continue;
struct dhcpv6_ia_hdr *ia = (struct dhcpv6_ia_hdr*)&odata[-4];
@@ -1310,7 +1346,7 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
if (reqlen > 64)
reqlen = 64;
- } else if (is_na) {
+ } else if (is_na || is_ta) {
uint8_t *sdata;
uint16_t stype, slen;
dhcpv6_for_each_option(&ia[1], odata + olen, stype, slen, sdata) {
@@ -1326,7 +1362,9 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
list_for_each_entry(c, &iface->ia_assignments, head) {
if ((c->clid_len == clid_len && !memcmp(c->clid_data, clid_data, clid_len)) &&
c->iaid == ia->iaid && (INFINITE_VALID(c->valid_until) || now < c->valid_until) &&
- ((is_pd && (c->flags & OAF_DHCPV6_PD)) || (is_na && (c->flags & OAF_DHCPV6_NA)))) {
+ ((is_pd && (c->flags & OAF_DHCPV6_PD)) ||
+ (is_na && (c->flags & OAF_DHCPV6_NA)) ||
+ (is_ta && (c->flags & OAF_DHCPV6_TA)))) {
a = c;
/* Reset state */
@@ -1365,7 +1403,7 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
a->iaid = ia->iaid;
a->length = reqlen;
a->peer = *addr;
- if (is_na)
+ if (is_na || is_ta)
a->assigned_host_id = l ? l->hostid : 0;
else
a->assigned_subnet_id = reqhint;
@@ -1373,7 +1411,7 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
a->preferred_until = now;
a->dhcp_free_cb = dhcpv6_ia_free_assignment;
a->iface = iface;
- a->flags = (is_pd ? OAF_DHCPV6_PD : OAF_DHCPV6_NA);
+ a->flags = (is_pd ? OAF_DHCPV6_PD : is_ta ? OAF_DHCPV6_TA : OAF_DHCPV6_NA);
if (first)
memcpy(a->key, first->key, sizeof(a->key));
@@ -1383,8 +1421,8 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
if (is_pd && iface->dhcpv6_pd)
while (!(assigned = assign_pd(iface, a)) &&
!a->managed_size && ++a->length <= 64);
- else if (is_na && iface->dhcpv6_na)
- assigned = assign_na(iface, a);
+ else if ((is_na && iface->dhcpv6_na) || (is_ta && iface->dhcpv6_ta))
+ assigned = assign_na(iface, a, is_ta);
if (l && assigned) {
a->flags |= OAF_STATIC;
@@ -1494,7 +1532,7 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
}
} else if (hdr->msg_type == DHCPV6_MSG_RELEASE) {
a->valid_until = now - 1;
- } else if ((a->flags & OAF_DHCPV6_NA) && hdr->msg_type == DHCPV6_MSG_DECLINE) {
+ } else if ((a->flags & (OAF_DHCPV6_NA | OAF_DHCPV6_TA)) && hdr->msg_type == DHCPV6_MSG_DECLINE) {
a->flags &= ~OAF_BOUND;
if (!(a->flags & OAF_STATIC) || a->lease->hostid != a->assigned_host_id) {
@@ -1518,7 +1556,7 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac
buf += ia_response_len;
buflen -= ia_response_len;
response_len += ia_response_len;
- dhcpv6_log(hdr->msg_type, iface, now, duidbuf, is_pd, a, status);
+ dhcpv6_log(hdr->msg_type, iface, now, duidbuf, otype, a, status);
}
switch (hdr->msg_type) {
diff --git a/src/dhcpv6.h b/src/dhcpv6.h
index c96dc0d..a12c896 100644
--- a/src/dhcpv6.h
+++ b/src/dhcpv6.h
@@ -41,6 +41,7 @@
#define DHCPV6_OPT_CLIENTID 1
#define DHCPV6_OPT_SERVERID 2
#define DHCPV6_OPT_IA_NA 3
+#define DHCPV6_OPT_IA_TA 4
#define DHCPV6_OPT_IA_ADDR 5
#define DHCPV6_OPT_ORO 6
#define DHCPV6_OPT_STATUS 13
diff --git a/src/odhcpd.h b/src/odhcpd.h
index fff28d5..d1463e1 100644
--- a/src/odhcpd.h
+++ b/src/odhcpd.h
@@ -141,6 +141,7 @@ enum odhcpd_assignment_flags {
OAF_DHCPV4 = (1 << 4),
OAF_DHCPV6_NA = (1 << 5),
OAF_DHCPV6_PD = (1 << 6),
+ OAF_DHCPV6_TA = (1 << 7),
};
struct config {
@@ -205,7 +206,7 @@ struct dhcp_assignment {
uint32_t assigned_subnet_id;
};
uint32_t iaid;
- uint8_t length; // length == 128 -> IA_NA, length <= 64 -> IA_PD
+ uint8_t length; // length == 128 -> IA_NA or IA_TA, length <= 64 -> IA_PD
struct odhcpd_ipaddr *managed;
ssize_t managed_size;
@@ -327,6 +328,7 @@ struct interface {
bool dhcpv6_assignall;
bool dhcpv6_pd;
bool dhcpv6_na;
+ bool dhcpv6_ta;
uint32_t dhcpv6_hostid_len;
char *upstream;
diff --git a/src/ubus.c b/src/ubus.c
index 45b29a4..25c2b1b 100644
--- a/src/ubus.c
+++ b/src/ubus.c
@@ -158,7 +158,7 @@ static int handle_dhcpv6_leases(_unused struct ubus_context *ctx, _unused struct
blobmsg_add_string(&b, NULL, "static");
blobmsg_close_array(&b, m);
- m = blobmsg_open_array(&b, a->flags & OAF_DHCPV6_NA ? "ipv6-addr": "ipv6-prefix");
+ m = blobmsg_open_array(&b, a->flags & (OAF_DHCPV6_NA | OAF_DHCPV6_TA) ? "ipv6-addr": "ipv6-prefix");
dhcpv6_ia_enum_addrs(iface, a, now, dhcpv6_blobmsg_ia_addr, NULL);
blobmsg_close_table(&b, m);