Commit add5f97b authored by Dan Williams's avatar Dan Williams
Browse files

core: make the Ethernet class aware of VLAN interfaces

Also track the VLAN ID and master interface index and validate
connections using those.
parent fcce9daa
......@@ -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,
......
......@@ -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
......
......@@ -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);
......
......@@ -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";
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment