From a5195e7825cbbc3942ca6e571ae0020c1cf080c4 Mon Sep 17 00:00:00 2001 From: Niels Widger Date: Mon, 1 Mar 2021 14:33:33 -0500 Subject: [PATCH] rpcd-mod-luci: Return array of addresses in getHostHints Update luci-rpc's getHostHints method to return two string arrays for each host, `ipaddrs` and `ip6addrs`, each containing the host's IPv4 and IPv6 addresses, respectively. Each array is sorted by a priority derived from the source from which the address was discovered. The current address sources and their priority is as follows (a higher (larger) priority is listed first): - neighbor table entries: 10 - /etc/ethers entries: 50 - DHCP leasefile: 100 - RRDNS queries: 100 - getifaddrs(): 200 - UCI static leases: 200 The existing `ipv4` and `ipv6` string fields for each host in `getHostHints` has been removed. Downstream users of getHostHints still need to be updated. Fixes: #4838 Signed-off-by: Niels Widger [squash commits, reformat fixes tag] Signed-off-by: Jo-Philipp Wich --- libs/rpcd-mod-luci/src/luci.c | 195 +++++++++++++++++++++++++++------- 1 file changed, 156 insertions(+), 39 deletions(-) diff --git a/libs/rpcd-mod-luci/src/luci.c b/libs/rpcd-mod-luci/src/luci.c index 131384d76d..030dabb823 100644 --- a/libs/rpcd-mod-luci/src/luci.c +++ b/libs/rpcd-mod-luci/src/luci.c @@ -1135,10 +1135,48 @@ rpc_luci_get_wireless_devices(struct ubus_context *ctx, 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) { @@ -1174,12 +1212,69 @@ rpc_luci_get_host_hint(struct reply_context *rctx, struct ether_addr *ea) 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; @@ -1213,9 +1308,9 @@ static int nl_cb_dump_neigh(struct nl_msg *msg, void *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; } @@ -1298,8 +1393,7 @@ rpc_luci_get_host_hints_ether(struct reply_context *rctx) 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); @@ -1315,13 +1409,13 @@ rpc_luci_get_host_hints_uci(struct reply_context *rctx) 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(); @@ -1379,8 +1473,8 @@ rpc_luci_get_host_hints_uci(struct reply_context *rctx) 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); @@ -1393,8 +1487,8 @@ rpc_luci_get_host_hints_uci(struct reply_context *rctx) 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); @@ -1410,11 +1504,12 @@ rpc_luci_get_host_hints_uci(struct reply_context *rctx) 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); @@ -1493,12 +1588,11 @@ rpc_luci_get_host_hints_ifaddrs(struct reply_context *rctx) 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); } } @@ -1527,7 +1621,8 @@ rpc_luci_get_host_hints_rrdns_cb(struct ubus_request *req, int type, 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); @@ -1538,7 +1633,8 @@ rpc_luci_get_host_hints_rrdns_cb(struct ubus_request *req, int type, } 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); @@ -1560,6 +1656,7 @@ rpc_luci_get_host_hints_rrdns(struct reply_context *rctx) char buf[INET6_ADDRSTRLEN]; struct blob_buf req = {}; struct host_hint *hint; + struct host_hint_addr *addr; int n = 0; void *a; @@ -1568,15 +1665,19 @@ rpc_luci_get_host_hints_rrdns(struct reply_context *rctx) 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++; + } } } @@ -1601,24 +1702,40 @@ static int 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); -- 2.30.2