diff --git a/main/dpdk.c b/main/dpdk.c index adb11d219..bd084cc24 100644 --- a/main/dpdk.c +++ b/main/dpdk.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -165,7 +166,7 @@ int dpdk_init(void) { else gr_vec_add(eal_args, "2048"); } - } else { + } else if (!gr_config.pdump) { gr_vec_add(eal_args, "--in-memory"); } @@ -186,6 +187,16 @@ int dpdk_init(void) { goto end; } + if (gr_config.pdump) { + ret = rte_pdump_init(); + if (ret < 0) { + ret = -ret; + LOG(ERR, "rte_pdump_init: %s", rte_strerror(ret)); + goto end; + } + LOG(NOTICE, "pdump enabled, secondary processes can attach for packet capture"); + } + char affinity[BUFSIZ]; cpuset_format(affinity, sizeof(affinity), &gr_config.control_cpus); LOG(INFO, "running control plane on CPU %s", affinity); diff --git a/main/gr_config.h b/main/gr_config.h index 30b0a390a..68f594976 100644 --- a/main/gr_config.h +++ b/main/gr_config.h @@ -21,6 +21,7 @@ struct gr_config { bool poll_mode; bool log_syslog; bool log_packets; + bool pdump; gr_vec char **eal_extra_args; cpu_set_t control_cpus; // control plane threads allowed CPUs cpu_set_t datapath_cpus; // datapath threads allowed CPUs diff --git a/main/main.c b/main/main.c index 3d0e8de33..70779e342 100644 --- a/main/main.c +++ b/main/main.c @@ -42,6 +42,7 @@ static void usage(void) { printf(" [-m PERMISSIONS]"); printf(" [-o USER:GROUP]"); printf("\n "); + printf(" [-P]"); printf(" [-p]"); printf(" [-s PATH]"); printf(" [-t]"); @@ -63,6 +64,7 @@ static void usage(void) { puts(" -h, --help Display this help message and exit."); puts(" -m, --socket-mode PERMISSIONS API socket file permissions (Default: 0660)."); puts(" -o, --socket-owner USER:GROUP API socket file ownership"); + puts(" -P, --pdump Enable pdump (shared hugepages for tcpdump)."); puts(" -p, --poll-mode Disable automatic micro-sleep."); puts(" -s, --socket PATH Path the control plane API socket."); puts(" Default: GROUT_SOCK_PATH from env or"); @@ -176,11 +178,12 @@ static int parse_sock_owner(char *user_group_str) { static int parse_args(int argc, char **argv) { int c; -#define FLAGS ":M:Vhm:o:pSs:tu:vx" +#define FLAGS ":M:VhPm:o:pSs:tu:vx" static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"max-mtu", required_argument, NULL, 'u'}, {"metrics", required_argument, NULL, 'M'}, + {"pdump", no_argument, NULL, 'P'}, {"poll-mode", no_argument, NULL, 'p'}, {"socket", required_argument, NULL, 's'}, {"socket-mode", required_argument, NULL, 'm'}, @@ -221,6 +224,9 @@ static int parse_args(int argc, char **argv) { if (parse_sock_owner(optarg) < 0) return errno_set(EINVAL); break; + case 'P': + gr_config.pdump = true; + break; case 'p': gr_config.poll_mode = true; break; diff --git a/meson.build b/meson.build index 845c629ea..e1e2fddcb 100644 --- a/meson.build +++ b/meson.build @@ -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,pdump,bpf,gso,vhost,cryptodev,dmadev,security', 'disable_apps=*', 'enable_docs=false', 'developer_mode=disabled', @@ -156,6 +156,42 @@ pkg.generate( install_dir: get_option('datadir') / 'pkgconfig', ) +# libpcap with DPDK pdump support (for tcpdump packet capture against grout). +# Builds libpcap.so with grout:N device support. Stock tcpdump picks it up +# via LD_LIBRARY_PATH or system install — no tcpdump recompilation needed. +libpcap_srcdir = meson.current_source_dir() / 'subprojects' / 'libpcap' +libpcap_builddir = meson.current_build_dir() / 'libpcap-build' + +dpdk_pkgconfig_path = meson.global_build_root() / 'meson-uninstalled' +dpdk_pc_path = meson.global_build_root() / 'meson-private' + +libpcap_build = custom_target( + 'libpcap', + output: '.libpcap-stamp', + command: [ + 'sh', '-c', + 'mkdir -p "' + libpcap_builddir + '" && ' + + 'cd "' + libpcap_builddir + '" && ' + + 'export PKG_CONFIG_PATH="' + dpdk_pkgconfig_path + ':' + dpdk_pc_path + ':$PKG_CONFIG_PATH" && ' + + 'DPDK_LIBDIR="' + meson.global_build_root() + '/subprojects/dpdk/lib" && ' + + 'DPDK_DRVDIR="' + meson.global_build_root() + '/subprojects/dpdk/drivers" && ' + + 'cmake "' + libpcap_srcdir + '" ' + + '-DDISABLE_DPDK:BOOL=OFF ' + + '-DHAVE_RTE_ETH_DEV_COUNT_AVAIL:BOOL=ON ' + + '-DHAVE_STRUCT_RTE_ETHER_ADDR:BOOL=ON ' + + '-DDISABLE_DBUS:BOOL=ON ' + + '-DDISABLE_RDMA:BOOL=ON ' + + '-DDISABLE_BLUETOOTH:BOOL=ON ' + + '-DDISABLE_NETMAP:BOOL=ON ' + + '-DBUILD_SHARED_LIBS:BOOL=ON ' + + '"-DCMAKE_SHARED_LINKER_FLAGS=-L$DPDK_LIBDIR -L$DPDK_DRVDIR -Wl,-rpath,$DPDK_LIBDIR:$DPDK_DRVDIR" ' + + '&& make -j pcap && ' + + 'ln -sf libpcap.so.1 libpcap.so.0.8 && ' + + 'touch "@OUTPUT@"', + ], + build_by_default: true, +) + cmocka_dep = dependency('cmocka', required: get_option('tests')) if cmocka_dep.found() fs = import('fs') diff --git a/subprojects/libpcap.wrap b/subprojects/libpcap.wrap new file mode 100644 index 000000000..504e38522 --- /dev/null +++ b/subprojects/libpcap.wrap @@ -0,0 +1,6 @@ +[wrap-git] +url = https://github.com/vjardin/libpcap +revision = vj_add_dpdk_grout +depth = 1 + +[provide] diff --git a/subprojects/packagefiles/libpcap/grout-tcpdump.sh b/subprojects/packagefiles/libpcap/grout-tcpdump.sh new file mode 100755 index 000000000..f3a3f8db8 --- /dev/null +++ b/subprojects/packagefiles/libpcap/grout-tcpdump.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# SPDX-License-Identifier: BSD-3-Clause +# Copyright (c) 2026 Vincent Jardin, Free Mobile, Iliad +# +# Wrapper to run system tcpdump with grout's libpcap (DPDK pdump support). +# +# This is a temporary workaround until libpcap merges the DPDK pdump +# capture module upstream and Linux distributions enable it at compile +# time. Track progress at: +# https://github.com/the-tcpdump-group/libpcap/pull/1656 +# +# Once distributions ship a libpcap with PCAP_SUPPORT_DPDK_PDUMP, +# this script is no longer needed — tcpdump will natively support +# "grout:N" devices. +# +# Usage: sudo ./subprojects/packagefiles/libpcap/grout-tcpdump.sh -i grout:0 -n + +SCRIPT="$(readlink -f "$0")" +BASEDIR="$(dirname "$SCRIPT")/../../.." +LIBPCAP_DIR="$BASEDIR/build/libpcap-build" +DPDK_LIBDIR="$BASEDIR/build/subprojects/dpdk/lib" +DPDK_DRVDIR="$BASEDIR/build/subprojects/dpdk/drivers" + +if [ ! -f "$LIBPCAP_DIR/libpcap.so" ]; then + echo "error: $LIBPCAP_DIR/libpcap.so not found, build grout first" >&2 + exit 1 +fi + +# Our libpcap.so.0.8 symlink is found before the system one. +export LD_LIBRARY_PATH="$LIBPCAP_DIR:$DPDK_LIBDIR:$DPDK_DRVDIR${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" + +# Tell the DPDK secondary process where to find PMD drivers. +# The compiled-in path (RTE_EAL_PMD_PATH) points to an install prefix +# that doesn't exist in a development build tree. +export DPDK_CFG="--proc-type=secondary -l0 --no-telemetry --log-level=critical -d $DPDK_DRVDIR" + +exec tcpdump "$@"