test_cgrp2_sock.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  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. struct bpf_insn *prog;
  64. size_t insns_cnt;
  65. void *p;
  66. int ret;
  67. insns_cnt = sizeof(prog_start) + sizeof(prog_end);
  68. if (idx)
  69. insns_cnt += sizeof(prog_dev);
  70. if (mark)
  71. insns_cnt += sizeof(prog_mark);
  72. if (prio)
  73. insns_cnt += sizeof(prog_prio);
  74. p = prog = malloc(insns_cnt);
  75. if (!prog) {
  76. fprintf(stderr, "Failed to allocate memory for instructions\n");
  77. return EXIT_FAILURE;
  78. }
  79. memcpy(p, prog_start, sizeof(prog_start));
  80. p += sizeof(prog_start);
  81. if (idx) {
  82. memcpy(p, prog_dev, sizeof(prog_dev));
  83. p += sizeof(prog_dev);
  84. }
  85. if (mark) {
  86. memcpy(p, prog_mark, sizeof(prog_mark));
  87. p += sizeof(prog_mark);
  88. }
  89. if (prio) {
  90. memcpy(p, prog_prio, sizeof(prog_prio));
  91. p += sizeof(prog_prio);
  92. }
  93. memcpy(p, prog_end, sizeof(prog_end));
  94. p += sizeof(prog_end);
  95. insns_cnt /= sizeof(struct bpf_insn);
  96. ret = bpf_load_program(BPF_PROG_TYPE_CGROUP_SOCK, prog, insns_cnt,
  97. "GPL", 0, bpf_log_buf, BPF_LOG_BUF_SIZE);
  98. free(prog);
  99. return ret;
  100. }
  101. static int get_bind_to_device(int sd, char *name, size_t len)
  102. {
  103. socklen_t optlen = len;
  104. int rc;
  105. name[0] = '\0';
  106. rc = getsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, name, &optlen);
  107. if (rc < 0)
  108. perror("setsockopt(SO_BINDTODEVICE)");
  109. return rc;
  110. }
  111. static unsigned int get_somark(int sd)
  112. {
  113. unsigned int mark = 0;
  114. socklen_t optlen = sizeof(mark);
  115. int rc;
  116. rc = getsockopt(sd, SOL_SOCKET, SO_MARK, &mark, &optlen);
  117. if (rc < 0)
  118. perror("getsockopt(SO_MARK)");
  119. return mark;
  120. }
  121. static unsigned int get_priority(int sd)
  122. {
  123. unsigned int prio = 0;
  124. socklen_t optlen = sizeof(prio);
  125. int rc;
  126. rc = getsockopt(sd, SOL_SOCKET, SO_PRIORITY, &prio, &optlen);
  127. if (rc < 0)
  128. perror("getsockopt(SO_PRIORITY)");
  129. return prio;
  130. }
  131. static int show_sockopts(int family)
  132. {
  133. unsigned int mark, prio;
  134. char name[16];
  135. int sd;
  136. sd = socket(family, SOCK_DGRAM, 17);
  137. if (sd < 0) {
  138. perror("socket");
  139. return 1;
  140. }
  141. if (get_bind_to_device(sd, name, sizeof(name)) < 0)
  142. return 1;
  143. mark = get_somark(sd);
  144. prio = get_priority(sd);
  145. close(sd);
  146. printf("sd %d: dev %s, mark %u, priority %u\n", sd, name, mark, prio);
  147. return 0;
  148. }
  149. static int usage(const char *argv0)
  150. {
  151. printf("Usage:\n");
  152. printf(" Attach a program\n");
  153. printf(" %s -b bind-to-dev -m mark -p prio cg-path\n", argv0);
  154. printf("\n");
  155. printf(" Detach a program\n");
  156. printf(" %s -d cg-path\n", argv0);
  157. printf("\n");
  158. printf(" Show inherited socket settings (mark, priority, and device)\n");
  159. printf(" %s [-6]\n", argv0);
  160. return EXIT_FAILURE;
  161. }
  162. int main(int argc, char **argv)
  163. {
  164. __u32 idx = 0, mark = 0, prio = 0;
  165. const char *cgrp_path = NULL;
  166. int cg_fd, prog_fd, ret;
  167. int family = PF_INET;
  168. int do_attach = 1;
  169. int rc;
  170. while ((rc = getopt(argc, argv, "db:m:p:6")) != -1) {
  171. switch (rc) {
  172. case 'd':
  173. do_attach = 0;
  174. break;
  175. case 'b':
  176. idx = if_nametoindex(optarg);
  177. if (!idx) {
  178. idx = strtoumax(optarg, NULL, 0);
  179. if (!idx) {
  180. printf("Invalid device name\n");
  181. return EXIT_FAILURE;
  182. }
  183. }
  184. break;
  185. case 'm':
  186. mark = strtoumax(optarg, NULL, 0);
  187. break;
  188. case 'p':
  189. prio = strtoumax(optarg, NULL, 0);
  190. break;
  191. case '6':
  192. family = PF_INET6;
  193. break;
  194. default:
  195. return usage(argv[0]);
  196. }
  197. }
  198. if (optind == argc)
  199. return show_sockopts(family);
  200. cgrp_path = argv[optind];
  201. if (!cgrp_path) {
  202. fprintf(stderr, "cgroup path not given\n");
  203. return EXIT_FAILURE;
  204. }
  205. if (do_attach && !idx && !mark && !prio) {
  206. fprintf(stderr,
  207. "One of device, mark or priority must be given\n");
  208. return EXIT_FAILURE;
  209. }
  210. cg_fd = open(cgrp_path, O_DIRECTORY | O_RDONLY);
  211. if (cg_fd < 0) {
  212. printf("Failed to open cgroup path: '%s'\n", strerror(errno));
  213. return EXIT_FAILURE;
  214. }
  215. if (do_attach) {
  216. prog_fd = prog_load(idx, mark, prio);
  217. if (prog_fd < 0) {
  218. printf("Failed to load prog: '%s'\n", strerror(errno));
  219. printf("Output from kernel verifier:\n%s\n-------\n",
  220. bpf_log_buf);
  221. return EXIT_FAILURE;
  222. }
  223. ret = bpf_prog_attach(prog_fd, cg_fd,
  224. BPF_CGROUP_INET_SOCK_CREATE, 0);
  225. if (ret < 0) {
  226. printf("Failed to attach prog to cgroup: '%s'\n",
  227. strerror(errno));
  228. return EXIT_FAILURE;
  229. }
  230. } else {
  231. ret = bpf_prog_detach(cg_fd, BPF_CGROUP_INET_SOCK_CREATE);
  232. if (ret < 0) {
  233. printf("Failed to detach prog from cgroup: '%s'\n",
  234. strerror(errno));
  235. return EXIT_FAILURE;
  236. }
  237. }
  238. close(cg_fd);
  239. return EXIT_SUCCESS;
  240. }