From a42890e6a7423dc7f8948ef9b292addfd37f506b Mon Sep 17 00:00:00 2001 From: Tobias Heider Date: Fri, 29 Oct 2021 09:36:31 +0200 Subject: [PATCH] Add automatic client side DNS configuration on Linux/systemd via systemd-resolved. --- CMakeLists.txt | 4 ++ iked/iked.h | 2 + iked/policy.c | 14 ++--- iked/util.c | 23 ++++++++ iked/vroute-netlink.c | 125 ++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 157 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 07190b79..68c5e8e9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,7 @@ elseif(CMAKE_SYSTEM_NAME MATCHES "Linux") add_definitions(-DHAVE_UDPENCAP6) add_definitions(-DSPT_TYPE=SPT_REUSEARGV) set(HAVE_VROUTE_NETLINK ON) + set(HAVE_SYSTEMD ON) endif() if (NOT DEFINED CMAKE_INSTALL_MANDIR) set (CMAKE_INSTALL_MANDIR ${CMAKE_INSTALL_PREFIX}/man) @@ -346,6 +347,9 @@ check_function_exists(getopt HAVE_GETOPT) if(HAVE_GETOPT) add_definitions(-DHAVE_GETOPT) endif() +if(HAVE_SYSTEMD) + add_definitions(-DHAVE_SYSTEMD) +endif() if(HAVE_VROUTE) add_definitions(-DHAVE_VROUTE) endif() diff --git a/iked/iked.h b/iked/iked.h index 44d94193..c3f887f4 100644 --- a/iked/iked.h +++ b/iked/iked.h @@ -1218,6 +1218,8 @@ void print_debug(const char *, ...) __attribute__((format(printf, 1, 2))); void print_verbose(const char *, ...) __attribute__((format(printf, 1, 2))); +int run_command(const char *, ...); +int run_command_va(const char *, va_list); /* imsg_util.c */ struct ibuf * diff --git a/iked/policy.c b/iked/policy.c index c80b180e..cfe113ae 100644 --- a/iked/policy.c +++ b/iked/policy.c @@ -681,13 +681,6 @@ sa_configure_iface(struct iked *env, struct iked_sa *sa, int add) if (sa->sa_policy == NULL || sa->sa_policy->pol_iface == 0) return (0); - if (sa->sa_cp_dns) { - if (vroute_setdns(env, add, - (struct sockaddr *)&sa->sa_cp_dns->addr, - sa->sa_policy->pol_iface) != 0) - return (-1); - } - if (!sa->sa_cp_addr && !sa->sa_cp_addr6) return (0); @@ -746,6 +739,13 @@ sa_configure_iface(struct iked *env, struct iked_sa *sa, int add) return (-1); } } + + if (sa->sa_cp_dns) { + if (vroute_setdns(env, add, + (struct sockaddr *)&sa->sa_cp_dns->addr, + sa->sa_policy->pol_iface) != 0) + return (-1); + } #endif /* defined(HAVE_VROUTE) || defined(HAVE_VROUTE_NETLINK) */ return (0); diff --git a/iked/util.c b/iked/util.c index 1392c9f5..8f6a1e07 100644 --- a/iked/util.c +++ b/iked/util.c @@ -865,3 +865,26 @@ print_verbose(const char *emsg, ...) va_end(ap); } } + +int +run_command(const char *cmd, ...) +{ + int r; + va_list args; + + va_start(args, cmd); + r = run_command_va(cmd, args); + va_end(args); + + return r; +} + +int +run_command_va(const char *cmd, va_list args) +{ + char cmdline[1024]; + + vsprintf(cmdline, cmd, args); + log_debug("%s: \"%s\"", __func__, cmdline); + return system(cmdline); +} diff --git a/iked/vroute-netlink.c b/iked/vroute-netlink.c index 11eb1fc6..5a5287f7 100644 --- a/iked/vroute-netlink.c +++ b/iked/vroute-netlink.c @@ -37,10 +37,13 @@ int vroute_setroute(struct iked *, uint8_t, struct sockaddr *, uint8_t, int vroute_doroute(struct iked *, uint8_t, struct sockaddr *, struct sockaddr *, struct sockaddr *, int); int vroute_doaddr(struct iked *, int, struct sockaddr *, struct sockaddr *, int); +int vroute_dodns(struct iked *, struct sockaddr *, int, unsigned int); void vroute_cleanup(struct iked *); void vroute_insertaddr(struct iked *, int, struct sockaddr *, struct sockaddr *); void vroute_removeaddr(struct iked *, int, struct sockaddr *, struct sockaddr *); +void vroute_insertiface(struct iked *, unsigned int); +void vroute_removeiface(struct iked *, unsigned int); void vroute_insertroute(struct iked *, struct sockaddr *); void vroute_removeroute(struct iked *, struct sockaddr *); @@ -58,11 +61,18 @@ struct vroute_route { }; TAILQ_HEAD(vroute_routes, vroute_route); +struct vroute_iface { + unsigned int vi_ifidx; + TAILQ_ENTRY(vroute_iface) vi_entry; +}; +TAILQ_HEAD(vroute_ifaces, vroute_iface); + static int nl_addattr(struct nlmsghdr *, int, void *, size_t); static int nl_dorule(struct iked *, int, uint32_t, int, int); struct iked_vroute_sc { struct vroute_addrs ivr_addrs; + struct vroute_ifaces ivr_ifaces; struct vroute_routes ivr_routes; int ivr_rtsock; }; @@ -85,6 +95,7 @@ vroute_init(struct iked *env) fatal("%s: failed to create netlink socket", __func__); TAILQ_INIT(&ivr->ivr_addrs); + TAILQ_INIT(&ivr->ivr_ifaces); TAILQ_INIT(&ivr->ivr_routes); env->sc_vroute = ivr; @@ -97,6 +108,7 @@ vroute_cleanup(struct iked *env) { struct iked_vroute_sc *ivr = env->sc_vroute; struct vroute_addr *addr; + struct vroute_iface *iface; struct vroute_route *route; while ((addr = TAILQ_FIRST(&ivr->ivr_addrs))) { @@ -107,6 +119,12 @@ vroute_cleanup(struct iked *env) free(addr); } + while ((iface = TAILQ_FIRST(&ivr->ivr_ifaces))) { + vroute_dodns(env, NULL, 0, iface->vi_ifidx); + TAILQ_REMOVE(&ivr->ivr_ifaces, iface, vi_entry); + free(iface); + } + while ((route = TAILQ_FIRST(&ivr->ivr_routes))) { vroute_doroute(env, RTM_DELROUTE, (struct sockaddr *)&route->vr_dest, NULL, NULL, 1); @@ -211,18 +229,90 @@ vroute_getaddr(struct iked *env, struct imsg *imsg) imsg->hdr.type == IMSG_IF_ADDADDR)); } - int -vroute_setdns(struct iked *env, int add, struct sockaddr *addr, +vroute_setdns(struct iked *env, int add, struct sockaddr *dns, unsigned int ifidx) { - return (0); + struct iovec iov[2]; + int iovcnt = 0; + + if (dns == NULL) + return (0); + + iov[0].iov_base = &ifidx; + iov[0].iov_len = sizeof(ifidx); + iovcnt++; + + iov[1].iov_base = dns; + iov[1].iov_len = SA_LEN(dns); + iovcnt++; + + return (proc_composev(&env->sc_ps, PROC_PARENT, + add ? IMSG_VDNS_ADD: IMSG_VDNS_DEL, iov, iovcnt)); } int vroute_getdns(struct iked *env, struct imsg *imsg) { - return (0); + struct sockaddr *dns; + uint8_t *ptr; + size_t left; + int add, ifidx; + + ptr = imsg->data; + left = IMSG_DATA_SIZE(imsg); + + if (left < sizeof(ifidx)) + fatalx("bad length imsg received"); + + memcpy(&ifidx, ptr, sizeof(ifidx)); + ptr += sizeof(ifidx); + left -= sizeof(ifidx); + + if (left < sizeof(*dns)) + fatalx("bad length imsg received"); + dns = (struct sockaddr *) ptr; + + if (left < SA_LEN(dns)) + fatalx("bad length imsg received"); + ptr += SA_LEN(dns); + left -= SA_LEN(dns); + + add = (imsg->hdr.type == IMSG_VDNS_ADD); + return (vroute_dodns(env, dns, add, ifidx)); +} + +void +vroute_insertiface(struct iked *env, unsigned int ifidx) +{ + struct iked_vroute_sc *ivr = env->sc_vroute; + struct vroute_iface *iface; + + /* Prevent duplicates */ + TAILQ_FOREACH(iface, &ivr->ivr_ifaces, vi_entry) { + if (iface->vi_ifidx == ifidx) + return; + } + + iface = calloc(1, sizeof(*iface)); + if (iface == NULL) + fatalx("%s: calloc.", __func__); + + iface->vi_ifidx = ifidx; + TAILQ_INSERT_TAIL(&ivr->ivr_ifaces, iface, vi_entry); +} + +void +vroute_removeiface(struct iked *env, unsigned int ifidx) +{ + struct iked_vroute_sc *ivr = env->sc_vroute; + struct vroute_iface *iface; + + TAILQ_FOREACH(iface, &ivr->ivr_ifaces, vi_entry) { + if (iface->vi_ifidx == ifidx) + continue; + TAILQ_REMOVE(&ivr->ivr_ifaces, iface, vi_entry); + } } void @@ -622,6 +712,33 @@ vroute_doaddr(struct iked *env, int ifidx, struct sockaddr *addr, return (0); } +int +vroute_dodns(struct iked *env, struct sockaddr *dns, int add, + unsigned int ifindex) +{ + if (add) + vroute_insertiface(env, ifindex); + else + vroute_removeiface(env, ifindex); + +#ifdef HAVE_SYSTEMD + char ifname[IF_NAMESIZE]; + + if (if_indextoname(ifindex, ifname) == NULL) + return (-1); + + if (add && dns) { + /* XXX: use native dbus instead */ + run_command("resolvectl dns %s %s", ifname, + print_host(dns, NULL, 0)); + run_command("resolvectl domain %s ~.", ifname); + } else { + run_command("resolvectl revert %s", ifname); + } +#endif + return (0); +} + static int nl_dorule(struct iked *env, int table, uint32_t prio, int family, int add) {