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