^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) /* eBPF example program:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * - Creates arraymap in kernel with 4 bytes keys and 8 byte values
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * - Loads eBPF program
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * The eBPF program accesses the map passed in to store two pieces of
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * information. The number of invocations of the program, which maps
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * to the number of packets received, is stored to key 0. Key 1 is
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * incremented on each iteration by the number of bytes stored in
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) * the skb.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) * - Attaches the new program to a cgroup using BPF_PROG_ATTACH
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) * - Every second, reads map[0] and map[1] to see how many bytes and
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) * packets were seen on any socket of tasks in the given cgroup.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #define _GNU_SOURCE
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <stdio.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #include <stdlib.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #include <stddef.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #include <string.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #include <unistd.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #include <assert.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #include <errno.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #include <fcntl.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) #include <linux/bpf.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) #include <bpf/bpf.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) #include "bpf_insn.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) enum {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) MAP_KEY_PACKETS,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) MAP_KEY_BYTES,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) char bpf_log_buf[BPF_LOG_BUF_SIZE];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) static int prog_load(int map_fd, int verdict)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) struct bpf_insn prog[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), /* save r6 so it's not clobbered by BPF_CALL */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) /* Count packets */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) BPF_MOV64_IMM(BPF_REG_0, MAP_KEY_PACKETS), /* r0 = 0 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) BPF_LD_MAP_FD(BPF_REG_1, map_fd), /* load map fd to r1 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) BPF_MOV64_IMM(BPF_REG_1, 1), /* r1 = 1 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* xadd r0 += r1 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) /* Count bytes */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) BPF_MOV64_IMM(BPF_REG_0, MAP_KEY_BYTES), /* r0 = 1 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) BPF_LD_MAP_FD(BPF_REG_1, map_fd),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_6, offsetof(struct __sk_buff, len)), /* r1 = skb->len */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* xadd r0 += r1 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) BPF_EXIT_INSN(),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) return bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) prog, insns_cnt, "GPL", 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) bpf_log_buf, BPF_LOG_BUF_SIZE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) static int usage(const char *argv0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) printf("Usage: %s [-d] [-D] <cg-path> <egress|ingress>\n", argv0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) printf(" -d Drop Traffic\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) printf(" -D Detach filter, and exit\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) return EXIT_FAILURE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) static int attach_filter(int cg_fd, int type, int verdict)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) int prog_fd, map_fd, ret, key;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) long long pkt_cnt, byte_cnt;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) sizeof(key), sizeof(byte_cnt),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) 256, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) if (map_fd < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) printf("Failed to create map: '%s'\n", strerror(errno));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) return EXIT_FAILURE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) prog_fd = prog_load(map_fd, verdict);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) printf("Output from kernel verifier:\n%s\n-------\n", bpf_log_buf);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) if (prog_fd < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) printf("Failed to load prog: '%s'\n", strerror(errno));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) return EXIT_FAILURE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) ret = bpf_prog_attach(prog_fd, cg_fd, type, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) if (ret < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) printf("Failed to attach prog to cgroup: '%s'\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) strerror(errno));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) return EXIT_FAILURE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) while (1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) key = MAP_KEY_PACKETS;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) assert(bpf_map_lookup_elem(map_fd, &key, &pkt_cnt) == 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) key = MAP_KEY_BYTES;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) assert(bpf_map_lookup_elem(map_fd, &key, &byte_cnt) == 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) printf("cgroup received %lld packets, %lld bytes\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) pkt_cnt, byte_cnt);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) sleep(1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) return EXIT_SUCCESS;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) int main(int argc, char **argv)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) int detach_only = 0, verdict = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) enum bpf_attach_type type;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) int opt, cg_fd, ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) while ((opt = getopt(argc, argv, "Dd")) != -1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) switch (opt) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) case 'd':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) verdict = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) case 'D':
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) detach_only = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) default:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) return usage(argv[0]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) if (argc - optind < 2)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) return usage(argv[0]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) if (strcmp(argv[optind + 1], "ingress") == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) type = BPF_CGROUP_INET_INGRESS;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) else if (strcmp(argv[optind + 1], "egress") == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) type = BPF_CGROUP_INET_EGRESS;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) return usage(argv[0]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) cg_fd = open(argv[optind], O_DIRECTORY | O_RDONLY);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) if (cg_fd < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) printf("Failed to open cgroup path: '%s'\n", strerror(errno));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) return EXIT_FAILURE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) if (detach_only) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) ret = bpf_prog_detach(cg_fd, type);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) printf("bpf_prog_detach() returned '%s' (%d)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) strerror(errno), errno);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) } else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) ret = attach_filter(cg_fd, type, verdict);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) }