1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | #include "vmlinux.h" |
3 | #include <bpf/bpf_helpers.h> |
4 | #include <bpf/bpf_endian.h> |
5 | #include "bpf_tracing_net.h" |
6 | |
7 | #define NF_DROP 0 |
8 | #define NF_ACCEPT 1 |
9 | #define ETH_P_IP 0x0800 |
10 | #define ETH_P_IPV6 0x86DD |
11 | #define IP_MF 0x2000 |
12 | #define IP_OFFSET 0x1FFF |
13 | #define NEXTHDR_FRAGMENT 44 |
14 | |
15 | extern int bpf_dynptr_from_skb(struct sk_buff *skb, __u64 flags, |
16 | struct bpf_dynptr *ptr__uninit) __ksym; |
17 | extern void *bpf_dynptr_slice(const struct bpf_dynptr *ptr, uint32_t offset, |
18 | void *buffer, uint32_t buffer__sz) __ksym; |
19 | |
20 | volatile int shootdowns = 0; |
21 | |
22 | static bool is_frag_v4(struct iphdr *iph) |
23 | { |
24 | int offset; |
25 | int flags; |
26 | |
27 | offset = bpf_ntohs(iph->frag_off); |
28 | flags = offset & ~IP_OFFSET; |
29 | offset &= IP_OFFSET; |
30 | offset <<= 3; |
31 | |
32 | return (flags & IP_MF) || offset; |
33 | } |
34 | |
35 | static bool is_frag_v6(struct ipv6hdr *ip6h) |
36 | { |
37 | /* Simplifying assumption that there are no extension headers |
38 | * between fixed header and fragmentation header. This assumption |
39 | * is only valid in this test case. It saves us the hassle of |
40 | * searching all potential extension headers. |
41 | */ |
42 | return ip6h->nexthdr == NEXTHDR_FRAGMENT; |
43 | } |
44 | |
45 | static int handle_v4(struct sk_buff *skb) |
46 | { |
47 | struct bpf_dynptr ptr; |
48 | u8 iph_buf[20] = {}; |
49 | struct iphdr *iph; |
50 | |
51 | if (bpf_dynptr_from_skb(skb, 0, &ptr)) |
52 | return NF_DROP; |
53 | |
54 | iph = bpf_dynptr_slice(&ptr, 0, iph_buf, sizeof(iph_buf)); |
55 | if (!iph) |
56 | return NF_DROP; |
57 | |
58 | /* Shootdown any frags */ |
59 | if (is_frag_v4(iph)) { |
60 | shootdowns++; |
61 | return NF_DROP; |
62 | } |
63 | |
64 | return NF_ACCEPT; |
65 | } |
66 | |
67 | static int handle_v6(struct sk_buff *skb) |
68 | { |
69 | struct bpf_dynptr ptr; |
70 | struct ipv6hdr *ip6h; |
71 | u8 ip6h_buf[40] = {}; |
72 | |
73 | if (bpf_dynptr_from_skb(skb, 0, &ptr)) |
74 | return NF_DROP; |
75 | |
76 | ip6h = bpf_dynptr_slice(&ptr, 0, ip6h_buf, sizeof(ip6h_buf)); |
77 | if (!ip6h) |
78 | return NF_DROP; |
79 | |
80 | /* Shootdown any frags */ |
81 | if (is_frag_v6(ip6h)) { |
82 | shootdowns++; |
83 | return NF_DROP; |
84 | } |
85 | |
86 | return NF_ACCEPT; |
87 | } |
88 | |
89 | SEC("netfilter" ) |
90 | int defrag(struct bpf_nf_ctx *ctx) |
91 | { |
92 | struct sk_buff *skb = ctx->skb; |
93 | |
94 | switch (bpf_ntohs(skb->protocol)) { |
95 | case ETH_P_IP: |
96 | return handle_v4(skb); |
97 | case ETH_P_IPV6: |
98 | return handle_v6(skb); |
99 | default: |
100 | return NF_ACCEPT; |
101 | } |
102 | } |
103 | |
104 | char _license[] SEC("license" ) = "GPL" ; |
105 | |