diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index 17116ee6cb..52b7c43827 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -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; } /** diff --git a/src/devices/nm-device-ethernet.c b/src/devices/nm-device-ethernet.c index e6f091d62c..79e9086fcd 100644 --- a/src/devices/nm-device-ethernet.c +++ b/src/devices/nm-device-ethernet.c @@ -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; diff --git a/src/tests/test-general.c b/src/tests/test-general.c index ecbda9a72b..207191fea1 100644 --- a/src/tests/test-general.c +++ b/src/tests/test-general.c @@ -21,6 +21,7 @@ #include #include #include +#include #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 (); }