xref: /linux/tools/testing/selftests/bpf/progs/ip_check_defrag.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
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