core: merge branch 'th/l3cfg-1'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/583
This commit is contained in:
Thomas Haller 2020-07-23 23:27:44 +02:00
commit 4cc93e1f80
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
22 changed files with 2002 additions and 268 deletions

View file

@ -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 \

View file

@ -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__ */

View file

@ -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

View file

@ -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;

View file

@ -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);

View file

@ -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)
{

View file

@ -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__ */

View file

@ -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
}

View file

@ -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__ */

View file

@ -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);

View file

@ -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',

View file

@ -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);

View file

@ -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 ())

View file

@ -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);

868
src/nm-l3-config-data.c Normal file
View file

@ -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 (&copy)
?: 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;
}

63
src/nm-l3-config-data.h Normal file
View file

@ -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__ */

341
src/nm-l3cfg.c Normal file
View file

@ -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);
}

92
src/nm-l3cfg.h Normal file
View file

@ -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__ */

View file

@ -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);

View file

@ -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__ */

View file

@ -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

View file

@ -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__ */