test_cgrp2_sock.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  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. #define _GNU_SOURCE
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <stddef.h>
  14. #include <string.h>
  15. #include <unistd.h>
  16. #include <assert.h>
  17. #include <errno.h>
  18. #include <fcntl.h>
  19. #include <net/if.h>
  20. #include <inttypes.h>
  21. #include <linux/bpf.h>
  22. #include <bpf/bpf.h>
  23. #include "bpf_insn.h"
  24. char bpf_log_buf[BPF_LOG_BUF_SIZE];
  25. static int prog_load(__u32 idx, __u32 mark, __u32 prio)
  26. {
  27. /* save pointer to context */
  28. struct bpf_insn prog_start[] = {
  29. BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
  30. };
  31. struct bpf_insn prog_end[] = {
  32. BPF_MOV64_IMM(BPF_REG_0, 1), /* r0 = verdict */
  33. BPF_EXIT_INSN(),
  34. };
  35. /* set sk_bound_dev_if on socket */
  36. struct bpf_insn prog_dev[] = {
  37. BPF_MOV64_IMM(BPF_REG_3, idx),
  38. BPF_MOV64_IMM(BPF_REG_2, offsetof(struct bpf_sock, bound_dev_if)),
  39. BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, offsetof(struct bpf_sock, bound_dev_if)),
  40. };
  41. /* set mark on socket */
  42. struct bpf_insn prog_mark[] = {
  43. /* get uid of process */
  44. BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
  45. BPF_FUNC_get_current_uid_gid),
  46. BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 0xffffffff),
  47. /* if uid is 0, use given mark, else use the uid as the mark */
  48. BPF_MOV64_REG(BPF_REG_3, BPF_REG_0),
  49. BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
  50. BPF_MOV64_IMM(BPF_REG_3, mark),
  51. /* set the mark on the new socket */
  52. BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
  53. BPF_MOV64_IMM(BPF_REG_2, offsetof(struct bpf_sock, mark)),
  54. BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, offsetof(struct bpf_sock, mark)),
  55. };
  56. /* set priority on socket */
  57. struct bpf_insn prog_prio[] = {
  58. BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
  59. BPF_MOV64_IMM(BPF_REG_3, prio),
  60. BPF_MOV64_IMM(BPF_REG_2, offsetof(struct bpf_sock, priority)),
  61. BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_3, offsetof(struct bpf_sock, priority)),
  62. };
  63. LIBBPF_OPTS(bpf_prog_load_opts, opts,
  64. .log_buf = bpf_log_buf,
  65. .log_size = BPF_LOG_BUF_SIZE,
  66. );
  67. struct bpf_insn *prog;
  68. size_t insns_cnt;
  69. void *p;
  70. int ret;
  71. insns_cnt = sizeof(prog_start) + sizeof(prog_end);
  72. if (idx)
  73. insns_cnt += sizeof(prog_dev);
  74. if (mark)
  75. insns_cnt += sizeof(prog_mark);
  76. if (prio)
  77. insns_cnt += sizeof(prog_prio);
  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. memcpy(p, prog_start, sizeof(prog_start));
  84. p += sizeof(prog_start);
  85. if (idx) {
  86. memcpy(p, prog_dev, sizeof(prog_dev));
  87. p += sizeof(prog_dev);
  88. }
  89. if (mark) {
  90. memcpy(p, prog_mark, sizeof(prog_mark));
  91. p += sizeof(prog_mark);
  92. }
  93. if (prio) {
  94. memcpy(p, prog_prio, sizeof(prog_prio));
  95. p += sizeof(prog_prio);
  96. }
  97. memcpy(p, prog_end, sizeof(prog_end));
  98. p += sizeof(prog_end);
  99. insns_cnt /= sizeof(struct bpf_insn);
  100. ret = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, NULL, "GPL",
  101. prog, insns_cnt, &opts);
  102. free(prog);
  103. return ret;
  104. }
  105. static int get_bind_to_device(int sd, char *name, size_t len)
  106. {
  107. socklen_t optlen = len;
  108. int rc;
  109. name[0] = '\0';
  110. rc = getsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, name, &optlen);
  111. if (rc < 0)
  112. perror("setsockopt(SO_BINDTODEVICE)");
  113. return rc;
  114. }
  115. static unsigned int get_somark(int sd)
  116. {
  117. unsigned int mark = 0;
  118. socklen_t optlen = sizeof(mark);
  119. int rc;
  120. rc = getsockopt(sd, SOL_SOCKET, SO_MARK, &mark, &optlen);
  121. if (rc < 0)
  122. perror("getsockopt(SO_MARK)");
  123. return mark;
  124. }
  125. static unsigned int get_priority(int sd)
  126. {
  127. unsigned int prio = 0;
  128. socklen_t optlen = sizeof(prio);
  129. int rc;
  130. rc = getsockopt(sd, SOL_SOCKET, SO_PRIORITY, &prio, &optlen);
  131. if (rc < 0)
  132. perror("getsockopt(SO_PRIORITY)");
  133. return prio;
  134. }
  135. static int show_sockopts(int family)
  136. {
  137. unsigned int mark, prio;
  138. char name[16];
  139. int sd;
  140. sd = socket(family, SOCK_DGRAM, 17);
  141. if (sd < 0) {
  142. perror("socket");
  143. return 1;
  144. }
  145. if (get_bind_to_device(sd, name, sizeof(name)) < 0)
  146. return 1;
  147. mark = get_somark(sd);
  148. prio = get_priority(sd);
  149. close(sd);
  150. printf("sd %d: dev %s, mark %u, priority %u\n", sd, name, mark, prio);
  151. return 0;
  152. }
  153. static int usage(const char *argv0)
  154. {
  155. printf("Usage:\n");
  156. printf(" Attach a program\n");
  157. printf(" %s -b bind-to-dev -m mark -p prio cg-path\n", argv0);
  158. printf("\n");
  159. printf(" Detach a program\n");
  160. printf(" %s -d cg-path\n", argv0);
  161. printf("\n");
  162. printf(" Show inherited socket settings (mark, priority, and device)\n");
  163. printf(" %s [-6]\n", argv0);
  164. return EXIT_FAILURE;
  165. }
  166. int main(int argc, char **argv)
  167. {
  168. __u32 idx = 0, mark = 0, prio = 0;
  169. const char *cgrp_path = NULL;
  170. int cg_fd, prog_fd, ret;
  171. int family = PF_INET;
  172. int do_attach = 1;
  173. int rc;
  174. while ((rc = getopt(argc, argv, "db:m:p:6")) != -1) {
  175. switch (rc) {
  176. case 'd':
  177. do_attach = 0;
  178. break;
  179. case 'b':
  180. idx = if_nametoindex(optarg);
  181. if (!idx) {
  182. idx = strtoumax(optarg, NULL, 0);
  183. if (!idx) {
  184. printf("Invalid device name\n");
  185. return EXIT_FAILURE;
  186. }
  187. }
  188. break;
  189. case 'm':
  190. mark = strtoumax(optarg, NULL, 0);
  191. break;
  192. case 'p':
  193. prio = strtoumax(optarg, NULL, 0);
  194. break;
  195. case '6':
  196. family = PF_INET6;
  197. break;
  198. default:
  199. return usage(argv[0]);
  200. }
  201. }
  202. if (optind == argc)
  203. return show_sockopts(family);
  204. cgrp_path = argv[optind];
  205. if (!cgrp_path) {
  206. fprintf(stderr, "cgroup path not given\n");
  207. return EXIT_FAILURE;
  208. }
  209. if (do_attach && !idx && !mark && !prio) {
  210. fprintf(stderr,
  211. "One of device, mark or priority must be given\n");
  212. return EXIT_FAILURE;
  213. }
  214. cg_fd = open(cgrp_path, O_DIRECTORY | O_RDONLY);
  215. if (cg_fd < 0) {
  216. printf("Failed to open cgroup path: '%s'\n", strerror(errno));
  217. return EXIT_FAILURE;
  218. }
  219. if (do_attach) {
  220. prog_fd = prog_load(idx, mark, prio);
  221. if (prog_fd < 0) {
  222. printf("Failed to load prog: '%s'\n", strerror(errno));
  223. printf("Output from kernel verifier:\n%s\n-------\n",
  224. bpf_log_buf);
  225. return EXIT_FAILURE;
  226. }
  227. ret = bpf_prog_attach(prog_fd, cg_fd,
  228. BPF_CGROUP_INET_SOCK_CREATE, 0);
  229. if (ret < 0) {
  230. printf("Failed to attach prog to cgroup: '%s'\n",
  231. strerror(errno));
  232. return EXIT_FAILURE;
  233. }
  234. } else {
  235. ret = bpf_prog_detach(cg_fd, BPF_CGROUP_INET_SOCK_CREATE);
  236. if (ret < 0) {
  237. printf("Failed to detach prog from cgroup: '%s'\n",
  238. strerror(errno));
  239. return EXIT_FAILURE;
  240. }
  241. }
  242. close(cg_fd);
  243. return EXIT_SUCCESS;
  244. }