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 | |