diff options
-rw-r--r-- | src/config.c | 37 | ||||
-rw-r--r-- | src/dhcpv4.c | 19 | ||||
-rw-r--r-- | src/dhcpv6-ia.c | 63 | ||||
-rw-r--r-- | src/dhcpv6.c | 8 | ||||
-rw-r--r-- | src/ndp.c | 9 | ||||
-rw-r--r-- | src/odhcpd.c | 3 | ||||
-rw-r--r-- | src/router.c | 81 | ||||
-rw-r--r-- | src/router.h | 18 |
8 files changed, 188 insertions, 50 deletions
diff --git a/src/config.c b/src/config.c index 6c09541..98a826f 100644 --- a/src/config.c +++ b/src/config.c @@ -187,8 +187,8 @@ static void set_config(struct uci_section *s) 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); @@ -218,6 +218,8 @@ static int set_lease(struct uci_section *s) 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); @@ -233,6 +235,9 @@ static int set_lease(struct uci_section *s) 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)); @@ -251,8 +256,10 @@ static int set_lease(struct uci_section *s) return 0; err: - free(lease->duid); - free(lease); + if (lease) { + free(lease->duid); + free(lease); + } return -1; } @@ -271,6 +278,9 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr 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); } @@ -348,6 +358,9 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr 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); } @@ -396,10 +409,16 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr 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; @@ -421,6 +440,9 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr 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; } @@ -467,6 +489,9 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr 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; } @@ -499,6 +524,10 @@ void odhcpd_reload(void) } struct interface *master = NULL, *i, *n; + + if (!uci) + return; + list_for_each_entry(i, &interfaces, head) clean_interface(i); diff --git a/src/dhcpv4.c b/src/dhcpv4.c index 79fabe2..49b75f4 100644 --- a/src/dhcpv4.c +++ b/src/dhcpv4.c @@ -61,6 +61,11 @@ int setup_dhcpv4_interface(struct interface *iface, bool enable) 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; @@ -139,7 +144,11 @@ int setup_dhcpv4_interface(struct interface *iface, bool enable) // 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); @@ -470,6 +479,10 @@ static struct dhcpv4_assignment* dhcpv4_lease(struct interface *iface, 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); @@ -478,6 +491,10 @@ static struct dhcpv4_assignment* dhcpv4_lease(struct interface *iface, 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 diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c index 3defa84..205b617 100644 --- a/src/dhcpv6-ia.c +++ b/src/dhcpv6-ia.c @@ -63,6 +63,11 @@ int setup_dhcpv6_ia_interface(struct interface *iface, bool enable) 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); } @@ -74,6 +79,12 @@ int setup_dhcpv6_ia_interface(struct interface *iface, bool enable) 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; @@ -885,7 +896,8 @@ size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface, 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; } } @@ -897,28 +909,31 @@ size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface, 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 @@ -959,8 +974,10 @@ size_t dhcpv6_handle_ia(uint8_t *buf, size_t buflen, struct interface *iface, } 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); diff --git a/src/dhcpv6.c b/src/dhcpv6.c index 9515f40..7560a75 100644 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -40,6 +40,11 @@ static struct odhcpd_event dhcpv6_event = {{.fd = -1}, handle_dhcpv6}; 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; @@ -91,8 +96,7 @@ int setup_dhcpv6_interface(struct interface *iface, bool enable) IPV6_ADD_MEMBERSHIP, &server, sizeof(server)); } - setup_dhcpv6_ia_interface(iface, enable); - return 0; + return setup_dhcpv6_ia_interface(iface, enable); } @@ -160,10 +160,19 @@ int setup_ndp_interface(struct interface *iface, bool 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; diff --git a/src/odhcpd.c b/src/odhcpd.c index d6ca298..cb84513 100644 --- a/src/odhcpd.c +++ b/src/odhcpd.c @@ -320,8 +320,7 @@ static void odhcpd_receive_packets(struct uloop_fd *u, _unused unsigned int even 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); diff --git a/src/router.c b/src/router.c index 945151e..86726be 100644 --- a/src/router.c +++ b/src/router.c @@ -130,12 +130,66 @@ static void sigusr1_refresh(_unused int signal) 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); @@ -211,7 +265,7 @@ static void send_router_advert(struct uloop_timeout *event) 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 = { @@ -230,7 +284,7 @@ static void send_router_advert(struct uloop_timeout *event) 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]; @@ -322,14 +376,7 @@ static void send_router_advert(struct uloop_timeout *event) 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 @@ -356,6 +403,11 @@ static void send_router_advert(struct uloop_timeout *event) 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; @@ -453,11 +505,12 @@ static void forward_router_advertisement(uint8_t *data, size_t len) 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; } } diff --git a/src/router.h b/src/router.h index 1e8649c..8d74967 100644 --- a/src/router.h +++ b/src/router.h @@ -20,15 +20,25 @@ 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) |