test_cgrp2_attach2.c 12 KB

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