mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2025-12-25 22:20:08 +01:00
Keep the include paths clean and separate. We use directories to group source files together. That makes sense (I guess), but then we should use this grouping also when including files. Thus require to #include files with their path relative to "src/". Also, we build various artifacts from the "src/" tree. Instead of having individual CFLAGS for each artifact in Makefile.am, the CFLAGS should be unified. Previously, the CFLAGS for each artifact differ and are inconsistent in which paths they add to the search path. Fix the inconsistency by just don't add the paths at all.
833 lines
26 KiB
C
833 lines
26 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
|
|
/* NetworkManager -- Network link manager
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Copyright 2004 - 2016 Red Hat, Inc.
|
|
* Copyright 2005 - 2008 Novell, Inc.
|
|
*/
|
|
|
|
#include "nm-default.h"
|
|
|
|
#include "NetworkManagerUtils.h"
|
|
|
|
#include "nm-common-macros.h"
|
|
#include "nm-utils.h"
|
|
#include "nm-setting-connection.h"
|
|
#include "nm-setting-ip4-config.h"
|
|
#include "nm-setting-ip6-config.h"
|
|
#include "nm-core-internal.h"
|
|
|
|
#include "platform/nm-platform.h"
|
|
#include "nm-exported-object.h"
|
|
#include "nm-auth-utils.h"
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
* nm_utils_get_shared_wifi_permission:
|
|
* @connection: the NMConnection to lookup the permission.
|
|
*
|
|
* Returns: a static string of the wifi-permission (if any) or %NULL.
|
|
*/
|
|
const char *
|
|
nm_utils_get_shared_wifi_permission (NMConnection *connection)
|
|
{
|
|
NMSettingWireless *s_wifi;
|
|
NMSettingWirelessSecurity *s_wsec;
|
|
const char *method = NULL;
|
|
|
|
method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
|
|
if (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_SHARED) != 0)
|
|
return NULL; /* Not shared */
|
|
|
|
s_wifi = nm_connection_get_setting_wireless (connection);
|
|
if (s_wifi) {
|
|
s_wsec = nm_connection_get_setting_wireless_security (connection);
|
|
if (s_wsec)
|
|
return NM_AUTH_PERMISSION_WIFI_SHARE_PROTECTED;
|
|
else
|
|
return NM_AUTH_PERMISSION_WIFI_SHARE_OPEN;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static char *
|
|
get_new_connection_name (const GSList *existing,
|
|
const char *preferred,
|
|
const char *fallback_prefix)
|
|
{
|
|
GSList *names = NULL;
|
|
const GSList *iter;
|
|
char *cname = NULL;
|
|
int i = 0;
|
|
gboolean preferred_found = FALSE;
|
|
|
|
g_assert (fallback_prefix);
|
|
|
|
for (iter = existing; iter; iter = g_slist_next (iter)) {
|
|
NMConnection *candidate = NM_CONNECTION (iter->data);
|
|
const char *id;
|
|
|
|
id = nm_connection_get_id (candidate);
|
|
g_assert (id);
|
|
names = g_slist_append (names, (gpointer) id);
|
|
|
|
if (preferred && !preferred_found && (strcmp (preferred, id) == 0))
|
|
preferred_found = TRUE;
|
|
}
|
|
|
|
/* Return the preferred name if it was unique */
|
|
if (preferred && !preferred_found) {
|
|
g_slist_free (names);
|
|
return g_strdup (preferred);
|
|
}
|
|
|
|
/* Otherwise find the next available unique connection name using the given
|
|
* connection name template.
|
|
*/
|
|
while (!cname && (i++ < 10000)) {
|
|
char *temp;
|
|
gboolean found = FALSE;
|
|
|
|
/* Translators: the first %s is a prefix for the connection id, such
|
|
* as "Wired Connection" or "VPN Connection". The %d is a number
|
|
* that is combined with the first argument to create a unique
|
|
* connection id. */
|
|
temp = g_strdup_printf (C_("connection id fallback", "%s %d"),
|
|
fallback_prefix, i);
|
|
for (iter = names; iter; iter = g_slist_next (iter)) {
|
|
if (!strcmp (iter->data, temp)) {
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
cname = temp;
|
|
else
|
|
g_free (temp);
|
|
}
|
|
|
|
g_slist_free (names);
|
|
return cname;
|
|
}
|
|
|
|
static char *
|
|
get_new_connection_ifname (NMPlatform *platform,
|
|
const GSList *existing,
|
|
const char *prefix)
|
|
{
|
|
int i;
|
|
char *name;
|
|
const GSList *iter;
|
|
gboolean found;
|
|
|
|
for (i = 0; i < 500; i++) {
|
|
name = g_strdup_printf ("%s%d", prefix, i);
|
|
|
|
if (nm_platform_link_get_by_ifname (platform, name))
|
|
goto next;
|
|
|
|
for (iter = existing, found = FALSE; iter; iter = g_slist_next (iter)) {
|
|
NMConnection *candidate = iter->data;
|
|
|
|
if (g_strcmp0 (nm_connection_get_interface_name (candidate), name) == 0) {
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
return name;
|
|
|
|
next:
|
|
g_free (name);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
const char *
|
|
nm_utils_get_ip_config_method (NMConnection *connection,
|
|
GType ip_setting_type)
|
|
{
|
|
NMSettingConnection *s_con;
|
|
NMSettingIPConfig *s_ip4, *s_ip6;
|
|
const char *method;
|
|
|
|
s_con = nm_connection_get_setting_connection (connection);
|
|
|
|
if (ip_setting_type == NM_TYPE_SETTING_IP4_CONFIG) {
|
|
g_return_val_if_fail (s_con != NULL, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
|
|
|
|
if (nm_setting_connection_get_master (s_con))
|
|
return NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
|
|
else {
|
|
s_ip4 = nm_connection_get_setting_ip4_config (connection);
|
|
if (!s_ip4)
|
|
return NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
|
|
method = nm_setting_ip_config_get_method (s_ip4);
|
|
g_return_val_if_fail (method != NULL, NM_SETTING_IP4_CONFIG_METHOD_AUTO);
|
|
|
|
return method;
|
|
}
|
|
|
|
} else if (ip_setting_type == NM_TYPE_SETTING_IP6_CONFIG) {
|
|
g_return_val_if_fail (s_con != NULL, NM_SETTING_IP6_CONFIG_METHOD_AUTO);
|
|
|
|
if (nm_setting_connection_get_master (s_con))
|
|
return NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
|
|
else {
|
|
s_ip6 = nm_connection_get_setting_ip6_config (connection);
|
|
if (!s_ip6)
|
|
return NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
|
|
method = nm_setting_ip_config_get_method (s_ip6);
|
|
g_return_val_if_fail (method != NULL, NM_SETTING_IP6_CONFIG_METHOD_AUTO);
|
|
|
|
return method;
|
|
}
|
|
|
|
} else
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
void
|
|
nm_utils_complete_generic (NMPlatform *platform,
|
|
NMConnection *connection,
|
|
const char *ctype,
|
|
const GSList *existing,
|
|
const char *preferred_id,
|
|
const char *fallback_id_prefix,
|
|
const char *ifname_prefix,
|
|
gboolean default_enable_ipv6)
|
|
{
|
|
NMSettingConnection *s_con;
|
|
char *id, *uuid, *ifname;
|
|
GHashTable *parameters;
|
|
|
|
g_assert (fallback_id_prefix);
|
|
|
|
s_con = nm_connection_get_setting_connection (connection);
|
|
if (!s_con) {
|
|
s_con = (NMSettingConnection *) nm_setting_connection_new ();
|
|
nm_connection_add_setting (connection, NM_SETTING (s_con));
|
|
}
|
|
g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_TYPE, ctype, NULL);
|
|
|
|
if (!nm_setting_connection_get_uuid (s_con)) {
|
|
uuid = nm_utils_uuid_generate ();
|
|
g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_UUID, uuid, NULL);
|
|
g_free (uuid);
|
|
}
|
|
|
|
/* Add a connection ID if absent */
|
|
if (!nm_setting_connection_get_id (s_con)) {
|
|
id = get_new_connection_name (existing, preferred_id, fallback_id_prefix);
|
|
g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_ID, id, NULL);
|
|
g_free (id);
|
|
}
|
|
|
|
/* Add an interface name, if requested */
|
|
if (ifname_prefix && !nm_setting_connection_get_interface_name (s_con)) {
|
|
ifname = get_new_connection_ifname (platform, existing, ifname_prefix);
|
|
g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_INTERFACE_NAME, ifname, NULL);
|
|
g_free (ifname);
|
|
}
|
|
|
|
/* Normalize */
|
|
parameters = g_hash_table_new (g_str_hash, g_str_equal);
|
|
g_hash_table_insert (parameters, NM_CONNECTION_NORMALIZE_PARAM_IP6_CONFIG_METHOD,
|
|
default_enable_ipv6 ? NM_SETTING_IP6_CONFIG_METHOD_AUTO : NM_SETTING_IP6_CONFIG_METHOD_IGNORE);
|
|
nm_connection_normalize (connection, parameters, NULL, NULL);
|
|
g_hash_table_destroy (parameters);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
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)
|
|
{
|
|
if (!p_hash)
|
|
return;
|
|
|
|
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 (NMConnection *orig,
|
|
NMConnection *candidate,
|
|
GHashTable *settings)
|
|
{
|
|
GHashTable *props;
|
|
const char *orig_ip6_method, *candidate_ip6_method;
|
|
NMSettingIPConfig *candidate_ip6;
|
|
gboolean allow = FALSE;
|
|
|
|
props = check_property_in_hash (settings,
|
|
NM_SETTING_IP6_CONFIG_SETTING_NAME,
|
|
NM_SETTING_IP_CONFIG_METHOD);
|
|
if (!props)
|
|
return TRUE;
|
|
|
|
/* If the generated 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
|
|
* in the decision because if the candidate is 'auto' but may-fail=FALSE, then
|
|
* the connection could not possibly have been previously activated on the
|
|
* device if the device has no non-link-local IPv6 address.
|
|
*/
|
|
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);
|
|
candidate_ip6 = nm_connection_get_setting_ip6_config (candidate);
|
|
|
|
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_ip_config_get_may_fail (candidate_ip6))) {
|
|
allow = TRUE;
|
|
}
|
|
|
|
/* If the generated connection method is 'link-local' or 'auto' and the candidate
|
|
* method is 'ignore' we can take the connection, because NM didn't simply take care
|
|
* of IPv6.
|
|
*/
|
|
if ( ( strcmp (orig_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL) == 0
|
|
|| strcmp (orig_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0)
|
|
&& strcmp (candidate_ip6_method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) == 0) {
|
|
allow = TRUE;
|
|
}
|
|
|
|
if (allow) {
|
|
remove_from_hash (settings, props,
|
|
NM_SETTING_IP6_CONFIG_SETTING_NAME,
|
|
NM_SETTING_IP_CONFIG_METHOD);
|
|
}
|
|
return allow;
|
|
}
|
|
|
|
static int
|
|
route_compare (NMIPRoute *route1, NMIPRoute *route2, gint64 default_metric)
|
|
{
|
|
gint64 r, metric1, metric2;
|
|
|
|
r = g_strcmp0 (nm_ip_route_get_dest (route1), nm_ip_route_get_dest (route2));
|
|
if (r)
|
|
return r;
|
|
|
|
r = nm_ip_route_get_prefix (route1) - nm_ip_route_get_prefix (route2);
|
|
if (r)
|
|
return r > 0 ? 1 : -1;
|
|
|
|
r = g_strcmp0 (nm_ip_route_get_next_hop (route1), nm_ip_route_get_next_hop (route2));
|
|
if (r)
|
|
return r;
|
|
|
|
metric1 = nm_ip_route_get_metric (route1) == -1 ? default_metric : nm_ip_route_get_metric (route1);
|
|
metric2 = nm_ip_route_get_metric (route2) == -1 ? default_metric : nm_ip_route_get_metric (route2);
|
|
|
|
r = metric1 - metric2;
|
|
if (r)
|
|
return r > 0 ? 1 : -1;
|
|
|
|
r = nm_ip_route_get_family (route1) - nm_ip_route_get_family (route2);
|
|
if (r)
|
|
return r > 0 ? 1 : -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
route_ptr_compare (const void *a, const void *b)
|
|
{
|
|
return route_compare (*(NMIPRoute **) a, *(NMIPRoute **) b, -1);
|
|
}
|
|
|
|
static gboolean
|
|
check_ip_routes (NMConnection *orig,
|
|
NMConnection *candidate,
|
|
GHashTable *settings,
|
|
gint64 default_metric,
|
|
gboolean v4)
|
|
{
|
|
gs_free NMIPRoute **routes1 = NULL, **routes2 = NULL;
|
|
NMSettingIPConfig *s_ip1, *s_ip2;
|
|
const char *s_name;
|
|
GHashTable *props;
|
|
guint i, num;
|
|
|
|
s_name = v4 ? NM_SETTING_IP4_CONFIG_SETTING_NAME :
|
|
NM_SETTING_IP6_CONFIG_SETTING_NAME;
|
|
|
|
props = check_property_in_hash (settings,
|
|
s_name,
|
|
NM_SETTING_IP_CONFIG_ROUTES);
|
|
if (!props)
|
|
return TRUE;
|
|
|
|
s_ip1 = (NMSettingIPConfig *) nm_connection_get_setting_by_name (orig, s_name);
|
|
s_ip2 = (NMSettingIPConfig *) nm_connection_get_setting_by_name (candidate, s_name);
|
|
|
|
if (!s_ip1 || !s_ip2)
|
|
return FALSE;
|
|
|
|
num = nm_setting_ip_config_get_num_routes (s_ip1);
|
|
if (num != nm_setting_ip_config_get_num_routes (s_ip2))
|
|
return FALSE;
|
|
|
|
routes1 = g_new (NMIPRoute *, num);
|
|
routes2 = g_new (NMIPRoute *, num);
|
|
|
|
for (i = 0; i < num; i++) {
|
|
routes1[i] = nm_setting_ip_config_get_route (s_ip1, i);
|
|
routes2[i] = nm_setting_ip_config_get_route (s_ip2, i);
|
|
}
|
|
|
|
qsort (routes1, num, sizeof (NMIPRoute *), route_ptr_compare);
|
|
qsort (routes2, num, sizeof (NMIPRoute *), route_ptr_compare);
|
|
|
|
for (i = 0; i < num; i++) {
|
|
if (route_compare (routes1[i], routes2[i], default_metric))
|
|
return FALSE;
|
|
}
|
|
|
|
remove_from_hash (settings, props, s_name, NM_SETTING_IP_CONFIG_ROUTES);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
check_ip4_method (NMConnection *orig,
|
|
NMConnection *candidate,
|
|
GHashTable *settings,
|
|
gboolean device_has_carrier)
|
|
{
|
|
GHashTable *props;
|
|
const char *orig_ip4_method, *candidate_ip4_method;
|
|
NMSettingIPConfig *candidate_ip4;
|
|
|
|
props = check_property_in_hash (settings,
|
|
NM_SETTING_IP4_CONFIG_SETTING_NAME,
|
|
NM_SETTING_IP_CONFIG_METHOD);
|
|
if (!props)
|
|
return TRUE;
|
|
|
|
/* If the generated connection is 'disabled' (device had no IP addresses)
|
|
* but it has no carrier, that most likely means that IP addressing could
|
|
* not complete and thus no IP addresses were assigned. In that case, allow
|
|
* matching to the "auto" method.
|
|
*/
|
|
orig_ip4_method = nm_utils_get_ip_config_method (orig, NM_TYPE_SETTING_IP4_CONFIG);
|
|
candidate_ip4_method = nm_utils_get_ip_config_method (candidate, NM_TYPE_SETTING_IP4_CONFIG);
|
|
candidate_ip4 = nm_connection_get_setting_ip4_config (candidate);
|
|
|
|
if ( strcmp (orig_ip4_method, NM_SETTING_IP4_CONFIG_METHOD_DISABLED) == 0
|
|
&& strcmp (candidate_ip4_method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0
|
|
&& (!candidate_ip4 || nm_setting_ip_config_get_may_fail (candidate_ip4))
|
|
&& (device_has_carrier == FALSE)) {
|
|
remove_from_hash (settings, props,
|
|
NM_SETTING_IP4_CONFIG_SETTING_NAME,
|
|
NM_SETTING_IP_CONFIG_METHOD);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
check_connection_interface_name (NMConnection *orig,
|
|
NMConnection *candidate,
|
|
GHashTable *settings)
|
|
{
|
|
GHashTable *props;
|
|
const char *orig_ifname, *cand_ifname;
|
|
NMSettingConnection *s_con_orig, *s_con_cand;
|
|
|
|
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 names is NULL, we accept that connection */
|
|
s_con_orig = nm_connection_get_setting_connection (orig);
|
|
s_con_cand = nm_connection_get_setting_connection (candidate);
|
|
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) {
|
|
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 char *orig_mac = NULL, *cand_mac = NULL;
|
|
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);
|
|
if (s_wired_orig)
|
|
orig_mac = nm_setting_wired_get_mac_address (s_wired_orig);
|
|
|
|
s_wired_cand = nm_connection_get_setting_wired (candidate);
|
|
if (s_wired_cand)
|
|
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;
|
|
}
|
|
|
|
static gboolean
|
|
check_connection_infiniband_mac_address (NMConnection *orig,
|
|
NMConnection *candidate,
|
|
GHashTable *settings)
|
|
{
|
|
GHashTable *props;
|
|
const char *orig_mac = NULL, *cand_mac = NULL;
|
|
NMSettingInfiniband *s_infiniband_orig, *s_infiniband_cand;
|
|
|
|
props = check_property_in_hash (settings,
|
|
NM_SETTING_INFINIBAND_SETTING_NAME,
|
|
NM_SETTING_INFINIBAND_MAC_ADDRESS);
|
|
if (!props)
|
|
return TRUE;
|
|
|
|
/* If one of the MAC addresses is NULL, we accept that connection */
|
|
s_infiniband_orig = nm_connection_get_setting_infiniband (orig);
|
|
if (s_infiniband_orig)
|
|
orig_mac = nm_setting_infiniband_get_mac_address (s_infiniband_orig);
|
|
|
|
s_infiniband_cand = nm_connection_get_setting_infiniband (candidate);
|
|
if (s_infiniband_cand)
|
|
cand_mac = nm_setting_infiniband_get_mac_address (s_infiniband_cand);
|
|
|
|
if (!orig_mac || !cand_mac) {
|
|
remove_from_hash (settings, props,
|
|
NM_SETTING_INFINIBAND_SETTING_NAME,
|
|
NM_SETTING_INFINIBAND_MAC_ADDRESS);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
check_connection_cloned_mac_address (NMConnection *orig,
|
|
NMConnection *candidate,
|
|
GHashTable *settings)
|
|
{
|
|
GHashTable *props;
|
|
const char *orig_mac = NULL, *cand_mac = NULL;
|
|
NMSettingWired *s_wired_orig, *s_wired_cand;
|
|
|
|
props = check_property_in_hash (settings,
|
|
NM_SETTING_WIRED_SETTING_NAME,
|
|
NM_SETTING_WIRED_CLONED_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);
|
|
if (s_wired_orig)
|
|
orig_mac = nm_setting_wired_get_cloned_mac_address (s_wired_orig);
|
|
|
|
s_wired_cand = nm_connection_get_setting_wired (candidate);
|
|
if (s_wired_cand)
|
|
cand_mac = nm_setting_wired_get_cloned_mac_address (s_wired_cand);
|
|
|
|
/* special cloned mac address entires are accepted. */
|
|
if (NM_CLONED_MAC_IS_SPECIAL (orig_mac))
|
|
orig_mac = NULL;
|
|
if (NM_CLONED_MAC_IS_SPECIAL (cand_mac))
|
|
cand_mac = NULL;
|
|
|
|
if (!orig_mac || !cand_mac) {
|
|
remove_from_hash (settings, props,
|
|
NM_SETTING_WIRED_SETTING_NAME,
|
|
NM_SETTING_WIRED_CLONED_MAC_ADDRESS);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
check_connection_s390_props (NMConnection *orig,
|
|
NMConnection *candidate,
|
|
GHashTable *settings)
|
|
{
|
|
GHashTable *props1, *props2, *props3;
|
|
NMSettingWired *s_wired_orig, *s_wired_cand;
|
|
|
|
props1 = check_property_in_hash (settings,
|
|
NM_SETTING_WIRED_SETTING_NAME,
|
|
NM_SETTING_WIRED_S390_SUBCHANNELS);
|
|
props2 = check_property_in_hash (settings,
|
|
NM_SETTING_WIRED_SETTING_NAME,
|
|
NM_SETTING_WIRED_S390_NETTYPE);
|
|
props3 = check_property_in_hash (settings,
|
|
NM_SETTING_WIRED_SETTING_NAME,
|
|
NM_SETTING_WIRED_S390_OPTIONS);
|
|
if (!props1 && !props2 && !props3)
|
|
return TRUE;
|
|
|
|
/* If the generated connection did not contain wired setting,
|
|
* allow it to match to a connection with a wired setting,
|
|
* but default (empty) s390-* properties */
|
|
s_wired_orig = nm_connection_get_setting_wired (orig);
|
|
s_wired_cand = nm_connection_get_setting_wired (candidate);
|
|
if (!s_wired_orig && s_wired_cand) {
|
|
const char * const *subchans = nm_setting_wired_get_s390_subchannels (s_wired_cand);
|
|
const char *nettype = nm_setting_wired_get_s390_nettype (s_wired_cand);
|
|
guint32 num_options = nm_setting_wired_get_num_s390_options (s_wired_cand);
|
|
|
|
if ((!subchans || !*subchans) && !nettype && num_options == 0) {
|
|
remove_from_hash (settings, props1,
|
|
NM_SETTING_WIRED_SETTING_NAME,
|
|
NM_SETTING_WIRED_S390_SUBCHANNELS);
|
|
remove_from_hash (settings, props2,
|
|
NM_SETTING_WIRED_SETTING_NAME,
|
|
NM_SETTING_WIRED_S390_NETTYPE);
|
|
remove_from_hash (settings, props3,
|
|
NM_SETTING_WIRED_SETTING_NAME,
|
|
NM_SETTING_WIRED_S390_OPTIONS);
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static NMConnection *
|
|
check_possible_match (NMConnection *orig,
|
|
NMConnection *candidate,
|
|
GHashTable *settings,
|
|
gboolean device_has_carrier,
|
|
gint64 default_v4_metric,
|
|
gint64 default_v6_metric)
|
|
{
|
|
g_return_val_if_fail (settings != NULL, NULL);
|
|
|
|
if (!check_ip6_method (orig, candidate, settings))
|
|
return NULL;
|
|
|
|
if (!check_ip4_method (orig, candidate, settings, device_has_carrier))
|
|
return NULL;
|
|
|
|
if (!check_ip_routes (orig, candidate, settings, default_v4_metric, TRUE))
|
|
return NULL;
|
|
|
|
if (!check_ip_routes (orig, candidate, settings, default_v6_metric, FALSE))
|
|
return NULL;
|
|
|
|
if (!check_connection_interface_name (orig, candidate, settings))
|
|
return NULL;
|
|
|
|
if (!check_connection_mac_address (orig, candidate, settings))
|
|
return NULL;
|
|
|
|
if (!check_connection_infiniband_mac_address (orig, candidate, settings))
|
|
return NULL;
|
|
|
|
if (!check_connection_cloned_mac_address (orig, candidate, settings))
|
|
return NULL;
|
|
|
|
if (!check_connection_s390_props (orig, candidate, settings))
|
|
return NULL;
|
|
|
|
if (g_hash_table_size (settings) == 0)
|
|
return candidate;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* nm_utils_match_connection:
|
|
* @connections: a (optionally pre-sorted) list of connections from which to
|
|
* find a matching connection to @original based on "inferrable" properties
|
|
* @original: the #NMConnection to find a match for from @connections
|
|
* @device_has_carrier: pass %TRUE if the device that generated @original has
|
|
* a carrier, %FALSE if not
|
|
* @match_filter_func: a function to check whether each connection from @connections
|
|
* should be considered for matching. This function should return %TRUE if the
|
|
* connection should be considered, %FALSE if the connection should be ignored
|
|
* @match_compat_data: data pointer passed to @match_filter_func
|
|
*
|
|
* Checks each connection from @connections until a matching connection is found
|
|
* considering only setting properties marked with %NM_SETTING_PARAM_INFERRABLE
|
|
* and checking a few other characteristics like IPv6 method. If the caller
|
|
* desires some priority order of the connections, @connections should be
|
|
* sorted before calling this function.
|
|
*
|
|
* Returns: the best #NMConnection matching @original, or %NULL if no connection
|
|
* matches well enough.
|
|
*/
|
|
NMConnection *
|
|
nm_utils_match_connection (GSList *connections,
|
|
NMConnection *original,
|
|
gboolean device_has_carrier,
|
|
gint64 default_v4_metric,
|
|
gint64 default_v6_metric,
|
|
NMUtilsMatchFilterFunc match_filter_func,
|
|
gpointer match_filter_data)
|
|
{
|
|
NMConnection *best_match = NULL;
|
|
GSList *iter;
|
|
|
|
for (iter = connections; iter; iter = iter->next) {
|
|
NMConnection *candidate = NM_CONNECTION (iter->data);
|
|
GHashTable *diffs = NULL;
|
|
|
|
if (match_filter_func) {
|
|
if (!match_filter_func (candidate, match_filter_data))
|
|
continue;
|
|
}
|
|
|
|
if (!nm_connection_diff (original, candidate, NM_SETTING_COMPARE_FLAG_INFERRABLE, &diffs)) {
|
|
if (!best_match) {
|
|
best_match = check_possible_match (original, candidate, diffs, device_has_carrier,
|
|
default_v4_metric, default_v6_metric);
|
|
}
|
|
|
|
if (!best_match && nm_logging_enabled (LOGL_DEBUG, LOGD_CORE)) {
|
|
GString *diff_string;
|
|
GHashTableIter s_iter, p_iter;
|
|
gpointer setting_name, setting;
|
|
gpointer property_name, value;
|
|
|
|
diff_string = g_string_new (NULL);
|
|
g_hash_table_iter_init (&s_iter, diffs);
|
|
while (g_hash_table_iter_next (&s_iter, &setting_name, &setting)) {
|
|
g_hash_table_iter_init (&p_iter, setting);
|
|
while (g_hash_table_iter_next (&p_iter, &property_name, &value)) {
|
|
if (diff_string->len)
|
|
g_string_append (diff_string, ", ");
|
|
g_string_append_printf (diff_string, "%s.%s",
|
|
(char *) setting_name,
|
|
(char *) property_name);
|
|
}
|
|
}
|
|
|
|
nm_log_dbg (LOGD_CORE, "Connection '%s' differs from candidate '%s' in %s",
|
|
nm_connection_get_id (original),
|
|
nm_connection_get_id (candidate),
|
|
diff_string->str);
|
|
g_string_free (diff_string, TRUE);
|
|
}
|
|
|
|
g_hash_table_unref (diffs);
|
|
continue;
|
|
}
|
|
|
|
/* Exact match */
|
|
return candidate;
|
|
}
|
|
|
|
/* Best match (if any) */
|
|
return best_match;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
* nm_utils_g_value_set_object_path:
|
|
* @value: a #GValue, initialized to store an object path
|
|
* @object: (allow-none): an #NMExportedObject
|
|
*
|
|
* Sets @value to @object's object path. If @object is %NULL, or not
|
|
* exported, @value is set to "/".
|
|
*/
|
|
void
|
|
nm_utils_g_value_set_object_path (GValue *value, gpointer object)
|
|
{
|
|
g_return_if_fail (!object || NM_IS_EXPORTED_OBJECT (object));
|
|
|
|
if (object && nm_exported_object_is_exported (object))
|
|
g_value_set_string (value, nm_exported_object_get_path (object));
|
|
else
|
|
g_value_set_string (value, "/");
|
|
}
|
|
|
|
/**
|
|
* nm_utils_g_value_set_object_path_array:
|
|
* @value: a #GValue, initialized to store an object path
|
|
* @objects: a #GSList of #NMExportedObjects
|
|
* @filter_func: (allow-none): function to call on each object in @objects
|
|
* @user_data: data to pass to @filter_func
|
|
*
|
|
* Sets @value to an array of object paths of the objects in @objects.
|
|
*/
|
|
void
|
|
nm_utils_g_value_set_object_path_array (GValue *value,
|
|
GSList *objects,
|
|
NMUtilsObjectFunc filter_func,
|
|
gpointer user_data)
|
|
{
|
|
char **paths;
|
|
guint i;
|
|
GSList *iter;
|
|
|
|
paths = g_new (char *, g_slist_length (objects) + 1);
|
|
for (i = 0, iter = objects; iter; iter = iter->next) {
|
|
NMExportedObject *object = iter->data;
|
|
const char *path;
|
|
|
|
path = nm_exported_object_get_path (object);
|
|
if (!path)
|
|
continue;
|
|
if (filter_func && !filter_func ((GObject *) object, user_data))
|
|
continue;
|
|
paths[i++] = g_strdup (path);
|
|
}
|
|
paths[i] = NULL;
|
|
g_value_take_boxed (value, paths);
|
|
}
|
|
|
|
/*****************************************************************************/
|