+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;
+}
+