-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];
-
- 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);
- if (!nla[IFA_ADDRESS])
- 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[IFA_ADDRESS],
- sizeof(addrs[ctxt->ret].addr));
-
- 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;
-}
-
-// compare prefixes
-static int prefixcmp(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;
-}
-
-// Detect an IPV6-address currently assigned to the given interface
-ssize_t odhcpd_get_interface_addresses(int ifindex, bool v6, struct odhcpd_ipaddr **addrs)
-{
- struct nl_msg *msg;
- struct ifaddrmsg ifa = {
- .ifa_family = v6? AF_INET6: AF_INET,
- .ifa_prefixlen = 0,
- .ifa_flags = 0,
- .ifa_scope = 0,
- .ifa_index = ifindex, };
- struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT);
- struct addr_info ctxt = {
- .ifindex = ifindex,
- .af = v6? AF_INET6: AF_INET,
- .addrs = addrs,
- .ret = 0,
- .pending = 1,
- };
-
- if (!cb) {
- ctxt.ret = -1;
- goto out;
- }
-
- msg = nlmsg_alloc_simple(RTM_GETADDR, NLM_F_REQUEST | NLM_F_DUMP);
-
- if (!msg) {
- ctxt.ret = - 1;
- goto out;
- }
-
- nlmsg_append(msg, &ifa, sizeof(ifa), 0);
-
- nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_valid_handler, &ctxt);
- nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_finish_handler, &ctxt);
- nl_cb_err(cb, NL_CB_CUSTOM, cb_error_handler, &ctxt);
-
- nl_send_auto_complete(rtnl_socket, msg);
- while (ctxt.pending > 0)
- nl_recvmsgs(rtnl_socket, cb);
-
- nlmsg_free(msg);
-
- if (ctxt.ret <= 0)
- goto out;
-
- time_t now = odhcpd_time();
- struct odhcpd_ipaddr *addr = *addrs;
-
- qsort(addr, ctxt.ret, sizeof(*addr), prefixcmp);
-
- for (ssize_t i = 0; i < ctxt.ret; ++i) {
- if (addr[i].preferred < UINT32_MAX - now)
- addr[i].preferred += now;
-
- if (addr[i].valid < UINT32_MAX - now)
- addr[i].valid += now;
- }
-
-out:
- nl_cb_put(cb);
-
- return ctxt.ret;
-}