[Openvpn-devel,2/8] implement networking API for iproute2

Message ID 20180420111624.7230-3-a@unstable.cc
State Superseded
Headers show
Series add netlink support for Linux | expand

Commit Message

Antonio Quartulli April 20, 2018, 1:16 a.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_ip.c | 369 ++++++++++++++++++++++++++++++++++++
 2 files changed, 370 insertions(+), 1 deletion(-)
 create mode 100644 src/openvpn/networking_ip.c

Comments

Antonio Quartulli April 20, 2018, 6:41 p.m. UTC | #1
Hi,

there is one little "open" thing in this patch that I wanted to
highlight. See below:


On 20/04/18 19:16, Antonio Quartulli wrote:
> +static struct env_set *es = NULL;
> +
.....
> +    openvpn_execve_check(&argv, es, S_FATAL, "Linux ip link set failed");

Right now the code initializing the environment set to be passed to
openvpn_execve() with NULL.
This means that not ENV is passed to the iproute commands.

This behaviour does not reflect the original one as in the current code
we always pass the complete ENV coming from the openvpn context.

This begs two questions:

1) how can we pass the env_set without making the API dependent on that?
(this is really just an iproute thing, unlreated to the networking side
of things)

2) do we really need to pass the entire ENV to iproute commands?

I understand it is needed for the scripts, but why passing it to these
commands too?
Shouldn't we expect them to work by using only the commandline arguments?


Cheers,

Patch

diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am
index c94296d3..6d9cde3e 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_ip.c networking.h \
 	ntlm.c ntlm.h \
 	occ.c occ.h occ-inline.h \
 	openssl_compat.h \
diff --git a/src/openvpn/networking_ip.c b/src/openvpn/networking_ip.c
new file mode 100644
index 00000000..bfe7b459
--- /dev/null
+++ b/src/openvpn/networking_ip.c
@@ -0,0 +1,369 @@ 
+/*
+ *  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_ip.h"
+#include "misc.h"
+#include "socket.h"
+
+#include <stdbool.h>
+#include <netinet/in.h>
+
+static struct env_set *es = NULL;
+
+int
+net_iface_up(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, es, S_FATAL, "Linux ip link set failed");
+
+    argv_reset(&argv);
+
+    return 0;
+}
+
+int
+net_iface_mtu_set(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, es, S_FATAL, "Linux ip link set failed");
+
+    return 0;
+}
+
+int
+net_addr_v4_add(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, es, S_FATAL, "Linux ip addr add failed");
+
+    free(addr_str);
+    free(brd_str);
+
+    argv_reset(&argv);
+
+    return 0;
+}
+
+int
+net_addr_v6_add(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, es, S_FATAL, "Linux ip -6 addr add failed");
+
+    free(addr_str);
+
+    argv_reset(&argv);
+
+    return 0;
+}
+
+int
+net_addr_v4_del(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, NULL, 0, "Linux ip addr del failed");
+
+    free(addr_str);
+
+    argv_reset(&argv);
+
+    return 0;
+}
+
+int
+net_addr_v6_del(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, NULL, 0, "Linux ip -6 addr del failed");
+
+    free(addr_str);
+
+    argv_reset(&argv);
+
+    return 0;
+}
+
+int
+net_addr_ptp_v4_add(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, 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(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, NULL, 0, "Linux ip addr del failed");
+
+    free(local_str);
+    free(remote_str);
+
+    argv_reset(&argv);
+
+    return 0;
+}
+
+int
+net_route_v4_add(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, es, 0, "ERROR: Linux route add command failed");
+
+    free(dst_str);
+
+    argv_reset(&argv);
+
+    return 0;
+}
+
+int
+net_route_v6_add(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, es, 0, "ERROR: Linux route -6 add command failed");
+
+    free(dst_str);
+
+    argv_reset(&argv);
+
+    return 0;
+}
+
+int
+net_route_v4_del(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, es, 0, "ERROR: Linux route delete command failed");
+
+    free(dst_str);
+
+    argv_reset(&argv);
+
+    return 0;
+}
+
+int
+net_route_v6_del(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, es, 0, "ERROR: Linux route -6 del command failed");
+
+    free(dst_str);
+
+    argv_reset(&argv);
+
+    return 0;
+}
+
+int
+net_route_v4_best_gw(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 */