xref: /OK3568_Linux_fs/kernel/tools/testing/selftests/bpf/progs/xdping_kern.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. */
3*4882a593Smuzhiyun 
4*4882a593Smuzhiyun #define KBUILD_MODNAME "foo"
5*4882a593Smuzhiyun #include <stddef.h>
6*4882a593Smuzhiyun #include <string.h>
7*4882a593Smuzhiyun #include <linux/bpf.h>
8*4882a593Smuzhiyun #include <linux/icmp.h>
9*4882a593Smuzhiyun #include <linux/in.h>
10*4882a593Smuzhiyun #include <linux/if_ether.h>
11*4882a593Smuzhiyun #include <linux/if_packet.h>
12*4882a593Smuzhiyun #include <linux/if_vlan.h>
13*4882a593Smuzhiyun #include <linux/ip.h>
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun #include <bpf/bpf_helpers.h>
16*4882a593Smuzhiyun #include <bpf/bpf_endian.h>
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #include "xdping.h"
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun struct {
21*4882a593Smuzhiyun 	__uint(type, BPF_MAP_TYPE_HASH);
22*4882a593Smuzhiyun 	__uint(max_entries, 256);
23*4882a593Smuzhiyun 	__type(key, __u32);
24*4882a593Smuzhiyun 	__type(value, struct pinginfo);
25*4882a593Smuzhiyun } ping_map SEC(".maps");
26*4882a593Smuzhiyun 
swap_src_dst_mac(void * data)27*4882a593Smuzhiyun static __always_inline void swap_src_dst_mac(void *data)
28*4882a593Smuzhiyun {
29*4882a593Smuzhiyun 	unsigned short *p = data;
30*4882a593Smuzhiyun 	unsigned short dst[3];
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun 	dst[0] = p[0];
33*4882a593Smuzhiyun 	dst[1] = p[1];
34*4882a593Smuzhiyun 	dst[2] = p[2];
35*4882a593Smuzhiyun 	p[0] = p[3];
36*4882a593Smuzhiyun 	p[1] = p[4];
37*4882a593Smuzhiyun 	p[2] = p[5];
38*4882a593Smuzhiyun 	p[3] = dst[0];
39*4882a593Smuzhiyun 	p[4] = dst[1];
40*4882a593Smuzhiyun 	p[5] = dst[2];
41*4882a593Smuzhiyun }
42*4882a593Smuzhiyun 
csum_fold_helper(__wsum sum)43*4882a593Smuzhiyun static __always_inline __u16 csum_fold_helper(__wsum sum)
44*4882a593Smuzhiyun {
45*4882a593Smuzhiyun 	sum = (sum & 0xffff) + (sum >> 16);
46*4882a593Smuzhiyun 	return ~((sum & 0xffff) + (sum >> 16));
47*4882a593Smuzhiyun }
48*4882a593Smuzhiyun 
ipv4_csum(void * data_start,int data_size)49*4882a593Smuzhiyun static __always_inline __u16 ipv4_csum(void *data_start, int data_size)
50*4882a593Smuzhiyun {
51*4882a593Smuzhiyun 	__wsum sum;
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 	sum = bpf_csum_diff(0, 0, data_start, data_size, 0);
54*4882a593Smuzhiyun 	return csum_fold_helper(sum);
55*4882a593Smuzhiyun }
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun #define ICMP_ECHO_LEN		64
58*4882a593Smuzhiyun 
icmp_check(struct xdp_md * ctx,int type)59*4882a593Smuzhiyun static __always_inline int icmp_check(struct xdp_md *ctx, int type)
60*4882a593Smuzhiyun {
61*4882a593Smuzhiyun 	void *data_end = (void *)(long)ctx->data_end;
62*4882a593Smuzhiyun 	void *data = (void *)(long)ctx->data;
63*4882a593Smuzhiyun 	struct ethhdr *eth = data;
64*4882a593Smuzhiyun 	struct icmphdr *icmph;
65*4882a593Smuzhiyun 	struct iphdr *iph;
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 	if (data + sizeof(*eth) + sizeof(*iph) + ICMP_ECHO_LEN > data_end)
68*4882a593Smuzhiyun 		return XDP_PASS;
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 	if (eth->h_proto != bpf_htons(ETH_P_IP))
71*4882a593Smuzhiyun 		return XDP_PASS;
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 	iph = data + sizeof(*eth);
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 	if (iph->protocol != IPPROTO_ICMP)
76*4882a593Smuzhiyun 		return XDP_PASS;
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 	if (bpf_ntohs(iph->tot_len) - sizeof(*iph) != ICMP_ECHO_LEN)
79*4882a593Smuzhiyun 		return XDP_PASS;
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun 	icmph = data + sizeof(*eth) + sizeof(*iph);
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun 	if (icmph->type != type)
84*4882a593Smuzhiyun 		return XDP_PASS;
85*4882a593Smuzhiyun 
86*4882a593Smuzhiyun 	return XDP_TX;
87*4882a593Smuzhiyun }
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun SEC("xdpclient")
xdping_client(struct xdp_md * ctx)90*4882a593Smuzhiyun int xdping_client(struct xdp_md *ctx)
91*4882a593Smuzhiyun {
92*4882a593Smuzhiyun 	void *data_end = (void *)(long)ctx->data_end;
93*4882a593Smuzhiyun 	void *data = (void *)(long)ctx->data;
94*4882a593Smuzhiyun 	struct pinginfo *pinginfo = NULL;
95*4882a593Smuzhiyun 	struct ethhdr *eth = data;
96*4882a593Smuzhiyun 	struct icmphdr *icmph;
97*4882a593Smuzhiyun 	struct iphdr *iph;
98*4882a593Smuzhiyun 	__u64 recvtime;
99*4882a593Smuzhiyun 	__be32 raddr;
100*4882a593Smuzhiyun 	__be16 seq;
101*4882a593Smuzhiyun 	int ret;
102*4882a593Smuzhiyun 	__u8 i;
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 	ret = icmp_check(ctx, ICMP_ECHOREPLY);
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	if (ret != XDP_TX)
107*4882a593Smuzhiyun 		return ret;
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	iph = data + sizeof(*eth);
110*4882a593Smuzhiyun 	icmph = data + sizeof(*eth) + sizeof(*iph);
111*4882a593Smuzhiyun 	raddr = iph->saddr;
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun 	/* Record time reply received. */
114*4882a593Smuzhiyun 	recvtime = bpf_ktime_get_ns();
115*4882a593Smuzhiyun 	pinginfo = bpf_map_lookup_elem(&ping_map, &raddr);
116*4882a593Smuzhiyun 	if (!pinginfo || pinginfo->seq != icmph->un.echo.sequence)
117*4882a593Smuzhiyun 		return XDP_PASS;
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	if (pinginfo->start) {
120*4882a593Smuzhiyun #pragma clang loop unroll(full)
121*4882a593Smuzhiyun 		for (i = 0; i < XDPING_MAX_COUNT; i++) {
122*4882a593Smuzhiyun 			if (pinginfo->times[i] == 0)
123*4882a593Smuzhiyun 				break;
124*4882a593Smuzhiyun 		}
125*4882a593Smuzhiyun 		/* verifier is fussy here... */
126*4882a593Smuzhiyun 		if (i < XDPING_MAX_COUNT) {
127*4882a593Smuzhiyun 			pinginfo->times[i] = recvtime -
128*4882a593Smuzhiyun 					     pinginfo->start;
129*4882a593Smuzhiyun 			pinginfo->start = 0;
130*4882a593Smuzhiyun 			i++;
131*4882a593Smuzhiyun 		}
132*4882a593Smuzhiyun 		/* No more space for values? */
133*4882a593Smuzhiyun 		if (i == pinginfo->count || i == XDPING_MAX_COUNT)
134*4882a593Smuzhiyun 			return XDP_PASS;
135*4882a593Smuzhiyun 	}
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	/* Now convert reply back into echo request. */
138*4882a593Smuzhiyun 	swap_src_dst_mac(data);
139*4882a593Smuzhiyun 	iph->saddr = iph->daddr;
140*4882a593Smuzhiyun 	iph->daddr = raddr;
141*4882a593Smuzhiyun 	icmph->type = ICMP_ECHO;
142*4882a593Smuzhiyun 	seq = bpf_htons(bpf_ntohs(icmph->un.echo.sequence) + 1);
143*4882a593Smuzhiyun 	icmph->un.echo.sequence = seq;
144*4882a593Smuzhiyun 	icmph->checksum = 0;
145*4882a593Smuzhiyun 	icmph->checksum = ipv4_csum(icmph, ICMP_ECHO_LEN);
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 	pinginfo->seq = seq;
148*4882a593Smuzhiyun 	pinginfo->start = bpf_ktime_get_ns();
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	return XDP_TX;
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun SEC("xdpserver")
xdping_server(struct xdp_md * ctx)154*4882a593Smuzhiyun int xdping_server(struct xdp_md *ctx)
155*4882a593Smuzhiyun {
156*4882a593Smuzhiyun 	void *data_end = (void *)(long)ctx->data_end;
157*4882a593Smuzhiyun 	void *data = (void *)(long)ctx->data;
158*4882a593Smuzhiyun 	struct ethhdr *eth = data;
159*4882a593Smuzhiyun 	struct icmphdr *icmph;
160*4882a593Smuzhiyun 	struct iphdr *iph;
161*4882a593Smuzhiyun 	__be32 raddr;
162*4882a593Smuzhiyun 	int ret;
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	ret = icmp_check(ctx, ICMP_ECHO);
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	if (ret != XDP_TX)
167*4882a593Smuzhiyun 		return ret;
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	iph = data + sizeof(*eth);
170*4882a593Smuzhiyun 	icmph = data + sizeof(*eth) + sizeof(*iph);
171*4882a593Smuzhiyun 	raddr = iph->saddr;
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 	/* Now convert request into echo reply. */
174*4882a593Smuzhiyun 	swap_src_dst_mac(data);
175*4882a593Smuzhiyun 	iph->saddr = iph->daddr;
176*4882a593Smuzhiyun 	iph->daddr = raddr;
177*4882a593Smuzhiyun 	icmph->type = ICMP_ECHOREPLY;
178*4882a593Smuzhiyun 	icmph->checksum = 0;
179*4882a593Smuzhiyun 	icmph->checksum = ipv4_csum(icmph, ICMP_ECHO_LEN);
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	return XDP_TX;
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun char _license[] SEC("license") = "GPL";
185