diff options
Diffstat (limited to 'samples')
51 files changed, 5042 insertions, 3250 deletions
diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index b1e322860a7f..13315ff1193c 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -2,7 +2,7 @@ obj- := dummy.o # List of programs to build -hostprogs-y := test_verifier test_maps +hostprogs-y := test_lru_dist hostprogs-y += sock_example hostprogs-y += fds_example hostprogs-y += sockex1 @@ -22,14 +22,20 @@ hostprogs-y += spintest hostprogs-y += map_perf_test hostprogs-y += test_overhead hostprogs-y += test_cgrp2_array_pin +hostprogs-y += test_cgrp2_attach +hostprogs-y += test_cgrp2_attach2 +hostprogs-y += test_cgrp2_sock +hostprogs-y += test_cgrp2_sock2 hostprogs-y += xdp1 hostprogs-y += xdp2 hostprogs-y += test_current_task_under_cgroup hostprogs-y += trace_event hostprogs-y += sampleip +hostprogs-y += tc_l2_redirect +hostprogs-y += lwt_len_hist +hostprogs-y += xdp_tx_iptunnel -test_verifier-objs := test_verifier.o libbpf.o -test_maps-objs := test_maps.o libbpf.o +test_lru_dist-objs := test_lru_dist.o libbpf.o sock_example-objs := sock_example.o libbpf.o fds_example-objs := bpf_load.o libbpf.o fds_example.o sockex1-objs := bpf_load.o libbpf.o sockex1_user.o @@ -49,13 +55,20 @@ spintest-objs := bpf_load.o libbpf.o spintest_user.o map_perf_test-objs := bpf_load.o libbpf.o map_perf_test_user.o test_overhead-objs := bpf_load.o libbpf.o test_overhead_user.o test_cgrp2_array_pin-objs := libbpf.o test_cgrp2_array_pin.o +test_cgrp2_attach-objs := libbpf.o test_cgrp2_attach.o +test_cgrp2_attach2-objs := libbpf.o test_cgrp2_attach2.o cgroup_helpers.o +test_cgrp2_sock-objs := libbpf.o test_cgrp2_sock.o +test_cgrp2_sock2-objs := bpf_load.o libbpf.o test_cgrp2_sock2.o xdp1-objs := bpf_load.o libbpf.o xdp1_user.o # reuse xdp1 source intentionally xdp2-objs := bpf_load.o libbpf.o xdp1_user.o -test_current_task_under_cgroup-objs := bpf_load.o libbpf.o \ +test_current_task_under_cgroup-objs := bpf_load.o libbpf.o cgroup_helpers.o \ test_current_task_under_cgroup_user.o trace_event-objs := bpf_load.o libbpf.o trace_event_user.o sampleip-objs := bpf_load.o libbpf.o sampleip_user.o +tc_l2_redirect-objs := bpf_load.o libbpf.o tc_l2_redirect_user.o +lwt_len_hist-objs := bpf_load.o libbpf.o lwt_len_hist_user.o +xdp_tx_iptunnel-objs := bpf_load.o libbpf.o xdp_tx_iptunnel_user.o # Tell kbuild to always build the programs always := $(hostprogs-y) @@ -68,10 +81,12 @@ always += tracex3_kern.o always += tracex4_kern.o always += tracex5_kern.o always += tracex6_kern.o +always += sock_flags_kern.o always += test_probe_write_user_kern.o always += trace_output_kern.o always += tcbpf1_kern.o always += tcbpf2_kern.o +always += tc_l2_redirect_kern.o always += lathist_kern.o always += offwaketime_kern.o always += spintest_kern.o @@ -85,8 +100,11 @@ always += xdp2_kern.o always += test_current_task_under_cgroup_kern.o always += trace_event_kern.o always += sampleip_kern.o +always += lwt_len_hist_kern.o +always += xdp_tx_iptunnel_kern.o HOSTCFLAGS += -I$(objtree)/usr/include +HOSTCFLAGS += -I$(srctree)/tools/testing/selftests/bpf/ HOSTCFLAGS_bpf_load.o += -I$(objtree)/usr/include -Wno-unused-variable HOSTLOADLIBES_fds_example += -lelf @@ -99,6 +117,7 @@ HOSTLOADLIBES_tracex3 += -lelf HOSTLOADLIBES_tracex4 += -lelf -lrt HOSTLOADLIBES_tracex5 += -lelf HOSTLOADLIBES_tracex6 += -lelf +HOSTLOADLIBES_test_cgrp2_sock2 += -lelf HOSTLOADLIBES_test_probe_write_user += -lelf HOSTLOADLIBES_trace_output += -lelf -lrt HOSTLOADLIBES_lathist += -lelf @@ -111,6 +130,9 @@ HOSTLOADLIBES_xdp2 += -lelf HOSTLOADLIBES_test_current_task_under_cgroup += -lelf HOSTLOADLIBES_trace_event += -lelf HOSTLOADLIBES_sampleip += -lelf +HOSTLOADLIBES_tc_l2_redirect += -l elf +HOSTLOADLIBES_lwt_len_hist += -l elf +HOSTLOADLIBES_xdp_tx_iptunnel += -lelf # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline: # make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang @@ -152,4 +174,6 @@ $(obj)/%.o: $(src)/%.c $(CLANG) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) $(EXTRA_CFLAGS) \ -D__KERNEL__ -D__ASM_SYSREG_H -Wno-unused-value -Wno-pointer-sign \ -Wno-compare-distinct-pointer-types \ + -Wno-gnu-variable-sized-type-not-at-end \ + -Wno-address-of-packed-member -Wno-tautological-compare \ -O2 -emit-llvm -c $< -o -| $(LLC) -march=bpf -filetype=obj -o $@ diff --git a/samples/bpf/bpf_helpers.h b/samples/bpf/bpf_helpers.h index 90f44bd2045e..faaffe2e139a 100644 --- a/samples/bpf/bpf_helpers.h +++ b/samples/bpf/bpf_helpers.h @@ -57,6 +57,8 @@ static int (*bpf_skb_set_tunnel_opt)(void *ctx, void *md, int size) = (void *) BPF_FUNC_skb_set_tunnel_opt; static unsigned long long (*bpf_get_prandom_u32)(void) = (void *) BPF_FUNC_get_prandom_u32; +static int (*bpf_xdp_adjust_head)(void *ctx, int offset) = + (void *) BPF_FUNC_xdp_adjust_head; /* llvm builtin functions that eBPF C program may use to * emit BPF_LD_ABS and BPF_LD_IND instructions @@ -80,6 +82,8 @@ struct bpf_map_def { unsigned int map_flags; }; +static int (*bpf_skb_load_bytes)(void *ctx, int off, void *to, int len) = + (void *) BPF_FUNC_skb_load_bytes; static int (*bpf_skb_store_bytes)(void *ctx, int off, void *from, int len, int flags) = (void *) BPF_FUNC_skb_store_bytes; static int (*bpf_l3_csum_replace)(void *ctx, int off, int from, int to, int flags) = @@ -88,6 +92,8 @@ static int (*bpf_l4_csum_replace)(void *ctx, int off, int from, int to, int flag (void *) BPF_FUNC_l4_csum_replace; static int (*bpf_skb_under_cgroup)(void *ctx, void *map, int index) = (void *) BPF_FUNC_skb_under_cgroup; +static int (*bpf_skb_change_head)(void *, int len, int flags) = + (void *) BPF_FUNC_skb_change_head; #if defined(__x86_64__) @@ -113,7 +119,7 @@ static int (*bpf_skb_under_cgroup)(void *ctx, void *map, int index) = #define PT_REGS_FP(x) ((x)->gprs[11]) /* Works only with CONFIG_FRAME_POINTER */ #define PT_REGS_RC(x) ((x)->gprs[2]) #define PT_REGS_SP(x) ((x)->gprs[15]) -#define PT_REGS_IP(x) ((x)->ip) +#define PT_REGS_IP(x) ((x)->psw.addr) #elif defined(__aarch64__) diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c index 97913e109b14..e30b6de94f2e 100644 --- a/samples/bpf/bpf_load.c +++ b/samples/bpf/bpf_load.c @@ -12,6 +12,10 @@ #include <linux/bpf.h> #include <linux/filter.h> #include <linux/perf_event.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#include <sys/types.h> +#include <sys/socket.h> #include <sys/syscall.h> #include <sys/ioctl.h> #include <sys/mman.h> @@ -52,6 +56,8 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size) bool is_tracepoint = strncmp(event, "tracepoint/", 11) == 0; bool is_xdp = strncmp(event, "xdp", 3) == 0; bool is_perf_event = strncmp(event, "perf_event", 10) == 0; + bool is_cgroup_skb = strncmp(event, "cgroup/skb", 10) == 0; + bool is_cgroup_sk = strncmp(event, "cgroup/sock", 11) == 0; enum bpf_prog_type prog_type; char buf[256]; int fd, efd, err, id; @@ -72,6 +78,10 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size) prog_type = BPF_PROG_TYPE_XDP; } else if (is_perf_event) { prog_type = BPF_PROG_TYPE_PERF_EVENT; + } else if (is_cgroup_skb) { + prog_type = BPF_PROG_TYPE_CGROUP_SKB; + } else if (is_cgroup_sk) { + prog_type = BPF_PROG_TYPE_CGROUP_SOCK; } else { printf("Unknown event '%s'\n", event); return -1; @@ -85,7 +95,7 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size) prog_fd[prog_cnt++] = fd; - if (is_xdp || is_perf_event) + if (is_xdp || is_perf_event || is_cgroup_skb || is_cgroup_sk) return 0; if (is_socket) { @@ -317,6 +327,10 @@ int load_bpf_file(char *path) &shdr_prog, &data_prog)) continue; + if (shdr_prog.sh_type != SHT_PROGBITS || + !(shdr_prog.sh_flags & SHF_EXECINSTR)) + continue; + insns = (struct bpf_insn *) data_prog->d_buf; processed_sec[shdr.sh_info] = true; @@ -330,7 +344,8 @@ int load_bpf_file(char *path) memcmp(shname_prog, "tracepoint/", 11) == 0 || memcmp(shname_prog, "xdp", 3) == 0 || memcmp(shname_prog, "perf_event", 10) == 0 || - memcmp(shname_prog, "socket", 6) == 0) + memcmp(shname_prog, "socket", 6) == 0 || + memcmp(shname_prog, "cgroup/", 7) == 0) load_and_attach(shname_prog, insns, data_prog->d_size); } } @@ -349,7 +364,8 @@ int load_bpf_file(char *path) memcmp(shname, "tracepoint/", 11) == 0 || memcmp(shname, "xdp", 3) == 0 || memcmp(shname, "perf_event", 10) == 0 || - memcmp(shname, "socket", 6) == 0) + memcmp(shname, "socket", 6) == 0 || + memcmp(shname, "cgroup/", 7) == 0) load_and_attach(shname, data->d_buf, data->d_size); } @@ -438,3 +454,93 @@ struct ksym *ksym_search(long key) /* out of range. return _stext */ return &syms[0]; } + +int set_link_xdp_fd(int ifindex, int fd) +{ + struct sockaddr_nl sa; + int sock, seq = 0, len, ret = -1; + char buf[4096]; + struct nlattr *nla, *nla_xdp; + struct { + struct nlmsghdr nh; + struct ifinfomsg ifinfo; + char attrbuf[64]; + } req; + struct nlmsghdr *nh; + struct nlmsgerr *err; + + memset(&sa, 0, sizeof(sa)); + sa.nl_family = AF_NETLINK; + + sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock < 0) { + printf("open netlink socket: %s\n", strerror(errno)); + return -1; + } + + if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { + printf("bind to netlink: %s\n", strerror(errno)); + goto cleanup; + } + + memset(&req, 0, sizeof(req)); + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + req.nh.nlmsg_type = RTM_SETLINK; + req.nh.nlmsg_pid = 0; + req.nh.nlmsg_seq = ++seq; + req.ifinfo.ifi_family = AF_UNSPEC; + req.ifinfo.ifi_index = ifindex; + nla = (struct nlattr *)(((char *)&req) + + NLMSG_ALIGN(req.nh.nlmsg_len)); + nla->nla_type = NLA_F_NESTED | 43/*IFLA_XDP*/; + + nla_xdp = (struct nlattr *)((char *)nla + NLA_HDRLEN); + nla_xdp->nla_type = 1/*IFLA_XDP_FD*/; + nla_xdp->nla_len = NLA_HDRLEN + sizeof(int); + memcpy((char *)nla_xdp + NLA_HDRLEN, &fd, sizeof(fd)); + nla->nla_len = NLA_HDRLEN + nla_xdp->nla_len; + + req.nh.nlmsg_len += NLA_ALIGN(nla->nla_len); + + if (send(sock, &req, req.nh.nlmsg_len, 0) < 0) { + printf("send to netlink: %s\n", strerror(errno)); + goto cleanup; + } + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + printf("recv from netlink: %s\n", strerror(errno)); + goto cleanup; + } + + for (nh = (struct nlmsghdr *)buf; NLMSG_OK(nh, len); + nh = NLMSG_NEXT(nh, len)) { + if (nh->nlmsg_pid != getpid()) { + printf("Wrong pid %d, expected %d\n", + nh->nlmsg_pid, getpid()); + goto cleanup; + } + if (nh->nlmsg_seq != seq) { + printf("Wrong seq %d, expected %d\n", + nh->nlmsg_seq, seq); + goto cleanup; + } + switch (nh->nlmsg_type) { + case NLMSG_ERROR: + err = (struct nlmsgerr *)NLMSG_DATA(nh); + if (!err->error) + continue; + printf("nlmsg error %s\n", strerror(-err->error)); + goto cleanup; + case NLMSG_DONE: + break; + } + } + + ret = 0; + +cleanup: + close(sock); + return ret; +} diff --git a/samples/bpf/bpf_load.h b/samples/bpf/bpf_load.h index dfa57fe65c8e..fb46a421ab41 100644 --- a/samples/bpf/bpf_load.h +++ b/samples/bpf/bpf_load.h @@ -7,6 +7,7 @@ extern int map_fd[MAX_MAPS]; extern int prog_fd[MAX_PROGS]; extern int event_fd[MAX_PROGS]; +extern int prog_cnt; /* parses elf file compiled by llvm .c->.o * . parses 'maps' section and creates maps via BPF syscall @@ -30,4 +31,5 @@ struct ksym { int load_kallsyms(void); struct ksym *ksym_search(long key); +int set_link_xdp_fd(int ifindex, int fd); #endif diff --git a/samples/bpf/cgroup_helpers.c b/samples/bpf/cgroup_helpers.c new file mode 100644 index 000000000000..9d1be9426401 --- /dev/null +++ b/samples/bpf/cgroup_helpers.c @@ -0,0 +1,177 @@ +#define _GNU_SOURCE +#include <sched.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <linux/limits.h> +#include <stdio.h> +#include <linux/sched.h> +#include <fcntl.h> +#include <unistd.h> +#include <ftw.h> + + +#include "cgroup_helpers.h" + +/* + * To avoid relying on the system setup, when setup_cgroup_env is called + * we create a new mount namespace, and cgroup namespace. The cgroup2 + * root is mounted at CGROUP_MOUNT_PATH + * + * Unfortunately, most people don't have cgroupv2 enabled at this point in time. + * It's easier to create our own mount namespace and manage it ourselves. + * + * We assume /mnt exists. + */ + +#define WALK_FD_LIMIT 16 +#define CGROUP_MOUNT_PATH "/mnt" +#define CGROUP_WORK_DIR "/cgroup-test-work-dir" +#define format_cgroup_path(buf, path) \ + snprintf(buf, sizeof(buf), "%s%s%s", CGROUP_MOUNT_PATH, \ + CGROUP_WORK_DIR, path) + +/** + * setup_cgroup_environment() - Setup the cgroup environment + * + * After calling this function, cleanup_cgroup_environment should be called + * once testing is complete. + * + * This function will print an error to stderr and return 1 if it is unable + * to setup the cgroup environment. If setup is successful, 0 is returned. + */ +int setup_cgroup_environment(void) +{ + char cgroup_workdir[PATH_MAX + 1]; + + format_cgroup_path(cgroup_workdir, ""); + + if (unshare(CLONE_NEWNS)) { + log_err("unshare"); + return 1; + } + + if (mount("none", "/", NULL, MS_REC | MS_PRIVATE, NULL)) { + log_err("mount fakeroot"); + return 1; + } + + if (mount("none", CGROUP_MOUNT_PATH, "cgroup2", 0, NULL)) { + log_err("mount cgroup2"); + return 1; + } + + /* Cleanup existing failed runs, now that the environment is setup */ + cleanup_cgroup_environment(); + + if (mkdir(cgroup_workdir, 0777) && errno != EEXIST) { + log_err("mkdir cgroup work dir"); + return 1; + } + + return 0; +} + +static int nftwfunc(const char *filename, const struct stat *statptr, + int fileflags, struct FTW *pfwt) +{ + if ((fileflags & FTW_D) && rmdir(filename)) + log_err("Removing cgroup: %s", filename); + return 0; +} + + +static int join_cgroup_from_top(char *cgroup_path) +{ + char cgroup_procs_path[PATH_MAX + 1]; + pid_t pid = getpid(); + int fd, rc = 0; + + snprintf(cgroup_procs_path, sizeof(cgroup_procs_path), + "%s/cgroup.procs", cgroup_path); + + fd = open(cgroup_procs_path, O_WRONLY); + if (fd < 0) { + log_err("Opening Cgroup Procs: %s", cgroup_procs_path); + return 1; + } + + if (dprintf(fd, "%d\n", pid) < 0) { + log_err("Joining Cgroup"); + rc = 1; + } + + close(fd); + return rc; +} + +/** + * join_cgroup() - Join a cgroup + * @path: The cgroup path, relative to the workdir, to join + * + * This function expects a cgroup to already be created, relative to the cgroup + * work dir, and it joins it. For example, passing "/my-cgroup" as the path + * would actually put the calling process into the cgroup + * "/cgroup-test-work-dir/my-cgroup" + * + * On success, it returns 0, otherwise on failure it returns 1. + */ +int join_cgroup(char *path) +{ + char cgroup_path[PATH_MAX + 1]; + + format_cgroup_path(cgroup_path, path); + return join_cgroup_from_top(cgroup_path); +} + +/** + * cleanup_cgroup_environment() - Cleanup Cgroup Testing Environment + * + * This is an idempotent function to delete all temporary cgroups that + * have been created during the test, including the cgroup testing work + * directory. + * + * At call time, it moves the calling process to the root cgroup, and then + * runs the deletion process. It is idempotent, and should not fail, unless + * a process is lingering. + * + * On failure, it will print an error to stderr, and try to continue. + */ +void cleanup_cgroup_environment(void) +{ + char cgroup_workdir[PATH_MAX + 1]; + + format_cgroup_path(cgroup_workdir, ""); + join_cgroup_from_top(CGROUP_MOUNT_PATH); + nftw(cgroup_workdir, nftwfunc, WALK_FD_LIMIT, FTW_DEPTH | FTW_MOUNT); +} + +/** + * create_and_get_cgroup() - Create a cgroup, relative to workdir, and get the FD + * @path: The cgroup path, relative to the workdir, to join + * + * This function creates a cgroup under the top level workdir and returns the + * file descriptor. It is idempotent. + * + * 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) +{ + char cgroup_path[PATH_MAX + 1]; + int fd; + + format_cgroup_path(cgroup_path, path); + if (mkdir(cgroup_path, 0777) && errno != EEXIST) { + log_err("mkdiring cgroup"); + return 0; + } + + fd = open(cgroup_path, O_RDONLY); + if (fd < 0) { + log_err("Opening Cgroup"); + return 0; + } + + return fd; +} diff --git a/samples/bpf/cgroup_helpers.h b/samples/bpf/cgroup_helpers.h new file mode 100644 index 000000000000..78c55207b6bd --- /dev/null +++ b/samples/bpf/cgroup_helpers.h @@ -0,0 +1,16 @@ +#ifndef __CGROUP_HELPERS_H +#define __CGROUP_HELPERS_H +#include <errno.h> +#include <string.h> + +#define clean_errno() (errno == 0 ? "None" : strerror(errno)) +#define log_err(MSG, ...) fprintf(stderr, "(%s:%d: errno: %s) " MSG "\n", \ + __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__) + + +int create_and_get_cgroup(char *path); +int join_cgroup(char *path); +int setup_cgroup_environment(void); +void cleanup_cgroup_environment(void); + +#endif diff --git a/samples/bpf/libbpf.c b/samples/bpf/libbpf.c index 9969e35550c3..9ce707bf02a7 100644 --- a/samples/bpf/libbpf.c +++ b/samples/bpf/libbpf.c @@ -104,6 +104,27 @@ int bpf_prog_load(enum bpf_prog_type prog_type, return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr)); } +int bpf_prog_attach(int prog_fd, int target_fd, enum bpf_attach_type type) +{ + union bpf_attr attr = { + .target_fd = target_fd, + .attach_bpf_fd = prog_fd, + .attach_type = type, + }; + + return syscall(__NR_bpf, BPF_PROG_ATTACH, &attr, sizeof(attr)); +} + +int bpf_prog_detach(int target_fd, enum bpf_attach_type type) +{ + union bpf_attr attr = { + .target_fd = target_fd, + .attach_type = type, + }; + + return syscall(__NR_bpf, BPF_PROG_DETACH, &attr, sizeof(attr)); +} + int bpf_obj_pin(int fd, const char *pathname) { union bpf_attr attr = { diff --git a/samples/bpf/libbpf.h b/samples/bpf/libbpf.h index ac6edb61b64a..94a901d86fc2 100644 --- a/samples/bpf/libbpf.h +++ b/samples/bpf/libbpf.h @@ -15,10 +15,13 @@ int bpf_prog_load(enum bpf_prog_type prog_type, const struct bpf_insn *insns, int insn_len, const char *license, int kern_version); +int bpf_prog_attach(int prog_fd, int attachable_fd, enum bpf_attach_type type); +int bpf_prog_detach(int attachable_fd, enum bpf_attach_type type); + int bpf_obj_pin(int fd, const char *pathname); int bpf_obj_get(const char *pathname); -#define LOG_BUF_SIZE 65536 +#define LOG_BUF_SIZE (256 * 1024) extern char bpf_log_buf[LOG_BUF_SIZE]; /* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */ diff --git a/samples/bpf/lwt_len_hist.sh b/samples/bpf/lwt_len_hist.sh new file mode 100644 index 000000000000..7d567744c7fa --- /dev/null +++ b/samples/bpf/lwt_len_hist.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +NS1=lwt_ns1 +VETH0=tst_lwt1a +VETH1=tst_lwt1b + +TRACE_ROOT=/sys/kernel/debug/tracing + +function cleanup { + ip route del 192.168.253.2/32 dev $VETH0 2> /dev/null + ip link del $VETH0 2> /dev/null + ip link del $VETH1 2> /dev/null + ip netns exec $NS1 killall netserver + ip netns delete $NS1 2> /dev/null +} + +cleanup + +ip netns add $NS1 +ip link add $VETH0 type veth peer name $VETH1 +ip link set dev $VETH0 up +ip addr add 192.168.253.1/24 dev $VETH0 +ip link set $VETH1 netns $NS1 +ip netns exec $NS1 ip link set dev $VETH1 up +ip netns exec $NS1 ip addr add 192.168.253.2/24 dev $VETH1 +ip netns exec $NS1 netserver + +echo 1 > ${TRACE_ROOT}/tracing_on +cp /dev/null ${TRACE_ROOT}/trace +ip route add 192.168.253.2/32 encap bpf out obj lwt_len_hist_kern.o section len_hist dev $VETH0 +netperf -H 192.168.253.2 -t TCP_STREAM +cat ${TRACE_ROOT}/trace | grep -v '^#' +./lwt_len_hist +cleanup +echo 0 > ${TRACE_ROOT}/tracing_on + |