struct host_hint {
struct avl_node avl;
char *hostname;
- struct in_addr ip;
- struct in6_addr ip6;
+ struct avl_tree ipaddrs;
+ struct avl_tree ip6addrs;
+};
+
+/* used to ignore priority with avl_find_element */
+#define HOST_HINT_PRIO_IGNORE -1
+
+/* higher (larger) priority addresses are listed first */
+#define HOST_HINT_PRIO_NL 10 /* neighbor table */
+#define HOST_HINT_PRIO_ETHER 50 /* /etc/ethers */
+#define HOST_HINT_PRIO_LEASEFILE 100 /* dhcp leasefile */
+#define HOST_HINT_PRIO_RRDNS 100 /* rrdns */
+#define HOST_HINT_PRIO_IFADDRS 200 /* getifaddrs() */
+#define HOST_HINT_PRIO_STATIC_LEASE 200 /* uci static leases */
+
+struct host_hint_addr {
+ struct avl_node avl;
+ int af;
+ int prio;
+ union {
+ struct in_addr in;
+ struct in6_addr in6;
+ } addr;
};
+static int
+host_hint_addr_avl_cmp(const void *k1, const void *k2, void *ptr)
+{
+ struct host_hint_addr *a1 = (struct host_hint_addr *)k1;
+ struct host_hint_addr *a2 = (struct host_hint_addr *)k2;
+
+ if (a1->prio != a2->prio &&
+ a1->prio != HOST_HINT_PRIO_IGNORE &&
+ a2->prio != HOST_HINT_PRIO_IGNORE)
+ return a1->prio < a2->prio ? 1 : -1;
+
+ if (a1->af != a2->af)
+ return a1->af < a2->af ? -1 : 1;
+
+ return memcmp(&a1->addr, &a2->addr, sizeof(a1->addr));
+}
+
static int
nl_cb_done(struct nl_msg *msg, void *arg)
{
return NULL;
hint->avl.key = strcpy(p, mac);
+ avl_init(&hint->ipaddrs, host_hint_addr_avl_cmp, false, NULL);
+ avl_init(&hint->ip6addrs, host_hint_addr_avl_cmp, false, NULL);
avl_insert(&rctx->avl, &hint->avl);
}
return hint;
}
+static void
+rpc_luci_add_host_hint_addr(struct host_hint *hint, int af, int prio, void *addr)
+{
+ struct host_hint_addr e, *a;
+ struct avl_tree *addrs = af == AF_INET ? &hint->ipaddrs : &hint->ip6addrs;
+
+ if (!addr)
+ return;
+
+ memset(&e, 0, sizeof(e));
+ e.af = af;
+ /* ignore prio when comparing against existing addresses */
+ e.prio = HOST_HINT_PRIO_IGNORE;
+
+ if (af == AF_INET)
+ memcpy(&e.addr.in, (struct in_addr *)addr, sizeof(e.addr.in));
+ else
+ memcpy(&e.addr.in6, (struct in6_addr *)addr, sizeof(e.addr.in6));
+
+ a = avl_find_element(addrs, &e, a, avl);
+
+ if (a) {
+ /* update prio of existing address if higher */
+ if (prio <= a->prio)
+ return;
+
+ avl_delete(addrs, &a->avl);
+ a->prio = prio;
+ avl_insert(addrs, &a->avl);
+ return;
+ }
+
+ a = calloc(1, sizeof(*a));
+
+ if (!a)
+ return;
+
+ memcpy(a, &e, sizeof(*a));
+ a->prio = prio;
+ a->avl.key = a;
+ avl_insert(addrs, &a->avl);
+}
+
+static void
+rpc_luci_add_host_hint_ipaddr(struct host_hint *hint, int prio, struct in_addr *addr)
+{
+ return rpc_luci_add_host_hint_addr(hint, AF_INET, prio, (void *)addr);
+}
+
+static void
+rpc_luci_add_host_hint_ip6addr(struct host_hint *hint, int prio, struct in6_addr *addr)
+{
+ return rpc_luci_add_host_hint_addr(hint, AF_INET6, prio, (void *)addr);
+}
+
static int nl_cb_dump_neigh(struct nl_msg *msg, void *arg)
{
struct reply_context *rctx = arg;
return NL_SKIP;
if (nd->ndm_family == AF_INET)
- hint->ip = *(struct in_addr *)dst;
+ rpc_luci_add_host_hint_ipaddr(hint, HOST_HINT_PRIO_NL, (struct in_addr *)dst);
else
- hint->ip6 = *(struct in6_addr *)dst;
+ rpc_luci_add_host_hint_ip6addr(hint, HOST_HINT_PRIO_NL, (struct in6_addr *)dst);
return NL_SKIP;
}
continue;
if (inet_pton(AF_INET, p, &in) == 1) {
- if (hint->ip.s_addr == 0)
- hint->ip = in;
+ rpc_luci_add_host_hint_ipaddr(hint, HOST_HINT_PRIO_ETHER, &in);
}
else if (*p && !hint->hostname) {
hint->hostname = strdup(p);
struct uci_ptr ptr = { .package = "dhcp" };
struct uci_context *uci = NULL;
struct uci_package *pkg = NULL;
- struct in6_addr empty = {};
struct lease_entry *lease;
struct host_hint *hint;
struct uci_element *e, *l;
struct uci_section *s;
struct in_addr in;
char *p, *n;
+ int i;
uci = uci_alloc_context();
if (!hint)
continue;
- if (hint->ip.s_addr == 0 && in.s_addr != 0)
- hint->ip = in;
+ if (in.s_addr != 0)
+ rpc_luci_add_host_hint_ipaddr(hint, HOST_HINT_PRIO_STATIC_LEASE, &in);
if (n && !hint->hostname)
hint->hostname = strdup(n);
if (!hint)
continue;
- if (hint->ip.s_addr == 0 && in.s_addr != 0)
- hint->ip = in;
+ if (in.s_addr != 0)
+ rpc_luci_add_host_hint_ipaddr(hint, HOST_HINT_PRIO_STATIC_LEASE, &in);
if (n && !hint->hostname)
hint->hostname = strdup(n);
if (!hint)
continue;
- if (lease->af == AF_INET && lease->n_addr && hint->ip.s_addr == 0)
- hint->ip = lease->addr[0].in;
- else if (lease->af == AF_INET6 && lease->n_addr &&
- !memcmp(&hint->ip6, &empty, sizeof(empty)))
- hint->ip6 = lease->addr[0].in6;
+ for (i = 0; i < lease->n_addr; i++) {
+ if (lease->af == AF_INET)
+ rpc_luci_add_host_hint_ipaddr(hint, HOST_HINT_PRIO_LEASEFILE, &lease->addr[i].in);
+ else if (lease->af == AF_INET6)
+ rpc_luci_add_host_hint_ip6addr(hint, HOST_HINT_PRIO_LEASEFILE, &lease->addr[i].in6);
+ }
if (lease->hostname && !hint->hostname)
hint->hostname = strdup(lease->hostname);
hint = rpc_luci_get_host_hint(rctx, &device->ea);
if (hint) {
- if (hint->ip.s_addr == 0 && device->in.s_addr != 0)
- hint->ip = device->in;
+ if (device->in.s_addr != 0)
+ rpc_luci_add_host_hint_ipaddr(hint, HOST_HINT_PRIO_IFADDRS, &device->in);
- if (memcmp(&hint->ip6, &empty_in6, sizeof(empty_in6)) == 0 &&
- memcmp(&device->in6, &empty_in6, sizeof(empty_in6)) != 0)
- hint->ip6 = device->in6;
+ if (memcmp(&device->in6, &empty_in6, sizeof(empty_in6)) != 0)
+ rpc_luci_add_host_hint_ip6addr(hint, HOST_HINT_PRIO_IFADDRS, &device->in6);
}
}
if (inet_pton(AF_INET6, blobmsg_name(cur), &in6) == 1) {
avl_for_each_element(&rctx->avl, hint, avl) {
- if (!memcmp(&hint->ip6, &in6, sizeof(in6))) {
+ rpc_luci_add_host_hint_ip6addr(hint, HOST_HINT_PRIO_RRDNS, &in6);
+ if (!avl_is_empty(&hint->ip6addrs)) {
if (hint->hostname)
free(hint->hostname);
}
else if (inet_pton(AF_INET, blobmsg_name(cur), &in) == 1) {
avl_for_each_element(&rctx->avl, hint, avl) {
- if (!memcmp(&hint->ip, &in, sizeof(in))) {
+ rpc_luci_add_host_hint_ipaddr(hint, HOST_HINT_PRIO_RRDNS, &in);
+ if (!avl_is_empty(&hint->ipaddrs)) {
if (hint->hostname)
free(hint->hostname);
char buf[INET6_ADDRSTRLEN];
struct blob_buf req = {};
struct host_hint *hint;
+ struct host_hint_addr *addr;
int n = 0;
void *a;
a = blobmsg_open_array(&req, "addrs");
avl_for_each_element(&rctx->avl, hint, avl) {
- if (hint->ip.s_addr != 0) {
- inet_ntop(AF_INET, &hint->ip, buf, sizeof(buf));
- blobmsg_add_string(&req, NULL, buf);
- n++;
+ avl_for_each_element(&hint->ipaddrs, addr, avl) {
+ if (addr->addr.in.s_addr != 0) {
+ inet_ntop(AF_INET, &addr->addr.in, buf, sizeof(buf));
+ blobmsg_add_string(&req, NULL, buf);
+ n++;
+ }
}
- else if (memcmp(&hint->ip6, &empty_in6, sizeof(empty_in6))) {
- inet_ntop(AF_INET6, &hint->ip6, buf, sizeof(buf));
- blobmsg_add_string(&req, NULL, buf);
- n++;
+ avl_for_each_element(&hint->ip6addrs, addr, avl) {
+ if (memcmp(&addr->addr.in6, &empty_in6, sizeof(empty_in6))) {
+ inet_ntop(AF_INET6, &addr->addr.in6, buf, sizeof(buf));
+ blobmsg_add_string(&req, NULL, buf);
+ n++;
+ }
}
}
rpc_luci_get_host_hints_finish(struct reply_context *rctx)
{
struct host_hint *hint, *nexthint;
+ struct host_hint_addr *addr, *nextaddr;
char buf[INET6_ADDRSTRLEN];
struct in6_addr in6 = {};
- struct in_addr in = {};
- void *o;
+ void *o, *a;
avl_remove_all_elements(&rctx->avl, hint, avl, nexthint) {
o = blobmsg_open_table(&rctx->blob, hint->avl.key);
- if (memcmp(&hint->ip, &in, sizeof(in))) {
- inet_ntop(AF_INET, &hint->ip, buf, sizeof(buf));
- blobmsg_add_string(&rctx->blob, "ipv4", buf);
+ a = blobmsg_open_array(&rctx->blob, "ipaddrs");
+
+ avl_remove_all_elements(&hint->ipaddrs, addr, avl, nextaddr) {
+ if (addr->addr.in.s_addr != 0) {
+ inet_ntop(AF_INET, &addr->addr.in, buf, sizeof(buf));
+ blobmsg_add_string(&rctx->blob, NULL, buf);
+ }
+
+ free(addr);
}
- if (memcmp(&hint->ip6, &in6, sizeof(in6))) {
- inet_ntop(AF_INET6, &hint->ip6, buf, sizeof(buf));
- blobmsg_add_string(&rctx->blob, "ipv6", buf);
+ blobmsg_close_array(&rctx->blob, a);
+
+ a = blobmsg_open_array(&rctx->blob, "ip6addrs");
+
+ avl_remove_all_elements(&hint->ip6addrs, addr, avl, nextaddr) {
+ if (memcmp(&addr->addr.in6, &in6, sizeof(in6))) {
+ inet_ntop(AF_INET6, &addr->addr.in6, buf, sizeof(buf));
+ blobmsg_add_string(&rctx->blob, NULL, buf);
+ }
+
+ free(addr);
}
+ blobmsg_close_array(&rctx->blob, a);
+
if (hint->hostname)
blobmsg_add_string(&rctx->blob, "name", hint->hostname);