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 | ||
f776d460 DA |
115 | static int get_bind_to_device(int sd, char *name, size_t len) |
116 | { | |
117 | socklen_t optlen = len; | |
118 | int rc; | |
119 | ||
120 | name[0] = '\0'; | |
121 | rc = getsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, name, &optlen); | |
122 | if (rc < 0) | |
123 | perror("setsockopt(SO_BINDTODEVICE)"); | |
124 | ||
125 | return rc; | |
126 | } | |
127 | ||
128 | static unsigned int get_somark(int sd) | |
129 | { | |
130 | unsigned int mark = 0; | |
131 | socklen_t optlen = sizeof(mark); | |
132 | int rc; | |
133 | ||
134 | rc = getsockopt(sd, SOL_SOCKET, SO_MARK, &mark, &optlen); | |
135 | if (rc < 0) | |
136 | perror("getsockopt(SO_MARK)"); | |
137 | ||
138 | return mark; | |
139 | } | |
140 | ||
141 | static unsigned int get_priority(int sd) | |
142 | { | |
143 | unsigned int prio = 0; | |
144 | socklen_t optlen = sizeof(prio); | |
145 | int rc; | |
146 | ||
147 | rc = getsockopt(sd, SOL_SOCKET, SO_PRIORITY, &prio, &optlen); | |
148 | if (rc < 0) | |
149 | perror("getsockopt(SO_PRIORITY)"); | |
150 | ||
151 | return prio; | |
152 | } | |
153 | ||
154 | static int show_sockopts(int family) | |
155 | { | |
156 | unsigned int mark, prio; | |
157 | char name[16]; | |
158 | int sd; | |
159 | ||
160 | sd = socket(family, SOCK_DGRAM, 17); | |
161 | if (sd < 0) { | |
162 | perror("socket"); | |
163 | return 1; | |
164 | } | |
165 | ||
166 | if (get_bind_to_device(sd, name, sizeof(name)) < 0) | |
167 | return 1; | |
168 | ||
169 | mark = get_somark(sd); | |
170 | prio = get_priority(sd); | |
171 | ||
172 | close(sd); | |
173 | ||
174 | printf("sd %d: dev %s, mark %u, priority %u\n", sd, name, mark, prio); | |
175 | ||
176 | return 0; | |
177 | } | |
178 | ||
ad2805dc DA |
179 | static int usage(const char *argv0) |
180 | { | |
609b1c32 DA |
181 | printf("Usage:\n"); |
182 | printf(" Attach a program\n"); | |
183 | printf(" %s -b bind-to-dev -m mark -p prio cg-path\n", argv0); | |
184 | printf("\n"); | |
185 | printf(" Detach a program\n"); | |
186 | printf(" %s -d cg-path\n", argv0); | |
f776d460 DA |
187 | printf("\n"); |
188 | printf(" Show inherited socket settings (mark, priority, and device)\n"); | |
189 | printf(" %s [-6]\n", argv0); | |
ad2805dc DA |
190 | return EXIT_FAILURE; |
191 | } | |
192 | ||
193 | int main(int argc, char **argv) | |
194 | { | |
fa38aa17 DA |
195 | __u32 idx = 0, mark = 0, prio = 0; |
196 | const char *cgrp_path = NULL; | |
ad2805dc | 197 | int cg_fd, prog_fd, ret; |
f776d460 | 198 | int family = PF_INET; |
609b1c32 | 199 | int do_attach = 1; |
fa38aa17 DA |
200 | int rc; |
201 | ||
f776d460 | 202 | while ((rc = getopt(argc, argv, "db:m:p:6")) != -1) { |
fa38aa17 | 203 | switch (rc) { |
609b1c32 DA |
204 | case 'd': |
205 | do_attach = 0; | |
206 | break; | |
fa38aa17 DA |
207 | case 'b': |
208 | idx = if_nametoindex(optarg); | |
209 | if (!idx) { | |
210 | idx = strtoumax(optarg, NULL, 0); | |
211 | if (!idx) { | |
212 | printf("Invalid device name\n"); | |
213 | return EXIT_FAILURE; | |
214 | } | |
215 | } | |
216 | break; | |
217 | case 'm': | |
218 | mark = strtoumax(optarg, NULL, 0); | |
219 | break; | |
220 | case 'p': | |
221 | prio = strtoumax(optarg, NULL, 0); | |
222 | break; | |
f776d460 DA |
223 | case '6': |
224 | family = PF_INET6; | |
225 | break; | |
fa38aa17 DA |
226 | default: |
227 | return usage(argv[0]); | |
228 | } | |
229 | } | |
ad2805dc | 230 | |
fa38aa17 | 231 | if (optind == argc) |
f776d460 | 232 | return show_sockopts(family); |
ad2805dc | 233 | |
fa38aa17 DA |
234 | cgrp_path = argv[optind]; |
235 | if (!cgrp_path) { | |
236 | fprintf(stderr, "cgroup path not given\n"); | |
ad2805dc DA |
237 | return EXIT_FAILURE; |
238 | } | |
239 | ||
609b1c32 | 240 | if (do_attach && !idx && !mark && !prio) { |
fa38aa17 DA |
241 | fprintf(stderr, |
242 | "One of device, mark or priority must be given\n"); | |
243 | return EXIT_FAILURE; | |
244 | } | |
245 | ||
246 | cg_fd = open(cgrp_path, O_DIRECTORY | O_RDONLY); | |
ad2805dc DA |
247 | if (cg_fd < 0) { |
248 | printf("Failed to open cgroup path: '%s'\n", strerror(errno)); | |
249 | return EXIT_FAILURE; | |
250 | } | |
251 | ||
609b1c32 DA |
252 | if (do_attach) { |
253 | prog_fd = prog_load(idx, mark, prio); | |
254 | if (prog_fd < 0) { | |
255 | printf("Failed to load prog: '%s'\n", strerror(errno)); | |
256 | printf("Output from kernel verifier:\n%s\n-------\n", | |
257 | bpf_log_buf); | |
258 | return EXIT_FAILURE; | |
259 | } | |
ad2805dc | 260 | |
609b1c32 DA |
261 | ret = bpf_prog_attach(prog_fd, cg_fd, |
262 | BPF_CGROUP_INET_SOCK_CREATE, 0); | |
263 | if (ret < 0) { | |
264 | printf("Failed to attach prog to cgroup: '%s'\n", | |
265 | strerror(errno)); | |
266 | return EXIT_FAILURE; | |
267 | } | |
268 | } else { | |
269 | ret = bpf_prog_detach(cg_fd, BPF_CGROUP_INET_SOCK_CREATE); | |
270 | if (ret < 0) { | |
271 | printf("Failed to detach prog from cgroup: '%s'\n", | |
272 | strerror(errno)); | |
273 | return EXIT_FAILURE; | |
274 | } | |
ad2805dc DA |
275 | } |
276 | ||
609b1c32 | 277 | close(cg_fd); |
ad2805dc DA |
278 | return EXIT_SUCCESS; |
279 | } |