+struct addr_info {
+ int ifindex;
+ int af;
+ struct odhcpd_ipaddr **addrs;
+ int pending;
+ ssize_t ret;
+};
+
+static int cb_valid_handler(struct nl_msg *msg, void *arg)
+{
+ struct addr_info *ctxt = (struct addr_info *)arg;
+ struct odhcpd_ipaddr *addrs = *(ctxt->addrs);
+ struct nlmsghdr *hdr = nlmsg_hdr(msg);
+ struct ifaddrmsg *ifa;
+ struct nlattr *nla[__IFA_MAX], *nla_addr = NULL;
+
+ if (hdr->nlmsg_type != RTM_NEWADDR)
+ return NL_SKIP;
+
+ ifa = NLMSG_DATA(hdr);
+ if (ifa->ifa_scope != RT_SCOPE_UNIVERSE ||
+ (ctxt->af != ifa->ifa_family) ||
+ (ctxt->ifindex && ifa->ifa_index != (unsigned)ctxt->ifindex))
+ return NL_SKIP;
+
+ nlmsg_parse(hdr, sizeof(*ifa), nla, __IFA_MAX - 1, NULL);
+
+ switch (ifa->ifa_family) {
+ case AF_INET6:
+ if (nla[IFA_ADDRESS])
+ nla_addr = nla[IFA_ADDRESS];
+ break;
+
+ case AF_INET:
+ if (nla[IFA_LOCAL])
+ nla_addr = nla[IFA_LOCAL];
+ break;
+
+ default:
+ break;
+ }
+ if (!nla_addr)
+ return NL_SKIP;
+
+ addrs = realloc(addrs, sizeof(*addrs)*(ctxt->ret + 1));
+ if (!addrs)
+ return NL_SKIP;
+
+ memset(&addrs[ctxt->ret], 0, sizeof(addrs[ctxt->ret]));
+ addrs[ctxt->ret].prefix = ifa->ifa_prefixlen;
+
+ nla_memcpy(&addrs[ctxt->ret].addr, nla_addr,
+ sizeof(addrs[ctxt->ret].addr));
+
+ if (nla[IFA_BROADCAST])
+ nla_memcpy(&addrs[ctxt->ret].broadcast, nla[IFA_BROADCAST],
+ sizeof(addrs[ctxt->ret].broadcast));
+
+ if (nla[IFA_CACHEINFO]) {
+ struct ifa_cacheinfo *ifc = nla_data(nla[IFA_CACHEINFO]);
+
+ addrs[ctxt->ret].preferred = ifc->ifa_prefered;
+ addrs[ctxt->ret].valid = ifc->ifa_valid;
+ }
+
+ if (ifa->ifa_flags & IFA_F_DEPRECATED)
+ addrs[ctxt->ret].preferred = 0;
+
+ ctxt->ret++;
+ *(ctxt->addrs) = addrs;
+
+ return NL_OK;
+}
+
+static int cb_finish_handler(_unused struct nl_msg *msg, void *arg)
+{
+ struct addr_info *ctxt = (struct addr_info *)arg;
+
+ ctxt->pending = 0;
+
+ return NL_STOP;
+}
+
+static int cb_error_handler(_unused struct sockaddr_nl *nla, struct nlmsgerr *err,
+ void *arg)
+{
+ struct addr_info *ctxt = (struct addr_info *)arg;
+
+ ctxt->pending = 0;
+ ctxt->ret = err->error;
+
+ return NL_STOP;
+}
+
+static int prefix_cmp(const void *va, const void *vb)
+{
+ const struct odhcpd_ipaddr *a = va, *b = vb;
+ return (ntohl(a->addr.in.s_addr) < ntohl(b->addr.in.s_addr)) ? 1 :
+ (ntohl(a->addr.in.s_addr) > ntohl(b->addr.in.s_addr)) ? -1 : 0;
+}
+
+// compare IPv6 prefixes
+static int prefix6_cmp(const void *va, const void *vb)
+{
+ const struct odhcpd_ipaddr *a = va, *b = vb;
+ uint32_t a_pref = IN6_IS_ADDR_ULA(&a->addr.in6) ? 1 : a->preferred;
+ uint32_t b_pref = IN6_IS_ADDR_ULA(&b->addr.in6) ? 1 : b->preferred;
+ return (a_pref < b_pref) ? 1 : (a_pref > b_pref) ? -1 : 0;
+}