Commit 13f7432b authored by Daniel Borkmann's avatar Daniel Borkmann

Merge branch 'bpf-tcp-listen-cb'

Andrey Ignatov says:

====================
This patchset adds TCP-BPF callback for listening sockets.

Patch 0001 provides more details and is the main patch in the set.

Patch 0006 adds selftest for the new callback.

Other patches are bug fixes and improvements in TCP-BPF selftest
to make it easier to extend in 0006.
====================
Acked-by: default avatarLawrence Brakmo <brakmo@fb.com>
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
parents 2d3feca8 78d8e26d
...@@ -2555,6 +2555,9 @@ enum { ...@@ -2555,6 +2555,9 @@ enum {
* Arg1: old_state * Arg1: old_state
* Arg2: new_state * Arg2: new_state
*/ */
BPF_SOCK_OPS_TCP_LISTEN_CB, /* Called on listen(2), right after
* socket transition to LISTEN state.
*/
}; };
/* List of TCP states. There is a build check in net/ipv4/tcp.c to detect /* List of TCP states. There is a build check in net/ipv4/tcp.c to detect
......
...@@ -229,6 +229,7 @@ int inet_listen(struct socket *sock, int backlog) ...@@ -229,6 +229,7 @@ int inet_listen(struct socket *sock, int backlog)
err = inet_csk_listen_start(sk, backlog); err = inet_csk_listen_start(sk, backlog);
if (err) if (err)
goto out; goto out;
tcp_call_bpf(sk, BPF_SOCK_OPS_TCP_LISTEN_CB, 0, NULL);
} }
sk->sk_max_ack_backlog = backlog; sk->sk_max_ack_backlog = backlog;
err = 0; err = 0;
......
...@@ -2555,6 +2555,9 @@ enum { ...@@ -2555,6 +2555,9 @@ enum {
* Arg1: old_state * Arg1: old_state
* Arg2: new_state * Arg2: new_state
*/ */
BPF_SOCK_OPS_TCP_LISTEN_CB, /* Called on listen(2), right after
* socket transition to LISTEN state.
*/
}; };
/* List of TCP states. There is a build check in net/ipv4/tcp.c to detect /* List of TCP states. There is a build check in net/ipv4/tcp.c to detect
......
...@@ -61,6 +61,7 @@ $(OUTPUT)/test_dev_cgroup: cgroup_helpers.c ...@@ -61,6 +61,7 @@ $(OUTPUT)/test_dev_cgroup: cgroup_helpers.c
$(OUTPUT)/test_sock: cgroup_helpers.c $(OUTPUT)/test_sock: cgroup_helpers.c
$(OUTPUT)/test_sock_addr: cgroup_helpers.c $(OUTPUT)/test_sock_addr: cgroup_helpers.c
$(OUTPUT)/test_sockmap: cgroup_helpers.c $(OUTPUT)/test_sockmap: cgroup_helpers.c
$(OUTPUT)/test_tcpbpf_user: cgroup_helpers.c
$(OUTPUT)/test_progs: trace_helpers.c $(OUTPUT)/test_progs: trace_helpers.c
$(OUTPUT)/get_cgroup_id_user: cgroup_helpers.c $(OUTPUT)/get_cgroup_id_user: cgroup_helpers.c
......
...@@ -118,7 +118,7 @@ static int join_cgroup_from_top(char *cgroup_path) ...@@ -118,7 +118,7 @@ static int join_cgroup_from_top(char *cgroup_path)
* *
* On success, it returns 0, otherwise on failure it returns 1. * On success, it returns 0, otherwise on failure it returns 1.
*/ */
int join_cgroup(char *path) int join_cgroup(const char *path)
{ {
char cgroup_path[PATH_MAX + 1]; char cgroup_path[PATH_MAX + 1];
...@@ -158,7 +158,7 @@ void cleanup_cgroup_environment(void) ...@@ -158,7 +158,7 @@ void cleanup_cgroup_environment(void)
* On success, it returns the file descriptor. On failure it returns 0. * On success, it returns the file descriptor. On failure it returns 0.
* If there is a failure, it prints the error to stderr. * If there is a failure, it prints the error to stderr.
*/ */
int create_and_get_cgroup(char *path) int create_and_get_cgroup(const char *path)
{ {
char cgroup_path[PATH_MAX + 1]; char cgroup_path[PATH_MAX + 1];
int fd; int fd;
...@@ -186,7 +186,7 @@ int create_and_get_cgroup(char *path) ...@@ -186,7 +186,7 @@ int create_and_get_cgroup(char *path)
* which is an invalid cgroup id. * which is an invalid cgroup id.
* If there is a failure, it prints the error to stderr. * If there is a failure, it prints the error to stderr.
*/ */
unsigned long long get_cgroup_id(char *path) unsigned long long get_cgroup_id(const char *path)
{ {
int dirfd, err, flags, mount_id, fhsize; int dirfd, err, flags, mount_id, fhsize;
union { union {
......
...@@ -9,10 +9,10 @@ ...@@ -9,10 +9,10 @@
__FILE__, __LINE__, clean_errno(), ##__VA_ARGS__) __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
int create_and_get_cgroup(char *path); int create_and_get_cgroup(const char *path);
int join_cgroup(char *path); int join_cgroup(const char *path);
int setup_cgroup_environment(void); int setup_cgroup_environment(void);
void cleanup_cgroup_environment(void); void cleanup_cgroup_environment(void);
unsigned long long get_cgroup_id(char *path); unsigned long long get_cgroup_id(const char *path);
#endif #endif
...@@ -12,5 +12,6 @@ struct tcpbpf_globals { ...@@ -12,5 +12,6 @@ struct tcpbpf_globals {
__u32 good_cb_test_rv; __u32 good_cb_test_rv;
__u64 bytes_received; __u64 bytes_received;
__u64 bytes_acked; __u64 bytes_acked;
__u32 num_listen;
}; };
#endif #endif
...@@ -96,15 +96,22 @@ int bpf_testcb(struct bpf_sock_ops *skops) ...@@ -96,15 +96,22 @@ int bpf_testcb(struct bpf_sock_ops *skops)
if (!gp) if (!gp)
break; break;
g = *gp; g = *gp;
if (skops->args[0] == BPF_TCP_LISTEN) {
g.num_listen++;
} else {
g.total_retrans = skops->total_retrans; g.total_retrans = skops->total_retrans;
g.data_segs_in = skops->data_segs_in; g.data_segs_in = skops->data_segs_in;
g.data_segs_out = skops->data_segs_out; g.data_segs_out = skops->data_segs_out;
g.bytes_received = skops->bytes_received; g.bytes_received = skops->bytes_received;
g.bytes_acked = skops->bytes_acked; g.bytes_acked = skops->bytes_acked;
}
bpf_map_update_elem(&global_map, &key, &g, bpf_map_update_elem(&global_map, &key, &g,
BPF_ANY); BPF_ANY);
} }
break; break;
case BPF_SOCK_OPS_TCP_LISTEN_CB:
bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_STATE_CB_FLAG);
break;
default: default:
rv = -1; rv = -1;
} }
......
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
#include <inttypes.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <signal.h>
#include <string.h> #include <string.h>
#include <assert.h>
#include <linux/perf_event.h>
#include <linux/ptrace.h>
#include <linux/bpf.h> #include <linux/bpf.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <bpf/bpf.h> #include <bpf/bpf.h>
#include <bpf/libbpf.h> #include <bpf/libbpf.h>
#include "bpf_util.h"
#include "bpf_rlimit.h" #include "bpf_rlimit.h"
#include <linux/perf_event.h> #include "bpf_util.h"
#include "cgroup_helpers.h"
#include "test_tcpbpf.h" #include "test_tcpbpf.h"
#define EXPECT_EQ(expected, actual, fmt) \
do { \
if ((expected) != (actual)) { \
printf(" Value of: " #actual "\n" \
" Actual: %" fmt "\n" \
" Expected: %" fmt "\n", \
(actual), (expected)); \
goto err; \
} \
} while (0)
int verify_result(const struct tcpbpf_globals *result)
{
__u32 expected_events;
expected_events = ((1 << BPF_SOCK_OPS_TIMEOUT_INIT) |
(1 << BPF_SOCK_OPS_RWND_INIT) |
(1 << BPF_SOCK_OPS_TCP_CONNECT_CB) |
(1 << BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB) |
(1 << BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB) |
(1 << BPF_SOCK_OPS_NEEDS_ECN) |
(1 << BPF_SOCK_OPS_STATE_CB) |
(1 << BPF_SOCK_OPS_TCP_LISTEN_CB));
EXPECT_EQ(expected_events, result->event_map, "#" PRIx32);
EXPECT_EQ(501ULL, result->bytes_received, "llu");
EXPECT_EQ(1002ULL, result->bytes_acked, "llu");
EXPECT_EQ(1, result->data_segs_in, PRIu32);
EXPECT_EQ(1, result->data_segs_out, PRIu32);
EXPECT_EQ(0x80, result->bad_cb_test_rv, PRIu32);
EXPECT_EQ(0, result->good_cb_test_rv, PRIu32);
EXPECT_EQ(1, result->num_listen, PRIu32);
return 0;
err:
return -1;
}
static int bpf_find_map(const char *test, struct bpf_object *obj, static int bpf_find_map(const char *test, struct bpf_object *obj,
const char *name) const char *name)
{ {
...@@ -35,42 +67,28 @@ static int bpf_find_map(const char *test, struct bpf_object *obj, ...@@ -35,42 +67,28 @@ static int bpf_find_map(const char *test, struct bpf_object *obj,
return bpf_map__fd(map); return bpf_map__fd(map);
} }
#define SYSTEM(CMD) \
do { \
if (system(CMD)) { \
printf("system(%s) FAILS!\n", CMD); \
} \
} while (0)
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
const char *file = "test_tcpbpf_kern.o"; const char *file = "test_tcpbpf_kern.o";
struct tcpbpf_globals g = {0}; struct tcpbpf_globals g = {0};
int cg_fd, prog_fd, map_fd; const char *cg_path = "/foo";
bool debug_flag = false;
int error = EXIT_FAILURE; int error = EXIT_FAILURE;
struct bpf_object *obj; struct bpf_object *obj;
char cmd[100], *dir; int prog_fd, map_fd;
struct stat buffer; int cg_fd = -1;
__u32 key = 0; __u32 key = 0;
int pid;
int rv; int rv;
if (argc > 1 && strcmp(argv[1], "-d") == 0) if (setup_cgroup_environment())
debug_flag = true; goto err;
dir = "/tmp/cgroupv2/foo"; cg_fd = create_and_get_cgroup(cg_path);
if (!cg_fd)
goto err;
if (stat(dir, &buffer) != 0) { if (join_cgroup(cg_path))
SYSTEM("mkdir -p /tmp/cgroupv2"); goto err;
SYSTEM("mount -t cgroup2 none /tmp/cgroupv2");
SYSTEM("mkdir -p /tmp/cgroupv2/foo");
}
pid = (int) getpid();
sprintf(cmd, "echo %d >> /tmp/cgroupv2/foo/cgroup.procs", pid);
SYSTEM(cmd);
cg_fd = open(dir, O_DIRECTORY, O_RDONLY);
if (bpf_prog_load(file, BPF_PROG_TYPE_SOCK_OPS, &obj, &prog_fd)) { if (bpf_prog_load(file, BPF_PROG_TYPE_SOCK_OPS, &obj, &prog_fd)) {
printf("FAILED: load_bpf_file failed for: %s\n", file); printf("FAILED: load_bpf_file failed for: %s\n", file);
goto err; goto err;
...@@ -83,7 +101,10 @@ int main(int argc, char **argv) ...@@ -83,7 +101,10 @@ int main(int argc, char **argv)
goto err; goto err;
} }
SYSTEM("./tcp_server.py"); if (system("./tcp_server.py")) {
printf("FAILED: TCP server\n");
goto err;
}
map_fd = bpf_find_map(__func__, obj, "global_map"); map_fd = bpf_find_map(__func__, obj, "global_map");
if (map_fd < 0) if (map_fd < 0)
...@@ -95,34 +116,16 @@ int main(int argc, char **argv) ...@@ -95,34 +116,16 @@ int main(int argc, char **argv)
goto err; goto err;
} }
if (g.bytes_received != 501 || g.bytes_acked != 1002 || if (verify_result(&g)) {
g.data_segs_in != 1 || g.data_segs_out != 1 ||
(g.event_map ^ 0x47e) != 0 || g.bad_cb_test_rv != 0x80 ||
g.good_cb_test_rv != 0) {
printf("FAILED: Wrong stats\n"); printf("FAILED: Wrong stats\n");
if (debug_flag) {
printf("\n");
printf("bytes_received: %d (expecting 501)\n",
(int)g.bytes_received);
printf("bytes_acked: %d (expecting 1002)\n",
(int)g.bytes_acked);
printf("data_segs_in: %d (expecting 1)\n",
g.data_segs_in);
printf("data_segs_out: %d (expecting 1)\n",
g.data_segs_out);
printf("event_map: 0x%x (at least 0x47e)\n",
g.event_map);
printf("bad_cb_test_rv: 0x%x (expecting 0x80)\n",
g.bad_cb_test_rv);
printf("good_cb_test_rv:0x%x (expecting 0)\n",
g.good_cb_test_rv);
}
goto err; goto err;
} }
printf("PASSED!\n"); printf("PASSED!\n");
error = 0; error = 0;
err: err:
bpf_prog_detach(cg_fd, BPF_CGROUP_SOCK_OPS); bpf_prog_detach(cg_fd, BPF_CGROUP_SOCK_OPS);
close(cg_fd);
cleanup_cgroup_environment();
return error; return error;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment