xref: /OK3568_Linux_fs/kernel/samples/bpf/xdp_adjust_tail_kern.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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