platform: support setting the nexthop in routes

This commit is contained in:
Beniamino Galvani 2025-12-10 14:15:57 +01:00
parent 6f154c1500
commit 00aa445226
3 changed files with 73 additions and 31 deletions

View file

@ -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);

View file

@ -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));

View file

@ -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 {