[Openvpn-devel,v3,1/7] implement networking API for iproute2

Message ID 20181219050118.6568-2-a@unstable.cc
State Accepted
Headers show
Series introduce networking API and add netlink support for Linux | expand

Commit Message

Antonio Quartulli Dec. 18, 2018, 6:01 p.m. UTC
iproute2 is the first user of the new networking API and
its one of the two currently supported functionalities on
Linux (the other being net-tools).

This patch simply copies the current code from tun.c/route.c
to networking_ip.c without introducing any funcional
change to the code.

Signed-off-by: Antonio Quartulli <a@unstable.cc>
---
 src/openvpn/Makefile.am           |   2 +-
 src/openvpn/networking.h          |   2 +-
 src/openvpn/networking_iproute2.c | 386 ++++++++++++++++++++++++++++++
 src/openvpn/networking_iproute2.h |  36 +++
 4 files changed, 424 insertions(+), 2 deletions(-)
 create mode 100644 src/openvpn/networking_iproute2.c
 create mode 100644 src/openvpn/networking_iproute2.h

Comments

Arne Schwabe May 8, 2019, 4:36 a.m. UTC | #1
Am 19.12.18 um 06:01 schrieb Antonio Quartulli:
> iproute2 is the first user of the new networking API and
> its one of the two currently supported functionalities on
> Linux (the other being net-tools).
> 
> This patch simply copies the current code from tun.c/route.c
> to networking_ip.c without introducing any funcional
> change to the code.

Should be networking_iproute2.c in the commit message too.

Otherise:

Acked-By: Arne Schwabe <arne@rfc2549.org>
Gert Doering May 10, 2019, 3:59 a.m. UTC | #2
Hi,

On Wed, Dec 19, 2018 at 03:01:12PM +1000, Antonio Quartulli wrote:
> iproute2 is the first user of the new networking API and
> its one of the two currently supported functionalities on
> Linux (the other being net-tools).
> 
> This patch simply copies the current code from tun.c/route.c
> to networking_ip.c without introducing any funcional
> change to the code.
> 
> Signed-off-by: Antonio Quartulli <a@unstable.cc>
> ---
>  src/openvpn/Makefile.am           |   2 +-
>  src/openvpn/networking.h          |   2 +-

"something is missing".  It is nice that Arne ACKed this, but this
patch does not apply to master, because we do not have a networking.h
that it could modify...

I have quickly checked my inbox for other patch sets that would
introduce "networking.h" but the only place I can see this mentioned
is this patch set - so could you please send a "-1/7" that makes
this patchset apply?

Or rebase on a clean "master", re-test, and re-send as v4?

gert
Gert Doering May 15, 2019, 9:28 a.m. UTC | #3
Your patch has been applied to the master branch.

I'm not really happy with this patch - it adds lots of code which is
not used yet.  It claims that this is "the code from route.c / tun.c",
but as it is not moved over but just copied, "git diff --color-moved=zebra"
won't help in actually verifying that it is, indeed, the same code - and
it cannot be tested because it is never called...  so this is not a good
patch granularity.  But since Arne has already ACKed the whole series,
I'm not holding this up, just grumbling a bit, hoping for a slightly
more convincing (= there is a way to actually test the code changes every
single commit brings) granularity in future series.

Oh, and as a side note - can we please stop configuring broadcast
addresses?  This was silly 15 years ago already...  the machine knows
quite well what the broadcast address is.

+    char *brd_str = (char *)print_in_addr_t(*broadcast, 0, NULL);
+
+    argv_printf(&argv, "%s addr add dev %s %s/%d broadcast %s", iproute_path,
+                iface, addr_str, prefixlen, brd_str);

.. and as yet another side note - this code introduces string allocations
using (implicit) malloc() and free()...  so this is very much *different*
code than "what we have in route.c/tun.c" - and it questions the design
of this API if every single backend *except* sitnl will have to start
doing local memory management.  ** This needs to be fixed **.


commit 678111936ffb33992684dd3b96dc5b21693dfa58
Author: Antonio Quartulli
Date:   Wed Dec 19 15:01:12 2018 +1000

     implement networking API for iproute2

     Signed-off-by: Antonio Quartulli <a@unstable.cc>
     Acked-by: Arne Schwabe <arne@rfc2549.org>
     Message-Id: <20181219050118.6568-2-a@unstable.cc>
     URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg18031.html
     Signed-off-by: Gert Doering <gert@greenie.muc.de>


--
kind regards,

Gert Doering

Patch

diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am
index 8afc4146..143bcf5c 100644
--- a/src/openvpn/Makefile.am
+++ b/src/openvpn/Makefile.am
@@ -80,7 +80,7 @@  openvpn_SOURCES = \
 	mtu.c mtu.h \
 	mudp.c mudp.h \
 	multi.c multi.h \
-	networking.h \
+	networking_iproute2.c networking_iproute2.h networking.h \
 	ntlm.c ntlm.h \
 	occ.c occ.h \
 	openssl_compat.h \
diff --git a/src/openvpn/networking.h b/src/openvpn/networking.h
index 716e61a5..4f0167e7 100644
--- a/src/openvpn/networking.h
+++ b/src/openvpn/networking.h
@@ -34,7 +34,7 @@  struct context;
 #ifdef ENABLE_SITNL
 #include "networking_sitnl.h"
 #elif ENABLE_IPROUTE
-#include "networking_ip.h"
+#include "networking_iproute2.h"
 #else
 /* define mock types to ensure code builds on any platform */
 typedef void * openvpn_net_ctx_t;
diff --git a/src/openvpn/networking_iproute2.c b/src/openvpn/networking_iproute2.c
new file mode 100644
index 00000000..a5a6585f
--- /dev/null
+++ b/src/openvpn/networking_iproute2.c
@@ -0,0 +1,386 @@ 
+/*
+ *  Networking API implementation for iproute2
+ *
+ *  Copyright (C) 2018 Antonio Quartulli <a@unstable.cc>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program (see the file COPYING included with this
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#elif defined(_MSC_VER)
+#include "config-msvc.h"
+#endif
+
+#if defined(TARGET_LINUX) && defined(ENABLE_IPROUTE)
+
+#include "syshead.h"
+
+#include "networking.h"
+#include "networking_iproute2.h"
+#include "misc.h"
+#include "openvpn.h"
+#include "run_command.h"
+#include "socket.h"
+
+#include <stdbool.h>
+#include <netinet/in.h>
+
+int
+net_ctx_init(struct context *c, openvpn_net_ctx_t *ctx)
+{
+    ctx->es = NULL;
+    if (c)
+        ctx->es = c->es;
+
+    return 0;
+}
+
+int
+net_iface_up(openvpn_net_ctx_t *ctx, const char *iface, bool up)
+{
+    struct argv argv = argv_new();
+
+    argv_printf(&argv, "%s link set dev %s %s", iproute_path, iface,
+                up ? "up" : "down");
+    argv_msg(M_INFO, &argv);
+    openvpn_execve_check(&argv, ctx->es, S_FATAL, "Linux ip link set failed");
+
+    argv_reset(&argv);
+
+    return 0;
+}
+
+int
+net_iface_mtu_set(openvpn_net_ctx_t *ctx, const char *iface, uint32_t mtu)
+{
+    struct argv argv = argv_new();
+
+    argv_printf(&argv, "%s link set dev %s up mtu %d", iproute_path, iface,
+                mtu);
+    argv_msg(M_INFO, &argv);
+    openvpn_execve_check(&argv, ctx->es, S_FATAL, "Linux ip link set failed");
+
+    return 0;
+}
+
+int
+net_addr_v4_add(openvpn_net_ctx_t *ctx, const char *iface,
+                const in_addr_t *addr, int prefixlen,
+                const in_addr_t *broadcast)
+{
+    struct argv argv = argv_new();
+
+    char *addr_str = (char *)print_in_addr_t(*addr, 0, NULL);
+    char *brd_str = (char *)print_in_addr_t(*broadcast, 0, NULL);
+
+    argv_printf(&argv, "%s addr add dev %s %s/%d broadcast %s", iproute_path,
+                iface, addr_str, prefixlen, brd_str);
+    argv_msg(M_INFO, &argv);
+    openvpn_execve_check(&argv, ctx->es, S_FATAL, "Linux ip addr add failed");
+
+    free(addr_str);
+    free(brd_str);
+
+    argv_reset(&argv);
+
+    return 0;
+}
+
+int
+net_addr_v6_add(openvpn_net_ctx_t *ctx, const char *iface,
+                const struct in6_addr *addr, int prefixlen)
+{
+    struct argv argv = argv_new();
+    char *addr_str = (char *)print_in6_addr(*addr, 0, NULL);
+
+    argv_printf(&argv, "%s -6 addr add %s/%d dev %s", iproute_path, addr_str,
+                prefixlen, iface);
+    argv_msg(M_INFO, &argv);
+    openvpn_execve_check(&argv, ctx->es, S_FATAL,
+                         "Linux ip -6 addr add failed");
+
+    free(addr_str);
+
+    argv_reset(&argv);
+
+    return 0;
+}
+
+int
+net_addr_v4_del(openvpn_net_ctx_t *ctx, const char *iface,
+                const in_addr_t *addr, int prefixlen)
+{
+    struct argv argv = argv_new();
+    char *addr_str = (char *)print_in_addr_t(*addr, 0, NULL);
+
+    argv_printf(&argv, "%s addr del dev %s %s/%d", iproute_path, iface,
+                addr_str, prefixlen);
+
+    argv_msg(M_INFO, &argv);
+    openvpn_execve_check(&argv, ctx->es, 0, "Linux ip addr del failed");
+
+    free(addr_str);
+
+    argv_reset(&argv);
+
+    return 0;
+}
+
+int
+net_addr_v6_del(openvpn_net_ctx_t *ctx, const char *iface,
+                const struct in6_addr *addr, int prefixlen)
+{
+    struct argv argv = argv_new();
+    char *addr_str = (char *)print_in6_addr(*addr, 0, NULL);
+
+    argv_printf(&argv, "%s -6 addr del %s/%d dev %s", iproute_path,
+                addr_str, prefixlen, iface);
+    argv_msg(M_INFO, &argv);
+    openvpn_execve_check(&argv, ctx->es, 0, "Linux ip -6 addr del failed");
+
+    free(addr_str);
+
+    argv_reset(&argv);
+
+    return 0;
+}
+
+int
+net_addr_ptp_v4_add(openvpn_net_ctx_t *ctx, const char *iface,
+                    const in_addr_t *local, const in_addr_t *remote)
+{
+    struct argv argv = argv_new();
+    char *local_str = (char *)print_in_addr_t(*local, 0, NULL);
+    char *remote_str = (char *)print_in_addr_t(*remote, 0, NULL);
+
+    argv_printf(&argv, "%s addr add dev %s local %s peer %s", iproute_path,
+                iface, local_str, remote_str);
+    argv_msg(M_INFO, &argv);
+    openvpn_execve_check(&argv, ctx->es, S_FATAL, "Linux ip addr add failed");
+
+    free(local_str);
+    free(remote_str);
+
+    argv_reset(&argv);
+
+    return 0;
+}
+
+int
+net_addr_ptp_v4_del(openvpn_net_ctx_t *ctx, const char *iface,
+                    const in_addr_t *local, const in_addr_t *remote)
+{
+    struct argv argv = argv_new();
+    char *local_str = (char *)print_in_addr_t(*local, 0, NULL);
+    char *remote_str = (char *)print_in_addr_t(*remote, 0, NULL);
+
+    argv_printf(&argv, "%s addr del dev %s local %s peer %s", iproute_path,
+                iface, local_str, remote_str);
+    argv_msg(M_INFO, &argv);
+    openvpn_execve_check(&argv, ctx->es, 0, "Linux ip addr del failed");
+
+    free(local_str);
+    free(remote_str);
+
+    argv_reset(&argv);
+
+    return 0;
+}
+
+int
+net_route_v4_add(openvpn_net_ctx_t *ctx, const in_addr_t *dst, int prefixlen,
+                 const in_addr_t *gw, const char *iface, uint32_t table,
+                 int metric)
+{
+    struct argv argv = argv_new();
+    char *dst_str = (char *)print_in_addr_t(*dst, 0, NULL);
+
+    argv_printf(&argv, "%s route add %s/%d", iproute_path, dst_str, prefixlen);
+
+    if (metric > 0)
+        argv_printf_cat(&argv, "metric %d", metric);
+
+    if (iface)
+        argv_printf_cat(&argv, "dev %s", iface);
+
+    if (gw)
+    {
+        char *gw_str = (char *)print_in_addr_t(*gw, 0, NULL);
+
+        argv_printf_cat(&argv, "via %s", gw_str);
+
+        free(gw_str);
+    }
+
+    argv_msg(D_ROUTE, &argv);
+    openvpn_execve_check(&argv, ctx->es, 0, "ERROR: Linux route add command failed");
+
+    free(dst_str);
+
+    argv_reset(&argv);
+
+    return 0;
+}
+
+int
+net_route_v6_add(openvpn_net_ctx_t *ctx, const struct in6_addr *dst,
+                 int prefixlen, const struct in6_addr *gw, const char *iface,
+                 uint32_t table, int metric)
+{
+    struct argv argv = argv_new();
+    char *dst_str = (char *)print_in6_addr(*dst, 0, NULL);
+
+    argv_printf(&argv, "%s -6 route add %s/%d dev %s", iproute_path, dst_str,
+                prefixlen, iface);
+
+    if (gw)
+    {
+        char *gw_str = (char *)print_in6_addr(*gw, 0, NULL);
+
+        argv_printf_cat(&argv, "via %s", gw_str);
+
+        free(gw_str);
+    }
+
+    if (metric > 0)
+        argv_printf_cat(&argv, "metric %d", metric);
+
+    argv_msg(D_ROUTE, &argv);
+    openvpn_execve_check(&argv, ctx->es, 0, "ERROR: Linux route -6 add command failed");
+
+    free(dst_str);
+
+    argv_reset(&argv);
+
+    return 0;
+}
+
+int
+net_route_v4_del(openvpn_net_ctx_t *ctx, const in_addr_t *dst, int prefixlen,
+                 const in_addr_t *gw, const char *iface, uint32_t table,
+                 int metric)
+{
+    struct argv argv = argv_new();
+    char *dst_str = (char *)print_in_addr_t(*dst, 0, NULL);
+
+    argv_printf(&argv, "%s route del %s/%d", iproute_path, dst_str, prefixlen);
+
+    if (metric > 0)
+        argv_printf_cat(&argv, "metric %d", metric);
+
+    argv_msg(D_ROUTE, &argv);
+    openvpn_execve_check(&argv, ctx->es, 0, "ERROR: Linux route delete command failed");
+
+    free(dst_str);
+
+    argv_reset(&argv);
+
+    return 0;
+}
+
+int
+net_route_v6_del(openvpn_net_ctx_t *ctx, const struct in6_addr *dst,
+                 int prefixlen, const struct in6_addr *gw, const char *iface,
+                 uint32_t table, int metric)
+{
+    struct argv argv = argv_new();
+    char *dst_str = (char *)print_in6_addr(*dst, 0, NULL);
+
+    argv_printf(&argv, "%s -6 route del %s/%d dev %s", iproute_path, dst_str,
+                prefixlen, iface);
+
+    if (gw)
+    {
+        char *gw_str = (char *)print_in6_addr(*gw, 0, NULL);
+
+        argv_printf_cat(&argv, "via %s", gw_str);
+
+        free(gw_str);
+    }
+
+    if (metric > 0)
+        argv_printf_cat(&argv, "metric %d", metric);
+
+    argv_msg(D_ROUTE, &argv);
+    openvpn_execve_check(&argv, ctx->es, 0, "ERROR: Linux route -6 del command failed");
+
+    free(dst_str);
+
+    argv_reset(&argv);
+
+    return 0;
+}
+
+int
+net_route_v4_best_gw(openvpn_net_ctx_t *ctx, const in_addr_t *dst,
+                     int prefixlen, in_addr_t *best_gw, char *best_iface)
+{
+    best_iface[0] = '\0';
+
+    FILE *fp = fopen("/proc/net/route", "r");
+    if (!fp)
+        return -1;
+
+    char line[256];
+    int count = 0;
+    unsigned int lowest_metric = UINT_MAX;
+    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)
+                {
+                    *best_gw = gw;
+                    strcpy(best_iface, name);
+                    lowest_metric = metric;
+                }
+            }
+        }
+        ++count;
+    }
+    fclose(fp);
+
+    return 0;
+}
+
+/*
+ * The following function is not implemented in the iproute backend as it
+ * already uses netlink in route.c.
+ *
+ * int
+ * net_route_v6_best_gw(const struct in6_addr *dst, int prefixlen,
+ *                      struct in6_addr *best_gw, char *best_iface)
+ */
+
+#endif /* ENABLE_IPROUTE && TARGET_LINUX */
diff --git a/src/openvpn/networking_iproute2.h b/src/openvpn/networking_iproute2.h
new file mode 100644
index 00000000..47b50a9f
--- /dev/null
+++ b/src/openvpn/networking_iproute2.h
@@ -0,0 +1,36 @@ 
+/*
+ *  Generic interface to platform specific networking code
+ *
+ *  Copyright (C) 2016-2018 Antonio Quartulli <a@unstable.cc>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program (see the file COPYING included with this
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef NETWORKING_IP_H_
+#define NETWORKING_IP_H_
+
+#include "env_set.h"
+
+typedef char openvpn_net_iface_t;
+
+struct openvpn_net_ctx
+{
+    struct env_set *es;
+};
+
+typedef struct openvpn_net_ctx openvpn_net_ctx_t;
+
+#endif /* NETWORKING_IP_H_ */