Merge branch 'master' of github.com:sbyx/odhcpd
authorSteven Barth <steven@midlink.org>
Tue, 10 Dec 2013 14:56:41 +0000 (15:56 +0100)
committerSteven Barth <steven@midlink.org>
Tue, 10 Dec 2013 14:56:41 +0000 (15:56 +0100)
src/config.c
src/dhcpv4.c
src/dhcpv6-ia.c
src/dhcpv6.c
src/ndp.c
src/odhcpd.c
src/router.c
src/router.h

index 6c09541..98a826f 100644 (file)
@@ -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);
 
index 79fabe2..49b75f4 100644 (file)
@@ -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
index 3defa84..205b617 100644 (file)
@@ -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);
index 9515f40..7560a75 100644 (file)
@@ -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);
 }
 
 
index 89bcd3c..bd62068 100644 (file)
--- a/src/ndp.c
+++ b/src/ndp.c
@@ -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;
 
index d6ca298..cb84513 100644 (file)
@@ -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);
index 945151e..86726be 100644 (file)
@@ -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;
                }
        }
 
index 1e8649c..8d74967 100644 (file)
 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)