1ad2805dcSDavid Ahern /* eBPF example program:
2ad2805dcSDavid Ahern *
3ad2805dcSDavid Ahern * - Loads eBPF program
4ad2805dcSDavid Ahern *
5ad2805dcSDavid Ahern * The eBPF program sets the sk_bound_dev_if index in new AF_INET{6}
6ad2805dcSDavid Ahern * sockets opened by processes in the cgroup.
7ad2805dcSDavid Ahern *
8ad2805dcSDavid Ahern * - Attaches the new program to a cgroup using BPF_PROG_ATTACH
9ad2805dcSDavid Ahern */
10ad2805dcSDavid Ahern
11ad2805dcSDavid Ahern #define _GNU_SOURCE
12ad2805dcSDavid Ahern
13ad2805dcSDavid Ahern #include <stdio.h>
14ad2805dcSDavid Ahern #include <stdlib.h>
15ad2805dcSDavid Ahern #include <stddef.h>
16ad2805dcSDavid Ahern #include <string.h>
17ad2805dcSDavid Ahern #include <unistd.h>
18ad2805dcSDavid Ahern #include <assert.h>
19ad2805dcSDavid Ahern #include <errno.h>
20ad2805dcSDavid Ahern #include <fcntl.h>
21ad2805dcSDavid Ahern #include <net/if.h>
22fa38aa17SDavid Ahern #include <inttypes.h>
23ad2805dcSDavid Ahern #include <linux/bpf.h>
248d930450SJakub Kicinski #include <bpf/bpf.h>
25ad2805dcSDavid Ahern
268d930450SJakub Kicinski #include "bpf_insn.h"
27ad2805dcSDavid Ahern
28d40fc181SJoe Stringer char bpf_log_buf[BPF_LOG_BUF_SIZE];
29d40fc181SJoe Stringer
prog_load(__u32 idx,__u32 mark,__u32 prio)30fa38aa17SDavid Ahern static int prog_load(__u32 idx, __u32 mark, __u32 prio)
31ad2805dcSDavid Ahern {
32fa38aa17SDavid Ahern /* save pointer to context */
33fa38aa17SDavid Ahern struct bpf_insn prog_start[] = {
34ad2805dcSDavid Ahern BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
35fa38aa17SDavid Ahern };
36fa38aa17SDavid Ahern struct bpf_insn prog_end[] = {
37ad2805dcSDavid Ahern BPF_MOV64_IMM(BPF_REG_0, 1), /* r0 = verdict */
38ad2805dcSDavid Ahern BPF_EXIT_INSN(),
39ad2805dcSDavid Ahern };
40ad2805dcSDavid Ahern
41fa38aa17SDavid Ahern /* set sk_bound_dev_if on socket */
42fa38aa17SDavid Ahern struct bpf_insn prog_dev[] = {
43fa38aa17SDavid Ahern BPF_MOV64_IMM(BPF_REG_3, idx),
44fa38aa17SDavid Ahern BPF_MOV64_IMM(BPF_REG_2, offsetof(struct bpf_sock, bound_dev_if)),
45fa38aa17SDavid Ahern BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, offsetof(struct bpf_sock, bound_dev_if)),
46fa38aa17SDavid Ahern };
47fa38aa17SDavid Ahern
48fa38aa17SDavid Ahern /* set mark on socket */
49fa38aa17SDavid Ahern struct bpf_insn prog_mark[] = {
500adc3dd9SDavid Ahern /* get uid of process */
510adc3dd9SDavid Ahern BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
520adc3dd9SDavid Ahern BPF_FUNC_get_current_uid_gid),
530adc3dd9SDavid Ahern BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 0xffffffff),
540adc3dd9SDavid Ahern
550adc3dd9SDavid Ahern /* if uid is 0, use given mark, else use the uid as the mark */
560adc3dd9SDavid Ahern BPF_MOV64_REG(BPF_REG_3, BPF_REG_0),
570adc3dd9SDavid Ahern BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
58fa38aa17SDavid Ahern BPF_MOV64_IMM(BPF_REG_3, mark),
590adc3dd9SDavid Ahern
600adc3dd9SDavid Ahern /* set the mark on the new socket */
610adc3dd9SDavid Ahern BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
62fa38aa17SDavid Ahern BPF_MOV64_IMM(BPF_REG_2, offsetof(struct bpf_sock, mark)),
63fa38aa17SDavid Ahern BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, offsetof(struct bpf_sock, mark)),
64fa38aa17SDavid Ahern };
65fa38aa17SDavid Ahern
66fa38aa17SDavid Ahern /* set priority on socket */
67fa38aa17SDavid Ahern struct bpf_insn prog_prio[] = {
68fa38aa17SDavid Ahern BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
69fa38aa17SDavid Ahern BPF_MOV64_IMM(BPF_REG_3, prio),
70fa38aa17SDavid Ahern BPF_MOV64_IMM(BPF_REG_2, offsetof(struct bpf_sock, priority)),
71fa38aa17SDavid Ahern BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, offsetof(struct bpf_sock, priority)),
72fa38aa17SDavid Ahern };
73c58f9815SAndrii Nakryiko LIBBPF_OPTS(bpf_prog_load_opts, opts,
74c58f9815SAndrii Nakryiko .log_buf = bpf_log_buf,
75c58f9815SAndrii Nakryiko .log_size = BPF_LOG_BUF_SIZE,
76c58f9815SAndrii Nakryiko );
77fa38aa17SDavid Ahern
78fa38aa17SDavid Ahern struct bpf_insn *prog;
79fa38aa17SDavid Ahern size_t insns_cnt;
80fa38aa17SDavid Ahern void *p;
81fa38aa17SDavid Ahern int ret;
82fa38aa17SDavid Ahern
83fa38aa17SDavid Ahern insns_cnt = sizeof(prog_start) + sizeof(prog_end);
84fa38aa17SDavid Ahern if (idx)
85fa38aa17SDavid Ahern insns_cnt += sizeof(prog_dev);
86fa38aa17SDavid Ahern
87fa38aa17SDavid Ahern if (mark)
88fa38aa17SDavid Ahern insns_cnt += sizeof(prog_mark);
89fa38aa17SDavid Ahern
90fa38aa17SDavid Ahern if (prio)
91fa38aa17SDavid Ahern insns_cnt += sizeof(prog_prio);
92fa38aa17SDavid Ahern
93fa38aa17SDavid Ahern p = prog = malloc(insns_cnt);
94fa38aa17SDavid Ahern if (!prog) {
95fa38aa17SDavid Ahern fprintf(stderr, "Failed to allocate memory for instructions\n");
96fa38aa17SDavid Ahern return EXIT_FAILURE;
97fa38aa17SDavid Ahern }
98fa38aa17SDavid Ahern
99fa38aa17SDavid Ahern memcpy(p, prog_start, sizeof(prog_start));
100fa38aa17SDavid Ahern p += sizeof(prog_start);
101fa38aa17SDavid Ahern
102fa38aa17SDavid Ahern if (idx) {
103fa38aa17SDavid Ahern memcpy(p, prog_dev, sizeof(prog_dev));
104fa38aa17SDavid Ahern p += sizeof(prog_dev);
105fa38aa17SDavid Ahern }
106fa38aa17SDavid Ahern
107fa38aa17SDavid Ahern if (mark) {
108fa38aa17SDavid Ahern memcpy(p, prog_mark, sizeof(prog_mark));
109fa38aa17SDavid Ahern p += sizeof(prog_mark);
110fa38aa17SDavid Ahern }
111fa38aa17SDavid Ahern
112fa38aa17SDavid Ahern if (prio) {
113fa38aa17SDavid Ahern memcpy(p, prog_prio, sizeof(prog_prio));
114fa38aa17SDavid Ahern p += sizeof(prog_prio);
115fa38aa17SDavid Ahern }
116fa38aa17SDavid Ahern
117fa38aa17SDavid Ahern memcpy(p, prog_end, sizeof(prog_end));
118fa38aa17SDavid Ahern p += sizeof(prog_end);
119fa38aa17SDavid Ahern
120fa38aa17SDavid Ahern insns_cnt /= sizeof(struct bpf_insn);
121fa38aa17SDavid Ahern
122c58f9815SAndrii Nakryiko ret = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, NULL, "GPL",
123c58f9815SAndrii Nakryiko prog, insns_cnt, &opts);
124fa38aa17SDavid Ahern
125fa38aa17SDavid Ahern free(prog);
126fa38aa17SDavid Ahern
127fa38aa17SDavid Ahern return ret;
128ad2805dcSDavid Ahern }
129ad2805dcSDavid Ahern
get_bind_to_device(int sd,char * name,size_t len)130f776d460SDavid Ahern static int get_bind_to_device(int sd, char *name, size_t len)
131f776d460SDavid Ahern {
132f776d460SDavid Ahern socklen_t optlen = len;
133f776d460SDavid Ahern int rc;
134f776d460SDavid Ahern
135f776d460SDavid Ahern name[0] = '\0';
136f776d460SDavid Ahern rc = getsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, name, &optlen);
137f776d460SDavid Ahern if (rc < 0)
138f776d460SDavid Ahern perror("setsockopt(SO_BINDTODEVICE)");
139f776d460SDavid Ahern
140f776d460SDavid Ahern return rc;
141f776d460SDavid Ahern }
142f776d460SDavid Ahern
get_somark(int sd)143f776d460SDavid Ahern static unsigned int get_somark(int sd)
144f776d460SDavid Ahern {
145f776d460SDavid Ahern unsigned int mark = 0;
146f776d460SDavid Ahern socklen_t optlen = sizeof(mark);
147f776d460SDavid Ahern int rc;
148f776d460SDavid Ahern
149f776d460SDavid Ahern rc = getsockopt(sd, SOL_SOCKET, SO_MARK, &mark, &optlen);
150f776d460SDavid Ahern if (rc < 0)
151f776d460SDavid Ahern perror("getsockopt(SO_MARK)");
152f776d460SDavid Ahern
153f776d460SDavid Ahern return mark;
154f776d460SDavid Ahern }
155f776d460SDavid Ahern
get_priority(int sd)156f776d460SDavid Ahern static unsigned int get_priority(int sd)
157f776d460SDavid Ahern {
158f776d460SDavid Ahern unsigned int prio = 0;
159f776d460SDavid Ahern socklen_t optlen = sizeof(prio);
160f776d460SDavid Ahern int rc;
161f776d460SDavid Ahern
162f776d460SDavid Ahern rc = getsockopt(sd, SOL_SOCKET, SO_PRIORITY, &prio, &optlen);
163f776d460SDavid Ahern if (rc < 0)
164f776d460SDavid Ahern perror("getsockopt(SO_PRIORITY)");
165f776d460SDavid Ahern
166f776d460SDavid Ahern return prio;
167f776d460SDavid Ahern }
168f776d460SDavid Ahern
show_sockopts(int family)169f776d460SDavid Ahern static int show_sockopts(int family)
170f776d460SDavid Ahern {
171f776d460SDavid Ahern unsigned int mark, prio;
172f776d460SDavid Ahern char name[16];
173f776d460SDavid Ahern int sd;
174f776d460SDavid Ahern
175f776d460SDavid Ahern sd = socket(family, SOCK_DGRAM, 17);
176f776d460SDavid Ahern if (sd < 0) {
177f776d460SDavid Ahern perror("socket");
178f776d460SDavid Ahern return 1;
179f776d460SDavid Ahern }
180f776d460SDavid Ahern
181*cf27f382SZhu Jun if (get_bind_to_device(sd, name, sizeof(name)) < 0) {
182*cf27f382SZhu Jun close(sd);
183f776d460SDavid Ahern return 1;
184*cf27f382SZhu Jun }
185f776d460SDavid Ahern
186f776d460SDavid Ahern mark = get_somark(sd);
187f776d460SDavid Ahern prio = get_priority(sd);
188f776d460SDavid Ahern
189f776d460SDavid Ahern close(sd);
190f776d460SDavid Ahern
191f776d460SDavid Ahern printf("sd %d: dev %s, mark %u, priority %u\n", sd, name, mark, prio);
192f776d460SDavid Ahern
193f776d460SDavid Ahern return 0;
194f776d460SDavid Ahern }
195f776d460SDavid Ahern
usage(const char * argv0)196ad2805dcSDavid Ahern static int usage(const char *argv0)
197ad2805dcSDavid Ahern {
198609b1c32SDavid Ahern printf("Usage:\n");
199609b1c32SDavid Ahern printf(" Attach a program\n");
200609b1c32SDavid Ahern printf(" %s -b bind-to-dev -m mark -p prio cg-path\n", argv0);
201609b1c32SDavid Ahern printf("\n");
202609b1c32SDavid Ahern printf(" Detach a program\n");
203609b1c32SDavid Ahern printf(" %s -d cg-path\n", argv0);
204f776d460SDavid Ahern printf("\n");
205f776d460SDavid Ahern printf(" Show inherited socket settings (mark, priority, and device)\n");
206f776d460SDavid Ahern printf(" %s [-6]\n", argv0);
207ad2805dcSDavid Ahern return EXIT_FAILURE;
208ad2805dcSDavid Ahern }
209ad2805dcSDavid Ahern
main(int argc,char ** argv)210ad2805dcSDavid Ahern int main(int argc, char **argv)
211ad2805dcSDavid Ahern {
212fa38aa17SDavid Ahern __u32 idx = 0, mark = 0, prio = 0;
213fa38aa17SDavid Ahern const char *cgrp_path = NULL;
214ad2805dcSDavid Ahern int cg_fd, prog_fd, ret;
215f776d460SDavid Ahern int family = PF_INET;
216609b1c32SDavid Ahern int do_attach = 1;
217fa38aa17SDavid Ahern int rc;
218ad2805dcSDavid Ahern
219f776d460SDavid Ahern while ((rc = getopt(argc, argv, "db:m:p:6")) != -1) {
220fa38aa17SDavid Ahern switch (rc) {
221609b1c32SDavid Ahern case 'd':
222609b1c32SDavid Ahern do_attach = 0;
223609b1c32SDavid Ahern break;
224fa38aa17SDavid Ahern case 'b':
225fa38aa17SDavid Ahern idx = if_nametoindex(optarg);
226fa38aa17SDavid Ahern if (!idx) {
227fa38aa17SDavid Ahern idx = strtoumax(optarg, NULL, 0);
228ad2805dcSDavid Ahern if (!idx) {
229ad2805dcSDavid Ahern printf("Invalid device name\n");
230ad2805dcSDavid Ahern return EXIT_FAILURE;
231ad2805dcSDavid Ahern }
232fa38aa17SDavid Ahern }
233fa38aa17SDavid Ahern break;
234fa38aa17SDavid Ahern case 'm':
235fa38aa17SDavid Ahern mark = strtoumax(optarg, NULL, 0);
236fa38aa17SDavid Ahern break;
237fa38aa17SDavid Ahern case 'p':
238fa38aa17SDavid Ahern prio = strtoumax(optarg, NULL, 0);
239fa38aa17SDavid Ahern break;
240f776d460SDavid Ahern case '6':
241f776d460SDavid Ahern family = PF_INET6;
242f776d460SDavid Ahern break;
243fa38aa17SDavid Ahern default:
244fa38aa17SDavid Ahern return usage(argv[0]);
245fa38aa17SDavid Ahern }
246fa38aa17SDavid Ahern }
247ad2805dcSDavid Ahern
248fa38aa17SDavid Ahern if (optind == argc)
249f776d460SDavid Ahern return show_sockopts(family);
250fa38aa17SDavid Ahern
251fa38aa17SDavid Ahern cgrp_path = argv[optind];
252fa38aa17SDavid Ahern if (!cgrp_path) {
253fa38aa17SDavid Ahern fprintf(stderr, "cgroup path not given\n");
254fa38aa17SDavid Ahern return EXIT_FAILURE;
255fa38aa17SDavid Ahern }
256fa38aa17SDavid Ahern
257609b1c32SDavid Ahern if (do_attach && !idx && !mark && !prio) {
258fa38aa17SDavid Ahern fprintf(stderr,
259fa38aa17SDavid Ahern "One of device, mark or priority must be given\n");
260fa38aa17SDavid Ahern return EXIT_FAILURE;
261fa38aa17SDavid Ahern }
262fa38aa17SDavid Ahern
263fa38aa17SDavid Ahern cg_fd = open(cgrp_path, O_DIRECTORY | O_RDONLY);
264ad2805dcSDavid Ahern if (cg_fd < 0) {
265ad2805dcSDavid Ahern printf("Failed to open cgroup path: '%s'\n", strerror(errno));
266ad2805dcSDavid Ahern return EXIT_FAILURE;
267ad2805dcSDavid Ahern }
268ad2805dcSDavid Ahern
269609b1c32SDavid Ahern if (do_attach) {
270fa38aa17SDavid Ahern prog_fd = prog_load(idx, mark, prio);
271ad2805dcSDavid Ahern if (prog_fd < 0) {
272ad2805dcSDavid Ahern printf("Failed to load prog: '%s'\n", strerror(errno));
273fa38aa17SDavid Ahern printf("Output from kernel verifier:\n%s\n-------\n",
274fa38aa17SDavid Ahern bpf_log_buf);
275ad2805dcSDavid Ahern return EXIT_FAILURE;
276ad2805dcSDavid Ahern }
277ad2805dcSDavid Ahern
278609b1c32SDavid Ahern ret = bpf_prog_attach(prog_fd, cg_fd,
279609b1c32SDavid Ahern BPF_CGROUP_INET_SOCK_CREATE, 0);
280ad2805dcSDavid Ahern if (ret < 0) {
281ad2805dcSDavid Ahern printf("Failed to attach prog to cgroup: '%s'\n",
282ad2805dcSDavid Ahern strerror(errno));
283ad2805dcSDavid Ahern return EXIT_FAILURE;
284ad2805dcSDavid Ahern }
285609b1c32SDavid Ahern } else {
286609b1c32SDavid Ahern ret = bpf_prog_detach(cg_fd, BPF_CGROUP_INET_SOCK_CREATE);
287609b1c32SDavid Ahern if (ret < 0) {
288609b1c32SDavid Ahern printf("Failed to detach prog from cgroup: '%s'\n",
289609b1c32SDavid Ahern strerror(errno));
290609b1c32SDavid Ahern return EXIT_FAILURE;
291609b1c32SDavid Ahern }
292609b1c32SDavid Ahern }
293ad2805dcSDavid Ahern
294609b1c32SDavid Ahern close(cg_fd);
295ad2805dcSDavid Ahern return EXIT_SUCCESS;
296ad2805dcSDavid Ahern }
297