test_sock.c 9.1 KB

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