From 1bbdecf5e125a2ae37823f464c75c93f42dcd98a Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Thu, 15 Sep 2022 14:03:51 +0200 Subject: [PATCH 1/5] platform: manage ECMP routes When reading from netlink an ECMP IPv4 route, we need to parse the multiple nexthops. In order to do that, we are introducing NMPlatformIP4RtNextHop struct. The first nexthop information will be kept at the original NMPlatformIP4Route and the new property n_nexthops will indicate how many nexthops we need to consider. --- src/core/platform/tests/test-route.c | 96 +++++------ src/libnm-platform/nm-linux-platform.c | 139 +++++++++++++--- src/libnm-platform/nm-platform.c | 213 +++++++++++++++++++++---- src/libnm-platform/nm-platform.h | 79 ++++++++- src/libnm-platform/nmp-object.c | 171 ++++++++++++++------ src/libnm-platform/nmp-object.h | 7 + 6 files changed, 563 insertions(+), 142 deletions(-) diff --git a/src/core/platform/tests/test-route.c b/src/core/platform/tests/test-route.c index 871263c61..c441f5841 100644 --- a/src/core/platform/tests/test-route.c +++ b/src/core/platform/tests/test-route.c @@ -333,30 +333,33 @@ test_ip4_route(void) /* Test route listing */ routes = nmtstp_ip4_route_get_all(NM_PLATFORM_GET, ifindex); memset(rts, 0, sizeof(rts)); - rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); - rts[0].network = gateway; - rts[0].plen = 32; - rts[0].ifindex = ifindex; - rts[0].gateway = INADDR_ANY; - rts[0].metric = metric; - rts[0].mss = mss; - rts[0].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_LINK); - rts[1].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); - rts[1].network = network; - rts[1].plen = plen; - rts[1].ifindex = ifindex; - rts[1].gateway = gateway; - rts[1].metric = metric; - rts[1].mss = mss; - rts[1].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_UNIVERSE); - rts[2].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); - rts[2].network = 0; - rts[2].plen = 0; - rts[2].ifindex = ifindex; - rts[2].gateway = gateway; - rts[2].metric = metric; - rts[2].mss = mss; - rts[2].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_UNIVERSE); + rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); + rts[0].network = gateway; + rts[0].plen = 32; + rts[0].ifindex = ifindex; + rts[0].gateway = INADDR_ANY; + rts[0].metric = metric; + rts[0].mss = mss; + rts[0].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_LINK); + rts[0].n_nexthops = 1; + rts[1].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); + rts[1].network = network; + rts[1].plen = plen; + rts[1].ifindex = ifindex; + rts[1].gateway = gateway; + rts[1].metric = metric; + rts[1].mss = mss; + rts[1].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_UNIVERSE); + rts[1].n_nexthops = 1; + rts[2].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER); + rts[2].network = 0; + rts[2].plen = 0; + rts[2].ifindex = ifindex; + rts[2].gateway = gateway; + rts[2].metric = metric; + rts[2].mss = mss; + rts[2].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_UNIVERSE); + rts[2].n_nexthops = 1; g_assert_cmpint(routes->len, ==, 3); nmtst_platform_ip4_routes_equal_aptr((const NMPObject *const *) routes->pdata, rts, @@ -635,21 +638,22 @@ test_ip4_route_options(gconstpointer test_data) switch (TEST_IDX) { case 1: rts_add[rts_n++] = ((NMPlatformIP4Route){ - .ifindex = IFINDEX, - .rt_source = NM_IP_CONFIG_SOURCE_USER, - .network = nmtst_inet4_from_string("172.16.1.0"), - .plen = 24, - .metric = 20, - .tos = 0x28, - .window = 10000, - .cwnd = 16, - .initcwnd = 30, - .initrwnd = 50, - .mtu = 1350, - .lock_cwnd = TRUE, - .mss = 1300, - .quickack = TRUE, - .rto_min = 1000, + .ifindex = IFINDEX, + .rt_source = NM_IP_CONFIG_SOURCE_USER, + .network = nmtst_inet4_from_string("172.16.1.0"), + .plen = 24, + .metric = 20, + .tos = 0x28, + .window = 10000, + .cwnd = 16, + .initcwnd = 30, + .initrwnd = 50, + .mtu = 1350, + .lock_cwnd = TRUE, + .mss = 1300, + .quickack = TRUE, + .rto_min = 1000, + .n_nexthops = 1, }); break; case 2: @@ -663,12 +667,13 @@ test_ip4_route_options(gconstpointer test_data) .n_ifa_flags = 0, }); rts_add[rts_n++] = ((NMPlatformIP4Route){ - .ifindex = IFINDEX, - .rt_source = NM_IP_CONFIG_SOURCE_USER, - .network = nmtst_inet4_from_string("172.17.1.0"), - .gateway = nmtst_inet4_from_string("172.16.1.1"), - .plen = 24, - .metric = 20, + .ifindex = IFINDEX, + .rt_source = NM_IP_CONFIG_SOURCE_USER, + .network = nmtst_inet4_from_string("172.17.1.0"), + .gateway = nmtst_inet4_from_string("172.16.1.1"), + .plen = 24, + .metric = 20, + .n_nexthops = 1, }); rts_add[rts_n++] = ((NMPlatformIP4Route){ .ifindex = IFINDEX, @@ -678,6 +683,7 @@ test_ip4_route_options(gconstpointer test_data) .r_rtm_flags = RTNH_F_ONLINK, .plen = 24, .metric = 20, + .n_nexthops = 1, }); break; default: diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c index cc57ce404..4ea67c179 100644 --- a/src/libnm-platform/nm-linux-platform.c +++ b/src/libnm-platform/nm-linux-platform.c @@ -3640,21 +3640,27 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter struct { gboolean found; gboolean has_more; + guint8 weight; int ifindex; NMIPAddr gateway; } nh = { .found = FALSE, .has_more = FALSE, }; - guint32 mss; - guint32 window = 0; - guint32 cwnd = 0; - guint32 initcwnd = 0; - guint32 initrwnd = 0; - guint32 mtu = 0; - guint32 rto_min = 0; - guint32 lock = 0; - gboolean quickack = FALSE; + guint v4_n_nexthops = 0; + NMPlatformIP4RtNextHop v4_nh_extra_nexthops_stack[10]; + gs_free NMPlatformIP4RtNextHop *v4_nh_extra_nexthops_heap = NULL; + NMPlatformIP4RtNextHop *v4_nh_extra_nexthops = v4_nh_extra_nexthops_stack; + guint v4_nh_extra_alloc = G_N_ELEMENTS(v4_nh_extra_nexthops_stack); + guint32 mss; + guint32 window = 0; + guint32 cwnd = 0; + guint32 initcwnd = 0; + guint32 initrwnd = 0; + guint32 mtu = 0; + guint32 rto_min = 0; + guint32 lock = 0; + gboolean quickack = FALSE; nm_assert((parse_nlmsg_iter->iter_more && parse_nlmsg_iter->ip6_route.next_multihop > 0) || (!parse_nlmsg_iter->iter_more && parse_nlmsg_iter->ip6_route.next_multihop == 0)); @@ -3712,9 +3718,57 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter idx = 0; while (TRUE) { - if (idx == multihop_idx) { + if (nh.found && IS_IPv4) { + NMPlatformIP4RtNextHop *new_nexthop; + + /* we parsed the first IPv4 nexthop in "nh", let's parse the following ones. + * + * At this point, v4_n_nexthops still counts how many hops we already added, + * now we are about to add the (v4_n_nexthops+1) hop. + * + * Note that the first hop (of then v4_n_nexthops) is tracked in "nh". + * v4_nh_extra_nexthops tracks the additional hops. + * + * v4_nh_extra_alloc is how many space is allocated for + * v4_nh_extra_nexthops (note that in the end we will only add (v4_n_nexthops-1) + * hops in this list). */ + nm_assert(v4_n_nexthops > 0u); + if (v4_n_nexthops - 1u >= v4_nh_extra_alloc) { + v4_nh_extra_alloc = NM_MAX(4, v4_nh_extra_alloc * 2u); + if (!v4_nh_extra_nexthops_heap) { + v4_nh_extra_nexthops_heap = + g_new(NMPlatformIP4RtNextHop, v4_nh_extra_alloc); + memcpy(v4_nh_extra_nexthops_heap, + v4_nh_extra_nexthops_stack, + G_N_ELEMENTS(v4_nh_extra_nexthops_stack)); + } else { + v4_nh_extra_nexthops_heap = g_renew(NMPlatformIP4RtNextHop, + v4_nh_extra_nexthops_heap, + v4_nh_extra_alloc); + } + v4_nh_extra_nexthops = v4_nh_extra_nexthops_heap; + } + nm_assert(v4_n_nexthops - 1u < v4_nh_extra_alloc); + new_nexthop = &v4_nh_extra_nexthops[v4_n_nexthops - 1u]; + new_nexthop->ifindex = rtnh->rtnh_ifindex; + new_nexthop->weight = NM_MAX(rtnh->rtnh_hops, 1u); + if (rtnh->rtnh_len > sizeof(*rtnh)) { + struct nlattr *ntb[RTA_MAX + 1]; + + if (nla_parse_arr(ntb, + (struct nlattr *) RTNH_DATA(rtnh), + rtnh->rtnh_len - sizeof(*rtnh), + NULL) + < 0) + return NULL; + + if (_check_addr_or_return_null(ntb, RTA_GATEWAY, addr_len)) + memcpy(&new_nexthop->gateway, nla_data(ntb[RTA_GATEWAY]), addr_len); + } + } else if (IS_IPv4 || idx == multihop_idx) { nh.found = TRUE; nh.ifindex = rtnh->rtnh_ifindex; + nh.weight = NM_MAX(rtnh->rtnh_hops, 1u); if (rtnh->rtnh_len > sizeof(*rtnh)) { struct nlattr *ntb[RTA_MAX + 1]; @@ -3731,15 +3785,6 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter } else if (nh.found) { /* we just parsed a nexthop, but there is yet another hop afterwards. */ nm_assert(idx == multihop_idx + 1); - if (IS_IPv4) { - /* for IPv4, multihop routes are currently not supported. - * - * If we ever support them, then the next-hop list is part of the NMPlatformIPRoute, - * that is, for IPv4 we truly have multihop routes. Unlike for IPv6. - * - * For now, just error out. */ - return NULL; - } /* For IPv6 multihop routes, we need to remember to iterate again. * For each next-hop, we will create a distinct single-hop NMPlatformIP6Route. */ @@ -3747,6 +3792,9 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter break; } + if (IS_IPv4) + v4_n_nexthops++; + if (tlen < RTNH_ALIGN(rtnh->rtnh_len) + sizeof(*rtnh)) break; @@ -3780,6 +3828,9 @@ rta_multipath_done: nh.ifindex = ifindex; nh.gateway = gateway; nh.found = TRUE; + nm_assert(v4_n_nexthops == 0); + if (IS_IPv4) + v4_n_nexthops = 1; } else { /* Kernel supports new style nexthop configuration, * verify that it is a duplicate and ignore old-style nexthop. */ @@ -3870,6 +3921,23 @@ rta_multipath_done: obj->ip_route.ifindex = nh.ifindex; + if (IS_IPv4) { + nm_assert((!!nh.found) == (v4_n_nexthops > 0u)); + obj->ip4_route.n_nexthops = v4_n_nexthops; + if (v4_n_nexthops > 1) { + /* We only set the weight for multihop routes. I think that corresponds to what kernel + * does. The weight is mostly undefined for single-hop. */ + obj->ip4_route.weight = NM_MAX(nh.weight, 1u); + + obj->_ip4_route.extra_nexthops = + (v4_nh_extra_alloc == v4_n_nexthops - 1u + && v4_nh_extra_nexthops == v4_nh_extra_nexthops_heap) + ? g_steal_pointer(&v4_nh_extra_nexthops_heap) + : nm_memdup(v4_nh_extra_nexthops, + sizeof(v4_nh_extra_nexthops[0]) * (v4_n_nexthops - 1u)); + } + } + if (_check_addr_or_return_null(tb, RTA_DST, addr_len)) memcpy(obj->ip_route.network_ptr, nla_data(tb[RTA_DST]), addr_len); @@ -5180,6 +5248,39 @@ _nl_msg_new_route(uint16_t nlmsg_type, uint16_t nlmsg_flags, const NMPObject *ob NLA_PUT(msg, RTA_PREFSRC, addr_len, &obj->ip6_route.pref_src); } + if (IS_IPv4 && obj->ip4_route.n_nexthops > 1u) { + struct nlattr *multipath; + guint i; + + if (!(multipath = nla_nest_start(msg, RTA_MULTIPATH))) + goto nla_put_failure; + + for (i = 0u; i < obj->ip4_route.n_nexthops; i++) { + struct rtnexthop *rtnh; + + rtnh = nlmsg_reserve(msg, sizeof(*rtnh), NLMSG_ALIGNTO); + if (!rtnh) + goto nla_put_failure; + + if (i == 0u) { + rtnh->rtnh_hops = NM_MAX(obj->ip4_route.weight, 1u); + rtnh->rtnh_ifindex = obj->ip4_route.ifindex; + NLA_PUT_U32(msg, RTA_GATEWAY, obj->ip4_route.gateway); + } else { + const NMPlatformIP4RtNextHop *n = &obj->_ip4_route.extra_nexthops[i - 1u]; + + rtnh->rtnh_hops = NM_MAX(n->weight, 1u); + rtnh->rtnh_ifindex = n->ifindex; + NLA_PUT_U32(msg, RTA_GATEWAY, n->gateway); + } + + rtnh->rtnh_flags = 0; + rtnh->rtnh_len = (char *) nlmsg_tail(nlmsg_hdr(msg)) - (char *) rtnh; + } + + nla_nest_end(msg, multipath); + } + if (obj->ip_route.mss || obj->ip_route.window || obj->ip_route.cwnd || obj->ip_route.initcwnd || obj->ip_route.initrwnd || obj->ip_route.mtu || obj->ip_route.quickack || obj->ip_route.rto_min || lock) { diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c index f8f715bc2..188db58ed 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -5206,7 +5206,30 @@ nm_platform_ip_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPObject *r int nm_platform_ip4_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP4Route *route) { - return _ip_route_add(self, flags, AF_INET, route); + gs_free NMPlatformIP4RtNextHop *extra_nexthops_free = NULL; + NMPObject obj; + + nm_assert(route); + nm_assert(route->n_nexthops <= 1u || extra_nexthops); + + nmp_object_stackinit(&obj, NMP_OBJECT_TYPE_IP4_ROUTE, (const NMPlatformObject *) route); + + if (route->n_nexthops > 1u) { + nm_assert(extra_nexthops); + /* we need to ensure that @extra_nexthops stays alive until the function returns. + * Copy the buffer. + * + * This is probably not necessary, because likely the caller will somehow ensure that + * the extra_nexthops stay alive. Still do it, because it is a very unusual case and + * likely cheap. */ + obj._ip4_route.extra_nexthops = + nm_memdup_maybe_a(500u, + extra_nexthops, + sizeof(extra_nexthops[0]) * (route->n_nexthops - 1u), + &extra_nexthops_free); + } + + return _ip_route_add(self, flags, &obj); } int @@ -6606,6 +6629,10 @@ _rtm_flags_to_string_full(char *buf, gsize buf_size, unsigned rtm_flags) /** * nm_platform_ip4_route_to_string: * @route: pointer to NMPlatformIP4Route route structure + * @extra_nexthops: (allow-none): the route might be a ECMP multihop route + * (with n_nexthops > 1). In that case, provide the list of extra hops + * to print too. It is allowed for a multihop route to omit the extra hops + * by passing NULL. * @buf: (allow-none): an optional buffer. If %NULL, a static buffer is used. * @len: the size of the @buf. If @buf is %NULL, this argument is ignored. * @@ -6616,30 +6643,40 @@ _rtm_flags_to_string_full(char *buf, gsize buf_size, unsigned rtm_flags) * Returns: a string representation of the route. */ const char * -nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsize len) +nm_platform_ip4_route_to_string_full(const NMPlatformIP4Route *route, + const NMPlatformIP4RtNextHop *extra_nexthops, + char *buf, + gsize len) { - char s_network[INET_ADDRSTRLEN]; - char s_gateway[INET_ADDRSTRLEN]; - char s_pref_src[INET_ADDRSTRLEN]; - char str_dev[30]; - char str_mss[32]; - char str_table[30]; - char str_scope[30]; - char s_source[50]; - char str_tos[32]; - char str_window[32]; - char str_cwnd[32]; - char str_initcwnd[32]; - char str_initrwnd[32]; - char str_rto_min[32]; - char str_mtu[32]; - char str_rtm_flags[_RTM_FLAGS_TO_STRING_MAXLEN]; - char str_type[30]; - char str_metric[30]; + char *buf0; + char s_network[INET_ADDRSTRLEN]; + char s_gateway[INET_ADDRSTRLEN]; + char s_pref_src[INET_ADDRSTRLEN]; + char str_dev[30]; + char str_mss[32]; + char str_table[30]; + char str_scope[30]; + char s_source[50]; + char str_tos[32]; + char str_window[32]; + char str_cwnd[32]; + char str_initcwnd[32]; + char str_initrwnd[32]; + char str_rto_min[32]; + char str_mtu[32]; + char str_rtm_flags[_RTM_FLAGS_TO_STRING_MAXLEN]; + char str_type[30]; + char str_metric[30]; + char weight_str[20]; + guint n_nexthops; if (!nm_utils_to_string_buffer_init_null(route, &buf, &len)) return buf; + buf0 = buf; + + n_nexthops = nm_platform_ip4_route_get_n_nexthops(route); + inet_ntop(AF_INET, &route->network, s_network, sizeof(s_network)); if (route->gateway == 0) @@ -6647,14 +6684,15 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz else inet_ntop(AF_INET, &route->gateway, s_gateway, sizeof(s_gateway)); - g_snprintf( - buf, - len, + nm_strbuf_append( + &buf, + &len, "type %s " /* type */ "%s" /* table */ "%s/%d" "%s%s" /* gateway */ - "%s" + "%s%s" /* weight */ + "%s" /* dev/ifindex */ " metric %s" "%s" /* mss */ " rt-src %s" /* protocol */ @@ -6682,9 +6720,13 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz : ""), s_network, route->plen, - s_gateway[0] ? " via " : "", - s_gateway, - _to_string_dev(str_dev, route->ifindex), + n_nexthops <= 1 && s_gateway[0] ? " via " : "", + n_nexthops <= 1 ? s_gateway : "", + NM_PRINT_FMT_QUOTED2(n_nexthops <= 1 && route->weight != 0, + " weight ", + nm_sprintf_buf(weight_str, "%u", route->weight), + ""), + n_nexthops <= 1 ? _to_string_dev(str_dev, route->ifindex) : "", route->metric_any ? (route->metric ? nm_sprintf_buf(str_metric, "??+%u", route->metric) : "??") : nm_sprintf_buf(str_metric, "%u", route->metric), @@ -6734,7 +6776,56 @@ nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsiz route->mtu) : "", route->r_force_commit ? " force-commit" : ""); - return buf; + + if ((n_nexthops == 1 && route->ifindex > 0) || n_nexthops == 0) { + /* A plain single hop route. Nothing extra to remark. */ + } else { + nm_strbuf_append(&buf, &len, " n_nexthops %u", n_nexthops); + if (n_nexthops > 1) { + nm_strbuf_append(&buf, + &len, + " nexthop" + "%s%s" /* gateway */ + "%s%s" /* weight */ + "%s" /* dev/ifindex */ + "", + s_gateway[0] ? " via " : "", + s_gateway, + NM_PRINT_FMT_QUOTED2(route->weight != 1, + " weight ", + nm_sprintf_buf(weight_str, "%u", route->weight), + ""), + _to_string_dev(str_dev, route->ifindex)); + if (!extra_nexthops) + nm_strbuf_append_str(&buf, &len, " nexthops [...]"); + else { + guint i; + + for (i = 1; i < n_nexthops; i++) { + const NMPlatformIP4RtNextHop *nexthop = &extra_nexthops[i - 1]; + + nm_strbuf_append( + &buf, + &len, + " nexthop" + "%s" /* ifindex */ + "%s%s" /* gateway */ + "%s%s" /* weight */ + "", + NM_PRINT_FMT_QUOTED2(nexthop->gateway != 0 || nexthop->ifindex <= 0, + " via ", + nm_inet4_ntop(nexthop->gateway, s_gateway), + ""), + _to_string_dev(str_dev, nexthop->ifindex), + NM_PRINT_FMT_QUOTED2(nexthop->weight != 1, + " weight ", + nm_sprintf_buf(weight_str, "%u", nexthop->weight), + "")); + } + } + } + } + return buf0; } /** @@ -8072,6 +8163,20 @@ nm_platform_lnk_wireguard_cmp(const NMPlatformLnkWireGuard *a, const NMPlatformL return 0; } +void +nm_platform_ip4_rt_nexthop_hash_update(const NMPlatformIP4RtNextHop *obj, + gboolean for_id, + NMHashState *h) +{ + guint8 w; + + nm_assert(obj); + + w = for_id ? NM_MAX(obj->weight, 1u) : obj->weight; + + nm_hash_update_vals(h, obj->ifindex, obj->gateway, w); +} + void nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, NMPlatformIPRouteCmpType cmp_type, @@ -8101,7 +8206,9 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, obj->ifindex, nmp_utils_ip_config_source_round_trip_rtprot(obj->rt_source), _ip_route_scope_inv_get_normalized(obj), + nm_platform_ip4_route_get_n_nexthops(obj), obj->gateway, + (guint8) NM_MAX(obj->weight, 1u), obj->mss, obj->pref_src, obj->window, @@ -8131,7 +8238,9 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, nm_ip4_addr_clear_host_address(obj->network, obj->plen), obj->plen, obj->metric, + nm_platform_ip4_route_get_n_nexthops(obj), obj->gateway, + (guint8) NM_MAX(obj->weight, 1u), nmp_utils_ip_config_source_round_trip_rtprot(obj->rt_source), _ip_route_scope_inv_get_normalized(obj), obj->tos, @@ -8164,6 +8273,8 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, obj->plen, obj->metric, obj->gateway, + obj->n_nexthops, + obj->weight, obj->rt_source, obj->scope_inv, obj->tos, @@ -8191,6 +8302,30 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, } } +int +nm_platform_ip4_rt_nexthop_cmp(const NMPlatformIP4RtNextHop *a, + const NMPlatformIP4RtNextHop *b, + gboolean for_id) +{ + guint8 w_a; + guint8 w_b; + + /* Note that weight zero is not valid (in kernel). We thus treat + * weight zero usually the same as 1. + * + * Not here for cmp/hash_update functions. These functions check for the exact + * bit-pattern, and not the it means at other places. */ + NM_CMP_SELF(a, b); + NM_CMP_FIELD(a, b, ifindex); + NM_CMP_FIELD(a, b, gateway); + + w_a = for_id ? NM_MAX(a->weight, 1u) : a->weight; + w_b = for_id ? NM_MAX(b->weight, 1u) : b->weight; + NM_CMP_DIRECT(w_a, w_b); + + return 0; +} + int nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, @@ -8215,7 +8350,10 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, nmp_utils_ip_config_source_round_trip_rtprot(b->rt_source)); NM_CMP_DIRECT(_ip_route_scope_inv_get_normalized(a), _ip_route_scope_inv_get_normalized(b)); + NM_CMP_DIRECT(nm_platform_ip4_route_get_n_nexthops(a), + nm_platform_ip4_route_get_n_nexthops(b)); NM_CMP_FIELD(a, b, gateway); + NM_CMP_DIRECT(NM_MAX(a->weight, 1u), NM_MAX(b->weight, 1u)); NM_CMP_FIELD(a, b, mss); NM_CMP_FIELD(a, b, pref_src); NM_CMP_FIELD(a, b, window); @@ -8251,7 +8389,16 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, NM_CMP_FIELD(a, b, plen); NM_CMP_FIELD_UNSAFE(a, b, metric_any); NM_CMP_FIELD(a, b, metric); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) { + NM_CMP_DIRECT(nm_platform_ip4_route_get_n_nexthops(a), + nm_platform_ip4_route_get_n_nexthops(b)); + } else + NM_CMP_FIELD(a, b, n_nexthops); NM_CMP_FIELD(a, b, gateway); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) + NM_CMP_DIRECT(NM_MAX(a->weight, 1u), NM_MAX(b->weight, 1u)); + else + NM_CMP_FIELD(a, b, weight); 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)); @@ -8913,7 +9060,10 @@ log_ip4_route(NMPlatform *self, _LOG3D("signal: route 4 %7s: %s", nm_platform_signal_change_type_to_string(change_type), - nm_platform_ip4_route_to_string(route, sbuf, sizeof(sbuf))); + nmp_object_to_string(NMP_OBJECT_UP_CAST(route), + NMP_OBJECT_TO_STRING_PUBLIC, + sbuf, + sizeof(sbuf))); } static void @@ -8928,7 +9078,10 @@ log_ip6_route(NMPlatform *self, _LOG3D("signal: route 6 %7s: %s", nm_platform_signal_change_type_to_string(change_type), - nm_platform_ip6_route_to_string(route, sbuf, sizeof(sbuf))); + nmp_object_to_string(NMP_OBJECT_UP_CAST(route), + NMP_OBJECT_TO_STRING_PUBLIC, + sbuf, + sizeof(sbuf))); } static void diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h index 9b711254c..6429e1d92 100644 --- a/src/libnm-platform/nm-platform.h +++ b/src/libnm-platform/nm-platform.h @@ -382,9 +382,27 @@ typedef struct { struct _NMPlatformIP4Route { __NMPlatformIPRoute_COMMON; + in_addr_t network; - /* RTA_GATEWAY. The gateway is part of the primary key for a route */ + /* If n_nexthops is zero, the the address has no next hops. That applies + * to certain route types like blackhole. + * If n_nexthops is 1, then the fields "ifindex", "gateway" and "weight" + * are the first next-hop. There are no further nexthops. + * If n_nexthops is greater than 1, the first next hop is in the fields + * "ifindex", "gateway", "weight", and the (n_nexthops-1) hops are in + * NMPObjectIP4Route.extra_nexthops field (outside the NMPlatformIP4Route + * struct). + * + * For convenience, if ifindex > 0 and n_nexthops == 0, we assume that n_nexthops + * is in fact 1. If ifindex is <= 0, n_nexthops must be zero. + * See nm_platform_ip4_route_get_n_nexthops(). */ + guint n_nexthops; + + /* RTA_GATEWAY. The gateway is part of the primary key for a route. + * If n_nexthops is zero, this value is undefined (should be zero). + * If n_nexthops is greater or equal to one, this is the gateway of + * the first hop. */ in_addr_t gateway; /* RTA_PREFSRC (called "src" by iproute2). @@ -412,6 +430,21 @@ struct _NMPlatformIP4Route { * For IPv6 routes, the scope is ignored and kernel always assumes global scope. * Hence, this field is only in NMPlatformIP4Route. */ guint8 scope_inv; + + /* This is the weight of for the first next-hop, in case of n_nexthops > 1. + * + * If n_nexthops is zero, this value is undefined (should be zero). + * If n_nexthops is 1, this also doesn't matter, but it's usually set to + * zero. + * If n_nexthops is greater or equal to one, this is the weight of + * the first hop. + * + * Note that upper layers use this flag to indicate whether this is a multihop route. + * Single-hop, non-ECMP routes will have a weight of zero. + * + * The valid range for weight is 1-255. For convenience, we treat 0 the same + * as 1 for multihop routes. */ + guint8 weight; }; struct _NMPlatformIP6Route { @@ -636,6 +669,14 @@ typedef struct { bool proto_ad : 1; } NMPlatformVFVlan; +typedef struct { + int ifindex; + in_addr_t gateway; + /* The valid range for weight is 1-255. For convenience, we treat 0 the same + * as 1 for multihop routes. */ + guint8 weight; +} NMPlatformIP4RtNextHop; + typedef struct { guint num_vlans; guint32 index; @@ -2129,6 +2170,23 @@ nm_platform_ip4_route_get_effective_metric(const NMPlatformIP4Route *r) : r->metric; } +static inline guint +nm_platform_ip4_route_get_n_nexthops(const NMPlatformIP4Route *r) +{ + /* The first hop of the "n_nexthops" is in NMPlatformIP4Route + * itself. Thus, if the caller only sets ifindex and leaves + * n_nexthops at zero, the number of next hops is still 1 + * (for convenience of the user who wants to initialize a + * single hop route). */ + if (r->n_nexthops >= 1) { + nm_assert(r->ifindex > 0); + return r->n_nexthops; + } + if (r->ifindex > 0) + return 1; + return 0; +} + static inline guint32 nm_platform_ip6_route_get_effective_metric(const NMPlatformIP6Route *r) { @@ -2216,7 +2274,18 @@ const char *nm_platform_lnk_vrf_to_string(const NMPlatformLnkVrf *lnk, char *buf const char *nm_platform_lnk_vxlan_to_string(const NMPlatformLnkVxlan *lnk, char *buf, gsize len); const char * nm_platform_lnk_wireguard_to_string(const NMPlatformLnkWireGuard *lnk, char *buf, gsize len); -const char *nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsize len); + +const char *nm_platform_ip4_route_to_string_full(const NMPlatformIP4Route *route, + const NMPlatformIP4RtNextHop *extra_nexthops, + char *buf, + gsize len); + +static inline const char * +nm_platform_ip4_route_to_string(const NMPlatformIP4Route *route, char *buf, gsize len) +{ + return nm_platform_ip4_route_to_string_full(route, NULL, buf, len); +} + const char *nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsize len); const char * nm_platform_routing_rule_to_string(const NMPlatformRoutingRule *routing_rule, char *buf, gsize len); @@ -2260,6 +2329,9 @@ GHashTable *nm_platform_ip4_address_addr_to_hash(NMPlatform *self, int ifindex); int nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, const NMPlatformIP4Route *b, NMPlatformIPRouteCmpType cmp_type); +int nm_platform_ip4_rt_nexthop_cmp(const NMPlatformIP4RtNextHop *a, + const NMPlatformIP4RtNextHop *b, + gboolean for_id); int nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, NMPlatformIPRouteCmpType cmp_type); @@ -2298,6 +2370,9 @@ void nm_platform_link_hash_update(const NMPlatformLink *obj, NMHashState *h); void nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, NMPlatformIPRouteCmpType cmp_type, NMHashState *h); +void nm_platform_ip4_rt_nexthop_hash_update(const NMPlatformIP4RtNextHop *obj, + gboolean for_id, + NMHashState *h); void nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj, NMPlatformIPRouteCmpType cmp_type, NMHashState *h); diff --git a/src/libnm-platform/nmp-object.c b/src/libnm-platform/nmp-object.c index a6ad5f65b..06457e392 100644 --- a/src/libnm-platform/nmp-object.c +++ b/src/libnm-platform/nmp-object.c @@ -734,6 +734,12 @@ _vt_cmd_obj_dispose_link(NMPObject *obj) nmp_object_unref(obj->_link.netlink.lnk); } +static void +_vt_cmd_obj_dispose_ip4_route(NMPObject *obj) +{ + nm_clear_g_free((gpointer *) &obj->_ip4_route.extra_nexthops); +} + static void _vt_cmd_obj_dispose_lnk_vlan(NMPObject *obj) { @@ -848,14 +854,12 @@ nmp_object_stackinit_id(NMPObject *obj, const NMPObject *src) if (klass->cmd_plobj_id_copy) klass->cmd_plobj_id_copy(&obj->object, &src->object); else { - /* This object must not implement cmd_obj_copy(). - * If it would, it would mean that we require a deep copy - * of the data. As @obj is stack-allocated, it cannot track - * ownership. The caller must not use nmp_object_stackinit_id() - * with an object of such a type. */ - nm_assert(!klass->cmd_obj_copy); - - /* plain memcpy of the public part suffices. */ + /* plain memcpy. + * + * Note that for NMPObjectIP4Route this also copies extra_nexthops + * pointer, aliasing it without taking ownership. That is potentially + * dangerous, but when using a stack allocated instance, you must + * always take care of ownership. */ memcpy(&obj->object, &src->object, klass->sizeof_data); } return obj; @@ -992,6 +996,41 @@ _vt_cmd_obj_to_string_link(const NMPObject *obj, } } +static const char * +_vt_cmd_obj_to_string_ip4_route(const NMPObject *obj, + NMPObjectToStringMode to_string_mode, + char *buf, + gsize buf_size) +{ + const NMPClass *klass; + char buf2[NM_UTILS_TO_STRING_BUFFER_SIZE]; + + klass = NMP_OBJECT_GET_CLASS(obj); + + switch (to_string_mode) { + case NMP_OBJECT_TO_STRING_PUBLIC: + case NMP_OBJECT_TO_STRING_ID: + nm_platform_ip4_route_to_string_full(&obj->ip4_route, + obj->_ip4_route.extra_nexthops, + buf, + buf_size); + return buf; + case NMP_OBJECT_TO_STRING_ALL: + g_snprintf(buf, + buf_size, + "[%s," NM_HASH_OBFUSCATE_PTR_FMT ",%u,%calive,%cvisible; %s]", + klass->obj_type_name, + NM_HASH_OBFUSCATE_PTR(obj), + obj->parent._ref_count, + nmp_object_is_alive(obj) ? '+' : '-', + nmp_object_is_visible(obj) ? '+' : '-', + nmp_object_to_string(obj, NMP_OBJECT_TO_STRING_PUBLIC, buf2, sizeof(buf2))); + return buf; + default: + g_return_val_if_reached("ERROR"); + } +} + static const char * _vt_cmd_obj_to_string_lnk_vlan(const NMPObject *obj, NMPObjectToStringMode to_string_mode, @@ -1197,6 +1236,21 @@ _vt_cmd_obj_hash_update_link(const NMPObject *obj, gboolean for_id, NMHashState nmp_object_hash_update(obj->_link.netlink.lnk, h); } +static void +_vt_cmd_obj_hash_update_ip4_route(const NMPObject *obj, gboolean for_id, NMHashState *h) +{ + guint i; + + nm_assert(NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_IP4_ROUTE); + + nm_platform_ip4_route_hash_update(&obj->ip4_route, + for_id ? NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID + : NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL, + h); + for (i = 1u; i < obj->ip4_route.n_nexthops; i++) + nm_platform_ip4_rt_nexthop_hash_update(&obj->_ip4_route.extra_nexthops[i - 1u], for_id, h); +} + static void _vt_cmd_obj_hash_update_lnk_vlan(const NMPObject *obj, gboolean for_id, NMHashState *h) { @@ -1298,7 +1352,9 @@ nmp_object_cmp_full(const NMPObject *obj1, const NMPObject *obj2, NMPObjectCmpFl } else if (obj1->obj_with_ifindex.ifindex != obj2->obj_with_ifindex.ifindex) { nmp_object_stackinit(&obj_stackcopy, klass->obj_type, &obj2->obj_with_ifindex); obj_stackcopy.obj_with_ifindex.ifindex = obj1->obj_with_ifindex.ifindex; - obj2 = &obj_stackcopy; + if (klass->obj_type == NMP_OBJECT_TYPE_IP4_ROUTE) + obj_stackcopy._ip4_route.extra_nexthops = obj2->_ip4_route.extra_nexthops; + obj2 = &obj_stackcopy; } } @@ -1335,6 +1391,28 @@ _vt_cmd_obj_cmp_link(const NMPObject *obj1, const NMPObject *obj2, gboolean for_ return 0; } +static int +_vt_cmd_obj_cmp_ip4_route(const NMPObject *obj1, const NMPObject *obj2, gboolean for_id) +{ + int c; + guint i; + + c = nm_platform_ip4_route_cmp(&obj1->ip4_route, + &obj2->ip4_route, + for_id ? NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID + : NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL); + NM_CMP_RETURN_DIRECT(c); + + for (i = 1u; i < obj1->ip4_route.n_nexthops; i++) { + c = nm_platform_ip4_rt_nexthop_cmp(&obj1->_ip4_route.extra_nexthops[i - 1u], + &obj2->_ip4_route.extra_nexthops[i - 1u], + for_id); + NM_CMP_RETURN_DIRECT(c); + } + + return 0; +} + static int _vt_cmd_obj_cmp_lnk_vlan(const NMPObject *obj1, const NMPObject *obj2, gboolean for_id) { @@ -1438,6 +1516,28 @@ _vt_cmd_obj_copy_link(NMPObject *dst, const NMPObject *src) dst->_link = src->_link; } +static void +_vt_cmd_obj_copy_ip4_route(NMPObject *dst, const NMPObject *src) +{ + nm_assert(dst != src); + + if (src->ip4_route.n_nexthops <= 1) { + nm_clear_g_free((gpointer *) &dst->_ip4_route.extra_nexthops); + } else if (src->ip4_route.n_nexthops != dst->ip4_route.n_nexthops + || !nm_memeq_n(src->_ip4_route.extra_nexthops, + src->ip4_route.n_nexthops - 1u, + dst->_ip4_route.extra_nexthops, + dst->ip4_route.n_nexthops - 1u, + sizeof(NMPlatformIP4RtNextHop))) { + nm_clear_g_free((gpointer *) &dst->_ip4_route.extra_nexthops); + dst->_ip4_route.extra_nexthops = + nm_memdup(src->_ip4_route.extra_nexthops, + sizeof(NMPlatformIP4RtNextHop) * (src->ip4_route.n_nexthops - 1u)); + } + + dst->ip4_route = src->ip4_route; +} + static void _vt_cmd_obj_copy_lnk_vlan(NMPObject *dst, const NMPObject *src) { @@ -1577,14 +1677,6 @@ _vt_cmd_plobj_id_cmp(tfilter, NMPlatformTfilter, { NM_CMP_FIELD(obj1, obj2, handle); }); -static int -_vt_cmd_plobj_id_cmp_ip4_route(const NMPlatformObject *obj1, const NMPlatformObject *obj2) -{ - return nm_platform_ip4_route_cmp((const NMPlatformIP4Route *) obj1, - (const NMPlatformIP4Route *) obj2, - NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID); -} - static int _vt_cmd_plobj_id_cmp_ip6_route(const NMPlatformObject *obj1, const NMPlatformObject *obj2) { @@ -1673,10 +1765,6 @@ _vt_cmd_plobj_id_hash_update(ip6_address, NMPlatformIP6Address, { obj->address); }); -_vt_cmd_plobj_id_hash_update(ip4_route, NMPlatformIP4Route, { - nm_platform_ip4_route_hash_update(obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, h); -}); - _vt_cmd_plobj_id_hash_update(ip6_route, NMPlatformIP6Route, { nm_platform_ip6_route_hash_update(obj, NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, h); }); @@ -1699,14 +1787,6 @@ _vt_cmd_plobj_id_hash_update(mptcp_addr, NMPlatformMptcpAddr, { nm_hash_update(h, &obj->addr, nm_utils_addr_family_to_size_untrusted(obj->addr_family)); }); -static void -_vt_cmd_plobj_hash_update_ip4_route(const NMPlatformObject *obj, NMHashState *h) -{ - return nm_platform_ip4_route_hash_update((const NMPlatformIP4Route *) obj, - NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL, - h); -} - static void _vt_cmd_plobj_hash_update_ip6_route(const NMPlatformObject *obj, NMHashState *h) { @@ -3218,23 +3298,22 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { }, [NMP_OBJECT_TYPE_IP4_ROUTE - 1] = { - .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), - .obj_type = NMP_OBJECT_TYPE_IP4_ROUTE, - .sizeof_data = sizeof(NMPObjectIP4Route), - .sizeof_public = sizeof(NMPlatformIP4Route), - .obj_type_name = "ip4-route", - .addr_family = AF_INET, - .rtm_gettype = RTM_GETROUTE, - .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, - .signal_type = NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, - .supported_cache_ids = _supported_cache_ids_ipx_route, - .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route, - .cmd_plobj_id_cmp = _vt_cmd_plobj_id_cmp_ip4_route, - .cmd_plobj_id_hash_update = _vt_cmd_plobj_id_hash_update_ip4_route, - .cmd_plobj_to_string_id = (CmdPlobjToStringIdFunc) nm_platform_ip4_route_to_string, - .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_ip4_route_to_string, - .cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_ip4_route, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_ip4_route_cmp_full, + .parent = DEDUP_MULTI_OBJ_CLASS_INIT(), + .obj_type = NMP_OBJECT_TYPE_IP4_ROUTE, + .sizeof_data = sizeof(NMPObjectIP4Route), + .sizeof_public = sizeof(NMPlatformIP4Route), + .obj_type_name = "ip4-route", + .addr_family = AF_INET, + .rtm_gettype = RTM_GETROUTE, + .signal_type_id = NM_PLATFORM_SIGNAL_ID_IP4_ROUTE, + .signal_type = NM_PLATFORM_SIGNAL_IP4_ROUTE_CHANGED, + .supported_cache_ids = _supported_cache_ids_ipx_route, + .cmd_obj_is_alive = _vt_cmd_obj_is_alive_ipx_route, + .cmd_obj_hash_update = _vt_cmd_obj_hash_update_ip4_route, + .cmd_obj_cmp = _vt_cmd_obj_cmp_ip4_route, + .cmd_obj_copy = _vt_cmd_obj_copy_ip4_route, + .cmd_obj_dispose = _vt_cmd_obj_dispose_ip4_route, + .cmd_obj_to_string = _vt_cmd_obj_to_string_ip4_route, }, [NMP_OBJECT_TYPE_IP6_ROUTE - 1] = { diff --git a/src/libnm-platform/nmp-object.h b/src/libnm-platform/nmp-object.h index 090436d21..9fa260b57 100644 --- a/src/libnm-platform/nmp-object.h +++ b/src/libnm-platform/nmp-object.h @@ -314,6 +314,13 @@ typedef struct { typedef struct { NMPlatformIP4Route _public; + + /* The first hop is embedded in _public (in the + * ifindex, gateway and weight fields). + * Only if _public.n_nexthops is greater than 1, then + * this contains the remaining(!!) (_public.n_nexthops - 1) + * extra hops for ECMP multihop routes. */ + const NMPlatformIP4RtNextHop *extra_nexthops; } NMPObjectIP4Route; typedef struct { From 151b2bed361af5209f605c39dc074caffb72f2bf Mon Sep 17 00:00:00 2001 From: Fernando Fernandez Mancera Date: Tue, 18 Oct 2022 18:53:46 +0200 Subject: [PATCH 2/5] platform: pass extra_hops to ip_route_add function When adding a new route we need to consider it contains extra nexthops i.e it is a ECMP route. As we cannot modify the NMPObject once created, we need to pass the extra nexthops as an argument. We cannot use the original NMPObject because normalization is happening during when adding the route. --- src/core/platform/nm-fake-platform.c | 23 ++++---- src/core/platform/tests/test-common.c | 4 +- src/core/platform/tests/test-route.c | 8 +-- src/libnm-platform/nm-linux-platform.c | 16 ++---- src/libnm-platform/nm-platform.c | 73 ++++++++++++++++++-------- src/libnm-platform/nm-platform.h | 10 ++-- 6 files changed, 78 insertions(+), 56 deletions(-) diff --git a/src/core/platform/nm-fake-platform.c b/src/core/platform/nm-fake-platform.c index 8740dbb90..f85a02e15 100644 --- a/src/core/platform/nm-fake-platform.c +++ b/src/core/platform/nm-fake-platform.c @@ -1092,10 +1092,7 @@ object_delete(NMPlatform *platform, const NMPObject *obj) } static int -ip_route_add(NMPlatform *platform, - NMPNlmFlags flags, - int addr_family, - const NMPlatformIPRoute *route) +ip_route_add(NMPlatform *platform, NMPNlmFlags flags, NMPObject *obj_stack) { NMDedupMultiIter iter; nm_auto_nmpobj NMPObject *obj = NULL; @@ -1109,23 +1106,29 @@ ip_route_add(NMPlatform *platform, NMPlatformIPRoute *r = NULL; NMPlatformIP4Route *r4 = NULL; NMPlatformIP6Route *r6 = NULL; + int addr_family; gboolean has_same_weak_id; gboolean only_dirty; guint16 nlmsgflags; - g_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6)); + g_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_stack), + NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); + + addr_family = NMP_OBJECT_GET_ADDR_FAMILY(obj_stack); flags = NM_FLAGS_UNSET(flags, NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE); /* currently, only replace is implemented. */ g_assert(flags == NMP_NLM_FLAG_REPLACE); - obj = nmp_object_new(addr_family == AF_INET ? NMP_OBJECT_TYPE_IP4_ROUTE - : NMP_OBJECT_TYPE_IP6_ROUTE, - (const NMPlatformObject *) route); - r = NMP_OBJECT_CAST_IP_ROUTE(obj); + if (NMP_OBJECT_GET_TYPE(obj_stack) == NMP_OBJECT_TYPE_IP4_ROUTE + && obj_stack->ip4_route.n_nexthops == 0 && obj_stack->ip4_route.ifindex > 0) + obj_stack->ip4_route.n_nexthops = 1; - nm_platform_ip_route_normalize(addr_family, r); + obj = nmp_object_clone(obj_stack, FALSE); + + r = NMP_OBJECT_CAST_IP_ROUTE(obj); switch (addr_family) { case AF_INET: diff --git a/src/core/platform/tests/test-common.c b/src/core/platform/tests/test-common.c index 09e7bd6c9..5049bc518 100644 --- a/src/core/platform/tests/test-common.c +++ b/src/core/platform/tests/test-common.c @@ -1250,8 +1250,8 @@ nmtstp_ip4_route_add(NMPlatform *platform, route.metric = metric; route.mss = mss; - g_assert( - NMTST_NM_ERR_SUCCESS(nm_platform_ip4_route_add(platform, NMP_NLM_FLAG_REPLACE, &route))); + g_assert(NMTST_NM_ERR_SUCCESS( + nm_platform_ip4_route_add(platform, NMP_NLM_FLAG_REPLACE, &route, NULL))); } void diff --git a/src/core/platform/tests/test-route.c b/src/core/platform/tests/test-route.c index c441f5841..1bf6eb516 100644 --- a/src/core/platform/tests/test-route.c +++ b/src/core/platform/tests/test-route.c @@ -713,7 +713,7 @@ test_ip4_route_options(gconstpointer test_data) for (i = 0; i < rts_n; i++) g_assert(NMTST_NM_ERR_SUCCESS( - nm_platform_ip4_route_add(NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &rts_add[i]))); + nm_platform_ip4_route_add(NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &rts_add[i], NULL))); for (i = 0; i < rts_n; i++) { rts_cmp[i] = rts_add[i]; @@ -988,8 +988,8 @@ again_find_idx: order_idx[order_len++] = idx; r->ifindex = iface_data[idx].ifindex; - g_assert( - NMTST_NM_ERR_SUCCESS(nm_platform_ip4_route_add(platform, NMP_NLM_FLAG_APPEND, r))); + g_assert(NMTST_NM_ERR_SUCCESS( + nm_platform_ip4_route_add(platform, NMP_NLM_FLAG_APPEND, r, NULL))); } else { i = nmtst_get_rand_uint32() % order_len; idx = order_idx[i]; @@ -1936,7 +1936,7 @@ test_blackhole(gconstpointer test_data) nm_platform_ip_route_normalize(addr_family, &rr.rx); if (IS_IPv4) - r = nm_platform_ip4_route_add(NM_PLATFORM_GET, NMP_NLM_FLAG_APPEND, &rr.r4); + r = nm_platform_ip4_route_add(NM_PLATFORM_GET, NMP_NLM_FLAG_APPEND, &rr.r4, NULL); else r = nm_platform_ip6_route_add(NM_PLATFORM_GET, NMP_NLM_FLAG_APPEND, &rr.r6); diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c index 4ea67c179..a7442e4ac 100644 --- a/src/libnm-platform/nm-linux-platform.c +++ b/src/libnm-platform/nm-linux-platform.c @@ -9427,25 +9427,15 @@ ip6_address_delete(NMPlatform *platform, int ifindex, struct in6_addr addr, guin /*****************************************************************************/ static int -ip_route_add(NMPlatform *platform, - NMPNlmFlags flags, - int addr_family, - const NMPlatformIPRoute *route) +ip_route_add(NMPlatform *platform, NMPNlmFlags flags, NMPObject *obj_stack) { nm_auto_nlmsg struct nl_msg *nlmsg = NULL; - NMPObject obj; - nmp_object_stackinit(&obj, - NMP_OBJECT_TYPE_IP_ROUTE(NM_IS_IPv4(addr_family)), - (const NMPlatformObject *) route); - - nm_platform_ip_route_normalize(addr_family, NMP_OBJECT_CAST_IP_ROUTE(&obj)); - - nlmsg = _nl_msg_new_route(RTM_NEWROUTE, flags & NMP_NLM_FLAG_FMASK, &obj); + nlmsg = _nl_msg_new_route(RTM_NEWROUTE, flags & NMP_NLM_FLAG_FMASK, obj_stack); if (!nlmsg) g_return_val_if_reached(-NME_BUG); return do_add_addrroute(platform, - &obj, + obj_stack, nlmsg, NM_FLAGS_HAS(flags, NMP_NLM_FLAG_SUPPRESS_NETLINK_FAILURE)); } diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c index 188db58ed..590c4c8ec 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -5164,47 +5164,73 @@ nm_platform_ip_route_normalize(int addr_family, NMPlatformIPRoute *route) } static int -_ip_route_add(NMPlatform *self, NMPNlmFlags flags, int addr_family, gconstpointer route) +_ip_route_add(NMPlatform *self, NMPNlmFlags flags, NMPObject *obj_stack) { char sbuf[NM_UTILS_TO_STRING_BUFFER_SIZE]; int ifindex; _CHECK_SELF(self, klass, FALSE); - nm_assert(route); - nm_assert(NM_IN_SET(addr_family, AF_INET, AF_INET6)); + /* The caller already ensures that this is a stack allocated copy, that + * - stays alive for the duration of the call. + * - that the ip_route_add() implementation is allowed to modify. + */ + nm_assert(obj_stack); + nm_assert(NMP_OBJECT_IS_STACKINIT(obj_stack)); + nm_assert(NM_IN_SET(NMP_OBJECT_GET_TYPE(obj_stack), + NMP_OBJECT_TYPE_IP4_ROUTE, + NMP_OBJECT_TYPE_IP6_ROUTE)); + + nm_assert(NMP_OBJECT_GET_TYPE(obj_stack) != NMP_OBJECT_TYPE_IP4_ROUTE + || obj_stack->ip4_route.n_nexthops <= 1u || obj_stack->_ip4_route.extra_nexthops); + + nm_platform_ip_route_normalize(NMP_OBJECT_GET_ADDR_FAMILY((obj_stack)), + NMP_OBJECT_CAST_IP_ROUTE(obj_stack)); + + ifindex = obj_stack->ip_route.ifindex; - ifindex = ((const NMPlatformIPRoute *) route)->ifindex; _LOG3D("route: %-10s IPv%c route: %s", _nmp_nlm_flag_to_string(flags & NMP_NLM_FLAG_FMASK), - nm_utils_addr_family_to_char(addr_family), - NM_IS_IPv4(addr_family) ? nm_platform_ip4_route_to_string(route, sbuf, sizeof(sbuf)) - : nm_platform_ip6_route_to_string(route, sbuf, sizeof(sbuf))); + nm_utils_addr_family_to_char(NMP_OBJECT_GET_ADDR_FAMILY(obj_stack)), + nmp_object_to_string(obj_stack, NMP_OBJECT_TO_STRING_PUBLIC, sbuf, sizeof(sbuf))); - return klass->ip_route_add(self, flags, addr_family, route); + /* At this point, we pass "obj_stack" to the klass->ip_route_add() implementation. + * The callee can rely on: + * - the object being normalized and validated. + * - staying fully alive until the function returns. In this case it + * is stack allocated (and the potential "extra_nexthops" array is + * guaranteed to stay alive too). + */ + return klass->ip_route_add(self, flags, obj_stack); } int -nm_platform_ip_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPObject *route) +nm_platform_ip_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPObject *obj) { - int addr_family; + nm_auto_nmpobj const NMPObject *obj_keep_alive = NULL; + NMPObject obj_stack; - 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); + nm_assert( + NM_IN_SET(NMP_OBJECT_GET_TYPE(obj), NMP_OBJECT_TYPE_IP4_ROUTE, NMP_OBJECT_TYPE_IP6_ROUTE)); + + nmp_object_stackinit(&obj_stack, NMP_OBJECT_GET_TYPE(obj), &obj->ip_route); + + if (NMP_OBJECT_GET_TYPE(obj) == NMP_OBJECT_TYPE_IP4_ROUTE && obj->ip4_route.n_nexthops > 1u) { + /* Ensure @obj stays alive, so we can alias extra_nexthops from the stackallocated + * @obj_stack. */ + nm_assert(obj->_ip4_route.extra_nexthops); + obj_keep_alive = nmp_object_ref(obj); + obj_stack._ip4_route.extra_nexthops = obj->_ip4_route.extra_nexthops; } - return _ip_route_add(self, flags, addr_family, NMP_OBJECT_CAST_IP_ROUTE(route)); + return _ip_route_add(self, flags, &obj_stack); } int -nm_platform_ip4_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP4Route *route) +nm_platform_ip4_route_add(NMPlatform *self, + NMPNlmFlags flags, + const NMPlatformIP4Route *route, + const NMPlatformIP4RtNextHop *extra_nexthops) { gs_free NMPlatformIP4RtNextHop *extra_nexthops_free = NULL; NMPObject obj; @@ -5235,7 +5261,10 @@ nm_platform_ip4_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformI int nm_platform_ip6_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP6Route *route) { - return _ip_route_add(self, flags, AF_INET6, route); + NMPObject obj; + + nmp_object_stackinit(&obj, NMP_OBJECT_TYPE_IP6_ROUTE, (const NMPlatformObject *) route); + return _ip_route_add(self, flags, &obj); } gboolean diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h index 6429e1d92..3a6f9a9f2 100644 --- a/src/libnm-platform/nm-platform.h +++ b/src/libnm-platform/nm-platform.h @@ -1206,10 +1206,7 @@ typedef struct { struct in6_addr address, guint8 plen); - int (*ip_route_add)(NMPlatform *self, - NMPNlmFlags flags, - int addr_family, - const NMPlatformIPRoute *route); + int (*ip_route_add)(NMPlatform *self, NMPNlmFlags flags, NMPObject *obj_stack); int (*ip_route_get)(NMPlatform *self, int addr_family, gconstpointer address, @@ -2220,7 +2217,10 @@ nm_platform_ip_route_get_gateway(int addr_family, const NMPlatformIPRoute *route } int nm_platform_ip_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPObject *route); -int nm_platform_ip4_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP4Route *route); +int nm_platform_ip4_route_add(NMPlatform *self, + NMPNlmFlags flags, + const NMPlatformIP4Route *route, + const NMPlatformIP4RtNextHop *extra_nexthops); int nm_platform_ip6_route_add(NMPlatform *self, NMPNlmFlags flags, const NMPlatformIP6Route *route); GPtrArray *nm_platform_ip_route_get_prune_list(NMPlatform *self, From 9270bf611f374038c0504860d153e4bfc50063c3 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 8 Nov 2022 15:54:48 +0100 Subject: [PATCH 3/5] platform: add nm_platform_ip4_route_hash() helper --- src/libnm-platform/nm-platform.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h index 3a6f9a9f2..339d804c9 100644 --- a/src/libnm-platform/nm-platform.h +++ b/src/libnm-platform/nm-platform.h @@ -2370,6 +2370,17 @@ void nm_platform_link_hash_update(const NMPlatformLink *obj, NMHashState *h); void nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, NMPlatformIPRouteCmpType cmp_type, NMHashState *h); + +static inline guint +nm_platform_ip4_route_hash(const NMPlatformIP4Route *obj, NMPlatformIPRouteCmpType cmp_type) +{ + NMHashState h; + + nm_hash_init(&h, 1118769853u); + nm_platform_ip4_route_hash_update(obj, cmp_type, &h); + return nm_hash_complete(&h); +} + void nm_platform_ip4_rt_nexthop_hash_update(const NMPlatformIP4RtNextHop *obj, gboolean for_id, NMHashState *h); From 8cc41d41feca14563ea1bbd755c46c6495ba6e0f Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Tue, 8 Nov 2022 15:56:56 +0100 Subject: [PATCH 4/5] platform: add NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID for comparing ECMP base route --- src/libnm-platform/nm-platform.c | 93 +++++++++++++++++--------------- src/libnm-platform/nm-platform.h | 5 ++ 2 files changed, 56 insertions(+), 42 deletions(-) diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c index 590c4c8ec..c5fcf50bc 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -8213,6 +8213,8 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, { switch (cmp_type) { case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID: + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: nm_hash_update_vals( h, nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)), @@ -8221,42 +8223,38 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj, obj->metric, obj->tos, NM_HASH_COMBINE_BOOLS(guint8, obj->metric_any, obj->table_any)); - break; - case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: - nm_hash_update_vals( - h, - obj->type_coerced, - nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)), - nm_ip4_addr_clear_host_address(obj->network, obj->plen), - obj->plen, - obj->metric, - obj->tos, - /* on top of WEAK_ID: */ - obj->ifindex, - nmp_utils_ip_config_source_round_trip_rtprot(obj->rt_source), - _ip_route_scope_inv_get_normalized(obj), - nm_platform_ip4_route_get_n_nexthops(obj), - obj->gateway, - (guint8) NM_MAX(obj->weight, 1u), - obj->mss, - obj->pref_src, - obj->window, - obj->cwnd, - obj->initcwnd, - obj->initrwnd, - obj->mtu, - obj->rto_min, - obj->r_rtm_flags & RTNH_F_ONLINK, - NM_HASH_COMBINE_BOOLS(guint16, - obj->metric_any, - obj->table_any, - obj->quickack, - obj->lock_window, - obj->lock_cwnd, - obj->lock_initcwnd, - obj->lock_initrwnd, - obj->lock_mtu, - obj->lock_mss)); + if (NM_IN_SET(cmp_type, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID)) { + nm_hash_update_vals(h, + obj->type_coerced, + nmp_utils_ip_config_source_round_trip_rtprot(obj->rt_source), + _ip_route_scope_inv_get_normalized(obj), + obj->mss, + obj->pref_src, + obj->window, + obj->cwnd, + obj->initcwnd, + obj->initrwnd, + obj->mtu, + obj->rto_min, + obj->r_rtm_flags & RTNH_F_ONLINK, + NM_HASH_COMBINE_BOOLS(guint16, + obj->quickack, + obj->lock_window, + obj->lock_cwnd, + obj->lock_initcwnd, + obj->lock_initrwnd, + obj->lock_mtu, + obj->lock_mss)); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { + nm_hash_update_vals(h, + obj->ifindex, + nm_platform_ip4_route_get_n_nexthops(obj), + obj->gateway, + (guint8) NM_MAX(obj->weight, 1u)); + } + } break; case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: nm_hash_update_vals( @@ -8362,6 +8360,7 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, { NM_CMP_SELF(a, b); switch (cmp_type) { + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID: case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: NM_CMP_FIELD_UNSAFE(a, b, table_any); @@ -8372,17 +8371,14 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, NM_CMP_FIELD_UNSAFE(a, b, metric_any); NM_CMP_FIELD(a, b, metric); NM_CMP_FIELD(a, b, tos); - if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { - NM_CMP_FIELD(a, b, ifindex); + if (NM_IN_SET(cmp_type, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID)) { NM_CMP_FIELD(a, b, type_coerced); NM_CMP_DIRECT(nmp_utils_ip_config_source_round_trip_rtprot(a->rt_source), nmp_utils_ip_config_source_round_trip_rtprot(b->rt_source)); NM_CMP_DIRECT(_ip_route_scope_inv_get_normalized(a), _ip_route_scope_inv_get_normalized(b)); - NM_CMP_DIRECT(nm_platform_ip4_route_get_n_nexthops(a), - nm_platform_ip4_route_get_n_nexthops(b)); - NM_CMP_FIELD(a, b, gateway); - NM_CMP_DIRECT(NM_MAX(a->weight, 1u), NM_MAX(b->weight, 1u)); NM_CMP_FIELD(a, b, mss); NM_CMP_FIELD(a, b, pref_src); NM_CMP_FIELD(a, b, window); @@ -8399,6 +8395,13 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a, NM_CMP_FIELD_UNSAFE(a, b, lock_initrwnd); NM_CMP_FIELD_UNSAFE(a, b, lock_mtu); NM_CMP_FIELD_UNSAFE(a, b, lock_mss); + if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { + NM_CMP_FIELD(a, b, ifindex); + NM_CMP_FIELD(a, b, gateway); + NM_CMP_DIRECT(NM_MAX(a->weight, 1u), NM_MAX(b->weight, 1u)); + NM_CMP_DIRECT(nm_platform_ip4_route_get_n_nexthops(a), + nm_platform_ip4_route_get_n_nexthops(b)); + } } break; case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: @@ -8484,6 +8487,9 @@ nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj, obj->src_plen, NM_HASH_COMBINE_BOOLS(guint8, obj->metric_any, obj->table_any)); break; + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID: + nm_assert_not_reached(); + /* fall-through */ case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: nm_hash_update_vals( h, @@ -8576,6 +8582,9 @@ nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a, { NM_CMP_SELF(a, b); switch (cmp_type) { + case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID: + nm_assert_not_reached(); + /* fall-through */ case NM_PLATFORM_IP_ROUTE_CMP_TYPE_WEAK_ID: case NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID: NM_CMP_FIELD_UNSAFE(a, b, table_any); diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h index 339d804c9..b4bbc64e2 100644 --- a/src/libnm-platform/nm-platform.h +++ b/src/libnm-platform/nm-platform.h @@ -102,6 +102,11 @@ typedef enum { */ NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID, + /* IPv4 route can have multiple hops. This is the ID, by which multiple + * routes are merged according to the next hop. This is basically NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID + * which ignores the next hops. */ + NM_PLATFORM_IP_ROUTE_CMP_TYPE_ECMP_ID, + /* compare all fields as they make sense for kernel. For example, * a route destination 192.168.1.5/24 is not accepted by kernel and * we treat it identical to 192.168.1.0/24. Semantically these From 48d7d1d78eb27a7fb35ff84d9d208e5122a93940 Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Mon, 21 Nov 2022 17:44:07 +0100 Subject: [PATCH 5/5] platform: drop inline cmp() wrappers around "full" versions We sometimes have functions foo() and foo_full(), in which case foo() has fewer arguments and just calls foo_full(). The "full" function here is the more powerful one, and foo() is implemented in terms of the former. nm_platform_ip4_route_cmp_full() and m_platform_ip4_route_cmp() inverted that pattern. The "_full" there stands for the full comparison, to not allowing to select the comparison type. That inconsistency is ugly. Also, these wrappers were used at only few places. Let's drop them. While at it, also drop nm_platform_qdisc_cmp() and rename nm_platform_qdisc_cmp_full(). Here cmp()/cmp_full() followed the common pattern foo()/foo_full(), but it's still hardly used and unnecessary. --- src/core/nm-test-utils-core.h | 14 ++++++++------ src/libnm-platform/nm-platform.c | 10 +--------- src/libnm-platform/nm-platform.h | 23 ++--------------------- src/libnm-platform/nmp-object.c | 32 +++++++++++++++++++++++++++++--- 4 files changed, 40 insertions(+), 39 deletions(-) diff --git a/src/core/nm-test-utils-core.h b/src/core/nm-test-utils-core.h index 725f58d2c..e341d62f1 100644 --- a/src/core/nm-test-utils-core.h +++ b/src/core/nm-test-utils-core.h @@ -178,8 +178,9 @@ nmtst_platform_ip6_route_full(const char *network, static inline int _nmtst_platform_ip4_routes_equal_sort(gconstpointer a, gconstpointer b, gpointer user_data) { - return nm_platform_ip4_route_cmp_full((const NMPlatformIP4Route *) a, - (const NMPlatformIP4Route *) b); + return nm_platform_ip4_route_cmp((const NMPlatformIP4Route *) a, + (const NMPlatformIP4Route *) b, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL); } static inline void @@ -210,7 +211,7 @@ nmtst_platform_ip4_routes_equal(const NMPlatformIP4Route *a, } for (i = 0; i < len; i++) { - if (nm_platform_ip4_route_cmp_full(&a[i], &b[i]) != 0) { + if (nm_platform_ip4_route_cmp(&a[i], &b[i], NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) != 0) { char buf1[NM_UTILS_TO_STRING_BUFFER_SIZE]; char buf2[NM_UTILS_TO_STRING_BUFFER_SIZE]; @@ -248,8 +249,9 @@ nmtst_platform_ip4_routes_equal_aptr(const NMPObject *const *a, static inline int _nmtst_platform_ip6_routes_equal_sort(gconstpointer a, gconstpointer b, gpointer user_data) { - return nm_platform_ip6_route_cmp_full((const NMPlatformIP6Route *) a, - (const NMPlatformIP6Route *) b); + return nm_platform_ip6_route_cmp((const NMPlatformIP6Route *) a, + (const NMPlatformIP6Route *) b, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL); } static inline void @@ -280,7 +282,7 @@ nmtst_platform_ip6_routes_equal(const NMPlatformIP6Route *a, } for (i = 0; i < len; i++) { - if (nm_platform_ip6_route_cmp_full(&a[i], &b[i]) != 0) { + if (nm_platform_ip6_route_cmp(&a[i], &b[i], NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL) != 0) { char buf1[NM_UTILS_TO_STRING_BUFFER_SIZE]; char buf2[NM_UTILS_TO_STRING_BUFFER_SIZE]; diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c index c5fcf50bc..d28e8771f 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -7316,9 +7316,7 @@ nm_platform_qdisc_hash_update(const NMPlatformQdisc *obj, NMHashState *h) } int -nm_platform_qdisc_cmp_full(const NMPlatformQdisc *a, - const NMPlatformQdisc *b, - gboolean compare_handle) +nm_platform_qdisc_cmp(const NMPlatformQdisc *a, const NMPlatformQdisc *b, gboolean compare_handle) { NM_CMP_SELF(a, b); NM_CMP_FIELD(a, b, ifindex); @@ -7355,12 +7353,6 @@ nm_platform_qdisc_cmp_full(const NMPlatformQdisc *a, return 0; } -int -nm_platform_qdisc_cmp(const NMPlatformQdisc *a, const NMPlatformQdisc *b) -{ - return nm_platform_qdisc_cmp_full(a, b, TRUE); -} - const char * nm_platform_tfilter_to_string(const NMPlatformTfilter *tfilter, char *buf, gsize len) { diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h index b4bbc64e2..32773b67a 100644 --- a/src/libnm-platform/nm-platform.h +++ b/src/libnm-platform/nm-platform.h @@ -2341,32 +2341,13 @@ int nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a, const NMPlatformIP6Route *b, NMPlatformIPRouteCmpType cmp_type); -static inline int -nm_platform_ip4_route_cmp_full(const NMPlatformIP4Route *a, const NMPlatformIP4Route *b) -{ - return nm_platform_ip4_route_cmp(a, b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL); -} - -static inline int -nm_platform_ip6_route_cmp_full(const NMPlatformIP6Route *a, const NMPlatformIP6Route *b) -{ - return nm_platform_ip6_route_cmp(a, b, NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL); -} - int nm_platform_routing_rule_cmp(const NMPlatformRoutingRule *a, const NMPlatformRoutingRule *b, NMPlatformRoutingRuleCmpType cmp_type); -static inline int -nm_platform_routing_rule_cmp_full(const NMPlatformRoutingRule *a, const NMPlatformRoutingRule *b) -{ - return nm_platform_routing_rule_cmp(a, b, NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL); -} +int +nm_platform_qdisc_cmp(const NMPlatformQdisc *a, const NMPlatformQdisc *b, gboolean compare_handle); -int nm_platform_qdisc_cmp(const NMPlatformQdisc *a, const NMPlatformQdisc *b); -int nm_platform_qdisc_cmp_full(const NMPlatformQdisc *a, - const NMPlatformQdisc *b, - gboolean compare_handle); int nm_platform_tfilter_cmp(const NMPlatformTfilter *a, const NMPlatformTfilter *b); int nm_platform_mptcp_addr_cmp(const NMPlatformMptcpAddr *a, const NMPlatformMptcpAddr *b); diff --git a/src/libnm-platform/nmp-object.c b/src/libnm-platform/nmp-object.c index 06457e392..c95dd6ed3 100644 --- a/src/libnm-platform/nmp-object.c +++ b/src/libnm-platform/nmp-object.c @@ -1795,6 +1795,14 @@ _vt_cmd_plobj_hash_update_ip6_route(const NMPlatformObject *obj, NMHashState *h) h); } +static int +_vt_cmd_plobj_cmp_ip6_route(const NMPlatformObject *obj1, const NMPlatformObject *obj2) +{ + return nm_platform_ip6_route_cmp((const NMPlatformIP6Route *) obj1, + (const NMPlatformIP6Route *) obj2, + NM_PLATFORM_IP_ROUTE_CMP_TYPE_FULL); +} + static void _vt_cmd_plobj_hash_update_routing_rule(const NMPlatformObject *obj, NMHashState *h) { @@ -1803,6 +1811,24 @@ _vt_cmd_plobj_hash_update_routing_rule(const NMPlatformObject *obj, NMHashState h); } +static inline int +_vt_cmd_plobj_cmp_routing_rule(const NMPlatformObject *obj1, const NMPlatformObject *obj2) +{ + return nm_platform_routing_rule_cmp((const NMPlatformRoutingRule *) obj1, + (const NMPlatformRoutingRule *) obj2, + NM_PLATFORM_ROUTING_RULE_CMP_TYPE_FULL); +} + +static int +_vt_cmd_plobj_cmp_qdisc(const NMPlatformObject *obj1, const NMPlatformObject *obj2) +{ + return nm_platform_qdisc_cmp((const NMPlatformQdisc *) obj1, + (const NMPlatformQdisc *) obj2, + TRUE); +} + +/*****************************************************************************/ + guint nmp_object_indirect_id_hash(gconstpointer a) { @@ -3333,7 +3359,7 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_to_string_id = (CmdPlobjToStringIdFunc) nm_platform_ip6_route_to_string, .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_ip6_route_to_string, .cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_ip6_route, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_ip6_route_cmp_full, + .cmd_plobj_cmp = _vt_cmd_plobj_cmp_ip6_route, }, [NMP_OBJECT_TYPE_ROUTING_RULE - 1] = { @@ -3352,7 +3378,7 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_to_string_id = (CmdPlobjToStringIdFunc) nm_platform_routing_rule_to_string, .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_routing_rule_to_string, .cmd_plobj_hash_update = _vt_cmd_plobj_hash_update_routing_rule, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_routing_rule_cmp_full, + .cmd_plobj_cmp = _vt_cmd_plobj_cmp_routing_rule, }, [NMP_OBJECT_TYPE_QDISC - 1] = { @@ -3371,7 +3397,7 @@ const NMPClass _nmp_classes[NMP_OBJECT_TYPE_MAX] = { .cmd_plobj_to_string_id = _vt_cmd_plobj_to_string_id_qdisc, .cmd_plobj_to_string = (CmdPlobjToStringFunc) nm_platform_qdisc_to_string, .cmd_plobj_hash_update = (CmdPlobjHashUpdateFunc) nm_platform_qdisc_hash_update, - .cmd_plobj_cmp = (CmdPlobjCmpFunc) nm_platform_qdisc_cmp, + .cmd_plobj_cmp = _vt_cmd_plobj_cmp_qdisc, }, [NMP_OBJECT_TYPE_TFILTER - 1] = {