dhcpv4: Keep DHCPv4 assignment lifetime value in sync with assigned leasetime
[project/odhcpd.git] / src / dhcpv4.c
index 647cdc0..618475f 100644 (file)
@@ -176,10 +176,13 @@ int setup_dhcpv4_interface(struct interface *iface, bool enable)
                                        iface->ifname);
                                return -1;
                        }
+                       if (lease->dhcpv4_leasetime >= 60)
+                               a->leasetime = lease->dhcpv4_leasetime;
                        a->addr = ntohl(lease->ipaddr.s_addr);
                        memcpy(a->hwaddr, lease->mac.ether_addr_octet, sizeof(a->hwaddr));
                        memcpy(a->hostname, lease->hostname, hostlen);
-                       a->valid_until = LONG_MAX;
+                       /* Infinite valid */
+                       a->valid_until = 0;
 
                        // Assign to all interfaces
                        struct dhcpv4_assignment *c;
@@ -222,7 +225,6 @@ int setup_dhcpv4_interface(struct interface *iface, bool enable)
                        struct dhcpv4_assignment *a = list_first_entry(&iface->dhcpv4_assignments,
                                        struct dhcpv4_assignment, head);
                        list_del(&a->head);
-                       free(a->hostname);
                        free(a);
                }
 
@@ -245,8 +247,7 @@ static void dhcpv4_put(struct dhcpv4_message *msg, uint8_t **cookie,
        *cookie = c + len;
 }
 
-
-// Simple DHCPv6-server for information requests
+// Handler for DHCPv4 messages
 static void handle_dhcpv4(void *addr, void *data, size_t len,
                struct interface *iface, _unused void *dest_addr)
 {
@@ -383,14 +384,25 @@ static void handle_dhcpv4(void *addr, void *data, size_t len,
        if (lease) {
                reply.yiaddr.s_addr = htonl(lease->addr);
 
-               uint32_t val = htonl(iface->dhcpv4_leasetime);
+               uint32_t val;
+               uint32_t leasetime;
+
+               if (lease->leasetime >= 60) {
+                       leasetime = lease->leasetime;
+               } else {
+                       leasetime = iface->dhcpv4_leasetime;
+               }
+
+               val = htonl(leasetime);
                dhcpv4_put(&reply, &cookie, DHCPV4_OPT_LEASETIME, 4, &val);
 
-               val = htonl(500 * iface->dhcpv4_leasetime / 1000);
-               dhcpv4_put(&reply, &cookie, DHCPV4_OPT_RENEW, 4, &val);
+               if (leasetime != UINT32_MAX) {
+                       val = htonl(500 * leasetime / 1000);
+                       dhcpv4_put(&reply, &cookie, DHCPV4_OPT_RENEW, 4, &val);
 
-               val = htonl(875 * iface->dhcpv4_leasetime / 1000);
-               dhcpv4_put(&reply, &cookie, DHCPV4_OPT_REBIND, 4, &val);
+                       val = htonl(875 * leasetime / 1000);
+                       dhcpv4_put(&reply, &cookie, DHCPV4_OPT_REBIND, 4, &val);
+               }
 
                dhcpv4_put(&reply, &cookie, DHCPV4_OPT_NETMASK, 4, &ifnetmask.sin_addr);
 
@@ -588,7 +600,7 @@ static struct dhcpv4_assignment* dhcpv4_lease(struct interface *iface,
                        a = c;
                        if (c->addr == raddr)
                                break;
-               } else if (c->valid_until < now) {
+               } else if (!INFINITE_VALID(c->valid_until) && c->valid_until < now) {
                        list_del(&c->head);
                        free(c);
                }
@@ -606,6 +618,8 @@ static struct dhcpv4_assignment* dhcpv4_lease(struct interface *iface,
                        }
                        memcpy(a->hwaddr, mac, sizeof(a->hwaddr));
                        memcpy(a->hostname, hostname, hostlen);
+                       // Don't consider new assignment as infinite
+                       a->valid_until = now;
 
                        assigned = dhcpv4_assign(iface, a, raddr);
                }
@@ -623,10 +637,18 @@ static struct dhcpv4_assignment* dhcpv4_lease(struct interface *iface,
                        a->head.prev->next = &a->head;
                }
 
-               // Was only a solicitation: mark binding for removal
-               if (assigned && a->valid_until < now) {
-                       a->valid_until = (msg == DHCPV4_MSG_DISCOVER) ? 0 :
-                                       (now + iface->dhcpv4_leasetime);
+               uint32_t leasetime;
+               if (a->leasetime) {
+                       leasetime = a->leasetime;
+               } else {
+                       leasetime = iface->dhcpv4_leasetime;
+               }
+
+               if (assigned) {
+                       if (!INFINITE_VALID(a->valid_until))
+                               // Was only a discover; mark binding for removal
+                               a->valid_until = ((msg == DHCPV4_MSG_DISCOVER) ? now : ((leasetime == UINT32_MAX) ?
+                                                       0 : (time_t)(now + leasetime)));
                } else if (!assigned && a) { // Cleanup failed assignment
                        free(a);
                        a = NULL;
@@ -635,9 +657,9 @@ static struct dhcpv4_assignment* dhcpv4_lease(struct interface *iface,
                if (assigned && a)
                        lease = a;
        } else if (msg == DHCPV4_MSG_RELEASE) {
-               if (a && a->valid_until != LONG_MAX)
-                       a->valid_until = 0;
-       } else if (msg == DHCPV4_MSG_DECLINE && a && a->valid_until != LONG_MAX) {
+               if (a && !INFINITE_VALID(a->valid_until))
+                       a->valid_until = now - 1;
+       } else if (msg == DHCPV4_MSG_DECLINE && a && !INFINITE_VALID(a->valid_until)) {
                memset(a->hwaddr, 0, sizeof(a->hwaddr));
                a->valid_until = now + 3600; // Block address for 1h
        }