Back to home page

OSCL-LXR

 
 

    


0001 /* SPDX-License-Identifier: GPL-2.0
0002  * Copyright (c) 2018 Facebook
0003  *
0004  * This program is free software; you can redistribute it and/or
0005  * modify it under the terms of version 2 of the GNU General Public
0006  * License as published by the Free Software Foundation.
0007  *
0008  * This program shows how to use bpf_xdp_adjust_tail() by
0009  * generating ICMPv4 "packet to big" (unreachable/ df bit set frag needed
0010  * to be more preice in case of v4)" where receiving packets bigger then
0011  * 600 bytes.
0012  */
0013 #define KBUILD_MODNAME "foo"
0014 #include <uapi/linux/bpf.h>
0015 #include <linux/in.h>
0016 #include <linux/if_ether.h>
0017 #include <linux/if_packet.h>
0018 #include <linux/if_vlan.h>
0019 #include <linux/ip.h>
0020 #include <linux/icmp.h>
0021 #include <bpf/bpf_helpers.h>
0022 
0023 #define DEFAULT_TTL 64
0024 #define MAX_PCKT_SIZE 600
0025 #define ICMP_TOOBIG_SIZE 98
0026 #define ICMP_TOOBIG_PAYLOAD_SIZE 92
0027 
0028 /* volatile to prevent compiler optimizations */
0029 static volatile __u32 max_pcktsz = MAX_PCKT_SIZE;
0030 
0031 struct {
0032     __uint(type, BPF_MAP_TYPE_ARRAY);
0033     __type(key, __u32);
0034     __type(value, __u64);
0035     __uint(max_entries, 1);
0036 } icmpcnt SEC(".maps");
0037 
0038 static __always_inline void count_icmp(void)
0039 {
0040     u64 key = 0;
0041     u64 *icmp_count;
0042 
0043     icmp_count = bpf_map_lookup_elem(&icmpcnt, &key);
0044     if (icmp_count)
0045         *icmp_count += 1;
0046 }
0047 
0048 static __always_inline void swap_mac(void *data, struct ethhdr *orig_eth)
0049 {
0050     struct ethhdr *eth;
0051 
0052     eth = data;
0053     memcpy(eth->h_source, orig_eth->h_dest, ETH_ALEN);
0054     memcpy(eth->h_dest, orig_eth->h_source, ETH_ALEN);
0055     eth->h_proto = orig_eth->h_proto;
0056 }
0057 
0058 static __always_inline __u16 csum_fold_helper(__u32 csum)
0059 {
0060     return ~((csum & 0xffff) + (csum >> 16));
0061 }
0062 
0063 static __always_inline void ipv4_csum(void *data_start, int data_size,
0064                       __u32 *csum)
0065 {
0066     *csum = bpf_csum_diff(0, 0, data_start, data_size, *csum);
0067     *csum = csum_fold_helper(*csum);
0068 }
0069 
0070 static __always_inline int send_icmp4_too_big(struct xdp_md *xdp)
0071 {
0072     int headroom = (int)sizeof(struct iphdr) + (int)sizeof(struct icmphdr);
0073 
0074     if (bpf_xdp_adjust_head(xdp, 0 - headroom))
0075         return XDP_DROP;
0076     void *data = (void *)(long)xdp->data;
0077     void *data_end = (void *)(long)xdp->data_end;
0078 
0079     if (data + (ICMP_TOOBIG_SIZE + headroom) > data_end)
0080         return XDP_DROP;
0081 
0082     struct iphdr *iph, *orig_iph;
0083     struct icmphdr *icmp_hdr;
0084     struct ethhdr *orig_eth;
0085     __u32 csum = 0;
0086     __u64 off = 0;
0087 
0088     orig_eth = data + headroom;
0089     swap_mac(data, orig_eth);
0090     off += sizeof(struct ethhdr);
0091     iph = data + off;
0092     off += sizeof(struct iphdr);
0093     icmp_hdr = data + off;
0094     off += sizeof(struct icmphdr);
0095     orig_iph = data + off;
0096     icmp_hdr->type = ICMP_DEST_UNREACH;
0097     icmp_hdr->code = ICMP_FRAG_NEEDED;
0098     icmp_hdr->un.frag.mtu = htons(max_pcktsz - sizeof(struct ethhdr));
0099     icmp_hdr->checksum = 0;
0100     ipv4_csum(icmp_hdr, ICMP_TOOBIG_PAYLOAD_SIZE, &csum);
0101     icmp_hdr->checksum = csum;
0102     iph->ttl = DEFAULT_TTL;
0103     iph->daddr = orig_iph->saddr;
0104     iph->saddr = orig_iph->daddr;
0105     iph->version = 4;
0106     iph->ihl = 5;
0107     iph->protocol = IPPROTO_ICMP;
0108     iph->tos = 0;
0109     iph->tot_len = htons(
0110         ICMP_TOOBIG_SIZE + headroom - sizeof(struct ethhdr));
0111     iph->check = 0;
0112     csum = 0;
0113     ipv4_csum(iph, sizeof(struct iphdr), &csum);
0114     iph->check = csum;
0115     count_icmp();
0116     return XDP_TX;
0117 }
0118 
0119 
0120 static __always_inline int handle_ipv4(struct xdp_md *xdp)
0121 {
0122     void *data_end = (void *)(long)xdp->data_end;
0123     void *data = (void *)(long)xdp->data;
0124     int pckt_size = data_end - data;
0125     int offset;
0126 
0127     if (pckt_size > max(max_pcktsz, ICMP_TOOBIG_SIZE)) {
0128         offset = pckt_size - ICMP_TOOBIG_SIZE;
0129         if (bpf_xdp_adjust_tail(xdp, 0 - offset))
0130             return XDP_PASS;
0131         return send_icmp4_too_big(xdp);
0132     }
0133     return XDP_PASS;
0134 }
0135 
0136 SEC("xdp_icmp")
0137 int _xdp_icmp(struct xdp_md *xdp)
0138 {
0139     void *data_end = (void *)(long)xdp->data_end;
0140     void *data = (void *)(long)xdp->data;
0141     struct ethhdr *eth = data;
0142     __u16 h_proto;
0143 
0144     if (eth + 1 > data_end)
0145         return XDP_DROP;
0146 
0147     h_proto = eth->h_proto;
0148 
0149     if (h_proto == htons(ETH_P_IP))
0150         return handle_ipv4(xdp);
0151     else
0152         return XDP_PASS;
0153 }
0154 
0155 char _license[] SEC("license") = "GPL";