]>
Commit | Line | Data |
---|---|---|
ad2805dc DA |
1 | /* eBPF example program: |
2 | * | |
3 | * - Loads eBPF program | |
4 | * | |
5 | * The eBPF program sets the sk_bound_dev_if index in new AF_INET{6} | |
6 | * sockets opened by processes in the cgroup. | |
7 | * | |
8 | * - Attaches the new program to a cgroup using BPF_PROG_ATTACH | |
9 | */ | |
10 | ||
11 | #define _GNU_SOURCE | |
12 | ||
13 | #include <stdio.h> | |
14 | #include <stdlib.h> | |
15 | #include <stddef.h> | |
16 | #include <string.h> | |
17 | #include <unistd.h> | |
18 | #include <assert.h> | |
19 | #include <errno.h> | |
20 | #include <fcntl.h> | |
21 | #include <net/if.h> | |
fa38aa17 | 22 | #include <inttypes.h> |
ad2805dc DA |
23 | #include <linux/bpf.h> |
24 | ||
25 | #include "libbpf.h" | |
26 | ||
d40fc181 JS |
27 | char bpf_log_buf[BPF_LOG_BUF_SIZE]; |
28 | ||
fa38aa17 | 29 | static int prog_load(__u32 idx, __u32 mark, __u32 prio) |
ad2805dc | 30 | { |
fa38aa17 DA |
31 | /* save pointer to context */ |
32 | struct bpf_insn prog_start[] = { | |
ad2805dc | 33 | BPF_MOV64_REG(BPF_REG_6, BPF_REG_1), |
fa38aa17 DA |
34 | }; |
35 | struct bpf_insn prog_end[] = { | |
36 | BPF_MOV64_IMM(BPF_REG_0, 1), /* r0 = verdict */ | |
37 | BPF_EXIT_INSN(), | |
38 | }; | |
39 | ||
40 | /* set sk_bound_dev_if on socket */ | |
41 | struct bpf_insn prog_dev[] = { | |
ad2805dc DA |
42 | BPF_MOV64_IMM(BPF_REG_3, idx), |
43 | BPF_MOV64_IMM(BPF_REG_2, offsetof(struct bpf_sock, bound_dev_if)), | |
44 | BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, offsetof(struct bpf_sock, bound_dev_if)), | |
ad2805dc DA |
45 | }; |
46 | ||
fa38aa17 DA |
47 | /* set mark on socket */ |
48 | struct bpf_insn prog_mark[] = { | |
49 | BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), | |
50 | BPF_MOV64_IMM(BPF_REG_3, mark), | |
51 | BPF_MOV64_IMM(BPF_REG_2, offsetof(struct bpf_sock, mark)), | |
52 | BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, offsetof(struct bpf_sock, mark)), | |
53 | }; | |
54 | ||
55 | /* set priority on socket */ | |
56 | struct bpf_insn prog_prio[] = { | |
57 | BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), | |
58 | BPF_MOV64_IMM(BPF_REG_3, prio), | |
59 | BPF_MOV64_IMM(BPF_REG_2, offsetof(struct bpf_sock, priority)), | |
60 | BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, offsetof(struct bpf_sock, priority)), | |
61 | }; | |
62 | ||
63 | struct bpf_insn *prog; | |
64 | size_t insns_cnt; | |
65 | void *p; | |
66 | int ret; | |
67 | ||
68 | insns_cnt = sizeof(prog_start) + sizeof(prog_end); | |
69 | if (idx) | |
70 | insns_cnt += sizeof(prog_dev); | |
71 | ||
72 | if (mark) | |
73 | insns_cnt += sizeof(prog_mark); | |
74 | ||
75 | if (prio) | |
76 | insns_cnt += sizeof(prog_prio); | |
77 | ||
78 | p = prog = malloc(insns_cnt); | |
79 | if (!prog) { | |
80 | fprintf(stderr, "Failed to allocate memory for instructions\n"); | |
81 | return EXIT_FAILURE; | |
82 | } | |
83 | ||
84 | memcpy(p, prog_start, sizeof(prog_start)); | |
85 | p += sizeof(prog_start); | |
86 | ||
87 | if (idx) { | |
88 | memcpy(p, prog_dev, sizeof(prog_dev)); | |
89 | p += sizeof(prog_dev); | |
90 | } | |
91 | ||
92 | if (mark) { | |
93 | memcpy(p, prog_mark, sizeof(prog_mark)); | |
94 | p += sizeof(prog_mark); | |
95 | } | |
96 | ||
97 | if (prio) { | |
98 | memcpy(p, prog_prio, sizeof(prog_prio)); | |
99 | p += sizeof(prog_prio); | |
100 | } | |
101 | ||
102 | memcpy(p, prog_end, sizeof(prog_end)); | |
103 | p += sizeof(prog_end); | |
104 | ||
105 | insns_cnt /= sizeof(struct bpf_insn); | |
106 | ||
107 | ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SOCK, prog, insns_cnt, | |
d40fc181 | 108 | "GPL", 0, bpf_log_buf, BPF_LOG_BUF_SIZE); |
fa38aa17 DA |
109 | |
110 | free(prog); | |
111 | ||
112 | return ret; | |
ad2805dc DA |
113 | } |
114 | ||
115 | static int usage(const char *argv0) | |
116 | { | |
fa38aa17 | 117 | printf("Usage: %s -b bind-to-dev -m mark -p prio cg-path\n", argv0); |
ad2805dc DA |
118 | return EXIT_FAILURE; |
119 | } | |
120 | ||
121 | int main(int argc, char **argv) | |
122 | { | |
fa38aa17 DA |
123 | __u32 idx = 0, mark = 0, prio = 0; |
124 | const char *cgrp_path = NULL; | |
ad2805dc | 125 | int cg_fd, prog_fd, ret; |
fa38aa17 DA |
126 | int rc; |
127 | ||
128 | while ((rc = getopt(argc, argv, "b:m:p:")) != -1) { | |
129 | switch (rc) { | |
130 | case 'b': | |
131 | idx = if_nametoindex(optarg); | |
132 | if (!idx) { | |
133 | idx = strtoumax(optarg, NULL, 0); | |
134 | if (!idx) { | |
135 | printf("Invalid device name\n"); | |
136 | return EXIT_FAILURE; | |
137 | } | |
138 | } | |
139 | break; | |
140 | case 'm': | |
141 | mark = strtoumax(optarg, NULL, 0); | |
142 | break; | |
143 | case 'p': | |
144 | prio = strtoumax(optarg, NULL, 0); | |
145 | break; | |
146 | default: | |
147 | return usage(argv[0]); | |
148 | } | |
149 | } | |
ad2805dc | 150 | |
fa38aa17 | 151 | if (optind == argc) |
ad2805dc DA |
152 | return usage(argv[0]); |
153 | ||
fa38aa17 DA |
154 | cgrp_path = argv[optind]; |
155 | if (!cgrp_path) { | |
156 | fprintf(stderr, "cgroup path not given\n"); | |
ad2805dc DA |
157 | return EXIT_FAILURE; |
158 | } | |
159 | ||
fa38aa17 DA |
160 | if (!idx && !mark && !prio) { |
161 | fprintf(stderr, | |
162 | "One of device, mark or priority must be given\n"); | |
163 | return EXIT_FAILURE; | |
164 | } | |
165 | ||
166 | cg_fd = open(cgrp_path, O_DIRECTORY | O_RDONLY); | |
ad2805dc DA |
167 | if (cg_fd < 0) { |
168 | printf("Failed to open cgroup path: '%s'\n", strerror(errno)); | |
169 | return EXIT_FAILURE; | |
170 | } | |
171 | ||
fa38aa17 | 172 | prog_fd = prog_load(idx, mark, prio); |
ad2805dc DA |
173 | if (prog_fd < 0) { |
174 | printf("Failed to load prog: '%s'\n", strerror(errno)); | |
fa38aa17 DA |
175 | printf("Output from kernel verifier:\n%s\n-------\n", |
176 | bpf_log_buf); | |
ad2805dc DA |
177 | return EXIT_FAILURE; |
178 | } | |
179 | ||
7f677633 | 180 | ret = bpf_prog_attach(prog_fd, cg_fd, BPF_CGROUP_INET_SOCK_CREATE, 0); |
ad2805dc DA |
181 | if (ret < 0) { |
182 | printf("Failed to attach prog to cgroup: '%s'\n", | |
183 | strerror(errno)); | |
184 | return EXIT_FAILURE; | |
185 | } | |
186 | ||
187 | return EXIT_SUCCESS; | |
188 | } |