The patch migrates the original tests at samples/bpf/tcbpf2_kern.c
and samples/bpf/test_tunnel_bpf.sh to selftests.  There are a couple
changes from the original:
    1) add ipv6 vxlan, ipv6 geneve, ipv6 ipip tests
    2) simplify the original ipip tests (remove iperf tests)
    3) improve documentation
    4) use bpf_ntoh* and bpf_hton* api

In summary, 'test_tunnel_kern.o' contains the following bpf program:
  GRE: gre_set_tunnel, gre_get_tunnel
  IP6GRE: ip6gretap_set_tunnel, ip6gretap_get_tunnel
  ERSPAN: erspan_set_tunnel, erspan_get_tunnel
  IP6ERSPAN: ip4ip6erspan_set_tunnel, ip4ip6erspan_get_tunnel
  VXLAN: vxlan_set_tunnel, vxlan_get_tunnel
  IP6VXLAN: ip6vxlan_set_tunnel, ip6vxlan_get_tunnel
  GENEVE: geneve_set_tunnel, geneve_get_tunnel
  IP6GENEVE: ip6geneve_set_tunnel, ip6geneve_get_tunnel
  IPIP: ipip_set_tunnel, ipip_get_tunnel
  IP6IP: ipip6_set_tunnel, ipip6_get_tunnel,
         ip6ip6_set_tunnel, ip6ip6_get_tunnel

Signed-off-by: William Tu <u9012...@gmail.com>
---
 samples/bpf/Makefile                           |   1 -
 samples/bpf/tcbpf2_kern.c                      | 612 ----------------------
 samples/bpf/test_tunnel_bpf.sh                 | 390 --------------
 tools/testing/selftests/bpf/Makefile           |   5 +-
 tools/testing/selftests/bpf/test_tunnel.sh     | 651 +++++++++++++++++++++++
 tools/testing/selftests/bpf/test_tunnel_kern.c | 691 +++++++++++++++++++++++++
 6 files changed, 1345 insertions(+), 1005 deletions(-)
 delete mode 100644 samples/bpf/tcbpf2_kern.c
 delete mode 100755 samples/bpf/test_tunnel_bpf.sh
 create mode 100755 tools/testing/selftests/bpf/test_tunnel.sh
 create mode 100644 tools/testing/selftests/bpf/test_tunnel_kern.c

diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index aa8c392e2e52..b853581592fd 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -114,7 +114,6 @@ always += sock_flags_kern.o
 always += test_probe_write_user_kern.o
 always += trace_output_kern.o
 always += tcbpf1_kern.o
-always += tcbpf2_kern.o
 always += tc_l2_redirect_kern.o
 always += lathist_kern.o
 always += offwaketime_kern.o
diff --git a/samples/bpf/tcbpf2_kern.c b/samples/bpf/tcbpf2_kern.c
deleted file mode 100644
index fa260c750fb1..000000000000
--- a/samples/bpf/tcbpf2_kern.c
+++ /dev/null
@@ -1,612 +0,0 @@
-/* Copyright (c) 2016 VMware
- * Copyright (c) 2016 Facebook
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation.
- */
-#define KBUILD_MODNAME "foo"
-#include <uapi/linux/bpf.h>
-#include <uapi/linux/if_ether.h>
-#include <uapi/linux/if_packet.h>
-#include <uapi/linux/ip.h>
-#include <uapi/linux/ipv6.h>
-#include <uapi/linux/in.h>
-#include <uapi/linux/tcp.h>
-#include <uapi/linux/filter.h>
-#include <uapi/linux/pkt_cls.h>
-#include <uapi/linux/erspan.h>
-#include <net/ipv6.h>
-#include "bpf_helpers.h"
-#include "bpf_endian.h"
-
-#define _htonl __builtin_bswap32
-#define ERROR(ret) do {\
-               char fmt[] = "ERROR line:%d ret:%d\n";\
-               bpf_trace_printk(fmt, sizeof(fmt), __LINE__, ret); \
-       } while(0)
-
-struct geneve_opt {
-       __be16  opt_class;
-       u8      type;
-       u8      length:5;
-       u8      r3:1;
-       u8      r2:1;
-       u8      r1:1;
-       u8      opt_data[8]; /* hard-coded to 8 byte */
-};
-
-struct vxlan_metadata {
-       u32     gbp;
-};
-
-SEC("gre_set_tunnel")
-int _gre_set_tunnel(struct __sk_buff *skb)
-{
-       int ret;
-       struct bpf_tunnel_key key;
-
-       __builtin_memset(&key, 0x0, sizeof(key));
-       key.remote_ipv4 = 0xac100164; /* 172.16.1.100 */
-       key.tunnel_id = 2;
-       key.tunnel_tos = 0;
-       key.tunnel_ttl = 64;
-
-       ret = bpf_skb_set_tunnel_key(skb, &key, sizeof(key),
-                                    BPF_F_ZERO_CSUM_TX | BPF_F_SEQ_NUMBER);
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       return TC_ACT_OK;
-}
-
-SEC("gre_get_tunnel")
-int _gre_get_tunnel(struct __sk_buff *skb)
-{
-       int ret;
-       struct bpf_tunnel_key key;
-       char fmt[] = "key %d remote ip 0x%x\n";
-
-       ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0);
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       bpf_trace_printk(fmt, sizeof(fmt), key.tunnel_id, key.remote_ipv4);
-       return TC_ACT_OK;
-}
-
-SEC("ip6gretap_set_tunnel")
-int _ip6gretap_set_tunnel(struct __sk_buff *skb)
-{
-       struct bpf_tunnel_key key;
-       int ret;
-
-       __builtin_memset(&key, 0x0, sizeof(key));
-       key.remote_ipv6[3] = _htonl(0x11); /* ::11 */
-       key.tunnel_id = 2;
-       key.tunnel_tos = 0;
-       key.tunnel_ttl = 64;
-       key.tunnel_label = 0xabcde;
-
-       ret = bpf_skb_set_tunnel_key(skb, &key, sizeof(key),
-                                    BPF_F_TUNINFO_IPV6 | BPF_F_ZERO_CSUM_TX |
-                                    BPF_F_SEQ_NUMBER);
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       return TC_ACT_OK;
-}
-
-SEC("ip6gretap_get_tunnel")
-int _ip6gretap_get_tunnel(struct __sk_buff *skb)
-{
-       char fmt[] = "key %d remote ip6 ::%x label %x\n";
-       struct bpf_tunnel_key key;
-       int ret;
-
-       ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key),
-                                    BPF_F_TUNINFO_IPV6);
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       bpf_trace_printk(fmt, sizeof(fmt),
-                        key.tunnel_id, key.remote_ipv6[3], key.tunnel_label);
-
-       return TC_ACT_OK;
-}
-
-SEC("erspan_set_tunnel")
-int _erspan_set_tunnel(struct __sk_buff *skb)
-{
-       struct bpf_tunnel_key key;
-       struct erspan_metadata md;
-       int ret;
-
-       __builtin_memset(&key, 0x0, sizeof(key));
-       key.remote_ipv4 = 0xac100164; /* 172.16.1.100 */
-       key.tunnel_id = 2;
-       key.tunnel_tos = 0;
-       key.tunnel_ttl = 64;
-
-       ret = bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 
BPF_F_ZERO_CSUM_TX);
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       __builtin_memset(&md, 0, sizeof(md));
-#ifdef ERSPAN_V1
-       md.version = 1;
-       md.u.index = bpf_htonl(123);
-#else
-       u8 direction = 1;
-       u8 hwid = 7;
-
-       md.version = 2;
-       md.u.md2.dir = direction;
-       md.u.md2.hwid = hwid & 0xf;
-       md.u.md2.hwid_upper = (hwid >> 4) & 0x3;
-#endif
-
-       ret = bpf_skb_set_tunnel_opt(skb, &md, sizeof(md));
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       return TC_ACT_OK;
-}
-
-SEC("erspan_get_tunnel")
-int _erspan_get_tunnel(struct __sk_buff *skb)
-{
-       char fmt[] = "key %d remote ip 0x%x erspan version %d\n";
-       struct bpf_tunnel_key key;
-       struct erspan_metadata md;
-       u32 index;
-       int ret;
-
-       ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0);
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       ret = bpf_skb_get_tunnel_opt(skb, &md, sizeof(md));
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       bpf_trace_printk(fmt, sizeof(fmt),
-                       key.tunnel_id, key.remote_ipv4, md.version);
-
-#ifdef ERSPAN_V1
-       char fmt2[] = "\tindex %x\n";
-
-       index = bpf_ntohl(md.u.index);
-       bpf_trace_printk(fmt2, sizeof(fmt2), index);
-#else
-       char fmt2[] = "\tdirection %d hwid %x timestamp %u\n";
-
-       bpf_trace_printk(fmt2, sizeof(fmt2),
-                        md.u.md2.dir,
-                        (md.u.md2.hwid_upper << 4) + md.u.md2.hwid,
-                        bpf_ntohl(md.u.md2.timestamp));
-#endif
-
-       return TC_ACT_OK;
-}
-
-SEC("ip4ip6erspan_set_tunnel")
-int _ip4ip6erspan_set_tunnel(struct __sk_buff *skb)
-{
-       struct bpf_tunnel_key key;
-       struct erspan_metadata md;
-       int ret;
-
-       __builtin_memset(&key, 0x0, sizeof(key));
-       key.remote_ipv6[3] = _htonl(0x11);
-       key.tunnel_id = 2;
-       key.tunnel_tos = 0;
-       key.tunnel_ttl = 64;
-
-       ret = bpf_skb_set_tunnel_key(skb, &key, sizeof(key),
-                                    BPF_F_TUNINFO_IPV6);
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       __builtin_memset(&md, 0, sizeof(md));
-
-#ifdef ERSPAN_V1
-       md.u.index = htonl(123);
-       md.version = 1;
-#else
-       u8 direction = 0;
-       u8 hwid = 17;
-
-       md.version = 2;
-       md.u.md2.dir = direction;
-       md.u.md2.hwid = hwid & 0xf;
-       md.u.md2.hwid_upper = (hwid >> 4) & 0x3;
-#endif
-
-       ret = bpf_skb_set_tunnel_opt(skb, &md, sizeof(md));
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       return TC_ACT_OK;
-}
-
-SEC("ip4ip6erspan_get_tunnel")
-int _ip4ip6erspan_get_tunnel(struct __sk_buff *skb)
-{
-       char fmt[] = "ip6erspan get key %d remote ip6 ::%x erspan version %d\n";
-       struct bpf_tunnel_key key;
-       struct erspan_metadata md;
-       u32 index;
-       int ret;
-
-       ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 
BPF_F_TUNINFO_IPV6);
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       ret = bpf_skb_get_tunnel_opt(skb, &md, sizeof(md));
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       bpf_trace_printk(fmt, sizeof(fmt),
-                       key.tunnel_id, key.remote_ipv4, md.version);
-
-#ifdef ERSPAN_V1
-       char fmt2[] = "\tindex %x\n";
-
-       index = bpf_ntohl(md.u.index);
-       bpf_trace_printk(fmt2, sizeof(fmt2), index);
-#else
-       char fmt2[] = "\tdirection %d hwid %x timestamp %u\n";
-
-       bpf_trace_printk(fmt2, sizeof(fmt2),
-                        md.u.md2.dir,
-                        (md.u.md2.hwid_upper << 4) + md.u.md2.hwid,
-                        bpf_ntohl(md.u.md2.timestamp));
-#endif
-
-       return TC_ACT_OK;
-}
-
-SEC("vxlan_set_tunnel")
-int _vxlan_set_tunnel(struct __sk_buff *skb)
-{
-       int ret;
-       struct bpf_tunnel_key key;
-       struct vxlan_metadata md;
-
-       __builtin_memset(&key, 0x0, sizeof(key));
-       key.remote_ipv4 = 0xac100164; /* 172.16.1.100 */
-       key.tunnel_id = 2;
-       key.tunnel_tos = 0;
-       key.tunnel_ttl = 64;
-
-       ret = bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 
BPF_F_ZERO_CSUM_TX);
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       md.gbp = 0x800FF; /* Set VXLAN Group Policy extension */
-       ret = bpf_skb_set_tunnel_opt(skb, &md, sizeof(md));
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       return TC_ACT_OK;
-}
-
-SEC("vxlan_get_tunnel")
-int _vxlan_get_tunnel(struct __sk_buff *skb)
-{
-       int ret;
-       struct bpf_tunnel_key key;
-       struct vxlan_metadata md;
-       char fmt[] = "key %d remote ip 0x%x vxlan gbp 0x%x\n";
-
-       ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0);
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       ret = bpf_skb_get_tunnel_opt(skb, &md, sizeof(md));
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       bpf_trace_printk(fmt, sizeof(fmt),
-                       key.tunnel_id, key.remote_ipv4, md.gbp);
-
-       return TC_ACT_OK;
-}
-
-SEC("geneve_set_tunnel")
-int _geneve_set_tunnel(struct __sk_buff *skb)
-{
-       int ret, ret2;
-       struct bpf_tunnel_key key;
-       struct geneve_opt gopt;
-
-       __builtin_memset(&key, 0x0, sizeof(key));
-       key.remote_ipv4 = 0xac100164; /* 172.16.1.100 */
-       key.tunnel_id = 2;
-       key.tunnel_tos = 0;
-       key.tunnel_ttl = 64;
-
-       __builtin_memset(&gopt, 0x0, sizeof(gopt));
-       gopt.opt_class = 0x102; /* Open Virtual Networking (OVN) */
-       gopt.type = 0x08;
-       gopt.r1 = 0;
-       gopt.r2 = 0;
-       gopt.r3 = 0;
-       gopt.length = 2; /* 4-byte multiple */
-       *(int *) &gopt.opt_data = 0xdeadbeef;
-
-       ret = bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 
BPF_F_ZERO_CSUM_TX);
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       ret = bpf_skb_set_tunnel_opt(skb, &gopt, sizeof(gopt));
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       return TC_ACT_OK;
-}
-
-SEC("geneve_get_tunnel")
-int _geneve_get_tunnel(struct __sk_buff *skb)
-{
-       int ret;
-       struct bpf_tunnel_key key;
-       struct geneve_opt gopt;
-       char fmt[] = "key %d remote ip 0x%x geneve class 0x%x\n";
-
-       ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0);
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       ret = bpf_skb_get_tunnel_opt(skb, &gopt, sizeof(gopt));
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       bpf_trace_printk(fmt, sizeof(fmt),
-                       key.tunnel_id, key.remote_ipv4, gopt.opt_class);
-       return TC_ACT_OK;
-}
-
-SEC("ipip_set_tunnel")
-int _ipip_set_tunnel(struct __sk_buff *skb)
-{
-       struct bpf_tunnel_key key = {};
-       void *data = (void *)(long)skb->data;
-       struct iphdr *iph = data;
-       struct tcphdr *tcp = data + sizeof(*iph);
-       void *data_end = (void *)(long)skb->data_end;
-       int ret;
-
-       /* single length check */
-       if (data + sizeof(*iph) + sizeof(*tcp) > data_end) {
-               ERROR(1);
-               return TC_ACT_SHOT;
-       }
-
-       key.tunnel_ttl = 64;
-       if (iph->protocol == IPPROTO_ICMP) {
-               key.remote_ipv4 = 0xac100164; /* 172.16.1.100 */
-       } else {
-               if (iph->protocol != IPPROTO_TCP || iph->ihl != 5)
-                       return TC_ACT_SHOT;
-
-               if (tcp->dest == htons(5200))
-                       key.remote_ipv4 = 0xac100164; /* 172.16.1.100 */
-               else if (tcp->dest == htons(5201))
-                       key.remote_ipv4 = 0xac100165; /* 172.16.1.101 */
-               else
-                       return TC_ACT_SHOT;
-       }
-
-       ret = bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 0);
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       return TC_ACT_OK;
-}
-
-SEC("ipip_get_tunnel")
-int _ipip_get_tunnel(struct __sk_buff *skb)
-{
-       int ret;
-       struct bpf_tunnel_key key;
-       char fmt[] = "remote ip 0x%x\n";
-
-       ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0);
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       bpf_trace_printk(fmt, sizeof(fmt), key.remote_ipv4);
-       return TC_ACT_OK;
-}
-
-SEC("ipip6_set_tunnel")
-int _ipip6_set_tunnel(struct __sk_buff *skb)
-{
-       struct bpf_tunnel_key key = {};
-       void *data = (void *)(long)skb->data;
-       struct iphdr *iph = data;
-       struct tcphdr *tcp = data + sizeof(*iph);
-       void *data_end = (void *)(long)skb->data_end;
-       int ret;
-
-       /* single length check */
-       if (data + sizeof(*iph) + sizeof(*tcp) > data_end) {
-               ERROR(1);
-               return TC_ACT_SHOT;
-       }
-
-       key.remote_ipv6[0] = _htonl(0x2401db00);
-       key.tunnel_ttl = 64;
-
-       if (iph->protocol == IPPROTO_ICMP) {
-               key.remote_ipv6[3] = _htonl(1);
-       } else {
-               if (iph->protocol != IPPROTO_TCP || iph->ihl != 5) {
-                       ERROR(iph->protocol);
-                       return TC_ACT_SHOT;
-               }
-
-               if (tcp->dest == htons(5200)) {
-                       key.remote_ipv6[3] = _htonl(1);
-               } else if (tcp->dest == htons(5201)) {
-                       key.remote_ipv6[3] = _htonl(2);
-               } else {
-                       ERROR(tcp->dest);
-                       return TC_ACT_SHOT;
-               }
-       }
-
-       ret = bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 
BPF_F_TUNINFO_IPV6);
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       return TC_ACT_OK;
-}
-
-SEC("ipip6_get_tunnel")
-int _ipip6_get_tunnel(struct __sk_buff *skb)
-{
-       int ret;
-       struct bpf_tunnel_key key;
-       char fmt[] = "remote ip6 %x::%x\n";
-
-       ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 
BPF_F_TUNINFO_IPV6);
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       bpf_trace_printk(fmt, sizeof(fmt), _htonl(key.remote_ipv6[0]),
-                        _htonl(key.remote_ipv6[3]));
-       return TC_ACT_OK;
-}
-
-SEC("ip6ip6_set_tunnel")
-int _ip6ip6_set_tunnel(struct __sk_buff *skb)
-{
-       struct bpf_tunnel_key key = {};
-       void *data = (void *)(long)skb->data;
-       struct ipv6hdr *iph = data;
-       struct tcphdr *tcp = data + sizeof(*iph);
-       void *data_end = (void *)(long)skb->data_end;
-       int ret;
-
-       /* single length check */
-       if (data + sizeof(*iph) + sizeof(*tcp) > data_end) {
-               ERROR(1);
-               return TC_ACT_SHOT;
-       }
-
-       key.remote_ipv6[0] = _htonl(0x2401db00);
-       key.tunnel_ttl = 64;
-
-       if (iph->nexthdr == NEXTHDR_ICMP) {
-               key.remote_ipv6[3] = _htonl(1);
-       } else {
-               if (iph->nexthdr != NEXTHDR_TCP) {
-                       ERROR(iph->nexthdr);
-                       return TC_ACT_SHOT;
-               }
-
-               if (tcp->dest == htons(5200)) {
-                       key.remote_ipv6[3] = _htonl(1);
-               } else if (tcp->dest == htons(5201)) {
-                       key.remote_ipv6[3] = _htonl(2);
-               } else {
-                       ERROR(tcp->dest);
-                       return TC_ACT_SHOT;
-               }
-       }
-
-       ret = bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 
BPF_F_TUNINFO_IPV6);
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       return TC_ACT_OK;
-}
-
-SEC("ip6ip6_get_tunnel")
-int _ip6ip6_get_tunnel(struct __sk_buff *skb)
-{
-       int ret;
-       struct bpf_tunnel_key key;
-       char fmt[] = "remote ip6 %x::%x\n";
-
-       ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 
BPF_F_TUNINFO_IPV6);
-       if (ret < 0) {
-               ERROR(ret);
-               return TC_ACT_SHOT;
-       }
-
-       bpf_trace_printk(fmt, sizeof(fmt), _htonl(key.remote_ipv6[0]),
-                        _htonl(key.remote_ipv6[3]));
-       return TC_ACT_OK;
-}
-
-SEC("xfrm_get_state")
-int _xfrm_get_state(struct __sk_buff *skb)
-{
-       struct bpf_xfrm_state x;
-       char fmt[] = "reqid %d spi 0x%x remote ip 0x%x\n";
-       int ret;
-
-       ret = bpf_skb_get_xfrm_state(skb, 0, &x, sizeof(x), 0);
-       if (ret < 0)
-               return TC_ACT_OK;
-
-       bpf_trace_printk(fmt, sizeof(fmt), x.reqid, bpf_ntohl(x.spi),
-                        bpf_ntohl(x.remote_ipv4));
-       return TC_ACT_OK;
-}
-
-char _license[] SEC("license") = "GPL";
diff --git a/samples/bpf/test_tunnel_bpf.sh b/samples/bpf/test_tunnel_bpf.sh
deleted file mode 100755
index 9c534dc07b36..000000000000
--- a/samples/bpf/test_tunnel_bpf.sh
+++ /dev/null
@@ -1,390 +0,0 @@
-#!/bin/bash
-# SPDX-License-Identifier: GPL-2.0
-# In Namespace 0 (at_ns0) using native tunnel
-# Overlay IP: 10.1.1.100
-# local 192.16.1.100 remote 192.16.1.200
-# veth0 IP: 172.16.1.100, tunnel dev <type>00
-
-# Out of Namespace using BPF set/get on lwtunnel
-# Overlay IP: 10.1.1.200
-# local 172.16.1.200 remote 172.16.1.100
-# veth1 IP: 172.16.1.200, tunnel dev <type>11
-
-function config_device {
-       ip netns add at_ns0
-       ip link add veth0 type veth peer name veth1
-       ip link set veth0 netns at_ns0
-       ip netns exec at_ns0 ip addr add 172.16.1.100/24 dev veth0
-       ip netns exec at_ns0 ip link set dev veth0 up
-       ip link set dev veth1 up mtu 1500
-       ip addr add dev veth1 172.16.1.200/24
-}
-
-function add_gre_tunnel {
-       # in namespace
-       ip netns exec at_ns0 \
-        ip link add dev $DEV_NS type $TYPE seq key 2 \
-               local 172.16.1.100 remote 172.16.1.200
-       ip netns exec at_ns0 ip link set dev $DEV_NS up
-       ip netns exec at_ns0 ip addr add dev $DEV_NS 10.1.1.100/24
-
-       # out of namespace
-       ip link add dev $DEV type $TYPE key 2 external
-       ip link set dev $DEV up
-       ip addr add dev $DEV 10.1.1.200/24
-}
-
-function add_ip6gretap_tunnel {
-
-       # assign ipv6 address
-       ip netns exec at_ns0 ip addr add ::11/96 dev veth0
-       ip netns exec at_ns0 ip link set dev veth0 up
-       ip addr add dev veth1 ::22/96
-       ip link set dev veth1 up
-
-       # in namespace
-       ip netns exec at_ns0 \
-               ip link add dev $DEV_NS type $TYPE seq flowlabel 0xbcdef key 2 \
-               local ::11 remote ::22
-
-       ip netns exec at_ns0 ip addr add dev $DEV_NS 10.1.1.100/24
-       ip netns exec at_ns0 ip addr add dev $DEV_NS fc80::100/96
-       ip netns exec at_ns0 ip link set dev $DEV_NS up
-
-       # out of namespace
-       ip link add dev $DEV type $TYPE external
-       ip addr add dev $DEV 10.1.1.200/24
-       ip addr add dev $DEV fc80::200/24
-       ip link set dev $DEV up
-}
-
-function add_erspan_tunnel {
-       # in namespace
-       if [ "$1" == "v1" ]; then
-               ip netns exec at_ns0 \
-               ip link add dev $DEV_NS type $TYPE seq key 2 \
-               local 172.16.1.100 remote 172.16.1.200 \
-               erspan_ver 1 erspan 123
-       else
-               ip netns exec at_ns0 \
-               ip link add dev $DEV_NS type $TYPE seq key 2 \
-               local 172.16.1.100 remote 172.16.1.200 \
-               erspan_ver 2 erspan_dir egress erspan_hwid 3
-       fi
-       ip netns exec at_ns0 ip link set dev $DEV_NS up
-       ip netns exec at_ns0 ip addr add dev $DEV_NS 10.1.1.100/24
-
-       # out of namespace
-       ip link add dev $DEV type $TYPE external
-       ip link set dev $DEV up
-       ip addr add dev $DEV 10.1.1.200/24
-}
-
-function add_ip6erspan_tunnel {
-
-       # assign ipv6 address
-       ip netns exec at_ns0 ip addr add ::11/96 dev veth0
-       ip netns exec at_ns0 ip link set dev veth0 up
-       ip addr add dev veth1 ::22/96
-       ip link set dev veth1 up
-
-       # in namespace
-       if [ "$1" == "v1" ]; then
-               ip netns exec at_ns0 \
-               ip link add dev $DEV_NS type $TYPE seq key 2 \
-               local ::11 remote ::22 \
-               erspan_ver 1 erspan 123
-       else
-               ip netns exec at_ns0 \
-               ip link add dev $DEV_NS type $TYPE seq key 2 \
-               local ::11 remote ::22 \
-               erspan_ver 2 erspan_dir egress erspan_hwid 7
-       fi
-       ip netns exec at_ns0 ip addr add dev $DEV_NS 10.1.1.100/24
-       ip netns exec at_ns0 ip link set dev $DEV_NS up
-
-       # out of namespace
-       ip link add dev $DEV type $TYPE external
-       ip addr add dev $DEV 10.1.1.200/24
-       ip link set dev $DEV up
-}
-
-function add_vxlan_tunnel {
-       # Set static ARP entry here because iptables set-mark works
-       # on L3 packet, as a result not applying to ARP packets,
-       # causing errors at get_tunnel_{key/opt}.
-
-       # in namespace
-       ip netns exec at_ns0 \
-               ip link add dev $DEV_NS type $TYPE id 2 dstport 4789 gbp remote 
172.16.1.200
-       ip netns exec at_ns0 ip link set dev $DEV_NS address 52:54:00:d9:01:00 
up
-       ip netns exec at_ns0 ip addr add dev $DEV_NS 10.1.1.100/24
-       ip netns exec at_ns0 arp -s 10.1.1.200 52:54:00:d9:02:00
-       ip netns exec at_ns0 iptables -A OUTPUT -j MARK --set-mark 0x800FF
-
-       # out of namespace
-       ip link add dev $DEV type $TYPE external gbp dstport 4789
-       ip link set dev $DEV address 52:54:00:d9:02:00 up
-       ip addr add dev $DEV 10.1.1.200/24
-       arp -s 10.1.1.100 52:54:00:d9:01:00
-}
-
-function add_geneve_tunnel {
-       # in namespace
-       ip netns exec at_ns0 \
-               ip link add dev $DEV_NS type $TYPE id 2 dstport 6081 remote 
172.16.1.200
-       ip netns exec at_ns0 ip link set dev $DEV_NS up
-       ip netns exec at_ns0 ip addr add dev $DEV_NS 10.1.1.100/24
-
-       # out of namespace
-       ip link add dev $DEV type $TYPE dstport 6081 external
-       ip link set dev $DEV up
-       ip addr add dev $DEV 10.1.1.200/24
-}
-
-function add_ipip_tunnel {
-       # in namespace
-       ip netns exec at_ns0 \
-               ip link add dev $DEV_NS type $TYPE local 172.16.1.100 remote 
172.16.1.200
-       ip netns exec at_ns0 ip link set dev $DEV_NS up
-       ip netns exec at_ns0 ip addr add dev $DEV_NS 10.1.1.100/24
-
-       # out of namespace
-       ip link add dev $DEV type $TYPE external
-       ip link set dev $DEV up
-       ip addr add dev $DEV 10.1.1.200/24
-}
-
-function setup_xfrm_tunnel {
-       auth=0x$(printf '1%.0s' {1..40})
-       enc=0x$(printf '2%.0s' {1..32})
-       spi_in_to_out=0x1
-       spi_out_to_in=0x2
-       # in namespace
-       # in -> out
-       ip netns exec at_ns0 \
-               ip xfrm state add src 172.16.1.100 dst 172.16.1.200 proto esp \
-                       spi $spi_in_to_out reqid 1 mode tunnel \
-                       auth-trunc 'hmac(sha1)' $auth 96 enc 'cbc(aes)' $enc
-       ip netns exec at_ns0 \
-               ip xfrm policy add src 10.1.1.100/32 dst 10.1.1.200/32 dir out \
-               tmpl src 172.16.1.100 dst 172.16.1.200 proto esp reqid 1 \
-               mode tunnel
-       # out -> in
-       ip netns exec at_ns0 \
-               ip xfrm state add src 172.16.1.200 dst 172.16.1.100 proto esp \
-                       spi $spi_out_to_in reqid 2 mode tunnel \
-                       auth-trunc 'hmac(sha1)' $auth 96 enc 'cbc(aes)' $enc
-       ip netns exec at_ns0 \
-               ip xfrm policy add src 10.1.1.200/32 dst 10.1.1.100/32 dir in \
-               tmpl src 172.16.1.200 dst 172.16.1.100 proto esp reqid 2 \
-               mode tunnel
-       # address & route
-       ip netns exec at_ns0 \
-               ip addr add dev veth0 10.1.1.100/32
-       ip netns exec at_ns0 \
-               ip route add 10.1.1.200 dev veth0 via 172.16.1.200 \
-                       src 10.1.1.100
-
-       # out of namespace
-       # in -> out
-       ip xfrm state add src 172.16.1.100 dst 172.16.1.200 proto esp \
-               spi $spi_in_to_out reqid 1 mode tunnel \
-               auth-trunc 'hmac(sha1)' $auth 96  enc 'cbc(aes)' $enc
-       ip xfrm policy add src 10.1.1.100/32 dst 10.1.1.200/32 dir in \
-               tmpl src 172.16.1.100 dst 172.16.1.200 proto esp reqid 1 \
-               mode tunnel
-       # out -> in
-       ip xfrm state add src 172.16.1.200 dst 172.16.1.100 proto esp \
-               spi $spi_out_to_in reqid 2 mode tunnel \
-               auth-trunc 'hmac(sha1)' $auth 96  enc 'cbc(aes)' $enc
-       ip xfrm policy add src 10.1.1.200/32 dst 10.1.1.100/32 dir out \
-               tmpl src 172.16.1.200 dst 172.16.1.100 proto esp reqid 2 \
-               mode tunnel
-       # address & route
-       ip addr add dev veth1 10.1.1.200/32
-       ip route add 10.1.1.100 dev veth1 via 172.16.1.100 src 10.1.1.200
-}
-
-function attach_bpf {
-       DEV=$1
-       SET_TUNNEL=$2
-       GET_TUNNEL=$3
-       tc qdisc add dev $DEV clsact
-       tc filter add dev $DEV egress bpf da obj tcbpf2_kern.o sec $SET_TUNNEL
-       tc filter add dev $DEV ingress bpf da obj tcbpf2_kern.o sec $GET_TUNNEL
-}
-
-function test_gre {
-       TYPE=gretap
-       DEV_NS=gretap00
-       DEV=gretap11
-       config_device
-       add_gre_tunnel
-       attach_bpf $DEV gre_set_tunnel gre_get_tunnel
-       ping -c 1 10.1.1.100
-       ip netns exec at_ns0 ping -c 1 10.1.1.200
-       cleanup
-}
-
-function test_ip6gre {
-       TYPE=ip6gre
-       DEV_NS=ip6gre00
-       DEV=ip6gre11
-       config_device
-       # reuse the ip6gretap function
-       add_ip6gretap_tunnel
-       attach_bpf $DEV ip6gretap_set_tunnel ip6gretap_get_tunnel
-       # underlay
-       ping6 -c 4 ::11
-       # overlay: ipv4 over ipv6
-       ip netns exec at_ns0 ping -c 1 10.1.1.200
-       ping -c 1 10.1.1.100
-       # overlay: ipv6 over ipv6
-       ip netns exec at_ns0 ping6 -c 1 fc80::200
-       cleanup
-}
-
-function test_ip6gretap {
-       TYPE=ip6gretap
-       DEV_NS=ip6gretap00
-       DEV=ip6gretap11
-       config_device
-       add_ip6gretap_tunnel
-       attach_bpf $DEV ip6gretap_set_tunnel ip6gretap_get_tunnel
-       # underlay
-       ping6 -c 4 ::11
-       # overlay: ipv4 over ipv6
-       ip netns exec at_ns0 ping -i .2 -c 1 10.1.1.200
-       ping -c 1 10.1.1.100
-       # overlay: ipv6 over ipv6
-       ip netns exec at_ns0 ping6 -c 1 fc80::200
-       cleanup
-}
-
-function test_erspan {
-       TYPE=erspan
-       DEV_NS=erspan00
-       DEV=erspan11
-       config_device
-       add_erspan_tunnel $1
-       attach_bpf $DEV erspan_set_tunnel erspan_get_tunnel
-       ping -c 1 10.1.1.100
-       ip netns exec at_ns0 ping -c 1 10.1.1.200
-       cleanup
-}
-
-function test_ip6erspan {
-       TYPE=ip6erspan
-       DEV_NS=ip6erspan00
-       DEV=ip6erspan11
-       config_device
-       add_ip6erspan_tunnel $1
-       attach_bpf $DEV ip4ip6erspan_set_tunnel ip4ip6erspan_get_tunnel
-       ping6 -c 3 ::11
-       ip netns exec at_ns0 ping -c 1 10.1.1.200
-       cleanup
-}
-
-function test_vxlan {
-       TYPE=vxlan
-       DEV_NS=vxlan00
-       DEV=vxlan11
-       config_device
-       add_vxlan_tunnel
-       attach_bpf $DEV vxlan_set_tunnel vxlan_get_tunnel
-       ping -c 1 10.1.1.100
-       ip netns exec at_ns0 ping -c 1 10.1.1.200
-       cleanup
-}
-
-function test_geneve {
-       TYPE=geneve
-       DEV_NS=geneve00
-       DEV=geneve11
-       config_device
-       add_geneve_tunnel
-       attach_bpf $DEV geneve_set_tunnel geneve_get_tunnel
-       ping -c 1 10.1.1.100
-       ip netns exec at_ns0 ping -c 1 10.1.1.200
-       cleanup
-}
-
-function test_ipip {
-       TYPE=ipip
-       DEV_NS=ipip00
-       DEV=ipip11
-       config_device
-       tcpdump -nei veth1 &
-       cat /sys/kernel/debug/tracing/trace_pipe &
-       add_ipip_tunnel
-       ethtool -K veth1 gso off gro off rx off tx off
-       ip link set dev veth1 mtu 1500
-       attach_bpf $DEV ipip_set_tunnel ipip_get_tunnel
-       ping -c 1 10.1.1.100
-       ip netns exec at_ns0 ping -c 1 10.1.1.200
-       ip netns exec at_ns0 iperf -sD -p 5200 > /dev/null
-       sleep 0.2
-       iperf -c 10.1.1.100 -n 5k -p 5200
-       cleanup
-}
-
-function test_xfrm_tunnel {
-       config_device
-        tcpdump -nei veth1 ip &
-       output=$(mktemp)
-       cat /sys/kernel/debug/tracing/trace_pipe | tee $output &
-        setup_xfrm_tunnel
-       tc qdisc add dev veth1 clsact
-       tc filter add dev veth1 proto ip ingress bpf da obj tcbpf2_kern.o \
-               sec xfrm_get_state
-       ip netns exec at_ns0 ping -c 1 10.1.1.200
-       grep "reqid 1" $output
-       grep "spi 0x1" $output
-       grep "remote ip 0xac100164" $output
-       cleanup
-}
-
-function cleanup {
-       set +ex
-       pkill iperf
-       ip netns delete at_ns0
-       ip link del veth1
-       ip link del ipip11
-       ip link del gretap11
-       ip link del ip6gre11
-       ip link del ip6gretap11
-       ip link del vxlan11
-       ip link del geneve11
-       ip link del erspan11
-       ip link del ip6erspan11
-       ip x s flush
-       ip x p flush
-       pkill tcpdump
-       pkill cat
-       set -ex
-}
-
-trap cleanup 0 2 3 6 9
-cleanup
-echo "Testing GRE tunnel..."
-test_gre
-echo "Testing IP6GRE tunnel..."
-test_ip6gre
-echo "Testing IP6GRETAP tunnel..."
-test_ip6gretap
-echo "Testing ERSPAN tunnel..."
-test_erspan v1
-test_erspan v2
-echo "Testing IP6ERSPAN tunnel..."
-test_ip6erspan v1
-test_ip6erspan v2
-echo "Testing VXLAN tunnel..."
-test_vxlan
-echo "Testing GENEVE tunnel..."
-test_geneve
-echo "Testing IPIP tunnel..."
-test_ipip
-echo "Testing IPSec tunnel..."
-test_xfrm_tunnel
-echo "*** PASS ***"
diff --git a/tools/testing/selftests/bpf/Makefile 
b/tools/testing/selftests/bpf/Makefile
index 0c19d5e08f08..b64a7a39cbc8 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -32,7 +32,7 @@ TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o 
test_tcp_estats.o test
        test_l4lb_noinline.o test_xdp_noinline.o test_stacktrace_map.o \
        sample_map_ret0.o test_tcpbpf_kern.o test_stacktrace_build_id.o \
        sockmap_tcp_msg_prog.o connect4_prog.o connect6_prog.o 
test_adjust_tail.o \
-       test_btf_haskv.o test_btf_nokv.o test_sockmap_kern.o
+       test_btf_haskv.o test_btf_nokv.o test_sockmap_kern.o test_tunnel_kern.o
 
 # Order correspond to 'make run_tests' order
 TEST_PROGS := test_kmod.sh \
@@ -40,7 +40,8 @@ TEST_PROGS := test_kmod.sh \
        test_xdp_redirect.sh \
        test_xdp_meta.sh \
        test_offload.py \
-       test_sock_addr.sh
+       test_sock_addr.sh \
+       test_tunnel.sh
 
 # Compile but not part of 'make run_tests'
 TEST_GEN_PROGS_EXTENDED = test_libbpf_open test_sock_addr
diff --git a/tools/testing/selftests/bpf/test_tunnel.sh 
b/tools/testing/selftests/bpf/test_tunnel.sh
new file mode 100755
index 000000000000..7fce054eaebd
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_tunnel.sh
@@ -0,0 +1,651 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+# End-to-end eBPF tunnel test suite
+#   The script tests BPF network tunnel implementation.
+#
+# Topology:
+# ---------
+#     root namespace   |     at_ns0 namespace
+#                      |
+#      -----------     |     -----------
+#      | tnl dev |     |     | tnl dev |  (overlay network)
+#      -----------     |     -----------
+#      metadata-mode   |     native-mode
+#       with bpf       |
+#                      |
+#      ----------      |     ----------
+#      |  veth1  | --------- |  veth0  |   (underlay network)
+#      ----------    peer    ----------
+#
+#
+# Device Configuration
+# --------------------
+# Root namespace with metadata-mode tunnel + BPF
+# Device names and addresses:
+#      veth1 IP: 172.16.1.200, IPv6: 00::22 (underlay)
+#      tunnel dev <type>11, ex: gre11, IPv4: 10.1.1.200 (overlay)
+#
+# Namespace at_ns0 with native tunnel
+# Device names and addresses:
+#      veth0 IPv4: 172.16.1.100, IPv6: 00::11 (underlay)
+#      tunnel dev <type>00, ex: gre00, IPv4: 10.1.1.100 (overlay)
+#
+#
+# End-to-end ping packet flow
+# ---------------------------
+# Most of the tests start by namespace creation, device configuration,
+# then ping the underlay and overlay network.  When doing 'ping 10.1.1.100'
+# from root namespace, the following operations happen:
+# 1) Route lookup shows 10.1.1.100/24 belongs to tnl dev, fwd to tnl dev.
+# 2) Tnl device's egress BPF program is triggered and set the tunnel metadata,
+#    with remote_ip=172.16.1.200 and others.
+# 3) Outer tunnel header is prepended and route the packet to veth1's egress
+# 4) veth0's ingress queue receive the tunneled packet at namespace at_ns0
+# 5) Tunnel protocol handler, ex: vxlan_rcv, decap the packet
+# 6) Forward the packet to the overlay tnl dev
+
+PING_ARG="-c 3 -w 10 -q"
+ret=0
+GREEN='\033[0;92m'
+RED='\033[0;31m'
+NC='\033[0m' # No Color
+
+config_device()
+{
+       ip netns add at_ns0
+       ip link add veth0 type veth peer name veth1
+       ip link set veth0 netns at_ns0
+       ip netns exec at_ns0 ip addr add 172.16.1.100/24 dev veth0
+       ip netns exec at_ns0 ip link set dev veth0 up
+       ip link set dev veth1 up mtu 1500
+       ip addr add dev veth1 172.16.1.200/24
+}
+
+add_gre_tunnel()
+{
+       # at_ns0 namespace
+       ip netns exec at_ns0 \
+        ip link add dev $DEV_NS type $TYPE seq key 2 \
+               local 172.16.1.100 remote 172.16.1.200
+       ip netns exec at_ns0 ip link set dev $DEV_NS up
+       ip netns exec at_ns0 ip addr add dev $DEV_NS 10.1.1.100/24
+
+       # root namespace
+       ip link add dev $DEV type $TYPE key 2 external
+       ip link set dev $DEV up
+       ip addr add dev $DEV 10.1.1.200/24
+}
+
+add_ip6gretap_tunnel()
+{
+
+       # assign ipv6 address
+       ip netns exec at_ns0 ip addr add ::11/96 dev veth0
+       ip netns exec at_ns0 ip link set dev veth0 up
+       ip addr add dev veth1 ::22/96
+       ip link set dev veth1 up
+
+       # at_ns0 namespace
+       ip netns exec at_ns0 \
+               ip link add dev $DEV_NS type $TYPE seq flowlabel 0xbcdef key 2 \
+               local ::11 remote ::22
+
+       ip netns exec at_ns0 ip addr add dev $DEV_NS 10.1.1.100/24
+       ip netns exec at_ns0 ip addr add dev $DEV_NS fc80::100/96
+       ip netns exec at_ns0 ip link set dev $DEV_NS up
+
+       # root namespace
+       ip link add dev $DEV type $TYPE external
+       ip addr add dev $DEV 10.1.1.200/24
+       ip addr add dev $DEV fc80::200/24
+       ip link set dev $DEV up
+}
+
+add_erspan_tunnel()
+{
+       # at_ns0 namespace
+       if [ "$1" == "v1" ]; then
+               ip netns exec at_ns0 \
+               ip link add dev $DEV_NS type $TYPE seq key 2 \
+               local 172.16.1.100 remote 172.16.1.200 \
+               erspan_ver 1 erspan 123
+       else
+               ip netns exec at_ns0 \
+               ip link add dev $DEV_NS type $TYPE seq key 2 \
+               local 172.16.1.100 remote 172.16.1.200 \
+               erspan_ver 2 erspan_dir egress erspan_hwid 3
+       fi
+       ip netns exec at_ns0 ip link set dev $DEV_NS up
+       ip netns exec at_ns0 ip addr add dev $DEV_NS 10.1.1.100/24
+
+       # root namespace
+       ip link add dev $DEV type $TYPE external
+       ip link set dev $DEV up
+       ip addr add dev $DEV 10.1.1.200/24
+}
+
+add_ip6erspan_tunnel()
+{
+
+       # assign ipv6 address
+       ip netns exec at_ns0 ip addr add ::11/96 dev veth0
+       ip netns exec at_ns0 ip link set dev veth0 up
+       ip addr add dev veth1 ::22/96
+       ip link set dev veth1 up
+
+       # at_ns0 namespace
+       if [ "$1" == "v1" ]; then
+               ip netns exec at_ns0 \
+               ip link add dev $DEV_NS type $TYPE seq key 2 \
+               local ::11 remote ::22 \
+               erspan_ver 1 erspan 123
+       else
+               ip netns exec at_ns0 \
+               ip link add dev $DEV_NS type $TYPE seq key 2 \
+               local ::11 remote ::22 \
+               erspan_ver 2 erspan_dir egress erspan_hwid 7
+       fi
+       ip netns exec at_ns0 ip addr add dev $DEV_NS 10.1.1.100/24
+       ip netns exec at_ns0 ip link set dev $DEV_NS up
+
+       # root namespace
+       ip link add dev $DEV type $TYPE external
+       ip addr add dev $DEV 10.1.1.200/24
+       ip link set dev $DEV up
+}
+
+add_vxlan_tunnel()
+{
+       # Set static ARP entry here because iptables set-mark works
+       # on L3 packet, as a result not applying to ARP packets,
+       # causing errors at get_tunnel_{key/opt}.
+
+       # at_ns0 namespace
+       ip netns exec at_ns0 \
+               ip link add dev $DEV_NS type $TYPE id 2 dstport 4789 gbp remote 
172.16.1.200
+       ip netns exec at_ns0 ip link set dev $DEV_NS address 52:54:00:d9:01:00 
up
+       ip netns exec at_ns0 ip addr add dev $DEV_NS 10.1.1.100/24
+       ip netns exec at_ns0 arp -s 10.1.1.200 52:54:00:d9:02:00
+       ip netns exec at_ns0 iptables -A OUTPUT -j MARK --set-mark 0x800FF
+
+       # root namespace
+       ip link add dev $DEV type $TYPE external gbp dstport 4789
+       ip link set dev $DEV address 52:54:00:d9:02:00 up
+       ip addr add dev $DEV 10.1.1.200/24
+       arp -s 10.1.1.100 52:54:00:d9:01:00
+}
+
+add_ip6vxlan_tunnel()
+{
+       #ip netns exec at_ns0 ip -4 addr del 172.16.1.100 dev veth0
+       ip netns exec at_ns0 ip -6 addr add ::11/96 dev veth0
+       ip netns exec at_ns0 ip link set dev veth0 up
+       #ip -4 addr del 172.16.1.200 dev veth1
+       ip -6 addr add dev veth1 ::22/96
+       ip link set dev veth1 up
+
+       # at_ns0 namespace
+       ip netns exec at_ns0 \
+               ip link add dev $DEV_NS type $TYPE id 22 dstport 4789 \
+               local ::11 remote ::22
+       ip netns exec at_ns0 ip addr add dev $DEV_NS 10.1.1.100/24
+       ip netns exec at_ns0 ip link set dev $DEV_NS up
+
+       echo "set dev done" > /dev/kmsg
+
+       # root namespace
+       ip link add dev $DEV type $TYPE external dstport 4789
+       ip addr add dev $DEV 10.1.1.200/24
+       ip link set dev $DEV up
+}
+
+add_geneve_tunnel()
+{
+       # at_ns0 namespace
+       ip netns exec at_ns0 \
+               ip link add dev $DEV_NS type $TYPE \
+               id 2 dstport 6081 remote 172.16.1.200
+       ip netns exec at_ns0 ip link set dev $DEV_NS up
+       ip netns exec at_ns0 ip addr add dev $DEV_NS 10.1.1.100/24
+
+       # root namespace
+       ip link add dev $DEV type $TYPE dstport 6081 external
+       ip link set dev $DEV up
+       ip addr add dev $DEV 10.1.1.200/24
+}
+
+add_ip6geneve_tunnel()
+{
+       ip netns exec at_ns0 ip addr add ::11/96 dev veth0
+       ip netns exec at_ns0 ip link set dev veth0 up
+       ip addr add dev veth1 ::22/96
+       ip link set dev veth1 up
+
+       # at_ns0 namespace
+       ip netns exec at_ns0 \
+               ip link add dev $DEV_NS type $TYPE id 22 \
+               remote ::22     # geneve has no local option
+       ip netns exec at_ns0 ip addr add dev $DEV_NS 10.1.1.100/24
+       ip netns exec at_ns0 ip link set dev $DEV_NS up
+
+       # root namespace
+       ip link add dev $DEV type $TYPE external
+       ip addr add dev $DEV 10.1.1.200/24
+       ip link set dev $DEV up
+}
+
+add_ipip_tunnel()
+{
+       # at_ns0 namespace
+       ip netns exec at_ns0 \
+               ip link add dev $DEV_NS type $TYPE \
+               local 172.16.1.100 remote 172.16.1.200
+       ip netns exec at_ns0 ip link set dev $DEV_NS up
+       ip netns exec at_ns0 ip addr add dev $DEV_NS 10.1.1.100/24
+
+       # root namespace
+       ip link add dev $DEV type $TYPE external
+       ip link set dev $DEV up
+       ip addr add dev $DEV 10.1.1.200/24
+}
+
+add_ipip6tnl_tunnel()
+{
+       ip netns exec at_ns0 ip addr add ::11/96 dev veth0
+       ip netns exec at_ns0 ip link set dev veth0 up
+       ip addr add dev veth1 ::22/96
+       ip link set dev veth1 up
+
+       # at_ns0 namespace
+       ip netns exec at_ns0 \
+               ip link add dev $DEV_NS type $TYPE \
+               local ::11 remote ::22
+       ip netns exec at_ns0 ip addr add dev $DEV_NS 10.1.1.100/24
+       ip netns exec at_ns0 ip link set dev $DEV_NS up
+
+       # root namespace
+       ip link add dev $DEV type $TYPE external
+       ip addr add dev $DEV 10.1.1.200/24
+       ip link set dev $DEV up
+}
+
+test_gre()
+{
+       TYPE=gretap
+       DEV_NS=gretap00
+       DEV=gretap11
+       ret=0
+
+       check $TYPE
+       config_device
+       add_gre_tunnel
+       attach_bpf $DEV gre_set_tunnel gre_get_tunnel
+       ping $PING_ARG 10.1.1.100
+       check_err $?
+       ip netns exec at_ns0 ping $PING_ARG 10.1.1.200
+       check_err $?
+       cleanup
+
+        if [ $ret -ne 0 ]; then
+                echo -e ${RED}"FAIL: $TYPE"${NC}
+                return 1
+        fi
+        echo -e ${GREEN}"PASS: $TYPE"${NC}
+}
+
+test_ip6gre()
+{
+       TYPE=ip6gre
+       DEV_NS=ip6gre00
+       DEV=ip6gre11
+       ret=0
+
+       check $TYPE
+       config_device
+       # reuse the ip6gretap function
+       add_ip6gretap_tunnel
+       attach_bpf $DEV ip6gretap_set_tunnel ip6gretap_get_tunnel
+       # underlay
+       ping6 $PING_ARG ::11
+       # overlay: ipv4 over ipv6
+       ip netns exec at_ns0 ping $PING_ARG 10.1.1.200
+       ping $PING_ARG 10.1.1.100
+       check_err $?
+       # overlay: ipv6 over ipv6
+       ip netns exec at_ns0 ping6 $PING_ARG fc80::200
+       check_err $?
+       cleanup
+
+        if [ $ret -ne 0 ]; then
+                echo -e ${RED}"FAIL: $TYPE"${NC}
+                return 1
+        fi
+        echo -e ${GREEN}"PASS: $TYPE"${NC}
+}
+
+test_ip6gretap()
+{
+       TYPE=ip6gretap
+       DEV_NS=ip6gretap00
+       DEV=ip6gretap11
+       ret=0
+
+       check $TYPE
+       config_device
+       add_ip6gretap_tunnel
+       attach_bpf $DEV ip6gretap_set_tunnel ip6gretap_get_tunnel
+       # underlay
+       ping6 $PING_ARG ::11
+       # overlay: ipv4 over ipv6
+       ip netns exec at_ns0 ping $PING_ARG 10.1.1.200
+       ping $PING_ARG 10.1.1.100
+       check_err $?
+       # overlay: ipv6 over ipv6
+       ip netns exec at_ns0 ping6 $PING_ARG fc80::200
+       check_err $?
+       cleanup
+
+       if [ $ret -ne 0 ]; then
+                echo -e ${RED}"FAIL: $TYPE"${NC}
+                return 1
+        fi
+        echo -e ${GREEN}"PASS: $TYPE"${NC}
+}
+
+test_erspan()
+{
+       TYPE=erspan
+       DEV_NS=erspan00
+       DEV=erspan11
+       ret=0
+
+       check $TYPE
+       config_device
+       add_erspan_tunnel $1
+       attach_bpf $DEV erspan_set_tunnel erspan_get_tunnel
+       ping $PING_ARG 10.1.1.100
+       check_err $?
+       ip netns exec at_ns0 ping $PING_ARG 10.1.1.200
+       check_err $?
+       cleanup
+
+       if [ $ret -ne 0 ]; then
+                echo -e ${RED}"FAIL: $TYPE"${NC}
+                return 1
+        fi
+        echo -e ${GREEN}"PASS: $TYPE"${NC}
+}
+
+test_ip6erspan()
+{
+       TYPE=ip6erspan
+       DEV_NS=ip6erspan00
+       DEV=ip6erspan11
+       ret=0
+
+       check $TYPE
+       config_device
+       add_ip6erspan_tunnel $1
+       attach_bpf $DEV ip4ip6erspan_set_tunnel ip4ip6erspan_get_tunnel
+       ping6 $PING_ARG ::11
+       ip netns exec at_ns0 ping $PING_ARG 10.1.1.200
+       check_err $?
+       cleanup
+
+       if [ $ret -ne 0 ]; then
+                echo -e ${RED}"FAIL: $TYPE"${NC}
+                return 1
+        fi
+        echo -e ${GREEN}"PASS: $TYPE"${NC}
+}
+
+test_vxlan()
+{
+       TYPE=vxlan
+       DEV_NS=vxlan00
+       DEV=vxlan11
+       ret=0
+
+       check $TYPE
+       config_device
+       add_vxlan_tunnel
+       attach_bpf $DEV vxlan_set_tunnel vxlan_get_tunnel
+       ping $PING_ARG 10.1.1.100
+       check_err $?
+       ip netns exec at_ns0 ping $PING_ARG 10.1.1.200
+       check_err $?
+       cleanup
+
+       if [ $ret -ne 0 ]; then
+                echo -e ${RED}"FAIL: $TYPE"${NC}
+                return 1
+        fi
+        echo -e ${GREEN}"PASS: $TYPE"${NC}
+}
+
+test_ip6vxlan()
+{
+       TYPE=vxlan
+       DEV_NS=ip6vxlan00
+       DEV=ip6vxlan11
+       ret=0
+
+       check $TYPE
+       config_device
+       add_ip6vxlan_tunnel
+       ip link set dev veth1 mtu 1500
+       attach_bpf $DEV ip6vxlan_set_tunnel ip6vxlan_get_tunnel
+       echo "set bpf done" > /dev/kmsg
+       # underlay
+       ping6 $PING_ARG ::11
+       echo "ping underlay" > /dev/kmsg
+       # ip4 over ip6
+       ping $PING_ARG 10.1.1.100
+       echo "ping overlay" > /dev/kmsg
+       check_err $?
+       ip netns exec at_ns0 ping $PING_ARG 10.1.1.200
+       check_err $?
+       cleanup
+
+       if [ $ret -ne 0 ]; then
+                echo -e ${RED}"FAIL: ip6$TYPE"${NC}
+                return 1
+        fi
+        echo -e ${GREEN}"PASS: ip6$TYPE"${NC}
+}
+
+test_geneve()
+{
+       TYPE=geneve
+       DEV_NS=geneve00
+       DEV=geneve11
+       ret=0
+
+       check $TYPE
+       config_device
+       add_geneve_tunnel
+       attach_bpf $DEV geneve_set_tunnel geneve_get_tunnel
+       ping $PING_ARG 10.1.1.100
+       check_err $?
+       ip netns exec at_ns0 ping $PING_ARG 10.1.1.200
+       check_err $?
+       cleanup
+
+       if [ $ret -ne 0 ]; then
+                echo -e ${RED}"FAIL: $TYPE"${NC}
+                return 1
+        fi
+        echo -e ${GREEN}"PASS: $TYPE"${NC}
+}
+
+test_ip6geneve()
+{
+       TYPE=geneve
+       DEV_NS=ip6geneve00
+       DEV=ip6geneve11
+       ret=0
+
+       check $TYPE
+       config_device
+       add_ip6geneve_tunnel
+       attach_bpf $DEV ip6geneve_set_tunnel ip6geneve_get_tunnel
+       ping $PING_ARG 10.1.1.100
+       check_err $?
+       ip netns exec at_ns0 ping $PING_ARG 10.1.1.200
+       check_err $?
+       cleanup
+
+       if [ $ret -ne 0 ]; then
+                echo -e ${RED}"FAIL: ip6$TYPE"${NC}
+                return 1
+        fi
+        echo -e ${GREEN}"PASS: ip6$TYPE"${NC}
+}
+
+test_ipip()
+{
+       TYPE=ipip
+       DEV_NS=ipip00
+       DEV=ipip11
+       ret=0
+
+       check $TYPE
+       config_device
+       add_ipip_tunnel
+       ip link set dev veth1 mtu 1500
+       attach_bpf $DEV ipip_set_tunnel ipip_get_tunnel
+       ping $PING_ARG 10.1.1.100
+       check_err $?
+       ip netns exec at_ns0 ping $PING_ARG 10.1.1.200
+       check_err $?
+       cleanup
+
+       if [ $ret -ne 0 ]; then
+                echo -e ${RED}"FAIL: $TYPE"${NC}
+                return 1
+        fi
+        echo -e ${GREEN}"PASS: $TYPE"${NC}
+}
+
+test_ipip6()
+{
+       TYPE=ip6tnl
+       DEV_NS=ipip6tnl00
+       DEV=ipip6tnl11
+       ret=0
+
+       check $TYPE
+       config_device
+       add_ipip6tnl_tunnel
+       ip link set dev veth1 mtu 1500
+       attach_bpf $DEV ipip6_set_tunnel ipip6_get_tunnel
+       # underlay
+       ping6 $PING_ARG ::11
+       # ip4 over ip6
+       ping $PING_ARG 10.1.1.100
+       check_err $?
+       ip netns exec at_ns0 ping $PING_ARG 10.1.1.200
+       check_err $?
+       cleanup
+
+       if [ $ret -ne 0 ]; then
+                echo -e ${RED}"FAIL: $TYPE"${NC}
+                return 1
+        fi
+        echo -e ${GREEN}"PASS: $TYPE"${NC}
+}
+
+attach_bpf()
+{
+       DEV=$1
+       SET=$2
+       GET=$3
+       tc qdisc add dev $DEV clsact
+       tc filter add dev $DEV egress bpf da obj test_tunnel_kern.o sec $SET
+       tc filter add dev $DEV ingress bpf da obj test_tunnel_kern.o sec $GET
+}
+
+cleanup()
+{
+       ip netns delete at_ns0 2> /dev/null
+       ip link del veth1 2> /dev/null
+       ip link del ipip11 2> /dev/null
+       ip link del ipip6tnl11 2> /dev/null
+       ip link del gretap11 2> /dev/null
+       ip link del ip6gre11 2> /dev/null
+       ip link del ip6gretap11 2> /dev/null
+       ip link del vxlan11 2> /dev/null
+       ip link del ip6vxlan11 2> /dev/null
+       ip link del geneve11 2> /dev/null
+       ip link del ip6geneve11 2> /dev/null
+       ip link del erspan11 2> /dev/null
+       ip link del ip6erspan11 2> /dev/null
+}
+
+cleanup_exit()
+{
+       echo "CATCH SIGKILL or SIGINT, cleanup and exit"
+       cleanup
+       exit 0
+}
+
+check()
+{
+   ip link help $1 2>&1 | grep -q "^Usage:"
+    if [ $? -ne 0 ];then
+        echo "SKIP $1: iproute2 not support"
+        cleanup
+        return 1
+    fi
+}
+
+enable_debug()
+{
+    echo 'file ip_gre.c +p' > /sys/kernel/debug/dynamic_debug/control
+    echo 'file ip6_gre.c +p' > /sys/kernel/debug/dynamic_debug/control
+    echo 'file vxlan.c +p' > /sys/kernel/debug/dynamic_debug/control
+    echo 'file geneve.c +p' > /sys/kernel/debug/dynamic_debug/control
+    echo 'file ipip.c +p' > /sys/kernel/debug/dynamic_debug/control
+}
+
+check_err()
+{
+    if [ $ret -eq 0 ]; then
+        ret=$1
+    fi
+}
+
+bpf_tunnel_test()
+{
+       echo "Testing GRE tunnel..."
+       test_gre
+       echo "Testing IP6GRE tunnel..."
+       test_ip6gre
+       echo "Testing IP6GRETAP tunnel..."
+       test_ip6gretap
+       echo "Testing ERSPAN tunnel..."
+       test_erspan v2
+       echo "Testing IP6ERSPAN tunnel..."
+       test_ip6erspan v2
+       echo "Testing VXLAN tunnel..."
+       test_vxlan
+       echo "Testing IP6VXLAN tunnel..."
+       test_ip6vxlan
+       echo "Testing GENEVE tunnel..."
+       test_geneve
+       echo "Testing IP6GENEVE tunnel..."
+       test_ip6geneve
+       echo "Testing IPIP tunnel..."
+       test_ipip
+       echo "Testing IPIP6 tunnel..."
+       test_ipip6
+}
+
+trap cleanup 0 3 6
+trap cleanup_exit 2 9
+
+cleanup
+bpf_tunnel_test
+
+exit 0
diff --git a/tools/testing/selftests/bpf/test_tunnel_kern.c 
b/tools/testing/selftests/bpf/test_tunnel_kern.c
new file mode 100644
index 000000000000..0bf642c530fd
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_tunnel_kern.c
@@ -0,0 +1,691 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2016 VMware
+ * Copyright (c) 2016 Facebook
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ */
+#include <stddef.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <linux/bpf.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/types.h>
+#include <linux/tcp.h>
+#include <linux/socket.h>
+#include <linux/pkt_cls.h>
+#include <linux/erspan.h>
+#include "bpf_helpers.h"
+#include "bpf_endian.h"
+
+#define ERROR(ret) do {\
+               char fmt[] = "ERROR line:%d ret:%d\n";\
+               bpf_trace_printk(fmt, sizeof(fmt), __LINE__, ret); \
+       } while(0)
+
+int _version SEC("version") = 1;
+
+struct geneve_opt {
+       __be16  opt_class;
+       __u8    type;
+       __u8    length:5;
+       __u8    r3:1;
+       __u8    r2:1;
+       __u8    r1:1;
+       __u8    opt_data[8]; /* hard-coded to 8 byte */
+};
+
+struct vxlan_metadata {
+       __u32     gbp;
+};
+
+SEC("gre_set_tunnel")
+int _gre_set_tunnel(struct __sk_buff *skb)
+{
+       int ret;
+       struct bpf_tunnel_key key;
+
+       __builtin_memset(&key, 0x0, sizeof(key));
+       key.remote_ipv4 = 0xac100164; /* 172.16.1.100 */
+       key.tunnel_id = 2;
+       key.tunnel_tos = 0;
+       key.tunnel_ttl = 64;
+
+       ret = bpf_skb_set_tunnel_key(skb, &key, sizeof(key),
+                                    BPF_F_ZERO_CSUM_TX | BPF_F_SEQ_NUMBER);
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       return TC_ACT_OK;
+}
+
+SEC("gre_get_tunnel")
+int _gre_get_tunnel(struct __sk_buff *skb)
+{
+       int ret;
+       struct bpf_tunnel_key key;
+       char fmt[] = "key %d remote ip 0x%x\n";
+
+       ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0);
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       bpf_trace_printk(fmt, sizeof(fmt), key.tunnel_id, key.remote_ipv4);
+       return TC_ACT_OK;
+}
+
+SEC("ip6gretap_set_tunnel")
+int _ip6gretap_set_tunnel(struct __sk_buff *skb)
+{
+       struct bpf_tunnel_key key;
+       int ret;
+
+       __builtin_memset(&key, 0x0, sizeof(key));
+       key.remote_ipv6[3] = bpf_htonl(0x11); /* ::11 */
+       key.tunnel_id = 2;
+       key.tunnel_tos = 0;
+       key.tunnel_ttl = 64;
+       key.tunnel_label = 0xabcde;
+
+       ret = bpf_skb_set_tunnel_key(skb, &key, sizeof(key),
+                                    BPF_F_TUNINFO_IPV6 | BPF_F_ZERO_CSUM_TX |
+                                    BPF_F_SEQ_NUMBER);
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       return TC_ACT_OK;
+}
+
+SEC("ip6gretap_get_tunnel")
+int _ip6gretap_get_tunnel(struct __sk_buff *skb)
+{
+       char fmt[] = "key %d remote ip6 ::%x label %x\n";
+       struct bpf_tunnel_key key;
+       int ret;
+
+       ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key),
+                                    BPF_F_TUNINFO_IPV6);
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       bpf_trace_printk(fmt, sizeof(fmt),
+                        key.tunnel_id, key.remote_ipv6[3], key.tunnel_label);
+
+       return TC_ACT_OK;
+}
+
+SEC("erspan_set_tunnel")
+int _erspan_set_tunnel(struct __sk_buff *skb)
+{
+       struct bpf_tunnel_key key;
+       struct erspan_metadata md;
+       int ret;
+
+       __builtin_memset(&key, 0x0, sizeof(key));
+       key.remote_ipv4 = 0xac100164; /* 172.16.1.100 */
+       key.tunnel_id = 2;
+       key.tunnel_tos = 0;
+       key.tunnel_ttl = 64;
+
+       ret = bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 
BPF_F_ZERO_CSUM_TX);
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       __builtin_memset(&md, 0, sizeof(md));
+#ifdef ERSPAN_V1
+       md.version = 1;
+       md.u.index = bpf_htonl(123);
+#else
+       __u8 direction = 1;
+       __u8 hwid = 7;
+
+       md.version = 2;
+       md.u.md2.dir = direction;
+       md.u.md2.hwid = hwid & 0xf;
+       md.u.md2.hwid_upper = (hwid >> 4) & 0x3;
+#endif
+
+       ret = bpf_skb_set_tunnel_opt(skb, &md, sizeof(md));
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       return TC_ACT_OK;
+}
+
+SEC("erspan_get_tunnel")
+int _erspan_get_tunnel(struct __sk_buff *skb)
+{
+       char fmt[] = "key %d remote ip 0x%x erspan version %d\n";
+       struct bpf_tunnel_key key;
+       struct erspan_metadata md;
+       __u32 index;
+       int ret;
+
+       ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0);
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       ret = bpf_skb_get_tunnel_opt(skb, &md, sizeof(md));
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       bpf_trace_printk(fmt, sizeof(fmt),
+                       key.tunnel_id, key.remote_ipv4, md.version);
+
+#ifdef ERSPAN_V1
+       char fmt2[] = "\tindex %x\n";
+
+       index = bpf_ntohl(md.u.index);
+       bpf_trace_printk(fmt2, sizeof(fmt2), index);
+#else
+       char fmt2[] = "\tdirection %d hwid %x timestamp %u\n";
+
+       bpf_trace_printk(fmt2, sizeof(fmt2),
+                        md.u.md2.dir,
+                        (md.u.md2.hwid_upper << 4) + md.u.md2.hwid,
+                        bpf_ntohl(md.u.md2.timestamp));
+#endif
+
+       return TC_ACT_OK;
+}
+
+SEC("ip4ip6erspan_set_tunnel")
+int _ip4ip6erspan_set_tunnel(struct __sk_buff *skb)
+{
+       struct bpf_tunnel_key key;
+       struct erspan_metadata md;
+       int ret;
+
+       __builtin_memset(&key, 0x0, sizeof(key));
+       key.remote_ipv6[3] = bpf_htonl(0x11);
+       key.tunnel_id = 2;
+       key.tunnel_tos = 0;
+       key.tunnel_ttl = 64;
+
+       ret = bpf_skb_set_tunnel_key(skb, &key, sizeof(key),
+                                    BPF_F_TUNINFO_IPV6);
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       __builtin_memset(&md, 0, sizeof(md));
+
+#ifdef ERSPAN_V1
+       md.u.index = bpf_htonl(123);
+       md.version = 1;
+#else
+       __u8 direction = 0;
+       __u8 hwid = 17;
+
+       md.version = 2;
+       md.u.md2.dir = direction;
+       md.u.md2.hwid = hwid & 0xf;
+       md.u.md2.hwid_upper = (hwid >> 4) & 0x3;
+#endif
+
+       ret = bpf_skb_set_tunnel_opt(skb, &md, sizeof(md));
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       return TC_ACT_OK;
+}
+
+SEC("ip4ip6erspan_get_tunnel")
+int _ip4ip6erspan_get_tunnel(struct __sk_buff *skb)
+{
+       char fmt[] = "ip6erspan get key %d remote ip6 ::%x erspan version %d\n";
+       struct bpf_tunnel_key key;
+       struct erspan_metadata md;
+       __u32 index;
+       int ret;
+
+       ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 
BPF_F_TUNINFO_IPV6);
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       ret = bpf_skb_get_tunnel_opt(skb, &md, sizeof(md));
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       bpf_trace_printk(fmt, sizeof(fmt),
+                       key.tunnel_id, key.remote_ipv4, md.version);
+
+#ifdef ERSPAN_V1
+       char fmt2[] = "\tindex %x\n";
+
+       index = bpf_ntohl(md.u.index);
+       bpf_trace_printk(fmt2, sizeof(fmt2), index);
+#else
+       char fmt2[] = "\tdirection %d hwid %x timestamp %u\n";
+
+       bpf_trace_printk(fmt2, sizeof(fmt2),
+                        md.u.md2.dir,
+                        (md.u.md2.hwid_upper << 4) + md.u.md2.hwid,
+                        bpf_ntohl(md.u.md2.timestamp));
+#endif
+
+       return TC_ACT_OK;
+}
+
+SEC("vxlan_set_tunnel")
+int _vxlan_set_tunnel(struct __sk_buff *skb)
+{
+       int ret;
+       struct bpf_tunnel_key key;
+       struct vxlan_metadata md;
+
+       __builtin_memset(&key, 0x0, sizeof(key));
+       key.remote_ipv4 = 0xac100164; /* 172.16.1.100 */
+       key.tunnel_id = 2;
+       key.tunnel_tos = 0;
+       key.tunnel_ttl = 64;
+
+       ret = bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 
BPF_F_ZERO_CSUM_TX);
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       md.gbp = 0x800FF; /* Set VXLAN Group Policy extension */
+       ret = bpf_skb_set_tunnel_opt(skb, &md, sizeof(md));
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       return TC_ACT_OK;
+}
+
+SEC("vxlan_get_tunnel")
+int _vxlan_get_tunnel(struct __sk_buff *skb)
+{
+       int ret;
+       struct bpf_tunnel_key key;
+       struct vxlan_metadata md;
+       char fmt[] = "key %d remote ip 0x%x vxlan gbp 0x%x\n";
+
+       ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0);
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       ret = bpf_skb_get_tunnel_opt(skb, &md, sizeof(md));
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       bpf_trace_printk(fmt, sizeof(fmt),
+                       key.tunnel_id, key.remote_ipv4, md.gbp);
+
+       return TC_ACT_OK;
+}
+
+SEC("ip6vxlan_set_tunnel")
+int _ip6vxlan_set_tunnel(struct __sk_buff *skb)
+{
+       struct bpf_tunnel_key key;
+       int ret;
+
+       __builtin_memset(&key, 0x0, sizeof(key));
+       key.remote_ipv6[3] = bpf_htonl(0x11); /* ::11 */
+       key.tunnel_id = 22;
+       key.tunnel_tos = 0;
+       key.tunnel_ttl = 64;
+
+       ret = bpf_skb_set_tunnel_key(skb, &key, sizeof(key),
+                                    BPF_F_TUNINFO_IPV6);
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       return TC_ACT_OK;
+}
+
+SEC("ip6vxlan_get_tunnel")
+int _ip6vxlan_get_tunnel(struct __sk_buff *skb)
+{
+       char fmt[] = "key %d remote ip6 ::%x label %x\n";
+       struct bpf_tunnel_key key;
+       int ret;
+
+       ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key),
+                                    BPF_F_TUNINFO_IPV6);
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       bpf_trace_printk(fmt, sizeof(fmt),
+                        key.tunnel_id, key.remote_ipv6[3], key.tunnel_label);
+
+       return TC_ACT_OK;
+}
+
+SEC("geneve_set_tunnel")
+int _geneve_set_tunnel(struct __sk_buff *skb)
+{
+       int ret, ret2;
+       struct bpf_tunnel_key key;
+       struct geneve_opt gopt;
+
+       __builtin_memset(&key, 0x0, sizeof(key));
+       key.remote_ipv4 = 0xac100164; /* 172.16.1.100 */
+       key.tunnel_id = 2;
+       key.tunnel_tos = 0;
+       key.tunnel_ttl = 64;
+
+       __builtin_memset(&gopt, 0x0, sizeof(gopt));
+       gopt.opt_class = bpf_htons(0x102); /* Open Virtual Networking (OVN) */
+       gopt.type = 0x08;
+       gopt.r1 = 0;
+       gopt.r2 = 0;
+       gopt.r3 = 0;
+       gopt.length = 2; /* 4-byte multiple */
+       *(int *) &gopt.opt_data = bpf_htonl(0xdeadbeef);
+
+       ret = bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 
BPF_F_ZERO_CSUM_TX);
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       ret = bpf_skb_set_tunnel_opt(skb, &gopt, sizeof(gopt));
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       return TC_ACT_OK;
+}
+
+SEC("geneve_get_tunnel")
+int _geneve_get_tunnel(struct __sk_buff *skb)
+{
+       int ret;
+       struct bpf_tunnel_key key;
+       struct geneve_opt gopt;
+       char fmt[] = "key %d remote ip 0x%x geneve class 0x%x\n";
+
+       ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0);
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       ret = bpf_skb_get_tunnel_opt(skb, &gopt, sizeof(gopt));
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       bpf_trace_printk(fmt, sizeof(fmt),
+                       key.tunnel_id, key.remote_ipv4, gopt.opt_class);
+       return TC_ACT_OK;
+}
+
+SEC("ip6geneve_set_tunnel")
+int _ip6geneve_set_tunnel(struct __sk_buff *skb)
+{
+       struct bpf_tunnel_key key;
+       struct geneve_opt gopt;
+       int ret;
+
+       __builtin_memset(&key, 0x0, sizeof(key));
+       key.remote_ipv6[3] = bpf_htonl(0x11); /* ::11 */
+       key.tunnel_id = 22;
+       key.tunnel_tos = 0;
+       key.tunnel_ttl = 64;
+
+       ret = bpf_skb_set_tunnel_key(skb, &key, sizeof(key),
+                                    BPF_F_TUNINFO_IPV6);
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       __builtin_memset(&gopt, 0x0, sizeof(gopt));
+       gopt.opt_class = bpf_htons(0x102); /* Open Virtual Networking (OVN) */
+       gopt.type = 0x08;
+       gopt.r1 = 0;
+       gopt.r2 = 0;
+       gopt.r3 = 0;
+       gopt.length = 2; /* 4-byte multiple */
+       *(int *) &gopt.opt_data = bpf_htonl(0xfeedbeef);
+
+       ret = bpf_skb_set_tunnel_opt(skb, &gopt, sizeof(gopt));
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       return TC_ACT_OK;
+}
+
+SEC("ip6geneve_get_tunnel")
+int _ip6geneve_get_tunnel(struct __sk_buff *skb)
+{
+       char fmt[] = "key %d remote ip 0x%x geneve class 0x%x\n";
+       struct bpf_tunnel_key key;
+       struct geneve_opt gopt;
+       int ret;
+
+       ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key),
+                                    BPF_F_TUNINFO_IPV6);
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       ret = bpf_skb_get_tunnel_opt(skb, &gopt, sizeof(gopt));
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       bpf_trace_printk(fmt, sizeof(fmt),
+                       key.tunnel_id, key.remote_ipv4, gopt.opt_class);
+
+       return TC_ACT_OK;
+}
+
+SEC("ipip_set_tunnel")
+int _ipip_set_tunnel(struct __sk_buff *skb)
+{
+       struct bpf_tunnel_key key = {};
+       void *data = (void *)(long)skb->data;
+       struct iphdr *iph = data;
+       struct tcphdr *tcp = data + sizeof(*iph);
+       void *data_end = (void *)(long)skb->data_end;
+       int ret;
+
+       /* single length check */
+       if (data + sizeof(*iph) + sizeof(*tcp) > data_end) {
+               ERROR(1);
+               return TC_ACT_SHOT;
+       }
+
+       key.tunnel_ttl = 64;
+       if (iph->protocol == IPPROTO_ICMP) {
+               key.remote_ipv4 = 0xac100164; /* 172.16.1.100 */
+       } else {
+               if (iph->protocol != IPPROTO_TCP || iph->ihl != 5)
+                       return TC_ACT_SHOT;
+
+               if (tcp->dest == bpf_htons(5200))
+                       key.remote_ipv4 = 0xac100164; /* 172.16.1.100 */
+               else if (tcp->dest == bpf_htons(5201))
+                       key.remote_ipv4 = 0xac100165; /* 172.16.1.101 */
+               else
+                       return TC_ACT_SHOT;
+       }
+
+       ret = bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 0);
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       return TC_ACT_OK;
+}
+
+SEC("ipip_get_tunnel")
+int _ipip_get_tunnel(struct __sk_buff *skb)
+{
+       int ret;
+       struct bpf_tunnel_key key;
+       char fmt[] = "remote ip 0x%x\n";
+
+       ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 0);
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       bpf_trace_printk(fmt, sizeof(fmt), key.remote_ipv4);
+       return TC_ACT_OK;
+}
+
+SEC("ipip6_set_tunnel")
+int _ipip6_set_tunnel(struct __sk_buff *skb)
+{
+       struct bpf_tunnel_key key = {};
+       void *data = (void *)(long)skb->data;
+       struct iphdr *iph = data;
+       struct tcphdr *tcp = data + sizeof(*iph);
+       void *data_end = (void *)(long)skb->data_end;
+       int ret;
+
+       /* single length check */
+       if (data + sizeof(*iph) + sizeof(*tcp) > data_end) {
+               ERROR(1);
+               return TC_ACT_SHOT;
+       }
+
+       __builtin_memset(&key, 0x0, sizeof(key));
+       key.remote_ipv6[3] = bpf_htonl(0x11); /* ::11 */
+       key.tunnel_ttl = 64;
+
+       ret = bpf_skb_set_tunnel_key(skb, &key, sizeof(key),
+                                    BPF_F_TUNINFO_IPV6);
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       return TC_ACT_OK;
+}
+
+SEC("ipip6_get_tunnel")
+int _ipip6_get_tunnel(struct __sk_buff *skb)
+{
+       int ret;
+       struct bpf_tunnel_key key;
+       char fmt[] = "remote ip6 %x::%x\n";
+
+       ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key),
+                                    BPF_F_TUNINFO_IPV6);
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       bpf_trace_printk(fmt, sizeof(fmt), bpf_htonl(key.remote_ipv6[0]),
+                        bpf_htonl(key.remote_ipv6[3]));
+       return TC_ACT_OK;
+}
+
+SEC("ip6ip6_set_tunnel")
+int _ip6ip6_set_tunnel(struct __sk_buff *skb)
+{
+       struct bpf_tunnel_key key = {};
+       void *data = (void *)(long)skb->data;
+       struct ipv6hdr *iph = data;
+       struct tcphdr *tcp = data + sizeof(*iph);
+       void *data_end = (void *)(long)skb->data_end;
+       int ret;
+
+       /* single length check */
+       if (data + sizeof(*iph) + sizeof(*tcp) > data_end) {
+               ERROR(1);
+               return TC_ACT_SHOT;
+       }
+
+       key.remote_ipv6[0] = bpf_htonl(0x2401db00);
+       key.tunnel_ttl = 64;
+
+       if (iph->nexthdr == 58 /* NEXTHDR_ICMP */) {
+               key.remote_ipv6[3] = bpf_htonl(1);
+       } else {
+               if (iph->nexthdr != 6 /* NEXTHDR_TCP */) {
+                       ERROR(iph->nexthdr);
+                       return TC_ACT_SHOT;
+               }
+
+               if (tcp->dest == bpf_htons(5200)) {
+                       key.remote_ipv6[3] = bpf_htonl(1);
+               } else if (tcp->dest == bpf_htons(5201)) {
+                       key.remote_ipv6[3] = bpf_htonl(2);
+               } else {
+                       ERROR(tcp->dest);
+                       return TC_ACT_SHOT;
+               }
+       }
+
+       ret = bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 
BPF_F_TUNINFO_IPV6);
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       return TC_ACT_OK;
+}
+
+SEC("ip6ip6_get_tunnel")
+int _ip6ip6_get_tunnel(struct __sk_buff *skb)
+{
+       int ret;
+       struct bpf_tunnel_key key;
+       char fmt[] = "remote ip6 %x::%x\n";
+
+       ret = bpf_skb_get_tunnel_key(skb, &key, sizeof(key), 
BPF_F_TUNINFO_IPV6);
+       if (ret < 0) {
+               ERROR(ret);
+               return TC_ACT_SHOT;
+       }
+
+       bpf_trace_printk(fmt, sizeof(fmt), bpf_htonl(key.remote_ipv6[0]),
+                        bpf_htonl(key.remote_ipv6[3]));
+       return TC_ACT_OK;
+}
+
+char _license[] SEC("license") = "GPL";
-- 
2.7.4

Reply via email to