X-Git-Url: http://git.openwrt.org/?p=project%2Fodhcpd.git;a=blobdiff_plain;f=src%2Fconfig.c;h=64639c91c43c46648205e8b9e80c2b59fb82279c;hp=9ffe1ac14123b2c0d64882d2fa61ea4093676d50;hb=65a9519fb295d8f5748fc69962be1666985011fe;hpb=8f498044938e6ae48a49bc441a21195dd889e632 diff --git a/src/config.c b/src/config.c index 9ffe1ac..64639c9 100644 --- a/src/config.c +++ b/src/config.c @@ -12,13 +12,22 @@ #include #include #include +#include +#include +#include +#include #include "odhcpd.h" static struct blob_buf b; static int reload_pipe[2]; -struct list_head leases = LIST_HEAD_INIT(leases); -struct list_head interfaces = LIST_HEAD_INIT(interfaces); + +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, .log_level = LOG_INFO}; @@ -45,6 +54,8 @@ enum { IFACE_ATTR_DHCPV4_FORCERECONF, IFACE_ATTR_DHCPV6_RAW, IFACE_ATTR_DHCPV6_ASSIGNALL, + IFACE_ATTR_DHCPV6_PD, + IFACE_ATTR_DHCPV6_NA, IFACE_ATTR_RA_DEFAULT, IFACE_ATTR_RA_MANAGEMENT, IFACE_ATTR_RA_OFFLINK, @@ -58,6 +69,7 @@ enum { IFACE_ATTR_RA_RETRANSTIME, IFACE_ATTR_RA_HOPLIMIT, IFACE_ATTR_RA_MTU, + IFACE_ATTR_RA_DNS, IFACE_ATTR_PD_MANAGER, IFACE_ATTR_PD_CER, IFACE_ATTR_NDPROXY_ROUTING, @@ -88,6 +100,8 @@ static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = { [IFACE_ATTR_DHCPV4_FORCERECONF] = { .name = "dhcpv4_forcereconf", .type = BLOBMSG_TYPE_BOOL }, [IFACE_ATTR_DHCPV6_RAW] = { .name = "dhcpv6_raw", .type = BLOBMSG_TYPE_STRING }, [IFACE_ATTR_DHCPV6_ASSIGNALL] = { .name ="dhcpv6_assignall", .type = BLOBMSG_TYPE_BOOL }, + [IFACE_ATTR_DHCPV6_PD] = { .name = "dhcpv6_pd", .type = BLOBMSG_TYPE_BOOL }, + [IFACE_ATTR_DHCPV6_NA] = { .name = "dhcpv6_na", .type = BLOBMSG_TYPE_BOOL }, [IFACE_ATTR_PD_MANAGER] = { .name = "pd_manager", .type = BLOBMSG_TYPE_STRING }, [IFACE_ATTR_PD_CER] = { .name = "pd_cer", .type = BLOBMSG_TYPE_STRING }, [IFACE_ATTR_RA_DEFAULT] = { .name = "ra_default", .type = BLOBMSG_TYPE_INT32 }, @@ -103,6 +117,7 @@ static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = { [IFACE_ATTR_RA_RETRANSTIME] = { .name = "ra_retranstime", .type = BLOBMSG_TYPE_INT32 }, [IFACE_ATTR_RA_HOPLIMIT] = { .name = "ra_hoplimit", .type = BLOBMSG_TYPE_INT32 }, [IFACE_ATTR_RA_MTU] = { .name = "ra_mtu", .type = BLOBMSG_TYPE_INT32 }, + [IFACE_ATTR_RA_DNS] = { .name = "ra_dns", .type = BLOBMSG_TYPE_BOOL }, [IFACE_ATTR_NDPROXY_ROUTING] = { .name = "ndproxy_routing", .type = BLOBMSG_TYPE_BOOL }, [IFACE_ATTR_NDPROXY_SLAVE] = { .name = "ndproxy_slave", .type = BLOBMSG_TYPE_BOOL }, [IFACE_ATTR_PREFIX_FILTER] = { .name = "prefix_filter", .type = BLOBMSG_TYPE_STRING }, @@ -191,33 +206,18 @@ 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 struct interface* get_interface(const char *name) -{ - struct interface *c; - list_for_each_entry(c, &interfaces, head) - if (!strcmp(c->name, name)) - return c; - return NULL; -} - static void set_interface_defaults(struct interface *iface) { iface->learn_routes = 1; iface->dhcpv4_leasetime = 43200; iface->dhcpv6_assignall = true; + iface->dhcpv6_pd = true; + iface->dhcpv6_na = true; iface->ra_managed = RA_MANAGED_MFLAG; iface->ra_maxinterval = 600; iface->ra_mininterval = iface->ra_maxinterval/3; iface->ra_lifetime = -1; + iface->ra_dns = true; } static void clean_interface(struct interface *iface) @@ -235,8 +235,7 @@ static void clean_interface(struct interface *iface) static void close_interface(struct interface *iface) { - if (iface->head.next) - list_del(&iface->head); + avl_delete(&interfaces, &iface->avl); router_setup_interface(iface, false); dhcpv6_setup_interface(iface, false); @@ -328,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])) { @@ -385,21 +392,23 @@ 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; } int config_parse_interface(void *data, size_t len, const char *name, bool overwrite) { + struct interface *iface; struct blob_attr *tb[IFACE_ATTR_MAX], *c; bool get_addrs = false; @@ -411,19 +420,24 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr if (!name) return -1; - struct interface *iface = get_interface(name); + iface = avl_find_element(&interfaces, name, iface, avl); if (!iface) { - char *iface_name; + char *new_name; - iface = calloc_a(sizeof(*iface), &iface_name, strlen(name) + 1); + iface = calloc_a(sizeof(*iface), &new_name, strlen(name) + 1); if (!iface) return -1; - iface->name = strcpy(iface_name, name); - + iface->name = strcpy(new_name, name); + iface->avl.key = iface->name; + iface->router_event.uloop.fd = -1; + iface->dhcpv6_event.uloop.fd = -1; + iface->ndp_event.uloop.fd = -1; + iface->ndp_ping_fd = -1; + iface->dhcpv4_event.uloop.fd = -1; set_interface_defaults(iface); - list_add(&iface->head, &interfaces); + avl_insert(&interfaces, &iface->avl); get_addrs = overwrite = true; } @@ -648,6 +662,12 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr if ((c = tb[IFACE_ATTR_DHCPV6_ASSIGNALL])) iface->dhcpv6_assignall = blobmsg_get_bool(c); + if ((c = tb[IFACE_ATTR_DHCPV6_PD])) + iface->dhcpv6_pd = blobmsg_get_bool(c); + + if ((c = tb[IFACE_ATTR_DHCPV6_NA])) + iface->dhcpv6_na = blobmsg_get_bool(c); + if ((c = tb[IFACE_ATTR_RA_DEFAULT])) iface->default_router = blobmsg_get_u32(c); @@ -704,6 +724,9 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr if ((c = tb[IFACE_ATTR_RA_USELEASETIME])) iface->ra_useleasetime = blobmsg_get_bool(c); + if ((c = tb[IFACE_ATTR_RA_DNS])) + iface->ra_dns = blobmsg_get_bool(c); + if ((c = tb[IFACE_ATTR_RA_PREFERENCE])) { const char *prio = blobmsg_get_string(c); @@ -764,19 +787,168 @@ static int set_interface(struct uci_section *s) return config_parse_interface(blob_data(b.head), blob_len(b.head), s->e.name, true); } -void odhcpd_reload(void) +static void lease_delete_assignments(struct lease *l, bool v6) { - struct uci_context *uci = uci_alloc_context(); + 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) + 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; - while (!list_empty(&leases)) - free_lease(list_first_entry(&leases, struct lease, head)); + 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; - struct interface *master = NULL, *i, *n; + 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) + 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; if (!uci) return; - list_for_each_entry(i, &interfaces, head) + vlist_update(&leases); + avl_for_each_element(&interfaces, i, avl) clean_interface(i); struct uci_package *dhcp = NULL; @@ -804,6 +976,8 @@ void odhcpd_reload(void) free(path); } + vlist_flush(&leases); + #ifdef WITH_UBUS ubus_apply_network(); #endif @@ -811,7 +985,7 @@ void odhcpd_reload(void) bool any_dhcpv6_slave = false, any_ra_slave = false, any_ndp_slave = false; /* Test for */ - list_for_each_entry(i, &interfaces, head) { + avl_for_each_element(&interfaces, i, avl) { if (i->master) continue; @@ -826,7 +1000,7 @@ void odhcpd_reload(void) } /* Evaluate hybrid mode for master */ - list_for_each_entry(i, &interfaces, head) { + avl_for_each_element(&interfaces, i, avl) { if (!i->master) continue; @@ -859,7 +1033,7 @@ void odhcpd_reload(void) } - list_for_each_entry_safe(i, n, &interfaces, head) { + avl_for_each_element_safe(&interfaces, i, avl, tmp) { if (i->inuse) { /* Resolve hybrid mode */ if (i->dhcpv6 == MODE_HYBRID) @@ -926,8 +1100,4 @@ void odhcpd_run(void) odhcpd_reload(); uloop_run(); - - while (!list_empty(&interfaces)) - close_interface(list_first_entry(&interfaces, struct interface, head)); } -