test_sock.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. // SPDX-License-Identifier: GPL-2.0
  2. // Copyright (c) 2018 Facebook
  3. #include <stdio.h>
  4. #include <unistd.h>
  5. #include <arpa/inet.h>
  6. #include <sys/types.h>
  7. #include <sys/socket.h>
  8. #include <linux/filter.h>
  9. #include <bpf/bpf.h>
  10. #include "cgroup_helpers.h"
  11. #include "bpf_rlimit.h"
  12. #include "bpf_util.h"
  13. #define CG_PATH "/foo"
  14. #define MAX_INSNS 512
  15. char bpf_log_buf[BPF_LOG_BUF_SIZE];
  16. struct sock_test {
  17. const char *descr;
  18. /* BPF prog properties */
  19. struct bpf_insn insns[MAX_INSNS];
  20. enum bpf_attach_type expected_attach_type;
  21. enum bpf_attach_type attach_type;
  22. /* Socket properties */
  23. int domain;
  24. int type;
  25. /* Endpoint to bind() to */
  26. const char *ip;
  27. unsigned short port;
  28. /* Expected test result */
  29. enum {
  30. LOAD_REJECT,
  31. ATTACH_REJECT,
  32. BIND_REJECT,
  33. SUCCESS,
  34. } result;
  35. };
  36. static struct sock_test tests[] = {
  37. {
  38. "bind4 load with invalid access: src_ip6",
  39. .insns = {
  40. BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
  41. BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
  42. offsetof(struct bpf_sock, src_ip6[0])),
  43. BPF_MOV64_IMM(BPF_REG_0, 1),
  44. BPF_EXIT_INSN(),
  45. },
  46. BPF_CGROUP_INET4_POST_BIND,
  47. BPF_CGROUP_INET4_POST_BIND,
  48. 0,
  49. 0,
  50. NULL,
  51. 0,
  52. LOAD_REJECT,
  53. },
  54. {
  55. "bind4 load with invalid access: mark",
  56. .insns = {
  57. BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
  58. BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
  59. offsetof(struct bpf_sock, mark)),
  60. BPF_MOV64_IMM(BPF_REG_0, 1),
  61. BPF_EXIT_INSN(),
  62. },
  63. BPF_CGROUP_INET4_POST_BIND,
  64. BPF_CGROUP_INET4_POST_BIND,
  65. 0,
  66. 0,
  67. NULL,
  68. 0,
  69. LOAD_REJECT,
  70. },
  71. {
  72. "bind6 load with invalid access: src_ip4",
  73. .insns = {
  74. BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
  75. BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
  76. offsetof(struct bpf_sock, src_ip4)),
  77. BPF_MOV64_IMM(BPF_REG_0, 1),
  78. BPF_EXIT_INSN(),
  79. },
  80. BPF_CGROUP_INET6_POST_BIND,
  81. BPF_CGROUP_INET6_POST_BIND,
  82. 0,
  83. 0,
  84. NULL,
  85. 0,
  86. LOAD_REJECT,
  87. },
  88. {
  89. "sock_create load with invalid access: src_port",
  90. .insns = {
  91. BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
  92. BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
  93. offsetof(struct bpf_sock, src_port)),
  94. BPF_MOV64_IMM(BPF_REG_0, 1),
  95. BPF_EXIT_INSN(),
  96. },
  97. BPF_CGROUP_INET_SOCK_CREATE,
  98. BPF_CGROUP_INET_SOCK_CREATE,
  99. 0,
  100. 0,
  101. NULL,
  102. 0,
  103. LOAD_REJECT,
  104. },
  105. {
  106. "sock_create load w/o expected_attach_type (compat mode)",
  107. .insns = {
  108. BPF_MOV64_IMM(BPF_REG_0, 1),
  109. BPF_EXIT_INSN(),
  110. },
  111. 0,
  112. BPF_CGROUP_INET_SOCK_CREATE,
  113. AF_INET,
  114. SOCK_STREAM,
  115. "127.0.0.1",
  116. 8097,
  117. SUCCESS,
  118. },
  119. {
  120. "sock_create load w/ expected_attach_type",
  121. .insns = {
  122. BPF_MOV64_IMM(BPF_REG_0, 1),
  123. BPF_EXIT_INSN(),
  124. },
  125. BPF_CGROUP_INET_SOCK_CREATE,
  126. BPF_CGROUP_INET_SOCK_CREATE,
  127. AF_INET,
  128. SOCK_STREAM,
  129. "127.0.0.1",
  130. 8097,
  131. SUCCESS,
  132. },
  133. {
  134. "attach type mismatch bind4 vs bind6",
  135. .insns = {
  136. BPF_MOV64_IMM(BPF_REG_0, 1),
  137. BPF_EXIT_INSN(),
  138. },
  139. BPF_CGROUP_INET4_POST_BIND,
  140. BPF_CGROUP_INET6_POST_BIND,
  141. 0,
  142. 0,
  143. NULL,
  144. 0,
  145. ATTACH_REJECT,
  146. },
  147. {
  148. "attach type mismatch bind6 vs bind4",
  149. .insns = {
  150. BPF_MOV64_IMM(BPF_REG_0, 1),
  151. BPF_EXIT_INSN(),
  152. },
  153. BPF_CGROUP_INET6_POST_BIND,
  154. BPF_CGROUP_INET4_POST_BIND,
  155. 0,
  156. 0,
  157. NULL,
  158. 0,
  159. ATTACH_REJECT,
  160. },
  161. {
  162. "attach type mismatch default vs bind4",
  163. .insns = {
  164. BPF_MOV64_IMM(BPF_REG_0, 1),
  165. BPF_EXIT_INSN(),
  166. },
  167. 0,
  168. BPF_CGROUP_INET4_POST_BIND,
  169. 0,
  170. 0,
  171. NULL,
  172. 0,
  173. ATTACH_REJECT,
  174. },
  175. {
  176. "attach type mismatch bind6 vs sock_create",
  177. .insns = {
  178. BPF_MOV64_IMM(BPF_REG_0, 1),
  179. BPF_EXIT_INSN(),
  180. },
  181. BPF_CGROUP_INET6_POST_BIND,
  182. BPF_CGROUP_INET_SOCK_CREATE,
  183. 0,
  184. 0,
  185. NULL,
  186. 0,
  187. ATTACH_REJECT,
  188. },
  189. {
  190. "bind4 reject all",
  191. .insns = {
  192. BPF_MOV64_IMM(BPF_REG_0, 0),
  193. BPF_EXIT_INSN(),
  194. },
  195. BPF_CGROUP_INET4_POST_BIND,
  196. BPF_CGROUP_INET4_POST_BIND,
  197. AF_INET,
  198. SOCK_STREAM,
  199. "0.0.0.0",
  200. 0,
  201. BIND_REJECT,
  202. },
  203. {
  204. "bind6 reject all",
  205. .insns = {
  206. BPF_MOV64_IMM(BPF_REG_0, 0),
  207. BPF_EXIT_INSN(),
  208. },
  209. BPF_CGROUP_INET6_POST_BIND,
  210. BPF_CGROUP_INET6_POST_BIND,
  211. AF_INET6,
  212. SOCK_STREAM,
  213. "::",
  214. 0,
  215. BIND_REJECT,
  216. },
  217. {
  218. "bind6 deny specific IP & port",
  219. .insns = {
  220. BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
  221. /* if (ip == expected && port == expected) */
  222. BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
  223. offsetof(struct bpf_sock, src_ip6[3])),
  224. BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x01000000, 4),
  225. BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
  226. offsetof(struct bpf_sock, src_port)),
  227. BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x2001, 2),
  228. /* return DENY; */
  229. BPF_MOV64_IMM(BPF_REG_0, 0),
  230. BPF_JMP_A(1),
  231. /* else return ALLOW; */
  232. BPF_MOV64_IMM(BPF_REG_0, 1),
  233. BPF_EXIT_INSN(),
  234. },
  235. BPF_CGROUP_INET6_POST_BIND,
  236. BPF_CGROUP_INET6_POST_BIND,
  237. AF_INET6,
  238. SOCK_STREAM,
  239. "::1",
  240. 8193,
  241. BIND_REJECT,
  242. },
  243. {
  244. "bind4 allow specific IP & port",
  245. .insns = {
  246. BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
  247. /* if (ip == expected && port == expected) */
  248. BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
  249. offsetof(struct bpf_sock, src_ip4)),
  250. BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x0100007F, 4),
  251. BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_6,
  252. offsetof(struct bpf_sock, src_port)),
  253. BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 0x1002, 2),
  254. /* return ALLOW; */
  255. BPF_MOV64_IMM(BPF_REG_0, 1),
  256. BPF_JMP_A(1),
  257. /* else return DENY; */
  258. BPF_MOV64_IMM(BPF_REG_0, 0),
  259. BPF_EXIT_INSN(),
  260. },
  261. BPF_CGROUP_INET4_POST_BIND,
  262. BPF_CGROUP_INET4_POST_BIND,
  263. AF_INET,
  264. SOCK_STREAM,
  265. "127.0.0.1",
  266. 4098,
  267. SUCCESS,
  268. },
  269. {
  270. "bind4 allow all",
  271. .insns = {
  272. BPF_MOV64_IMM(BPF_REG_0, 1),
  273. BPF_EXIT_INSN(),
  274. },
  275. BPF_CGROUP_INET4_POST_BIND,
  276. BPF_CGROUP_INET4_POST_BIND,
  277. AF_INET,
  278. SOCK_STREAM,
  279. "0.0.0.0",
  280. 0,
  281. SUCCESS,
  282. },
  283. {
  284. "bind6 allow all",
  285. .insns = {
  286. BPF_MOV64_IMM(BPF_REG_0, 1),
  287. BPF_EXIT_INSN(),
  288. },
  289. BPF_CGROUP_INET6_POST_BIND,
  290. BPF_CGROUP_INET6_POST_BIND,
  291. AF_INET6,
  292. SOCK_STREAM,
  293. "::",
  294. 0,
  295. SUCCESS,
  296. },
  297. };
  298. static size_t probe_prog_length(const struct bpf_insn *fp)
  299. {
  300. size_t len;
  301. for (len = MAX_INSNS - 1; len > 0; --len)
  302. if (fp[len].code != 0 || fp[len].imm != 0)
  303. break;
  304. return len + 1;
  305. }
  306. static int load_sock_prog(const struct bpf_insn *prog,
  307. enum bpf_attach_type attach_type)
  308. {
  309. struct bpf_load_program_attr attr;
  310. memset(&attr, 0, sizeof(struct bpf_load_program_attr));
  311. attr.prog_type = BPF_PROG_TYPE_CGROUP_SOCK;
  312. attr.expected_attach_type = attach_type;
  313. attr.insns = prog;
  314. attr.insns_cnt = probe_prog_length(attr.insns);
  315. attr.license = "GPL";
  316. return bpf_load_program_xattr(&attr, bpf_log_buf, BPF_LOG_BUF_SIZE);
  317. }
  318. static int attach_sock_prog(int cgfd, int progfd,
  319. enum bpf_attach_type attach_type)
  320. {
  321. return bpf_prog_attach(progfd, cgfd, attach_type, BPF_F_ALLOW_OVERRIDE);
  322. }
  323. static int bind_sock(int domain, int type, const char *ip, unsigned short port)
  324. {
  325. struct sockaddr_storage addr;
  326. struct sockaddr_in6 *addr6;
  327. struct sockaddr_in *addr4;
  328. int sockfd = -1;
  329. socklen_t len;
  330. int err = 0;
  331. sockfd = socket(domain, type, 0);
  332. if (sockfd < 0)
  333. goto err;
  334. memset(&addr, 0, sizeof(addr));
  335. if (domain == AF_INET) {
  336. len = sizeof(struct sockaddr_in);
  337. addr4 = (struct sockaddr_in *)&addr;
  338. addr4->sin_family = domain;
  339. addr4->sin_port = htons(port);
  340. if (inet_pton(domain, ip, (void *)&addr4->sin_addr) != 1)
  341. goto err;
  342. } else if (domain == AF_INET6) {
  343. len = sizeof(struct sockaddr_in6);
  344. addr6 = (struct sockaddr_in6 *)&addr;
  345. addr6->sin6_family = domain;
  346. addr6->sin6_port = htons(port);
  347. if (inet_pton(domain, ip, (void *)&addr6->sin6_addr) != 1)
  348. goto err;
  349. } else {
  350. goto err;
  351. }
  352. if (bind(sockfd, (const struct sockaddr *)&addr, len) == -1)
  353. goto err;
  354. goto out;
  355. err:
  356. err = -1;
  357. out:
  358. close(sockfd);
  359. return err;
  360. }
  361. static int run_test_case(int cgfd, const struct sock_test *test)
  362. {
  363. int progfd = -1;
  364. int err = 0;
  365. printf("Test case: %s .. ", test->descr);
  366. progfd = load_sock_prog(test->insns, test->expected_attach_type);
  367. if (progfd < 0) {
  368. if (test->result == LOAD_REJECT)
  369. goto out;
  370. else
  371. goto err;
  372. }
  373. if (attach_sock_prog(cgfd, progfd, test->attach_type) == -1) {
  374. if (test->result == ATTACH_REJECT)
  375. goto out;
  376. else
  377. goto err;
  378. }
  379. if (bind_sock(test->domain, test->type, test->ip, test->port) == -1) {
  380. /* sys_bind() may fail for different reasons, errno has to be
  381. * checked to confirm that BPF program rejected it.
  382. */
  383. if (test->result == BIND_REJECT && errno == EPERM)
  384. goto out;
  385. else
  386. goto err;
  387. }
  388. if (test->result != SUCCESS)
  389. goto err;
  390. goto out;
  391. err:
  392. err = -1;
  393. out:
  394. /* Detaching w/o checking return code: best effort attempt. */
  395. if (progfd != -1)
  396. bpf_prog_detach(cgfd, test->attach_type);
  397. close(progfd);
  398. printf("[%s]\n", err ? "FAIL" : "PASS");
  399. return err;
  400. }
  401. static int run_tests(int cgfd)
  402. {
  403. int passes = 0;
  404. int fails = 0;
  405. int i;
  406. for (i = 0; i < ARRAY_SIZE(tests); ++i) {
  407. if (run_test_case(cgfd, &tests[i]))
  408. ++fails;
  409. else
  410. ++passes;
  411. }
  412. printf("Summary: %d PASSED, %d FAILED\n", passes, fails);
  413. return fails ? -1 : 0;
  414. }
  415. int main(int argc, char **argv)
  416. {
  417. int cgfd = -1;
  418. int err = 0;
  419. if (setup_cgroup_environment())
  420. goto err;
  421. cgfd = create_and_get_cgroup(CG_PATH);
  422. if (!cgfd)
  423. goto err;
  424. if (join_cgroup(CG_PATH))
  425. goto err;
  426. if (run_tests(cgfd))
  427. goto err;
  428. goto out;
  429. err:
  430. err = -1;
  431. out:
  432. close(cgfd);
  433. cleanup_cgroup_environment();
  434. return err;
  435. }