From bfd38e3af0d91bd02ae2c15f1f6f90f8d91276e4 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 16 May 2005 12:57:08 +0000 Subject: [PATCH] 2005-05-16 Dan Williams Patch from Tomislav Vujec * gnome/applet/applet-dbus-info.c - (nmi_dbus_get_vpn_connection_routes): new function, pull routes out of GConf and pass them to NetworkManager. New key is 'routes' under the VPN connection, and should be a string list * src/NetworkManagerSystem.c - (nm_system_vpn_device_set_from_ip4_config): if user-defined routes exist, set them on the device when we set the rest of the VPN config. Ensure they are in the correct format since they are passed directly to the command line. * src/backends/NetworkManagerRedHat.c src/backends/NetworkManagerDebian.c - (nm_system_device_add_route_via_device_with_iface): new function * src/vpn-manager/nm-dbus-vpn.c - (nm_dbus_vpn_get_routes): grab VPN routes from NetworkManagerInfo * src/vpn-manager/nm-vpn-manager.c - (nm_vpn_manager_handle_ip4_config_signal): grab routes from NMI and pass them into the IP4 config functions git-svn-id: http://svn-archive.gnome.org/svn/NetworkManager/trunk@637 4912f4e0-d625-0410-9fb7-b9a5a253dbdc --- ChangeLog | 25 +++++++ gnome/applet/applet-dbus-info.c | 78 ++++++++++++++++++++ src/NetworkManagerSystem.c | 107 +++++++++++++++++++++++++++- src/NetworkManagerSystem.h | 4 +- src/backends/NetworkManagerDebian.c | 19 +++++ src/backends/NetworkManagerRedHat.c | 19 +++++ src/vpn-manager/nm-dbus-vpn.c | 81 +++++++++++++++++++++ src/vpn-manager/nm-dbus-vpn.h | 2 + src/vpn-manager/nm-vpn-manager.c | 7 +- 9 files changed, 337 insertions(+), 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index b8b63fa5f8..0f325c346b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,28 @@ +2005-05-16 Dan Williams + + Patch from Tomislav Vujec + * gnome/applet/applet-dbus-info.c + - (nmi_dbus_get_vpn_connection_routes): new function, pull routes out of + GConf and pass them to NetworkManager. New key is 'routes' under + the VPN connection, and should be a string list + + * src/NetworkManagerSystem.c + - (nm_system_vpn_device_set_from_ip4_config): if user-defined routes exist, + set them on the device when we set the rest of the VPN config. Ensure + they are in the correct format since they are passed directly to the + command line. + + * src/backends/NetworkManagerRedHat.c + src/backends/NetworkManagerDebian.c + - (nm_system_device_add_route_via_device_with_iface): new function + + * src/vpn-manager/nm-dbus-vpn.c + - (nm_dbus_vpn_get_routes): grab VPN routes from NetworkManagerInfo + + * src/vpn-manager/nm-vpn-manager.c + - (nm_vpn_manager_handle_ip4_config_signal): grab routes from NMI and pass + them into the IP4 config functions + 2005-05-15 Dan Williams From Filip Miletic: diff --git a/gnome/applet/applet-dbus-info.c b/gnome/applet/applet-dbus-info.c index ab76452d10..41a6e2ea30 100644 --- a/gnome/applet/applet-dbus-info.c +++ b/gnome/applet/applet-dbus-info.c @@ -668,6 +668,82 @@ static DBusMessage *nmi_dbus_get_vpn_connection_vpn_data (NMWirelessApplet *appl return (reply); } +/* + * nmi_dbus_get_vpn_connection_routes + * + * Returns routes for a particular VPN connection. + * + */ +static DBusMessage *nmi_dbus_get_vpn_connection_routes (NMWirelessApplet *applet, DBusMessage *message) +{ + DBusMessage *reply = NULL; + gchar *gconf_key = NULL; + char *name = NULL; + GConfValue *routes_value = NULL; + GConfValue *value = NULL; + DBusError error; + char *escaped_name; + DBusMessageIter iter, array_iter; + GSList *elt; + + g_return_val_if_fail (applet != NULL, NULL); + g_return_val_if_fail (message != NULL, NULL); + + dbus_error_init (&error); + if ( !dbus_message_get_args (message, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID) + || (strlen (name) <= 0)) + { + reply = nmwa_dbus_create_error_message (message, NMI_DBUS_INTERFACE, "InvalidArguments", + "NetworkManagerInfo::getVPNConnectionRoutes called with invalid arguments."); + return reply; + } + + escaped_name = gconf_escape_key (name, strlen (name)); + + /* User-visible name of connection */ + gconf_key = g_strdup_printf ("%s/%s/name", GCONF_PATH_VPN_CONNECTIONS, escaped_name); + if (!(value = gconf_client_get (applet->gconf_client, gconf_key, NULL))) + { + reply = nmwa_dbus_create_error_message (message, NMI_DBUS_INTERFACE, "BadVPNConnectionData", + "NetworkManagerInfo::getVPNConnectionRoutes could not access the name for connection '%s'", name); + return reply; + } + gconf_value_free (value); + g_free (gconf_key); + + /* Grab vpn-daemon specific data */ + gconf_key = g_strdup_printf ("%s/%s/routes", GCONF_PATH_VPN_CONNECTIONS, escaped_name); + if (!(routes_value = gconf_client_get (applet->gconf_client, gconf_key, NULL)) + || !(routes_value->type == GCONF_VALUE_LIST) + || !(gconf_value_get_list_type (routes_value) == GCONF_VALUE_STRING)) + { + reply = nmwa_dbus_create_error_message (message, NMI_DBUS_INTERFACE, "BadVPNConnectionData", + "NetworkManagerInfo::getVPNConnectionRoutes could not access the routes for connection '%s'", name); + if (routes_value) + gconf_value_free (routes_value); + return reply; + } + g_free (gconf_key); + + reply = dbus_message_new_method_return (message); + dbus_message_iter_init_append (reply, &iter); + dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &array_iter); + + for (elt = gconf_value_get_list (routes_value); elt; elt = g_slist_next (elt)) + { + const char *string = gconf_value_get_string ((GConfValue *)elt->data); + if (string) + dbus_message_iter_append_basic (&array_iter, DBUS_TYPE_STRING, &string); + } + + dbus_message_iter_close_container (&iter, &array_iter); + + gconf_value_free (routes_value); + g_free (escaped_name); + + return (reply); +} + /* * nmi_dbus_update_network_auth_method @@ -871,6 +947,8 @@ DBusHandlerResult nmi_dbus_info_message_handler (DBusConnection *connection, DBu reply = nmi_dbus_get_vpn_connection_properties (applet, message); else if (strcmp ("getVPNConnectionVPNData", method) == 0) reply = nmi_dbus_get_vpn_connection_vpn_data (applet, message); + else if (strcmp ("getVPNConnectionRoutes", method) == 0) + reply = nmi_dbus_get_vpn_connection_routes (applet, message); if (reply) { diff --git a/src/NetworkManagerSystem.c b/src/NetworkManagerSystem.c index ec1fe319ec..32fad6111a 100644 --- a/src/NetworkManagerSystem.c +++ b/src/NetworkManagerSystem.c @@ -214,13 +214,89 @@ gboolean nm_system_device_set_from_ip4_config (NMDevice *dev) } +/* + * validate_ip4_route + * + * Ensure that IP4 routes are in the correct format + * + */ +static char *validate_ip4_route (const char *route) +{ + char * ret = NULL; + char * temp = NULL; + int slash_pos = -1; + char * p = NULL; + int len, i; + int dot_count = 0; + gboolean have_slash = FALSE; + struct in_addr addr; + + g_return_val_if_fail (route != NULL, NULL); + + len = strlen (route); + /* Minimum length, ie 1.1.1.1/8 */ + if (len < 9) + return NULL; + + for (i = 0; i < len; i++) + { + /* Ensure there is only one slash */ + if (route[i] == '/') + { + if (have_slash) + goto out; + + have_slash = TRUE; + slash_pos = i; + continue; + } + + if (route[i] == '.') + { + if (dot_count >= 4) + goto out; + + dot_count++; + continue; + } + + if (!isdigit (route[i])) + goto out; + } + + /* Make sure there is at least one slash and 3 dots */ + if (!have_slash || !slash_pos || (dot_count != 3)) + goto out; + + /* Valid IP address part */ + temp = g_strdup (route); + temp[slash_pos] = '\0'; + memset (&addr, 0, sizeof (struct in_addr)); + if (inet_aton (temp, &addr) == 0) + goto out; + + /* Ensure the network # is valid */ + p = temp + slash_pos + 1; + i = (int) strtol (p, NULL, 10); + if ((i < 0) || (i > 32)) + goto out; + + /* Success! */ + ret = g_strdup (route); + +out: + g_free (temp); + return ret; +} + + /* * nm_system_vpn_device_set_from_ip4_config * * Set IPv4 configuration of a VPN device from an NMIP4Config object. * */ -gboolean nm_system_vpn_device_set_from_ip4_config (NMNamedManager *named, NMDevice *active_device, const char *iface, NMIP4Config *config) +gboolean nm_system_vpn_device_set_from_ip4_config (NMNamedManager *named, NMDevice *active_device, const char *iface, NMIP4Config *config, char **routes, int num_routes) { gboolean success = FALSE; NMIP4Config * ad_config = NULL; @@ -242,9 +318,34 @@ gboolean nm_system_vpn_device_set_from_ip4_config (NMNamedManager *named, NMDevi nm_system_device_set_ip4_netmask_with_iface (NULL, iface, nm_ip4_config_get_netmask (config)); nm_system_device_set_mtu_with_iface (NULL, iface, 1412); sleep (1); - nm_system_delete_default_route (); nm_system_device_flush_routes_with_iface (iface); - nm_system_device_add_default_route_via_device_with_iface (iface); + if (num_routes <= 0) + { + nm_system_delete_default_route (); + nm_system_device_add_default_route_via_device_with_iface (iface); + } + else + { + int i; + for (i = 0; i < num_routes; i++) + { + char *valid_ip4_route; + + /* Make sure the route is valid, otherwise it's a security risk as the route + * text is simply taken from the user, and passed directly to system(). If + * we did not check the route, think of: + * + * system("/sbin/ip route add `rm -rf /` dev eth0") + * + * where `rm -rf /` was the route text. As UID 0 (root), we have to be careful. + */ + if ((valid_ip4_route = validate_ip4_route (routes[i]))) + { + nm_system_device_add_route_via_device_with_iface (iface, valid_ip4_route); + g_free (valid_ip4_route); + } + } + } set_nameservers (named, config); set_search_domains (named, config); diff --git a/src/NetworkManagerSystem.h b/src/NetworkManagerSystem.h index 3aad32551c..ae371872c1 100644 --- a/src/NetworkManagerSystem.h +++ b/src/NetworkManagerSystem.h @@ -39,6 +39,8 @@ void nm_system_device_flush_routes_with_iface (const char *iface); void nm_system_device_add_default_route_via_device(NMDevice *dev); void nm_system_device_add_default_route_via_device_with_iface(const char *iface); +void nm_system_device_add_route_via_device_with_iface (const char *iface, const char *route); + void nm_system_device_flush_addresses (NMDevice *dev); void nm_system_device_flush_addresses_with_iface (const char *iface); @@ -64,7 +66,7 @@ void nm_system_remove_ip4_config_nameservers (NMNamedManager *named, NMIP4Con void nm_system_remove_ip4_config_search_domains (NMNamedManager *named, NMIP4Config *config); gboolean nm_system_device_set_from_ip4_config (NMDevice *dev); -gboolean nm_system_vpn_device_set_from_ip4_config (NMNamedManager *named, NMDevice *active_device, const char *iface, NMIP4Config *config); +gboolean nm_system_vpn_device_set_from_ip4_config (NMNamedManager *named, NMDevice *active_device, const char *iface, NMIP4Config *config, char **routes, int num_routes); gboolean nm_system_device_set_up_down (NMDevice *dev, gboolean up); gboolean nm_system_device_set_up_down_with_iface (NMDevice *dev, const char *iface, gboolean up); diff --git a/src/backends/NetworkManagerDebian.c b/src/backends/NetworkManagerDebian.c index 351176d75d..b0e6bcb37d 100644 --- a/src/backends/NetworkManagerDebian.c +++ b/src/backends/NetworkManagerDebian.c @@ -81,6 +81,25 @@ void nm_system_device_add_default_route_via_device_with_iface (const char *iface g_free (buf); } +/* + * nm_system_device_add_route_via_device_with_iface + * + * Add route to the given device + * + */ +void nm_system_device_add_route_via_device_with_iface (const char *iface, const char *route) +{ + char *buf; + + g_return_if_fail (iface != NULL); + + /* Add default gateway */ + buf = g_strdup_printf ("/sbin/ip route add %s dev %s", route, iface); + nm_spawn_process (buf); + g_free (buf); +} + + /* * nm_system_device_flush_addresses * diff --git a/src/backends/NetworkManagerRedHat.c b/src/backends/NetworkManagerRedHat.c index 6718c6a549..d465ce483d 100644 --- a/src/backends/NetworkManagerRedHat.c +++ b/src/backends/NetworkManagerRedHat.c @@ -116,6 +116,25 @@ void nm_system_device_add_default_route_via_device_with_iface (const char *iface } +/* + * nm_system_device_add_route_via_device_with_iface + * + * Add route to the given device + * + */ +void nm_system_device_add_route_via_device_with_iface (const char *iface, const char *route) +{ + char *buf; + + g_return_if_fail (iface != NULL); + + /* Add default gateway */ + buf = g_strdup_printf ("/sbin/ip route add %s dev %s", route, iface); + nm_spawn_process (buf); + g_free (buf); +} + + /* * nm_system_device_has_active_routes * diff --git a/src/vpn-manager/nm-dbus-vpn.c b/src/vpn-manager/nm-dbus-vpn.c index 2238278cf7..175b5f576f 100644 --- a/src/vpn-manager/nm-dbus-vpn.c +++ b/src/vpn-manager/nm-dbus-vpn.c @@ -233,6 +233,87 @@ static char ** nm_dbus_vpn_get_vpn_data (DBusConnection *connection, NMVPNConnec } +/* + * nm_dbus_vpn_get_routes + * + * Get VPN routes from NMI for a vpn connection + * + * NOTE: caller MUST free returned value using g_strfreev() + * + */ +char ** nm_dbus_vpn_get_routes (DBusConnection *connection, NMVPNConnection *vpn, int *num_items) +{ + DBusMessage *message; + DBusError error; + DBusMessage *reply; + char **routes = NULL; + const char *vpn_name; + + g_return_val_if_fail (connection != NULL, NULL); + g_return_val_if_fail (vpn != NULL, NULL); + g_return_val_if_fail (num_items != NULL, NULL); + + *num_items = -1; + + if (!(message = dbus_message_new_method_call (NMI_DBUS_SERVICE, NMI_DBUS_PATH, NMI_DBUS_INTERFACE, "getVPNConnectionRoutes"))) + { + nm_warning ("nm_dbus_vpn_get_routes(): Couldn't allocate the dbus message"); + return (NULL); + } + + vpn_name = nm_vpn_connection_get_name (vpn); + dbus_message_append_args (message, DBUS_TYPE_STRING, &vpn_name, DBUS_TYPE_INVALID); + + dbus_error_init (&error); + reply = dbus_connection_send_with_reply_and_block (connection, message, -1, &error); + dbus_message_unref (message); + if (dbus_error_is_set (&error)) + nm_warning ("nm_dbus_vpn_get_routes(): %s raised %s", error.name, error.message); + else if (!reply) + nm_info ("nm_dbus_vpn_get_routes(): reply was NULL."); + else + { + DBusMessageIter iter, array_iter; + GArray *buffer; + + dbus_message_iter_init (reply, &iter); + dbus_message_iter_recurse (&iter, &array_iter); + + buffer = g_array_new (TRUE, TRUE, sizeof (gchar *)); + + if (buffer == NULL) + return NULL; + + while (dbus_message_iter_get_arg_type (&array_iter) == DBUS_TYPE_STRING) + { + const char *value; + char *str; + + dbus_message_iter_get_basic (&array_iter, &value); + str = g_strdup (value); + + if (str == NULL) + { + g_array_free (buffer, TRUE); + return NULL; + } + + g_array_append_val (buffer, str); + + dbus_message_iter_next (&array_iter); + } + routes = (gchar **)(buffer->data); + *num_items = buffer->len; + g_array_free (buffer, FALSE); + } + + if (reply) + dbus_message_unref (reply); + + return (routes); +} + + typedef struct UpdateOneVPNCBData { NMData * data; diff --git a/src/vpn-manager/nm-dbus-vpn.h b/src/vpn-manager/nm-dbus-vpn.h index af072a30ec..6713861e4f 100644 --- a/src/vpn-manager/nm-dbus-vpn.h +++ b/src/vpn-manager/nm-dbus-vpn.h @@ -35,6 +35,8 @@ void nm_dbus_vpn_signal_vpn_connection_change (DBusConnection *con, NMVPNConn void nm_dbus_vpn_signal_vpn_failed (DBusConnection *con, const char *signal, NMVPNConnection *vpn, const char *error_msg); void nm_dbus_vpn_signal_vpn_login_banner (DBusConnection *con, NMVPNConnection *vpn, const char *banner); +char ** nm_dbus_vpn_get_routes (DBusConnection *connection, NMVPNConnection *vpn, int *num_items); + NMDbusMethodList * nm_dbus_vpn_methods_setup (void); #endif diff --git a/src/vpn-manager/nm-vpn-manager.c b/src/vpn-manager/nm-vpn-manager.c index b85f6105e5..9d42758f2a 100644 --- a/src/vpn-manager/nm-vpn-manager.c +++ b/src/vpn-manager/nm-vpn-manager.c @@ -434,10 +434,15 @@ void nm_vpn_manager_handle_ip4_config_signal (NMVPNManager *manager, DBusMessage vpn_dev = nm_get_active_device (manager->app_data); if (vpn_dev) { + int num_routes = -1; + char **routes = nm_dbus_vpn_get_routes (manager->app_data->dbus_connection, con, &num_routes); + nm_system_vpn_device_set_from_ip4_config (manager->app_data->named_manager, vpn_dev, - manager->active_device, manager->active_config); + manager->active_device, manager->active_config, + routes, num_routes); if (login_banner && strlen (login_banner)) nm_dbus_vpn_signal_vpn_login_banner (manager->app_data->dbus_connection, con, login_banner); + g_strfreev(routes); } } }