aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTetsuya Murakami <tetsuya.mrk@gmail.com>2019-11-06 11:05:51 -0800
committerOle Trøan <otroan@employees.org>2019-11-19 07:02:40 +0000
commit1b81e6ef66cbce15ba1b05904732f92251d531cd (patch)
tree114135a6f83405d4883bcb37f1c362051944831a
parent360b523b52a91bd0d1d7de1247141367c484d70f (diff)
srv6-mobile
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>
-rw-r--r--MAINTAINERS6
-rw-r--r--src/plugins/srv6-ad/ad.c1
-rw-r--r--src/plugins/srv6-ad/node.c13
-rw-r--r--src/plugins/srv6-am/am.c1
-rw-r--r--src/plugins/srv6-as/as.c1
-rw-r--r--src/plugins/srv6-mobile/CMakeLists.txt24
-rw-r--r--src/plugins/srv6-mobile/FEATURE.yaml12
-rw-r--r--src/plugins/srv6-mobile/README.md178
-rw-r--r--src/plugins/srv6-mobile/extra/Dockerfile.j248
-rw-r--r--src/plugins/srv6-mobile/extra/Dockerfile.j2.release37
-rw-r--r--src/plugins/srv6-mobile/extra/README.md173
-rw-r--r--src/plugins/srv6-mobile/extra/requirements.txt3
-rwxr-xr-xsrc/plugins/srv6-mobile/extra/runner.py2046
-rw-r--r--src/plugins/srv6-mobile/extra/startup.conf.j214
-rw-r--r--src/plugins/srv6-mobile/extra/topo-init.png0
-rw-r--r--src/plugins/srv6-mobile/extra/topo-test_gtp4d.png0
-rw-r--r--src/plugins/srv6-mobile/extra/topo-test_gtp6.png0
-rw-r--r--src/plugins/srv6-mobile/extra/topo-test_gtp6d.png0
-rw-r--r--src/plugins/srv6-mobile/extra/topo-test_gtp6ip6.png0
-rw-r--r--src/plugins/srv6-mobile/gtp4_e.c188
-rw-r--r--src/plugins/srv6-mobile/gtp6_d.c216
-rw-r--r--src/plugins/srv6-mobile/gtp6_d_di.c218
-rw-r--r--src/plugins/srv6-mobile/gtp6_e.c157
-rw-r--r--src/plugins/srv6-mobile/mobile.h243
-rw-r--r--src/plugins/srv6-mobile/node.c1533
-rw-r--r--src/plugins/srv6-mobile/test/test_srv6_end.py161
-rw-r--r--src/vat/api_format.c1
-rwxr-xr-xsrc/vnet/srv6/sr.h24
-rw-r--r--src/vnet/srv6/sr_api.c2
-rwxr-xr-xsrc/vnet/srv6/sr_localsid.c72
30 files changed, 5340 insertions, 32 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 6e05dfe3590..9ce148d5927 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -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>
diff --git a/src/plugins/srv6-ad/ad.c b/src/plugins/srv6-ad/ad.c
index 73ea3f6ac1f..1b11037f343 100644
--- a/src/plugins/srv6-ad/ad.c
+++ b/src/plugins/srv6-ad/ad.c
@@ -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,
diff --git a/src/plugins/srv6-ad/node.c b/src/plugins/srv6-ad/node.c
index 8f32ae0e49e..dbd68cf4c98 100644
--- a/src/plugins/srv6-ad/node.c
+++ b/src/plugins/srv6-ad/node.c
@@ -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:
*/
diff --git a/src/plugins/srv6-am/am.c b/src/plugins/srv6-am/am.c
index 2507a75dce8..1408ebc470b 100644
--- a/src/plugins/srv6-am/am.c
+++ b/src/plugins/srv6-am/am.c
@@ -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,
diff --git a/src/plugins/srv6-as/as.c b/src/plugins/srv6-as/as.c
index 99e68511f32..cb95991cdda 100644
--- a/src/plugins/srv6-as/as.c
+++ b/src/plugins/srv6-as/as.c
@@ -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
index 00000000000..612d626df91
--- /dev/null
+++ b/src/plugins/srv6-mobile/CMakeLists.txt
@@ -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
index 00000000000..6d5cf104bc4
--- /dev/null
+++ b/src/plugins/srv6-mobile/FEATURE.yaml
@@ -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
index 00000000000..18628806cbc
--- /dev/null
+++ b/src/plugins/srv6-mobile/README.md
@@ -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
index 00000000000..ac58fa36b64
--- /dev/null
+++ b/src/plugins/srv6-mobile/extra/Dockerfile.j2
@@ -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
index 00000000000..851aef66197
--- /dev/null
+++ b/src/plugins/srv6-mobile/extra/Dockerfile.j2.release
@@ -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
index 00000000000..3b24dea6fd6
--- /dev/null
+++ b/src/plugins/srv6-mobile/extra/README.md
@@ -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
index 00000000000..2938cddeceb
--- /dev/null
+++ b/src/plugins/srv6-mobile/extra/requirements.txt
@@ -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
index 00000000000..710252d5ce1
--- /dev/null
+++ b/src/plugins/srv6-mobile/extra/runner.py
@@ -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
index 00000000000..5a4a2ef103d
--- /dev/null
+++ b/src/plugins/srv6-mobile/extra/startup.conf.j2
@@ -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
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/src/plugins/srv6-mobile/extra/topo-init.png
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
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/src/plugins/srv6-mobile/extra/topo-test_gtp4d.png
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
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/src/plugins/srv6-mobile/extra/topo-test_gtp6.png
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
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/src/plugins/srv6-mobile/extra/topo-test_gtp6d.png
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
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/src/plugins/srv6-mobile/extra/topo-test_gtp6ip6.png
diff --git a/src/plugins/srv6-mobile/gtp4_e.c b/src/plugins/srv6-mobile/gtp4_e.c
new file mode 100644
index 00000000000..73a91f8592d
--- /dev/null
+++ b/src/plugins/srv6-mobile/gtp4_e.c
@@ -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
index 00000000000..3f366e81e5c
--- /dev/null
+++ b/src/plugins/srv6-mobile/gtp6_d.c
@@ -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
index 00000000000..54f18738605
--- /dev/null
+++ b/src/plugins/srv6-mobile/gtp6_d_di.c
@@ -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
index 00000000000..4bc8d97cec7
--- /dev/null
+++ b/src/plugins/srv6-mobile/gtp6_e.c
@@ -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
index 00000000000..8be9dc2b944
--- /dev/null
+++ b/src/plugins/srv6-mobile/mobile.h
@@ -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
index 00000000000..0c2c04e612e
--- /dev/null
+++ b/src/plugins/srv6-mobile/node.c
@@ -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
index 00000000000..4e2a5765bab
--- /dev/null
+++ b/src/plugins/srv6-mobile/test/test_srv6_end.py
@@ -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
diff --git a/src/vat/api_format.c b/src/vat/api_format.c
index e1e8bfee57a..7b5d5949159 100644
--- a/src/vat/api_format.c
+++ b/src/vat/api_format.c
@@ -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));
diff --git a/src/vnet/srv6/sr.h b/src/vnet/srv6/sr.h
index 2e2d43978bf..11935833394 100755
--- a/src/vnet/srv6/sr.h
+++ b/src/vnet/srv6/sr.h
@@ -54,6 +54,14 @@
#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);
diff --git a/src/vnet/srv6/sr_api.c b/src/vnet/srv6/sr_api.c
index f3738da668d..940336d9859 100644
--- a/src/vnet/srv6/sr_api.c
+++ b/src/vnet/srv6/sr_api.c
@@ -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),
diff --git a/src/vnet/srv6/sr_localsid.c b/src/vnet/srv6/sr_localsid.c
index c592f797d1c..05651bb1857 100755
--- a/src/vnet/srv6/sr_localsid.c
+++ b/src/vnet/srv6/sr_localsid.c
@@ -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;
@@ -289,6 +314,10 @@ sr_cli_localsid_command_fn (vlib_main_t * vm, unformat_input_t * input,
&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))
address_set = 1;
@@ -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;