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