Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
938 changes: 493 additions & 445 deletions docs/graph.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions frr/frr_plugin_install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ install -D -m 755 "$1" "$2"
sed -i -e '/^zebra_options=/ {
/-M[[:space:]]*dplane_grout/! s/"$/ -M dplane_grout"/
}' "$3"
sed -i -e 's/isisd=no/isisd=yes/' "$3"
sed -i -e 's/ospfd=no/ospfd=yes/' "$3"
touch "$4"
21 changes: 15 additions & 6 deletions frr/if_grout.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#endif

#include "if_grout.h"
#include "if_map.h"
#include "log_grout.h"

#include <gr_ip4.h>
Expand Down Expand Up @@ -51,11 +52,14 @@ void grout_link_change(struct gr_iface *gr_if, bool new, bool startup) {
const struct rte_ether_addr *mac = NULL;
uint32_t txqlen = 1000;

if (new)
add_ifindex_mapping(gr_if->id, if_nametoindex(gr_if->name));

switch (gr_if->base.type) {
case GR_IFACE_TYPE_VLAN:
gr_vlan = (const struct gr_iface_info_vlan *)&gr_if->info;
mac = &gr_vlan->mac;
link_ifindex = gr_vlan->parent_id;
link_ifindex = ifindex_grout_to_frr(gr_vlan->parent_id);
zif_type = ZEBRA_IF_VLAN;
link_type = ZEBRA_LLT_ETHER;
break;
Expand All @@ -72,6 +76,8 @@ void grout_link_change(struct gr_iface *gr_if, bool new, bool startup) {
link_type = ZEBRA_LLT_LOOPBACK;
if (gr_if->base.vrf_id)
zif_type = ZEBRA_IF_VRF;
else
zif_type = ZEBRA_IF_DUMMY;

// In kernel, there is no vrf interface for default vrf
// So sync gr-vrf0, as IF_OTHER
Expand All @@ -89,7 +95,7 @@ void grout_link_change(struct gr_iface *gr_if, bool new, bool startup) {
dplane_ctx_set_ns_id(ctx, GROUT_NS);
dplane_ctx_set_ifp_link_nsid(ctx, GROUT_NS);
dplane_ctx_set_ifp_zif_type(ctx, zif_type);
dplane_ctx_set_ifindex(ctx, gr_if->base.id);
dplane_ctx_set_ifindex(ctx, ifindex_grout_to_frr(gr_if->id));
dplane_ctx_set_ifname(ctx, gr_if->name);
dplane_ctx_set_ifp_startup(ctx, startup);
dplane_ctx_set_ifp_family(ctx, AF_UNSPEC);
Expand All @@ -112,12 +118,12 @@ void grout_link_change(struct gr_iface *gr_if, bool new, bool startup) {
dplane_ctx_set_ifp_protodown_set(ctx, false);

if (gr_if->base.vrf_id != 0) {
dplane_ctx_set_ifp_table_id(ctx, gr_if->base.vrf_id);
dplane_ctx_set_ifp_table_id(ctx, ifindex_grout_to_frr(gr_if->base.vrf_id));

// In Linux, vrf_id equals the interface index; in Grout we model a VRF
// with its gr‑vrf interface
// The gr‑vrf’s ifindex is guaranteed to match vrf_id
dplane_ctx_set_ifp_vrf_id(ctx, gr_if->base.vrf_id);
dplane_ctx_set_ifp_vrf_id(ctx, ifindex_grout_to_frr(gr_if->base.vrf_id));
} else {
dplane_ctx_set_ifp_table_id(ctx, 0);
dplane_ctx_set_ifp_vrf_id(ctx, 0);
Expand All @@ -139,6 +145,7 @@ void grout_link_change(struct gr_iface *gr_if, bool new, bool startup) {
} else {
dplane_ctx_set_op(ctx, DPLANE_OP_INTF_DELETE);
dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_QUEUED);
remove_mapping_by_grout_ifindex(gr_if->id);
}

dplane_provider_enqueue_to_zebra(ctx);
Expand All @@ -159,7 +166,7 @@ static void grout_interface_addr_change(
else
dplane_ctx_set_op(ctx, DPLANE_OP_INTF_ADDR_DEL);

dplane_ctx_set_ifindex(ctx, iface_id);
dplane_ctx_set_ifindex(ctx, ifindex_grout_to_frr(iface_id));
dplane_ctx_set_ns_id(ctx, GROUT_NS);

// Convert addr to prefix
Expand Down Expand Up @@ -191,8 +198,8 @@ void grout_interface_addr6_change(bool new, const struct gr_ip6_ifaddr *ifa) {
}

enum zebra_dplane_result grout_add_del_address(struct zebra_dplane_ctx *ctx) {
int gr_iface_id = ifindex_frr_to_grout(dplane_ctx_get_ifindex(ctx));
const struct prefix *p = dplane_ctx_get_intf_addr(ctx);
int gr_iface_id = dplane_ctx_get_ifindex(ctx);
union {
struct gr_ip4_addr_add_req ip4_add;
struct gr_ip4_addr_del_req ip4_del;
Expand All @@ -202,6 +209,8 @@ enum zebra_dplane_result grout_add_del_address(struct zebra_dplane_ctx *ctx) {
uint32_t req_type;
size_t req_len;

assert(gr_iface_id);

if (p->family != AF_INET && p->family != AF_INET6) {
gr_log_err(
"impossible to add/del address with family %u (not supported)", p->family
Expand Down
112 changes: 112 additions & 0 deletions frr/if_map.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Christophe Fontaine, Red Hat

#include "if_map.h"

#include <gr_infra.h>

DEFINE_MGROUP(GROUT, "Grout plugin memory");
DEFINE_MTYPE(GROUT, GROUT_MEM, "Grout plugin memory");

PREDECL_HASH(grout_to_frr); // grout_ifindex -> frr ifindex
PREDECL_HASH(frr_to_grout); // frr ifindex -> grout_ifindex

struct ifindex_mapping {
struct grout_to_frr_item forward_item;
struct frr_to_grout_item reverse_item;
ifindex_t grout_ifindex;
ifindex_t frr_ifindex;
};

static int grout_to_frr_cmp(const struct ifindex_mapping *a, const struct ifindex_mapping *b) {
return numcmp(a->grout_ifindex, b->grout_ifindex);
}

static uint32_t grout_to_frr_hash(const struct ifindex_mapping *mapping) {
return mapping->grout_ifindex;
}

DECLARE_HASH(
grout_to_frr,
struct ifindex_mapping,
forward_item,
grout_to_frr_cmp,
grout_to_frr_hash
);

static int frr_to_grout_cmp(const struct ifindex_mapping *a, const struct ifindex_mapping *b) {
return numcmp(a->frr_ifindex, b->frr_ifindex);
}

static uint32_t frr_to_grout_hash(const struct ifindex_mapping *mapping) {
return mapping->frr_ifindex;
}

DECLARE_HASH(
frr_to_grout,
struct ifindex_mapping,
reverse_item,
frr_to_grout_cmp,
frr_to_grout_hash
);

// Global hash table instances
static struct grout_to_frr_head grout_to_frr_mappings = INIT_HASH(grout_to_frr_mappings);
static struct frr_to_grout_head frr_to_grout_mappings = INIT_HASH(frr_to_grout_mappings);

// Add bidirectional mapping
bool add_ifindex_mapping(ifindex_t grout_ifindex, ifindex_t frr_ifindex) {
struct ifindex_mapping *mapping = XCALLOC(MTYPE_GROUT_MEM, sizeof(*mapping));
mapping->grout_ifindex = grout_ifindex;
mapping->frr_ifindex = frr_ifindex;

// Add to both hash tables
struct ifindex_mapping *existing_forward = grout_to_frr_add(
&grout_to_frr_mappings, mapping
);
if (existing_forward && existing_forward != mapping) {
XFREE(MTYPE_GROUT_MEM, mapping);
return false; // Duplicate grout_ifindex
}

struct ifindex_mapping *existing_reverse = frr_to_grout_add(
&frr_to_grout_mappings, mapping
);
if (existing_reverse && existing_reverse != mapping) {
grout_to_frr_del(&grout_to_frr_mappings, mapping);
XFREE(MTYPE_GROUT_MEM, mapping);
return false; // Duplicate frr_ifindex
}

return true;
}

ifindex_t ifindex_grout_to_frr(int16_t grout_ifindex) {
struct ifindex_mapping key = {.grout_ifindex = grout_ifindex};
struct ifindex_mapping *found = grout_to_frr_find(&grout_to_frr_mappings, &key);
return found ? found->frr_ifindex : IFINDEX_INTERNAL;
}

uint16_t ifindex_frr_to_grout(ifindex_t frr_ifindex) {
struct ifindex_mapping key = {.frr_ifindex = frr_ifindex};
struct ifindex_mapping *found = frr_to_grout_find(&frr_to_grout_mappings, &key);
return found ? found->grout_ifindex : GR_IFACE_ID_UNDEF;
}

bool remove_mapping_by_grout_ifindex(ifindex_t grout_ifindex) {
struct ifindex_mapping key = {.grout_ifindex = grout_ifindex};
struct ifindex_mapping *found = grout_to_frr_find(&grout_to_frr_mappings, &key);
if (!found)
return false;

grout_to_frr_del(&grout_to_frr_mappings, found);
frr_to_grout_del(&frr_to_grout_mappings, found);
XFREE(MTYPE_GROUT_MEM, found);
return true;
}

// Initialize the mapping tables
void init_ifindex_mappings(void) {
grout_to_frr_init(&grout_to_frr_mappings);
frr_to_grout_init(&frr_to_grout_mappings);
}
17 changes: 17 additions & 0 deletions frr/if_map.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Christophe Fontaine, Red Hat

#pragma once

#include <lib/if.h>
#include <lib/memory.h>

DECLARE_MGROUP(GROUT);
DECLARE_MTYPE(GROUT_MEM);

bool add_ifindex_mapping(ifindex_t grout_ifindex, ifindex_t frr_ifindex);
bool remove_mapping_by_grout_ifindex(ifindex_t grout_ifindex);

ifindex_t ifindex_grout_to_frr(int16_t grout_ifindex);
uint16_t ifindex_frr_to_grout(ifindex_t frr_ifindex);
void init_ifindex_mappings(void);
1 change: 1 addition & 0 deletions frr/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ install_build_flag = frr_dep.get_variable('install_on_build', default_value: 'fa
frr_plugin = shared_module(
'dplane_grout',
files(
'if_map.c',
'if_grout.c',
'rt_grout.c',
'zebra_dplane_grout.c',
Expand Down
53 changes: 36 additions & 17 deletions frr/rt_grout.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Maxime Leroy, Free Mobile

#include "if_map.h"
#include "log_grout.h"
#include "rt_grout.h"

Expand Down Expand Up @@ -190,8 +191,8 @@ static int grout_gr_nexthop_to_frr_nexthop(
struct nexthop *nh,
int *nh_family
) {
nh->ifindex = gr_nh->iface_id;
nh->vrf_id = gr_nh->vrf_id;
nh->ifindex = ifindex_grout_to_frr(gr_nh->iface_id);
nh->vrf_id = ifindex_grout_to_frr(gr_nh->vrf_id);
nh->weight = 1;

switch (gr_nh->type) {
Expand Down Expand Up @@ -267,7 +268,7 @@ static int grout_gr_nexthop_to_frr_nexthop(
break;
}

ctx.table = sr6->out_vrf_id;
ctx.table = ifindex_grout_to_frr(sr6->out_vrf_id);
nexthop_add_srv6_seg6local(nh, action, &ctx);
break;
}
Expand All @@ -290,8 +291,8 @@ static int grout_gr_nexthop_to_frr_nexthop(
break;
}
case GR_NH_T_GROUP:
nh->ifindex = gr_nh->iface_id;
nh->vrf_id = gr_nh->vrf_id;
nh->ifindex = ifindex_grout_to_frr(gr_nh->iface_id);
nh->vrf_id = ifindex_grout_to_frr(gr_nh->vrf_id);
*nh_family = AF_UNSPEC;
nh->weight = 1;
break;
Expand All @@ -313,11 +314,11 @@ static void grout_route_change(
uint8_t dest_prefixlen,
struct gr_nexthop *gr_nh
) {
uint32_t vrf_id = gr_nh->vrf_id;
// Grout has no per‑VRF routing tables; table_id always equals vrf_id
uint32_t tableid = vrf_id;
uint32_t vrf_id = ifindex_grout_to_frr(gr_nh->vrf_id);
int proto = ZEBRA_ROUTE_KERNEL;
uint32_t nh_id = gr_nh->nh_id;
// Grout has no per‑VRF routing tables; table_id always equals vrf_id
uint32_t tableid = vrf_id;
struct nexthop *nh = NULL;
uint32_t flags = 0;
struct prefix p;
Expand Down Expand Up @@ -463,7 +464,7 @@ enum zebra_dplane_result grout_add_del_route(struct zebra_dplane_ctx *ctx) {
struct gr_ip6_route_del_req r6_del;
} req;
uint32_t nh_id = dplane_ctx_get_nhe_id(ctx);
uint32_t vrf_id = dplane_ctx_get_vrf(ctx);
uint32_t vrf_id = ifindex_frr_to_grout(dplane_ctx_get_vrf(ctx));
const struct prefix *p;
gr_nh_origin_t origin;
uint32_t req_type;
Expand Down Expand Up @@ -667,8 +668,8 @@ grout_add_nexthop(uint32_t nh_id, gr_nh_origin_t origin, const struct nexthop *n
req->nh.nh_id = nh_id;
req->nh.origin = origin;
req->nh.type = type;
req->nh.vrf_id = nh->vrf_id;
req->nh.iface_id = nh->ifindex;
req->nh.vrf_id = ifindex_frr_to_grout(nh->vrf_id);
req->nh.iface_id = ifindex_frr_to_grout(nh->ifindex);

switch (type) {
case GR_NH_T_L3:
Expand Down Expand Up @@ -703,19 +704,27 @@ grout_add_nexthop(uint32_t nh_id, gr_nh_origin_t origin, const struct nexthop *n
break;
case ZEBRA_SEG6_LOCAL_ACTION_END_T:
sr6_local->behavior = SR_BEHAVIOR_END_T;
sr6_local->out_vrf_id = nh->nh_srv6->seg6local_ctx.table;
sr6_local->out_vrf_id = ifindex_frr_to_grout(
nh->nh_srv6->seg6local_ctx.table
);
break;
case ZEBRA_SEG6_LOCAL_ACTION_END_DT6:
sr6_local->behavior = SR_BEHAVIOR_END_DT6;
sr6_local->out_vrf_id = nh->nh_srv6->seg6local_ctx.table;
sr6_local->out_vrf_id = ifindex_frr_to_grout(
nh->nh_srv6->seg6local_ctx.table
);
break;
case ZEBRA_SEG6_LOCAL_ACTION_END_DT4:
sr6_local->behavior = SR_BEHAVIOR_END_DT4;
sr6_local->out_vrf_id = nh->nh_srv6->seg6local_ctx.table;
sr6_local->out_vrf_id = ifindex_frr_to_grout(
nh->nh_srv6->seg6local_ctx.table
);
break;
case ZEBRA_SEG6_LOCAL_ACTION_END_DT46:
sr6_local->behavior = SR_BEHAVIOR_END_DT46;
sr6_local->out_vrf_id = nh->nh_srv6->seg6local_ctx.table;
sr6_local->out_vrf_id = ifindex_frr_to_grout(
nh->nh_srv6->seg6local_ctx.table
);
break;
default:
gr_log_err(
Expand Down Expand Up @@ -822,7 +831,7 @@ void grout_nexthop_change(bool new, struct gr_nexthop *gr_nh, bool startup) {
}

if (!new) {
zebra_nhg_kernel_del(gr_nh->nh_id, gr_nh->vrf_id);
zebra_nhg_kernel_del(gr_nh->nh_id, ifindex_grout_to_frr(gr_nh->vrf_id));
return;
}

Expand All @@ -841,7 +850,17 @@ void grout_nexthop_change(bool new, struct gr_nexthop *gr_nh, bool startup) {
type = origin2zebra(gr_nh->origin, family, false);
SET_FLAG(nh->flags, NEXTHOP_FLAG_ACTIVE);

zebra_nhg_kernel_find(gr_nh->nh_id, nh, NULL, 0, gr_nh->vrf_id, afi, type, startup, NULL);
zebra_nhg_kernel_find(
gr_nh->nh_id,
nh,
NULL,
0,
ifindex_grout_to_frr(gr_nh->vrf_id),
afi,
type,
startup,
NULL
);

// zebra_nhg_kernel_find() makes a *shallow* copy of the allocated nexthop.
// nexthop_free() must *NOT* be used to preserve the nh_srv6 context.
Expand Down
Loading
Loading