2020-12-23 22:21:36 +01:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
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
|
|
|
*/
|
|
|
|
|
|
2021-02-04 18:04:13 +01:00
|
|
|
#include "src/core/nm-default-daemon.h"
|
2016-03-01 09:56:51 +01:00
|
|
|
|
|
|
|
|
#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>
|
|
|
|
|
|
2021-05-02 21:47:22 +02:00
|
|
|
#include "libnm-glib-aux/nm-uuid.h"
|
2021-03-04 07:50:01 +01:00
|
|
|
#include "libnm-platform/nmp-base.h"
|
2021-02-18 17:37:47 +01:00
|
|
|
#include "libnm-std-aux/unaligned.h"
|
2021-02-18 17:37:47 +01:00
|
|
|
#include "libnm-glib-aux/nm-random-utils.h"
|
|
|
|
|
#include "libnm-glib-aux/nm-io-utils.h"
|
|
|
|
|
#include "libnm-glib-aux/nm-secret-utils.h"
|
|
|
|
|
#include "libnm-glib-aux/nm-time-utils.h"
|
2021-05-24 21:49:35 +02:00
|
|
|
#include "libnm-glib-aux/nm-str-buf.h"
|
2016-03-01 09:56:51 +01:00
|
|
|
#include "nm-utils.h"
|
2021-02-12 15:01:09 +01:00
|
|
|
#include "libnm-core-intern/nm-core-internal.h"
|
2016-03-01 09:56:51 +01:00
|
|
|
#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__
|
2021-07-09 08:48:48 +02:00
|
|
|
#error \
|
|
|
|
|
"nm-core-utils.c should stay independent of systemd utils. Are you looking for NetworkMangerUtils.c? "
|
2018-10-19 10:21:53 +02:00
|
|
|
#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
|
2022-10-11 11:09:44 +02:00
|
|
|
nm_utils_get_testing_initialized(void)
|
2016-03-01 09:56:51 +01:00
|
|
|
{
|
|
|
|
|
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
|
2022-10-11 11:09:44 +02:00
|
|
|
nm_utils_get_testing(void)
|
2016-03-01 09:56:51 +01:00
|
|
|
{
|
|
|
|
|
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
|
|
|
|
2021-11-09 13:28:54 +01:00
|
|
|
static GSList *_singletons = NULL;
|
2016-03-01 09:56:51 +01:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
gboolean
|
2020-11-19 11:20:19 +01:00
|
|
|
nm_ether_addr_is_valid(const NMEtherAddr *addr)
|
2016-03-01 09:56:51 +01:00
|
|
|
{
|
2020-11-19 11:20:19 +01:00
|
|
|
static const guint8 invalid_addr[][ETH_ALEN] = {
|
2016-03-01 09:56:51 +01:00
|
|
|
{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 */
|
|
|
|
|
};
|
2020-11-19 11:20:19 +01:00
|
|
|
int i;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-11-19 11:20:19 +01:00
|
|
|
if (!addr)
|
2016-03-01 09:56:51 +01:00
|
|
|
return FALSE;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
/* Check for multicast address */
|
2020-11-19 11:20:19 +01:00
|
|
|
if (addr->ether_addr_octet[0] & 0x01u)
|
2016-03-01 09:56:51 +01:00
|
|
|
return FALSE;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-11-19 11:20:19 +01:00
|
|
|
for (i = 0; i < (int) G_N_ELEMENTS(invalid_addr); i++) {
|
|
|
|
|
if (memcmp(addr, invalid_addr[i], ETH_ALEN) == 0)
|
2016-03-01 09:56:51 +01:00
|
|
|
return FALSE;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-19 11:20:19 +01:00
|
|
|
gboolean
|
|
|
|
|
nm_ether_addr_is_valid_str(const char *str)
|
|
|
|
|
{
|
|
|
|
|
NMEtherAddr addr_bin;
|
|
|
|
|
|
|
|
|
|
if (!str)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
if (!nm_utils_hwaddr_aton(str, &addr_bin, ETH_ALEN))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
return nm_ether_addr_is_valid(&addr_bin);
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
g_return_if_fail(array);
|
|
|
|
|
if (!len)
|
|
|
|
|
return;
|
|
|
|
|
g_return_if_fail(indexes_to_delete);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
elt_size = g_array_get_element_size(array);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
i_itd = 0;
|
|
|
|
|
index_to_delete = indexes_to_delete[0];
|
|
|
|
|
if (index_to_delete >= array->len)
|
|
|
|
|
g_return_if_reached();
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
res_length = array->len - 1;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
mm_dst = index_to_delete;
|
|
|
|
|
mm_src = index_to_delete;
|
|
|
|
|
mm_len = 0;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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
|
2020-07-01 17:20:40 -04:00
|
|
|
* indexes. Otherwise, we would need to presort the indexes. */
|
2016-03-01 09:56:51 +01:00
|
|
|
while (TRUE) {
|
|
|
|
|
guint dd;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
if (i_itd + 1 >= len) {
|
|
|
|
|
index_to_delete = G_MAXUINT;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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();
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
2021-11-09 13:28:54 +01:00
|
|
|
void *user_data;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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,
|
2021-11-09 13:28:54 +01:00
|
|
|
const char *log_name,
|
2016-03-01 09:56:51 +01:00
|
|
|
NMUtilsKillChildAsyncCb callback,
|
2021-11-09 13:28:54 +01:00
|
|
|
void *user_data)
|
2016-03-01 09:56:51 +01:00
|
|
|
{
|
|
|
|
|
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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
data->async.source_timeout_kill_id = 0;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
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,
|
2021-11-09 13:28:54 +01:00
|
|
|
const char *log_name,
|
2016-03-01 09:56:51 +01:00
|
|
|
NMUtilsKillChildAsyncCb callback,
|
2021-11-09 13:28:54 +01:00
|
|
|
void *user_data,
|
2016-03-01 09:56:51 +01:00
|
|
|
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;
|
|
|
|
|
|
2021-07-23 21:47:32 +02:00
|
|
|
nm_g_idle_add(_kc_invoke_callback_idle, data);
|
2016-03-01 09:56:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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,
|
2021-11-09 13:28:54 +01:00
|
|
|
const char *log_name,
|
2016-03-01 09:56:51 +01:00
|
|
|
guint32 wait_before_kill_msec,
|
|
|
|
|
NMUtilsKillChildAsyncCb callback,
|
2021-11-09 13:28:54 +01:00
|
|
|
void *user_data)
|
2016-03-01 09:56:51 +01:00
|
|
|
{
|
|
|
|
|
int status = 0, errsv;
|
|
|
|
|
pid_t ret;
|
|
|
|
|
KillChildAsyncData *data;
|
|
|
|
|
char buf_exit[KC_EXIT_TO_STRING_BUF_SIZE];
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
g_return_if_fail(pid > 0);
|
|
|
|
|
g_return_if_fail(log_name != NULL);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
/* 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;
|
2020-09-28 16:03:33 +02:00
|
|
|
}
|
2016-03-01 09:56:51 +01:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
/* 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;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
/* 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;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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();
|
2020-09-28 16:03:33 +02:00
|
|
|
|
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));
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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) {
|
2021-05-27 09:01:49 +02:00
|
|
|
guint64 x = ((guint64) sleep_duration_msec) * 1000UL;
|
2016-03-01 09:56:51 +01:00
|
|
|
|
2021-06-01 17:39:25 +02:00
|
|
|
nm_assert(x < G_MAXULONG);
|
|
|
|
|
return x;
|
2016-03-01 09:56:51 +01:00
|
|
|
}
|
|
|
|
|
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
|
2020-07-01 17:20:40 -04:00
|
|
|
* and if it did, return the exit status. Otherwise, send one @sig signal. @sig will always be
|
2016-03-01 09:56:51 +01:00
|
|
|
* 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,
|
2021-11-09 13:28:54 +01:00
|
|
|
int *child_status,
|
2016-03-01 09:56:51 +01:00
|
|
|
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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
g_return_val_if_fail(pid > 0, FALSE);
|
|
|
|
|
g_return_val_if_fail(log_name != NULL, FALSE);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
/* 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;
|
2020-09-28 16:03:33 +02:00
|
|
|
}
|
2016-03-01 09:56:51 +01:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
/* 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
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
}
|
2016-03-01 09:56:51 +01:00
|
|
|
goto out;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-12-13 16:54:30 +01:00
|
|
|
wait_start_us = nm_utils_get_monotonic_timestamp_usec();
|
2020-09-28 16:03:33 +02:00
|
|
|
|
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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
}
|
|
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
if (!wait_until)
|
|
|
|
|
break;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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);
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
/* 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);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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;
|
2020-09-28 16:03:33 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-03-01 09:56:51 +01:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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" : "");
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
/* block until the child terminates. */
|
|
|
|
|
while ((ret = waitpid(pid, &status, 0)) <= 0) {
|
|
|
|
|
errsv = errno;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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;
|
2020-09-28 16:03:33 +02:00
|
|
|
}
|
2016-03-01 09:56:51 +01:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
g_return_if_fail(pid > 0);
|
|
|
|
|
g_return_if_fail(log_name != NULL);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-24 10:46:01 +01:00
|
|
|
/* wait for the process to terminate... */
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2019-12-13 16:54:30 +01:00
|
|
|
wait_start_us = nm_utils_get_monotonic_timestamp_usec();
|
2020-09-28 16:03:33 +02:00
|
|
|
|
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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
while (TRUE) {
|
|
|
|
|
start_time = nm_utils_get_start_time_for_pid(pid, &p_state, NULL);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
sleep_time = sleep_duration_usec;
|
2019-12-13 16:54:30 +01:00
|
|
|
now = nm_utils_get_monotonic_timestamp_usec();
|
2020-09-28 16:03:33 +02:00
|
|
|
|
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;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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);
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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;
|
2021-03-03 20:57:01 +01:00
|
|
|
guint8 bin[_NM_UTILS_HWADDR_LEN_MAX];
|
2017-01-21 15:26:53 +01:00
|
|
|
} 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,
|
2021-11-09 13:28:54 +01:00
|
|
|
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;
|
2021-11-09 13:28:54 +01:00
|
|
|
char *pa = NULL, *pb = NULL, *pc = NULL;
|
2017-01-25 16:07:36 +01:00
|
|
|
gint64 a, b, c;
|
2020-09-28 16:03:33 +02: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);
|
2020-09-28 16:03:33 +02: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 (!g_ascii_isxdigit(s390_subchannels[0]))
|
2016-03-01 09:56:51 +01:00
|
|
|
return FALSE;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
/* Get the first channel */
|
2017-01-25 16:07:36 +01:00
|
|
|
for (i = 0; s390_subchannels[i]; i++) {
|
|
|
|
|
char ch = s390_subchannels[i];
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2017-01-25 16:07:36 +01:00
|
|
|
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';
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
/* 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';
|
2020-09-28 16:03:33 +02:00
|
|
|
|
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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
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;
|
2020-09-28 16:03:33 +02: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;
|
2020-09-28 16:03:33 +02: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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2017-01-21 15:26:53 +01:00
|
|
|
if (match_data->hwaddr.value) {
|
|
|
|
|
gsize l;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2017-01-21 15:26:53 +01:00
|
|
|
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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02: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 (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, MatchDeviceData *match_data)
|
|
|
|
|
{
|
2017-01-23 09:35:50 +01:00
|
|
|
if (spec_str[0] == '*' && spec_str[1] == '\0')
|
|
|
|
|
return TRUE;
|
2020-09-28 16:03:33 +02: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_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
|
|
|
}
|
2020-09-28 16:03:33 +02: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);
|
2020-09-28 16:03:33 +02: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;
|
2020-09-28 16:03:33 +02: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 (spec_str[0] == '=')
|
|
|
|
|
spec_str += 1;
|
|
|
|
|
else {
|
|
|
|
|
if (spec_str[0] == '~')
|
|
|
|
|
spec_str += 1;
|
|
|
|
|
use_pattern = TRUE;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
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;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2017-03-17 16:18:48 +01:00
|
|
|
if (_MATCH_CHECK(spec_str, DRIVER_TAG)) {
|
|
|
|
|
const char *t;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2017-03-17 16:18:48 +01:00
|
|
|
if (!match_data->driver)
|
|
|
|
|
return FALSE;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2017-03-17 16:18:48 +01:00
|
|
|
/* 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,
|
2021-11-09 13:28:54 +01:00
|
|
|
const char *interface_name,
|
|
|
|
|
const char *device_type,
|
|
|
|
|
const char *driver,
|
|
|
|
|
const char *driver_version,
|
|
|
|
|
const char *hwaddr,
|
|
|
|
|
const char *s390_subchannels,
|
|
|
|
|
const char *dhcp_plugin)
|
2016-03-01 09:56:51 +01:00
|
|
|
{
|
2021-11-09 13:28:54 +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;
|
2021-11-09 13:28:54 +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
|
|
|
};
|
2020-09-28 16:03:33 +02: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));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
if (!specs)
|
|
|
|
|
return NM_MATCH_SPEC_NO_MATCH;
|
2020-09-28 16:03:33 +02: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
|
|
|
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;
|
2020-09-28 16:03:33 +02: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
|
|
|
spec_str = iter->data;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
if (!spec_str || !*spec_str)
|
|
|
|
|
continue;
|
2020-09-28 16:03:33 +02: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
|
|
|
spec_str = match_except(spec_str, &except);
|
2020-09-28 16:03:33 +02: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;
|
2020-09-28 16:03:33 +02: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_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
|
|
|
}
|
2020-09-28 16:03:33 +02: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, &match_data))
|
|
|
|
|
continue;
|
2020-09-28 16:03:33 +02: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 (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
|
|
|
}
|
2020-09-28 16:03:33 +02: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
|
|
|
}
|
|
|
|
|
|
2021-06-03 09:01:18 +02:00
|
|
|
typedef struct {
|
|
|
|
|
const char *uuid;
|
|
|
|
|
const char *id;
|
|
|
|
|
const char *origin;
|
|
|
|
|
} MatchConnectionData;
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
match_connection_eval(const char *spec_str, const MatchConnectionData *match_data)
|
|
|
|
|
{
|
|
|
|
|
if (spec_str[0] == '*' && spec_str[1] == '\0')
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
if (_MATCH_CHECK(spec_str, "id:"))
|
|
|
|
|
return nm_streq0(spec_str, match_data->id);
|
|
|
|
|
|
|
|
|
|
if (_MATCH_CHECK(spec_str, "uuid:"))
|
|
|
|
|
return nm_streq0(spec_str, match_data->uuid);
|
|
|
|
|
|
|
|
|
|
if (_MATCH_CHECK(spec_str, "origin:"))
|
|
|
|
|
return nm_streq0(spec_str, match_data->origin);
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static NMMatchSpecMatchType
|
|
|
|
|
match_spec_connection(const GSList *specs, const char *id, const char *uuid, const char *origin)
|
|
|
|
|
{
|
2021-11-09 13:28:54 +01:00
|
|
|
const GSList *iter;
|
2021-06-03 09:01:18 +02:00
|
|
|
gboolean has_match = FALSE;
|
|
|
|
|
gboolean has_match_except = FALSE;
|
|
|
|
|
gboolean has_except = FALSE;
|
|
|
|
|
gboolean has_not_except = FALSE;
|
2021-11-09 13:28:54 +01:00
|
|
|
const char *spec_str;
|
2021-06-03 09:01:18 +02:00
|
|
|
const MatchConnectionData match_data = {
|
|
|
|
|
.id = nm_str_not_empty(id),
|
|
|
|
|
.uuid = nm_str_not_empty(uuid),
|
|
|
|
|
.origin = nm_str_not_empty(origin),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (!specs)
|
|
|
|
|
return NM_MATCH_SPEC_NO_MATCH;
|
|
|
|
|
|
|
|
|
|
for (iter = specs; iter; iter = iter->next) {
|
|
|
|
|
gboolean except;
|
|
|
|
|
|
|
|
|
|
spec_str = iter->data;
|
|
|
|
|
|
|
|
|
|
if (!spec_str || !*spec_str)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
spec_str = match_except(spec_str, &except);
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!match_connection_eval(spec_str, &match_data))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (except)
|
|
|
|
|
has_match_except = TRUE;
|
|
|
|
|
else
|
|
|
|
|
has_match = TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return _match_result(has_except, has_not_except, has_match, has_match_except);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
nm_utils_connection_match_spec_list(NMConnection *connection,
|
|
|
|
|
const GSList *specs,
|
|
|
|
|
int no_match_value)
|
|
|
|
|
{
|
|
|
|
|
NMMatchSpecMatchType m;
|
2021-11-09 13:28:54 +01:00
|
|
|
NMSettingUser *s_user;
|
|
|
|
|
const char *origin = NULL;
|
2021-06-03 09:01:18 +02:00
|
|
|
|
|
|
|
|
if (!specs)
|
|
|
|
|
return no_match_value;
|
|
|
|
|
|
|
|
|
|
s_user = _nm_connection_get_setting(connection, NM_TYPE_SETTING_USER);
|
|
|
|
|
if (s_user)
|
|
|
|
|
origin = nm_setting_user_get_data(s_user, NM_USER_TAG_ORIGIN);
|
|
|
|
|
|
|
|
|
|
m = match_spec_connection(specs,
|
|
|
|
|
nm_connection_get_id(connection),
|
|
|
|
|
nm_connection_get_uuid(connection),
|
|
|
|
|
origin);
|
|
|
|
|
switch (m) {
|
|
|
|
|
case NM_MATCH_SPEC_MATCH:
|
|
|
|
|
return TRUE;
|
|
|
|
|
case NM_MATCH_SPEC_NEG_MATCH:
|
|
|
|
|
return FALSE;
|
|
|
|
|
case NM_MATCH_SPEC_NO_MATCH:
|
|
|
|
|
return no_match_value;
|
|
|
|
|
}
|
|
|
|
|
nm_assert_not_reached();
|
|
|
|
|
return no_match_value;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
{
|
2021-11-09 13:28:54 +01:00
|
|
|
gs_free char *s_ver = NULL;
|
2016-03-01 09:56:51 +01:00
|
|
|
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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
s_ver = g_strdup(str);
|
|
|
|
|
g_strstrip(s_ver);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
/* Let's be strict with the accepted format here. No funny stuff!! */
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
if (s_ver[strspn(s_ver, ".0123456789")] != '\0')
|
|
|
|
|
return FALSE;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
nm_decode_version(cur_nm_version, &c_maj, &c_min, &c_mic);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
#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; \
|
2020-09-28 16:03:33 +02:00
|
|
|
} \
|
2016-03-01 09:56:51 +01:00
|
|
|
} \
|
|
|
|
|
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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
if (!specs)
|
|
|
|
|
return NM_MATCH_SPEC_NO_MATCH;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
for (iter = specs; iter; iter = g_slist_next(iter)) {
|
|
|
|
|
const char *spec_str = iter->data;
|
|
|
|
|
gboolean except;
|
|
|
|
|
gboolean v_match;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
if (!spec_str || !*spec_str)
|
|
|
|
|
continue;
|
2020-09-28 16:03:33 +02: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
|
|
|
spec_str = match_except(spec_str, &except);
|
2020-09-28 16:03:33 +02: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;
|
2020-09-28 16:03:33 +02: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_match_except) || (!except && has_match)) {
|
|
|
|
|
/* evaluating the match does not give new information. Skip it. */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02: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_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;
|
2020-09-28 16:03:33 +02: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 (!v_match)
|
2016-03-01 09:56:51 +01:00
|
|
|
continue;
|
2020-09-28 16:03:33 +02: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_match_except = TRUE;
|
|
|
|
|
else
|
|
|
|
|
has_match = TRUE;
|
2016-03-01 09:56:51 +01:00
|
|
|
}
|
2020-09-28 16:03:33 +02: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)
|
|
|
|
|
{
|
2021-11-09 13:28:54 +01:00
|
|
|
char *string_value, *p, *q0, *q;
|
2016-03-01 09:56:51 +01:00
|
|
|
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);
|
2020-09-28 16:03:33 +02: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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
/* skip over leading whitespace */
|
|
|
|
|
while (g_ascii_isspace(*p))
|
|
|
|
|
p++;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
q0 = q = string_value;
|
|
|
|
|
trailing_ws = 0;
|
|
|
|
|
while (*p) {
|
|
|
|
|
if (*p == '\\') {
|
|
|
|
|
p++;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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++;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
*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;
|
2021-11-09 13:28:54 +01:00
|
|
|
GString *str;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
str = g_string_new("");
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
for (; specs; specs = specs->next) {
|
|
|
|
|
p = specs->data;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
if (!p || !*p)
|
|
|
|
|
continue;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
if (str->len > 0)
|
|
|
|
|
g_string_append_c(str, ',');
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
/* escape leading whitespace */
|
|
|
|
|
switch (*p) {
|
|
|
|
|
case ' ':
|
|
|
|
|
g_string_append(str, "\\s");
|
|
|
|
|
p++;
|
|
|
|
|
break;
|
|
|
|
|
case '\t':
|
|
|
|
|
g_string_append(str, "\\t");
|
|
|
|
|
p++;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
return g_string_free(str, FALSE);
|
|
|
|
|
}
|
|
|
|
|
|
libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'
For simple matches like match.interface-name, match.driver, and
match.path, arguably what we had was fine. There each element
(like "eth*") is a wildcard for a single name (like "eth1").
However, for match.kernel-command-line, the elements match individual
command line options, so we should have more flexibility of whether
a parameter is optional or mandatory. Extend the syntax for that.
- the elements can now be prefixed by either '|' or '&'. This makes
optional or mandatory elements, respectively. The entire match
evaluates to true if all mandatory elements match (if any) and
at least one of the optional elements (if any).
As before, if neither '|' nor '&' is specified, then the element
is optional (that means, "foo" is the same as "|foo").
- the exclamation mark is still used to invert the match. If used
alone (like "!foo") it is a shortcut for defining a mandatory match
("&!foo").
- the backslash can now be used to escape the special characters
above. Basically, the special characters ('|', '&', '!') are
stripped from the start of the element. If what is left afterwards
is a backslash, it also gets stripped and the remainder is the
pattern. For example, "\\&foo" has the pattern "&foo" where
'&' is no longer treated specially. This special handling of
the backslash is only done at the beginning of the element (after
the optional special characters). The remaining string is part
of the pattern, where backslashes might have their own meaning.
This change is mostly backward compatible, except for existing matches
that started with one of the special characters '|', '&', '!', and '\\'.
2020-06-23 09:39:48 +02:00
|
|
|
static void
|
2021-11-09 13:28:54 +01:00
|
|
|
_pattern_parse(const char *input,
|
libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'
For simple matches like match.interface-name, match.driver, and
match.path, arguably what we had was fine. There each element
(like "eth*") is a wildcard for a single name (like "eth1").
However, for match.kernel-command-line, the elements match individual
command line options, so we should have more flexibility of whether
a parameter is optional or mandatory. Extend the syntax for that.
- the elements can now be prefixed by either '|' or '&'. This makes
optional or mandatory elements, respectively. The entire match
evaluates to true if all mandatory elements match (if any) and
at least one of the optional elements (if any).
As before, if neither '|' nor '&' is specified, then the element
is optional (that means, "foo" is the same as "|foo").
- the exclamation mark is still used to invert the match. If used
alone (like "!foo") it is a shortcut for defining a mandatory match
("&!foo").
- the backslash can now be used to escape the special characters
above. Basically, the special characters ('|', '&', '!') are
stripped from the start of the element. If what is left afterwards
is a backslash, it also gets stripped and the remainder is the
pattern. For example, "\\&foo" has the pattern "&foo" where
'&' is no longer treated specially. This special handling of
the backslash is only done at the beginning of the element (after
the optional special characters). The remaining string is part
of the pattern, where backslashes might have their own meaning.
This change is mostly backward compatible, except for existing matches
that started with one of the special characters '|', '&', '!', and '\\'.
2020-06-23 09:39:48 +02:00
|
|
|
const char **out_pattern,
|
2021-11-09 13:28:54 +01:00
|
|
|
gboolean *out_is_inverted,
|
|
|
|
|
gboolean *out_is_mandatory)
|
libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'
For simple matches like match.interface-name, match.driver, and
match.path, arguably what we had was fine. There each element
(like "eth*") is a wildcard for a single name (like "eth1").
However, for match.kernel-command-line, the elements match individual
command line options, so we should have more flexibility of whether
a parameter is optional or mandatory. Extend the syntax for that.
- the elements can now be prefixed by either '|' or '&'. This makes
optional or mandatory elements, respectively. The entire match
evaluates to true if all mandatory elements match (if any) and
at least one of the optional elements (if any).
As before, if neither '|' nor '&' is specified, then the element
is optional (that means, "foo" is the same as "|foo").
- the exclamation mark is still used to invert the match. If used
alone (like "!foo") it is a shortcut for defining a mandatory match
("&!foo").
- the backslash can now be used to escape the special characters
above. Basically, the special characters ('|', '&', '!') are
stripped from the start of the element. If what is left afterwards
is a backslash, it also gets stripped and the remainder is the
pattern. For example, "\\&foo" has the pattern "&foo" where
'&' is no longer treated specially. This special handling of
the backslash is only done at the beginning of the element (after
the optional special characters). The remaining string is part
of the pattern, where backslashes might have their own meaning.
This change is mostly backward compatible, except for existing matches
that started with one of the special characters '|', '&', '!', and '\\'.
2020-06-23 09:39:48 +02:00
|
|
|
{
|
|
|
|
|
gboolean is_inverted = FALSE;
|
|
|
|
|
gboolean is_mandatory = FALSE;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'
For simple matches like match.interface-name, match.driver, and
match.path, arguably what we had was fine. There each element
(like "eth*") is a wildcard for a single name (like "eth1").
However, for match.kernel-command-line, the elements match individual
command line options, so we should have more flexibility of whether
a parameter is optional or mandatory. Extend the syntax for that.
- the elements can now be prefixed by either '|' or '&'. This makes
optional or mandatory elements, respectively. The entire match
evaluates to true if all mandatory elements match (if any) and
at least one of the optional elements (if any).
As before, if neither '|' nor '&' is specified, then the element
is optional (that means, "foo" is the same as "|foo").
- the exclamation mark is still used to invert the match. If used
alone (like "!foo") it is a shortcut for defining a mandatory match
("&!foo").
- the backslash can now be used to escape the special characters
above. Basically, the special characters ('|', '&', '!') are
stripped from the start of the element. If what is left afterwards
is a backslash, it also gets stripped and the remainder is the
pattern. For example, "\\&foo" has the pattern "&foo" where
'&' is no longer treated specially. This special handling of
the backslash is only done at the beginning of the element (after
the optional special characters). The remaining string is part
of the pattern, where backslashes might have their own meaning.
This change is mostly backward compatible, except for existing matches
that started with one of the special characters '|', '&', '!', and '\\'.
2020-06-23 09:39:48 +02:00
|
|
|
if (input[0] == '&') {
|
|
|
|
|
input++;
|
|
|
|
|
is_mandatory = TRUE;
|
|
|
|
|
if (input[0] == '!') {
|
|
|
|
|
input++;
|
|
|
|
|
is_inverted = TRUE;
|
|
|
|
|
}
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'
For simple matches like match.interface-name, match.driver, and
match.path, arguably what we had was fine. There each element
(like "eth*") is a wildcard for a single name (like "eth1").
However, for match.kernel-command-line, the elements match individual
command line options, so we should have more flexibility of whether
a parameter is optional or mandatory. Extend the syntax for that.
- the elements can now be prefixed by either '|' or '&'. This makes
optional or mandatory elements, respectively. The entire match
evaluates to true if all mandatory elements match (if any) and
at least one of the optional elements (if any).
As before, if neither '|' nor '&' is specified, then the element
is optional (that means, "foo" is the same as "|foo").
- the exclamation mark is still used to invert the match. If used
alone (like "!foo") it is a shortcut for defining a mandatory match
("&!foo").
- the backslash can now be used to escape the special characters
above. Basically, the special characters ('|', '&', '!') are
stripped from the start of the element. If what is left afterwards
is a backslash, it also gets stripped and the remainder is the
pattern. For example, "\\&foo" has the pattern "&foo" where
'&' is no longer treated specially. This special handling of
the backslash is only done at the beginning of the element (after
the optional special characters). The remaining string is part
of the pattern, where backslashes might have their own meaning.
This change is mostly backward compatible, except for existing matches
that started with one of the special characters '|', '&', '!', and '\\'.
2020-06-23 09:39:48 +02:00
|
|
|
if (input[0] == '|') {
|
|
|
|
|
input++;
|
|
|
|
|
if (input[0] == '!') {
|
|
|
|
|
input++;
|
|
|
|
|
is_inverted = TRUE;
|
|
|
|
|
}
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'
For simple matches like match.interface-name, match.driver, and
match.path, arguably what we had was fine. There each element
(like "eth*") is a wildcard for a single name (like "eth1").
However, for match.kernel-command-line, the elements match individual
command line options, so we should have more flexibility of whether
a parameter is optional or mandatory. Extend the syntax for that.
- the elements can now be prefixed by either '|' or '&'. This makes
optional or mandatory elements, respectively. The entire match
evaluates to true if all mandatory elements match (if any) and
at least one of the optional elements (if any).
As before, if neither '|' nor '&' is specified, then the element
is optional (that means, "foo" is the same as "|foo").
- the exclamation mark is still used to invert the match. If used
alone (like "!foo") it is a shortcut for defining a mandatory match
("&!foo").
- the backslash can now be used to escape the special characters
above. Basically, the special characters ('|', '&', '!') are
stripped from the start of the element. If what is left afterwards
is a backslash, it also gets stripped and the remainder is the
pattern. For example, "\\&foo" has the pattern "&foo" where
'&' is no longer treated specially. This special handling of
the backslash is only done at the beginning of the element (after
the optional special characters). The remaining string is part
of the pattern, where backslashes might have their own meaning.
This change is mostly backward compatible, except for existing matches
that started with one of the special characters '|', '&', '!', and '\\'.
2020-06-23 09:39:48 +02:00
|
|
|
if (input[0] == '!') {
|
|
|
|
|
input++;
|
|
|
|
|
is_inverted = TRUE;
|
|
|
|
|
is_mandatory = TRUE;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
if (input[0] == '\\')
|
|
|
|
|
input++;
|
|
|
|
|
|
|
|
|
|
*out_pattern = input;
|
|
|
|
|
*out_is_inverted = is_inverted;
|
|
|
|
|
*out_is_mandatory = is_mandatory;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-09 09:29:51 +02:00
|
|
|
gboolean
|
|
|
|
|
nm_wildcard_match_check(const char *str, const char *const *patterns, guint num_patterns)
|
|
|
|
|
{
|
libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'
For simple matches like match.interface-name, match.driver, and
match.path, arguably what we had was fine. There each element
(like "eth*") is a wildcard for a single name (like "eth1").
However, for match.kernel-command-line, the elements match individual
command line options, so we should have more flexibility of whether
a parameter is optional or mandatory. Extend the syntax for that.
- the elements can now be prefixed by either '|' or '&'. This makes
optional or mandatory elements, respectively. The entire match
evaluates to true if all mandatory elements match (if any) and
at least one of the optional elements (if any).
As before, if neither '|' nor '&' is specified, then the element
is optional (that means, "foo" is the same as "|foo").
- the exclamation mark is still used to invert the match. If used
alone (like "!foo") it is a shortcut for defining a mandatory match
("&!foo").
- the backslash can now be used to escape the special characters
above. Basically, the special characters ('|', '&', '!') are
stripped from the start of the element. If what is left afterwards
is a backslash, it also gets stripped and the remainder is the
pattern. For example, "\\&foo" has the pattern "&foo" where
'&' is no longer treated specially. This special handling of
the backslash is only done at the beginning of the element (after
the optional special characters). The remaining string is part
of the pattern, where backslashes might have their own meaning.
This change is mostly backward compatible, except for existing matches
that started with one of the special characters '|', '&', '!', and '\\'.
2020-06-23 09:39:48 +02:00
|
|
|
gboolean has_optional = FALSE;
|
|
|
|
|
gboolean has_any_optional = FALSE;
|
|
|
|
|
guint i;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-09 09:29:51 +02:00
|
|
|
for (i = 0; i < num_patterns; i++) {
|
libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'
For simple matches like match.interface-name, match.driver, and
match.path, arguably what we had was fine. There each element
(like "eth*") is a wildcard for a single name (like "eth1").
However, for match.kernel-command-line, the elements match individual
command line options, so we should have more flexibility of whether
a parameter is optional or mandatory. Extend the syntax for that.
- the elements can now be prefixed by either '|' or '&'. This makes
optional or mandatory elements, respectively. The entire match
evaluates to true if all mandatory elements match (if any) and
at least one of the optional elements (if any).
As before, if neither '|' nor '&' is specified, then the element
is optional (that means, "foo" is the same as "|foo").
- the exclamation mark is still used to invert the match. If used
alone (like "!foo") it is a shortcut for defining a mandatory match
("&!foo").
- the backslash can now be used to escape the special characters
above. Basically, the special characters ('|', '&', '!') are
stripped from the start of the element. If what is left afterwards
is a backslash, it also gets stripped and the remainder is the
pattern. For example, "\\&foo" has the pattern "&foo" where
'&' is no longer treated specially. This special handling of
the backslash is only done at the beginning of the element (after
the optional special characters). The remaining string is part
of the pattern, where backslashes might have their own meaning.
This change is mostly backward compatible, except for existing matches
that started with one of the special characters '|', '&', '!', and '\\'.
2020-06-23 09:39:48 +02:00
|
|
|
gboolean is_inverted;
|
|
|
|
|
gboolean is_mandatory;
|
|
|
|
|
gboolean match;
|
|
|
|
|
const char *p;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'
For simple matches like match.interface-name, match.driver, and
match.path, arguably what we had was fine. There each element
(like "eth*") is a wildcard for a single name (like "eth1").
However, for match.kernel-command-line, the elements match individual
command line options, so we should have more flexibility of whether
a parameter is optional or mandatory. Extend the syntax for that.
- the elements can now be prefixed by either '|' or '&'. This makes
optional or mandatory elements, respectively. The entire match
evaluates to true if all mandatory elements match (if any) and
at least one of the optional elements (if any).
As before, if neither '|' nor '&' is specified, then the element
is optional (that means, "foo" is the same as "|foo").
- the exclamation mark is still used to invert the match. If used
alone (like "!foo") it is a shortcut for defining a mandatory match
("&!foo").
- the backslash can now be used to escape the special characters
above. Basically, the special characters ('|', '&', '!') are
stripped from the start of the element. If what is left afterwards
is a backslash, it also gets stripped and the remainder is the
pattern. For example, "\\&foo" has the pattern "&foo" where
'&' is no longer treated specially. This special handling of
the backslash is only done at the beginning of the element (after
the optional special characters). The remaining string is part
of the pattern, where backslashes might have their own meaning.
This change is mostly backward compatible, except for existing matches
that started with one of the special characters '|', '&', '!', and '\\'.
2020-06-23 09:39:48 +02:00
|
|
|
_pattern_parse(patterns[i], &p, &is_inverted, &is_mandatory);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
core: fix crash in nm_wildcard_match_check()
It's not entirely clear how to treat %NULL.
Clearly "match.interface-name=eth0" should not
match with an interface %NULL. But what about
"match.interface-name=!eth0"? It's now implemented
that negative matches still succeed against %NULL.
What about "match.interface-name=*"? That probably
should also match with %NULL. So we treat %NULL really
like "".
Against commit 11cd443448bc ('iwd: Don't call IWD methods when device
unmanaged'), we got this backtrace:
#0 0x00007f1c164069f1 in __strnlen_avx2 () at ../sysdeps/x86_64/multiarch/strlen-avx2.S:62
#1 0x00007f1c1637ac9e in __fnmatch (pattern=<optimized out>, string=<optimized out>, string@entry=0x0, flags=flags@entry=0) at fnmatch.c:379
p = 0x0
res = <optimized out>
orig_pattern = <optimized out>
n = <optimized out>
wpattern = 0x7fff8d860730 L"pci-0000:03:00.0"
ps = {__count = 0, __value = {__wch = 0, __wchb = "\000\000\000"}}
wpattern_malloc = 0x0
wstring_malloc = 0x0
wstring = <optimized out>
alloca_used = 80
__PRETTY_FUNCTION__ = "__fnmatch"
#2 0x0000564484a978bf in nm_wildcard_match_check (str=0x0, patterns=<optimized out>, num_patterns=<optimized out>) at src/core/nm-core-utils.c:1959
is_inverted = 0
is_mandatory = 0
match = <optimized out>
p = 0x564486c43fa0 "pci-0000:03:00.0"
has_optional = 0
has_any_optional = 0
i = <optimized out>
#3 0x0000564484bf4797 in check_connection_compatible (self=<optimized out>, connection=<optimized out>, error=0x0) at src/core/devices/nm-device.c:7499
patterns = <optimized out>
device_driver = 0x564486c76bd0 "veth"
num_patterns = 1
priv = 0x564486cbe0b0
__func__ = "check_connection_compatible"
device_iface = <optimized out>
local = 0x564486c99a60
conn_iface = 0x0
klass = <optimized out>
s_match = 0x564486c63df0 [NMSettingMatch]
#4 0x0000564484c38491 in check_connection_compatible (device=0x564486cbe590 [NMDeviceVeth], connection=0x564486c6b160, error=0x0) at src/core/devices/nm-device-ethernet.c:348
self = 0x564486cbe590 [NMDeviceVeth]
s_wired = <optimized out>
Fixes: 3ced486f4162 ('libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'')
https://bugzilla.redhat.com/show_bug.cgi?id=1942741
2021-03-24 21:05:19 +01:00
|
|
|
match = (fnmatch(p, str ?: "", 0) == 0);
|
|
|
|
|
|
libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'
For simple matches like match.interface-name, match.driver, and
match.path, arguably what we had was fine. There each element
(like "eth*") is a wildcard for a single name (like "eth1").
However, for match.kernel-command-line, the elements match individual
command line options, so we should have more flexibility of whether
a parameter is optional or mandatory. Extend the syntax for that.
- the elements can now be prefixed by either '|' or '&'. This makes
optional or mandatory elements, respectively. The entire match
evaluates to true if all mandatory elements match (if any) and
at least one of the optional elements (if any).
As before, if neither '|' nor '&' is specified, then the element
is optional (that means, "foo" is the same as "|foo").
- the exclamation mark is still used to invert the match. If used
alone (like "!foo") it is a shortcut for defining a mandatory match
("&!foo").
- the backslash can now be used to escape the special characters
above. Basically, the special characters ('|', '&', '!') are
stripped from the start of the element. If what is left afterwards
is a backslash, it also gets stripped and the remainder is the
pattern. For example, "\\&foo" has the pattern "&foo" where
'&' is no longer treated specially. This special handling of
the backslash is only done at the beginning of the element (after
the optional special characters). The remaining string is part
of the pattern, where backslashes might have their own meaning.
This change is mostly backward compatible, except for existing matches
that started with one of the special characters '|', '&', '!', and '\\'.
2020-06-23 09:39:48 +02:00
|
|
|
if (is_inverted)
|
|
|
|
|
match = !match;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'
For simple matches like match.interface-name, match.driver, and
match.path, arguably what we had was fine. There each element
(like "eth*") is a wildcard for a single name (like "eth1").
However, for match.kernel-command-line, the elements match individual
command line options, so we should have more flexibility of whether
a parameter is optional or mandatory. Extend the syntax for that.
- the elements can now be prefixed by either '|' or '&'. This makes
optional or mandatory elements, respectively. The entire match
evaluates to true if all mandatory elements match (if any) and
at least one of the optional elements (if any).
As before, if neither '|' nor '&' is specified, then the element
is optional (that means, "foo" is the same as "|foo").
- the exclamation mark is still used to invert the match. If used
alone (like "!foo") it is a shortcut for defining a mandatory match
("&!foo").
- the backslash can now be used to escape the special characters
above. Basically, the special characters ('|', '&', '!') are
stripped from the start of the element. If what is left afterwards
is a backslash, it also gets stripped and the remainder is the
pattern. For example, "\\&foo" has the pattern "&foo" where
'&' is no longer treated specially. This special handling of
the backslash is only done at the beginning of the element (after
the optional special characters). The remaining string is part
of the pattern, where backslashes might have their own meaning.
This change is mostly backward compatible, except for existing matches
that started with one of the special characters '|', '&', '!', and '\\'.
2020-06-23 09:39:48 +02:00
|
|
|
if (is_mandatory) {
|
|
|
|
|
if (!match)
|
2018-08-09 09:29:51 +02:00
|
|
|
return FALSE;
|
libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'
For simple matches like match.interface-name, match.driver, and
match.path, arguably what we had was fine. There each element
(like "eth*") is a wildcard for a single name (like "eth1").
However, for match.kernel-command-line, the elements match individual
command line options, so we should have more flexibility of whether
a parameter is optional or mandatory. Extend the syntax for that.
- the elements can now be prefixed by either '|' or '&'. This makes
optional or mandatory elements, respectively. The entire match
evaluates to true if all mandatory elements match (if any) and
at least one of the optional elements (if any).
As before, if neither '|' nor '&' is specified, then the element
is optional (that means, "foo" is the same as "|foo").
- the exclamation mark is still used to invert the match. If used
alone (like "!foo") it is a shortcut for defining a mandatory match
("&!foo").
- the backslash can now be used to escape the special characters
above. Basically, the special characters ('|', '&', '!') are
stripped from the start of the element. If what is left afterwards
is a backslash, it also gets stripped and the remainder is the
pattern. For example, "\\&foo" has the pattern "&foo" where
'&' is no longer treated specially. This special handling of
the backslash is only done at the beginning of the element (after
the optional special characters). The remaining string is part
of the pattern, where backslashes might have their own meaning.
This change is mostly backward compatible, except for existing matches
that started with one of the special characters '|', '&', '!', and '\\'.
2020-06-23 09:39:48 +02:00
|
|
|
} else {
|
|
|
|
|
has_any_optional = TRUE;
|
|
|
|
|
if (match)
|
|
|
|
|
has_optional = TRUE;
|
2018-08-09 09:29:51 +02:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
}
|
|
|
|
|
|
libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'
For simple matches like match.interface-name, match.driver, and
match.path, arguably what we had was fine. There each element
(like "eth*") is a wildcard for a single name (like "eth1").
However, for match.kernel-command-line, the elements match individual
command line options, so we should have more flexibility of whether
a parameter is optional or mandatory. Extend the syntax for that.
- the elements can now be prefixed by either '|' or '&'. This makes
optional or mandatory elements, respectively. The entire match
evaluates to true if all mandatory elements match (if any) and
at least one of the optional elements (if any).
As before, if neither '|' nor '&' is specified, then the element
is optional (that means, "foo" is the same as "|foo").
- the exclamation mark is still used to invert the match. If used
alone (like "!foo") it is a shortcut for defining a mandatory match
("&!foo").
- the backslash can now be used to escape the special characters
above. Basically, the special characters ('|', '&', '!') are
stripped from the start of the element. If what is left afterwards
is a backslash, it also gets stripped and the remainder is the
pattern. For example, "\\&foo" has the pattern "&foo" where
'&' is no longer treated specially. This special handling of
the backslash is only done at the beginning of the element (after
the optional special characters). The remaining string is part
of the pattern, where backslashes might have their own meaning.
This change is mostly backward compatible, except for existing matches
that started with one of the special characters '|', '&', '!', and '\\'.
2020-06-23 09:39:48 +02:00
|
|
|
return has_optional || !has_any_optional;
|
|
|
|
|
}
|
2018-08-09 09:29:51 +02:00
|
|
|
|
libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'
For simple matches like match.interface-name, match.driver, and
match.path, arguably what we had was fine. There each element
(like "eth*") is a wildcard for a single name (like "eth1").
However, for match.kernel-command-line, the elements match individual
command line options, so we should have more flexibility of whether
a parameter is optional or mandatory. Extend the syntax for that.
- the elements can now be prefixed by either '|' or '&'. This makes
optional or mandatory elements, respectively. The entire match
evaluates to true if all mandatory elements match (if any) and
at least one of the optional elements (if any).
As before, if neither '|' nor '&' is specified, then the element
is optional (that means, "foo" is the same as "|foo").
- the exclamation mark is still used to invert the match. If used
alone (like "!foo") it is a shortcut for defining a mandatory match
("&!foo").
- the backslash can now be used to escape the special characters
above. Basically, the special characters ('|', '&', '!') are
stripped from the start of the element. If what is left afterwards
is a backslash, it also gets stripped and the remainder is the
pattern. For example, "\\&foo" has the pattern "&foo" where
'&' is no longer treated specially. This special handling of
the backslash is only done at the beginning of the element (after
the optional special characters). The remaining string is part
of the pattern, where backslashes might have their own meaning.
This change is mostly backward compatible, except for existing matches
that started with one of the special characters '|', '&', '!', and '\\'.
2020-06-23 09:39:48 +02:00
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
_kernel_cmdline_match(const char *const *proc_cmdline, const char *pattern)
|
|
|
|
|
{
|
|
|
|
|
if (proc_cmdline) {
|
|
|
|
|
gboolean has_equal = (!!strchr(pattern, '='));
|
|
|
|
|
gsize pattern_len = strlen(pattern);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'
For simple matches like match.interface-name, match.driver, and
match.path, arguably what we had was fine. There each element
(like "eth*") is a wildcard for a single name (like "eth1").
However, for match.kernel-command-line, the elements match individual
command line options, so we should have more flexibility of whether
a parameter is optional or mandatory. Extend the syntax for that.
- the elements can now be prefixed by either '|' or '&'. This makes
optional or mandatory elements, respectively. The entire match
evaluates to true if all mandatory elements match (if any) and
at least one of the optional elements (if any).
As before, if neither '|' nor '&' is specified, then the element
is optional (that means, "foo" is the same as "|foo").
- the exclamation mark is still used to invert the match. If used
alone (like "!foo") it is a shortcut for defining a mandatory match
("&!foo").
- the backslash can now be used to escape the special characters
above. Basically, the special characters ('|', '&', '!') are
stripped from the start of the element. If what is left afterwards
is a backslash, it also gets stripped and the remainder is the
pattern. For example, "\\&foo" has the pattern "&foo" where
'&' is no longer treated specially. This special handling of
the backslash is only done at the beginning of the element (after
the optional special characters). The remaining string is part
of the pattern, where backslashes might have their own meaning.
This change is mostly backward compatible, except for existing matches
that started with one of the special characters '|', '&', '!', and '\\'.
2020-06-23 09:39:48 +02:00
|
|
|
for (; proc_cmdline[0]; proc_cmdline++) {
|
|
|
|
|
const char *c = proc_cmdline[0];
|
2020-09-28 16:03:33 +02:00
|
|
|
|
libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'
For simple matches like match.interface-name, match.driver, and
match.path, arguably what we had was fine. There each element
(like "eth*") is a wildcard for a single name (like "eth1").
However, for match.kernel-command-line, the elements match individual
command line options, so we should have more flexibility of whether
a parameter is optional or mandatory. Extend the syntax for that.
- the elements can now be prefixed by either '|' or '&'. This makes
optional or mandatory elements, respectively. The entire match
evaluates to true if all mandatory elements match (if any) and
at least one of the optional elements (if any).
As before, if neither '|' nor '&' is specified, then the element
is optional (that means, "foo" is the same as "|foo").
- the exclamation mark is still used to invert the match. If used
alone (like "!foo") it is a shortcut for defining a mandatory match
("&!foo").
- the backslash can now be used to escape the special characters
above. Basically, the special characters ('|', '&', '!') are
stripped from the start of the element. If what is left afterwards
is a backslash, it also gets stripped and the remainder is the
pattern. For example, "\\&foo" has the pattern "&foo" where
'&' is no longer treated specially. This special handling of
the backslash is only done at the beginning of the element (after
the optional special characters). The remaining string is part
of the pattern, where backslashes might have their own meaning.
This change is mostly backward compatible, except for existing matches
that started with one of the special characters '|', '&', '!', and '\\'.
2020-06-23 09:39:48 +02:00
|
|
|
if (has_equal) {
|
|
|
|
|
/* if pattern contains '=' compare full key=value */
|
|
|
|
|
if (nm_streq(c, pattern))
|
|
|
|
|
return TRUE;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'
For simple matches like match.interface-name, match.driver, and
match.path, arguably what we had was fine. There each element
(like "eth*") is a wildcard for a single name (like "eth1").
However, for match.kernel-command-line, the elements match individual
command line options, so we should have more flexibility of whether
a parameter is optional or mandatory. Extend the syntax for that.
- the elements can now be prefixed by either '|' or '&'. This makes
optional or mandatory elements, respectively. The entire match
evaluates to true if all mandatory elements match (if any) and
at least one of the optional elements (if any).
As before, if neither '|' nor '&' is specified, then the element
is optional (that means, "foo" is the same as "|foo").
- the exclamation mark is still used to invert the match. If used
alone (like "!foo") it is a shortcut for defining a mandatory match
("&!foo").
- the backslash can now be used to escape the special characters
above. Basically, the special characters ('|', '&', '!') are
stripped from the start of the element. If what is left afterwards
is a backslash, it also gets stripped and the remainder is the
pattern. For example, "\\&foo" has the pattern "&foo" where
'&' is no longer treated specially. This special handling of
the backslash is only done at the beginning of the element (after
the optional special characters). The remaining string is part
of the pattern, where backslashes might have their own meaning.
This change is mostly backward compatible, except for existing matches
that started with one of the special characters '|', '&', '!', and '\\'.
2020-06-23 09:39:48 +02:00
|
|
|
/* otherwise consider pattern as key only */
|
|
|
|
|
if (strncmp(c, pattern, pattern_len) == 0 && NM_IN_SET(c[pattern_len], '\0', '='))
|
2020-05-06 16:10:30 +02:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
2018-08-09 09:29:51 +02:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-08-09 09:29:51 +02:00
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-23 08:39:04 +02:00
|
|
|
gboolean
|
|
|
|
|
nm_utils_kernel_cmdline_match_check(const char *const *proc_cmdline,
|
|
|
|
|
const char *const *patterns,
|
|
|
|
|
guint num_patterns,
|
2021-11-09 13:28:54 +01:00
|
|
|
GError **error)
|
2020-06-23 08:39:04 +02:00
|
|
|
{
|
libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'
For simple matches like match.interface-name, match.driver, and
match.path, arguably what we had was fine. There each element
(like "eth*") is a wildcard for a single name (like "eth1").
However, for match.kernel-command-line, the elements match individual
command line options, so we should have more flexibility of whether
a parameter is optional or mandatory. Extend the syntax for that.
- the elements can now be prefixed by either '|' or '&'. This makes
optional or mandatory elements, respectively. The entire match
evaluates to true if all mandatory elements match (if any) and
at least one of the optional elements (if any).
As before, if neither '|' nor '&' is specified, then the element
is optional (that means, "foo" is the same as "|foo").
- the exclamation mark is still used to invert the match. If used
alone (like "!foo") it is a shortcut for defining a mandatory match
("&!foo").
- the backslash can now be used to escape the special characters
above. Basically, the special characters ('|', '&', '!') are
stripped from the start of the element. If what is left afterwards
is a backslash, it also gets stripped and the remainder is the
pattern. For example, "\\&foo" has the pattern "&foo" where
'&' is no longer treated specially. This special handling of
the backslash is only done at the beginning of the element (after
the optional special characters). The remaining string is part
of the pattern, where backslashes might have their own meaning.
This change is mostly backward compatible, except for existing matches
that started with one of the special characters '|', '&', '!', and '\\'.
2020-06-23 09:39:48 +02:00
|
|
|
gboolean has_optional = FALSE;
|
|
|
|
|
gboolean has_any_optional = FALSE;
|
2020-06-23 08:39:04 +02:00
|
|
|
guint i;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-06-23 08:39:04 +02:00
|
|
|
for (i = 0; i < num_patterns; i++) {
|
libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'
For simple matches like match.interface-name, match.driver, and
match.path, arguably what we had was fine. There each element
(like "eth*") is a wildcard for a single name (like "eth1").
However, for match.kernel-command-line, the elements match individual
command line options, so we should have more flexibility of whether
a parameter is optional or mandatory. Extend the syntax for that.
- the elements can now be prefixed by either '|' or '&'. This makes
optional or mandatory elements, respectively. The entire match
evaluates to true if all mandatory elements match (if any) and
at least one of the optional elements (if any).
As before, if neither '|' nor '&' is specified, then the element
is optional (that means, "foo" is the same as "|foo").
- the exclamation mark is still used to invert the match. If used
alone (like "!foo") it is a shortcut for defining a mandatory match
("&!foo").
- the backslash can now be used to escape the special characters
above. Basically, the special characters ('|', '&', '!') are
stripped from the start of the element. If what is left afterwards
is a backslash, it also gets stripped and the remainder is the
pattern. For example, "\\&foo" has the pattern "&foo" where
'&' is no longer treated specially. This special handling of
the backslash is only done at the beginning of the element (after
the optional special characters). The remaining string is part
of the pattern, where backslashes might have their own meaning.
This change is mostly backward compatible, except for existing matches
that started with one of the special characters '|', '&', '!', and '\\'.
2020-06-23 09:39:48 +02:00
|
|
|
const char *element = patterns[i];
|
|
|
|
|
gboolean is_inverted = FALSE;
|
|
|
|
|
gboolean is_mandatory = FALSE;
|
|
|
|
|
gboolean match;
|
|
|
|
|
const char *p;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'
For simple matches like match.interface-name, match.driver, and
match.path, arguably what we had was fine. There each element
(like "eth*") is a wildcard for a single name (like "eth1").
However, for match.kernel-command-line, the elements match individual
command line options, so we should have more flexibility of whether
a parameter is optional or mandatory. Extend the syntax for that.
- the elements can now be prefixed by either '|' or '&'. This makes
optional or mandatory elements, respectively. The entire match
evaluates to true if all mandatory elements match (if any) and
at least one of the optional elements (if any).
As before, if neither '|' nor '&' is specified, then the element
is optional (that means, "foo" is the same as "|foo").
- the exclamation mark is still used to invert the match. If used
alone (like "!foo") it is a shortcut for defining a mandatory match
("&!foo").
- the backslash can now be used to escape the special characters
above. Basically, the special characters ('|', '&', '!') are
stripped from the start of the element. If what is left afterwards
is a backslash, it also gets stripped and the remainder is the
pattern. For example, "\\&foo" has the pattern "&foo" where
'&' is no longer treated specially. This special handling of
the backslash is only done at the beginning of the element (after
the optional special characters). The remaining string is part
of the pattern, where backslashes might have their own meaning.
This change is mostly backward compatible, except for existing matches
that started with one of the special characters '|', '&', '!', and '\\'.
2020-06-23 09:39:48 +02:00
|
|
|
_pattern_parse(element, &p, &is_inverted, &is_mandatory);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'
For simple matches like match.interface-name, match.driver, and
match.path, arguably what we had was fine. There each element
(like "eth*") is a wildcard for a single name (like "eth1").
However, for match.kernel-command-line, the elements match individual
command line options, so we should have more flexibility of whether
a parameter is optional or mandatory. Extend the syntax for that.
- the elements can now be prefixed by either '|' or '&'. This makes
optional or mandatory elements, respectively. The entire match
evaluates to true if all mandatory elements match (if any) and
at least one of the optional elements (if any).
As before, if neither '|' nor '&' is specified, then the element
is optional (that means, "foo" is the same as "|foo").
- the exclamation mark is still used to invert the match. If used
alone (like "!foo") it is a shortcut for defining a mandatory match
("&!foo").
- the backslash can now be used to escape the special characters
above. Basically, the special characters ('|', '&', '!') are
stripped from the start of the element. If what is left afterwards
is a backslash, it also gets stripped and the remainder is the
pattern. For example, "\\&foo" has the pattern "&foo" where
'&' is no longer treated specially. This special handling of
the backslash is only done at the beginning of the element (after
the optional special characters). The remaining string is part
of the pattern, where backslashes might have their own meaning.
This change is mostly backward compatible, except for existing matches
that started with one of the special characters '|', '&', '!', and '\\'.
2020-06-23 09:39:48 +02:00
|
|
|
match = _kernel_cmdline_match(proc_cmdline, p);
|
|
|
|
|
if (is_inverted)
|
|
|
|
|
match = !match;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'
For simple matches like match.interface-name, match.driver, and
match.path, arguably what we had was fine. There each element
(like "eth*") is a wildcard for a single name (like "eth1").
However, for match.kernel-command-line, the elements match individual
command line options, so we should have more flexibility of whether
a parameter is optional or mandatory. Extend the syntax for that.
- the elements can now be prefixed by either '|' or '&'. This makes
optional or mandatory elements, respectively. The entire match
evaluates to true if all mandatory elements match (if any) and
at least one of the optional elements (if any).
As before, if neither '|' nor '&' is specified, then the element
is optional (that means, "foo" is the same as "|foo").
- the exclamation mark is still used to invert the match. If used
alone (like "!foo") it is a shortcut for defining a mandatory match
("&!foo").
- the backslash can now be used to escape the special characters
above. Basically, the special characters ('|', '&', '!') are
stripped from the start of the element. If what is left afterwards
is a backslash, it also gets stripped and the remainder is the
pattern. For example, "\\&foo" has the pattern "&foo" where
'&' is no longer treated specially. This special handling of
the backslash is only done at the beginning of the element (after
the optional special characters). The remaining string is part
of the pattern, where backslashes might have their own meaning.
This change is mostly backward compatible, except for existing matches
that started with one of the special characters '|', '&', '!', and '\\'.
2020-06-23 09:39:48 +02:00
|
|
|
if (is_mandatory) {
|
|
|
|
|
if (!match) {
|
2020-06-23 08:39:04 +02:00
|
|
|
nm_utils_error_set(error,
|
|
|
|
|
NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
|
|
|
|
|
"device does not satisfy match.kernel-command-line property %s",
|
|
|
|
|
patterns[i]);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'
For simple matches like match.interface-name, match.driver, and
match.path, arguably what we had was fine. There each element
(like "eth*") is a wildcard for a single name (like "eth1").
However, for match.kernel-command-line, the elements match individual
command line options, so we should have more flexibility of whether
a parameter is optional or mandatory. Extend the syntax for that.
- the elements can now be prefixed by either '|' or '&'. This makes
optional or mandatory elements, respectively. The entire match
evaluates to true if all mandatory elements match (if any) and
at least one of the optional elements (if any).
As before, if neither '|' nor '&' is specified, then the element
is optional (that means, "foo" is the same as "|foo").
- the exclamation mark is still used to invert the match. If used
alone (like "!foo") it is a shortcut for defining a mandatory match
("&!foo").
- the backslash can now be used to escape the special characters
above. Basically, the special characters ('|', '&', '!') are
stripped from the start of the element. If what is left afterwards
is a backslash, it also gets stripped and the remainder is the
pattern. For example, "\\&foo" has the pattern "&foo" where
'&' is no longer treated specially. This special handling of
the backslash is only done at the beginning of the element (after
the optional special characters). The remaining string is part
of the pattern, where backslashes might have their own meaning.
This change is mostly backward compatible, except for existing matches
that started with one of the special characters '|', '&', '!', and '\\'.
2020-06-23 09:39:48 +02:00
|
|
|
} else {
|
|
|
|
|
has_any_optional = TRUE;
|
|
|
|
|
if (match)
|
|
|
|
|
has_optional = TRUE;
|
2020-06-23 08:39:04 +02:00
|
|
|
}
|
libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'
For simple matches like match.interface-name, match.driver, and
match.path, arguably what we had was fine. There each element
(like "eth*") is a wildcard for a single name (like "eth1").
However, for match.kernel-command-line, the elements match individual
command line options, so we should have more flexibility of whether
a parameter is optional or mandatory. Extend the syntax for that.
- the elements can now be prefixed by either '|' or '&'. This makes
optional or mandatory elements, respectively. The entire match
evaluates to true if all mandatory elements match (if any) and
at least one of the optional elements (if any).
As before, if neither '|' nor '&' is specified, then the element
is optional (that means, "foo" is the same as "|foo").
- the exclamation mark is still used to invert the match. If used
alone (like "!foo") it is a shortcut for defining a mandatory match
("&!foo").
- the backslash can now be used to escape the special characters
above. Basically, the special characters ('|', '&', '!') are
stripped from the start of the element. If what is left afterwards
is a backslash, it also gets stripped and the remainder is the
pattern. For example, "\\&foo" has the pattern "&foo" where
'&' is no longer treated specially. This special handling of
the backslash is only done at the beginning of the element (after
the optional special characters). The remaining string is part
of the pattern, where backslashes might have their own meaning.
This change is mostly backward compatible, except for existing matches
that started with one of the special characters '|', '&', '!', and '\\'.
2020-06-23 09:39:48 +02:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
libnm/match: extend syntax for match patterns with '|', '&', '!' and '\\'
For simple matches like match.interface-name, match.driver, and
match.path, arguably what we had was fine. There each element
(like "eth*") is a wildcard for a single name (like "eth1").
However, for match.kernel-command-line, the elements match individual
command line options, so we should have more flexibility of whether
a parameter is optional or mandatory. Extend the syntax for that.
- the elements can now be prefixed by either '|' or '&'. This makes
optional or mandatory elements, respectively. The entire match
evaluates to true if all mandatory elements match (if any) and
at least one of the optional elements (if any).
As before, if neither '|' nor '&' is specified, then the element
is optional (that means, "foo" is the same as "|foo").
- the exclamation mark is still used to invert the match. If used
alone (like "!foo") it is a shortcut for defining a mandatory match
("&!foo").
- the backslash can now be used to escape the special characters
above. Basically, the special characters ('|', '&', '!') are
stripped from the start of the element. If what is left afterwards
is a backslash, it also gets stripped and the remainder is the
pattern. For example, "\\&foo" has the pattern "&foo" where
'&' is no longer treated specially. This special handling of
the backslash is only done at the beginning of the element (after
the optional special characters). The remaining string is part
of the pattern, where backslashes might have their own meaning.
This change is mostly backward compatible, except for existing matches
that started with one of the special characters '|', '&', '!', and '\\'.
2020-06-23 09:39:48 +02:00
|
|
|
if (!has_optional && has_any_optional) {
|
|
|
|
|
nm_utils_error_set(error,
|
|
|
|
|
NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
|
|
|
|
|
"device does not satisfy any match.kernel-command-line property");
|
|
|
|
|
return FALSE;
|
2020-06-23 08:39:04 +02:00
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-06-23 08:39:04 +02:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2017-02-05 19:14:28 +01:00
|
|
|
if (a == b)
|
|
|
|
|
return 0;
|
|
|
|
|
if (!a)
|
|
|
|
|
return 1;
|
|
|
|
|
if (!b)
|
|
|
|
|
return -1;
|
2020-09-28 16:03:33 +02: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);
|
2020-09-28 16:03:33 +02: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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2017-02-05 19:14:28 +01:00
|
|
|
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;
|
2020-09-28 16:03:33 +02: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;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
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;
|
2021-11-09 13:28:54 +01:00
|
|
|
NMSetting *setting;
|
|
|
|
|
NMSetting *diff_base_setting;
|
2016-03-01 09:56:51 +01:00
|
|
|
GHashTable *setting_diff;
|
|
|
|
|
} LogConnectionSettingData;
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
2021-11-09 13:28:54 +01:00
|
|
|
const char *item_name;
|
2016-03-01 09:56:51 +01:00
|
|
|
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;
|
2021-11-09 13:28:54 +01:00
|
|
|
NMSetting *s1, *s2;
|
2016-03-01 09:56:51 +01:00
|
|
|
|
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,
|
2021-11-09 13:28:54 +01:00
|
|
|
GHashTable *connection_diff)
|
2016-03-01 09:56:51 +01:00
|
|
|
{
|
|
|
|
|
GHashTableIter iter;
|
2021-11-09 13:28:54 +01:00
|
|
|
GArray *sorted_hashes;
|
2016-03-01 09:56:51 +01:00
|
|
|
LogConnectionSettingData setting_data;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
sorted_hashes = g_array_sized_new(TRUE,
|
|
|
|
|
FALSE,
|
|
|
|
|
sizeof(LogConnectionSettingData),
|
|
|
|
|
g_hash_table_size(connection_diff));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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);
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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;
|
2021-11-09 13:28:54 +01:00
|
|
|
char *s;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
g_return_val_if_fail(setting, NULL);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
if (!NM_IS_SETTING_VPN(setting) && nm_setting_get_secret_flags(setting, name, NULL, NULL))
|
|
|
|
|
return g_strdup("****");
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
if (!_nm_setting_get_property(setting, name, &val))
|
2019-01-10 19:38:36 +01:00
|
|
|
return g_strdup("<unknown>");
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
if (G_VALUE_HOLDS_STRING(&val)) {
|
|
|
|
|
const char *val_s;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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, "'");
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
s = g_strdup_printf("'%s'", escaped);
|
|
|
|
|
g_free(escaped);
|
|
|
|
|
}
|
2021-12-20 07:36:44 +01:00
|
|
|
} else if (G_VALUE_HOLDS_VARIANT(&val)) {
|
|
|
|
|
s = g_variant_print(g_value_get_variant(&val), FALSE);
|
2016-03-01 09:56:51 +01:00
|
|
|
} else {
|
|
|
|
|
s = g_strdup_value_contents(&val);
|
|
|
|
|
if (s == NULL)
|
|
|
|
|
s = g_strdup("NULL");
|
|
|
|
|
else {
|
|
|
|
|
char *escaped = g_strescape(s, "'");
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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,
|
2021-11-09 13:28:54 +01:00
|
|
|
const char *name,
|
|
|
|
|
const char *prefix,
|
|
|
|
|
const char *dbus_path)
|
2016-03-01 09:56:51 +01:00
|
|
|
{
|
|
|
|
|
GHashTable *connection_diff = NULL;
|
2021-11-09 13:28:54 +01:00
|
|
|
GArray *sorted_hashes;
|
|
|
|
|
GArray *sorted_names = NULL;
|
2016-03-01 09:56:51 +01:00
|
|
|
int i, j;
|
|
|
|
|
gboolean connection_diff_are_same;
|
|
|
|
|
gboolean print_header = TRUE;
|
|
|
|
|
gboolean print_setting_header;
|
2021-11-09 13:28:54 +01:00
|
|
|
GString *str1;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
g_return_if_fail(NM_IS_CONNECTION(connection));
|
|
|
|
|
g_return_if_fail(!diff_base || (NM_IS_CONNECTION(diff_base) && diff_base != connection));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
/* For VPN setting types, this is broken, because we cannot (generically) print the content of data/secrets. Bummer... */
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
if (!nm_logging_enabled(level, domain))
|
|
|
|
|
return;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
if (!prefix)
|
|
|
|
|
prefix = "";
|
|
|
|
|
if (!name)
|
|
|
|
|
name = "";
|
2020-09-28 16:03:33 +02:00
|
|
|
|
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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2017-06-01 16:48:19 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
sorted_names = g_array_new(FALSE, FALSE, sizeof(LogConnectionSettingItem));
|
|
|
|
|
str1 = g_string_new(NULL);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
for (i = 0; i < sorted_hashes->len; i++) {
|
|
|
|
|
LogConnectionSettingData *setting_data =
|
2022-09-08 12:05:56 +02:00
|
|
|
&nm_g_array_index(sorted_hashes, LogConnectionSettingData, i);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
_log_connection_sort_names(setting_data, sorted_names);
|
|
|
|
|
print_setting_header = TRUE;
|
|
|
|
|
for (j = 0; j < sorted_names->len; j++) {
|
2021-11-09 13:28:54 +01:00
|
|
|
char *str_conn, *str_diff;
|
2016-03-01 09:56:51 +01:00
|
|
|
LogConnectionSettingItem *item =
|
2022-09-08 12:05:56 +02:00
|
|
|
&nm_g_array_index(sorted_names, LogConnectionSettingItem, j);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
if (print_header) {
|
2021-11-09 13:28:54 +01:00
|
|
|
GError *err_verify = NULL;
|
2017-06-01 16:48:19 +02:00
|
|
|
const char *t1, *t2;
|
2020-09-28 16:03:33 +02: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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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);
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
}
|
2016-03-01 09:56:51 +01:00
|
|
|
#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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
g_array_free(sorted_names, TRUE);
|
|
|
|
|
g_string_free(str1, TRUE);
|
|
|
|
|
out:
|
|
|
|
|
g_hash_table_destroy(connection_diff);
|
|
|
|
|
g_array_free(sorted_hashes, TRUE);
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
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);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2021-06-05 16:55:04 +02:00
|
|
|
uuid_data->bin = *uuid;
|
|
|
|
|
uuid_data->is_fake = is_fake;
|
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 (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);
|
2021-05-02 10:01:32 +02:00
|
|
|
nm_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;
|
2021-11-09 13:28:54 +01:00
|
|
|
gs_free char *content = 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
|
|
|
gboolean is_fake = TRUE;
|
2021-11-09 13:28:54 +01:00
|
|
|
const char *fake_type = 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;
|
2020-09-28 16:03:33 +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
|
|
|
/* 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",
|
2020-09-28 16:03:33 +02:00
|
|
|
100 * 1024,
|
|
|
|
|
0,
|
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
|
|
|
&content,
|
2020-09-28 16:03:33 +02:00
|
|
|
NULL,
|
|
|
|
|
NULL,
|
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
|
|
|
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,
|
2020-09-28 16:03:33 +02:00
|
|
|
FALSE,
|
|
|
|
|
FALSE,
|
2019-02-21 10:02:28 +01:00
|
|
|
NULL,
|
|
|
|
|
16,
|
|
|
|
|
(guint8 *) &uuid,
|
|
|
|
|
sizeof(uuid),
|
|
|
|
|
NULL)) {
|
2021-05-02 14:17:32 +02:00
|
|
|
if (!nm_uuid_is_null(&uuid)) {
|
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
|
|
|
/* an all-zero machine-id is not valid. */
|
|
|
|
|
is_fake = FALSE;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +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;
|
2021-06-05 14:56:12 +02:00
|
|
|
const NMUuid *hash_seed;
|
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
|
|
|
gsize seed_len;
|
2020-09-28 16:03:33 +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
|
|
|
if (!allow_fake) {
|
2020-07-04 11:37:01 +03:00
|
|
|
/* we don't allow generating (and memorizing) a fake key.
|
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
|
|
|
* 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)) {
|
2021-06-05 14:56:12 +02:00
|
|
|
static const NMUuid u =
|
|
|
|
|
NM_UUID_INIT(ab, 08, 5f, 06, b6, 29, 46, d1, a5, 53, 84, ee, ba, 56, 83, b6);
|
|
|
|
|
|
2020-09-02 13:19:11 +02:00
|
|
|
/* We have no valid machine-id but we have a valid secrey_key.
|
|
|
|
|
* Generate a fake machine ID by hashing the secret-key. The secret_key
|
|
|
|
|
* is commonly persisted, so it should be stable across reboots (despite
|
|
|
|
|
* having a broken system without proper machine-id).
|
|
|
|
|
*
|
|
|
|
|
* Note that we access the host-id here, which is based on secret_key.
|
|
|
|
|
* Also not that the secret_key may be generated based on the machine-id,
|
|
|
|
|
* so we have to be careful that they don't depend on each other (and
|
|
|
|
|
* no infinite recursion happens. This is done correctly, because the secret-key
|
|
|
|
|
* will call _machine_id_get(FALSE), so it won't allow accessing a fake
|
|
|
|
|
* machine-id, thus avoiding the problem. */
|
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
|
|
|
fake_type = "secret-key";
|
2021-06-05 14:56:12 +02:00
|
|
|
hash_seed = &u;
|
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 {
|
2021-06-05 14:56:12 +02:00
|
|
|
static const NMUuid u =
|
|
|
|
|
NM_UUID_INIT(7f, f0, c8, f5, 53, 99, 49, 01, ab, 63, 61, bf, 59, 4a, be, 8b);
|
|
|
|
|
|
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 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";
|
2021-06-05 14:56:12 +02:00
|
|
|
hash_seed = &u;
|
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
|
|
|
}
|
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. */
|
2021-05-02 21:32:31 +02:00
|
|
|
nm_uuid_generate_from_string(&uuid,
|
|
|
|
|
(const char *) seed_bin,
|
|
|
|
|
seed_len,
|
|
|
|
|
NM_UUID_TYPE_VERSION5,
|
2021-06-05 14:56:12 +02:00
|
|
|
hash_seed);
|
2016-04-25 21:18:06 +02:00
|
|
|
}
|
2020-09-28 16:03:33 +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;
|
2020-09-28 16:03:33 +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
|
|
|
d = _uuid_data_init(&uuid_data, TRUE, is_fake, &uuid);
|
|
|
|
|
g_atomic_pointer_set(&p_uuid_data, d);
|
|
|
|
|
g_once_init_leave(&lock, 1);
|
2020-09-28 16:03:33 +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) {
|
|
|
|
|
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
|
|
|
}
|
2020-09-28 16:03:33 +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,
|
2021-11-09 13:28:54 +01:00
|
|
|
gint64 *out_timestamp_ns)
|
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
|
|
|
{
|
|
|
|
|
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.
|
|
|
|
|
*
|
2022-03-11 09:31:16 +01:00
|
|
|
* Pick a timestamp from the past two years, by using a generated timespan
|
|
|
|
|
* by hashing the host-id. Yes, this timestamp counts back from @now, and is
|
|
|
|
|
* thus 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,
|
2021-05-03 21:48:58 +02: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,
|
2021-11-09 13:28:54 +01:00
|
|
|
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);
|
2021-11-09 13:28:54 +01:00
|
|
|
const UuidData *machine_id_data;
|
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
|
|
|
char slen[100];
|
2016-04-25 18:14:25 +02:00
|
|
|
|
2022-10-25 10:17:20 +02:00
|
|
|
/* The following snippet generates the same (binary) host-id:
|
|
|
|
|
|
|
|
|
|
(
|
|
|
|
|
stat -c '%s' /var/lib/NetworkManager/secret_key | tr -d '\n';
|
|
|
|
|
echo -n ' ';
|
|
|
|
|
cat /var/lib/NetworkManager/secret_key;
|
|
|
|
|
cat /etc/machine-id | tr -d '\n' | sed -n 's/[a-f0-9-]/\0/pg'
|
|
|
|
|
) \
|
|
|
|
|
| sha256sum \
|
|
|
|
|
| awk '{print $1}' \
|
|
|
|
|
| xxd -r -p
|
2020-09-29 09:02:29 +02:00
|
|
|
*/
|
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};
|
2021-11-09 13:28:54 +01:00
|
|
|
const guint8 *secret_arr;
|
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
|
|
|
gsize secret_len;
|
2021-11-09 13:28:54 +01:00
|
|
|
GError *error = NULL;
|
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
|
|
|
gboolean success;
|
2020-09-28 16:03:33 +02: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
|
|
|
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;
|
2020-09-02 13:19:11 +02:00
|
|
|
nm_log_dbg(LOGD_CORE,
|
|
|
|
|
"secret-key: v2 secret key loaded from \"%s\" (%zu bytes)",
|
|
|
|
|
SECRET_KEY_FILE,
|
|
|
|
|
file_content.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
|
|
|
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;
|
2020-09-02 13:19:11 +02:00
|
|
|
nm_log_dbg(LOGD_CORE,
|
|
|
|
|
"secret-key: v1 secret key loaded from \"%s\" (%zu bytes)",
|
|
|
|
|
SECRET_KEY_FILE,
|
|
|
|
|
file_content.len);
|
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;
|
|
|
|
|
|
core: block to get good random bytes for "/var/lib/NetworkManager/secret_key"
_host_id_read() is the only place where we really care to have good
random numbers, because that is the secret key that we persist to disk.
Previously, we tried only nm_random_get_bytes_full(), which is a best
effort to get strong random numbers. If it fails to generate those,
it would simply remember the generated key in memory and proceed, but not
persist it to disk.
nm_random_get_bytes_full() does not block waiting for good numbers.
Change that. Now, first call nm_random_get_crypto_bytes(), which would
block and try hard to get good random numbers. Only if that fails,
fallback to nm_random_get_bytes_full() as before. The difference is of
course only in early boot, when we might not yet have entropy. In that
case, I think it's better for NetworkManager to block.
2022-08-03 12:24:49 +02:00
|
|
|
if (nm_random_get_crypto_bytes(rnd_buf, sizeof(rnd_buf)) < 0)
|
|
|
|
|
nm_random_get_bytes_full(rnd_buf, sizeof(rnd_buf), &success);
|
|
|
|
|
else
|
|
|
|
|
success = TRUE;
|
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
|
|
|
|
|
|
|
|
/* 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));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
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;
|
2020-09-28 16:03:33 +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
|
|
|
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,
|
2021-04-14 15:09:39 +02:00
|
|
|
NULL,
|
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
|
|
|
&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
|
2020-09-02 13:19:11 +02:00
|
|
|
nm_log_dbg(LOGD_CORE,
|
|
|
|
|
"secret-key: persist new v2 secret key to \"%s\" (%zu bytes)",
|
|
|
|
|
SECRET_KEY_FILE,
|
|
|
|
|
len);
|
2020-09-28 16:03:33 +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_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;
|
2022-03-11 09:31:16 +01:00
|
|
|
|
2022-03-11 09:39:07 +01:00
|
|
|
/* The timestamp (in nsec since the Epoch) returned by nm_utils_host_id_get_timestamp_nsec().
|
2022-03-11 09:31:16 +01:00
|
|
|
* It is associated with the host (and the host-id). We currently use this for the LLT DUID
|
|
|
|
|
* generation for IPv6. Instead of persisting the timestamp separately to disk, we re-use the
|
|
|
|
|
* file timestamp of the secret_key file. */
|
2022-03-11 09:39:07 +01:00
|
|
|
gint64 timestamp_nsec;
|
2022-03-11 09:31:16 +01:00
|
|
|
|
2022-03-11 09:39:07 +01:00
|
|
|
bool is_good : 1;
|
|
|
|
|
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
|
|
|
|
2021-09-07 09:34:43 +02:00
|
|
|
static const HostIdData *volatile host_id_static;
|
|
|
|
|
|
2018-12-12 11:03:02 +01:00
|
|
|
static const HostIdData *
|
|
|
|
|
_host_id_get(void)
|
|
|
|
|
{
|
|
|
|
|
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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-12 11:03:02 +01:00
|
|
|
if (!g_once_init_enter(&init_value))
|
|
|
|
|
goto again;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-12 11:03:02 +01:00
|
|
|
host_id_data.is_good = _host_id_read(&host_id_data.host_id, &host_id_data.host_id_len);
|
2020-09-28 16:03:33 +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
|
|
|
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,
|
2022-03-11 09:39:07 +01:00
|
|
|
&host_id_data.timestamp_nsec);
|
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
|
|
|
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)");
|
2020-09-28 16:03:33 +02:00
|
|
|
|
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);
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-12 11:03:02 +01:00
|
|
|
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
|
2022-03-11 09:39:07 +01:00
|
|
|
nm_utils_host_id_get_timestamp_nsec(void)
|
2018-05-25 10:33:02 +02:00
|
|
|
{
|
2022-03-11 09:39:07 +01:00
|
|
|
return _host_id_get()->timestamp_nsec;
|
2018-05-25 10:33:02 +02:00
|
|
|
}
|
|
|
|
|
|
2021-11-09 13:28:54 +01:00
|
|
|
static GArray *nmtst_host_id_stack = NULL;
|
2021-09-07 09:34:43 +02:00
|
|
|
static GMutex nmtst_host_id_lock;
|
|
|
|
|
const HostIdData *nmtst_host_id_static_0 = NULL;
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
nmtst_utils_host_id_push(const guint8 *host_id,
|
|
|
|
|
gssize host_id_len,
|
|
|
|
|
gboolean is_good,
|
2022-03-11 09:39:07 +01:00
|
|
|
const gint64 *p_timestamp_nsec)
|
2021-09-07 09:34:43 +02:00
|
|
|
{
|
|
|
|
|
NM_G_MUTEX_LOCKED(&nmtst_host_id_lock);
|
|
|
|
|
gs_free char *str1_to_free = NULL;
|
2021-11-09 13:28:54 +01:00
|
|
|
HostIdData *h;
|
2021-09-07 09:34:43 +02:00
|
|
|
|
|
|
|
|
g_assert(host_id_len >= -1);
|
|
|
|
|
|
|
|
|
|
if (host_id_len < 0)
|
|
|
|
|
host_id_len = host_id ? strlen((const char *) host_id) : 0;
|
|
|
|
|
|
|
|
|
|
nm_log_dbg(LOGD_CORE,
|
|
|
|
|
"nmtst: host-id push: \"%s\" (%zu), is-good=%d, timestamp=%" G_GINT64_FORMAT "%s",
|
|
|
|
|
nm_utils_buf_utf8safe_escape(host_id,
|
|
|
|
|
host_id_len,
|
|
|
|
|
NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL,
|
|
|
|
|
&str1_to_free),
|
|
|
|
|
(gsize) host_id_len,
|
|
|
|
|
!!is_good,
|
2022-03-11 09:39:07 +01:00
|
|
|
p_timestamp_nsec ? *p_timestamp_nsec : 0,
|
|
|
|
|
p_timestamp_nsec ? "" : " (not-good)");
|
2021-09-07 09:34:43 +02:00
|
|
|
|
|
|
|
|
if (!nmtst_host_id_stack) {
|
|
|
|
|
nmtst_host_id_stack = g_array_new(FALSE, FALSE, sizeof(HostIdData));
|
|
|
|
|
nmtst_host_id_static_0 = g_atomic_pointer_get(&host_id_static);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
h = nm_g_array_append_new(nmtst_host_id_stack, HostIdData);
|
|
|
|
|
|
|
|
|
|
*h = (HostIdData){
|
|
|
|
|
.host_id = nm_memdup(host_id, host_id_len),
|
|
|
|
|
.host_id_len = host_id_len,
|
2022-03-11 09:39:07 +01:00
|
|
|
.timestamp_nsec = p_timestamp_nsec ? *p_timestamp_nsec : 0,
|
2021-09-07 09:34:43 +02:00
|
|
|
.is_good = is_good,
|
2022-03-11 09:39:07 +01:00
|
|
|
.timestamp_is_good = !!p_timestamp_nsec,
|
2021-09-07 09:34:43 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
g_atomic_pointer_set(&host_id_static, h);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
nmtst_utils_host_id_pop(void)
|
|
|
|
|
{
|
|
|
|
|
NM_G_MUTEX_LOCKED(&nmtst_host_id_lock);
|
|
|
|
|
HostIdData *h;
|
|
|
|
|
|
|
|
|
|
g_assert(nmtst_host_id_stack);
|
|
|
|
|
g_assert(nmtst_host_id_stack->len > 0);
|
|
|
|
|
|
|
|
|
|
nm_log_dbg(LOGD_CORE, "nmtst: host-id pop");
|
|
|
|
|
|
2022-09-08 12:05:56 +02:00
|
|
|
h = &nm_g_array_index(nmtst_host_id_stack, HostIdData, nmtst_host_id_stack->len - 1);
|
2021-09-07 09:34:43 +02:00
|
|
|
|
|
|
|
|
g_free((char *) h->host_id);
|
|
|
|
|
g_array_set_size(nmtst_host_id_stack, nmtst_host_id_stack->len - 1u);
|
|
|
|
|
|
|
|
|
|
if (!g_atomic_pointer_compare_and_exchange(
|
|
|
|
|
&host_id_static,
|
|
|
|
|
h,
|
|
|
|
|
nmtst_host_id_stack->len == 0u ? nmtst_host_id_static_0
|
glib-aux: add nm_g_array_index() macro and improve nm_g_array_index_p() macros
Add nm_g_array_index() as a replacement for g_array_index(). The value
of nm_g_array_index(), nm_g_array_index_p(), nm_g_array_first() and
nm_g_array_last() is that they add nm_assert() checks for valid
parameters.
nm_g_array_{first,last}() now returns an lvalue and not a pointer.
As such, they are just shorthands for nm_g_array_index() at index
0 and len-1, respectively.
`nm_g_array_index_p(arr, Type, idx)` is almost the same as
`&nm_g_array_index(arr, Type, idx)`. The only difference (and why the
former variant exists), is that nm_g_array_index_p() allows to get a
pointer one after the end.
This means, this is correct and valid to do:
// arr->len might be zero
arr = nm_g_array_index_p(arr, Type, 0);
for (i = 0; i < arr->len; i++, arr++)
...
ptr = nm_g_array_index_p(arr, Type, 0);
end = nm_g_array_index_p(arr, Type, arr->len);
for (; ptr < end; ptr++)
...
This would not be valid to do with nm_g_array_{index,first,last}().
Also fix supporting "const GArray *arr" parameter. Of course, the function
casts the constness away. Technically, that matches the fact that arr->data
is also not a const pointer. In practice, we might want to propagate the
constness of the container to the constness of the element lookup. While
doable, that is not implemented.
2022-09-08 11:52:49 +02:00
|
|
|
: &nm_g_array_last(nmtst_host_id_stack, HostIdData)))
|
2021-09-07 09:34:43 +02:00
|
|
|
g_assert_not_reached();
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
2021-11-09 13:28:54 +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;
|
2020-09-28 16:03:33 +02: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);
|
2021-05-02 13:45:53 +02:00
|
|
|
if (!contents || !nm_uuid_parse(nm_strstrip(contents), &uuid)) {
|
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
|
|
|
/* generate a random UUID instead. */
|
|
|
|
|
is_fake = TRUE;
|
2021-05-02 13:45:53 +02:00
|
|
|
nm_uuid_generate_random(&uuid);
|
2016-12-18 17:58:30 +01:00
|
|
|
}
|
2020-09-28 16:03:33 +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;
|
2020-09-28 16:03:33 +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
|
|
|
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
|
|
|
}
|
2020-09-28 16:03:33 +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 *
|
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;
|
|
|
|
|
|
2022-07-13 17:39:36 +02:00
|
|
|
/* /run/NetworkManager/proc-cmdline can be used to overrule /proc/cmdline. */
|
|
|
|
|
if (!g_file_get_contents(NMRUNDIR "/proc-cmdline", &str, NULL, NULL))
|
|
|
|
|
g_file_get_contents("/proc/cmdline", &str, NULL, NULL);
|
|
|
|
|
|
2020-03-15 11:25:33 +01:00
|
|
|
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-06-23 00:12:45 +02:00
|
|
|
gs_strfreev char **split = NULL;
|
|
|
|
|
|
|
|
|
|
split = nm_utils_strsplit_quoted(nm_utils_proc_cmdline());
|
|
|
|
|
if (!g_atomic_pointer_compare_and_exchange(&proc_cmdline_cached, NULL, (gpointer) split))
|
2020-03-15 11:25:33 +01:00
|
|
|
goto again;
|
2020-03-24 20:43:29 +01:00
|
|
|
|
2020-06-23 00:12:45 +02:00
|
|
|
proc_cmdline = (const char *const *) 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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-20 08:38:05 +01:00
|
|
|
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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-20 08:38:05 +01:00
|
|
|
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);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-20 08:38:05 +01:00
|
|
|
/* for infiniband, we only consider the last 8 bytes. */
|
|
|
|
|
if (arp_type == ARPHRD_INFINIBAND) {
|
|
|
|
|
*hwaddr += (INFINIBAND_ALEN - 8);
|
|
|
|
|
*hwaddr_len = 8;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-12-20 08:38:05 +01:00
|
|
|
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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
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,
|
2021-11-09 13:28:54 +01:00
|
|
|
const guint8 *hwaddr,
|
2016-03-01 09:56:51 +01:00
|
|
|
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.
|
|
|
|
|
*/
|
2022-12-03 19:57:35 +01:00
|
|
|
if (hwaddr_len == INFINIBAND_ALEN) {
|
|
|
|
|
memcpy(out_iid->id_u8, hwaddr + INFINIBAND_ALEN - 8, 8);
|
|
|
|
|
out_iid->id_u8[0] |= 0x02;
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2016-03-01 09:56:51 +01:00
|
|
|
case NM_LINK_TYPE_GRE:
|
|
|
|
|
/* Hardware address is the network-endian IPv4 address */
|
2022-12-03 19:57:35 +01:00
|
|
|
if (hwaddr_len == 4) {
|
|
|
|
|
addr = unaligned_read_ne32(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;
|
|
|
|
|
}
|
|
|
|
|
break;
|
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
|
2020-09-29 09:02:29 +02:00
|
|
|
* IEEE 802.15.4 networks. */
|
2022-12-03 19:57:35 +01:00
|
|
|
if (hwaddr_len == sizeof(out_iid->id_u8)) {
|
|
|
|
|
memcpy(out_iid->id_u8, hwaddr, sizeof(out_iid->id_u8));
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
break;
|
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
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2016-12-18 13:54:26 +01:00
|
|
|
char *
|
|
|
|
|
nm_utils_stable_id_random(void)
|
|
|
|
|
{
|
|
|
|
|
char buf[15];
|
|
|
|
|
|
glib-aux: rework random number utils
Heavily inspired by systemd ([1]).
We now also have nm_random_get_bytes{,_full}() and
nm_random_get_crypto_bytes(), like systemd's random_bytes()
and crypto_random_bytes(), respectively.
Differences:
- instead of systemd's random_bytes(), our nm_random_get_bytes_full()
also estimates whether the output is of high quality. The caller
may find that interesting. Due to that, we will first try to call
getrandom(GRND_NONBLOCK) before getrandom(GRND_INSECURE). That is
reversed from systemd's random_bytes(), because we want to find
out whether we can get good random numbers. In most cases, kernel
should have entropy already, and it makes no difference.
Otherwise, heavily rework the code. It should be easy to understand
and correct.
There is also a major bugfix here. Previously, if getrandom() failed
with ENOSYS and we fell back to /dev/urandom, we would assume that we
have high quality random numbers. That assumption is not warranted.
Now instead poll on /dev/random to find out.
[1] https://github.com/systemd/systemd/blob/a268e7f4021072e120a03b42660fad21e465c44e/src/basic/random-util.c#L81
2022-08-02 20:11:23 +02:00
|
|
|
nm_random_get_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];
|
2021-11-09 13:28:54 +01:00
|
|
|
char *base64;
|
2016-12-18 13:54:26 +01:00
|
|
|
|
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)
|
|
|
|
|
{
|
2022-03-11 10:21:44 +01:00
|
|
|
if (!substitution) {
|
|
|
|
|
/* Would have been nicer to append "=NIL;" to differentiate between
|
|
|
|
|
* empty and NULL.
|
|
|
|
|
*
|
|
|
|
|
* Can't do that now, as it would change behavior. */
|
2016-12-18 13:54:26 +01:00
|
|
|
substitution = "";
|
2022-03-11 10:21:44 +01:00
|
|
|
}
|
2016-12-18 13:54:26 +01:00
|
|
|
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,
|
2021-11-09 13:28:54 +01:00
|
|
|
char **out_generated)
|
2016-12-18 13:54:26 +01:00
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-12-18 13:54:26 +01:00
|
|
|
#define CHECK_PREFIX(prefix) \
|
|
|
|
|
({ \
|
|
|
|
|
gboolean _match = FALSE; \
|
|
|
|
|
\
|
2022-03-11 10:09:16 +01:00
|
|
|
if (NM_STR_HAS_PREFIX(&stable_id[i], "" prefix "")) { \
|
2016-12-18 13:54:26 +01:00
|
|
|
_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
|
2020-07-04 11:37:01 +03:00
|
|
|
* as the result is similar to specifying "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
|
2020-07-04 11:37:01 +03:00
|
|
|
* by toggling only the stable-id property of the connection.
|
2016-12-18 13:54:26 +01:00
|
|
|
* 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;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-25 15:13:36 +02:00
|
|
|
void
|
|
|
|
|
nm_utils_ipv6_addr_set_stable_privacy_with_host_id(NMUtilsStableType stable_type,
|
2021-11-09 13:28:54 +01:00
|
|
|
struct in6_addr *addr,
|
|
|
|
|
const char *ifname,
|
|
|
|
|
const char *network_id,
|
2021-08-25 15:13:36 +02:00
|
|
|
guint32 dad_counter,
|
2021-11-09 13:28:54 +01:00
|
|
|
const guint8 *host_id,
|
2021-08-25 15:13:36 +02:00
|
|
|
gsize host_id_len)
|
2016-03-01 09:56:51 +01:00
|
|
|
{
|
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
|
2020-07-04 11:37:01 +03:00
|
|
|
* and the terminating '\0' of @network_id, it is unambiguously
|
2016-06-21 18:07:56 +02:00
|
|
|
* 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
|
|
|
}
|
2020-09-28 16:03:33 +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);
|
2020-09-28 16:03:33 +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
|
|
|
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
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
memcpy(addr->s6_addr + 8, &digest[0], 8);
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-25 15:13:36 +02:00
|
|
|
void
|
|
|
|
|
nm_utils_ipv6_addr_set_stable_privacy(NMUtilsStableType stable_type,
|
2021-11-09 13:28:54 +01:00
|
|
|
struct in6_addr *addr,
|
|
|
|
|
const char *ifname,
|
|
|
|
|
const char *network_id,
|
2021-08-25 15:13:36 +02:00
|
|
|
guint32 dad_counter)
|
|
|
|
|
{
|
|
|
|
|
const guint8 *host_id;
|
|
|
|
|
gsize host_id_len;
|
|
|
|
|
|
|
|
|
|
nm_utils_host_id_get(&host_id, &host_id_len);
|
|
|
|
|
|
|
|
|
|
nm_utils_ipv6_addr_set_stable_privacy_with_host_id(stable_type,
|
|
|
|
|
addr,
|
|
|
|
|
ifname,
|
|
|
|
|
network_id,
|
|
|
|
|
dad_counter,
|
|
|
|
|
host_id,
|
|
|
|
|
host_id_len);
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-01 09:56:51 +01:00
|
|
|
/**
|
|
|
|
|
* 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
|
2021-08-25 15:13:36 +02:00
|
|
|
nm_utils_ipv6_addr_set_stable_privacy_may_fail(NMUtilsStableType stable_type,
|
2021-11-09 13:28:54 +01:00
|
|
|
struct in6_addr *addr,
|
|
|
|
|
const char *ifname,
|
|
|
|
|
const char *network_id,
|
2021-08-25 15:13:36 +02:00
|
|
|
guint32 dad_counter,
|
2021-11-09 13:28:54 +01:00
|
|
|
GError **error)
|
2016-03-01 09:56:51 +01:00
|
|
|
{
|
2016-12-18 14:03:38 +01:00
|
|
|
g_return_val_if_fail(network_id, FALSE);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2021-08-25 15:13:36 +02:00
|
|
|
if (dad_counter >= NM_STABLE_PRIVACY_RFC7217_IDGEN_RETRIES) {
|
2016-03-01 09:56:51 +01:00
|
|
|
g_set_error_literal(error,
|
|
|
|
|
NM_UTILS_ERROR,
|
|
|
|
|
NM_UTILS_ERROR_UNKNOWN,
|
|
|
|
|
"Too many DAD collisions");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2021-08-25 15:13:36 +02:00
|
|
|
nm_utils_ipv6_addr_set_stable_privacy(stable_type, addr, ifname, network_id, dad_counter);
|
|
|
|
|
return TRUE;
|
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,
|
2021-11-09 13:28:54 +01:00
|
|
|
const char *current_mac_address,
|
|
|
|
|
const char *generate_mac_address_mask)
|
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 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;
|
2020-09-28 16:03:33 +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
|
|
|
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;
|
2020-09-28 16:03:33 +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
|
|
|
nm_assert((ouis == NULL) ^ (ouis_len != 0));
|
|
|
|
|
if (ouis) {
|
all: use nm_random_*() instead of g_random_*()
g_random_*() is based on GRand, which is not a CSPRNG. Instead, rely on
kernel to give us good random numbers, which is what nm_random_*() does.
Note that nm_random_*() calls getrandom() (or reads /dev/urandom), which
most likely is slower than GRand. It doesn't matter for our uses though.
It is cumbersome to review all uses of g_rand_*() whether their usage of
a non-cryptographically secure generator is appropriate. Instead, just
always use an appropriate function, thereby avoiding this question. Even
glib documentation refers to reading "/dev/urandom" as alternative. Which
is what nm_random_*() does. These days, it seems unnecessary to not use
the best random generator available, unless it's not fast enough or you
need a stable/seedable stream of random numbers.
In particular in nmcli, we used g_random_int_range() to generate
passwords. That is not appropriate. Sure, it's *only* for the hotspot,
but still.
2023-01-27 12:52:20 +01:00
|
|
|
oui = ouis[nm_random_u64_range(ouis_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
|
|
|
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
|
|
|
|
glib-aux: rework random number utils
Heavily inspired by systemd ([1]).
We now also have nm_random_get_bytes{,_full}() and
nm_random_get_crypto_bytes(), like systemd's random_bytes()
and crypto_random_bytes(), respectively.
Differences:
- instead of systemd's random_bytes(), our nm_random_get_bytes_full()
also estimates whether the output is of high quality. The caller
may find that interesting. Due to that, we will first try to call
getrandom(GRND_NONBLOCK) before getrandom(GRND_INSECURE). That is
reversed from systemd's random_bytes(), because we want to find
out whether we can get good random numbers. In most cases, kernel
should have entropy already, and it makes no difference.
Otherwise, heavily rework the code. It should be easy to understand
and correct.
There is also a major bugfix here. Previously, if getrandom() failed
with ENOSYS and we fell back to /dev/urandom, we would assume that we
have high quality random numbers. That assumption is not warranted.
Now instead poll on /dev/random to find out.
[1] https://github.com/systemd/systemd/blob/a268e7f4021072e120a03b42660fad21e465c44e/src/basic/random-util.c#L81
2022-08-02 20:11:23 +02:00
|
|
|
nm_random_get_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,
|
2021-11-09 13:28:54 +01:00
|
|
|
const char *stable_id,
|
|
|
|
|
const guint8 *host_id,
|
2018-12-12 10:10:38 +01:00
|
|
|
gsize host_id_len,
|
2021-11-09 13:28:54 +01: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,
|
2021-11-09 13:28:54 +01:00
|
|
|
const char *stable_id,
|
|
|
|
|
const guint8 *host_id,
|
2018-12-12 10:10:38 +01:00
|
|
|
gsize host_id_len,
|
2021-11-09 13:28:54 +01:00
|
|
|
const char *ifname,
|
|
|
|
|
const char *current_mac_address,
|
|
|
|
|
const char *generate_mac_address_mask)
|
2016-10-18 11:54:58 +02:00
|
|
|
{
|
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,
|
2021-11-09 13:28:54 +01:00
|
|
|
const char *stable_id,
|
|
|
|
|
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)
|
|
|
|
|
{
|
2021-11-09 13:28:54 +01:00
|
|
|
guint8 *client_id_buf;
|
2018-12-20 08:38:05 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-04 17:32:47 +01:00
|
|
|
GBytes *
|
|
|
|
|
nm_utils_dhcp_client_id_duid(guint32 iaid, const guint8 *duid, gsize duid_len)
|
|
|
|
|
{
|
|
|
|
|
struct _nm_packed {
|
|
|
|
|
guint8 type;
|
|
|
|
|
guint32 iaid;
|
|
|
|
|
guint8 duid[];
|
|
|
|
|
} *client_id;
|
|
|
|
|
gsize total_size;
|
|
|
|
|
|
|
|
|
|
/* the @duid must include the 16 bit duid-type and the data (of max 128 bytes). */
|
|
|
|
|
g_return_val_if_fail(duid_len > 2 && duid_len < 128 + 2, NULL);
|
|
|
|
|
g_return_val_if_fail(duid, NULL);
|
|
|
|
|
|
|
|
|
|
total_size = sizeof(*client_id) + duid_len;
|
|
|
|
|
|
|
|
|
|
client_id = g_malloc(total_size);
|
|
|
|
|
|
|
|
|
|
client_id->type = 255;
|
|
|
|
|
unaligned_write_be32(&client_id->iaid, iaid);
|
|
|
|
|
memcpy(client_id->duid, duid, duid_len);
|
|
|
|
|
return g_bytes_new_take(client_id, total_size);
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-10-30 08:37:48 +01:00
|
|
|
g_return_val_if_fail(machine_id, NULL);
|
|
|
|
|
g_return_val_if_fail(machine_id_len > 0, NULL);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-10-30 08:37:48 +01:00
|
|
|
client_id = g_malloc(sizeof(*client_id));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-10-30 08:37:48 +01:00
|
|
|
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);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-10-30 08:37:48 +01:00
|
|
|
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));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2018-10-30 08:37:48 +01:00
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
2020-09-10 12:46:53 +02:00
|
|
|
GBytes *
|
|
|
|
|
nm_utils_generate_duid_llt(int arp_type, const guint8 *hwaddr, gsize hwaddr_len, gint64 time)
|
|
|
|
|
{
|
2021-11-09 13:28:54 +01:00
|
|
|
guint8 *arr;
|
2020-09-10 12:46:53 +02:00
|
|
|
const guint16 duid_type = htons(1);
|
|
|
|
|
const guint16 hw_type = htons(arp_type);
|
|
|
|
|
const guint32 duid_time = htonl(NM_MAX(0, time - NM_UTILS_EPOCH_DATETIME_200001010000));
|
|
|
|
|
|
|
|
|
|
if (!nm_utils_arp_type_get_hwaddr_relevant_part(arp_type, &hwaddr, &hwaddr_len))
|
|
|
|
|
nm_assert_not_reached();
|
|
|
|
|
|
|
|
|
|
arr = g_new(guint8, (2u + 2u + 4u) + hwaddr_len);
|
|
|
|
|
|
|
|
|
|
memcpy(&arr[0], &duid_type, 2);
|
|
|
|
|
memcpy(&arr[2], &hw_type, 2);
|
|
|
|
|
memcpy(&arr[4], &duid_time, 4);
|
|
|
|
|
memcpy(&arr[8], hwaddr, hwaddr_len);
|
|
|
|
|
|
|
|
|
|
return g_bytes_new_take(arr, (2u + 2u + 4u) + hwaddr_len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GBytes *
|
|
|
|
|
nm_utils_generate_duid_ll(int arp_type, const guint8 *hwaddr, gsize hwaddr_len)
|
|
|
|
|
{
|
2021-11-09 13:28:54 +01:00
|
|
|
guint8 *arr;
|
2020-09-10 12:46:53 +02:00
|
|
|
const guint16 duid_type = htons(3);
|
|
|
|
|
const guint16 hw_type = htons(arp_type);
|
|
|
|
|
|
|
|
|
|
if (!nm_utils_arp_type_get_hwaddr_relevant_part(arp_type, &hwaddr, &hwaddr_len))
|
|
|
|
|
nm_assert_not_reached();
|
|
|
|
|
|
|
|
|
|
arr = g_new(guint8, (2u + 2u) + hwaddr_len);
|
|
|
|
|
|
|
|
|
|
memcpy(&arr[0], &duid_type, 2);
|
|
|
|
|
memcpy(&arr[2], &hw_type, 2);
|
|
|
|
|
memcpy(&arr[4], hwaddr, hwaddr_len);
|
|
|
|
|
|
|
|
|
|
return g_bytes_new_take(arr, (2u + 2u) + hwaddr_len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GBytes *
|
|
|
|
|
nm_utils_generate_duid_uuid(const NMUuid *uuid)
|
|
|
|
|
{
|
|
|
|
|
const guint16 duid_type = htons(4);
|
2021-11-09 13:28:54 +01:00
|
|
|
guint8 *duid_buffer;
|
2020-09-10 12:46:53 +02:00
|
|
|
|
|
|
|
|
nm_assert(uuid);
|
|
|
|
|
|
|
|
|
|
/* Generate a DHCP Unique Identifier for DHCPv6 using the
|
|
|
|
|
* DUID-UUID method (see RFC 6355 section 4). Format is:
|
|
|
|
|
*
|
|
|
|
|
* u16: type (DUID-UUID = 4)
|
|
|
|
|
* u8[16]: UUID bytes
|
|
|
|
|
*/
|
|
|
|
|
G_STATIC_ASSERT_EXPR(sizeof(duid_type) == 2);
|
|
|
|
|
G_STATIC_ASSERT_EXPR(sizeof(*uuid) == 16);
|
|
|
|
|
duid_buffer = g_malloc(18);
|
|
|
|
|
memcpy(&duid_buffer[0], &duid_type, 2);
|
|
|
|
|
memcpy(&duid_buffer[2], uuid, 16);
|
|
|
|
|
return g_bytes_new_take(duid_buffer, 18);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GBytes *
|
|
|
|
|
nm_utils_generate_duid_from_machine_id(void)
|
|
|
|
|
{
|
|
|
|
|
static GBytes *volatile global_duid = NULL;
|
|
|
|
|
GBytes *p;
|
|
|
|
|
|
|
|
|
|
again:
|
|
|
|
|
p = g_atomic_pointer_get(&global_duid);
|
|
|
|
|
if (G_UNLIKELY(!p)) {
|
|
|
|
|
nm_auto_free_checksum GChecksum *sum = NULL;
|
2021-11-09 13:28:54 +01:00
|
|
|
const NMUuid *machine_id;
|
2020-09-10 12:46:53 +02:00
|
|
|
union {
|
|
|
|
|
guint8 sha256[NM_UTILS_CHECKSUM_LENGTH_SHA256];
|
|
|
|
|
NMUuid uuid;
|
|
|
|
|
} digest;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-09-10 12:46:53 +02:00
|
|
|
machine_id = nm_utils_machine_id_bin();
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-09-10 12:46:53 +02:00
|
|
|
/* Hash the machine ID so it's not leaked to the network.
|
|
|
|
|
*
|
|
|
|
|
* Optimally, we would choose an use case specific seed, but for historic
|
|
|
|
|
* reasons we didn't. */
|
|
|
|
|
sum = g_checksum_new(G_CHECKSUM_SHA256);
|
|
|
|
|
g_checksum_update(sum, (const guchar *) machine_id, sizeof(*machine_id));
|
|
|
|
|
nm_utils_checksum_get_digest(sum, digest.sha256);
|
|
|
|
|
|
|
|
|
|
G_STATIC_ASSERT_EXPR(sizeof(digest.sha256) > sizeof(digest.uuid));
|
|
|
|
|
p = nm_utils_generate_duid_uuid(&digest.uuid);
|
|
|
|
|
|
|
|
|
|
if (!g_atomic_pointer_compare_and_exchange(&global_duid, NULL, p)) {
|
|
|
|
|
g_bytes_unref(p);
|
|
|
|
|
goto again;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return g_bytes_ref(p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
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-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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-06-02 19:17:35 +02:00
|
|
|
nm_utils_to_string_buffer_init(&dest, &size);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
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";
|
2020-09-28 16:03:33 +02:00
|
|
|
|
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
|
|
|
|
|
|
|
|
/**
|
2020-11-20 20:08:37 +01:00
|
|
|
* nm_utils_get_reverse_dns_domains_ip_4:
|
2016-06-03 22:07:38 +02:00
|
|
|
* @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
|
2020-11-20 20:08:37 +01:00
|
|
|
nm_utils_get_reverse_dns_domains_ip_4(guint32 addr, guint8 plen, GPtrArray *domains)
|
2016-06-03 22:07:38 +02:00
|
|
|
{
|
|
|
|
|
guint32 ip, ip2, mask;
|
|
|
|
|
guchar *p;
|
|
|
|
|
guint octets;
|
|
|
|
|
guint i;
|
|
|
|
|
gsize len0, len;
|
2021-11-09 13:28:54 +01:00
|
|
|
char *str, *s;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-06-03 22:07:38 +02:00
|
|
|
g_return_if_fail(domains);
|
|
|
|
|
g_return_if_fail(plen <= 32);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-06-03 22:07:38 +02:00
|
|
|
if (!plen)
|
|
|
|
|
return;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-06-03 22:07:38 +02:00
|
|
|
octets = (plen - 1) / 8 + 1;
|
|
|
|
|
ip = ntohl(addr);
|
|
|
|
|
mask = 0xFFFFFFFF << (32 - plen);
|
|
|
|
|
ip &= mask;
|
|
|
|
|
ip2 = ip;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-06-03 22:07:38 +02:00
|
|
|
len0 = NM_STRLEN("in-addr.arpa") + (4 * octets) + 1;
|
|
|
|
|
while ((ip2 & mask) == ip) {
|
|
|
|
|
addr = htonl(ip2);
|
|
|
|
|
p = (guchar *) &addr;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-06-03 22:07:38 +02:00
|
|
|
len = len0;
|
|
|
|
|
str = s = g_malloc(len);
|
|
|
|
|
for (i = octets; i > 0; i--)
|
2021-07-30 08:53:16 +02:00
|
|
|
nm_strbuf_append(&s, &len, "%u.", p[i - 1] & 0xff);
|
|
|
|
|
nm_strbuf_append_str(&s, &len, "in-addr.arpa");
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-06-03 22:07:38 +02:00
|
|
|
g_ptr_array_add(domains, str);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-06-03 22:07:38 +02:00
|
|
|
ip2 += 1 << ((32 - plen) & ~7);
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-06-03 22:07:42 +02:00
|
|
|
|
|
|
|
|
/**
|
2020-11-20 20:08:37 +01:00
|
|
|
* nm_utils_get_reverse_dns_domains_ip_6:
|
2016-06-03 22:07:42 +02:00
|
|
|
* @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
|
2020-11-20 20:08:37 +01:00
|
|
|
nm_utils_get_reverse_dns_domains_ip_6(const struct in6_addr *ip, guint8 plen, GPtrArray *domains)
|
2016-06-03 22:07:42 +02:00
|
|
|
{
|
|
|
|
|
struct in6_addr addr;
|
|
|
|
|
guint nibbles, bits, entries;
|
|
|
|
|
int i, j;
|
|
|
|
|
gsize len0, len;
|
2021-11-09 13:28:54 +01:00
|
|
|
char *str, *s;
|
2016-06-03 22:07:42 +02:00
|
|
|
|
|
|
|
|
g_return_if_fail(domains);
|
|
|
|
|
g_return_if_fail(plen <= 128);
|
|
|
|
|
|
|
|
|
|
if (!plen)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
memcpy(&addr, ip, sizeof(struct in6_addr));
|
glib-aux: rename IP address related helpers from "nm-inet-utils.h"
- name things related to `in_addr_t`, `struct in6_addr`, `NMIPAddr` as
`nm_ip4_addr_*()`, `nm_ip6_addr_*()`, `nm_ip_addr_*()`, respectively.
- we have a wrapper `nm_inet_ntop()` for `inet_ntop()`. This name
of our wrapper is chosen to be familiar with the libc underlying
function. With this, also name functions that are about string
representations of addresses `nm_inet_*()`, `nm_inet4_*()`,
`nm_inet6_*()`. For example, `nm_inet_parse_str()`,
`nm_inet_is_normalized()`.
<<<<
R() {
git grep -l "$1" | xargs sed -i "s/\<$1\>/$2/g"
}
R NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX NM_CMP_DIRECT_IP4_ADDR_SAME_PREFIX
R NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX NM_CMP_DIRECT_IP6_ADDR_SAME_PREFIX
R NM_UTILS_INET_ADDRSTRLEN NM_INET_ADDRSTRLEN
R _nm_utils_inet4_ntop nm_inet4_ntop
R _nm_utils_inet6_ntop nm_inet6_ntop
R _nm_utils_ip4_get_default_prefix nm_ip4_addr_get_default_prefix
R _nm_utils_ip4_get_default_prefix0 nm_ip4_addr_get_default_prefix0
R _nm_utils_ip4_netmask_to_prefix nm_ip4_addr_netmask_to_prefix
R _nm_utils_ip4_prefix_to_netmask nm_ip4_addr_netmask_from_prefix
R nm_utils_inet4_ntop_dup nm_inet4_ntop_dup
R nm_utils_inet6_ntop_dup nm_inet6_ntop_dup
R nm_utils_inet_ntop nm_inet_ntop
R nm_utils_inet_ntop_dup nm_inet_ntop_dup
R nm_utils_ip4_address_clear_host_address nm_ip4_addr_clear_host_address
R nm_utils_ip4_address_is_link_local nm_ip4_addr_is_link_local
R nm_utils_ip4_address_is_loopback nm_ip4_addr_is_loopback
R nm_utils_ip4_address_is_zeronet nm_ip4_addr_is_zeronet
R nm_utils_ip4_address_same_prefix nm_ip4_addr_same_prefix
R nm_utils_ip4_address_same_prefix_cmp nm_ip4_addr_same_prefix_cmp
R nm_utils_ip6_address_clear_host_address nm_ip6_addr_clear_host_address
R nm_utils_ip6_address_same_prefix nm_ip6_addr_same_prefix
R nm_utils_ip6_address_same_prefix_cmp nm_ip6_addr_same_prefix_cmp
R nm_utils_ip6_is_ula nm_ip6_addr_is_ula
R nm_utils_ip_address_same_prefix nm_ip_addr_same_prefix
R nm_utils_ip_address_same_prefix_cmp nm_ip_addr_same_prefix_cmp
R nm_utils_ip_is_site_local nm_ip_addr_is_site_local
R nm_utils_ipaddr_is_normalized nm_inet_is_normalized
R nm_utils_ipaddr_is_valid nm_inet_is_valid
R nm_utils_ipx_address_clear_host_address nm_ip_addr_clear_host_address
R nm_utils_parse_inaddr nm_inet_parse_str
R nm_utils_parse_inaddr_bin nm_inet_parse_bin
R nm_utils_parse_inaddr_bin_full nm_inet_parse_bin_full
R nm_utils_parse_inaddr_prefix nm_inet_parse_with_prefix_str
R nm_utils_parse_inaddr_prefix_bin nm_inet_parse_with_prefix_bin
R test_nm_utils_ip6_address_same_prefix test_nm_ip_addr_same_prefix
./contrib/scripts/nm-code-format.sh -F
2022-08-19 13:15:20 +02:00
|
|
|
nm_ip6_addr_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--)
|
2021-07-30 08:53:16 +02:00
|
|
|
nm_strbuf_append(&s, &len, "%x.", (addr.s6_addr[j / 2] >> N_SHIFT(j)) & 0xf);
|
|
|
|
|
nm_strbuf_append_str(&s, &len, "ip6.arpa");
|
2016-06-03 22:07:42 +02:00
|
|
|
|
|
|
|
|
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 {
|
2021-11-09 13:28:54 +01:00
|
|
|
char *path;
|
2016-11-03 18:07:11 +01:00
|
|
|
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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-11-03 18:07:11 +01:00
|
|
|
ta = MAX(da->st.st_mtime, da->st.st_ctime);
|
|
|
|
|
tb = MAX(db->st.st_mtime, db->st.st_ctime);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-11-03 18:07:11 +01:00
|
|
|
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);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-11-17 09:56:16 +01:00
|
|
|
if (!S_ISREG(st->st_mode)) {
|
|
|
|
|
g_set_error_literal(error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, "not a regular file");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-11-17 09:56:16 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-11-17 09:56:16 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-11-17 09:56:16 +01:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-03 18:07:11 +01:00
|
|
|
char **
|
|
|
|
|
nm_utils_read_plugin_paths(const char *dirname, const char *prefix)
|
|
|
|
|
{
|
2021-11-09 13:28:54 +01:00
|
|
|
GDir *dir;
|
|
|
|
|
GError *error = NULL;
|
2016-11-03 18:07:11 +01:00
|
|
|
const char *item;
|
2021-11-09 13:28:54 +01:00
|
|
|
GArray *paths;
|
|
|
|
|
char **result;
|
2016-11-03 18:07:11 +01:00
|
|
|
guint i;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-11-03 18:07:11 +01:00
|
|
|
g_return_val_if_fail(dirname, NULL);
|
|
|
|
|
g_return_val_if_fail(prefix, NULL);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-11-03 18:07:11 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-11-03 18:07:11 +01:00
|
|
|
paths = g_array_new(FALSE, FALSE, sizeof(struct plugin_info));
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-11-03 18:07:11 +01:00
|
|
|
while ((item = g_dir_read_name(dir))) {
|
|
|
|
|
int errsv;
|
|
|
|
|
struct plugin_info data;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-11-03 18:07:11 +01:00
|
|
|
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;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-11-03 18:07:11 +01:00
|
|
|
data.path = g_build_filename(dirname, item, NULL);
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-11-03 18:07:11 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-11-17 09:56:16 +01:00
|
|
|
if (!nm_utils_validate_plugin(data.path, &data.st, &error)) {
|
|
|
|
|
nm_log_warn(LOGD_CORE, "plugin: skip invalid file %s: %s", data.path, error->message);
|
|
|
|
|
g_clear_error(&error);
|
2016-11-03 18:07:11 +01:00
|
|
|
goto skip;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2016-11-03 18:07:11 +01:00
|
|
|
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++)
|
2022-09-08 12:05:56 +02:00
|
|
|
result[i] = nm_g_array_index(paths, struct plugin_info, i).path;
|
2016-11-03 18:07:11 +01:00
|
|
|
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)
|
|
|
|
|
{
|
2021-11-09 13:28:54 +01:00
|
|
|
GHashTable *setting_diff;
|
|
|
|
|
char *setting_name, *prop_name;
|
2017-03-03 23:59:16 +01:00
|
|
|
GHashTableIter iter, iter2;
|
2021-11-09 13:28:54 +01:00
|
|
|
GString *str;
|
2017-03-03 23:59:16 +01:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-01-27 20:16:22 +01:00
|
|
|
*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,
|
2021-11-09 13:28:54 +01:00
|
|
|
guint32 *out_max_rate,
|
|
|
|
|
gboolean *out_metered,
|
|
|
|
|
gboolean *out_owe_transition_mode)
|
2020-01-27 20:16:22 +01:00
|
|
|
{
|
|
|
|
|
guint8 id, elem_len;
|
|
|
|
|
guint32 m;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-01-27 20:16:22 +01:00
|
|
|
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-09-28 16:03:33 +02:00
|
|
|
|
2020-01-27 20:16:22 +01:00
|
|
|
while (len) {
|
|
|
|
|
if (len < 2)
|
|
|
|
|
break;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-01-27 20:16:22 +01:00
|
|
|
id = *bytes++;
|
|
|
|
|
elem_len = *bytes++;
|
|
|
|
|
len -= 2;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-01-27 20:16:22 +01:00
|
|
|
if (elem_len > len)
|
|
|
|
|
break;
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-01-27 20:16:22 +01:00
|
|
|
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
|
2021-06-01 19:46:13 +02:00
|
|
|
&& bytes[3] == 0x11) /* OUI type: Network cost */
|
|
|
|
|
{
|
|
|
|
|
/* https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nct/ */
|
|
|
|
|
NM_SET_OUT(out_metered, (bytes[4] > 1)); /* Cost level > 1 */
|
|
|
|
|
}
|
|
|
|
|
if (elem_len >= 10 && bytes[0] == 0x50 /* OUI: WiFi Alliance */
|
2020-03-16 03:03:32 +01:00
|
|
|
&& 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;
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-01-27 20:16:22 +01:00
|
|
|
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 */
|
|
|
|
|
}
|
2020-09-28 16:03:33 +02:00
|
|
|
|
2020-01-27 19:31:23 +01:00
|
|
|
return CLAMP(val, 0, 100);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
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"), );
|
2021-05-24 21:49:35 +02:00
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
GPid pid;
|
2021-11-09 13:28:54 +01:00
|
|
|
GTask *task;
|
2021-05-24 21:49:35 +02:00
|
|
|
gulong cancellable_id;
|
|
|
|
|
GSource *child_watch_source;
|
|
|
|
|
GSource *timeout_source;
|
|
|
|
|
|
|
|
|
|
int child_stdin;
|
|
|
|
|
int child_stdout;
|
|
|
|
|
GSource *input_source;
|
|
|
|
|
GSource *output_source;
|
|
|
|
|
|
|
|
|
|
NMStrBuf in_buffer;
|
|
|
|
|
NMStrBuf out_buffer;
|
|
|
|
|
gsize out_buffer_offset;
|
|
|
|
|
} HelperInfo;
|
|
|
|
|
|
|
|
|
|
#define _NMLOG_PREFIX_NAME "helper"
|
|
|
|
|
#define _NMLOG_DOMAIN LOGD_CORE
|
|
|
|
|
#define _NMLOG2(level, info, ...) \
|
|
|
|
|
G_STMT_START \
|
|
|
|
|
{ \
|
|
|
|
|
if (nm_logging_enabled((level), (_NMLOG_DOMAIN))) { \
|
|
|
|
|
HelperInfo *_info = (info); \
|
|
|
|
|
\
|
|
|
|
|
_nm_log((level), \
|
|
|
|
|
(_NMLOG_DOMAIN), \
|
|
|
|
|
0, \
|
|
|
|
|
NULL, \
|
|
|
|
|
NULL, \
|
|
|
|
|
_NMLOG_PREFIX_NAME "[" NM_HASH_OBFUSCATE_PTR_FMT \
|
|
|
|
|
",%d]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \
|
|
|
|
|
NM_HASH_OBFUSCATE_PTR(_info), \
|
|
|
|
|
_info->pid _NM_UTILS_MACRO_REST(__VA_ARGS__)); \
|
|
|
|
|
} \
|
|
|
|
|
} \
|
|
|
|
|
G_STMT_END
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
helper_info_free(gpointer data)
|
|
|
|
|
{
|
|
|
|
|
HelperInfo *info = data;
|
|
|
|
|
|
|
|
|
|
nm_clear_g_source_inst(&info->child_watch_source);
|
|
|
|
|
nm_clear_g_source_inst(&info->timeout_source);
|
|
|
|
|
g_object_unref(info->task);
|
|
|
|
|
|
|
|
|
|
nm_str_buf_destroy(&info->in_buffer);
|
|
|
|
|
nm_str_buf_destroy(&info->out_buffer);
|
|
|
|
|
nm_clear_g_source_inst(&info->input_source);
|
|
|
|
|
nm_clear_g_source_inst(&info->output_source);
|
|
|
|
|
|
|
|
|
|
if (info->child_stdout != -1)
|
|
|
|
|
nm_close(info->child_stdout);
|
|
|
|
|
if (info->child_stdin != -1)
|
|
|
|
|
nm_close(info->child_stdin);
|
|
|
|
|
|
|
|
|
|
if (info->pid != -1) {
|
|
|
|
|
nm_assert(info->pid > 1);
|
|
|
|
|
nm_utils_kill_child_async(info->pid, SIGKILL, LOGD_CORE, _NMLOG_PREFIX_NAME, 0, NULL, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_free(info);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
helper_complete(HelperInfo *info, GError *error)
|
|
|
|
|
{
|
|
|
|
|
if (error) {
|
|
|
|
|
nm_clear_g_cancellable_disconnect(g_task_get_cancellable(info->task),
|
|
|
|
|
&info->cancellable_id);
|
|
|
|
|
g_task_return_error(info->task, error);
|
|
|
|
|
helper_info_free(info);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (info->input_source || info->output_source || info->pid != -1) {
|
|
|
|
|
/* Wait that pipes are closed and process has terminated */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nm_clear_g_cancellable_disconnect(g_task_get_cancellable(info->task), &info->cancellable_id);
|
|
|
|
|
g_task_return_pointer(info->task, nm_str_buf_finalize(&info->in_buffer, NULL), g_free);
|
|
|
|
|
helper_info_free(info);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
helper_can_write(int fd, GIOCondition condition, gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
HelperInfo *info = user_data;
|
|
|
|
|
gssize n_written;
|
|
|
|
|
int errsv;
|
|
|
|
|
|
|
|
|
|
if (NM_FLAGS_HAS(condition, G_IO_ERR)) {
|
|
|
|
|
errsv = EIO;
|
|
|
|
|
goto out_error;
|
|
|
|
|
} else if (NM_FLAGS_HAS(condition, G_IO_HUP)) {
|
|
|
|
|
errsv = EPIPE;
|
|
|
|
|
goto out_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n_written = write(info->child_stdin,
|
|
|
|
|
&((nm_str_buf_get_str_unsafe(&info->out_buffer))[info->out_buffer_offset]),
|
|
|
|
|
info->out_buffer.len - info->out_buffer_offset);
|
|
|
|
|
errsv = errno;
|
|
|
|
|
|
|
|
|
|
if (n_written < 0 && errsv != EAGAIN)
|
|
|
|
|
goto out_error;
|
|
|
|
|
|
|
|
|
|
if (n_written > 0) {
|
|
|
|
|
if ((gsize) n_written >= (info->out_buffer.len - info->out_buffer_offset)) {
|
|
|
|
|
nm_assert((gsize) n_written == (info->out_buffer.len - info->out_buffer_offset));
|
|
|
|
|
nm_clear_g_source_inst(&info->output_source);
|
|
|
|
|
nm_close(info->child_stdin);
|
|
|
|
|
info->child_stdin = -1;
|
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
|
|
|
}
|
|
|
|
|
info->out_buffer_offset += (gsize) n_written;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
|
|
|
|
|
|
|
|
out_error:
|
|
|
|
|
nm_clear_g_source_inst(&info->output_source);
|
|
|
|
|
helper_complete(info,
|
|
|
|
|
g_error_new(NM_UTILS_ERROR,
|
|
|
|
|
NM_UTILS_ERROR_UNKNOWN,
|
|
|
|
|
"error writing to helper: %d (%s)",
|
|
|
|
|
errsv,
|
|
|
|
|
nm_strerror_native(errsv)));
|
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
helper_have_data(int fd, GIOCondition condition, gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
HelperInfo *info = user_data;
|
|
|
|
|
gssize n_read;
|
2021-11-09 13:28:54 +01:00
|
|
|
GError *error = NULL;
|
2021-05-24 21:49:35 +02:00
|
|
|
|
|
|
|
|
n_read = nm_utils_fd_read(fd, &info->in_buffer);
|
|
|
|
|
_LOG2T(info, "read returns %ld", (long) n_read);
|
|
|
|
|
|
|
|
|
|
if (n_read > 0)
|
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
|
|
|
|
|
|
|
|
nm_clear_g_source_inst(&info->input_source);
|
|
|
|
|
nm_close(info->child_stdout);
|
|
|
|
|
info->child_stdout = -1;
|
|
|
|
|
|
|
|
|
|
_LOG2T(info, "stdout closed");
|
|
|
|
|
|
|
|
|
|
if (n_read < 0) {
|
|
|
|
|
error = g_error_new(NM_UTILS_ERROR,
|
|
|
|
|
NM_UTILS_ERROR_UNKNOWN,
|
|
|
|
|
"read from process returned %d (%s)",
|
|
|
|
|
(int) -n_read,
|
|
|
|
|
nm_strerror_native((int) -n_read));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
helper_complete(info, error);
|
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
helper_child_terminated(GPid pid, int status, gpointer user_data)
|
|
|
|
|
{
|
2021-11-09 13:28:54 +01:00
|
|
|
HelperInfo *info = user_data;
|
|
|
|
|
GError *error = NULL;
|
2021-05-24 21:49:35 +02:00
|
|
|
gs_free char *status_desc = NULL;
|
|
|
|
|
|
|
|
|
|
_LOG2D(info, "process %s", (status_desc = nm_utils_get_process_exit_status_desc(status)));
|
|
|
|
|
|
|
|
|
|
info->pid = -1;
|
|
|
|
|
nm_clear_g_source_inst(&info->child_watch_source);
|
|
|
|
|
|
|
|
|
|
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
|
|
|
|
if (!status_desc)
|
|
|
|
|
status_desc = nm_utils_get_process_exit_status_desc(status);
|
|
|
|
|
error =
|
|
|
|
|
g_error_new(NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, "helper process %s", status_desc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
helper_complete(info, error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
helper_timeout(gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
HelperInfo *info = user_data;
|
|
|
|
|
|
|
|
|
|
nm_clear_g_source_inst(&info->timeout_source);
|
|
|
|
|
helper_complete(info, g_error_new_literal(NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, "timed out"));
|
|
|
|
|
|
|
|
|
|
return G_SOURCE_CONTINUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
helper_cancelled(GObject *object, gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
HelperInfo *info = user_data;
|
2021-11-09 13:28:54 +01:00
|
|
|
GError *error = NULL;
|
2021-05-24 21:49:35 +02:00
|
|
|
|
|
|
|
|
nm_clear_g_signal_handler(g_task_get_cancellable(info->task), &info->cancellable_id);
|
|
|
|
|
nm_utils_error_set_cancelled(&error, FALSE, NULL);
|
|
|
|
|
helper_complete(info, error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2021-11-09 13:28:54 +01:00
|
|
|
nm_utils_spawn_helper(const char *const *args,
|
|
|
|
|
GCancellable *cancellable,
|
2021-05-24 21:49:35 +02:00
|
|
|
GAsyncReadyCallback callback,
|
|
|
|
|
gpointer cb_data)
|
|
|
|
|
{
|
|
|
|
|
gs_free_error GError *error = NULL;
|
2021-11-09 13:28:54 +01:00
|
|
|
gs_free char *commands = NULL;
|
|
|
|
|
HelperInfo *info;
|
2021-05-24 21:49:35 +02:00
|
|
|
int fd_flags;
|
2021-11-09 13:28:54 +01:00
|
|
|
const char *const *arg;
|
2021-05-24 21:49:35 +02:00
|
|
|
|
|
|
|
|
nm_assert(args && args[0]);
|
|
|
|
|
|
|
|
|
|
info = g_new(HelperInfo, 1);
|
|
|
|
|
*info = (HelperInfo){
|
|
|
|
|
.task = nm_g_task_new(NULL, cancellable, nm_utils_spawn_helper, callback, cb_data),
|
|
|
|
|
.child_stdin = -1,
|
|
|
|
|
.child_stdout = -1,
|
|
|
|
|
.pid = -1,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (!g_spawn_async_with_pipes("/",
|
|
|
|
|
(char **) NM_MAKE_STRV(LIBEXECDIR "/nm-daemon-helper"),
|
|
|
|
|
(char **) NM_MAKE_STRV(),
|
|
|
|
|
G_SPAWN_DO_NOT_REAP_CHILD,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
|
|
|
|
&info->pid,
|
|
|
|
|
&info->child_stdin,
|
|
|
|
|
&info->child_stdout,
|
|
|
|
|
NULL,
|
|
|
|
|
&error)) {
|
|
|
|
|
info->child_stdin = -1;
|
|
|
|
|
info->child_stdout = -1;
|
|
|
|
|
info->pid = -1;
|
|
|
|
|
g_task_return_error(info->task,
|
|
|
|
|
g_error_new(NM_UTILS_ERROR,
|
|
|
|
|
NM_UTILS_ERROR_UNKNOWN,
|
|
|
|
|
"error spawning nm-helper: %s",
|
|
|
|
|
error->message));
|
|
|
|
|
helper_info_free(info);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_LOG2D(info, "spawned process with args: %s", (commands = g_strjoinv(" ", (char **) args)));
|
|
|
|
|
|
|
|
|
|
info->child_watch_source = g_child_watch_source_new(info->pid);
|
|
|
|
|
g_source_set_callback(info->child_watch_source,
|
|
|
|
|
G_SOURCE_FUNC(helper_child_terminated),
|
|
|
|
|
info,
|
|
|
|
|
NULL);
|
|
|
|
|
g_source_attach(info->child_watch_source, g_main_context_get_thread_default());
|
|
|
|
|
|
|
|
|
|
info->timeout_source =
|
|
|
|
|
nm_g_timeout_source_new_seconds(20, G_PRIORITY_DEFAULT, helper_timeout, info, NULL);
|
|
|
|
|
g_source_attach(info->timeout_source, g_main_context_get_thread_default());
|
|
|
|
|
|
|
|
|
|
/* Set file descriptors as non-blocking */
|
|
|
|
|
fd_flags = fcntl(info->child_stdin, F_GETFD, 0);
|
|
|
|
|
fcntl(info->child_stdin, F_SETFL, fd_flags | O_NONBLOCK);
|
|
|
|
|
fd_flags = fcntl(info->child_stdout, F_GETFD, 0);
|
|
|
|
|
fcntl(info->child_stdout, F_SETFL, fd_flags | O_NONBLOCK);
|
|
|
|
|
|
|
|
|
|
/* Watch process stdin */
|
2023-02-20 18:14:14 +01:00
|
|
|
info->out_buffer = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_40, TRUE);
|
2021-05-24 21:49:35 +02:00
|
|
|
for (arg = args; *arg; arg++) {
|
|
|
|
|
nm_str_buf_append(&info->out_buffer, *arg);
|
|
|
|
|
nm_str_buf_append_c(&info->out_buffer, '\0');
|
|
|
|
|
}
|
|
|
|
|
info->output_source = nm_g_unix_fd_source_new(info->child_stdin,
|
|
|
|
|
G_IO_OUT | G_IO_ERR | G_IO_HUP,
|
|
|
|
|
G_PRIORITY_DEFAULT,
|
|
|
|
|
helper_can_write,
|
|
|
|
|
info,
|
|
|
|
|
NULL);
|
|
|
|
|
g_source_attach(info->output_source, g_main_context_get_thread_default());
|
|
|
|
|
|
|
|
|
|
/* Watch process stdout */
|
2023-02-20 18:14:14 +01:00
|
|
|
info->in_buffer = NM_STR_BUF_INIT(0, FALSE);
|
2021-05-24 21:49:35 +02:00
|
|
|
info->input_source = nm_g_unix_fd_source_new(info->child_stdout,
|
|
|
|
|
G_IO_IN | G_IO_ERR | G_IO_HUP,
|
|
|
|
|
G_PRIORITY_DEFAULT,
|
|
|
|
|
helper_have_data,
|
|
|
|
|
info,
|
|
|
|
|
NULL);
|
|
|
|
|
g_source_attach(info->input_source, g_main_context_get_thread_default());
|
|
|
|
|
|
|
|
|
|
if (cancellable) {
|
|
|
|
|
gulong signal_id;
|
|
|
|
|
|
|
|
|
|
signal_id = g_cancellable_connect(cancellable, G_CALLBACK(helper_cancelled), info, NULL);
|
|
|
|
|
if (signal_id == 0) {
|
|
|
|
|
/* the request is already cancelled. Return. */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
info->cancellable_id = signal_id;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
nm_utils_spawn_helper_finish(GAsyncResult *result, GError **error)
|
|
|
|
|
{
|
|
|
|
|
GTask *task = G_TASK(result);
|
|
|
|
|
|
|
|
|
|
nm_assert(nm_g_task_is_valid(result, NULL, nm_utils_spawn_helper));
|
|
|
|
|
|
|
|
|
|
return g_task_propagate_pointer(task, error);
|
|
|
|
|
}
|
2022-01-18 17:30:14 +01:00
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* nm_utils_get_nm_uid:
|
|
|
|
|
*
|
|
|
|
|
* Checks what EUID NetworkManager is running as.
|
|
|
|
|
* Saves the EUID so it can be reused without making many syscalls.
|
|
|
|
|
*/
|
|
|
|
|
uid_t
|
|
|
|
|
nm_utils_get_nm_uid(void)
|
|
|
|
|
{
|
2022-01-18 18:21:01 +01:00
|
|
|
static int u_static = -1;
|
|
|
|
|
int u;
|
2022-01-18 17:30:14 +01:00
|
|
|
|
2022-01-18 18:21:01 +01:00
|
|
|
again:
|
|
|
|
|
if ((u = g_atomic_int_get(&u_static)) == -1) {
|
|
|
|
|
uid_t u2;
|
|
|
|
|
|
|
|
|
|
u2 = geteuid();
|
|
|
|
|
u = u2;
|
|
|
|
|
nm_assert(u == u2);
|
|
|
|
|
nm_assert(u >= 0);
|
|
|
|
|
if (!g_atomic_int_compare_and_exchange(&u_static, -1, u))
|
|
|
|
|
goto again;
|
2022-01-18 17:30:14 +01:00
|
|
|
}
|
2022-01-18 18:21:01 +01:00
|
|
|
|
|
|
|
|
return u;
|
2022-01-18 17:30:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* nm_utils_get_nm_gid:
|
|
|
|
|
*
|
|
|
|
|
* Checks what EGID NetworkManager is running as.
|
|
|
|
|
* Saves the EGID so it can be reused without making many syscalls.
|
|
|
|
|
*/
|
|
|
|
|
gid_t
|
|
|
|
|
nm_utils_get_nm_gid(void)
|
|
|
|
|
{
|
2022-01-18 18:21:01 +01:00
|
|
|
static int g_static = -1;
|
|
|
|
|
int g;
|
2022-01-18 17:30:14 +01:00
|
|
|
|
2022-01-18 18:21:01 +01:00
|
|
|
again:
|
|
|
|
|
if ((g = g_atomic_int_get(&g_static)) == -1) {
|
|
|
|
|
gid_t g2;
|
|
|
|
|
|
|
|
|
|
g2 = geteuid();
|
|
|
|
|
g = g2;
|
|
|
|
|
nm_assert(g == g2);
|
|
|
|
|
nm_assert(g >= 0);
|
|
|
|
|
if (!g_atomic_int_compare_and_exchange(&g_static, -1, g))
|
|
|
|
|
goto again;
|
2022-01-18 17:30:14 +01:00
|
|
|
}
|
2022-01-18 18:21:01 +01:00
|
|
|
|
|
|
|
|
return g;
|
2022-01-18 17:30:14 +01:00
|
|
|
}
|
2022-03-04 08:09:39 +01:00
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* nm_utils_shorten_hostname:
|
|
|
|
|
* @hostname: the input hostname
|
|
|
|
|
* @shortened: (out) (transfer full): on return, the shortened hostname
|
|
|
|
|
*
|
|
|
|
|
* Checks whether the input hostname is valid. If not, tries to shorten it
|
2022-04-20 19:16:47 +02:00
|
|
|
* to HOST_NAME_MAX (64) or to the first dot, whatever comes earlier.
|
2022-03-04 08:09:39 +01:00
|
|
|
* The new hostname is returned in @shortened.
|
|
|
|
|
*
|
|
|
|
|
* Returns: %TRUE if the input hostname was already valid or if was shortened
|
|
|
|
|
* successfully; %FALSE otherwise
|
|
|
|
|
*/
|
|
|
|
|
gboolean
|
|
|
|
|
nm_utils_shorten_hostname(const char *hostname, char **shortened)
|
|
|
|
|
{
|
|
|
|
|
gs_free char *s = NULL;
|
|
|
|
|
const char *dot;
|
|
|
|
|
gsize l;
|
|
|
|
|
|
|
|
|
|
nm_assert(hostname);
|
|
|
|
|
nm_assert(shortened);
|
|
|
|
|
|
2022-04-12 17:25:53 +02:00
|
|
|
if (nm_hostname_is_valid(hostname, FALSE)) {
|
2022-03-04 08:09:39 +01:00
|
|
|
*shortened = NULL;
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dot = strchr(hostname, '.');
|
|
|
|
|
if (dot)
|
|
|
|
|
l = (dot - hostname);
|
|
|
|
|
else
|
|
|
|
|
l = strlen(hostname);
|
2022-04-20 19:16:47 +02:00
|
|
|
l = MIN(l, (gsize) NM_HOST_NAME_MAX);
|
2022-03-04 08:09:39 +01:00
|
|
|
|
|
|
|
|
s = g_strndup(hostname, l);
|
|
|
|
|
|
2022-04-12 17:25:53 +02:00
|
|
|
if (!nm_hostname_is_valid(s, FALSE)) {
|
2022-03-04 08:09:39 +01:00
|
|
|
*shortened = NULL;
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*shortened = g_steal_pointer(&s);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|