diff --git a/ChangeLog b/ChangeLog index b8b63fa5f..0f325c346 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 ab76452d1..41a6e2ea3 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 ec1fe319e..32fad6111 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 3aad32551..ae371872c 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 351176d75..b0e6bcb37 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 6718c6a54..d465ce483 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 2238278cf..175b5f576 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 af072a30e..6713861e4 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 b85f6105e..9d42758f2 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); } } }