mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-02-24 21:50:34 +01:00
platform: support setting the nexthop in routes
Routes can now reference a nexthop id instead of the (ifindex, gateway) tuple. 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. This is implemented only for IPv6 routes, because it will be needed by the ndisc code.
This commit is contained in:
parent
39a1baa289
commit
f696705727
4 changed files with 165 additions and 36 deletions
|
|
@ -2408,6 +2408,82 @@ test_nexthop_add(void)
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
static void
|
||||
test_ip6_route_with_nexthop(void)
|
||||
{
|
||||
const int ifindex = NMTSTP_ENV1_IFINDEXES[0];
|
||||
NMPObject obj;
|
||||
int r;
|
||||
const NMPlatformIP6Route *r6;
|
||||
struct in6_addr network;
|
||||
const guint32 metric = 22987;
|
||||
|
||||
g_assert(nm_platform_ip6_address_add(NM_PLATFORM_GET,
|
||||
ifindex,
|
||||
nmtst_inet6_from_string("fe80::1"),
|
||||
64,
|
||||
in6addr_any,
|
||||
NM_PLATFORM_LIFETIME_PERMANENT,
|
||||
NM_PLATFORM_LIFETIME_PERMANENT,
|
||||
0,
|
||||
NULL));
|
||||
|
||||
/* Add IPv6 nexthop without gateway (id 300) */
|
||||
nmp_object_stackinit(&obj, NMP_OBJECT_TYPE_IP6_NEXTHOP, NULL);
|
||||
obj.ip6_nexthop.id = 300;
|
||||
obj.ip6_nexthop.ifindex = ifindex;
|
||||
r = nm_platform_ip_nexthop_add(NM_PLATFORM_GET, NMP_NLM_FLAG_ADD, &obj, NULL);
|
||||
g_assert_cmpint(r, ==, 0);
|
||||
|
||||
/* Add IPv6 nexthop with gateway (id 301) */
|
||||
nmp_object_stackinit(&obj, NMP_OBJECT_TYPE_IP6_NEXTHOP, NULL);
|
||||
obj.ip6_nexthop.id = 301;
|
||||
obj.ip6_nexthop.ifindex = ifindex;
|
||||
obj.ip6_nexthop.gateway = nmtst_inet6_from_string("fe80::99");
|
||||
r = nm_platform_ip_nexthop_add(NM_PLATFORM_GET, NMP_NLM_FLAG_ADD, &obj, NULL);
|
||||
g_assert_cmpint(r, ==, 0);
|
||||
|
||||
/* Add route using nexthop without gateway */
|
||||
inet_pton(AF_INET6, "a:b:c:1::", &network);
|
||||
r = nm_platform_ip6_route_add(NM_PLATFORM_GET,
|
||||
NMP_NLM_FLAG_REPLACE,
|
||||
&((NMPlatformIP6Route) {
|
||||
.ifindex = ifindex,
|
||||
.network = network,
|
||||
.plen = 64,
|
||||
.metric = metric,
|
||||
.nhid = 300,
|
||||
}));
|
||||
g_assert_cmpint(r, ==, 0);
|
||||
|
||||
r6 = nmtstp_ip6_route_get(NM_PLATFORM_GET, ifindex, &network, 64, metric, NULL, 0);
|
||||
g_assert(r6);
|
||||
g_assert_cmpint(r6->ifindex, ==, ifindex);
|
||||
g_assert_cmpint(r6->nhid, ==, 300);
|
||||
nmtst_assert_ip6_address(&r6->gateway, "::");
|
||||
|
||||
/* Add route using nexthop with gateway */
|
||||
inet_pton(AF_INET6, "a:b:c:2::", &network);
|
||||
r = nm_platform_ip6_route_add(NM_PLATFORM_GET,
|
||||
NMP_NLM_FLAG_REPLACE,
|
||||
&((NMPlatformIP6Route) {
|
||||
.ifindex = ifindex,
|
||||
.network = network,
|
||||
.plen = 64,
|
||||
.metric = metric,
|
||||
.nhid = 301,
|
||||
}));
|
||||
g_assert_cmpint(r, ==, 0);
|
||||
|
||||
r6 = nmtstp_ip6_route_get(NM_PLATFORM_GET, ifindex, &network, 64, metric, NULL, 0);
|
||||
g_assert(r6);
|
||||
g_assert_cmpint(r6->ifindex, ==, ifindex);
|
||||
g_assert_cmpint(r6->nhid, ==, 301);
|
||||
nmtst_assert_ip6_address(&r6->gateway, "fe80::99");
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static void
|
||||
_ensure_onlink_routes(void)
|
||||
{
|
||||
|
|
@ -2686,6 +2762,7 @@ _nmtstp_setup_tests(void)
|
|||
if (nmtstp_is_root_test()) {
|
||||
add_test_func_with_if2("/route/nexthop/dump", test_nexthop_dump);
|
||||
add_test_func("/route/nexthop/add", test_nexthop_add);
|
||||
add_test_func("/route/ip6_with_nexthop", test_ip6_route_with_nexthop);
|
||||
}
|
||||
|
||||
if (nmtstp_is_root_test()) {
|
||||
|
|
|
|||
|
|
@ -4015,6 +4015,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},
|
||||
|
|
@ -4052,6 +4053,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;
|
||||
|
||||
|
|
@ -4097,7 +4099,12 @@ _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] && !IS_IPv4) {
|
||||
/* we only support NHID for IPv6 at the moment */
|
||||
nhid = nla_get_u32(tb[RTA_NH_ID]);
|
||||
}
|
||||
|
||||
if (nhid == 0 && tb[RTA_MULTIPATH]) {
|
||||
size_t tlen;
|
||||
struct rtnexthop *rtnh;
|
||||
guint idx;
|
||||
|
|
@ -4206,7 +4213,7 @@ rta_multipath_done:
|
|||
return nm_assert_unreachable_val(NULL);
|
||||
}
|
||||
|
||||
if (tb[RTA_OIF] || tb[RTA_GATEWAY] || tb[RTA_FLOW] || tb[RTA_VIA]) {
|
||||
if (nhid == 0 && (tb[RTA_OIF] || tb[RTA_GATEWAY] || tb[RTA_FLOW] || tb[RTA_VIA])) {
|
||||
int ifindex = 0;
|
||||
NMIPAddr gateway = {};
|
||||
|
||||
|
|
@ -4252,6 +4259,15 @@ rta_multipath_done:
|
|||
}
|
||||
}
|
||||
|
||||
if (nhid != 0) {
|
||||
if (tb[RTA_OIF]) {
|
||||
nh.ifindex = nla_get_u32(tb[RTA_OIF]);
|
||||
nh.found = TRUE;
|
||||
}
|
||||
if (_check_addr_or_return_null(tb, RTA_GATEWAY, addr_len))
|
||||
memcpy(&nh.gateway, nla_data(tb[RTA_GATEWAY]), addr_len);
|
||||
}
|
||||
|
||||
if (nm_platform_route_type_is_nodev(rtm->rtm_type)) {
|
||||
/* These routes are special. They don't have an device/ifindex.
|
||||
*
|
||||
|
|
@ -4388,6 +4404,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;
|
||||
|
|
@ -5991,10 +6010,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);
|
||||
|
|
|
|||
|
|
@ -7612,33 +7612,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));
|
||||
|
|
@ -7682,7 +7686,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
|
||||
|
|
@ -9459,23 +9463,25 @@ 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->ifindex : 0,
|
||||
obj->nhid == 0 ? obj->gateway : in6addr_any);
|
||||
break;
|
||||
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY:
|
||||
nm_hash_update_vals(
|
||||
h,
|
||||
obj->type_coerced,
|
||||
nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(obj)),
|
||||
obj->ifindex,
|
||||
*nm_ip6_addr_clear_host_address(&a1, &obj->network, obj->plen),
|
||||
obj->plen,
|
||||
obj->metric,
|
||||
obj->gateway,
|
||||
obj->nhid,
|
||||
obj->nhid == 0 ? obj->ifindex : 0,
|
||||
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,
|
||||
|
|
@ -9504,10 +9510,11 @@ nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj,
|
|||
nm_hash_update_vals(h,
|
||||
obj->type_coerced,
|
||||
obj->table_coerced,
|
||||
obj->ifindex,
|
||||
obj->network,
|
||||
obj->metric,
|
||||
obj->gateway,
|
||||
obj->nhid,
|
||||
obj->nhid == 0 ? obj->ifindex : 0,
|
||||
obj->nhid == 0 ? obj->gateway : in6addr_any,
|
||||
obj->pref_src,
|
||||
obj->src,
|
||||
obj->src_plen,
|
||||
|
|
@ -9557,9 +9564,12 @@ nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a,
|
|||
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);
|
||||
NM_CMP_FIELD(a, b, nhid);
|
||||
if (a->nhid == 0) {
|
||||
NM_CMP_FIELD(a, b, ifindex);
|
||||
NM_CMP_FIELD_IN6ADDR(a, b, gateway);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY:
|
||||
|
|
@ -9571,7 +9581,6 @@ nm_platform_ip6_route_cmp(const NMPlatformIP6Route *a,
|
|||
nm_platform_ip_route_get_effective_table(NM_PLATFORM_IP_ROUTE_CAST(b)));
|
||||
} else
|
||||
NM_CMP_FIELD(a, b, table_coerced);
|
||||
NM_CMP_FIELD(a, b, ifindex);
|
||||
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY)
|
||||
NM_CMP_DIRECT_IP6_ADDR_SAME_PREFIX(&a->network, &b->network, NM_MIN(a->plen, b->plen));
|
||||
else
|
||||
|
|
@ -9579,7 +9588,11 @@ 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(a, b, ifindex);
|
||||
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));
|
||||
|
|
|
|||
|
|
@ -531,6 +531,22 @@ 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. However, in the platform cache, routes
|
||||
* with nexthops always have also the ifindex and the gateway set. In
|
||||
* particular, the ifindex must be set when creating new synthetic
|
||||
* route because the platform code needs it to properly track the object.
|
||||
*/
|
||||
guint32 nhid;
|
||||
} _nm_alignas(NMPlatformObject);
|
||||
|
||||
typedef union {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue