1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2021 Facebook */
5 #include <linux/stddef.h>
6 #include <linux/if_ether.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>
28 QUIC_INITIAL_FLAG = 0x4,
39 struct v4_lpm_key key;
44 __uint(type, BPF_MAP_TYPE_HASH);
45 __uint(max_entries, 16);
46 __type(key, struct in6_addr);
48 } v6_addr_map SEC(".maps");
51 __uint(type, BPF_MAP_TYPE_HASH);
52 __uint(max_entries, 16);
55 } v4_addr_map SEC(".maps");
58 __uint(type, BPF_MAP_TYPE_LPM_TRIE);
59 __uint(max_entries, 16);
60 __uint(key_size, sizeof(struct v4_lpm_key));
61 __uint(value_size, sizeof(struct v4_lpm_val));
62 __uint(map_flags, BPF_F_NO_PREALLOC);
63 } v4_lpm_val_map SEC(".maps");
66 __uint(type, BPF_MAP_TYPE_ARRAY);
67 __uint(max_entries, 16);
70 } tcp_port_map SEC(".maps");
73 __uint(type, BPF_MAP_TYPE_ARRAY);
74 __uint(max_entries, 16);
77 } udp_port_map SEC(".maps");
79 enum ip_type { V4 = 1, V6 = 2 };
81 struct fw_match_info {
84 __u8 v4_src_prefix_match;
85 __u8 v4_dst_prefix_match;
101 __u16 trans_hdr_offset;
106 static __always_inline struct ethhdr *parse_ethhdr(void *data, void *data_end)
108 struct ethhdr *eth = data;
110 if (eth + 1 > data_end)
116 static __always_inline __u8 filter_ipv6_addr(const struct in6_addr *ipv6addr)
120 leaf = bpf_map_lookup_elem(&v6_addr_map, ipv6addr);
122 return leaf ? *leaf : 0;
125 static __always_inline __u8 filter_ipv4_addr(const __u32 ipaddr)
129 leaf = bpf_map_lookup_elem(&v4_addr_map, &ipaddr);
131 return leaf ? *leaf : 0;
134 static __always_inline __u8 filter_ipv4_lpm(const __u32 ipaddr)
136 struct v4_lpm_key v4_key = {};
137 struct v4_lpm_val *lpm_val;
140 v4_key.prefixlen = 32;
142 lpm_val = bpf_map_lookup_elem(&v4_lpm_val_map, &v4_key);
144 return lpm_val ? lpm_val->val : 0;
148 static __always_inline void
149 filter_src_dst_ip(struct pkt_info* info, struct fw_match_info* match_info)
151 if (info->type == V6) {
152 match_info->v6_src_ip_match =
153 filter_ipv6_addr(&info->ip.ipv6->saddr);
154 } else if (info->type == V4) {
155 match_info->v4_src_ip_match =
156 filter_ipv4_addr(info->ip.ipv4->saddr);
157 match_info->v4_src_prefix_match =
158 filter_ipv4_lpm(info->ip.ipv4->saddr);
159 match_info->v4_dst_prefix_match =
160 filter_ipv4_lpm(info->ip.ipv4->daddr);
164 static __always_inline void *
165 get_transport_hdr(__u16 offset, void *data, void *data_end)
167 if (offset > 255 || data + offset > data_end)
170 return data + offset;
173 static __always_inline bool tcphdr_only_contains_flag(struct tcphdr *tcp,
176 return (tcp_flag_word(tcp) &
177 (TCP_FLAG_ACK | TCP_FLAG_RST | TCP_FLAG_SYN | TCP_FLAG_FIN)) == FLAG;
180 static __always_inline void set_tcp_flags(struct pkt_info *info,
181 struct tcphdr *tcp) {
182 if (tcphdr_only_contains_flag(tcp, TCP_FLAG_SYN))
183 info->flags |= TCP_SYN;
184 else if (tcphdr_only_contains_flag(tcp, TCP_FLAG_ACK))
185 info->flags |= TCP_ACK;
186 else if (tcphdr_only_contains_flag(tcp, TCP_FLAG_RST))
187 info->flags |= TCP_RST;
190 static __always_inline bool
191 parse_tcp(struct pkt_info *info, void *transport_hdr, void *data_end)
193 struct tcphdr *tcp = transport_hdr;
195 if (tcp + 1 > data_end)
198 info->sport = bpf_ntohs(tcp->source);
199 info->dport = bpf_ntohs(tcp->dest);
200 set_tcp_flags(info, tcp);
205 static __always_inline bool
206 parse_udp(struct pkt_info *info, void *transport_hdr, void *data_end)
208 struct udphdr *udp = transport_hdr;
210 if (udp + 1 > data_end)
213 info->sport = bpf_ntohs(udp->source);
214 info->dport = bpf_ntohs(udp->dest);
219 static __always_inline __u8 filter_tcp_port(int port)
221 __u8 *leaf = bpf_map_lookup_elem(&tcp_port_map, &port);
223 return leaf ? *leaf : 0;
226 static __always_inline __u16 filter_udp_port(int port)
228 __u16 *leaf = bpf_map_lookup_elem(&udp_port_map, &port);
230 return leaf ? *leaf : 0;
233 static __always_inline bool
234 filter_transport_hdr(void *transport_hdr, void *data_end,
235 struct pkt_info *info, struct fw_match_info *match_info)
237 if (info->proto == IPPROTO_TCP) {
238 if (!parse_tcp(info, transport_hdr, data_end))
241 match_info->is_tcp = true;
242 match_info->is_tcp_syn = (info->flags & TCP_SYN) > 0;
244 match_info->tcp_dp_match = filter_tcp_port(info->dport);
245 } else if (info->proto == IPPROTO_UDP) {
246 if (!parse_udp(info, transport_hdr, data_end))
249 match_info->udp_dp_match = filter_udp_port(info->dport);
250 match_info->udp_sp_match = filter_udp_port(info->sport);
256 static __always_inline __u8
257 parse_gue_v6(struct pkt_info *info, struct ipv6hdr *ip6h, void *data_end)
259 struct udphdr *udp = (struct udphdr *)(ip6h + 1);
260 void *encap_data = udp + 1;
262 if (udp + 1 > data_end)
265 if (udp->dest != bpf_htons(6666))
268 info->flags |= TUNNEL;
270 if (encap_data + 1 > data_end)
271 return BAD_IP6GUE_HDR;
273 if (*(__u8 *)encap_data & 0x30) {
274 struct ipv6hdr *inner_ip6h = encap_data;
276 if (inner_ip6h + 1 > data_end)
277 return BAD_IP6GUE_HDR;
280 info->proto = inner_ip6h->nexthdr;
281 info->ip.ipv6 = inner_ip6h;
282 info->trans_hdr_offset += sizeof(struct ipv6hdr) + sizeof(struct udphdr);
284 struct iphdr *inner_ip4h = encap_data;
286 if (inner_ip4h + 1 > data_end)
287 return BAD_IP6GUE_HDR;
290 info->proto = inner_ip4h->protocol;
291 info->ip.ipv4 = inner_ip4h;
292 info->trans_hdr_offset += sizeof(struct iphdr) + sizeof(struct udphdr);
298 static __always_inline __u8 parse_ipv6_gue(struct pkt_info *info,
299 void *data, void *data_end)
301 struct ipv6hdr *ip6h = data + sizeof(struct ethhdr);
303 if (ip6h + 1 > data_end)
306 info->proto = ip6h->nexthdr;
307 info->ip.ipv6 = ip6h;
309 info->trans_hdr_offset = sizeof(struct ethhdr) + sizeof(struct ipv6hdr);
311 if (info->proto == IPPROTO_UDP)
312 return parse_gue_v6(info, ip6h, data_end);
318 int edgewall(struct xdp_md *ctx)
320 void *data_end = (void *)(long)(ctx->data_end);
321 void *data = (void *)(long)(ctx->data);
322 struct fw_match_info match_info = {};
323 struct pkt_info info = {};
329 eth = parse_ethhdr(data, data_end);
333 proto = eth->h_proto;
334 if (proto != bpf_htons(ETH_P_IPV6))
337 if (parse_ipv6_gue(&info, data, data_end))
340 if (info.proto == IPPROTO_ICMPV6)
343 if (info.proto != IPPROTO_TCP && info.proto != IPPROTO_UDP)
346 filter_src_dst_ip(&info, &match_info);
348 transport_hdr = get_transport_hdr(info.trans_hdr_offset, data,
353 filter_res = filter_transport_hdr(transport_hdr, data_end,
358 if (match_info.is_tcp && !match_info.is_tcp_syn)
364 char LICENSE[] SEC("license") = "GPL";