diff --git a/examples/python/gi/nm-wg-set b/examples/python/gi/nm-wg-set index 81ee5667e4..5f284c2c25 100755 --- a/examples/python/gi/nm-wg-set +++ b/examples/python/gi/nm-wg-set @@ -234,6 +234,15 @@ def secret_to_string(secret): return '' return secret +def val_to_str(val): + if val == NM.Ternary.DEFAULT: + return 'default' + if val == NM.Ternary.TRUE: + return 'true' + if val == NM.Ternary.FALSE: + return 'false' + return repr(val) + ############################################################################### def wg_read_private_key(privkey_file): @@ -273,6 +282,9 @@ def do_get(nm_client, connection): print('private-key-flags: %s' % (secret_flags_to_string(s_wg.get_private_key_flags()))) print('listen-port: %s' % (s_wg.get_listen_port())) print('fwmark: 0x%x' % (s_wg.get_fwmark())) + print('peer-routes: %s' % (val_to_str(s_wg.get_peer_routes()))) + print('ip4-auto-default-route: %s' % (val_to_str(s_wg.get_ip4_auto_default_route()))) + print('ip6-auto-default-route: %s' % (val_to_str(s_wg.get_ip6_auto_default_route()))) for i in range(s_wg.get_peers_len()): peer = s_wg.get_peer(i) print('peer[%d].public-key: %s' % (i, peer.get_public_key())) diff --git a/shared/nm-glib-aux/nm-hash-utils.h b/shared/nm-glib-aux/nm-hash-utils.h index f13e0b6d56..aa0a3d7c0a 100644 --- a/shared/nm-glib-aux/nm-hash-utils.h +++ b/shared/nm-glib-aux/nm-hash-utils.h @@ -26,6 +26,9 @@ /*****************************************************************************/ +#define NM_HASH_SEED_16(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af) \ + ((const guint8[16]) { a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af }) + void nm_hash_siphash42_init (CSipHash *h, guint static_seed); /* Siphash24 of binary buffer @arr and @len, using the randomized seed from diff --git a/src/devices/nm-device-wireguard.c b/src/devices/nm-device-wireguard.c index d36573cf25..f4da539356 100644 --- a/src/devices/nm-device-wireguard.c +++ b/src/devices/nm-device-wireguard.c @@ -265,157 +265,51 @@ done: *out_enabled_v6 = (enabled_v6 == TRUE); } -static guint32 -_auto_default_route_find_unused_table (NMPlatform *platform) -{ - guint32 table; - int is_ipv4; - - for (table = 51820; TRUE; table++) { - const NMDedupMultiHeadEntry *head_entry; - const guint32 table_coerced = nm_platform_route_table_coerce (table); - NMDedupMultiIter iter; - const NMPObject *plobj; - - /* find a table/fwmark that is not yet in use. */ - - for (is_ipv4 = 0; is_ipv4 < 2; is_ipv4++) { - head_entry = nm_platform_lookup_object (platform, - is_ipv4 - ? NMP_OBJECT_TYPE_IP4_ROUTE - : NMP_OBJECT_TYPE_IP6_ROUTE, - -1); - nmp_cache_iter_for_each (&iter, head_entry, &plobj) { - if (NMP_OBJECT_CAST_IP_ROUTE (plobj)->table_coerced == table_coerced) - goto try_next_table; - } - } - - head_entry = nm_platform_lookup_object_by_addr_family (platform, - NMP_OBJECT_TYPE_ROUTING_RULE, - AF_UNSPEC); - nmp_cache_iter_for_each (&iter, head_entry, &plobj) { - const NMPlatformRoutingRule *rr = NMP_OBJECT_CAST_ROUTING_RULE (plobj); - - if (rr->fwmark == table) - goto try_next_table; - } - - head_entry = nm_platform_lookup_obj_type (platform, NMP_OBJECT_TYPE_LINK); - nmp_cache_iter_for_each (&iter, head_entry, &plobj) { - const NMPObject *lnk_wg; - - if (plobj->link.type != NM_LINK_TYPE_WIREGUARD) - continue; - - lnk_wg = plobj->_link.netlink.lnk; - - if (!lnk_wg) - continue; - - if (NMP_OBJECT_GET_TYPE (lnk_wg) != NMP_OBJECT_TYPE_LNK_WIREGUARD) - continue; - - if (NMP_OBJECT_CAST_LNK_WIREGUARD (lnk_wg)->fwmark == table) - goto try_next_table; - } - - return table; -try_next_table: - ; - } -} - -#define PRIO_WIDTH ((guint32) 2) - -static gboolean -_auto_default_route_find_priority_exists (const NMDedupMultiHeadEntry *head_entry, - guint32 priority) -{ - NMDedupMultiIter iter; - const NMPObject *plobj; - - nmp_cache_iter_for_each (&iter, head_entry, &plobj) { - const NMPlatformRoutingRule *rr = NMP_OBJECT_CAST_ROUTING_RULE (plobj); - - /* we don't differenciate between IPv4 vs. IPv6. There should be no - * conflicting rules with the same priority. */ - if ( rr->priority >= priority - && rr->priority < priority + PRIO_WIDTH) - return TRUE; - } - - return FALSE; -} +#define AUTO_RANDOM_RANGE 500u static guint32 -_auto_default_route_find_priority (NMPlatform *platform, - const char *uuid) +_auto_default_route_get_auto_fwmark (const char *uuid) { - const NMDedupMultiHeadEntry *head_entry; guint64 rnd_seed; - const guint32 PRIME_NUMBER = 1111567573u; - const guint32 RANGE_TOP = ((32766u - 2u * PRIO_WIDTH) / PRIO_WIDTH); - const guint32 RANGE_LEN1 = 200u; - const guint32 RANGE_LEN2 = (RANGE_TOP - 100u) - RANGE_LEN1; - guint32 range_len; - guint32 range_top; - guint32 prio_candidate = 0; - guint32 i_step; - guint32 i; - /* For the auto-default-route policy routing rule we add 4 rules (2 Ipv4 and 2 IPv6). - * Hence, we choose a priority for the first (of the two rules) and the second - * rule gets priority + 1. - * We want a priority that is - * - unused so far. - * - smaller than 32766u (which is the priority of the default rules for IPv4 and IPv6) - * - stable for each connection but different between connections (we hash the UUID - * as a "random" seed) - * - if possible, close to 32766u (RANGE_LEN1). Only otherwise fallback to the entire - * range (RANGE_LEN2). + /* we use the generated number as fwmark but also as routing table for + * the default-route. + * + * We pick a number + * + * - based on the connection's UUID (as stable seed). + * - larger than 51820u (arbitrarily) + * - one out of AUTO_RANDOM_RANGE */ - rnd_seed = c_siphash_hash ((const guint8 [16]) { 0xb9, 0x39, 0x8e, 0xed, 0x15, 0xb3, 0xd1, 0xc4, 0x5f, 0x45, 0x00, 0x4f, 0xec, 0xc2, 0x2b, 0x7e }, + rnd_seed = c_siphash_hash (NM_HASH_SEED_16 (0xb9, 0x39, 0x8e, 0xed, 0x15, 0xb3, 0xd1, 0xc4, 0x5f, 0x45, 0x00, 0x4f, 0xec, 0xc2, 0x2b, 0x7e), (const guint8 *) uuid, uuid ? strlen (uuid) + 1u : 0u); - head_entry = nm_platform_lookup_object_by_addr_family (platform, - NMP_OBJECT_TYPE_ROUTING_RULE, - AF_UNSPEC); + return 51820u + (rnd_seed % AUTO_RANDOM_RANGE); +} - range_len = RANGE_LEN1; - range_top = RANGE_TOP; +#define PRIO_WIDTH 2u -again: - i_step = ((guint32) rnd_seed) % range_len; - for (i = 0; i < range_len; i++) { +static guint32 +_auto_default_route_get_auto_priority (const char *uuid) +{ + const guint32 RANGE_TOP = 32766u - 1000u; + guint64 rnd_seed; - /* we sample the range in a stable, but somewhat arbitrary order to - * find an unused priority. */ - i_step = (i_step + PRIME_NUMBER) % range_len; + /* we pick a priority for the routing rules as follows: + * + * - use the connection's UUID as stable seed for the "random" number. + * - have it smaller than RANGE_TOP (32766u - 1000u), where 32766u is the priority of the default + * rules + * - we add 2 rules (PRIO_WIDTH). Hence only pick even priorites. + * - pick one out of AUTO_RANDOM_RANGE. */ - nm_assert (i_step < range_top); + rnd_seed = c_siphash_hash (NM_HASH_SEED_16 (0x99, 0x22, 0x4d, 0x7c, 0x37, 0xda, 0x8e, 0x7b, 0x2f, 0x55, 0x16, 0x7b, 0x75, 0xda, 0x42, 0xdc), + (const guint8 *) uuid, + uuid ? strlen (uuid) + 1u : 0u); - prio_candidate = (range_top - i_step) * PRIO_WIDTH; - - nm_assert (prio_candidate < 32766u); - - if (!_auto_default_route_find_priority_exists (head_entry, prio_candidate)) - return prio_candidate; - } - - if (range_len == RANGE_LEN1) { - /* within the narrow range close to RANGE_TOP we couldn't find any unused - * priority. Retry with the entire range... */ - range_len = RANGE_LEN2; - range_top -= RANGE_LEN1; - goto again; - } - - /* Couldn't find an unused one? Very odd, this really should not happen unless there - * are thousands of rules already. Just pick the last one we sampled. */ - return prio_candidate; + return RANGE_TOP - (((rnd_seed % (PRIO_WIDTH * AUTO_RANDOM_RANGE)) / PRIO_WIDTH) * PRIO_WIDTH); } static void @@ -423,10 +317,10 @@ _auto_default_route_init (NMDeviceWireGuard *self) { NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (self); NMConnection *connection; - NMSettingWireGuard *s_wg; - gboolean enabled_v4; - gboolean enabled_v6; + gboolean enabled_v4 = FALSE; + gboolean enabled_v6 = FALSE; gboolean refreshing_only; + guint32 new_fwmark = 0; guint32 old_fwmark; char sbuf1[100]; @@ -436,40 +330,47 @@ _auto_default_route_init (NMDeviceWireGuard *self) refreshing_only = priv->auto_default_route_initialized && priv->auto_default_route_refresh; - priv->auto_default_route_refresh = FALSE; - - connection = nm_device_get_applied_connection (NM_DEVICE (self)); - - s_wg = _nm_connection_get_setting (connection, NM_TYPE_SETTING_WIREGUARD); old_fwmark = priv->auto_default_route_fwmark; - priv->auto_default_route_fwmark = nm_setting_wireguard_get_fwmark (s_wg); + connection = nm_device_get_applied_connection (NM_DEVICE (self)); + if (connection) { + NMSettingWireGuard *s_wg; - _auto_default_route_get_enabled (s_wg, - connection, - &enabled_v4, - &enabled_v6); + s_wg = _nm_connection_get_setting (connection, NM_TYPE_SETTING_WIREGUARD); + + new_fwmark = nm_setting_wireguard_get_fwmark (s_wg); + + _auto_default_route_get_enabled (s_wg, + connection, + &enabled_v4, + &enabled_v6); + } + + if ( ( enabled_v4 + || enabled_v6) + && new_fwmark == 0u) { + if (refreshing_only) + new_fwmark = old_fwmark; + else + new_fwmark = _auto_default_route_get_auto_fwmark (nm_connection_get_uuid (connection)); + } + + priv->auto_default_route_refresh = FALSE; + priv->auto_default_route_fwmark = new_fwmark; priv->auto_default_route_enabled_4 = enabled_v4; priv->auto_default_route_enabled_6 = enabled_v6; priv->auto_default_route_initialized = TRUE; - if ( ( priv->auto_default_route_enabled_4 - || priv->auto_default_route_enabled_6) - && priv->auto_default_route_fwmark == 0u) { - if (refreshing_only) - priv->auto_default_route_fwmark = old_fwmark; - else - priv->auto_default_route_fwmark = _auto_default_route_find_unused_table (nm_device_get_platform (NM_DEVICE (self))); + if (connection) { + _LOGT (LOGD_DEVICE, + "auto-default-route is %s for IPv4 and %s for IPv6%s", + priv->auto_default_route_enabled_4 ? "enabled" : "disabled", + priv->auto_default_route_enabled_6 ? "enabled" : "disabled", + priv->auto_default_route_enabled_4 || priv->auto_default_route_enabled_6 + ? nm_sprintf_buf (sbuf1, " (fwmark 0x%x)", priv->auto_default_route_fwmark) + : ""); } - - _LOGT (LOGD_DEVICE, - "auto-default-route is %s for IPv4 and %s for IPv6%s", - priv->auto_default_route_enabled_4 ? "enabled" : "disabled", - priv->auto_default_route_enabled_6 ? "enabled" : "disabled", - priv->auto_default_route_enabled_4 || priv->auto_default_route_enabled_6 - ? nm_sprintf_buf (sbuf1, " (fwmark 0x%x)", priv->auto_default_route_fwmark) - : ""); } static GPtrArray * @@ -506,8 +407,7 @@ get_extra_rules (NMDevice *device) if (priv->auto_default_route_priority_initialized) priority = priv->auto_default_route_priority; else { - priority = _auto_default_route_find_priority (nm_device_get_platform (device), - nm_connection_get_uuid (connection)); + priority = _auto_default_route_get_auto_priority (nm_connection_get_uuid (connection)); priv->auto_default_route_priority = priority; priv->auto_default_route_priority_initialized = TRUE; } @@ -1617,6 +1517,17 @@ link_config_delayed_resolver_cb (gpointer user_data) return G_SOURCE_REMOVE; } +static NMActStageReturn +act_stage1_prepare (NMDevice *device, NMDeviceStateReason *out_failure_reason) +{ + NMDeviceWireGuardPrivate *priv = NM_DEVICE_WIREGUARD_GET_PRIVATE (device); + + priv->auto_default_route_initialized = FALSE; + priv->auto_default_route_priority_initialized = FALSE; + + return NM_DEVICE_CLASS (nm_device_wireguard_parent_class)->act_stage1_prepare (device, out_failure_reason); +} + static NMActStageReturn act_stage2_config (NMDevice *device, NMDeviceStateReason *out_failure_reason) @@ -2057,6 +1968,7 @@ nm_device_wireguard_class_init (NMDeviceWireGuardClass *klass) device_class->connection_type_check_compatible = NM_SETTING_WIREGUARD_SETTING_NAME; device_class->link_types = NM_DEVICE_DEFINE_LINK_TYPES (NM_LINK_TYPE_WIREGUARD); + device_class->act_stage1_prepare = act_stage1_prepare; device_class->state_changed = device_state_changed; device_class->create_and_realize = create_and_realize; device_class->act_stage2_config = act_stage2_config;