18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci * Copyright (c) 2018 Facebook 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 58c2ecf20Sopenharmony_ci * modify it under the terms of version 2 of the GNU General Public 68c2ecf20Sopenharmony_ci * License as published by the Free Software Foundation. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This program shows how to use bpf_xdp_adjust_tail() by 98c2ecf20Sopenharmony_ci * generating ICMPv4 "packet to big" (unreachable/ df bit set frag needed 108c2ecf20Sopenharmony_ci * to be more preice in case of v4)" where receiving packets bigger then 118c2ecf20Sopenharmony_ci * 600 bytes. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci#define KBUILD_MODNAME "foo" 148c2ecf20Sopenharmony_ci#include <uapi/linux/bpf.h> 158c2ecf20Sopenharmony_ci#include <linux/in.h> 168c2ecf20Sopenharmony_ci#include <linux/if_ether.h> 178c2ecf20Sopenharmony_ci#include <linux/if_packet.h> 188c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 198c2ecf20Sopenharmony_ci#include <linux/ip.h> 208c2ecf20Sopenharmony_ci#include <linux/icmp.h> 218c2ecf20Sopenharmony_ci#include <bpf/bpf_helpers.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define DEFAULT_TTL 64 248c2ecf20Sopenharmony_ci#define MAX_PCKT_SIZE 600 258c2ecf20Sopenharmony_ci#define ICMP_TOOBIG_SIZE 98 268c2ecf20Sopenharmony_ci#define ICMP_TOOBIG_PAYLOAD_SIZE 92 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* volatile to prevent compiler optimizations */ 298c2ecf20Sopenharmony_cistatic volatile __u32 max_pcktsz = MAX_PCKT_SIZE; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct { 328c2ecf20Sopenharmony_ci __uint(type, BPF_MAP_TYPE_ARRAY); 338c2ecf20Sopenharmony_ci __type(key, __u32); 348c2ecf20Sopenharmony_ci __type(value, __u64); 358c2ecf20Sopenharmony_ci __uint(max_entries, 1); 368c2ecf20Sopenharmony_ci} icmpcnt SEC(".maps"); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic __always_inline void count_icmp(void) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci u64 key = 0; 418c2ecf20Sopenharmony_ci u64 *icmp_count; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci icmp_count = bpf_map_lookup_elem(&icmpcnt, &key); 448c2ecf20Sopenharmony_ci if (icmp_count) 458c2ecf20Sopenharmony_ci *icmp_count += 1; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic __always_inline void swap_mac(void *data, struct ethhdr *orig_eth) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct ethhdr *eth; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci eth = data; 538c2ecf20Sopenharmony_ci memcpy(eth->h_source, orig_eth->h_dest, ETH_ALEN); 548c2ecf20Sopenharmony_ci memcpy(eth->h_dest, orig_eth->h_source, ETH_ALEN); 558c2ecf20Sopenharmony_ci eth->h_proto = orig_eth->h_proto; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic __always_inline __u16 csum_fold_helper(__u32 csum) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci return ~((csum & 0xffff) + (csum >> 16)); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic __always_inline void ipv4_csum(void *data_start, int data_size, 648c2ecf20Sopenharmony_ci __u32 *csum) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci *csum = bpf_csum_diff(0, 0, data_start, data_size, *csum); 678c2ecf20Sopenharmony_ci *csum = csum_fold_helper(*csum); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic __always_inline int send_icmp4_too_big(struct xdp_md *xdp) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci int headroom = (int)sizeof(struct iphdr) + (int)sizeof(struct icmphdr); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (bpf_xdp_adjust_head(xdp, 0 - headroom)) 758c2ecf20Sopenharmony_ci return XDP_DROP; 768c2ecf20Sopenharmony_ci void *data = (void *)(long)xdp->data; 778c2ecf20Sopenharmony_ci void *data_end = (void *)(long)xdp->data_end; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (data + (ICMP_TOOBIG_SIZE + headroom) > data_end) 808c2ecf20Sopenharmony_ci return XDP_DROP; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci struct iphdr *iph, *orig_iph; 838c2ecf20Sopenharmony_ci struct icmphdr *icmp_hdr; 848c2ecf20Sopenharmony_ci struct ethhdr *orig_eth; 858c2ecf20Sopenharmony_ci __u32 csum = 0; 868c2ecf20Sopenharmony_ci __u64 off = 0; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci orig_eth = data + headroom; 898c2ecf20Sopenharmony_ci swap_mac(data, orig_eth); 908c2ecf20Sopenharmony_ci off += sizeof(struct ethhdr); 918c2ecf20Sopenharmony_ci iph = data + off; 928c2ecf20Sopenharmony_ci off += sizeof(struct iphdr); 938c2ecf20Sopenharmony_ci icmp_hdr = data + off; 948c2ecf20Sopenharmony_ci off += sizeof(struct icmphdr); 958c2ecf20Sopenharmony_ci orig_iph = data + off; 968c2ecf20Sopenharmony_ci icmp_hdr->type = ICMP_DEST_UNREACH; 978c2ecf20Sopenharmony_ci icmp_hdr->code = ICMP_FRAG_NEEDED; 988c2ecf20Sopenharmony_ci icmp_hdr->un.frag.mtu = htons(max_pcktsz - sizeof(struct ethhdr)); 998c2ecf20Sopenharmony_ci icmp_hdr->checksum = 0; 1008c2ecf20Sopenharmony_ci ipv4_csum(icmp_hdr, ICMP_TOOBIG_PAYLOAD_SIZE, &csum); 1018c2ecf20Sopenharmony_ci icmp_hdr->checksum = csum; 1028c2ecf20Sopenharmony_ci iph->ttl = DEFAULT_TTL; 1038c2ecf20Sopenharmony_ci iph->daddr = orig_iph->saddr; 1048c2ecf20Sopenharmony_ci iph->saddr = orig_iph->daddr; 1058c2ecf20Sopenharmony_ci iph->version = 4; 1068c2ecf20Sopenharmony_ci iph->ihl = 5; 1078c2ecf20Sopenharmony_ci iph->protocol = IPPROTO_ICMP; 1088c2ecf20Sopenharmony_ci iph->tos = 0; 1098c2ecf20Sopenharmony_ci iph->tot_len = htons( 1108c2ecf20Sopenharmony_ci ICMP_TOOBIG_SIZE + headroom - sizeof(struct ethhdr)); 1118c2ecf20Sopenharmony_ci iph->check = 0; 1128c2ecf20Sopenharmony_ci csum = 0; 1138c2ecf20Sopenharmony_ci ipv4_csum(iph, sizeof(struct iphdr), &csum); 1148c2ecf20Sopenharmony_ci iph->check = csum; 1158c2ecf20Sopenharmony_ci count_icmp(); 1168c2ecf20Sopenharmony_ci return XDP_TX; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic __always_inline int handle_ipv4(struct xdp_md *xdp) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci void *data_end = (void *)(long)xdp->data_end; 1238c2ecf20Sopenharmony_ci void *data = (void *)(long)xdp->data; 1248c2ecf20Sopenharmony_ci int pckt_size = data_end - data; 1258c2ecf20Sopenharmony_ci int offset; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (pckt_size > max(max_pcktsz, ICMP_TOOBIG_SIZE)) { 1288c2ecf20Sopenharmony_ci offset = pckt_size - ICMP_TOOBIG_SIZE; 1298c2ecf20Sopenharmony_ci if (bpf_xdp_adjust_tail(xdp, 0 - offset)) 1308c2ecf20Sopenharmony_ci return XDP_PASS; 1318c2ecf20Sopenharmony_ci return send_icmp4_too_big(xdp); 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci return XDP_PASS; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ciSEC("xdp_icmp") 1378c2ecf20Sopenharmony_ciint _xdp_icmp(struct xdp_md *xdp) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci void *data_end = (void *)(long)xdp->data_end; 1408c2ecf20Sopenharmony_ci void *data = (void *)(long)xdp->data; 1418c2ecf20Sopenharmony_ci struct ethhdr *eth = data; 1428c2ecf20Sopenharmony_ci __u16 h_proto; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (eth + 1 > data_end) 1458c2ecf20Sopenharmony_ci return XDP_DROP; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci h_proto = eth->h_proto; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (h_proto == htons(ETH_P_IP)) 1508c2ecf20Sopenharmony_ci return handle_ipv4(xdp); 1518c2ecf20Sopenharmony_ci else 1528c2ecf20Sopenharmony_ci return XDP_PASS; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cichar _license[] SEC("license") = "GPL"; 156