diff options
Diffstat (limited to 'src/platform')
34 files changed, 1709 insertions, 771 deletions
diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 40a85390..967aaee8 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform-fake.c - Fake platform interaction code for testing NetworkManager * * This program is free software; you can redistribute it and/or modify @@ -276,9 +275,9 @@ link_add_pre (NMPlatform *platform, o->_link.netlink.is_in_netlink = TRUE; if (address) { - g_assert (address_len > 0 && address_len <= sizeof (link->addr.data)); - memcpy (link->addr.data, address, address_len); - link->addr.len = address_len; + g_assert (address_len > 0 && address_len <= sizeof (link->l_address.data)); + memcpy (link->l_address.data, address, address_len); + link->l_address.len = address_len; } else g_assert (address_len == 0); @@ -584,9 +583,9 @@ link_set_address (NMPlatform *platform, int ifindex, gconstpointer addr, size_t return -NME_PL_EXISTS; obj_tmp = nmp_object_clone (device->obj, FALSE); - obj_tmp->link.addr.len = len; - memset (obj_tmp->link.addr.data, 0, sizeof (obj_tmp->link.addr.data)); - memcpy (obj_tmp->link.addr.data, addr, len); + obj_tmp->link.l_address.len = len; + memset (obj_tmp->link.l_address.data, 0, sizeof (obj_tmp->link.l_address.data)); + memcpy (obj_tmp->link.l_address.data, addr, len); link_set_obj (platform, device, obj_tmp); return 0; diff --git a/src/platform/nm-fake-platform.h b/src/platform/nm-fake-platform.h index 8f42d11a..d5be8c65 100644 --- a/src/platform/nm-fake-platform.h +++ b/src/platform/nm-fake-platform.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-fake-platform.h - Fake platform interaction code for testing NetworkManager * * This program is free software; you can redistribute it and/or modify diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index d4b01152..e2e1e581 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-linux-platform.c - Linux kernel & udev network configuration layer * * This program is free software; you can redistribute it and/or modify @@ -34,6 +33,7 @@ #include <linux/if_tun.h> #include <linux/if_tunnel.h> #include <linux/ip6_tunnel.h> +#include <linux/tc_act/tc_mirred.h> #include <netinet/icmp6.h> #include <netinet/in.h> #include <poll.h> @@ -84,6 +84,13 @@ enum { /*****************************************************************************/ +/* Compat with older kernels. */ + +#define TCA_FQ_CODEL_CE_THRESHOLD 7 +#define TCA_FQ_CODEL_MEMORY_LIMIT 9 + +/*****************************************************************************/ + #define VLAN_FLAG_MVRP 0x8 /*****************************************************************************/ @@ -732,29 +739,27 @@ _timestamp_nl_to_ms (guint32 timestamp_nl, gint64 monotonic_ms) static guint32 _addrtime_timestamp_to_nm (guint32 timestamp, gint32 *out_now_nm) { - struct timespec tp; - gint64 now_nl, now_nm, result; - int err; + gint64 now_nl; + gint64 now_nm; + gint64 result; /* timestamp is unset. Default to 1. */ if (!timestamp) { - if (out_now_nm) - *out_now_nm = 0; + NM_SET_OUT (out_now_nm, 0); return 1; } /* do all the calculations in milliseconds scale */ - err = clock_gettime (CLOCK_MONOTONIC, &tp); - g_assert (err == 0); now_nm = nm_utils_get_monotonic_timestamp_ms (); - now_nl = (((gint64) tp.tv_sec) * ((gint64) 1000)) + - (tp.tv_nsec / (NM_UTILS_NS_PER_SECOND/1000)); + now_nl = nm_utils_clock_gettime_ms (CLOCK_MONOTONIC); + + nm_assert (now_nm >= 1000); + nm_assert (now_nl >= 0); result = now_nm - (now_nl - _timestamp_nl_to_ms (timestamp, now_nl)); - if (out_now_nm) - *out_now_nm = now_nm / 1000; + NM_SET_OUT (out_now_nm, now_nm / 1000); /* converting the timestamp into nm_utils_get_monotonic_timestamp_ms() scale is * a good guess but fails in the following situations: @@ -2579,6 +2584,25 @@ link_wireguard_change (NMPlatform *platform, /*****************************************************************************/ +static void +_nmp_link_address_set (NMPLinkAddress *dst, + const struct nlattr *nla) +{ + *dst = (NMPLinkAddress) { + .len = 0, + }; + if (nla) { + int l = nla_len (nla); + + if ( l > 0 + && l <= NM_UTILS_HWADDR_LEN_MAX) { + G_STATIC_ASSERT_EXPR (sizeof (dst->data) == NM_UTILS_HWADDR_LEN_MAX); + memcpy (dst->data, nla_data (nla), l); + dst->len = l; + } + } +} + /* Copied and heavily modified from libnl3's link_msg_parser(). */ static NMPObject * _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr *nlh, gboolean id_only) @@ -2622,6 +2646,7 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr const NMPObject *link_cached = NULL; const NMPObject *lnk_data = NULL; gboolean address_complete_from_cache = TRUE; + gboolean broadcast_complete_from_cache = TRUE; gboolean lnk_data_complete_from_cache = TRUE; gboolean need_ext_data = FALSE; gboolean af_inet6_token_valid = FALSE; @@ -2720,16 +2745,15 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr } if (tb[IFLA_ADDRESS]) { - int l = nla_len (tb[IFLA_ADDRESS]); - - if (l > 0 && l <= NM_UTILS_HWADDR_LEN_MAX) { - G_STATIC_ASSERT (NM_UTILS_HWADDR_LEN_MAX == sizeof (obj->link.addr.data)); - memcpy (obj->link.addr.data, nla_data (tb[IFLA_ADDRESS]), l); - obj->link.addr.len = l; - } + _nmp_link_address_set (&obj->link.l_address, tb[IFLA_ADDRESS]); address_complete_from_cache = FALSE; } + if (tb[IFLA_BROADCAST]) { + _nmp_link_address_set (&obj->link.l_broadcast, tb[IFLA_BROADCAST]); + broadcast_complete_from_cache = FALSE; + } + if (tb[IFLA_AF_SPEC]) { struct nlattr *af_attr; int remaining; @@ -2803,6 +2827,7 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr && ( lnk_data_complete_from_cache || need_ext_data || address_complete_from_cache + || broadcast_complete_from_cache || !af_inet6_token_valid || !af_inet6_addr_gen_mode_valid || !tb[IFLA_STATS64])) { @@ -2832,7 +2857,9 @@ _new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr } if (address_complete_from_cache) - obj->link.addr = link_cached->link.addr; + obj->link.l_address = link_cached->link.l_address; + if (broadcast_complete_from_cache) + obj->link.l_broadcast = link_cached->link.l_broadcast; if (!af_inet6_token_valid) obj->link.inet6_token = link_cached->link.inet6_token; if (!af_inet6_addr_gen_mode_valid) @@ -3481,6 +3508,7 @@ _new_from_nl_qdisc (struct nlmsghdr *nlh, gboolean id_only) { static const struct nla_policy policy[] = { [TCA_KIND] = { .type = NLA_STRING }, + [TCA_OPTIONS] = { .type = NLA_NESTED }, }; struct nlattr *tb[G_N_ELEMENTS (policy)]; const struct tcmsg *tcm; @@ -3506,6 +3534,50 @@ _new_from_nl_qdisc (struct nlmsghdr *nlh, gboolean id_only) obj->qdisc.parent = tcm->tcm_parent; obj->qdisc.info = tcm->tcm_info; + if (nm_streq0 (obj->qdisc.kind, "fq_codel")) { + obj->qdisc.fq_codel.memory_limit = NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET; + obj->qdisc.fq_codel.ce_threshold = NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED; + } + + if (tb[TCA_OPTIONS]) { + struct nlattr *options_attr; + int remaining; + + nla_for_each_nested (options_attr, tb[TCA_OPTIONS], remaining) { + if (nla_len (options_attr) < sizeof (uint32_t)) + continue; + + if (nm_streq0 (obj->qdisc.kind, "fq_codel")) { + switch (nla_type (options_attr)) { + case TCA_FQ_CODEL_LIMIT: + obj->qdisc.fq_codel.limit = nla_get_u32 (options_attr); + break; + case TCA_FQ_CODEL_FLOWS: + obj->qdisc.fq_codel.flows = nla_get_u32 (options_attr); + break; + case TCA_FQ_CODEL_TARGET: + obj->qdisc.fq_codel.target = nla_get_u32 (options_attr); + break; + case TCA_FQ_CODEL_INTERVAL: + obj->qdisc.fq_codel.interval = nla_get_u32 (options_attr); + break; + case TCA_FQ_CODEL_QUANTUM: + obj->qdisc.fq_codel.quantum = nla_get_u32 (options_attr); + break; + case TCA_FQ_CODEL_CE_THRESHOLD: + obj->qdisc.fq_codel.ce_threshold = nla_get_u32 (options_attr); + break; + case TCA_FQ_CODEL_MEMORY_LIMIT: + obj->qdisc.fq_codel.memory_limit = nla_get_u32 (options_attr); + break; + case TCA_FQ_CODEL_ECN: + obj->qdisc.fq_codel.ecn = !!nla_get_u32 (options_attr); + break; + } + } + } + } + return obj; } @@ -3628,7 +3700,7 @@ _nl_msg_new_link_set_afspec (struct nl_msg *msg, return TRUE; nla_put_failure: - return FALSE; + g_return_val_if_reached (FALSE); } static gboolean @@ -3779,7 +3851,7 @@ _nl_msg_new_link_set_linkinfo_vlan (struct nl_msg *msg, return TRUE; nla_put_failure: - return FALSE; + g_return_val_if_reached (FALSE); } static struct nl_msg * @@ -4161,6 +4233,7 @@ _nl_msg_new_qdisc (int nlmsg_type, const NMPlatformQdisc *qdisc) { nm_auto_nlmsg struct nl_msg *msg = NULL; + struct nlattr *tc_options; const struct tcmsg tcm = { .tcm_family = qdisc->addr_family, .tcm_ifindex = qdisc->ifindex, @@ -4176,55 +4249,34 @@ _nl_msg_new_qdisc (int nlmsg_type, NLA_PUT_STRING (msg, TCA_KIND, qdisc->kind); - return g_steal_pointer (&msg); - -nla_put_failure: - g_return_val_if_reached (NULL); -} - -static gboolean -_add_action_simple (struct nl_msg *msg, - const NMPlatformActionSimple *simple) -{ - struct nlattr *act_options; - struct tc_defact sel = { 0, }; - - if (!(act_options = nla_nest_start (msg, TCA_ACT_OPTIONS))) - goto nla_put_failure; - - NLA_PUT (msg, TCA_DEF_PARMS, sizeof (sel), &sel); - NLA_PUT (msg, TCA_DEF_DATA, sizeof (simple->sdata), simple->sdata); - - nla_nest_end (msg, act_options); - - return TRUE; - -nla_put_failure: - return FALSE; -} - -static gboolean -_add_action (struct nl_msg *msg, - const NMPlatformAction *action) -{ - struct nlattr *prio; - - nm_assert (action || action->kind); - - if (!(prio = nla_nest_start (msg, 1 /* priority */))) + if (!(tc_options = nla_nest_start (msg, TCA_OPTIONS))) goto nla_put_failure; - NLA_PUT_STRING (msg, TCA_ACT_KIND, action->kind); - - if (nm_streq (action->kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) - _add_action_simple (msg, &action->simple); + if (strcmp (qdisc->kind, "fq_codel") == 0) { + if (qdisc->fq_codel.limit) + NLA_PUT_U32 (msg, TCA_FQ_CODEL_LIMIT, qdisc->fq_codel.limit); + if (qdisc->fq_codel.flows) + NLA_PUT_U32 (msg, TCA_FQ_CODEL_FLOWS, qdisc->fq_codel.flows); + if (qdisc->fq_codel.target) + NLA_PUT_U32 (msg, TCA_FQ_CODEL_TARGET, qdisc->fq_codel.target); + if (qdisc->fq_codel.interval) + NLA_PUT_U32 (msg, TCA_FQ_CODEL_INTERVAL, qdisc->fq_codel.interval); + if (qdisc->fq_codel.quantum) + NLA_PUT_U32 (msg, TCA_FQ_CODEL_QUANTUM, qdisc->fq_codel.quantum); + if (qdisc->fq_codel.ce_threshold != NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED) + NLA_PUT_U32 (msg, TCA_FQ_CODEL_CE_THRESHOLD, qdisc->fq_codel.ce_threshold); + if (qdisc->fq_codel.memory_limit != NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET) + NLA_PUT_U32 (msg, TCA_FQ_CODEL_MEMORY_LIMIT, qdisc->fq_codel.memory_limit); + if (qdisc->fq_codel.ecn) + NLA_PUT_U32 (msg, TCA_FQ_CODEL_ECN, qdisc->fq_codel.ecn); + } - nla_nest_end (msg, prio); + nla_nest_end (msg, tc_options); - return TRUE; + return g_steal_pointer (&msg); nla_put_failure: - return FALSE; + g_return_val_if_reached (NULL); } static struct nl_msg * @@ -4256,8 +4308,52 @@ _nl_msg_new_tfilter (int nlmsg_type, if (!(act_tab = nla_nest_start (msg, TCA_OPTIONS))) // 3 TCA_ACT_KIND TCA_ACT_KIND goto nla_put_failure; - if (tfilter->action.kind) - _add_action (msg, &tfilter->action); + if (tfilter->action.kind) { + const NMPlatformAction *action = &tfilter->action; + struct nlattr *prio; + struct nlattr *act_options; + + if (!(prio = nla_nest_start (msg, 1 /* priority */))) + goto nla_put_failure; + + NLA_PUT_STRING (msg, TCA_ACT_KIND, action->kind); + + if (nm_streq (action->kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) { + const NMPlatformActionSimple *simple = &action->simple; + struct tc_defact sel = { 0, }; + + if (!(act_options = nla_nest_start (msg, TCA_ACT_OPTIONS))) + goto nla_put_failure; + + NLA_PUT (msg, TCA_DEF_PARMS, sizeof (sel), &sel); + NLA_PUT (msg, TCA_DEF_DATA, sizeof (simple->sdata), simple->sdata); + + nla_nest_end (msg, act_options); + + } else if (nm_streq (action->kind, NM_PLATFORM_ACTION_KIND_MIRRED)) { + const NMPlatformActionMirred *mirred = &action->mirred; + struct tc_mirred sel = { 0, }; + + if (!(act_options = nla_nest_start (msg, TCA_ACT_OPTIONS))) + goto nla_put_failure; + + if (mirred->egress && mirred->redirect) + sel.eaction = TCA_EGRESS_REDIR; + else if (mirred->egress && mirred->mirror) + sel.eaction = TCA_EGRESS_MIRROR; + else if (mirred->ingress && mirred->redirect) + sel.eaction = TCA_INGRESS_REDIR; + else if (mirred->ingress && mirred->mirror) + sel.eaction = TCA_INGRESS_MIRROR; + sel.ifindex = mirred->ifindex; + + NLA_PUT (msg, TCA_MIRRED_PARMS, sizeof (sel), &sel); + + nla_nest_end (msg, act_options); + } + + nla_nest_end (msg, prio); + } nla_nest_end (msg, tc_options); @@ -4298,6 +4394,14 @@ _genl_sock (NMLinuxPlatform *platform) } \ } G_STMT_END +/*****************************************************************************/ + +/* core sysctl-set functions can be called from a non-main thread. + * Hence, we require locking from nm-logging. Indicate that by + * setting NM_THREAD_SAFE_ON_MAIN_THREAD to zero. */ +#undef NM_THREAD_SAFE_ON_MAIN_THREAD +#define NM_THREAD_SAFE_ON_MAIN_THREAD 0 + static void _log_dbg_sysctl_set_impl (NMPlatform *platform, const char *pathid, int dirfd, const char *path, const char *value) { @@ -4308,18 +4412,18 @@ _log_dbg_sysctl_set_impl (NMPlatform *platform, const char *pathid, int dirfd, c if (nm_utils_file_get_contents (dirfd, path, 1*1024*1024, NM_UTILS_FILE_GET_CONTENTS_FLAG_NONE, &contents, NULL, &error) < 0) { - _LOGD ("sysctl: setting '%s' to '%s' (current value cannot be read: %s)", pathid, value_escaped, error->message); + _LOGD ("sysctl: setting '%s' to '%s' (current value cannot be read: %s)", pathid ?: path, value_escaped, error->message); g_clear_error (&error); return; } g_strstrip (contents); if (nm_streq (contents, value)) - _LOGD ("sysctl: setting '%s' to '%s' (current value is identical)", pathid, value_escaped); + _LOGD ("sysctl: setting '%s' to '%s' (current value is identical)", pathid ?: path, value_escaped); else { gs_free char *contents_escaped = g_strescape (contents, NULL); - _LOGD ("sysctl: setting '%s' to '%s' (current value is '%s')", pathid, value_escaped, contents_escaped); + _LOGD ("sysctl: setting '%s' to '%s' (current value is '%s')", pathid ?: path, value_escaped, contents_escaped); } g_free (contents); } @@ -4332,9 +4436,12 @@ _log_dbg_sysctl_set_impl (NMPlatform *platform, const char *pathid, int dirfd, c } G_STMT_END static gboolean -sysctl_set (NMPlatform *platform, const char *pathid, int dirfd, const char *path, const char *value) +sysctl_set_internal (NMPlatform *platform, + const char *pathid, + int dirfd, + const char *path, + const char *value) { - nm_auto_pop_netns NMPNetns *netns = NULL; int fd, tries; gssize nwrote; gssize len; @@ -4342,17 +4449,7 @@ sysctl_set (NMPlatform *platform, const char *pathid, int dirfd, const char *pat gs_free char *actual_free = NULL; int errsv; - g_return_val_if_fail (path != NULL, FALSE); - g_return_val_if_fail (value != NULL, FALSE); - - ASSERT_SYSCTL_ARGS (pathid, dirfd, path); - if (dirfd < 0) { - if (!nm_platform_netns_push (platform, &netns)) { - errno = ENETDOWN; - return FALSE; - } - pathid = path; fd = open (path, O_WRONLY | O_TRUNC | O_CLOEXEC); @@ -4452,6 +4549,207 @@ sysctl_set (NMPlatform *platform, const char *pathid, int dirfd, const char *pat return TRUE; } +#undef NM_THREAD_SAFE_ON_MAIN_THREAD +#define NM_THREAD_SAFE_ON_MAIN_THREAD 1 + +/*****************************************************************************/ + +static gboolean +sysctl_set (NMPlatform *platform, + const char *pathid, + int dirfd, + const char *path, + const char *value) +{ + nm_auto_pop_netns NMPNetns *netns = NULL; + + g_return_val_if_fail (path, FALSE); + g_return_val_if_fail (value, FALSE); + + ASSERT_SYSCTL_ARGS (pathid, dirfd, path); + + if ( dirfd < 0 + && !nm_platform_netns_push (platform, &netns)) { + errno = ENETDOWN; + return FALSE; + } + + return sysctl_set_internal (platform, pathid, dirfd, path, value); +} + +typedef struct { + NMPlatform *platform; + char *pathid; + int dirfd; + char *path; + char **values; + GCancellable *cancellable; + NMPlatformAsyncCallback callback; + gpointer callback_data; +} SysctlAsyncInfo; + +static void +sysctl_async_info_free (SysctlAsyncInfo *info) +{ + g_object_unref (info->platform); + g_free (info->pathid); + if (info->dirfd >= 0) + nm_close (info->dirfd); + g_free (info->path); + g_strfreev (info->values); + g_object_unref (info->cancellable); + g_slice_free (SysctlAsyncInfo, info); +} + +static void +sysctl_async_cb (GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + NMPlatform *platform; + GTask *task = G_TASK (res); + SysctlAsyncInfo *info; + gs_free_error GError *error = NULL; + gs_free char *values_str = NULL; + + info = g_task_get_task_data (task); + + if (g_task_propagate_boolean (task, &error)) { + platform = info->platform; + _LOGD ("sysctl: successfully set-async '%s' to values '%s'", + info->pathid ?: info->path, + (values_str = g_strjoinv (", ", info->values))); + } + + if (info->callback) + info->callback (error, info->callback_data); +} + +static void +sysctl_async_thread_fn (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + nm_auto_pop_netns NMPNetns *netns = NULL; + SysctlAsyncInfo *info = task_data; + GError *error = NULL; + char **value; + + if (g_task_return_error_if_cancelled (task)) + return; + + if ( info->dirfd < 0 + && !nm_platform_netns_push (info->platform, &netns)) { + g_set_error_literal (&error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "sysctl: failed changing namespace"); + g_task_return_error (task, error); + return; + } + + for (value = info->values; *value; value++) { + if (!sysctl_set_internal (info->platform, + info->pathid, + info->dirfd, + info->path, + *value)) { + g_set_error (&error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "sysctl: failed setting '%s' to value '%s': %s", + info->pathid ?: info->path, + *value, + nm_strerror_native (errno)); + g_task_return_error (task, error); + return; + } + if (g_task_return_error_if_cancelled (task)) + return; + } + g_task_return_boolean (task, TRUE); +} + +static void +sysctl_set_async_return_idle (gpointer user_data, + GCancellable *cancellable) +{ + gs_unref_object NMPlatform *platform = NULL; + gs_free_error GError *cancelled_error = NULL; + gs_free_error GError *error = NULL; + NMPlatformAsyncCallback callback; + gpointer callback_data; + + nm_utils_user_data_unpack (user_data, &platform, &callback, &callback_data, &error); + g_cancellable_set_error_if_cancelled (cancellable, &cancelled_error); + callback (cancelled_error ?: error, callback_data); +} + +static void +sysctl_set_async (NMPlatform *platform, + const char *pathid, + int dirfd, + const char *path, + const char *const *values, + NMPlatformAsyncCallback callback, + gpointer data, + GCancellable *cancellable) +{ + SysctlAsyncInfo *info; + GTask *task; + int dirfd_dup, errsv; + gpointer packed; + GError *error = NULL; + + g_return_if_fail (platform); + g_return_if_fail (path); + g_return_if_fail (values && values[0]); + g_return_if_fail (cancellable); + g_return_if_fail (!data || callback); + + ASSERT_SYSCTL_ARGS (pathid, dirfd, path); + + if (dirfd >= 0) { + dirfd_dup = fcntl (dirfd, F_DUPFD_CLOEXEC, 0); + if (dirfd_dup < 0) { + if (!callback) + return; + errsv = errno; + g_set_error (&error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "sysctl: failure duplicating directory fd: %s", + nm_strerror_native (errsv)); + packed = nm_utils_user_data_pack (g_object_ref (platform), + callback, + data, + error); + nm_utils_invoke_on_idle (sysctl_set_async_return_idle, + packed, + cancellable); + return; + } + } else + dirfd_dup = -1; + + info = g_slice_new0 (SysctlAsyncInfo); + info->platform = g_object_ref (platform); + info->pathid = g_strdup (pathid); + info->dirfd = dirfd_dup; + info->path = g_strdup (path); + info->values = g_strdupv ((char **) values); + info->callback = callback; + info->callback_data = data; + info->cancellable = g_object_ref (cancellable); + + task = g_task_new (platform, cancellable, sysctl_async_cb, NULL); + g_task_set_task_data (task, info, (GDestroyNotify) sysctl_async_info_free); + g_task_set_return_on_cancel (task, FALSE); + g_task_run_in_thread (task, sysctl_async_thread_fn); + g_object_unref (task); +} + static GSList *sysctl_clear_cache_list; void @@ -5310,7 +5608,7 @@ cache_on_change (NMPlatform *platform, * Request it again. */ re_request_link = TRUE; } else if ( obj_new->link.type == NM_LINK_TYPE_ETHERNET - && obj_new->link.addr.len == 0) { + && obj_new->link.l_address.len == 0) { /* Due to a kernel bug, we sometimes receive spurious NEWLINK * messages after a wifi interface has disappeared. Since the * link is not present anymore we can't determine its type and @@ -6170,8 +6468,8 @@ retry: } else if ( NM_IN_SET (-((int) seq_result), ENFILE) && change_link_type == CHANGE_LINK_TYPE_SET_ADDRESS && (obj_cache = nmp_cache_lookup_link (nm_platform_get_cache (platform), ifindex)) - && obj_cache->link.addr.len == data->set_address.length - && memcmp (obj_cache->link.addr.data, data->set_address.address, data->set_address.length) == 0) { + && obj_cache->link.l_address.len == data->set_address.length + && memcmp (obj_cache->link.l_address.data, data->set_address.address, data->set_address.length) == 0) { /* workaround ENFILE which may be wrongly returned (bgo #770456). * If the MAC address is as expected, assume success? */ log_result = "success"; @@ -6409,7 +6707,8 @@ link_supports_carrier_detect (NMPlatform *platform, int ifindex) * us whether the device actually supports carrier detection in the first * place. We assume any device that does implements one of these two APIs. */ - return nmp_utils_ethtool_supports_carrier_detect (ifindex) || nmp_utils_mii_supports_carrier_detect (ifindex); + return nmp_utils_ethtool_supports_carrier_detect (ifindex) + || nmp_utils_mii_supports_carrier_detect (ifindex); } static gboolean @@ -6534,35 +6833,74 @@ nla_put_failure: g_return_val_if_reached (FALSE); } -static gboolean -link_set_sriov_params (NMPlatform *platform, - int ifindex, - guint num_vfs, - NMTernary autoprobe) +static void +sriov_idle_cb (gpointer user_data, + GCancellable *cancellable) +{ + gs_unref_object NMPlatform *platform = NULL; + gs_free_error GError *cancelled_error = NULL; + gs_free_error GError *error = NULL; + NMPlatformAsyncCallback callback; + gpointer callback_data; + + g_cancellable_set_error_if_cancelled (cancellable, &cancelled_error); + nm_utils_user_data_unpack (user_data, &platform, &error, &callback, &callback_data); + callback (cancelled_error ?: error, callback_data); +} + +static void +link_set_sriov_params_async (NMPlatform *platform, + int ifindex, + guint num_vfs, + NMTernary autoprobe, + NMPlatformAsyncCallback callback, + gpointer data, + GCancellable *cancellable) { nm_auto_pop_netns NMPNetns *netns = NULL; + gs_free_error GError *error = NULL; nm_auto_close int dirfd = -1; int current_autoprobe; - guint total; + guint i, total; gint64 current_num; char ifname[IFNAMSIZ]; + gpointer packed; + const char *values[3]; char buf[64]; - int errsv; - if (!nm_platform_netns_push (platform, &netns)) - return FALSE; + g_return_if_fail (callback || !data); + g_return_if_fail (cancellable); + + if (!nm_platform_netns_push (platform, &netns)) { + g_set_error_literal (&error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "couldn't change namespace"); + goto out_idle; + } dirfd = nm_platform_sysctl_open_netdir (platform, ifindex, ifname); - if (!dirfd) - return FALSE; + if (!dirfd) { + g_set_error_literal (&error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "couldn't open netdir"); + goto out_idle; + } total = nm_platform_sysctl_get_int_checked (platform, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "device/sriov_totalvfs"), 10, 0, G_MAXUINT, 0); - if (errno) - return FALSE; + if (errno) { + g_set_error (&error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "failed reading sriov_totalvfs value: %s", + nm_strerror_native (errno)); + goto out_idle; + } if (num_vfs > total) { _LOGW ("link: %d only supports %u VFs (requested %u)", ifindex, total, num_vfs); num_vfs = total; @@ -6594,23 +6932,7 @@ link_set_sriov_params (NMPlatform *platform, if ( current_num == num_vfs && (autoprobe == NM_TERNARY_DEFAULT || current_autoprobe == autoprobe)) - return TRUE; - - if (current_num != 0) { - /* We need to destroy all other VFs before changing any value */ - if (!nm_platform_sysctl_set (NM_PLATFORM_GET, - NMP_SYSCTL_PATHID_NETDIR (dirfd, - ifname, - "device/sriov_numvfs"), - "0")) { - errsv = errno; - _LOGW ("link: couldn't reset SR-IOV num_vfs: %s", nm_strerror_native (errsv)); - return FALSE; - } - } - - if (num_vfs == 0) - return TRUE; + goto out_idle; if ( NM_IN_SET (autoprobe, NM_TERNARY_TRUE, NM_TERNARY_FALSE) && current_autoprobe != autoprobe @@ -6619,22 +6941,40 @@ link_set_sriov_params (NMPlatform *platform, ifname, "device/sriov_drivers_autoprobe"), nm_sprintf_buf (buf, "%d", (int) autoprobe))) { - errsv = errno; - _LOGW ("link: couldn't set SR-IOV drivers-autoprobe to %d: %s", (int) autoprobe, nm_strerror_native (errsv)); - return FALSE; + g_set_error (&error, + NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "couldn't set SR-IOV drivers-autoprobe to %d: %s", + (int) autoprobe, nm_strerror_native (errno)); + goto out_idle; } - if (!nm_platform_sysctl_set (NM_PLATFORM_GET, - NMP_SYSCTL_PATHID_NETDIR (dirfd, - ifname, - "device/sriov_numvfs"), - nm_sprintf_buf (buf, "%u", num_vfs))) { - errsv = errno; - _LOGW ("link: couldn't set SR-IOV num_vfs to %d: %s", num_vfs, nm_strerror_native (errsv)); - return FALSE; - } + if (current_num == 0 && num_vfs == 0) + goto out_idle; - return TRUE; + i = 0; + if (current_num != 0) + values[i++] = "0"; + if (num_vfs != 0) + values[i++] = nm_sprintf_bufa (32, "%u", num_vfs); + values[i++] = NULL; + + sysctl_set_async (platform, + NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "device/sriov_numvfs"), + values, + callback, + data, + cancellable); + return; + +out_idle: + if (callback) { + packed = nm_utils_user_data_pack (g_object_ref (platform), + g_steal_pointer (&error), + callback, + data); + nm_utils_invoke_on_idle (sriov_idle_cb, packed, cancellable); + } } static gboolean @@ -8167,6 +8507,9 @@ qdisc_add (NMPlatform *platform, char s_buf[256]; nm_auto_nlmsg struct nl_msg *msg = NULL; + /* Note: @qdisc must not be copied or kept alive because the lifetime of qdisc.kind + * is undefined. */ + msg = _nl_msg_new_qdisc (RTM_NEWQDISC, flags, qdisc); event_handler_read_netlink (platform, FALSE); @@ -8208,6 +8551,9 @@ tfilter_add (NMPlatform *platform, char s_buf[256]; nm_auto_nlmsg struct nl_msg *msg = NULL; + /* Note: @tfilter must not be copied or kept alive because the lifetime of tfilter.kind + * and tfilter.action.kind is undefined. */ + msg = _nl_msg_new_tfilter (RTM_NEWTFILTER, flags, tfilter); event_handler_read_netlink (platform, FALSE); @@ -8913,6 +9259,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) object_class->finalize = finalize; platform_class->sysctl_set = sysctl_set; + platform_class->sysctl_set_async = sysctl_set_async; platform_class->sysctl_get = sysctl_get; platform_class->link_add = link_add; @@ -8936,7 +9283,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_get_permanent_address = link_get_permanent_address; platform_class->link_set_mtu = link_set_mtu; platform_class->link_set_name = link_set_name; - platform_class->link_set_sriov_params = link_set_sriov_params; + platform_class->link_set_sriov_params_async = link_set_sriov_params_async; platform_class->link_set_sriov_vfs = link_set_sriov_vfs; platform_class->link_set_bridge_vlans = link_set_bridge_vlans; diff --git a/src/platform/nm-linux-platform.h b/src/platform/nm-linux-platform.h index bff6c00c..f08f91aa 100644 --- a/src/platform/nm-linux-platform.h +++ b/src/platform/nm-linux-platform.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-linux-platform.h - Linux kernel & udev network configuration layer * * This program is free software; you can redistribute it and/or modify diff --git a/src/platform/nm-netlink.c b/src/platform/nm-netlink.c index 71506a2c..da009d01 100644 --- a/src/platform/nm-netlink.c +++ b/src/platform/nm-netlink.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify @@ -510,7 +509,7 @@ nla_nest_start (struct nl_msg *msg, int attrtype) { struct nlattr *start = (struct nlattr *) nlmsg_tail (msg->nm_nlh); - if (nla_put (msg, attrtype, 0, NULL) < 0) + if (nla_put (msg, NLA_F_NESTED | attrtype, 0, NULL) < 0) return NULL; return start; diff --git a/src/platform/nm-netlink.h b/src/platform/nm-netlink.h index b12fd243..c793f04a 100644 --- a/src/platform/nm-netlink.h +++ b/src/platform/nm-netlink.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify diff --git a/src/platform/nm-platform-private.h b/src/platform/nm-platform-private.h index b6c94baa..5a24eda0 100644 --- a/src/platform/nm-platform-private.h +++ b/src/platform/nm-platform-private.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify diff --git a/src/platform/nm-platform-utils.c b/src/platform/nm-platform-utils.c index 456871a7..bc9ec11d 100644 --- a/src/platform/nm-platform-utils.c +++ b/src/platform/nm-platform-utils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify @@ -69,37 +68,209 @@ nmp_utils_if_nametoindex (const char *ifname) typedef struct { int fd; - int ifindex; + const int ifindex; char ifname[IFNAMSIZ]; } SocketHandle; +#define SOCKET_HANDLE_INIT(_ifindex) \ + { \ + .fd = -1, \ + .ifindex = (_ifindex), \ + } + +static void +_nm_auto_socket_handle (SocketHandle *shandle) +{ + if (shandle->fd >= 0) + nm_close (shandle->fd); +} + +#define nm_auto_socket_handle nm_auto(_nm_auto_socket_handle) + +/*****************************************************************************/ + +typedef enum { + IOCTL_CALL_DATA_TYPE_NONE, + IOCTL_CALL_DATA_TYPE_IFRDATA, + IOCTL_CALL_DATA_TYPE_IFRU, +} IoctlCallDataType; + static int -socket_handle_init (SocketHandle *shandle, int ifindex) +_ioctl_call (const char *log_ioctl_type, + const char *log_subtype, + unsigned long int ioctl_request, + int ifindex, + int *inout_fd, + char *inout_ifname, + IoctlCallDataType edata_type, + gpointer edata, + gsize edata_size, + struct ifreq *out_ifreq) { - if (!nmp_utils_if_indextoname (ifindex, shandle->ifname)) { - shandle->ifindex = 0; - return -ENODEV; + nm_auto_close int fd_close = -1; + int fd; + int r; + gpointer edata_backup = NULL; + gs_free gpointer edata_backup_free = NULL; + guint try_count; + char known_ifnames[2][IFNAMSIZ]; + const char *failure_reason = NULL; + struct ifreq ifr; + + nm_assert (ifindex > 0); + nm_assert (NM_IN_SET (edata_type, IOCTL_CALL_DATA_TYPE_NONE, + IOCTL_CALL_DATA_TYPE_IFRDATA, + IOCTL_CALL_DATA_TYPE_IFRU)); + nm_assert (edata_type != IOCTL_CALL_DATA_TYPE_NONE || edata_size == 0); + nm_assert (edata_type != IOCTL_CALL_DATA_TYPE_IFRDATA || edata_size > 0); + nm_assert (edata_type != IOCTL_CALL_DATA_TYPE_IFRU || (edata_size > 0 && edata_size <= sizeof (ifr.ifr_ifru))); + nm_assert (edata_size == 0 || edata); + + /* open a file descriptor (or use the one provided). */ + if ( inout_fd + && *inout_fd >= 0) + fd = *inout_fd; + else { + fd = socket (PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (fd < 0) { + r = -NM_ERRNO_NATIVE (errno); + failure_reason = "failed creating socket or ioctl"; + goto out; + } + if (inout_fd) + *inout_fd = fd; + else + fd_close = fd; } - shandle->fd = socket (PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (shandle->fd < 0) { - shandle->ifindex = 0; - return -NM_ERRNO_NATIVE (errno); + /* resolve the ifindex to name (or use the one provided). */ + if ( inout_ifname + && inout_ifname[0]) + nm_utils_ifname_cpy (known_ifnames[0], inout_ifname); + else { + if (!nmp_utils_if_indextoname (ifindex, known_ifnames[0])) { + failure_reason = "cannot resolve ifindex"; + r = -ENODEV; + goto out; + } + if (inout_ifname) + nm_utils_ifname_cpy (inout_ifname, known_ifnames[0]); } - shandle->ifindex = ifindex; - return 0; -} + /* we might need to retry the request. Backup edata so that we can + * restore it on retry. */ + if (edata_size > 0) + edata_backup = nm_memdup_maybe_a (500, edata, edata_size, &edata_backup_free); -static void -socket_handle_destroy (SocketHandle *shandle) -{ - if (shandle->ifindex) { - shandle->ifindex = 0; - nm_close (shandle->fd); + try_count = 0; + +again: + { + const char *ifname = known_ifnames[try_count % 2]; + + nm_assert (ifindex > 0); + nm_assert (ifname && nm_utils_is_valid_iface_name (ifname, NULL)); + nm_assert (fd >= 0); + + memset (&ifr, 0, sizeof (ifr)); + nm_utils_ifname_cpy (ifr.ifr_name, ifname); + if (edata_type == IOCTL_CALL_DATA_TYPE_IFRDATA) + ifr.ifr_data = edata; + else if (edata_type == IOCTL_CALL_DATA_TYPE_IFRU) + memcpy (&ifr.ifr_ifru, edata, NM_MIN (edata_size, sizeof (ifr.ifr_ifru))); + + if (ioctl (fd, ioctl_request, &ifr) < 0) { + r = -NM_ERRNO_NATIVE (errno); + nm_log_trace (LOGD_PLATFORM, "%s[%d]: %s, %s: failed: %s", + log_ioctl_type, + ifindex, + log_subtype, + ifname, + nm_strerror_native (-r)); + } else { + r = 0; + nm_log_trace (LOGD_PLATFORM, "%s[%d]: %s, %s: success", + log_ioctl_type, + ifindex, + log_subtype, + ifname); + } + } + + try_count++; + + /* resolve the name again to see whether the ifindex still has the same name. */ + if (!nmp_utils_if_indextoname (ifindex, known_ifnames[try_count % 2])) { + /* we could not find the ifindex again. Probably the device just got + * removed. + * + * In both cases we return the error code we got from ioctl above. + * Either it failed because the device was gone already or it still + * managed to complete the call. In both cases, the error code is good. */ + failure_reason = "cannot resolve ifindex after ioctl call. Probably the device was just removed"; + goto out; } + + /* check whether the ifname changed in the meantime. If yes, would render the result + * invalid. Note that this cannot detect every race regarding renames, for example: + * + * - if_indextoname(#10) gives eth0 + * - rename(#10) => eth0_tmp + * - rename(#11) => eth0 + * - ioctl(eth0) (wrongly fetching #11, formerly eth1) + * - rename(#11) => eth_something + * - rename(#10) => eth0 + * - if_indextoname(#10) gives eth0 + */ + if (!nm_streq (known_ifnames[0], known_ifnames[1])) { + gboolean retry; + + /* we detected a possible(!) rename. + * + * For getters it's straight forward to just retry the call. + * + * For setters we also always retry. If our previous call operated on the right device, + * calling it again should have no bad effect (just setting the same thing more than once). + * + * The only potential bad thing is if there was a race involving swapping names, and we just + * set the ioctl option on the wrong device. But then the bad thing already happend and + * we cannot detect it (nor do anything about it). At least, we can retry and set the + * option on the right interface. */ + retry = (try_count < 5); + + nm_log_trace (LOGD_PLATFORM, "%s[%d]: %s: rename detected from \"%s\" to \"%s\". %s", + log_ioctl_type, + ifindex, + log_subtype, + known_ifnames[(try_count - 1) % 2], + known_ifnames[ try_count % 2], + retry + ? "Retry" + : "No retry"); + if (inout_ifname) + nm_utils_ifname_cpy (inout_ifname, known_ifnames[try_count % 2]); + if (retry) { + if (edata_size > 0) + memcpy (edata, edata_backup, edata_size); + goto again; + } + } + +out: + if (failure_reason) { + nm_log_trace (LOGD_PLATFORM, "%s[%d]: %s: %s: %s", + log_ioctl_type, + ifindex, + log_subtype, + failure_reason, + r < 0 + ? nm_strerror_native (-r) + : "assume success"); + } + if (r >= 0) + NM_SET_OUT (out_ifreq, ifr); + return r; } -#define nm_auto_socket_handle nm_auto(socket_handle_destroy) /****************************************************************************** * ethtool @@ -121,9 +292,13 @@ NM_UTILS_ENUM2STR_DEFINE_STATIC (_ethtool_cmd_to_string, guint32, ); static const char * -_ethtool_data_to_string (gconstpointer edata, char *buf, gsize len) +_ethtool_edata_to_string (gpointer edata, gsize edata_size, char *sbuf, gsize sbuf_len) { - return _ethtool_cmd_to_string (*((guint32 *) edata), buf, len); + nm_assert (edata); + nm_assert (edata_size >= sizeof (guint32)); + nm_assert ((((intptr_t) edata) % _nm_alignof (guint32)) == 0); + + return _ethtool_cmd_to_string (*((guint32 *) edata), sbuf, sbuf_len); } /*****************************************************************************/ @@ -136,56 +311,37 @@ _ethtool_data_to_string (gconstpointer edata, char *buf, gsize len) #endif static int -ethtool_call_handle (SocketHandle *shandle, gpointer edata) +_ethtool_call_handle (SocketHandle *shandle, gpointer edata, gsize edata_size) { - struct ifreq ifr = { - .ifr_data = edata, - }; char sbuf[50]; - int errsv; - - nm_assert (shandle); - nm_assert (shandle->ifindex); - nm_assert (shandle->ifname[0]); - nm_assert (strlen (shandle->ifname) < IFNAMSIZ); - nm_assert (edata); - memcpy (ifr.ifr_name, shandle->ifname, IFNAMSIZ); - if (ioctl (shandle->fd, SIOCETHTOOL, &ifr) < 0) { - errsv = errno; - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s, %s: failed: %s", - shandle->ifindex, - _ethtool_data_to_string (edata, sbuf, sizeof (sbuf)), - shandle->ifname, - nm_strerror_native (errsv)); - return -NM_ERRNO_NATIVE (errsv); - } - - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s, %s: success", - shandle->ifindex, - _ethtool_data_to_string (edata, sbuf, sizeof (sbuf)), - shandle->ifname); - return 0; + return _ioctl_call ("ethtool", + _ethtool_edata_to_string (edata, edata_size, sbuf, sizeof (sbuf)), + SIOCETHTOOL, + shandle->ifindex, + &shandle->fd, + shandle->ifname, + IOCTL_CALL_DATA_TYPE_IFRDATA, + edata, + edata_size, + NULL); } static int -ethtool_call_ifindex (int ifindex, gpointer edata) +_ethtool_call_once (int ifindex, gpointer edata, gsize edata_size) { - nm_auto_socket_handle SocketHandle shandle = { }; - int r; char sbuf[50]; - nm_assert (edata); - - if ((r = socket_handle_init (&shandle, ifindex)) < 0) { - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failed creating ethtool socket: %s", - ifindex, - _ethtool_data_to_string (edata, sbuf, sizeof (sbuf)), - nm_strerror_native (-r)); - return r; - } - - return ethtool_call_handle (&shandle, edata); + return _ioctl_call ("ethtool", + _ethtool_edata_to_string (edata, edata_size, sbuf, sizeof (sbuf)), + SIOCETHTOOL, + ifindex, + NULL, + NULL, + IOCTL_CALL_DATA_TYPE_IFRDATA, + edata, + edata_size, + NULL); } /*****************************************************************************/ @@ -196,27 +352,29 @@ ethtool_get_stringset (SocketHandle *shandle, int stringset_id) struct { struct ethtool_sset_info info; guint32 sentinel; - } sset_info = { }; + } sset_info = { + .info.cmd = ETHTOOL_GSSET_INFO, + .info.reserved = 0, + .info.sset_mask = (1ULL << stringset_id), + }; gs_free struct ethtool_gstrings *gstrings = NULL; + gsize gstrings_len; guint32 i, len; - sset_info.info.cmd = ETHTOOL_GSSET_INFO; - sset_info.info.reserved = 0; - sset_info.info.sset_mask = (1ULL << stringset_id); - - if (ethtool_call_handle (shandle, &sset_info) < 0) + if (_ethtool_call_handle (shandle, &sset_info, sizeof (sset_info)) < 0) return NULL; if (!sset_info.info.sset_mask) return NULL; len = sset_info.info.data[0]; - gstrings = g_malloc0 (sizeof (*gstrings) + (len * ETH_GSTRING_LEN)); + gstrings_len = sizeof (*gstrings) + (len * ETH_GSTRING_LEN); + gstrings = g_malloc0 (gstrings_len); gstrings->cmd = ETHTOOL_GSTRINGS; gstrings->string_set = stringset_id; gstrings->len = len; if (gstrings->len > 0) { - if (ethtool_call_handle (shandle, gstrings) < 0) + if (_ethtool_call_handle (shandle, gstrings, gstrings_len) < 0) return NULL; for (i = 0; i < gstrings->len; i++) { /* ensure NUL terminated */ @@ -402,18 +560,20 @@ ethtool_get_features (SocketHandle *shandle) return NULL; if (ss_features->len > 0) { - gs_free struct ethtool_gfeatures *gfeatures = NULL; + gs_free struct ethtool_gfeatures *gfeatures_free = NULL; + struct ethtool_gfeatures *gfeatures; + gsize gfeatures_len; guint idx; const NMEthtoolFeatureState *states_list0 = NULL; const NMEthtoolFeatureState *const*states_plist0 = NULL; guint states_plist_n = 0; - gfeatures = g_malloc0 ( sizeof (struct ethtool_gfeatures) - + (NM_DIV_ROUND_UP (ss_features->len, 32u) * sizeof(gfeatures->features[0]))); - + gfeatures_len = sizeof (struct ethtool_gfeatures) + + (NM_DIV_ROUND_UP (ss_features->len, 32u) * sizeof(gfeatures->features[0])); + gfeatures = nm_malloc0_maybe_a (300, gfeatures_len, &gfeatures_free); gfeatures->cmd = ETHTOOL_GFEATURES; gfeatures->size = NM_DIV_ROUND_UP (ss_features->len, 32u); - if (ethtool_call_handle (shandle, gfeatures) < 0) + if (_ethtool_call_handle (shandle, gfeatures, gfeatures_len) < 0) return NULL; for (idx = 0; idx < G_N_ELEMENTS (_ethtool_feature_infos); idx++) { @@ -477,20 +637,11 @@ ethtool_get_features (SocketHandle *shandle) NMEthtoolFeatureStates * nmp_utils_ethtool_get_features (int ifindex) { - nm_auto_socket_handle SocketHandle shandle = { }; + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT (ifindex); NMEthtoolFeatureStates *features; - int r; g_return_val_if_fail (ifindex > 0, 0); - if ((r = socket_handle_init (&shandle, ifindex)) < 0) { - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failed creating ethtool socket: %s", - ifindex, - "get-features", - nm_strerror_native (-r)); - return FALSE; - } - features = ethtool_get_features (&shandle); if (!features) { @@ -530,8 +681,10 @@ nmp_utils_ethtool_set_features (int ifindex, const NMTernary *requested /* indexed by NMEthtoolID - _NM_ETHTOOL_ID_FEATURE_FIRST */, gboolean do_set /* or reset */) { - nm_auto_socket_handle SocketHandle shandle = { }; - gs_free struct ethtool_sfeatures *sfeatures = NULL; + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT (ifindex); + gs_free struct ethtool_sfeatures *sfeatures_free = NULL; + struct ethtool_sfeatures *sfeatures; + gsize sfeatures_len; int r; guint i, j; struct { @@ -614,16 +767,9 @@ nmp_utils_ethtool_set_features (int ifindex, return TRUE; } - if ((r = socket_handle_init (&shandle, ifindex)) < 0) { - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failed creating ethtool socket: %s", - ifindex, - "set-features", - nm_strerror_native (-r)); - return FALSE; - } - - sfeatures = g_malloc0 (sizeof (struct ethtool_sfeatures) - + (NM_DIV_ROUND_UP (features->n_ss_features, 32U) * sizeof(sfeatures->features[0]))); + sfeatures_len = sizeof (struct ethtool_sfeatures) + + (NM_DIV_ROUND_UP (features->n_ss_features, 32U) * sizeof(sfeatures->features[0])); + sfeatures = nm_malloc0_maybe_a (300, sfeatures_len, &sfeatures_free); sfeatures->cmd = ETHTOOL_SFEATURES; sfeatures->size = NM_DIV_ROUND_UP (features->n_ss_features, 32U); @@ -649,7 +795,8 @@ nmp_utils_ethtool_set_features (int ifindex, sfeatures->features[i_block].requested &= ~i_flag; } - if ((r = ethtool_call_handle (&shandle, sfeatures)) < 0) { + r = _ethtool_call_handle (&shandle, sfeatures, sfeatures_len); + if (r < 0) { success = FALSE; nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failure setting features (%s)", ifindex, @@ -687,10 +834,10 @@ nmp_utils_ethtool_get_driver_info (int ifindex, g_return_val_if_fail (data, FALSE); drvinfo = (struct ethtool_drvinfo *) data; - - memset (drvinfo, 0, sizeof (*drvinfo)); - drvinfo->cmd = ETHTOOL_GDRVINFO; - return ethtool_call_ifindex (ifindex, drvinfo) >= 0; + *drvinfo = (struct ethtool_drvinfo) { + .cmd = ETHTOOL_GDRVINFO, + }; + return _ethtool_call_once (ifindex, drvinfo, sizeof (*drvinfo)) >= 0; } gboolean @@ -701,16 +848,16 @@ nmp_utils_ethtool_get_permanent_address (int ifindex, struct { struct ethtool_perm_addr e; guint8 _extra_data[NM_UTILS_HWADDR_LEN_MAX + 1]; - } edata; + } edata = { + .e.cmd = ETHTOOL_GPERMADDR, + .e.size = NM_UTILS_HWADDR_LEN_MAX, + }; + guint i; g_return_val_if_fail (ifindex > 0, FALSE); - memset (&edata, 0, sizeof (edata)); - edata.e.cmd = ETHTOOL_GPERMADDR; - edata.e.size = NM_UTILS_HWADDR_LEN_MAX; - - if (ethtool_call_ifindex (ifindex, &edata.e) < 0) + if (_ethtool_call_once (ifindex, &edata, sizeof (edata)) < 0) return FALSE; if (edata.e.size > NM_UTILS_HWADDR_LEN_MAX) @@ -747,27 +894,20 @@ nmp_utils_ethtool_supports_carrier_detect (int ifindex) * assume the device supports carrier-detect, otherwise we assume it * doesn't. */ - return ethtool_call_ifindex (ifindex, &edata) >= 0; + return _ethtool_call_once (ifindex, &edata, sizeof (edata)) >= 0; } gboolean nmp_utils_ethtool_supports_vlans (int ifindex) { - nm_auto_socket_handle SocketHandle shandle = { }; - int r; - gs_free struct ethtool_gfeatures *features = NULL; + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT (ifindex); + gs_free struct ethtool_gfeatures *features_free = NULL; + struct ethtool_gfeatures *features; + gsize features_len; int idx, block, bit, size; g_return_val_if_fail (ifindex > 0, FALSE); - if ((r = socket_handle_init (&shandle, ifindex)) < 0) { - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failed creating ethtool socket: %s", - ifindex, - "support-vlans", - nm_strerror_native (-r)); - return FALSE; - } - idx = ethtool_get_stringset_index (&shandle, ETH_SS_FEATURES, "vlan-challenged"); if (idx < 0) { nm_log_dbg (LOGD_PLATFORM, "ethtool[%d]: vlan-challenged ethtool feature does not exist?", ifindex); @@ -778,11 +918,13 @@ nmp_utils_ethtool_supports_vlans (int ifindex) bit = idx % 32; size = block + 1; - features = g_malloc0 (sizeof (*features) + size * sizeof (struct ethtool_get_features_block)); + features_len = sizeof (*features) + + (size * sizeof (struct ethtool_get_features_block)); + features = nm_malloc0_maybe_a (300, features_len, &features_free); features->cmd = ETHTOOL_GFEATURES; features->size = size; - if (ethtool_call_handle (&shandle, features) < 0) + if (_ethtool_call_handle (&shandle, features, features_len) < 0) return FALSE; return !(features->features[block].active & (1 << bit)); @@ -791,32 +933,25 @@ nmp_utils_ethtool_supports_vlans (int ifindex) int nmp_utils_ethtool_get_peer_ifindex (int ifindex) { - nm_auto_socket_handle SocketHandle shandle = { }; - int r; - - gs_free struct ethtool_stats *stats = NULL; + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT (ifindex); + gsize stats_len; + gs_free struct ethtool_stats *stats_free = NULL; + struct ethtool_stats *stats; int peer_ifindex_stat; g_return_val_if_fail (ifindex > 0, 0); - if ((r = socket_handle_init (&shandle, ifindex)) < 0) { - nm_log_trace (LOGD_PLATFORM, "ethtool[%d]: %s: failed creating ethtool socket: %s", - ifindex, - "get-peer-ifindex", - nm_strerror_native (-r)); - return FALSE; - } - peer_ifindex_stat = ethtool_get_stringset_index (&shandle, ETH_SS_STATS, "peer_ifindex"); if (peer_ifindex_stat < 0) { nm_log_dbg (LOGD_PLATFORM, "ethtool[%d]: peer_ifindex stat does not exist?", ifindex); return FALSE; } - stats = g_malloc0 (sizeof (*stats) + (peer_ifindex_stat + 1) * sizeof (guint64)); + stats_len = sizeof (*stats) + (peer_ifindex_stat + 1) * sizeof (guint64); + stats = nm_malloc0_maybe_a (300, stats_len, &stats_free); stats->cmd = ETHTOOL_GSTATS; stats->n_stats = peer_ifindex_stat + 1; - if (ethtool_call_ifindex (ifindex, stats) < 0) + if (_ethtool_call_handle (&shandle, stats, stats_len) < 0) return 0; return stats->data[peer_ifindex_stat]; @@ -825,13 +960,13 @@ nmp_utils_ethtool_get_peer_ifindex (int ifindex) gboolean nmp_utils_ethtool_get_wake_on_lan (int ifindex) { - struct ethtool_wolinfo wol; + struct ethtool_wolinfo wol = { + .cmd = ETHTOOL_GWOL, + }; g_return_val_if_fail (ifindex > 0, FALSE); - memset (&wol, 0, sizeof (wol)); - wol.cmd = ETHTOOL_GWOL; - if (ethtool_call_ifindex (ifindex, &wol) < 0) + if (_ethtool_call_once (ifindex, &wol, sizeof (wol)) < 0) return FALSE; return wol.wolopts != 0; @@ -849,11 +984,10 @@ nmp_utils_ethtool_get_link_settings (int ifindex, g_return_val_if_fail (ifindex > 0, FALSE); - if (ethtool_call_ifindex (ifindex, &edata) < 0) + if (_ethtool_call_once (ifindex, &edata, sizeof (edata)) < 0) return FALSE; - if (out_autoneg) - *out_autoneg = (edata.autoneg == AUTONEG_ENABLE); + NM_SET_OUT (out_autoneg, (edata.autoneg == AUTONEG_ENABLE)); if (out_speed) { guint32 speed; @@ -922,6 +1056,7 @@ nmp_utils_ethtool_set_link_settings (int ifindex, guint32 speed, NMPlatformLinkDuplexType duplex) { + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT (ifindex); struct ethtool_cmd edata = { .cmd = ETHTOOL_GSET, }; @@ -931,7 +1066,7 @@ nmp_utils_ethtool_set_link_settings (int ifindex, || (!speed && duplex == NM_PLATFORM_LINK_DUPLEX_UNKNOWN), FALSE); /* retrieve first current settings */ - if (ethtool_call_ifindex (ifindex, &edata) < 0) + if (_ethtool_call_handle (&shandle, &edata, sizeof (edata)) < 0) return FALSE; /* FIXME: try first new ETHTOOL_GLINKSETTINGS/SLINKSETTINGS API @@ -987,7 +1122,7 @@ nmp_utils_ethtool_set_link_settings (int ifindex, } } - return ethtool_call_ifindex (ifindex, &edata) >= 0; + return _ethtool_call_handle (&shandle, &edata, sizeof (edata)) >= 0; } gboolean @@ -995,7 +1130,10 @@ nmp_utils_ethtool_set_wake_on_lan (int ifindex, NMSettingWiredWakeOnLan wol, const char *wol_password) { - struct ethtool_wolinfo wol_info = { }; + struct ethtool_wolinfo wol_info = { + .cmd = ETHTOOL_SWOL, + .wolopts = 0, + }; g_return_val_if_fail (ifindex > 0, FALSE); @@ -1005,9 +1143,6 @@ nmp_utils_ethtool_set_wake_on_lan (int ifindex, nm_log_dbg (LOGD_PLATFORM, "ethtool[%d]: setting Wake-on-LAN options 0x%x, password '%s'", ifindex, (unsigned) wol, wol_password); - wol_info.cmd = ETHTOOL_SWOL; - wol_info.wolopts = 0; - if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_PHY)) wol_info.wolopts |= WAKE_PHY; if (NM_FLAGS_HAS (wol, NM_SETTING_WIRED_WAKE_ON_LAN_UNICAST)) @@ -1029,7 +1164,7 @@ nmp_utils_ethtool_set_wake_on_lan (int ifindex, wol_info.wolopts |= WAKE_MAGICSECURE; } - return ethtool_call_ifindex (ifindex, &wol_info) >= 0; + return _ethtool_call_once (ifindex, &wol_info, sizeof (wol_info)) >= 0; } /****************************************************************************** @@ -1039,40 +1174,44 @@ nmp_utils_ethtool_set_wake_on_lan (int ifindex, gboolean nmp_utils_mii_supports_carrier_detect (int ifindex) { - nm_auto_socket_handle SocketHandle shandle = { }; + nm_auto_socket_handle SocketHandle shandle = SOCKET_HANDLE_INIT (ifindex); int r; struct ifreq ifr; struct mii_ioctl_data *mii; - int errsv; g_return_val_if_fail (ifindex > 0, FALSE); - if ((r = socket_handle_init (&shandle, ifindex)) < 0) { - nm_log_trace (LOGD_PLATFORM, "mii[%d]: carrier-detect no: failed creating ethtool socket: %s", - ifindex, - nm_strerror_native (-r)); + r = _ioctl_call ("mii", + "SIOCGMIIPHY", + SIOCGMIIPHY, + shandle.ifindex, + &shandle.fd, + shandle.ifname, + IOCTL_CALL_DATA_TYPE_NONE, + NULL, + 0, + &ifr); + if (r < 0) return FALSE; - } - - memset (&ifr, 0, sizeof (struct ifreq)); - memcpy (ifr.ifr_name, shandle.ifname, IFNAMSIZ); - - if (ioctl (shandle.fd, SIOCGMIIPHY, &ifr) < 0) { - errsv = errno; - nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect no: SIOCGMIIPHY failed: %s", ifindex, shandle.ifname, nm_strerror_native (errsv)); - return FALSE; - } /* If we can read the BMSR register, we assume that the card supports MII link detection */ mii = (struct mii_ioctl_data *) &ifr.ifr_ifru; mii->reg_num = MII_BMSR; - if (ioctl (shandle.fd, SIOCGMIIREG, &ifr) != 0) { - errsv = errno; - nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect no: SIOCGMIIREG failed: %s", ifindex, shandle.ifname, nm_strerror_native (errsv)); + r = _ioctl_call ("mii", + "SIOCGMIIREG", + SIOCGMIIREG, + shandle.ifindex, + &shandle.fd, + shandle.ifname, + IOCTL_CALL_DATA_TYPE_IFRU, + mii, + sizeof (*mii), + &ifr); + if (r < 0) return FALSE; - } + mii = (struct mii_ioctl_data *) &ifr.ifr_ifru; nm_log_trace (LOGD_PLATFORM, "mii[%d,%s]: carrier-detect yes: SIOCGMIIREG result 0x%X", ifindex, shandle.ifname, mii->val_out); return TRUE; } diff --git a/src/platform/nm-platform-utils.h b/src/platform/nm-platform-utils.h index f5491679..ae3f51e2 100644 --- a/src/platform/nm-platform-utils.h +++ b/src/platform/nm-platform-utils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 1fc0ccb7..6ead15ed 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify @@ -34,6 +33,7 @@ #include <linux/if_tun.h> #include <linux/if_tunnel.h> #include <linux/rtnetlink.h> +#include <linux/tc_act/tc_mirred.h> #include <libudev.h> #include "nm-utils.h" @@ -50,12 +50,71 @@ /*****************************************************************************/ -G_STATIC_ASSERT (sizeof ( ((NMPlatformLink *) NULL)->addr.data ) == NM_UTILS_HWADDR_LEN_MAX); G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPAddress, address_ptr) == G_STRUCT_OFFSET (NMPlatformIP4Address, address)); G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPAddress, address_ptr) == G_STRUCT_OFFSET (NMPlatformIP6Address, address)); G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPRoute, network_ptr) == G_STRUCT_OFFSET (NMPlatformIP4Route, network)); G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPRoute, network_ptr) == G_STRUCT_OFFSET (NMPlatformIP6Route, network)); +/*****************************************************************************/ + +G_STATIC_ASSERT (sizeof ( ((NMPLinkAddress *) NULL)->data ) == NM_UTILS_HWADDR_LEN_MAX); +G_STATIC_ASSERT (sizeof ( ((NMPlatformLink *) NULL)->l_address.data ) == NM_UTILS_HWADDR_LEN_MAX); +G_STATIC_ASSERT (sizeof ( ((NMPlatformLink *) NULL)->l_broadcast.data ) == NM_UTILS_HWADDR_LEN_MAX); + +static const char * +_nmp_link_address_to_string (const NMPLinkAddress *addr, + char buf[static (NM_UTILS_HWADDR_LEN_MAX * 3)]) +{ + nm_assert (addr); + + if (addr->len > 0) { + if (!nm_utils_hwaddr_ntoa_buf (addr->data, + addr->len, + TRUE, + buf, + NM_UTILS_HWADDR_LEN_MAX * 3)) { + buf[0] = '\0'; + g_return_val_if_reached (buf); + } + } else + buf[0] = '\0'; + + return buf; +} + +gconstpointer +nmp_link_address_get (const NMPLinkAddress *addr, size_t *length) +{ + if ( !addr + || addr->len <= 0) { + NM_SET_OUT (length, 0); + return NULL; + } + + if (addr->len > NM_UTILS_HWADDR_LEN_MAX) { + NM_SET_OUT (length, 0); + g_return_val_if_reached (NULL); + } + + NM_SET_OUT (length, addr->len); + return addr->data; +} + +GBytes * +nmp_link_address_get_as_bytes (const NMPLinkAddress *addr) +{ + gconstpointer data; + size_t length; + + data = nmp_link_address_get (addr, &length); + + return length > 0 + ? g_bytes_new (data, length) + : NULL; +} + +/*****************************************************************************/ + #define _NMLOG_DOMAIN LOGD_PLATFORM #define _NMLOG_PREFIX_NAME "platform" @@ -220,7 +279,8 @@ nm_platform_setup (NMPlatform *instance) nm_singleton_instance_register (); - nm_log_dbg (LOGD_CORE, "setup %s singleton (%p, %s)", "NMPlatform", singleton_instance, G_OBJECT_TYPE_NAME (instance)); + nm_log_dbg (LOGD_CORE, "setup %s singleton ("NM_HASH_OBFUSCATE_PTR_FMT")", + "NMPlatform", NM_HASH_OBFUSCATE_PTR (instance)); } /** @@ -479,6 +539,40 @@ nm_platform_sysctl_set (NMPlatform *self, const char *pathid, int dirfd, const c return klass->sysctl_set (self, pathid, dirfd, path, value); } +/** + * nm_platform_sysctl_set_async: + * @self: platform instance + * @pathid: if @dirfd is present, this must be the full path that is looked up + * @dirfd: optional file descriptor for parent directory for openat() + * @path: absolute option path + * @values: NULL-terminated array of strings to be written + * @callback: function called on termination + * @data: data passed to callback function + * @cancellable: to cancel the operation + * + * This function is intended to be used for writing values to sysctl-style + * virtual runtime configuration files. This includes not only /proc/sys + * but also for example /sys/class. The function does not block and returns + * immediately. The callback is always invoked, and asynchronously. The file + * is closed after writing each value and reopened to write the next one so + * that the function can be used safely on all /proc and /sys files, + * independently of how /proc/sys/kernel/sysctl_writes_strict is configured. + */ +void nm_platform_sysctl_set_async (NMPlatform *self, + const char *pathid, + int dirfd, + const char *path, + const char *const *values, + NMPlatformAsyncCallback callback, + gpointer data, + GCancellable *cancellable) +{ + _CHECK_SELF_VOID (self, klass); + + klass->sysctl_set_async (self, pathid, dirfd, path, values, callback, data, cancellable); +} + + gboolean nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe (NMPlatform *self, const char *iface, @@ -621,14 +715,14 @@ nm_platform_sysctl_get_int_checked (NMPlatform *self, /*****************************************************************************/ char * -nm_platform_sysctl_ip_conf_get (NMPlatform *platform, +nm_platform_sysctl_ip_conf_get (NMPlatform *self, int addr_family, const char *ifname, const char *property) { char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; - return nm_platform_sysctl_get (platform, + return nm_platform_sysctl_get (self, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (addr_family, buf, ifname, @@ -636,7 +730,7 @@ nm_platform_sysctl_ip_conf_get (NMPlatform *platform, } gint64 -nm_platform_sysctl_ip_conf_get_int_checked (NMPlatform *platform, +nm_platform_sysctl_ip_conf_get_int_checked (NMPlatform *self, int addr_family, const char *ifname, const char *property, @@ -647,7 +741,7 @@ nm_platform_sysctl_ip_conf_get_int_checked (NMPlatform *platform, { char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; - return nm_platform_sysctl_get_int_checked (platform, + return nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (addr_family, buf, ifname, @@ -659,7 +753,7 @@ nm_platform_sysctl_ip_conf_get_int_checked (NMPlatform *platform, } gboolean -nm_platform_sysctl_ip_conf_set (NMPlatform *platform, +nm_platform_sysctl_ip_conf_set (NMPlatform *self, int addr_family, const char *ifname, const char *property, @@ -667,7 +761,7 @@ nm_platform_sysctl_ip_conf_set (NMPlatform *platform, { char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; - return nm_platform_sysctl_set (platform, + return nm_platform_sysctl_set (self, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (addr_family, buf, ifname, @@ -676,7 +770,7 @@ nm_platform_sysctl_ip_conf_set (NMPlatform *platform, } gboolean -nm_platform_sysctl_ip_conf_set_int64 (NMPlatform *platform, +nm_platform_sysctl_ip_conf_set_int64 (NMPlatform *self, int addr_family, const char *ifname, const char *property, @@ -685,7 +779,7 @@ nm_platform_sysctl_ip_conf_set_int64 (NMPlatform *platform, char buf[NM_UTILS_SYSCTL_IP_CONF_PATH_BUFSIZE]; char s[64]; - return nm_platform_sysctl_set (platform, + return nm_platform_sysctl_set (self, NMP_SYSCTL_PATHID_ABSOLUTE (nm_utils_sysctl_ip_conf_path (addr_family, buf, ifname, @@ -693,6 +787,47 @@ nm_platform_sysctl_ip_conf_set_int64 (NMPlatform *platform, nm_sprintf_buf (s, "%"G_GINT64_FORMAT, value)); } +int +nm_platform_sysctl_ip_conf_get_rp_filter_ipv4 (NMPlatform *self, + const char *ifname, + gboolean consider_all, + gboolean *out_due_to_all) +{ + int val, val_all; + + NM_SET_OUT (out_due_to_all, FALSE); + + if (!ifname) + return -1; + + val = nm_platform_sysctl_ip_conf_get_int_checked (self, + AF_INET, + ifname, + "rp_filter", + 10, 0, 2, -1); + if (val == -1) + return -1; + + /* the effectively used value is the rp_filter sysctl value of MAX(all,ifname). + * Note that this is the numerical MAX(), despite rp_filter "1" being more strict + * than "2". */ + if ( val < 2 + && consider_all + && !nm_streq (ifname, "all")) { + val_all = nm_platform_sysctl_ip_conf_get_int_checked (self, + AF_INET, + "all", + "rp_filter", + 10, 0, 2, val); + if (val_all > val) { + val = val_all; + NM_SET_OUT (out_due_to_all, TRUE); + } + } + + return val; +} + /*****************************************************************************/ static int @@ -905,14 +1040,15 @@ nm_platform_link_get_by_ifname (NMPlatform *self, const char *ifname) } struct _nm_platform_link_get_by_address_data { - gconstpointer address; - guint8 length; + gconstpointer data; + guint8 len; }; static gboolean _nm_platform_link_get_by_address_match_link (const NMPObject *obj, struct _nm_platform_link_get_by_address_data *d) { - return obj->link.addr.len == d->length && !memcmp (obj->link.addr.data, d->address, d->length); + return obj->link.l_address.len == d->len + && !memcmp (obj->link.l_address.data, d->data, d->len); } /** @@ -932,8 +1068,8 @@ nm_platform_link_get_by_address (NMPlatform *self, { const NMPObject *obj; struct _nm_platform_link_get_by_address_data d = { - .address = address, - .length = length, + .data = address, + .len = length, }; _CHECK_SELF (self, klass, NULL); @@ -1471,19 +1607,7 @@ nm_platform_link_get_address (NMPlatform *self, int ifindex, size_t *length) const NMPlatformLink *pllink; pllink = nm_platform_link_get (self, ifindex); - if ( !pllink - || pllink->addr.len <= 0) { - NM_SET_OUT (length, 0); - return NULL; - } - - if (pllink->addr.len > NM_UTILS_HWADDR_LEN_MAX) { - NM_SET_OUT (length, 0); - g_return_val_if_reached (NULL); - } - - NM_SET_OUT (length, pllink->addr.len); - return pllink->addr.data; + return nmp_link_address_get (pllink ? &pllink->l_address : NULL, length); } /** @@ -1551,19 +1675,35 @@ nm_platform_link_supports_sriov (NMPlatform *self, int ifindex) * @num_vfs: the number of VFs to create * @autoprobe: the new autoprobe-drivers value (pass * %NM_TERNARY_DEFAULT to keep current value) + * @callback: called when the operation finishes + * @callback_data: data passed to @callback + * @cancellable: cancellable to abort the operation + * + * Sets SR-IOV parameters asynchronously without + * blocking the main thread. The callback function is + * always invoked, and asynchronously. */ -gboolean -nm_platform_link_set_sriov_params (NMPlatform *self, - int ifindex, - guint num_vfs, - NMTernary autoprobe) +void +nm_platform_link_set_sriov_params_async (NMPlatform *self, + int ifindex, + guint num_vfs, + NMTernary autoprobe, + NMPlatformAsyncCallback callback, + gpointer callback_data, + GCancellable *cancellable) { - _CHECK_SELF (self, klass, FALSE); + _CHECK_SELF_VOID (self, klass); - g_return_val_if_fail (ifindex > 0, FALSE); + g_return_if_fail (ifindex > 0); _LOG3D ("link: setting %u total VFs and autoprobe %d", num_vfs, (int) autoprobe); - return klass->link_set_sriov_params (self, ifindex, num_vfs, autoprobe); + klass->link_set_sriov_params_async (self, + ifindex, + num_vfs, + autoprobe, + callback, + callback_data, + cancellable); } gboolean @@ -2369,9 +2509,10 @@ nm_platform_link_6lowpan_get_properties (NMPlatform *self, int ifindex, int *out if (out_parent) { const NMPlatformLink *parent_plink; - parent_plink = nm_platform_link_get_by_address (self, NM_LINK_TYPE_WPAN, - plink->addr.data, - plink->addr.len); + parent_plink = nm_platform_link_get_by_address (self, + NM_LINK_TYPE_WPAN, + plink->l_address.data, + plink->l_address.len); NM_SET_OUT (out_parent, parent_plink ? parent_plink->ifindex : -1); } @@ -3400,21 +3541,21 @@ nm_platform_ethtool_set_features (NMPlatform *self, /*****************************************************************************/ const NMDedupMultiHeadEntry * -nm_platform_lookup_all (NMPlatform *platform, +nm_platform_lookup_all (NMPlatform *self, NMPCacheIdType cache_id_type, const NMPObject *obj) { - return nmp_cache_lookup_all (nm_platform_get_cache (platform), + return nmp_cache_lookup_all (nm_platform_get_cache (self), cache_id_type, obj); } const NMDedupMultiEntry * -nm_platform_lookup_entry (NMPlatform *platform, +nm_platform_lookup_entry (NMPlatform *self, NMPCacheIdType cache_id_type, const NMPObject *obj) { - return nmp_cache_lookup_entry_with_idx_type (nm_platform_get_cache (platform), + return nmp_cache_lookup_entry_with_idx_type (nm_platform_get_cache (self), cache_id_type, obj); } @@ -5076,10 +5217,27 @@ nm_platform_qdisc_add (NMPlatform *self, int ifindex = qdisc->ifindex; _CHECK_SELF (self, klass, -NME_BUG); + /* Note: @qdisc must not be copied or kept alive because the lifetime of qdisc.kind + * is undefined. */ + _LOG3D ("adding or updating a qdisc: %s", nm_platform_qdisc_to_string (qdisc, NULL, 0)); return klass->qdisc_add (self, flags, qdisc); } +/** + * nm_platform_qdisc_sync: + * @self: the #NMPlatform instance + * @ifindex: the ifindex where to configure the qdiscs. + * @known_qdiscs: the list of qdiscs (#NMPObject). + * + * The function promises not to take any reference to the qdisc + * instances from @known_qdiscs, nor to keep them around after + * the function returns. This is important, because it allows the + * caller to pass NMPlatformQdisc instances which "kind" string + * have a limited lifetime. + * + * Returns: %TRUE on success. + */ gboolean nm_platform_qdisc_sync (NMPlatform *self, int ifindex, @@ -5142,10 +5300,27 @@ nm_platform_tfilter_add (NMPlatform *self, int ifindex = tfilter->ifindex; _CHECK_SELF (self, klass, -NME_BUG); + /* Note: @tfilter must not be copied or kept alive because the lifetime of tfilter.kind + * and tfilter.action.kind is undefined. */ + _LOG3D ("adding or updating a tfilter: %s", nm_platform_tfilter_to_string (tfilter, NULL, 0)); return klass->tfilter_add (self, flags, tfilter); } +/** + * nm_platform_qdisc_sync: + * @self: the #NMPlatform instance + * @ifindex: the ifindex where to configure the qdiscs. + * @known_tfilters: the list of tfilters (#NMPObject). + * + * The function promises not to take any reference to the tfilter + * instances from @known_tfilters, nor to keep them around after + * the function returns. This is important, because it allows the + * caller to pass NMPlatformTfilter instances which "kind" string + * have a limited lifetime. + * + * Returns: %TRUE on success. + */ gboolean nm_platform_tfilter_sync (NMPlatform *self, int ifindex, @@ -5268,31 +5443,36 @@ nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len) { char master[20]; char parent[20]; - GString *str_flags; + char str_flags[1 + NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN + 1]; + char str_highlighted_flags[50]; + char *s; + gsize l; char str_addrmode[30]; - gs_free char *str_addr = NULL; + char str_address[NM_UTILS_HWADDR_LEN_MAX * 3]; + char str_broadcast[NM_UTILS_HWADDR_LEN_MAX * 3]; char str_inet6_token[NM_UTILS_INET_ADDRSTRLEN]; const char *str_link_type; if (!nm_utils_to_string_buffer_init_null (link, &buf, &len)) return buf; - str_flags = g_string_new (NULL); + s = str_highlighted_flags; + l = sizeof (str_highlighted_flags); if (NM_FLAGS_HAS (link->n_ifi_flags, IFF_NOARP)) - g_string_append (str_flags, "NOARP,"); + nm_utils_strbuf_append_str (&s, &l, "NOARP,"); if (NM_FLAGS_HAS (link->n_ifi_flags, IFF_UP)) - g_string_append (str_flags, "UP"); + nm_utils_strbuf_append_str (&s, &l, "UP"); else - g_string_append (str_flags, "DOWN"); + nm_utils_strbuf_append_str (&s, &l, "DOWN"); if (link->connected) - g_string_append (str_flags, ",LOWER_UP"); + nm_utils_strbuf_append_str (&s, &l, ",LOWER_UP"); + nm_assert (s > str_highlighted_flags && l > 0); if (link->n_ifi_flags) { - char str_flags_buf[64]; - - nm_platform_link_flags2str (link->n_ifi_flags, str_flags_buf, sizeof (str_flags_buf)); - g_string_append_printf (str_flags, ";%s", str_flags_buf); - } + str_flags[0] = ';'; + nm_platform_link_flags2str (link->n_ifi_flags, &str_flags[1], sizeof (str_flags) - 1); + } else + str_flags[0] = '\0'; if (link->master) g_snprintf (master, sizeof (master), " master %d", link->master); @@ -5306,8 +5486,8 @@ nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len) else parent[0] = 0; - if (link->addr.len) - str_addr = nm_utils_hwaddr_ntoa (link->addr.data, MIN (link->addr.len, sizeof (link->addr.data))); + _nmp_link_address_to_string (&link->l_address, str_address); + _nmp_link_address_to_string (&link->l_broadcast, str_broadcast); str_link_type = nm_link_type_to_string (link->type); @@ -5315,7 +5495,7 @@ nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len) "%d: " /* ifindex */ "%s" /* name */ "%s" /* parent */ - " <%s>" /* flags */ + " <%s%s>" /* flags */ " mtu %d" "%s" /* master */ " arp %u" /* arptype */ @@ -5323,7 +5503,8 @@ nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len) "%s%s" /* kind */ "%s" /* is-in-udev */ "%s%s" /* addr-gen-mode */ - "%s%s" /* addr */ + "%s%s" /* l_address */ + "%s%s" /* l_broadcast */ "%s%s" /* inet6_token */ "%s%s" /* driver */ " rx:%"G_GUINT64_FORMAT",%"G_GUINT64_FORMAT @@ -5332,7 +5513,8 @@ nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len) link->ifindex, link->name, parent, - str_flags->str, + str_highlighted_flags, + str_flags, link->mtu, master, link->arptype, str_link_type ?: "???", @@ -5341,15 +5523,16 @@ nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len) link->initialized ? " init" : " not-init", link->inet6_addr_gen_mode_inv ? " addrgenmode " : "", link->inet6_addr_gen_mode_inv ? nm_platform_link_inet6_addrgenmode2str (_nm_platform_uint8_inv (link->inet6_addr_gen_mode_inv), str_addrmode, sizeof (str_addrmode)) : "", - str_addr ? " addr " : "", - str_addr ?: "", + str_address[0] ? " addr " : "", + str_address[0] ? str_address : "", + str_broadcast[0] ? " brd " : "", + str_broadcast[0] ? str_broadcast : "", link->inet6_token.id ? " inet6token " : "", link->inet6_token.id ? nm_utils_inet6_interface_identifier_to_token (link->inet6_token, str_inet6_token) : "", link->driver ? " driver " : "", link->driver ?: "", link->rx_packets, link->rx_bytes, link->tx_packets, link->tx_bytes); - g_string_free (str_flags, TRUE); return buf; } @@ -6426,19 +6609,41 @@ const char * nm_platform_qdisc_to_string (const NMPlatformQdisc *qdisc, char *buf, gsize len) { char str_dev[TO_STRING_DEV_BUF_SIZE]; + const char *buf0; if (!nm_utils_to_string_buffer_init_null (qdisc, &buf, &len)) return buf; - g_snprintf (buf, len, "%s%s family %d handle %x parent %x info %x", - qdisc->kind, - _to_string_dev (NULL, qdisc->ifindex, str_dev, sizeof (str_dev)), - qdisc->addr_family, - qdisc->handle, - qdisc->parent, - qdisc->info); + buf0 = buf; - return buf; + nm_utils_strbuf_append (&buf, &len, "%s%s family %u handle %x parent %x info %x", + qdisc->kind, + _to_string_dev (NULL, qdisc->ifindex, str_dev, sizeof (str_dev)), + qdisc->addr_family, + qdisc->handle, + qdisc->parent, + qdisc->info); + + if (nm_streq0 (qdisc->kind, "fq_codel")) { + if (qdisc->fq_codel.limit) + nm_utils_strbuf_append (&buf, &len, " limit %u", qdisc->fq_codel.limit); + if (qdisc->fq_codel.flows) + nm_utils_strbuf_append (&buf, &len, " flows %u", qdisc->fq_codel.flows); + if (qdisc->fq_codel.target) + nm_utils_strbuf_append (&buf, &len, " target %u", qdisc->fq_codel.target); + if (qdisc->fq_codel.interval) + nm_utils_strbuf_append (&buf, &len, " interval %u", qdisc->fq_codel.interval); + if (qdisc->fq_codel.quantum) + nm_utils_strbuf_append (&buf, &len, " quantum %u", qdisc->fq_codel.quantum); + if (qdisc->fq_codel.ce_threshold != NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED) + nm_utils_strbuf_append (&buf, &len, " ce_threshold %u", qdisc->fq_codel.ce_threshold); + if (qdisc->fq_codel.memory_limit != NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET) + nm_utils_strbuf_append (&buf, &len, " memory_limit %u", qdisc->fq_codel.memory_limit); + if (qdisc->fq_codel.ecn) + nm_utils_strbuf_append (&buf, &len, " ecn"); + } + + return buf0; } void @@ -6451,6 +6656,18 @@ nm_platform_qdisc_hash_update (const NMPlatformQdisc *obj, NMHashState *h) obj->handle, obj->parent, obj->info); + if (nm_streq0 (obj->kind, "fq_codel")) { + nm_hash_update_vals (h, + obj->fq_codel.limit, + obj->fq_codel.flows, + obj->fq_codel.target, + obj->fq_codel.interval, + obj->fq_codel.quantum, + obj->fq_codel.ce_threshold, + obj->fq_codel.memory_limit, + NM_HASH_COMBINE_BOOLS (guint8, + obj->fq_codel.ecn)); + } } int @@ -6464,6 +6681,17 @@ nm_platform_qdisc_cmp (const NMPlatformQdisc *a, const NMPlatformQdisc *b) NM_CMP_FIELD (a, b, handle); NM_CMP_FIELD (a, b, info); + if (nm_streq0 (a->kind, "fq_codel")) { + NM_CMP_FIELD (a, b, fq_codel.limit); + NM_CMP_FIELD (a, b, fq_codel.flows); + NM_CMP_FIELD (a, b, fq_codel.target); + NM_CMP_FIELD (a, b, fq_codel.interval); + NM_CMP_FIELD (a, b, fq_codel.quantum); + NM_CMP_FIELD (a, b, fq_codel.ce_threshold); + NM_CMP_FIELD (a, b, fq_codel.memory_limit); + NM_CMP_FIELD_UNSAFE (a, b, fq_codel.ecn); + } + return 0; } @@ -6492,11 +6720,18 @@ nm_platform_tfilter_to_string (const NMPlatformTfilter *tfilter, char *buf, gsiz NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL | NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_NON_ASCII, &t)); + } else if (nm_streq (tfilter->action.kind, NM_PLATFORM_ACTION_KIND_MIRRED)) { + nm_utils_strbuf_append (&p, &l, "%s%s%s%s dev %d", + tfilter->action.mirred.ingress ? " ingress" : "", + tfilter->action.mirred.egress ? " egress" : "", + tfilter->action.mirred.mirror ? " mirror" : "", + tfilter->action.mirred.redirect ? " redirect" : "", + tfilter->action.mirred.ifindex); } } else act_buf[0] = '\0'; - g_snprintf (buf, len, "%s%s family %d handle %x parent %x info %x%s", + g_snprintf (buf, len, "%s%s family %u handle %x parent %x info %x%s", tfilter->kind, _to_string_dev (NULL, tfilter->ifindex, str_dev, sizeof (str_dev)), tfilter->addr_family, @@ -6520,8 +6755,17 @@ nm_platform_tfilter_hash_update (const NMPlatformTfilter *obj, NMHashState *h) obj->info); if (obj->action.kind) { nm_hash_update_str (h, obj->action.kind); - if (nm_streq (obj->action.kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) + if (nm_streq (obj->action.kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) { nm_hash_update_strarr (h, obj->action.simple.sdata); + } else if (nm_streq (obj->action.kind, NM_PLATFORM_ACTION_KIND_MIRRED)) { + nm_hash_update_vals (h, + obj->action.mirred.ifindex, + NM_HASH_COMBINE_BOOLS (guint8, + obj->action.mirred.ingress, + obj->action.mirred.egress, + obj->action.mirred.mirror, + obj->action.mirred.redirect)); + } } } @@ -6538,8 +6782,15 @@ nm_platform_tfilter_cmp (const NMPlatformTfilter *a, const NMPlatformTfilter *b) NM_CMP_FIELD_STR_INTERNED (a, b, action.kind); if (a->action.kind) { - if (nm_streq (a->action.kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) + if (nm_streq (a->action.kind, NM_PLATFORM_ACTION_KIND_SIMPLE)) { NM_CMP_FIELD_STR (a, b, action.simple.sdata); + } else if (nm_streq (a->action.kind, NM_PLATFORM_ACTION_KIND_MIRRED)) { + NM_CMP_FIELD (a, b, action.mirred.ifindex); + NM_CMP_FIELD_UNSAFE (a, b, action.mirred.ingress); + NM_CMP_FIELD_UNSAFE (a, b, action.mirred.egress); + NM_CMP_FIELD_UNSAFE (a, b, action.mirred.mirror); + NM_CMP_FIELD_UNSAFE (a, b, action.mirred.redirect); + } } return 0; @@ -6641,7 +6892,8 @@ nm_platform_link_hash_update (const NMPlatformLink *obj, NMHashState *h) nm_hash_update_str0 (h, obj->kind); nm_hash_update_str0 (h, obj->driver); /* nm_hash_update_mem() also hashes the length obj->addr.len */ - nm_hash_update_mem (h, obj->addr.data, obj->addr.len); + nm_hash_update_mem (h, obj->l_address.data, NM_MIN (obj->l_address.len, sizeof (obj->l_address.data))); + nm_hash_update_mem (h, obj->l_broadcast.data, NM_MIN (obj->l_broadcast.len, sizeof (obj->l_broadcast.data))); } int @@ -6658,12 +6910,15 @@ nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b) NM_CMP_FIELD (a, b, mtu); NM_CMP_FIELD_BOOL (a, b, initialized); NM_CMP_FIELD (a, b, arptype); - NM_CMP_FIELD (a, b, addr.len); + NM_CMP_FIELD (a, b, l_address.len); + NM_CMP_FIELD (a, b, l_broadcast.len); NM_CMP_FIELD (a, b, inet6_addr_gen_mode_inv); NM_CMP_FIELD_STR_INTERNED (a, b, kind); NM_CMP_FIELD_STR_INTERNED (a, b, driver); - if (a->addr.len) - NM_CMP_FIELD_MEMCMP_LEN (a, b, addr.data, a->addr.len); + if (a->l_address.len) + NM_CMP_FIELD_MEMCMP_LEN (a, b, l_address.data, a->l_address.len); + if (a->l_broadcast.len) + NM_CMP_FIELD_MEMCMP_LEN (a, b, l_broadcast.data, a->l_broadcast.len); NM_CMP_FIELD_MEMCMP (a, b, inet6_token); NM_CMP_FIELD (a, b, rx_packets); NM_CMP_FIELD (a, b, rx_bytes); diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 97be8832..248eca44 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify @@ -159,6 +158,14 @@ typedef enum { NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL, } NMPlatformRoutingRuleCmpType; +typedef struct { + guint8 data[20 /* NM_UTILS_HWADDR_LEN_MAX */ ]; + guint8 len; +} NMPLinkAddress; + +gconstpointer nmp_link_address_get (const NMPLinkAddress *addr, size_t *length); +GBytes *nmp_link_address_get_as_bytes (const NMPLinkAddress *addr); + typedef enum { /* match-flags are strictly inclusive. That means, @@ -230,11 +237,11 @@ struct _NMPlatformLink { /* rtnl_link_get_arptype(), ifinfomsg.ifi_type. */ guint32 arptype; - /* rtnl_link_get_addr(), IFLA_ADDRESS */ - struct { - guint8 data[20]; /* NM_UTILS_HWADDR_LEN_MAX */ - guint8 len; - } addr; + /* IFLA_ADDRESS */ + NMPLinkAddress l_address; + + /* IFLA_BROADCAST */ + NMPLinkAddress l_broadcast; /* rtnl_link_inet6_get_token(), IFLA_INET6_TOKEN */ NMUtilsIPv6IfaceId inet6_token; @@ -596,13 +603,48 @@ typedef struct { bool uid_range_has:1; /* has(FRA_UID_RANGE) */ } NMPlatformRoutingRule; +#define NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET (~((guint32) 0)) + +#define NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED ((guint32) 0x83126E97u) + +G_STATIC_ASSERT (((((guint64) NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED) * 1000u) >> 10) == (guint64) INT_MAX); + +typedef struct { + guint32 limit; + guint32 flows; + guint32 target; + guint32 interval; + guint32 quantum; + guint32 ce_threshold; /* TCA_FQ_CODEL_CE_THRESHOLD: kernel internally stores this value as + * ((val64 * NSEC_PER_USEC) >> CODEL_SHIFT). The default value (in + * the domain with this coersion) is CODEL_DISABLED_THRESHOLD (INT_MAX). + * That means, "disabled" is expressed on RTM_NEWQDISC netlink API by absence of the + * netlink attribute but also as the special value 0x83126E97u + * (NM_PLATFORM_FQ_CODEL_CE_THRESHOLD_DISABLED). + * Beware: zero is not the default you must always explicitly set this value. */ + guint32 memory_limit; /* TCA_FQ_CODEL_MEMORY_LIMIT: note that only values <= 2^31 are accepted by kernel + * and kernel defaults to 32MB. + * Note that we use the special value NM_PLATFORM_FQ_CODEL_MEMORY_LIMIT_UNSET + * to indicate that no explicit limit is set (when we send a RTM_NEWQDISC request). + * This will cause kernel to choose the default (32MB). + * Beware: zero is not the default you must always explicitly set this value. */ + bool ecn:1; +} NMPlatformQdiscFqCodel; + typedef struct { __NMPlatformObjWithIfindex_COMMON; + + /* beware, kind is embedded in an NMPObject, hence you must + * take care of the lifetime of the string. */ const char *kind; + int addr_family; guint32 handle; guint32 parent; guint32 info; + union { + NMPlatformQdiscFqCodel fq_codel; + }; } NMPlatformQdisc; typedef struct { @@ -610,17 +652,35 @@ typedef struct { } NMPlatformActionSimple; typedef struct { + int ifindex; + bool egress:1; + bool ingress:1; + bool mirror:1; + bool redirect:1; +} NMPlatformActionMirred; + +typedef struct { + + /* beware, kind is embedded in an NMPObject, hence you must + * take care of the lifetime of the string. */ const char *kind; + union { NMPlatformActionSimple simple; + NMPlatformActionMirred mirred; }; } NMPlatformAction; #define NM_PLATFORM_ACTION_KIND_SIMPLE "simple" +#define NM_PLATFORM_ACTION_KIND_MIRRED "mirred" typedef struct { __NMPlatformObjWithIfindex_COMMON; + + /* beware, kind is embedded in an NMPObject, hence you must + * take care of the lifetime of the string. */ const char *kind; + int addr_family; guint32 handle; guint32 parent; @@ -842,6 +902,8 @@ typedef enum { } NMPlatformWireGuardChangePeerFlags; +typedef void (*NMPlatformAsyncCallback) (GError *error, gpointer user_data); + /*****************************************************************************/ typedef enum { @@ -864,28 +926,28 @@ extern volatile int _nm_platform_kernel_support_state[_NM_PLATFORM_KERNEL_SUPPOR int _nm_platform_kernel_support_init (NMPlatformKernelSupportType type, int value); -#define _nm_platform_kernel_support_detected(type) \ - G_LIKELY (({ \ - const NMPlatformKernelSupportType _type = (type); \ - \ - nm_assert (_NM_INT_NOT_NEGATIVE (_type) && _type < G_N_ELEMENTS (_nm_platform_kernel_support_state)); \ - \ - (_nm_platform_kernel_support_state[_type] != 0); \ - })) - -#define nm_platform_kernel_support_get(type) \ - ({ \ - const NMPlatformKernelSupportType _type = (type); \ - int _v; \ - \ - nm_assert (_NM_INT_NOT_NEGATIVE (_type) && _type < G_N_ELEMENTS (_nm_platform_kernel_support_state)); \ - \ - _v = _nm_platform_kernel_support_state[_type]; \ - if (G_UNLIKELY (_v == 0)) \ - _v = _nm_platform_kernel_support_init (_type, 0); \ - \ - (_v >= 0); \ - }) +static inline gboolean +_nm_platform_kernel_support_detected (NMPlatformKernelSupportType type) +{ + nm_assert ( _NM_INT_NOT_NEGATIVE (type) + && type < G_N_ELEMENTS (_nm_platform_kernel_support_state)); + + return G_LIKELY (_nm_platform_kernel_support_state[type] != 0); +} + +static inline gboolean +nm_platform_kernel_support_get (NMPlatformKernelSupportType type) +{ + int v; + + nm_assert (_NM_INT_NOT_NEGATIVE (type) + && type < G_N_ELEMENTS (_nm_platform_kernel_support_state)); + + v = _nm_platform_kernel_support_state[type]; + if (G_UNLIKELY (v == 0)) + v = _nm_platform_kernel_support_init (type, 0); + return (v >= 0); +} /*****************************************************************************/ @@ -900,64 +962,75 @@ struct _NMPlatform { typedef struct { GObjectClass parent; - gboolean (*sysctl_set) (NMPlatform *, const char *pathid, int dirfd, const char *path, const char *value); - char * (*sysctl_get) (NMPlatform *, const char *pathid, int dirfd, const char *path); + gboolean (*sysctl_set) (NMPlatform *self, const char *pathid, int dirfd, const char *path, const char *value); + void (*sysctl_set_async) (NMPlatform *self, + const char *pathid, + int dirfd, + const char *path, + const char *const *values, + NMPlatformAsyncCallback callback, + gpointer data, + GCancellable *cancellable); + char * (*sysctl_get) (NMPlatform *self, const char *pathid, int dirfd, const char *path); + + void (*refresh_all) (NMPlatform *self, NMPObjectType obj_type); + void (*process_events) (NMPlatform *self); - int (*link_add) (NMPlatform *, + int (*link_add) (NMPlatform *self, const char *name, NMLinkType type, const char *veth_peer, const void *address, size_t address_len, const NMPlatformLink **out_link); - - gboolean (*link_delete) (NMPlatform *, int ifindex); - - gboolean (*link_refresh) (NMPlatform *, int ifindex); - - gboolean (*link_set_netns) (NMPlatform *, int ifindex, int netns_fd); - - void (*process_events) (NMPlatform *self); - - gboolean (*link_set_up) (NMPlatform *, int ifindex, gboolean *out_no_firmware); - gboolean (*link_set_down) (NMPlatform *, int ifindex); - gboolean (*link_set_arp) (NMPlatform *, int ifindex); - gboolean (*link_set_noarp) (NMPlatform *, int ifindex); + gboolean (*link_delete) (NMPlatform *self, int ifindex); + gboolean (*link_refresh) (NMPlatform *self, int ifindex); + gboolean (*link_set_netns) (NMPlatform *self, int ifindex, int netns_fd); + gboolean (*link_set_up) (NMPlatform *self, int ifindex, gboolean *out_no_firmware); + gboolean (*link_set_down) (NMPlatform *self, int ifindex); + gboolean (*link_set_arp) (NMPlatform *self, int ifindex); + gboolean (*link_set_noarp) (NMPlatform *self, int ifindex); const char *(*link_get_udi) (NMPlatform *self, int ifindex); struct udev_device *(*link_get_udev_device) (NMPlatform *self, int ifindex); - int (*link_set_user_ipv6ll_enabled) (NMPlatform *, int ifindex, gboolean enabled); - gboolean (*link_set_token) (NMPlatform *, int ifindex, NMUtilsIPv6IfaceId iid); + int (*link_set_user_ipv6ll_enabled) (NMPlatform *self, int ifindex, gboolean enabled); + gboolean (*link_set_token) (NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId iid); - gboolean (*link_get_permanent_address) (NMPlatform *, + gboolean (*link_get_permanent_address) (NMPlatform *self, int ifindex, guint8 *buf, size_t *length); - int (*link_set_address) (NMPlatform *, int ifindex, gconstpointer address, size_t length); - int (*link_set_mtu) (NMPlatform *, int ifindex, guint32 mtu); - gboolean (*link_set_name) (NMPlatform *, int ifindex, const char *name); - gboolean (*link_set_sriov_params) (NMPlatform *, int ifindex, guint num_vfs, int autoprobe); + int (*link_set_address) (NMPlatform *self, int ifindex, gconstpointer address, size_t length); + int (*link_set_mtu) (NMPlatform *self, int ifindex, guint32 mtu); + gboolean (*link_set_name) (NMPlatform *self, int ifindex, const char *name); + void (*link_set_sriov_params_async) (NMPlatform *self, + int ifindex, + guint num_vfs, + int autoprobe, + NMPlatformAsyncCallback callback, + gpointer callback_data, + GCancellable *cancellable); gboolean (*link_set_sriov_vfs) (NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs); gboolean (*link_set_bridge_vlans) (NMPlatform *self, int ifindex, gboolean on_master, const NMPlatformBridgeVlan *const *vlans); - char * (*link_get_physical_port_id) (NMPlatform *, int ifindex); - guint (*link_get_dev_id) (NMPlatform *, int ifindex); - gboolean (*link_get_wake_on_lan) (NMPlatform *, int ifindex); - gboolean (*link_get_driver_info) (NMPlatform *, + char * (*link_get_physical_port_id) (NMPlatform *self, int ifindex); + guint (*link_get_dev_id) (NMPlatform *self, int ifindex); + gboolean (*link_get_wake_on_lan) (NMPlatform *self, int ifindex); + gboolean (*link_get_driver_info) (NMPlatform *self, int ifindex, char **out_driver_name, char **out_driver_version, char **out_fw_version); - gboolean (*link_supports_carrier_detect) (NMPlatform *, int ifindex); - gboolean (*link_supports_vlans) (NMPlatform *, int ifindex); - gboolean (*link_supports_sriov) (NMPlatform *, int ifindex); + gboolean (*link_supports_carrier_detect) (NMPlatform *self, int ifindex); + gboolean (*link_supports_vlans) (NMPlatform *self, int ifindex); + gboolean (*link_supports_sriov) (NMPlatform *self, int ifindex); - gboolean (*link_enslave) (NMPlatform *, int master, int slave); - gboolean (*link_release) (NMPlatform *, int master, int slave); + gboolean (*link_enslave) (NMPlatform *self, int master, int slave); + gboolean (*link_release) (NMPlatform *self, int master, int slave); - gboolean (*link_can_assume) (NMPlatform *, int ifindex); + gboolean (*link_can_assume) (NMPlatform *self, int ifindex); int (*link_wireguard_change) (NMPlatform *self, int ifindex, @@ -967,7 +1040,7 @@ typedef struct { guint peers_len, NMPlatformWireGuardChangeFlags change_flags); - gboolean (*vlan_add) (NMPlatform *, const char *name, int parent, int vlanid, guint32 vlanflags, const NMPlatformLink **out_link); + gboolean (*vlan_add) (NMPlatform *self, const char *name, int parent, int vlanid, guint32 vlanflags, const NMPlatformLink **out_link); gboolean (*link_vlan_change) (NMPlatform *self, int ifindex, NMVlanFlags flags_mask, @@ -978,81 +1051,79 @@ typedef struct { gboolean egress_reset_all, const NMVlanQosMapping *egress_map, gsize n_egress_map); - gboolean (*link_vxlan_add) (NMPlatform *, + gboolean (*link_vxlan_add) (NMPlatform *self, const char *name, const NMPlatformLnkVxlan *props, const NMPlatformLink **out_link); - gboolean (*link_gre_add) (NMPlatform *, + gboolean (*link_gre_add) (NMPlatform *self, const char *name, const NMPlatformLnkGre *props, const NMPlatformLink **out_link); - gboolean (*link_ip6tnl_add) (NMPlatform *, + gboolean (*link_ip6tnl_add) (NMPlatform *self, const char *name, const NMPlatformLnkIp6Tnl *props, const NMPlatformLink **out_link); - gboolean (*link_ip6gre_add) (NMPlatform *, + gboolean (*link_ip6gre_add) (NMPlatform *self, const char *name, const NMPlatformLnkIp6Tnl *props, const NMPlatformLink **out_link); - gboolean (*link_ipip_add) (NMPlatform *, + gboolean (*link_ipip_add) (NMPlatform *self, const char *name, const NMPlatformLnkIpIp *props, const NMPlatformLink **out_link); - gboolean (*link_macsec_add) (NMPlatform *, + gboolean (*link_macsec_add) (NMPlatform *self, const char *name, int parent, const NMPlatformLnkMacsec *props, const NMPlatformLink **out_link); - gboolean (*link_macvlan_add) (NMPlatform *, + gboolean (*link_macvlan_add) (NMPlatform *self, const char *name, int parent, const NMPlatformLnkMacvlan *props, const NMPlatformLink **out_link); - gboolean (*link_sit_add) (NMPlatform *, + gboolean (*link_sit_add) (NMPlatform *self, const char *name, const NMPlatformLnkSit *props, const NMPlatformLink **out_link); - - gboolean (*link_tun_add) (NMPlatform *platform, + gboolean (*link_tun_add) (NMPlatform *self, const char *name, const NMPlatformLnkTun *props, const NMPlatformLink **out_link, int *out_fd); - - gboolean (*link_6lowpan_add) (NMPlatform *platform, + gboolean (*link_6lowpan_add) (NMPlatform *self, const char *name, int parent, const NMPlatformLink **out_link); - gboolean (*infiniband_partition_add) (NMPlatform *, int parent, int p_key, const NMPlatformLink **out_link); - gboolean (*infiniband_partition_delete) (NMPlatform *, int parent, int p_key); - - gboolean (*wifi_get_capabilities) (NMPlatform *, int ifindex, NMDeviceWifiCapabilities *caps); - gboolean (*wifi_get_bssid) (NMPlatform *, int ifindex, guint8 *bssid); - guint32 (*wifi_get_frequency) (NMPlatform *, int ifindex); - int (*wifi_get_quality) (NMPlatform *, int ifindex); - guint32 (*wifi_get_rate) (NMPlatform *, int ifindex); - NM80211Mode (*wifi_get_mode) (NMPlatform *, int ifindex); - void (*wifi_set_mode) (NMPlatform *, int ifindex, NM80211Mode mode); - void (*wifi_set_powersave) (NMPlatform *, int ifindex, guint32 powersave); - guint32 (*wifi_find_frequency) (NMPlatform *, int ifindex, const guint32 *freqs); - void (*wifi_indicate_addressing_running) (NMPlatform *, int ifindex, gboolean running); - NMSettingWirelessWakeOnWLan (*wifi_get_wake_on_wlan) (NMPlatform *, int ifindex); - gboolean (*wifi_set_wake_on_wlan) (NMPlatform *, int ifindex, NMSettingWirelessWakeOnWLan wowl); - - guint32 (*mesh_get_channel) (NMPlatform *, int ifindex); - gboolean (*mesh_set_channel) (NMPlatform *, int ifindex, guint32 channel); - gboolean (*mesh_set_ssid) (NMPlatform *, int ifindex, const guint8 *ssid, gsize len); - - guint16 (*wpan_get_pan_id) (NMPlatform *, int ifindex); - gboolean (*wpan_set_pan_id) (NMPlatform *, int ifindex, guint16 pan_id); - guint16 (*wpan_get_short_addr) (NMPlatform *, int ifindex); - gboolean (*wpan_set_short_addr) (NMPlatform *, int ifindex, guint16 short_addr); - gboolean (*wpan_set_channel) (NMPlatform *, int ifindex, guint8 page, guint8 channel); - - gboolean (*object_delete) (NMPlatform *, const NMPObject *obj); - - gboolean (*ip4_address_add) (NMPlatform *, + gboolean (*infiniband_partition_add) (NMPlatform *self, int parent, int p_key, const NMPlatformLink **out_link); + gboolean (*infiniband_partition_delete) (NMPlatform *self, int parent, int p_key); + + gboolean (*wifi_get_capabilities) (NMPlatform *self, int ifindex, NMDeviceWifiCapabilities *caps); + gboolean (*wifi_get_bssid) (NMPlatform *self, int ifindex, guint8 *bssid); + guint32 (*wifi_get_frequency) (NMPlatform *self, int ifindex); + int (*wifi_get_quality) (NMPlatform *self, int ifindex); + guint32 (*wifi_get_rate) (NMPlatform *self, int ifindex); + NM80211Mode (*wifi_get_mode) (NMPlatform *self, int ifindex); + void (*wifi_set_mode) (NMPlatform *self, int ifindex, NM80211Mode mode); + void (*wifi_set_powersave) (NMPlatform *self, int ifindex, guint32 powersave); + guint32 (*wifi_find_frequency) (NMPlatform *self, int ifindex, const guint32 *freqs); + void (*wifi_indicate_addressing_running) (NMPlatform *self, int ifindex, gboolean running); + NMSettingWirelessWakeOnWLan (*wifi_get_wake_on_wlan) (NMPlatform *self, int ifindex); + gboolean (*wifi_set_wake_on_wlan) (NMPlatform *self, int ifindex, NMSettingWirelessWakeOnWLan wowl); + + guint32 (*mesh_get_channel) (NMPlatform *self, int ifindex); + gboolean (*mesh_set_channel) (NMPlatform *self, int ifindex, guint32 channel); + gboolean (*mesh_set_ssid) (NMPlatform *self, int ifindex, const guint8 *ssid, gsize len); + + guint16 (*wpan_get_pan_id) (NMPlatform *self, int ifindex); + gboolean (*wpan_set_pan_id) (NMPlatform *self, int ifindex, guint16 pan_id); + guint16 (*wpan_get_short_addr) (NMPlatform *self, int ifindex); + gboolean (*wpan_set_short_addr) (NMPlatform *self, int ifindex, guint16 short_addr); + gboolean (*wpan_set_channel) (NMPlatform *self, int ifindex, guint8 page, guint8 channel); + + gboolean (*object_delete) (NMPlatform *self, const NMPObject *obj); + + gboolean (*ip4_address_add) (NMPlatform *self, int ifindex, in_addr_t address, guint8 plen, @@ -1061,7 +1132,7 @@ typedef struct { guint32 preferred_lft, guint32 flags, const char *label); - gboolean (*ip6_address_add) (NMPlatform *, + gboolean (*ip6_address_add) (NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen, @@ -1069,10 +1140,10 @@ typedef struct { guint32 lifetime, guint32 preferred_lft, guint32 flags); - gboolean (*ip4_address_delete) (NMPlatform *, int ifindex, in_addr_t address, guint8 plen, in_addr_t peer_address); - gboolean (*ip6_address_delete) (NMPlatform *, int ifindex, struct in6_addr address, guint8 plen); + gboolean (*ip4_address_delete) (NMPlatform *self, int ifindex, in_addr_t address, guint8 plen, in_addr_t peer_address); + gboolean (*ip6_address_delete) (NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen); - int (*ip_route_add) (NMPlatform *, + int (*ip_route_add) (NMPlatform *self, NMPNlmFlags flags, int addr_family, const NMPlatformIPRoute *route); @@ -1215,7 +1286,7 @@ gboolean nm_platform_get_use_udev (NMPlatform *self); gboolean nm_platform_get_log_with_ptr (NMPlatform *self); NMPNetns *nm_platform_netns_get (NMPlatform *self); -gboolean nm_platform_netns_push (NMPlatform *platform, NMPNetns **netns); +gboolean nm_platform_netns_push (NMPlatform *self, NMPNetns **netns); const char *nm_link_type_to_string (NMLinkType link_type); @@ -1241,16 +1312,24 @@ const char *nm_link_type_to_string (NMLinkType link_type); int nm_platform_sysctl_open_netdir (NMPlatform *self, int ifindex, char *out_ifname); gboolean nm_platform_sysctl_set (NMPlatform *self, const char *pathid, int dirfd, const char *path, const char *value); +void nm_platform_sysctl_set_async (NMPlatform *self, + const char *pathid, + int dirfd, + const char *path, + const char *const *values, + NMPlatformAsyncCallback callback, + gpointer data, + GCancellable *cancellable); char *nm_platform_sysctl_get (NMPlatform *self, const char *pathid, int dirfd, const char *path); gint32 nm_platform_sysctl_get_int32 (NMPlatform *self, const char *pathid, int dirfd, const char *path, gint32 fallback); gint64 nm_platform_sysctl_get_int_checked (NMPlatform *self, const char *pathid, int dirfd, const char *path, guint base, gint64 min, gint64 max, gint64 fallback); -char *nm_platform_sysctl_ip_conf_get (NMPlatform *platform, +char *nm_platform_sysctl_ip_conf_get (NMPlatform *self, int addr_family, const char *ifname, const char *property); -gint64 nm_platform_sysctl_ip_conf_get_int_checked (NMPlatform *platform, +gint64 nm_platform_sysctl_ip_conf_get_int_checked (NMPlatform *self, int addr_family, const char *ifname, const char *property, @@ -1259,13 +1338,13 @@ gint64 nm_platform_sysctl_ip_conf_get_int_checked (NMPlatform *platform, gint64 max, gint64 fallback); -gboolean nm_platform_sysctl_ip_conf_set (NMPlatform *platform, +gboolean nm_platform_sysctl_ip_conf_set (NMPlatform *self, int addr_family, const char *ifname, const char *property, const char *value); -gboolean nm_platform_sysctl_ip_conf_set_int64 (NMPlatform *platform, +gboolean nm_platform_sysctl_ip_conf_set_int64 (NMPlatform *self, int addr_family, const char *ifname, const char *property, @@ -1274,6 +1353,10 @@ gboolean nm_platform_sysctl_ip_conf_set_int64 (NMPlatform *platform, gboolean nm_platform_sysctl_ip_conf_set_ipv6_hop_limit_safe (NMPlatform *self, const char *iface, int value); +int nm_platform_sysctl_ip_conf_get_rp_filter_ipv4 (NMPlatform *platform, + const char *iface, + gboolean consider_all, + gboolean *out_due_to_all); const char *nm_platform_if_indextoname (NMPlatform *self, int ifindex, char *out_ifname/* of size IFNAMSIZ */); int nm_platform_if_nametoindex (NMPlatform *self, const char *ifname); @@ -1298,7 +1381,7 @@ gboolean nm_platform_link_set_netns (NMPlatform *self, int ifindex, int netns_fd struct _NMDedupMultiHeadEntry; struct _NMPLookup; -const struct _NMDedupMultiHeadEntry *nm_platform_lookup (NMPlatform *platform, +const struct _NMDedupMultiHeadEntry *nm_platform_lookup (NMPlatform *self, const struct _NMPLookup *lookup); gboolean nm_platform_lookup_predicate_routes_main (const NMPObject *obj, @@ -1306,7 +1389,7 @@ gboolean nm_platform_lookup_predicate_routes_main (const NMPObject *obj, gboolean nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel (const NMPObject *obj, gpointer user_data); -GPtrArray *nm_platform_lookup_clone (NMPlatform *platform, +GPtrArray *nm_platform_lookup_clone (NMPlatform *self, const struct _NMPLookup *lookup, NMPObjectPredicateFunc predicate, gpointer user_data); @@ -1325,18 +1408,6 @@ gboolean nm_platform_link_get_user_ipv6ll_enabled (NMPlatform *self, int ifindex gconstpointer nm_platform_link_get_address (NMPlatform *self, int ifindex, size_t *length); -static inline GBytes * -nm_platform_link_get_address_as_bytes (NMPlatform *self, int ifindex) -{ - gconstpointer p; - gsize l; - - p = nm_platform_link_get_address (self, ifindex, &l); - return p - ? g_bytes_new (p, l) - : NULL; -} - int nm_platform_link_get_master (NMPlatform *self, int slave); gboolean nm_platform_link_can_assume (NMPlatform *self, int ifindex); @@ -1368,7 +1439,15 @@ gboolean nm_platform_link_get_permanent_address (NMPlatform *self, int ifindex, int nm_platform_link_set_address (NMPlatform *self, int ifindex, const void *address, size_t length); int nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu); gboolean nm_platform_link_set_name (NMPlatform *self, int ifindex, const char *name); -gboolean nm_platform_link_set_sriov_params (NMPlatform *self, int ifindex, guint num_vfs, int autoprobe); + +void nm_platform_link_set_sriov_params_async (NMPlatform *self, + int ifindex, + guint num_vfs, + int autoprobe, + NMPlatformAsyncCallback callback, + gpointer callback_data, + GCancellable *cancellable); + gboolean nm_platform_link_set_sriov_vfs (NMPlatform *self, int ifindex, const NMPlatformVF *const *vfs); gboolean nm_platform_link_set_bridge_vlans (NMPlatform *self, int ifindex, gboolean on_master, const NMPlatformBridgeVlan *const *vlans); @@ -1466,11 +1545,11 @@ guint32 nm_platform_mesh_get_channel (NMPlatform *self, int ifindex); gboolean nm_platform_mesh_set_channel (NMPlatform *self, int ifindex, guint32 channel); gboolean nm_platform_mesh_set_ssid (NMPlatform *self, int ifindex, const guint8 *ssid, gsize len); -guint16 nm_platform_wpan_get_pan_id (NMPlatform *platform, int ifindex); -gboolean nm_platform_wpan_set_pan_id (NMPlatform *platform, int ifindex, guint16 pan_id); -guint16 nm_platform_wpan_get_short_addr (NMPlatform *platform, int ifindex); -gboolean nm_platform_wpan_set_short_addr (NMPlatform *platform, int ifindex, guint16 short_addr); -gboolean nm_platform_wpan_set_channel (NMPlatform *platform, int ifindex, guint8 page, guint8 channel); +guint16 nm_platform_wpan_get_pan_id (NMPlatform *self, int ifindex); +gboolean nm_platform_wpan_set_pan_id (NMPlatform *self, int ifindex, guint16 pan_id); +guint16 nm_platform_wpan_get_short_addr (NMPlatform *self, int ifindex); +gboolean nm_platform_wpan_set_short_addr (NMPlatform *self, int ifindex, guint16 short_addr); +gboolean nm_platform_wpan_set_channel (NMPlatform *self, int ifindex, guint8 page, guint8 channel); void nm_platform_ip4_address_set_addr (NMPlatformIP4Address *addr, in_addr_t address, guint8 plen); const struct in6_addr *nm_platform_ip6_address_get_peer (const NMPlatformIP6Address *addr); @@ -1704,6 +1783,8 @@ void nm_platform_lnk_wireguard_hash_update (const NMPlatformLnkWireGuard *obj, N void nm_platform_qdisc_hash_update (const NMPlatformQdisc *obj, NMHashState *h); void nm_platform_tfilter_hash_update (const NMPlatformTfilter *obj, NMHashState *h); +#define NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN ((gsize) 162) + const char *nm_platform_link_flags2str (unsigned flags, char *buf, gsize len); const char *nm_platform_link_inet6_addrgenmode2str (guint8 mode, char *buf, gsize len); const char *nm_platform_addr_flags2str (unsigned flags, char *buf, gsize len); diff --git a/src/platform/nmp-netns.c b/src/platform/nmp-netns.c index f34dc83d..b33d86fb 100644 --- a/src/platform/nmp-netns.c +++ b/src/platform/nmp-netns.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify diff --git a/src/platform/nmp-netns.h b/src/platform/nmp-netns.h index 55a4b95f..3c0ca74b 100644 --- a/src/platform/nmp-netns.h +++ b/src/platform/nmp-netns.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index 0d20100c..5cbf9428 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify @@ -398,7 +397,7 @@ _idx_obj_part (const DedupMultiIdxType *idx_type, if (h) { nm_hash_update_vals (h, idx_type->cache_id_type, - NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (obj_a)->ifindex); + obj_a->obj_with_ifindex.ifindex); } return 1; @@ -787,7 +786,7 @@ _nmp_object_new_from_class (const NMPClass *klass) } NMPObject * -nmp_object_new (NMPObjectType obj_type, const NMPlatformObject *plobj) +nmp_object_new (NMPObjectType obj_type, gconstpointer plobj) { const NMPClass *klass = nmp_class_from_type (obj_type); NMPObject *obj; @@ -2147,9 +2146,12 @@ nmp_lookup_init_object_by_addr_family (NMPLookup *lookup, NMPObject *o; nm_assert (lookup); - nm_assert_addr_family (addr_family); nm_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_ROUTING_RULE)); + if (addr_family == AF_UNSPEC) + return nmp_lookup_init_obj_type (lookup, obj_type); + + nm_assert_addr_family (addr_family); o = _nmp_object_stackinit_from_type (&lookup->selector_obj, obj_type); NMP_OBJECT_CAST_ROUTING_RULE (o)->addr_family = addr_family; lookup->cache_id_type = NMP_CACHE_ID_TYPE_OBJECT_BY_ADDR_FAMILY; diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index 3ce9db36..cea5f958 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify @@ -535,101 +534,27 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX (NMPObjectType obj_type) _obj ? &NM_CONSTCAST (NMPObject, _obj)->obj_with_ifindex : NULL; \ }) -#define NMP_OBJECT_CAST_LINK(obj) \ +#define _NMP_OBJECT_CAST(obj, field, ...) \ ({ \ typeof (obj) _obj = (obj); \ \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_LINK); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->link : NULL; \ + nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), __VA_ARGS__)); \ + _obj ? &NM_CONSTCAST (NMPObject, _obj)->field : NULL; \ }) -#define NMP_OBJECT_CAST_IP_ADDRESS(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip_address : NULL; \ - }) - -#define NMP_OBJECT_CAST_IPX_ADDRESS(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS)); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->ipx_address : NULL; \ - }) - -#define NMP_OBJECT_CAST_IP4_ADDRESS(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_IP4_ADDRESS); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip4_address : NULL; \ - }) - -#define NMP_OBJECT_CAST_IP6_ADDRESS(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_IP6_ADDRESS); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip6_address : NULL; \ - }) - -#define NMP_OBJECT_CAST_IPX_ROUTE(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->ipx_route : NULL; \ - }) - -#define NMP_OBJECT_CAST_IP_ROUTE(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NM_IN_SET (NMP_OBJECT_GET_TYPE (_obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip_route : NULL; \ - }) - -#define NMP_OBJECT_CAST_IP4_ROUTE(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_IP4_ROUTE); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip4_route : NULL; \ - }) - -#define NMP_OBJECT_CAST_IP6_ROUTE(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_IP6_ROUTE); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->ip6_route : NULL; \ - }) - -#define NMP_OBJECT_CAST_ROUTING_RULE(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_ROUTING_RULE); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->routing_rule : NULL; \ - }) - -#define NMP_OBJECT_CAST_QDISC(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_QDISC); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->qdisc : NULL; \ - }) - -#define NMP_OBJECT_CAST_TFILTER(obj) \ - ({ \ - typeof (obj) _obj = (obj); \ - \ - nm_assert (!_obj || NMP_OBJECT_GET_TYPE (_obj) == NMP_OBJECT_TYPE_TFILTER); \ - _obj ? &NM_CONSTCAST (NMPObject, _obj)->tfilter : NULL; \ - }) +#define NMP_OBJECT_CAST_LINK(obj) _NMP_OBJECT_CAST (obj, link, NMP_OBJECT_TYPE_LINK) +#define NMP_OBJECT_CAST_IP_ADDRESS(obj) _NMP_OBJECT_CAST (obj, ip_address, NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS) +#define NMP_OBJECT_CAST_IPX_ADDRESS(obj) _NMP_OBJECT_CAST (obj, ipx_address, NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS) +#define NMP_OBJECT_CAST_IP4_ADDRESS(obj) _NMP_OBJECT_CAST (obj, ip4_address, NMP_OBJECT_TYPE_IP4_ADDRESS) +#define NMP_OBJECT_CAST_IP6_ADDRESS(obj) _NMP_OBJECT_CAST (obj, ip6_address, NMP_OBJECT_TYPE_IP6_ADDRESS) +#define NMP_OBJECT_CAST_IP_ROUTE(obj) _NMP_OBJECT_CAST (obj, ip_route, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE) +#define NMP_OBJECT_CAST_IPX_ROUTE(obj) _NMP_OBJECT_CAST (obj, ipx_route, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE) +#define NMP_OBJECT_CAST_IP4_ROUTE(obj) _NMP_OBJECT_CAST (obj, ip4_route, NMP_OBJECT_TYPE_IP4_ROUTE) +#define NMP_OBJECT_CAST_IP6_ROUTE(obj) _NMP_OBJECT_CAST (obj, ip6_route, NMP_OBJECT_TYPE_IP6_ROUTE) +#define NMP_OBJECT_CAST_ROUTING_RULE(obj) _NMP_OBJECT_CAST (obj, routing_rule, NMP_OBJECT_TYPE_ROUTING_RULE) +#define NMP_OBJECT_CAST_QDISC(obj) _NMP_OBJECT_CAST (obj, qdisc, NMP_OBJECT_TYPE_QDISC) +#define NMP_OBJECT_CAST_TFILTER(obj) _NMP_OBJECT_CAST (obj, tfilter, NMP_OBJECT_TYPE_TFILTER) +#define NMP_OBJECT_CAST_LNK_WIREGUARD(obj) _NMP_OBJECT_CAST (obj, lnk_wireguard, NMP_OBJECT_TYPE_LNK_WIREGUARD) static inline const NMPObject * nmp_object_ref (const NMPObject *obj) @@ -674,7 +599,7 @@ nmp_object_unref (const NMPObject *obj) _changed; \ }) -NMPObject *nmp_object_new (NMPObjectType obj_type, const NMPlatformObject *plob); +NMPObject *nmp_object_new (NMPObjectType obj_type, gconstpointer plobj); NMPObject *nmp_object_new_link (int ifindex); const NMPObject *nmp_object_stackinit (NMPObject *obj, NMPObjectType obj_type, gconstpointer plobj); @@ -1059,4 +984,15 @@ nm_platform_lookup_ip6_route_by_weak_id (NMPlatform *platform, return nm_platform_lookup (platform, &lookup); } +static inline const NMDedupMultiHeadEntry * +nm_platform_lookup_object_by_addr_family (NMPlatform *platform, + NMPObjectType obj_type, + int addr_family) +{ + NMPLookup lookup; + + nmp_lookup_init_object_by_addr_family (&lookup, obj_type, addr_family); + return nm_platform_lookup (platform, &lookup); +} + #endif /* __NMP_OBJECT_H__ */ diff --git a/src/platform/nmp-rules-manager.c b/src/platform/nmp-rules-manager.c index 970afcde..f982f04c 100644 --- a/src/platform/nmp-rules-manager.c +++ b/src/platform/nmp-rules-manager.c @@ -81,7 +81,7 @@ typedef struct { * But it will not remove or add them otherwise. * * Otherwise, the track_priority_val goes together with track_priority_present. - * In case of one rule being tracked multile times (with different priorities), + * In case of one rule being tracked multiple times (with different priorities), * the one with higher priority wins. See _rules_obj_get_best_data(). * Then, the winning present state either enforces that the rule is present * or absent. @@ -99,6 +99,17 @@ typedef enum { CONFIG_STATE_NONE = 0, CONFIG_STATE_ADDED_BY_US = 1, CONFIG_STATE_REMOVED_BY_US = 2, + + /* ConfigState encodes whether the rule was touched by us at all (CONFIG_STATE_NONE). + * + * Maybe we would only need to track whether we touched the rule at all. But we + * track it more in detail what we did: did we add it (CONFIG_STATE_ADDED_BY_US) + * or did we remove it (CONFIG_STATE_REMOVED_BY_US)? + * Finally, we need CONFIG_STATE_OWNED_BY_US, which means that we didn't actively + * add/remove it, but whenever we are about to undo the add/remove, we need to do it. + * In that sense, CONFIG_STATE_OWNED_BY_US is really just a flag that we unconditionally + * force the state next time when necessary. */ + CONFIG_STATE_OWNED_BY_US = 3, } ConfigState; typedef struct { @@ -111,8 +122,10 @@ typedef struct { * This makes NMPRulesManager stateful (beyond the configuration that indicates * which rules are tracked). * After a restart, NetworkManager would no longer remember which rules were added - * by us. That would need to be fixed by persisting the state and reloading it after - * restart. */ + * by us. + * + * That is partially fixed by NetworkManager taking over the rules that it + * actively configures (see %NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG). */ ConfigState config_state; } RulesObjData; @@ -121,6 +134,15 @@ typedef struct { CList user_tag_lst_head; } RulesUserTagData; +/*****************************************************************************/ + +static void _rules_data_untrack (NMPRulesManager *self, + RulesData *rules_data, + gboolean remove_user_tag_data, + gboolean make_owned_by_us); + +/*****************************************************************************/ + static void _rules_data_assert (const RulesData *rules_data, gboolean linked) { @@ -278,11 +300,31 @@ _rules_data_lookup (GHashTable *by_data, return g_hash_table_lookup (by_data, &rules_data_needle); } +/** + * nmp_rules_manager_track: + * @self: the #NMPRulesManager instance + * @routing_rule: the #NMPlatformRoutingRule to track or untrack + * @track_priority: the priority for tracking the rule. Note that + * negative values indicate a forced absence of the rule. Priorities + * are compared with their absolute values (with higher absolute + * value being more important). For example, if you track the same + * rule twice, once with priority -5 and +10, then the rule is + * present (because the positive number is more important). + * The special value 0 indicates weakly-tracked rules. + * @user_tag: the tag associated with tracking this rule. The same tag + * must be used to untrack the rule later. + * @user_tag_untrack: if not %NULL, at the same time untrack this user-tag + * for the same rule. Note that this is different from a plain nmp_rules_manager_untrack(), + * because it enforces ownership of the now tracked rule. On the other hand, + * a plain nmp_rules_manager_untrack() merely forgets about the tracking. + * The purpose here is to set this to %NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG. + */ void nmp_rules_manager_track (NMPRulesManager *self, const NMPlatformRoutingRule *routing_rule, gint32 track_priority, - gconstpointer user_tag) + gconstpointer user_tag, + gconstpointer user_tag_untrack) { NMPObject obj_stack; const NMPObject *p_obj_stack; @@ -359,6 +401,17 @@ nmp_rules_manager_track (NMPRulesManager *self, } } + if (user_tag_untrack) { + if (user_tag != user_tag_untrack) { + RulesData *rules_data_untrack; + + rules_data_untrack = _rules_data_lookup (self->by_data, p_obj_stack, user_tag_untrack); + if (rules_data_untrack) + _rules_data_untrack (self, rules_data_untrack, FALSE, TRUE); + } else + nm_assert_not_reached (); + } + _rules_data_assert (rules_data, TRUE); if (changed) { @@ -377,7 +430,8 @@ nmp_rules_manager_track (NMPRulesManager *self, static void _rules_data_untrack (NMPRulesManager *self, RulesData *rules_data, - gboolean remove_user_tag_data) + gboolean remove_user_tag_data, + gboolean make_owned_by_us) { RulesObjData *obj_data; @@ -401,15 +455,22 @@ _rules_data_untrack (NMPRulesManager *self, #endif nm_assert (!c_list_is_empty (&rules_data->user_tag_lst)); - if ( remove_user_tag_data - && c_list_length_is (&rules_data->user_tag_lst, 1)) - g_hash_table_remove (self->by_user_tag, &rules_data->user_tag); obj_data = g_hash_table_lookup (self->by_obj, &rules_data->obj); nm_assert (obj_data); nm_assert (c_list_contains (&obj_data->obj_lst_head, &rules_data->obj_lst)); nm_assert (obj_data == g_hash_table_lookup (self->by_obj, &rules_data->obj)); + if (make_owned_by_us) { + if (obj_data->config_state == CONFIG_STATE_NONE) { + /* we need to mark this entry that it requires a touch on the next + * sync. */ + obj_data->config_state = CONFIG_STATE_OWNED_BY_US; + } + } else if ( remove_user_tag_data + && c_list_length_is (&rules_data->user_tag_lst, 1)) + g_hash_table_remove (self->by_user_tag, &rules_data->user_tag); + /* if obj_data is marked to be "added_by_us" or "removed_by_us", we need to keep this entry * around for the next sync -- so that we can undo what we did earlier. */ if ( obj_data->config_state == CONFIG_STATE_NONE @@ -440,7 +501,7 @@ nmp_rules_manager_untrack (NMPRulesManager *self, rules_data = _rules_data_lookup (self->by_data, p_obj_stack, user_tag); if (rules_data) - _rules_data_untrack (self, rules_data, TRUE); + _rules_data_untrack (self, rules_data, TRUE, FALSE); } void @@ -486,7 +547,7 @@ nmp_rules_manager_untrack_all (NMPRulesManager *self, c_list_for_each_entry_safe (rules_data, rules_data_safe, &user_tag_data->user_tag_lst_head, user_tag_lst) { if ( all || rules_data->dirty) - _rules_data_untrack (self, rules_data, FALSE); + _rules_data_untrack (self, rules_data, FALSE, FALSE); } if (c_list_is_empty (&user_tag_data->user_tag_lst_head)) g_hash_table_remove (self->by_user_tag, user_tag_data); @@ -525,11 +586,17 @@ nmp_rules_manager_sync (NMPRulesManager *self, rd_best = _rules_obj_get_best_data (obj_data); if (rd_best) { - if (rd_best->track_priority_present) + if (rd_best->track_priority_present) { + if (obj_data->config_state == CONFIG_STATE_OWNED_BY_US) + obj_data->config_state = CONFIG_STATE_ADDED_BY_US; continue; + } if (rd_best->track_priority_val == 0) { - if (obj_data->config_state != CONFIG_STATE_ADDED_BY_US) + if (!NM_IN_SET (obj_data->config_state, CONFIG_STATE_ADDED_BY_US, + CONFIG_STATE_OWNED_BY_US)) { + obj_data->config_state = CONFIG_STATE_NONE; continue; + } obj_data->config_state = CONFIG_STATE_NONE; } } @@ -563,11 +630,17 @@ nmp_rules_manager_sync (NMPRulesManager *self, continue; } - if (!rd_best->track_priority_present) + if (!rd_best->track_priority_present) { + if (obj_data->config_state == CONFIG_STATE_OWNED_BY_US) + obj_data->config_state = CONFIG_STATE_REMOVED_BY_US; continue; + } if (rd_best->track_priority_val == 0) { - if (obj_data->config_state != CONFIG_STATE_REMOVED_BY_US) + if (!NM_IN_SET (obj_data->config_state, CONFIG_STATE_REMOVED_BY_US, + CONFIG_STATE_OWNED_BY_US)) { + obj_data->config_state = CONFIG_STATE_NONE; continue; + } obj_data->config_state = CONFIG_STATE_NONE; } @@ -610,7 +683,7 @@ nmp_rules_manager_track_from_platform (NMPRulesManager *self, && rr->addr_family != addr_family) continue; - nmp_rules_manager_track (self, rr, tracking_priority, user_tag); + nmp_rules_manager_track (self, rr, tracking_priority, user_tag, NULL); } } @@ -638,7 +711,8 @@ nmp_rules_manager_track_default (NMPRulesManager *self, .protocol = RTPROT_KERNEL, }), track_priority, - user_tag); + user_tag, + NULL); nmp_rules_manager_track (self, &((NMPlatformRoutingRule) { .addr_family = AF_INET, @@ -648,7 +722,8 @@ nmp_rules_manager_track_default (NMPRulesManager *self, .protocol = RTPROT_KERNEL, }), track_priority, - user_tag); + user_tag, + NULL); nmp_rules_manager_track (self, &((NMPlatformRoutingRule) { .addr_family = AF_INET, @@ -658,7 +733,8 @@ nmp_rules_manager_track_default (NMPRulesManager *self, .protocol = RTPROT_KERNEL, }), track_priority, - user_tag); + user_tag, + NULL); } if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET6)) { nmp_rules_manager_track (self, @@ -670,7 +746,8 @@ nmp_rules_manager_track_default (NMPRulesManager *self, .protocol = RTPROT_KERNEL, }), track_priority, - user_tag); + user_tag, + NULL); nmp_rules_manager_track (self, &((NMPlatformRoutingRule) { .addr_family = AF_INET6, @@ -680,7 +757,8 @@ nmp_rules_manager_track_default (NMPRulesManager *self, .protocol = RTPROT_KERNEL, }), track_priority, - user_tag); + user_tag, + NULL); } } diff --git a/src/platform/nmp-rules-manager.h b/src/platform/nmp-rules-manager.h index 310c7971..645df5c2 100644 --- a/src/platform/nmp-rules-manager.h +++ b/src/platform/nmp-rules-manager.h @@ -22,6 +22,8 @@ /*****************************************************************************/ +#define NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG ((const void *) nmp_rules_manager_new) + typedef struct _NMPRulesManager NMPRulesManager; NMPRulesManager *nmp_rules_manager_new (NMPlatform *platform); @@ -35,7 +37,8 @@ NM_AUTO_DEFINE_FCN0 (NMPRulesManager *, _nmp_rules_manager_unref, nmp_rules_mana void nmp_rules_manager_track (NMPRulesManager *self, const NMPlatformRoutingRule *routing_rule, gint32 track_priority, - gconstpointer user_tag); + gconstpointer user_tag, + gconstpointer user_tag_untrack); void nmp_rules_manager_track_default (NMPRulesManager *self, int addr_family, diff --git a/src/platform/tests/meson.build b/src/platform/tests/meson.build index 8086a46c..aaee8964 100644 --- a/src/platform/tests/meson.build +++ b/src/platform/tests/meson.build @@ -1,23 +1,22 @@ test_units = [ - ['test-link-fake', 'test-link.c', test_nm_dep_fake, default_test_timeout], - ['test-link-linux', 'test-link.c', test_nm_dep_linux, 900], - ['test-address-fake', 'test-address.c', test_nm_dep_fake, default_test_timeout], - ['test-address-linux', 'test-address.c', test_nm_dep_linux, default_test_timeout], - ['test-general', 'test-general.c', test_nm_dep, default_test_timeout], - ['test-nmp-object', 'test-nmp-object.c', test_nm_dep, default_test_timeout], - ['test-route-fake', 'test-route.c', test_nm_dep_fake, default_test_timeout], - ['test-route-linux', 'test-route.c', test_nm_dep_linux, default_test_timeout], - ['test-cleanup-fake', 'test-cleanup.c', test_nm_dep_fake, default_test_timeout], - ['test-cleanup-linux', 'test-cleanup.c', test_nm_dep_linux, default_test_timeout], + [ 'test-address-fake', 'test-address.c', test_nm_dep_fake, default_test_timeout ], + [ 'test-address-linux', 'test-address.c', test_nm_dep_linux, default_test_timeout ], + [ 'test-cleanup-fake', 'test-cleanup.c', test_nm_dep_fake, default_test_timeout ], + [ 'test-cleanup-linux', 'test-cleanup.c', test_nm_dep_linux, default_test_timeout ], + [ 'test-link-fake', 'test-link.c', test_nm_dep_fake, default_test_timeout ], + [ 'test-link-linux', 'test-link.c', test_nm_dep_linux, 900 ], + [ 'test-nmp-object', 'test-nmp-object.c', test_nm_dep, default_test_timeout ], + [ 'test-platform-general', 'test-platform-general.c', test_nm_dep, default_test_timeout ], + [ 'test-route-fake', 'test-route.c', test_nm_dep_fake, default_test_timeout ], + [ 'test-route-linux', 'test-route.c', test_nm_dep_linux, default_test_timeout ], ] foreach test_unit: test_units exe = executable( - 'platform-' + test_unit[0], + test_unit[0], test_unit[1], dependencies: test_unit[2], ) - test( 'platform/' + test_unit[0], test_script, @@ -26,10 +25,8 @@ foreach test_unit: test_units ) endforeach -test = 'monitor' - executable( - test, - test + '.c', + 'monitor', + 'monitor.c', dependencies: test_nm_dep, ) diff --git a/src/platform/tests/monitor.c b/src/platform/tests/monitor.c index fff5967b..6a5e0e16 100644 --- a/src/platform/tests/monitor.c +++ b/src/platform/tests/monitor.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c index d84a806d..3a3e9009 100644 --- a/src/platform/tests/test-address.c +++ b/src/platform/tests/test-address.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c index 6c73a63e..70d64c15 100644 --- a/src/platform/tests/test-cleanup.c +++ b/src/platform/tests/test-cleanup.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c index 7ebda9ff..cd0bef45 100644 --- a/src/platform/tests/test-common.c +++ b/src/platform/tests/test-common.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -679,7 +678,7 @@ nmtstp_run_command_check_external_global (void) { if (!nmtstp_is_root_test ()) return FALSE; - switch (nmtst_get_rand_int () % 3) { + switch (nmtst_get_rand_uint32 () % 3) { case 0: return -1; case 1: @@ -699,7 +698,7 @@ nmtstp_run_command_check_external (int external_command) } if (!nmtstp_is_root_test ()) return FALSE; - return (nmtst_get_rand_int () % 2) == 0; + return (nmtst_get_rand_uint32 () % 2) == 0; } /*****************************************************************************/ @@ -843,7 +842,7 @@ _ip_address_add (NMPlatform *platform, g_assert (flags == 0); if ( peer_address->addr4 != address->addr4 - || nmtst_get_rand_int () % 2) { + || nmtst_get_rand_uint32 () % 2) { /* If the peer is the same as the local address, we can omit it. The result should be identical */ nm_sprintf_buf (s_peer, " peer %s", nm_utils_inet4_ntop (peer_address->addr4, b2)); } else @@ -1994,7 +1993,7 @@ nmtstp_netns_select_random (NMPlatform **platforms, gsize n_platforms, NMPNetns for (i = 0; i < n_platforms; i++) g_assert (NM_IS_PLATFORM (platforms[i])); - i = nmtst_get_rand_int () % (n_platforms + 1); + i = nmtst_get_rand_uint32 () % (n_platforms + 1); if (i == 0) return; g_assert (nm_platform_netns_push (platforms[i - 1], netns)); diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index a0650cc1..5926dac1 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -241,7 +240,7 @@ test_slave (int master, int type, SignalData *master_changed) /* Set master up */ g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, master, NULL)); g_assert (nm_platform_link_is_up (NM_PLATFORM_GET, master)); - accept_signals (master_changed, 1, 2); + accept_signals (master_changed, 1, 3); g_signal_handlers_disconnect_by_func (NM_PLATFORM_GET, G_CALLBACK (test_link_changed_signal_cb), &test_link_changed_signal_arg1); g_signal_handlers_disconnect_by_func (NM_PLATFORM_GET, G_CALLBACK (test_link_changed_signal_cb), &test_link_changed_signal_arg2); @@ -287,7 +286,7 @@ test_slave (int master, int type, SignalData *master_changed) g_assert (nm_platform_link_is_connected (NM_PLATFORM_GET, master)); accept_signals (link_changed, 1, 3); /* NM running, can cause additional change of addrgenmode */ - accept_signals (master_changed, 0, 2); + accept_signals (master_changed, 0, 3); /* Enslave again * @@ -329,7 +328,7 @@ test_slave (int master, int type, SignalData *master_changed) ensure_no_signal (link_changed); accept_signal (link_removed); } - accept_signals (master_changed, 0, 2); + accept_signals (master_changed, 0, 3); ensure_no_signal (master_changed); @@ -515,8 +514,8 @@ test_bridge_addr (void) link = *plink; g_assert_cmpstr (link.name, ==, DEVICE_NAME); - g_assert_cmpint (link.addr.len, ==, sizeof (addr)); - g_assert (!memcmp (link.addr.data, addr, sizeof (addr))); + g_assert_cmpint (link.l_address.len, ==, sizeof (addr)); + g_assert (!memcmp (link.l_address.data, addr, sizeof (addr))); plink = nm_platform_link_get (NM_PLATFORM_GET, link.ifindex); g_assert (plink); @@ -538,8 +537,8 @@ test_bridge_addr (void) g_assert_cmpint (_nm_platform_uint8_inv (plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_EUI64); } - g_assert_cmpint (plink->addr.len, ==, sizeof (addr)); - g_assert (!memcmp (plink->addr.data, addr, sizeof (addr))); + g_assert_cmpint (plink->l_address.len, ==, sizeof (addr)); + g_assert (!memcmp (plink->l_address.data, addr, sizeof (addr))); nmtstp_link_delete (NULL, -1, link.ifindex, link.name, TRUE); } @@ -2100,7 +2099,7 @@ test_create_many_links_do (guint n_devices) char name[64]; const NMPlatformLink *pllink; gs_unref_array GArray *ifindexes = g_array_sized_new (FALSE, FALSE, sizeof (int), n_devices); - const int EX = ((int) (nmtst_get_rand_int () % 4)) - 1; + const int EX = ((int) (nmtst_get_rand_uint32 () % 4)) - 1; g_assert (EX >= -1 && EX <= 2); @@ -2459,17 +2458,17 @@ test_netns_general (gpointer fixture, gconstpointer test_data) NMPlatform *p = (k == 0 ? platform_1 : platform_2); const char *id = (k == 0 ? "a" : "b"); - for (i = 0, j = nmtst_get_rand_int () % 5; i < j; i++) + for (i = 0, j = nmtst_get_rand_uint32 () % 5; i < j; i++) _ADD_DUMMY (p, nm_sprintf_buf (sbuf, "other-a-%s-%02d", id, i)); _ADD_DUMMY (p, "dummy1_"); - for (i = 0, j = nmtst_get_rand_int () % 5; i < j; i++) + for (i = 0, j = nmtst_get_rand_uint32 () % 5; i < j; i++) _ADD_DUMMY (p, nm_sprintf_buf (sbuf, "other-b-%s-%02d", id, i)); _ADD_DUMMY (p, nm_sprintf_buf (sbuf, "dummy2%s", id)); - for (i = 0, j = nmtst_get_rand_int () % 5; i < j; i++) + for (i = 0, j = nmtst_get_rand_uint32 () % 5; i < j; i++) _ADD_DUMMY (p, nm_sprintf_buf (sbuf, "other-c-%s-%02d", id, i)); } @@ -2491,17 +2490,17 @@ test_netns_general (gpointer fixture, gconstpointer test_data) NMPlatform *pl; const char *path; - j = nmtst_get_rand_int () % 2; + j = nmtst_get_rand_uint32 () % 2; - if (nmtst_get_rand_int () % 2) { + if (nmtst_get_rand_uint32 () % 2) { pl = platform_1; - if (nmtst_get_rand_int () % 2) + if (nmtst_get_rand_uint32 () % 2) path = "/proc/sys/net/ipv6/conf/dummy1_/disable_ipv6"; else path = "/proc/sys/net/ipv6/conf/dummy2a/disable_ipv6"; } else { pl = platform_2; - if (nmtst_get_rand_int () % 2) + if (nmtst_get_rand_uint32 () % 2) path = "/proc/sys/net/ipv6/conf/dummy1_/disable_ipv6"; else path = "/proc/sys/net/ipv6/conf/dummy2b/disable_ipv6"; @@ -2654,7 +2653,7 @@ test_netns_push (gpointer fixture, gconstpointer test_data) pl[2].platform = platform_2 = _test_netns_create_platform (); pl_base = &pl[0]; - i = nmtst_get_rand_int () % (G_N_ELEMENTS (pl) + 1); + i = nmtst_get_rand_uint32 () % (G_N_ELEMENTS (pl) + 1); if (i < G_N_ELEMENTS (pl)) { pl_base = &pl[i]; g_assert (nm_platform_netns_push (pl[i].platform, &netns_pop)); @@ -2674,7 +2673,7 @@ test_netns_push (gpointer fixture, gconstpointer test_data) g_ptr_array_add (device_names, tmp); pl[i].sysctl_path = tmp; - pl[i].sysctl_value = nmtst_get_rand_int () % 2 ? "1" : "0"; + pl[i].sysctl_value = nmtst_get_rand_uint32 () % 2 ? "1" : "0"; _ADD_DUMMY (pl[i].platform, pl[i].device_name); @@ -2689,16 +2688,16 @@ test_netns_push (gpointer fixture, gconstpointer test_data) pl[i].ns_mnt = tmp; } - nstack = nmtst_get_rand_int () % (G_N_ELEMENTS (stack) + 1); + nstack = nmtst_get_rand_uint32 () % (G_N_ELEMENTS (stack) + 1); for (i = 0; i < nstack; i++) { - stack[i].pl = &pl[nmtst_get_rand_int () % G_N_ELEMENTS (pl)]; - stack[i].ns_types = ns_types_list[nmtst_get_rand_int () % G_N_ELEMENTS (ns_types_list)]; + stack[i].pl = &pl[nmtst_get_rand_uint32 () % G_N_ELEMENTS (pl)]; + stack[i].ns_types = ns_types_list[nmtst_get_rand_uint32 () % G_N_ELEMENTS (ns_types_list)]; nmp_netns_push_type (nm_platform_netns_get (stack[i].pl->platform), stack[i].ns_types); } /* pop some again. */ - for (i = nmtst_get_rand_int () % (nstack + 1); i > 0; i--) { + for (i = nmtst_get_rand_uint32 () % (nstack + 1); i > 0; i--) { g_assert (nstack > 0); nstack--; nmp_netns_pop (nm_platform_netns_get (stack[nstack].pl->platform)); @@ -2765,9 +2764,9 @@ test_netns_push (gpointer fixture, gconstpointer test_data) static void test_netns_bind_to_path (gpointer fixture, gconstpointer test_data) { -#define P_VAR_RUN "/var/run" -#define P_VAR_RUN_NETNS "/var/run/netns" -#define P_VAR_RUN_NETNS_BINDNAME "/var/run/netns/"P_NETNS_BINDNAME +#define P_VAR_RUN "/run" +#define P_VAR_RUN_NETNS "/run/netns" +#define P_VAR_RUN_NETNS_BINDNAME "/run/netns/"P_NETNS_BINDNAME #define P_NETNS_BINDNAME "nmtst-iproute2-netns" gs_unref_object NMPlatform *platform_0 = NULL; gs_unref_object NMPlatform *platform_1 = NULL; @@ -2789,7 +2788,7 @@ test_netns_bind_to_path (gpointer fixture, gconstpointer test_data) g_assert_cmpint (mount ("tmpfs", P_VAR_RUN, "tmpfs", MS_NOATIME | MS_NODEV | MS_NOSUID, "mode=0755,size=32K"), ==, 0); g_assert_cmpint (mkdir (P_VAR_RUN_NETNS, 755), ==, 0); - i = (nmtst_get_rand_int () % 2) + 1; + i = (nmtst_get_rand_uint32 () % 2) + 1; netns = nm_platform_netns_get (platforms[i]); _ADD_DUMMY (platforms[i], "dummy2b"); @@ -2832,9 +2831,9 @@ test_sysctl_rename (void) ifindex[0] = nmtstp_link_dummy_add (PL, -1, IFNAME[0])->ifindex; ifindex[1] = nmtstp_link_dummy_add (PL, -1, IFNAME[1])->ifindex; - s = (nmtst_get_rand_int () % 2) ? NULL : ifname_buf; + s = (nmtst_get_rand_uint32 () % 2) ? NULL : ifname_buf; - if (nmtst_get_rand_int () % 2) { + if (nmtst_get_rand_uint32 () % 2) { /* bring the platform cache out of sync */ nmtstp_run_command_check ("ip link set %s name %s", IFNAME[0], IFNAME[2]); nm_platform_process_events (PL); @@ -2847,7 +2846,7 @@ test_sysctl_rename (void) } /* open dirfd for IFNAME[0] */ - i = nmtst_get_rand_int () % (2 + G_N_ELEMENTS (IFNAME)); + i = nmtst_get_rand_uint32 () % (2 + G_N_ELEMENTS (IFNAME)); if (i == 0) { dirfd = nm_platform_sysctl_open_netdir (PL, ifindex[0], @@ -2866,7 +2865,7 @@ test_sysctl_rename (void) g_assert_cmpstr (s, ==, IFNAME[0]); /* possibly rename the interfaces. */ - switch (nmtst_get_rand_int () % 4) { + switch (nmtst_get_rand_uint32 () % 4) { case 0: break; case 1: @@ -2879,11 +2878,11 @@ test_sysctl_rename (void) } /* possibly, resync platform cache (should make no difference). */ - if (nmtst_get_rand_int () % 2) + if (nmtst_get_rand_uint32 () % 2) nm_platform_process_events (PL); /* check that we still read the same file. */ - switch (nmtst_get_rand_int () % 2) { + switch (nmtst_get_rand_uint32 () % 2) { case 0: { gs_free char *c = NULL; @@ -2930,7 +2929,7 @@ test_sysctl_netns_switch (void) platforms[0] = platform_0 = nm_linux_platform_new (TRUE, TRUE); platforms[1] = platform_1 = _test_netns_create_platform (); platforms[2] = platform_2 = _test_netns_create_platform (); - PL = platforms[nmtst_get_rand_int () % 3]; + PL = platforms[nmtst_get_rand_uint32 () % 3]; nmtstp_netns_select_random (platforms, G_N_ELEMENTS (platforms), &netns_pop_1); @@ -2938,7 +2937,7 @@ test_sysctl_netns_switch (void) nmtstp_netns_select_random (platforms, G_N_ELEMENTS (platforms), &netns_pop_2); - s = (nmtst_get_rand_int () % 2) ? NULL : ifname_buf; + s = (nmtst_get_rand_uint32 () % 2) ? NULL : ifname_buf; dirfd = nm_platform_sysctl_open_netdir (PL, ifindex, s); @@ -3015,6 +3014,92 @@ test_sysctl_netns_switch (void) nmtstp_link_delete (PL, FALSE, ifindex, NULL, TRUE); } +static void +sysctl_set_async_cb_assert_success (GError *error, gpointer data) +{ + g_assert_no_error (error); + g_main_loop_quit (data); +} + +static void +sysctl_set_async_cb_assert_failure (GError *error, gpointer data) +{ + g_assert (error); + g_main_loop_quit (data); +} + +static void +test_sysctl_set_async (void) +{ + NMPlatform *const PL = NM_PLATFORM_GET; + const char *const IFNAME = "nm-dummy-0"; + const char *const PATH = "/proc/sys/net/ipv4/conf/nm-dummy-0/rp_filter"; + gs_free GMainLoop *loop = NULL; + gs_unref_object GCancellable *cancellable = NULL; + int ifindex; + + ifindex = nmtstp_link_dummy_add (PL, -1, IFNAME)->ifindex; + loop = g_main_loop_new (NULL, FALSE); + cancellable = g_cancellable_new (); + + nm_platform_sysctl_set_async (PL, + NMP_SYSCTL_PATHID_ABSOLUTE (PATH), + (const char *[]) { "2", NULL}, + sysctl_set_async_cb_assert_success, + loop, + cancellable); + + if (!nmtst_main_loop_run (loop, 1000)) + g_assert_not_reached (); + + g_assert_cmpint (nm_platform_sysctl_get_int32 (PL, NMP_SYSCTL_PATHID_ABSOLUTE (PATH), -1), + ==, + 2); + + nm_platform_sysctl_set_async (PL, + NMP_SYSCTL_PATHID_ABSOLUTE (PATH), + (const char *[]) { "2", "0", "1", "0", "1", NULL}, + sysctl_set_async_cb_assert_success, + loop, + cancellable); + + if (!nmtst_main_loop_run (loop, 2000)) + g_assert_not_reached (); + + g_assert_cmpint (nm_platform_sysctl_get_int32 (PL, NMP_SYSCTL_PATHID_ABSOLUTE (PATH), -1), + ==, + 1); + + nmtstp_link_delete (NULL, -1, ifindex, IFNAME, TRUE); +} + +static void +test_sysctl_set_async_fail (void) +{ + NMPlatform *const PL = NM_PLATFORM_GET; + const char *const IFNAME = "nm-dummy-0"; + const char *const PATH = "/proc/sys/net/ipv4/conf/nm-dummy-0/does-not-exist"; + gs_free GMainLoop *loop = NULL; + gs_unref_object GCancellable *cancellable = NULL; + int ifindex; + + ifindex = nmtstp_link_dummy_add (PL, -1, IFNAME)->ifindex; + loop = g_main_loop_new (NULL, FALSE); + cancellable = g_cancellable_new (); + + nm_platform_sysctl_set_async (PL, + NMP_SYSCTL_PATHID_ABSOLUTE (PATH), + (const char *[]) { "2", NULL}, + sysctl_set_async_cb_assert_failure, + loop, + cancellable); + + if (!nmtst_main_loop_run (loop, 1000)) + g_assert_not_reached (); + + nmtstp_link_delete (NULL, -1, ifindex, IFNAME, TRUE); +} + /*****************************************************************************/ static gpointer @@ -3238,6 +3323,8 @@ _nmtstp_setup_tests (void) g_test_add_func ("/general/sysctl/rename", test_sysctl_rename); g_test_add_func ("/general/sysctl/netns-switch", test_sysctl_netns_switch); + g_test_add_func ("/general/sysctl/set-async", test_sysctl_set_async); + g_test_add_func ("/general/sysctl/set-async-fail", test_sysctl_set_async_fail); g_test_add_func ("/link/ethtool/features/get", test_ethtool_features_get); } diff --git a/src/platform/tests/test-nmp-object.c b/src/platform/tests/test-nmp-object.c index e2019e96..08bde437 100644 --- a/src/platform/tests/test-nmp-object.c +++ b/src/platform/tests/test-nmp-object.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify @@ -267,7 +266,7 @@ test_cache_link (void) struct udev_device *udev_device_3 = g_list_nth_data (global.udev_devices, 0); NMPCacheOpsType ops_type; nm_auto_unref_dedup_multi_index NMDedupMultiIndex *multi_idx = NULL; - gboolean use_udev = nmtst_get_rand_int () % 2; + gboolean use_udev = nmtst_get_rand_uint32 () % 2; multi_idx = nm_dedup_multi_index_new (); @@ -503,7 +502,7 @@ test_cache_qdisc (void) nm_auto_nmpobj NMPObject *obj2 = nmp_object_new (NMP_OBJECT_TYPE_QDISC, (NMPlatformObject *) &pl_qdisc_2); multi_idx = nm_dedup_multi_index_new (); - cache = nmp_cache_new (multi_idx, nmtst_get_rand_int () % 2); + cache = nmp_cache_new (multi_idx, nmtst_get_rand_uint32 () % 2); g_assert (nmp_cache_lookup_obj (cache, obj1a) == NULL); diff --git a/src/platform/tests/test-general.c b/src/platform/tests/test-platform-general.c index eebc15d8..2b723810 100644 --- a/src/platform/tests/test-general.c +++ b/src/platform/tests/test-platform-general.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* nm-platform.c - Handle runtime kernel networking configuration * * This program is free software; you can redistribute it and/or modify @@ -52,6 +51,31 @@ test_link_get_all (void) /*****************************************************************************/ +static void +test_nm_platform_link_flags2str (void) +{ + int i; + + for (i = 0; i < 100; i++) { + char buf[NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN + 100]; + const char *s; + const guint flags = ((i == 0) ? ~0u : nmtst_get_rand_uint ()); + gsize l; + + s = nm_platform_link_flags2str (flags, buf, sizeof (buf)); + g_assert (s); + + l = strlen (s); + if (l > NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN) + g_error ("nm_platform_link_flags2str(%x) produced a longer output than %zu chars: \"%s\"", flags, NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN, s); + if ( flags == ~0u + && l != NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN) + g_error ("nm_platform_link_flags2str(%x) is expected to produce %zu chars, but produced %zu: \"%s\"", flags, NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN, l, s); + } +} + +/*****************************************************************************/ + NMTST_DEFINE (); int @@ -61,7 +85,7 @@ main (int argc, char **argv) g_test_add_func ("/general/init_linux_platform", test_init_linux_platform); g_test_add_func ("/general/link_get_all", test_link_get_all); + g_test_add_func ("/general/nm_platform_link_flags2str", test_nm_platform_link_flags2str); return g_test_run (); } - diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c index e6ffe1ae..44bfbc58 100644 --- a/src/platform/tests/test-route.c +++ b/src/platform/tests/test-route.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -445,7 +444,7 @@ test_ip4_route_get (void) result = nm_platform_ip_route_get (NM_PLATFORM_GET, AF_INET, &a, - nmtst_get_rand_int () % 2 ? 0 : ifindex, + nmtst_get_rand_uint32 () % 2 ? 0 : ifindex, &route); g_assert (NMTST_NM_ERR_SUCCESS (result)); @@ -607,7 +606,7 @@ test_ip6_route_get (void) result = nm_platform_ip_route_get (NM_PLATFORM_GET, AF_INET6, a, - nmtst_get_rand_int () % 2 ? 0 : ifindex, + nmtst_get_rand_uint32 () % 2 ? 0 : ifindex, &route); g_assert (NMTST_NM_ERR_SUCCESS (result)); @@ -815,9 +814,9 @@ test_ip (gconstpointer test_data) if ( order_len == 0 || ( order_len < G_N_ELEMENTS (order_idx) - && nmtst_get_rand_int () % 2)) { + && nmtst_get_rand_uint32 () % 2)) { again_find_idx: - idx = nmtst_get_rand_int () % G_N_ELEMENTS (iface_data); + idx = nmtst_get_rand_uint32 () % G_N_ELEMENTS (iface_data); for (i = 0; i < order_len; i++) { if (order_idx[i] == idx) goto again_find_idx; @@ -827,7 +826,7 @@ again_find_idx: r->ifindex = iface_data[idx].ifindex; g_assert (NMTST_NM_ERR_SUCCESS (nm_platform_ip4_route_add (platform, NMP_NLM_FLAG_APPEND, r))); } else { - i = nmtst_get_rand_int () % order_len; + i = nmtst_get_rand_uint32 () % order_len; idx = order_idx[i]; for (i++; i < order_len; i++) order_idx[i - 1] = order_idx[i]; @@ -1129,17 +1128,17 @@ _rule_create_random (NMPlatform *platform) addr_size = nm_utils_addr_family_to_size (rr->addr_family); - p = nmtst_get_rand_int (); + p = nmtst_get_rand_uint32 (); if ((p % 1000u) < 50) rr->priority = 10000 + ((~p) % 20u); - p = nmtst_get_rand_int (); + p = nmtst_get_rand_uint32 (); if ((p % 1000u) < 40) nm_sprintf_buf (rr->iifname, "t-iif-%u", (~p) % 20); else if ((p % 1000u) < 80) nm_sprintf_buf (rr->iifname, "%s", DEVICE_NAME); - p = nmtst_get_rand_int (); + p = nmtst_get_rand_uint32 (); if ((p % 1000u) < 40) nm_sprintf_buf (rr->oifname, "t-oif-%d", (~p) % 20); else if ((p % 1000u) < 80) @@ -1149,14 +1148,14 @@ _rule_create_random (NMPlatform *platform) NMIPAddr *p_addr = i ? &rr->src : &rr->dst; guint8 *p_len = i ? &rr->src_len : &rr->dst_len; - p = nmtst_get_rand_int (); + p = nmtst_get_rand_uint32 (); if ((p % 1000u) < 100) { /* if we set src_len/dst_len to zero, the src/dst is actually ignored. * * For fuzzying, still set the address. It shall have no further effect. * */ *p_len = (~p) % (addr_size * 8 + 1); - p = nmtst_get_rand_int (); + p = nmtst_get_rand_uint32 (); if ((p % 3u) == 0) { if (rr->addr_family == AF_INET) p_addr->addr4 = nmtst_inet4_from_string (nm_sprintf_buf (saddr, "192.192.5.%u", (~p) % 256u)); @@ -1167,12 +1166,12 @@ _rule_create_random (NMPlatform *platform) } } - p = nmtst_get_rand_int (); + p = nmtst_get_rand_uint32 (); if ((p % 1000u) < 50) rr->tun_id = 10000 + ((~p) % 20); again_action: - p = nmtst_get_rand_int (); + p = nmtst_get_rand_uint32 (); if ((p % 1000u) < 500) rr->action = FR_ACT_UNSPEC; else if ((p % 1000u) < 750) @@ -1180,22 +1179,22 @@ again_action: else rr->action = (~p) % 0x100u; - rr->priority = _rr_rand_choose_u32 (nmtst_get_rand_int ()); + rr->priority = _rr_rand_choose_u32 (nmtst_get_rand_uint32 ()); if ( rr->action == FR_ACT_GOTO && rr->priority == G_MAXINT32) goto again_action; - p = nmtst_get_rand_int (); + p = nmtst_get_rand_uint32 (); if ((p % 10000u) < 100) rr->goto_target = rr->priority + 1; else - rr->goto_target = _rr_rand_choose_u32 (nmtst_get_rand_int ()); + rr->goto_target = _rr_rand_choose_u32 (nmtst_get_rand_uint32 ()); if ( rr->action == FR_ACT_GOTO && rr->goto_target <= rr->priority) goto again_action; - p = nmtst_get_rand_int (); + p = nmtst_get_rand_uint32 (); if ((p % 1000u) < 25) { if (_rule_check_kernel_support (platform, FRA_L3MDEV)) { rr->l3mdev = TRUE; @@ -1205,7 +1204,7 @@ again_action: again_table: if (!rr->l3mdev) { - p = nmtst_get_rand_int (); + p = nmtst_get_rand_uint32 (); if ((p % 1000u) < 700) rr->table = RT_TABLE_UNSPEC; else if ((p % 1000u) < 850) @@ -1217,33 +1216,33 @@ again_table: goto again_table; } - rr->fwmark = _rr_rand_choose_u32 (nmtst_get_rand_int ()); - rr->fwmask = _rr_rand_choose_u32 (nmtst_get_rand_int ()); + rr->fwmark = _rr_rand_choose_u32 (nmtst_get_rand_uint32 ()); + rr->fwmask = _rr_rand_choose_u32 (nmtst_get_rand_uint32 ()); - rr->flow = _rr_rand_choose_u32 (nmtst_get_rand_int ()); + rr->flow = _rr_rand_choose_u32 (nmtst_get_rand_uint32 ()); if (_rule_check_kernel_support (platform, FRA_PROTOCOL)) - rr->protocol = _rr_rand_choose_u8 (nmtst_get_rand_int ()); + rr->protocol = _rr_rand_choose_u8 (nmtst_get_rand_uint32 ()); #define IPTOS_TOS_MASK 0x1E again_tos: - rr->tos = _rr_rand_choose_u8 (nmtst_get_rand_int ()); + rr->tos = _rr_rand_choose_u8 (nmtst_get_rand_uint32 ()); if ( rr->addr_family == AF_INET && rr->tos & ~IPTOS_TOS_MASK) goto again_tos; if (_rule_check_kernel_support (platform, FRA_IP_PROTO)) - rr->ip_proto = _rr_rand_choose_u8 (nmtst_get_rand_int ()); + rr->ip_proto = _rr_rand_choose_u8 (nmtst_get_rand_uint32 ()); if (_rule_check_kernel_support (platform, FRA_SUPPRESS_PREFIXLEN)) - rr->suppress_prefixlen_inverse = ~_rr_rand_choose_u32 (nmtst_get_rand_int ()); + rr->suppress_prefixlen_inverse = ~_rr_rand_choose_u32 (nmtst_get_rand_uint32 ()); if (_rule_check_kernel_support (platform, FRA_SUPPRESS_IFGROUP)) - rr->suppress_ifgroup_inverse = ~_rr_rand_choose_u32 (nmtst_get_rand_int ()); + rr->suppress_ifgroup_inverse = ~_rr_rand_choose_u32 (nmtst_get_rand_uint32 ()); if (_rule_check_kernel_support (platform, FRA_UID_RANGE)) { - p = nmtst_get_rand_int (); + p = nmtst_get_rand_uint32 (); rr->uid_range_has = (p % 10000u) < 200; } @@ -1265,10 +1264,10 @@ again_uid_range: if (!_rule_check_kernel_support (platform, attribute)) continue; - p = nmtst_get_rand_int (); + p = nmtst_get_rand_uint32 (); if ((p % 10000u) < 300) { while (range->start == 0) { - p = p ^ nmtst_get_rand_int (); + p = p ^ nmtst_get_rand_uint32 (); range->start = nmtst_rand_select (1u, 0xFFFEu, ((p ) % 0xFFFEu) + 1); range->end = nmtst_rand_select (1u, 0xFFFEu, ((p >> 16) % 0xFFFEu) + 1, range->start); if (range->end < range->start) @@ -1277,7 +1276,7 @@ again_uid_range: } } - p = nmtst_get_rand_int () % 1000u; + p = nmtst_get_rand_uint32 () % 1000u; if (p < 100) rr->flags |= FIB_RULE_INVERT; @@ -1345,8 +1344,7 @@ _rule_fuzzy_equal (const NMPObject *obj, rr_co.tos = 0; if (rr->ip_proto == 0) rr_co.ip_proto = 0; - if (rr->suppress_prefixlen_inverse == 0) - rr_co.suppress_prefixlen_inverse = 0; + rr_co.suppress_prefixlen_inverse = rr->suppress_prefixlen_inverse; if (rr->suppress_ifgroup_inverse == 0) rr_co.suppress_ifgroup_inverse = 0; if (!rr->uid_range_has) @@ -1483,10 +1481,10 @@ test_rule (gconstpointer test_data) if (TEST_IDX != 1) { nmtst_rand_perm (NULL, objs->pdata, NULL, sizeof (gpointer), objs->len); - g_ptr_array_set_size (objs, nmtst_get_rand_int () % (objs->len + 1)); + g_ptr_array_set_size (objs, nmtst_get_rand_uint32 () % (objs->len + 1)); } - n = (TEST_IDX != 1) ? nmtst_get_rand_int () % 50u : 0u; + n = (TEST_IDX != 1) ? nmtst_get_rand_uint32 () % 50u : 0u; for (i = 0; i < n; i++) { nm_auto_nmpobj const NMPObject *o = NULL; guint try = 0; @@ -1536,16 +1534,18 @@ again: nmp_rules_manager_track (rules_manager, NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]), 1, - USER_TAG_1); + USER_TAG_1, + NULL); if (nmtst_get_rand_bool ()) { /* this has no effect, because a negative priority (of same absolute value) * has lower priority than the positive priority above. */ nmp_rules_manager_track (rules_manager, NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]), -1, - USER_TAG_2); + USER_TAG_2, + NULL); } - if (nmtst_get_rand_int () % objs_sync->len == 0) { + if (nmtst_get_rand_uint32 () % objs_sync->len == 0) { nmp_rules_manager_sync (rules_manager, FALSE); g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, i + 1); } @@ -1555,7 +1555,7 @@ again: g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, objs_sync->len); for (i = 0; i < objs_sync->len; i++) { - switch (nmtst_get_rand_int () % 3) { + switch (nmtst_get_rand_uint32 () % 3) { case 0: nmp_rules_manager_untrack (rules_manager, NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]), @@ -1568,16 +1568,18 @@ again: nmp_rules_manager_track (rules_manager, NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]), -1, - USER_TAG_1); + USER_TAG_1, + NULL); break; case 2: nmp_rules_manager_track (rules_manager, NMP_OBJECT_CAST_ROUTING_RULE (objs_sync->pdata[i]), -2, - USER_TAG_2); + USER_TAG_2, + NULL); break; } - if (nmtst_get_rand_int () % objs_sync->len == 0) { + if (nmtst_get_rand_uint32 () % objs_sync->len == 0) { nmp_rules_manager_sync (rules_manager, FALSE); g_assert_cmpint (nmtstp_platform_routing_rules_get_count (platform, AF_UNSPEC), ==, objs_sync->len - i - 1); } diff --git a/src/platform/wifi/nm-wifi-utils-nl80211.c b/src/platform/wifi/nm-wifi-utils-nl80211.c index 4f7ede97..164dada4 100644 --- a/src/platform/wifi/nm-wifi-utils-nl80211.c +++ b/src/platform/wifi/nm-wifi-utils-nl80211.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -103,7 +102,7 @@ _nl80211_alloc_msg (int id, int ifindex, int phy, guint32 cmd, guint32 flags) return g_steal_pointer (&msg); nla_put_failure: - return NULL; + g_return_val_if_reached (NULL); } static struct nl_msg * @@ -200,6 +199,9 @@ nl80211_iface_info_handler (struct nl_msg *msg, void *arg) case NL80211_IFTYPE_STATION: info->mode = NM_802_11_MODE_INFRA; break; + case NL80211_IFTYPE_MESH_POINT: + info->mode = NM_802_11_MODE_MESH; + break; } return NL_SKIP; @@ -242,6 +244,9 @@ wifi_nl80211_set_mode (NMWifiUtils *data, const NM80211Mode mode) case NM_802_11_MODE_AP: NLA_PUT_U32 (msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_AP); break; + case NM_802_11_MODE_MESH: + NLA_PUT_U32 (msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_MESH_POINT); + break; default: g_assert_not_reached (); } @@ -250,7 +255,7 @@ wifi_nl80211_set_mode (NMWifiUtils *data, const NM80211Mode mode) return err >= 0; nla_put_failure: - return FALSE; + g_return_val_if_reached (FALSE); } static gboolean @@ -267,7 +272,7 @@ wifi_nl80211_set_powersave (NMWifiUtils *data, guint32 powersave) return err >= 0; nla_put_failure: - return FALSE; + g_return_val_if_reached (FALSE); } static int @@ -365,7 +370,7 @@ wifi_nl80211_set_wake_on_wlan (NMWifiUtils *data, NMSettingWirelessWakeOnWLan wo return err >= 0; nla_put_failure: - return FALSE; + g_return_val_if_reached (FALSE); } /* @divisor: pass what value @xbm should be divided by to get dBm */ @@ -642,7 +647,7 @@ nl80211_get_ap_info (NMWifiUtilsNl80211 *self, return; nla_put_failure: - return; + g_return_if_reached (); } static guint32 @@ -695,7 +700,7 @@ wifi_nl80211_indicate_addressing_running (NMWifiUtils *data, gboolean running) return err >= 0; nla_put_failure: - return FALSE; + g_return_val_if_reached (FALSE); } struct nl80211_device_info { @@ -893,10 +898,11 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg) int i; nla_for_each_nested (nl_mode, tb[NL80211_ATTR_SUPPORTED_IFTYPES], i) { - if (nla_type (nl_mode) == NL80211_IFTYPE_AP) - info->caps |= NM_WIFI_DEVICE_CAP_AP; - else if (nla_type (nl_mode) == NL80211_IFTYPE_ADHOC) - info->caps |= NM_WIFI_DEVICE_CAP_ADHOC; + switch (nla_type (nl_mode)) { + case NL80211_IFTYPE_AP: info->caps |= NM_WIFI_DEVICE_CAP_AP; break; + case NL80211_IFTYPE_ADHOC: info->caps |= NM_WIFI_DEVICE_CAP_ADHOC; break; + case NL80211_IFTYPE_MESH_POINT: info->caps |= NM_WIFI_DEVICE_CAP_MESH; break; + } } } diff --git a/src/platform/wifi/nm-wifi-utils-nl80211.h b/src/platform/wifi/nm-wifi-utils-nl80211.h index 27f67697..a4c9ca55 100644 --- a/src/platform/wifi/nm-wifi-utils-nl80211.h +++ b/src/platform/wifi/nm-wifi-utils-nl80211.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/platform/wifi/nm-wifi-utils-private.h b/src/platform/wifi/nm-wifi-utils-private.h index bcbc1c51..4cae22e5 100644 --- a/src/platform/wifi/nm-wifi-utils-private.h +++ b/src/platform/wifi/nm-wifi-utils-private.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/platform/wifi/nm-wifi-utils-wext.c b/src/platform/wifi/nm-wifi-utils-wext.c index 3aa1720a..d19cf8a5 100644 --- a/src/platform/wifi/nm-wifi-utils-wext.c +++ b/src/platform/wifi/nm-wifi-utils-wext.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/platform/wifi/nm-wifi-utils-wext.h b/src/platform/wifi/nm-wifi-utils-wext.h index 44b11afb..70cae966 100644 --- a/src/platform/wifi/nm-wifi-utils-wext.h +++ b/src/platform/wifi/nm-wifi-utils-wext.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/platform/wifi/nm-wifi-utils.c b/src/platform/wifi/nm-wifi-utils.c index 96071faa..1e42db6a 100644 --- a/src/platform/wifi/nm-wifi-utils.c +++ b/src/platform/wifi/nm-wifi-utils.c @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify @@ -90,7 +89,8 @@ nm_wifi_utils_set_mode (NMWifiUtils *data, const NM80211Mode mode) g_return_val_if_fail (data != NULL, FALSE); g_return_val_if_fail ( (mode == NM_802_11_MODE_INFRA) || (mode == NM_802_11_MODE_AP) - || (mode == NM_802_11_MODE_ADHOC), FALSE); + || (mode == NM_802_11_MODE_ADHOC) + || (mode == NM_802_11_MODE_MESH), FALSE); klass = NM_WIFI_UTILS_GET_CLASS (data); diff --git a/src/platform/wifi/nm-wifi-utils.h b/src/platform/wifi/nm-wifi-utils.h index 36148b5a..2e89053b 100644 --- a/src/platform/wifi/nm-wifi-utils.h +++ b/src/platform/wifi/nm-wifi-utils.h @@ -1,4 +1,3 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ /* NetworkManager -- Network link manager * * This program is free software; you can redistribute it and/or modify diff --git a/src/platform/wpan/nm-wpan-utils.c b/src/platform/wpan/nm-wpan-utils.c index b7a51e9b..0afc2a4d 100644 --- a/src/platform/wpan/nm-wpan-utils.c +++ b/src/platform/wpan/nm-wpan-utils.c @@ -92,7 +92,7 @@ _nl802154_alloc_msg (int id, int ifindex, guint32 cmd, guint32 flags) return g_steal_pointer (&msg); nla_put_failure: - return NULL; + g_return_val_if_reached (NULL); } static struct nl_msg * @@ -217,7 +217,7 @@ nm_wpan_utils_set_pan_id (NMWpanUtils *self, guint16 pan_id) return err >= 0; nla_put_failure: - return FALSE; + g_return_val_if_reached (FALSE); } guint16 @@ -244,7 +244,7 @@ nm_wpan_utils_set_short_addr (NMWpanUtils *self, guint16 short_addr) return err >= 0; nla_put_failure: - return FALSE; + g_return_val_if_reached (FALSE); } gboolean @@ -262,7 +262,7 @@ nm_wpan_utils_set_channel (NMWpanUtils *self, guint8 page, guint8 channel) return err >= 0; nla_put_failure: - return FALSE; + g_return_val_if_reached (FALSE); } /*****************************************************************************/ |