diff --git a/Makefile.am b/Makefile.am index 01fd835e17..e8e72febaf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2170,6 +2170,11 @@ src_libNetworkManager_la_SOURCES = \ src/nm-checkpoint-manager.c \ src/nm-checkpoint-manager.h \ \ + src/nm-l3-config-data.c \ + src/nm-l3-config-data.h \ + src/nm-l3cfg.c \ + src/nm-l3cfg.h \ + \ src/devices/nm-acd-manager.c \ src/devices/nm-acd-manager.h \ src/devices/nm-lldp-listener.c \ diff --git a/shared/nm-glib-aux/nm-hash-utils.h b/shared/nm-glib-aux/nm-hash-utils.h index d1ab8dbc83..d1c76ea9a5 100644 --- a/shared/nm-glib-aux/nm-hash-utils.h +++ b/shared/nm-glib-aux/nm-hash-utils.h @@ -352,6 +352,19 @@ nm_hash_obfuscate_ptr (guint static_seed, gconstpointer val) * values in a global context. */ #define NM_HASH_OBFUSCATE_PTR(ptr) (nm_hash_obfuscate_ptr (1678382159u, ptr)) +static inline const char * +nm_hash_obfuscated_ptr_str (gconstpointer ptr, char buf[static 17]) +{ + int l; + + nm_assert (buf); + l = g_snprintf (buf, 17, NM_HASH_OBFUSCATE_PTR_FMT, NM_HASH_OBFUSCATE_PTR (ptr)); + nm_assert (l < 17); + return buf; +} + +#define nm_hash_obfuscated_ptr_str_a(ptr) (nm_hash_obfuscated_ptr_str ((ptr), g_alloca (17))) + /*****************************************************************************/ #endif /* __NM_HASH_UTILS_H__ */ diff --git a/shared/nm-glib-aux/nm-macros-internal.h b/shared/nm-glib-aux/nm-macros-internal.h index 9173d2792d..a8a5360548 100644 --- a/shared/nm-glib-aux/nm-macros-internal.h +++ b/shared/nm-glib-aux/nm-macros-internal.h @@ -700,24 +700,40 @@ static GParamSpec *obj_properties##suffix[_PROPERTY_ENUMS_LAST##suffix] = { NULL static inline void \ _nm_gobject_notify_together_impl##suffix (obj_type *obj, guint n, const _PropertyEnums##suffix *props) \ { \ - const gboolean freeze_thaw = (n > 1); \ + GObject *const gobj = (GObject *) obj; \ + GParamSpec *pspec_first = NULL; \ + gboolean frozen = FALSE; \ \ nm_assert (G_IS_OBJECT (obj)); \ nm_assert (n > 0); \ \ - if (freeze_thaw) \ - g_object_freeze_notify ((GObject *) obj); \ while (n-- > 0) { \ const _PropertyEnums##suffix prop = *props++; \ + GParamSpec *pspec; \ \ - if (prop != PROP_0##suffix) { \ - nm_assert ((gsize) prop < G_N_ELEMENTS (obj_properties##suffix)); \ - nm_assert (obj_properties##suffix[prop]); \ - g_object_notify_by_pspec ((GObject *) obj, obj_properties##suffix[prop]); \ + if (prop == PROP_0##suffix) \ + continue; \ + \ + nm_assert ((gsize) prop < G_N_ELEMENTS (obj_properties##suffix)); \ + pspec = obj_properties##suffix[prop]; \ + nm_assert (pspec); \ + \ + if (!frozen) { \ + if (!pspec_first) { \ + pspec_first = pspec; \ + continue; \ + } \ + frozen = TRUE; \ + g_object_freeze_notify (gobj); \ + g_object_notify_by_pspec (gobj, pspec_first); \ } \ + g_object_notify_by_pspec (gobj, pspec); \ } \ - if (freeze_thaw) \ - g_object_thaw_notify ((GObject *) obj); \ + \ + if (frozen) \ + g_object_thaw_notify (gobj); \ + else if (pspec_first) \ + g_object_notify_by_pspec (gobj, pspec_first); \ } \ \ _nm_unused static inline void \ @@ -809,6 +825,26 @@ nm_g_object_unref (gpointer obj) _changed; \ }) +#define nm_g_object_ref_set_take(pp, obj) \ + ({ \ + typeof (*(pp)) *const _pp = (pp); \ + typeof (*_pp) const _obj = (obj); \ + typeof (*_pp) _p; \ + gboolean _changed = FALSE; \ + \ + nm_assert (!_pp || !*_pp || G_IS_OBJECT (*_pp)); \ + nm_assert (!_obj || G_IS_OBJECT (_obj)); \ + \ + if ( _pp \ + && ((_p = *_pp) != _obj)) { \ + *_pp = _obj; \ + nm_g_object_unref (_p); \ + _changed = TRUE; \ + } else \ + nm_g_object_unref (_obj); \ + _changed; \ + }) + /* basically, replaces * g_clear_pointer (&location, g_free) * with diff --git a/shared/nm-glib-aux/nm-shared-utils.c b/shared/nm-glib-aux/nm-shared-utils.c index ee917fdf6e..f7ae5b88de 100644 --- a/shared/nm-glib-aux/nm-shared-utils.c +++ b/shared/nm-glib-aux/nm-shared-utils.c @@ -965,7 +965,7 @@ nm_utils_parse_inaddr_prefix_bin (int addr_family, slash = strchr (text, '/'); if (slash) - addrstr = addrstr_free = g_strndup (text, slash - text); + addrstr = nm_strndup_a (300, text, slash - text, &addrstr_free); else addrstr = text; @@ -975,9 +975,12 @@ nm_utils_parse_inaddr_prefix_bin (int addr_family, if (slash) { /* For IPv4, `ip addr add` supports the prefix-length as a netmask. We don't * do that. */ - prefix = _nm_utils_ascii_str_to_int64 (slash + 1, 10, + prefix = _nm_utils_ascii_str_to_int64 (&slash[1], + 10, 0, - addr_family == AF_INET ? 32 : 128, + addr_family == AF_INET + ? 32 + : 128, -1); if (prefix == -1) return FALSE; diff --git a/shared/nm-glib-aux/nm-shared-utils.h b/shared/nm-glib-aux/nm-shared-utils.h index 0f7c36e609..6aafd09e98 100644 --- a/shared/nm-glib-aux/nm-shared-utils.h +++ b/shared/nm-glib-aux/nm-shared-utils.h @@ -68,42 +68,6 @@ G_STATIC_ASSERT (sizeof (int) == sizeof (gint32)); /*****************************************************************************/ -static inline char -nm_utils_addr_family_to_char (int addr_family) -{ - switch (addr_family) { - case AF_UNSPEC: return 'X'; - case AF_INET: return '4'; - case AF_INET6: return '6'; - } - g_return_val_if_reached ('?'); -} - -static inline gsize -nm_utils_addr_family_to_size (int addr_family) -{ - switch (addr_family) { - case AF_INET: return sizeof (in_addr_t); - case AF_INET6: return sizeof (struct in6_addr); - } - g_return_val_if_reached (0); -} - -static inline int -nm_utils_addr_family_from_size (gsize len) -{ - switch (len) { - case sizeof (in_addr_t): return AF_INET; - case sizeof (struct in6_addr): return AF_INET6; - } - return AF_UNSPEC; -} - -#define nm_assert_addr_family(addr_family) \ - nm_assert (NM_IN_SET ((addr_family), AF_INET, AF_INET6)) - -/*****************************************************************************/ - typedef struct { union { guint8 addr_ptr[1]; @@ -1555,6 +1519,18 @@ nm_g_array_len (const GArray *arr) return arr ? arr->len : 0u; } +#define nm_g_array_append_new(arr, type) \ + ({ \ + GArray *_arr = (arr); \ + gsize _l; \ + \ + nm_assert (_arr); \ + _l = ((gsize) _arr->len) + 1u; \ + nm_assert (_l > _arr->len); \ + g_array_set_size (_arr, _l); \ + &g_array_index (arr, type, _l); \ + }) + /*****************************************************************************/ static inline guint @@ -1779,6 +1755,17 @@ GSource *nm_utils_g_main_context_create_integrate_source (GMainContext *internal /*****************************************************************************/ +static inline GPtrArray * +nm_strv_ptrarray_ensure (GPtrArray **p_arr) +{ + nm_assert (p_arr); + + if (G_UNLIKELY (!*p_arr)) + *p_arr = g_ptr_array_new_with_free_func (g_free); + + return *p_arr; +} + static inline void nm_strv_ptrarray_add_string_take (GPtrArray *cmd, char *str) @@ -1817,6 +1804,22 @@ nm_strv_ptrarray_take_gstring (GPtrArray *cmd, FALSE)); } +static inline gssize +nm_strv_ptrarray_find_first (const GPtrArray *strv, + const char *str) +{ + if (!strv) + return -1; + return nm_utils_strv_find_first ((char **) strv->pdata, strv->len, str); +} + +static inline gboolean +nm_strv_ptrarray_contains (const GPtrArray *strv, + const char *str) +{ + return nm_strv_ptrarray_find_first (strv, str) >= 0; +} + /*****************************************************************************/ int nm_utils_getpagesize (void); diff --git a/shared/nm-glib-aux/tests/test-shared-general.c b/shared/nm-glib-aux/tests/test-shared-general.c index f389770a7a..02b2f4e1ea 100644 --- a/shared/nm-glib-aux/tests/test-shared-general.c +++ b/shared/nm-glib-aux/tests/test-shared-general.c @@ -17,6 +17,16 @@ /*****************************************************************************/ +G_STATIC_ASSERT (NM_AF_UNSPEC == AF_UNSPEC); +G_STATIC_ASSERT (NM_AF_INET == AF_INET); +G_STATIC_ASSERT (NM_AF_INET6 == AF_INET6); + +G_STATIC_ASSERT (NM_AF_INET_SIZE == sizeof (in_addr_t)); +G_STATIC_ASSERT (NM_AF_INET_SIZE == sizeof (struct in_addr)); +G_STATIC_ASSERT (NM_AF_INET6_SIZE == sizeof (struct in6_addr)); + +/*****************************************************************************/ + static void test_gpid (void) { diff --git a/shared/nm-std-aux/nm-std-aux.h b/shared/nm-std-aux/nm-std-aux.h index 3262ef4c9d..91cd6c37fa 100644 --- a/shared/nm-std-aux/nm-std-aux.h +++ b/shared/nm-std-aux/nm-std-aux.h @@ -622,4 +622,58 @@ nm_steal_fd (int *p_fd) return -1; } +/*****************************************************************************/ + +#define NM_AF_UNSPEC 0 /* AF_UNSPEC */ +#define NM_AF_INET 2 /* AF_INET */ +#define NM_AF_INET6 10 /* AF_INET6 */ + +#define NM_AF_INET_SIZE 4 /* sizeof (in_addr_t) */ +#define NM_AF_INET6_SIZE 16 /* sizeof (stuct in6_addr) */ + +static inline char +nm_utils_addr_family_to_char (int addr_family) +{ + switch (addr_family) { + case NM_AF_UNSPEC: return 'X'; + case NM_AF_INET: return '4'; + case NM_AF_INET6: return '6'; + } + nm_assert_not_reached (); + return '?'; +} + +static inline size_t +nm_utils_addr_family_to_size (int addr_family) +{ + switch (addr_family) { + case NM_AF_INET: return NM_AF_INET_SIZE; + case NM_AF_INET6: return NM_AF_INET6_SIZE; + } + nm_assert_not_reached (); + return 0; +} + +static inline int +nm_utils_addr_family_from_size (size_t len) +{ + switch (len) { + case NM_AF_INET_SIZE: return NM_AF_INET; + case NM_AF_INET6_SIZE: return NM_AF_INET6; + } + return NM_AF_UNSPEC; +} + +#define nm_assert_addr_family(addr_family) \ + nm_assert (NM_IN_SET ((addr_family), NM_AF_INET, NM_AF_INET6)) + +#define NM_IS_IPv4(addr_family) \ + ({ \ + const int _addr_family = (addr_family); \ + \ + nm_assert_addr_family (_addr_family); \ + \ + (_addr_family == NM_AF_INET); \ + }) + #endif /* __NM_STD_AUX_H__ */ diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c index 7eaffb7dfc..230ffcda53 100644 --- a/src/NetworkManagerUtils.c +++ b/src/NetworkManagerUtils.c @@ -1186,7 +1186,7 @@ nm_utils_qdiscs_from_tc_setting (NMPlatform *platform, GET_ATTR ("limit", qdisc->tbf.limit, UINT32, uint32, 0); GET_ATTR ("latency", qdisc->tbf.latency, UINT32, uint32, 0); } -#undef GET_ADDR +#undef GET_ATTR g_ptr_array_add (qdiscs, q); } @@ -1263,3 +1263,106 @@ nm_utils_tfilters_from_tc_setting (NMPlatform *platform, return tfilters; } + +void +nm_utils_ip_route_attribute_to_platform (int addr_family, + NMIPRoute *s_route, + NMPlatformIPRoute *r, + guint32 route_table) +{ + GVariant *variant; + guint32 table; + NMIPAddr addr; + NMPlatformIP4Route *r4 = (NMPlatformIP4Route *) r; + NMPlatformIP6Route *r6 = (NMPlatformIP6Route *) r; + gboolean onlink; + + nm_assert (s_route); + nm_assert_addr_family (addr_family); + nm_assert (r); + +#define GET_ATTR(name, dst, variant_type, type, dflt) \ + G_STMT_START { \ + GVariant *_variant = nm_ip_route_get_attribute (s_route, ""name""); \ + \ + if ( _variant \ + && g_variant_is_of_type (_variant, G_VARIANT_TYPE_ ## variant_type)) \ + (dst) = g_variant_get_ ## type (_variant); \ + else \ + (dst) = (dflt); \ + } G_STMT_END + + if ( (variant = nm_ip_route_get_attribute (s_route, NM_IP_ROUTE_ATTRIBUTE_TYPE)) + && g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)) { + guint8 type; + + type = nm_utils_route_type_by_name (g_variant_get_string (variant, NULL)); + nm_assert (NM_IN_SET (type, + RTN_UNICAST, + RTN_LOCAL)); + + r->type_coerced = nm_platform_route_type_coerce (type); + } else + r->type_coerced = nm_platform_route_type_coerce (RTN_UNICAST); + + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_TABLE, table, UINT32, uint32, 0); + + if ( !table + && r->type_coerced == nm_platform_route_type_coerce (RTN_LOCAL)) + r->table_coerced = nm_platform_route_table_coerce (RT_TABLE_LOCAL); + else + r->table_coerced = nm_platform_route_table_coerce (table ?: (route_table ?: RT_TABLE_MAIN)); + + if (addr_family == AF_INET) { + guint8 scope; + + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_TOS, r4->tos, BYTE, byte, 0); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_SCOPE, scope, BYTE, byte, RT_SCOPE_NOWHERE); + r4->scope_inv = nm_platform_route_scope_inv (scope); + } + + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_ONLINK, onlink, BOOLEAN, boolean, FALSE); + + r->r_rtm_flags = ((onlink) ? (unsigned) RTNH_F_ONLINK : 0u); + + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_WINDOW, r->window, UINT32, uint32, 0); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_CWND, r->cwnd, UINT32, uint32, 0); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_INITCWND, r->initcwnd, UINT32, uint32, 0); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_INITRWND, r->initrwnd, UINT32, uint32, 0); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_MTU, r->mtu, UINT32, uint32, 0); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_WINDOW, r->lock_window, BOOLEAN, boolean, FALSE); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND, r->lock_cwnd, BOOLEAN, boolean, FALSE); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_INITCWND, r->lock_initcwnd, BOOLEAN, boolean, FALSE); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_INITRWND, r->lock_initrwnd, BOOLEAN, boolean, FALSE); + GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, r->lock_mtu, BOOLEAN, boolean, FALSE); + + if ( (variant = nm_ip_route_get_attribute (s_route, NM_IP_ROUTE_ATTRIBUTE_SRC)) + && g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)) { + if (inet_pton (addr_family, g_variant_get_string (variant, NULL), &addr) == 1) { + if (addr_family == AF_INET) + r4->pref_src = addr.addr4; + else + r6->pref_src = addr.addr6; + } + } + + if ( addr_family == AF_INET6 + && (variant = nm_ip_route_get_attribute (s_route, NM_IP_ROUTE_ATTRIBUTE_FROM)) + && g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)) { + int prefix; + + if (nm_utils_parse_inaddr_prefix_bin (addr_family, + g_variant_get_string (variant, NULL), + NULL, + &addr, + &prefix)) { + if (prefix < 0) + prefix = 128; + r6->src = addr.addr6; + r6->src_plen = prefix; + } + } +#undef GET_ATTR +} + + diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h index 38386c3dab..b0cb68e306 100644 --- a/src/NetworkManagerUtils.h +++ b/src/NetworkManagerUtils.h @@ -141,4 +141,9 @@ GPtrArray *nm_utils_tfilters_from_tc_setting (NMPlatform *platform, NMSettingTCConfig *s_tc, int ip_ifindex); +void nm_utils_ip_route_attribute_to_platform (int addr_family, + NMIPRoute *s_route, + NMPlatformIPRoute *r, + guint32 route_table); + #endif /* __NETWORKMANAGER_UTILS_H__ */ diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 90ca3eddd5..e3d7ae3d46 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -285,6 +285,16 @@ typedef struct _NMDevicePrivate { char * udi; char * path; + union { + NML3Cfg *const l3cfg; + NML3Cfg *l3cfg_; + }; + + union { + NML3Cfg *const ip_l3cfg; + NML3Cfg *ip_l3cfg_; + }; + union { const char *const iface; char * iface_; @@ -1761,6 +1771,45 @@ nm_device_get_iface (NMDevice *self) return NM_DEVICE_GET_PRIVATE (self)->iface; } +static gboolean +_set_ifindex (NMDevice *self, int ifindex, gboolean is_ip_ifindex) +{ + NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); + gs_unref_object NML3Cfg *l3cfg_old = NULL; + NML3Cfg **p_l3cfg; + int *p_ifindex; + + if (ifindex < 0) + ifindex = 0; + + p_ifindex = is_ip_ifindex + ? &priv->ip_ifindex_ + : &priv->ifindex_; + + if (*p_ifindex == ifindex) + return FALSE; + + *p_ifindex = ifindex; + + p_l3cfg = is_ip_ifindex + ? &priv->ip_l3cfg_ + : &priv->l3cfg_; + + l3cfg_old = g_steal_pointer (p_l3cfg); + if (ifindex > 0) + *p_l3cfg = nm_netns_access_l3cfg (priv->netns, ifindex); + + _LOGD (LOGD_DEVICE, + "ifindex: set %sifindex %d%s%s%s%s%s%s", + is_ip_ifindex ? "ip-" : "", + ifindex, + NM_PRINT_FMT_QUOTED (l3cfg_old, " (old-l3cfg: ", nm_hash_obfuscated_ptr_str_a (l3cfg_old), ")", ""), + NM_PRINT_FMT_QUOTED (*p_l3cfg, " (l3cfg: ", nm_hash_obfuscated_ptr_str_a (*p_l3cfg), ")", "")); + + _notify (self, PROP_IFINDEX); + return TRUE; +} + /** * nm_device_take_over_link: * @self: the #NMDevice @@ -1825,10 +1874,7 @@ nm_device_take_over_link (NMDevice *self, int ifindex, char **old_name, GError * NM_SET_OUT (old_name, g_steal_pointer (&name)); } - if (priv->ifindex != ifindex) { - priv->ifindex_ = ifindex; - _notify (self, PROP_IFINDEX); - } + _set_ifindex (self, ifindex, FALSE); return TRUE; } @@ -1931,7 +1977,8 @@ _set_ip_ifindex (NMDevice *self, NM_PRINT_FMT_QUOTE_STRING (ifname), ifindex); - priv->ip_ifindex_ = ifindex; + _set_ifindex (self, ifindex, TRUE); + if (!eq_name) { g_free (priv->ip_iface_); priv->ip_iface_ = g_strdup (ifname); @@ -2003,36 +2050,6 @@ nm_device_set_ip_iface (NMDevice *self, const char *ifname) return ifindex > 0; } -static gboolean -_ip_iface_update (NMDevice *self, const char *ip_iface) -{ - NMDevicePrivate *priv; - - g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - - priv = NM_DEVICE_GET_PRIVATE (self); - - g_return_val_if_fail (priv->ip_iface, FALSE); - g_return_val_if_fail (priv->ip_ifindex > 0, FALSE); - g_return_val_if_fail (ip_iface, FALSE); - - if (!ip_iface[0]) - return FALSE; - - if (nm_streq (priv->ip_iface, ip_iface)) - return FALSE; - - _LOGI (LOGD_DEVICE, "ip-ifname: interface index %d renamed ip_iface (%d) from '%s' to '%s'", - priv->ifindex, - priv->ip_ifindex, - priv->ip_iface, - ip_iface); - g_free (priv->ip_iface_); - priv->ip_iface_ = g_strdup (ip_iface); - _notify (self, PROP_IP_IFACE); - return TRUE; -} - /*****************************************************************************/ int @@ -2197,17 +2214,23 @@ _stats_update_counters (NMDevice *self, guint64 rx_bytes) { NMDevicePrivate *priv; + gboolean tx_changed = FALSE; + gboolean rx_changed = FALSE; priv = NM_DEVICE_GET_PRIVATE (self); if (priv->stats.tx_bytes != tx_bytes) { priv->stats.tx_bytes = tx_bytes; - _notify (self, PROP_STATISTICS_TX_BYTES); + tx_changed = TRUE; } if (priv->stats.rx_bytes != rx_bytes) { priv->stats.rx_bytes = rx_bytes; - _notify (self, PROP_STATISTICS_RX_BYTES); + rx_changed = TRUE; } + + nm_gobject_notify_together (self, + tx_changed ? PROP_STATISTICS_TX_BYTES : PROP_0, + rx_changed ? PROP_STATISTICS_RX_BYTES : PROP_0); } static void @@ -4463,23 +4486,42 @@ device_ip_link_changed (NMDevice *self) { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); const NMPlatformLink *pllink; + const char *ip_iface; priv->device_ip_link_changed_id = 0; - if (!priv->ip_ifindex) + if (priv->ip_ifindex <= 0) return G_SOURCE_REMOVE; + nm_assert (priv->ip_iface); + pllink = nm_platform_link_get (nm_device_get_platform (self), priv->ip_ifindex); if (!pllink) return G_SOURCE_REMOVE; - if (priv->ifindex <= 0 && pllink->mtu) + if ( priv->ifindex <= 0 + && pllink->mtu) _set_mtu (self, pllink->mtu); _stats_update_counters_from_pllink (self, pllink); - if (_ip_iface_update (self, pllink->name)) + ip_iface = pllink->name; + + if (!ip_iface[0]) + return FALSE; + + if (!nm_streq (priv->ip_iface, ip_iface)) { + _LOGI (LOGD_DEVICE, "ip-ifname: interface index %d renamed ip_iface (%d) from '%s' to '%s'", + priv->ifindex, + priv->ip_ifindex, + priv->ip_iface, + ip_iface); + g_free (priv->ip_iface_); + priv->ip_iface_ = g_strdup (ip_iface); + _notify (self, PROP_IP_IFACE); + nm_device_update_dynamic_ip_setup (self); + } return G_SOURCE_REMOVE; } @@ -4695,7 +4737,7 @@ nm_device_update_from_platform_link (NMDevice *self, const NMPlatformLink *plink { NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); const char *str; - int ifindex; + gboolean ifindex_changed; guint32 mtu; if (!NM_DEVICE_GET_CLASS (self)->can_update_from_platform_link (self, plink)) @@ -4740,12 +4782,12 @@ nm_device_update_from_platform_link (NMDevice *self, const NMPlatformLink *plink mtu = plink ? plink->mtu : 0; _set_mtu (self, mtu); - ifindex = plink ? plink->ifindex : 0; - if (priv->ifindex != ifindex) { - priv->ifindex_ = ifindex; - _notify (self, PROP_IFINDEX); + ifindex_changed = _set_ifindex (self, + plink ? plink->ifindex : 0, + FALSE); + + if (ifindex_changed) NM_DEVICE_GET_CLASS (self)->link_changed (self, plink); - } device_update_interface_flags (self, plink); } @@ -5215,11 +5257,8 @@ nm_device_unrealize (NMDevice *self, gboolean remove_resources, GError **error) _parent_set_ifindex (self, 0, FALSE); - if (priv->ifindex > 0) { - priv->ifindex_ = 0; - _notify (self, PROP_IFINDEX); - } - priv->ip_ifindex_ = 0; + _set_ifindex (self, 0, FALSE); + _set_ifindex (self, 0, TRUE); if (nm_clear_g_free (&priv->ip_iface_)) _notify (self, PROP_IP_IFACE); @@ -17788,7 +17827,7 @@ constructor (GType type, pllink = nm_platform_link_get_by_ifname (nm_device_get_platform (self), priv->iface); if (pllink && link_type_compatible (self, pllink->type, NULL, NULL)) { - priv->ifindex_ = pllink->ifindex; + _set_ifindex (self, pllink->ifindex, FALSE); priv->up = NM_FLAGS_HAS (pllink->n_ifi_flags, IFF_UP); } } @@ -17908,10 +17947,8 @@ dispose (GObject *object) carrier_disconnected_action_cancel (self); - if (priv->ifindex > 0) { - priv->ifindex_ = 0; - _notify (self, PROP_IFINDEX); - } + _set_ifindex (self, 0, FALSE); + _set_ifindex (self, 0, TRUE); if (priv->settings) { g_signal_handlers_disconnect_by_func (priv->settings, cp_connection_added, self); diff --git a/src/meson.build b/src/meson.build index 8ff9bd11de..468f2159da 100644 --- a/src/meson.build +++ b/src/meson.build @@ -131,6 +131,8 @@ sources = files( 'nm-auth-manager.c', 'nm-auth-utils.c', 'nm-dbus-manager.c', + 'nm-l3-config-data.c', + 'nm-l3cfg.c', 'nm-checkpoint.c', 'nm-checkpoint-manager.c', 'nm-config.c', diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 9ad8116423..bd09ed511f 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -400,12 +400,12 @@ _nm_ip_config_best_default_route_find_better (const NMPObject *obj_cur, const NM && NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_cmp), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)) || NMP_OBJECT_GET_TYPE (obj_cur) == NMP_OBJECT_GET_TYPE (obj_cmp)); nm_assert ( !obj_cur - || nm_ip_config_best_default_route_is (obj_cur)); + || nmp_object_ip_route_is_best_defaut_route (obj_cur)); /* assumes that @obj_cur is already the best default route (or NULL). It checks whether * @obj_cmp is also a default route and returns the best of both. */ if ( obj_cmp - && nm_ip_config_best_default_route_is (obj_cmp)) { + && nmp_object_ip_route_is_best_defaut_route (obj_cmp)) { guint32 metric_cur, metric_cmp; if (!obj_cur) @@ -436,23 +436,12 @@ _nm_ip_config_best_default_route_find_better (const NMPObject *obj_cur, const NM return obj_cur; } -gboolean -_nm_ip_config_best_default_route_set (const NMPObject **best_default_route, const NMPObject *new_candidate) -{ - if (new_candidate == *best_default_route) - return FALSE; - nmp_object_ref (new_candidate); - nm_clear_nmp_object (best_default_route); - *best_default_route = new_candidate; - return TRUE; -} - gboolean _nm_ip_config_best_default_route_merge (const NMPObject **best_default_route, const NMPObject *new_candidate) { new_candidate = _nm_ip_config_best_default_route_find_better (*best_default_route, new_candidate); - return _nm_ip_config_best_default_route_set (best_default_route, new_candidate); + return nmp_object_ref_set (best_default_route, new_candidate); } const NMPObject * @@ -858,110 +847,6 @@ nm_ip4_config_commit (const NMIP4Config *self, return success; } -void -_nm_ip_config_merge_route_attributes (int addr_family, - NMIPRoute *s_route, - NMPlatformIPRoute *r, - guint32 route_table) -{ - GVariant *variant; - guint32 table; - NMIPAddr addr; - NMPlatformIP4Route *r4 = (NMPlatformIP4Route *) r; - NMPlatformIP6Route *r6 = (NMPlatformIP6Route *) r; - gboolean onlink; - - nm_assert (s_route); - nm_assert_addr_family (addr_family); - nm_assert (r); - -#define GET_ATTR(name, dst, variant_type, type, dflt) \ - G_STMT_START { \ - GVariant *_variant = nm_ip_route_get_attribute (s_route, ""name""); \ - \ - if ( _variant \ - && g_variant_is_of_type (_variant, G_VARIANT_TYPE_ ## variant_type)) \ - (dst) = g_variant_get_ ## type (_variant); \ - else \ - (dst) = (dflt); \ - } G_STMT_END - - if ( (variant = nm_ip_route_get_attribute (s_route, NM_IP_ROUTE_ATTRIBUTE_TYPE)) - && g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)) { - guint8 type; - - type = nm_utils_route_type_by_name (g_variant_get_string (variant, NULL)); - nm_assert (NM_IN_SET (type, - RTN_UNICAST, - RTN_LOCAL)); - - r->type_coerced = nm_platform_route_type_coerce (type); - } else - r->type_coerced = nm_platform_route_type_coerce (RTN_UNICAST); - - GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_TABLE, table, UINT32, uint32, 0); - - if ( !table - && r->type_coerced == nm_platform_route_type_coerce (RTN_LOCAL)) - r->table_coerced = nm_platform_route_table_coerce (RT_TABLE_LOCAL); - else - r->table_coerced = nm_platform_route_table_coerce (table ?: (route_table ?: RT_TABLE_MAIN)); - - if (addr_family == AF_INET) { - guint8 scope; - - GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_TOS, r4->tos, BYTE, byte, 0); - GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_SCOPE, scope, BYTE, byte, RT_SCOPE_NOWHERE); - r4->scope_inv = nm_platform_route_scope_inv (scope); - } - - GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_ONLINK, onlink, BOOLEAN, boolean, FALSE); - - r->r_rtm_flags = ((onlink) ? (unsigned) RTNH_F_ONLINK : 0u); - - GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_WINDOW, r->window, UINT32, uint32, 0); - GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_CWND, r->cwnd, UINT32, uint32, 0); - GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_INITCWND, r->initcwnd, UINT32, uint32, 0); - GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_INITRWND, r->initrwnd, UINT32, uint32, 0); - GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_MTU, r->mtu, UINT32, uint32, 0); - GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_WINDOW, r->lock_window, BOOLEAN, boolean, FALSE); - GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND, r->lock_cwnd, BOOLEAN, boolean, FALSE); - GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_INITCWND, r->lock_initcwnd, BOOLEAN, boolean, FALSE); - GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_INITRWND, r->lock_initrwnd, BOOLEAN, boolean, FALSE); - GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, r->lock_mtu, BOOLEAN, boolean, FALSE); - - if ( (variant = nm_ip_route_get_attribute (s_route, NM_IP_ROUTE_ATTRIBUTE_SRC)) - && g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)) { - if (inet_pton (addr_family, g_variant_get_string (variant, NULL), &addr) == 1) { - if (addr_family == AF_INET) - r4->pref_src = addr.addr4; - else - r6->pref_src = addr.addr6; - } - } - - if ( addr_family == AF_INET6 - && (variant = nm_ip_route_get_attribute (s_route, NM_IP_ROUTE_ATTRIBUTE_FROM)) - && g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)) { - gs_free char *string = NULL; - guint8 plen = 128; - char *sep; - - string = g_variant_dup_string (variant, NULL); - sep = strchr (string, '/'); - if (sep) { - *sep = 0; - plen = _nm_utils_ascii_str_to_int64 (sep + 1, 10, 1, 128, 255); - } - if ( plen <= 128 - && inet_pton (AF_INET6, string, &addr) == 1) { - r6->src = addr.addr6; - r6->src_plen = plen; - } - } -#undef GET_ATTR -} - void nm_ip4_config_merge_setting (NMIP4Config *self, NMSettingIPConfig *setting, @@ -1051,10 +936,10 @@ nm_ip4_config_merge_setting (NMIP4Config *self, route.network = nm_utils_ip4_address_clear_host_address (route.network, route.plen); - _nm_ip_config_merge_route_attributes (AF_INET, - s_route, - NM_PLATFORM_IP_ROUTE_CAST (&route), - route_table); + nm_utils_ip_route_attribute_to_platform (AF_INET, + s_route, + NM_PLATFORM_IP_ROUTE_CAST (&route), + route_table); _add_route (self, NULL, &route, NULL); } @@ -1505,8 +1390,8 @@ nm_ip4_config_subtract (NMIP4Config *dst, } } if (changed_default_route) { - _nm_ip_config_best_default_route_set (&dst_priv->best_default_route, - _nm_ip4_config_best_default_route_find (dst)); + nmp_object_ref_set (&dst_priv->best_default_route, + _nm_ip4_config_best_default_route_find (dst)); _notify (dst, PROP_GATEWAY); } if (changed) @@ -1659,7 +1544,7 @@ _nm_ip4_config_intersect_helper (NMIP4Config *dst, nm_assert_not_reached (); changed = TRUE; } - if (_nm_ip_config_best_default_route_set (&dst_priv->best_default_route, new_best_default_route)) { + if (nmp_object_ref_set (&dst_priv->best_default_route, new_best_default_route)) { nm_assert (changed); _notify (dst, PROP_GATEWAY); } @@ -1896,7 +1781,7 @@ nm_ip4_config_replace (NMIP4Config *dst, const NMIP4Config *src, gboolean *relev new_best_default_route = _nm_ip_config_best_default_route_find_better (new_best_default_route, obj_new); } nm_dedup_multi_index_dirty_remove_idx (dst_priv->multi_idx, &dst_priv->idx_ip4_routes, FALSE); - if (_nm_ip_config_best_default_route_set (&dst_priv->best_default_route, new_best_default_route)) + if (nmp_object_ref_set (&dst_priv->best_default_route, new_best_default_route)) _notify (dst, PROP_GATEWAY); _notify_routes (dst); } @@ -2990,8 +2875,8 @@ nm_ip4_config_nmpobj_remove (NMIP4Config *self, break; case NMP_OBJECT_TYPE_IP4_ROUTE: if (priv->best_default_route == obj_old) { - if (_nm_ip_config_best_default_route_set (&priv->best_default_route, - _nm_ip4_config_best_default_route_find (self))) + if (nmp_object_ref_set (&priv->best_default_route, + _nm_ip4_config_best_default_route_find (self))) _notify (self, PROP_GATEWAY); } _notify_routes (self); diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h index ea06174976..f1c4a57e85 100644 --- a/src/nm-ip4-config.h +++ b/src/nm-ip4-config.h @@ -69,26 +69,7 @@ nm_ip_config_iter_ip4_route_next (NMDedupMultiIter *ipconf_iter, const NMPlatfor /*****************************************************************************/ -static inline gboolean -nm_ip_config_best_default_route_is (const NMPObject *obj) -{ - const NMPlatformIPRoute *r = NMP_OBJECT_CAST_IP_ROUTE (obj); - - /* return whether @obj is considered a default-route. - * - * NMIP4Config/NMIP6Config tracks the (best) default-route explicitly, because - * at various places we act differently depending on whether there is a default-route - * configured. - * - * Note that this only considers the main routing table. */ - return r - && NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r) - && nm_platform_route_table_is_main (r->table_coerced) - && r->type_coerced == nm_platform_route_type_coerce (1 /*RTN_UNICAST*/); -} - const NMPObject *_nm_ip_config_best_default_route_find_better (const NMPObject *obj_cur, const NMPObject *obj_cmp); -gboolean _nm_ip_config_best_default_route_set (const NMPObject **best_default_route, const NMPObject *new_candidate); gboolean _nm_ip_config_best_default_route_merge (const NMPObject **best_default_route, const NMPObject *new_candidate); /*****************************************************************************/ @@ -108,11 +89,6 @@ const NMDedupMultiEntry *_nm_ip_config_lookup_ip_route (const NMDedupMultiIndex const NMPObject *needle, NMPlatformIPRouteCmpType cmp_type); -void _nm_ip_config_merge_route_attributes (int addr_family, - NMIPRoute *s_route, - NMPlatformIPRoute *r, - guint32 route_table); - /*****************************************************************************/ #define NM_TYPE_IP4_CONFIG (nm_ip4_config_get_type ()) diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index deb30e77d7..6c15e99765 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -721,10 +721,10 @@ nm_ip6_config_merge_setting (NMIP6Config *self, nm_utils_ip6_address_clear_host_address (&route.network, &route.network, route.plen); - _nm_ip_config_merge_route_attributes (AF_INET6, - s_route, - NM_PLATFORM_IP_ROUTE_CAST (&route), - route_table); + nm_utils_ip_route_attribute_to_platform (AF_INET6, + s_route, + NM_PLATFORM_IP_ROUTE_CAST (&route), + route_table); _add_route (self, NULL, &route, NULL); } @@ -1120,8 +1120,8 @@ nm_ip6_config_subtract (NMIP6Config *dst, } } if (changed_default_route) { - _nm_ip_config_best_default_route_set (&dst_priv->best_default_route, - _nm_ip6_config_best_default_route_find (dst)); + nmp_object_ref_set (&dst_priv->best_default_route, + _nm_ip6_config_best_default_route_find (dst)); _notify (dst, PROP_GATEWAY); } if (changed) @@ -1244,7 +1244,7 @@ _nm_ip6_config_intersect_helper (NMIP6Config *dst, nm_assert_not_reached (); changed = TRUE; } - if (_nm_ip_config_best_default_route_set (&dst_priv->best_default_route, new_best_default_route)) { + if (nmp_object_ref_set (&dst_priv->best_default_route, new_best_default_route)) { nm_assert (changed); _notify (dst, PROP_GATEWAY); } @@ -1481,7 +1481,7 @@ nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relev new_best_default_route = _nm_ip_config_best_default_route_find_better (new_best_default_route, obj_new); } nm_dedup_multi_index_dirty_remove_idx (dst_priv->multi_idx, &dst_priv->idx_ip6_routes, FALSE); - if (_nm_ip_config_best_default_route_set (&dst_priv->best_default_route, new_best_default_route)) + if (nmp_object_ref_set (&dst_priv->best_default_route, new_best_default_route)) _notify (dst, PROP_GATEWAY); _notify_routes (dst); } @@ -1955,7 +1955,7 @@ nm_ip6_config_reset_routes_ndisc (NMIP6Config *self, if (nm_dedup_multi_index_dirty_remove_idx (priv->multi_idx, &priv->idx_ip6_routes, FALSE) > 0) changed = TRUE; - if (_nm_ip_config_best_default_route_set (&priv->best_default_route, new_best_default_route)) { + if (nmp_object_ref_set (&priv->best_default_route, new_best_default_route)) { changed = TRUE; _notify (self, PROP_GATEWAY); } @@ -2458,8 +2458,8 @@ nm_ip6_config_nmpobj_remove (NMIP6Config *self, break; case NMP_OBJECT_TYPE_IP6_ROUTE: if (priv->best_default_route == obj_old) { - if (_nm_ip_config_best_default_route_set (&priv->best_default_route, - _nm_ip6_config_best_default_route_find (self))) + if (nmp_object_ref_set (&priv->best_default_route, + _nm_ip6_config_best_default_route_find (self))) _notify (self, PROP_GATEWAY); } _notify_routes (self); diff --git a/src/nm-l3-config-data.c b/src/nm-l3-config-data.c new file mode 100644 index 0000000000..d20df9dc36 --- /dev/null +++ b/src/nm-l3-config-data.c @@ -0,0 +1,868 @@ +// SPDX-License-Identifier: LGPL-2.1+ + +#include "nm-default.h" + +#include "nm-l3-config-data.h" + +#include "nm-core-internal.h" +#include "platform/nm-platform.h" +#include "platform/nmp-object.h" +#include "NetworkManagerUtils.h" + +/*****************************************************************************/ + +typedef struct { + NMDedupMultiIdxType parent; + NMPObjectType obj_type; +} DedupMultiIdxType; + +struct _NML3ConfigData { + NMDedupMultiIndex *multi_idx; + + union { + struct { + DedupMultiIdxType idx_addresses_6; + DedupMultiIdxType idx_addresses_4; + }; + DedupMultiIdxType idx_addresses_x[2]; + }; + + union { + struct { + DedupMultiIdxType idx_routes_6; + DedupMultiIdxType idx_routes_4; + }; + DedupMultiIdxType idx_routes_x[2]; + }; + + union { + struct { + const NMPObject *best_default_route_6; + const NMPObject *best_default_route_4; + }; + const NMPObject *best_default_route_x[2]; + }; + + union { + struct { + GArray *nameservers_6; + GArray *nameservers_4; + }; + GArray *nameservers_x[2]; + }; + + union { + struct { + GPtrArray *domains_6; + GPtrArray *domains_4; + }; + GPtrArray *domains_x[2]; + }; + + union { + struct { + GPtrArray *searches_6; + GPtrArray *searches_4; + }; + GPtrArray *searches_x[2]; + }; + + union { + struct { + GPtrArray *dns_options_6; + GPtrArray *dns_options_4; + }; + GPtrArray *dns_options_x[2]; + }; + + union { + struct { + int dns_priority_6; + int dns_priority_4; + }; + int dns_priority_x[2]; + }; + + NMSettingConnectionMdns mdns; + NMSettingConnectionLlmnr llmnr; + + int ref_count; + + bool is_sealed:1; +}; + +/*****************************************************************************/ + +static gboolean +_route_valid_4 (const NMPlatformIP4Route *r) +{ + return r + && r->plen <= 32 + && r->network == nm_utils_ip4_address_clear_host_address (r->network, r->plen); +} + +static gboolean +_route_valid_6 (const NMPlatformIP6Route *r) +{ + struct in6_addr n; + + return r + && r->plen <= 128 + && (memcmp (&r->network, + nm_utils_ip6_address_clear_host_address (&n, &r->network, r->plen), + sizeof (n)) == 0); +} + +static gboolean +_route_valid (int addr_family, gconstpointer r) +{ + nm_assert_addr_family (addr_family); + + return addr_family == AF_INET + ? _route_valid_4 (r) + : _route_valid_6 (r); +} + +static gboolean +NM_IS_L3_CONFIG_DATA (const NML3ConfigData *self, gboolean allow_sealed) +{ + return self + && self->ref_count > 0 + && ( allow_sealed + || !self->is_sealed); +} + +static GArray * +_garray_ensure_for_addrbin (GArray **p_arr, + int addr_family) +{ + nm_assert (p_arr); + nm_assert_addr_family (addr_family); + + if (G_UNLIKELY (!*p_arr)) { + *p_arr = g_array_new (FALSE, + FALSE, + nm_utils_addr_family_to_size (addr_family)); + } + return *p_arr; +} + +static void +_idx_obj_id_hash_update (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj, + NMHashState *h) +{ + nmp_object_id_hash_update ((NMPObject *) obj, h); +} + +static gboolean +_idx_obj_id_equal (const NMDedupMultiIdxType *idx_type, + const NMDedupMultiObj *obj_a, + const NMDedupMultiObj *obj_b) +{ + return nmp_object_id_equal ((NMPObject *) obj_a, (NMPObject *) obj_b); +} + +static void +_idx_type_init (DedupMultiIdxType *idx_type, + NMPObjectType obj_type) +{ + static const NMDedupMultiIdxTypeClass idx_type_class = { + .idx_obj_id_hash_update = _idx_obj_id_hash_update, + .idx_obj_id_equal = _idx_obj_id_equal, + }; + + nm_dedup_multi_idx_type_init (&idx_type->parent, + &idx_type_class); + idx_type->obj_type = obj_type; +} + +NML3ConfigData * +nm_l3_config_data_new (NMDedupMultiIndex *multi_idx) +{ + NML3ConfigData *self; + + nm_assert (multi_idx); + + self = g_slice_new (NML3ConfigData); + *self = (NML3ConfigData) { + .ref_count = 1, + .multi_idx = nm_dedup_multi_index_ref (multi_idx), + .mdns = NM_SETTING_CONNECTION_MDNS_DEFAULT, + .llmnr = NM_SETTING_CONNECTION_LLMNR_DEFAULT, + }; + + _idx_type_init (&self->idx_addresses_4, NMP_OBJECT_TYPE_IP4_ADDRESS); + _idx_type_init (&self->idx_addresses_6, NMP_OBJECT_TYPE_IP4_ADDRESS); + _idx_type_init (&self->idx_routes_4, NMP_OBJECT_TYPE_IP4_ROUTE); + _idx_type_init (&self->idx_routes_6, NMP_OBJECT_TYPE_IP6_ROUTE); + + return self; +} + +NML3ConfigData * +nm_l3_config_data_ref (NML3ConfigData *self) +{ + nm_assert (NM_IS_L3_CONFIG_DATA (self, TRUE)); + self->ref_count++; + return self; +} + +NML3ConfigData * +nm_l3_config_data_ref_and_seal (NML3ConfigData *self) +{ + nm_assert (NM_IS_L3_CONFIG_DATA (self, TRUE)); + self->is_sealed = TRUE; + self->ref_count++; + return self; +} + +NML3ConfigData * +nm_l3_config_data_seal (NML3ConfigData *self) +{ + nm_assert (NM_IS_L3_CONFIG_DATA (self, TRUE)); + self->is_sealed = TRUE; + return self; +} + +gboolean +nm_l3_config_data_is_sealed (NML3ConfigData *self) +{ + nm_assert (NM_IS_L3_CONFIG_DATA (self, TRUE)); + return self->is_sealed; +} + +void +nm_l3_config_data_unref (NML3ConfigData *self) +{ + if (!self) + return; + + nm_assert (NM_IS_L3_CONFIG_DATA (self, TRUE)); + if (--self->ref_count > 0) + return; + + nm_dedup_multi_index_remove_idx (self->multi_idx, &self->idx_addresses_4.parent); + nm_dedup_multi_index_remove_idx (self->multi_idx, &self->idx_addresses_6.parent); + nm_dedup_multi_index_remove_idx (self->multi_idx, &self->idx_routes_4.parent); + nm_dedup_multi_index_remove_idx (self->multi_idx, &self->idx_routes_6.parent); + + nmp_object_unref (self->best_default_route_4); + nmp_object_unref (self->best_default_route_6); + + nm_clear_pointer (&self->nameservers_4, g_array_unref); + nm_clear_pointer (&self->nameservers_6, g_array_unref); + + nm_clear_pointer (&self->domains_4, g_ptr_array_unref); + nm_clear_pointer (&self->domains_6, g_ptr_array_unref); + + nm_clear_pointer (&self->searches_4, g_ptr_array_unref); + nm_clear_pointer (&self->searches_6, g_ptr_array_unref); + + nm_clear_pointer (&self->dns_options_4, g_ptr_array_unref); + nm_clear_pointer (&self->dns_options_6, g_ptr_array_unref); + + nm_dedup_multi_index_unref (self->multi_idx); + + nm_g_slice_free (self); +} + +/*****************************************************************************/ + +const NMDedupMultiHeadEntry * +nm_l3_config_data_lookup_addresses (const NML3ConfigData *self, + int addr_family) +{ + nm_assert (NM_IS_L3_CONFIG_DATA (self, TRUE)); + nm_assert_addr_family (addr_family); + + return nm_dedup_multi_index_lookup_head (self->multi_idx, + &self->idx_addresses_x[NM_IS_IPv4 (addr_family)].parent, + NULL); +} + +const NMDedupMultiHeadEntry * +nm_l3_config_data_lookup_routes (const NML3ConfigData *self, + int addr_family) +{ + nm_assert (NM_IS_L3_CONFIG_DATA (self, TRUE)); + nm_assert_addr_family (addr_family); + + return nm_dedup_multi_index_lookup_head (self->multi_idx, + &self->idx_routes_x[NM_IS_IPv4 (addr_family)].parent, + NULL); +} + +/*****************************************************************************/ + +static gboolean +_l3_config_data_add_obj (NMDedupMultiIndex *multi_idx, + DedupMultiIdxType *idx_type, + int ifindex, + const NMPObject *obj_new, + const NMPlatformObject *pl_new, + gboolean merge, + gboolean append_force, + const NMPObject **out_obj_old /* returns a reference! */, + const NMPObject **out_obj_new /* does not return a reference */) +{ + NMPObject obj_new_stackinit; + const NMDedupMultiEntry *entry_old; + const NMDedupMultiEntry *entry_new; + + nm_assert (multi_idx); + nm_assert (idx_type); + nm_assert (NM_IN_SET (idx_type->obj_type, NMP_OBJECT_TYPE_IP4_ADDRESS, + NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ADDRESS, + NMP_OBJECT_TYPE_IP6_ROUTE)); + nm_assert (ifindex > 0); + + /* we go through extra lengths to accept a full obj_new object. That one, + * can be reused by increasing the ref-count. */ + if (!obj_new) { + nm_assert (pl_new); + obj_new = nmp_object_stackinit (&obj_new_stackinit, idx_type->obj_type, pl_new); + NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (&obj_new_stackinit)->ifindex = ifindex; + } else { + nm_assert (!pl_new); + nm_assert (NMP_OBJECT_GET_TYPE (obj_new) == idx_type->obj_type); + if (NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (obj_new)->ifindex != ifindex) { + obj_new = nmp_object_stackinit_obj (&obj_new_stackinit, obj_new); + NMP_OBJECT_CAST_OBJ_WITH_IFINDEX (&obj_new_stackinit)->ifindex = ifindex; + } + } + nm_assert (NMP_OBJECT_GET_TYPE (obj_new) == idx_type->obj_type); + nm_assert (nmp_object_is_alive (obj_new)); + + entry_old = nm_dedup_multi_index_lookup_obj (multi_idx, &idx_type->parent, obj_new); + + if (entry_old) { + gboolean modified = FALSE; + const NMPObject *obj_old = entry_old->obj; + + if (nmp_object_equal (obj_new, obj_old)) { + nm_dedup_multi_entry_set_dirty (entry_old, FALSE); + goto append_force_and_out; + } + + /* if @merge, we merge the new object with the existing one. + * Otherwise, we replace it entirely. */ + if (merge) { + switch (idx_type->obj_type) { + case NMP_OBJECT_TYPE_IP4_ADDRESS: + case NMP_OBJECT_TYPE_IP6_ADDRESS: + /* for addresses that we read from the kernel, we keep the timestamps as defined + * by the previous source (item_old). The reason is, that the other source configured the lifetimes + * with "what should be" and the kernel values are "what turned out after configuring it". + * + * For other sources, the longer lifetime wins. */ + if ( ( obj_new->ip_address.addr_source == NM_IP_CONFIG_SOURCE_KERNEL + && obj_old->ip_address.addr_source != NM_IP_CONFIG_SOURCE_KERNEL) + || nm_platform_ip_address_cmp_expiry (NMP_OBJECT_CAST_IP_ADDRESS (obj_old), NMP_OBJECT_CAST_IP_ADDRESS(obj_new)) > 0) { + obj_new = nmp_object_stackinit_obj (&obj_new_stackinit, obj_new); + obj_new_stackinit.ip_address.timestamp = NMP_OBJECT_CAST_IP_ADDRESS (obj_old)->timestamp; + obj_new_stackinit.ip_address.lifetime = NMP_OBJECT_CAST_IP_ADDRESS (obj_old)->lifetime; + obj_new_stackinit.ip_address.preferred = NMP_OBJECT_CAST_IP_ADDRESS (obj_old)->preferred; + modified = TRUE; + } + + /* keep the maximum addr_source. */ + if (obj_new->ip_address.addr_source < obj_old->ip_address.addr_source) { + obj_new = nmp_object_stackinit_obj (&obj_new_stackinit, obj_new); + obj_new_stackinit.ip_address.addr_source = obj_old->ip_address.addr_source; + modified = TRUE; + } + break; + case NMP_OBJECT_TYPE_IP4_ROUTE: + case NMP_OBJECT_TYPE_IP6_ROUTE: + /* keep the maximum rt_source. */ + if (obj_new->ip_route.rt_source < obj_old->ip_route.rt_source) { + obj_new = nmp_object_stackinit_obj (&obj_new_stackinit, obj_new); + obj_new_stackinit.ip_route.rt_source = obj_old->ip_route.rt_source; + modified = TRUE; + } + break; + default: + nm_assert_not_reached (); + break; + } + + if ( modified + && nmp_object_equal (obj_new, obj_old)) { + nm_dedup_multi_entry_set_dirty (entry_old, FALSE); + goto append_force_and_out; + } + } + } + + if (!nm_dedup_multi_index_add_full (multi_idx, + &idx_type->parent, + obj_new, + append_force + ? NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE + : NM_DEDUP_MULTI_IDX_MODE_APPEND, + NULL, + entry_old ?: NM_DEDUP_MULTI_ENTRY_MISSING, + NULL, + &entry_new, + out_obj_old)) { + nm_assert_not_reached (); + NM_SET_OUT (out_obj_new, NULL); + return FALSE; + } + + NM_SET_OUT (out_obj_new, entry_new->obj); + return TRUE; + +append_force_and_out: + NM_SET_OUT (out_obj_old, nmp_object_ref (entry_old->obj)); + NM_SET_OUT (out_obj_new, entry_old->obj); + if (append_force) { + if (nm_dedup_multi_entry_reorder (entry_old, NULL, TRUE)) + return TRUE; + } + return FALSE; +} + +static const NMPObject * +_l3_config_best_default_route_find_better (const NMPObject *obj_cur, const NMPObject *obj_cmp) +{ + nm_assert ( !obj_cur + || NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_cur), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); + nm_assert ( !obj_cmp + || ( !obj_cur + && NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_cmp), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)) + || NMP_OBJECT_GET_TYPE (obj_cur) == NMP_OBJECT_GET_TYPE (obj_cmp)); + nm_assert ( !obj_cur + || nmp_object_ip_route_is_best_defaut_route (obj_cur)); + + /* assumes that @obj_cur is already the best default route (or NULL). It checks whether + * @obj_cmp is also a default route and returns the best of both. */ + if ( obj_cmp + && nmp_object_ip_route_is_best_defaut_route (obj_cmp)) { + guint32 metric_cur, metric_cmp; + + if (!obj_cur) + return obj_cmp; + + if (obj_cur == obj_cmp) + return obj_cmp; + + metric_cur = NMP_OBJECT_CAST_IP_ROUTE (obj_cur)->metric; + metric_cmp = NMP_OBJECT_CAST_IP_ROUTE (obj_cmp)->metric; + + if (metric_cmp < metric_cur) + return obj_cmp; + + if (metric_cmp == metric_cur) { + int c; + + /* Routes have the same metric. We still want to deterministically + * prefer one or the other. It's important to consistently choose one + * or the other, so that the order doesn't matter how routes are added + * (and merged). */ + c = nmp_object_cmp (obj_cur, obj_cmp); + if (c != 0) + return c < 0 ? obj_cur : obj_cmp; + + /* as last resort, compare pointers. */ + if (((uintptr_t) ((void *) (obj_cmp))) < ((uintptr_t) ((void *) (obj_cur)))) + return obj_cmp; + } + } + return obj_cur; +} + +static gboolean +_l3_config_best_default_route_merge (const NMPObject **best_default_route, const NMPObject *new_candidate) +{ + new_candidate = _l3_config_best_default_route_find_better (*best_default_route, + new_candidate); + return nmp_object_ref_set (best_default_route, new_candidate); +} + +gboolean +_nm_l3_config_data_add_route (NML3ConfigData *self, + int addr_family, + int ifindex, + const NMPObject *obj_new, + const NMPlatformIPRoute *pl_new, + const NMPObject **out_obj_new, + gboolean *out_changed_best_default_route) +{ + const gboolean IS_IPv4 = NM_IS_IPv4 (addr_family); + nm_auto_nmpobj const NMPObject *obj_old = NULL; + const NMPObject *obj_new_2; + gboolean changed = FALSE; + gboolean changed_best_default_route = FALSE; + + nm_assert_addr_family (addr_family); + nm_assert (ifindex > 0); + nm_assert ((!pl_new) != (!obj_new)); + nm_assert ( !pl_new + || _route_valid (addr_family, pl_new)); + nm_assert ( !obj_new + || ( NMP_OBJECT_GET_ADDR_FAMILY (obj_new) == addr_family + && _route_valid (addr_family, NMP_OBJECT_CAST_IP_ROUTE (obj_new)))); + + if (_l3_config_data_add_obj (self->multi_idx, + addr_family == AF_INET + ? &self->idx_routes_4 + : &self->idx_routes_6, + ifindex, + obj_new, + (const NMPlatformObject *) pl_new, + TRUE, + FALSE, + &obj_old, + &obj_new_2)) { + + if ( self->best_default_route_x[IS_IPv4] == obj_old + && obj_old != obj_new_2) { + changed_best_default_route = TRUE; + nm_clear_nmp_object (&self->best_default_route_x[IS_IPv4]); + } + + if (_l3_config_best_default_route_merge (&self->best_default_route_x[IS_IPv4], + obj_new_2)) + changed_best_default_route = TRUE; + + changed = TRUE; + } + + NM_SET_OUT (out_obj_new, nmp_object_ref (obj_new_2)); + NM_SET_OUT (out_changed_best_default_route, changed_best_default_route); + return changed; +} + +gboolean +_nm_l3_config_data_add_address (NML3ConfigData *self, + int addr_family, + int ifindex, + const NMPObject *obj_new, + const NMPlatformIPAddress *pl_new) +{ + nm_assert (NM_IS_L3_CONFIG_DATA (self, FALSE)); + nm_assert_addr_family (addr_family); + nm_assert (ifindex > 0); + nm_assert ((!pl_new) != (!obj_new)); + nm_assert ( !obj_new + || NMP_OBJECT_GET_ADDR_FAMILY (obj_new) == addr_family); + + return _l3_config_data_add_obj (self->multi_idx, + addr_family == AF_INET + ? &self->idx_addresses_4 + : &self->idx_addresses_6, + ifindex, + obj_new, + (const NMPlatformObject *) pl_new, + TRUE, + FALSE, + NULL, + NULL); +} + +/*****************************************************************************/ + +static gboolean +_check_and_add_domain (GPtrArray **p_arr, const char *domain) +{ + gs_free char *copy = NULL; + gsize len; + + nm_assert (p_arr); + g_return_val_if_fail (domain, FALSE); + + if (domain[0] == '\0') + g_return_val_if_reached (FALSE); + + if (domain[0] == '.' || strstr (domain, "..")) + return FALSE; + + len = strlen (domain); + if (domain[len - 1] == '.') { + copy = g_strndup (domain, len - 1); + domain = copy; + } + + if (nm_strv_ptrarray_contains (*p_arr, domain)) + return FALSE; + + nm_strv_ptrarray_add_string_take (nm_strv_ptrarray_ensure (p_arr), + g_steal_pointer (©) + ?: g_strdup (domain)); + return TRUE; +} + +gboolean +_nm_l3_config_data_add_domain (NML3ConfigData *self, + int addr_family, + const char *domain) +{ + nm_assert (NM_IS_L3_CONFIG_DATA (self, FALSE)); + nm_assert_addr_family (addr_family); + + return _check_and_add_domain (&self->domains_x[NM_IS_IPv4 (addr_family)], domain); +} + +gboolean +_nm_l3_config_data_add_search (NML3ConfigData *self, + int addr_family, + const char *search) +{ + nm_assert (NM_IS_L3_CONFIG_DATA (self, FALSE)); + nm_assert_addr_family (addr_family); + + return _check_and_add_domain (&self->searches_x[NM_IS_IPv4 (addr_family)], search); +} + +gboolean +_nm_l3_config_data_add_dns_option (NML3ConfigData *self, + int addr_family, + const char *dns_option) +{ + GPtrArray **p_arr; + + nm_assert (NM_IS_L3_CONFIG_DATA (self, FALSE)); + nm_assert_addr_family (addr_family); + + g_return_val_if_fail (dns_option, FALSE); + + if (!dns_option[0]) + g_return_val_if_reached (FALSE); + + p_arr = &self->dns_options_x[NM_IS_IPv4 (addr_family)]; + + if (nm_strv_ptrarray_contains (*p_arr, dns_option)) + return FALSE; + + nm_strv_ptrarray_add_string_dup (nm_strv_ptrarray_ensure (p_arr), + dns_option); + return TRUE; +} + +gboolean +_nm_l3_config_data_set_dns_priority (NML3ConfigData *self, + int addr_family, + int dns_priority) +{ + int *p_val; + + nm_assert (NM_IS_L3_CONFIG_DATA (self, FALSE)); + nm_assert_addr_family (addr_family); + + p_val = &self->dns_priority_x[NM_IS_IPv4 (addr_family)]; + if (*p_val == dns_priority) + return FALSE; + + *p_val = dns_priority; + return TRUE; +} + +/*****************************************************************************/ + +static void +_init_from_connection_ip (NML3ConfigData *self, + int addr_family, + int ifindex, + NMConnection *connection, + guint32 route_table, + guint32 route_metric) +{ + const gboolean IS_IPv4 = NM_IS_IPv4 (addr_family); + NMSettingIPConfig *s_ip; + guint naddresses; + guint nroutes; + guint nnameservers; + guint nsearches; + const char *gateway_str; + NMIPAddr gateway_bin; + guint i; + int idx; + + nm_assert (NM_IS_L3_CONFIG_DATA (self, FALSE)); + nm_assert (ifindex > 0); + nm_assert_addr_family (addr_family); + nm_assert (!connection || NM_IS_CONNECTION (connection)); + + if (!connection) + return; + + s_ip = nm_connection_get_setting_ip_config (connection, addr_family); + if (!s_ip) + return; + + if ( !nm_setting_ip_config_get_never_default (s_ip) + && (gateway_str = nm_setting_ip_config_get_gateway (s_ip)) + && inet_pton (addr_family, gateway_str, &gateway_bin) == 1 + && !nm_ip_addr_is_null (addr_family, &gateway_bin)) { + NMPlatformIPXRoute r; + + if (IS_IPv4) { + r.r4 = (NMPlatformIP4Route) { + .rt_source = NM_IP_CONFIG_SOURCE_USER, + .gateway = gateway_bin.addr4, + .table_coerced = nm_platform_route_table_coerce (route_table), + .metric = route_metric, + }; + } else { + r.r6 = (NMPlatformIP6Route) { + .rt_source = NM_IP_CONFIG_SOURCE_USER, + .gateway = gateway_bin.addr6, + .table_coerced = nm_platform_route_table_coerce (route_table), + .metric = route_metric, + }; + } + + _nm_l3_config_data_add_route (self, addr_family, ifindex, NULL, &r.rx, NULL, NULL); + } + + naddresses = nm_setting_ip_config_get_num_addresses (s_ip); + for (i = 0; i < naddresses; i++) { + NMIPAddress *s_addr = nm_setting_ip_config_get_address (s_ip, i); + NMPlatformIPXAddress a; + NMIPAddr addr_bin; + GVariant *label; + + nm_assert (nm_ip_address_get_family (s_addr) == addr_family); + + nm_ip_address_get_address_binary (s_addr, &addr_bin); + + if (IS_IPv4) { + a.a4 = (NMPlatformIP4Address) { + .address = addr_bin.addr4, + .peer_address = addr_bin.addr4, + .plen = nm_ip_address_get_prefix (s_addr), + .lifetime = NM_PLATFORM_LIFETIME_PERMANENT, + .preferred = NM_PLATFORM_LIFETIME_PERMANENT, + .addr_source = NM_IP_CONFIG_SOURCE_USER, + }; + label = nm_ip_address_get_attribute (s_addr, NM_IP_ADDRESS_ATTRIBUTE_LABEL); + if (label) + g_strlcpy (a.a4.label, g_variant_get_string (label, NULL), sizeof (a.a4.label)); + + nm_assert (a.a4.plen <= 32); + } else { + a.a6 = (NMPlatformIP6Address) { + .address = addr_bin.addr6, + .plen = nm_ip_address_get_prefix (s_addr), + .lifetime = NM_PLATFORM_LIFETIME_PERMANENT, + .preferred = NM_PLATFORM_LIFETIME_PERMANENT, + .addr_source = NM_IP_CONFIG_SOURCE_USER, + }; + + nm_assert (a.a6.plen <= 128); + } + + _nm_l3_config_data_add_address (self, addr_family, ifindex, NULL, &a.ax); + } + + nroutes = nm_setting_ip_config_get_num_routes (s_ip); + for (i = 0; i < nroutes; i++) { + NMIPRoute *s_route = nm_setting_ip_config_get_route (s_ip, i); + NMPlatformIPXRoute r; + NMIPAddr network_bin; + NMIPAddr next_hop_bin; + gint64 metric64; + guint32 metric; + guint plen; + + nm_assert (nm_ip_route_get_family (s_route) == addr_family); + + nm_ip_route_get_dest_binary (s_route, &network_bin); + nm_ip_route_get_next_hop_binary (s_route, &next_hop_bin); + + metric64 = nm_ip_route_get_metric (s_route); + if (metric64 < 0) + metric = route_metric; + else + metric = metric64; + metric = nm_utils_ip_route_metric_normalize (addr_family, metric); + + plen = nm_ip_route_get_prefix (s_route); + + nm_utils_ipx_address_clear_host_address (addr_family, &network_bin, &network_bin, plen); + + if (IS_IPv4) { + r.r4 = (NMPlatformIP4Route) { + .network = network_bin.addr4, + .plen = nm_ip_route_get_prefix (s_route), + .gateway = next_hop_bin.addr4, + .metric = metric, + .rt_source = NM_IP_CONFIG_SOURCE_USER, + }; + nm_assert (r.r4.plen <= 32); + } else { + r.r6 = (NMPlatformIP6Route) { + .network = network_bin.addr6, + .plen = nm_ip_route_get_prefix (s_route), + .gateway = next_hop_bin.addr6, + .metric = metric, + .rt_source = NM_IP_CONFIG_SOURCE_USER, + }; + nm_assert (r.r6.plen <= 128); + } + + nm_utils_ip_route_attribute_to_platform (addr_family, + s_route, + &r.rx, + route_table); + + _nm_l3_config_data_add_route (self, addr_family, ifindex, NULL, &r.rx, NULL, NULL); + } + + nnameservers = nm_setting_ip_config_get_num_dns (s_ip); + for (i = 0; i < nnameservers; i++) { + const char *s; + NMIPAddr ip; + + s = nm_setting_ip_config_get_dns (s_ip, i); + if (!nm_utils_parse_inaddr_bin (addr_family, s, NULL, &ip)) + continue; + g_array_append_vals (_garray_ensure_for_addrbin (&self->nameservers_x[IS_IPv4], addr_family), + &ip, + 1); + } + + nsearches = nm_setting_ip_config_get_num_dns_searches (s_ip); + for (i = 0; i < nsearches; i++) { + _nm_l3_config_data_add_search (self, + addr_family, + nm_setting_ip_config_get_dns_search (s_ip, i)); + } + + idx = 0; + while ((idx = nm_setting_ip_config_next_valid_dns_option (s_ip, i)) >= 0) { + _nm_l3_config_data_add_dns_option (self, + addr_family, + nm_setting_ip_config_get_dns_option (s_ip, i)); + idx++; + } + + _nm_l3_config_data_set_dns_priority (self, + addr_family, + nm_setting_ip_config_get_dns_priority (s_ip)); +} + +NML3ConfigData * +nm_l3_config_data_new_from_connection (NMDedupMultiIndex *multi_index, + int ifindex, + NMConnection *connection, + NMSettingConnectionMdns mdns, + NMSettingConnectionLlmnr llmnr, + guint32 route_table, + guint32 route_metric) +{ + NML3ConfigData *self; + + self = nm_l3_config_data_new (multi_index); + + _init_from_connection_ip (self, AF_INET, ifindex, connection, route_table, route_metric); + _init_from_connection_ip (self, AF_INET6, ifindex, connection, route_table, route_metric); + + self->mdns = mdns; + self->llmnr = llmnr; + + return self; +} diff --git a/src/nm-l3-config-data.h b/src/nm-l3-config-data.h new file mode 100644 index 0000000000..f7b87d7d16 --- /dev/null +++ b/src/nm-l3-config-data.h @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: LGPL-2.1+ + +#ifndef __NM_L3_CONFIG_DATA_H__ +#define __NM_L3_CONFIG_DATA_H__ + +#include "nm-glib-aux/nm-dedup-multi.h" +#include "nm-setting-connection.h" +#include "platform/nm-platform.h" + +typedef struct _NML3ConfigData NML3ConfigData; + +NML3ConfigData *nm_l3_config_data_new (NMDedupMultiIndex *multi_idx); +NML3ConfigData *nm_l3_config_data_ref (NML3ConfigData *self); +NML3ConfigData *nm_l3_config_data_ref_and_seal (NML3ConfigData *self); +NML3ConfigData *nm_l3_config_data_seal (NML3ConfigData *self); +void nm_l3_config_data_unref (NML3ConfigData *self); + +gboolean nm_l3_config_data_is_sealed (NML3ConfigData *self); + +const NMDedupMultiHeadEntry *nm_l3_config_data_lookup_addresses (const NML3ConfigData *self, int addr_family); +const NMDedupMultiHeadEntry *nm_l3_config_data_lookup_routes (const NML3ConfigData *self, int addr_family); + +NML3ConfigData *nm_l3_config_data_new_from_connection (NMDedupMultiIndex *multi_index, + int ifindex, + NMConnection *connection, + NMSettingConnectionMdns mdns, + NMSettingConnectionLlmnr llmnr, + guint32 route_table, + guint32 route_metric); + +/*****************************************************************************/ + +gboolean _nm_l3_config_data_add_address (NML3ConfigData *self, + int addr_family, + int ifindex, + const NMPObject *obj_new, + const NMPlatformIPAddress *pl_new); + +gboolean _nm_l3_config_data_add_route (NML3ConfigData *self, + int addr_family, + int ifindex, + const NMPObject *obj_new, + const NMPlatformIPRoute *pl_new, + const NMPObject **out_obj_new, + gboolean *out_changed_best_default_route); + +gboolean _nm_l3_config_data_add_domain (NML3ConfigData *self, + int addr_family, + const char *domain); + +gboolean _nm_l3_config_data_add_search (NML3ConfigData *self, + int addr_family, + const char *search); + +gboolean _nm_l3_config_data_add_dns_option (NML3ConfigData *self, + int addr_family, + const char *dns_option); + +gboolean _nm_l3_config_data_set_dns_priority (NML3ConfigData *self, + int addr_family, + int dns_priority); + +#endif /* __NM_L3_CONFIG_DATA_H__ */ diff --git a/src/nm-l3cfg.c b/src/nm-l3cfg.c new file mode 100644 index 0000000000..aefaef1cd8 --- /dev/null +++ b/src/nm-l3cfg.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: LGPL-2.1+ + +#include "nm-default.h" + +#include "nm-l3cfg.h" + +#include "platform/nm-platform.h" +#include "platform/nmp-object.h" +#include "nm-netns.h" + +/*****************************************************************************/ + +NM_GOBJECT_PROPERTIES_DEFINE (NML3Cfg, + PROP_NETNS, + PROP_IFINDEX, +); + +typedef struct _NML3CfgPrivate { + GArray *property_emit_list; +} NML3CfgPrivate; + +struct _NML3CfgClass { + GObjectClass parent; +}; + +G_DEFINE_TYPE (NML3Cfg, nm_l3cfg, G_TYPE_OBJECT) + +/*****************************************************************************/ + +#define _NMLOG_DOMAIN LOGD_CORE +#define _NMLOG_PREFIX_NAME "l3cfg" +#define _NMLOG(level, ...) \ + G_STMT_START { \ + nm_log ((level), (_NMLOG_DOMAIN), NULL, NULL, \ + "l3cfg["NM_HASH_OBFUSCATE_PTR_FMT",ifindex=%d]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + NM_HASH_OBFUSCATE_PTR (self), \ + nm_l3cfg_get_ifindex (self) \ + _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } G_STMT_END + +/*****************************************************************************/ + +static void _property_emit_notify (NML3Cfg *self, NML3CfgPropertyEmitType emit_type); + +/*****************************************************************************/ + +static void +_load_link (NML3Cfg *self, gboolean initial) +{ + nm_auto_nmpobj const NMPObject *obj_old = NULL; + const NMPObject *obj; + const char *ifname; + const char *ifname_old; + + obj = nm_platform_link_get_obj (self->priv.platform, self->priv.ifindex, TRUE); + + if ( initial + && obj == self->priv.pllink) + return; + + obj_old = g_steal_pointer (&self->priv.pllink); + self->priv.pllink = nmp_object_ref (obj); + + ifname_old = nmp_object_link_get_ifname (obj_old); + ifname = nmp_object_link_get_ifname (self->priv.pllink); + + if (initial) { + _LOGT ("link ifname changed: %s%s%s (initial)", + NM_PRINT_FMT_QUOTE_STRING (ifname)); + } else if (!nm_streq0 (ifname, ifname_old)) { + _LOGT ("link ifname changed: %s%s%s (was %s%s%s)", + NM_PRINT_FMT_QUOTE_STRING (ifname), + NM_PRINT_FMT_QUOTE_STRING (ifname_old)); + } +} + +/*****************************************************************************/ + +void +_nm_l3cfg_notify_platform_change_on_idle (NML3Cfg *self, guint32 obj_type_flags) +{ + if (NM_FLAGS_ANY (obj_type_flags, nmp_object_type_to_flags (NMP_OBJECT_TYPE_LINK))) + _load_link (self, FALSE); + if (NM_FLAGS_ANY (obj_type_flags, nmp_object_type_to_flags (NMP_OBJECT_TYPE_IP4_ROUTE))) + _property_emit_notify (self, NM_L3CFG_PROPERTY_EMIT_TYPE_IP4_ROUTE); + if (NM_FLAGS_ANY (obj_type_flags, nmp_object_type_to_flags (NMP_OBJECT_TYPE_IP6_ROUTE))) + _property_emit_notify (self, NM_L3CFG_PROPERTY_EMIT_TYPE_IP6_ROUTE); +} + +/*****************************************************************************/ + +typedef struct { + GObject *target_obj; + const GParamSpec *target_property; + NML3CfgPropertyEmitType emit_type; +} PropertyEmitData; + +static void +_property_emit_notify (NML3Cfg *self, NML3CfgPropertyEmitType emit_type) +{ + gs_free PropertyEmitData *collected_heap = NULL; + PropertyEmitData *collected = NULL; + PropertyEmitData *emit_data; + guint num; + guint i; + guint j; + + if (!self->priv.p->property_emit_list) + return; + + num = 0; + emit_data = &g_array_index (self->priv.p->property_emit_list, PropertyEmitData, 0); + for (i = 0; i < self->priv.p->property_emit_list->len; i++, emit_data++) { + if (emit_data->emit_type == emit_type) { + collected = emit_data; + num++; + } + } + + if (num == 0) + return; + + if (num == 1) { + g_object_notify_by_pspec (collected->target_obj, (GParamSpec *) collected->target_property); + return; + } + + if (num < 300u / sizeof (*collected)) + collected = g_alloca (sizeof (PropertyEmitData) * num); + else { + collected_heap = g_new (PropertyEmitData, num); + collected = collected_heap; + } + + emit_data = &g_array_index (self->priv.p->property_emit_list, PropertyEmitData, 0); + for (i = 0, j = 0; i < self->priv.p->property_emit_list->len; i++, emit_data++) { + if (emit_data->emit_type == emit_type) { + collected[j++] = *emit_data; + g_object_ref (collected->target_obj); + } + } + + nm_assert (j == num); + + for (i = 0; i < num; i++) { + g_object_notify_by_pspec (collected[i].target_obj, (GParamSpec *) collected[i].target_property); + if (i > 0) + g_object_unref (collected[i].target_obj); + } +} + +void +nm_l3cfg_property_emit_register (NML3Cfg *self, + GObject *target_obj, + const GParamSpec *target_property, + NML3CfgPropertyEmitType emit_type) +{ + PropertyEmitData *emit_data; + guint i; + + nm_assert (NM_IS_L3CFG (self)); + nm_assert (G_IS_OBJECT (target_obj)); + nm_assert (target_property); + nm_assert (NM_IN_SET (emit_type, NM_L3CFG_PROPERTY_EMIT_TYPE_IP4_ROUTE, + NM_L3CFG_PROPERTY_EMIT_TYPE_IP6_ROUTE)); + nm_assert (target_property == nm_g_object_class_find_property_from_gtype (G_OBJECT_TYPE (target_obj), + target_property->name)); + + if (!self->priv.p->property_emit_list) + self->priv.p->property_emit_list = g_array_new (FALSE, FALSE, sizeof (PropertyEmitData)); + else { + emit_data = &g_array_index (self->priv.p->property_emit_list, PropertyEmitData, 0); + for (i = 0; i < self->priv.p->property_emit_list->len; i++, emit_data++) { + if ( emit_data->target_obj != target_obj + || emit_data->target_property != target_property) + continue; + nm_assert (emit_data->emit_type == emit_type); + emit_data->emit_type = emit_type; + return; + } + } + + emit_data = nm_g_array_append_new (self->priv.p->property_emit_list, PropertyEmitData); + *emit_data = (PropertyEmitData) { + .target_obj = target_obj, + .target_property = target_property, + .emit_type = emit_type, + }; +} + +void +nm_l3cfg_property_emit_unregister (NML3Cfg *self, + GObject *target_obj, + const GParamSpec *target_property) +{ + PropertyEmitData *emit_data; + guint i; + + nm_assert (NM_IS_L3CFG (self)); + nm_assert (G_IS_OBJECT (target_obj)); + nm_assert ( !target_property + || target_property == nm_g_object_class_find_property_from_gtype (G_OBJECT_TYPE (target_obj), + target_property->name)); + + if (!self->priv.p->property_emit_list) + return; + + for (i = self->priv.p->property_emit_list->len; i > 0; i--) { + emit_data = &g_array_index (self->priv.p->property_emit_list, PropertyEmitData, i); + + if (emit_data->target_obj != target_obj) + continue; + if ( target_property + && emit_data->target_property != target_property) + continue; + + g_array_remove_index_fast (self->priv.p->property_emit_list, i); + + if (target_property) { + /* if a target-property is given, we don't have another entry in + * the list. */ + return; + } + } +} + +/*****************************************************************************/ + +static void +set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NML3Cfg *self = NM_L3CFG (object); + + switch (prop_id) { + case PROP_NETNS: + /* construct-only */ + self->priv.netns = g_object_ref (g_value_get_pointer (value)); + nm_assert (NM_IS_NETNS (self->priv.netns)); + break; + case PROP_IFINDEX: + /* construct-only */ + self->priv.ifindex = g_value_get_int (value); + nm_assert (self->priv.ifindex > 0); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/*****************************************************************************/ + +static void +nm_l3cfg_init (NML3Cfg *self) +{ + NML3CfgPrivate *p; + + p = G_TYPE_INSTANCE_GET_PRIVATE (self, NM_TYPE_L3CFG, NML3CfgPrivate); + + self->priv.p = p; +} + +static void +constructed (GObject *object) +{ + NML3Cfg *self = NM_L3CFG (object); + + nm_assert (NM_IS_NETNS (self->priv.netns)); + nm_assert (self->priv.ifindex > 0); + + self->priv.platform = g_object_ref (nm_netns_get_platform (self->priv.netns)); + nm_assert (NM_IS_PLATFORM (self->priv.platform)); + + _LOGT ("created (netns="NM_HASH_OBFUSCATE_PTR_FMT")", + NM_HASH_OBFUSCATE_PTR (self->priv.netns)); + + G_OBJECT_CLASS (nm_l3cfg_parent_class)->constructed (object); + + _load_link (self, TRUE); +} + +NML3Cfg * +nm_l3cfg_new (NMNetns *netns, int ifindex) +{ + nm_assert (NM_IS_NETNS (netns)); + nm_assert (ifindex > 0); + + return g_object_new (NM_TYPE_L3CFG, + NM_L3CFG_NETNS, netns, + NM_L3CFG_IFINDEX, ifindex, + NULL); +} + +static void +finalize (GObject *object) +{ + NML3Cfg *self = NM_L3CFG (object); + + nm_assert (nm_g_array_len (self->priv.p->property_emit_list) == 0u); + + g_clear_object (&self->priv.netns); + g_clear_object (&self->priv.platform); + + nm_clear_pointer (&self->priv.pllink, nmp_object_unref); + + _LOGT ("finalized"); + + G_OBJECT_CLASS (nm_l3cfg_parent_class)->finalize (object); +} + +static void +nm_l3cfg_class_init (NML3CfgClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (NML3CfgPrivate)); + + object_class->set_property = set_property; + object_class->constructed = constructed; + object_class->finalize = finalize; + + obj_properties[PROP_NETNS] = + g_param_spec_pointer (NM_L3CFG_NETNS, "", "", + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + obj_properties[PROP_IFINDEX] = + g_param_spec_int (NM_L3CFG_IFINDEX, "", "", + 0, + G_MAXINT, + 0, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); +} diff --git a/src/nm-l3cfg.h b/src/nm-l3cfg.h new file mode 100644 index 0000000000..7fc4b4b7d2 --- /dev/null +++ b/src/nm-l3cfg.h @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: LGPL-2.1+ + +#ifndef __NM_L3CFG_H__ +#define __NM_L3CFG_H__ + +#include "platform/nmp-object.h" + +#define NM_TYPE_L3CFG (nm_l3cfg_get_type ()) +#define NM_L3CFG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_L3CFG, NML3Cfg)) +#define NM_L3CFG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_L3CFG, NML3CfgClass)) +#define NM_IS_L3CFG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_L3CFG)) +#define NM_IS_L3CFG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_L3CFG)) +#define NM_L3CFG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_L3CFG, NML3CfgClass)) + +#define NM_L3CFG_NETNS "netns" +#define NM_L3CFG_IFINDEX "ifindex" + +struct _NML3CfgPrivate; + +struct _NML3Cfg { + GObject parent; + struct { + NMNetns *netns; + NMPlatform *platform; + int ifindex; + const NMPObject *pllink; + struct _NML3CfgPrivate *p; + } priv; +}; + +typedef struct _NML3CfgClass NML3CfgClass; + +GType nm_l3cfg_get_type (void); + +NML3Cfg *nm_l3cfg_new (NMNetns *netns, int ifindex); + +/*****************************************************************************/ + +void _nm_l3cfg_notify_platform_change_on_idle (NML3Cfg *self, guint32 obj_type_flags); + +/*****************************************************************************/ + +static inline int +nm_l3cfg_get_ifindex (const NML3Cfg *self) +{ + nm_assert (NM_IS_L3CFG (self)); + + return self->priv.ifindex; +} + +static inline const char * +nm_l3cfg_get_ifname (const NML3Cfg *self) +{ + nm_assert (NM_IS_L3CFG (self)); + + return nmp_object_link_get_ifname (self->priv.pllink); +} + +static inline NMNetns * +nm_l3cfg_get_netns (const NML3Cfg *self) +{ + nm_assert (NM_IS_L3CFG (self)); + + return self->priv.netns; +} + +static inline NMPlatform * +nm_l3cfg_get_platform (const NML3Cfg *self) +{ + nm_assert (NM_IS_L3CFG (self)); + + return self->priv.platform; +} + +/*****************************************************************************/ + +typedef enum { + NM_L3CFG_PROPERTY_EMIT_TYPE_ANY, + NM_L3CFG_PROPERTY_EMIT_TYPE_IP4_ROUTE, + NM_L3CFG_PROPERTY_EMIT_TYPE_IP6_ROUTE, +} NML3CfgPropertyEmitType; + +void nm_l3cfg_property_emit_register (NML3Cfg *self, + GObject *target_obj, + const GParamSpec *target_property, + NML3CfgPropertyEmitType emit_type); + +void nm_l3cfg_property_emit_unregister (NML3Cfg *self, + GObject *target_obj, + const GParamSpec *target_property); + +#endif /* __NM_L3CFG_H__ */ diff --git a/src/nm-netns.c b/src/nm-netns.c index 3652384ad9..5b170c036b 100644 --- a/src/nm-netns.c +++ b/src/nm-netns.c @@ -11,6 +11,7 @@ #include "NetworkManagerUtils.h" #include "nm-core-internal.h" +#include "nm-l3cfg.h" #include "platform/nm-platform.h" #include "platform/nmp-netns.h" #include "platform/nmp-rules-manager.h" @@ -22,9 +23,13 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE ( ); typedef struct { + NMNetns *_self_signal_user_data; NMPlatform *platform; NMPNetns *platform_netns; NMPRulesManager *rules_manager; + GHashTable *l3cfgs; + CList l3cfg_signal_pending_lst_head; + guint signal_pending_idle_id; } NMNetnsPrivate; struct _NMNetns { @@ -42,6 +47,18 @@ G_DEFINE_TYPE (NMNetns, nm_netns, G_TYPE_OBJECT); /*****************************************************************************/ +#define _NMLOG_DOMAIN LOGD_CORE +#define _NMLOG_PREFIX_NAME "netns" +#define _NMLOG(level, ...) \ + G_STMT_START { \ + nm_log ((level), (_NMLOG_DOMAIN), NULL, NULL, \ + "netns["NM_HASH_OBFUSCATE_PTR_FMT"]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + NM_HASH_OBFUSCATE_PTR (self) \ + _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } G_STMT_END + +/*****************************************************************************/ + NM_DEFINE_SINGLETON_GETTER (NMNetns, nm_netns_get, NM_TYPE_NETNS); /*****************************************************************************/ @@ -72,6 +89,131 @@ nm_netns_get_multi_idx (NMNetns *self) /*****************************************************************************/ +typedef struct { + int ifindex; + guint32 signal_pending_flag; + NML3Cfg *l3cfg; + CList signal_pending_lst; +} L3CfgData; + +static void +_l3cfg_data_free (gpointer ptr) +{ + L3CfgData *l3cfg_data = ptr; + + c_list_unlink_stale (&l3cfg_data->signal_pending_lst); + + nm_g_slice_free (l3cfg_data); +} + +static void +_l3cfg_weak_notify (gpointer data, + GObject *where_the_object_was) +{ + NMNetns *self = NM_NETNS (data); + NMNetnsPrivate *priv = NM_NETNS_GET_PRIVATE(data); + NML3Cfg *l3cfg = NM_L3CFG (where_the_object_was); + int ifindex = nm_l3cfg_get_ifindex (l3cfg); + + if (!g_hash_table_remove (priv->l3cfgs, &ifindex)) + nm_assert_not_reached (); + + if (NM_UNLIKELY (g_hash_table_size (priv->l3cfgs) == 0)) + g_object_unref (self); +} + +NML3Cfg * +nm_netns_access_l3cfg (NMNetns *self, + int ifindex) +{ + NMNetnsPrivate *priv; + L3CfgData *l3cfg_data; + + g_return_val_if_fail (NM_IS_NETNS (self), NULL); + g_return_val_if_fail (ifindex > 0, NULL); + + priv = NM_NETNS_GET_PRIVATE (self); + + l3cfg_data = g_hash_table_lookup (priv->l3cfgs, &ifindex); + + if (l3cfg_data) { + nm_log_trace (LOGD_CORE, + "l3cfg["NM_HASH_OBFUSCATE_PTR_FMT",ifindex=%d] %s", + NM_HASH_OBFUSCATE_PTR (l3cfg_data->l3cfg), + ifindex, + "referenced"); + return g_object_ref (l3cfg_data->l3cfg); + } + + l3cfg_data = g_slice_new (L3CfgData); + *l3cfg_data = (L3CfgData) { + .ifindex = ifindex, + .l3cfg = nm_l3cfg_new (self, ifindex), + .signal_pending_lst = C_LIST_INIT (l3cfg_data->signal_pending_lst), + }; + + if (!g_hash_table_add (priv->l3cfgs, l3cfg_data)) + nm_assert_not_reached (); + + if (NM_UNLIKELY (g_hash_table_size (priv->l3cfgs) == 1)) + g_object_ref (self); + + g_object_weak_ref (G_OBJECT (l3cfg_data->l3cfg), + _l3cfg_weak_notify, + self); + + /* Transfer ownership! We keep only a weak ref. */ + return l3cfg_data->l3cfg; +} + +/*****************************************************************************/ + +static gboolean +_platform_signal_on_idle_cb (gpointer user_data) +{ + gs_unref_object NMNetns *self = g_object_ref (NM_NETNS (user_data)); + NMNetnsPrivate *priv = NM_NETNS_GET_PRIVATE (self); + L3CfgData *l3cfg_data; + + while ((l3cfg_data = c_list_first_entry (&priv->l3cfg_signal_pending_lst_head, L3CfgData, signal_pending_lst))) { + c_list_unlink (&l3cfg_data->signal_pending_lst); + _nm_l3cfg_notify_platform_change_on_idle (l3cfg_data->l3cfg, + nm_steal_int (&l3cfg_data->signal_pending_flag)); + } + + priv->signal_pending_idle_id = 0; + return G_SOURCE_REMOVE; +} + +static void +_platform_signal_cb (NMPlatform *platform, + int obj_type_i, + int ifindex, + gconstpointer platform_object, + int change_type_i, + NMNetns **p_self) +{ + NMNetns *self = NM_NETNS (*p_self); + NMNetnsPrivate *priv = NM_NETNS_GET_PRIVATE (self); + const NMPObjectType obj_type = obj_type_i; + L3CfgData *l3cfg_data; + + l3cfg_data = g_hash_table_lookup (priv->l3cfgs, &ifindex); + if (!l3cfg_data) + return; + + l3cfg_data->signal_pending_flag |= nmp_object_type_to_flags (obj_type); + + if (!c_list_is_empty (&l3cfg_data->signal_pending_lst)) + return; + + c_list_link_tail (&priv->l3cfg_signal_pending_lst_head, &l3cfg_data->signal_pending_lst); + if (priv->signal_pending_idle_id == 0) + priv->signal_pending_idle_id = g_idle_add (_platform_signal_on_idle_cb, self); +} + +/*****************************************************************************/ + static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) @@ -98,6 +240,10 @@ set_property (GObject *object, guint prop_id, static void nm_netns_init (NMNetns *self) { + NMNetnsPrivate *priv = NM_NETNS_GET_PRIVATE (self); + + priv->_self_signal_user_data = self; + c_list_init (&priv->l3cfg_signal_pending_lst_head); } static void @@ -109,6 +255,8 @@ constructed (GObject *object) if (!priv->platform) g_return_if_reached (); + priv->l3cfgs = g_hash_table_new_full (nm_pint_hash, nm_pint_equals, _l3cfg_data_free, NULL); + priv->platform_netns = nm_platform_netns_get (priv->platform); priv->rules_manager = nmp_rules_manager_new (priv->platform); @@ -136,6 +284,10 @@ constructed (GObject *object) NMP_RULES_MANAGER_EXTERN_WEAKLY_TRACKED_USER_TAG); G_OBJECT_CLASS (nm_netns_parent_class)->constructed (object); + + g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (_platform_signal_cb), &priv->_self_signal_user_data); + g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (_platform_signal_cb), &priv->_self_signal_user_data); + g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (_platform_signal_cb), &priv->_self_signal_user_data); } NMNetns * @@ -152,7 +304,16 @@ dispose (GObject *object) NMNetns *self = NM_NETNS (object); NMNetnsPrivate *priv = NM_NETNS_GET_PRIVATE (self); + nm_assert (nm_g_hash_table_size (priv->l3cfgs) == 0); + nm_assert (c_list_is_empty (&priv->l3cfg_signal_pending_lst_head)); + + nm_clear_g_source (&priv->signal_pending_idle_id); + + if (priv->platform) + g_signal_handlers_disconnect_by_data (priv->platform, &priv->_self_signal_user_data); + g_clear_object (&priv->platform); + g_clear_pointer (&priv->l3cfgs, g_hash_table_unref); nm_clear_pointer (&priv->rules_manager, nmp_rules_manager_unref); diff --git a/src/nm-netns.h b/src/nm-netns.h index c65dd9076c..2af0242b8d 100644 --- a/src/nm-netns.h +++ b/src/nm-netns.h @@ -31,4 +31,7 @@ struct _NMDedupMultiIndex *nm_netns_get_multi_idx (NMNetns *self); #define NM_NETNS_GET (nm_netns_get ()) +NML3Cfg *nm_netns_access_l3cfg (NMNetns *netns, + int ifindex); + #endif /* __NM_NETNS_H__ */ diff --git a/src/nm-types.h b/src/nm-types.h index d2c999fada..6277e4d811 100644 --- a/src/nm-types.h +++ b/src/nm-types.h @@ -23,6 +23,7 @@ typedef struct _NMDBusManager NMDBusManager; typedef struct _NMConfig NMConfig; typedef struct _NMConfigData NMConfigData; typedef struct _NMConnectivity NMConnectivity; +typedef struct _NML3Cfg NML3Cfg; typedef struct _NMDevice NMDevice; typedef struct _NMDhcpConfig NMDhcpConfig; typedef struct _NMProxyConfig NMProxyConfig; @@ -235,6 +236,17 @@ typedef enum { NMP_OBJECT_TYPE_MAX = __NMP_OBJECT_TYPE_LAST - 1, } NMPObjectType; +static inline guint32 +nmp_object_type_to_flags (NMPObjectType obj_type) +{ + G_STATIC_ASSERT_EXPR (NMP_OBJECT_TYPE_MAX < 32); + + nm_assert (_NM_INT_NOT_NEGATIVE (obj_type)); + nm_assert (obj_type < NMP_OBJECT_TYPE_MAX); + + return ((guint32) 1u) << obj_type; +} + /** * NMIPConfigMergeFlags: * @NM_IP_CONFIG_MERGE_DEFAULT: no flags set diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index 7866caa428..dd00522064 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -556,6 +556,21 @@ _NMP_OBJECT_TYPE_IS_OBJ_WITH_IFINDEX (NMPObjectType obj_type) #define NMP_OBJECT_CAST_TFILTER(obj) _NMP_OBJECT_CAST (obj, tfilter, NMP_OBJECT_TYPE_TFILTER) #define NMP_OBJECT_CAST_LNK_WIREGUARD(obj) _NMP_OBJECT_CAST (obj, lnk_wireguard, NMP_OBJECT_TYPE_LNK_WIREGUARD) +static inline int +NMP_OBJECT_GET_ADDR_FAMILY (const NMPObject *obj) +{ + switch (NMP_OBJECT_GET_TYPE (obj)) { + case NMP_OBJECT_TYPE_IP4_ADDRESS: + case NMP_OBJECT_TYPE_IP4_ROUTE: + return AF_INET; + case NMP_OBJECT_TYPE_IP6_ADDRESS: + case NMP_OBJECT_TYPE_IP6_ROUTE: + return AF_INET6; + default: + return AF_UNSPEC; + } +} + static inline const NMPObject * nmp_object_ref (const NMPObject *obj) { @@ -599,6 +614,25 @@ nmp_object_unref (const NMPObject *obj) _changed; \ }) +static inline gboolean +nmp_object_ref_set (const NMPObject **pp, const NMPObject *obj) +{ + gboolean _changed = FALSE; + const NMPObject *p; + + nm_assert (!pp || !*pp || NMP_OBJECT_IS_VALID (*pp)); + nm_assert (!obj || NMP_OBJECT_IS_VALID (obj)); + + if ( pp + && ((p = *pp) != obj)) { + nmp_object_ref (obj); + *pp = obj; + nmp_object_unref (p); + _changed = TRUE; + } + return _changed; +} + NMPObject *nmp_object_new (NMPObjectType obj_type, gconstpointer plobj); NMPObject *nmp_object_new_link (int ifindex); @@ -995,4 +1029,32 @@ nm_platform_lookup_object_by_addr_family (NMPlatform *platform, return nm_platform_lookup (platform, &lookup); } +/*****************************************************************************/ + +static inline const char * +nmp_object_link_get_ifname (const NMPObject *obj) +{ + if (!obj) + return NULL; + return NMP_OBJECT_CAST_LINK (obj)->name; +} + +static inline gboolean +nmp_object_ip_route_is_best_defaut_route (const NMPObject *obj) +{ + const NMPlatformIPRoute *r = NMP_OBJECT_CAST_IP_ROUTE (obj); + + /* return whether @obj is considered a default-route. + * + * NMIP4Config/NMIP6Config tracks the (best) default-route explicitly, because + * at various places we act differently depending on whether there is a default-route + * configured. + * + * Note that this only considers the main routing table. */ + return r + && NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r) + && nm_platform_route_table_is_main (r->table_coerced) + && r->type_coerced == nm_platform_route_type_coerce (1 /* RTN_UNICAST */); +} + #endif /* __NMP_OBJECT_H__ */