summaryrefslogtreecommitdiffhomepage
path: root/src/config.c
diff options
context:
space:
mode:
authorHans Dedecker <dedeckeh@gmail.com>2019-02-12 16:07:24 +0100
committerHans Dedecker <dedeckeh@gmail.com>2019-02-18 11:43:09 +0100
commitca8ba91c757b1559bc6391707547d54477c8315a (patch)
tree72ec738d9c4cc531b15a001378e795d24062232d /src/config.c
parent36833ea26684b70b0d6093ecec648fe192a582ec (diff)
dhcp: rework static lease logic
Rework the static lease logic as the existing logic had different issues. Static leases are now added in a vlist tree which makes it easier to handle static lease config changes. For both DHCPv4 and DHCPv6 static assignments are now created upon the receival of DHCPv4/DHCPv6 messages as before the static assignment was created even if the client was not physically present. In case a hostname is specified in a static lease it won't be overriden anymore by the hostname received from the client. Signed-off-by: Hans Dedecker <dedeckeh@gmail.com>
Diffstat (limited to 'src/config.c')
-rw-r--r--src/config.c246
1 files changed, 204 insertions, 42 deletions
diff --git a/src/config.c b/src/config.c
index f4a0dcf..d857f24 100644
--- a/src/config.c
+++ b/src/config.c
@@ -14,12 +14,19 @@
#include <libubox/utils.h>
#include <libubox/avl.h>
#include <libubox/avl-cmp.h>
+#include <libubox/list.h>
+#include <libubox/vlist.h>
#include "odhcpd.h"
static struct blob_buf b;
static int reload_pipe[2];
-struct list_head leases = LIST_HEAD_INIT(leases);
+
+static int lease_cmp(const void *k1, const void *k2, void *ptr);
+static void lease_update(struct vlist_tree *tree, struct vlist_node *node_new,
+ struct vlist_node *node_old);
+
+struct vlist_tree leases = VLIST_TREE_INIT(leases, lease_cmp, lease_update, true, false);
AVL_TREE(interfaces, avl_strcmp, false, NULL);
struct config config = {.legacy = false, .main_dhcpv4 = false,
.dhcp_cb = NULL, .dhcp_statefile = NULL,
@@ -199,15 +206,6 @@ static int mkdir_p(char *dir, mode_t mask)
return ret;
}
-static void free_lease(struct lease *l)
-{
- if (l->head.next)
- list_del(&l->head);
-
- free(l->duid);
- free(l);
-}
-
static void set_interface_defaults(struct interface *iface)
{
iface->learn_routes = 1;
@@ -329,56 +327,64 @@ err:
return -1;
}
+static void free_lease(struct lease *l)
+{
+ free(l->hostname);
+ free(l);
+}
+
static int set_lease(struct uci_section *s)
{
struct blob_attr *tb[LEASE_ATTR_MAX], *c;
+ struct lease *l;
+ size_t duidlen = 0;
+ uint8_t *duid;
blob_buf_init(&b, 0);
uci_to_blob(&b, s, &lease_attr_list);
blobmsg_parse(lease_attrs, LEASE_ATTR_MAX, tb, blob_data(b.head), blob_len(b.head));
- size_t hostlen = 1;
- if ((c = tb[LEASE_ATTR_NAME]))
- hostlen = blobmsg_data_len(c);
+ if ((c = tb[LEASE_ATTR_DUID]))
+ duidlen = (blobmsg_data_len(c) - 1) / 2;
- struct lease *lease = calloc(1, sizeof(*lease) + hostlen);
- if (!lease)
+ l = calloc_a(sizeof(*l), &duid, duidlen);
+ if (!l)
goto err;
- if (hostlen > 1) {
- memcpy(lease->hostname, blobmsg_get_string(c), hostlen);
- if (!odhcpd_valid_hostname(lease->hostname))
- goto err;
- }
-
- if ((c = tb[LEASE_ATTR_IP]))
- if (inet_pton(AF_INET, blobmsg_get_string(c), &lease->ipaddr) < 0)
- goto err;
-
if ((c = tb[LEASE_ATTR_MAC]))
- if (!ether_aton_r(blobmsg_get_string(c), &lease->mac))
+ if (!ether_aton_r(blobmsg_get_string(c), &l->mac))
goto err;
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;
- ssize_t len = odhcpd_unhexlify(lease->duid,
- duidlen, blobmsg_get_string(c));
+ l->duid = duid;
+ len = odhcpd_unhexlify(l->duid, duidlen, blobmsg_get_string(c));
if (len < 0)
goto err;
- lease->duid_len = len;
+ l->duid_len = len;
+ }
+
+ if ((c = tb[LEASE_ATTR_NAME])) {
+ l->hostname = strdup(blobmsg_get_string(c));
+ if (!l->hostname || !odhcpd_valid_hostname(l->hostname))
+ goto err;
}
+ if ((c = tb[LEASE_ATTR_IP]))
+ if (inet_pton(AF_INET, blobmsg_get_string(c), &l->ipaddr) < 0)
+ goto err;
+
if ((c = tb[LEASE_ATTR_HOSTID])) {
errno = 0;
- lease->hostid = strtoul(blobmsg_get_string(c), NULL, 16);
+ l->hostid = strtoul(blobmsg_get_string(c), NULL, 16);
if (errno)
goto err;
+ } else {
+ uint32_t i4a = ntohl(l->ipaddr) & 0xff;
+ l->hostid = ((i4a / 100) << 8) | (((i4a % 100) / 10) << 4) | (i4a % 10);
}
if ((c = tb[LEASE_ATTR_LEASETIME])) {
@@ -386,15 +392,16 @@ static int set_lease(struct uci_section *s)
if (time < 0)
goto err;
- lease->dhcpv4_leasetime = time;
+ l->leasetime = time;
}
- list_add(&lease->head, &leases);
+ INIT_LIST_HEAD(&l->assignments);
+ vlist_add(&leases, &l->node, l);
return 0;
err:
- if (lease)
- free_lease(lease);
+ if (l)
+ free_lease(l);
return -1;
}
@@ -775,17 +782,171 @@ static int set_interface(struct uci_section *s)
return config_parse_interface(blob_data(b.head), blob_len(b.head), s->e.name, true);
}
+static void lease_delete_assignments(struct lease *l, bool v6)
+{
+ struct dhcp_assignment *a, *tmp;
+ unsigned int flag = v6 ? OAF_DHCPV6 : OAF_DHCPV4;
+
+ list_for_each_entry_safe(a, tmp, &l->assignments, lease_list) {
+ if (a->flags & flag)
+ v6 ? dhcpv6_ia_free_assignment(a) : dhcpv4_free_assignment(a);
+ }
+}
+
+static void lease_update_assignments(struct lease *l)
+{
+ struct dhcp_assignment *a;
+
+ list_for_each_entry(a, &l->assignments, lease_list) {
+ if (a->hostname)
+ free(a->hostname);
+ a->hostname = NULL;
+
+ if (l->hostname)
+ a->hostname = strdup(l->hostname);
+
+ a->leasetime = l->leasetime;
+ }
+}
+
+static int lease_cmp(const void *k1, const void *k2, _unused void *ptr)
+{
+ const struct lease *l1 = k1, *l2 = k2;
+ int cmp = 0;
+
+ if (l1->duid_len != l2->duid_len)
+ return l1->duid_len - l2->duid_len;
+
+ if (l1->duid_len && l2->duid_len)
+ cmp = memcmp(l1->duid, l2->duid, l1->duid_len);
+
+ if (cmp)
+ return cmp;
+
+ return memcmp(l1->mac.ether_addr_octet, l2->mac.ether_addr_octet,
+ sizeof(l1->mac.ether_addr_octet));
+}
+
+static void lease_change_config(struct lease *l_old, struct lease *l_new)
+{
+ bool update = false;
+
+ if ((!!l_new->hostname != !!l_old->hostname) ||
+ (l_new->hostname && strcmp(l_new->hostname, l_old->hostname))) {
+ free(l_old->hostname);
+ l_old->hostname = NULL;
+
+ if (l_new->hostname)
+ l_old->hostname = strdup(l_new->hostname);
+
+ update = true;
+ }
+
+ if (l_old->leasetime != l_new->leasetime) {
+ l_old->leasetime = l_new->leasetime;
+ update = true;
+ }
+
+ if (l_old->ipaddr != l_new->ipaddr) {
+ l_old->ipaddr = l_new->ipaddr;
+ lease_delete_assignments(l_old, false);
+ }
+
+ if (l_old->hostid != l_new->hostid) {
+ l_old->hostid = l_new->hostid;
+ lease_delete_assignments(l_old, true);
+ }
+
+ if (update)
+ lease_update_assignments(l_old);
+
+ free_lease(l_new);
+}
+
+static void lease_delete(struct lease *l)
+{
+ struct dhcp_assignment *a;
+
+ list_for_each_entry(a, &l->assignments, lease_list) {
+ if (a->flags & OAF_DHCPV6)
+ dhcpv6_ia_free_assignment(a);
+ else if (a->flags & OAF_DHCPV4)
+ dhcpv4_free_assignment(a);
+ }
+
+ free_lease(l);
+}
+
+static void lease_update(_unused struct vlist_tree *tree, struct vlist_node *node_new,
+ struct vlist_node *node_old)
+{
+ struct lease *lease_new = container_of(node_new, struct lease, node);
+ struct lease *lease_old = container_of(node_old, struct lease, node);
+
+ if (node_old && node_new)
+ lease_change_config(lease_old, lease_new);
+ else if (node_old)
+ lease_delete(lease_old);
+}
+
+struct lease *config_find_lease_by_duid(const uint8_t *duid, const uint16_t len)
+{
+ struct lease *l;
+
+ vlist_for_each_element(&leases, l, node) {
+ if (l->duid_len == len && !memcmp(l->duid, duid, len))
+ return l;
+ }
+
+ return NULL;
+}
+
+struct lease *config_find_lease_by_mac(const uint8_t *mac)
+{
+ struct lease *l;
+
+ vlist_for_each_element(&leases, l, node) {
+ if (!memcmp(l->mac.ether_addr_octet, mac,
+ sizeof(l->mac.ether_addr_octet)))
+ return l;
+ }
+
+ return NULL;
+}
+
+struct lease *config_find_lease_by_hostid(const uint32_t hostid)
+{
+ struct lease *l;
+
+ vlist_for_each_element(&leases, l, node) {
+ if (l->hostid == hostid)
+ return l;
+ }
+
+ return NULL;
+}
+
+struct lease *config_find_lease_by_ipaddr(const uint32_t ipaddr)
+{
+ struct lease *l;
+
+ vlist_for_each_element(&leases, l, node) {
+ if (l->ipaddr == ipaddr)
+ return l;
+ }
+
+ return NULL;
+}
+
void odhcpd_reload(void)
{
struct uci_context *uci = uci_alloc_context();
struct interface *master = NULL, *i, *tmp;
- while (!list_empty(&leases))
- free_lease(list_first_entry(&leases, struct lease, head));
-
if (!uci)
return;
+ vlist_update(&leases);
avl_for_each_element(&interfaces, i, avl)
clean_interface(i);
@@ -814,6 +975,8 @@ void odhcpd_reload(void)
free(path);
}
+ vlist_flush(&leases);
+
#ifdef WITH_UBUS
ubus_apply_network();
#endif
@@ -937,4 +1100,3 @@ void odhcpd_run(void)
odhcpd_reload();
uloop_run();
}
-