#include <unistd.h>
#include <syslog.h>
#include <stdbool.h>
+#include <ctype.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
for (size_t i = 0; i < n_prefixes; i++) {
struct dhcpv6_ia_hdr hdr_ia_pd = {
htons(DHCPV6_OPT_IA_PD),
- htons(sizeof(hdr_ia_pd) - 4 + sizeof(struct dhcpv6_ia_prefix)),
+ htons(sizeof(hdr_ia_pd) - 4 +
+ sizeof(struct dhcpv6_ia_prefix) * !!request_prefixes[i].length),
request_prefixes[i].iaid, 0, 0
};
struct dhcpv6_ia_prefix pref = {
.type = htons(DHCPV6_OPT_IA_PREFIX),
- .len = htons(25), .prefix = request_prefixes[i].length
+ .len = htons(sizeof(pref) - 4),
+ .prefix = request_prefixes[i].length
};
memcpy(ia_pd + ia_pd_len, &hdr_ia_pd, sizeof(hdr_ia_pd));
ia_pd_len += sizeof(hdr_ia_pd);
- memcpy(ia_pd + ia_pd_len, &pref, sizeof(pref));
- ia_pd_len += sizeof(pref);
+ if (request_prefixes[i].length) {
+ memcpy(ia_pd + ia_pd_len, &pref, sizeof(pref));
+ ia_pd_len += sizeof(pref);
+ }
}
} else {
struct odhcp6c_entry *e = odhcp6c_get_state(STATE_IA_PD, &ia_pd_entries);
if (otype == DHCPV6_OPT_SERVERID && olen <= 130) {
memcpy(cand.duid, odata, olen);
cand.duid_len = olen;
- } else if (otype == DHCPV6_OPT_STATUS && olen >= 2) {
- int error = ((int)odata[0] << 8 | (int)odata[1]);
-
- switch (error) {
- case DHCPV6_NoPrefixAvail:
- // Status code on global level
- cand.preference -= 2000;
- break;
-
- default :
- break;
- }
} else if (otype == DHCPV6_OPT_PREF && olen >= 1 &&
cand.preference >= 0) {
cand.preference = pref = odata[0];
if (inf_max_rt >= DHCPV6_INF_MAX_RT_MIN &&
inf_max_rt <= DHCPV6_INF_MAX_RT_MAX)
cand.inf_max_rt = inf_max_rt;
- } else if (otype == DHCPV6_OPT_IA_PD && request_prefix) {
+ } else if (otype == DHCPV6_OPT_IA_PD && request_prefix &&
+ olen >= -4 + sizeof(struct dhcpv6_ia_hdr)) {
struct dhcpv6_ia_hdr *h = (struct dhcpv6_ia_hdr*)&odata[-4];
uint8_t *oend = odata + olen, *d;
dhcpv6_for_each_option(&h[1], oend, otype, olen, d) {
have_pd = p->prefix;
}
}
- } else if (otype == DHCPV6_OPT_IA_NA) {
+ } else if (otype == DHCPV6_OPT_IA_NA &&
+ olen >= -4 + sizeof(struct dhcpv6_ia_hdr)) {
struct dhcpv6_ia_hdr *h = (struct dhcpv6_ia_hdr*)&odata[-4];
uint8_t *oend = odata + olen, *d;
dhcpv6_for_each_option(&h[1], oend, otype, olen, d)
odhcp6c_clear_state(STATE_S46_MAPE);
odhcp6c_clear_state(STATE_S46_LW);
odhcp6c_clear_state(STATE_PASSTHRU);
+ odhcp6c_clear_state(STATE_CUSTOM_OPTS);
// Parse and find all matching IAs
dhcpv6_for_each_option(opt, end, otype, olen, odata) {
if (code != DHCPV6_Success)
continue;
- dhcpv6_parse_ia(ia_hdr, odata + olen + sizeof(*ia_hdr));
+ dhcpv6_parse_ia(ia_hdr, odata + olen);
passthru = false;
} else if (otype == DHCPV6_OPT_STATUS && olen >= 2) {
uint8_t *mdata = (olen > 2) ? &odata[2] : NULL;
if (elen > 64)
elen = 64;
- if (elen <= 32 || elen <= entry.length) {
+ if (entry.length < 32 || elen <= entry.length) {
ok = false;
continue;
}
static void dhcpv6_log_status_code(const uint16_t code, const char *scope,
- const void *status_msg, const int len)
+ const void *status_msg, int len)
{
- uint8_t buf[len + 3];
+ const char *src = status_msg;
+ char buf[len + 3];
+ char *dst = buf;
- memset(buf, 0, sizeof(buf));
if (len) {
- buf[0] = '(';
- memcpy(&buf[1], status_msg, len);
- buf[len + 1] = ')';
+ *dst++ = '(';
+ while (len--) {
+ *dst = isprint((unsigned char)*src) ? *src : '?';
+ src++;
+ dst++;
+ }
+ *dst++ = ')';
}
+ *dst = 0;
syslog(LOG_WARNING, "Server returned %s status %i %s",
scope, code, buf);
}
}
+// Note this always takes ownership of cand->ia_na and cand->ia_pd
static void dhcpv6_add_server_cand(const struct dhcpv6_server_cand *cand)
{
size_t cand_len, i;
break;
}
- odhcp6c_insert_state(STATE_SERVER_CAND, i * sizeof(*c), cand, sizeof(*cand));
+ if (odhcp6c_insert_state(STATE_SERVER_CAND, i * sizeof(*c), cand, sizeof(*cand))) {
+ free(cand->ia_na);
+ free(cand->ia_pd);
+ }
}
static void dhcpv6_clear_all_server_cand(void)