/**
* Copyright (C) 2012-2014 Steven Barth <steven@midlink.org>
- * Copyright (C) 2017 Hans Dedecker <dedeckeh@gmail.com>
+ * Copyright (C) 2017-2018 Hans Dedecker <dedeckeh@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License v2 as published by
sock = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
if (sock < 0)
- return -1;
+ goto failure;
// Detect interface
struct ifreq ifr;
+ memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0)
- return -1;
+ goto failure;
ifindex = ifr.ifr_ifindex;
// Configure IPv6-options
int val = 1;
- setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
- setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
- setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val));
- setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname));
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) < 0)
+ goto failure;
+
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0)
+ goto failure;
+
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val)) < 0)
+ goto failure;
+
+ if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname)) < 0)
+ goto failure;
struct sockaddr_in6 client_addr = { .sin6_family = AF_INET6,
.sin6_port = htons(DHCPV6_CLIENT_PORT), .sin6_flowinfo = 0 };
if (bind(sock, (struct sockaddr*)&client_addr, sizeof(client_addr)) < 0)
- return -1;
+ goto failure;
return 0;
+
+failure:
+ if (sock >= 0)
+ close(sock);
+
+ return -1;
}
enum {
IOV_ORO_REFRESH,
IOV_CL_ID,
IOV_SRV_ID,
- IOV_VENDOR_CLASS_HDR,
- IOV_VENDOR_CLASS,
- IOV_USER_CLASS_HDR,
- IOV_USER_CLASS,
+ IOV_OPTS,
IOV_RECONF_ACCEPT,
IOV_FQDN,
IOV_HDR_IA_NA,
// Request Information Refresh
uint16_t oro_refresh = htons(DHCPV6_OPT_INFO_REFRESH);
- // Build vendor-class option
- size_t vendor_class_len, user_class_len;
- struct dhcpv6_vendorclass *vendor_class = odhcp6c_get_state(STATE_VENDORCLASS, &vendor_class_len);
- void *user_class = odhcp6c_get_state(STATE_USERCLASS, &user_class_len);
-
- struct {
- uint16_t type;
- uint16_t length;
- } vendor_class_hdr = {htons(DHCPV6_OPT_VENDOR_CLASS), htons(vendor_class_len)};
-
- struct {
- uint16_t type;
- uint16_t length;
- } user_class_hdr = {htons(DHCPV6_OPT_USER_CLASS), htons(user_class_len)};
+ // Option list
+ size_t opts_len;
+ void *opts = odhcp6c_get_state(STATE_OPTS, &opts_len);
// Prepare Header
size_t oro_len;
[IOV_ORO_REFRESH] = {&oro_refresh, 0},
[IOV_CL_ID] = {cl_id, cl_id_len},
[IOV_SRV_ID] = {srv_id, srv_id_len},
- [IOV_VENDOR_CLASS_HDR] = {&vendor_class_hdr, vendor_class_len ? sizeof(vendor_class_hdr) : 0},
- [IOV_VENDOR_CLASS] = {vendor_class, vendor_class_len},
- [IOV_USER_CLASS_HDR] = {&user_class_hdr, user_class_len ? sizeof(user_class_hdr) : 0},
- [IOV_USER_CLASS] = {user_class, user_class_len},
+ [IOV_OPTS] = { opts, opts_len },
[IOV_RECONF_ACCEPT] = {&reconf_accept, sizeof(reconf_accept)},
[IOV_FQDN] = {&fqdn, fqdn_len},
[IOV_HDR_IA_NA] = {&hdr_ia_na, sizeof(hdr_ia_na)},
// Set timeout for receiving
uint64_t t = round_end - round_start;
struct timeval tv = {t / 1000, (t % 1000) * 1000};
- setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
- &tv, sizeof(tv));
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
+ &tv, sizeof(tv)) < 0)
+ syslog(LOG_ERR, "setsockopt SO_RCVTIMEO failed (%s)",
+ strerror(errno));
// Receive cycle
len = recvmsg(sock, &msg, 0);
return false;
if (rep->msg_type == DHCPV6_MSG_RECONF) {
- if ((rcmsg != DHCPV6_MSG_RENEW && rcmsg != DHCPV6_MSG_INFO_REQ) ||
+ if ((rcmsg != DHCPV6_MSG_RENEW && rcmsg != DHCPV6_MSG_REBIND && rcmsg != DHCPV6_MSG_INFO_REQ) ||
(rcmsg == DHCPV6_MSG_INFO_REQ && ia_present) ||
!rcauth_ok || IN6_IS_ADDR_MULTICAST(daddr))
return false;
int msg = -1;
dhcpv6_for_each_option(opt, end, otype, olen, odata) {
- if (otype == DHCPV6_OPT_RECONF_MESSAGE && olen == 1 && (
- odata[0] == DHCPV6_MSG_RENEW ||
- odata[0] == DHCPV6_MSG_INFO_REQ))
- msg = odata[0];
+ if (otype == DHCPV6_OPT_RECONF_MESSAGE && olen == 1) {
+ switch (odata[0]) {
+ case DHCPV6_MSG_REBIND:
+ if (t2 != UINT32_MAX)
+ t2 = 0;
+ // Fall through
+ case DHCPV6_MSG_RENEW:
+ if (t1 != UINT32_MAX)
+ t1 = 0;
+ // Fall through
+ case DHCPV6_MSG_INFO_REQ:
+ msg = odata[0];
+ break;
+
+ default:
+ break;
+ }
+ }
}
dhcpv6_handle_reply(orig, rc, NULL, NULL, NULL);
// Parse and find all matching IAs
dhcpv6_for_each_option(opt, end, otype, olen, odata) {
- bool passthru = true;
+ struct odhcp6c_opt *dopt = odhcp6c_find_opt(otype);
if ((otype == DHCPV6_OPT_IA_PD || otype == DHCPV6_OPT_IA_NA)
&& olen > -4 + sizeof(struct dhcpv6_ia_hdr)) {
continue;
dhcpv6_parse_ia(ia_hdr, odata + olen);
- passthru = false;
- } else if (otype == DHCPV6_OPT_UNICAST && olen == sizeof(server_addr)) {
+ } else if (otype == DHCPV6_OPT_UNICAST && olen == sizeof(server_addr))
server_addr = *(struct in6_addr *)odata;
- passthru = false;
- } else if (otype == DHCPV6_OPT_STATUS && olen >= 2) {
+ else if (otype == DHCPV6_OPT_STATUS && olen >= 2) {
uint8_t *mdata = (olen > 2) ? &odata[2] : NULL;
uint16_t mlen = (olen > 2) ? olen - 2 : 0;
uint16_t code = ((int)odata[0]) << 8 | ((int)odata[1]);
dhcpv6_handle_status_code(orig, code, mdata, mlen, &ret);
- passthru = false;
} else if (otype == DHCPV6_OPT_DNS_SERVERS) {
if (olen % 16 == 0)
odhcp6c_add_state(STATE_DNS, odata, olen);
odhcp6c_add_state(STATE_SIP_FQDN, odata, olen);
else if (otype == DHCPV6_OPT_INFO_REFRESH && olen >= 4) {
refresh = ntohl_unaligned(odata);
- passthru = false;
} else if (otype == DHCPV6_OPT_AUTH) {
if (olen == -4 + sizeof(struct dhcpv6_auth_reconfigure)) {
struct dhcpv6_auth_reconfigure *r = (void*)&odata[-4];
r->reconf_type == 1)
memcpy(reconf_key, r->key, sizeof(r->key));
}
- passthru = false;
} else if (otype == DHCPV6_OPT_AFTR_NAME && olen > 3) {
size_t cur_len;
odhcp6c_get_state(STATE_AFTR_NAME, &cur_len);
if (cur_len == 0)
odhcp6c_add_state(STATE_AFTR_NAME, odata, olen);
- passthru = false;
} else if (otype == DHCPV6_OPT_SOL_MAX_RT && olen == 4) {
uint32_t sol_max_rt = ntohl_unaligned(odata);
if (sol_max_rt >= DHCPV6_SOL_MAX_RT_MIN &&
sol_max_rt <= DHCPV6_SOL_MAX_RT_MAX)
dhcpv6_retx[DHCPV6_MSG_SOLICIT].max_timeo = sol_max_rt;
- passthru = false;
} else if (otype == DHCPV6_OPT_INF_MAX_RT && olen == 4) {
uint32_t inf_max_rt = ntohl_unaligned(odata);
if (inf_max_rt >= DHCPV6_INF_MAX_RT_MIN &&
inf_max_rt <= DHCPV6_INF_MAX_RT_MAX)
dhcpv6_retx[DHCPV6_MSG_INFO_REQ].max_timeo = inf_max_rt;
- passthru = false;
#ifdef EXT_CER_ID
} else if (otype == DHCPV6_OPT_CER_ID && olen == -4 +
sizeof(struct dhcpv6_cer_id)) {
struct in6_addr any = IN6ADDR_ANY_INIT;
if (memcmp(&cer_id->addr, &any, sizeof(any)))
odhcp6c_add_state(STATE_CER, &cer_id->addr, sizeof(any));
- passthru = false;
#endif
} else if (otype == DHCPV6_OPT_S46_CONT_MAPT) {
odhcp6c_add_state(STATE_S46_MAPT, odata, olen);
- passthru = false;
} else if (otype == DHCPV6_OPT_S46_CONT_MAPE) {
size_t mape_len;
odhcp6c_get_state(STATE_S46_MAPE, &mape_len);
if (mape_len == 0)
odhcp6c_add_state(STATE_S46_MAPE, odata, olen);
- passthru = false;
} else if (otype == DHCPV6_OPT_S46_CONT_LW) {
odhcp6c_add_state(STATE_S46_LW, odata, olen);
- passthru = false;
- } else if (otype == DHCPV6_OPT_CLIENTID ||
- otype == DHCPV6_OPT_SERVERID ||
- otype == DHCPV6_OPT_IA_TA ||
- otype == DHCPV6_OPT_PREF ||
- otype == DHCPV6_OPT_UNICAST ||
- otype == DHCPV6_OPT_FQDN ||
- otype == DHCPV6_OPT_RECONF_ACCEPT)
- passthru = false;
- else
+ } else
odhcp6c_add_state(STATE_CUSTOM_OPTS, &odata[-4], olen + 4);
- if (passthru)
+ if (!dopt || !(dopt->flags & OPT_NO_PASSTHRU))
odhcp6c_add_state(STATE_PASSTHRU, &odata[-4], olen + 4);
}
}
}
if (ok) {
- odhcp6c_update_entry(STATE_IA_PD, &entry, 0, false);
+ odhcp6c_update_entry(STATE_IA_PD, &entry, 0, 0);
parsed_ia++;
}
entry.length = 128;
entry.target = addr->addr;
- odhcp6c_update_entry(STATE_IA_NA, &entry, 0, false);
+ odhcp6c_update_entry(STATE_IA_NA, &entry, 0, 0);
parsed_ia++;
}
}