diff options
Diffstat (limited to 'src/devices/nm-device-tun.c')
| -rw-r--r-- | src/devices/nm-device-tun.c | 356 |
1 files changed, 300 insertions, 56 deletions
diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c index 5aaff8b1..91ef8fe7 100644 --- a/src/devices/nm-device-tun.c +++ b/src/devices/nm-device-tun.c @@ -15,41 +15,43 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright 2013 Red Hat, Inc. + * Copyright 2013 - 2015 Red Hat, Inc. */ #include "config.h" #include <stdlib.h> #include <string.h> +#include <sys/types.h> +#include "nm-default.h" +#include "nm-activation-request.h" #include "nm-device-tun.h" #include "nm-device-private.h" -#include "nm-dbus-manager.h" -#include "nm-logging.h" +#include "nm-ip4-config.h" #include "nm-platform.h" #include "nm-device-factory.h" +#include "nm-setting-tun.h" +#include "nm-core-internal.h" -#include "nm-device-tun-glue.h" +#include "nmdbus-device-tun.h" #include "nm-device-logging.h" _LOG_DECLARE_SELF(NMDeviceTun); -G_DEFINE_TYPE (NMDeviceTun, nm_device_tun, NM_TYPE_DEVICE_GENERIC) +G_DEFINE_TYPE (NMDeviceTun, nm_device_tun, NM_TYPE_DEVICE) #define NM_DEVICE_TUN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_TUN, NMDeviceTunPrivate)) typedef struct { NMPlatformTunProperties props; const char *mode; - guint delay_tun_get_properties_id; } NMDeviceTunPrivate; enum { PROP_0, PROP_OWNER, PROP_GROUP, - PROP_FLAGS, PROP_MODE, PROP_NO_PI, PROP_VNET_HDR, @@ -64,11 +66,26 @@ reload_tun_properties (NMDeviceTun *self) NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self); GObject *object = G_OBJECT (self); NMPlatformTunProperties props; - - if (!nm_platform_tun_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (NM_DEVICE (self)), &props)) { - _LOGD (LOGD_HW, "could not read tun properties"); - return; - } + int ifindex; + + ifindex = nm_device_get_ifindex (NM_DEVICE (self)); + if (ifindex > 0) { + if (!nm_platform_link_tun_get_properties (NM_PLATFORM_GET, ifindex, &props)) { + _LOGD (LOGD_DEVICE, "tun-properties: cannot loading tun properties from platform for ifindex %d", ifindex); + ifindex = 0; + } else if (g_strcmp0 (priv->mode, props.mode) != 0) { + /* if the mode differs, we ignore what we loaded. A NMDeviceTun cannot + * change the mode after construction. */ + _LOGD (LOGD_DEVICE, "tun-properties: loading tun properties yielded tun-mode %s%s%s, but %s%s%s expected (ifindex %d)", + NM_PRINT_FMT_QUOTE_STRING (props.mode), + NM_PRINT_FMT_QUOTE_STRING (priv->mode), + ifindex); + ifindex = 0; + } + } else + _LOGD (LOGD_DEVICE, "tun-properties: ignore loading properties due to missing ifindex"); + if (ifindex <= 0) + memset (&props, 0, sizeof (props)); g_object_freeze_notify (object); @@ -97,54 +114,251 @@ link_changed (NMDevice *device, NMPlatformLink *info) } static gboolean -delay_tun_get_properties_cb (gpointer user_data) +complete_connection (NMDevice *device, + NMConnection *connection, + const char *specific_object, + const GSList *existing_connections, + GError **error) { - NMDeviceTun *self = user_data; - NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self); + NMSettingTun *s_tun; + + nm_utils_complete_generic (connection, + NM_SETTING_TUN_SETTING_NAME, + existing_connections, + NULL, + _("TUN connection"), + NULL, + TRUE); + + s_tun = nm_connection_get_setting_tun (connection); + if (!s_tun) { + g_set_error_literal (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_INVALID_CONNECTION, + "A 'tun' setting is required."); + return FALSE; + } - priv->delay_tun_get_properties_id = 0; + return TRUE; +} - reload_tun_properties (self); +static int +tun_mode_from_string (const char *string) +{ + if (!g_strcmp0 (string, "tap")) + return NM_SETTING_TUN_MODE_TAP; + else + return NM_SETTING_TUN_MODE_TUN; +} - return G_SOURCE_REMOVE; +static void +update_connection (NMDevice *device, NMConnection *connection) +{ + NMDeviceTun *self = NM_DEVICE_TUN (device); + NMSettingTun *s_tun = nm_connection_get_setting_tun (connection); + NMPlatformTunProperties props; + NMSettingTunMode mode; + gint64 user, group; + char *str; + + if (!s_tun) { + s_tun = (NMSettingTun *) nm_setting_tun_new (); + nm_connection_add_setting (connection, (NMSetting *) s_tun); + } + + if (!nm_platform_link_tun_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (device), &props)) { + _LOGW (LOGD_HW, "failed to get TUN interface info while updating connection."); + return; + } + + mode = tun_mode_from_string (props.mode); + + if (mode != nm_setting_tun_get_mode (s_tun)) + g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_MODE, mode, NULL); + + user = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_owner (s_tun), 10, 0, G_MAXINT32, -1); + group = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_group (s_tun), 10, 0, G_MAXINT32, -1); + + if (props.owner != user) { + str = props.owner >= 0 ? g_strdup_printf ("%" G_GINT32_FORMAT, (gint32) props.owner) : NULL; + g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_OWNER, str, NULL); + g_free (str); + } + + if (props.group != group) { + str = props.group >= 0 ? g_strdup_printf ("%" G_GINT32_FORMAT, (gint32) props.group) : NULL; + g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_GROUP, str, NULL); + g_free (str); + } + + if ((!props.no_pi) != nm_setting_tun_get_pi (s_tun)) + g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_PI, !props.no_pi, NULL); + if (props.vnet_hdr != nm_setting_tun_get_vnet_hdr (s_tun)) + g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_VNET_HDR, props.vnet_hdr, NULL); + if (props.multi_queue != nm_setting_tun_get_multi_queue (s_tun)) + g_object_set (G_OBJECT (s_tun), NM_SETTING_TUN_MULTI_QUEUE, props.multi_queue, NULL); } -/**************************************************************/ +static gboolean +create_and_realize (NMDevice *device, + NMConnection *connection, + NMDevice *parent, + const NMPlatformLink **out_plink, + GError **error) +{ + const char *iface = nm_device_get_iface (device); + NMPlatformError plerr; + NMSettingTun *s_tun; + gint64 user, group; + + s_tun = nm_connection_get_setting_tun (connection); + g_assert (s_tun); + + user = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_owner (s_tun), 10, 0, G_MAXINT32, -1); + group = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_group (s_tun), 10, 0, G_MAXINT32, -1); + + plerr = nm_platform_link_tun_add (NM_PLATFORM_GET, iface, + nm_setting_tun_get_mode (s_tun) == NM_SETTING_TUN_MODE_TAP, + user, group, + nm_setting_tun_get_pi (s_tun), + nm_setting_tun_get_vnet_hdr (s_tun), + nm_setting_tun_get_multi_queue (s_tun), + out_plink); + if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) { + g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, + "Failed to create TUN/TAP interface '%s' for '%s': %s", + iface, + nm_connection_get_id (connection), + nm_platform_error_to_string (plerr)); + return FALSE; + } + + return TRUE; +} static void -nm_device_tun_init (NMDeviceTun *self) +realize_start_notify (NMDevice *device, const NMPlatformLink *plink) { + NM_DEVICE_CLASS (nm_device_tun_parent_class)->realize_start_notify (device, plink); + reload_tun_properties (device); } -static void -constructed (GObject *object) +static gboolean +check_connection_compatible (NMDevice *device, NMConnection *connection) { - NMDeviceTun *self = NM_DEVICE_TUN (object); - gboolean properties_read; + NMDeviceTun *self = NM_DEVICE_TUN (device); NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self); + NMSettingTunMode mode; + NMSettingTun *s_tun; + gint64 user, group; + + if (!NM_DEVICE_CLASS (nm_device_tun_parent_class)->check_connection_compatible (device, connection)) + return FALSE; + + s_tun = nm_connection_get_setting_tun (connection); + if (!s_tun) + return FALSE; + + if (nm_device_is_real (device)) { + mode = tun_mode_from_string (priv->mode); + if (mode != nm_setting_tun_get_mode (s_tun)) + return FALSE; + + user = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_owner (s_tun), 10, 0, G_MAXINT32, -1); + group = _nm_utils_ascii_str_to_int64 (nm_setting_tun_get_group (s_tun), 10, 0, G_MAXINT32, -1); + + if (user != priv->props.owner) + return FALSE; + if (group != priv->props.group) + return FALSE; + if (nm_setting_tun_get_pi (s_tun) == priv->props.no_pi) + return FALSE; + if (nm_setting_tun_get_vnet_hdr (s_tun) != priv->props.vnet_hdr) + return FALSE; + if (nm_setting_tun_get_multi_queue (s_tun) != priv->props.multi_queue) + return FALSE; + } - properties_read = nm_platform_tun_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (NM_DEVICE (self)), &priv->props); + return TRUE; +} - G_OBJECT_CLASS (nm_device_tun_parent_class)->constructed (object); +static NMActStageReturn +act_stage1_prepare (NMDevice *device, NMDeviceStateReason *reason) +{ + NMDeviceTun *self = NM_DEVICE_TUN (device); + NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self); + NMActRequest *req; + NMConnection *connection; + NMSettingWired *s_wired; + const char *cloned_mac; + NMActStageReturn ret; + + g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + ret = NM_DEVICE_CLASS (nm_device_tun_parent_class)->act_stage1_prepare (device, reason); + if (ret != NM_ACT_STAGE_RETURN_SUCCESS) + return ret; + + /* Nothing to do for TUN devices */ + if (g_strcmp0 (priv->mode, "tap")) + return NM_ACT_STAGE_RETURN_SUCCESS; + + req = nm_device_get_act_request (device); + g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + connection = nm_act_request_get_applied_connection (req); + g_return_val_if_fail (connection != NULL, NM_ACT_STAGE_RETURN_FAILURE); + + s_wired = nm_connection_get_setting_wired (connection); + if (s_wired) { + /* Set device MAC address if the connection wants to change it */ + cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired); + if (cloned_mac) + nm_device_set_hw_addr (device, cloned_mac, "set", LOGD_DEVICE); + } + + return NM_ACT_STAGE_RETURN_SUCCESS; +} - if (!properties_read) { - /* Error reading the tun properties. Maybe this was due to a race. Try again a bit later. */ - _LOGD (LOGD_HW, "could not read tun properties (retry)"); - priv->delay_tun_get_properties_id = g_timeout_add_seconds (1, delay_tun_get_properties_cb, self); +static void +ip4_config_pre_commit (NMDevice *device, NMIP4Config *config) +{ + NMConnection *connection; + NMSettingWired *s_wired; + guint32 mtu; + + connection = nm_device_get_applied_connection (device); + g_assert (connection); + + s_wired = nm_connection_get_setting_wired (connection); + if (s_wired) { + mtu = nm_setting_wired_get_mtu (s_wired); + if (mtu) + nm_ip4_config_set_mtu (config, mtu, NM_IP_CONFIG_SOURCE_USER); } } static void -dispose (GObject *object) +unrealize_notify (NMDevice *device) { - NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (object); + NMDeviceTun *self = NM_DEVICE_TUN (device); + NMDeviceTunPrivate *priv = NM_DEVICE_TUN_GET_PRIVATE (self); + GParamSpec **properties; + guint n_properties, i; - if (priv->delay_tun_get_properties_id) { - g_source_remove (priv->delay_tun_get_properties_id); - priv->delay_tun_get_properties_id = 0; - } + NM_DEVICE_CLASS (nm_device_tun_parent_class)->unrealize_notify (device); + + memset (&priv->props, 0, sizeof (NMPlatformTunProperties)); + + properties = g_object_class_list_properties (G_OBJECT_GET_CLASS (self), &n_properties); + for (i = 0; i < n_properties; i++) + g_object_notify_by_pspec (G_OBJECT (self), properties[i]); + g_free (properties); +} - G_OBJECT_CLASS (nm_device_tun_parent_class)->dispose (object); +/**************************************************************/ + +static void +nm_device_tun_init (NMDeviceTun *self) +{ } static void @@ -214,12 +428,22 @@ nm_device_tun_class_init (NMDeviceTunClass *klass) g_type_class_add_private (klass, sizeof (NMDeviceTunPrivate)); - object_class->constructed = constructed; + NM_DEVICE_CLASS_DECLARE_TYPES (klass, NULL, NM_LINK_TYPE_TUN, NM_LINK_TYPE_TAP) + object_class->get_property = get_property; object_class->set_property = set_property; - object_class->dispose = dispose; + + device_class->connection_type = NM_SETTING_TUN_SETTING_NAME; device_class->link_changed = link_changed; + device_class->complete_connection = complete_connection; + device_class->check_connection_compatible = check_connection_compatible; + device_class->create_and_realize = create_and_realize; + device_class->realize_start_notify = realize_start_notify; + device_class->unrealize_notify = unrealize_notify; + device_class->update_connection = update_connection; + device_class->act_stage1_prepare = act_stage1_prepare; + device_class->ip4_config_pre_commit = ip4_config_pre_commit; /* properties */ g_object_class_install_property @@ -258,9 +482,9 @@ nm_device_tun_class_init (NMDeviceTunClass *klass) FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - nm_dbus_manager_register_exported_type (nm_dbus_manager_get (), - G_TYPE_FROM_CLASS (klass), - &dbus_glib_nm_device_tun_object_info); + nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass), + NMDBUS_TYPE_DEVICE_TUN_SKELETON, + NULL); } @@ -270,29 +494,49 @@ nm_device_tun_class_init (NMDeviceTunClass *klass) #define NM_TUN_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_TUN_FACTORY, NMTunFactory)) static NMDevice * -new_link (NMDeviceFactory *factory, NMPlatformLink *plink, gboolean *out_ignore, GError **error) +create_device (NMDeviceFactory *factory, + const char *iface, + const NMPlatformLink *plink, + NMConnection *connection, + gboolean *out_ignore) { - const char *mode = NULL; - - if (plink->type == NM_LINK_TYPE_TUN) - mode = "tun"; - else if (plink->type == NM_LINK_TYPE_TAP) - mode = "tap"; - else { - g_warn_if_reached (); - mode = "unknown"; + NMSettingTun *s_tun; + NMLinkType link_type = NM_LINK_TYPE_UNKNOWN; + const char *mode; + + if (plink) { + link_type = plink->type; + } else if (connection) { + s_tun = nm_connection_get_setting_tun (connection); + if (!s_tun) + return NULL; + switch (nm_setting_tun_get_mode (s_tun)) { + case NM_SETTING_TUN_MODE_TUN: + link_type = NM_LINK_TYPE_TUN; + break; + case NM_SETTING_TUN_MODE_TAP: + link_type = NM_LINK_TYPE_TAP; + break; + case NM_SETTING_TUN_MODE_UNKNOWN: + g_return_val_if_reached (NULL); + } } + g_return_val_if_fail (link_type != NM_LINK_TYPE_UNKNOWN, NULL); + mode = link_type == NM_LINK_TYPE_TUN ? "tun" : "tap"; + return (NMDevice *) g_object_new (NM_TYPE_DEVICE_TUN, - NM_DEVICE_PLATFORM_DEVICE, plink, + NM_DEVICE_IFACE, iface, NM_DEVICE_TYPE_DESC, "Tun", - NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_GENERIC, + NM_DEVICE_DEVICE_TYPE, NM_DEVICE_TYPE_TUN, + NM_DEVICE_LINK_TYPE, link_type, NM_DEVICE_TUN_MODE, mode, NULL); } NM_DEVICE_FACTORY_DEFINE_INTERNAL (TUN, Tun, tun, - NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_TUN, NM_LINK_TYPE_TAP), - factory_iface->new_link = new_link; + NM_DEVICE_FACTORY_DECLARE_LINK_TYPES (NM_LINK_TYPE_TUN, NM_LINK_TYPE_TAP) + NM_DEVICE_FACTORY_DECLARE_SETTING_TYPES (NM_SETTING_TUN_SETTING_NAME), + factory_iface->create_device = create_device; ) |