Skip to content
Draft
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
5 changes: 3 additions & 2 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ jobs:
libibverbs-dev libasan8 libcmocka-dev libedit-dev libarchive-dev \
libevent-dev libmnl-dev libnuma-dev python3-pyelftools \
socat tcpdump traceroute graphviz iproute2 iputils-ping ndisc6 jq \
dnsmasq systemd-coredump \
dnsmasq systemd-coredump libpcap-dev libbpf-dev \
"linux-modules-extra-$(uname -r)"
if echo $MESON_EXTRA_OPTS | grep -q frr=enabled ; then
sudo apt-get install -qy --no-install-recommends \
Expand Down Expand Up @@ -148,7 +148,8 @@ jobs:
apt install -qy --no-install-recommends \
make gcc ccache git meson scdoc python3-pyelftools ca-certificates pkg-config \
crossbuild-essential-arm64 libcmocka-dev:arm64 libedit-dev:arm64 \
libevent-dev:arm64 libmnl-dev:arm64 libnuma-dev:arm64
libevent-dev:arm64 libmnl-dev:arm64 libnuma-dev:arm64 \
libpcap-dev:arm64 libbpf-dev:arm64
- uses: actions/checkout@v4
with:
persist-credentials: false
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
libjson-c-dev libmnl-dev libnuma-dev libprotobuf-c-dev libreadline-dev \
librtr-dev libtool libyang-dev meson ninja-build \
patch pkg-config protobuf-c-compiler python3-dev python3-pyelftools \
systemd-dev texinfo
systemd-dev texinfo libpcap-dev libbpf-dev
- uses: actions/checkout@v4
with:
fetch-depth: 0 # force fetch all history
Expand Down Expand Up @@ -99,7 +99,7 @@ jobs:
libyang-devel libtool make meson ninja-build \
numactl-devel pkgconf protobuf-c-compiler protobuf-c-devel \
python3-pyelftools python3-devel rdma-core-devel readline-devel \
rpm-build scl-utils systemd
rpm-build scl-utils systemd libpcap-devel libbpf-devel
- uses: actions/checkout@v4
with:
fetch-depth: 0 # force fetch all history
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ image.
dnf install git gcc make meson ninja-build pkgconf \
python3-pyelftools scdoc libmnl-devel \
libcmocka-devel libedit-devel libevent-devel numactl-devel \
libarchive-devel rdma-core-devel
libarchive-devel rdma-core-devel libpcap-devel libbpf-devel
```

or
Expand All @@ -270,7 +270,7 @@ or
apt install git gcc make meson ninja-build pkgconf \
python3-pyelftools scdoc \
libcmocka-dev libedit-dev libevent-dev libnuma-dev libmnl-dev \
libarchive-dev libibverbs-dev
libarchive-dev libibverbs-dev libpcap-dev libbpf-dev
```

Important: `grout` requires at least `gcc` 13 or `clang` 15.
Expand Down
938 changes: 478 additions & 460 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.
9 changes: 7 additions & 2 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ dpdk_dep = dependency(
'werror=false',
'tests=false',
'enable_drivers=net/virtio,net/vhost,net/i40e,net/ice,net/iavf,net/ixgbe,net/null,net/tap,common/mlx5,net/mlx5,bus/auxiliary,net/vmxnet3',
'enable_libs=graph,hash,fib,rib,pcapng,gso,vhost,cryptodev,dmadev,security',
'enable_libs=graph,hash,fib,rib,pcapng,bpf,gso,vhost,cryptodev,dmadev,security',
'disable_apps=*',
'enable_docs=false',
'developer_mode=disabled',
Expand Down Expand Up @@ -103,6 +103,11 @@ elif compiler.has_function(
grout_cflags += ['-DHAVE_RTE_FIB_TBL8_GET_STATS']
endif

pcap_dep = dependency('libpcap', required: false, method: 'pkg-config')
if not pcap_dep.found()
pcap_dep = compiler.find_library('pcap', required: true)
endif

src = []
inc = []

Expand All @@ -124,7 +129,7 @@ subdir('frr')
grout_exe = executable(
'grout', src,
include_directories: inc + api_inc,
dependencies: [dpdk_dep, ev_core_dep, ev_extra_dep, ev_thread_dep, mnl_dep, numa_dep],
dependencies: [dpdk_dep, ev_core_dep, ev_extra_dep, ev_thread_dep, mnl_dep, numa_dep, pcap_dep],
c_args: ['-D__GROUT_MAIN__'] + grout_cflags,
install: true,
)
Expand Down
15 changes: 15 additions & 0 deletions modules/infra/api/gr_infra.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ typedef enum : uint16_t {
GR_IFACE_F_PACKET_TRACE = GR_BIT16(2),
GR_IFACE_F_SNAT_STATIC = GR_BIT16(3),
GR_IFACE_F_SNAT_DYNAMIC = GR_BIT16(4),
GR_IFACE_F_MIRROR = GR_BIT16(5),
} gr_iface_flags_t;

// Interface state flags.
Expand Down Expand Up @@ -70,6 +71,9 @@ typedef enum : uint8_t {
#define GR_IFACE_SET_VRF GR_BIT64(3)
#define GR_IFACE_SET_DOMAIN GR_BIT64(4)
#define GR_IFACE_SET_DESCR GR_BIT64(5)
#define GR_IFACE_SET_MIRROR_FILTER GR_BIT64(6)

#define GR_IFACE_MIRROR_FILTER_SIZE 64

// Generic struct for all network interfaces.
struct __gr_iface_base {
Expand All @@ -94,6 +98,7 @@ struct gr_iface {

char name[IFNAMSIZ]; // NUL terminated.
char description[256]; // NUL terminated.
char mirror_filter[GR_IFACE_MIRROR_FILTER_SIZE]; // BPF filter (tcpdump expr), empty = none.
uint8_t info[]; // Type specific interface info.
};

Expand Down Expand Up @@ -436,6 +441,16 @@ struct gr_affinity_cpu_set_req {

// struct gr_affinity_cpu_set_resp { };

// Mirror capture (pcapng)
#define GR_MIRROR_CAPTURE_SET REQUEST_TYPE(GR_INFRA_MODULE, 0x0080)

#define GR_MIRROR_CAPTURE_PATH_SIZE 256

struct gr_mirror_capture_set_req {
bool enabled;
char path[GR_MIRROR_CAPTURE_PATH_SIZE];
};

// Helper function to convert iface type enum to string
static inline const char *gr_iface_type_name(gr_iface_type_t type) {
switch (type) {
Expand Down
11 changes: 10 additions & 1 deletion modules/infra/cli/gr_cli_iface.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,23 @@ int arg_iface(
CLI_CONTEXT(root, INTERFACE_ARG, CTX_ARG("set", "Modify an existing interface."))

#define IFACE_ATTRS_CMD \
"(up|down),(promisc PROMISC),(mtu MTU),((vrf VRF)|(domain DOMAIN)),(description DESCR)"
"(up|down),(promisc PROMISC),(mtu MTU),((vrf VRF)|(domain DOMAIN)),(description DESCR)," \
"(mirror MIRROR),(filter FILTER)"

#define IFACE_ATTRS_ARGS \
with_help("Set the interface UP.", ec_node_str("up", "up")), \
with_help( \
"Enable/disable promiscuous mode.", \
EC_NODE_OR("PROMISC", ec_node_str("", "on"), ec_node_str("", "off")) \
), \
with_help( \
"Enable/disable port mirroring.", \
EC_NODE_OR("MIRROR", ec_node_str("", "on"), ec_node_str("", "off")) \
), \
with_help( \
"BPF filter expression (tcpdump syntax), empty to clear.", \
ec_node("any", "FILTER") \
), \
with_help("Set the interface DOWN.", ec_node_str("down", "down")), \
with_help( \
"Maximum transmission unit size.", \
Expand Down
29 changes: 28 additions & 1 deletion modules/infra/cli/iface.c
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ static ssize_t iface_flags_format(char *buf, size_t len, const struct gr_iface *
SAFE_BUF(snprintf, len, " tracing");
if (iface->flags & (GR_IFACE_F_SNAT_STATIC | GR_IFACE_F_SNAT_DYNAMIC))
SAFE_BUF(snprintf, len, " snat");
if (iface->flags & GR_IFACE_F_MIRROR)
SAFE_BUF(snprintf, len, " mirror");

return n;
err:
Expand All @@ -223,7 +225,7 @@ uint64_t parse_iface_args(
size_t info_size,
bool update
) {
const char *name, *promisc;
const char *name, *promisc, *mirror, *filter;
uint64_t set_attrs = 0;

name = arg_str(p, "NAME");
Expand Down Expand Up @@ -256,6 +258,24 @@ uint64_t parse_iface_args(
iface->flags &= ~GR_IFACE_F_PROMISC;
set_attrs |= GR_IFACE_SET_FLAGS;
}
mirror = arg_str(p, "MIRROR");
if (mirror != NULL && strcmp(mirror, "on") == 0) {
iface->flags |= GR_IFACE_F_MIRROR;
set_attrs |= GR_IFACE_SET_FLAGS;
} else if (mirror != NULL && strcmp(mirror, "off") == 0) {
iface->flags &= ~GR_IFACE_F_MIRROR;
set_attrs |= GR_IFACE_SET_FLAGS;
}
filter = arg_str(p, "FILTER");
if (filter != NULL) {
if (strlen(filter) >= GR_IFACE_MIRROR_FILTER_SIZE) {
errno = ENAMETOOLONG;
goto err;
}
memccpy(iface->mirror_filter, filter, 0, GR_IFACE_MIRROR_FILTER_SIZE);
iface->mirror_filter[GR_IFACE_MIRROR_FILTER_SIZE - 1] = '\0';
set_attrs |= GR_IFACE_SET_MIRROR_FILTER;
}

if (arg_u16(p, "MTU", &iface->mtu) == 0)
set_attrs |= GR_IFACE_SET_MTU;
Expand Down Expand Up @@ -517,6 +537,13 @@ static cmd_status_t iface_show(struct gr_api_client *c, const struct ec_pnode *p
else
gr_object_field(o, "speed", GR_DISP_INT, "%u", iface->speed);

if (iface->flags & GR_IFACE_F_MIRROR) {
if (iface->mirror_filter[0] != '\0')
printf("mirror-filter: %s\n", iface->mirror_filter);
else
printf("mirror-filter: (none)\n");
}

type = type_from_id(iface->type);
assert(type != NULL);
type->show(c, iface, o);
Expand Down
7 changes: 7 additions & 0 deletions modules/infra/control/gr_iface.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <gr_bitops.h>
#include <gr_infra.h>
#include <gr_mbuf.h>
#include <gr_metrics.h>
#include <gr_vec.h>

Expand All @@ -14,15 +15,19 @@
#include <stdint.h>
#include <sys/queue.h>

struct rte_bpf;

struct __rte_cache_aligned iface {
BASE(__gr_iface_base);

gr_vec struct iface **subinterfaces;
char *name;
char *description;
char *mirror_filter;
int cp_id; // Control plane (Linux) port ID
int cp_fd; // control plane fd
struct event *cp_ev; // libevent to poll cp_fd
struct rte_bpf *mirror_bpf; // Compiled BPF filter, NULL = mirror all.
alignas(alignof(void *)) uint8_t info[/* size depends on type */];
};

Expand Down Expand Up @@ -82,6 +87,8 @@ int iface_set_up_down(struct iface *, bool up);
int iface_set_promisc(struct iface *, bool enabled);
uint16_t ifaces_count(gr_iface_type_t type_id);
struct iface *iface_next(gr_iface_type_t type_id, const struct iface *prev);
int iface_mirror_filter_compile(const char *expr, struct rte_bpf **out);
void iface_mirror_filter_destroy(struct rte_bpf *bpf);

uint16_t vrf_default_get_or_create(void);

Expand Down
36 changes: 36 additions & 0 deletions modules/infra/control/iface.c
Original file line number Diff line number Diff line change
Expand Up @@ -373,11 +373,45 @@ int iface_reconfig(
goto err;
}

if (set_attrs & GR_IFACE_SET_MIRROR_FILTER) {
size_t len = strnlen(conf->mirror_filter, sizeof(conf->mirror_filter));
char *new_mirror_filter = NULL;

if (len >= sizeof(conf->mirror_filter))
return errno_set(ENAMETOOLONG);
if (len > 0) {
new_mirror_filter = strdup(conf->mirror_filter);
if (new_mirror_filter == NULL)
return errno_set(ENOMEM);
}
free(iface->mirror_filter);
iface->mirror_filter = new_mirror_filter;
if (iface->mirror_filter[0] != '\0') {
struct rte_bpf *old_bpf = iface->mirror_bpf;
struct rte_bpf *new_bpf = NULL;
if((ret = iface_mirror_filter_compile(conf->mirror_filter, &new_bpf)) < 0)
return errno_set(-ret);
iface->mirror_bpf = new_bpf;
rte_rcu_qsbr_synchronize(gr_datapath_rcu(), RTE_QSBR_THRID_INVALID);
iface_mirror_filter_destroy(old_bpf);
}
}


if (set_attrs & GR_IFACE_SET_FLAGS) {
if ((ret = iface_set_promisc(iface, conf->flags & GR_IFACE_F_PROMISC)) < 0)
goto err;
if ((ret = iface_set_up_down(iface, conf->flags & GR_IFACE_F_UP)) < 0)
goto err;
if (conf->flags & GR_IFACE_F_MIRROR) {
iface->flags |= GR_IFACE_F_MIRROR;
} else {
iface->flags &= ~GR_IFACE_F_MIRROR;
struct rte_bpf *old = iface->mirror_bpf;
iface->mirror_bpf = NULL;
rte_rcu_qsbr_synchronize(gr_datapath_rcu(), RTE_QSBR_THRID_INVALID);
iface_mirror_filter_destroy(old);
}
}

if (set_attrs & GR_IFACE_SET_VRF) {
Expand Down Expand Up @@ -612,6 +646,8 @@ int iface_destroy(struct iface *iface) {
iface->flags &= ~GR_IFACE_F_UP;
gr_event_push(GR_EVENT_IFACE_STATUS_DOWN, iface);
}
iface_mirror_filter_destroy(iface->mirror_bpf);
iface->mirror_bpf = NULL;
detach_domain(iface);

ifaces[iface->id] = NULL;
Expand Down
1 change: 1 addition & 0 deletions modules/infra/control/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ src += files(
'lacp.c',
'loopback.c',
'mempool.c',
'mirror.c',
'netlink.c',
'nexthop.c',
'port.c',
Expand Down
Loading
Loading