diff --git a/shared/nm-utils/nm-dedup-multi.h b/shared/nm-utils/nm-dedup-multi.h index ee74da10c..b6cfc4320 100644 --- a/shared/nm-utils/nm-dedup-multi.h +++ b/shared/nm-utils/nm-dedup-multi.h @@ -370,6 +370,30 @@ GPtrArray *nm_dedup_multi_objs_to_ptr_array_head (const NMDedupMultiHeadEntry *h NMDedupMultiFcnSelectPredicate predicate, gpointer user_data); +static inline const NMDedupMultiEntry * +nm_dedup_multi_head_entry_get_idx (const NMDedupMultiHeadEntry *head_entry, + int idx) +{ + CList *iter; + + if (head_entry) { + if (idx >= 0) { + c_list_for_each (iter, &head_entry->lst_entries_head) { + if (idx-- == 0) + return c_list_entry (iter, NMDedupMultiEntry, lst_entries); + } + } else { + for (iter = head_entry->lst_entries_head.prev; + iter != &head_entry->lst_entries_head; + iter = iter->prev) { + if (++idx == 0) + return c_list_entry (iter, NMDedupMultiEntry, lst_entries); + } + } + } + return NULL; +} + static inline void nm_dedup_multi_head_entry_sort (const NMDedupMultiHeadEntry *head_entry, CListSortCmp cmp, diff --git a/src/platform/nm-fake-platform.c b/src/platform/nm-fake-platform.c index 9adf2b43e..694a6a413 100644 --- a/src/platform/nm-fake-platform.c +++ b/src/platform/nm-fake-platform.c @@ -394,6 +394,7 @@ link_delete (NMPlatform *platform, int ifindex) cache_op = nmp_cache_remove (nm_platform_get_cache (platform), obj_old, FALSE, + FALSE, &obj_old2); g_assert (cache_op == NMP_CACHE_OPS_REMOVED); g_assert (obj_old2); @@ -1085,6 +1086,7 @@ ipx_address_delete (NMPlatform *platform, if (nmp_cache_remove (nm_platform_get_cache (platform), o, TRUE, + FALSE, &obj_old) != NMP_CACHE_OPS_REMOVED) g_assert_not_reached (); g_assert (obj_old); @@ -1171,6 +1173,7 @@ ipx_route_delete (NMPlatform *platform, if (nmp_cache_remove (nm_platform_get_cache (platform), o, TRUE, + FALSE, &obj_old) != NMP_CACHE_OPS_REMOVED) g_assert_not_reached (); g_assert (obj_old); @@ -1204,15 +1207,19 @@ ip_route_add (NMPlatform *platform, { NMDedupMultiIter iter; nm_auto_nmpobj NMPObject *obj = NULL; - NMPlatformIPRoute *rt; NMPCacheOpsType cache_op; const NMPObject *o = NULL; nm_auto_nmpobj const NMPObject *obj_old = NULL; nm_auto_nmpobj const NMPObject *obj_new = NULL; + nm_auto_nmpobj const NMPObject *obj_replace = NULL; NMPCache *cache = nm_platform_get_cache (platform); gboolean has_gateway = FALSE; - NMPlatformIP4Route *rt4 = NULL; - NMPlatformIP6Route *rt6 = NULL; + NMPlatformIPRoute *r = NULL; + NMPlatformIP4Route *r4 = NULL; + NMPlatformIP6Route *r6 = NULL; + gboolean has_same_weak_id; + gboolean only_dirty; + guint16 nlmsgflags; g_assert (NM_IN_SET (addr_family, AF_INET, AF_INET6)); @@ -1223,21 +1230,29 @@ ip_route_add (NMPlatform *platform, ? NMP_OBJECT_TYPE_IP4_ROUTE : NMP_OBJECT_TYPE_IP6_ROUTE, (const NMPlatformObject *) route); - rt = &obj->ip_route; - rt->rt_source = nmp_utils_ip_config_source_round_trip_rtprot (rt->rt_source); + r = &obj->ip_route; - if (addr_family == AF_INET) { - rt4 = NMP_OBJECT_CAST_IP4_ROUTE (obj); - rt4->scope_inv = nm_platform_route_scope_inv (rt4->gateway ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK); - rt4->network = nm_utils_ip4_address_clear_host_address (rt4->network, rt4->plen); - if (rt4->gateway) + 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; - } else { - rt6 = NMP_OBJECT_CAST_IP6_ROUTE (obj); - rt->metric = nm_utils_ip6_route_metric_normalize (rt->metric); - nm_utils_ip6_address_clear_host_address (&rt6->network, &rt6->network, rt->plen); - if (!IN6_IS_ADDR_UNSPECIFIED (&rt6->gateway)) + 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; + default: + nm_assert_not_reached (); } if (has_gateway) { @@ -1251,9 +1266,9 @@ ip_route_add (NMPlatform *platform, if (addr_family == AF_INET) { const NMPlatformIP4Route *item = NMP_OBJECT_CAST_IP4_ROUTE (o); guint32 n = nm_utils_ip4_address_clear_host_address (item->network, item->plen); - guint32 g = nm_utils_ip4_address_clear_host_address (rt4->gateway, item->plen); + guint32 g = nm_utils_ip4_address_clear_host_address (r4->gateway, item->plen); - if ( rt->ifindex == item->ifindex + if ( r->ifindex == item->ifindex && n == g) { has_route_to_gw = TRUE; break; @@ -1261,8 +1276,8 @@ ip_route_add (NMPlatform *platform, } else { const NMPlatformIP6Route *item = NMP_OBJECT_CAST_IP6_ROUTE (o); - if ( rt->ifindex == item->ifindex - && nm_utils_ip6_address_same_prefix (&rt6->gateway, &item->network, item->plen)) { + if ( r->ifindex == item->ifindex + && nm_utils_ip6_address_same_prefix (&r6->gateway, &item->network, item->plen)) { has_route_to_gw = TRUE; break; } @@ -1271,17 +1286,73 @@ ip_route_add (NMPlatform *platform, if (!has_route_to_gw) { if (addr_family == AF_INET) { nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip4-route '%d: %s/%d %d': Network Unreachable", - rt->ifindex, nm_utils_inet4_ntop (rt4->network, NULL), rt->plen, rt->metric); + r->ifindex, nm_utils_inet4_ntop (r4->network, NULL), r->plen, r->metric); } else { nm_log_warn (LOGD_PLATFORM, "Fake platform: failure adding ip6-route '%d: %s/%d %d': Network Unreachable", - rt->ifindex, nm_utils_inet6_ntop (&rt6->network, NULL), rt->plen, rt->metric); + r->ifindex, nm_utils_inet6_ntop (&r6->network, NULL), r->plen, r->metric); } return FALSE; } } - cache_op = nmp_cache_update_netlink (cache, obj, FALSE, &obj_old, &obj_new); - nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); + has_same_weak_id = FALSE; + nmp_cache_iter_for_each (&iter, + nm_platform_lookup_all (platform, + NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, + obj), + &o) { + if (addr_family == AF_INET) { + if (nm_platform_ip4_route_cmp (NMP_OBJECT_CAST_IP4_ROUTE (o), r4, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0) + continue; + } else { + if (nm_platform_ip6_route_cmp (NMP_OBJECT_CAST_IP6_ROUTE (o), r6, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0) + continue; + } + has_same_weak_id = TRUE; + } + + nlmsgflags = 0; + if (has_same_weak_id) { + switch (flags) { + case NMP_NLM_FLAG_REPLACE: + nlmsgflags = NLM_F_REPLACE; + break; + default: + g_assert_not_reached (); + break; + } + } + + /* we manipulate the cache the same was as NMLinuxPlatform does it. */ + cache_op = nmp_cache_update_netlink_route (cache, + obj, + FALSE, + nlmsgflags, + &obj_old, + &obj_new, + &obj_replace, + NULL); + only_dirty = FALSE; + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + if (obj_replace) { + const NMDedupMultiEntry *entry_replace; + + entry_replace = nmp_cache_lookup_entry (cache, obj_replace); + nm_assert (entry_replace && entry_replace->obj == obj_replace); + nm_dedup_multi_entry_set_dirty (entry_replace, TRUE); + only_dirty = TRUE; + } + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); + } + + if (obj_replace) { + cache_op = nmp_cache_remove (cache, obj_replace, TRUE, only_dirty, NULL); + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + nm_assert (cache_op == NMP_CACHE_OPS_REMOVED); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_replace, NULL); + } + } + return TRUE; } diff --git a/src/platform/nm-linux-platform.c b/src/platform/nm-linux-platform.c index e62d98ea5..865328a4e 100644 --- a/src/platform/nm-linux-platform.c +++ b/src/platform/nm-linux-platform.c @@ -3392,7 +3392,7 @@ cache_prune_one_type (NMPlatform *platform, NMPObjectType obj_type) obj = iter.current->obj; _LOGt ("cache-prune: prune %s", nmp_object_to_string (obj, NMP_OBJECT_TO_STRING_ALL, NULL, 0)); - cache_op = nmp_cache_remove (cache, obj, TRUE, &obj_old); + cache_op = nmp_cache_remove (cache, obj, TRUE, TRUE, &obj_old); nm_assert (cache_op == NMP_CACHE_OPS_REMOVED); cache_on_change (platform, cache_op, obj_old, NULL); nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, NULL); @@ -3645,39 +3645,6 @@ cache_on_change (NMPlatform *platform, } } -static void -cache_post (NMPlatform *platform, - struct nlmsghdr *msghdr, - NMPCacheOpsType cache_op, - const NMPObject *obj, - const NMPObject *obj_old, - const NMPObject *obj_new) -{ - if (msghdr->nlmsg_type == RTM_NEWROUTE) { - DelayedActionType action_type; - - action_type = NMP_OBJECT_GET_TYPE (obj) == NMP_OBJECT_TYPE_IP4_ROUTE - ? DELAYED_ACTION_TYPE_REFRESH_ALL_IP4_ROUTES - : DELAYED_ACTION_TYPE_REFRESH_ALL_IP6_ROUTES; - if ( !delayed_action_refresh_all_in_progress (platform, action_type) - && nmp_cache_find_other_route_for_same_destination (nm_platform_get_cache (platform), obj)) { - /* via `iproute route change` the user can update an existing route which effectively - * means that a new object (with a different ID) comes into existance, replacing the - * old on. In other words, as the ID of the object changes, we really see a new - * object with the old one deleted. - * However, kernel decides not to send a RTM_DELROUTE event for that. - * - * To hack around that, check if the update leaves us with multiple routes for the - * same network/plen,metric part. In that case, we cannot do better then requesting - * all routes anew, which sucks. - * - * One mitigation to avoid a dump is only to request a new dump, if we are not in - * the middle of an ongoing dump (delayed_action_refresh_all_in_progress). */ - delayed_action_schedule (platform, action_type, NULL); - } - } -} - /*****************************************************************************/ static int @@ -3939,16 +3906,73 @@ event_valid_msg (NMPlatform *platform, struct nl_msg *msg, gboolean handle_event case RTM_NEWLINK: case RTM_NEWADDR: - case RTM_NEWROUTE: case RTM_GETLINK: cache_op = nmp_cache_update_netlink (cache, obj, is_dump, &obj_old, &obj_new); if (cache_op != NMP_CACHE_OPS_UNCHANGED) { cache_on_change (platform, cache_op, obj_old, obj_new); - cache_post (platform, msghdr, cache_op, obj, obj_old, obj_new); nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); } break; + case RTM_NEWROUTE: { + nm_auto_nmpobj const NMPObject *obj_replace = NULL; + gboolean resync_required = FALSE; + gboolean only_dirty = FALSE; + + cache_op = nmp_cache_update_netlink_route (cache, + obj, + is_dump, + msghdr->nlmsg_flags, + &obj_old, + &obj_new, + &obj_replace, + &resync_required); + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + if (obj_replace) { + const NMDedupMultiEntry *entry_replace; + + /* we found an object that is to be replaced by the RTM_NEWROUTE message. + * While we invoke the signal, the platform cache might change and invalidate + * the findings. Mitigate that (for the most part), by marking the entry as + * dirty and only delete @obj_replace if it is still dirty afterwards. + * + * Yes, there is a tiny tiny chance for still getting it wrong. But in practice, + * the signal handlers do not cause to call the platform again, so the cache + * is not really changing. -- if they would, it would anyway be dangerous to overflow + * the stack and it's not ensured that the processing of netlink messages is + * reentrant (maybe it is). + */ + entry_replace = nmp_cache_lookup_entry (cache, obj_replace); + nm_assert (entry_replace && entry_replace->obj == obj_replace); + nm_dedup_multi_entry_set_dirty (entry_replace, TRUE); + only_dirty = TRUE; + } + cache_on_change (platform, cache_op, obj_old, obj_new); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_old, obj_new); + } + + if (obj_replace) { + /* the RTM_NEWROUTE message indicates that another route was replaced. + * Remove it now. */ + cache_op = nmp_cache_remove (cache, obj_replace, TRUE, only_dirty, NULL); + if (cache_op != NMP_CACHE_OPS_UNCHANGED) { + nm_assert (cache_op == NMP_CACHE_OPS_REMOVED); + cache_on_change (platform, cache_op, obj_replace, NULL); + nm_platform_cache_update_emit_signal (platform, cache_op, obj_replace, NULL); + } + } + + if (resync_required) { + /* we'd like to avoid such resyncs as they are expensive and we should only rely on the + * netlink events. This needs investigation. */ + _LOGT ("schedule resync of routes after RTM_NEWROUTE"); + delayed_action_schedule (platform, + delayed_action_refresh_from_object_type (NMP_OBJECT_GET_TYPE (obj)), + NULL); + } + break; + } + case RTM_DELLINK: case RTM_DELADDR: case RTM_DELROUTE: @@ -5829,6 +5853,7 @@ ip_route_add (NMPlatform *platform, 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: diff --git a/src/platform/nm-platform.c b/src/platform/nm-platform.c index d01b957f3..4f0ab9ac4 100644 --- a/src/platform/nm-platform.c +++ b/src/platform/nm-platform.c @@ -2803,6 +2803,26 @@ 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) +{ + return nmp_cache_lookup_all (nm_platform_get_cache (platform), + cache_id_type, + obj); +} + +const NMDedupMultiEntry * +nm_platform_lookup_entry (NMPlatform *platform, + NMPCacheIdType cache_id_type, + NMPObject *obj) +{ + return nmp_cache_lookup_entry_with_idx_type (nm_platform_get_cache (platform), + cache_id_type, + obj); +} + const NMDedupMultiHeadEntry * nm_platform_lookup (NMPlatform *self, const NMPLookup *lookup) @@ -3505,38 +3525,6 @@ nm_platform_ip_route_delete (NMPlatform *self, return klass->ip_route_delete (self, obj); } -const NMPlatformIP4Route * -nm_platform_ip4_route_get (NMPlatform *self, int ifindex, in_addr_t network, guint8 plen, guint32 metric) -{ - NMPObject obj_id; - const NMPObject *obj; - - _CHECK_SELF (self, klass, FALSE); - - nmp_object_stackinit_id_ip4_route (&obj_id, ifindex, network, plen, metric); - obj = nmp_cache_lookup_obj (nm_platform_get_cache (self), &obj_id); - if (nmp_object_is_visible (obj)) - return &obj->ip4_route; - return NULL; -} - -const NMPlatformIP6Route * -nm_platform_ip6_route_get (NMPlatform *self, int ifindex, struct in6_addr network, guint8 plen, guint32 metric) -{ - NMPObject obj_id; - const NMPObject *obj; - - _CHECK_SELF (self, klass, FALSE); - - metric = nm_utils_ip6_route_metric_normalize (metric); - - nmp_object_stackinit_id_ip6_route (&obj_id, ifindex, &network, plen, metric); - obj = nmp_cache_lookup_obj (nm_platform_get_cache (self), &obj_id); - if (nmp_object_is_visible (obj)) - return &obj->ip6_route; - return NULL; -} - /*****************************************************************************/ const char * @@ -4747,12 +4735,6 @@ nm_platform_ip4_route_hash (const NMPlatformIP4Route *obj, NMPlatformIPRouteCmpT h = NM_HASH_COMBINE (h, nm_utils_ip4_address_clear_host_address (obj->network, obj->plen)); h = NM_HASH_COMBINE (h, obj->plen); break; - case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID_CACHE: - h = NM_HASH_COMBINE (h, nm_utils_ip4_address_clear_host_address (obj->network, obj->plen)); - h = NM_HASH_COMBINE (h, obj->plen); - h = NM_HASH_COMBINE (h, obj->metric); - h = NM_HASH_COMBINE (h, obj->ifindex); - break; case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: h = NM_HASH_COMBINE (h, nm_utils_ip4_address_clear_host_address (obj->network, obj->plen)); @@ -4819,12 +4801,6 @@ nm_platform_ip4_route_cmp (const NMPlatformIP4Route *a, const NMPlatformIP4Route NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX (a->network, b->network, MIN (a->plen, b->plen)); NM_CMP_FIELD (a, b, plen); break; - case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID_CACHE: - NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX (a->network, b->network, MIN (a->plen, b->plen)); - NM_CMP_FIELD (a, b, plen); - NM_CMP_FIELD (a, b, metric); - NM_CMP_FIELD (a, b, ifindex); - break; case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: NM_CMP_DIRECT_IN4ADDR_SAME_PREFIX (a->network, b->network, MIN (a->plen, b->plen)); @@ -4892,12 +4868,6 @@ nm_platform_ip6_route_hash (const NMPlatformIP6Route *obj, NMPlatformIPRouteCmpT h = NM_HASH_COMBINE_IN6ADDR_PREFIX (h, &obj->network, obj->plen); h = NM_HASH_COMBINE (h, obj->plen); break; - case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID_CACHE: - 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, obj->ifindex); - break; case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: h = NM_HASH_COMBINE_IN6ADDR_PREFIX (h, &obj->network, obj->plen); @@ -4956,12 +4926,6 @@ nm_platform_ip6_route_cmp (const NMPlatformIP6Route *a, const NMPlatformIP6Route NM_CMP_DIRECT_IN6ADDR_SAME_PREFIX (&a->network, &b->network, MIN (a->plen, b->plen)); NM_CMP_FIELD (a, b, plen); break; - case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID_CACHE: - 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_FIELD (a, b, ifindex); - break; case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: 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 8e922b95e..841c5e77a 100644 --- a/src/platform/nm-platform.h +++ b/src/platform/nm-platform.h @@ -115,11 +115,6 @@ typedef enum { */ NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, - /* FIXME: this type is what NMPCache currently uses for object identity. - * Eventually, we want to use NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, - * which is the same what kernel does. */ - NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID_CACHE, - /* NMIP4Config and NMIP6Config also track a list of routes. They have their * own notion of what equality means. Basically, they consider network/plen * for IPv4 and IPv6. */ @@ -1102,9 +1097,6 @@ 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); -const NMPlatformIP4Route *nm_platform_ip4_route_get (NMPlatform *self, int ifindex, in_addr_t network, guint8 plen, guint32 metric); -const NMPlatformIP6Route *nm_platform_ip6_route_get (NMPlatform *self, int ifindex, struct in6_addr network, guint8 plen, guint32 metric); - 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); diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index 15594e82b..477ea6214 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -216,7 +216,7 @@ _idx_obj_part (const DedupMultiIdxType *idx_type, } return 1; - case NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION: + case NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID: obj_type = NMP_OBJECT_GET_TYPE (obj_a); if ( !NM_IN_SET (obj_type, NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE) @@ -589,29 +589,6 @@ nmp_object_stackinit_id_ip6_address (NMPObject *obj, int ifindex, const struct i return obj; } -const NMPObject * -nmp_object_stackinit_id_ip4_route (NMPObject *obj, int ifindex, guint32 network, guint8 plen, guint32 metric) -{ - _nmp_object_stackinit_from_type (obj, NMP_OBJECT_TYPE_IP4_ROUTE); - obj->ip4_route.ifindex = ifindex; - obj->ip4_route.network = network; - obj->ip4_route.plen = plen; - obj->ip4_route.metric = metric; - return obj; -} - -const NMPObject * -nmp_object_stackinit_id_ip6_route (NMPObject *obj, int ifindex, const struct in6_addr *network, guint8 plen, guint32 metric) -{ - _nmp_object_stackinit_from_type (obj, NMP_OBJECT_TYPE_IP6_ROUTE); - obj->ip6_route.ifindex = ifindex; - if (network) - obj->ip6_route.network = *network; - obj->ip6_route.plen = plen; - obj->ip6_route.metric = metric; - return obj; -} - /*****************************************************************************/ const char * @@ -765,8 +742,6 @@ _vt_cmd_plobj_to_string_id (ip4_address, NMPlatformIP4Address, "%d: %s/%d%s%s", obj->peer_address != obj->address ? "," : "", obj->peer_address != obj->address ? nm_utils_inet4_ntop (obj->peer_address & nm_utils_ip4_prefix_to_netmask (obj->plen), buf2) : ""); _vt_cmd_plobj_to_string_id (ip6_address, NMPlatformIP6Address, "%d: %s", obj->ifindex, nm_utils_inet6_ntop (&obj->address, buf1)); -_vt_cmd_plobj_to_string_id (ip4_route, NMPlatformIP4Route, "%d: %s/%d %d", obj->ifindex, nm_utils_inet4_ntop ( obj->network, buf1), obj->plen, obj->metric); -_vt_cmd_plobj_to_string_id (ip6_route, NMPlatformIP6Route, "%d: %s/%d %d", obj->ifindex, nm_utils_inet6_ntop (&obj->network, buf1), obj->plen, obj->metric); guint nmp_object_hash (const NMPObject *obj) @@ -984,18 +959,12 @@ _vt_cmd_plobj_id_copy (ip6_address, NMPlatformIP6Address, { dst->address = src->address; }); _vt_cmd_plobj_id_copy (ip4_route, NMPlatformIP4Route, { - dst->ifindex = src->ifindex; - dst->plen = src->plen; - dst->metric = src->metric; - dst->network = src->network; - nm_assert (nm_platform_ip4_route_cmp (dst, src, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID_CACHE) == 0); + *dst = *src; + nm_assert (nm_platform_ip4_route_cmp (dst, src, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0); }); _vt_cmd_plobj_id_copy (ip6_route, NMPlatformIP6Route, { - dst->ifindex = src->ifindex; - dst->plen = src->plen; - dst->metric = src->metric; - dst->network = src->network; - nm_assert (nm_platform_ip6_route_cmp (dst, src, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID_CACHE) == 0); + *dst = *src; + nm_assert (nm_platform_ip6_route_cmp (dst, src, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) == 0); }); /* Uses internally nmp_object_copy(), hence it also violates the const @@ -1080,13 +1049,13 @@ _vt_cmd_plobj_id_cmp (ip6_address, NMPlatformIP6Address, static int _vt_cmd_plobj_id_cmp_ip4_route (const NMPlatformObject *obj1, const NMPlatformObject *obj2) { - return nm_platform_ip4_route_cmp ((NMPlatformIP4Route *) obj1, (NMPlatformIP4Route *) obj2, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID_CACHE); + return nm_platform_ip4_route_cmp ((NMPlatformIP4Route *) obj1, (NMPlatformIP4Route *) obj2, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID); } static int _vt_cmd_plobj_id_cmp_ip6_route (const NMPlatformObject *obj1, const NMPlatformObject *obj2) { - return nm_platform_ip6_route_cmp ((NMPlatformIP6Route *) obj1, (NMPlatformIP6Route *) obj2, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID_CACHE); + return nm_platform_ip6_route_cmp ((NMPlatformIP6Route *) obj1, (NMPlatformIP6Route *) obj2, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID); } guint @@ -1140,10 +1109,10 @@ _vt_cmd_plobj_id_hash (ip6_address, NMPlatformIP6Address, { hash = NM_HASH_COMBINE (hash, nm_utils_in6_addr_hash (&obj->address)); }) _vt_cmd_plobj_id_hash (ip4_route, NMPlatformIP4Route, { - hash = nm_platform_ip4_route_hash (obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID_CACHE); + hash = nm_platform_ip4_route_hash (obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID); }) _vt_cmd_plobj_id_hash (ip6_route, NMPlatformIP6Route, { - hash = nm_platform_ip6_route_hash (obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID_CACHE); + hash = nm_platform_ip6_route_hash (obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID); }) gboolean @@ -1238,7 +1207,7 @@ static const guint8 _supported_cache_ids_ipx_route[] = { NMP_CACHE_ID_TYPE_OBJECT_TYPE, NMP_CACHE_ID_TYPE_ADDRROUTE_BY_IFINDEX, NMP_CACHE_ID_TYPE_DEFAULT_ROUTES, - NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION, + NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, 0, }; @@ -1617,39 +1586,80 @@ nmp_lookup_init_route_visible (NMPLookup *lookup, } const NMPLookup * -nmp_lookup_init_route_by_dest (NMPLookup *lookup, - int addr_family, - gconstpointer network, - guint plen, - guint32 metric) +nmp_lookup_init_route_by_weak_id (NMPLookup *lookup, + const NMPObject *obj) +{ + const NMPlatformIP4Route *r4; + const NMPlatformIP6Route *r6; + + nm_assert (lookup); + + switch (NMP_OBJECT_GET_TYPE (obj)) { + case NMP_OBJECT_TYPE_IP4_ROUTE: + r4 = NMP_OBJECT_CAST_IP4_ROUTE (obj); + return nmp_lookup_init_ip4_route_by_weak_id (lookup, + r4->network, + r4->plen, + r4->metric, + r4->tos); + case NMP_OBJECT_TYPE_IP6_ROUTE: + r6 = NMP_OBJECT_CAST_IP6_ROUTE (obj); + return nmp_lookup_init_ip6_route_by_weak_id (lookup, + &r6->network, + r6->plen, + r6->metric, + &r6->src, + r6->src_plen); + default: + nm_assert_not_reached (); + return NULL; + } +} + +const NMPLookup * +nmp_lookup_init_ip4_route_by_weak_id (NMPLookup *lookup, + in_addr_t network, + guint plen, + guint32 metric, + guint8 tos) { NMPObject *o; nm_assert (lookup); - switch (addr_family) { - case AF_INET: - o = _nmp_object_stackinit_from_type (&lookup->selector_obj, NMP_OBJECT_TYPE_IP4_ROUTE); - o->object.ifindex = 1; - o->ip_route.plen = plen; - o->ip_route.metric = metric; - if (network) - o->ip4_route.network = *((in_addr_t *) network); - lookup->cache_id_type = NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION; - break; - case AF_INET6: - o = _nmp_object_stackinit_from_type (&lookup->selector_obj, NMP_OBJECT_TYPE_IP6_ROUTE); - o->object.ifindex = 1; - o->ip_route.plen = plen; - o->ip_route.metric = metric; - if (network) - o->ip6_route.network = *((struct in6_addr *) network); - lookup->cache_id_type = NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION; - break; - default: - nm_assert_not_reached (); - return NULL; - } + o = _nmp_object_stackinit_from_type (&lookup->selector_obj, NMP_OBJECT_TYPE_IP4_ROUTE); + o->object.ifindex = 1; + o->ip_route.plen = plen; + o->ip_route.metric = metric; + if (network) + o->ip4_route.network = network; + o->ip4_route.tos = tos; + lookup->cache_id_type = NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID; + return _L (lookup); +} + +const NMPLookup * +nmp_lookup_init_ip6_route_by_weak_id (NMPLookup *lookup, + const struct in6_addr *network, + guint plen, + guint32 metric, + const struct in6_addr *src, + guint8 src_plen) +{ + NMPObject *o; + + nm_assert (lookup); + + o = _nmp_object_stackinit_from_type (&lookup->selector_obj, NMP_OBJECT_TYPE_IP6_ROUTE); + o->object.ifindex = 1; + o->ip_route.plen = plen; + o->ip_route.metric = metric; + if (network) + o->ip6_route.network = *network; + if (src) + o->ip6_route.src = *src; + o->ip6_route.src_plen = src_plen; + lookup->cache_id_type = NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID; return _L (lookup); } @@ -1684,55 +1694,6 @@ nmp_cache_lookup_to_array (const NMDedupMultiHeadEntry *head_entry, /*****************************************************************************/ -/** - * nmp_cache_find_other_route_for_same_destination: - * @cache: - * @route: - * - * Look into the cache whether there is a route to the same destination, - * in terms of network/plen,metric. - * - * Returns: (transfer none): the first found route object from the cache - * that has the same (network/plen,metric) values as @route, but has different - * ID. Or %NULL, if no such route exists. - */ -const NMPObject * -nmp_cache_find_other_route_for_same_destination (const NMPCache *cache, const NMPObject *route) -{ - NMPLookup lookup; - NMDedupMultiIter iter; - const NMPObject *o = NULL; - - nm_assert (cache); - - switch (NMP_OBJECT_GET_TYPE (route)) { - case NMP_OBJECT_TYPE_IP4_ROUTE: - nmp_lookup_init_route_by_dest (&lookup, - AF_INET, - &route->ip4_route.network, - route->ip_route.plen, - route->ip_route.metric); - break; - case NMP_OBJECT_TYPE_IP6_ROUTE: - nmp_lookup_init_route_by_dest (&lookup, - AF_INET6, - &route->ip6_route.network, - route->ip_route.plen, - route->ip_route.metric); - break; - default: - g_return_val_if_reached (NULL); - } - - nmp_cache_iter_for_each (&iter, nmp_cache_lookup (cache, &lookup), &o) { - nm_assert (NMP_OBJECT_GET_CLASS (route) == NMP_OBJECT_GET_CLASS (o)); - - if (!nmp_object_id_equal (route, o)) - return o; - } - return NULL; -} - const NMPObject * nmp_cache_lookup_link_full (const NMPCache *cache, int ifindex, @@ -1961,6 +1922,7 @@ NMPCacheOpsType nmp_cache_remove (NMPCache *cache, const NMPObject *obj_needle, gboolean equals_by_ptr, + gboolean only_dirty, const NMPObject **out_obj_old) { const NMDedupMultiEntry *entry_old; @@ -1983,6 +1945,11 @@ nmp_cache_remove (NMPCache *cache, * @obj_needle. */ return NMP_CACHE_OPS_UNCHANGED; } + if ( only_dirty + && !entry_old->dirty) { + /* the entry is not dirty. Skip. */ + return NMP_CACHE_OPS_UNCHANGED; + } _idxcache_update (cache, entry_old, NULL, FALSE, NULL); return NMP_CACHE_OPS_REMOVED; } @@ -2193,6 +2160,164 @@ nmp_cache_update_netlink (NMPCache *cache, return NMP_CACHE_OPS_UPDATED; } +NMPCacheOpsType +nmp_cache_update_netlink_route (NMPCache *cache, + NMPObject *obj_hand_over, + gboolean is_dump, + guint16 nlmsgflags, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new, + const NMPObject **out_obj_replace, + gboolean *out_resync_required) +{ + NMDedupMultiIter iter; + const NMDedupMultiEntry *entry_old; + const NMDedupMultiEntry *entry_new; + const NMDedupMultiEntry *entry_cur; + const NMDedupMultiEntry *entry_replace; + const NMDedupMultiHeadEntry *head_entry; + gboolean is_alive; + NMPCacheOpsType ops_type = NMP_CACHE_OPS_UNCHANGED; + gboolean resync_required; + + nm_assert (cache); + nm_assert (NMP_OBJECT_IS_VALID (obj_hand_over)); + nm_assert (!NMP_OBJECT_IS_STACKINIT (obj_hand_over)); + /* A link object from netlink must have the udev related fields unset. + * We could implement to handle that, but there is no need to support such + * a use-case */ + nm_assert (NM_IN_SET (NMP_OBJECT_GET_TYPE (obj_hand_over), NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); + nm_assert (nm_dedup_multi_index_obj_find (cache->multi_idx, obj_hand_over) != obj_hand_over); + + entry_old = _lookup_entry (cache, obj_hand_over); + entry_new = NULL; + + if (!entry_old) { + + if (!nmp_object_is_alive (obj_hand_over)) + goto update_done; + + _idxcache_update (cache, + entry_old, + obj_hand_over, + is_dump, + &entry_new); + ops_type = NMP_CACHE_OPS_ADDED; + goto update_done; + } + + is_alive = nmp_object_is_alive (obj_hand_over); + + if (!is_alive) { + /* the update would make @entry_old invalid. Remove it. */ + _idxcache_update (cache, entry_old, NULL, FALSE, NULL); + ops_type = NMP_CACHE_OPS_REMOVED; + goto update_done; + } + + if (nmp_object_equal (entry_old->obj, obj_hand_over)) { + nm_dedup_multi_entry_set_dirty (entry_old, FALSE); + goto update_done; + } + + _idxcache_update (cache, + entry_old, + obj_hand_over, + is_dump, + &entry_new); + ops_type = NMP_CACHE_OPS_UPDATED; + +update_done: + NM_SET_OUT (out_obj_old, nmp_object_ref (nm_dedup_multi_entry_get_obj (entry_old))); + NM_SET_OUT (out_obj_new, nmp_object_ref (nm_dedup_multi_entry_get_obj (entry_new))); + + /* a RTM_GETROUTE event may signal that another object was replaced. + * Find out whether that is the case and return it as @obj_replaced. + * + * Also, fixup the order of @entry_new within NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID + * index. For most parts, we don't care about the order of objects (including routes). + * But NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID we must keep in the correct order, to + * properly find @obj_replaced. */ + resync_required = FALSE; + entry_replace = NULL; + if (is_dump) { + goto out; + } + + if (!entry_new) { + if ( NM_FLAGS_HAS (nlmsgflags, NLM_F_REPLACE) + && nmp_cache_lookup_all (cache, + NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, + obj_hand_over)) { + /* hm. @obj_hand_over was not added, meaning it was not alive. + * However, we track some other objects with the same weak-id. + * It's unclear what that means. To be sure, resync. */ + resync_required = TRUE; + } + goto out; + } + + entry_cur = _lookup_entry_with_idx_type (cache, + NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, + entry_new->obj); + if (!entry_cur) { + nm_assert_not_reached (); + goto out; + } + nm_assert (entry_cur->obj == entry_new->obj); + + head_entry = entry_cur->head; + nm_assert (head_entry == nmp_cache_lookup_all (cache, + NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, + entry_cur->obj)); + + if (head_entry->len == 1) { + /* there is only one object, and we expect it to be @obj_new. */ + nm_assert (nm_dedup_multi_head_entry_get_idx (head_entry, 0) == entry_cur); + goto out; + } + + switch (nlmsgflags & (NLM_F_REPLACE | NLM_F_EXCL | NLM_F_CREATE | NLM_F_APPEND)) { + case NLM_F_REPLACE: + /* ip route change */ + + /* get the first element (but skip @obj_new). */ + nm_dedup_multi_iter_init (&iter, head_entry); + if (!nm_dedup_multi_iter_next (&iter)) + nm_assert_not_reached (); + if (iter.current == entry_cur) { + if (!nm_dedup_multi_iter_next (&iter)) + nm_assert_not_reached (); + } + entry_replace = iter.current; + + nm_assert ( entry_replace + && entry_cur != entry_replace); + + nm_dedup_multi_entry_reorder (entry_cur, entry_replace, FALSE); + break; + case NLM_F_CREATE | NLM_F_APPEND: + /* ip route append */ + nm_dedup_multi_entry_reorder (entry_cur, NULL, TRUE); + break; + case NLM_F_CREATE: + /* ip route prepend */ + nm_dedup_multi_entry_reorder (entry_cur, NULL, FALSE); + break; + default: + /* this is an unexecpted case, probably a bug that we need to handle better. */ + resync_required = TRUE; + break; + } + +out: + NM_SET_OUT (out_obj_replace, nmp_object_ref (nm_dedup_multi_entry_get_obj (entry_replace))); + NM_SET_OUT (out_resync_required, resync_required); + return ops_type; +} + + NMPCacheOpsType nmp_cache_update_link_udev (NMPCache *cache, int ifindex, @@ -2436,7 +2561,7 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip4_route, .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip4_route, .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip4_route, - .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip4_route, + .cmd_plobj_to_string_id = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip4_route_to_string, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip4_route_to_string, .cmd_plobj_hash = (guint (*) (const NMPlatformObject *obj)) nm_platform_ip4_route_hash_full, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip4_route_cmp_full, @@ -2456,7 +2581,7 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_id_copy = _vt_cmd_plobj_id_copy_ip6_route, .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip6_route, .cmd_plobj_id_hash = _vt_cmd_plobj_id_hash_ip6_route, - .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_ip6_route, + .cmd_plobj_to_string_id = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip6_route_to_string, .cmd_plobj_to_string = (const char *(*) (const NMPlatformObject *obj, char *buf, gsize len)) nm_platform_ip6_route_to_string, .cmd_plobj_hash = (guint (*) (const NMPlatformObject *obj)) nm_platform_ip6_route_hash_full, .cmd_plobj_cmp = (int (*) (const NMPlatformObject *obj1, const NMPlatformObject *obj2)) nm_platform_ip6_route_cmp_full, diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index 2201cb760..32a2a36df 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -93,7 +93,7 @@ typedef enum { /*< skip >*/ * sends one RTM_NEWADDR notification without notifying about the deletion. We detect * that by having this index to contain overlapping routes which require special * cache-resync. */ - NMP_CACHE_ID_TYPE_ROUTES_BY_DESTINATION, + NMP_CACHE_ID_TYPE_ROUTES_BY_WEAK_ID, __NMP_CACHE_ID_TYPE_MAX, NMP_CACHE_ID_TYPE_MAX = __NMP_CACHE_ID_TYPE_MAX - 1, @@ -452,8 +452,6 @@ const NMPObject *nmp_object_stackinit_id (NMPObject *obj, const NMPObject *src) const NMPObject *nmp_object_stackinit_id_link (NMPObject *obj, int ifindex); const NMPObject *nmp_object_stackinit_id_ip4_address (NMPObject *obj, int ifindex, guint32 address, guint8 plen, guint32 peer_address); const NMPObject *nmp_object_stackinit_id_ip6_address (NMPObject *obj, int ifindex, const struct in6_addr *address); -const NMPObject *nmp_object_stackinit_id_ip4_route (NMPObject *obj, int ifindex, guint32 network, guint8 plen, guint32 metric); -const NMPObject *nmp_object_stackinit_id_ip6_route (NMPObject *obj, int ifindex, const struct in6_addr *network, guint8 plen, guint32 metric); const char *nmp_object_to_string (const NMPObject *obj, NMPObjectToStringMode to_string_mode, char *buf, gsize buf_size); guint nmp_object_hash (const NMPObject *obj); @@ -529,11 +527,19 @@ const NMPLookup *nmp_lookup_init_route_visible (NMPLookup *lookup, NMPObjectType obj_type, int ifindex, gboolean only_default); -const NMPLookup *nmp_lookup_init_route_by_dest (NMPLookup *lookup, - int addr_family, - gconstpointer network, - guint plen, - guint32 metric); +const NMPLookup *nmp_lookup_init_route_by_weak_id (NMPLookup *lookup, + const NMPObject *obj); +const NMPLookup *nmp_lookup_init_ip4_route_by_weak_id (NMPLookup *lookup, + in_addr_t network, + guint plen, + guint32 metric, + guint8 tos); +const NMPLookup *nmp_lookup_init_ip6_route_by_weak_id (NMPLookup *lookup, + const struct in6_addr *network, + guint plen, + guint32 metric, + const struct in6_addr *src, + guint8 src_plen); GArray *nmp_cache_lookup_to_array (const NMDedupMultiHeadEntry *head_entry, NMPObjectType obj_type, @@ -577,8 +583,6 @@ nmp_cache_iter_next_link (NMDedupMultiIter *iter, const NMPlatformLink **out_obj nmp_cache_iter_next_link ((iter), (obj)); \ ) -const NMPObject *nmp_cache_find_other_route_for_same_destination (const NMPCache *cache, const NMPObject *route); - const NMPObject *nmp_cache_lookup_link_full (const NMPCache *cache, int ifindex, const char *ifname, @@ -598,6 +602,7 @@ void ASSERT_nmp_cache_is_consistent (const NMPCache *cache); NMPCacheOpsType nmp_cache_remove (NMPCache *cache, const NMPObject *obj_needle, gboolean equals_by_ptr, + gboolean only_dirty, const NMPObject **out_obj_old); NMPCacheOpsType nmp_cache_remove_netlink (NMPCache *cache, const NMPObject *obj_needle, @@ -608,6 +613,14 @@ NMPCacheOpsType nmp_cache_update_netlink (NMPCache *cache, gboolean is_dump, const NMPObject **out_obj_old, const NMPObject **out_obj_new); +NMPCacheOpsType nmp_cache_update_netlink_route (NMPCache *cache, + NMPObject *obj_hand_over, + gboolean is_dump, + guint16 nlmsgflags, + const NMPObject **out_obj_old, + const NMPObject **out_obj_new, + const NMPObject **out_obj_replace, + gboolean *out_resync_required); NMPCacheOpsType nmp_cache_update_link_udev (NMPCache *cache, int ifindex, struct udev_device *udevice, @@ -663,6 +676,14 @@ ASSERT_nmp_cache_ops (const NMPCache *cache, #endif } +const NMDedupMultiHeadEntry *nm_platform_lookup_all (NMPlatform *platform, + NMPCacheIdType cache_id_type, + NMPObject *obj); + +const NMDedupMultiEntry *nm_platform_lookup_entry (NMPlatform *platform, + NMPCacheIdType cache_id_type, + NMPObject *obj); + static inline const NMDedupMultiHeadEntry * nm_platform_lookup_obj_type (NMPlatform *platform, NMPObjectType obj_type) @@ -721,15 +742,29 @@ nm_platform_lookup_route_visible_clone (NMPlatform *platform, } static inline const NMDedupMultiHeadEntry * -nm_platform_lookup_route_by_dest (NMPlatform *platform, - int addr_family, - gconstpointer network, - guint plen, - guint32 metric) +nm_platform_lookup_ip4_route_by_weak_id (NMPlatform *platform, + in_addr_t network, + guint plen, + guint32 metric, + guint8 tos) { NMPLookup lookup; - nmp_lookup_init_route_by_dest (&lookup, addr_family, network, plen, metric); + nmp_lookup_init_ip4_route_by_weak_id (&lookup, network, plen, metric, tos); + return nm_platform_lookup (platform, &lookup); +} + +static inline const NMDedupMultiHeadEntry * +nm_platform_lookup_ip6_route_by_weak_id (NMPlatform *platform, + const struct in6_addr *network, + guint plen, + guint32 metric, + const struct in6_addr *src, + guint8 src_plen) +{ + NMPLookup lookup; + + nmp_lookup_init_ip6_route_by_weak_id (&lookup, network, plen, metric, src, src_plen); return nm_platform_lookup (platform, &lookup); } diff --git a/src/platform/tests/test-common.c b/src/platform/tests/test-common.c index feef869f4..9692c0905 100644 --- a/src/platform/tests/test-common.c +++ b/src/platform/tests/test-common.c @@ -294,113 +294,219 @@ link_callback (NMPlatform *platform, int obj_type_i, int ifindex, NMPlatformLink /*****************************************************************************/ -gboolean -nmtstp_ip4_route_exists (const char *ifname, guint32 network, int plen, guint32 metric) +static const NMPlatformIP4Route * +_ip4_route_get (NMPlatform *platform, + int ifindex, + guint32 network, + int plen, + guint32 metric, + guint8 tos, + guint *out_c_exists) { - gs_free char *arg_network = NULL; - const char *argv[] = { - NULL, - "route", - "list", - "dev", - ifname, - "exact", - NULL, - NULL, - }; - int exit_status; - gs_free char *std_out = NULL, *std_err = NULL; - char *out; - gboolean success; - gs_free_error GError *error = NULL; - gs_free char *metric_pattern = NULL; - - g_assert (ifname && nm_utils_is_valid_iface_name (ifname, NULL)); - g_assert (!strstr (ifname, " metric ")); - g_assert (plen >= 0 && plen <= 32); - - if (!nmtstp_is_root_test ()) { - /* If we don't test against linux-platform, we don't actually configure any - * routes in the system. */ - return -1; - } - - argv[0] = nm_utils_file_search_in_paths ("ip", NULL, - (const char *[]) { "/sbin", "/usr/sbin", NULL }, - G_FILE_TEST_IS_EXECUTABLE, NULL, NULL, NULL); - argv[6] = arg_network = g_strdup_printf ("%s/%d", nm_utils_inet4_ntop (network, NULL), plen); - - if (!argv[0]) { - /* Hm. There is no 'ip' binary. Return *unknown* */ - return -1; - } - - success = g_spawn_sync (NULL, - (char **) argv, - (char *[]) { NULL }, - 0, - NULL, - NULL, - &std_out, - &std_err, - &exit_status, - &error); - g_assert_no_error (error); - g_assert (success); - g_assert_cmpstr (std_err, ==, ""); - g_assert (std_out); - - metric_pattern = g_strdup_printf (" metric %u", metric); - out = std_out; - while (out) { - char *eol = strchr (out, '\n'); - gs_free char *line = eol ? g_strndup (out, eol - out) : g_strdup (out); - const char *p; - - out = eol ? &eol[1] : NULL; - if (!line[0]) - continue; - - if (metric == 0) { - if (!strstr (line, " metric ")) - return TRUE; - } - p = strstr (line, metric_pattern); - if (p && NM_IN_SET (p[strlen (metric_pattern)], ' ', '\0')) - return TRUE; - } - return FALSE; -} - -void -_nmtstp_assert_ip4_route_exists (const char *file, guint line, const char *func, NMPlatform *platform, gboolean exists, const char *ifname, guint32 network, int plen, guint32 metric) -{ - int ifindex; - gboolean exists_checked; + NMDedupMultiIter iter; + NMPLookup lookup; + const NMPObject *o; + guint c; + const NMPlatformIP4Route *r = NULL; _init_platform (&platform, FALSE); - /* Check for existance of the route by spawning iproute2. Do this because platform - * code might be entirely borked, but we expect ip-route to give a correct result. - * If the ip command cannot be found, we accept this as success. */ - exists_checked = nmtstp_ip4_route_exists (ifname, network, plen, metric); - if (exists_checked != -1 && !exists_checked != !exists) { - g_error ("[%s:%u] %s(): We expect the ip4 route %s/%d metric %u %s, but it %s", - file, line, func, - nm_utils_inet4_ntop (network, NULL), plen, metric, - exists ? "to exist" : "not to exist", - exists ? "doesn't" : "does"); + nmp_lookup_init_ip4_route_by_weak_id (&lookup, + network, + plen, + metric, + tos); + + c = 0; + nmp_cache_iter_for_each (&iter, + nm_platform_lookup (platform, &lookup), + &o) { + if ( NMP_OBJECT_CAST_IP4_ROUTE (o)->ifindex != ifindex + && ifindex > 0) + continue; + if (!r) + r = NMP_OBJECT_CAST_IP4_ROUTE (o); + c++; } - ifindex = nm_platform_link_get_ifindex (platform, ifname); - g_assert (ifindex > 0); - if (!nm_platform_ip4_route_get (platform, ifindex, network, plen, metric) != !exists) { - g_error ("[%s:%u] %s(): The ip4 route %s/%d metric %u %s, but platform thinks %s", - file, line, func, - nm_utils_inet4_ntop (network, NULL), plen, metric, - exists ? "exists" : "does not exist", - exists ? "it doesn't" : "it does"); + NM_SET_OUT (out_c_exists, c); + return r; +} + +const NMPlatformIP4Route * +_nmtstp_assert_ip4_route_exists (const char *file, + guint line, + const char *func, + NMPlatform *platform, + int c_exists, + const char *ifname, + guint32 network, + int plen, + guint32 metric, + guint8 tos) +{ + int ifindex; + guint c; + const NMPlatformIP4Route *r = NULL; + + _init_platform (&platform, FALSE); + + ifindex = -1; + if (ifname) { + ifindex = nm_platform_link_get_ifindex (platform, ifname); + g_assert (ifindex > 0); } + + r = _ip4_route_get (platform, + ifindex, + network, + plen, + metric, + tos, + &c); + + if (c != c_exists && c_exists != -1) { + g_error ("[%s:%u] %s(): The ip4 route %s/%d metric %u tos %u shall exist %u times, but platform has it %u times", + file, line, func, + nm_utils_inet4_ntop (network, NULL), plen, + metric, + tos, + c_exists, + c); + } + + return r; +} + +const NMPlatformIP4Route * +nmtstp_ip4_route_get (NMPlatform *platform, + int ifindex, + guint32 network, + int plen, + guint32 metric, + guint8 tos) +{ + return _ip4_route_get (platform, + ifindex, + network, + plen, + metric, + tos, + NULL); +} + +/*****************************************************************************/ + +static const NMPlatformIP6Route * +_ip6_route_get (NMPlatform *platform, + int ifindex, + const struct in6_addr *network, + guint plen, + guint32 metric, + const struct in6_addr *src, + guint8 src_plen, + guint *out_c_exists) +{ + NMDedupMultiIter iter; + NMPLookup lookup; + const NMPObject *o; + guint c; + const NMPlatformIP6Route *r = NULL; + + _init_platform (&platform, FALSE); + + nmp_lookup_init_ip6_route_by_weak_id (&lookup, + network, + plen, + metric, + src, + src_plen); + + c = 0; + nmp_cache_iter_for_each (&iter, + nm_platform_lookup (platform, &lookup), + &o) { + if ( NMP_OBJECT_CAST_IP6_ROUTE (o)->ifindex != ifindex + && ifindex > 0) + continue; + if (!r) + r = NMP_OBJECT_CAST_IP6_ROUTE (o); + c++; + } + + NM_SET_OUT (out_c_exists, c); + return r; +} + +const NMPlatformIP6Route * +_nmtstp_assert_ip6_route_exists (const char *file, + guint line, + const char *func, + NMPlatform *platform, + int c_exists, + const char *ifname, + const struct in6_addr *network, + guint plen, + guint32 metric, + const struct in6_addr *src, + guint8 src_plen) +{ + int ifindex; + guint c; + const NMPlatformIP6Route *r = NULL; + + _init_platform (&platform, FALSE); + + ifindex = -1; + if (ifname) { + ifindex = nm_platform_link_get_ifindex (platform, ifname); + g_assert (ifindex > 0); + } + + r = _ip6_route_get (platform, + ifindex, + network, + plen, + metric, + src, + src_plen, + &c); + + if (c != c_exists && c_exists != -1) { + char s_src[NM_UTILS_INET_ADDRSTRLEN]; + char s_network[NM_UTILS_INET_ADDRSTRLEN]; + + g_error ("[%s:%u] %s(): The ip6 route %s/%d metric %u src %s/%d shall exist %u times, but platform has it %u times", + file, line, func, + nm_utils_inet6_ntop (network, s_network), + plen, + metric, + nm_utils_inet6_ntop (src, s_src), + src_plen, + c_exists, + c); + } + + return r; +} + +const NMPlatformIP6Route * +nmtstp_ip6_route_get (NMPlatform *platform, + int ifindex, + const struct in6_addr *network, + guint plen, + guint32 metric, + const struct in6_addr *src, + guint8 src_plen) +{ + return _ip6_route_get (platform, + ifindex, + network, + plen, + metric, + src, + src_plen, + NULL); } /*****************************************************************************/ diff --git a/src/platform/tests/test-common.h b/src/platform/tests/test-common.h index 4d65a2668..8ec9c7e84 100644 --- a/src/platform/tests/test-common.h +++ b/src/platform/tests/test-common.h @@ -121,10 +121,45 @@ gboolean nmtstp_run_command_check_external (int external_command); /*****************************************************************************/ -gboolean nmtstp_ip4_route_exists (const char *ifname, guint32 network, int plen, guint32 metric); +const NMPlatformIP4Route *_nmtstp_assert_ip4_route_exists (const char *file, + guint line, + const char *func, + NMPlatform *platform, + int c_exists, + const char *ifname, + guint32 network, + int plen, + guint32 metric, + guint8 tos); +#define nmtstp_assert_ip4_route_exists(platform, c_exists, ifname, network, plen, metric, tos) _nmtstp_assert_ip4_route_exists (__FILE__, __LINE__, G_STRFUNC, platform, c_exists, ifname, network, plen, metric, tos) -void _nmtstp_assert_ip4_route_exists (const char *file, guint line, const char *func, NMPlatform *platform, gboolean exists, const char *ifname, guint32 network, int plen, guint32 metric); -#define nmtstp_assert_ip4_route_exists(platform, exists, ifname, network, plen, metric) _nmtstp_assert_ip4_route_exists (__FILE__, __LINE__, G_STRFUNC, platform, exists, ifname, network, plen, metric) +const NMPlatformIP4Route *nmtstp_ip4_route_get (NMPlatform *platform, + int ifindex, + guint32 network, + int plen, + guint32 metric, + guint8 tos); + +const NMPlatformIP6Route *_nmtstp_assert_ip6_route_exists (const char *file, + guint line, + const char *func, + NMPlatform *platform, + int c_exists, + const char *ifname, + const struct in6_addr *network, + guint plen, + guint32 metric, + const struct in6_addr *src, + guint8 src_plen); +#define nmtstp_assert_ip6_route_exists(platform, c_exists, ifname, network, plen, metric, src, src_plen) _nmtstp_assert_ip6_route_exists (__FILE__, __LINE__, G_STRFUNC, platform, c_exists, ifname, network, plen, metric, src, src_plen) + +const NMPlatformIP6Route *nmtstp_ip6_route_get (NMPlatform *platform, + int ifindex, + const struct in6_addr *network, + guint plen, + guint32 metric, + const struct in6_addr *src, + guint8 src_plen); /*****************************************************************************/ diff --git a/src/platform/tests/test-route.c b/src/platform/tests/test-route.c index 94be799bb..3dac6e575 100644 --- a/src/platform/tests/test-route.c +++ b/src/platform/tests/test-route.c @@ -139,50 +139,50 @@ test_ip4_route_metric0 (void) int mss = 1000; /* No routes initially */ - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, 0); - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, 0, 0); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, metric, 0); /* add the first route */ nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, metric, mss); accept_signal (route_added); - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, 0); - nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, 0, 0); + nmtstp_assert_ip4_route_exists (NULL, 1, DEVICE_NAME, network, plen, metric, 0); /* Deleting route with metric 0 does nothing */ g_assert (nmtstp_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, 0)); ensure_no_signal (route_removed); - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, 0); - nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, 0, 0); + nmtstp_assert_ip4_route_exists (NULL, 1, DEVICE_NAME, network, plen, metric, 0); /* add the second route */ nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, INADDR_ANY, 0, 0, mss); accept_signal (route_added); - nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, network, plen, 0); - nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 1, DEVICE_NAME, network, plen, 0, 0); + nmtstp_assert_ip4_route_exists (NULL, 1, DEVICE_NAME, network, plen, metric, 0); /* Delete route with metric 0 */ g_assert (nmtstp_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, 0)); accept_signal (route_removed); - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, 0); - nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, 0, 0); + nmtstp_assert_ip4_route_exists (NULL, 1, DEVICE_NAME, network, plen, metric, 0); /* Delete route with metric 0 again (we expect nothing to happen) */ g_assert (nmtstp_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, 0)); ensure_no_signal (route_removed); - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, 0); - nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, 0, 0); + nmtstp_assert_ip4_route_exists (NULL, 1, DEVICE_NAME, network, plen, metric, 0); /* Delete the other route */ g_assert (nmtstp_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); accept_signal (route_removed); - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, 0); - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, 0, 0); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, metric, 0); free_signal (route_added); free_signal (route_changed); @@ -213,9 +213,9 @@ test_ip4_route (void) accept_signal (route_added); /* Add route */ - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, metric, 0); nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, 0, metric, mss); - nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 1, DEVICE_NAME, network, plen, metric, 0); accept_signal (route_added); /* Add route again */ @@ -223,9 +223,9 @@ test_ip4_route (void) accept_signals (route_changed, 0, 1); /* Add default route */ - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, 0, 0, metric); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, 0, 0, metric, 0); nmtstp_ip4_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, 0, 0, gateway, 0, metric, mss); - nmtstp_assert_ip4_route_exists (NULL, TRUE, DEVICE_NAME, 0, 0, metric); + nmtstp_assert_ip4_route_exists (NULL, 1, DEVICE_NAME, 0, 0, metric, 0); accept_signal (route_added); /* Add default route again */ @@ -265,7 +265,7 @@ test_ip4_route (void) /* Remove route */ g_assert (nmtstp_platform_ip4_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); - nmtstp_assert_ip4_route_exists (NULL, FALSE, DEVICE_NAME, network, plen, metric); + nmtstp_assert_ip4_route_exists (NULL, 0, DEVICE_NAME, network, plen, metric, 0); accept_signal (route_removed); /* Remove route again */ @@ -315,9 +315,9 @@ test_ip6_route (void) accept_signal (route_added); /* Add route */ - g_assert (!nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric)); + g_assert (!nmtstp_ip6_route_get (NM_PLATFORM_GET, ifindex, &network, plen, metric, NULL, 0)); nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, network, plen, gateway, pref_src, metric, mss); - g_assert (nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric)); + g_assert (nmtstp_ip6_route_get (NM_PLATFORM_GET, ifindex, &network, plen, metric, NULL, 0)); accept_signal (route_added); /* Add route again */ @@ -325,9 +325,9 @@ test_ip6_route (void) accept_signals (route_changed, 0, 1); /* Add default route */ - g_assert (!nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, in6addr_any, 0, metric)); + g_assert (!nmtstp_ip6_route_get (NM_PLATFORM_GET, ifindex, &in6addr_any, 0, metric, NULL, 0)); nmtstp_ip6_route_add (NM_PLATFORM_GET, ifindex, NM_IP_CONFIG_SOURCE_USER, in6addr_any, 0, gateway, in6addr_any, metric, mss); - g_assert (nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, in6addr_any, 0, metric)); + g_assert (nmtstp_ip6_route_get (NM_PLATFORM_GET, ifindex, &in6addr_any, 0, metric, NULL, 0)); accept_signal (route_added); /* Add default route again */ @@ -367,7 +367,7 @@ test_ip6_route (void) /* Remove route */ g_assert (nmtstp_platform_ip6_route_delete (NM_PLATFORM_GET, ifindex, network, plen, metric)); - g_assert (!nm_platform_ip6_route_get (NM_PLATFORM_GET, ifindex, network, plen, metric)); + g_assert (!nmtstp_ip6_route_get (NM_PLATFORM_GET, ifindex, &network, plen, metric, NULL, 0)); accept_signal (route_removed); /* Remove route again */ @@ -398,8 +398,8 @@ test_ip4_zero_gateway (void) NMTST_WAIT_ASSERT (100, { nmtstp_wait_for_signal (NM_PLATFORM_GET, 10); - if ( nm_platform_ip4_route_get (NM_PLATFORM_GET, ifindex, nmtst_inet4_from_string ("1.2.3.1"), 32, 0) - && nm_platform_ip4_route_get (NM_PLATFORM_GET, ifindex, nmtst_inet4_from_string ("1.2.3.2"), 32, 0)) + if ( nmtstp_ip4_route_get (NM_PLATFORM_GET, ifindex, nmtst_inet4_from_string ("1.2.3.1"), 32, 0, 0) + && nmtstp_ip4_route_get (NM_PLATFORM_GET, ifindex, nmtst_inet4_from_string ("1.2.3.2"), 32, 0, 0)) break; }); @@ -466,7 +466,7 @@ test_ip6_route_options (gconstpointer test_data) const int TEST_IDX = GPOINTER_TO_INT (test_data); const int IFINDEX = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); GPtrArray *routes; -#define RTS_MAX 1 +#define RTS_MAX 3 NMPlatformIP6Route rts_add[RTS_MAX] = { }; NMPlatformIP6Route rts_cmp[RTS_MAX] = { }; NMPlatformIP6Address addr[1] = { }; @@ -512,6 +512,34 @@ test_ip6_route_options (gconstpointer test_data) .pref_src = *nmtst_inet6_from_string ("2000::2"), }); break; + case 3: + addr[addr_n++] = ((NMPlatformIP6Address) { + .ifindex = IFINDEX, + .address = *nmtst_inet6_from_string ("2001:db8:8086::5"), + .plen = 128, + .peer_address = in6addr_any, + .lifetime = NM_PLATFORM_LIFETIME_PERMANENT, + .preferred = NM_PLATFORM_LIFETIME_PERMANENT, + .n_ifa_flags = 0, + }); + rts_add[rts_n++] = ((NMPlatformIP6Route) { + .ifindex = IFINDEX, + .rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER), + .network = *nmtst_inet6_from_string ("2001:db8:8086::"), + .plen = 110, + .metric = 10021, + .mss = 0, + }); + rts_add[rts_n++] = ((NMPlatformIP6Route) { + .ifindex = IFINDEX, + .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, + .gateway = *nmtst_inet6_from_string ("2001:db8:8086::1"), + .metric = 21, + .mss = 0, + }); + break; default: g_assert_not_reached (); } @@ -538,6 +566,7 @@ test_ip6_route_options (gconstpointer test_data) switch (TEST_IDX) { case 1: case 2: + case 3: for (i = 0; i < rts_n; i++) { rts_cmp[i] = rts_add[i]; rts_cmp[i].rt_source = nmp_utils_ip_config_source_round_trip_rtprot (NM_IP_CONFIG_SOURCE_USER); @@ -567,6 +596,126 @@ test_ip6_route_options (gconstpointer test_data) /*****************************************************************************/ +static void +test_ip (gconstpointer test_data) +{ + const int TEST_IDX = GPOINTER_TO_INT (test_data); + const int IFINDEX = nm_platform_link_get_ifindex (NM_PLATFORM_GET, DEVICE_NAME); + guint i, j, k; + const NMPlatformLink *l; + char ifname[IFNAMSIZ]; + char ifname2[IFNAMSIZ]; + char s1[NM_UTILS_INET_ADDRSTRLEN]; + NMPlatform *platform = NM_PLATFORM_GET; + const int EX_ = -1; + struct { + int ifindex; + } iface_data[10] = { 0 }; + int order_idx[G_N_ELEMENTS (iface_data)] = { 0 }; + guint order_len; + guint try; + + for (i = 0; i < G_N_ELEMENTS (iface_data); i++) { + nm_sprintf_buf (ifname, "v%02u", i); + nm_sprintf_buf (ifname2, "w%02u", i); + + g_assert (!nm_platform_link_get_by_ifname (platform, ifname)); + g_assert (!nm_platform_link_get_by_ifname (platform, ifname2)); + l = nmtstp_link_veth_add (platform, EX_, ifname, ifname2); + iface_data[i].ifindex = l->ifindex; + + nmtstp_link_set_updown (platform, EX_, iface_data[i].ifindex, TRUE); + nmtstp_link_set_updown (platform, EX_, nmtstp_link_get (platform, -1, ifname2)->ifindex, TRUE); + + nm_sprintf_buf (s1, "192.168.7.%d", 100 + i); + nmtstp_ip4_address_add (platform, + EX_, + iface_data[i].ifindex, + nmtst_inet4_from_string (s1), + 24, + nmtst_inet4_from_string (s1), + 3600, + 3600, + 0, + NULL); + } + + order_len = 0; + for (try = 0; try < 5 * G_N_ELEMENTS (order_idx); try++) { + NMPObject o; + NMPlatformIP4Route *r; + guint idx; + const NMDedupMultiHeadEntry *head_entry; + NMPLookup lookup; + + nmp_object_stackinit (&o, NMP_OBJECT_TYPE_IP4_ROUTE, NULL); + r = NMP_OBJECT_CAST_IP4_ROUTE (&o); + r->network = nmtst_inet4_from_string ("192.168.9.0"); + r->plen = 24; + r->metric = 109; + + if ( order_len == 0 + || ( order_len < G_N_ELEMENTS (order_idx) + && nmtst_get_rand_int () % 2)) { +again_find_idx: + idx = nmtst_get_rand_int () % G_N_ELEMENTS (iface_data); + for (i = 0; i < order_len; i++) { + if (order_idx[i] == idx) + goto 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)); + } else { + i = nmtst_get_rand_int () % order_len; + idx = order_idx[i]; + for (i++; i < order_len; i++) + order_idx[i - 1] = order_idx[i]; + order_len--; + + r->ifindex = iface_data[idx].ifindex; + g_assert (nm_platform_ip_route_delete (platform, &o)); + } + + head_entry = nm_platform_lookup (platform, + nmp_lookup_init_obj_type (&lookup, NMP_OBJECT_TYPE_IP4_ROUTE)); + for (j = 0; j < G_N_ELEMENTS (iface_data); j++) { + gboolean has; + NMDedupMultiIter iter; + const NMPObject *o_cached; + + has = FALSE; + for (k = 0; k < order_len; k++) { + if (order_idx[k] == j) { + g_assert (!has); + has = TRUE; + } + } + + nmp_cache_iter_for_each (&iter, head_entry, &o_cached) { + const NMPlatformIP4Route *r_cached = NMP_OBJECT_CAST_IP4_ROUTE (o_cached); + + if ( r_cached->ifindex != iface_data[j].ifindex + || r_cached->metric != 109) + continue; + + g_assert (has); + has = FALSE; + } + g_assert (!has); + } + } + + for (i = 0; i < G_N_ELEMENTS (iface_data); i++) + g_assert (nm_platform_link_delete (platform, iface_data[i].ifindex)); + + (void) TEST_IDX; + (void) IFINDEX; +} + +/*****************************************************************************/ + NMTstpSetupFunc const _nmtstp_setup_platform_func = SETUP; void @@ -586,7 +735,10 @@ _nmtstp_setup_tests (void) add_test_func ("/route/ip4_options", test_ip4_route_options); add_test_func_data ("/route/ip6_options/1", test_ip6_route_options, GINT_TO_POINTER (1)); add_test_func_data ("/route/ip6_options/2", test_ip6_route_options, GINT_TO_POINTER (2)); + add_test_func_data ("/route/ip6_options/3", test_ip6_route_options, GINT_TO_POINTER (3)); - if (nmtstp_is_root_test ()) + if (nmtstp_is_root_test ()) { + add_test_func_data ("/route/ip/1", test_ip, GINT_TO_POINTER (1)); add_test_func ("/route/ip4_zero_gateway", test_ip4_zero_gateway); + } } diff --git a/src/tests/test-route-manager.c b/src/tests/test-route-manager.c index e7d0321a1..68e1f31b8 100644 --- a/src/tests/test-route-manager.c +++ b/src/tests/test-route-manager.c @@ -553,6 +553,7 @@ static void test_ip6 (test_fixture *fixture, gconstpointer user_data) { GArray *routes; + int i; NMPlatformIP6Route state1[] = { { @@ -771,7 +772,37 @@ test_ip6 (test_fixture *fixture, gconstpointer user_data) /* 2001:db8:abad:c0de::/64 on dev0 was updated for gateway removal*/ routes = ip_routes (fixture, NMP_OBJECT_TYPE_IP6_ROUTE); - g_assert_cmpint (routes->len, ==, G_N_ELEMENTS (state2)); + 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); @@ -806,9 +837,9 @@ _assert_route_check (const NMPlatformVTableRoute *vtable, gboolean has, const NM g_assert (route); if (vtable->is_ip4) - r = (const NMPlatformIPXRoute *) nm_platform_ip4_route_get (NM_PLATFORM_GET, route->rx.ifindex, route->r4.network, route->rx.plen, route->rx.metric); + 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 *) nm_platform_ip6_route_get (NM_PLATFORM_GET, route->rx.ifindex, route->r6.network, route->rx.plen, route->rx.metric); + 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); @@ -940,6 +971,5 @@ _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); }