From 20235453a990fc8f91d1de6f50f5c8290549215d Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 23 Aug 2017 18:34:44 +0200 Subject: [PATCH 01/34] core: fix crash in nm_ip4_config_address_exists() Fixes: 22edeb5b691befd796c534cf71901b32f0b7945b --- src/nm-ip4-config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 20e2d2f946..b47f478fb9 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -1975,7 +1975,7 @@ 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); } From 3faff5b4570f8b1f1adb7ffeebac341e53e731bb Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 21 Aug 2017 16:04:11 +0200 Subject: [PATCH 02/34] core/trivial: indentation in src/devices/nm-device-ip-tunnel.c --- src/devices/nm-device-ip-tunnel.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/devices/nm-device-ip-tunnel.c b/src/devices/nm-device-ip-tunnel.c index 2f505ef4cf..5aae3e7103 100644 --- a/src/devices/nm-device-ip-tunnel.c +++ b/src/devices/nm-device-ip-tunnel.c @@ -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 (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 (plerr)); return FALSE; } break; From d100ce28e09273205f3443286f9c582702c1db67 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 15 Aug 2017 12:32:37 +0200 Subject: [PATCH 03/34] shared: add nm_g_slice_free_fcn() util Useful, when you need a GDestroyNotify function for g_slice_free() of a certain type. --- libnm-core/tests/test-general.c | 21 ++++++++++++++ shared/nm-utils/nm-shared-utils.h | 47 +++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) 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-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 From 3fc501e83320c3e6ddc1826ea1ef37289a644ccd Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 16 Aug 2017 14:35:55 +0200 Subject: [PATCH 04/34] shared: indicate changes when only reordering happens during nm_dedup_multi_index_add() The return value shall indicate whether the add-call changed anything. Reordering shall count as a change too. On the other hand, clearing the dirty flag of the entry does not count as a change. --- shared/nm-utils/nm-dedup-multi.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/shared/nm-utils/nm-dedup-multi.c b/shared/nm-utils/nm-dedup-multi.c index ac47d11b97..8942ba36fb 100644 --- a/shared/nm-utils/nm-dedup-multi.c +++ b/shared/nm-utils/nm-dedup-multi.c @@ -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); From 2f693fb68c0b71cc77e7a722c5d3b8bebea01e35 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 23 Aug 2017 16:00:11 +0200 Subject: [PATCH 05/34] shared: return deleted object from nm_dedup_multi_index_remove_obj() For completeness of the API. remove_obj() is basically a shortcut of nm_dedup_multi_index_lookup_obj() combined with nm_dedup_multi_index_remove_entry(). As such, it is useful to return the actually deleted object. Note that the lookup needle @obj is not necessarily the same instance as the one that will be removed, it's only an instance that compares equal according to the index's equality operator. --- shared/nm-utils/nm-dedup-multi.c | 13 +++++++++++-- shared/nm-utils/nm-dedup-multi.h | 3 ++- src/nm-ip4-config.c | 12 ++++++++---- src/nm-ip6-config.c | 12 ++++++++---- 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/shared/nm-utils/nm-dedup-multi.c b/shared/nm-utils/nm-dedup-multi.c index 8942ba36fb..383e40fd72 100644 --- a/shared/nm-utils/nm-dedup-multi.c +++ b/shared/nm-utils/nm-dedup-multi.c @@ -626,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; } diff --git a/shared/nm-utils/nm-dedup-multi.h b/shared/nm-utils/nm-dedup-multi.h index b6cfc43209..c99d6d11e1 100644 --- a/shared/nm-utils/nm-dedup-multi.h +++ b/shared/nm-utils/nm-dedup-multi.h @@ -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/src/nm-ip4-config.c b/src/nm-ip4-config.c index b47f478fb9..5e4cd7f59d 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -1228,7 +1228,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 +1257,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) @@ -1921,7 +1923,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); } @@ -2040,7 +2043,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-ip6-config.c b/src/nm-ip6-config.c index dc45acc18d..a3d958a616 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -1025,7 +1025,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 +1055,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) @@ -1586,7 +1588,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); } @@ -1763,7 +1766,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); } From ffd47da5dc9fcc2c1220bd4db9c9008db0571234 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 22 Aug 2017 16:38:05 +0200 Subject: [PATCH 06/34] platform: use _Generic() for NM_PLATFORM_IP_ROUTE_IS_DEFAULT() macro Avoid the plain cast and use _Generic() to check the type of @route argument. --- src/platform/nm-platform.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 841c5e77a7..6575634bed 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -420,8 +420,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; From d85c239101fa77a431f1086c42390a97d65d9edc Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 14 Aug 2017 12:27:30 +0200 Subject: [PATCH 07/34] platform: fix loose comparison for metric of IPv6 routes For IPv6 routes, a metric of zero is identical to a metric of 1024. Unless we do NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL, compare them as equal. --- src/platform/nm-platform.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 4f0ab9ac44..252f69fac4 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -4872,7 +4872,7 @@ nm_platform_ip6_route_hash (const NMPlatformIP6Route *obj, NMPlatformIPRouteCmpT case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: 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) { @@ -4888,7 +4888,10 @@ nm_platform_ip6_route_hash (const NMPlatformIP6Route *obj, NMPlatformIPRouteCmpT 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) { @@ -4930,7 +4933,7 @@ nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: 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) { @@ -4946,7 +4949,10 @@ nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route 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) { From b0f52d41bc5223bb015f456300878319c95c5e0f Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 15 Aug 2017 11:37:58 +0200 Subject: [PATCH 08/34] platform: fix handling rt_source/rtprot when comparing routes In several cases, does the route compare function a fuzzy match, to get the result as what would happen if you add that route to kernel. The rt_source enum contains some NetworkManager specific values which are mapped to a certain rtm_protocol value. Especially, when adding a route to kernel, the resulting value will be coerced (and end up being different). We must take this coercion into account. --- src/platform/nm-platform.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 252f69fac4..7ee463d00c 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -4743,7 +4743,7 @@ nm_platform_ip4_route_hash (const NMPlatformIP4Route *obj, NMPlatformIPRouteCmpT 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, nmp_utils_ip_config_source_round_trip_rtprot (obj->rt_source)); h = NM_HASH_COMBINE (h, obj->scope_inv); h = NM_HASH_COMBINE (h, obj->gateway); h = NM_HASH_COMBINE (h, obj->mss); @@ -4770,7 +4770,10 @@ 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)); + else + h = NM_HASH_COMBINE (h, obj->rt_source); h = NM_HASH_COMBINE (h, obj->mss); h = NM_HASH_COMBINE (h, obj->scope_inv); h = NM_HASH_COMBINE (h, obj->pref_src); @@ -4809,7 +4812,8 @@ nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route 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_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_FIELD (a, b, scope_inv); NM_CMP_FIELD (a, b, gateway); NM_CMP_FIELD (a, b, mss); @@ -4836,7 +4840,11 @@ 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)); + } else + NM_CMP_FIELD (a, b, rt_source); NM_CMP_FIELD (a, b, mss); NM_CMP_FIELD (a, b, scope_inv); NM_CMP_FIELD (a, b, pref_src); @@ -4897,11 +4905,12 @@ nm_platform_ip6_route_hash (const NMPlatformIP6Route *obj, NMPlatformIPRouteCmpT 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); @@ -4958,11 +4967,13 @@ nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route 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); From 2cd25d7a373e7422c6a14fb7d5827d19f8ec1851 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 14 Aug 2017 14:16:20 +0200 Subject: [PATCH 09/34] platform: fix constness of argument for nm_platform_lookup_all() ... and nm_platform_lookup_entry(). --- src/platform/nm-platform.c | 4 ++-- src/platform/nmp-object.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 7ee463d00c..c950c85cf6 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -2806,7 +2806,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 +2816,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, diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index 8bb6e96877..2e34266119 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, From abaaf3a69368360d8bd78c5160c959faa505911a Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 21 Aug 2017 14:35:04 +0200 Subject: [PATCH 10/34] platform/trivial: move code Trivial code (like this which only depends on an enum defined in a header file) should go first, so that the complex stuff is close together. --- src/platform/nm-platform.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index c950c85cf6..87967fcad5 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -240,6 +240,24 @@ NM_UTILS_LOOKUP_STR_DEFINE (_nm_platform_error_to_string, NMPlatformError, NM_UTILS_LOOKUP_ITEM_IGNORE (_NM_PLATFORM_ERROR_MININT), ); +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), +); + +#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 @@ -3453,24 +3471,6 @@ nm_platform_address_flush (NMPlatform *self, int ifindex) /*****************************************************************************/ -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), -); - -#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_ip4_route_add (NMPlatform *self, NMPNlmFlags flags, From 35ed678cc61a49ad7d48c1599723c7daf8218e2f Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 14 Aug 2017 14:17:00 +0200 Subject: [PATCH 11/34] platform: add nm_platform_ip_route_add() function --- src/platform/nm-platform.c | 64 +++++++++++++++++++++++++------------- src/platform/nm-platform.h | 3 ++ 2 files changed, 46 insertions(+), 21 deletions(-) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 87967fcad5..8e3896079f 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -3471,23 +3471,56 @@ nm_platform_address_flush (NMPlatform *self, int ifindex) /*****************************************************************************/ -gboolean -nm_platform_ip4_route_add (NMPlatform *self, - NMPNlmFlags flags, - const NMPlatformIP4Route *route) +static gboolean +_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); - g_return_val_if_fail (route, FALSE); - g_return_val_if_fail (route->plen <= 32, FALSE); + nm_assert (route); + nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); - _LOGD ("route: %-10s IPv4 route: %s", + _LOGD ("route: %-10s IPv%c route: %s", _nmp_nlm_flag_to_string (flags), - nm_platform_ip4_route_to_string (route, sbuf, sizeof (sbuf))); + 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, AF_INET, (const NMPlatformIPRoute *) route); + return klass->ip_route_add (self, flags, addr_family, route); +} + +gboolean +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)); +} + +gboolean +nm_platform_ip4_route_add (NMPlatform *self, + NMPNlmFlags flags, + const NMPlatformIP4Route *route) +{ + return _ip_route_add (self, flags, AF_INET, route); } gboolean @@ -3495,18 +3528,7 @@ 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 diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 6575634bed..574f2a4d0f 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -1112,6 +1112,9 @@ gboolean nm_platform_ip4_address_sync (NMPlatform *self, int ifindex, GPtrArray 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_route_add (NMPlatform *self, + NMPNlmFlags flags, + const NMPObject *route); 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); From 29bfe7c1fd8349ad948e269fe86854a7340232f9 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 14 Aug 2017 14:24:39 +0200 Subject: [PATCH 12/34] core: merge code block in nm_ip4_config_commit() by un-indenting --- src/nm-ip4-config.c | 109 +++++++++++++++++++++----------------------- src/nm-ip6-config.c | 27 +++++------ 2 files changed, 65 insertions(+), 71 deletions(-) diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 5e4cd7f59d..944853a0bd 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -660,6 +660,10 @@ nm_ip4_config_commit (const NMIP4Config *self, NMPlatform *platform, NMRouteMana { gs_unref_ptrarray GPtrArray *addresses = NULL; const NMDedupMultiHeadEntry *head_entry; + guint i; + gs_unref_array GArray *routes = NULL; + gs_unref_array GArray *device_route_purge_list = NULL; + const CList *iter; g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (self != NULL, FALSE); @@ -670,77 +674,70 @@ nm_ip4_config_commit (const NMIP4Config *self, NMPlatform *platform, NMRouteMana nm_platform_ip4_address_sync (platform, ifindex, addresses); /* Routes */ - { - guint i; - gs_unref_array GArray *routes = NULL; - gs_unref_array GArray *device_route_purge_list = NULL; - const CList *iter; + head_entry = nm_ip4_config_lookup_routes (self); - head_entry = nm_ip4_config_lookup_routes (self); + routes = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformIP4Route), head_entry ? head_entry->len : 0); - routes = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformIP4Route), head_entry ? head_entry->len : 0); + 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 }; - 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 }; + if (!o) + continue; - if (!o) - continue; + addr = NMP_OBJECT_CAST_IP4_ADDRESS (o); + if (addr->plen == 0) + continue; - addr = NMP_OBJECT_CAST_IP4_ADDRESS (o); - if (addr->plen == 0) - continue; + nm_assert (addr->plen <= 32); - nm_assert (addr->plen <= 32); + route.ifindex = ifindex; + route.rt_source = NM_IP_CONFIG_SOURCE_KERNEL; - route.ifindex = ifindex; - route.rt_source = NM_IP_CONFIG_SOURCE_KERNEL; + /* The destination network depends on the peer-address. */ + route.network = nm_utils_ip4_address_clear_host_address (addr->peer_address, addr->plen); - /* The destination network depends on the peer-address. */ - route.network = nm_utils_ip4_address_clear_host_address (addr->peer_address, addr->plen); + 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 (_ipv4_is_zeronet (route.network)) { - /* Kernel doesn't add device-routes for destinations that - * start with 0.x.y.z. Skip them. */ - continue; - } + route.plen = addr->plen; + route.pref_src = addr->address; + route.metric = default_route_metric; - route.plen = addr->plen; - route.pref_src = addr->address; - route.metric = default_route_metric; + g_array_append_val (routes, route); - g_array_append_val (routes, 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); - } + 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); } } - - 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; } + 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; } diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index a3d958a616..1c682b055b 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -523,6 +523,8 @@ nm_ip6_config_commit (const NMIP6Config *self, { gs_unref_ptrarray GPtrArray *addresses = NULL; const NMDedupMultiHeadEntry *head_entry; + gs_unref_array GArray *routes = NULL; + const CList *iter; g_return_val_if_fail (ifindex > 0, FALSE); g_return_val_if_fail (self != NULL, FALSE); @@ -534,26 +536,21 @@ nm_ip6_config_commit (const NMIP6Config *self, nm_platform_ip6_address_sync (platform, ifindex, addresses, TRUE); /* Routes */ - { - gs_unref_array GArray *routes = NULL; - const CList *iter; + head_entry = nm_ip6_config_lookup_routes (self); - head_entry = nm_ip6_config_lookup_routes (self); + routes = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformIP6Route), head_entry ? head_entry->len : 0); - 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 (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; } + if (!nm_route_manager_ip6_route_sync (route_manager, ifindex, routes, TRUE, routes_full_sync)) + return FALSE; + return TRUE; } From 936ebdc7246c998d62e4531926387e907f7e8f17 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 16 Aug 2017 14:38:39 +0200 Subject: [PATCH 13/34] core: consistently use _nm_ip_config_add_obj() when adding route/address to ip-config _nm_ip_config_add_obj() does some additional checking, like setting the ifindex. We shall not bypass this also during bulk-update (replace). Add options @merge and @append_force to make _nm_ip_config_add_obj() suitable in those cases too, and use it. --- src/nm-ip4-config.c | 119 +++++++++++++++++++++++++------------------- src/nm-ip4-config.h | 4 +- src/nm-ip6-config.c | 34 +++++++------ 3 files changed, 91 insertions(+), 66 deletions(-) diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index 944853a0bd..e94662107d 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -164,7 +164,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 +203,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 +275,13 @@ _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; } /*****************************************************************************/ @@ -1884,7 +1897,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); } @@ -2004,7 +2019,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); } diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h index e11043b79c..594dcc5bf2 100644 --- a/src/nm-ip4-config.h +++ b/src/nm-ip4-config.h @@ -92,7 +92,9 @@ 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); /*****************************************************************************/ diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index 1c682b055b..9fc4c3e2ff 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -1258,12 +1258,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); @@ -1302,12 +1303,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); @@ -1549,7 +1551,9 @@ _add_address (NMIP6Config *self, &priv->idx_ip6_addresses_, priv->ifindex, obj_new, - (const NMPlatformObject *) new)) + (const NMPlatformObject *) new, + TRUE, + FALSE)) _notify_addresses (self); } @@ -1727,7 +1731,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); } From a738a3446b5c2934572cd4280ee162038ba1b913 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 14 Aug 2017 14:52:55 +0200 Subject: [PATCH 14/34] platform: add typedef for NMPObjectPredicateFunc function pointer Otherwise, casting a function pointer is cumbersome. --- src/platform/nm-platform.c | 2 +- src/platform/nm-platform.h | 5 ++++- src/platform/nmp-object.h | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 8e3896079f..ba325ca883 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -2879,7 +2879,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), diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 574f2a4d0f..6e45b9c09a 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 @@ -917,7 +920,7 @@ gboolean nm_platform_lookup_predicate_routes_skip_rtprot_kernel (const NMPObject 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. */ diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index 2e34266119..6b4a28c25c 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -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; From 990a050affe44fb6498b7ba2762751147cf10d4d Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 14 Aug 2017 18:13:10 +0200 Subject: [PATCH 15/34] platform: cleanup and renaming of nm_platform_address_flush() function Rename to nm_platform_ip_address_flush(), it's more consistent with naming for other platform functions. Also, pass an address family argument. Sometimes I feel an option makes it clearer what the function does. Otherwise, from the name it's not clear which address families are affected. As an API, it feels more correct to me. We soon also get a nm_platform_ip_route_flush() function, which will look similar. --- src/devices/nm-device.c | 2 +- src/devices/wwan/nm-modem.c | 2 +- src/platform/nm-platform.c | 17 ++++++++++++++--- src/platform/nm-platform.h | 4 +++- src/vpn/nm-vpn-connection.c | 2 +- 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 006d4e9227..29b20f790a 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -12202,7 +12202,7 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason, CleanupType clean 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); + nm_platform_ip_address_flush (nm_device_get_platform (self), AF_UNSPEC, ifindex); } } diff --git a/src/devices/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c index 76df89e58b..b6bcc25fbb 100644 --- a/src/devices/wwan/nm-modem.c +++ b/src/devices/wwan/nm-modem.c @@ -1068,7 +1068,7 @@ deactivate_cleanup (NMModem *self, NMDevice *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_ip_address_flush (nm_device_get_platform (device), AF_UNSPEC, ifindex); nm_platform_link_set_down (nm_device_get_platform (device), ifindex); } } diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index ba325ca883..705e54fa5e 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -3461,12 +3461,23 @@ 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; } /*****************************************************************************/ diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 6e45b9c09a..ce5b56a6a9 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -1113,7 +1113,9 @@ 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_ip_route_add (NMPlatform *self, NMPNlmFlags flags, diff --git a/src/vpn/nm-vpn-connection.c b/src/vpn/nm-vpn-connection.c index 63692cffbc..912df081e4 100644 --- a/src/vpn/nm-vpn-connection.c +++ b/src/vpn/nm-vpn-connection.c @@ -396,7 +396,7 @@ vpn_cleanup (NMVpnConnection *self, NMDevice *parent_dev) 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); + nm_platform_ip_address_flush (nm_netns_get_platform (priv->netns), AF_UNSPEC, priv->ip_ifindex); } remove_parent_device_config (self, parent_dev); From 69a50a50531e2d8a8b6f679c25a13a5aedb5ea9e Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 17 Aug 2017 13:37:21 +0200 Subject: [PATCH 16/34] platform: add and use nm_platform_ip_route_normalize() Adding a route to kernel may coerce/mangle some properties. Add a function nm_platform_ip_route_normalize() to simulate these changes. --- src/platform/nm-fake-platform.c | 11 ++-------- src/platform/nm-linux-platform.c | 14 ++---------- src/platform/nm-platform.c | 37 ++++++++++++++++++++++++++++++++ src/platform/nm-platform.h | 3 +++ 4 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 694a6a4139..d51acce791 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -1230,24 +1230,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; diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 71d564bd23..38b5691136 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -5836,30 +5836,20 @@ 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 (); } + nm_platform_ip_route_normalize (addr_family, NMP_OBJECT_CAST_IP_ROUTE (&obj)); + nlmsg = _nl_msg_new_route (RTM_NEWROUTE, flags, &obj); if (!nlmsg) g_return_val_if_reached (FALSE); diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 705e54fa5e..f392537e2a 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -3482,6 +3482,43 @@ nm_platform_ip_address_flush (NMPlatform *self, /*****************************************************************************/ +/** + * 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 = nm_platform_route_scope_inv (!r4->gateway + ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE); + 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 gboolean _ip_route_add (NMPlatform *self, NMPNlmFlags flags, diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index ce5b56a6a9..315fcd5c5e 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -1117,6 +1117,9 @@ gboolean nm_platform_ip_address_flush (NMPlatform *self, int addr_family, int ifindex); +void nm_platform_ip_route_normalize (int addr_family, + NMPlatformIPRoute *route); + gboolean nm_platform_ip_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPObject *route); From 974ff6299633bf739a73a240797cd46ebcd0e043 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 17 Aug 2017 15:17:40 +0200 Subject: [PATCH 17/34] platform: fix handling rt_scope of IPv4 route Kernel does not allow to add an IPv4 route with rt_scope RT_SCOPE_NOWHERE (255). It would fail with EINVAL. While adding a route, we coerce/normalize the scope in nm_platform_ip_route_normalize(). However, that should only be done, if the scope is not explicitly set already. Otherwise, leave it unchanged. nm_platform_ip_route_normalize() is related to the compare functions. Several compare modes do a fuzzy comparison, and they should compare equal as if they would be normalized. Hence, we must do the same normalization there. One pecularity in NetworkManager is that we track scope as it's inverse. The reason is to have a default value of zero meaning RT_SCOPE_NOWHERE. Hence "scope_inv". --- src/platform/nm-platform.c | 43 ++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index f392537e2a..e906ba3bc6 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -3482,6 +3482,26 @@ nm_platform_ip_address_flush (NMPlatform *self, /*****************************************************************************/ +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 @@ -3503,8 +3523,7 @@ nm_platform_ip_route_normalize (int addr_family, 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 = nm_platform_route_scope_inv (!r4->gateway - ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE); + r4->scope_inv = _ip_route_scope_inv_get_normalized (r4); break; case AF_INET6: r6 = (NMPlatformIP6Route *) route; @@ -4814,7 +4833,7 @@ nm_platform_ip4_route_hash (const NMPlatformIP4Route *obj, NMPlatformIPRouteCmpT if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { h = NM_HASH_COMBINE (h, obj->ifindex); h = NM_HASH_COMBINE (h, nmp_utils_ip_config_source_round_trip_rtprot (obj->rt_source)); - h = NM_HASH_COMBINE (h, obj->scope_inv); + 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); @@ -4840,12 +4859,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); - if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) + 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)); - else + 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); @@ -4884,7 +4905,8 @@ nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route NM_CMP_FIELD (a, b, ifindex); 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_FIELD (a, b, scope_inv); + 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); @@ -4913,10 +4935,13 @@ nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route 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)); - } else + 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); From f0de7d347f3663c6706b05d462adb2f144f78ab3 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 14 Aug 2017 14:18:53 +0200 Subject: [PATCH 18/34] platform: add non-exclusive routes and drop route-manager Previously, we would add exclusive routes via netlink message flags NLM_F_CREATE | NLM_F_REPLACE for RTM_NEWROUTE. Similar to `ip route replace`. Using that form of RTM_NEWROUTE message, we could only add a certain route with a certain network/plen,metric triple once. That was already hugely inconvenient, because - when configuring routes, multiple (managed) interfaces may get conflicting routes (multihoming). Only one of the routes can be actually configured using `ip route replace`, so we need to track routes that are currently shadowed. - when configuring routes, we might replace externally configured routes on unmanaged interfaces. We should not interfere with such routes. That was worked around by having NMRouteManager (and NMDefaultRouteManager). NMRouteManager would keep a list of the routes which NetworkManager would like to configure, even if momentarily being unable to do so due to conflicting routes. This worked mostly well but was complicated. It involved bumping metrics to avoid conflicts for device routes, as we might require them for gateway routes. Drop that now. Instead, use the corresponding of `ip route append` to configure routes. This allows NetworkManager to confiure (almost) all routes that we care. Especially, it can configure all routes on a managed interface, without replacing/interfering with routes on other interfaces. Hence, NMRouteManager becomes obsolete. It practice it is a bit more complicated because: - when adding an IPv4 address, kernel will automatically create a device route for the subnet. We should avoid that by using the IFA_F_NOPREFIXROUTE flag for IPv4 addresses (still to-do). But as kernel may not support that flag for IPv4 addresses yet (and we don't require such a kernel yet), we still need functionality similar to nm_route_manager_ip4_route_register_device_route_purge_list(). This functionality is now handled via nm_platform_ip4_dev_route_blacklist_set(). - trying to configure an IPv6 route with a source address will be rejected by kernel as long as the address is tentative (see related bug rh#1457196). Preferably, NMDevice would keep the list of routes which should be configured, while kernel would have the list of what actually is configured. There is a feed-back loop where both affect each other (for example, when externally deleting a route, NMDevice must forget about it too). Previously, NMRouteManager would have the task of remembering all routes which we currently want to configure, but cannot due to conflicting routes. We get rid of that, because now we configure non-exclusive routes. We however still will need to remember IPv6 routes with a source address, that currently cannot be configured yet. Hence, we will need to keep track of routes that currently cannot be configured, but later may be. That is still not done yet, as NMRouteManager didn't handle this correctly either. --- Makefile.am | 27 - shared/nm-utils/nm-dedup-multi.c | 8 +- shared/nm-utils/nm-dedup-multi.h | 4 +- src/devices/nm-device.c | 216 +++-- src/devices/wwan/nm-modem.c | 10 +- src/nm-iface-helper.c | 15 +- src/nm-ip4-config.c | 179 +++- src/nm-ip4-config.h | 9 +- src/nm-ip6-config.c | 44 +- src/nm-ip6-config.h | 5 +- src/nm-netns.c | 10 - src/nm-netns.h | 1 - src/nm-route-manager.c | 1380 ------------------------------ src/nm-route-manager.h | 49 -- src/nm-types.h | 1 - src/platform/nm-platform.c | 466 +++++++++- src/platform/nm-platform.h | 21 +- src/tests/test-route-manager.c | 974 --------------------- src/vpn/nm-vpn-connection.c | 19 +- 19 files changed, 769 insertions(+), 2669 deletions(-) delete mode 100644 src/nm-route-manager.c delete mode 100644 src/nm-route-manager.h delete mode 100644 src/tests/test-route-manager.c 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/shared/nm-utils/nm-dedup-multi.c b/shared/nm-utils/nm-dedup-multi.c index 383e40fd72..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) { @@ -682,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) { @@ -708,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 c99d6d11e1..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); diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 29b20f790a..740f270fa2 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,99 @@ 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)) + 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)) + 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 +2882,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 +3922,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 @@ -5582,7 +5658,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 +5824,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 +6379,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 +6567,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; @@ -9853,46 +9919,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 +10086,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 +10113,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 +10949,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 +12159,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 +12248,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_ip_address_flush (nm_device_get_platform (self), AF_UNSPEC, 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) @@ -13851,9 +13911,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 +13951,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/wwan/nm-modem.c b/src/devices/wwan/nm-modem.c index b6bcc25fbb..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_ip_address_flush (nm_device_get_platform (device), AF_UNSPEC, 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/nm-iface-helper.c b/src/nm-iface-helper.c index 3b60e0d64f..c695d591f6 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) @@ -257,7 +254,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 e94662107d..606bb342fc 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" @@ -284,6 +283,48 @@ append_force_and_out: 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; +} + /*****************************************************************************/ NM_GOBJECT_PROPERTIES_DEFINE (NMIP4Config, @@ -352,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); /*****************************************************************************/ @@ -669,37 +713,41 @@ 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; - gs_unref_array GArray *routes = NULL; - gs_unref_array GArray *device_route_purge_list = NULL; - const CList *iter; + 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 */ - head_entry = nm_ip4_config_lookup_routes (self); - - routes = g_array_sized_new (FALSE, FALSE, sizeof (NMPlatformIP4Route), head_entry ? head_entry->len : 0); - - if ( default_route_metric >= 0 - && addresses) { + 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 NMRouteManager these routes are very much important. */ + * the routes. */ for (i = 0; i < addresses->len; i++) { const NMPObject *o = addresses->pdata[i]; const NMPlatformIP4Address *addr; - NMPlatformIP4Route route = { 0 }; + nm_auto_nmpobj NMPObject *r = NULL; + NMPlatformIP4Route *route; + in_addr_t network; if (!o) continue; @@ -710,48 +758,77 @@ nm_ip4_config_commit (const NMIP4Config *self, NMPlatform *platform, NMRouteMana nm_assert (addr->plen <= 32); - route.ifindex = ifindex; - route.rt_source = NM_IP_CONFIG_SOURCE_KERNEL; - /* The destination network depends on the peer-address. */ - route.network = nm_utils_ip4_address_clear_host_address (addr->peer_address, addr->plen); + network = nm_utils_ip4_address_clear_host_address (addr->peer_address, addr->plen); - if (_ipv4_is_zeronet (route.network)) { + if (_ipv4_is_zeronet (network)) { /* Kernel doesn't add device-routes for destinations that * start with 0.x.y.z. Skip them. */ continue; } - route.plen = addr->plen; - route.pref_src = addr->address; - route.metric = default_route_metric; + r = nmp_object_new (NMP_OBJECT_TYPE_IP4_ROUTE, NULL); + route = NMP_OBJECT_CAST_IP4_ROUTE (r); - g_array_append_val (routes, route); + 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); + + nm_platform_ip_route_normalize (AF_INET, (NMPlatformIPRoute *) route); + + 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)); + } 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_auto_nmpobj NMPObject *r_dev = NULL; + + r_dev = nmp_object_clone (r, FALSE); + route = NMP_OBJECT_CAST_IP4_ROUTE (r_dev); + route->metric = NM_PLATFORM_ROUTE_METRIC_IP4_DEVICE_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_platform_ip4_address_sync (platform, ifindex, addresses); - nm_route_manager_ip4_route_register_device_route_purge_list (route_manager, device_route_purge_list); + if (!nm_platform_ip_route_sync (platform, + AF_INET, + ifindex, + routes, + nm_platform_lookup_predicate_routes_skip_rtprot_kernel, + NULL)) + success = FALSE; - if (!nm_route_manager_ip4_route_sync (route_manager, ifindex, routes, - default_route_metric < 0, routes_full_sync)) - return FALSE; + nm_platform_ip4_dev_route_blacklist_set (platform, + ifindex, + ip4_dev_route_blacklist); - return TRUE; + return success; } static void @@ -1996,6 +2073,24 @@ nm_ip4_config_address_exists (const NMIP4Config *self, /*****************************************************************************/ +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) { diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h index 594dcc5bf2..ab8fa51d4d 100644 --- a/src/nm-ip4-config.h +++ b/src/nm-ip4-config.h @@ -96,6 +96,11 @@ gboolean _nm_ip_config_add_obj (NMDedupMultiIndex *multi_idx, 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); + /*****************************************************************************/ #define NM_TYPE_IP4_CONFIG (nm_ip4_config_get_type ()) @@ -137,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 9fc4c3e2ff..3d42fb7b8a 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -32,7 +32,6 @@ #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" @@ -516,42 +515,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_array GArray *routes = NULL; - const CList *iter; + 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 */ - head_entry = nm_ip6_config_lookup_routes (self); + if (!nm_platform_ip_route_sync (platform, + AF_INET6, + ifindex, + routes, + nm_platform_lookup_predicate_routes_skip_rtprot_kernel, + NULL)) + success = FALSE; - 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 diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h index f296224fab..6e7b112110 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); 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-platform.c b/src/platform/nm-platform.c index e906ba3bc6..3b471e2075 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -91,6 +91,9 @@ enum { typedef struct _NMPlatformPrivate { bool use_udev:1; bool log_with_ptr:1; + 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 +104,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) { @@ -3482,6 +3489,153 @@ nm_platform_ip_address_flush (NMPlatform *self, /*****************************************************************************/ +/** + * 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_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; + + 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 TRUE; + + for (i_type = 0; i_type < 2; i_type++) { + for (i = 0; i < routes->len; i++) { + 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 identical already, otherwise we would + * have deleted it in the previous step. */ + continue; + } + + if (!nm_platform_ip_route_add (self, + NMP_NLM_FLAG_APPEND, + conf_o)) { + /* ignore error adding route. */ + } + } + } + + return TRUE; +} + +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) { @@ -3616,6 +3770,274 @@ nm_platform_ip_route_delete (NMPlatform *self, /*****************************************************************************/ +#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 IPv6 + * addresses, 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, @@ -5235,6 +5657,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), @@ -5284,40 +5711,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) { @@ -5333,7 +5726,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, }; @@ -5344,7 +5736,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, }; @@ -5416,6 +5807,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 315fcd5c5e..112d894fed 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -63,6 +63,8 @@ typedef gboolean (*NMPObjectPredicateFunc) (const NMPObject *obj, #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. */ @@ -521,11 +523,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; @@ -1128,6 +1125,16 @@ gboolean nm_platform_ip6_route_add (NMPlatform *self, NMPNlmFlags flags, const N 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); + 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); @@ -1218,6 +1225,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/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 912df081e4..562867545e 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_ip_address_flush (nm_netns_get_platform (priv->netns), AF_UNSPEC, 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); @@ -1104,21 +1105,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; } From 48bc116c206e0102fd8a5a47f3482a6133980387 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 16 Aug 2017 11:58:40 +0200 Subject: [PATCH 19/34] platform/trivial: move code --- src/platform/nm-linux-platform.c | 86 ++++++++++++++++---------------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 38b5691136..46304715ba 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -290,9 +290,9 @@ 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)) @@ -321,6 +321,48 @@ _support_user_ipv6ll_detect (struct nlattr **tb) } } +/***************************************************************************** + * extended IFA_FLAGS support + *****************************************************************************/ + +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; +} + /****************************************************************** * Various utilities ******************************************************************/ @@ -2610,46 +2652,6 @@ 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 ******************************************************************/ From 8670aacc7ceee772c2cc022696feab2ea9bddd9c Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 16 Aug 2017 11:58:57 +0200 Subject: [PATCH 20/34] platform: cleanup detecting kernel support for IFA_FLAGS and IPv6LL - cache the result in NMPlatformPrivate. No need to call the virtual function every time. The result is not ever going to change. - if we are unable to detect support, assume support. Those features were added quite a while ago to kernel, we should default to "support". Note, that we detect support based on the presence of the absence of certain netlink flags. That means, we will still detect no support. The only moment when we actually use the fallback value, is when we didn't encounter an RTM_NEWADDR or AF_INET6-IFLA_AF_SPEC message yet, which would be very unusual, because we fill the cache initially and usually will have some addresses there. - for no strong reason, track "undetected" as numerical value zero, and "support"/"no-support" as 1/-1. We already did that previously for _support_user_ipv6ll, so this just unifies the implementations. The minor reason is that this puts @_support_user_ipv6ll to the BSS section and allows us to omit initializing priv->check_support_user_ipv6ll_cached in platforms constructor. - detect _support_kernel_extended_ifa_flags also based on IPv4 RTM_NEWADDR messages. Originally, extended flags were added for IPv6, and later to IPv4 as well. Once we see an IPv4 message with IFA_FLAGS, we know we have support. --- src/platform/nm-linux-platform.c | 76 +++++++++++++++++--------------- src/platform/nm-platform.c | 34 +++++++++----- 2 files changed, 63 insertions(+), 47 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 46304715ba..6d899d3592 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -297,60 +297,62 @@ wait_for_nl_response_to_string (WaitForNlResponseResult seq_result, char *buf, g 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 ()); + + 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; - } - 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]; - - _support_user_ipv6ll = supported ? 1 : -1; - _LOG2D ("kernel-support: IFLA_INET6_ADDR_GEN_MODE: %s", - supported ? "detected" : "not detected"); + _support_user_ipv6ll = 1; + _LOG2D ("kernel-support: IFLA_INET6_ADDR_GEN_MODE: %s", "failed to detect; assume support"); } + return _support_user_ipv6ll >= 0; } /***************************************************************************** * extended IFA_FLAGS support *****************************************************************************/ -static int _support_kernel_extended_ifa_flags = -1; +static int _support_kernel_extended_ifa_flags = 0; -#define _support_kernel_extended_ifa_flags_still_undecided() (G_UNLIKELY (_support_kernel_extended_ifa_flags == -1)) +#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; - if (!_support_kernel_extended_ifa_flags_still_undecided ()) - return; + nm_assert (_support_kernel_extended_ifa_flags_still_undecided ()); + nm_assert (msg); 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) + 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 (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"); + 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 @@ -360,7 +362,7 @@ _support_kernel_extended_ifa_flags_get (void) _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; + return _support_kernel_extended_ifa_flags >= 0; } /****************************************************************** @@ -978,7 +980,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])); @@ -2992,7 +2995,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 (); } @@ -3000,7 +3003,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 (); } @@ -3863,7 +3866,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) @@ -4423,15 +4427,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, diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 3b471e2075..2ee046f07a 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -91,6 +91,10 @@ 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; @@ -270,27 +274,35 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_nmp_nlm_flag_to_string_lookup, NMPNlmFlags, 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; } /** From 1b3a0208d9571c124fba662416092d57984ac453 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Thu, 17 Aug 2017 15:16:14 +0200 Subject: [PATCH 21/34] all,trivial: include kernel versions and release dates in comments This will make us stop worry how relevant are chunks of compat code with older kernels when deciding whether it's worth supporting/testing them. As if we actually were testing old kernels. --- src/nm-core-utils.c | 2 +- src/platform/nm-linux-platform.c | 8 ++++++-- src/platform/tests/test-link.c | 10 +++++----- 3 files changed, 12 insertions(+), 8 deletions(-) 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/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 6d899d3592..9826c035a8 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -304,6 +304,7 @@ _support_user_ipv6ll_detect (struct nlattr **tb) 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", @@ -348,8 +349,11 @@ _support_kernel_extended_ifa_flags_detect (struct nl_msg *msg) /* 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). - **/ + * 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"); 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)); From 057b63979e0c3c345321dbc1fdfc1068dd55cdca Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 16 Aug 2017 13:57:01 +0200 Subject: [PATCH 22/34] core: move setting NDisc addresses/routes to NMIP6Config Add an utility function for resetting addresses/routes of NMIP6Config from NMNDisc data. For one, this de-duplicates code in device and nm-iface-helper. Also, we no longer first reset (delete) all addresses and add them anew. Instead, we first mark all entries as dirty for deletion, merge (append) the new entires, and delete the remaining dirty entires. This saves a extra work, in the expected case where NMIP6Config already contains several of the new entries. --- src/devices/nm-device.c | 49 ++++--------------- src/ndisc/nm-ndisc.h | 10 ++-- src/nm-iface-helper.c | 50 ++++--------------- src/nm-ip6-config.c | 104 ++++++++++++++++++++++++++++++++++++++++ src/nm-ip6-config.h | 12 +++++ 5 files changed, 140 insertions(+), 85 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 740f270fa2..40d0b1d665 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -7420,49 +7420,18 @@ 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); - - /* 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; - - 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, + system_support ? 64 : 128, + 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) { 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-iface-helper.c b/src/nm-iface-helper.c index c695d591f6..ff422de66c 100644 --- a/src/nm-iface-helper.c +++ b/src/nm-iface-helper.c @@ -156,7 +156,6 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in NMIP6Config *existing; int system_support; guint32 ifa_flags = 0x00; - int i; /* * Check, whether kernel is recent enough, to help user space handling RA. @@ -194,49 +193,18 @@ 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); - - /* 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; - - 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, + system_support ? 64 : 128, + 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) { diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index 3d42fb7b8a..9f9e345637 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -35,6 +35,7 @@ #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" @@ -1189,6 +1190,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 */ @@ -1520,6 +1523,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) { @@ -1698,6 +1753,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) { diff --git a/src/nm-ip6-config.h b/src/nm-ip6-config.h index 6e7b112110..eb2aae170d 100644 --- a/src/nm-ip6-config.h +++ b/src/nm-ip6-config.h @@ -191,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 */ From 2f83894498c2c67851f58aedb7b89f8963586ce5 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 16 Aug 2017 14:50:01 +0200 Subject: [PATCH 23/34] core: cleanup setting ifa_flags in ndisc_config_changed() Some refactoring and one change: If we don't have system-support, don't set IFA_F_MANAGETEMPADDR. --- src/devices/nm-device.c | 40 +++++++++++++++++++--------------------- src/nm-iface-helper.c | 39 ++++++++++++++++++--------------------- 2 files changed, 37 insertions(+), 42 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 40d0b1d665..9581a64491 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -7385,26 +7385,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); @@ -7420,10 +7401,27 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in } if (changed & NM_NDISC_CONFIG_ADDRESSES) { + guint8 plen; + guint32 ifa_flags; + + /* 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; + nm_ip6_config_reset_addresses_ndisc (priv->ac_ip6_config, rdata->addresses, rdata->addresses_n, - system_support ? 64 : 128, + plen, ifa_flags); } diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c index ff422de66c..f7741f5736 100644 --- a/src/nm-iface-helper.c +++ b/src/nm-iface-helper.c @@ -154,26 +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; - - /* - * 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); @@ -193,10 +173,27 @@ ndisc_config_changed (NMNDisc *ndisc, const NMNDiscData *rdata, guint changed_in } if (changed & NM_NDISC_CONFIG_ADDRESSES) { + guint8 plen; + guint32 ifa_flags; + + /* 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; + nm_ip6_config_reset_addresses_ndisc (ndisc_config, rdata->addresses, rdata->addresses_n, - system_support ? 64 : 128, + plen, ifa_flags); } From fec80e7473ad16979af75ed299d68103e7aa3fe9 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 17 Aug 2017 17:20:14 +0200 Subject: [PATCH 24/34] platform: use IFA_F_NOPREFIXROUTE flag for IPv4 addresses For IPv6 addresses we use IFA_F_NOPREFIXROUTE for a long time. If we detect that kernel does not support the flag (for IPv6), we add addresses as /128 to prevent kernel from adding an onlink route. We add IPv6 device routes explicitly, whenever needed according to the onlink RA flag. For IPv4, we also don't want the route added by kernel. The reason is that is has an undesired metric of zero. However, usually we want the route to have a different metric. The complicated part is that kernel does not add the route immediately but sometimes later. For that we have nm_platform_ip4_dev_route_blacklist_set() (previously that was nm_route_manager_ip4_route_register_device_route_purge_list()). It watches the interface and when a registered device route shows up, it deletes it. The better solution is to use the IFA_F_NOPREFIXROUTE flag to prevent the creation of the route in the first place. It was added for IPv4 to kernel in commit 7b1311807f3d3eb8bef3ccc53127838b3bea3771, October 2015. Contrary to IPv6, we cannot (easily) detect whether kernel supports IFA_F_NOPREFIXROUTE for IPv4 routes. Hence keep nm_platform_ip4_dev_route_blacklist_set() for older kernels. --- src/platform/nm-platform.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 2ee046f07a..3f7446d745 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -3262,6 +3262,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); @@ -3380,6 +3381,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; @@ -3396,7 +3401,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; @@ -3950,8 +3956,8 @@ _ip4_dev_route_blacklist_schedule (NMPlatform *self) * @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 IPv6 - * addresses, we require kernel support for IFA_F_NOPREFIXROUTE and always + * 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 From 33a2a7c3e3738b184233980a66f0093f073f97b1 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 16 Aug 2017 16:13:24 +0200 Subject: [PATCH 25/34] platform: add nm_platform_ip_route_get() for route-lookup Inspired from iproute2. As such, don't use libnl3's "struct nl_msg", but add _nl_addattr_l() and use a stack-allocated "struct nlmsghdr". With this, we are closer to the raw netlink API. It really is simple enough. The complicated part of the patch is that we re-use the existing netlink socket for events. Hence, we must process the socket via our common event_handler_recvmsgs(). That also means, that we get the netlink response a few layers down the stack and have to return the result via DelayedActionWaitForNlResponseData. --- src/platform/nm-linux-platform.c | 297 +++++++++++++++++++++++++++---- src/platform/nm-platform.c | 50 ++++++ src/platform/nm-platform.h | 10 ++ src/platform/tests/test-route.c | 38 ++++ 4 files changed, 361 insertions(+), 34 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 9826c035a8..6c4b34a03c 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -826,6 +826,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) @@ -2663,12 +2688,23 @@ nla_put_failure: * 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 { @@ -3087,11 +3123,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 @@ -3153,9 +3190,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); @@ -3365,13 +3415,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, @@ -3656,30 +3708,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 @@ -3714,7 +3847,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 @@ -3776,7 +3909,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; } @@ -3806,13 +3939,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; } } } @@ -3860,6 +3992,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; @@ -3929,6 +4062,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, @@ -4030,7 +4187,7 @@ do_add_link_with_lookup (NMPlatform *platform, } } - 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, @@ -4081,7 +4238,7 @@ 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, @@ -4134,7 +4291,7 @@ do_delete_object (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-delete-%s[%s]: failure sending netlink request \"%s\" (%d)", NMP_OBJECT_GET_CLASS (obj_id)->obj_type_name, @@ -4191,7 +4348,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, @@ -5887,6 +6044,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 +6844,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 3f7446d745..211a0abc9f 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -3788,6 +3788,56 @@ 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]; + + _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)); + } 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)) diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 112d894fed..2bd6509c73 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -803,6 +803,11 @@ typedef struct { 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; @@ -1135,6 +1140,11 @@ 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); diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c index 3dac6e5758..a8ba384492 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) { @@ -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); } } From fa7fafe8fd649b016dc4d0e736db669f21e8578f Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Thu, 17 Aug 2017 18:52:49 +0200 Subject: [PATCH 26/34] vpn: resolve route to external VPN gateway from kernel When activating a route, we commonly need to add a route to the external VPN gateway, so that the (encrypted) data is not sent over the VPN itself. Currently, our VPN connections are rather strongly tied to their parent device. Maybe the shouldn't be, as VPN may happily support changing the route from one device/IP address to another. Anyway, our previous way to determine the gateway for the route was not great. Instead, ask kernel how to reach the gateway via (something like) `ip route get`. If kernel would route the packets to the gateway via our parent device, we take that gateway. If not, we keep our previous heuristic (which is probably wrong in this case). --- src/vpn/nm-vpn-connection.c | 129 ++++++++++++++++++++++++------------ 1 file changed, 86 insertions(+), 43 deletions(-) diff --git a/src/vpn/nm-vpn-connection.c b/src/vpn/nm-vpn-connection.c index 562867545e..11eb9a4952 100644 --- a/src/vpn/nm-vpn-connection.c +++ b/src/vpn/nm-vpn-connection.c @@ -705,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); @@ -755,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); } } @@ -763,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); @@ -803,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 * @@ -1068,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); From 774c8a811e74097641b84b47e4e6c5fe90440113 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 21 Aug 2017 15:33:57 +0200 Subject: [PATCH 27/34] platform: return failure reason from route-add and return only netlink response Let nm_platform_ip_route_add() and friends return an NMPlatformError failure reason. Also, do_add_addrroute() did not return the response from kernel. Instead, it determined success/failure based on the presence of the object in the cache. That is racy and does not allow to give a failure reason from kernel. Instead, determine success solely based on the netlink reply from kernel. The received errno shall be authorative, there is no need to second guess the response. There is a problem that netlink is not a reliable protocol. In case of receive buffer overflow, the response is lost and we don't know whether the command succeeded (it likely did). It's unclear how to fix that, but for now just return "unspecified" error. We probably avoid that already by having a huge buffer size. Also, downgrade the error message to level. is really for bugs only. --- src/nm-default-route-manager.c | 4 +-- src/platform/nm-fake-platform.c | 6 ++-- src/platform/nm-linux-platform.c | 54 +++++++++++++++++--------------- src/platform/nm-platform.c | 18 ++++++----- src/platform/nm-platform.h | 19 +++++------ src/platform/tests/test-common.c | 4 +-- src/platform/tests/test-route.c | 6 ++-- 7 files changed, 60 insertions(+), 51 deletions(-) diff --git a/src/nm-default-route-manager.c b/src/nm-default-route-manager.c index 66a94a05a4..1f6342ccce 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", diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index d51acce791..40877b321a 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, @@ -1284,7 +1284,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; } } @@ -1346,7 +1346,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 6c4b34a03c..7c874df23f 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) { @@ -4223,14 +4233,12 @@ do_add_link_with_lookup (NMPlatform *platform, return !!obj; } -static gboolean +static NMPlatformError do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg) { 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, @@ -4244,7 +4252,7 @@ do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg * 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); @@ -4253,30 +4261,26 @@ do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg * _NMLOG (seq_result == WAIT_FOR_NL_RESPONSE_RESULT_RESPONSE_OK ? 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 @@ -5910,7 +5914,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) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -5940,7 +5944,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) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -5995,7 +5999,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, @@ -6019,7 +6023,7 @@ ip_route_add (NMPlatform *platform, nlmsg = _nl_msg_new_route (RTM_NEWROUTE, flags, &obj); if (!nlmsg) - g_return_val_if_reached (FALSE); + g_return_val_if_reached (NM_PLATFORM_ERROR_BUG); return do_add_addrroute (platform, &obj, nlmsg); } diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 211a0abc9f..ae409b207b 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -248,6 +248,7 @@ 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), ); @@ -3597,6 +3598,8 @@ nm_platform_ip_route_sync (NMPlatform *self, 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 \ @@ -3621,9 +3624,10 @@ nm_platform_ip_route_sync (NMPlatform *self, continue; } - if (!nm_platform_ip_route_add (self, - NMP_NLM_FLAG_APPEND, - conf_o)) { + plerr = nm_platform_ip_route_add (self, + NMP_NLM_FLAG_APPEND, + conf_o); + if (plerr != NM_PLATFORM_ERROR_SUCCESS) { /* ignore error adding route. */ } } @@ -3710,7 +3714,7 @@ nm_platform_ip_route_normalize (int addr_family, } } -static gboolean +static NMPlatformError _ip_route_add (NMPlatform *self, NMPNlmFlags flags, int addr_family, @@ -3733,7 +3737,7 @@ _ip_route_add (NMPlatform *self, return klass->ip_route_add (self, flags, addr_family, route); } -gboolean +NMPlatformError nm_platform_ip_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPObject *route) @@ -3754,7 +3758,7 @@ nm_platform_ip_route_add (NMPlatform *self, return _ip_route_add (self, flags, addr_family, NMP_OBJECT_CAST_IP_ROUTE (route)); } -gboolean +NMPlatformError nm_platform_ip4_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP4Route *route) @@ -3762,7 +3766,7 @@ nm_platform_ip4_route_add (NMPlatform *self, return _ip_route_add (self, flags, AF_INET, route); } -gboolean +NMPlatformError nm_platform_ip6_route_add (NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP6Route *route) diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 2bd6509c73..dbd55b4dd0 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -158,6 +158,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) @@ -797,10 +798,10 @@ 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, @@ -1122,11 +1123,11 @@ gboolean nm_platform_ip_address_flush (NMPlatform *self, void nm_platform_ip_route_normalize (int addr_family, NMPlatformIPRoute *route); -gboolean nm_platform_ip_route_add (NMPlatform *self, - NMPNlmFlags flags, - const NMPObject *route); -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); +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); 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-route.c b/src/platform/tests/test-route.c index a8ba384492..f749d7514c 100644 --- a/src/platform/tests/test-route.c +++ b/src/platform/tests/test-route.c @@ -469,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); @@ -597,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) { @@ -703,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]; From 5a69b27a64a033eba20cace4fce1ed92d17b5736 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 21 Aug 2017 16:11:31 +0200 Subject: [PATCH 28/34] platform: let platform operations only consider kernel response Also downgrade logging messages to . An external condition should never be able to trigger an , and clearly there is always a external race that can cause a netlink command to fail. --- src/platform/nm-linux-platform.c | 49 +++++++------------------------- 1 file changed, 11 insertions(+), 38 deletions(-) diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 7c874df23f..5aa5b989bc 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -4183,26 +4183,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_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; } @@ -4212,25 +4199,18 @@ 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 NMPlatformError @@ -4289,9 +4269,8 @@ 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); @@ -4301,13 +4280,14 @@ do_delete_object (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg * 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)) @@ -4322,21 +4302,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 @@ -4397,7 +4370,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, From 8746dddda20de06b079d1378eed90c920298cbaf Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 21 Aug 2017 16:55:02 +0200 Subject: [PATCH 29/34] platform: assert for namespace in nm_platform_cache_update_emit_signal() --- src/platform/nm-platform.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index ae409b207b..8a6e99d9de 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -5694,6 +5694,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) { From b524d879f0e814c57be9063ee5e6ee273310a19d Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 21 Aug 2017 19:18:45 +0200 Subject: [PATCH 30/34] platform: pass string buffer to nm_platform_error_to_string() and print numeric errno Change the output of nm_platform_error_to_string() to print the numeric value. Also, accept a string buffer instead of using an alloca() allocated buffer. There is still a macro to provide the previous functionality, but it was ill-suited to call from inside a loop. --- src/devices/nm-device-bond.c | 2 +- src/devices/nm-device-bridge.c | 2 +- src/devices/nm-device-dummy.c | 2 +- src/devices/nm-device-infiniband.c | 4 +-- src/devices/nm-device-ip-tunnel.c | 8 ++--- src/devices/nm-device-macsec.c | 2 +- src/devices/nm-device-macvlan.c | 2 +- src/devices/nm-device-tun.c | 2 +- src/devices/nm-device-vlan.c | 2 +- src/devices/nm-device-vxlan.c | 2 +- src/devices/nm-device.c | 4 +-- src/devices/team/nm-device-team.c | 2 +- src/platform/nm-platform.c | 52 +++++++++++++++++++++++------- src/platform/nm-platform.h | 7 ++-- 14 files changed, 62 insertions(+), 31 deletions(-) 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 5aae3e7103..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; @@ -673,7 +673,7 @@ create_and_realize (NMDevice *device, "Failed to create SIT 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; @@ -699,7 +699,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; @@ -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 9581a64491..79738891b9 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -7729,7 +7729,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) { @@ -13397,7 +13397,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) { 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/platform/nm-platform.c b/src/platform/nm-platform.c index 8a6e99d9de..33cd02c55b 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -228,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"), @@ -252,6 +243,42 @@ NM_UTILS_LOOKUP_STR_DEFINE (_nm_platform_error_to_string, NMPlatformError, 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"), @@ -3801,6 +3828,7 @@ nm_platform_ip_route_get (NMPlatform *self, nm_auto_nmpobj NMPObject *route = NULL; NMPlatformError result; char buf[NM_UTILS_INET_ADDRSTRLEN]; + char buf_err[200]; _CHECK_SELF (self, klass, FALSE); @@ -3826,7 +3854,7 @@ nm_platform_ip_route_get (NMPlatform *self, _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)); + 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)); diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index dbd55b4dd0..91c856aabf 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -868,8 +868,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) From 8f65f96a4328d64eb4431ec3d8789419acc604c8 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 21 Aug 2017 18:02:08 +0200 Subject: [PATCH 31/34] platform: add option to suppress error message from nm_platform_ip_route_add() When an error happens, we want to print a better message. Avoid duplicate error messages by adding a flag to suppress logging in the lower layer. --- src/platform/nm-fake-platform.c | 2 ++ src/platform/nm-linux-platform.c | 23 +++++++++++++++-------- src/platform/nm-platform.c | 4 +++- src/platform/nm-platform.h | 9 +++++++++ 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 40877b321a..1512b4f090 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -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); diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 5aa5b989bc..03585ab0d6 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -2585,7 +2585,7 @@ 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; @@ -2615,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); @@ -4214,7 +4213,10 @@ do_add_link_with_lookup (NMPlatform *platform, } static NMPlatformError -do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg *nlmsg) +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; @@ -4239,7 +4241,9 @@ do_add_addrroute (NMPlatform *platform, const NMPObject *obj_id, struct nl_msg * 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_WARN, "do-add-%s[%s]: %s", @@ -5887,7 +5891,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) == NM_PLATFORM_ERROR_SUCCESS; + return do_add_addrroute (platform, &obj_id, nlmsg, FALSE) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -5917,7 +5921,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) == NM_PLATFORM_ERROR_SUCCESS; + return do_add_addrroute (platform, &obj_id, nlmsg, FALSE) == NM_PLATFORM_ERROR_SUCCESS; } static gboolean @@ -5994,10 +5998,13 @@ ip_route_add (NMPlatform *platform, nm_platform_ip_route_normalize (addr_family, NMP_OBJECT_CAST_IP_ROUTE (&obj)); - nlmsg = _nl_msg_new_route (RTM_NEWROUTE, flags, &obj); + nlmsg = _nl_msg_new_route (RTM_NEWROUTE, flags & NMP_NLM_FLAG_FMASK, &obj); if (!nlmsg) g_return_val_if_reached (NM_PLATFORM_ERROR_BUG); - return do_add_addrroute (platform, &obj, nlmsg); + return do_add_addrroute (platform, + &obj, + nlmsg, + NM_FLAGS_HAS (flags, NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE)); } static gboolean diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 33cd02c55b..06e2c5c70a 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -288,6 +288,8 @@ NM_UTILS_LOOKUP_STR_DEFINE_STATIC (_nmp_nlm_flag_to_string_lookup, NMPNlmFlags, 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) \ @@ -3755,7 +3757,7 @@ _ip_route_add (NMPlatform *self, 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_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)) diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index 91c856aabf..d75b9143c0 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -86,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. */ From 538a0dd2dc6da24bf068ea36aa95455ecb787d30 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 21 Aug 2017 19:03:35 +0200 Subject: [PATCH 32/34] platform: report error when configuring manual route Rework to use nm_platform_ip_route_sync() broke to fail activation when we were unable to configure a route. Fix it. As before, we only do this for routes that are configured manually by the user. Invalid routes from DHCP do not break activation. Also, improve logging to give a hint what's wrong. --- src/platform/nm-platform.c | 65 ++++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 06e2c5c70a..665c5576a0 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -3569,6 +3569,10 @@ nm_platform_ip_route_sync (NMPlatform *self, 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]; nm_assert (NM_IS_PLATFORM (self)); nm_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); @@ -3623,7 +3627,7 @@ nm_platform_ip_route_sync (NMPlatform *self, } if (!routes) - return TRUE; + return success; for (i_type = 0; i_type < 2; i_type++) { for (i = 0; i < routes->len; i++) { @@ -3648,21 +3652,70 @@ nm_platform_ip_route_sync (NMPlatform *self, conf_o); if (plat_entry) { /* we alreay have a route with the same ID in the platform cache. - * Skip adding it again. It should identical already, otherwise we would - * have deleted it in the previous step. */ + * 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_APPEND + | NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE, conf_o); if (plerr != NM_PLATFORM_ERROR_SUCCESS) { - /* ignore error adding route. */ + 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 TRUE; + return success; } gboolean From 10ac675299cb3d734ae9bb720919dff1d4f1abbf Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 21 Aug 2017 23:17:12 +0200 Subject: [PATCH 33/34] platform: add support for routing tables to platform cache The upper layers still ignore all routes outside the main table. For now, just add support to NMPlatform. --- src/devices/nm-device.c | 12 +++++++----- src/nm-default-route-manager.c | 4 ++-- src/nm-ip4-config.c | 2 +- src/nm-ip6-config.c | 2 +- src/platform/nm-linux-platform.c | 9 ++++++--- src/platform/nm-platform.c | 26 +++++++++++++++++++++++++- src/platform/nm-platform.h | 30 ++++++++++++++++++++++++++++++ src/platform/tests/test-common.h | 4 ++-- 8 files changed, 74 insertions(+), 15 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 79738891b9..b7ff53d406 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -2825,7 +2825,8 @@ _v4_has_shadowed_routes_detect (NMDevice *self) nm_assert (r->ifindex == ifindex); - if (NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r)) + if ( NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r) + || r->table_coerced) continue; d = &data_arr[data_len++]; @@ -2845,7 +2846,8 @@ _v4_has_shadowed_routes_detect (NMDevice *self) IP4RPFilterData d; if ( r->ifindex == ifindex - || NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r)) + || NM_PLATFORM_IP_ROUTE_IS_DEFAULT (r) + || r->table_coerced) continue; d.network = nm_utils_ip4_address_clear_host_address (r->network, r->plen); @@ -5537,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 */ diff --git a/src/nm-default-route-manager.c b/src/nm-default-route-manager.c index 1f6342ccce..ea0eeeaa63 100644 --- a/src/nm-default-route-manager.c +++ b/src/nm-default-route-manager.c @@ -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-ip4-config.c b/src/nm-ip4-config.c index 606bb342fc..cd3b9a8c14 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -820,7 +820,7 @@ nm_ip4_config_commit (const NMIP4Config *self, AF_INET, ifindex, routes, - nm_platform_lookup_predicate_routes_skip_rtprot_kernel, + nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel, NULL)) success = FALSE; diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index 9f9e345637..d95398f6bb 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -538,7 +538,7 @@ nm_ip6_config_commit (const NMIP6Config *self, AF_INET6, ifindex, routes, - nm_platform_lookup_predicate_routes_skip_rtprot_kernel, + nm_platform_lookup_predicate_routes_main_skip_rtprot_kernel, NULL)) success = FALSE; diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index 03585ab0d6..4ca3d94580 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -2030,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; /*****************************************************************/ @@ -2148,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)) @@ -2592,12 +2591,13 @@ _nl_msg_new_route (int nlmsg_type, 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) @@ -2638,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); diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index 665c5576a0..ba2cf7a76b 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -2898,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) @@ -3547,7 +3557,7 @@ nm_platform_ip_address_flush (NMPlatform *self, * @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_skip_rtprot_kernel, + * 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. * @@ -4858,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]; @@ -4871,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" @@ -4887,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, @@ -4925,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]; @@ -4942,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" @@ -4957,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, @@ -5403,6 +5419,7 @@ 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); @@ -5428,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)); @@ -5474,6 +5492,7 @@ 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); @@ -5501,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)); @@ -5550,6 +5570,7 @@ 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, nm_utils_ip6_route_metric_normalize (obj->metric)); @@ -5562,6 +5583,7 @@ 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); @@ -5612,6 +5634,7 @@ 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_DIRECT (nm_utils_ip6_route_metric_normalize (a->metric), nm_utils_ip6_route_metric_normalize (b->metric)); @@ -5624,6 +5647,7 @@ 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)); diff --git a/src/platform/nm-platform.h b/src/platform/nm-platform.h index d75b9143c0..e14c71512d 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -424,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*/ @@ -852,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. @@ -930,6 +958,8 @@ 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); 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); } From e470e1392242a088573c4fc317b7b5ffaca4df19 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 23 Aug 2017 14:43:15 +0200 Subject: [PATCH 34/34] core: don't suppress host route to gateway in ip-config caputure Why would we do this? The route is there, so, add it. This revises commit 4fba2260f3e14a13f37499bd46ffcb220fdc5a4c which added this check for matching generated connections. I don't think this is still necessary, and if it is, then the matching should be relaxed instead. It's bad to hide routes from NMIP4Config/NMIP6Config, because those routes are also exported via D-Bus. --- src/nm-ip4-config.c | 15 +++++---------- src/nm-ip6-config.c | 14 +++++--------- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c index cd3b9a8c14..ca35d5b92a 100644 --- a/src/nm-ip4-config.c +++ b/src/nm-ip4-config.c @@ -681,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); } @@ -928,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); diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c index d95398f6bb..b2eccf3767 100644 --- a/src/nm-ip6-config.c +++ b/src/nm-ip6-config.c @@ -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); } @@ -656,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);