dhcpv4: improve error when a prefix is too long
[project/odhcpd.git] / src / dhcpv4.c
index 2634f65a69e997c5292c7c7f3679709c45cf62d4..3191ff2cc473d53b2e296daf8afbb76e029c97cb 100644 (file)
@@ -37,6 +37,7 @@
 
 #define PACKET_SIZE(start, end) (((uint8_t *)end - (uint8_t *)start) < DHCPV4_MIN_PACKET_SIZE ? \
                                 DHCPV4_MIN_PACKET_SIZE : (uint8_t *)end - (uint8_t *)start)
+#define MAX_PREFIX_LEN 28
 
 static void dhcpv4_netevent_cb(unsigned long event, struct netevent_handler_info *info);
 static int setup_dhcpv4_addresses(struct interface *iface);
@@ -77,13 +78,15 @@ int dhcpv4_setup_interface(struct interface *iface, bool enable)
 {
        int ret = 0;
 
+       enable = enable && (iface->dhcpv4 != MODE_DISABLED);
+
        if (iface->dhcpv4_event.uloop.fd >= 0) {
                uloop_fd_delete(&iface->dhcpv4_event.uloop);
                close(iface->dhcpv4_event.uloop.fd);
                iface->dhcpv4_event.uloop.fd = -1;
        }
 
-       if (iface->dhcpv4 && enable) {
+       if (enable) {
                struct sockaddr_in bind_addr = {AF_INET, htons(DHCPV4_SERVER_PORT),
                                        {INADDR_ANY}, {0}};
                int val = 1;
@@ -212,12 +215,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_WARNING, "Invalid DHCP range for %s", iface->name);
                return -1;
        }
 
        if (!iface->addr4_len) {
-               syslog(LOG_ERR, "No network(s) available on %s", iface->name);
+               syslog(LOG_WARNING, "No network(s) available on %s", iface->name);
                return -1;
        }
 
@@ -246,9 +249,9 @@ 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_ERR, "Auto allocation of DHCP range fails on %s", iface->name);
+       /* Don't allocate IP range for subnets smaller than /28 */
+       if (iface->addr4[0].prefix > MAX_PREFIX_LEN) {
+               syslog(LOG_WARNING, "Auto allocation of DHCP range fails on %s (prefix length must be < %d).", iface->name, MAX_PREFIX_LEN + 1);
                return -1;
        }
 
@@ -381,7 +384,7 @@ 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 on %s", iface->name);
+               syslog(LOG_WARNING, "Failed to add ip address on %s", iface->name);
                return;
        }
 
@@ -531,7 +534,7 @@ static void dhcpv4_fr_send(struct dhcp_assignment *a)
                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_NOTICE, "Sent %s to %s - %s", dhcpv4_msg_to_string(msg),
+               syslog(LOG_DEBUG, "Sent %s to %s - %s", dhcpv4_msg_to_string(msg),
                        odhcpd_print_mac(a->hwaddr, sizeof(a->hwaddr)), inet_ntoa(dest.sin_addr));
 }
 
@@ -586,23 +589,41 @@ static void dhcpv4_fr_stop(struct dhcp_assignment *a)
        a->fr_timer.cb = NULL;
 }
 
+static int dhcpv4_send_reply(const void *buf, size_t len,
+                            const struct sockaddr *dest, socklen_t dest_len,
+                            void *opaque)
+{
+       int *sock = opaque;
+
+       return sendto(*sock, buf, len, MSG_DONTWAIT, dest, dest_len);
+}
+
 /* Handler for DHCPv4 messages */
 static void handle_dhcpv4(void *addr, void *data, size_t len,
                struct interface *iface, _unused void *dest_addr)
+{
+       int sock = iface->dhcpv4_event.uloop.fd;
+
+       dhcpv4_handle_msg(addr, data, len, iface, dest_addr, dhcpv4_send_reply, &sock);
+}
+
+void dhcpv4_handle_msg(void *addr, void *data, size_t len,
+               struct interface *iface, _unused void *dest_addr,
+               send_reply_cb_t send_reply, void *opaque)
 {
        struct dhcpv4_message *req = data;
 
-       if (!iface->dhcpv4)
+       if (iface->dhcpv4 == MODE_DISABLED)
                return;
 
        if (len < offsetof(struct dhcpv4_message, options) + 4 ||
                        req->op != DHCPV4_BOOTREQUEST || req->hlen != 6)
                return;
 
-       syslog(LOG_NOTICE, "Got DHCPv4 request on %s", iface->name);
+       syslog(LOG_DEBUG, "Got DHCPv4 request on %s", iface->name);
 
        if (!iface->dhcpv4_start_ip.s_addr && !iface->dhcpv4_end_ip.s_addr) {
-               syslog(LOG_ERR, "No DHCP range available on %s", iface->name);
+               syslog(LOG_WARNING, "No DHCP range available on %s", iface->name);
                return;
        }
 
@@ -732,13 +753,13 @@ static void handle_dhcpv4(void *addr, void *data, size_t len,
                        req->ciaddr.s_addr = INADDR_ANY;
        }
 
-       syslog(LOG_NOTICE, "Received %s from %s on %s", dhcpv4_msg_to_string(reqmsg),
+       syslog(LOG_INFO, "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);
+                                       &req->ciaddr, a ? a->hostname : NULL, iface->ifname);
 #endif
        if (reqmsg == DHCPV4_MSG_DECLINE || reqmsg == DHCPV4_MSG_RELEASE)
                return;
@@ -822,12 +843,21 @@ static void handle_dhcpv4(void *addr, void *data, size_t len,
                                4 * iface->dhcpv4_router_cnt, iface->dhcpv4_router);
 
 
-       if (iface->dhcpv4_dns_cnt == 0)
-               dhcpv4_put(&reply, &cookie, DHCPV4_OPT_DNSSERVER, 4, &iface->dhcpv4_local);
-       else
+       if (iface->dhcpv4_dns_cnt == 0) {
+               if (iface->dns_service)
+                       dhcpv4_put(&reply, &cookie, DHCPV4_OPT_DNSSERVER, 4, &iface->dhcpv4_local);
+       } else
                dhcpv4_put(&reply, &cookie, DHCPV4_OPT_DNSSERVER,
                                4 * iface->dhcpv4_dns_cnt, iface->dhcpv4_dns);
 
+       if (a && a->reqopts && iface->dhcpv4_ntp_cnt != 0) {
+               for(size_t opts = 0; a->reqopts[opts]; opts++) {
+                       if (a->reqopts[opts] == DHCPV4_OPT_NTPSERVER) {
+                               dhcpv4_put(&reply, &cookie, DHCPV4_OPT_NTPSERVER,
+                                               4 * iface->dhcpv4_ntp_cnt, iface->dhcpv4_ntp);
+                       }
+               }
+       }
 
        dhcpv4_put(&reply, &cookie, DHCPV4_OPT_END, 0, NULL);
 
@@ -868,23 +898,25 @@ static void handle_dhcpv4(void *addr, void *data, size_t len,
                dest.sin_addr = reply.yiaddr;
                dest.sin_port = htons(DHCPV4_CLIENT_PORT);
 
-               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));
+               if (!(iface->ifflags & IFF_NOARP)) {
+                       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));
 
-               if (ioctl(sock, SIOCSARP, &arp) < 0)
-                       syslog(LOG_ERR, "ioctl(SIOCSARP): %m");
+                       if (ioctl(sock, SIOCSARP, &arp) < 0)
+                               syslog(LOG_ERR, "ioctl(SIOCSARP): %m");
+               }
        }
 
-       if (sendto(sock, &reply, PACKET_SIZE(&reply, cookie), MSG_DONTWAIT,
-                       (struct sockaddr*)&dest, sizeof(dest)) < 0)
+       if (send_reply(&reply, PACKET_SIZE(&reply, cookie),
+                      (struct sockaddr*)&dest, sizeof(dest), opaque) < 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
-               syslog(LOG_NOTICE, "Sent %s to %s - %s",
+               syslog(LOG_DEBUG, "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),
@@ -894,7 +926,7 @@ static void handle_dhcpv4(void *addr, void *data, size_t len,
 #ifdef WITH_UBUS
        if (msg == DHCPV4_MSG_ACK)
                ubus_bcast_dhcp_event("dhcp.ack", req->chaddr, req->hlen, &reply.yiaddr,
-                                       hostname, iface->ifname);
+                                       a ? a->hostname : NULL, iface->ifname);
 #endif
 }
 
@@ -947,7 +979,7 @@ static bool dhcpv4_assign(struct interface *iface, struct dhcp_assignment *a,
                                                    a, a->addr);
 
                if (assigned)
-                       syslog(LOG_INFO, "Assigning static IP: %s", ip4toa(a->addr));
+                       syslog(LOG_DEBUG, "Assigning static IP: %s", ip4toa(a->addr));
 
                return assigned;
        }
@@ -959,7 +991,7 @@ static bool dhcpv4_assign(struct interface *iface, struct dhcp_assignment *a,
                                                    a, raddr);
 
                if (assigned) {
-                       syslog(LOG_INFO, "Assigning the IP the client asked for: %s",
+                       syslog(LOG_DEBUG, "Assigning the IP the client asked for: %s",
                               ip4toa(a->addr));
 
                        return true;
@@ -986,14 +1018,15 @@ static bool dhcpv4_assign(struct interface *iface, struct dhcp_assignment *a,
                                                    a, n_try);
 
                if (assigned) {
-                       syslog(LOG_INFO, "Assigning mapped IP: %s (try %u of %u)",
+                       syslog(LOG_DEBUG, "Assigning mapped IP: %s (try %u of %u)",
                               ip4toa(a->addr), i + 1, count);
 
                        return true;
                }
        }
 
-       syslog(LOG_WARNING, "Can't assign any IP address -> address space is full");
+       syslog(LOG_NOTICE, "Can't assign any IP address -> address space is full");
+
        return false;
 }
 
@@ -1026,8 +1059,8 @@ dhcpv4_lease(struct interface *iface, enum dhcpv4_msg msg, const uint8_t *mac,
                                /* Create new binding */
                                a = alloc_assignment(0);
                                if (!a) {
-                                       syslog(LOG_ERR, "Failed to alloc assignment on interface %s",
-                                                       iface->ifname);
+                                       syslog(LOG_WARNING, "Failed to alloc assignment on interface %s",
+                                                           iface->ifname);
                                        return NULL;
                                }
                                memcpy(a->hwaddr, mac, sizeof(a->hwaddr));
@@ -1069,7 +1102,7 @@ dhcpv4_lease(struct interface *iface, enum dhcpv4_msg msg, const uint8_t *mac,
                        if (a->leasetime)
                                my_leasetime = a->leasetime;
                        else
-                               my_leasetime = iface->dhcpv4_leasetime;
+                               my_leasetime = iface->dhcp_leasetime;
 
                        if ((*leasetime == 0) || (my_leasetime < *leasetime))
                                *leasetime = my_leasetime;
@@ -1078,8 +1111,7 @@ dhcpv4_lease(struct interface *iface, enum dhcpv4_msg msg, const uint8_t *mac,
                                a->flags &= ~OAF_BOUND;
 
                                *incl_fr_opt = accept_fr_nonce;
-                               if (!(a->flags & OAF_STATIC))
-                                       a->valid_until = now;
+                               a->valid_until = now;
                        } else {
                                if ((!(a->flags & OAF_STATIC) || !a->hostname) && hostname_len > 0) {
                                        a->hostname = realloc(a->hostname, hostname_len + 1);
@@ -1110,8 +1142,7 @@ dhcpv4_lease(struct interface *iface, enum dhcpv4_msg msg, const uint8_t *mac,
                                } else
                                        *incl_fr_opt = false;
 
-                               if (!(a->flags & OAF_STATIC))
-                                       a->valid_until = ((*leasetime == UINT32_MAX) ? 0 : (time_t)(now + *leasetime));
+                               a->valid_until = ((*leasetime == UINT32_MAX) ? 0 : (time_t)(now + *leasetime));
                        }
                } else if (!assigned && a) {
                        /* Cleanup failed assignment */
@@ -1121,17 +1152,16 @@ dhcpv4_lease(struct interface *iface, enum dhcpv4_msg msg, const uint8_t *mac,
 
        } else if (msg == DHCPV4_MSG_RELEASE && a) {
                a->flags &= ~OAF_BOUND;
-
-               if (!(a->flags & OAF_STATIC))
-                       a->valid_until = now - 1;
+               a->valid_until = now - 1;
 
        } else if (msg == DHCPV4_MSG_DECLINE && a) {
                a->flags &= ~OAF_BOUND;
 
-               if (!(a->flags & OAF_STATIC)) {
+               if (!(a->flags & OAF_STATIC) || a->lease->ipaddr != a->addr) {
                        memset(a->hwaddr, 0, sizeof(a->hwaddr));
                        a->valid_until = now + 3600; /* Block address for 1h */
-               }
+               } else
+                       a->valid_until = now - 1;
        }
 
        dhcpv6_ia_write_statefile();