core: merge branch 'th/device-route-metric-rh1505893'

https://bugzilla.redhat.com/show_bug.cgi?id=1505893
This commit is contained in:
Thomas Haller 2017-12-15 11:42:41 +01:00
commit b2273ce3dd
9 changed files with 650 additions and 105 deletions

View file

@ -241,7 +241,13 @@ GPtrArray *_nm_utils_copy_object_array (const GPtrArray *array);
gssize _nm_utils_ptrarray_find_first (gconstpointer *list, gssize len, gconstpointer needle);
gssize _nm_utils_ptrarray_find_binary_search (gconstpointer *list, gsize len, gconstpointer needle, GCompareDataFunc cmpfcn, gpointer user_data);
gssize _nm_utils_ptrarray_find_binary_search (gconstpointer *list,
gsize len,
gconstpointer needle,
GCompareDataFunc cmpfcn,
gpointer user_data,
gssize *out_idx_first,
gssize *out_idx_last);
gssize _nm_utils_array_find_binary_search (gconstpointer list, gsize elem_size, gsize len, gconstpointer needle, GCompareDataFunc cmpfcn, gpointer user_data);
GSList * _nm_utils_strv_to_slist (char **strv, gboolean deep_copy);

View file

@ -646,36 +646,82 @@ _nm_utils_ptrarray_find_first (gconstpointer *list, gssize len, gconstpointer ne
}
gssize
_nm_utils_ptrarray_find_binary_search (gconstpointer *list, gsize len, gconstpointer needle, GCompareDataFunc cmpfcn, gpointer user_data)
_nm_utils_ptrarray_find_binary_search (gconstpointer *list,
gsize len,
gconstpointer needle,
GCompareDataFunc cmpfcn,
gpointer user_data,
gssize *out_idx_first,
gssize *out_idx_last)
{
gssize imin, imax, imid;
gssize imin, imax, imid, i2min, i2max, i2mid;
int cmp;
g_return_val_if_fail (list || !len, ~((gssize) 0));
g_return_val_if_fail (cmpfcn, ~((gssize) 0));
imin = 0;
if (len == 0)
return ~imin;
if (len > 0) {
imax = len - 1;
imax = len - 1;
while (imin <= imax) {
imid = imin + (imax - imin) / 2;
while (imin <= imax) {
imid = imin + (imax - imin) / 2;
cmp = cmpfcn (list[imid], needle, user_data);
if (cmp == 0) {
/* we found a matching entry at index imid.
*
* Does the caller request the first/last index as well (in case that
* there are multiple entries which compare equal). */
cmp = cmpfcn (list[imid], needle, user_data);
if (cmp == 0)
return imid;
if (out_idx_first) {
i2min = imin;
i2max = imid + 1;
while (i2min <= i2max) {
i2mid = i2min + (i2max - i2min) / 2;
if (cmp < 0)
imin = imid + 1;
else
imax = imid - 1;
cmp = cmpfcn (list[i2mid], needle, user_data);
if (cmp == 0)
i2max = i2mid -1;
else {
nm_assert (cmp < 0);
i2min = i2mid + 1;
}
}
*out_idx_first = i2min;
}
if (out_idx_last) {
i2min = imid + 1;
i2max = imax;
while (i2min <= i2max) {
i2mid = i2min + (i2max - i2min) / 2;
cmp = cmpfcn (list[i2mid], needle, user_data);
if (cmp == 0)
i2min = i2mid + 1;
else {
nm_assert (cmp > 0);
i2max = i2mid - 1;
}
}
*out_idx_last = i2min - 1;
}
return imid;
}
if (cmp < 0)
imin = imid + 1;
else
imax = imid - 1;
}
}
/* return the inverse of @imin. This is a negative number, but
* also is ~imin the position where the value should be inserted. */
return ~imin;
imin = ~imin;
NM_SET_OUT (out_idx_first, imin);
NM_SET_OUT (out_idx_last, imin);
return imin;
}
gssize

View file

@ -6149,7 +6149,7 @@ static void
_test_find_binary_search_do (const int *array, gsize len)
{
gsize i;
gssize idx;
gssize idx, idx_first, idx_last;
gs_free gconstpointer *parray = g_new (gconstpointer, len);
const int NEEDLE = 0;
gconstpointer pneedle = GINT_TO_POINTER (NEEDLE);
@ -6160,10 +6160,10 @@ _test_find_binary_search_do (const int *array, gsize len)
expected_result = _nm_utils_ptrarray_find_first (parray, len, pneedle);
idx = _nm_utils_ptrarray_find_binary_search (parray, len, pneedle, _test_find_binary_search_cmp, NULL);
if (expected_result >= 0)
idx = _nm_utils_ptrarray_find_binary_search (parray, len, pneedle, _test_find_binary_search_cmp, NULL, &idx_first, &idx_last);
if (expected_result >= 0) {
g_assert_cmpint (expected_result, ==, idx);
else {
} else {
gssize idx2 = ~idx;
g_assert_cmpint (idx, <, 0);
@ -6172,6 +6172,8 @@ _test_find_binary_search_do (const int *array, gsize len)
g_assert (idx2 - 1 < 0 || _test_find_binary_search_cmp (parray[idx2 - 1], pneedle, NULL) < 0);
g_assert (idx2 >= len || _test_find_binary_search_cmp (parray[idx2], pneedle, NULL) > 0);
}
g_assert_cmpint (idx, ==, idx_first);
g_assert_cmpint (idx, ==, idx_last);
for (i = 0; i < len; i++) {
int cmp;
@ -6272,6 +6274,94 @@ test_nm_utils_ptrarray_find_binary_search (void)
test_find_binary_search_do (-3, -2, -1, 1, 2, 3, 4);
}
/*****************************************************************************/
#define BIN_SEARCH_W_DUPS_LEN 100
#define BIN_SEARCH_W_DUPS_JITTER 10
static int
_test_bin_search2_cmp (gconstpointer pa,
gconstpointer pb,
gpointer user_data)
{
int a = GPOINTER_TO_INT (pa);
int b = GPOINTER_TO_INT (pb);
g_assert (a >= 0 && a <= BIN_SEARCH_W_DUPS_LEN + BIN_SEARCH_W_DUPS_JITTER);
g_assert (b >= 0 && b <= BIN_SEARCH_W_DUPS_LEN + BIN_SEARCH_W_DUPS_JITTER);
NM_CMP_DIRECT (a, b);
return 0;
}
static int
_test_bin_search2_cmp_p (gconstpointer pa,
gconstpointer pb,
gpointer user_data)
{
return _test_bin_search2_cmp (*((gpointer *) pa), *((gpointer *) pb), NULL);
}
static void
test_nm_utils_ptrarray_find_binary_search_with_duplicates (void)
{
gssize idx, idx2, idx_first2, idx_first, idx_last;
int i_test, i_len, i;
gssize j;
gconstpointer arr[BIN_SEARCH_W_DUPS_LEN];
const int N_TEST = 10;
for (i_test = 0; i_test < N_TEST; i_test++) {
for (i_len = 0; i_len < BIN_SEARCH_W_DUPS_LEN; i_len++) {
/* fill with random numbers... surely there are some duplicates
* there... or maybe even there are none... */
for (i = 0; i < i_len; i++)
arr[i] = GINT_TO_POINTER (nmtst_get_rand_int () % (i_len + BIN_SEARCH_W_DUPS_JITTER));
g_qsort_with_data (arr,
i_len,
sizeof (gpointer),
_test_bin_search2_cmp_p,
NULL);
for (i = 0; i < i_len + BIN_SEARCH_W_DUPS_JITTER; i++) {
gconstpointer p = GINT_TO_POINTER (i);
idx = _nm_utils_ptrarray_find_binary_search (arr, i_len, p, _test_bin_search2_cmp, NULL, &idx_first, &idx_last);
idx_first2 = _nm_utils_ptrarray_find_first (arr, i_len, p);
idx2 = _nm_utils_array_find_binary_search (arr, sizeof (gpointer), i_len, &p, _test_bin_search2_cmp_p, NULL);
g_assert_cmpint (idx, ==, idx2);
if (idx_first2 < 0) {
g_assert_cmpint (idx, <, 0);
g_assert_cmpint (idx, ==, idx_first);
g_assert_cmpint (idx, ==, idx_last);
idx = ~idx;
g_assert_cmpint (idx, >=, 0);
g_assert_cmpint (idx, <=, i_len);
if (i_len == 0)
g_assert_cmpint (idx, ==, 0);
else {
g_assert (idx == i_len || GPOINTER_TO_INT (arr[idx]) > i);
g_assert (idx == 0 || GPOINTER_TO_INT (arr[idx - 1]) < i);
}
} else {
g_assert_cmpint (idx_first, ==, idx_first2);
g_assert_cmpint (idx_first, >=, 0);
g_assert_cmpint (idx_last, <, i_len);
g_assert_cmpint (idx_first, <=, idx_last);
g_assert_cmpint (idx, >=, idx_first);
g_assert_cmpint (idx, <=, idx_last);
for (j = idx_first; j < idx_last; j++)
g_assert (GPOINTER_TO_INT (arr[j]) == i);
g_assert (idx_first == 0 || GPOINTER_TO_INT (arr[idx_first - 1]) < i);
g_assert (idx_last == i_len - 1 || GPOINTER_TO_INT (arr[idx_last + 1]) > i);
}
}
}
}
}
/*****************************************************************************/
static void
test_nm_utils_enum_from_str_do (GType type, const char *str,
@ -6962,6 +7052,7 @@ int main (int argc, char **argv)
g_test_add_func ("/core/general/_glib_compat_g_ptr_array_insert", test_g_ptr_array_insert);
g_test_add_func ("/core/general/_glib_compat_g_hash_table_get_keys_as_array", test_g_hash_table_get_keys_as_array);
g_test_add_func ("/core/general/_nm_utils_ptrarray_find_binary_search", test_nm_utils_ptrarray_find_binary_search);
g_test_add_func ("/core/general/_nm_utils_ptrarray_find_binary_search_with_duplicates", test_nm_utils_ptrarray_find_binary_search_with_duplicates);
g_test_add_func ("/core/general/_nm_utils_strstrdictkey", test_nm_utils_strstrdictkey);
g_test_add_func ("/core/general/nm_ptrarray_len", test_nm_ptrarray_len);

View file

@ -1721,8 +1721,8 @@ nm_device_get_metered (NMDevice *self)
return NM_DEVICE_GET_PRIVATE (self)->metered;
}
static guint32
_get_route_metric_default (NMDevice *self)
guint32
nm_device_get_route_metric_default (NMDeviceType device_type)
{
/* Device 'priority' is used for the default route-metric and is based on
* the device type. The settings ipv4.route-metric and ipv6.route-metric
@ -1741,7 +1741,7 @@ _get_route_metric_default (NMDevice *self)
* metrics (except for IPv6, where 0 means 1024).
*/
switch (nm_device_get_device_type (self)) {
switch (device_type) {
/* 50 is reserved for VPN (NM_VPN_ROUTE_METRIC_DEFAULT) */
case NM_DEVICE_TYPE_ETHERNET:
case NM_DEVICE_TYPE_VETH:
@ -1870,7 +1870,10 @@ nm_device_get_route_metric (NMDevice *self,
if (route_metric >= 0)
goto out;
}
route_metric = _get_route_metric_default (self);
route_metric = nm_manager_device_route_metric_reserve (nm_manager_get (),
nm_device_get_ip_ifindex (self),
nm_device_get_device_type (self));
out:
return nm_utils_ip_route_metric_normalize (addr_family, route_metric);
}
@ -12609,6 +12612,11 @@ _cleanup_generic_pre (NMDevice *self, CleanupType cleanup_type)
_cancel_activation (self);
if (cleanup_type != CLEANUP_TYPE_KEEP) {
nm_manager_device_route_metric_clear (nm_manager_get (),
nm_device_get_ip_ifindex (self));
}
if ( cleanup_type == CLEANUP_TYPE_DECONFIGURE
&& priv->fw_state >= FIREWALL_STATE_INITIALIZED
&& priv->fw_mgr
@ -13639,6 +13647,7 @@ nm_device_update_permanent_hw_address (NMDevice *self, gboolean force_freeze)
gboolean success_read;
int ifindex;
const NMPlatformLink *pllink;
const NMConfigDeviceStateData *dev_state;
if (priv->hw_addr_perm) {
/* the permanent hardware address is only read once and not
@ -13698,23 +13707,19 @@ nm_device_update_permanent_hw_address (NMDevice *self, gboolean force_freeze)
/* We also persist our choice of the fake address to the device state
* file to use the same address on restart of NetworkManager.
* First, try to reload the address from the state file. */
{
gs_free NMConfigDeviceStateData *dev_state = NULL;
dev_state = nm_config_device_state_load (ifindex);
if ( dev_state
&& dev_state->perm_hw_addr_fake
&& nm_utils_hwaddr_aton (dev_state->perm_hw_addr_fake, buf, priv->hw_addr_len)
&& !nm_utils_hwaddr_matches (buf, priv->hw_addr_len, priv->hw_addr, -1)) {
_LOGD (LOGD_PLATFORM | LOGD_ETHER, "hw-addr: %s (use from statefile: %s, current: %s)",
success_read
? "read HW addr length of permanent MAC address differs"
: "unable to read permanent MAC address",
dev_state->perm_hw_addr_fake,
priv->hw_addr);
priv->hw_addr_perm = nm_utils_hwaddr_ntoa (buf, priv->hw_addr_len);
goto notify_and_out;
}
dev_state = nm_config_device_state_get (nm_config_get (), ifindex);
if ( dev_state
&& dev_state->perm_hw_addr_fake
&& nm_utils_hwaddr_aton (dev_state->perm_hw_addr_fake, buf, priv->hw_addr_len)
&& !nm_utils_hwaddr_matches (buf, priv->hw_addr_len, priv->hw_addr, -1)) {
_LOGD (LOGD_PLATFORM | LOGD_ETHER, "hw-addr: %s (use from statefile: %s, current: %s)",
success_read
? "read HW addr length of permanent MAC address differs"
: "unable to read permanent MAC address",
dev_state->perm_hw_addr_fake,
priv->hw_addr);
priv->hw_addr_perm = nm_utils_hwaddr_ntoa (buf, priv->hw_addr_len);
goto notify_and_out;
}
_LOGD (LOGD_PLATFORM | LOGD_ETHER, "hw-addr: %s (use current: %s)",

View file

@ -450,6 +450,8 @@ NMMetered nm_device_get_metered (NMDevice *dev);
guint32 nm_device_get_route_table (NMDevice *self, int addr_family, gboolean fallback_main);
guint32 nm_device_get_route_metric (NMDevice *dev, int addr_family);
guint32 nm_device_get_route_metric_default (NMDeviceType device_type);
const char * nm_device_get_hw_address (NMDevice *dev);
const char * nm_device_get_permanent_hw_address (NMDevice *self);
const char * nm_device_get_permanent_hw_address_full (NMDevice *self,

View file

@ -121,6 +121,14 @@ typedef struct {
* because the state changes only on explicit actions from the daemon
* itself. */
State *state;
/* the hash table of device states. It is only loaded from disk
* once and kept immutable afterwards.
*
* We also read all state file at once. We don't want to support
* that they are changed outside of NM (at least not while NM is running).
* Hence, we read them once, that's it. */
GHashTable *device_states;
} NMConfigPrivate;
struct _NMConfig {
@ -182,6 +190,33 @@ nm_config_keyfile_get_boolean (const GKeyFile *keyfile,
return nm_config_parse_boolean (str, default_value);
}
gint64
nm_config_keyfile_get_int64 (const GKeyFile *keyfile,
const char *section,
const char *key,
guint base,
gint64 min,
gint64 max,
gint64 fallback)
{
gint64 v;
int errsv;
char *str;
g_return_val_if_fail (keyfile, fallback);
g_return_val_if_fail (section, fallback);
g_return_val_if_fail (key, fallback);
str = g_key_file_get_value ((GKeyFile *) keyfile, section, key, NULL);
v = _nm_utils_ascii_str_to_int64 (str, base, min, max, fallback);
if (str) {
errsv = errno;
g_free (str);
errno = errsv;
}
return v;
}
char *
nm_config_keyfile_get_value (const GKeyFile *keyfile,
const char *section,
@ -1898,6 +1933,7 @@ _nm_config_state_set (NMConfig *self,
#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_PERM_HW_ADDR_FAKE "perm-hw-addr-fake"
#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_CONNECTION_UUID "connection-uuid"
#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NM_OWNED "nm-owned"
#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT "route-metric-default"
NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_device_state_managed_type_to_str, NMConfigDeviceStateManagedType,
NM_UTILS_LOOKUP_DEFAULT_NM_ASSERT ("unknown"),
@ -1917,47 +1953,54 @@ _config_device_state_data_new (int ifindex, GKeyFile *kf)
gsize perm_hw_addr_fake_len;
gint nm_owned = -1;
char *p;
guint32 route_metric_default;
nm_assert (kf);
nm_assert (ifindex > 0);
if (kf) {
switch (nm_config_keyfile_get_boolean (kf,
DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_MANAGED,
-1)) {
case TRUE:
managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED;
connection_uuid = nm_config_keyfile_get_value (kf,
DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_CONNECTION_UUID,
NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY);
break;
case FALSE:
managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNMANAGED;
break;
case -1:
/* missing property in keyfile. */
break;
}
perm_hw_addr_fake = nm_config_keyfile_get_value (kf,
DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_PERM_HW_ADDR_FAKE,
NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY);
if (perm_hw_addr_fake) {
char *normalized;
normalized = nm_utils_hwaddr_canonical (perm_hw_addr_fake, -1);
g_free (perm_hw_addr_fake);
perm_hw_addr_fake = normalized;
}
nm_owned = nm_config_keyfile_get_boolean (kf,
DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NM_OWNED,
-1);
switch (nm_config_keyfile_get_boolean (kf,
DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_MANAGED,
-1)) {
case TRUE:
managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_MANAGED;
connection_uuid = nm_config_keyfile_get_value (kf,
DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_CONNECTION_UUID,
NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY);
break;
case FALSE:
managed_type = NM_CONFIG_DEVICE_STATE_MANAGED_TYPE_UNMANAGED;
break;
case -1:
/* missing property in keyfile. */
break;
}
perm_hw_addr_fake = nm_config_keyfile_get_value (kf,
DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_PERM_HW_ADDR_FAKE,
NM_CONFIG_GET_VALUE_STRIP | NM_CONFIG_GET_VALUE_NO_EMPTY);
if (perm_hw_addr_fake) {
char *normalized;
normalized = nm_utils_hwaddr_canonical (perm_hw_addr_fake, -1);
g_free (perm_hw_addr_fake);
perm_hw_addr_fake = normalized;
}
nm_owned = nm_config_keyfile_get_boolean (kf,
DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NM_OWNED,
-1);
/* metric zero is not a valid metric. While zero valid for IPv4, for IPv6 it is an alias
* for 1024. Since we handle here IPv4 and IPv6 the same, we cannot allow zero. */
route_metric_default = nm_config_keyfile_get_int64 (kf,
DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT,
10, 1, G_MAXUINT32, 0);
connection_uuid_len = connection_uuid ? strlen (connection_uuid) + 1 : 0;
perm_hw_addr_fake_len = perm_hw_addr_fake ? strlen (perm_hw_addr_fake) + 1 : 0;
@ -1970,6 +2013,7 @@ _config_device_state_data_new (int ifindex, GKeyFile *kf)
device_state->connection_uuid = NULL;
device_state->perm_hw_addr_fake = NULL;
device_state->nm_owned = nm_owned;
device_state->route_metric_default = route_metric_default;
p = (char *) (&device_state[1]);
if (connection_uuid) {
@ -2007,31 +2051,75 @@ nm_config_device_state_load (int ifindex)
kf = nm_config_create_keyfile ();
if (!g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, NULL))
g_clear_pointer (&kf, g_key_file_unref);
return NULL;
device_state = _config_device_state_data_new (ifindex, kf);
nm_owned_str = device_state->nm_owned == TRUE ?
", nm-owned=1" :
(device_state->nm_owned == FALSE ? ", nm-owned=0" : "");
_LOGT ("device-state: %s #%d (%s); managed=%s%s%s%s%s%s%s%s",
_LOGT ("device-state: %s #%d (%s); managed=%s%s%s%s%s%s%s%s, route-metric-default=%"G_GUINT32_FORMAT,
kf ? "read" : "miss",
ifindex, path,
_device_state_managed_type_to_str (device_state->managed),
NM_PRINT_FMT_QUOTED (device_state->connection_uuid, ", connection-uuid=", device_state->connection_uuid, "", ""),
NM_PRINT_FMT_QUOTED (device_state->perm_hw_addr_fake, ", perm-hw-addr-fake=", device_state->perm_hw_addr_fake, "", ""),
nm_owned_str);
nm_owned_str,
device_state->route_metric_default);
return device_state;
}
static int
_device_state_parse_filename (const char *filename)
{
if (!filename || !filename[0])
return 0;
if (!NM_STRCHAR_ALL (filename, ch, g_ascii_isdigit (ch)))
return 0;
return _nm_utils_ascii_str_to_int64 (filename, 10, 1, G_MAXINT, 0);
}
GHashTable *
nm_config_device_state_load_all (void)
{
GHashTable *states;
GDir *dir;
const char *fn;
int ifindex;
states = g_hash_table_new_full (nm_direct_hash, NULL, NULL, g_free);
dir = g_dir_open (NM_CONFIG_DEVICE_STATE_DIR, 0, NULL);
if (!dir)
return states;
while ((fn = g_dir_read_name (dir))) {
NMConfigDeviceStateData *state;
ifindex = _device_state_parse_filename (fn);
if (ifindex <= 0)
continue;
state = nm_config_device_state_load (ifindex);
if (!state)
continue;
if (!nm_g_hash_table_insert (states, GINT_TO_POINTER (ifindex), state))
nm_assert_not_reached ();
}
g_dir_close (dir);
return states;
}
gboolean
nm_config_device_state_write (int ifindex,
NMConfigDeviceStateManagedType managed,
const char *perm_hw_addr_fake,
const char *connection_uuid,
gint nm_owned)
gint nm_owned,
guint32 route_metric_default)
{
char path[NM_STRLEN (NM_CONFIG_DEVICE_STATE_DIR) + 60];
GError *local = NULL;
@ -2073,17 +2161,24 @@ nm_config_device_state_write (int ifindex,
nm_owned);
}
if (route_metric_default != 0) {
g_key_file_set_int64 (kf,
DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE,
DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT,
route_metric_default);
}
if (!g_key_file_save_to_file (kf, path, &local)) {
_LOGW ("device-state: write #%d (%s) failed: %s", ifindex, path, local->message);
g_error_free (local);
return FALSE;
}
_LOGT ("device-state: write #%d (%s); managed=%s%s%s%s%s%s%s",
_LOGT ("device-state: write #%d (%s); managed=%s%s%s%s%s%s%s, route-metric-default=%"G_GUINT32_FORMAT,
ifindex, path,
_device_state_managed_type_to_str (managed),
NM_PRINT_FMT_QUOTED (connection_uuid, ", connection-uuid=", connection_uuid, "", ""),
NM_PRINT_FMT_QUOTED (perm_hw_addr_fake, ", perm-hw-addr-fake=", perm_hw_addr_fake, "", ""));
NM_PRINT_FMT_QUOTED (perm_hw_addr_fake, ", perm-hw-addr-fake=", perm_hw_addr_fake, "", ""),
route_metric_default);
return TRUE;
}
@ -2094,7 +2189,6 @@ nm_config_device_state_prune_unseen (GHashTable *seen_ifindexes)
const char *fn;
int ifindex;
gsize fn_len;
gsize i;
char buf[NM_STRLEN (NM_CONFIG_DEVICE_STATE_DIR"/") + 30 + 3] = NM_CONFIG_DEVICE_STATE_DIR"/";
char *buf_p = &buf[NM_STRLEN (NM_CONFIG_DEVICE_STATE_DIR"/")];
@ -2105,24 +2199,20 @@ nm_config_device_state_prune_unseen (GHashTable *seen_ifindexes)
return;
while ((fn = g_dir_read_name (dir))) {
fn_len = strlen (fn);
/* skip over file names that are not plain integers. */
for (i = 0; i < fn_len; i++) {
if (!g_ascii_isdigit (fn[i]))
break;
}
if (fn_len == 0 || i != fn_len)
ifindex = _device_state_parse_filename (fn);
if (ifindex <= 0)
continue;
ifindex = _nm_utils_ascii_str_to_int64 (fn, 10, 1, G_MAXINT, 0);
if (!ifindex)
continue;
if (g_hash_table_contains (seen_ifindexes, GINT_TO_POINTER (ifindex)))
continue;
memcpy (buf_p, fn, fn_len + 1);
fn_len = strlen (fn) + 1;
nm_assert (&buf_p[fn_len] < &buf[G_N_ELEMENTS (buf)]);
memcpy (buf_p, fn, fn_len);
nm_assert (({
char bb[30];
nm_sprintf_buf (bb, "%d", ifindex);
nm_streq0 (bb, buf_p);
}));
_LOGT ("device-state: prune #%d (%s)", ifindex, buf);
(void) unlink (buf);
}
@ -2132,6 +2222,46 @@ nm_config_device_state_prune_unseen (GHashTable *seen_ifindexes)
/*****************************************************************************/
static GHashTable *
_device_state_get_all (NMConfig *self)
{
NMConfigPrivate *priv = NM_CONFIG_GET_PRIVATE (self);
if (G_UNLIKELY (!priv->device_states))
priv->device_states = nm_config_device_state_load_all ();
return priv->device_states;
}
/**
* nm_config_device_state_get_all:
* @self: the #NMConfig
*
* This function exists to give convenient access to all
* device states. Do not ever try to modify the returned
* hash, it's supposed to be immutable.
*
* Returns: the internal #GHashTable object with all device states.
*/
const GHashTable *
nm_config_device_state_get_all (NMConfig *self)
{
g_return_val_if_fail (NM_IS_CONFIG (self), NULL);
return _device_state_get_all (self);
}
const NMConfigDeviceStateData *
nm_config_device_state_get (NMConfig *self,
int ifindex)
{
g_return_val_if_fail (NM_IS_CONFIG (self), NULL);
g_return_val_if_fail (ifindex > 0 , NULL);
return g_hash_table_lookup (_device_state_get_all (self), GINT_TO_POINTER (ifindex));
}
/*****************************************************************************/
void
nm_config_reload (NMConfig *self, NMConfigChangeFlags reload_flags)
{

View file

@ -166,6 +166,13 @@ gint nm_config_keyfile_get_boolean (const GKeyFile *keyfile,
const char *section,
const char *key,
gint default_value);
gint64 nm_config_keyfile_get_int64 (const GKeyFile *keyfile,
const char *section,
const char *key,
guint base,
gint64 min,
gint64 max,
gint64 fallback);
char *nm_config_keyfile_get_value (const GKeyFile *keyfile,
const char *section,
const char *key,
@ -206,6 +213,9 @@ struct _NMConfigDeviceStateData {
int ifindex;
NMConfigDeviceStateManagedType managed;
/* a value of zero means that no metric is set. */
guint32 route_metric_default;
/* the UUID of the last settings-connection active
* on the device. */
const char *connection_uuid;
@ -214,17 +224,24 @@ struct _NMConfigDeviceStateData {
/* whether the device was nm-owned (0/1) or -1 for
* non-software devices. */
gint nm_owned;
int nm_owned:3;
};
NMConfigDeviceStateData *nm_config_device_state_load (int ifindex);
GHashTable *nm_config_device_state_load_all (void);
gboolean nm_config_device_state_write (int ifindex,
NMConfigDeviceStateManagedType managed,
const char *perm_hw_addr_fake,
const char *connection_uuid,
gint nm_owned);
gint nm_owned,
guint32 route_metric_default);
void nm_config_device_state_prune_unseen (GHashTable *seen_ifindexes);
const GHashTable *nm_config_device_state_get_all (NMConfig *self);
const NMConfigDeviceStateData *nm_config_device_state_get (NMConfig *self,
int ifindex);
/*****************************************************************************/
#endif /* __NETWORKMANAGER_CONFIG_H__ */

View file

@ -161,6 +161,8 @@ typedef struct {
NMAuthManager *auth_mgr;
GHashTable *device_route_metrics;
GSList *auth_chains;
GHashTable *sleep_devices;
@ -325,6 +327,237 @@ static NM_CACHED_QUARK_FCN ("autoconnect-root", autoconnect_root_quark)
/*****************************************************************************/
typedef struct {
int ifindex;
guint32 aspired_metric;
guint32 effective_metric;
} DeviceRouteMetricData;
static DeviceRouteMetricData *
_device_route_metric_data_new (int ifindex, guint32 metric)
{
DeviceRouteMetricData *data;
nm_assert (ifindex > 0);
/* For IPv4, metrics can use the entire uint32 bit range. For IPv6,
* zero is treated like 1024. Since we handle IPv4 and IPv6 identically,
* we cannot allow a zero metric here.
*/
nm_assert (metric > 0);
data = g_slice_new0 (DeviceRouteMetricData);
data->ifindex = ifindex;
data->aspired_metric = metric;
data->effective_metric = metric;
return data;
}
static guint
_device_route_metric_data_by_ifindex_hash (gconstpointer p)
{
const DeviceRouteMetricData *data = p;
NMHashState h;
nm_hash_init (&h, 1030338191);
nm_hash_update_vals (&h, data->ifindex);
return nm_hash_complete (&h);
}
static gboolean
_device_route_metric_data_by_ifindex_equal (gconstpointer pa, gconstpointer pb)
{
const DeviceRouteMetricData *a = pa;
const DeviceRouteMetricData *b = pb;
return a->ifindex == b->ifindex;
}
static guint32
_device_route_metric_get (NMManager *self,
int ifindex,
NMDeviceType device_type,
gboolean lookup_only)
{
NMManagerPrivate *priv;
const DeviceRouteMetricData *d2;
DeviceRouteMetricData *data;
DeviceRouteMetricData data_lookup;
const NMDedupMultiHeadEntry *all_links_head;
NMPObject links_needle;
guint n_links;
gboolean cleaned = FALSE;
GHashTableIter h_iter;
g_return_val_if_fail (NM_IS_MANAGER (self), 0);
if (ifindex <= 0) {
if (lookup_only)
return 0;
return nm_device_get_route_metric_default (device_type);
}
priv = NM_MANAGER_GET_PRIVATE (self);
if ( lookup_only
&& !priv->device_route_metrics)
return 0;
if (G_UNLIKELY (!priv->device_route_metrics)) {
const GHashTable *h;
const NMConfigDeviceStateData *device_state;
priv->device_route_metrics = g_hash_table_new_full (_device_route_metric_data_by_ifindex_hash,
_device_route_metric_data_by_ifindex_equal,
NULL,
nm_g_slice_free_fcn (DeviceRouteMetricData));
cleaned = TRUE;
/* we need to pre-populate the cache for all (still existing) devices from the state-file */
h = nm_config_device_state_get_all (priv->config);
if (!h)
goto initited;
g_hash_table_iter_init (&h_iter, (GHashTable *) h);
while (g_hash_table_iter_next (&h_iter, NULL, (gpointer *) &device_state)) {
if (!device_state->route_metric_default)
continue;
if (!nm_platform_link_get (priv->platform, device_state->ifindex)) {
/* we have the entry in the state file, but (currently) no such
* ifindex exists in platform. Most likely the entry is obsolete,
* hence we skip it. */
continue;
}
if (!nm_g_hash_table_add (priv->device_route_metrics,
_device_route_metric_data_new (device_state->ifindex,
device_state->route_metric_default)))
nm_assert_not_reached ();
}
}
initited:
data_lookup.ifindex = ifindex;
data = g_hash_table_lookup (priv->device_route_metrics, &data_lookup);
if (data)
return data->effective_metric;
if (lookup_only)
return 0;
if (!cleaned) {
/* get the number of all links in the platform cache. */
all_links_head = nm_platform_lookup_all (priv->platform,
NMP_CACHE_ID_TYPE_OBJECT_TYPE,
nmp_object_stackinit_id_link (&links_needle, 1));
n_links = all_links_head ? all_links_head->len : 0;
/* on systems where a lot of devices are created and go away, the index contains
* a lot of stale entries. We must from time to time clean them up.
*
* Do do this cleanup, whenever we have more enties then 2 times the number of links. */
if (G_UNLIKELY (g_hash_table_size (priv->device_route_metrics) > NM_MAX (20, n_links * 2))) {
/* from time to time, we need to do some house-keeping and prune stale entries.
* Otherwise, on a system where interfaces frequently come and go (docker), we
* keep growing this cache for ifindexes that no longer exist. */
g_hash_table_iter_init (&h_iter, priv->device_route_metrics);
while (g_hash_table_iter_next (&h_iter, NULL, (gpointer *) &d2)) {
if (!nm_platform_link_get (priv->platform, d2->ifindex))
g_hash_table_iter_remove (&h_iter);
}
cleaned = TRUE;
}
}
data = _device_route_metric_data_new (ifindex, nm_device_get_route_metric_default (device_type));
/* unfortunately, there is no stright forward way to lookup all reserved metrics.
* Note, that we don't only have to know which metrics are currently reserved,
* but also, which metrics are now seemingly un-used but caused another reserved
* metric to be bumped. Hence, the naive O(n^2) search :( */
again:
g_hash_table_iter_init (&h_iter, priv->device_route_metrics);
while (g_hash_table_iter_next (&h_iter, NULL, (gpointer *) &d2)) {
if ( data->effective_metric < d2->aspired_metric
|| data->effective_metric > d2->effective_metric) {
/* no overlap. Skip. */
continue;
}
if ( !cleaned
&& !nm_platform_link_get (priv->platform, d2->ifindex)) {
/* the metric seems taken, but there is no such interface. This entry
* is stale, forget about it. */
g_hash_table_iter_remove (&h_iter);
continue;
}
data->effective_metric = d2->effective_metric;
if (data->effective_metric == G_MAXUINT32) {
/* we cannot bump any further. Done. */
break;
}
if (data->effective_metric - data->aspired_metric > 50) {
/* as one active interface reserves an entire range of metrics
* (from aspired_metric to effective_metric), that means if you
* alternatingly activate two interfaces, their metric will
* juggle up.
*
* Limit this, don't bump the metric more then 50 times. */
break;
}
/* bump the metric, and search again. */
data->effective_metric++;
goto again;
}
_LOGT (LOGD_DEVICE, "default-route-metric: ifindex %d reserves metric %u (aspired %u)",
data->ifindex, data->effective_metric, data->aspired_metric);
if (!nm_g_hash_table_add (priv->device_route_metrics, data))
nm_assert_not_reached ();
return data->effective_metric;
}
guint32
nm_manager_device_route_metric_reserve (NMManager *self,
int ifindex,
NMDeviceType device_type)
{
guint32 metric;
metric = _device_route_metric_get (self, ifindex, device_type, FALSE);
nm_assert (metric != 0);
return metric;
}
guint32
nm_manager_device_route_metric_get (NMManager *self,
int ifindex)
{
return _device_route_metric_get (self, ifindex, NM_DEVICE_TYPE_UNKNOWN, TRUE);
}
void
nm_manager_device_route_metric_clear (NMManager *self,
int ifindex)
{
NMManagerPrivate *priv;
DeviceRouteMetricData data_lookup;
priv = NM_MANAGER_GET_PRIVATE (self);
if (!priv->device_route_metrics)
return;
data_lookup.ifindex = ifindex;
if (g_hash_table_remove (priv->device_route_metrics, &data_lookup)) {
_LOGT (LOGD_DEVICE, "default-route-metric: ifindex %d released",
ifindex);
}
}
/*****************************************************************************/
static void
_delete_volatile_connection_do (NMManager *self,
NMSettingsConnection *connection)
@ -2641,10 +2874,9 @@ platform_query_devices (NMManager *self)
return;
for (i = 0; i < links->len; i++) {
const NMPlatformLink *link = NMP_OBJECT_CAST_LINK (links->pdata[i]);
gs_free NMConfigDeviceStateData *dev_state = NULL;
dev_state = nm_config_device_state_load (link->ifindex);
const NMConfigDeviceStateData *dev_state;
dev_state = nm_config_device_state_get (priv->config, link->ifindex);
platform_link_added (self,
link->ifindex,
link,
@ -5200,6 +5432,7 @@ nm_manager_write_device_state (NMManager *self)
const char *uuid = NULL;
const char *perm_hw_addr_fake = NULL;
gboolean perm_hw_addr_is_fake;
guint32 route_metric_default;
ifindex = nm_device_get_ip_ifindex (device);
if (ifindex <= 0)
@ -5229,11 +5462,14 @@ nm_manager_write_device_state (NMManager *self)
nm_owned = nm_device_is_software (device) ? nm_device_is_nm_owned (device) : -1;
route_metric_default = nm_manager_device_route_metric_get (self, ifindex);
if (nm_config_device_state_write (ifindex,
managed_type,
perm_hw_addr_fake,
uuid,
nm_owned))
nm_owned,
route_metric_default))
g_hash_table_add (seen_ifindexes, GINT_TO_POINTER (ifindex));
}
@ -6612,6 +6848,8 @@ dispose (GObject *object)
nm_clear_g_source (&priv->timestamp_update_id);
g_clear_pointer (&priv->device_route_metrics, g_hash_table_destroy);
G_OBJECT_CLASS (nm_manager_parent_class)->dispose (object);
}

View file

@ -115,6 +115,16 @@ NMDevice * nm_manager_get_device_by_ifindex (NMManager *manager,
NMDevice * nm_manager_get_device_by_path (NMManager *manager,
const char *path);
guint32 nm_manager_device_route_metric_reserve (NMManager *self,
int ifindex,
NMDeviceType device_type);
guint32 nm_manager_device_route_metric_get (NMManager *self,
int ifindex);
void nm_manager_device_route_metric_clear (NMManager *self,
int ifindex);
char * nm_manager_get_connection_iface (NMManager *self,
NMConnection *connection,
NMDevice **out_parent,