Context |
Check |
Description |
netdev/series_format |
success
|
Posting correctly formatted
|
netdev/tree_selection |
success
|
Clearly marked for bpf-next
|
netdev/ynl |
success
|
Generated files up to date;
no warnings/errors;
no diff in generated;
|
netdev/fixes_present |
success
|
Fixes tag not required for -next series
|
netdev/header_inline |
success
|
No static functions without inline keyword in header files
|
netdev/build_32bit |
success
|
Errors and warnings before: 8 this patch: 8
|
netdev/build_tools |
success
|
Errors and warnings before: 0 this patch: 0
|
netdev/cc_maintainers |
warning
|
11 maintainers not CCed: kpsingh@kernel.org shuah@kernel.org haoluo@google.com daniel@iogearbox.net john.fastabend@gmail.com jolsa@kernel.org linux-kselftest@vger.kernel.org yonghong.song@linux.dev mykolal@fb.com eddyz87@gmail.com sdf@fomichev.me
|
netdev/build_clang |
success
|
Errors and warnings before: 8 this patch: 8
|
netdev/verify_signedoff |
success
|
Signed-off-by tag matches author and committer
|
netdev/deprecated_api |
success
|
None detected
|
netdev/check_selftest |
success
|
No net selftest shell script
|
netdev/verify_fixes |
success
|
No Fixes tag
|
netdev/build_allmodconfig_warn |
success
|
Errors and warnings before: 8 this patch: 8
|
netdev/checkpatch |
warning
|
WARNING: line length of 86 exceeds 80 columns
WARNING: line length of 88 exceeds 80 columns
WARNING: line length of 96 exceeds 80 columns
|
netdev/build_clang_rust |
success
|
No Rust files in patch. Skipping build
|
netdev/kdoc |
success
|
Errors and warnings before: 2 this patch: 2
|
netdev/source_inline |
success
|
Was 0 now: 0
|
bpf/vmtest-bpf-next-VM_Test-3 |
success
|
Logs for Validate matrix.py
|
bpf/vmtest-bpf-next-VM_Test-1 |
success
|
Logs for ShellCheck
|
bpf/vmtest-bpf-next-VM_Test-5 |
success
|
Logs for aarch64-gcc / build-release
|
bpf/vmtest-bpf-next-VM_Test-0 |
success
|
Logs for Lint
|
bpf/vmtest-bpf-next-VM_Test-2 |
success
|
Logs for Unittests
|
bpf/vmtest-bpf-next-VM_Test-4 |
success
|
Logs for aarch64-gcc / build / build for aarch64 with gcc
|
bpf/vmtest-bpf-next-VM_Test-10 |
success
|
Logs for aarch64-gcc / veristat
|
bpf/vmtest-bpf-next-VM_Test-12 |
success
|
Logs for s390x-gcc / build-release
|
bpf/vmtest-bpf-next-VM_Test-9 |
success
|
Logs for aarch64-gcc / test (test_verifier, false, 360) / test_verifier on aarch64 with gcc
|
bpf/vmtest-bpf-next-VM_Test-16 |
success
|
Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
|
bpf/vmtest-bpf-next-VM_Test-11 |
success
|
Logs for s390x-gcc / build / build for s390x with gcc
|
bpf/vmtest-bpf-next-VM_Test-6 |
success
|
Logs for aarch64-gcc / test (test_maps, false, 360) / test_maps on aarch64 with gcc
|
bpf/vmtest-bpf-next-VM_Test-20 |
success
|
Logs for x86_64-gcc / build-release
|
bpf/vmtest-bpf-next-VM_Test-13 |
success
|
Logs for s390x-gcc / test (test_maps, false, 360) / test_maps on s390x with gcc
|
bpf/vmtest-bpf-next-VM_Test-17 |
success
|
Logs for s390x-gcc / veristat
|
bpf/vmtest-bpf-next-VM_Test-19 |
success
|
Logs for x86_64-gcc / build / build for x86_64 with gcc
|
bpf/vmtest-bpf-next-VM_Test-18 |
success
|
Logs for set-matrix
|
bpf/vmtest-bpf-next-VM_Test-28 |
fail
|
Logs for x86_64-llvm-17 / build / build for x86_64 with llvm-17
|
bpf/vmtest-bpf-next-VM_Test-29 |
fail
|
Logs for x86_64-llvm-17 / build-release / build for x86_64 with llvm-17-O2
|
bpf/vmtest-bpf-next-VM_Test-30 |
success
|
Logs for x86_64-llvm-17 / test
|
bpf/vmtest-bpf-next-VM_Test-32 |
fail
|
Logs for x86_64-llvm-18 / build / build for x86_64 with llvm-18
|
bpf/vmtest-bpf-next-VM_Test-31 |
success
|
Logs for x86_64-llvm-17 / veristat
|
bpf/vmtest-bpf-next-VM_Test-33 |
fail
|
Logs for x86_64-llvm-18 / build-release / build for x86_64 with llvm-18-O2
|
bpf/vmtest-bpf-next-VM_Test-34 |
success
|
Logs for x86_64-llvm-18 / test
|
bpf/vmtest-bpf-next-VM_Test-35 |
success
|
Logs for x86_64-llvm-18 / veristat
|
bpf/vmtest-bpf-next-VM_Test-26 |
success
|
Logs for x86_64-gcc / test (test_verifier, false, 360) / test_verifier on x86_64 with gcc
|
bpf/vmtest-bpf-next-VM_Test-21 |
success
|
Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
|
bpf/vmtest-bpf-next-VM_Test-27 |
success
|
Logs for x86_64-gcc / veristat / veristat on x86_64 with gcc
|
bpf/vmtest-bpf-next-VM_Test-8 |
fail
|
Logs for aarch64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on aarch64 with gcc
|
bpf/vmtest-bpf-next-VM_Test-7 |
fail
|
Logs for aarch64-gcc / test (test_progs, false, 360) / test_progs on aarch64 with gcc
|
bpf/vmtest-bpf-next-VM_Test-15 |
fail
|
Logs for s390x-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on s390x with gcc
|
bpf/vmtest-bpf-next-VM_Test-14 |
fail
|
Logs for s390x-gcc / test (test_progs, false, 360) / test_progs on s390x with gcc
|
bpf/vmtest-bpf-next-PR |
fail
|
PR summary
|
bpf/vmtest-bpf-next-VM_Test-24 |
success
|
Logs for x86_64-gcc / test (test_progs_no_alu32_parallel, true, 30) / test_progs_no_alu32_parallel on x86_64 with gcc
|
bpf/vmtest-bpf-next-VM_Test-23 |
fail
|
Logs for x86_64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with gcc
|
bpf/vmtest-bpf-next-VM_Test-22 |
fail
|
Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs on x86_64 with gcc
|
bpf/vmtest-bpf-next-VM_Test-25 |
success
|
Logs for x86_64-gcc / test (test_progs_parallel, true, 30) / test_progs_parallel on x86_64 with gcc
|
@@ -12,6 +12,8 @@
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/un.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include <linux/err.h>
#include <linux/in.h>
@@ -575,6 +577,248 @@ int set_hw_ring_size(char *ifname, struct ethtool_ringparam *ring_param)
return 0;
}
+struct tmonitor_ctx {
+ pid_t pid;
+ const char *netns;
+ char log_name[PATH_MAX];
+};
+
+/* Make sure that tcpdump has handled all previous packets.
+ *
+ * Send one or more UDP packets to the loopback interface. The packet
+ * contains a mark string. The mark is used to check if tcpdump has handled
+ * the packet. The function waits for tcpdump to print a message for the
+ * packet containing the mark (by checking the payload length and the
+ * destination). This is not a perfect solution, but it should be enough
+ * for testing purposes.
+ *
+ * log_name is the file name where tcpdump writes its output.
+ * mark is the string that is sent in the UDP packet.
+ * repeat specifies if the function should send multiple packets.
+ *
+ * Device "lo" should be up in the namespace for this to work. This
+ * function should be called in the same network namespace as a
+ * tmonitor_ctx created for in order to create a socket for sending mark
+ * packets.
+ */
+static int traffic_monitor_sync(const char *log_name, const char *mark,
+ bool repeat)
+{
+ const int max_loop = 1000; /* 10s */
+ char mark_pkt_pattern[64];
+ struct sockaddr_in addr;
+ int sock, log_fd, rd_pos = 0;
+ int pattern_size;
+ struct stat st;
+ char buf[4096];
+ int send_cnt = repeat ? max_loop : 1;
+ bool found;
+ int i, n;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ log_err("Failed to create socket");
+ return -1;
+ }
+
+ /* Check only the destination and the payload length */
+ pattern_size = snprintf(mark_pkt_pattern, sizeof(mark_pkt_pattern),
+ " > 127.0.0.241.4321: UDP, length %ld",
+ strlen(mark));
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = inet_addr("127.0.0.241");
+ addr.sin_port = htons(4321);
+
+ /* Wait for the log file to be created */
+ for (i = 0; i < max_loop; i++) {
+ log_fd = open(log_name, O_RDONLY);
+ if (log_fd >= 0) {
+ fstat(log_fd, &st);
+ rd_pos = st.st_size;
+ break;
+ }
+ usleep(10000);
+ }
+ /* Wait for the mark packet */
+ for (found = false; i < max_loop && !found; i++) {
+ if (send_cnt-- > 0) {
+ /* Send an UDP packet */
+ if (sendto(sock, mark, strlen(mark), 0,
+ (struct sockaddr *)&addr,
+ sizeof(addr)) != strlen(mark))
+ log_err("Failed to sendto");
+ }
+
+ usleep(10000);
+ fstat(log_fd, &st);
+ /* Check the content of the log file */
+ while (rd_pos + pattern_size <= st.st_size) {
+ lseek(log_fd, rd_pos, SEEK_SET);
+ n = read(log_fd, buf, sizeof(buf) - 1);
+ if (n < pattern_size)
+ break;
+ buf[n] = 0;
+ if (strstr(buf, mark_pkt_pattern)) {
+ found = true;
+ break;
+ }
+ rd_pos += n - pattern_size + 1;
+ }
+ }
+
+ close(log_fd);
+ close(sock);
+
+ if (!found) {
+ log_err("Waited too long for synchronizing traffic monitor");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Start a tcpdump process to monitor traffic.
+ *
+ * netns specifies what network namespace you want to monitor. It will
+ * monitor the current namespace if netns is NULL.
+ */
+struct tmonitor_ctx *traffic_monitor_start(const char *netns)
+{
+ struct tmonitor_ctx *ctx = NULL;
+ struct nstoken *nstoken = NULL;
+ char log_name[PATH_MAX];
+ int status, log_fd;
+ pid_t pid;
+
+ if (netns) {
+ nstoken = open_netns(netns);
+ if (!nstoken)
+ return NULL;
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ log_err("Failed to fork");
+ goto error;
+ }
+
+ if (pid == 0) {
+ /* Child */
+ pid = getpid();
+ snprintf(log_name, sizeof(log_name), "/tmp/tmon_tcpdump_%d.log", pid);
+ log_fd = open(log_name, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ dup2(log_fd, STDOUT_FILENO);
+ dup2(log_fd, STDERR_FILENO);
+ if (log_fd != STDOUT_FILENO && log_fd != STDERR_FILENO)
+ close(log_fd);
+
+ /* -n don't convert addresses to hostnames.
+ *
+ * --immediate-mode handle captured packets immediately.
+ *
+ * -l print messages with line buffer. With this option,
+ * the output will be written at the end of each line
+ * rather than when the output buffer is full. This is
+ * needed to sync with tcpdump efficiently.
+ */
+ execlp("tcpdump", "tcpdump", "-i", "any", "-n", "--immediate-mode", "-l", NULL);
+ log_err("Failed to exec tcpdump");
+ exit(1);
+ }
+
+ ctx = malloc(sizeof(*ctx));
+ if (!ctx) {
+ log_err("Failed to malloc ctx");
+ goto error;
+ }
+
+ ctx->pid = pid;
+ ctx->netns = netns;
+ snprintf(ctx->log_name, sizeof(ctx->log_name), "/tmp/tmon_tcpdump_%d.log", pid);
+
+ /* Wait for tcpdump to be ready */
+ if (traffic_monitor_sync(ctx->log_name, "hello", true)) {
+ status = 0;
+ if (waitpid(pid, &status, WNOHANG) >= 0 &&
+ !WIFEXITED(status) && !WIFSIGNALED(status))
+ log_err("Wait too long for tcpdump");
+ else
+ log_err("Fail to start tcpdump");
+ goto error;
+ }
+
+ close_netns(nstoken);
+
+ return ctx;
+
+error:
+ close_netns(nstoken);
+ if (pid > 0) {
+ kill(pid, SIGTERM);
+ waitpid(pid, NULL, 0);
+ snprintf(log_name, sizeof(log_name), "/tmp/tmon_tcpdump_%d.log", pid);
+ unlink(log_name);
+ }
+ free(ctx);
+
+ return NULL;
+}
+
+void traffic_monitor_stop(struct tmonitor_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ kill(ctx->pid, SIGTERM);
+ /* Wait the tcpdump process in case that the log file is created
+ * after this line.
+ */
+ waitpid(ctx->pid, NULL, 0);
+ unlink(ctx->log_name);
+ free(ctx);
+}
+
+/* Report the traffic monitored by tcpdump.
+ *
+ * The function reads the log file created by tcpdump and writes the
+ * content to stderr.
+ */
+void traffic_monitor_report(struct tmonitor_ctx *ctx)
+{
+ struct nstoken *nstoken = NULL;
+ char buf[4096];
+ int log_fd, n;
+
+ if (!ctx)
+ return;
+
+ /* Make sure all previous packets have been handled by
+ * tcpdump.
+ */
+ if (ctx->netns) {
+ nstoken = open_netns(ctx->netns);
+ if (!nstoken) {
+ log_err("Failed to open netns: %s", ctx->netns);
+ goto out;
+ }
+ }
+ traffic_monitor_sync(ctx->log_name, "sync for report", false);
+ close_netns(nstoken);
+
+ /* Read the log file and write to stderr */
+ log_fd = open(ctx->log_name, O_RDONLY);
+ if (log_fd < 0) {
+ log_err("Failed to open log file");
+ return;
+ }
+
+ while ((n = read(log_fd, buf, sizeof(buf))) > 0)
+ fwrite(buf, n, 1, stderr);
+
+out:
+ close(log_fd);
+}
+
struct send_recv_arg {
int fd;
uint32_t bytes;
@@ -72,6 +72,11 @@ int get_socket_local_port(int sock_fd);
int get_hw_ring_size(char *ifname, struct ethtool_ringparam *ring_param);
int set_hw_ring_size(char *ifname, struct ethtool_ringparam *ring_param);
+struct tmonitor_ctx;
+struct tmonitor_ctx *traffic_monitor_start(const char *netns);
+void traffic_monitor_stop(struct tmonitor_ctx *ctx);
+void traffic_monitor_report(struct tmonitor_ctx *ctx);
+
struct nstoken;
/**
* open_netns() - Switch to specified network namespace by name.
Add functions that run tcpdump in the background, report the traffic log captured by tcpdump, and stop tcpdump. They are supposed to be used for debugging flaky network test cases. A monitored test case should call traffic_monitor_start() to start a tcpdump process in the background for a given namespace, call traffic_monitor_report() to print the log from tcpdump, and call traffic_monitor_stop() to shutdown the tcpdump process. Signed-off-by: Kui-Feng Lee <thinker.li@gmail.com> --- tools/testing/selftests/bpf/network_helpers.c | 244 ++++++++++++++++++ tools/testing/selftests/bpf/network_helpers.h | 5 + 2 files changed, 249 insertions(+)