18c2ecf20Sopenharmony_ci/* eBPF example program: 28c2ecf20Sopenharmony_ci * 38c2ecf20Sopenharmony_ci * - Loads eBPF program 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * The eBPF program sets the sk_bound_dev_if index in new AF_INET{6} 68c2ecf20Sopenharmony_ci * sockets opened by processes in the cgroup. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * - Attaches the new program to a cgroup using BPF_PROG_ATTACH 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define _GNU_SOURCE 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <stdio.h> 148c2ecf20Sopenharmony_ci#include <stdlib.h> 158c2ecf20Sopenharmony_ci#include <stddef.h> 168c2ecf20Sopenharmony_ci#include <string.h> 178c2ecf20Sopenharmony_ci#include <unistd.h> 188c2ecf20Sopenharmony_ci#include <assert.h> 198c2ecf20Sopenharmony_ci#include <errno.h> 208c2ecf20Sopenharmony_ci#include <fcntl.h> 218c2ecf20Sopenharmony_ci#include <net/if.h> 228c2ecf20Sopenharmony_ci#include <inttypes.h> 238c2ecf20Sopenharmony_ci#include <linux/bpf.h> 248c2ecf20Sopenharmony_ci#include <bpf/bpf.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "bpf_insn.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cichar bpf_log_buf[BPF_LOG_BUF_SIZE]; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic int prog_load(__u32 idx, __u32 mark, __u32 prio) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci /* save pointer to context */ 338c2ecf20Sopenharmony_ci struct bpf_insn prog_start[] = { 348c2ecf20Sopenharmony_ci BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), 358c2ecf20Sopenharmony_ci }; 368c2ecf20Sopenharmony_ci struct bpf_insn prog_end[] = { 378c2ecf20Sopenharmony_ci BPF_MOV64_IMM(BPF_REG_0, 1), /* r0 = verdict */ 388c2ecf20Sopenharmony_ci BPF_EXIT_INSN(), 398c2ecf20Sopenharmony_ci }; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci /* set sk_bound_dev_if on socket */ 428c2ecf20Sopenharmony_ci struct bpf_insn prog_dev[] = { 438c2ecf20Sopenharmony_ci BPF_MOV64_IMM(BPF_REG_3, idx), 448c2ecf20Sopenharmony_ci BPF_MOV64_IMM(BPF_REG_2, offsetof(struct bpf_sock, bound_dev_if)), 458c2ecf20Sopenharmony_ci BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, offsetof(struct bpf_sock, bound_dev_if)), 468c2ecf20Sopenharmony_ci }; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci /* set mark on socket */ 498c2ecf20Sopenharmony_ci struct bpf_insn prog_mark[] = { 508c2ecf20Sopenharmony_ci /* get uid of process */ 518c2ecf20Sopenharmony_ci BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, 528c2ecf20Sopenharmony_ci BPF_FUNC_get_current_uid_gid), 538c2ecf20Sopenharmony_ci BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 0xffffffff), 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci /* if uid is 0, use given mark, else use the uid as the mark */ 568c2ecf20Sopenharmony_ci BPF_MOV64_REG(BPF_REG_3, BPF_REG_0), 578c2ecf20Sopenharmony_ci BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), 588c2ecf20Sopenharmony_ci BPF_MOV64_IMM(BPF_REG_3, mark), 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci /* set the mark on the new socket */ 618c2ecf20Sopenharmony_ci BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), 628c2ecf20Sopenharmony_ci BPF_MOV64_IMM(BPF_REG_2, offsetof(struct bpf_sock, mark)), 638c2ecf20Sopenharmony_ci BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, offsetof(struct bpf_sock, mark)), 648c2ecf20Sopenharmony_ci }; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* set priority on socket */ 678c2ecf20Sopenharmony_ci struct bpf_insn prog_prio[] = { 688c2ecf20Sopenharmony_ci BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), 698c2ecf20Sopenharmony_ci BPF_MOV64_IMM(BPF_REG_3, prio), 708c2ecf20Sopenharmony_ci BPF_MOV64_IMM(BPF_REG_2, offsetof(struct bpf_sock, priority)), 718c2ecf20Sopenharmony_ci BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, offsetof(struct bpf_sock, priority)), 728c2ecf20Sopenharmony_ci }; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci struct bpf_insn *prog; 758c2ecf20Sopenharmony_ci size_t insns_cnt; 768c2ecf20Sopenharmony_ci void *p; 778c2ecf20Sopenharmony_ci int ret; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci insns_cnt = sizeof(prog_start) + sizeof(prog_end); 808c2ecf20Sopenharmony_ci if (idx) 818c2ecf20Sopenharmony_ci insns_cnt += sizeof(prog_dev); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (mark) 848c2ecf20Sopenharmony_ci insns_cnt += sizeof(prog_mark); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (prio) 878c2ecf20Sopenharmony_ci insns_cnt += sizeof(prog_prio); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci p = prog = malloc(insns_cnt); 908c2ecf20Sopenharmony_ci if (!prog) { 918c2ecf20Sopenharmony_ci fprintf(stderr, "Failed to allocate memory for instructions\n"); 928c2ecf20Sopenharmony_ci return EXIT_FAILURE; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci memcpy(p, prog_start, sizeof(prog_start)); 968c2ecf20Sopenharmony_ci p += sizeof(prog_start); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (idx) { 998c2ecf20Sopenharmony_ci memcpy(p, prog_dev, sizeof(prog_dev)); 1008c2ecf20Sopenharmony_ci p += sizeof(prog_dev); 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (mark) { 1048c2ecf20Sopenharmony_ci memcpy(p, prog_mark, sizeof(prog_mark)); 1058c2ecf20Sopenharmony_ci p += sizeof(prog_mark); 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (prio) { 1098c2ecf20Sopenharmony_ci memcpy(p, prog_prio, sizeof(prog_prio)); 1108c2ecf20Sopenharmony_ci p += sizeof(prog_prio); 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci memcpy(p, prog_end, sizeof(prog_end)); 1148c2ecf20Sopenharmony_ci p += sizeof(prog_end); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci insns_cnt /= sizeof(struct bpf_insn); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SOCK, prog, insns_cnt, 1198c2ecf20Sopenharmony_ci "GPL", 0, bpf_log_buf, BPF_LOG_BUF_SIZE); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci free(prog); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return ret; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic int get_bind_to_device(int sd, char *name, size_t len) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci socklen_t optlen = len; 1298c2ecf20Sopenharmony_ci int rc; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci name[0] = '\0'; 1328c2ecf20Sopenharmony_ci rc = getsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, name, &optlen); 1338c2ecf20Sopenharmony_ci if (rc < 0) 1348c2ecf20Sopenharmony_ci perror("setsockopt(SO_BINDTODEVICE)"); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return rc; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic unsigned int get_somark(int sd) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci unsigned int mark = 0; 1428c2ecf20Sopenharmony_ci socklen_t optlen = sizeof(mark); 1438c2ecf20Sopenharmony_ci int rc; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci rc = getsockopt(sd, SOL_SOCKET, SO_MARK, &mark, &optlen); 1468c2ecf20Sopenharmony_ci if (rc < 0) 1478c2ecf20Sopenharmony_ci perror("getsockopt(SO_MARK)"); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return mark; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic unsigned int get_priority(int sd) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci unsigned int prio = 0; 1558c2ecf20Sopenharmony_ci socklen_t optlen = sizeof(prio); 1568c2ecf20Sopenharmony_ci int rc; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci rc = getsockopt(sd, SOL_SOCKET, SO_PRIORITY, &prio, &optlen); 1598c2ecf20Sopenharmony_ci if (rc < 0) 1608c2ecf20Sopenharmony_ci perror("getsockopt(SO_PRIORITY)"); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return prio; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic int show_sockopts(int family) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci unsigned int mark, prio; 1688c2ecf20Sopenharmony_ci char name[16]; 1698c2ecf20Sopenharmony_ci int sd; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci sd = socket(family, SOCK_DGRAM, 17); 1728c2ecf20Sopenharmony_ci if (sd < 0) { 1738c2ecf20Sopenharmony_ci perror("socket"); 1748c2ecf20Sopenharmony_ci return 1; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (get_bind_to_device(sd, name, sizeof(name)) < 0) 1788c2ecf20Sopenharmony_ci return 1; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci mark = get_somark(sd); 1818c2ecf20Sopenharmony_ci prio = get_priority(sd); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci close(sd); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci printf("sd %d: dev %s, mark %u, priority %u\n", sd, name, mark, prio); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic int usage(const char *argv0) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci printf("Usage:\n"); 1938c2ecf20Sopenharmony_ci printf(" Attach a program\n"); 1948c2ecf20Sopenharmony_ci printf(" %s -b bind-to-dev -m mark -p prio cg-path\n", argv0); 1958c2ecf20Sopenharmony_ci printf("\n"); 1968c2ecf20Sopenharmony_ci printf(" Detach a program\n"); 1978c2ecf20Sopenharmony_ci printf(" %s -d cg-path\n", argv0); 1988c2ecf20Sopenharmony_ci printf("\n"); 1998c2ecf20Sopenharmony_ci printf(" Show inherited socket settings (mark, priority, and device)\n"); 2008c2ecf20Sopenharmony_ci printf(" %s [-6]\n", argv0); 2018c2ecf20Sopenharmony_ci return EXIT_FAILURE; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ciint main(int argc, char **argv) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci __u32 idx = 0, mark = 0, prio = 0; 2078c2ecf20Sopenharmony_ci const char *cgrp_path = NULL; 2088c2ecf20Sopenharmony_ci int cg_fd, prog_fd, ret; 2098c2ecf20Sopenharmony_ci int family = PF_INET; 2108c2ecf20Sopenharmony_ci int do_attach = 1; 2118c2ecf20Sopenharmony_ci int rc; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci while ((rc = getopt(argc, argv, "db:m:p:6")) != -1) { 2148c2ecf20Sopenharmony_ci switch (rc) { 2158c2ecf20Sopenharmony_ci case 'd': 2168c2ecf20Sopenharmony_ci do_attach = 0; 2178c2ecf20Sopenharmony_ci break; 2188c2ecf20Sopenharmony_ci case 'b': 2198c2ecf20Sopenharmony_ci idx = if_nametoindex(optarg); 2208c2ecf20Sopenharmony_ci if (!idx) { 2218c2ecf20Sopenharmony_ci idx = strtoumax(optarg, NULL, 0); 2228c2ecf20Sopenharmony_ci if (!idx) { 2238c2ecf20Sopenharmony_ci printf("Invalid device name\n"); 2248c2ecf20Sopenharmony_ci return EXIT_FAILURE; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci break; 2288c2ecf20Sopenharmony_ci case 'm': 2298c2ecf20Sopenharmony_ci mark = strtoumax(optarg, NULL, 0); 2308c2ecf20Sopenharmony_ci break; 2318c2ecf20Sopenharmony_ci case 'p': 2328c2ecf20Sopenharmony_ci prio = strtoumax(optarg, NULL, 0); 2338c2ecf20Sopenharmony_ci break; 2348c2ecf20Sopenharmony_ci case '6': 2358c2ecf20Sopenharmony_ci family = PF_INET6; 2368c2ecf20Sopenharmony_ci break; 2378c2ecf20Sopenharmony_ci default: 2388c2ecf20Sopenharmony_ci return usage(argv[0]); 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (optind == argc) 2438c2ecf20Sopenharmony_ci return show_sockopts(family); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci cgrp_path = argv[optind]; 2468c2ecf20Sopenharmony_ci if (!cgrp_path) { 2478c2ecf20Sopenharmony_ci fprintf(stderr, "cgroup path not given\n"); 2488c2ecf20Sopenharmony_ci return EXIT_FAILURE; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (do_attach && !idx && !mark && !prio) { 2528c2ecf20Sopenharmony_ci fprintf(stderr, 2538c2ecf20Sopenharmony_ci "One of device, mark or priority must be given\n"); 2548c2ecf20Sopenharmony_ci return EXIT_FAILURE; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci cg_fd = open(cgrp_path, O_DIRECTORY | O_RDONLY); 2588c2ecf20Sopenharmony_ci if (cg_fd < 0) { 2598c2ecf20Sopenharmony_ci printf("Failed to open cgroup path: '%s'\n", strerror(errno)); 2608c2ecf20Sopenharmony_ci return EXIT_FAILURE; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci if (do_attach) { 2648c2ecf20Sopenharmony_ci prog_fd = prog_load(idx, mark, prio); 2658c2ecf20Sopenharmony_ci if (prog_fd < 0) { 2668c2ecf20Sopenharmony_ci printf("Failed to load prog: '%s'\n", strerror(errno)); 2678c2ecf20Sopenharmony_ci printf("Output from kernel verifier:\n%s\n-------\n", 2688c2ecf20Sopenharmony_ci bpf_log_buf); 2698c2ecf20Sopenharmony_ci return EXIT_FAILURE; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci ret = bpf_prog_attach(prog_fd, cg_fd, 2738c2ecf20Sopenharmony_ci BPF_CGROUP_INET_SOCK_CREATE, 0); 2748c2ecf20Sopenharmony_ci if (ret < 0) { 2758c2ecf20Sopenharmony_ci printf("Failed to attach prog to cgroup: '%s'\n", 2768c2ecf20Sopenharmony_ci strerror(errno)); 2778c2ecf20Sopenharmony_ci return EXIT_FAILURE; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci } else { 2808c2ecf20Sopenharmony_ci ret = bpf_prog_detach(cg_fd, BPF_CGROUP_INET_SOCK_CREATE); 2818c2ecf20Sopenharmony_ci if (ret < 0) { 2828c2ecf20Sopenharmony_ci printf("Failed to detach prog from cgroup: '%s'\n", 2838c2ecf20Sopenharmony_ci strerror(errno)); 2848c2ecf20Sopenharmony_ci return EXIT_FAILURE; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci close(cg_fd); 2898c2ecf20Sopenharmony_ci return EXIT_SUCCESS; 2908c2ecf20Sopenharmony_ci} 291