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_end
= (void *)(long)ctx
->data_end
;
93 void *data
= (void *)(long)ctx
->data
;
94 struct pinginfo
*pinginfo
= NULL
;
95 struct ethhdr
*eth
= data
;
96 struct icmphdr
*icmph
;
104 ret
= icmp_check(ctx
, ICMP_ECHOREPLY
);
109 iph
= data
+ sizeof(*eth
);
110 icmph
= data
+ sizeof(*eth
) + sizeof(*iph
);
113 /* Record time reply received. */
114 recvtime
= bpf_ktime_get_ns();
115 pinginfo
= bpf_map_lookup_elem(&ping_map
, &raddr
);
116 if (!pinginfo
|| pinginfo
->seq
!= icmph
->un
.echo
.sequence
)
119 if (pinginfo
->start
) {
120 #pragma clang loop unroll(full)
121 for (i
= 0; i
< XDPING_MAX_COUNT
; i
++) {
122 if (pinginfo
->times
[i
] == 0)
125 /* verifier is fussy here... */
126 if (i
< XDPING_MAX_COUNT
) {
127 pinginfo
->times
[i
] = recvtime
-
132 /* No more space for values? */
133 if (i
== pinginfo
->count
|| i
== XDPING_MAX_COUNT
)
137 /* Now convert reply back into echo request. */
138 swap_src_dst_mac(data
);
139 iph
->saddr
= iph
->daddr
;
141 icmph
->type
= ICMP_ECHO
;
142 seq
= bpf_htons(bpf_ntohs(icmph
->un
.echo
.sequence
) + 1);
143 icmph
->un
.echo
.sequence
= seq
;
145 icmph
->checksum
= ipv4_csum(icmph
, ICMP_ECHO_LEN
);
148 pinginfo
->start
= bpf_ktime_get_ns();
154 int xdping_server(struct xdp_md
*ctx
)
156 void *data_end
= (void *)(long)ctx
->data_end
;
157 void *data
= (void *)(long)ctx
->data
;
158 struct ethhdr
*eth
= data
;
159 struct icmphdr
*icmph
;
164 ret
= icmp_check(ctx
, ICMP_ECHO
);
169 iph
= data
+ sizeof(*eth
);
170 icmph
= data
+ sizeof(*eth
) + sizeof(*iph
);
173 /* Now convert request into echo reply. */
174 swap_src_dst_mac(data
);
175 iph
->saddr
= iph
->daddr
;
177 icmph
->type
= ICMP_ECHOREPLY
;
179 icmph
->checksum
= ipv4_csum(icmph
, ICMP_ECHO_LEN
);
184 char _license
[] SEC("license") = "GPL";