test_select_reuseport_kern.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. // SPDX-License-Identifier: GPL-2.0
  2. /* Copyright (c) 2018 Facebook */
  3. #include <stdlib.h>
  4. #include <linux/in.h>
  5. #include <linux/ip.h>
  6. #include <linux/ipv6.h>
  7. #include <linux/tcp.h>
  8. #include <linux/udp.h>
  9. #include <linux/bpf.h>
  10. #include <linux/types.h>
  11. #include <linux/if_ether.h>
  12. #include "bpf_endian.h"
  13. #include "bpf_helpers.h"
  14. #include "test_select_reuseport_common.h"
  15. int _version SEC("version") = 1;
  16. #ifndef offsetof
  17. #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
  18. #endif
  19. struct bpf_map_def SEC("maps") outer_map = {
  20. .type = BPF_MAP_TYPE_ARRAY_OF_MAPS,
  21. .key_size = sizeof(__u32),
  22. .value_size = sizeof(__u32),
  23. .max_entries = 1,
  24. };
  25. struct bpf_map_def SEC("maps") result_map = {
  26. .type = BPF_MAP_TYPE_ARRAY,
  27. .key_size = sizeof(__u32),
  28. .value_size = sizeof(__u32),
  29. .max_entries = NR_RESULTS,
  30. };
  31. struct bpf_map_def SEC("maps") tmp_index_ovr_map = {
  32. .type = BPF_MAP_TYPE_ARRAY,
  33. .key_size = sizeof(__u32),
  34. .value_size = sizeof(int),
  35. .max_entries = 1,
  36. };
  37. struct bpf_map_def SEC("maps") linum_map = {
  38. .type = BPF_MAP_TYPE_ARRAY,
  39. .key_size = sizeof(__u32),
  40. .value_size = sizeof(__u32),
  41. .max_entries = 1,
  42. };
  43. struct bpf_map_def SEC("maps") data_check_map = {
  44. .type = BPF_MAP_TYPE_ARRAY,
  45. .key_size = sizeof(__u32),
  46. .value_size = sizeof(struct data_check),
  47. .max_entries = 1,
  48. };
  49. #define GOTO_DONE(_result) ({ \
  50. result = (_result); \
  51. linum = __LINE__; \
  52. goto done; \
  53. })
  54. SEC("select_by_skb_data")
  55. int _select_by_skb_data(struct sk_reuseport_md *reuse_md)
  56. {
  57. __u32 linum, index = 0, flags = 0, index_zero = 0;
  58. __u32 *result_cnt, *linum_value;
  59. struct data_check data_check = {};
  60. struct cmd *cmd, cmd_copy;
  61. void *data, *data_end;
  62. void *reuseport_array;
  63. enum result result;
  64. int *index_ovr;
  65. int err;
  66. data = reuse_md->data;
  67. data_end = reuse_md->data_end;
  68. data_check.len = reuse_md->len;
  69. data_check.eth_protocol = reuse_md->eth_protocol;
  70. data_check.ip_protocol = reuse_md->ip_protocol;
  71. data_check.hash = reuse_md->hash;
  72. data_check.bind_inany = reuse_md->bind_inany;
  73. if (data_check.eth_protocol == bpf_htons(ETH_P_IP)) {
  74. if (bpf_skb_load_bytes_relative(reuse_md,
  75. offsetof(struct iphdr, saddr),
  76. data_check.skb_addrs, 8,
  77. BPF_HDR_START_NET))
  78. GOTO_DONE(DROP_MISC);
  79. } else {
  80. if (bpf_skb_load_bytes_relative(reuse_md,
  81. offsetof(struct ipv6hdr, saddr),
  82. data_check.skb_addrs, 32,
  83. BPF_HDR_START_NET))
  84. GOTO_DONE(DROP_MISC);
  85. }
  86. /*
  87. * The ip_protocol could be a compile time decision
  88. * if the bpf_prog.o is dedicated to either TCP or
  89. * UDP.
  90. *
  91. * Otherwise, reuse_md->ip_protocol or
  92. * the protocol field in the iphdr can be used.
  93. */
  94. if (data_check.ip_protocol == IPPROTO_TCP) {
  95. struct tcphdr *th = data;
  96. if (th + 1 > data_end)
  97. GOTO_DONE(DROP_MISC);
  98. data_check.skb_ports[0] = th->source;
  99. data_check.skb_ports[1] = th->dest;
  100. if ((th->doff << 2) + sizeof(*cmd) > data_check.len)
  101. GOTO_DONE(DROP_ERR_SKB_DATA);
  102. if (bpf_skb_load_bytes(reuse_md, th->doff << 2, &cmd_copy,
  103. sizeof(cmd_copy)))
  104. GOTO_DONE(DROP_MISC);
  105. cmd = &cmd_copy;
  106. } else if (data_check.ip_protocol == IPPROTO_UDP) {
  107. struct udphdr *uh = data;
  108. if (uh + 1 > data_end)
  109. GOTO_DONE(DROP_MISC);
  110. data_check.skb_ports[0] = uh->source;
  111. data_check.skb_ports[1] = uh->dest;
  112. if (sizeof(struct udphdr) + sizeof(*cmd) > data_check.len)
  113. GOTO_DONE(DROP_ERR_SKB_DATA);
  114. if (data + sizeof(struct udphdr) + sizeof(*cmd) > data_end) {
  115. if (bpf_skb_load_bytes(reuse_md, sizeof(struct udphdr),
  116. &cmd_copy, sizeof(cmd_copy)))
  117. GOTO_DONE(DROP_MISC);
  118. cmd = &cmd_copy;
  119. } else {
  120. cmd = data + sizeof(struct udphdr);
  121. }
  122. } else {
  123. GOTO_DONE(DROP_MISC);
  124. }
  125. reuseport_array = bpf_map_lookup_elem(&outer_map, &index_zero);
  126. if (!reuseport_array)
  127. GOTO_DONE(DROP_ERR_INNER_MAP);
  128. index = cmd->reuseport_index;
  129. index_ovr = bpf_map_lookup_elem(&tmp_index_ovr_map, &index_zero);
  130. if (!index_ovr)
  131. GOTO_DONE(DROP_MISC);
  132. if (*index_ovr != -1) {
  133. index = *index_ovr;
  134. *index_ovr = -1;
  135. }
  136. err = bpf_sk_select_reuseport(reuse_md, reuseport_array, &index,
  137. flags);
  138. if (!err)
  139. GOTO_DONE(PASS);
  140. if (cmd->pass_on_failure)
  141. GOTO_DONE(PASS_ERR_SK_SELECT_REUSEPORT);
  142. else
  143. GOTO_DONE(DROP_ERR_SK_SELECT_REUSEPORT);
  144. done:
  145. result_cnt = bpf_map_lookup_elem(&result_map, &result);
  146. if (!result_cnt)
  147. return SK_DROP;
  148. bpf_map_update_elem(&linum_map, &index_zero, &linum, BPF_ANY);
  149. bpf_map_update_elem(&data_check_map, &index_zero, &data_check, BPF_ANY);
  150. (*result_cnt)++;
  151. return result < PASS ? SK_DROP : SK_PASS;
  152. }
  153. char _license[] SEC("license") = "GPL";