mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2025-12-25 10:40:08 +01:00
Rework VPN connection handling for a more consistent D-Bus API. The VPNManager object has been removed, and active VPN connections are now the same as any other active connection. The Manager object's ActivateConnection and DeactivateConnection methods are used to start and stop a VPN connection, and the VPNConnection objects are subclasses of the ActiveConnection objects. When activating a VPN connection, pass the path of the active connection to which the VPN connection is tied in the 'specific_object' argument. Consequently, the libnm-glib API has been reworked to match this arrangement, with the VPNManager object removed, and the NMVPNConnection objects now being subclasses of NMActiveConnection. git-svn-id: http://svn-archive.gnome.org/svn/NetworkManager/trunk@3504 4912f4e0-d625-0410-9fb7-b9a5a253dbdc
590 lines
17 KiB
C
590 lines
17 KiB
C
/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */
|
|
/* NetworkManager -- Network link manager
|
|
*
|
|
* Dan Williams <dcbw@redhat.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* (C) Copyright 2005 Red Hat, Inc.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <signal.h>
|
|
#include <fcntl.h>
|
|
#include <sys/select.h>
|
|
#include <string.h>
|
|
|
|
#include "NetworkManagerPolicy.h"
|
|
#include "NetworkManagerUtils.h"
|
|
#include "NetworkManagerAP.h"
|
|
#include "nm-activation-request.h"
|
|
#include "nm-utils.h"
|
|
#include "nm-device-interface.h"
|
|
#include "nm-device.h"
|
|
#include "nm-device-802-11-wireless.h"
|
|
#include "nm-device-802-3-ethernet.h"
|
|
#include "nm-gsm-device.h"
|
|
#include "nm-cdma-device.h"
|
|
#include "nm-dbus-manager.h"
|
|
#include "nm-setting-ip4-config.h"
|
|
#include "nm-setting-connection.h"
|
|
#include "NetworkManagerSystem.h"
|
|
#include "nm-named-manager.h"
|
|
|
|
struct NMPolicy {
|
|
NMManager *manager;
|
|
guint update_state_id;
|
|
GSList *pending_activation_checks;
|
|
GSList *signal_ids;
|
|
GSList *dev_signal_ids;
|
|
|
|
NMDevice *default_device;
|
|
};
|
|
|
|
#define INVALID_TAG "invalid"
|
|
|
|
static const char *
|
|
get_connection_id (NMConnection *connection)
|
|
{
|
|
NMSettingConnection *s_con;
|
|
|
|
g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
|
|
|
|
s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
|
|
g_return_val_if_fail (s_con != NULL, NULL);
|
|
|
|
return s_con->id;
|
|
}
|
|
|
|
static void
|
|
update_default_route (NMPolicy *policy, NMDevice *new)
|
|
{
|
|
const char *ip_iface;
|
|
|
|
/* FIXME: Not sure if the following makes any sense. */
|
|
/* If iface and ip_iface are the same, it's a regular network device and we
|
|
treat it as such. However, if they differ, it's most likely something like
|
|
a serial device with ppp interface, so route all the traffic to it. */
|
|
ip_iface = nm_device_get_ip_iface (new);
|
|
if (strcmp (ip_iface, nm_device_get_iface (new))) {
|
|
nm_system_device_replace_default_route (ip_iface, 0, 0);
|
|
} else {
|
|
NMIP4Config *config;
|
|
|
|
config = nm_device_get_ip4_config (new);
|
|
nm_system_device_replace_default_route (ip_iface, nm_ip4_config_get_gateway (config),
|
|
nm_ip4_config_get_mss (config));
|
|
}
|
|
}
|
|
|
|
static guint32
|
|
get_device_priority (NMDevice *dev)
|
|
{
|
|
if (NM_IS_CDMA_DEVICE (dev))
|
|
return 2;
|
|
|
|
if (NM_IS_GSM_DEVICE (dev))
|
|
return 3;
|
|
|
|
if (NM_IS_DEVICE_802_11_WIRELESS (dev))
|
|
return 4;
|
|
|
|
if (NM_IS_DEVICE_802_3_ETHERNET (dev))
|
|
return 5;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
update_routing_and_dns (NMPolicy *policy, gboolean force_update)
|
|
{
|
|
NMDevice *best = NULL;
|
|
guint32 best_prio = 0;
|
|
GSList *devices, *iter;
|
|
NMNamedManager *named_mgr;
|
|
NMIP4Config *config;
|
|
|
|
devices = nm_manager_get_devices (policy->manager);
|
|
for (iter = devices; iter; iter = g_slist_next (iter)) {
|
|
NMDevice *dev = NM_DEVICE (iter->data);
|
|
NMActRequest *req;
|
|
NMConnection *connection;
|
|
NMSettingIP4Config *s_ip4;
|
|
guint32 prio;
|
|
|
|
if ( (nm_device_get_state (dev) != NM_DEVICE_STATE_ACTIVATED)
|
|
|| !nm_device_get_ip4_config (dev))
|
|
continue;
|
|
|
|
req = nm_device_get_act_request (dev);
|
|
g_assert (req);
|
|
connection = nm_act_request_get_connection (req);
|
|
g_assert (connection);
|
|
|
|
/* Never set the default route through an IPv4LL-addressed device */
|
|
s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
|
|
if (s_ip4 && !strcmp (s_ip4->method, NM_SETTING_IP4_CONFIG_METHOD_AUTOIP))
|
|
continue;
|
|
|
|
prio = get_device_priority (dev);
|
|
if (prio > best_prio) {
|
|
best = dev;
|
|
best_prio = prio;
|
|
}
|
|
}
|
|
|
|
if (!best)
|
|
goto out;
|
|
if (!force_update && (best == policy->default_device))
|
|
goto out;
|
|
|
|
update_default_route (policy, best);
|
|
|
|
named_mgr = nm_named_manager_get ();
|
|
config = nm_device_get_ip4_config (best);
|
|
nm_named_manager_add_ip4_config (named_mgr, config, NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE);
|
|
g_object_unref (named_mgr);
|
|
|
|
nm_info ("Policy set (%s) as default device for routing and DNS.",
|
|
nm_device_get_iface (best));
|
|
|
|
out:
|
|
policy->default_device = best;
|
|
}
|
|
|
|
typedef struct {
|
|
NMPolicy *policy;
|
|
NMDevice *device;
|
|
guint id;
|
|
} ActivateData;
|
|
|
|
static gboolean
|
|
auto_activate_device (gpointer user_data)
|
|
{
|
|
ActivateData *data = (ActivateData *) user_data;
|
|
NMPolicy *policy;
|
|
NMConnection *best_connection;
|
|
char *specific_object = NULL;
|
|
GSList *connections, *iter;
|
|
|
|
g_assert (data);
|
|
policy = data->policy;
|
|
|
|
// FIXME: if a device is already activating (or activated) with a connection
|
|
// but another connection now overrides the current one for that device,
|
|
// deactivate the device and activate the new connection instead of just
|
|
// bailing if the device is already active
|
|
if (nm_device_get_act_request (data->device))
|
|
goto out;
|
|
|
|
/* System connections first, then user connections */
|
|
connections = nm_manager_get_connections (policy->manager, NM_CONNECTION_SCOPE_SYSTEM);
|
|
connections = g_slist_concat (connections, nm_manager_get_connections (policy->manager, NM_CONNECTION_SCOPE_USER));
|
|
|
|
/* Remove connections that are in the invalid list. */
|
|
iter = connections;
|
|
while (iter) {
|
|
NMConnection *iter_connection = NM_CONNECTION (iter->data);
|
|
GSList *next = g_slist_next (iter);
|
|
|
|
if (g_object_get_data (G_OBJECT (iter_connection), INVALID_TAG)) {
|
|
connections = g_slist_remove_link (connections, iter);
|
|
g_object_unref (iter_connection);
|
|
g_slist_free (iter);
|
|
}
|
|
iter = next;
|
|
}
|
|
|
|
best_connection = nm_device_get_best_auto_connection (data->device, connections, &specific_object);
|
|
if (best_connection) {
|
|
GError *error = NULL;
|
|
const char *device_path;
|
|
|
|
device_path = nm_device_get_udi (data->device);
|
|
if (!nm_manager_activate_connection (policy->manager,
|
|
best_connection,
|
|
specific_object,
|
|
device_path,
|
|
FALSE,
|
|
&error)) {
|
|
NMSettingConnection *s_con;
|
|
|
|
s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (best_connection, NM_TYPE_SETTING_CONNECTION));
|
|
g_assert (s_con);
|
|
|
|
nm_warning ("Connection '%s' auto-activation failed: (%d) %s",
|
|
s_con->id, error->code, error->message);
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
|
|
g_slist_foreach (connections, (GFunc) g_object_unref, NULL);
|
|
g_slist_free (connections);
|
|
|
|
out:
|
|
/* Remove this call's handler ID */
|
|
policy->pending_activation_checks = g_slist_remove (policy->pending_activation_checks, data);
|
|
g_object_unref (data->device);
|
|
g_free (data);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
global_state_changed (NMManager *manager, NMState state, gpointer user_data)
|
|
{
|
|
if (state == NM_STATE_CONNECTED)
|
|
nm_system_restart_mdns_responder ();
|
|
}
|
|
|
|
static void
|
|
schedule_activate_check (NMPolicy *policy, NMDevice *device)
|
|
{
|
|
ActivateData *data;
|
|
GSList *iter;
|
|
gboolean wireless_enabled;
|
|
|
|
if (nm_manager_get_state (policy->manager) == NM_STATE_ASLEEP)
|
|
return;
|
|
|
|
// FIXME: kind of a hack, but devices don't have access to the manager
|
|
// object directly
|
|
wireless_enabled = nm_manager_wireless_enabled (policy->manager);
|
|
if (!nm_device_can_activate (device, wireless_enabled))
|
|
return;
|
|
|
|
for (iter = policy->pending_activation_checks; iter; iter = g_slist_next (iter)) {
|
|
/* Only one pending activation check at a time */
|
|
if (((ActivateData *) iter->data)->device == device)
|
|
return;
|
|
}
|
|
|
|
data = g_malloc0 (sizeof (ActivateData));
|
|
g_return_if_fail (data != NULL);
|
|
|
|
data->policy = policy;
|
|
data->device = g_object_ref (device);
|
|
data->id = g_idle_add (auto_activate_device, data);
|
|
policy->pending_activation_checks = g_slist_append (policy->pending_activation_checks, data);
|
|
}
|
|
|
|
static NMConnection *
|
|
get_device_connection (NMDevice *device)
|
|
{
|
|
NMActRequest *req;
|
|
|
|
req = nm_device_get_act_request (device);
|
|
if (!req)
|
|
return NULL;
|
|
|
|
return nm_act_request_get_connection (req);
|
|
}
|
|
|
|
static void
|
|
device_state_changed (NMDevice *device, NMDeviceState state, gpointer user_data)
|
|
{
|
|
NMPolicy *policy = (NMPolicy *) user_data;
|
|
NMConnection *connection = get_device_connection (device);
|
|
|
|
if ((state == NM_DEVICE_STATE_FAILED) || (state == NM_DEVICE_STATE_CANCELLED)) {
|
|
/* Mark the connection invalid so it doesn't get automatically chosen */
|
|
if (connection) {
|
|
g_object_set_data (G_OBJECT (connection), INVALID_TAG, GUINT_TO_POINTER (TRUE));
|
|
nm_info ("Marking connection '%s' invalid.", get_connection_id (connection));
|
|
}
|
|
|
|
if (state == NM_DEVICE_STATE_CANCELLED)
|
|
schedule_activate_check (policy, device);
|
|
} else if (state == NM_DEVICE_STATE_ACTIVATED) {
|
|
/* Clear the invalid tag on the connection */
|
|
if (connection)
|
|
g_object_set_data (G_OBJECT (connection), INVALID_TAG, NULL);
|
|
|
|
update_routing_and_dns (policy, FALSE);
|
|
} else if (state == NM_DEVICE_STATE_DISCONNECTED) {
|
|
update_routing_and_dns (policy, FALSE);
|
|
|
|
schedule_activate_check (policy, device);
|
|
}
|
|
}
|
|
|
|
static void
|
|
device_carrier_changed (NMDevice8023Ethernet *device,
|
|
GParamSpec *pspec,
|
|
gpointer user_data)
|
|
{
|
|
const char *prop = g_param_spec_get_name (pspec);
|
|
|
|
g_return_if_fail (strcmp (prop, NM_DEVICE_802_3_ETHERNET_CARRIER) == 0);
|
|
|
|
if (!nm_device_802_3_ethernet_get_carrier (device))
|
|
nm_device_interface_deactivate (NM_DEVICE_INTERFACE (device));
|
|
else
|
|
schedule_activate_check ((NMPolicy *) user_data, NM_DEVICE (device));
|
|
}
|
|
|
|
static void
|
|
device_ip4_config_changed (NMDevice *device,
|
|
GParamSpec *pspec,
|
|
gpointer user_data)
|
|
{
|
|
update_routing_and_dns ((NMPolicy *) user_data, TRUE);
|
|
}
|
|
|
|
static void
|
|
wireless_networks_changed (NMDevice80211Wireless *device, NMAccessPoint *ap, gpointer user_data)
|
|
{
|
|
schedule_activate_check ((NMPolicy *) user_data, NM_DEVICE (device));
|
|
}
|
|
|
|
typedef struct {
|
|
gulong id;
|
|
NMDevice *device;
|
|
} DeviceSignalID;
|
|
|
|
static GSList *
|
|
add_device_signal_id (GSList *list, gulong id, NMDevice *device)
|
|
{
|
|
DeviceSignalID *data;
|
|
|
|
data = g_malloc0 (sizeof (DeviceSignalID));
|
|
if (!data)
|
|
return list;
|
|
|
|
data->id = id;
|
|
data->device = device;
|
|
return g_slist_append (list, data);
|
|
}
|
|
|
|
static void
|
|
device_added (NMManager *manager, NMDevice *device, gpointer user_data)
|
|
{
|
|
NMPolicy *policy = (NMPolicy *) user_data;
|
|
gulong id;
|
|
|
|
id = g_signal_connect (device, "state-changed",
|
|
G_CALLBACK (device_state_changed),
|
|
policy);
|
|
policy->dev_signal_ids = add_device_signal_id (policy->dev_signal_ids, id, device);
|
|
|
|
id = g_signal_connect (device, "notify::" NM_DEVICE_INTERFACE_IP4_CONFIG,
|
|
G_CALLBACK (device_ip4_config_changed),
|
|
policy);
|
|
policy->dev_signal_ids = add_device_signal_id (policy->dev_signal_ids, id, device);
|
|
|
|
if (NM_IS_DEVICE_802_11_WIRELESS (device)) {
|
|
id = g_signal_connect (device, "access-point-added",
|
|
G_CALLBACK (wireless_networks_changed),
|
|
policy);
|
|
policy->dev_signal_ids = add_device_signal_id (policy->dev_signal_ids, id, device);
|
|
|
|
id = g_signal_connect (device, "access-point-removed",
|
|
G_CALLBACK (wireless_networks_changed),
|
|
policy);
|
|
policy->dev_signal_ids = add_device_signal_id (policy->dev_signal_ids, id, device);
|
|
}
|
|
|
|
if (NM_IS_DEVICE_802_3_ETHERNET (device)) {
|
|
id = g_signal_connect (device, "notify::" NM_DEVICE_802_3_ETHERNET_CARRIER,
|
|
G_CALLBACK (device_carrier_changed),
|
|
policy);
|
|
policy->dev_signal_ids = add_device_signal_id (policy->dev_signal_ids, id, device);
|
|
}
|
|
|
|
schedule_activate_check (policy, device);
|
|
}
|
|
|
|
static void
|
|
device_removed (NMManager *manager, NMDevice *device, gpointer user_data)
|
|
{
|
|
NMPolicy *policy = (NMPolicy *) user_data;
|
|
GSList *iter = policy->dev_signal_ids;
|
|
|
|
/* Clear any signal handlers for this device */
|
|
while (iter) {
|
|
DeviceSignalID *data = (DeviceSignalID *) iter->data;
|
|
GSList *next = g_slist_next (iter);
|
|
|
|
if (data->device == device) {
|
|
policy->dev_signal_ids = g_slist_remove_link (policy->dev_signal_ids, iter);
|
|
|
|
g_signal_handler_disconnect (data->device, data->id);
|
|
g_free (data);
|
|
g_slist_free (iter);
|
|
}
|
|
iter = next;
|
|
}
|
|
|
|
update_routing_and_dns (policy, FALSE);
|
|
}
|
|
|
|
static void
|
|
schedule_activate_all (NMPolicy *policy)
|
|
{
|
|
GSList *iter, *devices;
|
|
|
|
devices = nm_manager_get_devices (policy->manager);
|
|
for (iter = devices; iter; iter = g_slist_next (iter))
|
|
schedule_activate_check (policy, NM_DEVICE (iter->data));
|
|
}
|
|
|
|
static void
|
|
connections_added (NMManager *manager,
|
|
NMConnectionScope scope,
|
|
gpointer user_data)
|
|
{
|
|
schedule_activate_all ((NMPolicy *) user_data);
|
|
}
|
|
|
|
static void
|
|
connection_added (NMManager *manager,
|
|
NMConnection *connection,
|
|
NMConnectionScope scope,
|
|
gpointer user_data)
|
|
{
|
|
schedule_activate_all ((NMPolicy *) user_data);
|
|
}
|
|
|
|
static void
|
|
connection_updated (NMManager *manager,
|
|
NMConnection *connection,
|
|
NMConnectionScope scope,
|
|
gpointer user_data)
|
|
{
|
|
/* Clear the invalid tag on the connection if it got updated. */
|
|
g_object_set_data (G_OBJECT (connection), INVALID_TAG, NULL);
|
|
|
|
schedule_activate_all ((NMPolicy *) user_data);
|
|
}
|
|
|
|
static void
|
|
connection_removed (NMManager *manager,
|
|
NMConnection *connection,
|
|
NMConnectionScope scope,
|
|
gpointer user_data)
|
|
{
|
|
NMSettingConnection *s_con;
|
|
GPtrArray *list;
|
|
int i;
|
|
|
|
s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
|
|
if (!s_con)
|
|
return;
|
|
|
|
list = nm_manager_get_active_connections_by_connection (manager, connection);
|
|
if (!list)
|
|
return;
|
|
|
|
for (i = 0; i < list->len; i++) {
|
|
char *path = g_ptr_array_index (list, i);
|
|
GError *error = NULL;
|
|
|
|
if (!nm_manager_deactivate_connection (manager, path, &error)) {
|
|
nm_warning ("Connection '%s' disappeared, but error deactivating it: (%d) %s",
|
|
s_con->id, error->code, error->message);
|
|
g_error_free (error);
|
|
}
|
|
g_free (path);
|
|
}
|
|
g_ptr_array_free (list, TRUE);
|
|
}
|
|
|
|
NMPolicy *
|
|
nm_policy_new (NMManager *manager)
|
|
{
|
|
NMPolicy *policy;
|
|
static gboolean initialized = FALSE;
|
|
gulong id;
|
|
|
|
g_return_val_if_fail (NM_IS_MANAGER (manager), NULL);
|
|
g_return_val_if_fail (initialized == FALSE, NULL);
|
|
|
|
policy = g_malloc0 (sizeof (NMPolicy));
|
|
policy->manager = g_object_ref (manager);
|
|
policy->update_state_id = 0;
|
|
|
|
id = g_signal_connect (manager, "state-changed",
|
|
G_CALLBACK (global_state_changed), policy);
|
|
policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id);
|
|
|
|
id = g_signal_connect (manager, "device-added",
|
|
G_CALLBACK (device_added), policy);
|
|
policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id);
|
|
|
|
id = g_signal_connect (manager, "device-removed",
|
|
G_CALLBACK (device_removed), policy);
|
|
policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id);
|
|
|
|
/* Large batch of connections added, manager doesn't want us to
|
|
* process each one individually.
|
|
*/
|
|
id = g_signal_connect (manager, "connections-added",
|
|
G_CALLBACK (connections_added), policy);
|
|
policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id);
|
|
|
|
/* Single connection added */
|
|
id = g_signal_connect (manager, "connection-added",
|
|
G_CALLBACK (connection_added), policy);
|
|
policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id);
|
|
|
|
id = g_signal_connect (manager, "connection-updated",
|
|
G_CALLBACK (connection_updated), policy);
|
|
policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id);
|
|
|
|
id = g_signal_connect (manager, "connection-removed",
|
|
G_CALLBACK (connection_removed), policy);
|
|
policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id);
|
|
|
|
return policy;
|
|
}
|
|
|
|
void
|
|
nm_policy_destroy (NMPolicy *policy)
|
|
{
|
|
GSList *iter;
|
|
|
|
g_return_if_fail (policy != NULL);
|
|
|
|
for (iter = policy->pending_activation_checks; iter; iter = g_slist_next (iter)) {
|
|
ActivateData *data = (ActivateData *) iter->data;
|
|
|
|
g_source_remove (data->id);
|
|
g_object_unref (data->device);
|
|
g_free (data);
|
|
}
|
|
g_slist_free (policy->pending_activation_checks);
|
|
|
|
for (iter = policy->signal_ids; iter; iter = g_slist_next (iter))
|
|
g_signal_handler_disconnect (policy->manager, (gulong) iter->data);
|
|
g_slist_free (policy->signal_ids);
|
|
|
|
for (iter = policy->dev_signal_ids; iter; iter = g_slist_next (iter)) {
|
|
DeviceSignalID *data = (DeviceSignalID *) iter->data;
|
|
|
|
g_signal_handler_disconnect (data->device, data->id);
|
|
g_free (data);
|
|
}
|
|
g_slist_free (policy->dev_signal_ids);
|
|
|
|
g_object_unref (policy->manager);
|
|
g_free (policy);
|
|
}
|
|
|