+ 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);
+out:
+ nl_cb_put(cb);
+
+ return ctxt.ret;
+}
+
+static int odhcpd_get_linklocal_interface_address(int ifindex, struct in6_addr *lladdr)
+{
+ int status = -1;
+ struct sockaddr_in6 addr = {AF_INET6, 0, 0, ALL_IPV6_ROUTERS, ifindex};
+ socklen_t alen = sizeof(addr);
+ int sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
+
+ if (!connect(sock, (struct sockaddr*)&addr, sizeof(addr)) &&
+ !getsockname(sock, (struct sockaddr*)&addr, &alen)) {
+ *lladdr = addr.sin6_addr;
+ status = 0;
+ }
+
+ close(sock);
+
+ return status;
+}
+
+/*
+ * DNS address selection criteria order :
+ * - use IPv6 address with valid lifetime if none is yet selected
+ * - use IPv6 address with a preferred lifetime if the already selected IPv6 address is deprecated
+ * - use an IPv6 ULA address if the already selected IPv6 address is not an ULA address
+ * - use the IPv6 address with the longest preferred lifetime
+ */
+int odhcpd_get_interface_dns_addr(const struct interface *iface, struct in6_addr *addr)
+{
+ time_t now = odhcpd_time();
+ ssize_t m = -1;
+
+ for (size_t i = 0; i < iface->ia_addr_len; ++i) {
+ if (iface->ia_addr[i].valid <= (uint32_t)now)
+ continue;
+
+ if (m < 0) {
+ m = i;