xref: /linux/samples/bpf/tc_l2_redirect_kern.c (revision f86fd32d)
1 /* Copyright (c) 2016 Facebook
2  *
3  * This program is free software; you can redistribute it and/or
4  * modify it under the terms of version 2 of the GNU General Public
5  * License as published by the Free Software Foundation.
6  */
7 #define KBUILD_MODNAME "foo"
8 #include <uapi/linux/bpf.h>
9 #include <uapi/linux/if_ether.h>
10 #include <uapi/linux/if_packet.h>
11 #include <uapi/linux/ip.h>
12 #include <uapi/linux/ipv6.h>
13 #include <uapi/linux/in.h>
14 #include <uapi/linux/tcp.h>
15 #include <uapi/linux/filter.h>
16 #include <uapi/linux/pkt_cls.h>
17 #include <net/ipv6.h>
18 #include <bpf/bpf_helpers.h>
19 
20 #define _htonl __builtin_bswap32
21 
22 #define PIN_GLOBAL_NS		2
23 struct bpf_elf_map {
24 	__u32 type;
25 	__u32 size_key;
26 	__u32 size_value;
27 	__u32 max_elem;
28 	__u32 flags;
29 	__u32 id;
30 	__u32 pinning;
31 };
32 
33 /* copy of 'struct ethhdr' without __packed */
34 struct eth_hdr {
35 	unsigned char   h_dest[ETH_ALEN];
36 	unsigned char   h_source[ETH_ALEN];
37 	unsigned short  h_proto;
38 };
39 
40 struct bpf_elf_map SEC("maps") tun_iface = {
41 	.type = BPF_MAP_TYPE_ARRAY,
42 	.size_key = sizeof(int),
43 	.size_value = sizeof(int),
44 	.pinning = PIN_GLOBAL_NS,
45 	.max_elem = 1,
46 };
47 
48 static __always_inline bool is_vip_addr(__be16 eth_proto, __be32 daddr)
49 {
50 	if (eth_proto == htons(ETH_P_IP))
51 		return (_htonl(0xffffff00) & daddr) == _htonl(0x0a0a0100);
52 	else if (eth_proto == htons(ETH_P_IPV6))
53 		return (daddr == _htonl(0x2401face));
54 
55 	return false;
56 }
57 
58 SEC("l2_to_iptun_ingress_forward")
59 int _l2_to_iptun_ingress_forward(struct __sk_buff *skb)
60 {
61 	struct bpf_tunnel_key tkey = {};
62 	void *data = (void *)(long)skb->data;
63 	struct eth_hdr *eth = data;
64 	void *data_end = (void *)(long)skb->data_end;
65 	int key = 0, *ifindex;
66 
67 	int ret;
68 
69 	if (data + sizeof(*eth) > data_end)
70 		return TC_ACT_OK;
71 
72 	ifindex = bpf_map_lookup_elem(&tun_iface, &key);
73 	if (!ifindex)
74 		return TC_ACT_OK;
75 
76 	if (eth->h_proto == htons(ETH_P_IP)) {
77 		char fmt4[] = "ingress forward to ifindex:%d daddr4:%x\n";
78 		struct iphdr *iph = data + sizeof(*eth);
79 
80 		if (data + sizeof(*eth) + sizeof(*iph) > data_end)
81 			return TC_ACT_OK;
82 
83 		if (iph->protocol != IPPROTO_IPIP)
84 			return TC_ACT_OK;
85 
86 		bpf_trace_printk(fmt4, sizeof(fmt4), *ifindex,
87 				 _htonl(iph->daddr));
88 		return bpf_redirect(*ifindex, BPF_F_INGRESS);
89 	} else if (eth->h_proto == htons(ETH_P_IPV6)) {
90 		char fmt6[] = "ingress forward to ifindex:%d daddr6:%x::%x\n";
91 		struct ipv6hdr *ip6h = data + sizeof(*eth);
92 
93 		if (data + sizeof(*eth) + sizeof(*ip6h) > data_end)
94 			return TC_ACT_OK;
95 
96 		if (ip6h->nexthdr != IPPROTO_IPIP &&
97 		    ip6h->nexthdr != IPPROTO_IPV6)
98 			return TC_ACT_OK;
99 
100 		bpf_trace_printk(fmt6, sizeof(fmt6), *ifindex,
101 				 _htonl(ip6h->daddr.s6_addr32[0]),
102 				 _htonl(ip6h->daddr.s6_addr32[3]));
103 		return bpf_redirect(*ifindex, BPF_F_INGRESS);
104 	}
105 
106 	return TC_ACT_OK;
107 }
108 
109 SEC("l2_to_iptun_ingress_redirect")
110 int _l2_to_iptun_ingress_redirect(struct __sk_buff *skb)
111 {
112 	struct bpf_tunnel_key tkey = {};
113 	void *data = (void *)(long)skb->data;
114 	struct eth_hdr *eth = data;
115 	void *data_end = (void *)(long)skb->data_end;
116 	int key = 0, *ifindex;
117 
118 	int ret;
119 
120 	if (data + sizeof(*eth) > data_end)
121 		return TC_ACT_OK;
122 
123 	ifindex = bpf_map_lookup_elem(&tun_iface, &key);
124 	if (!ifindex)
125 		return TC_ACT_OK;
126 
127 	if (eth->h_proto == htons(ETH_P_IP)) {
128 		char fmt4[] = "e/ingress redirect daddr4:%x to ifindex:%d\n";
129 		struct iphdr *iph = data + sizeof(*eth);
130 		__be32 daddr = iph->daddr;
131 
132 		if (data + sizeof(*eth) + sizeof(*iph) > data_end)
133 			return TC_ACT_OK;
134 
135 		if (!is_vip_addr(eth->h_proto, daddr))
136 			return TC_ACT_OK;
137 
138 		bpf_trace_printk(fmt4, sizeof(fmt4), _htonl(daddr), *ifindex);
139 	} else {
140 		return TC_ACT_OK;
141 	}
142 
143 	tkey.tunnel_id = 10000;
144 	tkey.tunnel_ttl = 64;
145 	tkey.remote_ipv4 = 0x0a020166; /* 10.2.1.102 */
146 	bpf_skb_set_tunnel_key(skb, &tkey, sizeof(tkey), 0);
147 	return bpf_redirect(*ifindex, 0);
148 }
149 
150 SEC("l2_to_ip6tun_ingress_redirect")
151 int _l2_to_ip6tun_ingress_redirect(struct __sk_buff *skb)
152 {
153 	struct bpf_tunnel_key tkey = {};
154 	void *data = (void *)(long)skb->data;
155 	struct eth_hdr *eth = data;
156 	void *data_end = (void *)(long)skb->data_end;
157 	int key = 0, *ifindex;
158 
159 	if (data + sizeof(*eth) > data_end)
160 		return TC_ACT_OK;
161 
162 	ifindex = bpf_map_lookup_elem(&tun_iface, &key);
163 	if (!ifindex)
164 		return TC_ACT_OK;
165 
166 	if (eth->h_proto == htons(ETH_P_IP)) {
167 		char fmt4[] = "e/ingress redirect daddr4:%x to ifindex:%d\n";
168 		struct iphdr *iph = data + sizeof(*eth);
169 
170 		if (data + sizeof(*eth) + sizeof(*iph) > data_end)
171 			return TC_ACT_OK;
172 
173 		if (!is_vip_addr(eth->h_proto, iph->daddr))
174 			return TC_ACT_OK;
175 
176 		bpf_trace_printk(fmt4, sizeof(fmt4), _htonl(iph->daddr),
177 				 *ifindex);
178 	} else if (eth->h_proto == htons(ETH_P_IPV6)) {
179 		char fmt6[] = "e/ingress redirect daddr6:%x to ifindex:%d\n";
180 		struct ipv6hdr *ip6h = data + sizeof(*eth);
181 
182 		if (data + sizeof(*eth) + sizeof(*ip6h) > data_end)
183 			return TC_ACT_OK;
184 
185 		if (!is_vip_addr(eth->h_proto, ip6h->daddr.s6_addr32[0]))
186 			return TC_ACT_OK;
187 
188 		bpf_trace_printk(fmt6, sizeof(fmt6),
189 				 _htonl(ip6h->daddr.s6_addr32[0]), *ifindex);
190 	} else {
191 		return TC_ACT_OK;
192 	}
193 
194 	tkey.tunnel_id = 10000;
195 	tkey.tunnel_ttl = 64;
196 	/* 2401:db02:0:0:0:0:0:66 */
197 	tkey.remote_ipv6[0] = _htonl(0x2401db02);
198 	tkey.remote_ipv6[1] = 0;
199 	tkey.remote_ipv6[2] = 0;
200 	tkey.remote_ipv6[3] = _htonl(0x00000066);
201 	bpf_skb_set_tunnel_key(skb, &tkey, sizeof(tkey), BPF_F_TUNINFO_IPV6);
202 	return bpf_redirect(*ifindex, 0);
203 }
204 
205 SEC("drop_non_tun_vip")
206 int _drop_non_tun_vip(struct __sk_buff *skb)
207 {
208 	struct bpf_tunnel_key tkey = {};
209 	void *data = (void *)(long)skb->data;
210 	struct eth_hdr *eth = data;
211 	void *data_end = (void *)(long)skb->data_end;
212 
213 	if (data + sizeof(*eth) > data_end)
214 		return TC_ACT_OK;
215 
216 	if (eth->h_proto == htons(ETH_P_IP)) {
217 		struct iphdr *iph = data + sizeof(*eth);
218 
219 		if (data + sizeof(*eth) + sizeof(*iph) > data_end)
220 			return TC_ACT_OK;
221 
222 		if (is_vip_addr(eth->h_proto, iph->daddr))
223 			return TC_ACT_SHOT;
224 	} else if (eth->h_proto == htons(ETH_P_IPV6)) {
225 		struct ipv6hdr *ip6h = data + sizeof(*eth);
226 
227 		if (data + sizeof(*eth) + sizeof(*ip6h) > data_end)
228 			return TC_ACT_OK;
229 
230 		if (is_vip_addr(eth->h_proto, ip6h->daddr.s6_addr32[0]))
231 			return TC_ACT_SHOT;
232 	}
233 
234 	return TC_ACT_OK;
235 }
236 
237 char _license[] SEC("license") = "GPL";
238