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