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