Merge changes for connection matching on connection assumption (rh #1083196)

https://bugzilla.redhat.com/show_bug.cgi?id=1083196
This commit is contained in:
Jiří Klimeš 2014-04-11 11:45:12 +02:00
commit 52dfc777bb
3 changed files with 313 additions and 102 deletions

View file

@ -649,22 +649,47 @@ nm_utils_read_resolv_conf_nameservers (const char *rc_contents)
return nameservers;
}
static GHashTable *
check_property_in_hash (GHashTable *hash,
const char *s_name,
const char *p_name)
{
GHashTable *props;
props = g_hash_table_lookup (hash, s_name);
if ( !props
|| !g_hash_table_lookup (props, p_name)) {
return NULL;
}
return props;
}
static void
remove_from_hash (GHashTable *s_hash,
GHashTable *p_hash,
const char *s_name,
const char *p_name)
{
g_hash_table_remove (p_hash, p_name);
if (g_hash_table_size (p_hash) == 0)
g_hash_table_remove (s_hash, s_name);
}
static gboolean
check_ip6_method_link_local_auto (NMConnection *orig,
NMConnection *candidate,
GHashTable *settings)
check_ip6_method (NMConnection *orig,
NMConnection *candidate,
GHashTable *settings)
{
GHashTable *props;
const char *orig_ip6_method, *candidate_ip6_method;
NMSettingIP6Config *candidate_ip6;
gboolean allow = FALSE;
props = g_hash_table_lookup (settings, NM_SETTING_IP6_CONFIG_SETTING_NAME);
if ( !props
|| (g_hash_table_size (props) != 1)
|| !g_hash_table_lookup (props, NM_SETTING_IP6_CONFIG_METHOD)) {
/* For now 'method' is the only difference we handle here */
return FALSE;
}
props = check_property_in_hash (settings,
NM_SETTING_IP6_CONFIG_SETTING_NAME,
NM_SETTING_IP6_CONFIG_METHOD);
if (!props)
return TRUE;
/* If the original connection is 'link-local' and the candidate is both 'auto'
* and may-fail=TRUE, then the candidate is OK to use. may-fail is included
@ -679,60 +704,41 @@ check_ip6_method_link_local_auto (NMConnection *orig,
if ( strcmp (orig_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0
&& strcmp (candidate_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0
&& (!candidate_ip6 || nm_setting_ip6_config_get_may_fail (candidate_ip6))) {
return TRUE;
}
return FALSE;
}
static gboolean
check_ip6_method_link_local_ignore (NMConnection *orig,
NMConnection *candidate,
GHashTable *settings)
{
GHashTable *props;
const char *orig_ip6_method, *candidate_ip6_method;
props = g_hash_table_lookup (settings, NM_SETTING_IP6_CONFIG_SETTING_NAME);
if ( !props
|| (g_hash_table_size (props) != 1)
|| !g_hash_table_lookup (props, NM_SETTING_IP6_CONFIG_METHOD)) {
/* We only handle ipv6 'method' here */
return FALSE;
allow = TRUE;
}
/* If the original connection method is 'link-local' and the candidate method
* is 'ignore' we can take the connection, because NM didn't simply take care
* of IPv6.
*/
orig_ip6_method = nm_utils_get_ip_config_method (orig, NM_TYPE_SETTING_IP6_CONFIG);
candidate_ip6_method = nm_utils_get_ip_config_method (candidate, NM_TYPE_SETTING_IP6_CONFIG);
if ( strcmp (orig_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0
&& strcmp (candidate_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) {
return TRUE;
allow = TRUE;
}
return FALSE;
if (allow) {
remove_from_hash (settings, props,
NM_SETTING_IP6_CONFIG_SETTING_NAME,
NM_SETTING_IP6_CONFIG_METHOD);
}
return allow;
}
static gboolean
check_ip4_method_disabled_auto (NMConnection *orig,
NMConnection *candidate,
GHashTable *settings,
gboolean device_has_carrier)
check_ip4_method (NMConnection *orig,
NMConnection *candidate,
GHashTable *settings,
gboolean device_has_carrier)
{
GHashTable *props;
const char *orig_ip4_method, *candidate_ip4_method;
NMSettingIP4Config *candidate_ip4;
props = g_hash_table_lookup (settings, NM_SETTING_IP4_CONFIG_SETTING_NAME);
if ( !props
|| (g_hash_table_size (props) != 1)
|| !g_hash_table_lookup (props, NM_SETTING_IP4_CONFIG_METHOD)) {
/* For now 'method' is the only difference we handle here */
return FALSE;
}
props = check_property_in_hash (settings,
NM_SETTING_IP4_CONFIG_SETTING_NAME,
NM_SETTING_IP4_CONFIG_METHOD);
if (!props)
return TRUE;
/* If the original connection is 'disabled' (device had no IP addresses)
* but it has no carrier, that most likely means that IP addressing could
@ -747,9 +753,11 @@ check_ip4_method_disabled_auto (NMConnection *orig,
&& strcmp (candidate_ip4_method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0
&& (!candidate_ip4 || nm_setting_ip4_config_get_may_fail (candidate_ip4))
&& (device_has_carrier == FALSE)) {
remove_from_hash (settings, props,
NM_SETTING_IP4_CONFIG_SETTING_NAME,
NM_SETTING_IP4_CONFIG_METHOD);
return TRUE;
}
return FALSE;
}
@ -762,13 +770,11 @@ check_connection_interface_name (NMConnection *orig,
const char *orig_ifname, *cand_ifname;
NMSettingConnection *s_con_orig, *s_con_cand;
props = g_hash_table_lookup (settings, NM_SETTING_CONNECTION_SETTING_NAME);
if ( !props
|| (g_hash_table_size (props) != 1)
|| !g_hash_table_lookup (props, NM_SETTING_CONNECTION_INTERFACE_NAME)) {
/* We only handle 'interface-name' here. */
return FALSE;
}
props = check_property_in_hash (settings,
NM_SETTING_CONNECTION_SETTING_NAME,
NM_SETTING_CONNECTION_INTERFACE_NAME);
if (!props)
return TRUE;
/* If one of the interface name is NULL, we accept that connection */
s_con_orig = nm_connection_get_setting_connection (orig);
@ -776,9 +782,42 @@ check_connection_interface_name (NMConnection *orig,
orig_ifname = nm_setting_connection_get_interface_name (s_con_orig);
cand_ifname = nm_setting_connection_get_interface_name (s_con_cand);
if (!orig_ifname || !cand_ifname)
if (!orig_ifname || !cand_ifname) {
remove_from_hash (settings, props,
NM_SETTING_CONNECTION_SETTING_NAME,
NM_SETTING_CONNECTION_INTERFACE_NAME);
return TRUE;
}
return FALSE;
}
static gboolean
check_connection_mac_address (NMConnection *orig,
NMConnection *candidate,
GHashTable *settings)
{
GHashTable *props;
const GByteArray *orig_mac, *cand_mac;
NMSettingWired *s_wired_orig, *s_wired_cand;
props = check_property_in_hash (settings,
NM_SETTING_WIRED_SETTING_NAME,
NM_SETTING_WIRED_MAC_ADDRESS);
if (!props)
return TRUE;
/* If one of the MAC addresses is NULL, we accept that connection */
s_wired_orig = nm_connection_get_setting_wired (orig);
s_wired_cand = nm_connection_get_setting_wired (candidate);
orig_mac = nm_setting_wired_get_mac_address (s_wired_orig);
cand_mac = nm_setting_wired_get_mac_address (s_wired_cand);
if (!orig_mac || !cand_mac) {
remove_from_hash (settings, props,
NM_SETTING_WIRED_SETTING_NAME,
NM_SETTING_WIRED_MAC_ADDRESS);
return TRUE;
}
return FALSE;
}
@ -790,19 +829,22 @@ check_possible_match (NMConnection *orig,
{
g_return_val_if_fail (settings != NULL, NULL);
if (check_ip6_method_link_local_auto (orig, candidate, settings))
return candidate;
if (!check_ip6_method (orig, candidate, settings))
return NULL;
if (check_ip6_method_link_local_ignore (orig, candidate, settings))
return candidate;
if (!check_ip4_method (orig, candidate, settings, device_has_carrier))
return NULL;
if (check_ip4_method_disabled_auto (orig, candidate, settings, device_has_carrier))
return candidate;
if (!check_connection_interface_name (orig, candidate, settings))
return NULL;
if (check_connection_interface_name (orig, candidate, settings))
return candidate;
if (!check_connection_mac_address (orig, candidate, settings))
return NULL;
return NULL;
if (g_hash_table_size (settings) == 0)
return candidate;
else
return NULL;
}
/**

View file

@ -15,7 +15,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2005 - 2013 Red Hat, Inc.
* Copyright (C) 2005 - 2014 Red Hat, Inc.
* Copyright (C) 2006 - 2008 Novell, Inc.
*/
@ -114,6 +114,8 @@ typedef struct {
char * subchan2;
char * subchan3;
char * subchannels; /* Composite used for checking unmanaged specs */
char * s390_nettype;
GHashTable * s390_options;
/* PPPoE */
NMPPPManager *ppp_manager;
@ -145,6 +147,25 @@ nm_ethernet_error_quark (void)
return quark;
}
static char *
get_link_basename (const char *parent_path, const char *name, GError **error)
{
char buf[128];
char *path;
char *result = NULL;
path = g_strdup_printf ("%s/%s", parent_path, name);
memset (buf, 0, sizeof (buf));
errno = 0;
if (readlink (path, &buf[0], sizeof (buf) - 1) >= 0)
result = g_path_get_basename (buf);
else
g_set_error (error, 0, 1, "failed to read link '%s': %d", path, errno);
g_free (path);
return result;
}
static void
_update_s390_subchannels (NMDeviceEthernet *self)
{
@ -192,34 +213,33 @@ _update_s390_subchannels (NMDeviceEthernet *self)
goto out;
}
/* FIXME: we probably care about ordering here to ensure that we map
* cdev0 -> subchan1, cdev1 -> subchan2, etc.
*/
while ((item = g_dir_read_name (dir))) {
char buf[50];
char *cdev_path;
if (strncmp (item, "cdev", 4))
continue; /* Not a subchannel link */
cdev_path = g_strdup_printf ("%s/%s", parent_path, item);
memset (buf, 0, sizeof (buf));
errno = 0;
if (readlink (cdev_path, &buf[0], sizeof (buf) - 1) >= 0) {
if (!priv->subchan1)
priv->subchan1 = g_path_get_basename (buf);
else if (!priv->subchan2)
priv->subchan2 = g_path_get_basename (buf);
else if (!priv->subchan3)
priv->subchan3 = g_path_get_basename (buf);
} else {
nm_log_warn (LOGD_DEVICE | LOGD_HW,
"(%s): failed to read cdev link '%s': %d",
iface, cdev_path, errno);
if (!strcmp (item, "cdev0")) {
priv->subchan1 = get_link_basename (parent_path, "cdev0", &error);
} else if (!strcmp (item, "cdev1")) {
priv->subchan2 = get_link_basename (parent_path, "cdev1", &error);
} else if (!strcmp (item, "cdev2")) {
priv->subchan3 = get_link_basename (parent_path, "cdev2", &error);
} else if (!strcmp (item, "driver")) {
priv->s390_nettype = get_link_basename (parent_path, "driver", &error);
} else if ( !strcmp (item, "layer2")
|| !strcmp (item, "portname")
|| !strcmp (item, "portno")) {
char *path, *value;
path = g_strdup_printf ("%s/%s", parent_path, item);
value = nm_platform_sysctl_get (path);
if (value && *value)
g_hash_table_insert (priv->s390_options, g_strdup (item), g_strdup (value));
else
nm_log_warn (LOGD_DEVICE | LOGD_HW, "(%s): error reading %s", iface, path);
g_free (path);
g_free (value);
}
g_free (cdev_path);
};
if (error) {
nm_log_warn (LOGD_DEVICE | LOGD_HW, "(%s): %s", iface, error->message);
g_clear_error (&error);
}
}
g_dir_close (dir);
@ -250,8 +270,8 @@ out:
static GObject*
constructor (GType type,
guint n_construct_params,
GObjectConstructParam *construct_params)
guint n_construct_params,
GObjectConstructParam *construct_params)
{
GObject *object;
NMDevice *self;
@ -268,8 +288,8 @@ constructor (GType type,
|| nm_platform_link_get_type (ifindex) == NM_LINK_TYPE_VETH);
nm_log_dbg (LOGD_HW | LOGD_ETHER, "(%s): kernel ifindex %d",
nm_device_get_iface (NM_DEVICE (self)),
nm_device_get_ifindex (NM_DEVICE (self)));
nm_device_get_iface (NM_DEVICE (self)),
nm_device_get_ifindex (NM_DEVICE (self)));
/* s390 stuff */
_update_s390_subchannels (NM_DEVICE_ETHERNET (self));
@ -305,8 +325,10 @@ device_state_changed (NMDevice *device,
}
static void
nm_device_ethernet_init (NMDeviceEthernet * self)
nm_device_ethernet_init (NMDeviceEthernet *self)
{
NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
priv->s390_options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
}
NMDevice *
@ -1543,6 +1565,8 @@ update_connection (NMDevice *device, NMConnection *connection)
static const guint8 null_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
const char *mac_prop = NM_SETTING_WIRED_MAC_ADDRESS;
GByteArray *array;
GHashTableIter iter;
gpointer key, value;
if (!s_wired) {
s_wired = (NMSettingWired *) nm_setting_wired_new ();
@ -1571,6 +1595,26 @@ update_connection (NMDevice *device, NMConnection *connection)
}
/* We don't set the MTU as we don't know whether it was set explicitly */
/* s390 */
if (priv->subchannels) {
GPtrArray *subchan_arr = g_ptr_array_sized_new (3);
if (priv->subchan1)
g_ptr_array_add (subchan_arr, priv->subchan1);
if (priv->subchan2)
g_ptr_array_add (subchan_arr, priv->subchan2);
if (priv->subchan3)
g_ptr_array_add (subchan_arr, priv->subchan3);
g_object_set (s_wired, NM_SETTING_WIRED_S390_SUBCHANNELS, subchan_arr, NULL);
g_ptr_array_free (subchan_arr, TRUE);
}
if (priv->s390_nettype)
g_object_set (s_wired, NM_SETTING_WIRED_S390_NETTYPE, priv->s390_nettype, NULL);
g_hash_table_iter_init (&iter, priv->s390_options);
while (g_hash_table_iter_next (&iter, &key, &value)) {
nm_setting_wired_add_s390_option (s_wired, (const char *) key, (const char *) value);
}
}
static void
@ -1633,12 +1677,6 @@ dispose (GObject *object)
NMDeviceEthernet *self = NM_DEVICE_ETHERNET (object);
NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
g_clear_object (&priv->supplicant.mgr);
g_free (priv->subchan1);
g_free (priv->subchan2);
g_free (priv->subchan3);
g_free (priv->subchannels);
if (priv->pppoe_wait_id) {
g_source_remove (priv->pppoe_wait_id);
priv->pppoe_wait_id = 0;
@ -1650,6 +1688,23 @@ dispose (GObject *object)
G_OBJECT_CLASS (nm_device_ethernet_parent_class)->dispose (object);
}
static void
finalize (GObject *object)
{
NMDeviceEthernet *self = NM_DEVICE_ETHERNET (object);
NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
g_clear_object (&priv->supplicant.mgr);
g_free (priv->subchan1);
g_free (priv->subchan2);
g_free (priv->subchan3);
g_free (priv->subchannels);
g_free (priv->s390_nettype);
g_hash_table_destroy (priv->s390_options);
G_OBJECT_CLASS (nm_device_ethernet_parent_class)->finalize (object);
}
static void
get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
@ -1694,6 +1749,7 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
/* virtual methods */
object_class->constructor = constructor;
object_class->dispose = dispose;
object_class->finalize = finalize;
object_class->get_property = get_property;
object_class->set_property = set_property;

View file

@ -21,6 +21,7 @@
#include <glib.h>
#include <string.h>
#include <errno.h>
#include <netinet/ether.h>
#include "NetworkManagerUtils.h"
#include "nm-utils.h"
@ -440,6 +441,116 @@ test_connection_match_interface_name (void)
g_object_unref (copy);
}
static void
test_connection_match_wired (void)
{
NMConnection *orig, *copy, *matched;
GSList *connections = NULL;
NMSettingWired *s_wired;
GPtrArray *subchan_arr = g_ptr_array_sized_new (3);
GByteArray *mac;
g_ptr_array_add (subchan_arr, "0.0.8000");
g_ptr_array_add (subchan_arr, "0.0.8001");
g_ptr_array_add (subchan_arr, "0.0.8002");
orig = _match_connection_new ();
copy = nm_connection_duplicate (orig);
connections = g_slist_append (connections, copy);
mac = nm_utils_hwaddr_atoba ("52:54:00:ab:db:23", ARPHRD_ETHER);
s_wired = nm_connection_get_setting_wired (orig);
g_assert (s_wired);
g_object_set (G_OBJECT (s_wired),
NM_SETTING_WIRED_PORT, "tp", /* port is not compared */
NM_SETTING_WIRED_MAC_ADDRESS, mac, /* we allow MAC address just in one connection */
NM_SETTING_WIRED_S390_SUBCHANNELS, subchan_arr,
NM_SETTING_WIRED_S390_NETTYPE, "qeth",
NULL);
g_byte_array_free (mac, TRUE);
s_wired = nm_connection_get_setting_wired (copy);
g_assert (s_wired);
g_object_set (G_OBJECT (s_wired),
NM_SETTING_WIRED_S390_SUBCHANNELS, subchan_arr,
NM_SETTING_WIRED_S390_NETTYPE, "qeth",
NULL);
matched = nm_utils_match_connection (connections, orig, TRUE, NULL, NULL);
g_assert (matched == copy);
g_slist_free (connections);
g_ptr_array_free (subchan_arr, TRUE);
g_object_unref (orig);
g_object_unref (copy);
}
static void
test_connection_no_match_ip4_addr (void)
{
NMConnection *orig, *copy, *matched;
GSList *connections = NULL;
NMSettingIP4Config *s_ip4;
NMSettingIP6Config *s_ip6;
NMIP4Address *nm_addr;
guint32 addr, gw;
orig = _match_connection_new ();
copy = nm_connection_duplicate (orig);
connections = g_slist_append (connections, copy);
/* Check that if we have two differences, ipv6.method (exception we allow) and
* ipv4.addresses (which is fatal), we don't match the connections.
*/
s_ip6 = nm_connection_get_setting_ip6_config (orig);
g_assert (s_ip6);
g_object_set (G_OBJECT (s_ip6),
NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL,
NULL);
s_ip6 = nm_connection_get_setting_ip6_config (copy);
g_assert (s_ip6);
g_object_set (G_OBJECT (s_ip6),
NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
NULL);
s_ip4 = nm_connection_get_setting_ip4_config (orig);
g_assert (s_ip4);
g_object_set (G_OBJECT (s_ip4),
NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
NULL);
nm_addr = nm_ip4_address_new ();
inet_pton (AF_INET, "1.1.1.4", &addr);
inet_pton (AF_INET, "1.1.1.254", &gw);
nm_ip4_address_set_address (nm_addr, addr);
nm_ip4_address_set_prefix (nm_addr, 24);
nm_ip4_address_set_gateway (nm_addr, gw);
nm_setting_ip4_config_add_address (s_ip4, nm_addr);
nm_ip4_address_unref (nm_addr);
s_ip4 = nm_connection_get_setting_ip4_config (copy);
g_assert (s_ip4);
g_object_set (G_OBJECT (s_ip4),
NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
NULL);
nm_addr = nm_ip4_address_new ();
inet_pton (AF_INET, "2.2.2.4", &addr);
inet_pton (AF_INET, "2.2.2.254", &gw);
nm_ip4_address_set_address (nm_addr, addr);
nm_ip4_address_set_prefix (nm_addr, 24);
nm_ip4_address_set_gateway (nm_addr, gw);
nm_setting_ip4_config_add_address (s_ip4, nm_addr);
nm_ip4_address_unref (nm_addr);
matched = nm_utils_match_connection (connections, orig, TRUE, NULL, NULL);
g_assert (matched != copy);
g_slist_free (connections);
g_object_unref (orig);
g_object_unref (copy);
}
/*******************************************/
int
@ -457,6 +568,8 @@ main (int argc, char **argv)
g_test_add_func ("/general/connection-match/ip6-method-ignore", test_connection_match_ip6_method_ignore);
g_test_add_func ("/general/connection-match/ip4-method", test_connection_match_ip4_method);
g_test_add_func ("/general/connection-match/con-interface-name", test_connection_match_interface_name);
g_test_add_func ("/general/connection-match/wired", test_connection_match_wired);
g_test_add_func ("/general/connection-match/no-match-ip4-addr", test_connection_no_match_ip4_addr);
return g_test_run ();
}