RFC: support setting DNS for systemd-resolved via DBus

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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



[Index of Archives]     [Linux Audio Users]     [Linux for Hams]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Fedora Users]

  Powered by Linux