xref: /linux/tools/testing/selftests/bpf/progs/icmp_send.c (revision 5a8cd539ac19f7a68e68e1d25ef9ca2ff55b8500)
139b337a3SMahe Tardy // SPDX-License-Identifier: GPL-2.0
239b337a3SMahe Tardy #include "vmlinux.h"
339b337a3SMahe Tardy #include <bpf/bpf_helpers.h>
439b337a3SMahe Tardy #include <bpf/bpf_endian.h>
539b337a3SMahe Tardy 
639b337a3SMahe Tardy /* 127.0.0.1 in host byte order */
739b337a3SMahe Tardy #define SERVER_IP 0x7F000001
8340a40dfSMahe Tardy /* ::1 in host byte order (last 32-bit word) */
9340a40dfSMahe Tardy #define SERVER_IP6_LO 0x00000001
1039b337a3SMahe Tardy 
1139b337a3SMahe Tardy __u16 server_port = 0;
12340a40dfSMahe Tardy int unreach_type = 0;
1339b337a3SMahe Tardy int unreach_code = 0;
1439b337a3SMahe Tardy int kfunc_ret = -1;
15*49d07ba6SMahe Tardy int target_pid = -1;
16*49d07ba6SMahe Tardy 
17*49d07ba6SMahe Tardy unsigned int rec_count = 0;
18*49d07ba6SMahe Tardy int rec_kfunc_rets[] = { -1, -1 };
1939b337a3SMahe Tardy 
2039b337a3SMahe Tardy SEC("cgroup_skb/egress")
egress(struct __sk_buff * skb)2139b337a3SMahe Tardy int egress(struct __sk_buff *skb)
2239b337a3SMahe Tardy {
2339b337a3SMahe Tardy 	void *data = (void *)(long)skb->data;
2439b337a3SMahe Tardy 	void *data_end = (void *)(long)skb->data_end;
2539b337a3SMahe Tardy 	struct iphdr *iph;
26340a40dfSMahe Tardy 	struct ipv6hdr *ip6h;
2739b337a3SMahe Tardy 	struct tcphdr *tcph;
28340a40dfSMahe Tardy 	__u8 version;
2939b337a3SMahe Tardy 
30340a40dfSMahe Tardy 	if (data + 1 > data_end)
31340a40dfSMahe Tardy 		return SK_PASS;
32340a40dfSMahe Tardy 
33340a40dfSMahe Tardy 	version = (*((__u8 *)data)) >> 4;
34340a40dfSMahe Tardy 
35340a40dfSMahe Tardy 	if (version == 4) {
3639b337a3SMahe Tardy 		iph = data;
37340a40dfSMahe Tardy 		if ((void *)(iph + 1) > data_end ||
38340a40dfSMahe Tardy 		    iph->protocol != IPPROTO_TCP ||
39340a40dfSMahe Tardy 		    iph->daddr != bpf_htonl(SERVER_IP))
4039b337a3SMahe Tardy 			return SK_PASS;
4139b337a3SMahe Tardy 
4239b337a3SMahe Tardy 		tcph = (void *)iph + iph->ihl * 4;
4339b337a3SMahe Tardy 		if ((void *)(tcph + 1) > data_end ||
4439b337a3SMahe Tardy 		    tcph->dest != bpf_htons(server_port))
4539b337a3SMahe Tardy 			return SK_PASS;
4639b337a3SMahe Tardy 
47340a40dfSMahe Tardy 	} else if (version == 6) {
48340a40dfSMahe Tardy 		ip6h = data;
49340a40dfSMahe Tardy 		if ((void *)(ip6h + 1) > data_end ||
50340a40dfSMahe Tardy 		    ip6h->nexthdr != IPPROTO_TCP)
51340a40dfSMahe Tardy 			return SK_PASS;
52340a40dfSMahe Tardy 
53340a40dfSMahe Tardy 		if (ip6h->daddr.in6_u.u6_addr32[0] != 0 ||
54340a40dfSMahe Tardy 		    ip6h->daddr.in6_u.u6_addr32[1] != 0 ||
55340a40dfSMahe Tardy 		    ip6h->daddr.in6_u.u6_addr32[2] != 0 ||
56340a40dfSMahe Tardy 		    ip6h->daddr.in6_u.u6_addr32[3] != bpf_htonl(SERVER_IP6_LO))
57340a40dfSMahe Tardy 			return SK_PASS;
58340a40dfSMahe Tardy 
59340a40dfSMahe Tardy 		tcph = (void *)(ip6h + 1);
60340a40dfSMahe Tardy 		if ((void *)(tcph + 1) > data_end ||
61340a40dfSMahe Tardy 		    tcph->dest != bpf_htons(server_port))
62340a40dfSMahe Tardy 			return SK_PASS;
63340a40dfSMahe Tardy 	} else {
64340a40dfSMahe Tardy 		return SK_PASS;
65340a40dfSMahe Tardy 	}
66340a40dfSMahe Tardy 
67340a40dfSMahe Tardy 	kfunc_ret = bpf_icmp_send(skb, unreach_type, unreach_code);
6839b337a3SMahe Tardy 
6939b337a3SMahe Tardy 	return SK_DROP;
7039b337a3SMahe Tardy }
7139b337a3SMahe Tardy 
72*49d07ba6SMahe Tardy SEC("cgroup_skb/egress")
recursion(struct __sk_buff * skb)73*49d07ba6SMahe Tardy int recursion(struct __sk_buff *skb)
74*49d07ba6SMahe Tardy {
75*49d07ba6SMahe Tardy 	void *data = (void *)(long)skb->data;
76*49d07ba6SMahe Tardy 	void *data_end = (void *)(long)skb->data_end;
77*49d07ba6SMahe Tardy 	struct icmphdr *icmph;
78*49d07ba6SMahe Tardy 	struct tcphdr *tcph;
79*49d07ba6SMahe Tardy 	struct iphdr *iph;
80*49d07ba6SMahe Tardy 	int ret;
81*49d07ba6SMahe Tardy 
82*49d07ba6SMahe Tardy 	if ((bpf_get_current_pid_tgid() >> 32) != target_pid)
83*49d07ba6SMahe Tardy 		return SK_PASS;
84*49d07ba6SMahe Tardy 
85*49d07ba6SMahe Tardy 	iph = data;
86*49d07ba6SMahe Tardy 	if ((void *)(iph + 1) > data_end || iph->version != 4)
87*49d07ba6SMahe Tardy 		return SK_PASS;
88*49d07ba6SMahe Tardy 
89*49d07ba6SMahe Tardy 	if (iph->daddr != bpf_htonl(SERVER_IP))
90*49d07ba6SMahe Tardy 		return SK_PASS;
91*49d07ba6SMahe Tardy 
92*49d07ba6SMahe Tardy 	if (iph->protocol == IPPROTO_TCP) {
93*49d07ba6SMahe Tardy 		tcph = (void *)iph + iph->ihl * 4;
94*49d07ba6SMahe Tardy 		if ((void *)(tcph + 1) > data_end ||
95*49d07ba6SMahe Tardy 		    tcph->dest != bpf_htons(server_port))
96*49d07ba6SMahe Tardy 			return SK_PASS;
97*49d07ba6SMahe Tardy 	} else if (iph->protocol == IPPROTO_ICMP) {
98*49d07ba6SMahe Tardy 		icmph = (void *)iph + iph->ihl * 4;
99*49d07ba6SMahe Tardy 		if ((void *)(icmph + 1) > data_end ||
100*49d07ba6SMahe Tardy 		    icmph->type != unreach_type || icmph->code != unreach_code)
101*49d07ba6SMahe Tardy 			return SK_PASS;
102*49d07ba6SMahe Tardy 	} else {
103*49d07ba6SMahe Tardy 		return SK_PASS;
104*49d07ba6SMahe Tardy 	}
105*49d07ba6SMahe Tardy 
106*49d07ba6SMahe Tardy 	/*
107*49d07ba6SMahe Tardy 	 * This call will provoke a recursion: the ICMP packet generated by the
108*49d07ba6SMahe Tardy 	 * kfunc will re-trigger this program since we are in the root cgroup in
109*49d07ba6SMahe Tardy 	 * which the kernel ICMP socket belongs. However when re-entering the
110*49d07ba6SMahe Tardy 	 * kfunc, it should return EBUSY.
111*49d07ba6SMahe Tardy 	 */
112*49d07ba6SMahe Tardy 	ret = bpf_icmp_send(skb, unreach_type, unreach_code);
113*49d07ba6SMahe Tardy 	rec_kfunc_rets[rec_count & 1] = ret;
114*49d07ba6SMahe Tardy 	__sync_fetch_and_add(&rec_count, 1);
115*49d07ba6SMahe Tardy 
116*49d07ba6SMahe Tardy 	/* Let the first ICMP error message pass */
117*49d07ba6SMahe Tardy 	if (iph->protocol == IPPROTO_ICMP)
118*49d07ba6SMahe Tardy 		return SK_PASS;
119*49d07ba6SMahe Tardy 
120*49d07ba6SMahe Tardy 	return SK_DROP;
121*49d07ba6SMahe Tardy }
122*49d07ba6SMahe Tardy 
12339b337a3SMahe Tardy char LICENSE[] SEC("license") = "Dual BSD/GPL";
124