Warning: That file was not part of the compilation database. It may have many parsing errors.
1 | // SPDX-License-Identifier: GPL-2.0 |
---|---|
2 | #include <limits.h> |
3 | #include <stddef.h> |
4 | #include <stdbool.h> |
5 | #include <string.h> |
6 | #include <linux/pkt_cls.h> |
7 | #include <linux/bpf.h> |
8 | #include <linux/in.h> |
9 | #include <linux/if_ether.h> |
10 | #include <linux/icmp.h> |
11 | #include <linux/ip.h> |
12 | #include <linux/ipv6.h> |
13 | #include <linux/tcp.h> |
14 | #include <linux/udp.h> |
15 | #include <linux/if_packet.h> |
16 | #include <sys/socket.h> |
17 | #include <linux/if_tunnel.h> |
18 | #include <linux/mpls.h> |
19 | #include "bpf_helpers.h" |
20 | #include "bpf_endian.h" |
21 | |
22 | int _version SEC("version") = 1; |
23 | #define PROG(F) SEC(#F) int bpf_func_##F |
24 | |
25 | /* These are the identifiers of the BPF programs that will be used in tail |
26 | * calls. Name is limited to 16 characters, with the terminating character and |
27 | * bpf_func_ above, we have only 6 to work with, anything after will be cropped. |
28 | */ |
29 | enum { |
30 | IP, |
31 | IPV6, |
32 | IPV6OP, /* Destination/Hop-by-Hop Options IPv6 Extension header */ |
33 | IPV6FR, /* Fragmentation IPv6 Extension Header */ |
34 | MPLS, |
35 | VLAN, |
36 | }; |
37 | |
38 | #define IP_MF 0x2000 |
39 | #define IP_OFFSET 0x1FFF |
40 | #define IP6_MF 0x0001 |
41 | #define IP6_OFFSET 0xFFF8 |
42 | |
43 | struct vlan_hdr { |
44 | __be16 h_vlan_TCI; |
45 | __be16 h_vlan_encapsulated_proto; |
46 | }; |
47 | |
48 | struct gre_hdr { |
49 | __be16 flags; |
50 | __be16 proto; |
51 | }; |
52 | |
53 | struct frag_hdr { |
54 | __u8 nexthdr; |
55 | __u8 reserved; |
56 | __be16 frag_off; |
57 | __be32 identification; |
58 | }; |
59 | |
60 | struct bpf_map_def SEC("maps") jmp_table = { |
61 | .type = BPF_MAP_TYPE_PROG_ARRAY, |
62 | .key_size = sizeof(__u32), |
63 | .value_size = sizeof(__u32), |
64 | .max_entries = 8 |
65 | }; |
66 | |
67 | static __always_inline void *bpf_flow_dissect_get_header(struct __sk_buff *skb, |
68 | __u16 hdr_size, |
69 | void *buffer) |
70 | { |
71 | void *data_end = (void *)(long)skb->data_end; |
72 | void *data = (void *)(long)skb->data; |
73 | __u16 thoff = skb->flow_keys->thoff; |
74 | __u8 *hdr; |
75 | |
76 | /* Verifies this variable offset does not overflow */ |
77 | if (thoff > (USHRT_MAX - hdr_size)) |
78 | return NULL; |
79 | |
80 | hdr = data + thoff; |
81 | if (hdr + hdr_size <= data_end) |
82 | return hdr; |
83 | |
84 | if (bpf_skb_load_bytes(skb, thoff, buffer, hdr_size)) |
85 | return NULL; |
86 | |
87 | return buffer; |
88 | } |
89 | |
90 | /* Dispatches on ETHERTYPE */ |
91 | static __always_inline int parse_eth_proto(struct __sk_buff *skb, __be16 proto) |
92 | { |
93 | struct bpf_flow_keys *keys = skb->flow_keys; |
94 | |
95 | keys->n_proto = proto; |
96 | switch (proto) { |
97 | case bpf_htons(ETH_P_IP): |
98 | bpf_tail_call(skb, &jmp_table, IP); |
99 | break; |
100 | case bpf_htons(ETH_P_IPV6): |
101 | bpf_tail_call(skb, &jmp_table, IPV6); |
102 | break; |
103 | case bpf_htons(ETH_P_MPLS_MC): |
104 | case bpf_htons(ETH_P_MPLS_UC): |
105 | bpf_tail_call(skb, &jmp_table, MPLS); |
106 | break; |
107 | case bpf_htons(ETH_P_8021Q): |
108 | case bpf_htons(ETH_P_8021AD): |
109 | bpf_tail_call(skb, &jmp_table, VLAN); |
110 | break; |
111 | default: |
112 | /* Protocol not supported */ |
113 | return BPF_DROP; |
114 | } |
115 | |
116 | return BPF_DROP; |
117 | } |
118 | |
119 | SEC("flow_dissector") |
120 | int _dissect(struct __sk_buff *skb) |
121 | { |
122 | if (!skb->vlan_present) |
123 | return parse_eth_proto(skb, skb->protocol); |
124 | else |
125 | return parse_eth_proto(skb, skb->vlan_proto); |
126 | } |
127 | |
128 | /* Parses on IPPROTO_* */ |
129 | static __always_inline int parse_ip_proto(struct __sk_buff *skb, __u8 proto) |
130 | { |
131 | struct bpf_flow_keys *keys = skb->flow_keys; |
132 | void *data_end = (void *)(long)skb->data_end; |
133 | struct icmphdr *icmp, _icmp; |
134 | struct gre_hdr *gre, _gre; |
135 | struct ethhdr *eth, _eth; |
136 | struct tcphdr *tcp, _tcp; |
137 | struct udphdr *udp, _udp; |
138 | |
139 | keys->ip_proto = proto; |
140 | switch (proto) { |
141 | case IPPROTO_ICMP: |
142 | icmp = bpf_flow_dissect_get_header(skb, sizeof(*icmp), &_icmp); |
143 | if (!icmp) |
144 | return BPF_DROP; |
145 | return BPF_OK; |
146 | case IPPROTO_IPIP: |
147 | keys->is_encap = true; |
148 | return parse_eth_proto(skb, bpf_htons(ETH_P_IP)); |
149 | case IPPROTO_IPV6: |
150 | keys->is_encap = true; |
151 | return parse_eth_proto(skb, bpf_htons(ETH_P_IPV6)); |
152 | case IPPROTO_GRE: |
153 | gre = bpf_flow_dissect_get_header(skb, sizeof(*gre), &_gre); |
154 | if (!gre) |
155 | return BPF_DROP; |
156 | |
157 | if (bpf_htons(gre->flags & GRE_VERSION)) |
158 | /* Only inspect standard GRE packets with version 0 */ |
159 | return BPF_OK; |
160 | |
161 | keys->thoff += sizeof(*gre); /* Step over GRE Flags and Proto */ |
162 | if (GRE_IS_CSUM(gre->flags)) |
163 | keys->thoff += 4; /* Step over chksum and Padding */ |
164 | if (GRE_IS_KEY(gre->flags)) |
165 | keys->thoff += 4; /* Step over key */ |
166 | if (GRE_IS_SEQ(gre->flags)) |
167 | keys->thoff += 4; /* Step over sequence number */ |
168 | |
169 | keys->is_encap = true; |
170 | |
171 | if (gre->proto == bpf_htons(ETH_P_TEB)) { |
172 | eth = bpf_flow_dissect_get_header(skb, sizeof(*eth), |
173 | &_eth); |
174 | if (!eth) |
175 | return BPF_DROP; |
176 | |
177 | keys->thoff += sizeof(*eth); |
178 | |
179 | return parse_eth_proto(skb, eth->h_proto); |
180 | } else { |
181 | return parse_eth_proto(skb, gre->proto); |
182 | } |
183 | case IPPROTO_TCP: |
184 | tcp = bpf_flow_dissect_get_header(skb, sizeof(*tcp), &_tcp); |
185 | if (!tcp) |
186 | return BPF_DROP; |
187 | |
188 | if (tcp->doff < 5) |
189 | return BPF_DROP; |
190 | |
191 | if ((__u8 *)tcp + (tcp->doff << 2) > data_end) |
192 | return BPF_DROP; |
193 | |
194 | keys->sport = tcp->source; |
195 | keys->dport = tcp->dest; |
196 | return BPF_OK; |
197 | case IPPROTO_UDP: |
198 | case IPPROTO_UDPLITE: |
199 | udp = bpf_flow_dissect_get_header(skb, sizeof(*udp), &_udp); |
200 | if (!udp) |
201 | return BPF_DROP; |
202 | |
203 | keys->sport = udp->source; |
204 | keys->dport = udp->dest; |
205 | return BPF_OK; |
206 | default: |
207 | return BPF_DROP; |
208 | } |
209 | |
210 | return BPF_DROP; |
211 | } |
212 | |
213 | static __always_inline int parse_ipv6_proto(struct __sk_buff *skb, __u8 nexthdr) |
214 | { |
215 | struct bpf_flow_keys *keys = skb->flow_keys; |
216 | |
217 | keys->ip_proto = nexthdr; |
218 | switch (nexthdr) { |
219 | case IPPROTO_HOPOPTS: |
220 | case IPPROTO_DSTOPTS: |
221 | bpf_tail_call(skb, &jmp_table, IPV6OP); |
222 | break; |
223 | case IPPROTO_FRAGMENT: |
224 | bpf_tail_call(skb, &jmp_table, IPV6FR); |
225 | break; |
226 | default: |
227 | return parse_ip_proto(skb, nexthdr); |
228 | } |
229 | |
230 | return BPF_DROP; |
231 | } |
232 | |
233 | PROG(IP)(struct __sk_buff *skb) |
234 | { |
235 | void *data_end = (void *)(long)skb->data_end; |
236 | struct bpf_flow_keys *keys = skb->flow_keys; |
237 | void *data = (void *)(long)skb->data; |
238 | struct iphdr *iph, _iph; |
239 | bool done = false; |
240 | |
241 | iph = bpf_flow_dissect_get_header(skb, sizeof(*iph), &_iph); |
242 | if (!iph) |
243 | return BPF_DROP; |
244 | |
245 | /* IP header cannot be smaller than 20 bytes */ |
246 | if (iph->ihl < 5) |
247 | return BPF_DROP; |
248 | |
249 | keys->addr_proto = ETH_P_IP; |
250 | keys->ipv4_src = iph->saddr; |
251 | keys->ipv4_dst = iph->daddr; |
252 | |
253 | keys->thoff += iph->ihl << 2; |
254 | if (data + keys->thoff > data_end) |
255 | return BPF_DROP; |
256 | |
257 | if (iph->frag_off & bpf_htons(IP_MF | IP_OFFSET)) { |
258 | keys->is_frag = true; |
259 | if (iph->frag_off & bpf_htons(IP_OFFSET)) |
260 | /* From second fragment on, packets do not have headers |
261 | * we can parse. |
262 | */ |
263 | done = true; |
264 | else |
265 | keys->is_first_frag = true; |
266 | } |
267 | |
268 | if (done) |
269 | return BPF_OK; |
270 | |
271 | return parse_ip_proto(skb, iph->protocol); |
272 | } |
273 | |
274 | PROG(IPV6)(struct __sk_buff *skb) |
275 | { |
276 | struct bpf_flow_keys *keys = skb->flow_keys; |
277 | struct ipv6hdr *ip6h, _ip6h; |
278 | |
279 | ip6h = bpf_flow_dissect_get_header(skb, sizeof(*ip6h), &_ip6h); |
280 | if (!ip6h) |
281 | return BPF_DROP; |
282 | |
283 | keys->addr_proto = ETH_P_IPV6; |
284 | memcpy(&keys->ipv6_src, &ip6h->saddr, 2*sizeof(ip6h->saddr)); |
285 | |
286 | keys->thoff += sizeof(struct ipv6hdr); |
287 | |
288 | return parse_ipv6_proto(skb, ip6h->nexthdr); |
289 | } |
290 | |
291 | PROG(IPV6OP)(struct __sk_buff *skb) |
292 | { |
293 | struct ipv6_opt_hdr *ip6h, _ip6h; |
294 | |
295 | ip6h = bpf_flow_dissect_get_header(skb, sizeof(*ip6h), &_ip6h); |
296 | if (!ip6h) |
297 | return BPF_DROP; |
298 | |
299 | /* hlen is in 8-octets and does not include the first 8 bytes |
300 | * of the header |
301 | */ |
302 | skb->flow_keys->thoff += (1 + ip6h->hdrlen) << 3; |
303 | |
304 | return parse_ipv6_proto(skb, ip6h->nexthdr); |
305 | } |
306 | |
307 | PROG(IPV6FR)(struct __sk_buff *skb) |
308 | { |
309 | struct bpf_flow_keys *keys = skb->flow_keys; |
310 | struct frag_hdr *fragh, _fragh; |
311 | |
312 | fragh = bpf_flow_dissect_get_header(skb, sizeof(*fragh), &_fragh); |
313 | if (!fragh) |
314 | return BPF_DROP; |
315 | |
316 | keys->thoff += sizeof(*fragh); |
317 | keys->is_frag = true; |
318 | if (!(fragh->frag_off & bpf_htons(IP6_OFFSET))) |
319 | keys->is_first_frag = true; |
320 | |
321 | return parse_ipv6_proto(skb, fragh->nexthdr); |
322 | } |
323 | |
324 | PROG(MPLS)(struct __sk_buff *skb) |
325 | { |
326 | struct mpls_label *mpls, _mpls; |
327 | |
328 | mpls = bpf_flow_dissect_get_header(skb, sizeof(*mpls), &_mpls); |
329 | if (!mpls) |
330 | return BPF_DROP; |
331 | |
332 | return BPF_OK; |
333 | } |
334 | |
335 | PROG(VLAN)(struct __sk_buff *skb) |
336 | { |
337 | struct bpf_flow_keys *keys = skb->flow_keys; |
338 | struct vlan_hdr *vlan, _vlan; |
339 | __be16 proto; |
340 | |
341 | /* Peek back to see if single or double-tagging */ |
342 | if (bpf_skb_load_bytes(skb, keys->thoff - sizeof(proto), &proto, |
343 | sizeof(proto))) |
344 | return BPF_DROP; |
345 | |
346 | /* Account for double-tagging */ |
347 | if (proto == bpf_htons(ETH_P_8021AD)) { |
348 | vlan = bpf_flow_dissect_get_header(skb, sizeof(*vlan), &_vlan); |
349 | if (!vlan) |
350 | return BPF_DROP; |
351 | |
352 | if (vlan->h_vlan_encapsulated_proto != bpf_htons(ETH_P_8021Q)) |
353 | return BPF_DROP; |
354 | |
355 | keys->thoff += sizeof(*vlan); |
356 | } |
357 | |
358 | vlan = bpf_flow_dissect_get_header(skb, sizeof(*vlan), &_vlan); |
359 | if (!vlan) |
360 | return BPF_DROP; |
361 | |
362 | keys->thoff += sizeof(*vlan); |
363 | /* Only allow 8021AD + 8021Q double tagging and no triple tagging.*/ |
364 | if (vlan->h_vlan_encapsulated_proto == bpf_htons(ETH_P_8021AD) || |
365 | vlan->h_vlan_encapsulated_proto == bpf_htons(ETH_P_8021Q)) |
366 | return BPF_DROP; |
367 | |
368 | return parse_eth_proto(skb, vlan->h_vlan_encapsulated_proto); |
369 | } |
370 | |
371 | char __license[] SEC("license") = "GPL"; |
372 |
Warning: That file was not part of the compilation database. It may have many parsing errors.