Skip to content

Commit ba0a925

Browse files
ip,ip6: introduce basic ip/ip6 loadbalance
Using the configured nexthop GR_NH_T_GROUP, we select one of the nexthop based on the input RSS. As of now, we consider all weights being equal to "1". A further commit will take into account the route weight. Add smoke test for ip_loadbalance node. Signed-off-by: Christophe Fontaine <[email protected]> Reviewed-by: Robin Jarry <[email protected]>
1 parent e60ea8d commit ba0a925

File tree

6 files changed

+604
-364
lines changed

6 files changed

+604
-364
lines changed

docs/graph.svg

Lines changed: 400 additions & 364 deletions
Loading
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
// Copyright (c) 2025 Christophe Fontaine
3+
4+
#include <gr_graph.h>
5+
#include <gr_ip4_datapath.h>
6+
#include <gr_mbuf.h>
7+
#include <gr_trace.h>
8+
#include <gr_vec.h>
9+
10+
#include <rte_fib.h>
11+
#include <rte_ip.h>
12+
#include <rte_mbuf.h>
13+
14+
enum edges {
15+
OUTPUT = 0,
16+
NO_NEXTHOP,
17+
EDGE_COUNT,
18+
};
19+
20+
static uint16_t ip_loadbalance_process(
21+
struct rte_graph *graph,
22+
struct rte_node *node,
23+
void **objs,
24+
uint16_t nb_objs
25+
) {
26+
struct ip_output_mbuf_data *d;
27+
struct nexthop_info_group *g;
28+
struct rte_mbuf *mbuf;
29+
rte_edge_t edge;
30+
uint16_t i;
31+
32+
for (i = 0; i < nb_objs; i++) {
33+
mbuf = objs[i];
34+
d = ip_output_mbuf_data(mbuf);
35+
g = (struct nexthop_info_group *)d->nh->info;
36+
edge = OUTPUT;
37+
if (unlikely(g->n_members == 0)) {
38+
edge = NO_NEXTHOP;
39+
goto next;
40+
}
41+
// TODO: increment xstat on ! mbuf->ol_flags & RTE_MBUF_F_RX_RSS_HASH
42+
d->nh = g->members[mbuf->hash.rss % g->n_members].nh;
43+
next:
44+
if (gr_mbuf_is_traced(mbuf))
45+
gr_mbuf_trace_add(mbuf, node, 0);
46+
47+
rte_node_enqueue_x1(graph, node, edge, mbuf);
48+
}
49+
50+
return nb_objs;
51+
}
52+
53+
static void loadbalance_register(void) {
54+
ip_output_register_nexthop_type(GR_NH_T_GROUP, "ip_loadbalance");
55+
}
56+
57+
static struct rte_node_register ip_lb_node = {
58+
.name = "ip_loadbalance",
59+
.process = ip_loadbalance_process,
60+
.nb_edges = EDGE_COUNT,
61+
.next_nodes = {
62+
[OUTPUT] = "ip_output",
63+
[NO_NEXTHOP] = "ip_lb_no_nexthop",
64+
},
65+
};
66+
67+
static struct gr_node_info info_loadbalance = {
68+
.node = &ip_lb_node,
69+
.register_callback = loadbalance_register,
70+
};
71+
72+
GR_NODE_REGISTER(info_loadbalance);
73+
GR_DROP_REGISTER(ip_lb_no_nexthop);

modules/ip/datapath/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ src += files(
1616
'ip_fragment.c',
1717
'ip_hold.c',
1818
'ip_input.c',
19+
'ip_loadbalance.c',
1920
'ip_local.c',
2021
'ip_output.c',
2122
)
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
// Copyright (c) 2025 Christophe Fontaine
3+
4+
#include <gr_graph.h>
5+
#include <gr_ip6_datapath.h>
6+
#include <gr_mbuf.h>
7+
#include <gr_trace.h>
8+
#include <gr_vec.h>
9+
10+
#include <rte_fib.h>
11+
#include <rte_ip.h>
12+
#include <rte_mbuf.h>
13+
14+
enum edges {
15+
OUTPUT = 0,
16+
NO_NEXTHOP,
17+
EDGE_COUNT,
18+
};
19+
20+
static uint16_t ip6_loadbalance_process(
21+
struct rte_graph *graph,
22+
struct rte_node *node,
23+
void **objs,
24+
uint16_t nb_objs
25+
) {
26+
struct ip6_output_mbuf_data *d;
27+
struct nexthop_info_group *g;
28+
struct rte_mbuf *mbuf;
29+
rte_edge_t edge;
30+
uint16_t i;
31+
32+
for (i = 0; i < nb_objs; i++) {
33+
mbuf = objs[i];
34+
d = ip6_output_mbuf_data(mbuf);
35+
g = (struct nexthop_info_group *)d->nh->info;
36+
edge = OUTPUT;
37+
if (unlikely(g->n_members == 0)) {
38+
edge = NO_NEXTHOP;
39+
goto next;
40+
}
41+
// TODO: increment xstat on ! mbuf->ol_flags & RTE_MBUF_F_RX_RSS_HASH
42+
d->nh = g->members[mbuf->hash.rss % g->n_members].nh;
43+
next:
44+
if (gr_mbuf_is_traced(mbuf))
45+
gr_mbuf_trace_add(mbuf, node, 0);
46+
47+
rte_node_enqueue_x1(graph, node, edge, mbuf);
48+
}
49+
50+
return nb_objs;
51+
}
52+
53+
static void loadbalance_register(void) {
54+
ip6_output_register_nexthop_type(GR_NH_T_GROUP, "ip6_loadbalance");
55+
}
56+
57+
static struct rte_node_register ip6_lb_node = {
58+
.name = "ip6_loadbalance",
59+
.process = ip6_loadbalance_process,
60+
.nb_edges = EDGE_COUNT,
61+
.next_nodes = {
62+
[OUTPUT] = "ip6_output",
63+
[NO_NEXTHOP] = "ip6_lb_no_nexthop",
64+
},
65+
};
66+
67+
static struct gr_node_info info_loadbalance = {
68+
.node = &ip6_lb_node,
69+
.register_callback = loadbalance_register,
70+
};
71+
72+
GR_NODE_REGISTER(info_loadbalance);
73+
GR_DROP_REGISTER(ip6_lb_no_nexthop);

modules/ip6/datapath/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ src += files(
1010
'ip6_forward.c',
1111
'ip6_hold.c',
1212
'ip6_input.c',
13+
'ip6_loadbalance.c',
1314
'ip6_local.c',
1415
'ip6_output.c',
1516
'ndp_na_input.c',

smoke/ip_loadbalance_test.sh

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#!/bin/bash
2+
# SPDX-License-Identifier: BSD-3-Clause
3+
# Copyright (c) 2025 Christophe Fontaine
4+
5+
. $(dirname $0)/_init.sh
6+
7+
p0=${run_id}0
8+
p1=${run_id}1
9+
p2=${run_id}2
10+
11+
grcli interface add port $p0 devargs net_tap0,iface=$p0 mac f0:0d:ac:dc:00:00
12+
grcli interface add port $p1 devargs net_tap1,iface=$p1 mac f0:0d:ac:dc:00:01
13+
grcli interface add port $p2 devargs net_tap2,iface=$p2 mac f0:0d:ac:dc:00:02
14+
15+
grcli address add 172.16.0.1/24 iface $p0
16+
grcli address add 172.16.1.1/24 iface $p1
17+
grcli address add 172.16.2.1/24 iface $p2
18+
19+
netns_add ${run_id}
20+
ip -n ${run_id} link set lo up
21+
ip -n ${run_id} addr add 192.200.0.2/24 dev lo
22+
23+
for n in 0 1; do
24+
p=$run_id$n
25+
ip link set $p netns ${run_id}
26+
ip -n ${run_id} link set $p address ba:d0:ca:ca:00:0$n
27+
ip -n ${run_id} link set $p up
28+
ip -n ${run_id} addr add 172.16.$n.2/24 dev $p
29+
done
30+
31+
# Add ECMP route
32+
grcli nexthop add l3 iface $p0 address 172.16.0.2 id 100
33+
grcli nexthop add l3 iface $p1 address 172.16.1.2 id 101
34+
grcli nexthop add group id 10 member 100 member 101
35+
grcli route add 192.200.0.0/24 via id 10
36+
37+
# Locally generated ICMP requests
38+
grcli ping 192.200.0.2 count 1 ident 1
39+
grcli ping 192.200.0.2 count 1 ident 2
40+
41+
# Externally generated ICMP requests
42+
ip -n ${run_id} nexthop add id 1601 via 172.16.0.1 dev $p0
43+
ip -n ${run_id} nexthop add id 1611 via 172.16.1.1 dev $p1
44+
ip -n ${run_id} nexthop add id 1620 group 1601/1611
45+
46+
ip -n ${run_id} route add 172.16.2.0/24 nhid 1620
47+
48+
netns_add $p2
49+
ip link set $p2 netns $p2
50+
ip -n $p2 link set $p2 address ba:d0:ca:ca:00:02
51+
ip -n $p2 link set $p2 up
52+
ip -n $p2 addr add 172.16.2.2/24 dev $p2
53+
ip -n $p2 route add default via 172.16.2.1
54+
ip netns exec $p2 ping 192.200.0.2 -c 3
55+
56+
grcli nexthop del 10

0 commit comments

Comments
 (0)