1 // SPDX-License-Identifier: GPL-2.0
3 /* In-place tunneling */
8 #include <linux/stddef.h>
10 #include <linux/if_ether.h>
13 #include <linux/ipv6.h>
14 #include <linux/mpls.h>
15 #include <linux/tcp.h>
16 #include <linux/udp.h>
17 #include <linux/pkt_cls.h>
18 #include <linux/types.h>
20 #include "bpf_endian.h"
21 #include "bpf_helpers.h"
23 static const int cfg_port
= 8000;
25 static const int cfg_udp_src
= 20000;
28 #define MPLS_OVER_UDP_PORT 6635
29 #define ETH_OVER_UDP_PORT 7777
31 /* MPLS label 1000 with S bit (last label) set and ttl of 255. */
32 static const __u32 mpls_label
= __bpf_constant_htonl(1000 << 12 |
33 MPLS_LS_S_MASK
| 0xff);
38 } __attribute__((packed
));
48 __u8 pad
[16]; /* enough space for L2 header */
49 } __attribute__((packed
));
54 __u8 pad
[16]; /* enough space for L2 header */
55 } __attribute__((packed
));
57 static __always_inline
void set_ipv4_csum(struct iphdr
*iph
)
59 __u16
*iph16
= (__u16
*)iph
;
65 #pragma clang loop unroll(full)
66 for (i
= 0, csum
= 0; i
< sizeof(*iph
) >> 1; i
++)
69 iph
->check
= ~((csum
& 0xffff) + (csum
>> 16));
72 static __always_inline
int encap_ipv4(struct __sk_buff
*skb
, __u8 encap_proto
,
75 __u16 udp_dst
= UDP_PORT
;
76 struct iphdr iph_inner
;
83 /* Most tests encapsulate a packet into a tunnel with the same
84 * network protocol, and derive the outer header fields from
87 * The 6in4 case tests different inner and outer protocols. As
88 * the inner is ipv6, but the outer expects an ipv4 header as
89 * input, manually build a struct iphdr based on the ipv6hdr.
91 if (encap_proto
== IPPROTO_IPV6
) {
92 const __u32 saddr
= (192 << 24) | (168 << 16) | (1 << 8) | 1;
93 const __u32 daddr
= (192 << 24) | (168 << 16) | (1 << 8) | 2;
94 struct ipv6hdr iph6_inner
;
96 /* Read the IPv6 header */
97 if (bpf_skb_load_bytes(skb
, ETH_HLEN
, &iph6_inner
,
98 sizeof(iph6_inner
)) < 0)
101 /* Derive the IPv4 header fields from the IPv6 header */
102 memset(&iph_inner
, 0, sizeof(iph_inner
));
103 iph_inner
.version
= 4;
105 iph_inner
.tot_len
= bpf_htons(sizeof(iph6_inner
) +
106 bpf_ntohs(iph6_inner
.payload_len
));
107 iph_inner
.ttl
= iph6_inner
.hop_limit
- 1;
108 iph_inner
.protocol
= iph6_inner
.nexthdr
;
109 iph_inner
.saddr
= __bpf_constant_htonl(saddr
);
110 iph_inner
.daddr
= __bpf_constant_htonl(daddr
);
112 tcp_off
= sizeof(iph6_inner
);
114 if (bpf_skb_load_bytes(skb
, ETH_HLEN
, &iph_inner
,
115 sizeof(iph_inner
)) < 0)
118 tcp_off
= sizeof(iph_inner
);
121 /* filter only packets we want */
122 if (iph_inner
.ihl
!= 5 || iph_inner
.protocol
!= IPPROTO_TCP
)
125 if (bpf_skb_load_bytes(skb
, ETH_HLEN
+ tcp_off
,
126 &tcph
, sizeof(tcph
)) < 0)
129 if (tcph
.dest
!= __bpf_constant_htons(cfg_port
))
132 olen
= sizeof(h_outer
.ip
);
135 flags
= BPF_F_ADJ_ROOM_FIXED_GSO
| BPF_F_ADJ_ROOM_ENCAP_L3_IPV4
;
139 l2_len
= sizeof(mpls_label
);
140 udp_dst
= MPLS_OVER_UDP_PORT
;
144 udp_dst
= ETH_OVER_UDP_PORT
;
147 flags
|= BPF_F_ADJ_ROOM_ENCAP_L2(l2_len
);
149 switch (encap_proto
) {
151 flags
|= BPF_F_ADJ_ROOM_ENCAP_L4_GRE
;
152 olen
+= sizeof(h_outer
.l4hdr
.gre
);
153 h_outer
.l4hdr
.gre
.protocol
= bpf_htons(l2_proto
);
154 h_outer
.l4hdr
.gre
.flags
= 0;
157 flags
|= BPF_F_ADJ_ROOM_ENCAP_L4_UDP
;
158 olen
+= sizeof(h_outer
.l4hdr
.udp
);
159 h_outer
.l4hdr
.udp
.source
= __bpf_constant_htons(cfg_udp_src
);
160 h_outer
.l4hdr
.udp
.dest
= bpf_htons(udp_dst
);
161 h_outer
.l4hdr
.udp
.check
= 0;
162 h_outer
.l4hdr
.udp
.len
= bpf_htons(bpf_ntohs(iph_inner
.tot_len
) +
163 sizeof(h_outer
.l4hdr
.udp
) +
173 /* add L2 encap (if specified) */
176 *((__u32
*)((__u8
*)&h_outer
+ olen
)) = mpls_label
;
179 if (bpf_skb_load_bytes(skb
, 0, (__u8
*)&h_outer
+ olen
,
186 /* add room between mac and network header */
187 if (bpf_skb_adjust_room(skb
, olen
, BPF_ADJ_ROOM_MAC
, flags
))
190 /* prepare new outer network header */
191 h_outer
.ip
= iph_inner
;
192 h_outer
.ip
.tot_len
= bpf_htons(olen
+
193 bpf_ntohs(h_outer
.ip
.tot_len
));
194 h_outer
.ip
.protocol
= encap_proto
;
196 set_ipv4_csum((void *)&h_outer
.ip
);
198 /* store new outer network header */
199 if (bpf_skb_store_bytes(skb
, ETH_HLEN
, &h_outer
, olen
,
200 BPF_F_INVALIDATE_HASH
) < 0)
203 /* if changing outer proto type, update eth->h_proto */
204 if (encap_proto
== IPPROTO_IPV6
) {
207 if (bpf_skb_load_bytes(skb
, 0, ð
, sizeof(eth
)) < 0)
209 eth
.h_proto
= bpf_htons(ETH_P_IP
);
210 if (bpf_skb_store_bytes(skb
, 0, ð
, sizeof(eth
), 0) < 0)
217 static __always_inline
int encap_ipv6(struct __sk_buff
*skb
, __u8 encap_proto
,
220 __u16 udp_dst
= UDP_PORT
;
221 struct ipv6hdr iph_inner
;
222 struct v6hdr h_outer
;
228 if (bpf_skb_load_bytes(skb
, ETH_HLEN
, &iph_inner
,
229 sizeof(iph_inner
)) < 0)
232 /* filter only packets we want */
233 if (bpf_skb_load_bytes(skb
, ETH_HLEN
+ sizeof(iph_inner
),
234 &tcph
, sizeof(tcph
)) < 0)
237 if (tcph
.dest
!= __bpf_constant_htons(cfg_port
))
240 olen
= sizeof(h_outer
.ip
);
243 flags
= BPF_F_ADJ_ROOM_FIXED_GSO
| BPF_F_ADJ_ROOM_ENCAP_L3_IPV6
;
247 l2_len
= sizeof(mpls_label
);
248 udp_dst
= MPLS_OVER_UDP_PORT
;
252 udp_dst
= ETH_OVER_UDP_PORT
;
255 flags
|= BPF_F_ADJ_ROOM_ENCAP_L2(l2_len
);
257 switch (encap_proto
) {
259 flags
|= BPF_F_ADJ_ROOM_ENCAP_L4_GRE
;
260 olen
+= sizeof(h_outer
.l4hdr
.gre
);
261 h_outer
.l4hdr
.gre
.protocol
= bpf_htons(l2_proto
);
262 h_outer
.l4hdr
.gre
.flags
= 0;
265 flags
|= BPF_F_ADJ_ROOM_ENCAP_L4_UDP
;
266 olen
+= sizeof(h_outer
.l4hdr
.udp
);
267 h_outer
.l4hdr
.udp
.source
= __bpf_constant_htons(cfg_udp_src
);
268 h_outer
.l4hdr
.udp
.dest
= bpf_htons(udp_dst
);
269 tot_len
= bpf_ntohs(iph_inner
.payload_len
) + sizeof(iph_inner
) +
270 sizeof(h_outer
.l4hdr
.udp
);
271 h_outer
.l4hdr
.udp
.check
= 0;
272 h_outer
.l4hdr
.udp
.len
= bpf_htons(tot_len
);
280 /* add L2 encap (if specified) */
283 *((__u32
*)((__u8
*)&h_outer
+ olen
)) = mpls_label
;
286 if (bpf_skb_load_bytes(skb
, 0, (__u8
*)&h_outer
+ olen
,
293 /* add room between mac and network header */
294 if (bpf_skb_adjust_room(skb
, olen
, BPF_ADJ_ROOM_MAC
, flags
))
297 /* prepare new outer network header */
298 h_outer
.ip
= iph_inner
;
299 h_outer
.ip
.payload_len
= bpf_htons(olen
+
300 bpf_ntohs(h_outer
.ip
.payload_len
));
302 h_outer
.ip
.nexthdr
= encap_proto
;
304 /* store new outer network header */
305 if (bpf_skb_store_bytes(skb
, ETH_HLEN
, &h_outer
, olen
,
306 BPF_F_INVALIDATE_HASH
) < 0)
312 SEC("encap_ipip_none")
313 int __encap_ipip_none(struct __sk_buff
*skb
)
315 if (skb
->protocol
== __bpf_constant_htons(ETH_P_IP
))
316 return encap_ipv4(skb
, IPPROTO_IPIP
, ETH_P_IP
);
321 SEC("encap_gre_none")
322 int __encap_gre_none(struct __sk_buff
*skb
)
324 if (skb
->protocol
== __bpf_constant_htons(ETH_P_IP
))
325 return encap_ipv4(skb
, IPPROTO_GRE
, ETH_P_IP
);
330 SEC("encap_gre_mpls")
331 int __encap_gre_mpls(struct __sk_buff
*skb
)
333 if (skb
->protocol
== __bpf_constant_htons(ETH_P_IP
))
334 return encap_ipv4(skb
, IPPROTO_GRE
, ETH_P_MPLS_UC
);
340 int __encap_gre_eth(struct __sk_buff
*skb
)
342 if (skb
->protocol
== __bpf_constant_htons(ETH_P_IP
))
343 return encap_ipv4(skb
, IPPROTO_GRE
, ETH_P_TEB
);
348 SEC("encap_udp_none")
349 int __encap_udp_none(struct __sk_buff
*skb
)
351 if (skb
->protocol
== __bpf_constant_htons(ETH_P_IP
))
352 return encap_ipv4(skb
, IPPROTO_UDP
, ETH_P_IP
);
357 SEC("encap_udp_mpls")
358 int __encap_udp_mpls(struct __sk_buff
*skb
)
360 if (skb
->protocol
== __bpf_constant_htons(ETH_P_IP
))
361 return encap_ipv4(skb
, IPPROTO_UDP
, ETH_P_MPLS_UC
);
367 int __encap_udp_eth(struct __sk_buff
*skb
)
369 if (skb
->protocol
== __bpf_constant_htons(ETH_P_IP
))
370 return encap_ipv4(skb
, IPPROTO_UDP
, ETH_P_TEB
);
375 SEC("encap_sit_none")
376 int __encap_sit_none(struct __sk_buff
*skb
)
378 if (skb
->protocol
== __bpf_constant_htons(ETH_P_IPV6
))
379 return encap_ipv4(skb
, IPPROTO_IPV6
, ETH_P_IP
);
384 SEC("encap_ip6tnl_none")
385 int __encap_ip6tnl_none(struct __sk_buff
*skb
)
387 if (skb
->protocol
== __bpf_constant_htons(ETH_P_IPV6
))
388 return encap_ipv6(skb
, IPPROTO_IPV6
, ETH_P_IPV6
);
393 SEC("encap_ip6gre_none")
394 int __encap_ip6gre_none(struct __sk_buff
*skb
)
396 if (skb
->protocol
== __bpf_constant_htons(ETH_P_IPV6
))
397 return encap_ipv6(skb
, IPPROTO_GRE
, ETH_P_IPV6
);
402 SEC("encap_ip6gre_mpls")
403 int __encap_ip6gre_mpls(struct __sk_buff
*skb
)
405 if (skb
->protocol
== __bpf_constant_htons(ETH_P_IPV6
))
406 return encap_ipv6(skb
, IPPROTO_GRE
, ETH_P_MPLS_UC
);
411 SEC("encap_ip6gre_eth")
412 int __encap_ip6gre_eth(struct __sk_buff
*skb
)
414 if (skb
->protocol
== __bpf_constant_htons(ETH_P_IPV6
))
415 return encap_ipv6(skb
, IPPROTO_GRE
, ETH_P_TEB
);
420 SEC("encap_ip6udp_none")
421 int __encap_ip6udp_none(struct __sk_buff
*skb
)
423 if (skb
->protocol
== __bpf_constant_htons(ETH_P_IPV6
))
424 return encap_ipv6(skb
, IPPROTO_UDP
, ETH_P_IPV6
);
429 SEC("encap_ip6udp_mpls")
430 int __encap_ip6udp_mpls(struct __sk_buff
*skb
)
432 if (skb
->protocol
== __bpf_constant_htons(ETH_P_IPV6
))
433 return encap_ipv6(skb
, IPPROTO_UDP
, ETH_P_MPLS_UC
);
438 SEC("encap_ip6udp_eth")
439 int __encap_ip6udp_eth(struct __sk_buff
*skb
)
441 if (skb
->protocol
== __bpf_constant_htons(ETH_P_IPV6
))
442 return encap_ipv6(skb
, IPPROTO_UDP
, ETH_P_TEB
);
447 static int decap_internal(struct __sk_buff
*skb
, int off
, int len
, char proto
)
449 char buf
[sizeof(struct v6hdr
)];
459 olen
+= sizeof(struct gre_hdr
);
460 if (bpf_skb_load_bytes(skb
, off
+ len
, &greh
, sizeof(greh
)) < 0)
462 switch (bpf_ntohs(greh
.protocol
)) {
464 olen
+= sizeof(mpls_label
);
472 olen
+= sizeof(struct udphdr
);
473 if (bpf_skb_load_bytes(skb
, off
+ len
, &udph
, sizeof(udph
)) < 0)
475 switch (bpf_ntohs(udph
.dest
)) {
476 case MPLS_OVER_UDP_PORT
:
477 olen
+= sizeof(mpls_label
);
479 case ETH_OVER_UDP_PORT
:
488 if (bpf_skb_adjust_room(skb
, -olen
, BPF_ADJ_ROOM_MAC
,
489 BPF_F_ADJ_ROOM_FIXED_GSO
))
495 static int decap_ipv4(struct __sk_buff
*skb
)
497 struct iphdr iph_outer
;
499 if (bpf_skb_load_bytes(skb
, ETH_HLEN
, &iph_outer
,
500 sizeof(iph_outer
)) < 0)
503 if (iph_outer
.ihl
!= 5)
506 return decap_internal(skb
, ETH_HLEN
, sizeof(iph_outer
),
510 static int decap_ipv6(struct __sk_buff
*skb
)
512 struct ipv6hdr iph_outer
;
514 if (bpf_skb_load_bytes(skb
, ETH_HLEN
, &iph_outer
,
515 sizeof(iph_outer
)) < 0)
518 return decap_internal(skb
, ETH_HLEN
, sizeof(iph_outer
),
523 int decap_f(struct __sk_buff
*skb
)
525 switch (skb
->protocol
) {
526 case __bpf_constant_htons(ETH_P_IP
):
527 return decap_ipv4(skb
);
528 case __bpf_constant_htons(ETH_P_IPV6
):
529 return decap_ipv6(skb
);
531 /* does not match, ignore */
536 char __license
[] SEC("license") = "GPL";