1 /* Copyright (c) 2016,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/if_ether.h>
11 #include <linux/if_packet.h>
13 #include <linux/ipv6.h>
15 #include <linux/udp.h>
16 #include <linux/tcp.h>
17 #include <linux/pkt_cls.h>
18 #include <sys/socket.h>
19 #include "bpf_helpers.h"
20 #include "bpf_endian.h"
21 #include "test_iptunnel_common.h"
23 int _version
SEC("version") = 1;
26 __uint(type
, BPF_MAP_TYPE_PERCPU_ARRAY
);
27 __uint(max_entries
, 256);
33 __uint(type
, BPF_MAP_TYPE_HASH
);
34 __uint(max_entries
, MAX_IPTNL_ENTRIES
);
35 __type(key
, struct vip
);
36 __type(value
, struct iptnl_info
);
37 } vip2tnl
SEC(".maps");
39 static __always_inline
void count_tx(__u32 protocol
)
43 rxcnt_count
= bpf_map_lookup_elem(&rxcnt
, &protocol
);
48 static __always_inline
int get_dport(void *trans_data
, void *data_end
,
56 th
= (struct tcphdr
*)trans_data
;
57 if (th
+ 1 > data_end
)
61 uh
= (struct udphdr
*)trans_data
;
62 if (uh
+ 1 > data_end
)
70 static __always_inline
void set_ethhdr(struct ethhdr
*new_eth
,
71 const struct ethhdr
*old_eth
,
72 const struct iptnl_info
*tnl
,
75 memcpy(new_eth
->h_source
, old_eth
->h_dest
, sizeof(new_eth
->h_source
));
76 memcpy(new_eth
->h_dest
, tnl
->dmac
, sizeof(new_eth
->h_dest
));
77 new_eth
->h_proto
= h_proto
;
80 static __always_inline
int handle_ipv4(struct xdp_md
*xdp
)
82 void *data_end
= (void *)(long)xdp
->data_end
;
83 void *data
= (void *)(long)xdp
->data
;
84 struct iptnl_info
*tnl
;
85 struct ethhdr
*new_eth
;
86 struct ethhdr
*old_eth
;
87 struct iphdr
*iph
= data
+ sizeof(struct ethhdr
);
95 if (iph
+ 1 > data_end
)
98 dport
= get_dport(iph
+ 1, data_end
, iph
->protocol
);
102 vip
.protocol
= iph
->protocol
;
103 vip
.family
= AF_INET
;
104 vip
.daddr
.v4
= iph
->daddr
;
106 payload_len
= bpf_ntohs(iph
->tot_len
);
108 tnl
= bpf_map_lookup_elem(&vip2tnl
, &vip
);
109 /* It only does v4-in-v4 */
110 if (!tnl
|| tnl
->family
!= AF_INET
)
113 if (bpf_xdp_adjust_head(xdp
, 0 - (int)sizeof(struct iphdr
)))
116 data
= (void *)(long)xdp
->data
;
117 data_end
= (void *)(long)xdp
->data_end
;
120 iph
= data
+ sizeof(*new_eth
);
121 old_eth
= data
+ sizeof(*iph
);
123 if (new_eth
+ 1 > data_end
||
124 old_eth
+ 1 > data_end
||
128 set_ethhdr(new_eth
, old_eth
, tnl
, bpf_htons(ETH_P_IP
));
131 iph
->ihl
= sizeof(*iph
) >> 2;
133 iph
->protocol
= IPPROTO_IPIP
;
136 iph
->tot_len
= bpf_htons(payload_len
+ sizeof(*iph
));
137 iph
->daddr
= tnl
->daddr
.v4
;
138 iph
->saddr
= tnl
->saddr
.v4
;
141 next_iph
= (__u16
*)iph
;
142 #pragma clang loop unroll(full)
143 for (i
= 0; i
< sizeof(*iph
) >> 1; i
++)
146 iph
->check
= ~((csum
& 0xffff) + (csum
>> 16));
148 count_tx(vip
.protocol
);
153 static __always_inline
int handle_ipv6(struct xdp_md
*xdp
)
155 void *data_end
= (void *)(long)xdp
->data_end
;
156 void *data
= (void *)(long)xdp
->data
;
157 struct iptnl_info
*tnl
;
158 struct ethhdr
*new_eth
;
159 struct ethhdr
*old_eth
;
160 struct ipv6hdr
*ip6h
= data
+ sizeof(struct ethhdr
);
165 if (ip6h
+ 1 > data_end
)
168 dport
= get_dport(ip6h
+ 1, data_end
, ip6h
->nexthdr
);
172 vip
.protocol
= ip6h
->nexthdr
;
173 vip
.family
= AF_INET6
;
174 memcpy(vip
.daddr
.v6
, ip6h
->daddr
.s6_addr32
, sizeof(vip
.daddr
));
176 payload_len
= ip6h
->payload_len
;
178 tnl
= bpf_map_lookup_elem(&vip2tnl
, &vip
);
179 /* It only does v6-in-v6 */
180 if (!tnl
|| tnl
->family
!= AF_INET6
)
183 if (bpf_xdp_adjust_head(xdp
, 0 - (int)sizeof(struct ipv6hdr
)))
186 data
= (void *)(long)xdp
->data
;
187 data_end
= (void *)(long)xdp
->data_end
;
190 ip6h
= data
+ sizeof(*new_eth
);
191 old_eth
= data
+ sizeof(*ip6h
);
193 if (new_eth
+ 1 > data_end
|| old_eth
+ 1 > data_end
||
197 set_ethhdr(new_eth
, old_eth
, tnl
, bpf_htons(ETH_P_IPV6
));
201 memset(ip6h
->flow_lbl
, 0, sizeof(ip6h
->flow_lbl
));
202 ip6h
->payload_len
= bpf_htons(bpf_ntohs(payload_len
) + sizeof(*ip6h
));
203 ip6h
->nexthdr
= IPPROTO_IPV6
;
205 memcpy(ip6h
->saddr
.s6_addr32
, tnl
->saddr
.v6
, sizeof(tnl
->saddr
.v6
));
206 memcpy(ip6h
->daddr
.s6_addr32
, tnl
->daddr
.v6
, sizeof(tnl
->daddr
.v6
));
208 count_tx(vip
.protocol
);
213 SEC("xdp_tx_iptunnel")
214 int _xdp_tx_iptunnel(struct xdp_md
*xdp
)
216 void *data_end
= (void *)(long)xdp
->data_end
;
217 void *data
= (void *)(long)xdp
->data
;
218 struct ethhdr
*eth
= data
;
221 if (eth
+ 1 > data_end
)
224 h_proto
= eth
->h_proto
;
226 if (h_proto
== bpf_htons(ETH_P_IP
))
227 return handle_ipv4(xdp
);
228 else if (h_proto
== bpf_htons(ETH_P_IPV6
))
230 return handle_ipv6(xdp
);
235 char _license
[] SEC("license") = "GPL";