Skip to contentSkip to conversation

add netlink support for Linux

RFC In review Antonio Quartulli 4 participants 11 messages Apr 1, 2018 – Apr 11, 2018

lore
Share & tools
lore
#0 lore raw

cover add netlink support for Linux

Hi all,

this patchset introduces native netlink support for the Linux platform.

At the moment openvpn operates on the tun interface and on the routing table by directly invoking the "ip" command (or ifconfig/route if nettools is selected at compile time).

With this patchset, openvpn would not need to fork new processes to run the "ip" binary any longer, but would directly talk to the kernel by means of the netlink interface.
This means simpler/cleaner code and, possibly, faster execution.

Another important advantage of this change is that the openvpn process will be in charge of directly working with the kernel, thus it can be granted special capabilities so that interfaces/routes operations can be carried out even when running as non-root.

Christian Hesse is working on a follow-up patch to properly allow the above.

This patchset also offers a first step towards a refactoring of the tun.c and route.c code.

The idea moving forward is to drop nettools support once this patchset is merged, but to retain support for ip and the --ifconfig/route-noexec options.

Feedback of any type is of course welcome.

This patch is posted as RFC because, as agreed during the last Hackathon, it will be considered for merging only when unit-tests will also be available. On to pof that, several aspects (like allowing iproute2 to be still used) have to be properly implemented.

This code can also be found on GitHub (based on latest master) at:
https://github.com/ordex/openvpn/tree/sitnl

Regards,

Antonio Quartulli (4):
configure: add LINUX conditional variable introduce sitnl: Simplified Interface To NetLink tun.c: use sitnl to handle tun configuration on Linux route.c: use sitnl to handle route configuration on Linux

 configure.ac            |    2 +
 src/openvpn/Makefile.am |    3 +
 src/openvpn/errlevel.h  |    1 +
 src/openvpn/route.c     |  364 +++------------
 src/openvpn/sitnl.c     | 1195 +++++++++++++++++++++++++++++++++++++++++++++++
 src/openvpn/sitnl.h     |  217 +++++++++
 src/openvpn/tun.c       |  199 +++-----
 7 files changed, 1547 insertions(+), 434 deletions(-)
 create mode 100644 src/openvpn/sitnl.c
 create mode 100644 src/openvpn/sitnl.h
Signature · 1 line
2.16.3
#1 lore raw

RFC 1/4 configure: add LINUX conditional variable

This variable is helpful when the configure script has to take actions that are strictly limited to the LINUX platform, as required by the introduction of netlink support.

---
 configure.ac | 2 ++
 1 file changed, 2 insertions(+)
configure.ac +2 -0
index 626b4dd4..f2e4aa47 100644
@@ -298,6 +298,7 @@ case "$host" in
 	*-*-linux*)
 		AC_DEFINE([TARGET_LINUX], [1], [Are we running on Linux?])
 		AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["L"], [Target prefix])
+		LINUX=yes
 		;;
 	*-*-solaris*)
 		AC_DEFINE([TARGET_SOLARIS], [1], [Are we running on Solaris?])
@@ -1367,6 +1368,7 @@ AC_SUBST([OPTIONAL_PKCS11_HELPER_LIBS])
 AC_SUBST([PLUGIN_AUTH_PAM_CFLAGS])
 AC_SUBST([PLUGIN_AUTH_PAM_LIBS])
 
+AM_CONDITIONAL([LINUX], [test "${LINUX}" = "yes"])
 AM_CONDITIONAL([WIN32], [test "${WIN32}" = "yes"])
 AM_CONDITIONAL([GIT_CHECKOUT], [test "${GIT_CHECKOUT}" = "yes"])
 AM_CONDITIONAL([ENABLE_PLUGIN_AUTH_PAM], [test "${enable_plugin_auth_pam}" = "yes"])
Signature · 1 line
2.16.3
#2 lore raw

RFC 2/4 introduce sitnl: Simplified Interface To NetLink

In reply to Antonio Quartulli

This patch introduces a tiny netlink interface, optimized for the openvpn use case.

It basically exposes all those operations that are currently handled by directly calling the /sbin/ip command (or even ifconfig/route, if configured).

By using netlink, openvpn won't need to spawn new processes when configuring the tun interface or routes.
This new approach will also allow openvpn to be granted CAP_NET_ADMIN and be able to properly work even though it dropped the root privileges (currently handled via workarounds).

By moving this logic into the sitnl module, tun.c and route.c also benefit from some code simplification

---
 src/openvpn/Makefile.am |    3 +
 src/openvpn/errlevel.h  |    1 +
 src/openvpn/sitnl.c     | 1195 +++++++++++++++++++++++++++++++++++++++++++++++
 src/openvpn/sitnl.h     |  217 +++++++++
 4 files changed, 1416 insertions(+)
 create mode 100644 src/openvpn/sitnl.c
 create mode 100644 src/openvpn/sitnl.h
src/openvpn/Makefile.am +3 -0
index eda08351..8bd25049 100644
@@ -131,6 +131,9 @@ openvpn_LDADD = \
 	$(OPTIONAL_SELINUX_LIBS) \
 	$(OPTIONAL_SYSTEMD_LIBS) \
 	$(OPTIONAL_DL_LIBS)
+if LINUX
+openvpn_SOURCES += sitnl.c sitnl.h
+endif
 if WIN32
 openvpn_SOURCES += openvpn_win32_resources.rc block_dns.c block_dns.h
 openvpn_LDADD += -lgdi32 -lws2_32 -lwininet -lcrypt32 -liphlpapi -lwinmm -lfwpuclnt -lrpcrt4 -lncrypt
src/openvpn/errlevel.h +1 -0
index 5ca4fa8f..3f2a0f1b 100644
@@ -109,6 +109,7 @@
 
 #define D_LOG_RW             LOGLEV(5, 0,  0)        /* Print 'R' or 'W' to stdout for read/write */
 
+#define D_RTNL               LOGLEV(6, 68, M_DEBUG)  /* show RTNL low level operations */
 #define D_LINK_RW            LOGLEV(6, 69, M_DEBUG)  /* show TCP/UDP reads/writes (terse) */
 #define D_TUN_RW             LOGLEV(6, 69, M_DEBUG)  /* show TUN/TAP reads/writes */
 #define D_TAP_WIN_DEBUG      LOGLEV(6, 69, M_DEBUG)  /* show TAP-Windows driver debug info */
src/openvpn/sitnl.c new file +1195 -0

1,195 lines not on this page · raw message

src/openvpn/sitnl.h new file +217 -0

217 lines not on this page · raw message

Signature · 1 line
2.16.3
#3 lore raw

RFC 3/4 tun.c: use sitnl to handle tun configuration on Linux

In reply to Antonio Quartulli
---
 src/openvpn/tun.c | 199 ++++++++++++++++--------------------------------------
 1 file changed, 58 insertions(+), 141 deletions(-)
src/openvpn/tun.c +58 -141
index 263cacdf..4e0b3f90 100644
@@ -46,6 +46,7 @@
 #include "route.h"
 #include "win32.h"
 #include "block_dns.h"
+#include "sitnl.h"
 
 #include "memdbg.h"
 
@@ -883,10 +884,12 @@ do_ifconfig(struct tuntap *tt,
     if (tt->did_ifconfig_setup)
     {
         bool tun = false;
+#if !defined(TARGET_LINUX)
         const char *ifconfig_local = NULL;
         const char *ifconfig_remote_netmask = NULL;
         const char *ifconfig_broadcast = NULL;
         const char *ifconfig_ipv6_local = NULL;
+#endif
         bool do_ipv6 = false;
         struct argv argv = argv_new();
 
@@ -898,18 +901,23 @@ do_ifconfig(struct tuntap *tt,
          */
         tun = is_tun_p2p(tt);
 
+#if !defined(TARGET_LINUX)
         /*
          * Set ifconfig parameters
          */
         ifconfig_local = print_in_addr_t(tt->local, 0, &gc);
         ifconfig_remote_netmask = print_in_addr_t(tt->remote_netmask, 0, &gc);
+#endif
 
         if (tt->did_ifconfig_ipv6_setup)
         {
+#if !defined(TARGET_LINUX)
             ifconfig_ipv6_local = print_in6_addr(tt->local_ipv6, 0, &gc);
+#endif
             do_ipv6 = true;
         }
 
+#if !defined(TARGET_LINUX)
         /*
          * If TAP-style device, generate broadcast address.
          */
@@ -917,6 +925,7 @@ do_ifconfig(struct tuntap *tt,
         {
             ifconfig_broadcast = print_in_addr_t(tt->broadcast, 0, &gc);
         }
+#endif
 
 #ifdef ENABLE_MANAGEMENT
         if (management)
@@ -933,102 +942,43 @@ do_ifconfig(struct tuntap *tt,
 
 
 #if defined(TARGET_LINUX)
-#ifdef ENABLE_IPROUTE
-        /*
-         * Set the MTU for the device
-         */
-        argv_printf(&argv,
-                    "%s link set dev %s up mtu %d",
-                    iproute_path,
-                    actual,
-                    tun_mtu
-                    );
-        argv_msg(M_INFO, &argv);
-        openvpn_execve_check(&argv, es, S_FATAL, "Linux ip link set failed");
-
-        if (tun)
-        {
-
-            /*
-             * Set the address for the device
-             */
-            argv_printf(&argv,
-                        "%s addr add dev %s local %s peer %s",
-                        iproute_path,
-                        actual,
-                        ifconfig_local,
-                        ifconfig_remote_netmask
-                        );
-            argv_msg(M_INFO, &argv);
-            openvpn_execve_check(&argv, es, S_FATAL, "Linux ip addr add failed");
-        }
-        else
+        if (sitnl_iface_mtu_set(actual, tun_mtu) < 0)
         {
-            argv_printf(&argv,
-                        "%s addr add dev %s %s/%d broadcast %s",
-                        iproute_path,
-                        actual,
-                        ifconfig_local,
-                        netmask_to_netbits2(tt->remote_netmask),
-                        ifconfig_broadcast
-                        );
-            argv_msg(M_INFO, &argv);
-            openvpn_execve_check(&argv, es, S_FATAL, "Linux ip addr add failed");
+            msg(M_FATAL, "Linux can't set mtu (%d) on %s", tun_mtu, actual);
         }
-        if (do_ipv6)
+
+        if (sitnl_iface_up(actual, true) < 0)
         {
-            argv_printf( &argv,
-                         "%s -6 addr add %s/%d dev %s",
-                         iproute_path,
-                         ifconfig_ipv6_local,
-                         tt->netbits_ipv6,
-                         actual
-                         );
-            argv_msg(M_INFO, &argv);
-            openvpn_execve_check(&argv, es, S_FATAL, "Linux ip -6 addr add failed");
+            msg(M_FATAL, "Linux can't bring %s up", actual);
         }
-        tt->did_ifconfig = true;
-#else  /* ifdef ENABLE_IPROUTE */
+
         if (tun)
         {
-            argv_printf(&argv,
-                        "%s %s %s pointopoint %s mtu %d",
-                        IFCONFIG_PATH,
-                        actual,
-                        ifconfig_local,
-                        ifconfig_remote_netmask,
-                        tun_mtu
-                        );
+            if (sitnl_addr_ptp_v4_add(actual, &tt->local,
+                                      &tt->remote_netmask) < 0)
+            {
+                msg(M_FATAL, "Linux can't add IP to TUN interface %s", actual);
+            }
         }
         else
         {
-            argv_printf(&argv,
-                        "%s %s %s netmask %s mtu %d broadcast %s",
-                        IFCONFIG_PATH,
-                        actual,
-                        ifconfig_local,
-                        ifconfig_remote_netmask,
-                        tun_mtu,
-                        ifconfig_broadcast
-                        );
+            if (sitnl_addr_v4_add(actual, &tt->local,
+                                  netmask_to_netbits2(tt->remote_netmask),
+                                  &tt->remote_netmask) < 0)
+            {
+                msg(M_FATAL, "Linux can't add IP to TAP interface %s", actual);
+            }
         }
-        argv_msg(M_INFO, &argv);
-        openvpn_execve_check(&argv, es, S_FATAL, "Linux ifconfig failed");
         if (do_ipv6)
         {
-            argv_printf(&argv,
-                        "%s %s add %s/%d",
-                        IFCONFIG_PATH,
-                        actual,
-                        ifconfig_ipv6_local,
-                        tt->netbits_ipv6
-                        );
-            argv_msg(M_INFO, &argv);
-            openvpn_execve_check(&argv, es, S_FATAL, "Linux ifconfig inet6 failed");
+            if (sitnl_addr_v6_add(actual, &tt->local_ipv6,
+                                  tt->netbits_ipv6) < 0)
+            {
+                msg(M_FATAL, "Linux can't add IPv6 to interface %s", actual);
+            }
         }
         tt->did_ifconfig = true;
 
-#endif /*ENABLE_IPROUTE*/
 #elif defined(TARGET_ANDROID)
 
         if (do_ipv6)
@@ -2103,77 +2053,44 @@ tuncfg(const char *dev, const char *dev_type, const char *dev_node, int persist_
 void
 close_tun(struct tuntap *tt)
 {
-    if (tt)
+    if (!tt)
     {
-        if (tt->type != DEV_TYPE_NULL && tt->did_ifconfig)
-        {
-            struct argv argv = argv_new();
-            struct gc_arena gc = gc_new();
+        return;
+    }
 
-#ifdef ENABLE_IPROUTE
-            if (is_tun_p2p(tt))
+    if (tt->type != DEV_TYPE_NULL && tt->did_ifconfig)
+    {
+        int netbits = netmask_to_netbits2(tt->remote_netmask);
+        if (is_tun_p2p(tt))
+        {
+            if (sitnl_addr_ptp_v4_del(tt->actual_name, &tt->local) < 0)
             {
-                argv_printf(&argv,
-                            "%s addr del dev %s local %s peer %s",
-                            iproute_path,
-                            tt->actual_name,
-                            print_in_addr_t(tt->local, 0, &gc),
-                            print_in_addr_t(tt->remote_netmask, 0, &gc)
-                            );
+                msg(M_WARN, "Linux can't del IP from TUN iface %s",
+                    tt->actual_name);
             }
-            else
+        }
+        else
+        {
+            if (sitnl_addr_v4_del(tt->actual_name, &tt->local, netbits) < 0)
             {
-                argv_printf(&argv,
-                            "%s addr del dev %s %s/%d",
-                            iproute_path,
-                            tt->actual_name,
-                            print_in_addr_t(tt->local, 0, &gc),
-                            netmask_to_netbits2(tt->remote_netmask)
-                            );
+                msg(M_WARN, "Linux can't del IP from TAP iface %s",
+                    tt->actual_name);
             }
-#else  /* ifdef ENABLE_IPROUTE */
-            argv_printf(&argv,
-                        "%s %s 0.0.0.0",
-                        IFCONFIG_PATH,
-                        tt->actual_name
-                        );
-#endif /* ifdef ENABLE_IPROUTE */
-
-            argv_msg(M_INFO, &argv);
-            openvpn_execve_check(&argv, NULL, 0, "Linux ip addr del failed");
+        }
 
-            if (tt->did_ifconfig_ipv6_setup)
+        if (tt->did_ifconfig_ipv6_setup)
+        {
+            if (sitnl_addr_v6_del(tt->actual_name, &tt->local_ipv6,
+                                  tt->netbits_ipv6) < 0)
             {
-                const char *ifconfig_ipv6_local = print_in6_addr(tt->local_ipv6, 0, &gc);
-
-#ifdef ENABLE_IPROUTE
-                argv_printf(&argv, "%s -6 addr del %s/%d dev %s",
-                            iproute_path,
-                            ifconfig_ipv6_local,
-                            tt->netbits_ipv6,
-                            tt->actual_name
-                            );
-                argv_msg(M_INFO, &argv);
-                openvpn_execve_check(&argv, NULL, 0, "Linux ip -6 addr del failed");
-#else  /* ifdef ENABLE_IPROUTE */
-                argv_printf(&argv,
-                            "%s %s del %s/%d",
-                            IFCONFIG_PATH,
-                            tt->actual_name,
-                            ifconfig_ipv6_local,
-                            tt->netbits_ipv6
-                            );
-                argv_msg(M_INFO, &argv);
-                openvpn_execve_check(&argv, NULL, 0, "Linux ifconfig inet6 del failed");
-#endif
+                msg(M_WARN, "Linux can't del IPv6 from iface %s",
+                    tt->actual_name);
             }
-
-            argv_reset(&argv);
-            gc_free(&gc);
         }
-        close_tun_generic(tt);
-        free(tt);
     }
+
+    close_tun_generic(tt);
+    free(tt);
 }
 
 int
Signature · 1 line
2.16.3
#4 lore raw

RFC 4/4 route.c: use sitnl to handle route configuration on Linux

In reply to Antonio Quartulli
---
 src/openvpn/route.c | 364 ++++++++++------------------------------------------
 1 file changed, 71 insertions(+), 293 deletions(-)
src/openvpn/route.c +71 -293
index 8990a986..4b398366 100644
@@ -41,6 +41,7 @@
 #include "manage.h"
 #include "win32.h"
 #include "options.h"
+#include "sitnl.h"
 
 #include "memdbg.h"
 
@@ -1529,13 +1530,17 @@ add_route(struct route_ipv4 *r,
 {
     struct gc_arena gc;
     struct argv argv = argv_new();
+#if !defined(TARGET_LINUX)
     const char *network;
 #if !defined(ENABLE_IPROUTE) && !defined(TARGET_AIX)
     const char *netmask;
 #endif
     const char *gateway;
+#endif
+    const char *iface;
     bool status = false;
     int is_local_route;
+    int metric;
 
     if (!(r->flags & RT_DEFINED))
     {
@@ -1544,11 +1549,13 @@ add_route(struct route_ipv4 *r,
 
     gc_init(&gc);
 
+#if !defined(TARGET_LINUX)
     network = print_in_addr_t(r->network, 0, &gc);
 #if !defined(ENABLE_IPROUTE) && !defined(TARGET_AIX)
     netmask = print_in_addr_t(r->netmask, 0, &gc);
 #endif
     gateway = print_in_addr_t(r->gateway, 0, &gc);
+#endif
 
     is_local_route = local_route(r->network, r->netmask, r->gateway, rgi);
     if (is_local_route == LR_ERROR)
@@ -1557,47 +1564,26 @@ add_route(struct route_ipv4 *r,
     }
 
 #if defined(TARGET_LINUX)
-#ifdef ENABLE_IPROUTE
-    argv_printf(&argv, "%s route add %s/%d",
-                iproute_path,
-                network,
-                netmask_to_netbits2(r->netmask));
-
-    if (r->flags & RT_METRIC_DEFINED)
-    {
-        argv_printf_cat(&argv, "metric %d", r->metric);
-    }
-
+    iface = NULL;
     if (is_on_link(is_local_route, flags, rgi))
     {
-        argv_printf_cat(&argv, "dev %s", rgi->iface);
+        iface = rgi->iface;
     }
-    else
-    {
-        argv_printf_cat(&argv, "via %s", gateway);
-    }
-#else  /* ifdef ENABLE_IPROUTE */
-    argv_printf(&argv, "%s add -net %s netmask %s",
-                ROUTE_PATH,
-                network,
-                netmask);
+
+    metric = -1;
     if (r->flags & RT_METRIC_DEFINED)
     {
-        argv_printf_cat(&argv, "metric %d", r->metric);
-    }
-    if (is_on_link(is_local_route, flags, rgi))
-    {
-        argv_printf_cat(&argv, "dev %s", rgi->iface);
+        metric = r->metric;
     }
-    else
+
+    status = true;
+    if (sitnl_route_v4_add(&r->network, netmask_to_netbits2(r->netmask),
+                           &r->gateway, iface, 0, metric) < 0)
     {
-        argv_printf_cat(&argv, "gw %s", gateway);
+        msg(M_WARN, "ERROR: Linux route add command failed");
+        status = false;
     }
 
-#endif  /*ENABLE_IPROUTE*/
-    argv_msg(D_ROUTE, &argv);
-    status = openvpn_execve_check(&argv, es, 0, "ERROR: Linux route add command failed");
-
 #elif defined (TARGET_ANDROID)
     char out[128];
 
@@ -1853,7 +1839,7 @@ add_route_ipv6(struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flag
     const char *gateway;
     bool status = false;
     const char *device = tt->actual_name;
-
+    int metric;
     bool gateway_needed = false;
 
     if (!(r6->flags & RT_DEFINED) )
@@ -1918,38 +1904,20 @@ add_route_ipv6(struct route_ipv6 *r6, const struct tuntap *tt, unsigned int flag
     }
 
 #if defined(TARGET_LINUX)
-#ifdef ENABLE_IPROUTE
-    argv_printf(&argv, "%s -6 route add %s/%d dev %s",
-                iproute_path,
-                network,
-                r6->netbits,
-                device);
-    if (gateway_needed)
-    {
-        argv_printf_cat(&argv, "via %s", gateway);
-    }
-    if ( (r6->flags & RT_METRIC_DEFINED) && r6->metric > 0)
+    metric = -1;
+    if ((r6->flags & RT_METRIC_DEFINED) && (r6->metric > 0))
     {
-        argv_printf_cat(&argv, " metric %d", r6->metric);
+        metric = r6->metric;
     }
 
-#else  /* ifdef ENABLE_IPROUTE */
-    argv_printf(&argv, "%s -A inet6 add %s/%d dev %s",
-                ROUTE_PATH,
-                network,
-                r6->netbits,
-                device);
-    if (gateway_needed)
+    status = true;
+    if (sitnl_route_v6_add(&r6->network, r6->netbits,
+                           gateway_needed ? &r6->gateway : NULL, device, 0,
+                           metric) < 0)
     {
-        argv_printf_cat(&argv, "gw %s", gateway);
+        msg(M_WARN, "ERROR: Linux IPv6 route can't be added");
+        status = false;
     }
-    if ( (r6->flags & RT_METRIC_DEFINED) && r6->metric > 0)
-    {
-        argv_printf_cat(&argv, " metric %d", r6->metric);
-    }
-#endif  /*ENABLE_IPROUTE*/
-    argv_msg(D_ROUTE, &argv);
-    status = openvpn_execve_check(&argv, es, 0, "ERROR: Linux route -6/-A inet6 add command failed");
 
 #elif defined (TARGET_ANDROID)
     char out[64];
@@ -2135,6 +2103,7 @@ delete_route(struct route_ipv4 *r,
 {
     struct gc_arena gc;
     struct argv argv = argv_new();
+#if !defined(TARGET_LINUX)
     const char *network;
 #if !defined(ENABLE_IPROUTE) && !defined(TARGET_AIX)
     const char *netmask;
@@ -2142,7 +2111,8 @@ delete_route(struct route_ipv4 *r,
 #if !defined(TARGET_LINUX) && !defined(TARGET_ANDROID)
     const char *gateway;
 #endif
-    int is_local_route;
+#endif
+    int is_local_route, metric;
 
     if ((r->flags & (RT_DEFINED|RT_ADDED)) != (RT_DEFINED|RT_ADDED))
     {
@@ -2151,12 +2121,14 @@ delete_route(struct route_ipv4 *r,
 
     gc_init(&gc);
 
+#if !defined(TARGET_LINUX)
     network = print_in_addr_t(r->network, 0, &gc);
 #if !defined(ENABLE_IPROUTE) && !defined(TARGET_AIX)
     netmask = print_in_addr_t(r->netmask, 0, &gc);
 #endif
 #if !defined(TARGET_LINUX) && !defined(TARGET_ANDROID)
     gateway = print_in_addr_t(r->gateway, 0, &gc);
+#endif
 #endif
 
     is_local_route = local_route(r->network, r->netmask, r->gateway, rgi);
@@ -2166,23 +2138,17 @@ delete_route(struct route_ipv4 *r,
     }
 
 #if defined(TARGET_LINUX)
-#ifdef ENABLE_IPROUTE
-    argv_printf(&argv, "%s route del %s/%d",
-                iproute_path,
-                network,
-                netmask_to_netbits2(r->netmask));
-#else
-    argv_printf(&argv, "%s del -net %s netmask %s",
-                ROUTE_PATH,
-                network,
-                netmask);
-#endif /*ENABLE_IPROUTE*/
+    metric = -1;
     if (r->flags & RT_METRIC_DEFINED)
     {
-        argv_printf_cat(&argv, "metric %d", r->metric);
+        metric = r->metric;
+    }
+
+    if (sitnl_route_v4_del(&r->network, netmask_to_netbits2(r->netmask),
+                           &r->gateway, NULL, 0, metric) < 0)
+    {
+        msg(M_WARN, "ERROR: Linux route delete command failed");
     }
-    argv_msg(D_ROUTE, &argv);
-    openvpn_execve_check(&argv, es, 0, "ERROR: Linux route delete command failed");
 
 #elif defined (_WIN32)
 
@@ -2324,9 +2290,12 @@ delete_route_ipv6(const struct route_ipv6 *r6, const struct tuntap *tt, unsigned
     struct gc_arena gc;
     struct argv argv = argv_new();
     const char *network;
+#if !defined(TARGET_LINUX)
     const char *gateway;
+#endif
     const char *device = tt->actual_name;
     bool gateway_needed = false;
+    int metric;
 
     if ((r6->flags & (RT_DEFINED|RT_ADDED)) != (RT_DEFINED|RT_ADDED))
     {
@@ -2344,7 +2313,9 @@ delete_route_ipv6(const struct route_ipv6 *r6, const struct tuntap *tt, unsigned
     gc_init(&gc);
 
     network = print_in6_addr( r6->network, 0, &gc);
+#if !defined(TARGET_LINUX)
     gateway = print_in6_addr( r6->gateway, 0, &gc);
+#endif
 
 #if defined(TARGET_DARWIN)    \
     || defined(TARGET_FREEBSD) || defined(TARGET_DRAGONFLY)    \
@@ -2375,35 +2346,19 @@ delete_route_ipv6(const struct route_ipv6 *r6, const struct tuntap *tt, unsigned
         gateway_needed = true;
     }
 
-
 #if defined(TARGET_LINUX)
-#ifdef ENABLE_IPROUTE
-    argv_printf(&argv, "%s -6 route del %s/%d dev %s",
-                iproute_path,
-                network,
-                r6->netbits,
-                device);
-    if (gateway_needed)
-    {
-        argv_printf_cat(&argv, "via %s", gateway);
-    }
-#else  /* ifdef ENABLE_IPROUTE */
-    argv_printf(&argv, "%s -A inet6 del %s/%d dev %s",
-                ROUTE_PATH,
-                network,
-                r6->netbits,
-                device);
-    if (gateway_needed)
+    metric = -1;
+    if ((r6->flags & RT_METRIC_DEFINED) && (r6->metric > 0))
     {
-        argv_printf_cat(&argv, "gw %s", gateway);
+        metric = r6->metric;
     }
-    if ( (r6->flags & RT_METRIC_DEFINED) && r6->metric > 0)
+
+    if (sitnl_route_v6_del(&r6->network, r6->netbits,
+                           gateway_needed ? &r6->gateway : NULL, device,
+                           0, metric) < 0)
     {
-        argv_printf_cat(&argv, " metric %d", r6->metric);
+        msg(M_WARN, "ERROR: Linux route v6 delete command failed");
     }
-#endif  /*ENABLE_IPROUTE*/
-    argv_msg(D_ROUTE, &argv);
-    openvpn_execve_check(&argv, es, 0, "ERROR: Linux route -6/-A inet6 del command failed");
 
 #elif defined (_WIN32)
 
@@ -3167,68 +3122,19 @@ get_default_gateway(struct route_gateway_info *rgi)
 {
     struct gc_arena gc = gc_new();
     int sd = -1;
-    char best_name[16];
-    best_name[0] = 0;
+    char best_name[IFNAMSIZ];
 
     CLEAR(*rgi);
+    CLEAR(best_name);
 
 #ifndef TARGET_ANDROID
     /* get default gateway IP addr */
+    if (sitnl_route_v4_best_gw(NULL, 0, &rgi->gateway.addr, best_name) == 0)
     {
-        FILE *fp = fopen("/proc/net/route", "r");
-        if (fp)
+        rgi->flags |= RGI_ADDR_DEFINED;
+        if (!rgi->gateway.addr && (strlen(best_name) > 0))
         {
-            char line[256];
-            int count = 0;
-            unsigned int lowest_metric = UINT_MAX;
-            in_addr_t best_gw = 0;
-            bool found = false;
-            while (fgets(line, sizeof(line), fp) != NULL)
-            {
-                if (count)
-                {
-                    unsigned int net_x = 0;
-                    unsigned int mask_x = 0;
-                    unsigned int gw_x = 0;
-                    unsigned int metric = 0;
-                    unsigned int flags = 0;
-                    char name[16];
-                    name[0] = 0;
-                    const int np = sscanf(line, "%15s\t%x\t%x\t%x\t%*s\t%*s\t%d\t%x",
-                                          name,
-                                          &net_x,
-                                          &gw_x,
-                                          &flags,
-                                          &metric,
-                                          &mask_x);
-                    if (np == 6 && (flags & IFF_UP))
-                    {
-                        const in_addr_t net = ntohl(net_x);
-                        const in_addr_t mask = ntohl(mask_x);
-                        const in_addr_t gw = ntohl(gw_x);
-
-                        if (!net && !mask && metric < lowest_metric)
-                        {
-                            found = true;
-                            best_gw = gw;
-                            strcpy(best_name, name);
-                            lowest_metric = metric;
-                        }
-                    }
-                }
-                ++count;
-            }
-            fclose(fp);
-
-            if (found)
-            {
-                rgi->gateway.addr = best_gw;
-                rgi->flags |= RGI_ADDR_DEFINED;
-                if (!rgi->gateway.addr && best_name[0])
-                {
-                    rgi->flags |= RGI_ON_LINK;
-                }
-            }
+            rgi->flags |= RGI_ON_LINK;
         }
     }
 #else  /* ifndef TARGET_ANDROID */
@@ -3371,150 +3277,28 @@ void
 get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6,
                          const struct in6_addr *dest)
 {
-    int nls = -1;
-    struct rtreq rtreq;
-    struct rtattr *rta;
-
-    char rtbuf[2000];
-    ssize_t ssize;
-
-    CLEAR(*rgi6);
-
-    nls = socket( PF_NETLINK, SOCK_RAW, NETLINK_ROUTE );
-    if (nls < 0)
-    {
-        msg(M_WARN|M_ERRNO, "GDG6: socket() failed" ); goto done;
-    }
-
-    /* bind() is not needed, no unsolicited msgs coming in */
-
-    /* request best matching route, see netlink(7) for explanations
-     */
-    CLEAR(rtreq);
-    rtreq.nh.nlmsg_type = RTM_GETROUTE;
-    rtreq.nh.nlmsg_flags = NLM_F_REQUEST;       /* best match only */
-    rtreq.rtm.rtm_family = AF_INET6;
-    rtreq.rtm.rtm_src_len = 0;                  /* not source dependent */
-    rtreq.rtm.rtm_dst_len = 128;                /* exact dst */
-    rtreq.rtm.rtm_table = RT_TABLE_MAIN;
-    rtreq.rtm.rtm_protocol = RTPROT_UNSPEC;
-    rtreq.nh.nlmsg_len = NLMSG_SPACE(sizeof(rtreq.rtm));
-
-    /* set RTA_DST for target IPv6 address we want */
-    rta = (struct rtattr *)(((char *) &rtreq)+NLMSG_ALIGN(rtreq.nh.nlmsg_len));
-    rta->rta_type = RTA_DST;
-    rta->rta_len = RTA_LENGTH(16);
-    rtreq.nh.nlmsg_len = NLMSG_ALIGN(rtreq.nh.nlmsg_len)
-                         +RTA_LENGTH(16);
-
-    if (dest == NULL)                   /* ::, unspecified */
-    {
-        memset( RTA_DATA(rta), 0, 16 ); /* :: = all-zero */
-    }
-    else
-    {
-        memcpy( RTA_DATA(rta), (void *)dest, 16 );
-    }
+    struct in_addr gw;
+    int flags;
 
-    /* send and receive reply */
-    if (send( nls, &rtreq, rtreq.nh.nlmsg_len, 0 ) < 0)
-    {
-        msg(M_WARN|M_ERRNO, "GDG6: send() failed" ); goto done;
-    }
-
-    ssize = recv(nls, rtbuf, sizeof(rtbuf), MSG_TRUNC);
-
-    if (ssize < 0)
-    {
-        msg(M_WARN|M_ERRNO, "GDG6: recv() failed" ); goto done;
-    }
+    CLEAR(gw);
 
-    if (ssize > sizeof(rtbuf))
+    if (sitnl_route_v6_best_gw(dest, 128, &rgi6->gateway.addr_ipv6,
+                               rgi6->iface) == 0)
     {
-        msg(M_WARN, "get_default_gateway_ipv6: returned message too big for buffer (%d>%d)", (int)ssize, (int)sizeof(rtbuf) );
-        goto done;
-    }
-
-    struct nlmsghdr *nh;
-
-    for (nh = (struct nlmsghdr *)rtbuf;
-         NLMSG_OK(nh, ssize);
-         nh = NLMSG_NEXT(nh, ssize))
-    {
-        struct rtmsg *rtm;
-        int attrlen;
-
-        if (nh->nlmsg_type == NLMSG_DONE)
+        if (rgi6->gateway.addr_ipv6.s6_addr)
         {
-            break;
-        }
-
-        if (nh->nlmsg_type == NLMSG_ERROR)
-        {
-            struct nlmsgerr *ne = (struct nlmsgerr *)NLMSG_DATA(nh);
-
-            /* since linux-4.11 -ENETUNREACH is returned when no route can be
-             * found. Don't print any error message in this case */
-            if (ne->error != -ENETUNREACH)
-            {
-                msg(M_WARN, "GDG6: NLMSG_ERROR: error %s\n",
-                    strerror(-ne->error));
-            }
-            break;
+            rgi6->flags |= RGI_ADDR_DEFINED;
         }
 
-        if (nh->nlmsg_type != RTM_NEWROUTE)
+        if (rgi6->iface)
         {
-            /* shouldn't happen */
-            msg(M_WARN, "GDG6: unexpected msg_type %d", nh->nlmsg_type );
-            continue;
-        }
-
-        rtm = (struct rtmsg *)NLMSG_DATA(nh);
-        attrlen = RTM_PAYLOAD(nh);
-
-        /* we're only looking for routes in the main table, as "we have
-         * no IPv6" will lead to a lookup result in "Local" (::/0 reject)
-         */
-        if (rtm->rtm_family != AF_INET6
-            || rtm->rtm_table != RT_TABLE_MAIN)
-        {
-            continue;
-        }                               /* we're not interested */
-
-        for (rta = RTM_RTA(rtm);
-             RTA_OK(rta, attrlen);
-             rta = RTA_NEXT(rta, attrlen))
-        {
-            if (rta->rta_type == RTA_GATEWAY)
-            {
-                if (RTA_PAYLOAD(rta) != sizeof(struct in6_addr) )
-                {
-                    msg(M_WARN, "GDG6: RTA_GW size mismatch"); continue;
-                }
-                rgi6->gateway.addr_ipv6 = *(struct in6_addr *) RTA_DATA(rta);
-                rgi6->flags |= RGI_ADDR_DEFINED;
-            }
-            else if (rta->rta_type == RTA_OIF)
-            {
-                char ifname[IF_NAMESIZE+1];
-                int oif;
-                if (RTA_PAYLOAD(rta) != sizeof(oif) )
-                {
-                    msg(M_WARN, "GDG6: oif size mismatch"); continue;
-                }
-
-                memcpy(&oif, RTA_DATA(rta), sizeof(oif));
-                if_indextoname(oif,ifname);
-                strncpy( rgi6->iface, ifname, sizeof(rgi6->iface)-1 );
-                rgi6->flags |= RGI_IFACE_DEFINED;
-            }
+            rgi6->flags |= RGI_IFACE_DEFINED;
         }
     }
 
     /* if we have an interface but no gateway, the destination is on-link */
-    if ( ( rgi6->flags & (RGI_IFACE_DEFINED|RGI_ADDR_DEFINED) ) ==
-         RGI_IFACE_DEFINED)
+    flags = rgi6->flags & (RGI_IFACE_DEFINED | RGI_ADDR_DEFINED);
+    if (flags == RGI_IFACE_DEFINED)
     {
         rgi6->flags |= (RGI_ADDR_DEFINED | RGI_ON_LINK);
         if (dest)
@@ -3522,12 +3306,6 @@ get_default_gateway_ipv6(struct route_ipv6_gateway_info *rgi6,
             rgi6->gateway.addr_ipv6 = *dest;
         }
     }
-
-done:
-    if (nls >= 0)
-    {
-        close(nls);
-    }
 }
 
 #elif defined(TARGET_DARWIN) || defined(TARGET_SOLARIS)    \
Signature · 1 line
2.16.3
#5 lore raw

Re: [Openvpn-devel] [RFC 0/4] add netlink support for Linux: update

In reply to Antonio Quartulli

Hi all,

in case anybody cares, I have updated my patchset on GitHub[1] (I didn't want to create more noise on the mailing list since it is still RFC).

This new version is quite different as I implemented a major architectural change: instead of creating a standalone sitnl module, I introduced a generic "networking API" (this is a concept that has been discussed with Gert and other devs in the past, but that never came into being).

This "networking API" is the glue code between tun.c/route.c and the Linux specific code.

Two new files, namely networking_sitnl.c and networking_ip.c, provides two implementations for this API: one uses the new sitnl code (netlink) and one uses iproute2.

This new architecture could be re-used in the future to move other platforms specific code (i.e. for drawin, openbsd, etc..) to their own files and hide all the details behind the new API.

If you try to compile my branch, openvpn will use sitnl by default unless you specify --enable-iproute2 at configure time.

Comments are review are welcome!

Cheers,

[1] https://github.com/ordex/openvpn/tree/sitnl

Signature · 1 line
Antonio Quartulli
#6 lore raw

Re: [Openvpn-devel] [RFC 0/4] add netlink support for Linux: update

Antonio Quartulli <[email protected]...> on Fri, 2018/04/06 15:43:

Quoted 3 lines

Two new files, namely networking_sitnl.c and networking_ip.c, provides two implementations for this API: one uses the new sitnl code (netlink) and one uses iproute2.

This complicates the situation for my followup code: Running the process with unprivileged user works with netlink interface only. If we want to support netlink and iproute2 we end up with creating the files from templates (or carry static files in at least two versions).

This kicks into the discussion we had about supporting newer systemd features selectively... Shipping different static files for distributions and/or systemd versions duplicates the number of files.

Signature · 3 lines
main(a){char*c=/*    Schoene Gruesse                         */"B?IJj;MEH"
"CX:;",b;for(a/*    Best regards             my address:    */=0;b=c[a++];)
putchar(b-1/(/*    Chris            cc -ox -xc - && ./x    */b/42*2-3)*42);}
#7 lore raw

Re: [Openvpn-devel] [RFC 0/4] add netlink support for Linux: update

Hi Christian,

Quoted 9 lines · Christian Hesse

Antonio Quartulli <[email protected]...> on Fri, 2018/04/06 15:43:

Quoted 3 lines

Two new files, namely networking_sitnl.c and networking_ip.c, provides two implementations for this API: one uses the new sitnl code (netlink) and one uses iproute2.

This complicates the situation for my followup code: Running the process with unprivileged user works with netlink interface only. If we want to support netlink and iproute2 we end up with creating the files from templates (or carry static files in at least two versions).

Keeping support for iproute2 is part of our agreement during the discussion at the last hackathon. Some of the reasons might be summarized in the hackathon page on the wiki. Therefore, we need to find a way to deal with that.

Quoted 5 lines

This kicks into the discussion we had about supporting newer systemd features selectively... Shipping different static files for distributions and/or systemd versions duplicates the number of files.

I am not into systemd, therefore I am not able to comment on the strategy we need to adopt.

However, what I imagine is that each distribution, when deciding what library to use (sitnl vs iproute2), will also decide which of the provided unit files to ship (if we have multiple precompiled files). Or our Makefile should generate the right ones based on the --enable-iproute2 switch (maybe this is what you meant with templates?).

Cheers,

Signature · 1 line
Antonio Quartulli
#8 lore raw

Re: [Openvpn-devel] [RFC 0/4] add netlink support for Linux: update

Hi,

Quoted 3 lines · Antonio Quartulli

However, what I imagine is that each distribution, when deciding what library to use (sitnl vs iproute2), will also decide which of the provided unit files to ship (if we have multiple precompiled files).

This is how I envisioned how the alternatives would look like - if you compile yourself, it's your own responsibility, but for the distro maintainers, they need to ensure that different pieces match.

Quoted 2 lines

Or our Makefile should generate the right ones based on the --enable-iproute2 switch (maybe this is what you meant with templates?).

That would be an interesting idea :-) - not sure it's worth the extra complications, though.

gert

Signature · 6 lines
"If was one thing all people took for granted, was conviction that if you 
 feed honest figures into a computer, honest figures come out. Never doubted 
 it myself till I met a computer with a sense of humor."
                             Robert A. Heinlein, The Moon is a Harsh Mistress

Gert Doering - Munich, Germany                             [email protected]...
#9 lore raw

Re: [Openvpn-devel] [RFC 0/4] add netlink support for Linux: update

In reply to Antonio Quartulli
Quoted 5 lines · Antonio Quartulli
Quoted 4 lines

This kicks into the discussion we had about supporting newer systemd features selectively... Shipping different static files for distributions and/or systemd versions duplicates the number of files.

Quoted 8 lines

I am not into systemd, therefore I am not able to comment on the strategy we need to adopt.

However, what I imagine is that each distribution, when deciding what library to use (sitnl vs iproute2), will also decide which of the provided unit files to ship (if we have multiple precompiled files). Or our Makefile should generate the right ones based on the --enable-iproute2 switch (maybe this is what you meant with templates?).

Systemd is developing quite fast, and is consistently improving on the security side - with more and more interesting lock-down features, most which can be automated if enabled correctly in the unit files. But newer features may not work so well on older systemd releases.

So this is actually a two-fold challenge

- How to figure out which features systemd supports? We can here presume the host building the package runs the systemd version OpenVPN needs to integrate against.

- How to output/generate unit files which are consistent with the available features?

We will most likely need some kind of template solution to achieve this. The template approach used with Makefile.am/Makefile.in is too limited for our need - that's essentially just a wrapped in sed, which replaces defined variables with something else.

There's plenty of other alternatives as well. But that can easily mean increasing the build time dependencies. I'm not convinced that is the right approach for this need.

I've been pondering on what would be the best approach ... using plain bash with friends (awk, sed, etc), using Python (lots of template engine alternatives, some are built-in) or even possibilities with XML+XSLT. All of these required tools for either approach are mostly available by default on most Liux distributions. Since systemd is Linux only, that's the base restriction. All of these alternatives have some pros, but more cons.

But in the end, I believe that currently it is probably better to have a simple shell script doing the generation.

A unit file typically consists of three sections (Unit, Service and Install). Now, the Service section is the one which will be mostly modified. In the Unit section, only the description is slightly modified between server and client variants.

Such a generator script typically need to have some kind of "feature matrix" which enlists which features we're interested in using in the currently available systemd version on the system. Then pass this "detected features" to a function which creates the [Service] section on-the-fly and dumps everything to stdout. The [Unit] section to use would be determined by the role (client or server), which could be an argument to the script. This role would need to also be used when creating the [Service] section too.

Thoughts?

Signature · 4 lines
kind regards,

David Sommerseth
OpenVPN Inc
#10 lore raw

Re: [Openvpn-devel] [RFC 0/4] add netlink support for Linux: update

Quoted 3 lines · David Sommerseth

But in the end, I believe that currently it is probably better to have a simple shell script doing the generation.

+1

Unless we have to create something quite complex (not the case here) that needs further extensions in the future (probably not the case too) we should keep this simple and use a plain bash script.

my 2 cents.

Cheers,

Signature · 1 line
Antonio Quartulli