162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#define KBUILD_MODNAME "foo" 562306a36Sopenharmony_ci#include <stddef.h> 662306a36Sopenharmony_ci#include <string.h> 762306a36Sopenharmony_ci#include <linux/bpf.h> 862306a36Sopenharmony_ci#include <linux/icmp.h> 962306a36Sopenharmony_ci#include <linux/in.h> 1062306a36Sopenharmony_ci#include <linux/if_ether.h> 1162306a36Sopenharmony_ci#include <linux/if_packet.h> 1262306a36Sopenharmony_ci#include <linux/if_vlan.h> 1362306a36Sopenharmony_ci#include <linux/ip.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <bpf/bpf_helpers.h> 1662306a36Sopenharmony_ci#include <bpf/bpf_endian.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "xdping.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct { 2162306a36Sopenharmony_ci __uint(type, BPF_MAP_TYPE_HASH); 2262306a36Sopenharmony_ci __uint(max_entries, 256); 2362306a36Sopenharmony_ci __type(key, __u32); 2462306a36Sopenharmony_ci __type(value, struct pinginfo); 2562306a36Sopenharmony_ci} ping_map SEC(".maps"); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic __always_inline void swap_src_dst_mac(void *data) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci unsigned short *p = data; 3062306a36Sopenharmony_ci unsigned short dst[3]; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci dst[0] = p[0]; 3362306a36Sopenharmony_ci dst[1] = p[1]; 3462306a36Sopenharmony_ci dst[2] = p[2]; 3562306a36Sopenharmony_ci p[0] = p[3]; 3662306a36Sopenharmony_ci p[1] = p[4]; 3762306a36Sopenharmony_ci p[2] = p[5]; 3862306a36Sopenharmony_ci p[3] = dst[0]; 3962306a36Sopenharmony_ci p[4] = dst[1]; 4062306a36Sopenharmony_ci p[5] = dst[2]; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic __always_inline __u16 csum_fold_helper(__wsum sum) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci sum = (sum & 0xffff) + (sum >> 16); 4662306a36Sopenharmony_ci return ~((sum & 0xffff) + (sum >> 16)); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic __always_inline __u16 ipv4_csum(void *data_start, int data_size) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci __wsum sum; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci sum = bpf_csum_diff(0, 0, data_start, data_size, 0); 5462306a36Sopenharmony_ci return csum_fold_helper(sum); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define ICMP_ECHO_LEN 64 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic __always_inline int icmp_check(struct xdp_md *ctx, int type) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci void *data_end = (void *)(long)ctx->data_end; 6262306a36Sopenharmony_ci void *data = (void *)(long)ctx->data; 6362306a36Sopenharmony_ci struct ethhdr *eth = data; 6462306a36Sopenharmony_ci struct icmphdr *icmph; 6562306a36Sopenharmony_ci struct iphdr *iph; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (data + sizeof(*eth) + sizeof(*iph) + ICMP_ECHO_LEN > data_end) 6862306a36Sopenharmony_ci return XDP_PASS; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (eth->h_proto != bpf_htons(ETH_P_IP)) 7162306a36Sopenharmony_ci return XDP_PASS; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci iph = data + sizeof(*eth); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (iph->protocol != IPPROTO_ICMP) 7662306a36Sopenharmony_ci return XDP_PASS; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (bpf_ntohs(iph->tot_len) - sizeof(*iph) != ICMP_ECHO_LEN) 7962306a36Sopenharmony_ci return XDP_PASS; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci icmph = data + sizeof(*eth) + sizeof(*iph); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (icmph->type != type) 8462306a36Sopenharmony_ci return XDP_PASS; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return XDP_TX; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ciSEC("xdp") 9062306a36Sopenharmony_ciint xdping_client(struct xdp_md *ctx) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci void *data = (void *)(long)ctx->data; 9362306a36Sopenharmony_ci struct pinginfo *pinginfo = NULL; 9462306a36Sopenharmony_ci struct ethhdr *eth = data; 9562306a36Sopenharmony_ci struct icmphdr *icmph; 9662306a36Sopenharmony_ci struct iphdr *iph; 9762306a36Sopenharmony_ci __u64 recvtime; 9862306a36Sopenharmony_ci __be32 raddr; 9962306a36Sopenharmony_ci __be16 seq; 10062306a36Sopenharmony_ci int ret; 10162306a36Sopenharmony_ci __u8 i; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci ret = icmp_check(ctx, ICMP_ECHOREPLY); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (ret != XDP_TX) 10662306a36Sopenharmony_ci return ret; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci iph = data + sizeof(*eth); 10962306a36Sopenharmony_ci icmph = data + sizeof(*eth) + sizeof(*iph); 11062306a36Sopenharmony_ci raddr = iph->saddr; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* Record time reply received. */ 11362306a36Sopenharmony_ci recvtime = bpf_ktime_get_ns(); 11462306a36Sopenharmony_ci pinginfo = bpf_map_lookup_elem(&ping_map, &raddr); 11562306a36Sopenharmony_ci if (!pinginfo || pinginfo->seq != icmph->un.echo.sequence) 11662306a36Sopenharmony_ci return XDP_PASS; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (pinginfo->start) { 11962306a36Sopenharmony_ci#pragma clang loop unroll(full) 12062306a36Sopenharmony_ci for (i = 0; i < XDPING_MAX_COUNT; i++) { 12162306a36Sopenharmony_ci if (pinginfo->times[i] == 0) 12262306a36Sopenharmony_ci break; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci /* verifier is fussy here... */ 12562306a36Sopenharmony_ci if (i < XDPING_MAX_COUNT) { 12662306a36Sopenharmony_ci pinginfo->times[i] = recvtime - 12762306a36Sopenharmony_ci pinginfo->start; 12862306a36Sopenharmony_ci pinginfo->start = 0; 12962306a36Sopenharmony_ci i++; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci /* No more space for values? */ 13262306a36Sopenharmony_ci if (i == pinginfo->count || i == XDPING_MAX_COUNT) 13362306a36Sopenharmony_ci return XDP_PASS; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* Now convert reply back into echo request. */ 13762306a36Sopenharmony_ci swap_src_dst_mac(data); 13862306a36Sopenharmony_ci iph->saddr = iph->daddr; 13962306a36Sopenharmony_ci iph->daddr = raddr; 14062306a36Sopenharmony_ci icmph->type = ICMP_ECHO; 14162306a36Sopenharmony_ci seq = bpf_htons(bpf_ntohs(icmph->un.echo.sequence) + 1); 14262306a36Sopenharmony_ci icmph->un.echo.sequence = seq; 14362306a36Sopenharmony_ci icmph->checksum = 0; 14462306a36Sopenharmony_ci icmph->checksum = ipv4_csum(icmph, ICMP_ECHO_LEN); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci pinginfo->seq = seq; 14762306a36Sopenharmony_ci pinginfo->start = bpf_ktime_get_ns(); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return XDP_TX; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ciSEC("xdp") 15362306a36Sopenharmony_ciint xdping_server(struct xdp_md *ctx) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci void *data = (void *)(long)ctx->data; 15662306a36Sopenharmony_ci struct ethhdr *eth = data; 15762306a36Sopenharmony_ci struct icmphdr *icmph; 15862306a36Sopenharmony_ci struct iphdr *iph; 15962306a36Sopenharmony_ci __be32 raddr; 16062306a36Sopenharmony_ci int ret; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci ret = icmp_check(ctx, ICMP_ECHO); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (ret != XDP_TX) 16562306a36Sopenharmony_ci return ret; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci iph = data + sizeof(*eth); 16862306a36Sopenharmony_ci icmph = data + sizeof(*eth) + sizeof(*iph); 16962306a36Sopenharmony_ci raddr = iph->saddr; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* Now convert request into echo reply. */ 17262306a36Sopenharmony_ci swap_src_dst_mac(data); 17362306a36Sopenharmony_ci iph->saddr = iph->daddr; 17462306a36Sopenharmony_ci iph->daddr = raddr; 17562306a36Sopenharmony_ci icmph->type = ICMP_ECHOREPLY; 17662306a36Sopenharmony_ci icmph->checksum = 0; 17762306a36Sopenharmony_ci icmph->checksum = ipv4_csum(icmph, ICMP_ECHO_LEN); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return XDP_TX; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cichar _license[] SEC("license") = "GPL"; 183