Commit 4eb5f3ad authored by Jiří Klimeš's avatar Jiří Klimeš

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
parent 5f0e2c25
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <arpa/inet.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
...@@ -40,8 +41,12 @@ ...@@ -40,8 +41,12 @@
#include <nm-setting-vpn.h> #include <nm-setting-vpn.h>
#include <nm-setting-connection.h> #include <nm-setting-connection.h>
#include <nm-setting-ip4-config.h> #include <nm-setting-ip4-config.h>
#include <nm-utils.h>
#define nm_simple_connection_new nm_connection_new #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 NM_SETTING_VPN_ERROR
#define OPENVPN_EDITOR_PLUGIN_ERROR_FILE_NOT_OPENVPN NM_SETTING_VPN_ERROR_UNKNOWN #define OPENVPN_EDITOR_PLUGIN_ERROR_FILE_NOT_OPENVPN NM_SETTING_VPN_ERROR_UNKNOWN
...@@ -101,6 +106,7 @@ ...@@ -101,6 +106,7 @@
#define TLS_REMOTE_TAG "tls-remote " #define TLS_REMOTE_TAG "tls-remote "
#define REMOTE_CERT_TLS_TAG "remote-cert-tls " #define REMOTE_CERT_TLS_TAG "remote-cert-tls "
#define TUNMTU_TAG "tun-mtu " #define TUNMTU_TAG "tun-mtu "
#define ROUTE_TAG "route "
static char * static char *
...@@ -448,11 +454,26 @@ handle_num_seconds_item (const char *line, ...@@ -448,11 +454,26 @@ handle_num_seconds_item (const char *line,
return FALSE; 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 * NMConnection *
do_import (const char *path, char **lines, GError **error) do_import (const char *path, char **lines, GError **error)
{ {
NMConnection *connection = NULL; NMConnection *connection = NULL;
NMSettingConnection *s_con; NMSettingConnection *s_con;
NMSettingIPConfig *s_ip4;
NMSettingVpn *s_vpn; NMSettingVpn *s_vpn;
char *last_dot; char *last_dot;
char **line; char **line;
...@@ -467,6 +488,9 @@ do_import (const char *path, char **lines, GError **error) ...@@ -467,6 +488,9 @@ do_import (const char *path, char **lines, GError **error)
connection = nm_simple_connection_new (); connection = nm_simple_connection_new ();
s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
nm_connection_add_setting (connection, NM_SETTING (s_con)); 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 ()); s_vpn = NM_SETTING_VPN (nm_setting_vpn_new ());
...@@ -926,6 +950,105 @@ do_import (const char *path, char **lines, GError **error) ...@@ -926,6 +950,105 @@ do_import (const char *path, char **lines, GError **error)
g_strfreev (items); g_strfreev (items);
continue; 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) { if (!have_client && !have_sk) {
...@@ -1007,6 +1130,7 @@ gboolean ...@@ -1007,6 +1130,7 @@ gboolean
do_export (const char *path, NMConnection *connection, GError **error) do_export (const char *path, NMConnection *connection, GError **error)
{ {
NMSettingConnection *s_con; NMSettingConnection *s_con;
NMSettingIPConfig *s_ip4;
NMSettingVpn *s_vpn; NMSettingVpn *s_vpn;
FILE *f; FILE *f;
const char *value; const char *value;
...@@ -1047,6 +1171,7 @@ do_export (const char *path, NMConnection *connection, GError **error) ...@@ -1047,6 +1171,7 @@ do_export (const char *path, NMConnection *connection, GError **error)
const char *proxy_retry = NULL; const char *proxy_retry = NULL;
const char *proxy_username = NULL; const char *proxy_username = NULL;
const char *proxy_password = NULL; const char *proxy_password = NULL;
int i;
s_con = nm_connection_get_setting_connection (connection); s_con = nm_connection_get_setting_connection (connection);
g_assert (s_con); g_assert (s_con);
...@@ -1366,6 +1491,55 @@ do_export (const char *path, NMConnection *connection, GError **error) ...@@ -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 */ /* Add hard-coded stuff */
fprintf (f, fprintf (f,
"nobind\n" "nobind\n"
......
...@@ -17,4 +17,5 @@ EXTRA_DIST = \ ...@@ -17,4 +17,5 @@ EXTRA_DIST = \
device-notype.ovpn \ device-notype.ovpn \
keepalive.ovpn \ keepalive.ovpn \
ping-with-exit.ovpn \ ping-with-exit.ovpn \
ping-with-restart.ovpn ping-with-restart.ovpn \
route.ovpn
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
...@@ -104,7 +104,6 @@ test_password_import (NMVpnEditorPlugin *plugin, const char *dir) ...@@ -104,7 +104,6 @@ test_password_import (NMVpnEditorPlugin *plugin, const char *dir)
{ {
NMConnection *connection; NMConnection *connection;
NMSettingConnection *s_con; NMSettingConnection *s_con;
NMSettingIPConfig *s_ip4;
NMSettingVpn *s_vpn; NMSettingVpn *s_vpn;
const char *expected_id = "password"; const char *expected_id = "password";
char *expected_cacert; char *expected_cacert;
...@@ -123,11 +122,6 @@ test_password_import (NMVpnEditorPlugin *plugin, const char *dir) ...@@ -123,11 +122,6 @@ test_password_import (NMVpnEditorPlugin *plugin, const char *dir)
ASSERT (nm_setting_connection_get_uuid (s_con) == NULL, ASSERT (nm_setting_connection_get_uuid (s_con) == NULL,
"password-import", "unexpected valid UUID"); "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 */ /* VPN setting */
s_vpn = nm_connection_get_setting_vpn (connection); s_vpn = nm_connection_get_setting_vpn (connection);
ASSERT (s_vpn != NULL, ASSERT (s_vpn != NULL,
...@@ -235,7 +229,6 @@ test_tls_import (NMVpnEditorPlugin *plugin, const char *dir) ...@@ -235,7 +229,6 @@ test_tls_import (NMVpnEditorPlugin *plugin, const char *dir)
{ {
NMConnection *connection; NMConnection *connection;
NMSettingConnection *s_con; NMSettingConnection *s_con;
NMSettingIPConfig *s_ip4;
NMSettingVpn *s_vpn; NMSettingVpn *s_vpn;
const char *expected_id = "tls"; const char *expected_id = "tls";
char *expected_path; char *expected_path;
...@@ -254,11 +247,6 @@ test_tls_import (NMVpnEditorPlugin *plugin, const char *dir) ...@@ -254,11 +247,6 @@ test_tls_import (NMVpnEditorPlugin *plugin, const char *dir)
ASSERT (nm_setting_connection_get_uuid (s_con) == NULL, ASSERT (nm_setting_connection_get_uuid (s_con) == NULL,
"tls-import", "unexpected valid UUID"); "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 */ /* VPN setting */
s_vpn = nm_connection_get_setting_vpn (connection); s_vpn = nm_connection_get_setting_vpn (connection);
ASSERT (s_vpn != NULL, ASSERT (s_vpn != NULL,
...@@ -351,7 +339,6 @@ test_pkcs12_import (NMVpnEditorPlugin *plugin, const char *dir) ...@@ -351,7 +339,6 @@ test_pkcs12_import (NMVpnEditorPlugin *plugin, const char *dir)
{ {
NMConnection *connection; NMConnection *connection;
NMSettingConnection *s_con; NMSettingConnection *s_con;
NMSettingIPConfig *s_ip4;
NMSettingVpn *s_vpn; NMSettingVpn *s_vpn;
const char *expected_id = "pkcs12"; const char *expected_id = "pkcs12";
char *expected_path; char *expected_path;
...@@ -370,11 +357,6 @@ test_pkcs12_import (NMVpnEditorPlugin *plugin, const char *dir) ...@@ -370,11 +357,6 @@ test_pkcs12_import (NMVpnEditorPlugin *plugin, const char *dir)
ASSERT (nm_setting_connection_get_uuid (s_con) == NULL, ASSERT (nm_setting_connection_get_uuid (s_con) == NULL,
"pkcs12-import", "unexpected valid UUID"); "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 */ /* VPN setting */
s_vpn = nm_connection_get_setting_vpn (connection); s_vpn = nm_connection_get_setting_vpn (connection);
ASSERT (s_vpn != NULL, ASSERT (s_vpn != NULL,
...@@ -501,7 +483,6 @@ test_static_key_import (NMVpnEditorPlugin *plugin, const char *dir) ...@@ -501,7 +483,6 @@ test_static_key_import (NMVpnEditorPlugin *plugin, const char *dir)
{ {
NMConnection *connection; NMConnection *connection;
NMSettingConnection *s_con; NMSettingConnection *s_con;
NMSettingIPConfig *s_ip4;
NMSettingVpn *s_vpn; NMSettingVpn *s_vpn;
const char *expected_id = "static"; const char *expected_id = "static";
char *expected_path; char *expected_path;
...@@ -520,11 +501,6 @@ test_static_key_import (NMVpnEditorPlugin *plugin, const char *dir) ...@@ -520,11 +501,6 @@ test_static_key_import (NMVpnEditorPlugin *plugin, const char *dir)
ASSERT (nm_setting_connection_get_uuid (s_con) == NULL, ASSERT (nm_setting_connection_get_uuid (s_con) == NULL,
"static-key-import", "unexpected valid UUID"); "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 */ /* VPN setting */
s_vpn = nm_connection_get_setting_vpn (connection); s_vpn = nm_connection_get_setting_vpn (connection);
ASSERT (s_vpn != NULL, ASSERT (s_vpn != NULL,
...@@ -1107,6 +1083,129 @@ test_device_export (NMVpnEditorPlugin *plugin, ...@@ -1107,6 +1083,129 @@ test_device_export (NMVpnEditorPlugin *plugin,
g_free (path); 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) int main (int argc, char **argv)
{ {
GError *error = NULL; GError *error = NULL;
...@@ -1190,6 +1289,9 @@ int main (int argc, char **argv) ...@@ -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_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_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); g_object_unref (plugin);
basename = g_path_get_basename (argv[0]); basename = g_path_get_basename (argv[0]);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment