xref: /linux/tools/testing/selftests/bpf/progs/icmp_send.c (revision 5a8cd539ac19f7a68e68e1d25ef9ca2ff55b8500)
1 // SPDX-License-Identifier: GPL-2.0
2 #include "vmlinux.h"
3 #include <bpf/bpf_helpers.h>
4 #include <bpf/bpf_endian.h>
5 
6 /* 127.0.0.1 in host byte order */
7 #define SERVER_IP 0x7F000001
8 /* ::1 in host byte order (last 32-bit word) */
9 #define SERVER_IP6_LO 0x00000001
10 
11 __u16 server_port = 0;
12 int unreach_type = 0;
13 int unreach_code = 0;
14 int kfunc_ret = -1;
15 int target_pid = -1;
16 
17 unsigned int rec_count = 0;
18 int rec_kfunc_rets[] = { -1, -1 };
19 
20 SEC("cgroup_skb/egress")
egress(struct __sk_buff * skb)21 int egress(struct __sk_buff *skb)
22 {
23 	void *data = (void *)(long)skb->data;
24 	void *data_end = (void *)(long)skb->data_end;
25 	struct iphdr *iph;
26 	struct ipv6hdr *ip6h;
27 	struct tcphdr *tcph;
28 	__u8 version;
29 
30 	if (data + 1 > data_end)
31 		return SK_PASS;
32 
33 	version = (*((__u8 *)data)) >> 4;
34 
35 	if (version == 4) {
36 		iph = data;
37 		if ((void *)(iph + 1) > data_end ||
38 		    iph->protocol != IPPROTO_TCP ||
39 		    iph->daddr != bpf_htonl(SERVER_IP))
40 			return SK_PASS;
41 
42 		tcph = (void *)iph + iph->ihl * 4;
43 		if ((void *)(tcph + 1) > data_end ||
44 		    tcph->dest != bpf_htons(server_port))
45 			return SK_PASS;
46 
47 	} else if (version == 6) {
48 		ip6h = data;
49 		if ((void *)(ip6h + 1) > data_end ||
50 		    ip6h->nexthdr != IPPROTO_TCP)
51 			return SK_PASS;
52 
53 		if (ip6h->daddr.in6_u.u6_addr32[0] != 0 ||
54 		    ip6h->daddr.in6_u.u6_addr32[1] != 0 ||
55 		    ip6h->daddr.in6_u.u6_addr32[2] != 0 ||
56 		    ip6h->daddr.in6_u.u6_addr32[3] != bpf_htonl(SERVER_IP6_LO))
57 			return SK_PASS;
58 
59 		tcph = (void *)(ip6h + 1);
60 		if ((void *)(tcph + 1) > data_end ||
61 		    tcph->dest != bpf_htons(server_port))
62 			return SK_PASS;
63 	} else {
64 		return SK_PASS;
65 	}
66 
67 	kfunc_ret = bpf_icmp_send(skb, unreach_type, unreach_code);
68 
69 	return SK_DROP;
70 }
71 
72 SEC("cgroup_skb/egress")
recursion(struct __sk_buff * skb)73 int recursion(struct __sk_buff *skb)
74 {
75 	void *data = (void *)(long)skb->data;
76 	void *data_end = (void *)(long)skb->data_end;
77 	struct icmphdr *icmph;
78 	struct tcphdr *tcph;
79 	struct iphdr *iph;
80 	int ret;
81 
82 	if ((bpf_get_current_pid_tgid() >> 32) != target_pid)
83 		return SK_PASS;
84 
85 	iph = data;
86 	if ((void *)(iph + 1) > data_end || iph->version != 4)
87 		return SK_PASS;
88 
89 	if (iph->daddr != bpf_htonl(SERVER_IP))
90 		return SK_PASS;
91 
92 	if (iph->protocol == IPPROTO_TCP) {
93 		tcph = (void *)iph + iph->ihl * 4;
94 		if ((void *)(tcph + 1) > data_end ||
95 		    tcph->dest != bpf_htons(server_port))
96 			return SK_PASS;
97 	} else if (iph->protocol == IPPROTO_ICMP) {
98 		icmph = (void *)iph + iph->ihl * 4;
99 		if ((void *)(icmph + 1) > data_end ||
100 		    icmph->type != unreach_type || icmph->code != unreach_code)
101 			return SK_PASS;
102 	} else {
103 		return SK_PASS;
104 	}
105 
106 	/*
107 	 * This call will provoke a recursion: the ICMP packet generated by the
108 	 * kfunc will re-trigger this program since we are in the root cgroup in
109 	 * which the kernel ICMP socket belongs. However when re-entering the
110 	 * kfunc, it should return EBUSY.
111 	 */
112 	ret = bpf_icmp_send(skb, unreach_type, unreach_code);
113 	rec_kfunc_rets[rec_count & 1] = ret;
114 	__sync_fetch_and_add(&rec_count, 1);
115 
116 	/* Let the first ICMP error message pass */
117 	if (iph->protocol == IPPROTO_ICMP)
118 		return SK_PASS;
119 
120 	return SK_DROP;
121 }
122 
123 char LICENSE[] SEC("license") = "Dual BSD/GPL";
124