project(odhcpd C)
set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
-set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -std=c99")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -std=gnu99")
FIND_PATH(ubox_include_dir libubox/uloop.h)
FIND_PATH(libnl-tiny_include_dir netlink-generic.h PATH_SUFFIXES libnl-tiny)
#include <uci.h>
#include <uci_blob.h>
#include <libubox/utils.h>
+#include <libubox/avl.h>
+#include <libubox/avl-cmp.h>
#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);
+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};
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;
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);
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;
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;
set_interface_defaults(iface);
- list_add(&iface->head, &interfaces);
+ avl_insert(&interfaces, &iface->avl);
get_addrs = overwrite = true;
}
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));
- struct interface *master = NULL, *i, *n;
-
if (!uci)
return;
- list_for_each_entry(i, &interfaces, head)
+ avl_for_each_element(&interfaces, i, avl)
clean_interface(i);
struct uci_package *dhcp = NULL;
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;
}
/* Evaluate hybrid mode for master */
- list_for_each_entry(i, &interfaces, head) {
+ avl_for_each_element(&interfaces, i, avl) {
if (!i->master)
continue;
}
- 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)
odhcpd_reload();
uloop_run();
-
- while (!list_empty(&interfaces))
- close_interface(list_first_entry(&interfaces, struct interface, head));
}
static void valid_until_cb(struct uloop_timeout *event)
{
- time_t now = odhcpd_time();
struct interface *iface;
- list_for_each_entry(iface, &interfaces, head) {
+ time_t now = odhcpd_time();
+
+ avl_for_each_element(&interfaces, iface, avl) {
if (iface->dhcpv4 != MODE_SERVER || iface->dhcpv4_assignments.next == NULL)
continue;
ctxt.buf = leasebuf;
ctxt.buf_len = sizeof(leasebuf);
- list_for_each_entry(ctxt.iface, &interfaces, head) {
+ avl_for_each_element(&interfaces, ctxt.iface, avl) {
if (ctxt.iface->dhcpv6 != MODE_SERVER &&
ctxt.iface->dhcpv4 != MODE_SERVER)
continue;
static void valid_until_cb(struct uloop_timeout *event)
{
- time_t now = odhcpd_time();
struct interface *iface;
- list_for_each_entry(iface, &interfaces, head) {
+ time_t now = odhcpd_time();
+
+ avl_for_each_element(&interfaces, iface, avl) {
+ struct dhcpv6_assignment *a, *n;
+
if (iface->dhcpv6 != MODE_SERVER || iface->ia_assignments.next == NULL)
continue;
- struct dhcpv6_assignment *a, *n;
list_for_each_entry_safe(a, n, &iface->ia_assignments, head) {
if (!INFINITE_VALID(a->valid_until) && a->valid_until < now) {
if ((a->length < 128 && a->clid_len > 0) ||
struct ip6_hdr *ip6 = data;
struct nd_neighbor_solicit *req = (struct nd_neighbor_solicit*)&ip6[1];
struct sockaddr_ll *ll = addr;
+ struct interface *c;
char ipbuf[INET6_ADDRSTRLEN];
uint8_t mac[6];
if (!memcmp(ll->sll_addr, mac, sizeof(mac)))
return; /* Looped back */
- struct interface *c;
- list_for_each_entry(c, &interfaces, head)
+ avl_for_each_element(&interfaces, c, avl) {
if (iface != c && c->ndp == MODE_RELAY &&
(ns_is_dad || !c->external))
ping6(&req->nd_ns_target, c);
+ }
}
/* Use rtnetlink to modify kernel routes */
inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf));
- list_for_each_entry(c, &interfaces, head) {
+ avl_for_each_element(&interfaces, c, avl) {
if (iface == c || (c->ndp != MODE_RELAY && !add))
continue;
struct interface* odhcpd_get_interface_by_index(int ifindex)
{
struct interface *iface;
- list_for_each_entry(iface, &interfaces, head)
+
+ avl_for_each_element(&interfaces, iface, avl) {
if (iface->ifindex == ifindex)
return iface;
+ }
return NULL;
}
struct interface* odhcpd_get_interface_by_name(const char *name)
{
struct interface *iface;
- list_for_each_entry(iface, &interfaces, head)
+
+ avl_for_each_element(&interfaces, iface, avl) {
if (!strcmp(iface->ifname, name))
return iface;
+ }
return NULL;
}
struct interface* odhcpd_get_master_interface(void)
{
struct interface *iface;
- list_for_each_entry(iface, &interfaces, head)
+
+ avl_for_each_element(&interfaces, iface, avl) {
if (iface->master)
return iface;
+ }
return NULL;
}
return;
} else if (destiface != 0) {
struct interface *iface;
- list_for_each_entry(iface, &interfaces, head) {
+
+ avl_for_each_element(&interfaces, iface, avl) {
if (iface->ifindex != destiface)
continue;
#include <libubox/blobmsg.h>
#include <libubox/list.h>
#include <libubox/uloop.h>
+#include <libubox/avl.h>
// RFC 6106 defines this router advertisement option
#define ND_OPT_ROUTE_INFO 24
struct interface {
- struct list_head head;
+ struct avl_node avl;
int ifindex;
char *ifname;
char *filter_class;
};
-extern struct list_head interfaces;
+extern struct avl_tree interfaces;
#define RA_MANAGED_NO_MFLAG 0
#define RA_MANAGED_MFLAG 1
if (info->rt.dst_len)
break;
- list_for_each_entry(iface, &interfaces, head) {
+ avl_for_each_element(&interfaces, iface, avl) {
if (iface->ra == MODE_SERVER && !iface->master)
uloop_timeout_set(&iface->timer_rs, 1000);
}
{
struct nd_router_advert *adv = (struct nd_router_advert *)data;
struct sockaddr_in6 all_nodes;
-
+ struct icmpv6_opt *opt;
+ struct interface *c;
+ struct iovec iov = { .iov_base = data, .iov_len = len };
/* Rewrite options */
uint8_t *end = data + len;
uint8_t *mac_ptr = NULL;
struct in6_addr *dns_ptr = NULL;
size_t dns_count = 0;
- struct icmpv6_opt *opt;
icmpv6_for_each_option(opt, &adv[1], end) {
if (opt->type == ND_OPT_SOURCE_LINKADDR) {
/* Store address of source MAC-address */
all_nodes.sin6_family = AF_INET6;
inet_pton(AF_INET6, ALL_IPV6_NODES, &all_nodes.sin6_addr);
- struct iovec iov = {data, len};
-
- struct interface *c;
- list_for_each_entry(c, &interfaces, head) {
+ avl_for_each_element(&interfaces, c, avl) {
if (c->ra != MODE_RELAY || c->master)
continue;
blob_buf_init(&b, 0);
a = blobmsg_open_table(&b, "device");
- list_for_each_entry(iface, &interfaces, head) {
+ avl_for_each_element(&interfaces, iface, avl) {
if (iface->dhcpv4 != MODE_SERVER || iface->dhcpv4_assignments.next == NULL)
continue;
blob_buf_init(&b, 0);
a = blobmsg_open_table(&b, "device");
- list_for_each_entry(iface, &interfaces, head) {
+ avl_for_each_element(&interfaces, iface, avl) {
if (iface->dhcpv6 != MODE_SERVER || iface->ia_assignments.next == NULL)
continue;
struct blob_attr *msg)
{
struct blob_attr *tb[IFACE_ATTR_MAX];
- blobmsg_parse(iface_attrs, IFACE_ATTR_MAX, tb, blob_data(msg), blob_len(msg));
+ struct interface *c;
+ bool update = false;
+ blobmsg_parse(iface_attrs, IFACE_ATTR_MAX, tb, blob_data(msg), blob_len(msg));
const char *interface = (tb[IFACE_ATTR_INTERFACE]) ?
blobmsg_get_string(tb[IFACE_ATTR_INTERFACE]) : "";
- const char *ifname = (tb[IFACE_ATTR_IFNAME]) ?
- blobmsg_get_string(tb[IFACE_ATTR_IFNAME]) : "";
- struct interface *c, *iface = NULL;
- list_for_each_entry(c, &interfaces, head)
- if (!strcmp(interface, c->name) || !strcmp(ifname, c->ifname))
- iface = c;
+ avl_for_each_element(&interfaces, c, avl) {
+ if (!strcmp(interface, c->name) && !c->ignore) {
+ update = true;
+ break;
+ }
+ }
- if (iface && iface->ignore)
- return 0;
+ if (update)
+ update_netifd(false);
- update_netifd(false);
return 0;
}
const char *interface = (tb[IFACE_ATTR_INTERFACE]) ?
blobmsg_get_string(tb[IFACE_ATTR_INTERFACE]) : "";
- const char *ifname = (tb[IFACE_ATTR_IFNAME]) ?
- blobmsg_get_string(tb[IFACE_ATTR_IFNAME]) : "";
bool matched = false;
- struct interface *c, *n;
- list_for_each_entry_safe(c, n, &interfaces, head) {
+ struct interface *c, *tmp;
+ avl_for_each_element_safe(&interfaces, c, avl, tmp) {
char *f = memmem(c->upstream, c->upstream_len,
interface, strlen(interface) + 1);
- bool cmatched = !strcmp(interface, c->name) || !strcmp(ifname, c->ifname);
+ bool cmatched = !strcmp(interface, c->name);
matched |= cmatched;
if (!cmatched && (!c->upstream_len || !f || (f != c->upstream && f[-1] != 0)))