From e63a2837acfaf54c97a9db466bf485e9f7024fe8 Mon Sep 17 00:00:00 2001 From: Steven Barth Date: Thu, 19 May 2016 08:54:24 +0200 Subject: [PATCH] Add per-host leasetime support Patch by Daniel Dickinson Signed-off-by: Steven Barth --- src/config.c | 56 ++++++++++++++++++++++++++++++++++++++++---------------- src/dhcpv4.c | 26 ++++++++++++++++++++++---- src/dhcpv4.h | 1 + src/dhcpv6-ia.c | 10 +++++++++- src/dhcpv6.h | 2 ++ src/odhcpd.h | 1 + 6 files changed, 75 insertions(+), 21 deletions(-) diff --git a/src/config.c b/src/config.c index 8f2c6bf..ef51112 100644 --- a/src/config.c +++ b/src/config.c @@ -98,6 +98,7 @@ enum { LEASE_ATTR_MAC, LEASE_ATTR_DUID, LEASE_ATTR_HOSTID, + LEASE_ATTR_LEASETIME, LEASE_ATTR_NAME, LEASE_ATTR_MAX }; @@ -108,6 +109,7 @@ static const struct blobmsg_policy lease_attrs[LEASE_ATTR_MAX] = { [LEASE_ATTR_MAC] = { .name = "mac", .type = BLOBMSG_TYPE_STRING }, [LEASE_ATTR_DUID] = { .name = "duid", .type = BLOBMSG_TYPE_STRING }, [LEASE_ATTR_HOSTID] = { .name = "hostid", .type = BLOBMSG_TYPE_STRING }, + [LEASE_ATTR_LEASETIME] = { .name = "leasetime", .type = BLOBMSG_TYPE_STRING }, [LEASE_ATTR_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING }, }; @@ -215,6 +217,32 @@ static void set_config(struct uci_section *s) } } +static double parse_leasetime(struct blob_attr *c) { + char *val = blobmsg_get_string(c), *endptr; + double time = strtod(val, &endptr); + if (time && endptr[0]) { + if (endptr[0] == 's') + time *= 1; + else if (endptr[0] == 'm') + time *= 60; + else if (endptr[0] == 'h') + time *= 3600; + else if (endptr[0] == 'd') + time *= 24 * 3600; + else if (endptr[0] == 'w') + time *= 7 * 24 * 3600; + else + goto err; + } + + if (time >= 60) + return time; + + return 0; + +err: + return -1; +} static int set_lease(struct uci_section *s) { @@ -265,6 +293,15 @@ static int set_lease(struct uci_section *s) goto err; } + if ((c = tb[LEASE_ATTR_LEASETIME])) { + double time = parse_leasetime(c); + if (time < 0) + goto err; + + if (time >= 60) + lease->dhcpv4_leasetime = time; + } + list_add(&lease->head, &leases); return 0; @@ -330,22 +367,9 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr iface->ignore = blobmsg_get_bool(c); if ((c = tb[IFACE_ATTR_LEASETIME])) { - char *val = blobmsg_get_string(c), *endptr; - double time = strtod(val, &endptr); - if (time && endptr[0]) { - if (endptr[0] == 's') - time *= 1; - else if (endptr[0] == 'm') - time *= 60; - else if (endptr[0] == 'h') - time *= 3600; - else if (endptr[0] == 'd') - time *= 24 * 3600; - else if (endptr[0] == 'w') - time *= 7 * 24 * 3600; - else - goto err; - } + double time = parse_leasetime(c); + if (time < 0) + goto err; if (time >= 60) iface->dhcpv4_leasetime = time; diff --git a/src/dhcpv4.c b/src/dhcpv4.c index a657e13..f277a67 100644 --- a/src/dhcpv4.c +++ b/src/dhcpv4.c @@ -176,6 +176,8 @@ 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); @@ -382,13 +384,22 @@ 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); + val = htonl(500 * leasetime / 1000); dhcpv4_put(&reply, &cookie, DHCPV4_OPT_RENEW, 4, &val); - val = htonl(875 * iface->dhcpv4_leasetime / 1000); + 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); @@ -622,10 +633,17 @@ static struct dhcpv4_assignment* dhcpv4_lease(struct interface *iface, a->head.prev->next = &a->head; } + uint32_t leasetime; + if (a->leasetime) { + leasetime = a->leasetime; + } else { + leasetime = iface->dhcpv4_leasetime; + } + // 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); + (now + leasetime); } else if (!assigned && a) { // Cleanup failed assignment free(a); a = NULL; diff --git a/src/dhcpv4.h b/src/dhcpv4.h index 6c72441..6c4dd50 100644 --- a/src/dhcpv4.h +++ b/src/dhcpv4.h @@ -78,6 +78,7 @@ struct dhcpv4_assignment { uint32_t addr; time_t valid_until; uint8_t hwaddr[6]; + uint32_t leasetime; char hostname[]; }; diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c index 9b8c479..4c321ac 100644 --- a/src/dhcpv6-ia.c +++ b/src/dhcpv6-ia.c @@ -100,6 +100,9 @@ int setup_dhcpv6_ia_interface(struct interface *iface, bool enable) return -1; } + if (lease->dhcpv4_leasetime > 0) + a->leasetime = lease->dhcpv4_leasetime; + a->clid_len = duid_len; a->length = 128; if (lease->hostid) { @@ -675,7 +678,12 @@ static size_t append_reply(uint8_t *buf, size_t buflen, uint16_t status, datalen += sizeof(stat); } else { if (a) { - uint32_t leasetime = iface->dhcpv4_leasetime; + uint32_t leasetime; + if (a->leasetime > 0) { + leasetime = a->leasetime; + } else { + leasetime = iface->dhcpv4_leasetime; + } if (leasetime == 0) leasetime = 3600; else if (leasetime < 60) diff --git a/src/dhcpv6.h b/src/dhcpv6.h index d9f42c8..09ab46f 100644 --- a/src/dhcpv6.h +++ b/src/dhcpv6.h @@ -155,6 +155,8 @@ struct dhcpv6_assignment { ssize_t managed_size; struct ustream_fd managed_sock; + uint32_t leasetime; + uint8_t clid_len; uint8_t clid_data[]; }; diff --git a/src/odhcpd.h b/src/odhcpd.h index fd43dfe..3e8fc3e 100644 --- a/src/odhcpd.h +++ b/src/odhcpd.h @@ -96,6 +96,7 @@ struct lease { struct ether_addr mac; uint16_t duid_len; uint8_t *duid; + uint32_t dhcpv4_leasetime; char hostname[]; }; -- 2.11.0