0001
0002
0003
0004 #define KBUILD_MODNAME "foo"
0005 #include <stddef.h>
0006 #include <string.h>
0007 #include <linux/bpf.h>
0008 #include <linux/icmp.h>
0009 #include <linux/in.h>
0010 #include <linux/if_ether.h>
0011 #include <linux/if_packet.h>
0012 #include <linux/if_vlan.h>
0013 #include <linux/ip.h>
0014
0015 #include <bpf/bpf_helpers.h>
0016 #include <bpf/bpf_endian.h>
0017
0018 #include "xdping.h"
0019
0020 struct {
0021 __uint(type, BPF_MAP_TYPE_HASH);
0022 __uint(max_entries, 256);
0023 __type(key, __u32);
0024 __type(value, struct pinginfo);
0025 } ping_map SEC(".maps");
0026
0027 static __always_inline void swap_src_dst_mac(void *data)
0028 {
0029 unsigned short *p = data;
0030 unsigned short dst[3];
0031
0032 dst[0] = p[0];
0033 dst[1] = p[1];
0034 dst[2] = p[2];
0035 p[0] = p[3];
0036 p[1] = p[4];
0037 p[2] = p[5];
0038 p[3] = dst[0];
0039 p[4] = dst[1];
0040 p[5] = dst[2];
0041 }
0042
0043 static __always_inline __u16 csum_fold_helper(__wsum sum)
0044 {
0045 sum = (sum & 0xffff) + (sum >> 16);
0046 return ~((sum & 0xffff) + (sum >> 16));
0047 }
0048
0049 static __always_inline __u16 ipv4_csum(void *data_start, int data_size)
0050 {
0051 __wsum sum;
0052
0053 sum = bpf_csum_diff(0, 0, data_start, data_size, 0);
0054 return csum_fold_helper(sum);
0055 }
0056
0057 #define ICMP_ECHO_LEN 64
0058
0059 static __always_inline int icmp_check(struct xdp_md *ctx, int type)
0060 {
0061 void *data_end = (void *)(long)ctx->data_end;
0062 void *data = (void *)(long)ctx->data;
0063 struct ethhdr *eth = data;
0064 struct icmphdr *icmph;
0065 struct iphdr *iph;
0066
0067 if (data + sizeof(*eth) + sizeof(*iph) + ICMP_ECHO_LEN > data_end)
0068 return XDP_PASS;
0069
0070 if (eth->h_proto != bpf_htons(ETH_P_IP))
0071 return XDP_PASS;
0072
0073 iph = data + sizeof(*eth);
0074
0075 if (iph->protocol != IPPROTO_ICMP)
0076 return XDP_PASS;
0077
0078 if (bpf_ntohs(iph->tot_len) - sizeof(*iph) != ICMP_ECHO_LEN)
0079 return XDP_PASS;
0080
0081 icmph = data + sizeof(*eth) + sizeof(*iph);
0082
0083 if (icmph->type != type)
0084 return XDP_PASS;
0085
0086 return XDP_TX;
0087 }
0088
0089 SEC("xdp")
0090 int xdping_client(struct xdp_md *ctx)
0091 {
0092 void *data_end = (void *)(long)ctx->data_end;
0093 void *data = (void *)(long)ctx->data;
0094 struct pinginfo *pinginfo = NULL;
0095 struct ethhdr *eth = data;
0096 struct icmphdr *icmph;
0097 struct iphdr *iph;
0098 __u64 recvtime;
0099 __be32 raddr;
0100 __be16 seq;
0101 int ret;
0102 __u8 i;
0103
0104 ret = icmp_check(ctx, ICMP_ECHOREPLY);
0105
0106 if (ret != XDP_TX)
0107 return ret;
0108
0109 iph = data + sizeof(*eth);
0110 icmph = data + sizeof(*eth) + sizeof(*iph);
0111 raddr = iph->saddr;
0112
0113
0114 recvtime = bpf_ktime_get_ns();
0115 pinginfo = bpf_map_lookup_elem(&ping_map, &raddr);
0116 if (!pinginfo || pinginfo->seq != icmph->un.echo.sequence)
0117 return XDP_PASS;
0118
0119 if (pinginfo->start) {
0120 #pragma clang loop unroll(full)
0121 for (i = 0; i < XDPING_MAX_COUNT; i++) {
0122 if (pinginfo->times[i] == 0)
0123 break;
0124 }
0125
0126 if (i < XDPING_MAX_COUNT) {
0127 pinginfo->times[i] = recvtime -
0128 pinginfo->start;
0129 pinginfo->start = 0;
0130 i++;
0131 }
0132
0133 if (i == pinginfo->count || i == XDPING_MAX_COUNT)
0134 return XDP_PASS;
0135 }
0136
0137
0138 swap_src_dst_mac(data);
0139 iph->saddr = iph->daddr;
0140 iph->daddr = raddr;
0141 icmph->type = ICMP_ECHO;
0142 seq = bpf_htons(bpf_ntohs(icmph->un.echo.sequence) + 1);
0143 icmph->un.echo.sequence = seq;
0144 icmph->checksum = 0;
0145 icmph->checksum = ipv4_csum(icmph, ICMP_ECHO_LEN);
0146
0147 pinginfo->seq = seq;
0148 pinginfo->start = bpf_ktime_get_ns();
0149
0150 return XDP_TX;
0151 }
0152
0153 SEC("xdp")
0154 int xdping_server(struct xdp_md *ctx)
0155 {
0156 void *data_end = (void *)(long)ctx->data_end;
0157 void *data = (void *)(long)ctx->data;
0158 struct ethhdr *eth = data;
0159 struct icmphdr *icmph;
0160 struct iphdr *iph;
0161 __be32 raddr;
0162 int ret;
0163
0164 ret = icmp_check(ctx, ICMP_ECHO);
0165
0166 if (ret != XDP_TX)
0167 return ret;
0168
0169 iph = data + sizeof(*eth);
0170 icmph = data + sizeof(*eth) + sizeof(*iph);
0171 raddr = iph->saddr;
0172
0173
0174 swap_src_dst_mac(data);
0175 iph->saddr = iph->daddr;
0176 iph->daddr = raddr;
0177 icmph->type = ICMP_ECHOREPLY;
0178 icmph->checksum = 0;
0179 icmph->checksum = ipv4_csum(icmph, ICMP_ECHO_LEN);
0180
0181 return XDP_TX;
0182 }
0183
0184 char _license[] SEC("license") = "GPL";