Unwind / libunbound goes pretty badly off the rails when an address
family is not available, it still tries to talk to nameservers with an
unreachable address family.
I don't think it's libunbound's place to figure this out. It can't
sensibly do a getifaddrs on every query...
So let's help it out a bit.
OK?
(I'm not 100% sure where to place check_available_af(). Maybe this
should go to the main process.)
diff --git frontend.c frontend.c
index 50dab6c70ca..c5e5da91b9a 100644
--- frontend.c
+++ frontend.c
@@ -20,6 +20,7 @@
*/
#include <sys/types.h>
+#include <sys/ioctl.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/syslog.h>
@@ -32,6 +33,7 @@
#include <errno.h>
#include <event.h>
+#include <ifaddrs.h>
#include <imsg.h>
#include <netdb.h>
#include <pwd.h>
@@ -150,6 +152,8 @@ void parse_blocklist(int);
int bl_cmp(struct bl_node *, struct bl_node *);
void free_bl(void);
int pending_query_cnt(void);
+int get_ifrdomain(char *);
+void check_available_af(void);
struct uw_conf *frontend_conf;
static struct imsgev *iev_main;
@@ -158,6 +162,7 @@ struct event ev_route;
int udp4sock = -1, udp6sock = -1;
int tcp4sock = -1, tcp6sock = -1;
int ta_fd = -1;
+int ioctlsock;
static struct trust_anchor_head trust_anchors, new_trust_anchors;
@@ -212,7 +217,10 @@ frontend(int debug, int verbose)
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
fatal("can't drop privileges");
- if (pledge("stdio unix recvfd", NULL) == -1)
+ if ((ioctlsock = socket(AF_INET6, SOCK_DGRAM, 0)) == -1)
+ fatal("socket");
+
+ if (pledge("stdio unix recvfd route", NULL) == -1)
fatal("pledge");
event_init();
@@ -660,6 +668,7 @@ frontend_startup(void)
event_add(&ev_route, NULL);
frontend_imsg_compose_main(IMSG_STARTUP_DONE, 0, NULL, 0);
+ check_available_af();
}
void
@@ -1362,6 +1371,10 @@ handle_route_message(struct rt_msghdr *rtm, struct
sockaddr **rti_info)
frontend_imsg_compose_resolver(IMSG_REPLACE_DNS, 0,
&rdns_proposal, sizeof(rdns_proposal));
break;
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ check_available_af();
+ break;
default:
break;
}
@@ -1765,3 +1778,72 @@ tcp_timeout(int fd, short events, void *arg)
{
free_pending_query(arg);
}
+
+int
+get_ifrdomain(char *if_name)
+{
+ struct ifreq ifr;
+
+ strlcpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name));
+ if (ioctl(ioctlsock, SIOCGIFRDOMAIN, (caddr_t)&ifr) == -1) {
+ log_warn("SIOCGIFRDOMAIN");
+ return -1;
+ }
+ return ifr.ifr_rdomainid;
+}
+
+void
+check_available_af()
+{
+ static int available_af = HAVE_IPV4 | HAVE_IPV6;
+ static int rtable = -1;
+ struct ifaddrs *ifap, *ifa;
+ struct sockaddr_in *sin4;
+ struct sockaddr_in6 *sin6;
+ int new_available_af = 0;
+
+ if (rtable == -1)
+ rtable = getrtable();
+
+ if (getifaddrs(&ifap) != 0) {
+ log_warn("getifaddrs");
+ return;
+ }
+
+ for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+ if (get_ifrdomain(ifa->ifa_name) != getrtable())
+ continue;
+ if (ifa->ifa_addr == NULL)
+ continue;
+ switch(ifa->ifa_addr->sa_family) {
+ case AF_INET:
+ sin4 = (struct sockaddr_in *)ifa->ifa_addr;
+ if ((ntohl(sin4->sin_addr.s_addr) >> 24) ==
+ IN_LOOPBACKNET)
+ continue;
+ new_available_af |= HAVE_IPV4;
+ break;
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+ if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr) ||
+ IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) ||
+ IN6_IS_ADDR_MC_LINKLOCAL(&sin6->sin6_addr) ||
+ IN6_IS_ADDR_MC_INTFACELOCAL(&sin6->sin6_addr))
+ continue;
+ new_available_af |= HAVE_IPV6;
+ break;
+ default:
+ break;
+ }
+ if (new_available_af == (HAVE_IPV4 | HAVE_IPV6))
+ break;
+ }
+ freeifaddrs(ifap);
+ if (new_available_af != available_af) {
+ log_debug("%s %d - %d", __func__, new_available_af,
+ available_af);
+ available_af = new_available_af;
+ frontend_imsg_compose_resolver(IMSG_CHANGE_AFS, 0,
+ &available_af, sizeof(available_af));
+ }
+}
diff --git frontend.h frontend.h
index cd6c21875af..0c18c7524dc 100644
--- frontend.h
+++ frontend.h
@@ -17,6 +17,9 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#define HAVE_IPV4 1
+#define HAVE_IPV6 2
+
struct trust_anchor {
TAILQ_ENTRY(trust_anchor) entry;
char *ta;
diff --git resolver.c resolver.c
index a3521e8025f..20e0a946a06 100644
--- resolver.c
+++ resolver.c
@@ -230,6 +230,7 @@ struct key_cache *unified_key_cache;
struct val_neg_cache *unified_neg_cache;
int dns64_present;
+int available_afs = HAVE_IPV4 | HAVE_IPV6;
static const char * const as112_zones[] = {
/* RFC1918 */
@@ -462,13 +463,13 @@ resolver_imsg_compose_frontend(int type, pid_t pid, void
*data,
void
resolver_dispatch_frontend(int fd, short event, void *bula)
{
- struct imsgev *iev = bula;
- struct imsgbuf *ibuf;
- struct imsg imsg;
- struct query_imsg *query_imsg;
- ssize_t n;
- int shut = 0, verbose, i;
- char *ta;
+ struct imsgev *iev = bula;
+ struct imsgbuf *ibuf;
+ struct imsg imsg;
+ struct query_imsg *query_imsg;
+ ssize_t n;
+ int shut = 0, verbose, i, new_available_afs;
+ char *ta;
ibuf = &iev->ibuf;
@@ -567,6 +568,18 @@ resolver_dispatch_frontend(int fd, short event, void *bula)
replace_autoconf_forwarders((struct
imsg_rdns_proposal *)imsg.data);
break;
+ case IMSG_CHANGE_AFS:
+ if (IMSG_DATA_SIZE(imsg) !=
+ sizeof(new_available_afs))
+ fatalx("%s: IMSG_CHANGE_AFS wrong length: %lu",
+ __func__, IMSG_DATA_SIZE(imsg));
+ memcpy(&new_available_afs, imsg.data,
+ sizeof(new_available_afs));
+ if (new_available_afs != available_afs) {
+ available_afs = new_available_afs;
+ restart_ub_resolvers();
+ }
+ break;
default:
log_debug("%s: unexpected imsg %d", __func__,
imsg.hdr.type);
@@ -1255,6 +1268,28 @@ create_resolver(enum uw_resolver_type type)
}
}
+ if (!(available_afs & HAVE_IPV4)) {
+ if((err = ub_ctx_set_option(res->ctx, "do-ip4:",
+ "no")) != 0) {
+ ub_ctx_delete(res->ctx);
+ free(res);
+ log_warnx("error setting do-ip4: no: %s",
+ ub_strerror(err));
+ return (NULL);
+ }
+ }
+
+ if (!(available_afs & HAVE_IPV6)) {
+ if((err = ub_ctx_set_option(res->ctx, "do-ip6:",
+ "no")) != 0) {
+ ub_ctx_delete(res->ctx);
+ free(res);
+ log_warnx("error setting do-ip6: no: %s",
+ ub_strerror(err));
+ return (NULL);
+ }
+ }
+
if (!log_getdebug()) {
if((err = ub_ctx_set_option(res->ctx, "use-syslog:",
"no")) != 0) {
diff --git unwind.c unwind.c
index 8cf1ab030ee..43a95966af1 100644
--- unwind.c
+++ unwind.c
@@ -269,7 +269,8 @@ main(int argc, char *argv[])
fatal("route socket");
rtfilter = ROUTE_FILTER(RTM_IFINFO) | ROUTE_FILTER(RTM_PROPOSAL)
- | ROUTE_FILTER(RTM_IFANNOUNCE);
+ | ROUTE_FILTER(RTM_IFANNOUNCE) | ROUTE_FILTER(RTM_NEWADDR)
+ | ROUTE_FILTER(RTM_DELADDR);
if (setsockopt(frontend_routesock, AF_ROUTE, ROUTE_MSGFILTER,
&rtfilter, sizeof(rtfilter)) == -1)
fatal("setsockopt(ROUTE_MSGFILTER)");
diff --git unwind.h unwind.h
index 1f007cbed0e..568a78ed2ee 100644
--- unwind.h
+++ unwind.h
@@ -122,6 +122,7 @@ enum imsg_type {
IMSG_NEW_DNS64_PREFIXES_START,
IMSG_NEW_DNS64_PREFIX,
IMSG_NEW_DNS64_PREFIXES_DONE,
+ IMSG_CHANGE_AFS,
};
struct uw_forwarder {
--
I'm not entirely sure you are real.