1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. */
4 #define KBUILD_MODNAME "foo"
8 #include <linux/icmp.h>
10 #include <linux/if_ether.h>
11 #include <linux/if_packet.h>
12 #include <linux/if_vlan.h>
15 #include <bpf/bpf_helpers.h>
16 #include <bpf/bpf_endian.h>
21 __uint(type, BPF_MAP_TYPE_HASH);
22 __uint(max_entries, 256);
24 __type(value, struct pinginfo);
25 } ping_map SEC(".maps");
27 static __always_inline void swap_src_dst_mac(void *data)
29 unsigned short *p = data;
30 unsigned short dst[3];
43 static __always_inline __u16 csum_fold_helper(__wsum sum)
45 sum = (sum & 0xffff) + (sum >> 16);
46 return ~((sum & 0xffff) + (sum >> 16));
49 static __always_inline __u16 ipv4_csum(void *data_start, int data_size)
53 sum = bpf_csum_diff(0, 0, data_start, data_size, 0);
54 return csum_fold_helper(sum);
57 #define ICMP_ECHO_LEN 64
59 static __always_inline int icmp_check(struct xdp_md *ctx, int type)
61 void *data_end = (void *)(long)ctx->data_end;
62 void *data = (void *)(long)ctx->data;
63 struct ethhdr *eth = data;
64 struct icmphdr *icmph;
67 if (data + sizeof(*eth) + sizeof(*iph) + ICMP_ECHO_LEN > data_end)
70 if (eth->h_proto != bpf_htons(ETH_P_IP))
73 iph = data + sizeof(*eth);
75 if (iph->protocol != IPPROTO_ICMP)
78 if (bpf_ntohs(iph->tot_len) - sizeof(*iph) != ICMP_ECHO_LEN)
81 icmph = data + sizeof(*eth) + sizeof(*iph);
83 if (icmph->type != type)
90 int xdping_client(struct xdp_md *ctx)
92 void *data = (void *)(long)ctx->data;
93 struct pinginfo *pinginfo = NULL;
94 struct ethhdr *eth = data;
95 struct icmphdr *icmph;
103 ret = icmp_check(ctx, ICMP_ECHOREPLY);
108 iph = data + sizeof(*eth);
109 icmph = data + sizeof(*eth) + sizeof(*iph);
112 /* Record time reply received. */
113 recvtime = bpf_ktime_get_ns();
114 pinginfo = bpf_map_lookup_elem(&ping_map, &raddr);
115 if (!pinginfo || pinginfo->seq != icmph->un.echo.sequence)
118 if (pinginfo->start) {
119 #pragma clang loop unroll(full)
120 for (i = 0; i < XDPING_MAX_COUNT; i++) {
121 if (pinginfo->times[i] == 0)
124 /* verifier is fussy here... */
125 if (i < XDPING_MAX_COUNT) {
126 pinginfo->times[i] = recvtime -
131 /* No more space for values? */
132 if (i == pinginfo->count || i == XDPING_MAX_COUNT)
136 /* Now convert reply back into echo request. */
137 swap_src_dst_mac(data);
138 iph->saddr = iph->daddr;
140 icmph->type = ICMP_ECHO;
141 seq = bpf_htons(bpf_ntohs(icmph->un.echo.sequence) + 1);
142 icmph->un.echo.sequence = seq;
144 icmph->checksum = ipv4_csum(icmph, ICMP_ECHO_LEN);
147 pinginfo->start = bpf_ktime_get_ns();
153 int xdping_server(struct xdp_md *ctx)
155 void *data = (void *)(long)ctx->data;
156 struct ethhdr *eth = data;
157 struct icmphdr *icmph;
162 ret = icmp_check(ctx, ICMP_ECHO);
167 iph = data + sizeof(*eth);
168 icmph = data + sizeof(*eth) + sizeof(*iph);
171 /* Now convert request into echo reply. */
172 swap_src_dst_mac(data);
173 iph->saddr = iph->daddr;
175 icmph->type = ICMP_ECHOREPLY;
177 icmph->checksum = ipv4_csum(icmph, ICMP_ECHO_LEN);
182 char _license[] SEC("license") = "GPL";