diff options
author | Thomas Haller <thaller@redhat.com> | 2015-11-02 13:58:45 +0100 |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2015-11-02 13:58:45 +0100 |
commit | 07c0a4952eb017e941e55fe601669818d73d021a (patch) | |
tree | 8ef49f079d9ea31383e6f5646cfdf2aa77514b22 | |
parent | 1a6f4d08ba41f6f5a9d2bbd38c5155c7791642c1 (diff) | |
parent | 5feda42813ddf5d51adf51c4e95cea51748a48fd (diff) | |
download | NetworkManager-07c0a4952eb017e941e55fe601669818d73d021a.tar.gz |
platform: merge branch 'th/platform-parse-nl-bgo754570'
https://bugzilla.gnome.org/show_bug.cgi?id=754570
56 files changed, 5947 insertions, 3407 deletions
diff --git a/configure.ac b/configure.ac index 57d3210018..c8e7913a48 100644 --- a/configure.ac +++ b/configure.ac @@ -526,37 +526,7 @@ fi AC_SUBST(NM_CONFIG_DEFAULT_LOGGING_AUDIT_TEXT) # libnl support for the linux platform -PKG_CHECK_MODULES(LIBNL, libnl-3.0 >= 3.2.8 libnl-route-3.0 libnl-genl-3.0) - -AC_CHECK_LIB([nl-route-3], [rtnl_link_inet6_get_addr_gen_mode], - ac_have_addr_gen_mode="1", - ac_have_addr_gen_mode="0") -AC_DEFINE_UNQUOTED(HAVE_LIBNL_INET6_ADDR_GEN_MODE, - $ac_have_addr_gen_mode, [Define if libnl has rtnl_link_inet6_get_addr_gen_mode()]) - -AC_MSG_CHECKING([Linux kernel IN6_ADDR_GEN_MODE enum]) -AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM( - [[#ifndef __user - #define __user - #endif - #include <linux/if_link.h>]], - [[int a = IN6_ADDR_GEN_MODE_EUI64; a++;]])], - [ac_have_kernel_gen_mode=yes], - [ac_have_kernel_gen_mode=no]) -AC_MSG_RESULT($ac_have_kernel_gen_mode) -if test "$ac_have_kernel_gen_mode" = yes; then - AC_DEFINE(HAVE_KERNEL_INET6_ADDR_GEN_MODE, 1, [Define if the kernel has IN6_ADDR_GEN_MODE_*]) -else - AC_DEFINE(HAVE_KERNEL_INET6_ADDR_GEN_MODE, 0, [Define if the kernel has IN6_ADDR_GEN_MODE_*]) -fi - -# IPv6 tokenized identifiers support in libnl -AC_CHECK_LIB([nl-route-3], [rtnl_link_inet6_get_token], - ac_have_ipv6_token="1", - ac_have_ipv6_token="0") -AC_DEFINE_UNQUOTED(HAVE_LIBNL_INET6_TOKEN, - $ac_have_ipv6_token, [Define if libnl has rtnl_link_inet6_get_token()]) +PKG_CHECK_MODULES(LIBNL, libnl-3.0 >= 3.2.8) # uuid library PKG_CHECK_MODULES(UUID, uuid) diff --git a/docs/libnm/Makefile.am b/docs/libnm/Makefile.am index 23631ddc24..5b96d5aaa2 100644 --- a/docs/libnm/Makefile.am +++ b/docs/libnm/Makefile.am @@ -39,6 +39,7 @@ IGNORE_HFILES= \ nm-dbus-helpers.h \ nm-core-internal.h \ nm-core-types.h \ + nm-core-types-internal.h \ nm-device-private.h \ nm-dhcp4-config.h \ nm-dhcp6-config.h \ diff --git a/include/nm-macros-internal.h b/include/nm-macros-internal.h index 5c1d61dbaf..95d6bbaa71 100644 --- a/include/nm-macros-internal.h +++ b/include/nm-macros-internal.h @@ -229,6 +229,18 @@ nm_clear_g_variant (GVariant **variant) return FALSE; } +static inline gboolean +nm_clear_g_cancellable (GCancellable **cancellable) +{ + if (cancellable && *cancellable) { + g_cancellable_cancel (*cancellable); + g_object_unref (*cancellable); + *cancellable = NULL; + return TRUE; + } + return FALSE; +} + /*****************************************************************************/ /* Determine whether @x is a power of two (@x being an integer type). @@ -316,6 +328,18 @@ nm_decode_version (guint version, guint *major, guint *minor, guint *micro) { *minor = (version & 0x0000FF00u) >> 8; *micro = (version & 0x000000FFu); } +/*****************************************************************************/ + +#define nm_sprintf_buf(buf, format, ...) ({ \ + char * _buf = (buf); \ + \ + /* some static assert trying to ensure that the buffer is statically allocated. + * It disallows a buffer size of sizeof(gpointer) to catch that. */ \ + G_STATIC_ASSERT (G_N_ELEMENTS (buf) == sizeof (buf) && sizeof (buf) != sizeof (char *)); \ + g_snprintf (_buf, sizeof (buf), \ + ""format"", __VA_ARGS__); \ + _buf; \ + }) /*****************************************************************************/ diff --git a/include/nm-test-utils.h b/include/nm-test-utils.h index fa795b5a99..9f40be9c12 100644 --- a/include/nm-test-utils.h +++ b/include/nm-test-utils.h @@ -400,6 +400,8 @@ __nmtst_init (int *argc, char ***argv, gboolean assert_logging, const char *log_ *out_set_logging = TRUE; #endif g_assert (success); + if (__nmtst_internal.no_expect_message) + g_log_set_always_fatal (G_LOG_FATAL_MASK); } else if (__nmtst_internal.no_expect_message) { /* We have a test that would be assert_logging, but the user specified no_expect_message. * This transforms g_test_expect_message() into a NOP, but we also have to relax @@ -583,6 +585,47 @@ nmtst_get_rand_int (void) return g_rand_int (nmtst_get_rand ()); } +inline static void * +nmtst_rand_perm (GRand *rand, void *dst, const void *src, gsize elmt_size, gsize n_elmt) +{ + gsize i, j; + char *p_, *pj; + char *bu; + + g_assert (dst); + g_assert (elmt_size > 0); + g_assert (n_elmt < G_MAXINT32); + + if (n_elmt == 0) + return dst; + + if (src && dst != src) + memcpy (dst, src, elmt_size * n_elmt); + + if (!rand) + rand = nmtst_get_rand (); + + bu = g_slice_alloc (elmt_size); + + p_ = dst; + for (i = n_elmt; i > 1; i--) { + j = g_rand_int_range (rand, 0, i); + + if (j != 0) { + pj = &p_[j * elmt_size]; + + /* swap */ + memcpy (bu, p_, elmt_size); + memcpy (p_, pj, elmt_size); + memcpy (pj, bu, elmt_size); + } + p_ += elmt_size; + } + + g_slice_free1 (elmt_size, bu); + return dst; +} + inline static const char * nmtst_get_sudo_cmd (void) { @@ -689,6 +732,37 @@ nmtst_inet6_from_string (const char *str) } inline static void +_nmtst_assert_ip4_address (const char *file, int line, in_addr_t addr, const char *str_expected) +{ + if (nmtst_inet4_from_string (str_expected) != addr) { + char buf[100]; + + g_error ("%s:%d: Unexpected IPv4 address: expected %s, got %s", + file, line, str_expected ? str_expected : "0.0.0.0", + inet_ntop (AF_INET, &addr, buf, sizeof (buf))); + } +} +#define nmtst_assert_ip4_address(addr, str_expected) _nmtst_assert_ip4_address (__FILE__, __LINE__, addr, str_expected) + +inline static void +_nmtst_assert_ip6_address (const char *file, int line, const struct in6_addr *addr, const char *str_expected) +{ + struct in6_addr any = in6addr_any; + + if (!addr) + addr = &any; + + if (memcmp (nmtst_inet6_from_string (str_expected), addr, sizeof (*addr)) != 0) { + char buf[100]; + + g_error ("%s:%d: Unexpected IPv6 address: expected %s, got %s", + file, line, str_expected ? str_expected : "::", + inet_ntop (AF_INET6, &addr, buf, sizeof (buf))); + } +} +#define nmtst_assert_ip6_address(addr, str_expected) _nmtst_assert_ip6_address (__FILE__, __LINE__, addr, str_expected) + +inline static void FAIL(const char *test_name, const char *fmt, ...) { va_list args; @@ -909,9 +983,11 @@ nmtst_platform_ip4_routes_equal (const NMPlatformIP4Route *a, const NMPlatformIP for (i = 0; i < len; i++) { if (nm_platform_ip4_route_cmp (&a[i], &b[i]) != 0) { + char buf[sizeof (_nm_utils_to_string_buffer)]; + g_error ("Error comparing IPv4 route[%lu]: %s vs %s", (long unsigned) i, - nmtst_static_1024_01 (nm_platform_ip4_route_to_string (&a[i])), - nmtst_static_1024_02 (nm_platform_ip4_route_to_string (&b[i]))); + nm_platform_ip4_route_to_string (&a[i], NULL, 0), + nm_platform_ip4_route_to_string (&b[i], buf, sizeof (buf))); g_assert_not_reached (); } } @@ -941,9 +1017,11 @@ nmtst_platform_ip6_routes_equal (const NMPlatformIP6Route *a, const NMPlatformIP for (i = 0; i < len; i++) { if (nm_platform_ip6_route_cmp (&a[i], &b[i]) != 0) { + char buf[sizeof (_nm_utils_to_string_buffer)]; + g_error ("Error comparing IPv6 route[%lu]: %s vs %s", (long unsigned) i, - nmtst_static_1024_01 (nm_platform_ip6_route_to_string (&a[i])), - nmtst_static_1024_02 (nm_platform_ip6_route_to_string (&b[i]))); + nm_platform_ip6_route_to_string (&a[i], NULL, 0), + nm_platform_ip6_route_to_string (&b[i], buf, sizeof (buf))); g_assert_not_reached (); } } diff --git a/libnm-core/Makefile.libnm-core b/libnm-core/Makefile.libnm-core index b5e205acef..fb201bdbad 100644 --- a/libnm-core/Makefile.libnm-core +++ b/libnm-core/Makefile.libnm-core @@ -51,6 +51,7 @@ libnm_core_private_headers = \ $(core)/crypto.h \ $(core)/nm-connection-private.h \ $(core)/nm-core-internal.h \ + $(core)/nm-core-types-internal.h \ $(core)/nm-keyfile-internal.h \ $(core)/nm-keyfile-utils.h \ $(core)/nm-property-compare.h \ diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 26c30b8a87..4c95d0d0a7 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -67,12 +67,7 @@ #include "nm-simple-connection.h" #include "nm-utils.h" #include "nm-vpn-dbus-interface.h" - -#define NM_UTILS_CLEAR_CANCELLABLE(c) \ - if (c) { \ - g_cancellable_cancel (c); \ - g_clear_object (&c); \ - } +#include "nm-core-types-internal.h" /* NM_SETTING_COMPARE_FLAG_INFERRABLE: check whether a device-generated * connection can be replaced by a already-defined connection. This flag only @@ -256,4 +251,15 @@ NMUtilsStrStrDictKey *_nm_utils_strstrdictkey_create (const char *v1, const char /***********************************************************/ +gboolean _nm_setting_vlan_set_priorities (NMSettingVlan *setting, + NMVlanPriorityMap map, + const NMVlanQosMapping *qos_map, + guint n_qos_map); +void _nm_setting_vlan_get_priorities (NMSettingVlan *setting, + NMVlanPriorityMap map, + NMVlanQosMapping **out_qos_map, + guint *out_n_qos_map); + +/***********************************************************/ + #endif diff --git a/libnm-core/nm-core-types-internal.h b/libnm-core/nm-core-types-internal.h new file mode 100644 index 0000000000..442a10a30f --- /dev/null +++ b/libnm-core/nm-core-types-internal.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2015 Red Hat, Inc. + */ + +#ifndef NM_CORE_TYPES_INTERNAL_H +#define NM_CORE_TYPES_INTERNAL_H + +typedef struct { + guint32 from; + guint32 to; +} NMVlanQosMapping; + +#endif /* NM_CORE_TYPES_INTERNAL_H */ diff --git a/libnm-core/nm-setting-vlan.c b/libnm-core/nm-setting-vlan.c index 66661ec613..8428a36904 100644 --- a/libnm-core/nm-setting-vlan.c +++ b/libnm-core/nm-setting-vlan.c @@ -25,7 +25,10 @@ #include <string.h> #include "nm-setting-vlan.h" +#include "nm-default.h" +#include "nm-macros-internal.h" #include "nm-utils.h" +#include "nm-core-types-internal.h" #include "nm-setting-connection.h" #include "nm-setting-private.h" #include "nm-setting-wired.h" @@ -66,11 +69,6 @@ enum { #define MAX_SKB_PRIO G_MAXUINT32 #define MAX_8021P_PRIO 7 /* Max 802.1p priority */ -typedef struct { - guint32 from; - guint32 to; -} PriorityMap; - /** * nm_setting_vlan_new: * @@ -133,10 +131,10 @@ get_max_prio (NMVlanPriorityMap map, gboolean from) g_assert_not_reached (); } -static PriorityMap * +static NMVlanQosMapping * priority_map_new_from_str (NMVlanPriorityMap map, const char *str) { - PriorityMap *p = NULL; + NMVlanQosMapping *p = NULL; gchar **t = NULL; guint32 len; guint64 from, to; @@ -150,7 +148,7 @@ priority_map_new_from_str (NMVlanPriorityMap map, const char *str) to = g_ascii_strtoull (t[1], NULL, 10); if ((from <= get_max_prio (map, TRUE)) && (to <= get_max_prio (map, FALSE))) { - p = g_malloc0 (sizeof (PriorityMap)); + p = g_malloc0 (sizeof (NMVlanQosMapping)); p->from = from; p->to = to; } @@ -164,7 +162,7 @@ priority_map_new_from_str (NMVlanPriorityMap map, const char *str) } static void -priority_map_free (PriorityMap *map) +priority_map_free (NMVlanQosMapping *map) { g_return_if_fail (map != NULL); g_free (map); @@ -182,8 +180,11 @@ get_map (NMSettingVlan *self, NMVlanPriorityMap map) } static gint -prio_map_compare (PriorityMap *a, PriorityMap *b) +prio_map_compare (gconstpointer p_a, gconstpointer p_b) { + const NMVlanQosMapping *a = p_a; + const NMVlanQosMapping *b = p_b; + return a->from < b->from ? -1 : (a->from > b->from @@ -194,11 +195,27 @@ prio_map_compare (PriorityMap *a, PriorityMap *b) static void set_map (NMSettingVlan *self, NMVlanPriorityMap map, GSList *list) { - /* Sort the list. - * First, it looks better. Second, it assures that comparing lists works - * as expected. - */ - list = g_slist_sort (list, (GCompareFunc) prio_map_compare); + /* Assert that the list is sorted */ +#if NM_MORE_ASSERTS >= 2 + { + GSList *iter, *last; + + last = list; + iter = list ? list->next : NULL; + while (iter) { + const NMVlanQosMapping *l = last->data; + const NMVlanQosMapping *m = iter->data; + + nm_assert (prio_map_compare (last->data, iter->data) < 0); + + /* Also reject duplicates (based on "from") */ + nm_assert (l->from < m->from); + + last = iter; + iter = iter->next; + } + } +#endif if (map == NM_VLAN_INGRESS_MAP) { NM_SETTING_VLAN_GET_PRIVATE (self)->ingress_priority_map = list; @@ -214,7 +231,7 @@ static gboolean check_replace_duplicate_priority (GSList *list, guint32 from, guint32 to) { GSList *iter; - PriorityMap *p; + NMVlanQosMapping *p; for (iter = list; iter; iter = g_slist_next (iter)) { p = iter->data; @@ -245,7 +262,7 @@ nm_setting_vlan_add_priority_str (NMSettingVlan *setting, const char *str) { GSList *list = NULL; - PriorityMap *item = NULL; + NMVlanQosMapping *item = NULL; g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), FALSE); g_return_val_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP, FALSE); @@ -267,7 +284,7 @@ nm_setting_vlan_add_priority_str (NMSettingVlan *setting, return TRUE; } - set_map (setting, map, g_slist_append (list, item)); + set_map (setting, map, g_slist_insert_sorted (list, item, prio_map_compare)); return TRUE; } @@ -312,7 +329,7 @@ nm_setting_vlan_get_priority (NMSettingVlan *setting, guint32 *out_to) { GSList *list = NULL; - PriorityMap *item = NULL; + NMVlanQosMapping *item = NULL; g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), FALSE); g_return_val_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP, FALSE); @@ -357,7 +374,7 @@ nm_setting_vlan_add_priority (NMSettingVlan *setting, guint32 to) { GSList *list = NULL; - PriorityMap *item; + NMVlanQosMapping *item; g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), FALSE); g_return_val_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP, FALSE); @@ -371,14 +388,96 @@ nm_setting_vlan_add_priority (NMSettingVlan *setting, return TRUE; } - item = g_malloc0 (sizeof (PriorityMap)); + item = g_malloc0 (sizeof (NMVlanQosMapping)); item->from = from; item->to = to; - set_map (setting, map, g_slist_append (list, item)); + set_map (setting, map, g_slist_insert_sorted (list, item, prio_map_compare)); return TRUE; } +gboolean +_nm_setting_vlan_set_priorities (NMSettingVlan *setting, + NMVlanPriorityMap map, + const NMVlanQosMapping *qos_map, + guint n_qos_map) +{ + gboolean has_changes = FALSE; + GSList *map_prev, *map_new; + guint i; + gint64 from_last; + + map_prev = get_map (setting, map); + + if (n_qos_map != g_slist_length (map_prev)) + has_changes = TRUE; + else { + const GSList *iter; + + iter = map_prev; + for (i = 0; i < n_qos_map; i++, iter = iter->next) { + const NMVlanQosMapping *m = iter->data; + + if ( m->from != qos_map[i].from + || m->to != qos_map[i].to) { + has_changes = TRUE; + break; + } + } + } + + if (!has_changes) + return FALSE; + + map_new = NULL; + from_last = G_MAXINT64; + for (i = n_qos_map; i > 0;) { + const NMVlanQosMapping *m = &qos_map[--i]; + NMVlanQosMapping *item; + + /* We require the array to be presorted. */ + if (m->from >= from_last) + g_return_val_if_reached (FALSE); + from_last = m->from; + + item = g_malloc0 (sizeof (NMVlanQosMapping)); + item->from = m->from; + item->to = m->to; + map_new = g_slist_prepend (map_new, item); + } + + g_slist_free_full (map_prev, g_free); + set_map (setting, map, map_new); + + return TRUE; +} + +void +_nm_setting_vlan_get_priorities (NMSettingVlan *setting, + NMVlanPriorityMap map, + NMVlanQosMapping **out_qos_map, + guint *out_n_qos_map) +{ + GSList *list; + NMVlanQosMapping *qos_map = NULL; + guint n_qos_map, i; + + list = get_map (setting, map); + + n_qos_map = g_slist_length (list); + + if (n_qos_map > 0) { + qos_map = g_new (NMVlanQosMapping, n_qos_map); + + for (i = 0; list; i++, list = list->next) { + nm_assert (i < n_qos_map); + qos_map[i] = *((const NMVlanQosMapping *) list->data); + } + } + *out_qos_map = qos_map; + *out_n_qos_map = n_qos_map; +} + /** * nm_setting_vlan_remove_priority: * @setting: the #NMSettingVlan @@ -403,7 +502,7 @@ nm_setting_vlan_remove_priority (NMSettingVlan *setting, g_return_if_fail (idx < g_slist_length (list)); item = g_slist_nth (list, idx); - priority_map_free ((PriorityMap *) (item->data)); + priority_map_free ((NMVlanQosMapping *) (item->data)); set_map (setting, map, g_slist_delete_link (list, item)); } @@ -427,7 +526,7 @@ nm_setting_vlan_remove_priority_by_value (NMSettingVlan *setting, guint32 to) { GSList *list = NULL, *iter = NULL; - PriorityMap *item; + NMVlanQosMapping *item; g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), FALSE); g_return_val_if_fail (map == NM_VLAN_INGRESS_MAP || map == NM_VLAN_EGRESS_MAP, FALSE); @@ -436,7 +535,7 @@ nm_setting_vlan_remove_priority_by_value (NMSettingVlan *setting, for (iter = list; iter; iter = g_slist_next (iter)) { item = iter->data; if (item->from == from && item->to == to) { - priority_map_free ((PriorityMap *) (iter->data)); + priority_map_free ((NMVlanQosMapping *) (iter->data)); set_map (setting, map, g_slist_delete_link (list, iter)); return TRUE; } @@ -461,7 +560,7 @@ nm_setting_vlan_remove_priority_str_by_value (NMSettingVlan *setting, NMVlanPriorityMap map, const char *str) { - PriorityMap *item; + NMVlanQosMapping *item; gboolean found; g_return_val_if_fail (NM_IS_SETTING_VLAN (setting), FALSE); @@ -604,7 +703,7 @@ priority_strv_to_maplist (NMVlanPriorityMap map, char **strv) int i; for (i = 0; strv && strv[i]; i++) { - PriorityMap *item; + NMVlanQosMapping *item; item = priority_map_new_from_str (map, strv[i]); if (item) { @@ -612,7 +711,7 @@ priority_strv_to_maplist (NMVlanPriorityMap map, char **strv) list = g_slist_prepend (list, item); } } - return g_slist_sort (list, (GCompareFunc) prio_map_compare); + return g_slist_sort (list, prio_map_compare); } static void @@ -658,7 +757,7 @@ priority_maplist_to_strv (GSList *list) strv = g_ptr_array_new (); for (iter = list; iter; iter = g_slist_next (iter)) { - PriorityMap *item = iter->data; + NMVlanQosMapping *item = iter->data; g_ptr_array_add (strv, g_strdup_printf ("%d:%d", item->from, item->to)); } diff --git a/libnm/nm-manager.c b/libnm/nm-manager.c index c0d026291a..7ed50e3ced 100644 --- a/libnm/nm-manager.c +++ b/libnm/nm-manager.c @@ -35,6 +35,7 @@ #include "nm-vpn-connection.h" #include "nm-object-cache.h" #include "nm-dbus-helpers.h" +#include "nm-macros-internal.h" #include "nmdbus-manager.h" @@ -1255,7 +1256,7 @@ nm_running_changed_cb (GObject *object, NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager); if (!nm_manager_get_nm_running (manager)) { - NM_UTILS_CLEAR_CANCELLABLE (priv->props_cancellable); + nm_clear_g_cancellable (&priv->props_cancellable); priv->state = NM_STATE_UNKNOWN; priv->startup = FALSE; @@ -1281,7 +1282,7 @@ nm_running_changed_cb (GObject *object, } else { _nm_object_suppress_property_updates (NM_OBJECT (manager), FALSE); - NM_UTILS_CLEAR_CANCELLABLE (priv->props_cancellable); + nm_clear_g_cancellable (&priv->props_cancellable); priv->props_cancellable = g_cancellable_new (); _nm_object_reload_properties_async (NM_OBJECT (manager), priv->props_cancellable, updated_properties, manager); diff --git a/libnm/nm-remote-settings.c b/libnm/nm-remote-settings.c index 4d240e3b38..480d7c19f5 100644 --- a/libnm/nm-remote-settings.c +++ b/libnm/nm-remote-settings.c @@ -34,6 +34,7 @@ #include "nm-dbus-helpers.h" #include "nm-object-private.h" #include "nm-core-internal.h" +#include "nm-macros-internal.h" #include "nmdbus-settings.h" @@ -626,7 +627,7 @@ nm_running_changed (GObject *object, GPtrArray *connections; int i; - NM_UTILS_CLEAR_CANCELLABLE (priv->props_cancellable); + nm_clear_g_cancellable (&priv->props_cancellable); /* Clear connections */ connections = priv->all_connections; @@ -651,7 +652,7 @@ nm_running_changed (GObject *object, } else { _nm_object_suppress_property_updates (NM_OBJECT (self), FALSE); - NM_UTILS_CLEAR_CANCELLABLE (priv->props_cancellable); + nm_clear_g_cancellable (&priv->props_cancellable); priv->props_cancellable = g_cancellable_new (); _nm_object_reload_properties_async (NM_OBJECT (self), priv->props_cancellable, updated_properties, self); } diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index 726ceb9b4a..deab2a5869 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -1725,6 +1725,147 @@ nm_match_spec_join (GSList *specs) return g_string_free (str, FALSE); } +/*****************************************************************************/ + +char _nm_utils_to_string_buffer[]; + +void +nm_utils_to_string_buffer_init (char **buf, gsize *len) +{ + if (!*buf) { + *buf = _nm_utils_to_string_buffer; + *len = sizeof (_nm_utils_to_string_buffer); + } +} + +gboolean +nm_utils_to_string_buffer_init_null (gconstpointer obj, char **buf, gsize *len) +{ + nm_utils_to_string_buffer_init (buf, len); + if (!obj) { + g_strlcpy (*buf, "(null)", *len); + return FALSE; + } + return TRUE; +} + +void +nm_utils_strbuf_append_c (char **buf, gsize *len, char c) +{ + switch (*len) { + case 0: + return; + case 1: + (*buf)[0] = '\0'; + *len = 0; + (*buf)++; + return; + default: + (*buf)[0] = c; + (*buf)[1] = '\0'; + (*len)--; + (*buf)++; + return; + } +} + +void +nm_utils_strbuf_append_str (char **buf, gsize *len, const char *str) +{ + gsize src_len; + + switch (*len) { + case 0: + return; + case 1: + if (!str || !*str) { + (*buf)[0] = '\0'; + return; + } + (*buf)[0] = '\0'; + *len = 0; + (*buf)++; + return; + default: + if (!str || !*str) { + (*buf)[0] = '\0'; + return; + } + src_len = g_strlcpy (*buf, str, *len); + if (src_len >= *len) { + *buf = &(*buf)[*len]; + *len = 0; + } else { + *buf = &(*buf)[src_len]; + *len -= src_len; + } + return; + } +} + +void +nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...) +{ + char *p = *buf; + va_list args; + gint retval; + + if (*len == 0) + return; + + va_start (args, format); + retval = g_vsnprintf (p, *len, format, args); + va_end (args); + + if (retval >= *len) { + *buf = &p[*len]; + *len = 0; + } else { + *buf = &p[retval]; + *len -= retval; + } +} + +const char * +nm_utils_flags2str (const NMUtilsFlags2StrDesc *descs, + gsize n_descs, + unsigned flags, + char *buf, + gsize len) +{ + gsize i; + char *p; + + nm_utils_to_string_buffer_init (&buf, &len); + + if (!len) + return buf; + + buf[0] = '\0'; + if (!flags) { + return buf; + } + + p = buf; + for (i = 0; flags && i < n_descs; i++) { + if (NM_FLAGS_HAS (flags, descs[i].flag)) { + flags &= ~descs[i].flag; + + if (buf[0] != '\0') + nm_utils_strbuf_append_c (&p, &len, ','); + nm_utils_strbuf_append_str (&p, &len, descs[i].name); + } + } + if (flags) { + if (buf[0] != '\0') + nm_utils_strbuf_append_c (&p, &len, ','); + nm_utils_strbuf_append (&p, &len, "0x%x", flags); + } + return buf; +}; + +/*****************************************************************************/ + /** * nm_utils_get_shared_wifi_permission: * @connection: the NMConnection to lookup the permission. diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index bf90e741cd..3da2235559 100644 --- a/src/NetworkManagerUtils.h +++ b/src/NetworkManagerUtils.h @@ -191,6 +191,28 @@ NMMatchSpecMatchType nm_match_spec_match_config (const GSList *specs, guint nm_v GSList *nm_match_spec_split (const char *value); char *nm_match_spec_join (GSList *specs); +extern char _nm_utils_to_string_buffer[2096]; + +void nm_utils_to_string_buffer_init (char **buf, gsize *len); +gboolean nm_utils_to_string_buffer_init_null (gconstpointer obj, char **buf, gsize *len); + +typedef struct { + unsigned flag; + const char *name; +} NMUtilsFlags2StrDesc; + +#define NM_UTILS_FLAGS2STR(f, n) { .flag = f, .name = ""n, } + +const char *nm_utils_flags2str (const NMUtilsFlags2StrDesc *descs, + gsize n_descs, + unsigned flags, + char *buf, + gsize len); + +void nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...) __attribute__((__format__ (__printf__, 3, 4))); +void nm_utils_strbuf_append_c (char **buf, gsize *len, char c); +void nm_utils_strbuf_append_str (char **buf, gsize *len, const char *str); + const char *nm_utils_get_shared_wifi_permission (NMConnection *connection); const char *nm_utils_get_ip_config_method (NMConnection *connection, diff --git a/src/devices/nm-device-gre.c b/src/devices/nm-device-gre.c index ddd913b586..9b9ffc9be1 100644 --- a/src/devices/nm-device-gre.c +++ b/src/devices/nm-device-gre.c @@ -41,7 +41,7 @@ G_DEFINE_TYPE (NMDeviceGre, nm_device_gre, NM_TYPE_DEVICE_GENERIC) #define NM_DEVICE_GRE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_GRE, NMDeviceGrePrivate)) typedef struct { - NMPlatformGreProperties props; + NMPlatformLnkGre props; } NMDeviceGrePrivate; enum { @@ -68,37 +68,38 @@ update_properties (NMDevice *device) NMDeviceGre *self = NM_DEVICE_GRE (device); NMDeviceGrePrivate *priv = NM_DEVICE_GRE_GET_PRIVATE (self); GObject *object = G_OBJECT (device); - NMPlatformGreProperties props; + const NMPlatformLnkGre *props; - if (!nm_platform_gre_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (device), &props)) { + props = nm_platform_link_get_lnk_gre (NM_PLATFORM_GET, nm_device_get_ifindex (device), NULL); + if (!props) { _LOGW (LOGD_HW, "could not read gre properties"); return; } g_object_freeze_notify (object); - if (priv->props.parent_ifindex != props.parent_ifindex) + if (priv->props.parent_ifindex != props->parent_ifindex) g_object_notify (object, NM_DEVICE_GRE_PARENT); - if (priv->props.input_flags != props.input_flags) + if (priv->props.input_flags != props->input_flags) g_object_notify (object, NM_DEVICE_GRE_INPUT_FLAGS); - if (priv->props.output_flags != props.output_flags) + if (priv->props.output_flags != props->output_flags) g_object_notify (object, NM_DEVICE_GRE_OUTPUT_FLAGS); - if (priv->props.input_key != props.input_key) + if (priv->props.input_key != props->input_key) g_object_notify (object, NM_DEVICE_GRE_INPUT_KEY); - if (priv->props.output_key != props.output_key) + if (priv->props.output_key != props->output_key) g_object_notify (object, NM_DEVICE_GRE_OUTPUT_KEY); - if (priv->props.local != props.local) + if (priv->props.local != props->local) g_object_notify (object, NM_DEVICE_GRE_LOCAL); - if (priv->props.remote != props.remote) + if (priv->props.remote != props->remote) g_object_notify (object, NM_DEVICE_GRE_REMOTE); - if (priv->props.ttl != props.ttl) + if (priv->props.ttl != props->ttl) g_object_notify (object, NM_DEVICE_GRE_TTL); - if (priv->props.tos != props.tos) + if (priv->props.tos != props->tos) g_object_notify (object, NM_DEVICE_GRE_TOS); - if (priv->props.path_mtu_discovery != props.path_mtu_discovery) + if (priv->props.path_mtu_discovery != props->path_mtu_discovery) g_object_notify (object, NM_DEVICE_GRE_PATH_MTU_DISCOVERY); - memcpy (&priv->props, &props, sizeof (NMPlatformGreProperties)); + priv->props = *props; g_object_thaw_notify (object); } diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c index 05bf0323af..a647245f63 100644 --- a/src/devices/nm-device-infiniband.c +++ b/src/devices/nm-device-infiniband.c @@ -226,7 +226,7 @@ update_connection (NMDevice *device, NMConnection *connection) ifindex = nm_device_get_ifindex (device); if (ifindex > 0) { - if (!nm_platform_infiniband_get_info (NM_PLATFORM_GET, ifindex, NULL, NULL, &transport_mode)) + if (!nm_platform_infiniband_get_properties (NM_PLATFORM_GET, ifindex, NULL, NULL, &transport_mode)) transport_mode = "datagram"; } g_object_set (G_OBJECT (s_infiniband), NM_SETTING_INFINIBAND_TRANSPORT_MODE, transport_mode, NULL); diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c index 26ea0be9b7..e2dd891c1a 100644 --- a/src/devices/nm-device-macvlan.c +++ b/src/devices/nm-device-macvlan.c @@ -39,7 +39,8 @@ G_DEFINE_TYPE (NMDeviceMacvlan, nm_device_macvlan, NM_TYPE_DEVICE_GENERIC) #define NM_DEVICE_MACVLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_MACVLAN, NMDeviceMacvlanPrivate)) typedef struct { - NMPlatformMacvlanProperties props; + int parent_ifindex; + NMPlatformLnkMacvlan props; } NMDeviceMacvlanPrivate; enum { @@ -61,23 +62,26 @@ update_properties (NMDevice *device) NMDeviceMacvlan *self = NM_DEVICE_MACVLAN (device); NMDeviceMacvlanPrivate *priv = NM_DEVICE_MACVLAN_GET_PRIVATE (device); GObject *object = G_OBJECT (device); - NMPlatformMacvlanProperties props; + const NMPlatformLnkMacvlan *props; + const NMPlatformLink *plink; - if (!nm_platform_macvlan_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (device), &props)) { - _LOGW (LOGD_HW, "could not read macvlan properties"); + props = nm_platform_link_get_lnk_macvlan (NM_PLATFORM_GET, nm_device_get_ifindex (device), &plink); + if (!props) { + _LOGW (LOGD_HW, "could not get macvlan properties"); return; } g_object_freeze_notify (object); - if (priv->props.parent_ifindex != props.parent_ifindex) + if (priv->parent_ifindex != plink->parent) g_object_notify (object, NM_DEVICE_MACVLAN_PARENT); - if (g_strcmp0 (priv->props.mode, props.mode) != 0) + if (g_strcmp0 (priv->props.mode, props->mode) != 0) g_object_notify (object, NM_DEVICE_MACVLAN_MODE); - if (priv->props.no_promisc != props.no_promisc) + if (priv->props.no_promisc != props->no_promisc) g_object_notify (object, NM_DEVICE_MACVLAN_NO_PROMISC); - memcpy (&priv->props, &props, sizeof (NMPlatformMacvlanProperties)); + priv->parent_ifindex = plink->parent; + priv->props = *props; g_object_thaw_notify (object); } @@ -113,8 +117,8 @@ get_property (GObject *object, guint prop_id, switch (prop_id) { case PROP_PARENT: - if (priv->props.parent_ifindex > 0) - parent = nm_manager_get_device_by_ifindex (nm_manager_get (), priv->props.parent_ifindex); + if (priv->parent_ifindex > 0) + parent = nm_manager_get_device_by_ifindex (nm_manager_get (), priv->parent_ifindex); else parent = NULL; nm_utils_g_value_set_object_path (value, parent); diff --git a/src/devices/nm-device-veth.c b/src/devices/nm-device-veth.c index d80624857e..9c22ad1e5c 100644 --- a/src/devices/nm-device-veth.c +++ b/src/devices/nm-device-veth.c @@ -76,17 +76,18 @@ get_peer (NMDeviceVeth *self) { NMDeviceVethPrivate *priv = NM_DEVICE_VETH_GET_PRIVATE (self); NMDevice *device = NM_DEVICE (self), *peer = NULL; - NMPlatformVethProperties props; + int peer_ifindex; if (priv->ever_had_peer) return priv->peer; - if (!nm_platform_veth_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (device), &props)) { + if (!nm_platform_veth_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (device), &peer_ifindex)) { _LOGW (LOGD_HW, "could not read veth properties"); return NULL; } - peer = nm_manager_get_device_by_ifindex (nm_manager_get (), props.peer); + if (peer_ifindex > 0) + peer = nm_manager_get_device_by_ifindex (nm_manager_get (), peer_ifindex); if (peer && NM_IS_DEVICE_VETH (peer)) { set_peer (self, peer); set_peer (NM_DEVICE_VETH (peer), device); diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c index d30ce5f4ea..55790a9273 100644 --- a/src/devices/nm-device-vlan.c +++ b/src/devices/nm-device-vlan.c @@ -36,6 +36,7 @@ #include "nm-device-factory.h" #include "nm-manager.h" #include "nm-core-internal.h" +#include "nmp-object.h" #include "nmdbus-device-vlan.h" @@ -164,27 +165,28 @@ realize (NMDevice *device, GError **error) { NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (device); - int parent_ifindex = -1, vlan_id = -1; NMDevice *parent; + const NMPlatformLnkVlan *plnk; g_return_val_if_fail (plink, FALSE); g_assert (plink->type == NM_LINK_TYPE_VLAN); - if (!nm_platform_vlan_get_info (NM_PLATFORM_GET, plink->ifindex, &parent_ifindex, &vlan_id)) { + plnk = nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, plink->ifindex, NULL); + if (!plnk) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, - "(%s): failed to read VLAN properties", plink->name); + "(%s): failed to get VLAN properties", plink->name); return FALSE; } - if (vlan_id < 0) { + if (plnk->id < 0) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, "(%s): VLAN ID invalid", plink->name); return FALSE; } - if (parent_ifindex != NM_PLATFORM_LINK_OTHER_NETNS) { - parent = nm_manager_get_device_by_ifindex (nm_manager_get (), parent_ifindex); + if (plink->parent != NM_PLATFORM_LINK_OTHER_NETNS) { + parent = nm_manager_get_device_by_ifindex (nm_manager_get (), plink->parent); if (!parent) { nm_log_dbg (LOGD_HW, "(%s): VLAN parent interface unknown", plink->name); g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_FAILED, @@ -196,7 +198,7 @@ realize (NMDevice *device, g_warn_if_fail (priv->parent == NULL); nm_device_vlan_set_parent (NM_DEVICE_VLAN (device), parent); - priv->vlan_id = vlan_id; + priv->vlan_id = plnk->id; return TRUE; } @@ -294,7 +296,8 @@ component_added (NMDevice *device, GObject *component) NMDeviceVlan *self = NM_DEVICE_VLAN (device); NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (self); NMDevice *added_device; - int parent_ifindex = -1; + const NMPlatformLink *plink; + const NMPlatformLnkVlan *plnk; if (priv->parent) return FALSE; @@ -303,13 +306,14 @@ component_added (NMDevice *device, GObject *component) return FALSE; added_device = NM_DEVICE (component); - if (!nm_platform_vlan_get_info (NM_PLATFORM_GET, nm_device_get_ifindex (device), &parent_ifindex, NULL)) { + plnk = nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, nm_device_get_ifindex (device), &plink); + if (!plnk) { _LOGW (LOGD_VLAN, "failed to get VLAN interface info while checking added component."); return FALSE; } - if ( parent_ifindex <= 0 - || nm_device_get_ifindex (added_device) != parent_ifindex) + if ( plink->parent <= 0 + || nm_device_get_ifindex (added_device) != plink->parent) return FALSE; nm_device_vlan_set_parent (self, added_device); @@ -462,30 +466,34 @@ update_connection (NMDevice *device, NMConnection *connection) NMDeviceVlanPrivate *priv = NM_DEVICE_VLAN_GET_PRIVATE (device); NMSettingVlan *s_vlan = nm_connection_get_setting_vlan (connection); int ifindex = nm_device_get_ifindex (device); - int parent_ifindex = -1, vlan_id = -1; NMDevice *parent; const char *setting_parent, *new_parent; + const NMPlatformLink *plink; + const NMPObject *polnk; + nm_auto_nmpobj NMPObject *polnk_ref = NULL; if (!s_vlan) { s_vlan = (NMSettingVlan *) nm_setting_vlan_new (); nm_connection_add_setting (connection, (NMSetting *) s_vlan); } - if (!nm_platform_vlan_get_info (NM_PLATFORM_GET, ifindex, &parent_ifindex, &vlan_id)) { + polnk = nm_platform_link_get_lnk (NM_PLATFORM_GET, ifindex, NM_LINK_TYPE_VLAN, &plink); + if (!polnk) { _LOGW (LOGD_VLAN, "failed to get VLAN interface info while updating connection."); return; } + polnk_ref = nmp_object_ref ((NMPObject *) polnk); - if (priv->vlan_id != vlan_id) { - priv->vlan_id = vlan_id; + if (priv->vlan_id != polnk->lnk_vlan.id) { + priv->vlan_id = polnk->lnk_vlan.id; g_object_notify (G_OBJECT (device), NM_DEVICE_VLAN_ID); } - if (vlan_id != nm_setting_vlan_get_id (s_vlan)) + if (polnk->lnk_vlan.id != nm_setting_vlan_get_id (s_vlan)) g_object_set (s_vlan, NM_SETTING_VLAN_ID, priv->vlan_id, NULL); - if (parent_ifindex != NM_PLATFORM_LINK_OTHER_NETNS) - parent = nm_manager_get_device_by_ifindex (nm_manager_get (), parent_ifindex); + if (plink->parent != NM_PLATFORM_LINK_OTHER_NETNS) + parent = nm_manager_get_device_by_ifindex (nm_manager_get (), plink->parent); else parent = NULL; nm_device_vlan_set_parent (NM_DEVICE_VLAN (device), parent); @@ -506,6 +514,16 @@ update_connection (NMDevice *device, NMConnection *connection) g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, new_parent, NULL); } else g_object_set (s_vlan, NM_SETTING_VLAN_PARENT, NULL, NULL); + + if (polnk->lnk_vlan.flags != nm_setting_vlan_get_flags (s_vlan)) + g_object_set (s_vlan, NM_SETTING_VLAN_FLAGS, (NMVlanFlags) polnk->lnk_vlan.flags, NULL); + + _nm_setting_vlan_set_priorities (s_vlan, NM_VLAN_INGRESS_MAP, + polnk->_lnk_vlan.ingress_qos_map, + polnk->_lnk_vlan.n_ingress_qos_map); + _nm_setting_vlan_set_priorities (s_vlan, NM_VLAN_EGRESS_MAP, + polnk->_lnk_vlan.egress_qos_map, + polnk->_lnk_vlan.n_egress_qos_map); } static NMActStageReturn @@ -540,20 +558,29 @@ act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason) s_vlan = nm_connection_get_setting_vlan (connection); if (s_vlan) { - int ifindex = nm_device_get_ifindex (dev); - int num, i; - guint32 from, to; - - num = nm_setting_vlan_get_num_priorities (s_vlan, NM_VLAN_INGRESS_MAP); - for (i = 0; i < num; i++) { - if (nm_setting_vlan_get_priority (s_vlan, NM_VLAN_INGRESS_MAP, i, &from, &to)) - nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, from, to); - } - num = nm_setting_vlan_get_num_priorities (s_vlan, NM_VLAN_EGRESS_MAP); - for (i = 0; i < num; i++) { - if (nm_setting_vlan_get_priority (s_vlan, NM_VLAN_EGRESS_MAP, i, &from, &to)) - nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, from, to); - } + gs_free NMVlanQosMapping *ingress_map = NULL; + gs_free NMVlanQosMapping *egress_map = NULL; + guint n_ingress_map = 0, n_egress_map = 0; + + _nm_setting_vlan_get_priorities (s_vlan, + NM_VLAN_INGRESS_MAP, + &ingress_map, + &n_ingress_map); + _nm_setting_vlan_get_priorities (s_vlan, + NM_VLAN_EGRESS_MAP, + &egress_map, + &n_egress_map); + + nm_platform_link_vlan_change (NM_PLATFORM_GET, + nm_device_get_ifindex (dev), + NM_VLAN_FLAGS_ALL, + nm_setting_vlan_get_flags (s_vlan), + TRUE, + ingress_map, + n_ingress_map, + TRUE, + egress_map, + n_egress_map); } return ret; diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c index b66f2368fb..7fb55f2086 100644 --- a/src/devices/nm-device-vxlan.c +++ b/src/devices/nm-device-vxlan.c @@ -40,7 +40,7 @@ G_DEFINE_TYPE (NMDeviceVxlan, nm_device_vxlan, NM_TYPE_DEVICE_GENERIC) #define NM_DEVICE_VXLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DEVICE_VXLAN, NMDeviceVxlanPrivate)) typedef struct { - NMPlatformVxlanProperties props; + NMPlatformLnkVxlan props; } NMDeviceVxlanPrivate; enum { @@ -73,53 +73,54 @@ update_properties (NMDevice *device) NMDeviceVxlan *self = NM_DEVICE_VXLAN (device); NMDeviceVxlanPrivate *priv = NM_DEVICE_VXLAN_GET_PRIVATE (device); GObject *object = G_OBJECT (device); - NMPlatformVxlanProperties props; + const NMPlatformLnkVxlan *props; - if (!nm_platform_vxlan_get_properties (NM_PLATFORM_GET, nm_device_get_ifindex (device), &props)) { - _LOGW (LOGD_HW, "could not read vxlan properties"); + props = nm_platform_link_get_lnk_vxlan (NM_PLATFORM_GET, nm_device_get_ifindex (device), NULL); + if (!props) { + _LOGW (LOGD_HW, "could not get vxlan properties"); return; } g_object_freeze_notify (object); - if (priv->props.parent_ifindex != props.parent_ifindex) + if (priv->props.parent_ifindex != props->parent_ifindex) g_object_notify (object, NM_DEVICE_VXLAN_PARENT); - if (priv->props.id != props.id) + if (priv->props.id != props->id) g_object_notify (object, NM_DEVICE_VXLAN_ID); - if (priv->props.group != props.group) + if (priv->props.group != props->group) g_object_notify (object, NM_DEVICE_VXLAN_GROUP); - if (priv->props.local != props.local) + if (priv->props.local != props->local) g_object_notify (object, NM_DEVICE_VXLAN_LOCAL); - if (memcmp (&priv->props.group6, &props.group6, sizeof (props.group6)) != 0) + if (memcmp (&priv->props.group6, &props->group6, sizeof (props->group6)) != 0) g_object_notify (object, NM_DEVICE_VXLAN_GROUP); - if (memcmp (&priv->props.local6, &props.local6, sizeof (props.local6)) != 0) + if (memcmp (&priv->props.local6, &props->local6, sizeof (props->local6)) != 0) g_object_notify (object, NM_DEVICE_VXLAN_LOCAL); - if (priv->props.tos != props.tos) + if (priv->props.tos != props->tos) g_object_notify (object, NM_DEVICE_VXLAN_TOS); - if (priv->props.ttl != props.ttl) + if (priv->props.ttl != props->ttl) g_object_notify (object, NM_DEVICE_VXLAN_TTL); - if (priv->props.learning != props.learning) + if (priv->props.learning != props->learning) g_object_notify (object, NM_DEVICE_VXLAN_LEARNING); - if (priv->props.ageing != props.ageing) + if (priv->props.ageing != props->ageing) g_object_notify (object, NM_DEVICE_VXLAN_AGEING); - if (priv->props.limit != props.limit) + if (priv->props.limit != props->limit) g_object_notify (object, NM_DEVICE_VXLAN_LIMIT); - if (priv->props.dst_port != props.dst_port) + if (priv->props.dst_port != props->dst_port) g_object_notify (object, NM_DEVICE_VXLAN_DST_PORT); - if (priv->props.src_port_min != props.src_port_min) + if (priv->props.src_port_min != props->src_port_min) g_object_notify (object, NM_DEVICE_VXLAN_SRC_PORT_MIN); - if (priv->props.src_port_max != props.src_port_max) + if (priv->props.src_port_max != props->src_port_max) g_object_notify (object, NM_DEVICE_VXLAN_SRC_PORT_MAX); - if (priv->props.proxy != props.proxy) + if (priv->props.proxy != props->proxy) g_object_notify (object, NM_DEVICE_VXLAN_PROXY); - if (priv->props.rsc != props.rsc) + if (priv->props.rsc != props->rsc) g_object_notify (object, NM_DEVICE_VXLAN_RSC); - if (priv->props.l2miss != props.l2miss) + if (priv->props.l2miss != props->l2miss) g_object_notify (object, NM_DEVICE_VXLAN_L2MISS); - if (priv->props.l3miss != props.l3miss) + if (priv->props.l3miss != props->l3miss) g_object_notify (object, NM_DEVICE_VXLAN_L3MISS); - memcpy (&priv->props, &props, sizeof (NMPlatformVxlanProperties)); + priv->props = *props; g_object_thaw_notify (object); } diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 7338ff5ffb..f1df6ef0a5 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -3274,8 +3274,7 @@ ipv4ll_get_ip4_config (NMDevice *self, guint32 lla) g_assert (config); memset (&address, 0, sizeof (address)); - address.address = lla; - address.plen = 16; + nm_platform_ip4_address_set_addr (&address, lla, 16); address.source = NM_IP_CONFIG_SOURCE_IP4LL; nm_ip4_config_add_address (config, &address); @@ -3711,7 +3710,7 @@ ip4_config_merge_and_apply (NMDevice *self, goto END_ADD_DEFAULT_ROUTE; has_direct_route = ( gateway == 0 - || nm_ip4_config_get_subnet_for_host (composite, gateway) + || nm_ip4_config_destination_is_direct (composite, gateway, 32) || nm_ip4_config_get_direct_route_for_host (composite, gateway)); priv->default_route.v4_has = TRUE; @@ -4016,10 +4015,11 @@ reserve_shared_ip (NMDevice *self, NMSettingIPConfig *s_ip4, NMPlatformIP4Addres if (s_ip4 && nm_setting_ip_config_get_num_addresses (s_ip4)) { /* Use the first user-supplied address */ NMIPAddress *user = nm_setting_ip_config_get_address (s_ip4, 0); + in_addr_t a; g_assert (user); - nm_ip_address_get_address_binary (user, &address->address); - address->plen = nm_ip_address_get_prefix (user); + nm_ip_address_get_address_binary (user, &a); + nm_platform_ip4_address_set_addr (address, a, nm_ip_address_get_prefix (user)); } else { /* Find an unused address in the 10.42.x.x range */ guint32 start = (guint32) ntohl (0x0a2a0001); /* 10.42.0.1 */ @@ -4032,8 +4032,7 @@ reserve_shared_ip (NMDevice *self, NMSettingIPConfig *s_ip4, NMPlatformIP4Addres return FALSE; } } - address->address = start + count; - address->plen = 24; + nm_platform_ip4_address_set_addr (address, start + count, 24); g_hash_table_insert (shared_ips, GUINT_TO_POINTER (address->address), @@ -4902,53 +4901,6 @@ linklocal6_start (NMDevice *self) /******************************************/ -static void -print_support_extended_ifa_flags (NMSettingIP6ConfigPrivacy use_tempaddr) -{ - static gint8 warn = 0; - static gint8 s_libnl = -1, s_kernel; - - if (warn >= 2) - return; - - if (s_libnl == -1) { - s_libnl = !!nm_platform_check_support_libnl_extended_ifa_flags (); - s_kernel = !!nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET); - - if (s_libnl && s_kernel) { - nm_log_dbg (LOGD_IP6, "kernel and libnl support extended IFA_FLAGS (needed by NM for IPv6 private addresses)"); - warn = 2; - return; - } - } - - if ( use_tempaddr != NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR - && use_tempaddr != NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR) { - if (warn == 0) { - nm_log_dbg (LOGD_IP6, "%s%s%s %s not support extended IFA_FLAGS (needed by NM for IPv6 private addresses)", - !s_kernel ? "kernel" : "", - !s_kernel && !s_libnl ? " and " : "", - !s_libnl ? "libnl" : "", - !s_kernel && !s_libnl ? "do" : "does"); - warn = 1; - } - return; - } - - if (!s_libnl && !s_kernel) { - nm_log_warn (LOGD_IP6, "libnl and the kernel do not support extended IFA_FLAGS needed by NM for " - "IPv6 private addresses. This feature is not available"); - } else if (!s_libnl) { - nm_log_warn (LOGD_IP6, "libnl does not support extended IFA_FLAGS needed by NM for " - "IPv6 private addresses. This feature is not available"); - } else if (!s_kernel) { - nm_log_warn (LOGD_IP6, "The kernel does not support extended IFA_FLAGS needed by NM for " - "IPv6 private addresses. This feature is not available"); - } - - warn = 2; -} - static void nm_device_ipv6_set_mtu (NMDevice *self, guint32 mtu); static void @@ -5006,24 +4958,20 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); int i; - static int system_support = -1; + int system_support; guint ifa_flags = 0x00; - if (system_support == -1) { - /* - * Check, if both libnl and the kernel are recent enough, - * to help user space handling RA. If it's not supported, - * we have no ipv6-privacy and must add autoconf addresses - * as /128. The reason for the /128 is to prevent the kernel - * from adding a prefix route for this address. - **/ - system_support = nm_platform_check_support_libnl_extended_ifa_flags () && - nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET); - } + /* + * Check, whether kernel is recent enough to help user space handling RA. + * If it's not supported, we have no ipv6-privacy and must add autoconf + * addresses as /128. The reason for the /128 is to prevent the kernel + * from adding a prefix route for this address. + **/ + system_support = nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET); if (system_support) ifa_flags = IFA_F_NOPREFIXROUTE; - if (priv->rdisc_use_tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR + if ( priv->rdisc_use_tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR || priv->rdisc_use_tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR) { /* without system_support, this flag will be ignored. Still set it, doesn't seem to do any harm. */ @@ -5233,7 +5181,12 @@ addrconf6_start (NMDevice *self, NMSettingIP6ConfigPrivacy use_tempaddr) } priv->rdisc_use_tempaddr = use_tempaddr; - print_support_extended_ifa_flags (use_tempaddr); + + if ( NM_IN_SET (use_tempaddr, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR) + && !nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET)) { + _LOGW (LOGD_IP6, "The kernel does not support extended IFA_FLAGS needed by NM for " + "IPv6 private addresses. This feature is not available"); + } if (!nm_setting_ip_config_get_may_fail (nm_connection_get_setting_ip6_config (connection))) nm_device_add_pending_action (self, PENDING_ACTION_AUTOCONF6, TRUE); diff --git a/src/devices/wwan/nm-modem-broadband.c b/src/devices/wwan/nm-modem-broadband.c index 7ec90c33ef..2236559477 100644 --- a/src/devices/wwan/nm-modem-broadband.c +++ b/src/devices/wwan/nm-modem-broadband.c @@ -750,6 +750,7 @@ static_stage3_ip4_done (NMModemBroadband *self) memset (&address, 0, sizeof (address)); address.address = address_network; + address.peer_address = address_network; address.plen = mm_bearer_ip_config_get_prefix (self->priv->ipv4_config); address.source = NM_IP_CONFIG_SOURCE_WWAN; nm_ip4_config_add_address (config, &address); diff --git a/src/dhcp-manager/nm-dhcp-dhclient-utils.c b/src/dhcp-manager/nm-dhcp-dhclient-utils.c index a86596c077..726d26c8d0 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient-utils.c +++ b/src/dhcp-manager/nm-dhcp-dhclient-utils.c @@ -675,6 +675,7 @@ nm_dhcp_dhclient_read_lease_ip_configs (const char *iface, continue; if (!inet_pton (AF_INET, value, &address.address)) continue; + address.peer_address = address.address; /* Gateway */ value = g_hash_table_lookup (hash, "option routers"); diff --git a/src/dhcp-manager/nm-dhcp-systemd.c b/src/dhcp-manager/nm-dhcp-systemd.c index 4459fd583f..e508ea13bb 100644 --- a/src/dhcp-manager/nm-dhcp-systemd.c +++ b/src/dhcp-manager/nm-dhcp-systemd.c @@ -232,6 +232,7 @@ lease_to_ip4_config (const char *iface, sd_dhcp_lease_get_address (lease, &tmp_addr); memset (&address, 0, sizeof (address)); address.address = tmp_addr.s_addr; + address.peer_address = tmp_addr.s_addr; str = nm_utils_inet4_ntop (tmp_addr.s_addr, NULL); LOG_LEASE (LOGD_DHCP4, " address %s", str); add_option (options, dhcp4_requests, DHCP_OPTION_IP_ADDRESS, str); diff --git a/src/dhcp-manager/nm-dhcp-utils.c b/src/dhcp-manager/nm-dhcp-utils.c index cdf26fcae9..e6bc18072c 100644 --- a/src/dhcp-manager/nm-dhcp-utils.c +++ b/src/dhcp-manager/nm-dhcp-utils.c @@ -320,7 +320,7 @@ process_classful_routes (GHashTable *options, guint32 priority, NMIP4Config *ip4 nm_ip4_config_add_route (ip4_config, &route); nm_log_info (LOGD_DHCP, " static route %s", - nm_platform_ip4_route_to_string (&route)); + nm_platform_ip4_route_to_string (&route, NULL, 0)); } out: @@ -380,6 +380,7 @@ nm_dhcp_utils_ip4_config_from_options (int ifindex, { NMIP4Config *ip4_config = NULL; guint32 tmp_addr; + in_addr_t addr; NMPlatformIP4Address address; char *str = NULL; guint32 gwaddr = 0, plen = 0; @@ -391,10 +392,9 @@ nm_dhcp_utils_ip4_config_from_options (int ifindex, address.timestamp = nm_utils_get_monotonic_timestamp_s (); str = g_hash_table_lookup (options, "ip_address"); - if (str && (inet_pton (AF_INET, str, &tmp_addr) > 0)) { - address.address = tmp_addr; + if (str && (inet_pton (AF_INET, str, &addr) > 0)) nm_log_info (LOGD_DHCP4, " address %s", str); - } else + else goto error; str = g_hash_table_lookup (options, "subnet_mask"); @@ -403,10 +403,10 @@ nm_dhcp_utils_ip4_config_from_options (int ifindex, nm_log_info (LOGD_DHCP4, " plen %d (%s)", plen, str); } else { /* Get default netmask for the IP according to appropriate class. */ - plen = nm_utils_ip4_get_default_prefix (address.address); + plen = nm_utils_ip4_get_default_prefix (addr); nm_log_info (LOGD_DHCP4, " plen %d (default)", plen); } - address.plen = plen; + nm_platform_ip4_address_set_addr (&address, addr, plen); /* Routes: if the server returns classless static routes, we MUST ignore * the 'static_routes' option. @@ -469,7 +469,7 @@ nm_dhcp_utils_ip4_config_from_options (int ifindex, route.metric = priority; nm_ip4_config_add_route (ip4_config, &route); nm_log_dbg (LOGD_IP, "adding route for server identifier: %s", - nm_platform_ip4_route_to_string (&route)); + nm_platform_ip4_route_to_string (&route, NULL, 0)); } } else diff --git a/src/dhcp-manager/tests/test-dhcp-dhclient.c b/src/dhcp-manager/tests/test-dhcp-dhclient.c index 15817addb4..ce3697a552 100644 --- a/src/dhcp-manager/tests/test-dhcp-dhclient.c +++ b/src/dhcp-manager/tests/test-dhcp-dhclient.c @@ -666,6 +666,7 @@ test_read_lease_ip4_config_basic (void) g_assert (inet_aton ("192.168.1.180", (struct in_addr *) &expected_addr)); addr = nm_ip4_config_get_address (config, 0); g_assert_cmpint (addr->address, ==, expected_addr); + g_assert_cmpint (addr->peer_address, ==, expected_addr); g_assert_cmpint (addr->plen, ==, 24); /* Gateway */ @@ -688,6 +689,7 @@ test_read_lease_ip4_config_basic (void) g_assert (inet_aton ("10.77.52.141", (struct in_addr *) &expected_addr)); addr = nm_ip4_config_get_address (config, 0); g_assert_cmpint (addr->address, ==, expected_addr); + g_assert_cmpint (addr->peer_address, ==, expected_addr); g_assert_cmpint (addr->plen, ==, 8); /* Gateway */ diff --git a/src/dhcp-manager/tests/test-dhcp-utils.c b/src/dhcp-manager/tests/test-dhcp-utils.c index 65a396092a..49987c40ca 100644 --- a/src/dhcp-manager/tests/test-dhcp-utils.c +++ b/src/dhcp-manager/tests/test-dhcp-utils.c @@ -95,7 +95,7 @@ test_generic_options (void) address = nm_ip4_config_get_address (ip4_config, 0); g_assert (inet_pton (AF_INET, expected_addr, &tmp) > 0); g_assert (address->address == tmp); - g_assert (address->peer_address == 0); + g_assert (address->peer_address == tmp); g_assert_cmpint (address->plen, ==, 24); /* Gateway */ diff --git a/src/dnsmasq-manager/tests/test-dnsmasq-utils.c b/src/dnsmasq-manager/tests/test-dnsmasq-utils.c index 8cc5fa9751..04bf1b4d74 100644 --- a/src/dnsmasq-manager/tests/test-dnsmasq-utils.c +++ b/src/dnsmasq-manager/tests/test-dnsmasq-utils.c @@ -27,13 +27,12 @@ #include "nm-test-utils.h" -static guint32 -addr_to_num (const char *addr) +static NMPlatformIP4Address * +_set_addr (NMPlatformIP4Address *addr, const char *address, int plen) { - guint n; - - g_assert (inet_pton (AF_INET, addr, (void *) &n) == 1); - return n; + memset (addr, 0, sizeof (*addr)); + nm_platform_ip4_address_set_addr (addr, nmtst_inet4_from_string (address), plen); + return addr; } static void @@ -44,58 +43,50 @@ test_address_ranges (void) char last[INET_ADDRSTRLEN]; char *error_desc = NULL; - addr.address = addr_to_num ("192.168.0.1"); - addr.plen = 24; + _set_addr (&addr, "192.168.0.1", 24); g_assert (nm_dnsmasq_utils_get_range (&addr, first, last, &error_desc)); g_assert (error_desc == NULL); g_assert_cmpstr (first, ==, "192.168.0.10"); g_assert_cmpstr (last, ==, "192.168.0.254"); - addr.address = addr_to_num ("192.168.0.99"); - addr.plen = 24; + _set_addr (&addr, "192.168.0.99", 24); g_assert (nm_dnsmasq_utils_get_range (&addr, first, last, &error_desc)); g_assert (error_desc == NULL); g_assert_cmpstr (first, ==, "192.168.0.108"); g_assert_cmpstr (last, ==, "192.168.0.254"); - addr.address = addr_to_num ("192.168.0.254"); - addr.plen = 24; + _set_addr (&addr, "192.168.0.254", 24); g_assert (nm_dnsmasq_utils_get_range (&addr, first, last, &error_desc)); g_assert (error_desc == NULL); g_assert_cmpstr (first, ==, "192.168.0.1"); g_assert_cmpstr (last, ==, "192.168.0.245"); /* Smaller networks */ - addr.address = addr_to_num ("1.2.3.1"); - addr.plen = 30; + _set_addr (&addr, "1.2.3.1", 30); g_assert (nm_dnsmasq_utils_get_range (&addr, first, last, &error_desc)); g_assert (error_desc == NULL); g_assert_cmpstr (first, ==, "1.2.3.2"); g_assert_cmpstr (last, ==, "1.2.3.2"); - addr.address = addr_to_num ("1.2.3.1"); - addr.plen = 29; + _set_addr (&addr, "1.2.3.1", 29); g_assert (nm_dnsmasq_utils_get_range (&addr, first, last, &error_desc)); g_assert (error_desc == NULL); g_assert_cmpstr (first, ==, "1.2.3.2"); g_assert_cmpstr (last, ==, "1.2.3.6"); - addr.address = addr_to_num ("1.2.3.1"); - addr.plen = 28; + _set_addr (&addr, "1.2.3.1", 28); g_assert (nm_dnsmasq_utils_get_range (&addr, first, last, &error_desc)); g_assert (error_desc == NULL); g_assert_cmpstr (first, ==, "1.2.3.3"); g_assert_cmpstr (last, ==, "1.2.3.14"); - addr.address = addr_to_num ("1.2.3.1"); - addr.plen = 26; + _set_addr (&addr, "1.2.3.1", 26); g_assert (nm_dnsmasq_utils_get_range (&addr, first, last, &error_desc)); g_assert (error_desc == NULL); g_assert_cmpstr (first, ==, "1.2.3.8"); g_assert_cmpstr (last, ==, "1.2.3.62"); - addr.address = addr_to_num ("1.2.3.1"); - addr.plen = 31; + _set_addr (&addr, "1.2.3.1", 31); g_assert (nm_dnsmasq_utils_get_range (&addr, first, last, &error_desc) == FALSE); g_assert (error_desc); g_free (error_desc); diff --git a/src/nm-default-route-manager.c b/src/nm-default-route-manager.c index 26baf1778b..91785566cc 100644 --- a/src/nm-default-route-manager.c +++ b/src/nm-default-route-manager.c @@ -281,7 +281,7 @@ _platform_route_sync_add (const VTableIP *vtable, NMDefaultRouteManager *self, g } if (!success) { _LOGW (vtable->vt->addr_family, "failed to add default route %s with effective metric %u", - vtable->vt->route_to_string (&entry->route), (guint) entry->effective_metric); + vtable->vt->route_to_string (&entry->route, NULL, 0), (guint) entry->effective_metric); } return TRUE; } @@ -553,23 +553,23 @@ _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *c g_array_append_val (changed_metrics, expected_metric); if (old_entry) { _LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": sync:update %s (%u -> %u)", LOG_ENTRY_ARGS (i, entry), - vtable->vt->route_to_string (&entry->route), (guint) old_entry->effective_metric, + vtable->vt->route_to_string (&entry->route, NULL, 0), (guint) old_entry->effective_metric, (guint) expected_metric); } else { _LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": sync:add %s (%u)", LOG_ENTRY_ARGS (i, entry), - vtable->vt->route_to_string (&entry->route), (guint) expected_metric); + vtable->vt->route_to_string (&entry->route, NULL, 0), (guint) expected_metric); } } else if (entry->effective_metric != expected_metric) { g_array_append_val (changed_metrics, entry->effective_metric); g_array_append_val (changed_metrics, expected_metric); _LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": sync:metric %s (%u -> %u)", LOG_ENTRY_ARGS (i, entry), - vtable->vt->route_to_string (&entry->route), (guint) entry->effective_metric, + vtable->vt->route_to_string (&entry->route, NULL, 0), (guint) entry->effective_metric, (guint) expected_metric); } else { if (!_vt_routes_has_entry (vtable, routes, entry)) { g_array_append_val (changed_metrics, entry->effective_metric); _LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": sync:re-add %s (%u -> %u)", LOG_ENTRY_ARGS (i, entry), - vtable->vt->route_to_string (&entry->route), (guint) entry->effective_metric, + vtable->vt->route_to_string (&entry->route, NULL, 0), (guint) entry->effective_metric, (guint) entry->effective_metric); } } @@ -636,7 +636,7 @@ _entry_at_idx_update (const VTableIP *vtable, NMDefaultRouteManager *self, guint _LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": %s %s (%"G_GUINT32_FORMAT")", LOG_ENTRY_ARGS (entry_idx, entry), old_entry ? "record:update" : "record:add ", - vtable->vt->route_to_string (&entry->route), + vtable->vt->route_to_string (&entry->route, NULL, 0), entry->effective_metric); g_ptr_array_sort_with_data (entries, _sort_entries_cmp, NULL); @@ -658,7 +658,7 @@ _entry_at_idx_remove (const VTableIP *vtable, NMDefaultRouteManager *self, guint entry = g_ptr_array_index (entries, entry_idx); _LOGD (vtable->vt->addr_family, LOG_ENTRY_FMT": record:remove %s (%u)", LOG_ENTRY_ARGS (entry_idx, entry), - vtable->vt->route_to_string (&entry->route), (guint) entry->effective_metric); + vtable->vt->route_to_string (&entry->route, NULL, 0), (guint) entry->effective_metric); /* Remove the entry from the list (but don't free it yet) */ g_ptr_array_index (entries, entry_idx) = NULL; diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c index 6ae19c5c7a..ecb67ea7bb 100644 --- a/src/nm-iface-helper.c +++ b/src/nm-iface-helper.c @@ -137,14 +137,13 @@ rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, gpointer user_da if (system_support == -1) { /* - * Check, if both libnl and the kernel are recent enough, - * to help user space handling RA. If it's not supported, - * we have no ipv6-privacy and must add autoconf addresses - * as /128. The reason for the /128 is to prevent the kernel + * Check, whether kernel is recent enough, to help user space handling RA. + * If it's not supported, we have no ipv6-privacy and must add autoconf + * addresses as /128. + * The reason for the /128 is to prevent the kernel * from adding a prefix route for this address. **/ - system_support = nm_platform_check_support_libnl_extended_ifa_flags () && - nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET); + system_support = nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET); } if (system_support) diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 2f14a3aa6c..7e74379ec0 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -100,13 +100,13 @@ nm_ip4_config_get_ifindex (const NMIP4Config *config) return NM_IP4_CONFIG_GET_PRIVATE (config)->ifindex; } +/******************************************************************/ + static gboolean -same_prefix (guint32 address1, guint32 address2, int plen) +_ipv4_is_zeronet (in_addr_t network) { - guint32 masked1 = ntohl (address1) >> (32 - plen); - guint32 masked2 = ntohl (address2) >> (32 - plen); - - return masked1 == masked2; + /* Same as ipv4_is_zeronet() from kernel's include/linux/in.h. */ + return (network & htonl(0xff000000)) == htonl(0x00000000); } /******************************************************************/ @@ -180,7 +180,7 @@ addresses_are_duplicate (const NMPlatformIP4Address *a, const NMPlatformIP4Addre { return a->address == b->address && a->plen == b->plen - && nm_platform_ip4_address_equal_peer_net (a, b); + && ((a->peer_address ^ b->peer_address) & nm_utils_ip4_prefix_to_netmask (a->plen)) == 0; } static gboolean @@ -307,7 +307,16 @@ nm_ip4_config_commit (const NMIP4Config *config, int ifindex, gboolean routes_fu route.ifindex = ifindex; route.source = NM_IP_CONFIG_SOURCE_KERNEL; - route.network = nm_utils_ip4_address_clear_host_address (addr->address, addr->plen); + + /* The destination network depends on the peer-address. */ + route.network = nm_utils_ip4_address_clear_host_address (addr->peer_address, addr->plen); + + if (_ipv4_is_zeronet (route.network)) { + /* Kernel doesn't add device-routes for destinations that + * start with 0.x.y.z. Skip them. */ + continue; + } + route.plen = addr->plen; route.pref_src = addr->address; route.metric = default_route_metric; @@ -395,6 +404,7 @@ nm_ip4_config_merge_setting (NMIP4Config *config, NMSettingIPConfig *setting, gu memset (&address, 0, sizeof (address)); nm_ip_address_get_address_binary (s_addr, &address.address); + address.peer_address = address.address; address.plen = nm_ip_address_get_prefix (s_addr); address.lifetime = NM_PLATFORM_LIFETIME_PERMANENT; address.preferred = NM_PLATFORM_LIFETIME_PERMANENT; @@ -996,7 +1006,7 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev dst_addr = nm_ip4_config_get_address (dst, i))) { are_equal = FALSE; if ( !addresses_are_duplicate (src_addr, dst_addr) - || (nm_platform_ip4_address_get_peer (src_addr) != nm_platform_ip4_address_get_peer (dst_addr))) { + || src_addr->peer_address != dst_addr->peer_address) { has_relevant_changes = TRUE; break; } @@ -1199,7 +1209,7 @@ nm_ip4_config_dump (const NMIP4Config *config, const char *detail) /* addresses */ for (i = 0; i < nm_ip4_config_get_num_addresses (config); i++) - g_message (" a: %s", nm_platform_ip4_address_to_string (nm_ip4_config_get_address (config, i))); + g_message (" a: %s", nm_platform_ip4_address_to_string (nm_ip4_config_get_address (config, i), NULL, 0)); /* default gateway */ if (nm_ip4_config_has_gateway (config)) { @@ -1215,7 +1225,7 @@ nm_ip4_config_dump (const NMIP4Config *config, const char *detail) /* routes */ for (i = 0; i < nm_ip4_config_get_num_routes (config); i++) - g_message (" rt: %s", nm_platform_ip4_route_to_string (nm_ip4_config_get_route (config, i))); + g_message (" rt: %s", nm_platform_ip4_route_to_string (nm_ip4_config_get_route (config, i), NULL, 0)); /* domains */ for (i = 0; i < nm_ip4_config_get_num_domains (config); i++) @@ -1256,12 +1266,22 @@ nm_ip4_config_destination_is_direct (const NMIP4Config *config, guint32 network, { guint naddresses = nm_ip4_config_get_num_addresses (config); int i; + in_addr_t peer_network; for (i = 0; i < naddresses; i++) { const NMPlatformIP4Address *item = nm_ip4_config_get_address (config, i); - if (item->plen <= plen && same_prefix (item->address, network, item->plen)) - return TRUE; + if (item->plen > plen) + continue; + + peer_network = nm_utils_ip4_address_clear_host_address (item->peer_address, item->plen); + if (_ipv4_is_zeronet (peer_network)) + continue; + + if (peer_network != nm_utils_ip4_address_clear_host_address (network, item->plen)) + continue; + + return TRUE; } return FALSE; @@ -1554,28 +1574,6 @@ nm_ip4_config_get_direct_route_for_host (const NMIP4Config *config, guint32 host return best_route; } -const NMPlatformIP4Address * -nm_ip4_config_get_subnet_for_host (const NMIP4Config *config, guint32 host) -{ - NMIP4ConfigPrivate *priv = NM_IP4_CONFIG_GET_PRIVATE (config); - guint i; - NMPlatformIP4Address *subnet = NULL; - - g_return_val_if_fail (host, NULL); - - for (i = 0; i < priv->addresses->len; i++) { - NMPlatformIP4Address *item = &g_array_index (priv->addresses, NMPlatformIP4Address, i); - - if (subnet && subnet->plen >= item->plen) - continue; - if (nm_utils_ip4_address_clear_host_address (host, item->plen) != nm_utils_ip4_address_clear_host_address (item->address, item->plen)) - continue; - subnet = item; - } - - return subnet; -} - /******************************************************************/ void @@ -2016,7 +2014,7 @@ nm_ip4_config_hash (const NMIP4Config *config, GChecksum *sum, gboolean dns_only const NMPlatformIP4Address *address = nm_ip4_config_get_address (config, i); hash_u32 (sum, address->address); hash_u32 (sum, address->plen); - hash_u32 (sum, nm_platform_ip4_address_get_peer_net (address)); + hash_u32 (sum, address->peer_address & nm_utils_ip4_prefix_to_netmask (address->plen)); } for (i = 0; i < nm_ip4_config_get_num_routes (config); i++) { @@ -2163,8 +2161,7 @@ get_property (GObject *object, guint prop_id, g_variant_builder_add (&addr_builder, "{sv}", "prefix", g_variant_new_uint32 (address->plen)); - if ( address->peer_address - && address->peer_address != address->address) { + if (address->peer_address != address->address) { g_variant_builder_add (&addr_builder, "{sv}", "peer", g_variant_new_string (nm_utils_inet4_ntop (address->peer_address, NULL))); diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h index 1c76b97e99..160f2246bf 100644 --- a/src/nm-ip4-config.h +++ b/src/nm-ip4-config.h @@ -102,7 +102,6 @@ guint32 nm_ip4_config_get_num_routes (const NMIP4Config *config); const NMPlatformIP4Route *nm_ip4_config_get_route (const NMIP4Config *config, guint32 i); const NMPlatformIP4Route *nm_ip4_config_get_direct_route_for_host (const NMIP4Config *config, guint32 host); -const NMPlatformIP4Address *nm_ip4_config_get_subnet_for_host (const NMIP4Config *config, guint32 host); /* Nameservers */ void nm_ip4_config_reset_nameservers (NMIP4Config *config); diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index c484bf52ab..51e8e121a7 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -1166,7 +1166,7 @@ nm_ip6_config_dump (const NMIP6Config *config, const char *detail) /* addresses */ for (i = 0; i < nm_ip6_config_get_num_addresses (config); i++) - g_message (" a: %s", nm_platform_ip6_address_to_string (nm_ip6_config_get_address (config, i))); + g_message (" a: %s", nm_platform_ip6_address_to_string (nm_ip6_config_get_address (config, i), NULL, 0)); /* default gateway */ tmp = nm_ip6_config_get_gateway (config); @@ -1181,7 +1181,7 @@ nm_ip6_config_dump (const NMIP6Config *config, const char *detail) /* routes */ for (i = 0; i < nm_ip6_config_get_num_routes (config); i++) - g_message (" rt: %s", nm_platform_ip6_route_to_string (nm_ip6_config_get_route (config, i))); + g_message (" rt: %s", nm_platform_ip6_route_to_string (nm_ip6_config_get_route (config, i), NULL, 0)); /* domains */ for (i = 0; i < nm_ip6_config_get_num_domains (config); i++) diff --git a/src/nm-route-manager.c b/src/nm-route-manager.c index 2c08c9eee7..1a89af7433 100644 --- a/src/nm-route-manager.c +++ b/src/nm-route-manager.c @@ -467,12 +467,12 @@ _vx_route_sync (const VTableIP *vtable, NMRouteManager *self, int ifindex, const if (_LOGT_ENABLED (vtable->vt->addr_family)) { for (i = 0; i < known_routes_idx->len; i++) { _LOGT (vtable->vt->addr_family, "%3d: sync new route #%u: %s", - ifindex, i, vtable->vt->route_to_string (VTABLE_ROUTE_INDEX (vtable, known_routes, i))); + ifindex, i, vtable->vt->route_to_string (VTABLE_ROUTE_INDEX (vtable, known_routes, i), NULL, 0)); } for (i = 0; i < ipx_routes->index->len; i++) _LOGT (vtable->vt->addr_family, "%3d: STATE: has #%u - %s (%lld)", ifindex, i, - vtable->vt->route_to_string (ipx_routes->index->entries[i]), + vtable->vt->route_to_string (ipx_routes->index->entries[i], NULL, 0), (long long) g_array_index (ipx_routes->effective_metrics, gint64, i)); } @@ -512,7 +512,8 @@ _vx_route_sync (const VTableIP *vtable, NMRouteManager *self, int ifindex, const cur_ipx_route->rx.ifindex = ifindex; cur_ipx_route->rx.metric = vtable->vt->metric_normalize (cur_ipx_route->rx.metric); ipx_routes_changed = TRUE; - _LOGT (vtable->vt->addr_family, "%3d: STATE: update #%u - %s", ifindex, i_ipx_routes, vtable->vt->route_to_string (cur_ipx_route)); + _LOGT (vtable->vt->addr_family, "%3d: STATE: update #%u - %s", ifindex, i_ipx_routes, + vtable->vt->route_to_string (cur_ipx_route, NULL, 0)); } } else if (cur_known_route) { g_assert (!cur_ipx_route || route_id_cmp_result > 0); @@ -570,7 +571,8 @@ _vx_route_sync (const VTableIP *vtable, NMRouteManager *self, int ifindex, const && cur_plat_route->rx.metric == *p_effective_metric) { /* we are about to delete cur_ipx_route and we have a matching route * in platform. Delete it. */ - _LOGT (vtable->vt->addr_family, "%3d: platform rt-rm #%u - %s", ifindex, i_plat_routes, vtable->vt->route_to_string (cur_plat_route)); + _LOGT (vtable->vt->addr_family, "%3d: platform rt-rm #%u - %s", ifindex, i_plat_routes, + vtable->vt->route_to_string (cur_plat_route, NULL, 0)); vtable->vt->route_delete (priv->platform, ifindex, cur_plat_route); } } @@ -582,7 +584,8 @@ _vx_route_sync (const VTableIP *vtable, NMRouteManager *self, int ifindex, const for (i = 0; i < to_delete_indexes->len; i++) { guint idx = g_array_index (to_delete_indexes, guint, i); - _LOGT (vtable->vt->addr_family, "%3d: STATE: delete #%u - %s", ifindex, idx, vtable->vt->route_to_string (ipx_routes->index->entries[idx])); + _LOGT (vtable->vt->addr_family, "%3d: STATE: delete #%u - %s", ifindex, idx, + vtable->vt->route_to_string (ipx_routes->index->entries[idx], NULL, 0)); g_array_index (to_delete_indexes, guint, i) = _route_index_reverse_idx (vtable, ipx_routes->index, idx, ipx_routes->entries); } g_array_sort (to_delete_indexes, (GCompareFunc) _sort_indexes_cmp); @@ -606,7 +609,8 @@ _vx_route_sync (const VTableIP *vtable, NMRouteManager *self, int ifindex, const g_array_index (ipx_routes->effective_metrics_reverse, gint64, j++) = -1; - _LOGT (vtable->vt->addr_family, "%3d: STATE: added #%u - %s", ifindex, ipx_routes->entries->len - 1, vtable->vt->route_to_string (ipx_route)); + _LOGT (vtable->vt->addr_family, "%3d: STATE: added #%u - %s", ifindex, ipx_routes->entries->len - 1, + vtable->vt->route_to_string (ipx_route, NULL, 0)); } g_ptr_array_unref (to_add_routes); } @@ -693,7 +697,7 @@ _vx_route_sync (const VTableIP *vtable, NMRouteManager *self, int ifindex, const next: _LOGT (vtable->vt->addr_family, "%3d: new metric #%u - %s (%lld)", ifindex, i_ipx_routes, - vtable->vt->route_to_string (cur_ipx_route), + vtable->vt->route_to_string (cur_ipx_route, NULL, 0), (long long) *p_effective_metric); } } @@ -716,7 +720,7 @@ next: g_assert (cur_plat_route->rx.ifindex == ifindex); - _LOGT (vtable->vt->addr_family, "%3d: platform rt #%u - %s", ifindex, i_plat_routes, vtable->vt->route_to_string (cur_plat_route)); + _LOGT (vtable->vt->addr_family, "%3d: platform rt #%u - %s", ifindex, i_plat_routes, vtable->vt->route_to_string (cur_plat_route, NULL, 0)); /* skip over @cur_ipx_route that are ordered before @cur_plat_route */ while ( cur_ipx_route @@ -870,7 +874,7 @@ next: _LOGD (vtable->vt->addr_family, "ignore error adding IPv%c route to kernel: %s", vtable->vt->is_ip4 ? '4' : '6', - vtable->vt->route_to_string (cur_ipx_route)); + vtable->vt->route_to_string (cur_ipx_route, NULL, 0)); } else { /* Remember that there was a failure, but for now continue trying * to sync the remaining routes. */ diff --git a/src/nm-types.h b/src/nm-types.h index 2554d47e98..6dbd3b3b45 100644 --- a/src/nm-types.h +++ b/src/nm-types.h @@ -79,6 +79,8 @@ typedef struct _NMPlatformIP6Address NMPlatformIP6Address; typedef struct _NMPlatformIP6Route NMPlatformIP6Route; typedef struct _NMPlatformLink NMPlatformLink; +typedef struct _NMPObject NMPObject; + typedef enum { /* Please don't interpret type numbers outside nm-platform and use functions * like nm_platform_link_is_software() and nm_platform_supports_slaves(). @@ -130,6 +132,13 @@ typedef enum { NMP_OBJECT_TYPE_IP6_ADDRESS, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE, + + NMP_OBJECT_TYPE_LNK_GRE, + NMP_OBJECT_TYPE_LNK_INFINIBAND, + NMP_OBJECT_TYPE_LNK_MACVLAN, + NMP_OBJECT_TYPE_LNK_VLAN, + NMP_OBJECT_TYPE_LNK_VXLAN, + __NMP_OBJECT_TYPE_LAST, NMP_OBJECT_TYPE_MAX = __NMP_OBJECT_TYPE_LAST - 1, } NMPObjectType; diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index cbb9775714..a7c6b000ff 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -27,6 +27,7 @@ #include <linux/rtnetlink.h> #include "nm-utils.h" +#include "nmp-object.h" #include "NetworkManagerUtils.h" #include "nm-fake-platform.h" #include "nm-default.h" @@ -74,8 +75,7 @@ typedef struct { NMPlatformLink link; char *udi; - int vlan_id; - int ib_p_key; + NMPObject *lnk; struct in6_addr ip6_lladdr; } NMFakePlatformLink; @@ -100,6 +100,14 @@ static gboolean ip6_address_delete (NMPlatform *platform, int ifindex, struct in /******************************************************************/ static gboolean +_ip4_address_equal_peer_net (in_addr_t peer1, in_addr_t peer2, int plen) +{ + return ((peer1 ^ peer2) & nm_utils_ip4_prefix_to_netmask (plen)) == 0; +} + +/******************************************************************/ + +static gboolean sysctl_set (NMPlatform *platform, const char *path, const char *value) { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); @@ -252,6 +260,32 @@ _nm_platform_link_get_by_address (NMPlatform *platform, return NULL; } +static const NMPObject * +link_get_lnk (NMPlatform *platform, + int ifindex, + NMLinkType link_type, + const NMPlatformLink **out_link) +{ + NMFakePlatformLink *device = link_get (platform, ifindex); + + if (!device) + return NULL; + + NM_SET_OUT (out_link, &device->link); + + if (!device->lnk) + return NULL; + + if (link_type == NM_LINK_TYPE_NONE) + return device->lnk; + + if ( link_type != device->link.type + || link_type != NMP_OBJECT_GET_CLASS (device->lnk)->lnk_link_type) + return NULL; + + return device->lnk; +} + static gboolean link_add (NMPlatform *platform, const char *name, @@ -265,6 +299,12 @@ link_add (NMPlatform *platform, link_init (&device, priv->links->len, type, name); + if (address) { + g_return_val_if_fail (address_len > 0 && address_len <= sizeof (device.link.addr.data), FALSE); + memcpy (device.link.addr.data, address, address_len); + device.link.addr.len = address_len; + } + g_array_append_val (priv->links, device); if (device.link.ifindex) { @@ -291,6 +331,8 @@ link_delete (NMPlatform *platform, int ifindex) memcpy (&deleted_device, &device->link, sizeof (deleted_device)); memset (&device->link, 0, sizeof (device->link)); + g_clear_pointer (&device->lnk, nmp_object_unref); + g_clear_pointer (&device->udi, g_free); /* Remove addresses and routes which belong to the deleted interface */ for (i = 0; i < priv->ip4_addresses->len; i++) { @@ -631,8 +673,10 @@ vlan_add (NMPlatform *platform, const char *name, int parent, int vlan_id, guint device = link_get (platform, nm_platform_link_get_ifindex (platform, name)); g_return_val_if_fail (device, FALSE); + g_return_val_if_fail (!device->lnk, FALSE); - device->vlan_id = vlan_id; + device->lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_VLAN, NULL); + device->lnk->lnk_vlan.id = vlan_id; device->link.parent = parent; if (out_link) @@ -641,30 +685,18 @@ vlan_add (NMPlatform *platform, const char *name, int parent, int vlan_id, guint } static gboolean -vlan_get_info (NMPlatform *platform, int ifindex, int *parent, int *vlan_id) +link_vlan_change (NMPlatform *platform, + 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) { - NMFakePlatformLink *device = link_get (platform, ifindex); - - g_return_val_if_fail (device, FALSE); - - if (parent) - *parent = device->link.parent; - if (vlan_id) - *vlan_id = device->vlan_id; - - return TRUE; -} - -static gboolean -vlan_set_ingress_map (NMPlatform *platform, int ifindex, int from, int to) -{ - return !!link_get (platform, ifindex); -} - -static gboolean -vlan_set_egress_map (NMPlatform *platform, int ifindex, int from, int to) -{ - return !!link_get (platform, ifindex); + return FALSE; } static gboolean @@ -682,63 +714,17 @@ infiniband_partition_add (NMPlatform *platform, int parent, int p_key, NMPlatfor device = link_get (platform, nm_platform_link_get_ifindex (platform, name)); g_return_val_if_fail (device, FALSE); + g_return_val_if_fail (!device->lnk, FALSE); - device->ib_p_key = p_key; + device->lnk = nmp_object_new (NMP_OBJECT_TYPE_LNK_VLAN, NULL); + device->lnk->lnk_infiniband.p_key = p_key; + device->lnk->lnk_infiniband.mode = "datagram"; device->link.parent = parent; return TRUE; } static gboolean -infiniband_get_info (NMPlatform *platform, int ifindex, int *parent, int *p_key, const char **mode) -{ - NMFakePlatformLink *device; - - device = link_get (platform, ifindex); - g_return_val_if_fail (device, FALSE); - g_return_val_if_fail (device->link.type == NM_LINK_TYPE_INFINIBAND, FALSE); - - if (parent) - *parent = device->link.parent; - if (p_key) - *p_key = device->ib_p_key; - if (mode) - *mode = "datagram"; - - return TRUE; -} - -static gboolean -veth_get_properties (NMPlatform *platform, int ifindex, NMPlatformVethProperties *props) -{ - return FALSE; -} - -static gboolean -tun_get_properties (NMPlatform *platform, int ifindex, NMPlatformTunProperties *props) -{ - return FALSE; -} - -static gboolean -macvlan_get_properties (NMPlatform *platform, int ifindex, NMPlatformMacvlanProperties *props) -{ - return FALSE; -} - -static gboolean -vxlan_get_properties (NMPlatform *platform, int ifindex, NMPlatformVxlanProperties *props) -{ - return FALSE; -} - -static gboolean -gre_get_properties (NMPlatform *platform, int ifindex, NMPlatformGreProperties *props) -{ - return FALSE; -} - -static gboolean wifi_get_capabilities (NMPlatform *platform, int ifindex, NMDeviceWifiCapabilities *caps) { NMFakePlatformLink *device = link_get (platform, ifindex); @@ -908,7 +894,7 @@ ip4_address_add (NMPlatform *platform, address.source = NM_IP_CONFIG_SOURCE_KERNEL; address.ifindex = ifindex; address.address = addr; - address.peer_address = peer_addr && peer_addr != addr ? peer_addr : 0; + address.peer_address = peer_addr; address.plen = plen; address.timestamp = nm_utils_get_monotonic_timestamp_s (); address.lifetime = lifetime; @@ -918,18 +904,19 @@ ip4_address_add (NMPlatform *platform, for (i = 0; i < priv->ip4_addresses->len; i++) { NMPlatformIP4Address *item = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); + gboolean changed; - if (item->ifindex != address.ifindex) - continue; - if (item->address != address.address) - continue; - if (item->plen != address.plen) - continue; - if (!nm_platform_ip4_address_equal_peer_net (item, &address)) + if ( item->ifindex != address.ifindex + || item->address != address.address + || item->plen != address.plen + || !_ip4_address_equal_peer_net (item->peer_address, address.peer_address, address.plen)) continue; + changed = !nm_platform_ip4_address_cmp (item, &address); + memcpy (item, &address, sizeof (address)); - g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &address, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); + if (changed) + g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP4_ADDRESS, ifindex, &address, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); return TRUE; } @@ -965,18 +952,17 @@ ip6_address_add (NMPlatform *platform, for (i = 0; i < priv->ip6_addresses->len; i++) { NMPlatformIP6Address *item = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); + gboolean changed; - if (item->ifindex != address.ifindex) - continue; - if (!IN6_ARE_ADDR_EQUAL (&item->address, &address.address)) - continue; - if (item->plen != address.plen) + if ( item->ifindex != address.ifindex + || !IN6_ARE_ADDR_EQUAL (&item->address, &address.address)) continue; - if (nm_platform_ip6_address_cmp (item, &address) != 0) { - memcpy (item, &address, sizeof (address)); + changed = !nm_platform_ip6_address_cmp (item, &address); + + memcpy (item, &address, sizeof (address)); + if (changed) g_signal_emit_by_name (platform, NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, NMP_OBJECT_TYPE_IP6_ADDRESS, ifindex, &address, NM_PLATFORM_SIGNAL_CHANGED, NM_PLATFORM_REASON_INTERNAL); - } return TRUE; } @@ -995,8 +981,10 @@ ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, int plen, for (i = 0; i < priv->ip4_addresses->len; i++) { NMPlatformIP4Address *address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); - if (address->ifindex == ifindex && address->plen == plen && address->address == addr && - (!peer_address || address->peer_address == peer_address)) { + if ( address->ifindex == ifindex + && address->plen == plen + && address->address == addr + && ((peer_address ^ address->peer_address) & nm_utils_ip4_prefix_to_netmask (plen)) == 0) { NMPlatformIP4Address deleted_address; memcpy (&deleted_address, address, sizeof (deleted_address)); @@ -1018,8 +1006,9 @@ ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int for (i = 0; i < priv->ip6_addresses->len; i++) { NMPlatformIP6Address *address = &g_array_index (priv->ip6_addresses, NMPlatformIP6Address, i); - if (address->ifindex == ifindex && address->plen == plen - && IN6_ARE_ADDR_EQUAL (&address->address, &addr)) { + if ( address->ifindex == ifindex + && address->plen == plen + && IN6_ARE_ADDR_EQUAL (&address->address, &addr)) { NMPlatformIP6Address deleted_address; memcpy (&deleted_address, address, sizeof (deleted_address)); @@ -1037,12 +1026,6 @@ ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, int plen, in { NMFakePlatformPrivate *priv = NM_FAKE_PLATFORM_GET_PRIVATE (platform); int i; - NMPlatformIP4Address a = { - .ifindex = ifindex, - .address = addr, - .plen = plen, - .peer_address = peer_address, - }; for (i = 0; i < priv->ip4_addresses->len; i++) { NMPlatformIP4Address *address = &g_array_index (priv->ip4_addresses, NMPlatformIP4Address, i); @@ -1050,7 +1033,7 @@ ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, int plen, in if ( address->ifindex == ifindex && address->plen == plen && address->address == addr - && nm_platform_ip4_address_equal_peer_net (address, &a)) + && _ip4_address_equal_peer_net (address->peer_address, peer_address, plen)) return address; } @@ -1407,6 +1390,7 @@ nm_fake_platform_finalize (GObject *object) NMFakePlatformLink *device = &g_array_index (priv->links, NMFakePlatformLink, i); g_free (device->udi); + g_clear_pointer (&device->lnk, nmp_object_unref); } g_array_unref (priv->links); g_array_unref (priv->ip4_addresses); @@ -1439,6 +1423,8 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) platform_class->link_delete = link_delete; platform_class->link_get_type_name = link_get_type_name; + platform_class->link_get_lnk = link_get_lnk; + platform_class->link_get_udi = link_get_udi; platform_class->link_set_up = link_set_up; @@ -1462,18 +1448,9 @@ nm_fake_platform_class_init (NMFakePlatformClass *klass) platform_class->slave_get_option = slave_get_option; platform_class->vlan_add = vlan_add; - platform_class->vlan_get_info = vlan_get_info; - platform_class->vlan_set_ingress_map = vlan_set_ingress_map; - platform_class->vlan_set_egress_map = vlan_set_egress_map; + platform_class->link_vlan_change = link_vlan_change; platform_class->infiniband_partition_add = infiniband_partition_add; - platform_class->infiniband_get_info = infiniband_get_info; - - platform_class->veth_get_properties = veth_get_properties; - platform_class->tun_get_properties = tun_get_properties; - platform_class->macvlan_get_properties = macvlan_get_properties; - platform_class->vxlan_get_properties = vxlan_get_properties; - platform_class->gre_get_properties = gre_get_properties; platform_class->wifi_get_capabilities = wifi_get_capabilities; platform_class->wifi_get_bssid = wifi_get_bssid; diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index ed8918be61..b07afc1d45 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -40,16 +40,6 @@ #include <netlink/route/route.h> #include <gudev/gudev.h> -#if HAVE_LIBNL_INET6_ADDR_GEN_MODE || HAVE_LIBNL_INET6_TOKEN -#include <netlink/route/link/inet6.h> -#if HAVE_LIBNL_INET6_ADDR_GEN_MODE && HAVE_KERNEL_INET6_ADDR_GEN_MODE -#include <linux/if_link.h> -#else -#define IN6_ADDR_GEN_MODE_EUI64 0 -#define IN6_ADDR_GEN_MODE_NONE 1 -#endif -#endif - #include "nm-core-internal.h" #include "NetworkManagerUtils.h" #include "nm-linux-platform.h" @@ -68,6 +58,39 @@ /*********************************************************************************************/ +#define IFQDISCSIZ 32 + +/*********************************************************************************************/ + +#ifndef IFLA_PROMISCUITY +#define IFLA_PROMISCUITY 30 +#endif +#define IFLA_NUM_TX_QUEUES 31 +#define IFLA_NUM_RX_QUEUES 32 +#define IFLA_CARRIER 33 +#define IFLA_PHYS_PORT_ID 34 +#define IFLA_LINK_NETNSID 37 +#define __IFLA_MAX 39 + +#define IFLA_INET6_TOKEN 7 +#define IFLA_INET6_ADDR_GEN_MODE 8 +#define __IFLA_INET6_MAX 9 + +#define IFLA_VLAN_PROTOCOL 5 +#define __IFLA_VLAN_MAX 6 + +#define IFA_FLAGS 8 +#define __IFA_MAX 9 + +#define IFLA_MACVLAN_FLAGS 2 +#define __IFLA_MACVLAN_MAX 3 + +#ifndef MACVLAN_FLAG_NOPROMISC +#define MACVLAN_FLAG_NOPROMISC 1 +#endif + +/*********************************************************************************************/ + #define _NMLOG_PREFIX_NAME "platform-linux" #define _NMLOG_DOMAIN LOGD_PLATFORM #define _NMLOG2_DOMAIN LOGD_PLATFORM @@ -119,433 +142,38 @@ typedef enum { DELAYED_ACTION_TYPE_MAX = __DELAYED_ACTION_TYPE_MAX -1, } DelayedActionType; -static gboolean tun_get_properties_ifname (NMPlatform *platform, const char *ifname, NMPlatformTunProperties *props); static void delayed_action_schedule (NMPlatform *platform, DelayedActionType action_type, gpointer user_data); static gboolean delayed_action_handle_all (NMPlatform *platform, gboolean read_netlink); static void do_request_link (NMPlatform *platform, int ifindex, const char *name, gboolean handle_delayed_action); static void do_request_all (NMPlatform *platform, DelayedActionType action_type, gboolean handle_delayed_action); static void cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMPCacheOpsType ops_type, gpointer user_data); static gboolean event_handler_read_netlink_all (NMPlatform *platform, gboolean wait_for_acks); -static NMPCacheOpsType cache_remove_netlink (NMPlatform *platform, const NMPObject *obj_needle, NMPObject **out_obj_cache, gboolean *out_was_visible, NMPlatformReason reason); +static NMPCacheOpsType cache_remove_netlink (NMPlatform *platform, const NMPObject *obj_id, NMPObject **out_obj_cache, gboolean *out_was_visible, NMPlatformReason reason); /****************************************************************** - * libnl unility functions and wrappers + * Support IFLA_INET6_ADDR_GEN_MODE ******************************************************************/ -struct libnl_vtable -{ - void *handle; - void *handle_route; - - int (*f_nl_has_capability) (int capability); - int (*f_rtnl_link_get_link_netnsid) (const struct rtnl_link *link, gint32 *out_link_netnsid); -}; - -static int -_nl_f_nl_has_capability (int capability) -{ - return FALSE; -} - -static const struct libnl_vtable * -_nl_get_vtable (void) -{ - static struct libnl_vtable vtable; - - if (G_UNLIKELY (!vtable.f_nl_has_capability)) { - vtable.handle = dlopen ("libnl-3.so.200", RTLD_LAZY | RTLD_NOLOAD); - if (vtable.handle) { - vtable.f_nl_has_capability = dlsym (vtable.handle, "nl_has_capability"); - } - vtable.handle_route = dlopen ("libnl-route-3.so.200", RTLD_LAZY | RTLD_NOLOAD); - if (vtable.handle_route) { - vtable.f_rtnl_link_get_link_netnsid = dlsym (vtable.handle_route, "rtnl_link_get_link_netnsid"); - } - - if (!vtable.f_nl_has_capability) - vtable.f_nl_has_capability = &_nl_f_nl_has_capability; - - _LOG2t ("libnl: rtnl_link_get_link_netnsid() %s", vtable.f_rtnl_link_get_link_netnsid ? "supported" : "not supported"); - - g_return_val_if_fail (vtable.handle, &vtable); - g_return_val_if_fail (vtable.handle_route, &vtable); - } - - return &vtable; -} - -static gboolean -_nl_has_capability (int capability) -{ - return (_nl_get_vtable ()->f_nl_has_capability) (capability); -} - -static int -_rtnl_link_get_link_netnsid (const struct rtnl_link *link, gint32 *out_link_netnsid) -{ - const struct libnl_vtable *vtable; - - g_return_val_if_fail (link, -NLE_INVAL); - g_return_val_if_fail (out_link_netnsid, -NLE_INVAL); - - vtable = _nl_get_vtable (); - return vtable->f_rtnl_link_get_link_netnsid - ? vtable->f_rtnl_link_get_link_netnsid (link, out_link_netnsid) - : -NLE_OPNOTSUPP; -} - -gboolean -nm_platform_check_support_libnl_link_netnsid (void) -{ - return !!(_nl_get_vtable ()->f_rtnl_link_get_link_netnsid); -} - -/* Automatic deallocation of local variables */ -#define auto_nl_object __attribute__((cleanup(_nl_auto_nl_object))) -static void -_nl_auto_nl_object (void *ptr) -{ - struct nl_object **object = ptr; - - if (object && *object) { - nl_object_put (*object); - *object = NULL; - } -} - -#define auto_nl_addr __attribute__((cleanup(_nl_auto_nl_addr))) -static void -_nl_auto_nl_addr (void *ptr) -{ - struct nl_addr **object = ptr; - - if (object && *object) { - nl_addr_put (*object); - *object = NULL; - } -} - -/* wrap the libnl alloc functions and abort on out-of-memory*/ - -static struct nl_addr * -_nl_addr_build (int family, const void *buf, size_t size) -{ - struct nl_addr *addr; - - addr = nl_addr_build (family, (void *) buf, size); - if (!addr) - g_error ("nl_addr_build() failed with out of memory"); - - return addr; -} - -static struct rtnl_link * -_nl_rtnl_link_alloc (int ifindex, const char*name) -{ - struct rtnl_link *rtnllink; - - rtnllink = rtnl_link_alloc (); - if (!rtnllink) - g_error ("rtnl_link_alloc() failed with out of memory"); - - if (ifindex > 0) - rtnl_link_set_ifindex (rtnllink, ifindex); - if (name) - rtnl_link_set_name (rtnllink, name); - return rtnllink; -} - -static struct rtnl_addr * -_nl_rtnl_addr_alloc (int ifindex) -{ - struct rtnl_addr *rtnladdr; - - rtnladdr = rtnl_addr_alloc (); - if (!rtnladdr) - g_error ("rtnl_addr_alloc() failed with out of memory"); - if (ifindex > 0) - rtnl_addr_set_ifindex (rtnladdr, ifindex); - return rtnladdr; -} - -static struct rtnl_route * -_nl_rtnl_route_alloc (void) -{ - struct rtnl_route *rtnlroute = rtnl_route_alloc (); - - if (!rtnlroute) - g_error ("rtnl_route_alloc() failed with out of memory"); - return rtnlroute; -} - -static struct rtnl_nexthop * -_nl_rtnl_route_nh_alloc (void) -{ - struct rtnl_nexthop *nexthop; - - nexthop = rtnl_route_nh_alloc (); - if (!nexthop) - g_error ("rtnl_route_nh_alloc () failed with out of memory"); - return nexthop; -} - -/* rtnl_addr_set_prefixlen fails to update the nl_addr prefixlen */ -static void -_nl_rtnl_addr_set_prefixlen (struct rtnl_addr *rtnladdr, int plen) -{ - struct nl_addr *nladdr; - - rtnl_addr_set_prefixlen (rtnladdr, plen); - - nladdr = rtnl_addr_get_local (rtnladdr); - if (nladdr) - nl_addr_set_prefixlen (nladdr, plen); -} - -static const char * -_nl_nlmsg_type_to_str (guint16 type, char *buf, gsize len) -{ - const char *str_type = NULL; - - switch (type) { - case RTM_NEWLINK: str_type = "NEWLINK"; break; - case RTM_DELLINK: str_type = "DELLINK"; break; - case RTM_NEWADDR: str_type = "NEWADDR"; break; - case RTM_DELADDR: str_type = "DELADDR"; break; - case RTM_NEWROUTE: str_type = "NEWROUTE"; break; - case RTM_DELROUTE: str_type = "DELROUTE"; break; - } - if (str_type) - g_strlcpy (buf, str_type, len); - else - g_snprintf (buf, len, "(%d)", type); - return buf; -} - -/******************************************************************/ - -/* _nl_link_parse_info_data(): Re-fetches a link from the kernel - * and parses its IFLA_INFO_DATA using a caller-provided parser. - * - * Code is stolen from rtnl_link_get_kernel(), nl_pickup(), and link_msg_parser(). - */ - -typedef int (*NMNLInfoDataParser) (struct nlattr *info_data, gpointer parser_data); - -typedef struct { - NMNLInfoDataParser parser; - gpointer parser_data; -} NMNLInfoDataClosure; - -static struct nla_policy info_data_link_policy[IFLA_MAX + 1] = { - [IFLA_LINKINFO] = { .type = NLA_NESTED }, -}; - -static struct nla_policy info_data_link_info_policy[IFLA_INFO_MAX + 1] = { - [IFLA_INFO_DATA] = { .type = NLA_NESTED }, -}; - -static int -_nl_link_parse_info_data_cb (struct nl_msg *msg, void *arg) -{ - NMNLInfoDataClosure *closure = arg; - struct nlmsghdr *n = nlmsg_hdr (msg); - struct nlattr *tb[IFLA_MAX + 1]; - struct nlattr *li[IFLA_INFO_MAX + 1]; - int err; - - if (!nlmsg_valid_hdr (n, sizeof (struct ifinfomsg))) - return -NLE_MSG_TOOSHORT; - - err = nlmsg_parse (n, sizeof (struct ifinfomsg), tb, IFLA_MAX, info_data_link_policy); - if (err < 0) - return err; - - if (!tb[IFLA_LINKINFO]) - return -NLE_MISSING_ATTR; - - err = nla_parse_nested (li, IFLA_INFO_MAX, tb[IFLA_LINKINFO], info_data_link_info_policy); - if (err < 0) - return err; - - if (!li[IFLA_INFO_DATA]) - return -NLE_MISSING_ATTR; - - return closure->parser (li[IFLA_INFO_DATA], closure->parser_data); -} - -static int -_nl_link_parse_info_data (struct nl_sock *sk, int ifindex, - NMNLInfoDataParser parser, gpointer parser_data) -{ - NMNLInfoDataClosure data = { .parser = parser, .parser_data = parser_data }; - struct nl_msg *msg = NULL; - struct nl_cb *cb; - int err; - - err = rtnl_link_build_get_request (ifindex, NULL, &msg); - if (err < 0) - return err; - - err = nl_send_auto (sk, msg); - nlmsg_free (msg); - if (err < 0) - return err; - - cb = nl_cb_clone (nl_socket_get_cb (sk)); - if (cb == NULL) - return -NLE_NOMEM; - nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, _nl_link_parse_info_data_cb, &data); - - err = nl_recvmsgs (sk, cb); - nl_cb_put (cb); - if (err < 0) - return err; - - nl_wait_for_ack (sk); - return 0; -} - -/******************************************************************/ - -static int -_nl_sock_flush_data (struct nl_sock *sk) -{ - int nle; - struct nl_cb *cb; - - cb = nl_cb_clone (nl_socket_get_cb (sk)); - if (cb == NULL) - return -NLE_NOMEM; - - nl_cb_set (cb, NL_CB_VALID, NL_CB_DEFAULT, NULL, NULL); - nl_cb_set (cb, NL_CB_SEQ_CHECK, NL_CB_DEFAULT, NULL, NULL); - nl_cb_err (cb, NL_CB_DEFAULT, NULL, NULL); - do { - errno = 0; - - nle = nl_recvmsgs (sk, cb); - - /* Work around a libnl bug fixed in 3.2.22 (375a6294) */ - if (nle == 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) - nle = -NLE_AGAIN; - } while (nle != -NLE_AGAIN); - - nl_cb_put (cb); - return nle; -} - -static void -_nl_msg_set_seq (struct nl_sock *sk, struct nl_msg *msg, guint32 *out_seq) -{ - guint32 seq; - - /* choose our own sequence number, because libnl does not ensure that - * it isn't zero -- which would confuse our checking for outstanding - * messages. */ - seq = nl_socket_use_seq (sk); - if (seq == 0) - seq = nl_socket_use_seq (sk); - - nlmsg_hdr (msg)->nlmsg_seq = seq; - if (out_seq) - *out_seq = seq; -} - -static int -_nl_sock_request_link (NMPlatform *platform, struct nl_sock *sk, int ifindex, const char *name, guint32 *out_seq) -{ - struct nl_msg *msg = NULL; - int err; - - if (name && !name[0]) - name = NULL; - - g_return_val_if_fail (ifindex > 0 || name, -NLE_INVAL); - - _LOGT ("sock: request-link %d%s%s%s", ifindex, name ? ", \"" : "", name ? name : "", name ? "\"" : ""); - - if ((err = rtnl_link_build_get_request (ifindex, name, &msg)) < 0) - return err; - - _nl_msg_set_seq (sk, msg, out_seq); - - err = nl_send_auto (sk, msg); - nlmsg_free(msg); - if (err < 0) - return err; - - return 0; -} - -static int -_nl_sock_request_all (NMPlatform *platform, struct nl_sock *sk, NMPObjectType obj_type, guint32 *out_seq) -{ - const NMPClass *klass; - struct rtgenmsg gmsg = { 0 }; - struct nl_msg *msg; - int err; - - klass = nmp_class_from_type (obj_type); - - _LOGT ("sock: request-all-%s", klass->obj_type_name); - - /* reimplement - * nl_rtgen_request (sk, klass->rtm_gettype, klass->addr_family, NLM_F_DUMP); - * because we need the sequence number. - */ - msg = nlmsg_alloc_simple (klass->rtm_gettype, NLM_F_DUMP); - if (!msg) - return -NLE_NOMEM; - - gmsg.rtgen_family = klass->addr_family; - err = nlmsg_append (msg, &gmsg, sizeof (gmsg), NLMSG_ALIGNTO); - if (err < 0) - goto errout; - - _nl_msg_set_seq (sk, msg, out_seq); - - err = nl_send_auto (sk, msg); -errout: - nlmsg_free(msg); - - return err >= 0 ? 0 : err; -} - -/******************************************************************/ - -#if HAVE_LIBNL_INET6_ADDR_GEN_MODE static int _support_user_ipv6ll = 0; #define _support_user_ipv6ll_still_undecided() (G_UNLIKELY (_support_user_ipv6ll == 0)) -#else -#define _support_user_ipv6ll_still_undecided() (FALSE) -#endif static gboolean _support_user_ipv6ll_get (void) { -#if HAVE_LIBNL_INET6_ADDR_GEN_MODE if (_support_user_ipv6ll_still_undecided ()) { _support_user_ipv6ll = -1; _LOG2W ("kernel support for IFLA_INET6_ADDR_GEN_MODE %s", "failed to detect; assume no support"); - } else - return _support_user_ipv6ll > 0; -#endif + return FALSE; + } + return _support_user_ipv6ll > 0; - return FALSE; } static void -_support_user_ipv6ll_detect (const struct rtnl_link *rtnl_link) +_support_user_ipv6ll_detect (struct nlattr **tb) { -#if HAVE_LIBNL_INET6_ADDR_GEN_MODE - /* If we ever see a link with valid IPv6 link-local address - * generation modes, the kernel supports it. - */ if (_support_user_ipv6ll_still_undecided ()) { - uint8_t mode; - - if (rtnl_link_inet6_get_addr_gen_mode ((struct rtnl_link *) rtnl_link, &mode) == 0) { + if (tb[IFLA_INET6_ADDR_GEN_MODE]) { _support_user_ipv6ll = 1; _LOG2D ("kernel support for IFLA_INET6_ADDR_GEN_MODE %s", "detected"); } else { @@ -553,54 +181,23 @@ _support_user_ipv6ll_detect (const struct rtnl_link *rtnl_link) _LOG2D ("kernel support for IFLA_INET6_ADDR_GEN_MODE %s", "not detected"); } } -#endif } -/******************************************************************/ - -static int _support_kernel_extended_ifa_flags = 0; - -#define _support_kernel_extended_ifa_flags_still_undecided() (G_UNLIKELY (_support_kernel_extended_ifa_flags == 0)) - -static void -_support_kernel_extended_ifa_flags_detect (struct nl_msg *msg) -{ - struct nlmsghdr *msg_hdr; - - if (!_support_kernel_extended_ifa_flags_still_undecided ()) - return; - - msg_hdr = nlmsg_hdr (msg); - if (msg_hdr->nlmsg_type != RTM_NEWADDR) - return; +/****************************************************************** + * Various utilities + ******************************************************************/ - /* the extended address flags are only set for AF_INET6 */ - if (((struct ifaddrmsg *) nlmsg_data (msg_hdr))->ifa_family != AF_INET6) - return; +const NMIPAddr nm_ip_addr_zero = NMIPAddrInit; - /* see if the nl_msg contains the IFA_FLAGS attribute. If it does, - * we assume, that the kernel supports extended flags, IFA_F_MANAGETEMPADDR - * and IFA_F_NOPREFIXROUTE (they were added together). - **/ - _support_kernel_extended_ifa_flags = - nlmsg_find_attr (msg_hdr, sizeof (struct ifaddrmsg), 8 /* IFA_FLAGS */) - ? 1 : -1; -} +#define IPV4LL_NETWORK (htonl (0xA9FE0000L)) +#define IPV4LL_NETMASK (htonl (0xFFFF0000L)) static gboolean -_support_kernel_extended_ifa_flags_get (void) +ip4_address_is_link_local (in_addr_t addr) { - if (_support_kernel_extended_ifa_flags_still_undecided ()) { - _LOG2W ("Unable to detect kernel support for extended IFA_FLAGS. Assume no kernel support."); - _support_kernel_extended_ifa_flags = -1; - } - return _support_kernel_extended_ifa_flags > 0; + return (addr & IPV4LL_NETMASK) == IPV4LL_NETWORK; } -/****************************************************************** - * Object type specific utilities - ******************************************************************/ - static guint _nm_ip_config_source_to_rtprot (NMIPConfigSource source) { @@ -640,7 +237,46 @@ _nm_ip_config_source_from_rtprot (guint rtprot) } } -/******************************************************************/ +static void +clear_host_address (int family, const void *network, int plen, void *dst) +{ + g_return_if_fail (plen == (guint8)plen); + g_return_if_fail (network); + + switch (family) { + case AF_INET: + *((in_addr_t *) dst) = nm_utils_ip4_address_clear_host_address (*((in_addr_t *) network), plen); + break; + case AF_INET6: + nm_utils_ip6_address_clear_host_address ((struct in6_addr *) dst, (const struct in6_addr *) network, plen); + break; + default: + g_assert_not_reached (); + } +} + +static int +_vlan_qos_mapping_cmp_from (gconstpointer a, gconstpointer b, gpointer user_data) +{ + const NMVlanQosMapping *map_a = a; + const NMVlanQosMapping *map_b = b; + + if (map_a->from != map_b->from) + return map_a->from < map_b->from ? -1 : 1; + return 0; +} + +static int +_vlan_qos_mapping_cmp_from_ptr (gconstpointer a, gconstpointer b, gpointer user_data) +{ + return _vlan_qos_mapping_cmp_from (*((const NMVlanQosMapping **) a), + *((const NMVlanQosMapping **) b), + NULL); +} + +/****************************************************************** + * NMLinkType functions + ******************************************************************/ typedef struct { const NMLinkType nm_type; @@ -719,129 +355,172 @@ nm_link_type_to_string (NMLinkType type) } /****************************************************************** - * NMPlatform types and functions + * Utilities ******************************************************************/ -typedef struct _NMLinuxPlatformPrivate NMLinuxPlatformPrivate; +/* _timestamp_nl_to_ms: + * @timestamp_nl: a timestamp from ifa_cacheinfo. + * @monotonic_ms: *now* in CLOCK_MONOTONIC. Needed to estimate the current + * uptime and how often timestamp_nl wrapped. + * + * Convert the timestamp from ifa_cacheinfo to CLOCK_MONOTONIC milliseconds. + * The ifa_cacheinfo fields tstamp and cstamp contains timestamps that counts + * with in 1/100th of a second of clock_gettime(CLOCK_MONOTONIC). However, + * the uint32 counter wraps every 497 days of uptime, so we have to compensate + * for that. */ +static gint64 +_timestamp_nl_to_ms (guint32 timestamp_nl, gint64 monotonic_ms) +{ + const gint64 WRAP_INTERVAL = (((gint64) G_MAXUINT32) + 1) * (1000 / 100); + gint64 timestamp_nl_ms; -struct _NMLinuxPlatformPrivate { - struct nl_sock *nlh; - struct nl_sock *nlh_event; - guint32 nlh_seq_expect; - guint32 nlh_seq_last; - NMPCache *cache; - GIOChannel *event_channel; - guint event_id; + /* convert timestamp from 1/100th of a second to msec. */ + timestamp_nl_ms = ((gint64) timestamp_nl) * (1000 / 100); - gboolean sysctl_get_warned; - GHashTable *sysctl_get_prev_values; + /* timestamp wraps every 497 days. Try to compensate for that.*/ + if (timestamp_nl_ms > monotonic_ms) { + /* timestamp_nl_ms is in the future. Truncate it to *now* */ + timestamp_nl_ms = monotonic_ms; + } else if (monotonic_ms >= WRAP_INTERVAL) { + timestamp_nl_ms += (monotonic_ms / WRAP_INTERVAL) * WRAP_INTERVAL; + if (timestamp_nl_ms > monotonic_ms) + timestamp_nl_ms -= WRAP_INTERVAL; + } - GUdevClient *udev_client; + return timestamp_nl_ms; +} - struct { - DelayedActionType flags; - GPtrArray *list_master_connected; - GPtrArray *list_refresh_link; - gint is_handling; - guint idle_id; - } delayed_action; +static guint32 +_addrtime_timestamp_to_nm (guint32 timestamp, gint32 *out_now_nm) +{ + struct timespec tp; + gint64 now_nl, now_nm, result; + int err; - GHashTable *prune_candidates; - GHashTable *delayed_deletion; + /* timestamp is unset. Default to 1. */ + if (!timestamp) { + if (out_now_nm) + *out_now_nm = 0; + return 1; + } - GHashTable *wifi_data; -}; + /* do all the calculations in milliseconds scale */ -static inline NMLinuxPlatformPrivate * -NM_LINUX_PLATFORM_GET_PRIVATE (const void *self) -{ - nm_assert (NM_IS_LINUX_PLATFORM (self)); + err = clock_gettime (CLOCK_MONOTONIC, &tp); + g_assert (err == 0); + now_nm = nm_utils_get_monotonic_timestamp_ms (); + now_nl = (((gint64) tp.tv_sec) * ((gint64) 1000)) + + (tp.tv_nsec / (NM_UTILS_NS_PER_SECOND/1000)); - return ((NMLinuxPlatform *) self)->priv; -} + result = now_nm - (now_nl - _timestamp_nl_to_ms (timestamp, now_nl)); -G_DEFINE_TYPE (NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM) + if (out_now_nm) + *out_now_nm = now_nm / 1000; -void -nm_linux_platform_setup (void) -{ - g_object_new (NM_TYPE_LINUX_PLATFORM, - NM_PLATFORM_REGISTER_SINGLETON, TRUE, - NULL); -} + /* converting the timestamp into nm_utils_get_monotonic_timestamp_ms() scale is + * a good guess but fails in the following situations: + * + * - If the address existed before start of the process, the timestamp in nm scale would + * be negative or zero. In this case we default to 1. + * - during hibernation, the CLOCK_MONOTONIC/timestamp drifts from + * nm_utils_get_monotonic_timestamp_ms() scale. + */ + if (result <= 1000) + return 1; -/******************************************************************/ + if (result > now_nm) + return now_nm / 1000; + + return result / 1000; +} -NMPObjectType -_nlo_get_object_type (const struct nl_object *object) +static guint32 +_addrtime_extend_lifetime (guint32 lifetime, guint32 seconds) { - const char *type_str; + guint64 v; - if (!object || !(type_str = nl_object_get_type (object))) - return NMP_OBJECT_TYPE_UNKNOWN; + if ( lifetime == NM_PLATFORM_LIFETIME_PERMANENT + || seconds == 0) + return lifetime; - if (!strcmp (type_str, "route/link")) - return NMP_OBJECT_TYPE_LINK; - else if (!strcmp (type_str, "route/addr")) { - switch (rtnl_addr_get_family ((struct rtnl_addr *) object)) { - case AF_INET: - return NMP_OBJECT_TYPE_IP4_ADDRESS; - case AF_INET6: - return NMP_OBJECT_TYPE_IP6_ADDRESS; - default: - return NMP_OBJECT_TYPE_UNKNOWN; - } - } else if (!strcmp (type_str, "route/route")) { - switch (rtnl_route_get_family ((struct rtnl_route *) object)) { - case AF_INET: - return NMP_OBJECT_TYPE_IP4_ROUTE; - case AF_INET6: - return NMP_OBJECT_TYPE_IP6_ROUTE; - default: - return NMP_OBJECT_TYPE_UNKNOWN; - } - } else - return NMP_OBJECT_TYPE_UNKNOWN; + v = (guint64) lifetime + (guint64) seconds; + return MIN (v, NM_PLATFORM_LIFETIME_PERMANENT - 1); } -/******************************************************************/ - -static gboolean -check_support_kernel_extended_ifa_flags (NMPlatform *platform) +/* The rtnl_addr object contains relative lifetimes @valid and @preferred + * that count in seconds, starting from the moment when the kernel constructed + * the netlink message. + * + * There is also a field rtnl_addr_last_update_time(), which is the absolute + * time in 1/100th of a second of clock_gettime (CLOCK_MONOTONIC) when the address + * was modified (wrapping every 497 days). + * Immediately at the time when the address was last modified, #NOW and @last_update_time + * are the same, so (only) in that case @valid and @preferred are anchored at @last_update_time. + * However, this is not true in general. As time goes by, whenever kernel sends a new address + * via netlink, the lifetimes keep counting down. + **/ +static void +_addrtime_get_lifetimes (guint32 timestamp, + guint32 lifetime, + guint32 preferred, + guint32 *out_timestamp, + guint32 *out_lifetime, + guint32 *out_preferred) { - g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); + gint32 now; - return _support_kernel_extended_ifa_flags_get (); -} + if ( lifetime != NM_PLATFORM_LIFETIME_PERMANENT + || preferred != NM_PLATFORM_LIFETIME_PERMANENT) { + if (preferred > lifetime) + preferred = lifetime; + timestamp = _addrtime_timestamp_to_nm (timestamp, &now); -static gboolean -check_support_user_ipv6ll (NMPlatform *platform) -{ - g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); + if (now == 0) { + /* strange. failed to detect the last-update time and assumed that timestamp is 1. */ + nm_assert (timestamp == 1); + now = nm_utils_get_monotonic_timestamp_s (); + } + if (timestamp < now) { + guint32 diff = now - timestamp; - return _support_user_ipv6ll_get (); + lifetime = _addrtime_extend_lifetime (lifetime, diff); + preferred = _addrtime_extend_lifetime (preferred, diff); + } else + nm_assert (timestamp == now); + } else + timestamp = 0; + *out_timestamp = timestamp; + *out_lifetime = lifetime; + *out_preferred = preferred; } -static void -process_events (NMPlatform *platform) +/******************************************************************/ + +static const NMPObject * +_lookup_cached_link (const NMPCache *cache, int ifindex, gboolean *completed_from_cache, const NMPObject **link_cached) { - delayed_action_handle_all (platform, TRUE); -} + const NMPObject *obj; -/******************************************************************/ + nm_assert (completed_from_cache && link_cached); -#define cache_lookup_all_objects(type, platform, obj_type, visible_only) \ - ((const type *const*) nmp_cache_lookup_multi (NM_LINUX_PLATFORM_GET_PRIVATE ((platform))->cache, \ - nmp_cache_id_init_object_type (NMP_CACHE_ID_STATIC, (obj_type), (visible_only)), \ - NULL)) + if (!*completed_from_cache) { + obj = ifindex > 0 && cache ? nmp_cache_lookup_link (cache, ifindex) : NULL; + if (obj && !obj->_link.netlink.is_in_netlink) + *link_cached = obj; + else + *link_cached = NULL; + *completed_from_cache = TRUE; + } + return *link_cached; +} /******************************************************************/ #define DEVTYPE_PREFIX "DEVTYPE=" static char * -read_devtype (const char *sysfs_path) +_linktype_read_devtype (const char *sysfs_path) { gs_free char *uevent = g_strdup_printf ("%s/uevent", sysfs_path); char *contents = NULL; @@ -863,69 +542,63 @@ read_devtype (const char *sysfs_path) return NULL; } -static const NMPObject * -_lookup_link_cached (NMPlatform *platform, int ifindex, gboolean *completed_from_cache, const NMPObject **link_cached) -{ - const NMPObject *obj; - - nm_assert (completed_from_cache && link_cached); - - if (!*completed_from_cache) { - obj = ifindex > 0 ? nmp_cache_lookup_link (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, ifindex) : NULL; - - if (obj && !obj->_link.netlink.is_in_netlink) - *link_cached = obj; - else - *link_cached = NULL; - *completed_from_cache = TRUE; - } - return *link_cached; -} - static NMLinkType -link_extract_type (NMPlatform *platform, struct rtnl_link *rtnllink, gboolean *completed_from_cache, const NMPObject **link_cached, const char **out_kind) +_linktype_get_type (NMPlatform *platform, + const NMPCache *cache, + const char *kind, + int ifindex, + const char *ifname, + unsigned flags, + unsigned arptype, + gboolean *completed_from_cache, + const NMPObject **link_cached, + const char **out_kind) { - const char *rtnl_type, *ifname; - int i, arptype; - - if (!rtnllink) { - if (out_kind) - *out_kind = NULL; - return NM_LINK_TYPE_NONE; - } + guint i; - rtnl_type = rtnl_link_get_type (rtnllink); - if (!rtnl_type && completed_from_cache) { + if (completed_from_cache) { const NMPObject *obj; - obj = _lookup_link_cached (platform, rtnl_link_get_ifindex (rtnllink), completed_from_cache, link_cached); - if (obj && obj->link.kind) { - rtnl_type = obj->link.kind; - _LOGT ("link_extract_type(): complete kind from cache: ifindex=%d, kind=%s", rtnl_link_get_ifindex (rtnllink), rtnl_type); + obj = _lookup_cached_link (cache, ifindex, completed_from_cache, link_cached); + + /* If we detected the link type before, we stick to that + * decision unless the "kind" changed. + * + * This way, we save edditional ethtool/sysctl lookups, but moreover, + * we keep the linktype stable and don't change it as long as the link + * exists. + * + * Note that kernel *can* reuse the ifindex (on integer overflow, and + * when moving interfce to other netns). Thus here there is a tiny potential + * of messing stuff up. */ + if ( obj + && !NM_IN_SET (obj->link.type, NM_LINK_TYPE_UNKNOWN, NM_LINK_TYPE_NONE) + && ( !kind + || !g_strcmp0 (kind, obj->link.kind))) { + nm_assert (obj->link.kind == g_intern_string (obj->link.kind)); + *out_kind = obj->link.kind; + return obj->link.type; } } - if (out_kind) - *out_kind = rtnl_type; - if (rtnl_type) { + + *out_kind = g_intern_string (kind); + + if (kind) { for (i = 0; i < G_N_ELEMENTS (linktypes); i++) { - if (g_strcmp0 (rtnl_type, linktypes[i].rtnl_type) == 0) + if (g_strcmp0 (kind, linktypes[i].rtnl_type) == 0) return linktypes[i].nm_type; } - if (!strcmp (rtnl_type, "tun")) { + if (!strcmp (kind, "tun")) { NMPlatformTunProperties props; - guint flags; - if (tun_get_properties_ifname (platform, rtnl_link_get_name (rtnllink), &props)) { + if ( platform + && nm_platform_tun_get_properties_ifname (platform, ifname, &props)) { if (!g_strcmp0 (props.mode, "tap")) return NM_LINK_TYPE_TAP; if (!g_strcmp0 (props.mode, "tun")) return NM_LINK_TYPE_TUN; } - flags = rtnl_link_get_flags (rtnllink); - - _LOGD ("Failed to read tun properties for interface %d (link flags: %X)", - rtnl_link_get_ifindex (rtnllink), flags); /* try guessing the type using the link flags instead... */ if (flags & IFF_POINTOPOINT) @@ -934,13 +607,11 @@ link_extract_type (NMPlatform *platform, struct rtnl_link *rtnllink, gboolean *c } } - arptype = rtnl_link_get_arptype (rtnllink); if (arptype == ARPHRD_LOOPBACK) return NM_LINK_TYPE_LOOPBACK; else if (arptype == ARPHRD_INFINIBAND) return NM_LINK_TYPE_INFINIBAND; - ifname = rtnl_link_get_name (rtnllink); if (ifname) { gs_free char *driver = NULL; gs_free char *sysfs_path = NULL; @@ -966,7 +637,7 @@ link_extract_type (NMPlatform *platform, struct rtnl_link *rtnllink, gboolean *c if (g_file_test (anycast_mask, G_FILE_TEST_EXISTS)) return NM_LINK_TYPE_OLPC_MESH; - devtype = read_devtype (sysfs_path); + devtype = _linktype_read_devtype (sysfs_path); for (i = 0; devtype && i < G_N_ELEMENTS (linktypes); i++) { if (g_strcmp0 (devtype, linktypes[i].devtype) == 0) { if (linktypes[i].nm_type == NM_LINK_TYPE_BNEP) { @@ -989,366 +660,1004 @@ link_extract_type (NMPlatform *platform, struct rtnl_link *rtnllink, gboolean *c * prevent future virtual network drivers from being treated as Ethernet * when they should be Generic instead. */ - if (arptype == ARPHRD_ETHER && !rtnl_type && !devtype) + if (arptype == ARPHRD_ETHER && !kind && !devtype) return NM_LINK_TYPE_ETHERNET; } return NM_LINK_TYPE_UNKNOWN; } -gboolean -_nmp_vt_cmd_plobj_init_from_nl_link (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache) +/****************************************************************** + * libnl unility functions and wrappers + ******************************************************************/ + +#define nm_auto_nlmsg __attribute__((cleanup(_nm_auto_nl_msg_cleanup))) +static void +_nm_auto_nl_msg_cleanup (void *ptr) { - NMPlatformLink *obj = (NMPlatformLink *) _obj; - NMPObjectLink *obj_priv = (NMPObjectLink *) _obj; - struct rtnl_link *nlo = (struct rtnl_link *) _nlo; - const char *name; - struct nl_addr *nladdr; - const char *kind; - gboolean completed_from_cache_val = FALSE; - gboolean *completed_from_cache = complete_from_cache ? &completed_from_cache_val : NULL; - const NMPObject *link_cached = NULL; - int parent; + nlmsg_free (*((struct nl_msg **) ptr)); +} - nm_assert (memcmp (obj, ((char [sizeof (NMPObjectLink)]) { 0 }), sizeof (NMPObjectLink)) == 0); +static const char * +_nl_nlmsg_type_to_str (guint16 type, char *buf, gsize len) +{ + const char *str_type = NULL; - if (_LOGT_ENABLED () && !NM_IN_SET (rtnl_link_get_family (nlo), AF_UNSPEC, AF_BRIDGE)) - _LOGT ("netlink object for ifindex %d has unusual family %d", rtnl_link_get_ifindex (nlo), rtnl_link_get_family (nlo)); + switch (type) { + case RTM_NEWLINK: str_type = "NEWLINK"; break; + case RTM_DELLINK: str_type = "DELLINK"; break; + case RTM_NEWADDR: str_type = "NEWADDR"; break; + case RTM_DELADDR: str_type = "DELADDR"; break; + case RTM_NEWROUTE: str_type = "NEWROUTE"; break; + case RTM_DELROUTE: str_type = "DELROUTE"; break; + } + if (str_type) + g_strlcpy (buf, str_type, len); + else + g_snprintf (buf, len, "(%d)", type); + return buf; +} - obj->ifindex = rtnl_link_get_ifindex (nlo); +/****************************************************************** + * NMPObject/netlink functions + ******************************************************************/ - if (id_only) - return TRUE; +#define _check_addr_or_errout(tb, attr, addr_len) \ + ({ \ + const struct nlattr *__t = (tb)[(attr)]; \ + \ + if (__t) { \ + if (nla_len (__t) != (addr_len)) { \ + goto errout; \ + } \ + } \ + !!__t; \ + }) - name = rtnl_link_get_name (nlo); - if (name) - g_strlcpy (obj->name, name, sizeof (obj->name)); - obj->type = link_extract_type (platform, nlo, completed_from_cache, &link_cached, &kind); - obj->kind = g_intern_string (kind); - obj->flags = rtnl_link_get_flags (nlo); - obj->connected = NM_FLAGS_HAS (obj->flags, IFF_LOWER_UP); - obj->master = rtnl_link_get_master (nlo); - parent = rtnl_link_get_link (nlo); - if (parent > 0) { - gint32 link_netnsid; - - if (_rtnl_link_get_link_netnsid (nlo, &link_netnsid) == 0) - obj->parent = NM_PLATFORM_LINK_OTHER_NETNS; - else - obj->parent = parent; - } - obj->mtu = rtnl_link_get_mtu (nlo); - obj->arptype = rtnl_link_get_arptype (nlo); - - if (obj->type == NM_LINK_TYPE_VLAN) { - if (!g_strcmp0 (rtnl_link_get_type (nlo), "vlan")) - obj->vlan_id = rtnl_link_vlan_get_id (nlo); - else if (completed_from_cache) { - _lookup_link_cached (platform, obj->ifindex, completed_from_cache, &link_cached); - if (link_cached) - obj->vlan_id = link_cached->link.vlan_id; - } - } +/*****************************************************************************/ - if ((nladdr = rtnl_link_get_addr (nlo))) { - unsigned int l = 0; +/* Copied and heavily modified from libnl3's inet6_parse_protinfo(). */ +static gboolean +_parse_af_inet6 (NMPlatform *platform, + struct nlattr *attr, + NMUtilsIPv6IfaceId *out_iid, + guint8 *out_iid_is_valid, + guint8 *out_addr_gen_mode_inv) +{ + static struct nla_policy policy[IFLA_INET6_MAX+1] = { + [IFLA_INET6_FLAGS] = { .type = NLA_U32 }, + [IFLA_INET6_CACHEINFO] = { .minlen = sizeof(struct ifla_cacheinfo) }, + [IFLA_INET6_CONF] = { .minlen = 4 }, + [IFLA_INET6_STATS] = { .minlen = 8 }, + [IFLA_INET6_ICMP6STATS] = { .minlen = 8 }, + [IFLA_INET6_TOKEN] = { .minlen = sizeof(struct in6_addr) }, + [IFLA_INET6_ADDR_GEN_MODE] = { .type = NLA_U8 }, + }; + struct nlattr *tb[IFLA_INET6_MAX+1]; + int err; + struct in6_addr i6_token; + gboolean iid_is_valid = FALSE; + guint8 i6_addr_gen_mode_inv = 0; + gboolean success = FALSE; - l = nl_addr_get_len (nladdr); - if (l > 0 && l <= NM_UTILS_HWADDR_LEN_MAX) { - G_STATIC_ASSERT (NM_UTILS_HWADDR_LEN_MAX == sizeof (obj->addr.data)); - memcpy (obj->addr.data, nl_addr_get_binary_addr (nladdr), l); - obj->addr.len = l; - } - } + err = nla_parse_nested (tb, IFLA_INET6_MAX, attr, policy); + if (err < 0) + goto errout; -#if HAVE_LIBNL_INET6_ADDR_GEN_MODE - if (_support_user_ipv6ll_get ()) { - guint8 mode = 0; + if (tb[IFLA_INET6_CONF] && nla_len(tb[IFLA_INET6_CONF]) % 4) + goto errout; + if (tb[IFLA_INET6_STATS] && nla_len(tb[IFLA_INET6_STATS]) % 8) + goto errout; + if (tb[IFLA_INET6_ICMP6STATS] && nla_len(tb[IFLA_INET6_ICMP6STATS]) % 8) + goto errout; - if (rtnl_link_inet6_get_addr_gen_mode (nlo, &mode) == 0) - obj->inet6_addr_gen_mode_inv = _nm_platform_uint8_inv (mode); + if (_check_addr_or_errout (tb, IFLA_INET6_TOKEN, sizeof (struct in6_addr))) { + nla_memcpy (&i6_token, tb[IFLA_INET6_TOKEN], sizeof (struct in6_addr)); + if (!IN6_IS_ADDR_UNSPECIFIED (&i6_token)) + iid_is_valid = TRUE; } -#endif -#if HAVE_LIBNL_INET6_TOKEN - if ((rtnl_link_inet6_get_token (nlo, &nladdr)) == 0) { - if ( nl_addr_get_family (nladdr) == AF_INET6 - && nl_addr_get_len (nladdr) == sizeof (struct in6_addr)) { - struct in6_addr *addr; - NMUtilsIPv6IfaceId *iid = &obj->inet6_token.iid; - - addr = nl_addr_get_binary_addr (nladdr); - iid->id_u8[7] = addr->s6_addr[15]; - iid->id_u8[6] = addr->s6_addr[14]; - iid->id_u8[5] = addr->s6_addr[13]; - iid->id_u8[4] = addr->s6_addr[12]; - iid->id_u8[3] = addr->s6_addr[11]; - iid->id_u8[2] = addr->s6_addr[10]; - iid->id_u8[1] = addr->s6_addr[9]; - iid->id_u8[0] = addr->s6_addr[8]; - obj->inet6_token.is_valid = TRUE; + /* Hack to detect support addrgenmode of the kernel. We only parse + * netlink messages that we receive from kernel, hence this check + * is valid. */ + _support_user_ipv6ll_detect (tb); + + if (tb[IFLA_INET6_ADDR_GEN_MODE]) { + i6_addr_gen_mode_inv = _nm_platform_uint8_inv (nla_get_u8 (tb[IFLA_INET6_ADDR_GEN_MODE])); + if (i6_addr_gen_mode_inv == 0) { + /* an inverse addrgenmode of zero is unexpected. We need to reserve zero + * to signal "unset". */ + goto errout; } - nl_addr_put (nladdr); } -#endif - - obj_priv->netlink.is_in_netlink = TRUE; - return TRUE; + success = TRUE; + if (iid_is_valid) { + out_iid->id_u8[7] = i6_token.s6_addr[15]; + out_iid->id_u8[6] = i6_token.s6_addr[14]; + out_iid->id_u8[5] = i6_token.s6_addr[13]; + out_iid->id_u8[4] = i6_token.s6_addr[12]; + out_iid->id_u8[3] = i6_token.s6_addr[11]; + out_iid->id_u8[2] = i6_token.s6_addr[10]; + out_iid->id_u8[1] = i6_token.s6_addr[9]; + out_iid->id_u8[0] = i6_token.s6_addr[8]; + *out_iid_is_valid = TRUE; + } + *out_addr_gen_mode_inv = i6_addr_gen_mode_inv; +errout: + return success; } -/* _timestamp_nl_to_ms: - * @timestamp_nl: a timestamp from ifa_cacheinfo. - * @monotonic_ms: *now* in CLOCK_MONOTONIC. Needed to estimate the current - * uptime and how often timestamp_nl wrapped. - * - * Convert the timestamp from ifa_cacheinfo to CLOCK_MONOTONIC milliseconds. - * The ifa_cacheinfo fields tstamp and cstamp contains timestamps that counts - * with in 1/100th of a second of clock_gettime(CLOCK_MONOTONIC). However, - * the uint32 counter wraps every 497 days of uptime, so we have to compensate - * for that. */ -static gint64 -_timestamp_nl_to_ms (guint32 timestamp_nl, gint64 monotonic_ms) +/*****************************************************************************/ + +static NMPObject * +_parse_lnk_gre (const char *kind, struct nlattr *info_data) { - const gint64 WRAP_INTERVAL = (((gint64) G_MAXUINT32) + 1) * (1000 / 100); - gint64 timestamp_nl_ms; + static struct nla_policy policy[IFLA_GRE_MAX + 1] = { + [IFLA_GRE_LINK] = { .type = NLA_U32 }, + [IFLA_GRE_IFLAGS] = { .type = NLA_U16 }, + [IFLA_GRE_OFLAGS] = { .type = NLA_U16 }, + [IFLA_GRE_IKEY] = { .type = NLA_U32 }, + [IFLA_GRE_OKEY] = { .type = NLA_U32 }, + [IFLA_GRE_LOCAL] = { .type = NLA_U32 }, + [IFLA_GRE_REMOTE] = { .type = NLA_U32 }, + [IFLA_GRE_TTL] = { .type = NLA_U8 }, + [IFLA_GRE_TOS] = { .type = NLA_U8 }, + [IFLA_GRE_PMTUDISC] = { .type = NLA_U8 }, + }; + struct nlattr *tb[IFLA_GRE_MAX + 1]; + int err; + NMPObject *obj; + NMPlatformLnkGre *props; - /* convert timestamp from 1/100th of a second to msec. */ - timestamp_nl_ms = ((gint64) timestamp_nl) * (1000 / 100); + if (!info_data || g_strcmp0 (kind, "gre")) + return NULL; - /* timestamp wraps every 497 days. Try to compensate for that.*/ - if (timestamp_nl_ms > monotonic_ms) { - /* timestamp_nl_ms is in the future. Truncate it to *now* */ - timestamp_nl_ms = monotonic_ms; - } else if (monotonic_ms >= WRAP_INTERVAL) { - timestamp_nl_ms += (monotonic_ms / WRAP_INTERVAL) * WRAP_INTERVAL; - if (timestamp_nl_ms > monotonic_ms) - timestamp_nl_ms -= WRAP_INTERVAL; - } + err = nla_parse_nested (tb, IFLA_GRE_MAX, info_data, policy); + if (err < 0) + return NULL; - return timestamp_nl_ms; + obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_GRE, NULL); + props = &obj->lnk_gre; + + props->parent_ifindex = tb[IFLA_GRE_LINK] ? nla_get_u32 (tb[IFLA_GRE_LINK]) : 0; + props->input_flags = tb[IFLA_GRE_IFLAGS] ? nla_get_u16 (tb[IFLA_GRE_IFLAGS]) : 0; + props->output_flags = tb[IFLA_GRE_OFLAGS] ? nla_get_u16 (tb[IFLA_GRE_OFLAGS]) : 0; + props->input_key = (props->input_flags & GRE_KEY) && tb[IFLA_GRE_IKEY] ? nla_get_u32 (tb[IFLA_GRE_IKEY]) : 0; + props->output_key = (props->output_flags & GRE_KEY) && tb[IFLA_GRE_OKEY] ? nla_get_u32 (tb[IFLA_GRE_OKEY]) : 0; + props->local = tb[IFLA_GRE_LOCAL] ? nla_get_u32 (tb[IFLA_GRE_LOCAL]) : 0; + props->remote = tb[IFLA_GRE_REMOTE] ? nla_get_u32 (tb[IFLA_GRE_REMOTE]) : 0; + props->tos = tb[IFLA_GRE_TOS] ? nla_get_u8 (tb[IFLA_GRE_TOS]) : 0; + props->ttl = tb[IFLA_GRE_TTL] ? nla_get_u8 (tb[IFLA_GRE_TTL]) : 0; + props->path_mtu_discovery = !tb[IFLA_GRE_PMTUDISC] || !!nla_get_u8 (tb[IFLA_GRE_PMTUDISC]); + + return obj; } -static guint32 -_rtnl_addr_last_update_time_to_nm (const struct rtnl_addr *rtnladdr, gint32 *out_now_nm) +/*****************************************************************************/ + +/* IFLA_IPOIB_* were introduced in the 3.7 kernel, but the kernel headers + * we're building against might not have those properties even though the + * running kernel might. + */ +#define IFLA_IPOIB_UNSPEC 0 +#define IFLA_IPOIB_PKEY 1 +#define IFLA_IPOIB_MODE 2 +#define IFLA_IPOIB_UMCAST 3 +#undef IFLA_IPOIB_MAX +#define IFLA_IPOIB_MAX IFLA_IPOIB_UMCAST + +#define IPOIB_MODE_DATAGRAM 0 /* using unreliable datagram QPs */ +#define IPOIB_MODE_CONNECTED 1 /* using connected QPs */ + +static NMPObject * +_parse_lnk_infiniband (const char *kind, struct nlattr *info_data) { - guint32 last_update_time = rtnl_addr_get_last_update_time ((struct rtnl_addr *) rtnladdr); - struct timespec tp; - gint64 now_nl, now_nm, result; + static struct nla_policy policy[IFLA_IPOIB_MAX + 1] = { + [IFLA_IPOIB_PKEY] = { .type = NLA_U16 }, + [IFLA_IPOIB_MODE] = { .type = NLA_U16 }, + [IFLA_IPOIB_UMCAST] = { .type = NLA_U16 }, + }; + struct nlattr *tb[IFLA_IPOIB_MAX + 1]; + NMPlatformLnkInfiniband *info; + NMPObject *obj; int err; + const char *mode; - /* timestamp is unset. Default to 1. */ - if (!last_update_time) { - if (out_now_nm) - *out_now_nm = 0; - return 1; - } - - /* do all the calculations in milliseconds scale */ + if (!info_data || g_strcmp0 (kind, "ipoib")) + return NULL; - err = clock_gettime (CLOCK_MONOTONIC, &tp); - g_assert (err == 0); - now_nm = nm_utils_get_monotonic_timestamp_ms (); - now_nl = (((gint64) tp.tv_sec) * ((gint64) 1000)) + - (tp.tv_nsec / (NM_UTILS_NS_PER_SECOND/1000)); + err = nla_parse_nested (tb, IFLA_IPOIB_MAX, info_data, policy); + if (err < 0) + return NULL; - result = now_nm - (now_nl - _timestamp_nl_to_ms (last_update_time, now_nl)); + if (!tb[IFLA_IPOIB_PKEY] || !tb[IFLA_IPOIB_MODE]) + return NULL; - if (out_now_nm) - *out_now_nm = now_nm / 1000; + switch (nla_get_u16 (tb[IFLA_IPOIB_MODE])) { + case IPOIB_MODE_DATAGRAM: + mode = "datagram"; + break; + case IPOIB_MODE_CONNECTED: + mode = "connected"; + break; + default: + return NULL; + } - /* converting the last_update_time into nm_utils_get_monotonic_timestamp_ms() scale is - * a good guess but fails in the following situations: - * - * - If the address existed before start of the process, the timestamp in nm scale would - * be negative or zero. In this case we default to 1. - * - during hibernation, the CLOCK_MONOTONIC/last_update_time drifts from - * nm_utils_get_monotonic_timestamp_ms() scale. - */ - if (result <= 1000) - return 1; + obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_INFINIBAND, NULL); + info = &obj->lnk_infiniband; - if (result > now_nm) - return now_nm / 1000; + info->p_key = nla_get_u16 (tb[IFLA_IPOIB_PKEY]); + info->mode = mode; - return result / 1000; + return obj; } -static guint32 -_extend_lifetime (guint32 lifetime, guint32 seconds) +/*****************************************************************************/ + +static NMPObject * +_parse_lnk_macvlan (const char *kind, struct nlattr *info_data) { - guint64 v; + static struct nla_policy policy[IFLA_MACVLAN_MAX + 1] = { + [IFLA_MACVLAN_MODE] = { .type = NLA_U32 }, + [IFLA_MACVLAN_FLAGS] = { .type = NLA_U16 }, + }; + NMPlatformLnkMacvlan *props; + struct nlattr *tb[IFLA_MACVLAN_MAX + 1]; + int err; + NMPObject *obj; + const char *mode; - if ( lifetime == NM_PLATFORM_LIFETIME_PERMANENT - || seconds == 0) - return lifetime; + if (!info_data || g_strcmp0 (kind, "macvlan")) + return NULL; - v = (guint64) lifetime + (guint64) seconds; - return MIN (v, NM_PLATFORM_LIFETIME_PERMANENT - 1); + err = nla_parse_nested (tb, IFLA_MACVLAN_MAX, info_data, policy); + if (err < 0) + return NULL; + + if (!tb[IFLA_MACVLAN_MODE]) + return NULL; + + switch (nla_get_u32 (tb[IFLA_MACVLAN_MODE])) { + case MACVLAN_MODE_PRIVATE: + mode = "private"; + break; + case MACVLAN_MODE_VEPA: + mode = "vepa"; + break; + case MACVLAN_MODE_BRIDGE: + mode = "bridge"; + break; + case MACVLAN_MODE_PASSTHRU: + mode = "passthru"; + break; + default: + return NULL; + } + + obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_MACVLAN, NULL); + props = &obj->lnk_macvlan; + props->mode = mode; + + if (tb[IFLA_MACVLAN_FLAGS]) + props->no_promisc = NM_FLAGS_HAS (nla_get_u16 (tb[IFLA_MACVLAN_FLAGS]), MACVLAN_FLAG_NOPROMISC); + + return obj; } -/* The rtnl_addr object contains relative lifetimes @valid and @preferred - * that count in seconds, starting from the moment when the kernel constructed - * the netlink message. - * - * There is also a field rtnl_addr_last_update_time(), which is the absolute - * time in 1/100th of a second of clock_gettime (CLOCK_MONOTONIC) when the address - * was modified (wrapping every 497 days). - * Immediately at the time when the address was last modified, #NOW and @last_update_time - * are the same, so (only) in that case @valid and @preferred are anchored at @last_update_time. - * However, this is not true in general. As time goes by, whenever kernel sends a new address - * via netlink, the lifetimes keep counting down. - **/ -static void -_nlo_rtnl_addr_get_lifetimes (const struct rtnl_addr *rtnladdr, - guint32 *out_timestamp, - guint32 *out_lifetime, - guint32 *out_preferred) +/*****************************************************************************/ + +static gboolean +_vlan_qos_mapping_from_nla (struct nlattr *nlattr, + const NMVlanQosMapping **out_map, + guint *out_n_map) { - guint32 timestamp = 0; - gint32 now; - guint32 lifetime = rtnl_addr_get_valid_lifetime ((struct rtnl_addr *) rtnladdr); - guint32 preferred = rtnl_addr_get_preferred_lifetime ((struct rtnl_addr *) rtnladdr); + struct nlattr *nla; + int remaining; + gs_unref_ptrarray GPtrArray *array = NULL; - if ( lifetime != NM_PLATFORM_LIFETIME_PERMANENT - || preferred != NM_PLATFORM_LIFETIME_PERMANENT) { - if (preferred > lifetime) - preferred = lifetime; - timestamp = _rtnl_addr_last_update_time_to_nm (rtnladdr, &now); + G_STATIC_ASSERT (sizeof (NMVlanQosMapping) == sizeof (struct ifla_vlan_qos_mapping)); + G_STATIC_ASSERT (sizeof (((NMVlanQosMapping *) 0)->to) == sizeof (((struct ifla_vlan_qos_mapping *) 0)->to)); + G_STATIC_ASSERT (sizeof (((NMVlanQosMapping *) 0)->from) == sizeof (((struct ifla_vlan_qos_mapping *) 0)->from)); + G_STATIC_ASSERT (sizeof (NMVlanQosMapping) == sizeof (((NMVlanQosMapping *) 0)->from) + sizeof (((NMVlanQosMapping *) 0)->to)); - if (now == 0) { - /* strange. failed to detect the last-update time and assumed that timestamp is 1. */ - nm_assert (timestamp == 1); - now = nm_utils_get_monotonic_timestamp_s (); + nm_assert (out_map && !*out_map); + nm_assert (out_n_map && !*out_n_map); + + if (!nlattr) + return TRUE; + + array = g_ptr_array_new (); + nla_for_each_nested (nla, nlattr, remaining) { + if (nla_len (nla) < sizeof(NMVlanQosMapping)) + return FALSE; + g_ptr_array_add (array, nla_data (nla)); + } + + if (array->len > 0) { + NMVlanQosMapping *list; + guint i, j; + + /* The sorting is necessary, because for egress mapping, kernel + * doesn't sent the items strictly sorted by the from field. */ + g_ptr_array_sort_with_data (array, _vlan_qos_mapping_cmp_from_ptr, NULL); + + list = g_new (NMVlanQosMapping, array->len); + + for (i = 0, j = 0; i < array->len; i++) { + NMVlanQosMapping *map; + + map = array->pdata[i]; + + /* kernel doesn't really send us duplicates. Just be extra cautious + * because we want strong guarantees about the sort order and uniqueness + * of our mapping list (for simpler equality comparison). */ + if ( j > 0 + && list[j - 1].from == map->from) + list[j - 1] = *map; + else + list[j++] = *map; } - if (timestamp < now) { - guint32 diff = now - timestamp; - lifetime = _extend_lifetime (lifetime, diff); - preferred = _extend_lifetime (preferred, diff); - } else - nm_assert (timestamp == now); + *out_n_map = j; + *out_map = list; } - *out_timestamp = timestamp; - *out_lifetime = lifetime; - *out_preferred = preferred; + + return TRUE; } -gboolean -_nmp_vt_cmd_plobj_init_from_nl_ip4_address (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache) +/* Copied and heavily modified from libnl3's vlan_parse() */ +static NMPObject * +_parse_lnk_vlan (const char *kind, struct nlattr *info_data) { - NMPlatformIP4Address *obj = (NMPlatformIP4Address *) _obj; - struct rtnl_addr *nlo = (struct rtnl_addr *) _nlo; - struct nl_addr *nladdr = rtnl_addr_get_local (nlo); - struct nl_addr *nlpeer = rtnl_addr_get_peer (nlo); - const char *label; + static struct nla_policy policy[IFLA_VLAN_MAX+1] = { + [IFLA_VLAN_ID] = { .type = NLA_U16 }, + [IFLA_VLAN_FLAGS] = { .minlen = sizeof(struct ifla_vlan_flags) }, + [IFLA_VLAN_INGRESS_QOS] = { .type = NLA_NESTED }, + [IFLA_VLAN_EGRESS_QOS] = { .type = NLA_NESTED }, + [IFLA_VLAN_PROTOCOL] = { .type = NLA_U16 }, + }; + struct nlattr *tb[IFLA_VLAN_MAX+1]; + int err; + nm_auto_nmpobj NMPObject *obj = NULL; + NMPObject *obj_result; - if (!nladdr || nl_addr_get_len (nladdr) != sizeof (obj->address)) - g_return_val_if_reached (FALSE); + if (!info_data || g_strcmp0 (kind, "vlan")) + return NULL; + + if ((err = nla_parse_nested (tb, IFLA_VLAN_MAX, info_data, policy)) < 0) + return NULL; + + if (!tb[IFLA_VLAN_ID]) + return NULL; + + obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_VLAN, NULL); + obj->lnk_vlan.id = nla_get_u16 (tb[IFLA_VLAN_ID]); + + if (tb[IFLA_VLAN_FLAGS]) { + struct ifla_vlan_flags flags; + + nla_memcpy (&flags, tb[IFLA_VLAN_FLAGS], sizeof(flags)); + + obj->lnk_vlan.flags = flags.flags; + } + + if (!_vlan_qos_mapping_from_nla (tb[IFLA_VLAN_INGRESS_QOS], + &obj->_lnk_vlan.ingress_qos_map, + &obj->_lnk_vlan.n_ingress_qos_map)) + return NULL; + + if (!_vlan_qos_mapping_from_nla (tb[IFLA_VLAN_EGRESS_QOS], + &obj->_lnk_vlan.egress_qos_map, + &obj->_lnk_vlan.n_egress_qos_map)) + return NULL; + + + obj_result = obj; + obj = NULL; + return obj_result; +} + +/*****************************************************************************/ + +/* The installed kernel headers might not have VXLAN stuff at all, or + * they might have the original properties, but not PORT, GROUP6, or LOCAL6. + * So until we depend on kernel >= 3.11, we just ignore the actual enum + * in if_link.h and define the values ourselves. + */ +#define IFLA_VXLAN_UNSPEC 0 +#define IFLA_VXLAN_ID 1 +#define IFLA_VXLAN_GROUP 2 +#define IFLA_VXLAN_LINK 3 +#define IFLA_VXLAN_LOCAL 4 +#define IFLA_VXLAN_TTL 5 +#define IFLA_VXLAN_TOS 6 +#define IFLA_VXLAN_LEARNING 7 +#define IFLA_VXLAN_AGEING 8 +#define IFLA_VXLAN_LIMIT 9 +#define IFLA_VXLAN_PORT_RANGE 10 +#define IFLA_VXLAN_PROXY 11 +#define IFLA_VXLAN_RSC 12 +#define IFLA_VXLAN_L2MISS 13 +#define IFLA_VXLAN_L3MISS 14 +#define IFLA_VXLAN_PORT 15 +#define IFLA_VXLAN_GROUP6 16 +#define IFLA_VXLAN_LOCAL6 17 +#undef IFLA_VXLAN_MAX +#define IFLA_VXLAN_MAX IFLA_VXLAN_LOCAL6 - obj->ifindex = rtnl_addr_get_ifindex (nlo); - obj->plen = rtnl_addr_get_prefixlen (nlo); - memcpy (&obj->address, nl_addr_get_binary_addr (nladdr), sizeof (obj->address)); +/* older kernel header might not contain 'struct ifla_vxlan_port_range'. + * Redefine it. */ +struct nm_ifla_vxlan_port_range { + guint16 low; + guint16 high; +}; + +static NMPObject * +_parse_lnk_vxlan (const char *kind, struct nlattr *info_data) +{ + static struct nla_policy policy[IFLA_VXLAN_MAX + 1] = { + [IFLA_VXLAN_ID] = { .type = NLA_U32 }, + [IFLA_VXLAN_GROUP] = { .type = NLA_U32 }, + [IFLA_VXLAN_GROUP6] = { .type = NLA_UNSPEC, + .minlen = sizeof (struct in6_addr) }, + [IFLA_VXLAN_LINK] = { .type = NLA_U32 }, + [IFLA_VXLAN_LOCAL] = { .type = NLA_U32 }, + [IFLA_VXLAN_LOCAL6] = { .type = NLA_UNSPEC, + .minlen = sizeof (struct in6_addr) }, + [IFLA_VXLAN_TOS] = { .type = NLA_U8 }, + [IFLA_VXLAN_TTL] = { .type = NLA_U8 }, + [IFLA_VXLAN_LEARNING] = { .type = NLA_U8 }, + [IFLA_VXLAN_AGEING] = { .type = NLA_U32 }, + [IFLA_VXLAN_LIMIT] = { .type = NLA_U32 }, + [IFLA_VXLAN_PORT_RANGE] = { .type = NLA_UNSPEC, + .minlen = sizeof (struct nm_ifla_vxlan_port_range) }, + [IFLA_VXLAN_PROXY] = { .type = NLA_U8 }, + [IFLA_VXLAN_RSC] = { .type = NLA_U8 }, + [IFLA_VXLAN_L2MISS] = { .type = NLA_U8 }, + [IFLA_VXLAN_L3MISS] = { .type = NLA_U8 }, + [IFLA_VXLAN_PORT] = { .type = NLA_U16 }, + }; + NMPlatformLnkVxlan *props; + struct nlattr *tb[IFLA_VXLAN_MAX + 1]; + struct nm_ifla_vxlan_port_range *range; + int err; + NMPObject *obj; + + if (!info_data || g_strcmp0 (kind, "vxlan")) + return NULL; + + err = nla_parse_nested (tb, IFLA_VXLAN_MAX, info_data, policy); + if (err < 0) + return NULL; + + obj = nmp_object_new (NMP_OBJECT_TYPE_LNK_VXLAN, NULL); + + props = &obj->lnk_vxlan; + + if (tb[IFLA_VXLAN_LINK]) + props->parent_ifindex = nla_get_u32 (tb[IFLA_VXLAN_LINK]); + if (tb[IFLA_VXLAN_ID]) + props->id = nla_get_u32 (tb[IFLA_VXLAN_ID]); + if (tb[IFLA_VXLAN_GROUP]) + props->group = nla_get_u32 (tb[IFLA_VXLAN_GROUP]); + if (tb[IFLA_VXLAN_LOCAL]) + props->local = nla_get_u32 (tb[IFLA_VXLAN_LOCAL]); + if (tb[IFLA_VXLAN_GROUP6]) + memcpy (&props->group6, nla_data (tb[IFLA_VXLAN_GROUP6]), sizeof (props->group6)); + if (tb[IFLA_VXLAN_LOCAL6]) + memcpy (&props->local6, nla_data (tb[IFLA_VXLAN_LOCAL6]), sizeof (props->local6)); + + if (tb[IFLA_VXLAN_AGEING]) + props->ageing = nla_get_u32 (tb[IFLA_VXLAN_AGEING]); + if (tb[IFLA_VXLAN_LIMIT]) + props->limit = nla_get_u32 (tb[IFLA_VXLAN_LIMIT]); + if (tb[IFLA_VXLAN_TOS]) + props->tos = nla_get_u8 (tb[IFLA_VXLAN_TOS]); + if (tb[IFLA_VXLAN_TTL]) + props->ttl = nla_get_u8 (tb[IFLA_VXLAN_TTL]); + + if (tb[IFLA_VXLAN_PORT]) + props->dst_port = ntohs (nla_get_u16 (tb[IFLA_VXLAN_PORT])); + + if (tb[IFLA_VXLAN_PORT_RANGE]) { + range = nla_data (tb[IFLA_VXLAN_PORT_RANGE]); + props->src_port_min = ntohs (range->low); + props->src_port_max = ntohs (range->high); + } + + if (tb[IFLA_VXLAN_LEARNING]) + props->learning = !!nla_get_u8 (tb[IFLA_VXLAN_LEARNING]); + if (tb[IFLA_VXLAN_PROXY]) + props->proxy = !!nla_get_u8 (tb[IFLA_VXLAN_PROXY]); + if (tb[IFLA_VXLAN_RSC]) + props->rsc = !!nla_get_u8 (tb[IFLA_VXLAN_RSC]); + if (tb[IFLA_VXLAN_L2MISS]) + props->l2miss = !!nla_get_u8 (tb[IFLA_VXLAN_L2MISS]); + if (tb[IFLA_VXLAN_L3MISS]) + props->l3miss = !!nla_get_u8 (tb[IFLA_VXLAN_L3MISS]); + + return obj; +} + +/*****************************************************************************/ + +/* Copied and heavily modified from libnl3's link_msg_parser(). */ +static NMPObject * +_new_from_nl_link (NMPlatform *platform, const NMPCache *cache, struct nlmsghdr *nlh, gboolean id_only) +{ + static struct nla_policy policy[IFLA_MAX+1] = { + [IFLA_IFNAME] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ }, + [IFLA_MTU] = { .type = NLA_U32 }, + [IFLA_TXQLEN] = { .type = NLA_U32 }, + [IFLA_LINK] = { .type = NLA_U32 }, + [IFLA_WEIGHT] = { .type = NLA_U32 }, + [IFLA_MASTER] = { .type = NLA_U32 }, + [IFLA_OPERSTATE] = { .type = NLA_U8 }, + [IFLA_LINKMODE] = { .type = NLA_U8 }, + [IFLA_LINKINFO] = { .type = NLA_NESTED }, + [IFLA_QDISC] = { .type = NLA_STRING, + .maxlen = IFQDISCSIZ }, + [IFLA_STATS] = { .minlen = sizeof(struct rtnl_link_stats) }, + [IFLA_STATS64] = { .minlen = sizeof(struct rtnl_link_stats64)}, + [IFLA_MAP] = { .minlen = sizeof(struct rtnl_link_ifmap) }, + [IFLA_IFALIAS] = { .type = NLA_STRING, .maxlen = IFALIASZ }, + [IFLA_NUM_VF] = { .type = NLA_U32 }, + [IFLA_AF_SPEC] = { .type = NLA_NESTED }, + [IFLA_PROMISCUITY] = { .type = NLA_U32 }, + [IFLA_NUM_TX_QUEUES] = { .type = NLA_U32 }, + [IFLA_NUM_RX_QUEUES] = { .type = NLA_U32 }, + [IFLA_GROUP] = { .type = NLA_U32 }, + [IFLA_CARRIER] = { .type = NLA_U8 }, + [IFLA_PHYS_PORT_ID] = { .type = NLA_UNSPEC }, + [IFLA_NET_NS_PID] = { .type = NLA_U32 }, + [IFLA_NET_NS_FD] = { .type = NLA_U32 }, + }; + static struct nla_policy policy_link_info[IFLA_INFO_MAX+1] = { + [IFLA_INFO_KIND] = { .type = NLA_STRING }, + [IFLA_INFO_DATA] = { .type = NLA_NESTED }, + [IFLA_INFO_XSTATS] = { .type = NLA_NESTED }, + }; + const struct ifinfomsg *ifi; + struct nlattr *tb[IFLA_MAX+1]; + struct nlattr *li[IFLA_INFO_MAX+1]; + struct nlattr *nl_info_data = NULL; + const char *nl_info_kind = NULL; + int err; + nm_auto_nmpobj NMPObject *obj = NULL; + NMPObject *obj_result = NULL; + gboolean completed_from_cache_val = FALSE; + gboolean *completed_from_cache = cache ? &completed_from_cache_val : NULL; + const NMPObject *link_cached = NULL; + nm_auto_nmpobj NMPObject *lnk_data = NULL; + + if (!nlmsg_valid_hdr (nlh, sizeof (*ifi))) + return NULL; + ifi = nlmsg_data(nlh); + + obj = nmp_object_new_link (ifi->ifi_index); if (id_only) - return TRUE; + goto done; - obj->source = NM_IP_CONFIG_SOURCE_KERNEL; - _nlo_rtnl_addr_get_lifetimes (nlo, - &obj->timestamp, - &obj->lifetime, - &obj->preferred); - if (nlpeer) { - if (nl_addr_get_len (nlpeer) != sizeof (obj->peer_address)) - g_warn_if_reached (); + err = nlmsg_parse (nlh, sizeof (*ifi), tb, IFLA_MAX, policy); + if (err < 0) + goto errout; + + if (!tb[IFLA_IFNAME]) + goto errout; + nla_strlcpy(obj->link.name, tb[IFLA_IFNAME], IFNAMSIZ); + if (!obj->link.name[0]) + goto errout; + + if (tb[IFLA_LINKINFO]) { + err = nla_parse_nested (li, IFLA_INFO_MAX, tb[IFLA_LINKINFO], policy_link_info); + if (err < 0) + goto errout; + + if (li[IFLA_INFO_KIND]) + nl_info_kind = nla_get_string (li[IFLA_INFO_KIND]); + + nl_info_data = li[IFLA_INFO_DATA]; + } + + obj->link.flags = ifi->ifi_flags; + obj->link.connected = NM_FLAGS_HAS (obj->link.flags, IFF_LOWER_UP); + obj->link.arptype = ifi->ifi_type; + + obj->link.type = _linktype_get_type (platform, + cache, + nl_info_kind, + obj->link.ifindex, + obj->link.name, + obj->link.flags, + obj->link.arptype, + completed_from_cache, + &link_cached, + &obj->link.kind); + + if (tb[IFLA_MASTER]) + obj->link.master = nla_get_u32 (tb[IFLA_MASTER]); + + if (tb[IFLA_LINK]) { + if (!tb[IFLA_LINK_NETNSID]) + obj->link.parent = nla_get_u32 (tb[IFLA_LINK]); else - memcpy (&obj->peer_address, nl_addr_get_binary_addr (nlpeer), sizeof (obj->peer_address)); + obj->link.parent = NM_PLATFORM_LINK_OTHER_NETNS; } - label = rtnl_addr_get_label (nlo); - /* Check for ':'; we're only interested in labels used as interface aliases */ - if (label && strchr (label, ':')) - g_strlcpy (obj->label, label, sizeof (obj->label)); - return TRUE; + if (tb[IFLA_ADDRESS]) { + int l = nla_len (tb[IFLA_ADDRESS]); + + if (l > 0 && l <= NM_UTILS_HWADDR_LEN_MAX) { + G_STATIC_ASSERT (NM_UTILS_HWADDR_LEN_MAX == sizeof (obj->link.addr.data)); + memcpy (obj->link.addr.data, nla_data (tb[IFLA_ADDRESS]), l); + obj->link.addr.len = l; + } + } + + if (tb[IFLA_AF_SPEC]) { + struct nlattr *af_attr; + int remaining; + + nla_for_each_nested (af_attr, tb[IFLA_AF_SPEC], remaining) { + switch (nla_type (af_attr)) { + case AF_INET6: + _parse_af_inet6 (platform, + af_attr, + &obj->link.inet6_token.iid, + &obj->link.inet6_token.is_valid, + &obj->link.inet6_addr_gen_mode_inv); + break; + } + } + } + + if (tb[IFLA_MTU]) + obj->link.mtu = nla_get_u32 (tb[IFLA_MTU]); + + switch (obj->link.type) { + case NM_LINK_TYPE_GRE: + lnk_data = _parse_lnk_gre (nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_INFINIBAND: + lnk_data = _parse_lnk_infiniband (nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_MACVLAN: + lnk_data = _parse_lnk_macvlan (nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_VLAN: + lnk_data = _parse_lnk_vlan (nl_info_kind, nl_info_data); + break; + case NM_LINK_TYPE_VXLAN: + lnk_data = _parse_lnk_vxlan (nl_info_kind, nl_info_data); + break; + default: + goto no_lnk_data; + } + + /* We always try to look into the cache and reuse the object there. + * We do that, because we consider the lnk object as immutable and don't + * modify it after creating. Hence we can share it and reuse. */ + if (completed_from_cache) { + _lookup_cached_link (cache, obj->link.ifindex, completed_from_cache, &link_cached); + if ( link_cached + && link_cached->link.type == obj->link.type + && ( !lnk_data + || nmp_object_equal (lnk_data, link_cached->_link.netlink.lnk))) { + nmp_object_unref (lnk_data); + lnk_data = nmp_object_ref (link_cached->_link.netlink.lnk); + } + } + +no_lnk_data: + + obj->_link.netlink.is_in_netlink = TRUE; + + obj->_link.netlink.lnk = lnk_data; + lnk_data = NULL; + +done: + obj_result = obj; + obj = NULL; +errout: + return obj_result; } -gboolean -_nmp_vt_cmd_plobj_init_from_nl_ip6_address (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache) +/* Copied and heavily modified from libnl3's addr_msg_parser(). */ +static NMPObject * +_new_from_nl_addr (struct nlmsghdr *nlh, gboolean id_only) { - NMPlatformIP6Address *obj = (NMPlatformIP6Address *) _obj; - struct rtnl_addr *nlo = (struct rtnl_addr *) _nlo; - struct nl_addr *nladdr = rtnl_addr_get_local (nlo); - struct nl_addr *nlpeer = rtnl_addr_get_peer (nlo); + static struct nla_policy policy[IFA_MAX+1] = { + [IFA_LABEL] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ }, + [IFA_CACHEINFO] = { .minlen = sizeof(struct ifa_cacheinfo) }, + }; + const struct ifaddrmsg *ifa; + struct nlattr *tb[IFA_MAX+1]; + int err; + gboolean is_v4; + nm_auto_nmpobj NMPObject *obj = NULL; + NMPObject *obj_result = NULL; + int addr_len; + guint32 lifetime, preferred, timestamp; - if (!nladdr || nl_addr_get_len (nladdr) != sizeof (obj->address)) - g_return_val_if_reached (FALSE); + if (!nlmsg_valid_hdr (nlh, sizeof (*ifa))) + return NULL; + ifa = nlmsg_data(nlh); - obj->ifindex = rtnl_addr_get_ifindex (nlo); - obj->plen = rtnl_addr_get_prefixlen (nlo); - memcpy (&obj->address, nl_addr_get_binary_addr (nladdr), sizeof (obj->address)); + if (!NM_IN_SET (ifa->ifa_family, AF_INET, AF_INET6)) + goto errout; + is_v4 = ifa->ifa_family == AF_INET; - if (id_only) - return TRUE; + err = nlmsg_parse(nlh, sizeof(*ifa), tb, IFA_MAX, policy); + if (err < 0) + goto errout; - obj->source = NM_IP_CONFIG_SOURCE_KERNEL; - _nlo_rtnl_addr_get_lifetimes (nlo, - &obj->timestamp, - &obj->lifetime, - &obj->preferred); - obj->flags = rtnl_addr_get_flags (nlo); + addr_len = is_v4 + ? sizeof (in_addr_t) + : sizeof (struct in6_addr); - if (nlpeer) { - if (nl_addr_get_len (nlpeer) != sizeof (obj->peer_address)) - g_warn_if_reached (); - else - memcpy (&obj->peer_address, nl_addr_get_binary_addr (nlpeer), sizeof (obj->peer_address)); + /*****************************************************************/ + + obj = nmp_object_new (is_v4 ? NMP_OBJECT_TYPE_IP4_ADDRESS : NMP_OBJECT_TYPE_IP6_ADDRESS, NULL); + + obj->ip_address.ifindex = ifa->ifa_index; + obj->ip_address.plen = ifa->ifa_prefixlen; + + _check_addr_or_errout (tb, IFA_ADDRESS, addr_len); + _check_addr_or_errout (tb, IFA_LOCAL, addr_len); + if (is_v4) { + /* For IPv4, kernel omits IFA_LOCAL/IFA_ADDRESS if (and only if) they + * are effectively 0.0.0.0 (all-zero). */ + if (tb[IFA_LOCAL]) + memcpy (&obj->ip4_address.address, nla_data (tb[IFA_LOCAL]), addr_len); + if (tb[IFA_ADDRESS]) + memcpy (&obj->ip4_address.peer_address, nla_data (tb[IFA_ADDRESS]), addr_len); + } else { + /* For IPv6, IFA_ADDRESS is always present. + * + * If IFA_LOCAL is missing, IFA_ADDRESS is @address and @peer_address + * is :: (all-zero). + * + * If unexpectely IFA_ADDRESS is missing, make the best of it -- but it _should_ + * actually be there. */ + if (tb[IFA_ADDRESS] || tb[IFA_LOCAL]) { + if (tb[IFA_LOCAL]) { + memcpy (&obj->ip6_address.address, nla_data (tb[IFA_LOCAL]), addr_len); + if (tb[IFA_ADDRESS]) + memcpy (&obj->ip6_address.peer_address, nla_data (tb[IFA_ADDRESS]), addr_len); + else + obj->ip6_address.peer_address = obj->ip6_address.address; + } else + memcpy (&obj->ip6_address.address, nla_data (tb[IFA_ADDRESS]), addr_len); + } } - return TRUE; + obj->ip_address.source = NM_IP_CONFIG_SOURCE_KERNEL; + + if (!is_v4) { + obj->ip6_address.flags = tb[IFA_FLAGS] + ? nla_get_u32 (tb[IFA_FLAGS]) + : ifa->ifa_flags; + } + + if (is_v4) { + if (tb[IFA_LABEL]) { + char label[IFNAMSIZ]; + + nla_strlcpy (label, tb[IFA_LABEL], IFNAMSIZ); + + /* Check for ':'; we're only interested in labels used as interface aliases */ + if (strchr (label, ':')) + g_strlcpy (obj->ip4_address.label, label, sizeof (obj->ip4_address.label)); + } + } + + lifetime = NM_PLATFORM_LIFETIME_PERMANENT; + preferred = NM_PLATFORM_LIFETIME_PERMANENT; + timestamp = 0; + /* IPv6 only */ + if (tb[IFA_CACHEINFO]) { + const struct ifa_cacheinfo *ca = nla_data(tb[IFA_CACHEINFO]); + + lifetime = ca->ifa_valid; + preferred = ca->ifa_prefered; + timestamp = ca->tstamp; + } + _addrtime_get_lifetimes (timestamp, + lifetime, + preferred, + &obj->ip_address.timestamp, + &obj->ip_address.lifetime, + &obj->ip_address.preferred); + + obj_result = obj; + obj = NULL; +errout: + return obj_result; } -gboolean -_nmp_vt_cmd_plobj_init_from_nl_ip4_route (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache) +/* Copied and heavily modified from libnl3's rtnl_route_parse() and parse_multipath(). */ +static NMPObject * +_new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) { - NMPlatformIP4Route *obj = (NMPlatformIP4Route *) _obj; - struct rtnl_route *nlo = (struct rtnl_route *) _nlo; - struct nl_addr *dst, *gw; - struct rtnl_nexthop *nexthop; - struct nl_addr *pref_src; + static struct nla_policy policy[RTA_MAX+1] = { + [RTA_IIF] = { .type = NLA_U32 }, + [RTA_OIF] = { .type = NLA_U32 }, + [RTA_PRIORITY] = { .type = NLA_U32 }, + [RTA_FLOW] = { .type = NLA_U32 }, + [RTA_CACHEINFO] = { .minlen = sizeof(struct rta_cacheinfo) }, + [RTA_METRICS] = { .type = NLA_NESTED }, + [RTA_MULTIPATH] = { .type = NLA_NESTED }, + }; + const struct rtmsg *rtm; + struct nlattr *tb[RTA_MAX + 1]; + int err; + gboolean is_v4; + nm_auto_nmpobj NMPObject *obj = NULL; + NMPObject *obj_result = NULL; + int addr_len; + struct { + gboolean is_present; + int ifindex; + NMIPAddr gateway; + } nh; + guint32 mss; + guint32 table; + + if (!nlmsg_valid_hdr (nlh, sizeof (*rtm))) + return NULL; + rtm = nlmsg_data(nlh); - if (rtnl_route_get_type (nlo) != RTN_UNICAST || - rtnl_route_get_table (nlo) != RT_TABLE_MAIN || - rtnl_route_get_tos (nlo) != 0 || - rtnl_route_get_nnexthops (nlo) != 1) - return FALSE; + /***************************************************************** + * only handle ~normal~ routes. + *****************************************************************/ - nexthop = rtnl_route_nexthop_n (nlo, 0); - if (!nexthop) - g_return_val_if_reached (FALSE); + if (!NM_IN_SET (rtm->rtm_family, AF_INET, AF_INET6)) + goto errout; - dst = rtnl_route_get_dst (nlo); - if (!dst) - g_return_val_if_reached (FALSE); + if ( rtm->rtm_type != RTN_UNICAST + || rtm->rtm_tos != 0) + goto errout; + + err = nlmsg_parse (nlh, sizeof (struct rtmsg), tb, RTA_MAX, policy); + if (err < 0) + goto errout; + + table = tb[RTA_TABLE] + ? nla_get_u32 (tb[RTA_TABLE]) + : (guint32) rtm->rtm_table; + if (table != RT_TABLE_MAIN) + goto errout; + + /*****************************************************************/ + + is_v4 = rtm->rtm_family == AF_INET; + addr_len = is_v4 + ? sizeof (in_addr_t) + : sizeof (struct in6_addr); + + /***************************************************************** + * parse nexthops. Only handle routes with one nh. + *****************************************************************/ + + memset (&nh, 0, sizeof (nh)); + + if (tb[RTA_MULTIPATH]) { + struct rtnexthop *rtnh = nla_data (tb[RTA_MULTIPATH]); + size_t tlen = nla_len(tb[RTA_MULTIPATH]); + + while (tlen >= sizeof(*rtnh) && tlen >= rtnh->rtnh_len) { + + if (nh.is_present) { + /* we don't support multipath routes. */ + goto errout; + } + nh.is_present = TRUE; + + nh.ifindex = rtnh->rtnh_ifindex; + + if (rtnh->rtnh_len > sizeof(*rtnh)) { + struct nlattr *ntb[RTA_MAX + 1]; + + err = nla_parse (ntb, RTA_MAX, (struct nlattr *) + RTNH_DATA(rtnh), + rtnh->rtnh_len - sizeof (*rtnh), + policy); + if (err < 0) + goto errout; - if (nl_addr_get_len (dst)) { - if (nl_addr_get_len (dst) != sizeof (obj->network)) - g_return_val_if_reached (FALSE); - memcpy (&obj->network, nl_addr_get_binary_addr (dst), sizeof (obj->network)); + if (_check_addr_or_errout (ntb, RTA_GATEWAY, addr_len)) + memcpy (&nh.gateway, nla_data (ntb[RTA_GATEWAY]), addr_len); + } + + tlen -= RTNH_ALIGN(rtnh->rtnh_len); + rtnh = RTNH_NEXT(rtnh); + } } - obj->ifindex = rtnl_route_nh_get_ifindex (nexthop); - obj->plen = nl_addr_get_prefixlen (dst); - obj->metric = rtnl_route_get_priority (nlo); - obj->scope_inv = nm_platform_route_scope_inv (rtnl_route_get_scope (nlo)); - - gw = rtnl_route_nh_get_gateway (nexthop); - if (gw) { - if (nl_addr_get_len (gw) != sizeof (obj->gateway)) - g_warn_if_reached (); - else - memcpy (&obj->gateway, nl_addr_get_binary_addr (gw), sizeof (obj->gateway)); + + if ( tb[RTA_OIF] + || tb[RTA_GATEWAY] + || tb[RTA_FLOW]) { + int ifindex = 0; + NMIPAddr gateway = NMIPAddrInit; + + if (tb[RTA_OIF]) + ifindex = nla_get_u32 (tb[RTA_OIF]); + if (_check_addr_or_errout (tb, RTA_GATEWAY, addr_len)) + memcpy (&gateway, nla_data (tb[RTA_GATEWAY]), addr_len); + + if (!nh.is_present) { + /* If no nexthops have been provided via RTA_MULTIPATH + * we add it as regular nexthop to maintain backwards + * compatibility */ + nh.ifindex = ifindex; + nh.gateway = gateway; + } else { + /* Kernel supports new style nexthop configuration, + * verify that it is a duplicate and ignore old-style nexthop. */ + if ( nh.ifindex != ifindex + || memcmp (&nh.gateway, &gateway, addr_len) != 0) + goto errout; + } + } else if (!nh.is_present) + goto errout; + + /*****************************************************************/ + + mss = 0; + if (tb[RTA_METRICS]) { + struct nlattr *mtb[RTAX_MAX + 1]; + int i; + + err = nla_parse_nested(mtb, RTAX_MAX, tb[RTA_METRICS], NULL); + if (err < 0) + goto errout; + + for (i = 1; i <= RTAX_MAX; i++) { + if (mtb[i]) { + if (i == RTAX_ADVMSS) { + if (nla_len (mtb[i]) >= sizeof (uint32_t)) + mss = nla_get_u32(mtb[i]); + break; + } + } + } } - rtnl_route_get_metric (nlo, RTAX_ADVMSS, &obj->mss); - if (rtnl_route_get_flags (nlo) & RTM_F_CLONED) { + + /*****************************************************************/ + + obj = nmp_object_new (is_v4 ? NMP_OBJECT_TYPE_IP4_ROUTE : NMP_OBJECT_TYPE_IP6_ROUTE, NULL); + + obj->ip_route.ifindex = nh.ifindex; + + if (_check_addr_or_errout (tb, RTA_DST, addr_len)) + memcpy (obj->ip_route.network_ptr, nla_data (tb[RTA_DST]), addr_len); + + obj->ip_route.plen = rtm->rtm_dst_len; + + if (tb[RTA_PRIORITY]) + obj->ip_route.metric = nla_get_u32(tb[RTA_PRIORITY]); + + if (is_v4) + obj->ip4_route.gateway = nh.gateway.addr4; + else + obj->ip6_route.gateway = nh.gateway.addr6; + + if (is_v4) + obj->ip4_route.scope_inv = nm_platform_route_scope_inv (rtm->rtm_scope); + + if (is_v4) { + if (_check_addr_or_errout (tb, RTA_PREFSRC, addr_len)) + memcpy (&obj->ip4_route.network, nla_data (tb[RTA_PREFSRC]), addr_len); + } + + obj->ip_route.mss = mss; + + if (NM_FLAGS_HAS (rtm->rtm_flags, RTM_F_CLONED)) { /* we must not straight way reject cloned routes, because we might have cached * a non-cloned route. If we now receive an update of the route with the route * being cloned, we must still return the object, so that we can remove the old @@ -1356,69 +1665,609 @@ _nmp_vt_cmd_plobj_init_from_nl_ip4_route (NMPlatform *platform, NMPlatformObject * * This happens, because this route is not nmp_object_is_alive(). * */ - obj->source = _NM_IP_CONFIG_SOURCE_RTM_F_CLONED; + obj->ip_route.source = _NM_IP_CONFIG_SOURCE_RTM_F_CLONED; } else - obj->source = _nm_ip_config_source_from_rtprot (rtnl_route_get_protocol (nlo)); + obj->ip_route.source = _nm_ip_config_source_from_rtprot (rtm->rtm_protocol); - pref_src = rtnl_route_get_pref_src (nlo); - if (pref_src) { - if (nl_addr_get_len (pref_src) != sizeof (obj->pref_src)) - g_warn_if_reached (); - else - memcpy (&obj->pref_src, nl_addr_get_binary_addr (pref_src), sizeof (obj->pref_src)); + obj_result = obj; + obj = NULL; +errout: + return obj_result; +} + +/** + * nmp_object_new_from_nl: + * @platform: (allow-none): for creating certain objects, the constructor wants to check + * sysfs. For this the platform instance is needed. If missing, the object might not + * be correctly detected. + * @cache: (allow-none): for certain objects, the netlink message doesn't contain all the information. + * If a cache is given, the object is completed with information from the cache. + * @nlh: the netlink message header + * @id_only: whether only to create an empty object with only the ID fields set. + * + * Returns: %NULL or a newly created NMPObject instance. + **/ +static NMPObject * +nmp_object_new_from_nl (NMPlatform *platform, const NMPCache *cache, struct nl_msg *msg, gboolean id_only) +{ + struct nlmsghdr *msghdr; + + if (nlmsg_get_proto (msg) != NETLINK_ROUTE) + return NULL; + + msghdr = nlmsg_hdr (msg); + + switch (msghdr->nlmsg_type) { + case RTM_NEWLINK: + case RTM_DELLINK: + case RTM_GETLINK: + case RTM_SETLINK: + return _new_from_nl_link (platform, cache, msghdr, id_only); + case RTM_NEWADDR: + case RTM_DELADDR: + case RTM_GETADDR: + return _new_from_nl_addr (msghdr, id_only); + case RTM_NEWROUTE: + case RTM_DELROUTE: + case RTM_GETROUTE: + return _new_from_nl_route (msghdr, id_only); + default: + return NULL; } +} + +/******************************************************************/ + +static gboolean +_nl_msg_new_link_set_afspec (struct nl_msg *msg, + int addr_gen_mode) +{ + struct nlattr *af_spec; + struct nlattr *af_attr; + + nm_assert (msg); + + if (!(af_spec = nla_nest_start (msg, IFLA_AF_SPEC))) + goto nla_put_failure; + + if (addr_gen_mode >= 0) { + if (!(af_attr = nla_nest_start (msg, AF_INET6))) + goto nla_put_failure; + + NLA_PUT_U8 (msg, IFLA_INET6_ADDR_GEN_MODE, addr_gen_mode); + + nla_nest_end (msg, af_attr); + } + + nla_nest_end (msg, af_spec); return TRUE; +nla_put_failure: + return FALSE; } -gboolean -_nmp_vt_cmd_plobj_init_from_nl_ip6_route (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache) +static gboolean +_nl_msg_new_link_set_linkinfo (struct nl_msg *msg, + NMLinkType link_type) { - NMPlatformIP6Route *obj = (NMPlatformIP6Route *) _obj; - struct rtnl_route *nlo = (struct rtnl_route *) _nlo; - struct nl_addr *dst, *gw; - struct rtnl_nexthop *nexthop; + struct nlattr *info; + const char *kind; - if (rtnl_route_get_type (nlo) != RTN_UNICAST || - rtnl_route_get_table (nlo) != RT_TABLE_MAIN || - rtnl_route_get_tos (nlo) != 0 || - rtnl_route_get_nnexthops (nlo) != 1) - return FALSE; + nm_assert (msg); - nexthop = rtnl_route_nexthop_n (nlo, 0); - if (!nexthop) - g_return_val_if_reached (FALSE); + kind = nm_link_type_to_rtnl_type_string (link_type); + if (!kind) + goto nla_put_failure; - dst = rtnl_route_get_dst (nlo); - if (!dst) - g_return_val_if_reached (FALSE); + if (!(info = nla_nest_start (msg, IFLA_LINKINFO))) + goto nla_put_failure; - if (nl_addr_get_len (dst)) { - if (nl_addr_get_len (dst) != sizeof (obj->network)) - g_return_val_if_reached (FALSE); - memcpy (&obj->network, nl_addr_get_binary_addr (dst), sizeof (obj->network)); - } - obj->ifindex = rtnl_route_nh_get_ifindex (nexthop); - obj->plen = nl_addr_get_prefixlen (dst); - obj->metric = rtnl_route_get_priority (nlo); + NLA_PUT_STRING (msg, IFLA_INFO_KIND, kind); - if (id_only) + nla_nest_end (msg, info); + + return TRUE; +nla_put_failure: + return FALSE; +} + +static gboolean +_nl_msg_new_link_set_linkinfo_vlan (struct nl_msg *msg, + int vlan_id, + guint32 flags_mask, + guint32 flags_set, + const NMVlanQosMapping *ingress_qos, + int ingress_qos_len, + const NMVlanQosMapping *egress_qos, + int egress_qos_len) +{ + struct nlattr *info; + struct nlattr *data; + guint i; + gboolean has_any_vlan_properties = FALSE; + +#define VLAN_XGRESS_PRIO_VALID(from) (((from) & ~(guint32) 0x07) == 0) + + nm_assert (msg); + + /* We must not create an empty IFLA_LINKINFO section. Otherwise, kernel + * rejects the request as invalid. */ + if ( flags_mask != 0 + || vlan_id >= 0) + has_any_vlan_properties = TRUE; + if ( !has_any_vlan_properties + && ingress_qos && ingress_qos_len > 0) { + for (i = 0; i < ingress_qos_len; i++) { + if (VLAN_XGRESS_PRIO_VALID (ingress_qos[i].from)) { + has_any_vlan_properties = TRUE; + break; + } + } + } + if ( !has_any_vlan_properties + && egress_qos && egress_qos_len > 0) { + for (i = 0; i < egress_qos_len; i++) { + if (VLAN_XGRESS_PRIO_VALID (egress_qos[i].to)) { + has_any_vlan_properties = TRUE; + break; + } + } + } + if (!has_any_vlan_properties) return TRUE; - gw = rtnl_route_nh_get_gateway (nexthop); - if (gw) { - if (nl_addr_get_len (gw) != sizeof (obj->gateway)) - g_warn_if_reached (); - else - memcpy (&obj->gateway, nl_addr_get_binary_addr (gw), sizeof (obj->gateway)); + if (!(info = nla_nest_start (msg, IFLA_LINKINFO))) + goto nla_put_failure; + + NLA_PUT_STRING (msg, IFLA_INFO_KIND, "vlan"); + + if (!(data = nla_nest_start (msg, IFLA_INFO_DATA))) + goto nla_put_failure; + + if (vlan_id >= 0) + NLA_PUT_U16 (msg, IFLA_VLAN_ID, vlan_id); + + if (flags_mask != 0) { + struct ifla_vlan_flags flags = { + .flags = flags_mask & flags_set, + .mask = flags_mask, + }; + + NLA_PUT (msg, IFLA_VLAN_FLAGS, sizeof (flags), &flags); } - rtnl_route_get_metric (nlo, RTAX_ADVMSS, &obj->mss); - if (rtnl_route_get_flags (nlo) & RTM_F_CLONED) - obj->source = _NM_IP_CONFIG_SOURCE_RTM_F_CLONED; - else - obj->source = _nm_ip_config_source_from_rtprot (rtnl_route_get_protocol (nlo)); + + if (ingress_qos && ingress_qos_len > 0) { + struct nlattr *qos = NULL; + + for (i = 0; i < ingress_qos_len; i++) { + /* Silently ignore invalid mappings. Kernel would truncate + * them and modify the wrong mapping. */ + if (VLAN_XGRESS_PRIO_VALID (ingress_qos[i].from)) { + if (!qos) { + if (!(qos = nla_nest_start (msg, IFLA_VLAN_INGRESS_QOS))) + goto nla_put_failure; + } + NLA_PUT (msg, i, sizeof (ingress_qos[i]), &ingress_qos[i]); + } + } + + if (qos) + nla_nest_end (msg, qos); + } + + if (egress_qos && egress_qos_len > 0) { + struct nlattr *qos = NULL; + + for (i = 0; i < egress_qos_len; i++) { + if (VLAN_XGRESS_PRIO_VALID (egress_qos[i].to)) { + if (!qos) { + if (!(qos = nla_nest_start(msg, IFLA_VLAN_EGRESS_QOS))) + goto nla_put_failure; + } + NLA_PUT (msg, i, sizeof (egress_qos[i]), &egress_qos[i]); + } + } + + if (qos) + nla_nest_end(msg, qos); + } + + nla_nest_end (msg, data); + nla_nest_end (msg, info); return TRUE; +nla_put_failure: + return FALSE; +} + +static struct nl_msg * +_nl_msg_new_link (int nlmsg_type, + int nlmsg_flags, + int ifindex, + const char *ifname, + unsigned flags) +{ + struct nl_msg *msg; + struct ifinfomsg ifi = { + .ifi_flags = flags, + .ifi_index = ifindex, + }; + + nm_assert (NM_IN_SET (nlmsg_type, RTM_DELLINK, RTM_NEWLINK, RTM_GETLINK)); + + if (!(msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags))) + g_return_val_if_reached (NULL); + + if (nlmsg_append (msg, &ifi, sizeof (ifi), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if (ifname) + NLA_PUT_STRING (msg, IFLA_IFNAME, ifname); + + return msg; +nla_put_failure: + nlmsg_free (msg); + g_return_val_if_reached (NULL); +} + +/* Copied and modified from libnl3's build_addr_msg(). */ +static struct nl_msg * +_nl_msg_new_address (int nlmsg_type, + int nlmsg_flags, + int family, + int ifindex, + gconstpointer address, + int plen, + gconstpointer peer_address, + guint32 flags, + int scope, + guint32 lifetime, + guint32 preferred, + const char *label) +{ + struct nl_msg *msg; + struct ifaddrmsg am = { + .ifa_family = family, + .ifa_index = ifindex, + .ifa_prefixlen = plen, + .ifa_flags = flags, + }; + gsize addr_len; + + nm_assert (NM_IN_SET (family, AF_INET, AF_INET6)); + nm_assert (NM_IN_SET (nlmsg_type, RTM_NEWADDR, RTM_DELADDR)); + + msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags); + if (!msg) + g_return_val_if_reached (NULL); + + if (scope == -1) { + /* Allow having scope unset, and detect the scope (including IPv4 compatibility hack). */ + if ( family == AF_INET + && address + && *((char *) address) == 127) + scope = RT_SCOPE_HOST; + else + scope = RT_SCOPE_UNIVERSE; + } + am.ifa_scope = scope, + + addr_len = family == AF_INET ? sizeof (in_addr_t) : sizeof (struct in6_addr); + + if (nlmsg_append (msg, &am, sizeof (am), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if (address) + NLA_PUT (msg, IFA_LOCAL, addr_len, address); + + if (peer_address) + NLA_PUT (msg, IFA_ADDRESS, addr_len, peer_address); + else if (address) + NLA_PUT (msg, IFA_ADDRESS, addr_len, address); + + if (label && label[0]) + NLA_PUT_STRING (msg, IFA_LABEL, label); + + if ( family == AF_INET + && nlmsg_type != RTM_DELADDR + && address + && *((in_addr_t *) address) != 0) { + in_addr_t broadcast; + + broadcast = *((in_addr_t *) address) | ~nm_utils_ip4_prefix_to_netmask (plen); + NLA_PUT (msg, IFA_BROADCAST, addr_len, &broadcast); + } + + if ( lifetime != NM_PLATFORM_LIFETIME_PERMANENT + || preferred != NM_PLATFORM_LIFETIME_PERMANENT) { + struct ifa_cacheinfo ca = { + .ifa_valid = lifetime, + .ifa_prefered = preferred, + }; + + NLA_PUT (msg, IFA_CACHEINFO, sizeof(ca), &ca); + } + + if (flags & ~0xFF) { + /* only set the IFA_FLAGS attribute, if they actually contain additional + * flags that are not already set to am.ifa_flags. + * + * Older kernels refuse RTM_NEWADDR and RTM_NEWROUTE messages with EINVAL + * if they contain unknown netlink attributes. See net/core/rtnetlink.c, which + * was fixed by kernel commit 661d2967b3f1b34eeaa7e212e7b9bbe8ee072b59. */ + NLA_PUT_U32 (msg, IFA_FLAGS, flags); + } + + return msg; + +nla_put_failure: + nlmsg_free (msg); + g_return_val_if_reached (NULL); +} + +/* Copied and modified from libnl3's build_route_msg() and rtnl_route_build_msg(). */ +static struct nl_msg * +_nl_msg_new_route (int nlmsg_type, + int nlmsg_flags, + int family, + int ifindex, + NMIPConfigSource source, + unsigned char scope, + gconstpointer network, + int plen, + gconstpointer gateway, + guint32 metric, + guint32 mss, + gconstpointer pref_src) +{ + struct nl_msg *msg; + struct rtmsg rtmsg = { + .rtm_family = family, + .rtm_tos = 0, + .rtm_table = RT_TABLE_MAIN, /* omit setting RTA_TABLE attribute */ + .rtm_protocol = _nm_ip_config_source_to_rtprot (source), + .rtm_scope = scope, + .rtm_type = RTN_UNICAST, + .rtm_flags = 0, + .rtm_dst_len = plen, + .rtm_src_len = 0, + }; + NMIPAddr network_clean; + + gsize addr_len; + + nm_assert (NM_IN_SET (family, AF_INET, AF_INET6)); + nm_assert (NM_IN_SET (nlmsg_type, RTM_NEWROUTE, RTM_DELROUTE)); + nm_assert (network); + + msg = nlmsg_alloc_simple (nlmsg_type, nlmsg_flags); + if (!msg) + g_return_val_if_reached (NULL); + + if (nlmsg_append (msg, &rtmsg, sizeof (rtmsg), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + addr_len = family == AF_INET ? sizeof (in_addr_t) : sizeof (struct in6_addr); + + clear_host_address (family, network, plen, &network_clean); + NLA_PUT (msg, RTA_DST, addr_len, &network_clean); + + NLA_PUT_U32 (msg, RTA_PRIORITY, metric); + + if (pref_src) + NLA_PUT (msg, RTA_PREFSRC, addr_len, pref_src); + + if (mss > 0) { + struct nlattr *metrics; + + metrics = nla_nest_start (msg, RTA_METRICS); + if (!metrics) + goto nla_put_failure; + + NLA_PUT_U32 (msg, RTAX_ADVMSS, mss); + + nla_nest_end(msg, metrics); + } + + /* We currently don't have need for multi-hop routes... */ + if ( gateway + && memcmp (gateway, &nm_ip_addr_zero, addr_len) != 0) + NLA_PUT (msg, RTA_GATEWAY, addr_len, gateway); + NLA_PUT_U32 (msg, RTA_OIF, ifindex); + + return msg; + +nla_put_failure: + nlmsg_free (msg); + g_return_val_if_reached (NULL); +} + +/******************************************************************/ + +static int +_nl_sock_flush_data (struct nl_sock *sk) +{ + int nle; + struct nl_cb *cb; + + cb = nl_cb_clone (nl_socket_get_cb (sk)); + if (cb == NULL) + return -NLE_NOMEM; + + nl_cb_set (cb, NL_CB_VALID, NL_CB_DEFAULT, NULL, NULL); + nl_cb_set (cb, NL_CB_SEQ_CHECK, NL_CB_DEFAULT, NULL, NULL); + nl_cb_err (cb, NL_CB_DEFAULT, NULL, NULL); + do { + errno = 0; + + nle = nl_recvmsgs (sk, cb); + + /* Work around a libnl bug fixed in 3.2.22 (375a6294) */ + if (nle == 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) + nle = -NLE_AGAIN; + } while (nle != -NLE_AGAIN); + + nl_cb_put (cb); + return nle; +} + +static void +_nl_msg_set_seq (struct nl_sock *sk, struct nl_msg *msg, guint32 *out_seq) +{ + guint32 seq; + + /* choose our own sequence number, because libnl does not ensure that + * it isn't zero -- which would confuse our checking for outstanding + * messages. */ + seq = nl_socket_use_seq (sk); + if (seq == 0) + seq = nl_socket_use_seq (sk); + + nlmsg_hdr (msg)->nlmsg_seq = seq; + if (out_seq) + *out_seq = seq; +} + +/******************************************************************/ + +static int _support_kernel_extended_ifa_flags = -1; + +#define _support_kernel_extended_ifa_flags_still_undecided() (G_UNLIKELY (_support_kernel_extended_ifa_flags == -1)) + +static void +_support_kernel_extended_ifa_flags_detect (struct nl_msg *msg) +{ + struct nlmsghdr *msg_hdr; + + if (!_support_kernel_extended_ifa_flags_still_undecided ()) + return; + + msg_hdr = nlmsg_hdr (msg); + if (msg_hdr->nlmsg_type != RTM_NEWADDR) + return; + + /* the extended address flags are only set for AF_INET6 */ + if (((struct ifaddrmsg *) nlmsg_data (msg_hdr))->ifa_family != AF_INET6) + return; + + /* see if the nl_msg contains the IFA_FLAGS attribute. If it does, + * we assume, that the kernel supports extended flags, IFA_F_MANAGETEMPADDR + * and IFA_F_NOPREFIXROUTE (they were added together). + **/ + _support_kernel_extended_ifa_flags = !!nlmsg_find_attr (msg_hdr, sizeof (struct ifaddrmsg), 8 /* IFA_FLAGS */); + _LOG2D ("support: kernel-extended-ifa-flags: %ssupported", _support_kernel_extended_ifa_flags ? "" : "not "); +} + +static gboolean +_support_kernel_extended_ifa_flags_get (void) +{ + if (_support_kernel_extended_ifa_flags_still_undecided ()) { + _LOG2W ("support: kernel-extended-ifa-flags: unable to detect kernel support for handling IPv6 temporary addresses. Assume none"); + _support_kernel_extended_ifa_flags = 0; + } + return _support_kernel_extended_ifa_flags; +} + +/****************************************************************** + * NMPlatform types and functions + ******************************************************************/ + +typedef struct _NMLinuxPlatformPrivate NMLinuxPlatformPrivate; + +struct _NMLinuxPlatformPrivate { + struct nl_sock *nlh; + struct nl_sock *nlh_event; + guint32 nlh_seq_expect; + guint32 nlh_seq_last; + NMPCache *cache; + GIOChannel *event_channel; + guint event_id; + + gboolean sysctl_get_warned; + GHashTable *sysctl_get_prev_values; + + GUdevClient *udev_client; + + struct { + DelayedActionType flags; + GPtrArray *list_master_connected; + GPtrArray *list_refresh_link; + gint is_handling; + guint idle_id; + } delayed_action; + + GHashTable *prune_candidates; + GHashTable *delayed_deletion; + + GHashTable *wifi_data; +}; + +static inline NMLinuxPlatformPrivate * +NM_LINUX_PLATFORM_GET_PRIVATE (const void *self) +{ + nm_assert (NM_IS_LINUX_PLATFORM (self)); + + return ((NMLinuxPlatform *) self)->priv; +} + +G_DEFINE_TYPE (NMLinuxPlatform, nm_linux_platform, NM_TYPE_PLATFORM) + +void +nm_linux_platform_setup (void) +{ + g_object_new (NM_TYPE_LINUX_PLATFORM, + NM_PLATFORM_REGISTER_SINGLETON, TRUE, + NULL); +} + +/******************************************************************/ + +static gboolean +check_support_kernel_extended_ifa_flags (NMPlatform *platform) +{ + g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); + + return _support_kernel_extended_ifa_flags_get (); +} + +static gboolean +check_support_user_ipv6ll (NMPlatform *platform) +{ + g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); + + return _support_user_ipv6ll_get (); +} + +static void +process_events (NMPlatform *platform) +{ + delayed_action_handle_all (platform, TRUE); +} + +/******************************************************************/ + +#define cache_lookup_all_objects(type, platform, obj_type, visible_only) \ + ((const type *const*) nmp_cache_lookup_multi (NM_LINUX_PLATFORM_GET_PRIVATE ((platform))->cache, \ + nmp_cache_id_init_object_type (NMP_CACHE_ID_STATIC, (obj_type), (visible_only)), \ + NULL)) + +static gboolean +_lookup_cached_link_data (NMPlatform *platform, + int ifindex, + const char *logging_tag, + unsigned *out_flags) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + const NMPObject *obj_cache; + + obj_cache = nmp_cache_lookup_link (priv->cache, ifindex); + if ( obj_cache + && obj_cache->_link.netlink.is_in_netlink) { + *out_flags = obj_cache->link.flags; + return TRUE; + } + _LOGD ("link: change %d: %s: link does not exist", ifindex, logging_tag); + return FALSE; } /******************************************************************/ @@ -1831,8 +2680,8 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP NMPlatform *platform = NM_PLATFORM (user_data); NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); const NMPClass *klass; - char str_buf[sizeof (_nm_platform_to_string_buffer)]; - char str_buf2[sizeof (_nm_platform_to_string_buffer)]; + char str_buf[sizeof (_nm_utils_to_string_buffer)]; + char str_buf2[sizeof (_nm_utils_to_string_buffer)]; nm_assert (old || new); nm_assert (NM_IN_SET (ops_type, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_REMOVED, NMP_CACHE_OPS_UPDATED)); @@ -1986,14 +2835,14 @@ cache_pre_hook (NMPCache *cache, const NMPObject *old, const NMPObject *new, NMP } static NMPCacheOpsType -cache_remove_netlink (NMPlatform *platform, const NMPObject *obj_needle, NMPObject **out_obj_cache, gboolean *out_was_visible, NMPlatformReason reason) +cache_remove_netlink (NMPlatform *platform, const NMPObject *obj_id, NMPObject **out_obj_cache, gboolean *out_was_visible, NMPlatformReason reason) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); NMPObject *obj_cache; gboolean was_visible; NMPCacheOpsType cache_op; - cache_op = nmp_cache_remove_netlink (priv->cache, obj_needle, &obj_cache, &was_visible, cache_pre_hook, platform); + cache_op = nmp_cache_remove_netlink (priv->cache, obj_id, &obj_cache, &was_visible, cache_pre_hook, platform); do_emit_signal (platform, obj_cache, cache_op, was_visible, NM_PLATFORM_REASON_INTERNAL); if (out_obj_cache) @@ -2046,9 +2895,16 @@ static void do_request_link (NMPlatform *platform, int ifindex, const char *name, gboolean handle_delayed_action) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + int nle; guint32 seq; - _LOGT ("do_request_link (%d,%s)", ifindex, name ? name : ""); + if (name && !name[0]) + name = NULL; + + g_return_if_fail (ifindex > 0 || name); + + _LOGD ("do-request-link: %d %s", ifindex, name ? name : ""); if (ifindex > 0) { NMPObject *obj; @@ -2062,8 +2918,18 @@ do_request_link (NMPlatform *platform, int ifindex, const char *name, gboolean h event_handler_read_netlink_all (platform, FALSE); - if (_nl_sock_request_link (platform, priv->nlh_event, ifindex, name, &seq) == 0) - _new_sequence_number (platform, seq); + nlmsg = _nl_msg_new_link (RTM_GETLINK, + 0, + ifindex, + name, + 0); + if (nlmsg) { + _nl_msg_set_seq (priv->nlh_event, nlmsg, &seq); + + nle = nl_send_auto (priv->nlh_event, nlmsg); + if (nle >= 0) + _new_sequence_number (platform, seq); + } event_handler_read_netlink_all (platform, TRUE); @@ -2098,6 +2964,12 @@ do_request_all (NMPlatform *platform, DelayedActionType action_type, gboolean ha for (iflags = (DelayedActionType) 0x1LL; iflags <= DELAYED_ACTION_TYPE_MAX; iflags <<= 1) { if (NM_FLAGS_HAS (action_type, iflags)) { NMPObjectType obj_type = delayed_action_refresh_to_object_type (iflags); + const NMPClass *klass = nmp_class_from_type (obj_type); + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + struct rtgenmsg gmsg = { + .rtgen_family = klass->addr_family, + }; + int nle; /* clear any delayed action that request a refresh of this object type. */ priv->delayed_action.flags &= ~iflags; @@ -2110,9 +2982,26 @@ do_request_all (NMPlatform *platform, DelayedActionType action_type, gboolean ha event_handler_read_netlink_all (platform, FALSE); - if (_nl_sock_request_all (platform, priv->nlh_event, obj_type, &seq) == 0) + /* reimplement + * nl_rtgen_request (sk, klass->rtm_gettype, klass->addr_family, NLM_F_DUMP); + * because we need the sequence number. + */ + nlmsg = nlmsg_alloc_simple (klass->rtm_gettype, NLM_F_DUMP); + if (!nlmsg) + goto next; + + nle = nlmsg_append (nlmsg, &gmsg, sizeof (gmsg), NLMSG_ALIGNTO); + if (nle < 0) + goto next; + + _nl_msg_set_seq (priv->nlh_event, nlmsg, &seq); + + nle = nl_send_auto (priv->nlh_event, nlmsg); + if (nle >= 0) _new_sequence_number (platform, seq); } +next: + ; } event_handler_read_netlink_all (platform, TRUE); @@ -2122,211 +3011,6 @@ do_request_all (NMPlatform *platform, DelayedActionType action_type, gboolean ha delayed_action_handle_all (platform, FALSE); } -static gboolean -kernel_add_object (NMPlatform *platform, NMPObjectType obj_type, const struct nl_object *nlo) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - int nle; - - g_return_val_if_fail (nlo, FALSE); - - switch (obj_type) { - case NMP_OBJECT_TYPE_LINK: - nle = rtnl_link_add (priv->nlh, (struct rtnl_link *) nlo, NLM_F_CREATE); - break; - case NMP_OBJECT_TYPE_IP4_ADDRESS: - case NMP_OBJECT_TYPE_IP6_ADDRESS: - nle = rtnl_addr_add (priv->nlh, (struct rtnl_addr *) nlo, NLM_F_CREATE | NLM_F_REPLACE); - break; - case NMP_OBJECT_TYPE_IP4_ROUTE: - case NMP_OBJECT_TYPE_IP6_ROUTE: - nle = rtnl_route_add (priv->nlh, (struct rtnl_route *) nlo, NLM_F_CREATE | NLM_F_REPLACE); - break; - default: - g_return_val_if_reached (-NLE_INVAL); - } - - _LOGT ("kernel-add-%s: returned %s (%d)", - nmp_class_from_type (obj_type)->obj_type_name, nl_geterror (nle), -nle); - - switch (nle) { - case -NLE_SUCCESS: - return -NLE_SUCCESS; - case -NLE_EXIST: - /* NLE_EXIST is considered equivalent to success to avoid race conditions. You - * never know when something sends an identical object just before - * NetworkManager. */ - if (obj_type != NMP_OBJECT_TYPE_LINK) - return -NLE_SUCCESS; - /* fall-through */ - default: - return nle; - } -} - -static int -kernel_delete_object (NMPlatform *platform, NMPObjectType object_type, const struct nl_object *object) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - int nle; - - switch (object_type) { - case NMP_OBJECT_TYPE_LINK: - nle = rtnl_link_delete (priv->nlh, (struct rtnl_link *) object); - break; - case NMP_OBJECT_TYPE_IP4_ADDRESS: - case NMP_OBJECT_TYPE_IP6_ADDRESS: - nle = rtnl_addr_delete (priv->nlh, (struct rtnl_addr *) object, 0); - break; - case NMP_OBJECT_TYPE_IP4_ROUTE: - case NMP_OBJECT_TYPE_IP6_ROUTE: - nle = rtnl_route_delete (priv->nlh, (struct rtnl_route *) object, 0); - break; - default: - g_assert_not_reached (); - } - - switch (nle) { - case -NLE_SUCCESS: - return NLE_SUCCESS; - case -NLE_OBJ_NOTFOUND: - _LOGT ("kernel-delete-%s: failed with \"%s\" (%d), meaning the object was already removed", - nmp_class_from_type (object_type)->obj_type_name, nl_geterror (nle), -nle); - return -NLE_SUCCESS; - case -NLE_FAILURE: - if (object_type == NMP_OBJECT_TYPE_IP6_ADDRESS) { - /* On RHEL7 kernel, deleting a non existing address fails with ENXIO (which libnl maps to NLE_FAILURE) */ - _LOGT ("kernel-delete-%s: deleting address failed with \"%s\" (%d), meaning the address was already removed", - nmp_class_from_type (object_type)->obj_type_name, nl_geterror (nle), -nle); - return NLE_SUCCESS; - } - break; - case -NLE_NOADDR: - if (object_type == NMP_OBJECT_TYPE_IP4_ADDRESS || object_type == NMP_OBJECT_TYPE_IP6_ADDRESS) { - _LOGT ("kernel-delete-%s: deleting address failed with \"%s\" (%d), meaning the address was already removed", - nmp_class_from_type (object_type)->obj_type_name, nl_geterror (nle), -nle); - return -NLE_SUCCESS; - } - break; - default: - break; - } - _LOGT ("kernel-delete-%s: failed with %s (%d)", - nmp_class_from_type (object_type)->obj_type_name, nl_geterror (nle), -nle); - return nle; -} - -static int -kernel_change_link (NMPlatform *platform, struct rtnl_link *nlo, gboolean *complete_from_cache) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - struct nl_msg *msg; - int nle; - const int nlflags = 0; - int ifindex; - - ifindex = rtnl_link_get_ifindex (nlo); - - g_return_val_if_fail (ifindex > 0, FALSE); - - /* Previously, we were using rtnl_link_change(), which builds a request based - * on the diff with an original link instance. - * - * The diff only reused ifi_family, ifi_index, ifi_flags, and name from - * the original link (see rtnl_link_build_change_request()). - * - * We don't do that anymore as we don't have an "orig" netlink instance that - * we can use. Instead the caller must ensure to properly initialize @nlo, - * especially it must set family, ifindex (or ifname) and flags. - * ifname should be set *only* if the caller wishes to change the name. - * - * @complete_from_cache is a convenience to copy the link flags over the link inside - * the platform cache. */ - - if (*complete_from_cache) { - const NMPObject *obj_cache; - - obj_cache = nmp_cache_lookup_link (priv->cache, ifindex); - if (!obj_cache || !obj_cache->_link.netlink.is_in_netlink) { - _LOGT ("kernel-change-link: failure changing link %d: cannot complete link", ifindex); - *complete_from_cache = FALSE; - return -NLE_INVAL; - } - - rtnl_link_set_flags (nlo, obj_cache->link.flags); - - /* If the caller wants to rename the link, he should explicitly set - * rtnl_link_set_name(). In all other cases, it should leave the name - * unset. Unfortunately, there is not public API in libnl to modify the - * attribute mask and clear (link->ce_mask = ~LINK_ATTR_IFNAME), so we - * require the caller to do the right thing -- i.e. don't set the name. - */ - } - - /* We don't use rtnl_link_change() because we have no original rtnl_link object - * at hand. We also don't use rtnl_link_add() because that doesn't have the - * hack to retry with RTM_SETLINK. Reimplement a mix of both. */ - - nle = rtnl_link_build_add_request (nlo, nlflags, &msg); - if (nle < 0) { - _LOGT ("kernel-change-link: failure changing link %d: cannot construct message (%s, %d)", - ifindex, nl_geterror (nle), -nle); - return nle; - } - -retry: - nle = nl_send_auto_complete (priv->nlh, msg); - if (nle < 0) - goto errout; - - nle = nl_wait_for_ack(priv->nlh); - if (nle == -NLE_OPNOTSUPP && nlmsg_hdr (msg)->nlmsg_type == RTM_NEWLINK) { - nlmsg_hdr (msg)->nlmsg_type = RTM_SETLINK; - goto retry; - } - -errout: - nlmsg_free(msg); - - /* NLE_EXIST is considered equivalent to success to avoid race conditions. You - * never know when something sends an identical object just before - * NetworkManager. - * - * When netlink returns NLE_OBJ_NOTFOUND, it usually means it failed to find - * firmware for the device, especially on nm_platform_link_set_up (). - * This is basically the same check as in the original code and could - * potentially be improved. - */ - switch (nle) { - case -NLE_SUCCESS: - _LOGT ("kernel-change-link: success changing link %d", ifindex); - break; - case -NLE_EXIST: - _LOGT ("kernel-change-link: success changing link %d: %s (%d)", - ifindex, nl_geterror (nle), -nle); - break; - case -NLE_OBJ_NOTFOUND: - _LOGT ("kernel-change-link: failure changing link %d: firmware not found (%s, %d)", - ifindex, nl_geterror (nle), -nle); - break; - default: - _LOGT ("kernel-change-link: failure changing link %d: netlink error (%s, %d)", - ifindex, nl_geterror (nle), -nle); - break; - } - - return nle; -} - -static void -ref_object (struct nl_object *obj, void *data) -{ - struct nl_object **out = data; - - nl_object_get (obj); - *out = obj; -} - static int event_seq_check (struct nl_msg *msg, gpointer user_data) { @@ -2374,85 +3058,75 @@ event_notification (struct nl_msg *msg, gpointer user_data) { NMPlatform *platform = NM_PLATFORM (user_data); NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (user_data); - auto_nl_object struct nl_object *nlo = NULL; nm_auto_nmpobj NMPObject *obj = NULL; + nm_auto_nmpobj NMPObject *obj_cache = NULL; struct nlmsghdr *msghdr; char buf_nlmsg_type[16]; + gboolean id_only = FALSE; msghdr = nlmsg_hdr (msg); if (_support_kernel_extended_ifa_flags_still_undecided () && msghdr->nlmsg_type == RTM_NEWADDR) _support_kernel_extended_ifa_flags_detect (msg); - nl_msg_parse (msg, ref_object, &nlo); - if (!nlo) - return NL_OK; - - if (_support_user_ipv6ll_still_undecided() && msghdr->nlmsg_type == RTM_NEWLINK) - _support_user_ipv6ll_detect ((struct rtnl_link *) nlo); + if (NM_IN_SET (msghdr->nlmsg_type, RTM_DELLINK, RTM_DELADDR, RTM_DELROUTE)) { + /* The event notifies about a deleted object. We don't need to initialize all + * fields of the object. */ + id_only = TRUE; + } - switch (msghdr->nlmsg_type) { - case RTM_DELADDR: - case RTM_DELLINK: - case RTM_DELROUTE: - /* The event notifies about a deleted object. We don't need to initialize all the - * fields of the nmp-object. Shortcut nmp_object_from_nl(). */ - obj = nmp_object_from_nl (platform, nlo, TRUE, TRUE); - _LOGt ("event-notification: %s, seq %u: %s", + obj = nmp_object_new_from_nl (platform, priv->cache, msg, id_only); + if (!obj) { + _LOGt ("event-notification: %s, seq %u: ignore", _nl_nlmsg_type_to_str (msghdr->nlmsg_type, buf_nlmsg_type, sizeof (buf_nlmsg_type)), - msghdr->nlmsg_seq, nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0)); - break; - default: - obj = nmp_object_from_nl (platform, nlo, FALSE, TRUE); - _LOGt ("event-notification: %s, seq %u: %s", - _nl_nlmsg_type_to_str (msghdr->nlmsg_type, buf_nlmsg_type, sizeof (buf_nlmsg_type)), - msghdr->nlmsg_seq, nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - break; + msghdr->nlmsg_seq); + return NL_OK; } - if (obj) { - nm_auto_nmpobj NMPObject *obj_cache = NULL; - - switch (msghdr->nlmsg_type) { + _LOGt ("event-notification: %s, seq %u: %s", + _nl_nlmsg_type_to_str (msghdr->nlmsg_type, buf_nlmsg_type, sizeof (buf_nlmsg_type)), + msghdr->nlmsg_seq, nmp_object_to_string (obj, + id_only ? NMP_OBJECT_TO_STRING_ID : NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - case RTM_NEWLINK: - if (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK) { - if (g_hash_table_lookup (priv->delayed_deletion, obj) != NULL) { - /* the object is scheduled for delayed deletion. Replace that object - * by clearing the value from priv->delayed_deletion. */ - _LOGT ("delayed-deletion: clear delayed deletion of protected object %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0)); - g_hash_table_insert (priv->delayed_deletion, nmp_object_ref (obj), NULL); - } - delayed_action_clear_REFRESH_LINK (platform, obj->link.ifindex); - } - /* fall-through */ - case RTM_NEWADDR: - case RTM_NEWROUTE: - cache_update_netlink (platform, obj, &obj_cache, NULL, NM_PLATFORM_REASON_EXTERNAL); - break; + switch (msghdr->nlmsg_type) { - case RTM_DELLINK: - if ( NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK - && g_hash_table_contains (priv->delayed_deletion, obj)) { - /* We sometimes receive spurious RTM_DELLINK events. In this case, we want to delay - * the deletion of the object until later. */ - _LOGT ("delayed-deletion: delay deletion of protected object %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0)); - g_hash_table_insert (priv->delayed_deletion, nmp_object_ref (obj), nmp_object_ref (obj)); - break; + case RTM_NEWLINK: + if (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK) { + if (g_hash_table_lookup (priv->delayed_deletion, obj) != NULL) { + /* the object is scheduled for delayed deletion. Replace that object + * by clearing the value from priv->delayed_deletion. */ + _LOGT ("delayed-deletion: clear delayed deletion of protected object %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + g_hash_table_insert (priv->delayed_deletion, nmp_object_ref (obj), NULL); } - /* fall-through */ - case RTM_DELADDR: - case RTM_DELROUTE: - cache_remove_netlink (platform, obj, &obj_cache, NULL, NM_PLATFORM_REASON_EXTERNAL); - break; + delayed_action_clear_REFRESH_LINK (platform, obj->link.ifindex); + } + /* fall-through */ + case RTM_NEWADDR: + case RTM_NEWROUTE: + cache_update_netlink (platform, obj, &obj_cache, NULL, NM_PLATFORM_REASON_EXTERNAL); + break; - default: + case RTM_DELLINK: + if ( NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK + && g_hash_table_contains (priv->delayed_deletion, obj)) { + /* We sometimes receive spurious RTM_DELLINK events. In this case, we want to delay + * the deletion of the object until later. */ + _LOGT ("delayed-deletion: delay deletion of protected object %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + g_hash_table_insert (priv->delayed_deletion, nmp_object_ref (obj), nmp_object_ref (obj)); break; } + /* fall-through */ + case RTM_DELADDR: + case RTM_DELROUTE: + cache_remove_netlink (platform, obj, &obj_cache, NULL, NM_PLATFORM_REASON_EXTERNAL); + break; - cache_prune_candidates_drop (platform, obj_cache); + default: + break; } + cache_prune_candidates_drop (platform, obj_cache); + return NL_OK; } @@ -2717,47 +3391,64 @@ _nm_platform_link_get_by_address (NMPlatform *platform, return obj ? &obj->link : NULL; } -static struct nl_object * -build_rtnl_link (int ifindex, const char *name, NMLinkType type) +/*****************************************************************************/ + +static const NMPObject * +link_get_lnk (NMPlatform *platform, int ifindex, NMLinkType link_type, const NMPlatformLink **out_link) { - struct rtnl_link *rtnllink; - int nle; + const NMPObject *obj = cache_lookup_link (platform, ifindex); - rtnllink = _nl_rtnl_link_alloc (ifindex, name); - if (type) { - nle = rtnl_link_set_type (rtnllink, nm_link_type_to_rtnl_type_string (type)); - g_assert (!nle); - } - return (struct nl_object *) rtnllink; -} + if (!obj) + return NULL; -struct nl_object * -_nmp_vt_cmd_plobj_to_nl_link (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only) -{ - const NMPlatformLink *obj = (const NMPlatformLink *) _obj; + NM_SET_OUT (out_link, &obj->link); - return build_rtnl_link (obj->ifindex, - obj->name[0] ? obj->name : NULL, - obj->type); + 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 gboolean -do_add_link (NMPlatform *platform, const char *name, const struct rtnl_link *nlo) +do_add_link (NMPlatform *platform, + NMLinkType link_type, + const char *name, + struct nl_msg *nlmsg) { - NMPObject obj_needle; + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); int nle; event_handler_read_netlink_all (platform, FALSE); - nle = kernel_add_object (platform, NMP_OBJECT_TYPE_LINK, (const struct nl_object *) nlo); + nle = nl_send_auto (priv->nlh, nlmsg); if (nle < 0) { - _LOGE ("do-add-link: failure adding link '%s': %s", name, nl_geterror (nle)); + _LOGE ("do-add-link[%s/%s]: failure sending netlink request \"%s\" (%d)", + name, + nm_link_type_to_string (link_type), + nl_geterror (nle), -nle); return FALSE; } - _LOGD ("do-add-link: success adding link '%s'", name); - nmp_object_stackinit_id_link (&obj_needle, 0); - g_strlcpy (obj_needle.link.name, name, sizeof (obj_needle.link.name)); + nle = nl_wait_for_ack (priv->nlh); + switch (nle) { + case -NLE_SUCCESS: + _LOGD ("do-add-link[%s/%s]: success adding", + name, + nm_link_type_to_string (link_type)); + break; + default: + _LOGE ("do-add-link[%s/%s]: failed with \"%s\" (%d)", + name, + nm_link_type_to_string (link_type), + nl_geterror (nle), -nle); + return FALSE; + } delayed_action_handle_all (platform, TRUE); @@ -2765,35 +3456,40 @@ do_add_link (NMPlatform *platform, const char *name, const struct rtnl_link *nlo * the notification is not yet ready via nlh_event, so we have to re-request the * link so that it is in the cache. A better solution would be to do everything * via one netlink socket. */ - if (!nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, 0, obj_needle.link.name, FALSE, NM_LINK_TYPE_NONE, NULL, NULL)) { - _LOGT ("do-add-link: reload: the added link is not yet ready. Request %s", obj_needle.link.name); - do_request_link (platform, 0, obj_needle.link.name, TRUE); + if (!nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, 0, name, FALSE, NM_LINK_TYPE_NONE, NULL, NULL)) { + _LOGT ("do-add-link[%s/%s]: the added link is not yet ready. Request anew", + name, + nm_link_type_to_string (link_type)); + do_request_link (platform, 0, name, TRUE); } - /* Return true, because kernel_add_object() succeeded. This doesn't indicate that the - * object is now actuall in the cache, because there could be a race. - * - * For that, you'd have to look at @out_obj. */ + /* Return true, because the netlink request succeeded. This doesn't indicate that the + * object is now actually in the cache, because there could be a race. */ return TRUE; } static gboolean -do_add_link_with_lookup (NMPlatform *platform, const char *name, const struct rtnl_link *nlo, NMLinkType expected_link_type, NMPlatformLink *out_link) +do_add_link_with_lookup (NMPlatform *platform, + NMLinkType link_type, + const char *name, + struct nl_msg *nlmsg, + NMPlatformLink *out_link) { const NMPObject *obj; - do_add_link (platform, name, nlo); + do_add_link (platform, link_type, name, nlmsg); obj = nmp_cache_lookup_link_full (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, - 0, name, FALSE, expected_link_type, NULL, NULL); + 0, name, FALSE, link_type, NULL, NULL); if (out_link && obj) *out_link = obj->link; return !!obj; } static gboolean -do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, const struct nl_object *nlo) +do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg) { + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); int nle; nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_id), @@ -2802,50 +3498,109 @@ do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, const struct nl event_handler_read_netlink_all (platform, FALSE); - nle = kernel_add_object (platform, NMP_OBJECT_GET_CLASS (obj_id)->obj_type, (const struct nl_object *) nlo); + nle = nl_send_auto (priv->nlh, nlmsg); if (nle < 0) { - _LOGW ("do-add-%s: failure adding %s '%s': %s (%d)", + _LOGE ("do-add-%s[%s]: failure sending netlink request \"%s\" (%d)", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, + nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), + nl_geterror (nle), -nle); + return FALSE; + } + + nle = nl_wait_for_ack (priv->nlh); + switch (nle) { + case -NLE_SUCCESS: + _LOGD ("do-add-%s[%s]: success adding", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + break; + case -NLE_EXIST: + /* NLE_EXIST is considered equivalent to success to avoid race conditions. You + * never know when something sends an identical object just before + * NetworkManager. */ + _LOGD ("do-add-%s[%s]: adding link failed with \"%s\" (%d), meaning such a link already exists", + NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, + nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), + nl_geterror (nle), -nle); + break; + default: + _LOGE ("do-add-%s[%s]: failed with \"%s\" (%d)", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), nl_geterror (nle), -nle); return FALSE; } - _LOGD ("do-add-%s: success adding object %s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); delayed_action_handle_all (platform, TRUE); /* FIXME: instead of re-requesting the added object, add it via nlh_event * so that the events are in sync. */ if (!nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, obj_id)) { - _LOGT ("do-add-%s: reload: the added object is not yet ready. Request %s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + _LOGT ("do-add-%s[%s]: the added object is not yet ready. Request anew", + NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, + nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id), TRUE); } /* The return value doesn't say, whether the object is in the platform cache after adding - * it. - * Instead the return value says, whether kernel_add_object() succeeded. */ + * it. Instead the return value says, whether the netlink request succeeded. */ return TRUE; } - static gboolean -do_delete_object (NMPlatform *platform, const NMPObject *obj_id, const struct nl_object *nlo) +do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - auto_nl_object struct nl_object *nlo_free = NULL; int nle; event_handler_read_netlink_all (platform, FALSE); - if (!nlo) - nlo = nlo_free = nmp_object_to_nl (platform, obj_id, FALSE); + nle = nl_send_auto (priv->nlh, nlmsg); + if (nle < 0) { + _LOGE ("do-delete-%s[%s]: failure sending netlink request \"%s\" (%d)", + NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, + nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), + nl_geterror (nle), -nle); + return FALSE; + } - nle = kernel_delete_object (platform, NMP_OBJECT_GET_TYPE (obj_id), nlo); - if (nle < 0) - _LOGE ("do-delete-%s: failure deleting '%s': %s (%d)", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), nl_geterror (nle), -nle); - else - _LOGD ("do-delete-%s: success deleting '%s'", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + nle = nl_wait_for_ack (priv->nlh); + switch (nle) { + case -NLE_SUCCESS: + _LOGD ("do-delete-%s[%s]: success deleting", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + break; + case -NLE_OBJ_NOTFOUND: + _LOGD ("do-delete-%s[%s]: failed with \"%s\" (%d), meaning the object was already removed", + NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, + nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), + nl_geterror (nle), -nle); + break; + case -NLE_FAILURE: + if (NMP_OBJECT_GET_TYPE (obj_id) != NMP_OBJECT_TYPE_IP6_ADDRESS) + goto nle_failure; + + /* On RHEL7 kernel, deleting a non existing address fails with ENXIO (which libnl maps to NLE_FAILURE) */ + _LOGD ("do-delete-%s[%s]: deleting address failed with \"%s\" (%d), meaning the address was already removed", + NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, + nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), + nl_geterror (nle), -nle); + break; + case -NLE_NOADDR: + if ( NMP_OBJECT_GET_TYPE (obj_id) != NMP_OBJECT_TYPE_IP4_ADDRESS + && NMP_OBJECT_GET_TYPE (obj_id) != NMP_OBJECT_TYPE_IP6_ADDRESS) + goto nle_failure; + + _LOGD ("do-delete-%s[%s]: deleting address failed with \"%s\" (%d), meaning the address was already removed", + NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, + nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), + nl_geterror (nle), -nle); + break; + default: +nle_failure: + _LOGE ("do-delete-%s[%s]: failed with \"%s\" (%d)", + NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, + nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), + nl_geterror (nle), -nle); + return FALSE; + } delayed_action_handle_all (platform, TRUE); @@ -2856,50 +3611,63 @@ do_delete_object (NMPlatform *platform, const NMPObject *obj_id, const struct nl obj = nmp_cache_lookup_link_full (priv->cache, obj_id->link.ifindex, obj_id->link.ifindex <= 0 && obj_id->link.name[0] ? obj_id->link.name : NULL, FALSE, NM_LINK_TYPE_NONE, NULL, NULL); if (obj && obj->_link.netlink.is_in_netlink) { - _LOGT ("do-delete-%s: reload: the deleted object is not yet removed. Request %s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + _LOGT ("do-delete-%s[%s]: reload: the deleted object is not yet removed. Request anew", + NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, + nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); do_request_link (platform, obj_id->link.ifindex, obj_id->link.name, TRUE); } } else { if (nmp_cache_lookup_obj (priv->cache, obj_id)) { - _LOGT ("do-delete-%s: reload: the deleted object is not yet removed. Request %s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); + _LOGT ("do-delete-%s[%s]: reload: the deleted object is not yet removed. Request anew", + NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, + nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0)); do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id), TRUE); } } /* The return value doesn't say, whether the object is in the platform cache after adding - * it. - * Instead the return value says, whether kernel_add_object() succeeded. */ - return nle >= 0; + * it. Instead the return value says, whether the netlink request succeeded. */ + return TRUE; } static NMPlatformError -do_change_link (NMPlatform *platform, struct rtnl_link *nlo, gboolean complete_from_cache) +do_change_link (NMPlatform *platform, int ifindex, struct nl_msg *nlmsg) { + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); int nle; - int ifindex; - gboolean complete_from_cache2 = complete_from_cache; - ifindex = rtnl_link_get_ifindex (nlo); - if (ifindex <= 0) - g_return_val_if_reached (NM_PLATFORM_ERROR_BUG); +retry: + nle = nl_send_auto_complete (priv->nlh, nlmsg); + if (nle < 0) { + _LOGE ("do-change-link[%d]: failure sending netlink request \"%s\" (%d)", + ifindex, + nl_geterror (nle), -nle); + return NM_PLATFORM_ERROR_UNSPECIFIED; + } - nle = kernel_change_link (platform, nlo, &complete_from_cache2); + nle = nl_wait_for_ack (priv->nlh); + if ( nle == -NLE_OPNOTSUPP + && nlmsg_hdr (nlmsg)->nlmsg_type == RTM_NEWLINK) { + nlmsg_hdr (nlmsg)->nlmsg_type = RTM_SETLINK; + goto retry; + } switch (nle) { case -NLE_SUCCESS: - _LOGD ("do-change-link: success changing link %d", ifindex); + _LOGD ("do-change-link[%d]: success changing link", ifindex); break; case -NLE_EXIST: - _LOGD ("do-change-link: success changing link %d: %s (%d)", ifindex, nl_geterror (nle), -nle); + _LOGD ("do-change-link[%d]: success changing link: %s (%d)", + ifindex, nl_geterror (nle), -nle); break; case -NLE_OBJ_NOTFOUND: - /* fall-through */ + _LOGD ("do-change-link[%d]: failure changing link: firmware not found (%s, %d)", + ifindex, nl_geterror (nle), -nle); + return NM_PLATFORM_ERROR_NO_FIRMWARE; default: - if (complete_from_cache != complete_from_cache2) - _LOGD ("do-change-link: failure changing link %d: link does not exist in cache", ifindex); - else - _LOGE ("do-change-link: failure changing link %d: %s (%d)", ifindex, nl_geterror (nle), -nle); - return nle == -NLE_OBJ_NOTFOUND ? NM_PLATFORM_ERROR_NO_FIRMWARE : NM_PLATFORM_ERROR_UNSPECIFIED; + _LOGE ("do-change-link[%d]: failure changing link: netlink error (%s, %d)", + ifindex, nl_geterror (nle), -nle); + return NM_PLATFORM_ERROR_UNSPECIFIED; } /* FIXME: as we modify the link via a separate socket, the cache is not in @@ -2916,7 +3684,7 @@ link_add (NMPlatform *platform, size_t address_len, NMPlatformLink *out_link) { - auto_nl_object struct nl_object *l = NULL; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; if (type == NM_LINK_TYPE_BOND) { /* When the kernel loads the bond module, either via explicit modprobe @@ -2933,31 +3701,45 @@ link_add (NMPlatform *platform, _LOGD ("link: add link '%s' of type '%s' (%d)", name, nm_link_type_to_string (type), (int) type); - l = build_rtnl_link (0, name, type); + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + NLM_F_CREATE, + 0, + name, + 0); + if (!nlmsg) + return FALSE; - g_assert ( (address != NULL) ^ (address_len == 0) ); - if (address) { - auto_nl_addr struct nl_addr *nladdr = _nl_addr_build (AF_LLC, address, address_len); + if (address && address_len) + NLA_PUT (nlmsg, IFLA_ADDRESS, address_len, address); - rtnl_link_set_addr ((struct rtnl_link *) l, nladdr); - } + if (!_nl_msg_new_link_set_linkinfo (nlmsg, type)) + return FALSE; - return do_add_link_with_lookup (platform, name, (struct rtnl_link *) l, type, out_link); + return do_add_link_with_lookup (platform, type, name, nlmsg, out_link); +nla_put_failure: + g_return_val_if_reached (FALSE); } static gboolean link_delete (NMPlatform *platform, int ifindex) { + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - NMPObject obj_needle; + NMPObject obj_id; const NMPObject *obj; obj = nmp_cache_lookup_link (priv->cache, ifindex); if (!obj || !obj->_link.netlink.is_in_netlink) return FALSE; - nmp_object_stackinit_id_link (&obj_needle, ifindex); - return do_delete_object (platform, &obj_needle, NULL); + nlmsg = _nl_msg_new_link (RTM_DELLINK, + 0, + ifindex, + NULL, + 0); + + nmp_object_stackinit_id_link (&obj_id, ifindex); + return do_delete_object (platform, &obj_id, nlmsg); } static const char * @@ -3010,26 +3792,32 @@ link_refresh (NMPlatform *platform, int ifindex) static NMPlatformError link_change_flags (NMPlatform *platform, int ifindex, unsigned int flags, gboolean value) { - auto_nl_object struct rtnl_link *change = _nl_rtnl_link_alloc (ifindex, NULL); - const NMPObject *obj_cache; - char buf[256]; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + unsigned f; - obj_cache = cache_lookup_link (platform, ifindex); - if (!obj_cache) + if (!_lookup_cached_link_data (platform, ifindex, "flags", &f)) return NM_PLATFORM_ERROR_NOT_FOUND; - rtnl_link_set_flags (change, obj_cache->link.flags); if (value) - rtnl_link_set_flags (change, flags); + f |= flags; else - rtnl_link_unset_flags (change, flags); + f &= ~flags; - _LOGD ("link: change %d: flags %s '%s' (%d)", ifindex, + _LOGD ("link: change %d: flags: %s '%s' (0x%x); new %s (0x%x)", ifindex, value ? "set" : "unset", - rtnl_link_flags2str (flags, buf, sizeof (buf)), - flags); + nm_platform_link_flags2str (flags, NULL, 0), + flags, + nm_platform_link_flags2str (f, NULL, 0), + f); - return do_change_link (platform, change, FALSE); + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + 0, + ifindex, + NULL, + f); + if (!nlmsg) + return NM_PLATFORM_ERROR_UNSPECIFIED; + return do_change_link (platform, ifindex, nlmsg); } static gboolean @@ -3090,19 +3878,33 @@ link_get_udev_device (NMPlatform *platform, int ifindex) static gboolean link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enabled) { -#if HAVE_LIBNL_INET6_ADDR_GEN_MODE - if (_support_user_ipv6ll_get ()) { - auto_nl_object struct rtnl_link *nlo = _nl_rtnl_link_alloc (ifindex, NULL); - guint8 mode = enabled ? IN6_ADDR_GEN_MODE_NONE : IN6_ADDR_GEN_MODE_EUI64; - char buf[32]; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + guint8 mode = enabled ? NM_IN6_ADDR_GEN_MODE_NONE : NM_IN6_ADDR_GEN_MODE_EUI64; + unsigned flags; - rtnl_link_inet6_set_addr_gen_mode (nlo, mode); - _LOGD ("link: change %d: set IPv6 address generation mode to %s", - ifindex, rtnl_link_inet6_addrgenmode2str (mode, buf, sizeof (buf))); - return do_change_link (platform, nlo, TRUE) == NM_PLATFORM_ERROR_SUCCESS; + if (!_support_user_ipv6ll_get ()) { + _LOGD ("link: change %d: user-ipv6ll: not supported", ifindex); + return FALSE; } -#endif - return FALSE; + + if (!_lookup_cached_link_data (platform, ifindex, "user-ipv6ll", &flags)) + return FALSE; + + _LOGD ("link: change %d: user-ipv6ll: set IPv6 address generation mode to %s", + ifindex, + nm_platform_link_inet6_addrgenmode2str (mode, NULL, 0)); + + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + 0, + ifindex, + NULL, + flags); + if ( !nlmsg + || !_nl_msg_new_link_set_afspec (nlmsg, + mode)) + return FALSE; + + return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -3137,16 +3939,33 @@ link_supports_vlans (NMPlatform *platform, int ifindex) static gboolean link_set_address (NMPlatform *platform, int ifindex, gconstpointer address, size_t length) { - auto_nl_object struct rtnl_link *change = _nl_rtnl_link_alloc (ifindex, NULL); - auto_nl_addr struct nl_addr *nladdr = _nl_addr_build (AF_LLC, address, length); + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; gs_free char *mac = NULL; + unsigned flags; - rtnl_link_set_addr (change, nladdr); + if (!address || !length) + g_return_val_if_reached (FALSE); - _LOGD ("link: change %d: address %s (%lu bytes)", ifindex, + if (!_lookup_cached_link_data (platform, ifindex, "address", &flags)) + return FALSE; + + _LOGD ("link: change %d: address: %s (%lu bytes)", ifindex, (mac = nm_utils_hwaddr_ntoa (address, length)), (unsigned long) length); - return do_change_link (platform, change, TRUE) == NM_PLATFORM_ERROR_SUCCESS; + + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + 0, + ifindex, + NULL, + flags); + if (!nlmsg) + return FALSE; + + NLA_PUT (nlmsg, IFLA_ADDRESS, length, address); + + return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; +nla_put_failure: + g_return_val_if_reached (FALSE); } static gboolean @@ -3161,12 +3980,27 @@ link_get_permanent_address (NMPlatform *platform, static gboolean link_set_mtu (NMPlatform *platform, int ifindex, guint32 mtu) { - auto_nl_object struct rtnl_link *change = _nl_rtnl_link_alloc (ifindex, NULL); + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + unsigned flags; - rtnl_link_set_mtu (change, mtu); - _LOGD ("link: change %d: mtu %lu", ifindex, (unsigned long)mtu); + if (!_lookup_cached_link_data (platform, ifindex, "mtu", &flags)) + return FALSE; - return do_change_link (platform, change, TRUE) == NM_PLATFORM_ERROR_SUCCESS; + _LOGD ("link: change %d: mtu: %u", ifindex, (unsigned) mtu); + + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + 0, + ifindex, + NULL, + flags); + if (!nlmsg) + return FALSE; + + NLA_PUT_U32 (nlmsg, IFLA_MTU, mtu); + + return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; +nla_put_failure: + g_return_val_if_reached (FALSE); } static char * @@ -3220,78 +4054,237 @@ vlan_add (NMPlatform *platform, guint32 vlan_flags, NMPlatformLink *out_link) { - auto_nl_object struct rtnl_link *rtnllink = (struct rtnl_link *) build_rtnl_link (0, name, NM_LINK_TYPE_VLAN); - unsigned int kernel_flags; - unsigned int all_flags = NM_VLAN_FLAGS_ALL; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; G_STATIC_ASSERT (NM_VLAN_FLAG_REORDER_HEADERS == (guint32) VLAN_FLAG_REORDER_HDR); G_STATIC_ASSERT (NM_VLAN_FLAG_GVRP == (guint32) VLAN_FLAG_GVRP); G_STATIC_ASSERT (NM_VLAN_FLAG_LOOSE_BINDING == (guint32) VLAN_FLAG_LOOSE_BINDING); G_STATIC_ASSERT (NM_VLAN_FLAG_MVRP == (guint32) VLAN_FLAG_MVRP); - kernel_flags = vlan_flags & ((guint32) NM_VLAN_FLAGS_ALL); + vlan_flags &= (guint32) NM_VLAN_FLAGS_ALL; - rtnl_link_set_link (rtnllink, parent); - rtnl_link_vlan_set_id (rtnllink, vlan_id); - rtnl_link_vlan_unset_flags (rtnllink, all_flags); - rtnl_link_vlan_set_flags (rtnllink, kernel_flags); + _LOGD ("link: add vlan '%s', parent %d, vlan id %d, flags %X", + name, parent, vlan_id, (unsigned int) vlan_flags); - _LOGD ("link: add vlan '%s', parent %d, vlan id %d, flags %X (native: %X)", - name, parent, vlan_id, (unsigned int) vlan_flags, kernel_flags); + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + NLM_F_CREATE, + 0, + name, + 0); + if (!nlmsg) + return FALSE; - return do_add_link_with_lookup (platform, name, rtnllink, NM_LINK_TYPE_VLAN, out_link); -} + NLA_PUT_U32 (nlmsg, IFLA_LINK, parent); -static gboolean -vlan_get_info (NMPlatform *platform, int ifindex, int *parent, int *vlan_id) -{ - const NMPObject *obj = cache_lookup_link (platform, ifindex); - int p = 0, v = 0; + if (!_nl_msg_new_link_set_linkinfo_vlan (nlmsg, + vlan_id, + NM_VLAN_FLAGS_ALL, + vlan_flags, + NULL, + 0, + NULL, + 0)) + return FALSE; - if (obj) { - p = obj->link.parent; - v = obj->link.vlan_id; - } - if (parent) - *parent = p; - if (vlan_id) - *vlan_id = v; - return !!obj; + return do_add_link_with_lookup (platform, NM_LINK_TYPE_VLAN, name, nlmsg, out_link); +nla_put_failure: + g_return_val_if_reached (FALSE); } -static gboolean -vlan_set_ingress_map (NMPlatform *platform, int ifindex, int from, int to) -{ - auto_nl_object struct rtnl_link *change = (struct rtnl_link *) build_rtnl_link (ifindex, NULL, NM_LINK_TYPE_VLAN); +static void +_vlan_change_vlan_qos_mapping_create (gboolean is_ingress_map, + gboolean reset_all, + const NMVlanQosMapping *current_map, + guint current_n_map, + const NMVlanQosMapping *set_map, + guint set_n_map, + NMVlanQosMapping **out_map, + guint *out_n_map) +{ + NMVlanQosMapping *map; + guint i, j, len; + const guint INGRESS_RANGE_LEN = 8; + + nm_assert (out_map && !*out_map); + nm_assert (out_n_map && !*out_n_map); + + if (!reset_all) + current_n_map = 0; + else if (is_ingress_map) + current_n_map = INGRESS_RANGE_LEN; + + len = current_n_map + set_n_map; + + if (len == 0) + return; - rtnl_link_vlan_set_ingress_map (change, from, to); + map = g_new (NMVlanQosMapping, len); - _LOGD ("link: change %d: vlan ingress map %d -> %d", ifindex, from, to); + if (current_n_map) { + if (is_ingress_map) { + /* For the ingress-map, there are only 8 entries (0 to 7). + * When the user requests to reset all entires, we don't actually + * need the cached entries, we can just explicitly clear all possible + * ones. + * + * That makes only a real difference in case our cache is out-of-date. + * + * For the egress map we cannot do that, because there are far too + * many. There we can only clear the entries that we know about. */ + for (i = 0; i < INGRESS_RANGE_LEN; i++) { + map[i].from = i; + map[i].to = 0; + } + } else { + for (i = 0; i < current_n_map; i++) { + map[i].from = current_map[i].from; + map[i].to = 0; + } + } + } + if (set_n_map) + memcpy (&map[current_n_map], set_map, sizeof (*set_map) * set_n_map); + + g_qsort_with_data (map, + len, + sizeof (*map), + _vlan_qos_mapping_cmp_from, + NULL); + + for (i = 0, j = 0; i < len; i++) { + if ( ( is_ingress_map && !VLAN_XGRESS_PRIO_VALID (map[i].from)) + || (!is_ingress_map && !VLAN_XGRESS_PRIO_VALID (map[i].to))) + continue; + if ( j > 0 + && map[j - 1].from == map[i].from) + map[j - 1] = map[i]; + else + map[j++] = map[i]; + } - return do_change_link (platform, change, TRUE) == NM_PLATFORM_ERROR_SUCCESS; + *out_map = map; + *out_n_map = j; } static gboolean -vlan_set_egress_map (NMPlatform *platform, int ifindex, int from, int to) +link_vlan_change (NMPlatform *platform, + 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) { - auto_nl_object struct rtnl_link *change = (struct rtnl_link *) build_rtnl_link (ifindex, NULL, NM_LINK_TYPE_VLAN); - - rtnl_link_vlan_set_egress_map (change, from, to); + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + const NMPObject *obj_cache; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + unsigned flags; + const NMPObjectLnkVlan *lnk; + guint new_n_ingress_map = 0; + guint new_n_egress_map = 0; + gs_free NMVlanQosMapping *new_ingress_map = NULL; + gs_free NMVlanQosMapping *new_egress_map = NULL; + char s_flags[64]; + char s_ingress[256]; + char s_egress[256]; + + obj_cache = nmp_cache_lookup_link (priv->cache, ifindex); + if ( !obj_cache + || !obj_cache->_link.netlink.is_in_netlink) { + _LOGD ("link: change %d: %s: link does not exist", ifindex, "vlan"); + return FALSE; + } - _LOGD ("link: change %d: vlan egress map %d -> %d", ifindex, from, to); + lnk = obj_cache->_link.netlink.lnk ? &obj_cache->_link.netlink.lnk->_lnk_vlan : NULL; + flags = obj_cache->link.flags; + + flags_set &= flags_mask; + + _vlan_change_vlan_qos_mapping_create (TRUE, + ingress_reset_all, + lnk ? lnk->ingress_qos_map : NULL, + lnk ? lnk->n_ingress_qos_map : 0, + ingress_map, + n_ingress_map, + &new_ingress_map, + &new_n_ingress_map); + + _vlan_change_vlan_qos_mapping_create (FALSE, + egress_reset_all, + lnk ? lnk->egress_qos_map : NULL, + lnk ? lnk->n_egress_qos_map : 0, + egress_map, + n_egress_map, + &new_egress_map, + &new_n_egress_map); + + _LOGD ("link: change %d: vlan:%s%s%s", + ifindex, + flags_mask + ? nm_sprintf_buf (s_flags, " flags 0x%x/0x%x", (unsigned) flags_set, (unsigned) flags_mask) + : "", + new_n_ingress_map + ? nm_platform_vlan_qos_mapping_to_string (" ingress-qos-map", + new_ingress_map, + new_n_ingress_map, + s_ingress, + sizeof (s_ingress)) + : "", + new_n_egress_map + ? nm_platform_vlan_qos_mapping_to_string (" egress-qos-map", + new_egress_map, + new_n_egress_map, + s_egress, + sizeof (s_egress)) + : ""); + + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + 0, + ifindex, + NULL, + flags); + if ( !nlmsg + || !_nl_msg_new_link_set_linkinfo_vlan (nlmsg, + -1, + flags_mask, + flags_set, + new_ingress_map, + new_n_ingress_map, + new_egress_map, + new_n_egress_map)) + return FALSE; - return do_change_link (platform, change, TRUE) == NM_PLATFORM_ERROR_SUCCESS; + return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean link_enslave (NMPlatform *platform, int master, int slave) { - auto_nl_object struct rtnl_link *change = _nl_rtnl_link_alloc (slave, NULL); + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + unsigned flags; + int ifindex = slave; + + if (!_lookup_cached_link_data (platform, ifindex, "enslave", &flags)) + return FALSE; + + _LOGD ("link: change %d: enslave: master %d", slave, master); - rtnl_link_set_master (change, master); - _LOGD ("link: change %d: enslave to master %d", slave, master); + nlmsg = _nl_msg_new_link (RTM_NEWLINK, + 0, + ifindex, + NULL, + flags); + if (!nlmsg) + return FALSE; + + NLA_PUT_U32 (nlmsg, IFLA_MASTER, master); - return do_change_link (platform, change, TRUE) == NM_PLATFORM_ERROR_SUCCESS; + return do_change_link (platform, ifindex, nlmsg) == NM_PLATFORM_ERROR_SUCCESS; +nla_put_failure: + g_return_val_if_reached (FALSE); } static gboolean @@ -3415,463 +4408,6 @@ infiniband_partition_add (NMPlatform *platform, int parent, int p_key, NMPlatfor return !!obj; } -typedef struct { - int p_key; - const char *mode; -} IpoibInfo; - -/* IFLA_IPOIB_* were introduced in the 3.7 kernel, but the kernel headers - * we're building against might not have those properties even though the - * running kernel might. - */ -#define IFLA_IPOIB_UNSPEC 0 -#define IFLA_IPOIB_PKEY 1 -#define IFLA_IPOIB_MODE 2 -#define IFLA_IPOIB_UMCAST 3 -#undef IFLA_IPOIB_MAX -#define IFLA_IPOIB_MAX IFLA_IPOIB_UMCAST - -#define IPOIB_MODE_DATAGRAM 0 /* using unreliable datagram QPs */ -#define IPOIB_MODE_CONNECTED 1 /* using connected QPs */ - -static const struct nla_policy infiniband_info_policy[IFLA_IPOIB_MAX + 1] = { - [IFLA_IPOIB_PKEY] = { .type = NLA_U16 }, - [IFLA_IPOIB_MODE] = { .type = NLA_U16 }, - [IFLA_IPOIB_UMCAST] = { .type = NLA_U16 }, -}; - -static int -infiniband_info_data_parser (struct nlattr *info_data, gpointer parser_data) -{ - IpoibInfo *info = parser_data; - struct nlattr *tb[IFLA_MACVLAN_MAX + 1]; - int err; - - err = nla_parse_nested (tb, IFLA_IPOIB_MAX, info_data, - (struct nla_policy *) infiniband_info_policy); - if (err < 0) - return err; - if (!tb[IFLA_IPOIB_PKEY] || !tb[IFLA_IPOIB_MODE]) - return -EINVAL; - - info->p_key = nla_get_u16 (tb[IFLA_IPOIB_PKEY]); - - switch (nla_get_u16 (tb[IFLA_IPOIB_MODE])) { - case IPOIB_MODE_DATAGRAM: - info->mode = "datagram"; - break; - case IPOIB_MODE_CONNECTED: - info->mode = "connected"; - break; - default: - return -NLE_PARSE_ERR; - } - - return 0; -} - -static gboolean -infiniband_get_info (NMPlatform *platform, int ifindex, int *parent, int *p_key, const char **mode) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - const NMPObject *obj; - IpoibInfo info = { -1, NULL }; - - obj = cache_lookup_link (platform, ifindex); - if (!obj) - return FALSE; - - if (parent) - *parent = obj->link.parent; - - if (_nl_link_parse_info_data (priv->nlh, - ifindex, - infiniband_info_data_parser, - &info) != 0) { - const char *iface = obj->link.name; - char *path, *contents = NULL; - - /* Fall back to reading sysfs */ - path = g_strdup_printf ("/sys/class/net/%s/mode", ASSERT_VALID_PATH_COMPONENT (iface)); - contents = nm_platform_sysctl_get (platform, path); - g_free (path); - if (!contents) - return FALSE; - - if (strstr (contents, "datagram")) - info.mode = "datagram"; - else if (strstr (contents, "connected")) - info.mode = "connected"; - g_free (contents); - - path = g_strdup_printf ("/sys/class/net/%s/pkey", ASSERT_VALID_PATH_COMPONENT (iface)); - contents = nm_platform_sysctl_get (platform, path); - g_free (path); - if (!contents) - return FALSE; - - info.p_key = (int) _nm_utils_ascii_str_to_int64 (contents, 16, 0, 0xFFFF, -1); - g_free (contents); - - if (info.p_key < 0) - return FALSE; - } - - if (p_key) - *p_key = info.p_key; - if (mode) - *mode = info.mode; - return TRUE; -} - -/******************************************************************/ - -static gboolean -veth_get_properties (NMPlatform *platform, int ifindex, NMPlatformVethProperties *props) -{ - const char *ifname; - int peer_ifindex; - - ifname = nm_platform_link_get_name (platform, ifindex); - if (!ifname) - return FALSE; - - peer_ifindex = nmp_utils_ethtool_get_peer_ifindex (ifname); - if (peer_ifindex <= 0) - return FALSE; - - props->peer = peer_ifindex; - return TRUE; -} - -/******************************************************************/ - -static gboolean -tun_get_properties_ifname (NMPlatform *platform, const char *ifname, NMPlatformTunProperties *props) -{ - char *path, *val; - gboolean success = TRUE; - - g_return_val_if_fail (props, FALSE); - - memset (props, 0, sizeof (*props)); - props->owner = -1; - props->group = -1; - - if (!ifname || !nm_utils_iface_valid_name (ifname)) - return FALSE; - ifname = ASSERT_VALID_PATH_COMPONENT (ifname); - - path = g_strdup_printf ("/sys/class/net/%s/owner", ifname); - val = nm_platform_sysctl_get (platform, path); - g_free (path); - if (val) { - props->owner = _nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1); - if (errno) - success = FALSE; - g_free (val); - } else - success = FALSE; - - path = g_strdup_printf ("/sys/class/net/%s/group", ifname); - val = nm_platform_sysctl_get (platform, path); - g_free (path); - if (val) { - props->group = _nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1); - if (errno) - success = FALSE; - g_free (val); - } else - success = FALSE; - - path = g_strdup_printf ("/sys/class/net/%s/tun_flags", ifname); - val = nm_platform_sysctl_get (platform, path); - g_free (path); - if (val) { - gint64 flags; - - flags = _nm_utils_ascii_str_to_int64 (val, 16, 0, G_MAXINT64, 0); - if (!errno) { -#ifndef IFF_MULTI_QUEUE - const int IFF_MULTI_QUEUE = 0x0100; -#endif - 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 & IFF_MULTI_QUEUE); - } else - success = FALSE; - g_free (val); - } else - success = FALSE; - - return success; -} - -static gboolean -tun_get_properties (NMPlatform *platform, int ifindex, NMPlatformTunProperties *props) -{ - return tun_get_properties_ifname (platform, nm_platform_link_get_name (platform, ifindex), props); -} - -/******************************************************************/ - -static const struct nla_policy macvlan_info_policy[IFLA_MACVLAN_MAX + 1] = { - [IFLA_MACVLAN_MODE] = { .type = NLA_U32 }, -#ifdef MACVLAN_FLAG_NOPROMISC - [IFLA_MACVLAN_FLAGS] = { .type = NLA_U16 }, -#endif -}; - -static int -macvlan_info_data_parser (struct nlattr *info_data, gpointer parser_data) -{ - NMPlatformMacvlanProperties *props = parser_data; - struct nlattr *tb[IFLA_MACVLAN_MAX + 1]; - int err; - - err = nla_parse_nested (tb, IFLA_MACVLAN_MAX, info_data, - (struct nla_policy *) macvlan_info_policy); - if (err < 0) - return err; - - switch (nla_get_u32 (tb[IFLA_MACVLAN_MODE])) { - case MACVLAN_MODE_PRIVATE: - props->mode = "private"; - break; - case MACVLAN_MODE_VEPA: - props->mode = "vepa"; - break; - case MACVLAN_MODE_BRIDGE: - props->mode = "bridge"; - break; - case MACVLAN_MODE_PASSTHRU: - props->mode = "passthru"; - break; - default: - return -NLE_PARSE_ERR; - } - -#ifdef MACVLAN_FLAG_NOPROMISC - props->no_promisc = !!(nla_get_u16 (tb[IFLA_MACVLAN_FLAGS]) & MACVLAN_FLAG_NOPROMISC); -#else - props->no_promisc = FALSE; -#endif - - return 0; -} - -static gboolean -macvlan_get_properties (NMPlatform *platform, int ifindex, NMPlatformMacvlanProperties *props) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - int err; - const NMPObject *obj; - - obj = cache_lookup_link (platform, ifindex); - if (!obj) - return FALSE; - - props->parent_ifindex = obj->link.parent; - - err = _nl_link_parse_info_data (priv->nlh, ifindex, - macvlan_info_data_parser, props); - if (err != 0) { - _LOGW ("(%s) could not read properties: %s", - obj->link.name, nl_geterror (err)); - } - return (err == 0); -} - -/******************************************************************/ - -/* The installed kernel headers might not have VXLAN stuff at all, or - * they might have the original properties, but not PORT, GROUP6, or LOCAL6. - * So until we depend on kernel >= 3.11, we just ignore the actual enum - * in if_link.h and define the values ourselves. - */ -#define IFLA_VXLAN_UNSPEC 0 -#define IFLA_VXLAN_ID 1 -#define IFLA_VXLAN_GROUP 2 -#define IFLA_VXLAN_LINK 3 -#define IFLA_VXLAN_LOCAL 4 -#define IFLA_VXLAN_TTL 5 -#define IFLA_VXLAN_TOS 6 -#define IFLA_VXLAN_LEARNING 7 -#define IFLA_VXLAN_AGEING 8 -#define IFLA_VXLAN_LIMIT 9 -#define IFLA_VXLAN_PORT_RANGE 10 -#define IFLA_VXLAN_PROXY 11 -#define IFLA_VXLAN_RSC 12 -#define IFLA_VXLAN_L2MISS 13 -#define IFLA_VXLAN_L3MISS 14 -#define IFLA_VXLAN_PORT 15 -#define IFLA_VXLAN_GROUP6 16 -#define IFLA_VXLAN_LOCAL6 17 -#undef IFLA_VXLAN_MAX -#define IFLA_VXLAN_MAX IFLA_VXLAN_LOCAL6 - -/* older kernel header might not contain 'struct ifla_vxlan_port_range'. - * Redefine it. */ -struct nm_ifla_vxlan_port_range { - guint16 low; - guint16 high; -}; - -static const struct nla_policy vxlan_info_policy[IFLA_VXLAN_MAX + 1] = { - [IFLA_VXLAN_ID] = { .type = NLA_U32 }, - [IFLA_VXLAN_GROUP] = { .type = NLA_U32 }, - [IFLA_VXLAN_GROUP6] = { .type = NLA_UNSPEC, - .minlen = sizeof (struct in6_addr) }, - [IFLA_VXLAN_LINK] = { .type = NLA_U32 }, - [IFLA_VXLAN_LOCAL] = { .type = NLA_U32 }, - [IFLA_VXLAN_LOCAL6] = { .type = NLA_UNSPEC, - .minlen = sizeof (struct in6_addr) }, - [IFLA_VXLAN_TOS] = { .type = NLA_U8 }, - [IFLA_VXLAN_TTL] = { .type = NLA_U8 }, - [IFLA_VXLAN_LEARNING] = { .type = NLA_U8 }, - [IFLA_VXLAN_AGEING] = { .type = NLA_U32 }, - [IFLA_VXLAN_LIMIT] = { .type = NLA_U32 }, - [IFLA_VXLAN_PORT_RANGE] = { .type = NLA_UNSPEC, - .minlen = sizeof (struct nm_ifla_vxlan_port_range) }, - [IFLA_VXLAN_PROXY] = { .type = NLA_U8 }, - [IFLA_VXLAN_RSC] = { .type = NLA_U8 }, - [IFLA_VXLAN_L2MISS] = { .type = NLA_U8 }, - [IFLA_VXLAN_L3MISS] = { .type = NLA_U8 }, - [IFLA_VXLAN_PORT] = { .type = NLA_U16 }, -}; - -static int -vxlan_info_data_parser (struct nlattr *info_data, gpointer parser_data) -{ - NMPlatformVxlanProperties *props = parser_data; - struct nlattr *tb[IFLA_VXLAN_MAX + 1]; - struct nm_ifla_vxlan_port_range *range; - int err; - - err = nla_parse_nested (tb, IFLA_VXLAN_MAX, info_data, - (struct nla_policy *) vxlan_info_policy); - if (err < 0) - return err; - - memset (props, 0, sizeof (*props)); - - if (tb[IFLA_VXLAN_LINK]) - props->parent_ifindex = nla_get_u32 (tb[IFLA_VXLAN_LINK]); - if (tb[IFLA_VXLAN_ID]) - props->id = nla_get_u32 (tb[IFLA_VXLAN_ID]); - if (tb[IFLA_VXLAN_GROUP]) - props->group = nla_get_u32 (tb[IFLA_VXLAN_GROUP]); - if (tb[IFLA_VXLAN_LOCAL]) - props->local = nla_get_u32 (tb[IFLA_VXLAN_LOCAL]); - if (tb[IFLA_VXLAN_GROUP6]) - memcpy (&props->group6, nla_data (tb[IFLA_VXLAN_GROUP6]), sizeof (props->group6)); - if (tb[IFLA_VXLAN_LOCAL6]) - memcpy (&props->local6, nla_data (tb[IFLA_VXLAN_LOCAL6]), sizeof (props->local6)); - - if (tb[IFLA_VXLAN_AGEING]) - props->ageing = nla_get_u32 (tb[IFLA_VXLAN_AGEING]); - if (tb[IFLA_VXLAN_LIMIT]) - props->limit = nla_get_u32 (tb[IFLA_VXLAN_LIMIT]); - if (tb[IFLA_VXLAN_TOS]) - props->tos = nla_get_u8 (tb[IFLA_VXLAN_TOS]); - if (tb[IFLA_VXLAN_TTL]) - props->ttl = nla_get_u8 (tb[IFLA_VXLAN_TTL]); - - if (tb[IFLA_VXLAN_PORT]) - props->dst_port = nla_get_u16 (tb[IFLA_VXLAN_PORT]); - - if (tb[IFLA_VXLAN_PORT_RANGE]) { - range = nla_data (tb[IFLA_VXLAN_PORT_RANGE]); - props->src_port_min = range->low; - props->src_port_max = range->high; - } - - if (tb[IFLA_VXLAN_LEARNING]) - props->learning = !!nla_get_u8 (tb[IFLA_VXLAN_LEARNING]); - if (tb[IFLA_VXLAN_PROXY]) - props->proxy = !!nla_get_u8 (tb[IFLA_VXLAN_PROXY]); - if (tb[IFLA_VXLAN_RSC]) - props->rsc = !!nla_get_u8 (tb[IFLA_VXLAN_RSC]); - if (tb[IFLA_VXLAN_L2MISS]) - props->l2miss = !!nla_get_u8 (tb[IFLA_VXLAN_L2MISS]); - if (tb[IFLA_VXLAN_L3MISS]) - props->l3miss = !!nla_get_u8 (tb[IFLA_VXLAN_L3MISS]); - - return 0; -} - -static gboolean -vxlan_get_properties (NMPlatform *platform, int ifindex, NMPlatformVxlanProperties *props) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - int err; - - err = _nl_link_parse_info_data (priv->nlh, ifindex, - vxlan_info_data_parser, props); - if (err != 0) { - _LOGW ("(%s) could not read vxlan properties: %s", - nm_platform_link_get_name (platform, ifindex), nl_geterror (err)); - } - return (err == 0); -} - -/******************************************************************/ - -static const struct nla_policy gre_info_policy[IFLA_GRE_MAX + 1] = { - [IFLA_GRE_LINK] = { .type = NLA_U32 }, - [IFLA_GRE_IFLAGS] = { .type = NLA_U16 }, - [IFLA_GRE_OFLAGS] = { .type = NLA_U16 }, - [IFLA_GRE_IKEY] = { .type = NLA_U32 }, - [IFLA_GRE_OKEY] = { .type = NLA_U32 }, - [IFLA_GRE_LOCAL] = { .type = NLA_U32 }, - [IFLA_GRE_REMOTE] = { .type = NLA_U32 }, - [IFLA_GRE_TTL] = { .type = NLA_U8 }, - [IFLA_GRE_TOS] = { .type = NLA_U8 }, - [IFLA_GRE_PMTUDISC] = { .type = NLA_U8 }, -}; - -static int -gre_info_data_parser (struct nlattr *info_data, gpointer parser_data) -{ - NMPlatformGreProperties *props = parser_data; - struct nlattr *tb[IFLA_GRE_MAX + 1]; - int err; - - err = nla_parse_nested (tb, IFLA_GRE_MAX, info_data, - (struct nla_policy *) gre_info_policy); - if (err < 0) - return err; - - props->parent_ifindex = tb[IFLA_GRE_LINK] ? nla_get_u32 (tb[IFLA_GRE_LINK]) : 0; - props->input_flags = nla_get_u16 (tb[IFLA_GRE_IFLAGS]); - props->output_flags = nla_get_u16 (tb[IFLA_GRE_OFLAGS]); - props->input_key = (props->input_flags & GRE_KEY) ? nla_get_u32 (tb[IFLA_GRE_IKEY]) : 0; - props->output_key = (props->output_flags & GRE_KEY) ? nla_get_u32 (tb[IFLA_GRE_OKEY]) : 0; - props->local = nla_get_u32 (tb[IFLA_GRE_LOCAL]); - props->remote = nla_get_u32 (tb[IFLA_GRE_REMOTE]); - props->tos = nla_get_u8 (tb[IFLA_GRE_TOS]); - props->ttl = nla_get_u8 (tb[IFLA_GRE_TTL]); - props->path_mtu_discovery = !!nla_get_u8 (tb[IFLA_GRE_PMTUDISC]); - - return 0; -} - -static gboolean -gre_get_properties (NMPlatform *platform, int ifindex, NMPlatformGreProperties *props) -{ - NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - int err; - - err = _nl_link_parse_info_data (priv->nlh, ifindex, - gre_info_data_parser, props); - if (err != 0) { - _LOGW ("(%s) could not read gre properties: %s", - nm_platform_link_get_name (platform, ifindex), nl_geterror (err)); - } - return (err == 0); -} - /******************************************************************/ static WifiData * @@ -4104,137 +4640,6 @@ ip6_address_get_all (NMPlatform *platform, int ifindex) return ipx_address_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP6_ADDRESS); } -#define IPV4LL_NETWORK (htonl (0xA9FE0000L)) -#define IPV4LL_NETMASK (htonl (0xFFFF0000L)) - -static gboolean -ip4_is_link_local (const struct in_addr *src) -{ - return (src->s_addr & IPV4LL_NETMASK) == IPV4LL_NETWORK; -} - -static struct nl_object * -build_rtnl_addr (NMPlatform *platform, - int family, - int ifindex, - gconstpointer addr, - gconstpointer peer_addr, - int plen, - guint32 lifetime, - guint32 preferred, - guint flags, - const char *label) -{ - auto_nl_object struct rtnl_addr *rtnladdr = _nl_rtnl_addr_alloc (ifindex); - struct rtnl_addr *rtnladdr_copy; - int addrlen = family == AF_INET ? sizeof (in_addr_t) : sizeof (struct in6_addr); - auto_nl_addr struct nl_addr *nladdr = _nl_addr_build (family, addr, addrlen); - int nle; - - /* IP address */ - nle = rtnl_addr_set_local (rtnladdr, nladdr); - if (nle) { - _LOGE ("build_rtnl_addr(): rtnl_addr_set_local failed with %s (%d)", nl_geterror (nle), nle); - return NULL; - } - - /* Tighten scope (IPv4 only) */ - if (family == AF_INET && ip4_is_link_local (addr)) - rtnl_addr_set_scope (rtnladdr, RT_SCOPE_LINK); - - /* IPv4 Broadcast address */ - if (family == AF_INET) { - in_addr_t bcast; - auto_nl_addr struct nl_addr *bcaddr = NULL; - - bcast = *((in_addr_t *) addr) | ~nm_utils_ip4_prefix_to_netmask (plen); - bcaddr = _nl_addr_build (family, &bcast, addrlen); - g_assert (bcaddr); - rtnl_addr_set_broadcast (rtnladdr, bcaddr); - } - - /* Peer/point-to-point address */ - if (peer_addr) { - auto_nl_addr struct nl_addr *nlpeer = _nl_addr_build (family, peer_addr, addrlen); - - nle = rtnl_addr_set_peer (rtnladdr, nlpeer); - if (nle && nle != -NLE_AF_NOSUPPORT) { - /* IPv6 doesn't support peer addresses yet */ - _LOGE ("build_rtnl_addr(): rtnl_addr_set_peer failed with %s (%d)", nl_geterror (nle), nle); - return NULL; - } - } - - _nl_rtnl_addr_set_prefixlen (rtnladdr, plen); - if ( (lifetime != 0 && lifetime != NM_PLATFORM_LIFETIME_PERMANENT) - || (preferred != 0 && preferred != NM_PLATFORM_LIFETIME_PERMANENT)) { - /* note that here we set the relative timestamps (ticking from *now*). */ - rtnl_addr_set_valid_lifetime (rtnladdr, lifetime); - rtnl_addr_set_preferred_lifetime (rtnladdr, preferred); - } - if (flags) { - if ((flags & ~0xFF) && !_support_kernel_extended_ifa_flags_get ()) { - /* Older kernels don't accept unknown netlink attributes. - * - * With commit libnl commit 5206c050504f8676a24854519b9c351470fb7cc6, libnl will only set - * the extended address flags attribute IFA_FLAGS when necessary (> 8 bit). But it's up to - * us not to shove those extended flags on to older kernels. - * - * Just silently clear them. The kernel should ignore those unknown flags anyway. */ - flags &= 0xFF; - } - rtnl_addr_set_flags (rtnladdr, flags); - } - if (label && *label) - rtnl_addr_set_label (rtnladdr, label); - - rtnladdr_copy = rtnladdr; - rtnladdr = NULL; - return (struct nl_object *) rtnladdr_copy; -} - -struct nl_object * -_nmp_vt_cmd_plobj_to_nl_ip4_address (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only) -{ - const NMPlatformIP4Address *obj = (const NMPlatformIP4Address *) _obj; - guint32 lifetime, preferred; - - nmp_utils_lifetime_get (obj->timestamp, obj->lifetime, obj->preferred, - 0, 0, &lifetime, &preferred); - - return build_rtnl_addr (platform, - AF_INET, - obj->ifindex, - &obj->address, - obj->peer_address ? &obj->peer_address : NULL, - obj->plen, - lifetime, - preferred, - 0, - obj->label[0] ? obj->label : NULL); -} - -struct nl_object * -_nmp_vt_cmd_plobj_to_nl_ip6_address (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only) -{ - const NMPlatformIP6Address *obj = (const NMPlatformIP6Address *) _obj; - guint32 lifetime, preferred; - - nmp_utils_lifetime_get (obj->timestamp, obj->lifetime, obj->preferred, - 0, 0, &lifetime, &preferred); - - return build_rtnl_addr (platform, - AF_INET6, - obj->ifindex, - &obj->address, - !IN6_IS_ADDR_UNSPECIFIED (&obj->peer_address) ? &obj->peer_address : NULL, - obj->plen, - lifetime, - preferred, - 0, - NULL); -} - static gboolean ip4_address_add (NMPlatform *platform, int ifindex, @@ -4245,16 +4650,24 @@ ip4_address_add (NMPlatform *platform, guint32 preferred, const char *label) { - NMPObject obj_needle; - auto_nl_object struct nl_object *nlo = NULL; + NMPObject obj_id; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - nlo = build_rtnl_addr (platform, AF_INET, ifindex, &addr, - peer_addr ? &peer_addr : NULL, - plen, lifetime, preferred, 0, - label); - return do_add_addrroute (platform, - nmp_object_stackinit_id_ip4_address (&obj_needle, ifindex, addr, plen, peer_addr), - nlo); + nlmsg = _nl_msg_new_address (RTM_NEWADDR, + NLM_F_CREATE | NLM_F_REPLACE, + AF_INET, + ifindex, + &addr, + plen, + &peer_addr, + 0, + ip4_address_is_link_local (addr) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE, + lifetime, + preferred, + label); + + nmp_object_stackinit_id_ip4_address (&obj_id, ifindex, addr, plen, peer_addr); + return do_add_addrroute (platform, &obj_id, nlmsg); } static gboolean @@ -4267,44 +4680,80 @@ ip6_address_add (NMPlatform *platform, guint32 preferred, guint flags) { - NMPObject obj_needle; - auto_nl_object struct nl_object *nlo = NULL; + NMPObject obj_id; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + + nlmsg = _nl_msg_new_address (RTM_NEWADDR, + NLM_F_CREATE | NLM_F_REPLACE, + AF_INET6, + ifindex, + &addr, + plen, + &peer_addr, + flags, + RT_SCOPE_UNIVERSE, + lifetime, + preferred, + NULL); - nlo = build_rtnl_addr (platform, AF_INET6, ifindex, &addr, - IN6_IS_ADDR_UNSPECIFIED (&peer_addr) ? NULL : &peer_addr, - plen, lifetime, preferred, flags, - NULL); - return do_add_addrroute (platform, - nmp_object_stackinit_id_ip6_address (&obj_needle, ifindex, &addr, plen), - nlo); + nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr, plen); + return do_add_addrroute (platform, &obj_id, nlmsg); } static gboolean ip4_address_delete (NMPlatform *platform, int ifindex, in_addr_t addr, int plen, in_addr_t peer_address) { - NMPObject obj_needle; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + NMPObject obj_id; - nmp_object_stackinit_id_ip4_address (&obj_needle, ifindex, addr, plen, peer_address); - return do_delete_object (platform, &obj_needle, NULL); + nlmsg = _nl_msg_new_address (RTM_DELADDR, + 0, + AF_INET, + ifindex, + &addr, + plen, + &peer_address, + 0, + RT_SCOPE_NOWHERE, + NM_PLATFORM_LIFETIME_PERMANENT, + NM_PLATFORM_LIFETIME_PERMANENT, + NULL); + + nmp_object_stackinit_id_ip4_address (&obj_id, ifindex, addr, plen, peer_address); + return do_delete_object (platform, &obj_id, nlmsg); } static gboolean ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen) { - NMPObject obj_needle; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + NMPObject obj_id; + + nlmsg = _nl_msg_new_address (RTM_DELADDR, + 0, + AF_INET6, + ifindex, + &addr, + plen, + NULL, + 0, + RT_SCOPE_NOWHERE, + NM_PLATFORM_LIFETIME_PERMANENT, + NM_PLATFORM_LIFETIME_PERMANENT, + NULL); - nmp_object_stackinit_id_ip6_address (&obj_needle, ifindex, &addr, plen); - return do_delete_object (platform, &obj_needle, NULL); + nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr, plen); + return do_delete_object (platform, &obj_id, nlmsg); } static const NMPlatformIP4Address * ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, int plen, in_addr_t peer_address) { - NMPObject obj_needle; + NMPObject obj_id; const NMPObject *obj; - nmp_object_stackinit_id_ip4_address (&obj_needle, ifindex, addr, plen, peer_address); - obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_needle); + nmp_object_stackinit_id_ip4_address (&obj_id, ifindex, addr, plen, peer_address); + obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_id); if (nmp_object_is_visible (obj)) return &obj->ip4_address; return NULL; @@ -4313,11 +4762,11 @@ ip4_address_get (NMPlatform *platform, int ifindex, in_addr_t addr, int plen, in static const NMPlatformIP6Address * ip6_address_get (NMPlatform *platform, int ifindex, struct in6_addr addr, int plen) { - NMPObject obj_needle; + NMPObject obj_id; const NMPObject *obj; - nmp_object_stackinit_id_ip6_address (&obj_needle, ifindex, &addr, plen); - obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_needle); + nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr, plen); + obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_id); if (nmp_object_is_visible (obj)) return &obj->ip6_address; return NULL; @@ -4376,113 +4825,29 @@ ip6_route_get_all (NMPlatform *platform, int ifindex, NMPlatformGetRouteFlags fl return ipx_route_get_all (platform, ifindex, NMP_OBJECT_TYPE_IP6_ROUTE, flags); } -static void -clear_host_address (int family, const void *network, int plen, void *dst) -{ - g_return_if_fail (plen == (guint8)plen); - g_return_if_fail (network); - - switch (family) { - case AF_INET: - *((in_addr_t *) dst) = nm_utils_ip4_address_clear_host_address (*((in_addr_t *) network), plen); - break; - case AF_INET6: - nm_utils_ip6_address_clear_host_address ((struct in6_addr *) dst, (const struct in6_addr *) network, plen); - break; - default: - g_assert_not_reached (); - } -} - -static struct nl_object * -build_rtnl_route (int family, int ifindex, NMIPConfigSource source, - gconstpointer network, int plen, gconstpointer gateway, - gconstpointer pref_src, - guint32 metric, guint32 mss) -{ - guint32 network_clean[4]; - struct rtnl_route *rtnlroute; - struct rtnl_nexthop *nexthop; - int addrlen = (family == AF_INET) ? sizeof (in_addr_t) : sizeof (struct in6_addr); - /* Workaround a libnl bug by using zero destination address length for default routes */ - auto_nl_addr struct nl_addr *dst = NULL; - auto_nl_addr struct nl_addr *gw = gateway ? _nl_addr_build (family, gateway, addrlen) : NULL; - auto_nl_addr struct nl_addr *pref_src_nl = pref_src ? _nl_addr_build (family, pref_src, addrlen) : NULL; - - /* There seem to be problems adding a route with non-zero host identifier. - * Adding IPv6 routes is simply ignored, without error message. - * In the IPv4 case, we got an error. Thus, we have to make sure, that - * the address is sane. */ - clear_host_address (family, network, plen, network_clean); - dst = _nl_addr_build (family, network_clean, plen ? addrlen : 0); - nl_addr_set_prefixlen (dst, plen); - - rtnlroute = _nl_rtnl_route_alloc (); - rtnl_route_set_table (rtnlroute, RT_TABLE_MAIN); - rtnl_route_set_tos (rtnlroute, 0); - rtnl_route_set_dst (rtnlroute, dst); - rtnl_route_set_priority (rtnlroute, metric); - rtnl_route_set_family (rtnlroute, family); - rtnl_route_set_protocol (rtnlroute, _nm_ip_config_source_to_rtprot (source)); - - nexthop = _nl_rtnl_route_nh_alloc (); - rtnl_route_nh_set_ifindex (nexthop, ifindex); - if (gw && !nl_addr_iszero (gw)) - rtnl_route_nh_set_gateway (nexthop, gw); - if (pref_src_nl) - rtnl_route_set_pref_src (rtnlroute, pref_src_nl); - rtnl_route_add_nexthop (rtnlroute, nexthop); - - if (mss > 0) - rtnl_route_set_metric (rtnlroute, RTAX_ADVMSS, mss); - - return (struct nl_object *) rtnlroute; -} - -struct nl_object * -_nmp_vt_cmd_plobj_to_nl_ip4_route (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only) -{ - const NMPlatformIP4Route *obj = (const NMPlatformIP4Route *) _obj; - - return build_rtnl_route (AF_INET, - obj->ifindex, - obj->source, - &obj->network, - obj->plen, - &obj->gateway, - obj->pref_src ? &obj->pref_src : NULL, - obj->metric, - obj->mss); -} - -struct nl_object * -_nmp_vt_cmd_plobj_to_nl_ip6_route (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only) -{ - const NMPlatformIP6Route *obj = (const NMPlatformIP6Route *) _obj; - - return build_rtnl_route (AF_INET6, - obj->ifindex, - obj->source, - &obj->network, - obj->plen, - &obj->gateway, - NULL, - obj->metric, - obj->mss); -} - static gboolean ip4_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, in_addr_t network, int plen, in_addr_t gateway, in_addr_t pref_src, guint32 metric, guint32 mss) { - NMPObject obj_needle; - auto_nl_object struct nl_object *nlo = NULL; + NMPObject obj_id; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + + nlmsg = _nl_msg_new_route (RTM_NEWROUTE, + NLM_F_CREATE | NLM_F_REPLACE, + AF_INET, + ifindex, + source, + gateway ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK, + &network, + plen, + &gateway, + metric, + mss, + pref_src ? &pref_src : NULL); - nlo = build_rtnl_route (AF_INET, ifindex, source, &network, plen, &gateway, pref_src ? &pref_src : NULL, metric, mss); - return do_add_addrroute (platform, - nmp_object_stackinit_id_ip4_route (&obj_needle, ifindex, network, plen, metric), - nlo); + nmp_object_stackinit_id_ip4_route (&obj_id, ifindex, network, plen, metric); + return do_add_addrroute (platform, &obj_id, nlmsg); } static gboolean @@ -4490,30 +4855,34 @@ ip6_route_add (NMPlatform *platform, int ifindex, NMIPConfigSource source, struct in6_addr network, int plen, struct in6_addr gateway, guint32 metric, guint32 mss) { - NMPObject obj_needle; - auto_nl_object struct nl_object *nlo = NULL; + NMPObject obj_id; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - metric = nm_utils_ip6_route_metric_normalize (metric); + nlmsg = _nl_msg_new_route (RTM_NEWROUTE, + NLM_F_CREATE | NLM_F_REPLACE, + AF_INET6, + ifindex, + source, + !IN6_IS_ADDR_UNSPECIFIED (&gateway) ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK, + &network, + plen, + &gateway, + metric, + mss, + NULL); - nlo = build_rtnl_route (AF_INET6, ifindex, source, &network, plen, &gateway, NULL, metric, mss); - return do_add_addrroute (platform, - nmp_object_stackinit_id_ip6_route (&obj_needle, ifindex, &network, plen, metric), - nlo); + nmp_object_stackinit_id_ip6_route (&obj_id, ifindex, &network, plen, metric); + return do_add_addrroute (platform, &obj_id, nlmsg); } static gboolean ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen, guint32 metric) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); - in_addr_t gateway = 0; - auto_nl_object struct nl_object *nlo = build_rtnl_route (AF_INET, ifindex, NM_IP_CONFIG_SOURCE_UNKNOWN, &network, plen, &gateway, NULL, metric, 0); - uint8_t scope = RT_SCOPE_NOWHERE; - const NMPObject *obj; - NMPObject obj_needle; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + NMPObject obj_id; - g_return_val_if_fail (nlo, FALSE); - - nmp_object_stackinit_id_ip4_route (&obj_needle, ifindex, network, plen, metric); + nmp_object_stackinit_id_ip4_route (&obj_id, ifindex, network, plen, metric); if (metric == 0) { /* Deleting an IPv4 route with metric 0 does not only delete an exectly matching route. @@ -4523,94 +4892,81 @@ ip4_route_delete (NMPlatform *platform, int ifindex, in_addr_t network, int plen * Instead, make sure that we have the most recent state and process all * delayed actions (including re-reading data from netlink). */ delayed_action_handle_all (platform, TRUE); - } - - obj = nmp_cache_lookup_obj (priv->cache, &obj_needle); - - if (metric == 0 && !obj) { - /* hmm... we are about to delete an IP4 route with metric 0. We must only - * send the delete request if such a route really exists. Above we refreshed - * the platform cache, still no such route exists. - * - * Be extra careful and reload the routes. We must be sure that such a - * route doesn't exists, because when we add an IPv4 address, we immediately - * afterwards try to delete the kernel-added device route with metric 0. - * It might be, that we didn't yet get the notification about that route. - * - * FIXME: once our ip4_address_add() is sure that upon return we have - * the latest state from in the platform cache, we might save this - * additional expensive cache-resync. */ - do_request_one_type (platform, NMP_OBJECT_TYPE_IP4_ROUTE, TRUE); - - obj = nmp_cache_lookup_obj (priv->cache, &obj_needle); - if (!obj) - return TRUE; - } - if (!_nl_has_capability (1 /* NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE */)) { - /* When searching for a matching IPv4 route to delete, the kernel - * searches for a matching scope, unless the RTM_DELROUTE message - * specifies RT_SCOPE_NOWHERE (see fib_table_delete()). - * - * However, if we set the scope of @rtnlroute to RT_SCOPE_NOWHERE (or - * leave it unset), rtnl_route_build_msg() will reset the scope to - * rtnl_route_guess_scope() -- which probably guesses wrong. - * - * As a workaround, we look at the cached route and use that scope. - * - * Newer versions of libnl, no longer reset the scope if explicitly set to RT_SCOPE_NOWHERE. - * So, this workaround is only needed unless we have NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE. - **/ - - if (obj) - scope = nm_platform_route_scope_inv (obj->ip4_route.scope_inv); + if (!nmp_cache_lookup_obj (priv->cache, &obj_id)) { + /* hmm... we are about to delete an IP4 route with metric 0. We must only + * send the delete request if such a route really exists. Above we refreshed + * the platform cache, still no such route exists. + * + * Be extra careful and reload the routes. We must be sure that such a + * route doesn't exists, because when we add an IPv4 address, we immediately + * afterwards try to delete the kernel-added device route with metric 0. + * It might be, that we didn't yet get the notification about that route. + * + * FIXME: once our ip4_address_add() is sure that upon return we have + * the latest state from in the platform cache, we might save this + * additional expensive cache-resync. */ + do_request_one_type (platform, NMP_OBJECT_TYPE_IP4_ROUTE, TRUE); - if (scope == RT_SCOPE_NOWHERE) { - /* If we would set the scope to RT_SCOPE_NOWHERE, libnl would guess the scope. - * But probably it will guess 'link' because we set the next hop of the route - * to zero (0.0.0.0). A better guess is 'global'. */ - scope = RT_SCOPE_UNIVERSE; + if (!nmp_cache_lookup_obj (priv->cache, &obj_id)) + return TRUE; } } - rtnl_route_set_scope ((struct rtnl_route *) nlo, scope); - /* we only support routes with TOS zero. As such, delete_route() is also only able to delete - * routes with tos==0. build_rtnl_route() already initializes tos properly. */ - - /* The following fields are also relevant when comparing the route, but the default values - * are already as we want them: - * - * type: RTN_UNICAST (setting to zero would ignore the type, but we only want to delete RTN_UNICAST) - * pref_src: NULL - */ + nlmsg = _nl_msg_new_route (RTM_DELROUTE, + 0, + AF_INET, + ifindex, + NM_IP_CONFIG_SOURCE_UNKNOWN, + RT_SCOPE_NOWHERE, + &network, + plen, + NULL, + metric, + 0, + NULL); + if (!nlmsg) + return FALSE; - return do_delete_object (platform, &obj_needle, nlo); + return do_delete_object (platform, &obj_id, nlmsg); } static gboolean ip6_route_delete (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, guint32 metric) { - struct in6_addr gateway = IN6ADDR_ANY_INIT; - auto_nl_object struct nl_object *nlo = NULL; - NMPObject obj_needle; + nm_auto_nlmsg struct nl_msg *nlmsg = NULL; + NMPObject obj_id; metric = nm_utils_ip6_route_metric_normalize (metric); - nlo = build_rtnl_route (AF_INET6, ifindex, NM_IP_CONFIG_SOURCE_UNKNOWN ,&network, plen, &gateway, NULL, metric, 0); + nlmsg = _nl_msg_new_route (RTM_DELROUTE, + 0, + AF_INET6, + ifindex, + NM_IP_CONFIG_SOURCE_UNKNOWN, + RT_SCOPE_NOWHERE, + &network, + plen, + NULL, + metric, + 0, + NULL); + if (!nlmsg) + return FALSE; - nmp_object_stackinit_id_ip6_route (&obj_needle, ifindex, &network, plen, metric); + nmp_object_stackinit_id_ip6_route (&obj_id, ifindex, &network, plen, metric); - return do_delete_object (platform, &obj_needle, nlo); + return do_delete_object (platform, &obj_id, nlmsg); } static const NMPlatformIP4Route * ip4_route_get (NMPlatform *platform, int ifindex, in_addr_t network, int plen, guint32 metric) { - NMPObject obj_needle; + NMPObject obj_id; const NMPObject *obj; - nmp_object_stackinit_id_ip4_route (&obj_needle, ifindex, network, plen, metric); - obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_needle); + nmp_object_stackinit_id_ip4_route (&obj_id, ifindex, network, plen, metric); + obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_id); if (nmp_object_is_visible (obj)) return &obj->ip4_route; return NULL; @@ -4619,13 +4975,13 @@ ip4_route_get (NMPlatform *platform, int ifindex, in_addr_t network, int plen, g static const NMPlatformIP6Route * ip6_route_get (NMPlatform *platform, int ifindex, struct in6_addr network, int plen, guint32 metric) { - NMPObject obj_needle; + NMPObject obj_id; const NMPObject *obj; metric = nm_utils_ip6_route_metric_normalize (metric); - nmp_object_stackinit_id_ip6_route (&obj_needle, ifindex, &network, plen, metric); - obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_needle); + nmp_object_stackinit_id_ip6_route (&obj_id, ifindex, &network, plen, metric); + obj = nmp_cache_lookup_obj (NM_LINUX_PLATFORM_GET_PRIVATE (platform)->cache, &obj_id); if (nmp_object_is_visible (obj)) return &obj->ip6_route; return NULL; @@ -5080,6 +5436,8 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->link_get_type_name = link_get_type_name; platform_class->link_get_unmanaged = link_get_unmanaged; + platform_class->link_get_lnk = link_get_lnk; + platform_class->link_refresh = link_refresh; platform_class->link_set_up = link_set_up; @@ -5112,18 +5470,9 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->slave_get_option = slave_get_option; platform_class->vlan_add = vlan_add; - platform_class->vlan_get_info = vlan_get_info; - platform_class->vlan_set_ingress_map = vlan_set_ingress_map; - platform_class->vlan_set_egress_map = vlan_set_egress_map; + platform_class->link_vlan_change = link_vlan_change; platform_class->infiniband_partition_add = infiniband_partition_add; - platform_class->infiniband_get_info = infiniband_get_info; - - platform_class->veth_get_properties = veth_get_properties; - platform_class->tun_get_properties = tun_get_properties; - platform_class->macvlan_get_properties = macvlan_get_properties; - platform_class->vxlan_get_properties = vxlan_get_properties; - platform_class->gre_get_properties = gre_get_properties; platform_class->wifi_get_capabilities = wifi_get_capabilities; platform_class->wifi_get_bssid = wifi_get_bssid; diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index f28a544f87..35cfc885ec 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -28,11 +28,15 @@ #include <string.h> #include <netlink/route/addr.h> #include <netlink/route/rtnl.h> +#include <linux/ip.h> +#include <linux/if_tun.h> +#include <linux/if_tunnel.h> #include "NetworkManagerUtils.h" #include "nm-utils.h" #include "nm-platform.h" #include "nm-platform-utils.h" +#include "nmp-object.h" #include "NetworkManagerUtils.h" #include "nm-default.h" #include "nm-enum-types.h" @@ -67,6 +71,8 @@ G_STATIC_ASSERT (G_STRUCT_OFFSET (NMPlatformIPRoute, network_ptr) == G_STRUCT_OF } \ } G_STMT_END +/*****************************************************************************/ + #define NM_PLATFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_PLATFORM, NMPlatformPrivate)) G_DEFINE_TYPE (NMPlatform, nm_platform, G_TYPE_OBJECT) @@ -207,22 +213,6 @@ nm_platform_error_to_string (NMPlatformError error) /******************************************************************/ -#define IFA_F_MANAGETEMPADDR_STR "mngtmpaddr" -#define IFA_F_NOPREFIXROUTE_STR "noprefixroute" -gboolean -nm_platform_check_support_libnl_extended_ifa_flags () -{ - static int supported = -1; - - /* support for extended ifa-flags was added together - * with the IFA_F_MANAGETEMPADDR flag. So, check if libnl - * is able to parse this flag. */ - if (supported == -1) - supported = rtnl_addr_str2flags (IFA_F_MANAGETEMPADDR_STR) == IFA_F_MANAGETEMPADDR; - - return supported; -} - gboolean nm_platform_check_support_kernel_extended_ifa_flags (NMPlatform *self) { @@ -430,7 +420,7 @@ nm_platform_link_get_all (NMPlatform *self) for (i = 0; i < links->len; i++) { item = &g_array_index (links, NMPlatformLink, i); - _LOGT ("link-get: %3d: %s", i, nm_platform_link_to_string (item)); + _LOGT ("link-get: %3d: %s", i, nm_platform_link_to_string (item, NULL, 0)); nm_assert (item->ifindex > 0 && !g_hash_table_contains (unseen, GINT_TO_POINTER (item->ifindex))); @@ -453,8 +443,7 @@ nm_platform_link_get_all (NMPlatform *self) 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 ( !nm_platform_check_support_libnl_link_netnsid () - || g_hash_table_contains (unseen, GINT_TO_POINTER (item->parent))); + g_warn_if_fail (g_hash_table_contains (unseen, GINT_TO_POINTER (item->parent))); } } } @@ -486,7 +475,7 @@ nm_platform_link_get_all (NMPlatform *self) if (item->parent > 0 && g_hash_table_contains (unseen, GINT_TO_POINTER (item->parent))) continue; - _LOGT ("link-get: add %3d -> %3d: %s", i, j, nm_platform_link_to_string (item)); + _LOGT ("link-get: add %3d -> %3d: %s", i, j, nm_platform_link_to_string (item, NULL, 0)); g_hash_table_remove (unseen, GINT_TO_POINTER (item->ifindex)); g_array_index (result, NMPlatformLink, j++) = *item; @@ -499,7 +488,7 @@ nm_platform_link_get_all (NMPlatform *self) * This can happen for veth pairs where each peer is parent of the other end. */ item = &g_array_index (links, NMPlatformLink, first_idx); - _LOGT ("link-get: add (loop) %3d -> %3d: %s", first_idx, j, nm_platform_link_to_string (item)); + _LOGT ("link-get: add (loop) %3d -> %3d: %s", first_idx, j, nm_platform_link_to_string (item, NULL, 0)); g_hash_table_remove (unseen, GINT_TO_POINTER (item->ifindex)); g_array_index (result, NMPlatformLink, j++) = *item; @@ -893,22 +882,19 @@ nm_platform_link_uses_arp (NMPlatform *self, int ifindex) gboolean nm_platform_link_get_ipv6_token (NMPlatform *self, int ifindex, NMUtilsIPv6IfaceId *iid) { + const NMPlatformLink *pllink; + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex >= 0, FALSE); g_return_val_if_fail (iid, FALSE); -#if HAVE_LIBNL_INET6_TOKEN - { - const NMPlatformLink *pllink; - pllink = nm_platform_link_get (self, ifindex); - if (pllink && pllink->inet6_token.is_valid) { - *iid = pllink->inet6_token.iid; - return TRUE; - } + pllink = nm_platform_link_get (self, ifindex); + if (pllink && pllink->inet6_token.is_valid) { + *iid = pllink->inet6_token.iid; + return TRUE; } -#endif return FALSE; } @@ -950,23 +936,18 @@ nm_platform_link_get_udev_device (NMPlatform *self, int 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); -#if HAVE_LIBNL_INET6_ADDR_GEN_MODE && HAVE_KERNEL_INET6_ADDR_GEN_MODE - { - const NMPlatformLink *pllink; - - 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) == IN6_ADDR_GEN_MODE_NONE; - } -#endif + 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 @@ -1387,6 +1368,83 @@ nm_platform_link_get_master (NMPlatform *self, int slave) return pllink ? pllink->master : 0; } +/*****************************************************************************/ + +/** + * 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) +{ + _CHECK_SELF (self, klass, FALSE); + + NM_SET_OUT (out_link, NULL); + + g_return_val_if_fail (ifindex > 0, NULL); + + return klass->link_get_lnk (self, ifindex, link_type, out_link); +} + +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 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 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_bridge_add: * @self: platform instance @@ -1524,43 +1582,91 @@ nm_platform_slave_get_option (NMPlatform *self, int ifindex, const char *option) } gboolean -nm_platform_vlan_get_info (NMPlatform *self, int ifindex, int *parent, int *vlanid) +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); - g_return_val_if_fail (klass->vlan_get_info, FALSE); + nm_assert (klass->link_vlan_change); - if (parent) - *parent = 0; - if (vlanid) - *vlanid = 0; + g_return_val_if_fail (!n_ingress_map || ingress_map, FALSE); + g_return_val_if_fail (!n_egress_map || egress_map, FALSE); - if (nm_platform_link_get_type (self, ifindex) != NM_LINK_TYPE_VLAN) - return 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)"); + } - return klass->vlan_get_info (self, ifindex, parent, vlanid); + 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_vlan_set_ingress_map (NMPlatform *self, int ifindex, int from, int to) { - _CHECK_SELF (self, klass, FALSE); + NMVlanQosMapping map = { + .from = from, + .to = to, + }; - g_return_val_if_fail (klass->vlan_set_ingress_map, FALSE); - - _LOGD ("link: setting vlan ingress map for %d from %d to %d", ifindex, from, to); - return klass->vlan_set_ingress_map (self, ifindex, from, to); + return nm_platform_link_vlan_change (self, ifindex, 0, 0, FALSE, &map, 1, FALSE, NULL, 0); } gboolean nm_platform_vlan_set_egress_map (NMPlatform *self, int ifindex, int from, int to) { - _CHECK_SELF (self, klass, FALSE); + NMVlanQosMapping map = { + .from = from, + .to = to, + }; - g_return_val_if_fail (klass->vlan_set_egress_map, FALSE); - - _LOGD ("link: setting vlan egress map for %d from %d to %d", ifindex, from, to); - return klass->vlan_set_egress_map (self, ifindex, from, to); + return nm_platform_link_vlan_change (self, ifindex, 0, 0, FALSE, NULL, 0, FALSE, &map, 1); } NMPlatformError @@ -1594,73 +1700,175 @@ nm_platform_infiniband_partition_add (NMPlatform *self, int parent, int p_key, N } gboolean -nm_platform_infiniband_get_info (NMPlatform *self, - int ifindex, - int *parent, - int *p_key, - const char **mode) -{ +nm_platform_infiniband_get_properties (NMPlatform *self, + int ifindex, + int *out_parent, + int *out_p_key, + const char **out_mode) +{ + const NMPlatformLnkInfiniband *plnk; + const NMPlatformLink *plink; + const char *iface; + char *path, *contents; + const char *mode; + int p_key = 0; + _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (klass->infiniband_get_info, FALSE); - return klass->infiniband_get_info (self, ifindex, parent, p_key, mode); -} + plnk = nm_platform_link_get_lnk_infiniband (self, ifindex, &plink); -gboolean -nm_platform_veth_get_properties (NMPlatform *self, int ifindex, NMPlatformVethProperties *props) -{ - _CHECK_SELF (self, klass, FALSE); + if ( !plink + || plink->type != NM_LINK_TYPE_INFINIBAND) + return FALSE; - g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (props != NULL, 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; + } - return klass->veth_get_properties (self, ifindex, props); -} + /* Could not get the link information via netlink. To support older kernels, + * fallback to reading sysfs. */ -gboolean -nm_platform_tun_get_properties (NMPlatform *self, int ifindex, NMPlatformTunProperties *props) -{ - _CHECK_SELF (self, klass, FALSE); + iface = ASSERT_VALID_PATH_COMPONENT (plink->name); - g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (props != NULL, FALSE); + /* Fall back to reading sysfs */ + path = g_strdup_printf ("/sys/class/net/%s/mode", iface); + contents = nm_platform_sysctl_get (self, path); + g_free (path); + if (!contents) + return FALSE; + + if (strstr (contents, "datagram")) + mode = "datagram"; + else if (strstr (contents, "connected")) + mode = "connected"; + else + mode = NULL; + g_free (contents); - return klass->tun_get_properties (self, ifindex, props); + path = g_strdup_printf ("/sys/class/net/%s/pkey", iface); + contents = nm_platform_sysctl_get (self, path); + g_free (path); + if (!contents) + return FALSE; + p_key = (int) _nm_utils_ascii_str_to_int64 (contents, 16, 0, 0xFFFF, -1); + g_free (contents); + 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; } gboolean -nm_platform_macvlan_get_properties (NMPlatform *self, int ifindex, NMPlatformMacvlanProperties *props) +nm_platform_veth_get_properties (NMPlatform *self, int ifindex, int *out_peer_ifindex) { + const NMPlatformLink *plink; + int peer_ifindex; _CHECK_SELF (self, klass, FALSE); - g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (props != NULL, 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; + } - return klass->macvlan_get_properties (self, ifindex, props); + /* Pre-4.1 kernel did not expose the peer_ifindex as IFA_LINK. Lookup via ethtool. */ + if (out_peer_ifindex) { + peer_ifindex = nmp_utils_ethtool_get_peer_ifindex (plink->name); + if (peer_ifindex <= 0) + return FALSE; + + *out_peer_ifindex = peer_ifindex; + } + return TRUE; } gboolean -nm_platform_vxlan_get_properties (NMPlatform *self, int ifindex, NMPlatformVxlanProperties *props) +nm_platform_tun_get_properties_ifname (NMPlatform *self, const char *ifname, NMPlatformTunProperties *props) { + char *path, *val; + gboolean success = TRUE; + _CHECK_SELF (self, klass, FALSE); - g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (props != NULL, FALSE); + g_return_val_if_fail (props, FALSE); + + memset (props, 0, sizeof (*props)); + props->owner = -1; + props->group = -1; - return klass->vxlan_get_properties (self, ifindex, props); + if (!ifname || !nm_utils_iface_valid_name (ifname)) + return FALSE; + ifname = ASSERT_VALID_PATH_COMPONENT (ifname); + + path = g_strdup_printf ("/sys/class/net/%s/owner", ifname); + val = nm_platform_sysctl_get (self, path); + g_free (path); + if (val) { + props->owner = _nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1); + if (errno) + success = FALSE; + g_free (val); + } else + success = FALSE; + + path = g_strdup_printf ("/sys/class/net/%s/group", ifname); + val = nm_platform_sysctl_get (self, path); + g_free (path); + if (val) { + props->group = _nm_utils_ascii_str_to_int64 (val, 10, -1, G_MAXINT64, -1); + if (errno) + success = FALSE; + g_free (val); + } else + success = FALSE; + + path = g_strdup_printf ("/sys/class/net/%s/tun_flags", ifname); + val = nm_platform_sysctl_get (self, path); + g_free (path); + if (val) { + gint64 flags; + + flags = _nm_utils_ascii_str_to_int64 (val, 16, 0, G_MAXINT64, 0); + if (!errno) { +#ifndef IFF_MULTI_QUEUE + const int IFF_MULTI_QUEUE = 0x0100; +#endif + 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 & IFF_MULTI_QUEUE); + } else + success = FALSE; + g_free (val); + } else + success = FALSE; + + return success; } gboolean -nm_platform_gre_get_properties (NMPlatform *self, int ifindex, NMPlatformGreProperties *props) +nm_platform_tun_get_properties (NMPlatform *self, int ifindex, NMPlatformTunProperties *props) { _CHECK_SELF (self, klass, FALSE); g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (props != NULL, FALSE); - return klass->gre_get_properties (self, ifindex, props); + return nm_platform_tun_get_properties_ifname (self, nm_platform_link_get_name (self, ifindex), props); } gboolean @@ -1827,10 +2035,12 @@ _to_string_dev (NMPlatform *self, int ifindex, char *buf, size_t size) /******************************************************************/ -in_addr_t -nm_platform_ip4_address_get_peer (const NMPlatformIP4Address *addr) +void +nm_platform_ip4_address_set_addr (NMPlatformIP4Address *addr, in_addr_t address, int plen) { - return addr->peer_address ?: addr->address; + addr->address = address; + addr->peer_address = address; + addr->plen = plen; } const struct in6_addr * @@ -1842,28 +2052,6 @@ nm_platform_ip6_address_get_peer (const NMPlatformIP6Address *addr) return &addr->peer_address; } -in_addr_t -nm_platform_ip4_address_get_peer_net (const NMPlatformIP4Address *addr) -{ - return (addr->peer_address ?: addr->address) & nm_utils_ip4_prefix_to_netmask (addr->plen); -} - -gboolean -nm_platform_ip4_address_equal_peer_net (const NMPlatformIP4Address *addr1, const NMPlatformIP4Address *addr2) -{ - guint32 a1, a2; - - if (addr1->plen != addr2->plen) - return FALSE; - - /* For kernel, if the peer address is unset, that effectively means that - * the peer address equals the local address. */ - a1 = addr1->peer_address ? addr1->peer_address : addr1->address; - a2 = addr2->peer_address ? addr2->peer_address : addr2->address; - - return ((a1 ^ a2) & nm_utils_ip4_prefix_to_netmask (addr1->plen)) == 0; -} - GArray * nm_platform_ip4_address_get_all (NMPlatform *self, int ifindex) { @@ -1918,7 +2106,7 @@ nm_platform_ip4_address_add (NMPlatform *self, 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)); + _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, label); } @@ -1953,7 +2141,7 @@ nm_platform_ip6_address_add (NMPlatform *self, addr.preferred = preferred; addr.flags = flags; - _LOGD ("address: adding or updating IPv6 address: %s", nm_platform_ip6_address_to_string (&addr)); + _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); } @@ -1962,7 +2150,8 @@ gboolean nm_platform_ip4_address_delete (NMPlatform *self, int ifindex, in_addr_t address, int plen, in_addr_t peer_address) { char str_dev[TO_STRING_DEV_BUF_SIZE]; - char str_peer[NM_UTILS_INET_ADDRSTRLEN]; + char str_peer2[NM_UTILS_INET_ADDRSTRLEN]; + char str_peer[100]; _CHECK_SELF (self, klass, FALSE); @@ -1970,11 +2159,10 @@ nm_platform_ip4_address_delete (NMPlatform *self, int ifindex, in_addr_t address g_return_val_if_fail (plen > 0, FALSE); g_return_val_if_fail (klass->ip4_address_delete, FALSE); - _LOGD ("address: deleting IPv4 address %s/%d, %s%s%sifindex %d%s", + _LOGD ("address: deleting IPv4 address %s/%d, %sifindex %d%s", nm_utils_inet4_ntop (address, NULL), plen, - peer_address ? "peer " : "", - peer_address ? nm_utils_inet4_ntop (peer_address, str_peer) : "", - peer_address ? ", " : "", + 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); @@ -2028,7 +2216,7 @@ array_contains_ip4_address (const GArray *addresses, const NMPlatformIP4Address if ( candidate->address == address->address && candidate->plen == address->plen - && nm_platform_ip4_address_equal_peer_net (candidate, address)) { + && ((candidate->peer_address & address->peer_address) & nm_utils_ip4_prefix_to_netmask (address->plen)) == 0) { guint32 lifetime, preferred; if (nmp_utils_lifetime_get (candidate->timestamp, candidate->lifetime, candidate->preferred, @@ -2236,7 +2424,7 @@ nm_platform_ip4_route_add (NMPlatform *self, route.mss = mss; route.pref_src = pref_src; - _LOGD ("route: adding or updating IPv4 route: %s", nm_platform_ip4_route_to_string (&route)); + _LOGD ("route: adding or updating IPv4 route: %s", nm_platform_ip4_route_to_string (&route, NULL, 0)); } return klass->ip4_route_add (self, ifindex, source, network, plen, gateway, pref_src, metric, mss); } @@ -2263,7 +2451,7 @@ nm_platform_ip6_route_add (NMPlatform *self, route.metric = metric; route.mss = mss; - _LOGD ("route: adding or updating IPv6 route: %s", nm_platform_ip6_route_to_string (&route)); + _LOGD ("route: adding or updating IPv6 route: %s", nm_platform_ip6_route_to_string (&route, NULL, 0)); } return klass->ip6_route_add (self, ifindex, source, network, plen, gateway, metric, mss); } @@ -2316,6 +2504,40 @@ nm_platform_ip6_route_get (NMPlatform *self, int ifindex, struct in6_addr networ /******************************************************************/ +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 * source_to_string (NMIPConfigSource source) { @@ -2368,21 +2590,29 @@ _lifetime_summary_to_string (gint32 now, guint32 timestamp, guint32 preferred, g return buf; } -char _nm_platform_to_string_buffer[256]; - +/** + * 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) +nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len) { char master[20]; char parent[20]; - char str_vlan[16]; GString *str_flags; char str_addrmode[30]; gs_free char *str_addr = NULL; gs_free char *str_inet6_token = NULL; + const char *str_link_type; - if (!link) - return "(unknown link)"; + if (!nm_utils_to_string_buffer_init_null (link, &buf, &len)) + return buf; str_flags = g_string_new (NULL); if (NM_FLAGS_HAS (link->flags, IFF_NOARP)) @@ -2397,7 +2627,7 @@ nm_platform_link_to_string (const NMPlatformLink *link) if (link->flags) { char str_flags_buf[64]; - rtnl_link_flags2str (link->flags, str_flags_buf, sizeof (str_flags_buf)); + nm_platform_link_flags2str (link->flags, str_flags_buf, sizeof (str_flags_buf)); g_string_append_printf (str_flags, ";%s", str_flags_buf); } @@ -2413,11 +2643,6 @@ nm_platform_link_to_string (const NMPlatformLink *link) else parent[0] = 0; - if (link->vlan_id) - g_snprintf (str_vlan, sizeof (str_vlan), " vlan %u", (guint) link->vlan_id); - else - str_vlan[0] = '\0'; - if (link->inet6_addr_gen_mode_inv) { switch (_nm_platform_uint8_inv (link->inet6_addr_gen_mode_inv)) { case 0: @@ -2438,14 +2663,15 @@ nm_platform_link_to_string (const NMPlatformLink *link) if (link->inet6_token.is_valid) str_inet6_token = nm_utils_hwaddr_ntoa (&link->inet6_token.iid, sizeof (link->inet6_token.iid)); - g_snprintf (_nm_platform_to_string_buffer, sizeof (_nm_platform_to_string_buffer), + 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 */ - "%s" /* vlan */ " arp %u" /* arptype */ "%s%s" /* link->type */ "%s%s" /* kind */ @@ -2460,12 +2686,11 @@ nm_platform_link_to_string (const NMPlatformLink *link) parent, str_flags->str, link->mtu, master, - str_vlan, link->arptype, - nm_link_type_to_string (link->type) ? " " : "", - str_if_set (nm_link_type_to_string (link->type), "???"), - link->kind ? (g_strcmp0 (nm_link_type_to_string (link->type), link->kind) ? "/" : "*") : "", - link->kind && g_strcmp0 (nm_link_type_to_string (link->type), link->kind) ? link->kind : "", + str_link_type ? " " : "", + str_if_set (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", str_addrmode, str_addr ? " addr " : "", @@ -2475,23 +2700,205 @@ nm_platform_link_to_string (const NMPlatformLink *link) link->driver ? " driver " : "", link->driver ? link->driver : ""); g_string_free (str_flags, TRUE); - return _nm_platform_to_string_buffer; + 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_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%s%s%s", + lnk->mode ? " mode " : "", + lnk->mode ?: "", + lnk->no_promisc ? " not-promisc" : " promisc"); + 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. The returned string - * is an internal buffer, so do not keep or free the returned string. - * Also, this function is not thread safe. + * Returns: a string representation of the address. */ const char * -nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address) +nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address, char *buf, gsize len) { char s_address[INET_ADDRSTRLEN]; char s_peer[INET_ADDRSTRLEN]; @@ -2502,11 +2909,12 @@ nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address) const char *str_lft_p, *str_pref_p, *str_time_p; gint32 now = nm_utils_get_monotonic_timestamp_s (); - g_return_val_if_fail (address, "(unknown)"); + 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) { + 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); } @@ -2528,73 +2936,122 @@ nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address) 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 (_nm_platform_to_string_buffer, sizeof (_nm_platform_to_string_buffer), "%s/%d lft %s pref %s%s%s%s%s src %s", + 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, str_label, source_to_string (address->source)); g_free (str_peer); - return _nm_platform_to_string_buffer; + return buf; } -/** - * nm_platform_addr_flags2str: wrapper for rtnl_addr_flags2str(), - * which might not yet support some recent address flags. - **/ -void -nm_platform_addr_flags2str (int flags, char *buf, size_t size) +const char * +nm_platform_link_flags2str (unsigned flags, char *buf, gsize len) +{ + static const NMUtilsFlags2StrDesc descs[] = { + 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"), + }; + return nm_utils_flags2str (descs, G_N_ELEMENTS (descs), flags, buf, len); +}; + +const char * +nm_platform_link_inet6_addrgenmode2str (guint8 mode, char *buf, gsize len) { - if ( !NM_FLAGS_ANY (flags, IFA_F_MANAGETEMPADDR | IFA_F_NOPREFIXROUTE) - || nm_platform_check_support_libnl_extended_ifa_flags ()) - rtnl_addr_flags2str (flags, buf, size); - else { - /* There are two recent flags IFA_F_MANAGETEMPADDR and IFA_F_NOPREFIXROUTE. - * If libnl does not yet support them, add them by hand. - * These two flags were introduced together with the extended ifa_flags - * so check for nm_platform_check_support_libnl_extended_ifa_flags (). */ - gboolean has_other_unknown_flags = FALSE; - size_t len; - - /* if there are unknown flags to rtnl_addr_flags2str(), libnl appends ',' - * to indicate them. We want to keep this behavior, if there are other - * unknown flags present. */ - - rtnl_addr_flags2str (flags & ~(IFA_F_MANAGETEMPADDR | IFA_F_NOPREFIXROUTE), buf, size); - - len = strlen (buf); - if (len > 0) { - has_other_unknown_flags = (buf[len - 1] == ','); - if (!has_other_unknown_flags) - g_strlcat (buf, ",", size); - } + nm_utils_to_string_buffer_init (&buf, &len); - if (NM_FLAGS_ALL (flags, IFA_F_MANAGETEMPADDR | IFA_F_NOPREFIXROUTE)) - g_strlcat (buf, IFA_F_MANAGETEMPADDR_STR","IFA_F_NOPREFIXROUTE_STR, size); - else if (NM_FLAGS_HAS (flags, IFA_F_MANAGETEMPADDR)) - g_strlcat (buf, IFA_F_MANAGETEMPADDR_STR, size); - else - g_strlcat (buf, IFA_F_NOPREFIXROUTE_STR, size); + switch (mode) { + case NM_IN6_ADDR_GEN_MODE_NONE: + g_snprintf (buf, len, "none"); + break; + case NM_IN6_ADDR_GEN_MODE_EUI64: + g_snprintf (buf, len, "eui64"); + break; + case NM_IN6_ADDR_GEN_MODE_STABLE_PRIVACY: + g_snprintf (buf, len, "stable-privacy"); + break; + default: + g_snprintf (buf, len, "%u", (unsigned) mode); + break; + } + return buf; +} + +const char * +nm_platform_addr_flags2str (unsigned flags, char *buf, gsize len) +{ + static const NMUtilsFlags2StrDesc descs[] = { + 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"), + }; + return nm_utils_flags2str (descs, G_N_ELEMENTS (descs), flags, buf, len); +}; + +const char * +nm_platform_route_scope2str (int scope, char *buf, gsize len) +{ + nm_utils_to_string_buffer_init (&buf, &len); - if (has_other_unknown_flags) - g_strlcat (buf, ",", size); + switch (scope) { + case 255: + g_snprintf (buf, len, "nowhere"); + break; + case 254: + g_snprintf (buf, len, "host"); + break; + case 200: + g_snprintf (buf, len, "site"); + break; + case 0: + g_snprintf (buf, len, "global"); + break; + default: + g_snprintf (buf, len, "%d", scope); + break; } + return buf; } /** * 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. The returned string - * is an internal buffer, so do not keep or free the returned string. - * Also, this function is not thread safe. + * Returns: a string representation of the address. */ const char * -nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address) +nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address, char *buf, gsize len) { #define S_FLAGS_PREFIX " flags " char s_flags[256]; @@ -2606,7 +3063,8 @@ nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address) const char *str_lft_p, *str_pref_p, *str_time_p; gint32 now = nm_utils_get_monotonic_timestamp_s (); - g_return_val_if_fail (address, "(unknown)"); + if (!nm_utils_to_string_buffer_init_null (address, &buf, &len)) + return buf; inet_ntop (AF_INET6, &address->address, s_address, sizeof (s_address)); @@ -2633,44 +3091,46 @@ nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address) 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 (_nm_platform_to_string_buffer, sizeof (_nm_platform_to_string_buffer), "%s/%d lft %s pref %s%s%s%s%s src %s", + 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, s_flags, source_to_string (address->source)); g_free (str_peer); - return _nm_platform_to_string_buffer; + 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. The returned string - * is an internal buffer, so do not keep or free the returned string. - * Also, this function is not thread safe. + * Returns: a string representation of the route. */ const char * -nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route) +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]; - g_return_val_if_fail (route, "(unknown)"); + 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 (_nm_platform_to_string_buffer, sizeof (_nm_platform_to_string_buffer), + g_snprintf (buf, len, "%s/%d" " via %s" "%s" @@ -2687,38 +3147,39 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route) route->mss, source_to_string (route->source), route->scope_inv ? " scope " : "", - route->scope_inv ? (rtnl_scope2str (nm_platform_route_scope_inv (route->scope_inv), str_scope, sizeof (str_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)) : ""); - return _nm_platform_to_string_buffer; + 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. The returned string - * is an internal buffer, so do not keep or free the returned string. - * Also, this function is not thread safe. + * Returns: a string representation of the route. */ const char * -nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route) +nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsize len) { char s_network[INET6_ADDRSTRLEN], s_gateway[INET6_ADDRSTRLEN]; char str_dev[TO_STRING_DEV_BUF_SIZE]; - g_return_val_if_fail (route, "(unknown)"); + 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)); _to_string_dev (NULL, route->ifindex, str_dev, sizeof (str_dev)); - g_snprintf (_nm_platform_to_string_buffer, sizeof (_nm_platform_to_string_buffer), + g_snprintf (buf, len, "%s/%d" " via %s" "%s" @@ -2732,7 +3193,7 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route) route->metric, route->mss, source_to_string (route->source)); - return _nm_platform_to_string_buffer; + return buf; } #define _CMP_SELF(a, b) \ @@ -2819,7 +3280,6 @@ nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b) _CMP_FIELD_STR (a, b, name); _CMP_FIELD (a, b, master); _CMP_FIELD (a, b, parent); - _CMP_FIELD (a, b, vlan_id); _CMP_FIELD (a, b, flags); _CMP_FIELD (a, b, connected); _CMP_FIELD (a, b, mtu); @@ -2838,22 +3298,83 @@ nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b) } int -nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a, const NMPlatformIP4Address *b) +nm_platform_lnk_gre_cmp (const NMPlatformLnkGre *a, const NMPlatformLnkGre *b) +{ + _CMP_SELF (a, b); + _CMP_FIELD (a, b, parent_ifindex); + _CMP_FIELD (a, b, input_flags); + _CMP_FIELD (a, b, output_flags); + _CMP_FIELD (a, b, input_key); + _CMP_FIELD (a, b, output_key); + _CMP_FIELD (a, b, local); + _CMP_FIELD (a, b, remote); + _CMP_FIELD (a, b, ttl); + _CMP_FIELD (a, b, tos); + _CMP_FIELD_BOOL (a, b, path_mtu_discovery); + return 0; +} + +int +nm_platform_lnk_infiniband_cmp (const NMPlatformLnkInfiniband *a, const NMPlatformLnkInfiniband *b) +{ + _CMP_SELF (a, b); + _CMP_FIELD (a, b, p_key); + _CMP_FIELD_STR_INTERNED (a, b, mode); + return 0; +} + +int +nm_platform_lnk_macvlan_cmp (const NMPlatformLnkMacvlan *a, const NMPlatformLnkMacvlan *b) +{ + _CMP_SELF (a, b); + _CMP_FIELD_STR_INTERNED (a, b, mode); + _CMP_FIELD_BOOL (a, b, no_promisc); + return 0; +} + +int +nm_platform_lnk_vlan_cmp (const NMPlatformLnkVlan *a, const NMPlatformLnkVlan *b) +{ + _CMP_SELF (a, b); + _CMP_FIELD (a, b, id); + _CMP_FIELD (a, b, flags); + return 0; +} + +int +nm_platform_lnk_vxlan_cmp (const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan *b) { - in_addr_t p_a, p_b; + _CMP_SELF (a, b); + _CMP_FIELD (a, b, parent_ifindex); + _CMP_FIELD (a, b, id); + _CMP_FIELD (a, b, group); + _CMP_FIELD (a, b, local); + _CMP_FIELD_MEMCMP (a, b, group6); + _CMP_FIELD_MEMCMP (a, b, local6); + _CMP_FIELD (a, b, tos); + _CMP_FIELD (a, b, ttl); + _CMP_FIELD_BOOL (a, b, learning); + _CMP_FIELD (a, b, ageing); + _CMP_FIELD (a, b, limit); + _CMP_FIELD (a, b, dst_port); + _CMP_FIELD (a, b, src_port_min); + _CMP_FIELD (a, b, src_port_max); + _CMP_FIELD_BOOL (a, b, proxy); + _CMP_FIELD_BOOL (a, b, rsc); + _CMP_FIELD_BOOL (a, b, l2miss); + _CMP_FIELD_BOOL (a, b, l3miss); + return 0; +} +int +nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a, const NMPlatformIP4Address *b) +{ _CMP_SELF (a, b); _CMP_FIELD (a, b, ifindex); _CMP_FIELD (a, b, source); _CMP_FIELD (a, b, address); _CMP_FIELD (a, b, plen); - - /* a peer-address of zero is the same as setting it to address. - * Here we consider the full address, including the host-part. */ - p_a = nm_platform_ip4_address_get_peer (a); - p_b = nm_platform_ip4_address_get_peer (b); - _CMP_DIRECT (p_a, p_b); - + _CMP_FIELD (a, b, peer_address); _CMP_FIELD (a, b, timestamp); _CMP_FIELD (a, b, lifetime); _CMP_FIELD (a, b, preferred); @@ -2983,31 +3504,31 @@ 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)); + _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)); + _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)); + _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)); + _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)); + _LOGD ("signal: route 6 %7s: %s", nm_platform_signal_change_type_to_string (change_type), nm_platform_ip6_route_to_string (route, NULL, 0)); } /******************************************************************/ @@ -3084,7 +3605,7 @@ const NMPlatformVTableRoute nm_platform_vtable_route_v4 = { .addr_family = AF_INET, .sizeof_route = sizeof (NMPlatformIP4Route), .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b)) nm_platform_ip4_route_cmp, - .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route)) nm_platform_ip4_route_to_string, + .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip4_route_to_string, .route_get_all = nm_platform_ip4_route_get_all, .route_add = _vtr_v4_route_add, .route_delete = _vtr_v4_route_delete, @@ -3097,7 +3618,7 @@ const NMPlatformVTableRoute nm_platform_vtable_route_v6 = { .addr_family = AF_INET6, .sizeof_route = sizeof (NMPlatformIP6Route), .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b)) nm_platform_ip6_route_cmp, - .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route)) nm_platform_ip6_route_to_string, + .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip6_route_to_string, .route_get_all = nm_platform_ip6_route_get_all, .route_add = _vtr_v6_route_add, .route_delete = _vtr_v6_route_delete, diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index f7c44d4eb6..42c3b79390 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -25,9 +25,11 @@ #include <linux/if.h> #include <linux/if_addr.h> -#include <nm-dbus-interface.h> +#include "nm-dbus-interface.h" #include "nm-default.h" #include "NetworkManagerUtils.h" +#include "nm-setting-vlan.h" +#include "nm-core-types-internal.h" #define NM_TYPE_PLATFORM (nm_platform_get_type ()) #define NM_PLATFORM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_PLATFORM, NMPlatform)) @@ -52,6 +54,13 @@ typedef struct _NMPlatform NMPlatform; #define IFA_F_NOPREFIXROUTE 0x200 #endif +/* Define of the IN6_ADDR_GEN_MODE_* values to workaround old kernel headers + * that don't define it. */ +#define NM_IN6_ADDR_GEN_MODE_UNKNOWN 255 /* no corresponding value. */ +#define NM_IN6_ADDR_GEN_MODE_EUI64 0 /* IN6_ADDR_GEN_MODE_EUI64 */ +#define NM_IN6_ADDR_GEN_MODE_NONE 1 /* IN6_ADDR_GEN_MODE_NONE */ +#define NM_IN6_ADDR_GEN_MODE_STABLE_PRIVACY 2 /* IN6_ADDR_GEN_MODE_STABLE_PRIVACY */ + typedef enum { /*< skip >*/ /* dummy value, to enforce that the enum type is signed and has a size @@ -85,6 +94,19 @@ typedef enum { _NM_PLATFORM_REASON_CACHE_CHECK_INTERNAL, } NMPlatformReason; + +typedef struct { + union { + guint8 addr_ptr[1]; + in_addr_t addr4; + struct in6_addr addr6; + }; +} NMIPAddr; + +extern const NMIPAddr nm_ip_addr_zero; + +#define NMIPAddrInit { .addr6 = IN6ADDR_ANY_INIT } + #define NM_PLATFORM_LINK_OTHER_NETNS (-1) #define __NMPlatformObject_COMMON \ @@ -131,9 +153,6 @@ struct _NMPlatformLink { * initialized with memset(0) has and unset value.*/ guint8 inet6_addr_gen_mode_inv; - /* rtnl_link_vlan_get_id(), IFLA_VLAN_ID */ - guint16 vlan_id; - /* IFF_* flags as u32. Note that ifi_flags in 'struct ifinfomsg' is declared as 'unsigned', * but libnl stores the flag internally as u32. */ guint32 flags; @@ -218,8 +237,21 @@ typedef struct { **/ struct _NMPlatformIP4Address { __NMPlatformIPAddress_COMMON; + + /* The local address IFA_LOCAL. */ in_addr_t address; + + /* The IFA_ADDRESS PTP peer address. This field is rather important, because + * it constitutes the identifier for the IPv4 address (e.g. you can add two + * addresses that only differ by their peer's network-part. + * + * Beware that for most cases, NetworkManager doesn't want to set an explicit + * peer-address. Hoever, that corresponds to setting the peer address to @address + * itself. Leaving peer-address unset/zero, means explicitly setting the peer + * address to 0.0.0.0, which you probably don't want. + * */ in_addr_t peer_address; /* PTP peer address */ + char label[IFNAMSIZ]; }; @@ -313,7 +345,7 @@ typedef struct { int addr_family; gsize sizeof_route; int (*route_cmp) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b); - const char *(*route_to_string) (const NMPlatformIPXRoute *route); + const char *(*route_to_string) (const NMPlatformIPXRoute *route, char *buf, gsize len); GArray *(*route_get_all) (NMPlatform *self, int ifindex, NMPlatformGetRouteFlags flags); gboolean (*route_add) (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route, gint64 metric); gboolean (*route_delete) (NMPlatform *self, int ifindex, const NMPlatformIPXRoute *route); @@ -324,26 +356,34 @@ typedef struct { extern const NMPlatformVTableRoute nm_platform_vtable_route_v4; extern const NMPlatformVTableRoute nm_platform_vtable_route_v6; -extern char _nm_platform_to_string_buffer[256]; - typedef struct { - int peer; -} NMPlatformVethProperties; + int parent_ifindex; + guint16 input_flags; + guint16 output_flags; + guint32 input_key; + guint32 output_key; + in_addr_t local; + in_addr_t remote; + guint8 ttl; + guint8 tos; + gboolean path_mtu_discovery; +} NMPlatformLnkGre; typedef struct { - gint64 owner; - gint64 group; + int p_key; const char *mode; - gboolean no_pi; - gboolean vnet_hdr; - gboolean multi_queue; -} NMPlatformTunProperties; +} NMPlatformLnkInfiniband; typedef struct { - int parent_ifindex; const char *mode; gboolean no_promisc; -} NMPlatformMacvlanProperties; +} NMPlatformLnkMacvlan; + +typedef struct { + /* rtnl_link_vlan_get_id(), IFLA_VLAN_ID */ + guint16 id; + NMVlanFlags flags; +} NMPlatformLnkVlan; typedef struct { int parent_ifindex; @@ -364,20 +404,16 @@ typedef struct { gboolean rsc; gboolean l2miss; gboolean l3miss; -} NMPlatformVxlanProperties; +} NMPlatformLnkVxlan; typedef struct { - int parent_ifindex; - guint16 input_flags; - guint16 output_flags; - guint32 input_key; - guint32 output_key; - in_addr_t local; - in_addr_t remote; - guint8 ttl; - guint8 tos; - gboolean path_mtu_discovery; -} NMPlatformGreProperties; + gint64 owner; + gint64 group; + const char *mode; + gboolean no_pi; + gboolean vnet_hdr; + gboolean multi_queue; +} NMPlatformTunProperties; /******************************************************************/ @@ -423,6 +459,8 @@ typedef struct { const NMPlatformLink *(*link_get_by_ifname) (NMPlatform *platform, const char *ifname); const NMPlatformLink *(*link_get_by_address) (NMPlatform *platform, gconstpointer address, size_t length); + const NMPObject *(*link_get_lnk) (NMPlatform *platform, int ifindex, NMLinkType link_type, const NMPlatformLink **out_link); + GArray *(*link_get_all) (NMPlatform *); gboolean (*link_add) (NMPlatform *, const char *name, @@ -474,22 +512,18 @@ typedef struct { char * (*slave_get_option) (NMPlatform *, int ifindex, const char *option); gboolean (*vlan_add) (NMPlatform *, const char *name, int parent, int vlanid, guint32 vlanflags, NMPlatformLink *out_link); - gboolean (*vlan_get_info) (NMPlatform *, int ifindex, int *parent, int *vlan_id); - gboolean (*vlan_set_ingress_map) (NMPlatform *, int ifindex, int from, int to); - gboolean (*vlan_set_egress_map) (NMPlatform *, int ifindex, int from, int to); + gboolean (*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); gboolean (*infiniband_partition_add) (NMPlatform *, int parent, int p_key, NMPlatformLink *out_link); - gboolean (*infiniband_get_info) (NMPlatform *, - int ifindex, - int *parent, - int *p_key, - const char **mode); - - gboolean (*veth_get_properties) (NMPlatform *, int ifindex, NMPlatformVethProperties *properties); - gboolean (*tun_get_properties) (NMPlatform *, int ifindex, NMPlatformTunProperties *properties); - gboolean (*macvlan_get_properties) (NMPlatform *, int ifindex, NMPlatformMacvlanProperties *props); - gboolean (*vxlan_get_properties) (NMPlatform *, int ifindex, NMPlatformVxlanProperties *props); - gboolean (*gre_get_properties) (NMPlatform *, int ifindex, NMPlatformGreProperties *props); gboolean (*wifi_get_capabilities) (NMPlatform *, int ifindex, NMDeviceWifiCapabilities *caps); gboolean (*wifi_get_bssid) (NMPlatform *, int ifindex, guint8 *bssid); @@ -672,19 +706,35 @@ char *nm_platform_master_get_option (NMPlatform *self, int ifindex, const char * gboolean nm_platform_slave_set_option (NMPlatform *self, int ifindex, const char *option, const char *value); char *nm_platform_slave_get_option (NMPlatform *self, int ifindex, const char *option); +const NMPObject *nm_platform_link_get_lnk (NMPlatform *self, int ifindex, NMLinkType link_type, const NMPlatformLink **out_link); +const NMPlatformLnkGre *nm_platform_link_get_lnk_gre (NMPlatform *self, int ifindex, const NMPlatformLink **out_link); +const NMPlatformLnkInfiniband *nm_platform_link_get_lnk_infiniband (NMPlatform *self, int ifindex, const NMPlatformLink **out_link); +const NMPlatformLnkMacvlan *nm_platform_link_get_lnk_macvlan (NMPlatform *self, int ifindex, const NMPlatformLink **out_link); +const NMPlatformLnkVlan *nm_platform_link_get_lnk_vlan (NMPlatform *self, int ifindex, const NMPlatformLink **out_link); +const NMPlatformLnkVxlan *nm_platform_link_get_lnk_vxlan (NMPlatform *self, int ifindex, const NMPlatformLink **out_link); + NMPlatformError nm_platform_vlan_add (NMPlatform *self, const char *name, int parent, int vlanid, guint32 vlanflags, NMPlatformLink *out_link); -gboolean nm_platform_vlan_get_info (NMPlatform *self, int ifindex, int *parent, int *vlanid); gboolean nm_platform_vlan_set_ingress_map (NMPlatform *self, int ifindex, int from, int to); gboolean nm_platform_vlan_set_egress_map (NMPlatform *self, int ifindex, int from, int to); +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); + NMPlatformError nm_platform_infiniband_partition_add (NMPlatform *self, int parent, int p_key, NMPlatformLink *out_link); -gboolean nm_platform_infiniband_get_info (NMPlatform *self, int ifindex, int *parent, int *p_key, const char **mode); +gboolean nm_platform_infiniband_get_properties (NMPlatform *self, int ifindex, int *parent, int *p_key, const char **mode); -gboolean nm_platform_veth_get_properties (NMPlatform *self, int ifindex, NMPlatformVethProperties *properties); +gboolean nm_platform_veth_get_properties (NMPlatform *self, int ifindex, int *out_peer_ifindex); gboolean nm_platform_tun_get_properties (NMPlatform *self, int ifindex, NMPlatformTunProperties *properties); -gboolean nm_platform_macvlan_get_properties (NMPlatform *self, int ifindex, NMPlatformMacvlanProperties *props); -gboolean nm_platform_vxlan_get_properties (NMPlatform *self, int ifindex, NMPlatformVxlanProperties *props); -gboolean nm_platform_gre_get_properties (NMPlatform *self, int ifindex, NMPlatformGreProperties *props); + +gboolean nm_platform_tun_get_properties_ifname (NMPlatform *platform, const char *ifname, NMPlatformTunProperties *props); gboolean nm_platform_wifi_get_capabilities (NMPlatform *self, int ifindex, NMDeviceWifiCapabilities *caps); gboolean nm_platform_wifi_get_bssid (NMPlatform *self, int ifindex, guint8 *bssid); @@ -701,10 +751,8 @@ guint32 nm_platform_mesh_get_channel (NMPlatform *self, int ifindex); gboolean nm_platform_mesh_set_channel (NMPlatform *self, int ifindex, guint32 channel); gboolean nm_platform_mesh_set_ssid (NMPlatform *self, int ifindex, const guint8 *ssid, gsize len); -in_addr_t nm_platform_ip4_address_get_peer (const NMPlatformIP4Address *addr); +void nm_platform_ip4_address_set_addr (NMPlatformIP4Address *addr, in_addr_t address, int plen); const struct in6_addr *nm_platform_ip6_address_get_peer (const NMPlatformIP6Address *addr); -in_addr_t nm_platform_ip4_address_get_peer_net (const NMPlatformIP4Address *addr); -gboolean nm_platform_ip4_address_equal_peer_net (const NMPlatformIP4Address *addr1, const NMPlatformIP4Address *addr2); const NMPlatformIP4Address *nm_platform_ip4_address_get (NMPlatform *self, int ifindex, in_addr_t address, int plen, in_addr_t peer_address); const NMPlatformIP6Address *nm_platform_ip6_address_get (NMPlatform *self, int ifindex, struct in6_addr address, int plen); @@ -745,24 +793,41 @@ gboolean nm_platform_ip6_route_add (NMPlatform *self, int ifindex, NMIPConfigSou gboolean nm_platform_ip4_route_delete (NMPlatform *self, int ifindex, in_addr_t network, int plen, guint32 metric); gboolean nm_platform_ip6_route_delete (NMPlatform *self, int ifindex, struct in6_addr network, int plen, guint32 metric); -const char *nm_platform_link_to_string (const NMPlatformLink *link); -const char *nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address); -const char *nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address); -const char *nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route); -const char *nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route); +const char *nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len); +const char *nm_platform_lnk_gre_to_string (const NMPlatformLnkGre *lnk, char *buf, gsize len); +const char *nm_platform_lnk_infiniband_to_string (const NMPlatformLnkInfiniband *lnk, char *buf, gsize len); +const char *nm_platform_lnk_macvlan_to_string (const NMPlatformLnkMacvlan *lnk, char *buf, gsize len); +const char *nm_platform_lnk_vlan_to_string (const NMPlatformLnkVlan *lnk, char *buf, gsize len); +const char *nm_platform_lnk_vxlan_to_string (const NMPlatformLnkVxlan *lnk, char *buf, gsize len); +const char *nm_platform_ip4_address_to_string (const NMPlatformIP4Address *address, char *buf, gsize len); +const char *nm_platform_ip6_address_to_string (const NMPlatformIP6Address *address, char *buf, gsize len); +const char *nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsize len); +const char *nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsize len); + +const char *nm_platform_vlan_qos_mapping_to_string (const char *name, + const NMVlanQosMapping *map, + gsize n_map, + char *buf, + gsize len); int nm_platform_link_cmp (const NMPlatformLink *a, const NMPlatformLink *b); +int nm_platform_lnk_gre_cmp (const NMPlatformLnkGre *a, const NMPlatformLnkGre *b); +int nm_platform_lnk_infiniband_cmp (const NMPlatformLnkInfiniband *a, const NMPlatformLnkInfiniband *b); +int nm_platform_lnk_macvlan_cmp (const NMPlatformLnkMacvlan *a, const NMPlatformLnkMacvlan *b); +int nm_platform_lnk_vlan_cmp (const NMPlatformLnkVlan *a, const NMPlatformLnkVlan *b); +int nm_platform_lnk_vxlan_cmp (const NMPlatformLnkVxlan *a, const NMPlatformLnkVxlan *b); int nm_platform_ip4_address_cmp (const NMPlatformIP4Address *a, const NMPlatformIP4Address *b); int nm_platform_ip6_address_cmp (const NMPlatformIP6Address *a, const NMPlatformIP6Address *b); int nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route *b); int nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route *b); -gboolean nm_platform_check_support_libnl_extended_ifa_flags (void); -gboolean nm_platform_check_support_libnl_link_netnsid (void); gboolean nm_platform_check_support_kernel_extended_ifa_flags (NMPlatform *self); gboolean nm_platform_check_support_user_ipv6ll (NMPlatform *self); -void nm_platform_addr_flags2str (int flags, char *buf, size_t size); +const char *nm_platform_link_flags2str (unsigned flags, char *buf, gsize len); +const char *nm_platform_link_inet6_addrgenmode2str (guint8 mode, char *buf, gsize len); +const char *nm_platform_addr_flags2str (unsigned flags, char *buf, gsize len); +const char *nm_platform_route_scope2str (int scope, char *buf, gsize len); int nm_platform_ip_address_cmp_expiry (const NMPlatformIPAddress *a, const NMPlatformIPAddress *b); diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index a8264827be..fbdc88771c 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -82,6 +82,42 @@ _id_hash_ip6_addr (const struct in6_addr *addr) return hash; } +static int +_vlan_xgress_qos_mappings_cmp (guint n_map, + const NMVlanQosMapping *map1, + const NMVlanQosMapping *map2) +{ + guint i; + + for (i = 0; i < n_map; i++) { + if (map1[i].from != map2[i].from) + return map1[i].from < map2[i].from ? -1 : 1; + if (map1[i].to != map2[i].to) + return map1[i].to < map2[i].to ? -1 : 1; + } + return 0; +} + +static void +_vlan_xgress_qos_mappings_cpy (guint *dst_n_map, + const NMVlanQosMapping **dst_map, + guint src_n_map, + const NMVlanQosMapping *src_map) +{ + if (src_n_map == 0) { + g_clear_pointer (dst_map, g_free); + *dst_n_map = 0; + } else if ( src_n_map != *dst_n_map + || _vlan_xgress_qos_mappings_cmp (src_n_map, *dst_map, src_map) != 0) { + g_clear_pointer (dst_map, g_free); + *dst_n_map = src_n_map; + if (src_n_map > 0) + *dst_map = g_memdup (src_map, sizeof (*src_map) * src_n_map); + } +} + +/******************************************************************/ + static const char * _link_get_driver (GUdevDevice *udev_device, const char *kind, const char *ifname) { @@ -207,6 +243,14 @@ static void _vt_cmd_obj_dispose_link (NMPObject *obj) { g_clear_object (&obj->_link.udev.device); + nmp_object_unref (obj->_link.netlink.lnk); +} + +static void +_vt_cmd_obj_dispose_lnk_vlan (NMPObject *obj) +{ + g_free ((gpointer) obj->_lnk_vlan.ingress_qos_map); + g_free ((gpointer) obj->_lnk_vlan.egress_qos_map); } static NMPObject * @@ -274,10 +318,16 @@ nmp_object_stackinit (NMPObject *obj, NMPObjectType obj_type, const NMPlatformOb const NMPObject * nmp_object_stackinit_id (NMPObject *obj, const NMPObject *src) { + const NMPClass *klass; + nm_assert (NMP_OBJECT_IS_VALID (src)); nm_assert (obj); - NMP_OBJECT_GET_CLASS (src)->cmd_obj_stackinit_id (obj, src); + klass = NMP_OBJECT_GET_CLASS (src); + if (!klass->cmd_obj_stackinit_id) + nmp_object_stackinit (obj, klass->obj_type, NULL); + else + klass->cmd_obj_stackinit_id (obj, src); return obj; } @@ -370,50 +420,131 @@ const char * nmp_object_to_string (const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size) { const NMPClass *klass; - char buf2[sizeof (_nm_platform_to_string_buffer)]; - char buf3[sizeof (_nm_platform_to_string_buffer)]; - const char *str; - - if (!buf) { - buf = _nm_platform_to_string_buffer; - buf_size = sizeof (_nm_platform_to_string_buffer); - } + char buf2[sizeof (_nm_utils_to_string_buffer)]; - if (!obj) { - g_strlcpy (buf, "NULL", buf_size); + if (!nm_utils_to_string_buffer_init_null (obj, &buf, &buf_size)) return buf; - } g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj), NULL); klass = NMP_OBJECT_GET_CLASS (obj); + if (klass->cmd_obj_to_string) + return klass->cmd_obj_to_string (obj, to_string_mode, buf, buf_size); + switch (to_string_mode) { case NMP_OBJECT_TO_STRING_ID: + if (!klass->cmd_plobj_to_string_id) { + g_snprintf (buf, buf_size, "%p", obj); + return buf; + } return klass->cmd_plobj_to_string_id (&obj->object, buf, buf_size); case NMP_OBJECT_TO_STRING_ALL: - g_strlcpy (buf2, NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object), sizeof (buf2)); + g_snprintf (buf, buf_size, + "[%s,%p,%d,%ccache,%calive,%cvisible; %s]", + klass->obj_type_name, obj, obj->_ref_count, + obj->is_cached ? '+' : '-', + nmp_object_is_alive (obj) ? '+' : '-', + nmp_object_is_visible (obj) ? '+' : '-', + NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object, buf2, sizeof (buf2))); + return buf; + case NMP_OBJECT_TO_STRING_PUBLIC: + NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object, buf, buf_size); + return buf; + default: + g_return_val_if_reached ("ERROR"); + } +} - if (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_LINK) { - g_snprintf (buf3, sizeof (buf3), - ",%cin-nl,%p", - obj->_link.netlink.is_in_netlink ? '+' : '-', - obj->_link.udev.device); +static const char * +_vt_cmd_obj_to_string_link (const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size) +{ + const NMPClass *klass = NMP_OBJECT_GET_CLASS (obj); + char buf2[sizeof (_nm_utils_to_string_buffer)]; + char buf3[sizeof (_nm_utils_to_string_buffer)]; + + switch (to_string_mode) { + case NMP_OBJECT_TO_STRING_ID: + return klass->cmd_plobj_to_string_id (&obj->object, buf, buf_size); + case NMP_OBJECT_TO_STRING_ALL: + g_snprintf (buf, buf_size, + "[%s,%p,%d,%ccache,%calive,%cvisible,%cin-nl,%p; %s]", + klass->obj_type_name, obj, obj->_ref_count, + obj->is_cached ? '+' : '-', + nmp_object_is_alive (obj) ? '+' : '-', + nmp_object_is_visible (obj) ? '+' : '-', + obj->_link.netlink.is_in_netlink ? '+' : '-', + obj->_link.udev.device, + nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, buf2, sizeof (buf2))); + return buf; + case NMP_OBJECT_TO_STRING_PUBLIC: + if (obj->_link.netlink.lnk) { + NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object, buf2, sizeof (buf2)); + nmp_object_to_string (obj->_link.netlink.lnk, NMP_OBJECT_TO_STRING_PUBLIC, buf3, sizeof (buf3)); + g_snprintf (buf, buf_size, + "%s; %s", + buf2, buf3); } else - buf3[0] = '\0'; + NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object, buf, buf_size); + return buf; + default: + g_return_val_if_reached ("ERROR"); + } +} + +static const char * +_vt_cmd_obj_to_string_lnk_vlan (const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size) +{ + const NMPClass *klass = NMP_OBJECT_GET_CLASS (obj); + char buf2[sizeof (_nm_utils_to_string_buffer)]; + char *b; + gsize l; + + klass = NMP_OBJECT_GET_CLASS (obj); + + switch (to_string_mode) { + case NMP_OBJECT_TO_STRING_ID: + g_snprintf (buf, buf_size, "%p", obj); + return buf; + case NMP_OBJECT_TO_STRING_ALL: g_snprintf (buf, buf_size, - "[%s,%p,%d,%ccache,%calive,%cvisible%s; %s]", + "[%s,%p,%d,%ccache,%calive,%cvisible; %s]", klass->obj_type_name, obj, obj->_ref_count, obj->is_cached ? '+' : '-', nmp_object_is_alive (obj) ? '+' : '-', nmp_object_is_visible (obj) ? '+' : '-', - buf3, buf2); + nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_PUBLIC, buf2, sizeof (buf2))); return buf; case NMP_OBJECT_TO_STRING_PUBLIC: - str = NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object); - if (str != buf) - g_strlcpy (buf, str, buf_size); + NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_string (&obj->object, buf, buf_size); + + b = buf; + l = strlen (b); + b += l; + buf_size -= l; + + if (obj->_lnk_vlan.n_ingress_qos_map) { + nm_platform_vlan_qos_mapping_to_string (" ingress-qos-map", + obj->_lnk_vlan.ingress_qos_map, + obj->_lnk_vlan.n_ingress_qos_map, + b, + buf_size); + l = strlen (b); + b += l; + buf_size -= l; + } + if (obj->_lnk_vlan.n_egress_qos_map) { + nm_platform_vlan_qos_mapping_to_string (" egress-qos-map", + obj->_lnk_vlan.egress_qos_map, + obj->_lnk_vlan.n_egress_qos_map, + b, + buf_size); + l = strlen (b); + b += l; + buf_size -= l; + } + return buf; default: g_return_val_if_reached ("ERROR"); @@ -436,8 +567,8 @@ _vt_cmd_plobj_to_string_id_##type (const NMPlatformObject *_obj, char *buf, gsiz } _vt_cmd_plobj_to_string_id (link, NMPlatformLink, "%d", obj->ifindex); _vt_cmd_plobj_to_string_id (ip4_address, NMPlatformIP4Address, "%d: %s/%d%s%s", obj->ifindex, nm_utils_inet4_ntop ( obj->address, buf1), obj->plen, - obj->peer_address && obj->peer_address != obj->address ? "," : "", - obj->peer_address && obj->peer_address != obj->address ? nm_utils_inet4_ntop (nm_platform_ip4_address_get_peer_net (obj), buf2) : ""); + obj->peer_address != obj->address ? "," : "", + obj->peer_address != obj->address ? nm_utils_inet4_ntop (obj->peer_address & nm_utils_ip4_prefix_to_netmask (obj->plen), buf2) : ""); _vt_cmd_plobj_to_string_id (ip6_address, NMPlatformIP6Address, "%d: %s", obj->ifindex, nm_utils_inet6_ntop (&obj->address, buf1)); _vt_cmd_plobj_to_string_id (ip4_route, NMPlatformIP4Route, "%d: %s/%d %d", obj->ifindex, nm_utils_inet4_ntop ( obj->network, buf1), obj->plen, obj->metric); _vt_cmd_plobj_to_string_id (ip6_route, NMPlatformIP6Route, "%d: %s/%d %d", obj->ifindex, nm_utils_inet6_ntop (&obj->network, buf1), obj->plen, obj->metric); @@ -445,6 +576,8 @@ _vt_cmd_plobj_to_string_id (ip6_route, NMPlatformIP6Route, "%d: %s/%d %d", int nmp_object_cmp (const NMPObject *obj1, const NMPObject *obj2) { + const NMPClass *klass1, *klass2; + if (obj1 == obj2) return 0; if (!obj1) @@ -455,49 +588,72 @@ nmp_object_cmp (const NMPObject *obj1, const NMPObject *obj2) g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj1), -1); g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj2), 1); - if (NMP_OBJECT_GET_CLASS (obj1) != NMP_OBJECT_GET_CLASS (obj2)) - return NMP_OBJECT_GET_CLASS (obj1) < NMP_OBJECT_GET_CLASS (obj2) ? -1 : 1; + klass1 = NMP_OBJECT_GET_CLASS (obj1); + klass2 = NMP_OBJECT_GET_CLASS (obj2); + + if (klass1 != klass2) + return klass1->obj_type < klass2->obj_type ? -1 : 1; - return NMP_OBJECT_GET_CLASS (obj1)->cmd_plobj_cmp (&obj1->object, &obj2->object); + if (klass1->cmd_obj_cmp) + return klass1->cmd_obj_cmp (obj1, obj2); + return klass1->cmd_plobj_cmp (&obj1->object, &obj2->object); } -gboolean -nmp_object_equal (const NMPObject *obj1, const NMPObject *obj2) +static int +_vt_cmd_obj_cmp_link (const NMPObject *obj1, const NMPObject *obj2) { - const NMPClass *klass; + int i; - g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj1), FALSE); - g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj2), FALSE); + i = nm_platform_link_cmp (&obj1->link, &obj2->link); + if (i) + return i; + if (obj1->_link.netlink.is_in_netlink != obj2->_link.netlink.is_in_netlink) + return obj1->_link.netlink.is_in_netlink ? -1 : 1; + i = nmp_object_cmp (obj1->_link.netlink.lnk, obj2->_link.netlink.lnk); + if (i) + return i; + if (obj1->_link.udev.device != obj2->_link.udev.device) { + if (!obj1->_link.udev.device) + return -1; + if (!obj2->_link.udev.device) + return 1; + + /* Only compare based on pointer values. That is ugly because it's not a + * stable sort order, but probably udev gives us always the same GUdevDevice + * instance. + * + * Have this check as very last. */ + return (obj1->_link.udev.device < obj2->_link.udev.device) ? -1 : 1; + } + return 0; +} - if (obj1 == obj2) - return TRUE; +static int +_vt_cmd_obj_cmp_lnk_vlan (const NMPObject *obj1, const NMPObject *obj2) +{ + int c; - klass = NMP_OBJECT_GET_CLASS (obj1); + c = nm_platform_lnk_vlan_cmp (&obj1->lnk_vlan, &obj2->lnk_vlan); + if (c) + return c; - if (klass != NMP_OBJECT_GET_CLASS (obj2)) - return FALSE; + if (obj1->_lnk_vlan.n_ingress_qos_map != obj2->_lnk_vlan.n_ingress_qos_map) + return obj1->_lnk_vlan.n_ingress_qos_map < obj2->_lnk_vlan.n_ingress_qos_map ? -1 : 1; + if (obj1->_lnk_vlan.n_egress_qos_map != obj2->_lnk_vlan.n_egress_qos_map) + return obj1->_lnk_vlan.n_egress_qos_map < obj2->_lnk_vlan.n_egress_qos_map ? -1 : 1; - return klass->cmd_obj_equal (obj1, obj2); -} + c = _vlan_xgress_qos_mappings_cmp (obj1->_lnk_vlan.n_ingress_qos_map, obj1->_lnk_vlan.ingress_qos_map, obj2->_lnk_vlan.ingress_qos_map); + if (c) + return c; + c = _vlan_xgress_qos_mappings_cmp (obj1->_lnk_vlan.n_egress_qos_map, obj1->_lnk_vlan.egress_qos_map, obj2->_lnk_vlan.egress_qos_map); -static gboolean -_vt_cmd_obj_equal_plain (const NMPObject *obj1, const NMPObject *obj2) -{ - return NMP_OBJECT_GET_CLASS (obj1)->cmd_plobj_cmp (&obj1->object, &obj2->object) == 0; + return c; } -static gboolean -_vt_cmd_obj_equal_link (const NMPObject *obj1, const NMPObject *obj2) +gboolean +nmp_object_equal (const NMPObject *obj1, const NMPObject *obj2) { - const NMPClass *klass = NMP_OBJECT_GET_CLASS (obj1); - - if (klass->cmd_plobj_cmp (&obj1->object, &obj2->object) != 0) - return FALSE; - if (obj1->_link.netlink.is_in_netlink != obj2->_link.netlink.is_in_netlink) - return FALSE; - if (obj1->_link.udev.device != obj2->_link.udev.device) - return FALSE; - return TRUE; + return nmp_object_cmp (obj1, obj2) == 0; } /* @src is a const object, which is not entirely correct for link types, where @@ -516,13 +672,48 @@ nmp_object_copy (NMPObject *dst, const NMPObject *src, gboolean id_only) g_return_if_fail (klass == NMP_OBJECT_GET_CLASS (src)); - if (id_only) - klass->cmd_plobj_id_copy (&dst->object, &src->object); - else + if (id_only) { + if (klass->cmd_plobj_id_copy) + klass->cmd_plobj_id_copy (&dst->object, &src->object); + } else if (klass->cmd_obj_copy) klass->cmd_obj_copy (dst, src); + else + memcpy (&dst->object, &src->object, klass->sizeof_data); } } +static void +_vt_cmd_obj_copy_link (NMPObject *dst, const NMPObject *src) +{ + if (dst->_link.udev.device != src->_link.udev.device) { + if (src->_link.udev.device) + g_object_ref (src->_link.udev.device); + if (dst->_link.udev.device) + g_object_unref (dst->_link.udev.device); + } + if (dst->_link.netlink.lnk != src->_link.netlink.lnk) { + if (src->_link.netlink.lnk) + nmp_object_ref (src->_link.netlink.lnk); + if (dst->_link.netlink.lnk) + nmp_object_unref (dst->_link.netlink.lnk); + } + dst->_link = src->_link; +} + +static void +_vt_cmd_obj_copy_lnk_vlan (NMPObject *dst, const NMPObject *src) +{ + dst->lnk_vlan = src->lnk_vlan; + _vlan_xgress_qos_mappings_cpy (&dst->_lnk_vlan.n_ingress_qos_map, + &dst->_lnk_vlan.ingress_qos_map, + src->_lnk_vlan.n_ingress_qos_map, + src->_lnk_vlan.ingress_qos_map); + _vlan_xgress_qos_mappings_cpy (&dst->_lnk_vlan.n_egress_qos_map, + &dst->_lnk_vlan.egress_qos_map, + src->_lnk_vlan.n_egress_qos_map, + src->_lnk_vlan.egress_qos_map); +} + #define _vt_cmd_plobj_id_copy(type, plat_type, cmd) \ static void \ _vt_cmd_plobj_id_copy_##type (NMPlatformObject *_dst, const NMPlatformObject *_src) \ @@ -557,24 +748,6 @@ _vt_cmd_plobj_id_copy (ip6_route, NMPlatformIP6Route, { dst->network = src->network; }); -static void -_vt_cmd_obj_copy_plain (NMPObject *dst, const NMPObject *src) -{ - memcpy (&dst->object, &src->object, NMP_OBJECT_GET_CLASS (dst)->sizeof_data); -} - -static void -_vt_cmd_obj_copy_link (NMPObject *dst, const NMPObject *src) -{ - if (dst->_link.udev.device != src->_link.udev.device) { - if (dst->_link.udev.device) - g_object_unref (dst->_link.udev.device); - if (src->_link.udev.device) - g_object_ref (src->_link.udev.device); - } - dst->_link = src->_link; -} - /* Uses internally nmp_object_copy(), hence it also violates the const * promise for @obj. * */ @@ -608,6 +781,7 @@ nmp_object_id_equal (const NMPObject *obj1, const NMPObject *obj2) klass = NMP_OBJECT_GET_CLASS (obj1); return klass == NMP_OBJECT_GET_CLASS (obj2) + && klass->cmd_plobj_id_equal && klass->cmd_plobj_id_equal (&obj1->object, &obj2->object); } @@ -627,7 +801,7 @@ _vt_cmd_plobj_id_equal (ip4_address, NMPlatformIP4Address, && obj1->address == obj2->address /* for IPv4 addresses, you can add the same local address with differing peer-adddress * (IFA_ADDRESS), provided that their net-part differs. */ - && nm_platform_ip4_address_equal_peer_net (obj1, obj2)); + && ((obj1->peer_address ^ obj2->peer_address) & nm_utils_ip4_prefix_to_netmask (obj1->plen)) == 0); _vt_cmd_plobj_id_equal (ip6_address, NMPlatformIP6Address, obj1->ifindex == obj2->ifindex /* for IPv6 addresses, the prefix length is not part of the primary identifier. */ @@ -646,11 +820,20 @@ _vt_cmd_plobj_id_equal (ip6_route, NMPlatformIP6Route, guint nmp_object_id_hash (const NMPObject *obj) { + const NMPClass *klass; + if (!obj) return 0; g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj), 0); - return NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_id_hash (&obj->object); + + klass = NMP_OBJECT_GET_CLASS (obj); + + if (klass->cmd_plobj_id_hash) + return klass->cmd_plobj_id_hash (&obj->object); + + /* unhashable objects implement pointer equality. */ + return g_direct_hash (obj); } #define _vt_cmd_plobj_id_hash(type, plat_type, cmd) \ @@ -673,7 +856,7 @@ _vt_cmd_plobj_id_hash (ip4_address, NMPlatformIP4Address, { hash = hash * 33 + ((guint) obj->address); /* for IPv4 we must also consider the net-part of the peer-address (IFA_ADDRESS) */ - hash = hash * 33 + ((guint) (nm_platform_ip4_address_get_peer_net (obj))); + hash = hash * 33 + ((guint) (obj->peer_address & nm_utils_ip4_prefix_to_netmask (obj->plen))); }) _vt_cmd_plobj_id_hash (ip6_address, NMPlatformIP6Address, { hash = (guint) 2907861637u; @@ -699,11 +882,15 @@ _vt_cmd_plobj_id_hash (ip6_route, NMPlatformIP6Route, { gboolean nmp_object_is_alive (const NMPObject *obj) { + const NMPClass *klass; + /* for convenience, allow NULL. */ if (!obj) return FALSE; - return NMP_OBJECT_GET_CLASS (obj)->cmd_obj_is_alive (obj); + klass = NMP_OBJECT_GET_CLASS (obj); + return !klass->cmd_obj_is_alive + || klass->cmd_obj_is_alive (obj); } static gboolean @@ -741,13 +928,17 @@ _vt_cmd_obj_is_alive_ipx_route (const NMPObject *obj) gboolean nmp_object_is_visible (const NMPObject *obj) - { + const NMPClass *klass; + /* for convenience, allow NULL. */ if (!obj) return FALSE; - return NMP_OBJECT_GET_CLASS (obj)->cmd_obj_is_visible (obj); + klass = NMP_OBJECT_GET_CLASS (obj); + + return !klass->cmd_obj_is_visible + || klass->cmd_obj_is_visible (obj); } static gboolean @@ -774,46 +965,6 @@ _vt_cmd_obj_is_visible_ipx_route (const NMPObject *obj) /******************************************************************/ -/** - * nmp_object_from_nl: - * @platform: platform instance (needed to lookup sysctl) - * @nlo: - * @id_only: if %TRUE, only fill the id fields of the object and leave the - * other fields unset. This is useful to create a needle to lookup a matching - * item in the cache. - * @complete_from_cache: sometimes the netlink object doesn't contain all information. - * If true, look them up in the cache and preserve the original value. - * - * Convert a libnl object to a platform object. - * Returns: a NMPObject containing @nlo. If @id_only is %TRUE, only the id fields - * are defined. - **/ -NMPObject * -nmp_object_from_nl (NMPlatform *platform, const struct nl_object *nlo, gboolean id_only, gboolean complete_from_cache) -{ - NMPObjectType obj_type = _nlo_get_object_type (nlo); - NMPObject *obj; - - if (obj_type == NMP_OBJECT_TYPE_UNKNOWN) - return NULL; - - obj = nmp_object_new (obj_type, NULL); - - if (!NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_init_from_nl (platform, &obj->object, nlo, id_only, complete_from_cache)) { - nmp_object_unref (obj); - return NULL; - } - return obj; -} - -struct nl_object * -nmp_object_to_nl (NMPlatform *platform, const NMPObject *obj, gboolean id_only) -{ - return NMP_OBJECT_GET_CLASS (obj)->cmd_plobj_to_nl (platform, &obj->object, id_only); -} - -/******************************************************************/ - gboolean nmp_cache_id_equal (const NMPCacheId *a, const NMPCacheId *b) { @@ -939,17 +1090,12 @@ _nmp_object_init_cache_id (const NMPObject *obj, NMPCacheIdType id_type, NMPCach *out_id = NULL; return TRUE; default: - return klass->cmd_obj_init_cache_id (obj, id_type, id, out_id); + return klass->cmd_obj_init_cache_id + && klass->cmd_obj_init_cache_id (obj, id_type, id, out_id); } } static gboolean -_vt_cmd_obj_init_cache_id_link (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) -{ - return FALSE; -} - -static gboolean _vt_cmd_obj_init_cache_id_ipx_address (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id) { switch (id_type) { @@ -1505,8 +1651,8 @@ nmp_cache_update_netlink (NMPCache *cache, NMPObject *obj, NMPObject **out_obj, * We could add efficient reverse lookup by adding a reverse index to NMMultiIndex. But that * also adds some cost to support an (uncommon?) usage pattern. * - * Instead we just don't support it. Actually, we expect the user to - * create a new instance with nmp_object_from_nl(). That is what nmp_cache_update_netlink(). + * Instead we just don't support it, instead we expect the user to + * create a new instance from netlink. * * TL;DR: a cached object must never be modified. */ @@ -1783,24 +1929,21 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .sizeof_data = sizeof (NMPObjectLink), .sizeof_public = sizeof (NMPlatformLink), .obj_type_name = "link", - .nl_type = "route/link", .addr_family = AF_UNSPEC, .rtm_gettype = RTM_GETLINK, .signal_type = NM_PLATFORM_SIGNAL_LINK_CHANGED, - .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_link, - .cmd_obj_equal = _vt_cmd_obj_equal_link, + .cmd_obj_cmp = _vt_cmd_obj_cmp_link, .cmd_obj_copy = _vt_cmd_obj_copy_link, .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_link, .cmd_obj_dispose = _vt_cmd_obj_dispose_link, .cmd_obj_is_alive = _vt_cmd_obj_is_alive_link, .cmd_obj_is_visible = _vt_cmd_obj_is_visible_link, - .cmd_plobj_init_from_nl = _nmp_vt_cmd_plobj_init_from_nl_link, - .cmd_plobj_to_nl = _nmp_vt_cmd_plobj_to_nl_link, + .cmd_obj_to_string = _vt_cmd_obj_to_string_link, .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_link, .cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_link, .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_link, .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_link, - .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj)) nm_platform_link_to_string, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_link_to_string, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_link_cmp, }, [NMP_OBJECT_TYPE_IP4_ADDRESS - 1] = { @@ -1808,23 +1951,18 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .sizeof_data = sizeof (NMPObjectIP4Address), .sizeof_public = sizeof (NMPlatformIP4Address), .obj_type_name = "ip4-address", - .nl_type = "route/addr", .addr_family = AF_INET, .rtm_gettype = RTM_GETADDR, .signal_type = NM_PLATFORM_SIGNAL_IP4_ADDRESS_CHANGED, .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_address, - .cmd_obj_equal = _vt_cmd_obj_equal_plain, - .cmd_obj_copy = _vt_cmd_obj_copy_plain, .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip4_address, .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_address, .cmd_obj_is_visible = _vt_cmd_obj_is_visible_ipx_address, - .cmd_plobj_init_from_nl = _nmp_vt_cmd_plobj_init_from_nl_ip4_address, - .cmd_plobj_to_nl = _nmp_vt_cmd_plobj_to_nl_ip4_address, .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip4_address, .cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_ip4_address, .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip4_address, .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip4_address, - .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj)) nm_platform_ip4_address_to_string, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip4_address_to_string, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip4_address_cmp, }, [NMP_OBJECT_TYPE_IP6_ADDRESS - 1] = { @@ -1832,23 +1970,18 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .sizeof_data = sizeof (NMPObjectIP6Address), .sizeof_public = sizeof (NMPlatformIP6Address), .obj_type_name = "ip6-address", - .nl_type = "route/addr", .addr_family = AF_INET6, .rtm_gettype = RTM_GETADDR, .signal_type = NM_PLATFORM_SIGNAL_IP6_ADDRESS_CHANGED, .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_address, - .cmd_obj_equal = _vt_cmd_obj_equal_plain, - .cmd_obj_copy = _vt_cmd_obj_copy_plain, .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip6_address, .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_address, .cmd_obj_is_visible = _vt_cmd_obj_is_visible_ipx_address, - .cmd_plobj_init_from_nl = _nmp_vt_cmd_plobj_init_from_nl_ip6_address, - .cmd_plobj_to_nl = _nmp_vt_cmd_plobj_to_nl_ip6_address, .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip6_address, .cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_ip6_address, .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip6_address, .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip6_address, - .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj)) nm_platform_ip6_address_to_string, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip6_address_to_string, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip6_address_cmp }, [NMP_OBJECT_TYPE_IP4_ROUTE - 1] = { @@ -1856,23 +1989,18 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .sizeof_data = sizeof (NMPObjectIP4Route), .sizeof_public = sizeof (NMPlatformIP4Route), .obj_type_name = "ip4-route", - .nl_type = "route/route", .addr_family = AF_INET, .rtm_gettype = RTM_GETROUTE, .signal_type = NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_route, - .cmd_obj_equal = _vt_cmd_obj_equal_plain, - .cmd_obj_copy = _vt_cmd_obj_copy_plain, .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip4_route, .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route, .cmd_obj_is_visible = _vt_cmd_obj_is_visible_ipx_route, - .cmd_plobj_init_from_nl = _nmp_vt_cmd_plobj_init_from_nl_ip4_route, - .cmd_plobj_to_nl = _nmp_vt_cmd_plobj_to_nl_ip4_route, .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip4_route, .cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_ip4_route, .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip4_route, .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip4_route, - .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj)) nm_platform_ip4_route_to_string, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip4_route_to_string, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip4_route_cmp, }, [NMP_OBJECT_TYPE_IP6_ROUTE - 1] = { @@ -1880,24 +2008,68 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .sizeof_data = sizeof (NMPObjectIP6Route), .sizeof_public = sizeof (NMPlatformIP6Route), .obj_type_name = "ip6-route", - .nl_type = "route/route", .addr_family = AF_INET6, .rtm_gettype = RTM_GETROUTE, .signal_type = NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, .cmd_obj_init_cache_id = _vt_cmd_obj_init_cache_id_ipx_route, - .cmd_obj_equal = _vt_cmd_obj_equal_plain, - .cmd_obj_copy = _vt_cmd_obj_copy_plain, .cmd_obj_stackinit_id = _vt_cmd_obj_stackinit_id_ip6_route, .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route, .cmd_obj_is_visible = _vt_cmd_obj_is_visible_ipx_route, - .cmd_plobj_init_from_nl = _nmp_vt_cmd_plobj_init_from_nl_ip6_route, - .cmd_plobj_to_nl = _nmp_vt_cmd_plobj_to_nl_ip6_route, .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip6_route, .cmd_plobj_id_equal = _vt_cmd_plobj_id_equal_ip6_route, .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip6_route, .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip6_route, - .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj)) nm_platform_ip6_route_to_string, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip6_route_to_string, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip6_route_cmp, }, + [NMP_OBJECT_TYPE_LNK_GRE - 1] = { + .obj_type = NMP_OBJECT_TYPE_LNK_GRE, + .sizeof_data = sizeof (NMPObjectLnkGre), + .sizeof_public = sizeof (NMPlatformLnkGre), + .obj_type_name = "gre", + .lnk_link_type = NM_LINK_TYPE_GRE, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_gre_to_string, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_gre_cmp, + }, + [NMP_OBJECT_TYPE_LNK_INFINIBAND - 1] = { + .obj_type = NMP_OBJECT_TYPE_LNK_INFINIBAND, + .sizeof_data = sizeof (NMPObjectLnkInfiniband), + .sizeof_public = sizeof (NMPlatformLnkInfiniband), + .obj_type_name = "infiniband", + .lnk_link_type = NM_LINK_TYPE_INFINIBAND, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_infiniband_to_string, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_infiniband_cmp, + }, + [NMP_OBJECT_TYPE_LNK_MACVLAN - 1] = { + .obj_type = NMP_OBJECT_TYPE_LNK_MACVLAN, + .sizeof_data = sizeof (NMPObjectLnkMacvlan), + .sizeof_public = sizeof (NMPlatformLnkMacvlan), + .obj_type_name = "macvlan", + .lnk_link_type = NM_LINK_TYPE_MACVLAN, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_macvlan_to_string, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_macvlan_cmp, + }, + [NMP_OBJECT_TYPE_LNK_VLAN - 1] = { + .obj_type = NMP_OBJECT_TYPE_LNK_VLAN, + .sizeof_data = sizeof (NMPObjectLnkVlan), + .sizeof_public = sizeof (NMPlatformLnkVlan), + .obj_type_name = "vlan", + .lnk_link_type = NM_LINK_TYPE_VLAN, + .cmd_obj_cmp = _vt_cmd_obj_cmp_lnk_vlan, + .cmd_obj_copy = _vt_cmd_obj_copy_lnk_vlan, + .cmd_obj_dispose = _vt_cmd_obj_dispose_lnk_vlan, + .cmd_obj_to_string = _vt_cmd_obj_to_string_lnk_vlan, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_vlan_to_string, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_vlan_cmp, + }, + [NMP_OBJECT_TYPE_LNK_VXLAN - 1] = { + .obj_type = NMP_OBJECT_TYPE_LNK_VXLAN, + .sizeof_data = sizeof (NMPObjectLnkVxlan), + .sizeof_public = sizeof (NMPlatformLnkVxlan), + .obj_type_name = "vxlan", + .lnk_link_type = NM_LINK_TYPE_VXLAN, + .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_lnk_vxlan_to_string, + .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_lnk_vxlan_cmp, + }, }; diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index cc8e801f92..e49d358675 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -117,28 +117,29 @@ typedef struct { int sizeof_data; int sizeof_public; const char *obj_type_name; - const char *nl_type; const char *signal_type; + /* Only for NMPObjectLnk* types. */ + NMLinkType lnk_link_type; + /* returns %FALSE, if the obj type would never have an entry for index type @id_type. If @obj has an index, * initialize @id and set @out_id to it. Otherwise, @out_id is NULL. */ gboolean (*cmd_obj_init_cache_id) (const NMPObject *obj, NMPCacheIdType id_type, NMPCacheId *id, const NMPCacheId **out_id); - gboolean (*cmd_obj_equal) (const NMPObject *obj1, const NMPObject *obj2); + int (*cmd_obj_cmp) (const NMPObject *obj1, const NMPObject *obj2); void (*cmd_obj_copy) (NMPObject *dst, const NMPObject *src); void (*cmd_obj_stackinit_id) (NMPObject *obj, const NMPObject *src); void (*cmd_obj_dispose) (NMPObject *obj); gboolean (*cmd_obj_is_alive) (const NMPObject *obj); gboolean (*cmd_obj_is_visible) (const NMPObject *obj); + const char *(*cmd_obj_to_string) (const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size); /* functions that operate on NMPlatformObject */ - gboolean (*cmd_plobj_init_from_nl) (NMPlatform *platform, NMPlatformObject *obj, const struct nl_object *nlo, gboolean id_only, gboolean complete_from_cache); - struct nl_object *(*cmd_plobj_to_nl) (NMPlatform *platform, const NMPlatformObject *obj, gboolean id_only); void (*cmd_plobj_id_copy) (NMPlatformObject *dst, const NMPlatformObject *src); gboolean (*cmd_plobj_id_equal) (const NMPlatformObject *obj1, const NMPlatformObject *obj2); guint (*cmd_plobj_id_hash) (const NMPlatformObject *obj); const char *(*cmd_plobj_to_string_id) (const NMPlatformObject *obj, char *buf, gsize buf_size); - const char *(*cmd_plobj_to_string) (const NMPlatformObject *obj); + const char *(*cmd_plobj_to_string) (const NMPlatformObject *obj, char *buf, gsize len); int (*cmd_plobj_cmp) (const NMPlatformObject *obj1, const NMPlatformObject *obj2); } NMPClass; @@ -149,6 +150,9 @@ typedef struct { struct { guint8 is_in_netlink; + + /* Additional data that depends on the link-type (IFLA_INFO_DATA) */ + NMPObject *lnk; } netlink; struct { @@ -157,6 +161,31 @@ typedef struct { } NMPObjectLink; typedef struct { + NMPlatformLnkGre _public; +} NMPObjectLnkGre; + +typedef struct { + NMPlatformLnkInfiniband _public; +} NMPObjectLnkInfiniband; + +typedef struct { + NMPlatformLnkMacvlan _public; +} NMPObjectLnkMacvlan; + +typedef struct { + NMPlatformLnkVlan _public; + + guint n_ingress_qos_map; + guint n_egress_qos_map; + const NMVlanQosMapping *ingress_qos_map; + const NMVlanQosMapping *egress_qos_map; +} NMPObjectLnkVlan; + +typedef struct { + NMPlatformLnkVxlan _public; +} NMPObjectLnkVxlan; + +typedef struct { NMPlatformIP4Address _public; } NMPObjectIP4Address; @@ -182,6 +211,21 @@ struct _NMPObject { NMPlatformLink link; NMPObjectLink _link; + NMPlatformLnkGre lnk_gre; + NMPObjectLnkGre _lnk_gre; + + NMPlatformLnkInfiniband lnk_infiniband; + NMPObjectLnkInfiniband _lnk_infiniband; + + NMPlatformLnkMacvlan lnk_macvlan; + NMPObjectLnkMacvlan _lnk_macvlan; + + NMPlatformLnkVlan lnk_vlan; + NMPObjectLnkVlan _lnk_vlan; + + NMPlatformLnkVxlan lnk_vxlan; + NMPObjectLnkVxlan _lnk_vxlan; + NMPlatformIPAddress ip_address; NMPlatformIPXAddress ipx_address; NMPlatformIP4Address ip4_address; @@ -341,21 +385,4 @@ NMPCacheOpsType nmp_cache_update_link_master_connected (NMPCache *cache, int ifi NMPCache *nmp_cache_new (void); void nmp_cache_free (NMPCache *cache); -NMPObject *nmp_object_from_nl (NMPlatform *platform, const struct nl_object *nlo, gboolean id_only, gboolean complete_from_cache); -struct nl_object *nmp_object_to_nl (NMPlatform *platform, const NMPObject *obj, gboolean id_only); - -/* the following functions are currently implemented inside nm-linux-platform, because - * they depend on utility functions there. */ -NMPObjectType _nlo_get_object_type (const struct nl_object *nlo); -gboolean _nmp_vt_cmd_plobj_init_from_nl_link (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache); -gboolean _nmp_vt_cmd_plobj_init_from_nl_ip4_address (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache); -gboolean _nmp_vt_cmd_plobj_init_from_nl_ip6_address (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache); -gboolean _nmp_vt_cmd_plobj_init_from_nl_ip4_route (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache); -gboolean _nmp_vt_cmd_plobj_init_from_nl_ip6_route (NMPlatform *platform, NMPlatformObject *_obj, const struct nl_object *_nlo, gboolean id_only, gboolean complete_from_cache); -struct nl_object *_nmp_vt_cmd_plobj_to_nl_link (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only); -struct nl_object *_nmp_vt_cmd_plobj_to_nl_ip4_address (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only); -struct nl_object *_nmp_vt_cmd_plobj_to_nl_ip6_address (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only); -struct nl_object *_nmp_vt_cmd_plobj_to_nl_ip4_route (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only); -struct nl_object *_nmp_vt_cmd_plobj_to_nl_ip6_route (NMPlatform *platform, const NMPlatformObject *_obj, gboolean id_only); - #endif /* __NMP_OBJECT_H__ */ diff --git a/src/platform/tests/Makefile.am b/src/platform/tests/Makefile.am index 1fb621d752..bc6ec39e20 100644 --- a/src/platform/tests/Makefile.am +++ b/src/platform/tests/Makefile.am @@ -31,7 +31,6 @@ PLATFORM_LDADD = \ @GNOME_CODE_COVERAGE_RULES@ noinst_PROGRAMS = \ - dump \ monitor \ platform \ test-link-fake \ @@ -50,9 +49,6 @@ EXTRA_DIST = test-common.h monitor_SOURCES = monitor.c $(PLATFORM_SOURCES) monitor_LDADD = $(PLATFORM_LDADD) -dump_SOURCES = dump.c $(PLATFORM_SOURCES) -dump_LDADD = $(PLATFORM_LDADD) - platform_SOURCES = platform.c $(PLATFORM_SOURCES) platform_LDADD = $(PLATFORM_LDADD) diff --git a/src/platform/tests/dump.c b/src/platform/tests/dump.c deleted file mode 100644 index b1f8012350..0000000000 --- a/src/platform/tests/dump.c +++ /dev/null @@ -1,137 +0,0 @@ -#include "config.h" - -#include <stdio.h> -#include <stdlib.h> -#include <arpa/inet.h> - -#include "nm-platform.h" -#include "nm-linux-platform.h" -#include "nm-fake-platform.h" -#include "nm-macros-internal.h" - -static void -dump_interface (NMPlatformLink *link) -{ - GArray *ip6_addresses; - GArray *ip4_addresses; - const NMPlatformIP6Address *ip6_address; - const NMPlatformIP4Address *ip4_address; - GArray *ip6_routes; - GArray *ip4_routes; - const NMPlatformIP6Route *ip6_route; - const NMPlatformIP4Route *ip4_route; - char networkstr[INET6_ADDRSTRLEN]; - char gatewaystr[INET6_ADDRSTRLEN]; - int vlan_id, vlan_parent; - const char *address; - size_t addrlen; - int i; - - g_assert (NM_FLAGS_HAS (link->flags, IFF_UP) || !link->connected); - - printf ("%d: %s: %s", link->ifindex, link->name, nm_link_type_to_string (link->type)); - if (NM_FLAGS_HAS (link->flags, IFF_UP)) - printf (" %s", link->connected ? "CONNECTED" : "DISCONNECTED"); - else - printf (" DOWN"); - if (NM_FLAGS_HAS (link->flags, IFF_NOARP)) - printf (" noarp"); - if (link->master) - printf (" master %d", link->master); - if (link->parent) - printf (" parent %d", link->parent); - printf (" mtu %d", link->mtu); - printf ("\n"); - if (link->driver) - printf (" driver: %s\n", link->driver); - printf (" UDI: %s\n", nm_platform_link_get_udi (NM_PLATFORM_GET, link->ifindex)); - if (!nm_platform_vlan_get_info (NM_PLATFORM_GET, link->ifindex, &vlan_parent, &vlan_id)) - g_assert_not_reached (); - if (vlan_parent) - printf (" vlan parent %d id %d\n", vlan_parent, vlan_id); - - if (nm_platform_link_is_software (NM_PLATFORM_GET, link->ifindex)) - printf (" class software\n"); - if (nm_platform_link_supports_slaves (NM_PLATFORM_GET, link->ifindex)) - printf (" class supports-slaves\n"); - if (nm_platform_link_supports_carrier_detect (NM_PLATFORM_GET, link->ifindex)) - printf (" feature carrier-detect\n"); - if (nm_platform_link_supports_vlans (NM_PLATFORM_GET, link->ifindex)) - printf (" feature vlans\n"); - - address = nm_platform_link_get_address (NM_PLATFORM_GET, link->ifindex, &addrlen); - if (address) { - printf (" link-address "); - for (i = 0; i < addrlen; i++) - printf ("%s%02hhx", i ? ":" : "", address[i]); - printf ("\n"); - } - - ip4_addresses = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, link->ifindex); - ip6_addresses = nm_platform_ip6_address_get_all (NM_PLATFORM_GET, link->ifindex); - - g_assert (ip4_addresses); - g_assert (ip6_addresses); - - for (i = 0; i < ip4_addresses->len; i++) { - ip4_address = &g_array_index (ip4_addresses, NMPlatformIP4Address, i); - printf (" ip4-address %s\n", nm_platform_ip4_address_to_string (ip4_address)); - } - - for (i = 0; i < ip6_addresses->len; i++) { - ip6_address = &g_array_index (ip6_addresses, NMPlatformIP6Address, i); - printf (" ip6-address %s\n", nm_platform_ip6_address_to_string (ip6_address)); - } - - g_array_unref (ip4_addresses); - g_array_unref (ip6_addresses); - - ip4_routes = nm_platform_ip4_route_get_all (NM_PLATFORM_GET, link->ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); - ip6_routes = nm_platform_ip6_route_get_all (NM_PLATFORM_GET, link->ifindex, NM_PLATFORM_GET_ROUTE_FLAGS_WITH_DEFAULT | NM_PLATFORM_GET_ROUTE_FLAGS_WITH_NON_DEFAULT); - - g_assert (ip4_routes); - g_assert (ip6_routes); - - for (i = 0; i < ip4_routes->len; i++) { - ip4_route = &g_array_index (ip4_routes, NMPlatformIP4Route, i); - inet_ntop (AF_INET, &ip4_route->network, networkstr, sizeof (networkstr)); - inet_ntop (AF_INET, &ip4_route->gateway, gatewaystr, sizeof (gatewaystr)); - printf (" ip4-route %s/%d via %s\n", networkstr, ip4_route->plen, gatewaystr); - } - - for (i = 0; i < ip6_routes->len; i++) { - ip6_route = &g_array_index (ip6_routes, NMPlatformIP6Route, i); - inet_ntop (AF_INET6, &ip6_route->network, networkstr, sizeof (networkstr)); - inet_ntop (AF_INET6, &ip6_route->gateway, gatewaystr, sizeof (gatewaystr)); - printf (" ip6-route %s/%d via %s\n", networkstr, ip6_route->plen, gatewaystr); - } - - g_array_unref (ip4_routes); - g_array_unref (ip6_routes); -} - -static void -dump_all (void) -{ - GArray *links = nm_platform_link_get_all (NM_PLATFORM_GET); - int i; - - for (i = 0; i < links->len; i++) - dump_interface (&g_array_index (links, NMPlatformLink, i)); -} - -int -main (int argc, char **argv) -{ - nm_g_type_init (); - - g_assert (argc <= 2); - if (argc > 1 && !g_strcmp0 (argv[1], "--fake")) - nm_fake_platform_setup (); - else - nm_linux_platform_setup (); - - dump_all (); - - return EXIT_SUCCESS; -} diff --git a/src/platform/tests/monitor.c b/src/platform/tests/monitor.c index 6c95830678..4d721ce947 100644 --- a/src/platform/tests/monitor.c +++ b/src/platform/tests/monitor.c @@ -4,25 +4,27 @@ #include <syslog.h> #include "nm-default.h" -#include "nm-fake-platform.h" #include "nm-linux-platform.h" +#include "nm-test-utils.h" + +NMTST_DEFINE (); + int main (int argc, char **argv) { GMainLoop *loop; - nm_g_type_init (); + if (!g_getenv ("G_MESSAGES_DEBUG")) + g_setenv ("G_MESSAGES_DEBUG", "all", TRUE); + + nmtst_init_with_logging (&argc, &argv, "DEBUG", "ALL"); + + nm_log_info (LOGD_PLATFORM, "platform monitor start"); loop = g_main_loop_new (NULL, FALSE); - nm_logging_setup ("debug", NULL, NULL, NULL); - openlog (G_LOG_DOMAIN, LOG_CONS | LOG_PERROR, LOG_DAEMON); - - g_assert (argc <= 2); - if (argc > 1 && !g_strcmp0 (argv[1], "--fake")) - nm_fake_platform_setup (); - else - nm_linux_platform_setup (); + + nm_linux_platform_setup (); g_main_loop_run (loop); diff --git a/src/platform/tests/platform.c b/src/platform/tests/platform.c index 08e89007ff..33ef8928dc 100644 --- a/src/platform/tests/platform.c +++ b/src/platform/tests/platform.c @@ -30,6 +30,7 @@ #include "nm-platform.h" #include "nm-linux-platform.h" #include "nm-fake-platform.h" +#include "nm-utils.h" #define error(...) fprintf (stderr, __VA_ARGS__) @@ -324,13 +325,14 @@ static gboolean do_vlan_get_info (char **argv) { int ifindex = parse_ifindex (*argv++); - int parent; - int vlanid; + const NMPlatformLink *plink; + const NMPlatformLnkVlan *plnk; - if (!nm_platform_vlan_get_info (NM_PLATFORM_GET, ifindex, &parent, &vlanid)) + plnk = nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, ifindex, &plink); + if (!plnk) return FALSE; - printf ("%d %d\n", parent, vlanid); + printf ("%d %d\n", plink->parent, plnk->id); return TRUE; } @@ -356,20 +358,6 @@ do_vlan_set_egress_map (char **argv) } static gboolean -do_veth_get_properties (char **argv) -{ - int ifindex = parse_ifindex (*argv++); - NMPlatformVethProperties props; - - if (!nm_platform_veth_get_properties (NM_PLATFORM_GET, ifindex, &props)) - return FALSE; - - printf ("peer: %d\n", props.peer); - - return TRUE; -} - -static gboolean do_tun_get_properties (char **argv) { int ifindex = parse_ifindex (*argv++); @@ -401,15 +389,17 @@ static gboolean do_macvlan_get_properties (char **argv) { int ifindex = parse_ifindex (*argv++); - NMPlatformMacvlanProperties props; + const NMPlatformLink *plink; + const NMPlatformLnkMacvlan *props; - if (!nm_platform_macvlan_get_properties (NM_PLATFORM_GET, ifindex, &props)) + props = nm_platform_link_get_lnk_macvlan (NM_PLATFORM_GET, ifindex, &plink); + if (!props) return FALSE; - printf ("parent: %d\n", props.parent_ifindex); - printf ("mode: %s\n", props.mode); + printf ("parent: %d\n", plink->parent); + printf ("mode: %s\n", props->mode); printf ("no-promisc: "); - print_boolean (props.no_promisc); + print_boolean (props->no_promisc); return TRUE; } @@ -417,45 +407,46 @@ static gboolean do_vxlan_get_properties (char **argv) { int ifindex = parse_ifindex (*argv++); - NMPlatformVxlanProperties props; + const NMPlatformLnkVxlan *props; char addrstr[INET6_ADDRSTRLEN]; - if (!nm_platform_vxlan_get_properties (NM_PLATFORM_GET, ifindex, &props)) + props = nm_platform_link_get_lnk_vxlan (NM_PLATFORM_GET, ifindex, NULL); + if (props) return FALSE; - printf ("parent-ifindex: %u\n", props.parent_ifindex); - printf ("id: %u\n", props.id); - if (props.group) - inet_ntop (AF_INET, &props.group, addrstr, sizeof (addrstr)); - else if (props.group6.s6_addr[0]) - inet_ntop (AF_INET6, &props.group6, addrstr, sizeof (addrstr)); + printf ("parent-ifindex: %u\n", props->parent_ifindex); + printf ("id: %u\n", props->id); + if (props->group) + inet_ntop (AF_INET, &props->group, addrstr, sizeof (addrstr)); + else if (props->group6.s6_addr[0]) + inet_ntop (AF_INET6, &props->group6, addrstr, sizeof (addrstr)); else strcpy (addrstr, "-"); printf ("group: %s\n", addrstr); - if (props.local) - inet_ntop (AF_INET, &props.local, addrstr, sizeof (addrstr)); - else if (props.local6.s6_addr[0]) - inet_ntop (AF_INET6, &props.local6, addrstr, sizeof (addrstr)); + if (props->local) + inet_ntop (AF_INET, &props->local, addrstr, sizeof (addrstr)); + else if (props->local6.s6_addr[0]) + inet_ntop (AF_INET6, &props->local6, addrstr, sizeof (addrstr)); else strcpy (addrstr, "-"); printf ("local: %s\n", addrstr); - printf ("tos: %u\n", props.tos); - printf ("ttl: %u\n", props.ttl); + printf ("tos: %u\n", props->tos); + printf ("ttl: %u\n", props->ttl); printf ("learning: "); - print_boolean (props.learning); - printf ("ageing: %u\n", props.ageing); - printf ("limit: %u\n", props.limit); - printf ("dst-port: %u\n", props.dst_port); - printf ("src-port-min: %u\n", props.src_port_min); - printf ("src-port-max: %u\n", props.src_port_max); + print_boolean (props->learning); + printf ("ageing: %u\n", props->ageing); + printf ("limit: %u\n", props->limit); + printf ("dst-port: %u\n", props->dst_port); + printf ("src-port-min: %u\n", props->src_port_min); + printf ("src-port-max: %u\n", props->src_port_max); printf ("proxy: "); - print_boolean (props.proxy); + print_boolean (props->proxy); printf ("rsc: "); - print_boolean (props.rsc); + print_boolean (props->rsc); printf ("l2miss: "); - print_boolean (props.l2miss); + print_boolean (props->l2miss); printf ("l3miss: "); - print_boolean (props.l3miss); + print_boolean (props->l3miss); return TRUE; } @@ -464,31 +455,32 @@ static gboolean do_gre_get_properties (char **argv) { int ifindex = parse_ifindex (*argv++); - NMPlatformGreProperties props; + const NMPlatformLnkGre *props; char addrstr[INET_ADDRSTRLEN]; - if (!nm_platform_gre_get_properties (NM_PLATFORM_GET, ifindex, &props)) + props = nm_platform_link_get_lnk_gre (NM_PLATFORM_GET, ifindex, NULL); + if (!props) return FALSE; - printf ("parent-ifindex: %u\n", props.parent_ifindex); - printf ("input-flags: %u\n", props.input_flags); - printf ("output-flags: %u\n", props.input_flags); - printf ("input-key: %u\n", props.input_key); - printf ("output-key: %u\n", props.output_key); - if (props.local) - inet_ntop (AF_INET, &props.local, addrstr, sizeof (addrstr)); + printf ("parent-ifindex: %u\n", props->parent_ifindex); + printf ("input-flags: %u\n", props->input_flags); + printf ("output-flags: %u\n", props->input_flags); + printf ("input-key: %u\n", props->input_key); + printf ("output-key: %u\n", props->output_key); + if (props->local) + inet_ntop (AF_INET, &props->local, addrstr, sizeof (addrstr)); else strcpy (addrstr, "-"); printf ("local: %s\n", addrstr); - if (props.remote) - inet_ntop (AF_INET, &props.remote, addrstr, sizeof (addrstr)); + if (props->remote) + inet_ntop (AF_INET, &props->remote, addrstr, sizeof (addrstr)); else strcpy (addrstr, "-"); printf ("remote: %s\n", addrstr); - printf ("ttl: %u\n", props.ttl); - printf ("tos: %u\n", props.tos); + printf ("ttl: %u\n", props->ttl); + printf ("tos: %u\n", props->tos); printf ("path-mtu-discovery: "); - print_boolean (props.path_mtu_discovery); + print_boolean (props->path_mtu_discovery); return TRUE; } @@ -499,15 +491,17 @@ do_ip4_address_get_all (char **argv) int ifindex = parse_ifindex (argv[0]); GArray *addresses; NMPlatformIP4Address *address; - char addrstr[INET_ADDRSTRLEN]; int i; if (ifindex) { addresses = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); for (i = 0; i < addresses->len; i++) { address = &g_array_index (addresses, NMPlatformIP4Address, i); - inet_ntop (AF_INET, &address->address, addrstr, sizeof (addrstr)); - printf ("%s/%d\n", addrstr, address->plen); + + printf ("%s", nm_utils_inet4_ntop (address->address, NULL)); + if (address->address != address->peer_address) + printf (" peer %s", nm_utils_inet4_ntop (address->peer_address, NULL)); + printf ("/%d\n", address->plen); } g_array_unref (addresses); } @@ -583,7 +577,7 @@ do_ip4_address_add (char **argv) guint32 lifetime = strtol (*argv++, NULL, 10); guint32 preferred = strtol (*argv++, NULL, 10); - gboolean value = nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, address, plen, 0, lifetime, preferred, NULL); + gboolean value = nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, address, plen, address, lifetime, preferred, NULL); return value; } else return FALSE; @@ -599,7 +593,7 @@ do_ip6_address_add (char **argv) if (ifindex && parse_ip6_address (*argv++, &address, &plen)) { guint32 lifetime = strtol (*argv++, NULL, 10); guint32 preferred = strtol (*argv++, NULL, 10); - guint flags = (*argv) ? rtnl_addr_str2flags (*argv++) : 0; + guint flags = 0; /* don't support flags */ gboolean value = nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, address, plen, in6addr_any, lifetime, preferred, flags); return value; @@ -624,11 +618,11 @@ do_ip6_address_add (char **argv) } else \ return FALSE; \ } -#define ADDR_CMD(cmdname, ...) ADDR_CMD_FULL (ip4, cmdname, FALSE, 0, ##__VA_ARGS__) ADDR_CMD_FULL (ip6, cmdname, FALSE) +#define ADDR_CMD(cmdname, ...) ADDR_CMD_FULL (ip4, cmdname, FALSE, address, ##__VA_ARGS__) ADDR_CMD_FULL (ip6, cmdname, FALSE) #define ADDR_CMD_PRINT(cmdname, ...) ADDR_CMD_FULL (ip4, cmdname, TRUE, ##__VA_ARGS__) ADDR_CMD_FULL (ip6, cmdname, TRUE) ADDR_CMD (delete) -ADDR_CMD_PRINT (get, 0) +ADDR_CMD_PRINT (get, address) static gboolean do_ip4_route_get_all (char **argv) @@ -820,8 +814,6 @@ static const command_t commands[] = { "<ifname/ifindex> <from> <to>" }, { "vlan-set-egress-map", "set vlan egress map", do_vlan_set_egress_map, 3, "<ifname/ifindex> <from> <to>" }, - { "veth-get-properties", "get veth properties", do_veth_get_properties, 1, - "<ifname/ifindex>" }, { "tun-get-properties", "get tun/tap properties", do_tun_get_properties, 1, "<ifname/ifindex>" }, { "macvlan-get-properties", "get macvlan properties", do_macvlan_get_properties, 1, diff --git a/src/platform/tests/test-address.c b/src/platform/tests/test-address.c index f37b05ceae..2cdd29622b 100644 --- a/src/platform/tests/test-address.c +++ b/src/platform/tests/test-address.c @@ -73,13 +73,13 @@ test_ip4_address_general (void) inet_pton (AF_INET, IP4_ADDRESS, &addr); /* Add address */ - g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0)); - nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, 0, lifetime, preferred, NULL); - g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0)); + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr)); + nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, NULL); + g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr)); accept_signal (address_added); /* Add address again (aka update) */ - nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, 0, lifetime + 100, preferred + 50, NULL); + nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime + 100, preferred + 50, NULL); accept_signals (address_changed, 0, 1); /* Test address listing */ @@ -89,16 +89,17 @@ test_ip4_address_general (void) address = &g_array_index (addresses, NMPlatformIP4Address, 0); g_assert_cmpint (address->ifindex, ==, ifindex); g_assert_cmphex (address->address, ==, addr); + g_assert_cmphex (address->peer_address, ==, addr); g_assert_cmpint (address->plen, ==, IP4_PLEN); g_array_unref (addresses); /* Remove address */ - nmtstp_ip4_address_del (EX, ifindex, addr, IP4_PLEN, 0); - g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0)); + nmtstp_ip4_address_del (EX, ifindex, addr, IP4_PLEN, addr); + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr)); accept_signal (address_removed); /* Remove address again */ - nmtstp_ip4_address_del (EX, ifindex, addr, IP4_PLEN, 0); + nmtstp_ip4_address_del (EX, ifindex, addr, IP4_PLEN, addr); free_signal (address_added); free_signal (address_changed); @@ -173,16 +174,16 @@ test_ip4_address_general_2 (void) g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, DEVICE_IFINDEX, NULL)); /* Add/delete notification */ - nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, 0, lifetime, preferred, NULL); + nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, NULL); accept_signal (address_added); - g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0)); - nmtstp_ip4_address_del (EX, ifindex, addr, IP4_PLEN, 0); + g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr)); + nmtstp_ip4_address_del (EX, ifindex, addr, IP4_PLEN, addr); accept_signal (address_removed); - g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0)); + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr)); /* Add/delete conflict */ - nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, 0, lifetime, preferred, NULL); - g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, 0)); + nmtstp_ip4_address_add (EX, ifindex, addr, IP4_PLEN, addr, lifetime, preferred, NULL); + g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, IP4_PLEN, addr)); accept_signal (address_added); free_signal (address_added); @@ -275,6 +276,59 @@ test_ip4_address_peer (void) /*****************************************************************************/ +static void +test_ip4_address_peer_zero (void) +{ + const int ifindex = DEVICE_IFINDEX; + in_addr_t addr, addr_peer; + guint32 lifetime = 2000; + guint32 preferred = 1000; + const int plen = 24; + const char *label = NULL; + in_addr_t peers[3], r_peers[3]; + int i; + GArray *addrs; + + g_assert (ifindex > 0); + + inet_pton (AF_INET, "192.168.5.2", &addr); + inet_pton (AF_INET, "192.168.6.2", &addr_peer); + peers[0] = addr; + peers[1] = addr_peer; + peers[2] = 0; + + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, NULL)); + + nmtst_rand_perm (NULL, r_peers, peers, sizeof (peers[0]), G_N_ELEMENTS (peers)); + for (i = 0; i < G_N_ELEMENTS (peers); i++) { + g_assert (!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, plen, r_peers[i])); + + nmtstp_ip4_address_add (EX, ifindex, addr, plen, r_peers[i], lifetime, preferred, label); + + addrs = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); + g_assert (addrs); + g_assert_cmpint (addrs->len, ==, i + 1); + g_array_unref (addrs); + } + + if (nmtst_is_debug ()) + nmtstp_run_command_check ("ip address show dev %s", DEVICE_NAME); + + nmtst_rand_perm (NULL, r_peers, peers, sizeof (peers[0]), G_N_ELEMENTS (peers)); + for (i = 0; i < G_N_ELEMENTS (peers); i++) { + g_assert (nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, addr, plen, r_peers[i])); + + nmtstp_ip4_address_del (EX, ifindex, addr, plen, r_peers[i]); + + addrs = nm_platform_ip4_address_get_all (NM_PLATFORM_GET, ifindex); + g_assert (addrs); + g_assert_cmpint (addrs->len, ==, G_N_ELEMENTS (peers) - i - 1); + g_array_unref (addrs); + } +} + +/*****************************************************************************/ + void init_tests (int *argc, char ***argv) { @@ -342,4 +396,5 @@ setup_tests (void) _g_test_add_func ("/address/ipv6/general-2", test_ip6_address_general_2); _g_test_add_func ("/address/ipv4/peer", test_ip4_address_peer); + _g_test_add_func ("/address/ipv4/peer/zero", test_ip4_address_peer_zero); } diff --git a/src/platform/tests/test-cleanup.c b/src/platform/tests/test-cleanup.c index 4ef6908588..94ce05491d 100644 --- a/src/platform/tests/test-cleanup.c +++ b/src/platform/tests/test-cleanup.c @@ -43,7 +43,7 @@ test_cleanup_internal (void) g_assert (ifindex > 0); /* Add routes and addresses */ - g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr4, plen4, 0, lifetime, preferred, NULL)); + g_assert (nm_platform_ip4_address_add (NM_PLATFORM_GET, ifindex, addr4, plen4, addr4, lifetime, preferred, NULL)); g_assert (nm_platform_ip6_address_add (NM_PLATFORM_GET, ifindex, addr6, plen6, in6addr_any, lifetime, preferred, flags)); g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, gateway4, 32, INADDR_ANY, 0, metric, mss)); g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network4, plen4, gateway4, 0, metric, mss)); diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c index af41a0380e..98280b4527 100644 --- a/src/platform/tests/test-common.c +++ b/src/platform/tests/test-common.c @@ -10,14 +10,6 @@ #define SIGNAL_DATA_FMT "'%s-%s' ifindex %d%s%s%s (%d times received)" #define SIGNAL_DATA_ARG(data) (data)->name, nm_platform_signal_change_type_to_string ((data)->change_type), (data)->ifindex, (data)->ifname ? " ifname '" : "", (data)->ifname ? (data)->ifname : "", (data)->ifname ? "'" : "", (data)->received_count -typedef struct { - union { - guint8 addr_ptr[1]; - in_addr_t addr4; - struct in6_addr addr6; - }; -} IPAddr; - gboolean nmtstp_is_root_test (void) { @@ -374,6 +366,50 @@ nmtstp_wait_for_signal_until (gint64 until_ms) } } +const NMPlatformLink * +nmtstp_wait_for_link (const char *ifname, guint timeout_ms) +{ + return nmtstp_wait_for_link_until (ifname, nm_utils_get_monotonic_timestamp_ms () + timeout_ms); +} + +const NMPlatformLink * +nmtstp_wait_for_link_until (const char *ifname, gint64 until_ms) +{ + const NMPlatformLink *plink; + gint64 now; + + while (TRUE) { + now = nm_utils_get_monotonic_timestamp_ms (); + + plink = nm_platform_link_get_by_ifname (NM_PLATFORM_GET, ifname); + if (plink) + return plink; + + if (until_ms < now) + return NULL; + + nmtstp_wait_for_signal (MAX (1, until_ms - now)); + } +} + +const NMPlatformLink * +nmtstp_assert_wait_for_link (const char *ifname, NMLinkType expected_link_type, guint timeout_ms) +{ + return nmtstp_assert_wait_for_link_until (ifname, expected_link_type, nm_utils_get_monotonic_timestamp_ms () + timeout_ms); +} + +const NMPlatformLink * +nmtstp_assert_wait_for_link_until (const char *ifname, NMLinkType expected_link_type, gint64 until_ms) +{ + const NMPlatformLink *plink; + + plink = nmtstp_wait_for_link_until (ifname, until_ms); + g_assert (plink); + if (expected_link_type != NM_LINK_TYPE_NONE) + g_assert_cmpint (plink->type, ==, expected_link_type); + return plink; +} + int nmtstp_run_command_check_external_global (void) { @@ -502,9 +538,9 @@ static void _ip_address_add (gboolean external_command, gboolean is_v4, int ifindex, - const IPAddr *address, + const NMIPAddr *address, int plen, - const IPAddr *peer_address, + const NMIPAddr *peer_address, guint32 lifetime, guint32 preferred, const char *label, @@ -524,9 +560,6 @@ _ip_address_add (gboolean external_command, ifname = nm_platform_link_get_name (NM_PLATFORM_GET, ifindex); g_assert (ifname); - if (peer_address == address) - peer_address = 0; - if (lifetime != NM_PLATFORM_LIFETIME_PERMANENT) s_valid = g_strdup_printf (" valid_lft %d", lifetime); if (preferred != NM_PLATFORM_LIFETIME_PERMANENT) @@ -535,11 +568,20 @@ _ip_address_add (gboolean external_command, s_label = g_strdup_printf ("%s:%s", ifname, label); if (is_v4) { + char s_peer[100]; + g_assert (flags == 0); - nmtstp_run_command_check ("ip address change %s%s%s/%d dev %s%s%s%s", + + if ( peer_address->addr4 != address->addr4 + || nmtst_get_rand_int () % 2) { + /* If the peer is the same as the local address, we can omit it. The result should be identical */ + g_snprintf (s_peer, sizeof (s_peer), " peer %s", nm_utils_inet4_ntop (peer_address->addr4, b2)); + } else + s_peer[0] = '\0'; + + nmtstp_run_command_check ("ip address change %s%s/%d dev %s%s%s%s", nm_utils_inet4_ntop (address->addr4, b1), - peer_address->addr4 ? " peer " : "", - peer_address->addr4 ? nm_utils_inet4_ntop (peer_address->addr4, b2) : "", + s_peer, plen, ifname, s_valid ?: "", @@ -601,7 +643,7 @@ _ip_address_add (gboolean external_command, g_assert (flags == 0); a = nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, address->addr4, plen, peer_address->addr4); if ( a - && nm_platform_ip4_address_get_peer (a) == (peer_address->addr4 ? peer_address->addr4 : address->addr4) + && a->peer_address == peer_address->addr4 && nmtstp_ip_address_check_lifetime ((NMPlatformIPAddress*) a, -1, lifetime, preferred) && strcmp (a->label, label ?: "") == 0) break; @@ -624,9 +666,6 @@ _ip_address_add (gboolean external_command, /* for internal command, we expect not to reach this line.*/ g_assert (external_command); - /* timeout? */ - g_assert (nm_utils_get_monotonic_timestamp_ms () < end_time); - g_assert (nmtstp_wait_for_signal_until (end_time)); } while (TRUE); } @@ -644,9 +683,9 @@ nmtstp_ip4_address_add (gboolean external_command, _ip_address_add (external_command, TRUE, ifindex, - (IPAddr *) &address, + (NMIPAddr *) &address, plen, - (IPAddr *) &peer_address, + (NMIPAddr *) &peer_address, lifetime, preferred, label, @@ -666,9 +705,9 @@ nmtstp_ip6_address_add (gboolean external_command, _ip_address_add (external_command, FALSE, ifindex, - (IPAddr *) &address, + (NMIPAddr *) &address, plen, - (IPAddr *) &peer_address, + (NMIPAddr *) &peer_address, lifetime, preferred, NULL, @@ -679,9 +718,9 @@ static void _ip_address_del (gboolean external_command, gboolean is_v4, int ifindex, - const IPAddr *address, + const NMIPAddr *address, int plen, - const IPAddr *peer_address) + const NMIPAddr *peer_address) { gint64 end_time; @@ -696,9 +735,6 @@ _ip_address_del (gboolean external_command, ifname = nm_platform_link_get_name (NM_PLATFORM_GET, ifindex); g_assert (ifname); - if (peer_address == address) - peer_address = 0; - /* let's wait until we see the address as we added it. */ if (is_v4) had_address = !!nm_platform_ip4_address_get (NM_PLATFORM_GET, ifindex, address->addr4, plen, peer_address->addr4); @@ -708,8 +744,8 @@ _ip_address_del (gboolean external_command, if (is_v4) { success = nmtstp_run_command ("ip address delete %s%s%s/%d dev %s", nm_utils_inet4_ntop (address->addr4, b1), - peer_address->addr4 ? " peer " : "", - peer_address->addr4 ? nm_utils_inet4_ntop (peer_address->addr4, b2) : "", + peer_address->addr4 != address->addr4 ? " peer " : "", + peer_address->addr4 != address->addr4 ? nm_utils_inet4_ntop (peer_address->addr4, b2) : "", plen, ifname); } else { @@ -763,9 +799,6 @@ _ip_address_del (gboolean external_command, /* for internal command, we expect not to reach this line.*/ g_assert (external_command); - /* timeout? */ - g_assert (nm_utils_get_monotonic_timestamp_ms () < end_time); - g_assert (nmtstp_wait_for_signal_until (end_time)); } while (TRUE); } @@ -780,9 +813,9 @@ nmtstp_ip4_address_del (gboolean external_command, _ip_address_del (external_command, TRUE, ifindex, - (IPAddr *) &address, + (NMIPAddr *) &address, plen, - (IPAddr *) &peer_address); + (NMIPAddr *) &peer_address); } void @@ -794,11 +827,57 @@ nmtstp_ip6_address_del (gboolean external_command, _ip_address_del (external_command, FALSE, ifindex, - (IPAddr *) &address, + (NMIPAddr *) &address, plen, NULL); } +void +nmtstp_link_set_updown (gboolean external_command, + int ifindex, + gboolean up) +{ + const NMPlatformLink *plink; + gint64 end_time; + + external_command = nmtstp_run_command_check_external (external_command); + + if (external_command) { + const char *ifname; + + ifname = nm_platform_link_get_name (NM_PLATFORM_GET, ifindex); + g_assert (ifname); + + nmtstp_run_command_check ("ip link set %s %s", + ifname, + up ? "up" : "down"); + } else { + if (up) + g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, ifindex, NULL)); + else + g_assert (nm_platform_link_set_down (NM_PLATFORM_GET, ifindex)); + } + + /* Let's wait until we get the result */ + end_time = nm_utils_get_monotonic_timestamp_ms () + 250; + do { + if (external_command) + nm_platform_process_events (NM_PLATFORM_GET); + + /* let's wait until we see the address as we added it. */ + plink = nm_platform_link_get (NM_PLATFORM_GET, ifindex); + g_assert (plink); + + if (NM_FLAGS_HAS (plink->flags, IFF_UP) == !!up) + break; + + /* for internal command, we expect not to reach this line.*/ + g_assert (external_command); + + g_assert (nmtstp_wait_for_signal_until (end_time)); + } while (TRUE); +} + /*****************************************************************************/ NMTST_DEFINE(); diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h index 626e9d2fde..a2794af7e3 100644 --- a/src/platform/tests/test-common.h +++ b/src/platform/tests/test-common.h @@ -75,6 +75,11 @@ int nmtstp_run_command (const char *format, ...) __attribute__((__format__ (__pr gboolean nmtstp_wait_for_signal (guint timeout_ms); gboolean nmtstp_wait_for_signal_until (gint64 until_ms); +const NMPlatformLink *nmtstp_wait_for_link (const char *ifname, guint timeout_ms); +const NMPlatformLink *nmtstp_wait_for_link_until (const char *ifname, gint64 until_ms); + +const NMPlatformLink *nmtstp_assert_wait_for_link (const char *ifname, NMLinkType expected_link_type, guint timeout_ms); +const NMPlatformLink *nmtstp_assert_wait_for_link_until (const char *ifname, NMLinkType expected_link_type, gint64 until_ms); int nmtstp_run_command_check_external_global (void); gboolean nmtstp_run_command_check_external (int external_command); @@ -113,6 +118,9 @@ void nmtstp_ip6_address_del (gboolean external_command, struct in6_addr address, int plen); +void nmtstp_link_set_updown (gboolean external_command, + int ifindex, + gboolean up); void init_tests (int *argc, char ***argv); void setup_tests (void); diff --git a/src/platform/tests/test-general.c b/src/platform/tests/test-general.c index b1dd454e28..f342e5653e 100644 --- a/src/platform/tests/test-general.c +++ b/src/platform/tests/test-general.c @@ -53,37 +53,6 @@ test_link_get_all (void) /******************************************************************/ -static void -test_nm_platform_ip6_address_to_string_flags (void) -{ - NMPlatformIP6Address addr = { 0 }; - - g_assert_cmpstr (strstr (nm_platform_ip6_address_to_string (&addr), " flags "), ==, NULL); - - addr.flags = IFA_F_MANAGETEMPADDR; - nmtst_assert_str_has_substr (nm_platform_ip6_address_to_string (&addr), " flags mngtmpaddr "); - - addr.flags = IFA_F_NOPREFIXROUTE; - nmtst_assert_str_has_substr (nm_platform_ip6_address_to_string (&addr), " flags noprefixroute "); - - addr.flags = IFA_F_MANAGETEMPADDR | IFA_F_NOPREFIXROUTE; - nmtst_assert_str_has_substr (nm_platform_ip6_address_to_string (&addr), " flags mngtmpaddr,noprefixroute "); - - addr.flags = IFA_F_TENTATIVE | IFA_F_NOPREFIXROUTE; - nmtst_assert_str_has_substr (nm_platform_ip6_address_to_string (&addr), " flags tentative,noprefixroute "); - - addr.flags = IFA_F_TENTATIVE | IFA_F_PERMANENT | IFA_F_MANAGETEMPADDR| IFA_F_NOPREFIXROUTE; - nmtst_assert_str_has_substr (nm_platform_ip6_address_to_string (&addr), " flags tentative,permanent,mngtmpaddr,noprefixroute "); - - addr.flags = IFA_F_TENTATIVE | IFA_F_PERMANENT | IFA_F_MANAGETEMPADDR| IFA_F_NOPREFIXROUTE | 0x8000; - nmtst_assert_str_has_substr (nm_platform_ip6_address_to_string (&addr), " flags tentative,permanent,mngtmpaddr,noprefixroute, "); - - addr.flags = IFA_F_TENTATIVE | IFA_F_PERMANENT | IFA_F_MANAGETEMPADDR| IFA_F_NOPREFIXROUTE | ((G_MAXUINT - (G_MAXUINT >> 1)) >> 1); - nmtst_assert_str_has_substr (nm_platform_ip6_address_to_string (&addr), " flags tentative,permanent,mngtmpaddr,noprefixroute, "); -} - -/******************************************************************/ - NMTST_DEFINE (); int @@ -93,7 +62,6 @@ main (int argc, char **argv) g_test_add_func ("/general/init_linux_platform", test_init_linux_platform); g_test_add_func ("/general/link_get_all", test_link_get_all); - g_test_add_func ("/general/nm_platform_ip6_address_to_string/flags", test_nm_platform_ip6_address_to_string_flags); return g_test_run (); } diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index e6ab6bdd84..9030f0e4df 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -1,5 +1,7 @@ #include "config.h" +#include "nmp-object.h" + #include "test-common.h" #include "nm-test-utils.h" @@ -45,7 +47,7 @@ test_bogus(void) g_assert (!nm_platform_link_supports_carrier_detect (NM_PLATFORM_GET, BOGUS_IFINDEX)); g_assert (!nm_platform_link_supports_vlans (NM_PLATFORM_GET, BOGUS_IFINDEX)); - g_assert (!nm_platform_vlan_get_info (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL, NULL)); + g_assert (!nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, BOGUS_IFINDEX, NULL)); g_assert (!nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, BOGUS_IFINDEX, 0, 0)); g_assert (!nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, BOGUS_IFINDEX, 0, 0)); } @@ -305,7 +307,7 @@ test_software (NMLinkType link_type, const char *link_typename) { int ifindex; char *value; - int vlan_parent, vlan_id; + int vlan_parent = -1, vlan_id; SignalData *link_added, *link_changed, *link_removed; @@ -321,7 +323,15 @@ test_software (NMLinkType link_type, const char *link_typename) link_changed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_CHANGED, link_callback, ifindex); link_removed = add_signal_ifindex (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_REMOVED, link_callback, ifindex); if (link_type == NM_LINK_TYPE_VLAN) { - g_assert (nm_platform_vlan_get_info (NM_PLATFORM_GET, ifindex, &vlan_parent, &vlan_id)); + const NMPlatformLink *plink; + const NMPlatformLnkVlan *plnk; + + plnk = nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, ifindex, &plink); + g_assert (plnk); + g_assert (plink); + + vlan_parent = plink->parent; + vlan_id = plnk->id; g_assert_cmpint (vlan_parent, ==, nm_platform_link_get_ifindex (NM_PLATFORM_GET, PARENT_NAME)); g_assert_cmpint (vlan_id, ==, VLAN_ID); } @@ -430,6 +440,52 @@ test_vlan (void) test_software (NM_LINK_TYPE_VLAN, "vlan"); } +/*****************************************************************************/ + +static void +test_bridge_addr (void) +{ + char addr[ETH_ALEN]; + NMPlatformLink link; + const NMPlatformLink *plink; + + nm_utils_hwaddr_aton ("de:ad:be:ef:00:11", addr, sizeof (addr)); + + g_assert_cmpint (nm_platform_bridge_add (NM_PLATFORM_GET, DEVICE_NAME, addr, sizeof (addr), &link), ==, NM_PLATFORM_ERROR_SUCCESS); + g_assert_cmpstr (link.name, ==, DEVICE_NAME); + + g_assert_cmpint (link.addr.len, ==, sizeof (addr)); + g_assert (!memcmp (link.addr.data, addr, sizeof (addr))); + + plink = nm_platform_link_get (NM_PLATFORM_GET, link.ifindex); + g_assert (plink); + + if (nm_platform_check_support_user_ipv6ll (NM_PLATFORM_GET)) { + g_assert (!nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex)); + g_assert_cmpint (_nm_platform_uint8_inv (plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_EUI64); + + g_assert (nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex, TRUE)); + g_assert (nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex)); + plink = nm_platform_link_get (NM_PLATFORM_GET, link.ifindex); + g_assert (plink); + g_assert_cmpint (_nm_platform_uint8_inv (plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_NONE); + + g_assert (nm_platform_link_set_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex, FALSE)); + g_assert (!nm_platform_link_get_user_ipv6ll_enabled (NM_PLATFORM_GET, link.ifindex)); + plink = nm_platform_link_get (NM_PLATFORM_GET, link.ifindex); + g_assert (plink); + g_assert_cmpint (_nm_platform_uint8_inv (plink->inet6_addr_gen_mode_inv), ==, NM_IN6_ADDR_GEN_MODE_EUI64); + } + + g_assert_cmpint (plink->addr.len, ==, sizeof (addr)); + g_assert (!memcmp (plink->addr.data, addr, sizeof (addr))); + + g_assert (nm_platform_link_delete (NM_PLATFORM_GET, link.ifindex)); + g_assert (!nm_platform_link_get (NM_PLATFORM_GET, link.ifindex)); +} + +/*****************************************************************************/ + static void test_internal (void) { @@ -511,14 +567,17 @@ test_internal (void) free_signal (link_removed); } +/*****************************************************************************/ + static void test_external (void) { const NMPlatformLink *pllink; - SignalData *link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME); - SignalData *link_changed, *link_removed; + SignalData *link_added, *link_changed, *link_removed; int ifindex; + link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, NM_PLATFORM_SIGNAL_ADDED, link_callback, DEVICE_NAME); + nmtstp_run_command_check ("ip link add %s type %s", DEVICE_NAME, "dummy"); wait_signal (link_added); @@ -570,6 +629,731 @@ test_external (void) free_signal (link_removed); } +/*****************************************************************************/ + +typedef struct { + NMLinkType link_type; + int test_mode; +} TestAddSoftwareDetectData; + +static void +test_software_detect (gconstpointer user_data) +{ + const TestAddSoftwareDetectData *test_data = user_data; + int ifindex, ifindex_parent; + const NMPlatformLink *plink; + const NMPObject *lnk; + guint i_step; + int exit_code; + + nmtstp_run_command_check ("ip link add %s type dummy", PARENT_NAME); + ifindex_parent = nmtstp_assert_wait_for_link (PARENT_NAME, NM_LINK_TYPE_DUMMY, 100)->ifindex; + + switch (test_data->link_type) { + case NM_LINK_TYPE_GRE: { + gboolean gracefully_skip = FALSE; + + if (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "gre0")) { + /* Seems that the ip_gre module is not loaded... try to load it. */ + gracefully_skip = nm_utils_modprobe (NULL, TRUE, "ip_gre", NULL) != 0; + } + exit_code = nmtstp_run_command ("ip tunnel add %s mode gre remote 172.168.10.25 local 192.168.233.204 ttl 174", DEVICE_NAME); + if (exit_code != 0) { + if (gracefully_skip) { + g_test_skip ("Cannot create gre tunnel because of missing ip_gre module (modprobe ip_gre)"); + goto out_delete_parent; + } + g_error ("Failed adding GRE tunnel: exit code %d", exit_code); + } + break; + } + case NM_LINK_TYPE_MACVLAN: + nmtstp_run_command_check ("ip link add name %s link %s type macvlan", DEVICE_NAME, PARENT_NAME); + break; + case NM_LINK_TYPE_VLAN: + nmtstp_run_command_check ("ip link add name %s link %s type vlan id 1242", DEVICE_NAME, PARENT_NAME); + break; + case NM_LINK_TYPE_VXLAN: + switch (test_data->test_mode) { + case 0: + nmtstp_run_command_check ("ip link add %s type vxlan id 42 local 23.1.2.164 group 239.1.2.134 dev %s ageing 1245 dstport 4789", DEVICE_NAME, PARENT_NAME); + break; + case 1: + nmtstp_run_command_check ("ip link add %s type vxlan id 11214423 local 1:2:3:4:334:23::23 group ff0e::115 dev %s ageing 3245 dstport 57412", DEVICE_NAME, PARENT_NAME); + break; + } + break; + default: + g_assert_not_reached (); + } + + ifindex = nmtstp_assert_wait_for_link (DEVICE_NAME, test_data->link_type, 100)->ifindex; + + nmtstp_link_set_updown (-1, ifindex_parent, TRUE); + + for (i_step = 0; i_step < 5; i_step++) { + + _LOGD ("test-software-detect: step %u", i_step); + if (nmtst_is_debug ()) + nmtstp_run_command_check ("ip -d link show %s", DEVICE_NAME); + + if (i_step > 0) { + gboolean set_up = (i_step % 2) == 1; + + if ( test_data->link_type == NM_LINK_TYPE_VXLAN + && set_up) { + /* On RHEL-7, we need to add a tiny sleep here, otherwise, + * upping the vxlan device fails with EADDRINUSE. + * https://bugzilla.redhat.com/show_bug.cgi?id=1277131 */ + g_usleep (1); + } + nmtstp_link_set_updown (-1, ifindex, set_up); + } + + lnk = nm_platform_link_get_lnk (NM_PLATFORM_GET, ifindex, test_data->link_type, &plink); + g_assert (plink); + g_assert_cmpint (plink->ifindex, ==, ifindex); + g_assert (lnk); + + switch (test_data->link_type) { + case NM_LINK_TYPE_GRE: { + const NMPlatformLnkGre *plnk = &lnk->lnk_gre; + + g_assert (plnk == nm_platform_link_get_lnk_gre (NM_PLATFORM_GET, ifindex, NULL)); + g_assert_cmpint (plnk->parent_ifindex, ==, 0); + g_assert_cmpint (plnk->input_flags, ==, 0); + g_assert_cmpint (plnk->output_flags, ==, 0); + g_assert_cmpint (plnk->input_key, ==, 0); + g_assert_cmpint (plnk->output_key, ==, 0); + nmtst_assert_ip4_address (plnk->local, "192.168.233.204"); + nmtst_assert_ip4_address (plnk->remote, "172.168.10.25"); + g_assert_cmpint (plnk->ttl, ==, 174); + g_assert_cmpint (plnk->tos, ==, 0); + g_assert_cmpint (plnk->path_mtu_discovery, ==, TRUE); + break; + } + case NM_LINK_TYPE_MACVLAN: { + const NMPlatformLnkMacvlan *plnk = &lnk->lnk_macvlan; + + g_assert (plnk == nm_platform_link_get_lnk_macvlan (NM_PLATFORM_GET, ifindex, NULL)); + g_assert_cmpint (plnk->no_promisc, ==, FALSE); + g_assert_cmpstr (plnk->mode, ==, "vepa"); + break; + } + case NM_LINK_TYPE_VLAN: { + const NMPlatformLnkVlan *plnk = &lnk->lnk_vlan; + + g_assert (plnk == nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, ifindex, NULL)); + g_assert_cmpint (plnk->id, ==, 1242); + break; + } + case NM_LINK_TYPE_VXLAN: { + const NMPlatformLnkVxlan *plnk = &lnk->lnk_vxlan; + + g_assert (plnk == nm_platform_link_get_lnk_vxlan (NM_PLATFORM_GET, ifindex, NULL)); + g_assert_cmpint (plnk->parent_ifindex, !=, 0); + g_assert_cmpint (plnk->tos, ==, 0); + g_assert_cmpint (plnk->ttl, ==, 0); + g_assert_cmpint (plnk->learning, ==, TRUE); + g_assert_cmpint (plnk->limit, ==, 0); + g_assert_cmpint (plnk->src_port_min, ==, 0); + g_assert_cmpint (plnk->src_port_max, ==, 0); + g_assert_cmpint (plnk->proxy, ==, FALSE); + g_assert_cmpint (plnk->rsc, ==, FALSE); + g_assert_cmpint (plnk->l2miss, ==, FALSE); + g_assert_cmpint (plnk->l3miss, ==, FALSE); + + switch (test_data->test_mode) { + case 0: + g_assert_cmpint (plnk->id, ==, 42); + nmtst_assert_ip4_address (plnk->local, "23.1.2.164"); + nmtst_assert_ip4_address (plnk->group, "239.1.2.134"); + nmtst_assert_ip6_address (&plnk->group6, "::"); + nmtst_assert_ip6_address (&plnk->local6, "::"); + g_assert_cmpint (plnk->ageing, ==, 1245); + g_assert_cmpint (plnk->dst_port, ==, 4789); + break; + case 1: + g_assert_cmpint (plnk->id, ==, 11214423); + nmtst_assert_ip4_address (plnk->local, "0.0.0.0"); + nmtst_assert_ip4_address (plnk->group, "0.0.0.0"); + nmtst_assert_ip6_address (&plnk->group6, "ff0e::115"); + nmtst_assert_ip6_address (&plnk->local6, "1:2:3:4:334:23::23"); + g_assert_cmpint (plnk->ageing, ==, 3245); + g_assert_cmpint (plnk->dst_port, ==, 57412); + break; + } + break; + } + default: + g_assert_not_reached (); + } + } + + g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); +out_delete_parent: + g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex_parent)); +} + +static void +test_software_detect_add (const char *testpath, + NMLinkType link_type, + int test_mode) +{ + TestAddSoftwareDetectData *test_data; + + test_data = g_new0 (TestAddSoftwareDetectData, 1); + test_data->link_type = link_type; + test_data->test_mode = test_mode; + + g_test_add_data_func_full (testpath, test_data, test_software_detect, g_free); +} + +/*****************************************************************************/ + +static void +_assert_xgress_qos_mappings_impl (int ifindex, + gboolean is_ingress_map , + int n_entries, + int n, + ...) +{ + const NMPlatformLink *plink; + const NMPObject *lnk; + guint n_map; + const NMVlanQosMapping *map; + va_list ap; + guint i; + + lnk = nm_platform_link_get_lnk (NM_PLATFORM_GET, ifindex, NM_LINK_TYPE_VLAN, &plink); + + g_assert (plink); + g_assert_cmpint (plink->ifindex, ==, ifindex); + g_assert (lnk); + g_assert (&lnk->lnk_vlan == nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, ifindex, NULL)); + + if (nmtst_is_debug ()) + nmtstp_run_command_check ("ip -d link show %s", plink->name); + + if (is_ingress_map) { + map = lnk->_lnk_vlan.ingress_qos_map; + n_map = lnk->_lnk_vlan.n_ingress_qos_map; + } else { + map = lnk->_lnk_vlan.egress_qos_map; + n_map = lnk->_lnk_vlan.n_egress_qos_map; + } + + if (n_entries != -1) + g_assert_cmpint (n_map, ==, n_entries); + + for (i = 0; i < n_map; i++) { + if (is_ingress_map) { + g_assert_cmpint (map[i].from, >=, 0); + g_assert_cmpint (map[i].from, <=, 7); + } + if (i > 0) + g_assert_cmpint (map[i - 1].from, <, map[i].from); + } + + va_start (ap, n); + for (; n > 0; n--) { + gboolean found = FALSE; + guint from = va_arg (ap, guint); + guint to = va_arg (ap, guint); + + for (i = 0; i < n_map; i++) { + if (map[i].from == from) { + g_assert (!found); + found = TRUE; + + g_assert (map[i].to == to); + } + } + g_assert (found); + } + va_end (ap); +} +#define _assert_xgress_qos_mappings(ifindex, is_ingress_map, n_entries, ...) \ + _assert_xgress_qos_mappings_impl ((ifindex), (is_ingress_map), (n_entries), \ + (G_STATIC_ASSERT_EXPR ((NM_NARG (__VA_ARGS__) % 2) == 0), NM_NARG (__VA_ARGS__) / 2), \ + __VA_ARGS__) +#define _assert_ingress_qos_mappings(ifindex, n_entries, ...) _assert_xgress_qos_mappings (ifindex, TRUE, n_entries, __VA_ARGS__) +#define _assert_egress_qos_mappings(ifindex, n_entries, ...) _assert_xgress_qos_mappings (ifindex, FALSE, n_entries, __VA_ARGS__) + +static void +_assert_vlan_flags (int ifindex, NMVlanFlags flags) +{ + const NMPlatformLnkVlan *plnk; + + plnk = nm_platform_link_get_lnk_vlan (NM_PLATFORM_GET, ifindex, NULL); + g_assert (plnk); + g_assert_cmpint (plnk->flags, ==, flags); +} + +static void +test_vlan_set_xgress (void) +{ + int ifindex, ifindex_parent; + + nmtstp_run_command_check ("ip link add %s type dummy", PARENT_NAME); + ifindex_parent = nmtstp_assert_wait_for_link (PARENT_NAME, NM_LINK_TYPE_DUMMY, 100)->ifindex; + + nmtstp_run_command_check ("ip link add name %s link %s type vlan id 1245", DEVICE_NAME, PARENT_NAME); + ifindex = nmtstp_assert_wait_for_link (DEVICE_NAME, NM_LINK_TYPE_VLAN, 100)->ifindex; + + /* ingress-qos-map */ + + g_assert (nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 4, 5)); + _assert_ingress_qos_mappings (ifindex, 1, + 4, 5); + + g_assert (nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 3, 7)); + _assert_ingress_qos_mappings (ifindex, 2, + 3, 7, + 4, 5); + + g_assert (nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 3, 8)); + _assert_ingress_qos_mappings (ifindex, 2, + 3, 8, + 4, 5); + + g_assert (nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 0, 4)); + _assert_ingress_qos_mappings (ifindex, 3, + 0, 4, + 3, 8, + 4, 5); + + g_assert (nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 0, G_MAXUINT32)); + _assert_ingress_qos_mappings (ifindex, 3, + 0, G_MAXUINT32, + 3, 8, + 4, 5); + + g_assert (nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 0, G_MAXUINT32 - 1)); + _assert_ingress_qos_mappings (ifindex, 3, + 0, G_MAXUINT32 - 1, + 3, 8, + 4, 5); + + g_assert (nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 0, 5)); + _assert_ingress_qos_mappings (ifindex, 3, + 0, 5, + 3, 8, + 4, 5); + + g_assert (nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 0, 5)); + _assert_ingress_qos_mappings (ifindex, 3, + 0, 5, + 3, 8, + 4, 5); + + /* Set invalid values: */ + g_assert (nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 8, 3)); + _assert_ingress_qos_mappings (ifindex, 3, + 0, 5, + 3, 8, + 4, 5); + + g_assert (nm_platform_vlan_set_ingress_map (NM_PLATFORM_GET, ifindex, 9, 4)); + _assert_ingress_qos_mappings (ifindex, 3, + 0, 5, + 3, 8, + 4, 5); + + /* egress-qos-map */ + + g_assert (nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 7, 3)); + _assert_egress_qos_mappings (ifindex, 1, + 7, 3); + + g_assert (nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 8, 4)); + _assert_egress_qos_mappings (ifindex, 2, + 7, 3, + 8, 4); + + g_assert (nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 0, 4)); + _assert_egress_qos_mappings (ifindex, 3, + 0, 4, + 7, 3, + 8, 4); + + g_assert (nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 1, 4)); + _assert_egress_qos_mappings (ifindex, 4, + 0, 4, + 1, 4, + 7, 3, + 8, 4); + + g_assert (nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 1, 5)); + _assert_egress_qos_mappings (ifindex, 4, + 0, 4, + 1, 5, + 7, 3, + 8, 4); + + g_assert (nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 9, 5)); + _assert_egress_qos_mappings (ifindex, 5, + 0, 4, + 1, 5, + 7, 3, + 8, 4, + 9, 5); + + g_assert (nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 8, 5)); + _assert_egress_qos_mappings (ifindex, 5, + 0, 4, + 1, 5, + 7, 3, + 8, 5, + 9, 5); + + g_assert (nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 8, 0)); + _assert_egress_qos_mappings (ifindex, 4, + 0, 4, + 1, 5, + 7, 3, + 9, 5); + + g_assert (nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 0, 0)); + _assert_egress_qos_mappings (ifindex, 3, + 1, 5, + 7, 3, + 9, 5); + + g_assert (nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 100, 4)); + _assert_egress_qos_mappings (ifindex, 4, + 1, 5, + 7, 3, + 9, 5, + 100, 4); + + g_assert (nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, G_MAXUINT32, 4)); + _assert_egress_qos_mappings (ifindex, 5, + 1, 5, + 7, 3, + 9, 5, + 100, 4, + G_MAXUINT32, 4); + + g_assert (nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, G_MAXUINT32, 8)); + _assert_egress_qos_mappings (ifindex, 5, + 1, 5, + 7, 3, + 9, 5, + 100, 4, + G_MAXUINT32, 4); + + g_assert (nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, G_MAXUINT32, 0)); + _assert_egress_qos_mappings (ifindex, 4, + 1, 5, + 7, 3, + 9, 5, + 100, 4); + + g_assert (nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 100, 0)); + _assert_egress_qos_mappings (ifindex, 3, + 1, 5, + 7, 3, + 9, 5); + + g_assert (nm_platform_vlan_set_egress_map (NM_PLATFORM_GET, ifindex, 1, 0)); + _assert_egress_qos_mappings (ifindex, 2, + 7, 3, + 9, 5); + + { + const NMVlanQosMapping ingress_map[] = { + { .from = 1, .to = 5 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + TRUE, + ingress_map, + G_N_ELEMENTS (ingress_map), + FALSE, + NULL, + 0)); + _assert_ingress_qos_mappings (ifindex, 1, + 1, 5); + } + + { + const NMVlanQosMapping ingress_map[] = { + { .from = 3, .to = 5 }, + { .from = 7, .to = 1655 }, + { .from = 7, .to = 17655 }, + { .from = 5, .to = 754 }, + { .from = 4, .to = 12 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + TRUE, + ingress_map, + G_N_ELEMENTS (ingress_map), + FALSE, + NULL, + 0)); + _assert_ingress_qos_mappings (ifindex, 4, + 3, 5, + 4, 12, + 7, 17655, + 5, 754); + } + + { + const NMVlanQosMapping ingress_map[] = { + { .from = 3, .to = 18 }, + { .from = 6, .to = 121 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + FALSE, + ingress_map, + G_N_ELEMENTS (ingress_map), + FALSE, + NULL, + 0)); + _assert_ingress_qos_mappings (ifindex, 5, + 3, 18, + 4, 12, + 6, 121, + 7, 17655, + 5, 754); + } + + { + const NMVlanQosMapping ingress_map[] = { + { .from = 3, .to = 0 }, + { .from = 6, .to = 7 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + TRUE, + ingress_map, + G_N_ELEMENTS (ingress_map), + FALSE, + NULL, + 0)); + _assert_ingress_qos_mappings (ifindex, 1, + 6, 7); + } + + + { + const NMVlanQosMapping ingress_map[] = { + { .from = 1, .to = 5 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + TRUE, + ingress_map, + G_N_ELEMENTS (ingress_map), + FALSE, + NULL, + 0)); + _assert_ingress_qos_mappings (ifindex, 1, + 1, 5); + } + + { + const NMVlanQosMapping egress_map[] = { + { .from = 5, .to = 1 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + FALSE, + NULL, + 0, + TRUE, + egress_map, + G_N_ELEMENTS (egress_map))); + _assert_egress_qos_mappings (ifindex, 1, + 5, 1); + } + + { + const NMVlanQosMapping egress_map[] = { + { .from = 5, .to = 3 }, + { .from = 1655, .to = 5 }, + { .from = 1655, .to = 7 }, + { .from = G_MAXUINT32, .to = 6 }, + { .from = G_MAXUINT32, .to = 8 }, + { .from = 754, .to = 4 }, + { .from = 3, .to = 2 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + FALSE, + NULL, + 0, + TRUE, + egress_map, + G_N_ELEMENTS (egress_map))); + _assert_egress_qos_mappings (ifindex, 5, + 3, 2, + 5, 3, + 754, 4, + 1655, 7, + G_MAXUINT32, 6); + } + + { + const NMVlanQosMapping egress_map[] = { + { .from = 754, .to = 3 }, + { .from = 755, .to = 8 }, + { .from = 1655, .to = 0 }, + { .from = 6, .to = 1 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + FALSE, + NULL, + 0, + FALSE, + egress_map, + G_N_ELEMENTS (egress_map))); + _assert_egress_qos_mappings (ifindex, 5, + 3, 2, + 5, 3, + 6, 1, + 754, 3, + G_MAXUINT32, 6); + } + + { + const NMVlanQosMapping egress_map[] = { + { .from = 6, .to = 0 }, + { .from = 3, .to = 4 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + FALSE, + NULL, + 0, + TRUE, + egress_map, + G_N_ELEMENTS (egress_map))); + _assert_egress_qos_mappings (ifindex, 1, + 3, 4); + } + + { + const NMVlanQosMapping egress_map[] = { + { .from = 1, .to = 5 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + 0, + 0, + FALSE, + NULL, + 0, + TRUE, + egress_map, + G_N_ELEMENTS (egress_map))); + _assert_egress_qos_mappings (ifindex, 1, + 1, 5); + } + + { + const NMVlanQosMapping ingress_map[] = { + { .from = 6, .to = 145 }, + { .from = 4, .to = 1 }, + { .from = 6, .to = 12 }, + }; + const NMVlanQosMapping egress_map[] = { + { .from = 1, .to = 5 }, + { .from = 3232, .to = 7 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + NM_VLAN_FLAG_REORDER_HEADERS | NM_VLAN_FLAG_GVRP, + NM_VLAN_FLAG_REORDER_HEADERS, + TRUE, + ingress_map, + G_N_ELEMENTS (ingress_map), + TRUE, + egress_map, + G_N_ELEMENTS (egress_map))); + _assert_ingress_qos_mappings (ifindex, 2, + 4, 1, + 6, 12); + _assert_egress_qos_mappings (ifindex, 2, + 1, 5, + 3232, 7); + _assert_vlan_flags (ifindex, NM_VLAN_FLAG_REORDER_HEADERS); + } + + { + const NMVlanQosMapping ingress_map[] = { + { .from = 6, .to = 145 }, + { .from = 4, .to = 1 }, + { .from = 6, .to = 12 }, + }; + const NMVlanQosMapping egress_map[] = { + { .from = 1, .to = 7 }, + { .from = 64, .to = 10 }, + { .from = 64, .to = 10 }, + { .from = 64, .to = 10 }, + { .from = 64, .to = 10 }, + { .from = 3232, .to = 0 }, + { .from = 64, .to = 4 }, + }; + + g_assert (nm_platform_link_vlan_change (NM_PLATFORM_GET, + ifindex, + NM_VLAN_FLAG_GVRP, + NM_VLAN_FLAG_GVRP, + FALSE, + ingress_map, + G_N_ELEMENTS (ingress_map), + FALSE, + egress_map, + G_N_ELEMENTS (egress_map))); + _assert_ingress_qos_mappings (ifindex, 2, + 4, 1, + 6, 12); + _assert_egress_qos_mappings (ifindex, 2, + 1, 7, + 64, 4); + _assert_vlan_flags (ifindex, NM_VLAN_FLAG_REORDER_HEADERS | NM_VLAN_FLAG_GVRP); + } + + g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex)); + g_assert (nm_platform_link_delete (NM_PLATFORM_GET, ifindex_parent)); +} + +/*****************************************************************************/ + void init_tests (int *argc, char ***argv) { @@ -593,7 +1377,17 @@ setup_tests (void) g_test_add_func ("/link/software/bond", test_bond); g_test_add_func ("/link/software/team", test_team); g_test_add_func ("/link/software/vlan", test_vlan); + g_test_add_func ("/link/software/bridge/addr", test_bridge_addr); - if (strcmp (g_type_name (G_TYPE_FROM_INSTANCE (nm_platform_get ())), "NMFakePlatform")) + if (nmtstp_is_root_test ()) { g_test_add_func ("/link/external", test_external); + + test_software_detect_add ("/link/software/detect/gre", NM_LINK_TYPE_GRE, 0); + test_software_detect_add ("/link/software/detect/macvlan", NM_LINK_TYPE_MACVLAN, 0); + test_software_detect_add ("/link/software/detect/vlan", NM_LINK_TYPE_VLAN, 0); + test_software_detect_add ("/link/software/detect/vxlan/0", NM_LINK_TYPE_VXLAN, 0); + test_software_detect_add ("/link/software/detect/vxlan/1", NM_LINK_TYPE_VXLAN, 1); + + g_test_add_func ("/link/software/vlan/set-xgress", test_vlan_set_xgress); + } } diff --git a/src/platform/wifi/wifi-utils-nl80211.c b/src/platform/wifi/wifi-utils-nl80211.c index a4d4017bc2..e05104f374 100644 --- a/src/platform/wifi/wifi-utils-nl80211.c +++ b/src/platform/wifi/wifi-utils-nl80211.c @@ -28,12 +28,8 @@ #include <net/ethernet.h> #include <unistd.h> #include <math.h> - - -#include <netlink/genl/genl.h> -#include <netlink/genl/family.h> -#include <netlink/genl/ctrl.h> - +#include <netlink/netlink.h> +#include <netlink/msg.h> #include <linux/nl80211.h> #include "nm-default.h" @@ -42,6 +38,198 @@ #include "nm-platform.h" #include "nm-utils.h" + +/***************************************************************************** + * Copied from libnl3/genl: + *****************************************************************************/ + +static void * +genlmsg_put (struct nl_msg *msg, uint32_t port, uint32_t seq, int family, + int hdrlen, int flags, uint8_t cmd, uint8_t version) +{ + struct nlmsghdr *nlh; + struct genlmsghdr hdr = { + .cmd = cmd, + .version = version, + }; + + nlh = nlmsg_put (msg, port, seq, family, GENL_HDRLEN + hdrlen, flags); + if (nlh == NULL) + return NULL; + + memcpy (nlmsg_data (nlh), &hdr, sizeof (hdr)); + + return (char *) nlmsg_data (nlh) + GENL_HDRLEN; +} + +static void * +genlmsg_data (const struct genlmsghdr *gnlh) +{ + return ((unsigned char *) gnlh + GENL_HDRLEN); +} + +static void * +genlmsg_user_hdr (const struct genlmsghdr *gnlh) +{ + return genlmsg_data (gnlh); +} + +static struct genlmsghdr * +genlmsg_hdr (struct nlmsghdr *nlh) +{ + return nlmsg_data (nlh); +} + +static void * +genlmsg_user_data (const struct genlmsghdr *gnlh, const int hdrlen) +{ + return (char *) genlmsg_user_hdr (gnlh) + NLMSG_ALIGN (hdrlen); +} + +static struct nlattr * +genlmsg_attrdata (const struct genlmsghdr *gnlh, int hdrlen) +{ + return genlmsg_user_data (gnlh, hdrlen); +} + +static int +genlmsg_len (const struct genlmsghdr *gnlh) +{ + const struct nlmsghdr *nlh; + + nlh = (const struct nlmsghdr *) ((const unsigned char *) gnlh - NLMSG_HDRLEN); + return (nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN); +} + +static int +genlmsg_attrlen (const struct genlmsghdr *gnlh, int hdrlen) +{ + return genlmsg_len (gnlh) - NLMSG_ALIGN (hdrlen); +} + +static int +genlmsg_valid_hdr (struct nlmsghdr *nlh, int hdrlen) +{ + struct genlmsghdr *ghdr; + + if (!nlmsg_valid_hdr (nlh, GENL_HDRLEN)) + return 0; + + ghdr = nlmsg_data (nlh); + if (genlmsg_len (ghdr) < NLMSG_ALIGN (hdrlen)) + return 0; + + return 1; +} + +static int +genlmsg_parse (struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], + int maxtype, struct nla_policy *policy) +{ + struct genlmsghdr *ghdr; + + if (!genlmsg_valid_hdr (nlh, hdrlen)) + return -NLE_MSG_TOOSHORT; + + ghdr = nlmsg_data (nlh); + return nla_parse (tb, maxtype, genlmsg_attrdata (ghdr, hdrlen), + genlmsg_attrlen (ghdr, hdrlen), policy); +} + +/***************************************************************************** + * Reimplementation of libnl3/genl functions: + *****************************************************************************/ + +static int +probe_response (struct nl_msg *msg, void *arg) +{ + static struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = { + [CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 }, + [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_STRING, + .maxlen = GENL_NAMSIZ }, + [CTRL_ATTR_VERSION] = { .type = NLA_U32 }, + [CTRL_ATTR_HDRSIZE] = { .type = NLA_U32 }, + [CTRL_ATTR_MAXATTR] = { .type = NLA_U32 }, + [CTRL_ATTR_OPS] = { .type = NLA_NESTED }, + [CTRL_ATTR_MCAST_GROUPS] = { .type = NLA_NESTED }, + }; + struct nlattr *tb[CTRL_ATTR_MAX+1]; + struct nlmsghdr *nlh = nlmsg_hdr (msg); + gint32 *response_data = arg; + + if (genlmsg_parse (nlh, 0, tb, CTRL_ATTR_MAX, ctrl_policy)) + return NL_SKIP; + + if (tb[CTRL_ATTR_FAMILY_ID]) + *response_data = nla_get_u16 (tb[CTRL_ATTR_FAMILY_ID]); + + return NL_STOP; +} + +static int +genl_ctrl_resolve (struct nl_sock *sk, const char *name) +{ + struct nl_msg *msg; + struct nl_cb *cb, *orig; + int rc; + int result = -NLE_OBJ_NOTFOUND; + gint32 response_data = -1; + + if (!(orig = nl_socket_get_cb (sk))) + goto out; + + cb = nl_cb_clone (orig); + nl_cb_put (orig); + if (!cb) + goto out; + + msg = nlmsg_alloc (); + if (!msg) + goto out_cb_free; + + if (!genlmsg_put (msg, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL, + 0, 0, CTRL_CMD_GETFAMILY, 1)) + goto out_msg_free; + + if (nla_put_string (msg, CTRL_ATTR_FAMILY_NAME, name) < 0) + goto out_msg_free; + + rc = nl_cb_set (cb, NL_CB_VALID, NL_CB_CUSTOM, probe_response, &response_data); + if (rc < 0) + goto out_msg_free; + + rc = nl_send_auto_complete (sk, msg); + if (rc < 0) + goto out_msg_free; + + rc = nl_recvmsgs (sk, cb); + if (rc < 0) + goto out_msg_free; + + /* If search was successful, request may be ACKed after data */ + rc = nl_wait_for_ack (sk); + if (rc < 0) + goto out_msg_free; + + if (response_data > 0) + result = response_data; + +out_msg_free: + nlmsg_free (msg); +out_cb_free: + nl_cb_put (cb); +out: + if (result >= 0) + nm_log_dbg (LOGD_WIFI, "genl_ctrl_resolve: resolved \"%s\" as 0x%x", name, result); + else + nm_log_err (LOGD_WIFI, "genl_ctrl_resolve: failed resolve \"%s\"", name); + return result; +} + +/***************************************************************************** + * </libn-genl-3> + *****************************************************************************/ + typedef struct { WifiData parent; struct nl_sock *nl_sock; @@ -143,7 +331,7 @@ _nl80211_send_and_recv (struct nl_sock *nl_sock, * not warn on DUMP_INTR error for get scan command. */ if (err == -NLE_DUMP_INTR && - genlmsg_hdr(nlmsg_hdr(msg))->cmd == NL80211_CMD_GET_SCAN) + genlmsg_hdr (nlmsg_hdr (msg))->cmd == NL80211_CMD_GET_SCAN) break; nm_log_warn (LOGD_WIFI, "nl_recvmsgs() error: (%d) %s", @@ -290,7 +478,7 @@ nl80211_xbm_to_percent (gint32 xbm, guint32 divisor) #define SIGNAL_MAX_DBM -20 xbm /= divisor; - xbm = CLAMP(xbm, NOISE_FLOOR_DBM, SIGNAL_MAX_DBM); + xbm = CLAMP (xbm, NOISE_FLOOR_DBM, SIGNAL_MAX_DBM); return 100 - 70 * (((float) SIGNAL_MAX_DBM - (float) xbm) / ((float) SIGNAL_MAX_DBM - (float) NOISE_FLOOR_DBM)); @@ -370,7 +558,7 @@ nl80211_bss_dump_handler (struct nl_msg *msg, void *arg) if (bss[NL80211_BSS_BSSID] == NULL) return NL_SKIP; - memcpy(info->bssid, nla_data (bss[NL80211_BSS_BSSID]), ETH_ALEN); + memcpy (info->bssid, nla_data (bss[NL80211_BSS_BSSID]), ETH_ALEN); if (bss[NL80211_BSS_FREQUENCY]) info->freq = nla_get_u32 (bss[NL80211_BSS_FREQUENCY]); @@ -387,7 +575,7 @@ nl80211_bss_dump_handler (struct nl_msg *msg, void *arg) guint8 *ssid; guint32 ssid_len; - find_ssid(nla_data (bss[NL80211_BSS_INFORMATION_ELEMENTS]), + find_ssid (nla_data (bss[NL80211_BSS_INFORMATION_ELEMENTS]), nla_len (bss[NL80211_BSS_INFORMATION_ELEMENTS]), &ssid, &ssid_len); if (ssid && ssid_len && ssid_len <= sizeof (info->ssid)) { @@ -407,7 +595,7 @@ nl80211_get_bss_info (WifiDataNl80211 *nl80211, { struct nl_msg *msg; - memset(bss_info, 0, sizeof (*bss_info)); + memset (bss_info, 0, sizeof (*bss_info)); msg = nl80211_alloc_msg (nl80211, NL80211_CMD_GET_SCAN, NLM_F_DUMP); @@ -450,7 +638,7 @@ wifi_nl80211_get_bssid (WifiData *data, guint8 *out_bssid) nl80211_get_bss_info (nl80211, &bss_info); if (bss_info.valid) - memcpy(out_bssid, bss_info.bssid, ETH_ALEN); + memcpy (out_bssid, bss_info.bssid, ETH_ALEN); return bss_info.valid; } @@ -532,7 +720,7 @@ nl80211_get_ap_info (WifiDataNl80211 *nl80211, struct nl_msg *msg; struct nl80211_bss_info bss_info; - memset(sta_info, 0, sizeof (*sta_info)); + memset (sta_info, 0, sizeof (*sta_info)); nl80211_get_bss_info (nl80211, &bss_info); if (!bss_info.valid) @@ -620,7 +808,7 @@ nl80211_wowlan_handler (struct nl_msg *msg, void *arg) info->enabled = FALSE; - if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata (gnlh, 0), genlmsg_attrlen (gnlh, 0), NULL) < 0) return NL_SKIP; @@ -694,7 +882,7 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg) G_STATIC_ASSERT (NL80211_FREQUENCY_ATTR_PASSIVE_SCAN != NL80211_FREQUENCY_ATTR_NO_IBSS); #endif - if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + if (nla_parse (tb, NL80211_ATTR_MAX, genlmsg_attrdata (gnlh, 0), genlmsg_attrlen (gnlh, 0), NULL) < 0) return NL_SKIP; @@ -742,13 +930,13 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg) nla_for_each_nested (nl_band, tb[NL80211_ATTR_WIPHY_BANDS], rem_band) { if (nla_parse_nested (tb_band, NL80211_BAND_ATTR_MAX, nl_band, - NULL) < 0) + NULL) < 0) return NL_SKIP; - nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], - rem_freq) { + nla_for_each_nested (nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], + rem_freq) { nla_parse_nested (tb_freq, NL80211_FREQUENCY_ATTR_MAX, - nl_freq, freq_policy); + nl_freq, freq_policy); if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) continue; @@ -766,7 +954,7 @@ static int nl80211_wiphy_info_handler (struct nl_msg *msg, void *arg) NULL) < 0) return NL_SKIP; - nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], + nla_for_each_nested (nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) { nla_parse_nested (tb_freq, NL80211_FREQUENCY_ATTR_MAX, nl_freq, freq_policy); @@ -867,7 +1055,7 @@ wifi_nl80211_init (const char *iface, int ifindex) if (nl80211->nl_sock == NULL) goto error; - if (genl_connect (nl80211->nl_sock)) + if (nl_connect (nl80211->nl_sock, NETLINK_GENERIC)) goto error; nl80211->id = genl_ctrl_resolve (nl80211->nl_sock, "nl80211"); @@ -885,22 +1073,22 @@ wifi_nl80211_init (const char *iface, int ifindex) if (nl80211_send_and_recv (nl80211, msg, nl80211_wiphy_info_handler, &device_info) < 0) { nm_log_dbg (LOGD_HW | LOGD_WIFI, - "(%s): NL80211_CMD_GET_WIPHY request failed", - nl80211->parent.iface); + "(%s): NL80211_CMD_GET_WIPHY request failed", + nl80211->parent.iface); goto error; } if (!device_info.success) { nm_log_dbg (LOGD_HW | LOGD_WIFI, - "(%s): NL80211_CMD_GET_WIPHY request indicated failure", - nl80211->parent.iface); + "(%s): NL80211_CMD_GET_WIPHY request indicated failure", + nl80211->parent.iface); goto error; } if (!device_info.supported) { nm_log_dbg (LOGD_HW | LOGD_WIFI, - "(%s): driver does not fully support nl80211, falling back to WEXT", - nl80211->parent.iface); + "(%s): driver does not fully support nl80211, falling back to WEXT", + nl80211->parent.iface); goto error; } @@ -913,8 +1101,8 @@ wifi_nl80211_init (const char *iface, int ifindex) if (device_info.num_freqs == 0 || device_info.freqs == NULL) { nm_log_err (LOGD_HW | LOGD_WIFI, - "(%s): driver reports no supported frequencies", - nl80211->parent.iface); + "(%s): driver reports no supported frequencies", + nl80211->parent.iface); goto error; } diff --git a/src/ppp-manager/nm-ppp-manager.c b/src/ppp-manager/nm-ppp-manager.c index 825cb9b496..7695063398 100644 --- a/src/ppp-manager/nm-ppp-manager.c +++ b/src/ppp-manager/nm-ppp-manager.c @@ -470,13 +470,14 @@ impl_ppp_manager_set_ip4_config (NMPPPManager *manager, memset (&address, 0, sizeof (address)); address.plen = 32; + if (g_variant_lookup (config_dict, NM_PPP_IP4_CONFIG_ADDRESS, "u", &u32)) + address.address = u32; + if (g_variant_lookup (config_dict, NM_PPP_IP4_CONFIG_GATEWAY, "u", &u32)) { nm_ip4_config_set_gateway (config, u32); address.peer_address = u32; - } - - if (g_variant_lookup (config_dict, NM_PPP_IP4_CONFIG_ADDRESS, "u", &u32)) - address.address = u32; + } else + address.peer_address = address.address; if (g_variant_lookup (config_dict, NM_PPP_IP4_CONFIG_PREFIX, "u", &u32)) address.plen = u32; diff --git a/src/tests/test-general.c b/src/tests/test-general.c index 3985d23ebe..2eb524c3b4 100644 --- a/src/tests/test-general.c +++ b/src/tests/test-general.c @@ -971,6 +971,116 @@ test_nm_match_spec_match_config (void) /*******************************************/ +static void +test_nm_utils_strbuf_append (void) +{ +#define BUF_ORIG "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +#define STR_ORIG "abcdefghijklmnopqrstuvwxyz" + int buf_len; + int rep; + char buf[STRLEN (BUF_ORIG) + 1]; + char str[STRLEN (BUF_ORIG) + 1]; + + for (buf_len = 0; buf_len < 10; buf_len++) { + for (rep = 0; rep < 50; rep++) { + const int s_len = nmtst_get_rand_int () % (sizeof (str) - 5); + char *t_buf; + gsize t_len; + int test_mode; + + strcpy (str, STR_ORIG); + str[s_len] = '\0'; + + g_assert_cmpint (str[sizeof (str) - 1], ==, '\0'); + g_assert_cmpint (strlen (str), ==, s_len); + + strcpy (buf, BUF_ORIG); + + t_buf = buf; + t_len = buf_len; + + test_mode = nmtst_get_rand_int () % 4; + + switch (test_mode) { + case 0: + if (s_len == 1) { + nm_utils_strbuf_append_c (&t_buf, &t_len, str[0]); + break; + } + /* fall-through */ + case 1: + nm_utils_strbuf_append_str (&t_buf, &t_len, str); + break; + case 2: + if (s_len == 1) { + nm_utils_strbuf_append (&t_buf, &t_len, "%c", str[0]); + break; + } + /* fall-through */ + case 3: + nm_utils_strbuf_append (&t_buf, &t_len, "%s", str); + break; + } + + /* Assert that the source-buffer is unmodified. */ + g_assert_cmpint (str[s_len], ==, '\0'); + str[s_len] = STR_ORIG[s_len]; + g_assert (!memcmp (str, STR_ORIG, sizeof (str))); + str[s_len] = '\0'; + + g_assert_cmpint (t_len, >=, 0); + g_assert_cmpint (t_len, <=, buf_len); + g_assert (t_buf >= buf); + + /* Assert what was written to the destination buffer. */ + switch (buf_len) { + case 0: + g_assert_cmpint (t_len, ==, 0); + g_assert (t_buf == buf); + g_assert (!memcmp (buf, BUF_ORIG, sizeof (buf))); + break; + case 1: + if (s_len == 0) { + g_assert_cmpint (t_len, ==, 1); + g_assert (t_buf == buf); + g_assert (buf[0] == '\0'); + g_assert (!memcmp (&buf[1], &BUF_ORIG[1], sizeof (buf) - 1)); + } else { + g_assert_cmpint (t_len, ==, 0); + g_assert (t_buf == &buf[1]); + g_assert (buf[0] == '\0'); + g_assert (!memcmp (&buf[1], &BUF_ORIG[1], sizeof (buf) - 1)); + } + break; + default: + if (s_len == 0) { + g_assert_cmpint (t_len, ==, buf_len); + g_assert (t_buf == buf); + g_assert (buf[0] == '\0'); + g_assert (!memcmp (&buf[1], &BUF_ORIG[1], sizeof (buf) - 1)); + } else if (buf_len <= s_len) { + g_assert_cmpint (t_len, ==, 0); + g_assert (t_buf == &buf[buf_len]); + g_assert (!memcmp (buf, STR_ORIG, buf_len - 1)); + g_assert (buf[buf_len - 1] == '\0'); + g_assert (!memcmp (&buf[buf_len], &BUF_ORIG[buf_len], sizeof (buf) - buf_len)); + } else { + g_assert_cmpint (t_len, >, 0); + g_assert_cmpint (buf_len - t_len, ==, s_len); + g_assert_cmpint (strlen (buf), ==, s_len); + g_assert (t_buf == &buf[s_len]); + g_assert (!memcmp (buf, STR_ORIG, s_len)); + g_assert (buf[s_len] == '\0'); + g_assert (!memcmp (&buf[s_len + 1], &BUF_ORIG[s_len + 1], sizeof (buf) - s_len - 1)); + } + break; + } + } + } +} + +/*******************************************/ + NMTST_DEFINE (); int @@ -978,6 +1088,8 @@ main (int argc, char **argv) { nmtst_init_with_logging (&argc, &argv, NULL, "ALL"); + g_test_add_func ("/general/nm_utils_strbuf_append", test_nm_utils_strbuf_append); + g_test_add_func ("/general/nm_utils_ip6_address_clear_host_address", test_nm_utils_ip6_address_clear_host_address); g_test_add_func ("/general/nm_utils_log_connection_diff", test_nm_utils_log_connection_diff); diff --git a/src/tests/test-ip4-config.c b/src/tests/test-ip4-config.c index abf623b15a..58fe213b5a 100644 --- a/src/tests/test-ip4-config.c +++ b/src/tests/test-ip4-config.c @@ -36,6 +36,8 @@ addr_init (NMPlatformIP4Address *a, const char *addr, const char *peer, guint pl g_assert (inet_pton (AF_INET, addr, (void *) &a->address) == 1); if (peer) g_assert (inet_pton (AF_INET, peer, (void *) &a->peer_address) == 1); + else + a->peer_address = a->address; a->plen = plen; } @@ -152,7 +154,7 @@ test_subtract (void) test_addr = nm_ip4_config_get_address (dst, 0); g_assert (test_addr != NULL); g_assert_cmpuint (test_addr->address, ==, addr_to_num (expected_addr)); - g_assert_cmpuint (test_addr->peer_address, ==, 0); + g_assert_cmpuint (test_addr->peer_address, ==, test_addr->address); g_assert_cmpuint (test_addr->plen, ==, expected_addr_plen); g_assert_cmpuint (nm_ip4_config_get_gateway (dst), ==, 0); diff --git a/src/tests/test-ip6-config.c b/src/tests/test-ip6-config.c index 8b59f6c477..0977ded43b 100644 --- a/src/tests/test-ip6-config.c +++ b/src/tests/test-ip6-config.c @@ -264,8 +264,8 @@ test_nm_ip6_config_addresses_sort_check (NMIP6Config *config, NMSettingIP6Config if (!nm_ip6_config_equal (copy, config)) { g_message ("%s", "SORTING yields unexpected output:"); for (i = 0; i < addr_count; i++) { - g_message (" >> [%d] = %s", i, nm_platform_ip6_address_to_string (nm_ip6_config_get_address (config, i))); - g_message (" << [%d] = %s", i, nm_platform_ip6_address_to_string (nm_ip6_config_get_address (copy, i))); + g_message (" >> [%d] = %s", i, nm_platform_ip6_address_to_string (nm_ip6_config_get_address (config, i), NULL, 0)); + g_message (" << [%d] = %s", i, nm_platform_ip6_address_to_string (nm_ip6_config_get_address (copy, i), NULL, 0)); } g_assert_not_reached (); } diff --git a/src/tests/test-route-manager.c b/src/tests/test-route-manager.c index d5902af4bc..eba7d9ef77 100644 --- a/src/tests/test-route-manager.c +++ b/src/tests/test-route-manager.c @@ -797,10 +797,12 @@ _assert_route_check (const NMPlatformVTableRoute *vtable, gboolean has, const NM if (!has) { g_assert (!r); } else { + char buf[sizeof (_nm_utils_to_string_buffer)]; + if (!r || vtable->route_cmp (route, r) != 0) g_error ("Invalid route. Expect %s, has %s", - nmtst_static_1024_01 (vtable->route_to_string (route)), - nmtst_static_1024_02 (vtable->route_to_string (r))); + vtable->route_to_string (route, NULL, 0), + vtable->route_to_string (r, buf, sizeof (buf))); g_assert (r); } } diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c index b3ef60bb56..b3ac9d3e05 100644 --- a/src/vpn-manager/nm-vpn-connection.c +++ b/src/vpn-manager/nm-vpn-connection.c @@ -1409,6 +1409,8 @@ nm_vpn_connection_ip4_config_get (NMVpnConnection *self, GVariant *dict) if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_PTP, "u", &u32)) address.peer_address = u32; + else + address.peer_address = address.address; if (g_variant_lookup (dict, NM_VPN_PLUGIN_IP4_CONFIG_PREFIX, "u", &u32)) address.plen = u32; |