diff --git a/include/NetworkManagerVPN.h b/include/NetworkManagerVPN.h index f755721b6..6e654b3a7 100644 --- a/include/NetworkManagerVPN.h +++ b/include/NetworkManagerVPN.h @@ -114,8 +114,28 @@ typedef enum { } NMVPNPluginFailure; +/*** Generic config ***/ + +/* string: VPN interface name (tun0, tap0, etc) */ +#define NM_VPN_PLUGIN_CONFIG_TUNDEV "tundev" + +/* string: Login message */ +#define NM_VPN_PLUGIN_CONFIG_BANNER "banner" + /* uint32 / array of uint8: IP address of the public external VPN gateway (network byte order) */ -#define NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY "gateway" +#define NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY "gateway" + +/* uint32: Maximum Transfer Unit that the VPN interface should use */ +#define NM_VPN_PLUGIN_CONFIG_MTU "mtu" + +/* boolean: Has IP4 configuration? */ +#define NM_VPN_PLUGIN_CONFIG_HAS_IP4 "has-ip4" + +/* boolean: Has IP6 configuration? */ +#define NM_VPN_PLUGIN_CONFIG_HAS_IP6 "has-ip6" + + +/*** Ip4Config ***/ /* uint32: IP address of the internal gateway of the subnet the VPN interface is * on, if the VPN uses subnet configuration (network byte order) @@ -142,30 +162,14 @@ typedef enum { /* uint32: Message Segment Size that the VPN interface should use */ #define NM_VPN_PLUGIN_IP4_CONFIG_MSS "mss" -/* uint32: Maximum Transfer Unit that the VPN interface should use */ -#define NM_VPN_PLUGIN_IP4_CONFIG_MTU "mtu" - -/* string: VPN interface name (tun0, tap0, etc) */ -#define NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV "tundev" - /* string: DNS domain name */ #define NM_VPN_PLUGIN_IP4_CONFIG_DOMAIN "domain" /* array of strings: DNS domain names */ #define NM_VPN_PLUGIN_IP4_CONFIG_DOMAINS "domains" -/* string: Login message */ -#define NM_VPN_PLUGIN_IP4_CONFIG_BANNER "banner" - -/* array of array of uint32: custom routes the client should apply. NOTE: NM - * expects the D-Bus argument signature "aau" here. i.e., an array of - * routes, where each route is a 4-element array of uint32 values. - * - * Each route consists of the following 4 uint32 values, in this order: - * 1: destination IP address (network byte order) - * 2: destination prefix (1 - 32 inclusive) - * 3: IP address of next hop (network byte order) - * 4: route metric +/* [ip4 routes]: custom routes the client should apply, in the format used + * by nm_utils_ip4_routes_to/from_gvalue */ #define NM_VPN_PLUGIN_IP4_CONFIG_ROUTES "routes" @@ -175,5 +179,52 @@ typedef enum { /* Deprecated */ #define NM_VPN_PLUGIN_IP4_CONFIG_GATEWAY NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY +/* Legacy IP4 items; these are included in the IP4 config by older plugins, + * but in the generic config by newer plugins. + */ + +#define NM_VPN_PLUGIN_IP4_CONFIG_BANNER NM_VPN_PLUGIN_CONFIG_BANNER +#define NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY +#define NM_VPN_PLUGIN_IP4_CONFIG_MTU NM_VPN_PLUGIN_CONFIG_MTU +#define NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV NM_VPN_PLUGIN_CONFIG_TUNDEV + + +/*** Ip6Config ***/ + +/* array of uint8: IP address of the internal gateway of the subnet the VPN interface is + * on, if the VPN uses subnet configuration (network byte order) + */ +#define NM_VPN_PLUGIN_IP6_CONFIG_INT_GATEWAY "internal-gateway" + +/* array of uint8: internal IP address of the local VPN interface (network byte order) */ +#define NM_VPN_PLUGIN_IP6_CONFIG_ADDRESS "address" + +/* array of uint8: IP address of the other side of Point-to-Point connection if the VPN + * uses Point-to-Point configuration. (network byte order) + */ +#define NM_VPN_PLUGIN_IP6_CONFIG_PTP "ptp" + +/* uint32: prefix length of the VPN interface; 1 - 128 inclusive */ +#define NM_VPN_PLUGIN_IP6_CONFIG_PREFIX "prefix" + +/* array of array of uint8: IP addresses of DNS servers for the VPN (network byte order) */ +#define NM_VPN_PLUGIN_IP6_CONFIG_DNS "dns" + +/* uint32: Message Segment Size that the VPN interface should use */ +#define NM_VPN_PLUGIN_IP6_CONFIG_MSS "mss" + +/* string: DNS domain name */ +#define NM_VPN_PLUGIN_IP6_CONFIG_DOMAIN "domain" + +/* array of strings: DNS domain names */ +#define NM_VPN_PLUGIN_IP6_CONFIG_DOMAINS "domains" + +/* [ip6 routes]: custom routes the client should apply, in the format used + * by nm_utils_ip6_routes_to/from_gvalue + */ +#define NM_VPN_PLUGIN_IP6_CONFIG_ROUTES "routes" + +/* boolean: prevent this VPN connection from ever getting the default route */ +#define NM_VPN_PLUGIN_IP6_CONFIG_NEVER_DEFAULT "never-default" #endif /* NETWORK_MANAGER_VPN_H */ diff --git a/introspection/nm-vpn-plugin.xml b/introspection/nm-vpn-plugin.xml index 72861f266..5fb11622a 100644 --- a/introspection/nm-vpn-plugin.xml +++ b/introspection/nm-vpn-plugin.xml @@ -55,6 +55,18 @@ + + + Set generic connection details on the connection. + + + + + Generic configuration details for the connection. + + + + Set IPv4 details on the connection. @@ -62,7 +74,21 @@ - Ip4Config details for the conneciton. + Ip4Config details for the connection. You must call + SetConfig() before calling this. + + + + + + + Set IPv6 details on the connection. + + + + + Ip6Config details for the connection. You must call + SetConfig() before calling this. @@ -96,6 +122,17 @@ + + + The plugin obtained generic configuration information. + + + + The configuration information. + + + + The plugin obtained an IPv4 configuration. @@ -107,6 +144,17 @@ + + + The plugin obtained an IPv6 configuration. + + + + The IPv6 configuration. + + + + Emitted when the plugin receives a login banner from the VPN service. diff --git a/libnm-glib/nm-vpn-plugin-utils.c b/libnm-glib/nm-vpn-plugin-utils.c index 8234a9197..b9838df93 100644 --- a/libnm-glib/nm-vpn-plugin-utils.c +++ b/libnm-glib/nm-vpn-plugin-utils.c @@ -24,7 +24,9 @@ #include #include "nm-vpn-plugin-utils.h" +#include "nm-vpn-plugin.h" #include "nm-setting-private.h" +#include "nm-dbus-glib-types.h" #define DATA_KEY_TAG "DATA_KEY=" #define DATA_VAL_TAG "DATA_VAL=" @@ -185,4 +187,3 @@ nm_vpn_plugin_utils_get_secret_flags (GHashTable *data, g_free (flag_name); return success; } - diff --git a/libnm-glib/nm-vpn-plugin.c b/libnm-glib/nm-vpn-plugin.c index daea2fc8e..d41a4f6dd 100644 --- a/libnm-glib/nm-vpn-plugin.c +++ b/libnm-glib/nm-vpn-plugin.c @@ -41,10 +41,18 @@ static gboolean impl_vpn_plugin_need_secrets (NMVPNPlugin *plugin, static gboolean impl_vpn_plugin_disconnect (NMVPNPlugin *plugin, GError **err); +static gboolean impl_vpn_plugin_set_config (NMVPNPlugin *plugin, + GHashTable *config, + GError **err); + static gboolean impl_vpn_plugin_set_ip4_config (NMVPNPlugin *plugin, GHashTable *config, GError **err); +static gboolean impl_vpn_plugin_set_ip6_config (NMVPNPlugin *plugin, + GHashTable *config, + GError **err); + static gboolean impl_vpn_plugin_set_failure (NMVPNPlugin *plugin, char *reason, GError **err); @@ -67,13 +75,21 @@ typedef struct { guint connect_timer; guint quit_timer; guint fail_stop_id; + + gboolean has_ip4, got_ip4; + gboolean has_ip6, got_ip6; + + /* Config stuff copied from config to ip4config */ + GValue banner, tundev, gateway, mtu; } NMVPNPluginPrivate; #define NM_VPN_PLUGIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_PLUGIN, NMVPNPluginPrivate)) enum { STATE_CHANGED, + CONFIG, IP4_CONFIG, + IP6_CONFIG, LOGIN_BANNER, FAILURE, QUIT, @@ -315,16 +331,108 @@ nm_vpn_plugin_connect (NMVPNPlugin *plugin, return ret; } +void +nm_vpn_plugin_set_config (NMVPNPlugin *plugin, + GHashTable *config) +{ + NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin); + GValue *val; + + g_return_if_fail (NM_IS_VPN_PLUGIN (plugin)); + g_return_if_fail (config != NULL); + + val = g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_HAS_IP4); + if (val && g_value_get_boolean (val)) + priv->has_ip4 = TRUE; + val = g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_HAS_IP6); + if (val && g_value_get_boolean (val)) + priv->has_ip6 = TRUE; + + g_warn_if_fail (priv->has_ip4 || priv->has_ip6); + + /* Record the items that need to also be inserted into the + * ip4config, for compatibility with older daemons. + */ + val = g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_BANNER); + if (val) { + g_value_init (&priv->banner, G_VALUE_TYPE (val)); + g_value_copy (val, &priv->banner); + } + val = g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_TUNDEV); + if (val) { + g_value_init (&priv->tundev, G_VALUE_TYPE (val)); + g_value_copy (val, &priv->tundev); + } + val = g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY); + if (val) { + g_value_init (&priv->gateway, G_VALUE_TYPE (val)); + g_value_copy (val, &priv->gateway); + } + val = g_hash_table_lookup (config, NM_VPN_PLUGIN_CONFIG_MTU); + if (val) { + g_value_init (&priv->mtu, G_VALUE_TYPE (val)); + g_value_copy (val, &priv->mtu); + } + + g_signal_emit (plugin, signals[CONFIG], 0, config); +} + void nm_vpn_plugin_set_ip4_config (NMVPNPlugin *plugin, GHashTable *ip4_config) { + NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin); + GHashTable *combined_config; + GHashTableIter iter; + gpointer key, value; + g_return_if_fail (NM_IS_VPN_PLUGIN (plugin)); g_return_if_fail (ip4_config != NULL); - g_signal_emit (plugin, signals[IP4_CONFIG], 0, ip4_config); + priv->got_ip4 = TRUE; - nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTED); + /* Older NetworkManager daemons expect all config info to be in + * the ip4 config, so they won't even notice the "config" signal + * being emitted. So just copy all of that data into the ip4 + * config too. + */ + combined_config = g_hash_table_new (g_str_hash, g_str_equal); + g_hash_table_iter_init (&iter, ip4_config); + while (g_hash_table_iter_next (&iter, &key, &value)) + g_hash_table_insert (combined_config, key, value); + + if (G_VALUE_TYPE (&priv->banner) != G_TYPE_INVALID) + g_hash_table_insert (combined_config, NM_VPN_PLUGIN_IP4_CONFIG_BANNER, &priv->banner); + if (G_VALUE_TYPE (&priv->tundev) != G_TYPE_INVALID) + g_hash_table_insert (combined_config, NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV, &priv->tundev); + if (G_VALUE_TYPE (&priv->gateway) != G_TYPE_INVALID) + g_hash_table_insert (combined_config, NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY, &priv->gateway); + if (G_VALUE_TYPE (&priv->mtu) != G_TYPE_INVALID) + g_hash_table_insert (combined_config, NM_VPN_PLUGIN_IP4_CONFIG_MTU, &priv->mtu); + + g_signal_emit (plugin, signals[IP4_CONFIG], 0, combined_config); + g_hash_table_destroy (combined_config); + + if ( priv->has_ip4 == priv->got_ip4 + && priv->has_ip6 == priv->got_ip6) + nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTED); +} + +void +nm_vpn_plugin_set_ip6_config (NMVPNPlugin *plugin, + GHashTable *ip6_config) +{ + NMVPNPluginPrivate *priv = NM_VPN_PLUGIN_GET_PRIVATE (plugin); + + g_return_if_fail (NM_IS_VPN_PLUGIN (plugin)); + g_return_if_fail (ip6_config != NULL); + + priv->got_ip6 = TRUE; + g_signal_emit (plugin, signals[IP6_CONFIG], 0, ip6_config); + + if ( priv->has_ip4 == priv->got_ip4 + && priv->has_ip6 == priv->got_ip6) + nm_vpn_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTED); } @@ -412,6 +520,16 @@ impl_vpn_plugin_disconnect (NMVPNPlugin *plugin, return nm_vpn_plugin_disconnect (plugin, err); } +static gboolean +impl_vpn_plugin_set_config (NMVPNPlugin *plugin, + GHashTable *config, + GError **err) +{ + nm_vpn_plugin_set_config (plugin, config); + + return TRUE; +} + static gboolean impl_vpn_plugin_set_ip4_config (NMVPNPlugin *plugin, GHashTable *config, @@ -422,6 +540,16 @@ impl_vpn_plugin_set_ip4_config (NMVPNPlugin *plugin, return TRUE; } +static gboolean +impl_vpn_plugin_set_ip6_config (NMVPNPlugin *plugin, + GHashTable *config, + GError **err) +{ + nm_vpn_plugin_set_ip6_config (plugin, config); + + return TRUE; +} + static gboolean impl_vpn_plugin_set_failure (NMVPNPlugin *plugin, char *reason, @@ -615,6 +743,11 @@ finalize (GObject *object) nm_vpn_plugin_set_connection (plugin, NULL); g_free (priv->dbus_service_name); + g_value_unset (&priv->banner); + g_value_unset (&priv->tundev); + g_value_unset (&priv->gateway); + g_value_unset (&priv->mtu); + G_OBJECT_CLASS (nm_vpn_plugin_parent_class)->finalize (object); } @@ -725,6 +858,16 @@ nm_vpn_plugin_class_init (NMVPNPluginClass *plugin_class) G_TYPE_NONE, 1, G_TYPE_UINT); + signals[CONFIG] = + g_signal_new ("config", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMVPNPluginClass, config), + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + DBUS_TYPE_G_MAP_OF_VARIANT); + signals[IP4_CONFIG] = g_signal_new ("ip4-config", G_OBJECT_CLASS_TYPE (object_class), @@ -735,6 +878,16 @@ nm_vpn_plugin_class_init (NMVPNPluginClass *plugin_class) G_TYPE_NONE, 1, DBUS_TYPE_G_MAP_OF_VARIANT); + signals[IP6_CONFIG] = + g_signal_new ("ip6-config", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (NMVPNPluginClass, ip6_config), + NULL, NULL, + g_cclosure_marshal_VOID__BOXED, + G_TYPE_NONE, 1, + DBUS_TYPE_G_MAP_OF_VARIANT); + signals[LOGIN_BANNER] = g_signal_new ("login-banner", G_OBJECT_CLASS_TYPE (object_class), diff --git a/libnm-glib/nm-vpn-plugin.h b/libnm-glib/nm-vpn-plugin.h index 252e1a217..cdd55c153 100644 --- a/libnm-glib/nm-vpn-plugin.h +++ b/libnm-glib/nm-vpn-plugin.h @@ -91,13 +91,17 @@ typedef struct { void (*quit) (NMVPNPlugin *plugin); + void (*config) (NMVPNPlugin *plugin, + GHashTable *config); + + void (*ip6_config) (NMVPNPlugin *plugin, + GHashTable *config); + /* Padding for future expansion */ void (*_reserved1) (void); void (*_reserved2) (void); void (*_reserved3) (void); void (*_reserved4) (void); - void (*_reserved5) (void); - void (*_reserved6) (void); } NMVPNPluginClass; GType nm_vpn_plugin_get_type (void); @@ -115,9 +119,15 @@ void nm_vpn_plugin_set_login_banner (NMVPNPlugin *plugin, void nm_vpn_plugin_failure (NMVPNPlugin *plugin, NMVPNPluginFailure reason); +void nm_vpn_plugin_set_config (NMVPNPlugin *plugin, + GHashTable *config); + void nm_vpn_plugin_set_ip4_config (NMVPNPlugin *plugin, GHashTable *ip4_config); +void nm_vpn_plugin_set_ip6_config (NMVPNPlugin *plugin, + GHashTable *ip6_config); + gboolean nm_vpn_plugin_disconnect (NMVPNPlugin *plugin, GError **err); diff --git a/src/dns-manager/nm-dns-manager.c b/src/dns-manager/nm-dns-manager.c index b75aa92b5..bb7280760 100644 --- a/src/dns-manager/nm-dns-manager.c +++ b/src/dns-manager/nm-dns-manager.c @@ -908,8 +908,6 @@ nm_dns_manager_add_ip6_config (NMDnsManager *mgr, switch (cfg_type) { case NM_DNS_IP_CONFIG_TYPE_VPN: - /* FIXME: not quite yet... */ - g_return_val_if_fail (cfg_type != NM_DNS_IP_CONFIG_TYPE_VPN, FALSE); priv->ip6_vpn_config = config; break; case NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE: diff --git a/src/nm-policy.c b/src/nm-policy.c index 16cd117a1..26b24f92e 100644 --- a/src/nm-policy.c +++ b/src/nm-policy.c @@ -449,9 +449,7 @@ update_ip4_routing_and_dns (NMPolicy *policy, gboolean force_update) int ip_ifindex = 0; best = get_best_ip4_device (policy->manager, &best_req); - if (!best) - goto out; - if (!force_update && (best == policy->default_device4)) + if (!force_update && best && (best == policy->default_device4)) goto out; /* If a VPN connection is active, it is preferred */ @@ -469,7 +467,7 @@ update_ip4_routing_and_dns (NMPolicy *policy, gboolean force_update) /* Check the active IP4 config from the VPN service daemon */ ip4_config = nm_vpn_connection_get_ip4_config (candidate); - if (ip4_config && nm_ip4_config_get_never_default (ip4_config)) + if (!ip4_config || nm_ip4_config_get_never_default (ip4_config)) can_default = FALSE; /* Check the user's preference from the NMConnection */ @@ -481,6 +479,7 @@ update_ip4_routing_and_dns (NMPolicy *policy, gboolean force_update) if (can_default && (vpn_state == NM_VPN_CONNECTION_STATE_ACTIVATED)) { NMIP4Config *parent_ip4; NMDevice *parent; + guint32 parent_mss; ip_iface = nm_vpn_connection_get_ip_iface (candidate); ip_ifindex = nm_vpn_connection_get_ip_ifindex (candidate); @@ -489,13 +488,14 @@ update_ip4_routing_and_dns (NMPolicy *policy, gboolean force_update) parent = nm_vpn_connection_get_parent_device (candidate); parent_ip4 = nm_device_get_ip4_config (parent); + parent_mss = parent_ip4 ? nm_ip4_config_get_mss (parent_ip4) : 0; nm_system_replace_default_ip4_route_vpn (ip_ifindex, nm_ip4_address_get_gateway (addr), nm_vpn_connection_get_ip4_internal_gateway (candidate), nm_ip4_config_get_mss (ip4_config), nm_device_get_ip_ifindex (parent), - nm_ip4_config_get_mss (parent_ip4)); + parent_mss); dns_type = NM_DNS_IP_CONFIG_TYPE_VPN; } @@ -504,6 +504,9 @@ update_ip4_routing_and_dns (NMPolicy *policy, gboolean force_update) /* The best device gets the default route if a VPN connection didn't */ if (!ip_iface || !ip4_config) { + if (!best) + goto out; + connection = nm_act_request_get_connection (best_req); ip_iface = nm_device_get_ip_iface (best); ip_ifindex = nm_device_get_ip_ifindex (best); @@ -570,9 +573,7 @@ update_ip6_routing_and_dns (NMPolicy *policy, gboolean force_update) NMActRequest *best_req = NULL; NMDnsManager *dns_mgr; GSList *devices = NULL, *iter; -#if 0 GSList *vpns; -#endif NMIP6Config *ip6_config = NULL; NMIP6Address *addr; const char *ip_iface = NULL; @@ -582,12 +583,9 @@ update_ip6_routing_and_dns (NMPolicy *policy, gboolean force_update) const char *connection_id; best = get_best_ip6_device (policy->manager, &best_req); - if (!best) - goto out; - if (!force_update && (best == policy->default_device6)) + if (!force_update && best && (best == policy->default_device6)) goto out; -#if 0 /* If a VPN connection is active, it is preferred */ vpns = nm_vpn_manager_get_active_connections (policy->vpn_manager); for (iter = vpns; iter; iter = g_slist_next (iter)) { @@ -600,6 +598,13 @@ update_ip6_routing_and_dns (NMPolicy *policy, gboolean force_update) /* If it's marked 'never-default', don't make it default */ vpn_connection = nm_vpn_connection_get_connection (candidate); g_assert (vpn_connection); + + /* Check the active IP6 config from the VPN service daemon */ + ip6_config = nm_vpn_connection_get_ip6_config (candidate); + if (!ip6_config || nm_ip6_config_get_never_default (ip6_config)) + can_default = FALSE; + + /* Check the user's preference from the NMConnection */ s_ip6 = nm_connection_get_setting_ip6_config (vpn_connection); if (s_ip6 && nm_setting_ip6_config_get_never_default (s_ip6)) can_default = FALSE; @@ -608,30 +613,34 @@ update_ip6_routing_and_dns (NMPolicy *policy, gboolean force_update) if (can_default && (vpn_state == NM_VPN_CONNECTION_STATE_ACTIVATED)) { NMIP6Config *parent_ip6; NMDevice *parent; + guint32 parent_mss; ip_iface = nm_vpn_connection_get_ip_iface (candidate); + ip_ifindex = nm_vpn_connection_get_ip_ifindex (candidate); connection = nm_vpn_connection_get_connection (candidate); - ip6_config = nm_vpn_connection_get_ip6_config (candidate); addr = nm_ip6_config_get_address (ip6_config, 0); parent = nm_vpn_connection_get_parent_device (candidate); parent_ip6 = nm_device_get_ip6_config (parent); + parent_mss = parent_ip6 ? nm_ip6_config_get_mss (parent_ip6) : 0; - nm_system_replace_default_ip6_route_vpn (ip_iface, + nm_system_replace_default_ip6_route_vpn (ip_ifindex, nm_ip6_address_get_gateway (addr), - nm_vpn_connection_get_ip4_internal_gateway (candidate), - nm_ip6_config_get_mss (ip4_config), - nm_device_get_ip_iface (parent), - nm_ip6_config_get_mss (parent_ip4)); + nm_vpn_connection_get_ip6_internal_gateway (candidate), + nm_ip6_config_get_mss (ip6_config), + nm_device_get_ip_ifindex (parent), + parent_mss); dns_type = NM_DNS_IP_CONFIG_TYPE_VPN; } } g_slist_free (vpns); -#endif /* The best device gets the default route if a VPN connection didn't */ if (!ip_iface || !ip6_config) { + if (!best) + goto out; + connection = nm_act_request_get_connection (best_req); ip_iface = nm_device_get_ip_iface (best); ip_ifindex = nm_device_get_ip_ifindex (best); diff --git a/src/nm-system.c b/src/nm-system.c index 741eea601..b58a626bb 100644 --- a/src/nm-system.c +++ b/src/nm-system.c @@ -992,7 +992,7 @@ nm_system_replace_default_ip4_route_vpn (int ifindex, goto out; if ((err != -NLE_OBJ_NOTFOUND) && (err != -NLE_FAILURE)) { - nm_log_err (LOGD_DEVICE | LOGD_IP4, + nm_log_err (LOGD_DEVICE | LOGD_VPN | LOGD_IP4, "(%s): failed to set IPv4 default route: %d", iface, err); goto out; @@ -1007,7 +1007,7 @@ nm_system_replace_default_ip4_route_vpn (int ifindex, err = replace_default_ip4_route (ifindex, int_gw, mss); if (err != 0) { nm_netlink_route_delete (gw_route); - nm_log_err (LOGD_DEVICE | LOGD_IP4, + nm_log_err (LOGD_DEVICE | LOGD_VPN | LOGD_IP4, "(%s): failed to set IPv4 default route (pass #2): %d", iface, err); } else @@ -1072,7 +1072,7 @@ out: } static struct rtnl_route * -add_ip6_route_to_gateway (int ifindex, const struct in6_addr *gw) +add_ip6_route_to_gateway (int ifindex, const struct in6_addr *gw, int mss) { struct nl_sock *nlh; struct rtnl_route *route = NULL; @@ -1082,7 +1082,7 @@ add_ip6_route_to_gateway (int ifindex, const struct in6_addr *gw) g_return_val_if_fail (nlh != NULL, NULL); /* Gateway might be over a bridge; try adding a route to gateway first */ - route = nm_netlink_route_new (ifindex, AF_INET6, 0, + route = nm_netlink_route_new (ifindex, AF_INET6, mss, NMNL_PROP_SCOPE, RT_SCOPE_LINK, NMNL_PROP_TABLE, RT_TABLE_MAIN, NULL); @@ -1106,7 +1106,7 @@ add_ip6_route_to_gateway (int ifindex, const struct in6_addr *gw) } static int -add_default_ip6_route (int ifindex, const struct in6_addr *gw) +add_default_ip6_route (int ifindex, const struct in6_addr *gw, int mss) { struct rtnl_route *route = NULL; struct nl_sock *nlh; @@ -1117,7 +1117,7 @@ add_default_ip6_route (int ifindex, const struct in6_addr *gw) nlh = nm_netlink_get_default_handle (); g_return_val_if_fail (nlh != NULL, -ENOMEM); - route = nm_netlink_route_new (ifindex, AF_INET6, 0, + route = nm_netlink_route_new (ifindex, AF_INET6, mss, NMNL_PROP_SCOPE, RT_SCOPE_UNIVERSE, NMNL_PROP_TABLE, RT_TABLE_MAIN, NMNL_PROP_PRIO, 1, @@ -1150,20 +1150,12 @@ find_static_default_routes (struct rtnl_route *route, return NULL; } -/* - * nm_system_replace_default_ip6_route - * - * Replace default IPv6 route with one via the given gateway - * - */ -gboolean -nm_system_replace_default_ip6_route (int ifindex, const struct in6_addr *gw) +static int +replace_default_ip6_route (int ifindex, const struct in6_addr *gw, int mss) { GList *def_routes, *iter; - struct rtnl_route *route, *gw_route = NULL; - gboolean success = FALSE; + struct rtnl_route *route; char *iface; - int err; /* We can't just use NLM_F_REPLACE here like in the IPv4 case, because * the kernel doesn't like it if we replace the default routes it @@ -1187,11 +1179,25 @@ nm_system_replace_default_ip6_route (int ifindex, const struct in6_addr *gw) } g_list_free (def_routes); - err = add_default_ip6_route (ifindex, gw); - if (err == 0) - return TRUE; + return add_default_ip6_route (ifindex, gw, mss); +} - if (err == -NLE_EXIST) +/* + * nm_system_replace_default_ip6_route + * + * Replace default IPv6 route with one via the given gateway + * + */ +gboolean +nm_system_replace_default_ip6_route (int ifindex, const struct in6_addr *gw) +{ + struct rtnl_route *gw_route = NULL; + gboolean success = FALSE; + char *iface; + int err; + + err = replace_default_ip6_route (ifindex, gw, 0); + if (err == 0 || err == -NLE_EXIST) return TRUE; iface = nm_netlink_index_to_iface (ifindex); @@ -1206,12 +1212,12 @@ nm_system_replace_default_ip6_route (int ifindex, const struct in6_addr *gw) } /* Try adding a direct route to the gateway first */ - gw_route = add_ip6_route_to_gateway (ifindex, gw); + gw_route = add_ip6_route_to_gateway (ifindex, gw, 0); if (!gw_route) goto out; /* Try adding the original route again */ - err = add_default_ip6_route (ifindex, gw); + err = replace_default_ip6_route (ifindex, gw, 0); if (err != 0) { nm_netlink_route_delete (gw_route); nm_log_err (LOGD_DEVICE | LOGD_IP6, @@ -1227,6 +1233,60 @@ out: return success; } +gboolean +nm_system_replace_default_ip6_route_vpn (int ifindex, + const struct in6_addr *ext_gw, + const struct in6_addr *int_gw, + guint32 mss, + int parent_ifindex, + guint32 parent_mss) +{ + struct rtnl_route *gw_route = NULL; + struct nl_sock *nlh; + gboolean success = FALSE; + int err; + char *iface; + + nlh = nm_netlink_get_default_handle (); + g_return_val_if_fail (nlh != NULL, FALSE); + + err = replace_default_ip6_route (ifindex, int_gw, mss); + if (err == 0) + return TRUE; + + iface = nm_netlink_index_to_iface (ifindex); + if (!iface) + goto out; + + if ((err != -NLE_OBJ_NOTFOUND) && (err != -NLE_FAILURE)) { + nm_log_err (LOGD_DEVICE | LOGD_VPN | LOGD_IP6, + "(%s): failed to set IPv6 default route: %d", + iface, err); + goto out; + } + + /* Try adding a direct route to the gateway first */ + gw_route = add_ip6_route_to_gateway (parent_ifindex, ext_gw, parent_mss); + if (!gw_route) + goto out; + + /* Try adding the original route again */ + err = replace_default_ip6_route (ifindex, int_gw, mss); + if (err != 0) { + nm_netlink_route_delete (gw_route); + nm_log_err (LOGD_DEVICE | LOGD_VPN | LOGD_IP6, + "(%s): failed to set IPv6 default route (pass #2): %d", + iface, err); + } else + success = TRUE; + +out: + if (gw_route) + rtnl_route_put (gw_route); + g_free (iface); + return success; +} + /* * nm_system_iface_flush_addresses * diff --git a/src/nm-system.h b/src/nm-system.h index 6a3983809..1acb88538 100644 --- a/src/nm-system.h +++ b/src/nm-system.h @@ -53,6 +53,13 @@ gboolean nm_system_replace_default_ip4_route_vpn (int ifindex, int parent_ifindex, guint32 parent_mss); +gboolean nm_system_replace_default_ip6_route_vpn (int ifindex, + const struct in6_addr *ext_gw, + const struct in6_addr *int_gw, + guint32 mss, + int parent_ifindex, + guint32 parent_mss); + struct rtnl_route *nm_system_add_ip4_vpn_gateway_route (NMDevice *parent_device, guint32 vpn_gw); struct rtnl_route *nm_system_add_ip6_vpn_gateway_route (NMDevice *parent_device, diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index 45483e150..1f0db995f 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -84,13 +84,18 @@ typedef struct { NMVPNConnectionStateReason failure_reason; DBusGProxy *proxy; guint ipconfig_timeout; + gboolean has_ip4; NMIP4Config *ip4_config; guint32 ip4_internal_gw; guint32 ip4_external_gw; + gboolean has_ip6; + NMIP6Config *ip6_config; + struct in6_addr *ip6_internal_gw; struct in6_addr *ip6_external_gw; char *ip_iface; int ip_ifindex; char *banner; + guint32 mtu; struct rtnl_route *gw_route; } NMVPNConnectionPrivate; @@ -180,7 +185,7 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection, priv->parent_dev, ip_iface, priv->ip4_config, - NULL); + priv->ip6_config); break; case NM_VPN_CONNECTION_STATE_FAILED: case NM_VPN_CONNECTION_STATE_DISCONNECTED: @@ -421,7 +426,7 @@ ip_address_to_string (guint32 numeric) } static const char * -ip6_address_to_string (struct in6_addr *addr) +ip6_address_to_string (const struct in6_addr *addr) { memset (addr_to_string_buf, '\0', sizeof (addr_to_string_buf)); if (inet_ntop (AF_INET6, addr, addr_to_string_buf, INET6_ADDRSTRLEN)) { @@ -437,6 +442,7 @@ print_vpn_config (NMVPNConnection *connection) { NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); NMIP4Address *addr; + NMIP6Address *addr6; char *dns_domain = NULL; guint32 num, i; @@ -448,41 +454,87 @@ print_vpn_config (NMVPNConnection *connection) ip6_address_to_string (priv->ip6_external_gw)); } - addr = nm_ip4_config_get_address (priv->ip4_config, 0); - - if (priv->ip4_internal_gw) - nm_log_info (LOGD_VPN, "Internal Gateway: %s", ip_address_to_string (priv->ip4_internal_gw)); nm_log_info (LOGD_VPN, "Tunnel Device: %s", priv->ip_iface); - nm_log_info (LOGD_VPN, "Internal IP4 Address: %s", ip_address_to_string (nm_ip4_address_get_address (addr))); - nm_log_info (LOGD_VPN, "Internal IP4 Prefix: %d", nm_ip4_address_get_prefix (addr)); - nm_log_info (LOGD_VPN, "Internal IP4 Point-to-Point Address: %s", - ip_address_to_string (nm_ip4_config_get_ptp_address (priv->ip4_config))); - nm_log_info (LOGD_VPN, "Maximum Segment Size (MSS): %d", nm_ip4_config_get_mss (priv->ip4_config)); - num = nm_ip4_config_get_num_routes (priv->ip4_config); - for (i = 0; i < num; i++) { - NMIP4Route *route; + if (priv->ip4_config) { + nm_log_info (LOGD_VPN, "IPv4 configuration:"); - route = nm_ip4_config_get_route (priv->ip4_config, i); - nm_log_info (LOGD_VPN, "Static Route: %s/%d Next Hop: %s", - ip_address_to_string (nm_ip4_route_get_dest (route)), - nm_ip4_route_get_prefix (route), - ip_address_to_string (nm_ip4_route_get_next_hop (route))); - } + addr = nm_ip4_config_get_address (priv->ip4_config, 0); - nm_log_info (LOGD_VPN, "Forbid Default Route: %s", - nm_ip4_config_get_never_default (priv->ip4_config) ? "yes" : "no"); + if (priv->ip4_internal_gw) + nm_log_info (LOGD_VPN, " Internal Gateway: %s", ip_address_to_string (priv->ip4_internal_gw)); + nm_log_info (LOGD_VPN, " Internal Address: %s", ip_address_to_string (nm_ip4_address_get_address (addr))); + nm_log_info (LOGD_VPN, " Internal Prefix: %d", nm_ip4_address_get_prefix (addr)); + nm_log_info (LOGD_VPN, " Internal Point-to-Point Address: %s", + ip_address_to_string (nm_ip4_config_get_ptp_address (priv->ip4_config))); + nm_log_info (LOGD_VPN, " Maximum Segment Size (MSS): %d", nm_ip4_config_get_mss (priv->ip4_config)); - num = nm_ip4_config_get_num_nameservers (priv->ip4_config); - for (i = 0; i < num; i++) { - nm_log_info (LOGD_VPN, "Internal IP4 DNS: %s", - ip_address_to_string (nm_ip4_config_get_nameserver (priv->ip4_config, i))); - } + num = nm_ip4_config_get_num_routes (priv->ip4_config); + for (i = 0; i < num; i++) { + NMIP4Route *route; - if (nm_ip4_config_get_num_domains (priv->ip4_config) > 0) - dns_domain = (char *) nm_ip4_config_get_domain (priv->ip4_config, 0); + route = nm_ip4_config_get_route (priv->ip4_config, i); + nm_log_info (LOGD_VPN, " Static Route: %s/%d Next Hop: %s", + ip_address_to_string (nm_ip4_route_get_dest (route)), + nm_ip4_route_get_prefix (route), + ip_address_to_string (nm_ip4_route_get_next_hop (route))); + } - nm_log_info (LOGD_VPN, "DNS Domain: '%s'", dns_domain ? dns_domain : "(none)"); + nm_log_info (LOGD_VPN, " Forbid Default Route: %s", + nm_ip4_config_get_never_default (priv->ip4_config) ? "yes" : "no"); + + num = nm_ip4_config_get_num_nameservers (priv->ip4_config); + for (i = 0; i < num; i++) { + nm_log_info (LOGD_VPN, " Internal DNS: %s", + ip_address_to_string (nm_ip4_config_get_nameserver (priv->ip4_config, i))); + } + + if (nm_ip4_config_get_num_domains (priv->ip4_config) > 0) + dns_domain = (char *) nm_ip4_config_get_domain (priv->ip4_config, 0); + + nm_log_info (LOGD_VPN, " DNS Domain: '%s'", dns_domain ? dns_domain : "(none)"); + } else + nm_log_info (LOGD_VPN, "No IPv4 configuration"); + + if (priv->ip6_config) { + nm_log_info (LOGD_VPN, "IPv6 configuration:"); + + addr6 = nm_ip6_config_get_address (priv->ip6_config, 0); + + if (priv->ip6_internal_gw) + nm_log_info (LOGD_VPN, " Internal Gateway: %s", ip6_address_to_string (priv->ip6_internal_gw)); + nm_log_info (LOGD_VPN, " Internal Address: %s", ip6_address_to_string (nm_ip6_address_get_address (addr6))); + nm_log_info (LOGD_VPN, " Internal Prefix: %d", nm_ip6_address_get_prefix (addr6)); + nm_log_info (LOGD_VPN, " Internal Point-to-Point Address: %s", + ip6_address_to_string (nm_ip6_config_get_ptp_address (priv->ip6_config))); + nm_log_info (LOGD_VPN, " Maximum Segment Size (MSS): %d", nm_ip6_config_get_mss (priv->ip6_config)); + + num = nm_ip6_config_get_num_routes (priv->ip6_config); + for (i = 0; i < num; i++) { + NMIP6Route *route; + + route = nm_ip6_config_get_route (priv->ip6_config, i); + nm_log_info (LOGD_VPN, " Static Route: %s/%d Next Hop: %s", + ip6_address_to_string (nm_ip6_route_get_dest (route)), + nm_ip6_route_get_prefix (route), + ip6_address_to_string (nm_ip6_route_get_next_hop (route))); + } + + nm_log_info (LOGD_VPN, " Forbid Default Route: %s", + nm_ip6_config_get_never_default (priv->ip6_config) ? "yes" : "no"); + + num = nm_ip6_config_get_num_nameservers (priv->ip6_config); + for (i = 0; i < num; i++) { + nm_log_info (LOGD_VPN, " Internal DNS: %s", + ip6_address_to_string (nm_ip6_config_get_nameserver (priv->ip6_config, i))); + } + + if (nm_ip6_config_get_num_domains (priv->ip6_config) > 0) + dns_domain = (char *) nm_ip6_config_get_domain (priv->ip6_config, 0); + + nm_log_info (LOGD_VPN, " DNS Domain: '%s'", dns_domain ? dns_domain : "(none)"); + } else + nm_log_info (LOGD_VPN, "No IPv6 configuration"); if (priv->banner && strlen (priv->banner)) { nm_log_info (LOGD_VPN, "Login Banner:"); @@ -492,6 +544,186 @@ print_vpn_config (NMVPNConnection *connection) } } +static gboolean +nm_vpn_connection_apply_config (NMVPNConnection *connection) +{ + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + NMDnsManager *dns_mgr; + + nm_system_iface_set_up (priv->ip_ifindex, TRUE, NULL); + + if (priv->ip4_config) { + if (!nm_system_apply_ip4_config (priv->ip_ifindex, priv->ip4_config, + 0, NM_IP4_COMPARE_FLAG_ALL)) + return FALSE; + } + + if (priv->ip6_config) { + if (!nm_system_apply_ip6_config (priv->ip_ifindex, priv->ip6_config, + 0, NM_IP6_COMPARE_FLAG_ALL)) + /* FIXME: remove ip4 config */ + return FALSE; + } + + /* Add any explicit route to the VPN gateway through the parent device */ + if (priv->ip4_external_gw) { + priv->gw_route = nm_system_add_ip4_vpn_gateway_route (priv->parent_dev, + priv->ip4_external_gw); + } else if (priv->ip6_external_gw) { + priv->gw_route = nm_system_add_ip6_vpn_gateway_route (priv->parent_dev, + priv->ip6_external_gw); + } else { + priv->gw_route = NULL; + } + + /* Add the VPN to DNS */ + dns_mgr = nm_dns_manager_get (NULL); + if (priv->ip4_config) { + nm_dns_manager_add_ip4_config (dns_mgr, priv->ip_iface, priv->ip4_config, + NM_DNS_IP_CONFIG_TYPE_VPN); + } + if (priv->ip6_config) { + nm_dns_manager_add_ip6_config (dns_mgr, priv->ip_iface, priv->ip6_config, + NM_DNS_IP_CONFIG_TYPE_VPN); + } + g_object_unref (dns_mgr); + + nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) complete.", + nm_vpn_connection_get_name (connection)); + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_ACTIVATED, + NM_VPN_CONNECTION_STATE_REASON_NONE); + return TRUE; +} + +static void +nm_vpn_connection_config_maybe_complete (NMVPNConnection *connection, + gboolean success) +{ + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + + if (priv->ipconfig_timeout == 0) { + /* config_complete() was already called with an error; + * ignore further calls. + */ + return; + } + + if (success) { + if ( (priv->has_ip4 && !priv->ip4_config) + || (priv->has_ip6 && !priv->ip6_config)) { + /* Need to wait for other config */ + return; + } + } + + g_source_remove (priv->ipconfig_timeout); + priv->ipconfig_timeout = 0; + + if (success) { + print_vpn_config (connection); + + if (nm_vpn_connection_apply_config (connection)) + return; + } + + g_clear_object (&priv->ip4_config); + g_clear_object (&priv->ip6_config); + + nm_log_warn (LOGD_VPN, "VPN connection '%s' did not receive valid IP config information.", + nm_vpn_connection_get_name (connection)); + nm_vpn_connection_set_vpn_state (connection, + NM_VPN_CONNECTION_STATE_FAILED, + NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID); +} + +static gboolean +process_generic_config (NMVPNConnection *connection, + GHashTable *config_hash) +{ + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + GValue *val; + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_CONFIG_TUNDEV); + if (val) + priv->ip_iface = g_strdup (g_value_get_string (val)); + else { + nm_log_err (LOGD_VPN, "invalid or missing tunnel device received!"); + nm_vpn_connection_config_maybe_complete (connection, FALSE); + return FALSE; + } + + /* Grab the interface index for address/routing operations */ + priv->ip_ifindex = nm_netlink_iface_to_index (priv->ip_iface); + if (priv->ip_ifindex < 0) { + nm_log_err (LOGD_VPN, "(%s): failed to look up VPN interface index", priv->ip_iface); + nm_vpn_connection_config_maybe_complete (connection, FALSE); + return FALSE; + } + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_CONFIG_BANNER); + if (val) { + g_free (priv->banner); + priv->banner = g_strdup (g_value_get_string (val)); + } + + /* External world-visible address of the VPN server */ + priv->ip4_external_gw = 0; + priv->ip6_external_gw = NULL; + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY); + if (val) { + if (G_VALUE_HOLDS (val, G_TYPE_UINT)) { + priv->ip4_external_gw = g_value_get_uint (val); + } else if (G_VALUE_HOLDS (val, DBUS_TYPE_G_UCHAR_ARRAY)) { + GByteArray *ba = g_value_get_boxed (val); + + if (ba->len == sizeof (struct in6_addr)) + priv->ip6_external_gw = g_memdup (ba->data, ba->len); + } else { + nm_log_err (LOGD_VPN, "(%s): VPN gateway is neither IPv4 nor IPv6", priv->ip_iface); + nm_vpn_connection_config_maybe_complete (connection, FALSE); + return FALSE; + } + } + + /* MTU; this is a per-connection value, though NM's API treats it + * like it's IP4-specific. So we store it for now and retrieve it + * later in ip4_config_get. + */ + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_CONFIG_MTU); + if (val) + priv->mtu = g_value_get_uint (val); + else + priv->mtu = 0; + + return TRUE; +} + +static void +nm_vpn_connection_config_get (DBusGProxy *proxy, + GHashTable *config_hash, + gpointer user_data) +{ + NMVPNConnection *connection = NM_VPN_CONNECTION (user_data); + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + GValue *val; + + nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) reply received.", + nm_vpn_connection_get_name (connection)); + + if (!process_generic_config (connection, config_hash)) + return; + + /* Note whether to expect IPv4 and IPv6 configs */ + val = g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_CONFIG_HAS_IP4); + priv->has_ip4 = val ? g_value_get_boolean (val) : FALSE; + g_clear_object (&priv->ip4_config); + + val = g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_CONFIG_HAS_IP6); + priv->has_ip6 = val ? g_value_get_boolean (val) : FALSE; + g_clear_object (&priv->ip6_config); +} + static void nm_vpn_connection_ip4_config_get (DBusGProxy *proxy, GHashTable *config_hash, @@ -505,47 +737,25 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy, GValue *val; int i; - nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) reply received.", - nm_vpn_connection_get_name (connection)); + if (priv->has_ip4) { + nm_log_info (LOGD_VPN, "VPN connection '%s' (IP4 Config Get) reply received.", + nm_vpn_connection_get_name (connection)); + } else { + nm_log_info (LOGD_VPN, "VPN connection '%s' (IP4 Config Get) reply received from old-style plugin.", + nm_vpn_connection_get_name (connection)); - g_source_remove (priv->ipconfig_timeout); - priv->ipconfig_timeout = 0; + /* In the old API, the generic and IPv4 configuration items + * were mixed together. + */ + if (!process_generic_config (connection, config_hash)) + return; + + priv->has_ip4 = TRUE; + priv->has_ip6 = FALSE; + } config = nm_ip4_config_new (); - val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV); - if (val) - priv->ip_iface = g_strdup (g_value_get_string (val)); - else { - nm_log_err (LOGD_VPN, "invalid or missing tunnel device received!"); - goto error; - } - - /* Grab the interface index for address/routing operations */ - priv->ip_ifindex = nm_netlink_iface_to_index (priv->ip_iface); - if (priv->ip_ifindex < 0) { - nm_log_err (LOGD_VPN, "(%s): failed to look up VPN interface index", priv->ip_iface); - goto error; - } - - /* External world-visible address of the VPN server */ - priv->ip4_external_gw = 0; - priv->ip6_external_gw = NULL; - val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY); - if (val) { - if (G_VALUE_HOLDS (val, G_TYPE_UINT)) { - priv->ip4_external_gw = g_value_get_uint (val); - } else if (G_VALUE_HOLDS (val, DBUS_TYPE_G_UCHAR_ARRAY)) { - GByteArray *ba = g_value_get_boxed (val); - - if (ba->len == sizeof (struct in6_addr)) - priv->ip6_external_gw = g_memdup (ba->data, ba->len); - } else { - nm_log_err (LOGD_VPN, "(%s): VPN gateway is neither IPv4 nor IPv6", priv->ip_iface); - goto error; - } - } - addr = nm_ip4_address_new (); nm_ip4_address_set_prefix (addr, 24); /* default to class C */ if (priv->ip4_external_gw) @@ -573,7 +783,9 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy, } else { nm_log_err (LOGD_VPN, "invalid IP4 config received!"); nm_ip4_address_unref (addr); - goto error; + g_object_unref (config); + nm_vpn_connection_config_maybe_complete (connection, FALSE); + return; } val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_DNS); @@ -596,9 +808,8 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy, if (val) nm_ip4_config_set_mss (config, g_value_get_uint (val)); - val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_MTU); - if (val) - nm_ip4_config_set_mtu (config, g_value_get_uint (val)); + if (priv->mtu) + nm_ip4_config_set_mtu (config, priv->mtu); val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_DOMAIN); if (val) @@ -613,12 +824,6 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy, nm_ip4_config_add_domain (config, *domain); } - val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_BANNER); - if (val) { - g_free (priv->banner); - priv->banner = g_strdup (g_value_get_string (val)); - } - val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_ROUTES); if (val) { GSList *routes; @@ -635,8 +840,10 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy, */ if ( priv->ip4_external_gw && nm_ip4_route_get_dest (route) == priv->ip4_external_gw - && nm_ip4_route_get_prefix (route) == 32) + && nm_ip4_route_get_prefix (route) == 32) { + nm_ip4_route_unref (route); continue; + } /* Otherwise accept the VPN-provided route */ nm_ip4_config_take_route (config, route); @@ -649,50 +856,143 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy, if (val && G_VALUE_HOLDS_BOOLEAN (val)) nm_ip4_config_set_never_default (config, g_value_get_boolean (val)); - priv->ip4_config = config; - print_vpn_config (connection); - /* Merge in user overrides from the NMConnection's IPv4 setting */ s_ip4 = nm_connection_get_setting_ip4_config (priv->connection); nm_utils_merge_ip4_config (config, s_ip4); - nm_system_iface_set_up (priv->ip_ifindex, TRUE, NULL); + priv->ip4_config = config; + nm_vpn_connection_config_maybe_complete (connection, TRUE); +} - if (nm_system_apply_ip4_config (priv->ip_ifindex, config, 0, NM_IP4_COMPARE_FLAG_ALL)) { - NMDnsManager *dns_mgr; +static void +nm_vpn_connection_ip6_config_get (DBusGProxy *proxy, + GHashTable *config_hash, + gpointer user_data) +{ + NMVPNConnection *connection = NM_VPN_CONNECTION (user_data); + NMVPNConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); + NMSettingIP6Config *s_ip6; + NMIP6Address *addr; + NMIP6Config *config; + GValue *val; + int i; - /* Add any explicit route to the VPN gateway through the parent device */ - if (priv->ip4_external_gw) { - priv->gw_route = nm_system_add_ip4_vpn_gateway_route (priv->parent_dev, - priv->ip4_external_gw); - } else if (priv->ip6_external_gw) { - priv->gw_route = nm_system_add_ip6_vpn_gateway_route (priv->parent_dev, - priv->ip6_external_gw); - } else - priv->gw_route = NULL; + nm_log_info (LOGD_VPN, "VPN connection '%s' (IP6 Config Get) reply received.", + nm_vpn_connection_get_name (connection)); - /* Add the VPN to DNS */ - dns_mgr = nm_dns_manager_get (NULL); - nm_dns_manager_add_ip4_config (dns_mgr, priv->ip_iface, config, NM_DNS_IP_CONFIG_TYPE_VPN); - g_object_unref (dns_mgr); + config = nm_ip6_config_new (); - nm_log_info (LOGD_VPN, "VPN connection '%s' (IP Config Get) complete.", - nm_vpn_connection_get_name (connection)); - nm_vpn_connection_set_vpn_state (connection, - NM_VPN_CONNECTION_STATE_ACTIVATED, - NM_VPN_CONNECTION_STATE_REASON_NONE); - return; + addr = nm_ip6_address_new (); + nm_ip6_address_set_prefix (addr, 128); /* default to class C */ + if (priv->ip6_external_gw) + nm_ip6_address_set_gateway (addr, priv->ip6_external_gw); + + /* Internal address of the VPN subnet's gateway */ + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_INT_GATEWAY); + if (val) { + GByteArray *ba = g_value_get_boxed (val); + + if (ba->len == sizeof (struct in6_addr)) + priv->ip6_internal_gw = g_memdup (ba->data, ba->len); } -error: - g_clear_object (&priv->ip4_config); + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_ADDRESS); + if (val) { + GByteArray *ba = g_value_get_boxed (val); - nm_log_warn (LOGD_VPN, "VPN connection '%s' did not receive valid IP config information.", - nm_vpn_connection_get_name (connection)); - nm_vpn_connection_set_vpn_state (connection, - NM_VPN_CONNECTION_STATE_FAILED, - NM_VPN_CONNECTION_STATE_REASON_IP_CONFIG_INVALID); - g_object_unref (config); + if (ba->len == sizeof (struct in6_addr)) + nm_ip6_address_set_address (addr, (struct in6_addr *)ba->data); + } + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_PTP); + if (val) { + GByteArray *ba = g_value_get_boxed (val); + + if (ba->len == sizeof (struct in6_addr)) + nm_ip6_config_set_ptp_address (config, (struct in6_addr *)ba->data); + } + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_PREFIX); + if (val) + nm_ip6_address_set_prefix (addr, g_value_get_uint (val)); + + if (nm_ip6_address_get_address (addr) && nm_ip6_address_get_prefix (addr)) { + nm_ip6_config_take_address (config, addr); + } else { + nm_log_err (LOGD_VPN, "invalid IP6 config received!"); + nm_ip6_address_unref (addr); + g_object_unref (config); + nm_vpn_connection_config_maybe_complete (connection, FALSE); + } + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_DNS); + if (val) { + GPtrArray *dns = (GPtrArray *) g_value_get_boxed (val); + GByteArray *ba; + + for (i = 0; i < dns->len; i++) { + ba = dns->pdata[i]; + if (ba->len == sizeof (struct in6_addr)) + nm_ip6_config_add_nameserver (config, (struct in6_addr *)ba->data); + } + } + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_MSS); + if (val) + nm_ip6_config_set_mss (config, g_value_get_uint (val)); + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_DOMAIN); + if (val) + nm_ip6_config_add_domain (config, g_value_get_string (val)); + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_DOMAINS); + if (val) { + const char **domains = g_value_get_boxed (val); + const char **domain; + + for (domain = domains; domain && *domain; domain++) + nm_ip6_config_add_domain (config, *domain); + } + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_ROUTES); + if (val) { + GSList *routes; + GSList *iter; + + routes = nm_utils_ip6_routes_from_gvalue (val); + for (iter = routes; iter; iter = iter->next) { + NMIP6Route *route = iter->data; + + /* Ignore host routes to the VPN gateway since NM adds one itself + * below. Since NM knows more about the routing situation than + * the VPN server, we want to use the NM created route instead of + * whatever the server provides. + */ + if ( priv->ip6_external_gw + && nm_ip6_route_get_prefix (route) == 128 + && memcmp (nm_ip6_route_get_dest (route), priv->ip6_external_gw, + sizeof (struct in6_addr)) == 0) { + nm_ip6_route_unref (route); + continue; + } + + /* Otherwise accept the VPN-provided route */ + nm_ip6_config_take_route (config, route); + } + + g_slist_free (routes); + } + + val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP6_CONFIG_NEVER_DEFAULT); + if (val && G_VALUE_HOLDS_BOOLEAN (val)) + nm_ip6_config_set_never_default (config, g_value_get_boolean (val)); + + /* Merge in user overrides from the NMConnection's IPv6 setting */ + s_ip6 = nm_connection_get_setting_ip6_config (priv->connection); + nm_utils_merge_ip6_config (config, s_ip6); + + priv->ip6_config = config; + nm_vpn_connection_config_maybe_complete (connection, TRUE); } static gboolean @@ -781,9 +1081,18 @@ really_activate (NMVPNConnection *connection, const char *username) priv = NM_VPN_CONNECTION_GET_PRIVATE (connection); - /* Ip4Config signal */ dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, G_TYPE_VALUE, G_TYPE_INVALID); + + /* Config signal */ + dbus_g_proxy_add_signal (priv->proxy, "Config", + DBUS_TYPE_G_MAP_OF_VARIANT, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "Config", + G_CALLBACK (nm_vpn_connection_config_get), + connection, NULL); + + /* Ip4Config signal */ dbus_g_proxy_add_signal (priv->proxy, "Ip4Config", DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID); @@ -791,6 +1100,14 @@ really_activate (NMVPNConnection *connection, const char *username) G_CALLBACK (nm_vpn_connection_ip4_config_get), connection, NULL); + /* Ip6Config signal */ + dbus_g_proxy_add_signal (priv->proxy, "Ip6Config", + DBUS_TYPE_G_MAP_OF_VARIANT, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (priv->proxy, "Ip6Config", + G_CALLBACK (nm_vpn_connection_ip6_config_get), + connection, NULL); + hash = _hash_with_username (priv->connection, username); org_freedesktop_NetworkManager_VPN_Plugin_connect_async (priv->proxy, hash, @@ -883,6 +1200,14 @@ nm_vpn_connection_get_ip4_config (NMVPNConnection *connection) return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ip4_config; } +NMIP6Config * +nm_vpn_connection_get_ip6_config (NMVPNConnection *connection) +{ + g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), NULL); + + return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ip6_config; +} + const char * nm_vpn_connection_get_ip_iface (NMVPNConnection *connection) { @@ -915,6 +1240,14 @@ nm_vpn_connection_get_ip4_internal_gateway (NMVPNConnection *connection) return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ip4_internal_gw; } +struct in6_addr * +nm_vpn_connection_get_ip6_internal_gateway (NMVPNConnection *connection) +{ + g_return_val_if_fail (NM_IS_VPN_CONNECTION (connection), 0); + + return NM_VPN_CONNECTION_GET_PRIVATE (connection)->ip6_internal_gw; +} + void nm_vpn_connection_fail (NMVPNConnection *connection, NMVPNConnectionStateReason reason) @@ -1090,10 +1423,6 @@ vpn_cleanup (NMVPNConnection *connection) nm_dns_manager_remove_ip4_config (dns_mgr, priv->ip_iface, priv->ip4_config); g_object_unref (dns_mgr); - /* Remove any previously added VPN gateway host route */ - if (priv->gw_route) - nm_netlink_route_delete (priv->gw_route); - /* Reset routes and addresses of the currently active device */ parent_config = nm_device_get_ip4_config (priv->parent_dev); if (parent_config) { @@ -1106,7 +1435,29 @@ vpn_cleanup (NMVPNConnection *connection) } } + if (priv->ip6_config) { + NMIP6Config *parent_config; + NMDnsManager *dns_mgr; + + /* Remove attributes of the VPN's IP6 Config */ + dns_mgr = nm_dns_manager_get (NULL); + nm_dns_manager_remove_ip6_config (dns_mgr, priv->ip_iface, priv->ip6_config); + g_object_unref (dns_mgr); + + /* Reset routes and addresses of the currently active device */ + parent_config = nm_device_get_ip6_config (priv->parent_dev); + if (parent_config) { + if (!nm_system_apply_ip6_config (nm_device_get_ip_ifindex (priv->parent_dev), + nm_device_get_ip6_config (priv->parent_dev), + nm_device_get_priority (priv->parent_dev), + NM_IP6_COMPARE_FLAG_ADDRESSES | NM_IP6_COMPARE_FLAG_ROUTES)) { + nm_log_err (LOGD_VPN, "failed to re-apply VPN parent device addresses and routes."); + } + } + } + if (priv->gw_route) { + nm_netlink_route_delete (priv->gw_route); rtnl_route_put (priv->gw_route); priv->gw_route = NULL; } @@ -1192,6 +1543,8 @@ dispose (GObject *object) if (priv->gw_route) rtnl_route_put (priv->gw_route); + if (priv->ip6_internal_gw) + g_free (priv->ip6_internal_gw); if (priv->ip6_external_gw) g_free (priv->ip6_external_gw); @@ -1207,6 +1560,8 @@ dispose (GObject *object) if (priv->ip4_config) g_object_unref (priv->ip4_config); + if (priv->ip6_config) + g_object_unref (priv->ip6_config); if (priv->ipconfig_timeout) g_source_remove (priv->ipconfig_timeout); diff --git a/src/vpn-manager/nm-vpn-connection.h b/src/vpn-manager/nm-vpn-connection.h index 93cc06d16..be1d20ee7 100644 --- a/src/vpn-manager/nm-vpn-connection.h +++ b/src/vpn-manager/nm-vpn-connection.h @@ -68,9 +68,11 @@ void nm_vpn_connection_fail (NMVPNConnection *connect void nm_vpn_connection_disconnect (NMVPNConnection *connection, NMVPNConnectionStateReason reason); NMIP4Config * nm_vpn_connection_get_ip4_config (NMVPNConnection *connection); +NMIP6Config * nm_vpn_connection_get_ip6_config (NMVPNConnection *connection); const char * nm_vpn_connection_get_ip_iface (NMVPNConnection *connection); int nm_vpn_connection_get_ip_ifindex (NMVPNConnection *connection); NMDevice * nm_vpn_connection_get_parent_device (NMVPNConnection *connection); guint32 nm_vpn_connection_get_ip4_internal_gateway (NMVPNConnection *connection); +struct in6_addr * nm_vpn_connection_get_ip6_internal_gateway (NMVPNConnection *connection); #endif /* NM_VPN_CONNECTION_H */