summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorSteven Barth <steven@midlink.org>2014-04-01 11:19:31 +0200
committerSteven Barth <steven@midlink.org>2014-04-01 11:19:31 +0200
commitd12c7b8c39bc0f727bde5aa34595e9a581891dca (patch)
treece1cbba4f2e879720f30679a66f40f26288e62e8
parent2b4200f2510874a3ee9c09f594d5ab6340ffb336 (diff)
Complete managed PD/CER-ID support
-rw-r--r--CMakeLists.txt4
-rw-r--r--src/config.c12
-rw-r--r--src/dhcpv6-ia.c59
-rw-r--r--src/dhcpv6.c37
-rw-r--r--src/dhcpv6.h11
-rw-r--r--src/odhcpd.c2
-rw-r--r--src/odhcpd.h1
7 files changed, 93 insertions, 33 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2eca68e..dd750b3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -14,6 +14,10 @@ if(${EXT_PREFIX_CLASS})
add_definitions(-DEXT_PREFIX_CLASS=${EXT_PREFIX_CLASS})
endif(${EXT_PREFIX_CLASS})
+if (${EXT_CER_ID})
+ add_definitions(-DEXT_CER_ID=${EXT_CER_ID})
+endif(${EXT_CER_ID})
+
if(${UBUS})
add_definitions(-DWITH_UBUS)
set(EXT_SRC ${EXT_SRC} src/ubus.c)
diff --git a/src/config.c b/src/config.c
index 7f51585..a392af7 100644
--- a/src/config.c
+++ b/src/config.c
@@ -36,6 +36,8 @@ enum {
IFACE_ATTR_RA_MANAGEMENT,
IFACE_ATTR_RA_OFFLINK,
IFACE_ATTR_RA_PREFERENCE,
+ IFACE_ATTR_PD_MANAGER,
+ IFACE_ATTR_PD_CER,
IFACE_ATTR_NDPROXY_ROUTING,
IFACE_ATTR_NDPROXY_SLAVE,
IFACE_ATTR_NDPROXY_STATIC,
@@ -59,6 +61,8 @@ static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = {
[IFACE_ATTR_NDP] = { .name = "ndp", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_DNS] = { .name = "dns", .type = BLOBMSG_TYPE_ARRAY },
[IFACE_ATTR_DOMAIN] = { .name = "domain", .type = BLOBMSG_TYPE_ARRAY },
+ [IFACE_ATTR_PD_MANAGER] = { .name = "pd_manager", .type = BLOBMSG_TYPE_STRING },
+ [IFACE_ATTR_PD_CER] = { .name = "pd_cer", .type = BLOBMSG_TYPE_STRING },
[IFACE_ATTR_RA_DEFAULT] = { .name = "ra_default", .type = BLOBMSG_TYPE_INT32 },
[IFACE_ATTR_RA_MANAGEMENT] = { .name = "ra_management", .type = BLOBMSG_TYPE_INT32 },
[IFACE_ATTR_RA_OFFLINK] = { .name = "ra_offlink", .type = BLOBMSG_TYPE_BOOL },
@@ -473,6 +477,14 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr
goto err;
}
+ if ((c = tb[IFACE_ATTR_PD_MANAGER]))
+ strncpy(iface->dhcpv6_pd_manager, blobmsg_get_string(c),
+ sizeof(iface->dhcpv6_pd_manager) - 1);
+
+ if ((c = tb[IFACE_ATTR_PD_CER]) &&
+ inet_pton(AF_INET6, blobmsg_get_string(c), &iface->dhcpv6_pd_cer) < 1)
+ goto err;
+
if ((c = tb[IFACE_ATTR_NDPROXY_ROUTING]))
iface->learn_routes = blobmsg_get_bool(c);
else if (overwrite)
diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c
index 593b5bd..a7fbd12 100644
--- a/src/dhcpv6-ia.c
+++ b/src/dhcpv6-ia.c
@@ -50,10 +50,13 @@ int dhcpv6_ia_init(void)
void free_dhcpv6_assignment(struct dhcpv6_assignment *c)
{
if (c->managed_sock.fd.registered) {
- close(c->managed_sock.fd.fd);
ustream_free(&c->managed_sock.stream);
+ close(c->managed_sock.fd.fd);
}
+ if (c->head.next)
+ list_del(&c->head);
+
free(c->managed);
free(c->hostname);
free(c->classes);
@@ -67,7 +70,6 @@ int setup_dhcpv6_ia_interface(struct interface *iface, bool enable)
struct dhcpv6_assignment *c;
while (!list_empty(&iface->ia_assignments)) {
c = list_first_entry(&iface->ia_assignments, struct dhcpv6_assignment, head);
- list_del(&c->head);
free_dhcpv6_assignment(c);
}
}
@@ -401,8 +403,22 @@ static void managed_handle_pd_data(struct ustream *s, _unused int bytes_new)
if (sscanf(x, "%u", &n->valid) < 1)
continue;
- n->preferred += now;
- n->valid += now;
+ if (n->preferred > n->valid)
+ continue;
+
+ if (UINT32_MAX - now < n->preferred)
+ n->preferred = UINT32_MAX;
+ else
+ n->preferred += now;
+
+ if (UINT32_MAX - now < n->valid)
+ n->valid = UINT32_MAX;
+ else
+ n->valid += now;
+
+ n->has_class = false;
+ n->class = 0;
+ n->dprefix = 0;
++c->managed_size;
}
@@ -410,10 +426,8 @@ static void managed_handle_pd_data(struct ustream *s, _unused int bytes_new)
ustream_consume(s, end - data);
}
- if (first && c->managed_size == 0) {
- list_del(&c->head);
+ if (first && c->managed_size == 0)
free_dhcpv6_assignment(c);
- }
}
@@ -436,10 +450,14 @@ static bool assign_pd(struct interface *iface, struct dhcpv6_assignment *assign)
if (iface->dhcpv6_pd_manager[0]) {
int fd = usock(USOCK_UNIX | USOCK_TCP, iface->dhcpv6_pd_manager, NULL);
if (fd >= 0) {
+ char iaidbuf[298];
+ odhcpd_hexlify(iaidbuf, assign->clid_data, assign->clid_len);
+
assign->managed_sock.stream.notify_read = managed_handle_pd_data;
assign->managed_sock.stream.notify_state = managed_handle_pd_done;
ustream_fd_init(&assign->managed_sock, fd);
- ustream_printf(&assign->managed_sock.stream, "::/%d,0,0\n\n", assign->length);
+ ustream_printf(&assign->managed_sock.stream, "%s,%x\n::/%d,0,0\n\n",
+ iaidbuf, assign->iaid, assign->length);
ustream_write_pending(&assign->managed_sock.stream);
assign->managed_size = -1;
list_add(&assign->head, &iface->ia_assignments);
@@ -693,6 +711,7 @@ static size_t append_reply(uint8_t *buf, size_t buflen, uint16_t status,
struct odhcpd_ipaddr *addrs = (a->managed) ? a->managed : iface->ia_addr;
size_t addrlen = (a->managed) ? (size_t)a->managed_size : iface->ia_addr_len;
+
for (size_t i = 0; i < addrlen; ++i) {
bool match = true;
if (addrs[i].has_class) {
@@ -737,7 +756,7 @@ static size_t append_reply(uint8_t *buf, size_t buflen, uint16_t status,
.len = htons(sizeof(p) - 4),
.preferred = htonl(prefix_pref),
.valid = htonl(prefix_valid),
- .prefix = (a->managed_size) ? a->managed_size : a->length,
+ .prefix = (a->managed_size) ? addrs[i].prefix : a->length,
.addr = addrs[i].addr
};
p.addr.s6_addr32[1] |= htonl(a->assigned);
@@ -751,7 +770,8 @@ static size_t append_reply(uint8_t *buf, size_t buflen, uint16_t status,
}
#endif
- if (datalen + entrlen + 4 > buflen || a->assigned == 0)
+ if (datalen + entrlen + 4 > buflen ||
+ (a->assigned == 0 && a->managed_size == 0))
continue;
memcpy(buf + datalen, &p, sizeof(p));
@@ -886,7 +906,7 @@ static size_t append_reply(uint8_t *buf, size_t buflen, uint16_t status,
}
-size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface,
+ssize_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface,
const struct sockaddr_in6 *addr, const void *data, const uint8_t *end)
{
time_t now = odhcpd_time();
@@ -935,7 +955,6 @@ size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface,
update(iface);
bool update_state = false;
- bool managed_pd_out = false;
struct dhcpv6_assignment *first = NULL;
dhcpv6_for_each_option(start, end, otype, olen, odata) {
@@ -1040,9 +1059,7 @@ size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface,
// Generic message handling
uint16_t status = DHCPV6_STATUS_OK;
if (a && a->managed_size < 0) {
- managed_pd_out = true;
- status = DHCPV6_STATUS_NOTONLINK;
- ia_response_len = append_reply(buf, buflen, status, ia, NULL, iface, true);
+ return -1;
} else if (hdr->msg_type == DHCPV6_MSG_SOLICIT || hdr->msg_type == DHCPV6_MSG_REQUEST) {
bool assigned = !!a;
@@ -1070,12 +1087,12 @@ size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface,
if (is_pd)
while (!(assigned = assign_pd(iface, a)) &&
- ++a->length <= 64 && !a->managed_size);
+ !a->managed_size && ++a->length <= 64);
else
assigned = assign_na(iface, a);
if (a->managed_size && !assigned)
- managed_pd_out = true;
+ return -1;
}
}
@@ -1168,14 +1185,6 @@ size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface,
buf[4] = 0;
buf[5] = DHCPV6_STATUS_OK;
response_len += 6;
- } else if (managed_pd_out && response_len + 6 < buflen) {
- buf[0] = 0;
- buf[1] = DHCPV6_OPT_STATUS;
- buf[2] = 0;
- buf[3] = 2;
- buf[4] = 0;
- buf[5] = DHCPV6_STATUS_NOADDRSAVAIL;
- response_len += 6;
}
if (update_state)
diff --git a/src/dhcpv6.c b/src/dhcpv6.c
index 3fa2acf..62f30e2 100644
--- a/src/dhcpv6.c
+++ b/src/dhcpv6.c
@@ -101,7 +101,7 @@ int setup_dhcpv6_interface(struct interface *iface, bool enable)
static void handle_nested_message(uint8_t *data, size_t len,
- uint8_t **opts, uint8_t **end, struct iovec iov[6])
+ uint8_t **opts, uint8_t **end, struct iovec iov[9])
{
struct dhcpv6_relay_header *hdr = (struct dhcpv6_relay_header*)data;
if (iov[0].iov_base == NULL) {
@@ -124,8 +124,8 @@ static void handle_nested_message(uint8_t *data, size_t len,
uint8_t *odata;
dhcpv6_for_each_option(hdr->options, data + len, otype, olen, odata) {
if (otype == DHCPV6_OPT_RELAY_MSG) {
- iov[7].iov_base = odata + olen;
- iov[7].iov_len = (((uint8_t*)iov[0].iov_base) + iov[0].iov_len)
+ iov[8].iov_base = odata + olen;
+ iov[8].iov_len = (((uint8_t*)iov[0].iov_base) + iov[0].iov_len)
- (odata + olen);
handle_nested_message(odata, olen, opts, end, iov);
return;
@@ -244,6 +244,14 @@ static void handle_client_request(void *addr, void *data, size_t len,
} search = {htons(DHCPV6_OPT_DNS_DOMAIN), htons(search_len)};
+ struct dhcpv6_cer_id cerid = {
+#ifdef EXT_CER_ID
+ .type = htons(EXT_CER_ID),
+#endif
+ .len = htons(36),
+ .addr = iface->dhcpv6_pd_cer,
+ };
+
uint8_t pdbuf[512];
struct iovec iov[] = {{NULL, 0},
@@ -253,6 +261,7 @@ static void handle_client_request(void *addr, void *data, size_t len,
{&search, (search_len) ? sizeof(search) : 0},
{search_domain, search_len},
{pdbuf, 0},
+ {&cerid, 0},
{NULL, 0}};
uint8_t *opts = (uint8_t*)&hdr[1], *opts_end = (uint8_t*)data + len;
@@ -290,19 +299,35 @@ static void handle_client_request(void *addr, void *data, size_t len,
if (((((size_t)c[0]) << 8) | c[1]) == elen && !memcmp(&c[2], excluded_class, elen))
return; // Ignore from homenet
}
+ } else if (otype == DHCPV6_OPT_IA_PD) {
+#ifdef EXT_CER_ID
+ iov[7].iov_len = sizeof(cerid);
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&cerid.addr)) {
+ struct odhcpd_ipaddr addrs[32];
+ ssize_t len = odhcpd_get_interface_addresses(0, addrs,
+ sizeof(addrs) / sizeof(*addrs));
+
+ for (ssize_t i = 0; i < len; ++i)
+ if (IN6_IS_ADDR_UNSPECIFIED(&cerid.addr)
+ || memcmp(&addrs[i].addr, &cerid.addr, sizeof(cerid.addr)) < 0)
+ cerid.addr = addrs[i].addr;
+ }
+#endif
}
}
if (opts[-4] != DHCPV6_MSG_INFORMATION_REQUEST) {
- iov[6].iov_len = dhcpv6_handle_ia(pdbuf, sizeof(pdbuf), iface, addr, &opts[-4], opts_end);
- if (iov[6].iov_len == 0 && opts[-4] == DHCPV6_MSG_REBIND)
+ ssize_t ialen = dhcpv6_handle_ia(pdbuf, sizeof(pdbuf), iface, addr, &opts[-4], opts_end);
+ iov[6].iov_len = ialen;
+ if (ialen < 0 || (ialen == 0 && opts[-4] == DHCPV6_MSG_REBIND))
return;
}
if (iov[0].iov_len > 0) // Update length
update_nested_message(data, len, iov[1].iov_len + iov[2].iov_len +
iov[3].iov_len + iov[4].iov_len + iov[5].iov_len +
- iov[6].iov_len - (4 + opts_end - opts));
+ iov[6].iov_len + iov[7].iov_len - (4 + opts_end - opts));
odhcpd_send(iface->dhcpv6_event.uloop.fd, addr, iov, ARRAY_SIZE(iov), iface);
}
diff --git a/src/dhcpv6.h b/src/dhcpv6.h
index 936a350..dbde88a 100644
--- a/src/dhcpv6.h
+++ b/src/dhcpv6.h
@@ -164,6 +164,15 @@ struct dhcpv6_assignment {
uint8_t clid_data[];
};
+struct dhcpv6_cer_id {
+ uint16_t type;
+ uint16_t len;
+ uint16_t reserved;
+ uint16_t auth_type;
+ uint8_t auth[16];
+ struct in6_addr addr;
+};
+
#define dhcpv6_for_each_option(start, end, otype, olen, odata)\
@@ -173,7 +182,7 @@ struct dhcpv6_assignment {
_o += 4 + (_o[2] << 8 | _o[3]))
int dhcpv6_init_ia(struct interface *iface, int socket);
-size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface,
+ssize_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface,
const struct sockaddr_in6 *addr, const void *data, const uint8_t *end);
int dhcpv6_ia_init(void);
int setup_dhcpv6_ia_interface(struct interface *iface, bool enable);
diff --git a/src/odhcpd.c b/src/odhcpd.c
index 25a6047..bf9f16d 100644
--- a/src/odhcpd.c
+++ b/src/odhcpd.c
@@ -217,7 +217,7 @@ ssize_t odhcpd_get_interface_addresses(int ifindex,
struct ifaddrmsg *ifa = NLMSG_DATA(nhm);
if (ifa->ifa_scope != RT_SCOPE_UNIVERSE ||
- ifa->ifa_index != (unsigned)ifindex)
+ (ifindex && ifa->ifa_index != (unsigned)ifindex))
continue;
struct rtattr *rta = (struct rtattr*)&ifa[1];
diff --git a/src/odhcpd.h b/src/odhcpd.h
index d0373f5..cf34938 100644
--- a/src/odhcpd.h
+++ b/src/odhcpd.h
@@ -122,6 +122,7 @@ struct interface {
// Managed PD
char dhcpv6_pd_manager[128];
+ struct in6_addr dhcpv6_pd_cer;
// Services
enum odhcpd_mode ra;