diff options
| author | Michael Biebl <biebl@debian.org> | 2020-10-05 22:27:18 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2020-10-05 22:27:18 +0200 |
| commit | aafc1dbe4712c86189bbc1d4d54ad8cb4c69be7e (patch) | |
| tree | a2a9bb4d007339a0b1304540388230ccedac32bd /src/nm-core-utils.c | |
| parent | e7b44ef4c80907346ec7492a09c45277459924fc (diff) | |
New upstream version 1.27.90 upstream/1.27.90
Diffstat (limited to 'src/nm-core-utils.c')
| -rw-r--r-- | src/nm-core-utils.c | 7577 |
1 files changed, 4037 insertions, 3540 deletions
diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index 3950c3c3..7adc011e 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Copyright (C) 2004 - 2018 Red Hat, Inc. * Copyright (C) 2005 - 2008 Novell, Inc. @@ -36,10 +36,11 @@ #include "nm-setting-wireless-security.h" #ifdef __NM_SD_UTILS_H__ -#error "nm-core-utils.c should stay independent of systemd utils. Are you looking for NetworkMangerUtils.c? " + #error \ + "nm-core-utils.c should stay independent of systemd utils. Are you looking for NetworkMangerUtils.c? " #endif -G_STATIC_ASSERT (sizeof (NMUtilsTestFlags) <= sizeof (int)); +G_STATIC_ASSERT(sizeof(NMUtilsTestFlags) <= sizeof(int)); /* we read _nm_utils_testing without memory barrier. This is thread-safe, * because the static variable is initialized to zero, and only reset @@ -54,120 +55,120 @@ G_STATIC_ASSERT (sizeof (NMUtilsTestFlags) <= sizeof (int)); static int _nm_utils_testing = 0; gboolean -nm_utils_get_testing_initialized () +nm_utils_get_testing_initialized() { - NMUtilsTestFlags flags; + 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; + flags = (NMUtilsTestFlags) _nm_utils_testing; + if (flags == NM_UTILS_TEST_NONE) + flags = (NMUtilsTestFlags) g_atomic_int_get(&_nm_utils_testing); + return flags != NM_UTILS_TEST_NONE; } NMUtilsTestFlags -nm_utils_get_testing () +nm_utils_get_testing() { - NMUtilsTestFlags flags; + NMUtilsTestFlags flags; again: - 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; - - 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; + 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; + + 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; } void -_nm_utils_set_testing (NMUtilsTestFlags flags) +_nm_utils_set_testing(NMUtilsTestFlags flags) { - g_assert (!NM_FLAGS_ANY (flags, ~NM_UTILS_TEST_ALL)); + 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); + /* 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 (); - } + 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(); + } } /*****************************************************************************/ -static GSList *_singletons = NULL; +static GSList * _singletons = NULL; static gboolean _singletons_shutdown = FALSE; static void -_nm_singleton_instance_weak_cb (gpointer data, - GObject *where_the_object_was) +_nm_singleton_instance_weak_cb(gpointer data, GObject *where_the_object_was) { - nm_assert (g_slist_find (_singletons, where_the_object_was)); + nm_assert(g_slist_find(_singletons, where_the_object_was)); - _singletons = g_slist_remove (_singletons, where_the_object_was); + _singletons = g_slist_remove(_singletons, where_the_object_was); } -static void __attribute__((destructor)) -_nm_singleton_instance_destroy (void) +static void __attribute__((destructor)) _nm_singleton_instance_destroy(void) { - _singletons_shutdown = TRUE; + _singletons_shutdown = TRUE; - while (_singletons) { - GObject *instance = _singletons->data; + while (_singletons) { + GObject *instance = _singletons->data; - _singletons = g_slist_delete_link (_singletons, _singletons); + _singletons = g_slist_delete_link(_singletons, _singletons); - g_object_weak_unref (instance, _nm_singleton_instance_weak_cb, NULL); + g_object_weak_unref(instance, _nm_singleton_instance_weak_cb, NULL); - 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)); - } + 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)); + } - g_object_unref (instance); - } + g_object_unref(instance); + } } void -_nm_singleton_instance_register_destruction (GObject *instance) +_nm_singleton_instance_register_destruction(GObject *instance) { - g_return_if_fail (G_IS_OBJECT (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); + /* 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); + g_object_weak_ref(instance, _nm_singleton_instance_weak_cb, NULL); - _singletons = g_slist_prepend (_singletons, instance); + _singletons = g_slist_prepend(_singletons, instance); } /*****************************************************************************/ static double -_exp10 (guint16 ex) +_exp10(guint16 ex) { - double v; + double v; - if (ex == 0) - return 1.0; + if (ex == 0) + return 1.0; - v = _exp10 (ex / 2); - v = v * v; - if (ex % 2) - v *= 10; - return v; + v = _exp10(ex / 2); + v = v * v; + if (ex % 2) + v *= 10; + return v; } /* @@ -177,11 +178,11 @@ _exp10 (guint16 ex) * Returns: 10^ex, or pow(10, ex), or exp10(ex). */ double -nm_utils_exp10 (gint16 ex) +nm_utils_exp10(gint16 ex) { - if (ex >= 0) - return _exp10 (ex); - return 1.0 / _exp10 (- ((gint32) ex)); + if (ex >= 0) + return _exp10(ex); + return 1.0 / _exp10(-((gint32) ex)); } /*****************************************************************************/ @@ -196,324 +197,380 @@ nm_utils_exp10 (gint16 ex) * Returns: %TRUE if @addr is a valid Ethernet address, %FALSE if it is not. */ gboolean -nm_ethernet_address_is_valid (gconstpointer addr, gssize len) +nm_ethernet_address_is_valid(gconstpointer addr, gssize len) { - guint8 invalid_addr[4][ETH_ALEN] = { - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x44, 0x44, 0x44, 0x44, 0x44, 0x44}, - {0x00, 0x30, 0xb4, 0x00, 0x00, 0x00}, /* prism54 dummy MAC */ - }; - guint8 addr_bin[ETH_ALEN]; - guint i; - - if (!addr) { - g_return_val_if_fail (len == -1 || len == ETH_ALEN, FALSE); - return FALSE; - } - - if (len == -1) { - if (!nm_utils_hwaddr_aton (addr, addr_bin, ETH_ALEN)) - return FALSE; - addr = addr_bin; - } else if (len != ETH_ALEN) - g_return_val_if_reached (FALSE); - - /* Check for multicast address */ - if ((((guint8 *) addr)[0]) & 0x01) - return FALSE; - - for (i = 0; i < G_N_ELEMENTS (invalid_addr); i++) { - if (nm_utils_hwaddr_matches (addr, ETH_ALEN, invalid_addr[i], ETH_ALEN)) - return FALSE; - } - - return TRUE; + guint8 invalid_addr[4][ETH_ALEN] = { + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x44, 0x44, 0x44, 0x44, 0x44, 0x44}, + {0x00, 0x30, 0xb4, 0x00, 0x00, 0x00}, /* prism54 dummy MAC */ + }; + guint8 addr_bin[ETH_ALEN]; + guint i; + + if (!addr) { + g_return_val_if_fail(len == -1 || len == ETH_ALEN, FALSE); + return FALSE; + } + + if (len == -1) { + if (!nm_utils_hwaddr_aton(addr, addr_bin, ETH_ALEN)) + return FALSE; + addr = addr_bin; + } else if (len != ETH_ALEN) + g_return_val_if_reached(FALSE); + + /* Check for multicast address */ + if ((((guint8 *) addr)[0]) & 0x01) + return FALSE; + + for (i = 0; i < G_N_ELEMENTS(invalid_addr); i++) { + if (nm_utils_hwaddr_matches(addr, ETH_ALEN, invalid_addr[i], ETH_ALEN)) + return FALSE; + } + + return TRUE; } /*****************************************************************************/ void -nm_utils_array_remove_at_indexes (GArray *array, const guint *indexes_to_delete, gsize len) +nm_utils_array_remove_at_indexes(GArray *array, const guint *indexes_to_delete, gsize len) { - gsize elt_size; - guint index_to_delete; - guint i_src; - guint mm_src, mm_dst, mm_len; - gsize i_itd; - guint res_length; - - g_return_if_fail (array); - if (!len) - return; - g_return_if_fail (indexes_to_delete); - - elt_size = g_array_get_element_size (array); - - i_itd = 0; - index_to_delete = indexes_to_delete[0]; - if (index_to_delete >= array->len) - g_return_if_reached (); - - res_length = array->len - 1; - - mm_dst = index_to_delete; - mm_src = index_to_delete; - mm_len = 0; - - for (i_src = index_to_delete; i_src < array->len; i_src++) { - if (i_src < index_to_delete) - mm_len++; - else { - /* we require indexes_to_delete to contain non-repeated, ascending - * indexes. Otherwise we would need to presort the indexes. */ - while (TRUE) { - guint dd; - - if (i_itd + 1 >= len) { - index_to_delete = G_MAXUINT; - break; - } - - dd = indexes_to_delete[++i_itd]; - if (dd > index_to_delete) { - if (dd >= array->len) - g_warn_if_reached (); - else { - g_assert (res_length > 0); - res_length--; - } - index_to_delete = dd; - break; - } - g_warn_if_reached (); - } - - if (mm_len) { - memmove (&array->data[mm_dst * elt_size], - &array->data[mm_src * elt_size], - mm_len * elt_size); - mm_dst += mm_len; - mm_src += mm_len + 1; - mm_len = 0; - } else - mm_src++; - } - } - if (mm_len) { - memmove (&array->data[mm_dst * elt_size], - &array->data[mm_src * elt_size], - mm_len * elt_size); - } - g_array_set_size (array, res_length); + gsize elt_size; + guint index_to_delete; + guint i_src; + guint mm_src, mm_dst, mm_len; + gsize i_itd; + guint res_length; + + g_return_if_fail(array); + if (!len) + return; + g_return_if_fail(indexes_to_delete); + + elt_size = g_array_get_element_size(array); + + i_itd = 0; + index_to_delete = indexes_to_delete[0]; + if (index_to_delete >= array->len) + g_return_if_reached(); + + res_length = array->len - 1; + + mm_dst = index_to_delete; + mm_src = index_to_delete; + mm_len = 0; + + for (i_src = index_to_delete; i_src < array->len; i_src++) { + if (i_src < index_to_delete) + mm_len++; + else { + /* we require indexes_to_delete to contain non-repeated, ascending + * indexes. Otherwise, we would need to presort the indexes. */ + while (TRUE) { + guint dd; + + if (i_itd + 1 >= len) { + index_to_delete = G_MAXUINT; + break; + } + + dd = indexes_to_delete[++i_itd]; + if (dd > index_to_delete) { + if (dd >= array->len) + g_warn_if_reached(); + else { + g_assert(res_length > 0); + res_length--; + } + index_to_delete = dd; + break; + } + g_warn_if_reached(); + } + + if (mm_len) { + memmove(&array->data[mm_dst * elt_size], + &array->data[mm_src * elt_size], + mm_len * elt_size); + mm_dst += mm_len; + mm_src += mm_len + 1; + mm_len = 0; + } else + mm_src++; + } + } + if (mm_len) { + memmove(&array->data[mm_dst * elt_size], + &array->data[mm_src * elt_size], + mm_len * elt_size); + } + g_array_set_size(array, res_length); } static const char * -_trunk_first_line (char *str) +_trunk_first_line(char *str) { - char *s; + char *s; - s = strchr (str, '\n'); - if (s) - s[0] = '\0'; - return str; + s = strchr(str, '\n'); + if (s) + s[0] = '\0'; + return str; } int -nm_utils_modprobe (GError **error, gboolean suppress_error_logging, const char *arg1, ...) +nm_utils_modprobe(GError **error, gboolean suppress_error_logging, const char *arg1, ...) { - gs_unref_ptrarray GPtrArray *argv = NULL; - int exit_status; - gs_free char *_log_str = NULL; -#define ARGV_TO_STR(argv) (_log_str ? _log_str : (_log_str = g_strjoinv (" ", (char **) argv->pdata))) - GError *local = NULL; - va_list ap; - NMLogLevel llevel = suppress_error_logging ? LOGL_DEBUG : LOGL_ERR; - gs_free char *std_out = NULL, *std_err = NULL; - - g_return_val_if_fail (!error || !*error, -1); - g_return_val_if_fail (arg1, -1); - - /* construct the argument list */ - argv = g_ptr_array_sized_new (4); - g_ptr_array_add (argv, "/sbin/modprobe"); - g_ptr_array_add (argv, "--use-blacklist"); - g_ptr_array_add (argv, (char *) arg1); - - va_start (ap, arg1); - while ((arg1 = va_arg (ap, const char *))) - g_ptr_array_add (argv, (char *) arg1); - va_end (ap); - - g_ptr_array_add (argv, NULL); - - nm_log_dbg (LOGD_CORE, "modprobe: '%s'", ARGV_TO_STR (argv)); - if (!g_spawn_sync (NULL, (char **) argv->pdata, NULL, 0, NULL, NULL, &std_out, &std_err, &exit_status, &local)) { - nm_log (llevel, LOGD_CORE, NULL, NULL, "modprobe: '%s' failed: %s", ARGV_TO_STR (argv), local->message); - g_propagate_error (error, local); - return -1; - } else if (exit_status != 0) { - nm_log (llevel, LOGD_CORE, NULL, NULL, "modprobe: '%s' exited with error %d%s%s%s%s%s%s", ARGV_TO_STR (argv), exit_status, - std_out&&*std_out ? " (" : "", std_out&&*std_out ? _trunk_first_line (std_out) : "", std_out&&*std_out ? ")" : "", - std_err&&*std_err ? " (" : "", std_err&&*std_err ? _trunk_first_line (std_err) : "", std_err&&*std_err ? ")" : ""); - } - - return exit_status; + gs_unref_ptrarray GPtrArray *argv = NULL; + int exit_status; + gs_free char * _log_str = NULL; +#define ARGV_TO_STR(argv) \ + (_log_str ? _log_str : (_log_str = g_strjoinv(" ", (char **) argv->pdata))) + GError * local = NULL; + va_list ap; + NMLogLevel llevel = suppress_error_logging ? LOGL_DEBUG : LOGL_ERR; + gs_free char *std_out = NULL, *std_err = NULL; + + g_return_val_if_fail(!error || !*error, -1); + g_return_val_if_fail(arg1, -1); + + /* construct the argument list */ + argv = g_ptr_array_sized_new(4); + g_ptr_array_add(argv, "/sbin/modprobe"); + g_ptr_array_add(argv, "--use-blacklist"); + g_ptr_array_add(argv, (char *) arg1); + + va_start(ap, arg1); + while ((arg1 = va_arg(ap, const char *))) + g_ptr_array_add(argv, (char *) arg1); + va_end(ap); + + g_ptr_array_add(argv, NULL); + + nm_log_dbg(LOGD_CORE, "modprobe: '%s'", ARGV_TO_STR(argv)); + if (!g_spawn_sync(NULL, + (char **) argv->pdata, + NULL, + 0, + NULL, + NULL, + &std_out, + &std_err, + &exit_status, + &local)) { + nm_log(llevel, + LOGD_CORE, + NULL, + NULL, + "modprobe: '%s' failed: %s", + ARGV_TO_STR(argv), + local->message); + g_propagate_error(error, local); + return -1; + } else if (exit_status != 0) { + nm_log(llevel, + LOGD_CORE, + NULL, + NULL, + "modprobe: '%s' exited with error %d%s%s%s%s%s%s", + ARGV_TO_STR(argv), + exit_status, + std_out && *std_out ? " (" : "", + std_out && *std_out ? _trunk_first_line(std_out) : "", + std_out && *std_out ? ")" : "", + std_err && *std_err ? " (" : "", + std_err && *std_err ? _trunk_first_line(std_err) : "", + std_err && *std_err ? ")" : ""); + } + + return exit_status; } /*****************************************************************************/ typedef struct { - pid_t pid; - NMLogDomain log_domain; - union { - struct { - gint64 wait_start_us; - guint source_timeout_kill_id; - } async; - struct { - gboolean success; - int child_status; - } sync; - }; - NMUtilsKillChildAsyncCb callback; - void *user_data; - - char log_name[1]; /* variable-length object, must be last element!! */ + pid_t pid; + NMLogDomain log_domain; + union { + struct { + gint64 wait_start_us; + guint source_timeout_kill_id; + } async; + struct { + gboolean success; + int child_status; + } sync; + }; + NMUtilsKillChildAsyncCb callback; + void * user_data; + + char log_name[1]; /* variable-length object, must be last element!! */ } KillChildAsyncData; -#define LOG_NAME_FMT "kill child process '%s' (%ld)" +#define LOG_NAME_FMT "kill child process '%s' (%ld)" #define LOG_NAME_PROCESS_FMT "kill process '%s' (%ld)" -#define LOG_NAME_ARGS log_name,(long)pid +#define LOG_NAME_ARGS log_name, (long) pid static KillChildAsyncData * -_kc_async_data_alloc (pid_t pid, NMLogDomain log_domain, const char *log_name, NMUtilsKillChildAsyncCb callback, void *user_data) +_kc_async_data_alloc(pid_t pid, + NMLogDomain log_domain, + const char * log_name, + NMUtilsKillChildAsyncCb callback, + void * user_data) { - KillChildAsyncData *data; - size_t log_name_len; + 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); + /* 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; + data->pid = pid; + data->user_data = user_data; + data->callback = callback; + data->log_domain = log_domain; - return data; + 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) ) +_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; + 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) +_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"; - } + 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) ) +_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)) { - g_snprintf (buf, KC_WAITED_TO_STRING, " (%ld usec elapsed)", (long) (nm_utils_get_monotonic_timestamp_usec () - wait_start_us)); - return buf; + g_snprintf(buf, + KC_WAITED_TO_STRING, + " (%ld usec elapsed)", + (long) (nm_utils_get_monotonic_timestamp_usec() - wait_start_us)); + return buf; } static void -_kc_cb_watch_child (GPid pid, int status, gpointer user_data) +_kc_cb_watch_child(GPid pid, int status, gpointer user_data) { - KillChildAsyncData *data = user_data; - char buf_exit[KC_EXIT_TO_STRING_BUF_SIZE], buf_wait[KC_WAITED_TO_STRING]; + 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); + 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)); + 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); + if (data->callback) + data->callback(pid, TRUE, status, data->user_data); - g_free (data); + g_free(data); } static gboolean -_kc_cb_timeout_grace_period (void *user_data) +_kc_cb_timeout_grace_period(void *user_data) { - KillChildAsyncData *data = user_data; - int ret, errsv; - - data->async.source_timeout_kill_id = 0; - - if ((ret = kill (data->pid, SIGKILL)) != 0) { - errsv = errno; - /* ESRCH means, process does not exist or is already a zombie. */ - if (errsv != ESRCH) { - nm_log_err (LOGD_CORE | data->log_domain, "%s: kill(SIGKILL) returned unexpected return value %d: (%s, %d)", - data->log_name, ret, nm_strerror_native (errsv), errsv); - } - } else { - nm_log_dbg (data->log_domain, "%s: process not terminated after %ld usec. Sending SIGKILL signal", - data->log_name, (long) (nm_utils_get_monotonic_timestamp_usec () - data->async.wait_start_us)); - } - - return G_SOURCE_REMOVE; + KillChildAsyncData *data = user_data; + int ret, errsv; + + data->async.source_timeout_kill_id = 0; + + if ((ret = kill(data->pid, SIGKILL)) != 0) { + errsv = errno; + /* ESRCH means, process does not exist or is already a zombie. */ + if (errsv != ESRCH) { + nm_log_err(LOGD_CORE | data->log_domain, + "%s: kill(SIGKILL) returned unexpected return value %d: (%s, %d)", + data->log_name, + ret, + nm_strerror_native(errsv), + errsv); + } + } else { + nm_log_dbg(data->log_domain, + "%s: process not terminated after %ld usec. Sending SIGKILL signal", + data->log_name, + (long) (nm_utils_get_monotonic_timestamp_usec() - data->async.wait_start_us)); + } + + return G_SOURCE_REMOVE; } static gboolean -_kc_invoke_callback_idle (gpointer user_data) +_kc_invoke_callback_idle(gpointer user_data) { - KillChildAsyncData *data = user_data; + KillChildAsyncData *data = user_data; - if (data->sync.success) { - char buf_exit[KC_EXIT_TO_STRING_BUF_SIZE]; + 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); + 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); + data->callback(data->pid, data->sync.success, data->sync.child_status, data->user_data); + g_free(data); - return G_SOURCE_REMOVE; + return G_SOURCE_REMOVE; } static void -_kc_invoke_callback (pid_t pid, NMLogDomain log_domain, const char *log_name, NMUtilsKillChildAsyncCb callback, void *user_data, gboolean success, int child_status) +_kc_invoke_callback(pid_t pid, + NMLogDomain log_domain, + const char * log_name, + NMUtilsKillChildAsyncCb callback, + void * user_data, + gboolean success, + int child_status) { - KillChildAsyncData *data; + KillChildAsyncData *data; - if (!callback) - return; + 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; + data = _kc_async_data_alloc(pid, log_domain, log_name, callback, user_data); + data->sync.success = success; + data->sync.child_status = child_status; - g_idle_add (_kc_invoke_callback_idle, data); + g_idle_add(_kc_invoke_callback_idle, data); } /* nm_utils_kill_child_async: @@ -534,87 +591,118 @@ _kc_invoke_callback (pid_t pid, NMLogDomain log_domain, const char *log_name, NM * supports only one watcher per child. **/ void -nm_utils_kill_child_async (pid_t pid, int sig, NMLogDomain log_domain, - const char *log_name, guint32 wait_before_kill_msec, - NMUtilsKillChildAsyncCb callback, void *user_data) +nm_utils_kill_child_async(pid_t pid, + int sig, + NMLogDomain log_domain, + const char * log_name, + guint32 wait_before_kill_msec, + NMUtilsKillChildAsyncCb callback, + void * user_data) { - int status = 0, errsv; - pid_t ret; - KillChildAsyncData *data; - char buf_exit[KC_EXIT_TO_STRING_BUF_SIZE]; - - g_return_if_fail (pid > 0); - g_return_if_fail (log_name != NULL); - - /* let's see if the child already terminated... */ - ret = waitpid (pid, &status, WNOHANG); - if (ret > 0) { - nm_log_dbg (log_domain, LOG_NAME_FMT ": process %ld already terminated %s", - LOG_NAME_ARGS, (long) ret, _kc_exit_to_string (buf_exit, status)); - _kc_invoke_callback (pid, log_domain, log_name, callback, user_data, TRUE, status); - return; - } else if (ret != 0) { - errsv = errno; - /* ECHILD means, the process is not a child/does not exist or it has SIGCHILD blocked. */ - if (errsv != ECHILD) { - nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": unexpected error while waitpid: %s (%d)", - LOG_NAME_ARGS, nm_strerror_native (errsv), errsv); - _kc_invoke_callback (pid, log_domain, log_name, callback, user_data, FALSE, -1); - return; - } - } - - /* send the first signal. */ - if (kill (pid, sig) != 0) { - errsv = errno; - /* ESRCH means, process does not exist or is already a zombie. */ - if (errsv != ESRCH) { - nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": unexpected error sending %s: %s (%d)", - LOG_NAME_ARGS, _kc_signal_to_string (sig), nm_strerror_native (errsv), errsv); - _kc_invoke_callback (pid, log_domain, log_name, callback, user_data, FALSE, -1); - return; - } - - /* let's try again with waitpid, probably there was a race... */ - ret = waitpid (pid, &status, 0); - if (ret > 0) { - nm_log_dbg (log_domain, LOG_NAME_FMT ": process %ld already terminated %s", - LOG_NAME_ARGS, (long) ret, _kc_exit_to_string (buf_exit, status)); - _kc_invoke_callback (pid, log_domain, log_name, callback, user_data, TRUE, status); - } else { - errsv = errno; - nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": failed due to unexpected return value %ld by waitpid (%s, %d) after sending %s", - LOG_NAME_ARGS, (long) ret, nm_strerror_native (errsv), errsv, _kc_signal_to_string (sig)); - _kc_invoke_callback (pid, log_domain, log_name, callback, user_data, FALSE, -1); - } - return; - } - - data = _kc_async_data_alloc (pid, log_domain, log_name, callback, user_data); - data->async.wait_start_us = nm_utils_get_monotonic_timestamp_usec (); - - if (sig != SIGKILL && wait_before_kill_msec > 0) { - data->async.source_timeout_kill_id = g_timeout_add (wait_before_kill_msec, _kc_cb_timeout_grace_period, data); - nm_log_dbg (log_domain, "%s: wait for process to terminate after sending %s (send SIGKILL in %ld milliseconds)...", - data->log_name, _kc_signal_to_string (sig), (long) wait_before_kill_msec); - } else { - data->async.source_timeout_kill_id = 0; - nm_log_dbg (log_domain, "%s: wait for process to terminate after sending %s...", - data->log_name, _kc_signal_to_string (sig)); - } - - g_child_watch_add (pid, _kc_cb_watch_child, data); + int status = 0, errsv; + pid_t ret; + KillChildAsyncData *data; + char buf_exit[KC_EXIT_TO_STRING_BUF_SIZE]; + + g_return_if_fail(pid > 0); + g_return_if_fail(log_name != NULL); + + /* let's see if the child already terminated... */ + ret = waitpid(pid, &status, WNOHANG); + if (ret > 0) { + nm_log_dbg(log_domain, + LOG_NAME_FMT ": process %ld already terminated %s", + LOG_NAME_ARGS, + (long) ret, + _kc_exit_to_string(buf_exit, status)); + _kc_invoke_callback(pid, log_domain, log_name, callback, user_data, TRUE, status); + return; + } else if (ret != 0) { + errsv = errno; + /* ECHILD means, the process is not a child/does not exist or it has SIGCHILD blocked. */ + if (errsv != ECHILD) { + nm_log_err(LOGD_CORE | log_domain, + LOG_NAME_FMT ": unexpected error while waitpid: %s (%d)", + LOG_NAME_ARGS, + nm_strerror_native(errsv), + errsv); + _kc_invoke_callback(pid, log_domain, log_name, callback, user_data, FALSE, -1); + return; + } + } + + /* send the first signal. */ + if (kill(pid, sig) != 0) { + errsv = errno; + /* ESRCH means, process does not exist or is already a zombie. */ + if (errsv != ESRCH) { + nm_log_err(LOGD_CORE | log_domain, + LOG_NAME_FMT ": unexpected error sending %s: %s (%d)", + LOG_NAME_ARGS, + _kc_signal_to_string(sig), + nm_strerror_native(errsv), + errsv); + _kc_invoke_callback(pid, log_domain, log_name, callback, user_data, FALSE, -1); + return; + } + + /* let's try again with waitpid, probably there was a race... */ + ret = waitpid(pid, &status, 0); + if (ret > 0) { + nm_log_dbg(log_domain, + LOG_NAME_FMT ": process %ld already terminated %s", + LOG_NAME_ARGS, + (long) ret, + _kc_exit_to_string(buf_exit, status)); + _kc_invoke_callback(pid, log_domain, log_name, callback, user_data, TRUE, status); + } else { + errsv = errno; + nm_log_err( + LOGD_CORE | log_domain, + LOG_NAME_FMT + ": failed due to unexpected return value %ld by waitpid (%s, %d) after sending %s", + LOG_NAME_ARGS, + (long) ret, + nm_strerror_native(errsv), + errsv, + _kc_signal_to_string(sig)); + _kc_invoke_callback(pid, log_domain, log_name, callback, user_data, FALSE, -1); + } + return; + } + + data = _kc_async_data_alloc(pid, log_domain, log_name, callback, user_data); + data->async.wait_start_us = nm_utils_get_monotonic_timestamp_usec(); + + if (sig != SIGKILL && wait_before_kill_msec > 0) { + data->async.source_timeout_kill_id = + g_timeout_add(wait_before_kill_msec, _kc_cb_timeout_grace_period, data); + nm_log_dbg(log_domain, + "%s: wait for process to terminate after sending %s (send SIGKILL in %ld " + "milliseconds)...", + data->log_name, + _kc_signal_to_string(sig), + (long) wait_before_kill_msec); + } else { + data->async.source_timeout_kill_id = 0; + nm_log_dbg(log_domain, + "%s: wait for process to terminate after sending %s...", + data->log_name, + _kc_signal_to_string(sig)); + } + + g_child_watch_add(pid, _kc_cb_watch_child, data); } static gulong -_sleep_duration_convert_ms_to_us (guint32 sleep_duration_msec) +_sleep_duration_convert_ms_to_us(guint32 sleep_duration_msec) { - if (sleep_duration_msec > 0) { - guint64 x = (gint64) sleep_duration_msec * (guint64) 1000L; + if (sleep_duration_msec > 0) { + guint64 x = (gint64) sleep_duration_msec * (guint64) 1000L; - return x < G_MAXULONG ? (gulong) x : G_MAXULONG; - } - return G_USEC_PER_SEC / 20; + return x < G_MAXULONG ? (gulong) x : G_MAXULONG; + } + return G_USEC_PER_SEC / 20; } /* nm_utils_kill_child_sync: @@ -631,7 +719,7 @@ _sleep_duration_convert_ms_to_us (guint32 sleep_duration_msec) * Set to zero, to use the default (meaning 20 wakeups per seconds). * * Kill a child process synchronously and wait. The function first checks if the child already terminated - * and if it did, return the exit status. Otherwise send one @sig signal. @sig will always be + * and if it did, return the exit status. Otherwise, send one @sig signal. @sig will always be * sent unless the child already exited. If the child does not exit within @wait_before_kill_msec milliseconds, * the function will send %SIGKILL and waits for the child indefinitely. If @wait_before_kill_msec is zero, no * %SIGKILL signal will be sent. @@ -639,158 +727,212 @@ _sleep_duration_convert_ms_to_us (guint32 sleep_duration_msec) * In case of error, errno is preserved to contain the last reason of failure. **/ gboolean -nm_utils_kill_child_sync (pid_t pid, int sig, NMLogDomain log_domain, const char *log_name, - int *child_status, guint32 wait_before_kill_msec, - guint32 sleep_duration_msec) +nm_utils_kill_child_sync(pid_t pid, + int sig, + NMLogDomain log_domain, + const char *log_name, + int * child_status, + guint32 wait_before_kill_msec, + guint32 sleep_duration_msec) { - int status = 0, errsv = 0; - pid_t ret; - gboolean success = FALSE; - gboolean was_waiting = FALSE, send_kill = FALSE; - char buf_exit[KC_EXIT_TO_STRING_BUF_SIZE]; - char buf_wait[KC_WAITED_TO_STRING]; - gint64 wait_start_us; - - g_return_val_if_fail (pid > 0, FALSE); - g_return_val_if_fail (log_name != NULL, FALSE); - - /* check if the child process already terminated... */ - ret = waitpid (pid, &status, WNOHANG); - if (ret > 0) { - nm_log_dbg (log_domain, LOG_NAME_FMT ": process %ld already terminated %s", - LOG_NAME_ARGS, (long) ret, _kc_exit_to_string (buf_exit, status)); - success = TRUE; - goto out; - } else if (ret != 0) { - errsv = errno; - /* ECHILD means, the process is not a child/does not exist or it has SIGCHILD blocked. */ - if (errsv != ECHILD) { - nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": unexpected error while waitpid: %s (%d)", - LOG_NAME_ARGS, nm_strerror_native (errsv), errsv); - goto out; - } - } - - /* send first signal @sig */ - if (kill (pid, sig) != 0) { - errsv = errno; - /* ESRCH means, process does not exist or is already a zombie. */ - if (errsv != ESRCH) { - nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": failed to send %s: %s (%d)", - LOG_NAME_ARGS, _kc_signal_to_string (sig), nm_strerror_native (errsv), errsv); - } 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", - LOG_NAME_ARGS, (long) ret, nm_strerror_native (errsv), errsv, _kc_signal_to_string (sig)); - } - } - goto out; - } - - wait_start_us = nm_utils_get_monotonic_timestamp_usec (); - - /* wait for the process to terminated... */ - if (sig != SIGKILL) { - gint64 wait_until, now; - gulong sleep_time, sleep_duration_usec; - int loop_count = 0; - - sleep_duration_usec = _sleep_duration_convert_ms_to_us (sleep_duration_msec); - wait_until = wait_before_kill_msec <= 0 ? 0 : wait_start_us + (((gint64) wait_before_kill_msec) * 1000L); - - while (TRUE) { - ret = waitpid (pid, &status, WNOHANG); - if (ret > 0) { - nm_log_dbg (log_domain, LOG_NAME_FMT ": after sending %s, process %ld exited %s%s", - LOG_NAME_ARGS, _kc_signal_to_string (sig), (long) ret, _kc_exit_to_string (buf_exit, status), - was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : ""); - success = TRUE; - goto out; - } - if (ret == -1) { - errsv = errno; - /* ECHILD means, the process is not a child/does not exist or it has SIGCHILD blocked. */ - if (errsv != ECHILD) { - nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": after sending %s, waitpid failed with %s (%d)%s", - LOG_NAME_ARGS, _kc_signal_to_string (sig), nm_strerror_native (errsv), errsv, - was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : ""); - goto out; - } - } - - if (!wait_until) - break; - - now = nm_utils_get_monotonic_timestamp_usec (); - if (now >= wait_until) - break; - - if (!was_waiting) { - nm_log_dbg (log_domain, LOG_NAME_FMT ": waiting up to %ld milliseconds for process to terminate normally after sending %s...", - LOG_NAME_ARGS, (long) MAX (wait_before_kill_msec, 0), _kc_signal_to_string (sig)); - was_waiting = TRUE; - } - - sleep_time = MIN (wait_until - now, sleep_duration_usec); - if (loop_count < 20) { - /* At the beginning we expect the process to die fast. - * Limit the sleep time, the limit doubles with every iteration. */ - sleep_time = MIN (sleep_time, (((guint64) 1) << loop_count) * G_USEC_PER_SEC / 2000); - loop_count++; - } - g_usleep (sleep_time); - } - - /* send SIGKILL, if called with @wait_before_kill_msec > 0 */ - if (wait_until) { - nm_log_dbg (log_domain, LOG_NAME_FMT ": sending SIGKILL...", LOG_NAME_ARGS); - - send_kill = TRUE; - if (kill (pid, SIGKILL) != 0) { - errsv = errno; - /* ESRCH means, process does not exist or is already a zombie. */ - if (errsv != ESRCH) { - nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": failed to send SIGKILL (after sending %s), %s (%d)", - LOG_NAME_ARGS, _kc_signal_to_string (sig), nm_strerror_native (errsv), errsv); - goto out; - } - } - } - } - - if (!was_waiting) { - nm_log_dbg (log_domain, LOG_NAME_FMT ": waiting for process to terminate after sending %s%s...", - LOG_NAME_ARGS, _kc_signal_to_string (sig), send_kill ? " and SIGKILL" : ""); - } - - /* block until the child terminates. */ - while ((ret = waitpid (pid, &status, 0)) <= 0) { - errsv = errno; - - if (errsv != EINTR) { - nm_log_err (LOGD_CORE | log_domain, LOG_NAME_FMT ": after sending %s%s, waitpid failed with %s (%d)%s", - LOG_NAME_ARGS, _kc_signal_to_string (sig), send_kill ? " and SIGKILL" : "", nm_strerror_native (errsv), errsv, - _kc_waited_to_string (buf_wait, wait_start_us)); - goto out; - } - } - - nm_log_dbg (log_domain, LOG_NAME_FMT ": after sending %s%s, process %ld exited %s%s", - LOG_NAME_ARGS, _kc_signal_to_string (sig), send_kill ? " and SIGKILL" : "", (long) ret, - _kc_exit_to_string (buf_exit, status), _kc_waited_to_string (buf_wait, wait_start_us)); - success = TRUE; + int status = 0, errsv = 0; + pid_t ret; + gboolean success = FALSE; + gboolean was_waiting = FALSE, send_kill = FALSE; + char buf_exit[KC_EXIT_TO_STRING_BUF_SIZE]; + char buf_wait[KC_WAITED_TO_STRING]; + gint64 wait_start_us; + + g_return_val_if_fail(pid > 0, FALSE); + g_return_val_if_fail(log_name != NULL, FALSE); + + /* check if the child process already terminated... */ + ret = waitpid(pid, &status, WNOHANG); + if (ret > 0) { + nm_log_dbg(log_domain, + LOG_NAME_FMT ": process %ld already terminated %s", + LOG_NAME_ARGS, + (long) ret, + _kc_exit_to_string(buf_exit, status)); + success = TRUE; + goto out; + } else if (ret != 0) { + errsv = errno; + /* ECHILD means, the process is not a child/does not exist or it has SIGCHILD blocked. */ + if (errsv != ECHILD) { + nm_log_err(LOGD_CORE | log_domain, + LOG_NAME_FMT ": unexpected error while waitpid: %s (%d)", + LOG_NAME_ARGS, + nm_strerror_native(errsv), + errsv); + goto out; + } + } + + /* send first signal @sig */ + if (kill(pid, sig) != 0) { + errsv = errno; + /* ESRCH means, process does not exist or is already a zombie. */ + if (errsv != ESRCH) { + nm_log_err(LOGD_CORE | log_domain, + LOG_NAME_FMT ": failed to send %s: %s (%d)", + LOG_NAME_ARGS, + _kc_signal_to_string(sig), + nm_strerror_native(errsv), + errsv); + } 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", + LOG_NAME_ARGS, + (long) ret, + nm_strerror_native(errsv), + errsv, + _kc_signal_to_string(sig)); + } + } + goto out; + } + + wait_start_us = nm_utils_get_monotonic_timestamp_usec(); + + /* wait for the process to terminated... */ + if (sig != SIGKILL) { + gint64 wait_until, now; + gulong sleep_time, sleep_duration_usec; + int loop_count = 0; + + sleep_duration_usec = _sleep_duration_convert_ms_to_us(sleep_duration_msec); + wait_until = wait_before_kill_msec <= 0 + ? 0 + : wait_start_us + (((gint64) wait_before_kill_msec) * 1000L); + + while (TRUE) { + ret = waitpid(pid, &status, WNOHANG); + if (ret > 0) { + nm_log_dbg(log_domain, + LOG_NAME_FMT ": after sending %s, process %ld exited %s%s", + LOG_NAME_ARGS, + _kc_signal_to_string(sig), + (long) ret, + _kc_exit_to_string(buf_exit, status), + was_waiting ? _kc_waited_to_string(buf_wait, wait_start_us) : ""); + success = TRUE; + goto out; + } + if (ret == -1) { + errsv = errno; + /* ECHILD means, the process is not a child/does not exist or it has SIGCHILD blocked. */ + if (errsv != ECHILD) { + nm_log_err(LOGD_CORE | log_domain, + LOG_NAME_FMT ": after sending %s, waitpid failed with %s (%d)%s", + LOG_NAME_ARGS, + _kc_signal_to_string(sig), + nm_strerror_native(errsv), + errsv, + was_waiting ? _kc_waited_to_string(buf_wait, wait_start_us) : ""); + goto out; + } + } + + if (!wait_until) + break; + + now = nm_utils_get_monotonic_timestamp_usec(); + if (now >= wait_until) + break; + + if (!was_waiting) { + nm_log_dbg(log_domain, + LOG_NAME_FMT ": waiting up to %ld milliseconds for process to terminate " + "normally after sending %s...", + LOG_NAME_ARGS, + (long) MAX(wait_before_kill_msec, 0), + _kc_signal_to_string(sig)); + was_waiting = TRUE; + } + + sleep_time = MIN(wait_until - now, sleep_duration_usec); + if (loop_count < 20) { + /* At the beginning we expect the process to die fast. + * Limit the sleep time, the limit doubles with every iteration. */ + sleep_time = MIN(sleep_time, (((guint64) 1) << loop_count) * G_USEC_PER_SEC / 2000); + loop_count++; + } + g_usleep(sleep_time); + } + + /* send SIGKILL, if called with @wait_before_kill_msec > 0 */ + if (wait_until) { + nm_log_dbg(log_domain, LOG_NAME_FMT ": sending SIGKILL...", LOG_NAME_ARGS); + + send_kill = TRUE; + if (kill(pid, SIGKILL) != 0) { + errsv = errno; + /* ESRCH means, process does not exist or is already a zombie. */ + if (errsv != ESRCH) { + nm_log_err(LOGD_CORE | log_domain, + LOG_NAME_FMT ": failed to send SIGKILL (after sending %s), %s (%d)", + LOG_NAME_ARGS, + _kc_signal_to_string(sig), + nm_strerror_native(errsv), + errsv); + goto out; + } + } + } + } + + if (!was_waiting) { + nm_log_dbg(log_domain, + LOG_NAME_FMT ": waiting for process to terminate after sending %s%s...", + LOG_NAME_ARGS, + _kc_signal_to_string(sig), + send_kill ? " and SIGKILL" : ""); + } + + /* block until the child terminates. */ + while ((ret = waitpid(pid, &status, 0)) <= 0) { + errsv = errno; + + if (errsv != EINTR) { + nm_log_err(LOGD_CORE | log_domain, + LOG_NAME_FMT ": after sending %s%s, waitpid failed with %s (%d)%s", + LOG_NAME_ARGS, + _kc_signal_to_string(sig), + send_kill ? " and SIGKILL" : "", + nm_strerror_native(errsv), + errsv, + _kc_waited_to_string(buf_wait, wait_start_us)); + goto out; + } + } + + nm_log_dbg(log_domain, + LOG_NAME_FMT ": after sending %s%s, process %ld exited %s%s", + LOG_NAME_ARGS, + _kc_signal_to_string(sig), + send_kill ? " and SIGKILL" : "", + (long) ret, + _kc_exit_to_string(buf_exit, status), + _kc_waited_to_string(buf_wait, wait_start_us)); + success = TRUE; out: - if (child_status) - *child_status = success ? status : -1; - errno = success ? 0 : errsv; - return success; + if (child_status) + *child_status = success ? status : -1; + errno = success ? 0 : errsv; + return success; } /* nm_utils_kill_process_sync: @@ -817,195 +959,260 @@ out: * 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) +nm_utils_kill_process_sync(pid_t pid, + guint64 start_time, + int sig, + NMLogDomain log_domain, + const char *log_name, + guint32 wait_before_kill_msec, + guint32 sleep_duration_msec, + guint32 max_wait_msec) { - int errsv; - guint64 start_time0; - gint64 wait_until_sigkill, now, wait_start_us, max_wait_until; - gulong sleep_time, sleep_duration_usec; - int loop_count = 0; - gboolean was_waiting = FALSE; - char buf_wait[KC_WAITED_TO_STRING]; - char p_state; - - g_return_if_fail (pid > 0); - g_return_if_fail (log_name != NULL); - - start_time0 = nm_utils_get_start_time_for_pid (pid, &p_state, NULL); - if (start_time0 == 0) { - nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": cannot kill process %ld because it seems already gone", - LOG_NAME_ARGS, (long int) pid); - return; - } - if (start_time != 0 && start_time != start_time0) { - nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": don't kill process %ld because the start_time is unexpectedly %lu instead of %ld", - LOG_NAME_ARGS, (long int) pid, (unsigned long) start_time0, (unsigned long) start_time); - return; - } - - switch (p_state) { - case 'Z': - case 'x': - case 'X': - nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": cannot kill process %ld because it is already a zombie (%c)", - LOG_NAME_ARGS, (long int) pid, p_state); - return; - default: - break; - } - - if (kill (pid, sig) != 0) { - errsv = errno; - /* ESRCH means, process does not exist or is already a zombie. */ - if (errsv == ESRCH) { - nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": failed to send %s because process seems gone", - LOG_NAME_ARGS, _kc_signal_to_string (sig)); - } else { - nm_log_warn (LOGD_CORE | log_domain, LOG_NAME_PROCESS_FMT ": failed to send %s: %s (%d)", - LOG_NAME_ARGS, _kc_signal_to_string (sig), nm_strerror_native (errsv), errsv); - } - return; - } - - /* wait for the process to terminate... */ - - wait_start_us = nm_utils_get_monotonic_timestamp_usec (); - - sleep_duration_usec = _sleep_duration_convert_ms_to_us (sleep_duration_msec); - if (sig != SIGKILL && wait_before_kill_msec) - wait_until_sigkill = wait_start_us + (((gint64) wait_before_kill_msec) * 1000L); - else - wait_until_sigkill = 0; - if (max_wait_msec > 0) { - max_wait_until = wait_start_us + (((gint64) max_wait_msec) * 1000L); - if (wait_until_sigkill > 0 && wait_until_sigkill > max_wait_msec) - wait_until_sigkill = 0; - } else - max_wait_until = 0; - - while (TRUE) { - start_time = nm_utils_get_start_time_for_pid (pid, &p_state, NULL); - - if (start_time != start_time0) { - nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": process is gone after sending signal %s%s", - LOG_NAME_ARGS, _kc_signal_to_string (sig), - was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : ""); - return; - } - switch (p_state) { - case 'Z': - case 'x': - case 'X': - nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": process is a zombie (%c) after sending signal %s%s", - LOG_NAME_ARGS, p_state, _kc_signal_to_string (sig), - was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : ""); - return; - default: - break; - } - - if (kill (pid, 0) != 0) { - errsv = errno; - /* ESRCH means, process does not exist or is already a zombie. */ - if (errsv == ESRCH) { - nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": process is gone or a zombie after sending signal %s%s", - LOG_NAME_ARGS, _kc_signal_to_string (sig), - was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : ""); - } else { - nm_log_warn (LOGD_CORE | log_domain, LOG_NAME_PROCESS_FMT ": failed to kill(%ld, 0): %s (%d)%s", - LOG_NAME_ARGS, (long int) pid, nm_strerror_native (errsv), errsv, - was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : ""); - } - return; - } - - sleep_time = sleep_duration_usec; - now = nm_utils_get_monotonic_timestamp_usec (); - - if ( max_wait_until != 0 - && now >= max_wait_until) { - if (wait_until_sigkill != 0) { - /* wait_before_kill_msec is not larger then max_wait_until but we did not yet send - * SIGKILL. Although we already reached our timeout, we don't want to skip sending - * the signal. Even if we don't wait for the process to disappear. */ - nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": sending SIGKILL", LOG_NAME_ARGS); - kill (pid, SIGKILL); - } - nm_log_warn (log_domain, LOG_NAME_PROCESS_FMT ": timeout %u msec waiting for process to disappear (after sending %s)%s", - LOG_NAME_ARGS, (unsigned) max_wait_until, _kc_signal_to_string (sig), - was_waiting ? _kc_waited_to_string (buf_wait, wait_start_us) : ""); - return; - } - - if (wait_until_sigkill != 0) { - if (now >= wait_until_sigkill) { - /* Still not dead. SIGKILL now... */ - nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": sending SIGKILL", LOG_NAME_ARGS); - if (kill (pid, SIGKILL) != 0) { - errsv = errno; - /* ESRCH means, process does not exist or is already a zombie. */ - if (errsv != ESRCH) { - nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": process is gone or a zombie%s", - LOG_NAME_ARGS, _kc_waited_to_string (buf_wait, wait_start_us)); - } else { - nm_log_warn (LOGD_CORE | log_domain, LOG_NAME_PROCESS_FMT ": failed to send SIGKILL (after sending %s), %s (%d)%s", - LOG_NAME_ARGS, _kc_signal_to_string (sig), nm_strerror_native (errsv), errsv, - _kc_waited_to_string (buf_wait, wait_start_us)); - } - return; - } - sig = SIGKILL; - wait_until_sigkill = 0; - loop_count = 0; /* reset the loop_count. Now we really expect the process to die quickly. */ - } else - sleep_time = MIN (wait_until_sigkill - now, sleep_duration_usec); - } - - if (!was_waiting) { - if (wait_until_sigkill != 0) { - nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": waiting up to %ld milliseconds for process to disappear before sending KILL signal after sending %s...", - LOG_NAME_ARGS, (long) wait_before_kill_msec, _kc_signal_to_string (sig)); - } else if (max_wait_until != 0) { - nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": waiting up to %ld milliseconds for process to disappear after sending %s...", - LOG_NAME_ARGS, (long) max_wait_msec, _kc_signal_to_string (sig)); - } else { - nm_log_dbg (log_domain, LOG_NAME_PROCESS_FMT ": waiting for process to disappear after sending %s...", - LOG_NAME_ARGS, _kc_signal_to_string (sig)); - } - was_waiting = TRUE; - } - - if (loop_count < 20) { - /* At the beginning we expect the process to die fast. - * Limit the sleep time, the limit doubles with every iteration. */ - sleep_time = MIN (sleep_time, (((guint64) 1) << loop_count) * G_USEC_PER_SEC / 2000); - loop_count++; - } - g_usleep (sleep_time); - } + int errsv; + guint64 start_time0; + gint64 wait_until_sigkill, now, wait_start_us, max_wait_until; + gulong sleep_time, sleep_duration_usec; + int loop_count = 0; + gboolean was_waiting = FALSE; + char buf_wait[KC_WAITED_TO_STRING]; + char p_state; + + g_return_if_fail(pid > 0); + g_return_if_fail(log_name != NULL); + + start_time0 = nm_utils_get_start_time_for_pid(pid, &p_state, NULL); + if (start_time0 == 0) { + nm_log_dbg(log_domain, + LOG_NAME_PROCESS_FMT ": cannot kill process %ld because it seems already gone", + LOG_NAME_ARGS, + (long int) pid); + return; + } + if (start_time != 0 && start_time != start_time0) { + nm_log_dbg( + log_domain, + LOG_NAME_PROCESS_FMT + ": don't kill process %ld because the start_time is unexpectedly %lu instead of %ld", + LOG_NAME_ARGS, + (long int) pid, + (unsigned long) start_time0, + (unsigned long) start_time); + return; + } + + switch (p_state) { + case 'Z': + case 'x': + case 'X': + nm_log_dbg(log_domain, + LOG_NAME_PROCESS_FMT + ": cannot kill process %ld because it is already a zombie (%c)", + LOG_NAME_ARGS, + (long int) pid, + p_state); + return; + default: + break; + } + + if (kill(pid, sig) != 0) { + errsv = errno; + /* ESRCH means, process does not exist or is already a zombie. */ + if (errsv == ESRCH) { + nm_log_dbg(log_domain, + LOG_NAME_PROCESS_FMT ": failed to send %s because process seems gone", + LOG_NAME_ARGS, + _kc_signal_to_string(sig)); + } else { + nm_log_warn(LOGD_CORE | log_domain, + LOG_NAME_PROCESS_FMT ": failed to send %s: %s (%d)", + LOG_NAME_ARGS, + _kc_signal_to_string(sig), + nm_strerror_native(errsv), + errsv); + } + return; + } + + /* wait for the process to terminate... */ + + wait_start_us = nm_utils_get_monotonic_timestamp_usec(); + + sleep_duration_usec = _sleep_duration_convert_ms_to_us(sleep_duration_msec); + if (sig != SIGKILL && wait_before_kill_msec) + wait_until_sigkill = wait_start_us + (((gint64) wait_before_kill_msec) * 1000L); + else + wait_until_sigkill = 0; + if (max_wait_msec > 0) { + max_wait_until = wait_start_us + (((gint64) max_wait_msec) * 1000L); + if (wait_until_sigkill > 0 && wait_until_sigkill > max_wait_msec) + wait_until_sigkill = 0; + } else + max_wait_until = 0; + + while (TRUE) { + start_time = nm_utils_get_start_time_for_pid(pid, &p_state, NULL); + + if (start_time != start_time0) { + nm_log_dbg(log_domain, + LOG_NAME_PROCESS_FMT ": process is gone after sending signal %s%s", + LOG_NAME_ARGS, + _kc_signal_to_string(sig), + was_waiting ? _kc_waited_to_string(buf_wait, wait_start_us) : ""); + return; + } + switch (p_state) { + case 'Z': + case 'x': + case 'X': + nm_log_dbg(log_domain, + LOG_NAME_PROCESS_FMT ": process is a zombie (%c) after sending signal %s%s", + LOG_NAME_ARGS, + p_state, + _kc_signal_to_string(sig), + was_waiting ? _kc_waited_to_string(buf_wait, wait_start_us) : ""); + return; + default: + break; + } + + if (kill(pid, 0) != 0) { + errsv = errno; + /* ESRCH means, process does not exist or is already a zombie. */ + if (errsv == ESRCH) { + nm_log_dbg(log_domain, + LOG_NAME_PROCESS_FMT + ": process is gone or a zombie after sending signal %s%s", + LOG_NAME_ARGS, + _kc_signal_to_string(sig), + was_waiting ? _kc_waited_to_string(buf_wait, wait_start_us) : ""); + } else { + nm_log_warn(LOGD_CORE | log_domain, + LOG_NAME_PROCESS_FMT ": failed to kill(%ld, 0): %s (%d)%s", + LOG_NAME_ARGS, + (long int) pid, + nm_strerror_native(errsv), + errsv, + was_waiting ? _kc_waited_to_string(buf_wait, wait_start_us) : ""); + } + return; + } + + sleep_time = sleep_duration_usec; + now = nm_utils_get_monotonic_timestamp_usec(); + + if (max_wait_until != 0 && now >= max_wait_until) { + if (wait_until_sigkill != 0) { + /* wait_before_kill_msec is not larger then max_wait_until but we did not yet send + * SIGKILL. Although we already reached our timeout, we don't want to skip sending + * the signal. Even if we don't wait for the process to disappear. */ + nm_log_dbg(log_domain, LOG_NAME_PROCESS_FMT ": sending SIGKILL", LOG_NAME_ARGS); + kill(pid, SIGKILL); + } + nm_log_warn(log_domain, + LOG_NAME_PROCESS_FMT + ": timeout %u msec waiting for process to disappear (after sending %s)%s", + LOG_NAME_ARGS, + (unsigned) max_wait_until, + _kc_signal_to_string(sig), + was_waiting ? _kc_waited_to_string(buf_wait, wait_start_us) : ""); + return; + } + + if (wait_until_sigkill != 0) { + if (now >= wait_until_sigkill) { + /* Still not dead. SIGKILL now... */ + nm_log_dbg(log_domain, LOG_NAME_PROCESS_FMT ": sending SIGKILL", LOG_NAME_ARGS); + if (kill(pid, SIGKILL) != 0) { + errsv = errno; + /* ESRCH means, process does not exist or is already a zombie. */ + if (errsv != ESRCH) { + nm_log_dbg(log_domain, + LOG_NAME_PROCESS_FMT ": process is gone or a zombie%s", + LOG_NAME_ARGS, + _kc_waited_to_string(buf_wait, wait_start_us)); + } else { + nm_log_warn(LOGD_CORE | log_domain, + LOG_NAME_PROCESS_FMT + ": failed to send SIGKILL (after sending %s), %s (%d)%s", + LOG_NAME_ARGS, + _kc_signal_to_string(sig), + nm_strerror_native(errsv), + errsv, + _kc_waited_to_string(buf_wait, wait_start_us)); + } + return; + } + sig = SIGKILL; + wait_until_sigkill = 0; + loop_count = + 0; /* reset the loop_count. Now we really expect the process to die quickly. */ + } else + sleep_time = MIN(wait_until_sigkill - now, sleep_duration_usec); + } + + if (!was_waiting) { + if (wait_until_sigkill != 0) { + nm_log_dbg(log_domain, + LOG_NAME_PROCESS_FMT + ": waiting up to %ld milliseconds for process to disappear before " + "sending KILL signal after sending %s...", + LOG_NAME_ARGS, + (long) wait_before_kill_msec, + _kc_signal_to_string(sig)); + } else if (max_wait_until != 0) { + nm_log_dbg( + log_domain, + LOG_NAME_PROCESS_FMT + ": waiting up to %ld milliseconds for process to disappear after sending %s...", + LOG_NAME_ARGS, + (long) max_wait_msec, + _kc_signal_to_string(sig)); + } else { + nm_log_dbg(log_domain, + LOG_NAME_PROCESS_FMT + ": waiting for process to disappear after sending %s...", + LOG_NAME_ARGS, + _kc_signal_to_string(sig)); + } + was_waiting = TRUE; + } + + if (loop_count < 20) { + /* At the beginning we expect the process to die fast. + * Limit the sleep time, the limit doubles with every iteration. */ + sleep_time = MIN(sleep_time, (((guint64) 1) << loop_count) * G_USEC_PER_SEC / 2000); + loop_count++; + } + g_usleep(sleep_time); + } } #undef LOG_NAME_FMT #undef LOG_NAME_PROCESS_FMT #undef LOG_NAME_ARGS const char *const NM_PATHS_DEFAULT[] = { - PREFIX "/sbin/", - PREFIX "/bin/", - "/usr/local/sbin/", - "/sbin/", - "/usr/sbin/", - "/usr/local/bin/", - "/bin/", - "/usr/bin/", - NULL, + PREFIX "/sbin/", + PREFIX "/bin/", + "/usr/local/sbin/", + "/sbin/", + "/usr/sbin/", + "/usr/local/bin/", + "/bin/", + "/usr/bin/", + NULL, }; const char * -nm_utils_find_helper (const char *progname, const char *try_first, GError **error) +nm_utils_find_helper(const char *progname, const char *try_first, GError **error) { - return nm_utils_file_search_in_paths (progname, try_first, NM_PATHS_DEFAULT, G_FILE_TEST_IS_EXECUTABLE, NULL, NULL, error); + return nm_utils_file_search_in_paths(progname, + try_first, + NM_PATHS_DEFAULT, + G_FILE_TEST_IS_EXECUTABLE, + NULL, + NULL, + error); } /*****************************************************************************/ @@ -1019,496 +1226,493 @@ nm_utils_find_helper (const char *progname, const char *try_first, GError **erro * and returns the result as absolute path. **/ char * -nm_utils_read_link_absolute (const char *link_file, GError **error) +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); - if (!g_path_is_absolute (dirname)) { - gs_free char *current_dir = g_get_current_dir (); - - /* @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); - g_free (dirname); - g_free (ln); - return ln_abs; + 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); + if (!g_path_is_absolute(dirname)) { + gs_free char *current_dir = g_get_current_dir(); + + /* @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); + g_free(dirname); + g_free(ln); + return ln_abs; } /*****************************************************************************/ -#define DEVICE_TYPE_TAG "type:" -#define DRIVER_TAG "driver:" -#define DHCP_PLUGIN_TAG "dhcp-plugin:" -#define EXCEPT_TAG "except:" -#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:" +#define DEVICE_TYPE_TAG "type:" +#define DRIVER_TAG "driver:" +#define DHCP_PLUGIN_TAG "dhcp-plugin:" +#define EXCEPT_TAG "except:" +#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:" typedef struct { - const char *interface_name; - const char *device_type; - const char *driver; - const char *driver_version; - const char *dhcp_plugin; - struct { - const char *value; - gboolean is_parsed; - guint len; - guint8 bin[NM_UTILS_HWADDR_LEN_MAX]; - } hwaddr; - struct { - const char *value; - gboolean is_parsed; - guint32 a; - guint32 b; - guint32 c; - } s390_subchannels; + const char *interface_name; + const char *device_type; + const char *driver; + const char *driver_version; + const char *dhcp_plugin; + struct { + const char *value; + gboolean is_parsed; + guint len; + guint8 bin[NM_UTILS_HWADDR_LEN_MAX]; + } hwaddr; + struct { + const char *value; + gboolean is_parsed; + guint32 a; + guint32 b; + guint32 c; + } s390_subchannels; } MatchDeviceData; static gboolean -match_device_s390_subchannels_parse (const char *s390_subchannels, guint32 *out_a, guint32 *out_b, guint32 *out_c) +match_device_s390_subchannels_parse(const char *s390_subchannels, + guint32 * out_a, + guint32 * out_b, + guint32 * out_c) { - char buf[30 + 1]; - const int BUFSIZE = G_N_ELEMENTS (buf) - 1; - guint i = 0; - char *pa = NULL, *pb = NULL, *pc = NULL; - gint64 a, b, c; - - nm_assert (s390_subchannels); - nm_assert (out_a); - nm_assert (out_b); - nm_assert (out_c); - - if (!g_ascii_isxdigit (s390_subchannels[0])) - return FALSE; - - /* Get the first channel */ - for (i = 0; s390_subchannels[i]; i++) { - char ch = s390_subchannels[i]; - - if (!g_ascii_isxdigit (ch) && ch != '.') { - if (ch == ',') { - /* FIXME: currently we consider the first channel and ignore - * everything after the first ',' separator. Maybe we should - * validate all present channels? */ - break; - } - return FALSE; /* Invalid chars */ - } - if (i >= BUFSIZE) - return FALSE; /* Too long to be a subchannel */ - buf[i] = ch; - } - buf[i] = '\0'; - - /* and grab each of its elements, there should be 3 */ - pa = &buf[0]; - pb = strchr (pa, '.'); - if (pb) - pc = strchr (pb + 1, '.'); - if (!pb || !pc) - return FALSE; - *pb++ = '\0'; - *pc++ = '\0'; - - a = _nm_utils_ascii_str_to_int64 (pa, 16, 0, G_MAXUINT32, -1); - if (a == -1) - return FALSE; - b = _nm_utils_ascii_str_to_int64 (pb, 16, 0, G_MAXUINT32, -1); - if (b == -1) - return FALSE; - c = _nm_utils_ascii_str_to_int64 (pc, 16, 0, G_MAXUINT32, -1); - if (c == -1) - return FALSE; - - *out_a = (guint32) a; - *out_b = (guint32) b; - *out_c = (guint32) c; - return TRUE; + char buf[30 + 1]; + const int BUFSIZE = G_N_ELEMENTS(buf) - 1; + guint i = 0; + char * pa = NULL, *pb = NULL, *pc = NULL; + gint64 a, b, c; + + nm_assert(s390_subchannels); + nm_assert(out_a); + nm_assert(out_b); + nm_assert(out_c); + + if (!g_ascii_isxdigit(s390_subchannels[0])) + return FALSE; + + /* Get the first channel */ + for (i = 0; s390_subchannels[i]; i++) { + char ch = s390_subchannels[i]; + + if (!g_ascii_isxdigit(ch) && ch != '.') { + if (ch == ',') { + /* FIXME: currently we consider the first channel and ignore + * everything after the first ',' separator. Maybe we should + * validate all present channels? */ + break; + } + return FALSE; /* Invalid chars */ + } + if (i >= BUFSIZE) + return FALSE; /* Too long to be a subchannel */ + buf[i] = ch; + } + buf[i] = '\0'; + + /* and grab each of its elements, there should be 3 */ + pa = &buf[0]; + pb = strchr(pa, '.'); + if (pb) + pc = strchr(pb + 1, '.'); + if (!pb || !pc) + return FALSE; + *pb++ = '\0'; + *pc++ = '\0'; + + a = _nm_utils_ascii_str_to_int64(pa, 16, 0, G_MAXUINT32, -1); + if (a == -1) + return FALSE; + b = _nm_utils_ascii_str_to_int64(pb, 16, 0, G_MAXUINT32, -1); + if (b == -1) + return FALSE; + c = _nm_utils_ascii_str_to_int64(pc, 16, 0, G_MAXUINT32, -1); + if (c == -1) + return FALSE; + + *out_a = (guint32) a; + *out_b = (guint32) b; + *out_c = (guint32) c; + return TRUE; } static gboolean -match_data_s390_subchannels_eval (const char *spec_str, - MatchDeviceData *match_data) +match_data_s390_subchannels_eval(const char *spec_str, MatchDeviceData *match_data) { - guint32 a, b, c; - - if (G_UNLIKELY (!match_data->s390_subchannels.is_parsed)) { - match_data->s390_subchannels.is_parsed = TRUE; - - 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) - return FALSE; - - 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; + guint32 a, b, c; + + if (G_UNLIKELY(!match_data->s390_subchannels.is_parsed)) { + match_data->s390_subchannels.is_parsed = TRUE; + + 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) + return FALSE; + + 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; } static gboolean -match_device_hwaddr_eval (const char *spec_str, - MatchDeviceData *match_data) +match_device_hwaddr_eval(const char *spec_str, MatchDeviceData *match_data) { - if (G_UNLIKELY (!match_data->hwaddr.is_parsed)) { - match_data->hwaddr.is_parsed = TRUE; - - if (match_data->hwaddr.value) { - gsize l; - - if (!_nm_utils_hwaddr_aton (match_data->hwaddr.value, match_data->hwaddr.bin, sizeof (match_data->hwaddr.bin), &l)) - g_return_val_if_reached (FALSE); - match_data->hwaddr.len = l; - } else - return FALSE; - } else if (!match_data->hwaddr.len) - return FALSE; - - return nm_utils_hwaddr_matches (spec_str, -1, match_data->hwaddr.bin, match_data->hwaddr.len); + if (G_UNLIKELY(!match_data->hwaddr.is_parsed)) { + match_data->hwaddr.is_parsed = TRUE; + + if (match_data->hwaddr.value) { + gsize l; + + if (!_nm_utils_hwaddr_aton(match_data->hwaddr.value, + match_data->hwaddr.bin, + sizeof(match_data->hwaddr.bin), + &l)) + g_return_val_if_reached(FALSE); + match_data->hwaddr.len = l; + } else + return FALSE; + } else if (!match_data->hwaddr.len) + return FALSE; + + return nm_utils_hwaddr_matches(spec_str, -1, match_data->hwaddr.bin, match_data->hwaddr.len); } -#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; \ - }) +#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; \ + }) static NMMatchSpecMatchType -_match_result (gboolean has_except, - gboolean has_not_except, - gboolean has_match, - gboolean has_match_except) +_match_result(gboolean has_except, + gboolean has_not_except, + gboolean has_match, + gboolean has_match_except) { - if ( has_except - && !has_not_except) { - /* a match spec that only consists of a list of except matches is treated specially. */ - nm_assert (!has_match); - if (has_match_except) { - /* one of the "except:" matches matched. The result is an explicit - * negative match. */ - return NM_MATCH_SPEC_NEG_MATCH; - } else { - /* none of the "except:" matches matched. The result is a positive match, - * despite there being no positive match. */ - return NM_MATCH_SPEC_MATCH; - } - } - - if (has_match_except) - return NM_MATCH_SPEC_NEG_MATCH; - if (has_match) - return NM_MATCH_SPEC_MATCH; - return NM_MATCH_SPEC_NO_MATCH; + if (has_except && !has_not_except) { + /* a match spec that only consists of a list of except matches is treated specially. */ + nm_assert(!has_match); + if (has_match_except) { + /* one of the "except:" matches matched. The result is an explicit + * negative match. */ + return NM_MATCH_SPEC_NEG_MATCH; + } else { + /* none of the "except:" matches matched. The result is a positive match, + * despite there being no positive match. */ + return NM_MATCH_SPEC_MATCH; + } + } + + if (has_match_except) + return NM_MATCH_SPEC_NEG_MATCH; + if (has_match) + return NM_MATCH_SPEC_MATCH; + return NM_MATCH_SPEC_NO_MATCH; } static const char * -match_except (const char *spec_str, gboolean *out_except) +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; + 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) +match_device_eval(const char *spec_str, gboolean allow_fuzzy, MatchDeviceData *match_data) { - if (spec_str[0] == '*' && spec_str[1] == '\0') - return TRUE; - - if (_MATCH_CHECK (spec_str, DEVICE_TYPE_TAG)) { - return match_data->device_type - && nm_streq (spec_str, match_data->device_type); - } - - if (_MATCH_CHECK (spec_str, NM_MATCH_SPEC_MAC_TAG)) - return match_device_hwaddr_eval (spec_str, match_data); - - if (_MATCH_CHECK (spec_str, NM_MATCH_SPEC_INTERFACE_NAME_TAG)) { - gboolean use_pattern = FALSE; - - if (spec_str[0] == '=') - spec_str += 1; - else { - if (spec_str[0] == '~') - spec_str += 1; - use_pattern = TRUE; - } - - if (match_data->interface_name) { - if (nm_streq (spec_str, match_data->interface_name)) - return TRUE; - if (use_pattern && g_pattern_match_simple (spec_str, match_data->interface_name)) - return TRUE; - } - return FALSE; - } - - if (_MATCH_CHECK (spec_str, DRIVER_TAG)) { - const char *t; - - if (!match_data->driver) - return FALSE; - - /* support: - * 1) "${DRIVER}" - * In this case, DRIVER may not contain a '/' character. - * It matches any driver version. - * 2) "${DRIVER}/${DRIVER_VERSION}" - * In this case, DRIVER may contains '/' but DRIVER_VERSION - * may not. A '/' in DRIVER_VERSION may be replaced by '?'. - * - * It follows, that "${DRIVER}/""*" is like 1), but allows - * '/' inside DRIVER. - * - * The fields match to what `nmcli -f GENERAL.DRIVER,GENERAL.DRIVER-VERSION device show` - * gives. However, DRIVER matches literally, while DRIVER_VERSION is a glob - * supporting ? and *. - */ - - t = strrchr (spec_str, '/'); - - if (!t) - return nm_streq (spec_str, match_data->driver); - - return (strncmp (spec_str, match_data->driver, t - spec_str) == 0) - && g_pattern_match_simple (&t[1], - match_data->driver_version ?: ""); - } - - if (_MATCH_CHECK (spec_str, NM_MATCH_SPEC_S390_SUBCHANNELS_TAG)) - return match_data_s390_subchannels_eval (spec_str, match_data); - - if (_MATCH_CHECK (spec_str, DHCP_PLUGIN_TAG)) - return nm_streq0 (spec_str, match_data->dhcp_plugin); - - if (allow_fuzzy) { - if (match_device_hwaddr_eval (spec_str, match_data)) - return TRUE; - if ( match_data->interface_name - && nm_streq (spec_str, match_data->interface_name)) - return TRUE; - } - - return FALSE; + if (spec_str[0] == '*' && spec_str[1] == '\0') + return TRUE; + + if (_MATCH_CHECK(spec_str, DEVICE_TYPE_TAG)) { + return match_data->device_type && nm_streq(spec_str, match_data->device_type); + } + + if (_MATCH_CHECK(spec_str, NM_MATCH_SPEC_MAC_TAG)) + return match_device_hwaddr_eval(spec_str, match_data); + + if (_MATCH_CHECK(spec_str, NM_MATCH_SPEC_INTERFACE_NAME_TAG)) { + gboolean use_pattern = FALSE; + + if (spec_str[0] == '=') + spec_str += 1; + else { + if (spec_str[0] == '~') + spec_str += 1; + use_pattern = TRUE; + } + + if (match_data->interface_name) { + if (nm_streq(spec_str, match_data->interface_name)) + return TRUE; + if (use_pattern && g_pattern_match_simple(spec_str, match_data->interface_name)) + return TRUE; + } + return FALSE; + } + + if (_MATCH_CHECK(spec_str, DRIVER_TAG)) { + const char *t; + + if (!match_data->driver) + return FALSE; + + /* support: + * 1) "${DRIVER}" + * In this case, DRIVER may not contain a '/' character. + * It matches any driver version. + * 2) "${DRIVER}/${DRIVER_VERSION}" + * In this case, DRIVER may contains '/' but DRIVER_VERSION + * may not. A '/' in DRIVER_VERSION may be replaced by '?'. + * + * It follows, that "${DRIVER}/""*" is like 1), but allows + * '/' inside DRIVER. + * + * The fields match to what `nmcli -f GENERAL.DRIVER,GENERAL.DRIVER-VERSION device show` + * gives. However, DRIVER matches literally, while DRIVER_VERSION is a glob + * supporting ? and *. + */ + + t = strrchr(spec_str, '/'); + + if (!t) + return nm_streq(spec_str, match_data->driver); + + return (strncmp(spec_str, match_data->driver, t - spec_str) == 0) + && g_pattern_match_simple(&t[1], match_data->driver_version ?: ""); + } + + if (_MATCH_CHECK(spec_str, NM_MATCH_SPEC_S390_SUBCHANNELS_TAG)) + return match_data_s390_subchannels_eval(spec_str, match_data); + + if (_MATCH_CHECK(spec_str, DHCP_PLUGIN_TAG)) + return nm_streq0(spec_str, match_data->dhcp_plugin); + + if (allow_fuzzy) { + if (match_device_hwaddr_eval(spec_str, match_data)) + return TRUE; + if (match_data->interface_name && nm_streq(spec_str, match_data->interface_name)) + return TRUE; + } + + return FALSE; } NMMatchSpecMatchType -nm_match_spec_device (const GSList *specs, - 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) +nm_match_spec_device(const GSList *specs, + 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) { - const GSList *iter; - gboolean has_match = FALSE; - gboolean has_match_except = FALSE; - gboolean has_except = FALSE; - gboolean has_not_except = FALSE; - const char *spec_str; - MatchDeviceData match_data = { - .interface_name = interface_name, - .device_type = nm_str_not_empty (device_type), - .driver = nm_str_not_empty (driver), - .driver_version = nm_str_not_empty (driver_version), - .dhcp_plugin = nm_str_not_empty (dhcp_plugin), - .hwaddr = { - .value = hwaddr, - }, - .s390_subchannels = { - .value = s390_subchannels, - }, - }; - - nm_assert (!hwaddr || nm_utils_hwaddr_valid (hwaddr, -1)); - - 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_device_eval (spec_str, - !except, - &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); + const GSList * iter; + gboolean has_match = FALSE; + gboolean has_match_except = FALSE; + gboolean has_except = FALSE; + gboolean has_not_except = FALSE; + const char * spec_str; + MatchDeviceData match_data = { + .interface_name = interface_name, + .device_type = nm_str_not_empty(device_type), + .driver = nm_str_not_empty(driver), + .driver_version = nm_str_not_empty(driver_version), + .dhcp_plugin = nm_str_not_empty(dhcp_plugin), + .hwaddr = + { + .value = hwaddr, + }, + .s390_subchannels = + { + .value = s390_subchannels, + }, + }; + + nm_assert(!hwaddr || nm_utils_hwaddr_valid(hwaddr, -1)); + + 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_device_eval(spec_str, !except, &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); } static gboolean -match_config_eval (const char *str, const char *tag, guint cur_nm_version) +match_config_eval(const char *str, const char *tag, guint cur_nm_version) { - gs_free char *s_ver = NULL; - gs_strfreev char **s_ver_tokens = NULL; - int v_maj = -1, v_min = -1, v_mic = -1; - guint c_maj = -1, c_min = -1, c_mic = -1; - guint n_tokens; - - s_ver = g_strdup (str); - g_strstrip (s_ver); - - /* Let's be strict with the accepted format here. No funny stuff!! */ - - if (s_ver[strspn (s_ver, ".0123456789")] != '\0') - return FALSE; - - s_ver_tokens = g_strsplit (s_ver, ".", -1); - n_tokens = g_strv_length (s_ver_tokens); - if (n_tokens == 0 || n_tokens > 3) - return FALSE; - - v_maj = _nm_utils_ascii_str_to_int64 (s_ver_tokens[0], 10, 0, 0xFFFF, -1); - if (v_maj < 0) - return FALSE; - if (n_tokens >= 2) { - v_min = _nm_utils_ascii_str_to_int64 (s_ver_tokens[1], 10, 0, 0xFF, -1); - if (v_min < 0) - return FALSE; - } - if (n_tokens >= 3) { - v_mic = _nm_utils_ascii_str_to_int64 (s_ver_tokens[2], 10, 0, 0xFF, -1); - if (v_mic < 0) - return FALSE; - } - - nm_decode_version (cur_nm_version, &c_maj, &c_min, &c_mic); - -#define CHECK_AND_RETURN_FALSE(cur, val, tag, is_last_digit) \ - G_STMT_START { \ - if (!strcmp (tag, MATCH_TAG_CONFIG_NM_VERSION_MIN)) { \ - if (cur < val) \ - return FALSE; \ - } else if (!strcmp (tag, MATCH_TAG_CONFIG_NM_VERSION_MAX)) { \ - if (cur > val) \ - return FALSE; \ - } else { \ - if (cur != val) \ - return FALSE; \ - } \ - if (!(is_last_digit)) { \ - if (cur != val) \ - return FALSE; \ - } \ - } G_STMT_END - if (v_mic >= 0) - CHECK_AND_RETURN_FALSE (c_mic, v_mic, tag, TRUE); - if (v_min >= 0) - CHECK_AND_RETURN_FALSE (c_min, v_min, tag, v_mic < 0); - CHECK_AND_RETURN_FALSE (c_maj, v_maj, tag, v_min < 0); - return TRUE; + gs_free char * s_ver = NULL; + gs_strfreev char **s_ver_tokens = NULL; + int v_maj = -1, v_min = -1, v_mic = -1; + guint c_maj = -1, c_min = -1, c_mic = -1; + guint n_tokens; + + s_ver = g_strdup(str); + g_strstrip(s_ver); + + /* Let's be strict with the accepted format here. No funny stuff!! */ + + if (s_ver[strspn(s_ver, ".0123456789")] != '\0') + return FALSE; + + s_ver_tokens = g_strsplit(s_ver, ".", -1); + n_tokens = g_strv_length(s_ver_tokens); + if (n_tokens == 0 || n_tokens > 3) + return FALSE; + + v_maj = _nm_utils_ascii_str_to_int64(s_ver_tokens[0], 10, 0, 0xFFFF, -1); + if (v_maj < 0) + return FALSE; + if (n_tokens >= 2) { + v_min = _nm_utils_ascii_str_to_int64(s_ver_tokens[1], 10, 0, 0xFF, -1); + if (v_min < 0) + return FALSE; + } + if (n_tokens >= 3) { + v_mic = _nm_utils_ascii_str_to_int64(s_ver_tokens[2], 10, 0, 0xFF, -1); + if (v_mic < 0) + return FALSE; + } + + nm_decode_version(cur_nm_version, &c_maj, &c_min, &c_mic); + +#define CHECK_AND_RETURN_FALSE(cur, val, tag, is_last_digit) \ + G_STMT_START \ + { \ + if (!strcmp(tag, MATCH_TAG_CONFIG_NM_VERSION_MIN)) { \ + if (cur < val) \ + return FALSE; \ + } else if (!strcmp(tag, MATCH_TAG_CONFIG_NM_VERSION_MAX)) { \ + if (cur > val) \ + return FALSE; \ + } else { \ + if (cur != val) \ + return FALSE; \ + } \ + if (!(is_last_digit)) { \ + if (cur != val) \ + return FALSE; \ + } \ + } \ + G_STMT_END + if (v_mic >= 0) + CHECK_AND_RETURN_FALSE(c_mic, v_mic, tag, TRUE); + if (v_min >= 0) + CHECK_AND_RETURN_FALSE(c_min, v_min, tag, v_mic < 0); + CHECK_AND_RETURN_FALSE(c_maj, v_maj, tag, v_min < 0); + return TRUE; } NMMatchSpecMatchType -nm_match_spec_config (const GSList *specs, guint cur_nm_version, const char *env) +nm_match_spec_config(const GSList *specs, guint cur_nm_version, const char *env) { - const GSList *iter; - gboolean has_match = FALSE; - gboolean has_match_except = FALSE; - gboolean has_except = FALSE; - gboolean has_not_except = FALSE; - - if (!specs) - return NM_MATCH_SPEC_NO_MATCH; - - for (iter = specs; iter; iter = g_slist_next (iter)) { - const char *spec_str = iter->data; - gboolean except; - gboolean v_match; - - if (!spec_str || !*spec_str) - continue; - - 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_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)) - v_match = env && env[0] && !strcmp (spec_str, env); - else - v_match = FALSE; - - if (!v_match) - continue; - - if (except) - has_match_except = TRUE; - else - has_match = TRUE; - } - - return _match_result (has_except, has_not_except, has_match, has_match_except); + const GSList *iter; + gboolean has_match = FALSE; + gboolean has_match_except = FALSE; + gboolean has_except = FALSE; + gboolean has_not_except = FALSE; + + if (!specs) + return NM_MATCH_SPEC_NO_MATCH; + + for (iter = specs; iter; iter = g_slist_next(iter)) { + const char *spec_str = iter->data; + gboolean except; + gboolean v_match; + + if (!spec_str || !*spec_str) + continue; + + 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_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)) + v_match = env && env[0] && !strcmp(spec_str, env); + else + v_match = FALSE; + + if (!v_match) + continue; + + if (except) + has_match_except = TRUE; + else + has_match = TRUE; + } + + return _match_result(has_except, has_not_except, has_match, has_match_except); } #undef _MATCH_CHECK @@ -1534,89 +1738,89 @@ nm_match_spec_config (const GSList *specs, guint cur_nm_version, const char *env * Returns: (transfer full): the list of device specs. */ GSList * -nm_match_spec_split (const char *value) +nm_match_spec_split(const char *value) { - char *string_value, *p, *q0, *q; - GSList *pieces = NULL; - int trailing_ws; - - if (!value || !*value) - return NULL; - - /* Copied from glibs g_key_file_parse_value_as_string() function - * and adjusted. */ - - string_value = g_new (char, strlen (value) + 1); - - p = (char *) value; - - /* skip over leading whitespace */ - while (g_ascii_isspace (*p)) - p++; - - q0 = q = string_value; - trailing_ws = 0; - while (*p) { - if (*p == '\\') { - p++; - - switch (*p) { - case 's': - *q = ' '; - break; - case 'n': - *q = '\n'; - break; - case 't': - *q = '\t'; - break; - case 'r': - *q = '\r'; - break; - case '\\': - *q = '\\'; - break; - case '\0': - break; - default: - if (NM_IN_SET (*p, ',', ';')) - *q = *p; - else { - *q++ = '\\'; - *q = *p; - } - break; - } - if (*p == '\0') - break; - p++; - trailing_ws = 0; - } else { - *q = *p; - if (*p == '\0') - break; - if (g_ascii_isspace (*p)) { - trailing_ws++; - p++; - } else if (NM_IN_SET (*p, ',', ';')) { - if (q0 < q - trailing_ws) - pieces = g_slist_prepend (pieces, g_strndup (q0, (q - q0) - trailing_ws)); - q0 = q + 1; - p++; - trailing_ws = 0; - while (g_ascii_isspace (*p)) - p++; - } else - p++; - } - q++; - } - - *q = '\0'; - if (q0 < q - trailing_ws) - pieces = g_slist_prepend (pieces, g_strndup (q0, (q - q0) - trailing_ws)); - g_free (string_value); - return g_slist_reverse (pieces); + char * string_value, *p, *q0, *q; + GSList *pieces = NULL; + int trailing_ws; + + if (!value || !*value) + return NULL; + + /* Copied from glibs g_key_file_parse_value_as_string() function + * and adjusted. */ + + string_value = g_new(char, strlen(value) + 1); + + p = (char *) value; + + /* skip over leading whitespace */ + while (g_ascii_isspace(*p)) + p++; + + q0 = q = string_value; + trailing_ws = 0; + while (*p) { + if (*p == '\\') { + p++; + + switch (*p) { + case 's': + *q = ' '; + break; + case 'n': + *q = '\n'; + break; + case 't': + *q = '\t'; + break; + case 'r': + *q = '\r'; + break; + case '\\': + *q = '\\'; + break; + case '\0': + break; + default: + if (NM_IN_SET(*p, ',', ';')) + *q = *p; + else { + *q++ = '\\'; + *q = *p; + } + break; + } + if (*p == '\0') + break; + p++; + trailing_ws = 0; + } else { + *q = *p; + if (*p == '\0') + break; + if (g_ascii_isspace(*p)) { + trailing_ws++; + p++; + } else if (NM_IN_SET(*p, ',', ';')) { + if (q0 < q - trailing_ws) + pieces = g_slist_prepend(pieces, g_strndup(q0, (q - q0) - trailing_ws)); + q0 = q + 1; + p++; + trailing_ws = 0; + while (g_ascii_isspace(*p)) + p++; + } else + p++; + } + q++; + } + + *q = '\0'; + if (q0 < q - trailing_ws) + pieces = g_slist_prepend(pieces, g_strndup(q0, (q - q0) - trailing_ws)); + g_free(string_value); + return g_slist_reverse(pieces); } /** @@ -1633,258 +1837,253 @@ nm_match_spec_split (const char *value) * ways to encode the same joined specs string). */ char * -nm_match_spec_join (GSList *specs) +nm_match_spec_join(GSList *specs) { - const char *p; - GString *str; - - str = g_string_new (""); - - for (; specs; specs = specs->next) { - p = specs->data; - - if (!p || !*p) - continue; - - if (str->len > 0) - g_string_append_c (str, ','); - - /* escape leading whitespace */ - switch (*p) { - case ' ': - g_string_append (str, "\\s"); - p++; - break; - case '\t': - g_string_append (str, "\\t"); - p++; - break; - } - - for (; *p; p++) { - switch (*p) { - case '\n': - g_string_append (str, "\\n"); - break; - case '\r': - g_string_append (str, "\\r"); - break; - case '\\': - g_string_append (str, "\\\\"); - break; - case ',': - g_string_append (str, "\\,"); - break; - case ';': - g_string_append (str, "\\;"); - break; - default: - g_string_append_c (str, *p); - break; - } - } - - /* escape trailing whitespaces */ - switch (str->str[str->len - 1]) { - case ' ': - g_string_overwrite (str, str->len - 1, "\\s"); - break; - case '\t': - g_string_overwrite (str, str->len - 1, "\\t"); - break; - } - } - - return g_string_free (str, FALSE); + const char *p; + GString * str; + + str = g_string_new(""); + + for (; specs; specs = specs->next) { + p = specs->data; + + if (!p || !*p) + continue; + + if (str->len > 0) + g_string_append_c(str, ','); + + /* escape leading whitespace */ + switch (*p) { + case ' ': + g_string_append(str, "\\s"); + p++; + break; + case '\t': + g_string_append(str, "\\t"); + p++; + break; + } + + for (; *p; p++) { + switch (*p) { + case '\n': + g_string_append(str, "\\n"); + break; + case '\r': + g_string_append(str, "\\r"); + break; + case '\\': + g_string_append(str, "\\\\"); + break; + case ',': + g_string_append(str, "\\,"); + break; + case ';': + g_string_append(str, "\\;"); + break; + default: + g_string_append_c(str, *p); + break; + } + } + + /* escape trailing whitespaces */ + switch (str->str[str->len - 1]) { + case ' ': + g_string_overwrite(str, str->len - 1, "\\s"); + break; + case '\t': + g_string_overwrite(str, str->len - 1, "\\t"); + break; + } + } + + return g_string_free(str, FALSE); } static void -_pattern_parse (const char *input, - const char **out_pattern, - gboolean *out_is_inverted, - gboolean *out_is_mandatory) +_pattern_parse(const char * input, + const char **out_pattern, + gboolean * out_is_inverted, + gboolean * out_is_mandatory) { - gboolean is_inverted = FALSE; - gboolean is_mandatory = FALSE; - - if (input[0] == '&') { - input++; - is_mandatory = TRUE; - if (input[0] == '!') { - input++; - is_inverted = TRUE; - } - goto out; - } - - if (input[0] == '|') { - input++; - if (input[0] == '!') { - input++; - is_inverted = TRUE; - } - goto out; - } - - if (input[0] == '!') { - input++; - is_inverted = TRUE; - is_mandatory = TRUE; - goto out; - } + gboolean is_inverted = FALSE; + gboolean is_mandatory = FALSE; + + if (input[0] == '&') { + input++; + is_mandatory = TRUE; + if (input[0] == '!') { + input++; + is_inverted = TRUE; + } + goto out; + } + + if (input[0] == '|') { + input++; + if (input[0] == '!') { + input++; + is_inverted = TRUE; + } + goto out; + } + + if (input[0] == '!') { + input++; + is_inverted = TRUE; + is_mandatory = TRUE; + goto out; + } out: - if (input[0] == '\\') - input++; + if (input[0] == '\\') + input++; - *out_pattern = input; - *out_is_inverted = is_inverted; - *out_is_mandatory = is_mandatory; + *out_pattern = input; + *out_is_inverted = is_inverted; + *out_is_mandatory = is_mandatory; } gboolean -nm_wildcard_match_check (const char *str, - const char *const *patterns, - guint num_patterns) +nm_wildcard_match_check(const char *str, const char *const *patterns, guint num_patterns) { - gboolean has_optional = FALSE; - gboolean has_any_optional = FALSE; - guint i; - - for (i = 0; i < num_patterns; i++) { - gboolean is_inverted; - gboolean is_mandatory; - gboolean match; - const char *p; - - _pattern_parse (patterns[i], &p, &is_inverted, &is_mandatory); - - match = (fnmatch (p, str, 0) == 0); - if (is_inverted) - match = !match; - - if (is_mandatory) { - if (!match) - return FALSE; - } else { - has_any_optional = TRUE; - if (match) - has_optional = TRUE; - } - } - - return has_optional - || !has_any_optional; + gboolean has_optional = FALSE; + gboolean has_any_optional = FALSE; + guint i; + + for (i = 0; i < num_patterns; i++) { + gboolean is_inverted; + gboolean is_mandatory; + gboolean match; + const char *p; + + _pattern_parse(patterns[i], &p, &is_inverted, &is_mandatory); + + match = (fnmatch(p, str, 0) == 0); + if (is_inverted) + match = !match; + + if (is_mandatory) { + if (!match) + return FALSE; + } else { + has_any_optional = TRUE; + if (match) + has_optional = TRUE; + } + } + + return has_optional || !has_any_optional; } /*****************************************************************************/ static gboolean -_kernel_cmdline_match (const char *const*proc_cmdline, - const char *pattern) +_kernel_cmdline_match(const char *const *proc_cmdline, const char *pattern) { - - if (proc_cmdline) { - gboolean has_equal = (!!strchr (pattern, '=')); - gsize pattern_len = strlen (pattern); - - for (; proc_cmdline[0]; proc_cmdline++) { - const char *c = proc_cmdline[0]; - - if (has_equal) { - /* if pattern contains '=' compare full key=value */ - if (nm_streq (c, pattern)) - return TRUE; - continue; - } - - /* otherwise consider pattern as key only */ - if ( strncmp (c, pattern, pattern_len) == 0 - && NM_IN_SET (c[pattern_len], '\0', '=')) - return TRUE; - } - } - - return FALSE; + if (proc_cmdline) { + gboolean has_equal = (!!strchr(pattern, '=')); + gsize pattern_len = strlen(pattern); + + for (; proc_cmdline[0]; proc_cmdline++) { + const char *c = proc_cmdline[0]; + + if (has_equal) { + /* if pattern contains '=' compare full key=value */ + if (nm_streq(c, pattern)) + return TRUE; + continue; + } + + /* otherwise consider pattern as key only */ + if (strncmp(c, pattern, pattern_len) == 0 && NM_IN_SET(c[pattern_len], '\0', '=')) + return TRUE; + } + } + + return FALSE; } gboolean -nm_utils_kernel_cmdline_match_check (const char *const*proc_cmdline, - const char *const*patterns, - guint num_patterns, - GError **error) +nm_utils_kernel_cmdline_match_check(const char *const *proc_cmdline, + const char *const *patterns, + guint num_patterns, + GError ** error) { - gboolean has_optional = FALSE; - gboolean has_any_optional = FALSE; - guint i; - - for (i = 0; i < num_patterns; i++) { - const char *element = patterns[i]; - gboolean is_inverted = FALSE; - gboolean is_mandatory = FALSE; - gboolean match; - const char *p; - - _pattern_parse (element, &p, &is_inverted, &is_mandatory); - - match = _kernel_cmdline_match (proc_cmdline, p); - if (is_inverted) - match = !match; - - if (is_mandatory) { - if (!match) { - 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; - } - } else { - has_any_optional = TRUE; - if (match) - has_optional = TRUE; - } - } - - 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; - } - - return TRUE; + gboolean has_optional = FALSE; + gboolean has_any_optional = FALSE; + guint i; + + for (i = 0; i < num_patterns; i++) { + const char *element = patterns[i]; + gboolean is_inverted = FALSE; + gboolean is_mandatory = FALSE; + gboolean match; + const char *p; + + _pattern_parse(element, &p, &is_inverted, &is_mandatory); + + match = _kernel_cmdline_match(proc_cmdline, p); + if (is_inverted) + match = !match; + + if (is_mandatory) { + if (!match) { + 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; + } + } else { + has_any_optional = TRUE; + if (match) + has_optional = TRUE; + } + } + + 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; + } + + return TRUE; } /*****************************************************************************/ char * -nm_utils_new_vlan_name (const char *parent_iface, guint32 vlan_id) +nm_utils_new_vlan_name(const char *parent_iface, guint32 vlan_id) { - guint id_len; - gsize parent_len; - char *ifname; - - g_return_val_if_fail (parent_iface && *parent_iface, NULL); - - if (vlan_id < 10) - id_len = 2; - else if (vlan_id < 100) - id_len = 3; - else if (vlan_id < 1000) - id_len = 4; - else { - g_return_val_if_fail (vlan_id < 4095, NULL); - id_len = 5; - } - - ifname = g_new (char, IFNAMSIZ); - - parent_len = strlen (parent_iface); - parent_len = MIN (parent_len, IFNAMSIZ - 1 - id_len); - memcpy (ifname, parent_iface, parent_len); - g_snprintf (&ifname[parent_len], IFNAMSIZ - parent_len, ".%u", vlan_id); - - return ifname; + guint id_len; + gsize parent_len; + char *ifname; + + g_return_val_if_fail(parent_iface && *parent_iface, NULL); + + if (vlan_id < 10) + id_len = 2; + else if (vlan_id < 100) + id_len = 3; + else if (vlan_id < 1000) + id_len = 4; + else { + g_return_val_if_fail(vlan_id < 4095, NULL); + id_len = 5; + } + + ifname = g_new(char, IFNAMSIZ); + + parent_len = strlen(parent_iface); + parent_len = MIN(parent_len, IFNAMSIZ - 1 - id_len); + memcpy(ifname, parent_iface, parent_len); + g_snprintf(&ifname[parent_len], IFNAMSIZ - parent_len, ".%u", vlan_id); + + return ifname; } /* nm_utils_new_infiniband_name: @@ -1897,21 +2096,21 @@ nm_utils_new_vlan_name (const char *parent_iface, guint32 vlan_id) * is returned. */ const char * -nm_utils_new_infiniband_name (char *name, const char *parent_name, int p_key) +nm_utils_new_infiniband_name(char *name, const char *parent_name, int p_key) { - g_return_val_if_fail (name, NULL); - g_return_val_if_fail (parent_name && parent_name[0], NULL); - g_return_val_if_fail (strlen (parent_name) < IFNAMSIZ, NULL); - - /* technically, p_key of 0x0000 and 0x8000 is not allowed either. But we don't - * want to assert against that in nm_utils_new_infiniband_name(). So be more - * resilient here, and accept those. */ - g_return_val_if_fail (p_key >= 0 && p_key <= 0xffff, NULL); - - /* If parent+suffix is too long, kernel would just truncate - * the name. We do the same. See ipoib_vlan_add(). */ - g_snprintf (name, IFNAMSIZ, "%s.%04x", parent_name, p_key); - return name; + g_return_val_if_fail(name, NULL); + g_return_val_if_fail(parent_name && parent_name[0], NULL); + g_return_val_if_fail(strlen(parent_name) < IFNAMSIZ, NULL); + + /* technically, p_key of 0x0000 and 0x8000 is not allowed either. But we don't + * want to assert against that in nm_utils_new_infiniband_name(). So be more + * resilient here, and accept those. */ + g_return_val_if_fail(p_key >= 0 && p_key <= 0xffff, NULL); + + /* If parent+suffix is too long, kernel would just truncate + * the name. We do the same. See ipoib_vlan_add(). */ + g_snprintf(name, IFNAMSIZ, "%s.%04x", parent_name, p_key); + return name; } /*****************************************************************************/ @@ -1933,327 +2132,422 @@ nm_utils_new_infiniband_name (char *name, const char *parent_name, int p_key) * Returns: -1, 0, or 1 */ int -nm_utils_cmp_connection_by_autoconnect_priority (NMConnection *a, NMConnection *b) +nm_utils_cmp_connection_by_autoconnect_priority(NMConnection *a, NMConnection *b) { - NMSettingConnection *a_s_con; - NMSettingConnection *b_s_con; - int a_ap, b_ap; - gboolean can_autoconnect; - - if (a == b) - return 0; - if (!a) - return 1; - if (!b) - return -1; - - a_s_con = nm_connection_get_setting_connection (a); - b_s_con = nm_connection_get_setting_connection (b); - - if (!a_s_con) - return !b_s_con ? 0 : 1; - if (!b_s_con) - return -1; - - can_autoconnect = !!nm_setting_connection_get_autoconnect (a_s_con); - if (can_autoconnect != (!!nm_setting_connection_get_autoconnect (b_s_con))) - return can_autoconnect ? -1 : 1; - - 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; - } - - return 0; + NMSettingConnection *a_s_con; + NMSettingConnection *b_s_con; + int a_ap, b_ap; + gboolean can_autoconnect; + + if (a == b) + return 0; + if (!a) + return 1; + if (!b) + return -1; + + a_s_con = nm_connection_get_setting_connection(a); + b_s_con = nm_connection_get_setting_connection(b); + + if (!a_s_con) + return !b_s_con ? 0 : 1; + if (!b_s_con) + return -1; + + can_autoconnect = !!nm_setting_connection_get_autoconnect(a_s_con); + if (can_autoconnect != (!!nm_setting_connection_get_autoconnect(b_s_con))) + return can_autoconnect ? -1 : 1; + + 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; + } + + return 0; } /*****************************************************************************/ -typedef struct -{ - const char *name; - NMSetting *setting; - NMSetting *diff_base_setting; - GHashTable *setting_diff; +typedef struct { + const char *name; + NMSetting * setting; + NMSetting * diff_base_setting; + GHashTable *setting_diff; } LogConnectionSettingData; -typedef struct -{ - const char *item_name; - NMSettingDiffResult diff_result; +typedef struct { + const char * item_name; + NMSettingDiffResult diff_result; } LogConnectionSettingItem; static int -_log_connection_sort_hashes_fcn (gconstpointer a, gconstpointer b) +_log_connection_sort_hashes_fcn(gconstpointer a, gconstpointer b) { - const LogConnectionSettingData *v1 = a; - const LogConnectionSettingData *v2 = b; - NMSettingPriority p1, p2; - NMSetting *s1, *s2; + const LogConnectionSettingData *v1 = a; + const LogConnectionSettingData *v2 = b; + NMSettingPriority p1, p2; + NMSetting * s1, *s2; - s1 = v1->setting ?: v1->diff_base_setting; - s2 = v2->setting ?: v2->diff_base_setting; + s1 = v1->setting ?: v1->diff_base_setting; + s2 = v2->setting ?: v2->diff_base_setting; - g_assert (s1 && s2); + g_assert(s1 && s2); - p1 = _nm_setting_get_setting_priority (s1); - p2 = _nm_setting_get_setting_priority (s2); + p1 = _nm_setting_get_setting_priority(s1); + p2 = _nm_setting_get_setting_priority(s2); - if (p1 != p2) - return p1 > p2 ? 1 : -1; + if (p1 != p2) + return p1 > p2 ? 1 : -1; - return strcmp (v1->name, v2->name); + return strcmp(v1->name, v2->name); } static GArray * -_log_connection_sort_hashes (NMConnection *connection, NMConnection *diff_base, GHashTable *connection_diff) +_log_connection_sort_hashes(NMConnection *connection, + NMConnection *diff_base, + GHashTable * connection_diff) { - GHashTableIter iter; - GArray *sorted_hashes; - LogConnectionSettingData setting_data; - - sorted_hashes = g_array_sized_new (TRUE, FALSE, sizeof (LogConnectionSettingData), g_hash_table_size (connection_diff)); - - g_hash_table_iter_init (&iter, connection_diff); - while (g_hash_table_iter_next (&iter, (gpointer) &setting_data.name, (gpointer) &setting_data.setting_diff)) { - setting_data.setting = nm_connection_get_setting_by_name (connection, setting_data.name); - setting_data.diff_base_setting = diff_base ? nm_connection_get_setting_by_name (diff_base, setting_data.name) : NULL; - g_assert (setting_data.setting || setting_data.diff_base_setting); - g_array_append_val (sorted_hashes, setting_data); - } - - g_array_sort (sorted_hashes, _log_connection_sort_hashes_fcn); - return sorted_hashes; + GHashTableIter iter; + GArray * sorted_hashes; + LogConnectionSettingData setting_data; + + sorted_hashes = g_array_sized_new(TRUE, + FALSE, + sizeof(LogConnectionSettingData), + g_hash_table_size(connection_diff)); + + g_hash_table_iter_init(&iter, connection_diff); + while (g_hash_table_iter_next(&iter, + (gpointer) &setting_data.name, + (gpointer) &setting_data.setting_diff)) { + setting_data.setting = nm_connection_get_setting_by_name(connection, setting_data.name); + setting_data.diff_base_setting = + diff_base ? nm_connection_get_setting_by_name(diff_base, setting_data.name) : NULL; + g_assert(setting_data.setting || setting_data.diff_base_setting); + g_array_append_val(sorted_hashes, setting_data); + } + + g_array_sort(sorted_hashes, _log_connection_sort_hashes_fcn); + return sorted_hashes; } static int -_log_connection_sort_names_fcn (gconstpointer a, gconstpointer b) +_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); + 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) +_log_connection_get_property(NMSetting *setting, const char *name) { - GValue val = G_VALUE_INIT; - char *s; - - g_return_val_if_fail (setting, NULL); - - if ( !NM_IS_SETTING_VPN (setting) - && nm_setting_get_secret_flags (setting, name, NULL, NULL)) - return g_strdup ("****"); - - if (!_nm_setting_get_property (setting, name, &val)) - return g_strdup ("<unknown>"); - - if (G_VALUE_HOLDS_STRING (&val)) { - const char *val_s; - - val_s = g_value_get_string (&val); - if (!val_s) { - /* for NULL, we want to return the unquoted string "NULL". */ - s = g_strdup ("NULL"); - } else { - char *escaped = g_strescape (val_s, "'"); - - s = g_strdup_printf ("'%s'", escaped); - g_free (escaped); - } - } else { - s = g_strdup_value_contents (&val); - if (s == NULL) - s = g_strdup ("NULL"); - else { - char *escaped = g_strescape (s, "'"); - - g_free (s); - s = escaped; - } - } - g_value_unset(&val); - return s; + GValue val = G_VALUE_INIT; + char * s; + + g_return_val_if_fail(setting, NULL); + + if (!NM_IS_SETTING_VPN(setting) && nm_setting_get_secret_flags(setting, name, NULL, NULL)) + return g_strdup("****"); + + if (!_nm_setting_get_property(setting, name, &val)) + return g_strdup("<unknown>"); + + if (G_VALUE_HOLDS_STRING(&val)) { + const char *val_s; + + val_s = g_value_get_string(&val); + if (!val_s) { + /* for NULL, we want to return the unquoted string "NULL". */ + s = g_strdup("NULL"); + } else { + char *escaped = g_strescape(val_s, "'"); + + s = g_strdup_printf("'%s'", escaped); + g_free(escaped); + } + } else { + s = g_strdup_value_contents(&val); + if (s == NULL) + s = g_strdup("NULL"); + else { + char *escaped = g_strescape(s, "'"); + + g_free(s); + s = escaped; + } + } + g_value_unset(&val); + return s; } static void -_log_connection_sort_names (LogConnectionSettingData *setting_data, GArray *sorted_names) +_log_connection_sort_names(LogConnectionSettingData *setting_data, GArray *sorted_names) { - GHashTableIter iter; - LogConnectionSettingItem item; - gpointer p; + GHashTableIter iter; + LogConnectionSettingItem item; + gpointer p; - g_array_set_size (sorted_names, 0); + 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_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); + g_array_sort(sorted_names, _log_connection_sort_names_fcn); } void -nm_utils_log_connection_diff (NMConnection *connection, - NMConnection *diff_base, - guint32 level, - guint64 domain, - const char *name, - const char *prefix, - const char *dbus_path) +nm_utils_log_connection_diff(NMConnection *connection, + NMConnection *diff_base, + guint32 level, + guint64 domain, + const char * name, + const char * prefix, + const char * dbus_path) { - GHashTable *connection_diff = NULL; - GArray *sorted_hashes; - GArray *sorted_names = NULL; - int i, j; - gboolean connection_diff_are_same; - gboolean print_header = TRUE; - gboolean print_setting_header; - GString *str1; - - g_return_if_fail (NM_IS_CONNECTION (connection)); - g_return_if_fail (!diff_base || (NM_IS_CONNECTION (diff_base) && diff_base != connection)); - - /* For VPN setting types, this is broken, because we cannot (generically) print the content of data/secrets. Bummer... */ - - if (!nm_logging_enabled (level, domain)) - return; - - if (!prefix) - prefix = ""; - if (!name) - name = ""; - - 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); - if (connection_diff_are_same) { - const char *t1, *t2; - - t1 = nm_connection_get_connection_type (connection); - if (diff_base) { - t2 = nm_connection_get_connection_type (diff_base); - nm_log (level, domain, NULL, NULL, - "%sconnection '%s' (%p/%s/%s%s%s and %p/%s/%s%s%s): no difference", - prefix, name, - connection, G_OBJECT_TYPE_NAME (connection), NM_PRINT_FMT_QUOTE_STRING (t1), - diff_base, G_OBJECT_TYPE_NAME (diff_base), NM_PRINT_FMT_QUOTE_STRING (t2)); - } else { - nm_log (level, domain, NULL, NULL, - "%sconnection '%s' (%p/%s/%s%s%s): no properties set", - prefix, name, - connection, G_OBJECT_TYPE_NAME (connection), NM_PRINT_FMT_QUOTE_STRING (t1)); - } - g_assert (!connection_diff); - return; - } - - /* FIXME: it doesn't nicely show the content of NMSettingVpn, because nm_connection_diff() does not - * expand the hash values. */ - - sorted_hashes = _log_connection_sort_hashes (connection, diff_base, connection_diff); - if (sorted_hashes->len <= 0) - goto out; - - sorted_names = g_array_new (FALSE, FALSE, sizeof (LogConnectionSettingItem)); - str1 = g_string_new (NULL); - - for (i = 0; i < sorted_hashes->len; i++) { - LogConnectionSettingData *setting_data = &g_array_index (sorted_hashes, LogConnectionSettingData, i); - - _log_connection_sort_names (setting_data, sorted_names); - print_setting_header = TRUE; - for (j = 0; j < sorted_names->len; j++) { - char *str_conn, *str_diff; - LogConnectionSettingItem *item = &g_array_index (sorted_names, LogConnectionSettingItem, j); - - str_conn = (item->diff_result & NM_SETTING_DIFF_RESULT_IN_A) - ? _log_connection_get_property (setting_data->setting, item->item_name) - : NULL; - str_diff = (item->diff_result & NM_SETTING_DIFF_RESULT_IN_B) - ? _log_connection_get_property (setting_data->diff_base_setting, item->item_name) - : NULL; - - if (print_header) { - GError *err_verify = NULL; - const char *t1, *t2; - - t1 = nm_connection_get_connection_type (connection); - if (diff_base) { - t2 = nm_connection_get_connection_type (diff_base); - nm_log (level, domain, NULL, NULL, "%sconnection '%s' (%p/%s/%s%s%s < %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), - NM_PRINT_FMT_QUOTED (dbus_path, " [", dbus_path, "]", "")); - } else { - 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), - NM_PRINT_FMT_QUOTED (dbus_path, " [", dbus_path, "]", "")); - } - print_header = FALSE; - - if (!nm_connection_verify (connection, &err_verify)) { - nm_log (level, domain, NULL, NULL, "%sconnection %p does not verify: %s", prefix, connection, err_verify->message); - g_clear_error (&err_verify); - } - } + GHashTable *connection_diff = NULL; + GArray * sorted_hashes; + GArray * sorted_names = NULL; + int i, j; + gboolean connection_diff_are_same; + gboolean print_header = TRUE; + gboolean print_setting_header; + GString * str1; + + g_return_if_fail(NM_IS_CONNECTION(connection)); + g_return_if_fail(!diff_base || (NM_IS_CONNECTION(diff_base) && diff_base != connection)); + + /* For VPN setting types, this is broken, because we cannot (generically) print the content of data/secrets. Bummer... */ + + if (!nm_logging_enabled(level, domain)) + return; + + if (!prefix) + prefix = ""; + if (!name) + name = ""; + + 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); + if (connection_diff_are_same) { + const char *t1, *t2; + + t1 = nm_connection_get_connection_type(connection); + if (diff_base) { + t2 = nm_connection_get_connection_type(diff_base); + nm_log(level, + domain, + NULL, + NULL, + "%sconnection '%s' (%p/%s/%s%s%s and %p/%s/%s%s%s): no difference", + prefix, + name, + connection, + G_OBJECT_TYPE_NAME(connection), + NM_PRINT_FMT_QUOTE_STRING(t1), + diff_base, + G_OBJECT_TYPE_NAME(diff_base), + NM_PRINT_FMT_QUOTE_STRING(t2)); + } else { + nm_log(level, + domain, + NULL, + NULL, + "%sconnection '%s' (%p/%s/%s%s%s): no properties set", + prefix, + name, + connection, + G_OBJECT_TYPE_NAME(connection), + NM_PRINT_FMT_QUOTE_STRING(t1)); + } + g_assert(!connection_diff); + return; + } + + /* FIXME: it doesn't nicely show the content of NMSettingVpn, because nm_connection_diff() does not + * expand the hash values. */ + + sorted_hashes = _log_connection_sort_hashes(connection, diff_base, connection_diff); + if (sorted_hashes->len <= 0) + goto out; + + sorted_names = g_array_new(FALSE, FALSE, sizeof(LogConnectionSettingItem)); + str1 = g_string_new(NULL); + + for (i = 0; i < sorted_hashes->len; i++) { + LogConnectionSettingData *setting_data = + &g_array_index(sorted_hashes, LogConnectionSettingData, i); + + _log_connection_sort_names(setting_data, sorted_names); + print_setting_header = TRUE; + for (j = 0; j < sorted_names->len; j++) { + char * str_conn, *str_diff; + LogConnectionSettingItem *item = + &g_array_index(sorted_names, LogConnectionSettingItem, j); + + str_conn = (item->diff_result & NM_SETTING_DIFF_RESULT_IN_A) + ? _log_connection_get_property(setting_data->setting, item->item_name) + : NULL; + str_diff = + (item->diff_result & NM_SETTING_DIFF_RESULT_IN_B) + ? _log_connection_get_property(setting_data->diff_base_setting, item->item_name) + : NULL; + + if (print_header) { + GError * err_verify = NULL; + const char *t1, *t2; + + t1 = nm_connection_get_connection_type(connection); + if (diff_base) { + t2 = nm_connection_get_connection_type(diff_base); + nm_log(level, + domain, + NULL, + NULL, + "%sconnection '%s' (%p/%s/%s%s%s < %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), + NM_PRINT_FMT_QUOTED(dbus_path, " [", dbus_path, "]", "")); + } else { + 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), + NM_PRINT_FMT_QUOTED(dbus_path, " [", dbus_path, "]", "")); + } + print_header = FALSE; + + if (!nm_connection_verify(connection, &err_verify)) { + nm_log(level, + domain, + NULL, + NULL, + "%sconnection %p does not verify: %s", + prefix, + connection, + err_verify->message); + g_clear_error(&err_verify); + } + } #define _NM_LOG_ALIGN "-25" - if (print_setting_header) { - if (diff_base) { - if (setting_data->setting && setting_data->diff_base_setting) - g_string_printf (str1, "%p < %p", setting_data->setting, setting_data->diff_base_setting); - else if (setting_data->diff_base_setting) - g_string_printf (str1, "*missing* < %p", setting_data->diff_base_setting); - else - g_string_printf (str1, "%p < *missing*", setting_data->setting); - nm_log (level, domain, NULL, NULL, "%s%"_NM_LOG_ALIGN"s [ %s ]", prefix, setting_data->name, str1->str); - } else - nm_log (level, domain, NULL, NULL, "%s%"_NM_LOG_ALIGN"s [ %p ]", prefix, setting_data->name, setting_data->setting); - 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: - nm_log (level, domain, NULL, NULL, "%s%"_NM_LOG_ALIGN"s < %s", prefix, str1->str, str_diff ?: "NULL"); - break; - case NM_SETTING_DIFF_RESULT_IN_A: - nm_log (level, domain, NULL, NULL, "%s%"_NM_LOG_ALIGN"s = %s", prefix, str1->str, str_conn ?: "NULL"); - break; - default: - nm_log (level, domain, NULL, NULL, "%s%"_NM_LOG_ALIGN"s = %s < %s", prefix, str1->str, str_conn ?: "NULL", str_diff ?: "NULL"); - break; + 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); + nm_log(level, + domain, + NULL, + NULL, + "%s%"_NM_LOG_ALIGN + "s [ %s ]", + prefix, + setting_data->name, + str1->str); + } else + nm_log(level, + domain, + NULL, + NULL, + "%s%"_NM_LOG_ALIGN + "s [ %p ]", + prefix, + setting_data->name, + setting_data->setting); + 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: + nm_log(level, + domain, + NULL, + NULL, + "%s%"_NM_LOG_ALIGN + "s < %s", + prefix, + str1->str, + str_diff ?: "NULL"); + break; + case NM_SETTING_DIFF_RESULT_IN_A: + nm_log(level, + domain, + NULL, + NULL, + "%s%"_NM_LOG_ALIGN + "s = %s", + prefix, + str1->str, + str_conn ?: "NULL"); + break; + default: + nm_log(level, + domain, + NULL, + NULL, + "%s%"_NM_LOG_ALIGN + "s = %s < %s", + prefix, + str1->str, + str_conn ?: "NULL", + str_diff ?: "NULL"); + break; #undef _NM_LOG_ALIGN - } - g_free (str_conn); - g_free (str_diff); - } - } - - g_array_free (sorted_names, TRUE); - g_string_free (str1, TRUE); + } + g_free(str_conn); + g_free(str_diff); + } + } + + g_array_free(sorted_names, TRUE); + g_string_free(str1, TRUE); out: - g_hash_table_destroy (connection_diff); - g_array_free (sorted_hashes, TRUE); + g_hash_table_destroy(connection_diff); + g_array_free(sorted_hashes, TRUE); } #define IPV6_PROPERTY_DIR "/proc/sys/net/ipv6/conf/" #define IPV4_PROPERTY_DIR "/proc/sys/net/ipv4/conf/" -G_STATIC_ASSERT (sizeof (IPV4_PROPERTY_DIR) == sizeof (IPV6_PROPERTY_DIR)); -G_STATIC_ASSERT (NM_STRLEN (IPV6_PROPERTY_DIR) + IFNAMSIZ + 60 == NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE); +G_STATIC_ASSERT(sizeof(IPV4_PROPERTY_DIR) == sizeof(IPV6_PROPERTY_DIR)); +G_STATIC_ASSERT(NM_STRLEN(IPV6_PROPERTY_DIR) + IFNAMSIZ + 60 + == NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE); /** * nm_utils_sysctl_ip_conf_path: @@ -2268,517 +2562,538 @@ G_STATIC_ASSERT (NM_STRLEN (IPV6_PROPERTY_DIR) + IFNAMSIZ + 60 == NM_UTILS_SYSCT * this returns the input argument @buf. */ const char * -nm_utils_sysctl_ip_conf_path (int addr_family, char *buf, const char *ifname, const char *property) +nm_utils_sysctl_ip_conf_path(int addr_family, char *buf, const char *ifname, const char *property) { - int len; - - nm_assert (buf); - nm_assert_addr_family (addr_family); - - g_assert (nm_utils_ifname_valid_kernel (ifname, NULL)); - property = NM_ASSERT_VALID_PATH_COMPONENT (property); - - len = g_snprintf (buf, - NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE, - "%s%s/%s", - addr_family == AF_INET6 ? IPV6_PROPERTY_DIR : IPV4_PROPERTY_DIR, - ifname, - property); - g_assert (len < NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE - 1); - return buf; + int len; + + nm_assert(buf); + nm_assert_addr_family(addr_family); + + g_assert(nm_utils_ifname_valid_kernel(ifname, NULL)); + property = NM_ASSERT_VALID_PATH_COMPONENT(property); + + len = g_snprintf(buf, + NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE, + "%s%s/%s", + addr_family == AF_INET6 ? IPV6_PROPERTY_DIR : IPV4_PROPERTY_DIR, + ifname, + property); + g_assert(len < NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE - 1); + return buf; } gboolean -nm_utils_sysctl_ip_conf_is_path (int addr_family, const char *path, const char *ifname, const char *property) +nm_utils_sysctl_ip_conf_is_path(int addr_family, + const char *path, + const char *ifname, + const char *property) { - g_return_val_if_fail (path, FALSE); - NM_ASSERT_VALID_PATH_COMPONENT (property); - g_assert (!ifname || nm_utils_ifname_valid_kernel (ifname, NULL)); - - if (addr_family == AF_INET) { - if (!g_str_has_prefix (path, IPV4_PROPERTY_DIR)) - return FALSE; - path += NM_STRLEN (IPV4_PROPERTY_DIR); - } else if (addr_family == AF_INET6) { - if (!g_str_has_prefix (path, IPV6_PROPERTY_DIR)) - return FALSE; - path += NM_STRLEN (IPV6_PROPERTY_DIR); - } else - g_return_val_if_reached (FALSE); - - if (ifname) { - if (!g_str_has_prefix (path, ifname)) - return FALSE; - path += strlen (ifname); - if (path[0] != '/') - return FALSE; - path++; - } else { - const char *slash; - char buf[IFNAMSIZ]; - gsize l; - - slash = strchr (path, '/'); - if (!slash) - return FALSE; - l = slash - path; - if (l >= IFNAMSIZ) - return FALSE; - memcpy (buf, path, l); - buf[l] = '\0'; - if (!nm_utils_ifname_valid_kernel (buf, NULL)) - return FALSE; - path = slash + 1; - } - - if (!nm_streq (path, property)) - return FALSE; - - return TRUE; + g_return_val_if_fail(path, FALSE); + NM_ASSERT_VALID_PATH_COMPONENT(property); + g_assert(!ifname || nm_utils_ifname_valid_kernel(ifname, NULL)); + + if (addr_family == AF_INET) { + if (!g_str_has_prefix(path, IPV4_PROPERTY_DIR)) + return FALSE; + path += NM_STRLEN(IPV4_PROPERTY_DIR); + } else if (addr_family == AF_INET6) { + if (!g_str_has_prefix(path, IPV6_PROPERTY_DIR)) + return FALSE; + path += NM_STRLEN(IPV6_PROPERTY_DIR); + } else + g_return_val_if_reached(FALSE); + + if (ifname) { + if (!g_str_has_prefix(path, ifname)) + return FALSE; + path += strlen(ifname); + if (path[0] != '/') + return FALSE; + path++; + } else { + const char *slash; + char buf[IFNAMSIZ]; + gsize l; + + slash = strchr(path, '/'); + if (!slash) + return FALSE; + l = slash - path; + if (l >= IFNAMSIZ) + return FALSE; + memcpy(buf, path, l); + buf[l] = '\0'; + if (!nm_utils_ifname_valid_kernel(buf, NULL)) + return FALSE; + path = slash + 1; + } + + if (!nm_streq(path, property)) + return FALSE; + + return TRUE; } gboolean -nm_utils_is_valid_path_component (const char *name) +nm_utils_is_valid_path_component(const char *name) { - const char *n; - - if (name == NULL || name[0] == '\0') - return FALSE; - - if (name[0] == '.') { - if (name[1] == '\0') - return FALSE; - if (name[1] == '.' && name[2] == '\0') - return FALSE; - } - n = name; - do { - if (*n == '/') - return FALSE; - } while (*(++n) != '\0'); - - return TRUE; + const char *n; + + if (name == NULL || name[0] == '\0') + return FALSE; + + if (name[0] == '.') { + if (name[1] == '\0') + return FALSE; + if (name[1] == '.' && name[2] == '\0') + return FALSE; + } + n = name; + do { + if (*n == '/') + return FALSE; + } while (*(++n) != '\0'); + + return TRUE; } const char * -NM_ASSERT_VALID_PATH_COMPONENT (const char *name) -{ - if (G_LIKELY (nm_utils_is_valid_path_component (name))) - return name; - - nm_log_err (LOGD_CORE, "Failed asserting path component: %s%s%s", - NM_PRINT_FMT_QUOTED (name, "\"", name, "\"", "(null)")); - g_error ("FATAL: Failed asserting path component: %s%s%s", - NM_PRINT_FMT_QUOTED (name, "\"", name, "\"", "(null)")); - g_assert_not_reached (); -} - -gboolean -nm_utils_is_specific_hostname (const char *name) +NM_ASSERT_VALID_PATH_COMPONENT(const char *name) { - if (!name) - return FALSE; - if ( strcmp (name, "(none)") - && strcmp (name, "localhost") - && strcmp (name, "localhost6") - && strcmp (name, "localhost.localdomain") - && strcmp (name, "localhost6.localdomain6")) - return TRUE; - return FALSE; + if (G_LIKELY(nm_utils_is_valid_path_component(name))) + return name; + + nm_log_err(LOGD_CORE, + "Failed asserting path component: %s%s%s", + NM_PRINT_FMT_QUOTED(name, "\"", name, "\"", "(null)")); + g_error("FATAL: Failed asserting path component: %s%s%s", + NM_PRINT_FMT_QUOTED(name, "\"", name, "\"", "(null)")); + g_assert_not_reached(); } /*****************************************************************************/ typedef struct { - NMUuid bin; - char _nul_sentinel; /* just for safety, if somebody accidentally uses the binary in a string context. */ - - /* 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; + NMUuid bin; + char + _nul_sentinel; /* just for safety, if somebody accidentally uses the binary in a string context. */ + + /* 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) +_uuid_data_init(UuidData *uuid_data, gboolean packed, gboolean is_fake, const NMUuid *uuid) { - nm_assert (uuid_data); - nm_assert (uuid); - - uuid_data->bin = *uuid; - uuid_data->_nul_sentinel = '\0'; - uuid_data->is_fake = is_fake; - if (packed) { - G_STATIC_ASSERT_EXPR (sizeof (uuid_data->str) >= (sizeof (*uuid) * 2 + 1)); - nm_utils_bin2hexstr_full (uuid, - sizeof (*uuid), - '\0', - FALSE, - uuid_data->str); - } else { - G_STATIC_ASSERT_EXPR (sizeof (uuid_data->str) >= 37); - _nm_utils_uuid_unparse (uuid, uuid_data->str); - } - return uuid_data; + nm_assert(uuid_data); + nm_assert(uuid); + + uuid_data->bin = *uuid; + uuid_data->_nul_sentinel = '\0'; + uuid_data->is_fake = is_fake; + if (packed) { + G_STATIC_ASSERT_EXPR(sizeof(uuid_data->str) >= (sizeof(*uuid) * 2 + 1)); + nm_utils_bin2hexstr_full(uuid, sizeof(*uuid), '\0', FALSE, uuid_data->str); + } else { + G_STATIC_ASSERT_EXPR(sizeof(uuid_data->str) >= 37); + _nm_utils_uuid_unparse(uuid, uuid_data->str); + } + return uuid_data; } /*****************************************************************************/ static const UuidData * -_machine_id_get (gboolean allow_fake) +_machine_id_get(gboolean allow_fake) { - static const UuidData *volatile p_uuid_data; - const UuidData *d; + static const UuidData *volatile p_uuid_data; + const UuidData *d; again: - d = g_atomic_pointer_get (&p_uuid_data); - if (G_UNLIKELY (!d)) { - static gsize lock; - static UuidData uuid_data; - gs_free char *content = NULL; - gboolean is_fake = TRUE; - const char *fake_type = NULL; - NMUuid uuid; - - /* Get the machine ID from /etc/machine-id; it's always in /etc no matter - * where our configured SYSCONFDIR is. Alternatively, it might be in - * LOCALSTATEDIR /lib/dbus/machine-id. - */ - if ( nm_utils_file_get_contents (-1, "/etc/machine-id", 100*1024, 0, &content, NULL, NULL, NULL) - || nm_utils_file_get_contents (-1, LOCALSTATEDIR"/lib/dbus/machine-id", 100*1024, 0, &content, NULL, NULL, NULL)) { - g_strstrip (content); - if (nm_utils_hexstr2bin_full (content, - FALSE, - FALSE, - NULL, - 16, - (guint8 *) &uuid, - sizeof (uuid), - NULL)) { - if (!nm_utils_uuid_is_null (&uuid)) { - /* an all-zero machine-id is not valid. */ - is_fake = FALSE; - } - } - } - - if (is_fake) { - const guint8 *seed_bin; - const char *hash_seed; - gsize seed_len; - - if (!allow_fake) { - /* we don't allow generating (and memoizing) a fake key. - * Signal that no valid machine-id exists. */ - return NULL; - } - - if (nm_utils_host_id_get (&seed_bin, &seed_len)) { - /* we have no valid machine-id. Generate a fake one by hashing - * the secret-key. This key is commonly persisted, so it should be - * stable across reboots (despite having a broken system without - * proper machine-id). */ - fake_type = "secret-key"; - hash_seed = "ab085f06-b629-46d1-a553-84eeba5683b6"; - } else { - /* the secret-key is not valid/persistent either. That happens when we fail - * to read/write the secret-key to disk. Fallback to boot-id. The boot-id - * itself may be fake and randomly generated ad-hoc, but that is as best - * as it gets. */ - seed_bin = (const guint8 *) nm_utils_boot_id_bin (); - seed_len = sizeof (NMUuid); - fake_type = "boot-id"; - hash_seed = "7ff0c8f5-5399-4901-ab63-61bf594abe8b"; - } - - /* the fake machine-id is based on secret-key/boot-id, but we hash it - * again, so that they are not literally the same. */ - nm_utils_uuid_generate_from_string_bin (&uuid, - (const char *) seed_bin, - seed_len, - NM_UTILS_UUID_TYPE_VERSION5, - (gpointer) hash_seed); - } - - if (!g_once_init_enter (&lock)) - goto again; - - d = _uuid_data_init (&uuid_data, TRUE, is_fake, &uuid); - g_atomic_pointer_set (&p_uuid_data, d); - g_once_init_leave (&lock, 1); - - if (is_fake) { - nm_log_err (LOGD_CORE, - "/etc/machine-id: no valid machine-id. Use fake one based on %s: %s", - fake_type, - d->str); - } else - nm_log_dbg (LOGD_CORE, "/etc/machine-id: %s", d->str); - } - - return d; + d = g_atomic_pointer_get(&p_uuid_data); + if (G_UNLIKELY(!d)) { + static gsize lock; + static UuidData uuid_data; + gs_free char * content = NULL; + gboolean is_fake = TRUE; + const char * fake_type = NULL; + NMUuid uuid; + + /* Get the machine ID from /etc/machine-id; it's always in /etc no matter + * where our configured SYSCONFDIR is. Alternatively, it might be in + * LOCALSTATEDIR /lib/dbus/machine-id. + */ + if (nm_utils_file_get_contents(-1, + "/etc/machine-id", + 100 * 1024, + 0, + &content, + NULL, + NULL, + NULL) + || nm_utils_file_get_contents(-1, + LOCALSTATEDIR "/lib/dbus/machine-id", + 100 * 1024, + 0, + &content, + NULL, + NULL, + NULL)) { + g_strstrip(content); + if (nm_utils_hexstr2bin_full(content, + FALSE, + FALSE, + FALSE, + NULL, + 16, + (guint8 *) &uuid, + sizeof(uuid), + NULL)) { + if (!nm_utils_uuid_is_null(&uuid)) { + /* an all-zero machine-id is not valid. */ + is_fake = FALSE; + } + } + } + + if (is_fake) { + const guint8 *seed_bin; + const char * hash_seed; + gsize seed_len; + + if (!allow_fake) { + /* we don't allow generating (and memorizing) a fake key. + * Signal that no valid machine-id exists. */ + return NULL; + } + + if (nm_utils_host_id_get(&seed_bin, &seed_len)) { + /* 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. */ + fake_type = "secret-key"; + hash_seed = "ab085f06-b629-46d1-a553-84eeba5683b6"; + } else { + /* the secret-key is not valid/persistent either. That happens when we fail + * to read/write the secret-key to disk. Fallback to boot-id. The boot-id + * itself may be fake and randomly generated ad-hoc, but that is as best + * as it gets. */ + seed_bin = (const guint8 *) nm_utils_boot_id_bin(); + seed_len = sizeof(NMUuid); + fake_type = "boot-id"; + hash_seed = "7ff0c8f5-5399-4901-ab63-61bf594abe8b"; + } + + /* the fake machine-id is based on secret-key/boot-id, but we hash it + * again, so that they are not literally the same. */ + nm_utils_uuid_generate_from_string_bin(&uuid, + (const char *) seed_bin, + seed_len, + NM_UTILS_UUID_TYPE_VERSION5, + (gpointer) hash_seed); + } + + if (!g_once_init_enter(&lock)) + goto again; + + d = _uuid_data_init(&uuid_data, TRUE, is_fake, &uuid); + g_atomic_pointer_set(&p_uuid_data, d); + g_once_init_leave(&lock, 1); + + if (is_fake) { + nm_log_err(LOGD_CORE, + "/etc/machine-id: no valid machine-id. Use fake one based on %s: %s", + fake_type, + d->str); + } else + nm_log_dbg(LOGD_CORE, "/etc/machine-id: %s", d->str); + } + + return d; } const char * -nm_utils_machine_id_str (void) +nm_utils_machine_id_str(void) { - return _machine_id_get (TRUE)->str; + return _machine_id_get(TRUE)->str; } const NMUuid * -nm_utils_machine_id_bin (void) +nm_utils_machine_id_bin(void) { - return &_machine_id_get (TRUE)->bin; + return &_machine_id_get(TRUE)->bin; } gboolean -nm_utils_machine_id_is_fake (void) +nm_utils_machine_id_is_fake(void) { - return _machine_id_get (TRUE)->is_fake; + return _machine_id_get(TRUE)->is_fake; } /*****************************************************************************/ /* prefix for version2 secret key. The secret key is hashed with /etc/machine-id. */ #define SECRET_KEY_V2_PREFIX "nm-v2:" -#define SECRET_KEY_FILE NMSTATEDIR"/secret_key" +#define SECRET_KEY_FILE NMSTATEDIR "/secret_key" static gboolean -_host_id_read_timestamp (gboolean use_secret_key_file, - const guint8 *host_id, - gsize host_id_len, - gint64 *out_timestamp_ns) +_host_id_read_timestamp(gboolean use_secret_key_file, + const guint8 *host_id, + gsize host_id_len, + gint64 * out_timestamp_ns) { - struct stat st; - gint64 now; - guint64 v; - - if ( use_secret_key_file - && stat (SECRET_KEY_FILE, &st) == 0) { - /* don't check for overflow or timestamps in the future. We get whatever - * (bogus) date is on the file. */ - *out_timestamp_ns = nm_utils_timespec_to_nsec (&st.st_mtim); - return TRUE; - } - - /* generate a fake timestamp based on the host-id. - * - * This really should never happen under normal circumstances. We already - * are in a code path, where the system has a problem (unable to get good randomness - * and/or can't access the secret_key). In such a scenario, a fake timestamp is the - * least of our problems. - * - * At least, generate something sensible so we don't have to worry about the - * timestamp. It is wrong to worry about using a fake timestamp (which is tied to - * the secret_key) if we are unable to access the secret_key file in the first place. - * - * Pick a random timestamp from the past two years. Yes, this timestamp - * is not stable across restarts, but apparently neither is the host-id - * nor the secret_key itself. */ - -#define EPOCH_TWO_YEARS (G_GINT64_CONSTANT (2 * 365 * 24 * 3600) * NM_UTILS_NSEC_PER_SEC) - - v = nm_hash_siphash42 (1156657133u, host_id, host_id_len); - - now = time (NULL); - *out_timestamp_ns = NM_MAX ((gint64) 1, - (now * NM_UTILS_NSEC_PER_SEC) - ((gint64) (v % ((guint64) (EPOCH_TWO_YEARS))))); - return FALSE; + 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. */ + *out_timestamp_ns = nm_utils_timespec_to_nsec(&st.st_mtim); + return TRUE; + } + + /* generate a fake timestamp based on the host-id. + * + * This really should never happen under normal circumstances. We already + * are in a code path, where the system has a problem (unable to get good randomness + * and/or can't access the secret_key). In such a scenario, a fake timestamp is the + * least of our problems. + * + * At least, generate something sensible so we don't have to worry about the + * timestamp. It is wrong to worry about using a fake timestamp (which is tied to + * the secret_key) if we are unable to access the secret_key file in the first place. + * + * Pick a random timestamp from the past two years. Yes, this timestamp + * is not stable across restarts, but apparently neither is the host-id + * nor the secret_key itself. */ + +#define EPOCH_TWO_YEARS (G_GINT64_CONSTANT(2 * 365 * 24 * 3600) * NM_UTILS_NSEC_PER_SEC) + + v = nm_hash_siphash42(1156657133u, host_id, host_id_len); + + now = time(NULL); + *out_timestamp_ns = + NM_MAX((gint64) 1, + (now * NM_UTILS_NSEC_PER_SEC) - ((gint64)(v % ((guint64)(EPOCH_TWO_YEARS))))); + return FALSE; } static const guint8 * -_host_id_hash_v2 (const guint8 *seed_arr, - gsize seed_len, - guint8 *out_digest /* 32 bytes (NM_UTILS_CHECKSUM_LENGTH_SHA256) */) +_host_id_hash_v2(const guint8 *seed_arr, + gsize seed_len, + guint8 * out_digest /* 32 bytes (NM_UTILS_CHECKSUM_LENGTH_SHA256) */) { - nm_auto_free_checksum GChecksum *sum = g_checksum_new (G_CHECKSUM_SHA256); - const UuidData *machine_id_data; - char slen[100]; + nm_auto_free_checksum GChecksum *sum = g_checksum_new(G_CHECKSUM_SHA256); + const UuidData * machine_id_data; + char slen[100]; - /* - (stat -c '%s' /var/lib/NetworkManager/secret_key; - echo -n ' '; - cat /var/lib/NetworkManager/secret_key; - cat /etc/machine-id | tr -d '\n' | sed -n 's/[a-f0-9-]/\0/pg') | sha256sum - */ + /* + (stat -c '%s' /var/lib/NetworkManager/secret_key; + echo -n ' '; + cat /var/lib/NetworkManager/secret_key; + cat /etc/machine-id | tr -d '\n' | sed -n 's/[a-f0-9-]/\0/pg') | sha256sum + */ - nm_sprintf_buf (slen, "%"G_GSIZE_FORMAT" ", seed_len); - g_checksum_update (sum, (const guchar *) slen, strlen (slen)); + 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); + 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)); + 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; + nm_utils_checksum_get_digest_len(sum, out_digest, NM_UTILS_CHECKSUM_LENGTH_SHA256); + return out_digest; } static gboolean -_host_id_read (guint8 **out_host_id, - gsize *out_host_id_len) +_host_id_read(guint8 **out_host_id, gsize *out_host_id_len) { #define SECRET_KEY_LEN 32u - guint8 sha256_digest[NM_UTILS_CHECKSUM_LENGTH_SHA256]; - nm_auto_clear_secret_ptr NMSecretPtr file_content = { 0 }; - const guint8 *secret_arr; - gsize secret_len; - GError *error = NULL; - gboolean success; - - 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)) { - if (!nm_utils_error_is_notfound (error)) { - nm_log_warn (LOGD_CORE, "secret-key: failure reading secret key in \"%s\": %s (generate new key)", - SECRET_KEY_FILE, error->message); - } - g_clear_error (&error); - } 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. */ - - secret_arr = _host_id_hash_v2 (file_content.bin, file_content.len, sha256_digest); - secret_len = NM_UTILS_CHECKSUM_LENGTH_SHA256; - success = TRUE; - goto out; - } else if (file_content.len >= 16) { - secret_arr = file_content.bin; - secret_len = file_content.len; - success = TRUE; - goto out; - } 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); - } - - /* generate and persist new key */ - { + guint8 sha256_digest[NM_UTILS_CHECKSUM_LENGTH_SHA256]; + nm_auto_clear_secret_ptr NMSecretPtr file_content = {0}; + const guint8 * secret_arr; + gsize secret_len; + GError * error = NULL; + gboolean success; + + 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)) { + if (!nm_utils_error_is_notfound(error)) { + nm_log_warn(LOGD_CORE, + "secret-key: failure reading secret key in \"%s\": %s (generate new key)", + SECRET_KEY_FILE, + error->message); + } + g_clear_error(&error); + } 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. */ + + secret_arr = _host_id_hash_v2(file_content.bin, file_content.len, sha256_digest); + secret_len = NM_UTILS_CHECKSUM_LENGTH_SHA256; + success = TRUE; + nm_log_dbg(LOGD_CORE, + "secret-key: v2 secret key loaded from \"%s\" (%zu bytes)", + SECRET_KEY_FILE, + file_content.len); + goto out; + } else if (file_content.len >= 16) { + secret_arr = file_content.bin; + secret_len = file_content.len; + success = TRUE; + nm_log_dbg(LOGD_CORE, + "secret-key: v1 secret key loaded from \"%s\" (%zu bytes)", + SECRET_KEY_FILE, + file_content.len); + goto out; + } 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); + } + + /* generate and persist new key */ + { #define SECRET_KEY_LEN_BASE64 ((((SECRET_KEY_LEN / 3) + 1) * 4) + 4) - guint8 rnd_buf[SECRET_KEY_LEN]; - guint8 new_content[NM_STRLEN (SECRET_KEY_V2_PREFIX) + SECRET_KEY_LEN_BASE64]; - int base64_state = 0; - int base64_save = 0; - gsize len; - - success = nm_utils_random_bytes (rnd_buf, sizeof (rnd_buf)); - - /* Our key is really binary data. But since we anyway generate a random seed - * (with 32 random bytes), don't write it in binary, but instead create - * an pure ASCII (base64) representation. Note that the ASCII will still be taken - * as-is (no base64 decoding is done). The sole purpose is to write a ASCII file - * instead of a binary. The content is gibberish either way. */ - memcpy (new_content, SECRET_KEY_V2_PREFIX, NM_STRLEN (SECRET_KEY_V2_PREFIX)); - len = NM_STRLEN (SECRET_KEY_V2_PREFIX); - len += g_base64_encode_step (rnd_buf, - sizeof (rnd_buf), - FALSE, - (char *) &new_content[len], - &base64_state, - &base64_save); - len += g_base64_encode_close (FALSE, - (char *) &new_content[len], - &base64_state, - &base64_save); - nm_assert (len <= sizeof (new_content)); - - secret_arr = _host_id_hash_v2 (new_content, len, sha256_digest); - secret_len = NM_UTILS_CHECKSUM_LENGTH_SHA256; - - if (!success) - nm_log_warn (LOGD_CORE, "secret-key: failure to generate good random data for secret-key (use non-persistent key)"); - else if (nm_utils_get_testing ()) { - /* for test code, we don't write the generated secret-key to disk. */ - } else if (!nm_utils_file_set_contents (SECRET_KEY_FILE, - (const char *) new_content, - len, - 0600, - NULL, - &error)) { - nm_log_warn (LOGD_CORE, "secret-key: failure to persist secret key in \"%s\" (%s) (use non-persistent key)", - SECRET_KEY_FILE, error->message); - g_clear_error (&error); - success = FALSE; - } else - nm_log_dbg (LOGD_CORE, "secret-key: persist new secret key to \"%s\"", SECRET_KEY_FILE); - - nm_explicit_bzero (rnd_buf, sizeof (rnd_buf)); - nm_explicit_bzero (new_content, sizeof (new_content)); - } + guint8 rnd_buf[SECRET_KEY_LEN]; + guint8 new_content[NM_STRLEN(SECRET_KEY_V2_PREFIX) + SECRET_KEY_LEN_BASE64]; + int base64_state = 0; + int base64_save = 0; + gsize len; + + success = nm_utils_random_bytes(rnd_buf, sizeof(rnd_buf)); + + /* Our key is really binary data. But since we anyway generate a random seed + * (with 32 random bytes), don't write it in binary, but instead create + * an pure ASCII (base64) representation. Note that the ASCII will still be taken + * as-is (no base64 decoding is done). The sole purpose is to write a ASCII file + * instead of a binary. The content is gibberish either way. */ + memcpy(new_content, SECRET_KEY_V2_PREFIX, NM_STRLEN(SECRET_KEY_V2_PREFIX)); + len = NM_STRLEN(SECRET_KEY_V2_PREFIX); + len += g_base64_encode_step(rnd_buf, + sizeof(rnd_buf), + FALSE, + (char *) &new_content[len], + &base64_state, + &base64_save); + len += + g_base64_encode_close(FALSE, (char *) &new_content[len], &base64_state, &base64_save); + nm_assert(len <= sizeof(new_content)); + + secret_arr = _host_id_hash_v2(new_content, len, sha256_digest); + secret_len = NM_UTILS_CHECKSUM_LENGTH_SHA256; + + if (!success) + nm_log_warn(LOGD_CORE, + "secret-key: failure to generate good random data for secret-key (use " + "non-persistent key)"); + else if (nm_utils_get_testing()) { + /* for test code, we don't write the generated secret-key to disk. */ + } else if (!nm_utils_file_set_contents(SECRET_KEY_FILE, + (const char *) new_content, + len, + 0600, + NULL, + &error)) { + nm_log_warn( + LOGD_CORE, + "secret-key: failure to persist secret key in \"%s\" (%s) (use non-persistent key)", + SECRET_KEY_FILE, + error->message); + g_clear_error(&error); + success = FALSE; + } else + nm_log_dbg(LOGD_CORE, + "secret-key: persist new v2 secret key to \"%s\" (%zu bytes)", + SECRET_KEY_FILE, + len); + + nm_explicit_bzero(rnd_buf, sizeof(rnd_buf)); + nm_explicit_bzero(new_content, sizeof(new_content)); + } out: - *out_host_id_len = secret_len; - *out_host_id = nm_memdup (secret_arr, secret_len); - return success; + *out_host_id_len = secret_len; + *out_host_id = nm_memdup(secret_arr, secret_len); + return success; } typedef struct { - guint8 *host_id; - gsize host_id_len; - gint64 timestamp_ns; - bool is_good:1; - bool timestamp_is_good:1; + guint8 *host_id; + gsize host_id_len; + gint64 timestamp_ns; + bool is_good : 1; + bool timestamp_is_good : 1; } HostIdData; static const HostIdData * -_host_id_get (void) +_host_id_get(void) { - static const HostIdData *volatile host_id_static; - const HostIdData *host_id; + static const HostIdData *volatile host_id_static; + const HostIdData *host_id; again: - host_id = g_atomic_pointer_get (&host_id_static); - if (G_UNLIKELY (!host_id)) { - static HostIdData host_id_data; - static gsize init_value = 0; - - if (!g_once_init_enter (&init_value)) - goto again; - - host_id_data.is_good = _host_id_read (&host_id_data.host_id, - &host_id_data.host_id_len); - - host_id_data.timestamp_is_good = _host_id_read_timestamp (host_id_data.is_good, - host_id_data.host_id, - host_id_data.host_id_len, - &host_id_data.timestamp_ns); - if ( !host_id_data.timestamp_is_good - && host_id_data.is_good) - nm_log_warn (LOGD_CORE, "secret-key: failure reading host timestamp (use fake one)"); - - host_id = &host_id_data; - g_atomic_pointer_set (&host_id_static, host_id); - g_once_init_leave (&init_value, 1); - } - - return host_id; + host_id = g_atomic_pointer_get(&host_id_static); + if (G_UNLIKELY(!host_id)) { + static HostIdData host_id_data; + static gsize init_value = 0; + + if (!g_once_init_enter(&init_value)) + goto again; + + host_id_data.is_good = _host_id_read(&host_id_data.host_id, &host_id_data.host_id_len); + + host_id_data.timestamp_is_good = _host_id_read_timestamp(host_id_data.is_good, + host_id_data.host_id, + host_id_data.host_id_len, + &host_id_data.timestamp_ns); + if (!host_id_data.timestamp_is_good && host_id_data.is_good) + nm_log_warn(LOGD_CORE, "secret-key: failure reading host timestamp (use fake one)"); + + host_id = &host_id_data; + g_atomic_pointer_set(&host_id_static, host_id); + g_once_init_leave(&init_value, 1); + } + + return host_id; } /** @@ -2797,123 +3112,121 @@ again: * to disk, and/or that it was generated with bad randomness. */ gboolean -nm_utils_host_id_get (const guint8 **out_host_id, - gsize *out_host_id_len) +nm_utils_host_id_get(const guint8 **out_host_id, gsize *out_host_id_len) { - const HostIdData *host_id; + const HostIdData *host_id; - host_id = _host_id_get (); - *out_host_id = host_id->host_id; - *out_host_id_len = host_id->host_id_len; - return host_id->is_good; + host_id = _host_id_get(); + *out_host_id = host_id->host_id; + *out_host_id_len = host_id->host_id_len; + return host_id->is_good; } gint64 -nm_utils_host_id_get_timestamp_ns (void) +nm_utils_host_id_get_timestamp_ns(void) { - return _host_id_get ()->timestamp_ns; + return _host_id_get()->timestamp_ns; } /*****************************************************************************/ static const UuidData * -_boot_id_get (void) +_boot_id_get(void) { - static const UuidData *volatile p_boot_id; - const UuidData *d; + static const UuidData *volatile p_boot_id; + const UuidData *d; again: - d = g_atomic_pointer_get (&p_boot_id); - if (G_UNLIKELY (!d)) { - static gsize lock; - static UuidData boot_id; - gs_free char *contents = NULL; - NMUuid uuid; - gboolean is_fake = FALSE; - - nm_utils_file_get_contents (-1, - "/proc/sys/kernel/random/boot_id", - 0, - NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, - &contents, - NULL, - NULL, - NULL); - if ( !contents - || !_nm_utils_uuid_parse (nm_strstrip (contents), &uuid)) { - /* generate a random UUID instead. */ - is_fake = TRUE; - _nm_utils_uuid_generate_random (&uuid); - } - - if (!g_once_init_enter (&lock)) - goto again; - - d = _uuid_data_init (&boot_id, FALSE, is_fake, &uuid); - g_atomic_pointer_set (&p_boot_id, d); - g_once_init_leave (&lock, 1); - } - - return d; + d = g_atomic_pointer_get(&p_boot_id); + if (G_UNLIKELY(!d)) { + static gsize lock; + static UuidData boot_id; + gs_free char * contents = NULL; + NMUuid uuid; + gboolean is_fake = FALSE; + + nm_utils_file_get_contents(-1, + "/proc/sys/kernel/random/boot_id", + 0, + NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, + &contents, + NULL, + NULL, + NULL); + if (!contents || !_nm_utils_uuid_parse(nm_strstrip(contents), &uuid)) { + /* generate a random UUID instead. */ + is_fake = TRUE; + _nm_utils_uuid_generate_random(&uuid); + } + + if (!g_once_init_enter(&lock)) + goto again; + + d = _uuid_data_init(&boot_id, FALSE, is_fake, &uuid); + g_atomic_pointer_set(&p_boot_id, d); + g_once_init_leave(&lock, 1); + } + + return d; } const char * -nm_utils_boot_id_str (void) +nm_utils_boot_id_str(void) { - return _boot_id_get ()->str; + return _boot_id_get()->str; } const NMUuid * -nm_utils_boot_id_bin (void) +nm_utils_boot_id_bin(void) { - return &_boot_id_get ()->bin; + return &_boot_id_get()->bin; } /*****************************************************************************/ const char * -nm_utils_proc_cmdline (void) +nm_utils_proc_cmdline(void) { - static const char *volatile proc_cmdline_cached = NULL; - const char *proc_cmdline; + 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; + proc_cmdline = g_atomic_pointer_get(&proc_cmdline_cached); + if (G_UNLIKELY(!proc_cmdline)) { + gs_free char *str = NULL; - g_file_get_contents ("/proc/cmdline", &str, NULL, NULL); - str = nm_str_realloc (str); + g_file_get_contents("/proc/cmdline", &str, NULL, NULL); + str = nm_str_realloc(str); - proc_cmdline = str ?: ""; - if (!g_atomic_pointer_compare_and_exchange (&proc_cmdline_cached, NULL, proc_cmdline)) - goto again; + proc_cmdline = str ?: ""; + if (!g_atomic_pointer_compare_and_exchange(&proc_cmdline_cached, NULL, proc_cmdline)) + goto again; - g_steal_pointer (&str); - } + g_steal_pointer(&str); + } - return proc_cmdline; + return proc_cmdline; } -const char *const* -nm_utils_proc_cmdline_split (void) +const char *const * +nm_utils_proc_cmdline_split(void) { - static const char *const*volatile proc_cmdline_cached = NULL; - const char *const*proc_cmdline; + static const char *const *volatile proc_cmdline_cached = NULL; + const char *const *proc_cmdline; again: - proc_cmdline = g_atomic_pointer_get (&proc_cmdline_cached); - if (G_UNLIKELY (!proc_cmdline)) { - gs_strfreev char **split = NULL; + proc_cmdline = g_atomic_pointer_get(&proc_cmdline_cached); + if (G_UNLIKELY(!proc_cmdline)) { + 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)) - goto again; + split = nm_utils_strsplit_quoted(nm_utils_proc_cmdline()); + if (!g_atomic_pointer_compare_and_exchange(&proc_cmdline_cached, NULL, (gpointer) split)) + goto again; - proc_cmdline = (const char *const*) g_steal_pointer (&split); - } + proc_cmdline = (const char *const *) g_steal_pointer(&split); + } - return proc_cmdline; + return proc_cmdline; } /*****************************************************************************/ @@ -2930,94 +3243,88 @@ again: * * Returns: the arp-type or negative value on error. */ int -nm_utils_arp_type_detect_from_hwaddrlen (gsize hwaddr_len) +nm_utils_arp_type_detect_from_hwaddrlen(gsize hwaddr_len) { - 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; - } + 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; + } } gboolean -nm_utils_arp_type_validate_hwaddr (int arp_type, - const guint8 *hwaddr, - gsize hwaddr_len) +nm_utils_arp_type_validate_hwaddr(int arp_type, const guint8 *hwaddr, gsize hwaddr_len) { - - if (!hwaddr) - return FALSE; - - if (arp_type == ARPHRD_ETHER) { - G_STATIC_ASSERT (ARPHRD_ETHER >= 0 && ARPHRD_ETHER <= 0xFF); - if (hwaddr_len != ETH_ALEN) - return FALSE; - } else if (arp_type == ARPHRD_INFINIBAND) { - G_STATIC_ASSERT (ARPHRD_INFINIBAND >= 0 && ARPHRD_INFINIBAND <= 0xFF); - if (hwaddr_len != INFINIBAND_ALEN) - return FALSE; - } else - return FALSE; - - nm_assert (arp_type == nm_utils_arp_type_detect_from_hwaddrlen (hwaddr_len)); - return TRUE; + if (!hwaddr) + return FALSE; + + if (arp_type == ARPHRD_ETHER) { + G_STATIC_ASSERT(ARPHRD_ETHER >= 0 && ARPHRD_ETHER <= 0xFF); + if (hwaddr_len != ETH_ALEN) + return FALSE; + } else if (arp_type == ARPHRD_INFINIBAND) { + G_STATIC_ASSERT(ARPHRD_INFINIBAND >= 0 && ARPHRD_INFINIBAND <= 0xFF); + if (hwaddr_len != INFINIBAND_ALEN) + return FALSE; + } else + return FALSE; + + nm_assert(arp_type == nm_utils_arp_type_detect_from_hwaddrlen(hwaddr_len)); + return TRUE; } gboolean -nm_utils_arp_type_get_hwaddr_relevant_part (int arp_type, - const guint8 **hwaddr, - gsize *hwaddr_len) +nm_utils_arp_type_get_hwaddr_relevant_part(int arp_type, const guint8 **hwaddr, gsize *hwaddr_len) { - g_return_val_if_fail ( hwaddr - && hwaddr_len - && nm_utils_arp_type_validate_hwaddr (arp_type, *hwaddr, *hwaddr_len), - FALSE); - - /* for infiniband, we only consider the last 8 bytes. */ - if (arp_type == ARPHRD_INFINIBAND) { - *hwaddr += (INFINIBAND_ALEN - 8); - *hwaddr_len = 8; - } - - return TRUE; + g_return_val_if_fail(hwaddr && hwaddr_len + && nm_utils_arp_type_validate_hwaddr(arp_type, *hwaddr, *hwaddr_len), + FALSE); + + /* for infiniband, we only consider the last 8 bytes. */ + if (arp_type == ARPHRD_INFINIBAND) { + *hwaddr += (INFINIBAND_ALEN - 8); + *hwaddr_len = 8; + } + + return TRUE; } /*****************************************************************************/ /* Returns the "u" (universal/local) bit value for a Modified EUI-64 */ static gboolean -get_gre_eui64_u_bit (guint32 addr) +get_gre_eui64_u_bit(guint32 addr) { - static const struct { - guint32 mask; - guint32 result; - } items[] = { - { 0xff000000 }, { 0x7f000000 }, /* IPv4 loopback */ - { 0xf0000000 }, { 0xe0000000 }, /* IPv4 multicast */ - { 0xffffff00 }, { 0xe0000000 }, /* IPv4 local multicast */ - { 0xffffffff }, { INADDR_BROADCAST }, /* limited broadcast */ - { 0xff000000 }, { 0x00000000 }, /* zero net */ - { 0xff000000 }, { 0x0a000000 }, /* private 10 (RFC3330) */ - { 0xfff00000 }, { 0xac100000 }, /* private 172 */ - { 0xffff0000 }, { 0xc0a80000 }, /* private 192 */ - { 0xffff0000 }, { 0xa9fe0000 }, /* IPv4 link-local */ - { 0xffffff00 }, { 0xc0586300 }, /* anycast 6-to-4 */ - { 0xffffff00 }, { 0xc0000200 }, /* test 192 */ - { 0xfffe0000 }, { 0xc6120000 }, /* test 198 */ - }; - guint i; - - for (i = 0; i < G_N_ELEMENTS (items); i++) { - if ((addr & htonl (items[i].mask)) == htonl (items[i].result)) - return 0x00; /* "local" scope */ - } - return 0x02; /* "universal" scope */ + static const struct { + guint32 mask; + guint32 result; + } items[] = { + {0xff000000}, {0x7f000000}, /* IPv4 loopback */ + {0xf0000000}, {0xe0000000}, /* IPv4 multicast */ + {0xffffff00}, {0xe0000000}, /* IPv4 local multicast */ + {0xffffffff}, {INADDR_BROADCAST}, /* limited broadcast */ + {0xff000000}, {0x00000000}, /* zero net */ + {0xff000000}, {0x0a000000}, /* private 10 (RFC3330) */ + {0xfff00000}, {0xac100000}, /* private 172 */ + {0xffff0000}, {0xc0a80000}, /* private 192 */ + {0xffff0000}, {0xa9fe0000}, /* IPv4 link-local */ + {0xffffff00}, {0xc0586300}, /* anycast 6-to-4 */ + {0xffffff00}, {0xc0000200}, /* test 192 */ + {0xfffe0000}, {0xc6120000}, /* test 198 */ + }; + guint i; + + for (i = 0; i < G_N_ELEMENTS(items); i++) { + if ((addr & htonl(items[i].mask)) == htonl(items[i].result)) + return 0x00; /* "local" scope */ + } + return 0x02; /* "universal" scope */ } /** @@ -3037,70 +3344,70 @@ get_gre_eui64_u_bit (guint32 addr) * if could not be constructed. */ gboolean -nm_utils_get_ipv6_interface_identifier (NMLinkType link_type, - const guint8 *hwaddr, - guint hwaddr_len, - guint dev_id, - NMUtilsIPv6IfaceId *out_iid) +nm_utils_get_ipv6_interface_identifier(NMLinkType link_type, + const guint8 * hwaddr, + guint hwaddr_len, + guint dev_id, + NMUtilsIPv6IfaceId *out_iid) { - guint32 addr; - - g_return_val_if_fail (hwaddr != NULL, FALSE); - g_return_val_if_fail (hwaddr_len > 0, FALSE); - g_return_val_if_fail (out_iid != NULL, FALSE); - - out_iid->id = 0; - - switch (link_type) { - case NM_LINK_TYPE_INFINIBAND: - /* Use the port GUID per http://tools.ietf.org/html/rfc4391#section-8, - * making sure to set the 'u' bit to 1. The GUID is the lower 64 bits - * of the IPoIB interface's hardware address. - */ - g_return_val_if_fail (hwaddr_len == INFINIBAND_ALEN, FALSE); - memcpy (out_iid->id_u8, hwaddr + INFINIBAND_ALEN - 8, 8); - out_iid->id_u8[0] |= 0x02; - return TRUE; - case NM_LINK_TYPE_GRE: - /* Hardware address is the network-endian IPv4 address */ - g_return_val_if_fail (hwaddr_len == 4, FALSE); - addr = * (guint32 *) hwaddr; - out_iid->id_u8[0] = get_gre_eui64_u_bit (addr); - out_iid->id_u8[1] = 0x00; - out_iid->id_u8[2] = 0x5E; - out_iid->id_u8[3] = 0xFE; - memcpy (out_iid->id_u8 + 4, &addr, 4); - return TRUE; - case NM_LINK_TYPE_6LOWPAN: - /* The hardware address is already 64-bit. This is the case for - * IEEE 802.15.4 networks. */ - memcpy (out_iid->id_u8, hwaddr, sizeof (out_iid->id_u8)); - return TRUE; - 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; + guint32 addr; + + g_return_val_if_fail(hwaddr != NULL, FALSE); + g_return_val_if_fail(hwaddr_len > 0, FALSE); + g_return_val_if_fail(out_iid != NULL, FALSE); + + out_iid->id = 0; + + switch (link_type) { + case NM_LINK_TYPE_INFINIBAND: + /* Use the port GUID per http://tools.ietf.org/html/rfc4391#section-8, + * making sure to set the 'u' bit to 1. The GUID is the lower 64 bits + * of the IPoIB interface's hardware address. + */ + g_return_val_if_fail(hwaddr_len == INFINIBAND_ALEN, FALSE); + memcpy(out_iid->id_u8, hwaddr + INFINIBAND_ALEN - 8, 8); + out_iid->id_u8[0] |= 0x02; + return TRUE; + case NM_LINK_TYPE_GRE: + /* Hardware address is the network-endian IPv4 address */ + g_return_val_if_fail(hwaddr_len == 4, FALSE); + addr = *(guint32 *) hwaddr; + out_iid->id_u8[0] = get_gre_eui64_u_bit(addr); + out_iid->id_u8[1] = 0x00; + out_iid->id_u8[2] = 0x5E; + out_iid->id_u8[3] = 0xFE; + memcpy(out_iid->id_u8 + 4, &addr, 4); + return TRUE; + case NM_LINK_TYPE_6LOWPAN: + /* The hardware address is already 64-bit. This is the case for + * IEEE 802.15.4 networks. */ + memcpy(out_iid->id_u8, hwaddr, sizeof(out_iid->id_u8)); + return TRUE; + 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; } /*****************************************************************************/ @@ -3116,10 +3423,9 @@ nm_utils_get_ipv6_interface_identifier (NMLinkType link_type, * token. */ void -nm_utils_ipv6_addr_set_interface_identifier (struct in6_addr *addr, - const NMUtilsIPv6IfaceId iid) +nm_utils_ipv6_addr_set_interface_identifier(struct in6_addr *addr, const NMUtilsIPv6IfaceId iid) { - memcpy (addr->s6_addr + 8, &iid.id_u8, 8); + memcpy(addr->s6_addr + 8, &iid.id_u8, 8); } /** @@ -3131,10 +3437,10 @@ nm_utils_ipv6_addr_set_interface_identifier (struct in6_addr *addr, * the interface identifier. */ void -nm_utils_ipv6_interface_identifier_get_from_addr (NMUtilsIPv6IfaceId *iid, +nm_utils_ipv6_interface_identifier_get_from_addr(NMUtilsIPv6IfaceId * iid, const struct in6_addr *addr) { - memcpy (iid, addr->s6_addr + 8, 8); + memcpy(iid, addr->s6_addr + 8, 8); } /** @@ -3148,21 +3454,20 @@ nm_utils_ipv6_interface_identifier_get_from_addr (NMUtilsIPv6IfaceId *iid, * Returns: %TRUE if the @token is a valid token, %FALSE otherwise */ gboolean -nm_utils_ipv6_interface_identifier_get_from_token (NMUtilsIPv6IfaceId *iid, - const char *token) +nm_utils_ipv6_interface_identifier_get_from_token(NMUtilsIPv6IfaceId *iid, const char *token) { - struct in6_addr i6_token; + struct in6_addr i6_token; - g_return_val_if_fail (token, FALSE); + g_return_val_if_fail(token, FALSE); - if (!inet_pton (AF_INET6, token, &i6_token)) - return FALSE; + if (!inet_pton(AF_INET6, token, &i6_token)) + return FALSE; - if (!_nm_utils_inet6_is_token (&i6_token)) - return FALSE; + if (!_nm_utils_inet6_is_token(&i6_token)) + return FALSE; - nm_utils_ipv6_interface_identifier_get_from_addr (iid, &i6_token); - return TRUE; + nm_utils_ipv6_interface_identifier_get_from_addr(iid, &i6_token); + return TRUE; } /** @@ -3176,291 +3481,299 @@ nm_utils_ipv6_interface_identifier_get_from_token (NMUtilsIPv6IfaceId *iid, * Returns: the input buffer filled with the id as string. */ const char * -nm_utils_inet6_interface_identifier_to_token (NMUtilsIPv6IfaceId iid, - char buf[static INET6_ADDRSTRLEN]) +nm_utils_inet6_interface_identifier_to_token(NMUtilsIPv6IfaceId iid, + char buf[static INET6_ADDRSTRLEN]) { - struct in6_addr i6_token = { .s6_addr = { 0, } }; + struct in6_addr i6_token = {.s6_addr = { + 0, + }}; - nm_assert (buf); - nm_utils_ipv6_addr_set_interface_identifier (&i6_token, iid); - return _nm_utils_inet6_ntop (&i6_token, buf); + nm_assert(buf); + nm_utils_ipv6_addr_set_interface_identifier(&i6_token, iid); + return _nm_utils_inet6_ntop(&i6_token, buf); } /*****************************************************************************/ char * -nm_utils_stable_id_random (void) +nm_utils_stable_id_random(void) { - char buf[15]; + char buf[15]; - nm_utils_random_bytes (buf, sizeof (buf)); - return g_base64_encode ((guchar *) buf, sizeof (buf)); + nm_utils_random_bytes(buf, sizeof(buf)); + return g_base64_encode((guchar *) buf, sizeof(buf)); } char * -nm_utils_stable_id_generated_complete (const char *stable_id_generated) +nm_utils_stable_id_generated_complete(const char *stable_id_generated) { - nm_auto_free_checksum GChecksum *sum = NULL; - guint8 buf[NM_UTILS_CHECKSUM_LENGTH_SHA1]; - char *base64; + nm_auto_free_checksum GChecksum *sum = NULL; + guint8 buf[NM_UTILS_CHECKSUM_LENGTH_SHA1]; + char * base64; - /* for NM_UTILS_STABLE_TYPE_GENERATED we generate a possibly long string - * by doing text-substitutions in nm_utils_stable_id_parse(). - * - * Let's shorten the (possibly) long stable_id to something more compact. */ + /* for NM_UTILS_STABLE_TYPE_GENERATED we generate a possibly long string + * 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); + 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)); - nm_utils_checksum_get_digest (sum, buf); + sum = g_checksum_new(G_CHECKSUM_SHA1); + g_checksum_update(sum, (guchar *) stable_id_generated, strlen(stable_id_generated)); + nm_utils_checksum_get_digest(sum, buf); - /* 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. */ + /* 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 = g_base64_encode((guchar *) buf, sizeof(buf)); + nm_assert(strlen(base64) == 28); + nm_assert(base64[27] == '='); - base64[27] = '\0'; - return base64; + base64[27] = '\0'; + return base64; } static void -_stable_id_append (GString *str, - const char *substitution) +_stable_id_append(GString *str, const char *substitution) { - if (!substitution) - substitution = ""; - g_string_append_printf (str, "=%zu{%s}", strlen (substitution), substitution); + if (!substitution) + substitution = ""; + g_string_append_printf(str, "=%zu{%s}", strlen(substitution), substitution); } NMUtilsStableType -nm_utils_stable_id_parse (const char *stable_id, - const char *deviceid, - const char *hwaddr, - const char *bootid, - const char *uuid, - char **out_generated) +nm_utils_stable_id_parse(const char *stable_id, + const char *deviceid, + const char *hwaddr, + const char *bootid, + const char *uuid, + char ** out_generated) { - gsize i, idx_start; - GString *str = NULL; - - g_return_val_if_fail (out_generated, NM_UTILS_STABLE_TYPE_RANDOM); - - if (!stable_id) { - *out_generated = NULL; - 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. - * In contrast however, the process is unambiguous so that the resulting - * effective id differs if: - * - the original, untranslated stable-id differs - * - or any of the subsitutions differs. - * - * The reason for that is, for example if you specify "${CONNECTION}" in the - * stable-id, then the resulting ID should be always(!) unique for this connection. - * There should be no way another connection could specify any stable-id that results - * in the same addresses to be generated (aside hash collisions). - * - * - * For example: say you have a connection with UUID - * "123e4567-e89b-12d3-a456-426655440000" which happens also to be - * the current boot-id. - * Then: - * (1) connection.stable-id = <NULL> - * (2) connection.stable-id = "123e4567-e89b-12d3-a456-426655440000" - * (3) connection.stable-id = "${CONNECTION}" - * (3) connection.stable-id = "${BOOT}" - * will all generate different addresses, although in one way or the - * other, they all mangle the uuid "123e4567-e89b-12d3-a456-426655440000". - * - * For example, with stable-id="${FOO}${BAR}" the substitutions - * - FOO="ab", BAR="c" - * - FOO="a", BAR="bc" - * should give a different effective id. - * - * For example, with FOO="x" and BAR="x", the stable-ids - * - "${FOO}${BAR}" - * - "${BAR}${FOO}" - * should give a different effective id. - */ - - idx_start = 0; - for (i = 0; stable_id[i]; ) { - if (stable_id[i] != '$') { - i++; - continue; - } - -#define CHECK_PREFIX(prefix) \ - ({ \ - gboolean _match = FALSE; \ - \ - if (g_str_has_prefix (&stable_id[i], ""prefix"")) { \ - _match = TRUE; \ - if (!str) \ - str = g_string_sized_new (256); \ - i += NM_STRLEN (prefix); \ - g_string_append_len (str, &(stable_id)[idx_start], i - idx_start); \ - idx_start = i; \ - } \ - _match; \ - }) - if (CHECK_PREFIX ("${CONNECTION}")) - _stable_id_append (str, uuid); - else if (CHECK_PREFIX ("${BOOT}")) - _stable_id_append (str, bootid); - else if (CHECK_PREFIX ("${DEVICE}")) - _stable_id_append (str, deviceid); - else if (CHECK_PREFIX ("${MAC}")) - _stable_id_append (str, hwaddr); - else if (g_str_has_prefix (&stable_id[i], "${RANDOM}")) { - /* RANDOM makes not so much sense for cloned-mac-address - * as the result is similar to specyifing "cloned-mac-address=random". - * It makes however sense for RFC 7217 Stable Privacy IPv6 addresses - * where this is effectively the only way to generate a different - * (random) host identifier for each connect. - * - * With RANDOM, the user can switch the lifetime of the - * generated cloned-mac-address and IPv6 host identifier - * by toggeling only the stable-id property of the connection. - * With RANDOM being the most short-lived, ~non-stable~ variant. - */ - if (str) - g_string_free (str, TRUE); - *out_generated = NULL; - return NM_UTILS_STABLE_TYPE_RANDOM; - } else { - /* The text following the '$' is not recognized as valid - * substitution pattern. Treat it verbatim. */ - i++; - - /* Note that using unrecognized substitution patterns might - * yield different results with future versions. Avoid that, - * by not using '$' (except for actual substitutions) or escape - * it as "$$" (which is guaranteed to be treated verbatim - * in future). */ - if (stable_id[i] == '$') - i++; - } - } + gsize i, idx_start; + GString *str = NULL; + + g_return_val_if_fail(out_generated, NM_UTILS_STABLE_TYPE_RANDOM); + + if (!stable_id) { + *out_generated = NULL; + 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. + * In contrast however, the process is unambiguous so that the resulting + * effective id differs if: + * - the original, untranslated stable-id differs + * - or any of the subsitutions differs. + * + * The reason for that is, for example if you specify "${CONNECTION}" in the + * stable-id, then the resulting ID should be always(!) unique for this connection. + * There should be no way another connection could specify any stable-id that results + * in the same addresses to be generated (aside hash collisions). + * + * + * For example: say you have a connection with UUID + * "123e4567-e89b-12d3-a456-426655440000" which happens also to be + * the current boot-id. + * Then: + * (1) connection.stable-id = <NULL> + * (2) connection.stable-id = "123e4567-e89b-12d3-a456-426655440000" + * (3) connection.stable-id = "${CONNECTION}" + * (3) connection.stable-id = "${BOOT}" + * will all generate different addresses, although in one way or the + * other, they all mangle the uuid "123e4567-e89b-12d3-a456-426655440000". + * + * For example, with stable-id="${FOO}${BAR}" the substitutions + * - FOO="ab", BAR="c" + * - FOO="a", BAR="bc" + * should give a different effective id. + * + * For example, with FOO="x" and BAR="x", the stable-ids + * - "${FOO}${BAR}" + * - "${BAR}${FOO}" + * should give a different effective id. + */ + + idx_start = 0; + for (i = 0; stable_id[i];) { + if (stable_id[i] != '$') { + i++; + continue; + } + +#define CHECK_PREFIX(prefix) \ + ({ \ + gboolean _match = FALSE; \ + \ + if (g_str_has_prefix(&stable_id[i], "" prefix "")) { \ + _match = TRUE; \ + if (!str) \ + str = g_string_sized_new(256); \ + i += NM_STRLEN(prefix); \ + g_string_append_len(str, &(stable_id)[idx_start], i - idx_start); \ + idx_start = i; \ + } \ + _match; \ + }) + if (CHECK_PREFIX("${CONNECTION}")) + _stable_id_append(str, uuid); + else if (CHECK_PREFIX("${BOOT}")) + _stable_id_append(str, bootid); + else if (CHECK_PREFIX("${DEVICE}")) + _stable_id_append(str, deviceid); + else if (CHECK_PREFIX("${MAC}")) + _stable_id_append(str, hwaddr); + else if (g_str_has_prefix(&stable_id[i], "${RANDOM}")) { + /* RANDOM makes not so much sense for cloned-mac-address + * as the result is similar to specifying "cloned-mac-address=random". + * It makes however sense for RFC 7217 Stable Privacy IPv6 addresses + * where this is effectively the only way to generate a different + * (random) host identifier for each connect. + * + * With RANDOM, the user can switch the lifetime of the + * generated cloned-mac-address and IPv6 host identifier + * by toggling only the stable-id property of the connection. + * With RANDOM being the most short-lived, ~non-stable~ variant. + */ + if (str) + g_string_free(str, TRUE); + *out_generated = NULL; + return NM_UTILS_STABLE_TYPE_RANDOM; + } else { + /* The text following the '$' is not recognized as valid + * substitution pattern. Treat it verbatim. */ + i++; + + /* Note that using unrecognized substitution patterns might + * yield different results with future versions. Avoid that, + * by not using '$' (except for actual substitutions) or escape + * it as "$$" (which is guaranteed to be treated verbatim + * in future). */ + if (stable_id[i] == '$') + i++; + } + } #undef CHECK_PREFIX - if (!str) { - *out_generated = NULL; - return NM_UTILS_STABLE_TYPE_STABLE_ID; - } + if (!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; + 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; } /*****************************************************************************/ static gboolean -_is_reserved_ipv6_iid (const guint8 *iid) +_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; + /* 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; } static gboolean -_set_stable_privacy (NMUtilsStableType stable_type, - struct in6_addr *addr, - const char *ifname, - const char *network_id, - guint32 dad_counter, - const guint8 *host_id, - gsize host_id_len, - GError **error) +_set_stable_privacy(NMUtilsStableType stable_type, + struct in6_addr * addr, + const char * ifname, + const char * network_id, + guint32 dad_counter, + const guint8 * host_id, + gsize host_id_len, + GError ** error) { - nm_auto_free_checksum GChecksum *sum = NULL; - guint8 digest[NM_UTILS_CHECKSUM_LENGTH_SHA256]; - guint32 tmp[2]; - - nm_assert (host_id_len); - nm_assert (network_id); - - sum = g_checksum_new (G_CHECKSUM_SHA256); - - host_id_len = MIN (host_id_len, G_MAXUINT32); - - if (stable_type != NM_UTILS_STABLE_TYPE_UUID) { - guint8 stable_type_uint8; - - nm_assert (stable_type < (NMUtilsStableType) 255); - stable_type_uint8 = (guint8) stable_type; - - /* 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 - * fields are hashed. That is, as we also hash @host_id_len - * and the terminating '\0' of @network_id, it is unambigiously - * possible to revert the process and deduce the @stable_type. - */ - g_checksum_update (sum, &stable_type_uint8, sizeof (stable_type_uint8)); - } - - g_checksum_update (sum, addr->s6_addr, 8); - g_checksum_update (sum, (const guchar *) ifname, strlen (ifname) + 1); - g_checksum_update (sum, (const guchar *) network_id, strlen (network_id) + 1); - tmp[0] = htonl (dad_counter); - tmp[1] = htonl (host_id_len); - g_checksum_update (sum, (const guchar *) tmp, sizeof (tmp)); - g_checksum_update (sum, (const guchar *) host_id, host_id_len); - nm_utils_checksum_get_digest (sum, digest); - - while (_is_reserved_ipv6_iid (digest)) { - g_checksum_reset (sum); - tmp[0] = htonl (++dad_counter); - g_checksum_update (sum, digest, sizeof (digest)); - g_checksum_update (sum, (const guchar *) &tmp[0], sizeof (tmp[0])); - nm_utils_checksum_get_digest (sum, digest); - } - - memcpy (addr->s6_addr + 8, &digest[0], 8); - return TRUE; + nm_auto_free_checksum GChecksum *sum = NULL; + guint8 digest[NM_UTILS_CHECKSUM_LENGTH_SHA256]; + guint32 tmp[2]; + + nm_assert(host_id_len); + nm_assert(network_id); + + sum = g_checksum_new(G_CHECKSUM_SHA256); + + host_id_len = MIN(host_id_len, G_MAXUINT32); + + if (stable_type != NM_UTILS_STABLE_TYPE_UUID) { + guint8 stable_type_uint8; + + nm_assert(stable_type < (NMUtilsStableType) 255); + stable_type_uint8 = (guint8) stable_type; + + /* 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 + * fields are hashed. That is, as we also hash @host_id_len + * and the terminating '\0' of @network_id, it is unambiguously + * possible to revert the process and deduce the @stable_type. + */ + g_checksum_update(sum, &stable_type_uint8, sizeof(stable_type_uint8)); + } + + g_checksum_update(sum, addr->s6_addr, 8); + g_checksum_update(sum, (const guchar *) ifname, strlen(ifname) + 1); + g_checksum_update(sum, (const guchar *) network_id, strlen(network_id) + 1); + tmp[0] = htonl(dad_counter); + tmp[1] = htonl(host_id_len); + g_checksum_update(sum, (const guchar *) tmp, sizeof(tmp)); + g_checksum_update(sum, (const guchar *) host_id, host_id_len); + nm_utils_checksum_get_digest(sum, digest); + + while (_is_reserved_ipv6_iid(digest)) { + g_checksum_reset(sum); + tmp[0] = htonl(++dad_counter); + g_checksum_update(sum, digest, sizeof(digest)); + g_checksum_update(sum, (const guchar *) &tmp[0], sizeof(tmp[0])); + nm_utils_checksum_get_digest(sum, digest); + } + + memcpy(addr->s6_addr + 8, &digest[0], 8); + return TRUE; } gboolean -nm_utils_ipv6_addr_set_stable_privacy_impl (NMUtilsStableType stable_type, - struct in6_addr *addr, - const char *ifname, - const char *network_id, - guint32 dad_counter, - guint8 *host_id, - gsize host_id_len, - GError **error) +nm_utils_ipv6_addr_set_stable_privacy_impl(NMUtilsStableType stable_type, + struct in6_addr * addr, + const char * ifname, + const char * network_id, + guint32 dad_counter, + guint8 * host_id, + gsize host_id_len, + GError ** error) { - return _set_stable_privacy (stable_type, addr, ifname, network_id, dad_counter, host_id, host_id_len, error); + return _set_stable_privacy(stable_type, + addr, + ifname, + network_id, + dad_counter, + host_id, + host_id_len, + error); } #define RFC7217_IDGEN_RETRIES 3 @@ -3473,190 +3786,218 @@ nm_utils_ipv6_addr_set_stable_privacy_impl (NMUtilsStableType stable_type, * Returns: %TRUE on success, %FALSE if the address could not be generated. */ gboolean -nm_utils_ipv6_addr_set_stable_privacy (NMUtilsStableType stable_type, - struct in6_addr *addr, - const char *ifname, - const char *network_id, - guint32 dad_counter, - GError **error) +nm_utils_ipv6_addr_set_stable_privacy(NMUtilsStableType stable_type, + struct in6_addr * addr, + const char * ifname, + const char * network_id, + guint32 dad_counter, + GError ** error) { - const guint8 *host_id; - gsize host_id_len; - - g_return_val_if_fail (network_id, FALSE); - - if (dad_counter >= RFC7217_IDGEN_RETRIES) { - g_set_error_literal (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - "Too many DAD collisions"); - return FALSE; - } - - nm_utils_host_id_get (&host_id, &host_id_len); - - return _set_stable_privacy (stable_type, addr, ifname, network_id, dad_counter, - host_id, host_id_len, error); + const guint8 *host_id; + gsize host_id_len; + + g_return_val_if_fail(network_id, FALSE); + + if (dad_counter >= RFC7217_IDGEN_RETRIES) { + g_set_error_literal(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "Too many DAD collisions"); + return FALSE; + } + + nm_utils_host_id_get(&host_id, &host_id_len); + + return _set_stable_privacy(stable_type, + addr, + ifname, + network_id, + dad_counter, + host_id, + host_id_len, + error); } /*****************************************************************************/ static void -_hw_addr_eth_complete (struct ether_addr *addr, - const char *current_mac_address, - const char *generate_mac_address_mask) +_hw_addr_eth_complete(struct ether_addr *addr, + const char * current_mac_address, + const char * generate_mac_address_mask) { - struct ether_addr mask; - struct ether_addr oui; - struct ether_addr *ouis; - gsize ouis_len; - guint i; - - /* the second LSB of the first octet means - * "globally unique, OUI enforced, BIA (burned-in-address)" - * vs. "locally-administered". By default, set it to - * generate locally-administered addresses. - * - * Maybe be overwritten by a mask below. */ - addr->ether_addr_octet[0] |= 2; - - if (!generate_mac_address_mask || !*generate_mac_address_mask) - goto out; - if (!_nm_utils_generate_mac_address_mask_parse (generate_mac_address_mask, - &mask, - &ouis, - &ouis_len, - NULL)) - goto out; - - nm_assert ((ouis == NULL) ^ (ouis_len != 0)); - if (ouis) { - /* g_random_int() is good enough here. It uses a static GRand instance - * that is seeded from /dev/urandom. */ - oui = ouis[g_random_int () % ouis_len]; - g_free (ouis); - } else { - if (!nm_utils_hwaddr_aton (current_mac_address, &oui, ETH_ALEN)) - goto out; - } - - for (i = 0; i < ETH_ALEN; i++) { - const guint8 a = addr->ether_addr_octet[i]; - const guint8 o = oui.ether_addr_octet[i]; - const guint8 m = mask.ether_addr_octet[i]; - - addr->ether_addr_octet[i] = (a & ~m) | (o & m); - } + struct ether_addr mask; + struct ether_addr oui; + struct ether_addr *ouis; + gsize ouis_len; + guint i; + + /* the second LSB of the first octet means + * "globally unique, OUI enforced, BIA (burned-in-address)" + * vs. "locally-administered". By default, set it to + * generate locally-administered addresses. + * + * Maybe be overwritten by a mask below. */ + addr->ether_addr_octet[0] |= 2; + + if (!generate_mac_address_mask || !*generate_mac_address_mask) + goto out; + if (!_nm_utils_generate_mac_address_mask_parse(generate_mac_address_mask, + &mask, + &ouis, + &ouis_len, + NULL)) + goto out; + + nm_assert((ouis == NULL) ^ (ouis_len != 0)); + if (ouis) { + /* g_random_int() is good enough here. It uses a static GRand instance + * that is seeded from /dev/urandom. */ + oui = ouis[g_random_int() % ouis_len]; + g_free(ouis); + } else { + if (!nm_utils_hwaddr_aton(current_mac_address, &oui, ETH_ALEN)) + goto out; + } + + for (i = 0; i < ETH_ALEN; i++) { + const guint8 a = addr->ether_addr_octet[i]; + const guint8 o = oui.ether_addr_octet[i]; + const guint8 m = mask.ether_addr_octet[i]; + + addr->ether_addr_octet[i] = (a & ~m) | (o & m); + } out: - /* The LSB of the first octet must always be cleared, - * it means Unicast vs. Multicast */ - addr->ether_addr_octet[0] &= ~1; + /* The LSB of the first octet must always be cleared, + * it means Unicast vs. Multicast */ + addr->ether_addr_octet[0] &= ~1; } char * -nm_utils_hw_addr_gen_random_eth (const char *current_mac_address, - const char *generate_mac_address_mask) +nm_utils_hw_addr_gen_random_eth(const char *current_mac_address, + const char *generate_mac_address_mask) { - struct ether_addr bin_addr; + struct ether_addr bin_addr; - nm_utils_random_bytes (&bin_addr, ETH_ALEN); - _hw_addr_eth_complete (&bin_addr, current_mac_address, generate_mac_address_mask); - return nm_utils_hwaddr_ntoa (&bin_addr, ETH_ALEN); + nm_utils_random_bytes(&bin_addr, ETH_ALEN); + _hw_addr_eth_complete(&bin_addr, current_mac_address, generate_mac_address_mask); + return nm_utils_hwaddr_ntoa(&bin_addr, ETH_ALEN); } static char * -_hw_addr_gen_stable_eth (NMUtilsStableType stable_type, - const char *stable_id, - const guint8 *host_id, - gsize host_id_len, - const char *ifname, - const char *current_mac_address, - const char *generate_mac_address_mask) +_hw_addr_gen_stable_eth(NMUtilsStableType stable_type, + const char * stable_id, + const guint8 * host_id, + gsize host_id_len, + const char * ifname, + const char * current_mac_address, + const char * generate_mac_address_mask) { - nm_auto_free_checksum GChecksum *sum = NULL; - guint32 tmp; - guint8 digest[NM_UTILS_CHECKSUM_LENGTH_SHA256]; - struct ether_addr bin_addr; - guint8 stable_type_uint8; + nm_auto_free_checksum GChecksum *sum = NULL; + guint32 tmp; + guint8 digest[NM_UTILS_CHECKSUM_LENGTH_SHA256]; + struct ether_addr bin_addr; + guint8 stable_type_uint8; - nm_assert (stable_id); - nm_assert (host_id); + nm_assert(stable_id); + nm_assert(host_id); - sum = g_checksum_new (G_CHECKSUM_SHA256); + sum = g_checksum_new(G_CHECKSUM_SHA256); - host_id_len = MIN (host_id_len, G_MAXUINT32); + host_id_len = MIN(host_id_len, G_MAXUINT32); - nm_assert (stable_type < (NMUtilsStableType) 255); - stable_type_uint8 = stable_type; - g_checksum_update (sum, (const guchar *) &stable_type_uint8, sizeof (stable_type_uint8)); + nm_assert(stable_type < (NMUtilsStableType) 255); + stable_type_uint8 = stable_type; + g_checksum_update(sum, (const guchar *) &stable_type_uint8, sizeof(stable_type_uint8)); - tmp = htonl ((guint32) host_id_len); - g_checksum_update (sum, (const guchar *) &tmp, sizeof (tmp)); - g_checksum_update (sum, (const guchar *) host_id, host_id_len); - g_checksum_update (sum, (const guchar *) (ifname ?: ""), ifname ? (strlen (ifname) + 1) : 1); - g_checksum_update (sum, (const guchar *) stable_id, strlen (stable_id) + 1); + tmp = htonl((guint32) host_id_len); + g_checksum_update(sum, (const guchar *) &tmp, sizeof(tmp)); + g_checksum_update(sum, (const guchar *) host_id, host_id_len); + g_checksum_update(sum, (const guchar *) (ifname ?: ""), ifname ? (strlen(ifname) + 1) : 1); + g_checksum_update(sum, (const guchar *) stable_id, strlen(stable_id) + 1); - nm_utils_checksum_get_digest (sum, digest); + nm_utils_checksum_get_digest(sum, digest); - 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); + 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); } char * -nm_utils_hw_addr_gen_stable_eth_impl (NMUtilsStableType stable_type, - const char *stable_id, - const guint8 *host_id, - gsize host_id_len, - const char *ifname, - const char *current_mac_address, - const char *generate_mac_address_mask) +nm_utils_hw_addr_gen_stable_eth_impl(NMUtilsStableType stable_type, + const char * stable_id, + const guint8 * host_id, + gsize host_id_len, + const char * ifname, + const char * current_mac_address, + const char * generate_mac_address_mask) { - return _hw_addr_gen_stable_eth (stable_type, stable_id, host_id, host_id_len, ifname, current_mac_address, generate_mac_address_mask); + return _hw_addr_gen_stable_eth(stable_type, + stable_id, + host_id, + host_id_len, + ifname, + current_mac_address, + generate_mac_address_mask); } char * -nm_utils_hw_addr_gen_stable_eth (NMUtilsStableType stable_type, - const char *stable_id, - const char *ifname, - const char *current_mac_address, - const char *generate_mac_address_mask) +nm_utils_hw_addr_gen_stable_eth(NMUtilsStableType stable_type, + const char * stable_id, + const char * ifname, + const char * current_mac_address, + const char * generate_mac_address_mask) { - const guint8 *host_id; - gsize host_id_len; + const guint8 *host_id; + gsize host_id_len; - g_return_val_if_fail (stable_id, NULL); + g_return_val_if_fail(stable_id, NULL); - nm_utils_host_id_get (&host_id, &host_id_len); + nm_utils_host_id_get(&host_id, &host_id_len); - return _hw_addr_gen_stable_eth (stable_type, - stable_id, - host_id, - host_id_len, - ifname, - current_mac_address, - generate_mac_address_mask); + return _hw_addr_gen_stable_eth(stable_type, + stable_id, + host_id, + host_id_len, + ifname, + current_mac_address, + generate_mac_address_mask); } /*****************************************************************************/ GBytes * -nm_utils_dhcp_client_id_mac (int arp_type, - const guint8 *hwaddr, - gsize hwaddr_len) +nm_utils_dhcp_client_id_mac(int arp_type, const guint8 *hwaddr, gsize hwaddr_len) { - guint8 *client_id_buf; - const guint8 hwaddr_type = arp_type; + guint8 * client_id_buf; + const guint8 hwaddr_type = arp_type; - if (!nm_utils_arp_type_get_hwaddr_relevant_part (arp_type, &hwaddr, &hwaddr_len)) - g_return_val_if_reached (NULL); + 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); + 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); } -#define HASH_KEY ((const guint8[16]) { 0x80, 0x11, 0x8c, 0xc2, 0xfe, 0x4a, 0x03, 0xee, 0x3e, 0xd6, 0x0c, 0x6f, 0x36, 0x39, 0x14, 0x09 }) +#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: @@ -3673,30 +4014,30 @@ nm_utils_dhcp_client_id_mac (int arp_type, * * 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) +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; - } + 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; + } } /** @@ -3713,51 +4054,154 @@ nm_utils_create_dhcp_iaid (gboolean legacy_unstable_byteorder, * Returns: a %GBytes of generated client-id. This function cannot fail. */ GBytes * -nm_utils_dhcp_client_id_systemd_node_specific_full (guint32 iaid, - const guint8 *machine_id, - gsize machine_id_len) +nm_utils_dhcp_client_id_systemd_node_specific_full(guint32 iaid, + const guint8 *machine_id, + gsize machine_id_len) +{ + const guint16 DUID_TYPE_EN = 2; + const guint32 SYSTEMD_PEN = 43793; + struct _nm_packed { + guint8 type; + guint32 iaid; + struct _nm_packed { + guint16 type; + union { + struct _nm_packed { + /* DUID_TYPE_EN */ + guint32 pen; + uint8_t id[8]; + } en; + }; + } duid; + } * client_id; + guint64 u64; + + g_return_val_if_fail(machine_id, NULL); + g_return_val_if_fail(machine_id_len > 0, NULL); + + client_id = g_malloc(sizeof(*client_id)); + + client_id->type = 255; + unaligned_write_be32(&client_id->iaid, iaid); + unaligned_write_be16(&client_id->duid.type, DUID_TYPE_EN); + unaligned_write_be32(&client_id->duid.en.pen, SYSTEMD_PEN); + + u64 = htole64(c_siphash_hash(HASH_KEY, machine_id, machine_id_len)); + memcpy(client_id->duid.en.id, &u64, sizeof(client_id->duid.en.id)); + + G_STATIC_ASSERT_EXPR(sizeof(*client_id) == 19); + return g_bytes_new_take(client_id, 19); +} + +GBytes * +nm_utils_dhcp_client_id_systemd_node_specific(guint32 iaid) +{ + return nm_utils_dhcp_client_id_systemd_node_specific_full( + iaid, + (const guint8 *) nm_utils_machine_id_bin(), + sizeof(NMUuid)); +} + +/*****************************************************************************/ + +GBytes * +nm_utils_generate_duid_llt(int arp_type, const guint8 *hwaddr, gsize hwaddr_len, gint64 time) +{ + guint8 * arr; + 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) { - const guint16 DUID_TYPE_EN = 2; - const guint32 SYSTEMD_PEN = 43793; - struct _nm_packed { - guint8 type; - guint32 iaid; - struct _nm_packed { - guint16 type; - union { - struct _nm_packed { - /* DUID_TYPE_EN */ - guint32 pen; - uint8_t id[8]; - } en; - }; - } duid; - } *client_id; - guint64 u64; - - g_return_val_if_fail (machine_id, NULL); - g_return_val_if_fail (machine_id_len > 0, NULL); - - client_id = g_malloc (sizeof (*client_id)); - - client_id->type = 255; - unaligned_write_be32 (&client_id->iaid, iaid); - unaligned_write_be16 (&client_id->duid.type, DUID_TYPE_EN); - unaligned_write_be32 (&client_id->duid.en.pen, SYSTEMD_PEN); - - u64 = htole64 (c_siphash_hash (HASH_KEY, machine_id, machine_id_len)); - memcpy(client_id->duid.en.id, &u64, sizeof (client_id->duid.en.id)); - - G_STATIC_ASSERT_EXPR (sizeof (*client_id) == 19); - return g_bytes_new_take (client_id, 19); + guint8 * arr; + 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_dhcp_client_id_systemd_node_specific (guint32 iaid) +nm_utils_generate_duid_uuid(const NMUuid *uuid) { - return nm_utils_dhcp_client_id_systemd_node_specific_full (iaid, - (const guint8 *) nm_utils_machine_id_bin (), - sizeof (NMUuid)); + const guint16 duid_type = htons(4); + guint8 * duid_buffer; + + 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; + const NMUuid * machine_id; + union { + guint8 sha256[NM_UTILS_CHECKSUM_LENGTH_SHA256]; + NMUuid uuid; + } digest; + + machine_id = nm_utils_machine_id_bin(); + + /* 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); } /*****************************************************************************/ @@ -3772,12 +4216,12 @@ nm_utils_dhcp_client_id_systemd_node_specific (guint32 iaid) * is killed). */ void -nm_utils_setpgid (gpointer unused G_GNUC_UNUSED) +nm_utils_setpgid(gpointer unused G_GNUC_UNUSED) { - pid_t pid; + pid_t pid; - pid = getpid (); - setpgid (pid, pid); + pid = getpid(); + setpgid(pid, pid); } /** @@ -3789,53 +4233,42 @@ nm_utils_setpgid (gpointer unused G_GNUC_UNUSED) * Converts @strings to a #GStrv and stores it in @value. */ void -nm_utils_g_value_set_strv (GValue *value, GPtrArray *strings) +nm_utils_g_value_set_strv(GValue *value, GPtrArray *strings) { - char **strv; - guint i; + char **strv; + guint i; - strv = g_new (char *, strings->len + 1); - for (i = 0; i < strings->len; i++) { - nm_assert (strings->pdata[i]); - strv[i] = g_strdup (strings->pdata[i]); - } - strv[i] = NULL; + strv = g_new(char *, strings->len + 1); + for (i = 0; i < strings->len; i++) { + nm_assert(strings->pdata[i]); + strv[i] = g_strdup(strings->pdata[i]); + } + strv[i] = NULL; - g_value_take_boxed (value, strv); + g_value_take_boxed(value, strv); } /*****************************************************************************/ void -nm_utils_ifname_cpy (char *dst, const char *name) +nm_utils_ifname_cpy(char *dst, const char *name) { - int i; + int i; - g_return_if_fail (dst); - g_return_if_fail (name && name[0]); + g_return_if_fail(dst); + g_return_if_fail(name && name[0]); - nm_assert (nm_utils_ifname_valid_kernel (name, NULL)); + nm_assert(nm_utils_ifname_valid_kernel(name, NULL)); - /* ensures NUL padding of the entire IFNAMSIZ buffer. */ + /* ensures NUL padding of the entire IFNAMSIZ buffer. */ - for (i = 0; i < (int) IFNAMSIZ && name[i] != '\0'; i++) - dst[i] = name[i]; + for (i = 0; i < (int) IFNAMSIZ && name[i] != '\0'; i++) + dst[i] = name[i]; - nm_assert (name[i] == '\0'); + nm_assert(name[i] == '\0'); - for (; i < (int) IFNAMSIZ; i++) - dst[i] = '\0'; -} - -/*****************************************************************************/ - -#define IPV4LL_NETWORK (htonl (0xA9FE0000L)) -#define IPV4LL_NETMASK (htonl (0xFFFF0000L)) - -gboolean -nm_utils_ip4_address_is_link_local (in_addr_t addr) -{ - return (addr & IPV4LL_NETMASK) == IPV4LL_NETWORK; + for (; i < (int) IFNAMSIZ; i++) + dst[i] = '\0'; } /*****************************************************************************/ @@ -3845,113 +4278,111 @@ nm_utils_ip4_address_is_link_local (in_addr_t addr) * on the new timestamp @now. */ guint32 -nm_utils_lifetime_rebase_relative_time_on_now (guint32 timestamp, - guint32 duration, - gint32 now) +nm_utils_lifetime_rebase_relative_time_on_now(guint32 timestamp, guint32 duration, gint32 now) { - gint64 t; - - nm_assert (now >= 0); - - if (duration == NM_PLATFORM_LIFETIME_PERMANENT) - return NM_PLATFORM_LIFETIME_PERMANENT; - - if (timestamp == 0) { - /* if the @timestamp is zero, assume it was just left unset and that the relative - * @duration starts counting from @now. This is convenient to construct an address - * and print it in nm_platform_ip4_address_to_string(). - * - * In general it does not make sense to set the @duration without anchoring at - * @timestamp because you don't know the absolute expiration time when looking - * at the address at a later moment. */ - timestamp = now; - } - - /* For timestamp > now, just accept it and calculate the expected(?) result. */ - t = (gint64) timestamp + (gint64) duration - (gint64) now; - - if (t <= 0) - return 0; - if (t >= NM_PLATFORM_LIFETIME_PERMANENT) - return NM_PLATFORM_LIFETIME_PERMANENT - 1; - return t; + gint64 t; + + nm_assert(now >= 0); + + if (duration == NM_PLATFORM_LIFETIME_PERMANENT) + return NM_PLATFORM_LIFETIME_PERMANENT; + + if (timestamp == 0) { + /* if the @timestamp is zero, assume it was just left unset and that the relative + * @duration starts counting from @now. This is convenient to construct an address + * and print it in nm_platform_ip4_address_to_string(). + * + * In general it does not make sense to set the @duration without anchoring at + * @timestamp because you don't know the absolute expiration time when looking + * at the address at a later moment. */ + timestamp = now; + } + + /* For timestamp > now, just accept it and calculate the expected(?) result. */ + t = (gint64) timestamp + (gint64) duration - (gint64) now; + + if (t <= 0) + return 0; + if (t >= NM_PLATFORM_LIFETIME_PERMANENT) + return NM_PLATFORM_LIFETIME_PERMANENT - 1; + return t; } guint32 -nm_utils_lifetime_get (guint32 timestamp, - guint32 lifetime, - guint32 preferred, - gint32 now, - guint32 *out_preferred) +nm_utils_lifetime_get(guint32 timestamp, + guint32 lifetime, + guint32 preferred, + gint32 now, + guint32 *out_preferred) { - guint32 t_lifetime, t_preferred; - - nm_assert (now >= 0); - - if (timestamp == 0 && lifetime == 0) { - /* We treat lifetime==0 && timestamp==0 addresses as permanent addresses to allow easy - * creation of such addresses (without requiring to set the lifetime fields to - * NM_PLATFORM_LIFETIME_PERMANENT). The real lifetime==0 addresses (E.g. DHCP6 telling us - * to drop an address will have timestamp set. - */ - NM_SET_OUT (out_preferred, NM_PLATFORM_LIFETIME_PERMANENT); - g_return_val_if_fail (preferred == 0, NM_PLATFORM_LIFETIME_PERMANENT); - return NM_PLATFORM_LIFETIME_PERMANENT; - } - - if (now <= 0) - now = nm_utils_get_monotonic_timestamp_sec (); - - t_lifetime = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, lifetime, now); - if (!t_lifetime) { - NM_SET_OUT (out_preferred, 0); - return 0; - } - - t_preferred = nm_utils_lifetime_rebase_relative_time_on_now (timestamp, preferred, now); - - NM_SET_OUT (out_preferred, MIN (t_preferred, t_lifetime)); - - /* Assert that non-permanent addresses have a (positive) @timestamp. nm_utils_lifetime_rebase_relative_time_on_now() - * treats addresses with timestamp 0 as *now*. Addresses passed to _address_get_lifetime() always - * should have a valid @timestamp, otherwise on every re-sync, their lifetime will be extended anew. - */ - g_return_val_if_fail ( timestamp != 0 - || ( lifetime == NM_PLATFORM_LIFETIME_PERMANENT - && preferred == NM_PLATFORM_LIFETIME_PERMANENT), t_lifetime); - g_return_val_if_fail (t_preferred <= t_lifetime, t_lifetime); - - return t_lifetime; + guint32 t_lifetime, t_preferred; + + nm_assert(now >= 0); + + if (timestamp == 0 && lifetime == 0) { + /* We treat lifetime==0 && timestamp==0 addresses as permanent addresses to allow easy + * creation of such addresses (without requiring to set the lifetime fields to + * NM_PLATFORM_LIFETIME_PERMANENT). The real lifetime==0 addresses (E.g. DHCP6 telling us + * to drop an address will have timestamp set. + */ + NM_SET_OUT(out_preferred, NM_PLATFORM_LIFETIME_PERMANENT); + g_return_val_if_fail(preferred == 0, NM_PLATFORM_LIFETIME_PERMANENT); + return NM_PLATFORM_LIFETIME_PERMANENT; + } + + if (now <= 0) + now = nm_utils_get_monotonic_timestamp_sec(); + + t_lifetime = nm_utils_lifetime_rebase_relative_time_on_now(timestamp, lifetime, now); + if (!t_lifetime) { + NM_SET_OUT(out_preferred, 0); + return 0; + } + + t_preferred = nm_utils_lifetime_rebase_relative_time_on_now(timestamp, preferred, now); + + NM_SET_OUT(out_preferred, MIN(t_preferred, t_lifetime)); + + /* Assert that non-permanent addresses have a (positive) @timestamp. nm_utils_lifetime_rebase_relative_time_on_now() + * treats addresses with timestamp 0 as *now*. Addresses passed to _address_get_lifetime() always + * should have a valid @timestamp, otherwise on every re-sync, their lifetime will be extended anew. + */ + g_return_val_if_fail(timestamp != 0 + || (lifetime == NM_PLATFORM_LIFETIME_PERMANENT + && preferred == NM_PLATFORM_LIFETIME_PERMANENT), + t_lifetime); + g_return_val_if_fail(t_preferred <= t_lifetime, t_lifetime); + + return t_lifetime; } const char * -nm_utils_dnsmasq_status_to_string (int status, char *dest, gsize size) +nm_utils_dnsmasq_status_to_string(int status, char *dest, gsize size) { - const char *msg; - - nm_utils_to_string_buffer_init (&dest, &size); - - 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"; - else if (status >= 11) { - g_snprintf (dest, size, "Lease script failed with error %d", status - 10); - return dest; - } - else - msg = "Unknown problem"; - - g_snprintf (dest, size, "%s (%d)", msg, status); - return dest; + const char *msg; + + nm_utils_to_string_buffer_init(&dest, &size); + + 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"; + else if (status >= 11) { + g_snprintf(dest, size, "Lease script failed with error %d", status - 10); + return dest; + } else + msg = "Unknown problem"; + + g_snprintf(dest, size, "%s (%d)", msg, status); + return dest; } /** @@ -3964,42 +4395,42 @@ nm_utils_dnsmasq_status_to_string (int status, char *dest, gsize size) * append them to @domains. */ void -nm_utils_get_reverse_dns_domains_ip4 (guint32 addr, guint8 plen, GPtrArray *domains) +nm_utils_get_reverse_dns_domains_ip4(guint32 addr, guint8 plen, GPtrArray *domains) { - guint32 ip, ip2, mask; - guchar *p; - guint octets; - guint i; - gsize len0, len; - char *str, *s; - - g_return_if_fail (domains); - g_return_if_fail (plen <= 32); - - if (!plen) - return; - - octets = (plen - 1) / 8 + 1; - ip = ntohl (addr); - mask = 0xFFFFFFFF << (32 - plen); - ip &= mask; - ip2 = ip; - - len0 = NM_STRLEN ("in-addr.arpa") + (4 * octets) + 1; - while ((ip2 & mask) == ip) { - addr = htonl (ip2); - p = (guchar *) &addr; - - len = len0; - str = s = g_malloc (len); - for (i = octets; i > 0; i--) - nm_utils_strbuf_append (&s, &len, "%u.", p[i - 1] & 0xff); - nm_utils_strbuf_append_str (&s, &len, "in-addr.arpa"); - - g_ptr_array_add (domains, str); - - ip2 += 1 << ((32 - plen) & ~7); - } + guint32 ip, ip2, mask; + guchar *p; + guint octets; + guint i; + gsize len0, len; + char * str, *s; + + g_return_if_fail(domains); + g_return_if_fail(plen <= 32); + + if (!plen) + return; + + octets = (plen - 1) / 8 + 1; + ip = ntohl(addr); + mask = 0xFFFFFFFF << (32 - plen); + ip &= mask; + ip2 = ip; + + len0 = NM_STRLEN("in-addr.arpa") + (4 * octets) + 1; + while ((ip2 & mask) == ip) { + addr = htonl(ip2); + p = (guchar *) &addr; + + len = len0; + str = s = g_malloc(len); + for (i = octets; i > 0; i--) + nm_utils_strbuf_append(&s, &len, "%u.", p[i - 1] & 0xff); + nm_utils_strbuf_append_str(&s, &len, "in-addr.arpa"); + + g_ptr_array_add(domains, str); + + ip2 += 1 << ((32 - plen) & ~7); + } } /** @@ -4012,613 +4443,679 @@ nm_utils_get_reverse_dns_domains_ip4 (guint32 addr, guint8 plen, GPtrArray *doma * append them to @domains. */ void -nm_utils_get_reverse_dns_domains_ip6 (const struct in6_addr *ip, guint8 plen, GPtrArray *domains) +nm_utils_get_reverse_dns_domains_ip6(const struct in6_addr *ip, guint8 plen, GPtrArray *domains) { - struct in6_addr addr; - guint nibbles, bits, entries; - int i, j; - gsize len0, len; - char *str, *s; + struct in6_addr addr; + guint nibbles, bits, entries; + int i, j; + gsize len0, len; + char * str, *s; - g_return_if_fail (domains); - g_return_if_fail (plen <= 128); + g_return_if_fail(domains); + g_return_if_fail(plen <= 128); - if (!plen) - return; + if (!plen) + return; - memcpy (&addr, ip, sizeof (struct in6_addr)); - nm_utils_ip6_address_clear_host_address (&addr, NULL, plen); + memcpy(&addr, ip, sizeof(struct in6_addr)); + nm_utils_ip6_address_clear_host_address(&addr, NULL, plen); - /* 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); + /* 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; + 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 (i = 0; i < entries; i++) { + len = len0; + str = s = g_malloc(len); - for (j = nibbles - 1; j >= 0; j--) - nm_utils_strbuf_append (&s, - &len, - "%x.", - (addr.s6_addr[j / 2] >> N_SHIFT (j)) & 0xf); - nm_utils_strbuf_append_str (&s, &len, "ip6.arpa"); + for (j = nibbles - 1; j >= 0; j--) + nm_utils_strbuf_append(&s, &len, "%x.", (addr.s6_addr[j / 2] >> N_SHIFT(j)) & 0xf); + nm_utils_strbuf_append_str(&s, &len, "ip6.arpa"); - g_ptr_array_add (domains, str); + g_ptr_array_add(domains, str); - addr.s6_addr[(nibbles - 1) / 2] += 1 << N_SHIFT (nibbles - 1); - } + addr.s6_addr[(nibbles - 1) / 2] += 1 << N_SHIFT(nibbles - 1); + } #undef N_SHIFT } struct plugin_info { - char *path; - struct stat st; + char * path; + struct stat st; }; static int -read_device_factory_paths_sort_fcn (gconstpointer a, gconstpointer b) +read_device_factory_paths_sort_fcn(gconstpointer a, gconstpointer b) { - const struct plugin_info *da = a; - const struct plugin_info *db = b; - time_t ta, tb; - - ta = MAX (da->st.st_mtime, da->st.st_ctime); - tb = MAX (db->st.st_mtime, db->st.st_ctime); - - if (ta < tb) - return 1; - if (ta > tb) - return -1; - return 0; + const struct plugin_info *da = a; + const struct plugin_info *db = b; + time_t ta, tb; + + ta = MAX(da->st.st_mtime, da->st.st_ctime); + tb = MAX(db->st.st_mtime, db->st.st_ctime); + + if (ta < tb) + return 1; + if (ta > tb) + return -1; + return 0; } gboolean -nm_utils_validate_plugin (const char *path, struct stat *st, GError **error) +nm_utils_validate_plugin(const char *path, struct stat *st, GError **error) { - g_return_val_if_fail (path, FALSE); - g_return_val_if_fail (st, FALSE); - g_return_val_if_fail (!error || !*error, FALSE); - - if (!S_ISREG (st->st_mode)) { - g_set_error_literal (error, - NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - "not a regular file"); - return FALSE; - } - - if (st->st_uid != 0) { - g_set_error_literal (error, - NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - "file has invalid owner (should be root)"); - return FALSE; - } - - if (st->st_mode & (S_IWGRP | S_IWOTH | S_ISUID)) { - g_set_error_literal (error, - NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, - "file has invalid permissions"); - return FALSE; - } - - return TRUE; + g_return_val_if_fail(path, FALSE); + g_return_val_if_fail(st, FALSE); + g_return_val_if_fail(!error || !*error, FALSE); + + if (!S_ISREG(st->st_mode)) { + g_set_error_literal(error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, "not a regular file"); + return FALSE; + } + + if (st->st_uid != 0) { + g_set_error_literal(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "file has invalid owner (should be root)"); + return FALSE; + } + + if (st->st_mode & (S_IWGRP | S_IWOTH | S_ISUID)) { + g_set_error_literal(error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "file has invalid permissions"); + return FALSE; + } + + return TRUE; } char ** -nm_utils_read_plugin_paths (const char *dirname, const char *prefix) +nm_utils_read_plugin_paths(const char *dirname, const char *prefix) { - GDir *dir; - GError *error = NULL; - const char *item; - GArray *paths; - char **result; - guint i; - - g_return_val_if_fail (dirname, NULL); - g_return_val_if_fail (prefix, NULL); - - dir = g_dir_open (dirname, 0, &error); - if (!dir) { - nm_log_warn (LOGD_CORE, "device plugin: failed to open directory %s: %s", - dirname, - error->message); - g_clear_error (&error); - return NULL; - } - - paths = g_array_new (FALSE, FALSE, sizeof (struct plugin_info)); - - while ((item = g_dir_read_name (dir))) { - int errsv; - struct plugin_info data; - - if (!g_str_has_prefix (item, prefix)) - continue; - if (!g_str_has_suffix (item, ".so")) - continue; - - data.path = g_build_filename (dirname, item, NULL); - - if (stat (data.path, &data.st) != 0) { - errsv = errno; - nm_log_warn (LOGD_CORE, - "plugin: skip invalid file %s (error during stat: %s)", - data.path, nm_strerror_native (errsv)); - goto skip; - } - - 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); - goto skip; - } - - g_array_append_val (paths, data); - continue; + GDir * dir; + GError * error = NULL; + const char *item; + GArray * paths; + char ** result; + guint i; + + g_return_val_if_fail(dirname, NULL); + g_return_val_if_fail(prefix, NULL); + + dir = g_dir_open(dirname, 0, &error); + if (!dir) { + nm_log_warn(LOGD_CORE, + "device plugin: failed to open directory %s: %s", + dirname, + error->message); + g_clear_error(&error); + return NULL; + } + + paths = g_array_new(FALSE, FALSE, sizeof(struct plugin_info)); + + while ((item = g_dir_read_name(dir))) { + int errsv; + struct plugin_info data; + + if (!g_str_has_prefix(item, prefix)) + continue; + if (!g_str_has_suffix(item, ".so")) + continue; + + data.path = g_build_filename(dirname, item, NULL); + + if (stat(data.path, &data.st) != 0) { + errsv = errno; + nm_log_warn(LOGD_CORE, + "plugin: skip invalid file %s (error during stat: %s)", + data.path, + nm_strerror_native(errsv)); + goto skip; + } + + 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); + goto skip; + } + + g_array_append_val(paths, data); + continue; skip: - g_free (data.path); - } - g_dir_close (dir); + g_free(data.path); + } + g_dir_close(dir); - /* sort filenames by modification time. */ - g_array_sort (paths, read_device_factory_paths_sort_fcn); + /* sort filenames by modification time. */ + g_array_sort(paths, read_device_factory_paths_sort_fcn); - result = g_new (char *, paths->len + 1); - for (i = 0; i < paths->len; i++) - result[i] = g_array_index (paths, struct plugin_info, i).path; - result[i] = NULL; + result = g_new(char *, paths->len + 1); + for (i = 0; i < paths->len; i++) + result[i] = g_array_index(paths, struct plugin_info, i).path; + result[i] = NULL; - g_array_free (paths, TRUE); - return result; + g_array_free(paths, TRUE); + return result; } char * -nm_utils_format_con_diff_for_audit (GHashTable *diff) +nm_utils_format_con_diff_for_audit(GHashTable *diff) { - GHashTable *setting_diff; - char *setting_name, *prop_name; - GHashTableIter iter, iter2; - GString *str; + GHashTable * setting_diff; + char * setting_name, *prop_name; + GHashTableIter iter, iter2; + GString * str; - str = g_string_sized_new (32); - g_hash_table_iter_init (&iter, diff); + 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; + 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); + 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); - } + 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'; + if (str->len) + str->str[str->len - 1] = '\0'; - return g_string_free (str, FALSE); + return g_string_free(str, FALSE); } const char * -nm_utils_parse_dns_domain (const char *domain, gboolean *is_routing) +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); + g_return_val_if_fail(domain, NULL); + g_return_val_if_fail(domain[0], NULL); - return domain; -} - -/*****************************************************************************/ + if (domain[0] == '~') { + domain++; + NM_SET_OUT(is_routing, TRUE); + } else + NM_SET_OUT(is_routing, FALSE); -GVariant * -nm_utils_strdict_to_variant (GHashTable *options) -{ - GVariantBuilder builder; - gs_free NMUtilsNamedValue *values = NULL; - guint i; - guint n; - - values = nm_utils_named_values_from_str_dict (options, &n); - - g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); - for (i = 0; i < n; i++) { - g_variant_builder_add (&builder, - "{sv}", - values[i].name, - g_variant_new_string (values[i].value_str)); - } - return g_variant_builder_end (&builder); + return domain; } /*****************************************************************************/ static guint32 -get_max_rate_ht_20 (int mcs) +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; + 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) +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; + 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) +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; + 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) +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; + 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) +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; + 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) +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; + 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) +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; + 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) +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; + 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) +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; + 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) +get_max_rate_vht(const guint8 *bytes, guint len, guint32 *out_maxrate) { - guint32 mcs, m; - guint8 vht_cap, tx_map; - - /* https://tda802dot11.blogspot.it/2014/10/vht-capabilities-element-vht.html - * http://chimera.labs.oreilly.com/books/1234000001739/ch03.html#management_frames */ - - if (len != 12) - return FALSE; - - vht_cap = bytes[0]; - tx_map = bytes[8]; - - /* Check for mcs rates 8 and 9 support */ - if (tx_map & 0x2a) - mcs = 9; - else if (tx_map & 0x15) - mcs = 8; - else - mcs = 7; - - /* Check for 160Mhz wide channel support and - * spatial stream support */ - if (vht_cap & (1 << 2)) { - if (tx_map & 0x30) - m = get_max_rate_vht_160_ss3 (mcs); - else if (tx_map & 0x0C) - m = get_max_rate_vht_160_ss2 (mcs); - else - m = get_max_rate_vht_160_ss1 (mcs); - } else { - if (tx_map & 0x30) - m = get_max_rate_vht_80_ss3 (mcs); - else if (tx_map & 0x0C) - m = get_max_rate_vht_80_ss2 (mcs); - else - m = get_max_rate_vht_80_ss1 (mcs); - } - - *out_maxrate = m; - return TRUE; + guint32 mcs, m; + guint8 vht_cap, tx_map; + + /* https://tda802dot11.blogspot.it/2014/10/vht-capabilities-element-vht.html + * http://chimera.labs.oreilly.com/books/1234000001739/ch03.html#management_frames */ + + if (len != 12) + return FALSE; + + vht_cap = bytes[0]; + tx_map = bytes[8]; + + /* Check for mcs rates 8 and 9 support */ + if (tx_map & 0x2a) + mcs = 9; + else if (tx_map & 0x15) + mcs = 8; + else + mcs = 7; + + /* Check for 160Mhz wide channel support and + * spatial stream support */ + if (vht_cap & (1 << 2)) { + if (tx_map & 0x30) + m = get_max_rate_vht_160_ss3(mcs); + else if (tx_map & 0x0C) + m = get_max_rate_vht_160_ss2(mcs); + else + m = get_max_rate_vht_160_ss1(mcs); + } else { + if (tx_map & 0x30) + m = get_max_rate_vht_80_ss3(mcs); + else if (tx_map & 0x0C) + m = get_max_rate_vht_80_ss2(mcs); + else + m = get_max_rate_vht_80_ss1(mcs); + } + + *out_maxrate = m; + return TRUE; } /* Management Frame Information Element IDs, ieee80211_eid */ -#define WLAN_EID_HT_CAPABILITY 45 -#define WLAN_EID_VHT_CAPABILITY 191 -#define WLAN_EID_VENDOR_SPECIFIC 221 +#define WLAN_EID_HT_CAPABILITY 45 +#define WLAN_EID_VHT_CAPABILITY 191 +#define WLAN_EID_VENDOR_SPECIFIC 221 void -nm_wifi_utils_parse_ies (const guint8 *bytes, - gsize len, - guint32 *out_max_rate, - gboolean *out_metered, - gboolean *out_owe_transition_mode) +nm_wifi_utils_parse_ies(const guint8 *bytes, + gsize len, + guint32 * out_max_rate, + gboolean * out_metered, + gboolean * out_owe_transition_mode) { - guint8 id, elem_len; - guint32 m; - - NM_SET_OUT (out_max_rate, 0); - NM_SET_OUT (out_metered, FALSE); - NM_SET_OUT (out_owe_transition_mode, FALSE); - - while (len) { - if (len < 2) - break; - - id = *bytes++; - elem_len = *bytes++; - len -= 2; - - if (elem_len > len) - break; - - switch (id) { - case WLAN_EID_HT_CAPABILITY: - if (out_max_rate) { - if (get_max_rate_ht (bytes, elem_len, &m)) - *out_max_rate = NM_MAX (*out_max_rate, m); - } - break; - case WLAN_EID_VHT_CAPABILITY: - if (out_max_rate) { - if (get_max_rate_vht (bytes, elem_len, &m)) - *out_max_rate = NM_MAX (*out_max_rate, m); - } - break; - case WLAN_EID_VENDOR_SPECIFIC: - if ( len == 8 - && bytes[0] == 0x00 /* OUI: Microsoft */ - && bytes[1] == 0x50 - && bytes[2] == 0xf2 - && bytes[3] == 0x11) /* OUI type: Network cost */ - NM_SET_OUT (out_metered, (bytes[7] > 1)); /* Cost level > 1 */ - if ( elem_len >= 10 - && bytes[0] == 0x50 /* OUI: WiFi Alliance */ - && bytes[1] == 0x6f - && bytes[2] == 0x9a - && bytes[3] == 0x1c) /* OUI type: OWE Transition Mode */ - NM_SET_OUT (out_owe_transition_mode, TRUE); - break; - } - - len -= elem_len; - bytes += elem_len; - } + guint8 id, elem_len; + guint32 m; + + NM_SET_OUT(out_max_rate, 0); + NM_SET_OUT(out_metered, FALSE); + NM_SET_OUT(out_owe_transition_mode, FALSE); + + while (len) { + if (len < 2) + break; + + id = *bytes++; + elem_len = *bytes++; + len -= 2; + + if (elem_len > len) + break; + + switch (id) { + case WLAN_EID_HT_CAPABILITY: + if (out_max_rate) { + if (get_max_rate_ht(bytes, elem_len, &m)) + *out_max_rate = NM_MAX(*out_max_rate, m); + } + break; + case WLAN_EID_VHT_CAPABILITY: + if (out_max_rate) { + if (get_max_rate_vht(bytes, elem_len, &m)) + *out_max_rate = NM_MAX(*out_max_rate, m); + } + break; + case WLAN_EID_VENDOR_SPECIFIC: + if (len == 8 && bytes[0] == 0x00 /* OUI: Microsoft */ + && bytes[1] == 0x50 && bytes[2] == 0xf2 + && bytes[3] == 0x11) /* OUI type: Network cost */ + NM_SET_OUT(out_metered, (bytes[7] > 1)); /* Cost level > 1 */ + if (elem_len >= 10 && bytes[0] == 0x50 /* OUI: WiFi Alliance */ + && bytes[1] == 0x6f && bytes[2] == 0x9a + && bytes[3] == 0x1c) /* OUI type: OWE Transition Mode */ + NM_SET_OUT(out_owe_transition_mode, TRUE); + break; + } + + len -= elem_len; + bytes += elem_len; + } } /*****************************************************************************/ guint8 -nm_wifi_utils_level_to_quality (int val) +nm_wifi_utils_level_to_quality(int val) { - if (val < 0) { - /* Assume dBm already; rough conversion: best = -40, worst = -100 */ - val = abs (CLAMP (val, -100, -40) + 40); /* normalize to 0 */ - val = 100 - (int) ((100.0 * (double) val) / 60.0); - } else if (val > 110 && val < 256) { - /* assume old-style WEXT 8-bit unsigned signal level */ - val -= 256; /* subtract 256 to convert to dBm */ - val = abs (CLAMP (val, -100, -40) + 40); /* normalize to 0 */ - val = 100 - (int) ((100.0 * (double) val) / 60.0); - } else { - /* Assume signal is a "quality" percentage */ - } - - return CLAMP (val, 0, 100); + if (val < 0) { + /* Assume dBm already; rough conversion: best = -40, worst = -100 */ + val = abs(CLAMP(val, -100, -40) + 40); /* normalize to 0 */ + val = 100 - (int) ((100.0 * (double) val) / 60.0); + } else if (val > 110 && val < 256) { + /* assume old-style WEXT 8-bit unsigned signal level */ + val -= 256; /* subtract 256 to convert to dBm */ + val = abs(CLAMP(val, -100, -40) + 40); /* normalize to 0 */ + val = 100 - (int) ((100.0 * (double) val) / 60.0); + } else { + /* Assume signal is a "quality" percentage */ + } + + return CLAMP(val, 0, 100); } /*****************************************************************************/ -NM_UTILS_ENUM2STR_DEFINE (nm_icmpv6_router_pref_to_string, NMIcmpv6RouterPref, - NM_UTILS_ENUM2STR (NM_ICMPV6_ROUTER_PREF_LOW, "low"), - NM_UTILS_ENUM2STR (NM_ICMPV6_ROUTER_PREF_MEDIUM, "medium"), - NM_UTILS_ENUM2STR (NM_ICMPV6_ROUTER_PREF_HIGH, "high"), - NM_UTILS_ENUM2STR (NM_ICMPV6_ROUTER_PREF_INVALID, "invalid"), -); - -NM_UTILS_LOOKUP_STR_DEFINE (nm_activation_type_to_string, NMActivationType, - NM_UTILS_LOOKUP_DEFAULT_WARN ("(unknown)"), - 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"), -) +NM_UTILS_ENUM2STR_DEFINE(nm_icmpv6_router_pref_to_string, + NMIcmpv6RouterPref, + NM_UTILS_ENUM2STR(NM_ICMPV6_ROUTER_PREF_LOW, "low"), + NM_UTILS_ENUM2STR(NM_ICMPV6_ROUTER_PREF_MEDIUM, "medium"), + NM_UTILS_ENUM2STR(NM_ICMPV6_ROUTER_PREF_HIGH, "high"), + NM_UTILS_ENUM2STR(NM_ICMPV6_ROUTER_PREF_INVALID, "invalid"), ); + +NM_UTILS_LOOKUP_STR_DEFINE(nm_activation_type_to_string, + NMActivationType, + NM_UTILS_LOOKUP_DEFAULT_WARN("(unknown)"), + 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"), ); |