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