xdpwall.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. // SPDX-License-Identifier: GPL-2.0
  2. /* Copyright (c) 2021 Facebook */
  3. #include <stdbool.h>
  4. #include <stdint.h>
  5. #include <linux/stddef.h>
  6. #include <linux/if_ether.h>
  7. #include <linux/in.h>
  8. #include <linux/in6.h>
  9. #include <linux/ip.h>
  10. #include <linux/ipv6.h>
  11. #include <linux/tcp.h>
  12. #include <linux/udp.h>
  13. #include <linux/bpf.h>
  14. #include <linux/types.h>
  15. #include <bpf/bpf_endian.h>
  16. #include <bpf/bpf_helpers.h>
  17. enum pkt_parse_err {
  18. NO_ERR,
  19. BAD_IP6_HDR,
  20. BAD_IP4GUE_HDR,
  21. BAD_IP6GUE_HDR,
  22. };
  23. enum pkt_flag {
  24. TUNNEL = 0x1,
  25. TCP_SYN = 0x2,
  26. QUIC_INITIAL_FLAG = 0x4,
  27. TCP_ACK = 0x8,
  28. TCP_RST = 0x10
  29. };
  30. struct v4_lpm_key {
  31. __u32 prefixlen;
  32. __u32 src;
  33. };
  34. struct v4_lpm_val {
  35. struct v4_lpm_key key;
  36. __u8 val;
  37. };
  38. struct {
  39. __uint(type, BPF_MAP_TYPE_HASH);
  40. __uint(max_entries, 16);
  41. __type(key, struct in6_addr);
  42. __type(value, bool);
  43. } v6_addr_map SEC(".maps");
  44. struct {
  45. __uint(type, BPF_MAP_TYPE_HASH);
  46. __uint(max_entries, 16);
  47. __type(key, __u32);
  48. __type(value, bool);
  49. } v4_addr_map SEC(".maps");
  50. struct {
  51. __uint(type, BPF_MAP_TYPE_LPM_TRIE);
  52. __uint(max_entries, 16);
  53. __uint(key_size, sizeof(struct v4_lpm_key));
  54. __uint(value_size, sizeof(struct v4_lpm_val));
  55. __uint(map_flags, BPF_F_NO_PREALLOC);
  56. } v4_lpm_val_map SEC(".maps");
  57. struct {
  58. __uint(type, BPF_MAP_TYPE_ARRAY);
  59. __uint(max_entries, 16);
  60. __type(key, int);
  61. __type(value, __u8);
  62. } tcp_port_map SEC(".maps");
  63. struct {
  64. __uint(type, BPF_MAP_TYPE_ARRAY);
  65. __uint(max_entries, 16);
  66. __type(key, int);
  67. __type(value, __u16);
  68. } udp_port_map SEC(".maps");
  69. enum ip_type { V4 = 1, V6 = 2 };
  70. struct fw_match_info {
  71. __u8 v4_src_ip_match;
  72. __u8 v6_src_ip_match;
  73. __u8 v4_src_prefix_match;
  74. __u8 v4_dst_prefix_match;
  75. __u8 tcp_dp_match;
  76. __u16 udp_sp_match;
  77. __u16 udp_dp_match;
  78. bool is_tcp;
  79. bool is_tcp_syn;
  80. };
  81. struct pkt_info {
  82. enum ip_type type;
  83. union {
  84. struct iphdr *ipv4;
  85. struct ipv6hdr *ipv6;
  86. } ip;
  87. int sport;
  88. int dport;
  89. __u16 trans_hdr_offset;
  90. __u8 proto;
  91. __u8 flags;
  92. };
  93. static __always_inline struct ethhdr *parse_ethhdr(void *data, void *data_end)
  94. {
  95. struct ethhdr *eth = data;
  96. if (eth + 1 > data_end)
  97. return NULL;
  98. return eth;
  99. }
  100. static __always_inline __u8 filter_ipv6_addr(const struct in6_addr *ipv6addr)
  101. {
  102. __u8 *leaf;
  103. leaf = bpf_map_lookup_elem(&v6_addr_map, ipv6addr);
  104. return leaf ? *leaf : 0;
  105. }
  106. static __always_inline __u8 filter_ipv4_addr(const __u32 ipaddr)
  107. {
  108. __u8 *leaf;
  109. leaf = bpf_map_lookup_elem(&v4_addr_map, &ipaddr);
  110. return leaf ? *leaf : 0;
  111. }
  112. static __always_inline __u8 filter_ipv4_lpm(const __u32 ipaddr)
  113. {
  114. struct v4_lpm_key v4_key = {};
  115. struct v4_lpm_val *lpm_val;
  116. v4_key.src = ipaddr;
  117. v4_key.prefixlen = 32;
  118. lpm_val = bpf_map_lookup_elem(&v4_lpm_val_map, &v4_key);
  119. return lpm_val ? lpm_val->val : 0;
  120. }
  121. static __always_inline void
  122. filter_src_dst_ip(struct pkt_info* info, struct fw_match_info* match_info)
  123. {
  124. if (info->type == V6) {
  125. match_info->v6_src_ip_match =
  126. filter_ipv6_addr(&info->ip.ipv6->saddr);
  127. } else if (info->type == V4) {
  128. match_info->v4_src_ip_match =
  129. filter_ipv4_addr(info->ip.ipv4->saddr);
  130. match_info->v4_src_prefix_match =
  131. filter_ipv4_lpm(info->ip.ipv4->saddr);
  132. match_info->v4_dst_prefix_match =
  133. filter_ipv4_lpm(info->ip.ipv4->daddr);
  134. }
  135. }
  136. static __always_inline void *
  137. get_transport_hdr(__u16 offset, void *data, void *data_end)
  138. {
  139. if (offset > 255 || data + offset > data_end)
  140. return NULL;
  141. return data + offset;
  142. }
  143. static __always_inline bool tcphdr_only_contains_flag(struct tcphdr *tcp,
  144. __u32 FLAG)
  145. {
  146. return (tcp_flag_word(tcp) &
  147. (TCP_FLAG_ACK | TCP_FLAG_RST | TCP_FLAG_SYN | TCP_FLAG_FIN)) == FLAG;
  148. }
  149. static __always_inline void set_tcp_flags(struct pkt_info *info,
  150. struct tcphdr *tcp) {
  151. if (tcphdr_only_contains_flag(tcp, TCP_FLAG_SYN))
  152. info->flags |= TCP_SYN;
  153. else if (tcphdr_only_contains_flag(tcp, TCP_FLAG_ACK))
  154. info->flags |= TCP_ACK;
  155. else if (tcphdr_only_contains_flag(tcp, TCP_FLAG_RST))
  156. info->flags |= TCP_RST;
  157. }
  158. static __always_inline bool
  159. parse_tcp(struct pkt_info *info, void *transport_hdr, void *data_end)
  160. {
  161. struct tcphdr *tcp = transport_hdr;
  162. if (tcp + 1 > data_end)
  163. return false;
  164. info->sport = bpf_ntohs(tcp->source);
  165. info->dport = bpf_ntohs(tcp->dest);
  166. set_tcp_flags(info, tcp);
  167. return true;
  168. }
  169. static __always_inline bool
  170. parse_udp(struct pkt_info *info, void *transport_hdr, void *data_end)
  171. {
  172. struct udphdr *udp = transport_hdr;
  173. if (udp + 1 > data_end)
  174. return false;
  175. info->sport = bpf_ntohs(udp->source);
  176. info->dport = bpf_ntohs(udp->dest);
  177. return true;
  178. }
  179. static __always_inline __u8 filter_tcp_port(int port)
  180. {
  181. __u8 *leaf = bpf_map_lookup_elem(&tcp_port_map, &port);
  182. return leaf ? *leaf : 0;
  183. }
  184. static __always_inline __u16 filter_udp_port(int port)
  185. {
  186. __u16 *leaf = bpf_map_lookup_elem(&udp_port_map, &port);
  187. return leaf ? *leaf : 0;
  188. }
  189. static __always_inline bool
  190. filter_transport_hdr(void *transport_hdr, void *data_end,
  191. struct pkt_info *info, struct fw_match_info *match_info)
  192. {
  193. if (info->proto == IPPROTO_TCP) {
  194. if (!parse_tcp(info, transport_hdr, data_end))
  195. return false;
  196. match_info->is_tcp = true;
  197. match_info->is_tcp_syn = (info->flags & TCP_SYN) > 0;
  198. match_info->tcp_dp_match = filter_tcp_port(info->dport);
  199. } else if (info->proto == IPPROTO_UDP) {
  200. if (!parse_udp(info, transport_hdr, data_end))
  201. return false;
  202. match_info->udp_dp_match = filter_udp_port(info->dport);
  203. match_info->udp_sp_match = filter_udp_port(info->sport);
  204. }
  205. return true;
  206. }
  207. static __always_inline __u8
  208. parse_gue_v6(struct pkt_info *info, struct ipv6hdr *ip6h, void *data_end)
  209. {
  210. struct udphdr *udp = (struct udphdr *)(ip6h + 1);
  211. void *encap_data = udp + 1;
  212. if (udp + 1 > data_end)
  213. return BAD_IP6_HDR;
  214. if (udp->dest != bpf_htons(6666))
  215. return NO_ERR;
  216. info->flags |= TUNNEL;
  217. if (encap_data + 1 > data_end)
  218. return BAD_IP6GUE_HDR;
  219. if (*(__u8 *)encap_data & 0x30) {
  220. struct ipv6hdr *inner_ip6h = encap_data;
  221. if (inner_ip6h + 1 > data_end)
  222. return BAD_IP6GUE_HDR;
  223. info->type = V6;
  224. info->proto = inner_ip6h->nexthdr;
  225. info->ip.ipv6 = inner_ip6h;
  226. info->trans_hdr_offset += sizeof(struct ipv6hdr) + sizeof(struct udphdr);
  227. } else {
  228. struct iphdr *inner_ip4h = encap_data;
  229. if (inner_ip4h + 1 > data_end)
  230. return BAD_IP6GUE_HDR;
  231. info->type = V4;
  232. info->proto = inner_ip4h->protocol;
  233. info->ip.ipv4 = inner_ip4h;
  234. info->trans_hdr_offset += sizeof(struct iphdr) + sizeof(struct udphdr);
  235. }
  236. return NO_ERR;
  237. }
  238. static __always_inline __u8 parse_ipv6_gue(struct pkt_info *info,
  239. void *data, void *data_end)
  240. {
  241. struct ipv6hdr *ip6h = data + sizeof(struct ethhdr);
  242. if (ip6h + 1 > data_end)
  243. return BAD_IP6_HDR;
  244. info->proto = ip6h->nexthdr;
  245. info->ip.ipv6 = ip6h;
  246. info->type = V6;
  247. info->trans_hdr_offset = sizeof(struct ethhdr) + sizeof(struct ipv6hdr);
  248. if (info->proto == IPPROTO_UDP)
  249. return parse_gue_v6(info, ip6h, data_end);
  250. return NO_ERR;
  251. }
  252. SEC("xdp")
  253. int edgewall(struct xdp_md *ctx)
  254. {
  255. void *data_end = (void *)(long)(ctx->data_end);
  256. void *data = (void *)(long)(ctx->data);
  257. struct fw_match_info match_info = {};
  258. struct pkt_info info = {};
  259. __u8 parse_err = NO_ERR;
  260. void *transport_hdr;
  261. struct ethhdr *eth;
  262. bool filter_res;
  263. __u32 proto;
  264. eth = parse_ethhdr(data, data_end);
  265. if (!eth)
  266. return XDP_DROP;
  267. proto = eth->h_proto;
  268. if (proto != bpf_htons(ETH_P_IPV6))
  269. return XDP_DROP;
  270. if (parse_ipv6_gue(&info, data, data_end))
  271. return XDP_DROP;
  272. if (info.proto == IPPROTO_ICMPV6)
  273. return XDP_PASS;
  274. if (info.proto != IPPROTO_TCP && info.proto != IPPROTO_UDP)
  275. return XDP_DROP;
  276. filter_src_dst_ip(&info, &match_info);
  277. transport_hdr = get_transport_hdr(info.trans_hdr_offset, data,
  278. data_end);
  279. if (!transport_hdr)
  280. return XDP_DROP;
  281. filter_res = filter_transport_hdr(transport_hdr, data_end,
  282. &info, &match_info);
  283. if (!filter_res)
  284. return XDP_DROP;
  285. if (match_info.is_tcp && !match_info.is_tcp_syn)
  286. return XDP_PASS;
  287. return XDP_DROP;
  288. }
  289. char LICENSE[] SEC("license") = "GPL";