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: Lawrence Brakmo <brakmo@fb.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
This commit is contained in:
Daniel Borkmann 2018-07-15 00:08:41 +02:00
commit 13f7432bdd
9 changed files with 88 additions and 69 deletions

View file

@ -2555,6 +2555,9 @@ enum {
* Arg1: old_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

View file

@ -229,6 +229,7 @@ int inet_listen(struct socket *sock, int backlog)
err = inet_csk_listen_start(sk, backlog);
if (err)
goto out;
tcp_call_bpf(sk, BPF_SOCK_OPS_TCP_LISTEN_CB, 0, NULL);
}
sk->sk_max_ack_backlog = backlog;
err = 0;

View file

@ -2555,6 +2555,9 @@ enum {
* Arg1: old_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

View file

@ -61,6 +61,7 @@ $(OUTPUT)/test_dev_cgroup: cgroup_helpers.c
$(OUTPUT)/test_sock: cgroup_helpers.c
$(OUTPUT)/test_sock_addr: cgroup_helpers.c
$(OUTPUT)/test_sockmap: cgroup_helpers.c
$(OUTPUT)/test_tcpbpf_user: cgroup_helpers.c
$(OUTPUT)/test_progs: trace_helpers.c
$(OUTPUT)/get_cgroup_id_user: cgroup_helpers.c

View file

@ -118,7 +118,7 @@ static int join_cgroup_from_top(char *cgroup_path)
*
* 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];
@ -158,7 +158,7 @@ void cleanup_cgroup_environment(void)
* On success, it returns the file descriptor. On failure it returns 0.
* 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];
int fd;
@ -186,7 +186,7 @@ int create_and_get_cgroup(char *path)
* which is an invalid cgroup id.
* 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;
union {

View file

@ -9,10 +9,10 @@
__FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
int create_and_get_cgroup(char *path);
int join_cgroup(char *path);
int create_and_get_cgroup(const char *path);
int join_cgroup(const char *path);
int setup_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

View file

@ -12,5 +12,6 @@ struct tcpbpf_globals {
__u32 good_cb_test_rv;
__u64 bytes_received;
__u64 bytes_acked;
__u32 num_listen;
};
#endif

View file

@ -96,15 +96,22 @@ int bpf_testcb(struct bpf_sock_ops *skops)
if (!gp)
break;
g = *gp;
g.total_retrans = skops->total_retrans;
g.data_segs_in = skops->data_segs_in;
g.data_segs_out = skops->data_segs_out;
g.bytes_received = skops->bytes_received;
g.bytes_acked = skops->bytes_acked;
if (skops->args[0] == BPF_TCP_LISTEN) {
g.num_listen++;
} else {
g.total_retrans = skops->total_retrans;
g.data_segs_in = skops->data_segs_in;
g.data_segs_out = skops->data_segs_out;
g.bytes_received = skops->bytes_received;
g.bytes_acked = skops->bytes_acked;
}
bpf_map_update_elem(&global_map, &key, &g,
BPF_ANY);
}
break;
case BPF_SOCK_OPS_TCP_LISTEN_CB:
bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_STATE_CB_FLAG);
break;
default:
rv = -1;
}

View file

@ -1,27 +1,59 @@
// SPDX-License-Identifier: GPL-2.0
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <assert.h>
#include <linux/perf_event.h>
#include <linux/ptrace.h>
#include <linux/bpf.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include "bpf_util.h"
#include "bpf_rlimit.h"
#include <linux/perf_event.h>
#include "bpf_util.h"
#include "cgroup_helpers.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,
const char *name)
{
@ -35,42 +67,28 @@ static int bpf_find_map(const char *test, struct bpf_object *obj,
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)
{
const char *file = "test_tcpbpf_kern.o";
struct tcpbpf_globals g = {0};
int cg_fd, prog_fd, map_fd;
bool debug_flag = false;
const char *cg_path = "/foo";
int error = EXIT_FAILURE;
struct bpf_object *obj;
char cmd[100], *dir;
struct stat buffer;
int prog_fd, map_fd;
int cg_fd = -1;
__u32 key = 0;
int pid;
int rv;
if (argc > 1 && strcmp(argv[1], "-d") == 0)
debug_flag = true;
if (setup_cgroup_environment())
goto err;
dir = "/tmp/cgroupv2/foo";
cg_fd = create_and_get_cgroup(cg_path);
if (!cg_fd)
goto err;
if (stat(dir, &buffer) != 0) {
SYSTEM("mkdir -p /tmp/cgroupv2");
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);
if (join_cgroup(cg_path))
goto err;
cg_fd = open(dir, O_DIRECTORY, O_RDONLY);
if (bpf_prog_load(file, BPF_PROG_TYPE_SOCK_OPS, &obj, &prog_fd)) {
printf("FAILED: load_bpf_file failed for: %s\n", file);
goto err;
@ -83,7 +101,10 @@ int main(int argc, char **argv)
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");
if (map_fd < 0)
@ -95,34 +116,16 @@ int main(int argc, char **argv)
goto err;
}
if (g.bytes_received != 501 || g.bytes_acked != 1002 ||
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) {
if (verify_result(&g)) {
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;
}
printf("PASSED!\n");
error = 0;
err:
bpf_prog_detach(cg_fd, BPF_CGROUP_SOCK_OPS);
close(cg_fd);
cleanup_cgroup_environment();
return error;
}