I've recently been building an embedded device to do (kernel-mode) PPPoE which uses only systemd-networkd + systemd-resolved (PC Engines APU2 with Ubuntu 18.04 - amd64). I discovered the ip-up.d/0000usepeerdns script doesn't support systemd-resolved. In this case /etc/resolv.conf was over-written since resolvconf is not installed but is a symlink to ../run/systemd/resolve/stub-resolv.conf. After some experimentation I've put together a small C helper program that reads DNS[12]= from the helper environment and uses DBus calls to set the per-link nameservers. It build-depends on libdbus but not systemd. I'm looking for feedback on whether this would be acceptable as an addition to the ppp project, and if so where in the source tree would be appropriate. I'd assume it'd be controlled by a ./configure switch (due to introducing a dependency on libdbus) but as the current ./configure isn't generated by autoconf maybe there is some other way to do this? If not considered to be part of ppp what would be the best distribution method? Usually the tool is silent but with internal 'debug=1' stderr reports progress thus: ./ppp-systemd-resolved-set-nameservers wlp11s0 Interface index: 4 DNS2=1011:1213:1415:1617::A0A1:A2A3 === 1011:1213:1415:1617::A0A1:A2A3 DNS1=1.2.3.4 === 1.2.3.4 Dbus SetLinkDNS() Protocol: 10 Address: 1011:1213:1415:1617::a0a1:a2a3 Dbus SetLinkDNS() Protocol: 2 Address: 1.2.3.4 I'm including a patch for the tool alone (outside of the ppp repository). -- 2 files changed, 130 insertions(+) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e2dc6cc --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +# hard coded for now + +PROG=ppp_systemd-resolved_set_nameservers +all: + gcc -g -o0 -I/usr/include/dbus-1.0 -I/usr/lib/x86_64-linux-gnu/dbus-1.0/include -o $(PROG) $(PROG).c -ldbus-1 + +clean: + rm $(PROG) + diff --git a/ppp_systemd-resolved_set_nameservers.c b/ppp_systemd-resolved_set_nameservers.c new file mode 100644 index 0000000..10d13c0 --- /dev/null +++ b/ppp_systemd-resolved_set_nameservers.c @@ -0,0 +1,121 @@ +/* + * Set DNS nameservers provided via PPP env-vars using systemd-resolved + * (c) Copyright 2018 Tj <hacker@xxxxxx> + * Licenced on the terms of the GNU General Public Licence version 3 + */ + +#include <stdio.h> +#include <string.h> +#include <net/if.h> +#include <arpa/inet.h> +#include <dbus/dbus.h> + +const char * const dbus_resolve1_name = "org.freedesktop.resolve1"; +const char * const dbus_resolve1_object = "/org/freedesktop/resolve1"; +static const unsigned int debug = 1; + +int main(int argc, char **argv, char **env) +{ + dbus_int32_t ifindex = -1; + unsigned int dns_count = 0; + char *dns_fmt = "DNS%d="; + char dns[6]; + ssize_t ssize_in6 = sizeof(struct in6_addr); + unsigned char nameservers[2][ssize_in6 + 1]; // last byte stores protocol (AF_INET/AF_INET6) + + nameservers[0][0] = nameservers[1][0] = 0; + + if (argc > 1) + { + ifindex = if_nametoindex(argv[1]); + if (ifindex) { + if (debug) fprintf(stderr, "Interface index: %d\n", ifindex); + for (char **p = env; *p && dns_count < 2; p++) { // search the environment + for (int i = 1; i <= 2; i++) { // pppd passes up to 2 DNS IP addresses + snprintf(dns, 6, dns_fmt, i); + if (strncmp(dns, *p, 5) == 0) { + if (debug) fprintf(stderr, "%s === %s\n", *p, (*p + 5)); + if (inet_pton(AF_INET, (*p + 5), (void *)nameservers[dns_count]) != 1) + if (inet_pton(AF_INET6, (*p + 5), (void *)nameservers[dns_count]) != 1) + fprintf(stderr, "unable to decode %s\n", *p); + else { + nameservers[dns_count++][ssize_in6] = AF_INET6; + } + else { + nameservers[dns_count++][ssize_in6] = AF_INET; + } + } + } + } + if (dns_count > 0) { // at least 1 DNS nameserver found + char buffer[128]; + buffer[0] = 0; + dbus_uint32_t dbus_serial = 1; + DBusError dbus_err; + dbus_error_init(&dbus_err); + DBusMessageIter dbus_iter0, dbus_iter1, dbus_iter2, dbus_iter3; + DBusConnection *dbus_connection = dbus_bus_get(DBUS_BUS_SYSTEM, &dbus_err); + if (dbus_connection == NULL) + fprintf(stderr, "%s\n", dbus_err.message); + else { + DBusMessage *set_link_dns = dbus_message_new_method_call(dbus_resolve1_name, + dbus_resolve1_object, + dbus_resolve1_name, + "SetLinkDNS"); + if (set_link_dns == NULL) + fprintf(stderr, "%s\n", "Unable to allocate memory for dbus_message_new_method_call()"); + else { + // dbus argument signature: in i ifindex, in a(iay) addresses + if (!dbus_message_append_args(set_link_dns, DBUS_TYPE_INT32, &ifindex)) + fprintf(stderr, "%s\n", "dbus_message_append_args(ifindex) failed"); + dbus_message_iter_init_append(set_link_dns, &dbus_iter0); + if (!dbus_message_iter_open_container(&dbus_iter0, DBUS_TYPE_ARRAY, "(iay)", &dbus_iter1)) + fprintf(stderr, "%s\n", "dbus_message_iter_open_container(ARRAY, '(iay)') failed"); + else { + for(int i = 0; i < dns_count; i++) { + if(!dbus_message_iter_open_container(&dbus_iter1, DBUS_TYPE_STRUCT, NULL, &dbus_iter2)) + fprintf(stderr, "%s\n", "dbus_message_iter_open_container(STRUCT, NULL) failed"); + else { + dbus_int32_t len = nameservers[i][ssize_in6] == AF_INET ? 4 : 16; + dbus_int32_t af = nameservers[i][ssize_in6]; + unsigned char *p_nameserver = nameservers[i]; + if (!dbus_message_iter_append_basic(&dbus_iter2, DBUS_TYPE_INT32, &af)) + fprintf(stderr, "%s\n", "dbus_message_iter_append_basic(INT32, af) failed"); + if (!dbus_message_iter_open_container(&dbus_iter2, DBUS_TYPE_ARRAY, "y", &dbus_iter3)) + fprintf(stderr, "%s\n", "dbus_message_iter_open_container(ARRAY, 'y') failed"); + else { + dbus_message_iter_append_fixed_array(&dbus_iter3, DBUS_TYPE_BYTE, &p_nameserver, len); + } + if( !dbus_message_iter_close_container(&dbus_iter2, &dbus_iter3)) + fprintf(stderr, "%s\n", "dbus_message_iter_close_container(iter3) failed"); + if (!dbus_message_iter_close_container(&dbus_iter1, &dbus_iter2)) + fprintf(stderr, "%s\n", "dbus_message_iter_close_container(iter2) failed"); + } + + inet_ntop(nameservers[i][ssize_in6], (void *)nameservers[i], buffer, sizeof(buffer)); + printf("Dbus SetLinkDNS() Protocol: %d Address: %s\n", nameservers[i][ssize_in6], buffer); + } + } + if (!dbus_message_iter_close_container(&dbus_iter0, &dbus_iter1)) + fprintf(stderr, "%s\n", "dbus_message_iter_close_container(iter3) failed"); + if (!dbus_message_append_args(set_link_dns, DBUS_TYPE_INVALID)) + fprintf(stderr, "%s\n", "dbus_message_append_args(INVALID) failed"); + + if (!dbus_connection_send(dbus_connection, set_link_dns, &dbus_serial)) { + fprintf(stderr, "%s\n", "Failed to send DBus message"); + } + dbus_connection_flush(dbus_connection); + dbus_connection_unref(dbus_connection); + dbus_message_unref(set_link_dns); + } + } + } + } + else + perror("Cannot find interface"); + } + else + fprintf(stderr, "usage: %s INTERFACE_NAME\n", argv[0]); + + return ifindex; +} -- 2.17.1