cgroup.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
  2. // Copyright (C) 2017 Facebook
  3. // Author: Roman Gushchin <[email protected]>
  4. #define _XOPEN_SOURCE 500
  5. #include <errno.h>
  6. #include <fcntl.h>
  7. #include <ftw.h>
  8. #include <mntent.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <sys/stat.h>
  13. #include <sys/types.h>
  14. #include <unistd.h>
  15. #include <bpf/bpf.h>
  16. #include <bpf/btf.h>
  17. #include "main.h"
  18. #define HELP_SPEC_ATTACH_FLAGS \
  19. "ATTACH_FLAGS := { multi | override }"
  20. #define HELP_SPEC_ATTACH_TYPES \
  21. " ATTACH_TYPE := { cgroup_inet_ingress | cgroup_inet_egress |\n" \
  22. " cgroup_inet_sock_create | cgroup_sock_ops |\n" \
  23. " cgroup_device | cgroup_inet4_bind |\n" \
  24. " cgroup_inet6_bind | cgroup_inet4_post_bind |\n" \
  25. " cgroup_inet6_post_bind | cgroup_inet4_connect |\n" \
  26. " cgroup_inet6_connect | cgroup_inet4_getpeername |\n" \
  27. " cgroup_inet6_getpeername | cgroup_inet4_getsockname |\n" \
  28. " cgroup_inet6_getsockname | cgroup_udp4_sendmsg |\n" \
  29. " cgroup_udp6_sendmsg | cgroup_udp4_recvmsg |\n" \
  30. " cgroup_udp6_recvmsg | cgroup_sysctl |\n" \
  31. " cgroup_getsockopt | cgroup_setsockopt |\n" \
  32. " cgroup_inet_sock_release }"
  33. static unsigned int query_flags;
  34. static struct btf *btf_vmlinux;
  35. static __u32 btf_vmlinux_id;
  36. static enum bpf_attach_type parse_attach_type(const char *str)
  37. {
  38. const char *attach_type_str;
  39. enum bpf_attach_type type;
  40. for (type = 0; ; type++) {
  41. attach_type_str = libbpf_bpf_attach_type_str(type);
  42. if (!attach_type_str)
  43. break;
  44. if (!strcmp(str, attach_type_str))
  45. return type;
  46. }
  47. /* Also check traditionally used attach type strings. For these we keep
  48. * allowing prefixed usage.
  49. */
  50. for (type = 0; ; type++) {
  51. attach_type_str = bpf_attach_type_input_str(type);
  52. if (!attach_type_str)
  53. break;
  54. if (is_prefix(str, attach_type_str))
  55. return type;
  56. }
  57. return __MAX_BPF_ATTACH_TYPE;
  58. }
  59. static void guess_vmlinux_btf_id(__u32 attach_btf_obj_id)
  60. {
  61. struct bpf_btf_info btf_info = {};
  62. __u32 btf_len = sizeof(btf_info);
  63. char name[16] = {};
  64. int err;
  65. int fd;
  66. btf_info.name = ptr_to_u64(name);
  67. btf_info.name_len = sizeof(name);
  68. fd = bpf_btf_get_fd_by_id(attach_btf_obj_id);
  69. if (fd < 0)
  70. return;
  71. err = bpf_obj_get_info_by_fd(fd, &btf_info, &btf_len);
  72. if (err)
  73. goto out;
  74. if (btf_info.kernel_btf && strncmp(name, "vmlinux", sizeof(name)) == 0)
  75. btf_vmlinux_id = btf_info.id;
  76. out:
  77. close(fd);
  78. }
  79. static int show_bpf_prog(int id, enum bpf_attach_type attach_type,
  80. const char *attach_flags_str,
  81. int level)
  82. {
  83. char prog_name[MAX_PROG_FULL_NAME];
  84. const char *attach_btf_name = NULL;
  85. struct bpf_prog_info info = {};
  86. const char *attach_type_str;
  87. __u32 info_len = sizeof(info);
  88. int prog_fd;
  89. prog_fd = bpf_prog_get_fd_by_id(id);
  90. if (prog_fd < 0)
  91. return -1;
  92. if (bpf_obj_get_info_by_fd(prog_fd, &info, &info_len)) {
  93. close(prog_fd);
  94. return -1;
  95. }
  96. attach_type_str = libbpf_bpf_attach_type_str(attach_type);
  97. if (btf_vmlinux) {
  98. if (!btf_vmlinux_id)
  99. guess_vmlinux_btf_id(info.attach_btf_obj_id);
  100. if (btf_vmlinux_id == info.attach_btf_obj_id &&
  101. info.attach_btf_id < btf__type_cnt(btf_vmlinux)) {
  102. const struct btf_type *t =
  103. btf__type_by_id(btf_vmlinux, info.attach_btf_id);
  104. attach_btf_name =
  105. btf__name_by_offset(btf_vmlinux, t->name_off);
  106. }
  107. }
  108. get_prog_full_name(&info, prog_fd, prog_name, sizeof(prog_name));
  109. if (json_output) {
  110. jsonw_start_object(json_wtr);
  111. jsonw_uint_field(json_wtr, "id", info.id);
  112. if (attach_type_str)
  113. jsonw_string_field(json_wtr, "attach_type", attach_type_str);
  114. else
  115. jsonw_uint_field(json_wtr, "attach_type", attach_type);
  116. if (!(query_flags & BPF_F_QUERY_EFFECTIVE))
  117. jsonw_string_field(json_wtr, "attach_flags", attach_flags_str);
  118. jsonw_string_field(json_wtr, "name", prog_name);
  119. if (attach_btf_name)
  120. jsonw_string_field(json_wtr, "attach_btf_name", attach_btf_name);
  121. jsonw_uint_field(json_wtr, "attach_btf_obj_id", info.attach_btf_obj_id);
  122. jsonw_uint_field(json_wtr, "attach_btf_id", info.attach_btf_id);
  123. jsonw_end_object(json_wtr);
  124. } else {
  125. printf("%s%-8u ", level ? " " : "", info.id);
  126. if (attach_type_str)
  127. printf("%-15s", attach_type_str);
  128. else
  129. printf("type %-10u", attach_type);
  130. if (query_flags & BPF_F_QUERY_EFFECTIVE)
  131. printf(" %-15s", prog_name);
  132. else
  133. printf(" %-15s %-15s", attach_flags_str, prog_name);
  134. if (attach_btf_name)
  135. printf(" %-15s", attach_btf_name);
  136. else if (info.attach_btf_id)
  137. printf(" attach_btf_obj_id=%d attach_btf_id=%d",
  138. info.attach_btf_obj_id, info.attach_btf_id);
  139. printf("\n");
  140. }
  141. close(prog_fd);
  142. return 0;
  143. }
  144. static int count_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type)
  145. {
  146. __u32 prog_cnt = 0;
  147. int ret;
  148. ret = bpf_prog_query(cgroup_fd, type, query_flags, NULL,
  149. NULL, &prog_cnt);
  150. if (ret)
  151. return -1;
  152. return prog_cnt;
  153. }
  154. static int cgroup_has_attached_progs(int cgroup_fd)
  155. {
  156. enum bpf_attach_type type;
  157. bool no_prog = true;
  158. for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
  159. int count = count_attached_bpf_progs(cgroup_fd, type);
  160. if (count < 0 && errno != EINVAL)
  161. return -1;
  162. if (count > 0) {
  163. no_prog = false;
  164. break;
  165. }
  166. }
  167. return no_prog ? 0 : 1;
  168. }
  169. static int show_effective_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
  170. int level)
  171. {
  172. LIBBPF_OPTS(bpf_prog_query_opts, p);
  173. __u32 prog_ids[1024] = {0};
  174. __u32 iter;
  175. int ret;
  176. p.query_flags = query_flags;
  177. p.prog_cnt = ARRAY_SIZE(prog_ids);
  178. p.prog_ids = prog_ids;
  179. ret = bpf_prog_query_opts(cgroup_fd, type, &p);
  180. if (ret)
  181. return ret;
  182. if (p.prog_cnt == 0)
  183. return 0;
  184. for (iter = 0; iter < p.prog_cnt; iter++)
  185. show_bpf_prog(prog_ids[iter], type, NULL, level);
  186. return 0;
  187. }
  188. static int show_attached_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
  189. int level)
  190. {
  191. LIBBPF_OPTS(bpf_prog_query_opts, p);
  192. __u32 prog_attach_flags[1024] = {0};
  193. const char *attach_flags_str;
  194. __u32 prog_ids[1024] = {0};
  195. char buf[32];
  196. __u32 iter;
  197. int ret;
  198. p.query_flags = query_flags;
  199. p.prog_cnt = ARRAY_SIZE(prog_ids);
  200. p.prog_ids = prog_ids;
  201. p.prog_attach_flags = prog_attach_flags;
  202. ret = bpf_prog_query_opts(cgroup_fd, type, &p);
  203. if (ret)
  204. return ret;
  205. if (p.prog_cnt == 0)
  206. return 0;
  207. for (iter = 0; iter < p.prog_cnt; iter++) {
  208. __u32 attach_flags;
  209. attach_flags = prog_attach_flags[iter] ?: p.attach_flags;
  210. switch (attach_flags) {
  211. case BPF_F_ALLOW_MULTI:
  212. attach_flags_str = "multi";
  213. break;
  214. case BPF_F_ALLOW_OVERRIDE:
  215. attach_flags_str = "override";
  216. break;
  217. case 0:
  218. attach_flags_str = "";
  219. break;
  220. default:
  221. snprintf(buf, sizeof(buf), "unknown(%x)", attach_flags);
  222. attach_flags_str = buf;
  223. }
  224. show_bpf_prog(prog_ids[iter], type,
  225. attach_flags_str, level);
  226. }
  227. return 0;
  228. }
  229. static int show_bpf_progs(int cgroup_fd, enum bpf_attach_type type,
  230. int level)
  231. {
  232. return query_flags & BPF_F_QUERY_EFFECTIVE ?
  233. show_effective_bpf_progs(cgroup_fd, type, level) :
  234. show_attached_bpf_progs(cgroup_fd, type, level);
  235. }
  236. static int do_show(int argc, char **argv)
  237. {
  238. enum bpf_attach_type type;
  239. int has_attached_progs;
  240. const char *path;
  241. int cgroup_fd;
  242. int ret = -1;
  243. query_flags = 0;
  244. if (!REQ_ARGS(1))
  245. return -1;
  246. path = GET_ARG();
  247. while (argc) {
  248. if (is_prefix(*argv, "effective")) {
  249. if (query_flags & BPF_F_QUERY_EFFECTIVE) {
  250. p_err("duplicated argument: %s", *argv);
  251. return -1;
  252. }
  253. query_flags |= BPF_F_QUERY_EFFECTIVE;
  254. NEXT_ARG();
  255. } else {
  256. p_err("expected no more arguments, 'effective', got: '%s'?",
  257. *argv);
  258. return -1;
  259. }
  260. }
  261. cgroup_fd = open(path, O_RDONLY);
  262. if (cgroup_fd < 0) {
  263. p_err("can't open cgroup %s", path);
  264. goto exit;
  265. }
  266. has_attached_progs = cgroup_has_attached_progs(cgroup_fd);
  267. if (has_attached_progs < 0) {
  268. p_err("can't query bpf programs attached to %s: %s",
  269. path, strerror(errno));
  270. goto exit_cgroup;
  271. } else if (!has_attached_progs) {
  272. ret = 0;
  273. goto exit_cgroup;
  274. }
  275. if (json_output)
  276. jsonw_start_array(json_wtr);
  277. else if (query_flags & BPF_F_QUERY_EFFECTIVE)
  278. printf("%-8s %-15s %-15s\n", "ID", "AttachType", "Name");
  279. else
  280. printf("%-8s %-15s %-15s %-15s\n", "ID", "AttachType",
  281. "AttachFlags", "Name");
  282. btf_vmlinux = libbpf_find_kernel_btf();
  283. for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++) {
  284. /*
  285. * Not all attach types may be supported, so it's expected,
  286. * that some requests will fail.
  287. * If we were able to get the show for at least one
  288. * attach type, let's return 0.
  289. */
  290. if (show_bpf_progs(cgroup_fd, type, 0) == 0)
  291. ret = 0;
  292. }
  293. if (json_output)
  294. jsonw_end_array(json_wtr);
  295. exit_cgroup:
  296. close(cgroup_fd);
  297. exit:
  298. return ret;
  299. }
  300. /*
  301. * To distinguish nftw() errors and do_show_tree_fn() errors
  302. * and avoid duplicating error messages, let's return -2
  303. * from do_show_tree_fn() in case of error.
  304. */
  305. #define NFTW_ERR -1
  306. #define SHOW_TREE_FN_ERR -2
  307. static int do_show_tree_fn(const char *fpath, const struct stat *sb,
  308. int typeflag, struct FTW *ftw)
  309. {
  310. enum bpf_attach_type type;
  311. int has_attached_progs;
  312. int cgroup_fd;
  313. if (typeflag != FTW_D)
  314. return 0;
  315. cgroup_fd = open(fpath, O_RDONLY);
  316. if (cgroup_fd < 0) {
  317. p_err("can't open cgroup %s: %s", fpath, strerror(errno));
  318. return SHOW_TREE_FN_ERR;
  319. }
  320. has_attached_progs = cgroup_has_attached_progs(cgroup_fd);
  321. if (has_attached_progs < 0) {
  322. p_err("can't query bpf programs attached to %s: %s",
  323. fpath, strerror(errno));
  324. close(cgroup_fd);
  325. return SHOW_TREE_FN_ERR;
  326. } else if (!has_attached_progs) {
  327. close(cgroup_fd);
  328. return 0;
  329. }
  330. if (json_output) {
  331. jsonw_start_object(json_wtr);
  332. jsonw_string_field(json_wtr, "cgroup", fpath);
  333. jsonw_name(json_wtr, "programs");
  334. jsonw_start_array(json_wtr);
  335. } else {
  336. printf("%s\n", fpath);
  337. }
  338. btf_vmlinux = libbpf_find_kernel_btf();
  339. for (type = 0; type < __MAX_BPF_ATTACH_TYPE; type++)
  340. show_bpf_progs(cgroup_fd, type, ftw->level);
  341. if (errno == EINVAL)
  342. /* Last attach type does not support query.
  343. * Do not report an error for this, especially because batch
  344. * mode would stop processing commands.
  345. */
  346. errno = 0;
  347. if (json_output) {
  348. jsonw_end_array(json_wtr);
  349. jsonw_end_object(json_wtr);
  350. }
  351. close(cgroup_fd);
  352. return 0;
  353. }
  354. static char *find_cgroup_root(void)
  355. {
  356. struct mntent *mnt;
  357. FILE *f;
  358. f = fopen("/proc/mounts", "r");
  359. if (f == NULL)
  360. return NULL;
  361. while ((mnt = getmntent(f))) {
  362. if (strcmp(mnt->mnt_type, "cgroup2") == 0) {
  363. fclose(f);
  364. return strdup(mnt->mnt_dir);
  365. }
  366. }
  367. fclose(f);
  368. return NULL;
  369. }
  370. static int do_show_tree(int argc, char **argv)
  371. {
  372. char *cgroup_root, *cgroup_alloced = NULL;
  373. int ret;
  374. query_flags = 0;
  375. if (!argc) {
  376. cgroup_alloced = find_cgroup_root();
  377. if (!cgroup_alloced) {
  378. p_err("cgroup v2 isn't mounted");
  379. return -1;
  380. }
  381. cgroup_root = cgroup_alloced;
  382. } else {
  383. cgroup_root = GET_ARG();
  384. while (argc) {
  385. if (is_prefix(*argv, "effective")) {
  386. if (query_flags & BPF_F_QUERY_EFFECTIVE) {
  387. p_err("duplicated argument: %s", *argv);
  388. return -1;
  389. }
  390. query_flags |= BPF_F_QUERY_EFFECTIVE;
  391. NEXT_ARG();
  392. } else {
  393. p_err("expected no more arguments, 'effective', got: '%s'?",
  394. *argv);
  395. return -1;
  396. }
  397. }
  398. }
  399. if (json_output)
  400. jsonw_start_array(json_wtr);
  401. else if (query_flags & BPF_F_QUERY_EFFECTIVE)
  402. printf("%s\n"
  403. "%-8s %-15s %-15s\n",
  404. "CgroupPath",
  405. "ID", "AttachType", "Name");
  406. else
  407. printf("%s\n"
  408. "%-8s %-15s %-15s %-15s\n",
  409. "CgroupPath",
  410. "ID", "AttachType", "AttachFlags", "Name");
  411. switch (nftw(cgroup_root, do_show_tree_fn, 1024, FTW_MOUNT)) {
  412. case NFTW_ERR:
  413. p_err("can't iterate over %s: %s", cgroup_root,
  414. strerror(errno));
  415. ret = -1;
  416. break;
  417. case SHOW_TREE_FN_ERR:
  418. ret = -1;
  419. break;
  420. default:
  421. ret = 0;
  422. }
  423. if (json_output)
  424. jsonw_end_array(json_wtr);
  425. free(cgroup_alloced);
  426. return ret;
  427. }
  428. static int do_attach(int argc, char **argv)
  429. {
  430. enum bpf_attach_type attach_type;
  431. int cgroup_fd, prog_fd;
  432. int attach_flags = 0;
  433. int ret = -1;
  434. int i;
  435. if (argc < 4) {
  436. p_err("too few parameters for cgroup attach");
  437. goto exit;
  438. }
  439. cgroup_fd = open(argv[0], O_RDONLY);
  440. if (cgroup_fd < 0) {
  441. p_err("can't open cgroup %s", argv[0]);
  442. goto exit;
  443. }
  444. attach_type = parse_attach_type(argv[1]);
  445. if (attach_type == __MAX_BPF_ATTACH_TYPE) {
  446. p_err("invalid attach type");
  447. goto exit_cgroup;
  448. }
  449. argc -= 2;
  450. argv = &argv[2];
  451. prog_fd = prog_parse_fd(&argc, &argv);
  452. if (prog_fd < 0)
  453. goto exit_cgroup;
  454. for (i = 0; i < argc; i++) {
  455. if (is_prefix(argv[i], "multi")) {
  456. attach_flags |= BPF_F_ALLOW_MULTI;
  457. } else if (is_prefix(argv[i], "override")) {
  458. attach_flags |= BPF_F_ALLOW_OVERRIDE;
  459. } else {
  460. p_err("unknown option: %s", argv[i]);
  461. goto exit_cgroup;
  462. }
  463. }
  464. if (bpf_prog_attach(prog_fd, cgroup_fd, attach_type, attach_flags)) {
  465. p_err("failed to attach program");
  466. goto exit_prog;
  467. }
  468. if (json_output)
  469. jsonw_null(json_wtr);
  470. ret = 0;
  471. exit_prog:
  472. close(prog_fd);
  473. exit_cgroup:
  474. close(cgroup_fd);
  475. exit:
  476. return ret;
  477. }
  478. static int do_detach(int argc, char **argv)
  479. {
  480. enum bpf_attach_type attach_type;
  481. int prog_fd, cgroup_fd;
  482. int ret = -1;
  483. if (argc < 4) {
  484. p_err("too few parameters for cgroup detach");
  485. goto exit;
  486. }
  487. cgroup_fd = open(argv[0], O_RDONLY);
  488. if (cgroup_fd < 0) {
  489. p_err("can't open cgroup %s", argv[0]);
  490. goto exit;
  491. }
  492. attach_type = parse_attach_type(argv[1]);
  493. if (attach_type == __MAX_BPF_ATTACH_TYPE) {
  494. p_err("invalid attach type");
  495. goto exit_cgroup;
  496. }
  497. argc -= 2;
  498. argv = &argv[2];
  499. prog_fd = prog_parse_fd(&argc, &argv);
  500. if (prog_fd < 0)
  501. goto exit_cgroup;
  502. if (bpf_prog_detach2(prog_fd, cgroup_fd, attach_type)) {
  503. p_err("failed to detach program");
  504. goto exit_prog;
  505. }
  506. if (json_output)
  507. jsonw_null(json_wtr);
  508. ret = 0;
  509. exit_prog:
  510. close(prog_fd);
  511. exit_cgroup:
  512. close(cgroup_fd);
  513. exit:
  514. return ret;
  515. }
  516. static int do_help(int argc, char **argv)
  517. {
  518. if (json_output) {
  519. jsonw_null(json_wtr);
  520. return 0;
  521. }
  522. fprintf(stderr,
  523. "Usage: %1$s %2$s { show | list } CGROUP [**effective**]\n"
  524. " %1$s %2$s tree [CGROUP_ROOT] [**effective**]\n"
  525. " %1$s %2$s attach CGROUP ATTACH_TYPE PROG [ATTACH_FLAGS]\n"
  526. " %1$s %2$s detach CGROUP ATTACH_TYPE PROG\n"
  527. " %1$s %2$s help\n"
  528. "\n"
  529. HELP_SPEC_ATTACH_TYPES "\n"
  530. " " HELP_SPEC_ATTACH_FLAGS "\n"
  531. " " HELP_SPEC_PROGRAM "\n"
  532. " " HELP_SPEC_OPTIONS " |\n"
  533. " {-f|--bpffs} }\n"
  534. "",
  535. bin_name, argv[-2]);
  536. return 0;
  537. }
  538. static const struct cmd cmds[] = {
  539. { "show", do_show },
  540. { "list", do_show },
  541. { "tree", do_show_tree },
  542. { "attach", do_attach },
  543. { "detach", do_detach },
  544. { "help", do_help },
  545. { 0 }
  546. };
  547. int do_cgroup(int argc, char **argv)
  548. {
  549. return cmd_select(cmds, argc, argv, do_help);
  550. }