srv6-mobile 97/23297/29
authorTetsuya Murakami <tetsuya.mrk@gmail.com>
Wed, 6 Nov 2019 19:05:51 +0000 (11:05 -0800)
committerOle Trøan <otroan@employees.org>
Tue, 19 Nov 2019 07:02:40 +0000 (07:02 +0000)
Type: feature

Plug-in for IPv6 Segment Routing Mobile

This funcion was developed by

Signed-off-by: Satoru Matsuchima <satoru.matsushima@gmail.com>
Signed-off-by: Filip Varga <filipvarga89@gmail.com>
Signed-off-by: Tetsuya Murakami <tetsuya.mrk@gmail.com>
Signed-off-by: Tetsuya Murakami <tetsuya.mrk@gmail.com>
Change-Id: Ie995adc73d8f8d444339aab71619c3599e69f12d
Signed-off-by: Tetsuya Murakami <tetsuya.mrk@gmail.com>
30 files changed:
MAINTAINERS
src/plugins/srv6-ad/ad.c
src/plugins/srv6-ad/node.c
src/plugins/srv6-am/am.c
src/plugins/srv6-as/as.c
src/plugins/srv6-mobile/CMakeLists.txt [new file with mode: 0644]
src/plugins/srv6-mobile/FEATURE.yaml [new file with mode: 0644]
src/plugins/srv6-mobile/README.md [new file with mode: 0644]
src/plugins/srv6-mobile/extra/Dockerfile.j2 [new file with mode: 0644]
src/plugins/srv6-mobile/extra/Dockerfile.j2.release [new file with mode: 0644]
src/plugins/srv6-mobile/extra/README.md [new file with mode: 0644]
src/plugins/srv6-mobile/extra/requirements.txt [new file with mode: 0644]
src/plugins/srv6-mobile/extra/runner.py [new file with mode: 0755]
src/plugins/srv6-mobile/extra/startup.conf.j2 [new file with mode: 0644]
src/plugins/srv6-mobile/extra/topo-init.png [new file with mode: 0644]
src/plugins/srv6-mobile/extra/topo-test_gtp4d.png [new file with mode: 0644]
src/plugins/srv6-mobile/extra/topo-test_gtp6.png [new file with mode: 0644]
src/plugins/srv6-mobile/extra/topo-test_gtp6d.png [new file with mode: 0644]
src/plugins/srv6-mobile/extra/topo-test_gtp6ip6.png [new file with mode: 0644]
src/plugins/srv6-mobile/gtp4_e.c [new file with mode: 0644]
src/plugins/srv6-mobile/gtp6_d.c [new file with mode: 0644]
src/plugins/srv6-mobile/gtp6_d_di.c [new file with mode: 0644]
src/plugins/srv6-mobile/gtp6_e.c [new file with mode: 0644]
src/plugins/srv6-mobile/mobile.h [new file with mode: 0644]
src/plugins/srv6-mobile/node.c [new file with mode: 0644]
src/plugins/srv6-mobile/test/test_srv6_end.py [new file with mode: 0644]
src/vat/api_format.c
src/vnet/srv6/sr.h
src/vnet/srv6/sr_api.c
src/vnet/srv6/sr_localsid.c

index 6e05dfe..9ce148d 100644 (file)
@@ -413,6 +413,12 @@ I: srv6-as
 M:     Francois Clad <fclad@cisco.com>
 F:     src/plugins/srv6-as/
 
+Plugin - IPv6 Segment Routing Mobile
+I:      srv6-mobile
+M:      Tetsuya Murakami <tetsuya.mrk@gmail.com>
+M:     Satoru Matsushima <satoru.matsushima@gmail.com>
+F:      src/plugins/srv6-mobile/
+
 Plugin - Link Aggregation Control Protocol
 I:     lacp
 M:     Steven Luong <sluong@cisco.com>
index 73ea3f6..1b11037 100644 (file)
@@ -425,6 +425,7 @@ srv6_ad_init (vlib_main_t * vm)
                                      keyword_str,
                                      def_str,
                                      params_str,
+                                     128,
                                      &sm->srv6_ad_dpo_type,
                                      format_srv6_ad_localsid,
                                      unformat_srv6_ad_localsid,
index 8f32ae0..dbd68cf 100644 (file)
@@ -1,4 +1,6 @@
 /*
+ * node.c
+ *
  * Copyright (c) 2015 Cisco and/or its affiliates.
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -12,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #include <vlib/vlib.h>
 #include <vnet/vnet.h>
 #include <vppinfra/error.h>
@@ -705,9 +708,9 @@ VLIB_REGISTER_NODE (srv6_ad6_rewrite_node) = {
 /* *INDENT-ON* */
 
 /*
-* fd.io coding-style-patch-verification: ON
-*
-* Local Variables:
-* eval: (c-set-style "gnu")
-* End:
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
 */
index 2507a75..1408ebc 100644 (file)
@@ -212,6 +212,7 @@ srv6_am_init (vlib_main_t * vm)
                                      keyword_str,
                                      def_str,
                                      params_str,
+                                     128,
                                      &sm->srv6_am_dpo_type,
                                      format_srv6_am_localsid,
                                      unformat_srv6_am_localsid,
index 99e6851..cb95991 100644 (file)
@@ -535,6 +535,7 @@ srv6_as_init (vlib_main_t * vm)
                                      keyword_str,
                                      def_str,
                                      params_str,
+                                     128,
                                      &sm->srv6_as_dpo_type,
                                      format_srv6_as_localsid,
                                      unformat_srv6_as_localsid,
diff --git a/src/plugins/srv6-mobile/CMakeLists.txt b/src/plugins/srv6-mobile/CMakeLists.txt
new file mode 100644 (file)
index 0000000..612d626
--- /dev/null
@@ -0,0 +1,24 @@
+# Copyright (c) 2019 Arrcus Inc and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+add_vpp_plugin(srv6mobile
+  SOURCES
+  gtp4_e.c
+  gtp6_e.c
+  gtp6_d.c
+  gtp6_d_di.c
+  node.c
+
+  INSTALL_HEADERS
+  mobile.h
+)
diff --git a/src/plugins/srv6-mobile/FEATURE.yaml b/src/plugins/srv6-mobile/FEATURE.yaml
new file mode 100644 (file)
index 0000000..6d5cf10
--- /dev/null
@@ -0,0 +1,12 @@
+name: SRv6 Mobuile
+maintainer: Tetsuya Murakami <tetsuya.mrk@gmail.com>
+features:
+  - GTP4.D
+  - GTP4.E
+  - GTP6.D
+  - GTP6.D.Di
+  - GTP6.E
+description: "SRv6 Mobile End Functions. GTP4.D, GTP4.E,
+              GTP6.D, GTP6.D.Di and GTP6.E are supported."
+state: production
+properties: [API, CLI, MULTITHREAD]
diff --git a/src/plugins/srv6-mobile/README.md b/src/plugins/srv6-mobile/README.md
new file mode 100644 (file)
index 0000000..1862880
--- /dev/null
@@ -0,0 +1,178 @@
+SRv6 Mobile User Plane Plugin for VPP
+========================
+
+## Introduction
+
+This plugin module can provide the stateless mobile user plane protocols translation between GTP-U and SRv6.
+The functions of the translation take advantage of SRv6 network programmability.
+[SRv6 Mobile User Plane](https://tools.ietf.org/html/draft-ietf-dmm-srv6-mobile-uplane) defines the user plane protocol using SRv6
+including following stateless translation functions:
+
+- **T.M.GTP4.D:**  
+   GTP-U over UDP/IPv4 -> SRv6
+- **End.M.GTP4.E:**  
+   SRv6 -> GTP-U over UDP/IPv4
+- **End.M.GTP6.D:**   
+   GTP-U over UDP/IPv6 -> SRv6
+- **End.M.GTP6.E:**  
+   SRv6 -> GTP-U over UDP/IPv6
+
+These functions benefit user plane(overlay) to be able to utilize data plane(underlay) networks properly. And also it benefits
+data plane to be able to handle user plane in routing paradigm.
+
+## Getting started
+To play with SRv6 Mobile User Plane on VPP, you need to install following packages:
+
+       docker
+       python3
+       pip3
+
+       Python packages (use pip):
+       docker
+       scapy
+       jinja2
+
+
+### Quick-start
+
+1. Build up the docker container image as following:
+
+```
+$ git clone https://github.com/filvarga/srv6-mobile.git
+$ cd ./srv6-mobile/extras/ietf105
+$ ./runner.py infra build
+
+$ docker images
+REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
+ietf105-image       latest              577e786b7ec6        2 days ago          5.57GB
+ubuntu              18.04               4c108a37151f        4 weeks ago         64.2MB
+
+```
+
+The runner script [runner.py](test/runner.py) has features to automate configurations and procedures for the test.
+
+2. Instantiate test Scenario
+
+Let's try following command to instantiate a topology:
+
+```
+$ ./runner.py infra start
+```
+
+This command instantiates 4 VPP containers with following topology:
+
+![Topology Diagram](test/topo-init.png)
+
+You can check the instantiated docker instances with "docker ps".
+
+
+```
+$ docker ps
+CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS               NAMES
+44cb98994500        ietf105-image       "/bin/sh -c 'vpp -c …"   About a minute ago   Up About a minute                       hck-vpp-4
+6d65fff8aee9        ietf105-image       "/bin/sh -c 'vpp -c …"   About a minute ago   Up About a minute                       hck-vpp-3
+ad123b516b24        ietf105-image       "/bin/sh -c 'vpp -c …"   About a minute ago   Up About a minute                       hck-vpp-2
+5efed405b96a        ietf105-image       "/bin/sh -c 'vpp -c …"   About a minute ago   Up About a minute                       hck-vpp-1
+
+```
+
+You can login to and configure each instantiated container.
+
+```
+$ ./runner.py cmd vppctl 0
+
+Verified image: None
+connecting to: hck-vpp-1
+    _______    _        _   _____  ___
+ __/ __/ _ \  (_)__    | | / / _ \/ _ \
+ _/ _// // / / / _ \   | |/ / ___/ ___/
+ /_/ /____(_)_/\___/   |___/_/  /_/    
+
+vpp#
+```
+
+## Test Scenarios
+### SRv6 Drop-in between GTP-U tunnel
+
+This test scenario introduces SRv6 path between GTP-U tunnel transparently. A GTP-U packet sent out from one end to another is translated to SRv6 and then back to GTP-U. All GTP-U tunnel identifiers are preserved in IPv6 header and SRH.
+
+
+#### GTP-U over UDP/IPv4 case
+
+This case uses SRv6 end functions, T.M.GTP4.D and End.M.GTP4.E.
+
+![Topology Diagram](test/topo-test_gtp4d.png)
+
+VPP1 is configured with "T.M.GTP4.D", and VPP4 is configured with "End.M.GTP4.E". Others are configured with "End". The packet generator sends a GTP-U packet over UDP/IPv4 toward the packet capture. VPP1 translates it to SRv6 toward D4::TEID with SR policy <D2::, D3::> in SRH. VPP4 translates the SRv6 packet to the original GTP-U packet and send out to the packet capture.
+
+To start this case with IPv4 payload over GTP-U, you can run:
+
+```
+$ ./runner.py test tmap
+```
+
+If you want to use IPv6 payload instead of IPv4, you can run:
+
+```
+$ ./runner.py test tmap_ipv6
+```
+
+
+#### GTP-U over UDP/IPv6 case
+
+This case uses SRv6 end functions, End.M.GTP6.D.Di and End.M.GTP6.E.
+
+![Topology Diagram](test/topo-test_gtp6d.png)
+
+VPP1 is configured with "End.M.GTP6.D.Di", and VPP4 is configured with "End.M.GTP4.E". Others are configured with "End". The packet generator sends a GTP-U packet over UDP/IPv6 toward D:: of the packet capture. VPP1 translates it to SRv6 toward D:: with SR policy <D2::, D3::, D4::TEID> in SRH. VPP4 translates the SRv6 packet to the original GTP-U packet and send out to the packet capture.
+
+To start this case with IPv4 payload over GTP-U, you can run:
+
+```
+$ ./runner.py test gtp6_drop_in
+```
+
+If you want to use IPv6 payload instead of IPv4, you can run:
+
+```
+$ ./runner.py test gtp6_drop_in_ipv6
+```
+
+
+### GTP-U to SRv6
+
+This test scenario demonstrates GTP-U to SRv6 translation. A GTP-U packet sent out from one end to another is translated to SRv6.
+
+#### GTP-U over UDP/IPv6 case
+
+##### IPv4 payload
+
+This case uses SRv6 end functions, End.M.GTP6.D and End.DT4.
+
+![Topology Diagram](test/topo-test_gtp6.png)
+
+VPP1 is configured with "End.M.GTP6.D", and VPP4 is configured with "End.DT4". Others are configured with "End". The packet generator sends a GTP-U packet over UDP/IPv6 toward D::2. VPP1 translates it to SRv6 toward the IPv6 destination consists of D4:: and TEID of GTP-U with SR policy <D2::, D3::> in SRH. VPP4 decapsulates the SRv6 packet and lookup the table for the inner IPv4 packet and send out to the packet capture.
+
+To start this case, you can run:
+
+```
+$ ./runner.py test gtp6
+```
+
+##### IPv6 payload
+
+This case uses SRv6 end functions, End.M.GTP6.D and End.DT6.
+
+
+![Topology Diagram](test/topo-test_gtp6ip6.png)
+
+The configurations are same with IPv4 payload case, except D4:: is configured as "End.DT6" in VPP4. VPP4 decapsulates the SRv6 packet and lookup the table for the inner IPv6 packet and send out to the packet capture.
+
+If you want to use IPv6 payload instead of IPv4, you can run:
+
+```
+$ ./runner.py test gtp6_ipv6
+```
+
+## More information
+TBD
diff --git a/src/plugins/srv6-mobile/extra/Dockerfile.j2 b/src/plugins/srv6-mobile/extra/Dockerfile.j2
new file mode 100644 (file)
index 0000000..ac58fa3
--- /dev/null
@@ -0,0 +1,48 @@
+
+FROM ubuntu:18.04
+
+RUN set -eux; \
+    apt-get update; \
+    apt-get install -y --no-install-recommends \
+    inetutils-traceroute \
+    ca-certificates \
+    build-essential \
+    git gdb sudo \
+    iputils-ping \
+    net-tools \
+    iproute2 \
+    tcpdump \
+    python3-cffi \
+    asciidoc \
+    xmlto \
+    netcat; \
+    rm -rf /var/lib/apt/lists/*; \
+    mv /usr/sbin/tcpdump /usr/bin/tcpdump
+
+RUN set -eux; \
+    mkdir -p {{vpp_path}}
+
+COPY . / {{vpp_path}}/
+
+WORKDIR {{vpp_path}}
+
+RUN set -eux; \
+    make wipe; \
+    export UNATTENDED=y; \
+    echo "y" | make install-dep; \
+    rm -rf /var/lib/apt/lists/* ; \
+    make build; \
+    make pkg-deb; \
+    rm -rf .ccache; \
+    find . -type f -name '*.o' -delete ; \
+    cd {{vpp_path}}/build-root; \
+    rm vpp-api-python_*.deb; \
+    tar czf vpp-package.tgz *.deb; \
+    mv vpp-package.tgz {{vpp_path}}/; \
+    dpkg -i *.deb ; \
+    cp {{vpp_path}}/startup.conf /etc/startup.conf
+
+WORKDIR /
+CMD vpp -c /etc/startup.conf
+
diff --git a/src/plugins/srv6-mobile/extra/Dockerfile.j2.release b/src/plugins/srv6-mobile/extra/Dockerfile.j2.release
new file mode 100644 (file)
index 0000000..851aef6
--- /dev/null
@@ -0,0 +1,37 @@
+
+FROM ubuntu:18.04
+
+RUN set -eux; \
+    apt-get update; \
+    apt-get install -y --no-install-recommends \
+    inetutils-traceroute \
+    ca-certificates \
+    libmbedcrypto1 \
+    libmbedtls10 \
+    libmbedx509-0 \
+    libnuma1 \
+    sudo \
+    iputils-ping \
+    net-tools \
+    iproute2 \
+    tcpdump \
+    python3-cffi \
+    netcat; \
+    rm -rf /var/lib/apt/lists/*; \
+    mv /usr/sbin/tcpdump /usr/bin/tcpdump
+
+WORKDIR /tmp
+
+COPY startup.conf /etc/startup.conf
+
+COPY vpp-package.tgz /tmp
+
+RUN set -eux; \
+    tar xzf vpp-package.tgz; \
+    dpkg -i *.deb ; \
+    rm -rf *.deb
+
+WORKDIR /
+CMD vpp -c /etc/startup.conf
+
diff --git a/src/plugins/srv6-mobile/extra/README.md b/src/plugins/srv6-mobile/extra/README.md
new file mode 100644 (file)
index 0000000..3b24dea
--- /dev/null
@@ -0,0 +1,173 @@
+# What's `runner.py` doing?
+
+## Common configurations
+
+### VPP1
+```
+create host-interface name eth1
+set int ip addr host-eth1 A1::1/120
+set int state host-eth1 up
+ip route add ::/0 via host-eth1 A1::2
+```
+
+
+### VPP2
+
+```
+create host-interface name eth1
+set int ip addr host-eth1 A1::2/120
+create host-interface name eth2
+set int ip addr host-eth2 A2::1/120
+set int state host-eth1 up
+set int state host-eth2 up
+ip route add ::/0 via host-eth2 A2::2
+```
+
+
+### VPP3
+
+```
+create host-interface name eth1
+set int ip addr host-eth1 A2::2/120
+create host-interface name eth2
+set int ip addr host-eth2 A3::1/120
+set int state host-eth1 up
+set int state host-eth2 up
+ip route add ::/0 via host-eth1 A2::1
+```
+
+### VPP4
+
+```
+create host-interface name eth1
+set int ip addr host-eth1 A3::2/120
+set int state host-eth1 up
+ip route add ::/0 via host-eth1 A3::1
+```
+
+
+## Drop-in for GTP-U over IPv4
+
+What's happened when you run `test tmap`:
+
+    $ ./runner.py test tmap
+
+
+Setting up a virtual interface of packet generator:
+
+#### VPP1
+
+```
+create packet-generator interface pg0
+set int mac address pg0 aa:bb:cc:dd:ee:01
+set int ip addr pg0 172.16.0.1/30
+set ip arp pg0 172.16.0.2/30 aa:bb:cc:dd:ee:02
+```
+
+#### VPP4
+
+```
+create packet-generator interface pg0
+set int mac address pg0 aa:bb:cc:dd:ee:11
+set int ip addr pg0 1.0.0.2/30
+set ip arp pg0 1.0.0.1 aa:bb:cc:dd:ee:22
+```
+
+SRv6 and IP routing settings:
+
+#### VPP1
+
+```
+sr policy add bsid D1:: next D2:: next D3:: gtp4_removal sr_prefix D4::/32 v6src_prefix C1::/64
+sr steer l3 172.20.0.1/32 via bsid D1::
+
+```
+
+#### VPP2
+
+```
+sr localsid address D2:: behavior end
+ip route add D3::/128 via host-eth2 A2::2
+```
+
+#### VPP3
+
+```
+sr localsid address D3:: behavior end
+ip route add D4::/32 via host-eth2 A3::2
+```
+
+#### VPP4
+
+```
+sr localsid prefix D4::/32 behavior end.m.gtp4.e v4src_position 64
+ip route add 172.20.0.1/32 via pg0 1.0.0.1
+```
+
+
+
+
+## Packet generator and testing
+
+    Example how to build custom SRv6 packet in scapy and ipaddress pkgs
+
+    s = '\x11' * 4 + IPv4Address(u"192.168.192.10").packed + '\x11' * 8
+    ip6 = IPv6Address(s)
+    IPv6(dst=ip6, src=ip6)
+
+
+## end.m.gtp4.e
+
+    First set behavior so our localsid node is called with the packet
+    matching C1::1 in fib table
+    sr localsid address C1::1 behavior end.m.gtp4.ess
+
+    show sr localsids behaviors
+    show sr localsid
+
+    We should send a well formated packet to C::1 destination address
+    that contains the correct spec as for end.m.gtp4.e with encapsulated
+    ipv4 src and dst address and teid with port for the conversion to
+    GTPU IPv4 packet
+
+
+## additional commands
+
+    gdb - breakpoint
+
+    break sr_policy_rewrite.c:1620
+
+    break src/plugins/srv6-end/node.c:84
+
+    TMAP
+    Linux:
+
+    ip link add tmp1 type veth peer name tmp2
+    ip link set dev tmp1 up
+    ip link set dev tmp2 up
+    ip addr add 172.20.0.2/24 dev tmp2
+
+    create host-interface name tmp1
+    set int mac address host-tmp1 02:fe:98:c6:c8:7b
+    set interface ip address host-tmp1 172.20.0.1/24
+    set interface state host-tmp1 up
+
+    VPP
+    set sr encaps source addr C1::
+    sr policy add bsid D1::999:2 next D2:: next D3:: gtp4_removal sr-prefix fc34:5678::/64 local-prefix C1::/64
+    sr steer l3 172.21.0.0/24 via bsid d1::999:2
+
+    END
+    Linux
+    create host-interface name tmp1
+    set int mac address host-tmp1 02:fe:98:c6:c8:7b
+    set interface ip address host-tmp1 A1::1/64
+    set interface state host-tmp1 up
+
+    VPP
+    sr localsid address 1111:1111:c0a8:c00a:1122:1111:1111:1111 behavior end.m.gtp4.e
+
+    trace add af-packet-input 10
+
+    sr localsid address C3:: behavior end.m.gtp4.e
+    sr localsid address 2001:200:0:1ce1:3000:757f:0:2 behavior end.m.gtp4.e
diff --git a/src/plugins/srv6-mobile/extra/requirements.txt b/src/plugins/srv6-mobile/extra/requirements.txt
new file mode 100644 (file)
index 0000000..2938cdd
--- /dev/null
@@ -0,0 +1,3 @@
+docker
+jinja2
+scapy
diff --git a/src/plugins/srv6-mobile/extra/runner.py b/src/plugins/srv6-mobile/extra/runner.py
new file mode 100755 (executable)
index 0000000..710252d
--- /dev/null
@@ -0,0 +1,2046 @@
+#!/usr/bin/env python3
+
+from os.path import dirname, realpath, split,\
+    join, isdir, exists
+from os import remove, system, mkdir
+from logging import getLogger, basicConfig,\
+    DEBUG, INFO, ERROR
+from argparse import ArgumentParser
+from atexit import register
+from shutil import rmtree
+
+from jinja2 import Environment, FileSystemLoader
+
+from docker.errors import NotFound, APIError
+from docker import from_env
+
+from scapy.contrib.gtp import *
+from scapy.all import *
+
+
+verbose_levels = {
+    'error': ERROR,
+    'debug': DEBUG,
+    'info': INFO}
+
+
+class ContainerStartupError(Exception):
+    pass
+
+
+class Container(object):
+
+    tmp = "/tmp"
+    cmd = "vppctl -s 0:5002"
+    cmd_bash = "/bin/bash"
+
+    def __init__(self, ref, name):
+        self._name = name
+        self._ref = ref
+
+    @property
+    def name(self):
+        return self._name
+
+    @property
+    def temp(self):
+        return join(self.tmp, self.name)
+
+    @property
+    def pg_input_file(self):
+        return join(self.temp, "pgi.pcap")
+
+    @property
+    def pg_output_file(self):
+        return join(self.temp, "pgo.pcap")
+
+    @property
+    def pg_input_file_in(self):
+        return join("/mnt", "pgi.pcap")
+
+    @property
+    def pg_output_file_in(self):
+        return join("/mnt", "pgo.pcap")
+
+    def disconnect_all(self):
+        status = False
+        for net in self._ref.client.networks.list():
+            try:
+                net.disconnect(self._ref)
+            except APIError:
+                continue
+            status = True
+        return status
+
+    @classmethod
+    def new(cls, client, image, name):
+
+        temp = join(cls.tmp, name)
+        if isdir(temp):
+            rmtree(temp)
+        mkdir(temp)
+
+        ref = client.containers.run(
+            detach=True,
+            remove=True,
+            auto_remove=True,
+            image=image,
+            name=name,
+            privileged=True,
+            volumes={
+                temp: {
+                    'bind': '/mnt',
+                    'mode': 'rw'}})
+
+        obj = cls.get(client, name)
+        if not obj:
+            raise ContainerStartupError()
+
+        obj.disconnect_all()
+        return obj
+
+    @classmethod
+    def get(cls, client, name):
+        try:
+            ref = client.containers.get(name)
+        except NotFound:
+            pass
+        else:
+            return cls(ref, name)
+
+    def rem(self):
+        self._ref.kill()
+
+    def vppctl(self):
+        system("docker exec -it {} {}".format(self.name, self.cmd))
+
+    def bash(self):
+        system("docker exec -it {} {}".format(self.name, self.cmd_bash))
+
+    def vppctl_exec(self, cmd):
+        ec, resp = self._ref.exec_run(cmd="{} {}".format(self.cmd, cmd))
+        assert(ec == 0)
+        return resp
+
+    def setup_host_interface(self, name, ip):
+        self.vppctl_exec("create host-interface name {}".format(name))
+        self.vppctl_exec("set int ip addr host-{} {}".format(name, ip))
+        self.vppctl_exec("set int state host-{} up".format(name))
+
+    def pg_create_interface(self, local_ip, remote_ip, local_mac, remote_mac):
+        # remote_ip can't have subnet mask
+
+        time.sleep(2)
+        self.vppctl_exec("create packet-generator interface pg0")
+        self.vppctl_exec("set int mac address pg0 {}".format(local_mac))
+        self.vppctl_exec("set int ip addr pg0 {}".format(local_ip))
+        self.vppctl_exec(
+            "set ip6 neighbor pg0 {} {}".format(remote_ip, remote_mac))
+        self.vppctl_exec("set int state pg0 up")
+
+    def pg_create_interface4(self, local_ip, remote_ip, local_mac, remote_mac):
+        # remote_ip can't have subnet mask
+
+        time.sleep(2)
+        self.vppctl_exec("create packet-generator interface pg0")
+        self.vppctl_exec("set int mac address pg0 {}".format(local_mac))
+        self.vppctl_exec("set int ip addr pg0 {}".format(local_ip))
+        self.vppctl_exec("set ip arp pg0 {} {}".format(remote_ip, remote_mac))
+        self.vppctl_exec("set int state pg0 up")
+
+    def pg_create_interface6(self, local_ip, remote_ip, local_mac, remote_mac):
+        # remote_ip can't have subnet mask
+
+        time.sleep(2)
+        self.vppctl_exec("create packet-generator interface pg0")
+        self.vppctl_exec("set int mac address pg0 {}".format(local_mac))
+        self.vppctl_exec("set int ip6 addr pg0 {}".format(local_ip))
+        self.vppctl_exec("set ip6 arp pg0 {} {}".format(remote_ip, remote_mac))
+        self.vppctl_exec("set int state pg0 up")
+
+    def pg_enable(self):
+        # start packet generator
+        self.vppctl_exec("packet-generator enable")
+
+    def pg_create_stream(self, stream):
+        wrpcap(self.pg_input_file, stream)
+        self.vppctl_exec(
+            "packet-generator new name pg-stream "
+            "node ethernet-input pcap {}".format(
+                self.pg_input_file_in))
+
+    def pg_start_capture(self):
+        if exists(self.pg_output_file):
+            remove(self.pg_output_file)
+        self.vppctl_exec(
+            "packet-generator capture pg0 pcap {}".format(
+                self.pg_output_file_in))
+
+    def pg_read_packets(self):
+        return rdpcap(self.pg_output_file)
+
+    def set_ipv6_route(self, out_if_name, next_hop_ip, subnet):
+        self.vppctl_exec(
+            "ip route add {} via host-{} {}".format(
+                subnet, out_if_name, next_hop_ip))
+
+    def set_ip_pgroute(self, out_if_name, next_hop_ip, subnet):
+        self.vppctl_exec("ip route add {} via {} {}".format(
+            subnet, out_if_name, next_hop_ip))
+
+    def set_ipv6_pgroute(self, out_if_name, next_hop_ip, subnet):
+        self.vppctl_exec("ip route add {} via {} {}".format(
+            subnet, out_if_name, next_hop_ip))
+
+    def set_ipv6_default_route(self, out_if_name, next_hop_ip):
+        self.vppctl_exec(
+            "ip route add ::/0 via host-{} {}".format(
+                out_if_name, next_hop_ip))
+
+    def enable_trace(self, count):
+        self.vppctl_exec("trace add af-packet-input {}".format(count))
+
+
+class Containers(object):
+
+    def __init__(self, client, image):
+        self.client = client
+        self.image = image
+
+    def tmp_render(self, path, template, kwargs):
+
+        with open(path, "w") as fo:
+            fo.write(template.render(**kwargs))
+
+        register(lambda: remove(path))
+
+    def build(self, path, vpp_path):
+        env = Environment(loader=FileSystemLoader(path),
+                          trim_blocks=True)
+
+        self.tmp_render(join(vpp_path, "Dockerfile"),
+                        env.get_template("Dockerfile.j2"),
+                        {'vpp_path': vpp_path})
+
+        self.tmp_render(join(vpp_path, "startup.conf"),
+                        env.get_template("startup.conf.j2"),
+                        {'vpp_path': vpp_path})
+
+        ref, _ = self.client.images.build(path=vpp_path,
+                                          tag=self.image, rm=True)
+        return ref
+
+    def release(self, path, vpp_path):
+        env = Environment(loader=FileSystemLoader(path),
+                          trim_blocks=True)
+
+        self.tmp_render(join(vpp_path, "Dockerfile"),
+                        env.get_template("Dockerfile.j2.release"),
+                        {'vpp_path': vpp_path})
+
+        self.tmp_render(join(vpp_path, "startup.conf"),
+                        env.get_template("startup.conf.j2"),
+                        {'vpp_path': vpp_path})
+
+        ref, _ = self.client.images.build(path=vpp_path,
+                                          tag="srv6m-release-image", rm=True)
+        return ref
+
+    def new(self, name):
+        return Container.new(self.client, self.image, name)
+
+    def get(self, name):
+        return Container.get(self.client, name)
+
+    def vppctl(self, name, command=None):
+        container = self.get(name)
+        if not command:
+            container.vppctl()
+        else:
+            print(container.vppctl_exec(command).decode())
+
+    def bash(self, name):
+        container = self.get(name)
+        container.bash()
+
+
+class Network(object):
+
+    def __init__(self, ref, name):
+        self._name = name
+        self._ref = ref
+
+    @property
+    def name(self):
+        return self._name
+
+    @classmethod
+    def new(cls, client, name):
+        ref = client.networks.create(name, driver="bridge",
+                                     check_duplicate=True)
+        return cls(ref, name)
+
+    @classmethod
+    def get(cls, client, name):
+        try:
+            ref = client.networks.get(name)
+        except NotFound:
+            pass
+        else:
+            return cls(ref, name)
+
+    def rem(self):
+        self._ref.remove()
+
+    def connect(self, c):
+        self._ref.connect(c.name)
+
+
+class Networks(object):
+
+    def __init__(self, client):
+        self.client = client
+
+    def new(self, name):
+        return Network.new(self.client, name)
+
+    def get(self, name):
+        return Network.get(self.client, name)
+
+
+class Program(object):
+
+    image = "srv6m-image"
+
+    name_prefix = "hck"
+
+    # TODO: add description to these instances
+    # for exmaple what the vpp is supposed to be
+    # in our topoloty overview
+
+    instance_names = ["vpp-1",
+                      "vpp-2",
+                      "vpp-3",
+                      "vpp-4"]
+
+    network_names = ["net-1",
+                     "net-2",
+                     "net-3"]
+
+    def __init__(self, image=None, prefix=None):
+        self.path = dirname(realpath(__file__))
+
+        if image:
+            self.image = image
+        if prefix is not None:
+            self.name_prefix = prefix
+
+        client = from_env()
+        self.containers = Containers(client, self.image)
+        self.networks = Networks(client)
+
+        self.logger = getLogger(__name__)
+
+    @property
+    def vpp_path(self):
+        return self.path.rsplit("/", 4)[0]
+
+    def get_name(self, name):
+        if not self.name_prefix:
+            return name
+        return "{}-{}".format(self.name_prefix, name)
+
+    def stop_containers(self):
+
+        for name in self.instance_names:
+            instance = self.containers.get(self.get_name(name))
+            if instance:
+                instance.rem()
+
+        for name in self.network_names:
+            network = self.networks.get(self.get_name(name))
+            if network:
+                network.rem()
+
+    def start_containers(self):
+
+        self.stop_containers()
+
+        networks = list()
+
+        for name in self.network_names:
+            networks.append(self.networks.new(self.get_name(name)))
+
+        n1, n2, n3 = networks
+
+        instances = list()
+
+        for name in self.instance_names:
+            instances.append(self.containers.new(self.get_name(name)))
+
+        c1, c2, c3, c4 = instances
+
+        # setup packet generator interfaces
+        # c1.pg_create_interface(local_ip="C::1/120", remote_ip="C::2",
+        # local_mac="aa:bb:cc:dd:ee:01", remote_mac="aa:bb:cc:dd:ee:02")
+
+        # setup network between instances
+        n1.connect(c1)
+        n1.connect(c2)
+
+        n2.connect(c2)
+        n2.connect(c3)
+
+        n3.connect(c3)
+        n3.connect(c4)
+
+        # c1 & c2 link
+        c1.setup_host_interface("eth1", "A1::1/120")
+        c2.setup_host_interface("eth1", "A1::2/120")
+
+        # c2 & c3 link
+        c2.setup_host_interface("eth2", "A2::1/120")
+        c3.setup_host_interface("eth1", "A2::2/120")
+
+        # c3 & c4 link
+        c3.setup_host_interface("eth2", "A3::1/120")
+        c4.setup_host_interface("eth1", "A3::2/120")
+
+        # c1 > c2 default route
+
+        c1.set_ipv6_default_route("eth1", "A1::2")
+        # c2 > c3 default route
+        c2.set_ipv6_default_route("eth2", "A2::2")
+        # c3 > c2 default route
+        c3.set_ipv6_default_route("eth1", "A2::1")
+        # c4 > c3 default route
+        c4.set_ipv6_default_route("eth1", "A3::1")
+
+        # c3 > c4 static route for address B::1/128
+        c3.set_ipv6_route("eth2", "A3::2", "B::1/128")
+        c3.set_ipv6_route("eth2", "A3::2", "B::2/128")
+
+    def test_ping(self):
+        # TESTS:
+        # trace add af-packet-input 10
+        # pg interface on c1 172.20.0.1
+        # pg interface on c4 B::1/120
+
+        self.start_containers()
+
+        c1 = self.containers.get(self.get_name(self.instance_names[0]))
+        c4 = self.containers.get(self.get_name(self.instance_names[-1]))
+
+        c1.pg_create_interface(
+            local_ip="C::1/120",
+            remote_ip="C::2",
+            local_mac="aa:bb:cc:dd:ee:01",
+            remote_mac="aa:bb:cc:dd:ee:02")
+        c4.pg_create_interface(
+            local_ip="B::1/120",
+            remote_ip="B::2",
+            local_mac="aa:bb:cc:dd:ee:11",
+            remote_mac="aa:bb:cc:dd:ee:22")
+
+        p = (Ether(src="aa:bb:cc:dd:ee:02", dst="aa:bb:cc:dd:ee:01") /
+             IPv6(src="C::2", dst="B::2") / ICMPv6EchoRequest())
+
+        print("Sending packet on {}:".format(c1.name))
+        p.show2()
+
+        c1.enable_trace(10)
+        c4.enable_trace(10)
+
+        c4.pg_start_capture()
+
+        c1.pg_create_stream(p)
+        c1.pg_enable()
+
+        # timeout (sleep) if needed
+
+        print("Receiving packet on {}:".format(c4.name))
+        for p in c4.pg_read_packets():
+            p.show2()
+
+    def test_srv6(self):
+        # TESTS:
+        # trace add af-packet-input 10
+        # pg interface on c1 C::1/120
+        # pg interface on c4 B::1/120
+
+        self.start_containers()
+
+        print("Sleeping")
+        time.sleep(30)
+
+        c1 = self.containers.get(self.get_name(self.instance_names[0]))
+        c2 = self.containers.get(self.get_name(self.instance_names[1]))
+        c3 = self.containers.get(self.get_name(self.instance_names[2]))
+        c4 = self.containers.get(self.get_name(self.instance_names[-1]))
+
+        c1.pg_create_interface(
+            local_ip="C::1/120",
+            remote_ip="C::2",
+            local_mac="aa:bb:cc:dd:ee:01",
+            remote_mac="aa:bb:cc:dd:ee:02")
+        c4.pg_create_interface(
+            local_ip="B::1/120",
+            remote_ip="B::2",
+            local_mac="aa:bb:cc:dd:ee:11",
+            remote_mac="aa:bb:cc:dd:ee:22")
+
+        c1.vppctl_exec("set sr encaps source addr D1::")
+        c1.vppctl_exec(
+            "sr policy add bsid D1::999:1 next D2:: next D3:: next D4::")
+        c1.vppctl_exec("sr steer l3 B::/120 via bsid D1::999:1")
+
+        c2.vppctl_exec("sr localsid address D2:: behavior end")
+
+        c3.vppctl_exec("sr localsid address D3:: behavior end")
+
+        c4.vppctl_exec("sr localsid address D4:: behavior end.dx6 pg0 B::2")
+
+        c2.set_ipv6_route("eth2", "A2::2", "D3::/128")
+        c2.set_ipv6_route("eth1", "A1::1", "C::/120")
+        c3.set_ipv6_route("eth2", "A3::2", "D4::/128")
+        c3.set_ipv6_route("eth1", "A2::1", "C::/120")
+
+        p = (Ether(src="aa:bb:cc:dd:ee:02", dst="aa:bb:cc:dd:ee:01") /
+             IPv6(src="C::2", dst="B::2") / ICMPv6EchoRequest())
+
+        print("Sending packet on {}:".format(c1.name))
+        p.show2()
+
+        c1.enable_trace(10)
+        c2.enable_trace(10)
+        c3.enable_trace(10)
+        c4.enable_trace(10)
+
+        c4.pg_start_capture()
+
+        c1.pg_create_stream(p)
+        c1.pg_enable()
+
+        # timeout (sleep) if needed
+
+        print("Receiving packet on {}:".format(c4.name))
+        for p in c4.pg_read_packets():
+            p.show2()
+
+    def test_tmap(self):
+        # TESTS:
+        # trace add af-packet-input 10
+        # pg interface on c1 172.20.0.1
+        # pg interface on c4 B::1/120
+
+        self.start_containers()
+
+        c1 = self.containers.get(self.get_name(self.instance_names[0]))
+        c2 = self.containers.get(self.get_name(self.instance_names[1]))
+        c3 = self.containers.get(self.get_name(self.instance_names[2]))
+        c4 = self.containers.get(self.get_name(self.instance_names[-1]))
+
+        c1.pg_create_interface4(
+            local_ip="172.16.0.1/30",
+            remote_ip="172.16.0.2/30",
+            local_mac="aa:bb:cc:dd:ee:01",
+            remote_mac="aa:bb:cc:dd:ee:02")
+        c4.pg_create_interface4(
+            local_ip="1.0.0.2/30",
+            remote_ip="1.0.0.1",
+            local_mac="aa:bb:cc:dd:ee:11",
+            remote_mac="aa:bb:cc:dd:ee:22")
+
+        c1.vppctl_exec("set sr encaps source addr A1::1")
+        c1.vppctl_exec(
+            "sr policy add bsid D1:: next D2:: next D3:: "
+            "gtp4_removal sr_prefix D4::/32 v6src_prefix C1::/64")
+        c1.vppctl_exec("sr steer l3 172.20.0.1/32 via bsid D1::")
+
+        c2.vppctl_exec("sr localsid address D2:: behavior end")
+
+        c3.vppctl_exec("sr localsid address D3:: behavior end")
+
+        c4.vppctl_exec(
+            "sr localsid prefix D4::/32 "
+            "behavior end.m.gtp4.e v4src_position 64")
+
+        c2.set_ipv6_route("eth2", "A2::2", "D3::/128")
+        c2.set_ipv6_route("eth1", "A1::1", "C::/120")
+        c3.set_ipv6_route("eth2", "A3::2", "D4::/32")
+        c3.set_ipv6_route("eth1", "A2::1", "C::/120")
+        c4.set_ip_pgroute("pg0", "1.0.0.1", "172.20.0.1/32")
+
+        p = (Ether(src="aa:bb:cc:dd:ee:02", dst="aa:bb:cc:dd:ee:01") /
+             IP(src="172.20.0.2", dst="172.20.0.1") /
+             UDP(sport=2152, dport=2152) /
+             GTP_U_Header(gtp_type="g_pdu", teid=200) /
+             IP(src="172.99.0.1", dst="172.99.0.2") /
+             ICMP())
+
+        print("Sending packet on {}:".format(c1.name))
+        p.show2()
+
+        c1.enable_trace(10)
+        c4.enable_trace(10)
+
+        c4.pg_start_capture()
+
+        c1.pg_create_stream(p)
+        c1.pg_enable()
+
+        # timeout (sleep) if needed
+        print("Sleeping")
+        time.sleep(5)
+
+        print("Receiving packet on {}:".format(c4.name))
+        for p in c4.pg_read_packets():
+            p.show2()
+
+    def test_tmap_5g(self):
+        # TESTS:
+        # trace add af-packet-input 10
+        # pg interface on c1 172.20.0.1
+        # pg interface on c4 B::1/120
+
+        self.start_containers()
+
+        c1 = self.containers.get(self.get_name(self.instance_names[0]))
+        c2 = self.containers.get(self.get_name(self.instance_names[1]))
+        c3 = self.containers.get(self.get_name(self.instance_names[2]))
+        c4 = self.containers.get(self.get_name(self.instance_names[-1]))
+
+        c1.pg_create_interface4(
+            local_ip="172.16.0.1/30",
+            remote_ip="172.16.0.2/30",
+            local_mac="aa:bb:cc:dd:ee:01",
+            remote_mac="aa:bb:cc:dd:ee:02")
+        c4.pg_create_interface4(
+            local_ip="1.0.0.2/30",
+            remote_ip="1.0.0.1",
+            local_mac="aa:bb:cc:dd:ee:11",
+            remote_mac="aa:bb:cc:dd:ee:22")
+
+        c1.vppctl_exec("set sr encaps source addr A1::1")
+        c1.vppctl_exec(
+            "sr policy add bsid D1:: next D2:: next D3:: "
+            "gtp4_removal sr_prefix D4::/32 v6src_prefix C1::/64")
+        c1.vppctl_exec("sr steer l3 172.20.0.1/32 via bsid D1::")
+
+        c2.vppctl_exec("sr localsid address D2:: behavior end")
+
+        c3.vppctl_exec("sr localsid address D3:: behavior end")
+
+        c4.vppctl_exec(
+            "sr localsid prefix D4::/32 "
+            "behavior end.m.gtp4.e v4src_position 64")
+
+        c2.set_ipv6_route("eth2", "A2::2", "D3::/128")
+        c2.set_ipv6_route("eth1", "A1::1", "C::/120")
+        c3.set_ipv6_route("eth2", "A3::2", "D4::/32")
+        c3.set_ipv6_route("eth1", "A2::1", "C::/120")
+        c4.set_ip_pgroute("pg0", "1.0.0.1", "172.20.0.1/32")
+
+        p = (Ether(src="aa:bb:cc:dd:ee:02", dst="aa:bb:cc:dd:ee:01") /
+             IP(src="172.20.0.2", dst="172.20.0.1") /
+             UDP(sport=2152, dport=2152) /
+             GTP_U_Header(gtp_type="g_pdu", teid=200) /
+             GTPPDUSessionContainer(R=1, QFI=3) /
+             IP(src="172.99.0.1", dst="172.99.0.2") /
+             ICMP())
+
+        print("Sending packet on {}:".format(c1.name))
+        p.show2()
+
+        c1.enable_trace(10)
+        c4.enable_trace(10)
+
+        c4.pg_start_capture()
+
+        c1.pg_create_stream(p)
+        c1.pg_enable()
+
+        # timeout (sleep) if needed
+        print("Sleeping")
+        time.sleep(5)
+
+        print("Receiving packet on {}:".format(c4.name))
+        for p in c4.pg_read_packets():
+            p.show2()
+
+    def test_tmap_ipv6(self):
+        # TESTS:
+        # trace add af-packet-input 10
+        # pg interface on c1 172.20.0.1
+        # pg interface on c4 B::1/120
+
+        self.start_containers()
+
+        c1 = self.containers.get(self.get_name(self.instance_names[0]))
+        c2 = self.containers.get(self.get_name(self.instance_names[1]))
+        c3 = self.containers.get(self.get_name(self.instance_names[2]))
+        c4 = self.containers.get(self.get_name(self.instance_names[-1]))
+
+        c1.pg_create_interface4(
+            local_ip="172.16.0.1/30",
+            remote_ip="172.16.0.2/30",
+            local_mac="aa:bb:cc:dd:ee:01",
+            remote_mac="aa:bb:cc:dd:ee:02")
+        c4.pg_create_interface4(
+            local_ip="1.0.0.2/30",
+            remote_ip="1.0.0.1",
+            local_mac="aa:bb:cc:dd:ee:11",
+            remote_mac="aa:bb:cc:dd:ee:22")
+
+        c1.vppctl_exec("set sr encaps source addr A1::1")
+        c1.vppctl_exec(
+            "sr policy add bsid D1:: next D2:: next D3:: "
+            "gtp4_removal sr_prefix D4::/32 v6src_prefix C1::/64")
+        c1.vppctl_exec("sr steer l3 172.20.0.1/32 via bsid D1::")
+
+        c2.vppctl_exec("sr localsid address D2:: behavior end")
+
+        c3.vppctl_exec("sr localsid address D3:: behavior end")
+
+        c4.vppctl_exec(
+            "sr localsid prefix D4::/32 "
+            "behavior end.m.gtp4.e v4src_position 64")
+
+        c2.set_ipv6_route("eth2", "A2::2", "D3::/128")
+        c2.set_ipv6_route("eth1", "A1::1", "C::/120")
+        c3.set_ipv6_route("eth2", "A3::2", "D4::/32")
+        c3.set_ipv6_route("eth1", "A2::1", "C::/120")
+        c4.set_ip_pgroute("pg0", "1.0.0.1", "172.20.0.1/32")
+
+        p = (Ether(src="aa:bb:cc:dd:ee:02", dst="aa:bb:cc:dd:ee:01") /
+             IP(src="172.20.0.2", dst="172.20.0.1") /
+             UDP(sport=2152, dport=2152) /
+             GTP_U_Header(gtp_type="g_pdu", teid=200) /
+             IPv6(src="2001::1", dst="2002::1") /
+             ICMPv6EchoRequest())
+
+        print("Sending packet on {}:".format(c1.name))
+        p.show2()
+
+        c1.enable_trace(10)
+        c4.enable_trace(10)
+
+        c4.pg_start_capture()
+
+        c1.pg_create_stream(p)
+        c1.pg_enable()
+
+        # timeout (sleep) if needed
+        print("Sleeping")
+        time.sleep(5)
+
+        print("Receiving packet on {}:".format(c4.name))
+        for p in c4.pg_read_packets():
+            p.show2()
+
+    def test_tmap_ipv6_5g(self):
+        # TESTS:
+        # trace add af-packet-input 10
+        # pg interface on c1 172.20.0.1
+        # pg interface on c4 B::1/120
+
+        self.start_containers()
+
+        c1 = self.containers.get(self.get_name(self.instance_names[0]))
+        c2 = self.containers.get(self.get_name(self.instance_names[1]))
+        c3 = self.containers.get(self.get_name(self.instance_names[2]))
+        c4 = self.containers.get(self.get_name(self.instance_names[-1]))
+
+        c1.pg_create_interface4(
+            local_ip="172.16.0.1/30",
+            remote_ip="172.16.0.2/30",
+            local_mac="aa:bb:cc:dd:ee:01",
+            remote_mac="aa:bb:cc:dd:ee:02")
+        c4.pg_create_interface4(
+            local_ip="1.0.0.2/30",
+            remote_ip="1.0.0.1",
+            local_mac="aa:bb:cc:dd:ee:11",
+            remote_mac="aa:bb:cc:dd:ee:22")
+
+        c1.vppctl_exec("set sr encaps source addr A1::1")
+        c1.vppctl_exec(
+            "sr policy add bsid D1:: next D2:: next D3:: "
+            "gtp4_removal sr_prefix D4::/32 v6src_prefix C1::/64")
+        c1.vppctl_exec("sr steer l3 172.20.0.1/32 via bsid D1::")
+
+        c2.vppctl_exec("sr localsid address D2:: behavior end")
+
+        c3.vppctl_exec("sr localsid address D3:: behavior end")
+
+        c4.vppctl_exec(
+            "sr localsid prefix D4::/32 "
+            "behavior end.m.gtp4.e v4src_position 64")
+
+        c2.set_ipv6_route("eth2", "A2::2", "D3::/128")
+        c2.set_ipv6_route("eth1", "A1::1", "C::/120")
+        c3.set_ipv6_route("eth2", "A3::2", "D4::/32")
+        c3.set_ipv6_route("eth1", "A2::1", "C::/120")
+        c4.set_ip_pgroute("pg0", "1.0.0.1", "172.20.0.1/32")
+
+        p = (Ether(src="aa:bb:cc:dd:ee:02", dst="aa:bb:cc:dd:ee:01") /
+             IP(src="172.20.0.2", dst="172.20.0.1") /
+             UDP(sport=2152, dport=2152) /
+             GTP_U_Header(gtp_type="g_pdu", teid=200) /
+             GTPPDUSessionContainer(R=1, QFI=3) /
+             IPv6(src="2001::1", dst="2002::1") /
+             ICMPv6EchoRequest())
+
+        print("Sending packet on {}:".format(c1.name))
+        print("Sending packet on {}:".format(c1.name))
+        p.show2()
+
+        c1.enable_trace(10)
+        c4.enable_trace(10)
+
+        c4.pg_start_capture()
+
+        c1.pg_create_stream(p)
+        c1.pg_enable()
+
+        # timeout (sleep) if needed
+        print("Sleeping")
+        time.sleep(5)
+
+        print("Receiving packet on {}:".format(c4.name))
+        for p in c4.pg_read_packets():
+            p.show2()
+
+    def test_gtp4(self):
+        # TESTS:
+        # trace add af-packet-input 10
+        # pg interface on c1 172.20.0.1
+        # pg interface on c4 B::1/120
+
+        self.start_containers()
+
+        c1 = self.containers.get(self.get_name(self.instance_names[0]))
+        c2 = self.containers.get(self.get_name(self.instance_names[1]))
+        c3 = self.containers.get(self.get_name(self.instance_names[2]))
+        c4 = self.containers.get(self.get_name(self.instance_names[-1]))
+
+        c1.pg_create_interface4(
+            local_ip="172.16.0.1/30",
+            remote_ip="172.16.0.2/30",
+            local_mac="aa:bb:cc:dd:ee:01",
+            remote_mac="aa:bb:cc:dd:ee:02")
+        c4.pg_create_interface4(
+            local_ip="1.0.0.2/30",
+            remote_ip="1.0.0.1",
+            local_mac="aa:bb:cc:dd:ee:11",
+            remote_mac="aa:bb:cc:dd:ee:22")
+
+        c1.vppctl_exec("set sr encaps source addr A1::1")
+        c1.vppctl_exec("sr policy add bsid D4:: next D2:: next D3::")
+        c1.vppctl_exec(
+            "sr localsid prefix 172.20.0.1/32 "
+            "behavior end.m.gtp4.d D4::/32 v6src_prefix C1::/64 nhtype ipv4")
+
+        c2.vppctl_exec("sr localsid address D2:: behavior end")
+
+        c3.vppctl_exec("sr localsid address D3:: behavior end")
+
+        c4.vppctl_exec(
+            "sr localsid prefix D4::/32 "
+            "behavior end.m.gtp4.e v4src_position 64")
+
+        c2.set_ipv6_route("eth2", "A2::2", "D3::/128")
+        c2.set_ipv6_route("eth1", "A1::1", "C::/120")
+        c3.set_ipv6_route("eth2", "A3::2", "D4::/32")
+        c3.set_ipv6_route("eth1", "A2::1", "C::/120")
+        c4.set_ip_pgroute("pg0", "1.0.0.1", "172.20.0.1/32")
+
+        p = (Ether(src="aa:bb:cc:dd:ee:02", dst="aa:bb:cc:dd:ee:01") /
+             IP(src="172.20.0.2", dst="172.20.0.1") /
+             UDP(sport=2152, dport=2152) /
+             GTP_U_Header(gtp_type="g_pdu", teid=200) /
+             IP(src="172.99.0.1", dst="172.99.0.2") /
+             ICMP())
+
+        print("Sending packet on {}:".format(c1.name))
+        p.show2()
+
+        c1.enable_trace(10)
+        c4.enable_trace(10)
+
+        c4.pg_start_capture()
+
+        c1.pg_create_stream(p)
+        c1.pg_enable()
+
+        # timeout (sleep) if needed
+        print("Sleeping")
+        time.sleep(5)
+
+        print("Receiving packet on {}:".format(c4.name))
+        for p in c4.pg_read_packets():
+            p.show2()
+
+    def test_gtp4_5g(self):
+        # TESTS:
+        # trace add af-packet-input 10
+        # pg interface on c1 172.20.0.1
+        # pg interface on c4 B::1/120
+
+        self.start_containers()
+
+        c1 = self.containers.get(self.get_name(self.instance_names[0]))
+        c2 = self.containers.get(self.get_name(self.instance_names[1]))
+        c3 = self.containers.get(self.get_name(self.instance_names[2]))
+        c4 = self.containers.get(self.get_name(self.instance_names[-1]))
+
+        c1.pg_create_interface4(
+            local_ip="172.16.0.1/30",
+            remote_ip="172.16.0.2/30",
+            local_mac="aa:bb:cc:dd:ee:01",
+            remote_mac="aa:bb:cc:dd:ee:02")
+        c4.pg_create_interface4(
+            local_ip="1.0.0.2/30",
+            remote_ip="1.0.0.1",
+            local_mac="aa:bb:cc:dd:ee:11",
+            remote_mac="aa:bb:cc:dd:ee:22")
+
+        c1.vppctl_exec("set sr encaps source addr A1::1")
+        c1.vppctl_exec("sr policy add bsid D4:: next D2:: next D3::")
+        c1.vppctl_exec(
+            "sr localsid prefix ::ffff:ac14:0001/128 "
+            "behavior end.m.gtp4.d D4::/32 v6src_prefix C1::/64 "
+            "nhtype ipv4")
+
+        c2.vppctl_exec("sr localsid address D2:: behavior end")
+
+        c3.vppctl_exec("sr localsid address D3:: behavior end")
+
+        c4.vppctl_exec(
+            "sr localsid prefix D4::/32 "
+            "behavior end.m.gtp4.e v4src_position 64")
+
+        c2.set_ipv6_route("eth2", "A2::2", "D3::/128")
+        c2.set_ipv6_route("eth1", "A1::1", "C::/120")
+        c3.set_ipv6_route("eth2", "A3::2", "D4::/32")
+        c3.set_ipv6_route("eth1", "A2::1", "C::/120")
+        c4.set_ip_pgroute("pg0", "1.0.0.1", "172.20.0.1/32")
+
+        p = (Ether(src="aa:bb:cc:dd:ee:02", dst="aa:bb:cc:dd:ee:01") /
+             IP(src="172.20.0.2", dst="172.20.0.1") /
+             UDP(sport=2152, dport=2152) /
+             GTP_U_Header(gtp_type="g_pdu", teid=200) /
+             GTPPDUSessionContainer(type=1, R=1, QFI=3) /
+             IP(src="172.99.0.1", dst="172.99.0.2") /
+             ICMP())
+
+        print("Sending packet on {}:".format(c1.name))
+        p.show2()
+
+        c1.enable_trace(10)
+        c4.enable_trace(10)
+
+        c4.pg_start_capture()
+
+        c1.pg_create_stream(p)
+        c1.pg_enable()
+
+        # timeout (sleep) if needed
+        print("Sleeping")
+        time.sleep(5)
+
+        print("Receiving packet on {}:".format(c4.name))
+        for p in c4.pg_read_packets():
+            p.show2()
+
+    def test_gtp4_echo(self):
+        # TESTS:
+        # trace add af-packet-input 10
+        # pg interface on c1 172.20.0.1
+        # pg interface on c4 B::1/120
+
+        self.start_containers()
+
+        c1 = self.containers.get(self.get_name(self.instance_names[0]))
+        c2 = self.containers.get(self.get_name(self.instance_names[1]))
+        c3 = self.containers.get(self.get_name(self.instance_names[2]))
+        c4 = self.containers.get(self.get_name(self.instance_names[-1]))
+
+        c1.pg_create_interface4(
+            local_ip="172.16.0.1/30",
+            remote_ip="172.16.0.2/30",
+            local_mac="aa:bb:cc:dd:ee:01",
+            remote_mac="aa:bb:cc:dd:ee:02")
+        c4.pg_create_interface4(
+            local_ip="1.0.0.2/30",
+            remote_ip="1.0.0.1",
+            local_mac="aa:bb:cc:dd:ee:11",
+            remote_mac="aa:bb:cc:dd:ee:22")
+
+        c1.vppctl_exec("set sr encaps source addr A1::1")
+        c1.vppctl_exec("sr policy add bsid D4:: next D2:: next D3::")
+        c1.vppctl_exec(
+            "sr localsid prefix 172.20.0.1/32 "
+            "behavior end.m.gtp4.d D4::/32 v6src_prefix C1::/64 "
+            "nhtype ipv4")
+
+        c2.vppctl_exec("sr localsid address D2:: behavior end")
+
+        c3.vppctl_exec("sr localsid address D3:: behavior end")
+
+        c4.vppctl_exec(
+            "sr localsid prefix D4::/32 "
+            "behavior end.m.gtp4.e v4src_position 64")
+
+        c2.set_ipv6_route("eth2", "A2::2", "D3::/128")
+        c2.set_ipv6_route("eth1", "A1::1", "C::/120")
+        c3.set_ipv6_route("eth2", "A3::2", "D4::/32")
+        c3.set_ipv6_route("eth1", "A2::1", "C::/120")
+        c4.set_ip_pgroute("pg0", "1.0.0.1", "172.20.0.1/32")
+
+        p = (Ether(src="aa:bb:cc:dd:ee:02", dst="aa:bb:cc:dd:ee:01") /
+             IP(src="172.20.0.2", dst="172.20.0.1") /
+             UDP(sport=2152, dport=2152) /
+             GTP_U_Header(gtp_type="echo_request", teid=200))
+
+        print("Sending packet on {}:".format(c1.name))
+        p.show2()
+
+        c1.enable_trace(10)
+        c4.enable_trace(10)
+
+        c4.pg_start_capture()
+
+        c1.pg_create_stream(p)
+        c1.pg_enable()
+
+        # timeout (sleep) if needed
+        print("Sleeping")
+        time.sleep(5)
+
+        print("Receiving packet on {}:".format(c4.name))
+        for p in c4.pg_read_packets():
+            p.show2()
+
+    def test_gtp4_ipv6(self):
+        # TESTS:
+        # trace add af-packet-input 10
+        # pg interface on c1 172.20.0.1
+        # pg interface on c4 B::1/120
+
+        self.start_containers()
+
+        c1 = self.containers.get(self.get_name(self.instance_names[0]))
+        c2 = self.containers.get(self.get_name(self.instance_names[1]))
+        c3 = self.containers.get(self.get_name(self.instance_names[2]))
+        c4 = self.containers.get(self.get_name(self.instance_names[-1]))
+
+        c1.pg_create_interface4(
+            local_ip="172.16.0.1/30",
+            remote_ip="172.16.0.2/30",
+            local_mac="aa:bb:cc:dd:ee:01",
+            remote_mac="aa:bb:cc:dd:ee:02")
+        c4.pg_create_interface4(
+            local_ip="1.0.0.2/30",
+            remote_ip="1.0.0.1",
+            local_mac="aa:bb:cc:dd:ee:11",
+            remote_mac="aa:bb:cc:dd:ee:22")
+
+        c1.vppctl_exec("set sr encaps source addr A1::1")
+        c1.vppctl_exec("sr policy add bsid D4:: next D2:: next D3::")
+        c1.vppctl_exec(
+            "sr localsid prefix ::ffff:ac14:0001/128 "
+            "behavior end.m.gtp4.d D4::/32 v6src_prefix C1::/64")
+
+        c2.vppctl_exec("sr localsid address D2:: behavior end")
+
+        c3.vppctl_exec("sr localsid address D3:: behavior end")
+
+        c4.vppctl_exec(
+            "sr localsid prefix D4::/32 "
+            "behavior end.m.gtp4.e v4src_position 64")
+
+        c2.set_ipv6_route("eth2", "A2::2", "D3::/128")
+        c2.set_ipv6_route("eth1", "A1::1", "C::/120")
+        c3.set_ipv6_route("eth2", "A3::2", "D4::/32")
+        c3.set_ipv6_route("eth1", "A2::1", "C::/120")
+        c4.set_ip_pgroute("pg0", "1.0.0.1", "172.20.0.1/32")
+
+        p = (Ether(src="aa:bb:cc:dd:ee:02", dst="aa:bb:cc:dd:ee:01") /
+             IP(src="172.20.0.2", dst="172.20.0.1") /
+             UDP(sport=2152, dport=2152) /
+             GTP_U_Header(gtp_type="g_pdu", teid=200) /
+             IPv6(src="2001::1", dst="2002::1") /
+             ICMPv6EchoRequest())
+
+        print("Sending packet on {}:".format(c1.name))
+        p.show2()
+
+        c1.enable_trace(10)
+        c4.enable_trace(10)
+
+        c4.pg_start_capture()
+
+        c1.pg_create_stream(p)
+        c1.pg_enable()
+
+        # timeout (sleep) if needed
+        print("Sleeping")
+        time.sleep(5)
+
+        print("Receiving packet on {}:".format(c4.name))
+        for p in c4.pg_read_packets():
+            p.show2()
+
+    def test_gtp4_ipv6_5g(self):
+        # TESTS:
+        # trace add af-packet-input 10
+        # pg interface on c1 172.20.0.1
+        # pg interface on c4 B::1/120
+
+        self.start_containers()
+
+        c1 = self.containers.get(self.get_name(self.instance_names[0]))
+        c2 = self.containers.get(self.get_name(self.instance_names[1]))
+        c3 = self.containers.get(self.get_name(self.instance_names[2]))
+        c4 = self.containers.get(self.get_name(self.instance_names[-1]))
+
+        c1.pg_create_interface4(
+            local_ip="172.16.0.1/30",
+            remote_ip="172.16.0.2/30",
+            local_mac="aa:bb:cc:dd:ee:01",
+            remote_mac="aa:bb:cc:dd:ee:02")
+        c4.pg_create_interface4(
+            local_ip="1.0.0.2/30",
+            remote_ip="1.0.0.1",
+            local_mac="aa:bb:cc:dd:ee:11",
+            remote_mac="aa:bb:cc:dd:ee:22")
+
+        c1.vppctl_exec("set sr encaps source addr A1::1")
+        c1.vppctl_exec("sr policy add bsid D4:: next D2:: next D3::")
+        c1.vppctl_exec(
+            "sr localsid prefix ::ffff:ac14:0001/128 "
+            "behavior end.m.gtp4.d D4::/32 v6src_prefix C1::/64")
+
+        c2.vppctl_exec("sr localsid address D2:: behavior end")
+
+        c3.vppctl_exec("sr localsid address D3:: behavior end")
+
+        c4.vppctl_exec(
+            "sr localsid prefix D4::/32 "
+            "behavior end.m.gtp4.e v4src_position 64")
+
+        c2.set_ipv6_route("eth2", "A2::2", "D3::/128")
+        c2.set_ipv6_route("eth1", "A1::1", "C::/120")
+        c3.set_ipv6_route("eth2", "A3::2", "D4::/32")
+        c3.set_ipv6_route("eth1", "A2::1", "C::/120")
+        c4.set_ip_pgroute("pg0", "1.0.0.1", "172.20.0.1/32")
+
+        p = (Ether(src="aa:bb:cc:dd:ee:02", dst="aa:bb:cc:dd:ee:01") /
+             IP(src="172.20.0.2", dst="172.20.0.1") /
+             UDP(sport=2152, dport=2152) /
+             GTP_U_Header(gtp_type="g_pdu", teid=200) /
+             GTPPDUSessionContainer(R=1, QFI=3) /
+             IPv6(src="2001::1", dst="2002::1") /
+             ICMPv6EchoRequest())
+
+        print("Sending packet on {}:".format(c1.name))
+        p.show2()
+
+        c1.enable_trace(10)
+        c4.enable_trace(10)
+
+        c4.pg_start_capture()
+
+        c1.pg_create_stream(p)
+        c1.pg_enable()
+
+        # timeout (sleep) if needed
+        print("Sleeping")
+        time.sleep(5)
+
+        print("Receiving packet on {}:".format(c4.name))
+        for p in c4.pg_read_packets():
+            p.show2()
+
+    def test_gtp6_drop_in(self):
+        # TESTS:
+        # trace add af-packet-input 10
+        # pg interface on c1 172.20.0.1
+        # pg interface on c4 B::1/120
+
+        self.start_containers()
+
+        print("Deleting the old containers...")
+        time.sleep(30)
+        print("Starting the new containers...")
+
+        c1 = self.containers.get(self.get_name(self.instance_names[0]))
+        c2 = self.containers.get(self.get_name(self.instance_names[1]))
+        c3 = self.containers.get(self.get_name(self.instance_names[2]))
+        c4 = self.containers.get(self.get_name(self.instance_names[-1]))
+
+        c1.pg_create_interface(
+            local_ip="C::1/120",
+            remote_ip="C::2",
+            local_mac="aa:bb:cc:dd:ee:01",
+            remote_mac="aa:bb:cc:dd:ee:02")
+        c4.pg_create_interface(
+            local_ip="B::1/120",
+            remote_ip="B::2",
+            local_mac="aa:bb:cc:dd:ee:11",
+            remote_mac="aa:bb:cc:dd:ee:22")
+
+        c1.vppctl_exec("set sr encaps source addr A1::1")
+        c1.vppctl_exec("sr policy add bsid D4:: next D2:: next D3::")
+
+        c1.vppctl_exec(
+            "sr localsid prefix D::/64 behavior end.m.gtp6.d.di D4::/64")
+
+        c2.vppctl_exec("sr localsid address D2:: behavior end")
+
+        c3.vppctl_exec("sr localsid address D3:: behavior end")
+
+        c4.vppctl_exec("sr localsid prefix D4::/64 behavior end.m.gtp6.e")
+
+        c2.set_ipv6_route("eth2", "A2::2", "D3::/128")
+        c2.set_ipv6_route("eth1", "A1::1", "C::/120")
+        c3.set_ipv6_route("eth2", "A3::2", "D4::/32")
+        c3.set_ipv6_route("eth1", "A2::1", "C::/120")
+        c4.set_ip_pgroute("pg0", "B::2", "D::2/128")
+
+        print("Waiting...")
+        time.sleep(30)
+
+        p = (Ether(src="aa:bb:cc:dd:ee:02", dst="aa:bb:cc:dd:ee:01") /
+             IPv6(src="C::2", dst="D::2") /
+             UDP(sport=2152, dport=2152) /
+             GTP_U_Header(gtp_type="g_pdu", teid=200) /
+             IP(src="172.99.0.1", dst="172.99.0.2") /
+             ICMP())
+
+        print("Sending packet on {}:".format(c1.name))
+        p.show2()
+
+        c1.enable_trace(10)
+        c4.enable_trace(10)
+
+        c4.pg_start_capture()
+
+        c1.pg_create_stream(p)
+        c1.pg_enable()
+
+        # timeout (sleep) if needed
+        print("Sleeping")
+        time.sleep(5)
+
+        print("Receiving packet on {}:".format(c4.name))
+        for p in c4.pg_read_packets():
+            p.show2()
+
+    def test_gtp6_drop_in_5g(self):
+        # TESTS:
+        # trace add af-packet-input 10
+        # pg interface on c1 172.20.0.1
+        # pg interface on c4 B::1/120
+
+        self.start_containers()
+
+        print("Deleting the old containers...")
+        time.sleep(30)
+        print("Starting the new containers...")
+
+        c1 = self.containers.get(self.get_name(self.instance_names[0]))
+        c2 = self.containers.get(self.get_name(self.instance_names[1]))
+        c3 = self.containers.get(self.get_name(self.instance_names[2]))
+        c4 = self.containers.get(self.get_name(self.instance_names[-1]))
+
+        c1.pg_create_interface(
+            local_ip="C::1/120",
+            remote_ip="C::2",
+            local_mac="aa:bb:cc:dd:ee:01",
+            remote_mac="aa:bb:cc:dd:ee:02")
+        c4.pg_create_interface(
+            local_ip="B::1/120",
+            remote_ip="B::2",
+            local_mac="aa:bb:cc:dd:ee:11",
+            remote_mac="aa:bb:cc:dd:ee:22")
+
+        c1.vppctl_exec("set sr encaps source addr A1::1")
+        c1.vppctl_exec("sr policy add bsid D4:: next D2:: next D3::")
+
+        c1.vppctl_exec(
+            "sr localsid prefix D::/64 behavior end.m.gtp6.d.di D4::/64")
+
+        c2.vppctl_exec("sr localsid address D2:: behavior end")
+
+        c3.vppctl_exec("sr localsid address D3:: behavior end")
+
+        c4.vppctl_exec("sr localsid prefix D4::/64 behavior end.m.gtp6.e")
+
+        c2.set_ipv6_route("eth2", "A2::2", "D3::/128")
+        c2.set_ipv6_route("eth1", "A1::1", "C::/120")
+        c3.set_ipv6_route("eth2", "A3::2", "D4::/32")
+        c3.set_ipv6_route("eth1", "A2::1", "C::/120")
+        c4.set_ip_pgroute("pg0", "B::2", "D::2/128")
+
+        print("Waiting...")
+        time.sleep(30)
+
+        p = (Ether(src="aa:bb:cc:dd:ee:02", dst="aa:bb:cc:dd:ee:01") /
+             IPv6(src="C::2", dst="D::2") /
+             UDP(sport=2152, dport=2152) /
+             GTP_U_Header(gtp_type="g_pdu", teid=200) /
+             GTPPDUSessionContainer(type=1, R=1, QFI=3) /
+             IP(src="172.99.0.1", dst="172.99.0.2") /
+             ICMP())
+
+        print("Sending packet on {}:".format(c1.name))
+        p.show2()
+
+        c1.enable_trace(10)
+        c4.enable_trace(10)
+
+        c4.pg_start_capture()
+
+        c1.pg_create_stream(p)
+        c1.pg_enable()
+
+        # timeout (sleep) if needed
+        print("Sleeping")
+        time.sleep(5)
+
+        print("Receiving packet on {}:".format(c4.name))
+        for p in c4.pg_read_packets():
+            p.show2()
+
+    def test_gtp6_drop_in_echo(self):
+        # TESTS:
+        # trace add af-packet-input 10
+        # pg interface on c1 172.20.0.1
+        # pg interface on c4 B::1/120
+
+        self.start_containers()
+
+        print("Deleting the old containers...")
+        time.sleep(30)
+        print("Starting the new containers...")
+
+        c1 = self.containers.get(self.get_name(self.instance_names[0]))
+        c2 = self.containers.get(self.get_name(self.instance_names[1]))
+        c3 = self.containers.get(self.get_name(self.instance_names[2]))
+        c4 = self.containers.get(self.get_name(self.instance_names[-1]))
+
+        c1.pg_create_interface(
+            local_ip="C::1/120",
+            remote_ip="C::2",
+            local_mac="aa:bb:cc:dd:ee:01",
+            remote_mac="aa:bb:cc:dd:ee:02")
+        c4.pg_create_interface(
+            local_ip="B::1/120",
+            remote_ip="B::2",
+            local_mac="aa:bb:cc:dd:ee:11",
+            remote_mac="aa:bb:cc:dd:ee:22")
+
+        c1.vppctl_exec("set sr encaps source addr A1::1")
+        c1.vppctl_exec("sr policy add bsid D4:: next D2:: next D3::")
+
+        c1.vppctl_exec(
+            "sr localsid prefix D::/64 behavior end.m.gtp6.d.di D4::/64")
+
+        c2.vppctl_exec("sr localsid address D2:: behavior end")
+
+        c3.vppctl_exec("sr localsid address D3:: behavior end")
+
+        c4.vppctl_exec("sr localsid prefix D4::/64 behavior end.m.gtp6.e")
+
+        c2.set_ipv6_route("eth2", "A2::2", "D3::/128")
+        c2.set_ipv6_route("eth1", "A1::1", "C::/120")
+        c3.set_ipv6_route("eth2", "A3::2", "D4::/32")
+        c3.set_ipv6_route("eth1", "A2::1", "C::/120")
+        c4.set_ip_pgroute("pg0", "B::2", "D::2/128")
+
+        print("Waiting...")
+        time.sleep(30)
+
+        p = (Ether(src="aa:bb:cc:dd:ee:02", dst="aa:bb:cc:dd:ee:01") /
+             IPv6(src="C::2", dst="D::2") /
+             UDP(sport=2152, dport=2152) /
+             GTP_U_Header(gtp_type="echo_request", teid=200))
+
+        print("Sending packet on {}:".format(c1.name))
+        p.show2()
+
+        c1.enable_trace(10)
+        c4.enable_trace(10)
+
+        c4.pg_start_capture()
+
+        c1.pg_create_stream(p)
+        c1.pg_enable()
+
+        # timeout (sleep) if needed
+        print("Sleeping")
+        time.sleep(5)
+
+        print("Receiving packet on {}:".format(c4.name))
+        for p in c4.pg_read_packets():
+            p.show2()
+
+    def test_gtp6_drop_in_ipv6(self):
+        # TESTS:
+        # trace add af-packet-input 10
+        # pg interface on c1 172.20.0.1
+        # pg interface on c4 B::1/120
+
+        self.start_containers()
+
+        print("Deleting the old containers...")
+        time.sleep(30)
+        print("Starting the new containers...")
+
+        c1 = self.containers.get(self.get_name(self.instance_names[0]))
+        c2 = self.containers.get(self.get_name(self.instance_names[1]))
+        c3 = self.containers.get(self.get_name(self.instance_names[2]))
+        c4 = self.containers.get(self.get_name(self.instance_names[-1]))
+
+        c1.pg_create_interface(
+            local_ip="C::1/120",
+            remote_ip="C::2",
+            local_mac="aa:bb:cc:dd:ee:01",
+            remote_mac="aa:bb:cc:dd:ee:02")
+        c4.pg_create_interface(
+            local_ip="B::1/120",
+            remote_ip="B::2",
+            local_mac="aa:bb:cc:dd:ee:11",
+            remote_mac="aa:bb:cc:dd:ee:22")
+
+        c1.vppctl_exec("set sr encaps source addr A1::1")
+        c1.vppctl_exec("sr policy add bsid D4:: next D2:: next D3::")
+
+        c1.vppctl_exec(
+            "sr localsid prefix D::/64 behavior end.m.gtp6.d.di D4::/64")
+
+        c2.vppctl_exec("sr localsid address D2:: behavior end")
+
+        c3.vppctl_exec("sr localsid address D3:: behavior end")
+
+        c4.vppctl_exec("sr localsid prefix D4::/64 behavior end.m.gtp6.e")
+
+        c2.set_ipv6_route("eth2", "A2::2", "D3::/128")
+        c2.set_ipv6_route("eth1", "A1::1", "C::/120")
+        c3.set_ipv6_route("eth2", "A3::2", "D4::/32")
+        c3.set_ipv6_route("eth1", "A2::1", "C::/120")
+        c4.set_ip_pgroute("pg0", "B::2", "D::2/128")
+
+        print("Waiting...")
+        time.sleep(30)
+
+        p = (Ether(src="aa:bb:cc:dd:ee:02", dst="aa:bb:cc:dd:ee:01") /
+             IPv6(src="C::2", dst="D::2") /
+             UDP(sport=2152, dport=2152) /
+             GTP_U_Header(gtp_type="g_pdu", teid=200) /
+             IPv6(src="2001::1", dst="2002::1") /
+             ICMPv6EchoRequest())
+
+        print("Sending packet on {}:".format(c1.name))
+        p.show2()
+
+        c1.enable_trace(10)
+        c4.enable_trace(10)
+
+        c4.pg_start_capture()
+
+        c1.pg_create_stream(p)
+        c1.pg_enable()
+
+        # timeout (sleep) if needed
+        print("Sleeping")
+        time.sleep(5)
+
+        print("Receiving packet on {}:".format(c4.name))
+        for p in c4.pg_read_packets():
+            p.show2()
+
+    def test_gtp6_drop_in_ipv6_5g(self):
+        # TESTS:
+        # trace add af-packet-input 10
+        # pg interface on c1 172.20.0.1
+        # pg interface on c4 B::1/120
+
+        self.start_containers()
+
+        print("Deleting the old containers...")
+        time.sleep(30)
+        print("Starting the new containers...")
+
+        c1 = self.containers.get(self.get_name(self.instance_names[0]))
+        c2 = self.containers.get(self.get_name(self.instance_names[1]))
+        c3 = self.containers.get(self.get_name(self.instance_names[2]))
+        c4 = self.containers.get(self.get_name(self.instance_names[-1]))
+
+        c1.pg_create_interface(
+            local_ip="C::1/120",
+            remote_ip="C::2",
+            local_mac="aa:bb:cc:dd:ee:01",
+            remote_mac="aa:bb:cc:dd:ee:02")
+        c4.pg_create_interface(
+            local_ip="B::1/120",
+            remote_ip="B::2",
+            local_mac="aa:bb:cc:dd:ee:11",
+            remote_mac="aa:bb:cc:dd:ee:22")
+
+        c1.vppctl_exec("set sr encaps source addr A1::1")
+        c1.vppctl_exec("sr policy add bsid D4:: next D2:: next D3::")
+
+        c1.vppctl_exec(
+            "sr localsid prefix D::/64 behavior end.m.gtp6.d.di D4::/64")
+
+        c2.vppctl_exec("sr localsid address D2:: behavior end")
+
+        c3.vppctl_exec("sr localsid address D3:: behavior end")
+
+        c4.vppctl_exec("sr localsid prefix D4::/64 behavior end.m.gtp6.e")
+
+        c2.set_ipv6_route("eth2", "A2::2", "D3::/128")
+        c2.set_ipv6_route("eth1", "A1::1", "C::/120")
+        c3.set_ipv6_route("eth2", "A3::2", "D4::/32")
+        c3.set_ipv6_route("eth1", "A2::1", "C::/120")
+        c4.set_ip_pgroute("pg0", "B::2", "D::2/128")
+
+        print("Waiting...")
+        time.sleep(30)
+
+        p = (Ether(src="aa:bb:cc:dd:ee:02", dst="aa:bb:cc:dd:ee:01") /
+             IPv6(src="C::2", dst="D::2") /
+             UDP(sport=2152, dport=2152) /
+             GTP_U_Header(gtp_type="g_pdu", teid=200) /
+             GTPPDUSessionContainer(R=1, QFI=3) /
+             IPv6(src="2001::1", dst="2002::1") /
+             ICMPv6EchoRequest())
+
+        print("Sending packet on {}:".format(c1.name))
+        p.show2()
+
+        c1.enable_trace(10)
+        c4.enable_trace(10)
+
+        c4.pg_start_capture()
+
+        c1.pg_create_stream(p)
+        c1.pg_enable()
+
+        # timeout (sleep) if needed
+        print("Sleeping")
+        time.sleep(5)
+
+        print("Receiving packet on {}:".format(c4.name))
+        for p in c4.pg_read_packets():
+            p.show2()
+
+    def test_gtp6(self):
+        # TESTS:
+        # trace add af-packet-input 10
+        # pg interface on c1 172.20.0.1
+        # pg interface on c4 B::1/120
+
+        self.start_containers()
+
+        print("Deleting the old containers...")
+        time.sleep(30)
+        print("Starting the new containers...")
+
+        c1 = self.containers.get(self.get_name(self.instance_names[0]))
+        c2 = self.containers.get(self.get_name(self.instance_names[1]))
+        c3 = self.containers.get(self.get_name(self.instance_names[2]))
+        c4 = self.containers.get(self.get_name(self.instance_names[-1]))
+
+        c1.pg_create_interface(
+            local_ip="C::1/120",
+            remote_ip="C::2",
+            local_mac="aa:bb:cc:dd:ee:01",
+            remote_mac="aa:bb:cc:dd:ee:02")
+        c4.pg_create_interface4(
+            local_ip="1.0.0.2/30",
+            remote_ip="1.0.0.1",
+            local_mac="aa:bb:cc:dd:ee:11",
+            remote_mac="aa:bb:cc:dd:ee:22")
+
+        c1.vppctl_exec("set sr encaps source addr A1::1")
+        c1.vppctl_exec("sr policy add bsid D4:: next D2:: next D3::")
+
+        c1.vppctl_exec(
+            "sr localsid prefix D::/64 behavior end.m.gtp6.d D4::/64")
+
+        c2.vppctl_exec("sr localsid address D2:: behavior end")
+
+        c3.vppctl_exec("sr localsid address D3:: behavior end")
+
+        c4.vppctl_exec("sr localsid prefix D4::/64 behavior end.dt4 2")
+
+        c2.set_ipv6_route("eth2", "A2::2", "D3::/128")
+        c2.set_ipv6_route("eth1", "A1::1", "C::/120")
+        c3.set_ipv6_route("eth2", "A3::2", "D4::/32")
+        c3.set_ipv6_route("eth1", "A2::1", "C::/120")
+        c4.set_ip_pgroute("pg0", "1.0.0.1", "172.200.0.1/32")
+
+        print("Waiting...")
+        time.sleep(30)
+
+        p = (Ether(src="aa:bb:cc:dd:ee:02", dst="aa:bb:cc:dd:ee:01") /
+             IPv6(src="C::2", dst="D::2") /
+             UDP(sport=2152, dport=2152) /
+             GTP_U_Header(gtp_type="g_pdu", teid=200) /
+             IP(src="172.100.0.1", dst="172.200.0.1") /
+             ICMP())
+
+        print("Sending packet on {}:".format(c1.name))
+        p.show2()
+
+        c1.enable_trace(10)
+        c4.enable_trace(10)
+
+        c4.pg_start_capture()
+
+        c1.pg_create_stream(p)
+        c1.pg_enable()
+
+        # timeout (sleep) if needed
+        print("Sleeping")
+        time.sleep(5)
+
+        print("Receiving packet on {}:".format(c4.name))
+        for p in c4.pg_read_packets():
+            p.show2()
+
+    def test_gtp6_5g(self):
+        # TESTS:
+        # trace add af-packet-input 10
+        # pg interface on c1 172.20.0.1
+        # pg interface on c4 B::1/120
+
+        self.start_containers()
+
+        print("Deleting the old containers...")
+        time.sleep(30)
+        print("Starting the new containers...")
+
+        c1 = self.containers.get(self.get_name(self.instance_names[0]))
+        c2 = self.containers.get(self.get_name(self.instance_names[1]))
+        c3 = self.containers.get(self.get_name(self.instance_names[2]))
+        c4 = self.containers.get(self.get_name(self.instance_names[-1]))
+
+        c1.pg_create_interface(
+            local_ip="C::1/120",
+            remote_ip="C::2",
+            local_mac="aa:bb:cc:dd:ee:01",
+            remote_mac="aa:bb:cc:dd:ee:02")
+        c4.pg_create_interface4(
+            local_ip="1.0.0.2/30",
+            remote_ip="1.0.0.1",
+            local_mac="aa:bb:cc:dd:ee:11",
+            remote_mac="aa:bb:cc:dd:ee:22")
+
+        c1.vppctl_exec("set sr encaps source addr A1::1")
+        c1.vppctl_exec("sr policy add bsid D4:: next D2:: next D3::")
+
+        c1.vppctl_exec(
+            "sr localsid prefix D::/64 behavior end.m.gtp6.d D4::/64")
+
+        c2.vppctl_exec("sr localsid address D2:: behavior end")
+
+        c3.vppctl_exec("sr localsid address D3:: behavior end")
+
+        c4.vppctl_exec("sr localsid prefix D4::/64 behavior end.dt4 2")
+
+        c2.set_ipv6_route("eth2", "A2::2", "D3::/128")
+        c2.set_ipv6_route("eth1", "A1::1", "C::/120")
+        c3.set_ipv6_route("eth2", "A3::2", "D4::/32")
+        c3.set_ipv6_route("eth1", "A2::1", "C::/120")
+        c4.set_ip_pgroute("pg0", "1.0.0.1", "172.200.0.1/32")
+
+        print("Waiting...")
+        time.sleep(30)
+
+        p = (Ether(src="aa:bb:cc:dd:ee:02", dst="aa:bb:cc:dd:ee:01") /
+             IPv6(src="C::2", dst="D::2") /
+             UDP(sport=2152, dport=2152) /
+             GTP_U_Header(gtp_type="g_pdu", teid=200) /
+             GTPPDUSessionContainer(R=1, QFI=3) /
+             IP(src="172.100.0.1", dst="172.200.0.1") /
+             ICMP())
+
+        print("Sending packet on {}:".format(c1.name))
+        p.show2()
+
+        c1.enable_trace(10)
+        c4.enable_trace(10)
+
+        c4.pg_start_capture()
+
+        c1.pg_create_stream(p)
+        c1.pg_enable()
+
+        # timeout (sleep) if needed
+        print("Sleeping")
+        time.sleep(5)
+
+        print("Receiving packet on {}:".format(c4.name))
+        for p in c4.pg_read_packets():
+            p.show2()
+
+    def test_gtp6_ipv6(self):
+        # TESTS:
+        # trace add af-packet-input 10
+        # pg interface on c1 172.20.0.1
+        # pg interface on c4 B::1/120
+
+        self.start_containers()
+
+        print("Deleting the old containers...")
+        time.sleep(30)
+        print("Starting the new containers...")
+
+        c1 = self.containers.get(self.get_name(self.instance_names[0]))
+        c2 = self.containers.get(self.get_name(self.instance_names[1]))
+        c3 = self.containers.get(self.get_name(self.instance_names[2]))
+        c4 = self.containers.get(self.get_name(self.instance_names[-1]))
+
+        c1.pg_create_interface(
+            local_ip="C::1/120",
+            remote_ip="C::2",
+            local_mac="aa:bb:cc:dd:ee:01",
+            remote_mac="aa:bb:cc:dd:ee:02")
+        c4.pg_create_interface(
+            local_ip="B::1/120",
+            remote_ip="B::2",
+            local_mac="aa:bb:cc:dd:ee:11",
+            remote_mac="aa:bb:cc:dd:ee:22")
+
+        c1.vppctl_exec("set sr encaps source addr A1::1")
+        c1.vppctl_exec("sr policy add bsid D4:: next D2:: next D3::")
+
+        c1.vppctl_exec(
+            "sr localsid prefix D::/64 behavior end.m.gtp6.d D4::/64")
+
+        c2.vppctl_exec("sr localsid address D2:: behavior end")
+
+        c3.vppctl_exec("sr localsid address D3:: behavior end")
+
+        c4.vppctl_exec("sr localsid prefix D4::/64 behavior end.dt6 2")
+
+        c2.set_ipv6_route("eth2", "A2::2", "D3::/128")
+        c2.set_ipv6_route("eth1", "A1::1", "C::/120")
+        c3.set_ipv6_route("eth2", "A3::2", "D4::/32")
+        c3.set_ipv6_route("eth1", "A2::1", "C::/120")
+        c4.set_ipv6_pgroute("pg0", "B::2", "2002::1/128")
+
+        print("Waiting...")
+        time.sleep(30)
+
+        p = (Ether(src="aa:bb:cc:dd:ee:02", dst="aa:bb:cc:dd:ee:01") /
+             IPv6(src="C::2", dst="D::2") /
+             UDP(sport=2152, dport=2152) /
+             GTP_U_Header(gtp_type="g_pdu", teid=200) /
+             IPv6(src="2001::1", dst="2002::1") /
+             ICMPv6EchoRequest())
+
+        print("Sending packet on {}:".format(c1.name))
+        p.show2()
+
+        c1.enable_trace(10)
+        c4.enable_trace(10)
+
+        c4.pg_start_capture()
+
+        c1.pg_create_stream(p)
+        c1.pg_enable()
+
+        # timeout (sleep) if needed
+        print("Sleeping")
+        time.sleep(5)
+
+        print("Receiving packet on {}:".format(c4.name))
+        for p in c4.pg_read_packets():
+            p.show2()
+
+    def test_gtp6_ipv6_5g(self):
+        # TESTS:
+        # trace add af-packet-input 10
+        # pg interface on c1 172.20.0.1
+        # pg interface on c4 B::1/120
+
+        self.start_containers()
+
+        print("Deleting the old containers...")
+        time.sleep(30)
+        print("Starting the new containers...")
+
+        c1 = self.containers.get(self.get_name(self.instance_names[0]))
+        c2 = self.containers.get(self.get_name(self.instance_names[1]))
+        c3 = self.containers.get(self.get_name(self.instance_names[2]))
+        c4 = self.containers.get(self.get_name(self.instance_names[-1]))
+
+        c1.pg_create_interface(
+            local_ip="C::1/120",
+            remote_ip="C::2",
+            local_mac="aa:bb:cc:dd:ee:01",
+            remote_mac="aa:bb:cc:dd:ee:02")
+        c4.pg_create_interface(
+            local_ip="B::1/120",
+            remote_ip="B::2",
+            local_mac="aa:bb:cc:dd:ee:11",
+            remote_mac="aa:bb:cc:dd:ee:22")
+
+        c1.vppctl_exec("set sr encaps source addr A1::1")
+        c1.vppctl_exec("sr policy add bsid D4:: next D2:: next D3::")
+
+        c1.vppctl_exec(
+            "sr localsid prefix D::/64 behavior end.m.gtp6.d D4::/64")
+
+        c2.vppctl_exec("sr localsid address D2:: behavior end")
+
+        c3.vppctl_exec("sr localsid address D3:: behavior end")
+
+        c4.vppctl_exec("sr localsid prefix D4::/64 behavior end.dt6 2")
+
+        c2.set_ipv6_route("eth2", "A2::2", "D3::/128")
+        c2.set_ipv6_route("eth1", "A1::1", "C::/120")
+        c3.set_ipv6_route("eth2", "A3::2", "D4::/32")
+        c3.set_ipv6_route("eth1", "A2::1", "C::/120")
+        c4.set_ipv6_pgroute("pg0", "B::2", "2002::1/128")
+
+        print("Waiting...")
+        time.sleep(30)
+
+        p = (Ether(src="aa:bb:cc:dd:ee:02", dst="aa:bb:cc:dd:ee:01") /
+             IPv6(src="C::2", dst="D::2") /
+             UDP(sport=2152, dport=2152) /
+             GTP_U_Header(gtp_type="g_pdu", teid=200) /
+             GTPPDUSessionContainer(R=1, QFI=3) /
+             IPv6(src="2001::1", dst="2002::1") /
+             ICMPv6EchoRequest())
+
+        print("Sending packet on {}:".format(c1.name))
+        p.show2()
+
+        c1.enable_trace(10)
+        c4.enable_trace(10)
+
+        c4.pg_start_capture()
+
+        c1.pg_create_stream(p)
+        c1.pg_enable()
+
+        # timeout (sleep) if needed
+        print("Sleeping")
+        time.sleep(5)
+
+        print("Receiving packet on {}:".format(c4.name))
+        for p in c4.pg_read_packets():
+            p.show2()
+
+    def status_containers(self):
+
+        print("Instances:")
+
+        for i, name in enumerate(self.instance_names):
+            name = self.get_name(name)
+            print("\t[{}] {} - {}".format(
+                i, name,
+                "running" if self.containers.get(name) else "missing"))
+
+        print("Networks:")
+
+        for i, name in enumerate(self.network_names):
+            name = self.get_name(name)
+            print("\t[{}] {} - {}".format(
+                i, name,
+                "running" if self.networks.get(name) else "missing"))
+
+    def build_image(self):
+        # TODO: optimize build process for speed and image size
+        print("VPP Path (build): {}".format(self.vpp_path))
+        self.containers.build(self.path, self.vpp_path)
+
+    def release_image(self):
+        print("VPP Path (release): {}".format(self.vpp_path))
+        instance = self.containers.new("release-build")
+
+        system(
+            "docker cp release-build:{}/vpp-package.tgz {}/".format(
+                self.vpp_path, self.vpp_path))
+
+        instance.rem()
+
+        self.containers.release(self.path, self.vpp_path)
+
+        system("rm -rf {}/vpp-package.tgz".format(self.vpp_path))
+
+    def vppctl(self, index, command=None):
+        if index >= len(self.instance_names):
+            return
+        name = self.get_name(self.instance_names[index])
+        self.logger.error("connecting to: {}".format(name))
+        self.containers.vppctl(name, command)
+
+    def bash(self, index):
+        if index >= len(self.instance_names):
+            return
+        name = self.get_name(self.instance_names[index])
+        self.logger.error("connecting to: {}".format(name))
+        self.containers.bash(name)
+
+
+def get_args():
+    parser = ArgumentParser()
+
+    parser.add_argument("--verbose", choices=['error', 'debug', 'info'])
+
+    parser.add_argument('--image', choices=['debug', 'release'])
+
+    subparsers = parser.add_subparsers()
+
+    p1 = subparsers.add_parser(
+        "infra", help="Infrastructure related commands.")
+
+    p1.add_argument(
+        "op",
+        choices=[
+            'stop',
+            'start',
+            'status',
+            'restart',
+            'build',
+            'release'])
+
+    p1.add_argument("--prefix")
+    p1.add_argument("--image")
+
+    p2 = subparsers.add_parser("cmd", help="Instance related commands.")
+
+    p2.add_argument("op", choices=['vppctl', 'bash'])
+
+    p2.add_argument(
+        "index",
+        type=int,
+        help="Container instance index. (./runner.py infra status)")
+
+    p2.add_argument(
+        "--command", help="Only vppctl supports this optional argument.")
+
+    p3 = subparsers.add_parser("test", help="Test related commands.")
+
+    p3.add_argument(
+        "op",
+        choices=[
+            "ping",
+            "srv6",
+            "tmap",
+            "tmap_5g",
+            "tmap_ipv6",
+            "tmap_ipv6_5g",
+            "gtp4",
+            "gtp4_5g",
+            "gtp4_echo",
+            "gtp4_ipv6",
+            "gtp4_ipv6_5g",
+            "gtp6_drop_in",
+            "gtp6_drop_in_5g",
+            "gtp6_drop_in_echo",
+            "gtp6_drop_in_ipv6",
+            "gtp6_drop_in_ipv6_5g",
+            "gtp6",
+            "gtp6_5g",
+            "gtp6_ipv6",
+            "gtp6_ipv6_5g"])
+
+    args = parser.parse_args()
+    if not hasattr(args, "op") or not args.op:
+        parser.print_help(sys.stderr)
+        sys.exit(1)
+
+    return vars(args)
+
+
+def main(op=None, prefix=None, verbose=None,
+         image=None, index=None, command=None):
+
+    if verbose:
+        basicConfig(level=verbose_levels[verbose])
+
+    if image == 'release':
+        image = "srv6m-release-image"
+    elif image == 'debug':
+        image = "srv6m-image"
+
+    print("Verified image: {}".format(image))
+
+    program = Program(image, prefix)
+
+    try:
+        if op == 'build':
+            program.build_image()
+        elif op == 'release':
+            program.release_image()
+        elif op == 'stop':
+            program.stop_containers()
+        elif op == 'start':
+            program.start_containers()
+        elif op == 'status':
+            program.status_containers()
+        elif op == 'vppctl':
+            program.vppctl(index, command)
+        elif op == 'bash':
+            program.bash(index)
+        elif op == 'ping':
+            program.test_ping()
+        elif op == 'srv6':
+            program.test_srv6()
+        elif op == 'tmap':
+            program.test_tmap()
+        elif op == 'tmap_5g':
+            program.test_tmap_5g()
+        elif op == 'tmap_ipv6':
+            program.test_tmap_ipv6()
+        elif op == 'tmap_ipv6_5g':
+            program.test_tmap_ipv6_5g()
+        elif op == 'gtp4':
+            program.test_gtp4()
+        elif op == 'gtp4_5g':
+            program.test_gtp4_5g()
+        elif op == 'gtp4_echo':
+            program.test_gtp4_echo()
+        elif op == 'gtp4_ipv6':
+            program.test_gtp4_ipv6()
+        elif op == 'gtp4_ipv6_5g':
+            program.test_gtp4_ipv6_5g()
+        elif op == 'gtp6_drop_in':
+            program.test_gtp6_drop_in()
+        elif op == 'gtp6_drop_in_5g':
+            program.test_gtp6_drop_in_5g()
+        elif op == 'gtp6_drop_in_echo':
+            program.test_gtp6_drop_in_echo()
+        elif op == 'gtp6_drop_in_ipv6':
+            program.test_gtp6_drop_in_ipv6()
+        elif op == 'gtp6_drop_in_ipv6_5g':
+            program.test_gtp6_drop_in_ipv6_5g()
+        elif op == 'gtp6':
+            program.test_gtp6()
+        elif op == 'gtp6_5g':
+            program.test_gtp6_5g()
+        elif op == 'gtp6_ipv6':
+            program.test_gtp6_ipv6()
+        elif op == 'gtp6_ipv6_5g':
+            program.test_gtp6_ipv6_5g()
+
+    except Exception:
+        program.logger.exception("")
+        rc = 1
+    else:
+        rc = 0
+
+    return rc
+
+
+if __name__ == "__main__":
+    sys.exit(main(**get_args()))
diff --git a/src/plugins/srv6-mobile/extra/startup.conf.j2 b/src/plugins/srv6-mobile/extra/startup.conf.j2
new file mode 100644 (file)
index 0000000..5a4a2ef
--- /dev/null
@@ -0,0 +1,14 @@
+unix {
+  nodaemon
+  cli-no-pager
+  cli-listen 0.0.0.0:5002
+}
+plugins {
+  plugin dpdk_plugin.so {
+    disable
+  }
+}
+cpu {
+  workers 2  
+}
+
diff --git a/src/plugins/srv6-mobile/extra/topo-init.png b/src/plugins/srv6-mobile/extra/topo-init.png
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/plugins/srv6-mobile/extra/topo-test_gtp4d.png b/src/plugins/srv6-mobile/extra/topo-test_gtp4d.png
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/plugins/srv6-mobile/extra/topo-test_gtp6.png b/src/plugins/srv6-mobile/extra/topo-test_gtp6.png
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/plugins/srv6-mobile/extra/topo-test_gtp6d.png b/src/plugins/srv6-mobile/extra/topo-test_gtp6d.png
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/plugins/srv6-mobile/extra/topo-test_gtp6ip6.png b/src/plugins/srv6-mobile/extra/topo-test_gtp6ip6.png
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/plugins/srv6-mobile/gtp4_e.c b/src/plugins/srv6-mobile/gtp4_e.c
new file mode 100644 (file)
index 0000000..73a91f8
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * srv6_end_m_gtp4_e.c
+ *
+ * Copyright (c) 2019 Arrcus Inc and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/adj/adj.h>
+#include <vnet/plugin/plugin.h>
+#include <vpp/app/version.h>
+#include <srv6-mobile/mobile.h>
+
+srv6_end_main_v4_t srv6_end_main_v4;
+
+static void
+clb_dpo_lock_srv6_end_m_gtp4_e (dpo_id_t * dpo)
+{
+}
+
+static void
+clb_dpo_unlock_srv6_end_m_gtp4_e (dpo_id_t * dpo)
+{
+}
+
+static u8 *
+clb_dpo_format_srv6_end_m_gtp4_e (u8 * s, va_list * args)
+{
+  index_t index = va_arg (*args, index_t);
+  CLIB_UNUSED (u32 indent) = va_arg (*args, u32);
+
+  return (format (s, "SR: dynamic_proxy_index:[%u]", index));
+}
+
+const static dpo_vft_t dpo_vft = {
+  .dv_lock = clb_dpo_lock_srv6_end_m_gtp4_e,
+  .dv_unlock = clb_dpo_unlock_srv6_end_m_gtp4_e,
+  .dv_format = clb_dpo_format_srv6_end_m_gtp4_e,
+};
+
+const static char *const srv6_end_m_gtp4_e_nodes[] = {
+  "srv6-end-m-gtp4-e",
+  NULL,
+};
+
+const static char *const *const dpo_nodes[DPO_PROTO_NUM] = {
+  [DPO_PROTO_IP6] = srv6_end_m_gtp4_e_nodes,
+};
+
+static u8 fn_name[] = "SRv6-End.M.GTP4.E-plugin";
+static u8 keyword_str[] = "end.m.gtp4.e";
+static u8 def_str[] =
+  "Endpoint function with encapsulation for IPv4/GTP tunnel";
+static u8 param_str[] = "";
+
+static u8 *
+clb_format_srv6_end_m_gtp4_e (u8 * s, va_list * args)
+{
+  srv6_end_gtp4_param_t *ls_mem = va_arg (*args, void *);
+
+  s = format (s, "SRv6 End gtp4.e\n\t");
+
+  s = format (s, "IPv4 address position: %d\n", ls_mem->v4src_position);
+
+  return s;
+}
+
+static uword
+clb_unformat_srv6_end_m_gtp4_e (unformat_input_t * input, va_list * args)
+{
+  void **plugin_mem_p = va_arg (*args, void **);
+  srv6_end_gtp4_param_t *ls_mem;
+  u32 v4src_position;
+
+  if (!unformat (input, "end.m.gtp4.e v4src_position %d", &v4src_position))
+    return 0;
+
+  ls_mem = clib_mem_alloc_aligned_at_offset (sizeof *ls_mem, 0, 0, 1);
+  clib_memset (ls_mem, 0, sizeof *ls_mem);
+  *plugin_mem_p = ls_mem;
+
+  ls_mem->v4src_position = v4src_position;
+
+  return 1;
+}
+
+static int
+clb_creation_srv6_end_m_gtp4_e (ip6_sr_localsid_t * localsid)
+{
+  return 0;
+}
+
+static int
+clb_removal_srv6_end_m_gtp4_e (ip6_sr_localsid_t * localsid)
+{
+  srv6_end_gtp4_param_t *ls_mem;
+
+  ls_mem = localsid->plugin_mem;
+
+  clib_mem_free (ls_mem);
+
+  return 0;
+}
+
+static clib_error_t *
+srv6_end_m_gtp4_e_init (vlib_main_t * vm)
+{
+  srv6_end_main_v4_t *sm = &srv6_end_main_v4;
+  ip4_header_t *ip4 = &sm->cache_hdr.ip4;
+  udp_header_t *udp = &sm->cache_hdr.udp;
+  gtpu_header_t *gtpu = &sm->cache_hdr.gtpu;
+  dpo_type_t dpo_type;
+  vlib_node_t *node;
+  u32 rc;
+
+  sm->vlib_main = vm;
+  sm->vnet_main = vnet_get_main ();
+
+  node = vlib_get_node_by_name (vm, (u8 *) "srv6-end-m-gtp4-e");
+  sm->end_m_gtp4_e_node_index = node->index;
+
+  node = vlib_get_node_by_name (vm, (u8 *) "error-drop");
+  sm->error_node_index = node->index;
+
+  sm->dst_p_len = 32;
+  sm->src_p_len = 64;
+
+  // clear the pre cached packet
+  clib_memset_u8 (ip4, 0, sizeof (ip4_gtpu_header_t));
+
+  // set defaults
+  ip4->ip_version_and_header_length = 0x45;
+  ip4->protocol = IP_PROTOCOL_UDP;
+  ip4->ttl = 64;
+
+  udp->dst_port = clib_host_to_net_u16 (SRV6_GTP_UDP_DST_PORT);
+
+  gtpu->ver_flags = GTPU_V1_VER | GTPU_PT_GTP;
+  gtpu->type = GTPU_TYPE_GTPU;
+  //
+
+  dpo_type = dpo_register_new_type (&dpo_vft, dpo_nodes);
+
+  rc = sr_localsid_register_function (vm, fn_name, keyword_str, def_str, param_str, 32,        //prefix len
+                                     &dpo_type,
+                                     clb_format_srv6_end_m_gtp4_e,
+                                     clb_unformat_srv6_end_m_gtp4_e,
+                                     clb_creation_srv6_end_m_gtp4_e,
+                                     clb_removal_srv6_end_m_gtp4_e);
+  if (rc < 0)
+    clib_error_return (0, "SRv6 Endpoint GTP4.E LocalSID function"
+                      "couldn't be registered");
+  return 0;
+}
+
+/* *INDENT-OFF* */
+VNET_FEATURE_INIT (srv6_end_m_gtp4_e, static) =
+{
+  .arc_name = "ip6-unicast",
+  .node_name = "srv6-end-m-gtp4-e",
+  .runs_before = 0,
+};
+
+VLIB_INIT_FUNCTION (srv6_end_m_gtp4_e_init);
+
+VLIB_PLUGIN_REGISTER () = {
+  .version = VPP_BUILD_VER,
+  .description = "SRv6 GTP Endpoint Functions",
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/srv6-mobile/gtp6_d.c b/src/plugins/srv6-mobile/gtp6_d.c
new file mode 100644 (file)
index 0000000..3f366e8
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * srv6_end_m_gtp6_d.c
+ *
+ * Copyright (c) 2019 Arrcus Inc and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/adj/adj.h>
+#include <vnet/plugin/plugin.h>
+#include <vpp/app/version.h>
+#include <srv6-mobile/mobile.h>
+
+srv6_end_main_v6_decap_t srv6_end_main_v6_decap;
+
+static void
+clb_dpo_lock_srv6_end_m_gtp6_d (dpo_id_t * dpo)
+{
+}
+
+static void
+clb_dpo_unlock_srv6_end_m_gtp6_d (dpo_id_t * dpo)
+{
+}
+
+static u8 *
+clb_dpo_format_srv6_end_m_gtp6_d (u8 * s, va_list * args)
+{
+  index_t index = va_arg (*args, index_t);
+  CLIB_UNUSED (u32 indent) = va_arg (*args, u32);
+
+  return (format (s, "SR: dynamic_proxy_index:[%u]", index));
+}
+
+const static dpo_vft_t dpo_vft = {
+  .dv_lock = clb_dpo_lock_srv6_end_m_gtp6_d,
+  .dv_unlock = clb_dpo_unlock_srv6_end_m_gtp6_d,
+  .dv_format = clb_dpo_format_srv6_end_m_gtp6_d,
+};
+
+const static char *const srv6_end_m_gtp6_d_nodes[] = {
+  "srv6-end-m-gtp6-d",
+  NULL,
+};
+
+const static char *const *const dpo_nodes[DPO_PROTO_NUM] = {
+  [DPO_PROTO_IP6] = srv6_end_m_gtp6_d_nodes,
+};
+
+static u8 fn_name[] = "SRv6-End.M.GTP6.D-plugin";
+static u8 keyword_str[] = "end.m.gtp6.d";
+static u8 def_str[] =
+  "Endpoint function with dencapsulation for IPv6/GTP tunnel";
+static u8 param_str[] = "<sr-prefix>/<sr-prefixlen> [nhtype <nhtype>]";
+
+static u8 *
+clb_format_srv6_end_m_gtp6_d (u8 * s, va_list * args)
+{
+  srv6_end_gtp6_param_t *ls_mem = va_arg (*args, void *);
+
+  s = format (s, "SRv6 End gtp6.d\n\t");
+
+  s =
+    format (s, "SR Prefix: %U/%d", format_ip6_address, &ls_mem->sr_prefix,
+           ls_mem->sr_prefixlen);
+
+  if (ls_mem->nhtype != SRV6_NHTYPE_NONE)
+    {
+      if (ls_mem->nhtype == SRV6_NHTYPE_IPV4)
+       s = format (s, ", NHType IPv4\n");
+      else if (ls_mem->nhtype == SRV6_NHTYPE_IPV6)
+       s = format (s, ", NHType IPv6\n");
+      else if (ls_mem->nhtype == SRV6_NHTYPE_NON_IP)
+       s = format (s, ", NHType Non-IP\n");
+      else
+       s = format (s, ", NHType Unknow(%d)\n", ls_mem->nhtype);
+    }
+  else
+    s = format (s, "\n");
+
+  return s;
+}
+
+static uword
+clb_unformat_srv6_end_m_gtp6_d (unformat_input_t * input, va_list * args)
+{
+  void **plugin_mem_p = va_arg (*args, void **);
+  srv6_end_gtp6_param_t *ls_mem;
+  ip6_address_t sr_prefix;
+  u32 sr_prefixlen;
+  u8 nhtype;
+
+  if (unformat (input, "end.m.gtp6.d %U/%d nh-type ipv4",
+               unformat_ip6_address, &sr_prefix, &sr_prefixlen))
+    {
+      nhtype = SRV6_NHTYPE_IPV4;
+    }
+  else if (unformat (input, "end.m.gtp6.d %U/%d nh-type ipv6",
+                    unformat_ip6_address, &sr_prefix, &sr_prefixlen))
+    {
+      nhtype = SRV6_NHTYPE_IPV6;
+    }
+  else if (unformat (input, "end.m.gtp6.d %U/%d nh-type none",
+                    unformat_ip6_address, &sr_prefix, &sr_prefixlen))
+    {
+      nhtype = SRV6_NHTYPE_NON_IP;
+    }
+  else if (unformat (input, "end.m.gtp6.d %U/%d",
+                    unformat_ip6_address, &sr_prefix, &sr_prefixlen))
+    {
+      nhtype = SRV6_NHTYPE_NONE;
+    }
+  else
+    {
+      return 0;
+    }
+
+  ls_mem = clib_mem_alloc_aligned_at_offset (sizeof *ls_mem, 0, 0, 1);
+  clib_memset (ls_mem, 0, sizeof *ls_mem);
+  *plugin_mem_p = ls_mem;
+
+  ls_mem->sr_prefix = sr_prefix;
+  ls_mem->sr_prefixlen = sr_prefixlen;
+
+  ls_mem->nhtype = nhtype;
+
+  return 1;
+}
+
+static int
+clb_creation_srv6_end_m_gtp6_d (ip6_sr_localsid_t * localsid)
+{
+  return 0;
+}
+
+static int
+clb_removal_srv6_end_m_gtp6_d (ip6_sr_localsid_t * localsid)
+{
+  srv6_end_gtp6_param_t *ls_mem;
+
+  ls_mem = localsid->plugin_mem;
+
+  clib_mem_free (ls_mem);
+
+  return 0;
+}
+
+static clib_error_t *
+srv6_end_m_gtp6_d_init (vlib_main_t * vm)
+{
+  srv6_end_main_v6_decap_t *sm = &srv6_end_main_v6_decap;
+  ip6_header_t *ip6;
+  dpo_type_t dpo_type;
+  vlib_node_t *node;
+  u32 rc;
+
+  sm->vlib_main = vm;
+  sm->vnet_main = vnet_get_main ();
+
+  node = vlib_get_node_by_name (vm, (u8 *) "srv6-end-m-gtp6-d");
+  sm->end_m_gtp6_d_node_index = node->index;
+
+  node = vlib_get_node_by_name (vm, (u8 *) "error-drop");
+  sm->error_node_index = node->index;
+
+  ip6 = &sm->cache_hdr;
+
+  clib_memset_u8 (ip6, 0, sizeof (ip6_header_t));
+
+  // IPv6 header (default)
+  ip6->ip_version_traffic_class_and_flow_label = 0x60;
+  ip6->hop_limit = 64;
+  ip6->protocol = IP_PROTOCOL_IPV6;
+
+  dpo_type = dpo_register_new_type (&dpo_vft, dpo_nodes);
+
+  rc = sr_localsid_register_function (vm, fn_name, keyword_str, def_str, param_str, 128,       //prefix len
+                                     &dpo_type,
+                                     clb_format_srv6_end_m_gtp6_d,
+                                     clb_unformat_srv6_end_m_gtp6_d,
+                                     clb_creation_srv6_end_m_gtp6_d,
+                                     clb_removal_srv6_end_m_gtp6_d);
+  if (rc < 0)
+    clib_error_return (0, "SRv6 Endpoint GTP6.D LocalSID function"
+                      "couldn't be registered");
+  return 0;
+}
+
+/* *INDENT-OFF* */
+VNET_FEATURE_INIT (srv6_end_m_gtp6_d, static) =
+{
+  .arc_name = "ip6-unicast",
+  .node_name = "srv6-end-m-gtp6-d",
+  .runs_before = 0,
+};
+
+VLIB_INIT_FUNCTION (srv6_end_m_gtp6_d_init);
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/srv6-mobile/gtp6_d_di.c b/src/plugins/srv6-mobile/gtp6_d_di.c
new file mode 100644 (file)
index 0000000..54f1873
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * srv6_end_m_gtp6_d_di_di.c
+ *
+ * Copyright (c) 2019 Arrcus Inc and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/adj/adj.h>
+#include <vnet/plugin/plugin.h>
+#include <vpp/app/version.h>
+#include <srv6-mobile/mobile.h>
+
+srv6_end_main_v6_decap_di_t srv6_end_main_v6_decap_di;
+
+static void
+clb_dpo_lock_srv6_end_m_gtp6_d_di (dpo_id_t * dpo)
+{
+}
+
+static void
+clb_dpo_unlock_srv6_end_m_gtp6_d_di (dpo_id_t * dpo)
+{
+}
+
+static u8 *
+clb_dpo_format_srv6_end_m_gtp6_d_di (u8 * s, va_list * args)
+{
+  index_t index = va_arg (*args, index_t);
+  CLIB_UNUSED (u32 indent) = va_arg (*args, u32);
+
+  return (format (s, "SR: dynamic_proxy_index:[%u]", index));
+}
+
+const static dpo_vft_t dpo_vft = {
+  .dv_lock = clb_dpo_lock_srv6_end_m_gtp6_d_di,
+  .dv_unlock = clb_dpo_unlock_srv6_end_m_gtp6_d_di,
+  .dv_format = clb_dpo_format_srv6_end_m_gtp6_d_di,
+};
+
+const static char *const srv6_end_m_gtp6_d_di_nodes[] = {
+  "srv6-end-m-gtp6-d-di",
+  NULL,
+};
+
+const static char *const *const dpo_nodes[DPO_PROTO_NUM] = {
+  [DPO_PROTO_IP6] = srv6_end_m_gtp6_d_di_nodes,
+};
+
+static u8 fn_name[] = "SRv6-End.M.GTP6.D.DI-plugin";
+static u8 keyword_str[] = "end.m.gtp6.d.di";
+static u8 def_str[] =
+  "Endpoint function with drop-in dencapsulation for IPv6/GTP tunnel";
+static u8 param_str[] = "<sr-prefix>/<sr-prefixlen> [nhtype <nhtype>]";
+
+static u8 *
+clb_format_srv6_end_m_gtp6_d_di (u8 * s, va_list * args)
+{
+  srv6_end_gtp6_param_t *ls_mem = va_arg (*args, void *);
+
+  s = format (s, "SRv6 End gtp6.d Drop-in\n\t");
+
+  s =
+    format (s, "SR Prefix: %U/%d", format_ip6_address, &ls_mem->sr_prefix,
+           ls_mem->sr_prefixlen);
+
+  if (ls_mem->nhtype != SRV6_NHTYPE_NONE)
+    {
+      if (ls_mem->nhtype == SRV6_NHTYPE_IPV4)
+       s = format (s, ", NHType IPv4\n");
+      else if (ls_mem->nhtype == SRV6_NHTYPE_IPV6)
+       s = format (s, ", NHType IPv6\n");
+      else if (ls_mem->nhtype == SRV6_NHTYPE_NON_IP)
+       s = format (s, ", NHType Non-IP\n");
+      else
+       s = format (s, ", NHType Unknow(%d)\n", ls_mem->nhtype);
+    }
+  else
+    s = format (s, "\n");
+
+  return s;
+}
+
+static uword
+clb_unformat_srv6_end_m_gtp6_d_di (unformat_input_t * input, va_list * args)
+{
+  void **plugin_mem_p = va_arg (*args, void **);
+  srv6_end_gtp6_param_t *ls_mem;
+  ip6_address_t sr_prefix;
+  u32 sr_prefixlen = 0;
+  u8 nhtype;
+
+  if (unformat (input, "end.m.gtp6.d.di %U/%d nhtype ipv4",
+               unformat_ip6_address, &sr_prefix, &sr_prefixlen))
+    {
+      nhtype = SRV6_NHTYPE_IPV4;
+    }
+  else if (unformat (input, "end.m.gtp6.d.di %U/%d nhtype ipv6",
+                    unformat_ip6_address, &sr_prefix, &sr_prefixlen))
+    {
+      nhtype = SRV6_NHTYPE_IPV6;
+    }
+  else if (unformat (input, "end.m.gtp6.d.di %U/%d nhtype non-ip",
+                    unformat_ip6_address, &sr_prefix, &sr_prefixlen))
+    {
+      nhtype = SRV6_NHTYPE_NON_IP;
+    }
+  else if (unformat (input, "end.m.gtp6.d.di %U/%d",
+                    unformat_ip6_address, &sr_prefix, &sr_prefixlen))
+    {
+      nhtype = SRV6_NHTYPE_NONE;
+    }
+  else
+    {
+      return 0;
+    }
+
+  ls_mem = clib_mem_alloc_aligned_at_offset (sizeof *ls_mem, 0, 0, 1);
+  clib_memset (ls_mem, 0, sizeof *ls_mem);
+  *plugin_mem_p = ls_mem;
+
+  ls_mem->sr_prefix = sr_prefix;
+  ls_mem->sr_prefixlen = sr_prefixlen;
+  ls_mem->nhtype = nhtype;
+
+  return 1;
+}
+
+static int
+clb_creation_srv6_end_m_gtp6_d_di (ip6_sr_localsid_t * localsid)
+{
+  return 0;
+}
+
+static int
+clb_removal_srv6_end_m_gtp6_d_di (ip6_sr_localsid_t * localsid)
+{
+  srv6_end_gtp6_param_t *ls_mem;
+
+  ls_mem = localsid->plugin_mem;
+
+  clib_mem_free (ls_mem);
+
+  return 0;
+}
+
+static clib_error_t *
+srv6_end_m_gtp6_d_di_init (vlib_main_t * vm)
+{
+  srv6_end_main_v6_decap_di_t *sm = &srv6_end_main_v6_decap_di;
+  ip6srv_combo_header_t *ip6;
+  dpo_type_t dpo_type;
+  vlib_node_t *node;
+  u32 rc;
+
+  sm->vlib_main = vm;
+  sm->vnet_main = vnet_get_main ();
+
+  node = vlib_get_node_by_name (vm, (u8 *) "srv6-end-m-gtp6-d-di");
+  sm->end_m_gtp6_d_di_node_index = node->index;
+
+  node = vlib_get_node_by_name (vm, (u8 *) "error-drop");
+  sm->error_node_index = node->index;
+
+  ip6 = &sm->cache_hdr;
+
+  clib_memset_u8 (ip6, 0, sizeof (ip6srv_combo_header_t));
+
+  // IPv6 header (default)
+  ip6->ip.ip_version_traffic_class_and_flow_label = 0x60;
+  ip6->ip.hop_limit = 64;
+  ip6->ip.protocol = IPPROTO_IPV6_ROUTE;
+
+  // SR header (default)
+  ip6->sr.type = 4;
+
+  dpo_type = dpo_register_new_type (&dpo_vft, dpo_nodes);
+
+  rc = sr_localsid_register_function (vm, fn_name, keyword_str, def_str, param_str, 128,       //prefix len
+                                     &dpo_type,
+                                     clb_format_srv6_end_m_gtp6_d_di,
+                                     clb_unformat_srv6_end_m_gtp6_d_di,
+                                     clb_creation_srv6_end_m_gtp6_d_di,
+                                     clb_removal_srv6_end_m_gtp6_d_di);
+  if (rc < 0)
+    clib_error_return (0, "SRv6 Endpoint GTP6.D.DI LocalSID function"
+                      "couldn't be registered");
+  return 0;
+}
+
+/* *INDENT-OFF* */
+VNET_FEATURE_INIT (srv6_end_m_gtp6_d_di, static) =
+{
+  .arc_name = "ip6-unicast",
+  .node_name = "srv6-end-m-gtp6-d-di",
+  .runs_before = 0,
+};
+
+VLIB_INIT_FUNCTION (srv6_end_m_gtp6_d_di_init);
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/srv6-mobile/gtp6_e.c b/src/plugins/srv6-mobile/gtp6_e.c
new file mode 100644 (file)
index 0000000..4bc8d97
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * srv6_end_m_gtp6_e.c
+ *
+ * Copyright (c) 2019 Arrcus Inc and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/adj/adj.h>
+#include <vnet/plugin/plugin.h>
+#include <vpp/app/version.h>
+#include <srv6-mobile/mobile.h>
+
+srv6_end_main_v6_t srv6_end_main_v6;
+
+static void
+clb_dpo_lock_srv6_end_m_gtp6_e (dpo_id_t * dpo)
+{
+}
+
+static void
+clb_dpo_unlock_srv6_end_m_gtp6_e (dpo_id_t * dpo)
+{
+}
+
+static u8 *
+clb_dpo_format_srv6_end_m_gtp6_e (u8 * s, va_list * args)
+{
+  index_t index = va_arg (*args, index_t);
+  CLIB_UNUSED (u32 indent) = va_arg (*args, u32);
+
+  return (format (s, "SR: dynamic_proxy_index:[%u]", index));
+}
+
+const static dpo_vft_t dpo_vft = {
+  .dv_lock = clb_dpo_lock_srv6_end_m_gtp6_e,
+  .dv_unlock = clb_dpo_unlock_srv6_end_m_gtp6_e,
+  .dv_format = clb_dpo_format_srv6_end_m_gtp6_e,
+};
+
+const static char *const srv6_end_m_gtp6_e_nodes[] = {
+  "srv6-end-m-gtp6-e",
+  NULL,
+};
+
+const static char *const *const dpo_nodes[DPO_PROTO_NUM] = {
+  [DPO_PROTO_IP6] = srv6_end_m_gtp6_e_nodes,
+};
+
+static u8 fn_name[] = "SRv6-End.M.GTP6.E-plugin";
+static u8 keyword_str[] = "end.m.gtp6.e";
+static u8 def_str[] =
+  "Endpoint function with encapsulation for IPv6/GTP tunnel";
+static u8 param_str[] = "";
+
+static u8 *
+clb_format_srv6_end_m_gtp6_e (u8 * s, va_list * args)
+{
+  s = format (s, "SRv6 End format function unsupported.");
+  return s;
+}
+
+static uword
+clb_unformat_srv6_end_m_gtp6_e (unformat_input_t * input, va_list * args)
+{
+  if (!unformat (input, "end.m.gtp6.e"))
+    return 0;
+  return 1;
+}
+
+static int
+clb_creation_srv6_end_m_gtp6_e (ip6_sr_localsid_t * localsid)
+{
+  return 0;
+}
+
+static int
+clb_removal_srv6_end_m_gtp6_e (ip6_sr_localsid_t * localsid)
+{
+  return 0;
+}
+
+static clib_error_t *
+srv6_end_m_gtp6_e_init (vlib_main_t * vm)
+{
+  srv6_end_main_v6_t *sm = &srv6_end_main_v6;
+  ip6_header_t *ip6 = &sm->cache_hdr.ip6;
+  udp_header_t *udp = &sm->cache_hdr.udp;
+  gtpu_header_t *gtpu = &sm->cache_hdr.gtpu;
+  dpo_type_t dpo_type;
+  vlib_node_t *node;
+  u32 rc;
+
+  sm->vlib_main = vm;
+  sm->vnet_main = vnet_get_main ();
+
+  node = vlib_get_node_by_name (vm, (u8 *) "srv6-end-m-gtp6-e");
+  sm->end_m_gtp6_e_node_index = node->index;
+
+  node = vlib_get_node_by_name (vm, (u8 *) "error-drop");
+  sm->error_node_index = node->index;
+
+  // clear the pre cached packet
+  clib_memset_u8 (ip6, 0, sizeof (ip6_gtpu_header_t));
+
+  // set defaults
+  ip6->ip_version_traffic_class_and_flow_label = 0x60;
+  ip6->protocol = IP_PROTOCOL_UDP;
+  ip6->hop_limit = 64;
+
+  udp->dst_port = clib_host_to_net_u16 (SRV6_GTP_UDP_DST_PORT);
+
+  gtpu->ver_flags = GTPU_V1_VER | GTPU_PT_GTP;
+  gtpu->type = GTPU_TYPE_GTPU;
+
+  dpo_type = dpo_register_new_type (&dpo_vft, dpo_nodes);
+
+  rc = sr_localsid_register_function (vm, fn_name, keyword_str, def_str, param_str, 128,       //prefix len
+                                     &dpo_type,
+                                     clb_format_srv6_end_m_gtp6_e,
+                                     clb_unformat_srv6_end_m_gtp6_e,
+                                     clb_creation_srv6_end_m_gtp6_e,
+                                     clb_removal_srv6_end_m_gtp6_e);
+  if (rc < 0)
+    clib_error_return (0, "SRv6 Endpoint GTP6.E LocalSID function"
+                      "couldn't be registered");
+  return 0;
+}
+
+/* *INDENT-OFF* */
+VNET_FEATURE_INIT (srv6_end_m_gtp6_e, static) =
+{
+  .arc_name = "ip6-unicast",
+  .node_name = "srv6-end-m-gtp6-e",
+  .runs_before = 0,
+};
+
+VLIB_INIT_FUNCTION (srv6_end_m_gtp6_e_init);
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/srv6-mobile/mobile.h b/src/plugins/srv6-mobile/mobile.h
new file mode 100644 (file)
index 0000000..8be9dc2
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * srv6_end.h
+ *
+ * Copyright (c) 2019 Arrcus Inc and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __included_srv6_end_h__
+#define __included_srv6_end_h__
+
+#include <vnet/vnet.h>
+#include <vnet/ip/ip.h>
+#include <vnet/srv6/sr.h>
+#include <vnet/srv6/sr_packet.h>
+
+#include <vppinfra/error.h>
+#include <vppinfra/elog.h>
+
+#define SRV6_GTP_UDP_DST_PORT 2152
+
+#define SRV6_NHTYPE_NONE       0
+#define SRV6_NHTYPE_IPV4       1
+#define SRV6_NHTYPE_IPV6       2
+#define SRV6_NHTYPE_NON_IP     3
+
+#ifndef IP_PROTOCOL_NONE
+#define IP_PROTOCOL_NONE       59
+#endif
+
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#define BITALIGN2(A,B)          A; B
+#define BITALIGN3(A,B,C)        A; B; C
+#else
+#define BITALIGN2(A,B)          B; A
+#define BITALIGN3(A,B,C)        C; B; A
+#endif
+
+#define GTPU_EXTHDR_FLAG                0x04
+#define GTPU_EXTHDR_PDU_SESSION         0x85
+
+#define SRH_TAG_ECHO_REPLY              0x0008
+#define SRH_TAG_ECHO_REQUEST            0x0004
+#define SRH_TAG_ERROR_INDICATION        0x0002
+#define SRH_TAG_END_MARKER              0x0001
+
+/* *INDENT-OFF* */
+typedef struct
+{
+  u16 seq;
+  u8 npdu_num;
+  u8 nextexthdr;
+} __attribute__ ((packed)) gtpu_exthdr_t;
+/* *INDENT-ON* */
+
+/* *INDENT-OFF* */
+typedef struct
+{
+  u8 ver_flags;
+  u8 type;
+  u16 length;     /* length in octets of the payload */
+  u32 teid;
+  gtpu_exthdr_t ext[0];
+} __attribute__ ((packed)) gtpu_header_t;
+/* *INDENT-ON* */
+
+#define GTPU_TYPE_ECHO_REQUEST          1
+#define GTPU_TYPE_ECHO_REPLY            2
+#define GTPU_TYPE_ERROR_INDICATION      26
+#define GTPU_TYPE_END_MARKER            254
+#define GTPU_TYPE_GTPU                  255
+
+/* *INDENT-OFF* */
+typedef struct
+{
+  BITALIGN2 (u8 ppi:3,
+             u8 spare:5);
+
+  u8 padding[3];
+} __attribute__ ((packed)) gtpu_paging_policy_t;
+/* *INDENT-ON* */
+
+/* *INDENT-OFF* */
+typedef struct
+{
+  u8 exthdrlen;
+  BITALIGN2(u8 type:4,
+            u8 spare:4);
+  union {
+    struct gtpu_qfi_bits {BITALIGN3(u8 p:1,
+                                   u8 r:1,
+                                   u8 qfi:6);
+    } bits;
+
+    u8 val;
+  } u;
+
+  gtpu_paging_policy_t  paging[0];
+  u8 nextexthdr;
+} __attribute__ ((packed)) gtpu_pdu_session_t;
+/* *INDENT-ON* */
+
+#define GTPU_PDU_SESSION_P_BIT_MASK     0x80
+#define GTPU_PDU_SESSION_R_BIT_MASK     0x40
+#define GTPU_PDU_SESSION_QFI_MASK       0x3f
+
+#define SRV6_PDU_SESSION_U_BIT_MASK     0x01
+#define SRV6_PDU_SESSION_R_BIT_MASK     0x02
+#define SRV6_PDU_SESSION_QFI_MASK       0xfC
+
+/* *INDENT-OFF* */
+typedef struct
+{
+  ip4_header_t ip4;            /* 20 bytes */
+  udp_header_t udp;            /* 8 bytes */
+  gtpu_header_t gtpu;        /* 8 bytes */
+} __attribute__ ((packed)) ip4_gtpu_header_t;
+/* *INDENT-ON* */
+
+/* *INDENT-OFF* */
+typedef struct
+{
+  ip6_header_t ip6;          /* 40 bytes */
+  udp_header_t udp;          /* 8 bytes */
+  gtpu_header_t gtpu;        /* 8 bytes */
+} __attribute__ ((packed)) ip6_gtpu_header_t;
+/* *INDENT-ON* */
+
+#define GTPU_V1_VER   (1<<5)
+
+#define GTPU_PT_GTP   (1<<4)
+
+typedef struct srv6_end_gtp6_param_s
+{
+  u8 nhtype;
+
+  ip6_address_t sr_prefix;
+  u32 sr_prefixlen;
+} srv6_end_gtp6_param_t;
+
+typedef struct srv6_end_gtp4_param_s
+{
+  u8 nhtype;
+
+  ip6_address_t sr_prefix;
+  u32 sr_prefixlen;
+
+  ip6_address_t v6src_prefix;
+  u32 v6src_prefixlen;
+
+  u32 v4src_position;
+} srv6_end_gtp4_param_t;
+
+typedef struct srv6_end_main_v4_s
+{
+  vlib_main_t *vlib_main;
+  vnet_main_t *vnet_main;
+
+  u32 end_m_gtp4_e_node_index;
+  u32 error_node_index;
+
+  u32 dst_p_len;               // dst prefix len
+  u32 src_p_len;               // src prefix len
+
+  ip4_gtpu_header_t cache_hdr;
+
+} srv6_end_main_v4_t;
+
+typedef struct srv6_end_main_v4_decap_s
+{
+  vlib_main_t *vlib_main;
+  vnet_main_t *vnet_main;
+
+  u32 end_m_gtp4_d_node_index;
+  u32 error_node_index;
+
+  ip6_header_t cache_hdr;
+} srv6_end_main_v4_decap_t;
+
+extern srv6_end_main_v4_t srv6_end_main_v4;
+extern srv6_end_main_v4_decap_t srv6_end_main_v4_decap;
+extern vlib_node_registration_t srv6_end_m_gtp4_e;
+
+typedef struct srv6_end_main_v6_s
+{
+  vlib_main_t *vlib_main;
+  vnet_main_t *vnet_main;
+
+  u32 end_m_gtp6_e_node_index;
+  u32 error_node_index;
+
+  ip6_gtpu_header_t cache_hdr;
+} srv6_end_main_v6_t;
+
+extern srv6_end_main_v6_t srv6_end_main_v6;
+extern vlib_node_registration_t srv6_end_m_gtp6_e;
+
+typedef struct srv6_end_main_v6_decap_s
+{
+  vlib_main_t *vlib_main;
+  vnet_main_t *vnet_main;
+
+  u32 end_m_gtp6_d_node_index;
+  u32 error_node_index;
+
+  ip6_header_t cache_hdr;
+} srv6_end_main_v6_decap_t;
+
+extern srv6_end_main_v6_decap_t srv6_end_main_v6_decap;
+extern vlib_node_registration_t srv6_end_m_gtp6_d;
+
+typedef struct srv6_end_main_v6_decap_di_s
+{
+  vlib_main_t *vlib_main;
+  vnet_main_t *vnet_main;
+
+  u32 end_m_gtp6_d_di_node_index;
+  u32 error_node_index;
+
+  ip6srv_combo_header_t cache_hdr;
+} srv6_end_main_v6_decap_di_t;
+
+extern srv6_end_main_v6_decap_di_t srv6_end_main_v6_decap_di;
+extern vlib_node_registration_t srv6_end_m_gtp6_d_di;
+
+#endif /* __included_srv6_end_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/srv6-mobile/node.c b/src/plugins/srv6-mobile/node.c
new file mode 100644 (file)
index 0000000..0c2c04e
--- /dev/null
@@ -0,0 +1,1533 @@
+/*
+ * Copyright (c) 2019 Arrcus Inc and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vppinfra/error.h>
+#include <vppinfra/hash.h>
+#include <srv6-mobile/mobile.h>
+
+extern ip6_address_t sr_pr_encaps_src;
+
+typedef struct
+{
+  ip6_address_t src, dst;
+  ip6_address_t sr_prefix;
+  u16 sr_prefixlen;
+  u32 teid;
+} srv6_end_rewrite_trace_t;
+
+static u16 srh_tagfield[256] = {
+  /* 0 */
+  0x0,
+  /* 1 : Echo Request */
+  0x0004,
+  /* 2 : Echo Reply */
+  0x0008,
+  /* 3 - 7 */
+  0x0, 0x0, 0x0, 0x0, 0x0,
+  /* 8 - 15 */
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  /* 16 - 23 */
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  /* 24 - 25 */
+  0x0, 0x0,
+  /* 26 : Error Indication */
+  0x0002,
+  /* 27 - 31 */
+  0x0, 0x0, 0x0, 0x0, 0x0,
+  /* 32 - 247 */
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  /* 248 - 253 */
+  0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+  /* 254 : End Maker */
+  0x0001,
+  /* 255 : G_PDU */
+  0x0
+};
+
+static u8 *
+format_srv6_end_rewrite_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  srv6_end_rewrite_trace_t *t = va_arg (*args, srv6_end_rewrite_trace_t *);
+
+  return format (s, "SRv6-END-rewrite: src %U dst %U\n\tTEID: 0x%x",
+                format_ip4_address, &t->src, format_ip4_address, &t->dst,
+                clib_net_to_host_u32 (t->teid));
+}
+
+static u8 *
+format_srv6_end_rewrite_trace6 (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  srv6_end_rewrite_trace_t *t = va_arg (*args, srv6_end_rewrite_trace_t *);
+
+  return format (s,
+                "SRv6-END-rewrite: src %U dst %U\n\tTEID: 0x%x\n\tsr_prefix: %U/%d",
+                format_ip6_address, &t->src, format_ip6_address, &t->dst,
+                clib_net_to_host_u32 (t->teid), format_ip6_address,
+                &t->sr_prefix, t->sr_prefixlen);
+}
+
+#define foreach_srv6_end_v4_error \
+  _(M_GTP4_E_PACKETS, "srv6 End.M.GTP4.E packets") \
+  _(M_GTP4_E_BAD_PACKETS, "srv6 End.M.GTP4.E bad packets")
+
+#define foreach_srv6_end_v4_d_error \
+  _(M_GTP4_D_PACKETS, "srv6 End.M.GTP4.D packets") \
+  _(M_GTP4_D_BAD_PACKETS, "srv6 End.M.GTP4.D bad packets")
+
+#define foreach_srv6_end_v6_e_error \
+  _(M_GTP6_E_PACKETS, "srv6 End.M.GTP6.E packets") \
+  _(M_GTP6_E_BAD_PACKETS, "srv6 End.M.GTP6.E bad packets")
+
+#define foreach_srv6_end_v6_d_error \
+  _(M_GTP6_D_PACKETS, "srv6 End.M.GTP6.D packets") \
+  _(M_GTP6_D_BAD_PACKETS, "srv6 End.M.GTP6.D bad packets")
+
+#define foreach_srv6_end_v6_d_di_error \
+  _(M_GTP6_D_DI_PACKETS, "srv6 End.M.GTP6.D.DI packets") \
+  _(M_GTP6_D_DI_BAD_PACKETS, "srv6 End.M.GTP6.D.DI bad packets")
+
+typedef enum
+{
+#define _(sym,str) SRV6_END_ERROR_##sym,
+  foreach_srv6_end_v4_error
+#undef _
+    SRV6_END_N_V4_ERROR,
+} srv6_end_error_v4_t;
+
+typedef enum
+{
+#define _(sym,str) SRV6_END_ERROR_##sym,
+  foreach_srv6_end_v4_d_error
+#undef _
+    SRV6_END_N_V4_D_ERROR,
+} srv6_end_error_v4_d_t;
+
+typedef enum
+{
+#define _(sym,str) SRV6_END_ERROR_##sym,
+  foreach_srv6_end_v6_e_error
+#undef _
+    SRV6_END_N_V6_E_ERROR,
+} srv6_end_error_v6_e_t;
+
+typedef enum
+{
+#define _(sym,str) SRV6_END_ERROR_##sym,
+  foreach_srv6_end_v6_d_error
+#undef _
+    SRV6_END_N_V6_D_ERROR,
+} srv6_end_error_v6_d_t;
+
+typedef enum
+{
+#define _(sym,str) SRV6_END_ERROR_##sym,
+  foreach_srv6_end_v6_d_di_error
+#undef _
+    SRV6_END_N_V6_D_DI_ERROR,
+} srv6_end_error_v6_d_di_t;
+
+static char *srv6_end_error_v4_strings[] = {
+#define _(sym,string) string,
+  foreach_srv6_end_v4_error
+#undef _
+};
+
+static char *srv6_end_error_v6_e_strings[] = {
+#define _(sym,string) string,
+  foreach_srv6_end_v6_e_error
+#undef _
+};
+
+static char *srv6_end_error_v6_d_strings[] = {
+#define _(sym,string) string,
+  foreach_srv6_end_v6_d_error
+#undef _
+};
+
+static char *srv6_end_error_v6_d_di_strings[] = {
+#define _(sym,string) string,
+  foreach_srv6_end_v6_d_di_error
+#undef _
+};
+
+typedef enum
+{
+  SRV6_END_M_GTP4_E_NEXT_DROP,
+  SRV6_END_M_GTP4_E_NEXT_LOOKUP,
+  SRV6_END_M_GTP4_E_N_NEXT,
+} srv6_end_m_gtp4_e_next_t;
+
+typedef enum
+{
+  SRV6_END_M_GTP6_E_NEXT_DROP,
+  SRV6_END_M_GTP6_E_NEXT_LOOKUP,
+  SRV6_END_M_GTP6_E_N_NEXT,
+} srv6_end_m_gtp6_e_next_t;
+
+typedef enum
+{
+  SRV6_END_M_GTP6_D_NEXT_DROP,
+  SRV6_END_M_GTP6_D_NEXT_LOOKUP,
+  SRV6_END_M_GTP6_D_N_NEXT,
+} srv6_end_m_gtp6_d_next_t;
+
+typedef enum
+{
+  SRV6_END_M_GTP6_D_DI_NEXT_DROP,
+  SRV6_END_M_GTP6_D_DI_NEXT_LOOKUP,
+  SRV6_END_M_GTP6_D_DI_N_NEXT,
+} srv6_end_m_gtp6_d_di_next_t;
+
+static inline u16
+hash_uword_to_u16 (uword * key)
+{
+  u16 *val;
+  val = (u16 *) key;
+#if uword_bits == 64
+  return val[0] ^ val[1] ^ val[2] ^ val[3];
+#else
+  return val[0] ^ val[1];
+#endif
+}
+
+static inline void
+gtp_type_set (gtpu_header_t * gtpu, u16 tag)
+{
+  u16 val;
+
+  val = clib_net_to_host_u16 (tag);
+  if (val & SRH_TAG_ECHO_REPLY)
+    gtpu->type = GTPU_TYPE_ECHO_REPLY;
+  else if (val & SRH_TAG_ECHO_REQUEST)
+    gtpu->type = GTPU_TYPE_ECHO_REQUEST;
+  else if (val & SRH_TAG_ERROR_INDICATION)
+    gtpu->type = GTPU_TYPE_ERROR_INDICATION;
+  else if (val & SRH_TAG_END_MARKER)
+    gtpu->type = GTPU_TYPE_END_MARKER;
+}
+
+// Function for SRv6 GTP4.E function.
+VLIB_NODE_FN (srv6_end_m_gtp4_e) (vlib_main_t * vm,
+                                 vlib_node_runtime_t * node,
+                                 vlib_frame_t * frame)
+{
+  srv6_end_main_v4_t *sm = &srv6_end_main_v4;
+  ip6_sr_main_t *sm2 = &sr_main;
+  u32 n_left_from, next_index, *from, *to_next;
+  u32 thread_index = vm->thread_index;
+
+  u32 good_n = 0, bad_n = 0;
+
+  from = vlib_frame_vector_args (frame);
+  n_left_from = frame->n_vectors;
+  next_index = node->cached_next_index;
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         u32 bi0;
+         vlib_buffer_t *b0;
+         ip6_sr_localsid_t *ls0;
+         srv6_end_gtp4_param_t *ls_param;
+
+         ip6srv_combo_header_t *ip6srv0;
+         ip6_address_t src0, dst0;
+
+         ip4_gtpu_header_t *hdr0 = NULL;
+         uword len0;
+
+         u32 next0 = SRV6_END_M_GTP4_E_NEXT_LOOKUP;
+
+         // defaults
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+
+         b0 = vlib_get_buffer (vm, bi0);
+         ls0 =
+           pool_elt_at_index (sm2->localsids,
+                              vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
+
+         ls_param = (srv6_end_gtp4_param_t *) ls0->plugin_mem;
+
+         ip6srv0 = vlib_buffer_get_current (b0);
+         src0 = ip6srv0->ip.src_address;
+         dst0 = ip6srv0->ip.dst_address;
+
+         len0 = vlib_buffer_length_in_chain (vm, b0);
+
+         if ((ip6srv0->ip.protocol == IPPROTO_IPV6_ROUTE
+              && len0 <
+              sizeof (ip6srv_combo_header_t) + ip6srv0->sr.length * 8)
+             || (len0 < sizeof (ip6_header_t)))
+           {
+             next0 = SRV6_END_M_GTP4_E_NEXT_DROP;
+
+             bad_n++;
+           }
+         else
+           {
+             u16 tag = 0;
+             u32 teid;
+             u8 *teid8p = (u8 *) & teid;
+             u8 qfi = 0;
+             u32 index;
+             u32 offset, shift;
+             u32 hdrlen = 0;
+             uword key;
+             u16 port;
+             ip4_address_t dst4;
+             void *p;
+
+             // we need to be sure there is enough space before
+             // ip6srv0 header, there is some extra space
+             // in the pre_data area for this kind of
+             // logic
+
+             // jump over variable length data
+             // not sure about the length
+             if (ip6srv0->ip.protocol == IPPROTO_IPV6_ROUTE)
+               {
+                 tag = ip6srv0->sr.tag;
+
+                 vlib_buffer_advance (b0,
+                                      (word) sizeof (ip6srv_combo_header_t) +
+                                      ip6srv0->sr.length * 8);
+               }
+             else
+               {
+                 vlib_buffer_advance (b0, (word) sizeof (ip6_header_t));
+               }
+
+             // get length of encapsulated IPv6 packet (the remaining part)
+             p = vlib_buffer_get_current (b0);
+
+             len0 = vlib_buffer_length_in_chain (vm, b0);
+
+             offset = ls0->localsid_len / 8;
+             shift = ls0->localsid_len % 8;
+
+             if (PREDICT_TRUE (shift == 0))
+               {
+                 clib_memcpy_fast (&dst4.as_u8[0], &dst0.as_u8[offset], 4);
+
+                 qfi = dst0.as_u8[offset + 4];
+
+                 clib_memcpy_fast (teid8p, &dst0.as_u8[offset + 5], 4);
+               }
+             else
+               {
+                 for (index = 0; index < 4; index++)
+                   {
+                     dst4.as_u8[index] = dst0.as_u8[offset + index] << shift;
+                     dst4.as_u8[index] |=
+                       dst0.as_u8[offset + index + 1] >> (8 - shift);
+                   }
+
+                 qfi |= dst0.as_u8[offset + 4] << shift;
+                 qfi |= dst0.as_u8[offset + 5] >> (8 - shift);
+
+                 for (index = 0; index < 4; index++)
+                   {
+                     *teid8p = dst0.as_u8[offset + 5 + index] << shift;
+                     *teid8p |=
+                       dst0.as_u8[offset + 6 + index] >> (8 - shift);
+                     teid8p++;
+                   }
+               }
+
+             if (qfi)
+               {
+                 hdrlen =
+                   sizeof (gtpu_exthdr_t) + sizeof (gtpu_pdu_session_t);
+                 len0 += hdrlen;
+               }
+             hdrlen += sizeof (ip4_gtpu_header_t);
+
+             // IPv4 GTP-U header creation.
+             vlib_buffer_advance (b0, -(word) hdrlen);
+
+             hdr0 = vlib_buffer_get_current (b0);
+
+             clib_memcpy_fast (hdr0, &sm->cache_hdr,
+                               sizeof (ip4_gtpu_header_t));
+
+             hdr0->ip4.dst_address.as_u32 = dst4.as_u32;
+
+             hdr0->gtpu.teid = teid;
+             hdr0->gtpu.length = clib_host_to_net_u16 (len0);
+
+             if (PREDICT_FALSE (tag != 0))
+               {
+                 gtp_type_set (&hdr0->gtpu, tag);
+               }
+
+             if (qfi)
+               {
+                 u8 type = 0;
+                 gtpu_pdu_session_t *sess;
+
+                 hdr0->gtpu.ver_flags |= GTPU_EXTHDR_FLAG;
+                 hdr0->gtpu.ext->seq = 0;
+                 hdr0->gtpu.ext->npdu_num = 0;
+                 hdr0->gtpu.ext->nextexthdr = GTPU_EXTHDR_PDU_SESSION;
+
+                 type = qfi & SRV6_PDU_SESSION_U_BIT_MASK;
+
+                 qfi =
+                   ((qfi & SRV6_PDU_SESSION_QFI_MASK) >> 2) |
+                   ((qfi & SRV6_PDU_SESSION_R_BIT_MASK) << 5);
+
+                 sess =
+                   (gtpu_pdu_session_t *) (((char *) hdr0) +
+                                           sizeof (ip4_gtpu_header_t) +
+                                           sizeof (gtpu_exthdr_t));
+                 sess->exthdrlen = 1;
+                 sess->type = type;
+                 sess->spare = 0;
+                 sess->u.val = qfi;
+                 sess->nextexthdr = 0;
+               }
+
+             offset = ls_param->v4src_position / 8;
+             shift = ls_param->v4src_position % 8;
+
+             if (PREDICT_TRUE (shift == 0))
+               {
+                 for (index = 0; index < 4; index++)
+                   {
+                     hdr0->ip4.src_address.as_u8[index] =
+                       src0.as_u8[offset + index];
+                   }
+               }
+             else
+               {
+                 for (index = 0; index < 4; index++)
+                   {
+                     hdr0->ip4.src_address.as_u8[index] =
+                       src0.as_u8[offset + index] << shift;
+                     hdr0->ip4.src_address.as_u8[index] |=
+                       src0.as_u8[offset + index + 1] >> (8 - shift);
+                   }
+               }
+
+             key = hash_memory (p, len0, 0);
+             port = hash_uword_to_u16 (&key);
+             hdr0->udp.src_port = port;
+
+             hdr0->udp.length = clib_host_to_net_u16 (len0 +
+                                                      sizeof (udp_header_t) +
+                                                      sizeof
+                                                      (gtpu_header_t));
+
+             hdr0->ip4.length = clib_host_to_net_u16 (len0 +
+                                                      sizeof
+                                                      (ip4_gtpu_header_t));
+
+             hdr0->ip4.checksum = ip4_header_checksum (&hdr0->ip4);
+
+             good_n++;
+
+             if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE) &&
+                 PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+               {
+                 srv6_end_rewrite_trace_t *tr =
+                   vlib_add_trace (vm, node, b0, sizeof (*tr));
+                 clib_memcpy (tr->src.as_u8, hdr0->ip4.src_address.as_u8,
+                              sizeof (tr->src.as_u8));
+                 clib_memcpy (tr->dst.as_u8, hdr0->ip4.dst_address.as_u8,
+                              sizeof (tr->dst.as_u8));
+                 tr->teid = hdr0->gtpu.teid;
+               }
+           }
+
+         vlib_increment_combined_counter
+           (((next0 ==
+              SRV6_END_M_GTP4_E_NEXT_DROP) ? &(sm2->sr_ls_invalid_counters) :
+             &(sm2->sr_ls_valid_counters)), thread_index,
+            ls0 - sm2->localsids, 1, vlib_buffer_length_in_chain (vm, b0));
+
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+                                          n_left_to_next, bi0, next0);
+       }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  vlib_node_increment_counter (vm, sm->end_m_gtp4_e_node_index,
+                              SRV6_END_ERROR_M_GTP4_E_BAD_PACKETS, bad_n);
+
+  vlib_node_increment_counter (vm, sm->end_m_gtp4_e_node_index,
+                              SRV6_END_ERROR_M_GTP4_E_PACKETS, good_n);
+
+  return frame->n_vectors;
+}
+
+VLIB_REGISTER_NODE (srv6_end_m_gtp4_e) =
+{
+  .name = "srv6-end-m-gtp4-e",.vector_size = sizeof (u32),.format_trace =
+    format_srv6_end_rewrite_trace,.type = VLIB_NODE_TYPE_INTERNAL,.n_errors =
+    ARRAY_LEN (srv6_end_error_v4_strings),.error_strings =
+    srv6_end_error_v4_strings,.n_next_nodes =
+    SRV6_END_M_GTP4_E_N_NEXT,.next_nodes =
+  {
+  [SRV6_END_M_GTP4_E_NEXT_DROP] =
+      "error-drop",[SRV6_END_M_GTP4_E_NEXT_LOOKUP] = "ip4-lookup",}
+,};
+
+// Function for SRv6 GTP6.E function
+VLIB_NODE_FN (srv6_end_m_gtp6_e) (vlib_main_t * vm,
+                                 vlib_node_runtime_t * node,
+                                 vlib_frame_t * frame)
+{
+  srv6_end_main_v6_t *sm = &srv6_end_main_v6;
+  ip6_sr_main_t *sm2 = &sr_main;
+  u32 n_left_from, next_index, *from, *to_next;
+  u32 thread_index = vm->thread_index;
+
+  u32 good_n = 0, bad_n = 0;
+
+  from = vlib_frame_vector_args (frame);
+  n_left_from = frame->n_vectors;
+  next_index = node->cached_next_index;
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         u32 bi0;
+         vlib_buffer_t *b0;
+         ip6_sr_localsid_t *ls0;
+
+         ip6srv_combo_header_t *ip6srv0;
+         ip6_address_t dst0, src0, seg0;
+
+         ip6_gtpu_header_t *hdr0 = NULL;
+         uword len0;
+         uword key;
+         u16 port;
+         u16 tag;
+         void *p;
+
+         u32 next0 = SRV6_END_M_GTP6_E_NEXT_LOOKUP;
+
+         // defaults
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+
+         b0 = vlib_get_buffer (vm, bi0);
+         ls0 =
+           pool_elt_at_index (sm2->localsids,
+                              vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
+
+         ip6srv0 = vlib_buffer_get_current (b0);
+         dst0 = ip6srv0->ip.dst_address;
+         src0 = ip6srv0->ip.src_address;
+         seg0 = ip6srv0->sr.segments[0];
+
+         tag = ip6srv0->sr.tag;
+
+         len0 = vlib_buffer_length_in_chain (vm, b0);
+
+         if ((ip6srv0->ip.protocol != IPPROTO_IPV6_ROUTE)
+             || (len0 <
+                 sizeof (ip6srv_combo_header_t) + 8 * ip6srv0->sr.length))
+           {
+             next0 = SRV6_END_M_GTP6_E_NEXT_DROP;
+
+             bad_n++;
+           }
+         else
+           {
+             // we need to be sure there is enough space before
+             // ip6srv0 header, there is some extra space
+             // in the pre_data area for this kind of
+             // logic
+
+             // jump over variable length data
+             // not sure about the length
+             vlib_buffer_advance (b0, (word) sizeof (ip6srv_combo_header_t) +
+                                  ip6srv0->sr.length * 8);
+
+             // get length of encapsulated IPv6 packet (the remaining part)
+             p = vlib_buffer_get_current (b0);
+
+             len0 = vlib_buffer_length_in_chain (vm, b0);
+
+             u32 teid;
+             u8 *teid8p = (u8 *) & teid;
+             u8 qfi = 0;
+             u16 index;
+             u16 offset, shift;
+             u32 hdrlen = 0;
+
+             index = ls0->localsid_len;
+             index += 8;
+             offset = index / 8;
+             shift = index % 8;
+
+             if (PREDICT_TRUE (shift == 0))
+               {
+                 clib_memcpy_fast (teid8p, &dst0.as_u8[offset], 4);
+
+                 qfi = dst0.as_u8[offset + 4];
+               }
+             else
+               {
+                 for (index = offset; index < offset + 4; index++)
+                   {
+                     *teid8p = dst0.as_u8[index] << shift;
+                     *teid8p |= dst0.as_u8[index + 1] >> (8 - shift);
+                     teid8p++;
+                   }
+
+                 qfi |= dst0.as_u8[offset + 4] << shift;
+                 qfi |= dst0.as_u8[offset + 5] >> (8 - shift);
+               }
+
+             if (qfi)
+               {
+                 hdrlen =
+                   sizeof (gtpu_exthdr_t) + sizeof (gtpu_pdu_session_t);
+                 len0 += hdrlen;
+               }
+             hdrlen += sizeof (ip6_gtpu_header_t);
+
+             vlib_buffer_advance (b0, -(word) hdrlen);
+
+             hdr0 = vlib_buffer_get_current (b0);
+
+             clib_memcpy_fast (hdr0, &sm->cache_hdr,
+                               sizeof (ip6_gtpu_header_t));
+
+             hdr0->gtpu.teid = teid;
+             hdr0->gtpu.length = clib_host_to_net_u16 (len0);
+
+             if (PREDICT_FALSE (tag != 0))
+               {
+                 gtp_type_set (&hdr0->gtpu, tag);
+               }
+
+             if (qfi)
+               {
+                 u8 type = 0;
+                 gtpu_pdu_session_t *sess;
+
+                 hdr0->gtpu.ver_flags |= GTPU_EXTHDR_FLAG;
+                 hdr0->gtpu.ext->seq = 0;
+                 hdr0->gtpu.ext->npdu_num = 0;
+                 hdr0->gtpu.ext->nextexthdr = GTPU_EXTHDR_PDU_SESSION;
+
+                 type = qfi & SRV6_PDU_SESSION_U_BIT_MASK;
+
+                 qfi =
+                   ((qfi & SRV6_PDU_SESSION_QFI_MASK) >> 2) |
+                   ((qfi & SRV6_PDU_SESSION_R_BIT_MASK) << 5);
+
+                 sess =
+                   (gtpu_pdu_session_t *) (((char *) hdr0) +
+                                           sizeof (ip6_gtpu_header_t) +
+                                           sizeof (gtpu_exthdr_t));
+                 sess->exthdrlen = 1;
+                 sess->type = type;
+                 sess->spare = 0;
+                 sess->u.val = qfi;
+                 sess->nextexthdr = 0;
+               }
+
+             hdr0->udp.length = clib_host_to_net_u16 (len0 +
+                                                      sizeof (udp_header_t) +
+                                                      sizeof
+                                                      (gtpu_header_t));
+
+             clib_memcpy_fast (hdr0->ip6.src_address.as_u8, src0.as_u8,
+                               sizeof (ip6_address_t));
+             clib_memcpy_fast (hdr0->ip6.dst_address.as_u8, &seg0.as_u8,
+                               sizeof (ip6_address_t));
+
+             hdr0->ip6.payload_length = clib_host_to_net_u16 (len0 +
+                                                              sizeof
+                                                              (udp_header_t)
+                                                              +
+                                                              sizeof
+                                                              (gtpu_header_t));
+
+             // UDP source port.
+             key = hash_memory (p, len0, 0);
+             port = hash_uword_to_u16 (&key);
+             hdr0->udp.src_port = port;
+
+             good_n++;
+
+             if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE) &&
+                 PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+               {
+                 srv6_end_rewrite_trace_t *tr =
+                   vlib_add_trace (vm, node, b0, sizeof (*tr));
+                 clib_memcpy (tr->src.as_u8, hdr0->ip6.src_address.as_u8,
+                              sizeof (ip6_address_t));
+                 clib_memcpy (tr->dst.as_u8, hdr0->ip6.dst_address.as_u8,
+                              sizeof (ip6_address_t));
+                 tr->teid = hdr0->gtpu.teid;
+               }
+           }
+
+         vlib_increment_combined_counter
+           (((next0 ==
+              SRV6_END_M_GTP6_E_NEXT_DROP) ? &(sm2->sr_ls_invalid_counters) :
+             &(sm2->sr_ls_valid_counters)), thread_index,
+            ls0 - sm2->localsids, 1, vlib_buffer_length_in_chain (vm, b0));
+
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+                                          n_left_to_next, bi0, next0);
+       }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  vlib_node_increment_counter (vm, sm->end_m_gtp6_e_node_index,
+                              SRV6_END_ERROR_M_GTP6_E_BAD_PACKETS, bad_n);
+
+  vlib_node_increment_counter (vm, sm->end_m_gtp6_e_node_index,
+                              SRV6_END_ERROR_M_GTP6_E_PACKETS, good_n);
+
+  return frame->n_vectors;
+}
+
+// Function for SRv6 GTP6.D function
+VLIB_NODE_FN (srv6_end_m_gtp6_d) (vlib_main_t * vm,
+                                 vlib_node_runtime_t * node,
+                                 vlib_frame_t * frame)
+{
+  srv6_end_main_v6_decap_t *sm = &srv6_end_main_v6_decap;
+  ip6_sr_main_t *sm2 = &sr_main;
+  u32 n_left_from, next_index, *from, *to_next;
+  u32 thread_index = vm->thread_index;
+
+  u32 good_n = 0, bad_n = 0;
+
+  from = vlib_frame_vector_args (frame);
+  n_left_from = frame->n_vectors;
+  next_index = node->cached_next_index;
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         u32 bi0;
+         vlib_buffer_t *b0;
+         ip6_sr_localsid_t *ls0;
+         srv6_end_gtp6_param_t *ls_param;
+
+         ip6_gtpu_header_t *hdr0 = NULL;
+         uword len0;
+
+         ip6_address_t seg0, src0;
+         u32 teid = 0;
+         u8 *teidp;
+         u8 gtpu_type = 0;
+         u8 qfi;
+         u8 *qfip = NULL;
+         u32 offset, shift;
+         u32 hdrlen;
+         ip6_header_t *encap = NULL;
+         gtpu_pdu_session_t *sess = NULL;
+
+         u32 next0 = SRV6_END_M_GTP6_D_NEXT_LOOKUP;
+
+         // defaults
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+
+         b0 = vlib_get_buffer (vm, bi0);
+         ls0 =
+           pool_elt_at_index (sm2->localsids,
+                              vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
+
+         ls_param = (srv6_end_gtp6_param_t *) ls0->plugin_mem;
+
+         hdr0 = vlib_buffer_get_current (b0);
+
+         hdrlen = sizeof (ip6_gtpu_header_t);
+
+         len0 = vlib_buffer_length_in_chain (vm, b0);
+
+         if ((hdr0->ip6.protocol != IP_PROTOCOL_UDP)
+             || (hdr0->udp.dst_port !=
+                 clib_host_to_net_u16 (SRV6_GTP_UDP_DST_PORT))
+             || (len0 < sizeof (ip6_gtpu_header_t)))
+           {
+             next0 = SRV6_END_M_GTP6_D_NEXT_DROP;
+
+             bad_n++;
+           }
+         else
+           {
+             seg0 = ls_param->sr_prefix;
+             src0 = hdr0->ip6.src_address;
+
+             gtpu_type = hdr0->gtpu.type;
+
+             teid = hdr0->gtpu.teid;
+             teidp = (u8 *) & teid;
+
+             if (hdr0->gtpu.ver_flags & GTPU_EXTHDR_FLAG)
+               {
+                 // Extention header.
+                 hdrlen += sizeof (gtpu_exthdr_t);
+                 if (hdr0->gtpu.ext->nextexthdr == GTPU_EXTHDR_PDU_SESSION)
+                   {
+                     // PDU Session Container.
+                     sess =
+                       (gtpu_pdu_session_t *) (((char *) hdr0) +
+                                               sizeof (ip6_gtpu_header_t) +
+                                               sizeof (gtpu_exthdr_t));
+                     qfi = sess->u.val & ~GTPU_PDU_SESSION_P_BIT_MASK;
+                     qfip = (u8 *) & qfi;
+
+                     hdrlen += sizeof (gtpu_pdu_session_t);
+
+                     if (sess->u.val & GTPU_PDU_SESSION_P_BIT_MASK)
+                       {
+                         hdrlen += sizeof (gtpu_paging_policy_t);
+                       }
+                   }
+               }
+
+             offset = ls_param->sr_prefixlen / 8;
+             shift = ls_param->sr_prefixlen % 8;
+
+             offset += 1;
+             if (PREDICT_TRUE (shift == 0))
+               {
+                 clib_memcpy_fast (&seg0.as_u8[offset], teidp, 4);
+                 if (qfip)
+                   {
+                     qfi =
+                       ((qfi & GTPU_PDU_SESSION_QFI_MASK) << 2) |
+                       ((qfi & GTPU_PDU_SESSION_R_BIT_MASK) >> 5);
+
+                     if (sess->type)
+                       {
+                         qfi |= SRV6_PDU_SESSION_U_BIT_MASK;
+                       }
+
+                     seg0.as_u8[offset + 4] = qfi;
+                   }
+               }
+             else
+               {
+                 int idx;
+
+                 for (idx = 0; idx < 4; idx++)
+                   {
+                     seg0.as_u8[offset + idx] |= teidp[idx] >> shift;
+                     seg0.as_u8[offset + idx + 1] |=
+                       teidp[idx] << (8 - shift);
+                   }
+
+                 if (qfip)
+                   {
+                     qfi =
+                       ((qfi & GTPU_PDU_SESSION_QFI_MASK) << 2) |
+                       ((qfi & ~GTPU_PDU_SESSION_R_BIT_MASK) >> 5);
+
+                     if (sess->type)
+                       {
+                         qfi |= SRV6_PDU_SESSION_U_BIT_MASK;
+                       }
+
+                     seg0.as_u8[offset + 4] |= qfi >> shift;
+                     seg0.as_u8[offset + 5] |= qfi << (8 - shift);
+                   }
+               }
+
+             // jump over variable length data
+             vlib_buffer_advance (b0, (word) hdrlen);
+
+             // get length of encapsulated IPv6 packet (the remaining part)
+             len0 = vlib_buffer_length_in_chain (vm, b0);
+
+             if (PREDICT_TRUE (gtpu_type == GTPU_TYPE_GTPU))
+               {
+                 encap = vlib_buffer_get_current (b0);
+               }
+
+             uword *p;
+             ip6srv_combo_header_t *ip6srv;
+             ip6_sr_policy_t *sr_policy = NULL;
+             ip6_sr_sl_t *sl = NULL;
+             u32 *sl_index;
+             u32 hdr_len;
+
+             p =
+               mhash_get (&sm2->sr_policies_index_hash,
+                          &ls_param->sr_prefix);
+             if (p)
+               {
+                 sr_policy = pool_elt_at_index (sm2->sr_policies, p[0]);
+               }
+
+             if (sr_policy)
+               {
+                 vec_foreach (sl_index, sr_policy->segments_lists)
+                 {
+                   sl = pool_elt_at_index (sm2->sid_lists, *sl_index);
+                   if (sl != NULL)
+                     break;
+                 }
+               }
+
+             if (sl)
+               {
+                 hdr_len = sizeof (ip6srv_combo_header_t);
+                 hdr_len += vec_len (sl->segments) * sizeof (ip6_address_t);
+                 hdr_len += sizeof (ip6_address_t);
+               }
+             else
+               {
+                 hdr_len = sizeof (ip6_header_t);
+                 if (PREDICT_FALSE (gtpu_type) != GTPU_TYPE_GTPU)
+                   {
+                     hdr_len += sizeof (ip6_sr_header_t);
+                     hdr_len += sizeof (ip6_address_t);
+                   }
+               }
+
+             // jump back to data[0] or pre_data if required
+             vlib_buffer_advance (b0, -(word) hdr_len);
+
+             ip6srv = vlib_buffer_get_current (b0);
+
+             if (sl)
+               {
+                 clib_memcpy_fast (ip6srv, sl->rewrite,
+                                   vec_len (sl->rewrite));
+
+                 ip6srv->ip.src_address = src0;
+                 ip6srv->ip.protocol = IP_PROTOCOL_IPV6_ROUTE;
+
+                 ip6srv->sr.tag =
+                   clib_host_to_net_u16 (srh_tagfield[gtpu_type]);
+
+                 ip6srv->sr.segments_left += 1;
+                 ip6srv->sr.last_entry += 1;
+
+                 ip6srv->sr.length += sizeof (ip6_address_t) / 8;
+                 ip6srv->sr.segments[0] = seg0;
+
+                 if (PREDICT_TRUE (encap != NULL))
+                   {
+                     if (ls_param->nhtype == SRV6_NHTYPE_NONE)
+                       {
+                         if ((clib_net_to_host_u32
+                              (encap->ip_version_traffic_class_and_flow_label)
+                              >> 28) == 6)
+                           ip6srv->sr.protocol = IP_PROTOCOL_IPV6;
+                         else
+                           ip6srv->sr.protocol = IP_PROTOCOL_IP_IN_IP;
+                       }
+                     else if (ls_param->nhtype == SRV6_NHTYPE_IPV4)
+                       {
+                         ip6srv->sr.protocol = IP_PROTOCOL_IP_IN_IP;
+                         if ((clib_net_to_host_u32
+                              (encap->ip_version_traffic_class_and_flow_label)
+                              >> 28) != 4)
+                           {
+                             // Bad encap packet.
+                             next0 = SRV6_END_M_GTP6_D_NEXT_DROP;
+                             bad_n++;
+                             goto DONE;
+                           }
+                       }
+                     else if (ls_param->nhtype == SRV6_NHTYPE_IPV6)
+                       {
+                         ip6srv->sr.protocol = IP_PROTOCOL_IPV6;
+                         if ((clib_net_to_host_u32
+                              (encap->ip_version_traffic_class_and_flow_label)
+                              >> 28) != 6)
+                           {
+                             // Bad encap packet.
+                             next0 = SRV6_END_M_GTP6_D_NEXT_DROP;
+                             bad_n++;
+                             goto DONE;
+                           }
+                       }
+                     else if (ls_param->nhtype == SRV6_NHTYPE_NON_IP)
+                       {
+                         ip6srv->sr.protocol = IP_PROTOCOL_NONE;
+                       }
+                   }
+                 else
+                   {
+                     ip6srv->sr.protocol = IP_PROTOCOL_NONE;
+                   }
+
+                 clib_memcpy_fast (&ip6srv->sr.segments[1],
+                                   (u8 *) (sl->rewrite +
+                                           sizeof (ip6_header_t) +
+                                           sizeof (ip6_sr_header_t)),
+                                   vec_len (sl->segments) *
+                                   sizeof (ip6_address_t));
+               }
+             else
+               {
+                 clib_memcpy_fast (ip6srv, &sm->cache_hdr,
+                                   sizeof (ip6_header_t));
+
+                 ip6srv->ip.src_address = src0;
+                 ip6srv->ip.dst_address = seg0;
+
+                 if (PREDICT_FALSE (gtpu_type) != GTPU_TYPE_GTPU)
+                   {
+                     ip6srv->ip.protocol = IP_PROTOCOL_IPV6_ROUTE;
+
+                     ip6srv->sr.protocol = IP_PROTOCOL_NONE;
+
+                     ip6srv->sr.tag =
+                       clib_host_to_net_u16 (srh_tagfield[gtpu_type]);
+
+                     ip6srv->sr.segments_left = 0;
+                     ip6srv->sr.last_entry = 0;
+
+                     ip6srv->sr.length = sizeof (ip6_address_t) / 8;
+                     ip6srv->sr.segments[0] = seg0;
+                   }
+                 else
+                   {
+                     if (ls_param->nhtype == SRV6_NHTYPE_NONE)
+                       {
+                         if ((clib_net_to_host_u32
+                              (encap->ip_version_traffic_class_and_flow_label)
+                              >> 28) != 6)
+                           ip6srv->ip.protocol = IP_PROTOCOL_IP_IN_IP;
+                       }
+                     else if (ls_param->nhtype == SRV6_NHTYPE_IPV4)
+                       {
+                         ip6srv->ip.protocol = IP_PROTOCOL_IP_IN_IP;
+                         if ((clib_net_to_host_u32
+                              (encap->ip_version_traffic_class_and_flow_label)
+                              >> 28) != 4)
+                           {
+                             // Bad encap packet.
+                             next0 = SRV6_END_M_GTP6_D_NEXT_DROP;
+                             bad_n++;
+                             goto DONE;
+                           }
+                       }
+                     else if (ls_param->nhtype == SRV6_NHTYPE_IPV6)
+                       {
+                         ip6srv->ip.protocol = IP_PROTOCOL_IPV6;
+                         if ((clib_net_to_host_u32
+                              (encap->ip_version_traffic_class_and_flow_label)
+                              >> 28) != 6)
+                           {
+                             // Bad encap packet.
+                             next0 = SRV6_END_M_GTP6_D_NEXT_DROP;
+                             bad_n++;
+                             goto DONE;
+                           }
+                       }
+                     else if (ls_param->nhtype == SRV6_NHTYPE_NON_IP)
+                       {
+                         ip6srv->ip.protocol = IP_PROTOCOL_NONE;
+                       }
+                   }
+               }
+
+             ip6srv->ip.payload_length =
+               clib_host_to_net_u16 (len0 + hdr_len - sizeof (ip6_header_t));
+
+             good_n++;
+
+             if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE) &&
+                 PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+               {
+                 srv6_end_rewrite_trace_t *tr =
+                   vlib_add_trace (vm, node, b0, sizeof (*tr));
+                 clib_memcpy (tr->src.as_u8, ip6srv->ip.src_address.as_u8,
+                              sizeof (ip6_address_t));
+                 clib_memcpy (tr->dst.as_u8, ip6srv->ip.dst_address.as_u8,
+                              sizeof (ip6_address_t));
+                 tr->teid = teid;
+                 clib_memcpy (tr->sr_prefix.as_u8, ls_param->sr_prefix.as_u8,
+                              sizeof (ip6_address_t));
+                 tr->sr_prefixlen = ls_param->sr_prefixlen;
+               }
+           }
+
+       DONE:
+         vlib_increment_combined_counter
+           (((next0 ==
+              SRV6_END_M_GTP6_D_NEXT_DROP) ? &(sm2->sr_ls_invalid_counters) :
+             &(sm2->sr_ls_valid_counters)), thread_index,
+            ls0 - sm2->localsids, 1, vlib_buffer_length_in_chain (vm, b0));
+
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+                                          n_left_to_next, bi0, next0);
+       }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  vlib_node_increment_counter (vm, sm->end_m_gtp6_d_node_index,
+                              SRV6_END_ERROR_M_GTP6_D_BAD_PACKETS, bad_n);
+
+  vlib_node_increment_counter (vm, sm->end_m_gtp6_d_node_index,
+                              SRV6_END_ERROR_M_GTP6_D_PACKETS, good_n);
+
+  return frame->n_vectors;
+}
+
+// Function for SRv6 GTP6.D.DI function
+VLIB_NODE_FN (srv6_end_m_gtp6_d_di) (vlib_main_t * vm,
+                                    vlib_node_runtime_t * node,
+                                    vlib_frame_t * frame)
+{
+  srv6_end_main_v6_decap_di_t *sm = &srv6_end_main_v6_decap_di;
+  ip6_sr_main_t *sm2 = &sr_main;
+  u32 n_left_from, next_index, *from, *to_next;
+  u32 thread_index = vm->thread_index;
+  srv6_end_gtp6_param_t *ls_param;
+
+  u32 good_n = 0, bad_n = 0;
+
+  from = vlib_frame_vector_args (frame);
+  n_left_from = frame->n_vectors;
+  next_index = node->cached_next_index;
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         u32 bi0;
+         vlib_buffer_t *b0;
+         ip6_sr_localsid_t *ls0;
+
+         ip6_gtpu_header_t *hdr0 = NULL;
+         uword len0;
+
+         ip6_address_t dst0;
+         ip6_address_t src0;
+         ip6_address_t seg0;
+         u32 teid = 0;
+         u8 *teidp;
+         u8 gtpu_type = 0;
+         u8 qfi = 0;
+         u8 *qfip = NULL;
+         u32 offset, shift;
+         u32 hdrlen;
+         ip6_header_t *encap = NULL;
+         gtpu_pdu_session_t *sess;
+
+         u32 next0 = SRV6_END_M_GTP6_D_DI_NEXT_LOOKUP;
+
+         // defaults
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+
+         b0 = vlib_get_buffer (vm, bi0);
+         ls0 =
+           pool_elt_at_index (sm2->localsids,
+                              vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
+
+         ls_param = (srv6_end_gtp6_param_t *) ls0->plugin_mem;
+
+         hdr0 = vlib_buffer_get_current (b0);
+
+         hdrlen = sizeof (ip6_gtpu_header_t);
+
+         len0 = vlib_buffer_length_in_chain (vm, b0);
+
+         if ((hdr0->ip6.protocol != IP_PROTOCOL_UDP)
+             || (hdr0->udp.dst_port !=
+                 clib_host_to_net_u16 (SRV6_GTP_UDP_DST_PORT))
+             || (len0 < sizeof (ip6_gtpu_header_t)))
+           {
+             next0 = SRV6_END_M_GTP6_D_DI_NEXT_DROP;
+
+             bad_n++;
+           }
+         else
+           {
+             dst0 = hdr0->ip6.dst_address;
+             src0 = hdr0->ip6.src_address;
+
+             gtpu_type = hdr0->gtpu.type;
+
+             seg0 = ls_param->sr_prefix;
+             teid = hdr0->gtpu.teid;
+             teidp = (u8 *) & teid;
+
+             if (hdr0->gtpu.ver_flags & GTPU_EXTHDR_FLAG)
+               {
+                 // Extention header.
+                 hdrlen += sizeof (gtpu_exthdr_t);
+                 if (hdr0->gtpu.ext->nextexthdr == GTPU_EXTHDR_PDU_SESSION)
+                   {
+                     // PDU Session Container.
+                     sess =
+                       (gtpu_pdu_session_t *) (((char *) hdr0) + hdrlen);
+                     qfi = sess->u.val & ~GTPU_PDU_SESSION_P_BIT_MASK;
+                     qfip = &qfi;
+
+                     hdrlen += sizeof (gtpu_pdu_session_t);
+
+                     if (sess->u.val & GTPU_PDU_SESSION_P_BIT_MASK)
+                       {
+                         hdrlen += sizeof (gtpu_paging_policy_t);
+                       }
+                   }
+               }
+
+             offset = ls_param->sr_prefixlen / 8;
+             shift = ls_param->sr_prefixlen % 8;
+
+             offset += 1;
+             if (PREDICT_TRUE (shift == 0))
+               {
+                 clib_memcpy_fast (&seg0.as_u8[offset], teidp, 4);
+
+                 if (qfip)
+                   {
+                     qfi =
+                       ((qfi & GTPU_PDU_SESSION_QFI_MASK) << 2) |
+                       ((qfi & GTPU_PDU_SESSION_R_BIT_MASK) >> 5);
+
+                     if (sess->type)
+                       {
+                         qfi |= SRV6_PDU_SESSION_U_BIT_MASK;
+                       }
+
+                     seg0.as_u8[offset + 4] = qfi;
+                   }
+               }
+             else
+               {
+                 int idx;
+
+                 for (idx = 0; idx < 4; idx++)
+                   {
+                     seg0.as_u8[offset + idx] |= teidp[idx] >> shift;
+                     seg0.as_u8[offset + idx + 1] |=
+                       teidp[idx] << (8 - shift);
+                   }
+
+                 if (qfip)
+                   {
+                     qfi =
+                       ((qfi & GTPU_PDU_SESSION_QFI_MASK) << 2) |
+                       ((qfi & GTPU_PDU_SESSION_R_BIT_MASK) >> 5);
+
+                     if (sess->type)
+                       {
+                         qfi |= SRV6_PDU_SESSION_U_BIT_MASK;
+                       }
+
+                     seg0.as_u8[offset + 4] |= qfi >> shift;
+                     seg0.as_u8[offset + 5] |= qfi << (8 - shift);
+                   }
+               }
+
+             // jump over variable length data
+             vlib_buffer_advance (b0, (word) hdrlen);
+
+             // get length of encapsulated IPv6 packet (the remaining part)
+             len0 = vlib_buffer_length_in_chain (vm, b0);
+
+             if (PREDICT_TRUE (gtpu_type == GTPU_TYPE_GTPU))
+               {
+                 encap = vlib_buffer_get_current (b0);
+               }
+
+             uword *p;
+             ip6srv_combo_header_t *ip6srv;
+             ip6_sr_policy_t *sr_policy = NULL;
+             ip6_sr_sl_t *sl = NULL;
+             u32 *sl_index;
+             u32 hdr_len;
+
+             p =
+               mhash_get (&sm2->sr_policies_index_hash,
+                          &ls_param->sr_prefix);
+             if (p)
+               {
+                 sr_policy = pool_elt_at_index (sm2->sr_policies, p[0]);
+               }
+
+             if (sr_policy)
+               {
+                 vec_foreach (sl_index, sr_policy->segments_lists)
+                 {
+                   sl = pool_elt_at_index (sm2->sid_lists, *sl_index);
+                   if (sl != NULL)
+                     break;
+                 }
+               }
+
+             hdr_len = sizeof (ip6srv_combo_header_t);
+
+             if (sl)
+               hdr_len += vec_len (sl->segments) * sizeof (ip6_address_t);
+
+             hdr_len += sizeof (ip6_address_t) * 2;
+
+             // jump back to data[0] or pre_data if required
+             vlib_buffer_advance (b0, -(word) hdr_len);
+
+             ip6srv = vlib_buffer_get_current (b0);
+
+             if (sl)
+               {
+                 clib_memcpy_fast (ip6srv, sl->rewrite,
+                                   vec_len (sl->rewrite));
+
+                 ip6srv->ip.src_address = src0;
+
+                 ip6srv->sr.tag =
+                   clib_host_to_net_u16 (srh_tagfield[gtpu_type]);
+
+                 ip6srv->sr.segments_left += 2;
+                 ip6srv->sr.last_entry += 2;
+               }
+             else
+               {
+                 clib_memcpy_fast (ip6srv, &sm->cache_hdr,
+                                   sizeof (ip6_header_t));
+
+                 ip6srv->ip.src_address = src0;
+                 ip6srv->ip.dst_address = seg0;
+
+                 ip6srv->sr.tag =
+                   clib_host_to_net_u16 (srh_tagfield[gtpu_type]);
+
+                 ip6srv->sr.segments_left += 1;
+                 ip6srv->sr.last_entry += 1;
+                 ip6srv->sr.type = ROUTING_HEADER_TYPE_SR;
+               }
+
+             ip6srv->ip.payload_length =
+               clib_host_to_net_u16 (len0 + hdr_len - sizeof (ip6_header_t));
+             ip6srv->ip.protocol = IP_PROTOCOL_IPV6_ROUTE;
+
+             if (PREDICT_TRUE (encap != NULL))
+               {
+                 if (ls_param->nhtype == SRV6_NHTYPE_NONE)
+                   {
+                     if ((clib_net_to_host_u32
+                          (encap->ip_version_traffic_class_and_flow_label) >>
+                          28) == 6)
+                       ip6srv->sr.protocol = IP_PROTOCOL_IPV6;
+                     else
+                       ip6srv->sr.protocol = IP_PROTOCOL_IP_IN_IP;
+                   }
+                 else if (ls_param->nhtype == SRV6_NHTYPE_IPV4)
+                   {
+                     ip6srv->sr.protocol = IP_PROTOCOL_IP_IN_IP;
+                     if ((clib_net_to_host_u32
+                          (encap->ip_version_traffic_class_and_flow_label) >>
+                          28) != 4)
+                       {
+                         // Bad encap packet.
+                         next0 = SRV6_END_M_GTP6_D_DI_NEXT_DROP;
+                         bad_n++;
+                         goto DONE;
+                       }
+                   }
+                 else if (ls_param->nhtype == SRV6_NHTYPE_IPV6)
+                   {
+                     ip6srv->sr.protocol = IP_PROTOCOL_IPV6;
+                     if ((clib_net_to_host_u32
+                          (encap->ip_version_traffic_class_and_flow_label) >>
+                          28) != 6)
+                       {
+                         // Bad encap packet.
+                         next0 = SRV6_END_M_GTP6_D_DI_NEXT_DROP;
+                         bad_n++;
+                         goto DONE;
+                       }
+                   }
+                 else if (ls_param->nhtype == SRV6_NHTYPE_NON_IP)
+                   {
+                     ip6srv->sr.protocol = IP_PROTOCOL_NONE;
+                   }
+               }
+             else
+               {
+                 ip6srv->sr.protocol = IP_PROTOCOL_NONE;
+               }
+
+             ip6srv->sr.length += ((sizeof (ip6_address_t) * 2) / 8);
+             ip6srv->sr.segments[0] = dst0;
+             ip6srv->sr.segments[1] = seg0;
+
+             if (sl)
+               {
+                 clib_memcpy_fast (&ip6srv->sr.segments[2],
+                                   (u8 *) (sl->rewrite +
+                                           sizeof (ip6_header_t) +
+                                           sizeof (ip6_sr_header_t)),
+                                   vec_len (sl->segments) *
+                                   sizeof (ip6_address_t));
+               }
+
+             good_n++;
+
+             if (PREDICT_FALSE (node->flags & VLIB_NODE_FLAG_TRACE) &&
+                 PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+               {
+                 srv6_end_rewrite_trace_t *tr =
+                   vlib_add_trace (vm, node, b0, sizeof (*tr));
+                 clib_memcpy (tr->src.as_u8, ip6srv->ip.src_address.as_u8,
+                              sizeof (ip6_address_t));
+                 clib_memcpy (tr->dst.as_u8, ip6srv->ip.dst_address.as_u8,
+                              sizeof (ip6_address_t));
+                 tr->teid = teid;
+                 clib_memcpy (tr->sr_prefix.as_u8, ls_param->sr_prefix.as_u8,
+                              sizeof (ip6_address_t));
+                 tr->sr_prefixlen = ls_param->sr_prefixlen;
+               }
+           }
+
+       DONE:
+         vlib_increment_combined_counter
+           (((next0 ==
+              SRV6_END_M_GTP6_D_DI_NEXT_DROP) ?
+             &(sm2->sr_ls_invalid_counters) : &(sm2->sr_ls_valid_counters)),
+            thread_index, ls0 - sm2->localsids, 1,
+            vlib_buffer_length_in_chain (vm, b0));
+
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
+                                          n_left_to_next, bi0, next0);
+       }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  vlib_node_increment_counter (vm, sm->end_m_gtp6_d_di_node_index,
+                              SRV6_END_ERROR_M_GTP6_D_DI_BAD_PACKETS, bad_n);
+
+  vlib_node_increment_counter (vm, sm->end_m_gtp6_d_di_node_index,
+                              SRV6_END_ERROR_M_GTP6_D_DI_PACKETS, good_n);
+
+  return frame->n_vectors;
+}
+
+VLIB_REGISTER_NODE (srv6_end_m_gtp6_e) =
+{
+  .name = "srv6-end-m-gtp6-e",.vector_size = sizeof (u32),.format_trace =
+    format_srv6_end_rewrite_trace6,.type = VLIB_NODE_TYPE_INTERNAL,.n_errors =
+    ARRAY_LEN (srv6_end_error_v6_e_strings),.error_strings =
+    srv6_end_error_v6_e_strings,.n_next_nodes =
+    SRV6_END_M_GTP6_E_N_NEXT,.next_nodes =
+  {
+  [SRV6_END_M_GTP6_E_NEXT_DROP] =
+      "error-drop",[SRV6_END_M_GTP6_E_NEXT_LOOKUP] = "ip6-lookup",}
+,};
+
+VLIB_REGISTER_NODE (srv6_end_m_gtp6_d) =
+{
+  .name = "srv6-end-m-gtp6-d",.vector_size = sizeof (u32),.format_trace =
+    format_srv6_end_rewrite_trace6,.type = VLIB_NODE_TYPE_INTERNAL,.n_errors =
+    ARRAY_LEN (srv6_end_error_v6_d_strings),.error_strings =
+    srv6_end_error_v6_d_strings,.n_next_nodes =
+    SRV6_END_M_GTP6_D_N_NEXT,.next_nodes =
+  {
+  [SRV6_END_M_GTP6_D_NEXT_DROP] =
+      "error-drop",[SRV6_END_M_GTP6_D_NEXT_LOOKUP] = "ip6-lookup",}
+,};
+
+VLIB_REGISTER_NODE (srv6_end_m_gtp6_d_di) =
+{
+  .name = "srv6-end-m-gtp6-d-di",.vector_size = sizeof (u32),.format_trace =
+    format_srv6_end_rewrite_trace6,.type = VLIB_NODE_TYPE_INTERNAL,.n_errors =
+    ARRAY_LEN (srv6_end_error_v6_d_di_strings),.error_strings =
+    srv6_end_error_v6_d_di_strings,.n_next_nodes =
+    SRV6_END_M_GTP6_D_DI_N_NEXT,.next_nodes =
+  {
+  [SRV6_END_M_GTP6_D_DI_NEXT_DROP] = "error-drop",
+      [SRV6_END_M_GTP6_D_DI_NEXT_LOOKUP] = "ip6-lookup",}
+,};
+
+/*
+* fd.io coding-style-patch-verification: ON
+*
+* Local Variables:
+* eval: (c-set-style "gnu")
+* End:
+*/
diff --git a/src/plugins/srv6-mobile/test/test_srv6_end.py b/src/plugins/srv6-mobile/test/test_srv6_end.py
new file mode 100644 (file)
index 0000000..4e2a576
--- /dev/null
@@ -0,0 +1,161 @@
+#!/usr/bin/env python
+
+from framework import VppTestCase
+from ipaddress import IPv4Address
+from ipaddress import IPv6Address
+from scapy.contrib.gtp import *
+from scapy.all import *
+
+
+class TestSRv6EndMGTP4E(VppTestCase):
+    """ SRv6 End.M.GTP4.E (SRv6 -> GTP-U) """
+
+    @classmethod
+    def setUpClass(cls):
+        super(TestSRv6EndMGTP4E, cls).setUpClass()
+        try:
+            cls.create_pg_interfaces(range(2))
+            cls.pg_if_i = cls.pg_interfaces[0]
+            cls.pg_if_o = cls.pg_interfaces[1]
+
+            cls.pg_if_i.config_ip6()
+            cls.pg_if_o.config_ip4()
+
+            cls.ip4_dst = cls.pg_if_o.remote_ip4
+            # cls.ip4_src = cls.pg_if_o.local_ip4
+            cls.ip4_src = "192.168.192.10"
+
+            for pg_if in cls.pg_interfaces:
+                pg_if.admin_up()
+                pg_if.resolve_arp()
+
+        except Exception:
+            super(TestSRv6EndMGTP4E, cls).tearDownClass()
+            raise
+
+    def create_packets(self, inner):
+
+        ip4_dst = IPv4Address(str(self.ip4_dst))
+        # 32bit prefix + 32bit IPv4 DA + 8bit + 32bit TEID + 24bit
+        dst = b'\xaa' * 4 + ip4_dst.packed + \
+            b'\x11' + b'\xbb' * 4 + b'\x11' * 3
+        ip6_dst = IPv6Address(dst)
+
+        ip4_src = IPv4Address(str(self.ip4_src))
+        # 64bit prefix + 32bit IPv4 SA + 16 bit port + 16bit
+        src = b'\xcc' * 8 + ip4_src.packed + \
+            b'\xdd' * 2 + b'\x11' * 2
+        ip6_src = IPv6Address(src)
+
+        self.logger.info("ip4 dst: {}".format(ip4_dst))
+        self.logger.info("ip4 src: {}".format(ip4_src))
+        self.logger.info("ip6 dst (remote srgw): {}".format(ip6_dst))
+        self.logger.info("ip6 src (local  srgw): {}".format(ip6_src))
+
+        pkts = list()
+        for d, s in inner:
+            pkt = (Ether() /
+                   IPv6(dst=str(ip6_dst), src=str(ip6_src)) /
+                   IPv6ExtHdrSegmentRouting() /
+                   IPv6(dst=d, src=s) /
+                   UDP(sport=1000, dport=23))
+
+            self.logger.info(pkt.show2())
+            pkts.append(pkt)
+
+        return pkts
+
+    def test_srv6_end(self):
+        """ test_srv6_end """
+        pkts = self.create_packets([("A::1", "B::1"), ("C::1", "D::1")])
+
+        self.vapi.cli(
+            "sr localsid address {} behavior end.m.gtp4.e v4src_position 64"
+            .format(pkts[0]['IPv6'].dst))
+        self.logger.info(self.vapi.cli("show sr localsids"))
+
+        self.vapi.cli("clear errors")
+
+        self.pg0.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+
+        self.logger.info(self.vapi.cli("show errors"))
+        self.logger.info(self.vapi.cli("show int address"))
+
+        capture = self.pg1.get_capture(len(pkts))
+
+        for pkt in capture:
+            self.logger.info(pkt.show2())
+            self.assertEqual(pkt[IP].dst, self.ip4_dst)
+            self.assertEqual(pkt[IP].src, self.ip4_src)
+            self.assertEqual(pkt[GTP_U_Header].teid, 0xbbbbbbbb)
+
+
+class TestSRv6TMTmap(VppTestCase):
+    """ SRv6 T.M.Tmap (GTP-U -> SRv6) """
+
+    @classmethod
+    def setUpClass(cls):
+        super(TestSRv6TMTmap, cls).setUpClass()
+        try:
+            cls.create_pg_interfaces(range(2))
+            cls.pg_if_i = cls.pg_interfaces[0]
+            cls.pg_if_o = cls.pg_interfaces[1]
+
+            cls.pg_if_i.config_ip4()
+            cls.pg_if_o.config_ip6()
+
+            for pg_if in cls.pg_interfaces:
+                pg_if.admin_up()
+                pg_if.resolve_arp()
+
+        except Exception:
+            super(TestSRv6TMTmap, cls).tearDownClass()
+            raise
+
+
+class TestSRv6EndMGTP6E(VppTestCase):
+    """ SRv6 End.M.GTP6.E """
+
+    @classmethod
+    def setUpClass(cls):
+        super(TestSRv6EndMGTP6E, cls).setUpClass()
+        try:
+            cls.create_pg_interfaces(range(2))
+            cls.pg_if_i = cls.pg_interfaces[0]
+            cls.pg_if_o = cls.pg_interfaces[1]
+
+            cls.pg_if_i.config_ip4()
+            cls.pg_if_o.config_ip6()
+
+            for pg_if in cls.pg_interfaces:
+                pg_if.admin_up()
+                pg_if.resolve_arp()
+
+        except Exception:
+            super(TestSRv6EndMGTP6E, cls).tearDownClass()
+            raise
+
+
+class TestSRv6EndMGTP6D(VppTestCase):
+    """ SRv6 End.M.GTP6.D """
+
+    @classmethod
+    def setUpClass(cls):
+        super(TestSRv6EndMGTP6D, cls).setUpClass()
+        try:
+            cls.create_pg_interfaces(range(2))
+            cls.pg_if_i = cls.pg_interfaces[0]
+            cls.pg_if_o = cls.pg_interfaces[1]
+
+            cls.pg_if_i.config_ip4()
+            cls.pg_if_o.config_ip6()
+
+            for pg_if in cls.pg_interfaces:
+                pg_if.admin_up()
+                pg_if.resolve_arp()
+
+        except Exception:
+            super(TestSRv6EndMGTP6D, cls).tearDownClass()
+            raise
index e1e8bfe..7b5d594 100644 (file)
@@ -9796,6 +9796,7 @@ api_sr_localsid_add_del (vat_main_t * vam)
   M (SR_LOCALSID_ADD_DEL, mp);
 
   clib_memcpy (mp->localsid.addr, &localsid, sizeof (mp->localsid));
+
   if (nexthop_set)
     {
       clib_memcpy (mp->nh_addr6, &nh_addr6, sizeof (mp->nh_addr6));
index 2e2d439..1193583 100755 (executable)
 
 #define SR_SEGMENT_LIST_WEIGHT_DEFAULT 1
 
+/* *INDENT-OFF* */
+typedef struct
+{
+  ip6_header_t ip;
+  ip6_sr_header_t sr;
+} __attribute__ ((packed)) ip6srv_combo_header_t;
+/* *INDENT-ON* */
+
 /**
  * @brief SR Segment List (SID list)
  */
@@ -103,6 +111,8 @@ typedef struct
 {
   ip6_address_t localsid;              /**< LocalSID IPv6 address */
 
+  u16 localsid_len;
+
   char end_psp;                                        /**< Combined with End.PSP? */
 
   u16 behavior;                                        /**< Behavior associated to this localsid */
@@ -141,6 +151,8 @@ typedef struct
 
   u8 *params_str;                                                      /**< Behavior parameters (i.e. <oif> <IP46next_hop>) */
 
+  u8 prefix_length;
+
   dpo_type_t dpo;                                                      /**< DPO type registration */
 
   format_function_t *ls_format;                                /**< LocalSID format function */
@@ -250,7 +262,8 @@ extern void sr_dpo_unlock (dpo_id_t * dpo);
 extern int
 sr_localsid_register_function (vlib_main_t * vm, u8 * fn_name,
                               u8 * keyword_str, u8 * def_str,
-                              u8 * params_str, dpo_type_t * dpo,
+                              u8 * params_str, u8 prefix_length,
+                              dpo_type_t * dpo,
                               format_function_t * ls_format,
                               unformat_function_t * ls_unformat,
                               sr_plugin_callback_t * creation_fn,
@@ -259,14 +272,13 @@ sr_localsid_register_function (vlib_main_t * vm, u8 * fn_name,
 extern int
 sr_policy_add (ip6_address_t * bsid, ip6_address_t * segments,
               u32 weight, u8 behavior, u32 fib_table, u8 is_encap);
-extern int
-sr_policy_mod (ip6_address_t * bsid, u32 index, u32 fib_table,
-              u8 operation, ip6_address_t * segments, u32 sl_index,
-              u32 weight);
+extern int sr_policy_mod (ip6_address_t * bsid, u32 index, u32 fib_table,
+                         u8 operation, ip6_address_t * segments,
+                         u32 sl_index, u32 weight);
 extern int sr_policy_del (ip6_address_t * bsid, u32 index);
 
 extern int
-sr_cli_localsid (char is_del, ip6_address_t * localsid_addr,
+sr_cli_localsid (char is_del, ip6_address_t * localsid_addr, u16 prefixlen,
                 char end_psp, u8 behavior, u32 sw_if_index,
                 u32 vlan_index, u32 fib_table, ip46_address_t * nh_addr,
                 void *ls_plugin_mem);
index f3738da..940336d 100644 (file)
@@ -80,7 +80,7 @@ static void vl_api_sr_localsid_add_del_t_handler
     memcpy (&prefix.ip6, mp->nh_addr6, sizeof (prefix.ip6));
 
   rv = sr_cli_localsid (mp->is_del,
-                       (ip6_address_t *) & mp->localsid,
+                       (ip6_address_t *) & mp->localsid, 0,
                        mp->end_psp,
                        mp->behavior,
                        ntohl (mp->sw_if_index),
index c592f79..05651bb 100755 (executable)
@@ -63,13 +63,15 @@ static dpo_type_t sr_localsid_d_dpo_type;
  * @return 0 on success, error otherwise.
  */
 int
-sr_cli_localsid (char is_del, ip6_address_t * localsid_addr,
+sr_cli_localsid (char is_del, ip6_address_t * localsid_addr, u16 prefixlen,
                 char end_psp, u8 behavior, u32 sw_if_index, u32 vlan_index,
                 u32 fib_table, ip46_address_t * nh_addr, void *ls_plugin_mem)
 {
   ip6_sr_main_t *sm = &sr_main;
   uword *p;
   int rv;
+  u8 pref_length = 128;
+  sr_localsid_fn_registration_t *plugin = 0;
 
   ip6_sr_localsid_t *ls = 0;
 
@@ -84,18 +86,30 @@ sr_cli_localsid (char is_del, ip6_address_t * localsid_addr,
        {
          /* Retrieve localsid */
          ls = pool_elt_at_index (sm->localsids, p[0]);
+         if (ls->behavior >= SR_BEHAVIOR_LAST)
+           {
+             plugin = pool_elt_at_index (sm->plugin_functions,
+                                         ls->behavior - SR_BEHAVIOR_LAST);
+             pref_length = plugin->prefix_length;
+           }
+
+         if (prefixlen != 0)
+           {
+             pref_length = prefixlen;
+           }
+
          /* Delete FIB entry */
          fib_prefix_t pfx = {
            .fp_proto = FIB_PROTOCOL_IP6,
-           .fp_len = 128,
+           .fp_len = pref_length,
            .fp_addr = {
                        .ip6 = *localsid_addr,
                        }
          };
 
-         fib_table_entry_delete (fib_table_find (FIB_PROTOCOL_IP6,
-                                                 fib_table),
-                                 &pfx, FIB_SOURCE_SR);
+         fib_table_entry_delete (fib_table_find
+                                 (FIB_PROTOCOL_IP6, fib_table), &pfx,
+                                 FIB_SOURCE_SR);
 
          /* In case it is a Xconnect iface remove the (OIF, NHOP) adj */
          if (ls->behavior == SR_BEHAVIOR_X || ls->behavior == SR_BEHAVIOR_DX6
@@ -104,10 +118,6 @@ sr_cli_localsid (char is_del, ip6_address_t * localsid_addr,
 
          if (ls->behavior >= SR_BEHAVIOR_LAST)
            {
-             sr_localsid_fn_registration_t *plugin = 0;
-             plugin = pool_elt_at_index (sm->plugin_functions,
-                                         ls->behavior - SR_BEHAVIOR_LAST);
-
              /* Callback plugin removal function */
              rv = plugin->removal (ls);
            }
@@ -125,15 +135,28 @@ sr_cli_localsid (char is_del, ip6_address_t * localsid_addr,
   if (is_del)
     return -2;
 
+  if (behavior >= SR_BEHAVIOR_LAST)
+    {
+      sr_localsid_fn_registration_t *plugin = 0;
+      plugin =
+       pool_elt_at_index (sm->plugin_functions, behavior - SR_BEHAVIOR_LAST);
+      pref_length = plugin->prefix_length;
+    }
+
   /* Check whether there exists a FIB entry with such address */
   fib_prefix_t pfx = {
     .fp_proto = FIB_PROTOCOL_IP6,
-    .fp_len = 128,
+    .fp_len = pref_length,
   };
 
   pfx.fp_addr.as_u64[0] = localsid_addr->as_u64[0];
   pfx.fp_addr.as_u64[1] = localsid_addr->as_u64[1];
 
+  if (prefixlen != 0)
+    {
+      pfx.fp_len = prefixlen;
+    }
+
   /* Lookup the FIB index associated to the table id provided */
   u32 fib_index = fib_table_find (FIB_PROTOCOL_IP6, fib_table);
   if (fib_index == ~0)
@@ -153,6 +176,7 @@ sr_cli_localsid (char is_del, ip6_address_t * localsid_addr,
   ls->behavior = behavior;
   ls->nh_adj = (u32) ~ 0;
   ls->fib_table = fib_table;
+  ls->localsid_len = pfx.fp_len;
   switch (behavior)
     {
     case SR_BEHAVIOR_END:
@@ -267,6 +291,7 @@ sr_cli_localsid_command_fn (vlib_main_t * vm, unformat_input_t * input,
   vnet_main_t *vnm = vnet_get_main ();
   ip6_sr_main_t *sm = &sr_main;
   u32 sw_if_index = (u32) ~ 0, vlan_index = (u32) ~ 0, fib_index = 0;
+  u16 prefix_len = 0;
   int is_del = 0;
   int end_psp = 0;
   ip6_address_t resulting_address;
@@ -288,6 +313,10 @@ sr_cli_localsid_command_fn (vlib_main_t * vm, unformat_input_t * input,
               && unformat (input, "address %U", unformat_ip6_address,
                            &resulting_address))
        address_set = 1;
+      else if (!address_set
+              && unformat (input, "prefix %U/%d", unformat_ip6_address,
+                           &resulting_address, &prefix_len))
+       address_set = 1;
       else if (!address_set
               && unformat (input, "addr %U", unformat_ip6_address,
                            &resulting_address))
@@ -325,12 +354,12 @@ sr_cli_localsid_command_fn (vlib_main_t * vm, unformat_input_t * input,
              sr_localsid_fn_registration_t **plugin_it = 0;
 
              /* Create a vector out of the plugin pool as recommended */
-        /* *INDENT-OFF* */
-        pool_foreach (plugin, sm->plugin_functions,
-        {
-          vec_add1 (vec_plugins, plugin);
-        });
-        /* *INDENT-ON* */
+              /* *INDENT-OFF* */
+              pool_foreach (plugin, sm->plugin_functions,
+                {
+                  vec_add1 (vec_plugins, plugin);
+                });
+              /* *INDENT-ON* */
 
              vec_foreach (plugin_it, vec_plugins)
              {
@@ -373,9 +402,10 @@ sr_cli_localsid_command_fn (vlib_main_t * vm, unformat_input_t * input,
     return clib_error_return (0,
                              "Error: SRv6 PSP only compatible with End and End.X");
 
-  rv = sr_cli_localsid (is_del, &resulting_address, end_psp, behavior,
-                       sw_if_index, vlan_index, fib_index, &next_hop,
-                       ls_plugin_mem);
+  rv =
+    sr_cli_localsid (is_del, &resulting_address, prefix_len, end_psp,
+                    behavior, sw_if_index, vlan_index, fib_index, &next_hop,
+                    ls_plugin_mem);
 
   switch (rv)
     {
@@ -1533,7 +1563,8 @@ const static char *const *const sr_loc_d_nodes[DPO_PROTO_NUM] = {
 int
 sr_localsid_register_function (vlib_main_t * vm, u8 * fn_name,
                               u8 * keyword_str, u8 * def_str,
-                              u8 * params_str, dpo_type_t * dpo,
+                              u8 * params_str, u8 prefix_length,
+                              dpo_type_t * dpo,
                               format_function_t * ls_format,
                               unformat_function_t * ls_unformat,
                               sr_plugin_callback_t * creation_fn,
@@ -1562,6 +1593,7 @@ sr_localsid_register_function (vlib_main_t * vm, u8 * fn_name,
 
   plugin->sr_localsid_function_number = (plugin - sm->plugin_functions);
   plugin->sr_localsid_function_number += SR_BEHAVIOR_LAST;
+  plugin->prefix_length = prefix_length;
   plugin->ls_format = ls_format;
   plugin->ls_unformat = ls_unformat;
   plugin->creation = creation_fn;