diff --git a/src/libnm-platform/nm-linux-platform.c b/src/libnm-platform/nm-linux-platform.c index fad1032e13..3e4c3792f4 100644 --- a/src/libnm-platform/nm-linux-platform.c +++ b/src/libnm-platform/nm-linux-platform.c @@ -4066,6 +4066,7 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter [RTA_PRIORITY] = {.type = NLA_U32}, [RTA_PREF] = {.type = NLA_U8}, [RTA_FLOW] = {.type = NLA_U32}, + [RTA_NH_ID] = {.type = NLA_U32}, [RTA_CACHEINFO] = {.minlen = nm_offsetofend(struct rta_cacheinfo, rta_tsage)}, [RTA_VIA] = {.minlen = nm_offsetofend(struct rtvia, rtvia_family)}, [RTA_METRICS] = {.type = NLA_NESTED}, @@ -4103,6 +4104,7 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter guint32 mtu = 0; guint32 rto_min = 0; guint32 lock = 0; + guint32 nhid = 0; gboolean quickack = FALSE; gboolean rto_min_set = FALSE; @@ -4148,7 +4150,7 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter if (rtm->rtm_dst_len > (IS_IPv4 ? 32 : 128)) return NULL; - if (tb[RTA_MULTIPATH]) { + if (!tb[RTA_NH_ID] && tb[RTA_MULTIPATH]) { size_t tlen; struct rtnexthop *rtnh; guint idx; @@ -4257,7 +4259,7 @@ rta_multipath_done: return nm_assert_unreachable_val(NULL); } - if (tb[RTA_OIF] || tb[RTA_GATEWAY] || tb[RTA_FLOW] || tb[RTA_VIA]) { + if (!tb[RTA_NH_ID] && (tb[RTA_OIF] || tb[RTA_GATEWAY] || tb[RTA_FLOW] || tb[RTA_VIA])) { int ifindex = 0; NMIPAddr gateway = {}; @@ -4303,6 +4305,10 @@ rta_multipath_done: } } + if (tb[RTA_NH_ID]) { + nhid = nla_get_u32(tb[RTA_NH_ID]); + } + if (nm_platform_route_type_is_nodev(rtm->rtm_type)) { /* These routes are special. They don't have an device/ifindex. * @@ -4439,6 +4445,9 @@ rta_multipath_done: obj->ip6_route.src_plen = rtm->rtm_src_len; } + if (!IS_IPv4) + obj->ip6_route.nhid = nhid; + obj->ip_route.mss = mss; obj->ip_route.window = window; obj->ip_route.cwnd = cwnd; @@ -5982,10 +5991,14 @@ _nl_msg_new_route(uint16_t nlmsg_type, uint16_t nlmsg_flags, const NMPObject *ob NLA_PUT(msg, RTA_GATEWAY, addr_len, &obj->ip4_route.gateway); } } else { - if (!IN6_IS_ADDR_UNSPECIFIED(&obj->ip6_route.gateway)) + if (obj->ip6_route.nhid != 0) { + NLA_PUT_U32(msg, RTA_NH_ID, obj->ip6_route.nhid); + } else if (!IN6_IS_ADDR_UNSPECIFIED(&obj->ip6_route.gateway)) NLA_PUT(msg, RTA_GATEWAY, addr_len, &obj->ip6_route.gateway); } - NLA_PUT_U32(msg, RTA_OIF, obj->ip_route.ifindex); + + if (IS_IPv4 || obj->ip6_route.nhid == 0) + NLA_PUT_U32(msg, RTA_OIF, obj->ip_route.ifindex); if (!IS_IPv4 && obj->ip6_route.rt_pref != NM_ICMPV6_ROUTER_PREF_MEDIUM) NLA_PUT_U8(msg, RTA_PREF, obj->ip6_route.rt_pref); diff --git a/src/libnm-platform/nm-platform.c b/src/libnm-platform/nm-platform.c index 4586d79055..be59ed69eb 100644 --- a/src/libnm-platform/nm-platform.c +++ b/src/libnm-platform/nm-platform.c @@ -7521,33 +7521,37 @@ nm_platform_ip6_nexthop_to_string(const NMPlatformIP6NextHop *nexthop, char *buf const char * nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsize len) { - char s_network[INET6_ADDRSTRLEN]; - char s_gateway[INET6_ADDRSTRLEN]; - char s_pref_src[INET6_ADDRSTRLEN]; - char s_src_all[INET6_ADDRSTRLEN + 40]; - char s_src[INET6_ADDRSTRLEN]; - char str_type[30]; - char str_table[30]; - char str_pref[40]; - char str_pref2[30]; - char str_dev[30]; - char str_mss[32]; - char s_source[50]; - 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_metric[30]; + char s_network[INET6_ADDRSTRLEN]; + char s_gateway[INET6_ADDRSTRLEN]; + char s_pref_src[INET6_ADDRSTRLEN]; + char s_src_all[INET6_ADDRSTRLEN + 40]; + char s_src[INET6_ADDRSTRLEN]; + char str_type[30]; + char str_table[30]; + char str_pref[40]; + char str_pref2[30]; + char str_dev[30]; + char str_mss[32]; + char s_source[50]; + 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_metric[30]; + gboolean has_nhid = FALSE; if (!nm_utils_to_string_buffer_init_null(route, &buf, &len)) return buf; inet_ntop(AF_INET6, &route->network, s_network, sizeof(s_network)); - if (IN6_IS_ADDR_UNSPECIFIED(&route->gateway)) + if (route->nhid) { + g_snprintf(s_gateway, sizeof(s_gateway), " nhid %u", route->nhid); + has_nhid = TRUE; + } else if (IN6_IS_ADDR_UNSPECIFIED(&route->gateway)) s_gateway[0] = '\0'; else inet_ntop(AF_INET6, &route->gateway, s_gateway, sizeof(s_gateway)); @@ -7591,7 +7595,7 @@ nm_platform_ip6_route_to_string(const NMPlatformIP6Route *route, char *buf, gsiz : ""), s_network, route->plen, - s_gateway[0] ? " via " : "", + !has_nhid && s_gateway[0] ? " via " : "", s_gateway, _to_string_dev(str_dev, route->ifindex), route->metric_any @@ -9353,6 +9357,7 @@ nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj, *nm_ip6_addr_clear_host_address(&a1, &obj->network, obj->plen), obj->plen, obj->metric, + obj->nhid, *nm_ip6_addr_clear_host_address(&a2, &obj->src, obj->src_plen), obj->src_plen, NM_HASH_COMBINE_BOOLS(guint8, obj->metric_any, obj->table_any)); @@ -9368,12 +9373,13 @@ nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj, *nm_ip6_addr_clear_host_address(&a1, &obj->network, obj->plen), obj->plen, obj->metric, + obj->nhid, *nm_ip6_addr_clear_host_address(&a2, &obj->src, obj->src_plen), obj->src_plen, NM_HASH_COMBINE_BOOLS(guint8, obj->metric_any, obj->table_any), /* on top of WEAK_ID: */ obj->ifindex, - obj->gateway); + obj->nhid == 0 ? obj->gateway : in6addr_any); break; case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: nm_hash_update_vals( @@ -9384,7 +9390,8 @@ nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj, *nm_ip6_addr_clear_host_address(&a1, &obj->network, obj->plen), obj->plen, obj->metric, - obj->gateway, + obj->nhid, + obj->nhid == 0 ? obj->gateway : in6addr_any, obj->pref_src, *nm_ip6_addr_clear_host_address(&a2, &obj->src, obj->src_plen), obj->src_plen, @@ -9416,7 +9423,8 @@ nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj, obj->ifindex, obj->network, obj->metric, - obj->gateway, + obj->nhid, + obj->nhid == 0 ? obj->gateway : in6addr_any, obj->pref_src, obj->src, obj->src_plen, @@ -9463,12 +9471,15 @@ nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a, NM_CMP_FIELD(a, b, plen); NM_CMP_FIELD_UNSAFE(a, b, metric_any); NM_CMP_FIELD(a, b, metric); + NM_CMP_FIELD(a, b, nhid); NM_CMP_DIRECT_IP6_ADDR_SAME_PREFIX(&a->src, &b->src, NM_MIN(a->src_plen, b->src_plen)); NM_CMP_FIELD(a, b, src_plen); if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) { NM_CMP_FIELD(a, b, ifindex); NM_CMP_FIELD(a, b, type_coerced); - NM_CMP_FIELD_IN6ADDR(a, b, gateway); + if (a->nhid == 0) { + NM_CMP_FIELD_IN6ADDR(a, b, gateway); + } } break; case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY: @@ -9488,7 +9499,10 @@ nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a, NM_CMP_FIELD(a, b, plen); NM_CMP_FIELD_UNSAFE(a, b, metric_any); NM_CMP_FIELD(a, b, metric); - NM_CMP_FIELD_IN6ADDR(a, b, gateway); + NM_CMP_FIELD(a, b, nhid); + if (a->nhid == 0) { + NM_CMP_FIELD_IN6ADDR(a, b, gateway); + } NM_CMP_FIELD_IN6ADDR(a, b, pref_src); if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) { NM_CMP_DIRECT_IP6_ADDR_SAME_PREFIX(&a->src, &b->src, NM_MIN(a->src_plen, b->src_plen)); diff --git a/src/libnm-platform/nm-platform.h b/src/libnm-platform/nm-platform.h index 7f75651885..c28653fa50 100644 --- a/src/libnm-platform/nm-platform.h +++ b/src/libnm-platform/nm-platform.h @@ -533,6 +533,21 @@ struct _NMPlatformIP6Route { * The type is guint8 to keep the struct size small. But the values are compatible with * the NMIcmpv6RouterPref enum. */ guint8 rt_pref; + + /* RTA_NH_ID. The unique id of the nexthop object. + * + * When sending a route with a nexthop to the kernel, the ifindex + * and gateway must be unset, otherwise the route will be + * rejected. When the kernel sends notifications to userspace it + * copies the ifindex and the gateway from the nexthop into the + * route. + * + * In a route platform object, the ifindex and gateway are ignored + * if the routes has a nexthop. The ifindex must be always set + * when creating new synthetic route because the platform code + * needs it to properly track the object. + */ + guint32 nhid; } _nm_alignas(NMPlatformObject); typedef union {