18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#define KBUILD_MODNAME "foo" 58c2ecf20Sopenharmony_ci#include <stddef.h> 68c2ecf20Sopenharmony_ci#include <string.h> 78c2ecf20Sopenharmony_ci#include <linux/bpf.h> 88c2ecf20Sopenharmony_ci#include <linux/icmp.h> 98c2ecf20Sopenharmony_ci#include <linux/in.h> 108c2ecf20Sopenharmony_ci#include <linux/if_ether.h> 118c2ecf20Sopenharmony_ci#include <linux/if_packet.h> 128c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 138c2ecf20Sopenharmony_ci#include <linux/ip.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <bpf/bpf_helpers.h> 168c2ecf20Sopenharmony_ci#include <bpf/bpf_endian.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "xdping.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct { 218c2ecf20Sopenharmony_ci __uint(type, BPF_MAP_TYPE_HASH); 228c2ecf20Sopenharmony_ci __uint(max_entries, 256); 238c2ecf20Sopenharmony_ci __type(key, __u32); 248c2ecf20Sopenharmony_ci __type(value, struct pinginfo); 258c2ecf20Sopenharmony_ci} ping_map SEC(".maps"); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic __always_inline void swap_src_dst_mac(void *data) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci unsigned short *p = data; 308c2ecf20Sopenharmony_ci unsigned short dst[3]; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci dst[0] = p[0]; 338c2ecf20Sopenharmony_ci dst[1] = p[1]; 348c2ecf20Sopenharmony_ci dst[2] = p[2]; 358c2ecf20Sopenharmony_ci p[0] = p[3]; 368c2ecf20Sopenharmony_ci p[1] = p[4]; 378c2ecf20Sopenharmony_ci p[2] = p[5]; 388c2ecf20Sopenharmony_ci p[3] = dst[0]; 398c2ecf20Sopenharmony_ci p[4] = dst[1]; 408c2ecf20Sopenharmony_ci p[5] = dst[2]; 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic __always_inline __u16 csum_fold_helper(__wsum sum) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci sum = (sum & 0xffff) + (sum >> 16); 468c2ecf20Sopenharmony_ci return ~((sum & 0xffff) + (sum >> 16)); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic __always_inline __u16 ipv4_csum(void *data_start, int data_size) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci __wsum sum; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci sum = bpf_csum_diff(0, 0, data_start, data_size, 0); 548c2ecf20Sopenharmony_ci return csum_fold_helper(sum); 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define ICMP_ECHO_LEN 64 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic __always_inline int icmp_check(struct xdp_md *ctx, int type) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci void *data_end = (void *)(long)ctx->data_end; 628c2ecf20Sopenharmony_ci void *data = (void *)(long)ctx->data; 638c2ecf20Sopenharmony_ci struct ethhdr *eth = data; 648c2ecf20Sopenharmony_ci struct icmphdr *icmph; 658c2ecf20Sopenharmony_ci struct iphdr *iph; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (data + sizeof(*eth) + sizeof(*iph) + ICMP_ECHO_LEN > data_end) 688c2ecf20Sopenharmony_ci return XDP_PASS; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (eth->h_proto != bpf_htons(ETH_P_IP)) 718c2ecf20Sopenharmony_ci return XDP_PASS; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci iph = data + sizeof(*eth); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (iph->protocol != IPPROTO_ICMP) 768c2ecf20Sopenharmony_ci return XDP_PASS; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (bpf_ntohs(iph->tot_len) - sizeof(*iph) != ICMP_ECHO_LEN) 798c2ecf20Sopenharmony_ci return XDP_PASS; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci icmph = data + sizeof(*eth) + sizeof(*iph); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (icmph->type != type) 848c2ecf20Sopenharmony_ci return XDP_PASS; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci return XDP_TX; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ciSEC("xdpclient") 908c2ecf20Sopenharmony_ciint xdping_client(struct xdp_md *ctx) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci void *data_end = (void *)(long)ctx->data_end; 938c2ecf20Sopenharmony_ci void *data = (void *)(long)ctx->data; 948c2ecf20Sopenharmony_ci struct pinginfo *pinginfo = NULL; 958c2ecf20Sopenharmony_ci struct ethhdr *eth = data; 968c2ecf20Sopenharmony_ci struct icmphdr *icmph; 978c2ecf20Sopenharmony_ci struct iphdr *iph; 988c2ecf20Sopenharmony_ci __u64 recvtime; 998c2ecf20Sopenharmony_ci __be32 raddr; 1008c2ecf20Sopenharmony_ci __be16 seq; 1018c2ecf20Sopenharmony_ci int ret; 1028c2ecf20Sopenharmony_ci __u8 i; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci ret = icmp_check(ctx, ICMP_ECHOREPLY); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (ret != XDP_TX) 1078c2ecf20Sopenharmony_ci return ret; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci iph = data + sizeof(*eth); 1108c2ecf20Sopenharmony_ci icmph = data + sizeof(*eth) + sizeof(*iph); 1118c2ecf20Sopenharmony_ci raddr = iph->saddr; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* Record time reply received. */ 1148c2ecf20Sopenharmony_ci recvtime = bpf_ktime_get_ns(); 1158c2ecf20Sopenharmony_ci pinginfo = bpf_map_lookup_elem(&ping_map, &raddr); 1168c2ecf20Sopenharmony_ci if (!pinginfo || pinginfo->seq != icmph->un.echo.sequence) 1178c2ecf20Sopenharmony_ci return XDP_PASS; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (pinginfo->start) { 1208c2ecf20Sopenharmony_ci#pragma clang loop unroll(full) 1218c2ecf20Sopenharmony_ci for (i = 0; i < XDPING_MAX_COUNT; i++) { 1228c2ecf20Sopenharmony_ci if (pinginfo->times[i] == 0) 1238c2ecf20Sopenharmony_ci break; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci /* verifier is fussy here... */ 1268c2ecf20Sopenharmony_ci if (i < XDPING_MAX_COUNT) { 1278c2ecf20Sopenharmony_ci pinginfo->times[i] = recvtime - 1288c2ecf20Sopenharmony_ci pinginfo->start; 1298c2ecf20Sopenharmony_ci pinginfo->start = 0; 1308c2ecf20Sopenharmony_ci i++; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci /* No more space for values? */ 1338c2ecf20Sopenharmony_ci if (i == pinginfo->count || i == XDPING_MAX_COUNT) 1348c2ecf20Sopenharmony_ci return XDP_PASS; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* Now convert reply back into echo request. */ 1388c2ecf20Sopenharmony_ci swap_src_dst_mac(data); 1398c2ecf20Sopenharmony_ci iph->saddr = iph->daddr; 1408c2ecf20Sopenharmony_ci iph->daddr = raddr; 1418c2ecf20Sopenharmony_ci icmph->type = ICMP_ECHO; 1428c2ecf20Sopenharmony_ci seq = bpf_htons(bpf_ntohs(icmph->un.echo.sequence) + 1); 1438c2ecf20Sopenharmony_ci icmph->un.echo.sequence = seq; 1448c2ecf20Sopenharmony_ci icmph->checksum = 0; 1458c2ecf20Sopenharmony_ci icmph->checksum = ipv4_csum(icmph, ICMP_ECHO_LEN); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci pinginfo->seq = seq; 1488c2ecf20Sopenharmony_ci pinginfo->start = bpf_ktime_get_ns(); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return XDP_TX; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ciSEC("xdpserver") 1548c2ecf20Sopenharmony_ciint xdping_server(struct xdp_md *ctx) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci void *data_end = (void *)(long)ctx->data_end; 1578c2ecf20Sopenharmony_ci void *data = (void *)(long)ctx->data; 1588c2ecf20Sopenharmony_ci struct ethhdr *eth = data; 1598c2ecf20Sopenharmony_ci struct icmphdr *icmph; 1608c2ecf20Sopenharmony_ci struct iphdr *iph; 1618c2ecf20Sopenharmony_ci __be32 raddr; 1628c2ecf20Sopenharmony_ci int ret; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci ret = icmp_check(ctx, ICMP_ECHO); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (ret != XDP_TX) 1678c2ecf20Sopenharmony_ci return ret; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci iph = data + sizeof(*eth); 1708c2ecf20Sopenharmony_ci icmph = data + sizeof(*eth) + sizeof(*iph); 1718c2ecf20Sopenharmony_ci raddr = iph->saddr; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* Now convert request into echo reply. */ 1748c2ecf20Sopenharmony_ci swap_src_dst_mac(data); 1758c2ecf20Sopenharmony_ci iph->saddr = iph->daddr; 1768c2ecf20Sopenharmony_ci iph->daddr = raddr; 1778c2ecf20Sopenharmony_ci icmph->type = ICMP_ECHOREPLY; 1788c2ecf20Sopenharmony_ci icmph->checksum = 0; 1798c2ecf20Sopenharmony_ci icmph->checksum = ipv4_csum(icmph, ICMP_ECHO_LEN); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return XDP_TX; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cichar _license[] SEC("license") = "GPL"; 185