struct blob_attr *tb[ODHCPD_ATTR_MAX], *c;
blob_buf_init(&b, 0);
- uci_to_blob(&b, s, &lease_attr_list);
- blobmsg_parse(lease_attrs, ODHCPD_ATTR_MAX, tb, blob_data(b.head), blob_len(b.head));
+ uci_to_blob(&b, s, &odhcpd_attr_list);
+ blobmsg_parse(odhcpd_attrs, ODHCPD_ATTR_MAX, tb, blob_data(b.head), blob_len(b.head));
if ((c = tb[ODHCPD_ATTR_LEGACY]))
config.legacy = blobmsg_get_bool(c);
hostlen = blobmsg_data_len(c);
struct lease *lease = calloc(1, sizeof(*lease) + hostlen);
+ if (!lease)
+ goto err;
if (hostlen > 1)
memcpy(lease->hostname, blobmsg_get_string(c), hostlen);
if ((c = tb[LEASE_ATTR_DUID])) {
size_t duidlen = (blobmsg_data_len(c) - 1) / 2;
lease->duid = malloc(duidlen);
+ if (!lease->duid)
+ goto err;
+
ssize_t len = odhcpd_unhexlify(lease->duid,
duidlen, blobmsg_get_string(c));
return 0;
err:
- free(lease->duid);
- free(lease);
+ if (lease) {
+ free(lease->duid);
+ free(lease);
+ }
return -1;
}
struct interface *iface = get_interface(name);
if (!iface) {
iface = calloc(1, sizeof(*iface));
+ if (!iface)
+ return -1;
+
strncpy(iface->name, name, sizeof(iface->name) - 1);
list_add(&iface->head, &interfaces);
}
iface->upstream = realloc(iface->upstream,
iface->upstream_len + blobmsg_data_len(cur));
+ if (!iface->upstream)
+ goto err;
+
memcpy(iface->upstream + iface->upstream_len, blobmsg_get_string(cur), blobmsg_data_len(cur));
iface->upstream_len += blobmsg_data_len(cur);
}
if (inet_pton(AF_INET, blobmsg_get_string(cur), &addr4) == 1) {
iface->dhcpv4_dns = realloc(iface->dhcpv4_dns,
(++iface->dhcpv4_dns_cnt) * sizeof(*iface->dhcpv4_dns));
+ if (!iface->dhcpv4_dns)
+ goto err;
+
iface->dhcpv4_dns[iface->dhcpv4_dns_cnt - 1] = addr4;
} else if (inet_pton(AF_INET6, blobmsg_get_string(cur), &addr6) == 1) {
iface->dns = realloc(iface->dns,
(++iface->dns_cnt) * sizeof(*iface->dns));
+ if (!iface->dns)
+ goto err;
+
iface->dns[iface->dns_cnt - 1] = addr6;
} else {
goto err;
goto err;
iface->search = realloc(iface->search, iface->search_len + len);
+ if (!iface->search)
+ goto err;
+
memcpy(&iface->search[iface->search_len], buf, len);
iface->search_len += len;
}
int len = blobmsg_data_len(cur);
iface->static_ndp = realloc(iface->static_ndp, iface->static_ndp_len + len);
+ if (!iface->static_ndp)
+ goto err;
+
memcpy(&iface->static_ndp[iface->static_ndp_len], blobmsg_get_string(cur), len);
iface->static_ndp_len += len;
}
}
struct interface *master = NULL, *i, *n;
+
+ if (!uci)
+ return;
+
list_for_each_entry(i, &interfaces, head)
clean_interface(i);
INIT_LIST_HEAD(&iface->dhcpv4_assignments);
int sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
+ if (sock < 0) {
+ syslog(LOG_ERR, "Failed to create DHCPv4 server socket: %s",
+ strerror(errno));
+ return -1;
+ }
// Basic IPv6 configuration
int val = 1;
// Construct entry
size_t hostlen = strlen(lease->hostname) + 1;
struct dhcpv4_assignment *a = calloc(1, sizeof(*a) + hostlen);
-
+ if (!a) {
+ syslog(LOG_ERR, "Calloc failed for static lease on interface %s",
+ iface->ifname);
+ return -1;
+ }
a->addr = ntohl(lease->ipaddr.s_addr);
memcpy(a->hwaddr, lease->mac.ether_addr_octet, sizeof(a->hwaddr));
memcpy(a->hostname, lease->hostname, hostlen);
if (!a && !iface->no_dynamic_dhcp) { // Create new binding
a = calloc(1, sizeof(*a) + hostlen);
+ if (!a) {
+ syslog(LOG_ERR, "Failed to calloc binding on interface %s", iface->ifname);
+ return NULL;
+ }
memcpy(a->hwaddr, mac, sizeof(a->hwaddr));
memcpy(a->hostname, hostname, hostlen);
if (assigned && !a->hostname[0] && hostname) {
a = realloc(a, sizeof(*a) + hostlen);
+ if (!a) {
+ syslog(LOG_ERR, "Failed to realloc binding on interface %s", iface->ifname);
+ return NULL;
+ }
memcpy(a->hostname, hostname, hostlen);
// Fixup list
if (list_empty(&iface->ia_assignments)) {
struct dhcpv6_assignment *border = calloc(1, sizeof(*border));
+ if (!border) {
+ syslog(LOG_ERR, "Calloc failed for border on interface %s", iface->ifname);
+ return -1;
+ }
+
border->length = 64;
list_add(&border->head, &iface->ia_assignments);
}
list_for_each_entry(lease, &leases, head) {
// Construct entry
struct dhcpv6_assignment *a = calloc(1, sizeof(*a) + lease->duid_len);
+ if (!a) {
+ syslog(LOG_ERR, "Calloc failed for static lease assignment on interface %s",
+ iface->ifname);
+ return -1;
+ }
+
a->clid_len = lease->duid_len;
a->length = 128;
a->assigned = lease->hostid;
a->all_class = class_oro;
a->classes_cnt = classes_cnt;
a->classes = realloc(a->classes, classes_cnt * sizeof(uint16_t));
- memcpy(a->classes, classes, classes_cnt * sizeof(uint16_t));
+ if (a->classes)
+ memcpy(a->classes, classes, classes_cnt * sizeof(uint16_t));
break;
}
}
if (!a && !iface->no_dynamic_dhcp) { // Create new binding
a = calloc(1, sizeof(*a) + clid_len);
- a->clid_len = clid_len;
- a->iaid = ia->iaid;
- a->length = reqlen;
- a->peer = *addr;
- a->assigned = reqhint;
- a->all_class = class_oro;
- a->classes_cnt = classes_cnt;
- if (classes_cnt) {
- a->classes = malloc(classes_cnt * sizeof(uint16_t));
- memcpy(a->classes, classes, classes_cnt * sizeof(uint16_t));
- }
+ if (a) {
+ a->clid_len = clid_len;
+ a->iaid = ia->iaid;
+ a->length = reqlen;
+ a->peer = *addr;
+ a->assigned = reqhint;
+ a->all_class = class_oro;
+ a->classes_cnt = classes_cnt;
+ if (classes_cnt) {
+ a->classes = malloc(classes_cnt * sizeof(uint16_t));
+ if (a->classes)
+ memcpy(a->classes, classes, classes_cnt * sizeof(uint16_t));
+ }
- if (first)
- memcpy(a->key, first->key, sizeof(a->key));
- else
- odhcpd_urandom(a->key, sizeof(a->key));
- memcpy(a->clid_data, clid_data, clid_len);
+ if (first)
+ memcpy(a->key, first->key, sizeof(a->key));
+ else
+ odhcpd_urandom(a->key, sizeof(a->key));
+ memcpy(a->clid_data, clid_data, clid_len);
- if (is_pd)
- while (!(assigned = assign_pd(iface, a)) && ++a->length <= 64);
- else
- assigned = assign_na(iface, a);
+ if (is_pd)
+ while (!(assigned = assign_pd(iface, a)) && ++a->length <= 64);
+ else
+ assigned = assign_na(iface, a);
+ }
}
if (!assigned || iface->ia_addr_len == 0) { // Set error status
} else if (assigned && hdr->msg_type == DHCPV6_MSG_REQUEST) {
if (hostname_len > 0) {
a->hostname = realloc(a->hostname, hostname_len + 1);
- memcpy(a->hostname, hostname, hostname_len);
- a->hostname[hostname_len] = 0;
+ if (a->hostname) {
+ memcpy(a->hostname, hostname, hostname_len);
+ a->hostname[hostname_len] = 0;
+ }
}
a->accept_reconf = accept_reconf;
apply_lease(iface, a, true);
int init_dhcpv6(void)
{
int sock = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
+ if (sock < 0) {
+ syslog(LOG_ERR, "Failed to create DHCPv6 server socket: %s",
+ strerror(errno));
+ return -1;
+ }
// Basic IPv6 configuration
int val = 1;
IPV6_ADD_MEMBERSHIP, &server, sizeof(server));
}
- setup_dhcpv6_ia_interface(iface, enable);
- return 0;
+ return setup_dhcpv6_ia_interface(iface, enable);
}
if (iface->static_ndp_len) {
char *entry = alloca(iface->static_ndp_len), *saveptr;
+ if (!entry) {
+ syslog(LOG_ERR, "Alloca failed for static NDP list");
+ return -1;
+ }
memcpy(entry, iface->static_ndp, iface->static_ndp_len);
for (entry = strtok_r(entry, " ", &saveptr); entry; entry = strtok_r(NULL, " ", &saveptr)) {
struct ndp_neighbor *n = malloc(sizeof(*n));
+ if (!n) {
+ syslog(LOG_ERR, "Malloc failed for static NDP-prefix %s", entry);
+ return -1;
+ }
+
n->iface = iface;
n->timeout = 0;
int *hlim = NULL;
struct in6_pktinfo *pktinfo;
struct in_pktinfo *pkt4info;
- for (struct cmsghdr *ch = CMSG_FIRSTHDR(&msg); ch != NULL &&
- destiface == 0; ch = CMSG_NXTHDR(&msg, ch)) {
+ for (struct cmsghdr *ch = CMSG_FIRSTHDR(&msg); ch != NULL; ch = CMSG_NXTHDR(&msg, ch)) {
if (ch->cmsg_level == IPPROTO_IPV6 &&
ch->cmsg_type == IPV6_PKTINFO) {
pktinfo = (struct in6_pktinfo*)CMSG_DATA(ch);
uloop_timeout_set(&iface->timer_rs, 1000);
}
+static int router_icmpv6_valid(struct sockaddr_in6 *source, uint8_t *data, size_t len)
+{
+ struct icmp6_hdr *hdr = (struct icmp6_hdr *)data;
+ struct icmpv6_opt *opt;
+ size_t optlen = len - sizeof(*hdr);
+
+ /* Hoplimit is already checked in odhcpd_receive_packets */
+ if (len < sizeof(*hdr))
+ return 0;
+
+ if (hdr->icmp6_code)
+ return 0;
+
+ switch (hdr->icmp6_type) {
+ case ND_ROUTER_ADVERT:
+ if (!IN6_IS_ADDR_LINKLOCAL(&source->sin6_addr))
+ return 0;
+
+ opt = (struct icmpv6_opt *)((struct nd_router_advert *)data + 1);
+ break;
+
+ case ND_ROUTER_SOLICIT:
+ opt = (struct icmpv6_opt *)((struct nd_router_solicit *)data + 1);
+ break;
+
+ default:
+ return 0;
+ }
+
+ while (optlen > 0) {
+ size_t l = opt->len << 3;
+
+ if (optlen < sizeof(*opt))
+ return 0;
+
+ if (l > optlen || l == 0)
+ return 0;
+
+ if (opt->type == ND_OPT_SOURCE_LINKADDR && IN6_IS_ADDR_UNSPECIFIED(&source->sin6_addr) &&
+ hdr->icmp6_type == ND_ROUTER_SOLICIT) {
+ return 0;
+ }
+
+ opt = (struct icmpv6_opt *)(((uint8_t *)opt) + l);
+
+ optlen -= l;
+ }
+
+ return 1;
+}
// Event handler for incoming ICMPv6 packets
-static void handle_icmpv6(_unused void *addr, void *data, size_t len,
+static void handle_icmpv6(void *addr, void *data, size_t len,
struct interface *iface)
{
struct icmp6_hdr *hdr = data;
+
+ if (!router_icmpv6_valid(addr, data, len))
+ return;
+
if ((iface->ra == RELAYD_SERVER && !iface->master)) { // Server mode
if (hdr->icmp6_type == ND_ROUTER_SOLICIT)
send_router_advert(&iface->timer_rs);
struct {
struct nd_router_advert h;
- struct icmpv6_opt lladdr;
+ struct nd_opt_slla lladdr;
struct nd_opt_mtu mtu;
struct nd_opt_prefix_info prefix[RELAYD_MAX_PREFIXES];
} adv = {
adv.h.nd_ra_flags_reserved |= ND_RA_PREF_LOW;
else if (iface->route_preference > 0)
adv.h.nd_ra_flags_reserved |= ND_RA_PREF_HIGH;
- odhcpd_get_mac(iface, adv.lladdr.data);
+ odhcpd_get_mac(iface, adv.lladdr.addr);
// If not currently shutting down
struct odhcpd_ipaddr addrs[RELAYD_MAX_PREFIXES];
if (!dns_addr)
dns_cnt = 0;
- struct {
- uint8_t type;
- uint8_t len;
- uint8_t pad;
- uint8_t pad2;
- uint32_t lifetime;
- } dns = {ND_OPT_RECURSIVE_DNS, (1 + (2 * dns_cnt)), 0, 0, htonl(dns_time)};
-
+ struct nd_opt_recursive_dns dns = {ND_OPT_RECURSIVE_DNS, (1 + (2 * dns_cnt)), 0, 0, htonl(dns_time)};
// DNS Search options
uint32_t lifetime;
uint8_t name[];
} *search = alloca(sizeof(*search) + search_padded);
+
+ if (!search) {
+ syslog(LOG_ERR, "Alloca failed for dns search on interface %s", iface->ifname);
+ return;
+ }
search->type = ND_OPT_DNS_SEARCH;
search->len = search_len ? ((sizeof(*search) + search_padded) / 8) : 0;
search->pad = 0;
icmpv6_for_each_option(opt, &adv[1], end) {
if (opt->type == ND_OPT_SOURCE_LINKADDR) {
// Store address of source MAC-address
- mac_ptr = opt->data;
+ mac_ptr = ((struct nd_opt_slla *)opt)->addr;
} else if (opt->type == ND_OPT_RECURSIVE_DNS && opt->len > 1) {
+ struct nd_opt_recursive_dns *dns = (struct nd_opt_recursive_dns *)opt;
// Check if we have to rewrite DNS
- dns_ptr = (struct in6_addr*)&opt->data[6];
- dns_count = (opt->len - 1) / 2;
+ dns_ptr = (struct in6_addr *)&dns[1];
+ dns_count = (dns->len - 1) / 2;
}
}
struct icmpv6_opt {
uint8_t type;
uint8_t len;
- uint8_t data[6];
};
+struct nd_opt_slla {
+ uint8_t type;
+ uint8_t len;
+ uint8_t addr[6];
+};
+
+struct nd_opt_recursive_dns {
+ uint8_t type;
+ uint8_t len;
+ uint8_t pad;
+ uint8_t pad2;
+ uint32_t lifetime;
+};
#define icmpv6_for_each_option(opt, start, end)\
for (opt = (struct icmpv6_opt*)(start);\
- (void*)(opt + 1) <= (void*)(end) && opt->len > 0 &&\
- (void*)(opt + opt->len) <= (void*)(end); opt += opt->len)
-
+ (void*)(opt + (opt->len << 3)) <= (void*)(end); opt += (opt->len << 3))
#define MaxRtrAdvInterval 600
#define MinRtrAdvInterval (MaxRtrAdvInterval / 3)