merge: branch 'bg/route-via'
https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2222
This commit is contained in:
@@ -623,6 +623,79 @@ test_ip4_zero_gateway(void)
|
||||
nmtstp_wait_for_signal(NM_PLATFORM_GET, 50);
|
||||
}
|
||||
|
||||
static void
|
||||
test_via(void)
|
||||
{
|
||||
int ifindex = nm_platform_link_get_ifindex(NM_PLATFORM_GET, DEVICE_NAME);
|
||||
GPtrArray *routes;
|
||||
NMPlatformIP4Route rts[1];
|
||||
struct in6_addr gateway6;
|
||||
const int metric = 22987;
|
||||
NMPlatformIP4Route route4;
|
||||
guint mss = 1000;
|
||||
in_addr_t net4;
|
||||
|
||||
/* Test IPv4 routes with a IPv6 gateway (using RTA_VIA attribute) */
|
||||
|
||||
inet_pton(AF_INET6, "fd01::1", &gateway6);
|
||||
inet_pton(AF_INET, "1.2.3.4", &net4);
|
||||
|
||||
/* Add direct route to IPv6 gateway: ip route add dev $DEV fd01::1/128 */
|
||||
nmtstp_ip6_route_add(NM_PLATFORM_GET,
|
||||
ifindex,
|
||||
NM_IP_CONFIG_SOURCE_USER,
|
||||
gateway6,
|
||||
128,
|
||||
in6addr_any,
|
||||
in6addr_any,
|
||||
metric,
|
||||
mss);
|
||||
g_assert(nmtstp_ip6_route_get(NM_PLATFORM_GET, ifindex, &gateway6, 128, metric, NULL, 0));
|
||||
|
||||
/* Add IPv4 route via IPv6 gateway: ip route add dev $DEV 1.2.3.4/32 via inet6 fd01::1 */
|
||||
route4 = (NMPlatformIP4Route) {
|
||||
.ifindex = ifindex,
|
||||
.rt_source = NM_IP_CONFIG_SOURCE_USER,
|
||||
.network = net4,
|
||||
.plen = 32,
|
||||
.metric = metric,
|
||||
.via.addr_family = AF_INET6,
|
||||
.via.addr.addr6 = gateway6,
|
||||
.mss = mss,
|
||||
};
|
||||
g_assert(NMTST_NM_ERR_SUCCESS(
|
||||
nm_platform_ip4_route_add(NM_PLATFORM_GET, NMP_NLM_FLAG_REPLACE, &route4, NULL)));
|
||||
g_assert(nmtstp_ip4_route_get(NM_PLATFORM_GET, ifindex, net4, 32, metric, 0));
|
||||
|
||||
/* Test route listing */
|
||||
routes = nmtstp_ip4_route_get_all(NM_PLATFORM_GET, ifindex);
|
||||
g_assert_cmpint(routes->len, ==, 1);
|
||||
|
||||
memset(rts, 0, sizeof(rts));
|
||||
rts[0].rt_source = nmp_utils_ip_config_source_round_trip_rtprot(NM_IP_CONFIG_SOURCE_USER);
|
||||
rts[0].scope_inv = nm_platform_route_scope_inv(RT_SCOPE_LINK);
|
||||
rts[0].network = net4;
|
||||
rts[0].plen = 32;
|
||||
rts[0].ifindex = ifindex;
|
||||
rts[0].gateway = INADDR_ANY;
|
||||
rts[0].metric = metric;
|
||||
rts[0].mss = mss;
|
||||
rts[0].via.addr_family = AF_INET6;
|
||||
rts[0].via.addr.addr6 = gateway6;
|
||||
rts[0].n_nexthops = 1;
|
||||
nmtst_platform_ip4_routes_equal_aptr((const NMPObject *const *) routes->pdata,
|
||||
rts,
|
||||
routes->len,
|
||||
TRUE);
|
||||
g_ptr_array_unref(routes);
|
||||
|
||||
/* Delete routes */
|
||||
g_assert(nmtstp_platform_ip6_route_delete(NM_PLATFORM_GET, ifindex, gateway6, 128, metric));
|
||||
g_assert(!nmtstp_ip6_route_get(NM_PLATFORM_GET, ifindex, &gateway6, 128, metric, NULL, 0));
|
||||
g_assert(nmtstp_platform_ip4_route_delete(NM_PLATFORM_GET, ifindex, net4, 32, metric));
|
||||
g_assert(!nmtstp_ip4_route_get(NM_PLATFORM_GET, ifindex, net4, 32, metric, 0));
|
||||
}
|
||||
|
||||
static void
|
||||
test_ip4_route_options(gconstpointer test_data)
|
||||
{
|
||||
@@ -2421,6 +2494,7 @@ _nmtstp_setup_tests(void)
|
||||
add_test_func("/route/ip4_route_get", test_ip4_route_get);
|
||||
add_test_func("/route/ip6_route_get", test_ip6_route_get);
|
||||
add_test_func("/route/ip4_zero_gateway", test_ip4_zero_gateway);
|
||||
add_test_func("/route/via", test_via);
|
||||
}
|
||||
|
||||
if (nmtstp_is_root_test()) {
|
||||
|
@@ -4013,6 +4013,7 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter
|
||||
[RTA_PREF] = {.type = NLA_U8},
|
||||
[RTA_FLOW] = {.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},
|
||||
[RTA_MULTIPATH] = {.type = NLA_NESTED},
|
||||
};
|
||||
@@ -4029,9 +4030,11 @@ _new_from_nl_route(const struct nlmsghdr *nlh, gboolean id_only, ParseNlmsgIter
|
||||
guint8 weight;
|
||||
int ifindex;
|
||||
NMIPAddr gateway;
|
||||
gboolean is_via;
|
||||
} nh = {
|
||||
.found = FALSE,
|
||||
.has_more = FALSE,
|
||||
.is_via = FALSE,
|
||||
};
|
||||
guint v4_n_nexthops = 0;
|
||||
NMPlatformIP4RtNextHop v4_nh_extra_nexthops_stack[10];
|
||||
@@ -4200,13 +4203,26 @@ rta_multipath_done:
|
||||
return nm_assert_unreachable_val(NULL);
|
||||
}
|
||||
|
||||
if (tb[RTA_OIF] || tb[RTA_GATEWAY] || tb[RTA_FLOW]) {
|
||||
if (tb[RTA_OIF] || tb[RTA_GATEWAY] || tb[RTA_FLOW] || tb[RTA_VIA]) {
|
||||
int ifindex = 0;
|
||||
NMIPAddr gateway = {};
|
||||
|
||||
if (tb[RTA_OIF])
|
||||
ifindex = nla_get_u32(tb[RTA_OIF]);
|
||||
if (_check_addr_or_return_null(tb, RTA_GATEWAY, addr_len))
|
||||
|
||||
if (tb[RTA_VIA]) {
|
||||
struct rtvia *rtvia;
|
||||
|
||||
/* Used when the gateway family doesn't match the route family */
|
||||
rtvia = nla_data(tb[RTA_VIA]);
|
||||
if (rtvia->rtvia_family != AF_INET6)
|
||||
return NULL;
|
||||
if (nla_len(tb[RTA_VIA]) < sizeof(struct rtvia) + sizeof(struct in6_addr))
|
||||
return NULL;
|
||||
|
||||
nh.is_via = TRUE;
|
||||
memcpy(&gateway, rtvia->rtvia_addr, sizeof(struct in6_addr));
|
||||
} else if (_check_addr_or_return_null(tb, RTA_GATEWAY, addr_len))
|
||||
memcpy(&gateway, nla_data(tb[RTA_GATEWAY]), addr_len);
|
||||
|
||||
if (!nh.found) {
|
||||
@@ -4222,6 +4238,9 @@ rta_multipath_done:
|
||||
} else {
|
||||
/* Kernel supports new style nexthop configuration,
|
||||
* verify that it is a duplicate and ignore old-style nexthop. */
|
||||
if (nh.is_via)
|
||||
return NULL;
|
||||
|
||||
if (nh.ifindex != ifindex || memcmp(&nh.gateway, &gateway, addr_len) != 0) {
|
||||
/* we have a RTA_MULTIPATH attribute that does not agree.
|
||||
* That seems not right. Error out. */
|
||||
@@ -4237,7 +4256,7 @@ rta_multipath_done:
|
||||
* 1 (lo). Of course it does!! */
|
||||
if (nh.found) {
|
||||
if (IS_IPv4) {
|
||||
if (nh.ifindex != 0 || nh.gateway.addr4 != 0) {
|
||||
if (nh.ifindex != 0 || nh.gateway.addr4 != 0 || nh.is_via) {
|
||||
/* we only accept kernel to notify about the ifindex/gateway, if it
|
||||
* is zero. This is only to be a bit forgiving, but we really don't
|
||||
* know how to handle such routes that have an ifindex. */
|
||||
@@ -4336,9 +4355,14 @@ rta_multipath_done:
|
||||
if (tb[RTA_PRIORITY])
|
||||
obj->ip_route.metric = nla_get_u32(tb[RTA_PRIORITY]);
|
||||
|
||||
if (IS_IPv4)
|
||||
obj->ip4_route.gateway = nh.gateway.addr4;
|
||||
else
|
||||
if (IS_IPv4) {
|
||||
if (nh.is_via) {
|
||||
obj->ip4_route.via.addr_family = AF_INET6;
|
||||
obj->ip4_route.via.addr = nh.gateway;
|
||||
} else {
|
||||
obj->ip4_route.gateway = nh.gateway.addr4;
|
||||
}
|
||||
} else
|
||||
obj->ip6_route.gateway = nh.gateway.addr6;
|
||||
|
||||
if (IS_IPv4)
|
||||
@@ -5828,7 +5852,24 @@ _nl_msg_new_route(uint16_t nlmsg_type, uint16_t nlmsg_flags, const NMPObject *ob
|
||||
|
||||
/* We currently don't have need for multi-hop routes... */
|
||||
if (IS_IPv4) {
|
||||
NLA_PUT(msg, RTA_GATEWAY, addr_len, &obj->ip4_route.gateway);
|
||||
if (obj->ip4_route.gateway == INADDR_ANY && obj->ip4_route.via.addr_family != AF_UNSPEC) {
|
||||
struct rtvia *rtvia;
|
||||
|
||||
nm_assert(obj->ip4_route.via.addr_family == AF_INET6);
|
||||
|
||||
rtvia = nla_data(nla_reserve(
|
||||
msg,
|
||||
RTA_VIA,
|
||||
sizeof(*rtvia) + nm_utils_addr_family_to_size(obj->ip4_route.via.addr_family)));
|
||||
if (!rtvia)
|
||||
goto nla_put_failure;
|
||||
rtvia->rtvia_family = obj->ip4_route.via.addr_family;
|
||||
memcpy(rtvia->rtvia_addr,
|
||||
obj->ip4_route.via.addr.addr_ptr,
|
||||
nm_utils_addr_family_to_size(obj->ip4_route.via.addr_family));
|
||||
} else {
|
||||
NLA_PUT(msg, RTA_GATEWAY, addr_len, &obj->ip4_route.gateway);
|
||||
}
|
||||
} else {
|
||||
if (!IN6_IS_ADDR_UNSPECIFIED(&obj->ip6_route.gateway))
|
||||
NLA_PUT(msg, RTA_GATEWAY, addr_len, &obj->ip6_route.gateway);
|
||||
|
@@ -7199,7 +7199,7 @@ nm_platform_ip4_route_to_string_full(const NMPlatformIP4Route *route,
|
||||
{
|
||||
char *buf0;
|
||||
char s_network[INET_ADDRSTRLEN];
|
||||
char s_gateway[INET_ADDRSTRLEN];
|
||||
char s_gateway[INET6_ADDRSTRLEN];
|
||||
char s_pref_src[INET_ADDRSTRLEN];
|
||||
char str_dev[30];
|
||||
char str_mss[32];
|
||||
@@ -7228,10 +7228,13 @@ nm_platform_ip4_route_to_string_full(const NMPlatformIP4Route *route,
|
||||
|
||||
inet_ntop(AF_INET, &route->network, s_network, sizeof(s_network));
|
||||
|
||||
if (route->gateway == 0)
|
||||
s_gateway[0] = '\0';
|
||||
else
|
||||
if (route->gateway != INADDR_ANY) {
|
||||
inet_ntop(AF_INET, &route->gateway, s_gateway, sizeof(s_gateway));
|
||||
} else if (route->via.addr_family == AF_INET6) {
|
||||
inet_ntop(AF_INET6, route->via.addr.addr_ptr, s_gateway, sizeof(s_gateway));
|
||||
} else {
|
||||
s_gateway[0] = '\0';
|
||||
}
|
||||
|
||||
nm_strbuf_append(
|
||||
&buf,
|
||||
@@ -8967,6 +8970,9 @@ nm_platform_ip4_route_hash_update(const NMPlatformIP4Route *obj,
|
||||
nm_hash_update_vals(h,
|
||||
obj->ifindex,
|
||||
n_nexthops,
|
||||
obj->via.addr_family,
|
||||
obj->via.addr_family == AF_INET6 ? obj->via.addr.addr6
|
||||
: in6addr_any,
|
||||
obj->gateway,
|
||||
_ip4_route_weight_normalize(n_nexthops, obj->weight, FALSE));
|
||||
}
|
||||
@@ -9117,6 +9123,10 @@ nm_platform_ip4_route_cmp(const NMPlatformIP4Route *a,
|
||||
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_ID) {
|
||||
NM_CMP_FIELD(a, b, ifindex);
|
||||
NM_CMP_FIELD(a, b, gateway);
|
||||
NM_CMP_FIELD(a, b, via.addr_family);
|
||||
if (a->via.addr_family == AF_INET6) {
|
||||
NM_CMP_FIELD_IN6ADDR(a, b, via.addr.addr6);
|
||||
}
|
||||
n_nexthops = nm_platform_ip4_route_get_n_nexthops(a);
|
||||
NM_CMP_DIRECT(n_nexthops, nm_platform_ip4_route_get_n_nexthops(b));
|
||||
NM_CMP_DIRECT(_ip4_route_weight_normalize(n_nexthops, a->weight, FALSE),
|
||||
@@ -9142,6 +9152,10 @@ 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, gateway);
|
||||
NM_CMP_FIELD(a, b, via.addr_family);
|
||||
if (a->via.addr_family == AF_INET6) {
|
||||
NM_CMP_FIELD_IN6ADDR(a, b, via.addr.addr6);
|
||||
}
|
||||
if (cmp_type == NM_PLATFORM_IP_ROUTE_CMP_TYPE_SEMANTICALLY) {
|
||||
n_nexthops = nm_platform_ip4_route_get_n_nexthops(a);
|
||||
NM_CMP_DIRECT(n_nexthops, nm_platform_ip4_route_get_n_nexthops(b));
|
||||
|
@@ -443,6 +443,12 @@ struct _NMPlatformIP4Route {
|
||||
* the first hop. */
|
||||
in_addr_t gateway;
|
||||
|
||||
/* RTA_VIA. Part of the primary key for a route. Allows a gateway for a
|
||||
* route to exist in a different address family.
|
||||
* Only valid if: n_nexthops == 1, gateway == 0, via.family != AF_UNSPEC
|
||||
*/
|
||||
NMIPAddrTyped via;
|
||||
|
||||
/* RTA_PREFSRC (called "src" by iproute2).
|
||||
*
|
||||
* pref_src is part of the ID of an IPv4 route. When deleting a route,
|
||||
@@ -2404,6 +2410,14 @@ nm_platform_ip_route_get_gateway(int addr_family, const NMPlatformIPRoute *route
|
||||
return &((NMPlatformIP6Route *) route)->gateway;
|
||||
}
|
||||
|
||||
static inline const NMIPAddrTyped *
|
||||
nm_platform_ip4_route_get_via(const NMPlatformIP4Route *route)
|
||||
{
|
||||
nm_assert(route);
|
||||
|
||||
return &route->via;
|
||||
}
|
||||
|
||||
static inline gconstpointer
|
||||
nm_platform_ip_route_get_pref_src(int addr_family, const NMPlatformIPRoute *route)
|
||||
{
|
||||
|
Reference in New Issue
Block a user