merge: brnach 'ff/platform_ecmp'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1466
This commit is contained in:
Fernando Fernandez Mancera 2022-11-22 09:37:31 +01:00
commit fe7bea9685
9 changed files with 743 additions and 274 deletions

View file

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

View file

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

View file

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

View file

@ -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:
@ -707,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];
@ -982,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];
@ -1930,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);

View file

@ -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) {
@ -9326,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));
}

View file

@ -5164,55 +5164,107 @@ 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)
{
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
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
@ -6606,6 +6658,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 +6672,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 +6713,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 +6749,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 +6805,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;
}
/**
@ -7196,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);
@ -7235,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)
{
@ -8072,6 +8184,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,
@ -8079,6 +8205,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)),
@ -8087,40 +8215,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),
obj->gateway,
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(
@ -8131,7 +8257,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 +8292,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 +8321,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,
@ -8198,6 +8352,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);
@ -8208,14 +8363,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_FIELD(a, b, gateway);
NM_CMP_FIELD(a, b, mss);
NM_CMP_FIELD(a, b, pref_src);
NM_CMP_FIELD(a, b, window);
@ -8232,6 +8387,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:
@ -8251,7 +8413,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));
@ -8308,6 +8479,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,
@ -8400,6 +8574,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);
@ -8913,7 +9090,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 +9108,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

View file

@ -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
@ -382,9 +387,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 +435,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 +674,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;
@ -1165,10 +1211,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,
@ -2129,6 +2172,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)
{
@ -2162,7 +2222,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,
@ -2216,7 +2279,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,36 +2334,20 @@ 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);
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);
@ -2298,6 +2356,20 @@ 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);
void nm_platform_ip6_route_hash_update(const NMPlatformIP6Route *obj,
NMPlatformIPRouteCmpType cmp_type,
NMHashState *h);

View file

@ -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)
{
@ -1715,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)
{
@ -1723,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)
{
@ -3218,23 +3324,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] =
{
@ -3254,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] =
{
@ -3273,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] =
{
@ -3292,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] =
{

View file

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