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