at v4.18-rc6 423 lines 11 kB view raw
1/* eBPF example program: 2 * 3 * - Creates arraymap in kernel with 4 bytes keys and 8 byte values 4 * 5 * - Loads eBPF program 6 * 7 * The eBPF program accesses the map passed in to store two pieces of 8 * information. The number of invocations of the program, which maps 9 * to the number of packets received, is stored to key 0. Key 1 is 10 * incremented on each iteration by the number of bytes stored in 11 * the skb. 12 * 13 * - Attaches the new program to a cgroup using BPF_PROG_ATTACH 14 * 15 * - Every second, reads map[0] and map[1] to see how many bytes and 16 * packets were seen on any socket of tasks in the given cgroup. 17 */ 18 19#define _GNU_SOURCE 20 21#include <stdio.h> 22#include <stdlib.h> 23#include <assert.h> 24#include <unistd.h> 25 26#include <linux/bpf.h> 27#include <bpf/bpf.h> 28 29#include "bpf_insn.h" 30#include "cgroup_helpers.h" 31 32#define FOO "/foo" 33#define BAR "/foo/bar/" 34#define PING_CMD "ping -c1 -w1 127.0.0.1 > /dev/null" 35 36char bpf_log_buf[BPF_LOG_BUF_SIZE]; 37 38static int prog_load(int verdict) 39{ 40 int ret; 41 struct bpf_insn prog[] = { 42 BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */ 43 BPF_EXIT_INSN(), 44 }; 45 size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn); 46 47 ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB, 48 prog, insns_cnt, "GPL", 0, 49 bpf_log_buf, BPF_LOG_BUF_SIZE); 50 51 if (ret < 0) { 52 log_err("Loading program"); 53 printf("Output from verifier:\n%s\n-------\n", bpf_log_buf); 54 return 0; 55 } 56 return ret; 57} 58 59static int test_foo_bar(void) 60{ 61 int drop_prog, allow_prog, foo = 0, bar = 0, rc = 0; 62 63 allow_prog = prog_load(1); 64 if (!allow_prog) 65 goto err; 66 67 drop_prog = prog_load(0); 68 if (!drop_prog) 69 goto err; 70 71 if (setup_cgroup_environment()) 72 goto err; 73 74 /* Create cgroup /foo, get fd, and join it */ 75 foo = create_and_get_cgroup(FOO); 76 if (!foo) 77 goto err; 78 79 if (join_cgroup(FOO)) 80 goto err; 81 82 if (bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS, 83 BPF_F_ALLOW_OVERRIDE)) { 84 log_err("Attaching prog to /foo"); 85 goto err; 86 } 87 88 printf("Attached DROP prog. This ping in cgroup /foo should fail...\n"); 89 assert(system(PING_CMD) != 0); 90 91 /* Create cgroup /foo/bar, get fd, and join it */ 92 bar = create_and_get_cgroup(BAR); 93 if (!bar) 94 goto err; 95 96 if (join_cgroup(BAR)) 97 goto err; 98 99 printf("Attached DROP prog. This ping in cgroup /foo/bar should fail...\n"); 100 assert(system(PING_CMD) != 0); 101 102 if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 103 BPF_F_ALLOW_OVERRIDE)) { 104 log_err("Attaching prog to /foo/bar"); 105 goto err; 106 } 107 108 printf("Attached PASS prog. This ping in cgroup /foo/bar should pass...\n"); 109 assert(system(PING_CMD) == 0); 110 111 if (bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS)) { 112 log_err("Detaching program from /foo/bar"); 113 goto err; 114 } 115 116 printf("Detached PASS from /foo/bar while DROP is attached to /foo.\n" 117 "This ping in cgroup /foo/bar should fail...\n"); 118 assert(system(PING_CMD) != 0); 119 120 if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 121 BPF_F_ALLOW_OVERRIDE)) { 122 log_err("Attaching prog to /foo/bar"); 123 goto err; 124 } 125 126 if (bpf_prog_detach(foo, BPF_CGROUP_INET_EGRESS)) { 127 log_err("Detaching program from /foo"); 128 goto err; 129 } 130 131 printf("Attached PASS from /foo/bar and detached DROP from /foo.\n" 132 "This ping in cgroup /foo/bar should pass...\n"); 133 assert(system(PING_CMD) == 0); 134 135 if (bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 136 BPF_F_ALLOW_OVERRIDE)) { 137 log_err("Attaching prog to /foo/bar"); 138 goto err; 139 } 140 141 if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 0)) { 142 errno = 0; 143 log_err("Unexpected success attaching prog to /foo/bar"); 144 goto err; 145 } 146 147 if (bpf_prog_detach(bar, BPF_CGROUP_INET_EGRESS)) { 148 log_err("Detaching program from /foo/bar"); 149 goto err; 150 } 151 152 if (!bpf_prog_detach(foo, BPF_CGROUP_INET_EGRESS)) { 153 errno = 0; 154 log_err("Unexpected success in double detach from /foo"); 155 goto err; 156 } 157 158 if (bpf_prog_attach(allow_prog, foo, BPF_CGROUP_INET_EGRESS, 0)) { 159 log_err("Attaching non-overridable prog to /foo"); 160 goto err; 161 } 162 163 if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 0)) { 164 errno = 0; 165 log_err("Unexpected success attaching non-overridable prog to /foo/bar"); 166 goto err; 167 } 168 169 if (!bpf_prog_attach(allow_prog, bar, BPF_CGROUP_INET_EGRESS, 170 BPF_F_ALLOW_OVERRIDE)) { 171 errno = 0; 172 log_err("Unexpected success attaching overridable prog to /foo/bar"); 173 goto err; 174 } 175 176 if (!bpf_prog_attach(allow_prog, foo, BPF_CGROUP_INET_EGRESS, 177 BPF_F_ALLOW_OVERRIDE)) { 178 errno = 0; 179 log_err("Unexpected success attaching overridable prog to /foo"); 180 goto err; 181 } 182 183 if (bpf_prog_attach(drop_prog, foo, BPF_CGROUP_INET_EGRESS, 0)) { 184 log_err("Attaching different non-overridable prog to /foo"); 185 goto err; 186 } 187 188 goto out; 189 190err: 191 rc = 1; 192 193out: 194 close(foo); 195 close(bar); 196 cleanup_cgroup_environment(); 197 if (!rc) 198 printf("### override:PASS\n"); 199 else 200 printf("### override:FAIL\n"); 201 return rc; 202} 203 204static int map_fd = -1; 205 206static int prog_load_cnt(int verdict, int val) 207{ 208 if (map_fd < 0) 209 map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, 4, 8, 1, 0); 210 if (map_fd < 0) { 211 printf("failed to create map '%s'\n", strerror(errno)); 212 return -1; 213 } 214 215 struct bpf_insn prog[] = { 216 BPF_MOV32_IMM(BPF_REG_0, 0), 217 BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp - 4) = r0 */ 218 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), 219 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp - 4 */ 220 BPF_LD_MAP_FD(BPF_REG_1, map_fd), 221 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), 222 BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2), 223 BPF_MOV64_IMM(BPF_REG_1, val), /* r1 = 1 */ 224 BPF_RAW_INSN(BPF_STX | BPF_XADD | BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* xadd r0 += r1 */ 225 BPF_MOV64_IMM(BPF_REG_0, verdict), /* r0 = verdict */ 226 BPF_EXIT_INSN(), 227 }; 228 size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn); 229 int ret; 230 231 ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SKB, 232 prog, insns_cnt, "GPL", 0, 233 bpf_log_buf, BPF_LOG_BUF_SIZE); 234 235 if (ret < 0) { 236 log_err("Loading program"); 237 printf("Output from verifier:\n%s\n-------\n", bpf_log_buf); 238 return 0; 239 } 240 return ret; 241} 242 243 244static int test_multiprog(void) 245{ 246 __u32 prog_ids[4], prog_cnt = 0, attach_flags, saved_prog_id; 247 int cg1 = 0, cg2 = 0, cg3 = 0, cg4 = 0, cg5 = 0, key = 0; 248 int drop_prog, allow_prog[6] = {}, rc = 0; 249 unsigned long long value; 250 int i = 0; 251 252 for (i = 0; i < 6; i++) { 253 allow_prog[i] = prog_load_cnt(1, 1 << i); 254 if (!allow_prog[i]) 255 goto err; 256 } 257 drop_prog = prog_load_cnt(0, 1); 258 if (!drop_prog) 259 goto err; 260 261 if (setup_cgroup_environment()) 262 goto err; 263 264 cg1 = create_and_get_cgroup("/cg1"); 265 if (!cg1) 266 goto err; 267 cg2 = create_and_get_cgroup("/cg1/cg2"); 268 if (!cg2) 269 goto err; 270 cg3 = create_and_get_cgroup("/cg1/cg2/cg3"); 271 if (!cg3) 272 goto err; 273 cg4 = create_and_get_cgroup("/cg1/cg2/cg3/cg4"); 274 if (!cg4) 275 goto err; 276 cg5 = create_and_get_cgroup("/cg1/cg2/cg3/cg4/cg5"); 277 if (!cg5) 278 goto err; 279 280 if (join_cgroup("/cg1/cg2/cg3/cg4/cg5")) 281 goto err; 282 283 if (bpf_prog_attach(allow_prog[0], cg1, BPF_CGROUP_INET_EGRESS, 284 BPF_F_ALLOW_MULTI)) { 285 log_err("Attaching prog to cg1"); 286 goto err; 287 } 288 if (!bpf_prog_attach(allow_prog[0], cg1, BPF_CGROUP_INET_EGRESS, 289 BPF_F_ALLOW_MULTI)) { 290 log_err("Unexpected success attaching the same prog to cg1"); 291 goto err; 292 } 293 if (bpf_prog_attach(allow_prog[1], cg1, BPF_CGROUP_INET_EGRESS, 294 BPF_F_ALLOW_MULTI)) { 295 log_err("Attaching prog2 to cg1"); 296 goto err; 297 } 298 if (bpf_prog_attach(allow_prog[2], cg2, BPF_CGROUP_INET_EGRESS, 299 BPF_F_ALLOW_OVERRIDE)) { 300 log_err("Attaching prog to cg2"); 301 goto err; 302 } 303 if (bpf_prog_attach(allow_prog[3], cg3, BPF_CGROUP_INET_EGRESS, 304 BPF_F_ALLOW_MULTI)) { 305 log_err("Attaching prog to cg3"); 306 goto err; 307 } 308 if (bpf_prog_attach(allow_prog[4], cg4, BPF_CGROUP_INET_EGRESS, 309 BPF_F_ALLOW_OVERRIDE)) { 310 log_err("Attaching prog to cg4"); 311 goto err; 312 } 313 if (bpf_prog_attach(allow_prog[5], cg5, BPF_CGROUP_INET_EGRESS, 0)) { 314 log_err("Attaching prog to cg5"); 315 goto err; 316 } 317 assert(system(PING_CMD) == 0); 318 assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0); 319 assert(value == 1 + 2 + 8 + 32); 320 321 /* query the number of effective progs in cg5 */ 322 assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE, 323 NULL, NULL, &prog_cnt) == 0); 324 assert(prog_cnt == 4); 325 /* retrieve prog_ids of effective progs in cg5 */ 326 assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE, 327 &attach_flags, prog_ids, &prog_cnt) == 0); 328 assert(prog_cnt == 4); 329 assert(attach_flags == 0); 330 saved_prog_id = prog_ids[0]; 331 /* check enospc handling */ 332 prog_ids[0] = 0; 333 prog_cnt = 2; 334 assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE, 335 &attach_flags, prog_ids, &prog_cnt) == -1 && 336 errno == ENOSPC); 337 assert(prog_cnt == 4); 338 /* check that prog_ids are returned even when buffer is too small */ 339 assert(prog_ids[0] == saved_prog_id); 340 /* retrieve prog_id of single attached prog in cg5 */ 341 prog_ids[0] = 0; 342 assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, 0, 343 NULL, prog_ids, &prog_cnt) == 0); 344 assert(prog_cnt == 1); 345 assert(prog_ids[0] == saved_prog_id); 346 347 /* detach bottom program and ping again */ 348 if (bpf_prog_detach2(-1, cg5, BPF_CGROUP_INET_EGRESS)) { 349 log_err("Detaching prog from cg5"); 350 goto err; 351 } 352 value = 0; 353 assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0); 354 assert(system(PING_CMD) == 0); 355 assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0); 356 assert(value == 1 + 2 + 8 + 16); 357 358 /* detach 3rd from bottom program and ping again */ 359 errno = 0; 360 if (!bpf_prog_detach2(0, cg3, BPF_CGROUP_INET_EGRESS)) { 361 log_err("Unexpected success on detach from cg3"); 362 goto err; 363 } 364 if (bpf_prog_detach2(allow_prog[3], cg3, BPF_CGROUP_INET_EGRESS)) { 365 log_err("Detaching from cg3"); 366 goto err; 367 } 368 value = 0; 369 assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0); 370 assert(system(PING_CMD) == 0); 371 assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0); 372 assert(value == 1 + 2 + 16); 373 374 /* detach 2nd from bottom program and ping again */ 375 if (bpf_prog_detach2(-1, cg4, BPF_CGROUP_INET_EGRESS)) { 376 log_err("Detaching prog from cg4"); 377 goto err; 378 } 379 value = 0; 380 assert(bpf_map_update_elem(map_fd, &key, &value, 0) == 0); 381 assert(system(PING_CMD) == 0); 382 assert(bpf_map_lookup_elem(map_fd, &key, &value) == 0); 383 assert(value == 1 + 2 + 4); 384 385 prog_cnt = 4; 386 assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, BPF_F_QUERY_EFFECTIVE, 387 &attach_flags, prog_ids, &prog_cnt) == 0); 388 assert(prog_cnt == 3); 389 assert(attach_flags == 0); 390 assert(bpf_prog_query(cg5, BPF_CGROUP_INET_EGRESS, 0, 391 NULL, prog_ids, &prog_cnt) == 0); 392 assert(prog_cnt == 0); 393 goto out; 394err: 395 rc = 1; 396 397out: 398 for (i = 0; i < 6; i++) 399 if (allow_prog[i] > 0) 400 close(allow_prog[i]); 401 close(cg1); 402 close(cg2); 403 close(cg3); 404 close(cg4); 405 close(cg5); 406 cleanup_cgroup_environment(); 407 if (!rc) 408 printf("### multi:PASS\n"); 409 else 410 printf("### multi:FAIL\n"); 411 return rc; 412} 413 414int main(int argc, char **argv) 415{ 416 int rc = 0; 417 418 rc = test_foo_bar(); 419 if (rc) 420 return rc; 421 422 return test_multiprog(); 423}