2019-09-10 11:19:01 +02:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2019-09-25 13:13:40 +02:00
|
|
|
/*
|
2019-10-01 09:20:35 +02:00
|
|
|
* Copyright (C) 2004 - 2018 Red Hat, Inc.
|
|
|
|
|
* Copyright (C) 2005 - 2008 Novell, Inc.
|
2016-03-01 09:56:51 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "nm-default.h"
|
|
|
|
|
|
|
|
|
|
#include "nm-core-utils.h"
|
|
|
|
|
|
|
|
|
|
#include <fcntl.h>
|
2018-08-09 09:29:51 +02:00
|
|
|
#include <fnmatch.h>
|
2016-03-01 09:56:51 +01:00
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <resolv.h>
|
2018-10-30 08:37:48 +01:00
|
|
|
#include <byteswap.h>
|
2016-03-01 09:56:51 +01:00
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <linux/if.h>
|
|
|
|
|
#include <linux/if_infiniband.h>
|
2018-12-14 08:45:09 +01:00
|
|
|
#include <net/if_arp.h>
|
2016-03-01 09:56:51 +01:00
|
|
|
#include <net/ethernet.h>
|
|
|
|
|
|
2019-04-14 13:36:32 +02:00
|
|
|
#include "nm-std-aux/unaligned.h"
|
2019-04-15 08:16:00 +02:00
|
|
|
#include "nm-glib-aux/nm-random-utils.h"
|
|
|
|
|
#include "nm-glib-aux/nm-io-utils.h"
|
|
|
|
|
#include "nm-glib-aux/nm-secret-utils.h"
|
2019-07-23 11:52:19 +02:00
|
|
|
#include "nm-glib-aux/nm-time-utils.h"
|
2016-03-01 09:56:51 +01:00
|
|
|
#include "nm-utils.h"
|
|
|
|
|
#include "nm-core-internal.h"
|
|
|
|
|
#include "nm-setting-connection.h"
|
|
|
|
|
#include "nm-setting-ip4-config.h"
|
|
|
|
|
#include "nm-setting-ip6-config.h"
|
|
|
|
|
#include "nm-setting-wireless.h"
|
|
|
|
|
#include "nm-setting-wireless-security.h"
|
|
|
|
|
|
2018-10-19 10:21:53 +02:00
|
|
|
#ifdef __NM_SD_UTILS_H__
|
|
|
|
|
#error "nm-core-utils.c should stay independent of systemd utils. Are you looking for NetworkMangerUtils.c? "
|
|
|
|
|
#endif
|
|
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
G_STATIC_ASSERT (sizeof (NMUtilsTestFlags) <= sizeof (int));
|
2018-11-13 19:01:54 +01:00
|
|
|
|
|
|
|
|
/* we read _nm_utils_testing without memory barrier. This is thread-safe,
|
|
|
|
|
* because the static variable is initialized to zero, and only reset
|
|
|
|
|
* once to a non-zero value (via g_atomic_int_compare_and_exchange()).
|
|
|
|
|
*
|
|
|
|
|
* Since there is only one integer that contains the data, there is no
|
|
|
|
|
* caching problem reading this (atomic int) variable without
|
|
|
|
|
* synchronization/memory-barrier. Contrary to a double-checked locking,
|
|
|
|
|
* where one needs a memory barrier to read the variable and ensure
|
|
|
|
|
* that also the related data is coherent in cache. Here there is no
|
|
|
|
|
* related data. */
|
2016-06-02 10:57:23 +02:00
|
|
|
static int _nm_utils_testing = 0;
|
2016-03-01 09:56:51 +01:00
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
|
nm_utils_get_testing_initialized ()
|
|
|
|
|
{
|
|
|
|
|
NMUtilsTestFlags flags;
|
|
|
|
|
|
|
|
|
|
flags = (NMUtilsTestFlags) _nm_utils_testing;
|
|
|
|
|
if (flags == NM_UTILS_TEST_NONE)
|
|
|
|
|
flags = (NMUtilsTestFlags) g_atomic_int_get (&_nm_utils_testing);
|
|
|
|
|
return flags != NM_UTILS_TEST_NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NMUtilsTestFlags
|
|
|
|
|
nm_utils_get_testing ()
|
|
|
|
|
{
|
|
|
|
|
NMUtilsTestFlags flags;
|
|
|
|
|
|
2018-11-13 19:01:54 +01:00
|
|
|
again:
|
2016-03-01 09:56:51 +01:00
|
|
|
flags = (NMUtilsTestFlags) _nm_utils_testing;
|
|
|
|
|
if (flags != NM_UTILS_TEST_NONE) {
|
|
|
|
|
/* Flags already initialized. Return them. */
|
|
|
|
|
return flags & NM_UTILS_TEST_ALL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Accessing nm_utils_get_testing() causes us to set the flags to initialized.
|
|
|
|
|
* Detecting running tests also based on g_test_initialized(). */
|
|
|
|
|
flags = _NM_UTILS_TEST_INITIALIZED;
|
|
|
|
|
if (g_test_initialized ())
|
|
|
|
|
flags |= _NM_UTILS_TEST_GENERAL;
|
|
|
|
|
|
2018-11-13 19:01:54 +01:00
|
|
|
g_atomic_int_compare_and_exchange (&_nm_utils_testing, 0, (int) flags);
|
|
|
|
|
|
|
|
|
|
/* regardless of whether we won the race of initializing _nm_utils_testing,
|
|
|
|
|
* go back and read the value again. It must be non-zero by now. */
|
|
|
|
|
goto again;
|
2016-03-01 09:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
_nm_utils_set_testing (NMUtilsTestFlags flags)
|
|
|
|
|
{
|
|
|
|
|
g_assert (!NM_FLAGS_ANY (flags, ~NM_UTILS_TEST_ALL));
|
|
|
|
|
|
|
|
|
|
/* mask out everything except ALL, and always set GENERAL. */
|
|
|
|
|
flags = (flags & NM_UTILS_TEST_ALL) | (_NM_UTILS_TEST_GENERAL | _NM_UTILS_TEST_INITIALIZED);
|
|
|
|
|
|
|
|
|
|
if (!g_atomic_int_compare_and_exchange (&_nm_utils_testing, 0, (int) flags)) {
|
|
|
|
|
/* We only allow setting _nm_utils_set_testing() once, before fetching the
|
|
|
|
|
* value with nm_utils_get_testing(). */
|
|
|
|
|
g_return_if_reached ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2017-04-27 12:35:21 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
static GSList *_singletons = NULL;
|
|
|
|
|
static gboolean _singletons_shutdown = FALSE;
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_nm_singleton_instance_weak_cb (gpointer data,
|
|
|
|
|
GObject *where_the_object_was)
|
|
|
|
|
{
|
2019-06-19 20:29:17 +02:00
|
|
|
nm_assert (g_slist_find (_singletons, where_the_object_was));
|
|
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
_singletons = g_slist_remove (_singletons, where_the_object_was);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void __attribute__((destructor))
|
|
|
|
|
_nm_singleton_instance_destroy (void)
|
|
|
|
|
{
|
|
|
|
|
_singletons_shutdown = TRUE;
|
|
|
|
|
|
|
|
|
|
while (_singletons) {
|
|
|
|
|
GObject *instance = _singletons->data;
|
|
|
|
|
|
|
|
|
|
_singletons = g_slist_delete_link (_singletons, _singletons);
|
|
|
|
|
|
|
|
|
|
g_object_weak_unref (instance, _nm_singleton_instance_weak_cb, NULL);
|
|
|
|
|
|
2019-05-05 15:40:04 +02:00
|
|
|
if (instance->ref_count > 1) {
|
|
|
|
|
nm_log_dbg (LOGD_CORE, "disown %s singleton ("NM_HASH_OBFUSCATE_PTR_FMT")",
|
|
|
|
|
G_OBJECT_TYPE_NAME (instance), NM_HASH_OBFUSCATE_PTR (instance));
|
|
|
|
|
}
|
2016-03-01 09:56:51 +01:00
|
|
|
|
|
|
|
|
g_object_unref (instance);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
_nm_singleton_instance_register_destruction (GObject *instance)
|
|
|
|
|
{
|
|
|
|
|
g_return_if_fail (G_IS_OBJECT (instance));
|
|
|
|
|
|
|
|
|
|
/* Don't allow registration after shutdown. We only destroy the singletons
|
|
|
|
|
* once. */
|
|
|
|
|
g_return_if_fail (!_singletons_shutdown);
|
|
|
|
|
|
|
|
|
|
g_object_weak_ref (instance, _nm_singleton_instance_weak_cb, NULL);
|
|
|
|
|
|
|
|
|
|
_singletons = g_slist_prepend (_singletons, instance);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2017-03-23 16:01:07 +01:00
|
|
|
static double
|
2017-03-24 11:27:41 +01:00
|
|
|
_exp10 (guint16 ex)
|
2017-03-23 16:01:07 +01:00
|
|
|
{
|
|
|
|
|
double v;
|
|
|
|
|
|
|
|
|
|
if (ex == 0)
|
|
|
|
|
return 1.0;
|
|
|
|
|
|
2017-03-24 11:27:41 +01:00
|
|
|
v = _exp10 (ex / 2);
|
2017-03-23 16:01:07 +01:00
|
|
|
v = v * v;
|
|
|
|
|
if (ex % 2)
|
|
|
|
|
v *= 10;
|
|
|
|
|
return v;
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-28 13:33:27 +02:00
|
|
|
/*
|
2017-03-24 11:27:41 +01:00
|
|
|
* nm_utils_exp10:
|
2017-03-23 16:01:07 +01:00
|
|
|
* @ex: the exponent
|
|
|
|
|
*
|
2017-03-24 11:27:41 +01:00
|
|
|
* Returns: 10^ex, or pow(10, ex), or exp10(ex).
|
2017-03-23 16:01:07 +01:00
|
|
|
*/
|
|
|
|
|
double
|
2017-03-24 11:27:41 +01:00
|
|
|
nm_utils_exp10 (gint16 ex)
|
2017-03-23 16:01:07 +01:00
|
|
|
{
|
|
|
|
|
if (ex >= 0)
|
2017-03-24 11:27:41 +01:00
|
|
|
return _exp10 (ex);
|
|
|
|
|
return 1.0 / _exp10 (- ((gint32) ex));
|
2017-03-23 16:01:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2017-06-12 18:39:53 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
/*
|
|
|
|
|
* nm_ethernet_address_is_valid:
|
|
|
|
|
* @addr: pointer to a binary or ASCII Ethernet address
|
|
|
|
|
* @len: length of @addr, or -1 if @addr is ASCII
|
|
|
|
|
*
|
|
|
|
|
* Compares an Ethernet address against known invalid addresses.
|
|
|
|
|
|
|
|
|
|
* Returns: %TRUE if @addr is a valid Ethernet address, %FALSE if it is not.
|
|
|
|
|
*/
|
|
|
|
|
gboolean
|
|
|
|
|
nm_ethernet_address_is_valid (gconstpointer addr, gssize len)
|
|
|
|
|
{
|
|
|
|
|
guint8 invalid_addr[4][ETH_ALEN] = {
|
|
|
|
|
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
|
|
|
|
|
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
|
|
|
|
{0x44, 0x44, 0x44, 0x44, 0x44, 0x44},
|
|
|
|
|
{0x00, 0x30, 0xb4, 0x00, 0x00, 0x00}, /* prism54 dummy MAC */
|
|
|
|
|
};
|
|
|
|
|
guint8 addr_bin[ETH_ALEN];
|
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
|
|
if (!addr) {
|
|
|
|
|
g_return_val_if_fail (len == -1 || len == ETH_ALEN, FALSE);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (len == -1) {
|
|
|
|
|
if (!nm_utils_hwaddr_aton (addr, addr_bin, ETH_ALEN))
|
|
|
|
|
return FALSE;
|
|
|
|
|
addr = addr_bin;
|
|
|
|
|
} else if (len != ETH_ALEN)
|
|
|
|
|
g_return_val_if_reached (FALSE);
|
|
|
|
|
|
|
|
|
|
/* Check for multicast address */
|
|
|
|
|
if ((((guint8 *) addr)[0]) & 0x01)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (invalid_addr); i++) {
|
|
|
|
|
if (nm_utils_hwaddr_matches (addr, ETH_ALEN, invalid_addr[i], ETH_ALEN))
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-06 17:33:19 +02:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
void
|
|
|
|
|
nm_utils_array_remove_at_indexes (GArray *array, const guint *indexes_to_delete, gsize len)
|
|
|
|
|
{
|
|
|
|
|
gsize elt_size;
|
|
|
|
|
guint index_to_delete;
|
|
|
|
|
guint i_src;
|
|
|
|
|
guint mm_src, mm_dst, mm_len;
|
|
|
|
|
gsize i_itd;
|
|
|
|
|
guint res_length;
|
|
|
|
|
|
|
|
|
|
g_return_if_fail (array);
|
|
|
|
|
if (!len)
|
|
|
|
|
return;
|
|
|
|
|
g_return_if_fail (indexes_to_delete);
|
|
|
|
|
|
|
|
|
|
elt_size = g_array_get_element_size (array);
|
|
|
|
|
|
|
|
|
|
i_itd = 0;
|
|
|
|
|
index_to_delete = indexes_to_delete[0];
|
|
|
|
|
if (index_to_delete >= array->len)
|
|
|
|
|
g_return_if_reached ();
|
|
|
|
|
|
|
|
|
|
res_length = array->len - 1;
|
|
|
|
|
|
|
|
|
|
mm_dst = index_to_delete;
|
|
|
|
|
mm_src = index_to_delete;
|
|
|
|
|
mm_len = 0;
|
|
|
|
|
|
|
|
|
|
for (i_src = index_to_delete; i_src < array->len; i_src++) {
|
|
|
|
|
if (i_src < index_to_delete)
|
|
|
|
|
mm_len++;
|
|
|
|
|
else {
|
|
|
|
|
/* we require indexes_to_delete to contain non-repeated, ascending
|
|
|
|
|
* indexes. Otherwise we would need to presort the indexes. */
|
|
|
|
|
while (TRUE) {
|
|
|
|
|
guint dd;
|
|
|
|
|
|
|
|
|
|
if (i_itd + 1 >= len) {
|
|
|
|
|
index_to_delete = G_MAXUINT;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dd = indexes_to_delete[++i_itd];
|
|
|
|
|
if (dd > index_to_delete) {
|
|
|
|
|
if (dd >= array->len)
|
|
|
|
|
g_warn_if_reached ();
|
|
|
|
|
else {
|
|
|
|
|
g_assert (res_length > 0);
|
|
|
|
|
res_length--;
|
|
|
|
|
}
|
|
|
|
|
index_to_delete = dd;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
g_warn_if_reached ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mm_len) {
|
|
|
|
|
memmove (&array->data[mm_dst * elt_size],
|
|
|
|
|
&array->data[mm_src * elt_size],
|
|
|
|
|
mm_len * elt_size);
|
|
|
|
|
mm_dst += mm_len;
|
|
|
|
|
mm_src += mm_len + 1;
|
|
|
|
|
mm_len = 0;
|
|
|
|
|
} else
|
|
|
|
|
mm_src++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (mm_len) {
|
|
|
|
|
memmove (&array->data[mm_dst * elt_size],
|
|
|
|
|
&array->data[mm_src * elt_size],
|
|
|
|
|
mm_len * elt_size);
|
|
|
|
|
}
|
|
|
|
|
g_array_set_size (array, res_length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
_trunk_first_line (char *str)
|
|
|
|
|
{
|
|
|
|
|
char *s;
|
|
|
|
|
|
|
|
|
|
s = strchr (str, '\n');
|
|
|
|
|
if (s)
|
|
|
|
|
s[0] = '\0';
|
|
|
|
|
return str;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
nm_utils_modprobe (GError **error, gboolean suppress_error_logging, const char *arg1, ...)
|
|
|
|
|
{
|
|
|
|
|
gs_unref_ptrarray GPtrArray *argv = NULL;
|
|
|
|
|
int exit_status;
|
|
|
|
|
gs_free char *_log_str = NULL;
|
|
|
|
|
#define ARGV_TO_STR(argv) (_log_str ? _log_str : (_log_str = g_strjoinv (" ", (char **) argv->pdata)))
|
|
|
|
|
GError *local = NULL;
|
|
|
|
|
va_list ap;
|
|
|
|
|
NMLogLevel llevel = suppress_error_logging ? LOGL_DEBUG : LOGL_ERR;
|
|
|
|
|
gs_free char *std_out = NULL, *std_err = NULL;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (!error || !*error, -1);
|
|
|
|
|
g_return_val_if_fail (arg1, -1);
|
|
|
|
|
|
|
|
|
|
/* construct the argument list */
|
|
|
|
|
argv = g_ptr_array_sized_new (4);
|
|
|
|
|
g_ptr_array_add (argv, "/sbin/modprobe");
|
2019-03-07 15:40:51 +01:00
|
|
|
g_ptr_array_add (argv, "--use-blacklist");
|
2016-03-01 09:56:51 +01:00
|
|
|
g_ptr_array_add (argv, (char *) arg1);
|
|
|
|
|
|
|
|
|
|
va_start (ap, arg1);
|
|
|
|
|
while ((arg1 = va_arg (ap, const char *)))
|
|
|
|
|
g_ptr_array_add (argv, (char *) arg1);
|
|
|
|
|
va_end (ap);
|
|
|
|
|
|
|
|
|
|
g_ptr_array_add (argv, NULL);
|
|
|
|
|
|
|
|
|
|
nm_log_dbg (LOGD_CORE, "modprobe: '%s'", ARGV_TO_STR (argv));
|
|
|
|
|
if (!g_spawn_sync (NULL, (char **) argv->pdata, NULL, 0, NULL, NULL, &std_out, &std_err, &exit_status, &local)) {
|
2017-03-01 10:20:01 +00:00
|
|
|
nm_log (llevel, LOGD_CORE, NULL, NULL, "modprobe: '%s' failed: %s", ARGV_TO_STR (argv), local->message);
|
2016-03-01 09:56:51 +01:00
|
|
|
g_propagate_error (error, local);
|
|
|
|
|
return -1;
|
2016-03-04 09:02:45 +01:00
|
|
|
} else if (exit_status != 0) {
|
2017-03-01 10:20:01 +00:00
|
|
|
nm_log (llevel, LOGD_CORE, NULL, NULL, "modprobe: '%s' exited with error %d%s%s%s%s%s%s", ARGV_TO_STR (argv), exit_status,
|
2016-03-01 09:56:51 +01:00
|
|
|
std_out&&*std_out ? " (" : "", std_out&&*std_out ? _trunk_first_line (std_out) : "", std_out&&*std_out ? ")" : "",
|
|
|
|
|
std_err&&*std_err ? " (" : "", std_err&&*std_err ? _trunk_first_line (std_err) : "", std_err&&*std_err ? ")" : "");
|
2016-03-04 09:02:45 +01:00
|
|
|
}
|
2016-03-01 09:56:51 +01:00
|
|
|
|
|
|
|
|
return exit_status;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-02 18:22:50 +02:00
|
|
|
/*****************************************************************************/
|
2016-03-01 09:56:51 +01:00
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
pid_t pid;
|
|
|
|
|
NMLogDomain log_domain;
|
|
|
|
|
union {
|
|
|
|
|
struct {
|
|
|
|
|
gint64 wait_start_us;
|
|
|
|
|
guint source_timeout_kill_id;
|
|
|
|
|
} async;
|
|
|
|
|
struct {
|
|
|
|
|
gboolean success;
|
|
|
|
|
int child_status;
|
|
|
|
|
} sync;
|
|
|
|
|
};
|
|
|
|
|
NMUtilsKillChildAsyncCb callback;
|
|
|
|
|
void *user_data;
|
|
|
|
|
|
|
|
|
|
char log_name[1]; /* variable-length object, must be last element!! */
|
|
|
|
|
} KillChildAsyncData;
|
|
|
|
|
|
|
|
|
|
#define LOG_NAME_FMT "kill child process '%s' (%ld)"
|
|
|
|
|
#define LOG_NAME_PROCESS_FMT "kill process '%s' (%ld)"
|
|
|
|
|
#define LOG_NAME_ARGS log_name,(long)pid
|
|
|
|
|
|
|
|
|
|
static KillChildAsyncData *
|
|
|
|
|
_kc_async_data_alloc (pid_t pid, NMLogDomain log_domain, const char *log_name, NMUtilsKillChildAsyncCb callback, void *user_data)
|
|
|
|
|
{
|
|
|
|
|
KillChildAsyncData *data;
|
|
|
|
|
size_t log_name_len;
|
|
|
|
|
|
|
|
|
|
/* append the name at the end of our KillChildAsyncData. */
|
|
|
|
|
log_name_len = strlen (LOG_NAME_FMT) + 20 + strlen (log_name);
|
|
|
|
|
data = g_malloc (sizeof (KillChildAsyncData) - 1 + log_name_len);
|
|
|
|
|
g_snprintf (data->log_name, log_name_len, LOG_NAME_FMT, LOG_NAME_ARGS);
|
|
|
|
|
|
|
|
|
|
data->pid = pid;
|
|
|
|
|
data->user_data = user_data;
|
|
|
|
|
data->callback = callback;
|
|
|
|
|
data->log_domain = log_domain;
|
|
|
|
|
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define KC_EXIT_TO_STRING_BUF_SIZE 128
|
|
|
|
|
static const char *
|
|
|
|
|
_kc_exit_to_string (char *buf, int exit)
|
|
|
|
|
#define _kc_exit_to_string(buf, exit) ( G_STATIC_ASSERT_EXPR(sizeof (buf) == KC_EXIT_TO_STRING_BUF_SIZE && sizeof ((buf)[0]) == 1), _kc_exit_to_string (buf, exit) )
|
|
|
|
|
{
|
|
|
|
|
if (WIFEXITED (exit))
|
|
|
|
|
g_snprintf (buf, KC_EXIT_TO_STRING_BUF_SIZE, "normally with status %d", WEXITSTATUS (exit));
|
|
|
|
|
else if (WIFSIGNALED (exit))
|
|
|
|
|
g_snprintf (buf, KC_EXIT_TO_STRING_BUF_SIZE, "by signal %d", WTERMSIG (exit));
|
|
|
|
|
else
|
|
|
|
|
g_snprintf (buf, KC_EXIT_TO_STRING_BUF_SIZE, "with unexpected status %d", exit);
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
_kc_signal_to_string (int sig)
|
|
|
|
|
{
|
|
|
|
|
switch (sig) {
|
|
|
|
|
case 0: return "no signal (0)";
|
|
|
|
|
case SIGKILL: return "SIGKILL (" G_STRINGIFY (SIGKILL) ")";
|
|
|
|
|
case SIGTERM: return "SIGTERM (" G_STRINGIFY (SIGTERM) ")";
|
|
|
|
|
default:
|
|
|
|
|
return "Unexpected signal";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define KC_WAITED_TO_STRING 100
|
|
|
|
|
static const char *
|
|
|
|
|
_kc_waited_to_string (char *buf, gint64 wait_start_us)
|
|
|
|
|
#define _kc_waited_to_string(buf, wait_start_us) ( G_STATIC_ASSERT_EXPR(sizeof (buf) == KC_WAITED_TO_STRING && sizeof ((buf)[0]) == 1), _kc_waited_to_string (buf, wait_start_us) )
|
|
|
|
|
{
|
2019-12-13 16:54:30 +01:00
|
|
|
g_snprintf (buf, KC_WAITED_TO_STRING, " (%ld usec elapsed)", (long) (nm_utils_get_monotonic_timestamp_usec () - wait_start_us));
|
2016-03-01 09:56:51 +01:00
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
all: don't use gchar/gshort/gint/glong but C types
We commonly don't use the glib typedefs for char/short/int/long,
but their C types directly.
$ git grep '\<g\(char\|short\|int\|long\|float\|double\)\>' | wc -l
587
$ git grep '\<\(char\|short\|int\|long\|float\|double\)\>' | wc -l
21114
One could argue that using the glib typedefs is preferable in
public API (of our glib based libnm library) or where it clearly
is related to glib, like during
g_object_set (obj, PROPERTY, (gint) value, NULL);
However, that argument does not seem strong, because in practice we don't
follow that argument today, and seldomly use the glib typedefs.
Also, the style guide for this would be hard to formalize, because
"using them where clearly related to a glib" is a very loose suggestion.
Also note that glib typedefs will always just be typedefs of the
underlying C types. There is no danger of glib changing the meaning
of these typedefs (because that would be a major API break of glib).
A simple style guide is instead: don't use these typedefs.
No manual actions, I only ran the bash script:
FILES=($(git ls-files '*.[hc]'))
sed -i \
-e 's/\<g\(char\|short\|int\|long\|float\|double\)\>\( [^ ]\)/\1\2/g' \
-e 's/\<g\(char\|short\|int\|long\|float\|double\)\> /\1 /g' \
-e 's/\<g\(char\|short\|int\|long\|float\|double\)\>/\1/g' \
"${FILES[@]}"
2018-07-11 07:40:19 +02:00
|
|
|
_kc_cb_watch_child (GPid pid, int status, gpointer user_data)
|
2016-03-01 09:56:51 +01:00
|
|
|
{
|
|
|
|
|
KillChildAsyncData *data = user_data;
|
|
|
|
|
char buf_exit[KC_EXIT_TO_STRING_BUF_SIZE], buf_wait[KC_WAITED_TO_STRING];
|
|
|
|
|
|
|
|
|
|
if (data->async.source_timeout_kill_id)
|
|
|
|
|
g_source_remove (data->async.source_timeout_kill_id);
|
|
|
|
|
|
|
|
|
|
nm_log_dbg (data->log_domain, "%s: terminated %s%s",
|
|
|
|
|
data->log_name, _kc_exit_to_string (buf_exit, status),
|
|
|
|
|
_kc_waited_to_string (buf_wait, data->async.wait_start_us));
|
|
|
|
|
|
|
|
|
|
if (data->callback)
|
|
|
|
|
data->callback (pid, TRUE, status, data->user_data);
|
|
|
|
|
|
|
|
|
|
g_free (data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
_kc_cb_timeout_grace_period (void *user_data)
|
|
|
|
|
{
|
|
|
|
|
KillChildAsyncData *data = user_data;
|
|
|
|
|
int ret, errsv;
|
|
|
|
|
|
|
|
|
|
data->async.source_timeout_kill_id = 0;
|
|
|
|
|
|
|
|
|
|
if ((ret = kill (data->pid, SIGKILL)) != 0) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
/* ESRCH means, process does not exist or is already a zombie. */
|
|
|
|
|
if (errsv != ESRCH) {
|
|
|
|
|
nm_log_err (LOGD_CORE | data->log_domain, "%s: kill(SIGKILL) returned unexpected return value %d: (%s, %d)",
|
2019-01-31 17:22:18 +01:00
|
|
|
data->log_name, ret, nm_strerror_native (errsv), errsv);
|
2016-03-01 09:56:51 +01:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
nm_log_dbg (data->log_domain, "%s: process not terminated after %ld usec. Sending SIGKILL signal",
|
2019-12-13 16:54:30 +01:00
|
|
|
data->log_name, (long) (nm_utils_get_monotonic_timestamp_usec () - data->async.wait_start_us));
|
2016-03-01 09:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return G_SOURCE_REMOVE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
_kc_invoke_callback_idle (gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
KillChildAsyncData *data = user_data;
|
|
|
|
|
|
|
|
|
|
if (data->sync.success) {
|
|
|
|
|
char buf_exit[KC_EXIT_TO_STRING_BUF_SIZE];
|
|
|
|
|
|
|
|
|
|
nm_log_dbg (data->log_domain, "%s: invoke callback: terminated %s",
|
|
|
|
|
data->log_name, _kc_exit_to_string (buf_exit, data->sync.child_status));
|
|
|
|
|
} else
|
|
|
|
|
nm_log_dbg (data->log_domain, "%s: invoke callback: killing child failed", data->log_name);
|
|
|
|
|
|
|
|
|
|
data->callback (data->pid, data->sync.success, data->sync.child_status, data->user_data);
|
|
|
|
|
g_free (data);
|
|
|
|
|
|
|
|
|
|
return G_SOURCE_REMOVE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_kc_invoke_callback (pid_t pid, NMLogDomain log_domain, const char *log_name, NMUtilsKillChildAsyncCb callback, void *user_data, gboolean success, int child_status)
|
|
|
|
|
{
|
|
|
|
|
KillChildAsyncData *data;
|
|
|
|
|
|
|
|
|
|
if (!callback)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
data = _kc_async_data_alloc (pid, log_domain, log_name, callback, user_data);
|
|
|
|
|
data->sync.success = success;
|
|
|
|
|
data->sync.child_status = child_status;
|
|
|
|
|
|
|
|
|
|
g_idle_add (_kc_invoke_callback_idle, data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* nm_utils_kill_child_async:
|
|
|
|
|
* @pid: the process id of the process to kill
|
|
|
|
|
* @sig: signal to send initially. Set to 0 to send not signal.
|
|
|
|
|
* @log_domain: the logging domain used for logging (LOGD_NONE to suppress logging)
|
|
|
|
|
* @log_name: for logging, the name of the processes to kill
|
|
|
|
|
* @wait_before_kill_msec: Waittime in milliseconds before sending %SIGKILL signal. Set this value
|
|
|
|
|
* to zero, not to send %SIGKILL. If @sig is already %SIGKILL, this parameter is ignored.
|
|
|
|
|
* @callback: (allow-none): callback after the child terminated. This function will always
|
|
|
|
|
* be invoked asynchronously.
|
|
|
|
|
* @user_data: passed on to callback
|
|
|
|
|
*
|
|
|
|
|
* Uses g_child_watch_add(), so note the glib comment: if you obtain pid from g_spawn_async() or
|
|
|
|
|
* g_spawn_async_with_pipes() you will need to pass %G_SPAWN_DO_NOT_REAP_CHILD as flag to the spawn
|
|
|
|
|
* function for the child watching to work.
|
|
|
|
|
* Also note, that you must g_source_remove() any other child watchers for @pid because glib
|
|
|
|
|
* supports only one watcher per child.
|
|
|
|
|
**/
|
|
|
|
|
void
|
|
|
|
|
nm_utils_kill_child_async (pid_t pid, int sig, NMLogDomain log_domain,
|
|
|
|
|
const char *log_name, guint32 wait_before_kill_msec,
|
|
|
|
|
NMUtilsKillChildAsyncCb callback, void *user_data)
|
|
|
|
|
{
|
|
|
|
|
int status = 0, errsv;
|
|
|
|
|
pid_t ret;
|
|
|
|
|
KillChildAsyncData *data;
|
|
|
|
|
char buf_exit[KC_EXIT_TO_STRING_BUF_SIZE];
|
|
|
|
|
|
|
|
|
|
g_return_if_fail (pid > 0);
|
|
|
|
|
g_return_if_fail (log_name != NULL);
|
|
|
|
|
|
|
|
|
|
/* let's see if the child already terminated... */
|
|
|
|
|
ret = waitpid (pid, &status, WNOHANG);
|
|
|
|
|
if (ret > 0) {
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_FMT ": process %ld already terminated %s",
|
|
|
|
|
LOG_NAME_ARGS, (long) ret, _kc_exit_to_string (buf_exit, status));
|
|
|
|
|
_kc_invoke_callback (pid, log_domain, log_name, callback, user_data, TRUE, status);
|
|
|
|
|
return;
|
|
|
|
|
} else if (ret != 0) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
/* ECHILD means, the process is not a child/does not exist or it has SIGCHILD blocked. */
|
|
|
|
|
if (errsv != ECHILD) {
|
|
|
|
|
nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": unexpected error while waitpid: %s (%d)",
|
2019-01-31 17:22:18 +01:00
|
|
|
LOG_NAME_ARGS, nm_strerror_native (errsv), errsv);
|
2016-03-01 09:56:51 +01:00
|
|
|
_kc_invoke_callback (pid, log_domain, log_name, callback, user_data, FALSE, -1);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* send the first signal. */
|
|
|
|
|
if (kill (pid, sig) != 0) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
/* ESRCH means, process does not exist or is already a zombie. */
|
|
|
|
|
if (errsv != ESRCH) {
|
|
|
|
|
nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": unexpected error sending %s: %s (%d)",
|
2019-01-31 17:22:18 +01:00
|
|
|
LOG_NAME_ARGS, _kc_signal_to_string (sig), nm_strerror_native (errsv), errsv);
|
2016-03-01 09:56:51 +01:00
|
|
|
_kc_invoke_callback (pid, log_domain, log_name, callback, user_data, FALSE, -1);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* let's try again with waitpid, probably there was a race... */
|
|
|
|
|
ret = waitpid (pid, &status, 0);
|
|
|
|
|
if (ret > 0) {
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_FMT ": process %ld already terminated %s",
|
|
|
|
|
LOG_NAME_ARGS, (long) ret, _kc_exit_to_string (buf_exit, status));
|
|
|
|
|
_kc_invoke_callback (pid, log_domain, log_name, callback, user_data, TRUE, status);
|
|
|
|
|
} else {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": failed due to unexpected return value %ld by waitpid (%s, %d) after sending %s",
|
2019-01-31 17:22:18 +01:00
|
|
|
LOG_NAME_ARGS, (long) ret, nm_strerror_native (errsv), errsv, _kc_signal_to_string (sig));
|
2016-03-01 09:56:51 +01:00
|
|
|
_kc_invoke_callback (pid, log_domain, log_name, callback, user_data, FALSE, -1);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data = _kc_async_data_alloc (pid, log_domain, log_name, callback, user_data);
|
2019-12-13 16:54:30 +01:00
|
|
|
data->async.wait_start_us = nm_utils_get_monotonic_timestamp_usec ();
|
2016-03-01 09:56:51 +01:00
|
|
|
|
|
|
|
|
if (sig != SIGKILL && wait_before_kill_msec > 0) {
|
|
|
|
|
data->async.source_timeout_kill_id = g_timeout_add (wait_before_kill_msec, _kc_cb_timeout_grace_period, data);
|
|
|
|
|
nm_log_dbg (log_domain, "%s: wait for process to terminate after sending %s (send SIGKILL in %ld milliseconds)...",
|
|
|
|
|
data->log_name, _kc_signal_to_string (sig), (long) wait_before_kill_msec);
|
|
|
|
|
} else {
|
|
|
|
|
data->async.source_timeout_kill_id = 0;
|
|
|
|
|
nm_log_dbg (log_domain, "%s: wait for process to terminate after sending %s...",
|
|
|
|
|
data->log_name, _kc_signal_to_string (sig));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_child_watch_add (pid, _kc_cb_watch_child, data);
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-06 09:04:23 +01:00
|
|
|
static gulong
|
2016-03-01 09:56:51 +01:00
|
|
|
_sleep_duration_convert_ms_to_us (guint32 sleep_duration_msec)
|
|
|
|
|
{
|
|
|
|
|
if (sleep_duration_msec > 0) {
|
|
|
|
|
guint64 x = (gint64) sleep_duration_msec * (guint64) 1000L;
|
|
|
|
|
|
|
|
|
|
return x < G_MAXULONG ? (gulong) x : G_MAXULONG;
|
|
|
|
|
}
|
|
|
|
|
return G_USEC_PER_SEC / 20;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* nm_utils_kill_child_sync:
|
|
|
|
|
* @pid: process id to kill
|
|
|
|
|
* @sig: signal to sent initially. If 0, no signal is sent. If %SIGKILL, the
|
|
|
|
|
* second %SIGKILL signal is not sent after @wait_before_kill_msec milliseconds.
|
|
|
|
|
* @log_domain: log debug information for this domain. Errors and warnings are logged both
|
|
|
|
|
* as %LOGD_CORE and @log_domain.
|
|
|
|
|
* @log_name: name of the process to kill for logging.
|
2018-09-15 07:20:54 -04:00
|
|
|
* @child_status: (out) (allow-none): return the exit status of the child, if no error occurred.
|
2016-03-01 09:56:51 +01:00
|
|
|
* @wait_before_kill_msec: Waittime in milliseconds before sending %SIGKILL signal. Set this value
|
|
|
|
|
* to zero, not to send %SIGKILL. If @sig is already %SIGKILL, this parameter has not effect.
|
|
|
|
|
* @sleep_duration_msec: the synchronous function sleeps repeatedly waiting for the child to terminate.
|
|
|
|
|
* Set to zero, to use the default (meaning 20 wakeups per seconds).
|
|
|
|
|
*
|
|
|
|
|
* Kill a child process synchronously and wait. The function first checks if the child already terminated
|
|
|
|
|
* and if it did, return the exit status. Otherwise send one @sig signal. @sig will always be
|
|
|
|
|
* sent unless the child already exited. If the child does not exit within @wait_before_kill_msec milliseconds,
|
2018-09-15 07:20:54 -04:00
|
|
|
* the function will send %SIGKILL and waits for the child indefinitely. If @wait_before_kill_msec is zero, no
|
2016-03-01 09:56:51 +01:00
|
|
|
* %SIGKILL signal will be sent.
|
|
|
|
|
*
|
|
|
|
|
* In case of error, errno is preserved to contain the last reason of failure.
|
|
|
|
|
**/
|
|
|
|
|
gboolean
|
|
|
|
|
nm_utils_kill_child_sync (pid_t pid, int sig, NMLogDomain log_domain, const char *log_name,
|
|
|
|
|
int *child_status, guint32 wait_before_kill_msec,
|
|
|
|
|
guint32 sleep_duration_msec)
|
|
|
|
|
{
|
|
|
|
|
int status = 0, errsv = 0;
|
|
|
|
|
pid_t ret;
|
|
|
|
|
gboolean success = FALSE;
|
|
|
|
|
gboolean was_waiting = FALSE, send_kill = FALSE;
|
|
|
|
|
char buf_exit[KC_EXIT_TO_STRING_BUF_SIZE];
|
|
|
|
|
char buf_wait[KC_WAITED_TO_STRING];
|
|
|
|
|
gint64 wait_start_us;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (pid > 0, FALSE);
|
|
|
|
|
g_return_val_if_fail (log_name != NULL, FALSE);
|
|
|
|
|
|
|
|
|
|
/* check if the child process already terminated... */
|
|
|
|
|
ret = waitpid (pid, &status, WNOHANG);
|
|
|
|
|
if (ret > 0) {
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_FMT ": process %ld already terminated %s",
|
|
|
|
|
LOG_NAME_ARGS, (long) ret, _kc_exit_to_string (buf_exit, status));
|
|
|
|
|
success = TRUE;
|
|
|
|
|
goto out;
|
|
|
|
|
} else if (ret != 0) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
/* ECHILD means, the process is not a child/does not exist or it has SIGCHILD blocked. */
|
|
|
|
|
if (errsv != ECHILD) {
|
|
|
|
|
nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": unexpected error while waitpid: %s (%d)",
|
2019-01-31 17:22:18 +01:00
|
|
|
LOG_NAME_ARGS, nm_strerror_native (errsv), errsv);
|
2016-03-01 09:56:51 +01:00
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* send first signal @sig */
|
|
|
|
|
if (kill (pid, sig) != 0) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
/* ESRCH means, process does not exist or is already a zombie. */
|
|
|
|
|
if (errsv != ESRCH) {
|
|
|
|
|
nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": failed to send %s: %s (%d)",
|
2019-01-31 17:22:18 +01:00
|
|
|
LOG_NAME_ARGS, _kc_signal_to_string (sig), nm_strerror_native (errsv), errsv);
|
2016-03-01 09:56:51 +01:00
|
|
|
} else {
|
|
|
|
|
/* let's try again with waitpid, probably there was a race... */
|
|
|
|
|
ret = waitpid (pid, &status, 0);
|
|
|
|
|
if (ret > 0) {
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_FMT ": process %ld already terminated %s",
|
|
|
|
|
LOG_NAME_ARGS, (long) ret, _kc_exit_to_string (buf_exit, status));
|
|
|
|
|
success = TRUE;
|
|
|
|
|
} else {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": failed due to unexpected return value %ld by waitpid (%s, %d) after sending %s",
|
2019-01-31 17:22:18 +01:00
|
|
|
LOG_NAME_ARGS, (long) ret, nm_strerror_native (errsv), errsv, _kc_signal_to_string (sig));
|
2016-03-01 09:56:51 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-13 16:54:30 +01:00
|
|
|
wait_start_us = nm_utils_get_monotonic_timestamp_usec ();
|
2016-03-01 09:56:51 +01:00
|
|
|
|
|
|
|
|
/* wait for the process to terminated... */
|
|
|
|
|
if (sig != SIGKILL) {
|
|
|
|
|
gint64 wait_until, now;
|
|
|
|
|
gulong sleep_time, sleep_duration_usec;
|
|
|
|
|
int loop_count = 0;
|
|
|
|
|
|
|
|
|
|
sleep_duration_usec = _sleep_duration_convert_ms_to_us (sleep_duration_msec);
|
|
|
|
|
wait_until = wait_before_kill_msec <= 0 ? 0 : wait_start_us + (((gint64) wait_before_kill_msec) * 1000L);
|
|
|
|
|
|
|
|
|
|
while (TRUE) {
|
|
|
|
|
ret = waitpid (pid, &status, WNOHANG);
|
|
|
|
|
if (ret > 0) {
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_FMT ": after sending %s, process %ld exited %s%s",
|
|
|
|
|
LOG_NAME_ARGS, _kc_signal_to_string (sig), (long) ret, _kc_exit_to_string (buf_exit, status),
|
|
|
|
|
was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : "");
|
|
|
|
|
success = TRUE;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
if (ret == -1) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
/* ECHILD means, the process is not a child/does not exist or it has SIGCHILD blocked. */
|
|
|
|
|
if (errsv != ECHILD) {
|
|
|
|
|
nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": after sending %s, waitpid failed with %s (%d)%s",
|
2019-01-31 17:22:18 +01:00
|
|
|
LOG_NAME_ARGS, _kc_signal_to_string (sig), nm_strerror_native (errsv), errsv,
|
2016-03-01 09:56:51 +01:00
|
|
|
was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : "");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!wait_until)
|
|
|
|
|
break;
|
|
|
|
|
|
2019-12-13 16:54:30 +01:00
|
|
|
now = nm_utils_get_monotonic_timestamp_usec ();
|
2016-03-01 09:56:51 +01:00
|
|
|
if (now >= wait_until)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (!was_waiting) {
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_FMT ": waiting up to %ld milliseconds for process to terminate normally after sending %s...",
|
|
|
|
|
LOG_NAME_ARGS, (long) MAX (wait_before_kill_msec, 0), _kc_signal_to_string (sig));
|
|
|
|
|
was_waiting = TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sleep_time = MIN (wait_until - now, sleep_duration_usec);
|
|
|
|
|
if (loop_count < 20) {
|
|
|
|
|
/* At the beginning we expect the process to die fast.
|
|
|
|
|
* Limit the sleep time, the limit doubles with every iteration. */
|
|
|
|
|
sleep_time = MIN (sleep_time, (((guint64) 1) << loop_count) * G_USEC_PER_SEC / 2000);
|
|
|
|
|
loop_count++;
|
|
|
|
|
}
|
|
|
|
|
g_usleep (sleep_time);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* send SIGKILL, if called with @wait_before_kill_msec > 0 */
|
|
|
|
|
if (wait_until) {
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_FMT ": sending SIGKILL...", LOG_NAME_ARGS);
|
|
|
|
|
|
|
|
|
|
send_kill = TRUE;
|
|
|
|
|
if (kill (pid, SIGKILL) != 0) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
/* ESRCH means, process does not exist or is already a zombie. */
|
|
|
|
|
if (errsv != ESRCH) {
|
|
|
|
|
nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": failed to send SIGKILL (after sending %s), %s (%d)",
|
2019-01-31 17:22:18 +01:00
|
|
|
LOG_NAME_ARGS, _kc_signal_to_string (sig), nm_strerror_native (errsv), errsv);
|
2016-03-01 09:56:51 +01:00
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!was_waiting) {
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_FMT ": waiting for process to terminate after sending %s%s...",
|
|
|
|
|
LOG_NAME_ARGS, _kc_signal_to_string (sig), send_kill ? " and SIGKILL" : "");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* block until the child terminates. */
|
|
|
|
|
while ((ret = waitpid (pid, &status, 0)) <= 0) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
|
|
|
|
|
if (errsv != EINTR) {
|
|
|
|
|
nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": after sending %s%s, waitpid failed with %s (%d)%s",
|
2019-01-31 17:22:18 +01:00
|
|
|
LOG_NAME_ARGS, _kc_signal_to_string (sig), send_kill ? " and SIGKILL" : "", nm_strerror_native (errsv), errsv,
|
2016-03-01 09:56:51 +01:00
|
|
|
_kc_waited_to_string (buf_wait, wait_start_us));
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_FMT ": after sending %s%s, process %ld exited %s%s",
|
|
|
|
|
LOG_NAME_ARGS, _kc_signal_to_string (sig), send_kill ? " and SIGKILL" : "", (long) ret,
|
|
|
|
|
_kc_exit_to_string (buf_exit, status), _kc_waited_to_string (buf_wait, wait_start_us));
|
|
|
|
|
success = TRUE;
|
|
|
|
|
out:
|
|
|
|
|
if (child_status)
|
|
|
|
|
*child_status = success ? status : -1;
|
|
|
|
|
errno = success ? 0 : errsv;
|
|
|
|
|
return success;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* nm_utils_kill_process_sync:
|
|
|
|
|
* @pid: process id to kill
|
|
|
|
|
* @start_time: the start time of the process to kill (as obtained by nm_utils_get_start_time_for_pid()).
|
|
|
|
|
* This is an optional argument, to avoid (somewhat) killing the wrong process as @pid
|
|
|
|
|
* might get recycled. You can pass 0, to not provide this parameter.
|
|
|
|
|
* @sig: signal to sent initially. If 0, no signal is sent. If %SIGKILL, the
|
|
|
|
|
* second %SIGKILL signal is not sent after @wait_before_kill_msec milliseconds.
|
|
|
|
|
* @log_domain: log debug information for this domain. Errors and warnings are logged both
|
|
|
|
|
* as %LOGD_CORE and @log_domain.
|
|
|
|
|
* @log_name: name of the process to kill for logging.
|
|
|
|
|
* @wait_before_kill_msec: Waittime in milliseconds before sending %SIGKILL signal. Set this value
|
|
|
|
|
* to zero, not to send %SIGKILL. If @sig is already %SIGKILL, this parameter has no effect.
|
|
|
|
|
* If @max_wait_msec is set but less then @wait_before_kill_msec, the final %SIGKILL will also
|
|
|
|
|
* not be send.
|
|
|
|
|
* @sleep_duration_msec: the synchronous function sleeps repeatedly waiting for the child to terminate.
|
|
|
|
|
* Set to zero, to use the default (meaning 20 wakeups per seconds).
|
|
|
|
|
* @max_wait_msec: if 0, waits indefinitely until the process is gone (or a zombie). Otherwise, this
|
2018-09-15 07:20:54 -04:00
|
|
|
* is the maximum wait time until returning. If @max_wait_msec is non-zero but smaller then @wait_before_kill_msec,
|
2016-03-01 09:56:51 +01:00
|
|
|
* we will not send a final %SIGKILL.
|
|
|
|
|
*
|
|
|
|
|
* Kill a non-child process synchronously and wait. This function will not return before the
|
|
|
|
|
* process with PID @pid is gone, the process is a zombie, or @max_wait_msec expires.
|
|
|
|
|
**/
|
|
|
|
|
void
|
|
|
|
|
nm_utils_kill_process_sync (pid_t pid, guint64 start_time, int sig, NMLogDomain log_domain,
|
|
|
|
|
const char *log_name, guint32 wait_before_kill_msec,
|
|
|
|
|
guint32 sleep_duration_msec, guint32 max_wait_msec)
|
|
|
|
|
{
|
|
|
|
|
int errsv;
|
|
|
|
|
guint64 start_time0;
|
|
|
|
|
gint64 wait_until_sigkill, now, wait_start_us, max_wait_until;
|
|
|
|
|
gulong sleep_time, sleep_duration_usec;
|
|
|
|
|
int loop_count = 0;
|
|
|
|
|
gboolean was_waiting = FALSE;
|
|
|
|
|
char buf_wait[KC_WAITED_TO_STRING];
|
|
|
|
|
char p_state;
|
|
|
|
|
|
|
|
|
|
g_return_if_fail (pid > 0);
|
|
|
|
|
g_return_if_fail (log_name != NULL);
|
|
|
|
|
|
|
|
|
|
start_time0 = nm_utils_get_start_time_for_pid (pid, &p_state, NULL);
|
|
|
|
|
if (start_time0 == 0) {
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": cannot kill process %ld because it seems already gone",
|
|
|
|
|
LOG_NAME_ARGS, (long int) pid);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (start_time != 0 && start_time != start_time0) {
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": don't kill process %ld because the start_time is unexpectedly %lu instead of %ld",
|
2017-03-14 11:15:05 +01:00
|
|
|
LOG_NAME_ARGS, (long int) pid, (unsigned long) start_time0, (unsigned long) start_time);
|
2016-03-01 09:56:51 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (p_state) {
|
|
|
|
|
case 'Z':
|
|
|
|
|
case 'x':
|
|
|
|
|
case 'X':
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": cannot kill process %ld because it is already a zombie (%c)",
|
|
|
|
|
LOG_NAME_ARGS, (long int) pid, p_state);
|
|
|
|
|
return;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (kill (pid, sig) != 0) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
/* ESRCH means, process does not exist or is already a zombie. */
|
|
|
|
|
if (errsv == ESRCH) {
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": failed to send %s because process seems gone",
|
|
|
|
|
LOG_NAME_ARGS, _kc_signal_to_string (sig));
|
|
|
|
|
} else {
|
|
|
|
|
nm_log_warn (LOGD_CORE | log_domain, LOG_NAME_PROCESS_FMT ": failed to send %s: %s (%d)",
|
2019-01-31 17:22:18 +01:00
|
|
|
LOG_NAME_ARGS, _kc_signal_to_string (sig), nm_strerror_native (errsv), errsv);
|
2016-03-01 09:56:51 +01:00
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-24 10:46:01 +01:00
|
|
|
/* wait for the process to terminate... */
|
2016-03-01 09:56:51 +01:00
|
|
|
|
2019-12-13 16:54:30 +01:00
|
|
|
wait_start_us = nm_utils_get_monotonic_timestamp_usec ();
|
2016-03-01 09:56:51 +01:00
|
|
|
|
|
|
|
|
sleep_duration_usec = _sleep_duration_convert_ms_to_us (sleep_duration_msec);
|
2016-03-24 10:46:01 +01:00
|
|
|
if (sig != SIGKILL && wait_before_kill_msec)
|
2016-03-01 09:56:51 +01:00
|
|
|
wait_until_sigkill = wait_start_us + (((gint64) wait_before_kill_msec) * 1000L);
|
|
|
|
|
else
|
|
|
|
|
wait_until_sigkill = 0;
|
|
|
|
|
if (max_wait_msec > 0) {
|
|
|
|
|
max_wait_until = wait_start_us + (((gint64) max_wait_msec) * 1000L);
|
|
|
|
|
if (wait_until_sigkill > 0 && wait_until_sigkill > max_wait_msec)
|
|
|
|
|
wait_until_sigkill = 0;
|
|
|
|
|
} else
|
|
|
|
|
max_wait_until = 0;
|
|
|
|
|
|
|
|
|
|
while (TRUE) {
|
|
|
|
|
start_time = nm_utils_get_start_time_for_pid (pid, &p_state, NULL);
|
|
|
|
|
|
|
|
|
|
if (start_time != start_time0) {
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": process is gone after sending signal %s%s",
|
|
|
|
|
LOG_NAME_ARGS, _kc_signal_to_string (sig),
|
|
|
|
|
was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : "");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
switch (p_state) {
|
|
|
|
|
case 'Z':
|
|
|
|
|
case 'x':
|
|
|
|
|
case 'X':
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": process is a zombie (%c) after sending signal %s%s",
|
|
|
|
|
LOG_NAME_ARGS, p_state, _kc_signal_to_string (sig),
|
|
|
|
|
was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : "");
|
|
|
|
|
return;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (kill (pid, 0) != 0) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
/* ESRCH means, process does not exist or is already a zombie. */
|
|
|
|
|
if (errsv == ESRCH) {
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": process is gone or a zombie after sending signal %s%s",
|
|
|
|
|
LOG_NAME_ARGS, _kc_signal_to_string (sig),
|
|
|
|
|
was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : "");
|
|
|
|
|
} else {
|
|
|
|
|
nm_log_warn (LOGD_CORE | log_domain, LOG_NAME_PROCESS_FMT ": failed to kill(%ld, 0): %s (%d)%s",
|
2019-01-31 17:22:18 +01:00
|
|
|
LOG_NAME_ARGS, (long int) pid, nm_strerror_native (errsv), errsv,
|
2016-03-01 09:56:51 +01:00
|
|
|
was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : "");
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sleep_time = sleep_duration_usec;
|
2019-12-13 16:54:30 +01:00
|
|
|
now = nm_utils_get_monotonic_timestamp_usec ();
|
2016-03-01 09:56:51 +01:00
|
|
|
|
|
|
|
|
if ( max_wait_until != 0
|
|
|
|
|
&& now >= max_wait_until) {
|
|
|
|
|
if (wait_until_sigkill != 0) {
|
|
|
|
|
/* wait_before_kill_msec is not larger then max_wait_until but we did not yet send
|
|
|
|
|
* SIGKILL. Although we already reached our timeout, we don't want to skip sending
|
|
|
|
|
* the signal. Even if we don't wait for the process to disappear. */
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": sending SIGKILL", LOG_NAME_ARGS);
|
|
|
|
|
kill (pid, SIGKILL);
|
|
|
|
|
}
|
|
|
|
|
nm_log_warn (log_domain, LOG_NAME_PROCESS_FMT ": timeout %u msec waiting for process to disappear (after sending %s)%s",
|
|
|
|
|
LOG_NAME_ARGS, (unsigned) max_wait_until, _kc_signal_to_string (sig),
|
|
|
|
|
was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : "");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (wait_until_sigkill != 0) {
|
|
|
|
|
if (now >= wait_until_sigkill) {
|
|
|
|
|
/* Still not dead. SIGKILL now... */
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": sending SIGKILL", LOG_NAME_ARGS);
|
|
|
|
|
if (kill (pid, SIGKILL) != 0) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
/* ESRCH means, process does not exist or is already a zombie. */
|
|
|
|
|
if (errsv != ESRCH) {
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": process is gone or a zombie%s",
|
|
|
|
|
LOG_NAME_ARGS, _kc_waited_to_string (buf_wait, wait_start_us));
|
|
|
|
|
} else {
|
|
|
|
|
nm_log_warn (LOGD_CORE | log_domain, LOG_NAME_PROCESS_FMT ": failed to send SIGKILL (after sending %s), %s (%d)%s",
|
2019-01-31 17:22:18 +01:00
|
|
|
LOG_NAME_ARGS, _kc_signal_to_string (sig), nm_strerror_native (errsv), errsv,
|
2016-03-01 09:56:51 +01:00
|
|
|
_kc_waited_to_string (buf_wait, wait_start_us));
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
sig = SIGKILL;
|
|
|
|
|
wait_until_sigkill = 0;
|
|
|
|
|
loop_count = 0; /* reset the loop_count. Now we really expect the process to die quickly. */
|
|
|
|
|
} else
|
|
|
|
|
sleep_time = MIN (wait_until_sigkill - now, sleep_duration_usec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!was_waiting) {
|
|
|
|
|
if (wait_until_sigkill != 0) {
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": waiting up to %ld milliseconds for process to disappear before sending KILL signal after sending %s...",
|
|
|
|
|
LOG_NAME_ARGS, (long) wait_before_kill_msec, _kc_signal_to_string (sig));
|
|
|
|
|
} else if (max_wait_until != 0) {
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": waiting up to %ld milliseconds for process to disappear after sending %s...",
|
|
|
|
|
LOG_NAME_ARGS, (long) max_wait_msec, _kc_signal_to_string (sig));
|
|
|
|
|
} else {
|
|
|
|
|
nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": waiting for process to disappear after sending %s...",
|
|
|
|
|
LOG_NAME_ARGS, _kc_signal_to_string (sig));
|
|
|
|
|
}
|
|
|
|
|
was_waiting = TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (loop_count < 20) {
|
|
|
|
|
/* At the beginning we expect the process to die fast.
|
|
|
|
|
* Limit the sleep time, the limit doubles with every iteration. */
|
|
|
|
|
sleep_time = MIN (sleep_time, (((guint64) 1) << loop_count) * G_USEC_PER_SEC / 2000);
|
|
|
|
|
loop_count++;
|
|
|
|
|
}
|
|
|
|
|
g_usleep (sleep_time);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#undef LOG_NAME_FMT
|
|
|
|
|
#undef LOG_NAME_PROCESS_FMT
|
|
|
|
|
#undef LOG_NAME_ARGS
|
|
|
|
|
|
|
|
|
|
const char *const NM_PATHS_DEFAULT[] = {
|
|
|
|
|
PREFIX "/sbin/",
|
|
|
|
|
PREFIX "/bin/",
|
2016-09-30 15:57:24 +01:00
|
|
|
"/usr/local/sbin/",
|
2016-03-01 09:56:51 +01:00
|
|
|
"/sbin/",
|
|
|
|
|
"/usr/sbin/",
|
2016-09-30 15:57:24 +01:00
|
|
|
"/usr/local/bin/",
|
2016-03-01 09:56:51 +01:00
|
|
|
"/bin/",
|
|
|
|
|
"/usr/bin/",
|
|
|
|
|
NULL,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const char *
|
dns/dnsmasq: refactor tracking of dnsmasq process
Several points.
- We spawn the dnsmasq process directly. That has several downsides:
- The lifetime of the process is tied to NetworkManager's. When
stopping NetworkManager, we usually also stop dnsmasq. Or we keep
the process running, but later the process is no longer a child process
of NetworkManager and we can only kill it using the pidfile.
- We don't do special sandboxing of the dnsmasq process.
- Note that we want to ensure that only one dnsmasq process is running
at any time. We should track that in a singletone. Note that NMDnsDnsmasq
is not a singleton. While there is only one instance active at any time,
the DNS plugin can be swapped (e.g. during SIGHUP). Hence, don't track the
process per-NMDnsDnsmasq instance, but in a global variable "gl_pid".
- Usually, when NetworkManager quits, it also stops the dnsmasq process.
Previously, we would always try to terminate the process based on the
pidfile. That is wrong. Most of the time, NetworkManager spawned the
process itself, as a child process. Hence, the PID is known and NetworkManager
will get a signal when dnsmasq exits. The only moment when NetworkManager should
use the pidfile, is the first time when checking to kill the previous instance.
That is: only once at the beginning, to kill instances that were
intentionally or unintentionally (crash) left running earlier.
This is now done by _gl_pid_kill_external().
- Previously, before starting a new dnsmasq instance we would kill a
possibly already running one, and block while waiting for the process to
disappear. We should never block. Especially, since we afterwards start
the process also in non-blocking way, there is no reason to kill the
existing process in a blocking way. For the most part, starting dnsmasq
is already asynchronous and so should be the killing of the dnsmasq
process.
- Drop GDBusProxy and only use GDBusConnection. It fully suffices.
- When we kill a dnsmasq instance, we actually don't have to wait at
all. That can happen fully in background. The only pecularity is that
when we restart a new instance before the previous instance is killed,
then we must wait for the previous process to terminate first. Also, if
we are about to exit while killing the dnsmasq instance, we must register
nm_shutdown_wait_obj_*() to wait until the process is fully gone.
2019-08-31 09:50:54 +02:00
|
|
|
nm_utils_find_helper (const char *progname, const char *try_first, GError **error)
|
2016-03-01 09:56:51 +01:00
|
|
|
{
|
|
|
|
|
return nm_utils_file_search_in_paths (progname, try_first, NM_PATHS_DEFAULT, G_FILE_TEST_IS_EXECUTABLE, NULL, NULL, error);
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-02 18:22:50 +02:00
|
|
|
/*****************************************************************************/
|
2016-03-01 09:56:51 +01:00
|
|
|
|
2016-04-27 14:47:59 +02:00
|
|
|
/**
|
|
|
|
|
* nm_utils_read_link_absolute:
|
|
|
|
|
* @link_file: file name of the symbolic link
|
|
|
|
|
* @error: error reason in case of failure
|
|
|
|
|
*
|
|
|
|
|
* Uses to g_file_read_link()/readlink() to read the symlink
|
|
|
|
|
* and returns the result as absolute path.
|
|
|
|
|
**/
|
|
|
|
|
char *
|
|
|
|
|
nm_utils_read_link_absolute (const char *link_file, GError **error)
|
|
|
|
|
{
|
|
|
|
|
char *ln, *dirname, *ln_abs;
|
|
|
|
|
|
|
|
|
|
ln = g_file_read_link (link_file, error);
|
|
|
|
|
if (!ln)
|
|
|
|
|
return NULL;
|
|
|
|
|
if (g_path_is_absolute (ln))
|
|
|
|
|
return ln;
|
|
|
|
|
|
|
|
|
|
dirname = g_path_get_dirname (link_file);
|
2018-09-27 13:51:55 +02:00
|
|
|
if (!g_path_is_absolute (dirname)) {
|
2016-05-12 15:24:05 +02:00
|
|
|
gs_free char *current_dir = g_get_current_dir ();
|
2016-04-27 14:47:59 +02:00
|
|
|
|
2018-09-27 13:51:55 +02:00
|
|
|
/* @link_file argument was not an absolute path in the first place.
|
|
|
|
|
* That actually may be a bug, because the CWD is not well defined
|
|
|
|
|
* in most cases. Anyway, apparently we were able to load the file
|
|
|
|
|
* even from a relative path. So, when making the link absolute, we
|
|
|
|
|
* also need to prepend the CWD. */
|
|
|
|
|
ln_abs = g_build_filename (current_dir, dirname, ln, NULL);
|
|
|
|
|
} else
|
|
|
|
|
ln_abs = g_build_filename (dirname, ln, NULL);
|
2016-04-27 14:47:59 +02:00
|
|
|
g_free (dirname);
|
|
|
|
|
g_free (ln);
|
|
|
|
|
return ln_abs;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-02 18:22:50 +02:00
|
|
|
/*****************************************************************************/
|
2016-04-27 14:47:59 +02:00
|
|
|
|
2019-07-18 15:37:55 +02:00
|
|
|
#define DEVICE_TYPE_TAG "type:"
|
|
|
|
|
#define DRIVER_TAG "driver:"
|
|
|
|
|
#define DHCP_PLUGIN_TAG "dhcp-plugin:"
|
|
|
|
|
#define EXCEPT_TAG "except:"
|
2016-03-01 09:56:51 +01:00
|
|
|
#define MATCH_TAG_CONFIG_NM_VERSION "nm-version:"
|
|
|
|
|
#define MATCH_TAG_CONFIG_NM_VERSION_MIN "nm-version-min:"
|
|
|
|
|
#define MATCH_TAG_CONFIG_NM_VERSION_MAX "nm-version-max:"
|
|
|
|
|
#define MATCH_TAG_CONFIG_ENV "env:"
|
|
|
|
|
|
2017-01-21 15:26:53 +01:00
|
|
|
typedef struct {
|
|
|
|
|
const char *interface_name;
|
|
|
|
|
const char *device_type;
|
2017-03-17 16:18:48 +01:00
|
|
|
const char *driver;
|
|
|
|
|
const char *driver_version;
|
device: add "dhcp-plugin" match spec for device
The need for this is the following:
"ipv4.dhcp-client-id" can be specified via global connection defaults.
In absence of any configuration in NetworkManager, the default depends
on the DHCP client plugin. In case of "dhclient", the default further
depends on /etc/dhcp.
For "internal" plugin, we may very well want to change the default
client-id to "mac" by universally installing a configuration
snippet
[connection-use-mac-client-id]
ipv4.dhcp-client-id=mac
However, if we the user happens to enable "dhclient" plugin, this also
forces the client-id and overrules configuration from /etc/dhcp. The real
problem is, that dhclient can be configured via means outside of NetworkManager,
so our defaults shall not overwrite defaults from /etc/dhcp.
With the new device spec, we can avoid this issue:
[connection-dhcp-client-id]
match-device=except:dhcp-plugin:dhclient
ipv4.dhcp-client-id=mac
This will be part of the solution for rh#1640494. Note that merely
dropping a configuration snippet is not yet enough. More fixes for
DHCP will follow. Also, bug rh#1640494 may have alternative solutions
as well. The nice part of this new feature is that it is generally
useful for configuring connection defaults and not specifically for
the client-id issue.
Note that this match spec is per-device, although the plugin is selected
globally. That makes some sense, because in the future we may or may not
configure the DHCP plugin per-device or per address family.
https://bugzilla.redhat.com/show_bug.cgi?id=1640494
2018-10-24 08:43:45 +02:00
|
|
|
const char *dhcp_plugin;
|
2017-01-21 15:26:53 +01:00
|
|
|
struct {
|
|
|
|
|
const char *value;
|
|
|
|
|
gboolean is_parsed;
|
|
|
|
|
guint len;
|
|
|
|
|
guint8 bin[NM_UTILS_HWADDR_LEN_MAX];
|
|
|
|
|
} hwaddr;
|
|
|
|
|
struct {
|
|
|
|
|
const char *value;
|
|
|
|
|
gboolean is_parsed;
|
|
|
|
|
guint32 a;
|
|
|
|
|
guint32 b;
|
|
|
|
|
guint32 c;
|
|
|
|
|
} s390_subchannels;
|
|
|
|
|
} MatchDeviceData;
|
|
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
static gboolean
|
2017-01-25 16:07:36 +01:00
|
|
|
match_device_s390_subchannels_parse (const char *s390_subchannels, guint32 *out_a, guint32 *out_b, guint32 *out_c)
|
2016-03-01 09:56:51 +01:00
|
|
|
{
|
2017-12-12 15:49:39 +01:00
|
|
|
char buf[30 + 1];
|
|
|
|
|
const int BUFSIZE = G_N_ELEMENTS (buf) - 1;
|
2017-01-25 16:07:36 +01:00
|
|
|
guint i = 0;
|
2016-03-01 09:56:51 +01:00
|
|
|
char *pa = NULL, *pb = NULL, *pc = NULL;
|
2017-01-25 16:07:36 +01:00
|
|
|
gint64 a, b, c;
|
2016-03-01 09:56:51 +01:00
|
|
|
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
nm_assert (s390_subchannels);
|
2017-01-25 16:07:36 +01:00
|
|
|
nm_assert (out_a);
|
|
|
|
|
nm_assert (out_b);
|
|
|
|
|
nm_assert (out_c);
|
|
|
|
|
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
if (!g_ascii_isxdigit (s390_subchannels[0]))
|
2016-03-01 09:56:51 +01:00
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
/* Get the first channel */
|
2017-01-25 16:07:36 +01:00
|
|
|
for (i = 0; s390_subchannels[i]; i++) {
|
|
|
|
|
char ch = s390_subchannels[i];
|
|
|
|
|
|
|
|
|
|
if (!g_ascii_isxdigit (ch) && ch != '.') {
|
|
|
|
|
if (ch == ',') {
|
|
|
|
|
/* FIXME: currently we consider the first channel and ignore
|
|
|
|
|
* everything after the first ',' separator. Maybe we should
|
|
|
|
|
* validate all present channels? */
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-03-01 09:56:51 +01:00
|
|
|
return FALSE; /* Invalid chars */
|
2017-01-25 16:07:36 +01:00
|
|
|
}
|
2016-03-01 09:56:51 +01:00
|
|
|
if (i >= BUFSIZE)
|
|
|
|
|
return FALSE; /* Too long to be a subchannel */
|
2017-01-25 16:07:36 +01:00
|
|
|
buf[i] = ch;
|
2016-03-01 09:56:51 +01:00
|
|
|
}
|
|
|
|
|
buf[i] = '\0';
|
|
|
|
|
|
|
|
|
|
/* and grab each of its elements, there should be 3 */
|
|
|
|
|
pa = &buf[0];
|
2017-01-25 16:07:36 +01:00
|
|
|
pb = strchr (pa, '.');
|
2016-03-01 09:56:51 +01:00
|
|
|
if (pb)
|
|
|
|
|
pc = strchr (pb + 1, '.');
|
2017-01-25 16:07:36 +01:00
|
|
|
if (!pb || !pc)
|
2016-03-01 09:56:51 +01:00
|
|
|
return FALSE;
|
|
|
|
|
*pb++ = '\0';
|
|
|
|
|
*pc++ = '\0';
|
|
|
|
|
|
2017-01-25 16:07:36 +01:00
|
|
|
a = _nm_utils_ascii_str_to_int64 (pa, 16, 0, G_MAXUINT32, -1);
|
|
|
|
|
if (a == -1)
|
2016-03-01 09:56:51 +01:00
|
|
|
return FALSE;
|
2017-01-25 16:07:36 +01:00
|
|
|
b = _nm_utils_ascii_str_to_int64 (pb, 16, 0, G_MAXUINT32, -1);
|
|
|
|
|
if (b == -1)
|
2016-03-01 09:56:51 +01:00
|
|
|
return FALSE;
|
2017-01-25 16:07:36 +01:00
|
|
|
c = _nm_utils_ascii_str_to_int64 (pc, 16, 0, G_MAXUINT32, -1);
|
|
|
|
|
if (c == -1)
|
2016-03-01 09:56:51 +01:00
|
|
|
return FALSE;
|
|
|
|
|
|
2017-01-25 16:07:36 +01:00
|
|
|
*out_a = (guint32) a;
|
|
|
|
|
*out_b = (guint32) b;
|
|
|
|
|
*out_c = (guint32) c;
|
2016-03-01 09:56:51 +01:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
static gboolean
|
2017-01-21 15:26:53 +01:00
|
|
|
match_data_s390_subchannels_eval (const char *spec_str,
|
|
|
|
|
MatchDeviceData *match_data)
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
{
|
2017-01-25 16:07:36 +01:00
|
|
|
guint32 a, b, c;
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
|
2017-01-21 15:26:53 +01:00
|
|
|
if (G_UNLIKELY (!match_data->s390_subchannels.is_parsed)) {
|
|
|
|
|
match_data->s390_subchannels.is_parsed = TRUE;
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
|
2017-01-21 15:26:53 +01:00
|
|
|
if ( !match_data->s390_subchannels.value
|
|
|
|
|
|| !match_device_s390_subchannels_parse (match_data->s390_subchannels.value,
|
|
|
|
|
&match_data->s390_subchannels.a,
|
|
|
|
|
&match_data->s390_subchannels.b,
|
|
|
|
|
&match_data->s390_subchannels.c)) {
|
|
|
|
|
match_data->s390_subchannels.value = NULL;
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
} else if (!match_data->s390_subchannels.value)
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
return FALSE;
|
2017-01-21 15:26:53 +01:00
|
|
|
|
|
|
|
|
if (!match_device_s390_subchannels_parse (spec_str, &a, &b, &c))
|
|
|
|
|
return FALSE;
|
|
|
|
|
return match_data->s390_subchannels.a == a
|
|
|
|
|
&& match_data->s390_subchannels.b == b
|
|
|
|
|
&& match_data->s390_subchannels.c == c;
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2017-01-21 15:26:53 +01:00
|
|
|
match_device_hwaddr_eval (const char *spec_str,
|
|
|
|
|
MatchDeviceData *match_data)
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
{
|
2017-01-21 15:26:53 +01:00
|
|
|
if (G_UNLIKELY (!match_data->hwaddr.is_parsed)) {
|
|
|
|
|
match_data->hwaddr.is_parsed = TRUE;
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
|
2017-01-21 15:26:53 +01:00
|
|
|
if (match_data->hwaddr.value) {
|
|
|
|
|
gsize l;
|
|
|
|
|
|
|
|
|
|
if (!_nm_utils_hwaddr_aton (match_data->hwaddr.value, match_data->hwaddr.bin, sizeof (match_data->hwaddr.bin), &l))
|
|
|
|
|
g_return_val_if_reached (FALSE);
|
|
|
|
|
match_data->hwaddr.len = l;
|
|
|
|
|
} else
|
|
|
|
|
return FALSE;
|
|
|
|
|
} else if (!match_data->hwaddr.len)
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
return FALSE;
|
2017-01-21 15:26:53 +01:00
|
|
|
|
|
|
|
|
return nm_utils_hwaddr_matches (spec_str, -1, match_data->hwaddr.bin, match_data->hwaddr.len);
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define _MATCH_CHECK(spec_str, tag) \
|
|
|
|
|
({ \
|
|
|
|
|
gboolean _has = FALSE; \
|
|
|
|
|
\
|
|
|
|
|
if (!g_ascii_strncasecmp (spec_str, (""tag""), NM_STRLEN (tag))) { \
|
|
|
|
|
spec_str += NM_STRLEN (tag); \
|
|
|
|
|
_has = TRUE; \
|
|
|
|
|
} \
|
|
|
|
|
_has; \
|
|
|
|
|
})
|
|
|
|
|
|
core: fix match spec behavior for a list of all "except:"
If the spec specifies only negative matches (and none of them matches),
then the result shall be positive.
Meaning:
[connection*] match-device=except:dhcp-plugin:dhclient
[connection*] match-device=except:interface-name:eth0
[.config] enabled=except:nm-version:1.14
should be the same as:
[connection*] match-device=*,except:dhcp-plugin:dhclient
[connection*] match-device=*,except:interface-name:eth0
[.config] enabled=*,except:nm-version:1.14
and match by default. Previously, such specs would never yield a
positive match, which seems wrong.
Note that "except:" already has a special meaning. It is not merely
"not:". That is because we don't support "and:" nor grouping, but all
matches are combined by an implicit "or:". With such a meaning, having
a "not:" would be unclear to define. Instead it is defined that any
"except:" match always wins and makes the entire condition to explicitly
not match. As such, it makes sense to treat a match that only consists
of "except:" matches special.
This is a change in behavior, but the alternative meaning makes
little sense.
2018-12-11 12:35:40 +01:00
|
|
|
static NMMatchSpecMatchType
|
|
|
|
|
_match_result (gboolean has_except,
|
|
|
|
|
gboolean has_not_except,
|
|
|
|
|
gboolean has_match,
|
|
|
|
|
gboolean has_match_except)
|
|
|
|
|
{
|
|
|
|
|
if ( has_except
|
|
|
|
|
&& !has_not_except) {
|
|
|
|
|
/* a match spec that only consists of a list of except matches is treated specially. */
|
|
|
|
|
nm_assert (!has_match);
|
|
|
|
|
if (has_match_except) {
|
|
|
|
|
/* one of the "except:" matches matched. The result is an explicit
|
|
|
|
|
* negative match. */
|
|
|
|
|
return NM_MATCH_SPEC_NEG_MATCH;
|
|
|
|
|
} else {
|
|
|
|
|
/* none of the "except:" matches matched. The result is a positive match,
|
|
|
|
|
* despite there being no positive match. */
|
|
|
|
|
return NM_MATCH_SPEC_MATCH;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (has_match_except)
|
|
|
|
|
return NM_MATCH_SPEC_NEG_MATCH;
|
|
|
|
|
if (has_match)
|
|
|
|
|
return NM_MATCH_SPEC_MATCH;
|
|
|
|
|
return NM_MATCH_SPEC_NO_MATCH;
|
|
|
|
|
}
|
|
|
|
|
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
static const char *
|
|
|
|
|
match_except (const char *spec_str, gboolean *out_except)
|
|
|
|
|
{
|
|
|
|
|
if (_MATCH_CHECK (spec_str, EXCEPT_TAG))
|
|
|
|
|
*out_except = TRUE;
|
|
|
|
|
else
|
|
|
|
|
*out_except = FALSE;
|
|
|
|
|
return spec_str;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
match_device_eval (const char *spec_str,
|
|
|
|
|
gboolean allow_fuzzy,
|
2017-01-21 15:26:53 +01:00
|
|
|
MatchDeviceData *match_data)
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
{
|
2017-01-23 09:35:50 +01:00
|
|
|
if (spec_str[0] == '*' && spec_str[1] == '\0')
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
if (_MATCH_CHECK (spec_str, DEVICE_TYPE_TAG)) {
|
2017-01-21 15:26:53 +01:00
|
|
|
return match_data->device_type
|
|
|
|
|
&& nm_streq (spec_str, match_data->device_type);
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
}
|
|
|
|
|
|
2019-07-18 15:37:55 +02:00
|
|
|
if (_MATCH_CHECK (spec_str, NM_MATCH_SPEC_MAC_TAG))
|
2017-01-21 15:26:53 +01:00
|
|
|
return match_device_hwaddr_eval (spec_str, match_data);
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
|
2019-07-18 15:37:55 +02:00
|
|
|
if (_MATCH_CHECK (spec_str, NM_MATCH_SPEC_INTERFACE_NAME_TAG)) {
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
gboolean use_pattern = FALSE;
|
|
|
|
|
|
|
|
|
|
if (spec_str[0] == '=')
|
|
|
|
|
spec_str += 1;
|
|
|
|
|
else {
|
|
|
|
|
if (spec_str[0] == '~')
|
|
|
|
|
spec_str += 1;
|
|
|
|
|
use_pattern = TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-21 15:26:53 +01:00
|
|
|
if (match_data->interface_name) {
|
|
|
|
|
if (nm_streq (spec_str, match_data->interface_name))
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
return TRUE;
|
2017-01-21 15:26:53 +01:00
|
|
|
if (use_pattern && g_pattern_match_simple (spec_str, match_data->interface_name))
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-17 16:18:48 +01:00
|
|
|
if (_MATCH_CHECK (spec_str, DRIVER_TAG)) {
|
|
|
|
|
const char *t;
|
|
|
|
|
|
|
|
|
|
if (!match_data->driver)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
/* support:
|
|
|
|
|
* 1) "${DRIVER}"
|
|
|
|
|
* In this case, DRIVER may not contain a '/' character.
|
|
|
|
|
* It matches any driver version.
|
|
|
|
|
* 2) "${DRIVER}/${DRIVER_VERSION}"
|
|
|
|
|
* In this case, DRIVER may contains '/' but DRIVER_VERSION
|
|
|
|
|
* may not. A '/' in DRIVER_VERSION may be replaced by '?'.
|
|
|
|
|
*
|
|
|
|
|
* It follows, that "${DRIVER}/""*" is like 1), but allows
|
|
|
|
|
* '/' inside DRIVER.
|
|
|
|
|
*
|
|
|
|
|
* The fields match to what `nmcli -f GENERAL.DRIVER,GENERAL.DRIVER-VERSION device show`
|
|
|
|
|
* gives. However, DRIVER matches literally, while DRIVER_VERSION is a glob
|
|
|
|
|
* supporting ? and *.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
t = strrchr (spec_str, '/');
|
|
|
|
|
|
|
|
|
|
if (!t)
|
|
|
|
|
return nm_streq (spec_str, match_data->driver);
|
|
|
|
|
|
|
|
|
|
return (strncmp (spec_str, match_data->driver, t - spec_str) == 0)
|
|
|
|
|
&& g_pattern_match_simple (&t[1],
|
|
|
|
|
match_data->driver_version ?: "");
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-18 15:37:55 +02:00
|
|
|
if (_MATCH_CHECK (spec_str, NM_MATCH_SPEC_S390_SUBCHANNELS_TAG))
|
2017-01-21 15:26:53 +01:00
|
|
|
return match_data_s390_subchannels_eval (spec_str, match_data);
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
|
device: add "dhcp-plugin" match spec for device
The need for this is the following:
"ipv4.dhcp-client-id" can be specified via global connection defaults.
In absence of any configuration in NetworkManager, the default depends
on the DHCP client plugin. In case of "dhclient", the default further
depends on /etc/dhcp.
For "internal" plugin, we may very well want to change the default
client-id to "mac" by universally installing a configuration
snippet
[connection-use-mac-client-id]
ipv4.dhcp-client-id=mac
However, if we the user happens to enable "dhclient" plugin, this also
forces the client-id and overrules configuration from /etc/dhcp. The real
problem is, that dhclient can be configured via means outside of NetworkManager,
so our defaults shall not overwrite defaults from /etc/dhcp.
With the new device spec, we can avoid this issue:
[connection-dhcp-client-id]
match-device=except:dhcp-plugin:dhclient
ipv4.dhcp-client-id=mac
This will be part of the solution for rh#1640494. Note that merely
dropping a configuration snippet is not yet enough. More fixes for
DHCP will follow. Also, bug rh#1640494 may have alternative solutions
as well. The nice part of this new feature is that it is generally
useful for configuring connection defaults and not specifically for
the client-id issue.
Note that this match spec is per-device, although the plugin is selected
globally. That makes some sense, because in the future we may or may not
configure the DHCP plugin per-device or per address family.
https://bugzilla.redhat.com/show_bug.cgi?id=1640494
2018-10-24 08:43:45 +02:00
|
|
|
if (_MATCH_CHECK (spec_str, DHCP_PLUGIN_TAG))
|
|
|
|
|
return nm_streq0 (spec_str, match_data->dhcp_plugin);
|
|
|
|
|
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
if (allow_fuzzy) {
|
2017-01-21 15:26:53 +01:00
|
|
|
if (match_device_hwaddr_eval (spec_str, match_data))
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
return TRUE;
|
2017-01-21 15:26:53 +01:00
|
|
|
if ( match_data->interface_name
|
|
|
|
|
&& nm_streq (spec_str, match_data->interface_name))
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
NMMatchSpecMatchType
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
nm_match_spec_device (const GSList *specs,
|
|
|
|
|
const char *interface_name,
|
|
|
|
|
const char *device_type,
|
2017-03-17 16:18:48 +01:00
|
|
|
const char *driver,
|
|
|
|
|
const char *driver_version,
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
const char *hwaddr,
|
device: add "dhcp-plugin" match spec for device
The need for this is the following:
"ipv4.dhcp-client-id" can be specified via global connection defaults.
In absence of any configuration in NetworkManager, the default depends
on the DHCP client plugin. In case of "dhclient", the default further
depends on /etc/dhcp.
For "internal" plugin, we may very well want to change the default
client-id to "mac" by universally installing a configuration
snippet
[connection-use-mac-client-id]
ipv4.dhcp-client-id=mac
However, if we the user happens to enable "dhclient" plugin, this also
forces the client-id and overrules configuration from /etc/dhcp. The real
problem is, that dhclient can be configured via means outside of NetworkManager,
so our defaults shall not overwrite defaults from /etc/dhcp.
With the new device spec, we can avoid this issue:
[connection-dhcp-client-id]
match-device=except:dhcp-plugin:dhclient
ipv4.dhcp-client-id=mac
This will be part of the solution for rh#1640494. Note that merely
dropping a configuration snippet is not yet enough. More fixes for
DHCP will follow. Also, bug rh#1640494 may have alternative solutions
as well. The nice part of this new feature is that it is generally
useful for configuring connection defaults and not specifically for
the client-id issue.
Note that this match spec is per-device, although the plugin is selected
globally. That makes some sense, because in the future we may or may not
configure the DHCP plugin per-device or per address family.
https://bugzilla.redhat.com/show_bug.cgi?id=1640494
2018-10-24 08:43:45 +02:00
|
|
|
const char *s390_subchannels,
|
|
|
|
|
const char *dhcp_plugin)
|
2016-03-01 09:56:51 +01:00
|
|
|
{
|
|
|
|
|
const GSList *iter;
|
core: fix match spec behavior for a list of all "except:"
If the spec specifies only negative matches (and none of them matches),
then the result shall be positive.
Meaning:
[connection*] match-device=except:dhcp-plugin:dhclient
[connection*] match-device=except:interface-name:eth0
[.config] enabled=except:nm-version:1.14
should be the same as:
[connection*] match-device=*,except:dhcp-plugin:dhclient
[connection*] match-device=*,except:interface-name:eth0
[.config] enabled=*,except:nm-version:1.14
and match by default. Previously, such specs would never yield a
positive match, which seems wrong.
Note that "except:" already has a special meaning. It is not merely
"not:". That is because we don't support "and:" nor grouping, but all
matches are combined by an implicit "or:". With such a meaning, having
a "not:" would be unclear to define. Instead it is defined that any
"except:" match always wins and makes the entire condition to explicitly
not match. As such, it makes sense to treat a match that only consists
of "except:" matches special.
This is a change in behavior, but the alternative meaning makes
little sense.
2018-12-11 12:35:40 +01:00
|
|
|
gboolean has_match = FALSE;
|
|
|
|
|
gboolean has_match_except = FALSE;
|
|
|
|
|
gboolean has_except = FALSE;
|
|
|
|
|
gboolean has_not_except = FALSE;
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
const char *spec_str;
|
2017-01-21 15:26:53 +01:00
|
|
|
MatchDeviceData match_data = {
|
|
|
|
|
.interface_name = interface_name,
|
|
|
|
|
.device_type = nm_str_not_empty (device_type),
|
2017-03-17 16:18:48 +01:00
|
|
|
.driver = nm_str_not_empty (driver),
|
|
|
|
|
.driver_version = nm_str_not_empty (driver_version),
|
device: add "dhcp-plugin" match spec for device
The need for this is the following:
"ipv4.dhcp-client-id" can be specified via global connection defaults.
In absence of any configuration in NetworkManager, the default depends
on the DHCP client plugin. In case of "dhclient", the default further
depends on /etc/dhcp.
For "internal" plugin, we may very well want to change the default
client-id to "mac" by universally installing a configuration
snippet
[connection-use-mac-client-id]
ipv4.dhcp-client-id=mac
However, if we the user happens to enable "dhclient" plugin, this also
forces the client-id and overrules configuration from /etc/dhcp. The real
problem is, that dhclient can be configured via means outside of NetworkManager,
so our defaults shall not overwrite defaults from /etc/dhcp.
With the new device spec, we can avoid this issue:
[connection-dhcp-client-id]
match-device=except:dhcp-plugin:dhclient
ipv4.dhcp-client-id=mac
This will be part of the solution for rh#1640494. Note that merely
dropping a configuration snippet is not yet enough. More fixes for
DHCP will follow. Also, bug rh#1640494 may have alternative solutions
as well. The nice part of this new feature is that it is generally
useful for configuring connection defaults and not specifically for
the client-id issue.
Note that this match spec is per-device, although the plugin is selected
globally. That makes some sense, because in the future we may or may not
configure the DHCP plugin per-device or per address family.
https://bugzilla.redhat.com/show_bug.cgi?id=1640494
2018-10-24 08:43:45 +02:00
|
|
|
.dhcp_plugin = nm_str_not_empty (dhcp_plugin),
|
2017-01-21 15:26:53 +01:00
|
|
|
.hwaddr = {
|
|
|
|
|
.value = hwaddr,
|
|
|
|
|
},
|
|
|
|
|
.s390_subchannels = {
|
|
|
|
|
.value = s390_subchannels,
|
|
|
|
|
},
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
};
|
2016-03-01 09:56:51 +01:00
|
|
|
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
nm_assert (!hwaddr || nm_utils_hwaddr_valid (hwaddr, -1));
|
2016-03-01 09:56:51 +01:00
|
|
|
|
|
|
|
|
if (!specs)
|
|
|
|
|
return NM_MATCH_SPEC_NO_MATCH;
|
|
|
|
|
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
for (iter = specs; iter; iter = iter->next) {
|
core: fix match spec behavior for a list of all "except:"
If the spec specifies only negative matches (and none of them matches),
then the result shall be positive.
Meaning:
[connection*] match-device=except:dhcp-plugin:dhclient
[connection*] match-device=except:interface-name:eth0
[.config] enabled=except:nm-version:1.14
should be the same as:
[connection*] match-device=*,except:dhcp-plugin:dhclient
[connection*] match-device=*,except:interface-name:eth0
[.config] enabled=*,except:nm-version:1.14
and match by default. Previously, such specs would never yield a
positive match, which seems wrong.
Note that "except:" already has a special meaning. It is not merely
"not:". That is because we don't support "and:" nor grouping, but all
matches are combined by an implicit "or:". With such a meaning, having
a "not:" would be unclear to define. Instead it is defined that any
"except:" match always wins and makes the entire condition to explicitly
not match. As such, it makes sense to treat a match that only consists
of "except:" matches special.
This is a change in behavior, but the alternative meaning makes
little sense.
2018-12-11 12:35:40 +01:00
|
|
|
gboolean except;
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
|
|
|
|
|
spec_str = iter->data;
|
2016-03-01 09:56:51 +01:00
|
|
|
|
|
|
|
|
if (!spec_str || !*spec_str)
|
|
|
|
|
continue;
|
|
|
|
|
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
spec_str = match_except (spec_str, &except);
|
2016-03-01 09:56:51 +01:00
|
|
|
|
core: fix match spec behavior for a list of all "except:"
If the spec specifies only negative matches (and none of them matches),
then the result shall be positive.
Meaning:
[connection*] match-device=except:dhcp-plugin:dhclient
[connection*] match-device=except:interface-name:eth0
[.config] enabled=except:nm-version:1.14
should be the same as:
[connection*] match-device=*,except:dhcp-plugin:dhclient
[connection*] match-device=*,except:interface-name:eth0
[.config] enabled=*,except:nm-version:1.14
and match by default. Previously, such specs would never yield a
positive match, which seems wrong.
Note that "except:" already has a special meaning. It is not merely
"not:". That is because we don't support "and:" nor grouping, but all
matches are combined by an implicit "or:". With such a meaning, having
a "not:" would be unclear to define. Instead it is defined that any
"except:" match always wins and makes the entire condition to explicitly
not match. As such, it makes sense to treat a match that only consists
of "except:" matches special.
This is a change in behavior, but the alternative meaning makes
little sense.
2018-12-11 12:35:40 +01:00
|
|
|
if (except)
|
|
|
|
|
has_except = TRUE;
|
|
|
|
|
else
|
|
|
|
|
has_not_except = TRUE;
|
|
|
|
|
|
|
|
|
|
if ( ( except && has_match_except)
|
|
|
|
|
|| (!except && has_match)) {
|
|
|
|
|
/* evaluating the match does not give new information. Skip it. */
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
continue;
|
2016-03-01 09:56:51 +01:00
|
|
|
}
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
|
|
|
|
|
if (!match_device_eval (spec_str,
|
|
|
|
|
!except,
|
2017-01-21 15:26:53 +01:00
|
|
|
&match_data))
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (except)
|
core: fix match spec behavior for a list of all "except:"
If the spec specifies only negative matches (and none of them matches),
then the result shall be positive.
Meaning:
[connection*] match-device=except:dhcp-plugin:dhclient
[connection*] match-device=except:interface-name:eth0
[.config] enabled=except:nm-version:1.14
should be the same as:
[connection*] match-device=*,except:dhcp-plugin:dhclient
[connection*] match-device=*,except:interface-name:eth0
[.config] enabled=*,except:nm-version:1.14
and match by default. Previously, such specs would never yield a
positive match, which seems wrong.
Note that "except:" already has a special meaning. It is not merely
"not:". That is because we don't support "and:" nor grouping, but all
matches are combined by an implicit "or:". With such a meaning, having
a "not:" would be unclear to define. Instead it is defined that any
"except:" match always wins and makes the entire condition to explicitly
not match. As such, it makes sense to treat a match that only consists
of "except:" matches special.
This is a change in behavior, but the alternative meaning makes
little sense.
2018-12-11 12:35:40 +01:00
|
|
|
has_match_except = TRUE;
|
|
|
|
|
else
|
|
|
|
|
has_match = TRUE;
|
2016-03-01 09:56:51 +01:00
|
|
|
}
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
|
core: fix match spec behavior for a list of all "except:"
If the spec specifies only negative matches (and none of them matches),
then the result shall be positive.
Meaning:
[connection*] match-device=except:dhcp-plugin:dhclient
[connection*] match-device=except:interface-name:eth0
[.config] enabled=except:nm-version:1.14
should be the same as:
[connection*] match-device=*,except:dhcp-plugin:dhclient
[connection*] match-device=*,except:interface-name:eth0
[.config] enabled=*,except:nm-version:1.14
and match by default. Previously, such specs would never yield a
positive match, which seems wrong.
Note that "except:" already has a special meaning. It is not merely
"not:". That is because we don't support "and:" nor grouping, but all
matches are combined by an implicit "or:". With such a meaning, having
a "not:" would be unclear to define. Instead it is defined that any
"except:" match always wins and makes the entire condition to explicitly
not match. As such, it makes sense to treat a match that only consists
of "except:" matches special.
This is a change in behavior, but the alternative meaning makes
little sense.
2018-12-11 12:35:40 +01:00
|
|
|
return _match_result (has_except, has_not_except, has_match, has_match_except);
|
2016-03-01 09:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
match_config_eval (const char *str, const char *tag, guint cur_nm_version)
|
2016-03-01 09:56:51 +01:00
|
|
|
{
|
|
|
|
|
gs_free char *s_ver = NULL;
|
|
|
|
|
gs_strfreev char **s_ver_tokens = NULL;
|
all: don't use gchar/gshort/gint/glong but C types
We commonly don't use the glib typedefs for char/short/int/long,
but their C types directly.
$ git grep '\<g\(char\|short\|int\|long\|float\|double\)\>' | wc -l
587
$ git grep '\<\(char\|short\|int\|long\|float\|double\)\>' | wc -l
21114
One could argue that using the glib typedefs is preferable in
public API (of our glib based libnm library) or where it clearly
is related to glib, like during
g_object_set (obj, PROPERTY, (gint) value, NULL);
However, that argument does not seem strong, because in practice we don't
follow that argument today, and seldomly use the glib typedefs.
Also, the style guide for this would be hard to formalize, because
"using them where clearly related to a glib" is a very loose suggestion.
Also note that glib typedefs will always just be typedefs of the
underlying C types. There is no danger of glib changing the meaning
of these typedefs (because that would be a major API break of glib).
A simple style guide is instead: don't use these typedefs.
No manual actions, I only ran the bash script:
FILES=($(git ls-files '*.[hc]'))
sed -i \
-e 's/\<g\(char\|short\|int\|long\|float\|double\)\>\( [^ ]\)/\1\2/g' \
-e 's/\<g\(char\|short\|int\|long\|float\|double\)\> /\1 /g' \
-e 's/\<g\(char\|short\|int\|long\|float\|double\)\>/\1/g' \
"${FILES[@]}"
2018-07-11 07:40:19 +02:00
|
|
|
int v_maj = -1, v_min = -1, v_mic = -1;
|
2016-03-01 09:56:51 +01:00
|
|
|
guint c_maj = -1, c_min = -1, c_mic = -1;
|
|
|
|
|
guint n_tokens;
|
|
|
|
|
|
|
|
|
|
s_ver = g_strdup (str);
|
|
|
|
|
g_strstrip (s_ver);
|
|
|
|
|
|
|
|
|
|
/* Let's be strict with the accepted format here. No funny stuff!! */
|
|
|
|
|
|
|
|
|
|
if (s_ver[strspn (s_ver, ".0123456789")] != '\0')
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
s_ver_tokens = g_strsplit (s_ver, ".", -1);
|
|
|
|
|
n_tokens = g_strv_length (s_ver_tokens);
|
|
|
|
|
if (n_tokens == 0 || n_tokens > 3)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
v_maj = _nm_utils_ascii_str_to_int64 (s_ver_tokens[0], 10, 0, 0xFFFF, -1);
|
|
|
|
|
if (v_maj < 0)
|
|
|
|
|
return FALSE;
|
|
|
|
|
if (n_tokens >= 2) {
|
|
|
|
|
v_min = _nm_utils_ascii_str_to_int64 (s_ver_tokens[1], 10, 0, 0xFF, -1);
|
|
|
|
|
if (v_min < 0)
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
if (n_tokens >= 3) {
|
|
|
|
|
v_mic = _nm_utils_ascii_str_to_int64 (s_ver_tokens[2], 10, 0, 0xFF, -1);
|
|
|
|
|
if (v_mic < 0)
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nm_decode_version (cur_nm_version, &c_maj, &c_min, &c_mic);
|
|
|
|
|
|
|
|
|
|
#define CHECK_AND_RETURN_FALSE(cur, val, tag, is_last_digit) \
|
|
|
|
|
G_STMT_START { \
|
|
|
|
|
if (!strcmp (tag, MATCH_TAG_CONFIG_NM_VERSION_MIN)) { \
|
|
|
|
|
if (cur < val) \
|
|
|
|
|
return FALSE; \
|
|
|
|
|
} else if (!strcmp (tag, MATCH_TAG_CONFIG_NM_VERSION_MAX)) { \
|
|
|
|
|
if (cur > val) \
|
|
|
|
|
return FALSE; \
|
|
|
|
|
} else { \
|
|
|
|
|
if (cur != val) \
|
|
|
|
|
return FALSE; \
|
|
|
|
|
} \
|
|
|
|
|
if (!(is_last_digit)) { \
|
|
|
|
|
if (cur != val) \
|
|
|
|
|
return FALSE; \
|
|
|
|
|
} \
|
|
|
|
|
} G_STMT_END
|
|
|
|
|
if (v_mic >= 0)
|
|
|
|
|
CHECK_AND_RETURN_FALSE (c_mic, v_mic, tag, TRUE);
|
|
|
|
|
if (v_min >= 0)
|
|
|
|
|
CHECK_AND_RETURN_FALSE (c_min, v_min, tag, v_mic < 0);
|
|
|
|
|
CHECK_AND_RETURN_FALSE (c_maj, v_maj, tag, v_min < 0);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NMMatchSpecMatchType
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
nm_match_spec_config (const GSList *specs, guint cur_nm_version, const char *env)
|
2016-03-01 09:56:51 +01:00
|
|
|
{
|
|
|
|
|
const GSList *iter;
|
core: fix match spec behavior for a list of all "except:"
If the spec specifies only negative matches (and none of them matches),
then the result shall be positive.
Meaning:
[connection*] match-device=except:dhcp-plugin:dhclient
[connection*] match-device=except:interface-name:eth0
[.config] enabled=except:nm-version:1.14
should be the same as:
[connection*] match-device=*,except:dhcp-plugin:dhclient
[connection*] match-device=*,except:interface-name:eth0
[.config] enabled=*,except:nm-version:1.14
and match by default. Previously, such specs would never yield a
positive match, which seems wrong.
Note that "except:" already has a special meaning. It is not merely
"not:". That is because we don't support "and:" nor grouping, but all
matches are combined by an implicit "or:". With such a meaning, having
a "not:" would be unclear to define. Instead it is defined that any
"except:" match always wins and makes the entire condition to explicitly
not match. As such, it makes sense to treat a match that only consists
of "except:" matches special.
This is a change in behavior, but the alternative meaning makes
little sense.
2018-12-11 12:35:40 +01:00
|
|
|
gboolean has_match = FALSE;
|
|
|
|
|
gboolean has_match_except = FALSE;
|
|
|
|
|
gboolean has_except = FALSE;
|
|
|
|
|
gboolean has_not_except = FALSE;
|
2016-03-01 09:56:51 +01:00
|
|
|
|
|
|
|
|
if (!specs)
|
|
|
|
|
return NM_MATCH_SPEC_NO_MATCH;
|
|
|
|
|
|
|
|
|
|
for (iter = specs; iter; iter = g_slist_next (iter)) {
|
|
|
|
|
const char *spec_str = iter->data;
|
|
|
|
|
gboolean except;
|
|
|
|
|
gboolean v_match;
|
|
|
|
|
|
|
|
|
|
if (!spec_str || !*spec_str)
|
|
|
|
|
continue;
|
|
|
|
|
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
spec_str = match_except (spec_str, &except);
|
2016-03-01 09:56:51 +01:00
|
|
|
|
core: fix match spec behavior for a list of all "except:"
If the spec specifies only negative matches (and none of them matches),
then the result shall be positive.
Meaning:
[connection*] match-device=except:dhcp-plugin:dhclient
[connection*] match-device=except:interface-name:eth0
[.config] enabled=except:nm-version:1.14
should be the same as:
[connection*] match-device=*,except:dhcp-plugin:dhclient
[connection*] match-device=*,except:interface-name:eth0
[.config] enabled=*,except:nm-version:1.14
and match by default. Previously, such specs would never yield a
positive match, which seems wrong.
Note that "except:" already has a special meaning. It is not merely
"not:". That is because we don't support "and:" nor grouping, but all
matches are combined by an implicit "or:". With such a meaning, having
a "not:" would be unclear to define. Instead it is defined that any
"except:" match always wins and makes the entire condition to explicitly
not match. As such, it makes sense to treat a match that only consists
of "except:" matches special.
This is a change in behavior, but the alternative meaning makes
little sense.
2018-12-11 12:35:40 +01:00
|
|
|
if (except)
|
|
|
|
|
has_except = TRUE;
|
|
|
|
|
else
|
|
|
|
|
has_not_except = TRUE;
|
|
|
|
|
|
|
|
|
|
if ( ( except && has_match_except)
|
|
|
|
|
|| (!except && has_match)) {
|
|
|
|
|
/* evaluating the match does not give new information. Skip it. */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
if (_MATCH_CHECK (spec_str, MATCH_TAG_CONFIG_NM_VERSION))
|
|
|
|
|
v_match = match_config_eval (spec_str, MATCH_TAG_CONFIG_NM_VERSION, cur_nm_version);
|
|
|
|
|
else if (_MATCH_CHECK (spec_str, MATCH_TAG_CONFIG_NM_VERSION_MIN))
|
|
|
|
|
v_match = match_config_eval (spec_str, MATCH_TAG_CONFIG_NM_VERSION_MIN, cur_nm_version);
|
|
|
|
|
else if (_MATCH_CHECK (spec_str, MATCH_TAG_CONFIG_NM_VERSION_MAX))
|
|
|
|
|
v_match = match_config_eval (spec_str, MATCH_TAG_CONFIG_NM_VERSION_MAX, cur_nm_version);
|
|
|
|
|
else if (_MATCH_CHECK (spec_str, MATCH_TAG_CONFIG_ENV))
|
2016-03-01 09:56:51 +01:00
|
|
|
v_match = env && env[0] && !strcmp (spec_str, env);
|
|
|
|
|
else
|
core: fix match spec behavior for a list of all "except:"
If the spec specifies only negative matches (and none of them matches),
then the result shall be positive.
Meaning:
[connection*] match-device=except:dhcp-plugin:dhclient
[connection*] match-device=except:interface-name:eth0
[.config] enabled=except:nm-version:1.14
should be the same as:
[connection*] match-device=*,except:dhcp-plugin:dhclient
[connection*] match-device=*,except:interface-name:eth0
[.config] enabled=*,except:nm-version:1.14
and match by default. Previously, such specs would never yield a
positive match, which seems wrong.
Note that "except:" already has a special meaning. It is not merely
"not:". That is because we don't support "and:" nor grouping, but all
matches are combined by an implicit "or:". With such a meaning, having
a "not:" would be unclear to define. Instead it is defined that any
"except:" match always wins and makes the entire condition to explicitly
not match. As such, it makes sense to treat a match that only consists
of "except:" matches special.
This is a change in behavior, but the alternative meaning makes
little sense.
2018-12-11 12:35:40 +01:00
|
|
|
v_match = FALSE;
|
|
|
|
|
|
|
|
|
|
if (!v_match)
|
2016-03-01 09:56:51 +01:00
|
|
|
continue;
|
|
|
|
|
|
core: fix match spec behavior for a list of all "except:"
If the spec specifies only negative matches (and none of them matches),
then the result shall be positive.
Meaning:
[connection*] match-device=except:dhcp-plugin:dhclient
[connection*] match-device=except:interface-name:eth0
[.config] enabled=except:nm-version:1.14
should be the same as:
[connection*] match-device=*,except:dhcp-plugin:dhclient
[connection*] match-device=*,except:interface-name:eth0
[.config] enabled=*,except:nm-version:1.14
and match by default. Previously, such specs would never yield a
positive match, which seems wrong.
Note that "except:" already has a special meaning. It is not merely
"not:". That is because we don't support "and:" nor grouping, but all
matches are combined by an implicit "or:". With such a meaning, having
a "not:" would be unclear to define. Instead it is defined that any
"except:" match always wins and makes the entire condition to explicitly
not match. As such, it makes sense to treat a match that only consists
of "except:" matches special.
This is a change in behavior, but the alternative meaning makes
little sense.
2018-12-11 12:35:40 +01:00
|
|
|
if (except)
|
|
|
|
|
has_match_except = TRUE;
|
|
|
|
|
else
|
|
|
|
|
has_match = TRUE;
|
2016-03-01 09:56:51 +01:00
|
|
|
}
|
core: fix match spec behavior for a list of all "except:"
If the spec specifies only negative matches (and none of them matches),
then the result shall be positive.
Meaning:
[connection*] match-device=except:dhcp-plugin:dhclient
[connection*] match-device=except:interface-name:eth0
[.config] enabled=except:nm-version:1.14
should be the same as:
[connection*] match-device=*,except:dhcp-plugin:dhclient
[connection*] match-device=*,except:interface-name:eth0
[.config] enabled=*,except:nm-version:1.14
and match by default. Previously, such specs would never yield a
positive match, which seems wrong.
Note that "except:" already has a special meaning. It is not merely
"not:". That is because we don't support "and:" nor grouping, but all
matches are combined by an implicit "or:". With such a meaning, having
a "not:" would be unclear to define. Instead it is defined that any
"except:" match always wins and makes the entire condition to explicitly
not match. As such, it makes sense to treat a match that only consists
of "except:" matches special.
This is a change in behavior, but the alternative meaning makes
little sense.
2018-12-11 12:35:40 +01:00
|
|
|
|
|
|
|
|
return _match_result (has_except, has_not_except, has_match, has_match_except);
|
2016-03-01 09:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
core: refactor evaluation of device's match-spec
Previously, we would have different functions like
- nm_match_spec_device_type()
- nm_match_spec_hwaddr()
- nm_match_spec_s390_subchannels()
- nm_match_spec_interface_name()
which all would handle one type of match-spec.
So, to get the overall result whether the arguments
match or not, nm_device_spec_match_list() had to stich
them together and iterate the list multiple times.
Refactor the code to have one nm_match_spec_device()
function that gets all relevant paramters.
The upside is:
- the logic how to evaluate the match-spec is all at one place
(match_device_eval()) instead of spread over multiple
functions.
- It requires iterating the list at most twice. Twice, because
we do a fast pre-search for "*".
One downside could be, that we have to pass all 4 arguments
for the evaluation, even if the might no be needed. That is,
because "nm-core-utils.c" shall be independend from NMDevice, it
cannot receive a device instance to get the parameters as needed.
As we would add new match-types, the argument list would grow.
However, all arguments are cached and fetching them from the
device's private data is very cheap.
(cherry picked from commit b957403efd53ff7d826ac7a4f80487032c03824b)
2017-01-20 19:50:25 +01:00
|
|
|
#undef _MATCH_CHECK
|
|
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
/**
|
|
|
|
|
* nm_match_spec_split:
|
|
|
|
|
* @value: the string of device specs
|
|
|
|
|
*
|
|
|
|
|
* Splits the specs from the string and returns them as individual
|
2017-05-05 14:11:05 +02:00
|
|
|
* entries in a #GSList.
|
2016-03-01 09:56:51 +01:00
|
|
|
*
|
|
|
|
|
* It does not validate any specs, it basically just does a special
|
|
|
|
|
* strsplit with ',' or ';' as separators and supporting '\\' as
|
|
|
|
|
* escape character.
|
|
|
|
|
*
|
|
|
|
|
* Leading and trailing spaces of each entry are removed. But the user
|
|
|
|
|
* can preserve them by specifying "\\s has 2 leading" or "has 2 trailing \\s".
|
|
|
|
|
*
|
|
|
|
|
* Specs can have a qualifier like "interface-name:". We still don't strip
|
|
|
|
|
* any whitespace after the colon, so "interface-name: X" matches an interface
|
|
|
|
|
* named " X".
|
|
|
|
|
*
|
|
|
|
|
* Returns: (transfer full): the list of device specs.
|
|
|
|
|
*/
|
|
|
|
|
GSList *
|
|
|
|
|
nm_match_spec_split (const char *value)
|
|
|
|
|
{
|
|
|
|
|
char *string_value, *p, *q0, *q;
|
|
|
|
|
GSList *pieces = NULL;
|
|
|
|
|
int trailing_ws;
|
|
|
|
|
|
|
|
|
|
if (!value || !*value)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
/* Copied from glibs g_key_file_parse_value_as_string() function
|
|
|
|
|
* and adjusted. */
|
|
|
|
|
|
all: don't use gchar/gshort/gint/glong but C types
We commonly don't use the glib typedefs for char/short/int/long,
but their C types directly.
$ git grep '\<g\(char\|short\|int\|long\|float\|double\)\>' | wc -l
587
$ git grep '\<\(char\|short\|int\|long\|float\|double\)\>' | wc -l
21114
One could argue that using the glib typedefs is preferable in
public API (of our glib based libnm library) or where it clearly
is related to glib, like during
g_object_set (obj, PROPERTY, (gint) value, NULL);
However, that argument does not seem strong, because in practice we don't
follow that argument today, and seldomly use the glib typedefs.
Also, the style guide for this would be hard to formalize, because
"using them where clearly related to a glib" is a very loose suggestion.
Also note that glib typedefs will always just be typedefs of the
underlying C types. There is no danger of glib changing the meaning
of these typedefs (because that would be a major API break of glib).
A simple style guide is instead: don't use these typedefs.
No manual actions, I only ran the bash script:
FILES=($(git ls-files '*.[hc]'))
sed -i \
-e 's/\<g\(char\|short\|int\|long\|float\|double\)\>\( [^ ]\)/\1\2/g' \
-e 's/\<g\(char\|short\|int\|long\|float\|double\)\> /\1 /g' \
-e 's/\<g\(char\|short\|int\|long\|float\|double\)\>/\1/g' \
"${FILES[@]}"
2018-07-11 07:40:19 +02:00
|
|
|
string_value = g_new (char, strlen (value) + 1);
|
2016-03-01 09:56:51 +01:00
|
|
|
|
all: don't use gchar/gshort/gint/glong but C types
We commonly don't use the glib typedefs for char/short/int/long,
but their C types directly.
$ git grep '\<g\(char\|short\|int\|long\|float\|double\)\>' | wc -l
587
$ git grep '\<\(char\|short\|int\|long\|float\|double\)\>' | wc -l
21114
One could argue that using the glib typedefs is preferable in
public API (of our glib based libnm library) or where it clearly
is related to glib, like during
g_object_set (obj, PROPERTY, (gint) value, NULL);
However, that argument does not seem strong, because in practice we don't
follow that argument today, and seldomly use the glib typedefs.
Also, the style guide for this would be hard to formalize, because
"using them where clearly related to a glib" is a very loose suggestion.
Also note that glib typedefs will always just be typedefs of the
underlying C types. There is no danger of glib changing the meaning
of these typedefs (because that would be a major API break of glib).
A simple style guide is instead: don't use these typedefs.
No manual actions, I only ran the bash script:
FILES=($(git ls-files '*.[hc]'))
sed -i \
-e 's/\<g\(char\|short\|int\|long\|float\|double\)\>\( [^ ]\)/\1\2/g' \
-e 's/\<g\(char\|short\|int\|long\|float\|double\)\> /\1 /g' \
-e 's/\<g\(char\|short\|int\|long\|float\|double\)\>/\1/g' \
"${FILES[@]}"
2018-07-11 07:40:19 +02:00
|
|
|
p = (char *) value;
|
2016-03-01 09:56:51 +01:00
|
|
|
|
|
|
|
|
/* skip over leading whitespace */
|
|
|
|
|
while (g_ascii_isspace (*p))
|
|
|
|
|
p++;
|
|
|
|
|
|
|
|
|
|
q0 = q = string_value;
|
|
|
|
|
trailing_ws = 0;
|
|
|
|
|
while (*p) {
|
|
|
|
|
if (*p == '\\') {
|
|
|
|
|
p++;
|
|
|
|
|
|
|
|
|
|
switch (*p) {
|
|
|
|
|
case 's':
|
|
|
|
|
*q = ' ';
|
|
|
|
|
break;
|
|
|
|
|
case 'n':
|
|
|
|
|
*q = '\n';
|
|
|
|
|
break;
|
|
|
|
|
case 't':
|
|
|
|
|
*q = '\t';
|
|
|
|
|
break;
|
|
|
|
|
case 'r':
|
|
|
|
|
*q = '\r';
|
|
|
|
|
break;
|
|
|
|
|
case '\\':
|
|
|
|
|
*q = '\\';
|
|
|
|
|
break;
|
|
|
|
|
case '\0':
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
if (NM_IN_SET (*p, ',', ';'))
|
|
|
|
|
*q = *p;
|
|
|
|
|
else {
|
|
|
|
|
*q++ = '\\';
|
|
|
|
|
*q = *p;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (*p == '\0')
|
|
|
|
|
break;
|
|
|
|
|
p++;
|
|
|
|
|
trailing_ws = 0;
|
|
|
|
|
} else {
|
|
|
|
|
*q = *p;
|
|
|
|
|
if (*p == '\0')
|
|
|
|
|
break;
|
|
|
|
|
if (g_ascii_isspace (*p)) {
|
|
|
|
|
trailing_ws++;
|
|
|
|
|
p++;
|
|
|
|
|
} else if (NM_IN_SET (*p, ',', ';')) {
|
|
|
|
|
if (q0 < q - trailing_ws)
|
|
|
|
|
pieces = g_slist_prepend (pieces, g_strndup (q0, (q - q0) - trailing_ws));
|
|
|
|
|
q0 = q + 1;
|
|
|
|
|
p++;
|
|
|
|
|
trailing_ws = 0;
|
|
|
|
|
while (g_ascii_isspace (*p))
|
|
|
|
|
p++;
|
|
|
|
|
} else
|
|
|
|
|
p++;
|
|
|
|
|
}
|
|
|
|
|
q++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*q = '\0';
|
|
|
|
|
if (q0 < q - trailing_ws)
|
|
|
|
|
pieces = g_slist_prepend (pieces, g_strndup (q0, (q - q0) - trailing_ws));
|
|
|
|
|
g_free (string_value);
|
|
|
|
|
return g_slist_reverse (pieces);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* nm_match_spec_join:
|
|
|
|
|
* @specs: the device specs to join
|
|
|
|
|
*
|
|
|
|
|
* This is based on g_key_file_parse_string_as_value(), analog to
|
|
|
|
|
* nm_match_spec_split() which is based on g_key_file_parse_value_as_string().
|
|
|
|
|
*
|
|
|
|
|
* Returns: (transfer full): a joined list of device specs that can be
|
|
|
|
|
* split again with nm_match_spec_split(). Note that
|
|
|
|
|
* nm_match_spec_split (nm_match_spec_join (specs)) yields the original
|
|
|
|
|
* result (which is not true the other way around because there are multiple
|
|
|
|
|
* ways to encode the same joined specs string).
|
|
|
|
|
*/
|
|
|
|
|
char *
|
|
|
|
|
nm_match_spec_join (GSList *specs)
|
|
|
|
|
{
|
|
|
|
|
const char *p;
|
|
|
|
|
GString *str;
|
|
|
|
|
|
|
|
|
|
str = g_string_new ("");
|
|
|
|
|
|
|
|
|
|
for (; specs; specs = specs->next) {
|
|
|
|
|
p = specs->data;
|
|
|
|
|
|
|
|
|
|
if (!p || !*p)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (str->len > 0)
|
|
|
|
|
g_string_append_c (str, ',');
|
|
|
|
|
|
|
|
|
|
/* escape leading whitespace */
|
|
|
|
|
switch (*p) {
|
|
|
|
|
case ' ':
|
|
|
|
|
g_string_append (str, "\\s");
|
|
|
|
|
p++;
|
|
|
|
|
break;
|
|
|
|
|
case '\t':
|
|
|
|
|
g_string_append (str, "\\t");
|
|
|
|
|
p++;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (; *p; p++) {
|
|
|
|
|
switch (*p) {
|
|
|
|
|
case '\n':
|
|
|
|
|
g_string_append (str, "\\n");
|
|
|
|
|
break;
|
|
|
|
|
case '\r':
|
|
|
|
|
g_string_append (str, "\\r");
|
|
|
|
|
break;
|
|
|
|
|
case '\\':
|
|
|
|
|
g_string_append (str, "\\\\");
|
|
|
|
|
break;
|
|
|
|
|
case ',':
|
|
|
|
|
g_string_append (str, "\\,");
|
|
|
|
|
break;
|
|
|
|
|
case ';':
|
|
|
|
|
g_string_append (str, "\\;");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
g_string_append_c (str, *p);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* escape trailing whitespaces */
|
|
|
|
|
switch (str->str[str->len - 1]) {
|
|
|
|
|
case ' ':
|
|
|
|
|
g_string_overwrite (str, str->len - 1, "\\s");
|
|
|
|
|
break;
|
|
|
|
|
case '\t':
|
|
|
|
|
g_string_overwrite (str, str->len - 1, "\\t");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return g_string_free (str, FALSE);
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-09 09:29:51 +02:00
|
|
|
gboolean
|
|
|
|
|
nm_wildcard_match_check (const char *str,
|
|
|
|
|
const char *const *patterns,
|
|
|
|
|
guint num_patterns)
|
|
|
|
|
{
|
2020-05-06 16:10:30 +02:00
|
|
|
gsize i, neg = 0;
|
2018-08-09 09:29:51 +02:00
|
|
|
|
|
|
|
|
for (i = 0; i < num_patterns; i++) {
|
|
|
|
|
if (patterns[i][0] == '!') {
|
|
|
|
|
neg++;
|
2020-05-06 16:10:30 +02:00
|
|
|
if (!str)
|
|
|
|
|
continue;
|
2018-08-09 09:29:51 +02:00
|
|
|
if (!fnmatch (patterns[i] + 1, str, 0))
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (neg == num_patterns)
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
2020-05-06 16:10:30 +02:00
|
|
|
if (str) {
|
|
|
|
|
for (i = 0; i < num_patterns; i++) {
|
|
|
|
|
if ( patterns[i][0] != '!'
|
|
|
|
|
&& !fnmatch (patterns[i], str, 0))
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
2018-08-09 09:29:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
nm_utils_new_vlan_name (const char *parent_iface, guint32 vlan_id)
|
|
|
|
|
{
|
|
|
|
|
guint id_len;
|
|
|
|
|
gsize parent_len;
|
|
|
|
|
char *ifname;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (parent_iface && *parent_iface, NULL);
|
|
|
|
|
|
|
|
|
|
if (vlan_id < 10)
|
|
|
|
|
id_len = 2;
|
|
|
|
|
else if (vlan_id < 100)
|
|
|
|
|
id_len = 3;
|
|
|
|
|
else if (vlan_id < 1000)
|
|
|
|
|
id_len = 4;
|
|
|
|
|
else {
|
|
|
|
|
g_return_val_if_fail (vlan_id < 4095, NULL);
|
|
|
|
|
id_len = 5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ifname = g_new (char, IFNAMSIZ);
|
|
|
|
|
|
|
|
|
|
parent_len = strlen (parent_iface);
|
|
|
|
|
parent_len = MIN (parent_len, IFNAMSIZ - 1 - id_len);
|
|
|
|
|
memcpy (ifname, parent_iface, parent_len);
|
|
|
|
|
g_snprintf (&ifname[parent_len], IFNAMSIZ - parent_len, ".%u", vlan_id);
|
|
|
|
|
|
|
|
|
|
return ifname;
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-20 10:45:31 +02:00
|
|
|
/* nm_utils_new_infiniband_name:
|
|
|
|
|
* @name: the output-buffer where the value will be written. Must be
|
|
|
|
|
* not %NULL and point to a string buffer of at least IFNAMSIZ bytes.
|
|
|
|
|
* @parent_name: the parent interface name
|
|
|
|
|
* @p_key: the partition key.
|
|
|
|
|
*
|
|
|
|
|
* Returns: the infiniband name will be written to @name and @name
|
|
|
|
|
* is returned.
|
|
|
|
|
*/
|
|
|
|
|
const char *
|
|
|
|
|
nm_utils_new_infiniband_name (char *name, const char *parent_name, int p_key)
|
|
|
|
|
{
|
|
|
|
|
g_return_val_if_fail (name, NULL);
|
|
|
|
|
g_return_val_if_fail (parent_name && parent_name[0], NULL);
|
|
|
|
|
g_return_val_if_fail (strlen (parent_name) < IFNAMSIZ, NULL);
|
|
|
|
|
|
|
|
|
|
/* technically, p_key of 0x0000 and 0x8000 is not allowed either. But we don't
|
|
|
|
|
* want to assert against that in nm_utils_new_infiniband_name(). So be more
|
|
|
|
|
* resilient here, and accept those. */
|
|
|
|
|
g_return_val_if_fail (p_key >= 0 && p_key <= 0xffff, NULL);
|
|
|
|
|
|
|
|
|
|
/* If parent+suffix is too long, kernel would just truncate
|
|
|
|
|
* the name. We do the same. See ipoib_vlan_add(). */
|
|
|
|
|
g_snprintf (name, IFNAMSIZ, "%s.%04x", parent_name, p_key);
|
|
|
|
|
return name;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-06 13:17:58 +02:00
|
|
|
/*****************************************************************************/
|
2016-03-01 09:56:51 +01:00
|
|
|
|
2017-02-05 19:14:28 +01:00
|
|
|
/**
|
|
|
|
|
* nm_utils_cmp_connection_by_autoconnect_priority:
|
|
|
|
|
* @a:
|
|
|
|
|
* @b:
|
|
|
|
|
*
|
|
|
|
|
* compare connections @a and @b for their autoconnect property
|
|
|
|
|
* (with sorting the connection that has autoconnect enabled before
|
|
|
|
|
* the other)
|
|
|
|
|
* If they both have autoconnect enabled, sort them depending on their
|
|
|
|
|
* autoconnect-priority (with the higher priority first).
|
|
|
|
|
*
|
|
|
|
|
* If their autoconnect/autoconnect-priority is the same, 0 is returned.
|
|
|
|
|
* That is, they compare equal.
|
|
|
|
|
*
|
|
|
|
|
* Returns: -1, 0, or 1
|
|
|
|
|
*/
|
2016-03-01 09:56:51 +01:00
|
|
|
int
|
2017-02-03 15:38:58 +01:00
|
|
|
nm_utils_cmp_connection_by_autoconnect_priority (NMConnection *a, NMConnection *b)
|
2016-03-01 09:56:51 +01:00
|
|
|
{
|
2017-02-05 19:14:28 +01:00
|
|
|
NMSettingConnection *a_s_con;
|
|
|
|
|
NMSettingConnection *b_s_con;
|
|
|
|
|
int a_ap, b_ap;
|
|
|
|
|
gboolean can_autoconnect;
|
|
|
|
|
|
|
|
|
|
if (a == b)
|
|
|
|
|
return 0;
|
|
|
|
|
if (!a)
|
|
|
|
|
return 1;
|
|
|
|
|
if (!b)
|
|
|
|
|
return -1;
|
2016-03-01 09:56:51 +01:00
|
|
|
|
2017-02-03 15:38:58 +01:00
|
|
|
a_s_con = nm_connection_get_setting_connection (a);
|
|
|
|
|
b_s_con = nm_connection_get_setting_connection (b);
|
2016-03-01 09:56:51 +01:00
|
|
|
|
2017-02-05 19:14:28 +01:00
|
|
|
if (!a_s_con)
|
|
|
|
|
return !b_s_con ? 0 : 1;
|
|
|
|
|
if (!b_s_con)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
can_autoconnect = !!nm_setting_connection_get_autoconnect (a_s_con);
|
|
|
|
|
if (can_autoconnect != (!!nm_setting_connection_get_autoconnect (b_s_con)))
|
|
|
|
|
return can_autoconnect ? -1 : 1;
|
2016-03-01 09:56:51 +01:00
|
|
|
|
2017-02-05 19:14:28 +01:00
|
|
|
if (can_autoconnect) {
|
|
|
|
|
a_ap = nm_setting_connection_get_autoconnect_priority (a_s_con);
|
|
|
|
|
b_ap = nm_setting_connection_get_autoconnect_priority (b_s_con);
|
|
|
|
|
if (a_ap != b_ap)
|
|
|
|
|
return (a_ap > b_ap) ? -1 : 1;
|
|
|
|
|
}
|
2016-03-01 09:56:51 +01:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-02 18:22:50 +02:00
|
|
|
/*****************************************************************************/
|
2016-03-01 09:56:51 +01:00
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
const char *name;
|
|
|
|
|
NMSetting *setting;
|
|
|
|
|
NMSetting *diff_base_setting;
|
|
|
|
|
GHashTable *setting_diff;
|
|
|
|
|
} LogConnectionSettingData;
|
|
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
const char *item_name;
|
|
|
|
|
NMSettingDiffResult diff_result;
|
|
|
|
|
} LogConnectionSettingItem;
|
|
|
|
|
|
all: don't use gchar/gshort/gint/glong but C types
We commonly don't use the glib typedefs for char/short/int/long,
but their C types directly.
$ git grep '\<g\(char\|short\|int\|long\|float\|double\)\>' | wc -l
587
$ git grep '\<\(char\|short\|int\|long\|float\|double\)\>' | wc -l
21114
One could argue that using the glib typedefs is preferable in
public API (of our glib based libnm library) or where it clearly
is related to glib, like during
g_object_set (obj, PROPERTY, (gint) value, NULL);
However, that argument does not seem strong, because in practice we don't
follow that argument today, and seldomly use the glib typedefs.
Also, the style guide for this would be hard to formalize, because
"using them where clearly related to a glib" is a very loose suggestion.
Also note that glib typedefs will always just be typedefs of the
underlying C types. There is no danger of glib changing the meaning
of these typedefs (because that would be a major API break of glib).
A simple style guide is instead: don't use these typedefs.
No manual actions, I only ran the bash script:
FILES=($(git ls-files '*.[hc]'))
sed -i \
-e 's/\<g\(char\|short\|int\|long\|float\|double\)\>\( [^ ]\)/\1\2/g' \
-e 's/\<g\(char\|short\|int\|long\|float\|double\)\> /\1 /g' \
-e 's/\<g\(char\|short\|int\|long\|float\|double\)\>/\1/g' \
"${FILES[@]}"
2018-07-11 07:40:19 +02:00
|
|
|
static int
|
2016-03-01 09:56:51 +01:00
|
|
|
_log_connection_sort_hashes_fcn (gconstpointer a, gconstpointer b)
|
|
|
|
|
{
|
|
|
|
|
const LogConnectionSettingData *v1 = a;
|
|
|
|
|
const LogConnectionSettingData *v2 = b;
|
2017-06-01 13:43:52 +02:00
|
|
|
NMSettingPriority p1, p2;
|
2016-03-01 09:56:51 +01:00
|
|
|
NMSetting *s1, *s2;
|
|
|
|
|
|
2018-04-24 11:20:03 +02:00
|
|
|
s1 = v1->setting ?: v1->diff_base_setting;
|
|
|
|
|
s2 = v2->setting ?: v2->diff_base_setting;
|
2016-03-01 09:56:51 +01:00
|
|
|
|
|
|
|
|
g_assert (s1 && s2);
|
|
|
|
|
|
|
|
|
|
p1 = _nm_setting_get_setting_priority (s1);
|
|
|
|
|
p2 = _nm_setting_get_setting_priority (s2);
|
|
|
|
|
|
|
|
|
|
if (p1 != p2)
|
|
|
|
|
return p1 > p2 ? 1 : -1;
|
|
|
|
|
|
|
|
|
|
return strcmp (v1->name, v2->name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GArray *
|
|
|
|
|
_log_connection_sort_hashes (NMConnection *connection, NMConnection *diff_base, GHashTable *connection_diff)
|
|
|
|
|
{
|
|
|
|
|
GHashTableIter iter;
|
|
|
|
|
GArray *sorted_hashes;
|
|
|
|
|
LogConnectionSettingData setting_data;
|
|
|
|
|
|
|
|
|
|
sorted_hashes = g_array_sized_new (TRUE, FALSE, sizeof (LogConnectionSettingData), g_hash_table_size (connection_diff));
|
|
|
|
|
|
|
|
|
|
g_hash_table_iter_init (&iter, connection_diff);
|
|
|
|
|
while (g_hash_table_iter_next (&iter, (gpointer) &setting_data.name, (gpointer) &setting_data.setting_diff)) {
|
|
|
|
|
setting_data.setting = nm_connection_get_setting_by_name (connection, setting_data.name);
|
|
|
|
|
setting_data.diff_base_setting = diff_base ? nm_connection_get_setting_by_name (diff_base, setting_data.name) : NULL;
|
|
|
|
|
g_assert (setting_data.setting || setting_data.diff_base_setting);
|
|
|
|
|
g_array_append_val (sorted_hashes, setting_data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_array_sort (sorted_hashes, _log_connection_sort_hashes_fcn);
|
|
|
|
|
return sorted_hashes;
|
|
|
|
|
}
|
|
|
|
|
|
all: don't use gchar/gshort/gint/glong but C types
We commonly don't use the glib typedefs for char/short/int/long,
but their C types directly.
$ git grep '\<g\(char\|short\|int\|long\|float\|double\)\>' | wc -l
587
$ git grep '\<\(char\|short\|int\|long\|float\|double\)\>' | wc -l
21114
One could argue that using the glib typedefs is preferable in
public API (of our glib based libnm library) or where it clearly
is related to glib, like during
g_object_set (obj, PROPERTY, (gint) value, NULL);
However, that argument does not seem strong, because in practice we don't
follow that argument today, and seldomly use the glib typedefs.
Also, the style guide for this would be hard to formalize, because
"using them where clearly related to a glib" is a very loose suggestion.
Also note that glib typedefs will always just be typedefs of the
underlying C types. There is no danger of glib changing the meaning
of these typedefs (because that would be a major API break of glib).
A simple style guide is instead: don't use these typedefs.
No manual actions, I only ran the bash script:
FILES=($(git ls-files '*.[hc]'))
sed -i \
-e 's/\<g\(char\|short\|int\|long\|float\|double\)\>\( [^ ]\)/\1\2/g' \
-e 's/\<g\(char\|short\|int\|long\|float\|double\)\> /\1 /g' \
-e 's/\<g\(char\|short\|int\|long\|float\|double\)\>/\1/g' \
"${FILES[@]}"
2018-07-11 07:40:19 +02:00
|
|
|
static int
|
2016-03-01 09:56:51 +01:00
|
|
|
_log_connection_sort_names_fcn (gconstpointer a, gconstpointer b)
|
|
|
|
|
{
|
|
|
|
|
const LogConnectionSettingItem *v1 = a;
|
|
|
|
|
const LogConnectionSettingItem *v2 = b;
|
|
|
|
|
|
|
|
|
|
/* we want to first show the items, that disappeared, then the one that changed and
|
|
|
|
|
* then the ones that were added. */
|
|
|
|
|
|
|
|
|
|
if ((v1->diff_result & NM_SETTING_DIFF_RESULT_IN_A) != (v2->diff_result & NM_SETTING_DIFF_RESULT_IN_A))
|
|
|
|
|
return (v1->diff_result & NM_SETTING_DIFF_RESULT_IN_A) ? -1 : 1;
|
|
|
|
|
if ((v1->diff_result & NM_SETTING_DIFF_RESULT_IN_B) != (v2->diff_result & NM_SETTING_DIFF_RESULT_IN_B))
|
|
|
|
|
return (v1->diff_result & NM_SETTING_DIFF_RESULT_IN_B) ? 1 : -1;
|
|
|
|
|
return strcmp (v1->item_name, v2->item_name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
_log_connection_get_property (NMSetting *setting, const char *name)
|
|
|
|
|
{
|
|
|
|
|
GValue val = G_VALUE_INIT;
|
|
|
|
|
char *s;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (setting, NULL);
|
|
|
|
|
|
|
|
|
|
if ( !NM_IS_SETTING_VPN (setting)
|
|
|
|
|
&& nm_setting_get_secret_flags (setting, name, NULL, NULL))
|
|
|
|
|
return g_strdup ("****");
|
|
|
|
|
|
|
|
|
|
if (!_nm_setting_get_property (setting, name, &val))
|
2019-01-10 19:38:36 +01:00
|
|
|
return g_strdup ("<unknown>");
|
2016-03-01 09:56:51 +01:00
|
|
|
|
|
|
|
|
if (G_VALUE_HOLDS_STRING (&val)) {
|
|
|
|
|
const char *val_s;
|
|
|
|
|
|
|
|
|
|
val_s = g_value_get_string (&val);
|
|
|
|
|
if (!val_s) {
|
|
|
|
|
/* for NULL, we want to return the unquoted string "NULL". */
|
|
|
|
|
s = g_strdup ("NULL");
|
|
|
|
|
} else {
|
|
|
|
|
char *escaped = g_strescape (val_s, "'");
|
|
|
|
|
|
|
|
|
|
s = g_strdup_printf ("'%s'", escaped);
|
|
|
|
|
g_free (escaped);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
s = g_strdup_value_contents (&val);
|
|
|
|
|
if (s == NULL)
|
|
|
|
|
s = g_strdup ("NULL");
|
|
|
|
|
else {
|
|
|
|
|
char *escaped = g_strescape (s, "'");
|
|
|
|
|
|
|
|
|
|
g_free (s);
|
|
|
|
|
s = escaped;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
g_value_unset(&val);
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_log_connection_sort_names (LogConnectionSettingData *setting_data, GArray *sorted_names)
|
|
|
|
|
{
|
|
|
|
|
GHashTableIter iter;
|
|
|
|
|
LogConnectionSettingItem item;
|
|
|
|
|
gpointer p;
|
|
|
|
|
|
|
|
|
|
g_array_set_size (sorted_names, 0);
|
|
|
|
|
|
|
|
|
|
g_hash_table_iter_init (&iter, setting_data->setting_diff);
|
|
|
|
|
while (g_hash_table_iter_next (&iter, (gpointer) &item.item_name, &p)) {
|
|
|
|
|
item.diff_result = GPOINTER_TO_UINT (p);
|
|
|
|
|
g_array_append_val (sorted_names, item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_array_sort (sorted_names, _log_connection_sort_names_fcn);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2018-03-29 11:47:52 +02:00
|
|
|
nm_utils_log_connection_diff (NMConnection *connection,
|
|
|
|
|
NMConnection *diff_base,
|
|
|
|
|
guint32 level,
|
|
|
|
|
guint64 domain,
|
|
|
|
|
const char *name,
|
|
|
|
|
const char *prefix,
|
|
|
|
|
const char *dbus_path)
|
2016-03-01 09:56:51 +01:00
|
|
|
{
|
|
|
|
|
GHashTable *connection_diff = NULL;
|
|
|
|
|
GArray *sorted_hashes;
|
|
|
|
|
GArray *sorted_names = NULL;
|
|
|
|
|
int i, j;
|
|
|
|
|
gboolean connection_diff_are_same;
|
|
|
|
|
gboolean print_header = TRUE;
|
|
|
|
|
gboolean print_setting_header;
|
|
|
|
|
GString *str1;
|
|
|
|
|
|
|
|
|
|
g_return_if_fail (NM_IS_CONNECTION (connection));
|
|
|
|
|
g_return_if_fail (!diff_base || (NM_IS_CONNECTION (diff_base) && diff_base != connection));
|
|
|
|
|
|
|
|
|
|
/* For VPN setting types, this is broken, because we cannot (generically) print the content of data/secrets. Bummer... */
|
|
|
|
|
|
|
|
|
|
if (!nm_logging_enabled (level, domain))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (!prefix)
|
|
|
|
|
prefix = "";
|
|
|
|
|
if (!name)
|
|
|
|
|
name = "";
|
|
|
|
|
|
2017-06-01 16:48:19 +02:00
|
|
|
connection_diff_are_same = nm_connection_diff (connection, diff_base,
|
|
|
|
|
NM_SETTING_COMPARE_FLAG_EXACT | NM_SETTING_COMPARE_FLAG_DIFF_RESULT_NO_DEFAULT,
|
|
|
|
|
&connection_diff);
|
2016-03-01 09:56:51 +01:00
|
|
|
if (connection_diff_are_same) {
|
2017-06-01 16:48:19 +02:00
|
|
|
const char *t1, *t2;
|
|
|
|
|
|
|
|
|
|
t1 = nm_connection_get_connection_type (connection);
|
|
|
|
|
if (diff_base) {
|
|
|
|
|
t2 = nm_connection_get_connection_type (diff_base);
|
|
|
|
|
nm_log (level, domain, NULL, NULL,
|
|
|
|
|
"%sconnection '%s' (%p/%s/%s%s%s and %p/%s/%s%s%s): no difference",
|
|
|
|
|
prefix, name,
|
|
|
|
|
connection, G_OBJECT_TYPE_NAME (connection), NM_PRINT_FMT_QUOTE_STRING (t1),
|
|
|
|
|
diff_base, G_OBJECT_TYPE_NAME (diff_base), NM_PRINT_FMT_QUOTE_STRING (t2));
|
|
|
|
|
} else {
|
|
|
|
|
nm_log (level, domain, NULL, NULL,
|
|
|
|
|
"%sconnection '%s' (%p/%s/%s%s%s): no properties set",
|
|
|
|
|
prefix, name,
|
|
|
|
|
connection, G_OBJECT_TYPE_NAME (connection), NM_PRINT_FMT_QUOTE_STRING (t1));
|
|
|
|
|
}
|
2016-03-01 09:56:51 +01:00
|
|
|
g_assert (!connection_diff);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-15 07:20:54 -04:00
|
|
|
/* FIXME: it doesn't nicely show the content of NMSettingVpn, because nm_connection_diff() does not
|
2016-03-01 09:56:51 +01:00
|
|
|
* expand the hash values. */
|
|
|
|
|
|
|
|
|
|
sorted_hashes = _log_connection_sort_hashes (connection, diff_base, connection_diff);
|
|
|
|
|
if (sorted_hashes->len <= 0)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
sorted_names = g_array_new (FALSE, FALSE, sizeof (LogConnectionSettingItem));
|
|
|
|
|
str1 = g_string_new (NULL);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < sorted_hashes->len; i++) {
|
|
|
|
|
LogConnectionSettingData *setting_data = &g_array_index (sorted_hashes, LogConnectionSettingData, i);
|
|
|
|
|
|
|
|
|
|
_log_connection_sort_names (setting_data, sorted_names);
|
|
|
|
|
print_setting_header = TRUE;
|
|
|
|
|
for (j = 0; j < sorted_names->len; j++) {
|
|
|
|
|
char *str_conn, *str_diff;
|
|
|
|
|
LogConnectionSettingItem *item = &g_array_index (sorted_names, LogConnectionSettingItem, j);
|
|
|
|
|
|
|
|
|
|
str_conn = (item->diff_result & NM_SETTING_DIFF_RESULT_IN_A)
|
|
|
|
|
? _log_connection_get_property (setting_data->setting, item->item_name)
|
|
|
|
|
: NULL;
|
|
|
|
|
str_diff = (item->diff_result & NM_SETTING_DIFF_RESULT_IN_B)
|
|
|
|
|
? _log_connection_get_property (setting_data->diff_base_setting, item->item_name)
|
|
|
|
|
: NULL;
|
|
|
|
|
|
|
|
|
|
if (print_header) {
|
|
|
|
|
GError *err_verify = NULL;
|
2017-06-01 16:48:19 +02:00
|
|
|
const char *t1, *t2;
|
2016-03-01 09:56:51 +01:00
|
|
|
|
2017-06-01 16:48:19 +02:00
|
|
|
t1 = nm_connection_get_connection_type (connection);
|
2016-03-01 09:56:51 +01:00
|
|
|
if (diff_base) {
|
2017-06-01 16:48:19 +02:00
|
|
|
t2 = nm_connection_get_connection_type (diff_base);
|
|
|
|
|
nm_log (level, domain, NULL, NULL, "%sconnection '%s' (%p/%s/%s%s%s < %p/%s/%s%s%s)%s%s%s:",
|
|
|
|
|
prefix, name,
|
|
|
|
|
connection, G_OBJECT_TYPE_NAME (connection), NM_PRINT_FMT_QUOTE_STRING (t1),
|
|
|
|
|
diff_base, G_OBJECT_TYPE_NAME (diff_base), NM_PRINT_FMT_QUOTE_STRING (t2),
|
2018-03-29 11:47:52 +02:00
|
|
|
NM_PRINT_FMT_QUOTED (dbus_path, " [", dbus_path, "]", ""));
|
2016-03-01 09:56:51 +01:00
|
|
|
} else {
|
2017-06-01 16:48:19 +02:00
|
|
|
nm_log (level, domain, NULL, NULL, "%sconnection '%s' (%p/%s/%s%s%s):%s%s%s",
|
|
|
|
|
prefix, name,
|
|
|
|
|
connection, G_OBJECT_TYPE_NAME (connection), NM_PRINT_FMT_QUOTE_STRING (t1),
|
2018-03-29 11:47:52 +02:00
|
|
|
NM_PRINT_FMT_QUOTED (dbus_path, " [", dbus_path, "]", ""));
|
2016-03-01 09:56:51 +01:00
|
|
|
}
|
|
|
|
|
print_header = FALSE;
|
|
|
|
|
|
|
|
|
|
if (!nm_connection_verify (connection, &err_verify)) {
|
2017-03-01 10:20:01 +00:00
|
|
|
nm_log (level, domain, NULL, NULL, "%sconnection %p does not verify: %s", prefix, connection, err_verify->message);
|
2016-03-01 09:56:51 +01:00
|
|
|
g_clear_error (&err_verify);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#define _NM_LOG_ALIGN "-25"
|
|
|
|
|
if (print_setting_header) {
|
|
|
|
|
if (diff_base) {
|
|
|
|
|
if (setting_data->setting && setting_data->diff_base_setting)
|
|
|
|
|
g_string_printf (str1, "%p < %p", setting_data->setting, setting_data->diff_base_setting);
|
|
|
|
|
else if (setting_data->diff_base_setting)
|
|
|
|
|
g_string_printf (str1, "*missing* < %p", setting_data->diff_base_setting);
|
|
|
|
|
else
|
|
|
|
|
g_string_printf (str1, "%p < *missing*", setting_data->setting);
|
2017-03-01 10:20:01 +00:00
|
|
|
nm_log (level, domain, NULL, NULL, "%s%"_NM_LOG_ALIGN"s [ %s ]", prefix, setting_data->name, str1->str);
|
2016-03-01 09:56:51 +01:00
|
|
|
} else
|
2017-03-01 10:20:01 +00:00
|
|
|
nm_log (level, domain, NULL, NULL, "%s%"_NM_LOG_ALIGN"s [ %p ]", prefix, setting_data->name, setting_data->setting);
|
2016-03-01 09:56:51 +01:00
|
|
|
print_setting_header = FALSE;
|
|
|
|
|
}
|
|
|
|
|
g_string_printf (str1, "%s.%s", setting_data->name, item->item_name);
|
|
|
|
|
switch (item->diff_result & (NM_SETTING_DIFF_RESULT_IN_A | NM_SETTING_DIFF_RESULT_IN_B)) {
|
|
|
|
|
case NM_SETTING_DIFF_RESULT_IN_B:
|
2018-04-24 11:20:03 +02:00
|
|
|
nm_log (level, domain, NULL, NULL, "%s%"_NM_LOG_ALIGN"s < %s", prefix, str1->str, str_diff ?: "NULL");
|
2016-03-01 09:56:51 +01:00
|
|
|
break;
|
|
|
|
|
case NM_SETTING_DIFF_RESULT_IN_A:
|
2018-04-24 11:20:03 +02:00
|
|
|
nm_log (level, domain, NULL, NULL, "%s%"_NM_LOG_ALIGN"s = %s", prefix, str1->str, str_conn ?: "NULL");
|
2016-03-01 09:56:51 +01:00
|
|
|
break;
|
|
|
|
|
default:
|
2018-04-24 11:20:03 +02:00
|
|
|
nm_log (level, domain, NULL, NULL, "%s%"_NM_LOG_ALIGN"s = %s < %s", prefix, str1->str, str_conn ?: "NULL", str_diff ?: "NULL");
|
2016-03-01 09:56:51 +01:00
|
|
|
break;
|
|
|
|
|
#undef _NM_LOG_ALIGN
|
|
|
|
|
}
|
|
|
|
|
g_free (str_conn);
|
|
|
|
|
g_free (str_diff);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_array_free (sorted_names, TRUE);
|
|
|
|
|
g_string_free (str1, TRUE);
|
|
|
|
|
out:
|
|
|
|
|
g_hash_table_destroy (connection_diff);
|
|
|
|
|
g_array_free (sorted_hashes, TRUE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define IPV6_PROPERTY_DIR "/proc/sys/net/ipv6/conf/"
|
|
|
|
|
#define IPV4_PROPERTY_DIR "/proc/sys/net/ipv4/conf/"
|
|
|
|
|
G_STATIC_ASSERT (sizeof (IPV4_PROPERTY_DIR) == sizeof (IPV6_PROPERTY_DIR));
|
2017-10-23 13:55:54 +02:00
|
|
|
G_STATIC_ASSERT (NM_STRLEN (IPV6_PROPERTY_DIR) + IFNAMSIZ + 60 == NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE);
|
2016-03-01 09:56:51 +01:00
|
|
|
|
2017-10-23 13:55:54 +02:00
|
|
|
/**
|
|
|
|
|
* nm_utils_sysctl_ip_conf_path:
|
|
|
|
|
* @addr_family: either AF_INET or AF_INET6.
|
|
|
|
|
* @buf: the output buffer where to write the path. It
|
|
|
|
|
* must be at least NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE bytes
|
|
|
|
|
* long.
|
|
|
|
|
* @ifname: an interface name
|
|
|
|
|
* @property: a property name
|
|
|
|
|
*
|
|
|
|
|
* Returns: the path to IPv6 property @property on @ifname. Note that
|
|
|
|
|
* this returns the input argument @buf.
|
|
|
|
|
*/
|
|
|
|
|
const char *
|
|
|
|
|
nm_utils_sysctl_ip_conf_path (int addr_family, char *buf, const char *ifname, const char *property)
|
2016-03-01 09:56:51 +01:00
|
|
|
{
|
|
|
|
|
int len;
|
|
|
|
|
|
2017-10-23 13:55:54 +02:00
|
|
|
nm_assert (buf);
|
2017-10-23 13:55:54 +02:00
|
|
|
nm_assert_addr_family (addr_family);
|
2017-10-23 13:55:54 +02:00
|
|
|
|
2020-02-12 18:01:13 +01:00
|
|
|
g_assert (nm_utils_ifname_valid_kernel (ifname, NULL));
|
2016-03-08 17:35:41 +01:00
|
|
|
property = NM_ASSERT_VALID_PATH_COMPONENT (property);
|
2016-03-01 09:56:51 +01:00
|
|
|
|
2017-10-23 13:55:54 +02:00
|
|
|
len = g_snprintf (buf,
|
2017-10-23 13:55:54 +02:00
|
|
|
NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE,
|
2016-03-01 09:56:51 +01:00
|
|
|
"%s%s/%s",
|
2017-10-23 13:55:54 +02:00
|
|
|
addr_family == AF_INET6 ? IPV6_PROPERTY_DIR : IPV4_PROPERTY_DIR,
|
2016-03-01 09:56:51 +01:00
|
|
|
ifname,
|
|
|
|
|
property);
|
2017-10-23 13:55:54 +02:00
|
|
|
g_assert (len < NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE - 1);
|
2017-10-23 13:55:54 +02:00
|
|
|
return buf;
|
2016-03-01 09:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
2017-10-23 14:24:28 +02:00
|
|
|
gboolean
|
|
|
|
|
nm_utils_sysctl_ip_conf_is_path (int addr_family, const char *path, const char *ifname, const char *property)
|
|
|
|
|
{
|
|
|
|
|
g_return_val_if_fail (path, FALSE);
|
|
|
|
|
NM_ASSERT_VALID_PATH_COMPONENT (property);
|
2020-02-12 18:01:13 +01:00
|
|
|
g_assert (!ifname || nm_utils_ifname_valid_kernel (ifname, NULL));
|
2017-10-23 14:24:28 +02:00
|
|
|
|
|
|
|
|
if (addr_family == AF_INET) {
|
|
|
|
|
if (!g_str_has_prefix (path, IPV4_PROPERTY_DIR))
|
|
|
|
|
return FALSE;
|
|
|
|
|
path += NM_STRLEN (IPV4_PROPERTY_DIR);
|
|
|
|
|
} else if (addr_family == AF_INET6) {
|
|
|
|
|
if (!g_str_has_prefix (path, IPV6_PROPERTY_DIR))
|
|
|
|
|
return FALSE;
|
|
|
|
|
path += NM_STRLEN (IPV6_PROPERTY_DIR);
|
|
|
|
|
} else
|
|
|
|
|
g_return_val_if_reached (FALSE);
|
|
|
|
|
|
|
|
|
|
if (ifname) {
|
|
|
|
|
if (!g_str_has_prefix (path, ifname))
|
|
|
|
|
return FALSE;
|
|
|
|
|
path += strlen (ifname);
|
|
|
|
|
if (path[0] != '/')
|
|
|
|
|
return FALSE;
|
|
|
|
|
path++;
|
|
|
|
|
} else {
|
|
|
|
|
const char *slash;
|
|
|
|
|
char buf[IFNAMSIZ];
|
|
|
|
|
gsize l;
|
|
|
|
|
|
|
|
|
|
slash = strchr (path, '/');
|
|
|
|
|
if (!slash)
|
|
|
|
|
return FALSE;
|
|
|
|
|
l = slash - path;
|
|
|
|
|
if (l >= IFNAMSIZ)
|
|
|
|
|
return FALSE;
|
|
|
|
|
memcpy (buf, path, l);
|
|
|
|
|
buf[l] = '\0';
|
2020-02-12 18:01:13 +01:00
|
|
|
if (!nm_utils_ifname_valid_kernel (buf, NULL))
|
2017-10-23 14:24:28 +02:00
|
|
|
return FALSE;
|
|
|
|
|
path = slash + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!nm_streq (path, property))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
gboolean
|
|
|
|
|
nm_utils_is_valid_path_component (const char *name)
|
|
|
|
|
{
|
|
|
|
|
const char *n;
|
|
|
|
|
|
|
|
|
|
if (name == NULL || name[0] == '\0')
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
if (name[0] == '.') {
|
|
|
|
|
if (name[1] == '\0')
|
|
|
|
|
return FALSE;
|
|
|
|
|
if (name[1] == '.' && name[2] == '\0')
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
n = name;
|
|
|
|
|
do {
|
|
|
|
|
if (*n == '/')
|
|
|
|
|
return FALSE;
|
|
|
|
|
} while (*(++n) != '\0');
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
2016-03-08 17:35:41 +01:00
|
|
|
NM_ASSERT_VALID_PATH_COMPONENT (const char *name)
|
2016-03-01 09:56:51 +01:00
|
|
|
{
|
|
|
|
|
if (G_LIKELY (nm_utils_is_valid_path_component (name)))
|
|
|
|
|
return name;
|
|
|
|
|
|
|
|
|
|
nm_log_err (LOGD_CORE, "Failed asserting path component: %s%s%s",
|
|
|
|
|
NM_PRINT_FMT_QUOTED (name, "\"", name, "\"", "(null)"));
|
|
|
|
|
g_error ("FATAL: Failed asserting path component: %s%s%s",
|
|
|
|
|
NM_PRINT_FMT_QUOTED (name, "\"", name, "\"", "(null)"));
|
|
|
|
|
g_assert_not_reached ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
|
nm_utils_is_specific_hostname (const char *name)
|
|
|
|
|
{
|
|
|
|
|
if (!name)
|
|
|
|
|
return FALSE;
|
|
|
|
|
if ( strcmp (name, "(none)")
|
|
|
|
|
&& strcmp (name, "localhost")
|
|
|
|
|
&& strcmp (name, "localhost6")
|
|
|
|
|
&& strcmp (name, "localhost.localdomain")
|
|
|
|
|
&& strcmp (name, "localhost6.localdomain6"))
|
|
|
|
|
return TRUE;
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-02 18:22:50 +02:00
|
|
|
/*****************************************************************************/
|
2016-03-01 09:56:51 +01:00
|
|
|
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
typedef struct {
|
|
|
|
|
NMUuid bin;
|
|
|
|
|
char _nul_sentinel; /* just for safety, if somebody accidentally uses the binary in a string context. */
|
2016-04-25 21:18:06 +02:00
|
|
|
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
/* depending on whether the string is packed or not (with/without hyphens),
|
|
|
|
|
* it's 32 or 36 characters long (plus the trailing NUL).
|
|
|
|
|
*
|
|
|
|
|
* The difference is that boot-id is a valid RFC 4211 UUID and represented
|
|
|
|
|
* as a 36 ascii string (with hyphens). The machine-id technically is not
|
|
|
|
|
* a UUID, but just a 32 byte sequence of hexchars. */
|
|
|
|
|
char str[37];
|
|
|
|
|
bool is_fake;
|
|
|
|
|
} UuidData;
|
|
|
|
|
|
|
|
|
|
static UuidData *
|
|
|
|
|
_uuid_data_init (UuidData *uuid_data,
|
|
|
|
|
gboolean packed,
|
|
|
|
|
gboolean is_fake,
|
|
|
|
|
const NMUuid *uuid)
|
|
|
|
|
{
|
|
|
|
|
nm_assert (uuid_data);
|
|
|
|
|
nm_assert (uuid);
|
|
|
|
|
|
|
|
|
|
uuid_data->bin = *uuid;
|
|
|
|
|
uuid_data->_nul_sentinel = '\0';
|
|
|
|
|
uuid_data->is_fake = is_fake;
|
|
|
|
|
if (packed) {
|
|
|
|
|
G_STATIC_ASSERT_EXPR (sizeof (uuid_data->str) >= (sizeof (*uuid) * 2 + 1));
|
2019-01-28 16:56:46 +01:00
|
|
|
nm_utils_bin2hexstr_full (uuid,
|
|
|
|
|
sizeof (*uuid),
|
|
|
|
|
'\0',
|
|
|
|
|
FALSE,
|
|
|
|
|
uuid_data->str);
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
} else {
|
|
|
|
|
G_STATIC_ASSERT_EXPR (sizeof (uuid_data->str) >= 37);
|
|
|
|
|
_nm_utils_uuid_unparse (uuid, uuid_data->str);
|
2016-04-25 21:18:06 +02:00
|
|
|
}
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
return uuid_data;
|
2016-04-25 21:18:06 +02:00
|
|
|
}
|
|
|
|
|
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static const UuidData *
|
core: combine secret-key with /etc/machine-id
NetworkManager loads (and generates) a secret key as
"/var/lib/NetworkManager/secret_key".
The secret key is used for seeding a per-host component when generating
hashed, stable data. For example, it contributes to "ipv4.dhcp-client-id=duid"
"ipv6.addr-gen-mode=stable-privacy", "ethernet.cloned-mac-address=stable", etc.
As such, it corresponds to the identity of the host.
Also "/etc/machine-id" is the host's identity. When cloning a virtual machine,
it may be a good idea to generate a new "/etc/machine-id", at least in those
cases where the VM's identity shall be different. Systemd provides various
mechanisms for doing that, like accepting a new machine-id via kernel command line.
For the same reason, the user should also regenerate a new NetworkManager's
secrey key when the host's identity shall change. However, that is less obvious,
less understood and less documented.
Support and use a new variant of secret key. This secret key is combined
with "/etc/machine-id" by sha256 hashing it together. That means, when the
user generates a new machine-id, NetworkManager's per-host key also changes.
Since we don't want to change behavior for existing installations, we
only do this when generating a new secret key file. For that, we encode
a version tag inside the "/var/lib/NetworkManager/secret_key" file.
Note that this is all abstracted by nm_utils_secret_key_get(). For
version 2 secret-keys, it internally combines the secret_key file with
machine-id (via sha256). The advantage is that callers don't care that
the secret-key now also contains the machine-id. Also, since we want to
stick to the previous behavior if we have an old secret-key, this is
nicely abstracted. Otherwise, the caller would not only need to handle
two per-host parts, but it would also need to check the version to
determine whether the machine-id should be explicitly included.
At this point, nm_utils_secret_key_get() should be renamed to
nm_utils_host_key_get().
2018-12-05 13:42:33 +01:00
|
|
|
_machine_id_get (gboolean allow_fake)
|
2016-04-25 21:18:06 +02:00
|
|
|
{
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
static const UuidData *volatile p_uuid_data;
|
|
|
|
|
const UuidData *d;
|
2016-04-25 21:18:06 +02:00
|
|
|
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
again:
|
|
|
|
|
d = g_atomic_pointer_get (&p_uuid_data);
|
|
|
|
|
if (G_UNLIKELY (!d)) {
|
|
|
|
|
static gsize lock;
|
|
|
|
|
static UuidData uuid_data;
|
|
|
|
|
gs_free char *content = NULL;
|
|
|
|
|
gboolean is_fake = TRUE;
|
|
|
|
|
const char *fake_type = NULL;
|
|
|
|
|
NMUuid uuid;
|
|
|
|
|
|
|
|
|
|
/* Get the machine ID from /etc/machine-id; it's always in /etc no matter
|
|
|
|
|
* where our configured SYSCONFDIR is. Alternatively, it might be in
|
|
|
|
|
* LOCALSTATEDIR /lib/dbus/machine-id.
|
|
|
|
|
*/
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
if ( nm_utils_file_get_contents (-1, "/etc/machine-id", 100*1024, 0, &content, NULL, NULL, NULL)
|
|
|
|
|
|| nm_utils_file_get_contents (-1, LOCALSTATEDIR"/lib/dbus/machine-id", 100*1024, 0, &content, NULL, NULL, NULL)) {
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
g_strstrip (content);
|
2019-02-21 10:02:28 +01:00
|
|
|
if (nm_utils_hexstr2bin_full (content,
|
|
|
|
|
FALSE,
|
|
|
|
|
FALSE,
|
|
|
|
|
NULL,
|
|
|
|
|
16,
|
|
|
|
|
(guint8 *) &uuid,
|
|
|
|
|
sizeof (uuid),
|
|
|
|
|
NULL)) {
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
if (!nm_utils_uuid_is_null (&uuid)) {
|
|
|
|
|
/* an all-zero machine-id is not valid. */
|
|
|
|
|
is_fake = FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-04-25 21:18:06 +02:00
|
|
|
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
if (is_fake) {
|
|
|
|
|
const guint8 *seed_bin;
|
|
|
|
|
const char *hash_seed;
|
|
|
|
|
gsize seed_len;
|
|
|
|
|
|
core: combine secret-key with /etc/machine-id
NetworkManager loads (and generates) a secret key as
"/var/lib/NetworkManager/secret_key".
The secret key is used for seeding a per-host component when generating
hashed, stable data. For example, it contributes to "ipv4.dhcp-client-id=duid"
"ipv6.addr-gen-mode=stable-privacy", "ethernet.cloned-mac-address=stable", etc.
As such, it corresponds to the identity of the host.
Also "/etc/machine-id" is the host's identity. When cloning a virtual machine,
it may be a good idea to generate a new "/etc/machine-id", at least in those
cases where the VM's identity shall be different. Systemd provides various
mechanisms for doing that, like accepting a new machine-id via kernel command line.
For the same reason, the user should also regenerate a new NetworkManager's
secrey key when the host's identity shall change. However, that is less obvious,
less understood and less documented.
Support and use a new variant of secret key. This secret key is combined
with "/etc/machine-id" by sha256 hashing it together. That means, when the
user generates a new machine-id, NetworkManager's per-host key also changes.
Since we don't want to change behavior for existing installations, we
only do this when generating a new secret key file. For that, we encode
a version tag inside the "/var/lib/NetworkManager/secret_key" file.
Note that this is all abstracted by nm_utils_secret_key_get(). For
version 2 secret-keys, it internally combines the secret_key file with
machine-id (via sha256). The advantage is that callers don't care that
the secret-key now also contains the machine-id. Also, since we want to
stick to the previous behavior if we have an old secret-key, this is
nicely abstracted. Otherwise, the caller would not only need to handle
two per-host parts, but it would also need to check the version to
determine whether the machine-id should be explicitly included.
At this point, nm_utils_secret_key_get() should be renamed to
nm_utils_host_key_get().
2018-12-05 13:42:33 +01:00
|
|
|
if (!allow_fake) {
|
|
|
|
|
/* we don't allow generating (and memoizing) a fake key.
|
|
|
|
|
* Signal that no valid machine-id exists. */
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-12 10:10:38 +01:00
|
|
|
if (nm_utils_host_id_get (&seed_bin, &seed_len)) {
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
/* we have no valid machine-id. Generate a fake one by hashing
|
|
|
|
|
* the secret-key. This key is commonly persisted, so it should be
|
2019-01-11 17:07:03 -02:00
|
|
|
* stable across reboots (despite having a broken system without
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
* proper machine-id). */
|
|
|
|
|
fake_type = "secret-key";
|
|
|
|
|
hash_seed = "ab085f06-b629-46d1-a553-84eeba5683b6";
|
|
|
|
|
} else {
|
|
|
|
|
/* the secret-key is not valid/persistent either. That happens when we fail
|
|
|
|
|
* to read/write the secret-key to disk. Fallback to boot-id. The boot-id
|
|
|
|
|
* itself may be fake and randomly generated ad-hoc, but that is as best
|
|
|
|
|
* as it gets. */
|
2018-12-12 10:14:51 +01:00
|
|
|
seed_bin = (const guint8 *) nm_utils_boot_id_bin ();
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
seed_len = sizeof (NMUuid);
|
|
|
|
|
fake_type = "boot-id";
|
|
|
|
|
hash_seed = "7ff0c8f5-5399-4901-ab63-61bf594abe8b";
|
|
|
|
|
}
|
2016-04-25 21:18:06 +02:00
|
|
|
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
/* the fake machine-id is based on secret-key/boot-id, but we hash it
|
|
|
|
|
* again, so that they are not literally the same. */
|
|
|
|
|
nm_utils_uuid_generate_from_string_bin (&uuid,
|
|
|
|
|
(const char *) seed_bin,
|
|
|
|
|
seed_len,
|
|
|
|
|
NM_UTILS_UUID_TYPE_VERSION5,
|
|
|
|
|
(gpointer) hash_seed);
|
2016-04-25 21:18:06 +02:00
|
|
|
}
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
|
|
|
|
|
if (!g_once_init_enter (&lock))
|
|
|
|
|
goto again;
|
|
|
|
|
|
|
|
|
|
d = _uuid_data_init (&uuid_data, TRUE, is_fake, &uuid);
|
|
|
|
|
g_atomic_pointer_set (&p_uuid_data, d);
|
|
|
|
|
g_once_init_leave (&lock, 1);
|
|
|
|
|
|
|
|
|
|
if (is_fake) {
|
|
|
|
|
nm_log_err (LOGD_CORE,
|
|
|
|
|
"/etc/machine-id: no valid machine-id. Use fake one based on %s: %s",
|
|
|
|
|
fake_type,
|
|
|
|
|
d->str);
|
|
|
|
|
} else
|
|
|
|
|
nm_log_dbg (LOGD_CORE, "/etc/machine-id: %s", d->str);
|
2016-04-25 21:18:06 +02:00
|
|
|
}
|
|
|
|
|
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
return d;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
nm_utils_machine_id_str (void)
|
|
|
|
|
{
|
core: combine secret-key with /etc/machine-id
NetworkManager loads (and generates) a secret key as
"/var/lib/NetworkManager/secret_key".
The secret key is used for seeding a per-host component when generating
hashed, stable data. For example, it contributes to "ipv4.dhcp-client-id=duid"
"ipv6.addr-gen-mode=stable-privacy", "ethernet.cloned-mac-address=stable", etc.
As such, it corresponds to the identity of the host.
Also "/etc/machine-id" is the host's identity. When cloning a virtual machine,
it may be a good idea to generate a new "/etc/machine-id", at least in those
cases where the VM's identity shall be different. Systemd provides various
mechanisms for doing that, like accepting a new machine-id via kernel command line.
For the same reason, the user should also regenerate a new NetworkManager's
secrey key when the host's identity shall change. However, that is less obvious,
less understood and less documented.
Support and use a new variant of secret key. This secret key is combined
with "/etc/machine-id" by sha256 hashing it together. That means, when the
user generates a new machine-id, NetworkManager's per-host key also changes.
Since we don't want to change behavior for existing installations, we
only do this when generating a new secret key file. For that, we encode
a version tag inside the "/var/lib/NetworkManager/secret_key" file.
Note that this is all abstracted by nm_utils_secret_key_get(). For
version 2 secret-keys, it internally combines the secret_key file with
machine-id (via sha256). The advantage is that callers don't care that
the secret-key now also contains the machine-id. Also, since we want to
stick to the previous behavior if we have an old secret-key, this is
nicely abstracted. Otherwise, the caller would not only need to handle
two per-host parts, but it would also need to check the version to
determine whether the machine-id should be explicitly included.
At this point, nm_utils_secret_key_get() should be renamed to
nm_utils_host_key_get().
2018-12-05 13:42:33 +01:00
|
|
|
return _machine_id_get (TRUE)->str;
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const NMUuid *
|
|
|
|
|
nm_utils_machine_id_bin (void)
|
|
|
|
|
{
|
core: combine secret-key with /etc/machine-id
NetworkManager loads (and generates) a secret key as
"/var/lib/NetworkManager/secret_key".
The secret key is used for seeding a per-host component when generating
hashed, stable data. For example, it contributes to "ipv4.dhcp-client-id=duid"
"ipv6.addr-gen-mode=stable-privacy", "ethernet.cloned-mac-address=stable", etc.
As such, it corresponds to the identity of the host.
Also "/etc/machine-id" is the host's identity. When cloning a virtual machine,
it may be a good idea to generate a new "/etc/machine-id", at least in those
cases where the VM's identity shall be different. Systemd provides various
mechanisms for doing that, like accepting a new machine-id via kernel command line.
For the same reason, the user should also regenerate a new NetworkManager's
secrey key when the host's identity shall change. However, that is less obvious,
less understood and less documented.
Support and use a new variant of secret key. This secret key is combined
with "/etc/machine-id" by sha256 hashing it together. That means, when the
user generates a new machine-id, NetworkManager's per-host key also changes.
Since we don't want to change behavior for existing installations, we
only do this when generating a new secret key file. For that, we encode
a version tag inside the "/var/lib/NetworkManager/secret_key" file.
Note that this is all abstracted by nm_utils_secret_key_get(). For
version 2 secret-keys, it internally combines the secret_key file with
machine-id (via sha256). The advantage is that callers don't care that
the secret-key now also contains the machine-id. Also, since we want to
stick to the previous behavior if we have an old secret-key, this is
nicely abstracted. Otherwise, the caller would not only need to handle
two per-host parts, but it would also need to check the version to
determine whether the machine-id should be explicitly included.
At this point, nm_utils_secret_key_get() should be renamed to
nm_utils_host_key_get().
2018-12-05 13:42:33 +01:00
|
|
|
return &_machine_id_get (TRUE)->bin;
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
|
nm_utils_machine_id_is_fake (void)
|
|
|
|
|
{
|
core: combine secret-key with /etc/machine-id
NetworkManager loads (and generates) a secret key as
"/var/lib/NetworkManager/secret_key".
The secret key is used for seeding a per-host component when generating
hashed, stable data. For example, it contributes to "ipv4.dhcp-client-id=duid"
"ipv6.addr-gen-mode=stable-privacy", "ethernet.cloned-mac-address=stable", etc.
As such, it corresponds to the identity of the host.
Also "/etc/machine-id" is the host's identity. When cloning a virtual machine,
it may be a good idea to generate a new "/etc/machine-id", at least in those
cases where the VM's identity shall be different. Systemd provides various
mechanisms for doing that, like accepting a new machine-id via kernel command line.
For the same reason, the user should also regenerate a new NetworkManager's
secrey key when the host's identity shall change. However, that is less obvious,
less understood and less documented.
Support and use a new variant of secret key. This secret key is combined
with "/etc/machine-id" by sha256 hashing it together. That means, when the
user generates a new machine-id, NetworkManager's per-host key also changes.
Since we don't want to change behavior for existing installations, we
only do this when generating a new secret key file. For that, we encode
a version tag inside the "/var/lib/NetworkManager/secret_key" file.
Note that this is all abstracted by nm_utils_secret_key_get(). For
version 2 secret-keys, it internally combines the secret_key file with
machine-id (via sha256). The advantage is that callers don't care that
the secret-key now also contains the machine-id. Also, since we want to
stick to the previous behavior if we have an old secret-key, this is
nicely abstracted. Otherwise, the caller would not only need to handle
two per-host parts, but it would also need to check the version to
determine whether the machine-id should be explicitly included.
At this point, nm_utils_secret_key_get() should be renamed to
nm_utils_host_key_get().
2018-12-05 13:42:33 +01:00
|
|
|
return _machine_id_get (TRUE)->is_fake;
|
2016-04-25 21:18:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
core: combine secret-key with /etc/machine-id
NetworkManager loads (and generates) a secret key as
"/var/lib/NetworkManager/secret_key".
The secret key is used for seeding a per-host component when generating
hashed, stable data. For example, it contributes to "ipv4.dhcp-client-id=duid"
"ipv6.addr-gen-mode=stable-privacy", "ethernet.cloned-mac-address=stable", etc.
As such, it corresponds to the identity of the host.
Also "/etc/machine-id" is the host's identity. When cloning a virtual machine,
it may be a good idea to generate a new "/etc/machine-id", at least in those
cases where the VM's identity shall be different. Systemd provides various
mechanisms for doing that, like accepting a new machine-id via kernel command line.
For the same reason, the user should also regenerate a new NetworkManager's
secrey key when the host's identity shall change. However, that is less obvious,
less understood and less documented.
Support and use a new variant of secret key. This secret key is combined
with "/etc/machine-id" by sha256 hashing it together. That means, when the
user generates a new machine-id, NetworkManager's per-host key also changes.
Since we don't want to change behavior for existing installations, we
only do this when generating a new secret key file. For that, we encode
a version tag inside the "/var/lib/NetworkManager/secret_key" file.
Note that this is all abstracted by nm_utils_secret_key_get(). For
version 2 secret-keys, it internally combines the secret_key file with
machine-id (via sha256). The advantage is that callers don't care that
the secret-key now also contains the machine-id. Also, since we want to
stick to the previous behavior if we have an old secret-key, this is
nicely abstracted. Otherwise, the caller would not only need to handle
two per-host parts, but it would also need to check the version to
determine whether the machine-id should be explicitly included.
At this point, nm_utils_secret_key_get() should be renamed to
nm_utils_host_key_get().
2018-12-05 13:42:33 +01:00
|
|
|
/* prefix for version2 secret key. The secret key is hashed with /etc/machine-id. */
|
|
|
|
|
#define SECRET_KEY_V2_PREFIX "nm-v2:"
|
2018-12-12 07:34:34 +01:00
|
|
|
#define SECRET_KEY_FILE NMSTATEDIR"/secret_key"
|
|
|
|
|
|
core: never fail reading host-id timestamp and never change it
The timestamp of the host-id is the timestamp of the secret_key file.
Under normal circumstances, reading the timestamp should never fail,
and reading it multiple times should always yield the same result.
If we unexpectedly fail to read the timestamp from the file we want:
- log a warning, so that the user can find out what's wrong. But
do so only once.
- we don't want to handle errors or fail operation due to a missing
timestamp. Remember, it's not supposed to ever fail, and if it does,
just log a warning and proceed with a fake timestamp instead. In
that case something is wrong, but using a non-stable, fake timestamp
is the least of the problems here.
We already have a stable identifier (the host-id) which we can use to
generate a fake timestamp. Use it.
In case the user would replace the secret_key file, we also don't want
that accessing nm_utils_host_id_get_timestamp*() yields different
results. It's not implemented (nor necessary) to support reloading a
different timestamp. Hence, nm_utils_host_id_get_timestamp() should
memoize the value and ensure that it never changes.
2018-12-12 11:04:12 +01:00
|
|
|
static gboolean
|
|
|
|
|
_host_id_read_timestamp (gboolean use_secret_key_file,
|
|
|
|
|
const guint8 *host_id,
|
|
|
|
|
gsize host_id_len,
|
|
|
|
|
gint64 *out_timestamp_ns)
|
|
|
|
|
{
|
|
|
|
|
struct stat st;
|
|
|
|
|
gint64 now;
|
|
|
|
|
guint64 v;
|
|
|
|
|
|
|
|
|
|
if ( use_secret_key_file
|
|
|
|
|
&& stat (SECRET_KEY_FILE, &st) == 0) {
|
|
|
|
|
/* don't check for overflow or timestamps in the future. We get whatever
|
|
|
|
|
* (bogus) date is on the file. */
|
2020-01-28 16:21:23 +01:00
|
|
|
*out_timestamp_ns = nm_utils_timespec_to_nsec (&st.st_mtim);
|
core: never fail reading host-id timestamp and never change it
The timestamp of the host-id is the timestamp of the secret_key file.
Under normal circumstances, reading the timestamp should never fail,
and reading it multiple times should always yield the same result.
If we unexpectedly fail to read the timestamp from the file we want:
- log a warning, so that the user can find out what's wrong. But
do so only once.
- we don't want to handle errors or fail operation due to a missing
timestamp. Remember, it's not supposed to ever fail, and if it does,
just log a warning and proceed with a fake timestamp instead. In
that case something is wrong, but using a non-stable, fake timestamp
is the least of the problems here.
We already have a stable identifier (the host-id) which we can use to
generate a fake timestamp. Use it.
In case the user would replace the secret_key file, we also don't want
that accessing nm_utils_host_id_get_timestamp*() yields different
results. It's not implemented (nor necessary) to support reloading a
different timestamp. Hence, nm_utils_host_id_get_timestamp() should
memoize the value and ensure that it never changes.
2018-12-12 11:04:12 +01:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* generate a fake timestamp based on the host-id.
|
|
|
|
|
*
|
|
|
|
|
* This really should never happen under normal circumstances. We already
|
|
|
|
|
* are in a code path, where the system has a problem (unable to get good randomness
|
|
|
|
|
* and/or can't access the secret_key). In such a scenario, a fake timestamp is the
|
|
|
|
|
* least of our problems.
|
|
|
|
|
*
|
|
|
|
|
* At least, generate something sensible so we don't have to worry about the
|
|
|
|
|
* timestamp. It is wrong to worry about using a fake timestamp (which is tied to
|
|
|
|
|
* the secret_key) if we are unable to access the secret_key file in the first place.
|
|
|
|
|
*
|
|
|
|
|
* Pick a random timestamp from the past two years. Yes, this timestamp
|
2019-01-11 17:07:03 -02:00
|
|
|
* is not stable across restarts, but apparently neither is the host-id
|
core: never fail reading host-id timestamp and never change it
The timestamp of the host-id is the timestamp of the secret_key file.
Under normal circumstances, reading the timestamp should never fail,
and reading it multiple times should always yield the same result.
If we unexpectedly fail to read the timestamp from the file we want:
- log a warning, so that the user can find out what's wrong. But
do so only once.
- we don't want to handle errors or fail operation due to a missing
timestamp. Remember, it's not supposed to ever fail, and if it does,
just log a warning and proceed with a fake timestamp instead. In
that case something is wrong, but using a non-stable, fake timestamp
is the least of the problems here.
We already have a stable identifier (the host-id) which we can use to
generate a fake timestamp. Use it.
In case the user would replace the secret_key file, we also don't want
that accessing nm_utils_host_id_get_timestamp*() yields different
results. It's not implemented (nor necessary) to support reloading a
different timestamp. Hence, nm_utils_host_id_get_timestamp() should
memoize the value and ensure that it never changes.
2018-12-12 11:04:12 +01:00
|
|
|
* nor the secret_key itself. */
|
|
|
|
|
|
2019-12-13 16:54:30 +01:00
|
|
|
#define EPOCH_TWO_YEARS (G_GINT64_CONSTANT (2 * 365 * 24 * 3600) * NM_UTILS_NSEC_PER_SEC)
|
core: never fail reading host-id timestamp and never change it
The timestamp of the host-id is the timestamp of the secret_key file.
Under normal circumstances, reading the timestamp should never fail,
and reading it multiple times should always yield the same result.
If we unexpectedly fail to read the timestamp from the file we want:
- log a warning, so that the user can find out what's wrong. But
do so only once.
- we don't want to handle errors or fail operation due to a missing
timestamp. Remember, it's not supposed to ever fail, and if it does,
just log a warning and proceed with a fake timestamp instead. In
that case something is wrong, but using a non-stable, fake timestamp
is the least of the problems here.
We already have a stable identifier (the host-id) which we can use to
generate a fake timestamp. Use it.
In case the user would replace the secret_key file, we also don't want
that accessing nm_utils_host_id_get_timestamp*() yields different
results. It's not implemented (nor necessary) to support reloading a
different timestamp. Hence, nm_utils_host_id_get_timestamp() should
memoize the value and ensure that it never changes.
2018-12-12 11:04:12 +01:00
|
|
|
|
|
|
|
|
v = nm_hash_siphash42 (1156657133u, host_id, host_id_len);
|
|
|
|
|
|
|
|
|
|
now = time (NULL);
|
|
|
|
|
*out_timestamp_ns = NM_MAX ((gint64) 1,
|
2019-12-13 16:54:30 +01:00
|
|
|
(now * NM_UTILS_NSEC_PER_SEC) - ((gint64) (v % ((guint64) (EPOCH_TWO_YEARS)))));
|
core: never fail reading host-id timestamp and never change it
The timestamp of the host-id is the timestamp of the secret_key file.
Under normal circumstances, reading the timestamp should never fail,
and reading it multiple times should always yield the same result.
If we unexpectedly fail to read the timestamp from the file we want:
- log a warning, so that the user can find out what's wrong. But
do so only once.
- we don't want to handle errors or fail operation due to a missing
timestamp. Remember, it's not supposed to ever fail, and if it does,
just log a warning and proceed with a fake timestamp instead. In
that case something is wrong, but using a non-stable, fake timestamp
is the least of the problems here.
We already have a stable identifier (the host-id) which we can use to
generate a fake timestamp. Use it.
In case the user would replace the secret_key file, we also don't want
that accessing nm_utils_host_id_get_timestamp*() yields different
results. It's not implemented (nor necessary) to support reloading a
different timestamp. Hence, nm_utils_host_id_get_timestamp() should
memoize the value and ensure that it never changes.
2018-12-12 11:04:12 +01:00
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
core: combine secret-key with /etc/machine-id
NetworkManager loads (and generates) a secret key as
"/var/lib/NetworkManager/secret_key".
The secret key is used for seeding a per-host component when generating
hashed, stable data. For example, it contributes to "ipv4.dhcp-client-id=duid"
"ipv6.addr-gen-mode=stable-privacy", "ethernet.cloned-mac-address=stable", etc.
As such, it corresponds to the identity of the host.
Also "/etc/machine-id" is the host's identity. When cloning a virtual machine,
it may be a good idea to generate a new "/etc/machine-id", at least in those
cases where the VM's identity shall be different. Systemd provides various
mechanisms for doing that, like accepting a new machine-id via kernel command line.
For the same reason, the user should also regenerate a new NetworkManager's
secrey key when the host's identity shall change. However, that is less obvious,
less understood and less documented.
Support and use a new variant of secret key. This secret key is combined
with "/etc/machine-id" by sha256 hashing it together. That means, when the
user generates a new machine-id, NetworkManager's per-host key also changes.
Since we don't want to change behavior for existing installations, we
only do this when generating a new secret key file. For that, we encode
a version tag inside the "/var/lib/NetworkManager/secret_key" file.
Note that this is all abstracted by nm_utils_secret_key_get(). For
version 2 secret-keys, it internally combines the secret_key file with
machine-id (via sha256). The advantage is that callers don't care that
the secret-key now also contains the machine-id. Also, since we want to
stick to the previous behavior if we have an old secret-key, this is
nicely abstracted. Otherwise, the caller would not only need to handle
two per-host parts, but it would also need to check the version to
determine whether the machine-id should be explicitly included.
At this point, nm_utils_secret_key_get() should be renamed to
nm_utils_host_key_get().
2018-12-05 13:42:33 +01:00
|
|
|
static const guint8 *
|
2018-12-12 10:10:38 +01:00
|
|
|
_host_id_hash_v2 (const guint8 *seed_arr,
|
|
|
|
|
gsize seed_len,
|
|
|
|
|
guint8 *out_digest /* 32 bytes (NM_UTILS_CHECKSUM_LENGTH_SHA256) */)
|
2016-04-25 18:14:25 +02:00
|
|
|
{
|
core: combine secret-key with /etc/machine-id
NetworkManager loads (and generates) a secret key as
"/var/lib/NetworkManager/secret_key".
The secret key is used for seeding a per-host component when generating
hashed, stable data. For example, it contributes to "ipv4.dhcp-client-id=duid"
"ipv6.addr-gen-mode=stable-privacy", "ethernet.cloned-mac-address=stable", etc.
As such, it corresponds to the identity of the host.
Also "/etc/machine-id" is the host's identity. When cloning a virtual machine,
it may be a good idea to generate a new "/etc/machine-id", at least in those
cases where the VM's identity shall be different. Systemd provides various
mechanisms for doing that, like accepting a new machine-id via kernel command line.
For the same reason, the user should also regenerate a new NetworkManager's
secrey key when the host's identity shall change. However, that is less obvious,
less understood and less documented.
Support and use a new variant of secret key. This secret key is combined
with "/etc/machine-id" by sha256 hashing it together. That means, when the
user generates a new machine-id, NetworkManager's per-host key also changes.
Since we don't want to change behavior for existing installations, we
only do this when generating a new secret key file. For that, we encode
a version tag inside the "/var/lib/NetworkManager/secret_key" file.
Note that this is all abstracted by nm_utils_secret_key_get(). For
version 2 secret-keys, it internally combines the secret_key file with
machine-id (via sha256). The advantage is that callers don't care that
the secret-key now also contains the machine-id. Also, since we want to
stick to the previous behavior if we have an old secret-key, this is
nicely abstracted. Otherwise, the caller would not only need to handle
two per-host parts, but it would also need to check the version to
determine whether the machine-id should be explicitly included.
At this point, nm_utils_secret_key_get() should be renamed to
nm_utils_host_key_get().
2018-12-05 13:42:33 +01:00
|
|
|
nm_auto_free_checksum GChecksum *sum = g_checksum_new (G_CHECKSUM_SHA256);
|
|
|
|
|
const UuidData *machine_id_data;
|
|
|
|
|
char slen[100];
|
2016-04-25 18:14:25 +02:00
|
|
|
|
core: combine secret-key with /etc/machine-id
NetworkManager loads (and generates) a secret key as
"/var/lib/NetworkManager/secret_key".
The secret key is used for seeding a per-host component when generating
hashed, stable data. For example, it contributes to "ipv4.dhcp-client-id=duid"
"ipv6.addr-gen-mode=stable-privacy", "ethernet.cloned-mac-address=stable", etc.
As such, it corresponds to the identity of the host.
Also "/etc/machine-id" is the host's identity. When cloning a virtual machine,
it may be a good idea to generate a new "/etc/machine-id", at least in those
cases where the VM's identity shall be different. Systemd provides various
mechanisms for doing that, like accepting a new machine-id via kernel command line.
For the same reason, the user should also regenerate a new NetworkManager's
secrey key when the host's identity shall change. However, that is less obvious,
less understood and less documented.
Support and use a new variant of secret key. This secret key is combined
with "/etc/machine-id" by sha256 hashing it together. That means, when the
user generates a new machine-id, NetworkManager's per-host key also changes.
Since we don't want to change behavior for existing installations, we
only do this when generating a new secret key file. For that, we encode
a version tag inside the "/var/lib/NetworkManager/secret_key" file.
Note that this is all abstracted by nm_utils_secret_key_get(). For
version 2 secret-keys, it internally combines the secret_key file with
machine-id (via sha256). The advantage is that callers don't care that
the secret-key now also contains the machine-id. Also, since we want to
stick to the previous behavior if we have an old secret-key, this is
nicely abstracted. Otherwise, the caller would not only need to handle
two per-host parts, but it would also need to check the version to
determine whether the machine-id should be explicitly included.
At this point, nm_utils_secret_key_get() should be renamed to
nm_utils_host_key_get().
2018-12-05 13:42:33 +01:00
|
|
|
/*
|
|
|
|
|
(stat -c '%s' /var/lib/NetworkManager/secret_key;
|
|
|
|
|
echo -n ' ';
|
|
|
|
|
cat /var/lib/NetworkManager/secret_key;
|
|
|
|
|
cat /etc/machine-id | tr -d '\n' | sed -n 's/[a-f0-9-]/\0/pg') | sha256sum
|
|
|
|
|
*/
|
core: let nm_utils_secret_key_read() handle failures internally
and add nm_utils_secret_key_get() to cache the secret-key, to only
obtain it once.
nm_utils_secret_key_read() is not expected to fail. However, in case
of an unexpected error, don't propagate the error to the caller,
but instead handle it internally.
That means, in case of error:
- log a warning within nm_utils_secret_key_read() itself.
- always return a generated secret-key. In case of error, the
key won't be persisted (obviously). But the caller can ignore
the error and just proceed with an in-memory key.
Hence, also add nm_utils_secret_key_get() to cache the key. This way,
we only try to actually generate/read the secret-key once. Since that
might fail and return an in-memory key, we must for future invocations
return the same key, without generating a new one.
2018-05-22 16:25:06 +02:00
|
|
|
|
core: combine secret-key with /etc/machine-id
NetworkManager loads (and generates) a secret key as
"/var/lib/NetworkManager/secret_key".
The secret key is used for seeding a per-host component when generating
hashed, stable data. For example, it contributes to "ipv4.dhcp-client-id=duid"
"ipv6.addr-gen-mode=stable-privacy", "ethernet.cloned-mac-address=stable", etc.
As such, it corresponds to the identity of the host.
Also "/etc/machine-id" is the host's identity. When cloning a virtual machine,
it may be a good idea to generate a new "/etc/machine-id", at least in those
cases where the VM's identity shall be different. Systemd provides various
mechanisms for doing that, like accepting a new machine-id via kernel command line.
For the same reason, the user should also regenerate a new NetworkManager's
secrey key when the host's identity shall change. However, that is less obvious,
less understood and less documented.
Support and use a new variant of secret key. This secret key is combined
with "/etc/machine-id" by sha256 hashing it together. That means, when the
user generates a new machine-id, NetworkManager's per-host key also changes.
Since we don't want to change behavior for existing installations, we
only do this when generating a new secret key file. For that, we encode
a version tag inside the "/var/lib/NetworkManager/secret_key" file.
Note that this is all abstracted by nm_utils_secret_key_get(). For
version 2 secret-keys, it internally combines the secret_key file with
machine-id (via sha256). The advantage is that callers don't care that
the secret-key now also contains the machine-id. Also, since we want to
stick to the previous behavior if we have an old secret-key, this is
nicely abstracted. Otherwise, the caller would not only need to handle
two per-host parts, but it would also need to check the version to
determine whether the machine-id should be explicitly included.
At this point, nm_utils_secret_key_get() should be renamed to
nm_utils_host_key_get().
2018-12-05 13:42:33 +01:00
|
|
|
nm_sprintf_buf (slen, "%"G_GSIZE_FORMAT" ", seed_len);
|
|
|
|
|
g_checksum_update (sum, (const guchar *) slen, strlen (slen));
|
|
|
|
|
|
|
|
|
|
g_checksum_update (sum, (const guchar *) seed_arr, seed_len);
|
|
|
|
|
|
|
|
|
|
machine_id_data = _machine_id_get (FALSE);
|
|
|
|
|
if ( machine_id_data
|
|
|
|
|
&& !machine_id_data->is_fake)
|
|
|
|
|
g_checksum_update (sum, (const guchar *) machine_id_data->str, strlen (machine_id_data->str));
|
|
|
|
|
|
|
|
|
|
nm_utils_checksum_get_digest_len (sum, out_digest, NM_UTILS_CHECKSUM_LENGTH_SHA256);
|
|
|
|
|
return out_digest;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2018-12-12 10:10:38 +01:00
|
|
|
_host_id_read (guint8 **out_host_id,
|
|
|
|
|
gsize *out_host_id_len)
|
core: combine secret-key with /etc/machine-id
NetworkManager loads (and generates) a secret key as
"/var/lib/NetworkManager/secret_key".
The secret key is used for seeding a per-host component when generating
hashed, stable data. For example, it contributes to "ipv4.dhcp-client-id=duid"
"ipv6.addr-gen-mode=stable-privacy", "ethernet.cloned-mac-address=stable", etc.
As such, it corresponds to the identity of the host.
Also "/etc/machine-id" is the host's identity. When cloning a virtual machine,
it may be a good idea to generate a new "/etc/machine-id", at least in those
cases where the VM's identity shall be different. Systemd provides various
mechanisms for doing that, like accepting a new machine-id via kernel command line.
For the same reason, the user should also regenerate a new NetworkManager's
secrey key when the host's identity shall change. However, that is less obvious,
less understood and less documented.
Support and use a new variant of secret key. This secret key is combined
with "/etc/machine-id" by sha256 hashing it together. That means, when the
user generates a new machine-id, NetworkManager's per-host key also changes.
Since we don't want to change behavior for existing installations, we
only do this when generating a new secret key file. For that, we encode
a version tag inside the "/var/lib/NetworkManager/secret_key" file.
Note that this is all abstracted by nm_utils_secret_key_get(). For
version 2 secret-keys, it internally combines the secret_key file with
machine-id (via sha256). The advantage is that callers don't care that
the secret-key now also contains the machine-id. Also, since we want to
stick to the previous behavior if we have an old secret-key, this is
nicely abstracted. Otherwise, the caller would not only need to handle
two per-host parts, but it would also need to check the version to
determine whether the machine-id should be explicitly included.
At this point, nm_utils_secret_key_get() should be renamed to
nm_utils_host_key_get().
2018-12-05 13:42:33 +01:00
|
|
|
{
|
|
|
|
|
#define SECRET_KEY_LEN 32u
|
|
|
|
|
guint8 sha256_digest[NM_UTILS_CHECKSUM_LENGTH_SHA256];
|
|
|
|
|
nm_auto_clear_secret_ptr NMSecretPtr file_content = { 0 };
|
|
|
|
|
const guint8 *secret_arr;
|
|
|
|
|
gsize secret_len;
|
|
|
|
|
GError *error = NULL;
|
|
|
|
|
gboolean success;
|
|
|
|
|
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
if (!nm_utils_file_get_contents (-1,
|
|
|
|
|
SECRET_KEY_FILE,
|
|
|
|
|
10*1024,
|
|
|
|
|
NM_UTILS_FILE_GET_CONTENTS_FLAG_SECRET,
|
|
|
|
|
&file_content.str,
|
|
|
|
|
&file_content.len,
|
|
|
|
|
NULL,
|
|
|
|
|
&error)) {
|
2018-12-12 07:29:38 +01:00
|
|
|
if (!nm_utils_error_is_notfound (error)) {
|
core: let nm_utils_secret_key_read() handle failures internally
and add nm_utils_secret_key_get() to cache the secret-key, to only
obtain it once.
nm_utils_secret_key_read() is not expected to fail. However, in case
of an unexpected error, don't propagate the error to the caller,
but instead handle it internally.
That means, in case of error:
- log a warning within nm_utils_secret_key_read() itself.
- always return a generated secret-key. In case of error, the
key won't be persisted (obviously). But the caller can ignore
the error and just proceed with an in-memory key.
Hence, also add nm_utils_secret_key_get() to cache the key. This way,
we only try to actually generate/read the secret-key once. Since that
might fail and return an in-memory key, we must for future invocations
return the same key, without generating a new one.
2018-05-22 16:25:06 +02:00
|
|
|
nm_log_warn (LOGD_CORE, "secret-key: failure reading secret key in \"%s\": %s (generate new key)",
|
2018-12-12 07:34:34 +01:00
|
|
|
SECRET_KEY_FILE, error->message);
|
core: let nm_utils_secret_key_read() handle failures internally
and add nm_utils_secret_key_get() to cache the secret-key, to only
obtain it once.
nm_utils_secret_key_read() is not expected to fail. However, in case
of an unexpected error, don't propagate the error to the caller,
but instead handle it internally.
That means, in case of error:
- log a warning within nm_utils_secret_key_read() itself.
- always return a generated secret-key. In case of error, the
key won't be persisted (obviously). But the caller can ignore
the error and just proceed with an in-memory key.
Hence, also add nm_utils_secret_key_get() to cache the key. This way,
we only try to actually generate/read the secret-key once. Since that
might fail and return an in-memory key, we must for future invocations
return the same key, without generating a new one.
2018-05-22 16:25:06 +02:00
|
|
|
}
|
|
|
|
|
g_clear_error (&error);
|
core: combine secret-key with /etc/machine-id
NetworkManager loads (and generates) a secret key as
"/var/lib/NetworkManager/secret_key".
The secret key is used for seeding a per-host component when generating
hashed, stable data. For example, it contributes to "ipv4.dhcp-client-id=duid"
"ipv6.addr-gen-mode=stable-privacy", "ethernet.cloned-mac-address=stable", etc.
As such, it corresponds to the identity of the host.
Also "/etc/machine-id" is the host's identity. When cloning a virtual machine,
it may be a good idea to generate a new "/etc/machine-id", at least in those
cases where the VM's identity shall be different. Systemd provides various
mechanisms for doing that, like accepting a new machine-id via kernel command line.
For the same reason, the user should also regenerate a new NetworkManager's
secrey key when the host's identity shall change. However, that is less obvious,
less understood and less documented.
Support and use a new variant of secret key. This secret key is combined
with "/etc/machine-id" by sha256 hashing it together. That means, when the
user generates a new machine-id, NetworkManager's per-host key also changes.
Since we don't want to change behavior for existing installations, we
only do this when generating a new secret key file. For that, we encode
a version tag inside the "/var/lib/NetworkManager/secret_key" file.
Note that this is all abstracted by nm_utils_secret_key_get(). For
version 2 secret-keys, it internally combines the secret_key file with
machine-id (via sha256). The advantage is that callers don't care that
the secret-key now also contains the machine-id. Also, since we want to
stick to the previous behavior if we have an old secret-key, this is
nicely abstracted. Otherwise, the caller would not only need to handle
two per-host parts, but it would also need to check the version to
determine whether the machine-id should be explicitly included.
At this point, nm_utils_secret_key_get() should be renamed to
nm_utils_host_key_get().
2018-12-05 13:42:33 +01:00
|
|
|
} else if ( file_content.len >= NM_STRLEN (SECRET_KEY_V2_PREFIX) + SECRET_KEY_LEN
|
|
|
|
|
&& memcmp (file_content.bin, SECRET_KEY_V2_PREFIX, NM_STRLEN (SECRET_KEY_V2_PREFIX)) == 0) {
|
|
|
|
|
/* for this type of secret key, we require a prefix followed at least SECRET_KEY_LEN (32) bytes. We
|
|
|
|
|
* (also) do that, because older versions of NetworkManager wrote exactly 32 bytes without
|
|
|
|
|
* prefix, so we won't wrongly interpret such legacy keys as v2 (if they accidentally have
|
|
|
|
|
* a SECRET_KEY_V2_PREFIX prefix, they'll still have the wrong size).
|
|
|
|
|
*
|
|
|
|
|
* Note that below we generate the random seed in base64 encoding. But that is only done
|
|
|
|
|
* to write an ASCII file. There is no base64 decoding and the ASCII is hashed as-is.
|
|
|
|
|
* We would accept any binary data just as well (provided a suitable prefix and at least
|
|
|
|
|
* 32 bytes).
|
|
|
|
|
*
|
|
|
|
|
* Note that when hashing the v2 content, we also hash the prefix. There is no strong reason,
|
|
|
|
|
* except that it seems simpler not to distinguish between the v2 prefix and the content.
|
|
|
|
|
* It's all just part of the seed. */
|
2016-06-03 15:07:43 +02:00
|
|
|
|
2018-12-12 10:10:38 +01:00
|
|
|
secret_arr = _host_id_hash_v2 (file_content.bin, file_content.len, sha256_digest);
|
core: combine secret-key with /etc/machine-id
NetworkManager loads (and generates) a secret key as
"/var/lib/NetworkManager/secret_key".
The secret key is used for seeding a per-host component when generating
hashed, stable data. For example, it contributes to "ipv4.dhcp-client-id=duid"
"ipv6.addr-gen-mode=stable-privacy", "ethernet.cloned-mac-address=stable", etc.
As such, it corresponds to the identity of the host.
Also "/etc/machine-id" is the host's identity. When cloning a virtual machine,
it may be a good idea to generate a new "/etc/machine-id", at least in those
cases where the VM's identity shall be different. Systemd provides various
mechanisms for doing that, like accepting a new machine-id via kernel command line.
For the same reason, the user should also regenerate a new NetworkManager's
secrey key when the host's identity shall change. However, that is less obvious,
less understood and less documented.
Support and use a new variant of secret key. This secret key is combined
with "/etc/machine-id" by sha256 hashing it together. That means, when the
user generates a new machine-id, NetworkManager's per-host key also changes.
Since we don't want to change behavior for existing installations, we
only do this when generating a new secret key file. For that, we encode
a version tag inside the "/var/lib/NetworkManager/secret_key" file.
Note that this is all abstracted by nm_utils_secret_key_get(). For
version 2 secret-keys, it internally combines the secret_key file with
machine-id (via sha256). The advantage is that callers don't care that
the secret-key now also contains the machine-id. Also, since we want to
stick to the previous behavior if we have an old secret-key, this is
nicely abstracted. Otherwise, the caller would not only need to handle
two per-host parts, but it would also need to check the version to
determine whether the machine-id should be explicitly included.
At this point, nm_utils_secret_key_get() should be renamed to
nm_utils_host_key_get().
2018-12-05 13:42:33 +01:00
|
|
|
secret_len = NM_UTILS_CHECKSUM_LENGTH_SHA256;
|
|
|
|
|
success = TRUE;
|
core: let nm_utils_secret_key_read() handle failures internally
and add nm_utils_secret_key_get() to cache the secret-key, to only
obtain it once.
nm_utils_secret_key_read() is not expected to fail. However, in case
of an unexpected error, don't propagate the error to the caller,
but instead handle it internally.
That means, in case of error:
- log a warning within nm_utils_secret_key_read() itself.
- always return a generated secret-key. In case of error, the
key won't be persisted (obviously). But the caller can ignore
the error and just proceed with an in-memory key.
Hence, also add nm_utils_secret_key_get() to cache the key. This way,
we only try to actually generate/read the secret-key once. Since that
might fail and return an in-memory key, we must for future invocations
return the same key, without generating a new one.
2018-05-22 16:25:06 +02:00
|
|
|
goto out;
|
core: combine secret-key with /etc/machine-id
NetworkManager loads (and generates) a secret key as
"/var/lib/NetworkManager/secret_key".
The secret key is used for seeding a per-host component when generating
hashed, stable data. For example, it contributes to "ipv4.dhcp-client-id=duid"
"ipv6.addr-gen-mode=stable-privacy", "ethernet.cloned-mac-address=stable", etc.
As such, it corresponds to the identity of the host.
Also "/etc/machine-id" is the host's identity. When cloning a virtual machine,
it may be a good idea to generate a new "/etc/machine-id", at least in those
cases where the VM's identity shall be different. Systemd provides various
mechanisms for doing that, like accepting a new machine-id via kernel command line.
For the same reason, the user should also regenerate a new NetworkManager's
secrey key when the host's identity shall change. However, that is less obvious,
less understood and less documented.
Support and use a new variant of secret key. This secret key is combined
with "/etc/machine-id" by sha256 hashing it together. That means, when the
user generates a new machine-id, NetworkManager's per-host key also changes.
Since we don't want to change behavior for existing installations, we
only do this when generating a new secret key file. For that, we encode
a version tag inside the "/var/lib/NetworkManager/secret_key" file.
Note that this is all abstracted by nm_utils_secret_key_get(). For
version 2 secret-keys, it internally combines the secret_key file with
machine-id (via sha256). The advantage is that callers don't care that
the secret-key now also contains the machine-id. Also, since we want to
stick to the previous behavior if we have an old secret-key, this is
nicely abstracted. Otherwise, the caller would not only need to handle
two per-host parts, but it would also need to check the version to
determine whether the machine-id should be explicitly included.
At this point, nm_utils_secret_key_get() should be renamed to
nm_utils_host_key_get().
2018-12-05 13:42:33 +01:00
|
|
|
} else if (file_content.len >= 16) {
|
|
|
|
|
secret_arr = file_content.bin;
|
|
|
|
|
secret_len = file_content.len;
|
|
|
|
|
success = TRUE;
|
2018-11-13 18:50:01 +01:00
|
|
|
goto out;
|
core: combine secret-key with /etc/machine-id
NetworkManager loads (and generates) a secret key as
"/var/lib/NetworkManager/secret_key".
The secret key is used for seeding a per-host component when generating
hashed, stable data. For example, it contributes to "ipv4.dhcp-client-id=duid"
"ipv6.addr-gen-mode=stable-privacy", "ethernet.cloned-mac-address=stable", etc.
As such, it corresponds to the identity of the host.
Also "/etc/machine-id" is the host's identity. When cloning a virtual machine,
it may be a good idea to generate a new "/etc/machine-id", at least in those
cases where the VM's identity shall be different. Systemd provides various
mechanisms for doing that, like accepting a new machine-id via kernel command line.
For the same reason, the user should also regenerate a new NetworkManager's
secrey key when the host's identity shall change. However, that is less obvious,
less understood and less documented.
Support and use a new variant of secret key. This secret key is combined
with "/etc/machine-id" by sha256 hashing it together. That means, when the
user generates a new machine-id, NetworkManager's per-host key also changes.
Since we don't want to change behavior for existing installations, we
only do this when generating a new secret key file. For that, we encode
a version tag inside the "/var/lib/NetworkManager/secret_key" file.
Note that this is all abstracted by nm_utils_secret_key_get(). For
version 2 secret-keys, it internally combines the secret_key file with
machine-id (via sha256). The advantage is that callers don't care that
the secret-key now also contains the machine-id. Also, since we want to
stick to the previous behavior if we have an old secret-key, this is
nicely abstracted. Otherwise, the caller would not only need to handle
two per-host parts, but it would also need to check the version to
determine whether the machine-id should be explicitly included.
At this point, nm_utils_secret_key_get() should be renamed to
nm_utils_host_key_get().
2018-12-05 13:42:33 +01:00
|
|
|
} else {
|
|
|
|
|
/* the secret key is borked. Log a warning, but proceed below to generate
|
|
|
|
|
* a new one. */
|
|
|
|
|
nm_log_warn (LOGD_CORE, "secret-key: too short secret key in \"%s\" (generate new key)", SECRET_KEY_FILE);
|
2018-11-13 18:50:01 +01:00
|
|
|
}
|
|
|
|
|
|
core: combine secret-key with /etc/machine-id
NetworkManager loads (and generates) a secret key as
"/var/lib/NetworkManager/secret_key".
The secret key is used for seeding a per-host component when generating
hashed, stable data. For example, it contributes to "ipv4.dhcp-client-id=duid"
"ipv6.addr-gen-mode=stable-privacy", "ethernet.cloned-mac-address=stable", etc.
As such, it corresponds to the identity of the host.
Also "/etc/machine-id" is the host's identity. When cloning a virtual machine,
it may be a good idea to generate a new "/etc/machine-id", at least in those
cases where the VM's identity shall be different. Systemd provides various
mechanisms for doing that, like accepting a new machine-id via kernel command line.
For the same reason, the user should also regenerate a new NetworkManager's
secrey key when the host's identity shall change. However, that is less obvious,
less understood and less documented.
Support and use a new variant of secret key. This secret key is combined
with "/etc/machine-id" by sha256 hashing it together. That means, when the
user generates a new machine-id, NetworkManager's per-host key also changes.
Since we don't want to change behavior for existing installations, we
only do this when generating a new secret key file. For that, we encode
a version tag inside the "/var/lib/NetworkManager/secret_key" file.
Note that this is all abstracted by nm_utils_secret_key_get(). For
version 2 secret-keys, it internally combines the secret_key file with
machine-id (via sha256). The advantage is that callers don't care that
the secret-key now also contains the machine-id. Also, since we want to
stick to the previous behavior if we have an old secret-key, this is
nicely abstracted. Otherwise, the caller would not only need to handle
two per-host parts, but it would also need to check the version to
determine whether the machine-id should be explicitly included.
At this point, nm_utils_secret_key_get() should be renamed to
nm_utils_host_key_get().
2018-12-05 13:42:33 +01:00
|
|
|
/* generate and persist new key */
|
|
|
|
|
{
|
|
|
|
|
#define SECRET_KEY_LEN_BASE64 ((((SECRET_KEY_LEN / 3) + 1) * 4) + 4)
|
|
|
|
|
guint8 rnd_buf[SECRET_KEY_LEN];
|
|
|
|
|
guint8 new_content[NM_STRLEN (SECRET_KEY_V2_PREFIX) + SECRET_KEY_LEN_BASE64];
|
|
|
|
|
int base64_state = 0;
|
|
|
|
|
int base64_save = 0;
|
|
|
|
|
gsize len;
|
|
|
|
|
|
|
|
|
|
success = nm_utils_random_bytes (rnd_buf, sizeof (rnd_buf));
|
|
|
|
|
|
|
|
|
|
/* Our key is really binary data. But since we anyway generate a random seed
|
|
|
|
|
* (with 32 random bytes), don't write it in binary, but instead create
|
|
|
|
|
* an pure ASCII (base64) representation. Note that the ASCII will still be taken
|
|
|
|
|
* as-is (no base64 decoding is done). The sole purpose is to write a ASCII file
|
|
|
|
|
* instead of a binary. The content is gibberish either way. */
|
|
|
|
|
memcpy (new_content, SECRET_KEY_V2_PREFIX, NM_STRLEN (SECRET_KEY_V2_PREFIX));
|
|
|
|
|
len = NM_STRLEN (SECRET_KEY_V2_PREFIX);
|
|
|
|
|
len += g_base64_encode_step (rnd_buf,
|
|
|
|
|
sizeof (rnd_buf),
|
|
|
|
|
FALSE,
|
|
|
|
|
(char *) &new_content[len],
|
|
|
|
|
&base64_state,
|
|
|
|
|
&base64_save);
|
|
|
|
|
len += g_base64_encode_close (FALSE,
|
|
|
|
|
(char *) &new_content[len],
|
|
|
|
|
&base64_state,
|
|
|
|
|
&base64_save);
|
|
|
|
|
nm_assert (len <= sizeof (new_content));
|
|
|
|
|
|
2018-12-12 10:10:38 +01:00
|
|
|
secret_arr = _host_id_hash_v2 (new_content, len, sha256_digest);
|
core: combine secret-key with /etc/machine-id
NetworkManager loads (and generates) a secret key as
"/var/lib/NetworkManager/secret_key".
The secret key is used for seeding a per-host component when generating
hashed, stable data. For example, it contributes to "ipv4.dhcp-client-id=duid"
"ipv6.addr-gen-mode=stable-privacy", "ethernet.cloned-mac-address=stable", etc.
As such, it corresponds to the identity of the host.
Also "/etc/machine-id" is the host's identity. When cloning a virtual machine,
it may be a good idea to generate a new "/etc/machine-id", at least in those
cases where the VM's identity shall be different. Systemd provides various
mechanisms for doing that, like accepting a new machine-id via kernel command line.
For the same reason, the user should also regenerate a new NetworkManager's
secrey key when the host's identity shall change. However, that is less obvious,
less understood and less documented.
Support and use a new variant of secret key. This secret key is combined
with "/etc/machine-id" by sha256 hashing it together. That means, when the
user generates a new machine-id, NetworkManager's per-host key also changes.
Since we don't want to change behavior for existing installations, we
only do this when generating a new secret key file. For that, we encode
a version tag inside the "/var/lib/NetworkManager/secret_key" file.
Note that this is all abstracted by nm_utils_secret_key_get(). For
version 2 secret-keys, it internally combines the secret_key file with
machine-id (via sha256). The advantage is that callers don't care that
the secret-key now also contains the machine-id. Also, since we want to
stick to the previous behavior if we have an old secret-key, this is
nicely abstracted. Otherwise, the caller would not only need to handle
two per-host parts, but it would also need to check the version to
determine whether the machine-id should be explicitly included.
At this point, nm_utils_secret_key_get() should be renamed to
nm_utils_host_key_get().
2018-12-05 13:42:33 +01:00
|
|
|
secret_len = NM_UTILS_CHECKSUM_LENGTH_SHA256;
|
|
|
|
|
|
|
|
|
|
if (!success)
|
|
|
|
|
nm_log_warn (LOGD_CORE, "secret-key: failure to generate good random data for secret-key (use non-persistent key)");
|
|
|
|
|
else if (nm_utils_get_testing ()) {
|
|
|
|
|
/* for test code, we don't write the generated secret-key to disk. */
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
} else if (!nm_utils_file_set_contents (SECRET_KEY_FILE,
|
|
|
|
|
(const char *) new_content,
|
|
|
|
|
len,
|
|
|
|
|
0600,
|
|
|
|
|
NULL,
|
|
|
|
|
&error)) {
|
core: combine secret-key with /etc/machine-id
NetworkManager loads (and generates) a secret key as
"/var/lib/NetworkManager/secret_key".
The secret key is used for seeding a per-host component when generating
hashed, stable data. For example, it contributes to "ipv4.dhcp-client-id=duid"
"ipv6.addr-gen-mode=stable-privacy", "ethernet.cloned-mac-address=stable", etc.
As such, it corresponds to the identity of the host.
Also "/etc/machine-id" is the host's identity. When cloning a virtual machine,
it may be a good idea to generate a new "/etc/machine-id", at least in those
cases where the VM's identity shall be different. Systemd provides various
mechanisms for doing that, like accepting a new machine-id via kernel command line.
For the same reason, the user should also regenerate a new NetworkManager's
secrey key when the host's identity shall change. However, that is less obvious,
less understood and less documented.
Support and use a new variant of secret key. This secret key is combined
with "/etc/machine-id" by sha256 hashing it together. That means, when the
user generates a new machine-id, NetworkManager's per-host key also changes.
Since we don't want to change behavior for existing installations, we
only do this when generating a new secret key file. For that, we encode
a version tag inside the "/var/lib/NetworkManager/secret_key" file.
Note that this is all abstracted by nm_utils_secret_key_get(). For
version 2 secret-keys, it internally combines the secret_key file with
machine-id (via sha256). The advantage is that callers don't care that
the secret-key now also contains the machine-id. Also, since we want to
stick to the previous behavior if we have an old secret-key, this is
nicely abstracted. Otherwise, the caller would not only need to handle
two per-host parts, but it would also need to check the version to
determine whether the machine-id should be explicitly included.
At this point, nm_utils_secret_key_get() should be renamed to
nm_utils_host_key_get().
2018-12-05 13:42:33 +01:00
|
|
|
nm_log_warn (LOGD_CORE, "secret-key: failure to persist secret key in \"%s\" (%s) (use non-persistent key)",
|
|
|
|
|
SECRET_KEY_FILE, error->message);
|
|
|
|
|
g_clear_error (&error);
|
|
|
|
|
success = FALSE;
|
|
|
|
|
} else
|
|
|
|
|
nm_log_dbg (LOGD_CORE, "secret-key: persist new secret key to \"%s\"", SECRET_KEY_FILE);
|
|
|
|
|
|
|
|
|
|
nm_explicit_bzero (rnd_buf, sizeof (rnd_buf));
|
|
|
|
|
nm_explicit_bzero (new_content, sizeof (new_content));
|
2016-04-25 18:14:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out:
|
2018-12-12 10:10:38 +01:00
|
|
|
*out_host_id_len = secret_len;
|
|
|
|
|
*out_host_id = nm_memdup (secret_arr, secret_len);
|
core: let nm_utils_secret_key_read() handle failures internally
and add nm_utils_secret_key_get() to cache the secret-key, to only
obtain it once.
nm_utils_secret_key_read() is not expected to fail. However, in case
of an unexpected error, don't propagate the error to the caller,
but instead handle it internally.
That means, in case of error:
- log a warning within nm_utils_secret_key_read() itself.
- always return a generated secret-key. In case of error, the
key won't be persisted (obviously). But the caller can ignore
the error and just proceed with an in-memory key.
Hence, also add nm_utils_secret_key_get() to cache the key. This way,
we only try to actually generate/read the secret-key once. Since that
might fail and return an in-memory key, we must for future invocations
return the same key, without generating a new one.
2018-05-22 16:25:06 +02:00
|
|
|
return success;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
2018-12-12 10:10:38 +01:00
|
|
|
guint8 *host_id;
|
|
|
|
|
gsize host_id_len;
|
core: never fail reading host-id timestamp and never change it
The timestamp of the host-id is the timestamp of the secret_key file.
Under normal circumstances, reading the timestamp should never fail,
and reading it multiple times should always yield the same result.
If we unexpectedly fail to read the timestamp from the file we want:
- log a warning, so that the user can find out what's wrong. But
do so only once.
- we don't want to handle errors or fail operation due to a missing
timestamp. Remember, it's not supposed to ever fail, and if it does,
just log a warning and proceed with a fake timestamp instead. In
that case something is wrong, but using a non-stable, fake timestamp
is the least of the problems here.
We already have a stable identifier (the host-id) which we can use to
generate a fake timestamp. Use it.
In case the user would replace the secret_key file, we also don't want
that accessing nm_utils_host_id_get_timestamp*() yields different
results. It's not implemented (nor necessary) to support reloading a
different timestamp. Hence, nm_utils_host_id_get_timestamp() should
memoize the value and ensure that it never changes.
2018-12-12 11:04:12 +01:00
|
|
|
gint64 timestamp_ns;
|
core: let nm_utils_secret_key_read() handle failures internally
and add nm_utils_secret_key_get() to cache the secret-key, to only
obtain it once.
nm_utils_secret_key_read() is not expected to fail. However, in case
of an unexpected error, don't propagate the error to the caller,
but instead handle it internally.
That means, in case of error:
- log a warning within nm_utils_secret_key_read() itself.
- always return a generated secret-key. In case of error, the
key won't be persisted (obviously). But the caller can ignore
the error and just proceed with an in-memory key.
Hence, also add nm_utils_secret_key_get() to cache the key. This way,
we only try to actually generate/read the secret-key once. Since that
might fail and return an in-memory key, we must for future invocations
return the same key, without generating a new one.
2018-05-22 16:25:06 +02:00
|
|
|
bool is_good:1;
|
core: never fail reading host-id timestamp and never change it
The timestamp of the host-id is the timestamp of the secret_key file.
Under normal circumstances, reading the timestamp should never fail,
and reading it multiple times should always yield the same result.
If we unexpectedly fail to read the timestamp from the file we want:
- log a warning, so that the user can find out what's wrong. But
do so only once.
- we don't want to handle errors or fail operation due to a missing
timestamp. Remember, it's not supposed to ever fail, and if it does,
just log a warning and proceed with a fake timestamp instead. In
that case something is wrong, but using a non-stable, fake timestamp
is the least of the problems here.
We already have a stable identifier (the host-id) which we can use to
generate a fake timestamp. Use it.
In case the user would replace the secret_key file, we also don't want
that accessing nm_utils_host_id_get_timestamp*() yields different
results. It's not implemented (nor necessary) to support reloading a
different timestamp. Hence, nm_utils_host_id_get_timestamp() should
memoize the value and ensure that it never changes.
2018-12-12 11:04:12 +01:00
|
|
|
bool timestamp_is_good:1;
|
2018-12-12 10:10:38 +01:00
|
|
|
} HostIdData;
|
core: let nm_utils_secret_key_read() handle failures internally
and add nm_utils_secret_key_get() to cache the secret-key, to only
obtain it once.
nm_utils_secret_key_read() is not expected to fail. However, in case
of an unexpected error, don't propagate the error to the caller,
but instead handle it internally.
That means, in case of error:
- log a warning within nm_utils_secret_key_read() itself.
- always return a generated secret-key. In case of error, the
key won't be persisted (obviously). But the caller can ignore
the error and just proceed with an in-memory key.
Hence, also add nm_utils_secret_key_get() to cache the key. This way,
we only try to actually generate/read the secret-key once. Since that
might fail and return an in-memory key, we must for future invocations
return the same key, without generating a new one.
2018-05-22 16:25:06 +02:00
|
|
|
|
2018-12-12 11:03:02 +01:00
|
|
|
static const HostIdData *
|
|
|
|
|
_host_id_get (void)
|
|
|
|
|
{
|
|
|
|
|
static const HostIdData *volatile host_id_static;
|
|
|
|
|
const HostIdData *host_id;
|
|
|
|
|
|
|
|
|
|
again:
|
|
|
|
|
host_id = g_atomic_pointer_get (&host_id_static);
|
|
|
|
|
if (G_UNLIKELY (!host_id)) {
|
|
|
|
|
static HostIdData host_id_data;
|
|
|
|
|
static gsize init_value = 0;
|
|
|
|
|
|
|
|
|
|
if (!g_once_init_enter (&init_value))
|
|
|
|
|
goto again;
|
|
|
|
|
|
|
|
|
|
host_id_data.is_good = _host_id_read (&host_id_data.host_id,
|
|
|
|
|
&host_id_data.host_id_len);
|
core: never fail reading host-id timestamp and never change it
The timestamp of the host-id is the timestamp of the secret_key file.
Under normal circumstances, reading the timestamp should never fail,
and reading it multiple times should always yield the same result.
If we unexpectedly fail to read the timestamp from the file we want:
- log a warning, so that the user can find out what's wrong. But
do so only once.
- we don't want to handle errors or fail operation due to a missing
timestamp. Remember, it's not supposed to ever fail, and if it does,
just log a warning and proceed with a fake timestamp instead. In
that case something is wrong, but using a non-stable, fake timestamp
is the least of the problems here.
We already have a stable identifier (the host-id) which we can use to
generate a fake timestamp. Use it.
In case the user would replace the secret_key file, we also don't want
that accessing nm_utils_host_id_get_timestamp*() yields different
results. It's not implemented (nor necessary) to support reloading a
different timestamp. Hence, nm_utils_host_id_get_timestamp() should
memoize the value and ensure that it never changes.
2018-12-12 11:04:12 +01:00
|
|
|
|
|
|
|
|
host_id_data.timestamp_is_good = _host_id_read_timestamp (host_id_data.is_good,
|
|
|
|
|
host_id_data.host_id,
|
|
|
|
|
host_id_data.host_id_len,
|
|
|
|
|
&host_id_data.timestamp_ns);
|
|
|
|
|
if ( !host_id_data.timestamp_is_good
|
|
|
|
|
&& host_id_data.is_good)
|
|
|
|
|
nm_log_warn (LOGD_CORE, "secret-key: failure reading host timestamp (use fake one)");
|
|
|
|
|
|
2018-12-12 11:03:02 +01:00
|
|
|
host_id = &host_id_data;
|
|
|
|
|
g_atomic_pointer_set (&host_id_static, host_id);
|
|
|
|
|
g_once_init_leave (&init_value, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return host_id;
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-12 10:10:38 +01:00
|
|
|
/**
|
|
|
|
|
* nm_utils_host_id_get:
|
|
|
|
|
* @out_host_id: (out) (transfer none): the binary host key
|
|
|
|
|
* @out_host_id_len: the length of the host key.
|
|
|
|
|
*
|
|
|
|
|
* This returns a per-host key that depends on /var/lib/NetworkManage/secret_key
|
|
|
|
|
* and (depending on the version) on /etc/machine-id. If /var/lib/NetworkManage/secret_key
|
|
|
|
|
* does not exist, it will be generated and persisted for next boot.
|
|
|
|
|
*
|
|
|
|
|
* Returns: %TRUE, if the host key is "good". Note that this function
|
|
|
|
|
* will always succeed to return a host-key, and that this key
|
|
|
|
|
* won't change during the run of the program (no matter what).
|
|
|
|
|
* A %FALSE return possibly means, that the secret_key is not persisted
|
|
|
|
|
* to disk, and/or that it was generated with bad randomness.
|
|
|
|
|
*/
|
core: let nm_utils_secret_key_read() handle failures internally
and add nm_utils_secret_key_get() to cache the secret-key, to only
obtain it once.
nm_utils_secret_key_read() is not expected to fail. However, in case
of an unexpected error, don't propagate the error to the caller,
but instead handle it internally.
That means, in case of error:
- log a warning within nm_utils_secret_key_read() itself.
- always return a generated secret-key. In case of error, the
key won't be persisted (obviously). But the caller can ignore
the error and just proceed with an in-memory key.
Hence, also add nm_utils_secret_key_get() to cache the key. This way,
we only try to actually generate/read the secret-key once. Since that
might fail and return an in-memory key, we must for future invocations
return the same key, without generating a new one.
2018-05-22 16:25:06 +02:00
|
|
|
gboolean
|
2018-12-12 10:10:38 +01:00
|
|
|
nm_utils_host_id_get (const guint8 **out_host_id,
|
|
|
|
|
gsize *out_host_id_len)
|
core: let nm_utils_secret_key_read() handle failures internally
and add nm_utils_secret_key_get() to cache the secret-key, to only
obtain it once.
nm_utils_secret_key_read() is not expected to fail. However, in case
of an unexpected error, don't propagate the error to the caller,
but instead handle it internally.
That means, in case of error:
- log a warning within nm_utils_secret_key_read() itself.
- always return a generated secret-key. In case of error, the
key won't be persisted (obviously). But the caller can ignore
the error and just proceed with an in-memory key.
Hence, also add nm_utils_secret_key_get() to cache the key. This way,
we only try to actually generate/read the secret-key once. Since that
might fail and return an in-memory key, we must for future invocations
return the same key, without generating a new one.
2018-05-22 16:25:06 +02:00
|
|
|
{
|
2018-12-12 10:10:38 +01:00
|
|
|
const HostIdData *host_id;
|
core: let nm_utils_secret_key_read() handle failures internally
and add nm_utils_secret_key_get() to cache the secret-key, to only
obtain it once.
nm_utils_secret_key_read() is not expected to fail. However, in case
of an unexpected error, don't propagate the error to the caller,
but instead handle it internally.
That means, in case of error:
- log a warning within nm_utils_secret_key_read() itself.
- always return a generated secret-key. In case of error, the
key won't be persisted (obviously). But the caller can ignore
the error and just proceed with an in-memory key.
Hence, also add nm_utils_secret_key_get() to cache the key. This way,
we only try to actually generate/read the secret-key once. Since that
might fail and return an in-memory key, we must for future invocations
return the same key, without generating a new one.
2018-05-22 16:25:06 +02:00
|
|
|
|
2018-12-12 11:03:02 +01:00
|
|
|
host_id = _host_id_get ();
|
2018-12-12 10:10:38 +01:00
|
|
|
*out_host_id = host_id->host_id;
|
|
|
|
|
*out_host_id_len = host_id->host_id_len;
|
|
|
|
|
return host_id->is_good;
|
2016-04-25 18:14:25 +02:00
|
|
|
}
|
|
|
|
|
|
2018-03-06 16:10:01 +01:00
|
|
|
gint64
|
core: never fail reading host-id timestamp and never change it
The timestamp of the host-id is the timestamp of the secret_key file.
Under normal circumstances, reading the timestamp should never fail,
and reading it multiple times should always yield the same result.
If we unexpectedly fail to read the timestamp from the file we want:
- log a warning, so that the user can find out what's wrong. But
do so only once.
- we don't want to handle errors or fail operation due to a missing
timestamp. Remember, it's not supposed to ever fail, and if it does,
just log a warning and proceed with a fake timestamp instead. In
that case something is wrong, but using a non-stable, fake timestamp
is the least of the problems here.
We already have a stable identifier (the host-id) which we can use to
generate a fake timestamp. Use it.
In case the user would replace the secret_key file, we also don't want
that accessing nm_utils_host_id_get_timestamp*() yields different
results. It's not implemented (nor necessary) to support reloading a
different timestamp. Hence, nm_utils_host_id_get_timestamp() should
memoize the value and ensure that it never changes.
2018-12-12 11:04:12 +01:00
|
|
|
nm_utils_host_id_get_timestamp_ns (void)
|
2018-05-25 10:33:02 +02:00
|
|
|
{
|
core: never fail reading host-id timestamp and never change it
The timestamp of the host-id is the timestamp of the secret_key file.
Under normal circumstances, reading the timestamp should never fail,
and reading it multiple times should always yield the same result.
If we unexpectedly fail to read the timestamp from the file we want:
- log a warning, so that the user can find out what's wrong. But
do so only once.
- we don't want to handle errors or fail operation due to a missing
timestamp. Remember, it's not supposed to ever fail, and if it does,
just log a warning and proceed with a fake timestamp instead. In
that case something is wrong, but using a non-stable, fake timestamp
is the least of the problems here.
We already have a stable identifier (the host-id) which we can use to
generate a fake timestamp. Use it.
In case the user would replace the secret_key file, we also don't want
that accessing nm_utils_host_id_get_timestamp*() yields different
results. It's not implemented (nor necessary) to support reloading a
different timestamp. Hence, nm_utils_host_id_get_timestamp() should
memoize the value and ensure that it never changes.
2018-12-12 11:04:12 +01:00
|
|
|
return _host_id_get ()->timestamp_ns;
|
2018-05-25 10:33:02 +02:00
|
|
|
}
|
|
|
|
|
|
2016-12-18 17:58:30 +01:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
static const UuidData *
|
|
|
|
|
_boot_id_get (void)
|
2016-12-18 17:58:30 +01:00
|
|
|
{
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
static const UuidData *volatile p_boot_id;
|
|
|
|
|
const UuidData *d;
|
2016-12-18 17:58:30 +01:00
|
|
|
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
again:
|
|
|
|
|
d = g_atomic_pointer_get (&p_boot_id);
|
|
|
|
|
if (G_UNLIKELY (!d)) {
|
|
|
|
|
static gsize lock;
|
|
|
|
|
static UuidData boot_id;
|
2016-12-18 17:58:30 +01:00
|
|
|
gs_free char *contents = NULL;
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
NMUuid uuid;
|
|
|
|
|
gboolean is_fake = FALSE;
|
2016-12-18 17:58:30 +01:00
|
|
|
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
nm_utils_file_get_contents (-1,
|
|
|
|
|
"/proc/sys/kernel/random/boot_id",
|
|
|
|
|
0,
|
2018-08-30 13:56:05 +02:00
|
|
|
NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE,
|
shared,all: return boolean success from nm_utils_file_get_contents()
... and nm_utils_fd_get_contents() and nm_utils_file_set_contents().
Don't mix negative errno return value with a GError output. Instead,
return a boolean result indicating success or failure.
Also, optionally
- output GError
- set out_errsv to the positive errno (or 0 on success)
Obviously, the return value and the output arguments (contents, length,
out_errsv, error) must all agree in their success/failure result.
That means, you may check any of the return value, out_errsv, error, and
contents to reliably detect failure or success.
Also note that out_errsv gives the positive(!) errno. But you probably
shouldn't care about the distinction and use nm_errno_native() either
way to normalize the value.
2019-08-08 11:09:58 +02:00
|
|
|
&contents,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL);
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
if ( !contents
|
|
|
|
|
|| !_nm_utils_uuid_parse (nm_strstrip (contents), &uuid)) {
|
|
|
|
|
/* generate a random UUID instead. */
|
|
|
|
|
is_fake = TRUE;
|
|
|
|
|
_nm_utils_uuid_generate_random (&uuid);
|
2016-12-18 17:58:30 +01:00
|
|
|
}
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
|
|
|
|
|
if (!g_once_init_enter (&lock))
|
|
|
|
|
goto again;
|
|
|
|
|
|
|
|
|
|
d = _uuid_data_init (&boot_id, FALSE, is_fake, &uuid);
|
|
|
|
|
g_atomic_pointer_set (&p_boot_id, d);
|
|
|
|
|
g_once_init_leave (&lock, 1);
|
2016-12-18 17:58:30 +01:00
|
|
|
}
|
|
|
|
|
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
return d;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
2018-12-12 10:14:51 +01:00
|
|
|
nm_utils_boot_id_str (void)
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
{
|
|
|
|
|
return _boot_id_get ()->str;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const NMUuid *
|
2018-12-12 10:14:51 +01:00
|
|
|
nm_utils_boot_id_bin (void)
|
core: refactor loading machine-id and cache it
Previously, whenever we needed /etc/machine-id we would re-load it
from file. The are 3 downsides of that:
- the smallest downside is the runtime overhead of repeatedly
reading the file and parse it.
- as we read it multiple times, it may change anytime. Most
code in NetworkManager does not expect or handle a change of
the machine-id.
Generally, the admin should make sure that the machine-id is properly
initialized before NetworkManager starts, and not change it. As such,
a change of the machine-id should never happen in practice.
But if it would change, we would get odd behaviors. Note for example
how generate_duid_from_machine_id() already cached the generated DUID
and only read it once.
It's better to pick the machine-id once, and rely to use the same
one for the remainder of the program.
If the admin wants to change the machine-id, NetworkManager must be
restarted as well (in case the admin cares).
Also, as we now only load it once, it makes sense to log an error
(once) when we fail to read the machine-id.
- previously, loading the machine-id could fail each time. And we
have to somehow handle that error. It seems, the best thing what we
anyway can do, is to log an error once and continue with a fake
machine-id. Here we add a fake machine-id based on the secret-key
or the boot-id. Now obtaining a machine-id can no longer fail
and error handling is no longer necessary.
Also, ensure that a machine-id of all zeros is not valid.
Technically, a machine-id is not an RFC 4122 UUID. But it's
the same size, so we also use NMUuid data structure for it.
While at it, also refactor caching of the boot-id and the secret
key. In particular, fix the thread-safety of the double-checked
locking implementations.
2018-10-30 14:07:11 +01:00
|
|
|
{
|
|
|
|
|
return &_boot_id_get ()->bin;
|
2016-12-18 17:58:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-03-15 11:25:33 +01:00
|
|
|
const char *
|
|
|
|
|
nm_utils_proc_cmdline (void)
|
|
|
|
|
{
|
|
|
|
|
static const char *volatile proc_cmdline_cached = NULL;
|
|
|
|
|
const char *proc_cmdline;
|
|
|
|
|
|
|
|
|
|
again:
|
|
|
|
|
proc_cmdline = g_atomic_pointer_get (&proc_cmdline_cached);
|
|
|
|
|
if (G_UNLIKELY (!proc_cmdline)) {
|
|
|
|
|
gs_free char *str = NULL;
|
|
|
|
|
|
|
|
|
|
g_file_get_contents ("/proc/cmdline", &str, NULL, NULL);
|
|
|
|
|
str = nm_str_realloc (str);
|
|
|
|
|
|
|
|
|
|
proc_cmdline = str ?: "";
|
|
|
|
|
if (!g_atomic_pointer_compare_and_exchange (&proc_cmdline_cached, NULL, proc_cmdline))
|
|
|
|
|
goto again;
|
|
|
|
|
|
|
|
|
|
g_steal_pointer (&str);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return proc_cmdline;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *const*
|
|
|
|
|
nm_utils_proc_cmdline_split (void)
|
|
|
|
|
{
|
2020-03-24 20:43:29 +01:00
|
|
|
static const char *const*volatile proc_cmdline_cached = NULL;
|
|
|
|
|
const char *const*proc_cmdline;
|
2020-03-15 11:25:33 +01:00
|
|
|
|
|
|
|
|
again:
|
|
|
|
|
proc_cmdline = g_atomic_pointer_get (&proc_cmdline_cached);
|
|
|
|
|
if (G_UNLIKELY (!proc_cmdline)) {
|
2020-03-24 20:43:29 +01:00
|
|
|
gs_free const char **split = NULL;
|
|
|
|
|
|
2020-05-12 09:35:08 +02:00
|
|
|
/* FIXME(release-blocker): support quotation, like systemd's proc_cmdline_extract_first().
|
2020-03-24 20:43:29 +01:00
|
|
|
* For that, add a new NMUtilsStrsplitSetFlags flag. */
|
|
|
|
|
split = nm_utils_strsplit_set_full (nm_utils_proc_cmdline (),
|
|
|
|
|
NM_ASCII_WHITESPACES,
|
|
|
|
|
NM_UTILS_STRSPLIT_SET_FLAGS_NONE);
|
|
|
|
|
proc_cmdline = split
|
|
|
|
|
?: NM_PTRARRAY_EMPTY (const char *);
|
2020-03-15 11:25:33 +01:00
|
|
|
if (!g_atomic_pointer_compare_and_exchange (&proc_cmdline_cached, NULL, proc_cmdline))
|
|
|
|
|
goto again;
|
2020-03-24 20:43:29 +01:00
|
|
|
|
|
|
|
|
g_steal_pointer (&split);
|
2020-03-15 11:25:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return proc_cmdline;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2018-12-14 08:45:09 +01:00
|
|
|
/**
|
2018-12-20 08:50:07 +01:00
|
|
|
* nm_utils_arp_type_detect_from_hwaddrlen:
|
2018-12-14 08:45:09 +01:00
|
|
|
* @hwaddr_len: the length of the hardware address in bytes.
|
|
|
|
|
*
|
|
|
|
|
* Detects the arp-type based on the length of the MAC address.
|
|
|
|
|
* On success, this returns a (positive) value in uint16_t range,
|
|
|
|
|
* like ARPHRD_ETHER or ARPHRD_INFINIBAND.
|
|
|
|
|
*
|
|
|
|
|
* On failure, returns a negative error code.
|
|
|
|
|
*
|
|
|
|
|
* Returns: the arp-type or negative value on error. */
|
|
|
|
|
int
|
2018-12-20 08:50:07 +01:00
|
|
|
nm_utils_arp_type_detect_from_hwaddrlen (gsize hwaddr_len)
|
2018-12-14 08:45:09 +01:00
|
|
|
{
|
|
|
|
|
switch (hwaddr_len) {
|
|
|
|
|
case ETH_ALEN:
|
|
|
|
|
return ARPHRD_ETHER;
|
|
|
|
|
case INFINIBAND_ALEN:
|
|
|
|
|
return ARPHRD_INFINIBAND;
|
|
|
|
|
default:
|
|
|
|
|
/* Note: if you ever support anything but ethernet and infiniband,
|
|
|
|
|
* make sure to look at all callers. They assert that it's one of
|
|
|
|
|
* these two. */
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-20 08:38:05 +01:00
|
|
|
gboolean
|
|
|
|
|
nm_utils_arp_type_validate_hwaddr (int arp_type,
|
|
|
|
|
const guint8 *hwaddr,
|
|
|
|
|
gsize hwaddr_len)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if (!hwaddr)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
if (arp_type == ARPHRD_ETHER) {
|
|
|
|
|
G_STATIC_ASSERT (ARPHRD_ETHER >= 0 && ARPHRD_ETHER <= 0xFF);
|
|
|
|
|
if (hwaddr_len != ETH_ALEN)
|
|
|
|
|
return FALSE;
|
|
|
|
|
} else if (arp_type == ARPHRD_INFINIBAND) {
|
|
|
|
|
G_STATIC_ASSERT (ARPHRD_INFINIBAND >= 0 && ARPHRD_INFINIBAND <= 0xFF);
|
|
|
|
|
if (hwaddr_len != INFINIBAND_ALEN)
|
|
|
|
|
return FALSE;
|
|
|
|
|
} else
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
nm_assert (arp_type == nm_utils_arp_type_detect_from_hwaddrlen (hwaddr_len));
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
|
nm_utils_arp_type_get_hwaddr_relevant_part (int arp_type,
|
|
|
|
|
const guint8 **hwaddr,
|
|
|
|
|
gsize *hwaddr_len)
|
|
|
|
|
{
|
|
|
|
|
g_return_val_if_fail ( hwaddr
|
|
|
|
|
&& hwaddr_len
|
|
|
|
|
&& nm_utils_arp_type_validate_hwaddr (arp_type, *hwaddr, *hwaddr_len),
|
|
|
|
|
FALSE);
|
|
|
|
|
|
|
|
|
|
/* for infiniband, we only consider the last 8 bytes. */
|
|
|
|
|
if (arp_type == ARPHRD_INFINIBAND) {
|
|
|
|
|
*hwaddr += (INFINIBAND_ALEN - 8);
|
|
|
|
|
*hwaddr_len = 8;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
/* Returns the "u" (universal/local) bit value for a Modified EUI-64 */
|
|
|
|
|
static gboolean
|
|
|
|
|
get_gre_eui64_u_bit (guint32 addr)
|
|
|
|
|
{
|
|
|
|
|
static const struct {
|
|
|
|
|
guint32 mask;
|
|
|
|
|
guint32 result;
|
|
|
|
|
} items[] = {
|
|
|
|
|
{ 0xff000000 }, { 0x7f000000 }, /* IPv4 loopback */
|
|
|
|
|
{ 0xf0000000 }, { 0xe0000000 }, /* IPv4 multicast */
|
|
|
|
|
{ 0xffffff00 }, { 0xe0000000 }, /* IPv4 local multicast */
|
|
|
|
|
{ 0xffffffff }, { INADDR_BROADCAST }, /* limited broadcast */
|
|
|
|
|
{ 0xff000000 }, { 0x00000000 }, /* zero net */
|
|
|
|
|
{ 0xff000000 }, { 0x0a000000 }, /* private 10 (RFC3330) */
|
|
|
|
|
{ 0xfff00000 }, { 0xac100000 }, /* private 172 */
|
|
|
|
|
{ 0xffff0000 }, { 0xc0a80000 }, /* private 192 */
|
|
|
|
|
{ 0xffff0000 }, { 0xa9fe0000 }, /* IPv4 link-local */
|
|
|
|
|
{ 0xffffff00 }, { 0xc0586300 }, /* anycast 6-to-4 */
|
|
|
|
|
{ 0xffffff00 }, { 0xc0000200 }, /* test 192 */
|
|
|
|
|
{ 0xfffe0000 }, { 0xc6120000 }, /* test 198 */
|
|
|
|
|
};
|
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (items); i++) {
|
|
|
|
|
if ((addr & htonl (items[i].mask)) == htonl (items[i].result))
|
|
|
|
|
return 0x00; /* "local" scope */
|
|
|
|
|
}
|
|
|
|
|
return 0x02; /* "universal" scope */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* nm_utils_get_ipv6_interface_identifier:
|
|
|
|
|
* @link_type: the hardware link type
|
|
|
|
|
* @hwaddr: the hardware address of the interface
|
|
|
|
|
* @hwaddr_len: the length (in bytes) of @hwaddr
|
|
|
|
|
* @dev_id: the device identifier, if any
|
|
|
|
|
* @out_iid: on success, filled with the interface identifier; on failure
|
|
|
|
|
* zeroed out
|
|
|
|
|
*
|
|
|
|
|
* Constructs an interface identifier in "Modified EUI-64" format which is
|
|
|
|
|
* suitable for constructing IPv6 addresses. Note that the identifier is
|
|
|
|
|
* not obscured in any way (eg, RFC3041).
|
|
|
|
|
*
|
|
|
|
|
* Returns: %TRUE if the interface identifier could be constructed, %FALSE if
|
|
|
|
|
* if could not be constructed.
|
|
|
|
|
*/
|
|
|
|
|
gboolean
|
|
|
|
|
nm_utils_get_ipv6_interface_identifier (NMLinkType link_type,
|
|
|
|
|
const guint8 *hwaddr,
|
|
|
|
|
guint hwaddr_len,
|
|
|
|
|
guint dev_id,
|
|
|
|
|
NMUtilsIPv6IfaceId *out_iid)
|
|
|
|
|
{
|
|
|
|
|
guint32 addr;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (hwaddr != NULL, FALSE);
|
|
|
|
|
g_return_val_if_fail (hwaddr_len > 0, FALSE);
|
|
|
|
|
g_return_val_if_fail (out_iid != NULL, FALSE);
|
|
|
|
|
|
|
|
|
|
out_iid->id = 0;
|
|
|
|
|
|
|
|
|
|
switch (link_type) {
|
|
|
|
|
case NM_LINK_TYPE_INFINIBAND:
|
|
|
|
|
/* Use the port GUID per http://tools.ietf.org/html/rfc4391#section-8,
|
|
|
|
|
* making sure to set the 'u' bit to 1. The GUID is the lower 64 bits
|
|
|
|
|
* of the IPoIB interface's hardware address.
|
|
|
|
|
*/
|
|
|
|
|
g_return_val_if_fail (hwaddr_len == INFINIBAND_ALEN, FALSE);
|
|
|
|
|
memcpy (out_iid->id_u8, hwaddr + INFINIBAND_ALEN - 8, 8);
|
|
|
|
|
out_iid->id_u8[0] |= 0x02;
|
|
|
|
|
return TRUE;
|
|
|
|
|
case NM_LINK_TYPE_GRE:
|
|
|
|
|
/* Hardware address is the network-endian IPv4 address */
|
|
|
|
|
g_return_val_if_fail (hwaddr_len == 4, FALSE);
|
|
|
|
|
addr = * (guint32 *) hwaddr;
|
|
|
|
|
out_iid->id_u8[0] = get_gre_eui64_u_bit (addr);
|
|
|
|
|
out_iid->id_u8[1] = 0x00;
|
|
|
|
|
out_iid->id_u8[2] = 0x5E;
|
|
|
|
|
out_iid->id_u8[3] = 0xFE;
|
|
|
|
|
memcpy (out_iid->id_u8 + 4, &addr, 4);
|
|
|
|
|
return TRUE;
|
2018-05-23 11:03:05 +02:00
|
|
|
case NM_LINK_TYPE_6LOWPAN:
|
|
|
|
|
/* The hardware address is already 64-bit. This is the case for
|
|
|
|
|
* IEEE 802.15.4 networks. */
|
|
|
|
|
memcpy (out_iid->id_u8, hwaddr, sizeof (out_iid->id_u8));
|
|
|
|
|
return TRUE;
|
2016-03-01 09:56:51 +01:00
|
|
|
default:
|
|
|
|
|
if (hwaddr_len == ETH_ALEN) {
|
|
|
|
|
/* Translate 48-bit MAC address to a 64-bit Modified EUI-64. See
|
|
|
|
|
* http://tools.ietf.org/html/rfc4291#appendix-A and the Linux
|
|
|
|
|
* kernel's net/ipv6/addrconf.c::ipv6_generate_eui64() function.
|
|
|
|
|
*/
|
|
|
|
|
out_iid->id_u8[0] = hwaddr[0];
|
|
|
|
|
out_iid->id_u8[1] = hwaddr[1];
|
|
|
|
|
out_iid->id_u8[2] = hwaddr[2];
|
|
|
|
|
if (dev_id) {
|
|
|
|
|
out_iid->id_u8[3] = (dev_id >> 8) & 0xff;
|
|
|
|
|
out_iid->id_u8[4] = dev_id & 0xff;
|
|
|
|
|
} else {
|
|
|
|
|
out_iid->id_u8[0] ^= 0x02;
|
|
|
|
|
out_iid->id_u8[3] = 0xff;
|
|
|
|
|
out_iid->id_u8[4] = 0xfe;
|
|
|
|
|
}
|
|
|
|
|
out_iid->id_u8[5] = hwaddr[3];
|
|
|
|
|
out_iid->id_u8[6] = hwaddr[4];
|
|
|
|
|
out_iid->id_u8[7] = hwaddr[5];
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2016-04-30 16:43:10 +02:00
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* nm_utils_ipv6_addr_set_interface_identifier:
|
|
|
|
|
* @addr: output token encoded as %in6_addr
|
|
|
|
|
* @iid: %NMUtilsIPv6IfaceId interface identifier
|
|
|
|
|
*
|
|
|
|
|
* Converts the %NMUtilsIPv6IfaceId to an %in6_addr (suitable for use
|
|
|
|
|
* with Linux platform). This only copies the lower 8 bytes, ignoring
|
|
|
|
|
* the /64 network prefix which is expected to be all-zero for a valid
|
|
|
|
|
* token.
|
|
|
|
|
*/
|
2016-03-01 09:56:51 +01:00
|
|
|
void
|
2016-05-27 18:19:55 +02:00
|
|
|
nm_utils_ipv6_addr_set_interface_identifier (struct in6_addr *addr,
|
2016-03-01 09:56:51 +01:00
|
|
|
const NMUtilsIPv6IfaceId iid)
|
|
|
|
|
{
|
|
|
|
|
memcpy (addr->s6_addr + 8, &iid.id_u8, 8);
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-30 16:43:10 +02:00
|
|
|
/**
|
|
|
|
|
* nm_utils_ipv6_interface_identifier_get_from_addr:
|
|
|
|
|
* @iid: output %NMUtilsIPv6IfaceId interface identifier set from the token
|
|
|
|
|
* @addr: token encoded as %in6_addr
|
|
|
|
|
*
|
|
|
|
|
* Converts the %in6_addr encoded token (as used by Linux platform) to
|
|
|
|
|
* the interface identifier.
|
|
|
|
|
*/
|
2016-03-01 09:56:51 +01:00
|
|
|
void
|
2016-05-27 18:19:55 +02:00
|
|
|
nm_utils_ipv6_interface_identifier_get_from_addr (NMUtilsIPv6IfaceId *iid,
|
2016-03-01 09:56:51 +01:00
|
|
|
const struct in6_addr *addr)
|
|
|
|
|
{
|
|
|
|
|
memcpy (iid, addr->s6_addr + 8, 8);
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-30 16:43:10 +02:00
|
|
|
/**
|
|
|
|
|
* nm_utils_ipv6_interface_identifier_get_from_token:
|
|
|
|
|
* @iid: output %NMUtilsIPv6IfaceId interface identifier set from the token
|
|
|
|
|
* @token: token encoded as string
|
|
|
|
|
*
|
|
|
|
|
* Converts the %in6_addr encoded token (as used in ip6 settings) to
|
|
|
|
|
* the interface identifier.
|
|
|
|
|
*
|
|
|
|
|
* Returns: %TRUE if the @token is a valid token, %FALSE otherwise
|
|
|
|
|
*/
|
|
|
|
|
gboolean
|
|
|
|
|
nm_utils_ipv6_interface_identifier_get_from_token (NMUtilsIPv6IfaceId *iid,
|
2017-04-27 13:58:38 +02:00
|
|
|
const char *token)
|
2016-04-30 16:43:10 +02:00
|
|
|
{
|
|
|
|
|
struct in6_addr i6_token;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (token, FALSE);
|
|
|
|
|
|
|
|
|
|
if (!inet_pton (AF_INET6, token, &i6_token))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
if (!_nm_utils_inet6_is_token (&i6_token))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
nm_utils_ipv6_interface_identifier_get_from_addr (iid, &i6_token);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* nm_utils_inet6_interface_identifier_to_token:
|
|
|
|
|
* @iid: %NMUtilsIPv6IfaceId interface identifier
|
2018-11-26 16:49:51 +01:00
|
|
|
* @buf: the destination buffer of at least %NM_UTILS_INET_ADDRSTRLEN
|
|
|
|
|
* bytes.
|
2016-04-30 16:43:10 +02:00
|
|
|
*
|
|
|
|
|
* Converts the interface identifier to a string token.
|
|
|
|
|
*
|
2018-11-26 16:49:51 +01:00
|
|
|
* Returns: the input buffer filled with the id as string.
|
2016-04-30 16:43:10 +02:00
|
|
|
*/
|
|
|
|
|
const char *
|
2020-01-09 11:48:18 +01:00
|
|
|
nm_utils_inet6_interface_identifier_to_token (NMUtilsIPv6IfaceId iid,
|
|
|
|
|
char buf[static INET6_ADDRSTRLEN])
|
2016-04-30 16:43:10 +02:00
|
|
|
{
|
|
|
|
|
struct in6_addr i6_token = { .s6_addr = { 0, } };
|
|
|
|
|
|
2020-01-09 11:29:27 +01:00
|
|
|
nm_assert (buf);
|
2016-04-30 16:43:10 +02:00
|
|
|
nm_utils_ipv6_addr_set_interface_identifier (&i6_token, iid);
|
2020-01-09 11:29:27 +01:00
|
|
|
return _nm_utils_inet6_ntop (&i6_token, buf);
|
2016-04-30 16:43:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2016-12-18 13:54:26 +01:00
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
nm_utils_stable_id_random (void)
|
|
|
|
|
{
|
|
|
|
|
char buf[15];
|
|
|
|
|
|
2017-10-12 15:43:21 +02:00
|
|
|
nm_utils_random_bytes (buf, sizeof (buf));
|
2016-12-18 13:54:26 +01:00
|
|
|
return g_base64_encode ((guchar *) buf, sizeof (buf));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
nm_utils_stable_id_generated_complete (const char *stable_id_generated)
|
|
|
|
|
{
|
2018-11-01 12:52:38 +01:00
|
|
|
nm_auto_free_checksum GChecksum *sum = NULL;
|
|
|
|
|
guint8 buf[NM_UTILS_CHECKSUM_LENGTH_SHA1];
|
2016-12-18 13:54:26 +01:00
|
|
|
char *base64;
|
|
|
|
|
|
2019-01-11 17:07:03 -02:00
|
|
|
/* for NM_UTILS_STABLE_TYPE_GENERATED we generate a possibly long string
|
2016-12-18 13:54:26 +01:00
|
|
|
* by doing text-substitutions in nm_utils_stable_id_parse().
|
|
|
|
|
*
|
|
|
|
|
* Let's shorten the (possibly) long stable_id to something more compact. */
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (stable_id_generated, NULL);
|
|
|
|
|
|
|
|
|
|
sum = g_checksum_new (G_CHECKSUM_SHA1);
|
|
|
|
|
g_checksum_update (sum, (guchar *) stable_id_generated, strlen (stable_id_generated));
|
2018-11-01 12:52:38 +01:00
|
|
|
nm_utils_checksum_get_digest (sum, buf);
|
2016-12-18 13:54:26 +01:00
|
|
|
|
|
|
|
|
/* we don't care to use the sha1 sum in common hex representation.
|
|
|
|
|
* Use instead base64, it's 27 chars (stripping the padding) vs.
|
|
|
|
|
* 40. */
|
|
|
|
|
|
|
|
|
|
base64 = g_base64_encode ((guchar *) buf, sizeof (buf));
|
|
|
|
|
nm_assert (strlen (base64) == 28);
|
|
|
|
|
nm_assert (base64[27] == '=');
|
|
|
|
|
|
|
|
|
|
base64[27] = '\0';
|
|
|
|
|
return base64;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_stable_id_append (GString *str,
|
|
|
|
|
const char *substitution)
|
|
|
|
|
{
|
|
|
|
|
if (!substitution)
|
|
|
|
|
substitution = "";
|
|
|
|
|
g_string_append_printf (str, "=%zu{%s}", strlen (substitution), substitution);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NMUtilsStableType
|
|
|
|
|
nm_utils_stable_id_parse (const char *stable_id,
|
2018-05-22 18:35:43 +02:00
|
|
|
const char *deviceid,
|
2018-11-02 12:07:09 +01:00
|
|
|
const char *hwaddr,
|
2016-12-18 13:54:26 +01:00
|
|
|
const char *bootid,
|
2018-05-22 18:35:43 +02:00
|
|
|
const char *uuid,
|
2016-12-18 13:54:26 +01:00
|
|
|
char **out_generated)
|
|
|
|
|
{
|
|
|
|
|
gsize i, idx_start;
|
|
|
|
|
GString *str = NULL;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (out_generated, NM_UTILS_STABLE_TYPE_RANDOM);
|
|
|
|
|
|
|
|
|
|
if (!stable_id) {
|
2017-07-25 13:08:56 +02:00
|
|
|
*out_generated = NULL;
|
2016-12-18 13:54:26 +01:00
|
|
|
return NM_UTILS_STABLE_TYPE_UUID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* the stable-id allows for some dynamic by performing text-substitutions
|
|
|
|
|
* of ${...} patterns.
|
|
|
|
|
*
|
|
|
|
|
* At first, it looks a bit like bash parameter substitution.
|
2019-01-11 17:07:03 -02:00
|
|
|
* In contrast however, the process is unambiguous so that the resulting
|
2016-12-18 13:54:26 +01:00
|
|
|
* effective id differs if:
|
|
|
|
|
* - the original, untranslated stable-id differs
|
|
|
|
|
* - or any of the subsitutions differs.
|
|
|
|
|
*
|
|
|
|
|
* The reason for that is, for example if you specify "${CONNECTION}" in the
|
|
|
|
|
* stable-id, then the resulting ID should be always(!) unique for this connection.
|
|
|
|
|
* There should be no way another connection could specify any stable-id that results
|
|
|
|
|
* in the same addresses to be generated (aside hash collisions).
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* For example: say you have a connection with UUID
|
|
|
|
|
* "123e4567-e89b-12d3-a456-426655440000" which happens also to be
|
|
|
|
|
* the current boot-id.
|
|
|
|
|
* Then:
|
|
|
|
|
* (1) connection.stable-id = <NULL>
|
|
|
|
|
* (2) connection.stable-id = "123e4567-e89b-12d3-a456-426655440000"
|
|
|
|
|
* (3) connection.stable-id = "${CONNECTION}"
|
|
|
|
|
* (3) connection.stable-id = "${BOOT}"
|
|
|
|
|
* will all generate different addresses, although in one way or the
|
|
|
|
|
* other, they all mangle the uuid "123e4567-e89b-12d3-a456-426655440000".
|
|
|
|
|
*
|
|
|
|
|
* For example, with stable-id="${FOO}${BAR}" the substitutions
|
|
|
|
|
* - FOO="ab", BAR="c"
|
|
|
|
|
* - FOO="a", BAR="bc"
|
|
|
|
|
* should give a different effective id.
|
|
|
|
|
*
|
|
|
|
|
* For example, with FOO="x" and BAR="x", the stable-ids
|
|
|
|
|
* - "${FOO}${BAR}"
|
|
|
|
|
* - "${BAR}${FOO}"
|
|
|
|
|
* should give a different effective id.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
idx_start = 0;
|
|
|
|
|
for (i = 0; stable_id[i]; ) {
|
|
|
|
|
if (stable_id[i] != '$') {
|
|
|
|
|
i++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define CHECK_PREFIX(prefix) \
|
|
|
|
|
({ \
|
|
|
|
|
gboolean _match = FALSE; \
|
|
|
|
|
\
|
|
|
|
|
if (g_str_has_prefix (&stable_id[i], ""prefix"")) { \
|
|
|
|
|
_match = TRUE; \
|
|
|
|
|
if (!str) \
|
|
|
|
|
str = g_string_sized_new (256); \
|
|
|
|
|
i += NM_STRLEN (prefix); \
|
|
|
|
|
g_string_append_len (str, &(stable_id)[idx_start], i - idx_start); \
|
|
|
|
|
idx_start = i; \
|
|
|
|
|
} \
|
|
|
|
|
_match; \
|
|
|
|
|
})
|
|
|
|
|
if (CHECK_PREFIX ("${CONNECTION}"))
|
|
|
|
|
_stable_id_append (str, uuid);
|
|
|
|
|
else if (CHECK_PREFIX ("${BOOT}"))
|
2018-11-02 12:39:02 +01:00
|
|
|
_stable_id_append (str, bootid);
|
2018-05-22 18:35:43 +02:00
|
|
|
else if (CHECK_PREFIX ("${DEVICE}"))
|
|
|
|
|
_stable_id_append (str, deviceid);
|
2018-11-02 12:07:09 +01:00
|
|
|
else if (CHECK_PREFIX ("${MAC}"))
|
|
|
|
|
_stable_id_append (str, hwaddr);
|
2016-12-18 13:54:26 +01:00
|
|
|
else if (g_str_has_prefix (&stable_id[i], "${RANDOM}")) {
|
|
|
|
|
/* RANDOM makes not so much sense for cloned-mac-address
|
2018-09-15 07:20:54 -04:00
|
|
|
* as the result is similar to specyifing "cloned-mac-address=random".
|
2016-12-18 13:54:26 +01:00
|
|
|
* It makes however sense for RFC 7217 Stable Privacy IPv6 addresses
|
|
|
|
|
* where this is effectively the only way to generate a different
|
|
|
|
|
* (random) host identifier for each connect.
|
|
|
|
|
*
|
|
|
|
|
* With RANDOM, the user can switch the lifetime of the
|
|
|
|
|
* generated cloned-mac-address and IPv6 host identifier
|
|
|
|
|
* by toggeling only the stable-id property of the connection.
|
|
|
|
|
* With RANDOM being the most short-lived, ~non-stable~ variant.
|
|
|
|
|
*/
|
|
|
|
|
if (str)
|
|
|
|
|
g_string_free (str, TRUE);
|
|
|
|
|
*out_generated = NULL;
|
|
|
|
|
return NM_UTILS_STABLE_TYPE_RANDOM;
|
|
|
|
|
} else {
|
|
|
|
|
/* The text following the '$' is not recognized as valid
|
|
|
|
|
* substitution pattern. Treat it verbatim. */
|
|
|
|
|
i++;
|
|
|
|
|
|
|
|
|
|
/* Note that using unrecognized substitution patterns might
|
|
|
|
|
* yield different results with future versions. Avoid that,
|
|
|
|
|
* by not using '$' (except for actual substitutions) or escape
|
|
|
|
|
* it as "$$" (which is guaranteed to be treated verbatim
|
|
|
|
|
* in future). */
|
|
|
|
|
if (stable_id[i] == '$')
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#undef CHECK_PREFIX
|
|
|
|
|
|
|
|
|
|
if (!str) {
|
|
|
|
|
*out_generated = NULL;
|
|
|
|
|
return NM_UTILS_STABLE_TYPE_STABLE_ID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (idx_start < i)
|
|
|
|
|
g_string_append_len (str, &stable_id[idx_start], i - idx_start);
|
|
|
|
|
*out_generated = g_string_free (str, FALSE);
|
|
|
|
|
return NM_UTILS_STABLE_TYPE_GENERATED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2016-04-30 16:43:10 +02:00
|
|
|
|
core: avoid generating reserved IPv6 interface identifiers
https://tools.ietf.org/html/rfc7217 says:
The resulting Interface Identifier SHOULD be compared against the
reserved IPv6 Interface Identifiers [RFC5453] [IANA-RESERVED-IID]
and against those Interface Identifiers already employed in an
address of the same network interface and the same network
prefix. In the event that an unacceptable identifier has been
generated, this situation SHOULD be handled in the same way as
the case of duplicate addresses (see Section 6).
In case of conflict, this suggests to create a new address incrementing
the DAD counter, etc. Don't do that. If we generate an address of the
reserved region, just rehash it right away. Note that the actual address
anyway appears random, so this re-hashing is just as good as incrementing
the DAD counter and going through the entire process again.
Note that now we no longer generate certain addresses like we did
previously. But realize that we now merely reject (1 + 16777216 + 128)
addresses out of 2^64. So, the likelyhood of of a user accidentally
generating an address that is suddenly rejected is in the order of
10e-13 (1 / 1,099,503,173,697). Which is not astronomically, but still
extreeeemely unlikely.
Also, the whole process is anyway build on the idea that somebody else
might generate conflicting addresses (DAD). It means, there was always
the extremely tiny chance that the address you generated last time is
suddenly taken by somebody else. So, this change appears to a user
like these reserved addresses are now claimed by another (non existing)
host and a different address gets generated -- business as usual, as
far as SLAAC is concerned.
(cherry picked from commit f15c4961ad02a1edf766f140dd94e2d5449f8d82)
2017-04-27 12:38:40 +02:00
|
|
|
static gboolean
|
|
|
|
|
_is_reserved_ipv6_iid (const guint8 *iid)
|
|
|
|
|
{
|
|
|
|
|
/* https://tools.ietf.org/html/rfc5453 */
|
|
|
|
|
/* https://www.iana.org/assignments/ipv6-interface-ids/ipv6-interface-ids.xml */
|
|
|
|
|
|
|
|
|
|
/* 0000:0000:0000:0000 (Subnet-Router Anycast [RFC4291]) */
|
|
|
|
|
if (memcmp (iid, &nm_ip_addr_zero.addr6.s6_addr[8], 8) == 0)
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
/* 0200:5EFF:FE00:0000 - 0200:5EFF:FE00:5212 (Reserved IPv6 Interface Identifiers corresponding to the IANA Ethernet Block [RFC4291])
|
|
|
|
|
* 0200:5EFF:FE00:5213 (Proxy Mobile IPv6 [RFC6543])
|
|
|
|
|
* 0200:5EFF:FE00:5214 - 0200:5EFF:FEFF:FFFF (Reserved IPv6 Interface Identifiers corresponding to the IANA Ethernet Block [RFC4291]) */
|
|
|
|
|
if (memcmp (iid, (const guint8[]) { 0x02, 0x00, 0x5E, 0xFF, 0xFE }, 5) == 0)
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
/* FDFF:FFFF:FFFF:FF80 - FDFF:FFFF:FFFF:FFFF (Reserved Subnet Anycast Addresses [RFC2526]) */
|
|
|
|
|
if (memcmp (iid, (const guint8[]) { 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, 7) == 0) {
|
|
|
|
|
if (iid[7] & 0x80)
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
static gboolean
|
2016-12-18 14:03:38 +01:00
|
|
|
_set_stable_privacy (NMUtilsStableType stable_type,
|
2016-06-21 18:07:56 +02:00
|
|
|
struct in6_addr *addr,
|
2016-03-01 09:56:51 +01:00
|
|
|
const char *ifname,
|
2016-06-21 18:07:00 +02:00
|
|
|
const char *network_id,
|
2017-04-27 13:58:38 +02:00
|
|
|
guint32 dad_counter,
|
2018-12-12 10:10:38 +01:00
|
|
|
const guint8 *host_id,
|
|
|
|
|
gsize host_id_len,
|
2016-03-01 09:56:51 +01:00
|
|
|
GError **error)
|
|
|
|
|
{
|
2018-11-01 12:52:38 +01:00
|
|
|
nm_auto_free_checksum GChecksum *sum = NULL;
|
|
|
|
|
guint8 digest[NM_UTILS_CHECKSUM_LENGTH_SHA256];
|
2016-03-01 09:56:51 +01:00
|
|
|
guint32 tmp[2];
|
|
|
|
|
|
2018-12-12 10:10:38 +01:00
|
|
|
nm_assert (host_id_len);
|
2016-12-18 14:03:38 +01:00
|
|
|
nm_assert (network_id);
|
2016-03-01 09:56:51 +01:00
|
|
|
|
|
|
|
|
sum = g_checksum_new (G_CHECKSUM_SHA256);
|
|
|
|
|
|
2018-12-12 10:10:38 +01:00
|
|
|
host_id_len = MIN (host_id_len, G_MAXUINT32);
|
2016-03-01 09:56:51 +01:00
|
|
|
|
2016-06-21 18:07:56 +02:00
|
|
|
if (stable_type != NM_UTILS_STABLE_TYPE_UUID) {
|
2016-12-18 14:03:38 +01:00
|
|
|
guint8 stable_type_uint8;
|
|
|
|
|
|
|
|
|
|
nm_assert (stable_type < (NMUtilsStableType) 255);
|
|
|
|
|
stable_type_uint8 = (guint8) stable_type;
|
|
|
|
|
|
2016-06-21 18:07:56 +02:00
|
|
|
/* Preferably, we would always like to include the stable-type,
|
|
|
|
|
* but for backward compatibility reasons, we cannot for UUID.
|
|
|
|
|
*
|
|
|
|
|
* That is no real problem and it is still impossible to
|
|
|
|
|
* force a collision here, because of how the remaining
|
2018-12-12 10:10:38 +01:00
|
|
|
* fields are hashed. That is, as we also hash @host_id_len
|
2016-06-21 18:07:56 +02:00
|
|
|
* and the terminating '\0' of @network_id, it is unambigiously
|
|
|
|
|
* possible to revert the process and deduce the @stable_type.
|
|
|
|
|
*/
|
2016-12-18 14:03:38 +01:00
|
|
|
g_checksum_update (sum, &stable_type_uint8, sizeof (stable_type_uint8));
|
2016-06-21 18:07:56 +02:00
|
|
|
}
|
|
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
g_checksum_update (sum, addr->s6_addr, 8);
|
|
|
|
|
g_checksum_update (sum, (const guchar *) ifname, strlen (ifname) + 1);
|
2016-06-21 18:07:00 +02:00
|
|
|
g_checksum_update (sum, (const guchar *) network_id, strlen (network_id) + 1);
|
2016-03-01 09:56:51 +01:00
|
|
|
tmp[0] = htonl (dad_counter);
|
2018-12-12 10:10:38 +01:00
|
|
|
tmp[1] = htonl (host_id_len);
|
2016-03-01 09:56:51 +01:00
|
|
|
g_checksum_update (sum, (const guchar *) tmp, sizeof (tmp));
|
2018-12-12 10:10:38 +01:00
|
|
|
g_checksum_update (sum, (const guchar *) host_id, host_id_len);
|
2018-11-01 12:52:38 +01:00
|
|
|
nm_utils_checksum_get_digest (sum, digest);
|
core: avoid generating reserved IPv6 interface identifiers
https://tools.ietf.org/html/rfc7217 says:
The resulting Interface Identifier SHOULD be compared against the
reserved IPv6 Interface Identifiers [RFC5453] [IANA-RESERVED-IID]
and against those Interface Identifiers already employed in an
address of the same network interface and the same network
prefix. In the event that an unacceptable identifier has been
generated, this situation SHOULD be handled in the same way as
the case of duplicate addresses (see Section 6).
In case of conflict, this suggests to create a new address incrementing
the DAD counter, etc. Don't do that. If we generate an address of the
reserved region, just rehash it right away. Note that the actual address
anyway appears random, so this re-hashing is just as good as incrementing
the DAD counter and going through the entire process again.
Note that now we no longer generate certain addresses like we did
previously. But realize that we now merely reject (1 + 16777216 + 128)
addresses out of 2^64. So, the likelyhood of of a user accidentally
generating an address that is suddenly rejected is in the order of
10e-13 (1 / 1,099,503,173,697). Which is not astronomically, but still
extreeeemely unlikely.
Also, the whole process is anyway build on the idea that somebody else
might generate conflicting addresses (DAD). It means, there was always
the extremely tiny chance that the address you generated last time is
suddenly taken by somebody else. So, this change appears to a user
like these reserved addresses are now claimed by another (non existing)
host and a different address gets generated -- business as usual, as
far as SLAAC is concerned.
(cherry picked from commit f15c4961ad02a1edf766f140dd94e2d5449f8d82)
2017-04-27 12:38:40 +02:00
|
|
|
|
|
|
|
|
while (_is_reserved_ipv6_iid (digest)) {
|
|
|
|
|
g_checksum_reset (sum);
|
|
|
|
|
tmp[0] = htonl (++dad_counter);
|
2018-11-01 12:52:38 +01:00
|
|
|
g_checksum_update (sum, digest, sizeof (digest));
|
core: avoid generating reserved IPv6 interface identifiers
https://tools.ietf.org/html/rfc7217 says:
The resulting Interface Identifier SHOULD be compared against the
reserved IPv6 Interface Identifiers [RFC5453] [IANA-RESERVED-IID]
and against those Interface Identifiers already employed in an
address of the same network interface and the same network
prefix. In the event that an unacceptable identifier has been
generated, this situation SHOULD be handled in the same way as
the case of duplicate addresses (see Section 6).
In case of conflict, this suggests to create a new address incrementing
the DAD counter, etc. Don't do that. If we generate an address of the
reserved region, just rehash it right away. Note that the actual address
anyway appears random, so this re-hashing is just as good as incrementing
the DAD counter and going through the entire process again.
Note that now we no longer generate certain addresses like we did
previously. But realize that we now merely reject (1 + 16777216 + 128)
addresses out of 2^64. So, the likelyhood of of a user accidentally
generating an address that is suddenly rejected is in the order of
10e-13 (1 / 1,099,503,173,697). Which is not astronomically, but still
extreeeemely unlikely.
Also, the whole process is anyway build on the idea that somebody else
might generate conflicting addresses (DAD). It means, there was always
the extremely tiny chance that the address you generated last time is
suddenly taken by somebody else. So, this change appears to a user
like these reserved addresses are now claimed by another (non existing)
host and a different address gets generated -- business as usual, as
far as SLAAC is concerned.
(cherry picked from commit f15c4961ad02a1edf766f140dd94e2d5449f8d82)
2017-04-27 12:38:40 +02:00
|
|
|
g_checksum_update (sum, (const guchar *) &tmp[0], sizeof (tmp[0]));
|
2018-11-01 12:52:38 +01:00
|
|
|
nm_utils_checksum_get_digest (sum, digest);
|
core: avoid generating reserved IPv6 interface identifiers
https://tools.ietf.org/html/rfc7217 says:
The resulting Interface Identifier SHOULD be compared against the
reserved IPv6 Interface Identifiers [RFC5453] [IANA-RESERVED-IID]
and against those Interface Identifiers already employed in an
address of the same network interface and the same network
prefix. In the event that an unacceptable identifier has been
generated, this situation SHOULD be handled in the same way as
the case of duplicate addresses (see Section 6).
In case of conflict, this suggests to create a new address incrementing
the DAD counter, etc. Don't do that. If we generate an address of the
reserved region, just rehash it right away. Note that the actual address
anyway appears random, so this re-hashing is just as good as incrementing
the DAD counter and going through the entire process again.
Note that now we no longer generate certain addresses like we did
previously. But realize that we now merely reject (1 + 16777216 + 128)
addresses out of 2^64. So, the likelyhood of of a user accidentally
generating an address that is suddenly rejected is in the order of
10e-13 (1 / 1,099,503,173,697). Which is not astronomically, but still
extreeeemely unlikely.
Also, the whole process is anyway build on the idea that somebody else
might generate conflicting addresses (DAD). It means, there was always
the extremely tiny chance that the address you generated last time is
suddenly taken by somebody else. So, this change appears to a user
like these reserved addresses are now claimed by another (non existing)
host and a different address gets generated -- business as usual, as
far as SLAAC is concerned.
(cherry picked from commit f15c4961ad02a1edf766f140dd94e2d5449f8d82)
2017-04-27 12:38:40 +02:00
|
|
|
}
|
|
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
memcpy (addr->s6_addr + 8, &digest[0], 8);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-18 11:54:58 +02:00
|
|
|
gboolean
|
2016-12-18 14:03:38 +01:00
|
|
|
nm_utils_ipv6_addr_set_stable_privacy_impl (NMUtilsStableType stable_type,
|
2016-10-18 11:54:58 +02:00
|
|
|
struct in6_addr *addr,
|
|
|
|
|
const char *ifname,
|
|
|
|
|
const char *network_id,
|
2017-04-27 13:58:38 +02:00
|
|
|
guint32 dad_counter,
|
2018-12-12 10:10:38 +01:00
|
|
|
guint8 *host_id,
|
|
|
|
|
gsize host_id_len,
|
2016-10-18 11:54:58 +02:00
|
|
|
GError **error)
|
|
|
|
|
{
|
2018-12-12 10:10:38 +01:00
|
|
|
return _set_stable_privacy (stable_type, addr, ifname, network_id, dad_counter, host_id, host_id_len, error);
|
2016-10-18 11:54:58 +02:00
|
|
|
}
|
|
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
#define RFC7217_IDGEN_RETRIES 3
|
|
|
|
|
/**
|
|
|
|
|
* nm_utils_ipv6_addr_set_stable_privacy:
|
|
|
|
|
*
|
|
|
|
|
* Extend the address prefix with an interface identifier using the
|
|
|
|
|
* RFC 7217 Stable Privacy mechanism.
|
|
|
|
|
*
|
|
|
|
|
* Returns: %TRUE on success, %FALSE if the address could not be generated.
|
|
|
|
|
*/
|
|
|
|
|
gboolean
|
2016-06-21 18:07:56 +02:00
|
|
|
nm_utils_ipv6_addr_set_stable_privacy (NMUtilsStableType stable_type,
|
|
|
|
|
struct in6_addr *addr,
|
2016-03-01 09:56:51 +01:00
|
|
|
const char *ifname,
|
2016-06-21 18:07:00 +02:00
|
|
|
const char *network_id,
|
2017-04-27 13:58:38 +02:00
|
|
|
guint32 dad_counter,
|
2016-03-01 09:56:51 +01:00
|
|
|
GError **error)
|
|
|
|
|
{
|
2018-12-12 10:10:38 +01:00
|
|
|
const guint8 *host_id;
|
|
|
|
|
gsize host_id_len;
|
2016-03-01 09:56:51 +01:00
|
|
|
|
2016-12-18 14:03:38 +01:00
|
|
|
g_return_val_if_fail (network_id, FALSE);
|
2016-06-21 18:07:56 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
if (dad_counter >= RFC7217_IDGEN_RETRIES) {
|
|
|
|
|
g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
|
|
|
|
|
"Too many DAD collisions");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-12 10:10:38 +01:00
|
|
|
nm_utils_host_id_get (&host_id, &host_id_len);
|
2016-03-01 09:56:51 +01:00
|
|
|
|
2016-06-21 18:07:56 +02:00
|
|
|
return _set_stable_privacy (stable_type, addr, ifname, network_id, dad_counter,
|
2018-12-12 10:10:38 +01:00
|
|
|
host_id, host_id_len, error);
|
2016-03-01 09:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
device: extend MAC address handling including randomization for ethernet and wifi
Extend the "ethernet.cloned-mac-address" and "wifi.cloned-mac-address"
settings. Instead of specifying an explicit MAC address, the additional
special values "permanent", "preserve", "random", "random-bia", "stable" and
"stable-bia" are supported.
"permanent" means to use the permanent hardware address. Previously that
was the default if no explict cloned-mac-address was set. The default is
thus still "permanent", but it can be overwritten by global
configuration.
"preserve" means not to configure the MAC address when activating the
device. That was actually the default behavior before introducing MAC
address handling with commit 1b49f941a69af910b0e68530be7339e8053068e5.
"random" and "random-bia" use a randomized MAC address for each
connection. "stable" and "stable-bia" use a generated, stable
address based on some token. The "bia" suffix says to generate a
burned-in address. The stable method by default uses as token the
connection UUID, but the token can be explicitly choosen via
"stable:<TOKEN>" and "stable-bia:<TOKEN>".
On a D-Bus level, the "cloned-mac-address" is a bytestring and thus
cannot express the new forms. It is replaced by the new
"assigned-mac-address" field. For the GObject property, libnm's API,
nmcli, keyfile, etc. the old name "cloned-mac-address" is still used.
Deprecating the old field seems more complicated then just extending
the use of the existing "cloned-mac-address" field, although the name
doesn't match well with the extended meaning.
There is some overlap with the "wifi.mac-address-randomization" setting.
https://bugzilla.gnome.org/show_bug.cgi?id=705545
https://bugzilla.gnome.org/show_bug.cgi?id=708820
https://bugzilla.gnome.org/show_bug.cgi?id=758301
2016-05-24 15:57:16 +02:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static void
|
all: make MAC address randomization algorithm configurable
For the per-connection settings "ethernet.cloned-mac-address"
and "wifi.cloned-mac-address", and for the per-device setting
"wifi.scan-rand-mac-address", we may generate MAC addresses using
either the "random" or "stable" algorithm.
Add new properties "generate-mac-address-mask" that allow to configure
which bits of the MAC address will be scrambled.
By default, the "random" and "stable" algorithms scamble all bits
of the MAC address, including the OUI part and generate a locally-
administered, unicast address.
By specifying a MAC address mask, we can now configure to perserve
parts of the current MAC address of the device. For example, setting
"FF:FF:FF:00:00:00" will preserve the first 3 octects of the current
MAC address.
One can also explicitly specify a MAC address to use instead of the
current MAC address. For example, "FF:FF:FF:00:00:00 68:F7:28:00:00:00"
sets the OUI part of the MAC address to "68:F7:28" while scrambling
the last 3 octects.
Similarly, "02:00:00:00:00:00 00:00:00:00:00:00" will scamble
all bits of the MAC address, except clearing the second-least
significant bit. Thus, creating a burned-in address, globally
administered.
One can also supply a list of MAC addresses like
"FF:FF:FF:00:00:00 68:F7:28:00:00:00 00:0C:29:00:00:00 ..." in which
case a MAC address is choosen randomly.
To fully scamble the MAC address one can configure
"02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00".
which also randomly creates either a locally or globally administered
address.
With this, the following macchanger options can be implemented:
`macchanger --random`
This is the default if no mask is configured.
-> ""
while is the same as:
-> "00:00:00:00:00:00"
-> "02:00:00:00:00:00 02:00:00:00:00:00"
`macchanger --random --bia`
-> "02:00:00:00:00:00 00:00:00:00:00:00"
`macchanger --ending`
This option cannot be fully implemented, because macchanger
uses the current MAC address but also implies --bia.
-> "FF:FF:FF:00:00:00"
This would yields the same result only if the current MAC address
is already a burned-in address too. Otherwise, it has not the same
effect as --ending.
-> "FF:FF:FF:00:00:00 <MAC_ADDR>"
Alternatively, instead of using the current MAC address,
spell the OUI part out. But again, that is not really the
same as macchanger does because you explictly have to name
the OUI part to use.
`machanger --another`
`machanger --another_any`
-> "FF:FF:FF:00:00:00 <MAC_ADDR> <MAC_ADDR> ..."
"$(printf "FF:FF:FF:00:00:00 %s\n" "$(sed -n 's/^\([0-9a-fA-F][0-9a-fA-F]\) \([0-9a-fA-F][0-9a-fA-F]\) \([0-9a-fA-F][0-9a-fA-F]\) .*/\1:\2:\3:00:00:00/p' /usr/share/macchanger/wireless.list | xargs)")"
2016-06-22 20:31:39 +02:00
|
|
|
_hw_addr_eth_complete (struct ether_addr *addr,
|
|
|
|
|
const char *current_mac_address,
|
|
|
|
|
const char *generate_mac_address_mask)
|
|
|
|
|
{
|
|
|
|
|
struct ether_addr mask;
|
|
|
|
|
struct ether_addr oui;
|
|
|
|
|
struct ether_addr *ouis;
|
|
|
|
|
gsize ouis_len;
|
|
|
|
|
guint i;
|
device: extend MAC address handling including randomization for ethernet and wifi
Extend the "ethernet.cloned-mac-address" and "wifi.cloned-mac-address"
settings. Instead of specifying an explicit MAC address, the additional
special values "permanent", "preserve", "random", "random-bia", "stable" and
"stable-bia" are supported.
"permanent" means to use the permanent hardware address. Previously that
was the default if no explict cloned-mac-address was set. The default is
thus still "permanent", but it can be overwritten by global
configuration.
"preserve" means not to configure the MAC address when activating the
device. That was actually the default behavior before introducing MAC
address handling with commit 1b49f941a69af910b0e68530be7339e8053068e5.
"random" and "random-bia" use a randomized MAC address for each
connection. "stable" and "stable-bia" use a generated, stable
address based on some token. The "bia" suffix says to generate a
burned-in address. The stable method by default uses as token the
connection UUID, but the token can be explicitly choosen via
"stable:<TOKEN>" and "stable-bia:<TOKEN>".
On a D-Bus level, the "cloned-mac-address" is a bytestring and thus
cannot express the new forms. It is replaced by the new
"assigned-mac-address" field. For the GObject property, libnm's API,
nmcli, keyfile, etc. the old name "cloned-mac-address" is still used.
Deprecating the old field seems more complicated then just extending
the use of the existing "cloned-mac-address" field, although the name
doesn't match well with the extended meaning.
There is some overlap with the "wifi.mac-address-randomization" setting.
https://bugzilla.gnome.org/show_bug.cgi?id=705545
https://bugzilla.gnome.org/show_bug.cgi?id=708820
https://bugzilla.gnome.org/show_bug.cgi?id=758301
2016-05-24 15:57:16 +02:00
|
|
|
|
|
|
|
|
/* the second LSB of the first octet means
|
|
|
|
|
* "globally unique, OUI enforced, BIA (burned-in-address)"
|
all: make MAC address randomization algorithm configurable
For the per-connection settings "ethernet.cloned-mac-address"
and "wifi.cloned-mac-address", and for the per-device setting
"wifi.scan-rand-mac-address", we may generate MAC addresses using
either the "random" or "stable" algorithm.
Add new properties "generate-mac-address-mask" that allow to configure
which bits of the MAC address will be scrambled.
By default, the "random" and "stable" algorithms scamble all bits
of the MAC address, including the OUI part and generate a locally-
administered, unicast address.
By specifying a MAC address mask, we can now configure to perserve
parts of the current MAC address of the device. For example, setting
"FF:FF:FF:00:00:00" will preserve the first 3 octects of the current
MAC address.
One can also explicitly specify a MAC address to use instead of the
current MAC address. For example, "FF:FF:FF:00:00:00 68:F7:28:00:00:00"
sets the OUI part of the MAC address to "68:F7:28" while scrambling
the last 3 octects.
Similarly, "02:00:00:00:00:00 00:00:00:00:00:00" will scamble
all bits of the MAC address, except clearing the second-least
significant bit. Thus, creating a burned-in address, globally
administered.
One can also supply a list of MAC addresses like
"FF:FF:FF:00:00:00 68:F7:28:00:00:00 00:0C:29:00:00:00 ..." in which
case a MAC address is choosen randomly.
To fully scamble the MAC address one can configure
"02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00".
which also randomly creates either a locally or globally administered
address.
With this, the following macchanger options can be implemented:
`macchanger --random`
This is the default if no mask is configured.
-> ""
while is the same as:
-> "00:00:00:00:00:00"
-> "02:00:00:00:00:00 02:00:00:00:00:00"
`macchanger --random --bia`
-> "02:00:00:00:00:00 00:00:00:00:00:00"
`macchanger --ending`
This option cannot be fully implemented, because macchanger
uses the current MAC address but also implies --bia.
-> "FF:FF:FF:00:00:00"
This would yields the same result only if the current MAC address
is already a burned-in address too. Otherwise, it has not the same
effect as --ending.
-> "FF:FF:FF:00:00:00 <MAC_ADDR>"
Alternatively, instead of using the current MAC address,
spell the OUI part out. But again, that is not really the
same as macchanger does because you explictly have to name
the OUI part to use.
`machanger --another`
`machanger --another_any`
-> "FF:FF:FF:00:00:00 <MAC_ADDR> <MAC_ADDR> ..."
"$(printf "FF:FF:FF:00:00:00 %s\n" "$(sed -n 's/^\([0-9a-fA-F][0-9a-fA-F]\) \([0-9a-fA-F][0-9a-fA-F]\) \([0-9a-fA-F][0-9a-fA-F]\) .*/\1:\2:\3:00:00:00/p' /usr/share/macchanger/wireless.list | xargs)")"
2016-06-22 20:31:39 +02:00
|
|
|
* vs. "locally-administered". By default, set it to
|
|
|
|
|
* generate locally-administered addresses.
|
|
|
|
|
*
|
|
|
|
|
* Maybe be overwritten by a mask below. */
|
|
|
|
|
addr->ether_addr_octet[0] |= 2;
|
|
|
|
|
|
|
|
|
|
if (!generate_mac_address_mask || !*generate_mac_address_mask)
|
|
|
|
|
goto out;
|
|
|
|
|
if (!_nm_utils_generate_mac_address_mask_parse (generate_mac_address_mask,
|
|
|
|
|
&mask,
|
|
|
|
|
&ouis,
|
|
|
|
|
&ouis_len,
|
|
|
|
|
NULL))
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
nm_assert ((ouis == NULL) ^ (ouis_len != 0));
|
|
|
|
|
if (ouis) {
|
|
|
|
|
/* g_random_int() is good enough here. It uses a static GRand instance
|
|
|
|
|
* that is seeded from /dev/urandom. */
|
|
|
|
|
oui = ouis[g_random_int () % ouis_len];
|
|
|
|
|
g_free (ouis);
|
|
|
|
|
} else {
|
|
|
|
|
if (!nm_utils_hwaddr_aton (current_mac_address, &oui, ETH_ALEN))
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < ETH_ALEN; i++) {
|
|
|
|
|
const guint8 a = addr->ether_addr_octet[i];
|
|
|
|
|
const guint8 o = oui.ether_addr_octet[i];
|
|
|
|
|
const guint8 m = mask.ether_addr_octet[i];
|
|
|
|
|
|
|
|
|
|
addr->ether_addr_octet[i] = (a & ~m) | (o & m);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
/* The LSB of the first octet must always be cleared,
|
|
|
|
|
* it means Unicast vs. Multicast */
|
|
|
|
|
addr->ether_addr_octet[0] &= ~1;
|
device: extend MAC address handling including randomization for ethernet and wifi
Extend the "ethernet.cloned-mac-address" and "wifi.cloned-mac-address"
settings. Instead of specifying an explicit MAC address, the additional
special values "permanent", "preserve", "random", "random-bia", "stable" and
"stable-bia" are supported.
"permanent" means to use the permanent hardware address. Previously that
was the default if no explict cloned-mac-address was set. The default is
thus still "permanent", but it can be overwritten by global
configuration.
"preserve" means not to configure the MAC address when activating the
device. That was actually the default behavior before introducing MAC
address handling with commit 1b49f941a69af910b0e68530be7339e8053068e5.
"random" and "random-bia" use a randomized MAC address for each
connection. "stable" and "stable-bia" use a generated, stable
address based on some token. The "bia" suffix says to generate a
burned-in address. The stable method by default uses as token the
connection UUID, but the token can be explicitly choosen via
"stable:<TOKEN>" and "stable-bia:<TOKEN>".
On a D-Bus level, the "cloned-mac-address" is a bytestring and thus
cannot express the new forms. It is replaced by the new
"assigned-mac-address" field. For the GObject property, libnm's API,
nmcli, keyfile, etc. the old name "cloned-mac-address" is still used.
Deprecating the old field seems more complicated then just extending
the use of the existing "cloned-mac-address" field, although the name
doesn't match well with the extended meaning.
There is some overlap with the "wifi.mac-address-randomization" setting.
https://bugzilla.gnome.org/show_bug.cgi?id=705545
https://bugzilla.gnome.org/show_bug.cgi?id=708820
https://bugzilla.gnome.org/show_bug.cgi?id=758301
2016-05-24 15:57:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
all: make MAC address randomization algorithm configurable
For the per-connection settings "ethernet.cloned-mac-address"
and "wifi.cloned-mac-address", and for the per-device setting
"wifi.scan-rand-mac-address", we may generate MAC addresses using
either the "random" or "stable" algorithm.
Add new properties "generate-mac-address-mask" that allow to configure
which bits of the MAC address will be scrambled.
By default, the "random" and "stable" algorithms scamble all bits
of the MAC address, including the OUI part and generate a locally-
administered, unicast address.
By specifying a MAC address mask, we can now configure to perserve
parts of the current MAC address of the device. For example, setting
"FF:FF:FF:00:00:00" will preserve the first 3 octects of the current
MAC address.
One can also explicitly specify a MAC address to use instead of the
current MAC address. For example, "FF:FF:FF:00:00:00 68:F7:28:00:00:00"
sets the OUI part of the MAC address to "68:F7:28" while scrambling
the last 3 octects.
Similarly, "02:00:00:00:00:00 00:00:00:00:00:00" will scamble
all bits of the MAC address, except clearing the second-least
significant bit. Thus, creating a burned-in address, globally
administered.
One can also supply a list of MAC addresses like
"FF:FF:FF:00:00:00 68:F7:28:00:00:00 00:0C:29:00:00:00 ..." in which
case a MAC address is choosen randomly.
To fully scamble the MAC address one can configure
"02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00".
which also randomly creates either a locally or globally administered
address.
With this, the following macchanger options can be implemented:
`macchanger --random`
This is the default if no mask is configured.
-> ""
while is the same as:
-> "00:00:00:00:00:00"
-> "02:00:00:00:00:00 02:00:00:00:00:00"
`macchanger --random --bia`
-> "02:00:00:00:00:00 00:00:00:00:00:00"
`macchanger --ending`
This option cannot be fully implemented, because macchanger
uses the current MAC address but also implies --bia.
-> "FF:FF:FF:00:00:00"
This would yields the same result only if the current MAC address
is already a burned-in address too. Otherwise, it has not the same
effect as --ending.
-> "FF:FF:FF:00:00:00 <MAC_ADDR>"
Alternatively, instead of using the current MAC address,
spell the OUI part out. But again, that is not really the
same as macchanger does because you explictly have to name
the OUI part to use.
`machanger --another`
`machanger --another_any`
-> "FF:FF:FF:00:00:00 <MAC_ADDR> <MAC_ADDR> ..."
"$(printf "FF:FF:FF:00:00:00 %s\n" "$(sed -n 's/^\([0-9a-fA-F][0-9a-fA-F]\) \([0-9a-fA-F][0-9a-fA-F]\) \([0-9a-fA-F][0-9a-fA-F]\) .*/\1:\2:\3:00:00:00/p' /usr/share/macchanger/wireless.list | xargs)")"
2016-06-22 20:31:39 +02:00
|
|
|
nm_utils_hw_addr_gen_random_eth (const char *current_mac_address,
|
|
|
|
|
const char *generate_mac_address_mask)
|
device: extend MAC address handling including randomization for ethernet and wifi
Extend the "ethernet.cloned-mac-address" and "wifi.cloned-mac-address"
settings. Instead of specifying an explicit MAC address, the additional
special values "permanent", "preserve", "random", "random-bia", "stable" and
"stable-bia" are supported.
"permanent" means to use the permanent hardware address. Previously that
was the default if no explict cloned-mac-address was set. The default is
thus still "permanent", but it can be overwritten by global
configuration.
"preserve" means not to configure the MAC address when activating the
device. That was actually the default behavior before introducing MAC
address handling with commit 1b49f941a69af910b0e68530be7339e8053068e5.
"random" and "random-bia" use a randomized MAC address for each
connection. "stable" and "stable-bia" use a generated, stable
address based on some token. The "bia" suffix says to generate a
burned-in address. The stable method by default uses as token the
connection UUID, but the token can be explicitly choosen via
"stable:<TOKEN>" and "stable-bia:<TOKEN>".
On a D-Bus level, the "cloned-mac-address" is a bytestring and thus
cannot express the new forms. It is replaced by the new
"assigned-mac-address" field. For the GObject property, libnm's API,
nmcli, keyfile, etc. the old name "cloned-mac-address" is still used.
Deprecating the old field seems more complicated then just extending
the use of the existing "cloned-mac-address" field, although the name
doesn't match well with the extended meaning.
There is some overlap with the "wifi.mac-address-randomization" setting.
https://bugzilla.gnome.org/show_bug.cgi?id=705545
https://bugzilla.gnome.org/show_bug.cgi?id=708820
https://bugzilla.gnome.org/show_bug.cgi?id=758301
2016-05-24 15:57:16 +02:00
|
|
|
{
|
all: make MAC address randomization algorithm configurable
For the per-connection settings "ethernet.cloned-mac-address"
and "wifi.cloned-mac-address", and for the per-device setting
"wifi.scan-rand-mac-address", we may generate MAC addresses using
either the "random" or "stable" algorithm.
Add new properties "generate-mac-address-mask" that allow to configure
which bits of the MAC address will be scrambled.
By default, the "random" and "stable" algorithms scamble all bits
of the MAC address, including the OUI part and generate a locally-
administered, unicast address.
By specifying a MAC address mask, we can now configure to perserve
parts of the current MAC address of the device. For example, setting
"FF:FF:FF:00:00:00" will preserve the first 3 octects of the current
MAC address.
One can also explicitly specify a MAC address to use instead of the
current MAC address. For example, "FF:FF:FF:00:00:00 68:F7:28:00:00:00"
sets the OUI part of the MAC address to "68:F7:28" while scrambling
the last 3 octects.
Similarly, "02:00:00:00:00:00 00:00:00:00:00:00" will scamble
all bits of the MAC address, except clearing the second-least
significant bit. Thus, creating a burned-in address, globally
administered.
One can also supply a list of MAC addresses like
"FF:FF:FF:00:00:00 68:F7:28:00:00:00 00:0C:29:00:00:00 ..." in which
case a MAC address is choosen randomly.
To fully scamble the MAC address one can configure
"02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00".
which also randomly creates either a locally or globally administered
address.
With this, the following macchanger options can be implemented:
`macchanger --random`
This is the default if no mask is configured.
-> ""
while is the same as:
-> "00:00:00:00:00:00"
-> "02:00:00:00:00:00 02:00:00:00:00:00"
`macchanger --random --bia`
-> "02:00:00:00:00:00 00:00:00:00:00:00"
`macchanger --ending`
This option cannot be fully implemented, because macchanger
uses the current MAC address but also implies --bia.
-> "FF:FF:FF:00:00:00"
This would yields the same result only if the current MAC address
is already a burned-in address too. Otherwise, it has not the same
effect as --ending.
-> "FF:FF:FF:00:00:00 <MAC_ADDR>"
Alternatively, instead of using the current MAC address,
spell the OUI part out. But again, that is not really the
same as macchanger does because you explictly have to name
the OUI part to use.
`machanger --another`
`machanger --another_any`
-> "FF:FF:FF:00:00:00 <MAC_ADDR> <MAC_ADDR> ..."
"$(printf "FF:FF:FF:00:00:00 %s\n" "$(sed -n 's/^\([0-9a-fA-F][0-9a-fA-F]\) \([0-9a-fA-F][0-9a-fA-F]\) \([0-9a-fA-F][0-9a-fA-F]\) .*/\1:\2:\3:00:00:00/p' /usr/share/macchanger/wireless.list | xargs)")"
2016-06-22 20:31:39 +02:00
|
|
|
struct ether_addr bin_addr;
|
device: extend MAC address handling including randomization for ethernet and wifi
Extend the "ethernet.cloned-mac-address" and "wifi.cloned-mac-address"
settings. Instead of specifying an explicit MAC address, the additional
special values "permanent", "preserve", "random", "random-bia", "stable" and
"stable-bia" are supported.
"permanent" means to use the permanent hardware address. Previously that
was the default if no explict cloned-mac-address was set. The default is
thus still "permanent", but it can be overwritten by global
configuration.
"preserve" means not to configure the MAC address when activating the
device. That was actually the default behavior before introducing MAC
address handling with commit 1b49f941a69af910b0e68530be7339e8053068e5.
"random" and "random-bia" use a randomized MAC address for each
connection. "stable" and "stable-bia" use a generated, stable
address based on some token. The "bia" suffix says to generate a
burned-in address. The stable method by default uses as token the
connection UUID, but the token can be explicitly choosen via
"stable:<TOKEN>" and "stable-bia:<TOKEN>".
On a D-Bus level, the "cloned-mac-address" is a bytestring and thus
cannot express the new forms. It is replaced by the new
"assigned-mac-address" field. For the GObject property, libnm's API,
nmcli, keyfile, etc. the old name "cloned-mac-address" is still used.
Deprecating the old field seems more complicated then just extending
the use of the existing "cloned-mac-address" field, although the name
doesn't match well with the extended meaning.
There is some overlap with the "wifi.mac-address-randomization" setting.
https://bugzilla.gnome.org/show_bug.cgi?id=705545
https://bugzilla.gnome.org/show_bug.cgi?id=708820
https://bugzilla.gnome.org/show_bug.cgi?id=758301
2016-05-24 15:57:16 +02:00
|
|
|
|
2017-10-12 15:43:21 +02:00
|
|
|
nm_utils_random_bytes (&bin_addr, ETH_ALEN);
|
all: make MAC address randomization algorithm configurable
For the per-connection settings "ethernet.cloned-mac-address"
and "wifi.cloned-mac-address", and for the per-device setting
"wifi.scan-rand-mac-address", we may generate MAC addresses using
either the "random" or "stable" algorithm.
Add new properties "generate-mac-address-mask" that allow to configure
which bits of the MAC address will be scrambled.
By default, the "random" and "stable" algorithms scamble all bits
of the MAC address, including the OUI part and generate a locally-
administered, unicast address.
By specifying a MAC address mask, we can now configure to perserve
parts of the current MAC address of the device. For example, setting
"FF:FF:FF:00:00:00" will preserve the first 3 octects of the current
MAC address.
One can also explicitly specify a MAC address to use instead of the
current MAC address. For example, "FF:FF:FF:00:00:00 68:F7:28:00:00:00"
sets the OUI part of the MAC address to "68:F7:28" while scrambling
the last 3 octects.
Similarly, "02:00:00:00:00:00 00:00:00:00:00:00" will scamble
all bits of the MAC address, except clearing the second-least
significant bit. Thus, creating a burned-in address, globally
administered.
One can also supply a list of MAC addresses like
"FF:FF:FF:00:00:00 68:F7:28:00:00:00 00:0C:29:00:00:00 ..." in which
case a MAC address is choosen randomly.
To fully scamble the MAC address one can configure
"02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00".
which also randomly creates either a locally or globally administered
address.
With this, the following macchanger options can be implemented:
`macchanger --random`
This is the default if no mask is configured.
-> ""
while is the same as:
-> "00:00:00:00:00:00"
-> "02:00:00:00:00:00 02:00:00:00:00:00"
`macchanger --random --bia`
-> "02:00:00:00:00:00 00:00:00:00:00:00"
`macchanger --ending`
This option cannot be fully implemented, because macchanger
uses the current MAC address but also implies --bia.
-> "FF:FF:FF:00:00:00"
This would yields the same result only if the current MAC address
is already a burned-in address too. Otherwise, it has not the same
effect as --ending.
-> "FF:FF:FF:00:00:00 <MAC_ADDR>"
Alternatively, instead of using the current MAC address,
spell the OUI part out. But again, that is not really the
same as macchanger does because you explictly have to name
the OUI part to use.
`machanger --another`
`machanger --another_any`
-> "FF:FF:FF:00:00:00 <MAC_ADDR> <MAC_ADDR> ..."
"$(printf "FF:FF:FF:00:00:00 %s\n" "$(sed -n 's/^\([0-9a-fA-F][0-9a-fA-F]\) \([0-9a-fA-F][0-9a-fA-F]\) \([0-9a-fA-F][0-9a-fA-F]\) .*/\1:\2:\3:00:00:00/p' /usr/share/macchanger/wireless.list | xargs)")"
2016-06-22 20:31:39 +02:00
|
|
|
_hw_addr_eth_complete (&bin_addr, current_mac_address, generate_mac_address_mask);
|
|
|
|
|
return nm_utils_hwaddr_ntoa (&bin_addr, ETH_ALEN);
|
device: extend MAC address handling including randomization for ethernet and wifi
Extend the "ethernet.cloned-mac-address" and "wifi.cloned-mac-address"
settings. Instead of specifying an explicit MAC address, the additional
special values "permanent", "preserve", "random", "random-bia", "stable" and
"stable-bia" are supported.
"permanent" means to use the permanent hardware address. Previously that
was the default if no explict cloned-mac-address was set. The default is
thus still "permanent", but it can be overwritten by global
configuration.
"preserve" means not to configure the MAC address when activating the
device. That was actually the default behavior before introducing MAC
address handling with commit 1b49f941a69af910b0e68530be7339e8053068e5.
"random" and "random-bia" use a randomized MAC address for each
connection. "stable" and "stable-bia" use a generated, stable
address based on some token. The "bia" suffix says to generate a
burned-in address. The stable method by default uses as token the
connection UUID, but the token can be explicitly choosen via
"stable:<TOKEN>" and "stable-bia:<TOKEN>".
On a D-Bus level, the "cloned-mac-address" is a bytestring and thus
cannot express the new forms. It is replaced by the new
"assigned-mac-address" field. For the GObject property, libnm's API,
nmcli, keyfile, etc. the old name "cloned-mac-address" is still used.
Deprecating the old field seems more complicated then just extending
the use of the existing "cloned-mac-address" field, although the name
doesn't match well with the extended meaning.
There is some overlap with the "wifi.mac-address-randomization" setting.
https://bugzilla.gnome.org/show_bug.cgi?id=705545
https://bugzilla.gnome.org/show_bug.cgi?id=708820
https://bugzilla.gnome.org/show_bug.cgi?id=758301
2016-05-24 15:57:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
_hw_addr_gen_stable_eth (NMUtilsStableType stable_type,
|
|
|
|
|
const char *stable_id,
|
2018-12-12 10:10:38 +01:00
|
|
|
const guint8 *host_id,
|
|
|
|
|
gsize host_id_len,
|
all: make MAC address randomization algorithm configurable
For the per-connection settings "ethernet.cloned-mac-address"
and "wifi.cloned-mac-address", and for the per-device setting
"wifi.scan-rand-mac-address", we may generate MAC addresses using
either the "random" or "stable" algorithm.
Add new properties "generate-mac-address-mask" that allow to configure
which bits of the MAC address will be scrambled.
By default, the "random" and "stable" algorithms scamble all bits
of the MAC address, including the OUI part and generate a locally-
administered, unicast address.
By specifying a MAC address mask, we can now configure to perserve
parts of the current MAC address of the device. For example, setting
"FF:FF:FF:00:00:00" will preserve the first 3 octects of the current
MAC address.
One can also explicitly specify a MAC address to use instead of the
current MAC address. For example, "FF:FF:FF:00:00:00 68:F7:28:00:00:00"
sets the OUI part of the MAC address to "68:F7:28" while scrambling
the last 3 octects.
Similarly, "02:00:00:00:00:00 00:00:00:00:00:00" will scamble
all bits of the MAC address, except clearing the second-least
significant bit. Thus, creating a burned-in address, globally
administered.
One can also supply a list of MAC addresses like
"FF:FF:FF:00:00:00 68:F7:28:00:00:00 00:0C:29:00:00:00 ..." in which
case a MAC address is choosen randomly.
To fully scamble the MAC address one can configure
"02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00".
which also randomly creates either a locally or globally administered
address.
With this, the following macchanger options can be implemented:
`macchanger --random`
This is the default if no mask is configured.
-> ""
while is the same as:
-> "00:00:00:00:00:00"
-> "02:00:00:00:00:00 02:00:00:00:00:00"
`macchanger --random --bia`
-> "02:00:00:00:00:00 00:00:00:00:00:00"
`macchanger --ending`
This option cannot be fully implemented, because macchanger
uses the current MAC address but also implies --bia.
-> "FF:FF:FF:00:00:00"
This would yields the same result only if the current MAC address
is already a burned-in address too. Otherwise, it has not the same
effect as --ending.
-> "FF:FF:FF:00:00:00 <MAC_ADDR>"
Alternatively, instead of using the current MAC address,
spell the OUI part out. But again, that is not really the
same as macchanger does because you explictly have to name
the OUI part to use.
`machanger --another`
`machanger --another_any`
-> "FF:FF:FF:00:00:00 <MAC_ADDR> <MAC_ADDR> ..."
"$(printf "FF:FF:FF:00:00:00 %s\n" "$(sed -n 's/^\([0-9a-fA-F][0-9a-fA-F]\) \([0-9a-fA-F][0-9a-fA-F]\) \([0-9a-fA-F][0-9a-fA-F]\) .*/\1:\2:\3:00:00:00/p' /usr/share/macchanger/wireless.list | xargs)")"
2016-06-22 20:31:39 +02:00
|
|
|
const char *ifname,
|
|
|
|
|
const char *current_mac_address,
|
|
|
|
|
const char *generate_mac_address_mask)
|
device: extend MAC address handling including randomization for ethernet and wifi
Extend the "ethernet.cloned-mac-address" and "wifi.cloned-mac-address"
settings. Instead of specifying an explicit MAC address, the additional
special values "permanent", "preserve", "random", "random-bia", "stable" and
"stable-bia" are supported.
"permanent" means to use the permanent hardware address. Previously that
was the default if no explict cloned-mac-address was set. The default is
thus still "permanent", but it can be overwritten by global
configuration.
"preserve" means not to configure the MAC address when activating the
device. That was actually the default behavior before introducing MAC
address handling with commit 1b49f941a69af910b0e68530be7339e8053068e5.
"random" and "random-bia" use a randomized MAC address for each
connection. "stable" and "stable-bia" use a generated, stable
address based on some token. The "bia" suffix says to generate a
burned-in address. The stable method by default uses as token the
connection UUID, but the token can be explicitly choosen via
"stable:<TOKEN>" and "stable-bia:<TOKEN>".
On a D-Bus level, the "cloned-mac-address" is a bytestring and thus
cannot express the new forms. It is replaced by the new
"assigned-mac-address" field. For the GObject property, libnm's API,
nmcli, keyfile, etc. the old name "cloned-mac-address" is still used.
Deprecating the old field seems more complicated then just extending
the use of the existing "cloned-mac-address" field, although the name
doesn't match well with the extended meaning.
There is some overlap with the "wifi.mac-address-randomization" setting.
https://bugzilla.gnome.org/show_bug.cgi?id=705545
https://bugzilla.gnome.org/show_bug.cgi?id=708820
https://bugzilla.gnome.org/show_bug.cgi?id=758301
2016-05-24 15:57:16 +02:00
|
|
|
{
|
2018-11-01 12:52:38 +01:00
|
|
|
nm_auto_free_checksum GChecksum *sum = NULL;
|
device: extend MAC address handling including randomization for ethernet and wifi
Extend the "ethernet.cloned-mac-address" and "wifi.cloned-mac-address"
settings. Instead of specifying an explicit MAC address, the additional
special values "permanent", "preserve", "random", "random-bia", "stable" and
"stable-bia" are supported.
"permanent" means to use the permanent hardware address. Previously that
was the default if no explict cloned-mac-address was set. The default is
thus still "permanent", but it can be overwritten by global
configuration.
"preserve" means not to configure the MAC address when activating the
device. That was actually the default behavior before introducing MAC
address handling with commit 1b49f941a69af910b0e68530be7339e8053068e5.
"random" and "random-bia" use a randomized MAC address for each
connection. "stable" and "stable-bia" use a generated, stable
address based on some token. The "bia" suffix says to generate a
burned-in address. The stable method by default uses as token the
connection UUID, but the token can be explicitly choosen via
"stable:<TOKEN>" and "stable-bia:<TOKEN>".
On a D-Bus level, the "cloned-mac-address" is a bytestring and thus
cannot express the new forms. It is replaced by the new
"assigned-mac-address" field. For the GObject property, libnm's API,
nmcli, keyfile, etc. the old name "cloned-mac-address" is still used.
Deprecating the old field seems more complicated then just extending
the use of the existing "cloned-mac-address" field, although the name
doesn't match well with the extended meaning.
There is some overlap with the "wifi.mac-address-randomization" setting.
https://bugzilla.gnome.org/show_bug.cgi?id=705545
https://bugzilla.gnome.org/show_bug.cgi?id=708820
https://bugzilla.gnome.org/show_bug.cgi?id=758301
2016-05-24 15:57:16 +02:00
|
|
|
guint32 tmp;
|
2018-11-01 12:52:38 +01:00
|
|
|
guint8 digest[NM_UTILS_CHECKSUM_LENGTH_SHA256];
|
all: make MAC address randomization algorithm configurable
For the per-connection settings "ethernet.cloned-mac-address"
and "wifi.cloned-mac-address", and for the per-device setting
"wifi.scan-rand-mac-address", we may generate MAC addresses using
either the "random" or "stable" algorithm.
Add new properties "generate-mac-address-mask" that allow to configure
which bits of the MAC address will be scrambled.
By default, the "random" and "stable" algorithms scamble all bits
of the MAC address, including the OUI part and generate a locally-
administered, unicast address.
By specifying a MAC address mask, we can now configure to perserve
parts of the current MAC address of the device. For example, setting
"FF:FF:FF:00:00:00" will preserve the first 3 octects of the current
MAC address.
One can also explicitly specify a MAC address to use instead of the
current MAC address. For example, "FF:FF:FF:00:00:00 68:F7:28:00:00:00"
sets the OUI part of the MAC address to "68:F7:28" while scrambling
the last 3 octects.
Similarly, "02:00:00:00:00:00 00:00:00:00:00:00" will scamble
all bits of the MAC address, except clearing the second-least
significant bit. Thus, creating a burned-in address, globally
administered.
One can also supply a list of MAC addresses like
"FF:FF:FF:00:00:00 68:F7:28:00:00:00 00:0C:29:00:00:00 ..." in which
case a MAC address is choosen randomly.
To fully scamble the MAC address one can configure
"02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00".
which also randomly creates either a locally or globally administered
address.
With this, the following macchanger options can be implemented:
`macchanger --random`
This is the default if no mask is configured.
-> ""
while is the same as:
-> "00:00:00:00:00:00"
-> "02:00:00:00:00:00 02:00:00:00:00:00"
`macchanger --random --bia`
-> "02:00:00:00:00:00 00:00:00:00:00:00"
`macchanger --ending`
This option cannot be fully implemented, because macchanger
uses the current MAC address but also implies --bia.
-> "FF:FF:FF:00:00:00"
This would yields the same result only if the current MAC address
is already a burned-in address too. Otherwise, it has not the same
effect as --ending.
-> "FF:FF:FF:00:00:00 <MAC_ADDR>"
Alternatively, instead of using the current MAC address,
spell the OUI part out. But again, that is not really the
same as macchanger does because you explictly have to name
the OUI part to use.
`machanger --another`
`machanger --another_any`
-> "FF:FF:FF:00:00:00 <MAC_ADDR> <MAC_ADDR> ..."
"$(printf "FF:FF:FF:00:00:00 %s\n" "$(sed -n 's/^\([0-9a-fA-F][0-9a-fA-F]\) \([0-9a-fA-F][0-9a-fA-F]\) \([0-9a-fA-F][0-9a-fA-F]\) .*/\1:\2:\3:00:00:00/p' /usr/share/macchanger/wireless.list | xargs)")"
2016-06-22 20:31:39 +02:00
|
|
|
struct ether_addr bin_addr;
|
device: extend MAC address handling including randomization for ethernet and wifi
Extend the "ethernet.cloned-mac-address" and "wifi.cloned-mac-address"
settings. Instead of specifying an explicit MAC address, the additional
special values "permanent", "preserve", "random", "random-bia", "stable" and
"stable-bia" are supported.
"permanent" means to use the permanent hardware address. Previously that
was the default if no explict cloned-mac-address was set. The default is
thus still "permanent", but it can be overwritten by global
configuration.
"preserve" means not to configure the MAC address when activating the
device. That was actually the default behavior before introducing MAC
address handling with commit 1b49f941a69af910b0e68530be7339e8053068e5.
"random" and "random-bia" use a randomized MAC address for each
connection. "stable" and "stable-bia" use a generated, stable
address based on some token. The "bia" suffix says to generate a
burned-in address. The stable method by default uses as token the
connection UUID, but the token can be explicitly choosen via
"stable:<TOKEN>" and "stable-bia:<TOKEN>".
On a D-Bus level, the "cloned-mac-address" is a bytestring and thus
cannot express the new forms. It is replaced by the new
"assigned-mac-address" field. For the GObject property, libnm's API,
nmcli, keyfile, etc. the old name "cloned-mac-address" is still used.
Deprecating the old field seems more complicated then just extending
the use of the existing "cloned-mac-address" field, although the name
doesn't match well with the extended meaning.
There is some overlap with the "wifi.mac-address-randomization" setting.
https://bugzilla.gnome.org/show_bug.cgi?id=705545
https://bugzilla.gnome.org/show_bug.cgi?id=708820
https://bugzilla.gnome.org/show_bug.cgi?id=758301
2016-05-24 15:57:16 +02:00
|
|
|
guint8 stable_type_uint8;
|
|
|
|
|
|
|
|
|
|
nm_assert (stable_id);
|
2018-12-12 10:10:38 +01:00
|
|
|
nm_assert (host_id);
|
device: extend MAC address handling including randomization for ethernet and wifi
Extend the "ethernet.cloned-mac-address" and "wifi.cloned-mac-address"
settings. Instead of specifying an explicit MAC address, the additional
special values "permanent", "preserve", "random", "random-bia", "stable" and
"stable-bia" are supported.
"permanent" means to use the permanent hardware address. Previously that
was the default if no explict cloned-mac-address was set. The default is
thus still "permanent", but it can be overwritten by global
configuration.
"preserve" means not to configure the MAC address when activating the
device. That was actually the default behavior before introducing MAC
address handling with commit 1b49f941a69af910b0e68530be7339e8053068e5.
"random" and "random-bia" use a randomized MAC address for each
connection. "stable" and "stable-bia" use a generated, stable
address based on some token. The "bia" suffix says to generate a
burned-in address. The stable method by default uses as token the
connection UUID, but the token can be explicitly choosen via
"stable:<TOKEN>" and "stable-bia:<TOKEN>".
On a D-Bus level, the "cloned-mac-address" is a bytestring and thus
cannot express the new forms. It is replaced by the new
"assigned-mac-address" field. For the GObject property, libnm's API,
nmcli, keyfile, etc. the old name "cloned-mac-address" is still used.
Deprecating the old field seems more complicated then just extending
the use of the existing "cloned-mac-address" field, although the name
doesn't match well with the extended meaning.
There is some overlap with the "wifi.mac-address-randomization" setting.
https://bugzilla.gnome.org/show_bug.cgi?id=705545
https://bugzilla.gnome.org/show_bug.cgi?id=708820
https://bugzilla.gnome.org/show_bug.cgi?id=758301
2016-05-24 15:57:16 +02:00
|
|
|
|
|
|
|
|
sum = g_checksum_new (G_CHECKSUM_SHA256);
|
|
|
|
|
|
2018-12-12 10:10:38 +01:00
|
|
|
host_id_len = MIN (host_id_len, G_MAXUINT32);
|
device: extend MAC address handling including randomization for ethernet and wifi
Extend the "ethernet.cloned-mac-address" and "wifi.cloned-mac-address"
settings. Instead of specifying an explicit MAC address, the additional
special values "permanent", "preserve", "random", "random-bia", "stable" and
"stable-bia" are supported.
"permanent" means to use the permanent hardware address. Previously that
was the default if no explict cloned-mac-address was set. The default is
thus still "permanent", but it can be overwritten by global
configuration.
"preserve" means not to configure the MAC address when activating the
device. That was actually the default behavior before introducing MAC
address handling with commit 1b49f941a69af910b0e68530be7339e8053068e5.
"random" and "random-bia" use a randomized MAC address for each
connection. "stable" and "stable-bia" use a generated, stable
address based on some token. The "bia" suffix says to generate a
burned-in address. The stable method by default uses as token the
connection UUID, but the token can be explicitly choosen via
"stable:<TOKEN>" and "stable-bia:<TOKEN>".
On a D-Bus level, the "cloned-mac-address" is a bytestring and thus
cannot express the new forms. It is replaced by the new
"assigned-mac-address" field. For the GObject property, libnm's API,
nmcli, keyfile, etc. the old name "cloned-mac-address" is still used.
Deprecating the old field seems more complicated then just extending
the use of the existing "cloned-mac-address" field, although the name
doesn't match well with the extended meaning.
There is some overlap with the "wifi.mac-address-randomization" setting.
https://bugzilla.gnome.org/show_bug.cgi?id=705545
https://bugzilla.gnome.org/show_bug.cgi?id=708820
https://bugzilla.gnome.org/show_bug.cgi?id=758301
2016-05-24 15:57:16 +02:00
|
|
|
|
2016-12-18 14:03:38 +01:00
|
|
|
nm_assert (stable_type < (NMUtilsStableType) 255);
|
device: extend MAC address handling including randomization for ethernet and wifi
Extend the "ethernet.cloned-mac-address" and "wifi.cloned-mac-address"
settings. Instead of specifying an explicit MAC address, the additional
special values "permanent", "preserve", "random", "random-bia", "stable" and
"stable-bia" are supported.
"permanent" means to use the permanent hardware address. Previously that
was the default if no explict cloned-mac-address was set. The default is
thus still "permanent", but it can be overwritten by global
configuration.
"preserve" means not to configure the MAC address when activating the
device. That was actually the default behavior before introducing MAC
address handling with commit 1b49f941a69af910b0e68530be7339e8053068e5.
"random" and "random-bia" use a randomized MAC address for each
connection. "stable" and "stable-bia" use a generated, stable
address based on some token. The "bia" suffix says to generate a
burned-in address. The stable method by default uses as token the
connection UUID, but the token can be explicitly choosen via
"stable:<TOKEN>" and "stable-bia:<TOKEN>".
On a D-Bus level, the "cloned-mac-address" is a bytestring and thus
cannot express the new forms. It is replaced by the new
"assigned-mac-address" field. For the GObject property, libnm's API,
nmcli, keyfile, etc. the old name "cloned-mac-address" is still used.
Deprecating the old field seems more complicated then just extending
the use of the existing "cloned-mac-address" field, although the name
doesn't match well with the extended meaning.
There is some overlap with the "wifi.mac-address-randomization" setting.
https://bugzilla.gnome.org/show_bug.cgi?id=705545
https://bugzilla.gnome.org/show_bug.cgi?id=708820
https://bugzilla.gnome.org/show_bug.cgi?id=758301
2016-05-24 15:57:16 +02:00
|
|
|
stable_type_uint8 = stable_type;
|
|
|
|
|
g_checksum_update (sum, (const guchar *) &stable_type_uint8, sizeof (stable_type_uint8));
|
|
|
|
|
|
2018-12-12 10:10:38 +01:00
|
|
|
tmp = htonl ((guint32) host_id_len);
|
device: extend MAC address handling including randomization for ethernet and wifi
Extend the "ethernet.cloned-mac-address" and "wifi.cloned-mac-address"
settings. Instead of specifying an explicit MAC address, the additional
special values "permanent", "preserve", "random", "random-bia", "stable" and
"stable-bia" are supported.
"permanent" means to use the permanent hardware address. Previously that
was the default if no explict cloned-mac-address was set. The default is
thus still "permanent", but it can be overwritten by global
configuration.
"preserve" means not to configure the MAC address when activating the
device. That was actually the default behavior before introducing MAC
address handling with commit 1b49f941a69af910b0e68530be7339e8053068e5.
"random" and "random-bia" use a randomized MAC address for each
connection. "stable" and "stable-bia" use a generated, stable
address based on some token. The "bia" suffix says to generate a
burned-in address. The stable method by default uses as token the
connection UUID, but the token can be explicitly choosen via
"stable:<TOKEN>" and "stable-bia:<TOKEN>".
On a D-Bus level, the "cloned-mac-address" is a bytestring and thus
cannot express the new forms. It is replaced by the new
"assigned-mac-address" field. For the GObject property, libnm's API,
nmcli, keyfile, etc. the old name "cloned-mac-address" is still used.
Deprecating the old field seems more complicated then just extending
the use of the existing "cloned-mac-address" field, although the name
doesn't match well with the extended meaning.
There is some overlap with the "wifi.mac-address-randomization" setting.
https://bugzilla.gnome.org/show_bug.cgi?id=705545
https://bugzilla.gnome.org/show_bug.cgi?id=708820
https://bugzilla.gnome.org/show_bug.cgi?id=758301
2016-05-24 15:57:16 +02:00
|
|
|
g_checksum_update (sum, (const guchar *) &tmp, sizeof (tmp));
|
2018-12-12 10:10:38 +01:00
|
|
|
g_checksum_update (sum, (const guchar *) host_id, host_id_len);
|
device: extend MAC address handling including randomization for ethernet and wifi
Extend the "ethernet.cloned-mac-address" and "wifi.cloned-mac-address"
settings. Instead of specifying an explicit MAC address, the additional
special values "permanent", "preserve", "random", "random-bia", "stable" and
"stable-bia" are supported.
"permanent" means to use the permanent hardware address. Previously that
was the default if no explict cloned-mac-address was set. The default is
thus still "permanent", but it can be overwritten by global
configuration.
"preserve" means not to configure the MAC address when activating the
device. That was actually the default behavior before introducing MAC
address handling with commit 1b49f941a69af910b0e68530be7339e8053068e5.
"random" and "random-bia" use a randomized MAC address for each
connection. "stable" and "stable-bia" use a generated, stable
address based on some token. The "bia" suffix says to generate a
burned-in address. The stable method by default uses as token the
connection UUID, but the token can be explicitly choosen via
"stable:<TOKEN>" and "stable-bia:<TOKEN>".
On a D-Bus level, the "cloned-mac-address" is a bytestring and thus
cannot express the new forms. It is replaced by the new
"assigned-mac-address" field. For the GObject property, libnm's API,
nmcli, keyfile, etc. the old name "cloned-mac-address" is still used.
Deprecating the old field seems more complicated then just extending
the use of the existing "cloned-mac-address" field, although the name
doesn't match well with the extended meaning.
There is some overlap with the "wifi.mac-address-randomization" setting.
https://bugzilla.gnome.org/show_bug.cgi?id=705545
https://bugzilla.gnome.org/show_bug.cgi?id=708820
https://bugzilla.gnome.org/show_bug.cgi?id=758301
2016-05-24 15:57:16 +02:00
|
|
|
g_checksum_update (sum, (const guchar *) (ifname ?: ""), ifname ? (strlen (ifname) + 1) : 1);
|
|
|
|
|
g_checksum_update (sum, (const guchar *) stable_id, strlen (stable_id) + 1);
|
|
|
|
|
|
2018-11-01 12:52:38 +01:00
|
|
|
nm_utils_checksum_get_digest (sum, digest);
|
device: extend MAC address handling including randomization for ethernet and wifi
Extend the "ethernet.cloned-mac-address" and "wifi.cloned-mac-address"
settings. Instead of specifying an explicit MAC address, the additional
special values "permanent", "preserve", "random", "random-bia", "stable" and
"stable-bia" are supported.
"permanent" means to use the permanent hardware address. Previously that
was the default if no explict cloned-mac-address was set. The default is
thus still "permanent", but it can be overwritten by global
configuration.
"preserve" means not to configure the MAC address when activating the
device. That was actually the default behavior before introducing MAC
address handling with commit 1b49f941a69af910b0e68530be7339e8053068e5.
"random" and "random-bia" use a randomized MAC address for each
connection. "stable" and "stable-bia" use a generated, stable
address based on some token. The "bia" suffix says to generate a
burned-in address. The stable method by default uses as token the
connection UUID, but the token can be explicitly choosen via
"stable:<TOKEN>" and "stable-bia:<TOKEN>".
On a D-Bus level, the "cloned-mac-address" is a bytestring and thus
cannot express the new forms. It is replaced by the new
"assigned-mac-address" field. For the GObject property, libnm's API,
nmcli, keyfile, etc. the old name "cloned-mac-address" is still used.
Deprecating the old field seems more complicated then just extending
the use of the existing "cloned-mac-address" field, although the name
doesn't match well with the extended meaning.
There is some overlap with the "wifi.mac-address-randomization" setting.
https://bugzilla.gnome.org/show_bug.cgi?id=705545
https://bugzilla.gnome.org/show_bug.cgi?id=708820
https://bugzilla.gnome.org/show_bug.cgi?id=758301
2016-05-24 15:57:16 +02:00
|
|
|
|
all: make MAC address randomization algorithm configurable
For the per-connection settings "ethernet.cloned-mac-address"
and "wifi.cloned-mac-address", and for the per-device setting
"wifi.scan-rand-mac-address", we may generate MAC addresses using
either the "random" or "stable" algorithm.
Add new properties "generate-mac-address-mask" that allow to configure
which bits of the MAC address will be scrambled.
By default, the "random" and "stable" algorithms scamble all bits
of the MAC address, including the OUI part and generate a locally-
administered, unicast address.
By specifying a MAC address mask, we can now configure to perserve
parts of the current MAC address of the device. For example, setting
"FF:FF:FF:00:00:00" will preserve the first 3 octects of the current
MAC address.
One can also explicitly specify a MAC address to use instead of the
current MAC address. For example, "FF:FF:FF:00:00:00 68:F7:28:00:00:00"
sets the OUI part of the MAC address to "68:F7:28" while scrambling
the last 3 octects.
Similarly, "02:00:00:00:00:00 00:00:00:00:00:00" will scamble
all bits of the MAC address, except clearing the second-least
significant bit. Thus, creating a burned-in address, globally
administered.
One can also supply a list of MAC addresses like
"FF:FF:FF:00:00:00 68:F7:28:00:00:00 00:0C:29:00:00:00 ..." in which
case a MAC address is choosen randomly.
To fully scamble the MAC address one can configure
"02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00".
which also randomly creates either a locally or globally administered
address.
With this, the following macchanger options can be implemented:
`macchanger --random`
This is the default if no mask is configured.
-> ""
while is the same as:
-> "00:00:00:00:00:00"
-> "02:00:00:00:00:00 02:00:00:00:00:00"
`macchanger --random --bia`
-> "02:00:00:00:00:00 00:00:00:00:00:00"
`macchanger --ending`
This option cannot be fully implemented, because macchanger
uses the current MAC address but also implies --bia.
-> "FF:FF:FF:00:00:00"
This would yields the same result only if the current MAC address
is already a burned-in address too. Otherwise, it has not the same
effect as --ending.
-> "FF:FF:FF:00:00:00 <MAC_ADDR>"
Alternatively, instead of using the current MAC address,
spell the OUI part out. But again, that is not really the
same as macchanger does because you explictly have to name
the OUI part to use.
`machanger --another`
`machanger --another_any`
-> "FF:FF:FF:00:00:00 <MAC_ADDR> <MAC_ADDR> ..."
"$(printf "FF:FF:FF:00:00:00 %s\n" "$(sed -n 's/^\([0-9a-fA-F][0-9a-fA-F]\) \([0-9a-fA-F][0-9a-fA-F]\) \([0-9a-fA-F][0-9a-fA-F]\) .*/\1:\2:\3:00:00:00/p' /usr/share/macchanger/wireless.list | xargs)")"
2016-06-22 20:31:39 +02:00
|
|
|
memcpy (&bin_addr, digest, ETH_ALEN);
|
|
|
|
|
_hw_addr_eth_complete (&bin_addr, current_mac_address, generate_mac_address_mask);
|
|
|
|
|
return nm_utils_hwaddr_ntoa (&bin_addr, ETH_ALEN);
|
device: extend MAC address handling including randomization for ethernet and wifi
Extend the "ethernet.cloned-mac-address" and "wifi.cloned-mac-address"
settings. Instead of specifying an explicit MAC address, the additional
special values "permanent", "preserve", "random", "random-bia", "stable" and
"stable-bia" are supported.
"permanent" means to use the permanent hardware address. Previously that
was the default if no explict cloned-mac-address was set. The default is
thus still "permanent", but it can be overwritten by global
configuration.
"preserve" means not to configure the MAC address when activating the
device. That was actually the default behavior before introducing MAC
address handling with commit 1b49f941a69af910b0e68530be7339e8053068e5.
"random" and "random-bia" use a randomized MAC address for each
connection. "stable" and "stable-bia" use a generated, stable
address based on some token. The "bia" suffix says to generate a
burned-in address. The stable method by default uses as token the
connection UUID, but the token can be explicitly choosen via
"stable:<TOKEN>" and "stable-bia:<TOKEN>".
On a D-Bus level, the "cloned-mac-address" is a bytestring and thus
cannot express the new forms. It is replaced by the new
"assigned-mac-address" field. For the GObject property, libnm's API,
nmcli, keyfile, etc. the old name "cloned-mac-address" is still used.
Deprecating the old field seems more complicated then just extending
the use of the existing "cloned-mac-address" field, although the name
doesn't match well with the extended meaning.
There is some overlap with the "wifi.mac-address-randomization" setting.
https://bugzilla.gnome.org/show_bug.cgi?id=705545
https://bugzilla.gnome.org/show_bug.cgi?id=708820
https://bugzilla.gnome.org/show_bug.cgi?id=758301
2016-05-24 15:57:16 +02:00
|
|
|
}
|
|
|
|
|
|
2016-10-18 11:54:58 +02:00
|
|
|
char *
|
|
|
|
|
nm_utils_hw_addr_gen_stable_eth_impl (NMUtilsStableType stable_type,
|
|
|
|
|
const char *stable_id,
|
2018-12-12 10:10:38 +01:00
|
|
|
const guint8 *host_id,
|
|
|
|
|
gsize host_id_len,
|
2016-10-18 11:54:58 +02:00
|
|
|
const char *ifname,
|
|
|
|
|
const char *current_mac_address,
|
|
|
|
|
const char *generate_mac_address_mask)
|
|
|
|
|
{
|
2018-12-12 10:10:38 +01:00
|
|
|
return _hw_addr_gen_stable_eth (stable_type, stable_id, host_id, host_id_len, ifname, current_mac_address, generate_mac_address_mask);
|
2016-10-18 11:54:58 +02:00
|
|
|
}
|
|
|
|
|
|
device: extend MAC address handling including randomization for ethernet and wifi
Extend the "ethernet.cloned-mac-address" and "wifi.cloned-mac-address"
settings. Instead of specifying an explicit MAC address, the additional
special values "permanent", "preserve", "random", "random-bia", "stable" and
"stable-bia" are supported.
"permanent" means to use the permanent hardware address. Previously that
was the default if no explict cloned-mac-address was set. The default is
thus still "permanent", but it can be overwritten by global
configuration.
"preserve" means not to configure the MAC address when activating the
device. That was actually the default behavior before introducing MAC
address handling with commit 1b49f941a69af910b0e68530be7339e8053068e5.
"random" and "random-bia" use a randomized MAC address for each
connection. "stable" and "stable-bia" use a generated, stable
address based on some token. The "bia" suffix says to generate a
burned-in address. The stable method by default uses as token the
connection UUID, but the token can be explicitly choosen via
"stable:<TOKEN>" and "stable-bia:<TOKEN>".
On a D-Bus level, the "cloned-mac-address" is a bytestring and thus
cannot express the new forms. It is replaced by the new
"assigned-mac-address" field. For the GObject property, libnm's API,
nmcli, keyfile, etc. the old name "cloned-mac-address" is still used.
Deprecating the old field seems more complicated then just extending
the use of the existing "cloned-mac-address" field, although the name
doesn't match well with the extended meaning.
There is some overlap with the "wifi.mac-address-randomization" setting.
https://bugzilla.gnome.org/show_bug.cgi?id=705545
https://bugzilla.gnome.org/show_bug.cgi?id=708820
https://bugzilla.gnome.org/show_bug.cgi?id=758301
2016-05-24 15:57:16 +02:00
|
|
|
char *
|
|
|
|
|
nm_utils_hw_addr_gen_stable_eth (NMUtilsStableType stable_type,
|
|
|
|
|
const char *stable_id,
|
all: make MAC address randomization algorithm configurable
For the per-connection settings "ethernet.cloned-mac-address"
and "wifi.cloned-mac-address", and for the per-device setting
"wifi.scan-rand-mac-address", we may generate MAC addresses using
either the "random" or "stable" algorithm.
Add new properties "generate-mac-address-mask" that allow to configure
which bits of the MAC address will be scrambled.
By default, the "random" and "stable" algorithms scamble all bits
of the MAC address, including the OUI part and generate a locally-
administered, unicast address.
By specifying a MAC address mask, we can now configure to perserve
parts of the current MAC address of the device. For example, setting
"FF:FF:FF:00:00:00" will preserve the first 3 octects of the current
MAC address.
One can also explicitly specify a MAC address to use instead of the
current MAC address. For example, "FF:FF:FF:00:00:00 68:F7:28:00:00:00"
sets the OUI part of the MAC address to "68:F7:28" while scrambling
the last 3 octects.
Similarly, "02:00:00:00:00:00 00:00:00:00:00:00" will scamble
all bits of the MAC address, except clearing the second-least
significant bit. Thus, creating a burned-in address, globally
administered.
One can also supply a list of MAC addresses like
"FF:FF:FF:00:00:00 68:F7:28:00:00:00 00:0C:29:00:00:00 ..." in which
case a MAC address is choosen randomly.
To fully scamble the MAC address one can configure
"02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00".
which also randomly creates either a locally or globally administered
address.
With this, the following macchanger options can be implemented:
`macchanger --random`
This is the default if no mask is configured.
-> ""
while is the same as:
-> "00:00:00:00:00:00"
-> "02:00:00:00:00:00 02:00:00:00:00:00"
`macchanger --random --bia`
-> "02:00:00:00:00:00 00:00:00:00:00:00"
`macchanger --ending`
This option cannot be fully implemented, because macchanger
uses the current MAC address but also implies --bia.
-> "FF:FF:FF:00:00:00"
This would yields the same result only if the current MAC address
is already a burned-in address too. Otherwise, it has not the same
effect as --ending.
-> "FF:FF:FF:00:00:00 <MAC_ADDR>"
Alternatively, instead of using the current MAC address,
spell the OUI part out. But again, that is not really the
same as macchanger does because you explictly have to name
the OUI part to use.
`machanger --another`
`machanger --another_any`
-> "FF:FF:FF:00:00:00 <MAC_ADDR> <MAC_ADDR> ..."
"$(printf "FF:FF:FF:00:00:00 %s\n" "$(sed -n 's/^\([0-9a-fA-F][0-9a-fA-F]\) \([0-9a-fA-F][0-9a-fA-F]\) \([0-9a-fA-F][0-9a-fA-F]\) .*/\1:\2:\3:00:00:00/p' /usr/share/macchanger/wireless.list | xargs)")"
2016-06-22 20:31:39 +02:00
|
|
|
const char *ifname,
|
|
|
|
|
const char *current_mac_address,
|
|
|
|
|
const char *generate_mac_address_mask)
|
device: extend MAC address handling including randomization for ethernet and wifi
Extend the "ethernet.cloned-mac-address" and "wifi.cloned-mac-address"
settings. Instead of specifying an explicit MAC address, the additional
special values "permanent", "preserve", "random", "random-bia", "stable" and
"stable-bia" are supported.
"permanent" means to use the permanent hardware address. Previously that
was the default if no explict cloned-mac-address was set. The default is
thus still "permanent", but it can be overwritten by global
configuration.
"preserve" means not to configure the MAC address when activating the
device. That was actually the default behavior before introducing MAC
address handling with commit 1b49f941a69af910b0e68530be7339e8053068e5.
"random" and "random-bia" use a randomized MAC address for each
connection. "stable" and "stable-bia" use a generated, stable
address based on some token. The "bia" suffix says to generate a
burned-in address. The stable method by default uses as token the
connection UUID, but the token can be explicitly choosen via
"stable:<TOKEN>" and "stable-bia:<TOKEN>".
On a D-Bus level, the "cloned-mac-address" is a bytestring and thus
cannot express the new forms. It is replaced by the new
"assigned-mac-address" field. For the GObject property, libnm's API,
nmcli, keyfile, etc. the old name "cloned-mac-address" is still used.
Deprecating the old field seems more complicated then just extending
the use of the existing "cloned-mac-address" field, although the name
doesn't match well with the extended meaning.
There is some overlap with the "wifi.mac-address-randomization" setting.
https://bugzilla.gnome.org/show_bug.cgi?id=705545
https://bugzilla.gnome.org/show_bug.cgi?id=708820
https://bugzilla.gnome.org/show_bug.cgi?id=758301
2016-05-24 15:57:16 +02:00
|
|
|
{
|
2018-12-12 10:10:38 +01:00
|
|
|
const guint8 *host_id;
|
|
|
|
|
gsize host_id_len;
|
device: extend MAC address handling including randomization for ethernet and wifi
Extend the "ethernet.cloned-mac-address" and "wifi.cloned-mac-address"
settings. Instead of specifying an explicit MAC address, the additional
special values "permanent", "preserve", "random", "random-bia", "stable" and
"stable-bia" are supported.
"permanent" means to use the permanent hardware address. Previously that
was the default if no explict cloned-mac-address was set. The default is
thus still "permanent", but it can be overwritten by global
configuration.
"preserve" means not to configure the MAC address when activating the
device. That was actually the default behavior before introducing MAC
address handling with commit 1b49f941a69af910b0e68530be7339e8053068e5.
"random" and "random-bia" use a randomized MAC address for each
connection. "stable" and "stable-bia" use a generated, stable
address based on some token. The "bia" suffix says to generate a
burned-in address. The stable method by default uses as token the
connection UUID, but the token can be explicitly choosen via
"stable:<TOKEN>" and "stable-bia:<TOKEN>".
On a D-Bus level, the "cloned-mac-address" is a bytestring and thus
cannot express the new forms. It is replaced by the new
"assigned-mac-address" field. For the GObject property, libnm's API,
nmcli, keyfile, etc. the old name "cloned-mac-address" is still used.
Deprecating the old field seems more complicated then just extending
the use of the existing "cloned-mac-address" field, although the name
doesn't match well with the extended meaning.
There is some overlap with the "wifi.mac-address-randomization" setting.
https://bugzilla.gnome.org/show_bug.cgi?id=705545
https://bugzilla.gnome.org/show_bug.cgi?id=708820
https://bugzilla.gnome.org/show_bug.cgi?id=758301
2016-05-24 15:57:16 +02:00
|
|
|
|
|
|
|
|
g_return_val_if_fail (stable_id, NULL);
|
|
|
|
|
|
2018-12-12 10:10:38 +01:00
|
|
|
nm_utils_host_id_get (&host_id, &host_id_len);
|
device: extend MAC address handling including randomization for ethernet and wifi
Extend the "ethernet.cloned-mac-address" and "wifi.cloned-mac-address"
settings. Instead of specifying an explicit MAC address, the additional
special values "permanent", "preserve", "random", "random-bia", "stable" and
"stable-bia" are supported.
"permanent" means to use the permanent hardware address. Previously that
was the default if no explict cloned-mac-address was set. The default is
thus still "permanent", but it can be overwritten by global
configuration.
"preserve" means not to configure the MAC address when activating the
device. That was actually the default behavior before introducing MAC
address handling with commit 1b49f941a69af910b0e68530be7339e8053068e5.
"random" and "random-bia" use a randomized MAC address for each
connection. "stable" and "stable-bia" use a generated, stable
address based on some token. The "bia" suffix says to generate a
burned-in address. The stable method by default uses as token the
connection UUID, but the token can be explicitly choosen via
"stable:<TOKEN>" and "stable-bia:<TOKEN>".
On a D-Bus level, the "cloned-mac-address" is a bytestring and thus
cannot express the new forms. It is replaced by the new
"assigned-mac-address" field. For the GObject property, libnm's API,
nmcli, keyfile, etc. the old name "cloned-mac-address" is still used.
Deprecating the old field seems more complicated then just extending
the use of the existing "cloned-mac-address" field, although the name
doesn't match well with the extended meaning.
There is some overlap with the "wifi.mac-address-randomization" setting.
https://bugzilla.gnome.org/show_bug.cgi?id=705545
https://bugzilla.gnome.org/show_bug.cgi?id=708820
https://bugzilla.gnome.org/show_bug.cgi?id=758301
2016-05-24 15:57:16 +02:00
|
|
|
|
|
|
|
|
return _hw_addr_gen_stable_eth (stable_type,
|
|
|
|
|
stable_id,
|
2018-12-12 10:10:38 +01:00
|
|
|
host_id,
|
|
|
|
|
host_id_len,
|
all: make MAC address randomization algorithm configurable
For the per-connection settings "ethernet.cloned-mac-address"
and "wifi.cloned-mac-address", and for the per-device setting
"wifi.scan-rand-mac-address", we may generate MAC addresses using
either the "random" or "stable" algorithm.
Add new properties "generate-mac-address-mask" that allow to configure
which bits of the MAC address will be scrambled.
By default, the "random" and "stable" algorithms scamble all bits
of the MAC address, including the OUI part and generate a locally-
administered, unicast address.
By specifying a MAC address mask, we can now configure to perserve
parts of the current MAC address of the device. For example, setting
"FF:FF:FF:00:00:00" will preserve the first 3 octects of the current
MAC address.
One can also explicitly specify a MAC address to use instead of the
current MAC address. For example, "FF:FF:FF:00:00:00 68:F7:28:00:00:00"
sets the OUI part of the MAC address to "68:F7:28" while scrambling
the last 3 octects.
Similarly, "02:00:00:00:00:00 00:00:00:00:00:00" will scamble
all bits of the MAC address, except clearing the second-least
significant bit. Thus, creating a burned-in address, globally
administered.
One can also supply a list of MAC addresses like
"FF:FF:FF:00:00:00 68:F7:28:00:00:00 00:0C:29:00:00:00 ..." in which
case a MAC address is choosen randomly.
To fully scamble the MAC address one can configure
"02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00".
which also randomly creates either a locally or globally administered
address.
With this, the following macchanger options can be implemented:
`macchanger --random`
This is the default if no mask is configured.
-> ""
while is the same as:
-> "00:00:00:00:00:00"
-> "02:00:00:00:00:00 02:00:00:00:00:00"
`macchanger --random --bia`
-> "02:00:00:00:00:00 00:00:00:00:00:00"
`macchanger --ending`
This option cannot be fully implemented, because macchanger
uses the current MAC address but also implies --bia.
-> "FF:FF:FF:00:00:00"
This would yields the same result only if the current MAC address
is already a burned-in address too. Otherwise, it has not the same
effect as --ending.
-> "FF:FF:FF:00:00:00 <MAC_ADDR>"
Alternatively, instead of using the current MAC address,
spell the OUI part out. But again, that is not really the
same as macchanger does because you explictly have to name
the OUI part to use.
`machanger --another`
`machanger --another_any`
-> "FF:FF:FF:00:00:00 <MAC_ADDR> <MAC_ADDR> ..."
"$(printf "FF:FF:FF:00:00:00 %s\n" "$(sed -n 's/^\([0-9a-fA-F][0-9a-fA-F]\) \([0-9a-fA-F][0-9a-fA-F]\) \([0-9a-fA-F][0-9a-fA-F]\) .*/\1:\2:\3:00:00:00/p' /usr/share/macchanger/wireless.list | xargs)")"
2016-06-22 20:31:39 +02:00
|
|
|
ifname,
|
|
|
|
|
current_mac_address,
|
|
|
|
|
generate_mac_address_mask);
|
device: extend MAC address handling including randomization for ethernet and wifi
Extend the "ethernet.cloned-mac-address" and "wifi.cloned-mac-address"
settings. Instead of specifying an explicit MAC address, the additional
special values "permanent", "preserve", "random", "random-bia", "stable" and
"stable-bia" are supported.
"permanent" means to use the permanent hardware address. Previously that
was the default if no explict cloned-mac-address was set. The default is
thus still "permanent", but it can be overwritten by global
configuration.
"preserve" means not to configure the MAC address when activating the
device. That was actually the default behavior before introducing MAC
address handling with commit 1b49f941a69af910b0e68530be7339e8053068e5.
"random" and "random-bia" use a randomized MAC address for each
connection. "stable" and "stable-bia" use a generated, stable
address based on some token. The "bia" suffix says to generate a
burned-in address. The stable method by default uses as token the
connection UUID, but the token can be explicitly choosen via
"stable:<TOKEN>" and "stable-bia:<TOKEN>".
On a D-Bus level, the "cloned-mac-address" is a bytestring and thus
cannot express the new forms. It is replaced by the new
"assigned-mac-address" field. For the GObject property, libnm's API,
nmcli, keyfile, etc. the old name "cloned-mac-address" is still used.
Deprecating the old field seems more complicated then just extending
the use of the existing "cloned-mac-address" field, although the name
doesn't match well with the extended meaning.
There is some overlap with the "wifi.mac-address-randomization" setting.
https://bugzilla.gnome.org/show_bug.cgi?id=705545
https://bugzilla.gnome.org/show_bug.cgi?id=708820
https://bugzilla.gnome.org/show_bug.cgi?id=758301
2016-05-24 15:57:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2018-12-20 08:38:05 +01:00
|
|
|
GBytes *
|
|
|
|
|
nm_utils_dhcp_client_id_mac (int arp_type,
|
|
|
|
|
const guint8 *hwaddr,
|
|
|
|
|
gsize hwaddr_len)
|
|
|
|
|
{
|
|
|
|
|
guint8 *client_id_buf;
|
|
|
|
|
const guint8 hwaddr_type = arp_type;
|
|
|
|
|
|
|
|
|
|
if (!nm_utils_arp_type_get_hwaddr_relevant_part (arp_type, &hwaddr, &hwaddr_len))
|
|
|
|
|
g_return_val_if_reached (NULL);
|
|
|
|
|
|
|
|
|
|
client_id_buf = g_malloc (hwaddr_len + 1);
|
|
|
|
|
client_id_buf[0] = hwaddr_type;
|
|
|
|
|
memcpy (&client_id_buf[1], hwaddr, hwaddr_len);
|
|
|
|
|
return g_bytes_new_take (client_id_buf, hwaddr_len + 1);
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-23 13:00:07 +01:00
|
|
|
#define HASH_KEY ((const guint8[16]) { 0x80, 0x11, 0x8c, 0xc2, 0xfe, 0x4a, 0x03, 0xee, 0x3e, 0xd6, 0x0c, 0x6f, 0x36, 0x39, 0x14, 0x09 })
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* nm_utils_create_dhcp_iaid:
|
|
|
|
|
* @legacy_unstable_byteorder: legacy behavior is to generate a u32 iaid which
|
2019-01-11 17:07:03 -02:00
|
|
|
* is endianness dependent. This is to preserve backward compatibility.
|
2018-11-23 13:00:07 +01:00
|
|
|
* For non-legacy behavior, the returned integer is in stable endianness,
|
|
|
|
|
* and corresponds to legacy behavior on little endian systems.
|
|
|
|
|
* @interface_id: the seed for hashing when generating the ID. Usually,
|
|
|
|
|
* this is the interface name.
|
|
|
|
|
* @interface_id_len: length of @interface_id
|
|
|
|
|
*
|
|
|
|
|
* This corresponds to systemd's dhcp_identifier_set_iaid() for generating
|
|
|
|
|
* a IAID for the interface.
|
|
|
|
|
*
|
|
|
|
|
* Returns: the IAID in host byte order. */
|
|
|
|
|
guint32
|
|
|
|
|
nm_utils_create_dhcp_iaid (gboolean legacy_unstable_byteorder,
|
|
|
|
|
const guint8 *interface_id,
|
|
|
|
|
gsize interface_id_len)
|
|
|
|
|
{
|
|
|
|
|
guint64 u64;
|
|
|
|
|
guint32 u32;
|
|
|
|
|
|
|
|
|
|
u64 = c_siphash_hash (HASH_KEY, interface_id, interface_id_len);
|
|
|
|
|
u32 = (u64 & 0xffffffffu) ^ (u64 >> 32);
|
|
|
|
|
if (legacy_unstable_byteorder) {
|
|
|
|
|
/* legacy systemd code dhcp_identifier_set_iaid() generates the iaid
|
|
|
|
|
* dependent on the host endianness. Since this function returns the IAID
|
|
|
|
|
* in native-byte order, we need to account for that.
|
|
|
|
|
*
|
|
|
|
|
* On little endian systems, we want the legacy-behavior is identical to
|
|
|
|
|
* the endianness-agnostic behavior. So, we need to swap the bytes on
|
|
|
|
|
* big-endian systems.
|
|
|
|
|
*
|
|
|
|
|
* (https://github.com/systemd/systemd/pull/10614). */
|
|
|
|
|
return htole32 (u32);
|
|
|
|
|
} else {
|
|
|
|
|
/* we return the value as-is, in native byte order. */
|
|
|
|
|
return u32;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-30 08:37:48 +01:00
|
|
|
/**
|
|
|
|
|
* nm_utils_dhcp_client_id_systemd_node_specific_full:
|
2019-10-15 14:45:47 +02:00
|
|
|
* @iaid: the IAID (identity association identifier) in native byte order
|
2018-10-30 08:37:48 +01:00
|
|
|
* @machine_id: the binary identifier for the machine. It is hashed
|
|
|
|
|
* into the DUID. It commonly is /etc/machine-id (parsed in binary as NMUuid).
|
|
|
|
|
* @machine_id_len: the length of the @machine_id.
|
|
|
|
|
*
|
|
|
|
|
* Systemd's sd_dhcp_client generates a default client ID (type 255, node-specific,
|
|
|
|
|
* RFC 4361) if no explicit client-id is set. This function duplicates that
|
|
|
|
|
* implementation and exposes it as (internal) API.
|
|
|
|
|
*
|
|
|
|
|
* Returns: a %GBytes of generated client-id. This function cannot fail.
|
|
|
|
|
*/
|
|
|
|
|
GBytes *
|
2019-12-02 11:29:13 +01:00
|
|
|
nm_utils_dhcp_client_id_systemd_node_specific_full (guint32 iaid,
|
2018-10-30 08:37:48 +01:00
|
|
|
const guint8 *machine_id,
|
|
|
|
|
gsize machine_id_len)
|
|
|
|
|
{
|
|
|
|
|
const guint16 DUID_TYPE_EN = 2;
|
|
|
|
|
const guint32 SYSTEMD_PEN = 43793;
|
|
|
|
|
struct _nm_packed {
|
|
|
|
|
guint8 type;
|
|
|
|
|
guint32 iaid;
|
|
|
|
|
struct _nm_packed {
|
|
|
|
|
guint16 type;
|
|
|
|
|
union {
|
|
|
|
|
struct _nm_packed {
|
|
|
|
|
/* DUID_TYPE_EN */
|
|
|
|
|
guint32 pen;
|
|
|
|
|
uint8_t id[8];
|
|
|
|
|
} en;
|
|
|
|
|
};
|
|
|
|
|
} duid;
|
|
|
|
|
} *client_id;
|
|
|
|
|
guint64 u64;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (machine_id, NULL);
|
|
|
|
|
g_return_val_if_fail (machine_id_len > 0, NULL);
|
|
|
|
|
|
|
|
|
|
client_id = g_malloc (sizeof (*client_id));
|
|
|
|
|
|
|
|
|
|
client_id->type = 255;
|
2019-10-15 14:45:47 +02:00
|
|
|
unaligned_write_be32 (&client_id->iaid, iaid);
|
2018-10-30 08:37:48 +01:00
|
|
|
unaligned_write_be16 (&client_id->duid.type, DUID_TYPE_EN);
|
|
|
|
|
unaligned_write_be32 (&client_id->duid.en.pen, SYSTEMD_PEN);
|
|
|
|
|
|
|
|
|
|
u64 = htole64 (c_siphash_hash (HASH_KEY, machine_id, machine_id_len));
|
|
|
|
|
memcpy(client_id->duid.en.id, &u64, sizeof (client_id->duid.en.id));
|
|
|
|
|
|
|
|
|
|
G_STATIC_ASSERT_EXPR (sizeof (*client_id) == 19);
|
|
|
|
|
return g_bytes_new_take (client_id, 19);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GBytes *
|
2019-12-02 11:29:13 +01:00
|
|
|
nm_utils_dhcp_client_id_systemd_node_specific (guint32 iaid)
|
2018-10-30 08:37:48 +01:00
|
|
|
{
|
2019-12-02 11:29:13 +01:00
|
|
|
return nm_utils_dhcp_client_id_systemd_node_specific_full (iaid,
|
2018-10-30 08:37:48 +01:00
|
|
|
(const guint8 *) nm_utils_machine_id_bin (),
|
|
|
|
|
sizeof (NMUuid));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
/**
|
|
|
|
|
* nm_utils_setpgid:
|
|
|
|
|
* @unused: unused
|
|
|
|
|
*
|
|
|
|
|
* This can be passed as a child setup function to the g_spawn*() family
|
|
|
|
|
* of functions, to ensure that the child is in its own process group
|
|
|
|
|
* (and thus, in some situations, will not be killed when NetworkManager
|
|
|
|
|
* is killed).
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
nm_utils_setpgid (gpointer unused G_GNUC_UNUSED)
|
|
|
|
|
{
|
|
|
|
|
pid_t pid;
|
|
|
|
|
|
|
|
|
|
pid = getpid ();
|
|
|
|
|
setpgid (pid, pid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* nm_utils_g_value_set_strv:
|
|
|
|
|
* @value: a #GValue, initialized to store a #G_TYPE_STRV
|
2018-03-29 12:09:10 +02:00
|
|
|
* @strings: a #GPtrArray of strings. %NULL values are not
|
|
|
|
|
* allowed.
|
2016-03-01 09:56:51 +01:00
|
|
|
*
|
|
|
|
|
* Converts @strings to a #GStrv and stores it in @value.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
nm_utils_g_value_set_strv (GValue *value, GPtrArray *strings)
|
|
|
|
|
{
|
|
|
|
|
char **strv;
|
2018-03-29 12:09:10 +02:00
|
|
|
guint i;
|
2016-03-01 09:56:51 +01:00
|
|
|
|
|
|
|
|
strv = g_new (char *, strings->len + 1);
|
2018-03-29 12:09:10 +02:00
|
|
|
for (i = 0; i < strings->len; i++) {
|
|
|
|
|
nm_assert (strings->pdata[i]);
|
2016-03-01 09:56:51 +01:00
|
|
|
strv[i] = g_strdup (strings->pdata[i]);
|
2018-03-29 12:09:10 +02:00
|
|
|
}
|
2016-03-01 09:56:51 +01:00
|
|
|
strv[i] = NULL;
|
|
|
|
|
|
|
|
|
|
g_value_take_boxed (value, strv);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2016-03-07 11:45:44 +01:00
|
|
|
|
2016-03-07 11:11:59 +01:00
|
|
|
void
|
|
|
|
|
nm_utils_ifname_cpy (char *dst, const char *name)
|
|
|
|
|
{
|
2019-05-16 09:58:28 +02:00
|
|
|
int i;
|
|
|
|
|
|
2016-03-07 11:11:59 +01:00
|
|
|
g_return_if_fail (dst);
|
|
|
|
|
g_return_if_fail (name && name[0]);
|
|
|
|
|
|
2020-02-12 18:01:13 +01:00
|
|
|
nm_assert (nm_utils_ifname_valid_kernel (name, NULL));
|
2016-03-07 11:11:59 +01:00
|
|
|
|
2019-05-16 09:58:28 +02:00
|
|
|
/* ensures NUL padding of the entire IFNAMSIZ buffer. */
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < (int) IFNAMSIZ && name[i] != '\0'; i++)
|
|
|
|
|
dst[i] = name[i];
|
|
|
|
|
|
|
|
|
|
nm_assert (name[i] == '\0');
|
|
|
|
|
|
|
|
|
|
for (; i < (int) IFNAMSIZ; i++)
|
|
|
|
|
dst[i] = '\0';
|
2016-03-07 11:11:59 +01:00
|
|
|
}
|
2016-03-01 09:56:51 +01:00
|
|
|
|
2016-03-07 11:45:44 +01:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#define IPV4LL_NETWORK (htonl (0xA9FE0000L))
|
|
|
|
|
#define IPV4LL_NETMASK (htonl (0xFFFF0000L))
|
|
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
|
nm_utils_ip4_address_is_link_local (in_addr_t addr)
|
|
|
|
|
{
|
|
|
|
|
return (addr & IPV4LL_NETMASK) == IPV4LL_NETWORK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Takes a pair @timestamp and @duration, and returns the remaining duration based
|
|
|
|
|
* on the new timestamp @now.
|
|
|
|
|
*/
|
|
|
|
|
guint32
|
|
|
|
|
nm_utils_lifetime_rebase_relative_time_on_now (guint32 timestamp,
|
|
|
|
|
guint32 duration,
|
2016-03-15 17:40:43 +01:00
|
|
|
gint32 now)
|
2016-03-07 11:45:44 +01:00
|
|
|
{
|
|
|
|
|
gint64 t;
|
|
|
|
|
|
2016-03-15 17:40:43 +01:00
|
|
|
nm_assert (now >= 0);
|
|
|
|
|
|
2016-03-07 11:45:44 +01:00
|
|
|
if (duration == NM_PLATFORM_LIFETIME_PERMANENT)
|
|
|
|
|
return NM_PLATFORM_LIFETIME_PERMANENT;
|
|
|
|
|
|
|
|
|
|
if (timestamp == 0) {
|
|
|
|
|
/* if the @timestamp is zero, assume it was just left unset and that the relative
|
|
|
|
|
* @duration starts counting from @now. This is convenient to construct an address
|
|
|
|
|
* and print it in nm_platform_ip4_address_to_string().
|
|
|
|
|
*
|
|
|
|
|
* In general it does not make sense to set the @duration without anchoring at
|
|
|
|
|
* @timestamp because you don't know the absolute expiration time when looking
|
|
|
|
|
* at the address at a later moment. */
|
|
|
|
|
timestamp = now;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* For timestamp > now, just accept it and calculate the expected(?) result. */
|
|
|
|
|
t = (gint64) timestamp + (gint64) duration - (gint64) now;
|
|
|
|
|
|
|
|
|
|
if (t <= 0)
|
|
|
|
|
return 0;
|
|
|
|
|
if (t >= NM_PLATFORM_LIFETIME_PERMANENT)
|
|
|
|
|
return NM_PLATFORM_LIFETIME_PERMANENT - 1;
|
|
|
|
|
return t;
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-07 21:32:10 +01:00
|
|
|
guint32
|
2016-03-07 11:45:44 +01:00
|
|
|
nm_utils_lifetime_get (guint32 timestamp,
|
|
|
|
|
guint32 lifetime,
|
|
|
|
|
guint32 preferred,
|
2016-03-15 17:40:43 +01:00
|
|
|
gint32 now,
|
2016-03-07 11:45:44 +01:00
|
|
|
guint32 *out_preferred)
|
|
|
|
|
{
|
|
|
|
|
guint32 t_lifetime, t_preferred;
|
|
|
|
|
|
2016-03-15 17:40:43 +01:00
|
|
|
nm_assert (now >= 0);
|
|
|
|
|
|
2016-11-01 19:25:40 +01:00
|
|
|
if (timestamp == 0 && lifetime == 0) {
|
2018-02-07 21:32:10 +01:00
|
|
|
/* We treat lifetime==0 && timestamp==0 addresses as permanent addresses to allow easy
|
2016-11-01 19:25:40 +01:00
|
|
|
* creation of such addresses (without requiring to set the lifetime fields to
|
|
|
|
|
* NM_PLATFORM_LIFETIME_PERMANENT). The real lifetime==0 addresses (E.g. DHCP6 telling us
|
|
|
|
|
* to drop an address will have timestamp set.
|
|
|
|
|
*/
|
2018-02-06 16:12:07 +01:00
|
|
|
NM_SET_OUT (out_preferred, NM_PLATFORM_LIFETIME_PERMANENT);
|
2018-02-07 21:32:10 +01:00
|
|
|
g_return_val_if_fail (preferred == 0, NM_PLATFORM_LIFETIME_PERMANENT);
|
|
|
|
|
return NM_PLATFORM_LIFETIME_PERMANENT;
|
|
|
|
|
}
|
2016-03-07 11:45:44 +01:00
|
|
|
|
2018-02-07 21:32:10 +01:00
|
|
|
if (now <= 0)
|
2019-12-13 16:54:30 +01:00
|
|
|
now = nm_utils_get_monotonic_timestamp_sec ();
|
2016-03-07 11:45:44 +01:00
|
|
|
|
2018-02-07 21:32:10 +01:00
|
|
|
t_lifetime = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, lifetime, now);
|
|
|
|
|
if (!t_lifetime) {
|
|
|
|
|
NM_SET_OUT (out_preferred, 0);
|
|
|
|
|
return 0;
|
2016-03-07 11:45:44 +01:00
|
|
|
}
|
2018-02-07 21:32:10 +01:00
|
|
|
|
|
|
|
|
t_preferred = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, preferred, now);
|
|
|
|
|
|
|
|
|
|
NM_SET_OUT (out_preferred, MIN (t_preferred, t_lifetime));
|
|
|
|
|
|
|
|
|
|
/* Assert that non-permanent addresses have a (positive) @timestamp. nm_utils_lifetime_rebase_relative_time_on_now()
|
|
|
|
|
* treats addresses with timestamp 0 as *now*. Addresses passed to _address_get_lifetime() always
|
|
|
|
|
* should have a valid @timestamp, otherwise on every re-sync, their lifetime will be extended anew.
|
|
|
|
|
*/
|
|
|
|
|
g_return_val_if_fail ( timestamp != 0
|
|
|
|
|
|| ( lifetime == NM_PLATFORM_LIFETIME_PERMANENT
|
|
|
|
|
&& preferred == NM_PLATFORM_LIFETIME_PERMANENT), t_lifetime);
|
|
|
|
|
g_return_val_if_fail (t_preferred <= t_lifetime, t_lifetime);
|
|
|
|
|
|
|
|
|
|
return t_lifetime;
|
2016-03-07 11:45:44 +01:00
|
|
|
}
|
|
|
|
|
|
2016-05-01 22:24:41 +02:00
|
|
|
const char *
|
2016-06-02 19:17:35 +02:00
|
|
|
nm_utils_dnsmasq_status_to_string (int status, char *dest, gsize size)
|
2016-05-01 22:24:41 +02:00
|
|
|
{
|
2016-06-02 19:17:35 +02:00
|
|
|
const char *msg;
|
|
|
|
|
|
|
|
|
|
nm_utils_to_string_buffer_init (&dest, &size);
|
2016-05-01 22:24:41 +02:00
|
|
|
|
|
|
|
|
if (status == 0)
|
|
|
|
|
msg = "Success";
|
|
|
|
|
else if (status == 1)
|
|
|
|
|
msg = "Configuration problem";
|
|
|
|
|
else if (status == 2)
|
|
|
|
|
msg = "Network access problem (address in use, permissions)";
|
|
|
|
|
else if (status == 3)
|
|
|
|
|
msg = "Filesystem problem (missing file/directory, permissions)";
|
|
|
|
|
else if (status == 4)
|
|
|
|
|
msg = "Memory allocation failure";
|
|
|
|
|
else if (status == 5)
|
|
|
|
|
msg = "Other problem";
|
2016-06-02 19:17:35 +02:00
|
|
|
else if (status >= 11) {
|
|
|
|
|
g_snprintf (dest, size, "Lease script failed with error %d", status - 10);
|
|
|
|
|
return dest;
|
|
|
|
|
}
|
2016-05-01 22:24:41 +02:00
|
|
|
else
|
|
|
|
|
msg = "Unknown problem";
|
|
|
|
|
|
2016-06-02 19:17:35 +02:00
|
|
|
g_snprintf (dest, size, "%s (%d)", msg, status);
|
|
|
|
|
return dest;
|
2016-05-01 22:24:41 +02:00
|
|
|
}
|
2016-06-03 22:07:38 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* nm_utils_get_reverse_dns_domains_ip4:
|
|
|
|
|
* @addr: IP address in network order
|
|
|
|
|
* @plen: prefix length
|
|
|
|
|
* @domains: array for results
|
|
|
|
|
*
|
|
|
|
|
* Creates reverse DNS domains for the given address and prefix length, and
|
|
|
|
|
* append them to @domains.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
nm_utils_get_reverse_dns_domains_ip4 (guint32 addr, guint8 plen, GPtrArray *domains)
|
|
|
|
|
{
|
|
|
|
|
guint32 ip, ip2, mask;
|
|
|
|
|
guchar *p;
|
|
|
|
|
guint octets;
|
|
|
|
|
guint i;
|
|
|
|
|
gsize len0, len;
|
|
|
|
|
char *str, *s;
|
|
|
|
|
|
|
|
|
|
g_return_if_fail (domains);
|
|
|
|
|
g_return_if_fail (plen <= 32);
|
|
|
|
|
|
|
|
|
|
if (!plen)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
octets = (plen - 1) / 8 + 1;
|
|
|
|
|
ip = ntohl (addr);
|
|
|
|
|
mask = 0xFFFFFFFF << (32 - plen);
|
|
|
|
|
ip &= mask;
|
|
|
|
|
ip2 = ip;
|
|
|
|
|
|
|
|
|
|
len0 = NM_STRLEN ("in-addr.arpa") + (4 * octets) + 1;
|
|
|
|
|
while ((ip2 & mask) == ip) {
|
|
|
|
|
addr = htonl (ip2);
|
|
|
|
|
p = (guchar *) &addr;
|
|
|
|
|
|
|
|
|
|
len = len0;
|
|
|
|
|
str = s = g_malloc (len);
|
|
|
|
|
for (i = octets; i > 0; i--)
|
|
|
|
|
nm_utils_strbuf_append (&s, &len, "%u.", p[i - 1] & 0xff);
|
|
|
|
|
nm_utils_strbuf_append_str (&s, &len, "in-addr.arpa");
|
|
|
|
|
|
|
|
|
|
g_ptr_array_add (domains, str);
|
|
|
|
|
|
|
|
|
|
ip2 += 1 << ((32 - plen) & ~7);
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-06-03 22:07:42 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* nm_utils_get_reverse_dns_domains_ip6:
|
|
|
|
|
* @addr: IPv6 address
|
|
|
|
|
* @plen: prefix length
|
|
|
|
|
* @domains: array for results
|
|
|
|
|
*
|
|
|
|
|
* Creates reverse DNS domains for the given address and prefix length, and
|
|
|
|
|
* append them to @domains.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
nm_utils_get_reverse_dns_domains_ip6 (const struct in6_addr *ip, guint8 plen, GPtrArray *domains)
|
|
|
|
|
{
|
|
|
|
|
struct in6_addr addr;
|
|
|
|
|
guint nibbles, bits, entries;
|
|
|
|
|
int i, j;
|
|
|
|
|
gsize len0, len;
|
|
|
|
|
char *str, *s;
|
|
|
|
|
|
|
|
|
|
g_return_if_fail (domains);
|
|
|
|
|
g_return_if_fail (plen <= 128);
|
|
|
|
|
|
|
|
|
|
if (!plen)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
memcpy (&addr, ip, sizeof (struct in6_addr));
|
2019-03-04 15:50:52 +01:00
|
|
|
nm_utils_ip6_address_clear_host_address (&addr, NULL, plen);
|
2016-06-03 22:07:42 +02:00
|
|
|
|
|
|
|
|
/* Number of nibbles to include in domains */
|
|
|
|
|
nibbles = (plen - 1) / 4 + 1;
|
|
|
|
|
/* Prefix length in nibble */
|
|
|
|
|
bits = plen - ((plen - 1) / 4 * 4);
|
|
|
|
|
/* Number of domains */
|
|
|
|
|
entries = 1 << (4 - bits);
|
|
|
|
|
|
|
|
|
|
len0 = NM_STRLEN ("ip6.arpa") + (2 * nibbles) + 1;
|
|
|
|
|
|
|
|
|
|
#define N_SHIFT(x) ((x) % 2 ? 0 : 4)
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < entries; i++) {
|
|
|
|
|
len = len0;
|
|
|
|
|
str = s = g_malloc (len);
|
|
|
|
|
|
|
|
|
|
for (j = nibbles - 1; j >= 0; j--)
|
|
|
|
|
nm_utils_strbuf_append (&s,
|
|
|
|
|
&len,
|
|
|
|
|
"%x.",
|
|
|
|
|
(addr.s6_addr[j / 2] >> N_SHIFT (j)) & 0xf);
|
|
|
|
|
nm_utils_strbuf_append_str (&s, &len, "ip6.arpa");
|
|
|
|
|
|
|
|
|
|
g_ptr_array_add (domains, str);
|
|
|
|
|
|
|
|
|
|
addr.s6_addr[(nibbles - 1) / 2] += 1 << N_SHIFT (nibbles - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#undef N_SHIFT
|
|
|
|
|
}
|
2016-08-26 17:07:14 +02:00
|
|
|
|
2016-11-03 18:07:11 +01:00
|
|
|
struct plugin_info {
|
|
|
|
|
char *path;
|
|
|
|
|
struct stat st;
|
|
|
|
|
};
|
|
|
|
|
|
all: don't use gchar/gshort/gint/glong but C types
We commonly don't use the glib typedefs for char/short/int/long,
but their C types directly.
$ git grep '\<g\(char\|short\|int\|long\|float\|double\)\>' | wc -l
587
$ git grep '\<\(char\|short\|int\|long\|float\|double\)\>' | wc -l
21114
One could argue that using the glib typedefs is preferable in
public API (of our glib based libnm library) or where it clearly
is related to glib, like during
g_object_set (obj, PROPERTY, (gint) value, NULL);
However, that argument does not seem strong, because in practice we don't
follow that argument today, and seldomly use the glib typedefs.
Also, the style guide for this would be hard to formalize, because
"using them where clearly related to a glib" is a very loose suggestion.
Also note that glib typedefs will always just be typedefs of the
underlying C types. There is no danger of glib changing the meaning
of these typedefs (because that would be a major API break of glib).
A simple style guide is instead: don't use these typedefs.
No manual actions, I only ran the bash script:
FILES=($(git ls-files '*.[hc]'))
sed -i \
-e 's/\<g\(char\|short\|int\|long\|float\|double\)\>\( [^ ]\)/\1\2/g' \
-e 's/\<g\(char\|short\|int\|long\|float\|double\)\> /\1 /g' \
-e 's/\<g\(char\|short\|int\|long\|float\|double\)\>/\1/g' \
"${FILES[@]}"
2018-07-11 07:40:19 +02:00
|
|
|
static int
|
2016-11-03 18:07:11 +01:00
|
|
|
read_device_factory_paths_sort_fcn (gconstpointer a, gconstpointer b)
|
|
|
|
|
{
|
|
|
|
|
const struct plugin_info *da = a;
|
|
|
|
|
const struct plugin_info *db = b;
|
|
|
|
|
time_t ta, tb;
|
|
|
|
|
|
|
|
|
|
ta = MAX (da->st.st_mtime, da->st.st_ctime);
|
|
|
|
|
tb = MAX (db->st.st_mtime, db->st.st_ctime);
|
|
|
|
|
|
|
|
|
|
if (ta < tb)
|
|
|
|
|
return 1;
|
|
|
|
|
if (ta > tb)
|
|
|
|
|
return -1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-17 09:56:16 +01:00
|
|
|
gboolean
|
|
|
|
|
nm_utils_validate_plugin (const char *path, struct stat *st, GError **error)
|
|
|
|
|
{
|
|
|
|
|
g_return_val_if_fail (path, FALSE);
|
|
|
|
|
g_return_val_if_fail (st, FALSE);
|
|
|
|
|
g_return_val_if_fail (!error || !*error, FALSE);
|
|
|
|
|
|
|
|
|
|
if (!S_ISREG (st->st_mode)) {
|
|
|
|
|
g_set_error_literal (error,
|
|
|
|
|
NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
|
|
|
|
|
"not a regular file");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (st->st_uid != 0) {
|
|
|
|
|
g_set_error_literal (error,
|
|
|
|
|
NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
|
|
|
|
|
"file has invalid owner (should be root)");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (st->st_mode & (S_IWGRP | S_IWOTH | S_ISUID)) {
|
|
|
|
|
g_set_error_literal (error,
|
|
|
|
|
NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN,
|
|
|
|
|
"file has invalid permissions");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-03 18:07:11 +01:00
|
|
|
char **
|
|
|
|
|
nm_utils_read_plugin_paths (const char *dirname, const char *prefix)
|
|
|
|
|
{
|
|
|
|
|
GDir *dir;
|
|
|
|
|
GError *error = NULL;
|
|
|
|
|
const char *item;
|
|
|
|
|
GArray *paths;
|
|
|
|
|
char **result;
|
|
|
|
|
guint i;
|
|
|
|
|
|
|
|
|
|
g_return_val_if_fail (dirname, NULL);
|
|
|
|
|
g_return_val_if_fail (prefix, NULL);
|
|
|
|
|
|
|
|
|
|
dir = g_dir_open (dirname, 0, &error);
|
|
|
|
|
if (!dir) {
|
|
|
|
|
nm_log_warn (LOGD_CORE, "device plugin: failed to open directory %s: %s",
|
|
|
|
|
dirname,
|
|
|
|
|
error->message);
|
|
|
|
|
g_clear_error (&error);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
paths = g_array_new (FALSE, FALSE, sizeof (struct plugin_info));
|
|
|
|
|
|
|
|
|
|
while ((item = g_dir_read_name (dir))) {
|
|
|
|
|
int errsv;
|
|
|
|
|
struct plugin_info data;
|
|
|
|
|
|
|
|
|
|
if (!g_str_has_prefix (item, prefix))
|
|
|
|
|
continue;
|
2018-05-09 12:55:53 +02:00
|
|
|
if (!g_str_has_suffix (item, ".so"))
|
2016-11-03 18:07:11 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
data.path = g_build_filename (dirname, item, NULL);
|
|
|
|
|
|
|
|
|
|
if (stat (data.path, &data.st) != 0) {
|
|
|
|
|
errsv = errno;
|
|
|
|
|
nm_log_warn (LOGD_CORE,
|
|
|
|
|
"plugin: skip invalid file %s (error during stat: %s)",
|
2019-01-31 17:22:18 +01:00
|
|
|
data.path, nm_strerror_native (errsv));
|
2016-11-03 18:07:11 +01:00
|
|
|
goto skip;
|
|
|
|
|
}
|
2016-11-17 09:56:16 +01:00
|
|
|
|
|
|
|
|
if (!nm_utils_validate_plugin (data.path, &data.st, &error)) {
|
2016-11-03 18:07:11 +01:00
|
|
|
nm_log_warn (LOGD_CORE,
|
2016-11-17 09:56:16 +01:00
|
|
|
"plugin: skip invalid file %s: %s",
|
|
|
|
|
data.path, error->message);
|
|
|
|
|
g_clear_error (&error);
|
2016-11-03 18:07:11 +01:00
|
|
|
goto skip;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_array_append_val (paths, data);
|
|
|
|
|
continue;
|
|
|
|
|
skip:
|
|
|
|
|
g_free (data.path);
|
|
|
|
|
}
|
|
|
|
|
g_dir_close (dir);
|
|
|
|
|
|
|
|
|
|
/* sort filenames by modification time. */
|
|
|
|
|
g_array_sort (paths, read_device_factory_paths_sort_fcn);
|
|
|
|
|
|
|
|
|
|
result = g_new (char *, paths->len + 1);
|
|
|
|
|
for (i = 0; i < paths->len; i++)
|
|
|
|
|
result[i] = g_array_index (paths, struct plugin_info, i).path;
|
|
|
|
|
result[i] = NULL;
|
|
|
|
|
|
|
|
|
|
g_array_free (paths, TRUE);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2016-11-03 18:38:27 +01:00
|
|
|
|
2017-03-03 23:59:16 +01:00
|
|
|
char *
|
|
|
|
|
nm_utils_format_con_diff_for_audit (GHashTable *diff)
|
|
|
|
|
{
|
|
|
|
|
GHashTable *setting_diff;
|
|
|
|
|
char *setting_name, *prop_name;
|
|
|
|
|
GHashTableIter iter, iter2;
|
|
|
|
|
GString *str;
|
|
|
|
|
|
|
|
|
|
str = g_string_sized_new (32);
|
|
|
|
|
g_hash_table_iter_init (&iter, diff);
|
|
|
|
|
|
|
|
|
|
while (g_hash_table_iter_next (&iter,
|
|
|
|
|
(gpointer *) &setting_name,
|
|
|
|
|
(gpointer *) &setting_diff)) {
|
|
|
|
|
if (!setting_diff)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
g_hash_table_iter_init (&iter2, setting_diff);
|
|
|
|
|
|
|
|
|
|
while (g_hash_table_iter_next (&iter2, (gpointer *) &prop_name, NULL))
|
|
|
|
|
g_string_append_printf (str, "%s.%s,", setting_name, prop_name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (str->len)
|
|
|
|
|
str->str[str->len - 1] = '\0';
|
|
|
|
|
|
|
|
|
|
return g_string_free (str, FALSE);
|
|
|
|
|
}
|
2017-03-07 11:04:36 +01:00
|
|
|
|
2018-01-10 11:23:33 +01:00
|
|
|
const char *
|
|
|
|
|
nm_utils_parse_dns_domain (const char *domain, gboolean *is_routing)
|
|
|
|
|
{
|
|
|
|
|
g_return_val_if_fail (domain, NULL);
|
|
|
|
|
g_return_val_if_fail (domain[0], NULL);
|
|
|
|
|
|
|
|
|
|
if (domain[0] == '~') {
|
|
|
|
|
domain++;
|
|
|
|
|
NM_SET_OUT (is_routing, TRUE);
|
|
|
|
|
} else
|
|
|
|
|
NM_SET_OUT (is_routing, FALSE);
|
|
|
|
|
|
|
|
|
|
return domain;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-21 13:04:39 +02:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-01-27 20:16:22 +01:00
|
|
|
static guint32
|
|
|
|
|
get_max_rate_ht_20 (int mcs)
|
|
|
|
|
{
|
|
|
|
|
switch (mcs) {
|
|
|
|
|
case 0: return 6500000;
|
|
|
|
|
case 1:
|
|
|
|
|
case 8: return 13000000;
|
|
|
|
|
case 2:
|
|
|
|
|
case 16: return 19500000;
|
|
|
|
|
case 3:
|
|
|
|
|
case 9:
|
|
|
|
|
case 24: return 26000000;
|
|
|
|
|
case 4:
|
|
|
|
|
case 10:
|
|
|
|
|
case 17: return 39000000;
|
|
|
|
|
case 5:
|
|
|
|
|
case 11:
|
|
|
|
|
case 25: return 52000000;
|
|
|
|
|
case 6:
|
|
|
|
|
case 18: return 58500000;
|
|
|
|
|
case 7: return 65000000;
|
|
|
|
|
case 12:
|
|
|
|
|
case 19:
|
|
|
|
|
case 26: return 78000000;
|
|
|
|
|
case 13:
|
|
|
|
|
case 27: return 104000000;
|
|
|
|
|
case 14:
|
|
|
|
|
case 20: return 117000000;
|
|
|
|
|
case 15: return 130000000;
|
|
|
|
|
case 21:
|
|
|
|
|
case 28: return 156000000;
|
|
|
|
|
case 22: return 175500000;
|
|
|
|
|
case 23: return 195000000;
|
|
|
|
|
case 29: return 208000000;
|
|
|
|
|
case 30: return 234000000;
|
|
|
|
|
case 31: return 260000000;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static guint32
|
|
|
|
|
get_max_rate_ht_40 (int mcs)
|
|
|
|
|
{
|
|
|
|
|
switch (mcs) {
|
|
|
|
|
case 0: return 13500000;
|
|
|
|
|
case 1:
|
|
|
|
|
case 8: return 27000000;
|
|
|
|
|
case 2: return 40500000;
|
|
|
|
|
case 3:
|
|
|
|
|
case 9:
|
|
|
|
|
case 24: return 54000000;
|
|
|
|
|
case 4:
|
|
|
|
|
case 10:
|
|
|
|
|
case 17: return 81000000;
|
|
|
|
|
case 5:
|
|
|
|
|
case 11:
|
|
|
|
|
case 25: return 108000000;
|
|
|
|
|
case 6:
|
|
|
|
|
case 18: return 121500000;
|
|
|
|
|
case 7: return 135000000;
|
|
|
|
|
case 12:
|
|
|
|
|
case 19:
|
|
|
|
|
case 26: return 162000000;
|
|
|
|
|
case 13:
|
|
|
|
|
case 27: return 216000000;
|
|
|
|
|
case 14:
|
|
|
|
|
case 20: return 243000000;
|
|
|
|
|
case 15: return 270000000;
|
|
|
|
|
case 16: return 40500000;
|
|
|
|
|
case 21:
|
|
|
|
|
case 28: return 324000000;
|
|
|
|
|
case 22: return 364500000;
|
|
|
|
|
case 23: return 405000000;
|
|
|
|
|
case 29: return 432000000;
|
|
|
|
|
case 30: return 486000000;
|
|
|
|
|
case 31: return 540000000;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static guint32
|
|
|
|
|
get_max_rate_vht_80_ss1 (int mcs)
|
|
|
|
|
{
|
|
|
|
|
switch (mcs) {
|
|
|
|
|
case 0: return 29300000;
|
|
|
|
|
case 1: return 58500000;
|
|
|
|
|
case 2: return 87800000;
|
|
|
|
|
case 3: return 117000000;
|
|
|
|
|
case 4: return 175500000;
|
|
|
|
|
case 5: return 234000000;
|
|
|
|
|
case 6: return 263300000;
|
|
|
|
|
case 7: return 292500000;
|
|
|
|
|
case 8: return 351000000;
|
|
|
|
|
case 9: return 390000000;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static guint32
|
|
|
|
|
get_max_rate_vht_80_ss2 (int mcs)
|
|
|
|
|
{
|
|
|
|
|
switch (mcs) {
|
|
|
|
|
case 0: return 58500000;
|
|
|
|
|
case 1: return 117000000;
|
|
|
|
|
case 2: return 175500000;
|
|
|
|
|
case 3: return 234000000;
|
|
|
|
|
case 4: return 351000000;
|
|
|
|
|
case 5: return 468000000;
|
|
|
|
|
case 6: return 526500000;
|
|
|
|
|
case 7: return 585000000;
|
|
|
|
|
case 8: return 702000000;
|
|
|
|
|
case 9: return 780000000;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static guint32
|
|
|
|
|
get_max_rate_vht_80_ss3 (int mcs)
|
|
|
|
|
{
|
|
|
|
|
switch (mcs) {
|
|
|
|
|
case 0: return 87800000;
|
|
|
|
|
case 1: return 175500000;
|
|
|
|
|
case 2: return 263300000;
|
|
|
|
|
case 3: return 351000000;
|
|
|
|
|
case 4: return 526500000;
|
|
|
|
|
case 5: return 702000000;
|
|
|
|
|
case 6: return 0;
|
|
|
|
|
case 7: return 877500000;
|
|
|
|
|
case 8: return 105300000;
|
|
|
|
|
case 9: return 117000000;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static guint32
|
|
|
|
|
get_max_rate_vht_160_ss1 (int mcs)
|
|
|
|
|
{
|
|
|
|
|
switch (mcs) {
|
|
|
|
|
case 0: return 58500000;
|
|
|
|
|
case 1: return 117000000;
|
|
|
|
|
case 2: return 175500000;
|
|
|
|
|
case 3: return 234000000;
|
|
|
|
|
case 4: return 351000000;
|
|
|
|
|
case 5: return 468000000;
|
|
|
|
|
case 6: return 526500000;
|
|
|
|
|
case 7: return 585000000;
|
|
|
|
|
case 8: return 702000000;
|
|
|
|
|
case 9: return 780000000;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static guint32
|
|
|
|
|
get_max_rate_vht_160_ss2 (int mcs)
|
|
|
|
|
{
|
|
|
|
|
switch (mcs) {
|
|
|
|
|
case 0: return 117000000;
|
|
|
|
|
case 1: return 234000000;
|
|
|
|
|
case 2: return 351000000;
|
|
|
|
|
case 3: return 468000000;
|
|
|
|
|
case 4: return 702000000;
|
|
|
|
|
case 5: return 936000000;
|
|
|
|
|
case 6: return 1053000000;
|
|
|
|
|
case 7: return 1170000000;
|
|
|
|
|
case 8: return 1404000000;
|
|
|
|
|
case 9: return 1560000000;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static guint32
|
|
|
|
|
get_max_rate_vht_160_ss3 (int mcs)
|
|
|
|
|
{
|
|
|
|
|
switch (mcs) {
|
|
|
|
|
case 0: return 175500000;
|
|
|
|
|
case 1: return 351000000;
|
|
|
|
|
case 2: return 526500000;
|
|
|
|
|
case 3: return 702000000;
|
|
|
|
|
case 4: return 1053000000;
|
|
|
|
|
case 5: return 1404000000;
|
|
|
|
|
case 6: return 1579500000;
|
|
|
|
|
case 7: return 1755000000;
|
|
|
|
|
case 8: return 2106000000;
|
|
|
|
|
case 9: return 0;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
get_max_rate_ht (const guint8 *bytes, guint len, guint32 *out_maxrate)
|
|
|
|
|
{
|
|
|
|
|
guint32 i;
|
|
|
|
|
guint8 ht_cap_info;
|
|
|
|
|
const guint8 *supported_mcs_set;
|
|
|
|
|
guint32 rate;
|
|
|
|
|
|
|
|
|
|
/* http://standards.ieee.org/getieee802/download/802.11-2012.pdf
|
|
|
|
|
* https://mrncciew.com/2014/10/19/cwap-ht-capabilities-ie/
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (len != 26)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
ht_cap_info = bytes[0];
|
|
|
|
|
supported_mcs_set = &bytes[3];
|
|
|
|
|
*out_maxrate = 0;
|
|
|
|
|
|
|
|
|
|
/* Find the maximum supported mcs rate */
|
|
|
|
|
for (i = 0; i <= 76; i++) {
|
|
|
|
|
const unsigned mcs_octet = i / 8;
|
|
|
|
|
const unsigned MCS_RATE_BIT = 1 << i % 8;
|
|
|
|
|
|
|
|
|
|
if (supported_mcs_set[mcs_octet] & MCS_RATE_BIT) {
|
|
|
|
|
/* Check for 40Mhz wide channel support */
|
|
|
|
|
if (ht_cap_info & (1 << 1))
|
|
|
|
|
rate = get_max_rate_ht_40 (i);
|
|
|
|
|
else
|
|
|
|
|
rate = get_max_rate_ht_20 (i);
|
|
|
|
|
|
|
|
|
|
if (rate > *out_maxrate)
|
|
|
|
|
*out_maxrate = rate;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
get_max_rate_vht (const guint8 *bytes, guint len, guint32 *out_maxrate)
|
|
|
|
|
{
|
|
|
|
|
guint32 mcs, m;
|
|
|
|
|
guint8 vht_cap, tx_map;
|
|
|
|
|
|
|
|
|
|
/* https://tda802dot11.blogspot.it/2014/10/vht-capabilities-element-vht.html
|
|
|
|
|
* http://chimera.labs.oreilly.com/books/1234000001739/ch03.html#management_frames */
|
|
|
|
|
|
|
|
|
|
if (len != 12)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
vht_cap = bytes[0];
|
|
|
|
|
tx_map = bytes[8];
|
|
|
|
|
|
|
|
|
|
/* Check for mcs rates 8 and 9 support */
|
|
|
|
|
if (tx_map & 0x2a)
|
|
|
|
|
mcs = 9;
|
|
|
|
|
else if (tx_map & 0x15)
|
|
|
|
|
mcs = 8;
|
|
|
|
|
else
|
|
|
|
|
mcs = 7;
|
|
|
|
|
|
|
|
|
|
/* Check for 160Mhz wide channel support and
|
|
|
|
|
* spatial stream support */
|
|
|
|
|
if (vht_cap & (1 << 2)) {
|
|
|
|
|
if (tx_map & 0x30)
|
|
|
|
|
m = get_max_rate_vht_160_ss3 (mcs);
|
|
|
|
|
else if (tx_map & 0x0C)
|
|
|
|
|
m = get_max_rate_vht_160_ss2 (mcs);
|
|
|
|
|
else
|
|
|
|
|
m = get_max_rate_vht_160_ss1 (mcs);
|
|
|
|
|
} else {
|
|
|
|
|
if (tx_map & 0x30)
|
|
|
|
|
m = get_max_rate_vht_80_ss3 (mcs);
|
|
|
|
|
else if (tx_map & 0x0C)
|
|
|
|
|
m = get_max_rate_vht_80_ss2 (mcs);
|
|
|
|
|
else
|
|
|
|
|
m = get_max_rate_vht_80_ss1 (mcs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*out_maxrate = m;
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Management Frame Information Element IDs, ieee80211_eid */
|
|
|
|
|
#define WLAN_EID_HT_CAPABILITY 45
|
|
|
|
|
#define WLAN_EID_VHT_CAPABILITY 191
|
|
|
|
|
#define WLAN_EID_VENDOR_SPECIFIC 221
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
nm_wifi_utils_parse_ies (const guint8 *bytes,
|
|
|
|
|
gsize len,
|
|
|
|
|
guint32 *out_max_rate,
|
2020-03-16 03:03:32 +01:00
|
|
|
gboolean *out_metered,
|
2020-03-22 11:04:28 +01:00
|
|
|
gboolean *out_owe_transition_mode)
|
2020-01-27 20:16:22 +01:00
|
|
|
{
|
|
|
|
|
guint8 id, elem_len;
|
|
|
|
|
guint32 m;
|
|
|
|
|
|
|
|
|
|
NM_SET_OUT (out_max_rate, 0);
|
|
|
|
|
NM_SET_OUT (out_metered, FALSE);
|
2020-03-22 11:04:28 +01:00
|
|
|
NM_SET_OUT (out_owe_transition_mode, FALSE);
|
2020-01-27 20:16:22 +01:00
|
|
|
|
|
|
|
|
while (len) {
|
|
|
|
|
if (len < 2)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
id = *bytes++;
|
|
|
|
|
elem_len = *bytes++;
|
|
|
|
|
len -= 2;
|
|
|
|
|
|
|
|
|
|
if (elem_len > len)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
switch (id) {
|
|
|
|
|
case WLAN_EID_HT_CAPABILITY:
|
|
|
|
|
if (out_max_rate) {
|
|
|
|
|
if (get_max_rate_ht (bytes, elem_len, &m))
|
|
|
|
|
*out_max_rate = NM_MAX (*out_max_rate, m);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case WLAN_EID_VHT_CAPABILITY:
|
|
|
|
|
if (out_max_rate) {
|
|
|
|
|
if (get_max_rate_vht (bytes, elem_len, &m))
|
|
|
|
|
*out_max_rate = NM_MAX (*out_max_rate, m);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case WLAN_EID_VENDOR_SPECIFIC:
|
2020-05-12 14:37:01 +02:00
|
|
|
if ( len == 8
|
|
|
|
|
&& bytes[0] == 0x00 /* OUI: Microsoft */
|
|
|
|
|
&& bytes[1] == 0x50
|
|
|
|
|
&& bytes[2] == 0xf2
|
|
|
|
|
&& bytes[3] == 0x11) /* OUI type: Network cost */
|
|
|
|
|
NM_SET_OUT (out_metered, (bytes[7] > 1)); /* Cost level > 1 */
|
|
|
|
|
if ( elem_len >= 10
|
2020-03-16 03:03:32 +01:00
|
|
|
&& bytes[0] == 0x50 /* OUI: WiFi Alliance */
|
|
|
|
|
&& bytes[1] == 0x6f
|
|
|
|
|
&& bytes[2] == 0x9a
|
|
|
|
|
&& bytes[3] == 0x1c) /* OUI type: OWE Transition Mode */
|
2020-05-12 14:37:01 +02:00
|
|
|
NM_SET_OUT (out_owe_transition_mode, TRUE);
|
2020-01-27 20:16:22 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
len -= elem_len;
|
|
|
|
|
bytes += elem_len;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-01-27 19:31:23 +01:00
|
|
|
guint8
|
|
|
|
|
nm_wifi_utils_level_to_quality (int val)
|
|
|
|
|
{
|
|
|
|
|
if (val < 0) {
|
|
|
|
|
/* Assume dBm already; rough conversion: best = -40, worst = -100 */
|
|
|
|
|
val = abs (CLAMP (val, -100, -40) + 40); /* normalize to 0 */
|
|
|
|
|
val = 100 - (int) ((100.0 * (double) val) / 60.0);
|
|
|
|
|
} else if (val > 110 && val < 256) {
|
|
|
|
|
/* assume old-style WEXT 8-bit unsigned signal level */
|
|
|
|
|
val -= 256; /* subtract 256 to convert to dBm */
|
|
|
|
|
val = abs (CLAMP (val, -100, -40) + 40); /* normalize to 0 */
|
|
|
|
|
val = 100 - (int) ((100.0 * (double) val) / 60.0);
|
|
|
|
|
} else {
|
|
|
|
|
/* Assume signal is a "quality" percentage */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return CLAMP (val, 0, 100);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2017-10-09 11:56:51 +02:00
|
|
|
NM_UTILS_ENUM2STR_DEFINE (nm_icmpv6_router_pref_to_string, NMIcmpv6RouterPref,
|
|
|
|
|
NM_UTILS_ENUM2STR (NM_ICMPV6_ROUTER_PREF_LOW, "low"),
|
|
|
|
|
NM_UTILS_ENUM2STR (NM_ICMPV6_ROUTER_PREF_MEDIUM, "medium"),
|
|
|
|
|
NM_UTILS_ENUM2STR (NM_ICMPV6_ROUTER_PREF_HIGH, "high"),
|
|
|
|
|
NM_UTILS_ENUM2STR (NM_ICMPV6_ROUTER_PREF_INVALID, "invalid"),
|
|
|
|
|
);
|
|
|
|
|
|
2017-03-07 11:04:36 +01:00
|
|
|
NM_UTILS_LOOKUP_STR_DEFINE (nm_activation_type_to_string, NMActivationType,
|
|
|
|
|
NM_UTILS_LOOKUP_DEFAULT_WARN ("(unknown)"),
|
2017-03-08 12:22:01 +01:00
|
|
|
NM_UTILS_LOOKUP_STR_ITEM (NM_ACTIVATION_TYPE_MANAGED, "managed"),
|
|
|
|
|
NM_UTILS_LOOKUP_STR_ITEM (NM_ACTIVATION_TYPE_ASSUME, "assume"),
|
|
|
|
|
NM_UTILS_LOOKUP_STR_ITEM (NM_ACTIVATION_TYPE_EXTERNAL, "external"),
|
2017-03-07 11:04:36 +01:00
|
|
|
)
|