linux-ppp.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Tj <linux@iam.tj>
To: linux-ppp@vger.kernel.org
Subject: RFC: support setting DNS for systemd-resolved via DBus
Date: Tue, 04 Sep 2018 10:17:03 +0000	[thread overview]
Message-ID: <e5fa9fe1-073c-e9c5-c774-21bc5487fe7e@iam.tj> (raw)

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\x1011: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@iam.tj>
+ * 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

             reply	other threads:[~2018-09-04 10:17 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-09-04 10:17 Tj [this message]
2018-09-04 13:06 ` RFC: support setting DNS for systemd-resolved via DBus Michael Richardson
2018-09-04 13:22 ` Marco d'Itri

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=e5fa9fe1-073c-e9c5-c774-21bc5487fe7e@iam.tj \
    --to=linux@iam.tj \
    --cc=linux-ppp@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).