8000 frr: add FRR dplane plugin by maxime-leroy · Pull Request #220 · DPDK/grout · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

frr: add FRR dplane plugin #220

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 18 additions & 1 deletion .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,29 +26,35 @@ jobs:
sanitize: none
buildtype: debugoptimized
os: ubuntu-24.04
extra_opts: -Dfrr=enabled
- compiler: gcc-14
sanitize: address
buildtype: debug
os: ubuntu-24.04
extra_opts:
- compiler: clang-15
sanitize: none
buildtype: debugoptimized
os: ubuntu-22.04
extra_opts:
- compiler: clang-16
sanitize: none
buildtype: debugoptimized
os: ubuntu-24.04
extra_opts:
- compiler: clang-18
sanitize: none
buildtype: debugoptimized
os: ubuntu-24.04
extra_opts:
runs-on: ${{ matrix.conf.os }}
env:
SANITIZE: ${{ matrix.conf.sanitize }}
BUILDTYPE: ${{ matrix.conf.buildtype }}
DEBIAN_FRONTEND: noninteractive
NEEDRESTART_MODE: l
CC: ccache ${{ matrix.conf.compiler }}
MESON_EXTRA_OPTS: ${{ matrix.conf.extra_opts }}
steps:
- name: install system dependencies
run: |
Expand All @@ -58,7 +64,18 @@ jobs:
make gcc ccache ninja-build meson git go-md2man libibverbs-dev \
libasan8 libcmocka-dev libedit-dev libarchive-dev \
libevent-dev libsmartcols-dev libnuma-dev python3-pyelftools \
socat tcpdump traceroute graphviz iproute2 iputils-ping ndisc6
socat tcpdump traceroute graphviz iproute2 iputils-ping ndisc6 jq
if echo $MESON_EXTRA_OPTS | grep -q frr=enabled ; then
sudo apt-get install -qy --no-install-recommends \
libjson-c-dev libelf-dev libprotobuf-c-dev protobuf-c-compiler \
libreadline-dev libcap-dev
sudo mkdir -p /etc/apt/keyrings
sudo curl -s -o /etc/apt/keyrings/frrouting.gpg https://deb.frrouting.org/frr/keys.gpg
echo "deb [signed-by=/etc/apt/keyrings/frrouting.gpg] https://deb.frrouting.org/frr \
$(lsb_release -s -c) frr-stable" | sudo tee /etc/apt/sources.list.d/frr.list > /dev/null
sudo apt-get update -qy
sudo apt-get install -qy librtr-dev libyang2-dev libyang2-tools
fi
if printf '%s\n0.63.0\n' "$(meson --version)" | sort -V -C ; then
sudo apt install -y python3-pip
pip3 install --user -U meson~=0.63.0
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@
/redhat-linux-build
/subprojects/dpdk
/subprojects/ecoli
/subprojects/frr
12 changes: 9 additions & 3 deletions GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ rpm:
CLANG_FORMAT ?= clang-format
c_src = git ls-files '*.[ch]' ':!:subprojects'
all_files = git ls-files ':!:subprojects'
licensed_files = git ls-files ':!:*.svg' ':!:LICENSE' ':!:*.md' ':!:*.asc' ':!:subprojects' ':!:debian' ':!:.*'
licensed_files = git ls-files ':!:*.svg' ':!:licenses' ':!:*.md' ':!:*.asc' ':!:subprojects' ':!:debian' ':!:.*'

.PHONY: lint
lint:
Expand All @@ -103,8 +103,14 @@ lint:
$(CLANG_FORMAT) --files="$$tmp" --dry-run --Werror
@echo '[license-check]'
$Q ! $(licensed_files) | while read -r f; do \
if ! grep -qF 'SPDX-License-Identifier: BSD-3-Clause' $$f; then \
echo $$f; \
if echo "$$f" | grep -q '^frr/'; then \
if ! grep -qF 'SPDX-License-Identifier: GPL-2.0-or-later' "$$f"; then \
echo "$$f"; \
fi; \
else \
if ! grep -qF 'SPDX-License-Identifier: BSD-3-Clause' $$f; then \
echo $$f; \
fi; \
fi; \
if ! grep -q 'Copyright .* [0-9]\{4\} .*' $$f; then \
echo $$f; \
Expand Down
7 changes: 7 additions & 0 deletions README.md
5D39
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ also in scripts one command at a time, or by batches.
## License

[BSD-3-Clause](https://spdx.org/licenses/BSD-3-Clause.html)
[GPL-2.0-or-later](https://spdx.org/licenses/GPL-2.0-or-later.html) — only for `frr`

## Features

Expand Down Expand Up @@ -315,3 +316,9 @@ Anyone can contribute to `grout`. See [`CONTRIBUTING.md`](/CONTRIBUTING.md).
| go-md2man | Build | MIT | https://github.com/cpuguy83/go-md2man |
| libasan | Dev | MIT+BSD | https://github.com/gcc-mirror/gcc/tree/master/libsanitizer |
| clang-format | Dev | MIT+BSD | https://clang.llvm.org/docs/ClangFormat.html |

Optional (compiled with `-Dfrr=enabled`):

| Name | Type | License | Code |
|------|------|---------|------|
| FRR | Build & Runtime | GPL-2.0-or-later | https://github.com/FRRouting/frr |
12 changes: 12 additions & 0 deletions frr/frr_plugin_install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (c) 2025 Maxime Leroy, Free Mobile

set -eu

# $1 = built .so, $2 = destination .so, $3 = daemons file, $4 = stamp file
install -D -m 755 "$1" "$2"
sed -i -e '/^zebra_options=/ {
/-M[[:space:]]*frr_dplane_grout/! s/"$/ -M frr_dplane_grout"/
}' "$3"
touch "$4"
264 changes: 264 additions & 0 deletions frr/if_grout.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Maxime Leroy, Free Mobile

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <net/if.h>
#ifdef GNU_LINUX
#include <linux/if.h>
#endif /* GNU_LINUX */

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

#include <gr_ip4.h>
#include <gr_ip6.h>

#include <zebra/interface.h>
#include <zebra_dplane_grout.h>

// ugly hack to avoid collision with ifindex kernel
// Don't use 1<<32, because ietf-interfaces.yang defined int32, not uint32
// else it triggers assert in
// `libyang: Unsatisfied raInge - value "-2147483647" is out of the allowed range`
#define GROUT_INDEX_OFFSET (1000000000U) // 1<<30U , round-up to lower decimal numbers
#define GROUT_NS NS_DEFAULT

static uint64_t gr_if_flags_to_netlink(struct gr_iface *gr_if, enum zebra_link_type link_type) {
uint64_t frr_if_flags = 0;

if (link_type == ZEBRA_LLT_LOOPBACK)
frr_if_flags |= IFF_LOOPBACK;

if (gr_if->base.flags & GR_IFACE_F_UP)
frr_if_flags |= IFF_UP;
if (gr_if->base.flags & GR_IFACE_F_PROMISC)
frr_if_flags |= IFF_PROMISC;
if (gr_if->base.flags & GR_IFACE_F_ALLMULTI)
frr_if_flags |= IFF_ALLMULTI;
if (gr_if->base.state & GR_IFACE_S_RUNNING)
frr_if_flags |= IFF_RUNNING | IFF_LOWER_UP;

// Force BROADCAST and MULTICAST
if (link_type == ZEBRA_LLT_ETHER)
frr_if_flags |= IFF_BROADCAST | IFF_MULTICAST;

return frr_if_flags;
}

void grout_link_change(struct gr_iface *gr_if, bool new, bool startup) {
struct zebra_dplane_ctx *ctx = dplane_ctx_alloc();
enum zebra_link_type link_type = ZEBRA_LLT_UNKNOWN;
enum zebra_iftype zif_type = ZEBRA_IF_OTHER;
const struct gr_iface_info_vlan *gr_vlan = NULL;
const struct gr_iface_info_port *gr_port = NULL;
ifindex_t link_ifindex = IFINDEX_INTERNAL;
const struct rte_ether_addr *mac = NULL;
uint32_t txqlen = 1000;

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 + GROUT_INDEX_OFFSET;
zif_type = ZEBRA_IF_VLAN;
link_type = ZEBRA_LLT_ETHER;
break;
case GR_IFACE_TYPE_PORT:
gr_port = (struct gr_iface_info_port *)&gr_if->info;
txqlen = gr_port->base.txq_size;
mac = &gr_port->base.mac;
link_type = ZEBRA_LLT_ETHER;
break;
case GR_IFACE_TYPE_IPIP:
link_type = ZEBRA_LLT_IPIP;
break;
case GR_IFACE_TYPE_LOOPBACK:
link_type = ZEBRA_LLT_LOOPBACK;
break;
case GR_IFACE_TYPE_UNDEF:
default:
gr_log_err(
"iface '%s' unknown type (%u) can not be sync",
gr_if->name,
gr_if->base.type
);
return;
}

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 + GROUT_INDEX_OFFSET);
dplane_ctx_set_ifname(ctx, gr_if->name);
dplane_ctx_set_ifp_startup(ctx, startup);
dplane_ctx_set_ifp_family(ctx, AF_UNSPEC);
dplane_ctx_set_intf_txqlen(ctx, txqlen);

if (new) {
dplane_ctx_set_ifp_link_ifindex(ctx, link_ifindex);
dplane_ctx_set_op(ctx, DPLANE_OP_INTF_INSTALL);
dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_QUEUED);
dplane_ctx_set_ifp_mtu(ctx, gr_if->base.mtu);

// No VRF support
if (gr_if->base.vrf_id != 0) {
gr_log_err(
"VRF are not supported, interface %s on vrf %u can not be sync",
gr_if->name,
gr_if->base.vrf_id
);
dplane_ctx_fini(&ctx);
return;
}

// no bond/bridge support in grout
dplane_ctx_set_ifp_zif_slave_type(ctx, ZEBRA_IF_SLAVE_NONE);
dplane_ctx_set_ifp_vrf_id(ctx, 0);
dplane_ctx_set_ifp_master_ifindex(ctx, IFINDEX_INTERNAL);
dplane_ctx_set_ifp_bridge_ifindex(ctx, IFINDEX_INTERNAL);
dplane_ctx_set_ifp_bond_ifindex(ctx, IFINDEX_INTERNAL);
dplane_ctx_set_ifp_bypass(ctx, 0);
dplane_ctx_set_ifp_zltype(ctx, link_type);

if (vrf_is_backend_netns())
dplane_ctx_set_ifp_vrf_id(ctx, GROUT_NS);

dplane_ctx_set_ifp_flags(ctx, gr_if_flags_to_netlink(gr_if, link_type));
dplane_ctx_set_ifp_protodown_set(ctx, false);

if (mac)
dplane_ctx_set_ifp_hw_addr(
ctx, sizeof(struct rte_ether_addr), (uint8_t *)mac
);

// Extract and save L2 interface information, take
// additional actions.
if (gr_vlan) {
struct zebra_l2info_vlan vlan_info = {};

vlan_info.vid = gr_vlan->vlan_id;
dplane_ctx_set_ifp_vlan_info(ctx, &vlan_info);
}
} else {
dplane_ctx_set_op(ctx, DPLANE_OP_INTF_DELETE);
dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_QUEUED);
}

dplane_provider_enqueue_to_zebra(ctx);
}

void grout_interface_addr_dplane(struct gr_nexthop *gr_nh, bool new) {
struct zebra_dplane_ctx *ctx = dplane_ctx_alloc();
struct prefix p = {};

if (gr_nh->vrf_id != 0) {
gr_log_err("VRF are not supported");
dplane_ctx_fini(&ctx);
return;
}

if (new)
dplane_ctx_set_op(ctx, DPLANE_OP_INTF_ADDR_ADD);
else
dplane_ctx_set_op(ctx, DPLANE_OP_INTF_ADDR_DEL);

dplane_ctx_set_ifindex(ctx, gr_nh->iface_id + GROUT_INDEX_OFFSET);
dplane_ctx_set_ns_id(ctx, GROUT_NS);

// Convert addr to prefix
p.prefixlen = gr_nh->prefixlen;
if (gr_nh->type == GR_NH_IPV4) {
p.family = AF_INET;
p.u.prefix4.s_addr = gr_nh->ipv4;
} else {
p.family = AF_INET6;
memcpy(&p.u.prefix6, &gr_nh->ipv6, sizeof(p.u.prefix6));
}
dplane_ctx_set_intf_addr(ctx, &p);
dplane_ctx_set_intf_metric(ctx, METRIC_MAX);

// Enqueue ctx for main pthread to process
dplane_provider_enqueue_to_zebra(ctx);
}

enum zebra_dplane_result grout_add_del_address(struct zebra_dplane_ctx *ctx) {
int gr_iface_id = dplane_ctx_get_ifindex(ctx) - GROUT_INDEX_OFFSET;
const struct prefix *p = dplane_ctx_get_intf_addr(ctx);
union {
struct gr_ip4_addr_add_req ip4_add;
struct gr_ip4_addr_del_req ip4_del;
struct gr_ip6_addr_add_req ip6_add;
struct gr_ip6_addr_del_req ip6_del;
} req;
uint32_t req_type;
size_t req_len;

if (dplane_ctx_get_vrf(ctx) != 0) {
gr_log_err(
"impossible to add/del address on vrf %u (vrf not supported)",
dplane_ctx_get_vrf(ctx)
);
return ZEBRA_DPLANE_REQUEST_FAILURE;
}

if (p->family != AF_INET && p->family != AF_INET6) {
gr_log_err(
"impossible to add/del address with family %u (not supported)", p->family
);
return ZEBRA_DPLANE_REQUEST_FAILURE;
}

if (p->family == AF_INET) {
struct gr_ip4_ifaddr *ip4_addr;

if (dplane_ctx_get_op(ctx) == DPLANE_OP_ADDR_INSTALL) {
req.ip4_add = (struct gr_ip4_addr_add_req) {.exist_ok = true};

req_type = GR_IP4_ADDR_ADD;
req_len = sizeof(struct gr_ip4_addr_add_req);

ip4_addr = &req.ip4_add.addr;
} else {
req.ip4_del = (struct gr_ip4_addr_del_req) {.missing_ok = true};

req_type = GR_IP4_ADDR_DEL;
req_len = sizeof(struct gr_ip4_addr_del_req);

ip4_addr = &req.ip4_del.addr;
}

ip4_addr->addr.ip = p->u.prefix4.s_addr;
ip4_addr->addr.prefixlen = p->prefixlen;
ip4_addr->iface_id = gr_iface_id;
} else {
struct gr_ip6_ifaddr *ip6_addr;

if (dplane_ctx_get_op(ctx) == DPLANE_OP_ADDR_INSTALL) {
req.ip6_add = (struct gr_ip6_addr_add_req) {.exist_ok = true};

req_type = GR_IP6_ADDR_ADD;
req_len = sizeof(struct gr_ip6_addr_add_req);

ip6_addr = &req.ip6_add.addr;
} else {
req.ip6_del = (struct gr_ip6_addr_del_req) {.missing_ok = true};

req_type = GR_IP6_ADDR_DEL;
req_len = sizeof(struct gr_ip6_addr_del_req);

ip6_addr = &req.ip6_del.addr;
}

memcpy(ip6_addr->addr.ip.a, p->u.prefix6.s6_addr, sizeof(ip6_addr->addr.ip.a));
ip6_addr->addr.prefixlen = p->prefixlen;
ip6_addr->iface_id = gr_iface_id;
}

if (grout_client_send_recv(req_type, req_len, &req, NULL) < 0)
return ZEBRA_DPLANE_REQUEST_FAILURE;

return ZEBRA_DPLANE_REQUEST_SUCCESS;
}
Loading
0