1 /* Copyright (c) 2017 Facebook
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.
10 #include <linux/pkt_cls.h>
11 #include <linux/bpf.h>
13 #include <linux/if_ether.h>
15 #include <linux/ipv6.h>
16 #include <linux/icmp.h>
17 #include <linux/icmpv6.h>
18 #include <linux/tcp.h>
19 #include <linux/udp.h>
20 #include "bpf_helpers.h"
21 #include "test_iptunnel_common.h"
22 #include "bpf_endian.h"
24 int _version
SEC("version") = 1;
26 static inline __u32
rol32(__u32 word
, unsigned int shift
)
28 return (word
<< shift
) | (word
>> ((-shift
) & 31));
31 /* copy paste of jhash from kernel sources to make sure llvm
32 * can compile it into valid sequence of bpf instructions
34 #define __jhash_mix(a, b, c) \
36 a -= c; a ^= rol32(c, 4); c += b; \
37 b -= a; b ^= rol32(a, 6); a += c; \
38 c -= b; c ^= rol32(b, 8); b += a; \
39 a -= c; a ^= rol32(c, 16); c += b; \
40 b -= a; b ^= rol32(a, 19); a += c; \
41 c -= b; c ^= rol32(b, 4); b += a; \
44 #define __jhash_final(a, b, c) \
46 c ^= b; c -= rol32(b, 14); \
47 a ^= c; a -= rol32(c, 11); \
48 b ^= a; b -= rol32(a, 25); \
49 c ^= b; c -= rol32(b, 16); \
50 a ^= c; a -= rol32(c, 4); \
51 b ^= a; b -= rol32(a, 14); \
52 c ^= b; c -= rol32(b, 24); \
55 #define JHASH_INITVAL 0xdeadbeef
57 typedef unsigned int u32
;
59 static inline u32
jhash(const void *key
, u32 length
, u32 initval
)
62 const unsigned char *k
= key
;
64 a
= b
= c
= JHASH_INITVAL
+ length
+ initval
;
75 case 12: c
+= (u32
)k
[11]<<24;
76 case 11: c
+= (u32
)k
[10]<<16;
77 case 10: c
+= (u32
)k
[9]<<8;
79 case 8: b
+= (u32
)k
[7]<<24;
80 case 7: b
+= (u32
)k
[6]<<16;
81 case 6: b
+= (u32
)k
[5]<<8;
83 case 4: a
+= (u32
)k
[3]<<24;
84 case 3: a
+= (u32
)k
[2]<<16;
85 case 2: a
+= (u32
)k
[1]<<8;
87 __jhash_final(a
, b
, c
);
88 case 0: /* Nothing left to add */
95 static inline u32
__jhash_nwords(u32 a
, u32 b
, u32 c
, u32 initval
)
100 __jhash_final(a
, b
, c
);
104 static inline u32
jhash_2words(u32 a
, u32 b
, u32 initval
)
106 return __jhash_nwords(a
, b
, 0, initval
+ JHASH_INITVAL
+ (2 << 2));
109 #define PCKT_FRAGMENTED 65343
110 #define IPV4_HDR_LEN_NO_OPT 20
111 #define IPV4_PLUS_ICMP_HDR 28
112 #define IPV6_PLUS_ICMP_HDR 48
116 #define CTL_MAP_SIZE 16
117 #define CH_RINGS_SIZE (MAX_VIPS * RING_SIZE)
118 #define F_IPV6 (1 << 0)
119 #define F_HASH_NO_SRC_PORT (1 << 0)
120 #define F_ICMP (1 << 0)
121 #define F_SYN_SET (1 << 1)
123 struct packet_description
{
153 struct real_definition
{
167 unsigned char eth_dest
[ETH_ALEN
];
168 unsigned char eth_source
[ETH_ALEN
];
169 unsigned short eth_proto
;
173 __uint(type
, BPF_MAP_TYPE_HASH
);
174 __uint(max_entries
, MAX_VIPS
);
175 __type(key
, struct vip
);
176 __type(value
, struct vip_meta
);
177 } vip_map
SEC(".maps");
180 __uint(type
, BPF_MAP_TYPE_ARRAY
);
181 __uint(max_entries
, CH_RINGS_SIZE
);
183 __type(value
, __u32
);
184 } ch_rings
SEC(".maps");
187 __uint(type
, BPF_MAP_TYPE_ARRAY
);
188 __uint(max_entries
, MAX_REALS
);
190 __type(value
, struct real_definition
);
191 } reals
SEC(".maps");
194 __uint(type
, BPF_MAP_TYPE_PERCPU_ARRAY
);
195 __uint(max_entries
, MAX_VIPS
);
197 __type(value
, struct vip_stats
);
198 } stats
SEC(".maps");
201 __uint(type
, BPF_MAP_TYPE_ARRAY
);
202 __uint(max_entries
, CTL_MAP_SIZE
);
204 __type(value
, struct ctl_value
);
205 } ctl_array
SEC(".maps");
207 static __always_inline __u32
get_packet_hash(struct packet_description
*pckt
,
211 return jhash_2words(jhash(pckt
->srcv6
, 16, MAX_VIPS
),
212 pckt
->ports
, CH_RINGS_SIZE
);
214 return jhash_2words(pckt
->src
, pckt
->ports
, CH_RINGS_SIZE
);
217 static __always_inline
bool get_packet_dst(struct real_definition
**real
,
218 struct packet_description
*pckt
,
219 struct vip_meta
*vip_info
,
222 __u32 hash
= get_packet_hash(pckt
, is_ipv6
) % RING_SIZE
;
223 __u32 key
= RING_SIZE
* vip_info
->vip_num
+ hash
;
226 real_pos
= bpf_map_lookup_elem(&ch_rings
, &key
);
230 *real
= bpf_map_lookup_elem(&reals
, &key
);
236 static __always_inline
int parse_icmpv6(void *data
, void *data_end
, __u64 off
,
237 struct packet_description
*pckt
)
239 struct icmp6hdr
*icmp_hdr
;
240 struct ipv6hdr
*ip6h
;
242 icmp_hdr
= data
+ off
;
243 if (icmp_hdr
+ 1 > data_end
)
245 if (icmp_hdr
->icmp6_type
!= ICMPV6_PKT_TOOBIG
)
247 off
+= sizeof(struct icmp6hdr
);
249 if (ip6h
+ 1 > data_end
)
251 pckt
->proto
= ip6h
->nexthdr
;
252 pckt
->flags
|= F_ICMP
;
253 memcpy(pckt
->srcv6
, ip6h
->daddr
.s6_addr32
, 16);
254 memcpy(pckt
->dstv6
, ip6h
->saddr
.s6_addr32
, 16);
255 return TC_ACT_UNSPEC
;
258 static __always_inline
int parse_icmp(void *data
, void *data_end
, __u64 off
,
259 struct packet_description
*pckt
)
261 struct icmphdr
*icmp_hdr
;
264 icmp_hdr
= data
+ off
;
265 if (icmp_hdr
+ 1 > data_end
)
267 if (icmp_hdr
->type
!= ICMP_DEST_UNREACH
||
268 icmp_hdr
->code
!= ICMP_FRAG_NEEDED
)
270 off
+= sizeof(struct icmphdr
);
272 if (iph
+ 1 > data_end
)
276 pckt
->proto
= iph
->protocol
;
277 pckt
->flags
|= F_ICMP
;
278 pckt
->src
= iph
->daddr
;
279 pckt
->dst
= iph
->saddr
;
280 return TC_ACT_UNSPEC
;
283 static __always_inline
bool parse_udp(void *data
, __u64 off
, void *data_end
,
284 struct packet_description
*pckt
)
289 if (udp
+ 1 > data_end
)
292 if (!(pckt
->flags
& F_ICMP
)) {
293 pckt
->port16
[0] = udp
->source
;
294 pckt
->port16
[1] = udp
->dest
;
296 pckt
->port16
[0] = udp
->dest
;
297 pckt
->port16
[1] = udp
->source
;
302 static __always_inline
bool parse_tcp(void *data
, __u64 off
, void *data_end
,
303 struct packet_description
*pckt
)
308 if (tcp
+ 1 > data_end
)
312 pckt
->flags
|= F_SYN_SET
;
314 if (!(pckt
->flags
& F_ICMP
)) {
315 pckt
->port16
[0] = tcp
->source
;
316 pckt
->port16
[1] = tcp
->dest
;
318 pckt
->port16
[0] = tcp
->dest
;
319 pckt
->port16
[1] = tcp
->source
;
324 static __always_inline
int process_packet(void *data
, __u64 off
, void *data_end
,
325 bool is_ipv6
, struct __sk_buff
*skb
)
327 void *pkt_start
= (void *)(long)skb
->data
;
328 struct packet_description pckt
= {};
329 struct eth_hdr
*eth
= pkt_start
;
330 struct bpf_tunnel_key tkey
= {};
331 struct vip_stats
*data_stats
;
332 struct real_definition
*dst
;
333 struct vip_meta
*vip_info
;
334 struct ctl_value
*cval
;
335 __u32 v4_intf_pos
= 1;
336 __u32 v6_intf_pos
= 2;
337 struct ipv6hdr
*ip6h
;
348 tkey
.tunnel_ttl
= 64;
351 if (ip6h
+ 1 > data_end
)
354 iph_len
= sizeof(struct ipv6hdr
);
355 protocol
= ip6h
->nexthdr
;
356 pckt
.proto
= protocol
;
357 pkt_bytes
= bpf_ntohs(ip6h
->payload_len
);
359 if (protocol
== IPPROTO_FRAGMENT
) {
361 } else if (protocol
== IPPROTO_ICMPV6
) {
362 action
= parse_icmpv6(data
, data_end
, off
, &pckt
);
365 off
+= IPV6_PLUS_ICMP_HDR
;
367 memcpy(pckt
.srcv6
, ip6h
->saddr
.s6_addr32
, 16);
368 memcpy(pckt
.dstv6
, ip6h
->daddr
.s6_addr32
, 16);
372 if (iph
+ 1 > data_end
)
377 protocol
= iph
->protocol
;
378 pckt
.proto
= protocol
;
379 pkt_bytes
= bpf_ntohs(iph
->tot_len
);
380 off
+= IPV4_HDR_LEN_NO_OPT
;
382 if (iph
->frag_off
& PCKT_FRAGMENTED
)
384 if (protocol
== IPPROTO_ICMP
) {
385 action
= parse_icmp(data
, data_end
, off
, &pckt
);
388 off
+= IPV4_PLUS_ICMP_HDR
;
390 pckt
.src
= iph
->saddr
;
391 pckt
.dst
= iph
->daddr
;
394 protocol
= pckt
.proto
;
396 if (protocol
== IPPROTO_TCP
) {
397 if (!parse_tcp(data
, off
, data_end
, &pckt
))
399 } else if (protocol
== IPPROTO_UDP
) {
400 if (!parse_udp(data
, off
, data_end
, &pckt
))
407 memcpy(vip
.daddr
.v6
, pckt
.dstv6
, 16);
409 vip
.daddr
.v4
= pckt
.dst
;
411 vip
.dport
= pckt
.port16
[1];
412 vip
.protocol
= pckt
.proto
;
413 vip_info
= bpf_map_lookup_elem(&vip_map
, &vip
);
416 vip_info
= bpf_map_lookup_elem(&vip_map
, &vip
);
422 if (vip_info
->flags
& F_HASH_NO_SRC_PORT
)
425 if (!get_packet_dst(&dst
, &pckt
, vip_info
, is_ipv6
))
428 if (dst
->flags
& F_IPV6
) {
429 cval
= bpf_map_lookup_elem(&ctl_array
, &v6_intf_pos
);
432 ifindex
= cval
->ifindex
;
433 memcpy(tkey
.remote_ipv6
, dst
->dstv6
, 16);
434 tun_flag
= BPF_F_TUNINFO_IPV6
;
436 cval
= bpf_map_lookup_elem(&ctl_array
, &v4_intf_pos
);
439 ifindex
= cval
->ifindex
;
440 tkey
.remote_ipv4
= dst
->dst
;
442 vip_num
= vip_info
->vip_num
;
443 data_stats
= bpf_map_lookup_elem(&stats
, &vip_num
);
447 data_stats
->bytes
+= pkt_bytes
;
448 bpf_skb_set_tunnel_key(skb
, &tkey
, sizeof(tkey
), tun_flag
);
449 *(u32
*)eth
->eth_dest
= tkey
.remote_ipv4
;
450 return bpf_redirect(ifindex
, 0);
454 int balancer_ingress(struct __sk_buff
*ctx
)
456 void *data_end
= (void *)(long)ctx
->data_end
;
457 void *data
= (void *)(long)ctx
->data
;
458 struct eth_hdr
*eth
= data
;
462 nh_off
= sizeof(struct eth_hdr
);
463 if (data
+ nh_off
> data_end
)
465 eth_proto
= eth
->eth_proto
;
466 if (eth_proto
== bpf_htons(ETH_P_IP
))
467 return process_packet(data
, nh_off
, data_end
, false, ctx
);
468 else if (eth_proto
== bpf_htons(ETH_P_IPV6
))
469 return process_packet(data
, nh_off
, data_end
, true, ctx
);
473 char _license
[] SEC("license") = "GPL";