diff options
-rw-r--r-- | src/config.c | 8 | ||||
-rw-r--r-- | src/dhcpv6-ia.c | 114 | ||||
-rw-r--r-- | src/dhcpv6.h | 1 | ||||
-rw-r--r-- | src/odhcpd.h | 4 | ||||
-rw-r--r-- | src/ubus.c | 2 |
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; @@ -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); |