summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2013-12-05 17:25:33 -0600
committerDan Williams <dcbw@redhat.com>2013-12-05 17:25:33 -0600
commit597c1c7a9a8448c4e6adc435b0e31609b0cb3e85 (patch)
treeba98c1c26fa4cf1bf2757caefba5057af4fe65da
parent5f32b8588ec4f70ebff88c9f7803353a1894e6c3 (diff)
parent6abc7b78f68e2e815bf8a8cec2a3235e35bb07e4 (diff)
downloadNetworkManager-597c1c7a9a8448c4e6adc435b0e31609b0cb3e85.tar.gz
core: fuzzier matching of connections on startup (rh #1029859)
Because it's not trivial to generate a connection that exactly matches one which was applied by NetworkManager before a restart, we need to make matching somewhat fuzzier. Mark any setting property that can be read from the system or kernel as INFERRABLE, and match only on those properties when trying to find the persistent connection (if any) which is already active on that device. https://bugzilla.redhat.com/show_bug.cgi?id=1029859
-rw-r--r--libnm-util/nm-connection.c4
-rw-r--r--libnm-util/nm-setting-bluetooth.c4
-rw-r--r--libnm-util/nm-setting-bond.c4
-rw-r--r--libnm-util/nm-setting-bridge-port.c7
-rw-r--r--libnm-util/nm-setting-bridge.c14
-rw-r--r--libnm-util/nm-setting-connection.c8
-rw-r--r--libnm-util/nm-setting-infiniband.c8
-rw-r--r--libnm-util/nm-setting-ip4-config.c12
-rw-r--r--libnm-util/nm-setting-ip6-config.c8
-rw-r--r--libnm-util/nm-setting-olpc-mesh.c4
-rw-r--r--libnm-util/nm-setting-private.h25
-rw-r--r--libnm-util/nm-setting-team-port.c2
-rw-r--r--libnm-util/nm-setting-team.c4
-rw-r--r--libnm-util/nm-setting-vlan.c12
-rw-r--r--libnm-util/nm-setting-wired.c10
-rw-r--r--libnm-util/nm-setting.c23
-rw-r--r--libnm-util/nm-setting.h3
-rw-r--r--libnm-util/tests/test-general.c65
-rw-r--r--src/NetworkManagerUtils.c90
-rw-r--r--src/NetworkManagerUtils.h8
-rw-r--r--src/nm-ip4-config.c16
-rw-r--r--src/nm-ip6-config.c16
-rw-r--r--src/nm-manager.c35
-rw-r--r--src/settings/nm-settings.c17
-rw-r--r--src/settings/nm-settings.h2
25 files changed, 306 insertions, 95 deletions
diff --git a/libnm-util/nm-connection.c b/libnm-util/nm-connection.c
index 7e588aeec1..991fed62a8 100644
--- a/libnm-util/nm-connection.c
+++ b/libnm-util/nm-connection.c
@@ -451,10 +451,6 @@ nm_connection_compare (NMConnection *a,
if (!a || !b)
return FALSE;
- /* CANDIDATE flag also implies FUZZY */
- if (flags & NM_SETTING_COMPARE_FLAG_CANDIDATE)
- flags |= NM_SETTING_COMPARE_FLAG_FUZZY;
-
/* A / B: ensure all settings in A match corresponding ones in B */
g_hash_table_iter_init (&iter, NM_CONNECTION_GET_PRIVATE (a)->settings);
while (g_hash_table_iter_next (&iter, NULL, (gpointer) &src)) {
diff --git a/libnm-util/nm-setting-bluetooth.c b/libnm-util/nm-setting-bluetooth.c
index d03c04cc80..874effcf3a 100644
--- a/libnm-util/nm-setting-bluetooth.c
+++ b/libnm-util/nm-setting-bluetooth.c
@@ -284,7 +284,7 @@ nm_setting_bluetooth_class_init (NMSettingBluetoothClass *setting_class)
"Bluetooth address",
"The Bluetooth address of the device",
DBUS_TYPE_G_UCHAR_ARRAY,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingBluetooth:type:
@@ -301,5 +301,5 @@ nm_setting_bluetooth_class_init (NMSettingBluetoothClass *setting_class)
"'" NM_SETTING_BLUETOOTH_TYPE_PANU "' for "
"Personal Area Networking connections.",
NULL,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
}
diff --git a/libnm-util/nm-setting-bond.c b/libnm-util/nm-setting-bond.c
index 3459300ec9..426dfe03df 100644
--- a/libnm-util/nm-setting-bond.c
+++ b/libnm-util/nm-setting-bond.c
@@ -802,7 +802,7 @@ nm_setting_bond_class_init (NMSettingBondClass *setting_class)
"InterfaceName",
"The name of the virtual in-kernel bonding network interface",
NULL,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingBond:options:
@@ -820,5 +820,5 @@ nm_setting_bond_class_init (NMSettingBondClass *setting_class)
"strings. Option names must contain only "
"alphanumeric characters (ie, [a-zA-Z0-9]).",
DBUS_TYPE_G_MAP_OF_STRING,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
}
diff --git a/libnm-util/nm-setting-bridge-port.c b/libnm-util/nm-setting-bridge-port.c
index 07d5dbd6bd..214e9d3cfb 100644
--- a/libnm-util/nm-setting-bridge-port.c
+++ b/libnm-util/nm-setting-bridge-port.c
@@ -269,7 +269,7 @@ nm_setting_bridge_port_class_init (NMSettingBridgePortClass *setting_class)
"Priority",
"The Spanning Tree Protocol (STP) priority of this bridge port",
0, BR_MAX_PORT_PRIORITY, BR_DEF_PRIORITY,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingBridgePort:path-cost:
@@ -285,7 +285,7 @@ nm_setting_bridge_port_class_init (NMSettingBridgePortClass *setting_class)
"The Spanning Tree Protocol (STP) port cost for "
"destinations via this port.",
0, BR_MAX_PATH_COST, 100,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingBridgePort:hairpin-mode:
@@ -303,6 +303,5 @@ nm_setting_bridge_port_class_init (NMSettingBridgePortClass *setting_class)
"port, which allows frames to be sent back out "
"through the port the frame was received on.",
FALSE,
- G_PARAM_READWRITE));
-
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
}
diff --git a/libnm-util/nm-setting-bridge.c b/libnm-util/nm-setting-bridge.c
index f5131e50a3..0890c45173 100644
--- a/libnm-util/nm-setting-bridge.c
+++ b/libnm-util/nm-setting-bridge.c
@@ -434,7 +434,7 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *setting_class)
"InterfaceName",
"The name of the virtual in-kernel bridging network interface",
NULL,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingBridge:stp:
@@ -450,7 +450,7 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *setting_class)
"Controls whether Spanning Tree Protocol (STP) "
"is enabled for this bridge.",
TRUE,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingBridge:priority:
@@ -470,7 +470,7 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *setting_class)
"lowest priority bridge will be elected the root "
"bridge.",
0, G_MAXUINT16, 0x80,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingBridge:forward-delay:
@@ -486,7 +486,7 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *setting_class)
"The Spanning Tree Protocol (STP) forwarding "
"delay, in seconds.",
0, BR_MAX_FORWARD_DELAY, 15,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingBridge:hello-time:
@@ -502,7 +502,7 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *setting_class)
"The Spanning Tree Protocol (STP) hello time, in "
"seconds.",
0, BR_MAX_HELLO_TIME, 2,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingBridge:max-age:
@@ -518,7 +518,7 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *setting_class)
"The Spanning Tree Protocol (STP) maximum message "
"age, in seconds.",
0, BR_MAX_MAX_AGE, 20,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingBridge:ageing-time:
@@ -533,6 +533,6 @@ nm_setting_bridge_class_init (NMSettingBridgeClass *setting_class)
"AgeingTime",
"The ethernet MAC address aging time, in seconds.",
0, BR_MAX_AGEING_TIME, 300,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_INFERRABLE));
}
diff --git a/libnm-util/nm-setting-connection.c b/libnm-util/nm-setting-connection.c
index 19c57d1ae2..48ca028a38 100644
--- a/libnm-util/nm-setting-connection.c
+++ b/libnm-util/nm-setting-connection.c
@@ -1099,7 +1099,7 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class)
"reordered the connection may be applied to the wrong "
"interface.",
NULL,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingConnection:type:
@@ -1121,7 +1121,7 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class)
"otherwise, should contain the setting name of that "
"setting type (ie, 'vpn' or 'bridge', etc).",
NULL,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingConnection:permissions:
@@ -1251,7 +1251,7 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class)
"Interface name of the master device or UUID of "
"the master connection",
NULL,
- G_PARAM_READWRITE | NM_SETTING_PARAM_FUZZY_IGNORE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingConnection:slave-type:
@@ -1267,7 +1267,7 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class)
"this connection is (ie, 'bond') or NULL if this "
"connection is not a slave.",
NULL,
- G_PARAM_READWRITE | NM_SETTING_PARAM_FUZZY_IGNORE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingConnection:secondaries:
diff --git a/libnm-util/nm-setting-infiniband.c b/libnm-util/nm-setting-infiniband.c
index 9739afeadf..4995940239 100644
--- a/libnm-util/nm-setting-infiniband.c
+++ b/libnm-util/nm-setting-infiniband.c
@@ -367,7 +367,7 @@ nm_setting_infiniband_class_init (NMSettingInfinibandClass *setting_class)
"This property does not change the MAC address "
"of the device (i.e. MAC spoofing).",
DBUS_TYPE_G_UCHAR_ARRAY,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingInfiniband:mtu:
@@ -397,7 +397,7 @@ nm_setting_infiniband_class_init (NMSettingInfinibandClass *setting_class)
"Transport Mode",
"The IPoIB transport mode. Either 'datagram' or 'connected'.",
NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingInfiniband:p-key:
@@ -414,7 +414,7 @@ nm_setting_infiniband_class_init (NMSettingInfinibandClass *setting_class)
"The InfiniBand P_Key. Either -1 for the "
"default, or a 16-bit unsigned integer.",
-1, 0xFFFF, -1,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingInfiniband:parent:
@@ -430,6 +430,6 @@ nm_setting_infiniband_class_init (NMSettingInfinibandClass *setting_class)
"Parent",
"The interface name of the parent device, or NULL",
NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_INFERRABLE));
}
diff --git a/libnm-util/nm-setting-ip4-config.c b/libnm-util/nm-setting-ip4-config.c
index 83d72d6c5f..5fd1cfd48e 100644
--- a/libnm-util/nm-setting-ip4-config.c
+++ b/libnm-util/nm-setting-ip4-config.c
@@ -1027,7 +1027,7 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *setting_class)
"connection. 'disabled' means IPv4 will not be "
"used on this connection. This property must be set.",
NULL,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingIP4Config:dns:
@@ -1107,7 +1107,7 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *setting_class)
"'disabled' methods as addressing is either "
"automatic or disabled with these methods.",
DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingIP4Config:routes:
@@ -1138,7 +1138,7 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *setting_class)
"'shared', 'link-local', or 'disabled', methods "
"as there is no upstream network.",
DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingIP4Config:ignore-auto-routes:
@@ -1231,7 +1231,7 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *setting_class)
"the specified name will be sent to the DHCP server "
"when acquiring a lease.",
NULL,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingIP4Config:never-default:
@@ -1247,7 +1247,7 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *setting_class)
"IPv4 connection, meaning it will never be assigned "
"the default route by NetworkManager.",
FALSE,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingIP4Config:may-fail:
@@ -1272,7 +1272,7 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *setting_class)
"configuration to succeed if IPv4 configuration "
"fails but IPv6 configuration completes successfully.",
TRUE,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_INFERRABLE));
}
diff --git a/libnm-util/nm-setting-ip6-config.c b/libnm-util/nm-setting-ip6-config.c
index 1ed89f7939..c4ec4d373e 100644
--- a/libnm-util/nm-setting-ip6-config.c
+++ b/libnm-util/nm-setting-ip6-config.c
@@ -924,7 +924,7 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *setting_class)
"property must be set. Note: the 'shared' method "
"is not yet supported.",
NULL,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingIP6Config:dhcp-hostname:
@@ -1028,7 +1028,7 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *setting_class)
"as the interface is automatically assigned an "
"address with these methods.",
DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingIP6Config:routes:
@@ -1059,7 +1059,7 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *setting_class)
"used with the 'shared' or 'link-local' methods "
"because there is no upstream network.",
DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingIP6Config:ignore-auto-routes:
@@ -1167,7 +1167,7 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *setting_class)
NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN,
NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR,
NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_INFERRABLE));
}
/********************************************************************/
diff --git a/libnm-util/nm-setting-olpc-mesh.c b/libnm-util/nm-setting-olpc-mesh.c
index 834aa29028..c98bad5432 100644
--- a/libnm-util/nm-setting-olpc-mesh.c
+++ b/libnm-util/nm-setting-olpc-mesh.c
@@ -248,7 +248,7 @@ nm_setting_olpc_mesh_class_init (NMSettingOlpcMeshClass *setting_class)
"SSID",
"SSID of the mesh network to join.",
DBUS_TYPE_G_UCHAR_ARRAY,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingOlpcMesh:channel:
@@ -261,7 +261,7 @@ nm_setting_olpc_mesh_class_init (NMSettingOlpcMeshClass *setting_class)
"Channel",
"Channel on which the mesh network to join is located.",
0, G_MAXUINT32, 0,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingOlpcMesh:dhcp-anycast-address:
diff --git a/libnm-util/nm-setting-private.h b/libnm-util/nm-setting-private.h
index 485a667d37..a90721e93a 100644
--- a/libnm-util/nm-setting-private.h
+++ b/libnm-util/nm-setting-private.h
@@ -38,13 +38,26 @@ GType _nm_setting_lookup_setting_type (const char *name);
GType _nm_setting_lookup_setting_type_by_quark (GQuark error_quark);
gint _nm_setting_compare_priority (gconstpointer a, gconstpointer b);
-/* NM_SETTING_COMPARE_FLAG_CANDIDATE: check a whether a device-generated connection
- * can be meaningfully replaced by a configured connection. With this flag
- * the matching function is asymetric and only takes into account properties
- * mandated by the candidate configured connection. It is for internal use by
- * NetworkManager.
+/* NM_SETTING_COMPARE_FLAG_INFERRABLE: check whether a device-generated
+ * connection can be replaced by a already-defined connection. This flag only
+ * takes into account properties marked with the %NM_SETTING_PARAM_INFERRABLE
+ * flag.
*/
-#define NM_SETTING_COMPARE_FLAG_CANDIDATE 0x80000000
+#define NM_SETTING_COMPARE_FLAG_INFERRABLE 0x80000000
+
+/* The property of the #NMSetting should be considered during comparisons that
+ * use the %NM_SETTING_COMPARE_FLAG_INFERRABLE flag. Properties that don't have
+ * this flag, are ignored when doing an infrerrable comparison. This flag should
+ * be set on all properties that are read from the kernel or the system when a
+ * connection is generated. eg, IP addresses/routes can be read from the
+ * kernel, but the 'autoconnect' property cannot, so
+ * %NM_SETTING_IP4_CONFIG_ADDRESSES gets the INFERRABLE flag, but
+ * %NM_SETTING_CONNECTION_AUTOCONNECT would not.
+ *
+ * This flag should not be used with properties where the default cannot be
+ * read separately from the current value, like MTU or wired duplex mode.
+ */
+#define NM_SETTING_PARAM_INFERRABLE (1 << (4 + G_PARAM_USER_SHIFT))
/* Ensure the setting's GType is registered at library load time */
#define NM_SETTING_REGISTER_TYPE(x) \
diff --git a/libnm-util/nm-setting-team-port.c b/libnm-util/nm-setting-team-port.c
index e70c24714a..0b61bab871 100644
--- a/libnm-util/nm-setting-team-port.c
+++ b/libnm-util/nm-setting-team-port.c
@@ -179,5 +179,5 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *setting_class)
"teamd. If not specified, the dafault configuration is used. "
"See man teamd.conf for the format details.",
NULL,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
}
diff --git a/libnm-util/nm-setting-team.c b/libnm-util/nm-setting-team.c
index 35d4ef719e..06dd064108 100644
--- a/libnm-util/nm-setting-team.c
+++ b/libnm-util/nm-setting-team.c
@@ -238,7 +238,7 @@ nm_setting_team_class_init (NMSettingTeamClass *setting_class)
"InterfaceName",
"The name of the virtual in-kernel team network interface",
NULL,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingTeam:config:
@@ -259,5 +259,5 @@ nm_setting_team_class_init (NMSettingTeamClass *setting_class)
"teamd. If not specified, the default configuration is used. "
"See man teamd.conf for the format details.",
NULL,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
}
diff --git a/libnm-util/nm-setting-vlan.c b/libnm-util/nm-setting-vlan.c
index 32f8e35c94..9ea6fc09fa 100644
--- a/libnm-util/nm-setting-vlan.c
+++ b/libnm-util/nm-setting-vlan.c
@@ -696,7 +696,7 @@ nm_setting_vlan_class_init (NMSettingVlanClass *setting_class)
"the 'parent' property or by the 'mac-address' "
"property of a 'wired' setting.",
NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingVlan:parent:
@@ -716,7 +716,7 @@ nm_setting_vlan_class_init (NMSettingVlanClass *setting_class)
"not specified, the connection must contain a "
"'wired' setting with a 'mac-address' property.",
NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingVlan:id:
@@ -731,7 +731,7 @@ nm_setting_vlan_class_init (NMSettingVlanClass *setting_class)
"The VLAN indentifier the interface created by "
"this connection should be assigned.",
0, 4095, 0,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingVlan:flags:
@@ -750,7 +750,7 @@ nm_setting_vlan_class_init (NMSettingVlanClass *setting_class)
"of the interface to its master device's operating "
"state (0x04).",
0, G_MAXUINT32, 0,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingVlan:ingress-priority-map:
@@ -769,7 +769,7 @@ nm_setting_vlan_class_init (NMSettingVlanClass *setting_class)
"format 'from:to' where both 'from' and "
"'to' are unsigned integers, ie '7:3'.",
DBUS_TYPE_G_LIST_OF_STRING,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingVlan:egress-priority-map:
@@ -788,5 +788,5 @@ nm_setting_vlan_class_init (NMSettingVlanClass *setting_class)
"format 'from:to' where both 'from' and "
"'to' are unsigned integers, ie '7:3'.",
DBUS_TYPE_G_LIST_OF_STRING,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
}
diff --git a/libnm-util/nm-setting-wired.c b/libnm-util/nm-setting-wired.c
index d04e70fbe1..67f0fa26eb 100644
--- a/libnm-util/nm-setting-wired.c
+++ b/libnm-util/nm-setting-wired.c
@@ -897,7 +897,7 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_class)
"This property does not change the MAC address "
"of the device (i.e. MAC spoofing).",
DBUS_TYPE_G_UCHAR_ARRAY,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingWired:cloned-mac-address:
@@ -913,7 +913,7 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_class)
"this MAC address instead of its permanent MAC address. "
"This is known as MAC cloning or spoofing.",
DBUS_TYPE_G_UCHAR_ARRAY,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingWired:mac-address-blacklist:
@@ -974,7 +974,7 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_class)
"string may only be composed of hexadecimal "
"characters and the period (.) character.",
DBUS_TYPE_G_ARRAY_OF_STRING,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingWired:s390-nettype:
@@ -990,7 +990,7 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_class)
"'ctc', representing the different types of virtual "
"network devices available on s390 systems.",
NULL,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
/**
* NMSettingWired:s390-options:
@@ -1009,6 +1009,6 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_class)
"strings. Allowed keys include 'portno', "
"'layer2', 'portname', 'protocol', among others.",
DBUS_TYPE_G_MAP_OF_STRING,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE));
}
diff --git a/libnm-util/nm-setting.c b/libnm-util/nm-setting.c
index ceabc56451..a0e287339b 100644
--- a/libnm-util/nm-setting.c
+++ b/libnm-util/nm-setting.c
@@ -569,6 +569,9 @@ nm_setting_compare (NMSetting *a,
&& (prop_spec->flags & (NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_SECRET)))
continue;
+ if ((flags & NM_SETTING_COMPARE_FLAG_INFERRABLE) && !(prop_spec->flags & NM_SETTING_PARAM_INFERRABLE))
+ continue;
+
if ( (flags & NM_SETTING_COMPARE_FLAG_IGNORE_SECRETS)
&& (prop_spec->flags & NM_SETTING_PARAM_SECRET))
continue;
@@ -591,6 +594,9 @@ should_compare_prop (NMSetting *setting,
&& (prop_flags & (NM_SETTING_PARAM_FUZZY_IGNORE | NM_SETTING_PARAM_SECRET)))
return FALSE;
+ if ((comp_flags & NM_SETTING_COMPARE_FLAG_INFERRABLE) && !(prop_flags & NM_SETTING_PARAM_INFERRABLE))
+ return FALSE;
+
if (prop_flags & NM_SETTING_PARAM_SECRET) {
NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
@@ -693,22 +699,19 @@ nm_setting_diff (NMSetting *a,
continue;
if (b) {
- g_value_init (&a_value, prop_spec->value_type);
- g_object_get_property (G_OBJECT (a), prop_spec->name, &a_value);
-
- g_value_init (&b_value, prop_spec->value_type);
- g_object_get_property (G_OBJECT (b), prop_spec->name, &b_value);
-
- different = !!g_param_values_cmp (prop_spec, &a_value, &b_value);
+ different = !NM_SETTING_GET_CLASS (a)->compare_property (a, b, prop_spec, flags);
if (different) {
+ g_value_init (&a_value, prop_spec->value_type);
+ g_value_init (&b_value, prop_spec->value_type);
+
if (!g_param_value_defaults (prop_spec, &a_value))
r |= a_result;
if (!g_param_value_defaults (prop_spec, &b_value))
r |= b_result;
- }
- g_value_unset (&a_value);
- g_value_unset (&b_value);
+ g_value_unset (&a_value);
+ g_value_unset (&b_value);
+ }
} else
r = a_result; /* only in A */
diff --git a/libnm-util/nm-setting.h b/libnm-util/nm-setting.h
index ca8334f6f8..e054288509 100644
--- a/libnm-util/nm-setting.h
+++ b/libnm-util/nm-setting.h
@@ -78,6 +78,9 @@ GQuark nm_setting_error_quark (void);
*/
#define NM_SETTING_PARAM_FUZZY_IGNORE (1 << (3 + G_PARAM_USER_SHIFT))
+/* Note: all non-glib GParamFlags bits are reserved by NetworkManager */
+
+
#define NM_SETTING_NAME "name"
/**
diff --git a/libnm-util/tests/test-general.c b/libnm-util/tests/test-general.c
index 56d6e679e2..e4703d267c 100644
--- a/libnm-util/tests/test-general.c
+++ b/libnm-util/tests/test-general.c
@@ -28,6 +28,7 @@
#include "nm-test-helpers.h"
#include <nm-utils.h>
+#include "nm-setting-private.h"
#include "nm-setting-connection.h"
#include "nm-setting-vpn.h"
#include "nm-setting-gsm.h"
@@ -1062,7 +1063,7 @@ test_connection_compare_key_only_in_a (void)
b = nm_connection_duplicate (a);
s_con = (NMSettingConnection *) nm_connection_get_setting (b, NM_TYPE_SETTING_CONNECTION);
g_assert (s_con);
- g_object_set (s_con, NM_SETTING_CONNECTION_TIMESTAMP, (gulong) 0, NULL);
+ g_object_set (s_con, NM_SETTING_CONNECTION_TIMESTAMP, (guint64) 0, NULL);
g_assert (!nm_connection_compare (a, b, NM_SETTING_COMPARE_FLAG_EXACT));
g_object_unref (a);
@@ -1092,7 +1093,7 @@ test_connection_compare_key_only_in_b (void)
b = nm_connection_duplicate (a);
s_con = (NMSettingConnection *) nm_connection_get_setting (b, NM_TYPE_SETTING_CONNECTION);
g_assert (s_con);
- g_object_set (s_con, NM_SETTING_CONNECTION_TIMESTAMP, (gulong) 0, NULL);
+ g_object_set (s_con, NM_SETTING_CONNECTION_TIMESTAMP, (guint64) 0, NULL);
g_assert (!nm_connection_compare (a, b, NM_SETTING_COMPARE_FLAG_EXACT));
g_object_unref (a);
@@ -1321,6 +1322,65 @@ test_connection_diff_no_secrets (void)
}
static void
+test_connection_diff_inferrable (void)
+{
+ NMConnection *a, *b;
+ GHashTable *out_diffs = NULL;
+ gboolean same;
+ NMSettingConnection *s_con;
+ NMSettingWired *s_wired;
+ NMSettingIP4Config *s_ip4;
+ char *uuid;
+ const DiffSetting settings[] = {
+ { NM_SETTING_CONNECTION_SETTING_NAME, {
+ { NM_SETTING_CONNECTION_INTERFACE_NAME, NM_SETTING_DIFF_RESULT_IN_A },
+ { NULL, NM_SETTING_DIFF_RESULT_UNKNOWN },
+ } },
+ };
+
+ a = new_test_connection ();
+ b = nm_connection_duplicate (a);
+
+ /* Change the UUID, wired MTU, and set ignore-auto-dns */
+ s_con = nm_connection_get_setting_connection (a);
+ g_assert (s_con);
+ uuid = nm_utils_uuid_generate ();
+ g_object_set (G_OBJECT (s_con),
+ NM_SETTING_CONNECTION_UUID, uuid,
+ NM_SETTING_CONNECTION_ID, "really neat connection",
+ NULL);
+ g_free (uuid);
+
+ s_wired = nm_connection_get_setting_wired (a);
+ g_assert (s_wired);
+ g_object_set (G_OBJECT (s_wired), NM_SETTING_WIRED_MTU, 300, NULL);
+
+ s_ip4 = nm_connection_get_setting_ip4_config (a);
+ g_assert (s_ip4);
+ g_object_set (G_OBJECT (s_ip4), NM_SETTING_IP4_CONFIG_IGNORE_AUTO_DNS, TRUE, NULL);
+
+ /* Make sure the diff returns no results as secrets are ignored */
+ same = nm_connection_diff (a, b, NM_SETTING_COMPARE_FLAG_INFERRABLE, &out_diffs);
+ g_assert (same == TRUE);
+ g_assert (out_diffs == NULL);
+
+ /* And change a INFERRABLE property to ensure that it shows up in the diff results */
+ g_object_set (G_OBJECT (s_con), NM_SETTING_CONNECTION_INTERFACE_NAME, "usb0", NULL);
+
+ /* Make sure the diff returns no results as secrets are ignored */
+ same = nm_connection_diff (a, b, NM_SETTING_COMPARE_FLAG_INFERRABLE, &out_diffs);
+ g_assert (same == FALSE);
+ g_assert (out_diffs != NULL);
+ g_assert (g_hash_table_size (out_diffs) > 0);
+
+ ensure_diffs (out_diffs, settings, ARRAY_LEN (settings));
+
+ g_hash_table_destroy (out_diffs);
+ g_object_unref (a);
+ g_object_unref (b);
+}
+
+static void
add_generic_settings (NMConnection *connection, const char *ctype)
{
NMSetting *setting;
@@ -2150,6 +2210,7 @@ int main (int argc, char **argv)
test_connection_diff_same ();
test_connection_diff_different ();
test_connection_diff_no_secrets ();
+ test_connection_diff_inferrable ();
test_connection_good_base_types ();
test_connection_bad_base_types ();
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
index 4d42ad2d9d..f15d18aaed 100644
--- a/src/NetworkManagerUtils.c
+++ b/src/NetworkManagerUtils.c
@@ -659,3 +659,93 @@ nm_utils_read_resolv_conf_nameservers (const char *rc_contents)
return nameservers;
}
+static NMConnection *
+check_possible_match (NMConnection *orig,
+ NMConnection *candidate,
+ GHashTable *settings)
+{
+ GHashTable *props;
+ const char *orig_ip6_method, *candidate_ip6_method;
+ NMSettingIP6Config *candidate_ip6;
+
+ g_return_val_if_fail (settings != NULL, NULL);
+
+ 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 NULL;
+ }
+
+ /* 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
+ * 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_ip6_config_get_may_fail (candidate_ip6))) {
+ return candidate;
+ }
+
+ 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
+ * @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,
+ 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);
+ g_hash_table_unref (diffs);
+ continue;
+ }
+
+ /* Exact match */
+ return candidate;
+ }
+
+ /* Best match (if any) */
+ return best_match;
+}
+
diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h
index a84ba10741..06058a5612 100644
--- a/src/NetworkManagerUtils.h
+++ b/src/NetworkManagerUtils.h
@@ -31,6 +31,7 @@
#include "nm-ip6-config.h"
#include "nm-setting-ip6-config.h"
#include "nm-connection.h"
+#include "nm-setting-private.h"
gboolean nm_ethernet_address_is_valid (const struct ether_addr *test_addr);
@@ -86,4 +87,11 @@ char *nm_utils_new_vlan_name (const char *parent_iface, guint32 vlan_id);
GPtrArray *nm_utils_read_resolv_conf_nameservers (const char *rc_contents);
+typedef gboolean (NMUtilsMatchFilterFunc) (NMConnection *connection, gpointer user_data);
+
+NMConnection *nm_utils_match_connection (GSList *connections,
+ NMConnection *original,
+ NMUtilsMatchFilterFunc match_filter_func,
+ gpointer match_filter_data);
+
#endif /* NETWORK_MANAGER_UTILS_H */
diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c
index 640a1e1bfe..f7c3e07466 100644
--- a/src/nm-ip4-config.c
+++ b/src/nm-ip4-config.c
@@ -210,6 +210,22 @@ nm_ip4_config_capture (int ifindex, gboolean capture_resolv_conf)
}
}
+ /* If there is a host route to the gateway, ignore that route. It is
+ * automatically added by NetworkManager when needed.
+ */
+ if (has_gateway) {
+ for (i = 0; i < priv->routes->len; i++) {
+ const NMPlatformIP4Route *route = &g_array_index (priv->routes, NMPlatformIP4Route, i);
+
+ if ( (route->plen == 32)
+ && (route->network == priv->gateway)
+ && (route->gateway == 0)) {
+ g_array_remove_index (priv->routes, i);
+ i--;
+ }
+ }
+ }
+
/* If the interface has the default route, and has IPv4 addresses, capture
* nameservers from /etc/resolv.conf.
*/
diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c
index 6319dbf316..083f215810 100644
--- a/src/nm-ip6-config.c
+++ b/src/nm-ip6-config.c
@@ -212,6 +212,22 @@ nm_ip6_config_capture (int ifindex, gboolean capture_resolv_conf)
}
}
+ /* If there is a host route to the gateway, ignore that route. It is
+ * automatically added by NetworkManager when needed.
+ */
+ if (has_gateway) {
+ for (i = 0; i < priv->routes->len; i++) {
+ const NMPlatformIP6Route *route = &g_array_index (priv->routes, NMPlatformIP6Route, i);
+
+ if ( route->plen == 128
+ && IN6_ARE_ADDR_EQUAL (&route->network, &priv->gateway)
+ && IN6_IS_ADDR_UNSPECIFIED (&route->gateway)) {
+ g_array_remove_index (priv->routes, i);
+ i--;
+ }
+ }
+ }
+
/* If the interface has the default route, and has IPv4 addresses, capture
* nameservers from /etc/resolv.conf.
*/
diff --git a/src/nm-manager.c b/src/nm-manager.c
index 395bcdbcf3..875464eb8d 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -57,7 +57,6 @@
#include "nm-device-tun.h"
#include "nm-device-macvlan.h"
#include "nm-device-gre.h"
-#include "nm-setting-private.h"
#include "nm-setting-bluetooth.h"
#include "nm-setting-connection.h"
#include "nm-setting-wireless.h"
@@ -1713,6 +1712,12 @@ local_slist_free (void *loc)
g_slist_free (*location);
}
+static gboolean
+match_connection_filter (NMConnection *connection, gpointer user_data)
+{
+ return nm_device_check_connection_compatible (NM_DEVICE (user_data), connection, NULL);
+}
+
/**
* get_existing_connection:
* @manager: #NMManager instance
@@ -1726,9 +1731,8 @@ get_existing_connection (NMManager *manager, NMDevice *device)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
free_slist GSList *connections = nm_manager_get_activatable_connections (manager);
- NMConnection *connection = NULL;
+ NMConnection *connection = NULL, *matched;
NMSettingsConnection *added = NULL;
- GSList *iter;
GError *error = NULL;
nm_device_capture_initial_config (device);
@@ -1752,21 +1756,22 @@ get_existing_connection (NMManager *manager, NMDevice *device)
* When no configured connection matches the generated connection, we keep
* the generated connection instead.
*/
- for (iter = connections; iter; iter = iter->next) {
- NMConnection *candidate = NM_CONNECTION (iter->data);
-
- if (nm_connection_compare (connection, candidate, NM_SETTING_COMPARE_FLAG_CANDIDATE)) {
- nm_log_info (LOGD_DEVICE, "(%s): found matching connection '%s'",
- nm_device_get_iface (device),
- nm_connection_get_id (candidate));
- g_object_unref (connection);
- return candidate;
- }
+ connections = g_slist_sort (connections, nm_settings_sort_connections);
+ matched = nm_utils_match_connection (connections,
+ connection,
+ match_connection_filter,
+ device);
+ if (matched) {
+ nm_log_info (LOGD_DEVICE, "(%s): found matching connection '%s'",
+ nm_device_get_iface (device),
+ nm_connection_get_id (matched));
+ g_object_unref (connection);
+ return matched;
}
nm_log_dbg (LOGD_DEVICE, "(%s): generated connection '%s'",
- nm_device_get_iface (device),
- nm_connection_get_id (connection));
+ nm_device_get_iface (device),
+ nm_connection_get_id (connection));
added = nm_settings_add_connection (priv->settings, connection, FALSE, &error);
if (!added) {
diff --git a/src/settings/nm-settings.c b/src/settings/nm-settings.c
index 057dc45382..a940656eed 100644
--- a/src/settings/nm-settings.c
+++ b/src/settings/nm-settings.c
@@ -1660,21 +1660,20 @@ nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quittin
/***************************************************************/
-static gint
-best_connection_sort (gconstpointer a, gconstpointer b, gpointer user_data)
+/* GCompareFunc helper for sorting "best" connections */
+gint
+nm_settings_sort_connections (gconstpointer a, gconstpointer b)
{
NMSettingsConnection *ac = (NMSettingsConnection *) a;
NMSettingsConnection *bc = (NMSettingsConnection *) b;
guint64 ats = 0, bts = 0;
- if (!ac && bc)
+ if (ac == bc)
+ return 0;
+ if (!ac)
return -1;
- else if (ac && !bc)
+ if (!bc)
return 1;
- else if (!ac && !bc)
- return 0;
-
- g_assert (ac && bc);
/* In the future we may use connection priorities in addition to timestamps */
nm_settings_connection_get_timestamp (ac, &ats);
@@ -1722,7 +1721,7 @@ get_best_connections (NMConnectionProvider *provider,
}
/* List is sorted with oldest first */
- sorted = g_slist_insert_sorted_with_data (sorted, connection, best_connection_sort, NULL);
+ sorted = g_slist_insert_sorted (sorted, connection, nm_settings_sort_connections);
added++;
if (max_requested && added > max_requested) {
diff --git a/src/settings/nm-settings.h b/src/settings/nm-settings.h
index 345eb04d44..7e699c2820 100644
--- a/src/settings/nm-settings.h
+++ b/src/settings/nm-settings.h
@@ -122,4 +122,6 @@ void nm_settings_device_added (NMSettings *self, NMDevice *device);
void nm_settings_device_removed (NMSettings *self, NMDevice *device, gboolean quitting);
+gint nm_settings_sort_connections (gconstpointer a, gconstpointer b);
+
#endif /* __NM_SETTINGS_H__ */