On Mon, Jan 25, 2021 at 07:05:40PM +0100, Florian Obser wrote:
> 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?
This is better.
- 100% less ioctl, leading to tighter pledge after clue-bat from
claudio while working in asr.
- also handle RTM_DESYNC, pointed out by deraadt
I was also toying with the idea of counting arivals and departure of
IP addresses using the routing socket, but getting the account right
seems complicated. I don't think a call to getifaddrs will be
triggered that often.
diff --git frontend.c frontend.c
index 50dab6c70ca..b25fd76fe2e 100644
--- frontend.c
+++ frontend.c
@@ -32,6 +32,7 @@
#include <errno.h>
#include <event.h>
+#include <ifaddrs.h>
#include <imsg.h>
#include <netdb.h>
#include <pwd.h>
@@ -150,6 +151,7 @@ void parse_blocklist(int);
int bl_cmp(struct bl_node *, struct bl_node *);
void free_bl(void);
int pending_query_cnt(void);
+void check_available_af(void);
struct uw_conf *frontend_conf;
static struct imsgev *iev_main;
@@ -212,7 +214,7 @@ 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 (pledge("stdio dns unix recvfd", NULL) == -1)
fatal("pledge");
event_init();
@@ -660,6 +662,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 +1365,11 @@ 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:
+ case RTM_DESYNC:
+ check_available_af();
+ break;
default:
break;
}
@@ -1765,3 +1773,66 @@ tcp_timeout(int fd, short events, void *arg)
{
free_pending_query(arg);
}
+
+void
+check_available_af()
+{
+ static int available_af = HAVE_IPV4 | HAVE_IPV6;
+ static int rtable = -1;
+ struct ifaddrs *ifap, *ifa;
+ struct if_data *ifa_data;
+ struct sockaddr_in *sin4;
+ struct sockaddr_in6 *sin6;
+ int new_available_af = 0, ifa_rtable = -1;
+
+ if (rtable == -1)
+ rtable = getrtable();
+
+ if (getifaddrs(&ifap) != 0) {
+ log_warn("getifaddrs");
+ return;
+ }
+
+ for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr == NULL)
+ continue;
+ switch(ifa->ifa_addr->sa_family) {
+ case AF_LINK:
+ ifa_data = (struct if_data *)ifa->ifa_data;
+ ifa_rtable = ifa_data->ifi_rdomain;
+ break;
+ case AF_INET:
+ if (ifa_rtable != rtable)
+ continue;
+
+ 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:
+ if (ifa_rtable != rtable)
+ continue;
+
+ 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) {
+ 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 c1e399b859a..1ac11da22db 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);
@@ -1253,6 +1266,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.