1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2019 Facebook
6 #include <linux/if_ether.h>
7 #include <linux/if_packet.h>
9 #include <linux/ipv6.h>
11 #include <linux/udp.h>
12 #include <linux/tcp.h>
13 #include <linux/pkt_cls.h>
14 #include <sys/socket.h>
15 #include <bpf/bpf_helpers.h>
16 #include <bpf/bpf_endian.h>
17 #include "test_iptunnel_common.h"
19 int _version
SEC("version") = 1;
22 __uint(type
, BPF_MAP_TYPE_PERCPU_ARRAY
);
23 __uint(max_entries
, 256);
29 __uint(type
, BPF_MAP_TYPE_HASH
);
30 __uint(max_entries
, MAX_IPTNL_ENTRIES
);
31 __type(key
, struct vip
);
32 __type(value
, struct iptnl_info
);
33 } vip2tnl
SEC(".maps");
35 static __always_inline
void count_tx(__u32 protocol
)
39 rxcnt_count
= bpf_map_lookup_elem(&rxcnt
, &protocol
);
44 static __always_inline
int get_dport(void *trans_data
, void *data_end
,
52 th
= (struct tcphdr
*)trans_data
;
53 if (th
+ 1 > data_end
)
57 uh
= (struct udphdr
*)trans_data
;
58 if (uh
+ 1 > data_end
)
66 static __always_inline
void set_ethhdr(struct ethhdr
*new_eth
,
67 const struct ethhdr
*old_eth
,
68 const struct iptnl_info
*tnl
,
71 memcpy(new_eth
->h_source
, old_eth
->h_dest
, sizeof(new_eth
->h_source
));
72 memcpy(new_eth
->h_dest
, tnl
->dmac
, sizeof(new_eth
->h_dest
));
73 new_eth
->h_proto
= h_proto
;
76 static __always_inline
int handle_ipv4(struct xdp_md
*xdp
)
78 void *data_end
= (void *)(long)xdp
->data_end
;
79 void *data
= (void *)(long)xdp
->data
;
80 struct iptnl_info
*tnl
;
81 struct ethhdr
*new_eth
;
82 struct ethhdr
*old_eth
;
83 struct iphdr
*iph
= data
+ sizeof(struct ethhdr
);
91 if (iph
+ 1 > data_end
)
94 dport
= get_dport(iph
+ 1, data_end
, iph
->protocol
);
98 vip
.protocol
= iph
->protocol
;
100 vip
.daddr
.v4
= iph
->daddr
;
102 payload_len
= bpf_ntohs(iph
->tot_len
);
104 tnl
= bpf_map_lookup_elem(&vip2tnl
, &vip
);
105 /* It only does v4-in-v4 */
106 if (!tnl
|| tnl
->family
!= AF_INET
)
109 if (bpf_xdp_adjust_head(xdp
, 0 - (int)sizeof(struct iphdr
)))
112 data
= (void *)(long)xdp
->data
;
113 data_end
= (void *)(long)xdp
->data_end
;
116 iph
= data
+ sizeof(*new_eth
);
117 old_eth
= data
+ sizeof(*iph
);
119 if (new_eth
+ 1 > data_end
||
120 old_eth
+ 1 > data_end
||
124 set_ethhdr(new_eth
, old_eth
, tnl
, bpf_htons(ETH_P_IP
));
127 iph
->ihl
= sizeof(*iph
) >> 2;
129 iph
->protocol
= IPPROTO_IPIP
;
132 iph
->tot_len
= bpf_htons(payload_len
+ sizeof(*iph
));
133 iph
->daddr
= tnl
->daddr
.v4
;
134 iph
->saddr
= tnl
->saddr
.v4
;
137 next_iph
= (__u16
*)iph
;
138 #pragma clang loop unroll(disable)
139 for (i
= 0; i
< sizeof(*iph
) >> 1; i
++)
142 iph
->check
= ~((csum
& 0xffff) + (csum
>> 16));
144 count_tx(vip
.protocol
);
149 static __always_inline
int handle_ipv6(struct xdp_md
*xdp
)
151 void *data_end
= (void *)(long)xdp
->data_end
;
152 void *data
= (void *)(long)xdp
->data
;
153 struct iptnl_info
*tnl
;
154 struct ethhdr
*new_eth
;
155 struct ethhdr
*old_eth
;
156 struct ipv6hdr
*ip6h
= data
+ sizeof(struct ethhdr
);
161 if (ip6h
+ 1 > data_end
)
164 dport
= get_dport(ip6h
+ 1, data_end
, ip6h
->nexthdr
);
168 vip
.protocol
= ip6h
->nexthdr
;
169 vip
.family
= AF_INET6
;
170 memcpy(vip
.daddr
.v6
, ip6h
->daddr
.s6_addr32
, sizeof(vip
.daddr
));
172 payload_len
= ip6h
->payload_len
;
174 tnl
= bpf_map_lookup_elem(&vip2tnl
, &vip
);
175 /* It only does v6-in-v6 */
176 if (!tnl
|| tnl
->family
!= AF_INET6
)
179 if (bpf_xdp_adjust_head(xdp
, 0 - (int)sizeof(struct ipv6hdr
)))
182 data
= (void *)(long)xdp
->data
;
183 data_end
= (void *)(long)xdp
->data_end
;
186 ip6h
= data
+ sizeof(*new_eth
);
187 old_eth
= data
+ sizeof(*ip6h
);
189 if (new_eth
+ 1 > data_end
|| old_eth
+ 1 > data_end
||
193 set_ethhdr(new_eth
, old_eth
, tnl
, bpf_htons(ETH_P_IPV6
));
197 memset(ip6h
->flow_lbl
, 0, sizeof(ip6h
->flow_lbl
));
198 ip6h
->payload_len
= bpf_htons(bpf_ntohs(payload_len
) + sizeof(*ip6h
));
199 ip6h
->nexthdr
= IPPROTO_IPV6
;
201 memcpy(ip6h
->saddr
.s6_addr32
, tnl
->saddr
.v6
, sizeof(tnl
->saddr
.v6
));
202 memcpy(ip6h
->daddr
.s6_addr32
, tnl
->daddr
.v6
, sizeof(tnl
->daddr
.v6
));
204 count_tx(vip
.protocol
);
209 SEC("xdp_tx_iptunnel")
210 int _xdp_tx_iptunnel(struct xdp_md
*xdp
)
212 void *data_end
= (void *)(long)xdp
->data_end
;
213 void *data
= (void *)(long)xdp
->data
;
214 struct ethhdr
*eth
= data
;
217 if (eth
+ 1 > data_end
)
220 h_proto
= eth
->h_proto
;
222 if (h_proto
== bpf_htons(ETH_P_IP
))
223 return handle_ipv4(xdp
);
224 else if (h_proto
== bpf_htons(ETH_P_IPV6
))
226 return handle_ipv6(xdp
);
231 char _license
[] SEC("license") = "GPL";