diff --git a/Makefile.am b/Makefile.am index 92ad10c36c..e8a6a8aeea 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1323,9 +1323,6 @@ src_libNetworkManagerBase_la_SOURCES = \ src/nm-ip6-config.c \ src/nm-ip6-config.h \ \ - src/nm-route-manager.c \ - src/nm-route-manager.h \ - \ src/dhcp/nm-dhcp-client.c \ src/dhcp/nm-dhcp-client.h \ src/dhcp/nm-dhcp-client-logging.h \ @@ -2961,8 +2958,6 @@ check_programs += \ src/tests/test-general-with-expect \ src/tests/test-ip4-config \ src/tests/test-ip6-config \ - src/tests/test-route-manager-linux \ - src/tests/test-route-manager-fake \ src/tests/test-dcb \ src/tests/test-systemd \ src/tests/test-resolvconf-capture \ @@ -3010,28 +3005,6 @@ $(src_tests_test_general_with_expect_OBJECTS): $(libnm_core_lib_h_pub_mkenums) $(src_tests_test_wired_defname_OBJECTS): $(libnm_core_lib_h_pub_mkenums) $(src_tests_test_utils_OBJECTS): $(libnm_core_lib_h_pub_mkenums) -src_tests_test_route_manager_ldflags = \ - $(CODE_COVERAGE_LDFLAGS) - -src_tests_test_route_manager_ldadd = \ - src/libNetworkManagerTest.la \ - $(GLIB_LIBS) \ - $(LIBUDEV_LIBS) \ - $(LIBNL_LIBS) - -src_tests_test_route_manager_fake_SOURCES = src/tests/test-route-manager.c -src_tests_test_route_manager_fake_CPPFLAGS = $(src_tests_cppflags_fake) -src_tests_test_route_manager_fake_LDFLAGS = $(src_tests_test_route_manager_ldflags) -src_tests_test_route_manager_fake_LDADD = $(src_tests_test_route_manager_ldadd) - -src_tests_test_route_manager_linux_SOURCES = src/tests/test-route-manager.c -src_tests_test_route_manager_linux_CPPFLAGS = $(src_tests_cppflags_linux) -src_tests_test_route_manager_linux_LDFLAGS = $(src_tests_test_route_manager_ldflags) -src_tests_test_route_manager_linux_LDADD = $(src_tests_test_route_manager_ldadd) - -$(src_tests_test_route_manager_fake_OBJECTS): $(libnm_core_lib_h_pub_mkenums) -$(src_tests_test_route_manager_linux_OBJECTS): $(libnm_core_lib_h_pub_mkenums) - src_tests_test_systemd_CPPFLAGS = $(src_libsystemd_nm_la_cppflags) src_tests_test_systemd_LDADD = \ src/libsystemd-nm.la \ diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 20b27de9f5..00ac2cebe5 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -78,6 +78,26 @@ G_STATIC_ASSERT (sizeof (bool) <= sizeof (int)); /*****************************************************************************/ +static void +test_nm_g_slice_free_fcn (void) +{ + gpointer p; + + p = g_slice_new (gint64); + (nm_g_slice_free_fcn (gint64)) (p); + + p = g_slice_new (gint32); + (nm_g_slice_free_fcn (gint32)) (p); + + p = g_slice_new (gint); + (nm_g_slice_free_fcn (gint)) (p); + + p = g_slice_new (gint64); + nm_g_slice_free_fcn_gint64 (p); +} + +/*****************************************************************************/ + typedef struct { int val; int idx; @@ -6177,6 +6197,7 @@ int main (int argc, char **argv) { nmtst_init (&argc, &argv, TRUE); + g_test_add_func ("/core/general/test_nm_g_slice_free_fcn", test_nm_g_slice_free_fcn); g_test_add_func ("/core/general/test_c_list_sort", test_c_list_sort); g_test_add_func ("/core/general/test_dedup_multi", test_dedup_multi); g_test_add_func ("/core/general/test_utils_str_utf8safe", test_utils_str_utf8safe); diff --git a/shared/nm-utils/nm-dedup-multi.c b/shared/nm-utils/nm-dedup-multi.c index ac47d11b97..51a866abca 100644 --- a/shared/nm-utils/nm-dedup-multi.c +++ b/shared/nm-utils/nm-dedup-multi.c @@ -77,7 +77,7 @@ nm_dedup_multi_idx_type_init (NMDedupMultiIdxType *idx_type, /*****************************************************************************/ static NMDedupMultiEntry * -_entry_lookup_obj (NMDedupMultiIndex *self, +_entry_lookup_obj (const NMDedupMultiIndex *self, const NMDedupMultiIdxType *idx_type, const NMDedupMultiObj *obj) { @@ -92,7 +92,7 @@ _entry_lookup_obj (NMDedupMultiIndex *self, } static NMDedupMultiHeadEntry * -_entry_lookup_head (NMDedupMultiIndex *self, +_entry_lookup_head (const NMDedupMultiIndex *self, const NMDedupMultiIdxType *idx_type, const NMDedupMultiObj *obj) { @@ -253,6 +253,8 @@ _add (NMDedupMultiIndex *self, })); if (entry) { + gboolean changed = FALSE; + nm_dedup_multi_entry_set_dirty (entry, FALSE); nm_assert (!head_existing || entry->head == head_existing); @@ -270,11 +272,13 @@ _add (NMDedupMultiIndex *self, && entry->lst_entries.next != &entry_order->lst_entries) { c_list_unlink (&entry->lst_entries); c_list_link_before ((CList *) &entry_order->lst_entries, &entry->lst_entries); + changed = TRUE; } } else { if (entry->lst_entries.prev != &entry->head->lst_entries_head) { c_list_unlink (&entry->lst_entries); c_list_link_front ((CList *) &entry->head->lst_entries_head, &entry->lst_entries); + changed = TRUE; } } break; @@ -284,11 +288,13 @@ _add (NMDedupMultiIndex *self, && entry->lst_entries.prev != &entry_order->lst_entries) { c_list_unlink (&entry->lst_entries); c_list_link_after ((CList *) &entry_order->lst_entries, &entry->lst_entries); + changed = TRUE; } } else { if (entry->lst_entries.next != &entry->head->lst_entries_head) { c_list_unlink (&entry->lst_entries); c_list_link_tail ((CList *) &entry->head->lst_entries_head, &entry->lst_entries); + changed = TRUE; } } break; @@ -303,7 +309,7 @@ _add (NMDedupMultiIndex *self, entry->obj)) { NM_SET_OUT (out_entry, entry); NM_SET_OUT (out_obj_old, nm_dedup_multi_obj_ref (entry->obj)); - return FALSE; + return changed; } obj_new = nm_dedup_multi_index_obj_intern (self, obj); @@ -620,13 +626,22 @@ nm_dedup_multi_index_remove_entry (NMDedupMultiIndex *self, guint nm_dedup_multi_index_remove_obj (NMDedupMultiIndex *self, NMDedupMultiIdxType *idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj) + /*const NMDedupMultiObj * */ gconstpointer obj, + /*const NMDedupMultiObj ** */ gconstpointer *out_obj) { const NMDedupMultiEntry *entry; entry = nm_dedup_multi_index_lookup_obj (self, idx_type, obj); - if (!entry) + if (!entry) { + NM_SET_OUT (out_obj, NULL); return 0; + } + + /* since we are about to remove the object, we obviously pass + * a reference to @out_obj, the caller MUST unref the object, + * if he chooses to provide @out_obj. */ + NM_SET_OUT (out_obj, nm_dedup_multi_obj_ref (entry->obj)); + _remove_entry (self, (NMDedupMultiEntry *) entry, NULL); return 1; } @@ -667,7 +682,7 @@ nm_dedup_multi_index_remove_idx (NMDedupMultiIndex *self, * Returns: the cache entry or %NULL if the entry wasn't found. */ const NMDedupMultiEntry * -nm_dedup_multi_index_lookup_obj (NMDedupMultiIndex *self, +nm_dedup_multi_index_lookup_obj (const NMDedupMultiIndex *self, const NMDedupMultiIdxType *idx_type, /*const NMDedupMultiObj * */ gconstpointer obj) { @@ -693,7 +708,7 @@ nm_dedup_multi_index_lookup_obj (NMDedupMultiIndex *self, * Returns: the cache entry or %NULL if the entry wasn't found. */ const NMDedupMultiHeadEntry * -nm_dedup_multi_index_lookup_head (NMDedupMultiIndex *self, +nm_dedup_multi_index_lookup_head (const NMDedupMultiIndex *self, const NMDedupMultiIdxType *idx_type, /*const NMDedupMultiObj * */ gconstpointer obj) { diff --git a/shared/nm-utils/nm-dedup-multi.h b/shared/nm-utils/nm-dedup-multi.h index b6cfc43209..ff505b696f 100644 --- a/shared/nm-utils/nm-dedup-multi.h +++ b/shared/nm-utils/nm-dedup-multi.h @@ -278,11 +278,11 @@ gboolean nm_dedup_multi_index_add (NMDedupMultiIndex *self, const NMDedupMultiEntry **out_entry, /* const NMDedupMultiObj ** */ gpointer out_obj_old); -const NMDedupMultiEntry *nm_dedup_multi_index_lookup_obj (NMDedupMultiIndex *self, +const NMDedupMultiEntry *nm_dedup_multi_index_lookup_obj (const NMDedupMultiIndex *self, const NMDedupMultiIdxType *idx_type, /*const NMDedupMultiObj * */ gconstpointer obj); -const NMDedupMultiHeadEntry *nm_dedup_multi_index_lookup_head (NMDedupMultiIndex *self, +const NMDedupMultiHeadEntry *nm_dedup_multi_index_lookup_head (const NMDedupMultiIndex *self, const NMDedupMultiIdxType *idx_type, /*const NMDedupMultiObj * */ gconstpointer obj); @@ -291,7 +291,8 @@ guint nm_dedup_multi_index_remove_entry (NMDedupMultiIndex *self, guint nm_dedup_multi_index_remove_obj (NMDedupMultiIndex *self, NMDedupMultiIdxType *idx_type, - /*const NMDedupMultiObj * */ gconstpointer obj); + /*const NMDedupMultiObj * */ gconstpointer obj, + /*const NMDedupMultiObj ** */ gconstpointer *out_obj); guint nm_dedup_multi_index_remove_head (NMDedupMultiIndex *self, NMDedupMultiIdxType *idx_type, diff --git a/shared/nm-utils/nm-shared-utils.h b/shared/nm-utils/nm-shared-utils.h index c4306f4120..6c8eb4dac2 100644 --- a/shared/nm-utils/nm-shared-utils.h +++ b/shared/nm-utils/nm-shared-utils.h @@ -150,6 +150,53 @@ gint _nm_utils_ascii_str_to_bool (const char *str, /*****************************************************************************/ +#define _nm_g_slice_free_fcn_define(mem_size) \ +static inline void \ +_nm_g_slice_free_fcn_##mem_size (gpointer mem_block) \ +{ \ + g_slice_free1 (mem_size, mem_block); \ +} + +_nm_g_slice_free_fcn_define (1) +_nm_g_slice_free_fcn_define (2) +_nm_g_slice_free_fcn_define (4) +_nm_g_slice_free_fcn_define (8) +_nm_g_slice_free_fcn_define (16) + +#define _nm_g_slice_free_fcn1(mem_size) \ + ({ \ + void (*_fcn) (gpointer); \ + \ + /* If mem_size is a compile time constant, the compiler + * will be able to optimize this. Hence, you don't want + * to call this with a non-constant size argument. */ \ + switch (mem_size) { \ + case 1: _fcn = _nm_g_slice_free_fcn_1; break; \ + case 2: _fcn = _nm_g_slice_free_fcn_2; break; \ + case 4: _fcn = _nm_g_slice_free_fcn_4; break; \ + case 8: _fcn = _nm_g_slice_free_fcn_8; break; \ + case 16: _fcn = _nm_g_slice_free_fcn_16; break; \ + default: g_assert_not_reached (); _fcn = NULL; break; \ + } \ + _fcn; \ + }) + +/** + * nm_g_slice_free_fcn: + * @type: type argument for sizeof() operator that you would + * pass to g_slice_new(). + * + * Returns: a function pointer with GDestroyNotify signature + * for g_slice_free(type,*). + * + * Only certain types are implemented. You'll get an assertion + * using the wrong type. */ +#define nm_g_slice_free_fcn(type) (_nm_g_slice_free_fcn1 (sizeof (type))) + +#define nm_g_slice_free_fcn_gint64 (nm_g_slice_free_fcn (gint64)) + +/*****************************************************************************/ + /** * NMUtilsError: * @NM_UTILS_ERROR_UNKNOWN: unknown or unclassified error diff --git a/src/devices/nm-device-bond.c b/src/devices/nm-device-bond.c index 26e50aabcc..910dd0bfe3 100644 --- a/src/devices/nm-device-bond.c +++ b/src/devices/nm-device-bond.c @@ -483,7 +483,7 @@ create_and_realize (NMDevice *device, "Failed to create bond interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } return TRUE; diff --git a/src/devices/nm-device-bridge.c b/src/devices/nm-device-bridge.c index 26ebe5154f..74689aef6a 100644 --- a/src/devices/nm-device-bridge.c +++ b/src/devices/nm-device-bridge.c @@ -473,7 +473,7 @@ create_and_realize (NMDevice *device, "Failed to create bridge interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } diff --git a/src/devices/nm-device-dummy.c b/src/devices/nm-device-dummy.c index dce4f7bc56..085c44e6a3 100644 --- a/src/devices/nm-device-dummy.c +++ b/src/devices/nm-device-dummy.c @@ -112,7 +112,7 @@ create_and_realize (NMDevice *device, "Failed to create dummy interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } diff --git a/src/devices/nm-device-infiniband.c b/src/devices/nm-device-infiniband.c index 7e04127035..12b0845485 100644 --- a/src/devices/nm-device-infiniband.c +++ b/src/devices/nm-device-infiniband.c @@ -295,7 +295,7 @@ create_and_realize (NMDevice *device, "Failed to create InfiniBand P_Key interface '%s' for '%s': %s", nm_device_get_iface (device), nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } @@ -324,7 +324,7 @@ unrealize (NMDevice *device, GError **error) g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, "Failed to remove InfiniBand P_Key interface '%s': %s", nm_device_get_iface (device), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } diff --git a/src/devices/nm-device-ip-tunnel.c b/src/devices/nm-device-ip-tunnel.c index 2f505ef4cf..317a5d4e74 100644 --- a/src/devices/nm-device-ip-tunnel.c +++ b/src/devices/nm-device-ip-tunnel.c @@ -647,7 +647,7 @@ create_and_realize (NMDevice *device, "Failed to create GRE interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } break; @@ -670,10 +670,10 @@ create_and_realize (NMDevice *device, plerr = nm_platform_link_sit_add (nm_device_get_platform (device), iface, &lnk_sit, out_plink); if (plerr != NM_PLATFORM_ERROR_SUCCESS) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, - "Failed to create SIT interface '%s' for '%s': %s", - iface, - nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + "Failed to create SIT interface '%s' for '%s': %s", + iface, + nm_connection_get_id (connection), + nm_platform_error_to_string_a (plerr)); return FALSE; } break; @@ -696,10 +696,10 @@ create_and_realize (NMDevice *device, plerr = nm_platform_link_ipip_add (nm_device_get_platform (device), iface, &lnk_ipip, out_plink); if (plerr != NM_PLATFORM_ERROR_SUCCESS) { g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED, - "Failed to create IPIP interface '%s' for '%s': %s", - iface, - nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + "Failed to create IPIP interface '%s' for '%s': %s", + iface, + nm_connection_get_id (connection), + nm_platform_error_to_string_a (plerr)); return FALSE; } break; @@ -728,7 +728,7 @@ create_and_realize (NMDevice *device, "Failed to create IPIP interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } break; diff --git a/src/devices/nm-device-macsec.c b/src/devices/nm-device-macsec.c index 8add3f6fb8..3a43dd5c69 100644 --- a/src/devices/nm-device-macsec.c +++ b/src/devices/nm-device-macsec.c @@ -720,7 +720,7 @@ create_and_realize (NMDevice *device, "Failed to create macsec interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } diff --git a/src/devices/nm-device-macvlan.c b/src/devices/nm-device-macvlan.c index cea2b984fc..5d6328f1f4 100644 --- a/src/devices/nm-device-macvlan.c +++ b/src/devices/nm-device-macvlan.c @@ -258,7 +258,7 @@ create_and_realize (NMDevice *device, lnk.tap ? "macvtap" : "macvlan", iface, nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } diff --git a/src/devices/nm-device-tun.c b/src/devices/nm-device-tun.c index b4af441662..a7d7c0bf19 100644 --- a/src/devices/nm-device-tun.c +++ b/src/devices/nm-device-tun.c @@ -244,7 +244,7 @@ create_and_realize (NMDevice *device, "Failed to create TUN/TAP interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } diff --git a/src/devices/nm-device-vlan.c b/src/devices/nm-device-vlan.c index ee3755d8d4..c6c3f46f94 100644 --- a/src/devices/nm-device-vlan.c +++ b/src/devices/nm-device-vlan.c @@ -266,7 +266,7 @@ create_and_realize (NMDevice *device, "Failed to create VLAN interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } diff --git a/src/devices/nm-device-vxlan.c b/src/devices/nm-device-vxlan.c index d0b88874c1..204b85c001 100644 --- a/src/devices/nm-device-vxlan.c +++ b/src/devices/nm-device-vxlan.c @@ -223,7 +223,7 @@ create_and_realize (NMDevice *device, "Failed to create VXLAN interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 006d4e9227..b7ff53d406 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -66,7 +66,6 @@ #include "dns/nm-dns-manager.h" #include "nm-core-internal.h" #include "nm-default-route-manager.h" -#include "nm-route-manager.h" #include "systemd/nm-sd.h" #include "nm-lldp-listener.h" #include "nm-audit-manager.h" @@ -491,16 +490,14 @@ static void nm_device_set_proxy_config (NMDevice *self, const char *pac_url); static gboolean nm_device_set_ip4_config (NMDevice *self, NMIP4Config *config, guint32 default_route_metric, - gboolean commit, - gboolean routes_full_sync); + gboolean commit); static gboolean ip4_config_merge_and_apply (NMDevice *self, NMIP4Config *config, gboolean commit); static gboolean nm_device_set_ip6_config (NMDevice *self, NMIP6Config *config, - gboolean commit, - gboolean routes_full_sync); + gboolean commit); static gboolean ip6_config_merge_and_apply (NMDevice *self, gboolean commit); @@ -2767,6 +2764,101 @@ link_changed_cb (NMPlatform *platform, } } +/*****************************************************************************/ + +typedef struct { + in_addr_t network; + guint8 plen; +} IP4RPFilterData; + +static guint +_v4_has_shadowed_routes_detect_hash (const IP4RPFilterData *d) +{ + guint h = 0; + + h = NM_HASH_COMBINE (h, d->network); + h = NM_HASH_COMBINE (h, d->plen); + return h; +} + +static gboolean +_v4_has_shadowed_routes_detect_equal (const IP4RPFilterData *d1, const IP4RPFilterData *d2) +{ + return d1->network == d2->network && d1->plen == d2->plen; +} + +static gboolean +_v4_has_shadowed_routes_detect (NMDevice *self) +{ + NMPlatform *platform; + int ifindex; + NMPLookup lookup; + const NMDedupMultiHeadEntry *head_entry; + NMDedupMultiIter iter; + const NMPObject *o; + guint data_len; + gs_unref_hashtable GHashTable *data_hash = NULL; + gs_free IP4RPFilterData *data_arr = NULL; + + ifindex = nm_device_get_ip_ifindex (self); + if (ifindex <= 0) + return FALSE; + + platform = nm_device_get_platform (self); + + head_entry = nm_platform_lookup (platform, + nmp_lookup_init_addrroute (&lookup, + NMP_OBJECT_TYPE_IP4_ROUTE, + ifindex)); + if (!head_entry) + return FALSE; + + /* first, create a lookup index @data_hash for all network/plen pairs. */ + data_len = 0; + data_arr = g_new (IP4RPFilterData, head_entry->len); + data_hash = g_hash_table_new ((GHashFunc) _v4_has_shadowed_routes_detect_hash, + (GEqualFunc) _v4_has_shadowed_routes_detect_equal); + + nmp_cache_iter_for_each (&iter, head_entry, &o) { + const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (o); + IP4RPFilterData *d; + + nm_assert (r->ifindex == ifindex); + + if ( NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r) + || r->table_coerced) + continue; + + d = &data_arr[data_len++]; + d->network = nm_utils_ip4_address_clear_host_address (r->network, r->plen); + d->plen = r->plen; + g_hash_table_add (data_hash, d); + } + + /* then, search if there is any route on another interface with the same + * network/plen destination. If yes, we consider this a multihoming + * setup. */ + head_entry = nm_platform_lookup (platform, + nmp_lookup_init_obj_type (&lookup, + NMP_OBJECT_TYPE_IP4_ROUTE)); + nmp_cache_iter_for_each (&iter, head_entry, &o) { + const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (o); + IP4RPFilterData d; + + if ( r->ifindex == ifindex + || NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r) + || r->table_coerced) + continue; + + d.network = nm_utils_ip4_address_clear_host_address (r->network, r->plen); + d.plen = r->plen; + if (g_hash_table_contains (data_hash, &d)) + return TRUE; + } + + return FALSE; +} + static void ip4_rp_filter_update (NMDevice *self) { @@ -2792,20 +2884,6 @@ ip4_rp_filter_update (NMDevice *self) } } -static void -ip4_routes_changed_changed_cb (NMRouteManager *route_manager, NMDevice *self) -{ - NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - int ifindex = nm_device_get_ip_ifindex (self); - - if (nm_device_sys_iface_state_is_external_or_assume (self)) - return; - - priv->v4_has_shadowed_routes = nm_route_manager_ip4_routes_shadowed (route_manager, - ifindex); - ip4_rp_filter_update (self); -} - static void link_changed (NMDevice *self, const NMPlatformLink *pllink) { @@ -3846,8 +3924,8 @@ nm_device_removed (NMDevice *self, gboolean unconfigure_ip_config) _update_default_route (self, AF_INET6, priv->default_route.v6_has, TRUE); _update_default_route (self, AF_INET, FALSE, TRUE); _update_default_route (self, AF_INET6, FALSE, TRUE); - nm_device_set_ip4_config (self, NULL, 0, FALSE, FALSE); - nm_device_set_ip6_config (self, NULL, FALSE, FALSE); + nm_device_set_ip4_config (self, NULL, 0, FALSE); + nm_device_set_ip6_config (self, NULL, FALSE); } static gboolean @@ -5461,9 +5539,9 @@ _device_get_default_route_from_platform (NMDevice *self, int addr_family, NMPlat guint32 m; const NMPlatformIPRoute *r = NMP_OBJECT_CAST_IP_ROUTE (plobj); - if (r->ifindex != ifindex) - continue; - if (r->rt_source == NM_IP_CONFIG_SOURCE_RTPROT_KERNEL) + if ( r->ifindex != ifindex + || r->rt_source == NM_IP_CONFIG_SOURCE_RTPROT_KERNEL + || r->table_coerced) continue; /* if there are several default routes, find the one with the best metric */ @@ -5582,7 +5660,6 @@ ip4_config_merge_and_apply (NMDevice *self, const guint32 default_route_metric = nm_device_get_ip4_route_metric (self); guint32 gateway; gboolean connection_has_default_route, connection_is_never_default; - gboolean routes_full_sync; gboolean ignore_auto_routes = FALSE; gboolean ignore_auto_dns = FALSE; gboolean auto_method = FALSE; @@ -5749,11 +5826,7 @@ END_ADD_DEFAULT_ROUTE: NM_DEVICE_GET_CLASS (self)->ip4_config_pre_commit (self, composite); } - routes_full_sync = commit - && priv->v4_commit_first_time - && !nm_device_sys_iface_state_is_external_or_assume (self); - - success = nm_device_set_ip4_config (self, composite, default_route_metric, commit, routes_full_sync); + success = nm_device_set_ip4_config (self, composite, default_route_metric, commit); g_object_unref (composite); if (commit) @@ -6308,7 +6381,6 @@ ip6_config_merge_and_apply (NMDevice *self, gboolean has_direct_route; const struct in6_addr *gateway; gboolean connection_has_default_route, connection_is_never_default; - gboolean routes_full_sync; gboolean ignore_auto_routes = FALSE; gboolean ignore_auto_dns = FALSE; gboolean auto_method = FALSE; @@ -6497,11 +6569,7 @@ END_ADD_DEFAULT_ROUTE: } } - routes_full_sync = commit - && priv->v6_commit_first_time - && !nm_device_sys_iface_state_is_external_or_assume (self); - - success = nm_device_set_ip6_config (self, composite, commit, routes_full_sync); + success = nm_device_set_ip6_config (self, composite, commit); g_object_unref (composite); if (commit) priv->v6_commit_first_time = FALSE; @@ -7319,26 +7387,7 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in { NMNDiscConfigMap changed = changed_int; NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self); - int i; - int system_support; - guint32 ifa_flags = 0x00; - - /* - * Check, whether kernel is recent enough to help user space handling RA. - * If it's not supported, we have no ipv6-privacy and must add autoconf - * addresses as /128. The reason for the /128 is to prevent the kernel - * from adding a prefix route for this address. - **/ - system_support = nm_platform_check_support_kernel_extended_ifa_flags (nm_device_get_platform (self)); - - if (system_support) - ifa_flags = IFA_F_NOPREFIXROUTE; - if ( priv->ndisc_use_tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR - || priv->ndisc_use_tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR) - { - /* without system_support, this flag will be ignored. Still set it, doesn't seem to do any harm. */ - ifa_flags |= IFA_F_MANAGETEMPADDR; - } + guint i; g_return_if_fail (priv->act_request); @@ -7354,49 +7403,35 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in } if (changed & NM_NDISC_CONFIG_ADDRESSES) { - /* Rebuild address list from neighbor discovery cache. */ - nm_ip6_config_reset_addresses (priv->ac_ip6_config); + guint8 plen; + guint32 ifa_flags; - /* ndisc->addresses contains at most max_addresses entries. - * This is different from what the kernel does, which - * also counts static and temporary addresses when checking - * max_addresses. - **/ - for (i = 0; i < rdata->addresses_n; i++) { - const NMNDiscAddress *discovered_address = &rdata->addresses[i]; - NMPlatformIP6Address address; + /* Check, whether kernel is recent enough to help user space handling RA. + * If it's not supported, we have no ipv6-privacy and must add autoconf + * addresses as /128. The reason for the /128 is to prevent the kernel + * from adding a prefix route for this address. */ + ifa_flags = 0; + if (nm_platform_check_support_kernel_extended_ifa_flags (nm_device_get_platform (self))) { + ifa_flags |= IFA_F_NOPREFIXROUTE; + if (NM_IN_SET (priv->ndisc_use_tempaddr, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, + NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)) + ifa_flags |= IFA_F_MANAGETEMPADDR; + plen = 64; + } else + plen = 128; - memset (&address, 0, sizeof (address)); - address.address = discovered_address->address; - address.plen = system_support ? 64 : 128; - address.timestamp = discovered_address->timestamp; - address.lifetime = discovered_address->lifetime; - address.preferred = discovered_address->preferred; - if (address.preferred > address.lifetime) - address.preferred = address.lifetime; - address.addr_source = NM_IP_CONFIG_SOURCE_NDISC; - address.n_ifa_flags = ifa_flags; - - nm_ip6_config_add_address (priv->ac_ip6_config, &address); - } + nm_ip6_config_reset_addresses_ndisc (priv->ac_ip6_config, + rdata->addresses, + rdata->addresses_n, + plen, + ifa_flags); } if (changed & NM_NDISC_CONFIG_ROUTES) { - /* Rebuild route list from neighbor discovery cache. */ - nm_ip6_config_reset_routes (priv->ac_ip6_config); - - for (i = 0; i < rdata->routes_n; i++) { - const NMNDiscRoute *discovered_route = &rdata->routes[i]; - const NMPlatformIP6Route route = { - .network = discovered_route->network, - .plen = discovered_route->plen, - .gateway = discovered_route->gateway, - .rt_source = NM_IP_CONFIG_SOURCE_NDISC, - .metric = nm_device_get_ip6_route_metric (self), - }; - - nm_ip6_config_add_route (priv->ac_ip6_config, &route); - } + nm_ip6_config_reset_routes_ndisc (priv->ac_ip6_config, + rdata->routes, + rdata->routes_n, + nm_device_get_ip6_route_metric (self)); } if (changed & NM_NDISC_CONFIG_DNS_SERVERS) { @@ -7696,7 +7731,7 @@ set_nm_ipv6ll (NMDevice *self, gboolean enable) LOGD_IP6, "failed to %s userspace IPv6LL address handling (%s)", detail, - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); } if (enable) { @@ -9853,46 +9888,35 @@ static gboolean nm_device_set_ip4_config (NMDevice *self, NMIP4Config *new_config, guint32 default_route_metric, - gboolean commit, - gboolean routes_full_sync) + gboolean commit) { NMDevicePrivate *priv; NMIP4Config *old_config = NULL; gboolean has_changes = FALSE; gboolean success = TRUE; gboolean def_route_changed; - int ip_ifindex, config_ifindex; + int ip_ifindex = 0; g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - _LOGD (LOGD_IP4, "ip4-config: update (commit=%d, routes-full-sync=%d, new-config=%p)", - commit, routes_full_sync, new_config); + _LOGD (LOGD_IP4, "ip4-config: update (commit=%d, new-config=%p)", + commit, new_config); + + nm_assert ( !new_config + || ( new_config + && ((ip_ifindex = nm_device_get_ip_ifindex (self)) > 0) + && ip_ifindex == nm_ip4_config_get_ifindex (new_config))); priv = NM_DEVICE_GET_PRIVATE (self); - ip_ifindex = nm_device_get_ip_ifindex (self); - - if (new_config) { - config_ifindex = nm_ip4_config_get_ifindex (new_config); - if (config_ifindex > 0) - g_return_val_if_fail (ip_ifindex == config_ifindex, FALSE); - } old_config = priv->ip4_config; /* Always commit to nm-platform to update lifetimes */ if (commit && new_config) { - gboolean assumed = nm_device_sys_iface_state_is_external_or_assume (self); - _commit_mtu (self, new_config); - /* For assumed devices we must not touch the kernel-routes, such as the device-route. - * FIXME: this is wrong in case where "assumed" means "take-over-seamlessly". In this - * case, we should manage the device route, for example on new DHCP lease. */ success = nm_ip4_config_commit (new_config, nm_device_get_platform (self), - nm_netns_get_route_manager (priv->netns), - ip_ifindex, - routes_full_sync, - assumed ? (gint64) -1 : (gint64) default_route_metric); + default_route_metric); } if (new_config) { @@ -10031,29 +10055,26 @@ nm_device_set_wwan_ip4_config (NMDevice *self, NMIP4Config *config) static gboolean nm_device_set_ip6_config (NMDevice *self, NMIP6Config *new_config, - gboolean commit, - gboolean routes_full_sync) + gboolean commit) { NMDevicePrivate *priv; NMIP6Config *old_config = NULL; gboolean has_changes = FALSE; gboolean success = TRUE; gboolean def_route_changed; - int ip_ifindex, config_ifindex; + int ip_ifindex = 0; g_return_val_if_fail (NM_IS_DEVICE (self), FALSE); - _LOGD (LOGD_IP6, "ip6-config: update (commit=%d, routes-full-sync=%d, new-config=%p)", - commit, routes_full_sync, new_config); + _LOGD (LOGD_IP6, "ip6-config: update (commit=%d, new-config=%p)", + commit, new_config); + + nm_assert ( !new_config + || ( new_config + && ((ip_ifindex = nm_device_get_ip_ifindex (self)) > 0) + && ip_ifindex == nm_ip6_config_get_ifindex (new_config))); priv = NM_DEVICE_GET_PRIVATE (self); - ip_ifindex = nm_device_get_ip_ifindex (self); - - if (new_config) { - config_ifindex = nm_ip6_config_get_ifindex (new_config); - if (config_ifindex > 0) - g_return_val_if_fail (ip_ifindex == config_ifindex, FALSE); - } old_config = priv->ip6_config; @@ -10061,10 +10082,7 @@ nm_device_set_ip6_config (NMDevice *self, if (commit && new_config) { _commit_mtu (self, priv->ip4_config); success = nm_ip6_config_commit (new_config, - nm_device_get_platform (self), - nm_netns_get_route_manager (priv->netns), - ip_ifindex, - routes_full_sync); + nm_device_get_platform (self)); } if (new_config) { @@ -10900,6 +10918,11 @@ queued_ip4_config_change (gpointer user_data) set_unmanaged_external_down (self, TRUE); + if (!nm_device_sys_iface_state_is_external_or_assume (self)) { + priv->v4_has_shadowed_routes = _v4_has_shadowed_routes_detect (self);; + ip4_rp_filter_update (self); + } + return FALSE; } @@ -12105,8 +12128,8 @@ _cleanup_generic_post (NMDevice *self, CleanupType cleanup_type) /* Clean up IP configs; this does not actually deconfigure the * interface; the caller must flush routes and addresses explicitly. */ - nm_device_set_ip4_config (self, NULL, 0, TRUE, TRUE); - nm_device_set_ip6_config (self, NULL, TRUE, TRUE); + nm_device_set_ip4_config (self, NULL, 0, TRUE); + nm_device_set_ip6_config (self, NULL, TRUE); g_clear_object (&priv->proxy_config); g_clear_object (&priv->con_ip4_config); g_clear_object (&priv->dev_ip4_config); @@ -12194,18 +12217,24 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean if (NM_DEVICE_GET_CLASS (self)->deactivate) NM_DEVICE_GET_CLASS (self)->deactivate (self); + ifindex = nm_device_get_ip_ifindex (self); + if (cleanup_type == CLEANUP_TYPE_DECONFIGURE) { /* master: release slaves */ nm_device_master_release_slaves (self); /* Take out any entries in the routing table and any IP address the device had. */ - ifindex = nm_device_get_ip_ifindex (self); if (ifindex > 0) { - nm_route_manager_route_flush (nm_netns_get_route_manager (priv->netns), ifindex); - nm_platform_address_flush (nm_device_get_platform (self), ifindex); + NMPlatform *platform = nm_device_get_platform (self); + + nm_platform_ip_route_flush (platform, AF_UNSPEC, ifindex); + nm_platform_ip_address_flush (platform, AF_UNSPEC, ifindex); } } + if (ifindex > 0) + nm_platform_ip4_dev_route_blacklist_set (nm_device_get_platform (self), ifindex, NULL); + /* slave: mark no longer enslaved */ if ( priv->master && nm_platform_link_get_master (nm_device_get_platform (self), priv->ifindex) <= 0) @@ -13370,7 +13399,7 @@ handle_fail: _NMLOG (plerr == NM_PLATFORM_ERROR_NOT_FOUND ? LOGL_DEBUG : LOGL_WARN, LOGD_DEVICE, "set-hw-addr: failed to %s MAC address to %s (%s) (%s)", operation, addr, detail, - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); } if (was_up) { @@ -13851,9 +13880,6 @@ constructed (GObject *object) g_signal_connect (platform, NM_PLATFORM_SIGNAL_IP6_ROUTE_CHANGED, G_CALLBACK (device_ipx_changed), self); g_signal_connect (platform, NM_PLATFORM_SIGNAL_LINK_CHANGED, G_CALLBACK (link_changed_cb), self); - g_signal_connect (nm_netns_get_route_manager (priv->netns), NM_ROUTE_MANAGER_IP4_ROUTES_CHANGED, - G_CALLBACK (ip4_routes_changed_changed_cb), self); - priv->settings = g_object_ref (NM_SETTINGS_GET); g_assert (priv->settings); @@ -13894,9 +13920,6 @@ dispose (GObject *object) g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (device_ipx_changed), self); g_signal_handlers_disconnect_by_func (platform, G_CALLBACK (link_changed_cb), self); - g_signal_handlers_disconnect_by_func (nm_netns_get_route_manager (priv->netns), - G_CALLBACK (ip4_routes_changed_changed_cb), self); - g_slist_free_full (priv->arping.dad_list, (GDestroyNotify) nm_arping_manager_destroy); priv->arping.dad_list = NULL; diff --git a/src/devices/team/nm-device-team.c b/src/devices/team/nm-device-team.c index aeb39ebf9e..bf9fda7a9d 100644 --- a/src/devices/team/nm-device-team.c +++ b/src/devices/team/nm-device-team.c @@ -774,7 +774,7 @@ create_and_realize (NMDevice *device, "Failed to create team master interface '%s' for '%s': %s", iface, nm_connection_get_id (connection), - nm_platform_error_to_string (plerr)); + nm_platform_error_to_string_a (plerr)); return FALSE; } diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c index 76df89e58b..6e2ad7f2e9 100644 --- a/src/devices/wwan/nm-modem.c +++ b/src/devices/wwan/nm-modem.c @@ -32,7 +32,6 @@ #include "nm-setting-connection.h" #include "NetworkManagerUtils.h" #include "devices/nm-device-private.h" -#include "nm-route-manager.h" #include "nm-netns.h" #include "nm-act-request.h" #include "nm-ip4-config.h" @@ -1066,10 +1065,11 @@ deactivate_cleanup (NMModem *self, NMDevice *device) priv->ip6_method == NM_MODEM_IP_METHOD_AUTO) { ifindex = nm_device_get_ip_ifindex (device); if (ifindex > 0) { - nm_route_manager_route_flush (nm_netns_get_route_manager (nm_device_get_netns (device)), - ifindex); - nm_platform_address_flush (nm_device_get_platform (device), ifindex); - nm_platform_link_set_down (nm_device_get_platform (device), ifindex); + NMPlatform *platform = nm_device_get_platform (device); + + nm_platform_ip_route_flush (platform, AF_UNSPEC, ifindex); + nm_platform_ip_address_flush (platform, AF_UNSPEC, ifindex); + nm_platform_link_set_down (platform, ifindex); } } } diff --git a/src/ndisc/nm-ndisc.h b/src/ndisc/nm-ndisc.h index 7c67289d1e..db19db0021 100644 --- a/src/ndisc/nm-ndisc.h +++ b/src/ndisc/nm-ndisc.h @@ -69,22 +69,24 @@ typedef struct { NMNDiscPreference preference; } NMNDiscGateway; -typedef struct { +struct _NMNDiscAddress { struct in6_addr address; guint8 dad_counter; guint32 timestamp; guint32 lifetime; guint32 preferred; -} NMNDiscAddress; +}; +typedef struct _NMNDiscAddress NMNDiscAddress; -typedef struct { +struct _NMNDiscRoute { struct in6_addr network; guint8 plen; struct in6_addr gateway; guint32 timestamp; guint32 lifetime; NMNDiscPreference preference; -} NMNDiscRoute; +}; +typedef struct _NMNDiscRoute NMNDiscRoute; typedef struct { struct in6_addr address; diff --git a/src/nm-core-utils.c b/src/nm-core-utils.c index ca880de50b..9a3efd5dce 100644 --- a/src/nm-core-utils.c +++ b/src/nm-core-utils.c @@ -2146,7 +2146,7 @@ monotonic_timestamp_get (struct timespec *tp) break; case 2: /* fallback, return CLOCK_MONOTONIC. Kernels prior to 2.6.39 - * don't support CLOCK_BOOTTIME. */ + * (released on 18 May, 2011) don't support CLOCK_BOOTTIME. */ err = clock_gettime (CLOCK_MONOTONIC, tp); break; } diff --git a/src/nm-default-route-manager.c b/src/nm-default-route-manager.c index 66a94a05a4..ea0eeeaa63 100644 --- a/src/nm-default-route-manager.c +++ b/src/nm-default-route-manager.c @@ -304,7 +304,7 @@ _platform_route_sync_add (const VTableIP *vtable, NMDefaultRouteManager *self, g rt.plen = 0; rt.metric = entry->effective_metric; - success = nm_platform_ip4_route_add (priv->platform, NMP_NLM_FLAG_REPLACE, &rt); + success = (nm_platform_ip4_route_add (priv->platform, NMP_NLM_FLAG_REPLACE, &rt) == NM_PLATFORM_ERROR_SUCCESS); } else { NMPlatformIP6Route rt = entry->route.r6; @@ -312,7 +312,7 @@ _platform_route_sync_add (const VTableIP *vtable, NMDefaultRouteManager *self, g rt.plen = 0; rt.metric = entry->effective_metric; - success = nm_platform_ip6_route_add (priv->platform, NMP_NLM_FLAG_REPLACE, &rt); + success = (nm_platform_ip6_route_add (priv->platform, NMP_NLM_FLAG_REPLACE, &rt) == NM_PLATFORM_ERROR_SUCCESS); } if (!success) { _LOGW (vtable->vt->addr_family, "failed to add default route %s with effective metric %u", @@ -334,7 +334,7 @@ _platform_route_sync_flush (const VTableIP *vtable, NMDefaultRouteManager *self, routes = nm_platform_lookup_route_default_clone (priv->platform, vtable->vt->obj_type, - nm_platform_lookup_predicate_routes_skip_rtprot_kernel, + nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel, NULL); if (!routes) return FALSE; @@ -515,7 +515,7 @@ _resync_all (const VTableIP *vtable, NMDefaultRouteManager *self, const Entry *c routes = nm_platform_lookup_route_default_clone (priv->platform, vtable->vt->obj_type, - nm_platform_lookup_predicate_routes_skip_rtprot_kernel, + nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel, NULL); assumed_metrics = _get_assumed_interface_metrics (vtable, self, routes); diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c index 3b60e0d64f..f7741f5736 100644 --- a/src/nm-iface-helper.c +++ b/src/nm-iface-helper.c @@ -42,7 +42,6 @@ #include "nm-utils.h" #include "nm-setting-ip6-config.h" #include "systemd/nm-sd.h" -#include "nm-route-manager.h" #if !defined(NM_DIST_VERSION) # define NM_DIST_VERSION VERSION @@ -98,12 +97,6 @@ static struct { /*****************************************************************************/ -NMRouteManager *route_manager_get (void); - -NM_DEFINE_SINGLETON_GETTER (NMRouteManager, route_manager_get, NM_TYPE_ROUTE_MANAGER); - -/*****************************************************************************/ - static void dhcp4_state_changed (NMDhcpClient *client, NMDhcpState state, @@ -122,13 +115,17 @@ dhcp4_state_changed (NMDhcpClient *client, switch (state) { case NM_DHCP_STATE_BOUND: g_assert (ip4_config); + g_assert (nm_ip4_config_get_ifindex (ip4_config) == gl.ifindex); + existing = nm_ip4_config_capture (nm_platform_get_multi_idx (NM_PLATFORM_GET), NM_PLATFORM_GET, gl.ifindex, FALSE); if (last_config) nm_ip4_config_subtract (existing, last_config); nm_ip4_config_merge (existing, ip4_config, NM_IP_CONFIG_MERGE_DEFAULT); - if (!nm_ip4_config_commit (existing, NM_PLATFORM_GET, route_manager_get (), gl.ifindex, TRUE, global_opt.priority_v4)) + if (!nm_ip4_config_commit (existing, + NM_PLATFORM_GET, + global_opt.priority_v4)) _LOGW (LOGD_DHCP4, "failed to apply DHCPv4 config"); if (last_config) @@ -157,27 +154,6 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in NMNDiscConfigMap changed = changed_int; static NMIP6Config *ndisc_config = NULL; NMIP6Config *existing; - int system_support; - guint32 ifa_flags = 0x00; - int i; - - /* - * Check, whether kernel is recent enough, to help user space handling RA. - * If it's not supported, we have no ipv6-privacy and must add autoconf - * addresses as /128. - * The reason for the /128 is to prevent the kernel - * from adding a prefix route for this address. - **/ - system_support = nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET); - - if (system_support) - ifa_flags = IFA_F_NOPREFIXROUTE; - if (global_opt.tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR - || global_opt.tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR) - { - /* without system_support, this flag will be ignored. Still set it, doesn't seem to do any harm. */ - ifa_flags |= IFA_F_MANAGETEMPADDR; - } existing = nm_ip6_config_capture (nm_platform_get_multi_idx (NM_PLATFORM_GET), NM_PLATFORM_GET, gl.ifindex, FALSE, global_opt.tempaddr); @@ -197,49 +173,35 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in } if (changed & NM_NDISC_CONFIG_ADDRESSES) { - /* Rebuild address list from neighbor discovery cache. */ - nm_ip6_config_reset_addresses (ndisc_config); + guint8 plen; + guint32 ifa_flags; - /* ndisc->addresses contains at most max_addresses entries. - * This is different from what the kernel does, which - * also counts static and temporary addresses when checking - * max_addresses. - **/ - for (i = 0; i < rdata->addresses_n; i++) { - const NMNDiscAddress *discovered_address = &rdata->addresses[i]; - NMPlatformIP6Address address; + /* Check, whether kernel is recent enough to help user space handling RA. + * If it's not supported, we have no ipv6-privacy and must add autoconf + * addresses as /128. The reason for the /128 is to prevent the kernel + * from adding a prefix route for this address. */ + ifa_flags = 0; + if (nm_platform_check_support_kernel_extended_ifa_flags (NM_PLATFORM_GET)) { + ifa_flags |= IFA_F_NOPREFIXROUTE; + if (NM_IN_SET (global_opt.tempaddr, NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR, + NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)) + ifa_flags |= IFA_F_MANAGETEMPADDR; + plen = 64; + } else + plen = 128; - memset (&address, 0, sizeof (address)); - address.address = discovered_address->address; - address.plen = system_support ? 64 : 128; - address.timestamp = discovered_address->timestamp; - address.lifetime = discovered_address->lifetime; - address.preferred = discovered_address->preferred; - if (address.preferred > address.lifetime) - address.preferred = address.lifetime; - address.addr_source = NM_IP_CONFIG_SOURCE_NDISC; - address.n_ifa_flags = ifa_flags; - - nm_ip6_config_add_address (ndisc_config, &address); - } + nm_ip6_config_reset_addresses_ndisc (ndisc_config, + rdata->addresses, + rdata->addresses_n, + plen, + ifa_flags); } if (changed & NM_NDISC_CONFIG_ROUTES) { - /* Rebuild route list from neighbor discovery cache. */ - nm_ip6_config_reset_routes (ndisc_config); - - for (i = 0; i < rdata->routes_n; i++) { - const NMNDiscRoute *discovered_route = &rdata->routes[i]; - const NMPlatformIP6Route route = { - .network = discovered_route->network, - .plen = discovered_route->plen, - .gateway = discovered_route->gateway, - .rt_source = NM_IP_CONFIG_SOURCE_NDISC, - .metric = global_opt.priority_v6, - }; - - nm_ip6_config_add_route (ndisc_config, &route); - } + nm_ip6_config_reset_routes_ndisc (ndisc_config, + rdata->routes, + rdata->routes_n, + global_opt.priority_v6); } if (changed & NM_NDISC_CONFIG_DHCP_LEVEL) { @@ -257,7 +219,7 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in } nm_ip6_config_merge (existing, ndisc_config, NM_IP_CONFIG_MERGE_DEFAULT); - if (!nm_ip6_config_commit (existing, NM_PLATFORM_GET, route_manager_get (), gl.ifindex, TRUE)) + if (!nm_ip6_config_commit (existing, NM_PLATFORM_GET)) _LOGW (LOGD_IP6, "failed to apply IPv6 config"); } diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 20e2d2f946..ca35d5b92a 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -33,7 +33,6 @@ #include "platform/nm-platform.h" #include "platform/nm-platform-utils.h" #include "NetworkManagerUtils.h" -#include "nm-route-manager.h" #include "nm-core-internal.h" #include "introspection/org.freedesktop.NetworkManager.IP4Config.h" @@ -164,7 +163,9 @@ _nm_ip_config_add_obj (NMDedupMultiIndex *multi_idx, NMIPConfigDedupMultiIdxType *idx_type, int ifindex, const NMPObject *obj_new, - const NMPlatformObject *pl_new) + const NMPlatformObject *pl_new, + gboolean merge, + gboolean append_force) { NMPObject obj_new_stackinit; const NMDedupMultiEntry *entry_old; @@ -201,58 +202,62 @@ _nm_ip_config_add_obj (NMDedupMultiIndex *multi_idx, const NMPObject *obj_old = entry_old->obj; if (nmp_object_equal (obj_new, obj_old)) - return FALSE; + goto append_force_and_out; - switch (idx_type->obj_type) { - case NMP_OBJECT_TYPE_IP4_ADDRESS: - case NMP_OBJECT_TYPE_IP6_ADDRESS: - /* we want to keep the maximum addr_source. But since we expect - * that usually we already add the maxiumum right away, we first try to - * add the new address (replacing the old one). Only if we later - * find out that addr_source is now lower, we fix it. - */ - if (obj_new->ip_address.addr_source < obj_old->ip_address.addr_source) { - obj_new = nmp_object_stackinit_obj (&obj_new_stackinit, obj_new); - obj_new_stackinit.ip_address.addr_source = obj_old->ip_address.addr_source; - modified = TRUE; + /* if @merge, we merge the new object with the existing one. + * Otherwise, we replace it entirely. */ + if (merge) { + switch (idx_type->obj_type) { + case NMP_OBJECT_TYPE_IP4_ADDRESS: + case NMP_OBJECT_TYPE_IP6_ADDRESS: + /* we want to keep the maximum addr_source. But since we expect + * that usually we already add the maxiumum right away, we first try to + * add the new address (replacing the old one). Only if we later + * find out that addr_source is now lower, we fix it. + */ + if (obj_new->ip_address.addr_source < obj_old->ip_address.addr_source) { + obj_new = nmp_object_stackinit_obj (&obj_new_stackinit, obj_new); + obj_new_stackinit.ip_address.addr_source = obj_old->ip_address.addr_source; + modified = TRUE; + } + + /* for addresses that we read from the kernel, we keep the timestamps as defined + * by the previous source (item_old). The reason is, that the other source configured the lifetimes + * with "what should be" and the kernel values are "what turned out after configuring it". + * + * For other sources, the longer lifetime wins. */ + if ( ( obj_new->ip_address.addr_source == NM_IP_CONFIG_SOURCE_KERNEL + && obj_old->ip_address.addr_source != NM_IP_CONFIG_SOURCE_KERNEL) + || nm_platform_ip_address_cmp_expiry (NMP_OBJECT_CAST_IP_ADDRESS (obj_old), NMP_OBJECT_CAST_IP_ADDRESS(obj_new)) > 0) { + obj_new = nmp_object_stackinit_obj (&obj_new_stackinit, obj_new); + obj_new_stackinit.ip_address.timestamp = NMP_OBJECT_CAST_IP_ADDRESS (obj_old)->timestamp; + obj_new_stackinit.ip_address.lifetime = NMP_OBJECT_CAST_IP_ADDRESS (obj_old)->lifetime; + obj_new_stackinit.ip_address.preferred = NMP_OBJECT_CAST_IP_ADDRESS (obj_old)->preferred; + modified = TRUE; + } + break; + case NMP_OBJECT_TYPE_IP4_ROUTE: + case NMP_OBJECT_TYPE_IP6_ROUTE: + /* we want to keep the maximum rt_source. But since we expect + * that usually we already add the maxiumum right away, we first try to + * add the new route (replacing the old one). Only if we later + * find out that rt_source is now lower, we fix it. + */ + if (obj_new->ip_route.rt_source < obj_old->ip_route.rt_source) { + obj_new = nmp_object_stackinit_obj (&obj_new_stackinit, obj_new); + obj_new_stackinit.ip_route.rt_source = obj_old->ip_route.rt_source; + modified = TRUE; + } + break; + default: + nm_assert_not_reached (); + break; } - /* for addresses that we read from the kernel, we keep the timestamps as defined - * by the previous source (item_old). The reason is, that the other source configured the lifetimes - * with "what should be" and the kernel values are "what turned out after configuring it". - * - * For other sources, the longer lifetime wins. */ - if ( ( obj_new->ip_address.addr_source == NM_IP_CONFIG_SOURCE_KERNEL - && obj_old->ip_address.addr_source != NM_IP_CONFIG_SOURCE_KERNEL) - || nm_platform_ip_address_cmp_expiry (NMP_OBJECT_CAST_IP_ADDRESS (obj_old), NMP_OBJECT_CAST_IP_ADDRESS(obj_new)) > 0) { - obj_new = nmp_object_stackinit_obj (&obj_new_stackinit, obj_new); - obj_new_stackinit.ip_address.timestamp = NMP_OBJECT_CAST_IP_ADDRESS (obj_old)->timestamp; - obj_new_stackinit.ip_address.lifetime = NMP_OBJECT_CAST_IP_ADDRESS (obj_old)->lifetime; - obj_new_stackinit.ip_address.preferred = NMP_OBJECT_CAST_IP_ADDRESS (obj_old)->preferred; - modified = TRUE; - } - break; - case NMP_OBJECT_TYPE_IP4_ROUTE: - case NMP_OBJECT_TYPE_IP6_ROUTE: - /* we want to keep the maximum rt_source. But since we expect - * that usually we already add the maxiumum right away, we first try to - * add the new route (replacing the old one). Only if we later - * find out that rt_source is now lower, we fix it. - */ - if (obj_new->ip_route.rt_source < obj_old->ip_route.rt_source) { - obj_new = nmp_object_stackinit_obj (&obj_new_stackinit, obj_new); - obj_new_stackinit.ip_route.rt_source = obj_old->ip_route.rt_source; - modified = TRUE; - } - break; - default: - nm_assert_not_reached (); - break; + if ( modified + && nmp_object_equal (obj_new, obj_old)) + goto append_force_and_out; } - - if ( modified - && nmp_object_equal (obj_new, obj_old)) - return FALSE; } if (!nm_dedup_multi_index_add_full (multi_idx, @@ -269,6 +274,55 @@ _nm_ip_config_add_obj (NMDedupMultiIndex *multi_idx, } return TRUE; + +append_force_and_out: + if (append_force) { + if (nm_dedup_multi_entry_reorder (entry_old, NULL, TRUE)) + return TRUE; + } + return FALSE; +} + +/** + * _nm_ip_config_lookup_ip_route: + * @multi_idx: + * @idx_type: + * @needle: + * @cmp_type: after lookup, filter the result by comparing with @cmp_type. Only + * return the result, if it compares equal to @needle according to this @cmp_type. + * Note that the index uses %NM_PLATFORM_IP_ROUTE_CMP_TYPE_DST type, so passing + * that compare-type means not to filter any further. + * + * Returns: the found entry or %NULL. + */ +const NMDedupMultiEntry * +_nm_ip_config_lookup_ip_route (const NMDedupMultiIndex *multi_idx, + const NMIPConfigDedupMultiIdxType *idx_type, + const NMPObject *needle, + NMPlatformIPRouteCmpType cmp_type) +{ + const NMDedupMultiEntry *entry; + + nm_assert (multi_idx); + nm_assert (idx_type); + nm_assert (NM_IN_SET (idx_type->obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); + nm_assert (NMP_OBJECT_GET_TYPE (needle) == idx_type->obj_type); + + entry = nm_dedup_multi_index_lookup_obj (multi_idx, + &idx_type->parent, + needle); + if (!entry) + return NULL; + + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_DST) + nm_assert (nm_platform_ip4_route_cmp (NMP_OBJECT_CAST_IP4_ROUTE (entry->obj), NMP_OBJECT_CAST_IP4_ROUTE (needle), cmp_type) == 0); + else { + if (nm_platform_ip4_route_cmp (NMP_OBJECT_CAST_IP4_ROUTE (entry->obj), + NMP_OBJECT_CAST_IP4_ROUTE (needle), + cmp_type) != 0) + return NULL; + } + return entry; } /*****************************************************************************/ @@ -339,6 +393,9 @@ G_DEFINE_TYPE (NMIP4Config, nm_ip4_config, NM_TYPE_EXPORTED_OBJECT) static void _add_address (NMIP4Config *self, const NMPObject *obj_new, const NMPlatformIP4Address *new); static void _add_route (NMIP4Config *self, const NMPObject *obj_new, const NMPlatformIP4Route *new); +static const NMDedupMultiEntry *_lookup_route (const NMIP4Config *self, + const NMPObject *needle, + NMPlatformIPRouteCmpType cmp_type); /*****************************************************************************/ @@ -624,16 +681,6 @@ nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int i continue; if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) continue; - - if ( priv->has_gateway - && route->plen == 32 - && route->network == priv->gateway - && route->gateway == 0) { - /* If there is a host route to the gateway, ignore that route. It is - * automatically added by NetworkManager when needed. - */ - continue; - } _add_route (self, plobj, NULL); } @@ -656,92 +703,122 @@ nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int i } gboolean -nm_ip4_config_commit (const NMIP4Config *self, NMPlatform *platform, NMRouteManager *route_manager, int ifindex, gboolean routes_full_sync, gint64 default_route_metric) +nm_ip4_config_commit (const NMIP4Config *self, + NMPlatform *platform, + guint32 default_route_metric) { + const NMIP4ConfigPrivate *priv; gs_unref_ptrarray GPtrArray *addresses = NULL; - const NMDedupMultiHeadEntry *head_entry; + gs_unref_ptrarray GPtrArray *routes = NULL; + gs_unref_ptrarray GPtrArray *ip4_dev_route_blacklist = NULL; + int ifindex; + guint i; + gboolean success = TRUE; + g_return_val_if_fail (NM_IS_IP4_CONFIG (self), FALSE); + + priv = NM_IP4_CONFIG_GET_PRIVATE (self); + + ifindex = nm_ip4_config_get_ifindex (self); g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (self != NULL, FALSE); addresses = nm_dedup_multi_objs_to_ptr_array_head (nm_ip4_config_lookup_addresses (self), NULL, NULL); - nm_platform_ip4_address_sync (platform, ifindex, addresses); + routes = nm_dedup_multi_objs_to_ptr_array_head (nm_ip4_config_lookup_routes (self), + NULL, NULL); - /* Routes */ - { - guint i; - gs_unref_array GArray *routes = NULL; - gs_unref_array GArray *device_route_purge_list = NULL; - const CList *iter; + if (addresses) { + /* For IPv6, we explicitly add the device-routes (onlink) to NMIP6Config. + * As we don't do that for IPv4, add it here shortly before syncing + * the routes. */ + for (i = 0; i < addresses->len; i++) { + const NMPObject *o = addresses->pdata[i]; + const NMPlatformIP4Address *addr; + nm_auto_nmpobj NMPObject *r = NULL; + NMPlatformIP4Route *route; + in_addr_t network; - head_entry = nm_ip4_config_lookup_routes (self); + if (!o) + continue; - routes = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformIP4Route), head_entry ? head_entry->len : 0); + addr = NMP_OBJECT_CAST_IP4_ADDRESS (o); + if (addr->plen == 0) + continue; - if ( default_route_metric >= 0 - && addresses) { - /* For IPv6, we explicitly add the device-routes (onlink) to NMIP6Config. - * As we don't do that for IPv4, add it here shortly before syncing - * the routes. For NMRouteManager these routes are very much important. */ - for (i = 0; i < addresses->len; i++) { - const NMPObject *o = addresses->pdata[i]; - const NMPlatformIP4Address *addr; - NMPlatformIP4Route route = { 0 }; + nm_assert (addr->plen <= 32); - if (!o) - continue; + /* The destination network depends on the peer-address. */ + network = nm_utils_ip4_address_clear_host_address (addr->peer_address, addr->plen); - addr = NMP_OBJECT_CAST_IP4_ADDRESS (o); - if (addr->plen == 0) - continue; + if (_ipv4_is_zeronet (network)) { + /* Kernel doesn't add device-routes for destinations that + * start with 0.x.y.z. Skip them. */ + continue; + } - nm_assert (addr->plen <= 32); + r = nmp_object_new (NMP_OBJECT_TYPE_IP4_ROUTE, NULL); + route = NMP_OBJECT_CAST_IP4_ROUTE (r); - route.ifindex = ifindex; - route.rt_source = NM_IP_CONFIG_SOURCE_KERNEL; + route->ifindex = ifindex; + route->rt_source = NM_IP_CONFIG_SOURCE_KERNEL; + route->network = network; + route->plen = addr->plen; + route->pref_src = addr->address; + route->metric = default_route_metric; + route->scope_inv = nm_platform_route_scope_inv (NM_RT_SCOPE_LINK); - /* The destination network depends on the peer-address. */ - route.network = nm_utils_ip4_address_clear_host_address (addr->peer_address, addr->plen); + nm_platform_ip_route_normalize (AF_INET, (NMPlatformIPRoute *) route); - if (_ipv4_is_zeronet (route.network)) { - /* Kernel doesn't add device-routes for destinations that - * start with 0.x.y.z. Skip them. */ - continue; - } + if (_lookup_route (self, + r, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID)) { + /* we already track this route. Don't add it again. */ + } else { + if (!routes) + routes = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + g_ptr_array_add (routes, (gpointer) nmp_object_ref (r)); + } - route.plen = addr->plen; - route.pref_src = addr->address; - route.metric = default_route_metric; + if (default_route_metric != NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE) { + nm_auto_nmpobj NMPObject *r_dev = NULL; - g_array_append_val (routes, route); + r_dev = nmp_object_clone (r, FALSE); + route = NMP_OBJECT_CAST_IP4_ROUTE (r_dev); + route->metric = NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE; - if (default_route_metric != NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE) { - if (!device_route_purge_list) - device_route_purge_list = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); - route.metric = NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_ROUTE; - g_array_append_val (device_route_purge_list, route); + nm_platform_ip_route_normalize (AF_INET, (NMPlatformIPRoute *) route); + + if (_lookup_route (self, + r_dev, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID)) { + /* we track such a route explicitly. Don't blacklist it. */ + } else { + if (!ip4_dev_route_blacklist) + ip4_dev_route_blacklist = g_ptr_array_new_with_free_func ((GDestroyNotify) nmp_object_unref); + + g_ptr_array_add (ip4_dev_route_blacklist, + g_steal_pointer (&r_dev)); } } } - - if (head_entry) { - c_list_for_each (iter, &head_entry->lst_entries_head) { - g_array_append_vals (routes, - NMP_OBJECT_CAST_IP4_ROUTE (c_list_entry (iter, NMDedupMultiEntry, lst_entries)->obj), - 1); - } - } - - nm_route_manager_ip4_route_register_device_route_purge_list (route_manager, device_route_purge_list); - - if (!nm_route_manager_ip4_route_sync (route_manager, ifindex, routes, - default_route_metric < 0, routes_full_sync)) - return FALSE; } - return TRUE; + nm_platform_ip4_address_sync (platform, ifindex, addresses); + + if (!nm_platform_ip_route_sync (platform, + AF_INET, + ifindex, + routes, + nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel, + NULL)) + success = FALSE; + + nm_platform_ip4_dev_route_blacklist_set (platform, + ifindex, + ip4_dev_route_blacklist); + + return success; } static void @@ -841,6 +918,11 @@ nm_ip4_config_merge_setting (NMIP4Config *self, NMSettingIPConfig *setting, guin NMIPRoute *s_route = nm_setting_ip_config_get_route (setting, i); NMPlatformIP4Route route; + if (nm_ip_route_get_family (s_route) != AF_INET) { + nm_assert_not_reached (); + continue; + } + memset (&route, 0, sizeof (route)); nm_ip_route_get_dest_binary (s_route, &route.network); @@ -1228,7 +1310,8 @@ nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src) nm_ip_config_iter_ip4_address_for_each (&ipconf_iter, src, &a) { if (nm_dedup_multi_index_remove_obj (priv_dst->multi_idx, &priv_dst->idx_ip4_addresses, - NMP_OBJECT_UP_CAST (a))) + NMP_OBJECT_UP_CAST (a), + NULL)) changed = TRUE; } if (changed) @@ -1256,7 +1339,8 @@ nm_ip4_config_subtract (NMIP4Config *dst, const NMIP4Config *src) nm_ip_config_iter_ip4_route_for_each (&ipconf_iter, src, &r) { if (nm_dedup_multi_index_remove_obj (priv_dst->multi_idx, &priv_dst->idx_ip4_routes, - NMP_OBJECT_UP_CAST (r))) + NMP_OBJECT_UP_CAST (r), + NULL)) changed = TRUE; } if (changed) @@ -1885,7 +1969,9 @@ _add_address (NMIP4Config *self, const NMPObject *obj_new, const NMPlatformIP4Ad &priv->idx_ip4_addresses_, priv->ifindex, obj_new, - (const NMPlatformObject *) new)) + (const NMPlatformObject *) new, + TRUE, + FALSE)) _notify_addresses (self); } @@ -1921,7 +2007,8 @@ _nmtst_nm_ip4_config_del_address (NMIP4Config *self, guint i) if (nm_dedup_multi_index_remove_obj (priv->multi_idx, &priv->idx_ip4_addresses, - NMP_OBJECT_UP_CAST (a)) != 1) + NMP_OBJECT_UP_CAST (a), + NULL) != 1) g_return_if_reached (); _notify_addresses (self); } @@ -1975,12 +2062,30 @@ nm_ip4_config_address_exists (const NMIP4Config *self, needle->plen, needle->peer_address); return !!nm_dedup_multi_index_lookup_obj (priv->multi_idx, - &priv->idx_ip4_routes, + &priv->idx_ip4_addresses, &obj_stack); } /*****************************************************************************/ +static const NMDedupMultiEntry * +_lookup_route (const NMIP4Config *self, + const NMPObject *needle, + NMPlatformIPRouteCmpType cmp_type) +{ + const NMIP4ConfigPrivate *priv; + + nm_assert (NM_IS_IP4_CONFIG (self)); + nm_assert (NMP_OBJECT_GET_TYPE (needle) == NMP_OBJECT_TYPE_IP4_ROUTE); + + priv = NM_IP4_CONFIG_GET_PRIVATE (self); + + return _nm_ip_config_lookup_ip_route (priv->multi_idx, + &priv->idx_ip4_routes_, + needle, + cmp_type); +} + void nm_ip4_config_reset_routes (NMIP4Config *self) { @@ -2004,7 +2109,9 @@ _add_route (NMIP4Config *self, const NMPObject *obj_new, const NMPlatformIP4Rout &priv->idx_ip4_routes_, priv->ifindex, obj_new, - (const NMPlatformObject *) new)) + (const NMPlatformObject *) new, + TRUE, + FALSE)) _notify_routes (self); } @@ -2040,7 +2147,8 @@ _nmtst_nm_ip4_config_del_route (NMIP4Config *self, guint i) if (nm_dedup_multi_index_remove_obj (priv->multi_idx, &priv->idx_ip4_routes, - NMP_OBJECT_UP_CAST (r)) != 1) + NMP_OBJECT_UP_CAST (r), + NULL) != 1) g_return_if_reached (); _notify_routes (self); } diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h index e11043b79c..ab8fa51d4d 100644 --- a/src/nm-ip4-config.h +++ b/src/nm-ip4-config.h @@ -92,7 +92,14 @@ gboolean _nm_ip_config_add_obj (NMDedupMultiIndex *multi_idx, NMIPConfigDedupMultiIdxType *idx_type, int ifindex, const NMPObject *obj_new, - const NMPlatformObject *pl_new); + const NMPlatformObject *pl_new, + gboolean merge, + gboolean append_force); + +const NMDedupMultiEntry *_nm_ip_config_lookup_ip_route (const NMDedupMultiIndex *multi_idx, + const NMIPConfigDedupMultiIdxType *idx_type, + const NMPObject *needle, + NMPlatformIPRouteCmpType cmp_type); /*****************************************************************************/ @@ -135,7 +142,9 @@ int nm_ip4_config_get_ifindex (const NMIP4Config *self); NMDedupMultiIndex *nm_ip4_config_get_multi_idx (const NMIP4Config *self); NMIP4Config *nm_ip4_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex, gboolean capture_resolv_conf); -gboolean nm_ip4_config_commit (const NMIP4Config *self, NMPlatform *platform, NMRouteManager *route_manager, int ifindex, gboolean routes_full_sync, gint64 default_route_metric); +gboolean nm_ip4_config_commit (const NMIP4Config *self, + NMPlatform *platform, + guint32 default_route_metric); void nm_ip4_config_merge_setting (NMIP4Config *self, NMSettingIPConfig *setting, guint32 default_route_metric); NMSetting *nm_ip4_config_create_setting (const NMIP4Config *self); diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index dc45acc18d..b2eccf3767 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -32,10 +32,10 @@ #include "platform/nmp-object.h" #include "platform/nm-platform.h" #include "platform/nm-platform-utils.h" -#include "nm-route-manager.h" #include "nm-core-internal.h" #include "NetworkManagerUtils.h" #include "nm-ip4-config.h" +#include "ndisc/nm-ndisc.h" #include "introspection/org.freedesktop.NetworkManager.IP6Config.h" @@ -483,15 +483,6 @@ nm_ip6_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int i if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (route)) continue; - if ( has_gateway - && route->plen == 128 - && IN6_ARE_ADDR_EQUAL (&route->network, &priv->gateway) - && IN6_IS_ADDR_UNSPECIFIED (&route->gateway)) { - /* If there is a host route to the gateway, ignore that route. It is - * automatically added by NetworkManager when needed. - */ - continue; - } _add_route (self, plobj, NULL); } @@ -516,45 +507,33 @@ nm_ip6_config_capture (NMDedupMultiIndex *multi_idx, NMPlatform *platform, int i gboolean nm_ip6_config_commit (const NMIP6Config *self, - NMPlatform *platform, - NMRouteManager *route_manager, - int ifindex, - gboolean routes_full_sync) + NMPlatform *platform) { gs_unref_ptrarray GPtrArray *addresses = NULL; - const NMDedupMultiHeadEntry *head_entry; + gs_unref_ptrarray GPtrArray *routes = NULL; + int ifindex; + gboolean success = TRUE; + g_return_val_if_fail (NM_IS_IP6_CONFIG (self), FALSE); + + ifindex = nm_ip6_config_get_ifindex (self); g_return_val_if_fail (ifindex > 0, FALSE); - g_return_val_if_fail (self != NULL, FALSE); - /* Addresses */ addresses = nm_dedup_multi_objs_to_ptr_array_head (nm_ip6_config_lookup_addresses (self), NULL, NULL); - + routes = nm_dedup_multi_objs_to_ptr_array_head (nm_ip6_config_lookup_routes (self), + NULL, NULL); nm_platform_ip6_address_sync (platform, ifindex, addresses, TRUE); - /* Routes */ - { - gs_unref_array GArray *routes = NULL; - const CList *iter; + if (!nm_platform_ip_route_sync (platform, + AF_INET6, + ifindex, + routes, + nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel, + NULL)) + success = FALSE; - head_entry = nm_ip6_config_lookup_routes (self); - - routes = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformIP6Route), head_entry ? head_entry->len : 0); - - if (head_entry) { - c_list_for_each (iter, &head_entry->lst_entries_head) { - g_array_append_vals (routes, - NMP_OBJECT_CAST_IP6_ROUTE (c_list_entry (iter, NMDedupMultiEntry, lst_entries)->obj), - 1); - } - } - - if (!nm_route_manager_ip6_route_sync (route_manager, ifindex, routes, TRUE, routes_full_sync)) - return FALSE; - } - - return TRUE; + return success; } static void @@ -668,6 +647,11 @@ nm_ip6_config_merge_setting (NMIP6Config *self, NMSettingIPConfig *setting, guin NMIPRoute *s_route = nm_setting_ip_config_get_route (setting, i); NMPlatformIP6Route route; + if (nm_ip_route_get_family (s_route) != AF_INET6) { + nm_assert_not_reached (); + continue; + } + memset (&route, 0, sizeof (route)); nm_ip_route_get_dest_binary (s_route, &route.network); @@ -1025,7 +1009,8 @@ nm_ip6_config_subtract (NMIP6Config *dst, const NMIP6Config *src) nm_ip_config_iter_ip6_address_for_each (&ipconf_iter, src, &a) { if (nm_dedup_multi_index_remove_obj (priv_dst->multi_idx, &priv_dst->idx_ip6_addresses, - NMP_OBJECT_UP_CAST (a))) + NMP_OBJECT_UP_CAST (a), + NULL)) changed = TRUE; } if (changed) @@ -1054,7 +1039,8 @@ nm_ip6_config_subtract (NMIP6Config *dst, const NMIP6Config *src) nm_ip_config_iter_ip6_route_for_each (&ipconf_iter, src, &r) { if (nm_dedup_multi_index_remove_obj (priv_dst->multi_idx, &priv_dst->idx_ip6_routes, - NMP_OBJECT_UP_CAST (r))) + NMP_OBJECT_UP_CAST (r), + NULL)) changed = TRUE; } if (changed) @@ -1200,6 +1186,8 @@ nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relev dst_priv = NM_IP6_CONFIG_GET_PRIVATE (dst); src_priv = NM_IP6_CONFIG_GET_PRIVATE (src); + g_return_val_if_fail (src_priv->ifindex > 0, FALSE); + g_object_freeze_notify (G_OBJECT (dst)); /* ifindex */ @@ -1259,12 +1247,13 @@ nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relev has_minor_changes = TRUE; nm_dedup_multi_index_dirty_set_idx (dst_priv->multi_idx, &dst_priv->idx_ip6_addresses); nm_dedup_multi_iter_for_each (&ipconf_iter_src, head_entry_src) { - nm_dedup_multi_index_add (dst_priv->multi_idx, - &dst_priv->idx_ip6_addresses, - ipconf_iter_src.current->obj, - NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE, - NULL, - NULL); + _nm_ip_config_add_obj (dst_priv->multi_idx, + &dst_priv->idx_ip6_addresses_, + dst_priv->ifindex, + ipconf_iter_src.current->obj, + NULL, + FALSE, + TRUE); } nm_dedup_multi_index_dirty_remove_idx (dst_priv->multi_idx, &dst_priv->idx_ip6_addresses, FALSE); _notify_addresses (dst); @@ -1303,12 +1292,13 @@ nm_ip6_config_replace (NMIP6Config *dst, const NMIP6Config *src, gboolean *relev has_minor_changes = TRUE; nm_dedup_multi_index_dirty_set_idx (dst_priv->multi_idx, &dst_priv->idx_ip6_routes); nm_dedup_multi_iter_for_each (&ipconf_iter_src, head_entry_src) { - nm_dedup_multi_index_add (dst_priv->multi_idx, - &dst_priv->idx_ip6_routes, - ipconf_iter_src.current->obj, - NM_DEDUP_MULTI_IDX_MODE_APPEND_FORCE, - NULL, - NULL); + _nm_ip_config_add_obj (dst_priv->multi_idx, + &dst_priv->idx_ip6_routes_, + dst_priv->ifindex, + ipconf_iter_src.current->obj, + NULL, + FALSE, + TRUE); } nm_dedup_multi_index_dirty_remove_idx (dst_priv->multi_idx, &dst_priv->idx_ip6_routes, FALSE); _notify_routes (dst); @@ -1529,6 +1519,58 @@ nm_ip6_config_get_route_metric (const NMIP6Config *self) /*****************************************************************************/ +void +nm_ip6_config_reset_addresses_ndisc (NMIP6Config *self, + const NMNDiscAddress *addresses, + guint addresses_n, + guint8 plen, + guint32 ifa_flags) +{ + NMIP6ConfigPrivate *priv; + guint i; + gboolean changed = FALSE; + + g_return_if_fail (NM_IS_IP6_CONFIG (self)); + + priv = NM_IP6_CONFIG_GET_PRIVATE (self); + + g_return_if_fail (priv->ifindex > 0); + + nm_dedup_multi_index_dirty_set_idx (priv->multi_idx, &priv->idx_ip6_addresses); + + for (i = 0; i < addresses_n; i++) { + const NMNDiscAddress *ndisc_addr = &addresses[i]; + NMPObject obj; + NMPlatformIP6Address *a; + + nmp_object_stackinit (&obj, NMP_OBJECT_TYPE_IP6_ADDRESS, NULL); + a = NMP_OBJECT_CAST_IP6_ADDRESS (&obj); + a->ifindex = priv->ifindex; + a->address = ndisc_addr->address; + a->plen = plen; + a->timestamp = ndisc_addr->timestamp; + a->lifetime = ndisc_addr->lifetime; + a->preferred = MIN (ndisc_addr->lifetime, ndisc_addr->preferred); + a->addr_source = NM_IP_CONFIG_SOURCE_NDISC; + a->n_ifa_flags = ifa_flags; + + if (_nm_ip_config_add_obj (priv->multi_idx, + &priv->idx_ip6_addresses_, + priv->ifindex, + &obj, + NULL, + FALSE, + TRUE)) + changed = TRUE; + } + + if (nm_dedup_multi_index_dirty_remove_idx (priv->multi_idx, &priv->idx_ip6_addresses, FALSE) > 0) + changed = TRUE; + + if (changed) + _notify_addresses (self); +} + void nm_ip6_config_reset_addresses (NMIP6Config *self) { @@ -1550,7 +1592,9 @@ _add_address (NMIP6Config *self, &priv->idx_ip6_addresses_, priv->ifindex, obj_new, - (const NMPlatformObject *) new)) + (const NMPlatformObject *) new, + TRUE, + FALSE)) _notify_addresses (self); } @@ -1586,7 +1630,8 @@ _nmtst_nm_ip6_config_del_address (NMIP6Config *self, guint i) if (nm_dedup_multi_index_remove_obj (priv->multi_idx, &priv->idx_ip6_addresses, - NMP_OBJECT_UP_CAST (a)) != 1) + NMP_OBJECT_UP_CAST (a), + NULL) != 1) g_return_if_reached (); _notify_addresses (self); } @@ -1704,6 +1749,55 @@ nm_ip6_config_has_any_dad_pending (const NMIP6Config *self, /*****************************************************************************/ +void +nm_ip6_config_reset_routes_ndisc (NMIP6Config *self, + const NMNDiscRoute *routes, + guint routes_n, + guint32 metric) +{ + NMIP6ConfigPrivate *priv; + guint i; + gboolean changed = FALSE; + + g_return_if_fail (NM_IS_IP6_CONFIG (self)); + + priv = NM_IP6_CONFIG_GET_PRIVATE (self); + + g_return_if_fail (priv->ifindex > 0); + + nm_dedup_multi_index_dirty_set_idx (priv->multi_idx, &priv->idx_ip6_routes); + + for (i = 0; i < routes_n; i++) { + const NMNDiscRoute *ndisc_route = &routes[i]; + NMPObject obj; + NMPlatformIP6Route *r; + + nmp_object_stackinit (&obj, NMP_OBJECT_TYPE_IP6_ROUTE, NULL); + r = NMP_OBJECT_CAST_IP6_ROUTE (&obj); + r->ifindex = priv->ifindex; + r->network = ndisc_route->network; + r->plen = ndisc_route->plen; + r->gateway = ndisc_route->gateway; + r->rt_source = NM_IP_CONFIG_SOURCE_NDISC; + r->metric = metric; + + if (_nm_ip_config_add_obj (priv->multi_idx, + &priv->idx_ip6_routes_, + priv->ifindex, + &obj, + NULL, + FALSE, + TRUE)) + changed = TRUE; + } + + if (nm_dedup_multi_index_dirty_remove_idx (priv->multi_idx, &priv->idx_ip6_routes, FALSE) > 0) + changed = TRUE; + + if (changed) + _notify_routes (self); +} + void nm_ip6_config_reset_routes (NMIP6Config *self) { @@ -1727,7 +1821,9 @@ _add_route (NMIP6Config *self, const NMPObject *obj_new, const NMPlatformIP6Rout &priv->idx_ip6_routes_, priv->ifindex, obj_new, - (const NMPlatformObject *) new)) + (const NMPlatformObject *) new, + TRUE, + FALSE)) _notify_routes (self); } @@ -1763,7 +1859,8 @@ _nmtst_ip6_config_del_route (NMIP6Config *self, guint i) if (nm_dedup_multi_index_remove_obj (priv->multi_idx, &priv->idx_ip6_routes, - NMP_OBJECT_UP_CAST (r)) != 1) + NMP_OBJECT_UP_CAST (r), + NULL) != 1) g_return_if_reached (); _notify_routes (self); } diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h index f296224fab..eb2aae170d 100644 --- a/src/nm-ip6-config.h +++ b/src/nm-ip6-config.h @@ -112,10 +112,7 @@ struct _NMDedupMultiIndex *nm_ip6_config_get_multi_idx (const NMIP6Config *self) NMIP6Config *nm_ip6_config_capture (struct _NMDedupMultiIndex *multi_idx, NMPlatform *platform, int ifindex, gboolean capture_resolv_conf, NMSettingIP6ConfigPrivacy use_temporary); gboolean nm_ip6_config_commit (const NMIP6Config *self, - NMPlatform *platform, - NMRouteManager *route_manager, - int ifindex, - gboolean routes_full_sync); + NMPlatform *platform); void nm_ip6_config_merge_setting (NMIP6Config *self, NMSettingIPConfig *setting, guint32 default_route_metric); NMSetting *nm_ip6_config_create_setting (const NMIP6Config *self); @@ -194,6 +191,18 @@ gboolean nm_ip6_config_equal (const NMIP6Config *a, const NMIP6Config *b); void nm_ip6_config_set_privacy (NMIP6Config *self, NMSettingIP6ConfigPrivacy privacy); +struct _NMNDiscAddress; +void nm_ip6_config_reset_addresses_ndisc (NMIP6Config *self, + const struct _NMNDiscAddress *addresses, + guint addresses_n, + guint8 plen, + guint32 ifa_flags); +struct _NMNDiscRoute; +void nm_ip6_config_reset_routes_ndisc (NMIP6Config *self, + const struct _NMNDiscRoute *routes, + guint routes_n, + guint32 metric); + /*****************************************************************************/ /* Testing-only functions */ diff --git a/src/nm-netns.c b/src/nm-netns.c index f5e6b0014d..8069cfccc3 100644 --- a/src/nm-netns.c +++ b/src/nm-netns.c @@ -26,7 +26,6 @@ #include "platform/nm-platform.h" #include "platform/nmp-netns.h" -#include "nm-route-manager.h" #include "nm-default-route-manager.h" #include "nm-core-internal.h" #include "NetworkManagerUtils.h" @@ -40,7 +39,6 @@ NM_GOBJECT_PROPERTIES_DEFINE_BASE ( typedef struct { NMPlatform *platform; NMPNetns *platform_netns; - NMRouteManager *route_manager; NMDefaultRouteManager *default_route_manager; bool log_with_ptr; } NMNetnsPrivate; @@ -88,12 +86,6 @@ nm_netns_get_default_route_manager (NMNetns *self) return NM_NETNS_GET_PRIVATE (self)->default_route_manager; } -NMRouteManager * -nm_netns_get_route_manager (NMNetns *self) -{ - return NM_NETNS_GET_PRIVATE (self)->route_manager; -} - /*****************************************************************************/ static void @@ -137,7 +129,6 @@ constructed (GObject *object) log_with_ptr = nm_platform_get_log_with_ptr (priv->platform); priv->platform_netns = nm_platform_netns_get (priv->platform); - priv->route_manager = nm_route_manager_new (log_with_ptr, priv->platform); priv->default_route_manager = nm_default_route_manager_new (log_with_ptr, priv->platform); G_OBJECT_CLASS (nm_netns_parent_class)->constructed (object); @@ -157,7 +148,6 @@ dispose (GObject *object) NMNetns *self = NM_NETNS (object); NMNetnsPrivate *priv = NM_NETNS_GET_PRIVATE (self); - g_clear_object (&priv->route_manager); g_clear_object (&priv->default_route_manager); g_clear_object (&priv->platform); diff --git a/src/nm-netns.h b/src/nm-netns.h index ebe9d1f2a8..bc71880400 100644 --- a/src/nm-netns.h +++ b/src/nm-netns.h @@ -39,7 +39,6 @@ NMNetns *nm_netns_new (NMPlatform *platform); NMPlatform *nm_netns_get_platform (NMNetns *self); NMPNetns *nm_netns_get_platform_netns (NMNetns *self); -NMRouteManager *nm_netns_get_route_manager (NMNetns *self); NMDefaultRouteManager *nm_netns_get_default_route_manager (NMNetns *self); struct _NMDedupMultiIndex *nm_netns_get_multi_idx (NMNetns *self); diff --git a/src/nm-route-manager.c b/src/nm-route-manager.c deleted file mode 100644 index 12583e5c06..0000000000 --- a/src/nm-route-manager.c +++ /dev/null @@ -1,1380 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2015 Red Hat, Inc. - */ - -#include "nm-default.h" - -#include "nm-route-manager.h" - -#include - -#include "platform/nm-platform.h" -#include "platform/nmp-object.h" -#include "nm-core-internal.h" -#include "NetworkManagerUtils.h" - -/* if within half a second after adding an IP address a matching device-route shows - * up, we delete it. */ -#define IP4_DEVICE_ROUTES_WAIT_TIME_NS (NM_UTILS_NS_PER_SECOND / 2) - -#define IP4_DEVICE_ROUTES_GC_INTERVAL_MSEC ((IP4_DEVICE_ROUTES_WAIT_TIME_NS / 1000000) * 3) - -/*****************************************************************************/ - -typedef struct { - guint len; - NMPlatformIPXRoute *entries[1]; -} RouteIndex; - -typedef struct { - GArray *entries; - RouteIndex *index; - - /* list of effective metrics. The indexes of the array correspond to @index, not @entries. */ - GArray *effective_metrics; - - /* this array contains the effective metrics but using the reversed index that corresponds - * to @entries, instead of @index. */ - GArray *effective_metrics_reverse; -} RouteEntries; - -typedef struct { - NMRouteManager *self; - gint64 scheduled_at_ns; - guint idle_id; - const NMPObject *obj; - const NMPObject *obj_cached; -} IP4DeviceRoutePurgeEntry; - -/*****************************************************************************/ - -enum { - IP4_ROUTES_CHANGED, - LAST_SIGNAL, -}; -static guint signals[LAST_SIGNAL] = { 0 }; - -NM_GOBJECT_PROPERTIES_DEFINE_BASE ( - PROP_LOG_WITH_PTR, - PROP_PLATFORM, -); - -typedef struct { - NMPlatform *platform; - - RouteEntries ip4_routes; - RouteEntries ip6_routes; - struct { - GHashTable *entries; - guint gc_id; - } ip4_device_routes; - - bool log_with_ptr; -} NMRouteManagerPrivate; - -struct _NMRouteManager { - GObject parent; - NMRouteManagerPrivate _priv; -}; - -struct _NMRouteManagerClass { - GObjectClass parent; -}; - -G_DEFINE_TYPE (NMRouteManager, nm_route_manager, G_TYPE_OBJECT); - -#define NM_ROUTE_MANAGER_GET_PRIVATE(self) _NM_GET_PRIVATE (self, NMRouteManager, NM_IS_ROUTE_MANAGER) - -/*****************************************************************************/ - -typedef struct { - const NMPlatformVTableRoute *vt; - - /* a compare function for two routes that considers only the destination fields network/plen. - * It is a looser comparisong then @route_id_cmp(), that means that if @route_dest_cmp() - * returns non-zero, also @route_id_cmp() returns the same value. It also means, that - * sorting by @route_id_cmp() implicitly sorts by @route_dest_cmp() as well. */ - int (*route_dest_cmp) (const NMPlatformIPXRoute *r1, const NMPlatformIPXRoute *r2); - - /* a compare function for two routes that considers only the fields network/plen,metric. */ - int (*route_id_cmp) (const NMPlatformIPXRoute *r1, const NMPlatformIPXRoute *r2); -} VTableIP; - -static const VTableIP vtable_v4, vtable_v6; - -#define VTABLE_ROUTE_INDEX(vtable, garray, idx) ((NMPlatformIPXRoute *) &((garray)->data[(idx) * (vtable)->vt->sizeof_route])) - -#define VTABLE_IS_DEVICE_ROUTE(vtable, route) ((vtable)->vt->is_ip4 \ - ? ((route)->r4.gateway == 0) \ - : IN6_IS_ADDR_UNSPECIFIED (&(route)->r6.gateway) ) - -#define CMP_AND_RETURN_INT(a, b) \ - G_STMT_START { \ - typeof(a) _a = (a), _b = (b); \ - \ - if (_a < _b) \ - return -1; \ - if (_a > _b) \ - return 1; \ - } G_STMT_END - -/*****************************************************************************/ - -#define _NMLOG_PREFIX_NAME "route-mgr" -#undef _NMLOG_ENABLED -#define _NMLOG_ENABLED(level, addr_family) \ - ({ \ - const int __addr_family = (addr_family); \ - const NMLogLevel __level = (level); \ - const NMLogDomain __domain = __addr_family == AF_INET ? LOGD_IP4 : (__addr_family == AF_INET6 ? LOGD_IP6 : LOGD_IP); \ - \ - nm_logging_enabled (__level, __domain); \ - }) -#define _NMLOG(level, addr_family, ...) \ - G_STMT_START { \ - const int __addr_family = (addr_family); \ - const NMLogLevel __level = (level); \ - const NMLogDomain __domain = __addr_family == AF_INET ? LOGD_IP4 : (__addr_family == AF_INET6 ? LOGD_IP6 : LOGD_IP); \ - \ - if (nm_logging_enabled (__level, __domain)) { \ - char __ch = __addr_family == AF_INET ? '4' : (__addr_family == AF_INET6 ? '6' : '-'); \ - char __prefix[30] = _NMLOG_PREFIX_NAME; \ - \ - if (NM_ROUTE_MANAGER_GET_PRIVATE (self)->log_with_ptr) \ - g_snprintf (__prefix, sizeof (__prefix), "%s%c[%p]", _NMLOG_PREFIX_NAME, __ch, (self)); \ - else \ - __prefix[NM_STRLEN (_NMLOG_PREFIX_NAME)] = __ch; \ - _nm_log ((level), (__domain), 0, NULL, NULL, \ - "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ - __prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ - } \ - } G_STMT_END - -/*****************************************************************************/ - -static gboolean _ip4_device_routes_cancel (NMRouteManager *self); - -/*****************************************************************************/ - -#if NM_MORE_ASSERTS && !defined (G_DISABLE_ASSERT) -static inline void -ASSERT_route_index_valid (const VTableIP *vtable, const GArray *entries, const RouteIndex *index, gboolean unique_ifindexes) -{ - guint i, j; - int c; - const NMPlatformIPXRoute *r1, *r2; - gs_unref_hashtable GHashTable *ptrs = g_hash_table_new (NULL, NULL); - const NMPlatformIPXRoute *r_first = NULL, *r_last = NULL; - - g_assert (index); - - if (entries) - g_assert_cmpint (entries->len, ==, index->len); - else - g_assert (index->len == 0); - - if (index->len > 0) { - r_first = VTABLE_ROUTE_INDEX (vtable, entries, 0); - r_last = VTABLE_ROUTE_INDEX (vtable, entries, index->len - 1); - } - - /* assert that the @index is valid for the @entries. */ - - g_assert (!index->entries[index->len]); - for (i = 0; i < index->len; i++) { - r1 = index->entries[i]; - - g_assert (r1); - g_assert (r1 >= r_first); - g_assert (r1 <= r_last); - g_assert_cmpint ((((char *) r1) - ((char *) entries->data)) % vtable->vt->sizeof_route, ==, 0); - - g_assert (!g_hash_table_contains (ptrs, (gpointer) r1)); - g_hash_table_add (ptrs, (gpointer) r1); - - for (j = i; j > 0; ) { - r2 = index->entries[--j]; - - c = vtable->route_id_cmp (r1, r2); - g_assert (c >= 0); - if (c != 0) - break; - if (unique_ifindexes) - g_assert_cmpint (r1->rx.ifindex, !=, r2->rx.ifindex); - } - } -} -#else -#define ASSERT_route_index_valid(vtable, entries, index, unique_ifindexes) G_STMT_START { (void) 0; } G_STMT_END -#endif - -/*****************************************************************************/ - -static int -_v4_route_dest_cmp (const NMPlatformIP4Route *r1, const NMPlatformIP4Route *r2) -{ - CMP_AND_RETURN_INT (r1->plen, r2->plen); - CMP_AND_RETURN_INT (nm_utils_ip4_address_clear_host_address (r1->network, r1->plen), - nm_utils_ip4_address_clear_host_address (r2->network, r2->plen)); - return 0; -} - -static int -_v6_route_dest_cmp (const NMPlatformIP6Route *r1, const NMPlatformIP6Route *r2) -{ - struct in6_addr n1, n2; - - CMP_AND_RETURN_INT (r1->plen, r2->plen); - - nm_utils_ip6_address_clear_host_address (&n1, &r1->network, r1->plen); - nm_utils_ip6_address_clear_host_address (&n2, &r2->network, r2->plen ); - return memcmp (&n1, &n2, sizeof (n1)); -} - -static int -_v4_route_id_cmp (const NMPlatformIP4Route *r1, const NMPlatformIP4Route *r2) -{ - CMP_AND_RETURN_INT (r1->plen, r2->plen); - CMP_AND_RETURN_INT (nm_utils_ip4_address_clear_host_address (r1->network, r1->plen), - nm_utils_ip4_address_clear_host_address (r2->network, r2->plen)); - CMP_AND_RETURN_INT (r1->metric, r2->metric); - return 0; -} - -static int -_v6_route_id_cmp (const NMPlatformIP6Route *r1, const NMPlatformIP6Route *r2) -{ - struct in6_addr n1, n2; - int c; - - CMP_AND_RETURN_INT (r1->plen, r2->plen); - - nm_utils_ip6_address_clear_host_address (&n1, &r1->network, r1->plen); - nm_utils_ip6_address_clear_host_address (&n2, &r2->network, r2->plen); - c = memcmp (&n1, &n2, sizeof (n1)); - if (c != 0) - return c; - - CMP_AND_RETURN_INT (nm_utils_ip6_route_metric_normalize (r1->metric), - nm_utils_ip6_route_metric_normalize (r2->metric)); - return 0; -} - -/*****************************************************************************/ - -static int -_route_index_create_sort (const NMPlatformIPXRoute **p1, const NMPlatformIPXRoute ** p2, const VTableIP *vtable) -{ - return vtable->route_id_cmp (*p1, *p2); -} - -static RouteIndex * -_route_index_create (const VTableIP *vtable, const GArray *routes) -{ - RouteIndex *index; - guint i; - guint len = routes ? routes->len : 0; - - index = g_malloc (sizeof (RouteIndex) + len * sizeof (NMPlatformIPXRoute *)); - - index->len = len; - for (i = 0; i < len; i++) - index->entries[i] = VTABLE_ROUTE_INDEX (vtable, routes, i); - index->entries[i] = NULL; - - /* this is a stable sort, which is very important at this point. */ - g_qsort_with_data (index->entries, - len, - sizeof (NMPlatformIPXRoute *), - (GCompareDataFunc) _route_index_create_sort, - (gpointer) vtable); - return index; -} - -static RouteIndex * -_route_index_create_from_platform (const VTableIP *vtable, - NMPlatform *platform, - int ifindex, - gboolean ignore_kernel_routes, - GPtrArray **out_storage) -{ - RouteIndex *index; - guint i, j, len; - GPtrArray *storage; - - nm_assert (out_storage && !*out_storage); - - storage = nm_platform_lookup_addrroute_clone (platform, - vtable->vt->obj_type, - ifindex, - ignore_kernel_routes - ? nm_platform_lookup_predicate_routes_skip_rtprot_kernel - : NULL, - NULL); - if (!storage) - return _route_index_create (vtable, NULL); - - len = storage->len; - index = g_malloc (sizeof (RouteIndex) + len * sizeof (NMPlatformIPXRoute *)); - - j = 0; - for (i = 0; i < len; i++) { - const NMPlatformIPXRoute *ipx_route = NMP_OBJECT_CAST_IPX_ROUTE (storage->pdata[i]); - - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (ipx_route)) - continue; - - /* we cast away the const-ness of the NMPObjects. The caller must - * ensure not to modify the object via index->entries. */ - index->entries[j++] = (NMPlatformIPXRoute *) ipx_route; - } - index->entries[j] = NULL; - index->len = j; - - /* this is a stable sort, which is very important at this point. */ - g_qsort_with_data (index->entries, - index->len, - sizeof (NMPlatformIPXRoute *), - (GCompareDataFunc) _route_index_create_sort, - (gpointer) vtable); - *out_storage = storage; - return index; -} - -static int -_vx_route_id_cmp_full (const NMPlatformIPXRoute *r1, const NMPlatformIPXRoute *r2, const VTableIP *vtable) -{ - return vtable->route_id_cmp (r1, r2); -} - -static gssize -_route_index_find (const VTableIP *vtable, const RouteIndex *index, const NMPlatformIPXRoute *needle) -{ - gssize idx, idx2; - - idx = _nm_utils_ptrarray_find_binary_search ((gconstpointer *) index->entries, index->len, needle, (GCompareDataFunc) _vx_route_id_cmp_full, (gpointer) vtable); - if (idx < 0) - return idx; - - /* we only know that the route at index @idx has matching destination. Also find the one with the right - * ifindex by searching the neighbours */ - - idx2 = idx; - do { - if (index->entries[idx2]->rx.ifindex == needle->rx.ifindex) - return idx2; - } while ( idx2 > 0 - && vtable->route_id_cmp (index->entries[--idx2], needle) != 0); - - for (idx++; idx < index->len; idx++ ){ - if (vtable->route_id_cmp (index->entries[idx], needle) != 0) - break; - if (index->entries[idx]->rx.ifindex == needle->rx.ifindex) - return idx; - } - - return ~idx; -} - -static guint -_route_index_reverse_idx (const VTableIP *vtable, const RouteIndex *index, guint idx_idx, const GArray *routes) -{ - const NMPlatformIPXRoute *r, *r0; - gssize offset; - - /* reverse the @idx_idx that points into @index, to the corresponding index into the unsorted @routes array. */ - - r = index->entries[idx_idx]; - r0 = VTABLE_ROUTE_INDEX (vtable, routes, 0); - - if (vtable->vt->is_ip4) - offset = &r->r4 - &r0->r4; - else - offset = &r->r6 - &r0->r6; - g_assert (offset >= 0 && offset < index->len); - g_assert (VTABLE_ROUTE_INDEX (vtable, routes, offset) == r); - return offset; -} - -/*****************************************************************************/ - -static gboolean -_route_equals_ignoring_ifindex (const VTableIP *vtable, const NMPlatformIPXRoute *r1, const NMPlatformIPXRoute *r2, gint64 r2_metric) -{ - NMPlatformIPXRoute r2_backup; - - if ( r1->rx.ifindex != r2->rx.ifindex - || (r2_metric >= 0 && ((guint32) r2_metric) != r2->rx.metric)) { - memcpy (&r2_backup, r2, vtable->vt->sizeof_route); - r2_backup.rx.ifindex = r1->rx.ifindex; - if (r2_metric >= 0) - r2_backup.rx.metric = (guint32) r2_metric; - r2 = &r2_backup; - } - return vtable->vt->route_cmp (r1, r2, NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) == 0; -} - -static NMPlatformIPXRoute * -_get_next_ipx_route (const RouteIndex *index, gboolean start_at_zero, guint *cur_idx, int ifindex) -{ - guint i; - - if (start_at_zero) - i = 0; - else - i = *cur_idx + 1; - /* Find the next route with matching @ifindex. */ - for (; i < index->len; i++) { - if (index->entries[i]->rx.ifindex == ifindex) { - *cur_idx = i; - return index->entries[i]; - } - } - *cur_idx = index->len; - return NULL; -} - -static const NMPlatformIPXRoute * -_get_next_known_route (const VTableIP *vtable, const RouteIndex *index, gboolean start_at_zero, guint *cur_idx) -{ - guint i = 0; - const NMPlatformIPXRoute *cur = NULL; - - if (!start_at_zero) { - i = *cur_idx; - cur = index->entries[i]; - i++; - } - /* For @known_routes we expect that all routes have the same @ifindex. This is not enforced however, - * the ifindex value of these routes is ignored. */ - for (; i < index->len; i++) { - const NMPlatformIPXRoute *r = index->entries[i]; - - /* skip over default routes. */ - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r)) - continue; - - /* @known_routes should not, but could contain duplicate routes. Skip over them. */ - if (cur && vtable->route_id_cmp (cur, r) == 0) - continue; - - *cur_idx = i; - return r; - } - *cur_idx = index->len; - return NULL; -} - -static const NMPlatformIPXRoute * -_get_next_plat_route (const RouteIndex *index, gboolean start_at_zero, guint *cur_idx) -{ - if (start_at_zero) - *cur_idx = 0; - else - ++*cur_idx; - - /* get next route from the platform index. */ - if (*cur_idx < index->len) { - nm_assert (NMP_OBJECT_UP_CAST (index->entries[*cur_idx])); - return index->entries[*cur_idx]; - } - *cur_idx = index->len; - return NULL; -} - -static int -_sort_indexes_cmp (guint *a, guint *b) -{ - CMP_AND_RETURN_INT (*a, *b); - g_return_val_if_reached (0); -} - -/*****************************************************************************/ - -static gboolean -_vx_route_sync (const VTableIP *vtable, NMRouteManager *self, int ifindex, const GArray *known_routes, gboolean ignore_kernel_routes, gboolean full_sync) -{ - NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - gs_unref_ptrarray GPtrArray *plat_routes = NULL; - RouteEntries *ipx_routes; - RouteIndex *plat_routes_idx, *known_routes_idx; - gboolean success = TRUE; - guint i, i_type; - GArray *to_delete_indexes = NULL; - GPtrArray *to_add_routes = NULL; - guint i_known_routes, i_plat_routes, i_ipx_routes; - const NMPlatformIPXRoute *cur_known_route, *cur_plat_route; - NMPlatformIPXRoute *cur_ipx_route; - gint64 *p_effective_metric = NULL; - gboolean ipx_routes_changed = FALSE; - gint64 *effective_metrics = NULL; - - nm_platform_process_events (priv->platform); - - ipx_routes = vtable->vt->is_ip4 ? &priv->ip4_routes : &priv->ip6_routes; - - /* the objects referenced by play_routes_idx are shared from the platform cache. They - * must not be modified. */ - plat_routes_idx = _route_index_create_from_platform (vtable, priv->platform, ifindex, ignore_kernel_routes, &plat_routes); - - known_routes_idx = _route_index_create (vtable, known_routes); - - effective_metrics = &g_array_index (ipx_routes->effective_metrics, gint64, 0); - - ASSERT_route_index_valid (vtable, known_routes, known_routes_idx, FALSE); - - _LOGD (vtable->vt->addr_family, "%3d: sync %u IPv%c routes", ifindex, known_routes_idx->len, vtable->vt->is_ip4 ? '4' : '6'); - if (_LOGt_ENABLED (vtable->vt->addr_family)) { - for (i = 0; i < known_routes_idx->len; i++) { - _LOGt (vtable->vt->addr_family, "%3d: sync new route #%u: %s", - ifindex, i, vtable->vt->route_to_string (VTABLE_ROUTE_INDEX (vtable, known_routes, i), NULL, 0)); - } - for (i = 0; i < ipx_routes->index->len; i++) - _LOGt (vtable->vt->addr_family, "%3d: STATE: has #%u - %s (%lld)", - ifindex, i, - vtable->vt->route_to_string (ipx_routes->index->entries[i], NULL, 0), - (long long) g_array_index (ipx_routes->effective_metrics, gint64, i)); - } - - /*************************************************************************** - * Check which routes are in @known_routes, and update @ipx_routes. - * - * This first part only updates @ipx_routes to find out what routes must - * be added/deleted. - **************************************************************************/ - - /* iterate over @ipx_routes and @known_routes */ - cur_ipx_route = _get_next_ipx_route (ipx_routes->index, TRUE, &i_ipx_routes, ifindex); - cur_known_route = _get_next_known_route (vtable, known_routes_idx, TRUE, &i_known_routes); - while (cur_ipx_route || cur_known_route) { - int route_id_cmp_result = -1; - - while ( cur_ipx_route - && ( !cur_known_route - || ((route_id_cmp_result = vtable->route_id_cmp (cur_ipx_route, cur_known_route)) < 0))) { - /* we have @cur_ipx_route, which is less then @cur_known_route. Hence, - * the route does no longer exist in @known_routes */ - if (!to_delete_indexes) - to_delete_indexes = g_array_new (FALSE, FALSE, sizeof (guint)); - g_array_append_val (to_delete_indexes, i_ipx_routes); - - /* find the next @cur_ipx_route with matching ifindex. */ - cur_ipx_route = _get_next_ipx_route (ipx_routes->index, FALSE, &i_ipx_routes, ifindex); - } - if ( cur_ipx_route - && cur_known_route - && route_id_cmp_result == 0) { - if (!_route_equals_ignoring_ifindex (vtable, cur_ipx_route, cur_known_route, -1)) { - /* The routes match. Update the entry in place. As this is an exact match of primary - * fields, this only updates possibly modified fields such as @gateway or @mss. - * Modifiying @cur_ipx_route this way does not invalidate @ipx_routes->index. */ - memcpy (cur_ipx_route, cur_known_route, vtable->vt->sizeof_route); - cur_ipx_route->rx.ifindex = ifindex; - cur_ipx_route->rx.metric = vtable->vt->metric_normalize (cur_ipx_route->rx.metric); - nm_utils_ipx_address_clear_host_address (vtable->vt->addr_family, cur_ipx_route->rx.network_ptr, - cur_ipx_route->rx.network_ptr, cur_ipx_route->rx.plen); - ipx_routes_changed = TRUE; - _LOGt (vtable->vt->addr_family, "%3d: STATE: update #%u - %s", ifindex, i_ipx_routes, - vtable->vt->route_to_string (cur_ipx_route, NULL, 0)); - } - } else if (cur_known_route) { - g_assert (!cur_ipx_route || route_id_cmp_result > 0); - /* @cur_known_route is new. We cannot immediately add @cur_known_route to @ipx_routes, because - * it would invalidate @ipx_routes->index. Instead remember to add it later. */ - if (!to_add_routes) - to_add_routes = g_ptr_array_new (); - g_ptr_array_add (to_add_routes, (gpointer) cur_known_route); - } - - if (cur_ipx_route && (!cur_known_route || route_id_cmp_result == 0)) - cur_ipx_route = _get_next_ipx_route (ipx_routes->index, FALSE, &i_ipx_routes, ifindex); - if (cur_known_route) - cur_known_route = _get_next_known_route (vtable, known_routes_idx, FALSE, &i_known_routes); - } - - if (!full_sync && to_delete_indexes) { - /*************************************************************************** - * Delete routes in platform, that we are about to remove from @ipx_routes - * - * When doing a non-full_sync, we delete routes from platform that were previously - * known by route-manager, and are now deleted. - ***************************************************************************/ - - /* iterate over @to_delete_indexes and @plat_routes. - * @to_delete_indexes contains the indexes (relative to ipx_routes->index) of items - * we are about to delete. */ - cur_plat_route = _get_next_plat_route (plat_routes_idx, TRUE, &i_plat_routes); - for (i = 0; i < to_delete_indexes->len; i++) { - int route_dest_cmp_result = 0; - i_ipx_routes = g_array_index (to_delete_indexes, guint, i); - cur_ipx_route = ipx_routes->index->entries[i_ipx_routes]; - p_effective_metric = &effective_metrics[i_ipx_routes]; - - nm_assert (cur_ipx_route->rx.ifindex == ifindex); - - if (*p_effective_metric == -1) - continue; - - /* skip over @plat_routes that are ordered before our @cur_ipx_route. */ - while ( cur_plat_route - && (route_dest_cmp_result = vtable->route_dest_cmp (cur_plat_route, cur_ipx_route)) <= 0) { - if ( route_dest_cmp_result == 0 - && cur_plat_route->rx.metric >= *p_effective_metric) - break; - cur_plat_route = _get_next_plat_route (plat_routes_idx, FALSE, &i_plat_routes); - } - - if (!cur_plat_route) { - /* no more platform routes. Break the loop. */ - break; - } - - if ( route_dest_cmp_result == 0 - && cur_plat_route->rx.metric == *p_effective_metric) { - /* we are about to delete cur_ipx_route and we have a matching route - * in platform. Delete it. */ - _LOGt (vtable->vt->addr_family, "%3d: platform rt-rm #%u - %s", ifindex, i_plat_routes, - vtable->vt->route_to_string (cur_plat_route, NULL, 0)); - nm_assert (ifindex == cur_plat_route->rx.ifindex); - nm_platform_ip_route_delete (priv->platform, NMP_OBJECT_UP_CAST (cur_plat_route)); - } - } - } - - /* Update @ipx_routes with the just learned changes. */ - if (to_delete_indexes || to_add_routes) { - if (to_delete_indexes) { - for (i = 0; i < to_delete_indexes->len; i++) { - guint idx = g_array_index (to_delete_indexes, guint, i); - - _LOGt (vtable->vt->addr_family, "%3d: STATE: delete #%u - %s", ifindex, idx, - vtable->vt->route_to_string (ipx_routes->index->entries[idx], NULL, 0)); - g_array_index (to_delete_indexes, guint, i) = _route_index_reverse_idx (vtable, ipx_routes->index, idx, ipx_routes->entries); - } - g_array_sort (to_delete_indexes, (GCompareFunc) _sort_indexes_cmp); - nm_utils_array_remove_at_indexes (ipx_routes->entries, &g_array_index (to_delete_indexes, guint, 0), to_delete_indexes->len); - nm_utils_array_remove_at_indexes (ipx_routes->effective_metrics_reverse, &g_array_index (to_delete_indexes, guint, 0), to_delete_indexes->len); - g_array_unref (to_delete_indexes); - } - if (to_add_routes) { - guint j = ipx_routes->effective_metrics_reverse->len; - - g_array_set_size (ipx_routes->effective_metrics_reverse, j + to_add_routes->len); - - for (i = 0; i < to_add_routes->len; i++) { - NMPlatformIPXRoute *ipx_route; - - g_array_append_vals (ipx_routes->entries, g_ptr_array_index (to_add_routes, i), 1); - - ipx_route = VTABLE_ROUTE_INDEX (vtable, ipx_routes->entries, ipx_routes->entries->len - 1); - ipx_route->rx.ifindex = ifindex; - ipx_route->rx.metric = vtable->vt->metric_normalize (ipx_route->rx.metric); - nm_utils_ipx_address_clear_host_address (vtable->vt->addr_family, ipx_route->rx.network_ptr, - ipx_route->rx.network_ptr, ipx_route->rx.plen); - - g_array_index (ipx_routes->effective_metrics_reverse, gint64, j++) = -1; - - _LOGt (vtable->vt->addr_family, "%3d: STATE: added #%u - %s", ifindex, ipx_routes->entries->len - 1, - vtable->vt->route_to_string (ipx_route, NULL, 0)); - } - g_ptr_array_unref (to_add_routes); - } - g_free (ipx_routes->index); - ipx_routes->index = _route_index_create (vtable, ipx_routes->entries); - ipx_routes_changed = TRUE; - ASSERT_route_index_valid (vtable, ipx_routes->entries, ipx_routes->index, TRUE); - } - - if (ipx_routes_changed) { - /*************************************************************************** - * Rebuild the list of effective metrics. In case of conflicting routes, - * we configure device routes with a bumped metric. We do this, because non-direct - * routes might require this direct route to reach the gateway (e.g. the default - * route). - * - * We determine the effective metrics only based on our internal list @ipx_routes - * and don't consider @plat_routes. That means, we might bump the metric of a route - * and thereby cause a conflict with an existing route on an unmanaged device (which - * causes the route on the unmanaged device to be replaced). - * Still, that is not much different then from messing with unmanaged routes when - * the effective and the intended metrics equal. The rules is: NM will leave routes - * on unmanaged devices alone, unless they conflict with what NM wants to configure. - ***************************************************************************/ - - g_array_set_size (ipx_routes->effective_metrics, ipx_routes->entries->len); - effective_metrics = &g_array_index (ipx_routes->effective_metrics, gint64, 0); - - /* Completely regenerate the list of effective metrics by walking through - * ipx_routes->index and determining the effective metric. */ - - for (i_ipx_routes = 0; i_ipx_routes < ipx_routes->index->len; i_ipx_routes++) { - gint64 *p_effective_metric_before; - gboolean is_shadowed; - guint i_ipx_routes_before; - - cur_ipx_route = ipx_routes->index->entries[i_ipx_routes]; - p_effective_metric = &effective_metrics[i_ipx_routes]; - - is_shadowed = i_ipx_routes > 0 - && vtable->route_dest_cmp (cur_ipx_route, ipx_routes->index->entries[i_ipx_routes - 1]) == 0; - - if (!is_shadowed) { - /* the route is not shadowed, the effective metric is just as specified. */ - *p_effective_metric = cur_ipx_route->rx.metric; - goto next; - } - if (!VTABLE_IS_DEVICE_ROUTE (vtable, cur_ipx_route)) { - /* The route is not a device route. We want to add redundant device routes, because - * we might need the direct routes to the gateway. For non-direct routes, there is not much - * reason to do the metric increment. */ - *p_effective_metric = -1; - goto next; - } - - /* The current route might be shadowed by several other routes. Find the one with the highest metric, - * i.e. the one with an effecive metric set and in the index before the current index. */ - i_ipx_routes_before = i_ipx_routes; - while (TRUE) { - nm_assert (i_ipx_routes_before > 0); - - i_ipx_routes_before--; - - p_effective_metric_before = &effective_metrics[i_ipx_routes_before]; - - if (*p_effective_metric_before == -1) { - /* this route is also shadowed, continue search. */ - continue; - } - - if (*p_effective_metric_before < cur_ipx_route->rx.metric) { - /* the previous route has a lower metric. There is no conflict, - * just use the original metric. */ - *p_effective_metric = cur_ipx_route->rx.metric; - } else if (*p_effective_metric_before == G_MAXUINT32) { - /* we cannot bump the metric. Don't configure this route. */ - *p_effective_metric = -1; - } else { - /* bump the metric by one. */ - *p_effective_metric = *p_effective_metric_before + 1; - } - break; - } -next: - _LOGt (vtable->vt->addr_family, "%3d: new metric #%u - %s (%lld)", - ifindex, i_ipx_routes, - vtable->vt->route_to_string (cur_ipx_route, NULL, 0), - (long long) *p_effective_metric); - } - } - - if (full_sync) { - /*************************************************************************** - * Delete all routes in platform, that no longer exist in @ipx_routes - * - * Different from the delete action above, we delete every unknown route on - * the interface. - ***************************************************************************/ - - /* iterate over @plat_routes and @ipx_routes */ - cur_plat_route = _get_next_plat_route (plat_routes_idx, TRUE, &i_plat_routes); - cur_ipx_route = _get_next_ipx_route (ipx_routes->index, TRUE, &i_ipx_routes, ifindex); - if (cur_ipx_route) - p_effective_metric = &effective_metrics[i_ipx_routes]; - while (cur_plat_route) { - int route_dest_cmp_result = 0; - - nm_assert (cur_plat_route->rx.ifindex == ifindex); - - _LOGt (vtable->vt->addr_family, "%3d: platform rt #%u - %s", ifindex, i_plat_routes, vtable->vt->route_to_string (cur_plat_route, NULL, 0)); - - /* skip over @cur_ipx_route that are ordered before @cur_plat_route */ - while ( cur_ipx_route - && ((route_dest_cmp_result = vtable->route_dest_cmp (cur_ipx_route, cur_plat_route)) <= 0)) { - if ( route_dest_cmp_result == 0 - && *p_effective_metric != -1 - && *p_effective_metric >= cur_plat_route->rx.metric) { - break; - } - cur_ipx_route = _get_next_ipx_route (ipx_routes->index, FALSE, &i_ipx_routes, ifindex); - if (cur_ipx_route) - p_effective_metric = &effective_metrics[i_ipx_routes]; - } - - /* if @cur_ipx_route is not equal to @plat_route, the route must be deleted. */ - if ( !cur_ipx_route - || route_dest_cmp_result != 0 - || *p_effective_metric != cur_plat_route->rx.metric) { - nm_assert (ifindex == cur_plat_route->rx.ifindex); - nm_platform_ip_route_delete (priv->platform, NMP_OBJECT_UP_CAST (cur_plat_route)); - } - - cur_plat_route = _get_next_plat_route (plat_routes_idx, FALSE, &i_plat_routes); - } - } - - /*************************************************************************** - * Restore shadowed routes. These routes are on an other @ifindex then what - * we are syncing now. But the current changes make it necessary to add those - * routes. - * - * Only add some routes that might be necessary. We don't delete any routes - * on other ifindexes here. I.e. we don't do a full sync, but only ~add~ routes - * that were shadowed previously, but should be now present with a different - * metric. - **************************************************************************/ - - if (ipx_routes_changed) { - GArray *gateway_routes = NULL; - - /* @effective_metrics_reverse contains the list of assigned metrics from the last - * sync. Walk through it and see what changes there are (and possibly restore a - * shadowed route). - * Thereby also update @effective_metrics_reverse to be up-to-date again. */ - for (i_ipx_routes = 0; i_ipx_routes < ipx_routes->entries->len; i_ipx_routes++) { - guint i_ipx_routes_reverse; - gint64 *p_effective_metric_reversed; - - p_effective_metric = &effective_metrics[i_ipx_routes]; - - i_ipx_routes_reverse = _route_index_reverse_idx (vtable, ipx_routes->index, i_ipx_routes, ipx_routes->entries); - p_effective_metric_reversed = &g_array_index (ipx_routes->effective_metrics_reverse, gint64, i_ipx_routes_reverse); - - if (*p_effective_metric_reversed == *p_effective_metric) { - /* The entry is up to date. No change, continue with the next one. */ - continue; - } - *p_effective_metric_reversed = *p_effective_metric; - - if (*p_effective_metric == -1) { - /* the entry is shadowed. Nothing to do. */ - continue; - } - - cur_ipx_route = ipx_routes->index->entries[i_ipx_routes]; - if (cur_ipx_route->rx.ifindex == ifindex) { - /* @cur_ipx_route is on the current @ifindex. No need to special handling them - * because we are about to do a full sync of the ifindex. */ - continue; - } - - /* the effective metric from previous sync changed. While @cur_ipx_route is not on the - * ifindex we are about to sync, we still must add this route. Possibly it was shadowed - * before, and now we want to restore it. - * - * Note that we don't do a full sync on the other ifindex. Especially, we don't delete - * or add any further routes then this. That means there might be some stale routes - * (with a higher metric!). They will only be removed on the next sync of that other - * ifindex. */ - - if (!VTABLE_IS_DEVICE_ROUTE (vtable, cur_ipx_route)) { - /* the route to restore has a gateway. We can only restore the route - * when we also have a direct route to the gateway. There can be cases - * where the direct route is shadowed too, and we cannot restore the gateway - * route. - * - * Restore first the direct-routes, and gateway-routes afterwards. - * This can avoid some cases where we would fail to add the - * gateway route. */ - if (!gateway_routes) - gateway_routes = g_array_new (FALSE, FALSE, sizeof (guint)); - g_array_append_val (gateway_routes, i_ipx_routes); - } else - vtable->vt->route_add (priv->platform, NMP_NLM_FLAG_REPLACE, - cur_ipx_route, 0, *p_effective_metric); - } - - if (gateway_routes) { - for (i = 0; i < gateway_routes->len; i++) { - i_ipx_routes = g_array_index (gateway_routes, guint, i); - vtable->vt->route_add (priv->platform, NMP_NLM_FLAG_REPLACE, - ipx_routes->index->entries[i_ipx_routes], - 0, effective_metrics[i_ipx_routes]); - } - g_array_unref (gateway_routes); - } - } - - /*************************************************************************** - * Sync @ipx_routes for @ifindex to platform - **************************************************************************/ - - for (i_type = 0; i_type < 2; i_type++) { - /* iterate (twice) over @ipx_routes and @plat_routes */ - cur_plat_route = _get_next_plat_route (plat_routes_idx, TRUE, &i_plat_routes); - cur_ipx_route = _get_next_ipx_route (ipx_routes->index, TRUE, &i_ipx_routes, ifindex); - /* Iterate here over @ipx_routes instead of @known_routes. That is done because - * we need to know whether a route is shadowed by another route, and that - * requires to look at @ipx_routes. */ - for (; cur_ipx_route; cur_ipx_route = _get_next_ipx_route (ipx_routes->index, FALSE, &i_ipx_routes, ifindex)) { - int route_dest_cmp_result = -1; - - if ( (i_type == 0 && !VTABLE_IS_DEVICE_ROUTE (vtable, cur_ipx_route)) - || (i_type == 1 && VTABLE_IS_DEVICE_ROUTE (vtable, cur_ipx_route))) { - /* Make two runs over the list of @ipx_routes. On the first, only add - * device routes, on the second the others (gateway routes). */ - continue; - } - - p_effective_metric = &effective_metrics[i_ipx_routes]; - - if (*p_effective_metric == -1) { - /* @cur_ipx_route is shadewed by another route. */ - continue; - } - - /* skip over @plat_routes that are ordered before our @cur_ipx_route. */ - while ( cur_plat_route - && (route_dest_cmp_result = vtable->route_dest_cmp (cur_plat_route, cur_ipx_route)) <= 0) { - if ( route_dest_cmp_result == 0 - && cur_plat_route->rx.metric >= *p_effective_metric) - break; - cur_plat_route = _get_next_plat_route (plat_routes_idx, FALSE, &i_plat_routes); - } - - /* only add the route if we don't have an identical route in @plat_routes, - * i.e. if @cur_plat_route is different from @cur_ipx_route. */ - if ( !cur_plat_route - || route_dest_cmp_result != 0 - || !_route_equals_ignoring_ifindex (vtable, cur_plat_route, cur_ipx_route, *p_effective_metric)) { - - if (!vtable->vt->route_add (priv->platform, NMP_NLM_FLAG_REPLACE, - cur_ipx_route, ifindex, *p_effective_metric)) { - if (cur_ipx_route->rx.rt_source < NM_IP_CONFIG_SOURCE_USER) { - _LOGD (vtable->vt->addr_family, - "ignore error adding IPv%c route to kernel: %s", - vtable->vt->is_ip4 ? '4' : '6', - vtable->vt->route_to_string (cur_ipx_route, NULL, 0)); - } else { - /* Remember that there was a failure, but for now continue trying - * to sync the remaining routes. */ - success = FALSE; - } - } - } - } - } - - if (vtable->vt->is_ip4 && ipx_routes_changed) - g_signal_emit (self, signals[IP4_ROUTES_CHANGED], 0); - - g_free (known_routes_idx); - g_free (plat_routes_idx); - - return success; -} - -/** - * nm_route_manager_ip4_route_sync: - * @ifindex: Interface index - * @known_routes: List of routes - * @ignore_kernel_routes: if %TRUE, ignore kernel routes. - * @full_sync: whether to do a full sync and delete routes - * that are configured on the interface but not currently - * tracked by route-manager. - * - * A convenience function to synchronize routes for a specific interface - * with the least possible disturbance. It simply removes routes that are - * not listed and adds routes that are. - * Default routes are ignored (both in @known_routes and those already - * configured on the device). - * - * Returns: %TRUE on success. - */ -gboolean -nm_route_manager_ip4_route_sync (NMRouteManager *self, int ifindex, const GArray *known_routes, gboolean ignore_kernel_routes, gboolean full_sync) -{ - return _vx_route_sync (&vtable_v4, self, ifindex, known_routes, ignore_kernel_routes, full_sync); -} - -/** - * nm_route_manager_ip6_route_sync: - * @ifindex: Interface index - * @known_routes: List of routes - * @ignore_kernel_routes: if %TRUE, ignore kernel routes. - * @full_sync: whether to do a full sync and delete routes - * that are configured on the interface but not currently - * tracked by route-manager. - * - * A convenience function to synchronize routes for a specific interface - * with the least possible disturbance. It simply removes routes that are - * not listed and adds routes that are. - * Default routes are ignored (both in @known_routes and those already - * configured on the device). - * - * Returns: %TRUE on success. - */ -gboolean -nm_route_manager_ip6_route_sync (NMRouteManager *self, int ifindex, const GArray *known_routes, gboolean ignore_kernel_routes, gboolean full_sync) -{ - return _vx_route_sync (&vtable_v6, self, ifindex, known_routes, ignore_kernel_routes, full_sync); -} - -gboolean -nm_route_manager_route_flush (NMRouteManager *self, int ifindex) -{ - bool success = TRUE; - - success &= (bool) nm_route_manager_ip4_route_sync (self, ifindex, NULL, FALSE, TRUE); - success &= (bool) nm_route_manager_ip6_route_sync (self, ifindex, NULL, FALSE, TRUE); - return success; -} - -/** - * nm_route_manager_ip4_routes_shadowed: - * @ifindex: Interface index - * - * Returns: %TRUE if some other link has a route to the same destination - * with a lower metric. - */ -gboolean -nm_route_manager_ip4_routes_shadowed (NMRouteManager *self, int ifindex) -{ - NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - RouteIndex *index = priv->ip4_routes.index; - const NMPlatformIP4Route *route; - guint i; - - for (i = 1; i < index->len; i++) { - route = (const NMPlatformIP4Route *) index->entries[i]; - - if (route->ifindex != ifindex) - continue; - if (_v4_route_dest_cmp (route, (const NMPlatformIP4Route *) index->entries[i - 1]) == 0) - return TRUE; - } - - return FALSE; -} - -/*****************************************************************************/ - -static gboolean -_ip4_device_routes_entry_expired (const IP4DeviceRoutePurgeEntry *entry, gint64 now) -{ - return entry->scheduled_at_ns + IP4_DEVICE_ROUTES_WAIT_TIME_NS < now; -} - -static IP4DeviceRoutePurgeEntry * -_ip4_device_routes_purge_entry_create (NMRouteManager *self, const NMPlatformIP4Route *route, gint64 now_ns) -{ - IP4DeviceRoutePurgeEntry *entry; - - entry = g_slice_new (IP4DeviceRoutePurgeEntry); - - entry->self = self; - entry->scheduled_at_ns = now_ns; - entry->idle_id = 0; - entry->obj = nmp_object_new (NMP_OBJECT_TYPE_IP4_ROUTE, (NMPlatformObject *) route); - entry->obj_cached = NULL; - return entry; -} - -static void -_ip4_device_routes_purge_entry_free (IP4DeviceRoutePurgeEntry *entry) -{ - nmp_object_unref (entry->obj); - nmp_object_unref (entry->obj_cached); - nm_clear_g_source (&entry->idle_id); - g_slice_free (IP4DeviceRoutePurgeEntry, entry); -} - -static gboolean -_ip4_device_routes_idle_cb (IP4DeviceRoutePurgeEntry *entry) -{ - NMRouteManager *self; - NMRouteManagerPrivate *priv; - - nm_clear_g_source (&entry->idle_id); - - self = entry->self; - priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - if (_route_index_find (&vtable_v4, priv->ip4_routes.index, &entry->obj->ipx_route) >= 0) { - /* we have an identical route in our list. Don't delete it. */ - return G_SOURCE_REMOVE; - } - - _LOGt (vtable_v4.vt->addr_family, "device-route: delete %s", nmp_object_to_string (entry->obj_cached, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - - nm_platform_ip_route_delete (priv->platform, entry->obj_cached); - - g_hash_table_remove (priv->ip4_device_routes.entries, entry->obj); - _ip4_device_routes_cancel (self); - return G_SOURCE_REMOVE; -} - -static void -_ip4_device_routes_ip4_route_changed (NMPlatform *platform, - int obj_type_i, - int ifindex, - const NMPlatformIP4Route *route, - int change_type_i, - NMRouteManager *self) -{ - const NMPlatformSignalChangeType change_type = change_type_i; - NMRouteManagerPrivate *priv; - const NMPObject *obj; - IP4DeviceRoutePurgeEntry *entry; - - if ( route->rt_source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL - || route->metric != 0) { - /* we don't have an automatically created device route at hand. Bail out early. */ - return; - } - - priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - - obj = NMP_OBJECT_UP_CAST (route); - - entry = g_hash_table_lookup (priv->ip4_device_routes.entries, obj); - if (!entry) - return; - - if (_ip4_device_routes_entry_expired (entry, nm_utils_get_monotonic_timestamp_ns ())) { - _LOGt (vtable_v4.vt->addr_family, "device-route: cleanup-ch %s", nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - g_hash_table_remove (priv->ip4_device_routes.entries, entry->obj); - _ip4_device_routes_cancel (self); - return; - } - - entry->obj_cached = nmp_object_unref (entry->obj_cached); - - if (change_type == NM_PLATFORM_SIGNAL_REMOVED) { - if (nm_clear_g_source (&entry->idle_id)) - _LOGt (vtable_v4.vt->addr_family, "device-route: unschedule %s", nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - return; - } - - entry->obj_cached = nmp_object_ref (obj); - if (entry->idle_id == 0) { - _LOGt (vtable_v4.vt->addr_family, "device-route: schedule %s", nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - entry->idle_id = g_idle_add ((GSourceFunc) _ip4_device_routes_idle_cb, entry); - } -} - -static gboolean -_ip4_device_routes_cancel (NMRouteManager *self) -{ - NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - - if (priv->ip4_device_routes.gc_id) { - if (g_hash_table_size (priv->ip4_device_routes.entries) > 0) - return G_SOURCE_CONTINUE; - _LOGt (vtable_v4.vt->addr_family, "device-route: cancel"); - if (priv->platform) - g_signal_handlers_disconnect_by_func (priv->platform, G_CALLBACK (_ip4_device_routes_ip4_route_changed), self); - nm_clear_g_source (&priv->ip4_device_routes.gc_id); - } - return G_SOURCE_REMOVE; -} - -static gboolean -_ip4_device_routes_gc (NMRouteManager *self) -{ - NMRouteManagerPrivate *priv; - GHashTableIter iter; - IP4DeviceRoutePurgeEntry *entry; - gint64 now = nm_utils_get_monotonic_timestamp_ns (); - - priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - - g_hash_table_iter_init (&iter, priv->ip4_device_routes.entries); - while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &entry)) { - if (_ip4_device_routes_entry_expired (entry, now)) { - _LOGt (vtable_v4.vt->addr_family, "device-route: cleanup-gc %s", nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - g_hash_table_iter_remove (&iter); - } - } - - return _ip4_device_routes_cancel (self); -} - -/** - * nm_route_manager_ip4_route_register_device_route_purge_list: - * - * When adding an IPv4 address, kernel will automatically add a device route with - * metric zero. We don't want that route and want to delete it. However, the route - * by kernel immediately, but some time after. That means during nm_route_manager_ip4_route_sync() - * such a route doesn't exist yet. We must remember that we expect such a route to appear later - * and to remove it. */ -void -nm_route_manager_ip4_route_register_device_route_purge_list (NMRouteManager *self, GArray *device_route_purge_list) -{ - NMRouteManagerPrivate *priv; - guint i; - gint64 now_ns; - - if (!device_route_purge_list || device_route_purge_list->len == 0) - return; - - priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - - now_ns = nm_utils_get_monotonic_timestamp_ns (); - for (i = 0; i < device_route_purge_list->len; i++) { - IP4DeviceRoutePurgeEntry *entry; - - entry = _ip4_device_routes_purge_entry_create (self, &g_array_index (device_route_purge_list, NMPlatformIP4Route, i), now_ns); - _LOGt (vtable_v4.vt->addr_family, "device-route: watch (%s) %s", - g_hash_table_contains (priv->ip4_device_routes.entries, entry->obj) - ? "update" : "new", - nmp_object_to_string (entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); - g_hash_table_replace (priv->ip4_device_routes.entries, - (NMPObject *) nmp_object_ref (entry->obj), - entry); - } - if (priv->ip4_device_routes.gc_id == 0) { - g_signal_connect (priv->platform, NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, G_CALLBACK (_ip4_device_routes_ip4_route_changed), self); - priv->ip4_device_routes.gc_id = g_timeout_add (IP4_DEVICE_ROUTES_GC_INTERVAL_MSEC, (GSourceFunc) _ip4_device_routes_gc, self); - } -} - -/*****************************************************************************/ - -static const VTableIP vtable_v4 = { - .vt = &nm_platform_vtable_route_v4, - .route_dest_cmp = (int (*) (const NMPlatformIPXRoute *, const NMPlatformIPXRoute *)) _v4_route_dest_cmp, - .route_id_cmp = (int (*) (const NMPlatformIPXRoute *, const NMPlatformIPXRoute *)) _v4_route_id_cmp, -}; - -static const VTableIP vtable_v6 = { - .vt = &nm_platform_vtable_route_v6, - .route_dest_cmp = (int (*) (const NMPlatformIPXRoute *, const NMPlatformIPXRoute *)) _v6_route_dest_cmp, - .route_id_cmp = (int (*) (const NMPlatformIPXRoute *, const NMPlatformIPXRoute *)) _v6_route_id_cmp, -}; - -/*****************************************************************************/ - -static void -set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - NMRouteManager *self = NM_ROUTE_MANAGER (object); - NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - - switch (prop_id) { - case PROP_LOG_WITH_PTR: - /* construct-only */ - priv->log_with_ptr = g_value_get_boolean (value); - break; - case PROP_PLATFORM: - /* construct-only */ - priv->platform = g_value_get_object (value) ? : NM_PLATFORM_GET; - if (!priv->platform) - g_return_if_reached (); - g_object_ref (priv->platform); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -/*****************************************************************************/ - -static void -nm_route_manager_init (NMRouteManager *self) -{ - NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - - priv->ip4_routes.entries = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); - priv->ip6_routes.entries = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route)); - priv->ip4_routes.effective_metrics = g_array_new (FALSE, FALSE, sizeof (gint64)); - priv->ip6_routes.effective_metrics = g_array_new (FALSE, FALSE, sizeof (gint64)); - priv->ip4_routes.effective_metrics_reverse = g_array_new (FALSE, FALSE, sizeof (gint64)); - priv->ip6_routes.effective_metrics_reverse = g_array_new (FALSE, FALSE, sizeof (gint64)); - priv->ip4_routes.index = _route_index_create (&vtable_v4, priv->ip4_routes.entries); - priv->ip6_routes.index = _route_index_create (&vtable_v6, priv->ip6_routes.entries); - priv->ip4_device_routes.entries = g_hash_table_new_full ((GHashFunc) nmp_object_id_hash, - (GEqualFunc) nmp_object_id_equal, - (GDestroyNotify) nmp_object_unref, - (GDestroyNotify) _ip4_device_routes_purge_entry_free); -} - -NMRouteManager * -nm_route_manager_new (gboolean log_with_ptr, NMPlatform *platform) -{ - return g_object_new (NM_TYPE_ROUTE_MANAGER, - NM_ROUTE_MANAGER_LOG_WITH_PTR, log_with_ptr, - NM_ROUTE_MANAGER_PLATFORM, platform, - NULL); -} - -static void -dispose (GObject *object) -{ - NMRouteManager *self = NM_ROUTE_MANAGER (object); - NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE (self); - - g_hash_table_remove_all (priv->ip4_device_routes.entries); - _ip4_device_routes_cancel (self); - - G_OBJECT_CLASS (nm_route_manager_parent_class)->dispose (object); -} - -static void -finalize (GObject *object) -{ - NMRouteManagerPrivate *priv = NM_ROUTE_MANAGER_GET_PRIVATE ((NMRouteManager *) object); - - g_array_free (priv->ip4_routes.entries, TRUE); - g_array_free (priv->ip6_routes.entries, TRUE); - g_array_free (priv->ip4_routes.effective_metrics, TRUE); - g_array_free (priv->ip6_routes.effective_metrics, TRUE); - g_array_free (priv->ip4_routes.effective_metrics_reverse, TRUE); - g_array_free (priv->ip6_routes.effective_metrics_reverse, TRUE); - g_free (priv->ip4_routes.index); - g_free (priv->ip6_routes.index); - - g_hash_table_unref (priv->ip4_device_routes.entries); - - g_clear_object (&priv->platform); - - G_OBJECT_CLASS (nm_route_manager_parent_class)->finalize (object); -} - -static void -nm_route_manager_class_init (NMRouteManagerClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->set_property = set_property; - object_class->dispose = dispose; - object_class->finalize = finalize; - - obj_properties[PROP_LOG_WITH_PTR] = - g_param_spec_boolean (NM_ROUTE_MANAGER_LOG_WITH_PTR, "", "", - TRUE, - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - - obj_properties[PROP_PLATFORM] = - g_param_spec_object (NM_ROUTE_MANAGER_PLATFORM, "", "", - NM_TYPE_PLATFORM, - G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - g_object_class_install_properties (object_class, _PROPERTY_ENUMS_LAST, obj_properties); - - signals[IP4_ROUTES_CHANGED] = - g_signal_new (NM_ROUTE_MANAGER_IP4_ROUTES_CHANGED, - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, NULL, NULL, NULL, - G_TYPE_NONE, 0); -} diff --git a/src/nm-route-manager.h b/src/nm-route-manager.h deleted file mode 100644 index bdf79a09ab..0000000000 --- a/src/nm-route-manager.h +++ /dev/null @@ -1,49 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2015 Red Hat, Inc. - */ - -#ifndef __NM_ROUTE_MANAGER_H__ -#define __NM_ROUTE_MANAGER_H__ - -#define NM_TYPE_ROUTE_MANAGER (nm_route_manager_get_type ()) -#define NM_ROUTE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_ROUTE_MANAGER, NMRouteManager)) -#define NM_ROUTE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_ROUTE_MANAGER, NMRouteManagerClass)) -#define NM_IS_ROUTE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_ROUTE_MANAGER)) -#define NM_IS_ROUTE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_ROUTE_MANAGER)) -#define NM_ROUTE_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_ROUTE_MANAGER, NMRouteManagerClass)) - -#define NM_ROUTE_MANAGER_LOG_WITH_PTR "log-with-ptr" -#define NM_ROUTE_MANAGER_PLATFORM "platform" - -#define NM_ROUTE_MANAGER_IP4_ROUTES_CHANGED "ip4-routes-changed" - -typedef struct _NMRouteManagerClass NMRouteManagerClass; - -GType nm_route_manager_get_type (void); - -gboolean nm_route_manager_ip4_route_sync (NMRouteManager *self, int ifindex, const GArray *known_routes, gboolean ignore_kernel_routes, gboolean full_sync); -gboolean nm_route_manager_ip6_route_sync (NMRouteManager *self, int ifindex, const GArray *known_routes, gboolean ignore_kernel_routes, gboolean full_sync); -gboolean nm_route_manager_route_flush (NMRouteManager *self, int ifindex); - -gboolean nm_route_manager_ip4_routes_shadowed (NMRouteManager *self, int ifindex); -void nm_route_manager_ip4_route_register_device_route_purge_list (NMRouteManager *self, GArray *device_route_purge_list); - -NMRouteManager *nm_route_manager_new (gboolean log_with_ptr, NMPlatform *platform); - -#endif /* __NM_ROUTE_MANAGER_H__ */ diff --git a/src/nm-types.h b/src/nm-types.h index 136cc450a8..64d718b1ac 100644 --- a/src/nm-types.h +++ b/src/nm-types.h @@ -50,7 +50,6 @@ typedef struct _NMNetns NMNetns; typedef struct _NMPolicy NMPolicy; typedef struct _NMRfkillManager NMRfkillManager; typedef struct _NMPacrunnerManager NMPacrunnerManager; -typedef struct _NMRouteManager NMRouteManager; typedef struct _NMSessionMonitor NMSessionMonitor; typedef struct _NMSleepMonitor NMSleepMonitor; typedef struct _NMLldpListener NMLldpListener; diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 694a6a4139..1512b4f090 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -1199,7 +1199,7 @@ ip_route_delete (NMPlatform *platform, const NMPObject *obj) return ipx_route_delete (platform, AF_UNSPEC, -1, obj); } -static gboolean +static NMPlatformError ip_route_add (NMPlatform *platform, NMPNlmFlags flags, int addr_family, @@ -1223,6 +1223,8 @@ ip_route_add (NMPlatform *platform, g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + flags = NM_FLAGS_UNSET (flags, NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE); + /* currently, only replace is implemented. */ g_assert (flags == NMP_NLM_FLAG_REPLACE); @@ -1230,24 +1232,17 @@ ip_route_add (NMPlatform *platform, ? NMP_OBJECT_TYPE_IP4_ROUTE : NMP_OBJECT_TYPE_IP6_ROUTE, (const NMPlatformObject *) route); - r = &obj->ip_route; + r = NMP_OBJECT_CAST_IP_ROUTE (obj); + nm_platform_ip_route_normalize (addr_family, r); switch (addr_family) { case AF_INET: r4 = NMP_OBJECT_CAST_IP4_ROUTE (obj); - r4->network = nm_utils_ip4_address_clear_host_address (r4->network, r4->plen); - r4->rt_source = nmp_utils_ip_config_source_round_trip_rtprot (r4->rt_source), - r4->scope_inv = nm_platform_route_scope_inv (!r4->gateway - ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE); if (r4->gateway) has_gateway = TRUE; break; case AF_INET6: r6 = NMP_OBJECT_CAST_IP6_ROUTE (obj); - nm_utils_ip6_address_clear_host_address (&r6->network, &r6->network, r6->plen); - r6->rt_source = nmp_utils_ip_config_source_round_trip_rtprot (r6->rt_source), - r6->metric = nm_utils_ip6_route_metric_normalize (r6->metric); - nm_utils_ip6_address_clear_host_address (&r6->src, &r6->src, r6->src_plen); if (!IN6_IS_ADDR_UNSPECIFIED (&r6->gateway)) has_gateway = TRUE; break; @@ -1291,7 +1286,7 @@ ip_route_add (NMPlatform *platform, nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip6-route '%d: %s/%d %d': Network Unreachable", r->ifindex, nm_utils_inet6_ntop (&r6->network, NULL), r->plen, r->metric); } - return FALSE; + return NM_PLATFORM_ERROR_UNSPECIFIED; } } @@ -1353,7 +1348,7 @@ ip_route_add (NMPlatform *platform, } } - return TRUE; + return NM_PLATFORM_ERROR_SUCCESS; } /*****************************************************************************/ diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 71d564bd23..4ca3d94580 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -265,6 +265,16 @@ static gboolean event_handler_read_netlink (NMPlatform *platform, gboolean wait_ /*****************************************************************************/ +static NMPlatformError +wait_for_nl_response_to_plerr (WaitForNlResponseResult seq_result) +{ + if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) + return NM_PLATFORM_ERROR_SUCCESS; + if (seq_result < 0) + return (NMPlatformError) seq_result; + return NM_PLATFORM_ERROR_NETLINK; +} + static const char * wait_for_nl_response_to_string (WaitForNlResponseResult seq_result, char *buf, gsize buf_size) { @@ -290,35 +300,83 @@ wait_for_nl_response_to_string (WaitForNlResponseResult seq_result, char *buf, g return buf0; } -/****************************************************************** +/***************************************************************************** * Support IFLA_INET6_ADDR_GEN_MODE - ******************************************************************/ + *****************************************************************************/ static int _support_user_ipv6ll = 0; #define _support_user_ipv6ll_still_undecided() (G_UNLIKELY (_support_user_ipv6ll == 0)) +static void +_support_user_ipv6ll_detect (struct nlattr **tb) +{ + gboolean supported; + + nm_assert (_support_user_ipv6ll_still_undecided ()); + + /* IFLA_INET6_ADDR_GEN_MODE was added in kernel 3.17, dated 5 October, 2014. */ + supported = !!tb[IFLA_INET6_ADDR_GEN_MODE]; + _support_user_ipv6ll = supported ? 1 : -1; + _LOG2D ("kernel-support: IFLA_INET6_ADDR_GEN_MODE: %s", + supported ? "detected" : "not detected"); +} + static gboolean _support_user_ipv6ll_get (void) { if (_support_user_ipv6ll_still_undecided ()) { - _support_user_ipv6ll = -1; - _LOG2D ("kernel-support: IFLA_INET6_ADDR_GEN_MODE: %s", "failed to detect; assume no support"); - return FALSE; + _support_user_ipv6ll = 1; + _LOG2D ("kernel-support: IFLA_INET6_ADDR_GEN_MODE: %s", "failed to detect; assume support"); } - return _support_user_ipv6ll > 0; - + return _support_user_ipv6ll >= 0; } -static void -_support_user_ipv6ll_detect (struct nlattr **tb) -{ - if (_support_user_ipv6ll_still_undecided ()) { - gboolean supported = !!tb[IFLA_INET6_ADDR_GEN_MODE]; +/***************************************************************************** + * extended IFA_FLAGS support + *****************************************************************************/ - _support_user_ipv6ll = supported ? 1 : -1; - _LOG2D ("kernel-support: IFLA_INET6_ADDR_GEN_MODE: %s", - supported ? "detected" : "not detected"); +static int _support_kernel_extended_ifa_flags = 0; + +#define _support_kernel_extended_ifa_flags_still_undecided() (G_UNLIKELY (_support_kernel_extended_ifa_flags == 0)) + +static void +_support_kernel_extended_ifa_flags_detect (struct nl_msg *msg) +{ + struct nlmsghdr *msg_hdr; + gboolean support; + + nm_assert (_support_kernel_extended_ifa_flags_still_undecided ()); + nm_assert (msg); + + msg_hdr = nlmsg_hdr (msg); + + nm_assert (msg_hdr && msg_hdr->nlmsg_type == RTM_NEWADDR); + + /* IFA_FLAGS is set for IPv4 and IPv6 addresses. It was added first to IPv6, + * but if we encounter an IPv4 address with IFA_FLAGS, we surely have support. */ + if (NM_IN_SET (((struct ifaddrmsg *) nlmsg_data (msg_hdr))->ifa_family, AF_INET, AF_INET6)) + return; + + /* see if the nl_msg contains the IFA_FLAGS attribute. If it does, + * we assume, that the kernel supports extended flags, IFA_F_MANAGETEMPADDR + * and IFA_F_NOPREFIXROUTE for IPv6. They were added together in kernel 3.14, + * dated 30 March, 2014. + * + * For IPv4, IFA_F_NOPREFIXROUTE was added later, but there is no easy + * way to detect kernel support. */ + support = !!nlmsg_find_attr (msg_hdr, sizeof (struct ifaddrmsg), IFA_FLAGS); + _support_kernel_extended_ifa_flags = support ? 1 : -1; + _LOG2D ("kernel-support: extended-ifa-flags: %s", support ? "detected" : "not detected"); +} + +static gboolean +_support_kernel_extended_ifa_flags_get (void) +{ + if (_support_kernel_extended_ifa_flags_still_undecided ()) { + _LOG2D ("kernel-support: extended-ifa-flags: %s", "unable to detect kernel support for handling IPv6 temporary addresses. Assume support"); + _support_kernel_extended_ifa_flags = 1; } + return _support_kernel_extended_ifa_flags >= 0; } /****************************************************************** @@ -778,6 +836,31 @@ _linktype_get_type (NMPlatform *platform, * libnl unility functions and wrappers ******************************************************************/ +#define NLMSG_TAIL(nmsg) \ + ((struct rtattr *) (((char *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) + +/* copied from iproute2's addattr_l(). */ +static gboolean +_nl_addattr_l (struct nlmsghdr *n, + int maxlen, + int type, + const void *data, + int alen) +{ + int len = RTA_LENGTH (alen); + struct rtattr *rta; + + if (NLMSG_ALIGN (n->nlmsg_len) + RTA_ALIGN (len) > maxlen) + return FALSE; + + rta = NLMSG_TAIL (n); + rta->rta_type = type; + rta->rta_len = len; + memcpy (RTA_DATA (rta), data, alen); + n->nlmsg_len = NLMSG_ALIGN (n->nlmsg_len) + RTA_ALIGN (len); + return TRUE; +} + #define nm_auto_nlmsg __attribute__((cleanup(_nm_auto_nl_msg_cleanup))) static void _nm_auto_nl_msg_cleanup (void *ptr) @@ -936,7 +1019,8 @@ _parse_af_inet6 (NMPlatform *platform, /* Hack to detect support addrgenmode of the kernel. We only parse * netlink messages that we receive from kernel, hence this check * is valid. */ - _support_user_ipv6ll_detect (tb); + if (_support_user_ipv6ll_still_undecided ()) + _support_user_ipv6ll_detect (tb); if (tb[IFLA_INET6_ADDR_GEN_MODE]) { i6_addr_gen_mode_inv = _nm_platform_uint8_inv (nla_get_u8 (tb[IFLA_INET6_ADDR_GEN_MODE])); @@ -1946,8 +2030,6 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) table = tb[RTA_TABLE] ? nla_get_u32 (tb[RTA_TABLE]) : (guint32) rtm->rtm_table; - if (table != RT_TABLE_MAIN) - goto errout; /*****************************************************************/ @@ -2064,6 +2146,7 @@ _new_from_nl_route (struct nlmsghdr *nlh, gboolean id_only) obj = nmp_object_new (is_v4 ? NMP_OBJECT_TYPE_IP4_ROUTE : NMP_OBJECT_TYPE_IP6_ROUTE, NULL); + obj->ip_route.table_coerced = nm_platform_route_table_coerce (table); obj->ip_route.ifindex = nh.ifindex; if (_check_addr_or_errout (tb, RTA_DST, addr_len)) @@ -2501,19 +2584,20 @@ ip_route_get_lock_flag (const NMPlatformIPRoute *route) /* Copied and modified from libnl3's build_route_msg() and rtnl_route_build_msg(). */ static struct nl_msg * _nl_msg_new_route (int nlmsg_type, - NMPNlmFlags nlmsgflags, + guint16 nlmsgflags, const NMPObject *obj) { struct nl_msg *msg; const NMPClass *klass = NMP_OBJECT_GET_CLASS (obj); gboolean is_v4 = klass->addr_family == AF_INET; const guint32 lock = ip_route_get_lock_flag (NMP_OBJECT_CAST_IP_ROUTE (obj)); + const guint32 table = nm_platform_route_table_coerce (NMP_OBJECT_CAST_IP_ROUTE (obj)->table_coerced); struct rtmsg rtmsg = { .rtm_family = klass->addr_family, .rtm_tos = is_v4 ? obj->ip4_route.tos : 0, - .rtm_table = RT_TABLE_MAIN, /* omit setting RTA_TABLE attribute */ + .rtm_table = table <= 0xFF ? table : RT_TABLE_UNSPEC, .rtm_protocol = nmp_utils_ip_config_source_coerce_to_rtprot (obj->ip_route.rt_source), .rtm_scope = is_v4 ? nm_platform_route_scope_inv (obj->ip4_route.scope_inv) @@ -2531,7 +2615,6 @@ _nl_msg_new_route (int nlmsg_type, nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); nm_assert (NM_IN_SET (nlmsg_type, RTM_NEWROUTE, RTM_DELROUTE)); - nm_assert (((NMPNlmFlags) ((int) nlmsgflags)) == nlmsgflags); msg = nlmsg_alloc_simple (nlmsg_type, (int) nlmsgflags); if (!msg) g_return_val_if_reached (NULL); @@ -2555,6 +2638,9 @@ _nl_msg_new_route (int nlmsg_type, NLA_PUT_U32 (msg, RTA_PRIORITY, obj->ip_route.metric); + if (table > 0xFF) + NLA_PUT_U32 (msg, RTA_TABLE, table); + if (is_v4) { if (NMP_OBJECT_CAST_IP4_ROUTE (obj)->pref_src) NLA_PUT (msg, RTA_PREFSRC, addr_len, &obj->ip4_route.pref_src); @@ -2610,56 +2696,27 @@ nla_put_failure: g_return_val_if_reached (NULL); } -/*****************************************************************************/ - -static int _support_kernel_extended_ifa_flags = -1; - -#define _support_kernel_extended_ifa_flags_still_undecided() (G_UNLIKELY (_support_kernel_extended_ifa_flags == -1)) - -static void -_support_kernel_extended_ifa_flags_detect (struct nl_msg *msg) -{ - struct nlmsghdr *msg_hdr; - - if (!_support_kernel_extended_ifa_flags_still_undecided ()) - return; - - msg_hdr = nlmsg_hdr (msg); - if (msg_hdr->nlmsg_type != RTM_NEWADDR) - return; - - /* the extended address flags are only set for AF_INET6 */ - if (((struct ifaddrmsg *) nlmsg_data (msg_hdr))->ifa_family != AF_INET6) - return; - - /* see if the nl_msg contains the IFA_FLAGS attribute. If it does, - * we assume, that the kernel supports extended flags, IFA_F_MANAGETEMPADDR - * and IFA_F_NOPREFIXROUTE (they were added together). - **/ - _support_kernel_extended_ifa_flags = !!nlmsg_find_attr (msg_hdr, sizeof (struct ifaddrmsg), IFA_FLAGS); - _LOG2D ("kernel-support: extended-ifa-flags: %s", _support_kernel_extended_ifa_flags ? "detected" : "not detected"); -} - -static gboolean -_support_kernel_extended_ifa_flags_get (void) -{ - if (_support_kernel_extended_ifa_flags_still_undecided ()) { - _LOG2D ("kernel-support: extended-ifa-flags: %s", "unable to detect kernel support for handling IPv6 temporary addresses. Assume support"); - _support_kernel_extended_ifa_flags = 1; - } - return _support_kernel_extended_ifa_flags; -} - /****************************************************************** * NMPlatform types and functions ******************************************************************/ +typedef enum { + DELAYED_ACTION_RESPONSE_TYPE_VOID = 0, + DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS = 1, + DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET = 2, +} DelayedActionWaitForNlResponseType; + typedef struct { guint32 seq_number; WaitForNlResponseResult seq_result; + DelayedActionWaitForNlResponseType response_type; gint64 timeout_abs_ns; WaitForNlResponseResult *out_seq_result; - gint *out_refresh_all_in_progess; + union { + gint *out_refresh_all_in_progess; + NMPObject **out_route_get; + gpointer out_data; + } response; } DelayedActionWaitForNlResponseData; typedef struct { @@ -2990,7 +3047,7 @@ sysctl_get (NMPlatform *platform, const char *pathid, int dirfd, const char *pat static gboolean check_support_kernel_extended_ifa_flags (NMPlatform *platform) { - g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); + nm_assert (NM_IS_LINUX_PLATFORM (platform)); return _support_kernel_extended_ifa_flags_get (); } @@ -2998,7 +3055,7 @@ check_support_kernel_extended_ifa_flags (NMPlatform *platform) static gboolean check_support_user_ipv6ll (NMPlatform *platform) { - g_return_val_if_fail (NM_IS_LINUX_PLATFORM (platform), FALSE); + nm_assert (NM_IS_LINUX_PLATFORM (platform)); return _support_user_ipv6ll_get (); } @@ -3078,11 +3135,12 @@ delayed_action_to_string_full (DelayedActionType action_type, gpointer user_data gint64 timeout = data->timeout_abs_ns - nm_utils_get_monotonic_timestamp_ns (); char b[255]; - nm_utils_strbuf_append (&buf, &buf_size, " (seq %u, timeout in %s%"G_GINT64_FORMAT".%09"G_GINT64_FORMAT"%s%s)", + nm_utils_strbuf_append (&buf, &buf_size, " (seq %u, timeout in %s%"G_GINT64_FORMAT".%09"G_GINT64_FORMAT", response-type %d%s%s)", data->seq_number, timeout < 0 ? "-" : "", (timeout < 0 ? -timeout : timeout) / NM_UTILS_NS_PER_SECOND, (timeout < 0 ? -timeout : timeout) % NM_UTILS_NS_PER_SECOND, + (int) data->response_type, data->seq_result ? ", " : "", data->seq_result ? wait_for_nl_response_to_string (data->seq_result, b, sizeof (b)) : ""); } else @@ -3144,9 +3202,22 @@ delayed_action_wait_for_nl_response_complete (NMPlatform *platform, priv->delayed_action.flags &= ~DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE; if (data->out_seq_result) *data->out_seq_result = seq_result; - if (data->out_refresh_all_in_progess) { - nm_assert (*data->out_refresh_all_in_progess > 0); - *data->out_refresh_all_in_progess -= 1; + switch (data->response_type) { + case DELAYED_ACTION_RESPONSE_TYPE_VOID: + break; + case DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS: + if (data->response.out_refresh_all_in_progess) { + nm_assert (*data->response.out_refresh_all_in_progess > 0); + *data->response.out_refresh_all_in_progess -= 1; + data->response.out_refresh_all_in_progess = NULL; + } + break; + case DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET: + if (data->response.out_route_get) { + nm_assert (!*data->response.out_route_get); + data->response.out_route_get = NULL; + } + break; } g_array_remove_index_fast (priv->delayed_action.list_wait_for_nl_response, idx); @@ -3356,13 +3427,15 @@ static void delayed_action_schedule_WAIT_FOR_NL_RESPONSE (NMPlatform *platform, guint32 seq_number, WaitForNlResponseResult *out_seq_result, - gint *out_refresh_all_in_progess) + DelayedActionWaitForNlResponseType response_type, + gpointer response_out_data) { DelayedActionWaitForNlResponseData data = { .seq_number = seq_number, .timeout_abs_ns = nm_utils_get_monotonic_timestamp_ns () + (200 * (NM_UTILS_NS_PER_SECOND / 1000)), .out_seq_result = out_seq_result, - .out_refresh_all_in_progess = out_refresh_all_in_progess, + .response_type = response_type, + .response.out_data = response_out_data, }; delayed_action_schedule (platform, @@ -3647,30 +3720,111 @@ cache_on_change (NMPlatform *platform, /*****************************************************************************/ +static guint32 +_nlh_seq_next_get (NMLinuxPlatformPrivate *priv) +{ + /* generate a new sequence number, but skip zero. */ + return priv->nlh_seq_next++ ?: priv->nlh_seq_next++; +} + +/** + * _nl_send_nlmsghdr: + * @platform: + * @nlhdr: + * @out_seq_result: + * @response_type: + * @response_out_data: + * + * Returns: 0 on success or a negative errno. Beware, it's an errno, not nlerror. + */ static int -_nl_send_auto_with_seq (NMPlatform *platform, - struct nl_msg *nlmsg, - WaitForNlResponseResult *out_seq_result, - gint *out_refresh_all_in_progess) +_nl_send_nlmsghdr (NMPlatform *platform, + struct nlmsghdr *nlhdr, + WaitForNlResponseResult *out_seq_result, + DelayedActionWaitForNlResponseType response_type, + gpointer response_out_data) { NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); guint32 seq; int nle; - /* complete the message with a sequence number (ensuring it's not zero). */ - seq = priv->nlh_seq_next++ ?: priv->nlh_seq_next++; + nm_assert (nlhdr); - nlmsg_hdr (nlmsg)->nlmsg_seq = seq; + seq = _nlh_seq_next_get (priv); + nlhdr->nlmsg_seq = seq; + + { + struct sockaddr_nl nladdr = { + .nl_family = AF_NETLINK, + }; + struct iovec iov = { + .iov_base = nlhdr, + .iov_len = nlhdr->nlmsg_len + }; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + int try_count; + + if (!nlhdr->nlmsg_pid) + nlhdr->nlmsg_pid = nl_socket_get_local_port (priv->nlh); + nlhdr->nlmsg_flags |= (NLM_F_REQUEST | NLM_F_ACK); + + try_count = 0; +again: + nle = sendmsg (nl_socket_get_fd (priv->nlh), &msg, 0); + if (nle < 0) { + nle = errno; + if (nle == EINTR && try_count++ < 100) + goto again; + _LOGD ("netlink: nl-send-nlmsghdr: failed sending message: %s (%d)", g_strerror (nle), nle); + return -nle; + } + } + + delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result, + response_type, response_out_data); + return 0; +} + +/** + * _nl_send_nlmsg: + * @platform: + * @nlmsg: + * @out_seq_result: + * @response_type: + * @response_out_data: + * + * Returns: 0 on success, or a negative libnl3 error code (beware, it's not an errno). + */ +static int +_nl_send_nlmsg (NMPlatform *platform, + struct nl_msg *nlmsg, + WaitForNlResponseResult *out_seq_result, + DelayedActionWaitForNlResponseType response_type, + gpointer response_out_data) +{ + NMLinuxPlatformPrivate *priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + struct nlmsghdr *nlhdr; + guint32 seq; + int nle; + + nlhdr = nlmsg_hdr (nlmsg); + seq = _nlh_seq_next_get (priv); + nlhdr->nlmsg_seq = seq; nle = nl_send_auto (priv->nlh, nlmsg); + if (nle < 0) { + _LOGD ("netlink: nl-send-nlmsg: failed sending message: %s (%d)", nl_geterror (nle), nle); + return nle; + } - if (nle >= 0) { - nle = 0; - delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result, out_refresh_all_in_progess); - } else - _LOGD ("netlink: send: failed sending message: %s (%d)", nl_geterror (nle), nle); - - return nle; + delayed_action_schedule_WAIT_FOR_NL_RESPONSE (platform, seq, out_seq_result, + response_type, response_out_data); + return 0; } static void @@ -3705,7 +3859,7 @@ do_request_link_no_delayed_actions (NMPlatform *platform, int ifindex, const cha 0, 0); if (nlmsg) - _nl_send_auto_with_seq (platform, nlmsg, NULL, NULL); + _nl_send_nlmsg (platform, nlmsg, NULL, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); } static void @@ -3767,7 +3921,7 @@ do_request_all_no_delayed_actions (NMPlatform *platform, DelayedActionType actio if (nle < 0) continue; - if (_nl_send_auto_with_seq (platform, nlmsg, NULL, out_refresh_all_in_progess) < 0) { + if (_nl_send_nlmsg (platform, nlmsg, NULL, DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS, out_refresh_all_in_progess) < 0) { nm_assert (*out_refresh_all_in_progess > 0); *out_refresh_all_in_progess -= 1; } @@ -3797,13 +3951,12 @@ event_seq_check_refresh_all (NMPlatform *platform, guint32 seq_number) for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; i++) { data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, i); - if (data->seq_number == priv->nlh_seq_last_seen) { - if (data->out_refresh_all_in_progess) { - nm_assert (*data->out_refresh_all_in_progess > 0); - *data->out_refresh_all_in_progess -= 1; - data->out_refresh_all_in_progess = NULL; - break; - } + if ( data->response_type == DELAYED_ACTION_RESPONSE_TYPE_REFRESH_ALL_IN_PROGRESS + && data->response.out_refresh_all_in_progess + && data->seq_number == priv->nlh_seq_last_seen) { + *data->response.out_refresh_all_in_progess -= 1; + data->response.out_refresh_all_in_progess = NULL; + break; } } } @@ -3851,6 +4004,7 @@ event_seq_check (NMPlatform *platform, guint32 seq_number, WaitForNlResponseResu static void event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_events) { + NMLinuxPlatformPrivate *priv; nm_auto_nmpobj NMPObject *obj = NULL; NMPCacheOpsType cache_op; struct nlmsghdr *msghdr; @@ -3861,7 +4015,8 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event msghdr = nlmsg_hdr (msg); - if (_support_kernel_extended_ifa_flags_still_undecided () && msghdr->nlmsg_type == RTM_NEWADDR) + if ( _support_kernel_extended_ifa_flags_still_undecided () + && msghdr->nlmsg_type == RTM_NEWADDR) _support_kernel_extended_ifa_flags_detect (msg); if (!handle_events) @@ -3919,6 +4074,30 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event gboolean resync_required = FALSE; gboolean only_dirty = FALSE; + if (obj->ip_route.rt_cloned) { + /* a cloned route might be a response for RTM_GETROUTE. Check, whether it is. */ + nm_assert (!nmp_object_is_alive (obj)); + priv = NM_LINUX_PLATFORM_GET_PRIVATE (platform); + if (NM_FLAGS_HAS (priv->delayed_action.flags, DELAYED_ACTION_TYPE_WAIT_FOR_NL_RESPONSE)) { + guint i; + + nm_assert (priv->delayed_action.list_wait_for_nl_response->len > 0); + for (i = 0; i < priv->delayed_action.list_wait_for_nl_response->len; i++) { + DelayedActionWaitForNlResponseData *data = &g_array_index (priv->delayed_action.list_wait_for_nl_response, DelayedActionWaitForNlResponseData, i); + + if ( data->response_type == DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET + && data->response.out_route_get) { + nm_assert (!*data->response.out_route_get); + if (data->seq_number == nlmsg_hdr (msg)->nlmsg_seq) { + *data->response.out_route_get = nmp_object_clone (obj, FALSE); + data->response.out_route_get = NULL; + break; + } + } + } + } + } + cache_op = nmp_cache_update_netlink_route (cache, obj, is_dump, @@ -4006,26 +4185,13 @@ do_add_link_with_lookup (NMPlatform *platform, event_handler_read_netlink (platform, FALSE); - if (nmp_cache_lookup_link_full (cache, 0, name, FALSE, NM_LINK_TYPE_NONE, NULL, NULL)) { - /* hm, a link with such a name already exists. Try reloading first. */ - do_request_link (platform, 0, name); - - obj = nmp_cache_lookup_link_full (cache, 0, name, FALSE, NM_LINK_TYPE_NONE, NULL, NULL); - if (obj) { - _LOGE ("do-add-link[%s/%s]: link already exists: %s", - name, - nm_link_type_to_string (link_type), - nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ID, NULL, 0)); - return FALSE; - } - } - - nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result, NULL); + nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { _LOGE ("do-add-link[%s/%s]: failed sending netlink request \"%s\" (%d)", name, nm_link_type_to_string (link_type), nl_geterror (nle), -nle); + NM_SET_OUT (out_link, NULL); return FALSE; } @@ -4035,35 +4201,29 @@ do_add_link_with_lookup (NMPlatform *platform, _NMLOG (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK ? LOGL_DEBUG - : LOGL_ERR, + : LOGL_WARN, "do-add-link[%s/%s]: %s", name, nm_link_type_to_string (link_type), wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf))); - if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) - obj = nmp_cache_lookup_link_full (cache, 0, name, FALSE, link_type, NULL, NULL); - - if (!obj) { - /* either kernel signaled failure, or it signaled success and the link object - * is not (yet) in the cache. Try to reload it... */ - do_request_link (platform, 0, name); + if (out_link) { obj = nmp_cache_lookup_link_full (cache, 0, name, FALSE, link_type, NULL, NULL); + *out_link = NMP_OBJECT_CAST_LINK (obj); } - if (out_link) - *out_link = obj ? &obj->link : NULL; - return !!obj; + return seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; } -static gboolean -do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg) +static NMPlatformError +do_add_addrroute (NMPlatform *platform, + const NMPObject *obj_id, + struct nl_msg *nlmsg, + gboolean suppress_netlink_failure) { WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; int nle; char s_buf[256]; - const NMPObject *obj; - NMPCache *cache = nm_platform_get_cache (platform); nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_id), NMP_OBJECT_TYPE_IP4_ADDRESS, NMP_OBJECT_TYPE_IP6_ADDRESS, @@ -4071,45 +4231,43 @@ do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg * event_handler_read_netlink (platform, FALSE); - nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result, NULL); + nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { _LOGE ("do-add-%s[%s]: failure sending netlink request \"%s\" (%d)", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), nl_geterror (nle), -nle); - return FALSE; + return NM_PLATFORM_ERROR_NETLINK; } delayed_action_handle_all (platform, FALSE); nm_assert (seq_result); - _NMLOG (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK + _NMLOG (( seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK + || ( suppress_netlink_failure + && seq_result < 0)) ? LOGL_DEBUG - : LOGL_ERR, + : LOGL_WARN, "do-add-%s[%s]: %s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf))); - /* In rare cases, the object is not yet ready as we received the ACK from - * kernel. Need to refetch. - * - * We want to safe the expensive refetch, thus we look first into the cache - * whether the object exists. - * - * FIXME: if the object already existed previously, we might not notice a - * missing update. It's not clear how to fix that reliably without refechting - * all the time. */ - obj = nmp_cache_lookup_obj (cache, obj_id); - if (!obj) { - do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id)); - obj = nmp_cache_lookup_obj (cache, obj_id); + if (NMP_OBJECT_GET_TYPE (obj_id) == NMP_OBJECT_TYPE_IP6_ADDRESS) { + /* In rare cases, the object is not yet ready as we received the ACK from + * kernel. Need to refetch. + * + * We want to safe the expensive refetch, thus we look first into the cache + * whether the object exists. + * + * rh#1484434 */ + if (!nmp_cache_lookup_obj (nm_platform_get_cache (platform), + obj_id)) + do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id)); } - /* Adding is only successful, if kernel reported success *and* we have the - * expected object in cache afterwards. */ - return obj && seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK; + return wait_for_nl_response_to_plerr (seq_result); } static gboolean @@ -4118,25 +4276,25 @@ do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg * WaitForNlResponseResult seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; int nle; char s_buf[256]; - gboolean success = TRUE; + gboolean success; const char *log_detail = ""; - NMPCache *cache = nm_platform_get_cache (platform); event_handler_read_netlink (platform, FALSE); - nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result, NULL); + nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { _LOGE ("do-delete-%s[%s]: failure sending netlink request \"%s\" (%d)", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), nl_geterror (nle), -nle); - goto out; + return FALSE; } delayed_action_handle_all (platform, FALSE); nm_assert (seq_result); + success = TRUE; if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) { /* ok */ } else if (NM_IN_SET (-((int) seq_result), ESRCH, ENOENT)) @@ -4151,21 +4309,14 @@ do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg * else success = FALSE; - _NMLOG (success ? LOGL_DEBUG : LOGL_ERR, + _NMLOG (success ? LOGL_DEBUG : LOGL_WARN, "do-delete-%s[%s]: %s%s", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, nmp_object_to_string (obj_id, NMP_OBJECT_TO_STRING_ID, NULL, 0), wait_for_nl_response_to_string (seq_result, s_buf, sizeof (s_buf)), log_detail); -out: - if (!nmp_cache_lookup_obj (cache, obj_id)) - return TRUE; - - /* such an object still exists in the cache. To be sure, refetch it (and - * hope it's gone) */ - do_request_one_type (platform, NMP_OBJECT_GET_TYPE (obj_id)); - return !nmp_cache_lookup_obj (cache, obj_id); + return success; } static WaitForNlResponseResult @@ -4181,7 +4332,7 @@ do_change_link_request (NMPlatform *platform, return WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; retry: - nle = _nl_send_auto_with_seq (platform, nlmsg, &seq_result, NULL); + nle = _nl_send_nlmsg (platform, nlmsg, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_VOID, NULL); if (nle < 0) { _LOGE ("do-change-link[%d]: failure sending netlink request \"%s\" (%d)", ifindex, @@ -4226,7 +4377,7 @@ do_change_link_result (NMPlatform *platform, log_level = LOGL_DEBUG; result = NM_PLATFORM_ERROR_NOT_FOUND; } else { - log_level = LOGL_ERR; + log_level = LOGL_WARN; result = NM_PLATFORM_ERROR_UNSPECIFIED; } _NMLOG (log_level, @@ -4421,15 +4572,15 @@ link_set_user_ipv6ll_enabled (NMPlatform *platform, int ifindex, gboolean enable nm_auto_nlmsg struct nl_msg *nlmsg = NULL; guint8 mode = enabled ? NM_IN6_ADDR_GEN_MODE_NONE : NM_IN6_ADDR_GEN_MODE_EUI64; + _LOGD ("link: change %d: user-ipv6ll: set IPv6 address generation mode to %s", + ifindex, + nm_platform_link_inet6_addrgenmode2str (mode, NULL, 0)); + if (!_support_user_ipv6ll_get ()) { _LOGD ("link: change %d: user-ipv6ll: not supported", ifindex); return NM_PLATFORM_ERROR_OPNOTSUPP; } - _LOGD ("link: change %d: user-ipv6ll: set IPv6 address generation mode to %s", - ifindex, - nm_platform_link_inet6_addrgenmode2str (mode, NULL, 0)); - nlmsg = _nl_msg_new_link (RTM_NEWLINK, 0, ifindex, @@ -5743,7 +5894,7 @@ ip4_address_add (NMPlatform *platform, label); nmp_object_stackinit_id_ip4_address (&obj_id, ifindex, addr, plen, peer_addr); - return do_add_addrroute (platform, &obj_id, nlmsg); + return do_add_addrroute (platform, &obj_id, nlmsg, FALSE) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -5773,7 +5924,7 @@ ip6_address_add (NMPlatform *platform, NULL); nmp_object_stackinit_id_ip6_address (&obj_id, ifindex, &addr); - return do_add_addrroute (platform, &obj_id, nlmsg); + return do_add_addrroute (platform, &obj_id, nlmsg, FALSE) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -5828,7 +5979,7 @@ ip6_address_delete (NMPlatform *platform, int ifindex, struct in6_addr addr, gui /*****************************************************************************/ -static gboolean +static NMPlatformError ip_route_add (NMPlatform *platform, NMPNlmFlags flags, int addr_family, @@ -5836,34 +5987,27 @@ ip_route_add (NMPlatform *platform, { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; NMPObject obj; - NMPlatformIP4Route *r4; - NMPlatformIP6Route *r6; switch (addr_family) { case AF_INET: nmp_object_stackinit (&obj, NMP_OBJECT_TYPE_IP4_ROUTE, (const NMPlatformObject *) route); - r4 = NMP_OBJECT_CAST_IP4_ROUTE (&obj); - r4->network = nm_utils_ip4_address_clear_host_address (r4->network, r4->plen); - r4->rt_source = nmp_utils_ip_config_source_round_trip_rtprot (r4->rt_source), - r4->scope_inv = nm_platform_route_scope_inv (!r4->gateway - ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE); break; case AF_INET6: nmp_object_stackinit (&obj, NMP_OBJECT_TYPE_IP6_ROUTE, (const NMPlatformObject *) route); - r6 = NMP_OBJECT_CAST_IP6_ROUTE (&obj); - nm_utils_ip6_address_clear_host_address (&r6->network, &r6->network, r6->plen); - r6->rt_source = nmp_utils_ip_config_source_round_trip_rtprot (r6->rt_source), - r6->metric = nm_utils_ip6_route_metric_normalize (r6->metric); - nm_utils_ip6_address_clear_host_address (&r6->src, &r6->src, r6->src_plen); break; default: nm_assert_not_reached (); } - nlmsg = _nl_msg_new_route (RTM_NEWROUTE, flags, &obj); + nm_platform_ip_route_normalize (addr_family, NMP_OBJECT_CAST_IP_ROUTE (&obj)); + + nlmsg = _nl_msg_new_route (RTM_NEWROUTE, flags & NMP_NLM_FLAG_FMASK, &obj); if (!nlmsg) - g_return_val_if_reached (FALSE); - return do_add_addrroute (platform, &obj, nlmsg); + g_return_val_if_reached (NM_PLATFORM_ERROR_BUG); + return do_add_addrroute (platform, + &obj, + nlmsg, + NM_FLAGS_HAS (flags, NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE)); } static gboolean @@ -5887,6 +6031,77 @@ ip_route_delete (NMPlatform *platform, /*****************************************************************************/ +static NMPlatformError +ip_route_get (NMPlatform *platform, + int addr_family, + gconstpointer address, + NMPObject **out_route) +{ + const gboolean is_v4 = (addr_family == AF_INET); + const int addr_len = is_v4 ? 4 : 16; + int try_count = 0; + WaitForNlResponseResult seq_result; + int nle; + nm_auto_nlmsg NMPObject *route = NULL; + + nm_assert (NM_IS_LINUX_PLATFORM (platform)); + nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + nm_assert (address); + + do { + struct { + struct nlmsghdr n; + struct rtmsg r; + char buf[64]; + } req = { + .n.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg)), + .n.nlmsg_flags = NLM_F_REQUEST, + .n.nlmsg_type = RTM_GETROUTE, + .r.rtm_family = addr_family, + .r.rtm_tos = 0, + .r.rtm_dst_len = is_v4 ? 32 : 128, + .r.rtm_flags = 0x1000 /* RTM_F_LOOKUP_TABLE */, + }; + + g_clear_pointer (&route, nmp_object_unref); + + if (!_nl_addattr_l (&req.n, sizeof (req), RTA_DST, address, addr_len)) + nm_assert_not_reached (); + + seq_result = WAIT_FOR_NL_RESPONSE_RESULT_UNKNOWN; + nle = _nl_send_nlmsghdr (platform, &req.n, &seq_result, DELAYED_ACTION_RESPONSE_TYPE_ROUTE_GET, &route); + if (nle < 0) { + _LOGE ("get-route: failure sending netlink request \"%s\" (%d)", + g_strerror (-nle), -nle); + return NM_PLATFORM_ERROR_UNSPECIFIED; + } + + delayed_action_handle_all (platform, FALSE); + + /* Retry, if we failed due to a cache resync. That can happen when the netlink + * socket fills up and we lost the response. */ + } while ( seq_result == WAIT_FOR_NL_RESPONSE_RESULT_FAILED_RESYNC + && ++try_count < 10); + + if (seq_result < 0) { + /* negative seq_result is an errno from kernel. Map it to negative + * NMPlatformError (which are also errno). */ + return (NMPlatformError) seq_result; + } + + if (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK) { + if (route) { + NM_SET_OUT (out_route, g_steal_pointer (&route)); + return NM_PLATFORM_ERROR_SUCCESS; + } + seq_result = WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_UNKNOWN; + } + + return NM_PLATFORM_ERROR_UNSPECIFIED; +} + +/*****************************************************************************/ + #define EVENT_CONDITIONS ((GIOCondition) (G_IO_IN | G_IO_PRI)) #define ERROR_CONDITIONS ((GIOCondition) (G_IO_ERR | G_IO_NVAL)) #define DISCONNECT_CONDITIONS ((GIOCondition) (G_IO_HUP)) @@ -6616,6 +6831,7 @@ nm_linux_platform_class_init (NMLinuxPlatformClass *klass) platform_class->ip_route_add = ip_route_add; platform_class->ip_route_delete = ip_route_delete; + platform_class->ip_route_get = ip_route_get; platform_class->check_support_kernel_extended_ifa_flags = check_support_kernel_extended_ifa_flags; platform_class->check_support_user_ipv6ll = check_support_user_ipv6ll; diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 4f0ab9ac44..ba2cf7a76b 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -91,6 +91,13 @@ enum { typedef struct _NMPlatformPrivate { bool use_udev:1; bool log_with_ptr:1; + + gint8 check_support_kernel_extended_ifa_flags_cached; + gint8 check_support_user_ipv6ll_cached; + + guint ip4_dev_route_blacklist_check_id; + guint ip4_dev_route_blacklist_gc_timeout_id; + GHashTable *ip4_dev_route_blacklist_hash; NMDedupMultiIndex *multi_idx; NMPCache *cache; } NMPlatformPrivate; @@ -101,6 +108,10 @@ G_DEFINE_TYPE (NMPlatform, nm_platform, G_TYPE_OBJECT) /*****************************************************************************/ +static void _ip4_dev_route_blacklist_schedule (NMPlatform *self); + +/*****************************************************************************/ + gboolean nm_platform_get_use_udev (NMPlatform *self) { @@ -217,17 +228,8 @@ nm_platform_get_multi_idx (NMPlatform *self) /*****************************************************************************/ -/** - * _nm_platform_error_to_string: - * @error_code: the error code to stringify. - * - * Returns: A string representation of the error. - * For negative numbers, this function interprets - * the code as -errno. - * For invalid (positive) numbers it returns NULL. - */ -NM_UTILS_LOOKUP_STR_DEFINE (_nm_platform_error_to_string, NMPlatformError, - NM_UTILS_LOOKUP_DEFAULT ( val < 0 ? g_strerror (- ((int) val)) : NULL ), +NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_nm_platform_error_to_string, NMPlatformError, + NM_UTILS_LOOKUP_DEFAULT (NULL), NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_SUCCESS, "success"), NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_BUG, "bug"), NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_UNSPECIFIED, "unspecified"), @@ -237,35 +239,100 @@ NM_UTILS_LOOKUP_STR_DEFINE (_nm_platform_error_to_string, NMPlatformError, NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_NOT_SLAVE, "not-slave"), NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_NO_FIRMWARE, "no-firmware"), NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_OPNOTSUPP, "not-supported"), + NM_UTILS_LOOKUP_STR_ITEM (NM_PLATFORM_ERROR_NETLINK, "netlink"), NM_UTILS_LOOKUP_ITEM_IGNORE (_NM_PLATFORM_ERROR_MININT), ); +/** + * nm_platform_error_to_string: + * @error_code: the error code to stringify. + * @buf: (allow-none): buffer + * @buf_len: size of buffer + * + * Returns: A string representation of the error. + * For negative numbers, this function interprets + * the code as -errno. + * For invalid (positive) numbers it returns NULL. + */ +const char * +nm_platform_error_to_string (NMPlatformError error_code, char *buf, gsize buf_len) +{ + const char *s; + + if (error_code < 0) { + int errsv = -((int) error_code); + + nm_utils_to_string_buffer_init (&buf, &buf_len); + g_snprintf (buf, buf_len, "%s (%d)", g_strerror (errsv), errsv); + } else { + s = _nm_platform_error_to_string (error_code); + if (s) { + if (!buf) + return s; + g_strlcpy (buf, s, buf_len); + } else { + nm_utils_to_string_buffer_init (&buf, &buf_len); + g_snprintf (buf, buf_len, "(%d)", (int) error_code); + } + } + + return buf; +} + +NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_nmp_nlm_flag_to_string_lookup, NMPNlmFlags, + NM_UTILS_LOOKUP_DEFAULT (NULL), + NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_ADD, "add"), + NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_CHANGE, "change"), + NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_REPLACE, "replace"), + NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_PREPEND, "prepend"), + NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_APPEND, "append"), + NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_TEST, "test"), + NM_UTILS_LOOKUP_ITEM_IGNORE (NMP_NLM_FLAG_F_APPEND), + NM_UTILS_LOOKUP_ITEM_IGNORE (NMP_NLM_FLAG_FMASK), + NM_UTILS_LOOKUP_ITEM_IGNORE (NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE), +); + +#define _nmp_nlm_flag_to_string(flags) \ + ({ \ + NMPNlmFlags _flags = (flags); \ + \ + _nmp_nlm_flag_to_string_lookup (flags) ?: nm_sprintf_bufa (100, "new[0x%x]", (unsigned) _flags); \ + }) + /*****************************************************************************/ gboolean nm_platform_check_support_kernel_extended_ifa_flags (NMPlatform *self) { - _CHECK_SELF (self, klass, FALSE); + NMPlatformPrivate *priv; - if (!klass->check_support_kernel_extended_ifa_flags) - return FALSE; + _CHECK_SELF (self, klass, TRUE); - return klass->check_support_kernel_extended_ifa_flags (self); + priv = NM_PLATFORM_GET_PRIVATE (self); + + if (G_UNLIKELY (priv->check_support_kernel_extended_ifa_flags_cached == 0)) { + priv->check_support_kernel_extended_ifa_flags_cached = ( klass->check_support_kernel_extended_ifa_flags + && klass->check_support_kernel_extended_ifa_flags (self)) + ? 1 : -1; + } + return priv->check_support_kernel_extended_ifa_flags_cached >= 0; } gboolean nm_platform_check_support_user_ipv6ll (NMPlatform *self) { - static int supported = -1; + NMPlatformPrivate *priv; - _CHECK_SELF (self, klass, FALSE); + _CHECK_SELF (self, klass, TRUE); - if (!klass->check_support_user_ipv6ll) - return FALSE; + priv = NM_PLATFORM_GET_PRIVATE (self); - if (supported < 0) - supported = klass->check_support_user_ipv6ll (self) ? 1 : 0; - return !!supported; + if (G_UNLIKELY (priv->check_support_user_ipv6ll_cached == 0)) { + priv->check_support_user_ipv6ll_cached = ( klass->check_support_user_ipv6ll + && klass->check_support_user_ipv6ll (self)) + ? 1 : -1; + } + return priv->check_support_user_ipv6ll_cached >= 0; } /** @@ -2806,7 +2873,7 @@ nm_platform_ethtool_get_link_settings (NMPlatform *self, int ifindex, gboolean * const NMDedupMultiHeadEntry * nm_platform_lookup_all (NMPlatform *platform, NMPCacheIdType cache_id_type, - NMPObject *obj) + const NMPObject *obj) { return nmp_cache_lookup_all (nm_platform_get_cache (platform), cache_id_type, @@ -2816,7 +2883,7 @@ nm_platform_lookup_all (NMPlatform *platform, const NMDedupMultiEntry * nm_platform_lookup_entry (NMPlatform *platform, NMPCacheIdType cache_id_type, - NMPObject *obj) + const NMPObject *obj) { return nmp_cache_lookup_entry_with_idx_type (nm_platform_get_cache (platform), cache_id_type, @@ -2831,6 +2898,16 @@ nm_platform_lookup (NMPlatform *self, lookup); } +gboolean +nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel (const NMPObject *obj, + gpointer user_data) +{ + nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj), NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); + return !obj->ip_route.table_coerced + && obj->ip_route.rt_source != NM_IP_CONFIG_SOURCE_RTPROT_KERNEL; +} + gboolean nm_platform_lookup_predicate_routes_skip_rtprot_kernel (const NMPObject *obj, gpointer user_data) @@ -2861,7 +2938,7 @@ nm_platform_lookup_predicate_routes_skip_rtprot_kernel (const NMPObject *obj, GPtrArray * nm_platform_lookup_clone (NMPlatform *self, const NMPLookup *lookup, - gboolean (*predicate) (const NMPObject *obj, gpointer user_data), + NMPObjectPredicateFunc predicate, gpointer user_data) { return nm_dedup_multi_objs_to_ptr_array_head (nm_platform_lookup (self, lookup), @@ -3225,6 +3302,7 @@ nm_platform_ip4_address_sync (NMPlatform *self, guint i, j, len; NMPLookup lookup; guint32 lifetime, preferred; + guint32 ifa_flags; _CHECK_SELF (self, klass, FALSE); @@ -3343,6 +3421,10 @@ delete_and_next: if (!known_addresses) return TRUE; + ifa_flags = nm_platform_check_support_kernel_extended_ifa_flags (self) + ? IFA_F_NOPREFIXROUTE + : 0; + /* Add missing addresses */ for (i = 0; i < known_addresses->len; i++) { const NMPObject *o; @@ -3359,7 +3441,8 @@ delete_and_next: if (!nm_platform_ip4_address_add (self, ifindex, known_address->address, known_address->plen, known_address->peer_address, lifetime, preferred, - 0, known_address->label)) + ifa_flags, + known_address->label)) goto delete_and_next2; continue; @@ -3443,70 +3526,344 @@ nm_platform_ip6_address_sync (NMPlatform *self, } gboolean -nm_platform_address_flush (NMPlatform *self, int ifindex) +nm_platform_ip_address_flush (NMPlatform *self, + int addr_family, + int ifindex) { + gboolean success = TRUE; + _CHECK_SELF (self, klass, FALSE); - return nm_platform_ip4_address_sync (self, ifindex, NULL) - && nm_platform_ip6_address_sync (self, ifindex, NULL, FALSE); + nm_assert (NM_IN_SET (addr_family, AF_UNSPEC, + AF_INET, + AF_INET6)); + + if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET)) + success &= nm_platform_ip4_address_sync (self, ifindex, NULL); + if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET6)) + success &= nm_platform_ip6_address_sync (self, ifindex, NULL, FALSE); + return success; } /*****************************************************************************/ -NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_nmp_nlm_flag_to_string_lookup, NMPNlmFlags, - NM_UTILS_LOOKUP_DEFAULT (NULL), - NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_ADD, "add"), - NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_CHANGE, "change"), - NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_REPLACE, "replace"), - NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_PREPEND, "prepend"), - NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_APPEND, "append"), - NM_UTILS_LOOKUP_ITEM (NMP_NLM_FLAG_TEST, "test"), - NM_UTILS_LOOKUP_ITEM_IGNORE (NMP_NLM_FLAG_F_APPEND), -); +/** + * nm_platform_ip_route_sync: + * @self: the #NMPlatform instance. + * @addr_family: AF_INET or AF_INET6. + * @ifindex: the @ifindex for which the routes are to be added. + * @routes: (allow-none): a list of routes to configure. Must contain + * NMPObject instances of routes, according to @addr_family. + * @kernel_delete_predicate: (allow-none): if not %NULL, previously + * existing routes already configured will only be deleted if the + * predicate returns TRUE. This allows to preserve/ignore some + * routes. For example by passing @nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel, + * routes with "proto kernel" will be left untouched. + * @kernel_delete_userdata: user data for @kernel_delete_predicate. + * + * Returns: %TRUE on success. + */ +gboolean +nm_platform_ip_route_sync (NMPlatform *self, + int addr_family, + int ifindex, + GPtrArray *routes, + NMPObjectPredicateFunc kernel_delete_predicate, + gpointer kernel_delete_userdata) +{ + const NMPlatformVTableRoute *vt; + gs_unref_ptrarray GPtrArray *plat_routes = NULL; + gs_unref_hashtable GHashTable *routes_idx = NULL; + const NMPObject *plat_o; + const NMPObject *conf_o; + const NMDedupMultiEntry *plat_entry; + guint i; + int i_type; + gboolean success = TRUE; + char sbuf1[sizeof (_nm_utils_to_string_buffer)]; + char sbuf2[sizeof (_nm_utils_to_string_buffer)]; + char sbuf_err[60]; -#define _nmp_nlm_flag_to_string(flags) \ - ({ \ - NMPNlmFlags _flags = (flags); \ - \ - _nmp_nlm_flag_to_string_lookup (flags) ?: nm_sprintf_bufa (100, "new[0x%x]", (unsigned) _flags); \ - }) + nm_assert (NM_IS_PLATFORM (self)); + nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + nm_assert (ifindex > 0); + + vt = addr_family == AF_INET + ? &nm_platform_vtable_route_v4 + : &nm_platform_vtable_route_v6; + + plat_routes = nm_platform_lookup_addrroute_clone (self, + vt->obj_type, + ifindex, + kernel_delete_predicate, + kernel_delete_userdata); + /* first delete routes which are in platform (@plat_routes), but not to configure (@routes/@routes_idx). */ + if (plat_routes) { + + /* create a lookup index. */ + if (routes && routes->len > 0) { + routes_idx = g_hash_table_new ((GHashFunc) nmp_object_id_hash, + (GEqualFunc) nmp_object_id_equal); + for (i = 0; i < routes->len; i++) { + conf_o = routes->pdata[i]; + if (!nm_g_hash_table_insert (routes_idx, (gpointer) conf_o, (gpointer) conf_o)) { + /* we ignore duplicate @routes. */ + } + } + } + + for (i = 0; i < plat_routes->len; i++) { + plat_o = plat_routes->pdata[i]; + + if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (NMP_OBJECT_CAST_IP_ROUTE (plat_o))) { + /* don't delete default routes. */ + continue; + } + + conf_o = routes_idx ? g_hash_table_lookup (routes_idx, plat_o) : NULL; + if ( !conf_o + || vt->route_cmp (NMP_OBJECT_CAST_IPX_ROUTE (conf_o), + NMP_OBJECT_CAST_IPX_ROUTE (plat_o), + NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) == 0) { + /* the route in platform is identical to the one we want to add. + * Keep it. */ + continue; + } + + if (!nm_platform_ip_route_delete (self, plat_o)) { + /* ignore error... */ + } + } + } + + if (!routes) + return success; + + for (i_type = 0; i_type < 2; i_type++) { + for (i = 0; i < routes->len; i++) { + NMPlatformError plerr; + + conf_o = routes->pdata[i]; + +#define VTABLE_IS_DEVICE_ROUTE(vt, o) (vt->is_ip4 \ + ? (NMP_OBJECT_CAST_IP4_ROUTE (o)->gateway == 0) \ + : IN6_IS_ADDR_UNSPECIFIED (&NMP_OBJECT_CAST_IP6_ROUTE (o)->gateway) ) + + if ( (i_type == 0 && !VTABLE_IS_DEVICE_ROUTE (vt, conf_o)) + || (i_type == 1 && VTABLE_IS_DEVICE_ROUTE (vt, conf_o))) { + /* we add routes in two runs over @i_type. + * + * First device routes, then gateway routes. */ + continue; + } + + plat_entry = nm_platform_lookup_entry (self, + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + conf_o); + if (plat_entry) { + /* we alreay have a route with the same ID in the platform cache. + * Skip adding it again. It should be identical already, otherwise we + * would have deleted above. */ + if (_LOGD_ENABLED ()) { + if (vt->route_cmp (NMP_OBJECT_CAST_IPX_ROUTE (conf_o), + NMP_OBJECT_CAST_IPX_ROUTE (plat_entry->obj), + NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) != 0) { + _LOGD ("route-sync: skip adding route %s due to existing (different!) route %s", + nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), + nmp_object_to_string (plat_entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf2, sizeof (sbuf2))); + } + } + continue; + } + + plerr = nm_platform_ip_route_add (self, + NMP_NLM_FLAG_APPEND + | NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE, + conf_o); + if (plerr != NM_PLATFORM_ERROR_SUCCESS) { + if (-((int) plerr) == EEXIST) { + /* Don't fail for EEXIST. It's not clear that the existing route + * is identical to the one that we were about to add. However, + * above we should have deleted conflicting (non-identical) routes. */ + if (_LOGD_ENABLED ()) { + plat_entry = nm_platform_lookup_entry (self, + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + conf_o); + if (!plat_entry) { + _LOGD ("route-sync: adding route %s failed with EEXIST, however we cannot find such a route", + nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1))); + } else if (vt->route_cmp (NMP_OBJECT_CAST_IPX_ROUTE (conf_o), + NMP_OBJECT_CAST_IPX_ROUTE (plat_entry->obj), + NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) != 0) { + _LOGD ("route-sync: adding route %s failed due to existing (different!) route %s", + nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), + nmp_object_to_string (plat_entry->obj, NMP_OBJECT_TO_STRING_PUBLIC, sbuf2, sizeof (sbuf2))); + } + } + } else if (NMP_OBJECT_CAST_IP_ROUTE (conf_o)->rt_source < NM_IP_CONFIG_SOURCE_USER) { + _LOGD ("route-sync: ignore failure to add IPv%c route: %s: %s", + vt->is_ip4 ? '4' : '6', + nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), + nm_platform_error_to_string (plerr, sbuf_err, sizeof (sbuf_err))); + } else { + const char *reason = ""; + + if ( -((int) plerr) == ENETUNREACH + && ( vt->is_ip4 + ? !!NMP_OBJECT_CAST_IP4_ROUTE (conf_o)->gateway + : !IN6_IS_ADDR_UNSPECIFIED (&NMP_OBJECT_CAST_IP6_ROUTE (conf_o)->gateway))) + reason = "; is the gateway directly reachable?"; + + _LOGW ("route-sync: failure to add IPv%c route: %s: %s%s", + vt->is_ip4 ? '4' : '6', + nmp_object_to_string (conf_o, NMP_OBJECT_TO_STRING_PUBLIC, sbuf1, sizeof (sbuf1)), + nm_platform_error_to_string (plerr, sbuf_err, sizeof (sbuf_err)), + reason); + success = FALSE; + } + } + } + } + + return success; +} gboolean +nm_platform_ip_route_flush (NMPlatform *self, + int addr_family, + int ifindex) +{ + gboolean success = TRUE; + + _CHECK_SELF (self, klass, FALSE); + + nm_assert (NM_IN_SET (addr_family, AF_UNSPEC, + AF_INET, + AF_INET6)); + + if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET)) + success &= nm_platform_ip_route_sync (self, AF_INET, ifindex, NULL, NULL, NULL); + if (NM_IN_SET (addr_family, AF_UNSPEC, AF_INET6)) + success &= nm_platform_ip_route_sync (self, AF_INET6, ifindex, NULL, NULL, NULL); + return success; +} + +/*****************************************************************************/ + +static guint8 +_ip_route_scope_inv_get_normalized (const NMPlatformIP4Route *route) +{ + /* in kernel, you cannot set scope to RT_SCOPE_NOWHERE (255). + * That means, in NM, we treat RT_SCOPE_NOWHERE as unset, and detect + * it based on the presence of the gateway. In other words, when adding + * a route with scope RT_SCOPE_NOWHERE (in NetworkManager) to kernel, + * the resulting scope will be either "link" or "universe" (depending + * on the gateway). + * + * Note that internally, we track @scope_inv is the inverse of scope, + * so that the default equals zero (~(RT_SCOPE_NOWHERE)). + **/ + if (route->scope_inv == 0) { + return nm_platform_route_scope_inv (!route->gateway + ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE); + } + return route->scope_inv; +} + +/** + * nm_platform_ip_route_normalize: + * @addr_family: AF_INET or AF_INET6 + * @route: an NMPlatformIP4Route or NMPlatformIP6Route instance, depending on @addr_family. + * + * Adding a route to kernel via nm_platform_ip_route_add() will normalize/coerce some + * properties of the route. This function modifies (normalizes) the route like it + * would be done by adding the route in kernel. + */ +void +nm_platform_ip_route_normalize (int addr_family, + NMPlatformIPRoute *route) +{ + NMPlatformIP4Route *r4; + NMPlatformIP6Route *r6; + + switch (addr_family) { + case AF_INET: + r4 = (NMPlatformIP4Route *) route; + r4->network = nm_utils_ip4_address_clear_host_address (r4->network, r4->plen); + r4->rt_source = nmp_utils_ip_config_source_round_trip_rtprot (r4->rt_source); + r4->scope_inv = _ip_route_scope_inv_get_normalized (r4); + break; + case AF_INET6: + r6 = (NMPlatformIP6Route *) route; + nm_utils_ip6_address_clear_host_address (&r6->network, &r6->network, r6->plen); + r6->rt_source = nmp_utils_ip_config_source_round_trip_rtprot (r6->rt_source), + r6->metric = nm_utils_ip6_route_metric_normalize (r6->metric); + nm_utils_ip6_address_clear_host_address (&r6->src, &r6->src, r6->src_plen); + break; + default: + nm_assert_not_reached (); + break; + } +} + +static NMPlatformError +_ip_route_add (NMPlatform *self, + NMPNlmFlags flags, + int addr_family, + gconstpointer route) +{ + char sbuf[sizeof (_nm_utils_to_string_buffer)]; + + _CHECK_SELF (self, klass, FALSE); + + nm_assert (route); + nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); + + _LOGD ("route: %-10s IPv%c route: %s", + _nmp_nlm_flag_to_string (flags & NMP_NLM_FLAG_FMASK), + addr_family == AF_INET ? '4' : '6', + addr_family == AF_INET + ? nm_platform_ip4_route_to_string (route, sbuf, sizeof (sbuf)) + : nm_platform_ip6_route_to_string (route, sbuf, sizeof (sbuf))); + + return klass->ip_route_add (self, flags, addr_family, route); +} + +NMPlatformError +nm_platform_ip_route_add (NMPlatform *self, + NMPNlmFlags flags, + const NMPObject *route) +{ + int addr_family; + + switch (NMP_OBJECT_GET_TYPE (route)) { + case NMP_OBJECT_TYPE_IP4_ROUTE: + addr_family = AF_INET; + break; + case NMP_OBJECT_TYPE_IP6_ROUTE: + addr_family = AF_INET6; + break; + default: + g_return_val_if_reached (FALSE); + } + + return _ip_route_add (self, flags, addr_family, NMP_OBJECT_CAST_IP_ROUTE (route)); +} + +NMPlatformError nm_platform_ip4_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP4Route *route) { - char sbuf[sizeof (_nm_utils_to_string_buffer)]; - - _CHECK_SELF (self, klass, FALSE); - - g_return_val_if_fail (route, FALSE); - g_return_val_if_fail (route->plen <= 32, FALSE); - - _LOGD ("route: %-10s IPv4 route: %s", - _nmp_nlm_flag_to_string (flags), - nm_platform_ip4_route_to_string (route, sbuf, sizeof (sbuf))); - - return klass->ip_route_add (self, flags, AF_INET, (const NMPlatformIPRoute *) route); + return _ip_route_add (self, flags, AF_INET, route); } -gboolean +NMPlatformError nm_platform_ip6_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP6Route *route) { - char sbuf[sizeof (_nm_utils_to_string_buffer)]; - - _CHECK_SELF (self, klass, FALSE); - - g_return_val_if_fail (route, FALSE); - g_return_val_if_fail (route->plen <= 128, FALSE); - - _LOGD ("route: %-10s IPv6 route: %s", - _nmp_nlm_flag_to_string (flags), - nm_platform_ip6_route_to_string (route, sbuf, sizeof (sbuf))); - - return klass->ip_route_add (self, flags, AF_INET6, (const NMPlatformIPRoute *) route); + return _ip_route_add (self, flags, AF_INET6, route); } gboolean @@ -3527,6 +3884,325 @@ nm_platform_ip_route_delete (NMPlatform *self, /*****************************************************************************/ +NMPlatformError +nm_platform_ip_route_get (NMPlatform *self, + int addr_family, + gconstpointer address /* in_addr_t or struct in6_addr */, + NMPObject **out_route) +{ + nm_auto_nmpobj NMPObject *route = NULL; + NMPlatformError result; + char buf[NM_UTILS_INET_ADDRSTRLEN]; + char buf_err[200]; + + _CHECK_SELF (self, klass, FALSE); + + g_return_val_if_fail (address, NM_PLATFORM_ERROR_BUG); + g_return_val_if_fail (NM_IN_SET (addr_family, AF_INET, + AF_INET6), NM_PLATFORM_ERROR_BUG); + + _LOGT ("route: get IPv%c route for: %s", + addr_family == AF_INET ? '4' : '6', + inet_ntop (addr_family, address, buf, sizeof (buf))); + + if (!klass->ip_route_get) + result = NM_PLATFORM_ERROR_OPNOTSUPP; + else { + result = klass->ip_route_get (self, + addr_family, + address, + &route); + } + + if (result != NM_PLATFORM_ERROR_SUCCESS) { + nm_assert (!route); + _LOGW ("route: get IPv%c route for: %s failed with %s", + addr_family == AF_INET ? '4' : '6', + inet_ntop (addr_family, address, buf, sizeof (buf)), + nm_platform_error_to_string (result, buf_err, sizeof (buf_err))); + } else { + nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (route), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); + nm_assert (!NMP_OBJECT_IS_STACKINIT (route)); + nm_assert (route->parent._ref_count == 1); + _LOGD ("route: get IPv%c route for: %s succeeded: %s", + addr_family == AF_INET ? '4' : '6', + inet_ntop (addr_family, address, buf, sizeof (buf)), + nmp_object_to_string (route, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + NM_SET_OUT (out_route, g_steal_pointer (&route)); + } + return result; +} + +/*****************************************************************************/ + +#define IP4_DEV_ROUTE_BLACKLIST_TIMEOUT_MS ((int) 1500) +#define IP4_DEV_ROUTE_BLACKLIST_GC_TIMEOUT_S ((int) (((IP4_DEV_ROUTE_BLACKLIST_TIMEOUT_MS + 999) * 3) / 1000)) + +static gint64 +_ip4_dev_route_blacklist_timeout_ms_get (gint64 timeout_ms) +{ + return timeout_ms >> 1; +} + +static gint64 +_ip4_dev_route_blacklist_timeout_ms_marked (gint64 timeout_ms) +{ + return !!(timeout_ms & ((gint64) 1)); +} + +static gboolean +_ip4_dev_route_blacklist_check_cb (gpointer user_data) +{ + NMPlatform *self = user_data; + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self); + GHashTableIter iter; + const NMPObject *p_obj; + gint64 *p_timeout_ms; + gint64 now_ms; + + priv->ip4_dev_route_blacklist_check_id = 0; + +again: + if (!priv->ip4_dev_route_blacklist_hash) + goto out; + + now_ms = nm_utils_get_monotonic_timestamp_ms (); + + g_hash_table_iter_init (&iter, priv->ip4_dev_route_blacklist_hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &p_obj, (gpointer *) &p_timeout_ms)) { + if (!_ip4_dev_route_blacklist_timeout_ms_marked (*p_timeout_ms)) + continue; + + /* unmark because we checked it. */ + *p_timeout_ms = *p_timeout_ms & ~((gint64) 1); + + if (now_ms > _ip4_dev_route_blacklist_timeout_ms_get (*p_timeout_ms)) + continue; + + if (!nm_platform_lookup_entry (self, + NMP_CACHE_ID_TYPE_OBJECT_TYPE, + p_obj)) + continue; + + _LOGT ("ip4-dev-route: delete %s", + nmp_object_to_string (p_obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + nm_platform_ip_route_delete (self, p_obj); + goto again; + } + +out: + return G_SOURCE_REMOVE; +} + +static void +_ip4_dev_route_blacklist_check_schedule (NMPlatform *self) +{ + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self); + + if (!priv->ip4_dev_route_blacklist_check_id) { + priv->ip4_dev_route_blacklist_check_id = g_idle_add_full (G_PRIORITY_HIGH, + _ip4_dev_route_blacklist_check_cb, + self, + NULL); + } +} + +static void +_ip4_dev_route_blacklist_notify_route (NMPlatform *self, + const NMPObject *obj) +{ + NMPlatformPrivate *priv; + const NMPObject *p_obj; + gint64 *p_timeout_ms; + gint64 now_ms; + + nm_assert (NM_IS_PLATFORM (self)); + nm_assert (NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_IP4_ROUTE); + + priv = NM_PLATFORM_GET_PRIVATE (self); + + nm_assert (priv->ip4_dev_route_blacklist_gc_timeout_id); + + if (!g_hash_table_lookup_extended (priv->ip4_dev_route_blacklist_hash, + obj, + (gpointer *) &p_obj, + (gpointer *) &p_timeout_ms)) + return; + + now_ms = nm_utils_get_monotonic_timestamp_ms (); + if (now_ms > _ip4_dev_route_blacklist_timeout_ms_get (*p_timeout_ms)) { + /* already expired. Wait for gc. */ + return; + } + + if (_ip4_dev_route_blacklist_timeout_ms_marked (*p_timeout_ms)) { + nm_assert (priv->ip4_dev_route_blacklist_check_id); + return; + } + + /* We cannot delete it right away because we are in the process of receiving netlink messages. + * It may be possible to do so, but complicated and error prone. + * + * Instead, we mark the entry and schedule an idle action (with high priority). */ + *p_timeout_ms = (*p_timeout_ms) | ((gint64) 1); + _ip4_dev_route_blacklist_check_schedule (self); +} + +static gboolean +_ip4_dev_route_blacklist_gc_timeout_handle (gpointer user_data) +{ + NMPlatform *self = user_data; + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self); + GHashTableIter iter; + const NMPObject *p_obj; + gint64 *p_timeout_ms; + gint64 now_ms; + + nm_assert (priv->ip4_dev_route_blacklist_gc_timeout_id); + + now_ms = nm_utils_get_monotonic_timestamp_ms (); + + g_hash_table_iter_init (&iter, priv->ip4_dev_route_blacklist_hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &p_obj, (gpointer *) &p_timeout_ms)) { + if (now_ms > _ip4_dev_route_blacklist_timeout_ms_get (*p_timeout_ms)) { + _LOGT ("ip4-dev-route: cleanup %s", + nmp_object_to_string (p_obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + g_hash_table_iter_remove (&iter); + } + } + + _ip4_dev_route_blacklist_schedule (self); + return G_SOURCE_CONTINUE; +} + +static void +_ip4_dev_route_blacklist_schedule (NMPlatform *self) +{ + NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self); + + if ( !priv->ip4_dev_route_blacklist_hash + || g_hash_table_size (priv->ip4_dev_route_blacklist_hash) == 0) { + g_clear_pointer (&priv->ip4_dev_route_blacklist_hash, g_hash_table_unref); + nm_clear_g_source (&priv->ip4_dev_route_blacklist_gc_timeout_id); + } else { + if (!priv->ip4_dev_route_blacklist_gc_timeout_id) { + /* this timeout is only to garbage collect the expired entries from priv->ip4_dev_route_blacklist_hash. + * It can run infrequently, and it doesn't hurt if expired entries linger around a bit + * longer then necessary. */ + priv->ip4_dev_route_blacklist_gc_timeout_id = g_timeout_add_seconds (IP4_DEV_ROUTE_BLACKLIST_GC_TIMEOUT_S, + _ip4_dev_route_blacklist_gc_timeout_handle, + self); + } + } +} + +/** + * nm_platform_ip4_dev_route_blacklist_set: + * @self: + * @ifindex: + * @ip4_dev_route_blacklist: + * + * When adding an IP address, kernel automatically adds a device route. + * This can be suppressed via the IFA_F_NOPREFIXROUTE address flag. For proper + * IPv6 support, we require kernel support for IFA_F_NOPREFIXROUTE and always + * add the device route manually. + * + * For IPv4, this flag is rather new and we don't rely on it yet. We want to use + * it (but currently still don't). So, for IPv4, kernel possibly adds a device + * route, however it has a wrong metric of zero. We add our own device route (with + * proper metric), but need to delete the route that kernel adds. + * + * The problem is, that kernel does not immidiately add the route, when adding + * the address. It only shows up some time later. So, we register here a list + * of blacklisted routes, and when they show up within a time out, we assume it's + * the kernel generated one, and we delete it. + * + * Eventually, we want to get rid of this and use IFA_F_NOPREFIXROUTE for IPv4 + * routes as well. + */ +void +nm_platform_ip4_dev_route_blacklist_set (NMPlatform *self, + int ifindex, + GPtrArray *ip4_dev_route_blacklist) +{ + NMPlatformPrivate *priv; + GHashTableIter iter; + const NMPObject *p_obj; + guint i; + gint64 timeout_ms; + gint64 timeout_ms_val; + gint64 *p_timeout_ms; + gboolean needs_check = FALSE; + + nm_assert (NM_IS_PLATFORM (self)); + nm_assert (ifindex > 0); + + priv = NM_PLATFORM_GET_PRIVATE (self); + + /* first, expire all for current ifindex... */ + if (priv->ip4_dev_route_blacklist_hash) { + g_hash_table_iter_init (&iter, priv->ip4_dev_route_blacklist_hash); + while (g_hash_table_iter_next (&iter, (gpointer *) &p_obj, (gpointer *) &p_timeout_ms)) { + if (NMP_OBJECT_CAST_IP4_ROUTE (p_obj)->ifindex == ifindex) { + /* we could g_hash_table_iter_remove(&iter) the current entry. + * Instead, just expire it and let _ip4_dev_route_blacklist_gc_timeout_handle() + * handle it. + * + * The assumption is, that ip4_dev_route_blacklist contains the very same entry + * again, with a new timeout. So, we can un-expire it below. */ + *p_timeout_ms = 0; + } + } + } + + if ( ip4_dev_route_blacklist + && ip4_dev_route_blacklist->len > 0) { + + if (!priv->ip4_dev_route_blacklist_hash) { + priv->ip4_dev_route_blacklist_hash = g_hash_table_new_full ((GHashFunc) nmp_object_id_hash, + (GEqualFunc) nmp_object_id_equal, + (GDestroyNotify) nmp_object_unref, + nm_g_slice_free_fcn_gint64); + } + + timeout_ms = nm_utils_get_monotonic_timestamp_ms () + IP4_DEV_ROUTE_BLACKLIST_TIMEOUT_MS; + timeout_ms_val = (timeout_ms << 1) | ((gint64) 1); + for (i = 0; i < ip4_dev_route_blacklist->len; i++) { + const NMPObject *o; + + needs_check = TRUE; + o = ip4_dev_route_blacklist->pdata[i]; + if (g_hash_table_lookup_extended (priv->ip4_dev_route_blacklist_hash, + o, + (gpointer *) &p_obj, + (gpointer *) &p_timeout_ms)) { + if (nmp_object_equal (p_obj, o)) { + /* un-expire and reuse the entry. */ + _LOGT ("ip4-dev-route: register %s (update)", + nmp_object_to_string (p_obj, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + *p_timeout_ms = timeout_ms_val; + continue; + } + } + + _LOGT ("ip4-dev-route: register %s", + nmp_object_to_string (o, NMP_OBJECT_TO_STRING_PUBLIC, NULL, 0)); + p_timeout_ms = g_slice_new (gint64); + *p_timeout_ms = timeout_ms_val; + g_hash_table_replace (priv->ip4_dev_route_blacklist_hash, + (gpointer) nmp_object_ref (o), + p_timeout_ms); + } + } + + _ip4_dev_route_blacklist_schedule (self); + + if (needs_check) + _ip4_dev_route_blacklist_check_schedule (self); +} + +/*****************************************************************************/ + const char * nm_platform_vlan_qos_mapping_to_string (const char *name, const NMVlanQosMapping *map, @@ -4192,6 +4868,7 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi char s_network[INET_ADDRSTRLEN], s_gateway[INET_ADDRSTRLEN]; char s_pref_src[INET_ADDRSTRLEN]; char str_dev[TO_STRING_DEV_BUF_SIZE]; + char str_table[30]; char str_scope[30], s_source[50]; char str_tos[32], str_window[32], str_cwnd[32], str_initcwnd[32], str_initrwnd[32], str_mtu[32]; @@ -4205,6 +4882,7 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi g_snprintf (buf, len, + "%s" /* table */ "%s/%d" " via %s" "%s" @@ -4221,6 +4899,7 @@ nm_platform_ip4_route_to_string (const NMPlatformIP4Route *route, char *buf, gsi "%s" /* initrwnd */ "%s" /* mtu */ "", + route->table_coerced ? nm_sprintf_buf (str_table, "table %u ", nm_platform_route_table_coerce (route->table_coerced)) : "", s_network, route->plen, s_gateway, @@ -4259,6 +4938,7 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsi { char s_network[INET6_ADDRSTRLEN], s_gateway[INET6_ADDRSTRLEN], s_pref_src[INET6_ADDRSTRLEN]; char s_src_all[INET6_ADDRSTRLEN + 40], s_src[INET6_ADDRSTRLEN]; + char str_table[30]; char str_dev[TO_STRING_DEV_BUF_SIZE], s_source[50]; char str_window[32], str_cwnd[32], str_initcwnd[32], str_initrwnd[32], str_mtu[32]; @@ -4276,6 +4956,7 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsi _to_string_dev (NULL, route->ifindex, str_dev, sizeof (str_dev)); g_snprintf (buf, len, + "%s" /* table */ "%s/%d" " via %s" "%s" @@ -4291,6 +4972,7 @@ nm_platform_ip6_route_to_string (const NMPlatformIP6Route *route, char *buf, gsi "%s" /* initrwnd */ "%s" /* mtu */ "", + route->table_coerced ? nm_sprintf_buf (str_table, "table %u ", nm_platform_route_table_coerce (route->table_coerced)) : "", s_network, route->plen, s_gateway, @@ -4737,14 +5419,15 @@ nm_platform_ip4_route_hash (const NMPlatformIP4Route *obj, NMPlatformIPRouteCmpT break; case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: + h = NM_HASH_COMBINE (h, obj->table_coerced); h = NM_HASH_COMBINE (h, nm_utils_ip4_address_clear_host_address (obj->network, obj->plen)); h = NM_HASH_COMBINE (h, obj->plen); h = NM_HASH_COMBINE (h, obj->metric); h = NM_HASH_COMBINE (h, obj->tos); if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { h = NM_HASH_COMBINE (h, obj->ifindex); - h = NM_HASH_COMBINE (h, obj->rt_source); - h = NM_HASH_COMBINE (h, obj->scope_inv); + h = NM_HASH_COMBINE (h, nmp_utils_ip_config_source_round_trip_rtprot (obj->rt_source)); + h = NM_HASH_COMBINE (h, _ip_route_scope_inv_get_normalized (obj)); h = NM_HASH_COMBINE (h, obj->gateway); h = NM_HASH_COMBINE (h, obj->mss); h = NM_HASH_COMBINE (h, obj->pref_src); @@ -4762,6 +5445,7 @@ nm_platform_ip4_route_hash (const NMPlatformIP4Route *obj, NMPlatformIPRouteCmpT break; case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL: + h = NM_HASH_COMBINE (h, obj->table_coerced); h = NM_HASH_COMBINE (h, obj->ifindex); if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) h = NM_HASH_COMBINE (h, nm_utils_ip4_address_clear_host_address (obj->network, obj->plen)); @@ -4770,9 +5454,14 @@ nm_platform_ip4_route_hash (const NMPlatformIP4Route *obj, NMPlatformIPRouteCmpT h = NM_HASH_COMBINE (h, obj->plen); h = NM_HASH_COMBINE (h, obj->metric); h = NM_HASH_COMBINE (h, obj->gateway); - h = NM_HASH_COMBINE (h, obj->rt_source); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) { + h = NM_HASH_COMBINE (h, nmp_utils_ip_config_source_round_trip_rtprot (obj->rt_source)); + h = NM_HASH_COMBINE (h, _ip_route_scope_inv_get_normalized (obj)); + } else { + h = NM_HASH_COMBINE (h, obj->rt_source); + h = NM_HASH_COMBINE (h, obj->scope_inv); + } h = NM_HASH_COMBINE (h, obj->mss); - h = NM_HASH_COMBINE (h, obj->scope_inv); h = NM_HASH_COMBINE (h, obj->pref_src); h = NM_HASH_COMBINE (h, obj->rt_cloned); h = NM_HASH_COMBINE (h, obj->tos); @@ -4803,14 +5492,17 @@ nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route break; case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: + NM_CMP_FIELD (a, b, table_coerced); NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX (a->network, b->network, MIN (a->plen, b->plen)); NM_CMP_FIELD (a, b, plen); NM_CMP_FIELD (a, b, metric); NM_CMP_FIELD (a, b, tos); if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { NM_CMP_FIELD (a, b, ifindex); - NM_CMP_FIELD (a, b, rt_source); - NM_CMP_FIELD (a, b, scope_inv); + NM_CMP_DIRECT (nmp_utils_ip_config_source_round_trip_rtprot (a->rt_source), + nmp_utils_ip_config_source_round_trip_rtprot (b->rt_source)); + NM_CMP_DIRECT (_ip_route_scope_inv_get_normalized (a), + _ip_route_scope_inv_get_normalized (b)); NM_CMP_FIELD (a, b, gateway); NM_CMP_FIELD (a, b, mss); NM_CMP_FIELD (a, b, pref_src); @@ -4828,6 +5520,7 @@ nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route break; case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL: + NM_CMP_FIELD (a, b, table_coerced); NM_CMP_FIELD (a, b, ifindex); if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX (a->network, b->network, MIN (a->plen, b->plen)); @@ -4836,9 +5529,16 @@ nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route NM_CMP_FIELD (a, b, plen); NM_CMP_FIELD (a, b, metric); NM_CMP_FIELD (a, b, gateway); - NM_CMP_FIELD (a, b, rt_source); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) { + NM_CMP_DIRECT (nmp_utils_ip_config_source_round_trip_rtprot (a->rt_source), + nmp_utils_ip_config_source_round_trip_rtprot (b->rt_source)); + NM_CMP_DIRECT (_ip_route_scope_inv_get_normalized (a), + _ip_route_scope_inv_get_normalized (b)); + } else { + NM_CMP_FIELD (a, b, rt_source); + NM_CMP_FIELD (a, b, scope_inv); + } NM_CMP_FIELD (a, b, mss); - NM_CMP_FIELD (a, b, scope_inv); NM_CMP_FIELD (a, b, pref_src); NM_CMP_FIELD_UNSAFE (a, b, rt_cloned); NM_CMP_FIELD (a, b, tos); @@ -4870,9 +5570,10 @@ nm_platform_ip6_route_hash (const NMPlatformIP6Route *obj, NMPlatformIPRouteCmpT break; case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: + h = NM_HASH_COMBINE (h, obj->table_coerced); h = NM_HASH_COMBINE_IN6ADDR_PREFIX (h, &obj->network, obj->plen); h = NM_HASH_COMBINE (h, obj->plen); - h = NM_HASH_COMBINE (h, obj->metric); + h = NM_HASH_COMBINE (h, nm_utils_ip6_route_metric_normalize (obj->metric)); h = NM_HASH_COMBINE_IN6ADDR_PREFIX (h, &obj->src, obj->src_plen); h = NM_HASH_COMBINE (h, obj->src_plen); if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { @@ -4882,23 +5583,28 @@ nm_platform_ip6_route_hash (const NMPlatformIP6Route *obj, NMPlatformIPRouteCmpT break; case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL: + h = NM_HASH_COMBINE (h, obj->table_coerced); h = NM_HASH_COMBINE (h, obj->ifindex); if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) h = NM_HASH_COMBINE_IN6ADDR_PREFIX (h, &obj->network, obj->plen); else h = NM_HASH_COMBINE_IN6ADDR (h, &obj->network); h = NM_HASH_COMBINE (h, obj->plen); - h = NM_HASH_COMBINE (h, obj->metric); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) + h = NM_HASH_COMBINE (h, nm_utils_ip6_route_metric_normalize (obj->metric)); + else + h = NM_HASH_COMBINE (h, obj->metric); h = NM_HASH_COMBINE_IN6ADDR (h, &obj->gateway); h = NM_HASH_COMBINE_IN6ADDR (h, &obj->pref_src); if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) { h = NM_HASH_COMBINE_IN6ADDR_PREFIX (h, &obj->src, obj->src_plen); h = NM_HASH_COMBINE (h, obj->src_plen); + h = NM_HASH_COMBINE (h, nmp_utils_ip_config_source_round_trip_rtprot (obj->rt_source)); } else { h = NM_HASH_COMBINE_IN6ADDR (h, &obj->src); h = NM_HASH_COMBINE (h, obj->src_plen); + h = NM_HASH_COMBINE (h, obj->rt_source); } - h = NM_HASH_COMBINE (h, obj->rt_source); h = NM_HASH_COMBINE (h, obj->mss); h = NM_HASH_COMBINE (h, obj->rt_cloned); h = NM_HASH_COMBINE (h, obj->lock_window); @@ -4928,9 +5634,10 @@ nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route break; case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: + NM_CMP_FIELD (a, b, table_coerced); NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX (&a->network, &b->network, MIN (a->plen, b->plen)); NM_CMP_FIELD (a, b, plen); - NM_CMP_FIELD (a, b, metric); + NM_CMP_DIRECT (nm_utils_ip6_route_metric_normalize (a->metric), nm_utils_ip6_route_metric_normalize (b->metric)); NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX (&a->src, &b->src, MIN (a->src_plen, b->src_plen)); NM_CMP_FIELD (a, b, src_plen); if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { @@ -4940,23 +5647,29 @@ nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route break; case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: case NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL: + NM_CMP_FIELD (a, b, table_coerced); NM_CMP_FIELD (a, b, ifindex); if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX (&a->network, &b->network, MIN (a->plen, b->plen)); else NM_CMP_FIELD_IN6ADDR (a, b, network); NM_CMP_FIELD (a, b, plen); - NM_CMP_FIELD (a, b, metric); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) + NM_CMP_DIRECT (nm_utils_ip6_route_metric_normalize (a->metric), nm_utils_ip6_route_metric_normalize (b->metric)); + else + NM_CMP_FIELD (a, b, metric); NM_CMP_FIELD_IN6ADDR (a, b, gateway); NM_CMP_FIELD_IN6ADDR (a, b, pref_src); if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) { NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX (&a->src, &b->src, MIN (a->src_plen, b->src_plen)); NM_CMP_FIELD (a, b, src_plen); + NM_CMP_DIRECT (nmp_utils_ip_config_source_round_trip_rtprot (a->rt_source), + nmp_utils_ip_config_source_round_trip_rtprot (b->rt_source)); } else { NM_CMP_FIELD_IN6ADDR (a, b, src); NM_CMP_FIELD (a, b, src_plen); + NM_CMP_FIELD (a, b, rt_source); } - NM_CMP_FIELD (a, b, rt_source); NM_CMP_FIELD (a, b, mss); NM_CMP_FIELD_UNSAFE (a, b, rt_cloned); NM_CMP_FIELD_UNSAFE (a, b, lock_window); @@ -5088,6 +5801,10 @@ nm_platform_cache_update_emit_signal (NMPlatform *self, ASSERT_nmp_cache_ops (nm_platform_get_cache (self), cache_op, obj_old, obj_new); + nm_assert (NM_IN_SET (nm_platform_netns_get (self), + NULL, + nmp_netns_get_current ())); + NMTST_ASSERT_PLATFORM_NETNS_CURRENT (self); switch (cache_op) { @@ -5123,6 +5840,11 @@ nm_platform_cache_update_emit_signal (NMPlatform *self, klass = NMP_OBJECT_GET_CLASS (o); + if ( klass->obj_type == NMP_OBJECT_TYPE_IP4_ROUTE + && NM_PLATFORM_GET_PRIVATE (self)->ip4_dev_route_blacklist_gc_timeout_id + && NM_IN_SET (cache_op, NMP_CACHE_OPS_ADDED, NMP_CACHE_OPS_UPDATED)) + _ip4_dev_route_blacklist_notify_route (self, o); + _LOGt ("emit signal %s %s: %s", klass->signal_type, nm_platform_signal_change_type_to_string ((NMPlatformSignalChangeType) cache_op), @@ -5172,40 +5894,6 @@ nm_platform_netns_push (NMPlatform *self, NMPNetns **netns) /*****************************************************************************/ -static gboolean -_vtr_v4_route_add (NMPlatform *self, - NMPNlmFlags flags, - const NMPlatformIPXRoute *route, - int ifindex, - gint64 metric) -{ - NMPlatformIP4Route rt = route->r4; - - if (ifindex > 0) - rt.ifindex = ifindex; - if (metric >= 0) - rt.metric = metric; - - return nm_platform_ip4_route_add (self, flags, &rt); -} - -static gboolean -_vtr_v6_route_add (NMPlatform *self, - NMPNlmFlags flags, - const NMPlatformIPXRoute *route, - int ifindex, - gint64 metric) -{ - NMPlatformIP6Route rt = route->r6; - - if (ifindex > 0) - rt.ifindex = ifindex; - if (metric >= 0) - rt.metric = metric; - - return nm_platform_ip6_route_add (self, flags, &rt); -} - static guint32 _vtr_v4_metric_normalize (guint32 metric) { @@ -5221,7 +5909,6 @@ const NMPlatformVTableRoute nm_platform_vtable_route_v4 = { .sizeof_route = sizeof (NMPlatformIP4Route), .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, NMPlatformIPRouteCmpType cmp_type)) nm_platform_ip4_route_cmp, .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip4_route_to_string, - .route_add = _vtr_v4_route_add, .metric_normalize = _vtr_v4_metric_normalize, }; @@ -5232,7 +5919,6 @@ const NMPlatformVTableRoute nm_platform_vtable_route_v6 = { .sizeof_route = sizeof (NMPlatformIP6Route), .route_cmp = (int (*) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, NMPlatformIPRouteCmpType cmp_type)) nm_platform_ip6_route_cmp, .route_to_string = (const char *(*) (const NMPlatformIPXRoute *route, char *buf, gsize len)) nm_platform_ip6_route_to_string, - .route_add = _vtr_v6_route_add, .metric_normalize = nm_utils_ip6_route_metric_normalize, }; @@ -5304,6 +5990,9 @@ finalize (GObject *object) NMPlatform *self = NM_PLATFORM (object); NMPlatformPrivate *priv = NM_PLATFORM_GET_PRIVATE (self); + nm_clear_g_source (&priv->ip4_dev_route_blacklist_check_id); + nm_clear_g_source (&priv->ip4_dev_route_blacklist_gc_timeout_id); + g_clear_pointer (&priv->ip4_dev_route_blacklist_hash, g_hash_table_unref); g_clear_object (&self->_netns); nm_dedup_multi_index_unref (priv->multi_idx); nmp_cache_free (priv->cache); diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 841c5e77a7..e14c71512d 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -52,6 +52,9 @@ struct udev_device; +typedef gboolean (*NMPObjectPredicateFunc) (const NMPObject *obj, + gpointer user_data); + /* workaround for older libnl version, that does not define these flags. */ #ifndef IFA_F_MANAGETEMPADDR #define IFA_F_MANAGETEMPADDR 0x100 @@ -60,6 +63,8 @@ struct udev_device; #define IFA_F_NOPREFIXROUTE 0x200 #endif +#define NM_RT_SCOPE_LINK 253 /* RT_SCOPE_LINK */ + /* Define of the IN6_ADDR_GEN_MODE_* values to workaround old kernel headers * that don't define it. */ #define NM_IN6_ADDR_GEN_MODE_UNKNOWN 255 /* no corresponding value. */ @@ -81,6 +86,15 @@ typedef enum { NMP_NLM_FLAG_F_CREATE = 0x400, /* NLM_F_CREATE, Create, if it does not exist */ NMP_NLM_FLAG_F_APPEND = 0x800, /* NLM_F_APPEND, Add to end of list */ + NMP_NLM_FLAG_FMASK = 0xFFFF, /* a mask for all NMP_NLM_FLAG_F_* flags */ + + /* instructs NM to suppress logging an error message for any failures + * received from kernel. + * + * It will still log with debug-level, and it will still log + * other failures aside the kernel response. */ + NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE = 0x10000, + /* the following aliases correspond to iproute2's `ip route CMD` for * RTM_NEWROUTE, with CMD being one of add, change, replace, prepend, * append and test. */ @@ -153,6 +167,7 @@ typedef enum { /*< skip >*/ NM_PLATFORM_ERROR_NOT_SLAVE, NM_PLATFORM_ERROR_NO_FIRMWARE, NM_PLATFORM_ERROR_OPNOTSUPP, + NM_PLATFORM_ERROR_NETLINK, } NMPlatformError; #define NM_PLATFORM_LINK_OTHER_NETNS (-1) @@ -409,6 +424,13 @@ typedef union { /* RTA_PRIORITY (iproute2: metric) */ \ guint32 metric; \ \ + /* rtm_table, RTA_TABLE. + * + * This is not the original table ID. Instead, 254 (RT_TABLE_MAIN) and + * zero (RT_TABLE_UNSPEC) are swapped, so that the default is the main + * table. Use nm_platform_route_table_coerce(). */ \ + guint32 table_coerced; \ + \ /*end*/ @@ -420,8 +442,23 @@ typedef struct { }; } NMPlatformIPRoute; +#if _NM_CC_SUPPORT_GENERIC +#define NM_PLATFORM_IP_ROUTE_IS_DEFAULT(route) \ + (_Generic ((route), \ + const NMPlatformIPRoute *: ((const NMPlatformIPRoute *) (route))->plen, \ + NMPlatformIPRoute *: ((const NMPlatformIPRoute *) (route))->plen, \ + const NMPlatformIPXRoute *: ((const NMPlatformIPRoute *) (route))->plen, \ + NMPlatformIPXRoute *: ((const NMPlatformIPRoute *) (route))->plen, \ + const NMPlatformIP4Route *: ((const NMPlatformIPRoute *) (route))->plen, \ + NMPlatformIP4Route *: ((const NMPlatformIPRoute *) (route))->plen, \ + const NMPlatformIP6Route *: ((const NMPlatformIPRoute *) (route))->plen, \ + NMPlatformIP6Route *: ((const NMPlatformIPRoute *) (route))->plen, \ + const void *: ((const NMPlatformIPRoute *) (route))->plen, \ + void *: ((const NMPlatformIPRoute *) (route))->plen) == 0) +#else #define NM_PLATFORM_IP_ROUTE_IS_DEFAULT(route) \ ( ((const NMPlatformIPRoute *) (route))->plen <= 0 ) +#endif struct _NMPlatformIP4Route { __NMPlatformIPRoute_COMMON; @@ -503,11 +540,6 @@ typedef struct { gsize sizeof_route; int (*route_cmp) (const NMPlatformIPXRoute *a, const NMPlatformIPXRoute *b, NMPlatformIPRouteCmpType cmp_type); const char *(*route_to_string) (const NMPlatformIPXRoute *route, char *buf, gsize len); - gboolean (*route_add) (NMPlatform *self, - NMPNlmFlags flags, - const NMPlatformIPXRoute *route, - int ifindex, - gint64 metric); guint32 (*metric_normalize) (guint32 metric); } NMPlatformVTableRoute; @@ -782,12 +814,17 @@ typedef struct { gboolean (*ip4_address_delete) (NMPlatform *, int ifindex, in_addr_t address, guint8 plen, in_addr_t peer_address); gboolean (*ip6_address_delete) (NMPlatform *, int ifindex, struct in6_addr address, guint8 plen); - gboolean (*ip_route_add) (NMPlatform *, - NMPNlmFlags flags, - int addr_family, - const NMPlatformIPRoute *route); + NMPlatformError (*ip_route_add) (NMPlatform *, + NMPNlmFlags flags, + int addr_family, + const NMPlatformIPRoute *route); gboolean (*ip_route_delete) (NMPlatform *, const NMPObject *obj); + NMPlatformError (*ip_route_get) (NMPlatform *self, + int addr_family, + gconstpointer address, + NMPObject **out_route); + gboolean (*check_support_kernel_extended_ifa_flags) (NMPlatform *); gboolean (*check_support_user_ipv6ll) (NMPlatform *); } NMPlatformClass; @@ -822,6 +859,27 @@ NMPlatform *nm_platform_get (void); /*****************************************************************************/ +/** + * nm_platform_route_table_coerce: + * @table: the route table, either its original value, or its coerced. + * + * Returns: returns the coerced table id. If the table id is like + * RTA_TABLE, it returns a value for NMPlatformIPRoute.table_coerced + * and vice versa. + */ +static inline guint32 +nm_platform_route_table_coerce (guint32 table) +{ + switch (table) { + case 0 /* RT_TABLE_UNSPEC */: + return 254; + case 254 /* RT_TABLE_MAIN */: + return 0; + default: + return table; + } +} + /** * nm_platform_route_scope_inv: * @scope: the route scope, either its original value, or its inverse. @@ -847,8 +905,11 @@ gboolean nm_platform_netns_push (NMPlatform *platform, NMPNetns **netns); const char *nm_link_type_to_string (NMLinkType link_type); -const char *_nm_platform_error_to_string (NMPlatformError error); -#define nm_platform_error_to_string(error) NM_UTILS_LOOKUP_STR (_nm_platform_error_to_string, error) +const char *nm_platform_error_to_string (NMPlatformError error, + char *buf, + gsize buf_len); +#define nm_platform_error_to_string_a(error) \ + (nm_platform_error_to_string ((error), g_alloca (30), 30)) #define NMP_SYSCTL_PATHID_ABSOLUTE(path) \ ((const char *) NULL), -1, (path) @@ -897,12 +958,14 @@ struct _NMPLookup; const struct _NMDedupMultiHeadEntry *nm_platform_lookup (NMPlatform *platform, const struct _NMPLookup *lookup); +gboolean nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel (const NMPObject *obj, + gpointer user_data); gboolean nm_platform_lookup_predicate_routes_skip_rtprot_kernel (const NMPObject *obj, gpointer user_data); GPtrArray *nm_platform_lookup_clone (NMPlatform *platform, const struct _NMPLookup *lookup, - gboolean (*predicate) (const NMPObject *obj, gpointer user_data), + NMPObjectPredicateFunc predicate, gpointer user_data); /* convienience methods to lookup the link and access fields of NMPlatformLink. */ @@ -1095,13 +1158,36 @@ gboolean nm_platform_ip4_address_delete (NMPlatform *self, int ifindex, in_addr_ gboolean nm_platform_ip6_address_delete (NMPlatform *self, int ifindex, struct in6_addr address, guint8 plen); gboolean nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, GPtrArray *known_addresse); gboolean nm_platform_ip6_address_sync (NMPlatform *self, int ifindex, const GPtrArray *known_addresses, gboolean keep_link_local); -gboolean nm_platform_address_flush (NMPlatform *self, int ifindex); +gboolean nm_platform_ip_address_flush (NMPlatform *self, + int addr_family, + int ifindex); -gboolean nm_platform_ip4_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP4Route *route); -gboolean nm_platform_ip6_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP6Route *route); +void nm_platform_ip_route_normalize (int addr_family, + NMPlatformIPRoute *route); + +NMPlatformError nm_platform_ip_route_add (NMPlatform *self, + NMPNlmFlags flags, + const NMPObject *route); +NMPlatformError nm_platform_ip4_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP4Route *route); +NMPlatformError nm_platform_ip6_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP6Route *route); gboolean nm_platform_ip_route_delete (NMPlatform *self, const NMPObject *route); +gboolean nm_platform_ip_route_sync (NMPlatform *self, + int addr_family, + int ifindex, + GPtrArray *routes, + NMPObjectPredicateFunc kernel_delete_predicate, + gpointer kernel_delete_userdata); +gboolean nm_platform_ip_route_flush (NMPlatform *self, + int addr_family, + int ifindex); + +NMPlatformError nm_platform_ip_route_get (NMPlatform *self, + int addr_family, + gconstpointer address, + NMPObject **out_route); + const char *nm_platform_link_to_string (const NMPlatformLink *link, char *buf, gsize len); const char *nm_platform_lnk_gre_to_string (const NMPlatformLnkGre *lnk, char *buf, gsize len); const char *nm_platform_lnk_infiniband_to_string (const NMPlatformLnkInfiniband *lnk, char *buf, gsize len); @@ -1192,6 +1278,10 @@ gboolean nm_platform_ethtool_set_wake_on_lan (NMPlatform *self, int ifindex, NMS gboolean nm_platform_ethtool_set_link_settings (NMPlatform *self, int ifindex, gboolean autoneg, guint32 speed, NMPlatformLinkDuplexType duplex); gboolean nm_platform_ethtool_get_link_settings (NMPlatform *self, int ifindex, gboolean *out_autoneg, guint32 *out_speed, NMPlatformLinkDuplexType *out_duplex); +void nm_platform_ip4_dev_route_blacklist_set (NMPlatform *self, + int ifindex, + GPtrArray *ip4_dev_route_blacklist); + struct _NMDedupMultiIndex *nm_platform_get_multi_idx (NMPlatform *self); #endif /* __NETWORKMANAGER_PLATFORM_H__ */ diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index 8bb6e96877..6b4a28c25c 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -676,11 +676,11 @@ ASSERT_nmp_cache_ops (const NMPCache *cache, const NMDedupMultiHeadEntry *nm_platform_lookup_all (NMPlatform *platform, NMPCacheIdType cache_id_type, - NMPObject *obj); + const NMPObject *obj); const NMDedupMultiEntry *nm_platform_lookup_entry (NMPlatform *platform, NMPCacheIdType cache_id_type, - NMPObject *obj); + const NMPObject *obj); static inline const NMDedupMultiHeadEntry * nm_platform_lookup_obj_type (NMPlatform *platform, @@ -717,7 +717,7 @@ static inline GPtrArray * nm_platform_lookup_addrroute_clone (NMPlatform *platform, NMPObjectType obj_type, int ifindex, - gboolean (*predicate) (const NMPObject *obj, gpointer user_data), + NMPObjectPredicateFunc predicate, gpointer user_data) { NMPLookup lookup; @@ -739,7 +739,7 @@ nm_platform_lookup_route_default (NMPlatform *platform, static inline GPtrArray * nm_platform_lookup_route_default_clone (NMPlatform *platform, NMPObjectType obj_type, - gboolean (*predicate) (const NMPObject *obj, gpointer user_data), + NMPObjectPredicateFunc predicate, gpointer user_data) { NMPLookup lookup; diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c index 9692c09050..c0e6cb96de 100644 --- a/src/platform/tests/test-common.c +++ b/src/platform/tests/test-common.c @@ -1009,7 +1009,7 @@ void nmtstp_ip4_route_add (NMPlatform *platform, route.metric = metric; route.mss = mss; - g_assert (nm_platform_ip4_route_add (platform, NMP_NLM_FLAG_REPLACE, &route)); + g_assert_cmpint (nm_platform_ip4_route_add (platform, NMP_NLM_FLAG_REPLACE, &route), ==, NM_PLATFORM_ERROR_SUCCESS); } void nmtstp_ip6_route_add (NMPlatform *platform, @@ -1033,7 +1033,7 @@ void nmtstp_ip6_route_add (NMPlatform *platform, route.metric = metric; route.mss = mss; - g_assert (nm_platform_ip6_route_add (platform, NMP_NLM_FLAG_REPLACE, &route)); + g_assert_cmpint (nm_platform_ip6_route_add (platform, NMP_NLM_FLAG_REPLACE, &route), ==, NM_PLATFORM_ERROR_SUCCESS); } /*****************************************************************************/ diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h index ad58e141a9..4010aa2f83 100644 --- a/src/platform/tests/test-common.h +++ b/src/platform/tests/test-common.h @@ -230,7 +230,7 @@ nmtstp_ip4_route_get_all (NMPlatform *platform, return nm_platform_lookup_addrroute_clone (platform, NMP_OBJECT_TYPE_IP4_ROUTE, ifindex, - nm_platform_lookup_predicate_routes_skip_rtprot_kernel, + nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel, NULL); } @@ -241,7 +241,7 @@ nmtstp_ip6_route_get_all (NMPlatform *platform, return nm_platform_lookup_addrroute_clone (platform, NMP_OBJECT_TYPE_IP6_ROUTE, ifindex, - nm_platform_lookup_predicate_routes_skip_rtprot_kernel, + nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel, NULL); } diff --git a/src/platform/tests/test-link.c b/src/platform/tests/test-link.c index 0e5fcbe453..0a974eb13a 100644 --- a/src/platform/tests/test-link.c +++ b/src/platform/tests/test-link.c @@ -791,7 +791,7 @@ test_software_detect (gconstpointer user_data) * namespaced, the creation can fail if a macvtap in another namespace * has the same index. Try to detect this situation and skip already * used indexes. - * http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=17af2bce88d31e65ed73d638bb752d2e13c66ced + * The fix (17af2bce) is included kernel 4.7, dated 24 July, 2016. */ for (i = ifindex_parent + 1; i < ifindex_parent + 100; i++) { snprintf (buf, sizeof (buf), "/sys/class/macvtap/tap%d", i); @@ -1728,8 +1728,8 @@ test_nl_bugs_veth (void) pllink_veth0 = nm_platform_link_get (NM_PLATFORM_GET, ifindex_veth0); g_assert (pllink_veth0); if (pllink_veth0->parent == 0) { - /* pre-4.1 kernels don't support exposing the veth peer as IFA_LINK. skip the remainder - * of the test. */ + /* Kernels prior to 4.1 dated 21 June, 2015 don't support exposing the veth peer + * as IFA_LINK. skip the remainder of the test. */ goto out; } g_assert_cmpint (pllink_veth0->parent, ==, ifindex_veth1); @@ -2023,8 +2023,8 @@ test_netns_general (gpointer fixture, gconstpointer test_data) _sysctl_assert_eq (platform_1, "/proc/sys/net/ipv6/conf/dummy2b/disable_ipv6", NULL); _sysctl_assert_eq (platform_2, "/proc/sys/net/ipv6/conf/dummy2a/disable_ipv6", NULL); - /* older kernels (Ubuntu 12.04) don't support ethtool -i for dummy devices. Work around that and - * skip asserts that are known to fail. */ + /* Kernels prior to 3.19 dated 8 February, 2015 don't support ethtool -i for dummy devices. + * Work around that and skip asserts that are known to fail. */ ethtool_support = nmtstp_run_command ("ethtool -i dummy1_ > /dev/null") == 0; if (ethtool_support) { g_assert (nmp_utils_ethtool_get_driver_info (nmtstp_link_get_typed (platform_1, 0, "dummy1_", NM_LINK_TYPE_DUMMY)->ifindex, &driver_info)); diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c index 3dac6e5758..f749d7514c 100644 --- a/src/platform/tests/test-route.c +++ b/src/platform/tests/test-route.c @@ -388,6 +388,43 @@ test_ip6_route (void) /*****************************************************************************/ +static void +test_ip_route_get (void) +{ + int ifindex = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); + in_addr_t a; + NMPlatformError result; + nm_auto_nmpobj NMPObject *route = NULL; + const NMPlatformIP4Route *r; + + nmtstp_run_command_check ("ip route add 1.2.3.0/24 dev %s", DEVICE_NAME); + + NMTST_WAIT_ASSERT (100, { + nmtstp_wait_for_signal (NM_PLATFORM_GET, 10); + if (nmtstp_ip4_route_get (NM_PLATFORM_GET, ifindex, nmtst_inet4_from_string ("1.2.3.0"), 24, 0, 0)) + break; + }); + + a = nmtst_inet4_from_string ("1.2.3.1"); + result = nm_platform_ip_route_get (NM_PLATFORM_GET, + AF_INET, + &a, + &route); + + g_assert (result == NM_PLATFORM_ERROR_SUCCESS); + g_assert (NMP_OBJECT_GET_TYPE (route) == NMP_OBJECT_TYPE_IP4_ROUTE); + g_assert (!NMP_OBJECT_IS_STACKINIT (route)); + g_assert (route->parent._ref_count == 1); + r = NMP_OBJECT_CAST_IP4_ROUTE (route); + g_assert (r->rt_cloned); + g_assert (r->network == a); + g_assert (r->plen == 32); + + nmtstp_run_command_check ("ip route flush dev %s", DEVICE_NAME); + + nmtstp_wait_for_signal (NM_PLATFORM_GET, 50); +} + static void test_ip4_zero_gateway (void) { @@ -432,7 +469,7 @@ test_ip4_route_options (void) route.mtu = 1350; route.lock_cwnd = TRUE; - g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &route)); + g_assert (nm_platform_ip4_route_add (NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &route) == NM_PLATFORM_ERROR_SUCCESS); /* Test route listing */ routes = nmtstp_ip4_route_get_all (NM_PLATFORM_GET, ifindex); @@ -560,7 +597,7 @@ test_ip6_route_options (gconstpointer test_data) _wait_for_ipv6_addr_non_tentative (NM_PLATFORM_GET, 400, IFINDEX, addr_n, addr_in6); for (i = 0; i < rts_n; i++) - g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &rts_add[i])); + g_assert (nm_platform_ip6_route_add (NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &rts_add[i]) == NM_PLATFORM_ERROR_SUCCESS); routes = nmtstp_ip6_route_get_all (NM_PLATFORM_GET, IFINDEX); switch (TEST_IDX) { @@ -666,7 +703,7 @@ again_find_idx: order_idx[order_len++] = idx; r->ifindex = iface_data[idx].ifindex; - g_assert (nm_platform_ip4_route_add (platform, NMP_NLM_FLAG_APPEND, r)); + g_assert (nm_platform_ip4_route_add (platform, NMP_NLM_FLAG_APPEND, r) == NM_PLATFORM_ERROR_SUCCESS); } else { i = nmtst_get_rand_int () % order_len; idx = order_idx[i]; @@ -739,6 +776,7 @@ _nmtstp_setup_tests (void) if (nmtstp_is_root_test ()) { add_test_func_data ("/route/ip/1", test_ip, GINT_TO_POINTER (1)); + add_test_func ("/route/ip_route_get", test_ip_route_get); add_test_func ("/route/ip4_zero_gateway", test_ip4_zero_gateway); } } diff --git a/src/tests/test-route-manager.c b/src/tests/test-route-manager.c deleted file mode 100644 index 2778cfcc82..0000000000 --- a/src/tests/test-route-manager.c +++ /dev/null @@ -1,974 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2015 Red Hat, Inc. - * - */ - -#include "nm-default.h" - -#include -#include - -#include "platform/nm-platform.h" -#include "platform/nm-platform-utils.h" -#include "nm-route-manager.h" - -#include "platform/tests/test-common.h" - -typedef struct { - int ifindex0, ifindex1; -} test_fixture; - -NMRouteManager *route_manager_get (void); - -NM_DEFINE_SINGLETON_GETTER (NMRouteManager, route_manager_get, NM_TYPE_ROUTE_MANAGER); - -/*****************************************************************************/ - -static void -setup_dev0_ip4 (int ifindex, guint mss_of_first_route, guint32 metric_of_second_route) -{ - GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); - NMPlatformIP4Route route = { 0 }; - - route.ifindex = ifindex; - route.mss = 0; - - route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); - inet_pton (AF_INET, "6.6.6.0", &route.network); - route.plen = 24; - route.gateway = INADDR_ANY; - route.metric = 20; - route.mss = mss_of_first_route; - g_array_append_val (routes, route); - - route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); - inet_pton (AF_INET, "7.0.0.0", &route.network); - route.plen = 8; - inet_pton (AF_INET, "6.6.6.1", &route.gateway); - route.metric = metric_of_second_route; - route.mss = 0; - g_array_append_val (routes, route); - - nm_route_manager_ip4_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE); - g_array_free (routes, TRUE); -} - -static void -setup_dev1_ip4 (int ifindex) -{ - GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); - NMPlatformIP4Route route = { 0 }; - - route.ifindex = ifindex; - route.mss = 0; - - /* Add some route outside of route manager. The route manager - * should get rid of it upon sync. */ - nmtstp_ip4_route_add (NM_PLATFORM_GET, - route.ifindex, - NM_IP_CONFIG_SOURCE_USER, - nmtst_inet4_from_string ("9.0.0.0"), - 8, - INADDR_ANY, - 0, - 10, - route.mss); - - route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); - inet_pton (AF_INET, "6.6.6.0", &route.network); - route.plen = 24; - route.gateway = INADDR_ANY; - route.metric = 20; - g_array_append_val (routes, route); - - route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); - inet_pton (AF_INET, "7.0.0.0", &route.network); - route.plen = 8; - route.gateway = INADDR_ANY; - route.metric = 22; - g_array_append_val (routes, route); - - route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); - inet_pton (AF_INET, "8.0.0.0", &route.network); - route.plen = 8; - inet_pton (AF_INET, "6.6.6.2", &route.gateway); - route.metric = 22; - g_array_append_val (routes, route); - - nm_route_manager_ip4_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE); - g_array_free (routes, TRUE); -} - -static void -update_dev0_ip4 (int ifindex) -{ - GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); - NMPlatformIP4Route route = { 0 }; - - route.ifindex = ifindex; - route.mss = 0; - - route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); - inet_pton (AF_INET, "6.6.6.0", &route.network); - route.plen = 24; - route.gateway = INADDR_ANY; - route.metric = 20; - g_array_append_val (routes, route); - - route.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); - inet_pton (AF_INET, "7.0.0.0", &route.network); - route.plen = 8; - route.gateway = INADDR_ANY; - route.metric = 21; - g_array_append_val (routes, route); - - nm_route_manager_ip4_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE); - g_array_free (routes, TRUE); -} - - -static GArray * -ip_routes (test_fixture *fixture, NMPObjectType obj_type) -{ - const NMPClass *klass; - GArray *routes; - const NMDedupMultiHeadEntry *pl_head_entry; - NMDedupMultiIter iter; - const NMPObject *plobj = NULL; - guint i; - - g_assert (NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); - - klass = nmp_class_from_type (obj_type); - - routes = g_array_new (FALSE, FALSE, klass->sizeof_public); - - for (i = 0; i < 2; i++) { - int ifindex; - - if (i == 0) - ifindex = fixture->ifindex0; - else - ifindex = fixture->ifindex1; - - pl_head_entry = nm_platform_lookup_addrroute (NM_PLATFORM_GET, - obj_type, - ifindex); - nmp_cache_iter_for_each (&iter, pl_head_entry, &plobj) { - const NMPlatformIPRoute *r = NMP_OBJECT_CAST_IP_ROUTE (plobj); - - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r)) - continue; - if (r->rt_source == NM_IP_CONFIG_SOURCE_RTPROT_KERNEL) - continue; - g_assert (r->ifindex == ifindex); - g_assert (nmp_object_is_visible (plobj)); - g_array_append_vals (routes, r, 1); - } - } - - return routes; -} - -static void -test_ip4 (test_fixture *fixture, gconstpointer user_data) -{ - GArray *routes; - - NMPlatformIP4Route state1[] = { - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("6.6.6.0"), - .plen = 24, - .ifindex = fixture->ifindex0, - .gateway = INADDR_ANY, - .metric = 20, - .mss = 1000, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("7.0.0.0"), - .plen = 8, - .ifindex = fixture->ifindex0, - .gateway = nmtst_inet4_from_string ("6.6.6.1"), - .metric = 21021, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("7.0.0.0"), - .plen = 8, - .ifindex = fixture->ifindex1, - .gateway = INADDR_ANY, - .metric = 22, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("6.6.6.0"), - .plen = 24, - .ifindex = fixture->ifindex1, - .gateway = INADDR_ANY, - .metric = 21, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("8.0.0.0"), - .plen = 8, - .ifindex = fixture->ifindex1, - .gateway = nmtst_inet4_from_string ("6.6.6.2"), - .metric = 22, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE), - }, - }; - - NMPlatformIP4Route state2[] = { - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("6.6.6.0"), - .plen = 24, - .ifindex = fixture->ifindex0, - .gateway = INADDR_ANY, - .metric = 20, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("7.0.0.0"), - .plen = 8, - .ifindex = fixture->ifindex0, - .gateway = INADDR_ANY, - .metric = 21, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("7.0.0.0"), - .plen = 8, - .ifindex = fixture->ifindex1, - .gateway = INADDR_ANY, - .metric = 22, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("6.6.6.0"), - .plen = 24, - .ifindex = fixture->ifindex1, - .gateway = INADDR_ANY, - .metric = 21, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("8.0.0.0"), - .plen = 8, - .ifindex = fixture->ifindex1, - .gateway = nmtst_inet4_from_string ("6.6.6.2"), - .metric = 22, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE), - }, - }; - - NMPlatformIP4Route state3[] = { - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("7.0.0.0"), - .plen = 8, - .ifindex = fixture->ifindex1, - .gateway = INADDR_ANY, - .metric = 22, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("6.6.6.0"), - .plen = 24, - .ifindex = fixture->ifindex1, - .gateway = INADDR_ANY, - .metric = 20, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("8.0.0.0"), - .plen = 8, - .ifindex = fixture->ifindex1, - .gateway = nmtst_inet4_from_string ("6.6.6.2"), - .metric = 22, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_UNIVERSE), - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = nmtst_inet4_from_string ("6.6.6.0"), - .plen = 24, - .ifindex = fixture->ifindex1, - .gateway = INADDR_ANY, - /* this is a ghost entry because we synced ifindex0 and restore the route - * with metric 20 (above). But we don't remove the metric 21. */ - .metric = 21, - .mss = 0, - .scope_inv = nm_platform_route_scope_inv (RT_SCOPE_LINK), - }, - }; - - setup_dev0_ip4 (fixture->ifindex0, 1000, 21021); - setup_dev1_ip4 (fixture->ifindex1); - g_test_assert_expected_messages (); - - /* - 6.6.6.0/24 on dev0 won over 6.6.6.0/24 on dev1 - * - 6.6.6.0/24 on dev1 has metric bumped. - * - 7.0.0.0/8 route, metric 21021 added - * - 7.0.0.0/8 route, metric 22 added - * - 8.0.0.0/8 could be added. */ - routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP4_ROUTE); - g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1)); - nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state1, routes->len, TRUE); - g_array_free (routes, TRUE); - - setup_dev1_ip4 (fixture->ifindex1); - g_test_assert_expected_messages (); - - setup_dev0_ip4 (fixture->ifindex0, 0, 21); - - /* Ensure nothing changed. */ - routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP4_ROUTE); - g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1)); - state1[0].mss = 0; - state1[1].metric = 21; - nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state1, routes->len, TRUE); - g_array_free (routes, TRUE); - - update_dev0_ip4 (fixture->ifindex0); - - /* minor changes in the routes. Quite similar to state1. */ - routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP4_ROUTE); - g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state2)); - nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state2, routes->len, TRUE); - g_array_free (routes, TRUE); - - nm_route_manager_route_flush (route_manager_get (), fixture->ifindex0); - - /* 6.6.6.0/24 is now on dev1 - * 6.6.6.0/24 is also still on dev1 with bumped metric 21. - * 7.0.0.0/8 gone from dev0, still present on dev1 - * 8.0.0.0/8 is present on dev1 - * No dev0 routes left. */ - routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP4_ROUTE); - g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state3)); - nmtst_platform_ip4_routes_equal ((NMPlatformIP4Route *) routes->data, state3, routes->len, TRUE); - g_array_free (routes, TRUE); - - nm_route_manager_route_flush (route_manager_get (), fixture->ifindex1); - - /* No routes left. */ - routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP4_ROUTE); - g_assert_cmpint (routes->len, ==, 0); - g_array_free (routes, TRUE); -} - -static void -setup_dev0_ip6 (int ifindex) -{ - GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route)); - NMPlatformIP6Route *route; - - /* Add an address so that a route to the gateway below gets added. */ - nm_platform_ip6_address_add (NM_PLATFORM_GET, - ifindex, - *nmtst_inet6_from_string ("2001:db8:8086::666"), - 64, - in6addr_any, - 3600, - 3600, - 0); - - route = nmtst_platform_ip6_route_full ("2001:db8:8086::", - 48, - NULL, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 20, - 0); - g_array_append_val (routes, *route); - - route = nmtst_platform_ip6_route_full ("2001:db8:1337::", - 48, - NULL, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 0, - 0); - g_array_append_val (routes, *route); - - route = nmtst_platform_ip6_route_full ("2001:db8:abad:c0de::", - 64, - "2001:db8:8086::1", - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 21, - 0); - g_array_append_val (routes, *route); - - nm_route_manager_ip6_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE); - g_array_free (routes, TRUE); -} - -static void -setup_dev1_ip6 (int ifindex) -{ - GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route)); - NMPlatformIP6Route *route; - - /* Add some route outside of route manager. The route manager - * should get rid of it upon sync. */ - nmtstp_ip6_route_add (NM_PLATFORM_GET, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - *nmtst_inet6_from_string ("2001:db8:8088::"), - 48, - in6addr_any, - in6addr_any, - 10, - 0); - - route = nmtst_platform_ip6_route_full ("2001:db8:8086::", - 48, - NULL, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 20, - 0); - g_array_append_val (routes, *route); - - route = nmtst_platform_ip6_route_full ("2001:db8:1337::", - 48, - NULL, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 1024, - 0); - g_array_append_val (routes, *route); - - route = nmtst_platform_ip6_route_full ("2001:db8:d34d::", - 64, - "2001:db8:8086::2", - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 20, - 0); - g_array_append_val (routes, *route); - - route = nmtst_platform_ip6_route_full ("2001:db8:abad:c0de::", - 64, - NULL, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 22, - 0); - g_array_append_val (routes, *route); - - nm_route_manager_ip6_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE); - g_array_free (routes, TRUE); -} - -static void -update_dev0_ip6 (int ifindex) -{ - GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP6Route)); - NMPlatformIP6Route *route; - - /* Add an address so that a route to the gateway below gets added. */ - nm_platform_ip6_address_add (NM_PLATFORM_GET, - ifindex, - *nmtst_inet6_from_string ("2001:db8:8086::2"), - 64, - in6addr_any, - 3600, - 3600, - 0); - - route = nmtst_platform_ip6_route_full ("2001:db8:8086::", - 48, - NULL, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 20, - 0); - g_array_append_val (routes, *route); - - route = nmtst_platform_ip6_route_full ("2001:db8:1337::", - 48, - NULL, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 0, - 0); - g_array_append_val (routes, *route); - - route = nmtst_platform_ip6_route_full ("2001:db8:abad:c0de::", - 64, - NULL, - ifindex, - NM_IP_CONFIG_SOURCE_USER, - 21, - 0); - g_array_append_val (routes, *route); - - nm_route_manager_ip6_route_sync (route_manager_get (), ifindex, routes, TRUE, TRUE); - g_array_free (routes, TRUE); -} - -static void -test_ip6 (test_fixture *fixture, gconstpointer user_data) -{ - GArray *routes; - int i; - - NMPlatformIP6Route state1[] = { - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:8086::"), - .plen = 48, - .ifindex = fixture->ifindex0, - .gateway = in6addr_any, - .metric = 20, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:1337::"), - .plen = 48, - .ifindex = fixture->ifindex0, - .gateway = in6addr_any, - .metric = 1024, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"), - .plen = 64, - .ifindex = fixture->ifindex0, - .gateway = *nmtst_inet6_from_string ("2001:db8:8086::1"), - .metric = 21, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"), - .plen = 64, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 22, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:1337::"), - .plen = 48, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 1025, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:8086::"), - .plen = 48, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 21, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:d34d::"), - .plen = 64, - .ifindex = fixture->ifindex1, - .gateway = *nmtst_inet6_from_string ("2001:db8:8086::2"), - .metric = 20, - .mss = 0, - }, - }; - - NMPlatformIP6Route state2[] = { - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:8086::"), - .plen = 48, - .ifindex = fixture->ifindex0, - .gateway = in6addr_any, - .metric = 20, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:1337::"), - .plen = 48, - .ifindex = fixture->ifindex0, - .gateway = in6addr_any, - .metric = 1024, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"), - .plen = 64, - .ifindex = fixture->ifindex0, - .gateway = in6addr_any, - .metric = 21, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"), - .plen = 64, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 22, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:1337::"), - .plen = 48, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 1025, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:8086::"), - .plen = 48, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 21, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:d34d::"), - .plen = 64, - .ifindex = fixture->ifindex1, - .gateway = *nmtst_inet6_from_string ("2001:db8:8086::2"), - .metric = 20, - .mss = 0, - }, - }; - - NMPlatformIP6Route state3[] = { - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"), - .plen = 64, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 22, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:8086::"), - .plen = 48, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 20, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:1337::"), - .plen = 48, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 1024, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:1337::"), - .plen = 48, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 1025, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:8086::"), - .plen = 48, - .ifindex = fixture->ifindex1, - .gateway = in6addr_any, - .metric = 21, - .mss = 0, - }, - { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:d34d::"), - .plen = 64, - .ifindex = fixture->ifindex1, - .gateway = *nmtst_inet6_from_string ("2001:db8:8086::2"), - .metric = 20, - .mss = 0, - }, - }; - - setup_dev0_ip6 (fixture->ifindex0); - setup_dev1_ip6 (fixture->ifindex1); - g_test_assert_expected_messages (); - - /* 2001:db8:8086::/48 on dev0 won over 2001:db8:8086::/48 on dev1 - * 2001:db8:d34d::/64 on dev1 could not be added - * 2001:db8:1337::/48 on dev0 won over 2001:db8:1337::/48 on dev1 and has metric 1024 - * 2001:db8:abad:c0de::/64 routes did not clash */ - routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP6_ROUTE); - g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1)); - nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state1, routes->len, TRUE); - g_array_free (routes, TRUE); - - - setup_dev1_ip6 (fixture->ifindex1); - g_test_assert_expected_messages (); - setup_dev0_ip6 (fixture->ifindex0); - - /* Ensure nothing changed. */ - routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP6_ROUTE); - g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state1)); - nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state1, routes->len, TRUE); - g_array_free (routes, TRUE); - - update_dev0_ip6 (fixture->ifindex0); - - /* 2001:db8:abad:c0de::/64 on dev0 was updated for gateway removal*/ - routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP6_ROUTE); - if (routes->len != G_N_ELEMENTS (state2)) { - NMPlatformIP6Route rr; - - /* hm, seems kernel may wrongly treat IPv6 gateway for `ip route replace`. - * See rh#1480427. - * - * We would expect that `ip route replace` replaces an existing route. - * However, kernel may not do so, and instead prepend it. - * - * Work around that, by checking if such a route exists and accept - * it. */ - g_assert (nmtstp_is_root_test ()); - g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state2) + 1); - rr = ((NMPlatformIP6Route) { - .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), - .network = *nmtst_inet6_from_string ("2001:db8:abad:c0de::"), - .plen = 64, - .ifindex = fixture->ifindex0, - .gateway = *nmtst_inet6_from_string ("2001:db8:8086::1"), - .metric = 21, - .mss = 0, - }); - for (i = 0; i < routes->len; i++) { - if (nm_platform_ip6_route_cmp (&rr, - &g_array_index (routes, NMPlatformIP6Route, i), - NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) != 0) - continue; - g_array_remove_index (routes, i); - break; - } - } - nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state2, routes->len, TRUE); - g_array_free (routes, TRUE); - - nm_route_manager_route_flush (route_manager_get (), fixture->ifindex0); - - /* 2001:db8:abad:c0de::/64 on dev1 is still there, went away from dev0 - * 2001:db8:8086::/48 is now on dev1 - * 2001:db8:1337::/48 is now on dev1, metric of 1024 still applies - * 2001:db8:d34d::/64 is present now that 2001:db8:8086::/48 is on dev1 - * No dev0 routes left. */ - routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP6_ROUTE); - g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state3)); - nmtst_platform_ip6_routes_equal ((NMPlatformIP6Route *) routes->data, state3, routes->len, TRUE); - g_array_free (routes, TRUE); - - nm_route_manager_route_flush (route_manager_get (), fixture->ifindex1); - - /* No routes left. */ - routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP6_ROUTE); - g_assert_cmpint (routes->len, ==, 0); - g_array_free (routes, TRUE); -} - -/*****************************************************************************/ - -static void -_assert_route_check (const NMPlatformVTableRoute *vtable, gboolean has, const NMPlatformIPXRoute *route) -{ - const NMPlatformIPXRoute *r; - NMPlatformIPXRoute c; - - g_assert (route); - - if (vtable->is_ip4) - r = (const NMPlatformIPXRoute *) nmtstp_ip4_route_get (NM_PLATFORM_GET, route->rx.ifindex, route->r4.network, route->rx.plen, route->rx.metric, route->r4.tos); - else - r = (const NMPlatformIPXRoute *) nmtstp_ip6_route_get (NM_PLATFORM_GET, route->rx.ifindex, &route->r6.network, route->rx.plen, route->rx.metric, &route->r6.src, route->r6.src_plen); - - if (!has) { - g_assert (!r); - } else { - char buf[sizeof (_nm_utils_to_string_buffer)]; - - if (r) { - if (vtable->is_ip4) - c.r4 = route->r4; - else - c.r6 = route->r6; - c.rx.rt_source = nmp_utils_ip_config_source_round_trip_rtprot (c.rx.rt_source); - } - if (!r || vtable->route_cmp (r, &c, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) != 0) { - g_error ("Invalid route. Expect %s, has %s", - vtable->route_to_string (&c, NULL, 0), - vtable->route_to_string (r, buf, sizeof (buf))); - } - } -} - -static void -test_ip4_full_sync (test_fixture *fixture, gconstpointer user_data) -{ - const NMPlatformVTableRoute *vtable = &nm_platform_vtable_route_v4; - gs_unref_array GArray *routes = g_array_new (FALSE, FALSE, sizeof (NMPlatformIP4Route)); - NMPlatformIP4Route r01, r02, r03; - - nm_log_dbg (LOGD_CORE, "TEST start test_ip4_full_sync(): start"); - - r01 = *nmtst_platform_ip4_route_full ("12.3.4.0", 24, NULL, - fixture->ifindex0, NM_IP_CONFIG_SOURCE_USER, - 100, 0, RT_SCOPE_LINK, NULL); - r02 = *nmtst_platform_ip4_route_full ("13.4.5.6", 32, "12.3.4.1", - fixture->ifindex0, NM_IP_CONFIG_SOURCE_USER, - 100, 0, RT_SCOPE_UNIVERSE, NULL); - r03 = *nmtst_platform_ip4_route_full ("14.5.6.7", 32, "12.3.4.1", - fixture->ifindex0, NM_IP_CONFIG_SOURCE_USER, - 110, 0, RT_SCOPE_UNIVERSE, NULL); - g_array_set_size (routes, 2); - g_array_index (routes, NMPlatformIP4Route, 0) = r01; - g_array_index (routes, NMPlatformIP4Route, 1) = r02; - nm_route_manager_ip4_route_sync (route_manager_get (), fixture->ifindex0, routes, TRUE, TRUE); - - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01); - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r02); - _assert_route_check (vtable, FALSE, (const NMPlatformIPXRoute *) &r03); - - vtable->route_add (NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, (const NMPlatformIPXRoute *) &r03, 0, -1); - - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01); - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r02); - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r03); - - nm_route_manager_ip4_route_sync (route_manager_get (), fixture->ifindex0, routes, TRUE, FALSE); - - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01); - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r02); - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r03); - - g_array_set_size (routes, 1); - - nm_route_manager_ip4_route_sync (route_manager_get (), fixture->ifindex0, routes, TRUE, FALSE); - - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01); - _assert_route_check (vtable, FALSE, (const NMPlatformIPXRoute *) &r02); - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r03); - - nm_route_manager_ip4_route_sync (route_manager_get (), fixture->ifindex0, routes, TRUE, TRUE); - - _assert_route_check (vtable, TRUE, (const NMPlatformIPXRoute *) &r01); - _assert_route_check (vtable, FALSE, (const NMPlatformIPXRoute *) &r02); - _assert_route_check (vtable, FALSE, (const NMPlatformIPXRoute *) &r03); - - nm_log_dbg (LOGD_CORE, "TEST test_ip4_full_sync(): done"); -} - -/*****************************************************************************/ - -static void -fixture_setup (test_fixture *fixture, gconstpointer user_data) -{ - SignalData *link_added; - - link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, - NM_PLATFORM_SIGNAL_ADDED, - link_callback, - "nm-test-device0"); - nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device0")); - g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "nm-test-device0")); - g_assert (nm_platform_link_dummy_add (NM_PLATFORM_GET, "nm-test-device0", NULL) == NM_PLATFORM_ERROR_SUCCESS); - accept_signal (link_added); - free_signal (link_added); - fixture->ifindex0 = nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device0"); - g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, fixture->ifindex0, NULL)); - - link_added = add_signal_ifname (NM_PLATFORM_SIGNAL_LINK_CHANGED, - NM_PLATFORM_SIGNAL_ADDED, - link_callback, - "nm-test-device1"); - nm_platform_link_delete (NM_PLATFORM_GET, nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device1")); - g_assert (!nm_platform_link_get_by_ifname (NM_PLATFORM_GET, "nm-test-device1")); - g_assert (nm_platform_link_dummy_add (NM_PLATFORM_GET, "nm-test-device1", NULL) == NM_PLATFORM_ERROR_SUCCESS); - accept_signal (link_added); - free_signal (link_added); - fixture->ifindex1 = nm_platform_link_get_ifindex (NM_PLATFORM_GET, "nm-test-device1"); - g_assert (nm_platform_link_set_up (NM_PLATFORM_GET, fixture->ifindex1, NULL)); -} - -static void -fixture_teardown (test_fixture *fixture, gconstpointer user_data) -{ - nm_platform_link_delete (NM_PLATFORM_GET, fixture->ifindex0); - nm_platform_link_delete (NM_PLATFORM_GET, fixture->ifindex1); -} - -/*****************************************************************************/ - -NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP; - -void -_nmtstp_init_tests (int *argc, char ***argv) -{ - nmtst_init_assert_logging (argc, argv, "WARN", "ALL"); -} - -void -_nmtstp_setup_tests (void) -{ - g_test_add ("/route-manager/ip4", test_fixture, NULL, fixture_setup, test_ip4, fixture_teardown); - g_test_add ("/route-manager/ip6", test_fixture, NULL, fixture_setup, test_ip6, fixture_teardown); - g_test_add ("/route-manager/ip4-full-sync", test_fixture, NULL, fixture_setup, test_ip4_full_sync, fixture_teardown); -} diff --git a/src/vpn/nm-vpn-connection.c b/src/vpn/nm-vpn-connection.c index 63692cffbc..11eb9a4952 100644 --- a/src/vpn/nm-vpn-connection.c +++ b/src/vpn/nm-vpn-connection.c @@ -45,7 +45,6 @@ #include "nm-core-internal.h" #include "nm-pacrunner-manager.h" #include "nm-default-route-manager.h" -#include "nm-route-manager.h" #include "nm-firewall-manager.h" #include "nm-config.h" #include "nm-vpn-plugin-info.h" @@ -394,9 +393,11 @@ vpn_cleanup (NMVpnConnection *self, NMDevice *parent_dev) NMVpnConnectionPrivate *priv = NM_VPN_CONNECTION_GET_PRIVATE (self); if (priv->ip_ifindex) { - nm_platform_link_set_down (nm_netns_get_platform (priv->netns), priv->ip_ifindex); - nm_route_manager_route_flush (nm_netns_get_route_manager (priv->netns), priv->ip_ifindex); - nm_platform_address_flush (nm_netns_get_platform (priv->netns), priv->ip_ifindex); + NMPlatform *platform = nm_netns_get_platform (priv->netns); + + nm_platform_link_set_down (platform, priv->ip_ifindex); + nm_platform_ip_route_flush (platform, AF_UNSPEC, priv->ip_ifindex); + nm_platform_ip_address_flush (platform, AF_UNSPEC, priv->ip_ifindex); } remove_parent_device_config (self, parent_dev); @@ -704,41 +705,55 @@ device_state_changed (NMActiveConnection *active, } static void -add_ip4_vpn_gateway_route (NMIP4Config *config, NMDevice *parent_device, guint32 vpn_gw) +add_ip4_vpn_gateway_route (NMIP4Config *config, + NMDevice *parent_device, + in_addr_t vpn_gw, + NMPlatform *platform) { NMIP4Config *parent_config; guint32 parent_gw; NMPlatformIP4Route route; guint32 route_metric; + nm_auto_nmpobj const NMPObject *route_resolved = NULL; g_return_if_fail (NM_IS_IP4_CONFIG (config)); g_return_if_fail (NM_IS_DEVICE (parent_device)); g_return_if_fail (vpn_gw != 0); + nm_assert (nm_ip4_config_get_ifindex (config) > 0); /* Set up a route to the VPN gateway's public IP address through the default * network device if the VPN gateway is on a different subnet. */ parent_config = nm_device_get_ip4_config (parent_device); g_return_if_fail (parent_config != NULL); + parent_gw = nm_ip4_config_get_gateway (parent_config); + /* If the VPN gateway is in the same subnet as one of the parent device's + * IP addresses, don't add the host route to it, but a route through the + * parent device. + */ + if (nm_ip4_config_destination_is_direct (parent_config, vpn_gw, 32)) + parent_gw = 0; + + /* actually, let's ask kernel how to reach @vpn_gw. If (and only if) + * the destination is on @parent_device, then we take that @parent_gw. */ + if (nm_platform_ip_route_get (platform, + AF_INET, + &vpn_gw, + (NMPObject **) &route_resolved) == NM_PLATFORM_ERROR_SUCCESS) { + const NMPlatformIP4Route *r = NMP_OBJECT_CAST_IP4_ROUTE (route_resolved); + + if (r->ifindex == nm_ip4_config_get_ifindex (config)) + parent_gw = r->gateway; + } + route_metric = nm_device_get_ip4_route_metric (parent_device); memset (&route, 0, sizeof (route)); route.network = vpn_gw; route.plen = 32; route.gateway = parent_gw; - /* Set up a device route if the parent device has no gateway */ - if (!parent_gw) - route.ifindex = nm_device_get_ip_ifindex (parent_device); - - /* If the VPN gateway is in the same subnet as one of the parent device's - * IP addresses, don't add the host route to it, but a route through the - * parent device. - */ - if (nm_ip4_config_destination_is_direct (parent_config, vpn_gw, 32)) - route.gateway = 0; - route.rt_source = NM_IP_CONFIG_SOURCE_VPN; route.metric = route_metric; nm_ip4_config_add_route (config, &route); @@ -754,7 +769,6 @@ add_ip4_vpn_gateway_route (NMIP4Config *config, NMDevice *parent_device, guint32 route.plen = 32; route.rt_source = NM_IP_CONFIG_SOURCE_VPN; route.metric = route_metric; - nm_ip4_config_add_route (config, &route); } } @@ -762,37 +776,54 @@ add_ip4_vpn_gateway_route (NMIP4Config *config, NMDevice *parent_device, guint32 static void add_ip6_vpn_gateway_route (NMIP6Config *config, NMDevice *parent_device, - const struct in6_addr *vpn_gw) + const struct in6_addr *vpn_gw, + NMPlatform *platform) { NMIP6Config *parent_config; const struct in6_addr *parent_gw; NMPlatformIP6Route route; guint32 route_metric; + nm_auto_nmpobj const NMPObject *route_resolved = NULL; g_return_if_fail (NM_IS_IP6_CONFIG (config)); g_return_if_fail (NM_IS_DEVICE (parent_device)); g_return_if_fail (vpn_gw != NULL); + nm_assert (nm_ip6_config_get_ifindex (config) > 0); parent_config = nm_device_get_ip6_config (parent_device); g_return_if_fail (parent_config != NULL); + + /* we add a direct route to the VPN gateway, but we only do that + * on the @parent_device. That is probably not correct in every case... */ parent_gw = nm_ip6_config_get_gateway (parent_config); if (!parent_gw) return; + /* If the VPN gateway is in the same subnet as one of the parent device's + * IP addresses, don't add the host route to it, but a route through the + * parent device. + */ + if (nm_ip6_config_destination_is_direct (parent_config, vpn_gw, 128)) + parent_gw = &in6addr_any; + + /* actually, let's ask kernel how to reach @vpn_gw. If (and only if) + * the destination is on @parent_device, then we take that @parent_gw. */ + if (nm_platform_ip_route_get (platform, + AF_INET6, + vpn_gw, + (NMPObject **) &route_resolved) == NM_PLATFORM_ERROR_SUCCESS) { + const NMPlatformIP6Route *r = NMP_OBJECT_CAST_IP6_ROUTE (route_resolved); + + if (r->ifindex == nm_ip6_config_get_ifindex (config)) + parent_gw = &r->gateway; + } + route_metric = nm_device_get_ip6_route_metric (parent_device); memset (&route, 0, sizeof (route)); route.network = *vpn_gw; route.plen = 128; route.gateway = *parent_gw; - - /* If the VPN gateway is in the same subnet as one of the parent device's - * IP addresses, don't add the host route to it, but a route through the - * parent device. - */ - if (nm_ip6_config_destination_is_direct (parent_config, vpn_gw, 128)) - route.gateway = in6addr_any; - route.rt_source = NM_IP_CONFIG_SOURCE_VPN; route.metric = route_metric; nm_ip6_config_add_route (config, &route); @@ -802,13 +833,14 @@ add_ip6_vpn_gateway_route (NMIP6Config *config, * routes include a subnet that matches the parent device's subnet, * the parent device's gateway would get routed through the VPN and fail. */ - memset (&route, 0, sizeof (route)); - route.network = *parent_gw; - route.plen = 128; - route.rt_source = NM_IP_CONFIG_SOURCE_VPN; - route.metric = route_metric; - - nm_ip6_config_add_route (config, &route); + if (!IN6_IS_ADDR_UNSPECIFIED (parent_gw)) { + memset (&route, 0, sizeof (route)); + route.network = *parent_gw; + route.plen = 128; + route.rt_source = NM_IP_CONFIG_SOURCE_VPN; + route.metric = route_metric; + nm_ip6_config_add_route (config, &route); + } } NMVpnConnection * @@ -1067,24 +1099,36 @@ apply_parent_device_config (NMVpnConnection *self) * vpn-config. Instead we tell NMDefaultRouteManager directly about the * default route. */ ifindex = nm_device_get_ip_ifindex (parent_dev); - if (priv->ip4_config) { - vpn4_parent_config = nm_ip4_config_new (nm_netns_get_multi_idx (priv->netns), - ifindex); - nm_ip4_config_merge (vpn4_parent_config, priv->ip4_config, NM_IP_CONFIG_MERGE_NO_DNS); - } - if (priv->ip6_config) { - vpn6_parent_config = nm_ip6_config_new (nm_netns_get_multi_idx (priv->netns), - ifindex); - nm_ip6_config_merge (vpn6_parent_config, priv->ip6_config, NM_IP_CONFIG_MERGE_NO_DNS); - nm_ip6_config_set_gateway (vpn6_parent_config, NULL); + if (ifindex > 0) { + if (priv->ip4_config) { + vpn4_parent_config = nm_ip4_config_new (nm_netns_get_multi_idx (priv->netns), + ifindex); + nm_ip4_config_merge (vpn4_parent_config, priv->ip4_config, NM_IP_CONFIG_MERGE_NO_DNS); + } + if (priv->ip6_config) { + vpn6_parent_config = nm_ip6_config_new (nm_netns_get_multi_idx (priv->netns), + ifindex); + nm_ip6_config_merge (vpn6_parent_config, priv->ip6_config, NM_IP_CONFIG_MERGE_NO_DNS); + nm_ip6_config_set_gateway (vpn6_parent_config, NULL); + } } } /* Add any explicit route to the VPN gateway through the parent device */ - if (vpn4_parent_config && priv->ip4_external_gw) - add_ip4_vpn_gateway_route (vpn4_parent_config, parent_dev, priv->ip4_external_gw); - if (vpn6_parent_config && priv->ip6_external_gw) - add_ip6_vpn_gateway_route (vpn6_parent_config, parent_dev, priv->ip6_external_gw); + if ( vpn4_parent_config + && priv->ip4_external_gw) { + add_ip4_vpn_gateway_route (vpn4_parent_config, + parent_dev, + priv->ip4_external_gw, + nm_netns_get_platform (priv->netns)); + } + if ( vpn6_parent_config + && priv->ip6_external_gw) { + add_ip6_vpn_gateway_route (vpn6_parent_config, + parent_dev, + priv->ip6_external_gw, + nm_netns_get_platform (priv->netns)); + } nm_device_replace_vpn4_config (parent_dev, priv->last_device_ip4_config, vpn4_parent_config); g_clear_object (&priv->last_device_ip4_config); @@ -1104,21 +1148,17 @@ nm_vpn_connection_apply_config (NMVpnConnection *self) nm_platform_link_set_up (nm_netns_get_platform (priv->netns), priv->ip_ifindex, NULL); if (priv->ip4_config) { + nm_assert (priv->ip_ifindex == nm_ip4_config_get_ifindex (priv->ip4_config)); if (!nm_ip4_config_commit (priv->ip4_config, nm_netns_get_platform (priv->netns), - nm_netns_get_route_manager (priv->netns), - priv->ip_ifindex, - TRUE, nm_vpn_connection_get_ip4_route_metric (self))) return FALSE; } if (priv->ip6_config) { + nm_assert (priv->ip_ifindex == nm_ip6_config_get_ifindex (priv->ip6_config)); if (!nm_ip6_config_commit (priv->ip6_config, - nm_netns_get_platform (priv->netns), - nm_netns_get_route_manager (priv->netns), - priv->ip_ifindex, - TRUE)) + nm_netns_get_platform (priv->netns))) return FALSE; }