treewide: align syslog tracing
[oweals/odhcpd.git] / src / dhcpv4.c
index 166582e94ebe2fe971c97c457b0f7ad2a7a1a61b..4a74b8a983b300894b179f2cea1b37e4fddeb60e 100644 (file)
 static void dhcpv4_netevent_cb(unsigned long event, struct netevent_handler_info *info);
 static int setup_dhcpv4_addresses(struct interface *iface);
 static void update_static_assignments(struct interface *iface);
+static bool addr_is_fr_ip(struct interface *iface, struct in_addr *addr);
 static void valid_until_cb(struct uloop_timeout *event);
 static void handle_addrlist_change(struct interface *iface);
 static void free_dhcpv4_assignment(struct dhcpv4_assignment *a);
 static void dhcpv4_fr_start(struct dhcpv4_assignment *a);
+static void dhcpv4_fr_rand_delay(struct dhcpv4_assignment *a);
 static void dhcpv4_fr_stop(struct dhcpv4_assignment *a);
 static void handle_dhcpv4(void *addr, void *data, size_t len,
                struct interface *iface, void *dest_addr);
@@ -71,6 +73,8 @@ int dhcpv4_init(void)
 
 int dhcpv4_setup_interface(struct interface *iface, bool enable)
 {
+       int ret = 0;
+
        if (iface->dhcpv4_event.uloop.fd > 0) {
                uloop_fd_delete(&iface->dhcpv4_event.uloop);
                close(iface->dhcpv4_event.uloop.fd);
@@ -78,57 +82,97 @@ int dhcpv4_setup_interface(struct interface *iface, bool enable)
        }
 
        if (iface->dhcpv4 && enable) {
+               struct sockaddr_in bind_addr = {AF_INET, htons(DHCPV4_SERVER_PORT),
+                                       {INADDR_ANY}, {0}};
+               int val = 1;
+
                if (!iface->dhcpv4_assignments.next)
                        INIT_LIST_HEAD(&iface->dhcpv4_assignments);
 
                if (!iface->dhcpv4_fr_ips.next)
                        INIT_LIST_HEAD(&iface->dhcpv4_fr_ips);
 
-               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;
+               iface->dhcpv4_event.uloop.fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
+               if (iface->dhcpv4_event.uloop.fd < 0) {
+                       syslog(LOG_ERR, "socket(AF_INET): %m");
+                       ret = -1;
+                       goto out;
                }
 
                /* Basic IPv4 configuration */
-               int val = 1;
-               setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
-               setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val));
-               setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof(val));
+               if (setsockopt(iface->dhcpv4_event.uloop.fd, SOL_SOCKET, SO_REUSEADDR,
+                                       &val, sizeof(val)) < 0) {
+                       syslog(LOG_ERR, "setsockopt(SO_REUSEADDR): %m");
+                       ret = -1;
+                       goto out;
+               }
+
+               if (setsockopt(iface->dhcpv4_event.uloop.fd, SOL_SOCKET, SO_BROADCAST,
+                                       &val, sizeof(val)) < 0) {
+                       syslog(LOG_ERR, "setsockopt(SO_BROADCAST): %m");
+                       ret = -1;
+                       goto out;
+               }
+
+               if (setsockopt(iface->dhcpv4_event.uloop.fd, IPPROTO_IP, IP_PKTINFO,
+                                       &val, sizeof(val)) < 0) {
+                       syslog(LOG_ERR, "setsockopt(IP_PKTINFO): %m");
+                       ret = -1;
+                       goto out;
+               }
 
                val = IPTOS_PREC_INTERNETCONTROL;
-               setsockopt(sock, IPPROTO_IP, IP_TOS, &val, sizeof(val));
+               if (setsockopt(iface->dhcpv4_event.uloop.fd, IPPROTO_IP, IP_TOS,
+                                       &val, sizeof(val)) < 0) {
+                       syslog(LOG_ERR, "setsockopt(IP_TOS): %m");
+                       ret = -1;
+                       goto out;
+               }
 
                val = IP_PMTUDISC_DONT;
-               setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val));
+               if (setsockopt(iface->dhcpv4_event.uloop.fd, IPPROTO_IP, IP_MTU_DISCOVER,
+                                       &val, sizeof(val)) < 0) {
+                       syslog(LOG_ERR, "setsockopt(IP_MTU_DISCOVER): %m");
+                       ret = -1;
+                       goto out;
+               }
 
-               setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE,
-                               iface->ifname, strlen(iface->ifname));
+               if (setsockopt(iface->dhcpv4_event.uloop.fd, SOL_SOCKET, SO_BINDTODEVICE,
+                                       iface->ifname, strlen(iface->ifname)) < 0) {
+                       syslog(LOG_ERR, "setsockopt(SO_BINDTODEVICE): %m");
+                       ret = -1;
+                       goto out;
+               }
 
-               struct sockaddr_in bind_addr = {AF_INET, htons(DHCPV4_SERVER_PORT),
-                                       {INADDR_ANY}, {0}};
+               if (bind(iface->dhcpv4_event.uloop.fd, (struct sockaddr*)&bind_addr,
+                                       sizeof(bind_addr)) < 0) {
+                       syslog(LOG_ERR, "bind(): %m");
+                       ret = -1;
+                       goto out;
+               }
 
-               if (bind(sock, (struct sockaddr*)&bind_addr, sizeof(bind_addr))) {
-                       syslog(LOG_ERR, "Failed to open DHCPv4 server socket: %s",
-                                       strerror(errno));
-                       return -1;
+               if (setup_dhcpv4_addresses(iface) < 0) {
+                       ret = -1;
+                       goto out;
                }
 
-               iface->dhcpv4_event.uloop.fd = sock;
                iface->dhcpv4_event.handle_dgram = handle_dhcpv4;
                odhcpd_register(&iface->dhcpv4_event);
 
-               if (setup_dhcpv4_addresses(iface) < 0)
-                       return -1;
-
                update_static_assignments(iface);
        } else if (iface->dhcpv4_assignments.next) {
                while (!list_empty(&iface->dhcpv4_assignments))
                        free_dhcpv4_assignment(list_first_entry(&iface->dhcpv4_assignments,
                                                        struct dhcpv4_assignment, head));
        }
-       return 0;
+
+out:
+       if (ret < 0 && iface->dhcpv4_event.uloop.fd > 0) {
+               close(iface->dhcpv4_event.uloop.fd);
+               iface->dhcpv4_event.uloop.fd = -1;
+       }
+
+       return ret;
 }
 
 
@@ -185,12 +229,12 @@ static int setup_dhcpv4_addresses(struct interface *iface)
        if (iface->dhcpv4_start.s_addr & htonl(0xffff0000) ||
            iface->dhcpv4_end.s_addr & htonl(0xffff0000) ||
            ntohl(iface->dhcpv4_start.s_addr) > ntohl(iface->dhcpv4_end.s_addr)) {
-               syslog(LOG_ERR, "invalid DHCP range for %s", iface->name);
+               syslog(LOG_ERR, "Invalid DHCP range for %s", iface->name);
                return -1;
        }
 
        if (!iface->addr4_len) {
-               syslog(LOG_WARNING, "no network(s) available on %s", iface->name);
+               syslog(LOG_WARNING, "No network(s) available on %s", iface->name);
                return -1;
        }
 
@@ -201,6 +245,9 @@ static int setup_dhcpv4_addresses(struct interface *iface)
                struct in_addr *addr = &iface->addr4[i].addr.in;
                struct in_addr mask;
 
+               if (addr_is_fr_ip(iface, addr))
+                       continue;
+
                odhcpd_bitlen2netmask(false, iface->addr4[i].prefix, &mask);
                if ((start & ntohl(~mask.s_addr)) == start &&
                            (end & ntohl(~mask.s_addr)) == end) {
@@ -217,7 +264,7 @@ static int setup_dhcpv4_addresses(struct interface *iface)
 
        /* Don't allocate IP range for subnets bigger than 28 */
        if (iface->addr4[0].prefix > 28) {
-               syslog(LOG_WARNING, "auto allocation of DHCP range fails on %s", iface->name);
+               syslog(LOG_WARNING, "Auto allocation of DHCP range fails on %s", iface->name);
                return -1;
        }
 
@@ -274,8 +321,7 @@ static void update_static_assignments(struct interface *iface)
                        /* Construct entry */
                        a = calloc(1, sizeof(*a));
                        if (!a) {
-                               syslog(LOG_ERR, "Calloc failed for static lease on interface %s",
-                                       iface->ifname);
+                               syslog(LOG_ERR, "Calloc failed for static lease on %s", iface->name);
                                continue;
                        }
                        memcpy(a->hwaddr, lease->mac.ether_addr_octet, sizeof(a->hwaddr));
@@ -327,6 +373,18 @@ static void decr_ref_cnt_ip(struct odhcpd_ref_ip **ptr, struct interface *iface)
        *ptr = NULL;
 }
 
+static bool addr_is_fr_ip(struct interface *iface, struct in_addr *addr)
+{
+       struct odhcpd_ref_ip *p;
+
+       list_for_each_entry(p, &iface->dhcpv4_fr_ips, head) {
+               if (addr->s_addr == p->addr.addr.in.s_addr)
+                       return true;
+       }
+
+       return false;
+}
+
 static bool leases_require_fr(struct interface *iface, struct odhcpd_ipaddr *addr,
                                uint32_t mask)
 {
@@ -396,14 +454,14 @@ static void handle_addrlist_change(struct interface *iface)
        a = list_first_entry(&iface->dhcpv4_fr_ips, struct odhcpd_ref_ip, head);
 
        if (netlink_setup_addr(&a->addr, iface->ifindex, false, true)) {
-               syslog(LOG_ERR, "Failed to add ip address");
+               syslog(LOG_ERR, "Failed to add ip address on %s", iface->name);
                return;
        }
 
        list_for_each_entry(c, &iface->dhcpv4_assignments, head) {
                if ((c->flags & OAF_BOUND) && c->fr_ip && !c->fr_cnt) {
                        if (c->accept_fr_nonce || iface->dhcpv4_forcereconf)
-                               dhcpv4_fr_start(c);
+                               dhcpv4_fr_rand_delay(c);
                        else
                                dhcpv4_fr_stop(c);
                }
@@ -452,7 +510,9 @@ static void dhcpv4_put(struct dhcpv4_message *msg, uint8_t **cookie,
                uint8_t type, uint8_t len, const void *data)
 {
        uint8_t *c = *cookie;
-       if (*cookie + 2 + len > (uint8_t*)&msg[1])
+       uint8_t *end = (uint8_t *)msg + sizeof(*msg);
+
+       if (*cookie + 2 + len > end)
                return;
 
        *c++ = type;
@@ -540,14 +600,13 @@ static void dhcpv4_fr_send(struct dhcpv4_assignment *a)
        dest.sin_port = htons(DHCPV4_CLIENT_PORT);
        dest.sin_addr.s_addr = a->addr;
 
-       syslog(LOG_WARNING, "sending %s to %02x:%02x:%02x:%02x:%02x:%02x - %s",
-                       dhcpv4_msg_to_string(msg),
-                       a->hwaddr[0], a->hwaddr[1], a->hwaddr[2],
-                       a->hwaddr[3], a->hwaddr[4], a->hwaddr[5],
-                       inet_ntoa(dest.sin_addr));
-
-       sendto(iface->dhcpv4_event.uloop.fd, &fr_msg, sizeof(fr_msg),
-                       MSG_DONTWAIT, (struct sockaddr*)&dest, sizeof(dest));
+       if (sendto(iface->dhcpv4_event.uloop.fd, &fr_msg, sizeof(fr_msg),
+                       MSG_DONTWAIT, (struct sockaddr*)&dest, sizeof(dest)) < 0)
+               syslog(LOG_ERR, "Failed to send %s to %s - %s: %m", dhcpv4_msg_to_string(msg),
+                       odhcpd_print_mac(a->hwaddr, sizeof(a->hwaddr)), inet_ntoa(dest.sin_addr));
+       else
+               syslog(LOG_WARNING, "Sent %s to %s - %s", dhcpv4_msg_to_string(msg),
+                       odhcpd_print_mac(a->hwaddr, sizeof(a->hwaddr)), inet_ntoa(dest.sin_addr));
 }
 
 static void dhcpv4_fr_timer(struct uloop_timeout *event)
@@ -571,6 +630,28 @@ static void dhcpv4_fr_start(struct dhcpv4_assignment *a)
        dhcpv4_fr_send(a);
 }
 
+static void dhcpv4_fr_delay_timer(struct uloop_timeout *event)
+{
+       struct dhcpv4_assignment *a = container_of(event, struct dhcpv4_assignment, fr_timer);
+       struct interface *iface = a->iface;
+
+       (iface->dhcpv4_event.uloop.fd == -1 ? dhcpv4_fr_rand_delay(a) : dhcpv4_fr_start(a));
+}
+
+static void dhcpv4_fr_rand_delay(struct dhcpv4_assignment *a)
+{
+#define MIN_DELAY   500
+#define MAX_FUZZ    500
+       int msecs;
+
+       odhcpd_urandom(&msecs, sizeof(msecs));
+
+       msecs = labs(msecs)%MAX_FUZZ + MIN_DELAY;
+
+       uloop_timeout_set(&a->fr_timer, msecs);
+       a->fr_timer.cb = dhcpv4_fr_delay_timer;
+}
+
 static void dhcpv4_fr_stop(struct dhcpv4_assignment *a)
 {
        uloop_timeout_cancel(&a->fr_timer);
@@ -579,7 +660,7 @@ static void dhcpv4_fr_stop(struct dhcpv4_assignment *a)
        a->fr_timer.cb = NULL;
 }
 
-// Handler for DHCPv4 messages
+/* Handler for DHCPv4 messages */
 static void handle_dhcpv4(void *addr, void *data, size_t len,
                struct interface *iface, _unused void *dest_addr)
 {
@@ -592,7 +673,7 @@ static void handle_dhcpv4(void *addr, void *data, size_t len,
                return;
 
 
-       syslog(LOG_NOTICE, "Got DHCPv4 request");
+       syslog(LOG_NOTICE, "Got DHCPv4 request on %s", iface->name);
 
        if (!iface->dhcpv4_start_ip.s_addr && !iface->dhcpv4_end_ip.s_addr) {
                syslog(LOG_WARNING, "No DHCP range available on %s", iface->name);
@@ -718,11 +799,14 @@ static void handle_dhcpv4(void *addr, void *data, size_t len,
                        req->ciaddr.s_addr = INADDR_ANY;
        }
 
-       syslog(LOG_WARNING, "received %s from %02x:%02x:%02x:%02x:%02x:%02x",
-                       dhcpv4_msg_to_string(reqmsg),
-                       req->chaddr[0],req->chaddr[1],req->chaddr[2],
-                       req->chaddr[3],req->chaddr[4],req->chaddr[5]);
+       syslog(LOG_WARNING, "Received %s from %s on %s", dhcpv4_msg_to_string(reqmsg),
+                       odhcpd_print_mac(req->chaddr, req->hlen), iface->name);
 
+#ifdef WITH_UBUS
+       if (reqmsg == DHCPV4_MSG_RELEASE)
+               ubus_bcast_dhcp_event("dhcp.release", req->chaddr, req->hlen,
+                                       &req->ciaddr, hostname, iface->ifname);
+#endif
        if (reqmsg == DHCPV4_MSG_DECLINE || reqmsg == DHCPV4_MSG_RELEASE)
                return;
 
@@ -776,8 +860,10 @@ static void handle_dhcpv4(void *addr, void *data, size_t len,
                }
        }
 
-       struct ifreq ifr = {.ifr_name = ""};
-       strncpy(ifr.ifr_name, iface->ifname, sizeof(ifr.ifr_name));
+       struct ifreq ifr;
+
+       memset(&ifr, 0, sizeof(ifr));
+       strncpy(ifr.ifr_name, iface->ifname, sizeof(ifr.ifr_name) - 1);
 
        if (!ioctl(sock, SIOCGIFMTU, &ifr)) {
                uint16_t mtu = htons(ifr.ifr_mtu);
@@ -841,37 +927,42 @@ static void handle_dhcpv4(void *addr, void *data, size_t len,
                dest.sin_addr.s_addr = INADDR_BROADCAST;
                dest.sin_port = htons(DHCPV4_CLIENT_PORT);
        } else {
+               struct arpreq arp = {.arp_flags = ATF_COM};
+
                /*
                 * send reply to the newly (in this proccess) allocated IP
                 */
                dest.sin_addr = reply.yiaddr;
                dest.sin_port = htons(DHCPV4_CLIENT_PORT);
 
-               struct arpreq arp = {.arp_flags = ATF_COM};
                memcpy(arp.arp_ha.sa_data, req->chaddr, 6);
                memcpy(&arp.arp_pa, &dest, sizeof(arp.arp_pa));
                memcpy(arp.arp_dev, iface->ifname, sizeof(arp.arp_dev));
-               ioctl(sock, SIOCSARP, &arp);
+
+               if (ioctl(sock, SIOCSARP, &arp) < 0)
+                       syslog(LOG_ERR, "ioctl(SIOCSARP): %m");
        }
 
-       if (dest.sin_addr.s_addr == INADDR_BROADCAST)
-               /* reply goes to IP broadcast -> MAC broadcast */
-               syslog(LOG_WARNING, "sending %s to ff:ff:ff:ff:ff:ff - %s",
-                               dhcpv4_msg_to_string(msg),
-                               inet_ntoa(dest.sin_addr));
+       if (sendto(sock, &reply, sizeof(reply), MSG_DONTWAIT,
+                       (struct sockaddr*)&dest, sizeof(dest)) < 0)
+               syslog(LOG_ERR, "Failed to send %s to %s - %s: %m",
+                       dhcpv4_msg_to_string(msg),
+                       dest.sin_addr.s_addr == INADDR_BROADCAST ?
+                       "ff:ff:ff:ff:ff:ff": odhcpd_print_mac(req->chaddr, req->hlen),
+                       inet_ntoa(dest.sin_addr));
        else
-               /*
-                * reply is send directly to IP,
-                * MAC is assumed to be the same as the request
-                */
-               syslog(LOG_WARNING, "sending %s to %02x:%02x:%02x:%02x:%02x:%02x - %s",
-                               dhcpv4_msg_to_string(msg),
-                               req->chaddr[0],req->chaddr[1],req->chaddr[2],
-                               req->chaddr[3],req->chaddr[4],req->chaddr[5],
-                               inet_ntoa(dest.sin_addr));
-
-       sendto(sock, &reply, sizeof(reply), MSG_DONTWAIT,
-                       (struct sockaddr*)&dest, sizeof(dest));
+               syslog(LOG_ERR, "Sent %s to %s - %s",
+                       dhcpv4_msg_to_string(msg),
+                       dest.sin_addr.s_addr == INADDR_BROADCAST ?
+                       "ff:ff:ff:ff:ff:ff": odhcpd_print_mac(req->chaddr, req->hlen),
+                       inet_ntoa(dest.sin_addr));
+
+
+#ifdef WITH_UBUS
+       if (msg == DHCPV4_MSG_ACK)
+               ubus_bcast_dhcp_event("dhcp.ack", req->chaddr, req->hlen, &reply.yiaddr,
+                                       hostname, iface->ifname);
+#endif
 }
 
 static bool dhcpv4_assign(struct interface *iface,
@@ -881,11 +972,11 @@ static bool dhcpv4_assign(struct interface *iface,
        uint32_t end = ntohl(iface->dhcpv4_end_ip.s_addr);
        uint32_t count = end - start + 1;
 
-       // try to assign the IP the client asked for
+       /* try to assign the IP the client asked for */
        if (start <= ntohl(raddr) && ntohl(raddr) <= end &&
                        !find_assignment_by_addr(iface, raddr)) {
                assign->addr = raddr;
-               syslog(LOG_INFO, "assigning the IP the client asked for: %u.%u.%u.%u",
+               syslog(LOG_INFO, "Assigning the IP the client asked for: %u.%u.%u.%u",
                                ((uint8_t *)&assign->addr)[0],
                                ((uint8_t *)&assign->addr)[1],
                                ((uint8_t *)&assign->addr)[2],
@@ -893,10 +984,10 @@ static bool dhcpv4_assign(struct interface *iface,
                return true;
        }
 
-       // Seed RNG with checksum of hwaddress
+       /* Seed RNG with checksum of hwaddress */
        uint32_t seed = 0;
        for (size_t i = 0; i < sizeof(assign->hwaddr); ++i) {
-               // Knuth's multiplicative method
+               /* Knuth's multiplicative method */
                uint8_t o = assign->hwaddr[i];
                seed += (o*2654435761) % UINT32_MAX;
        }
@@ -907,7 +998,7 @@ static bool dhcpv4_assign(struct interface *iface,
 
        if (list_empty(&iface->dhcpv4_assignments)) {
                assign->addr = htonl(try);
-               syslog(LOG_INFO, "assigning mapped IP (empty list): %u.%u.%u.%u",
+               syslog(LOG_INFO, "Assigning mapped IP (empty list): %u.%u.%u.%u",
                                ((uint8_t *)&assign->addr)[0],
                                ((uint8_t *)&assign->addr)[1],
                                ((uint8_t *)&assign->addr)[2],
@@ -919,7 +1010,7 @@ static bool dhcpv4_assign(struct interface *iface,
                if (!find_assignment_by_addr(iface, htonl(try))) {
                        /* test was successful: IP address is not assigned, assign it */
                        assign->addr = htonl(try);
-                       syslog(LOG_DEBUG, "assigning mapped IP: %u.%u.%u.%u (try %u of %u)",
+                       syslog(LOG_DEBUG, "Assigning mapped IP: %u.%u.%u.%u (try %u of %u)",
                                        ((uint8_t *)&assign->addr)[0],
                                        ((uint8_t *)&assign->addr)[1],
                                        ((uint8_t *)&assign->addr)[2],
@@ -930,7 +1021,7 @@ static bool dhcpv4_assign(struct interface *iface,
                try = (((try - start) + 1) % count) + start;
        }
 
-       syslog(LOG_WARNING, "can't assign any IP address -> address space is full");
+       syslog(LOG_WARNING, "Can't assign any IP address -> address space is full");
        return false;
 }
 
@@ -1003,6 +1094,11 @@ static struct dhcpv4_assignment* dhcpv4_lease(struct interface *iface,
                                        if (a->hostname) {
                                                memcpy(a->hostname, hostname, hostname_len);
                                                a->hostname[hostname_len] = 0;
+
+                                               if (odhcpd_valid_hostname(a->hostname))
+                                                       a->flags &= ~OAF_BROKEN_HOSTNAME;
+                                               else
+                                                       a->flags |= OAF_BROKEN_HOSTNAME;
                                        }
                                }