Skip to content

Commit a739736

Browse files
committed
frr: add route support via vrf interface
Synchronize FRR route with IFINDEX nexthop on a vrf interface by using GR_VRF_ROUTE_ADD/DEL api. It could be better if grout support pure L3 nexthop (i.e. not gateway). Signed-off-by: Maxime Leroy <maxime@leroys.fr>
1 parent b1020a4 commit a739736

5 files changed

Lines changed: 162 additions & 5 deletions

File tree

frr/if_grout.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
#include <zebra/interface.h>
2020
#include <zebra_dplane_grout.h>
2121

22-
#define GROUT_NS NS_DEFAULT
23-
2422
static uint64_t gr_if_flags_to_netlink(struct gr_iface *gr_if, enum zebra_link_type link_type) {
2523
uint64_t frr_if_flags = 0;
2624

frr/if_grout.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#include <stdbool.h>
1010
#include <zebra/zebra_dplane.h>
1111

12+
#define GROUT_NS NS_DEFAULT
13+
1214
enum zebra_dplane_result grout_add_del_address(struct zebra_dplane_ctx *ctx);
1315

1416
void grout_interface_addr_dplane(struct gr_nexthop *gr_nh, bool new);

frr/rt_grout.c

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
#include "rt_grout.h"
77

88
#include <gr_srv6.h>
9+
#include <gr_vrf.h>
910

1011
#include <lib/srv6.h>
12+
#include <zebra/interface.h>
1113
#include <zebra/rib.h>
1214
#include <zebra/table_manager.h>
1315
#include <zebra_dplane_grout.h>
@@ -381,6 +383,90 @@ void grout_route6_change(bool new, struct gr_ip6_route *gr_r6) {
381383
);
382384
}
383385

386+
static enum zebra_dplane_result grout_add_del_vrf_route(
387+
struct zebra_dplane_ctx *ctx,
388+
const struct prefix *p,
389+
gr_nh_origin_t origin,
390+
struct nexthop *nh,
391+
vrf_id_t vrf_id,
392+
bool new
393+
) {
394+
union {
395+
struct gr_vrf_route_add_req rvrf_add;
396+
struct gr_vrf_route_del_req rvrf_del;
397+
} req;
398+
struct gr_vrf_route *vrf_route;
399+
struct gr_vrf_route_key *key;
400+
uint32_t req_type;
401+
size_t req_len;
402+
403+
if (new) {
404+
req.rvrf_add = (struct gr_vrf_route_add_req) {
405+
.r.key.vrf_id = vrf_id,
406+
.exist_ok = true,
407+
.origin = origin,
408+
};
409+
vrf_route = &req.rvrf_add.r;
410+
key = &vrf_route->key;
411+
req_type = GR_VRF_ROUTE_ADD;
412+
req_len = sizeof(struct gr_vrf_route_add_req);
413+
} else {
414+
req.rvrf_del = (struct gr_vrf_route_del_req) {
415+
.key.vrf_id = vrf_id,
416+
.missing_ok = false,
417+
};
418+
vrf_route = NULL;
419+
key = &req.rvrf_del.key;
420+
req_type = GR_VRF_ROUTE_DEL;
421+
req_len = sizeof(struct gr_vrf_route_del_req);
422+
}
423+
*key = (struct gr_vrf_route_key) {.vrf_id = vrf_id, .is_dest6 = (p->family == AF_INET6)};
424+
425+
if (key->is_dest6) {
426+
memcpy(key->dest6.ip.a, p->u.prefix6.s6_addr, sizeof(key->dest6.ip.a));
427+
key->dest6.prefixlen = p->prefixlen;
428+
429+
gr_log_debug(
430+
"%s vrfroute %pI6/%u (origin %s) on vrf %u",
431+
new ? "add" : "del",
432+
&key->dest6.ip,
433+
key->dest6.prefixlen,
434+
gr_nh_origin_name(origin),
435+
vrf_id
436+
);
437+
438+
} else {
439+
key->dest4.ip = p->u.prefix4.s_addr;
440+
key->dest4.prefixlen = p->prefixlen;
441+
442+
gr_log_debug(
443+
"%s vrf route %pI4/%u (origin %s) on vrf %u",
444+
new ? "add" : "del",
445+
&key->dest4.ip,
446+
key->dest4.prefixlen,
447+
gr_nh_origin_name(origin),
448+
vrf_id
449+
);
450+
}
451+
452+
// just need key to delete
453+
if (!new)
454+
goto end;
455+
456+
vrf_route->out_vrf_id = nh->vrf_id;
457+
458+
end:
459+
if (!is_selfroute(origin)) {
460+
gr_log_debug("no frr route, skip it");
461+
return ZEBRA_DPLANE_REQUEST_SUCCESS;
462+
}
463+
464+
if (grout_client_send_recv(req_type, req_len, &req, NULL) < 0)
465+
return ZEBRA_DPLANE_REQUEST_FAILURE;
466+
467+
return ZEBRA_DPLANE_REQUEST_SUCCESS;
468+
}
469+
384470
static_assert(SRV6_MAX_SEGS <= GR_SRV6_ROUTE_SEGLIST_COUNT_MAX);
385471

386472
static enum zebra_dplane_result grout_add_del_srv6_route(
@@ -662,7 +748,15 @@ enum zebra_dplane_result grout_add_del_route(struct zebra_dplane_ctx *ctx) {
662748
gr_log_err("impossible to add/del srv6 route (invalid format)");
663749
return ZEBRA_DPLANE_REQUEST_FAILURE;
664750
}
751+
if (nh && nh->type == NEXTHOP_TYPE_IFINDEX) {
752+
struct interface *ifp;
753+
754+
ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(GROUT_NS), nh->ifindex);
755+
if (ifp && IS_ZEBRA_IF_VRF(ifp))
756+
return grout_add_del_vrf_route(ctx, p, origin, nh, vrf_id, new);
665757

758+
gr_log_err("impossible to add route with ifindex nexthop type");
759+
}
666760
if (new && nh_id == 0) {
667761
gr_log_err("impossible to add route with no nexthop id");
668762
return ZEBRA_DPLANE_REQUEST_FAILURE;
@@ -840,7 +934,17 @@ enum zebra_dplane_result grout_add_del_nexthop(struct zebra_dplane_ctx *ctx) {
840934
gr_log_debug("add nexthop id %u gw %pI6", nh_id, &gr_nh->ipv6);
841935
break;
842936
case NEXTHOP_TYPE_IFINDEX:
843-
gr_log_debug("add nexthop id %u ifindex %u", nh_id, gr_nh->iface_id);
937+
struct interface *ifp;
938+
939+
ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(GROUT_NS), nh->ifindex);
940+
if (ifp && IS_ZEBRA_IF_VRF(ifp)) {
941+
gr_log_err(
942+
"impossible to add/del nexthop on vrf interface (not supported)"
943+
);
944+
return ZEBRA_DPLANE_REQUEST_SUCCESS;
945+
}
946+
947+
gr_log_err("impossible to add nexthop id %u ifindex %u", nh_id, gr_nh->iface_id);
844948
return ZEBRA_DPLANE_REQUEST_FAILURE;
845949
default:
846950
gr_log_err("impossible to add nexthop %u (type %u not supported)", nh_id, nh->type);

smoke/_init_frr.sh

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,53 @@ vrf_name_from_id() {
7474
fi
7575
}
7676

77+
set_vrf_iface() {
78+
local vrf_id="$1"
79+
local vrf_name="$(vrf_name_from_id "$vrf_id")"
80+
81+
vtysh <<-EOF
82+
configure terminal
83+
interface ${vrf_name} vrf ${vrf_name}
84+
exit
85+
EOF
86+
}
87+
88+
set_vrf_route() {
89+
local prefix="$1"
90+
local vrf_id="2"
91+
local out_vrf_id="$3"
92+
local max_tries=5
93+
local count=0
94+
local vrf_name="$(vrf_name_from_id "$vrf_id")"
95+
local out_vrf_name="$(vrf_name_from_id "$out_vrf_id")"
96+
97+
if echo "$prefix" | grep -q ':'; then
98+
# IPv6
99+
local frr_ip="ipv6"
100+
else
101+
# IPv4
102+
local frr_ip="ip"
103+
fi
104+
105+
local grep_pattern="^${vrf_id}[[:space:]]+${prefix}[[:space:]]+${out_vrf_id}[[:space:]]*$"
106+
107+
vtysh <<-EOF
108+
configure terminal
109+
${frr_ip} route ${prefix} ${out_vrf_name} vrf ${vrf_name} nexthop-vrf ${out_vrf_name}
110+
exit
111+
EOF
112+
113+
while ! grcli show vrf route vrf ${vrf_id} | grep -qE "${grep_pattern}"; do
114+
if [ "$count" -ge "$max_tries" ]; then
115+
echo "Route ${prefix} from vrf ${vrf_id} to vrf ${out_vrf_id} not found after ${max_tries} attempts."
116+
grcli show vrf route vrf ${vrf_id}
117+
exit 1
118+
fi
119+
sleep 1
120+
count=$((count + 1))
121+
done
122+
}
123+
77124
set_ip_route() {
78125
local prefix="$1"
79126
local next_hop="$2"

smoke/cross_vrf_forward_frr_test.sh

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!/bin/bash
1+
1#!/bin/bash
22
# SPDX-License-Identifier: BSD-3-Clause
33
# Copyright (c) 2025 Maxime Leroy, Free Mobile
44

@@ -25,10 +25,16 @@ done
2525

2626
set_ip_address $p0 172.16.0.1/24 1
2727
set_ip_address $p1 172.16.1.1/24 2
28+
set_vrf_iface 1
29+
set_vrf_iface 2
2830

29-
set_ip_route 16.0.0.0/16 172.16.0.2 2 1
31+
# from 16.0.0.1 to 16.1.0.1, only one route lookup is done
3032
set_ip_route 16.1.0.0/16 172.16.1.2 1 2
3133

34+
# from 16.1.0.1 to 16.0.0.1, two route lookup are done
35+
set_vrf_route 16.0.0.0/16 2 1
36+
set_ip_route 16.0.0.0/16 172.16.0.2 1
37+
3238
ip netns exec n-$p0 ping -i0.01 -c3 -n 172.16.0.1
3339
ip netns exec n-$p1 ping -i0.01 -c3 -n 172.16.1.1
3440
ip netns exec n-$p0 ping -i0.01 -c3 -I 16.0.0.1 -n 16.1.0.1

0 commit comments

Comments
 (0)