mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2025-12-25 22:20:08 +01:00
mptcp: merge branch 'th/mptcp-2'
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1315
This commit is contained in:
commit
a5cbd700d3
40 changed files with 2627 additions and 716 deletions
|
|
@ -854,6 +854,10 @@ ipv6.ip6-privacy=0
|
|||
<term><varname>connection.mdns</varname></term>
|
||||
<listitem><para>If unspecified, the ultimate default values depends on the DNS plugin. With systemd-resolved the default currently is "no" (0) and for all other plugins also "no" (0).</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>connection.mptcp-flags</varname></term>
|
||||
<listitem><para>If unspecified, the fallback is either 0 (<literal>"disabled"</literal>) or 0x22 (<literal>"enabled-on-global-iface,subflow"</literal>), depending on <literal>/proc/sys/net/mptcp/enabled</literal>.</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>connection.dns-over-tls</varname></term>
|
||||
<listitem><para>If unspecified, the ultimate default values depends on the DNS plugin. With systemd-resolved the default currently is global setting and for all other plugins "no" (0).</para></listitem>
|
||||
|
|
|
|||
|
|
@ -1398,6 +1398,56 @@ _prop_get_connection_dns_over_tls(NMDevice *self)
|
|||
NM_SETTING_CONNECTION_DNS_OVER_TLS_DEFAULT);
|
||||
}
|
||||
|
||||
static NMMptcpFlags
|
||||
_prop_get_connection_mptcp_flags(NMDevice *self)
|
||||
{
|
||||
NMConnection *connection;
|
||||
NMMptcpFlags mptcp_flags = NM_MPTCP_FLAGS_NONE;
|
||||
|
||||
g_return_val_if_fail(NM_IS_DEVICE(self), NM_MPTCP_FLAGS_DISABLED);
|
||||
|
||||
connection = nm_device_get_applied_connection(self);
|
||||
if (connection) {
|
||||
mptcp_flags =
|
||||
nm_setting_connection_get_mptcp_flags(nm_connection_get_setting_connection(connection));
|
||||
if (mptcp_flags != NM_MPTCP_FLAGS_NONE)
|
||||
mptcp_flags = nm_mptcp_flags_normalize(mptcp_flags);
|
||||
}
|
||||
|
||||
if (mptcp_flags == NM_MPTCP_FLAGS_NONE) {
|
||||
guint64 v;
|
||||
|
||||
v = nm_config_data_get_connection_default_int64(NM_CONFIG_GET_DATA,
|
||||
NM_CON_DEFAULT("connection.mptcp-flags"),
|
||||
self,
|
||||
0,
|
||||
G_MAXINT64,
|
||||
NM_MPTCP_FLAGS_NONE);
|
||||
/* We filter out all invalid settings and accept it. Somewhat intentionally, we don't do a
|
||||
* strict parsing of the value to support forward compatibility. */
|
||||
if (v != NM_MPTCP_FLAGS_NONE)
|
||||
mptcp_flags = nm_mptcp_flags_normalize(v);
|
||||
}
|
||||
|
||||
if (mptcp_flags == NM_MPTCP_FLAGS_NONE) {
|
||||
gint32 v;
|
||||
|
||||
v = nm_platform_sysctl_get_int32(nm_device_get_platform(self),
|
||||
NMP_SYSCTL_PATHID_ABSOLUTE("/proc/sys/net/mptcp/enabled"),
|
||||
-1);
|
||||
if (v > 0) {
|
||||
/* if MPTCP is enabled via the sysctl, we use the default. */
|
||||
mptcp_flags = _NM_MPTCP_FLAGS_DEFAULT;
|
||||
} else
|
||||
mptcp_flags = NM_MPTCP_FLAGS_DISABLED;
|
||||
}
|
||||
|
||||
nm_assert(mptcp_flags != NM_MPTCP_FLAGS_NONE
|
||||
&& mptcp_flags == nm_mptcp_flags_normalize(mptcp_flags));
|
||||
|
||||
return mptcp_flags;
|
||||
}
|
||||
|
||||
static guint32
|
||||
_prop_get_ipvx_route_table(NMDevice *self, int addr_family)
|
||||
{
|
||||
|
|
@ -2773,6 +2823,7 @@ nm_device_create_l3_config_data_from_connection(NMDevice *self, NMConnection *co
|
|||
nm_l3_config_data_set_llmnr(l3cd, _prop_get_connection_llmnr(self));
|
||||
nm_l3_config_data_set_dns_over_tls(l3cd, _prop_get_connection_dns_over_tls(self));
|
||||
nm_l3_config_data_set_ip6_privacy(l3cd, _prop_get_ipv6_ip6_privacy(self));
|
||||
nm_l3_config_data_set_mptcp_flags(l3cd, _prop_get_connection_mptcp_flags(self));
|
||||
return l3cd;
|
||||
}
|
||||
|
||||
|
|
@ -12662,6 +12713,7 @@ can_reapply_change(NMDevice *self,
|
|||
NM_SETTING_CONNECTION_MDNS,
|
||||
NM_SETTING_CONNECTION_LLMNR,
|
||||
NM_SETTING_CONNECTION_DNS_OVER_TLS,
|
||||
NM_SETTING_CONNECTION_MPTCP_FLAGS,
|
||||
NM_SETTING_CONNECTION_WAIT_ACTIVATION_DELAY);
|
||||
}
|
||||
|
||||
|
|
@ -12873,6 +12925,17 @@ check_and_reapply_connection(NMDevice *self,
|
|||
if (nm_g_hash_table_lookup(diffs, NM_SETTING_IP6_CONFIG_SETTING_NAME))
|
||||
priv->ip_data_6.do_reapply = TRUE;
|
||||
|
||||
if (nm_g_hash_table_contains_any(
|
||||
nm_g_hash_table_lookup(diffs, NM_SETTING_CONNECTION_SETTING_NAME),
|
||||
NM_SETTING_CONNECTION_LLDP,
|
||||
NM_SETTING_CONNECTION_MDNS,
|
||||
NM_SETTING_CONNECTION_LLMNR,
|
||||
NM_SETTING_CONNECTION_DNS_OVER_TLS,
|
||||
NM_SETTING_CONNECTION_MPTCP_FLAGS)) {
|
||||
priv->ip_data_4.do_reapply = TRUE;
|
||||
priv->ip_data_6.do_reapply = TRUE;
|
||||
}
|
||||
|
||||
nm_device_activate_schedule_stage3_ip_config(self, FALSE);
|
||||
|
||||
_routing_rules_sync(self, NM_TERNARY_TRUE);
|
||||
|
|
|
|||
|
|
@ -149,6 +149,8 @@ struct _NML3ConfigData {
|
|||
|
||||
NMSettingIP6ConfigPrivacy ip6_privacy : 4;
|
||||
|
||||
NMMptcpFlags mptcp_flags : 18;
|
||||
|
||||
bool is_sealed : 1;
|
||||
|
||||
bool has_routes_with_type_local_4_set : 1;
|
||||
|
|
@ -582,6 +584,16 @@ nm_l3_config_data_log(const NML3ConfigData *self,
|
|||
NULL)));
|
||||
}
|
||||
|
||||
if (self->mptcp_flags != NM_MPTCP_FLAGS_NONE) {
|
||||
gs_free char *s = NULL;
|
||||
|
||||
_L("mptcp-flags: %s",
|
||||
(s = _nm_utils_enum_to_str_full(nm_mptcp_flags_get_type(),
|
||||
self->mptcp_flags,
|
||||
" ",
|
||||
NULL)));
|
||||
}
|
||||
|
||||
if (self->ip6_token.id != 0) {
|
||||
_L("ipv6-token: %s",
|
||||
nm_utils_inet6_interface_identifier_to_token(&self->ip6_token, sbuf_addr));
|
||||
|
|
@ -694,6 +706,7 @@ nm_l3_config_data_new(NMDedupMultiIndex *multi_idx, int ifindex, NMIPConfigSourc
|
|||
.never_default_4 = NM_OPTION_BOOL_DEFAULT,
|
||||
.source = source,
|
||||
.ip6_privacy = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN,
|
||||
.mptcp_flags = NM_MPTCP_FLAGS_NONE,
|
||||
.ndisc_hop_limit_set = FALSE,
|
||||
.ndisc_reachable_time_msec_set = FALSE,
|
||||
.ndisc_retrans_timer_msec_set = FALSE,
|
||||
|
|
@ -1875,6 +1888,30 @@ nm_l3_config_data_set_ip6_token(NML3ConfigData *self, NMUtilsIPv6IfaceId ipv6_to
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
NMMptcpFlags
|
||||
nm_l3_config_data_get_mptcp_flags(const NML3ConfigData *self)
|
||||
{
|
||||
nm_assert(!self || _NM_IS_L3_CONFIG_DATA(self, TRUE));
|
||||
|
||||
if (!self)
|
||||
return NM_MPTCP_FLAGS_NONE;
|
||||
|
||||
return self->mptcp_flags;
|
||||
}
|
||||
|
||||
gboolean
|
||||
nm_l3_config_data_set_mptcp_flags(NML3ConfigData *self, NMMptcpFlags mptcp_flags)
|
||||
{
|
||||
nm_assert(_NM_IS_L3_CONFIG_DATA(self, FALSE));
|
||||
nm_assert(!NM_FLAGS_ANY(mptcp_flags, ~_NM_MPTCP_FLAGS_ALL));
|
||||
|
||||
if (self->mptcp_flags == mptcp_flags)
|
||||
return FALSE;
|
||||
self->mptcp_flags = mptcp_flags;
|
||||
nm_assert(self->mptcp_flags == mptcp_flags);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
NMProxyConfigMethod
|
||||
nm_l3_config_data_get_proxy_method(const NML3ConfigData *self)
|
||||
{
|
||||
|
|
@ -2324,6 +2361,7 @@ nm_l3_config_data_cmp_full(const NML3ConfigData *a,
|
|||
NM_CMP_DIRECT_REF_STRING(a->proxy_pac_url, b->proxy_pac_url);
|
||||
NM_CMP_DIRECT_REF_STRING(a->proxy_pac_script, b->proxy_pac_script);
|
||||
NM_CMP_DIRECT_UNSAFE(a->ip6_privacy, b->ip6_privacy);
|
||||
NM_CMP_DIRECT_UNSAFE(a->mptcp_flags, b->mptcp_flags);
|
||||
|
||||
NM_CMP_DIRECT_UNSAFE(a->ndisc_hop_limit_set, b->ndisc_hop_limit_set);
|
||||
if (a->ndisc_hop_limit_set)
|
||||
|
|
@ -3242,6 +3280,9 @@ nm_l3_config_data_merge(NML3ConfigData *self,
|
|||
if (self->ip6_privacy == NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN)
|
||||
self->ip6_privacy = src->ip6_privacy;
|
||||
|
||||
if (self->mptcp_flags == NM_MPTCP_FLAGS_NONE)
|
||||
self->mptcp_flags = src->mptcp_flags;
|
||||
|
||||
if (!self->ndisc_hop_limit_set && src->ndisc_hop_limit_set) {
|
||||
self->ndisc_hop_limit_set = TRUE;
|
||||
self->ndisc_hop_limit_val = src->ndisc_hop_limit_val;
|
||||
|
|
|
|||
|
|
@ -487,6 +487,10 @@ NMUtilsIPv6IfaceId nm_l3_config_data_get_ip6_token(const NML3ConfigData *self);
|
|||
|
||||
gboolean nm_l3_config_data_set_ip6_token(NML3ConfigData *self, NMUtilsIPv6IfaceId ipv6_token);
|
||||
|
||||
NMMptcpFlags nm_l3_config_data_get_mptcp_flags(const NML3ConfigData *self);
|
||||
|
||||
gboolean nm_l3_config_data_set_mptcp_flags(NML3ConfigData *self, NMMptcpFlags mptcp_flags);
|
||||
|
||||
const in_addr_t *nm_l3_config_data_get_wins(const NML3ConfigData *self, guint *out_len);
|
||||
|
||||
gboolean nm_l3_config_data_add_wins(NML3ConfigData *self, in_addr_t wins);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
#include "nm-l3cfg.h"
|
||||
|
||||
#include "libnm-std-aux/nm-linux-compat.h"
|
||||
|
||||
#include <net/if.h>
|
||||
#include <linux/if_addr.h>
|
||||
#include <linux/if_ether.h>
|
||||
|
|
@ -270,6 +272,15 @@ typedef struct _NML3CfgPrivate {
|
|||
NMIPConfig *ipconfig_x[2];
|
||||
};
|
||||
|
||||
/* Whether we earlier configured MPTCP endpoints for the interface. */
|
||||
union {
|
||||
struct {
|
||||
bool mptcp_set_6;
|
||||
bool mptcp_set_4;
|
||||
};
|
||||
bool mptcp_set_x[2];
|
||||
};
|
||||
|
||||
/* This is for rate-limiting the creation of nacd instance. */
|
||||
GSource *nacd_instance_ensure_retry;
|
||||
|
||||
|
|
@ -311,6 +322,9 @@ typedef struct _NML3CfgPrivate {
|
|||
|
||||
bool changed_configs_configs : 1;
|
||||
bool changed_configs_acd_state : 1;
|
||||
|
||||
bool rp_filter_handled : 1;
|
||||
bool rp_filter_set : 1;
|
||||
} NML3CfgPrivate;
|
||||
|
||||
struct _NML3CfgClass {
|
||||
|
|
@ -321,6 +335,13 @@ G_DEFINE_TYPE(NML3Cfg, nm_l3cfg, G_TYPE_OBJECT)
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
#define _NODEV_ROUTES_TAG(self, IS_IPv4) \
|
||||
((gconstpointer) (&(((const char *) (self))[0 + (!(IS_IPv4))])))
|
||||
|
||||
#define _MPTCP_TAG(self, IS_IPv4) ((gconstpointer) (&(((const char *) (self))[2 + (!(IS_IPv4))])))
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#define _NMLOG_DOMAIN LOGD_CORE
|
||||
#define _NMLOG_PREFIX_NAME "l3cfg"
|
||||
#define _NMLOG(level, ...) \
|
||||
|
|
@ -3450,9 +3471,6 @@ nm_l3cfg_remove_config_all_dirty(NML3Cfg *self, gconstpointer tag)
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
#define _NODEV_ROUTES_TAG(self, IS_IPv4) \
|
||||
((gconstpointer) (&(&(self)->priv.global_tracker)[IS_IPv4]))
|
||||
|
||||
static gboolean
|
||||
_nodev_routes_untrack(NML3Cfg *self, int addr_family)
|
||||
{
|
||||
|
|
@ -3492,10 +3510,11 @@ out_clear:
|
|||
if (_nodev_routes_untrack(self, addr_family))
|
||||
changed = TRUE;
|
||||
|
||||
if (changed || commit_type >= NM_L3_CFG_COMMIT_TYPE_REAPPLY)
|
||||
if (changed || commit_type >= NM_L3_CFG_COMMIT_TYPE_REAPPLY) {
|
||||
nmp_global_tracker_sync(self->priv.global_tracker,
|
||||
NMP_OBJECT_TYPE_IP_ROUTE(IS_IPv4),
|
||||
FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
@ -4184,6 +4203,259 @@ _l3_commit_ip6_token(NML3Cfg *self, NML3CfgCommitType commit_type)
|
|||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static void
|
||||
_rp_filter_update(NML3Cfg *self, gboolean reapply)
|
||||
{
|
||||
gboolean rp_filter_relax = FALSE;
|
||||
const char *ifname;
|
||||
int rf_val;
|
||||
|
||||
/* The rp_filter sysctl is only an IPv4 thing. We only enable the rp-filter
|
||||
* handling, if we did anything to MPTCP about IPv4 addresses.
|
||||
*
|
||||
* While we only have one "connection.mptcp-flags=enabled" property, whether
|
||||
* we handle MPTCP is still tracked per AF. In particular, with "enabled-on-global-iface"
|
||||
* flag, which honors the AF-specific default route. */
|
||||
if (self->priv.p->mptcp_set_4)
|
||||
rp_filter_relax = TRUE;
|
||||
|
||||
if (!rp_filter_relax) {
|
||||
if (self->priv.p->rp_filter_handled) {
|
||||
self->priv.p->rp_filter_handled = FALSE;
|
||||
if (self->priv.p->rp_filter_set) {
|
||||
self->priv.p->rp_filter_set = FALSE;
|
||||
|
||||
ifname = nm_l3cfg_get_ifname(self, TRUE);
|
||||
rf_val = nm_platform_sysctl_ip_conf_get_rp_filter_ipv4(self->priv.platform,
|
||||
ifname,
|
||||
FALSE,
|
||||
NULL);
|
||||
if (rf_val == 2) {
|
||||
/* We only relaxed from 1 to 2. Only if that is still the case, reset. */
|
||||
nm_platform_sysctl_ip_conf_set(self->priv.platform,
|
||||
AF_INET,
|
||||
ifname,
|
||||
"rp_filter",
|
||||
"1");
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->priv.p->rp_filter_handled && !reapply) {
|
||||
/* We only set rp_filter once (except reapply). */
|
||||
return;
|
||||
}
|
||||
|
||||
/* No matter whether we actually reset the value below, we set the "handled" flag
|
||||
* so that we only do this once (except during reapply). */
|
||||
self->priv.p->rp_filter_handled = TRUE;
|
||||
|
||||
ifname = nm_l3cfg_get_ifname(self, TRUE);
|
||||
|
||||
rf_val =
|
||||
nm_platform_sysctl_ip_conf_get_rp_filter_ipv4(self->priv.platform, ifname, FALSE, NULL);
|
||||
|
||||
if (rf_val != 1) {
|
||||
/* We only relax from strict (1) to loose (2). No other transition. Nothing to do. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* We actually loosen the flag. We need to remember to reset it. */
|
||||
self->priv.p->rp_filter_set = TRUE;
|
||||
nm_platform_sysctl_ip_conf_set(self->priv.platform, AF_INET, ifname, "rp_filter", "2");
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static gboolean
|
||||
_global_tracker_mptcp_untrack(NML3Cfg *self, int addr_family)
|
||||
{
|
||||
return nmp_global_tracker_untrack_all(self->priv.global_tracker,
|
||||
_MPTCP_TAG(self, NM_IS_IPv4(addr_family)),
|
||||
FALSE,
|
||||
TRUE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_l3_commit_mptcp_af(NML3Cfg *self,
|
||||
NML3CfgCommitType commit_type,
|
||||
int addr_family,
|
||||
gboolean *out_reapply)
|
||||
{
|
||||
const int IS_IPv4 = NM_IS_IPv4(addr_family);
|
||||
NMMptcpFlags mptcp_flags;
|
||||
gboolean reapply = FALSE;
|
||||
gboolean changed = FALSE;
|
||||
|
||||
nm_assert(NM_IS_L3CFG(self));
|
||||
nm_assert(NM_IN_SET(commit_type,
|
||||
NM_L3_CFG_COMMIT_TYPE_NONE,
|
||||
NM_L3_CFG_COMMIT_TYPE_REAPPLY,
|
||||
NM_L3_CFG_COMMIT_TYPE_UPDATE));
|
||||
|
||||
if (commit_type >= NM_L3_CFG_COMMIT_TYPE_REAPPLY)
|
||||
reapply = TRUE;
|
||||
|
||||
if (commit_type != NM_L3_CFG_COMMIT_TYPE_NONE && self->priv.p->combined_l3cd_commited)
|
||||
mptcp_flags = nm_l3_config_data_get_mptcp_flags(self->priv.p->combined_l3cd_commited);
|
||||
else
|
||||
mptcp_flags = NM_MPTCP_FLAGS_DISABLED;
|
||||
|
||||
if (mptcp_flags == NM_MPTCP_FLAGS_NONE || NM_FLAGS_HAS(mptcp_flags, NM_MPTCP_FLAGS_DISABLED))
|
||||
mptcp_flags = NM_MPTCP_FLAGS_DISABLED;
|
||||
else if (NM_FLAGS_HAS(mptcp_flags, NM_MPTCP_FLAGS_ENABLED_ON_GLOBAL_IFACE)) {
|
||||
/* Whether MPTCP is enabled/disabled, depends on whether we have a unicast default
|
||||
* route (in the main routing table). */
|
||||
if (self->priv.p->combined_l3cd_commited
|
||||
&& nm_l3_config_data_get_best_default_route(self->priv.p->combined_l3cd_commited,
|
||||
addr_family))
|
||||
mptcp_flags = NM_FLAGS_UNSET(mptcp_flags, NM_MPTCP_FLAGS_ENABLED_ON_GLOBAL_IFACE)
|
||||
| NM_MPTCP_FLAGS_ENABLED;
|
||||
else
|
||||
mptcp_flags = NM_MPTCP_FLAGS_DISABLED;
|
||||
} else
|
||||
mptcp_flags |= NM_MPTCP_FLAGS_ENABLED;
|
||||
|
||||
if (NM_FLAGS_HAS(mptcp_flags, NM_MPTCP_FLAGS_DISABLED)) {
|
||||
if (!self->priv.p->mptcp_set_x[IS_IPv4] && !reapply) {
|
||||
/* Nothing to configure, and we did not earlier configure MPTCP. Nothing to do. */
|
||||
NM_SET_OUT(out_reapply, FALSE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
self->priv.p->mptcp_set_x[IS_IPv4] = FALSE;
|
||||
reapply = TRUE;
|
||||
} else {
|
||||
const NMPlatformIPXAddress *addr;
|
||||
NMDedupMultiIter iter;
|
||||
const guint32 FLAGS =
|
||||
(NM_FLAGS_HAS(mptcp_flags, NM_MPTCP_FLAGS_SIGNAL) ? MPTCP_PM_ADDR_FLAG_SIGNAL : 0)
|
||||
| (NM_FLAGS_HAS(mptcp_flags, NM_MPTCP_FLAGS_SUBFLOW) ? MPTCP_PM_ADDR_FLAG_SUBFLOW : 0)
|
||||
| (NM_FLAGS_HAS(mptcp_flags, NM_MPTCP_FLAGS_BACKUP) ? MPTCP_PM_ADDR_FLAG_BACKUP : 0)
|
||||
| (NM_FLAGS_HAS(mptcp_flags, NM_MPTCP_FLAGS_FULLMESH) ? MPTCP_PM_ADDR_FLAG_FULLMESH
|
||||
: 0);
|
||||
NMPlatformMptcpAddr a = {
|
||||
.ifindex = self->priv.ifindex,
|
||||
.id = 0,
|
||||
.flags = FLAGS,
|
||||
.addr_family = addr_family,
|
||||
.port = 0,
|
||||
};
|
||||
gboolean any_tracked = FALSE;
|
||||
|
||||
self->priv.p->mptcp_set_x[IS_IPv4] = TRUE;
|
||||
|
||||
if (self->priv.p->combined_l3cd_commited) {
|
||||
gint32 addr_prio;
|
||||
|
||||
addr_prio = 100;
|
||||
|
||||
nm_l3_config_data_iter_ip_address_for_each (&iter,
|
||||
self->priv.p->combined_l3cd_commited,
|
||||
addr_family,
|
||||
(const NMPlatformIPAddress **) &addr) {
|
||||
/* We want to evaluate the with-{loopback,link_local}-{4,6} flags based on the actual
|
||||
* ifa_scope that the address will have once we configure it.
|
||||
* "addr" is an address we want to configure, we expect that it will
|
||||
* later have the scope nm_platform_ip_address_get_scope() based on
|
||||
* the address. */
|
||||
switch (nm_platform_ip_address_get_scope(addr_family, addr->ax.address_ptr)) {
|
||||
case RT_SCOPE_HOST:
|
||||
goto skip_addr;
|
||||
case RT_SCOPE_LINK:
|
||||
goto skip_addr;
|
||||
default:
|
||||
if (IS_IPv4) {
|
||||
/* We take all addresses, including rfc1918 private addresses
|
||||
* (nm_utils_ip_is_site_local()). */
|
||||
} else {
|
||||
if (nm_utils_ip6_is_ula(&addr->a6.address)) {
|
||||
/* Exclude unique local IPv6 addresses fc00::/7. */
|
||||
goto skip_addr;
|
||||
} else {
|
||||
/* We take all other addresses, including deprecated IN6_IS_ADDR_SITELOCAL()
|
||||
* (fec0::/10). */
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
a.addr = nm_ip_addr_init(addr_family, addr->ax.address_ptr);
|
||||
|
||||
/* We track the address with different priorities, that depends
|
||||
* on the order in which they are listed here. NMPGlobalTracker
|
||||
* will sort all tracked addresses by priority. That means, if we
|
||||
* have multiple interfaces then we will prefer the first address
|
||||
* of those interfaces, then the second, etc.
|
||||
*
|
||||
* That is relevant, because the overall number of addresses we
|
||||
* can configure in kernel is strongly limited (MPTCP_PM_ADDR_MAX). */
|
||||
if (addr_prio > 10)
|
||||
addr_prio--;
|
||||
|
||||
if (nmp_global_tracker_track(self->priv.global_tracker,
|
||||
NMP_OBJECT_TYPE_MPTCP_ADDR,
|
||||
&a,
|
||||
addr_prio,
|
||||
_MPTCP_TAG(self, IS_IPv4),
|
||||
NULL))
|
||||
changed = TRUE;
|
||||
|
||||
any_tracked = TRUE;
|
||||
|
||||
skip_addr:
|
||||
(void) 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!any_tracked) {
|
||||
/* We need to make it known that this ifindex is used. Track a dummy object. */
|
||||
if (nmp_global_tracker_track(
|
||||
self->priv.global_tracker,
|
||||
NMP_OBJECT_TYPE_MPTCP_ADDR,
|
||||
nmp_global_tracker_mptcp_addr_init_for_ifindex(&a, self->priv.ifindex),
|
||||
1,
|
||||
_MPTCP_TAG(self, IS_IPv4),
|
||||
NULL))
|
||||
changed = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (_global_tracker_mptcp_untrack(self, addr_family))
|
||||
changed = TRUE;
|
||||
|
||||
NM_SET_OUT(out_reapply, reapply);
|
||||
return changed || reapply;
|
||||
}
|
||||
|
||||
static void
|
||||
_l3_commit_mptcp(NML3Cfg *self, NML3CfgCommitType commit_type)
|
||||
{
|
||||
gboolean changed = FALSE;
|
||||
gboolean reapply = FALSE;
|
||||
gboolean i_reapply = FALSE;
|
||||
|
||||
if (_l3_commit_mptcp_af(self, commit_type, AF_INET, &i_reapply))
|
||||
changed = TRUE;
|
||||
reapply |= i_reapply;
|
||||
if (_l3_commit_mptcp_af(self, commit_type, AF_INET6, &i_reapply))
|
||||
changed = TRUE;
|
||||
reapply |= i_reapply;
|
||||
|
||||
nm_assert(commit_type < NM_L3_CFG_COMMIT_TYPE_REAPPLY || reapply);
|
||||
|
||||
if (changed)
|
||||
nmp_global_tracker_sync_mptcp_addrs(self->priv.global_tracker, reapply, FALSE);
|
||||
else
|
||||
nm_assert(!reapply);
|
||||
|
||||
_rp_filter_update(self, reapply);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_l3_commit_one(NML3Cfg *self,
|
||||
int addr_family,
|
||||
|
|
@ -4382,6 +4654,8 @@ _l3_commit(NML3Cfg *self, NML3CfgCommitType commit_type, gboolean is_idle)
|
|||
_l3_commit_one(self, AF_INET, commit_type, changed_combined_l3cd, l3cd_old);
|
||||
_l3_commit_one(self, AF_INET6, commit_type, changed_combined_l3cd, l3cd_old);
|
||||
|
||||
_l3_commit_mptcp(self, commit_type);
|
||||
|
||||
_l3_acd_data_process_changes(self);
|
||||
|
||||
if (self->priv.p->l3_config_datas) {
|
||||
|
|
@ -4790,6 +5064,7 @@ static void
|
|||
finalize(GObject *object)
|
||||
{
|
||||
NML3Cfg *self = NM_L3CFG(object);
|
||||
gboolean changed;
|
||||
|
||||
nm_assert(!self->priv.p->ipconfig_4);
|
||||
nm_assert(!self->priv.p->ipconfig_6);
|
||||
|
|
@ -4827,6 +5102,14 @@ finalize(GObject *object)
|
|||
if (_nodev_routes_untrack(self, AF_INET6))
|
||||
nmp_global_tracker_sync(self->priv.global_tracker, NMP_OBJECT_TYPE_IP6_ROUTE, FALSE);
|
||||
|
||||
changed = FALSE;
|
||||
if (_global_tracker_mptcp_untrack(self, AF_INET))
|
||||
changed = TRUE;
|
||||
if (_global_tracker_mptcp_untrack(self, AF_INET6))
|
||||
changed = TRUE;
|
||||
if (changed)
|
||||
nmp_global_tracker_sync_mptcp_addrs(self->priv.global_tracker, FALSE, FALSE);
|
||||
|
||||
g_clear_object(&self->priv.netns);
|
||||
g_clear_object(&self->priv.platform);
|
||||
nm_clear_pointer(&self->priv.global_tracker, nmp_global_tracker_unref);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <syslog.h>
|
||||
|
||||
#include "libnm-platform/nm-linux-platform.h"
|
||||
#include "libnm-platform/nmp-object.h"
|
||||
|
||||
#include "nm-test-utils-core.h"
|
||||
|
||||
|
|
@ -50,6 +51,18 @@ read_argv(int *argc, char ***argv)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static void
|
||||
mptcp_addr_dump(NMPlatform *platform)
|
||||
{
|
||||
gs_unref_ptrarray GPtrArray *addrs = NULL;
|
||||
|
||||
addrs = nm_platform_mptcp_addrs_dump(platform);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
|
|
@ -69,6 +82,8 @@ main(int argc, char **argv)
|
|||
|
||||
nm_linux_platform_setup();
|
||||
|
||||
mptcp_addr_dump(NM_PLATFORM_GET);
|
||||
|
||||
if (global_opt.persist)
|
||||
g_main_loop_run(loop);
|
||||
|
||||
|
|
|
|||
|
|
@ -518,6 +518,11 @@ void nmtstp_link_delete(NMPlatform *platform,
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
#define nmtst_object_new_mptcp_addr(...) \
|
||||
nmp_object_new(NMP_OBJECT_TYPE_MPTCP_ADDR, &((const NMPlatformMptcpAddr){__VA_ARGS__}))
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
extern int NMTSTP_ENV1_IFINDEX;
|
||||
extern int NMTSTP_ENV1_EX;
|
||||
|
||||
|
|
|
|||
|
|
@ -1961,6 +1961,200 @@ test_blackhole(gconstpointer test_data)
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
static gboolean
|
||||
_mptcp_has_permissions(void)
|
||||
{
|
||||
static int has_permissions = -1;
|
||||
int p;
|
||||
|
||||
/* We create a new netns for testing, where we also have CAP_NET_ADMIN.
|
||||
* However, that is not enough for configuring MPTCP endpoints. Probably
|
||||
* you can only create them, by running the test as root. Detect the
|
||||
* inability, to skip the test.
|
||||
*
|
||||
* See https://lore.kernel.org/mptcp/20220805115020.525181-1-thaller@redhat.com/T/#u */
|
||||
|
||||
again:
|
||||
p = g_atomic_int_get(&has_permissions);
|
||||
|
||||
if (p == -1) {
|
||||
static gsize lock;
|
||||
const NMPlatformMptcpAddr mptcp_addr = (NMPlatformMptcpAddr){
|
||||
.id = 1,
|
||||
.addr_family = AF_INET,
|
||||
.addr.addr4 = nmtst_inet4_from_string("1.2.3.4"),
|
||||
};
|
||||
int r;
|
||||
|
||||
if (!g_once_init_enter(&lock))
|
||||
goto again;
|
||||
|
||||
if (nmtst_get_rand_one_case_in(3)) {
|
||||
gs_unref_ptrarray GPtrArray *arr = NULL;
|
||||
|
||||
arr = nm_platform_mptcp_addrs_dump(NM_PLATFORM_GET);
|
||||
g_assert_cmpint(nm_g_ptr_array_len(arr), ==, 0);
|
||||
}
|
||||
|
||||
r = nm_platform_mptcp_addr_update(NM_PLATFORM_GET, TRUE, &mptcp_addr);
|
||||
if (r == 0)
|
||||
p = TRUE;
|
||||
else if (r == -EPERM)
|
||||
p = FALSE;
|
||||
else
|
||||
g_assert_cmpint(r, ==, 0);
|
||||
|
||||
if (p) {
|
||||
if (nmtst_get_rand_one_case_in(3)) {
|
||||
gs_unref_ptrarray GPtrArray *arr = NULL;
|
||||
|
||||
arr = nm_platform_mptcp_addrs_dump(NM_PLATFORM_GET);
|
||||
g_assert_cmpint(nm_g_ptr_array_len(arr), ==, 1);
|
||||
}
|
||||
|
||||
r = nm_platform_mptcp_addr_update(NM_PLATFORM_GET, FALSE, &mptcp_addr);
|
||||
g_assert_cmpint(r, ==, 0);
|
||||
}
|
||||
|
||||
if (nmtst_get_rand_one_case_in(3)) {
|
||||
gs_unref_ptrarray GPtrArray *arr = NULL;
|
||||
|
||||
arr = nm_platform_mptcp_addrs_dump(NM_PLATFORM_GET);
|
||||
g_assert_cmpint(nm_g_ptr_array_len(arr), ==, 0);
|
||||
}
|
||||
|
||||
g_atomic_int_set(&has_permissions, p);
|
||||
g_once_init_leave(&lock, 1);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_mptcp_skip_test(void)
|
||||
{
|
||||
if (nm_platform_genl_get_family_id(NM_PLATFORM_GET, NMP_GENL_FAMILY_TYPE_MPTCP_PM) == 0) {
|
||||
g_test_skip("mptcp not available");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (!_mptcp_has_permissions()) {
|
||||
g_test_skip("No permissions to create MPTCP endpoints");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
test_mptcp(gconstpointer test_data)
|
||||
{
|
||||
const int TEST_IDX = GPOINTER_TO_INT(test_data);
|
||||
gs_unref_object NMPlatform *platform = g_object_ref(NM_PLATFORM_GET);
|
||||
nm_auto_unref_global_tracker NMPGlobalTracker *global_tracker =
|
||||
nmp_global_tracker_new(platform);
|
||||
gconstpointer const USER_TAG = &TEST_IDX;
|
||||
const int IFINDEX = nm_platform_link_get_ifindex(platform, DEVICE_NAME);
|
||||
guint i;
|
||||
guint j;
|
||||
int r;
|
||||
gs_unref_ptrarray GPtrArray *arr_external =
|
||||
g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref);
|
||||
gs_unref_ptrarray GPtrArray *arr_tracked =
|
||||
g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref);
|
||||
const NMPObject *obj;
|
||||
gboolean delete_extra;
|
||||
|
||||
g_assert_cmpint(IFINDEX, >, 0);
|
||||
|
||||
if (_mptcp_skip_test())
|
||||
return;
|
||||
|
||||
j = nmtst_get_rand_uint32() % 5;
|
||||
for (i = 0; i < j; i++) {
|
||||
obj = nmtst_object_new_mptcp_addr(.id = i + 1,
|
||||
.ifindex = IFINDEX,
|
||||
.addr_family = AF_INET,
|
||||
.addr.addr4 = htonl(0xC0A80001u + i));
|
||||
g_ptr_array_add(arr_external, (gpointer) obj);
|
||||
r = nm_platform_mptcp_addr_update(platform, TRUE, NMP_OBJECT_CAST_MPTCP_ADDR(obj));
|
||||
g_assert_cmpint(r, ==, 0);
|
||||
}
|
||||
|
||||
j = nmtst_get_rand_uint32() % 10;
|
||||
for (i = 0; i < j; i++) {
|
||||
obj = nmtst_object_new_mptcp_addr(.ifindex = IFINDEX,
|
||||
.addr_family = AF_INET,
|
||||
.addr.addr4 = htonl(0xC0A80001u + i));
|
||||
g_ptr_array_add(arr_tracked, (gpointer) obj);
|
||||
nmp_global_tracker_track(global_tracker,
|
||||
NMP_OBJECT_TYPE_MPTCP_ADDR,
|
||||
NMP_OBJECT_CAST_MPTCP_ADDR(obj),
|
||||
20 - i,
|
||||
USER_TAG,
|
||||
NULL);
|
||||
}
|
||||
for (i = 0; i < arr_tracked->len;) {
|
||||
if (nmtst_get_rand_bool()) {
|
||||
nmp_global_tracker_untrack(global_tracker,
|
||||
NMP_OBJECT_TYPE_MPTCP_ADDR,
|
||||
NMP_OBJECT_CAST_MPTCP_ADDR(arr_tracked->pdata[i]),
|
||||
USER_TAG);
|
||||
g_ptr_array_remove_index(arr_tracked, i);
|
||||
} else
|
||||
i++;
|
||||
}
|
||||
|
||||
if (arr_tracked->len == 0 || nmtst_get_rand_bool()) {
|
||||
NMPlatformMptcpAddr a;
|
||||
|
||||
/* Track a dummy object that marks the ifindex as managed. */
|
||||
nmp_global_tracker_track(global_tracker,
|
||||
NMP_OBJECT_TYPE_MPTCP_ADDR,
|
||||
nmp_global_tracker_mptcp_addr_init_for_ifindex(&a, IFINDEX),
|
||||
10,
|
||||
USER_TAG,
|
||||
NULL);
|
||||
}
|
||||
|
||||
nmp_global_tracker_sync_mptcp_addrs(global_tracker, FALSE, FALSE);
|
||||
|
||||
if (nmtst_get_rand_bool()) {
|
||||
gboolean reapply;
|
||||
|
||||
nmp_global_tracker_untrack_all(global_tracker, USER_TAG, TRUE, FALSE);
|
||||
reapply = nmtst_get_rand_bool();
|
||||
nmp_global_tracker_sync_mptcp_addrs(global_tracker, reapply, FALSE);
|
||||
|
||||
delete_extra = !reapply;
|
||||
} else
|
||||
delete_extra = TRUE;
|
||||
|
||||
if (delete_extra) {
|
||||
gs_unref_ptrarray GPtrArray *arr = NULL;
|
||||
|
||||
/* We need to delete all MPTCP address again, because the next test uses the
|
||||
* same netns (this test setup doesn't create a netns per test). */
|
||||
arr = nm_platform_mptcp_addrs_dump(platform);
|
||||
for (i = 0; i < nm_g_ptr_array_len(arr); i++) {
|
||||
r = nm_platform_mptcp_addr_update(platform,
|
||||
FALSE,
|
||||
NMP_OBJECT_CAST_MPTCP_ADDR(arr->pdata[i]));
|
||||
g_assert(NMTST_NM_ERR_SUCCESS(r));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
gs_unref_ptrarray GPtrArray *arr = NULL;
|
||||
|
||||
arr = nm_platform_mptcp_addrs_dump(platform);
|
||||
g_assert(arr);
|
||||
g_assert_cmpint(arr->len, ==, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP;
|
||||
|
||||
void
|
||||
|
|
@ -1975,6 +2169,7 @@ _nmtstp_setup_tests(void)
|
|||
#define add_test_func(testpath, test_func) nmtstp_env1_add_test_func(testpath, test_func, TRUE)
|
||||
#define add_test_func_data(testpath, test_func, arg) \
|
||||
nmtstp_env1_add_test_func_data(testpath, test_func, arg, TRUE)
|
||||
|
||||
add_test_func("/route/ip4", test_ip4_route);
|
||||
add_test_func("/route/ip6", test_ip6_route);
|
||||
add_test_func("/route/ip4_metric0", test_ip4_route_metric0);
|
||||
|
|
@ -2002,4 +2197,8 @@ _nmtstp_setup_tests(void)
|
|||
add_test_func_data("/route/blackhole/1", test_blackhole, GINT_TO_POINTER(1));
|
||||
add_test_func_data("/route/blackhole/2", test_blackhole, GINT_TO_POINTER(2));
|
||||
}
|
||||
if (nmtstp_is_root_test()) {
|
||||
add_test_func_data("/route/mptcp/1", test_mptcp, GINT_TO_POINTER(1));
|
||||
add_test_func_data("/route/mptcp/2", test_mptcp, GINT_TO_POINTER(2));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -645,6 +645,11 @@ make_connection_setting(const char *file,
|
|||
PARSE_WARNING("invalid DNS_OVER_TLS setting");
|
||||
g_object_set(s_con, NM_SETTING_CONNECTION_DNS_OVER_TLS, i_val, NULL);
|
||||
|
||||
i_val = NM_MPTCP_FLAGS_NONE;
|
||||
if (!svGetValueEnum(ifcfg, "MPTCP_FLAGS", nm_mptcp_flags_get_type(), &i_val, NULL))
|
||||
PARSE_WARNING("invalid MPTCP_FLAGS setting");
|
||||
g_object_set(s_con, NM_SETTING_CONNECTION_MPTCP_FLAGS, (guint) i_val, NULL);
|
||||
|
||||
vint32 = svGetValueInt64(ifcfg, "WAIT_ACTIVATION_DELAY", 10, -1, G_MAXINT32, -1);
|
||||
g_object_set(s_con, NM_SETTING_CONNECTION_WAIT_ACTIVATION_DELAY, (int) vint32, NULL);
|
||||
|
||||
|
|
|
|||
|
|
@ -1006,6 +1006,7 @@ const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[] = {
|
|||
_KEY_TYPE("MDNS", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
|
||||
_KEY_TYPE("METRIC", NMS_IFCFG_KEY_TYPE_IS_NUMBERED),
|
||||
_KEY_TYPE("MODE", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
|
||||
_KEY_TYPE("MPTCP_FLAGS", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
|
||||
_KEY_TYPE("MTU", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
|
||||
_KEY_TYPE("MUD_URL", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
|
||||
_KEY_TYPE("MULTI_CONNECT", NMS_IFCFG_KEY_TYPE_IS_PLAIN),
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ typedef struct {
|
|||
NMSIfcfgKeyTypeFlags key_flags;
|
||||
} NMSIfcfgKeyTypeInfo;
|
||||
|
||||
extern const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[255];
|
||||
extern const NMSIfcfgKeyTypeInfo nms_ifcfg_well_known_keys[256];
|
||||
|
||||
const NMSIfcfgKeyTypeInfo *nms_ifcfg_well_known_key_find_info(const char *key, gssize *out_idx);
|
||||
|
||||
|
|
|
|||
|
|
@ -2095,6 +2095,7 @@ write_connection_setting(NMSettingConnection *s_con, shvarFile *ifcfg)
|
|||
NMSettingConnectionMdns mdns;
|
||||
NMSettingConnectionLlmnr llmnr;
|
||||
NMSettingConnectionDnsOverTls dns_over_tls;
|
||||
NMMptcpFlags mptcp_flags;
|
||||
guint32 vuint32;
|
||||
const char *tmp, *mud_url;
|
||||
|
||||
|
|
@ -2289,6 +2290,10 @@ write_connection_setting(NMSettingConnection *s_con, shvarFile *ifcfg)
|
|||
nm_setting_connection_dns_over_tls_get_type(),
|
||||
dns_over_tls);
|
||||
}
|
||||
|
||||
mptcp_flags = nm_setting_connection_get_mptcp_flags(s_con);
|
||||
if (mptcp_flags != NM_MPTCP_FLAGS_NONE)
|
||||
svSetValueEnum(ifcfg, "MPTCP_FLAGS", nm_mptcp_flags_get_type(), mptcp_flags);
|
||||
}
|
||||
|
||||
static char *
|
||||
|
|
|
|||
|
|
@ -1839,6 +1839,7 @@ global:
|
|||
libnm_1_40_0 {
|
||||
global:
|
||||
nm_conn_wireguard_import;
|
||||
nm_mptcp_flags_get_type;
|
||||
nm_setting_connection_get_wait_activation_delay;
|
||||
nm_setting_ip4_link_local_get_type;
|
||||
nm_setting_ip6_config_get_mtu;
|
||||
|
|
|
|||
|
|
@ -493,3 +493,36 @@ _nm_connection_new_setting(NMConnection *connection, GType gtype)
|
|||
nm_connection_add_setting(connection, setting);
|
||||
return setting;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
NMMptcpFlags
|
||||
nm_mptcp_flags_normalize(NMMptcpFlags flags)
|
||||
{
|
||||
/* Certain combinations of flags are incompatible. Normalize them.
|
||||
*
|
||||
* This function never returns 0x0 (NONE). If the flags are neither
|
||||
* disabled,enabled-on-global-iface,enabled, then we default to "enabled". */
|
||||
|
||||
if (NM_FLAGS_HAS(flags, NM_MPTCP_FLAGS_DISABLED)) {
|
||||
/* If the disabled flag is set, then that's the end of it. */
|
||||
return NM_MPTCP_FLAGS_DISABLED;
|
||||
}
|
||||
|
||||
/* Clear all unknown flags. */
|
||||
flags &= _NM_MPTCP_FLAGS_ALL;
|
||||
|
||||
/* We must either set "enabled-on-global-iface" or "enabled". The
|
||||
* former takes precedence, if they are both set.
|
||||
*
|
||||
* If neither is set, we default to "enabled". */
|
||||
if (NM_FLAGS_HAS(flags, NM_MPTCP_FLAGS_ENABLED_ON_GLOBAL_IFACE))
|
||||
flags = NM_FLAGS_UNSET(flags, NM_MPTCP_FLAGS_ENABLED);
|
||||
else
|
||||
flags = NM_FLAGS_SET(flags, NM_MPTCP_FLAGS_ENABLED);
|
||||
|
||||
if (NM_FLAGS_ALL(flags, NM_MPTCP_FLAGS_SIGNAL | NM_MPTCP_FLAGS_FULLMESH))
|
||||
flags = NM_FLAGS_UNSET(flags, NM_MPTCP_FLAGS_FULLMESH);
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -267,4 +267,16 @@ gboolean nm_settings_connection_validate_permission_user(const char *item, gssiz
|
|||
gpointer _nm_connection_ensure_setting(NMConnection *connection, GType gtype);
|
||||
gpointer _nm_connection_new_setting(NMConnection *connection, GType gtype);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#define _NM_MPTCP_FLAGS_ALL \
|
||||
((NMMptcpFlags) (NM_MPTCP_FLAGS_DISABLED | NM_MPTCP_FLAGS_ENABLED_ON_GLOBAL_IFACE \
|
||||
| NM_MPTCP_FLAGS_ENABLED | NM_MPTCP_FLAGS_SIGNAL | NM_MPTCP_FLAGS_SUBFLOW \
|
||||
| NM_MPTCP_FLAGS_BACKUP | NM_MPTCP_FLAGS_FULLMESH))
|
||||
|
||||
#define _NM_MPTCP_FLAGS_DEFAULT \
|
||||
((NMMptcpFlags) (NM_MPTCP_FLAGS_ENABLED_ON_GLOBAL_IFACE | NM_MPTCP_FLAGS_SUBFLOW))
|
||||
|
||||
NMMptcpFlags nm_mptcp_flags_normalize(NMMptcpFlags flags);
|
||||
|
||||
#endif /* __NM_LIBNM_SHARED_UTILS_H__ */
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ NM_GOBJECT_PROPERTIES_DEFINE(NMSettingConnection,
|
|||
PROP_MDNS,
|
||||
PROP_LLMNR,
|
||||
PROP_DNS_OVER_TLS,
|
||||
PROP_MPTCP_FLAGS,
|
||||
PROP_STABLE_ID,
|
||||
PROP_AUTH_RETRIES,
|
||||
PROP_WAIT_DEVICE_TIMEOUT,
|
||||
|
|
@ -96,6 +97,7 @@ typedef struct {
|
|||
gint32 wait_device_timeout;
|
||||
gint32 lldp;
|
||||
gint32 wait_activation_delay;
|
||||
guint32 mptcp_flags;
|
||||
guint32 gateway_ping_timeout;
|
||||
bool autoconnect;
|
||||
bool read_only;
|
||||
|
|
@ -1015,6 +1017,22 @@ nm_setting_connection_get_dns_over_tls(NMSettingConnection *setting)
|
|||
return NM_SETTING_CONNECTION_GET_PRIVATE(setting)->dns_over_tls;
|
||||
}
|
||||
|
||||
/**
|
||||
* nm_setting_connection_get_mptcp_flags:
|
||||
* @setting: the #NMSettingConnection
|
||||
*
|
||||
* Returns: the #NMSettingConnection:mptcp-flags property of the setting.
|
||||
*
|
||||
* Since: 1.40
|
||||
**/
|
||||
NMMptcpFlags
|
||||
nm_setting_connection_get_mptcp_flags(NMSettingConnection *setting)
|
||||
{
|
||||
g_return_val_if_fail(NM_IS_SETTING_CONNECTION(setting), NM_MPTCP_FLAGS_NONE);
|
||||
|
||||
return NM_SETTING_CONNECTION_GET_PRIVATE(setting)->mptcp_flags;
|
||||
}
|
||||
|
||||
static void
|
||||
_set_error_missing_base_setting(GError **error, const char *type)
|
||||
{
|
||||
|
|
@ -1367,6 +1385,64 @@ after_interface_name:
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
if (priv->mptcp_flags != 0) {
|
||||
if (NM_FLAGS_HAS(priv->mptcp_flags, NM_MPTCP_FLAGS_DISABLED)) {
|
||||
if (priv->mptcp_flags != NM_MPTCP_FLAGS_DISABLED) {
|
||||
g_set_error_literal(
|
||||
error,
|
||||
NM_CONNECTION_ERROR,
|
||||
NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
||||
_("\"disabled\" flag cannot be combined with other MPTCP flags"));
|
||||
g_prefix_error(error,
|
||||
"%s.%s: ",
|
||||
NM_SETTING_CONNECTION_SETTING_NAME,
|
||||
NM_SETTING_CONNECTION_MPTCP_FLAGS);
|
||||
return FALSE;
|
||||
}
|
||||
} else {
|
||||
guint32 f;
|
||||
|
||||
if (NM_FLAGS_ALL(priv->mptcp_flags,
|
||||
NM_MPTCP_FLAGS_ENABLED_ON_GLOBAL_IFACE | NM_MPTCP_FLAGS_ENABLED)) {
|
||||
g_set_error_literal(
|
||||
error,
|
||||
NM_CONNECTION_ERROR,
|
||||
NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
||||
_("\"enabled\" and \"enabled-on-global-iface\" flag cannot be set together"));
|
||||
g_prefix_error(error,
|
||||
"%s.%s: ",
|
||||
NM_SETTING_CONNECTION_SETTING_NAME,
|
||||
NM_SETTING_CONNECTION_MPTCP_FLAGS);
|
||||
return FALSE;
|
||||
}
|
||||
if (NM_FLAGS_ALL(priv->mptcp_flags, NM_MPTCP_FLAGS_SIGNAL | NM_MPTCP_FLAGS_FULLMESH)) {
|
||||
g_set_error_literal(error,
|
||||
NM_CONNECTION_ERROR,
|
||||
NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
||||
_("cannot set both \"signal\" and \"fullmesh\" MPTCP flags"));
|
||||
g_prefix_error(error,
|
||||
"%s.%s: ",
|
||||
NM_SETTING_CONNECTION_SETTING_NAME,
|
||||
NM_SETTING_CONNECTION_MPTCP_FLAGS);
|
||||
return FALSE;
|
||||
}
|
||||
f = NM_FLAGS_UNSET(priv->mptcp_flags, NM_MPTCP_FLAGS_ENABLED_ON_GLOBAL_IFACE)
|
||||
| ((guint32) NM_MPTCP_FLAGS_ENABLED);
|
||||
if (f != nm_mptcp_flags_normalize(f)) {
|
||||
g_set_error(error,
|
||||
NM_CONNECTION_ERROR,
|
||||
NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
||||
_("value %u is not a valid combination of MPTCP flags"),
|
||||
priv->mptcp_flags);
|
||||
g_prefix_error(error,
|
||||
"%s.%s: ",
|
||||
NM_SETTING_CONNECTION_SETTING_NAME,
|
||||
NM_SETTING_CONNECTION_MPTCP_FLAGS);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!NM_IN_SET(priv->multi_connect,
|
||||
(int) NM_CONNECTION_MULTI_CONNECT_DEFAULT,
|
||||
(int) NM_CONNECTION_MULTI_CONNECT_SINGLE,
|
||||
|
|
@ -2483,6 +2559,101 @@ nm_setting_connection_class_init(NMSettingConnectionClass *klass)
|
|||
NMSettingConnectionPrivate,
|
||||
dns_over_tls);
|
||||
|
||||
/* Notes about "mptcp-flags":
|
||||
*
|
||||
* It is a bit odd that NMMptcpFlags mixes flags with different purposes:
|
||||
*
|
||||
* - "disabled", "disabled-on-local-iface", "enable": whether MPTCP handling
|
||||
* is enabled. The flag "disabled-on-local-iface" enables it based on whether
|
||||
* the interface has a default route.
|
||||
* - "signal", "subflow", "backup", "fullmesh": the endpoint flags
|
||||
* that are used.
|
||||
*
|
||||
* The reason is, that it is useful to have one "connection.mptcp-flags"
|
||||
* property, that can express various aspects at once. The alternatives
|
||||
* would be multiple properties like "connection.mptcp-enabled",
|
||||
* "connection.mptcp-addr-flags" and "connection.mptcp-notify-flags".
|
||||
* More properties does not necessarily make the API simpler. In particular
|
||||
* for something like MPTCP, which should just work by default and only
|
||||
* in special cases require special configuration.
|
||||
*
|
||||
* The entire idea is to only have one "connection.mptcp-flags" property (for now).
|
||||
* That one can encode multiple aspects about MPTCP, whether it's enabled at all,
|
||||
* which address flags to use when configuring endpoints, and opt-in addresses
|
||||
* that otherwise would not be configured as endpoints.
|
||||
*
|
||||
* "connection.mptcp-flags" applies to all addresses on the interface (minus the ones
|
||||
* that are not included by default). The idea is that in the future we could have
|
||||
* more properties like "ipv4.dhcp-mptcp-flags=subflow", "ipv6.link-local-mptcp-flags=disabled",
|
||||
* "ipv4.addresses='192.168.1.5/24 mptcp-flags=signal,backup'", which can overwrite the
|
||||
* flags on a per-address basis.
|
||||
*
|
||||
* But for that future extension, we now need a global "connection.mptcp-flags" property
|
||||
* in the API that is the basis and applies to all addresses.
|
||||
*/
|
||||
|
||||
/**
|
||||
* NMSettingConnection:mptcp-flags:
|
||||
*
|
||||
* Whether to configure MPTCP endpoints and the address flags.
|
||||
* If MPTCP is enabled in NetworkManager, it will configure the
|
||||
* addresses of the interface as MPTCP endpoints. Note that
|
||||
* IPv4 loopback addresses (127.0.0.0/8), IPv4 link local
|
||||
* addresses (169.254.0.0/16), the IPv6 loopback address (::1),
|
||||
* IPv6 link local addresses (fe80::/10), IPv6 unique
|
||||
* local addresses (ULA, fc00::/7) and IPv6 privacy extension addresses
|
||||
* (rfc3041, ipv6.ip6-privacy) will be excluded from being
|
||||
* configured as endpoints.
|
||||
*
|
||||
* If "disabled" (0x1), MPTCP handling for the interface is disabled and
|
||||
* no endpoints are registered.
|
||||
*
|
||||
* The flag "enabled-on-global-iface" (0x2) means that MPTCP handling is enabled
|
||||
* if the interface configures a default route in the main routing table.
|
||||
* This choice is per-address family, for example if there is an IPv4 default route
|
||||
* 0.0.0.0/0, IPv4 endpoints are configured.
|
||||
*
|
||||
* The "enabled" (0x4) flag means that MPTCP handling is explicitly enabled.
|
||||
* This flag can also be implied from the presence of other flags.
|
||||
*
|
||||
* If MPTCP handling is enabled, then endpoints will be configured
|
||||
* with the specified address flags "signal" (0x10), "subflow" (0x20), "backup" (0x40),
|
||||
* "fullmesh" (0x80). See ip-mptcp(8) manual for additional information about the flags.
|
||||
*
|
||||
* If the flags are zero, the global connection default from NetworkManager.conf is
|
||||
* honored. If still unspecified, the fallback is either "disabled" or
|
||||
* "enabled-on-global-iface,subflow" depending on "/proc/sys/net/mptcp/enabled".
|
||||
*
|
||||
* NetworkManager does not change the MPTCP limits nor enable MPTCP via
|
||||
* "/proc/sys/net/mptcp/enabled". That is a host configuration which the
|
||||
* admin can change via sysctl and ip-mptcp.
|
||||
*
|
||||
* Strict reverse path filtering (rp_filter) breaks many MPTCP use cases, so when
|
||||
* MPTCP handling for IPv4 addresses on the interface is enabled, NetworkManager would
|
||||
* loosen the strict reverse path filtering (1) to the loose setting (2).
|
||||
*
|
||||
* Since: 1.40
|
||||
**/
|
||||
/* ---ifcfg-rh---
|
||||
* property: mptcp-flags
|
||||
* variable: MPTCP_FLAGS(+)
|
||||
* default: missing variable means global default
|
||||
* description: The MPTCP flags that indicate whether MPTCP is enabled
|
||||
* and which flags to use for the address endpoints.
|
||||
* example: MPTCP_FLAGS="signal,subflow"
|
||||
* ---end---
|
||||
*/
|
||||
_nm_setting_property_define_direct_uint32(properties_override,
|
||||
obj_properties,
|
||||
NM_SETTING_CONNECTION_MPTCP_FLAGS,
|
||||
PROP_MPTCP_FLAGS,
|
||||
0,
|
||||
G_MAXUINT32,
|
||||
NM_MPTCP_FLAGS_NONE,
|
||||
NM_SETTING_PARAM_NONE,
|
||||
NMSettingConnectionPrivate,
|
||||
mptcp_flags);
|
||||
|
||||
/**
|
||||
* NMSettingConnection:wait-device-timeout:
|
||||
*
|
||||
|
|
|
|||
|
|
@ -3954,6 +3954,7 @@ test_connection_diff_a_only(void)
|
|||
{NM_SETTING_CONNECTION_MDNS, NM_SETTING_DIFF_RESULT_IN_A},
|
||||
{NM_SETTING_CONNECTION_LLMNR, NM_SETTING_DIFF_RESULT_IN_A},
|
||||
{NM_SETTING_CONNECTION_DNS_OVER_TLS, NM_SETTING_DIFF_RESULT_IN_A},
|
||||
{NM_SETTING_CONNECTION_MPTCP_FLAGS, NM_SETTING_DIFF_RESULT_IN_A},
|
||||
{NM_SETTING_CONNECTION_MUD_URL, NM_SETTING_DIFF_RESULT_IN_A},
|
||||
{NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT, NM_SETTING_DIFF_RESULT_IN_A},
|
||||
{NM_SETTING_CONNECTION_WAIT_ACTIVATION_DELAY, NM_SETTING_DIFF_RESULT_IN_A},
|
||||
|
|
|
|||
|
|
@ -1315,4 +1315,49 @@ typedef enum /*< flags >*/ {
|
|||
NM_RADIO_FLAG_WWAN_AVAILABLE = 0x2,
|
||||
} NMRadioFlags;
|
||||
|
||||
/**
|
||||
* NMMptcpFlags:
|
||||
* @NM_MPTCP_FLAGS_NONE: The default, meaning that no MPTCP flags are set.
|
||||
* @NM_MPTCP_FLAGS_DISABLED: don't configure MPTCP endpoints on the device.
|
||||
* @NM_MPTCP_FLAGS_ENABLED_ON_GLOBAL_IFACE: MPTCP handling is enabled
|
||||
* or disabled depending on whether a /0 default route (either IPv4 or IPv6) is
|
||||
* configured in the main routing table.
|
||||
* @NM_MPTCP_FLAGS_ENABLED: MPTCP is enabled and endpoints will be configured.
|
||||
* This flag is implied if any of the other flags indicate that
|
||||
* MPTCP is enabled and therefore in most cases unnecessary.
|
||||
* @NM_MPTCP_FLAGS_SIGNAL: Flag for the MPTCP endpoint. The endpoint will be
|
||||
* announced/signaled to each peer via an MPTCP ADD_ADDR sub-option.
|
||||
* @NM_MPTCP_FLAGS_SUBFLOW: Flag for the MPTCP endpoint. If additional subflow creation
|
||||
* is allowed by the MPTCP limits, the MPTCP path manager will try to create an
|
||||
* additional subflow using this endpoint as the source address after the MPTCP connection
|
||||
* is established.
|
||||
* @NM_MPTCP_FLAGS_BACKUP: Flag for the MPTCP endpoint. If this is a subflow endpoint, the
|
||||
* subflows created using this endpoint will have the backup flag set during the connection
|
||||
* process. This flag instructs the peer to only send data on a given subflow when all
|
||||
* non-backup subflows are unavailable. This does not affect outgoing data,
|
||||
* where subflow priority is determined by the backup/non-backup flag received
|
||||
* from the peer
|
||||
* @NM_MPTCP_FLAGS_FULLMESH: Flag for the MPTCP endpoint. If this is a subflow endpoint and additional
|
||||
* subflow creation is allowed by the MPTCP limits, the MPTCP path manager will try to create an
|
||||
* additional subflow for each known peer address, using this endpoint as the source address.
|
||||
* This will occur after the MPTCP connection is established. If the peer did not announce
|
||||
* any additional addresses using the MPTCP ADD_ADDR sub-option, this will behave the same
|
||||
* as a plain subflow endpoint. When the peer does announce addresses, each received ADD_ADDR
|
||||
* sub-option will trigger creation of an additional subflow to generate a full mesh topology.
|
||||
*
|
||||
* Since: 1.40
|
||||
*/
|
||||
typedef enum /*< flags >*/ {
|
||||
NM_MPTCP_FLAGS_NONE = 0,
|
||||
|
||||
NM_MPTCP_FLAGS_DISABLED = 0x1,
|
||||
NM_MPTCP_FLAGS_ENABLED_ON_GLOBAL_IFACE = 0x2,
|
||||
NM_MPTCP_FLAGS_ENABLED = 0x4,
|
||||
|
||||
NM_MPTCP_FLAGS_SIGNAL = 0x10,
|
||||
NM_MPTCP_FLAGS_SUBFLOW = 0x20,
|
||||
NM_MPTCP_FLAGS_BACKUP = 0x40,
|
||||
NM_MPTCP_FLAGS_FULLMESH = 0x80,
|
||||
} NMMptcpFlags;
|
||||
|
||||
#endif /* __NM_DBUS_INTERFACE_H__ */
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ G_BEGIN_DECLS
|
|||
#define NM_SETTING_CONNECTION_MDNS "mdns"
|
||||
#define NM_SETTING_CONNECTION_LLMNR "llmnr"
|
||||
#define NM_SETTING_CONNECTION_DNS_OVER_TLS "dns-over-tls"
|
||||
#define NM_SETTING_CONNECTION_MPTCP_FLAGS "mptcp-flags"
|
||||
#define NM_SETTING_CONNECTION_WAIT_DEVICE_TIMEOUT "wait-device-timeout"
|
||||
#define NM_SETTING_CONNECTION_MUD_URL "mud-url"
|
||||
#define NM_SETTING_CONNECTION_WAIT_ACTIVATION_DELAY "wait-activation-delay"
|
||||
|
|
@ -216,6 +217,9 @@ NMSettingConnectionLlmnr nm_setting_connection_get_llmnr(NMSettingConnection *se
|
|||
NM_AVAILABLE_IN_1_34
|
||||
NMSettingConnectionDnsOverTls nm_setting_connection_get_dns_over_tls(NMSettingConnection *setting);
|
||||
|
||||
NM_AVAILABLE_IN_1_40
|
||||
NMMptcpFlags nm_setting_connection_get_mptcp_flags(NMSettingConnection *setting);
|
||||
|
||||
NM_AVAILABLE_IN_1_20
|
||||
gint32 nm_setting_connection_get_wait_device_timeout(NMSettingConnection *setting);
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ static NM_UTILS_LOOKUP_STR_DEFINE(
|
|||
NM_UTILS_LOOKUP_STR_ITEM(NME_NL_ATTRSIZE, "NME_NL_ATTRSIZE"),
|
||||
NM_UTILS_LOOKUP_STR_ITEM(NME_NL_BAD_SOCK, "NME_NL_BAD_SOCK"),
|
||||
NM_UTILS_LOOKUP_STR_ITEM(NME_NL_DUMP_INTR, "NME_NL_DUMP_INTR"),
|
||||
NM_UTILS_LOOKUP_STR_ITEM(NME_NL_MSG_INVAL, "NME_NL_MSG_INVAL"),
|
||||
NM_UTILS_LOOKUP_STR_ITEM(NME_NL_MSG_OVERFLOW, "NME_NL_MSG_OVERFLOW"),
|
||||
NM_UTILS_LOOKUP_STR_ITEM(NME_NL_MSG_TOOSHORT, "NME_NL_MSG_TOOSHORT"),
|
||||
NM_UTILS_LOOKUP_STR_ITEM(NME_NL_MSG_TRUNC, "NME_NL_MSG_TRUNC"),
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ enum _NMErrno {
|
|||
|
||||
/* netlink errors. */
|
||||
NME_NL_SEQ_MISMATCH,
|
||||
NME_NL_MSG_INVAL,
|
||||
NME_NL_MSG_TRUNC,
|
||||
NME_NL_MSG_TOOSHORT,
|
||||
NME_NL_DUMP_INTR,
|
||||
|
|
|
|||
|
|
@ -1015,12 +1015,22 @@ nm_utils_ip_is_site_local(int addr_family, const void *address)
|
|||
return (addr4 & 0xff000000) == 0x0a000000 || (addr4 & 0xfff00000) == 0xac100000
|
||||
|| (addr4 & 0xffff0000) == 0xc0a80000;
|
||||
case AF_INET6:
|
||||
/* IN6_IS_ADDR_SITELOCAL() is for deprecated fec0::/10 addresses (see rfc3879, 4.).
|
||||
* Note that for unique local IPv6 addresses (ULA, fc00::/7) this returns false,
|
||||
* which may or may not be a bug. */
|
||||
return IN6_IS_ADDR_SITELOCAL(address);
|
||||
default:
|
||||
g_return_val_if_reached(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
nm_utils_ip6_is_ula(const struct in6_addr *address)
|
||||
{
|
||||
/* Unique local IPv6 address (ULA) fc00::/7 */
|
||||
return (address->s6_addr32[0] & htonl(0xfe000000u)) == htonl(0xfc000000u);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static gboolean
|
||||
|
|
|
|||
|
|
@ -504,6 +504,7 @@ nm_utils_ip_address_same_prefix(int addr_family,
|
|||
/*****************************************************************************/
|
||||
|
||||
gboolean nm_utils_ip_is_site_local(int addr_family, const void *address);
|
||||
gboolean nm_utils_ip6_is_ula(const struct in6_addr *address);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
|
|
@ -2405,6 +2406,25 @@ nm_g_hash_table_contains(GHashTable *hash, gconstpointer key)
|
|||
return hash ? g_hash_table_contains(hash, key) : FALSE;
|
||||
}
|
||||
|
||||
#define nm_g_hash_table_contains_any(hash, ...) \
|
||||
({ \
|
||||
GHashTable *const _hash = (hash); \
|
||||
gconstpointer const _keys[] = {__VA_ARGS__}; \
|
||||
int _i_key; \
|
||||
gboolean _contains = FALSE; \
|
||||
\
|
||||
if (_hash) { \
|
||||
for (_i_key = 0; _i_key < (int) G_N_ELEMENTS(_keys); _i_key++) { \
|
||||
if (g_hash_table_contains(_hash, _keys[_i_key])) { \
|
||||
_contains = TRUE; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
_contains; \
|
||||
})
|
||||
|
||||
static inline gboolean
|
||||
nm_g_hash_table_remove(GHashTable *hash, gconstpointer key)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2576,7 +2576,7 @@ typedef struct {
|
|||
} WireGuardParseData;
|
||||
|
||||
static int
|
||||
_wireguard_get_device_cb(struct nl_msg *msg, void *arg)
|
||||
_wireguard_get_device_cb(const struct nl_msg *msg, void *arg)
|
||||
{
|
||||
static const struct nla_policy policy[] = {
|
||||
[WGDEVICE_A_IFINDEX] = {.type = NLA_U32},
|
||||
|
|
@ -2873,14 +2873,6 @@ _wireguard_create_change_nlmsgs(NMPlatform *platfo
|
|||
struct nlattr *nest_curr_allowed_ip;
|
||||
NMPlatformWireGuardChangePeerFlags p_flags = NM_PLATFORM_WIREGUARD_CHANGE_PEER_FLAG_DEFAULT;
|
||||
|
||||
#define _nla_nest_end(msg, nest_start) \
|
||||
G_STMT_START \
|
||||
{ \
|
||||
if (nla_nest_end((msg), (nest_start)) < 0) \
|
||||
g_return_val_if_reached(-NME_BUG); \
|
||||
} \
|
||||
G_STMT_END
|
||||
|
||||
/* Adapted from LGPL-2.1+ code [1].
|
||||
*
|
||||
* [1] https://git.zx2c4.com/WireGuard/tree/contrib/examples/embeddable-wg-library/wireguard.c?id=5e99a6d43fe2351adf36c786f5ea2086a8fe7ab8#n1073 */
|
||||
|
|
@ -3036,36 +3028,36 @@ again:
|
|||
if (nla_put_uint8(msg, WGALLOWEDIP_A_CIDR_MASK, aip->mask) < 0)
|
||||
goto toobig_allowedips;
|
||||
|
||||
_nla_nest_end(msg, nest_curr_allowed_ip);
|
||||
NLA_NEST_END(msg, nest_curr_allowed_ip);
|
||||
nest_curr_allowed_ip = NULL;
|
||||
}
|
||||
idx_allowed_ips_curr = IDX_NIL;
|
||||
|
||||
_nla_nest_end(msg, nest_allowed_ips);
|
||||
NLA_NEST_END(msg, nest_allowed_ips);
|
||||
nest_allowed_ips = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
_nla_nest_end(msg, nest_curr_peer);
|
||||
NLA_NEST_END(msg, nest_curr_peer);
|
||||
nest_curr_peer = NULL;
|
||||
}
|
||||
|
||||
_nla_nest_end(msg, nest_peers);
|
||||
NLA_NEST_END(msg, nest_peers);
|
||||
goto send;
|
||||
|
||||
toobig_allowedips:
|
||||
if (nest_curr_allowed_ip)
|
||||
nla_nest_cancel(msg, nest_curr_allowed_ip);
|
||||
if (nest_allowed_ips)
|
||||
_nla_nest_end(msg, nest_allowed_ips);
|
||||
_nla_nest_end(msg, nest_curr_peer);
|
||||
_nla_nest_end(msg, nest_peers);
|
||||
NLA_NEST_END(msg, nest_allowed_ips);
|
||||
NLA_NEST_END(msg, nest_curr_peer);
|
||||
NLA_NEST_END(msg, nest_peers);
|
||||
goto send;
|
||||
|
||||
toobig_peers:
|
||||
if (nest_curr_peer)
|
||||
nla_nest_cancel(msg, nest_curr_peer);
|
||||
_nla_nest_end(msg, nest_peers);
|
||||
NLA_NEST_END(msg, nest_peers);
|
||||
goto send;
|
||||
|
||||
send:
|
||||
|
|
@ -3081,8 +3073,6 @@ send:
|
|||
|
||||
nla_put_failure:
|
||||
g_return_val_if_reached(-NME_BUG);
|
||||
|
||||
#undef _nla_nest_end
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
@ -9344,6 +9334,9 @@ object_delete(NMPlatform *platform, const NMPObject *obj)
|
|||
case NMP_OBJECT_TYPE_TFILTER:
|
||||
nlmsg = _nl_msg_new_tfilter(RTM_DELTFILTER, 0, NMP_OBJECT_CAST_TFILTER(obj));
|
||||
break;
|
||||
case NMP_OBJECT_TYPE_MPTCP_ADDR:
|
||||
return (nm_platform_mptcp_addr_update(platform, FALSE, NMP_OBJECT_CAST_MPTCP_ADDR(obj))
|
||||
>= 0);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -9866,49 +9859,20 @@ continue_reading:
|
|||
* NL_SKIP or NL_PROCEED (dangerous) */
|
||||
retval = -NME_NL_MSG_OVERFLOW;
|
||||
} else if (msg.nm_nlh->nlmsg_type == NLMSG_ERROR) {
|
||||
/* Message carries a nlmsgerr */
|
||||
struct nlmsgerr *e = nlmsg_data(msg.nm_nlh);
|
||||
int errsv;
|
||||
|
||||
if (msg.nm_nlh->nlmsg_len < nlmsg_size(sizeof(*e))) {
|
||||
/* Truncated error message, the default action
|
||||
* is to stop parsing. The user may overrule
|
||||
* this action by returning NL_SKIP or
|
||||
* NL_PROCEED (dangerous) */
|
||||
retval = -NME_NL_MSG_TRUNC;
|
||||
} else if (e->error) {
|
||||
int errsv = nm_errno_native(e->error);
|
||||
|
||||
if (NM_FLAGS_HAS(msg.nm_nlh->nlmsg_flags, NLM_F_ACK_TLVS)
|
||||
&& msg.nm_nlh->nlmsg_len >= sizeof(*e) + e->msg.nlmsg_len) {
|
||||
static const struct nla_policy policy[] = {
|
||||
[NLMSGERR_ATTR_MSG] = {.type = NLA_STRING},
|
||||
[NLMSGERR_ATTR_OFFS] = {.type = NLA_U32},
|
||||
};
|
||||
struct nlattr *tb[G_N_ELEMENTS(policy)];
|
||||
struct nlattr *tlvs;
|
||||
|
||||
tlvs = (struct nlattr *) ((char *) e + sizeof(*e) + e->msg.nlmsg_len
|
||||
- NLMSG_HDRLEN);
|
||||
if (nla_parse_arr(tb,
|
||||
tlvs,
|
||||
msg.nm_nlh->nlmsg_len - sizeof(*e) - e->msg.nlmsg_len,
|
||||
policy)
|
||||
>= 0) {
|
||||
if (tb[NLMSGERR_ATTR_MSG])
|
||||
extack_msg = nla_get_string(tb[NLMSGERR_ATTR_MSG]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Error message reported back from kernel. */
|
||||
errsv = nlmsg_parse_error(msg.nm_nlh, &extack_msg);
|
||||
if (errsv == 0)
|
||||
seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK;
|
||||
else {
|
||||
_LOGD("%s: recvmsg: error message from kernel: %s (%d)%s%s%s for request %d",
|
||||
log_prefix,
|
||||
nm_strerror_native(errsv),
|
||||
nm_strerror(errsv),
|
||||
errsv,
|
||||
NM_PRINT_FMT_QUOTED(extack_msg, " \"", extack_msg, "\"", ""),
|
||||
msg.nm_nlh->nlmsg_seq);
|
||||
seq_result = -NM_ERRNO_NATIVE(errsv);
|
||||
} else
|
||||
seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK;
|
||||
seq_result = errsv;
|
||||
}
|
||||
} else
|
||||
process_valid_msg = TRUE;
|
||||
|
||||
|
|
@ -10140,6 +10104,301 @@ out:
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
static const NMPObject *
|
||||
_mptcp_addrs_dump_parse_addr(struct nlattr *attr)
|
||||
{
|
||||
static const struct nla_policy policy[] = {
|
||||
[MPTCP_PM_ADDR_ATTR_FAMILY] = {.type = NLA_U16},
|
||||
[MPTCP_PM_ADDR_ATTR_ID] = {.type = NLA_U8},
|
||||
[MPTCP_PM_ADDR_ATTR_ADDR4] = {.minlen = sizeof(in_addr_t)},
|
||||
[MPTCP_PM_ADDR_ATTR_ADDR6] = {.minlen = sizeof(struct in6_addr)},
|
||||
[MPTCP_PM_ADDR_ATTR_PORT] = {.type = NLA_U16},
|
||||
[MPTCP_PM_ADDR_ATTR_FLAGS] = {.type = NLA_U32},
|
||||
[MPTCP_PM_ADDR_ATTR_IF_IDX] = {.type = NLA_S32},
|
||||
};
|
||||
struct nlattr *tb[G_N_ELEMENTS(policy)];
|
||||
nm_auto_nmpobj NMPObject *obj = NULL;
|
||||
NMPlatformMptcpAddr *mptcp_addr;
|
||||
int addr_family;
|
||||
int addr_attr;
|
||||
|
||||
if (nla_parse_nested_arr(tb, attr, policy) < 0)
|
||||
return NULL;
|
||||
|
||||
if (!tb[MPTCP_PM_ADDR_ATTR_ID])
|
||||
return NULL;
|
||||
|
||||
obj = nmp_object_new(NMP_OBJECT_TYPE_MPTCP_ADDR, NULL);
|
||||
mptcp_addr = &obj->mptcp_addr;
|
||||
|
||||
mptcp_addr->id = nla_get_u8(tb[MPTCP_PM_ADDR_ATTR_ID]);
|
||||
|
||||
if (!tb[MPTCP_PM_ADDR_ATTR_FAMILY]) {
|
||||
/* If we don't have the family. Only create a stub object containing
|
||||
* the ID. */
|
||||
goto out;
|
||||
}
|
||||
|
||||
addr_family = nla_get_u16(tb[MPTCP_PM_ADDR_ATTR_FAMILY]);
|
||||
|
||||
if (addr_family == AF_INET)
|
||||
addr_attr = MPTCP_PM_ADDR_ATTR_ADDR4;
|
||||
else if (addr_family == AF_INET6)
|
||||
addr_attr = MPTCP_PM_ADDR_ATTR_ADDR6;
|
||||
else
|
||||
goto out;
|
||||
|
||||
if (!tb[addr_attr])
|
||||
goto out;
|
||||
|
||||
mptcp_addr->addr_family = addr_family;
|
||||
memcpy(&mptcp_addr->addr, nla_data(tb[addr_attr]), nm_utils_addr_family_to_size(addr_family));
|
||||
|
||||
if (tb[MPTCP_PM_ADDR_ATTR_PORT])
|
||||
mptcp_addr->port = nla_get_u16(tb[MPTCP_PM_ADDR_ATTR_PORT]);
|
||||
|
||||
if (tb[MPTCP_PM_ADDR_ATTR_IF_IDX])
|
||||
mptcp_addr->ifindex = nla_get_s32(tb[MPTCP_PM_ADDR_ATTR_IF_IDX]);
|
||||
|
||||
if (tb[MPTCP_PM_ADDR_ATTR_FLAGS])
|
||||
mptcp_addr->flags = nla_get_u32(tb[MPTCP_PM_ADDR_ATTR_FLAGS]);
|
||||
|
||||
out:
|
||||
|
||||
return g_steal_pointer(&obj);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
GPtrArray *addrs;
|
||||
} FetchMptcpAddrParseData;
|
||||
|
||||
static int
|
||||
_mptcp_addrs_dump_parse_cb(const struct nl_msg *msg, void *arg)
|
||||
{
|
||||
static const struct nla_policy policy[] = {
|
||||
[MPTCP_PM_ATTR_ADDR] = {.type = NLA_NESTED},
|
||||
};
|
||||
struct nlattr *tb[G_N_ELEMENTS(policy)];
|
||||
FetchMptcpAddrParseData *parse_data = arg;
|
||||
|
||||
if (genlmsg_parse_arr(nlmsg_hdr(msg), 0, tb, policy) < 0)
|
||||
return NL_SKIP;
|
||||
|
||||
if (tb[MPTCP_PM_ATTR_ADDR]) {
|
||||
const NMPObject *obj;
|
||||
|
||||
obj = _mptcp_addrs_dump_parse_addr(tb[MPTCP_PM_ATTR_ADDR]);
|
||||
if (obj)
|
||||
g_ptr_array_add(parse_data->addrs, (gpointer) obj);
|
||||
}
|
||||
|
||||
return NL_OK;
|
||||
}
|
||||
|
||||
#define EXTACK_MSG_BUFSIZE 200
|
||||
|
||||
static int
|
||||
_mptcp_addr_update_err_cb(const struct sockaddr_nl *nla, const struct nlmsgerr *nlerr, void *arg)
|
||||
{
|
||||
char *out_extack_msg = arg;
|
||||
const char *extack_msg;
|
||||
int errsv;
|
||||
|
||||
errsv = nlmsg_parse_error(nlmsg_undata(nlerr), &extack_msg);
|
||||
if (extack_msg)
|
||||
g_strlcpy(out_extack_msg, extack_msg, EXTACK_MSG_BUFSIZE);
|
||||
return errsv;
|
||||
}
|
||||
|
||||
static int
|
||||
mptcp_addr_update(NMPlatform *platform, NMOptionBool add, const NMPlatformMptcpAddr *addr)
|
||||
{
|
||||
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
|
||||
nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
|
||||
struct nlattr *nla_nest;
|
||||
char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
|
||||
guint16 genl_family_id;
|
||||
guint8 cmd_num;
|
||||
const char *cmd_str;
|
||||
int nle;
|
||||
char extack_msg[EXTACK_MSG_BUFSIZE];
|
||||
const struct nl_cb cb = {
|
||||
.err_cb = _mptcp_addr_update_err_cb,
|
||||
.err_arg = extack_msg,
|
||||
};
|
||||
|
||||
extack_msg[0] = '\0';
|
||||
|
||||
if (add == NM_OPTION_BOOL_DEFAULT) {
|
||||
cmd_num = MPTCP_PM_CMD_SET_FLAGS;
|
||||
cmd_str = "update";
|
||||
} else if (add) {
|
||||
cmd_num = MPTCP_PM_CMD_ADD_ADDR;
|
||||
cmd_str = "add";
|
||||
} else {
|
||||
cmd_num = MPTCP_PM_CMD_DEL_ADDR;
|
||||
cmd_str = "delete";
|
||||
}
|
||||
|
||||
nm_assert_addr_family_or_unspec(addr->addr_family);
|
||||
nm_assert(cmd_num != MPTCP_PM_CMD_ADD_ADDR
|
||||
|| (addr->port == 0 && addr->addr_family != AF_UNSPEC));
|
||||
nm_assert(cmd_num != MPTCP_PM_CMD_SET_FLAGS || (addr->id != 0 && addr->port == 0));
|
||||
nm_assert(cmd_num != MPTCP_PM_CMD_DEL_ADDR || addr->id != 0);
|
||||
|
||||
genl_family_id = nm_platform_genl_get_family_id(platform, NMP_GENL_FAMILY_TYPE_MPTCP_PM);
|
||||
if (genl_family_id == 0) {
|
||||
_LOGT("mptcp: %s address %s fails because %s generic netlink family is unknown",
|
||||
cmd_str,
|
||||
nm_platform_mptcp_addr_to_string(addr, sbuf, sizeof(sbuf)),
|
||||
nmp_genl_family_infos[NMP_GENL_FAMILY_TYPE_MPTCP_PM].name);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
_LOGT("mptcp: %s address %s",
|
||||
cmd_str,
|
||||
nm_platform_mptcp_addr_to_string(addr, sbuf, sizeof(sbuf)));
|
||||
|
||||
nlmsg = nlmsg_alloc_size(nlmsg_total_size(GENL_HDRLEN) + 200);
|
||||
|
||||
if (!genlmsg_put(nlmsg,
|
||||
NL_AUTO_PORT,
|
||||
NL_AUTO_SEQ,
|
||||
genl_family_id,
|
||||
0,
|
||||
NLM_F_REQUEST,
|
||||
cmd_num,
|
||||
MPTCP_PM_VER))
|
||||
goto nla_put_failure;
|
||||
|
||||
nla_nest = nla_nest_start(nlmsg, MPTCP_PM_ATTR_ADDR | NLA_F_NESTED);
|
||||
if (!nla_nest)
|
||||
goto nla_put_failure;
|
||||
|
||||
if (addr->id != 0)
|
||||
NLA_PUT_U8(nlmsg, MPTCP_PM_ADDR_ATTR_ID, addr->id);
|
||||
if (addr->flags != 0)
|
||||
NLA_PUT_U32(nlmsg, MPTCP_PM_ADDR_ATTR_FLAGS, addr->flags);
|
||||
if (addr->ifindex != 0)
|
||||
NLA_PUT_S32(nlmsg, MPTCP_PM_ADDR_ATTR_IF_IDX, addr->ifindex);
|
||||
if (addr->port != 0)
|
||||
NLA_PUT_U16(nlmsg, MPTCP_PM_ADDR_ATTR_PORT, addr->port);
|
||||
if (addr->addr_family != AF_UNSPEC) {
|
||||
int addr_attr;
|
||||
|
||||
NLA_PUT_U16(nlmsg, MPTCP_PM_ADDR_ATTR_FAMILY, addr->addr_family);
|
||||
if (addr->addr_family == AF_INET)
|
||||
addr_attr = MPTCP_PM_ADDR_ATTR_ADDR4;
|
||||
else if (addr->addr_family == AF_INET6)
|
||||
addr_attr = MPTCP_PM_ADDR_ATTR_ADDR6;
|
||||
else
|
||||
g_return_val_if_reached(-NME_BUG);
|
||||
NLA_PUT(nlmsg, addr_attr, nm_utils_addr_family_to_size(addr->addr_family), &addr->addr);
|
||||
}
|
||||
|
||||
NLA_NEST_END(nlmsg, nla_nest);
|
||||
|
||||
nle = nl_send_auto(priv->sk_genl_sync, nlmsg);
|
||||
if (nle < 0) {
|
||||
_LOGT("mptcp: %s address %s: failed sending request: %s (%d)",
|
||||
cmd_str,
|
||||
nm_platform_mptcp_addr_to_string(addr, sbuf, sizeof(sbuf)),
|
||||
nm_strerror(nle),
|
||||
nle);
|
||||
return nle;
|
||||
}
|
||||
|
||||
do {
|
||||
nle = nl_recvmsgs(priv->sk_genl_sync, &cb);
|
||||
} while (nle == -EAGAIN);
|
||||
|
||||
if (nle < 0) {
|
||||
_LOGT("mptcp: %s address %s: failed: %s (%d)%s%s%s",
|
||||
cmd_str,
|
||||
nm_platform_mptcp_addr_to_string(addr, sbuf, sizeof(sbuf)),
|
||||
nm_strerror(nle),
|
||||
nle,
|
||||
NM_PRINT_FMT_QUOTED(extack_msg[0] != '\0', " \"", extack_msg, "\"", ""));
|
||||
return nle;
|
||||
}
|
||||
|
||||
_LOGT("mptcp: %s address %s: success",
|
||||
cmd_str,
|
||||
nm_platform_mptcp_addr_to_string(addr, sbuf, sizeof(sbuf)));
|
||||
|
||||
return 0;
|
||||
|
||||
nla_put_failure:
|
||||
g_return_val_if_reached(-NME_BUG);
|
||||
}
|
||||
|
||||
static GPtrArray *
|
||||
mptcp_addrs_dump(NMPlatform *platform)
|
||||
{
|
||||
NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE(platform);
|
||||
gs_unref_ptrarray GPtrArray *addrs = NULL;
|
||||
nm_auto_nlmsg struct nl_msg *nlmsg = NULL;
|
||||
FetchMptcpAddrParseData parse_data;
|
||||
guint16 genl_family_id;
|
||||
int r;
|
||||
guint i;
|
||||
|
||||
genl_family_id = nm_platform_genl_get_family_id(platform, NMP_GENL_FAMILY_TYPE_MPTCP_PM);
|
||||
if (genl_family_id == 0) {
|
||||
_LOGT("mptcp: dump addresses fails because %s generic netlink family is unknown",
|
||||
nmp_genl_family_infos[NMP_GENL_FAMILY_TYPE_MPTCP_PM].name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nlmsg = nlmsg_alloc_size(nlmsg_total_size(GENL_HDRLEN));
|
||||
|
||||
if (!genlmsg_put(nlmsg,
|
||||
NL_AUTO_PORT,
|
||||
NL_AUTO_SEQ,
|
||||
genl_family_id,
|
||||
0,
|
||||
NLM_F_REQUEST | NLM_F_DUMP,
|
||||
MPTCP_PM_CMD_GET_ADDR,
|
||||
MPTCP_PM_VER))
|
||||
g_return_val_if_reached(NULL);
|
||||
|
||||
r = nl_send_auto(priv->sk_genl_sync, nlmsg);
|
||||
if (r < 0) {
|
||||
_LOGT("mptcp: dump addresses failed to send dump request: %s", nm_strerror(r));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
addrs = g_ptr_array_new_with_free_func((GDestroyNotify) nmp_object_unref);
|
||||
|
||||
parse_data = (FetchMptcpAddrParseData){
|
||||
.addrs = addrs,
|
||||
};
|
||||
|
||||
nl_recvmsgs(priv->sk_genl_sync,
|
||||
&((const struct nl_cb){
|
||||
.valid_cb = _mptcp_addrs_dump_parse_cb,
|
||||
.valid_arg = (gpointer) &parse_data,
|
||||
}));
|
||||
|
||||
if (_LOGT_ENABLED()) {
|
||||
_LOGT("mptcp: %u addresses dumped", addrs->len);
|
||||
for (i = 0; i < addrs->len; i++) {
|
||||
char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
|
||||
|
||||
_LOGT("mptcp: address[%04d]: %s",
|
||||
i,
|
||||
nmp_object_to_string(addrs->pdata[i],
|
||||
NMP_OBJECT_TO_STRING_PUBLIC,
|
||||
sbuf,
|
||||
sizeof(sbuf)));
|
||||
}
|
||||
}
|
||||
|
||||
return g_steal_pointer(&addrs);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static void
|
||||
cache_update_link_udev(NMPlatform *platform, int ifindex, struct udev_device *udevice)
|
||||
{
|
||||
|
|
@ -10628,4 +10887,6 @@ nm_linux_platform_class_init(NMLinuxPlatformClass *klass)
|
|||
platform_class->process_events = process_events;
|
||||
|
||||
platform_class->genl_get_family_id = genl_get_family_id;
|
||||
platform_class->mptcp_addr_update = mptcp_addr_update;
|
||||
platform_class->mptcp_addrs_dump = mptcp_addrs_dump;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ flags_done:
|
|||
/*****************************************************************************/
|
||||
|
||||
struct nlmsghdr *
|
||||
nlmsg_hdr(struct nl_msg *n)
|
||||
nlmsg_hdr(const struct nl_msg *n)
|
||||
{
|
||||
return n->nm_nlh;
|
||||
}
|
||||
|
|
@ -291,6 +291,52 @@ nlmsg_reserve(struct nl_msg *n, uint32_t len, uint32_t pad)
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
int
|
||||
nlmsg_parse_error(const struct nlmsghdr *nlh, const char **out_extack_msg)
|
||||
{
|
||||
const struct nlmsgerr *e;
|
||||
|
||||
nm_assert(nlh);
|
||||
|
||||
NM_SET_OUT(out_extack_msg, NULL);
|
||||
|
||||
if (nlh->nlmsg_type != NLMSG_ERROR)
|
||||
return -NME_NL_MSG_INVAL;
|
||||
|
||||
if (nlh->nlmsg_len < nlmsg_size(sizeof(struct nlmsgerr))) {
|
||||
/* Truncated error message, the default action
|
||||
* is to stop parsing. The user may overrule
|
||||
* this action by returning NL_SKIP or
|
||||
* NL_PROCEED (dangerous) */
|
||||
return -NME_NL_MSG_TRUNC;
|
||||
}
|
||||
|
||||
e = nlmsg_data(nlh);
|
||||
|
||||
if (!e->error)
|
||||
return 0;
|
||||
|
||||
if (NM_FLAGS_HAS(nlh->nlmsg_flags, NLM_F_ACK_TLVS) && out_extack_msg
|
||||
&& nlh->nlmsg_len >= sizeof(*e) + e->msg.nlmsg_len) {
|
||||
static const struct nla_policy policy[] = {
|
||||
[NLMSGERR_ATTR_MSG] = {.type = NLA_STRING},
|
||||
[NLMSGERR_ATTR_OFFS] = {.type = NLA_U32},
|
||||
};
|
||||
struct nlattr *tb[G_N_ELEMENTS(policy)];
|
||||
struct nlattr *tlvs;
|
||||
|
||||
tlvs = (struct nlattr *) ((char *) e + sizeof(*e) + e->msg.nlmsg_len - NLMSG_HDRLEN);
|
||||
if (nla_parse_arr(tb, tlvs, nlh->nlmsg_len - sizeof(*e) - e->msg.nlmsg_len, policy) >= 0) {
|
||||
if (tb[NLMSGERR_ATTR_MSG])
|
||||
*out_extack_msg = nla_get_string(tb[NLMSGERR_ATTR_MSG]);
|
||||
}
|
||||
}
|
||||
|
||||
return -nm_errno_from_native(e->error);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
struct nlattr *
|
||||
nla_reserve(struct nl_msg *msg, int attrtype, int attrlen)
|
||||
{
|
||||
|
|
@ -856,7 +902,7 @@ const struct nla_policy genl_ctrl_policy[CTRL_ATTR_MCAST_GROUPS + 1] = {
|
|||
};
|
||||
|
||||
static int
|
||||
_genl_parse_getfamily(struct nl_msg *msg, void *arg)
|
||||
_genl_parse_getfamily(const struct nl_msg *msg, void *arg)
|
||||
{
|
||||
struct nlattr *tb[G_N_ELEMENTS(genl_ctrl_policy)];
|
||||
struct nlmsghdr *nlh = nlmsg_hdr(msg);
|
||||
|
|
@ -1155,7 +1201,7 @@ _cb_init(struct nl_cb *dst, const struct nl_cb *src)
|
|||
}
|
||||
|
||||
static int
|
||||
ack_wait_handler(struct nl_msg *msg, void *arg)
|
||||
ack_wait_handler(const struct nl_msg *msg, void *arg)
|
||||
{
|
||||
return NL_STOP;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -354,6 +354,14 @@ void nla_nest_cancel(struct nl_msg *msg, const struct nlattr *attr);
|
|||
struct nlattr *nla_nest_start(struct nl_msg *msg, int attrtype);
|
||||
int nla_nest_end(struct nl_msg *msg, struct nlattr *start);
|
||||
|
||||
#define NLA_NEST_END(msg, nest_start) \
|
||||
G_STMT_START \
|
||||
{ \
|
||||
if (nla_nest_end((msg), (nest_start)) < 0) \
|
||||
goto nla_put_failure; \
|
||||
} \
|
||||
G_STMT_END
|
||||
|
||||
int nla_parse(struct nlattr *tb[],
|
||||
int maxtype,
|
||||
struct nlattr *head,
|
||||
|
|
@ -442,19 +450,26 @@ void nlmsg_set_creds(struct nl_msg *msg, struct ucred *creds);
|
|||
NM_AUTO_DEFINE_FCN0(struct nl_msg *, _nm_auto_nl_msg_cleanup, nlmsg_free);
|
||||
#define nm_auto_nlmsg nm_auto(_nm_auto_nl_msg_cleanup)
|
||||
|
||||
static inline const struct nlmsghdr *
|
||||
nlmsg_undata(const void *data)
|
||||
{
|
||||
/* from the data, get back the header. It's the inverse of nlmsg_data(). */
|
||||
return (void *) (((unsigned char *) data) - NLMSG_HDRLEN);
|
||||
}
|
||||
|
||||
static inline void *
|
||||
nlmsg_data(const struct nlmsghdr *nlh)
|
||||
{
|
||||
return (unsigned char *) nlh + NLMSG_HDRLEN;
|
||||
return ((unsigned char *) nlh) + NLMSG_HDRLEN;
|
||||
}
|
||||
|
||||
static inline void *
|
||||
nlmsg_tail(const struct nlmsghdr *nlh)
|
||||
{
|
||||
return (unsigned char *) nlh + NLMSG_ALIGN(nlh->nlmsg_len);
|
||||
return ((unsigned char *) nlh) + NLMSG_ALIGN(nlh->nlmsg_len);
|
||||
}
|
||||
|
||||
struct nlmsghdr *nlmsg_hdr(struct nl_msg *n);
|
||||
struct nlmsghdr *nlmsg_hdr(const struct nl_msg *n);
|
||||
|
||||
static inline int
|
||||
nlmsg_valid_hdr(const struct nlmsghdr *nlh, int hdrlen)
|
||||
|
|
@ -490,6 +505,8 @@ nlmsg_find_attr(struct nlmsghdr *nlh, int hdrlen, int attrtype)
|
|||
return nla_find(nlmsg_attrdata(nlh, hdrlen), nlmsg_attrlen(nlh, hdrlen), attrtype);
|
||||
}
|
||||
|
||||
int nlmsg_parse_error(const struct nlmsghdr *nlh, const char **out_extack_msg);
|
||||
|
||||
int nlmsg_parse(const struct nlmsghdr *nlh,
|
||||
int hdrlen,
|
||||
struct nlattr *tb[],
|
||||
|
|
@ -585,9 +602,11 @@ enum nl_cb_action {
|
|||
NL_STOP,
|
||||
};
|
||||
|
||||
typedef int (*nl_recvmsg_msg_cb_t)(struct nl_msg *msg, void *arg);
|
||||
typedef int (*nl_recvmsg_msg_cb_t)(const struct nl_msg *msg, void *arg);
|
||||
|
||||
typedef int (*nl_recvmsg_err_cb_t)(struct sockaddr_nl *nla, struct nlmsgerr *nlerr, void *arg);
|
||||
typedef int (*nl_recvmsg_err_cb_t)(const struct sockaddr_nl *nla,
|
||||
const struct nlmsgerr *nlerr,
|
||||
void *arg);
|
||||
|
||||
struct nl_cb {
|
||||
nl_recvmsg_msg_cb_t valid_cb;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#include "nm-platform.h"
|
||||
|
||||
#include "libnm-std-aux/nm-linux-compat.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <netinet/in.h>
|
||||
|
|
@ -402,7 +404,7 @@ const NMPGenlFamilyInfo nmp_genl_family_infos[_NMP_GENL_FAMILY_TYPE_NUM] = {
|
|||
},
|
||||
[NMP_GENL_FAMILY_TYPE_MPTCP_PM] =
|
||||
{
|
||||
.name = "mptcp_pm",
|
||||
.name = MPTCP_PM_NAME,
|
||||
},
|
||||
[NMP_GENL_FAMILY_TYPE_NL80211] =
|
||||
{
|
||||
|
|
@ -5320,23 +5322,26 @@ nm_platform_object_delete(NMPlatform *self, const NMPObject *obj)
|
|||
|
||||
_CHECK_SELF(self, klass, FALSE);
|
||||
|
||||
switch (NMP_OBJECT_GET_TYPE(obj)) {
|
||||
case NMP_OBJECT_TYPE_ROUTING_RULE:
|
||||
_LOGD("%s: delete %s",
|
||||
NMP_OBJECT_GET_CLASS(obj)->obj_type_name,
|
||||
nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf)));
|
||||
break;
|
||||
case NMP_OBJECT_TYPE_IP4_ROUTE:
|
||||
case NMP_OBJECT_TYPE_IP6_ROUTE:
|
||||
case NMP_OBJECT_TYPE_QDISC:
|
||||
case NMP_OBJECT_TYPE_TFILTER:
|
||||
ifindex = NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj)->ifindex;
|
||||
_LOG3D("%s: delete %s",
|
||||
NMP_OBJECT_GET_CLASS(obj)->obj_type_name,
|
||||
nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf)));
|
||||
break;
|
||||
default:
|
||||
g_return_val_if_reached(FALSE);
|
||||
if (_LOGD_ENABLED()) {
|
||||
switch (NMP_OBJECT_GET_TYPE(obj)) {
|
||||
case NMP_OBJECT_TYPE_ROUTING_RULE:
|
||||
case NMP_OBJECT_TYPE_MPTCP_ADDR:
|
||||
_LOGD("%s: delete %s",
|
||||
NMP_OBJECT_GET_CLASS(obj)->obj_type_name,
|
||||
nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf)));
|
||||
break;
|
||||
case NMP_OBJECT_TYPE_IP4_ROUTE:
|
||||
case NMP_OBJECT_TYPE_IP6_ROUTE:
|
||||
case NMP_OBJECT_TYPE_QDISC:
|
||||
case NMP_OBJECT_TYPE_TFILTER:
|
||||
ifindex = NMP_OBJECT_CAST_OBJ_WITH_IFINDEX(obj)->ifindex;
|
||||
_LOG3D("%s: delete %s",
|
||||
NMP_OBJECT_GET_CLASS(obj)->obj_type_name,
|
||||
nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf)));
|
||||
break;
|
||||
default:
|
||||
g_return_val_if_reached(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
return klass->object_delete(self, obj);
|
||||
|
|
@ -7761,14 +7766,12 @@ nm_platform_mptcp_addr_to_string(const NMPlatformMptcpAddr *mptcp_addr, char *bu
|
|||
|
||||
g_snprintf(buf,
|
||||
len,
|
||||
"%s" /* in_kernel */
|
||||
"%s" /* address */
|
||||
"%s" /* port */
|
||||
"%s" /* id */
|
||||
"%s" /* flags */
|
||||
"%s" /* ifindex */
|
||||
"",
|
||||
mptcp_addr->in_kernel ? "" : "[nm] ",
|
||||
str_addr,
|
||||
mptcp_addr->port == 0 ? "" : nm_sprintf_buf(str_port, " port %u", mptcp_addr->port),
|
||||
mptcp_addr->id == 0 ? "" : nm_sprintf_buf(str_id, " id %u", mptcp_addr->id),
|
||||
|
|
@ -7785,13 +7788,7 @@ nm_platform_mptcp_addr_hash_update(const NMPlatformMptcpAddr *obj, NMHashState *
|
|||
nm_assert(obj);
|
||||
nm_assert_addr_family_or_unspec(obj->addr_family);
|
||||
|
||||
nm_hash_update_vals(h,
|
||||
obj->id,
|
||||
obj->flags,
|
||||
obj->port,
|
||||
obj->addr_family,
|
||||
(bool) obj->in_kernel,
|
||||
obj->ifindex);
|
||||
nm_hash_update_vals(h, obj->id, obj->flags, obj->port, obj->addr_family, obj->ifindex);
|
||||
if (NM_IN_SET(obj->addr_family, AF_INET, AF_INET6))
|
||||
nm_hash_update(h, &obj->addr, nm_utils_addr_family_to_size(obj->addr_family));
|
||||
}
|
||||
|
|
@ -7804,16 +7801,38 @@ nm_platform_mptcp_addr_cmp(const NMPlatformMptcpAddr *a, const NMPlatformMptcpAd
|
|||
nm_assert_addr_family_or_unspec(a->addr_family);
|
||||
nm_assert_addr_family_or_unspec(b->addr_family);
|
||||
|
||||
NM_CMP_FIELD(a, b, ifindex);
|
||||
NM_CMP_FIELD(a, b, id);
|
||||
NM_CMP_FIELD_UNSAFE(a, b, in_kernel);
|
||||
NM_CMP_FIELD(a, b, addr_family);
|
||||
if (NM_IN_SET(a->addr_family, AF_INET, AF_INET6))
|
||||
NM_CMP_FIELD_MEMCMP_LEN(a, b, addr, nm_utils_addr_family_to_size(a->addr_family));
|
||||
NM_CMP_FIELD(a, b, ifindex);
|
||||
NM_CMP_FIELD(a, b, port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
guint
|
||||
nm_platform_mptcp_addr_index_addr_cmp(gconstpointer data)
|
||||
{
|
||||
const NMPlatformMptcpAddr *mptcp_addr = data;
|
||||
NMHashState h;
|
||||
|
||||
nm_hash_init(&h, 1408914077u);
|
||||
nm_hash_update_val(&h, mptcp_addr->addr_family);
|
||||
nm_hash_update(&h, &mptcp_addr->addr, nm_utils_addr_family_to_size(mptcp_addr->addr_family));
|
||||
return nm_hash_complete(&h);
|
||||
}
|
||||
|
||||
gboolean
|
||||
nm_platform_mptcp_addr_index_addr_equal(gconstpointer data_a, gconstpointer data_b)
|
||||
{
|
||||
const NMPlatformMptcpAddr *mptcp_addr_a = data_a;
|
||||
const NMPlatformMptcpAddr *mptcp_addr_b = data_b;
|
||||
|
||||
return mptcp_addr_a->addr_family == mptcp_addr_b->addr_family
|
||||
&& nm_ip_addr_equal(mptcp_addr_a->addr_family, &mptcp_addr_a->addr, &mptcp_addr_b->addr);
|
||||
}
|
||||
|
||||
const char *
|
||||
nm_platform_vf_to_string(const NMPlatformVF *vf, char *buf, gsize len)
|
||||
{
|
||||
|
|
@ -9379,6 +9398,24 @@ nm_platform_genl_get_family_id(NMPlatform *self, NMPGenlFamilyType family_type)
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
int
|
||||
nm_platform_mptcp_addr_update(NMPlatform *self, NMOptionBool add, const NMPlatformMptcpAddr *addr)
|
||||
{
|
||||
_CHECK_SELF(self, klass, -NME_BUG);
|
||||
|
||||
return klass->mptcp_addr_update(self, add, addr);
|
||||
}
|
||||
|
||||
GPtrArray *
|
||||
nm_platform_mptcp_addrs_dump(NMPlatform *self)
|
||||
{
|
||||
_CHECK_SELF(self, klass, NULL);
|
||||
|
||||
return klass->mptcp_addrs_dump(self);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
GHashTable *
|
||||
nm_platform_ip4_address_addr_to_hash(NMPlatform *self, int ifindex)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1098,21 +1098,11 @@ typedef void (*NMPlatformAsyncCallback)(GError *error, gpointer user_data);
|
|||
|
||||
typedef struct {
|
||||
__NMPlatformObjWithIfindex_COMMON;
|
||||
|
||||
guint32 id;
|
||||
guint32 flags;
|
||||
guint16 port;
|
||||
NMIPAddr addr;
|
||||
gint8 addr_family;
|
||||
|
||||
/* If TRUE, then the instance was received by kernel and is inside NMPlatform
|
||||
* cache. In that case, the "id" is set and acts as primary key for the instance.
|
||||
*
|
||||
* If FALSE, this instance is not yet configured in kernel. In this case,
|
||||
* the tuple (id, addr_family, addr) is the primary key of the instance.
|
||||
* This way, we can track mptcp addresses in NetworkManager internally,
|
||||
* before configuring them in kernel. */
|
||||
bool in_kernel : 1;
|
||||
} NMPlatformMptcpAddr;
|
||||
|
||||
#undef __NMPlatformObjWithIfindex_COMMON
|
||||
|
|
@ -1404,6 +1394,10 @@ typedef struct {
|
|||
|
||||
guint16 (*genl_get_family_id)(NMPlatform *platform, NMPGenlFamilyType family_type);
|
||||
|
||||
int (*mptcp_addr_update)(NMPlatform *self, NMOptionBool add, const NMPlatformMptcpAddr *addr);
|
||||
|
||||
GPtrArray *(*mptcp_addrs_dump)(NMPlatform *self);
|
||||
|
||||
} NMPlatformClass;
|
||||
|
||||
/* NMPlatform signals
|
||||
|
|
@ -1617,6 +1611,18 @@ nm_platform_ip6_address_get_scope(const struct in6_addr *addr)
|
|||
static inline guint8
|
||||
nm_platform_ip_address_get_scope(int addr_family, gconstpointer addr)
|
||||
{
|
||||
/* Note that this function returns the scope as we configure
|
||||
* it in kernel (for IPv4) or as kernel chooses it (for IPv6).
|
||||
*
|
||||
* That means, rfc1918 private addresses nm_utils_ip_is_site_local() are
|
||||
* considered RT_SCOPE_UNIVERSE.
|
||||
*
|
||||
* Also, the deprecated IN6_IS_ADDR_SITELOCAL() addresses (fec0::/10)
|
||||
* are considered RT_SCOPE_SITE, while unique local addresses (ULA, fc00::/7)
|
||||
* are considered RT_SCOPE_UNIVERSE.
|
||||
*
|
||||
* You may not want to use this function when reasoning about
|
||||
* site-local addresses (RFC1918, ULA). */
|
||||
if (NM_IS_IPv4(addr_family))
|
||||
return nm_platform_ip4_address_get_scope(*((in_addr_t *) addr));
|
||||
return nm_platform_ip6_address_get_scope(addr);
|
||||
|
|
@ -2587,6 +2593,9 @@ void nm_platform_tfilter_hash_update(const NMPlatformTfilter *obj, NMHashState *
|
|||
|
||||
void nm_platform_mptcp_addr_hash_update(const NMPlatformMptcpAddr *obj, NMHashState *h);
|
||||
|
||||
guint nm_platform_mptcp_addr_index_addr_cmp(gconstpointer data);
|
||||
gboolean nm_platform_mptcp_addr_index_addr_equal(gconstpointer data_a, gconstpointer data_b);
|
||||
|
||||
#define NM_PLATFORM_LINK_FLAGS2STR_MAX_LEN ((gsize) 162)
|
||||
|
||||
const char *nm_platform_link_flags2str(unsigned flags, char *buf, gsize len);
|
||||
|
|
@ -2663,4 +2672,9 @@ gboolean nm_platform_ip_address_match(int addr_family,
|
|||
|
||||
guint16 nm_platform_genl_get_family_id(NMPlatform *self, NMPGenlFamilyType family_type);
|
||||
|
||||
int
|
||||
nm_platform_mptcp_addr_update(NMPlatform *self, NMOptionBool add, const NMPlatformMptcpAddr *addr);
|
||||
|
||||
GPtrArray *nm_platform_mptcp_addrs_dump(NMPlatform *self);
|
||||
|
||||
#endif /* __NETWORKMANAGER_PLATFORM_H__ */
|
||||
|
|
|
|||
|
|
@ -11,6 +11,10 @@
|
|||
#include "libnm-std-aux/c-list-util.h"
|
||||
#include "nmp-object.h"
|
||||
|
||||
/* This limit comes from kernel, and it limits the number of MPTCP addresses
|
||||
* we can configure. */
|
||||
#define MPTCP_PM_ADDR_MAX 8
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* NMPGlobalTracker tracks certain objects for the entire network namespace and can
|
||||
|
|
@ -48,7 +52,7 @@ struct _NMPGlobalTracker {
|
|||
GHashTable *by_obj;
|
||||
GHashTable *by_user_tag;
|
||||
GHashTable *by_data;
|
||||
CList by_obj_lst_heads[3];
|
||||
CList by_obj_lst_heads[4];
|
||||
guint ref_count;
|
||||
};
|
||||
|
||||
|
|
@ -93,6 +97,9 @@ typedef struct {
|
|||
guint32 track_priority_val;
|
||||
bool track_priority_present : 1;
|
||||
|
||||
/* Calling nmp_global_tracker_track() will ensure that the tracked entry is
|
||||
* non-dirty. Together with nmp_global_tracker_set_dirty() and nmp_global_tracker_untrack_all()'s
|
||||
* @all parameter, this can be used to remove stale entries. */
|
||||
bool dirty : 1;
|
||||
} TrackData;
|
||||
|
||||
|
|
@ -149,7 +156,7 @@ static void _track_data_untrack(NMPGlobalTracker *self,
|
|||
static CList *
|
||||
_by_obj_lst_head(NMPGlobalTracker *self, NMPObjectType obj_type)
|
||||
{
|
||||
G_STATIC_ASSERT(G_N_ELEMENTS(self->by_obj_lst_heads) == 3);
|
||||
G_STATIC_ASSERT(G_N_ELEMENTS(self->by_obj_lst_heads) == 4);
|
||||
|
||||
switch (obj_type) {
|
||||
case NMP_OBJECT_TYPE_IP4_ROUTE:
|
||||
|
|
@ -158,6 +165,8 @@ _by_obj_lst_head(NMPGlobalTracker *self, NMPObjectType obj_type)
|
|||
return &self->by_obj_lst_heads[1];
|
||||
case NMP_OBJECT_TYPE_ROUTING_RULE:
|
||||
return &self->by_obj_lst_heads[2];
|
||||
case NMP_OBJECT_TYPE_MPTCP_ADDR:
|
||||
return &self->by_obj_lst_heads[3];
|
||||
default:
|
||||
return nm_assert_unreachable_val(NULL);
|
||||
}
|
||||
|
|
@ -172,7 +181,8 @@ _track_data_assert(const TrackData *track_data, gboolean linked)
|
|||
nm_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(track_data->obj),
|
||||
NMP_OBJECT_TYPE_IP4_ROUTE,
|
||||
NMP_OBJECT_TYPE_IP6_ROUTE,
|
||||
NMP_OBJECT_TYPE_ROUTING_RULE));
|
||||
NMP_OBJECT_TYPE_ROUTING_RULE,
|
||||
NMP_OBJECT_TYPE_MPTCP_ADDR));
|
||||
nm_assert(nmp_object_is_visible(track_data->obj));
|
||||
nm_assert(track_data->user_tag);
|
||||
nm_assert(!linked || !c_list_is_empty(&track_data->obj_lst));
|
||||
|
|
@ -296,13 +306,42 @@ _track_data_lookup(GHashTable *by_data, const NMPObject *obj, gconstpointer user
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
static const NMPObject *
|
||||
_obj_stackinit(NMPObject *obj_stack, NMPObjectType obj_type, gconstpointer obj)
|
||||
{
|
||||
nmp_object_stackinit(obj_stack, obj_type, obj);
|
||||
|
||||
if (NM_MORE_ASSERTS > 10) {
|
||||
if (obj_type == NMP_OBJECT_TYPE_MPTCP_ADDR) {
|
||||
NMPlatformMptcpAddr *m = NMP_OBJECT_CAST_MPTCP_ADDR(obj_stack);
|
||||
NMPlatformMptcpAddr m_dummy;
|
||||
|
||||
/* Only certain MPTCP addresses can be added. */
|
||||
nm_assert(m->ifindex > 0);
|
||||
if (nm_platform_mptcp_addr_cmp(
|
||||
nmp_global_tracker_mptcp_addr_init_for_ifindex(&m_dummy, m->ifindex),
|
||||
m)
|
||||
== 0) {
|
||||
/* This is a dummy instance. We are good. */
|
||||
} else {
|
||||
nm_assert_addr_family(m->addr_family);
|
||||
nm_assert(m->port == 0);
|
||||
nm_assert(m->id == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nm_assert(nmp_object_is_visible(obj_stack));
|
||||
return obj_stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* nmp_global_tracker_track:
|
||||
* @self: the #NMPGlobalTracker instance
|
||||
* @obj_type: the NMPObjectType of @obj that we are tracking.
|
||||
* @obj: the NMPlatformObject (of type NMPObjectType) to track. Usually
|
||||
* a #NMPlatformRoutingRule, #NMPlatformIP4Route or #NMPlatformIP6Route
|
||||
* pointer.
|
||||
* a #NMPlatformRoutingRule, #NMPlatformIP4Route, #NMPlatformIP6Route
|
||||
* or #NMPlatformMptcpAddr pointer.
|
||||
* @track_priority: the priority for tracking the rule. Note that
|
||||
* negative values indicate a forced absence of the rule. Priorities
|
||||
* are compared with their absolute values (with higher absolute
|
||||
|
|
@ -345,16 +384,15 @@ nmp_global_tracker_track(NMPGlobalTracker *self,
|
|||
/* The route must not be tied to an interface. We can only handle here
|
||||
* blackhole/unreachable/prohibit route types. */
|
||||
g_return_val_if_fail(
|
||||
obj_type == NMP_OBJECT_TYPE_ROUTING_RULE
|
||||
NM_IN_SET(obj_type, NMP_OBJECT_TYPE_ROUTING_RULE, NMP_OBJECT_TYPE_MPTCP_ADDR)
|
||||
|| (NM_IN_SET(obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)
|
||||
&& ((const NMPlatformIPRoute *) obj)->ifindex == 0),
|
||||
FALSE);
|
||||
|
||||
nm_assert(track_priority != G_MININT32);
|
||||
/* only positive track priorities are implemented for MPTCP addrs. */
|
||||
nm_assert(obj_type != NMP_OBJECT_TYPE_MPTCP_ADDR || track_priority > 0);
|
||||
|
||||
p_obj_stack = nmp_object_stackinit(&obj_stack, obj_type, obj);
|
||||
|
||||
nm_assert(nmp_object_is_visible(p_obj_stack));
|
||||
p_obj_stack = _obj_stackinit(&obj_stack, obj_type, obj);
|
||||
|
||||
if (track_priority >= 0) {
|
||||
track_priority_val = track_priority;
|
||||
|
|
@ -512,13 +550,12 @@ nmp_global_tracker_untrack(NMPGlobalTracker *self,
|
|||
nm_assert(NM_IN_SET(obj_type,
|
||||
NMP_OBJECT_TYPE_IP4_ROUTE,
|
||||
NMP_OBJECT_TYPE_IP6_ROUTE,
|
||||
NMP_OBJECT_TYPE_ROUTING_RULE));
|
||||
NMP_OBJECT_TYPE_ROUTING_RULE,
|
||||
NMP_OBJECT_TYPE_MPTCP_ADDR));
|
||||
g_return_val_if_fail(obj, FALSE);
|
||||
g_return_val_if_fail(user_tag, FALSE);
|
||||
|
||||
p_obj_stack = nmp_object_stackinit(&obj_stack, obj_type, obj);
|
||||
|
||||
nm_assert(nmp_object_is_visible(p_obj_stack));
|
||||
p_obj_stack = _obj_stackinit(&obj_stack, obj_type, obj);
|
||||
|
||||
track_data = _track_data_lookup(self->by_data, p_obj_stack, user_tag);
|
||||
if (track_data) {
|
||||
|
|
@ -584,12 +621,335 @@ nmp_global_tracker_untrack_all(NMPGlobalTracker *self,
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Usually, we track NMPlatformMptcpAddr instances with an ifindex set.
|
||||
* If we have *any* such instance, we know that the ifindex is fully
|
||||
* synched (meaning, we will delete all unknown endpoints for that interface).
|
||||
* However, if we don't have an endpoint on the interface, we may still
|
||||
* want to track that a certain ifindex is fully managed.
|
||||
*
|
||||
* This initializes a dummy instance for exactly that purpose. */
|
||||
const NMPlatformMptcpAddr *
|
||||
nmp_global_tracker_mptcp_addr_init_for_ifindex(NMPlatformMptcpAddr *addr, int ifindex)
|
||||
{
|
||||
nm_assert(addr);
|
||||
nm_assert(ifindex > 0);
|
||||
|
||||
*addr = (NMPlatformMptcpAddr){
|
||||
.ifindex = ifindex,
|
||||
.addr_family = AF_UNSPEC,
|
||||
};
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
TrackObjData *obj_data;
|
||||
const TrackData *td_best;
|
||||
} MptcpSyncData;
|
||||
|
||||
static int
|
||||
_mptcp_entries_cmp(gconstpointer a, gconstpointer b, gpointer user_data)
|
||||
{
|
||||
const MptcpSyncData *d_a = a;
|
||||
const MptcpSyncData *d_b = b;
|
||||
|
||||
/* 1) prefer addresses based on the priority (highest priority
|
||||
* sorted first). */
|
||||
NM_CMP_FIELD(d_b->td_best, d_a->td_best, track_priority_val);
|
||||
|
||||
/* Finally, we only care about the order in which they were tracked.
|
||||
* Rely on the stable sort to get that right. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
nmp_global_tracker_sync_mptcp_addrs(NMPGlobalTracker *self, gboolean reapply, gboolean keep_deleted)
|
||||
{
|
||||
char sbuf[64 + NM_UTILS_TO_STRING_BUFFER_SIZE];
|
||||
gs_unref_ptrarray GPtrArray *kaddrs_arr = NULL;
|
||||
gs_unref_hashtable GHashTable *kaddrs_idx = NULL;
|
||||
TrackObjData *obj_data;
|
||||
TrackObjData *obj_data_safe;
|
||||
CList *by_obj_lst_head;
|
||||
const TrackData *td_best;
|
||||
gs_unref_hashtable GHashTable *handled_ifindexes = NULL;
|
||||
gs_unref_array GArray *entries = NULL;
|
||||
gs_unref_hashtable GHashTable *entries_hash_by_addr = NULL;
|
||||
gs_unref_hashtable GHashTable *entries_to_delete = NULL;
|
||||
guint i;
|
||||
guint j;
|
||||
|
||||
g_return_if_fail(NMP_IS_GLOBAL_TRACKER(self));
|
||||
|
||||
_LOGD("sync mptcp-addr%s%s",
|
||||
reapply ? " (reapply)" : "",
|
||||
keep_deleted ? " (keep-deleted)" : "");
|
||||
|
||||
/* Iterate over the tracked objects and construct @handled_ifindexes, @entries
|
||||
* and @entries_to_delete.
|
||||
* - @handled_ifindexes is a hash with all managed interfaces (their ifindex).
|
||||
* - @entries are the MptcpSyncData instances for the tracked objects.
|
||||
* - @entries_to_delete are the NMPObject which we added earlier, but now not
|
||||
* anymore (and which we shall delete). */
|
||||
by_obj_lst_head = _by_obj_lst_head(self, NMP_OBJECT_TYPE_MPTCP_ADDR);
|
||||
c_list_for_each_entry_safe (obj_data, obj_data_safe, by_obj_lst_head, by_obj_lst) {
|
||||
const NMPlatformMptcpAddr *mptcp_addr = NMP_OBJECT_CAST_MPTCP_ADDR(obj_data->obj);
|
||||
NMPlatformMptcpAddr xtst;
|
||||
|
||||
nm_assert(mptcp_addr->port == 0);
|
||||
nm_assert(mptcp_addr->ifindex > 0);
|
||||
nm_assert(mptcp_addr->id == 0);
|
||||
nm_assert_addr_family_or_unspec(mptcp_addr->addr_family);
|
||||
|
||||
/* AF_UNSPEC means this is the dummy object. We only care about it to make the
|
||||
* ifindex as managed via @handled_ifindexes. */
|
||||
nm_assert(
|
||||
(mptcp_addr->addr_family == AF_UNSPEC)
|
||||
== (nm_platform_mptcp_addr_cmp(
|
||||
mptcp_addr,
|
||||
nmp_global_tracker_mptcp_addr_init_for_ifindex(&xtst, mptcp_addr->ifindex))
|
||||
== 0));
|
||||
|
||||
if (reapply) {
|
||||
/* For a reapply, we clear all configured MPTCP addresses that we no longer
|
||||
* shall configured, provided they are on one of the ifindexes we care
|
||||
* about. */
|
||||
if (!handled_ifindexes)
|
||||
handled_ifindexes = g_hash_table_new(nm_direct_hash, NULL);
|
||||
g_hash_table_add(handled_ifindexes, GINT_TO_POINTER(mptcp_addr->ifindex));
|
||||
}
|
||||
|
||||
td_best = _track_obj_data_get_best_data(obj_data);
|
||||
|
||||
if (!td_best) {
|
||||
nm_assert(obj_data->config_state == CONFIG_STATE_ADDED_BY_US);
|
||||
|
||||
/* This entry is a tombstone, that tells us that added the object earlier.
|
||||
* We can delete the MPTCP address (if it's still configured).
|
||||
*
|
||||
* Then we can drop the tombstone. */
|
||||
|
||||
if (mptcp_addr->addr_family != AF_UNSPEC) {
|
||||
if (!reapply) {
|
||||
if (!entries_to_delete) {
|
||||
entries_to_delete = g_hash_table_new_full((GHashFunc) nmp_object_id_hash,
|
||||
(GEqualFunc) nmp_object_id_equal,
|
||||
(GDestroyNotify) nmp_object_unref,
|
||||
NULL);
|
||||
}
|
||||
g_hash_table_add(entries_to_delete, (gpointer) nmp_object_ref(obj_data->obj));
|
||||
}
|
||||
}
|
||||
|
||||
/* We can forget about this entry now. */
|
||||
g_hash_table_remove(self->by_obj, obj_data);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* negative and zero track priorities are not implemented (and make no sense?). */
|
||||
nm_assert(td_best->track_priority_val > 0);
|
||||
nm_assert(td_best->track_priority_present);
|
||||
|
||||
if (mptcp_addr->addr_family == AF_UNSPEC) {
|
||||
/* This is a nmp_global_tracker_mptcp_addr_init_for_ifindex() dummy entry.
|
||||
* It only exists so we can add the @handled_ifindexes entry above
|
||||
* and handle addresses on this interface. */
|
||||
obj_data->config_state = CONFIG_STATE_ADDED_BY_US;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!entries)
|
||||
entries = g_array_new(FALSE, FALSE, sizeof(MptcpSyncData));
|
||||
|
||||
g_array_append_val(entries,
|
||||
((const MptcpSyncData){
|
||||
.obj_data = obj_data,
|
||||
.td_best = td_best,
|
||||
}));
|
||||
}
|
||||
/* We collected all the entires we want to configure. Now, sort them by
|
||||
* priority, and drop all the duplicates (preferring the entries that
|
||||
* appear first, where first means "older"). In kernel, we can only configure an IP
|
||||
* address (without port) as endpoint once. If two interfaces provide the same IP
|
||||
* address, we can only configure one. We need to select one and filter out duplicates.
|
||||
* While there is no solution, the idea is to select the preferred address
|
||||
* somewhat consistently.
|
||||
*
|
||||
* Also, create a lookup index @entries_hash_by_addr to lookup by address. */
|
||||
if (entries) {
|
||||
/* First we sort the entries by priority, to prefer the ones with higher
|
||||
* priority. In case of equal priority, we rely on the stable sort to
|
||||
* preserve the order in which things got tracked. */
|
||||
g_array_sort_with_data(entries, _mptcp_entries_cmp, NULL);
|
||||
|
||||
entries_hash_by_addr = g_hash_table_new(nm_platform_mptcp_addr_index_addr_cmp,
|
||||
nm_platform_mptcp_addr_index_addr_equal);
|
||||
|
||||
/* Now, drop all duplicates addresses. Only keep the first one. */
|
||||
for (i = 0, j = 0; i < entries->len; i++) {
|
||||
const MptcpSyncData *d = nm_g_array_index_p(entries, MptcpSyncData, i);
|
||||
const NMPlatformMptcpAddr *mptcp_addr = NMP_OBJECT_CAST_MPTCP_ADDR(d->obj_data->obj);
|
||||
|
||||
obj_data = g_hash_table_lookup(entries_hash_by_addr, (gpointer) mptcp_addr);
|
||||
if (obj_data) {
|
||||
/* This object is shadowed. We ignore it.
|
||||
*
|
||||
* However, we first propagate the config_state. For MPTCP addrs, it can only be
|
||||
* NONE or ADDED_BY_US. */
|
||||
nm_assert(NM_IN_SET(d->obj_data->config_state,
|
||||
CONFIG_STATE_NONE,
|
||||
CONFIG_STATE_ADDED_BY_US));
|
||||
nm_assert(
|
||||
NM_IN_SET(obj_data->config_state, CONFIG_STATE_NONE, CONFIG_STATE_ADDED_BY_US));
|
||||
|
||||
if (d->obj_data->config_state == CONFIG_STATE_ADDED_BY_US) {
|
||||
obj_data->config_state = CONFIG_STATE_ADDED_BY_US;
|
||||
d->obj_data->config_state = CONFIG_STATE_NONE;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!g_hash_table_insert(entries_hash_by_addr, (gpointer) mptcp_addr, d->obj_data))
|
||||
nm_assert_not_reached();
|
||||
|
||||
if (i != j)
|
||||
*(nm_g_array_index_p(entries, MptcpSyncData, j)) = *d;
|
||||
j++;
|
||||
|
||||
if (j >= MPTCP_PM_ADDR_MAX) {
|
||||
/* Kernel limits the number of addresses we can configure.
|
||||
* It's hard-coded here, taken from current kernel. Hopefully
|
||||
* it matches the running kernel.
|
||||
*
|
||||
* It's worse. There might be other addresses already configured
|
||||
* on other interfaces (or with a port). Our sync method will leave
|
||||
* them alone, as they were not added by us. So the actual limit
|
||||
* is possibly smaller, and kernel fails with EINVAL.
|
||||
*
|
||||
* Still, we definitely need to truncate the list here. Imagine
|
||||
* during an earlier sync we added MAX addresses on one interface.
|
||||
* Now, another interface activates, and wants to configure one
|
||||
* address. That address will get a higher priority (chosen by NML3Cfg),
|
||||
* so that part is good. However, it means we must drop the last from
|
||||
* the other MAX addresses. We achieve that by truncating the list
|
||||
* to MPTCP_PM_ADDR_MAX.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_array_set_size(entries, j);
|
||||
}
|
||||
|
||||
/* Get the list of currently (in kernel) configured MPTCP endpoints. */
|
||||
kaddrs_arr = nm_platform_mptcp_addrs_dump(self->platform);
|
||||
|
||||
/* First, delete all kaddrs which we no longer want... */
|
||||
if (kaddrs_arr) {
|
||||
for (i = 0; i < kaddrs_arr->len; i++) {
|
||||
const NMPObject *obj = kaddrs_arr->pdata[i];
|
||||
const NMPlatformMptcpAddr *mptcp_addr = NMP_OBJECT_CAST_MPTCP_ADDR(obj);
|
||||
|
||||
if (mptcp_addr->port != 0 || mptcp_addr->ifindex <= 0) {
|
||||
/* We ignore all endpoints that have a port or no ifindex.
|
||||
* Those were never created by us, let the user who created
|
||||
* them handle them. */
|
||||
nm_clear_pointer(&kaddrs_arr->pdata[i], nmp_object_unref);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reapply) {
|
||||
/* In full-sync-mode, we delete all MPTCP addrs that are for ifindexes
|
||||
* that we care about. */
|
||||
if (!nm_g_hash_table_contains(handled_ifindexes,
|
||||
GINT_TO_POINTER(mptcp_addr->ifindex))) {
|
||||
goto index_and_next;
|
||||
}
|
||||
} else {
|
||||
/* Otherwise, we only delete objects that we remember to have
|
||||
* added earlier. */
|
||||
if (!nm_g_hash_table_contains(entries_to_delete, obj)) {
|
||||
/* This object is not to delete. */
|
||||
goto index_and_next;
|
||||
}
|
||||
}
|
||||
|
||||
/* We have the object in the delete-list. However, we might still also want
|
||||
* to add it back. Check for that too. */
|
||||
obj_data = nm_g_hash_table_lookup(entries_hash_by_addr, mptcp_addr);
|
||||
if (obj_data) {
|
||||
const NMPlatformMptcpAddr *mptcp_addr2 = NMP_OBJECT_CAST_MPTCP_ADDR(obj_data->obj);
|
||||
|
||||
if (mptcp_addr->flags == mptcp_addr2->flags
|
||||
&& mptcp_addr->ifindex == mptcp_addr2->ifindex) {
|
||||
/* We also want to re-add this very same address. Don't delete it. */
|
||||
goto index_and_next;
|
||||
}
|
||||
}
|
||||
|
||||
if (keep_deleted) {
|
||||
_LOGD("forget/leak object added by us: %s \"%s\"",
|
||||
NMP_OBJECT_GET_CLASS(obj)->obj_type_name,
|
||||
nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf)));
|
||||
goto index_and_next;
|
||||
}
|
||||
|
||||
/* This entry is marked for deletion. Delete it. */
|
||||
if (nm_platform_object_delete(self->platform, obj)) {
|
||||
nm_clear_pointer(&kaddrs_arr->pdata[i], nmp_object_unref);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We failed to delete it. It's unclear what is the matter with this
|
||||
* object. Pretend it doesn't exist (don't add it to kaddrs_idx and
|
||||
* proceed. */
|
||||
nm_clear_pointer(&kaddrs_arr->pdata[i], nmp_object_unref);
|
||||
continue;
|
||||
|
||||
index_and_next:
|
||||
_LOGt("keep: %s \"%s\"",
|
||||
NMP_OBJECT_GET_CLASS(obj)->obj_type_name,
|
||||
nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf)));
|
||||
if (!kaddrs_idx) {
|
||||
kaddrs_idx = g_hash_table_new((GHashFunc) nmp_object_id_hash,
|
||||
(GEqualFunc) nmp_object_id_equal);
|
||||
}
|
||||
g_hash_table_add(kaddrs_idx, (gpointer) obj);
|
||||
}
|
||||
}
|
||||
|
||||
if (entries) {
|
||||
for (i = 0; i < entries->len; i++) {
|
||||
const MptcpSyncData *d = nm_g_array_index_p(entries, MptcpSyncData, i);
|
||||
const NMPlatformMptcpAddr *mptcp_addr = NMP_OBJECT_CAST_MPTCP_ADDR(d->obj_data->obj);
|
||||
const NMPObject *kobj;
|
||||
|
||||
d->obj_data->config_state = CONFIG_STATE_ADDED_BY_US;
|
||||
|
||||
kobj = nm_g_hash_table_lookup(kaddrs_idx, d->obj_data->obj);
|
||||
if (kobj && kobj->mptcp_addr.flags == mptcp_addr->flags) {
|
||||
/* This address is already added with the right flags. We can
|
||||
* skip it. */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Kernel actually only allows us to add a small number of addresses.
|
||||
* Also, if we have a conflicting address on another interface, the
|
||||
* request will be rejected.
|
||||
*
|
||||
* Don't try to handle that. Just attempt to add the address, and if
|
||||
* we fail, there is nothing we can do about it. */
|
||||
nm_platform_mptcp_addr_update(self->platform, TRUE, mptcp_addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nmp_global_tracker_sync(NMPGlobalTracker *self, NMPObjectType obj_type, gboolean keep_deleted)
|
||||
{
|
||||
char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE];
|
||||
const NMDedupMultiHeadEntry *pl_head_entry;
|
||||
NMDedupMultiIter pl_iter;
|
||||
const NMPObject *plobj;
|
||||
gs_unref_ptrarray GPtrArray *objs_to_delete = NULL;
|
||||
TrackObjData *obj_data;
|
||||
|
|
@ -614,6 +974,8 @@ nmp_global_tracker_sync(NMPGlobalTracker *self, NMPObjectType obj_type, gboolean
|
|||
pl_head_entry = nm_platform_lookup_object(self->platform, obj_type, 0);
|
||||
|
||||
if (pl_head_entry) {
|
||||
NMDedupMultiIter pl_iter;
|
||||
|
||||
nmp_cache_iter_for_each (&pl_iter, pl_head_entry, &plobj) {
|
||||
obj_data = g_hash_table_lookup(self->by_obj, &plobj);
|
||||
|
||||
|
|
@ -691,8 +1053,32 @@ nmp_global_tracker_sync(NMPGlobalTracker *self, NMPObjectType obj_type, gboolean
|
|||
|
||||
plobj =
|
||||
nm_platform_lookup_obj(self->platform, NMP_CACHE_ID_TYPE_OBJECT_TYPE, obj_data->obj);
|
||||
if (plobj)
|
||||
continue;
|
||||
if (plobj) {
|
||||
int c;
|
||||
|
||||
switch (obj_type) {
|
||||
case NMP_OBJECT_TYPE_ROUTING_RULE:
|
||||
c = nm_platform_routing_rule_cmp(NMP_OBJECT_CAST_ROUTING_RULE(obj_data->obj),
|
||||
NMP_OBJECT_CAST_ROUTING_RULE(plobj),
|
||||
NM_PLATFORM_ROUTING_RULE_CMP_TYPE_SEMANTICALLY);
|
||||
break;
|
||||
case NMP_OBJECT_TYPE_IP4_ROUTE:
|
||||
c = nm_platform_ip4_route_cmp(NMP_OBJECT_CAST_IP4_ROUTE(obj_data->obj),
|
||||
NMP_OBJECT_CAST_IP4_ROUTE(plobj),
|
||||
NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY);
|
||||
break;
|
||||
case NMP_OBJECT_TYPE_IP6_ROUTE:
|
||||
c = nm_platform_ip6_route_cmp(NMP_OBJECT_CAST_IP6_ROUTE(obj_data->obj),
|
||||
NMP_OBJECT_CAST_IP6_ROUTE(plobj),
|
||||
NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY);
|
||||
break;
|
||||
default:
|
||||
nm_assert_not_reached();
|
||||
}
|
||||
if (c == 0)
|
||||
continue;
|
||||
nm_platform_object_delete(self->platform, plobj);
|
||||
}
|
||||
|
||||
obj_data->config_state = CONFIG_STATE_ADDED_BY_US;
|
||||
|
||||
|
|
@ -843,6 +1229,7 @@ nmp_global_tracker_new(NMPlatform *platform)
|
|||
.by_obj_lst_heads[0] = C_LIST_INIT(self->by_obj_lst_heads[0]),
|
||||
.by_obj_lst_heads[1] = C_LIST_INIT(self->by_obj_lst_heads[1]),
|
||||
.by_obj_lst_heads[2] = C_LIST_INIT(self->by_obj_lst_heads[2]),
|
||||
.by_obj_lst_heads[3] = C_LIST_INIT(self->by_obj_lst_heads[3]),
|
||||
};
|
||||
return self;
|
||||
}
|
||||
|
|
@ -870,6 +1257,7 @@ nmp_global_tracker_unref(NMPGlobalTracker *self)
|
|||
nm_assert(c_list_is_empty(&self->by_obj_lst_heads[0]));
|
||||
nm_assert(c_list_is_empty(&self->by_obj_lst_heads[1]));
|
||||
nm_assert(c_list_is_empty(&self->by_obj_lst_heads[2]));
|
||||
nm_assert(c_list_is_empty(&self->by_obj_lst_heads[3]));
|
||||
g_object_unref(self->platform);
|
||||
nm_g_slice_free(self);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,13 @@ gboolean nmp_global_tracker_untrack_all(NMPGlobalTracker *self,
|
|||
|
||||
void nmp_global_tracker_sync(NMPGlobalTracker *self, NMPObjectType obj_type, gboolean keep_deleted);
|
||||
|
||||
void nmp_global_tracker_sync_mptcp_addrs(NMPGlobalTracker *self,
|
||||
gboolean reapply,
|
||||
gboolean keep_deleted);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
const NMPlatformMptcpAddr *nmp_global_tracker_mptcp_addr_init_for_ifindex(NMPlatformMptcpAddr *addr,
|
||||
int ifindex);
|
||||
|
||||
#endif /* __NMP_GLOBAL_TRACKER_H__ */
|
||||
|
|
|
|||
|
|
@ -1553,20 +1553,34 @@ _vt_cmd_plobj_id_cmp_routing_rule(const NMPlatformObject *obj1, const NMPlatform
|
|||
}
|
||||
|
||||
_vt_cmd_plobj_id_cmp(mptcp_addr, NMPlatformMptcpAddr, {
|
||||
NM_CMP_FIELD(obj1, obj2, id);
|
||||
NM_CMP_FIELD_UNSAFE(obj1, obj2, in_kernel);
|
||||
if (!obj1->in_kernel) {
|
||||
/* See comment NMPlatformMptcpAddr.in_kernel for why. */
|
||||
NM_CMP_FIELD(obj1, obj2, addr_family);
|
||||
|
||||
/* nm_utils_addr_family_to_size() asserts that addr-family is either AF_INET or AF_INET6.
|
||||
* This means, we cannot compare totally bogus objects. That is in particular fine
|
||||
* for instances which are not "in_kernel". While we might receive unexpected values
|
||||
* from kernel, we should not create them for internal purposes. */
|
||||
NM_CMP_FIELD_MEMCMP_LEN(obj1, obj2, addr, nm_utils_addr_family_to_size(obj1->addr_family));
|
||||
|
||||
NM_CMP_FIELD(obj1, obj2, port);
|
||||
}
|
||||
/* The primary key of an MPTCP endpoint is only the address:port@ifindex.
|
||||
*
|
||||
* Which does not fully correspond to kernel's view. Kernel's view is determined
|
||||
* by the question whether you can add two objects that only differ by one
|
||||
* attribute. If you can, the attribute is part of the ID otherwise it isn't.
|
||||
*
|
||||
* Note that for kernel, the "ifindex" is not part of the identity.
|
||||
* That is, you cannot two add two endpoints that only differ by
|
||||
* ifindex. However, for our purpose, it is very useful to make
|
||||
* the "ifindex" part of the identity. For example, NMPGlobalTracker will use
|
||||
* this to track MPTCP addresses from independent callers (NML3Cfg).
|
||||
* It would be bad, if objects that differ by "ifindex" would be
|
||||
* combined.
|
||||
*
|
||||
* The "id" is intentionally not part of the identity for us. Note however, that kernel
|
||||
* does not allow configuring duplicates "id" -- so the "id" could be a primary key
|
||||
* as far as kernel is concerned. However, when we track MPTCP endpoints that
|
||||
* we want to configure, the "id" is left undefined (and we let kernel choose it).
|
||||
* If the "id" would be part of the NMPObject's ID, we could not lookup
|
||||
* an object unless we know the "id" -- which we often don't.
|
||||
*/
|
||||
NM_CMP_FIELD(obj1, obj2, ifindex);
|
||||
NM_CMP_FIELD(obj1, obj2, addr_family);
|
||||
NM_CMP_FIELD_MEMCMP_LEN(obj1,
|
||||
obj2,
|
||||
addr,
|
||||
nm_utils_addr_family_to_size_untrusted(obj1->addr_family));
|
||||
NM_CMP_FIELD(obj1, obj2, port);
|
||||
});
|
||||
|
||||
void
|
||||
|
|
@ -1655,13 +1669,9 @@ _vt_cmd_plobj_id_hash_update(tfilter, NMPlatformTfilter, {
|
|||
});
|
||||
|
||||
_vt_cmd_plobj_id_hash_update(mptcp_addr, NMPlatformMptcpAddr, {
|
||||
if (obj->in_kernel) {
|
||||
nm_hash_update_val(h, obj->id);
|
||||
} else {
|
||||
/* _vt_cmd_plobj_id_cmp_mptcp_addr for why. */
|
||||
nm_hash_update_vals(h, obj->id, obj->addr_family, obj->port);
|
||||
nm_hash_update(h, &obj->addr, nm_utils_addr_family_to_size(obj->addr_family));
|
||||
}
|
||||
/* See the corresponding ID cmp function for details. */
|
||||
nm_hash_update_vals(h, obj->addr_family, obj->port, obj->ifindex);
|
||||
nm_hash_update(h, &obj->addr, nm_utils_addr_family_to_size_untrusted(obj->addr_family));
|
||||
});
|
||||
|
||||
static void
|
||||
|
|
@ -1775,7 +1785,7 @@ _vt_cmd_obj_is_alive_tfilter(const NMPObject *obj)
|
|||
static gboolean
|
||||
_vt_cmd_obj_is_alive_mptcp_addr(const NMPObject *obj)
|
||||
{
|
||||
return NM_IN_SET(obj->mptcp_addr.addr_family, AF_INET, AF_INET6);
|
||||
return NM_IN_SET(obj->mptcp_addr.addr_family, AF_INET, AF_INET6, AF_UNSPEC);
|
||||
}
|
||||
|
||||
gboolean
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ typedef struct {
|
|||
G_DEFINE_TYPE(NMWifiUtilsNl80211, nm_wifi_utils_nl80211, NM_TYPE_WIFI_UTILS)
|
||||
|
||||
static int
|
||||
ack_handler(struct nl_msg *msg, void *arg)
|
||||
ack_handler(const struct nl_msg *msg, void *arg)
|
||||
{
|
||||
int *done = arg;
|
||||
*done = 1;
|
||||
|
|
@ -66,7 +66,7 @@ ack_handler(struct nl_msg *msg, void *arg)
|
|||
}
|
||||
|
||||
static int
|
||||
finish_handler(struct nl_msg *msg, void *arg)
|
||||
finish_handler(const struct nl_msg *msg, void *arg)
|
||||
{
|
||||
int *done = arg;
|
||||
*done = 1;
|
||||
|
|
@ -74,7 +74,7 @@ finish_handler(struct nl_msg *msg, void *arg)
|
|||
}
|
||||
|
||||
static int
|
||||
error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
|
||||
error_handler(const struct sockaddr_nl *nla, const struct nlmsgerr *err, void *arg)
|
||||
{
|
||||
int *done = arg;
|
||||
*done = err->error;
|
||||
|
|
@ -106,7 +106,7 @@ nl80211_alloc_msg(NMWifiUtilsNl80211 *self, uint8_t cmd, uint16_t flags)
|
|||
static int
|
||||
nl80211_send_and_recv(NMWifiUtilsNl80211 *self,
|
||||
struct nl_msg *msg,
|
||||
int (*valid_handler)(struct nl_msg *, void *),
|
||||
int (*valid_handler)(const struct nl_msg *, void *),
|
||||
void *valid_data)
|
||||
{
|
||||
int err;
|
||||
|
|
@ -167,7 +167,7 @@ struct nl80211_iface_info {
|
|||
};
|
||||
|
||||
static int
|
||||
nl80211_iface_info_handler(struct nl_msg *msg, void *arg)
|
||||
nl80211_iface_info_handler(const struct nl_msg *msg, void *arg)
|
||||
{
|
||||
struct nl80211_iface_info *info = arg;
|
||||
struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
|
||||
|
|
@ -269,7 +269,7 @@ nla_put_failure:
|
|||
}
|
||||
|
||||
static int
|
||||
nl80211_get_wake_on_wlan_handler(struct nl_msg *msg, void *arg)
|
||||
nl80211_get_wake_on_wlan_handler(const struct nl_msg *msg, void *arg)
|
||||
{
|
||||
_NMSettingWirelessWakeOnWLan *wowl = arg;
|
||||
struct nlattr *attrs[NL80211_ATTR_MAX + 1];
|
||||
|
|
@ -422,7 +422,7 @@ struct nl80211_station_info {
|
|||
};
|
||||
|
||||
static int
|
||||
nl80211_station_dump_handler(struct nl_msg *msg, void *arg)
|
||||
nl80211_station_dump_handler(const struct nl_msg *msg, void *arg)
|
||||
{
|
||||
static const struct nla_policy stats_policy[] = {
|
||||
[NL80211_STA_INFO_INACTIVE_TIME] = {.type = NLA_U32},
|
||||
|
|
@ -576,7 +576,7 @@ struct nl80211_device_info {
|
|||
#define WLAN_CIPHER_SUITE_SMS4 0x00147201
|
||||
|
||||
static int
|
||||
nl80211_wiphy_info_handler(struct nl_msg *msg, void *arg)
|
||||
nl80211_wiphy_info_handler(const struct nl_msg *msg, void *arg)
|
||||
{
|
||||
static const struct nla_policy freq_policy[] = {
|
||||
[NL80211_FREQUENCY_ATTR_FREQ] = {.type = NLA_U32},
|
||||
|
|
@ -816,7 +816,7 @@ struct nl80211_csme_conn_info {
|
|||
};
|
||||
|
||||
static int
|
||||
nl80211_csme_conn_event_handler(struct nl_msg *msg, void *arg)
|
||||
nl80211_csme_conn_event_handler(const struct nl_msg *msg, void *arg)
|
||||
{
|
||||
struct nl80211_csme_conn_info *info = arg;
|
||||
NMPlatformCsmeConnInfo *out_conn_info = info->conn_info;
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ G_DEFINE_TYPE(NMWpanUtils, nm_wpan_utils, G_TYPE_OBJECT)
|
|||
/*****************************************************************************/
|
||||
|
||||
static int
|
||||
ack_handler(struct nl_msg *msg, void *arg)
|
||||
ack_handler(const struct nl_msg *msg, void *arg)
|
||||
{
|
||||
int *done = arg;
|
||||
*done = 1;
|
||||
|
|
@ -59,7 +59,7 @@ ack_handler(struct nl_msg *msg, void *arg)
|
|||
}
|
||||
|
||||
static int
|
||||
finish_handler(struct nl_msg *msg, void *arg)
|
||||
finish_handler(const struct nl_msg *msg, void *arg)
|
||||
{
|
||||
int *done = arg;
|
||||
*done = 1;
|
||||
|
|
@ -67,7 +67,7 @@ finish_handler(struct nl_msg *msg, void *arg)
|
|||
}
|
||||
|
||||
static int
|
||||
error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
|
||||
error_handler(const struct sockaddr_nl *nla, const struct nlmsgerr *err, void *arg)
|
||||
{
|
||||
int *done = arg;
|
||||
*done = err->error;
|
||||
|
|
@ -98,7 +98,7 @@ nl802154_alloc_msg(NMWpanUtils *self, uint8_t cmd, uint16_t flags)
|
|||
static int
|
||||
nl802154_send_and_recv(NMWpanUtils *self,
|
||||
struct nl_msg *msg,
|
||||
int (*valid_handler)(struct nl_msg *, void *),
|
||||
int (*valid_handler)(const struct nl_msg *, void *),
|
||||
void *valid_data)
|
||||
{
|
||||
int err;
|
||||
|
|
@ -144,7 +144,7 @@ struct nl802154_interface {
|
|||
};
|
||||
|
||||
static int
|
||||
nl802154_get_interface_handler(struct nl_msg *msg, void *arg)
|
||||
nl802154_get_interface_handler(const struct nl_msg *msg, void *arg)
|
||||
{
|
||||
static const struct nla_policy nl802154_policy[] = {
|
||||
[NL802154_ATTR_PAN_ID] = {.type = NLA_U16},
|
||||
|
|
|
|||
|
|
@ -1307,6 +1307,28 @@ nm_utils_addr_family_to_size(int addr_family)
|
|||
return nm_assert_unreachable_val(0);
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
nm_utils_addr_family_to_size_untrusted(int addr_family)
|
||||
{
|
||||
/* This is almost the same as nm_utils_addr_family_to_size().
|
||||
* The difference is that nm_utils_addr_family_to_size() requires that
|
||||
* addr_family is either AF_INET or AF_INET6 (it asserts against that).
|
||||
*
|
||||
* This variant accepts any addr_family, but returns zero for any unknown
|
||||
* family.
|
||||
*
|
||||
* Use this, if the address family is untrusted or not guaranteed to be valid.
|
||||
* Of course, then you also need to handle that this function potentially returns
|
||||
* zero. */
|
||||
switch (addr_family) {
|
||||
case NM_AF_INET:
|
||||
return NM_AF_INET_SIZE;
|
||||
case NM_AF_INET6:
|
||||
return NM_AF_INET6_SIZE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
nm_utils_addr_family_from_size(size_t len)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ static char *secret_flags_to_string(guint32 flags, NMMetaAccessorGetType get_typ
|
|||
(NM_SETTING_SECRET_FLAG_NONE | NM_SETTING_SECRET_FLAG_AGENT_OWNED \
|
||||
| NM_SETTING_SECRET_FLAG_NOT_SAVED | NM_SETTING_SECRET_FLAG_NOT_REQUIRED)
|
||||
|
||||
const NMUtilsEnumValueInfo GOBJECT_ENUM_VALUE_INFOS_GET_FROM_SETTER[1];
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static GType
|
||||
|
|
@ -1082,7 +1084,7 @@ _get_fcn_gobject_enum(ARGS_GET_FCN)
|
|||
GType gtype = 0;
|
||||
nm_auto_unref_gtypeclass GTypeClass *gtype_class = NULL;
|
||||
nm_auto_unref_gtypeclass GTypeClass *gtype_prop_class = NULL;
|
||||
const struct _NMUtilsEnumValueInfo *value_infos = NULL;
|
||||
const NMUtilsEnumValueInfo *value_infos = NULL;
|
||||
gboolean has_gtype = FALSE;
|
||||
nm_auto_unset_gvalue GValue gval = G_VALUE_INIT;
|
||||
gint64 v;
|
||||
|
|
@ -1198,11 +1200,12 @@ _get_fcn_gobject_enum(ARGS_GET_FCN)
|
|||
RETURN_STR_TO_FREE(g_steal_pointer(&s));
|
||||
}
|
||||
|
||||
/* the gobject_enum.value_infos are currently ignored for the getter. They
|
||||
* only declare additional aliases for the setter. */
|
||||
|
||||
if (property_info->property_typ_data)
|
||||
if (property_info->property_typ_data) {
|
||||
value_infos = property_info->property_typ_data->subtype.gobject_enum.value_infos_get;
|
||||
if (value_infos == GOBJECT_ENUM_VALUE_INFOS_GET_FROM_SETTER)
|
||||
value_infos = property_info->property_typ_data->subtype.gobject_enum.value_infos;
|
||||
}
|
||||
|
||||
s = _nm_utils_enum_to_str_full(gtype, (int) v, ", ", value_infos);
|
||||
|
||||
if (!format_numeric)
|
||||
|
|
@ -1676,12 +1679,13 @@ fail:
|
|||
static const char *const *
|
||||
_values_fcn_gobject_enum(ARGS_VALUES_FCN)
|
||||
{
|
||||
GType gtype = 0;
|
||||
gboolean has_gtype = FALSE;
|
||||
gboolean has_minmax = FALSE;
|
||||
int min = G_MININT;
|
||||
int max = G_MAXINT;
|
||||
char **v;
|
||||
const NMUtilsEnumValueInfo *value_infos = NULL;
|
||||
GType gtype = 0;
|
||||
gboolean has_gtype = FALSE;
|
||||
gboolean has_minmax = FALSE;
|
||||
int min = G_MININT;
|
||||
int max = G_MAXINT;
|
||||
char **v;
|
||||
|
||||
if (property_info->property_typ_data) {
|
||||
if (property_info->property_typ_data->subtype.gobject_enum.min
|
||||
|
|
@ -1711,10 +1715,29 @@ _values_fcn_gobject_enum(ARGS_VALUES_FCN)
|
|||
}
|
||||
}
|
||||
|
||||
/* the gobject_enum.value_infos are currently ignored for the list of
|
||||
* values. They only declare additional (hidden) aliases for the setter. */
|
||||
/* There is a problem. For flags, we don't expand to all the values that we could
|
||||
* complete for. We only expand to a single flag "FLAG1", but if the property
|
||||
* is already set to "FLAG2", we should also expand to "FLAG1,FLAG2". */
|
||||
|
||||
v = nm_strv_make_deep_copied(nm_utils_enum_get_values(gtype, min, max));
|
||||
|
||||
if (property_info->property_typ_data
|
||||
&& (value_infos = property_info->property_typ_data->subtype.gobject_enum.value_infos)) {
|
||||
const guint V_N = NM_PTRARRAY_LEN(v);
|
||||
guint n;
|
||||
guint i;
|
||||
|
||||
nm_assert(value_infos[0].nick);
|
||||
|
||||
for (n = 0; value_infos[n].nick;)
|
||||
n++;
|
||||
|
||||
v = g_realloc(v, (V_N + n + 1) * sizeof(char *));
|
||||
for (i = 0; i < n; i++)
|
||||
v[V_N + i] = g_strdup(value_infos[i].nick);
|
||||
v[V_N + n] = NULL;
|
||||
}
|
||||
|
||||
return (const char *const *) (*out_to_free = v);
|
||||
}
|
||||
|
||||
|
|
@ -5555,6 +5578,21 @@ static const NMMetaPropertyInfo *const property_infos_CONNECTION[] = {
|
|||
),
|
||||
),
|
||||
),
|
||||
PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_MPTCP_FLAGS,
|
||||
.property_type = &_pt_gobject_enum,
|
||||
.property_typ_data = DEFINE_PROPERTY_TYP_DATA (
|
||||
PROPERTY_TYP_DATA_SUBTYPE (gobject_enum,
|
||||
.get_gtype = nm_mptcp_flags_get_type,
|
||||
.value_infos_get = GOBJECT_ENUM_VALUE_INFOS_GET_FROM_SETTER,
|
||||
.value_infos = ENUM_VALUE_INFOS (
|
||||
{
|
||||
.value = NM_MPTCP_FLAGS_NONE,
|
||||
.nick = "default",
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
PROPERTY_INFO_WITH_DESC (NM_SETTING_CONNECTION_MUD_URL,
|
||||
.property_type = &_pt_gobject_string,
|
||||
.hide_if_default = TRUE,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#define DESCRIBE_DOC_NM_SETTING_CONNECTION_MASTER N_("Interface name of the master device or UUID of the master connection.")
|
||||
#define DESCRIBE_DOC_NM_SETTING_CONNECTION_MDNS N_("Whether mDNS is enabled for the connection. The permitted values are: \"yes\" (2) register hostname and resolving for the connection, \"no\" (0) disable mDNS for the interface, \"resolve\" (1) do not register hostname but allow resolving of mDNS host names and \"default\" (-1) to allow lookup of a global default in NetworkManager.conf. If unspecified, \"default\" ultimately depends on the DNS plugin (which for systemd-resolved currently means \"no\"). This feature requires a plugin which supports mDNS. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved.")
|
||||
#define DESCRIBE_DOC_NM_SETTING_CONNECTION_METERED N_("Whether the connection is metered. When updating this property on a currently activated connection, the change takes effect immediately.")
|
||||
#define DESCRIBE_DOC_NM_SETTING_CONNECTION_MPTCP_FLAGS N_("Whether to configure MPTCP endpoints and the address flags. If MPTCP is enabled in NetworkManager, it will configure the addresses of the interface as MPTCP endpoints. Note that IPv4 loopback addresses (127.0.0.0/8), IPv4 link local addresses (169.254.0.0/16), the IPv6 loopback address (::1), IPv6 link local addresses (fe80::/10), IPv6 unique local addresses (ULA, fc00::/7) and IPv6 privacy extension addresses (rfc3041, ipv6.ip6-privacy) will be excluded from being configured as endpoints. If \"disabled\" (0x1), MPTCP handling for the interface is disabled and no endpoints are registered. The flag \"enabled-on-global-iface\" (0x2) means that MPTCP handling is enabled if the interface configures a default route in the main routing table. This choice is per-address family, for example if there is an IPv4 default route 0.0.0.0/0, IPv4 endpoints are configured. The \"enabled\" (0x4) flag means that MPTCP handling is explicitly enabled. This flag can also be implied from the presence of other flags. If MPTCP handling is enabled, then endpoints will be configured with the specified address flags \"signal\" (0x10), \"subflow\" (0x20), \"backup\" (0x40), \"fullmesh\" (0x80). See ip-mptcp(8) manual for additional information about the flags. If the flags are zero, the global connection default from NetworkManager.conf is honored. If still unspecified, the fallback is either \"disabled\" or \"enabled-on-global-iface,subflow\" depending on \"/proc/sys/net/mptcp/enabled\". NetworkManager does not change the MPTCP limits nor enable MPTCP via \"/proc/sys/net/mptcp/enabled\". That is a host configuration which the admin can change via sysctl and ip-mptcp. Strict reverse path filtering (rp_filter) breaks many MPTCP use cases, so when MPTCP handling for IPv4 addresses on the interface is enabled, NetworkManager would loosen the strict reverse path filtering (1) to the loose setting (2).")
|
||||
#define DESCRIBE_DOC_NM_SETTING_CONNECTION_MUD_URL N_("If configured, set to a Manufacturer Usage Description (MUD) URL that points to manufacturer-recommended network policies for IoT devices. It is transmitted as a DHCPv4 or DHCPv6 option. The value must be a valid URL starting with \"https://\". The special value \"none\" is allowed to indicate that no MUD URL is used. If the per-profile value is unspecified (the default), a global connection default gets consulted. If still unspecified, the ultimate default is \"none\".")
|
||||
#define DESCRIBE_DOC_NM_SETTING_CONNECTION_MULTI_CONNECT N_("Specifies whether the profile can be active multiple times at a particular moment. The value is of type NMConnectionMultiConnect.")
|
||||
#define DESCRIBE_DOC_NM_SETTING_CONNECTION_PERMISSIONS N_("An array of strings defining what access a given user has to this connection. If this is NULL or empty, all users are allowed to access this connection; otherwise users are allowed if and only if they are in this list. When this is not empty, the connection can be active only when one of the specified users is logged into an active session. Each entry is of the form \"[type]:[id]:[reserved]\"; for example, \"user:dcbw:blah\". At this time only the \"user\" [type] is allowed. Any other values are ignored and reserved for future use. [id] is the username that this permission refers to, which may not contain the \":\" character. Any [reserved] information present must be ignored and is reserved for future use. All of [type], [id], and [reserved] must be valid UTF-8.")
|
||||
|
|
|
|||
|
|
@ -419,6 +419,8 @@
|
|||
description="Whether Link-Local Multicast Name Resolution (LLMNR) is enabled for the connection. LLMNR is a protocol based on the Domain Name System (DNS) packet format that allows both IPv4 and IPv6 hosts to perform name resolution for hosts on the same local link. The permitted values are: "yes" (2) register hostname and resolving for the connection, "no" (0) disable LLMNR for the interface, "resolve" (1) do not register hostname but allow resolving of LLMNR host names If unspecified, "default" ultimately depends on the DNS plugin (which for systemd-resolved currently means "yes"). This feature requires a plugin which supports LLMNR. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved." />
|
||||
<property name="dns-over-tls"
|
||||
description="Whether DNSOverTls (dns-over-tls) is enabled for the connection. DNSOverTls is a technology which uses TLS to encrypt dns traffic. The permitted values are: "yes" (2) use DNSOverTls and disabled fallback, "opportunistic" (1) use DNSOverTls but allow fallback to unencrypted resolution, "no" (0) don't ever use DNSOverTls. If unspecified "default" depends on the plugin used. Systemd-resolved uses global setting. This feature requires a plugin which supports DNSOverTls. Otherwise, the setting has no effect. One such plugin is dns-systemd-resolved." />
|
||||
<property name="mptcp-flags"
|
||||
description="Whether to configure MPTCP endpoints and the address flags. If MPTCP is enabled in NetworkManager, it will configure the addresses of the interface as MPTCP endpoints. Note that IPv4 loopback addresses (127.0.0.0/8), IPv4 link local addresses (169.254.0.0/16), the IPv6 loopback address (::1), IPv6 link local addresses (fe80::/10), IPv6 unique local addresses (ULA, fc00::/7) and IPv6 privacy extension addresses (rfc3041, ipv6.ip6-privacy) will be excluded from being configured as endpoints. If "disabled" (0x1), MPTCP handling for the interface is disabled and no endpoints are registered. The flag "enabled-on-global-iface" (0x2) means that MPTCP handling is enabled if the interface configures a default route in the main routing table. This choice is per-address family, for example if there is an IPv4 default route 0.0.0.0/0, IPv4 endpoints are configured. The "enabled" (0x4) flag means that MPTCP handling is explicitly enabled. This flag can also be implied from the presence of other flags. If MPTCP handling is enabled, then endpoints will be configured with the specified address flags "signal" (0x10), "subflow" (0x20), "backup" (0x40), "fullmesh" (0x80). See ip-mptcp(8) manual for additional information about the flags. If the flags are zero, the global connection default from NetworkManager.conf is honored. If still unspecified, the fallback is either "disabled" or "enabled-on-global-iface,subflow" depending on "/proc/sys/net/mptcp/enabled". NetworkManager does not change the MPTCP limits nor enable MPTCP via "/proc/sys/net/mptcp/enabled". That is a host configuration which the admin can change via sysctl and ip-mptcp. Strict reverse path filtering (rp_filter) breaks many MPTCP use cases, so when MPTCP handling for IPv4 addresses on the interface is enabled, NetworkManager would loosen the strict reverse path filtering (1) to the loose setting (2)." />
|
||||
<property name="mud-url"
|
||||
description="If configured, set to a Manufacturer Usage Description (MUD) URL that points to manufacturer-recommended network policies for IoT devices. It is transmitted as a DHCPv4 or DHCPv6 option. The value must be a valid URL starting with "https://". The special value "none" is allowed to indicate that no MUD URL is used. If the per-profile value is unspecified (the default), a global connection default gets consulted. If still unspecified, the ultimate default is "none"." />
|
||||
<property name="wait-device-timeout"
|
||||
|
|
|
|||
|
|
@ -502,12 +502,12 @@ NAME UUID TYPE DEVICE
|
|||
con-1 5fcfd6d7-1e63-3332-8826-a7eda103792d ethernet --
|
||||
|
||||
<<<
|
||||
size: 1362
|
||||
size: 1416
|
||||
location: src/tests/client/test-client.py:test_002()/23
|
||||
cmd: $NMCLI c s con-1
|
||||
lang: C
|
||||
returncode: 0
|
||||
stdout: 1234 bytes
|
||||
stdout: 1288 bytes
|
||||
>>>
|
||||
connection.id: con-1
|
||||
connection.uuid: 5fcfd6d7-1e63-3332-8826-a7eda103792d
|
||||
|
|
@ -533,16 +533,17 @@ connection.lldp: default
|
|||
connection.mdns: -1 (default)
|
||||
connection.llmnr: -1 (default)
|
||||
connection.dns-over-tls: -1 (default)
|
||||
connection.mptcp-flags: 0x0 (default)
|
||||
connection.wait-device-timeout: -1
|
||||
connection.wait-activation-delay: -1
|
||||
|
||||
<<<
|
||||
size: 1374
|
||||
size: 1428
|
||||
location: src/tests/client/test-client.py:test_002()/24
|
||||
cmd: $NMCLI c s con-1
|
||||
lang: pl_PL.UTF-8
|
||||
returncode: 0
|
||||
stdout: 1236 bytes
|
||||
stdout: 1290 bytes
|
||||
>>>
|
||||
connection.id: con-1
|
||||
connection.uuid: 5fcfd6d7-1e63-3332-8826-a7eda103792d
|
||||
|
|
@ -568,6 +569,7 @@ connection.lldp: default
|
|||
connection.mdns: -1 (default)
|
||||
connection.llmnr: -1 (default)
|
||||
connection.dns-over-tls: -1 (default)
|
||||
connection.mptcp-flags: 0x0 (default)
|
||||
connection.wait-device-timeout: -1
|
||||
connection.wait-activation-delay: -1
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue