Merge tag 'bpf-next-6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next
Pull bpf updates from Alexei Starovoitov: - Add BPF uprobe session support (Jiri Olsa) - Optimize uprobe performance (Andrii Nakryiko) - Add bpf_fastcall support to helpers and kfuncs (Eduard Zingerman) - Avoid calling free_htab_elem() under hash map bucket lock (Hou Tao) - Prevent tailcall infinite loop caused by freplace (Leon Hwang) - Mark raw_tracepoint arguments as nullable (Kumar Kartikeya Dwivedi) - Introduce uptr support in the task local storage map (Martin KaFai Lau) - Stringify errno log messages in libbpf (Mykyta Yatsenko) - Add kmem_cache BPF iterator for perf's lock profiling (Namhyung Kim) - Support BPF objects of either endianness in libbpf (Tony Ambardar) - Add ksym to struct_ops trampoline to fix stack trace (Xu Kuohai) - Introduce private stack for eligible BPF programs (Yonghong Song) - Migrate samples/bpf tests to selftests/bpf test_progs (Daniel T. Lee) - Migrate test_sock to selftests/bpf test_progs (Jordan Rife) * tag 'bpf-next-6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next: (152 commits) libbpf: Change hash_combine parameters from long to unsigned long selftests/bpf: Fix build error with llvm 19 libbpf: Fix memory leak in bpf_program__attach_uprobe_multi bpf: use common instruction history across all states bpf: Add necessary migrate_disable to range_tree. bpf: Do not alloc arena on unsupported arches selftests/bpf: Set test path for token/obj_priv_implicit_token_envvar selftests/bpf: Add a test for arena range tree algorithm bpf: Introduce range_tree data structure and use it in bpf arena samples/bpf: Remove unused variable in xdp2skb_meta_kern.c samples/bpf: Remove unused variables in tc_l2_redirect_kern.c bpftool: Cast variable `var` to long long bpf, x86: Propagate tailcall info only for subprogs bpf: Add kernel symbol for struct_ops trampoline bpf: Use function pointers count as struct_ops links count bpf: Remove unused member rcu from bpf_struct_ops_map selftests/bpf: Add struct_ops prog private stack tests bpf: Support private stack for struct_ops progs selftests/bpf: Add tracing prog private stack tests bpf, x86: Support private stack in jit ...
This commit is contained in:
@@ -17,20 +17,12 @@ tprogs-y += tracex3
|
||||
tprogs-y += tracex4
|
||||
tprogs-y += tracex5
|
||||
tprogs-y += tracex6
|
||||
tprogs-y += tracex7
|
||||
tprogs-y += test_probe_write_user
|
||||
tprogs-y += trace_output
|
||||
tprogs-y += lathist
|
||||
tprogs-y += offwaketime
|
||||
tprogs-y += spintest
|
||||
tprogs-y += map_perf_test
|
||||
tprogs-y += test_overhead
|
||||
tprogs-y += test_cgrp2_array_pin
|
||||
tprogs-y += test_cgrp2_attach
|
||||
tprogs-y += test_cgrp2_sock
|
||||
tprogs-y += test_cgrp2_sock2
|
||||
tprogs-y += xdp_router_ipv4
|
||||
tprogs-y += test_current_task_under_cgroup
|
||||
tprogs-y += trace_event
|
||||
tprogs-y += sampleip
|
||||
tprogs-y += tc_l2_redirect
|
||||
@@ -66,20 +58,12 @@ tracex3-objs := tracex3_user.o
|
||||
tracex4-objs := tracex4_user.o
|
||||
tracex5-objs := tracex5_user.o $(TRACE_HELPERS)
|
||||
tracex6-objs := tracex6_user.o
|
||||
tracex7-objs := tracex7_user.o
|
||||
test_probe_write_user-objs := test_probe_write_user_user.o
|
||||
trace_output-objs := trace_output_user.o
|
||||
lathist-objs := lathist_user.o
|
||||
offwaketime-objs := offwaketime_user.o $(TRACE_HELPERS)
|
||||
spintest-objs := spintest_user.o $(TRACE_HELPERS)
|
||||
map_perf_test-objs := map_perf_test_user.o
|
||||
test_overhead-objs := test_overhead_user.o
|
||||
test_cgrp2_array_pin-objs := test_cgrp2_array_pin.o
|
||||
test_cgrp2_attach-objs := test_cgrp2_attach.o
|
||||
test_cgrp2_sock-objs := test_cgrp2_sock.o
|
||||
test_cgrp2_sock2-objs := test_cgrp2_sock2.o
|
||||
test_current_task_under_cgroup-objs := $(CGROUP_HELPERS) \
|
||||
test_current_task_under_cgroup_user.o
|
||||
trace_event-objs := trace_event_user.o $(TRACE_HELPERS)
|
||||
sampleip-objs := sampleip_user.o $(TRACE_HELPERS)
|
||||
tc_l2_redirect-objs := tc_l2_redirect_user.o
|
||||
@@ -107,9 +91,6 @@ always-y += tracex3.bpf.o
|
||||
always-y += tracex4.bpf.o
|
||||
always-y += tracex5.bpf.o
|
||||
always-y += tracex6.bpf.o
|
||||
always-y += tracex7.bpf.o
|
||||
always-y += sock_flags.bpf.o
|
||||
always-y += test_probe_write_user.bpf.o
|
||||
always-y += trace_output.bpf.o
|
||||
always-y += tcbpf1_kern.o
|
||||
always-y += tc_l2_redirect_kern.o
|
||||
@@ -117,12 +98,7 @@ always-y += lathist_kern.o
|
||||
always-y += offwaketime.bpf.o
|
||||
always-y += spintest.bpf.o
|
||||
always-y += map_perf_test.bpf.o
|
||||
always-y += test_overhead_tp.bpf.o
|
||||
always-y += test_overhead_raw_tp.bpf.o
|
||||
always-y += test_overhead_kprobe.bpf.o
|
||||
always-y += parse_varlen.o parse_simple.o parse_ldabs.o
|
||||
always-y += test_cgrp2_tc.bpf.o
|
||||
always-y += test_current_task_under_cgroup.bpf.o
|
||||
always-y += trace_event_kern.o
|
||||
always-y += sampleip_kern.o
|
||||
always-y += lwt_len_hist.bpf.o
|
||||
@@ -195,7 +171,6 @@ TPROGLDLIBS_xdp_router_ipv4 += -lm -pthread
|
||||
TPROGLDLIBS_tracex4 += -lrt
|
||||
TPROGLDLIBS_trace_output += -lrt
|
||||
TPROGLDLIBS_map_perf_test += -lrt
|
||||
TPROGLDLIBS_test_overhead += -lrt
|
||||
|
||||
# Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline:
|
||||
# make M=samples/bpf LLC=~/git/llvm-project/llvm/build/bin/llc CLANG=~/git/llvm-project/llvm/build/bin/clang
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "vmlinux.h"
|
||||
#include "net_shared.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
SEC("cgroup/sock")
|
||||
int bpf_prog1(struct bpf_sock *sk)
|
||||
{
|
||||
char fmt[] = "socket: family %d type %d protocol %d\n";
|
||||
char fmt2[] = "socket: uid %u gid %u\n";
|
||||
__u64 gid_uid = bpf_get_current_uid_gid();
|
||||
__u32 uid = gid_uid & 0xffffffff;
|
||||
__u32 gid = gid_uid >> 32;
|
||||
|
||||
bpf_trace_printk(fmt, sizeof(fmt), sk->family, sk->type, sk->protocol);
|
||||
bpf_trace_printk(fmt2, sizeof(fmt2), uid, gid);
|
||||
|
||||
/* block AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6 sockets
|
||||
* ie., make ping6 fail
|
||||
*/
|
||||
if (sk->family == AF_INET6 &&
|
||||
sk->type == SOCK_DGRAM &&
|
||||
sk->protocol == IPPROTO_ICMPV6)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
SEC("cgroup/sock")
|
||||
int bpf_prog2(struct bpf_sock *sk)
|
||||
{
|
||||
char fmt[] = "socket: family %d type %d protocol %d\n";
|
||||
|
||||
bpf_trace_printk(fmt, sizeof(fmt), sk->family, sk->type, sk->protocol);
|
||||
|
||||
/* block AF_INET, SOCK_DGRAM, IPPROTO_ICMP sockets
|
||||
* ie., make ping fail
|
||||
*/
|
||||
if (sk->family == AF_INET &&
|
||||
sk->type == SOCK_DGRAM &&
|
||||
sk->protocol == IPPROTO_ICMP)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
@@ -2,6 +2,9 @@
|
||||
#include <uapi/linux/unistd.h>
|
||||
#include <linux/kbuild.h>
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wmissing-prototypes"
|
||||
|
||||
#define SYSNR(_NR) DEFINE(SYS ## _NR, _NR)
|
||||
|
||||
void syscall_defines(void)
|
||||
@@ -17,3 +20,5 @@ void syscall_defines(void)
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
@@ -58,14 +58,11 @@ static __always_inline bool is_vip_addr(__be16 eth_proto, __be32 daddr)
|
||||
SEC("l2_to_iptun_ingress_forward")
|
||||
int _l2_to_iptun_ingress_forward(struct __sk_buff *skb)
|
||||
{
|
||||
struct bpf_tunnel_key tkey = {};
|
||||
void *data = (void *)(long)skb->data;
|
||||
struct eth_hdr *eth = data;
|
||||
void *data_end = (void *)(long)skb->data_end;
|
||||
int key = 0, *ifindex;
|
||||
|
||||
int ret;
|
||||
|
||||
if (data + sizeof(*eth) > data_end)
|
||||
return TC_ACT_OK;
|
||||
|
||||
@@ -115,8 +112,6 @@ int _l2_to_iptun_ingress_redirect(struct __sk_buff *skb)
|
||||
void *data_end = (void *)(long)skb->data_end;
|
||||
int key = 0, *ifindex;
|
||||
|
||||
int ret;
|
||||
|
||||
if (data + sizeof(*eth) > data_end)
|
||||
return TC_ACT_OK;
|
||||
|
||||
@@ -205,7 +200,6 @@ int _l2_to_ip6tun_ingress_redirect(struct __sk_buff *skb)
|
||||
SEC("drop_non_tun_vip")
|
||||
int _drop_non_tun_vip(struct __sk_buff *skb)
|
||||
{
|
||||
struct bpf_tunnel_key tkey = {};
|
||||
void *data = (void *)(long)skb->data;
|
||||
struct eth_hdr *eth = data;
|
||||
void *data_end = (void *)(long)skb->data_end;
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2016 Facebook
|
||||
*/
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/bpf.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <bpf/bpf.h>
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
printf("Usage: test_cgrp2_array_pin [...]\n");
|
||||
printf(" -F <file> File to pin an BPF cgroup array\n");
|
||||
printf(" -U <file> Update an already pinned BPF cgroup array\n");
|
||||
printf(" -v <value> Full path of the cgroup2\n");
|
||||
printf(" -h Display this help\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *pinned_file = NULL, *cg2 = NULL;
|
||||
int create_array = 1;
|
||||
int array_key = 0;
|
||||
int array_fd = -1;
|
||||
int cg2_fd = -1;
|
||||
int ret = -1;
|
||||
int opt;
|
||||
|
||||
while ((opt = getopt(argc, argv, "F:U:v:")) != -1) {
|
||||
switch (opt) {
|
||||
/* General args */
|
||||
case 'F':
|
||||
pinned_file = optarg;
|
||||
break;
|
||||
case 'U':
|
||||
pinned_file = optarg;
|
||||
create_array = 0;
|
||||
break;
|
||||
case 'v':
|
||||
cg2 = optarg;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (!cg2 || !pinned_file) {
|
||||
usage();
|
||||
goto out;
|
||||
}
|
||||
|
||||
cg2_fd = open(cg2, O_RDONLY);
|
||||
if (cg2_fd < 0) {
|
||||
fprintf(stderr, "open(%s,...): %s(%d)\n",
|
||||
cg2, strerror(errno), errno);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (create_array) {
|
||||
array_fd = bpf_map_create(BPF_MAP_TYPE_CGROUP_ARRAY, NULL,
|
||||
sizeof(uint32_t), sizeof(uint32_t),
|
||||
1, NULL);
|
||||
if (array_fd < 0) {
|
||||
fprintf(stderr,
|
||||
"bpf_create_map(BPF_MAP_TYPE_CGROUP_ARRAY,...): %s(%d)\n",
|
||||
strerror(errno), errno);
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
array_fd = bpf_obj_get(pinned_file);
|
||||
if (array_fd < 0) {
|
||||
fprintf(stderr, "bpf_obj_get(%s): %s(%d)\n",
|
||||
pinned_file, strerror(errno), errno);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = bpf_map_update_elem(array_fd, &array_key, &cg2_fd, 0);
|
||||
if (ret) {
|
||||
perror("bpf_map_update_elem");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (create_array) {
|
||||
ret = bpf_obj_pin(array_fd, pinned_file);
|
||||
if (ret) {
|
||||
fprintf(stderr, "bpf_obj_pin(..., %s): %s(%d)\n",
|
||||
pinned_file, strerror(errno), errno);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (array_fd != -1)
|
||||
close(array_fd);
|
||||
if (cg2_fd != -1)
|
||||
close(cg2_fd);
|
||||
return ret;
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
/* eBPF example program:
|
||||
*
|
||||
* - Creates arraymap in kernel with 4 bytes keys and 8 byte values
|
||||
*
|
||||
* - Loads eBPF program
|
||||
*
|
||||
* The eBPF program accesses the map passed in to store two pieces of
|
||||
* information. The number of invocations of the program, which maps
|
||||
* to the number of packets received, is stored to key 0. Key 1 is
|
||||
* incremented on each iteration by the number of bytes stored in
|
||||
* the skb.
|
||||
*
|
||||
* - Attaches the new program to a cgroup using BPF_PROG_ATTACH
|
||||
*
|
||||
* - Every second, reads map[0] and map[1] to see how many bytes and
|
||||
* packets were seen on any socket of tasks in the given cgroup.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf.h>
|
||||
|
||||
#include "bpf_insn.h"
|
||||
#include "bpf_util.h"
|
||||
|
||||
enum {
|
||||
MAP_KEY_PACKETS,
|
||||
MAP_KEY_BYTES,
|
||||
};
|
||||
|
||||
char bpf_log_buf[BPF_LOG_BUF_SIZE];
|
||||
|
||||
static int prog_load(int map_fd, int verdict)
|
||||
{
|
||||
struct bpf_insn prog[] = {
|
||||
BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), /* save r6 so it's not clobbered by BPF_CALL */
|
||||
|
||||
/* Count packets */
|
||||
BPF_MOV64_IMM(BPF_REG_0, MAP_KEY_PACKETS), /* r0 = 0 */
|
||||
BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */
|
||||
BPF_LD_MAP_FD(BPF_REG_1, map_fd), /* load map fd to r1 */
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
|
||||
BPF_MOV64_IMM(BPF_REG_1, 1), /* r1 = 1 */
|
||||
BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_0, BPF_REG_1, 0),
|
||||
|
||||
/* Count bytes */
|
||||
BPF_MOV64_IMM(BPF_REG_0, MAP_KEY_BYTES), /* r0 = 1 */
|
||||
BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */
|
||||
BPF_LD_MAP_FD(BPF_REG_1, map_fd),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
|
||||
BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_6, offsetof(struct __sk_buff, len)), /* r1 = skb->len */
|
||||
|
||||
BPF_ATOMIC_OP(BPF_DW, BPF_ADD, BPF_REG_0, BPF_REG_1, 0),
|
||||
|
||||
BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */
|
||||
BPF_EXIT_INSN(),
|
||||
};
|
||||
size_t insns_cnt = ARRAY_SIZE(prog);
|
||||
LIBBPF_OPTS(bpf_prog_load_opts, opts,
|
||||
.log_buf = bpf_log_buf,
|
||||
.log_size = BPF_LOG_BUF_SIZE,
|
||||
);
|
||||
|
||||
return bpf_prog_load(BPF_PROG_TYPE_CGROUP_SKB, NULL, "GPL",
|
||||
prog, insns_cnt, &opts);
|
||||
}
|
||||
|
||||
static int usage(const char *argv0)
|
||||
{
|
||||
printf("Usage: %s [-d] [-D] <cg-path> <egress|ingress>\n", argv0);
|
||||
printf(" -d Drop Traffic\n");
|
||||
printf(" -D Detach filter, and exit\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
static int attach_filter(int cg_fd, int type, int verdict)
|
||||
{
|
||||
int prog_fd, map_fd, ret, key;
|
||||
long long pkt_cnt, byte_cnt;
|
||||
|
||||
map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL,
|
||||
sizeof(key), sizeof(byte_cnt),
|
||||
256, NULL);
|
||||
if (map_fd < 0) {
|
||||
printf("Failed to create map: '%s'\n", strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
prog_fd = prog_load(map_fd, verdict);
|
||||
printf("Output from kernel verifier:\n%s\n-------\n", bpf_log_buf);
|
||||
|
||||
if (prog_fd < 0) {
|
||||
printf("Failed to load prog: '%s'\n", strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
ret = bpf_prog_attach(prog_fd, cg_fd, type, 0);
|
||||
if (ret < 0) {
|
||||
printf("Failed to attach prog to cgroup: '%s'\n",
|
||||
strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
while (1) {
|
||||
key = MAP_KEY_PACKETS;
|
||||
assert(bpf_map_lookup_elem(map_fd, &key, &pkt_cnt) == 0);
|
||||
|
||||
key = MAP_KEY_BYTES;
|
||||
assert(bpf_map_lookup_elem(map_fd, &key, &byte_cnt) == 0);
|
||||
|
||||
printf("cgroup received %lld packets, %lld bytes\n",
|
||||
pkt_cnt, byte_cnt);
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int detach_only = 0, verdict = 1;
|
||||
enum bpf_attach_type type;
|
||||
int opt, cg_fd, ret;
|
||||
|
||||
while ((opt = getopt(argc, argv, "Dd")) != -1) {
|
||||
switch (opt) {
|
||||
case 'd':
|
||||
verdict = 0;
|
||||
break;
|
||||
case 'D':
|
||||
detach_only = 1;
|
||||
break;
|
||||
default:
|
||||
return usage(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (argc - optind < 2)
|
||||
return usage(argv[0]);
|
||||
|
||||
if (strcmp(argv[optind + 1], "ingress") == 0)
|
||||
type = BPF_CGROUP_INET_INGRESS;
|
||||
else if (strcmp(argv[optind + 1], "egress") == 0)
|
||||
type = BPF_CGROUP_INET_EGRESS;
|
||||
else
|
||||
return usage(argv[0]);
|
||||
|
||||
cg_fd = open(argv[optind], O_DIRECTORY | O_RDONLY);
|
||||
if (cg_fd < 0) {
|
||||
printf("Failed to open cgroup path: '%s'\n", strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (detach_only) {
|
||||
ret = bpf_prog_detach(cg_fd, type);
|
||||
printf("bpf_prog_detach() returned '%s' (%d)\n",
|
||||
strerror(errno), errno);
|
||||
} else
|
||||
ret = attach_filter(cg_fd, type, verdict);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1,294 +0,0 @@
|
||||
/* eBPF example program:
|
||||
*
|
||||
* - Loads eBPF program
|
||||
*
|
||||
* The eBPF program sets the sk_bound_dev_if index in new AF_INET{6}
|
||||
* sockets opened by processes in the cgroup.
|
||||
*
|
||||
* - Attaches the new program to a cgroup using BPF_PROG_ATTACH
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <net/if.h>
|
||||
#include <inttypes.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf.h>
|
||||
|
||||
#include "bpf_insn.h"
|
||||
|
||||
char bpf_log_buf[BPF_LOG_BUF_SIZE];
|
||||
|
||||
static int prog_load(__u32 idx, __u32 mark, __u32 prio)
|
||||
{
|
||||
/* save pointer to context */
|
||||
struct bpf_insn prog_start[] = {
|
||||
BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
|
||||
};
|
||||
struct bpf_insn prog_end[] = {
|
||||
BPF_MOV64_IMM(BPF_REG_0, 1), /* r0 = verdict */
|
||||
BPF_EXIT_INSN(),
|
||||
};
|
||||
|
||||
/* set sk_bound_dev_if on socket */
|
||||
struct bpf_insn prog_dev[] = {
|
||||
BPF_MOV64_IMM(BPF_REG_3, idx),
|
||||
BPF_MOV64_IMM(BPF_REG_2, offsetof(struct bpf_sock, bound_dev_if)),
|
||||
BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, offsetof(struct bpf_sock, bound_dev_if)),
|
||||
};
|
||||
|
||||
/* set mark on socket */
|
||||
struct bpf_insn prog_mark[] = {
|
||||
/* get uid of process */
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
|
||||
BPF_FUNC_get_current_uid_gid),
|
||||
BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 0xffffffff),
|
||||
|
||||
/* if uid is 0, use given mark, else use the uid as the mark */
|
||||
BPF_MOV64_REG(BPF_REG_3, BPF_REG_0),
|
||||
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
|
||||
BPF_MOV64_IMM(BPF_REG_3, mark),
|
||||
|
||||
/* set the mark on the new socket */
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
|
||||
BPF_MOV64_IMM(BPF_REG_2, offsetof(struct bpf_sock, mark)),
|
||||
BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, offsetof(struct bpf_sock, mark)),
|
||||
};
|
||||
|
||||
/* set priority on socket */
|
||||
struct bpf_insn prog_prio[] = {
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
|
||||
BPF_MOV64_IMM(BPF_REG_3, prio),
|
||||
BPF_MOV64_IMM(BPF_REG_2, offsetof(struct bpf_sock, priority)),
|
||||
BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, offsetof(struct bpf_sock, priority)),
|
||||
};
|
||||
LIBBPF_OPTS(bpf_prog_load_opts, opts,
|
||||
.log_buf = bpf_log_buf,
|
||||
.log_size = BPF_LOG_BUF_SIZE,
|
||||
);
|
||||
|
||||
struct bpf_insn *prog;
|
||||
size_t insns_cnt;
|
||||
void *p;
|
||||
int ret;
|
||||
|
||||
insns_cnt = sizeof(prog_start) + sizeof(prog_end);
|
||||
if (idx)
|
||||
insns_cnt += sizeof(prog_dev);
|
||||
|
||||
if (mark)
|
||||
insns_cnt += sizeof(prog_mark);
|
||||
|
||||
if (prio)
|
||||
insns_cnt += sizeof(prog_prio);
|
||||
|
||||
p = prog = malloc(insns_cnt);
|
||||
if (!prog) {
|
||||
fprintf(stderr, "Failed to allocate memory for instructions\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
memcpy(p, prog_start, sizeof(prog_start));
|
||||
p += sizeof(prog_start);
|
||||
|
||||
if (idx) {
|
||||
memcpy(p, prog_dev, sizeof(prog_dev));
|
||||
p += sizeof(prog_dev);
|
||||
}
|
||||
|
||||
if (mark) {
|
||||
memcpy(p, prog_mark, sizeof(prog_mark));
|
||||
p += sizeof(prog_mark);
|
||||
}
|
||||
|
||||
if (prio) {
|
||||
memcpy(p, prog_prio, sizeof(prog_prio));
|
||||
p += sizeof(prog_prio);
|
||||
}
|
||||
|
||||
memcpy(p, prog_end, sizeof(prog_end));
|
||||
p += sizeof(prog_end);
|
||||
|
||||
insns_cnt /= sizeof(struct bpf_insn);
|
||||
|
||||
ret = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, NULL, "GPL",
|
||||
prog, insns_cnt, &opts);
|
||||
|
||||
free(prog);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_bind_to_device(int sd, char *name, size_t len)
|
||||
{
|
||||
socklen_t optlen = len;
|
||||
int rc;
|
||||
|
||||
name[0] = '\0';
|
||||
rc = getsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, name, &optlen);
|
||||
if (rc < 0)
|
||||
perror("setsockopt(SO_BINDTODEVICE)");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static unsigned int get_somark(int sd)
|
||||
{
|
||||
unsigned int mark = 0;
|
||||
socklen_t optlen = sizeof(mark);
|
||||
int rc;
|
||||
|
||||
rc = getsockopt(sd, SOL_SOCKET, SO_MARK, &mark, &optlen);
|
||||
if (rc < 0)
|
||||
perror("getsockopt(SO_MARK)");
|
||||
|
||||
return mark;
|
||||
}
|
||||
|
||||
static unsigned int get_priority(int sd)
|
||||
{
|
||||
unsigned int prio = 0;
|
||||
socklen_t optlen = sizeof(prio);
|
||||
int rc;
|
||||
|
||||
rc = getsockopt(sd, SOL_SOCKET, SO_PRIORITY, &prio, &optlen);
|
||||
if (rc < 0)
|
||||
perror("getsockopt(SO_PRIORITY)");
|
||||
|
||||
return prio;
|
||||
}
|
||||
|
||||
static int show_sockopts(int family)
|
||||
{
|
||||
unsigned int mark, prio;
|
||||
char name[16];
|
||||
int sd;
|
||||
|
||||
sd = socket(family, SOCK_DGRAM, 17);
|
||||
if (sd < 0) {
|
||||
perror("socket");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (get_bind_to_device(sd, name, sizeof(name)) < 0)
|
||||
return 1;
|
||||
|
||||
mark = get_somark(sd);
|
||||
prio = get_priority(sd);
|
||||
|
||||
close(sd);
|
||||
|
||||
printf("sd %d: dev %s, mark %u, priority %u\n", sd, name, mark, prio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usage(const char *argv0)
|
||||
{
|
||||
printf("Usage:\n");
|
||||
printf(" Attach a program\n");
|
||||
printf(" %s -b bind-to-dev -m mark -p prio cg-path\n", argv0);
|
||||
printf("\n");
|
||||
printf(" Detach a program\n");
|
||||
printf(" %s -d cg-path\n", argv0);
|
||||
printf("\n");
|
||||
printf(" Show inherited socket settings (mark, priority, and device)\n");
|
||||
printf(" %s [-6]\n", argv0);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
__u32 idx = 0, mark = 0, prio = 0;
|
||||
const char *cgrp_path = NULL;
|
||||
int cg_fd, prog_fd, ret;
|
||||
int family = PF_INET;
|
||||
int do_attach = 1;
|
||||
int rc;
|
||||
|
||||
while ((rc = getopt(argc, argv, "db:m:p:6")) != -1) {
|
||||
switch (rc) {
|
||||
case 'd':
|
||||
do_attach = 0;
|
||||
break;
|
||||
case 'b':
|
||||
idx = if_nametoindex(optarg);
|
||||
if (!idx) {
|
||||
idx = strtoumax(optarg, NULL, 0);
|
||||
if (!idx) {
|
||||
printf("Invalid device name\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
mark = strtoumax(optarg, NULL, 0);
|
||||
break;
|
||||
case 'p':
|
||||
prio = strtoumax(optarg, NULL, 0);
|
||||
break;
|
||||
case '6':
|
||||
family = PF_INET6;
|
||||
break;
|
||||
default:
|
||||
return usage(argv[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind == argc)
|
||||
return show_sockopts(family);
|
||||
|
||||
cgrp_path = argv[optind];
|
||||
if (!cgrp_path) {
|
||||
fprintf(stderr, "cgroup path not given\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (do_attach && !idx && !mark && !prio) {
|
||||
fprintf(stderr,
|
||||
"One of device, mark or priority must be given\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
cg_fd = open(cgrp_path, O_DIRECTORY | O_RDONLY);
|
||||
if (cg_fd < 0) {
|
||||
printf("Failed to open cgroup path: '%s'\n", strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (do_attach) {
|
||||
prog_fd = prog_load(idx, mark, prio);
|
||||
if (prog_fd < 0) {
|
||||
printf("Failed to load prog: '%s'\n", strerror(errno));
|
||||
printf("Output from kernel verifier:\n%s\n-------\n",
|
||||
bpf_log_buf);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
ret = bpf_prog_attach(prog_fd, cg_fd,
|
||||
BPF_CGROUP_INET_SOCK_CREATE, 0);
|
||||
if (ret < 0) {
|
||||
printf("Failed to attach prog to cgroup: '%s'\n",
|
||||
strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
} else {
|
||||
ret = bpf_prog_detach(cg_fd, BPF_CGROUP_INET_SOCK_CREATE);
|
||||
if (ret < 0) {
|
||||
printf("Failed to detach prog from cgroup: '%s'\n",
|
||||
strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
close(cg_fd);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
# Test various socket options that can be set by attaching programs to cgroups.
|
||||
|
||||
MY_DIR=$(dirname $0)
|
||||
TEST=$MY_DIR/test_cgrp2_sock
|
||||
CGRP_MNT="/tmp/cgroupv2-test_cgrp2_sock"
|
||||
|
||||
################################################################################
|
||||
#
|
||||
print_result()
|
||||
{
|
||||
local rc=$1
|
||||
local status=" OK "
|
||||
|
||||
[ $rc -ne 0 ] && status="FAIL"
|
||||
|
||||
printf "%-50s [%4s]\n" "$2" "$status"
|
||||
}
|
||||
|
||||
check_sock()
|
||||
{
|
||||
out=$($TEST)
|
||||
echo $out | grep -q "$1"
|
||||
if [ $? -ne 0 ]; then
|
||||
print_result 1 "IPv4: $2"
|
||||
echo " expected: $1"
|
||||
echo " have: $out"
|
||||
rc=1
|
||||
else
|
||||
print_result 0 "IPv4: $2"
|
||||
fi
|
||||
}
|
||||
|
||||
check_sock6()
|
||||
{
|
||||
out=$($TEST -6)
|
||||
echo $out | grep -q "$1"
|
||||
if [ $? -ne 0 ]; then
|
||||
print_result 1 "IPv6: $2"
|
||||
echo " expected: $1"
|
||||
echo " have: $out"
|
||||
rc=1
|
||||
else
|
||||
print_result 0 "IPv6: $2"
|
||||
fi
|
||||
}
|
||||
|
||||
################################################################################
|
||||
#
|
||||
|
||||
cleanup()
|
||||
{
|
||||
echo $$ >> ${CGRP_MNT}/cgroup.procs
|
||||
rmdir ${CGRP_MNT}/sockopts
|
||||
}
|
||||
|
||||
cleanup_and_exit()
|
||||
{
|
||||
local rc=$1
|
||||
local msg="$2"
|
||||
|
||||
[ -n "$msg" ] && echo "ERROR: $msg"
|
||||
|
||||
$TEST -d ${CGRP_MNT}/sockopts
|
||||
ip li del cgrp2_sock
|
||||
umount ${CGRP_MNT}
|
||||
|
||||
exit $rc
|
||||
}
|
||||
|
||||
|
||||
################################################################################
|
||||
# main
|
||||
|
||||
rc=0
|
||||
|
||||
ip li add cgrp2_sock type dummy 2>/dev/null
|
||||
|
||||
set -e
|
||||
mkdir -p ${CGRP_MNT}
|
||||
mount -t cgroup2 none ${CGRP_MNT}
|
||||
set +e
|
||||
|
||||
|
||||
# make sure we have a known start point
|
||||
cleanup 2>/dev/null
|
||||
|
||||
mkdir -p ${CGRP_MNT}/sockopts
|
||||
[ $? -ne 0 ] && cleanup_and_exit 1 "Failed to create cgroup hierarchy"
|
||||
|
||||
|
||||
# set pid into cgroup
|
||||
echo $$ > ${CGRP_MNT}/sockopts/cgroup.procs
|
||||
|
||||
# no bpf program attached, so socket should show no settings
|
||||
check_sock "dev , mark 0, priority 0" "No programs attached"
|
||||
check_sock6 "dev , mark 0, priority 0" "No programs attached"
|
||||
|
||||
# verify device is set
|
||||
#
|
||||
$TEST -b cgrp2_sock ${CGRP_MNT}/sockopts
|
||||
if [ $? -ne 0 ]; then
|
||||
cleanup_and_exit 1 "Failed to install program to set device"
|
||||
fi
|
||||
check_sock "dev cgrp2_sock, mark 0, priority 0" "Device set"
|
||||
check_sock6 "dev cgrp2_sock, mark 0, priority 0" "Device set"
|
||||
|
||||
# verify mark is set
|
||||
#
|
||||
$TEST -m 666 ${CGRP_MNT}/sockopts
|
||||
if [ $? -ne 0 ]; then
|
||||
cleanup_and_exit 1 "Failed to install program to set mark"
|
||||
fi
|
||||
check_sock "dev , mark 666, priority 0" "Mark set"
|
||||
check_sock6 "dev , mark 666, priority 0" "Mark set"
|
||||
|
||||
# verify priority is set
|
||||
#
|
||||
$TEST -p 123 ${CGRP_MNT}/sockopts
|
||||
if [ $? -ne 0 ]; then
|
||||
cleanup_and_exit 1 "Failed to install program to set priority"
|
||||
fi
|
||||
check_sock "dev , mark 0, priority 123" "Priority set"
|
||||
check_sock6 "dev , mark 0, priority 123" "Priority set"
|
||||
|
||||
# all 3 at once
|
||||
#
|
||||
$TEST -b cgrp2_sock -m 666 -p 123 ${CGRP_MNT}/sockopts
|
||||
if [ $? -ne 0 ]; then
|
||||
cleanup_and_exit 1 "Failed to install program to set device, mark and priority"
|
||||
fi
|
||||
check_sock "dev cgrp2_sock, mark 666, priority 123" "Priority set"
|
||||
check_sock6 "dev cgrp2_sock, mark 666, priority 123" "Priority set"
|
||||
|
||||
cleanup_and_exit $rc
|
||||
@@ -1,95 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* eBPF example program:
|
||||
*
|
||||
* - Loads eBPF program
|
||||
*
|
||||
* The eBPF program loads a filter from file and attaches the
|
||||
* program to a cgroup using BPF_PROG_ATTACH
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
|
||||
#include "bpf_insn.h"
|
||||
|
||||
static int usage(const char *argv0)
|
||||
{
|
||||
printf("Usage: %s cg-path filter-path [filter-id]\n", argv0);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int cg_fd, err, ret = EXIT_FAILURE, filter_id = 0, prog_cnt = 0;
|
||||
const char *link_pin_path = "/sys/fs/bpf/test_cgrp2_sock2";
|
||||
struct bpf_link *link = NULL;
|
||||
struct bpf_program *progs[2];
|
||||
struct bpf_program *prog;
|
||||
struct bpf_object *obj;
|
||||
|
||||
if (argc < 3)
|
||||
return usage(argv[0]);
|
||||
|
||||
if (argc > 3)
|
||||
filter_id = atoi(argv[3]);
|
||||
|
||||
cg_fd = open(argv[1], O_DIRECTORY | O_RDONLY);
|
||||
if (cg_fd < 0) {
|
||||
printf("Failed to open cgroup path: '%s'\n", strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
|
||||
obj = bpf_object__open_file(argv[2], NULL);
|
||||
if (libbpf_get_error(obj)) {
|
||||
printf("ERROR: opening BPF object file failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
bpf_object__for_each_program(prog, obj) {
|
||||
progs[prog_cnt] = prog;
|
||||
prog_cnt++;
|
||||
}
|
||||
|
||||
if (filter_id >= prog_cnt) {
|
||||
printf("Invalid program id; program not found in file\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* load BPF program */
|
||||
if (bpf_object__load(obj)) {
|
||||
printf("ERROR: loading BPF object file failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
link = bpf_program__attach_cgroup(progs[filter_id], cg_fd);
|
||||
if (libbpf_get_error(link)) {
|
||||
printf("ERROR: bpf_program__attach failed\n");
|
||||
link = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
err = bpf_link__pin(link, link_pin_path);
|
||||
if (err < 0) {
|
||||
printf("ERROR: bpf_link__pin failed: %d\n", err);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = EXIT_SUCCESS;
|
||||
|
||||
cleanup:
|
||||
bpf_link__destroy(link);
|
||||
bpf_object__close(obj);
|
||||
return ret;
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
BPFFS=/sys/fs/bpf
|
||||
MY_DIR=$(dirname $0)
|
||||
TEST=$MY_DIR/test_cgrp2_sock2
|
||||
LINK_PIN=$BPFFS/test_cgrp2_sock2
|
||||
BPF_PROG=$MY_DIR/sock_flags.bpf.o
|
||||
|
||||
function config_device {
|
||||
ip netns add at_ns0
|
||||
ip link add veth0 type veth peer name veth0b
|
||||
ip link set veth0 netns at_ns0
|
||||
ip netns exec at_ns0 sysctl -q net.ipv6.conf.veth0.disable_ipv6=0
|
||||
ip netns exec at_ns0 ip addr add 172.16.1.100/24 dev veth0
|
||||
ip netns exec at_ns0 ip addr add 2401:db00::1/64 dev veth0 nodad
|
||||
ip netns exec at_ns0 ip link set dev veth0 up
|
||||
sysctl -q net.ipv6.conf.veth0b.disable_ipv6=0
|
||||
ip addr add 172.16.1.101/24 dev veth0b
|
||||
ip addr add 2401:db00::2/64 dev veth0b nodad
|
||||
ip link set veth0b up
|
||||
}
|
||||
|
||||
function config_cgroup {
|
||||
rm -rf /tmp/cgroupv2
|
||||
mkdir -p /tmp/cgroupv2
|
||||
mount -t cgroup2 none /tmp/cgroupv2
|
||||
mkdir -p /tmp/cgroupv2/foo
|
||||
echo $$ >> /tmp/cgroupv2/foo/cgroup.procs
|
||||
}
|
||||
|
||||
function config_bpffs {
|
||||
if mount | grep $BPFFS > /dev/null; then
|
||||
echo "bpffs already mounted"
|
||||
else
|
||||
echo "bpffs not mounted. Mounting..."
|
||||
mount -t bpf none $BPFFS
|
||||
fi
|
||||
}
|
||||
|
||||
function attach_bpf {
|
||||
$TEST /tmp/cgroupv2/foo $BPF_PROG $1
|
||||
[ $? -ne 0 ] && exit 1
|
||||
}
|
||||
|
||||
function cleanup {
|
||||
rm -rf $LINK_PIN
|
||||
ip link del veth0b
|
||||
ip netns delete at_ns0
|
||||
umount /tmp/cgroupv2
|
||||
rm -rf /tmp/cgroupv2
|
||||
}
|
||||
|
||||
cleanup 2>/dev/null
|
||||
|
||||
set -e
|
||||
config_device
|
||||
config_cgroup
|
||||
config_bpffs
|
||||
set +e
|
||||
|
||||
#
|
||||
# Test 1 - fail ping6
|
||||
#
|
||||
attach_bpf 0
|
||||
ping -c1 -w1 172.16.1.100
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "ping failed when it should succeed"
|
||||
cleanup
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ping6 -c1 -w1 2401:db00::1
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "ping6 succeeded when it should not"
|
||||
cleanup
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -rf $LINK_PIN
|
||||
sleep 1 # Wait for link detach
|
||||
|
||||
#
|
||||
# Test 2 - fail ping
|
||||
#
|
||||
attach_bpf 1
|
||||
ping6 -c1 -w1 2401:db00::1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "ping6 failed when it should succeed"
|
||||
cleanup
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ping -c1 -w1 172.16.1.100
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "ping succeeded when it should not"
|
||||
cleanup
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cleanup
|
||||
echo
|
||||
echo "*** PASS ***"
|
||||
@@ -1,56 +0,0 @@
|
||||
/* Copyright (c) 2016 Facebook
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*/
|
||||
#define KBUILD_MODNAME "foo"
|
||||
#include "vmlinux.h"
|
||||
#include "net_shared.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
/* copy of 'struct ethhdr' without __packed */
|
||||
struct eth_hdr {
|
||||
unsigned char h_dest[ETH_ALEN];
|
||||
unsigned char h_source[ETH_ALEN];
|
||||
unsigned short h_proto;
|
||||
};
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_CGROUP_ARRAY);
|
||||
__type(key, u32);
|
||||
__type(value, u32);
|
||||
__uint(pinning, LIBBPF_PIN_BY_NAME);
|
||||
__uint(max_entries, 1);
|
||||
} test_cgrp2_array_pin SEC(".maps");
|
||||
|
||||
SEC("filter")
|
||||
int handle_egress(struct __sk_buff *skb)
|
||||
{
|
||||
void *data = (void *)(long)skb->data;
|
||||
struct eth_hdr *eth = data;
|
||||
struct ipv6hdr *ip6h = data + sizeof(*eth);
|
||||
void *data_end = (void *)(long)skb->data_end;
|
||||
char dont_care_msg[] = "dont care %04x %d\n";
|
||||
char pass_msg[] = "pass\n";
|
||||
char reject_msg[] = "reject\n";
|
||||
|
||||
/* single length check */
|
||||
if (data + sizeof(*eth) + sizeof(*ip6h) > data_end)
|
||||
return TC_ACT_OK;
|
||||
|
||||
if (eth->h_proto != bpf_htons(ETH_P_IPV6) ||
|
||||
ip6h->nexthdr != IPPROTO_ICMPV6) {
|
||||
bpf_trace_printk(dont_care_msg, sizeof(dont_care_msg),
|
||||
eth->h_proto, ip6h->nexthdr);
|
||||
return TC_ACT_OK;
|
||||
} else if (bpf_skb_under_cgroup(skb, &test_cgrp2_array_pin, 0) != 1) {
|
||||
bpf_trace_printk(pass_msg, sizeof(pass_msg));
|
||||
return TC_ACT_OK;
|
||||
} else {
|
||||
bpf_trace_printk(reject_msg, sizeof(reject_msg));
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
@@ -1,187 +0,0 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
MY_DIR=$(dirname $0)
|
||||
# Details on the bpf prog
|
||||
BPF_CGRP2_ARRAY_NAME='test_cgrp2_array_pin'
|
||||
BPF_PROG="$MY_DIR/test_cgrp2_tc.bpf.o"
|
||||
BPF_SECTION='filter'
|
||||
|
||||
[ -z "$TC" ] && TC='tc'
|
||||
[ -z "$IP" ] && IP='ip'
|
||||
|
||||
# Names of the veth interface, net namespace...etc.
|
||||
HOST_IFC='ve'
|
||||
NS_IFC='vens'
|
||||
NS='ns'
|
||||
|
||||
find_mnt() {
|
||||
cat /proc/mounts | \
|
||||
awk '{ if ($3 == "'$1'" && mnt == "") { mnt = $2 }} END { print mnt }'
|
||||
}
|
||||
|
||||
# Init cgroup2 vars
|
||||
init_cgrp2_vars() {
|
||||
CGRP2_ROOT=$(find_mnt cgroup2)
|
||||
if [ -z "$CGRP2_ROOT" ]
|
||||
then
|
||||
CGRP2_ROOT='/mnt/cgroup2'
|
||||
MOUNT_CGRP2="yes"
|
||||
fi
|
||||
CGRP2_TC="$CGRP2_ROOT/tc"
|
||||
CGRP2_TC_LEAF="$CGRP2_TC/leaf"
|
||||
}
|
||||
|
||||
# Init bpf fs vars
|
||||
init_bpf_fs_vars() {
|
||||
local bpf_fs_root=$(find_mnt bpf)
|
||||
[ -n "$bpf_fs_root" ] || return -1
|
||||
BPF_FS_TC_SHARE="$bpf_fs_root/tc/globals"
|
||||
}
|
||||
|
||||
setup_cgrp2() {
|
||||
case $1 in
|
||||
start)
|
||||
if [ "$MOUNT_CGRP2" == 'yes' ]
|
||||
then
|
||||
[ -d $CGRP2_ROOT ] || mkdir -p $CGRP2_ROOT
|
||||
mount -t cgroup2 none $CGRP2_ROOT || return $?
|
||||
fi
|
||||
mkdir -p $CGRP2_TC_LEAF
|
||||
;;
|
||||
*)
|
||||
rmdir $CGRP2_TC_LEAF && rmdir $CGRP2_TC
|
||||
[ "$MOUNT_CGRP2" == 'yes' ] && umount $CGRP2_ROOT
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
setup_bpf_cgrp2_array() {
|
||||
local bpf_cgrp2_array="$BPF_FS_TC_SHARE/$BPF_CGRP2_ARRAY_NAME"
|
||||
case $1 in
|
||||
start)
|
||||
$MY_DIR/test_cgrp2_array_pin -U $bpf_cgrp2_array -v $CGRP2_TC
|
||||
;;
|
||||
*)
|
||||
[ -d "$BPF_FS_TC_SHARE" ] && rm -f $bpf_cgrp2_array
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
setup_net() {
|
||||
case $1 in
|
||||
start)
|
||||
$IP link add $HOST_IFC type veth peer name $NS_IFC || return $?
|
||||
$IP link set dev $HOST_IFC up || return $?
|
||||
sysctl -q net.ipv6.conf.$HOST_IFC.disable_ipv6=0
|
||||
sysctl -q net.ipv6.conf.$HOST_IFC.accept_dad=0
|
||||
|
||||
$IP netns add $NS || return $?
|
||||
$IP link set dev $NS_IFC netns $NS || return $?
|
||||
$IP -n $NS link set dev $NS_IFC up || return $?
|
||||
$IP netns exec $NS sysctl -q net.ipv6.conf.$NS_IFC.disable_ipv6=0
|
||||
$IP netns exec $NS sysctl -q net.ipv6.conf.$NS_IFC.accept_dad=0
|
||||
$TC qdisc add dev $HOST_IFC clsact || return $?
|
||||
$TC filter add dev $HOST_IFC egress bpf da obj $BPF_PROG sec $BPF_SECTION || return $?
|
||||
;;
|
||||
*)
|
||||
$IP netns del $NS
|
||||
$IP link del $HOST_IFC
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
run_in_cgrp() {
|
||||
# Fork another bash and move it under the specified cgroup.
|
||||
# It makes the cgroup cleanup easier at the end of the test.
|
||||
cmd='echo $$ > '
|
||||
cmd="$cmd $1/cgroup.procs; exec $2"
|
||||
bash -c "$cmd"
|
||||
}
|
||||
|
||||
do_test() {
|
||||
run_in_cgrp $CGRP2_TC_LEAF "ping -6 -c3 ff02::1%$HOST_IFC >& /dev/null"
|
||||
local dropped=$($TC -s qdisc show dev $HOST_IFC | tail -3 | \
|
||||
awk '/drop/{print substr($7, 0, index($7, ",")-1)}')
|
||||
if [[ $dropped -eq 0 ]]
|
||||
then
|
||||
echo "FAIL"
|
||||
return 1
|
||||
else
|
||||
echo "Successfully filtered $dropped packets"
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
do_exit() {
|
||||
if [ "$DEBUG" == "yes" ] && [ "$MODE" != 'cleanuponly' ]
|
||||
then
|
||||
echo "------ DEBUG ------"
|
||||
echo "mount: "; mount | grep -E '(cgroup2|bpf)'; echo
|
||||
echo "$CGRP2_TC_LEAF: "; ls -l $CGRP2_TC_LEAF; echo
|
||||
if [ -d "$BPF_FS_TC_SHARE" ]
|
||||
then
|
||||
echo "$BPF_FS_TC_SHARE: "; ls -l $BPF_FS_TC_SHARE; echo
|
||||
fi
|
||||
echo "Host net:"
|
||||
$IP netns
|
||||
$IP link show dev $HOST_IFC
|
||||
$IP -6 a show dev $HOST_IFC
|
||||
$TC -s qdisc show dev $HOST_IFC
|
||||
echo
|
||||
echo "$NS net:"
|
||||
$IP -n $NS link show dev $NS_IFC
|
||||
$IP -n $NS -6 link show dev $NS_IFC
|
||||
echo "------ DEBUG ------"
|
||||
echo
|
||||
fi
|
||||
|
||||
if [ "$MODE" != 'nocleanup' ]
|
||||
then
|
||||
setup_net stop
|
||||
setup_bpf_cgrp2_array stop
|
||||
setup_cgrp2 stop
|
||||
fi
|
||||
}
|
||||
|
||||
init_cgrp2_vars
|
||||
init_bpf_fs_vars
|
||||
|
||||
while [[ $# -ge 1 ]]
|
||||
do
|
||||
a="$1"
|
||||
case $a in
|
||||
debug)
|
||||
DEBUG='yes'
|
||||
shift 1
|
||||
;;
|
||||
cleanup-only)
|
||||
MODE='cleanuponly'
|
||||
shift 1
|
||||
;;
|
||||
no-cleanup)
|
||||
MODE='nocleanup'
|
||||
shift 1
|
||||
;;
|
||||
*)
|
||||
echo "test_cgrp2_tc [debug] [cleanup-only | no-cleanup]"
|
||||
echo " debug: Print cgrp and network setup details at the end of the test"
|
||||
echo " cleanup-only: Try to cleanup things from last test. No test will be run"
|
||||
echo " no-cleanup: Run the test but don't do cleanup at the end"
|
||||
echo "[Note: If no arg is given, it will run the test and do cleanup at the end]"
|
||||
echo
|
||||
exit -1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
trap do_exit 0
|
||||
|
||||
[ "$MODE" == 'cleanuponly' ] && exit
|
||||
|
||||
setup_cgrp2 start || exit $?
|
||||
setup_net start || exit $?
|
||||
init_bpf_fs_vars || exit $?
|
||||
setup_bpf_cgrp2_array start || exit $?
|
||||
do_test
|
||||
echo
|
||||
@@ -1,43 +0,0 @@
|
||||
/* Copyright (c) 2016 Sargun Dhillon <sargun@sargun.me>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include "vmlinux.h"
|
||||
#include <linux/version.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include <bpf/bpf_core_read.h>
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_CGROUP_ARRAY);
|
||||
__uint(key_size, sizeof(u32));
|
||||
__uint(value_size, sizeof(u32));
|
||||
__uint(max_entries, 1);
|
||||
} cgroup_map SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__type(key, u32);
|
||||
__type(value, u64);
|
||||
__uint(max_entries, 1);
|
||||
} perf_map SEC(".maps");
|
||||
|
||||
/* Writes the last PID that called sync to a map at index 0 */
|
||||
SEC("ksyscall/sync")
|
||||
int BPF_KSYSCALL(bpf_prog1)
|
||||
{
|
||||
u64 pid = bpf_get_current_pid_tgid();
|
||||
int idx = 0;
|
||||
|
||||
if (!bpf_current_task_under_cgroup(&cgroup_map, 0))
|
||||
return 0;
|
||||
|
||||
bpf_map_update_elem(&perf_map, &idx, &pid, BPF_ANY);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
u32 _version SEC("version") = LINUX_VERSION_CODE;
|
||||
@@ -1,115 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2016 Sargun Dhillon <sargun@sargun.me>
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
#include "cgroup_helpers.h"
|
||||
|
||||
#define CGROUP_PATH "/my-cgroup"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
pid_t remote_pid, local_pid = getpid();
|
||||
int cg2 = -1, idx = 0, rc = 1;
|
||||
struct bpf_link *link = NULL;
|
||||
struct bpf_program *prog;
|
||||
struct bpf_object *obj;
|
||||
char filename[256];
|
||||
int map_fd[2];
|
||||
|
||||
snprintf(filename, sizeof(filename), "%s.bpf.o", argv[0]);
|
||||
obj = bpf_object__open_file(filename, NULL);
|
||||
if (libbpf_get_error(obj)) {
|
||||
fprintf(stderr, "ERROR: opening BPF object file failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
prog = bpf_object__find_program_by_name(obj, "bpf_prog1");
|
||||
if (!prog) {
|
||||
printf("finding a prog in obj file failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* load BPF program */
|
||||
if (bpf_object__load(obj)) {
|
||||
fprintf(stderr, "ERROR: loading BPF object file failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
map_fd[0] = bpf_object__find_map_fd_by_name(obj, "cgroup_map");
|
||||
map_fd[1] = bpf_object__find_map_fd_by_name(obj, "perf_map");
|
||||
if (map_fd[0] < 0 || map_fd[1] < 0) {
|
||||
fprintf(stderr, "ERROR: finding a map in obj file failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
link = bpf_program__attach(prog);
|
||||
if (libbpf_get_error(link)) {
|
||||
fprintf(stderr, "ERROR: bpf_program__attach failed\n");
|
||||
link = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (setup_cgroup_environment())
|
||||
goto err;
|
||||
|
||||
cg2 = create_and_get_cgroup(CGROUP_PATH);
|
||||
|
||||
if (cg2 < 0)
|
||||
goto err;
|
||||
|
||||
if (bpf_map_update_elem(map_fd[0], &idx, &cg2, BPF_ANY)) {
|
||||
log_err("Adding target cgroup to map");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (join_cgroup(CGROUP_PATH))
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* The installed helper program catched the sync call, and should
|
||||
* write it to the map.
|
||||
*/
|
||||
|
||||
sync();
|
||||
bpf_map_lookup_elem(map_fd[1], &idx, &remote_pid);
|
||||
|
||||
if (local_pid != remote_pid) {
|
||||
fprintf(stderr,
|
||||
"BPF Helper didn't write correct PID to map, but: %d\n",
|
||||
remote_pid);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Verify the negative scenario; leave the cgroup */
|
||||
if (join_cgroup("/"))
|
||||
goto err;
|
||||
|
||||
remote_pid = 0;
|
||||
bpf_map_update_elem(map_fd[1], &idx, &remote_pid, BPF_ANY);
|
||||
|
||||
sync();
|
||||
bpf_map_lookup_elem(map_fd[1], &idx, &remote_pid);
|
||||
|
||||
if (local_pid == remote_pid) {
|
||||
fprintf(stderr, "BPF cgroup negative test did not work\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
|
||||
err:
|
||||
if (cg2 != -1)
|
||||
close(cg2);
|
||||
|
||||
cleanup_cgroup_environment();
|
||||
|
||||
cleanup:
|
||||
bpf_link__destroy(link);
|
||||
bpf_object__close(obj);
|
||||
return rc;
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/* Copyright (c) 2016 Facebook
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*/
|
||||
#include "vmlinux.h"
|
||||
#include <linux/version.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include <bpf/bpf_core_read.h>
|
||||
|
||||
SEC("kprobe/__set_task_comm")
|
||||
int prog(struct pt_regs *ctx)
|
||||
{
|
||||
struct signal_struct *signal;
|
||||
struct task_struct *tsk;
|
||||
char oldcomm[TASK_COMM_LEN] = {};
|
||||
char newcomm[TASK_COMM_LEN] = {};
|
||||
u16 oom_score_adj;
|
||||
u32 pid;
|
||||
|
||||
tsk = (void *)PT_REGS_PARM1_CORE(ctx);
|
||||
|
||||
pid = BPF_CORE_READ(tsk, pid);
|
||||
bpf_core_read_str(oldcomm, sizeof(oldcomm), &tsk->comm);
|
||||
bpf_core_read_str(newcomm, sizeof(newcomm),
|
||||
(void *)PT_REGS_PARM2(ctx));
|
||||
signal = BPF_CORE_READ(tsk, signal);
|
||||
oom_score_adj = BPF_CORE_READ(signal, oom_score_adj);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("kprobe/fib_table_lookup")
|
||||
int prog2(struct pt_regs *ctx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
u32 _version SEC("version") = LINUX_VERSION_CODE;
|
||||
@@ -1,17 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2018 Facebook */
|
||||
#include "vmlinux.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
SEC("raw_tracepoint/task_rename")
|
||||
int prog(struct bpf_raw_tracepoint_args *ctx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("raw_tracepoint/fib_table_lookup")
|
||||
int prog2(struct bpf_raw_tracepoint_args *ctx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
char _license[] SEC("license") = "GPL";
|
||||
@@ -1,23 +0,0 @@
|
||||
/* Copyright (c) 2016 Facebook
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*/
|
||||
#include "vmlinux.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
/* from /sys/kernel/tracing/events/task/task_rename/format */
|
||||
SEC("tracepoint/task/task_rename")
|
||||
int prog(struct trace_event_raw_task_rename *ctx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* from /sys/kernel/tracing/events/fib/fib_table_lookup/format */
|
||||
SEC("tracepoint/fib/fib_table_lookup")
|
||||
int prog2(struct trace_event_raw_fib_table_lookup *ctx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
char _license[] SEC("license") = "GPL";
|
||||
@@ -1,225 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2016 Facebook
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <sched.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
|
||||
#define MAX_CNT 1000000
|
||||
#define DUMMY_IP "127.0.0.1"
|
||||
#define DUMMY_PORT 80
|
||||
|
||||
static struct bpf_link *links[2];
|
||||
static struct bpf_object *obj;
|
||||
static int cnt;
|
||||
|
||||
static __u64 time_get_ns(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return ts.tv_sec * 1000000000ull + ts.tv_nsec;
|
||||
}
|
||||
|
||||
static void test_task_rename(int cpu)
|
||||
{
|
||||
char buf[] = "test\n";
|
||||
__u64 start_time;
|
||||
int i, fd;
|
||||
|
||||
fd = open("/proc/self/comm", O_WRONLY|O_TRUNC);
|
||||
if (fd < 0) {
|
||||
printf("couldn't open /proc\n");
|
||||
exit(1);
|
||||
}
|
||||
start_time = time_get_ns();
|
||||
for (i = 0; i < MAX_CNT; i++) {
|
||||
if (write(fd, buf, sizeof(buf)) < 0) {
|
||||
printf("task rename failed: %s\n", strerror(errno));
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
printf("task_rename:%d: %lld events per sec\n",
|
||||
cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time));
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void test_fib_table_lookup(int cpu)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
char buf[] = "test\n";
|
||||
__u64 start_time;
|
||||
int i, fd;
|
||||
|
||||
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (fd < 0) {
|
||||
printf("couldn't open socket\n");
|
||||
exit(1);
|
||||
}
|
||||
memset((char *)&addr, 0, sizeof(addr));
|
||||
addr.sin_addr.s_addr = inet_addr(DUMMY_IP);
|
||||
addr.sin_port = htons(DUMMY_PORT);
|
||||
addr.sin_family = AF_INET;
|
||||
start_time = time_get_ns();
|
||||
for (i = 0; i < MAX_CNT; i++) {
|
||||
if (sendto(fd, buf, strlen(buf), 0,
|
||||
(struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
printf("failed to start ping: %s\n", strerror(errno));
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
printf("fib_table_lookup:%d: %lld events per sec\n",
|
||||
cpu, MAX_CNT * 1000000000ll / (time_get_ns() - start_time));
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void loop(int cpu, int flags)
|
||||
{
|
||||
cpu_set_t cpuset;
|
||||
|
||||
CPU_ZERO(&cpuset);
|
||||
CPU_SET(cpu, &cpuset);
|
||||
sched_setaffinity(0, sizeof(cpuset), &cpuset);
|
||||
|
||||
if (flags & 1)
|
||||
test_task_rename(cpu);
|
||||
if (flags & 2)
|
||||
test_fib_table_lookup(cpu);
|
||||
}
|
||||
|
||||
static void run_perf_test(int tasks, int flags)
|
||||
{
|
||||
pid_t pid[tasks];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < tasks; i++) {
|
||||
pid[i] = fork();
|
||||
if (pid[i] == 0) {
|
||||
loop(i, flags);
|
||||
exit(0);
|
||||
} else if (pid[i] == -1) {
|
||||
printf("couldn't spawn #%d process\n", i);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < tasks; i++) {
|
||||
int status;
|
||||
|
||||
assert(waitpid(pid[i], &status, 0) == pid[i]);
|
||||
assert(status == 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int load_progs(char *filename)
|
||||
{
|
||||
struct bpf_program *prog;
|
||||
int err = 0;
|
||||
|
||||
obj = bpf_object__open_file(filename, NULL);
|
||||
err = libbpf_get_error(obj);
|
||||
if (err < 0) {
|
||||
fprintf(stderr, "ERROR: opening BPF object file failed\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* load BPF program */
|
||||
err = bpf_object__load(obj);
|
||||
if (err < 0) {
|
||||
fprintf(stderr, "ERROR: loading BPF object file failed\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
bpf_object__for_each_program(prog, obj) {
|
||||
links[cnt] = bpf_program__attach(prog);
|
||||
err = libbpf_get_error(links[cnt]);
|
||||
if (err < 0) {
|
||||
fprintf(stderr, "ERROR: bpf_program__attach failed\n");
|
||||
links[cnt] = NULL;
|
||||
return err;
|
||||
}
|
||||
cnt++;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void unload_progs(void)
|
||||
{
|
||||
while (cnt)
|
||||
bpf_link__destroy(links[--cnt]);
|
||||
|
||||
bpf_object__close(obj);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int num_cpu = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
int test_flags = ~0;
|
||||
char filename[256];
|
||||
int err = 0;
|
||||
|
||||
|
||||
if (argc > 1)
|
||||
test_flags = atoi(argv[1]) ? : test_flags;
|
||||
if (argc > 2)
|
||||
num_cpu = atoi(argv[2]) ? : num_cpu;
|
||||
|
||||
if (test_flags & 0x3) {
|
||||
printf("BASE\n");
|
||||
run_perf_test(num_cpu, test_flags);
|
||||
}
|
||||
|
||||
if (test_flags & 0xC) {
|
||||
snprintf(filename, sizeof(filename),
|
||||
"%s_kprobe.bpf.o", argv[0]);
|
||||
|
||||
printf("w/KPROBE\n");
|
||||
err = load_progs(filename);
|
||||
if (!err)
|
||||
run_perf_test(num_cpu, test_flags >> 2);
|
||||
|
||||
unload_progs();
|
||||
}
|
||||
|
||||
if (test_flags & 0x30) {
|
||||
snprintf(filename, sizeof(filename),
|
||||
"%s_tp.bpf.o", argv[0]);
|
||||
printf("w/TRACEPOINT\n");
|
||||
err = load_progs(filename);
|
||||
if (!err)
|
||||
run_perf_test(num_cpu, test_flags >> 4);
|
||||
|
||||
unload_progs();
|
||||
}
|
||||
|
||||
if (test_flags & 0xC0) {
|
||||
snprintf(filename, sizeof(filename),
|
||||
"%s_raw_tp.bpf.o", argv[0]);
|
||||
printf("w/RAW_TRACEPOINT\n");
|
||||
err = load_progs(filename);
|
||||
if (!err)
|
||||
run_perf_test(num_cpu, test_flags >> 6);
|
||||
|
||||
unload_progs();
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
rm -r tmpmnt
|
||||
rm -f testfile.img
|
||||
dd if=/dev/zero of=testfile.img bs=1M seek=1000 count=1
|
||||
DEVICE=$(losetup --show -f testfile.img)
|
||||
mkfs.btrfs -f $DEVICE
|
||||
mkdir tmpmnt
|
||||
./tracex7 $DEVICE
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
echo "SUCCESS!"
|
||||
else
|
||||
echo "FAILED!"
|
||||
fi
|
||||
losetup -d $DEVICE
|
||||
@@ -1,52 +0,0 @@
|
||||
/* Copyright (c) 2016 Sargun Dhillon <sargun@sargun.me>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of version 2 of the GNU General Public
|
||||
* License as published by the Free Software Foundation.
|
||||
*/
|
||||
#include "vmlinux.h"
|
||||
#include <string.h>
|
||||
#include <linux/version.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include <bpf/bpf_core_read.h>
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_HASH);
|
||||
__type(key, struct sockaddr_in);
|
||||
__type(value, struct sockaddr_in);
|
||||
__uint(max_entries, 256);
|
||||
} dnat_map SEC(".maps");
|
||||
|
||||
/* kprobe is NOT a stable ABI
|
||||
* kernel functions can be removed, renamed or completely change semantics.
|
||||
* Number of arguments and their positions can change, etc.
|
||||
* In such case this bpf+kprobe example will no longer be meaningful
|
||||
*
|
||||
* This example sits on a syscall, and the syscall ABI is relatively stable
|
||||
* of course, across platforms, and over time, the ABI may change.
|
||||
*/
|
||||
SEC("ksyscall/connect")
|
||||
int BPF_KSYSCALL(bpf_prog1, int fd, struct sockaddr_in *uservaddr,
|
||||
int addrlen)
|
||||
{
|
||||
struct sockaddr_in new_addr, orig_addr = {};
|
||||
struct sockaddr_in *mapped_addr;
|
||||
|
||||
if (addrlen > sizeof(orig_addr))
|
||||
return 0;
|
||||
|
||||
if (bpf_probe_read_user(&orig_addr, sizeof(orig_addr), uservaddr) != 0)
|
||||
return 0;
|
||||
|
||||
mapped_addr = bpf_map_lookup_elem(&dnat_map, &orig_addr);
|
||||
if (mapped_addr != NULL) {
|
||||
memcpy(&new_addr, mapped_addr, sizeof(new_addr));
|
||||
bpf_probe_write_user(uservaddr, &new_addr,
|
||||
sizeof(new_addr));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
u32 _version SEC("version") = LINUX_VERSION_CODE;
|
||||
@@ -1,108 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
int main(int ac, char **argv)
|
||||
{
|
||||
struct sockaddr_in *serv_addr_in, *mapped_addr_in, *tmp_addr_in;
|
||||
struct sockaddr serv_addr, mapped_addr, tmp_addr;
|
||||
int serverfd, serverconnfd, clientfd, map_fd;
|
||||
struct bpf_link *link = NULL;
|
||||
struct bpf_program *prog;
|
||||
struct bpf_object *obj;
|
||||
socklen_t sockaddr_len;
|
||||
char filename[256];
|
||||
char *ip;
|
||||
|
||||
serv_addr_in = (struct sockaddr_in *)&serv_addr;
|
||||
mapped_addr_in = (struct sockaddr_in *)&mapped_addr;
|
||||
tmp_addr_in = (struct sockaddr_in *)&tmp_addr;
|
||||
|
||||
snprintf(filename, sizeof(filename), "%s.bpf.o", argv[0]);
|
||||
obj = bpf_object__open_file(filename, NULL);
|
||||
if (libbpf_get_error(obj)) {
|
||||
fprintf(stderr, "ERROR: opening BPF object file failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
prog = bpf_object__find_program_by_name(obj, "bpf_prog1");
|
||||
if (libbpf_get_error(prog)) {
|
||||
fprintf(stderr, "ERROR: finding a prog in obj file failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* load BPF program */
|
||||
if (bpf_object__load(obj)) {
|
||||
fprintf(stderr, "ERROR: loading BPF object file failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
map_fd = bpf_object__find_map_fd_by_name(obj, "dnat_map");
|
||||
if (map_fd < 0) {
|
||||
fprintf(stderr, "ERROR: finding a map in obj file failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
link = bpf_program__attach(prog);
|
||||
if (libbpf_get_error(link)) {
|
||||
fprintf(stderr, "ERROR: bpf_program__attach failed\n");
|
||||
link = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
assert((serverfd = socket(AF_INET, SOCK_STREAM, 0)) > 0);
|
||||
assert((clientfd = socket(AF_INET, SOCK_STREAM, 0)) > 0);
|
||||
|
||||
/* Bind server to ephemeral port on lo */
|
||||
memset(&serv_addr, 0, sizeof(serv_addr));
|
||||
serv_addr_in->sin_family = AF_INET;
|
||||
serv_addr_in->sin_port = 0;
|
||||
serv_addr_in->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
|
||||
assert(bind(serverfd, &serv_addr, sizeof(serv_addr)) == 0);
|
||||
|
||||
sockaddr_len = sizeof(serv_addr);
|
||||
assert(getsockname(serverfd, &serv_addr, &sockaddr_len) == 0);
|
||||
ip = inet_ntoa(serv_addr_in->sin_addr);
|
||||
printf("Server bound to: %s:%d\n", ip, ntohs(serv_addr_in->sin_port));
|
||||
|
||||
memset(&mapped_addr, 0, sizeof(mapped_addr));
|
||||
mapped_addr_in->sin_family = AF_INET;
|
||||
mapped_addr_in->sin_port = htons(5555);
|
||||
mapped_addr_in->sin_addr.s_addr = inet_addr("255.255.255.255");
|
||||
|
||||
assert(!bpf_map_update_elem(map_fd, &mapped_addr, &serv_addr, BPF_ANY));
|
||||
|
||||
assert(listen(serverfd, 5) == 0);
|
||||
|
||||
ip = inet_ntoa(mapped_addr_in->sin_addr);
|
||||
printf("Client connecting to: %s:%d\n",
|
||||
ip, ntohs(mapped_addr_in->sin_port));
|
||||
assert(connect(clientfd, &mapped_addr, sizeof(mapped_addr)) == 0);
|
||||
|
||||
sockaddr_len = sizeof(tmp_addr);
|
||||
ip = inet_ntoa(tmp_addr_in->sin_addr);
|
||||
assert((serverconnfd = accept(serverfd, &tmp_addr, &sockaddr_len)) > 0);
|
||||
printf("Server received connection from: %s:%d\n",
|
||||
ip, ntohs(tmp_addr_in->sin_port));
|
||||
|
||||
sockaddr_len = sizeof(tmp_addr);
|
||||
assert(getpeername(clientfd, &tmp_addr, &sockaddr_len) == 0);
|
||||
ip = inet_ntoa(tmp_addr_in->sin_addr);
|
||||
printf("Client's peer address: %s:%d\n",
|
||||
ip, ntohs(tmp_addr_in->sin_port));
|
||||
|
||||
/* Is the server's getsockname = the socket getpeername */
|
||||
assert(memcmp(&serv_addr, &tmp_addr, sizeof(struct sockaddr_in)) == 0);
|
||||
|
||||
cleanup:
|
||||
bpf_link__destroy(link);
|
||||
bpf_object__close(obj);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#include "vmlinux.h"
|
||||
#include <linux/version.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
SEC("kprobe/open_ctree")
|
||||
int bpf_prog1(struct pt_regs *ctx)
|
||||
{
|
||||
unsigned long rc = -12;
|
||||
|
||||
bpf_override_return(ctx, rc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
u32 _version SEC("version") = LINUX_VERSION_CODE;
|
||||
@@ -1,56 +0,0 @@
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <bpf/libbpf.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct bpf_link *link = NULL;
|
||||
struct bpf_program *prog;
|
||||
struct bpf_object *obj;
|
||||
char filename[256];
|
||||
char command[256];
|
||||
int ret = 0;
|
||||
FILE *f;
|
||||
|
||||
if (!argv[1]) {
|
||||
fprintf(stderr, "ERROR: Run with the btrfs device argument!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
snprintf(filename, sizeof(filename), "%s.bpf.o", argv[0]);
|
||||
obj = bpf_object__open_file(filename, NULL);
|
||||
if (libbpf_get_error(obj)) {
|
||||
fprintf(stderr, "ERROR: opening BPF object file failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
prog = bpf_object__find_program_by_name(obj, "bpf_prog1");
|
||||
if (!prog) {
|
||||
fprintf(stderr, "ERROR: finding a prog in obj file failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* load BPF program */
|
||||
if (bpf_object__load(obj)) {
|
||||
fprintf(stderr, "ERROR: loading BPF object file failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
link = bpf_program__attach(prog);
|
||||
if (libbpf_get_error(link)) {
|
||||
fprintf(stderr, "ERROR: bpf_program__attach failed\n");
|
||||
link = NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
snprintf(command, 256, "mount %s tmpmnt/", argv[1]);
|
||||
f = popen(command, "r");
|
||||
ret = pclose(f);
|
||||
|
||||
cleanup:
|
||||
bpf_link__destroy(link);
|
||||
bpf_object__close(obj);
|
||||
return ret ? 0 : 1;
|
||||
}
|
||||
@@ -32,7 +32,7 @@ SEC("xdp_mark")
|
||||
int _xdp_mark(struct xdp_md *ctx)
|
||||
{
|
||||
struct meta_info *meta;
|
||||
void *data, *data_end;
|
||||
void *data;
|
||||
int ret;
|
||||
|
||||
/* Reserve space in-front of data pointer for our meta info.
|
||||
|
||||
@@ -57,6 +57,7 @@ static __always_inline void swap_mac(void *data, struct ethhdr *orig_eth)
|
||||
|
||||
static __always_inline __u16 csum_fold_helper(__u32 csum)
|
||||
{
|
||||
csum = (csum & 0xffff) + (csum >> 16);
|
||||
return ~((csum & 0xffff) + (csum >> 16));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user