[network-manager-openvpn] import/export: import and export "route" option (bgo #753578)



commit 4eb5f3ad43cdc62c6d4d254731e24c90b87ba91a
Author: Jiří Klimeš <jklimes redhat com>
Date:   Wed Nov 18 17:18:17 2015 +0100

    import/export: import and export "route" option (bgo #753578)
    
    We do not allow openvpn to configure routes (--routes-noexec), but let's
    configure these static routes in ipv4 setting.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=753578

 properties/import-export.c            |  174 +++++++++++++++++++++++++++++++++
 properties/tests/conf/Makefile.am     |    3 +-
 properties/tests/conf/route.ovpn      |   26 +++++
 properties/tests/test-import-export.c |  150 ++++++++++++++++++++++++-----
 4 files changed, 328 insertions(+), 25 deletions(-)
---
diff --git a/properties/import-export.c b/properties/import-export.c
index ceccae2..bad0369 100644
--- a/properties/import-export.c
+++ b/properties/import-export.c
@@ -27,6 +27,7 @@
 #include <sys/types.h>
 #include <stdlib.h>
 #include <sys/stat.h>
+#include <arpa/inet.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
@@ -40,8 +41,12 @@
 #include <nm-setting-vpn.h>
 #include <nm-setting-connection.h>
 #include <nm-setting-ip4-config.h>
+#include <nm-utils.h>
 
 #define nm_simple_connection_new nm_connection_new
+#define NM_SETTING_IP_CONFIG NM_SETTING_IP4_CONFIG
+#define NM_SETTING_IP_CONFIG_METHOD NM_SETTING_IP4_CONFIG_METHOD
+#define NMSettingIPConfig NMSettingIP4Config
 
 #define OPENVPN_EDITOR_PLUGIN_ERROR                     NM_SETTING_VPN_ERROR
 #define OPENVPN_EDITOR_PLUGIN_ERROR_FILE_NOT_OPENVPN    NM_SETTING_VPN_ERROR_UNKNOWN
@@ -101,6 +106,7 @@
 #define TLS_REMOTE_TAG "tls-remote "
 #define REMOTE_CERT_TLS_TAG "remote-cert-tls "
 #define TUNMTU_TAG "tun-mtu "
+#define ROUTE_TAG "route "
 
 
 static char *
@@ -448,11 +454,26 @@ handle_num_seconds_item (const char *line,
        return FALSE;
 }
 
+static gboolean
+parse_ip (const char *str, const char *line, guint32 *out_ip)
+{
+       struct in_addr ip;
+
+       if (inet_pton (AF_INET, str, &ip) <= 0) {
+               g_warning ("%s: invalid IP '%s' in option '%s'", __func__, str, line);
+               return FALSE;
+       }
+       if (out_ip)
+               *out_ip = ip.s_addr;
+       return TRUE;
+}
+
 NMConnection *
 do_import (const char *path, char **lines, GError **error)
 {
        NMConnection *connection = NULL;
        NMSettingConnection *s_con;
+       NMSettingIPConfig *s_ip4;
        NMSettingVpn *s_vpn;
        char *last_dot;
        char **line;
@@ -467,6 +488,9 @@ do_import (const char *path, char **lines, GError **error)
        connection = nm_simple_connection_new ();
        s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
        nm_connection_add_setting (connection, NM_SETTING (s_con));
+       s_ip4 = NM_SETTING_IP_CONFIG (nm_setting_ip4_config_new ());
+       nm_connection_add_setting (connection, NM_SETTING (s_ip4));
+       g_object_set (s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL);
 
        s_vpn = NM_SETTING_VPN (nm_setting_vpn_new ());
 
@@ -926,6 +950,105 @@ do_import (const char *path, char **lines, GError **error)
                        g_strfreev (items);
                        continue;
                }
+
+#ifdef NM_OPENVPN_OLD
+               if (!strncmp (*line, ROUTE_TAG, strlen (ROUTE_TAG))) {
+                       items = get_args (*line + strlen (ROUTE_TAG), &nitems);
+                       if (nitems >= 1 && nitems <= 4) {
+                               guint32 dest, next_hop, prefix, metric;
+                               NMIP4Route *route;
+
+                               if (!parse_ip (items[0], *line, &dest))
+                                       goto route_fail;
+
+                               /* init default values */
+                               next_hop = 0;
+                               prefix = 32;
+                               metric = 0;
+                               if (nitems >= 2) {
+                                       if (!parse_ip (items[1], *line, &prefix))
+                                               goto route_fail;
+                                       prefix = nm_utils_ip4_netmask_to_prefix (prefix);
+                                       if (nitems >= 3) {
+                                               if (!parse_ip (items[2], *line, &next_hop))
+                                                       goto route_fail;
+                                               if (nitems == 4) {
+                                                       long num;
+                                                       errno = 0;
+                                                       num = strtol (items[3], NULL, 10);
+                                                       if ((errno == 0) && (num >= 0) && (num <= 65535))
+                                                               metric = (guint32) num;
+                                                       else {
+                                                               g_warning ("%s: invalid metric '%s' in option 
'%s'",
+                                                                          __func__, items[3], *line);
+                                                               goto route_fail;
+                                                       }
+                                               }
+                                       }
+                               }
+
+                               route = nm_ip4_route_new ();
+                               nm_ip4_route_set_dest (route, dest);
+                               nm_ip4_route_set_prefix (route, prefix);
+                               nm_ip4_route_set_next_hop (route, next_hop);
+                               nm_ip4_route_set_metric (route, metric);
+                               nm_setting_ip4_config_add_route (s_ip4, route);
+                               nm_ip4_route_unref (route);
+                       } else
+                               g_warning ("%s: invalid number of arguments in option '%s'", __func__, *line);
+
+route_fail:
+                       g_strfreev (items);
+                       continue;
+               }
+#else
+               if (!strncmp (*line, ROUTE_TAG, strlen (ROUTE_TAG))) {
+                       items = get_args (*line + strlen (ROUTE_TAG), &nitems);
+                       if (nitems >= 1 && nitems <= 4) {
+                               guint32 prefix = 32;
+                               guint32 metric = 0;
+                               const char *dest = items[0];
+                               const char *next_hop = "0.0.0.0";
+                               NMIPRoute *route;
+
+                               if (!parse_ip (items[0], *line, NULL))
+                                       goto route_fail;
+
+                               if (nitems >= 2) {
+                                       if (!parse_ip (items[1], *line, &prefix))
+                                               goto route_fail;
+                                       prefix = nm_utils_ip4_netmask_to_prefix (prefix);
+                                       if (nitems >= 3) {
+                                               if (!parse_ip (items[2], *line, NULL))
+                                                       goto route_fail;
+                                               next_hop = items[2];
+                                               if (nitems == 4) {
+                                                       long num;
+                                                       errno = 0;
+                                                       num = strtol (items[3], NULL, 10);
+                                                       if ((errno == 0) && (num >= 0) && (num <= 65535))
+                                                               metric = (guint32) num;
+                                                       else {
+                                                               g_warning ("%s: invalid metric '%s' in option 
'%s'",
+                                                                          __func__, items[3], *line);
+                                                               goto route_fail;
+                                                       }
+                                               }
+                                       }
+                               }
+
+                               route = nm_ip_route_new (AF_INET, dest, prefix, next_hop, metric, NULL);
+                               nm_setting_ip_config_add_route (s_ip4, route);
+                               nm_ip_route_unref (route);
+                       } else
+                               g_warning ("%s: invalid number of arguments in option '%s'", __func__, *line);
+
+route_fail:
+                       g_strfreev (items);
+                       continue;
+               }
+#endif
+
        }
 
        if (!have_client && !have_sk) {
@@ -1007,6 +1130,7 @@ gboolean
 do_export (const char *path, NMConnection *connection, GError **error)
 {
        NMSettingConnection *s_con;
+       NMSettingIPConfig *s_ip4;
        NMSettingVpn *s_vpn;
        FILE *f;
        const char *value;
@@ -1047,6 +1171,7 @@ do_export (const char *path, NMConnection *connection, GError **error)
        const char *proxy_retry = NULL;
        const char *proxy_username = NULL;
        const char *proxy_password = NULL;
+       int i;
 
        s_con = nm_connection_get_setting_connection (connection);
        g_assert (s_con);
@@ -1366,6 +1491,55 @@ do_export (const char *path, NMConnection *connection, GError **error)
                }
        }
 
+#ifdef NM_OPENVPN_OLD
+       /* Route handling is different in libnm-util and libnm */
+       /* Static routes */
+       s_ip4 = nm_connection_get_setting_ip4_config (connection);
+       for (i = 0; s_ip4 && i < nm_setting_ip4_config_get_num_routes (s_ip4); i++) {
+               char dest_str[INET_ADDRSTRLEN];
+               char netmask_str[INET_ADDRSTRLEN];
+               char next_hop_str[INET_ADDRSTRLEN];
+               guint32 dest, netmask, next_hop;
+               NMIP4Route *route = nm_setting_ip4_config_get_route (s_ip4, i);
+
+               dest = nm_ip4_route_get_dest (route);
+               memset (dest_str, '\0', sizeof (dest_str));
+               inet_ntop (AF_INET, (const void *) &dest, dest_str, sizeof (dest_str));
+
+               memset (netmask_str, '\0', sizeof (netmask_str));
+               netmask = nm_utils_ip4_prefix_to_netmask (nm_ip4_route_get_prefix (route));
+               inet_ntop (AF_INET, (const void *) &netmask, netmask_str, sizeof (netmask_str));
+
+               next_hop = nm_ip4_route_get_next_hop (route);
+               memset (next_hop_str, '\0', sizeof (next_hop_str));
+               inet_ntop (AF_INET, (const void *) &next_hop, next_hop_str, sizeof (next_hop_str));
+
+               fprintf (f, "route %s %s %s %u\n",
+                        dest_str,
+                        netmask_str,
+                        next_hop_str,
+                        nm_ip4_route_get_metric (route));
+       }
+#else
+       /* Static routes */
+       s_ip4 = nm_connection_get_setting_ip4_config (connection);
+       for (i = 0; s_ip4 && i < nm_setting_ip_config_get_num_routes (s_ip4); i++) {
+               char netmask_str[INET_ADDRSTRLEN];
+               guint32 netmask;
+               NMIPRoute *route = nm_setting_ip_config_get_route (s_ip4, i);
+
+               memset (netmask_str, '\0', sizeof (netmask_str));
+               netmask = nm_utils_ip4_prefix_to_netmask (nm_ip_route_get_prefix (route));
+               inet_ntop (AF_INET, (const void *) &netmask, netmask_str, sizeof (netmask_str));
+
+               fprintf (f, "route %s %s %s %ld\n",
+                        nm_ip_route_get_dest (route),
+                        netmask_str,
+                        nm_ip_route_get_next_hop (route) ? nm_ip_route_get_next_hop (route) : "0.0.0.0",
+                        nm_ip_route_get_metric (route));
+       }
+#endif
+
        /* Add hard-coded stuff */
        fprintf (f,
                 "nobind\n"
diff --git a/properties/tests/conf/Makefile.am b/properties/tests/conf/Makefile.am
index a91c743..ee12b43 100644
--- a/properties/tests/conf/Makefile.am
+++ b/properties/tests/conf/Makefile.am
@@ -17,4 +17,5 @@ EXTRA_DIST = \
        device-notype.ovpn \
        keepalive.ovpn \
        ping-with-exit.ovpn \
-       ping-with-restart.ovpn
+       ping-with-restart.ovpn \
+       route.ovpn
diff --git a/properties/tests/conf/route.ovpn b/properties/tests/conf/route.ovpn
new file mode 100644
index 0000000..4c09c3d
--- /dev/null
+++ b/properties/tests/conf/route.ovpn
@@ -0,0 +1,26 @@
+route 1.2.3.0 255.255.255.0 1.2.3.254 99
+route 5.6.7.8 255.255.255.252
+route 192.168.0.0 255.255.0.0 192.168.44.1
+
+remote 173.8.149.245
+resolv-retry infinite
+
+dev tun
+persist-key
+persist-tun
+link-mtu 1400
+proto udp
+nobind
+pull
+tls-client
+
+ca keys/mg8.ca
+cert keys/clee.crt
+key keys/clee.key
+
+tls-auth keys/46.key 1
+tls-remote "/CN=myvpn.company.com"
+
+comp-lzo
+verb 3
+
diff --git a/properties/tests/test-import-export.c b/properties/tests/test-import-export.c
index b889717..fb476ca 100644
--- a/properties/tests/test-import-export.c
+++ b/properties/tests/test-import-export.c
@@ -104,7 +104,6 @@ test_password_import (NMVpnEditorPlugin *plugin, const char *dir)
 {
        NMConnection *connection;
        NMSettingConnection *s_con;
-       NMSettingIPConfig *s_ip4;
        NMSettingVpn *s_vpn;
        const char *expected_id = "password";
        char *expected_cacert;
@@ -123,11 +122,6 @@ test_password_import (NMVpnEditorPlugin *plugin, const char *dir)
        ASSERT (nm_setting_connection_get_uuid (s_con) == NULL,
                "password-import", "unexpected valid UUID");
 
-       /* IP4 setting */
-       s_ip4 = nm_connection_get_setting_ip4_config (connection);
-       ASSERT (s_ip4 == NULL,
-               "password-import", "unexpected 'ip4-config' setting");
-
        /* VPN setting */
        s_vpn = nm_connection_get_setting_vpn (connection);
        ASSERT (s_vpn != NULL,
@@ -235,7 +229,6 @@ test_tls_import (NMVpnEditorPlugin *plugin, const char *dir)
 {
        NMConnection *connection;
        NMSettingConnection *s_con;
-       NMSettingIPConfig *s_ip4;
        NMSettingVpn *s_vpn;
        const char *expected_id = "tls";
        char *expected_path;
@@ -254,11 +247,6 @@ test_tls_import (NMVpnEditorPlugin *plugin, const char *dir)
        ASSERT (nm_setting_connection_get_uuid (s_con) == NULL,
                "tls-import", "unexpected valid UUID");
 
-       /* IP4 setting */
-       s_ip4 = nm_connection_get_setting_ip4_config (connection);
-       ASSERT (s_ip4 == NULL,
-               "tls-import", "unexpected 'ip4-config' setting");
-
        /* VPN setting */
        s_vpn = nm_connection_get_setting_vpn (connection);
        ASSERT (s_vpn != NULL,
@@ -351,7 +339,6 @@ test_pkcs12_import (NMVpnEditorPlugin *plugin, const char *dir)
 {
        NMConnection *connection;
        NMSettingConnection *s_con;
-       NMSettingIPConfig *s_ip4;
        NMSettingVpn *s_vpn;
        const char *expected_id = "pkcs12";
        char *expected_path;
@@ -370,11 +357,6 @@ test_pkcs12_import (NMVpnEditorPlugin *plugin, const char *dir)
        ASSERT (nm_setting_connection_get_uuid (s_con) == NULL,
                "pkcs12-import", "unexpected valid UUID");
 
-       /* IP4 setting */
-       s_ip4 = nm_connection_get_setting_ip4_config (connection);
-       ASSERT (s_ip4 == NULL,
-               "pkcs12-import", "unexpected 'ip4-config' setting");
-
        /* VPN setting */
        s_vpn = nm_connection_get_setting_vpn (connection);
        ASSERT (s_vpn != NULL,
@@ -501,7 +483,6 @@ test_static_key_import (NMVpnEditorPlugin *plugin, const char *dir)
 {
        NMConnection *connection;
        NMSettingConnection *s_con;
-       NMSettingIPConfig *s_ip4;
        NMSettingVpn *s_vpn;
        const char *expected_id = "static";
        char *expected_path;
@@ -520,11 +501,6 @@ test_static_key_import (NMVpnEditorPlugin *plugin, const char *dir)
        ASSERT (nm_setting_connection_get_uuid (s_con) == NULL,
                "static-key-import", "unexpected valid UUID");
 
-       /* IP4 setting */
-       s_ip4 = nm_connection_get_setting_ip4_config (connection);
-       ASSERT (s_ip4 == NULL,
-               "static-key-import", "unexpected 'ip4-config' setting");
-
        /* VPN setting */
        s_vpn = nm_connection_get_setting_vpn (connection);
        ASSERT (s_vpn != NULL,
@@ -1107,6 +1083,129 @@ test_device_export (NMVpnEditorPlugin *plugin,
        g_free (path);
 }
 
+static void
+test_route_import (NMVpnEditorPlugin *plugin,
+                   const char *detail,
+                   const char *dir)
+{
+       NMConnection *connection;
+       NMSettingConnection *s_con;
+       NMSettingIPConfig *s_ip4;
+       NMSettingVpn *s_vpn;
+       int num_routes;
+       NMIPRoute *route;
+       const char *expected_dest1 = "1.2.3.0";
+       guint32 expected_prefix1   = 24;
+       const char *expected_nh1   = "1.2.3.254";
+       guint32 expected_metric1   = 99;
+       const char *expected_dest2 = "5.6.7.8";
+       guint32 expected_prefix2   = 30;
+       const char *expected_nh2   = "0.0.0.0";
+       guint32 expected_metric2   = 0;
+       const char *expected_dest3 = "192.168.0.0";
+       guint32 expected_prefix3   = 16;
+       const char *expected_nh3   = "192.168.44.1";
+       guint32 expected_metric3   = 0;
+
+       connection = get_basic_connection (detail, plugin, dir, "route.ovpn");
+       ASSERT (connection != NULL, detail, "failed to import connection");
+
+       /* Connection setting */
+       s_con = nm_connection_get_setting_connection (connection);
+       ASSERT (s_con != NULL, detail, "missing 'connection' setting");
+
+       /* VPN setting */
+       s_vpn = nm_connection_get_setting_vpn (connection);
+       ASSERT (s_vpn != NULL, detail, "missing 'vpn' setting");
+
+       /* Data items */
+       test_item (detail, s_vpn, NM_OPENVPN_KEY_CONNECTION_TYPE, NM_OPENVPN_CONTYPE_TLS);
+
+       /* IP4 setting */
+       s_ip4 = nm_connection_get_setting_ip4_config (connection);
+       ASSERT (s_ip4 != NULL, detail, "missing 'ip4-config' setting");
+       num_routes = nm_setting_ip_config_get_num_routes (s_ip4);
+       ASSERT (num_routes == 3, detail, "incorrect number of static routes");
+       /* route 1 */
+       route = nm_setting_ip_config_get_route (s_ip4, 0);
+       ASSERT (g_strcmp0 (nm_ip_route_get_dest (route), expected_dest1) == 0,
+               detail, "unexpected dest of 1. route");
+       ASSERT (nm_ip_route_get_prefix (route) == expected_prefix1,
+               detail, "unexpected prefix of 1. route");
+       ASSERT (g_strcmp0 (nm_ip_route_get_next_hop (route), expected_nh1) == 0,
+               detail, "unexpected next_hop of 1. route");
+       ASSERT (nm_ip_route_get_metric (route) == expected_metric1,
+               detail, "unexpected metric of 1. route");
+
+       /* route 2 */
+       route = nm_setting_ip_config_get_route (s_ip4, 1);
+       ASSERT (g_strcmp0 (nm_ip_route_get_dest (route), expected_dest2) == 0,
+               detail, "unexpected dest of 2. route");
+       ASSERT (nm_ip_route_get_prefix (route) == expected_prefix2,
+               detail, "unexpected prefix of 2. route");
+       ASSERT (   nm_ip_route_get_next_hop (route) == NULL
+               || g_strcmp0 (nm_ip_route_get_next_hop (route), expected_nh2) == 0,
+               detail, "unexpected next_hop of 2. route");
+       ASSERT (nm_ip_route_get_metric (route) == expected_metric2,
+               detail, "unexpected metric of 2. route");
+
+       /* route 3 */
+       route = nm_setting_ip_config_get_route (s_ip4, 2);
+       ASSERT (g_strcmp0 (nm_ip_route_get_dest (route), expected_dest3) == 0,
+               detail, "unexpected dest of 3. route");
+       ASSERT (nm_ip_route_get_prefix (route) == expected_prefix3,
+               detail, "unexpected prefix of 3. route");
+       ASSERT (g_strcmp0 (nm_ip_route_get_next_hop (route), expected_nh3) == 0,
+               detail, "unexpected next_hop of 3. route");
+       ASSERT (nm_ip_route_get_metric (route) == expected_metric3,
+               detail, "unexpected metric of 3. route");
+
+       g_object_unref (connection);
+}
+
+#define ROUTE_EXPORTED_NAME "route.ovpntest"
+static void
+test_route_export (NMVpnEditorPlugin *plugin,
+                   const char *detail,
+                   const char *dir,
+                   const char *tmpdir)
+{
+       NMConnection *connection;
+       NMConnection *reimported;
+       char *path;
+       gboolean success;
+       GError *error = NULL;
+
+       connection = get_basic_connection (detail, plugin, dir, "route.ovpn");
+       ASSERT (connection != NULL, detail, "failed to import connection");
+
+       path = g_build_path ("/", tmpdir, ROUTE_EXPORTED_NAME, NULL);
+       success = nm_vpn_editor_plugin_export (plugin, path, connection, &error);
+       if (!success) {
+               if (!error)
+                       FAIL (detail, "export failed with missing error");
+               else
+                       FAIL (detail, "export failed: %s", error->message);
+       }
+
+       /* Now re-import it and compare the connections to ensure they are the same */
+       reimported = get_basic_connection (detail, plugin, tmpdir, ROUTE_EXPORTED_NAME);
+       (void) unlink (path);
+       ASSERT (reimported != NULL, detail, "failed to re-import connection");
+
+       /* Clear secrets first, since they don't get exported, and thus would
+        * make the connection comparison below fail.
+        */
+       remove_secrets (connection);
+
+       ASSERT (nm_connection_compare (connection, reimported, NM_SETTING_COMPARE_FLAG_EXACT) == TRUE,
+               detail, "original and reimported connection differ");
+
+       g_object_unref (reimported);
+       g_object_unref (connection);
+       g_free (path);
+}
+
 int main (int argc, char **argv)
 {
        GError *error = NULL;
@@ -1190,6 +1289,9 @@ int main (int argc, char **argv)
        test_device_import (plugin, "device-import", test_dir, "device-notype.ovpn", "tap", NULL);
        test_device_export (plugin, "device-export", test_dir, argv[2], "device-notype.ovpn", 
"device-notype.ovpntest");
 
+       test_route_import (plugin, "route-import", test_dir);
+       test_route_export (plugin, "route-export", test_dir, argv[2]);
+
        g_object_unref (plugin);
 
        basename = g_path_get_basename (argv[0]);


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