1*c6ffd1ffSNikita V. Shirokov /* SPDX-License-Identifier: GPL-2.0 2*c6ffd1ffSNikita V. Shirokov * Copyright (c) 2018 Facebook 3*c6ffd1ffSNikita V. Shirokov * 4*c6ffd1ffSNikita V. Shirokov * This program is free software; you can redistribute it and/or 5*c6ffd1ffSNikita V. Shirokov * modify it under the terms of version 2 of the GNU General Public 6*c6ffd1ffSNikita V. Shirokov * License as published by the Free Software Foundation. 7*c6ffd1ffSNikita V. Shirokov * 8*c6ffd1ffSNikita V. Shirokov * This program shows how to use bpf_xdp_adjust_tail() by 9*c6ffd1ffSNikita V. Shirokov * generating ICMPv4 "packet to big" (unreachable/ df bit set frag needed 10*c6ffd1ffSNikita V. Shirokov * to be more preice in case of v4)" where receiving packets bigger then 11*c6ffd1ffSNikita V. Shirokov * 600 bytes. 12*c6ffd1ffSNikita V. Shirokov */ 13*c6ffd1ffSNikita V. Shirokov #define KBUILD_MODNAME "foo" 14*c6ffd1ffSNikita V. Shirokov #include <uapi/linux/bpf.h> 15*c6ffd1ffSNikita V. Shirokov #include <linux/in.h> 16*c6ffd1ffSNikita V. Shirokov #include <linux/if_ether.h> 17*c6ffd1ffSNikita V. Shirokov #include <linux/if_packet.h> 18*c6ffd1ffSNikita V. Shirokov #include <linux/if_vlan.h> 19*c6ffd1ffSNikita V. Shirokov #include <linux/ip.h> 20*c6ffd1ffSNikita V. Shirokov #include <linux/icmp.h> 21*c6ffd1ffSNikita V. Shirokov #include "bpf_helpers.h" 22*c6ffd1ffSNikita V. Shirokov 23*c6ffd1ffSNikita V. Shirokov #define DEFAULT_TTL 64 24*c6ffd1ffSNikita V. Shirokov #define MAX_PCKT_SIZE 600 25*c6ffd1ffSNikita V. Shirokov #define ICMP_TOOBIG_SIZE 98 26*c6ffd1ffSNikita V. Shirokov #define ICMP_TOOBIG_PAYLOAD_SIZE 92 27*c6ffd1ffSNikita V. Shirokov 28*c6ffd1ffSNikita V. Shirokov struct bpf_map_def SEC("maps") icmpcnt = { 29*c6ffd1ffSNikita V. Shirokov .type = BPF_MAP_TYPE_ARRAY, 30*c6ffd1ffSNikita V. Shirokov .key_size = sizeof(__u32), 31*c6ffd1ffSNikita V. Shirokov .value_size = sizeof(__u64), 32*c6ffd1ffSNikita V. Shirokov .max_entries = 1, 33*c6ffd1ffSNikita V. Shirokov }; 34*c6ffd1ffSNikita V. Shirokov 35*c6ffd1ffSNikita V. Shirokov static __always_inline void count_icmp(void) 36*c6ffd1ffSNikita V. Shirokov { 37*c6ffd1ffSNikita V. Shirokov u64 key = 0; 38*c6ffd1ffSNikita V. Shirokov u64 *icmp_count; 39*c6ffd1ffSNikita V. Shirokov 40*c6ffd1ffSNikita V. Shirokov icmp_count = bpf_map_lookup_elem(&icmpcnt, &key); 41*c6ffd1ffSNikita V. Shirokov if (icmp_count) 42*c6ffd1ffSNikita V. Shirokov *icmp_count += 1; 43*c6ffd1ffSNikita V. Shirokov } 44*c6ffd1ffSNikita V. Shirokov 45*c6ffd1ffSNikita V. Shirokov static __always_inline void swap_mac(void *data, struct ethhdr *orig_eth) 46*c6ffd1ffSNikita V. Shirokov { 47*c6ffd1ffSNikita V. Shirokov struct ethhdr *eth; 48*c6ffd1ffSNikita V. Shirokov 49*c6ffd1ffSNikita V. Shirokov eth = data; 50*c6ffd1ffSNikita V. Shirokov memcpy(eth->h_source, orig_eth->h_dest, ETH_ALEN); 51*c6ffd1ffSNikita V. Shirokov memcpy(eth->h_dest, orig_eth->h_source, ETH_ALEN); 52*c6ffd1ffSNikita V. Shirokov eth->h_proto = orig_eth->h_proto; 53*c6ffd1ffSNikita V. Shirokov } 54*c6ffd1ffSNikita V. Shirokov 55*c6ffd1ffSNikita V. Shirokov static __always_inline __u16 csum_fold_helper(__u32 csum) 56*c6ffd1ffSNikita V. Shirokov { 57*c6ffd1ffSNikita V. Shirokov return ~((csum & 0xffff) + (csum >> 16)); 58*c6ffd1ffSNikita V. Shirokov } 59*c6ffd1ffSNikita V. Shirokov 60*c6ffd1ffSNikita V. Shirokov static __always_inline void ipv4_csum(void *data_start, int data_size, 61*c6ffd1ffSNikita V. Shirokov __u32 *csum) 62*c6ffd1ffSNikita V. Shirokov { 63*c6ffd1ffSNikita V. Shirokov *csum = bpf_csum_diff(0, 0, data_start, data_size, *csum); 64*c6ffd1ffSNikita V. Shirokov *csum = csum_fold_helper(*csum); 65*c6ffd1ffSNikita V. Shirokov } 66*c6ffd1ffSNikita V. Shirokov 67*c6ffd1ffSNikita V. Shirokov static __always_inline int send_icmp4_too_big(struct xdp_md *xdp) 68*c6ffd1ffSNikita V. Shirokov { 69*c6ffd1ffSNikita V. Shirokov int headroom = (int)sizeof(struct iphdr) + (int)sizeof(struct icmphdr); 70*c6ffd1ffSNikita V. Shirokov 71*c6ffd1ffSNikita V. Shirokov if (bpf_xdp_adjust_head(xdp, 0 - headroom)) 72*c6ffd1ffSNikita V. Shirokov return XDP_DROP; 73*c6ffd1ffSNikita V. Shirokov void *data = (void *)(long)xdp->data; 74*c6ffd1ffSNikita V. Shirokov void *data_end = (void *)(long)xdp->data_end; 75*c6ffd1ffSNikita V. Shirokov 76*c6ffd1ffSNikita V. Shirokov if (data + (ICMP_TOOBIG_SIZE + headroom) > data_end) 77*c6ffd1ffSNikita V. Shirokov return XDP_DROP; 78*c6ffd1ffSNikita V. Shirokov 79*c6ffd1ffSNikita V. Shirokov struct iphdr *iph, *orig_iph; 80*c6ffd1ffSNikita V. Shirokov struct icmphdr *icmp_hdr; 81*c6ffd1ffSNikita V. Shirokov struct ethhdr *orig_eth; 82*c6ffd1ffSNikita V. Shirokov __u32 csum = 0; 83*c6ffd1ffSNikita V. Shirokov __u64 off = 0; 84*c6ffd1ffSNikita V. Shirokov 85*c6ffd1ffSNikita V. Shirokov orig_eth = data + headroom; 86*c6ffd1ffSNikita V. Shirokov swap_mac(data, orig_eth); 87*c6ffd1ffSNikita V. Shirokov off += sizeof(struct ethhdr); 88*c6ffd1ffSNikita V. Shirokov iph = data + off; 89*c6ffd1ffSNikita V. Shirokov off += sizeof(struct iphdr); 90*c6ffd1ffSNikita V. Shirokov icmp_hdr = data + off; 91*c6ffd1ffSNikita V. Shirokov off += sizeof(struct icmphdr); 92*c6ffd1ffSNikita V. Shirokov orig_iph = data + off; 93*c6ffd1ffSNikita V. Shirokov icmp_hdr->type = ICMP_DEST_UNREACH; 94*c6ffd1ffSNikita V. Shirokov icmp_hdr->code = ICMP_FRAG_NEEDED; 95*c6ffd1ffSNikita V. Shirokov icmp_hdr->un.frag.mtu = htons(MAX_PCKT_SIZE-sizeof(struct ethhdr)); 96*c6ffd1ffSNikita V. Shirokov icmp_hdr->checksum = 0; 97*c6ffd1ffSNikita V. Shirokov ipv4_csum(icmp_hdr, ICMP_TOOBIG_PAYLOAD_SIZE, &csum); 98*c6ffd1ffSNikita V. Shirokov icmp_hdr->checksum = csum; 99*c6ffd1ffSNikita V. Shirokov iph->ttl = DEFAULT_TTL; 100*c6ffd1ffSNikita V. Shirokov iph->daddr = orig_iph->saddr; 101*c6ffd1ffSNikita V. Shirokov iph->saddr = orig_iph->daddr; 102*c6ffd1ffSNikita V. Shirokov iph->version = 4; 103*c6ffd1ffSNikita V. Shirokov iph->ihl = 5; 104*c6ffd1ffSNikita V. Shirokov iph->protocol = IPPROTO_ICMP; 105*c6ffd1ffSNikita V. Shirokov iph->tos = 0; 106*c6ffd1ffSNikita V. Shirokov iph->tot_len = htons( 107*c6ffd1ffSNikita V. Shirokov ICMP_TOOBIG_SIZE + headroom - sizeof(struct ethhdr)); 108*c6ffd1ffSNikita V. Shirokov iph->check = 0; 109*c6ffd1ffSNikita V. Shirokov csum = 0; 110*c6ffd1ffSNikita V. Shirokov ipv4_csum(iph, sizeof(struct iphdr), &csum); 111*c6ffd1ffSNikita V. Shirokov iph->check = csum; 112*c6ffd1ffSNikita V. Shirokov count_icmp(); 113*c6ffd1ffSNikita V. Shirokov return XDP_TX; 114*c6ffd1ffSNikita V. Shirokov } 115*c6ffd1ffSNikita V. Shirokov 116*c6ffd1ffSNikita V. Shirokov 117*c6ffd1ffSNikita V. Shirokov static __always_inline int handle_ipv4(struct xdp_md *xdp) 118*c6ffd1ffSNikita V. Shirokov { 119*c6ffd1ffSNikita V. Shirokov void *data_end = (void *)(long)xdp->data_end; 120*c6ffd1ffSNikita V. Shirokov void *data = (void *)(long)xdp->data; 121*c6ffd1ffSNikita V. Shirokov int pckt_size = data_end - data; 122*c6ffd1ffSNikita V. Shirokov int offset; 123*c6ffd1ffSNikita V. Shirokov 124*c6ffd1ffSNikita V. Shirokov if (pckt_size > MAX_PCKT_SIZE) { 125*c6ffd1ffSNikita V. Shirokov offset = pckt_size - ICMP_TOOBIG_SIZE; 126*c6ffd1ffSNikita V. Shirokov if (bpf_xdp_adjust_tail(xdp, 0 - offset)) 127*c6ffd1ffSNikita V. Shirokov return XDP_PASS; 128*c6ffd1ffSNikita V. Shirokov return send_icmp4_too_big(xdp); 129*c6ffd1ffSNikita V. Shirokov } 130*c6ffd1ffSNikita V. Shirokov return XDP_PASS; 131*c6ffd1ffSNikita V. Shirokov } 132*c6ffd1ffSNikita V. Shirokov 133*c6ffd1ffSNikita V. Shirokov SEC("xdp_icmp") 134*c6ffd1ffSNikita V. Shirokov int _xdp_icmp(struct xdp_md *xdp) 135*c6ffd1ffSNikita V. Shirokov { 136*c6ffd1ffSNikita V. Shirokov void *data_end = (void *)(long)xdp->data_end; 137*c6ffd1ffSNikita V. Shirokov void *data = (void *)(long)xdp->data; 138*c6ffd1ffSNikita V. Shirokov struct ethhdr *eth = data; 139*c6ffd1ffSNikita V. Shirokov __u16 h_proto; 140*c6ffd1ffSNikita V. Shirokov 141*c6ffd1ffSNikita V. Shirokov if (eth + 1 > data_end) 142*c6ffd1ffSNikita V. Shirokov return XDP_DROP; 143*c6ffd1ffSNikita V. Shirokov 144*c6ffd1ffSNikita V. Shirokov h_proto = eth->h_proto; 145*c6ffd1ffSNikita V. Shirokov 146*c6ffd1ffSNikita V. Shirokov if (h_proto == htons(ETH_P_IP)) 147*c6ffd1ffSNikita V. Shirokov return handle_ipv4(xdp); 148*c6ffd1ffSNikita V. Shirokov else 149*c6ffd1ffSNikita V. Shirokov return XDP_PASS; 150*c6ffd1ffSNikita V. Shirokov } 151*c6ffd1ffSNikita V. Shirokov 152*c6ffd1ffSNikita V. Shirokov char _license[] SEC("license") = "GPL"; 153