From add5f97b3874cbd2155d9da8aa88c38023c6a2af Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sun, 5 Feb 2012 23:56:15 -0600 Subject: [PATCH] core: make the Ethernet class aware of VLAN interfaces Also track the VLAN ID and master interface index and validate connections using those. --- src/nm-device-ethernet.c | 89 +++++++++++++++++++++++++++++++++++++++- src/nm-system.c | 49 ++++++++++++++++++++++ src/nm-system.h | 4 ++ src/nm-udev-manager.c | 3 ++ 4 files changed, 144 insertions(+), 1 deletion(-) diff --git a/src/nm-device-ethernet.c b/src/nm-device-ethernet.c index fc31eccb3d..2510bd8f61 100644 --- a/src/nm-device-ethernet.c +++ b/src/nm-device-ethernet.c @@ -49,11 +49,13 @@ #include "nm-setting-8021x.h" #include "nm-setting-pppoe.h" #include "nm-setting-bond.h" +#include "nm-setting-vlan.h" #include "ppp-manager/nm-ppp-manager.h" #include "nm-logging.h" #include "nm-properties-changed-signal.h" #include "nm-utils.h" #include "nm-enum-types.h" +#include "nm-netlink-monitor.h" #include "nm-device-ethernet-glue.h" @@ -70,6 +72,7 @@ typedef enum { NM_ETHERNET_TYPE_UNSPEC = 0, NM_ETHERNET_TYPE_BOND, + NM_ETHERNET_TYPE_VLAN, } NMEthernetType; typedef struct Supplicant { @@ -103,6 +106,10 @@ typedef struct { NMIP4Config *pending_ip4_config; NMEthernetType type; + + /* VLAN stuff */ + int vlan_id; + int vlan_master_ifindex; } NMDeviceEthernetPrivate; enum { @@ -263,7 +270,37 @@ constructor (GType type, itype = nm_system_get_iface_type (nm_device_get_ifindex (self), nm_device_get_iface (self)); if (itype == NM_IFACE_TYPE_BOND) priv->type = NM_ETHERNET_TYPE_BOND; - else + else if (itype == NM_IFACE_TYPE_VLAN) { + char *master_iface; + + priv->type = NM_ETHERNET_TYPE_VLAN; + + if (!nm_system_get_iface_vlan_info (nm_device_get_ifindex (self), + &priv->vlan_master_ifindex, + &priv->vlan_id)) { + nm_log_warn (LOGD_DEVICE, "(%s): failed to get VLAN interface info.", + nm_device_get_iface (self)); + g_object_unref (object); + return NULL; + } + + if (priv->vlan_master_ifindex < 0 || priv->vlan_id < 0) { + nm_log_warn (LOGD_DEVICE, "(%s): VLAN master ifindex (%d) or VLAN ID (%d) invalid.", + nm_device_get_iface (self), + priv->vlan_master_ifindex, + priv->vlan_id); + g_object_unref (object); + return NULL; + } + + master_iface = nm_netlink_index_to_iface (priv->vlan_master_ifindex); + nm_log_info (LOGD_DEVICE, "(%s): VLAN ID %d with master %s (ifindex: %d)", + nm_device_get_iface (self), + priv->vlan_id, + master_iface ? master_iface : "(unknown)", + priv->vlan_master_ifindex); + g_free (master_iface); + } else priv->type = NM_ETHERNET_TYPE_UNSPEC; nm_log_dbg (LOGD_HW | LOGD_ETHER, "(%s): kernel ifindex %d", @@ -325,6 +362,11 @@ device_state_changed (NMDevice *device, static void nm_device_ethernet_init (NMDeviceEthernet * self) { + NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self); + + priv->vlan_id = -1; + priv->vlan_master_ifindex = -1; + g_signal_connect (self, "state-changed", G_CALLBACK (device_state_changed), NULL); } @@ -591,6 +633,51 @@ match_ethernet_connection (NMDevice *device, NMConnection *connection, /* NOP */ } else if (nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME)) { /* NOP */ + } else if (nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME)) { + NMSettingConnection *s_con = nm_connection_get_setting_connection (connection); + NMSettingVlan *s_vlan = nm_connection_get_setting_vlan (connection); + const char *master; + int con_master_ifindex; + + g_assert (s_vlan); + g_assert (s_con); + + if (priv->type != NM_ETHERNET_TYPE_VLAN) { + g_set_error (error, NM_ETHERNET_ERROR, NM_ETHERNET_ERROR_CONNECTION_INVALID, + "The device was not a VLAN interface."); + return FALSE; + } + + if (nm_setting_vlan_get_id (s_vlan) != priv->vlan_id) { + g_set_error (error, NM_ETHERNET_ERROR, NM_ETHERNET_ERROR_CONNECTION_INVALID, + "The connection's VLAN ID did not match the device's VLAN ID."); + return FALSE; + } + + /* Check master interface */ + master = nm_setting_connection_get_master (s_con); + if (!master) { + g_set_error (error, NM_ETHERNET_ERROR, NM_ETHERNET_ERROR_CONNECTION_INVALID, + "The connection did not specify a VLAN master."); + return FALSE; + } + + if (nm_utils_is_uuid (master)) { + /* FIXME: find the master device based on the connection UUID; this + * is a bit hard because NMDevice objects (by design) have no + * knowledge of other NMDevice objects, and we need to look through + * all active NMDevices to see if they are using the given + * connection UUID. + */ + } else { + /* It's an interface name; match it against our master */ + con_master_ifindex = nm_netlink_iface_to_index (master); + if (con_master_ifindex < 0 || con_master_ifindex != priv->vlan_master_ifindex) { + g_set_error (error, NM_ETHERNET_ERROR, NM_ETHERNET_ERROR_CONNECTION_INVALID, + "The connection's VLAN master did not match the device's VLAN master interface."); + return FALSE; + } + } } else if (nm_connection_is_type (connection, NM_SETTING_WIRED_SETTING_NAME)) { if (!s_wired) { g_set_error (error, diff --git a/src/nm-system.c b/src/nm-system.c index 0a5dcfa60a..04fc954a98 100644 --- a/src/nm-system.c +++ b/src/nm-system.c @@ -1529,6 +1529,55 @@ out: return res; } +/** + * nm_system_get_iface_vlan_info: + * @ifindex: the VLAN interface index + * @out_master_ifindex: on success, the interface index of the master interface of + * @iface + * @out_vlan_id: on success, the VLAN ID of @iface + * + * Gets the VLAN master interface name and VLAN ID. + * + * Returns: %TRUE if the interface is a VLAN device and no error occurred; + * %FALSE if the interface was not a VLAN interface or an error occurred + **/ +gboolean +nm_system_get_iface_vlan_info (int ifindex, + int *out_master_ifindex, + int *out_vlan_id) +{ + struct nl_sock *nlh; + struct rtnl_link *lk; + struct nl_cache *cache = NULL; + gboolean success = FALSE; + int ret; + + if (nm_system_get_iface_type (ifindex, NULL) != NM_IFACE_TYPE_VLAN) + return FALSE; + + nlh = nm_netlink_get_default_handle (); + if (!nlh) + return FALSE; + + ret = rtnl_link_alloc_cache (nlh, &cache); + g_return_val_if_fail (ret == 0, FALSE); + g_return_val_if_fail (cache != NULL, FALSE); + + lk = rtnl_link_get (cache, ifindex); + if (lk) { + if (out_master_ifindex) + *out_master_ifindex = rtnl_link_get_link (lk); + if (out_vlan_id) + *out_vlan_id = rtnl_link_vlan_get_id (lk); + + rtnl_link_put (lk); + success = TRUE; + } + + nl_cache_free (cache); + return success; +} + /** * nm_system_add_vlan_iface: * @connection: the #NMConnection that describes the VLAN interface diff --git a/src/nm-system.h b/src/nm-system.h index c7f5182817..9ee2b0625d 100644 --- a/src/nm-system.h +++ b/src/nm-system.h @@ -103,6 +103,10 @@ enum { int nm_system_get_iface_type (int ifindex, const char *name); +gboolean nm_system_get_iface_vlan_info (int ifindex, + int *out_master_ifindex, + int *out_vlan_id); + gboolean nm_system_add_vlan_iface (NMConnection *connection, const char *iface, int master_ifindex); diff --git a/src/nm-udev-manager.c b/src/nm-udev-manager.c index d8d213ef10..45262f8743 100644 --- a/src/nm-udev-manager.c +++ b/src/nm-udev-manager.c @@ -410,6 +410,9 @@ net_add (NMUdevManager *self, GUdevDevice *udev_device) case NM_IFACE_TYPE_BOND: driver = "bonding"; break; + case NM_IFACE_TYPE_VLAN: + driver = "8021q"; + break; default: if (g_str_has_prefix (ifname, "easytether")) driver = "easytether";