1 /* Copyright (c) 2016 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.
7 * This program shows how to use bpf_xdp_adjust_head() by
8 * encapsulating the incoming packet in an IPv4/v6 header
9 * and then XDP_TX it out.
11 #include <uapi/linux/bpf.h>
13 #include <linux/if_ether.h>
14 #include <linux/if_packet.h>
15 #include <linux/if_vlan.h>
17 #include <linux/ipv6.h>
18 #include "bpf_helpers.h"
19 #include "xdp_tx_iptunnel_common.h"
21 struct bpf_map_def
SEC("maps") rxcnt
= {
22 .type
= BPF_MAP_TYPE_PERCPU_ARRAY
,
23 .key_size
= sizeof(__u32
),
24 .value_size
= sizeof(__u64
),
28 struct bpf_map_def
SEC("maps") vip2tnl
= {
29 .type
= BPF_MAP_TYPE_HASH
,
30 .key_size
= sizeof(struct vip
),
31 .value_size
= sizeof(struct iptnl_info
),
32 .max_entries
= MAX_IPTNL_ENTRIES
,
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
= 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 /* The vip key is found. Add an IP header and send it out */
111 if (bpf_xdp_adjust_head(xdp
, 0 - (int)sizeof(struct iphdr
)))
114 data
= (void *)(long)xdp
->data
;
115 data_end
= (void *)(long)xdp
->data_end
;
118 iph
= data
+ sizeof(*new_eth
);
119 old_eth
= data
+ sizeof(*iph
);
121 if (new_eth
+ 1 > data_end
||
122 old_eth
+ 1 > data_end
||
126 set_ethhdr(new_eth
, old_eth
, tnl
, htons(ETH_P_IP
));
129 iph
->ihl
= sizeof(*iph
) >> 2;
131 iph
->protocol
= IPPROTO_IPIP
;
134 iph
->tot_len
= htons(payload_len
+ sizeof(*iph
));
135 iph
->daddr
= tnl
->daddr
.v4
;
136 iph
->saddr
= tnl
->saddr
.v4
;
139 next_iph_u16
= (u16
*)iph
;
140 #pragma clang loop unroll(full)
141 for (i
= 0; i
< sizeof(*iph
) >> 1; i
++)
142 csum
+= *next_iph_u16
++;
144 iph
->check
= ~((csum
& 0xffff) + (csum
>> 16));
146 count_tx(vip
.protocol
);
151 static __always_inline
int handle_ipv6(struct xdp_md
*xdp
)
153 void *data_end
= (void *)(long)xdp
->data_end
;
154 void *data
= (void *)(long)xdp
->data
;
155 struct iptnl_info
*tnl
;
156 struct ethhdr
*new_eth
;
157 struct ethhdr
*old_eth
;
158 struct ipv6hdr
*ip6h
= data
+ sizeof(struct ethhdr
);
163 if (ip6h
+ 1 > data_end
)
166 dport
= get_dport(ip6h
+ 1, data_end
, ip6h
->nexthdr
);
170 vip
.protocol
= ip6h
->nexthdr
;
171 vip
.family
= AF_INET6
;
172 memcpy(vip
.daddr
.v6
, ip6h
->daddr
.s6_addr32
, sizeof(vip
.daddr
));
174 payload_len
= ip6h
->payload_len
;
176 tnl
= bpf_map_lookup_elem(&vip2tnl
, &vip
);
177 /* It only does v6-in-v6 */
178 if (!tnl
|| tnl
->family
!= AF_INET6
)
181 /* The vip key is found. Add an IP header and send it out */
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
||
194 old_eth
+ 1 > data_end
||
198 set_ethhdr(new_eth
, old_eth
, tnl
, htons(ETH_P_IPV6
));
202 memset(ip6h
->flow_lbl
, 0, sizeof(ip6h
->flow_lbl
));
203 ip6h
->payload_len
= htons(ntohs(payload_len
) + sizeof(*ip6h
));
204 ip6h
->nexthdr
= IPPROTO_IPV6
;
206 memcpy(ip6h
->saddr
.s6_addr32
, tnl
->saddr
.v6
, sizeof(tnl
->saddr
.v6
));
207 memcpy(ip6h
->daddr
.s6_addr32
, tnl
->daddr
.v6
, sizeof(tnl
->daddr
.v6
));
209 count_tx(vip
.protocol
);
214 SEC("xdp_tx_iptunnel")
215 int _xdp_tx_iptunnel(struct xdp_md
*xdp
)
217 void *data_end
= (void *)(long)xdp
->data_end
;
218 void *data
= (void *)(long)xdp
->data
;
219 struct ethhdr
*eth
= data
;
222 if (eth
+ 1 > data_end
)
225 h_proto
= eth
->h_proto
;
227 if (h_proto
== htons(ETH_P_IP
))
228 return handle_ipv4(xdp
);
229 else if (h_proto
== htons(ETH_P_IPV6
))
231 return handle_ipv6(xdp
);
236 char _license
[] SEC("license") = "GPL";