
For IPv6 routes, a metric of zero is identical to a metric of 1024. Unless we do NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL, compare them as equal.
5378 lines
164 KiB
C
5378 lines
164 KiB
C
/* -*- 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
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Copyright (C) 2012 Red Hat, Inc.
|
|
*/
|
|
|
|
#include "nm-default.h"
|
|
|
|
#include "nm-platform.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <string.h>
|
|
#include <linux/ip.h>
|
|
#include <linux/if_tun.h>
|
|
#include <linux/if_tunnel.h>
|
|
#include <linux/rtnetlink.h>
|
|
#include <libudev.h>
|
|
|
|
#include "nm-utils.h"
|
|
#include "nm-core-internal.h"
|
|
#include "nm-utils/nm-dedup-multi.h"
|
|
#include "nm-utils/nm-udev-utils.h"
|
|
|
|
#include "nm-core-utils.h"
|
|
#include "nm-platform-utils.h"
|
|
#include "nm-platform-private.h"
|
|
#include "nmp-object.h"
|
|
#include "nmp-netns.h"
|
|
|
|
/*****************************************************************************/
|
|
|
|
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));
|
|
|
|
#define _NMLOG_DOMAIN LOGD_PLATFORM
|
|
#define _NMLOG_PREFIX_NAME "platform"
|
|
#define _NMLOG(level, ...) \
|
|
G_STMT_START { \
|
|
const NMLogLevel __level = (level); \
|
|
\
|
|
if (nm_logging_enabled (__level, _NMLOG_DOMAIN)) { \
|
|
char __prefix[32]; \
|
|
const char *__p_prefix = _NMLOG_PREFIX_NAME; \
|
|
const NMPlatform *const __self = (self); \
|
|
\
|
|
if (__self && NM_PLATFORM_GET_PRIVATE (__self)->log_with_ptr) { \
|
|
g_snprintf (__prefix, sizeof (__prefix), "%s[%p]", _NMLOG_PREFIX_NAME, __self); \
|
|
__p_prefix = __prefix; \
|
|
} \
|
|
_nm_log (__level, _NMLOG_DOMAIN, 0, NULL, NULL, \
|
|
"%s: " _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \
|
|
__p_prefix _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
|
|
} \
|
|
} G_STMT_END
|
|
|
|
#define LOG_FMT_IP_TUNNEL "adding %s '%s' parent %u local %s remote %s"
|
|
|
|
/*****************************************************************************/
|
|
|
|
static guint signals[_NM_PLATFORM_SIGNAL_ID_LAST] = { 0 };
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_NETNS_SUPPORT,
|
|
PROP_USE_UDEV,
|
|
PROP_LOG_WITH_PTR,
|
|
LAST_PROP,
|
|
};
|
|
|
|
typedef struct _NMPlatformPrivate {
|
|
bool use_udev:1;
|
|
bool log_with_ptr:1;
|
|
NMDedupMultiIndex *multi_idx;
|
|
NMPCache *cache;
|
|
} NMPlatformPrivate;
|
|
|
|
G_DEFINE_TYPE (NMPlatform, nm_platform, G_TYPE_OBJECT)
|
|
|
|
#define NM_PLATFORM_GET_PRIVATE(self) _NM_GET_PRIVATE_PTR (self, NMPlatform, NM_IS_PLATFORM)
|
|
|
|
/*****************************************************************************/
|
|
|
|
gboolean
|
|
nm_platform_get_use_udev (NMPlatform *self)
|
|
{
|
|
return NM_PLATFORM_GET_PRIVATE (self)->use_udev;
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_get_log_with_ptr (NMPlatform *self)
|
|
{
|
|
return NM_PLATFORM_GET_PRIVATE (self)->log_with_ptr;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
guint
|
|
_nm_platform_signal_id_get (NMPlatformSignalIdType signal_type)
|
|
{
|
|
nm_assert ( signal_type > 0
|
|
&& signal_type != NM_PLATFORM_SIGNAL_ID_NONE
|
|
&& signal_type < _NM_PLATFORM_SIGNAL_ID_LAST);
|
|
|
|
return signals[signal_type];
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* Singleton NMPlatform subclass instance and cached class object */
|
|
NM_DEFINE_SINGLETON_INSTANCE (NMPlatform);
|
|
|
|
NM_DEFINE_SINGLETON_REGISTER (NMPlatform);
|
|
|
|
/* Just always initialize a @klass instance. NM_PLATFORM_GET_CLASS()
|
|
* is only a plain read on the self instance, which the compiler
|
|
* like can optimize out.
|
|
*/
|
|
#define _CHECK_SELF_VOID(self, klass) \
|
|
NMPlatformClass *klass; \
|
|
do { \
|
|
g_return_if_fail (NM_IS_PLATFORM (self)); \
|
|
klass = NM_PLATFORM_GET_CLASS (self); \
|
|
(void) klass; \
|
|
} while (0)
|
|
|
|
#define _CHECK_SELF(self, klass, err_val) \
|
|
NMPlatformClass *klass; \
|
|
do { \
|
|
g_return_val_if_fail (NM_IS_PLATFORM (self), err_val); \
|
|
klass = NM_PLATFORM_GET_CLASS (self); \
|
|
(void) klass; \
|
|
} while (0)
|
|
|
|
#define _CHECK_SELF_NETNS(self, klass, netns, err_val) \
|
|
nm_auto_pop_netns NMPNetns *netns = NULL; \
|
|
NMPlatformClass *klass; \
|
|
do { \
|
|
g_return_val_if_fail (NM_IS_PLATFORM (self), err_val); \
|
|
klass = NM_PLATFORM_GET_CLASS (self); \
|
|
(void) klass; \
|
|
if (!nm_platform_netns_push (self, &netns)) \
|
|
return (err_val); \
|
|
} while (0)
|
|
|
|
/**
|
|
* nm_platform_setup:
|
|
* @instance: the #NMPlatform instance
|
|
*
|
|
* Failing to set up #NMPlatform singleton results in a fatal error,
|
|
* as well as trying to initialize it multiple times without freeing
|
|
* it.
|
|
*
|
|
* NetworkManager will typically use only one platform object during
|
|
* its run. Test programs might want to switch platform implementations,
|
|
* though.
|
|
*/
|
|
void
|
|
nm_platform_setup (NMPlatform *instance)
|
|
{
|
|
g_return_if_fail (NM_IS_PLATFORM (instance));
|
|
g_return_if_fail (!singleton_instance);
|
|
|
|
singleton_instance = instance;
|
|
|
|
nm_singleton_instance_register ();
|
|
|
|
nm_log_dbg (LOGD_CORE, "setup %s singleton (%p, %s)", "NMPlatform", singleton_instance, G_OBJECT_TYPE_NAME (instance));
|
|
}
|
|
|
|
/**
|
|
* nm_platform_get:
|
|
* @self: platform instance
|
|
*
|
|
* Retrieve #NMPlatform singleton. Use this whenever you want to connect to
|
|
* #NMPlatform signals. It is an error to call it before nm_platform_setup().
|
|
*
|
|
* Returns: (transfer none): The #NMPlatform singleton reference.
|
|
*/
|
|
NMPlatform *
|
|
nm_platform_get ()
|
|
{
|
|
g_assert (singleton_instance);
|
|
|
|
return singleton_instance;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
NMDedupMultiIndex *
|
|
nm_platform_get_multi_idx (NMPlatform *self)
|
|
{
|
|
g_return_val_if_fail (NM_IS_PLATFORM (self), NULL);
|
|
|
|
return NM_PLATFORM_GET_PRIVATE (self)->multi_idx;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
* _nm_platform_error_to_string:
|
|
* @error_code: the error code to stringify.
|
|
*
|
|
* Returns: A string representation of the error.
|
|
* For negative numbers, this function interprets
|
|
* the code as -errno.
|
|
* For invalid (positive) numbers it returns NULL.
|
|
*/
|
|
NM_UTILS_LOOKUP_STR_DEFINE (_nm_platform_error_to_string, NMPlatformError,
|
|
NM_UTILS_LOOKUP_DEFAULT ( val < 0 ? g_strerror (- ((int) val)) : NULL ),
|
|
NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_SUCCESS, "success"),
|
|
NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_BUG, "bug"),
|
|
NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_UNSPECIFIED, "unspecified"),
|
|
NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_NOT_FOUND, "not-found"),
|
|
NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_EXISTS, "exists"),
|
|
NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_WRONG_TYPE, "wrong-type"),
|
|
NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_NOT_SLAVE, "not-slave"),
|
|
NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_NO_FIRMWARE, "no-firmware"),
|
|
NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_OPNOTSUPP, "not-supported"),
|
|
NM_UTILS_LOOKUP_ITEM_IGNORE (_NM_PLATFORM_ERROR_MININT),
|
|
);
|
|
|
|
/*****************************************************************************/
|
|
|
|
gboolean
|
|
nm_platform_check_support_kernel_extended_ifa_flags (NMPlatform *self)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
if (!klass->check_support_kernel_extended_ifa_flags)
|
|
return FALSE;
|
|
|
|
return klass->check_support_kernel_extended_ifa_flags (self);
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_check_support_user_ipv6ll (NMPlatform *self)
|
|
{
|
|
static int supported = -1;
|
|
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
if (!klass->check_support_user_ipv6ll)
|
|
return FALSE;
|
|
|
|
if (supported < 0)
|
|
supported = klass->check_support_user_ipv6ll (self) ? 1 : 0;
|
|
return !!supported;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_process_events:
|
|
* @self: platform instance
|
|
*
|
|
* Process pending events or handle pending delayed-actions.
|
|
* Effectively, this reads the netlink socket and processes
|
|
* new netlink messages. Possibly it will raise change signals.
|
|
*/
|
|
void
|
|
nm_platform_process_events (NMPlatform *self)
|
|
{
|
|
_CHECK_SELF_VOID (self, klass);
|
|
|
|
if (klass->process_events)
|
|
klass->process_events (self);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
* nm_platform_sysctl_open_netdir:
|
|
* @self: platform instance
|
|
* @ifindex: the ifindex for which to open /sys/class/net/%s
|
|
* @out_ifname: optional output argument of the found ifname.
|
|
*
|
|
* Wraps nmp_utils_sysctl_open_netdir() by first changing into the right
|
|
* network-namespace.
|
|
*
|
|
* Returns: on success, the open file descriptor to the /sys/class/net/%s
|
|
* directory.
|
|
*/
|
|
int
|
|
nm_platform_sysctl_open_netdir (NMPlatform *self, int ifindex, char *out_ifname)
|
|
{
|
|
const char*ifname_guess;
|
|
_CHECK_SELF_NETNS (self, klass, netns, -1);
|
|
|
|
g_return_val_if_fail (ifindex > 0, -1);
|
|
|
|
/* we don't have an @ifname_guess argument to make the API nicer.
|
|
* But still do a cache-lookup first. Chances are good that we have
|
|
* the right ifname cached and save if_indextoname() */
|
|
ifname_guess = nm_platform_link_get_name (self, ifindex);
|
|
|
|
return nmp_utils_sysctl_open_netdir (ifindex, ifname_guess, out_ifname);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_sysctl_set:
|
|
* @self: platform instance
|
|
* @pathid: if @dirfd is present, this must be the full path that is looked up.
|
|
* It is required for logging.
|
|
* @dirfd: optional file descriptor for parent directory for openat()
|
|
* @path: Absolute option path
|
|
* @value: Value to write
|
|
*
|
|
* 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.
|
|
*
|
|
* Returns: %TRUE on success.
|
|
*/
|
|
gboolean
|
|
nm_platform_sysctl_set (NMPlatform *self, const char *pathid, int dirfd, const char *path, const char *value)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (path, FALSE);
|
|
g_return_val_if_fail (value, FALSE);
|
|
|
|
return klass->sysctl_set (self, pathid, dirfd, path, value);
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_sysctl_set_ip6_hop_limit_safe (NMPlatform *self, const char *iface, int value)
|
|
{
|
|
const char *path;
|
|
gint64 cur;
|
|
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
/* the hop-limit provided via RA is uint8. */
|
|
if (value > 0xFF)
|
|
return FALSE;
|
|
|
|
/* don't allow unreasonable small values */
|
|
if (value < 10)
|
|
return FALSE;
|
|
|
|
path = nm_utils_ip6_property_path (iface, "hop_limit");
|
|
cur = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_ABSOLUTE (path), 10, 1, G_MAXINT32, -1);
|
|
|
|
/* only allow increasing the hop-limit to avoid DOS by an attacker
|
|
* setting a low hop-limit (CVE-2015-2924, rh#1209902) */
|
|
|
|
if (value < cur)
|
|
return FALSE;
|
|
if (value != cur) {
|
|
char svalue[20];
|
|
|
|
sprintf (svalue, "%d", value);
|
|
nm_platform_sysctl_set (self, NMP_SYSCTL_PATHID_ABSOLUTE (path), svalue);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_sysctl_get:
|
|
* @self: platform instance
|
|
* @dirfd: if non-negative, used to lookup the path via openat().
|
|
* @pathid: if @dirfd is present, this must be the full path that is looked up.
|
|
* It is required for logging.
|
|
* @path: Absolute path to sysctl
|
|
*
|
|
* Returns: (transfer full): Contents of the virtual sysctl file.
|
|
*/
|
|
char *
|
|
nm_platform_sysctl_get (NMPlatform *self, const char *pathid, int dirfd, const char *path)
|
|
{
|
|
_CHECK_SELF (self, klass, NULL);
|
|
|
|
g_return_val_if_fail (path, NULL);
|
|
|
|
return klass->sysctl_get (self, pathid, dirfd, path);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_sysctl_get_int32:
|
|
* @self: platform instance
|
|
* @pathid: if @dirfd is present, this must be the full path that is looked up.
|
|
* It is required for logging.
|
|
* @dirfd: if non-negative, used to lookup the path via openat().
|
|
* @path: Absolute path to sysctl
|
|
* @fallback: default value, if the content of path could not be read
|
|
* as decimal integer.
|
|
*
|
|
* Returns: contents of the sysctl file parsed as s32 integer, or
|
|
* @fallback on error. On error, %errno will be set to a non-zero
|
|
* value, on success %errno will be set to zero.
|
|
*/
|
|
gint32
|
|
nm_platform_sysctl_get_int32 (NMPlatform *self, const char *pathid, int dirfd, const char *path, gint32 fallback)
|
|
{
|
|
return nm_platform_sysctl_get_int_checked (self, pathid, dirfd, path, 10, G_MININT32, G_MAXINT32, fallback);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_sysctl_get_int_checked:
|
|
* @self: platform instance
|
|
* @pathid: if @dirfd is present, this must be the full path that is looked up.
|
|
* It is required for logging.
|
|
* @dirfd: if non-negative, used to lookup the path via openat().
|
|
* @path: Absolute path to sysctl
|
|
* @base: base of numeric conversion
|
|
* @min: minimal value that is still valid
|
|
* @max: maximal value that is still valid
|
|
* @fallback: default value, if the content of path could not be read
|
|
* as valid integer.
|
|
*
|
|
* Returns: contents of the sysctl file parsed as s64 integer, or
|
|
* @fallback on error. On error, %errno will be set to a non-zero
|
|
* value. On success, %errno will be set to zero. The returned value
|
|
* will always be in the range between @min and @max
|
|
* (inclusive) or @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 *value = NULL;
|
|
gint32 ret;
|
|
|
|
_CHECK_SELF (self, klass, fallback);
|
|
|
|
g_return_val_if_fail (path, fallback);
|
|
|
|
if (path)
|
|
value = nm_platform_sysctl_get (self, pathid, dirfd, path);
|
|
|
|
if (!value) {
|
|
errno = EINVAL;
|
|
return fallback;
|
|
}
|
|
|
|
ret = _nm_utils_ascii_str_to_int64 (value, base, min, max, fallback);
|
|
g_free (value);
|
|
return ret;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static int
|
|
_link_get_all_presort (gconstpointer p_a,
|
|
gconstpointer p_b,
|
|
gpointer sort_by_name)
|
|
{
|
|
const NMPlatformLink *a = NMP_OBJECT_CAST_LINK (*((const NMPObject **) p_a));
|
|
const NMPlatformLink *b = NMP_OBJECT_CAST_LINK (*((const NMPObject **) p_b));
|
|
|
|
/* Loopback always first */
|
|
if (a->ifindex == 1)
|
|
return -1;
|
|
if (b->ifindex == 1)
|
|
return 1;
|
|
|
|
if (GPOINTER_TO_INT (sort_by_name)) {
|
|
/* Initialized links first */
|
|
if (a->initialized > b->initialized)
|
|
return -1;
|
|
if (a->initialized < b->initialized)
|
|
return 1;
|
|
|
|
return strcmp (a->name, b->name);
|
|
} else
|
|
return a->ifindex - b->ifindex;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_get_all:
|
|
* @self: platform instance
|
|
* @sort_by_name: whether to sort by name or ifindex.
|
|
*
|
|
* Retrieve a snapshot of configuration for all links at once. The result is
|
|
* owned by the caller and should be freed with g_ptr_array_unref().
|
|
*/
|
|
GPtrArray *
|
|
nm_platform_link_get_all (NMPlatform *self, gboolean sort_by_name)
|
|
{
|
|
gs_unref_ptrarray GPtrArray *links = NULL;
|
|
GPtrArray *result;
|
|
guint i, nresult;
|
|
gs_unref_hashtable GHashTable *unseen = NULL;
|
|
const NMPlatformLink *item;
|
|
NMPLookup lookup;
|
|
|
|
_CHECK_SELF (self, klass, NULL);
|
|
|
|
nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_LINK);
|
|
links = nm_dedup_multi_objs_to_ptr_array_head (nm_platform_lookup (self, &lookup),
|
|
NULL, NULL);
|
|
if (!links)
|
|
return NULL;
|
|
|
|
for (i = 0; i < links->len; ) {
|
|
if (!nmp_object_is_visible (links->pdata[i]))
|
|
g_ptr_array_remove_index_fast (links, i);
|
|
else
|
|
i++;
|
|
}
|
|
|
|
if (links->len == 0)
|
|
return NULL;
|
|
|
|
/* first sort the links by their ifindex or name. Below we will sort
|
|
* further by moving children/slaves to the end. */
|
|
g_ptr_array_sort_with_data (links, _link_get_all_presort, GINT_TO_POINTER (sort_by_name));
|
|
|
|
unseen = g_hash_table_new (g_direct_hash, g_direct_equal);
|
|
for (i = 0; i < links->len; i++) {
|
|
item = NMP_OBJECT_CAST_LINK (links->pdata[i]);
|
|
nm_assert (item->ifindex > 0);
|
|
if (!nm_g_hash_table_insert (unseen, GINT_TO_POINTER (item->ifindex), NULL))
|
|
nm_assert_not_reached ();
|
|
}
|
|
|
|
#if NM_MORE_ASSERTS
|
|
/* Ensure that link_get_all returns a consistent and valid result. */
|
|
for (i = 0; i < links->len; i++) {
|
|
item = NMP_OBJECT_CAST_LINK (links->pdata[i]);
|
|
|
|
if (!item->ifindex)
|
|
continue;
|
|
if (item->master != 0) {
|
|
g_warn_if_fail (item->master > 0);
|
|
g_warn_if_fail (item->master != item->ifindex);
|
|
g_warn_if_fail (g_hash_table_contains (unseen, GINT_TO_POINTER (item->master)));
|
|
}
|
|
if (item->parent != 0) {
|
|
if (item->parent != NM_PLATFORM_LINK_OTHER_NETNS) {
|
|
g_warn_if_fail (item->parent > 0);
|
|
g_warn_if_fail (item->parent != item->ifindex);
|
|
g_warn_if_fail (g_hash_table_contains (unseen, GINT_TO_POINTER (item->parent)));
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Re-order the links list such that children/slaves come after all ancestors */
|
|
nm_assert (g_hash_table_size (unseen) == links->len);
|
|
nresult = links->len;
|
|
result = g_ptr_array_new_full (nresult, (GDestroyNotify) nmp_object_unref);
|
|
|
|
while (TRUE) {
|
|
gboolean found_something = FALSE;
|
|
guint first_idx = G_MAXUINT;
|
|
|
|
for (i = 0; i < links->len; i++) {
|
|
item = NMP_OBJECT_CAST_LINK (links->pdata[i]);
|
|
|
|
if (!item)
|
|
continue;
|
|
|
|
g_assert (g_hash_table_contains (unseen, GINT_TO_POINTER (item->ifindex)));
|
|
|
|
if (item->master > 0 && g_hash_table_contains (unseen, GINT_TO_POINTER (item->master)))
|
|
goto skip;
|
|
if (item->parent > 0 && g_hash_table_contains (unseen, GINT_TO_POINTER (item->parent)))
|
|
goto skip;
|
|
|
|
g_hash_table_remove (unseen, GINT_TO_POINTER (item->ifindex));
|
|
g_ptr_array_add (result, links->pdata[i]);
|
|
links->pdata[i] = NULL;
|
|
found_something = TRUE;
|
|
continue;
|
|
skip:
|
|
if (first_idx == G_MAXUINT)
|
|
first_idx = i;
|
|
}
|
|
|
|
if (found_something) {
|
|
if (first_idx == G_MAXUINT)
|
|
break;
|
|
} else {
|
|
nm_assert (first_idx != G_MAXUINT);
|
|
/* There is a loop, pop the first (remaining) element from the list.
|
|
* This can happen for veth pairs where each peer is parent of the other end. */
|
|
item = NMP_OBJECT_CAST_LINK (links->pdata[first_idx]);
|
|
g_hash_table_remove (unseen, GINT_TO_POINTER (item->ifindex));
|
|
g_ptr_array_add (result, links->pdata[first_idx]);
|
|
links->pdata[first_idx] = NULL;
|
|
}
|
|
nm_assert (result->len < nresult);
|
|
}
|
|
nm_assert (result->len == nresult);
|
|
|
|
return result;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
const NMPObject *
|
|
nm_platform_link_get_obj (NMPlatform *self,
|
|
int ifindex,
|
|
gboolean visible_only)
|
|
{
|
|
const NMPObject *obj_cache;
|
|
|
|
obj_cache = nmp_cache_lookup_link (nm_platform_get_cache (self), ifindex);
|
|
if ( !obj_cache
|
|
|| ( visible_only
|
|
&& !nmp_object_is_visible (obj_cache)))
|
|
return NULL;
|
|
return obj_cache;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
* nm_platform_link_get:
|
|
* @self: platform instance
|
|
* @ifindex: ifindex of the link
|
|
*
|
|
* Lookup the internal NMPlatformLink object.
|
|
*
|
|
* Returns: %NULL, if such a link exists or the internal
|
|
* platform link object. Do not modify the returned value.
|
|
* Also, be aware that any subsequent platform call might
|
|
* invalidate/modify the returned instance.
|
|
**/
|
|
const NMPlatformLink *
|
|
nm_platform_link_get (NMPlatform *self, int ifindex)
|
|
{
|
|
const NMPObject *obj;
|
|
|
|
_CHECK_SELF (self, klass, NULL);
|
|
|
|
if (ifindex <= 0)
|
|
return NULL;
|
|
|
|
obj = nm_platform_link_get_obj (self, ifindex, TRUE);
|
|
return NMP_OBJECT_CAST_LINK (obj);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_get_by_ifname:
|
|
* @self: platform instance
|
|
* @ifname: the ifname
|
|
*
|
|
* Returns: the first #NMPlatformLink instance with the given name.
|
|
**/
|
|
const NMPlatformLink *
|
|
nm_platform_link_get_by_ifname (NMPlatform *self, const char *ifname)
|
|
{
|
|
const NMPObject *obj;
|
|
|
|
_CHECK_SELF (self, klass, NULL);
|
|
|
|
if (!ifname || !*ifname)
|
|
return NULL;
|
|
|
|
obj = nmp_cache_lookup_link_full (nm_platform_get_cache (self),
|
|
0, ifname, TRUE, NM_LINK_TYPE_NONE, NULL, NULL);
|
|
return NMP_OBJECT_CAST_LINK (obj);
|
|
}
|
|
|
|
struct _nm_platform_link_get_by_address_data {
|
|
gconstpointer address;
|
|
guint8 length;
|
|
};
|
|
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_get_by_address:
|
|
* @self: platform instance
|
|
* @address: a pointer to the binary hardware address
|
|
* @length: the size of @address in bytes
|
|
*
|
|
* Returns: the first #NMPlatformLink object with a matching
|
|
* address.
|
|
**/
|
|
const NMPlatformLink *
|
|
nm_platform_link_get_by_address (NMPlatform *self,
|
|
gconstpointer address,
|
|
size_t length)
|
|
{
|
|
const NMPObject *obj;
|
|
struct _nm_platform_link_get_by_address_data d = {
|
|
.address = address,
|
|
.length = length,
|
|
};
|
|
|
|
_CHECK_SELF (self, klass, NULL);
|
|
|
|
if (length == 0)
|
|
return NULL;
|
|
|
|
if (length > NM_UTILS_HWADDR_LEN_MAX)
|
|
g_return_val_if_reached (NULL);
|
|
if (!address)
|
|
g_return_val_if_reached (NULL);
|
|
|
|
obj = nmp_cache_lookup_link_full (nm_platform_get_cache (self),
|
|
0, NULL, TRUE, NM_LINK_TYPE_NONE,
|
|
(NMPObjectMatchFn) _nm_platform_link_get_by_address_match_link, &d);
|
|
return NMP_OBJECT_CAST_LINK (obj);
|
|
}
|
|
|
|
static NMPlatformError
|
|
_link_add_check_existing (NMPlatform *self, const char *name, NMLinkType type, const NMPlatformLink **out_link)
|
|
{
|
|
const NMPlatformLink *pllink;
|
|
|
|
pllink = nm_platform_link_get_by_ifname (self, name);
|
|
if (pllink) {
|
|
gboolean wrong_type;
|
|
|
|
wrong_type = type != NM_LINK_TYPE_NONE && pllink->type != type;
|
|
_LOGD ("link: skip adding link due to existing interface '%s' of type %s%s%s",
|
|
name,
|
|
nm_link_type_to_string (pllink->type),
|
|
wrong_type ? ", expected " : "",
|
|
wrong_type ? nm_link_type_to_string (type) : "");
|
|
if (out_link)
|
|
*out_link = pllink;
|
|
if (wrong_type)
|
|
return NM_PLATFORM_ERROR_WRONG_TYPE;
|
|
return NM_PLATFORM_ERROR_EXISTS;
|
|
}
|
|
if (out_link)
|
|
*out_link = NULL;
|
|
return NM_PLATFORM_ERROR_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_add:
|
|
* @self: platform instance
|
|
* @name: Interface name
|
|
* @type: Interface type
|
|
* @veth_peer: For veths, the peer name
|
|
* @address: (allow-none): set the mac address of the link
|
|
* @address_len: the length of the @address
|
|
* @out_link: on success, the link object
|
|
*
|
|
* Add a software interface. If the interface already exists and is of type
|
|
* @type, return NM_PLATFORM_ERROR_EXISTS and returns the link
|
|
* in @out_link. If the interface already exists and is not of type @type,
|
|
* return NM_PLATFORM_ERROR_WRONG_TYPE.
|
|
*
|
|
* Any link-changed ADDED signal will be emitted directly, before this
|
|
* function finishes.
|
|
*
|
|
* Returns: the error reason or NM_PLATFORM_ERROR_SUCCESS.
|
|
*/
|
|
static NMPlatformError
|
|
nm_platform_link_add (NMPlatform *self,
|
|
const char *name,
|
|
NMLinkType type,
|
|
const char *veth_peer,
|
|
const void *address,
|
|
size_t address_len,
|
|
const NMPlatformLink **out_link)
|
|
{
|
|
NMPlatformError plerr;
|
|
char addr_buf[NM_UTILS_HWADDR_LEN_MAX * 3];
|
|
|
|
_CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG);
|
|
|
|
g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG);
|
|
g_return_val_if_fail ((address != NULL) ^ (address_len == 0) , NM_PLATFORM_ERROR_BUG);
|
|
g_return_val_if_fail (address_len <= NM_UTILS_HWADDR_LEN_MAX, NM_PLATFORM_ERROR_BUG);
|
|
g_return_val_if_fail ((!!veth_peer) == (type == NM_LINK_TYPE_VETH), NM_PLATFORM_ERROR_BUG);
|
|
|
|
plerr = _link_add_check_existing (self, name, type, out_link);
|
|
if (plerr != NM_PLATFORM_ERROR_SUCCESS)
|
|
return plerr;
|
|
|
|
_LOGD ("link: add link '%s' of type '%s' (%d)"
|
|
"%s%s" /* address */
|
|
"%s%s" /* veth peer */
|
|
"",
|
|
name,
|
|
nm_link_type_to_string (type),
|
|
(int) type,
|
|
address ? ", address: " : "",
|
|
address ? nm_utils_hwaddr_ntoa_buf (address, address_len, FALSE, addr_buf, sizeof (addr_buf)) : "",
|
|
veth_peer ? ", veth-peer: " : "",
|
|
veth_peer ?: "");
|
|
|
|
if (!klass->link_add (self, name, type, veth_peer, address, address_len, out_link))
|
|
return NM_PLATFORM_ERROR_UNSPECIFIED;
|
|
return NM_PLATFORM_ERROR_SUCCESS;
|
|
}
|
|
|
|
NMPlatformError
|
|
nm_platform_link_veth_add (NMPlatform *self,
|
|
const char *name,
|
|
const char *peer,
|
|
const NMPlatformLink **out_link)
|
|
{
|
|
return nm_platform_link_add (self, name, NM_LINK_TYPE_VETH, peer, NULL, 0, out_link);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_dummy_add:
|
|
* @self: platform instance
|
|
* @name: New interface name
|
|
* @out_link: on success, the link object
|
|
*
|
|
* Create a software ethernet-like interface
|
|
*/
|
|
NMPlatformError
|
|
nm_platform_link_dummy_add (NMPlatform *self,
|
|
const char *name,
|
|
const NMPlatformLink **out_link)
|
|
{
|
|
return nm_platform_link_add (self, name, NM_LINK_TYPE_DUMMY, NULL, NULL, 0, out_link);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_delete:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index
|
|
*/
|
|
gboolean
|
|
nm_platform_link_delete (NMPlatform *self, int ifindex)
|
|
{
|
|
const NMPlatformLink *pllink;
|
|
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
pllink = nm_platform_link_get (self, ifindex);
|
|
if (!pllink)
|
|
return FALSE;
|
|
|
|
_LOGD ("link: deleting '%s' (%d)", pllink->name, ifindex);
|
|
return klass->link_delete (self, ifindex);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_set_netns:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index
|
|
* @netns_fd: the file descriptor for the new netns.
|
|
*
|
|
* Returns: %TRUE on success.
|
|
*/
|
|
gboolean
|
|
nm_platform_link_set_netns (NMPlatform *self, int ifindex, int netns_fd)
|
|
{
|
|
const NMPlatformLink *pllink;
|
|
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex > 0, FALSE);
|
|
g_return_val_if_fail (netns_fd > 0, FALSE);
|
|
|
|
pllink = nm_platform_link_get (self, ifindex);
|
|
if (!pllink)
|
|
return FALSE;
|
|
|
|
_LOGD ("link: ifindex %d changing network namespace to %d", ifindex, netns_fd);
|
|
return klass->link_set_netns (self, ifindex, netns_fd);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_get_index:
|
|
* @self: platform instance
|
|
* @name: Interface name
|
|
*
|
|
* Returns: The interface index corresponding to the given interface name
|
|
* or 0. Inteface name is owned by #NMPlatform, don't free it.
|
|
*/
|
|
int
|
|
nm_platform_link_get_ifindex (NMPlatform *self, const char *name)
|
|
{
|
|
const NMPlatformLink *pllink;
|
|
|
|
pllink = nm_platform_link_get_by_ifname (self, name);
|
|
return pllink ? pllink->ifindex : 0;
|
|
}
|
|
|
|
const char *
|
|
nm_platform_if_indextoname (NMPlatform *self, int ifindex, char *out_ifname/* of size IFNAMSIZ */)
|
|
{
|
|
_CHECK_SELF_NETNS (self, klass, netns, FALSE);
|
|
|
|
return nmp_utils_if_indextoname (ifindex, out_ifname);
|
|
}
|
|
|
|
int
|
|
nm_platform_if_nametoindex (NMPlatform *self, const char *ifname)
|
|
{
|
|
_CHECK_SELF_NETNS (self, klass, netns, FALSE);
|
|
|
|
return nmp_utils_if_nametoindex (ifname);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_get_name:
|
|
* @self: platform instance
|
|
* @name: Interface name
|
|
*
|
|
* Returns: The interface name corresponding to the given interface index
|
|
* or %NULL.
|
|
*/
|
|
const char *
|
|
nm_platform_link_get_name (NMPlatform *self, int ifindex)
|
|
{
|
|
const NMPlatformLink *pllink;
|
|
|
|
_CHECK_SELF (self, klass, NULL);
|
|
|
|
pllink = nm_platform_link_get (self, ifindex);
|
|
return pllink ? pllink->name : NULL;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_get_type:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index.
|
|
*
|
|
* Returns: Link type constant as defined in nm-platform.h. On error,
|
|
* NM_LINK_TYPE_NONE is returned.
|
|
*/
|
|
NMLinkType
|
|
nm_platform_link_get_type (NMPlatform *self, int ifindex)
|
|
{
|
|
const NMPlatformLink *pllink;
|
|
|
|
_CHECK_SELF (self, klass, NM_LINK_TYPE_NONE);
|
|
|
|
pllink = nm_platform_link_get (self, ifindex);
|
|
return pllink ? pllink->type : NM_LINK_TYPE_NONE;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_get_type_name:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index.
|
|
*
|
|
* Returns: A string describing the type of link. In some cases this
|
|
* may be more specific than nm_platform_link_get_type(), but in
|
|
* other cases it may not. On error, %NULL is returned.
|
|
*/
|
|
const char *
|
|
nm_platform_link_get_type_name (NMPlatform *self, int ifindex)
|
|
{
|
|
const NMPObject *obj;
|
|
|
|
_CHECK_SELF (self, klass, NULL);
|
|
|
|
obj = nm_platform_link_get_obj (self, ifindex, TRUE);
|
|
|
|
if (!obj)
|
|
return NULL;
|
|
|
|
if (obj->link.type != NM_LINK_TYPE_UNKNOWN) {
|
|
/* We could detect the @link_type. In this case the function returns
|
|
* our internel module names, which differs from rtnl_link_get_type():
|
|
* - NM_LINK_TYPE_INFINIBAND (gives "infiniband", instead of "ipoib")
|
|
* - NM_LINK_TYPE_TAP (gives "tap", instead of "tun").
|
|
* Note that this functions is only used by NMDeviceGeneric to
|
|
* set type_description. */
|
|
return nm_link_type_to_string (obj->link.type);
|
|
}
|
|
/* Link type not detected. Fallback to rtnl_link_get_type()/IFLA_INFO_KIND. */
|
|
return obj->link.kind ?: "unknown";
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_get_unmanaged:
|
|
* @self: platform instance
|
|
* @ifindex: interface index
|
|
* @unmanaged: management status (in case %TRUE is returned)
|
|
*
|
|
* Returns: %TRUE if platform overrides NM default-unmanaged status,
|
|
* %FALSE otherwise (with @unmanaged unmodified).
|
|
*/
|
|
gboolean
|
|
nm_platform_link_get_unmanaged (NMPlatform *self, int ifindex, gboolean *unmanaged)
|
|
{
|
|
const NMPObject *link;
|
|
struct udev_device *udevice = NULL;
|
|
const char *uproperty;
|
|
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
link = nmp_cache_lookup_link (nm_platform_get_cache (self), ifindex);
|
|
if (!link)
|
|
return FALSE;
|
|
|
|
udevice = link->_link.udev.device;
|
|
if (!udevice)
|
|
return FALSE;
|
|
|
|
uproperty = udev_device_get_property_value (udevice, "NM_UNMANAGED");
|
|
if (!uproperty)
|
|
return FALSE;
|
|
|
|
*unmanaged = nm_udev_utils_property_as_boolean (uproperty);
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_is_software:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index.
|
|
*
|
|
* Returns: %TRUE if ifindex belongs to a software interface, not backed by
|
|
* a physical device.
|
|
*/
|
|
gboolean
|
|
nm_platform_link_is_software (NMPlatform *self, int ifindex)
|
|
{
|
|
return (nm_platform_link_get_type (self, ifindex) & 0x10000);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_supports_slaves:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index.
|
|
*
|
|
* Returns: %TRUE if ifindex belongs to an interface capable of enslaving
|
|
* other interfaces.
|
|
*/
|
|
gboolean
|
|
nm_platform_link_supports_slaves (NMPlatform *self, int ifindex)
|
|
{
|
|
return (nm_platform_link_get_type (self, ifindex) & 0x20000);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_refresh:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index
|
|
*
|
|
* Reload the cache for ifindex synchronously.
|
|
*/
|
|
gboolean
|
|
nm_platform_link_refresh (NMPlatform *self, int ifindex)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex > 0, FALSE);
|
|
|
|
if (klass->link_refresh)
|
|
return klass->link_refresh (self, ifindex);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static guint
|
|
_link_get_flags (NMPlatform *self, int ifindex)
|
|
{
|
|
const NMPlatformLink *pllink;
|
|
|
|
pllink = nm_platform_link_get (self, ifindex);
|
|
return pllink ? pllink->n_ifi_flags : IFF_NOARP;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_is_up:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index
|
|
*
|
|
* Check if the interface is up.
|
|
*/
|
|
gboolean
|
|
nm_platform_link_is_up (NMPlatform *self, int ifindex)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
return NM_FLAGS_HAS (_link_get_flags (self, ifindex), IFF_UP);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_is_connected:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index
|
|
*
|
|
* Check if the interface is connected.
|
|
*/
|
|
gboolean
|
|
nm_platform_link_is_connected (NMPlatform *self, int ifindex)
|
|
{
|
|
const NMPlatformLink *pllink;
|
|
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
pllink = nm_platform_link_get (self, ifindex);
|
|
return pllink ? pllink->connected : FALSE;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_uses_arp:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index
|
|
*
|
|
* Check if the interface is configured to use ARP.
|
|
*/
|
|
gboolean
|
|
nm_platform_link_uses_arp (NMPlatform *self, int ifindex)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
return !NM_FLAGS_HAS (_link_get_flags (self, ifindex), IFF_NOARP);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_set_ipv6_token:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index
|
|
* @iid: Tokenized interface identifier
|
|
*
|
|
* Sets then IPv6 tokenized interface identifier.
|
|
*
|
|
* Returns: %TRUE a tokenized identifier was available
|
|
*/
|
|
gboolean
|
|
nm_platform_link_set_ipv6_token (NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId iid)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex >= 0, FALSE);
|
|
g_return_val_if_fail (iid.id, FALSE);
|
|
|
|
if (klass->link_set_token)
|
|
return klass->link_set_token (self, ifindex, iid);
|
|
return FALSE;
|
|
}
|
|
|
|
const char *
|
|
nm_platform_link_get_udi (NMPlatform *self, int ifindex)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex >= 0, NULL);
|
|
|
|
if (klass->link_get_udi)
|
|
return klass->link_get_udi (self, ifindex);
|
|
return NULL;
|
|
}
|
|
|
|
struct udev_device *
|
|
nm_platform_link_get_udev_device (NMPlatform *self, int ifindex)
|
|
{
|
|
const NMPObject *obj_cache;
|
|
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex >= 0, NULL);
|
|
|
|
obj_cache = nm_platform_link_get_obj (self, ifindex, FALSE);
|
|
return obj_cache ? obj_cache->_link.udev.device : NULL;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_get_user_ip6vll_enabled:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index
|
|
*
|
|
* Check whether NM handles IPv6LL address creation for the link. If the
|
|
* platform or OS doesn't support changing the IPv6LL address mode, this call
|
|
* will fail and return %FALSE.
|
|
*
|
|
* Returns: %TRUE if NM handles the IPv6LL address for @ifindex
|
|
*/
|
|
gboolean
|
|
nm_platform_link_get_user_ipv6ll_enabled (NMPlatform *self, int ifindex)
|
|
{
|
|
const NMPlatformLink *pllink;
|
|
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex >= 0, FALSE);
|
|
|
|
pllink = nm_platform_link_get (self, ifindex);
|
|
if (pllink && pllink->inet6_addr_gen_mode_inv)
|
|
return _nm_platform_uint8_inv (pllink->inet6_addr_gen_mode_inv) == NM_IN6_ADDR_GEN_MODE_NONE;
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_set_user_ip6vll_enabled:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index
|
|
*
|
|
* Set whether NM handles IPv6LL address creation for the link. If the
|
|
* platform or OS doesn't support changing the IPv6LL address mode, this call
|
|
* will fail and return %FALSE.
|
|
*
|
|
* Returns: %NM_PLATFORM_ERROR_SUCCESS if the operation was successful or an error code otherwise.
|
|
*/
|
|
NMPlatformError
|
|
nm_platform_link_set_user_ipv6ll_enabled (NMPlatform *self, int ifindex, gboolean enabled)
|
|
{
|
|
_CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG);
|
|
|
|
g_return_val_if_fail (ifindex > 0, NM_PLATFORM_ERROR_BUG);
|
|
|
|
return klass->link_set_user_ipv6ll_enabled (self, ifindex, enabled);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_set_address:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index
|
|
* @address: The new MAC address
|
|
*
|
|
* Set interface MAC address.
|
|
*/
|
|
NMPlatformError
|
|
nm_platform_link_set_address (NMPlatform *self, int ifindex, gconstpointer address, size_t length)
|
|
{
|
|
_CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG);
|
|
|
|
g_return_val_if_fail (ifindex > 0, NM_PLATFORM_ERROR_BUG);
|
|
g_return_val_if_fail (address, NM_PLATFORM_ERROR_BUG);
|
|
g_return_val_if_fail (length > 0, NM_PLATFORM_ERROR_BUG);
|
|
|
|
_LOGD ("link: setting %s (%d) hardware address",
|
|
nm_strquote_a (20, nm_platform_link_get_name (self, ifindex)),
|
|
ifindex);
|
|
return klass->link_set_address (self, ifindex, address, length);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_get_address:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index
|
|
* @length: Pointer to a variable to store address length
|
|
*
|
|
* Returns: the interface hardware address as an array of bytes of
|
|
* length @length.
|
|
*/
|
|
gconstpointer
|
|
nm_platform_link_get_address (NMPlatform *self, int ifindex, size_t *length)
|
|
{
|
|
const NMPlatformLink *pllink;
|
|
gconstpointer a = NULL;
|
|
guint8 l = 0;
|
|
|
|
_CHECK_SELF (self, klass, NULL);
|
|
|
|
if (length)
|
|
*length = 0;
|
|
|
|
g_return_val_if_fail (ifindex > 0, NULL);
|
|
|
|
pllink = nm_platform_link_get (self, ifindex);
|
|
if (pllink && pllink->addr.len > 0) {
|
|
if (pllink->addr.len > NM_UTILS_HWADDR_LEN_MAX) {
|
|
if (length)
|
|
*length = 0;
|
|
g_return_val_if_reached (NULL);
|
|
}
|
|
a = pllink->addr.data;
|
|
l = pllink->addr.len;
|
|
}
|
|
|
|
if (length)
|
|
*length = l;
|
|
return a;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_get_permanent_address:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index
|
|
* @buf: buffer of at least %NM_UTILS_HWADDR_LEN_MAX bytes, on success
|
|
* the permanent hardware address
|
|
* @length: Pointer to a variable to store address length
|
|
*
|
|
* Returns: %TRUE on success, %FALSE on failure to read the permanent hardware
|
|
* address.
|
|
*/
|
|
gboolean
|
|
nm_platform_link_get_permanent_address (NMPlatform *self, int ifindex, guint8 *buf, size_t *length)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
if (length)
|
|
*length = 0;
|
|
|
|
g_return_val_if_fail (ifindex > 0, FALSE);
|
|
g_return_val_if_fail (buf, FALSE);
|
|
g_return_val_if_fail (length, FALSE);
|
|
|
|
if (klass->link_get_permanent_address)
|
|
return klass->link_get_permanent_address (self, ifindex, buf, length);
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_link_supports_carrier_detect (NMPlatform *self, int ifindex)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex >= 0, FALSE);
|
|
|
|
return klass->link_supports_carrier_detect (self, ifindex);
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_link_supports_vlans (NMPlatform *self, int ifindex)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex >= 0, FALSE);
|
|
|
|
return klass->link_supports_vlans (self, ifindex);
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_link_supports_sriov (NMPlatform *self, int ifindex)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex >= 0, FALSE);
|
|
|
|
return klass->link_supports_sriov (self, ifindex);
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_link_set_sriov_num_vfs (NMPlatform *self, int ifindex, guint num_vfs)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex > 0, FALSE);
|
|
|
|
_LOGD ("link: setting %u VFs for %s (%d)",
|
|
num_vfs,
|
|
nm_strquote_a (25, nm_platform_link_get_name (self, ifindex)),
|
|
ifindex);
|
|
return klass->link_set_sriov_num_vfs (self, ifindex, num_vfs);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_set_up:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index
|
|
* @out_no_firmware: (allow-none): if the failure reason is due to missing firmware.
|
|
*
|
|
* Bring the interface up.
|
|
*/
|
|
gboolean
|
|
nm_platform_link_set_up (NMPlatform *self, int ifindex, gboolean *out_no_firmware)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex > 0, FALSE);
|
|
|
|
_LOGD ("link: setting up %s (%d)", nm_strquote_a (25, nm_platform_link_get_name (self, ifindex)), ifindex);
|
|
return klass->link_set_up (self, ifindex, out_no_firmware);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_set_down:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index
|
|
*
|
|
* Take the interface down.
|
|
*/
|
|
gboolean
|
|
nm_platform_link_set_down (NMPlatform *self, int ifindex)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex > 0, FALSE);
|
|
|
|
_LOGD ("link: setting down %s (%d)", nm_strquote_a (25, nm_platform_link_get_name (self, ifindex)), ifindex);
|
|
return klass->link_set_down (self, ifindex);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_set_arp:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index
|
|
*
|
|
* Enable ARP on the interface.
|
|
*/
|
|
gboolean
|
|
nm_platform_link_set_arp (NMPlatform *self, int ifindex)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex >= 0, FALSE);
|
|
|
|
_LOGD ("link: setting arp %s (%d)", nm_strquote_a (25, nm_platform_link_get_name (self, ifindex)), ifindex);
|
|
return klass->link_set_arp (self, ifindex);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_set_noarp:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index
|
|
*
|
|
* Disable ARP on the interface.
|
|
*/
|
|
gboolean
|
|
nm_platform_link_set_noarp (NMPlatform *self, int ifindex)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex >= 0, FALSE);
|
|
|
|
_LOGD ("link: setting noarp '%s' (%d)", nm_platform_link_get_name (self, ifindex), ifindex);
|
|
return klass->link_set_noarp (self, ifindex);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_set_mtu:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index
|
|
* @mtu: The new MTU value
|
|
*
|
|
* Set interface MTU.
|
|
*/
|
|
gboolean
|
|
nm_platform_link_set_mtu (NMPlatform *self, int ifindex, guint32 mtu)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex >= 0, FALSE);
|
|
g_return_val_if_fail (mtu > 0, FALSE);
|
|
|
|
_LOGD ("link: setting '%s' (%d) mtu %"G_GUINT32_FORMAT, nm_platform_link_get_name (self, ifindex), ifindex, mtu);
|
|
return klass->link_set_mtu (self, ifindex, mtu);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_get_mtu:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index
|
|
*
|
|
* Returns: MTU value for the interface or 0 on error.
|
|
*/
|
|
guint32
|
|
nm_platform_link_get_mtu (NMPlatform *self, int ifindex)
|
|
{
|
|
const NMPlatformLink *pllink;
|
|
|
|
_CHECK_SELF (self, klass, 0);
|
|
|
|
pllink = nm_platform_link_get (self, ifindex);
|
|
return pllink ? pllink->mtu : 0;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_set_name:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index
|
|
* @name: The new interface name
|
|
*
|
|
* Set interface name.
|
|
*/
|
|
gboolean
|
|
nm_platform_link_set_name (NMPlatform *self, int ifindex, const char *name)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex >= 0, FALSE);
|
|
g_return_val_if_fail (name, FALSE);
|
|
|
|
_LOGD ("link: setting '%s' (%d) name %s", nm_platform_link_get_name (self, ifindex), ifindex, name);
|
|
|
|
if (strlen (name) + 1 > IFNAMSIZ)
|
|
return FALSE;
|
|
|
|
return klass->link_set_name (self, ifindex, name);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_get_physical_port_id:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index
|
|
*
|
|
* The physical port ID, if present, indicates some unique identifier of
|
|
* the parent interface (eg, the physical port of which this link is a child).
|
|
* Two links that report the same physical port ID can be assumed to be
|
|
* children of the same physical port and may share resources that limit
|
|
* their abilities.
|
|
*
|
|
* Returns: physical port ID for the interface, or %NULL on error
|
|
* or if the interface has no physical port ID.
|
|
*/
|
|
char *
|
|
nm_platform_link_get_physical_port_id (NMPlatform *self, int ifindex)
|
|
{
|
|
_CHECK_SELF (self, klass, NULL);
|
|
|
|
g_return_val_if_fail (ifindex >= 0, NULL);
|
|
|
|
if (klass->link_get_physical_port_id)
|
|
return klass->link_get_physical_port_id (self, ifindex);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_get_dev_id:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index
|
|
*
|
|
* In contrast to the physical device ID (which indicates which parent a
|
|
* child has) the device ID differentiates sibling devices that may share
|
|
* the same MAC address.
|
|
*
|
|
* Returns: device ID for the interface, or 0 on error or if the
|
|
* interface has no device ID.
|
|
*/
|
|
guint
|
|
nm_platform_link_get_dev_id (NMPlatform *self, int ifindex)
|
|
{
|
|
_CHECK_SELF (self, klass, 0);
|
|
|
|
g_return_val_if_fail (ifindex >= 0, 0);
|
|
|
|
if (klass->link_get_dev_id)
|
|
return klass->link_get_dev_id (self, ifindex);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_get_wake_onlan:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index
|
|
*
|
|
* Returns: the "Wake-on-LAN" status for @ifindex.
|
|
*/
|
|
gboolean
|
|
nm_platform_link_get_wake_on_lan (NMPlatform *self, int ifindex)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex >= 0, FALSE);
|
|
|
|
if (klass->link_get_wake_on_lan)
|
|
return klass->link_get_wake_on_lan (self, ifindex);
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_get_driver_info:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index
|
|
* @out_driver_name: (transfer full): on success, the driver name if available
|
|
* @out_driver_version: (transfer full): on success, the driver version if available
|
|
* @out_fw_version: (transfer full): on success, the firmware version if available
|
|
*
|
|
* Returns: %TRUE on success (though @out_driver_name, @out_driver_version and
|
|
* @out_fw_version can be %NULL if no information was available), %FALSE on
|
|
* failure.
|
|
*/
|
|
gboolean
|
|
nm_platform_link_get_driver_info (NMPlatform *self,
|
|
int ifindex,
|
|
char **out_driver_name,
|
|
char **out_driver_version,
|
|
char **out_fw_version)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex >= 0, FALSE);
|
|
|
|
return klass->link_get_driver_info (self,
|
|
ifindex,
|
|
out_driver_name,
|
|
out_driver_version,
|
|
out_fw_version);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_enslave:
|
|
* @self: platform instance
|
|
* @master: Interface index of the master
|
|
* @slave: Interface index of the slave
|
|
*
|
|
* Enslave @slave to @master.
|
|
*/
|
|
gboolean
|
|
nm_platform_link_enslave (NMPlatform *self, int master, int slave)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (master > 0, FALSE);
|
|
g_return_val_if_fail (slave> 0, FALSE);
|
|
|
|
_LOGD ("link: enslaving '%s' (%d) to master '%s' (%d)",
|
|
nm_platform_link_get_name (self, slave), slave,
|
|
nm_platform_link_get_name (self, master), master);
|
|
return klass->link_enslave (self, master, slave);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_release:
|
|
* @self: platform instance
|
|
* @master: Interface index of the master
|
|
* @slave: Interface index of the slave
|
|
*
|
|
* Release @slave from @master.
|
|
*/
|
|
gboolean
|
|
nm_platform_link_release (NMPlatform *self, int master, int slave)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (master > 0, FALSE);
|
|
g_return_val_if_fail (slave > 0, FALSE);
|
|
|
|
if (nm_platform_link_get_master (self, slave) != master)
|
|
return FALSE;
|
|
|
|
_LOGD ("link: releasing '%s' (%d) from master '%s' (%d)",
|
|
nm_platform_link_get_name (self, slave), slave,
|
|
nm_platform_link_get_name (self, master), master);
|
|
return klass->link_release (self, master, slave);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_get_master:
|
|
* @self: platform instance
|
|
* @slave: Interface index of the slave.
|
|
*
|
|
* Returns: Interface index of the slave's master.
|
|
*/
|
|
int
|
|
nm_platform_link_get_master (NMPlatform *self, int slave)
|
|
{
|
|
const NMPlatformLink *pllink;
|
|
|
|
_CHECK_SELF (self, klass, 0);
|
|
|
|
g_return_val_if_fail (slave >= 0, FALSE);
|
|
|
|
pllink = nm_platform_link_get (self, slave);
|
|
return pllink ? pllink->master : 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
gboolean
|
|
nm_platform_link_can_assume (NMPlatform *self, int ifindex)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
if (klass->link_can_assume)
|
|
return klass->link_can_assume (self, ifindex);
|
|
g_return_val_if_reached (FALSE);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
* nm_platform_link_get_lnk:
|
|
* @self: the platform instance
|
|
* @ifindex: the link ifindex to lookup
|
|
* @link_type: filter by link-type.
|
|
* @out_link: (allow-none): returns the platform link instance
|
|
*
|
|
* If the function returns %NULL, that could mean that no such ifindex
|
|
* exists, of that the link has no lnk data. You can find that out
|
|
* by checking @out_link. @out_link will always be set if a link
|
|
* with @ifindex exists.
|
|
*
|
|
* If @link_type is %NM_LINK_TYPE_NONE, the function returns the lnk
|
|
* object if it is present. If you set link-type, you can be sure
|
|
* that only a link type of the matching type is returned (or %NULL).
|
|
*
|
|
* Returns: the internal link lnk object. The returned object
|
|
* is owned by the platform cache and must not be modified. Note
|
|
* however, that the object is guaranteed to be immutable, so
|
|
* you can savely take a reference and keep it for yourself
|
|
* (but don't modify it).
|
|
*/
|
|
const NMPObject *
|
|
nm_platform_link_get_lnk (NMPlatform *self, int ifindex, NMLinkType link_type, const NMPlatformLink **out_link)
|
|
{
|
|
const NMPObject *obj;
|
|
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
NM_SET_OUT (out_link, NULL);
|
|
|
|
g_return_val_if_fail (ifindex > 0, NULL);
|
|
|
|
obj = nm_platform_link_get_obj (self, ifindex, TRUE);
|
|
if (!obj)
|
|
return NULL;
|
|
|
|
NM_SET_OUT (out_link, &obj->link);
|
|
|
|
if (!obj->_link.netlink.lnk)
|
|
return NULL;
|
|
if ( link_type != NM_LINK_TYPE_NONE
|
|
&& ( link_type != obj->link.type
|
|
|| link_type != NMP_OBJECT_GET_CLASS (obj->_link.netlink.lnk)->lnk_link_type))
|
|
return NULL;
|
|
|
|
return obj->_link.netlink.lnk;
|
|
}
|
|
|
|
static gconstpointer
|
|
_link_get_lnk (NMPlatform *self, int ifindex, NMLinkType link_type, const NMPlatformLink **out_link)
|
|
{
|
|
const NMPObject *lnk;
|
|
|
|
lnk = nm_platform_link_get_lnk (self, ifindex, link_type, out_link);
|
|
return lnk ? &lnk->object : NULL;
|
|
}
|
|
|
|
const NMPlatformLnkGre *
|
|
nm_platform_link_get_lnk_gre (NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
|
|
{
|
|
return _link_get_lnk (self, ifindex, NM_LINK_TYPE_GRE, out_link);
|
|
}
|
|
|
|
const NMPlatformLnkInfiniband *
|
|
nm_platform_link_get_lnk_infiniband (NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
|
|
{
|
|
return _link_get_lnk (self, ifindex, NM_LINK_TYPE_INFINIBAND, out_link);
|
|
}
|
|
|
|
const NMPlatformLnkIp6Tnl *
|
|
nm_platform_link_get_lnk_ip6tnl (NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
|
|
{
|
|
return _link_get_lnk (self, ifindex, NM_LINK_TYPE_IP6TNL, out_link);
|
|
}
|
|
|
|
const NMPlatformLnkIpIp *
|
|
nm_platform_link_get_lnk_ipip (NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
|
|
{
|
|
return _link_get_lnk (self, ifindex, NM_LINK_TYPE_IPIP, out_link);
|
|
}
|
|
|
|
const NMPlatformLnkMacsec *
|
|
nm_platform_link_get_lnk_macsec (NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
|
|
{
|
|
return _link_get_lnk (self, ifindex, NM_LINK_TYPE_MACSEC, out_link);
|
|
}
|
|
|
|
const NMPlatformLnkMacvlan *
|
|
nm_platform_link_get_lnk_macvlan (NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
|
|
{
|
|
return _link_get_lnk (self, ifindex, NM_LINK_TYPE_MACVLAN, out_link);
|
|
}
|
|
|
|
const NMPlatformLnkMacvtap *
|
|
nm_platform_link_get_lnk_macvtap (NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
|
|
{
|
|
return _link_get_lnk (self, ifindex, NM_LINK_TYPE_MACVTAP, out_link);
|
|
}
|
|
|
|
const NMPlatformLnkSit *
|
|
nm_platform_link_get_lnk_sit (NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
|
|
{
|
|
return _link_get_lnk (self, ifindex, NM_LINK_TYPE_SIT, out_link);
|
|
}
|
|
|
|
const NMPlatformLnkVlan *
|
|
nm_platform_link_get_lnk_vlan (NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
|
|
{
|
|
return _link_get_lnk (self, ifindex, NM_LINK_TYPE_VLAN, out_link);
|
|
}
|
|
|
|
const NMPlatformLnkVxlan *
|
|
nm_platform_link_get_lnk_vxlan (NMPlatform *self, int ifindex, const NMPlatformLink **out_link)
|
|
{
|
|
return _link_get_lnk (self, ifindex, NM_LINK_TYPE_VXLAN, out_link);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
* nm_platform_link_bridge_add:
|
|
* @self: platform instance
|
|
* @name: New interface name
|
|
* @address: (allow-none): set the mac address of the new bridge
|
|
* @address_len: the length of the @address
|
|
* @out_link: on success, the link object
|
|
*
|
|
* Create a software bridge.
|
|
*/
|
|
NMPlatformError
|
|
nm_platform_link_bridge_add (NMPlatform *self,
|
|
const char *name,
|
|
const void *address,
|
|
size_t address_len,
|
|
const NMPlatformLink **out_link)
|
|
{
|
|
return nm_platform_link_add (self, name, NM_LINK_TYPE_BRIDGE, NULL, address, address_len, out_link);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_bond_add:
|
|
* @self: platform instance
|
|
* @name: New interface name
|
|
* @out_link: on success, the link object
|
|
*
|
|
* Create a software bonding device.
|
|
*/
|
|
NMPlatformError
|
|
nm_platform_link_bond_add (NMPlatform *self,
|
|
const char *name,
|
|
const NMPlatformLink **out_link)
|
|
{
|
|
return nm_platform_link_add (self, name, NM_LINK_TYPE_BOND, NULL, NULL, 0, out_link);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_team_add:
|
|
* @self: platform instance
|
|
* @name: New interface name
|
|
* @out_link: on success, the link object
|
|
*
|
|
* Create a software teaming device.
|
|
*/
|
|
NMPlatformError
|
|
nm_platform_link_team_add (NMPlatform *self,
|
|
const char *name,
|
|
const NMPlatformLink **out_link)
|
|
{
|
|
return nm_platform_link_add (self, name, NM_LINK_TYPE_TEAM, NULL, NULL, 0, out_link);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_vlan_add:
|
|
* @self: platform instance
|
|
* @name: New interface name
|
|
* @vlanid: VLAN identifier
|
|
* @vlanflags: VLAN flags from libnm
|
|
* @out_link: on success, the link object
|
|
*
|
|
* Create a software VLAN device.
|
|
*/
|
|
NMPlatformError
|
|
nm_platform_link_vlan_add (NMPlatform *self,
|
|
const char *name,
|
|
int parent,
|
|
int vlanid,
|
|
guint32 vlanflags,
|
|
const NMPlatformLink **out_link)
|
|
{
|
|
NMPlatformError plerr;
|
|
|
|
_CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG);
|
|
|
|
g_return_val_if_fail (parent >= 0, NM_PLATFORM_ERROR_BUG);
|
|
g_return_val_if_fail (vlanid >= 0, NM_PLATFORM_ERROR_BUG);
|
|
g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG);
|
|
|
|
plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_VLAN, out_link);
|
|
if (plerr != NM_PLATFORM_ERROR_SUCCESS)
|
|
return plerr;
|
|
|
|
_LOGD ("link: adding vlan '%s' parent %d vlanid %d vlanflags %x",
|
|
name, parent, vlanid, vlanflags);
|
|
if (!klass->vlan_add (self, name, parent, vlanid, vlanflags, out_link))
|
|
return NM_PLATFORM_ERROR_UNSPECIFIED;
|
|
return NM_PLATFORM_ERROR_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_vxlan_add:
|
|
* @self: platform instance
|
|
* @name: New interface name
|
|
* @props: properties of the new link
|
|
* @out_link: on success, the link object
|
|
*
|
|
* Create a VXLAN device.
|
|
*/
|
|
NMPlatformError
|
|
nm_platform_link_vxlan_add (NMPlatform *self,
|
|
const char *name,
|
|
const NMPlatformLnkVxlan *props,
|
|
const NMPlatformLink **out_link)
|
|
{
|
|
NMPlatformError plerr;
|
|
|
|
_CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG);
|
|
|
|
g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG);
|
|
|
|
plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_VXLAN, out_link);
|
|
if (plerr != NM_PLATFORM_ERROR_SUCCESS)
|
|
return plerr;
|
|
|
|
_LOGD ("link: adding vxlan '%s' parent %d id %d",
|
|
name, props->parent_ifindex, props->id);
|
|
if (!klass->link_vxlan_add (self, name, props, out_link))
|
|
return NM_PLATFORM_ERROR_UNSPECIFIED;
|
|
return NM_PLATFORM_ERROR_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_tun_add:
|
|
* @self: platform instance
|
|
* @name: new interface name
|
|
* @tap: whether the interface is a TAP
|
|
* @owner: interface owner or -1
|
|
* @group: interface group or -1
|
|
* @pi: whether to clear the IFF_NO_PI flag
|
|
* @vnet_hdr: whether to set the IFF_VNET_HDR flag
|
|
* @multi_queue: whether to set the IFF_MULTI_QUEUE flag
|
|
* @out_link: on success, the link object
|
|
*
|
|
* Create a TUN or TAP interface.
|
|
*/
|
|
NMPlatformError
|
|
nm_platform_link_tun_add (NMPlatform *self,
|
|
const char *name,
|
|
gboolean tap,
|
|
gint64 owner,
|
|
gint64 group,
|
|
gboolean pi,
|
|
gboolean vnet_hdr,
|
|
gboolean multi_queue,
|
|
const NMPlatformLink **out_link)
|
|
{
|
|
NMPlatformError plerr;
|
|
|
|
_CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG);
|
|
|
|
g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG);
|
|
|
|
plerr = _link_add_check_existing (self, name, tap ? NM_LINK_TYPE_TAP : NM_LINK_TYPE_TUN, out_link);
|
|
if (plerr != NM_PLATFORM_ERROR_SUCCESS)
|
|
return plerr;
|
|
|
|
_LOGD ("link: adding %s '%s' owner %" G_GINT64_FORMAT " group %" G_GINT64_FORMAT,
|
|
tap ? "tap" : "tun", name, owner, group);
|
|
if (!klass->tun_add (self, name, tap, owner, group, pi, vnet_hdr, multi_queue, out_link))
|
|
return NM_PLATFORM_ERROR_UNSPECIFIED;
|
|
return NM_PLATFORM_ERROR_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static gboolean
|
|
link_set_option (NMPlatform *self, int ifindex, const char *category, const char *option, const char *value)
|
|
{
|
|
nm_auto_close int dirfd = -1;
|
|
char ifname_verified[IFNAMSIZ];
|
|
const char *path;
|
|
|
|
if (!category || !option)
|
|
return FALSE;
|
|
|
|
dirfd = nm_platform_sysctl_open_netdir (self, ifindex, ifname_verified);
|
|
if (dirfd < 0)
|
|
return FALSE;
|
|
|
|
path = nm_sprintf_bufa (strlen (category) + strlen (option) + 2,
|
|
"%s/%s",
|
|
category, option);
|
|
return nm_platform_sysctl_set (self, NMP_SYSCTL_PATHID_NETDIR_unsafe (dirfd, ifname_verified, path), value);
|
|
}
|
|
|
|
static char *
|
|
link_get_option (NMPlatform *self, int ifindex, const char *category, const char *option)
|
|
{
|
|
nm_auto_close int dirfd = -1;
|
|
char ifname_verified[IFNAMSIZ];
|
|
const char *path;
|
|
|
|
if (!category || !option)
|
|
return NULL;
|
|
|
|
dirfd = nm_platform_sysctl_open_netdir (self, ifindex, ifname_verified);
|
|
if (dirfd < 0)
|
|
return NULL;
|
|
|
|
path = nm_sprintf_bufa (strlen (category) + strlen (option) + 2,
|
|
"%s/%s",
|
|
category, option);
|
|
return nm_platform_sysctl_get (self, NMP_SYSCTL_PATHID_NETDIR_unsafe (dirfd, ifname_verified, path));
|
|
}
|
|
|
|
static const char *
|
|
master_category (NMPlatform *self, int master)
|
|
{
|
|
switch (nm_platform_link_get_type (self, master)) {
|
|
case NM_LINK_TYPE_BRIDGE:
|
|
return "bridge";
|
|
case NM_LINK_TYPE_BOND:
|
|
return "bonding";
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static const char *
|
|
slave_category (NMPlatform *self, int slave)
|
|
{
|
|
int master = nm_platform_link_get_master (self, slave);
|
|
|
|
if (master <= 0)
|
|
return NULL;
|
|
|
|
switch (nm_platform_link_get_type (self, master)) {
|
|
case NM_LINK_TYPE_BRIDGE:
|
|
return "brport";
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_sysctl_master_set_option (NMPlatform *self, int ifindex, const char *option, const char *value)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex > 0, FALSE);
|
|
g_return_val_if_fail (option, FALSE);
|
|
g_return_val_if_fail (value, FALSE);
|
|
|
|
return link_set_option (self, ifindex, master_category (self, ifindex), option, value);
|
|
}
|
|
|
|
char *
|
|
nm_platform_sysctl_master_get_option (NMPlatform *self, int ifindex, const char *option)
|
|
{
|
|
_CHECK_SELF (self, klass, NULL);
|
|
|
|
g_return_val_if_fail (ifindex > 0, FALSE);
|
|
g_return_val_if_fail (option, FALSE);
|
|
|
|
return link_get_option (self, ifindex, master_category (self, ifindex), option);
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_sysctl_slave_set_option (NMPlatform *self, int ifindex, const char *option, const char *value)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex > 0, FALSE);
|
|
g_return_val_if_fail (option, FALSE);
|
|
g_return_val_if_fail (value, FALSE);
|
|
|
|
return link_set_option (self, ifindex, slave_category (self, ifindex), option, value);
|
|
}
|
|
|
|
char *
|
|
nm_platform_sysctl_slave_get_option (NMPlatform *self, int ifindex, const char *option)
|
|
{
|
|
_CHECK_SELF (self, klass, NULL);
|
|
|
|
g_return_val_if_fail (ifindex > 0, FALSE);
|
|
g_return_val_if_fail (option, FALSE);
|
|
|
|
return link_get_option (self, ifindex, slave_category (self, ifindex), option);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
gboolean
|
|
nm_platform_link_vlan_change (NMPlatform *self,
|
|
int ifindex,
|
|
NMVlanFlags flags_mask,
|
|
NMVlanFlags flags_set,
|
|
gboolean ingress_reset_all,
|
|
const NMVlanQosMapping *ingress_map,
|
|
gsize n_ingress_map,
|
|
gboolean egress_reset_all,
|
|
const NMVlanQosMapping *egress_map,
|
|
gsize n_egress_map)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
nm_assert (klass->link_vlan_change);
|
|
|
|
g_return_val_if_fail (!n_ingress_map || ingress_map, FALSE);
|
|
g_return_val_if_fail (!n_egress_map || egress_map, FALSE);
|
|
|
|
flags_set &= flags_mask;
|
|
|
|
if (_LOGD_ENABLED ()) {
|
|
char buf[512];
|
|
char *b = buf;
|
|
gsize len, i;
|
|
|
|
b[0] = '\0';
|
|
len = sizeof (buf);
|
|
|
|
if (flags_mask)
|
|
nm_utils_strbuf_append (&b, &len, " flags 0x%x/0x%x", (unsigned) flags_set, (unsigned) flags_mask);
|
|
|
|
if (ingress_reset_all || n_ingress_map) {
|
|
nm_utils_strbuf_append_str (&b, &len, " ingress-qos-map");
|
|
nm_platform_vlan_qos_mapping_to_string ("", ingress_map, n_ingress_map, b, len);
|
|
i = strlen (b);
|
|
b += i;
|
|
len -= i;
|
|
if (ingress_reset_all)
|
|
nm_utils_strbuf_append_str (&b, &len, " (reset-all)");
|
|
}
|
|
|
|
if (egress_reset_all || n_egress_map) {
|
|
nm_utils_strbuf_append_str (&b, &len, " egress-qos-map");
|
|
nm_platform_vlan_qos_mapping_to_string ("", egress_map, n_egress_map, b, len);
|
|
i = strlen (b);
|
|
b += i;
|
|
len -= i;
|
|
if (egress_reset_all)
|
|
nm_utils_strbuf_append_str (&b, &len, " (reset-all)");
|
|
}
|
|
|
|
_LOGD ("link: change vlan %d:%s", ifindex, buf);
|
|
}
|
|
return klass->link_vlan_change (self,
|
|
ifindex,
|
|
flags_mask,
|
|
flags_set,
|
|
ingress_reset_all,
|
|
ingress_map,
|
|
n_ingress_map,
|
|
egress_reset_all,
|
|
egress_map,
|
|
n_egress_map);
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_link_vlan_set_ingress_map (NMPlatform *self, int ifindex, int from, int to)
|
|
{
|
|
NMVlanQosMapping map = {
|
|
.from = from,
|
|
.to = to,
|
|
};
|
|
|
|
return nm_platform_link_vlan_change (self, ifindex, 0, 0, FALSE, &map, 1, FALSE, NULL, 0);
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_link_vlan_set_egress_map (NMPlatform *self, int ifindex, int from, int to)
|
|
{
|
|
NMVlanQosMapping map = {
|
|
.from = from,
|
|
.to = to,
|
|
};
|
|
|
|
return nm_platform_link_vlan_change (self, ifindex, 0, 0, FALSE, NULL, 0, FALSE, &map, 1);
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_gre_add:
|
|
* @self: platform instance
|
|
* @name: name of the new interface
|
|
* @props: interface properties
|
|
* @out_link: on success, the link object
|
|
*
|
|
* Create a software GRE device.
|
|
*/
|
|
NMPlatformError
|
|
nm_platform_link_gre_add (NMPlatform *self,
|
|
const char *name,
|
|
const NMPlatformLnkGre *props,
|
|
const NMPlatformLink **out_link)
|
|
{
|
|
NMPlatformError plerr;
|
|
char buffer[INET_ADDRSTRLEN];
|
|
|
|
_CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG);
|
|
|
|
g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG);
|
|
g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG);
|
|
|
|
plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_GRE, out_link);
|
|
if (plerr != NM_PLATFORM_ERROR_SUCCESS)
|
|
return plerr;
|
|
|
|
_LOGD (LOG_FMT_IP_TUNNEL,
|
|
"gre",
|
|
name,
|
|
props->parent_ifindex,
|
|
nm_utils_inet4_ntop (props->local, NULL),
|
|
nm_utils_inet4_ntop (props->remote, buffer));
|
|
|
|
if (!klass->link_gre_add (self, name, props, out_link))
|
|
return NM_PLATFORM_ERROR_UNSPECIFIED;
|
|
return NM_PLATFORM_ERROR_SUCCESS;
|
|
}
|
|
|
|
static NMPlatformError
|
|
_infiniband_add_add_or_delete (NMPlatform *self,
|
|
int parent,
|
|
int p_key,
|
|
gboolean add,
|
|
const NMPlatformLink **out_link)
|
|
{
|
|
char name[IFNAMSIZ];
|
|
const NMPlatformLink *parent_link;
|
|
NMPlatformError plerr;
|
|
|
|
_CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG);
|
|
|
|
g_return_val_if_fail (parent >= 0, NM_PLATFORM_ERROR_BUG);
|
|
g_return_val_if_fail (p_key >= 0 && p_key <= 0xffff, NM_PLATFORM_ERROR_BUG);
|
|
|
|
/* the special keys 0x0000 and 0x8000 are not allowed. */
|
|
if (NM_IN_SET (p_key, 0, 0x8000))
|
|
return NM_PLATFORM_ERROR_UNSPECIFIED;
|
|
|
|
parent_link = nm_platform_link_get (self, parent);
|
|
if (!parent_link)
|
|
return NM_PLATFORM_ERROR_NOT_FOUND;
|
|
|
|
if (parent_link->type != NM_LINK_TYPE_INFINIBAND)
|
|
return NM_PLATFORM_ERROR_WRONG_TYPE;
|
|
|
|
nm_utils_new_infiniband_name (name, parent_link->name, p_key);
|
|
|
|
if (add) {
|
|
plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_INFINIBAND, out_link);
|
|
if (plerr != NM_PLATFORM_ERROR_SUCCESS)
|
|
return plerr;
|
|
|
|
_LOGD ("link: adding infiniband partition %s for parent '%s' (%d), key %d",
|
|
name, parent_link->name, parent, p_key);
|
|
if (!klass->infiniband_partition_add (self, parent, p_key, out_link))
|
|
return NM_PLATFORM_ERROR_UNSPECIFIED;
|
|
} else {
|
|
if (!klass->infiniband_partition_delete (self, parent, p_key))
|
|
return NM_PLATFORM_ERROR_UNSPECIFIED;
|
|
}
|
|
|
|
return NM_PLATFORM_ERROR_SUCCESS;
|
|
}
|
|
|
|
NMPlatformError
|
|
nm_platform_link_infiniband_add (NMPlatform *self,
|
|
int parent,
|
|
int p_key,
|
|
const NMPlatformLink **out_link)
|
|
{
|
|
return _infiniband_add_add_or_delete (self, parent, p_key, TRUE, out_link);
|
|
}
|
|
|
|
NMPlatformError
|
|
nm_platform_link_infiniband_delete (NMPlatform *self,
|
|
int parent,
|
|
int p_key)
|
|
{
|
|
return _infiniband_add_add_or_delete (self, parent, p_key, FALSE, NULL);
|
|
}
|
|
|
|
|
|
gboolean
|
|
nm_platform_link_infiniband_get_properties (NMPlatform *self,
|
|
int ifindex,
|
|
int *out_parent,
|
|
int *out_p_key,
|
|
const char **out_mode)
|
|
{
|
|
nm_auto_close int dirfd = -1;
|
|
char ifname_verified[IFNAMSIZ];
|
|
const NMPlatformLnkInfiniband *plnk;
|
|
const NMPlatformLink *plink;
|
|
char *contents;
|
|
const char *mode;
|
|
int p_key = 0;
|
|
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex > 0, FALSE);
|
|
|
|
plnk = nm_platform_link_get_lnk_infiniband (self, ifindex, &plink);
|
|
|
|
if ( !plink
|
|
|| plink->type != NM_LINK_TYPE_INFINIBAND)
|
|
return FALSE;
|
|
|
|
if (plnk) {
|
|
NM_SET_OUT (out_parent, plink->parent);
|
|
NM_SET_OUT (out_p_key, plnk->p_key);
|
|
NM_SET_OUT (out_mode, plnk->mode);
|
|
return TRUE;
|
|
}
|
|
|
|
/* Could not get the link information via netlink. To support older kernels,
|
|
* fallback to reading sysfs. */
|
|
|
|
dirfd = nm_platform_sysctl_open_netdir (self, ifindex, ifname_verified);
|
|
if (dirfd < 0)
|
|
return FALSE;
|
|
|
|
contents = nm_platform_sysctl_get (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname_verified, "mode"));
|
|
if (!contents)
|
|
return FALSE;
|
|
if (strstr (contents, "datagram"))
|
|
mode = "datagram";
|
|
else if (strstr (contents, "connected"))
|
|
mode = "connected";
|
|
else
|
|
mode = NULL;
|
|
g_free (contents);
|
|
|
|
p_key = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname_verified, "pkey"), 16, 0, 0xFFFF, -1);
|
|
if (p_key < 0)
|
|
return FALSE;
|
|
|
|
NM_SET_OUT (out_parent, plink->parent);
|
|
NM_SET_OUT (out_p_key, p_key);
|
|
NM_SET_OUT (out_mode, mode);
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_ip6tnl_add:
|
|
* @self: platform instance
|
|
* @name: name of the new interface
|
|
* @props: interface properties
|
|
* @out_link: on success, the link object
|
|
*
|
|
* Create an IPv6 tunnel.
|
|
*/
|
|
NMPlatformError
|
|
nm_platform_link_ip6tnl_add (NMPlatform *self,
|
|
const char *name,
|
|
const NMPlatformLnkIp6Tnl *props,
|
|
const NMPlatformLink **out_link)
|
|
{
|
|
NMPlatformError plerr;
|
|
char buffer[INET6_ADDRSTRLEN];
|
|
|
|
_CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG);
|
|
|
|
g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG);
|
|
g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG);
|
|
|
|
plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_IP6TNL, out_link);
|
|
if (plerr != NM_PLATFORM_ERROR_SUCCESS)
|
|
return plerr;
|
|
|
|
_LOGD (LOG_FMT_IP_TUNNEL,
|
|
"ip6tnl",
|
|
name,
|
|
props->parent_ifindex,
|
|
nm_utils_inet6_ntop (&props->local, NULL),
|
|
nm_utils_inet6_ntop (&props->remote, buffer));
|
|
|
|
if (!klass->link_ip6tnl_add (self, name, props, out_link))
|
|
return NM_PLATFORM_ERROR_UNSPECIFIED;
|
|
return NM_PLATFORM_ERROR_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_ipip_add:
|
|
* @self: platform instance
|
|
* @name: name of the new interface
|
|
* @props: interface properties
|
|
* @out_link: on success, the link object
|
|
*
|
|
* Create an IPIP tunnel.
|
|
*/
|
|
NMPlatformError
|
|
nm_platform_link_ipip_add (NMPlatform *self,
|
|
const char *name,
|
|
const NMPlatformLnkIpIp *props,
|
|
const NMPlatformLink **out_link)
|
|
{
|
|
NMPlatformError plerr;
|
|
char buffer[INET_ADDRSTRLEN];
|
|
|
|
_CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG);
|
|
|
|
g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG);
|
|
g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG);
|
|
|
|
plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_IPIP, out_link);
|
|
if (plerr != NM_PLATFORM_ERROR_SUCCESS)
|
|
return plerr;
|
|
|
|
_LOGD (LOG_FMT_IP_TUNNEL,
|
|
"ipip",
|
|
name,
|
|
props->parent_ifindex,
|
|
nm_utils_inet4_ntop (props->local, NULL),
|
|
nm_utils_inet4_ntop (props->remote, buffer));
|
|
|
|
if (!klass->link_ipip_add (self, name, props, out_link))
|
|
return NM_PLATFORM_ERROR_UNSPECIFIED;
|
|
return NM_PLATFORM_ERROR_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_macsec_add:
|
|
* @self: platform instance
|
|
* @name: name of the new interface
|
|
* @props: interface properties
|
|
* @out_link: on success, the link object
|
|
*
|
|
* Create a MACsec interface.
|
|
*/
|
|
NMPlatformError
|
|
nm_platform_link_macsec_add (NMPlatform *self,
|
|
const char *name,
|
|
int parent,
|
|
const NMPlatformLnkMacsec *props,
|
|
const NMPlatformLink **out_link)
|
|
{
|
|
NMPlatformError plerr;
|
|
|
|
_CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG);
|
|
|
|
g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG);
|
|
g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG);
|
|
|
|
plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_MACSEC, out_link);
|
|
if (plerr != NM_PLATFORM_ERROR_SUCCESS)
|
|
return plerr;
|
|
|
|
_LOGD ("adding macsec '%s' parent %u sci %llx",
|
|
name,
|
|
parent,
|
|
(unsigned long long) props->sci);
|
|
|
|
if (!klass->link_macsec_add (self, name, parent, props, out_link))
|
|
return NM_PLATFORM_ERROR_UNSPECIFIED;
|
|
return NM_PLATFORM_ERROR_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_macvlan_add:
|
|
* @self: platform instance
|
|
* @name: name of the new interface
|
|
* @props: interface properties
|
|
* @out_link: on success, the link object
|
|
*
|
|
* Create a MACVLAN or MACVTAP device.
|
|
*/
|
|
NMPlatformError
|
|
nm_platform_link_macvlan_add (NMPlatform *self,
|
|
const char *name,
|
|
int parent,
|
|
const NMPlatformLnkMacvlan *props,
|
|
const NMPlatformLink **out_link)
|
|
{
|
|
NMPlatformError plerr;
|
|
NMLinkType type;
|
|
|
|
_CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG);
|
|
|
|
g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG);
|
|
g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG);
|
|
|
|
type = props->tap ? NM_LINK_TYPE_MACVTAP : NM_LINK_TYPE_MACVLAN;
|
|
|
|
plerr = _link_add_check_existing (self, name, type, out_link);
|
|
if (plerr != NM_PLATFORM_ERROR_SUCCESS)
|
|
return plerr;
|
|
|
|
_LOGD ("adding %s '%s' parent %u mode %u",
|
|
props->tap ? "macvtap" : "macvlan",
|
|
name,
|
|
parent,
|
|
props->mode);
|
|
|
|
if (!klass->link_macvlan_add (self, name, parent, props, out_link))
|
|
return NM_PLATFORM_ERROR_UNSPECIFIED;
|
|
return NM_PLATFORM_ERROR_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_sit_add:
|
|
* @self: platform instance
|
|
* @name: name of the new interface
|
|
* @props: interface properties
|
|
* @out_link: on success, the link object
|
|
*
|
|
* Create a software SIT device.
|
|
*/
|
|
NMPlatformError
|
|
nm_platform_link_sit_add (NMPlatform *self,
|
|
const char *name,
|
|
const NMPlatformLnkSit *props,
|
|
const NMPlatformLink **out_link)
|
|
{
|
|
NMPlatformError plerr;
|
|
char buffer[INET_ADDRSTRLEN];
|
|
|
|
_CHECK_SELF (self, klass, NM_PLATFORM_ERROR_BUG);
|
|
|
|
g_return_val_if_fail (props, NM_PLATFORM_ERROR_BUG);
|
|
g_return_val_if_fail (name, NM_PLATFORM_ERROR_BUG);
|
|
|
|
plerr = _link_add_check_existing (self, name, NM_LINK_TYPE_SIT, out_link);
|
|
if (plerr != NM_PLATFORM_ERROR_SUCCESS)
|
|
return plerr;
|
|
|
|
_LOGD (LOG_FMT_IP_TUNNEL,
|
|
"sit",
|
|
name,
|
|
props->parent_ifindex,
|
|
nm_utils_inet4_ntop (props->local, NULL),
|
|
nm_utils_inet4_ntop (props->remote, buffer));
|
|
|
|
if (!klass->link_sit_add (self, name, props, out_link))
|
|
return NM_PLATFORM_ERROR_UNSPECIFIED;
|
|
return NM_PLATFORM_ERROR_SUCCESS;
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_link_veth_get_properties (NMPlatform *self, int ifindex, int *out_peer_ifindex)
|
|
{
|
|
const NMPlatformLink *plink;
|
|
int peer_ifindex;
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
plink = nm_platform_link_get (self, ifindex);
|
|
|
|
if (!plink)
|
|
return FALSE;
|
|
if (plink->type != NM_LINK_TYPE_VETH)
|
|
return FALSE;
|
|
|
|
if (plink->parent != 0) {
|
|
NM_SET_OUT (out_peer_ifindex, plink->parent);
|
|
return TRUE;
|
|
}
|
|
|
|
/* Pre-4.1 kernel did not expose the peer_ifindex as IFA_LINK. Lookup via ethtool. */
|
|
if (out_peer_ifindex) {
|
|
nm_auto_pop_netns NMPNetns *netns = NULL;
|
|
|
|
if (!nm_platform_netns_push (self, &netns))
|
|
return FALSE;
|
|
peer_ifindex = nmp_utils_ethtool_get_peer_ifindex (plink->ifindex);
|
|
if (peer_ifindex <= 0)
|
|
return FALSE;
|
|
|
|
*out_peer_ifindex = peer_ifindex;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_link_tun_get_properties (NMPlatform *self, int ifindex, NMPlatformTunProperties *props)
|
|
{
|
|
nm_auto_close int dirfd = -1;
|
|
char ifname[IFNAMSIZ];
|
|
gint64 flags;
|
|
gboolean success = TRUE;
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex > 0, FALSE);
|
|
g_return_val_if_fail (props, FALSE);
|
|
|
|
memset (props, 0, sizeof (*props));
|
|
props->owner = -1;
|
|
props->group = -1;
|
|
|
|
dirfd = nm_platform_sysctl_open_netdir (self, ifindex, ifname);
|
|
if (dirfd < 0)
|
|
return FALSE;
|
|
|
|
props->owner = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "owner"), 10, -1, G_MAXINT64, -1);
|
|
if (errno)
|
|
success = FALSE;
|
|
|
|
props->group = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "group"), 10, -1, G_MAXINT64, -1);
|
|
if (errno)
|
|
success = FALSE;
|
|
|
|
flags = nm_platform_sysctl_get_int_checked (self, NMP_SYSCTL_PATHID_NETDIR (dirfd, ifname, "tun_flags"), 16, 0, G_MAXINT64, -1);
|
|
if (flags >= 0) {
|
|
props->mode = ((flags & (IFF_TUN | IFF_TAP)) == IFF_TUN) ? "tun" : "tap";
|
|
props->no_pi = !!(flags & IFF_NO_PI);
|
|
props->vnet_hdr = !!(flags & IFF_VNET_HDR);
|
|
props->multi_queue = !!(flags & NM_IFF_MULTI_QUEUE);
|
|
} else
|
|
success = FALSE;
|
|
|
|
return success;
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_wifi_get_capabilities (NMPlatform *self, int ifindex, NMDeviceWifiCapabilities *caps)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex > 0, FALSE);
|
|
|
|
return klass->wifi_get_capabilities (self, ifindex, caps);
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_wifi_get_bssid (NMPlatform *self, int ifindex, guint8 *bssid)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex > 0, FALSE);
|
|
|
|
return klass->wifi_get_bssid (self, ifindex, bssid);
|
|
}
|
|
|
|
guint32
|
|
nm_platform_wifi_get_frequency (NMPlatform *self, int ifindex)
|
|
{
|
|
_CHECK_SELF (self, klass, 0);
|
|
|
|
g_return_val_if_fail (ifindex > 0, 0);
|
|
|
|
return klass->wifi_get_frequency (self, ifindex);
|
|
}
|
|
|
|
int
|
|
nm_platform_wifi_get_quality (NMPlatform *self, int ifindex)
|
|
{
|
|
_CHECK_SELF (self, klass, 0);
|
|
|
|
g_return_val_if_fail (ifindex > 0, 0);
|
|
|
|
return klass->wifi_get_quality (self, ifindex);
|
|
}
|
|
|
|
guint32
|
|
nm_platform_wifi_get_rate (NMPlatform *self, int ifindex)
|
|
{
|
|
_CHECK_SELF (self, klass, 0);
|
|
|
|
g_return_val_if_fail (ifindex > 0, 0);
|
|
|
|
return klass->wifi_get_rate (self, ifindex);
|
|
}
|
|
|
|
NM80211Mode
|
|
nm_platform_wifi_get_mode (NMPlatform *self, int ifindex)
|
|
{
|
|
_CHECK_SELF (self, klass, NM_802_11_MODE_UNKNOWN);
|
|
|
|
g_return_val_if_fail (ifindex > 0, NM_802_11_MODE_UNKNOWN);
|
|
|
|
return klass->wifi_get_mode (self, ifindex);
|
|
}
|
|
|
|
void
|
|
nm_platform_wifi_set_mode (NMPlatform *self, int ifindex, NM80211Mode mode)
|
|
{
|
|
_CHECK_SELF_VOID (self, klass);
|
|
|
|
g_return_if_fail (ifindex > 0);
|
|
|
|
klass->wifi_set_mode (self, ifindex, mode);
|
|
}
|
|
|
|
static void
|
|
wifi_set_powersave (NMPlatform *p, int ifindex, guint32 powersave)
|
|
{
|
|
/* empty */
|
|
}
|
|
|
|
void
|
|
nm_platform_wifi_set_powersave (NMPlatform *self, int ifindex, guint32 powersave)
|
|
{
|
|
_CHECK_SELF_VOID (self, klass);
|
|
|
|
g_return_if_fail (ifindex > 0);
|
|
|
|
klass->wifi_set_powersave (self, ifindex, powersave);
|
|
}
|
|
|
|
guint32
|
|
nm_platform_wifi_find_frequency (NMPlatform *self, int ifindex, const guint32 *freqs)
|
|
{
|
|
_CHECK_SELF (self, klass, 0);
|
|
|
|
g_return_val_if_fail (ifindex > 0, 0);
|
|
g_return_val_if_fail (freqs != NULL, 0);
|
|
|
|
return klass->wifi_find_frequency (self, ifindex, freqs);
|
|
}
|
|
|
|
void
|
|
nm_platform_wifi_indicate_addressing_running (NMPlatform *self, int ifindex, gboolean running)
|
|
{
|
|
_CHECK_SELF_VOID (self, klass);
|
|
|
|
g_return_if_fail (ifindex > 0);
|
|
|
|
klass->wifi_indicate_addressing_running (self, ifindex, running);
|
|
}
|
|
|
|
guint32
|
|
nm_platform_mesh_get_channel (NMPlatform *self, int ifindex)
|
|
{
|
|
_CHECK_SELF (self, klass, 0);
|
|
|
|
g_return_val_if_fail (ifindex > 0, 0);
|
|
|
|
return klass->mesh_get_channel (self, ifindex);
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_mesh_set_channel (NMPlatform *self, int ifindex, guint32 channel)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex > 0, FALSE);
|
|
|
|
return klass->mesh_set_channel (self, ifindex, channel);
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_mesh_set_ssid (NMPlatform *self, int ifindex, const guint8 *ssid, gsize len)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex > 0, FALSE);
|
|
g_return_val_if_fail (ssid != NULL, FALSE);
|
|
|
|
return klass->mesh_set_ssid (self, ifindex, ssid, len);
|
|
}
|
|
|
|
#define TO_STRING_DEV_BUF_SIZE (5+15+1)
|
|
static const char *
|
|
_to_string_dev (NMPlatform *self, int ifindex, char *buf, size_t size)
|
|
{
|
|
g_assert (buf && size >= TO_STRING_DEV_BUF_SIZE);
|
|
|
|
if (ifindex) {
|
|
const char *name = ifindex > 0 && self ? nm_platform_link_get_name (self, ifindex) : NULL;
|
|
char *buf2;
|
|
|
|
strcpy (buf, " dev ");
|
|
buf2 = buf + 5;
|
|
size -= 5;
|
|
|
|
if (name)
|
|
g_strlcpy (buf2, name, size);
|
|
else
|
|
g_snprintf (buf2, size, "%d", ifindex);
|
|
} else
|
|
buf[0] = 0;
|
|
|
|
return buf;
|
|
}
|
|
|
|
#define TO_STRING_IFA_FLAGS_BUF_SIZE 256
|
|
|
|
static const char *
|
|
_to_string_ifa_flags (guint32 ifa_flags, char *buf, gsize size)
|
|
{
|
|
#define S_FLAGS_PREFIX " flags "
|
|
nm_assert (buf && size >= TO_STRING_IFA_FLAGS_BUF_SIZE && size > NM_STRLEN (S_FLAGS_PREFIX));
|
|
|
|
if (!ifa_flags)
|
|
buf[0] = '\0';
|
|
else {
|
|
nm_platform_addr_flags2str (ifa_flags, &buf[NM_STRLEN (S_FLAGS_PREFIX)], size - NM_STRLEN (S_FLAGS_PREFIX));
|
|
if (buf[NM_STRLEN (S_FLAGS_PREFIX)] == '\0')
|
|
buf[0] = '\0';
|
|
else
|
|
memcpy (buf, S_FLAGS_PREFIX, NM_STRLEN (S_FLAGS_PREFIX));
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
gboolean
|
|
nm_platform_ethtool_set_wake_on_lan (NMPlatform *self, int ifindex, NMSettingWiredWakeOnLan wol, const char *wol_password)
|
|
{
|
|
_CHECK_SELF_NETNS (self, klass, netns, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex > 0, FALSE);
|
|
|
|
return nmp_utils_ethtool_set_wake_on_lan (ifindex, wol, wol_password);
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_ethtool_set_link_settings (NMPlatform *self, int ifindex, gboolean autoneg, guint32 speed, NMPlatformLinkDuplexType duplex)
|
|
{
|
|
_CHECK_SELF_NETNS (self, klass, netns, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex > 0, FALSE);
|
|
|
|
return nmp_utils_ethtool_set_link_settings (ifindex, autoneg, speed, duplex);
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_ethtool_get_link_settings (NMPlatform *self, int ifindex, gboolean *out_autoneg, guint32 *out_speed, NMPlatformLinkDuplexType *out_duplex)
|
|
{
|
|
_CHECK_SELF_NETNS (self, klass, netns, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex > 0, FALSE);
|
|
|
|
return nmp_utils_ethtool_get_link_settings (ifindex, out_autoneg, out_speed, out_duplex);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
const NMDedupMultiHeadEntry *
|
|
nm_platform_lookup_all (NMPlatform *platform,
|
|
NMPCacheIdType cache_id_type,
|
|
NMPObject *obj)
|
|
{
|
|
return nmp_cache_lookup_all (nm_platform_get_cache (platform),
|
|
cache_id_type,
|
|
obj);
|
|
}
|
|
|
|
const NMDedupMultiEntry *
|
|
nm_platform_lookup_entry (NMPlatform *platform,
|
|
NMPCacheIdType cache_id_type,
|
|
NMPObject *obj)
|
|
{
|
|
return nmp_cache_lookup_entry_with_idx_type (nm_platform_get_cache (platform),
|
|
cache_id_type,
|
|
obj);
|
|
}
|
|
|
|
const NMDedupMultiHeadEntry *
|
|
nm_platform_lookup (NMPlatform *self,
|
|
const NMPLookup *lookup)
|
|
{
|
|
return nmp_cache_lookup (nm_platform_get_cache (self),
|
|
lookup);
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_lookup_predicate_routes_skip_rtprot_kernel (const NMPObject *obj,
|
|
gpointer user_data)
|
|
{
|
|
nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE,
|
|
NMP_OBJECT_TYPE_IP6_ROUTE));
|
|
return obj->ip_route.rt_source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_lookup_clone:
|
|
* @self:
|
|
* @lookup:
|
|
* @predicate: if given, only objects for which @predicate returns %TRUE are included
|
|
* in the result.
|
|
* @user_data: user data for @predicate
|
|
*
|
|
* Returns the result of lookup in a GPtrArray. The result array contains
|
|
* references objects from the cache, it's destroy function will unref them.
|
|
*
|
|
* The user must unref the GPtrArray, which will also unref the NMPObject
|
|
* elements.
|
|
*
|
|
* The elements in the array *must* not be modified.
|
|
*
|
|
* Returns: the result of the lookup.
|
|
*/
|
|
GPtrArray *
|
|
nm_platform_lookup_clone (NMPlatform *self,
|
|
const NMPLookup *lookup,
|
|
gboolean (*predicate) (const NMPObject *obj, gpointer user_data),
|
|
gpointer user_data)
|
|
{
|
|
return nm_dedup_multi_objs_to_ptr_array_head (nm_platform_lookup (self, lookup),
|
|
(NMDedupMultiFcnSelectPredicate) predicate,
|
|
user_data);
|
|
}
|
|
|
|
void
|
|
nm_platform_ip4_address_set_addr (NMPlatformIP4Address *addr, in_addr_t address, guint8 plen)
|
|
{
|
|
nm_assert (plen <= 32);
|
|
|
|
addr->address = address;
|
|
addr->peer_address = address;
|
|
addr->plen = plen;
|
|
}
|
|
|
|
const struct in6_addr *
|
|
nm_platform_ip6_address_get_peer (const NMPlatformIP6Address *addr)
|
|
{
|
|
if ( IN6_IS_ADDR_UNSPECIFIED (&addr->peer_address)
|
|
|| IN6_ARE_ADDR_EQUAL (&addr->peer_address, &addr->address))
|
|
return &addr->address;
|
|
return &addr->peer_address;
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_ip4_address_add (NMPlatform *self,
|
|
int ifindex,
|
|
in_addr_t address,
|
|
guint8 plen,
|
|
in_addr_t peer_address,
|
|
guint32 lifetime,
|
|
guint32 preferred,
|
|
guint32 flags,
|
|
const char *label)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex > 0, FALSE);
|
|
g_return_val_if_fail (plen <= 32, FALSE);
|
|
g_return_val_if_fail (lifetime > 0, FALSE);
|
|
g_return_val_if_fail (preferred <= lifetime, FALSE);
|
|
g_return_val_if_fail (!label || strlen (label) < sizeof (((NMPlatformIP4Address *) NULL)->label), FALSE);
|
|
|
|
if (_LOGD_ENABLED ()) {
|
|
NMPlatformIP4Address addr = { 0 };
|
|
|
|
addr.ifindex = ifindex;
|
|
addr.address = address;
|
|
addr.peer_address = peer_address;
|
|
addr.plen = plen;
|
|
addr.timestamp = 0; /* set it at zero, which to_string will treat as *now* */
|
|
addr.lifetime = lifetime;
|
|
addr.preferred = preferred;
|
|
addr.n_ifa_flags = flags;
|
|
if (label)
|
|
g_strlcpy (addr.label, label, sizeof (addr.label));
|
|
|
|
_LOGD ("address: adding or updating IPv4 address: %s", nm_platform_ip4_address_to_string (&addr, NULL, 0));
|
|
}
|
|
return klass->ip4_address_add (self, ifindex, address, plen, peer_address, lifetime, preferred, flags, label);
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_ip6_address_add (NMPlatform *self,
|
|
int ifindex,
|
|
struct in6_addr address,
|
|
guint8 plen,
|
|
struct in6_addr peer_address,
|
|
guint32 lifetime,
|
|
guint32 preferred,
|
|
guint32 flags)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex > 0, FALSE);
|
|
g_return_val_if_fail (plen <= 128, FALSE);
|
|
g_return_val_if_fail (lifetime > 0, FALSE);
|
|
g_return_val_if_fail (preferred <= lifetime, FALSE);
|
|
|
|
if (_LOGD_ENABLED ()) {
|
|
NMPlatformIP6Address addr = { 0 };
|
|
|
|
addr.ifindex = ifindex;
|
|
addr.address = address;
|
|
addr.peer_address = peer_address;
|
|
addr.plen = plen;
|
|
addr.timestamp = 0; /* set it to zero, which to_string will treat as *now* */
|
|
addr.lifetime = lifetime;
|
|
addr.preferred = preferred;
|
|
addr.n_ifa_flags = flags;
|
|
|
|
_LOGD ("address: adding or updating IPv6 address: %s", nm_platform_ip6_address_to_string (&addr, NULL, 0));
|
|
}
|
|
return klass->ip6_address_add (self, ifindex, address, plen, peer_address, lifetime, preferred, flags);
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_ip4_address_delete (NMPlatform *self, int ifindex, in_addr_t address, guint8 plen, in_addr_t peer_address)
|
|
{
|
|
char str_dev[TO_STRING_DEV_BUF_SIZE];
|
|
char str_peer2[NM_UTILS_INET_ADDRSTRLEN];
|
|
char str_peer[100];
|
|
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex > 0, FALSE);
|
|
g_return_val_if_fail (plen <= 32, FALSE);
|
|
|
|
_LOGD ("address: deleting IPv4 address %s/%d, %sifindex %d%s",
|
|
nm_utils_inet4_ntop (address, NULL), plen,
|
|
peer_address != address
|
|
? nm_sprintf_buf (str_peer, "peer %s, ", nm_utils_inet4_ntop (peer_address, str_peer2)) : "",
|
|
ifindex,
|
|
_to_string_dev (self, ifindex, str_dev, sizeof (str_dev)));
|
|
return klass->ip4_address_delete (self, ifindex, address, plen, peer_address);
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_ip6_address_delete (NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen)
|
|
{
|
|
char str_dev[TO_STRING_DEV_BUF_SIZE];
|
|
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (ifindex > 0, FALSE);
|
|
g_return_val_if_fail (plen <= 128, FALSE);
|
|
|
|
_LOGD ("address: deleting IPv6 address %s/%d, ifindex %d%s",
|
|
nm_utils_inet6_ntop (&address, NULL), plen, ifindex,
|
|
_to_string_dev (self, ifindex, str_dev, sizeof (str_dev)));
|
|
return klass->ip6_address_delete (self, ifindex, address, plen);
|
|
}
|
|
|
|
const NMPlatformIP4Address *
|
|
nm_platform_ip4_address_get (NMPlatform *self, int ifindex, in_addr_t address, guint8 plen, guint32 peer_address)
|
|
{
|
|
NMPObject obj_id;
|
|
const NMPObject *obj;
|
|
|
|
_CHECK_SELF (self, klass, NULL);
|
|
|
|
g_return_val_if_fail (plen <= 32, NULL);
|
|
|
|
nmp_object_stackinit_id_ip4_address (&obj_id, ifindex, address, plen, peer_address);
|
|
obj = nmp_cache_lookup_obj (nm_platform_get_cache (self), &obj_id);
|
|
nm_assert (!obj || nmp_object_is_visible (obj));
|
|
return NMP_OBJECT_CAST_IP4_ADDRESS (obj);
|
|
}
|
|
|
|
const NMPlatformIP6Address *
|
|
nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr address)
|
|
{
|
|
NMPObject obj_id;
|
|
const NMPObject *obj;
|
|
|
|
_CHECK_SELF (self, klass, NULL);
|
|
|
|
nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &address);
|
|
obj = nmp_cache_lookup_obj (nm_platform_get_cache (self), &obj_id);
|
|
nm_assert (!obj || nmp_object_is_visible (obj));
|
|
return NMP_OBJECT_CAST_IP6_ADDRESS (obj);
|
|
}
|
|
|
|
static gboolean
|
|
array_contains_ip6_address (const GPtrArray *addresses, const NMPlatformIP6Address *address, gint32 now)
|
|
{
|
|
guint len = addresses ? addresses->len : 0;
|
|
guint i;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
NMPlatformIP6Address *candidate = NMP_OBJECT_CAST_IP6_ADDRESS (addresses->pdata[i]);
|
|
|
|
if (IN6_ARE_ADDR_EQUAL (&candidate->address, &address->address) && candidate->plen == address->plen) {
|
|
guint32 lifetime, preferred;
|
|
|
|
if (nm_utils_lifetime_get (candidate->timestamp, candidate->lifetime, candidate->preferred,
|
|
now, &lifetime, &preferred))
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
ip4_addr_subnets_is_plain_address (const GPtrArray *addresses, gconstpointer needle)
|
|
{
|
|
return needle >= (gconstpointer) &addresses->pdata[0]
|
|
&& needle < (gconstpointer) &addresses->pdata[addresses->len];
|
|
}
|
|
|
|
static const NMPObject **
|
|
ip4_addr_subnets_addr_list_get (const GPtrArray *addr_list, guint idx)
|
|
{
|
|
nm_assert (addr_list);
|
|
nm_assert (addr_list->len > 1);
|
|
nm_assert (idx < addr_list->len);
|
|
nm_assert (addr_list->pdata[idx]);
|
|
nm_assert ( !(*((gpointer *) addr_list->pdata[idx]))
|
|
|| NMP_OBJECT_CAST_IP4_ADDRESS (*((gpointer *) addr_list->pdata[idx])));
|
|
nm_assert (idx == 0 || ip4_addr_subnets_addr_list_get (addr_list, idx - 1));
|
|
return addr_list->pdata[idx];
|
|
}
|
|
|
|
static void
|
|
ip4_addr_subnets_destroy_index (GHashTable *subnets, const GPtrArray *addresses)
|
|
{
|
|
GHashTableIter iter;
|
|
gpointer p;
|
|
|
|
if (!subnets)
|
|
return;
|
|
|
|
g_hash_table_iter_init (&iter, subnets);
|
|
while (g_hash_table_iter_next (&iter, NULL, &p)) {
|
|
if (!ip4_addr_subnets_is_plain_address (addresses, p))
|
|
g_ptr_array_free ((GPtrArray *) p, TRUE);
|
|
}
|
|
|
|
g_hash_table_unref (subnets);
|
|
}
|
|
|
|
static GHashTable *
|
|
ip4_addr_subnets_build_index (const GPtrArray *addresses,
|
|
gboolean consider_flags,
|
|
gboolean full_index)
|
|
{
|
|
GHashTable *subnets;
|
|
guint i;
|
|
|
|
nm_assert (addresses && addresses->len);
|
|
|
|
subnets = g_hash_table_new (NULL, NULL);
|
|
|
|
/* Build a hash table of all addresses per subnet */
|
|
for (i = 0; i < addresses->len; i++) {
|
|
const NMPlatformIP4Address *address;
|
|
gpointer p_address;
|
|
GPtrArray *addr_list;
|
|
guint32 net;
|
|
int position;
|
|
gpointer p;
|
|
|
|
if (!addresses->pdata[i])
|
|
continue;
|
|
|
|
p_address = &addresses->pdata[i];
|
|
address = NMP_OBJECT_CAST_IP4_ADDRESS (addresses->pdata[i]);
|
|
|
|
net = address->address & nm_utils_ip4_prefix_to_netmask (address->plen);
|
|
if (!g_hash_table_lookup_extended (subnets, GUINT_TO_POINTER (net), NULL, &p)) {
|
|
g_hash_table_insert (subnets, GUINT_TO_POINTER (net), p_address);
|
|
continue;
|
|
}
|
|
nm_assert (p);
|
|
|
|
if (full_index) {
|
|
if (ip4_addr_subnets_is_plain_address (addresses, p)) {
|
|
addr_list = g_ptr_array_new ();
|
|
g_hash_table_insert (subnets, GUINT_TO_POINTER (net), addr_list);
|
|
g_ptr_array_add (addr_list, p);
|
|
} else
|
|
addr_list = p;
|
|
|
|
if ( !consider_flags
|
|
|| NM_FLAGS_HAS (address->n_ifa_flags, IFA_F_SECONDARY))
|
|
position = -1; /* append */
|
|
else
|
|
position = 0; /* prepend */
|
|
g_ptr_array_insert (addr_list, position, p_address);
|
|
} else {
|
|
/* we only care about the primary. No need to track the secondaries
|
|
* as a GPtrArray. */
|
|
nm_assert (ip4_addr_subnets_is_plain_address (addresses, p));
|
|
if ( consider_flags
|
|
&& !NM_FLAGS_HAS (address->n_ifa_flags, IFA_F_SECONDARY)) {
|
|
g_hash_table_insert (subnets, GUINT_TO_POINTER (net), p_address);
|
|
}
|
|
}
|
|
}
|
|
|
|
return subnets;
|
|
}
|
|
|
|
/**
|
|
* ip4_addr_subnets_is_secondary:
|
|
* @address: an address
|
|
* @subnets: the hash table mapping subnets to addresses
|
|
* @addresses: array of addresses in the hash table
|
|
* @out_addr_list: array of addresses belonging to the same subnet
|
|
*
|
|
* Checks whether @address is secondary and returns in @out_addr_list the list of addresses
|
|
* belonging to the same subnet, if it contains other elements.
|
|
*
|
|
* Returns: %TRUE if the address is secondary, %FALSE otherwise
|
|
*/
|
|
static gboolean
|
|
ip4_addr_subnets_is_secondary (const NMPObject *address,
|
|
GHashTable *subnets,
|
|
const GPtrArray *addresses,
|
|
const GPtrArray **out_addr_list)
|
|
{
|
|
const NMPlatformIP4Address *a;
|
|
const GPtrArray *addr_list;
|
|
gconstpointer p;
|
|
guint32 net;
|
|
const NMPObject **o;
|
|
|
|
a = NMP_OBJECT_CAST_IP4_ADDRESS (address);
|
|
|
|
net = a->address & nm_utils_ip4_prefix_to_netmask (a->plen);
|
|
p = g_hash_table_lookup (subnets, GUINT_TO_POINTER (net));
|
|
nm_assert (p);
|
|
if (!ip4_addr_subnets_is_plain_address (addresses, p)) {
|
|
addr_list = p;
|
|
nm_assert (addr_list->len > 1);
|
|
NM_SET_OUT (out_addr_list, addr_list);
|
|
o = ip4_addr_subnets_addr_list_get (addr_list, 0);
|
|
nm_assert (o && *o);
|
|
if (*o != address)
|
|
return TRUE;
|
|
} else {
|
|
NM_SET_OUT (out_addr_list, NULL);
|
|
return address != *((gconstpointer *) p);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_ip4_address_sync:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index
|
|
* @known_addresses: List of addresses. The list will be modified and only
|
|
* addresses that were successfully added will be kept in the list.
|
|
* That means, expired addresses and addresses that could not be added
|
|
* will be dropped.
|
|
* Hence, the input argument @known_addresses is also an output argument
|
|
* telling which addresses were succesfully added.
|
|
* Addresses are removed by unrefing the instance via nmp_object_unref()
|
|
* and leaving a NULL tombstone.
|
|
*
|
|
* A convenience function to synchronize addresses for a specific interface
|
|
* with the least possible disturbance. It simply removes addresses that are
|
|
* not listed and adds addresses that are.
|
|
*
|
|
* Returns: %TRUE on success.
|
|
*/
|
|
gboolean
|
|
nm_platform_ip4_address_sync (NMPlatform *self,
|
|
int ifindex,
|
|
GPtrArray *known_addresses)
|
|
{
|
|
gs_unref_ptrarray GPtrArray *plat_addresses = NULL;
|
|
const NMPlatformIP4Address *known_address;
|
|
gint32 now = nm_utils_get_monotonic_timestamp_s ();
|
|
GHashTable *plat_subnets = NULL;
|
|
GHashTable *known_subnets = NULL;
|
|
gs_unref_hashtable GHashTable *known_addresses_idx = NULL;
|
|
guint i, j, len;
|
|
NMPLookup lookup;
|
|
guint32 lifetime, preferred;
|
|
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
if (known_addresses) {
|
|
/* remove all addresses that are already expired. */
|
|
for (i = 0; i < known_addresses->len; i++) {
|
|
const NMPObject *o;
|
|
|
|
o = known_addresses->pdata[i];
|
|
nm_assert (o);
|
|
|
|
known_address = NMP_OBJECT_CAST_IP4_ADDRESS (known_addresses->pdata[i]);
|
|
|
|
if (!nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred,
|
|
now, &lifetime, &preferred))
|
|
goto delete_and_next;
|
|
|
|
if (G_UNLIKELY (!known_addresses_idx)) {
|
|
known_addresses_idx = g_hash_table_new ((GHashFunc) nmp_object_id_hash,
|
|
(GEqualFunc) nmp_object_id_equal);
|
|
}
|
|
if (!nm_g_hash_table_insert (known_addresses_idx, (gpointer) o, (gpointer) o)) {
|
|
/* duplicate? Keep only the first instance. */
|
|
goto delete_and_next;
|
|
}
|
|
|
|
continue;
|
|
delete_and_next:
|
|
nmp_object_unref (o);
|
|
known_addresses->pdata[i] = NULL;
|
|
}
|
|
|
|
if ( !known_addresses_idx
|
|
|| g_hash_table_size (known_addresses_idx) == 0)
|
|
known_addresses = NULL;
|
|
}
|
|
|
|
plat_addresses = nm_platform_lookup_clone (self,
|
|
nmp_lookup_init_addrroute (&lookup,
|
|
NMP_OBJECT_TYPE_IP4_ADDRESS,
|
|
ifindex),
|
|
NULL, NULL);
|
|
if (plat_addresses)
|
|
plat_subnets = ip4_addr_subnets_build_index (plat_addresses, TRUE, TRUE);
|
|
|
|
/* Delete unknown addresses */
|
|
len = plat_addresses ? plat_addresses->len : 0;
|
|
for (i = 0; i < len; i++) {
|
|
const NMPObject *plat_obj;
|
|
const NMPlatformIP4Address *plat_address;
|
|
const GPtrArray *addr_list;
|
|
|
|
plat_obj = plat_addresses->pdata[i];
|
|
if (!plat_obj) {
|
|
/* Already deleted */
|
|
continue;
|
|
}
|
|
|
|
plat_address = NMP_OBJECT_CAST_IP4_ADDRESS (plat_obj);
|
|
|
|
if (known_addresses) {
|
|
const NMPObject *o;
|
|
|
|
o = g_hash_table_lookup (known_addresses_idx, plat_obj);
|
|
if (o) {
|
|
gboolean secondary;
|
|
|
|
if (!known_subnets)
|
|
known_subnets = ip4_addr_subnets_build_index (known_addresses, FALSE, FALSE);
|
|
|
|
secondary = ip4_addr_subnets_is_secondary (o, known_subnets, known_addresses, NULL);
|
|
if (secondary == NM_FLAGS_HAS (plat_address->n_ifa_flags, IFA_F_SECONDARY)) {
|
|
/* if we have an existing known-address, with matching secondary role,
|
|
* do not delete the platform-address. */
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
nm_platform_ip4_address_delete (self, ifindex,
|
|
plat_address->address,
|
|
plat_address->plen,
|
|
plat_address->peer_address);
|
|
|
|
if ( !ip4_addr_subnets_is_secondary (plat_obj, plat_subnets, plat_addresses, &addr_list)
|
|
&& addr_list) {
|
|
/* If we just deleted a primary addresses and there were
|
|
* secondary ones the kernel can do two things, depending on
|
|
* version and sysctl setting: delete also secondary addresses
|
|
* or promote a secondary to primary. Ensure that secondary
|
|
* addresses are deleted, so that we can start with a clean
|
|
* slate and add addresses in the right order. */
|
|
for (j = 1; j < addr_list->len; j++) {
|
|
const NMPObject **o;
|
|
|
|
o = ip4_addr_subnets_addr_list_get (addr_list, j);
|
|
nm_assert (o);
|
|
|
|
if (*o) {
|
|
const NMPlatformIP4Address *a;
|
|
|
|
a = NMP_OBJECT_CAST_IP4_ADDRESS (*o);
|
|
nm_platform_ip4_address_delete (self, ifindex,
|
|
a->address,
|
|
a->plen,
|
|
a->peer_address);
|
|
nmp_object_unref (*o);
|
|
*o = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ip4_addr_subnets_destroy_index (plat_subnets, plat_addresses);
|
|
ip4_addr_subnets_destroy_index (known_subnets, known_addresses);
|
|
|
|
if (!known_addresses)
|
|
return TRUE;
|
|
|
|
/* Add missing addresses */
|
|
for (i = 0; i < known_addresses->len; i++) {
|
|
const NMPObject *o;
|
|
|
|
o = known_addresses->pdata[i];
|
|
if (!o)
|
|
continue;
|
|
|
|
known_address = NMP_OBJECT_CAST_IP4_ADDRESS (o);
|
|
|
|
if (!nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred,
|
|
now, &lifetime, &preferred))
|
|
goto delete_and_next2;
|
|
|
|
if (!nm_platform_ip4_address_add (self, ifindex, known_address->address, known_address->plen,
|
|
known_address->peer_address, lifetime, preferred,
|
|
0, known_address->label))
|
|
goto delete_and_next2;
|
|
|
|
continue;
|
|
delete_and_next2:
|
|
nmp_object_unref (o);
|
|
known_addresses->pdata[i] = NULL;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_ip6_address_sync:
|
|
* @self: platform instance
|
|
* @ifindex: Interface index
|
|
* @known_addresses: List of IPv6 addresses, as NMPObject. The list
|
|
* is not modified.
|
|
* @keep_link_local: Don't remove link-local address
|
|
*
|
|
* A convenience function to synchronize addresses for a specific interface
|
|
* with the least possible disturbance. It simply removes addresses that are
|
|
* not listed and adds addresses that are.
|
|
*
|
|
* Returns: %TRUE on success.
|
|
*/
|
|
gboolean
|
|
nm_platform_ip6_address_sync (NMPlatform *self,
|
|
int ifindex,
|
|
const GPtrArray *known_addresses,
|
|
gboolean keep_link_local)
|
|
{
|
|
gs_unref_ptrarray GPtrArray *plat_addresses = NULL;
|
|
NMPlatformIP6Address *address;
|
|
gint32 now = nm_utils_get_monotonic_timestamp_s ();
|
|
guint i;
|
|
NMPLookup lookup;
|
|
|
|
/* Delete unknown addresses */
|
|
plat_addresses = nm_platform_lookup_clone (self,
|
|
nmp_lookup_init_addrroute (&lookup,
|
|
NMP_OBJECT_TYPE_IP6_ADDRESS,
|
|
ifindex),
|
|
NULL, NULL);
|
|
if (plat_addresses) {
|
|
for (i = 0; i < plat_addresses->len; i++) {
|
|
address = NMP_OBJECT_CAST_IP6_ADDRESS (plat_addresses->pdata[i]);
|
|
|
|
/* Leave link local address management to the kernel */
|
|
if (keep_link_local && IN6_IS_ADDR_LINKLOCAL (&address->address))
|
|
continue;
|
|
|
|
if (!array_contains_ip6_address (known_addresses, address, now))
|
|
nm_platform_ip6_address_delete (self, ifindex, address->address, address->plen);
|
|
}
|
|
}
|
|
|
|
if (!known_addresses)
|
|
return TRUE;
|
|
|
|
/* Add missing addresses */
|
|
for (i = 0; i < known_addresses->len; i++) {
|
|
const NMPlatformIP6Address *known_address = NMP_OBJECT_CAST_IP6_ADDRESS (known_addresses->pdata[i]);
|
|
guint32 lifetime, preferred;
|
|
|
|
if (NM_FLAGS_HAS (known_address->n_ifa_flags, IFA_F_TEMPORARY)) {
|
|
/* Kernel manages these */
|
|
continue;
|
|
}
|
|
|
|
if (!nm_utils_lifetime_get (known_address->timestamp, known_address->lifetime, known_address->preferred,
|
|
now, &lifetime, &preferred))
|
|
continue;
|
|
|
|
if (!nm_platform_ip6_address_add (self, ifindex, known_address->address,
|
|
known_address->plen, known_address->peer_address,
|
|
lifetime, preferred, known_address->n_ifa_flags))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_address_flush (NMPlatform *self, int ifindex)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
return nm_platform_ip4_address_sync (self, ifindex, NULL)
|
|
&& nm_platform_ip6_address_sync (self, ifindex, NULL, FALSE);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_nmp_nlm_flag_to_string_lookup, NMPNlmFlags,
|
|
NM_UTILS_LOOKUP_DEFAULT (NULL),
|
|
NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_ADD, "add"),
|
|
NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_CHANGE, "change"),
|
|
NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_REPLACE, "replace"),
|
|
NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_PREPEND, "prepend"),
|
|
NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_APPEND, "append"),
|
|
NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_TEST, "test"),
|
|
NM_UTILS_LOOKUP_ITEM_IGNORE (NMP_NLM_FLAG_F_APPEND),
|
|
);
|
|
|
|
#define _nmp_nlm_flag_to_string(flags) \
|
|
({ \
|
|
NMPNlmFlags _flags = (flags); \
|
|
\
|
|
_nmp_nlm_flag_to_string_lookup (flags) ?: nm_sprintf_bufa (100, "new[0x%x]", (unsigned) _flags); \
|
|
})
|
|
|
|
gboolean
|
|
nm_platform_ip4_route_add (NMPlatform *self,
|
|
NMPNlmFlags flags,
|
|
const NMPlatformIP4Route *route)
|
|
{
|
|
char sbuf[sizeof (_nm_utils_to_string_buffer)];
|
|
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (route, FALSE);
|
|
g_return_val_if_fail (route->plen <= 32, FALSE);
|
|
|
|
_LOGD ("route: %-10s IPv4 route: %s",
|
|
_nmp_nlm_flag_to_string (flags),
|
|
nm_platform_ip4_route_to_string (route, sbuf, sizeof (sbuf)));
|
|
|
|
return klass->ip_route_add (self, flags, AF_INET, (const NMPlatformIPRoute *) route);
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_ip6_route_add (NMPlatform *self,
|
|
NMPNlmFlags flags,
|
|
const NMPlatformIP6Route *route)
|
|
{
|
|
char sbuf[sizeof (_nm_utils_to_string_buffer)];
|
|
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
g_return_val_if_fail (route, FALSE);
|
|
g_return_val_if_fail (route->plen <= 128, FALSE);
|
|
|
|
_LOGD ("route: %-10s IPv6 route: %s",
|
|
_nmp_nlm_flag_to_string (flags),
|
|
nm_platform_ip6_route_to_string (route, sbuf, sizeof (sbuf)));
|
|
|
|
return klass->ip_route_add (self, flags, AF_INET6, (const NMPlatformIPRoute *) route);
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_ip_route_delete (NMPlatform *self,
|
|
const NMPObject *obj)
|
|
{
|
|
_CHECK_SELF (self, klass, FALSE);
|
|
|
|
nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE,
|
|
NMP_OBJECT_TYPE_IP6_ROUTE));
|
|
|
|
_LOGD ("route: delete IPv%c route %s",
|
|
NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_IP4_ROUTE ? '4' : '6',
|
|
nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
|
|
|
|
return klass->ip_route_delete (self, obj);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
const char *
|
|
nm_platform_vlan_qos_mapping_to_string (const char *name,
|
|
const NMVlanQosMapping *map,
|
|
gsize n_map,
|
|
char *buf,
|
|
gsize len)
|
|
{
|
|
gsize i;
|
|
char *b;
|
|
|
|
nm_utils_to_string_buffer_init (&buf, &len);
|
|
|
|
if (!n_map) {
|
|
nm_utils_strbuf_append_str (&buf, &len, "");
|
|
return buf;
|
|
}
|
|
|
|
if (!map)
|
|
g_return_val_if_reached ("");
|
|
|
|
b = buf;
|
|
|
|
if (name) {
|
|
nm_utils_strbuf_append_str (&b, &len, name);
|
|
nm_utils_strbuf_append_str (&b, &len, " {");
|
|
} else
|
|
nm_utils_strbuf_append_c (&b, &len, '{');
|
|
|
|
for (i = 0; i < n_map; i++)
|
|
nm_utils_strbuf_append (&b, &len, " %u:%u", map[i].from, map[i].to);
|
|
nm_utils_strbuf_append_str (&b, &len, " }");
|
|
return buf;
|
|
}
|
|
|
|
static const char *
|
|
_lifetime_to_string (guint32 timestamp, guint32 lifetime, gint32 now, char *buf, size_t buf_size)
|
|
{
|
|
if (lifetime == NM_PLATFORM_LIFETIME_PERMANENT)
|
|
return "forever";
|
|
|
|
g_snprintf (buf, buf_size, "%usec",
|
|
nm_utils_lifetime_rebase_relative_time_on_now (timestamp, lifetime, now));
|
|
return buf;
|
|
}
|
|
|
|
|
|
static const char *
|
|
_lifetime_summary_to_string (gint32 now, guint32 timestamp, guint32 preferred, guint32 lifetime, char *buf, size_t buf_size)
|
|
{
|
|
g_snprintf (buf, buf_size, " lifetime %d-%u[%u,%u]",
|
|
(signed) now, (unsigned) timestamp, (unsigned) preferred, (unsigned) lifetime);
|
|
return buf;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_link_to_string:
|
|
* @route: pointer to NMPlatformLink address structure
|
|
* @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used.
|
|
* @len: the size of the @buf. If @buf is %NULL, this argument is ignored.
|
|
*
|
|
* A method for converting an link struct into a string representation.
|
|
*
|
|
* Returns: a string representation of the link.
|
|
*/
|
|
const char *
|
|
nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len)
|
|
{
|
|
char master[20];
|
|
char parent[20];
|
|
GString *str_flags;
|
|
char str_addrmode[30];
|
|
gs_free char *str_addr = NULL;
|
|
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);
|
|
if (NM_FLAGS_HAS (link->n_ifi_flags, IFF_NOARP))
|
|
g_string_append (str_flags, "NOARP,");
|
|
if (NM_FLAGS_HAS (link->n_ifi_flags, IFF_UP))
|
|
g_string_append (str_flags, "UP");
|
|
else
|
|
g_string_append (str_flags, "DOWN");
|
|
if (link->connected)
|
|
g_string_append (str_flags, ",LOWER_UP");
|
|
|
|
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);
|
|
}
|
|
|
|
if (link->master)
|
|
g_snprintf (master, sizeof (master), " master %d", link->master);
|
|
else
|
|
master[0] = 0;
|
|
|
|
if (link->parent > 0)
|
|
g_snprintf (parent, sizeof (parent), "@%d", link->parent);
|
|
else if (link->parent == NM_PLATFORM_LINK_OTHER_NETNS)
|
|
g_strlcpy (parent, "@other-netns", sizeof (parent));
|
|
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)));
|
|
|
|
str_link_type = nm_link_type_to_string (link->type);
|
|
|
|
g_snprintf (buf, len,
|
|
"%d: " /* ifindex */
|
|
"%s" /* name */
|
|
"%s" /* parent */
|
|
" <%s>" /* flags */
|
|
" mtu %d"
|
|
"%s" /* master */
|
|
" arp %u" /* arptype */
|
|
" %s" /* link->type */
|
|
"%s%s" /* kind */
|
|
"%s" /* is-in-udev */
|
|
"%s%s" /* addr-gen-mode */
|
|
"%s%s" /* addr */
|
|
"%s%s" /* inet6_token */
|
|
"%s%s" /* driver */
|
|
" rx:%"G_GUINT64_FORMAT",%"G_GUINT64_FORMAT
|
|
" tx:%"G_GUINT64_FORMAT",%"G_GUINT64_FORMAT
|
|
,
|
|
link->ifindex,
|
|
link->name,
|
|
parent,
|
|
str_flags->str,
|
|
link->mtu, master,
|
|
link->arptype,
|
|
str_link_type ?: "???",
|
|
link->kind ? (g_strcmp0 (str_link_type, link->kind) ? "/" : "*") : "?",
|
|
link->kind && g_strcmp0 (str_link_type, link->kind) ? link->kind : "",
|
|
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_addr : "",
|
|
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->driver : "",
|
|
link->rx_packets, link->rx_bytes,
|
|
link->tx_packets, link->tx_bytes);
|
|
g_string_free (str_flags, TRUE);
|
|
return buf;
|
|
}
|
|
|
|
const char *
|
|
nm_platform_lnk_gre_to_string (const NMPlatformLnkGre *lnk, char *buf, gsize len)
|
|
{
|
|
char str_local[30];
|
|
char str_local1[NM_UTILS_INET_ADDRSTRLEN];
|
|
char str_remote[30];
|
|
char str_remote1[NM_UTILS_INET_ADDRSTRLEN];
|
|
char str_ttl[30];
|
|
char str_tos[30];
|
|
char str_parent_ifindex[30];
|
|
char str_input_flags[30];
|
|
char str_output_flags[30];
|
|
char str_input_key[30];
|
|
char str_input_key1[NM_UTILS_INET_ADDRSTRLEN];
|
|
char str_output_key[30];
|
|
char str_output_key1[NM_UTILS_INET_ADDRSTRLEN];
|
|
|
|
if (!nm_utils_to_string_buffer_init_null (lnk, &buf, &len))
|
|
return buf;
|
|
|
|
g_snprintf (buf, len,
|
|
"gre"
|
|
"%s" /* remote */
|
|
"%s" /* local */
|
|
"%s" /* parent_ifindex */
|
|
"%s" /* ttl */
|
|
"%s" /* tos */
|
|
"%s" /* path_mtu_discovery */
|
|
"%s" /* iflags */
|
|
"%s" /* oflags */
|
|
"%s" /* ikey */
|
|
"%s" /* okey */
|
|
"",
|
|
lnk->remote ? nm_sprintf_buf (str_remote, " remote %s", nm_utils_inet4_ntop (lnk->remote, str_remote1)) : "",
|
|
lnk->local ? nm_sprintf_buf (str_local, " local %s", nm_utils_inet4_ntop (lnk->local, str_local1)) : "",
|
|
lnk->parent_ifindex ? nm_sprintf_buf (str_parent_ifindex, " dev %d", lnk->parent_ifindex) : "",
|
|
lnk->ttl ? nm_sprintf_buf (str_ttl, " ttl %u", lnk->ttl) : " ttl inherit",
|
|
lnk->tos ? (lnk->tos == 1 ? " tos inherit" : nm_sprintf_buf (str_tos, " tos 0x%x", lnk->tos)) : "",
|
|
lnk->path_mtu_discovery ? "" : " nopmtudisc",
|
|
lnk->input_flags ? nm_sprintf_buf (str_input_flags, " iflags 0x%x", lnk->input_flags) : "",
|
|
lnk->output_flags ? nm_sprintf_buf (str_output_flags, " oflags 0x%x", lnk->output_flags) : "",
|
|
NM_FLAGS_HAS (lnk->input_flags, GRE_KEY) || lnk->input_key ? nm_sprintf_buf (str_input_key, " ikey %s", nm_utils_inet4_ntop (lnk->input_key, str_input_key1)) : "",
|
|
NM_FLAGS_HAS (lnk->output_flags, GRE_KEY) || lnk->output_key ? nm_sprintf_buf (str_output_key, " okey %s", nm_utils_inet4_ntop (lnk->output_key, str_output_key1)) : "");
|
|
return buf;
|
|
}
|
|
|
|
const char *
|
|
nm_platform_lnk_infiniband_to_string (const NMPlatformLnkInfiniband *lnk, char *buf, gsize len)
|
|
{
|
|
char str_p_key[64];
|
|
|
|
if (!nm_utils_to_string_buffer_init_null (lnk, &buf, &len))
|
|
return buf;
|
|
|
|
g_snprintf (buf, len,
|
|
"infiniband"
|
|
"%s" /* p_key */
|
|
"%s%s" /* mode */
|
|
"",
|
|
lnk->p_key ? nm_sprintf_buf (str_p_key, " pkey %d", lnk->p_key) : "",
|
|
lnk->mode ? " mode " : "",
|
|
lnk->mode ?: "");
|
|
return buf;
|
|
}
|
|
|
|
const char *
|
|
nm_platform_lnk_ip6tnl_to_string (const NMPlatformLnkIp6Tnl *lnk, char *buf, gsize len)
|
|
{
|
|
char str_local[30];
|
|
char str_local1[NM_UTILS_INET_ADDRSTRLEN];
|
|
char str_remote[30];
|
|
char str_remote1[NM_UTILS_INET_ADDRSTRLEN];
|
|
char str_ttl[30];
|
|
char str_tclass[30];
|
|
char str_flow[30];
|
|
char str_encap[30];
|
|
char str_proto[30];
|
|
char str_parent_ifindex[30];
|
|
|
|
if (!nm_utils_to_string_buffer_init_null (lnk, &buf, &len))
|
|
return buf;
|
|
|
|
g_snprintf (buf, len,
|
|
"ip6tnl"
|
|
"%s" /* remote */
|
|
"%s" /* local */
|
|
"%s" /* parent_ifindex */
|
|
"%s" /* ttl */
|
|
"%s" /* tclass */
|
|
"%s" /* encap limit */
|
|
"%s" /* flow label */
|
|
"%s" /* proto */
|
|
"",
|
|
nm_sprintf_buf (str_remote, " remote %s", nm_utils_inet6_ntop (&lnk->remote, str_remote1)),
|
|
nm_sprintf_buf (str_local, " local %s", nm_utils_inet6_ntop (&lnk->local, str_local1)),
|
|
lnk->parent_ifindex ? nm_sprintf_buf (str_parent_ifindex, " dev %d", lnk->parent_ifindex) : "",
|
|
lnk->ttl ? nm_sprintf_buf (str_ttl, " ttl %u", lnk->ttl) : " ttl inherit",
|
|
lnk->tclass == 1 ? " tclass inherit" : nm_sprintf_buf (str_tclass, " tclass 0x%x", lnk->tclass),
|
|
nm_sprintf_buf (str_encap, " encap-limit %u", lnk->encap_limit),
|
|
nm_sprintf_buf (str_flow, " flow-label 0x05%x", lnk->flow_label),
|
|
nm_sprintf_buf (str_proto, " proto %u", lnk->proto));
|
|
return buf;
|
|
}
|
|
|
|
const char *
|
|
nm_platform_lnk_ipip_to_string (const NMPlatformLnkIpIp *lnk, char *buf, gsize len)
|
|
{
|
|
char str_local[30];
|
|
char str_local1[NM_UTILS_INET_ADDRSTRLEN];
|
|
char str_remote[30];
|
|
char str_remote1[NM_UTILS_INET_ADDRSTRLEN];
|
|
char str_ttl[30];
|
|
char str_tos[30];
|
|
char str_parent_ifindex[30];
|
|
|
|
if (!nm_utils_to_string_buffer_init_null (lnk, &buf, &len))
|
|
return buf;
|
|
|
|
g_snprintf (buf, len,
|
|
"ipip"
|
|
"%s" /* remote */
|
|
"%s" /* local */
|
|
"%s" /* parent_ifindex */
|
|
"%s" /* ttl */
|
|
"%s" /* tos */
|
|
"%s" /* path_mtu_discovery */
|
|
"",
|
|
lnk->remote ? nm_sprintf_buf (str_remote, " remote %s", nm_utils_inet4_ntop (lnk->remote, str_remote1)) : "",
|
|
lnk->local ? nm_sprintf_buf (str_local, " local %s", nm_utils_inet4_ntop (lnk->local, str_local1)) : "",
|
|
lnk->parent_ifindex ? nm_sprintf_buf (str_parent_ifindex, " dev %d", lnk->parent_ifindex) : "",
|
|
lnk->ttl ? nm_sprintf_buf (str_ttl, " ttl %u", lnk->ttl) : " ttl inherit",
|
|
lnk->tos ? (lnk->tos == 1 ? " tos inherit" : nm_sprintf_buf (str_tos, " tos 0x%x", lnk->tos)) : "",
|
|
lnk->path_mtu_discovery ? "" : " nopmtudisc");
|
|
return buf;
|
|
}
|
|
|
|
const char *
|
|
nm_platform_lnk_macsec_to_string (const NMPlatformLnkMacsec *lnk, char *buf, gsize len)
|
|
{
|
|
if (!nm_utils_to_string_buffer_init_null (lnk, &buf, &len))
|
|
return buf;
|
|
|
|
g_snprintf (buf, len,
|
|
"macsec "
|
|
"sci %016llx "
|
|
"protect %s "
|
|
"cipher %016llx "
|
|
"icvlen %u "
|
|
"encodingsa %u "
|
|
"validate %u "
|
|
"encrypt %s "
|
|
"send_sci %s "
|
|
"end_station %s "
|
|
"scb %s "
|
|
"replay %s",
|
|
(unsigned long long) lnk->sci,
|
|
lnk->protect ? "on" : "off",
|
|
(unsigned long long) lnk->cipher_suite,
|
|
lnk->icv_length,
|
|
lnk->encoding_sa,
|
|
lnk->validation,
|
|
lnk->encrypt ? "on" : "off",
|
|
lnk->include_sci ? "on" : "off",
|
|
lnk->es ? "on" : "off",
|
|
lnk->scb ? "on" : "off",
|
|
lnk->replay_protect ? "on" : "off");
|
|
return buf;
|
|
}
|
|
|
|
const char *
|
|
nm_platform_lnk_macvlan_to_string (const NMPlatformLnkMacvlan *lnk, char *buf, gsize len)
|
|
{
|
|
if (!nm_utils_to_string_buffer_init_null (lnk, &buf, &len))
|
|
return buf;
|
|
|
|
g_snprintf (buf, len,
|
|
"macvlan mode %u %s",
|
|
lnk->mode,
|
|
lnk->no_promisc ? "not-promisc" : "promisc");
|
|
return buf;
|
|
}
|
|
|
|
const char *
|
|
nm_platform_lnk_sit_to_string (const NMPlatformLnkSit *lnk, char *buf, gsize len)
|
|
{
|
|
char str_local[30];
|
|
char str_local1[NM_UTILS_INET_ADDRSTRLEN];
|
|
char str_remote[30];
|
|
char str_remote1[NM_UTILS_INET_ADDRSTRLEN];
|
|
char str_ttl[30];
|
|
char str_tos[30];
|
|
char str_flags[30];
|
|
char str_proto[30];
|
|
char str_parent_ifindex[30];
|
|
|
|
if (!nm_utils_to_string_buffer_init_null (lnk, &buf, &len))
|
|
return buf;
|
|
|
|
g_snprintf (buf, len,
|
|
"sit"
|
|
"%s" /* remote */
|
|
"%s" /* local */
|
|
"%s" /* parent_ifindex */
|
|
"%s" /* ttl */
|
|
"%s" /* tos */
|
|
"%s" /* path_mtu_discovery */
|
|
"%s" /* flags */
|
|
"%s" /* proto */
|
|
"",
|
|
lnk->remote ? nm_sprintf_buf (str_remote, " remote %s", nm_utils_inet4_ntop (lnk->remote, str_remote1)) : "",
|
|
lnk->local ? nm_sprintf_buf (str_local, " local %s", nm_utils_inet4_ntop (lnk->local, str_local1)) : "",
|
|
lnk->parent_ifindex ? nm_sprintf_buf (str_parent_ifindex, " dev %d", lnk->parent_ifindex) : "",
|
|
lnk->ttl ? nm_sprintf_buf (str_ttl, " ttl %u", lnk->ttl) : " ttl inherit",
|
|
lnk->tos ? (lnk->tos == 1 ? " tos inherit" : nm_sprintf_buf (str_tos, " tos 0x%x", lnk->tos)) : "",
|
|
lnk->path_mtu_discovery ? "" : " nopmtudisc",
|
|
lnk->flags ? nm_sprintf_buf (str_flags, " flags 0x%x", lnk->flags) : "",
|
|
lnk->proto ? nm_sprintf_buf (str_proto, " proto 0x%x", lnk->proto) : "");
|
|
return buf;
|
|
}
|
|
|
|
const char *
|
|
nm_platform_lnk_vlan_to_string (const NMPlatformLnkVlan *lnk, char *buf, gsize len)
|
|
{
|
|
char *b;
|
|
|
|
if (!nm_utils_to_string_buffer_init_null (lnk, &buf, &len))
|
|
return buf;
|
|
|
|
b = buf;
|
|
|
|
nm_utils_strbuf_append (&b, &len, "vlan %u", lnk->id);
|
|
if (lnk->flags)
|
|
nm_utils_strbuf_append (&b, &len, " flags 0x%x", lnk->flags);
|
|
return buf;
|
|
}
|
|
|
|
const char *
|
|
nm_platform_lnk_vxlan_to_string (const NMPlatformLnkVxlan *lnk, char *buf, gsize len)
|
|
{
|
|
char str_group[100];
|
|
char str_group6[100];
|
|
char str_local[100];
|
|
char str_local6[100];
|
|
char str_dev[25];
|
|
char str_limit[25];
|
|
char str_src_port[35];
|
|
char str_dst_port[25];
|
|
char str_tos[25];
|
|
char str_ttl[25];
|
|
|
|
if (!nm_utils_to_string_buffer_init_null (lnk, &buf, &len))
|
|
return buf;
|
|
|
|
if (lnk->group == 0)
|
|
str_group[0] = '\0';
|
|
else {
|
|
g_snprintf (str_group, sizeof (str_group),
|
|
" %s %s",
|
|
IN_MULTICAST (ntohl (lnk->group)) ? "group" : "remote",
|
|
nm_utils_inet4_ntop (lnk->group, NULL));
|
|
}
|
|
if (IN6_IS_ADDR_UNSPECIFIED (&lnk->group6))
|
|
str_group6[0] = '\0';
|
|
else {
|
|
g_snprintf (str_group6, sizeof (str_group6),
|
|
" %s%s %s",
|
|
IN6_IS_ADDR_MULTICAST (&lnk->group6) ? "group" : "remote",
|
|
str_group[0] ? "6" : "", /* usually, a vxlan has either v4 or v6 only. */
|
|
nm_utils_inet6_ntop (&lnk->group6, NULL));
|
|
}
|
|
|
|
if (lnk->local == 0)
|
|
str_local[0] = '\0';
|
|
else {
|
|
g_snprintf (str_local, sizeof (str_local),
|
|
" local %s",
|
|
nm_utils_inet4_ntop (lnk->local, NULL));
|
|
}
|
|
if (IN6_IS_ADDR_UNSPECIFIED (&lnk->local6))
|
|
str_local6[0] = '\0';
|
|
else {
|
|
g_snprintf (str_local6, sizeof (str_local6),
|
|
" local%s %s",
|
|
str_local[0] ? "6" : "", /* usually, a vxlan has either v4 or v6 only. */
|
|
nm_utils_inet6_ntop (&lnk->local6, NULL));
|
|
}
|
|
|
|
g_snprintf (buf, len,
|
|
"vxlan"
|
|
" id %u" /* id */
|
|
"%s%s" /* group/group6 */
|
|
"%s%s" /* local/local6 */
|
|
"%s" /* dev */
|
|
"%s" /* src_port_min/src_port_max */
|
|
"%s" /* dst_port */
|
|
"%s" /* learning */
|
|
"%s" /* proxy */
|
|
"%s" /* rsc */
|
|
"%s" /* l2miss */
|
|
"%s" /* l3miss */
|
|
"%s" /* tos */
|
|
"%s" /* ttl */
|
|
" ageing %u" /* ageing */
|
|
"%s" /* limit */
|
|
"",
|
|
(guint) lnk->id,
|
|
str_group, str_group6,
|
|
str_local, str_local6,
|
|
lnk->parent_ifindex ? nm_sprintf_buf (str_dev, " dev %d", lnk->parent_ifindex) : "",
|
|
lnk->src_port_min || lnk->src_port_max ? nm_sprintf_buf (str_src_port, " srcport %u %u", lnk->src_port_min, lnk->src_port_max) : "",
|
|
lnk->dst_port ? nm_sprintf_buf (str_dst_port, " dstport %u", lnk->dst_port) : "",
|
|
!lnk->learning ? " nolearning" : "",
|
|
lnk->proxy ? " proxy" : "",
|
|
lnk->rsc ? " rsc" : "",
|
|
lnk->l2miss ? " l2miss" : "",
|
|
lnk->l3miss ? " l3miss" : "",
|
|
lnk->tos == 1 ? " tos inherit" : nm_sprintf_buf (str_tos, " tos %#x", lnk->tos),
|
|
lnk->ttl ? nm_sprintf_buf (str_ttl, " ttl %u", lnk->ttl) : "",
|
|
lnk->ageing,
|
|
lnk->limit ? nm_sprintf_buf (str_limit, " maxaddr %u", lnk->limit) : "");
|
|
return buf;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_ip4_address_to_string:
|
|
* @route: pointer to NMPlatformIP4Address address structure
|
|
* @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used.
|
|
* @len: the size of the @buf. If @buf is %NULL, this argument is ignored.
|
|
*
|
|
* A method for converting an address struct into a string representation.
|
|
*
|
|
* Example output: ""
|
|
*
|
|
* Returns: a string representation of the address.
|
|
*/
|
|
const char *
|
|
nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address, char *buf, gsize len)
|
|
{
|
|
char s_flags[TO_STRING_IFA_FLAGS_BUF_SIZE];
|
|
char s_address[INET_ADDRSTRLEN];
|
|
char s_peer[INET_ADDRSTRLEN];
|
|
char str_dev[TO_STRING_DEV_BUF_SIZE];
|
|
char str_label[32];
|
|
char str_lft[30], str_pref[30], str_time[50], s_source[50];
|
|
char *str_peer = NULL;
|
|
const char *str_lft_p, *str_pref_p, *str_time_p;
|
|
gint32 now = nm_utils_get_monotonic_timestamp_s ();
|
|
|
|
if (!nm_utils_to_string_buffer_init_null (address, &buf, &len))
|
|
return buf;
|
|
|
|
inet_ntop (AF_INET, &address->address, s_address, sizeof (s_address));
|
|
|
|
if (address->peer_address != address->address) {
|
|
inet_ntop (AF_INET, &address->peer_address, s_peer, sizeof (s_peer));
|
|
str_peer = g_strconcat (" ptp ", s_peer, NULL);
|
|
}
|
|
|
|
_to_string_dev (NULL, address->ifindex, str_dev, sizeof (str_dev));
|
|
|
|
if (*address->label)
|
|
g_snprintf (str_label, sizeof (str_label), " label %s", address->label);
|
|
else
|
|
str_label[0] = 0;
|
|
|
|
str_lft_p = _lifetime_to_string (address->timestamp,
|
|
address->lifetime ? address->lifetime : NM_PLATFORM_LIFETIME_PERMANENT,
|
|
now, str_lft, sizeof (str_lft)),
|
|
str_pref_p = (address->lifetime == address->preferred)
|
|
? str_lft_p
|
|
: ( _lifetime_to_string (address->timestamp,
|
|
address->lifetime ? MIN (address->preferred, address->lifetime) : NM_PLATFORM_LIFETIME_PERMANENT,
|
|
now, str_pref, sizeof (str_pref)) );
|
|
str_time_p = _lifetime_summary_to_string (now, address->timestamp, address->preferred, address->lifetime, str_time, sizeof (str_time));
|
|
|
|
g_snprintf (buf, len,
|
|
"%s/%d lft %s pref %s%s%s%s%s%s src %s",
|
|
s_address, address->plen, str_lft_p, str_pref_p, str_time_p,
|
|
str_peer ? str_peer : "",
|
|
str_dev,
|
|
_to_string_ifa_flags (address->n_ifa_flags, s_flags, sizeof (s_flags)),
|
|
str_label,
|
|
nmp_utils_ip_config_source_to_string (address->addr_source, s_source, sizeof (s_source)));
|
|
g_free (str_peer);
|
|
return buf;
|
|
}
|
|
|
|
NM_UTILS_FLAGS2STR_DEFINE (nm_platform_link_flags2str, unsigned,
|
|
NM_UTILS_FLAGS2STR (IFF_LOOPBACK, "loopback"),
|
|
NM_UTILS_FLAGS2STR (IFF_BROADCAST, "broadcast"),
|
|
NM_UTILS_FLAGS2STR (IFF_POINTOPOINT, "pointopoint"),
|
|
NM_UTILS_FLAGS2STR (IFF_MULTICAST, "multicast"),
|
|
NM_UTILS_FLAGS2STR (IFF_NOARP, "noarp"),
|
|
NM_UTILS_FLAGS2STR (IFF_ALLMULTI, "allmulti"),
|
|
NM_UTILS_FLAGS2STR (IFF_PROMISC, "promisc"),
|
|
NM_UTILS_FLAGS2STR (IFF_MASTER, "master"),
|
|
NM_UTILS_FLAGS2STR (IFF_SLAVE, "slave"),
|
|
NM_UTILS_FLAGS2STR (IFF_DEBUG, "debug"),
|
|
NM_UTILS_FLAGS2STR (IFF_DYNAMIC, "dynamic"),
|
|
NM_UTILS_FLAGS2STR (IFF_AUTOMEDIA, "automedia"),
|
|
NM_UTILS_FLAGS2STR (IFF_PORTSEL, "portsel"),
|
|
NM_UTILS_FLAGS2STR (IFF_NOTRAILERS, "notrailers"),
|
|
NM_UTILS_FLAGS2STR (IFF_UP, "up"),
|
|
NM_UTILS_FLAGS2STR (IFF_RUNNING, "running"),
|
|
NM_UTILS_FLAGS2STR (IFF_LOWER_UP, "lowerup"),
|
|
NM_UTILS_FLAGS2STR (IFF_DORMANT, "dormant"),
|
|
NM_UTILS_FLAGS2STR (IFF_ECHO, "echo"),
|
|
);
|
|
|
|
NM_UTILS_ENUM2STR_DEFINE (nm_platform_link_inet6_addrgenmode2str, guint8,
|
|
NM_UTILS_ENUM2STR (NM_IN6_ADDR_GEN_MODE_NONE, "none"),
|
|
NM_UTILS_ENUM2STR (NM_IN6_ADDR_GEN_MODE_EUI64, "eui64"),
|
|
NM_UTILS_ENUM2STR (NM_IN6_ADDR_GEN_MODE_STABLE_PRIVACY, "stable-privacy"),
|
|
NM_UTILS_ENUM2STR (NM_IN6_ADDR_GEN_MODE_RANDOM, "random"),
|
|
);
|
|
|
|
NM_UTILS_FLAGS2STR_DEFINE (nm_platform_addr_flags2str, unsigned,
|
|
NM_UTILS_FLAGS2STR (IFA_F_SECONDARY, "secondary"),
|
|
NM_UTILS_FLAGS2STR (IFA_F_NODAD, "nodad"),
|
|
NM_UTILS_FLAGS2STR (IFA_F_OPTIMISTIC, "optimistic"),
|
|
NM_UTILS_FLAGS2STR (IFA_F_HOMEADDRESS, "homeaddress"),
|
|
NM_UTILS_FLAGS2STR (IFA_F_DEPRECATED, "deprecated"),
|
|
NM_UTILS_FLAGS2STR (IFA_F_TENTATIVE, "tentative"),
|
|
NM_UTILS_FLAGS2STR (IFA_F_PERMANENT, "permanent"),
|
|
NM_UTILS_FLAGS2STR (IFA_F_MANAGETEMPADDR, "mngtmpaddr"),
|
|
NM_UTILS_FLAGS2STR (IFA_F_NOPREFIXROUTE, "noprefixroute"),
|
|
);
|
|
|
|
NM_UTILS_ENUM2STR_DEFINE (nm_platform_route_scope2str, int,
|
|
NM_UTILS_ENUM2STR (RT_SCOPE_NOWHERE, "nowhere"),
|
|
NM_UTILS_ENUM2STR (RT_SCOPE_HOST, "host"),
|
|
NM_UTILS_ENUM2STR (RT_SCOPE_LINK, "link"),
|
|
NM_UTILS_ENUM2STR (RT_SCOPE_SITE, "site"),
|
|
NM_UTILS_ENUM2STR (RT_SCOPE_UNIVERSE, "global"),
|
|
);
|
|
|
|
/**
|
|
* nm_platform_ip6_address_to_string:
|
|
* @route: pointer to NMPlatformIP6Address address structure
|
|
* @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used.
|
|
* @len: the size of the @buf. If @buf is %NULL, this argument is ignored.
|
|
*
|
|
* A method for converting an address struct into a string representation.
|
|
*
|
|
* Example output: "2001:db8:0:f101::1/64 lft 4294967295 pref 4294967295 time 16922666 on dev em1"
|
|
*
|
|
* Returns: a string representation of the address.
|
|
*/
|
|
const char *
|
|
nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address, char *buf, gsize len)
|
|
{
|
|
char s_flags[TO_STRING_IFA_FLAGS_BUF_SIZE];
|
|
char s_address[INET6_ADDRSTRLEN];
|
|
char s_peer[INET6_ADDRSTRLEN];
|
|
char str_lft[30], str_pref[30], str_time[50], s_source[50];
|
|
char str_dev[TO_STRING_DEV_BUF_SIZE];
|
|
char *str_peer = NULL;
|
|
const char *str_lft_p, *str_pref_p, *str_time_p;
|
|
gint32 now = nm_utils_get_monotonic_timestamp_s ();
|
|
|
|
if (!nm_utils_to_string_buffer_init_null (address, &buf, &len))
|
|
return buf;
|
|
|
|
inet_ntop (AF_INET6, &address->address, s_address, sizeof (s_address));
|
|
|
|
if (!IN6_IS_ADDR_UNSPECIFIED (&address->peer_address)) {
|
|
inet_ntop (AF_INET6, &address->peer_address, s_peer, sizeof (s_peer));
|
|
str_peer = g_strconcat (" ptp ", s_peer, NULL);
|
|
}
|
|
|
|
_to_string_dev (NULL, address->ifindex, str_dev, sizeof (str_dev));
|
|
|
|
str_lft_p = _lifetime_to_string (address->timestamp,
|
|
address->lifetime ? address->lifetime : NM_PLATFORM_LIFETIME_PERMANENT,
|
|
now, str_lft, sizeof (str_lft)),
|
|
str_pref_p = (address->lifetime == address->preferred)
|
|
? str_lft_p
|
|
: ( _lifetime_to_string (address->timestamp,
|
|
address->lifetime ? MIN (address->preferred, address->lifetime) : NM_PLATFORM_LIFETIME_PERMANENT,
|
|
now, str_pref, sizeof (str_pref)) );
|
|
str_time_p = _lifetime_summary_to_string (now, address->timestamp, address->preferred, address->lifetime, str_time, sizeof (str_time));
|
|
|
|
g_snprintf (buf, len,
|
|
"%s/%d lft %s pref %s%s%s%s%s src %s",
|
|
s_address, address->plen, str_lft_p, str_pref_p, str_time_p,
|
|
str_peer ? str_peer : "",
|
|
str_dev,
|
|
_to_string_ifa_flags (address->n_ifa_flags, s_flags, sizeof (s_flags)),
|
|
nmp_utils_ip_config_source_to_string (address->addr_source, s_source, sizeof (s_source)));
|
|
g_free (str_peer);
|
|
return buf;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_ip4_route_to_string:
|
|
* @route: pointer to NMPlatformIP4Route route structure
|
|
* @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used.
|
|
* @len: the size of the @buf. If @buf is %NULL, this argument is ignored.
|
|
*
|
|
* A method for converting a route struct into a string representation.
|
|
*
|
|
* Example output: "192.168.1.0/24 via 0.0.0.0 dev em1 metric 0 mss 0"
|
|
*
|
|
* Returns: a string representation of the route.
|
|
*/
|
|
const char *
|
|
nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsize len)
|
|
{
|
|
char s_network[INET_ADDRSTRLEN], s_gateway[INET_ADDRSTRLEN];
|
|
char s_pref_src[INET_ADDRSTRLEN];
|
|
char str_dev[TO_STRING_DEV_BUF_SIZE];
|
|
char str_scope[30], s_source[50];
|
|
char str_tos[32], str_window[32], str_cwnd[32], str_initcwnd[32], str_initrwnd[32], str_mtu[32];
|
|
|
|
if (!nm_utils_to_string_buffer_init_null (route, &buf, &len))
|
|
return buf;
|
|
|
|
inet_ntop (AF_INET, &route->network, s_network, sizeof(s_network));
|
|
inet_ntop (AF_INET, &route->gateway, s_gateway, sizeof(s_gateway));
|
|
|
|
_to_string_dev (NULL, route->ifindex, str_dev, sizeof (str_dev));
|
|
|
|
|
|
g_snprintf (buf, len,
|
|
"%s/%d"
|
|
" via %s"
|
|
"%s"
|
|
" metric %"G_GUINT32_FORMAT
|
|
" mss %"G_GUINT32_FORMAT
|
|
" rt-src %s" /* protocol */
|
|
"%s" /* cloned */
|
|
"%s%s" /* scope */
|
|
"%s%s" /* pref-src */
|
|
"%s" /* tos */
|
|
"%s" /* window */
|
|
"%s" /* cwnd */
|
|
"%s" /* initcwnd */
|
|
"%s" /* initrwnd */
|
|
"%s" /* mtu */
|
|
"",
|
|
s_network,
|
|
route->plen,
|
|
s_gateway,
|
|
str_dev,
|
|
route->metric,
|
|
route->mss,
|
|
nmp_utils_ip_config_source_to_string (route->rt_source, s_source, sizeof (s_source)),
|
|
route->rt_cloned ? " cloned" : "",
|
|
route->scope_inv ? " scope " : "",
|
|
route->scope_inv ? (nm_platform_route_scope2str (nm_platform_route_scope_inv (route->scope_inv), str_scope, sizeof (str_scope))) : "",
|
|
route->pref_src ? " pref-src " : "",
|
|
route->pref_src ? inet_ntop (AF_INET, &route->pref_src, s_pref_src, sizeof(s_pref_src)) : "",
|
|
route->tos ? nm_sprintf_buf (str_tos, " tos 0x%x", (unsigned) route->tos) : "",
|
|
route->window || route->lock_window ? nm_sprintf_buf (str_window, " window %s%"G_GUINT32_FORMAT, route->lock_window ? "lock " : "", route->window) : "",
|
|
route->cwnd || route->lock_cwnd ? nm_sprintf_buf (str_cwnd, " cwnd %s%"G_GUINT32_FORMAT, route->lock_cwnd ? "lock " : "", route->cwnd) : "",
|
|
route->initcwnd || route->lock_initcwnd ? nm_sprintf_buf (str_initcwnd, " initcwnd %s%"G_GUINT32_FORMAT, route->lock_initcwnd ? "lock " : "", route->initcwnd) : "",
|
|
route->initrwnd || route->lock_initrwnd ? nm_sprintf_buf (str_initrwnd, " initrwnd %s%"G_GUINT32_FORMAT, route->lock_initrwnd ? "lock " : "", route->initrwnd) : "",
|
|
route->mtu || route->lock_mtu ? nm_sprintf_buf (str_mtu, " mtu %s%"G_GUINT32_FORMAT, route->lock_mtu ? "lock " : "", route->mtu) : "");
|
|
return buf;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_ip6_route_to_string:
|
|
* @route: pointer to NMPlatformIP6Route route structure
|
|
* @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used.
|
|
* @len: the size of the @buf. If @buf is %NULL, this argument is ignored.
|
|
*
|
|
* A method for converting a route struct into a string representation.
|
|
*
|
|
* Example output: "ff02::fb/128 via :: dev em1 metric 0"
|
|
*
|
|
* Returns: a string representation of the route.
|
|
*/
|
|
const char *
|
|
nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsize len)
|
|
{
|
|
char s_network[INET6_ADDRSTRLEN], s_gateway[INET6_ADDRSTRLEN], s_pref_src[INET6_ADDRSTRLEN];
|
|
char s_src_all[INET6_ADDRSTRLEN + 40], s_src[INET6_ADDRSTRLEN];
|
|
char str_dev[TO_STRING_DEV_BUF_SIZE], s_source[50];
|
|
char str_window[32], str_cwnd[32], str_initcwnd[32], str_initrwnd[32], str_mtu[32];
|
|
|
|
if (!nm_utils_to_string_buffer_init_null (route, &buf, &len))
|
|
return buf;
|
|
|
|
inet_ntop (AF_INET6, &route->network, s_network, sizeof (s_network));
|
|
inet_ntop (AF_INET6, &route->gateway, s_gateway, sizeof (s_gateway));
|
|
|
|
if (IN6_IS_ADDR_UNSPECIFIED (&route->pref_src))
|
|
s_pref_src[0] = 0;
|
|
else
|
|
inet_ntop (AF_INET6, &route->pref_src, s_pref_src, sizeof (s_pref_src));
|
|
|
|
_to_string_dev (NULL, route->ifindex, str_dev, sizeof (str_dev));
|
|
|
|
g_snprintf (buf, len,
|
|
"%s/%d"
|
|
" via %s"
|
|
"%s"
|
|
" metric %"G_GUINT32_FORMAT
|
|
" mss %"G_GUINT32_FORMAT
|
|
" rt-src %s" /* protocol */
|
|
"%s" /* source */
|
|
"%s" /* cloned */
|
|
"%s%s" /* pref-src */
|
|
"%s" /* window */
|
|
"%s" /* cwnd */
|
|
"%s" /* initcwnd */
|
|
"%s" /* initrwnd */
|
|
"%s" /* mtu */
|
|
"",
|
|
s_network,
|
|
route->plen,
|
|
s_gateway,
|
|
str_dev,
|
|
route->metric,
|
|
route->mss,
|
|
nmp_utils_ip_config_source_to_string (route->rt_source, s_source, sizeof (s_source)),
|
|
route->src_plen || !IN6_IS_ADDR_UNSPECIFIED (&route->src)
|
|
? nm_sprintf_buf (s_src_all, " src %s/%u", nm_utils_inet6_ntop (&route->src, s_src), (unsigned) route->src_plen)
|
|
: "",
|
|
route->rt_cloned ? " cloned" : "",
|
|
s_pref_src[0] ? " pref-src " : "",
|
|
s_pref_src[0] ? s_pref_src : "",
|
|
route->window || route->lock_window ? nm_sprintf_buf (str_window, " window %s%"G_GUINT32_FORMAT, route->lock_window ? "lock " : "", route->window) : "",
|
|
route->cwnd || route->lock_cwnd ? nm_sprintf_buf (str_cwnd, " cwnd %s%"G_GUINT32_FORMAT, route->lock_cwnd ? "lock " : "", route->cwnd) : "",
|
|
route->initcwnd || route->lock_initcwnd ? nm_sprintf_buf (str_initcwnd, " initcwnd %s%"G_GUINT32_FORMAT, route->lock_initcwnd ? "lock " : "", route->initcwnd) : "",
|
|
route->initrwnd || route->lock_initrwnd ? nm_sprintf_buf (str_initrwnd, " initrwnd %s%"G_GUINT32_FORMAT, route->lock_initrwnd ? "lock " : "", route->initrwnd) : "",
|
|
route->mtu || route->lock_mtu ? nm_sprintf_buf (str_mtu, " mtu %s%"G_GUINT32_FORMAT, route->lock_mtu ? "lock " : "", route->mtu) : "");
|
|
|
|
return buf;
|
|
}
|
|
|
|
guint
|
|
nm_platform_link_hash (const NMPlatformLink *obj)
|
|
{
|
|
guint h = 99413953;
|
|
guint8 i8;
|
|
|
|
h = NM_HASH_COMBINE (h, obj->ifindex);
|
|
h = NM_HASH_COMBINE (h, obj->type);
|
|
h = NM_HASH_COMBINE (h, g_str_hash (obj->name));
|
|
h = NM_HASH_COMBINE (h, obj->master);
|
|
h = NM_HASH_COMBINE (h, obj->parent);
|
|
h = NM_HASH_COMBINE (h, obj->n_ifi_flags);
|
|
h = NM_HASH_COMBINE (h, obj->connected);
|
|
h = NM_HASH_COMBINE (h, obj->mtu);
|
|
h = NM_HASH_COMBINE (h, !!obj->initialized);
|
|
h = NM_HASH_COMBINE (h, obj->arptype);
|
|
h = NM_HASH_COMBINE (h, obj->addr.len);
|
|
h = NM_HASH_COMBINE (h, obj->inet6_addr_gen_mode_inv);
|
|
if (obj->kind)
|
|
h = NM_HASH_COMBINE (h, g_str_hash (obj->kind));
|
|
if (obj->driver)
|
|
h = NM_HASH_COMBINE (h, g_str_hash (obj->driver));
|
|
for (i8 = 0; i8 < obj->addr.len; i8++)
|
|
h = NM_HASH_COMBINE (h, obj->addr.data[i8]);
|
|
for (i8 = 0; i8 < sizeof (obj->inet6_token); i8++)
|
|
h = NM_HASH_COMBINE (h, obj->inet6_token.id_u8[i8]);
|
|
h = NM_HASH_COMBINE (h, obj->rx_packets);
|
|
h = NM_HASH_COMBINE (h, obj->rx_bytes);
|
|
h = NM_HASH_COMBINE (h, obj->tx_packets);
|
|
h = NM_HASH_COMBINE (h, obj->tx_bytes);
|
|
return h;
|
|
}
|
|
|
|
int
|
|
nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b)
|
|
{
|
|
NM_CMP_SELF (a, b);
|
|
NM_CMP_FIELD (a, b, ifindex);
|
|
NM_CMP_FIELD (a, b, type);
|
|
NM_CMP_FIELD_STR (a, b, name);
|
|
NM_CMP_FIELD (a, b, master);
|
|
NM_CMP_FIELD (a, b, parent);
|
|
NM_CMP_FIELD (a, b, n_ifi_flags);
|
|
NM_CMP_FIELD_UNSAFE (a, b, connected);
|
|
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, 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);
|
|
NM_CMP_FIELD_MEMCMP (a, b, inet6_token);
|
|
NM_CMP_FIELD (a, b, rx_packets);
|
|
NM_CMP_FIELD (a, b, rx_bytes);
|
|
NM_CMP_FIELD (a, b, tx_packets);
|
|
NM_CMP_FIELD (a, b, tx_bytes);
|
|
return 0;
|
|
}
|
|
|
|
guint
|
|
nm_platform_lnk_gre_hash (const NMPlatformLnkGre *obj)
|
|
{
|
|
guint h = 1887023311;
|
|
|
|
h = NM_HASH_COMBINE (h, obj->parent_ifindex);
|
|
h = NM_HASH_COMBINE (h, obj->input_flags);
|
|
h = NM_HASH_COMBINE (h, obj->output_flags);
|
|
h = NM_HASH_COMBINE (h, obj->input_key);
|
|
h = NM_HASH_COMBINE (h, obj->output_key);
|
|
h = NM_HASH_COMBINE (h, obj->local);
|
|
h = NM_HASH_COMBINE (h, obj->remote);
|
|
h = NM_HASH_COMBINE (h, obj->ttl);
|
|
h = NM_HASH_COMBINE (h, obj->tos);
|
|
h = NM_HASH_COMBINE (h, !obj->path_mtu_discovery);
|
|
return h;
|
|
}
|
|
|
|
int
|
|
nm_platform_lnk_gre_cmp (const NMPlatformLnkGre *a, const NMPlatformLnkGre *b)
|
|
{
|
|
NM_CMP_SELF (a, b);
|
|
NM_CMP_FIELD (a, b, parent_ifindex);
|
|
NM_CMP_FIELD (a, b, input_flags);
|
|
NM_CMP_FIELD (a, b, output_flags);
|
|
NM_CMP_FIELD (a, b, input_key);
|
|
NM_CMP_FIELD (a, b, output_key);
|
|
NM_CMP_FIELD (a, b, local);
|
|
NM_CMP_FIELD (a, b, remote);
|
|
NM_CMP_FIELD (a, b, ttl);
|
|
NM_CMP_FIELD (a, b, tos);
|
|
NM_CMP_FIELD_BOOL (a, b, path_mtu_discovery);
|
|
return 0;
|
|
}
|
|
|
|
guint
|
|
nm_platform_lnk_infiniband_hash (const NMPlatformLnkInfiniband *obj)
|
|
{
|
|
guint h = 1748638583;
|
|
|
|
h = NM_HASH_COMBINE (h, obj->p_key);
|
|
if (obj->mode)
|
|
h = NM_HASH_COMBINE (h, g_str_hash (obj->mode));
|
|
return h;
|
|
}
|
|
|
|
int
|
|
nm_platform_lnk_infiniband_cmp (const NMPlatformLnkInfiniband *a, const NMPlatformLnkInfiniband *b)
|
|
{
|
|
NM_CMP_SELF (a, b);
|
|
NM_CMP_FIELD (a, b, p_key);
|
|
NM_CMP_FIELD_STR_INTERNED (a, b, mode);
|
|
return 0;
|
|
}
|
|
|
|
guint
|
|
nm_platform_lnk_ip6tnl_hash (const NMPlatformLnkIp6Tnl *obj)
|
|
{
|
|
guint h = 1651660009;
|
|
|
|
h = NM_HASH_COMBINE (h, obj->parent_ifindex);
|
|
h = NM_HASH_COMBINE_IN6ADDR (h, &obj->local);
|
|
h = NM_HASH_COMBINE_IN6ADDR (h, &obj->remote);
|
|
h = NM_HASH_COMBINE (h, obj->ttl);
|
|
h = NM_HASH_COMBINE (h, obj->tclass);
|
|
h = NM_HASH_COMBINE (h, obj->encap_limit);
|
|
h = NM_HASH_COMBINE (h, obj->flow_label);
|
|
h = NM_HASH_COMBINE (h, obj->proto);
|
|
return h;
|
|
}
|
|
|
|
int
|
|
nm_platform_lnk_ip6tnl_cmp (const NMPlatformLnkIp6Tnl *a, const NMPlatformLnkIp6Tnl *b)
|
|
{
|
|
NM_CMP_SELF (a, b);
|
|
NM_CMP_FIELD (a, b, parent_ifindex);
|
|
NM_CMP_FIELD_MEMCMP (a, b, local);
|
|
NM_CMP_FIELD_MEMCMP (a, b, remote);
|
|
NM_CMP_FIELD (a, b, ttl);
|
|
NM_CMP_FIELD (a, b, tclass);
|
|
NM_CMP_FIELD (a, b, encap_limit);
|
|
NM_CMP_FIELD (a, b, flow_label);
|
|
NM_CMP_FIELD (a, b, proto);
|
|
return 0;
|
|
}
|
|
|
|
guint
|
|
nm_platform_lnk_ipip_hash (const NMPlatformLnkIpIp *obj)
|
|
{
|
|
guint h = 861934429;
|
|
|
|
h = NM_HASH_COMBINE (h, obj->parent_ifindex);
|
|
h = NM_HASH_COMBINE (h, obj->local);
|
|
h = NM_HASH_COMBINE (h, obj->remote);
|
|
h = NM_HASH_COMBINE (h, obj->ttl);
|
|
h = NM_HASH_COMBINE (h, obj->tos);
|
|
h = NM_HASH_COMBINE (h, obj->path_mtu_discovery);
|
|
return h;
|
|
}
|
|
|
|
int
|
|
nm_platform_lnk_ipip_cmp (const NMPlatformLnkIpIp *a, const NMPlatformLnkIpIp *b)
|
|
{
|
|
NM_CMP_SELF (a, b);
|
|
NM_CMP_FIELD (a, b, parent_ifindex);
|
|
NM_CMP_FIELD (a, b, local);
|
|
NM_CMP_FIELD (a, b, remote);
|
|
NM_CMP_FIELD (a, b, ttl);
|
|
NM_CMP_FIELD (a, b, tos);
|
|
NM_CMP_FIELD_BOOL (a, b, path_mtu_discovery);
|
|
return 0;
|
|
}
|
|
|
|
guint
|
|
nm_platform_lnk_macsec_hash (const NMPlatformLnkMacsec *obj)
|
|
{
|
|
guint h = 226984267;
|
|
|
|
h = NM_HASH_COMBINE (h, obj->sci);
|
|
h = NM_HASH_COMBINE_UINT64 (h, obj->icv_length);
|
|
h = NM_HASH_COMBINE_UINT64 (h, obj->cipher_suite);
|
|
h = NM_HASH_COMBINE (h, obj->window);
|
|
h = NM_HASH_COMBINE (h, obj->encoding_sa);
|
|
h = NM_HASH_COMBINE (h, obj->validation);
|
|
h = NM_HASH_COMBINE (h, obj->encrypt);
|
|
h = NM_HASH_COMBINE (h, obj->protect);
|
|
h = NM_HASH_COMBINE (h, obj->include_sci);
|
|
h = NM_HASH_COMBINE (h, obj->es);
|
|
h = NM_HASH_COMBINE (h, obj->scb);
|
|
h = NM_HASH_COMBINE (h, obj->replay_protect);
|
|
return h;
|
|
}
|
|
|
|
int
|
|
nm_platform_lnk_macsec_cmp (const NMPlatformLnkMacsec *a, const NMPlatformLnkMacsec *b)
|
|
{
|
|
NM_CMP_SELF (a, b);
|
|
NM_CMP_FIELD (a, b, sci);
|
|
NM_CMP_FIELD (a, b, icv_length);
|
|
NM_CMP_FIELD (a, b, cipher_suite);
|
|
NM_CMP_FIELD (a, b, window);
|
|
NM_CMP_FIELD (a, b, encoding_sa);
|
|
NM_CMP_FIELD (a, b, validation);
|
|
NM_CMP_FIELD_UNSAFE (a, b, encrypt);
|
|
NM_CMP_FIELD_UNSAFE (a, b, protect);
|
|
NM_CMP_FIELD_UNSAFE (a, b, include_sci);
|
|
NM_CMP_FIELD_UNSAFE (a, b, es);
|
|
NM_CMP_FIELD_UNSAFE (a, b, scb);
|
|
NM_CMP_FIELD_UNSAFE (a, b, replay_protect);
|
|
return 0;
|
|
}
|
|
|
|
guint
|
|
nm_platform_lnk_macvlan_hash (const NMPlatformLnkMacvlan *obj)
|
|
{
|
|
guint h = 771014989;
|
|
|
|
h = NM_HASH_COMBINE (h, obj->mode);
|
|
h = NM_HASH_COMBINE (h, obj->no_promisc);
|
|
h = NM_HASH_COMBINE (h, obj->tap);
|
|
return h;
|
|
}
|
|
|
|
int
|
|
nm_platform_lnk_macvlan_cmp (const NMPlatformLnkMacvlan *a, const NMPlatformLnkMacvlan *b)
|
|
{
|
|
NM_CMP_SELF (a, b);
|
|
NM_CMP_FIELD (a, b, mode);
|
|
NM_CMP_FIELD_UNSAFE (a, b, no_promisc);
|
|
NM_CMP_FIELD_UNSAFE (a, b, tap);
|
|
return 0;
|
|
}
|
|
|
|
guint
|
|
nm_platform_lnk_sit_hash (const NMPlatformLnkSit *obj)
|
|
{
|
|
guint h = 1690154969;
|
|
|
|
h = NM_HASH_COMBINE (h, obj->parent_ifindex);
|
|
h = NM_HASH_COMBINE (h, obj->local);
|
|
h = NM_HASH_COMBINE (h, obj->remote);
|
|
h = NM_HASH_COMBINE (h, obj->ttl);
|
|
h = NM_HASH_COMBINE (h, obj->tos);
|
|
h = NM_HASH_COMBINE (h, obj->path_mtu_discovery);
|
|
h = NM_HASH_COMBINE (h, obj->flags);
|
|
h = NM_HASH_COMBINE (h, obj->proto);
|
|
return h;
|
|
}
|
|
|
|
int
|
|
nm_platform_lnk_sit_cmp (const NMPlatformLnkSit *a, const NMPlatformLnkSit *b)
|
|
{
|
|
NM_CMP_SELF (a, b);
|
|
NM_CMP_FIELD (a, b, parent_ifindex);
|
|
NM_CMP_FIELD (a, b, local);
|
|
NM_CMP_FIELD (a, b, remote);
|
|
NM_CMP_FIELD (a, b, ttl);
|
|
NM_CMP_FIELD (a, b, tos);
|
|
NM_CMP_FIELD_BOOL (a, b, path_mtu_discovery);
|
|
NM_CMP_FIELD (a, b, flags);
|
|
NM_CMP_FIELD (a, b, proto);
|
|
return 0;
|
|
}
|
|
|
|
guint
|
|
nm_platform_lnk_vlan_hash (const NMPlatformLnkVlan *obj)
|
|
{
|
|
guint h = 58751383;
|
|
|
|
h = NM_HASH_COMBINE (h, obj->id);
|
|
h = NM_HASH_COMBINE (h, obj->flags);
|
|
return h;
|
|
}
|
|
|
|
int
|
|
nm_platform_lnk_vlan_cmp (const NMPlatformLnkVlan *a, const NMPlatformLnkVlan *b)
|
|
{
|
|
NM_CMP_SELF (a, b);
|
|
NM_CMP_FIELD (a, b, id);
|
|
NM_CMP_FIELD (a, b, flags);
|
|
return 0;
|
|
}
|
|
|
|
guint
|
|
nm_platform_lnk_vxlan_hash (const NMPlatformLnkVxlan *obj)
|
|
{
|
|
guint h = 461041297;
|
|
|
|
h = NM_HASH_COMBINE (h, obj->parent_ifindex);
|
|
h = NM_HASH_COMBINE (h, obj->id);
|
|
h = NM_HASH_COMBINE (h, obj->group);
|
|
h = NM_HASH_COMBINE (h, obj->local);
|
|
h = NM_HASH_COMBINE_IN6ADDR (h, &obj->group6);
|
|
h = NM_HASH_COMBINE_IN6ADDR (h, &obj->local6);
|
|
h = NM_HASH_COMBINE (h, obj->tos);
|
|
h = NM_HASH_COMBINE (h, obj->ttl);
|
|
h = NM_HASH_COMBINE (h, obj->learning);
|
|
h = NM_HASH_COMBINE (h, obj->ageing);
|
|
h = NM_HASH_COMBINE (h, obj->limit);
|
|
h = NM_HASH_COMBINE (h, obj->dst_port);
|
|
h = NM_HASH_COMBINE (h, obj->src_port_min);
|
|
h = NM_HASH_COMBINE (h, obj->src_port_max);
|
|
h = NM_HASH_COMBINE (h, obj->proxy);
|
|
h = NM_HASH_COMBINE (h, obj->rsc);
|
|
h = NM_HASH_COMBINE (h, obj->l2miss);
|
|
h = NM_HASH_COMBINE (h, obj->l3miss);
|
|
return h;
|
|
}
|
|
|
|
int
|
|
nm_platform_lnk_vxlan_cmp (const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan *b)
|
|
{
|
|
NM_CMP_SELF (a, b);
|
|
NM_CMP_FIELD (a, b, parent_ifindex);
|
|
NM_CMP_FIELD (a, b, id);
|
|
NM_CMP_FIELD (a, b, group);
|
|
NM_CMP_FIELD (a, b, local);
|
|
NM_CMP_FIELD_MEMCMP (a, b, group6);
|
|
NM_CMP_FIELD_MEMCMP (a, b, local6);
|
|
NM_CMP_FIELD (a, b, tos);
|
|
NM_CMP_FIELD (a, b, ttl);
|
|
NM_CMP_FIELD_BOOL (a, b, learning);
|
|
NM_CMP_FIELD (a, b, ageing);
|
|
NM_CMP_FIELD (a, b, limit);
|
|
NM_CMP_FIELD (a, b, dst_port);
|
|
NM_CMP_FIELD (a, b, src_port_min);
|
|
NM_CMP_FIELD (a, b, src_port_max);
|
|
NM_CMP_FIELD_BOOL (a, b, proxy);
|
|
NM_CMP_FIELD_BOOL (a, b, rsc);
|
|
NM_CMP_FIELD_BOOL (a, b, l2miss);
|
|
NM_CMP_FIELD_BOOL (a, b, l3miss);
|
|
return 0;
|
|
}
|
|
|
|
guint
|
|
nm_platform_ip4_address_hash (const NMPlatformIP4Address *obj)
|
|
{
|
|
guint h = 469681301;
|
|
|
|
if (obj) {
|
|
h = NM_HASH_COMBINE (h, obj->ifindex);
|
|
h = NM_HASH_COMBINE (h, obj->address);
|
|
h = NM_HASH_COMBINE (h, obj->plen);
|
|
h = NM_HASH_COMBINE (h, obj->peer_address);
|
|
h = NM_HASH_COMBINE (h, obj->addr_source);
|
|
h = NM_HASH_COMBINE (h, obj->timestamp);
|
|
h = NM_HASH_COMBINE (h, obj->lifetime);
|
|
h = NM_HASH_COMBINE (h, obj->preferred);
|
|
h = NM_HASH_COMBINE (h, obj->n_ifa_flags);
|
|
h = NM_HASH_COMBINE (h, g_str_hash (obj->label));
|
|
}
|
|
return h;
|
|
}
|
|
|
|
int
|
|
nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a, const NMPlatformIP4Address *b)
|
|
{
|
|
NM_CMP_SELF (a, b);
|
|
NM_CMP_FIELD (a, b, ifindex);
|
|
NM_CMP_FIELD (a, b, address);
|
|
NM_CMP_FIELD (a, b, plen);
|
|
NM_CMP_FIELD (a, b, peer_address);
|
|
NM_CMP_FIELD (a, b, addr_source);
|
|
NM_CMP_FIELD (a, b, timestamp);
|
|
NM_CMP_FIELD (a, b, lifetime);
|
|
NM_CMP_FIELD (a, b, preferred);
|
|
NM_CMP_FIELD (a, b, n_ifa_flags);
|
|
NM_CMP_FIELD_STR (a, b, label);
|
|
return 0;
|
|
}
|
|
|
|
guint
|
|
nm_platform_ip6_address_hash (const NMPlatformIP6Address *obj)
|
|
{
|
|
guint h = 605908909;
|
|
|
|
if (obj) {
|
|
h = NM_HASH_COMBINE (h, obj->ifindex);
|
|
h = NM_HASH_COMBINE_IN6ADDR (h, &obj->address);
|
|
h = NM_HASH_COMBINE (h, obj->plen);
|
|
h = NM_HASH_COMBINE_IN6ADDR (h, &obj->peer_address);
|
|
h = NM_HASH_COMBINE (h, obj->addr_source);
|
|
h = NM_HASH_COMBINE (h, obj->timestamp);
|
|
h = NM_HASH_COMBINE (h, obj->lifetime);
|
|
h = NM_HASH_COMBINE (h, obj->preferred);
|
|
h = NM_HASH_COMBINE (h, obj->n_ifa_flags);
|
|
}
|
|
return h;
|
|
}
|
|
|
|
int
|
|
nm_platform_ip6_address_cmp (const NMPlatformIP6Address *a, const NMPlatformIP6Address *b)
|
|
{
|
|
const struct in6_addr *p_a, *p_b;
|
|
|
|
NM_CMP_SELF (a, b);
|
|
NM_CMP_FIELD (a, b, ifindex);
|
|
NM_CMP_FIELD_MEMCMP (a, b, address);
|
|
NM_CMP_FIELD (a, b, plen);
|
|
p_a = nm_platform_ip6_address_get_peer (a);
|
|
p_b = nm_platform_ip6_address_get_peer (b);
|
|
NM_CMP_DIRECT_MEMCMP (p_a, p_b, sizeof (*p_a));
|
|
NM_CMP_FIELD (a, b, addr_source);
|
|
NM_CMP_FIELD (a, b, timestamp);
|
|
NM_CMP_FIELD (a, b, lifetime);
|
|
NM_CMP_FIELD (a, b, preferred);
|
|
NM_CMP_FIELD (a, b, n_ifa_flags);
|
|
return 0;
|
|
}
|
|
|
|
guint
|
|
nm_platform_ip4_route_hash (const NMPlatformIP4Route *obj, NMPlatformIPRouteCmpType cmp_type)
|
|
{
|
|
guint h = NM_HASH_COMBINE (1228913327, cmp_type);
|
|
|
|
if (obj) {
|
|
switch (cmp_type) {
|
|
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_DST:
|
|
h = NM_HASH_COMBINE (h, nm_utils_ip4_address_clear_host_address (obj->network, obj->plen));
|
|
h = NM_HASH_COMBINE (h, obj->plen);
|
|
break;
|
|
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID:
|
|
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID:
|
|
h = NM_HASH_COMBINE (h, nm_utils_ip4_address_clear_host_address (obj->network, obj->plen));
|
|
h = NM_HASH_COMBINE (h, obj->plen);
|
|
h = NM_HASH_COMBINE (h, obj->metric);
|
|
h = NM_HASH_COMBINE (h, obj->tos);
|
|
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) {
|
|
h = NM_HASH_COMBINE (h, obj->ifindex);
|
|
h = NM_HASH_COMBINE (h, obj->rt_source);
|
|
h = NM_HASH_COMBINE (h, obj->scope_inv);
|
|
h = NM_HASH_COMBINE (h, obj->gateway);
|
|
h = NM_HASH_COMBINE (h, obj->mss);
|
|
h = NM_HASH_COMBINE (h, obj->pref_src);
|
|
h = NM_HASH_COMBINE (h, obj->window);
|
|
h = NM_HASH_COMBINE (h, obj->cwnd);
|
|
h = NM_HASH_COMBINE (h, obj->initcwnd);
|
|
h = NM_HASH_COMBINE (h, obj->initrwnd);
|
|
h = NM_HASH_COMBINE (h, obj->mtu);
|
|
h = NM_HASH_COMBINE (h, obj->lock_window);
|
|
h = NM_HASH_COMBINE (h, obj->lock_cwnd);
|
|
h = NM_HASH_COMBINE (h, obj->lock_initcwnd);
|
|
h = NM_HASH_COMBINE (h, obj->lock_initrwnd);
|
|
h = NM_HASH_COMBINE (h, obj->lock_mtu);
|
|
}
|
|
break;
|
|
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY:
|
|
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL:
|
|
h = NM_HASH_COMBINE (h, obj->ifindex);
|
|
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY)
|
|
h = NM_HASH_COMBINE (h, nm_utils_ip4_address_clear_host_address (obj->network, obj->plen));
|
|
else
|
|
h = NM_HASH_COMBINE (h, obj->network);
|
|
h = NM_HASH_COMBINE (h, obj->plen);
|
|
h = NM_HASH_COMBINE (h, obj->metric);
|
|
h = NM_HASH_COMBINE (h, obj->gateway);
|
|
h = NM_HASH_COMBINE (h, obj->rt_source);
|
|
h = NM_HASH_COMBINE (h, obj->mss);
|
|
h = NM_HASH_COMBINE (h, obj->scope_inv);
|
|
h = NM_HASH_COMBINE (h, obj->pref_src);
|
|
h = NM_HASH_COMBINE (h, obj->rt_cloned);
|
|
h = NM_HASH_COMBINE (h, obj->tos);
|
|
h = NM_HASH_COMBINE (h, obj->lock_window);
|
|
h = NM_HASH_COMBINE (h, obj->lock_cwnd);
|
|
h = NM_HASH_COMBINE (h, obj->lock_initcwnd);
|
|
h = NM_HASH_COMBINE (h, obj->lock_initrwnd);
|
|
h = NM_HASH_COMBINE (h, obj->lock_mtu);
|
|
h = NM_HASH_COMBINE (h, obj->window);
|
|
h = NM_HASH_COMBINE (h, obj->cwnd);
|
|
h = NM_HASH_COMBINE (h, obj->initcwnd);
|
|
h = NM_HASH_COMBINE (h, obj->initrwnd);
|
|
h = NM_HASH_COMBINE (h, obj->mtu);
|
|
break;
|
|
}
|
|
}
|
|
return h;
|
|
}
|
|
|
|
int
|
|
nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, NMPlatformIPRouteCmpType cmp_type)
|
|
{
|
|
NM_CMP_SELF (a, b);
|
|
switch (cmp_type) {
|
|
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_DST:
|
|
NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX (a->network, b->network, MIN (a->plen, b->plen));
|
|
NM_CMP_FIELD (a, b, plen);
|
|
break;
|
|
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID:
|
|
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID:
|
|
NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX (a->network, b->network, MIN (a->plen, b->plen));
|
|
NM_CMP_FIELD (a, b, plen);
|
|
NM_CMP_FIELD (a, b, metric);
|
|
NM_CMP_FIELD (a, b, tos);
|
|
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) {
|
|
NM_CMP_FIELD (a, b, ifindex);
|
|
NM_CMP_FIELD (a, b, rt_source);
|
|
NM_CMP_FIELD (a, b, scope_inv);
|
|
NM_CMP_FIELD (a, b, gateway);
|
|
NM_CMP_FIELD (a, b, mss);
|
|
NM_CMP_FIELD (a, b, pref_src);
|
|
NM_CMP_FIELD (a, b, window);
|
|
NM_CMP_FIELD (a, b, cwnd);
|
|
NM_CMP_FIELD (a, b, initcwnd);
|
|
NM_CMP_FIELD (a, b, initrwnd);
|
|
NM_CMP_FIELD (a, b, mtu);
|
|
NM_CMP_FIELD_UNSAFE (a, b, lock_window);
|
|
NM_CMP_FIELD_UNSAFE (a, b, lock_cwnd);
|
|
NM_CMP_FIELD_UNSAFE (a, b, lock_initcwnd);
|
|
NM_CMP_FIELD_UNSAFE (a, b, lock_initrwnd);
|
|
NM_CMP_FIELD_UNSAFE (a, b, lock_mtu);
|
|
}
|
|
break;
|
|
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY:
|
|
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL:
|
|
NM_CMP_FIELD (a, b, ifindex);
|
|
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY)
|
|
NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX (a->network, b->network, MIN (a->plen, b->plen));
|
|
else
|
|
NM_CMP_FIELD (a, b, network);
|
|
NM_CMP_FIELD (a, b, plen);
|
|
NM_CMP_FIELD (a, b, metric);
|
|
NM_CMP_FIELD (a, b, gateway);
|
|
NM_CMP_FIELD (a, b, rt_source);
|
|
NM_CMP_FIELD (a, b, mss);
|
|
NM_CMP_FIELD (a, b, scope_inv);
|
|
NM_CMP_FIELD (a, b, pref_src);
|
|
NM_CMP_FIELD_UNSAFE (a, b, rt_cloned);
|
|
NM_CMP_FIELD (a, b, tos);
|
|
NM_CMP_FIELD_UNSAFE (a, b, lock_window);
|
|
NM_CMP_FIELD_UNSAFE (a, b, lock_cwnd);
|
|
NM_CMP_FIELD_UNSAFE (a, b, lock_initcwnd);
|
|
NM_CMP_FIELD_UNSAFE (a, b, lock_initrwnd);
|
|
NM_CMP_FIELD_UNSAFE (a, b, lock_mtu);
|
|
NM_CMP_FIELD (a, b, window);
|
|
NM_CMP_FIELD (a, b, cwnd);
|
|
NM_CMP_FIELD (a, b, initcwnd);
|
|
NM_CMP_FIELD (a, b, initrwnd);
|
|
NM_CMP_FIELD (a, b, mtu);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
guint
|
|
nm_platform_ip6_route_hash (const NMPlatformIP6Route *obj, NMPlatformIPRouteCmpType cmp_type)
|
|
{
|
|
guint h = NM_HASH_COMBINE (1053326051, cmp_type);
|
|
|
|
if (obj) {
|
|
switch (cmp_type) {
|
|
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_DST:
|
|
h = NM_HASH_COMBINE_IN6ADDR_PREFIX (h, &obj->network, obj->plen);
|
|
h = NM_HASH_COMBINE (h, obj->plen);
|
|
break;
|
|
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID:
|
|
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID:
|
|
h = NM_HASH_COMBINE_IN6ADDR_PREFIX (h, &obj->network, obj->plen);
|
|
h = NM_HASH_COMBINE (h, obj->plen);
|
|
h = NM_HASH_COMBINE (h, nm_utils_ip6_route_metric_normalize (obj->metric));
|
|
h = NM_HASH_COMBINE_IN6ADDR_PREFIX (h, &obj->src, obj->src_plen);
|
|
h = NM_HASH_COMBINE (h, obj->src_plen);
|
|
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) {
|
|
h = NM_HASH_COMBINE (h, obj->ifindex);
|
|
h = NM_HASH_COMBINE_IN6ADDR (h, &obj->gateway);
|
|
}
|
|
break;
|
|
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY:
|
|
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL:
|
|
h = NM_HASH_COMBINE (h, obj->ifindex);
|
|
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY)
|
|
h = NM_HASH_COMBINE_IN6ADDR_PREFIX (h, &obj->network, obj->plen);
|
|
else
|
|
h = NM_HASH_COMBINE_IN6ADDR (h, &obj->network);
|
|
h = NM_HASH_COMBINE (h, obj->plen);
|
|
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY)
|
|
h = NM_HASH_COMBINE (h, nm_utils_ip6_route_metric_normalize (obj->metric));
|
|
else
|
|
h = NM_HASH_COMBINE (h, obj->metric);
|
|
h = NM_HASH_COMBINE_IN6ADDR (h, &obj->gateway);
|
|
h = NM_HASH_COMBINE_IN6ADDR (h, &obj->pref_src);
|
|
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) {
|
|
h = NM_HASH_COMBINE_IN6ADDR_PREFIX (h, &obj->src, obj->src_plen);
|
|
h = NM_HASH_COMBINE (h, obj->src_plen);
|
|
} else {
|
|
h = NM_HASH_COMBINE_IN6ADDR (h, &obj->src);
|
|
h = NM_HASH_COMBINE (h, obj->src_plen);
|
|
}
|
|
h = NM_HASH_COMBINE (h, obj->rt_source);
|
|
h = NM_HASH_COMBINE (h, obj->mss);
|
|
h = NM_HASH_COMBINE (h, obj->rt_cloned);
|
|
h = NM_HASH_COMBINE (h, obj->lock_window);
|
|
h = NM_HASH_COMBINE (h, obj->lock_cwnd);
|
|
h = NM_HASH_COMBINE (h, obj->lock_initcwnd);
|
|
h = NM_HASH_COMBINE (h, obj->lock_initrwnd);
|
|
h = NM_HASH_COMBINE (h, obj->lock_mtu);
|
|
h = NM_HASH_COMBINE (h, obj->window);
|
|
h = NM_HASH_COMBINE (h, obj->cwnd);
|
|
h = NM_HASH_COMBINE (h, obj->initcwnd);
|
|
h = NM_HASH_COMBINE (h, obj->initrwnd);
|
|
h = NM_HASH_COMBINE (h, obj->mtu);
|
|
break;
|
|
}
|
|
}
|
|
return h;
|
|
}
|
|
|
|
int
|
|
nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, NMPlatformIPRouteCmpType cmp_type)
|
|
{
|
|
NM_CMP_SELF (a, b);
|
|
switch (cmp_type) {
|
|
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_DST:
|
|
NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX (&a->network, &b->network, MIN (a->plen, b->plen));
|
|
NM_CMP_FIELD (a, b, plen);
|
|
break;
|
|
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID:
|
|
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID:
|
|
NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX (&a->network, &b->network, MIN (a->plen, b->plen));
|
|
NM_CMP_FIELD (a, b, plen);
|
|
NM_CMP_DIRECT (nm_utils_ip6_route_metric_normalize (a->metric), nm_utils_ip6_route_metric_normalize (b->metric));
|
|
NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX (&a->src, &b->src, MIN (a->src_plen, b->src_plen));
|
|
NM_CMP_FIELD (a, b, src_plen);
|
|
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) {
|
|
NM_CMP_FIELD (a, b, ifindex);
|
|
NM_CMP_FIELD_IN6ADDR (a, b, gateway);
|
|
}
|
|
break;
|
|
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY:
|
|
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL:
|
|
NM_CMP_FIELD (a, b, ifindex);
|
|
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY)
|
|
NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX (&a->network, &b->network, MIN (a->plen, b->plen));
|
|
else
|
|
NM_CMP_FIELD_IN6ADDR (a, b, network);
|
|
NM_CMP_FIELD (a, b, plen);
|
|
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY)
|
|
NM_CMP_DIRECT (nm_utils_ip6_route_metric_normalize (a->metric), nm_utils_ip6_route_metric_normalize (b->metric));
|
|
else
|
|
NM_CMP_FIELD (a, b, metric);
|
|
NM_CMP_FIELD_IN6ADDR (a, b, gateway);
|
|
NM_CMP_FIELD_IN6ADDR (a, b, pref_src);
|
|
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) {
|
|
NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX (&a->src, &b->src, MIN (a->src_plen, b->src_plen));
|
|
NM_CMP_FIELD (a, b, src_plen);
|
|
} else {
|
|
NM_CMP_FIELD_IN6ADDR (a, b, src);
|
|
NM_CMP_FIELD (a, b, src_plen);
|
|
}
|
|
NM_CMP_FIELD (a, b, rt_source);
|
|
NM_CMP_FIELD (a, b, mss);
|
|
NM_CMP_FIELD_UNSAFE (a, b, rt_cloned);
|
|
NM_CMP_FIELD_UNSAFE (a, b, lock_window);
|
|
NM_CMP_FIELD_UNSAFE (a, b, lock_cwnd);
|
|
NM_CMP_FIELD_UNSAFE (a, b, lock_initcwnd);
|
|
NM_CMP_FIELD_UNSAFE (a, b, lock_initrwnd);
|
|
NM_CMP_FIELD_UNSAFE (a, b, lock_mtu);
|
|
NM_CMP_FIELD (a, b, window);
|
|
NM_CMP_FIELD (a, b, cwnd);
|
|
NM_CMP_FIELD (a, b, initcwnd);
|
|
NM_CMP_FIELD (a, b, initrwnd);
|
|
NM_CMP_FIELD (a, b, mtu);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* nm_platform_ip_address_cmp_expiry:
|
|
* @a: a NMPlatformIPAddress to compare
|
|
* @b: the other NMPlatformIPAddress to compare
|
|
*
|
|
* Compares two addresses and returns which one has a longer remaining lifetime.
|
|
* If both addresses have the same lifetime, look at the remaining preferred time.
|
|
*
|
|
* For comparison, only the timestamp, lifetime and preferred fields are considered.
|
|
* If they compare equal (== 0), their other fields were not considered.
|
|
*
|
|
* Returns: -1, 0, or 1 according to the comparison
|
|
**/
|
|
int
|
|
nm_platform_ip_address_cmp_expiry (const NMPlatformIPAddress *a, const NMPlatformIPAddress *b)
|
|
{
|
|
gint64 ta = 0, tb = 0;
|
|
|
|
NM_CMP_SELF (a, b);
|
|
|
|
if (a->lifetime == NM_PLATFORM_LIFETIME_PERMANENT || a->lifetime == 0)
|
|
ta = G_MAXINT64;
|
|
else if (a->timestamp)
|
|
ta = ((gint64) a->timestamp) + a->lifetime;
|
|
|
|
if (b->lifetime == NM_PLATFORM_LIFETIME_PERMANENT || b->lifetime == 0)
|
|
tb = G_MAXINT64;
|
|
else if (b->timestamp)
|
|
tb = ((gint64) b->timestamp) + b->lifetime;
|
|
|
|
if (ta == tb) {
|
|
/* if the lifetime is equal, compare the preferred time. */
|
|
ta = tb = 0;
|
|
|
|
if (a->preferred == NM_PLATFORM_LIFETIME_PERMANENT || a->lifetime == 0 /* liftime==0 means permanent! */)
|
|
ta = G_MAXINT64;
|
|
else if (a->timestamp)
|
|
ta = ((gint64) a->timestamp) + a->preferred;
|
|
|
|
if (b->preferred == NM_PLATFORM_LIFETIME_PERMANENT|| b->lifetime == 0)
|
|
tb = G_MAXINT64;
|
|
else if (b->timestamp)
|
|
tb = ((gint64) b->timestamp) + b->preferred;
|
|
|
|
if (ta == tb)
|
|
return 0;
|
|
}
|
|
|
|
return ta < tb ? -1 : 1;
|
|
}
|
|
|
|
const char *
|
|
nm_platform_signal_change_type_to_string (NMPlatformSignalChangeType change_type)
|
|
{
|
|
switch (change_type) {
|
|
case NM_PLATFORM_SIGNAL_ADDED:
|
|
return "added";
|
|
case NM_PLATFORM_SIGNAL_CHANGED:
|
|
return "changed";
|
|
case NM_PLATFORM_SIGNAL_REMOVED:
|
|
return "removed";
|
|
default:
|
|
g_return_val_if_reached ("UNKNOWN");
|
|
}
|
|
}
|
|
|
|
static void
|
|
log_link (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformLink *device, NMPlatformSignalChangeType change_type, gpointer user_data)
|
|
{
|
|
|
|
_LOGD ("signal: link %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_link_to_string (device, NULL, 0));
|
|
}
|
|
|
|
static void
|
|
log_ip4_address (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformIP4Address *address, NMPlatformSignalChangeType change_type, gpointer user_data)
|
|
{
|
|
_LOGD ("signal: address 4 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip4_address_to_string (address, NULL, 0));
|
|
}
|
|
|
|
static void
|
|
log_ip6_address (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformIP6Address *address, NMPlatformSignalChangeType change_type, gpointer user_data)
|
|
{
|
|
_LOGD ("signal: address 6 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip6_address_to_string (address, NULL, 0));
|
|
}
|
|
|
|
static void
|
|
log_ip4_route (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformIP4Route *route, NMPlatformSignalChangeType change_type, gpointer user_data)
|
|
{
|
|
_LOGD ("signal: route 4 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip4_route_to_string (route, NULL, 0));
|
|
}
|
|
|
|
static void
|
|
log_ip6_route (NMPlatform *self, NMPObjectType obj_type, int ifindex, NMPlatformIP6Route *route, NMPlatformSignalChangeType change_type, gpointer user_data)
|
|
{
|
|
_LOGD ("signal: route 6 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip6_route_to_string (route, NULL, 0));
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
void
|
|
nm_platform_cache_update_emit_signal (NMPlatform *self,
|
|
NMPCacheOpsType cache_op,
|
|
const NMPObject *obj_old,
|
|
const NMPObject *obj_new)
|
|
{
|
|
gboolean visible_new;
|
|
gboolean visible_old;
|
|
const NMPObject *o;
|
|
const NMPClass *klass;
|
|
|
|
nm_assert (NM_IN_SET ((NMPlatformSignalChangeType) cache_op, (NMPlatformSignalChangeType) NMP_CACHE_OPS_UNCHANGED, NM_PLATFORM_SIGNAL_ADDED, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_SIGNAL_REMOVED));
|
|
|
|
ASSERT_nmp_cache_ops (nm_platform_get_cache (self), cache_op, obj_old, obj_new);
|
|
|
|
NMTST_ASSERT_PLATFORM_NETNS_CURRENT (self);
|
|
|
|
switch (cache_op) {
|
|
case NMP_CACHE_OPS_ADDED:
|
|
if (!nmp_object_is_visible (obj_new))
|
|
return;
|
|
o = obj_new;
|
|
break;
|
|
case NMP_CACHE_OPS_UPDATED:
|
|
visible_old = nmp_object_is_visible (obj_old);
|
|
visible_new = nmp_object_is_visible (obj_new);
|
|
if (!visible_old && visible_new) {
|
|
o = obj_new;
|
|
cache_op = NMP_CACHE_OPS_ADDED;
|
|
} else if (visible_old && !visible_new) {
|
|
o = obj_old;
|
|
cache_op = NMP_CACHE_OPS_REMOVED;
|
|
} else if (!visible_new) {
|
|
/* it was invisible and stayed invisible. Nothing to do. */
|
|
return;
|
|
} else
|
|
o = obj_new;
|
|
break;
|
|
case NMP_CACHE_OPS_REMOVED:
|
|
if (!nmp_object_is_visible (obj_old))
|
|
return;
|
|
o = obj_old;
|
|
break;
|
|
default:
|
|
nm_assert (cache_op == NMP_CACHE_OPS_UNCHANGED);
|
|
return;
|
|
}
|
|
|
|
klass = NMP_OBJECT_GET_CLASS (o);
|
|
|
|
_LOGt ("emit signal %s %s: %s",
|
|
klass->signal_type,
|
|
nm_platform_signal_change_type_to_string ((NMPlatformSignalChangeType) cache_op),
|
|
nmp_object_to_string (o, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0));
|
|
|
|
nmp_object_ref (o);
|
|
g_signal_emit (self,
|
|
_nm_platform_signal_id_get (klass->signal_type_id),
|
|
0,
|
|
(int) klass->obj_type,
|
|
o->object.ifindex,
|
|
&o->object,
|
|
(int) cache_op);
|
|
nmp_object_unref (o);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
NMPCache *
|
|
nm_platform_get_cache (NMPlatform *self)
|
|
{
|
|
return NM_PLATFORM_GET_PRIVATE (self)->cache;
|
|
}
|
|
|
|
NMPNetns *
|
|
nm_platform_netns_get (NMPlatform *self)
|
|
{
|
|
_CHECK_SELF (self, klass, NULL);
|
|
|
|
return self->_netns;
|
|
}
|
|
|
|
gboolean
|
|
nm_platform_netns_push (NMPlatform *self, NMPNetns **netns)
|
|
{
|
|
g_return_val_if_fail (NM_IS_PLATFORM (self), FALSE);
|
|
|
|
if ( self->_netns
|
|
&& !nmp_netns_push (self->_netns)) {
|
|
NM_SET_OUT (netns, NULL);
|
|
return FALSE;
|
|
}
|
|
|
|
NM_SET_OUT (netns, self->_netns);
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static gboolean
|
|
_vtr_v4_route_add (NMPlatform *self,
|
|
NMPNlmFlags flags,
|
|
const NMPlatformIPXRoute *route,
|
|
int ifindex,
|
|
gint64 metric)
|
|
{
|
|
NMPlatformIP4Route rt = route->r4;
|
|
|
|
if (ifindex > 0)
|
|
rt.ifindex = ifindex;
|
|
if (metric >= 0)
|
|
rt.metric = metric;
|
|
|
|
return nm_platform_ip4_route_add (self, flags, &rt);
|
|
}
|
|
|
|
static gboolean
|
|
_vtr_v6_route_add (NMPlatform *self,
|
|
NMPNlmFlags flags,
|
|
const NMPlatformIPXRoute *route,
|
|
int ifindex,
|
|
gint64 metric)
|
|
{
|
|
NMPlatformIP6Route rt = route->r6;
|
|
|
|
if (ifindex > 0)
|
|
rt.ifindex = ifindex;
|
|
if (metric >= 0)
|
|
rt.metric = metric;
|
|
|
|
return nm_platform_ip6_route_add (self, flags, &rt);
|
|
}
|
|
|
|
static guint32
|
|
_vtr_v4_metric_normalize (guint32 metric)
|
|
{
|
|
return metric;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
const NMPlatformVTableRoute nm_platform_vtable_route_v4 = {
|
|
.is_ip4 = TRUE,
|
|
.obj_type = NMP_OBJECT_TYPE_IP4_ROUTE,
|
|
.addr_family = AF_INET,
|
|
.sizeof_route = sizeof (NMPlatformIP4Route),
|
|
.route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, NMPlatformIPRouteCmpType cmp_type)) nm_platform_ip4_route_cmp,
|
|
.route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip4_route_to_string,
|
|
.route_add = _vtr_v4_route_add,
|
|
.metric_normalize = _vtr_v4_metric_normalize,
|
|
};
|
|
|
|
const NMPlatformVTableRoute nm_platform_vtable_route_v6 = {
|
|
.is_ip4 = FALSE,
|
|
.obj_type = NMP_OBJECT_TYPE_IP6_ROUTE,
|
|
.addr_family = AF_INET6,
|
|
.sizeof_route = sizeof (NMPlatformIP6Route),
|
|
.route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, NMPlatformIPRouteCmpType cmp_type)) nm_platform_ip6_route_cmp,
|
|
.route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip6_route_to_string,
|
|
.route_add = _vtr_v6_route_add,
|
|
.metric_normalize = nm_utils_ip6_route_metric_normalize,
|
|
};
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
set_property (GObject *object, guint prop_id,
|
|
const GValue *value, GParamSpec *pspec)
|
|
{
|
|
NMPlatform *self = NM_PLATFORM (object);
|
|
NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self);
|
|
|
|
switch (prop_id) {
|
|
case PROP_NETNS_SUPPORT:
|
|
/* construct-only */
|
|
if (g_value_get_boolean (value)) {
|
|
NMPNetns *netns;
|
|
|
|
netns = nmp_netns_get_current ();
|
|
if (netns)
|
|
self->_netns = g_object_ref (netns);
|
|
}
|
|
break;
|
|
case PROP_USE_UDEV:
|
|
/* construct-only */
|
|
priv->use_udev = g_value_get_boolean (value);
|
|
break;
|
|
case PROP_LOG_WITH_PTR:
|
|
/* construct-only */
|
|
priv->log_with_ptr = g_value_get_boolean (value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
nm_platform_init (NMPlatform *self)
|
|
{
|
|
self->_priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_PLATFORM, NMPlatformPrivate);
|
|
}
|
|
|
|
static GObject *
|
|
constructor (GType type,
|
|
guint n_construct_params,
|
|
GObjectConstructParam *construct_params)
|
|
{
|
|
GObject *object;
|
|
NMPlatform *self;
|
|
NMPlatformPrivate *priv;
|
|
|
|
object = G_OBJECT_CLASS (nm_platform_parent_class)->constructor (type,
|
|
n_construct_params,
|
|
construct_params);
|
|
self = NM_PLATFORM (object);
|
|
priv = NM_PLATFORM_GET_PRIVATE (self);
|
|
|
|
priv->multi_idx = nm_dedup_multi_index_new ();
|
|
|
|
priv->cache = nmp_cache_new (nm_platform_get_multi_idx (self),
|
|
priv->use_udev);
|
|
return object;
|
|
}
|
|
|
|
static void
|
|
finalize (GObject *object)
|
|
{
|
|
NMPlatform *self = NM_PLATFORM (object);
|
|
NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self);
|
|
|
|
g_clear_object (&self->_netns);
|
|
nm_dedup_multi_index_unref (priv->multi_idx);
|
|
nmp_cache_free (priv->cache);
|
|
}
|
|
|
|
static void
|
|
nm_platform_class_init (NMPlatformClass *platform_class)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (platform_class);
|
|
|
|
g_type_class_add_private (object_class, sizeof (NMPlatformPrivate));
|
|
|
|
object_class->constructor = constructor;
|
|
object_class->set_property = set_property;
|
|
object_class->finalize = finalize;
|
|
|
|
platform_class->wifi_set_powersave = wifi_set_powersave;
|
|
|
|
g_object_class_install_property
|
|
(object_class, PROP_NETNS_SUPPORT,
|
|
g_param_spec_boolean (NM_PLATFORM_NETNS_SUPPORT, "", "",
|
|
NM_PLATFORM_NETNS_SUPPORT_DEFAULT,
|
|
G_PARAM_WRITABLE |
|
|
G_PARAM_CONSTRUCT_ONLY |
|
|
G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property
|
|
(object_class, PROP_USE_UDEV,
|
|
g_param_spec_boolean (NM_PLATFORM_USE_UDEV, "", "",
|
|
FALSE,
|
|
G_PARAM_WRITABLE |
|
|
G_PARAM_CONSTRUCT_ONLY |
|
|
G_PARAM_STATIC_STRINGS));
|
|
|
|
g_object_class_install_property
|
|
(object_class, PROP_LOG_WITH_PTR,
|
|
g_param_spec_boolean (NM_PLATFORM_LOG_WITH_PTR, "", "",
|
|
TRUE,
|
|
G_PARAM_WRITABLE |
|
|
G_PARAM_CONSTRUCT_ONLY |
|
|
G_PARAM_STATIC_STRINGS));
|
|
|
|
#define SIGNAL(signal, signal_id, method) \
|
|
G_STMT_START { \
|
|
signals[signal] = \
|
|
g_signal_new_class_handler (""signal_id"", \
|
|
G_OBJECT_CLASS_TYPE (object_class), \
|
|
G_SIGNAL_RUN_FIRST, \
|
|
G_CALLBACK (method), \
|
|
NULL, NULL, NULL, \
|
|
G_TYPE_NONE, 4, \
|
|
G_TYPE_INT, /* (int) NMPObjectType */ \
|
|
G_TYPE_INT, /* ifindex */ \
|
|
G_TYPE_POINTER /* const NMPObject * */, \
|
|
G_TYPE_INT /* (int) NMPlatformSignalChangeType */ \
|
|
); \
|
|
} G_STMT_END
|
|
|
|
/* Signals */
|
|
SIGNAL (NM_PLATFORM_SIGNAL_ID_LINK, NM_PLATFORM_SIGNAL_LINK_CHANGED, log_link);
|
|
SIGNAL (NM_PLATFORM_SIGNAL_ID_IP4_ADDRESS, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, log_ip4_address);
|
|
SIGNAL (NM_PLATFORM_SIGNAL_ID_IP6_ADDRESS, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, log_ip6_address);
|
|
SIGNAL (NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, log_ip4_route);
|
|
SIGNAL (NM_PLATFORM_SIGNAL_ID_IP6_ROUTE, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, log_ip6_route);
|
|
}
|