srv6-mobile: Support GTP4/6.DT and User Plane message mapping

Support the following new features in srv6-mobile.

1. GTP4.DT
2. GTP6.DT
3. User Plane Message encoding based on draft-murakami-dmm-user-plane-message-encoding-01
4. Add SRv6 mobile test cases

Type: feature

Signed-off-by: Tetsuya Murakami <tetsuya.mrk@gmail.com>
Change-Id: I890e5171bf03513d54b4830f01b9dc7f47fe7c48
Signed-off-by: Tetsuya Murakami <tetsuya.mrk@gmail.com>
This commit is contained in:
Tetsuya Murakami
2020-03-04 16:27:14 -08:00
committed by Damjan Marion
parent 7a6f5a4fee
commit 9e722bd466
10 changed files with 2119 additions and 206 deletions

View File

@ -15,9 +15,11 @@ add_vpp_plugin(srv6mobile
SOURCES
gtp4_e.c
gtp4_d.c
gtp4_dt.c
gtp6_e.c
gtp6_d.c
gtp6_d_di.c
gtp6_dt.c
node.c
INSTALL_HEADERS

View File

@ -16,6 +16,7 @@ RUN set -eux; \
iproute2 \
tcpdump \
python3-cffi \
python2.7 \
netcat; \
rm -rf /var/lib/apt/lists/*; \
mv /usr/sbin/tcpdump /usr/bin/tcpdump

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,205 @@
/*
* srv6_t_m_gtp4_dt.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/fib/fib_table.h>
#include <vnet/plugin/plugin.h>
#include <vpp/app/version.h>
#include <srv6-mobile/mobile.h>
srv6_t_main_v4_dt_t srv6_t_main_v4_dt;
static void
clb_dpo_lock_srv6_t_m_gtp4_dt (dpo_id_t * dpo)
{
}
static void
clb_dpo_unlock_srv6_t_m_gtp4_dt (dpo_id_t * dpo)
{
}
static u8 *
clb_dpo_format_srv6_t_m_gtp4_dt (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_t_m_gtp4_dt,
.dv_unlock = clb_dpo_unlock_srv6_t_m_gtp4_dt,
.dv_format = clb_dpo_format_srv6_t_m_gtp4_dt,
};
const static char *const srv6_t_m_gtp4_dt_nodes[] = {
"srv6-t-m-gtp4-dt",
NULL,
};
const static char *const srv6_t_m_gtp4_dt_v6_nodes[] = {
"error-drop",
NULL,
};
const static char *const *const dpo_nodes[DPO_PROTO_NUM] = {
[DPO_PROTO_IP6] = srv6_t_m_gtp4_dt_v6_nodes,
[DPO_PROTO_IP4] = srv6_t_m_gtp4_dt_nodes,
};
static u8 fn_name[] = "SRv6-T.M.GTP4.DT-plugin";
static u8 keyword_str[] = "t.m.gtp4.dt";
static u8 def_str[] = "Transit function with DT for IPv4/GTP tunnel";
static u8 param_str[] = "fib-index <index> [local-fib-table <index>]";
static u8 *
clb_format_srv6_t_m_gtp4_dt (u8 * s, va_list * args)
{
srv6_t_gtp4_dt_param_t *ls_mem = va_arg (*args, void *);
s = format (s, "SRv6 Transit gtp4.dt\n\t");
if (ls_mem->type == SRV6_GTP4_DT4)
s = format (s, " Type GTP4.DT4 fib-table %u\n", ls_mem->fib4_index);
else if (ls_mem->type == SRV6_GTP4_DT6)
s = format (s, " Type GTP4.DT6, fib-table %u, local-fib-table %u\n",
ls_mem->fib6_index, ls_mem->local_fib_index);
else if (ls_mem->type == SRV6_GTP4_DT46)
s = format (s, " Type GTP4.DT46, fib-table %u, local-fib-table %u\n",
ls_mem->fib6_index, ls_mem->local_fib_index);
else
s = format (s, "\n");
return s;
}
static uword
clb_unformat_srv6_t_m_gtp4_dt (unformat_input_t * input, va_list * args)
{
void **plugin_mem_p = va_arg (*args, void **);
srv6_t_gtp4_dt_param_t *ls_mem;
u32 fib_index = 0;
u32 local_fib_index = 0;
u32 type;
if (unformat (input, "t.m.gtp4.dt4 fib-table %u", &fib_index))
{
type = SRV6_GTP4_DT4;
}
else if (unformat (input, "t.m.gtp4.dt6 fib-table %u local-fib-table %u",
&fib_index, &local_fib_index))
{
type = SRV6_GTP4_DT6;
}
else if (unformat (input, "t.m.gtp4.dt46 fib-table %u local-fib-table %u",
&fib_index, &local_fib_index))
{
type = SRV6_GTP4_DT46;
}
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->fib4_index = fib_table_find (FIB_PROTOCOL_IP4, fib_index);
ls_mem->fib6_index = fib_table_find (FIB_PROTOCOL_IP6, fib_index);
if (type == SRV6_GTP4_DT6 || type == SRV6_GTP4_DT46)
{
ls_mem->local_fib_index =
fib_table_find (FIB_PROTOCOL_IP6, local_fib_index);
}
ls_mem->type = type;
return 1;
}
static int
clb_creation_srv6_t_m_gtp4_dt (ip6_sr_policy_t * sr_policy)
{
return 0;
}
static int
clb_removal_srv6_t_m_gtp4_dt (ip6_sr_policy_t * sr_policy)
{
srv6_t_gtp4_dt_param_t *ls_mem;
ls_mem = (srv6_t_gtp4_dt_param_t *) sr_policy->plugin_mem;
clib_mem_free (ls_mem);
return 0;
}
static clib_error_t *
srv6_t_m_gtp4_dt_init (vlib_main_t * vm)
{
srv6_t_main_v4_dt_t *sm = &srv6_t_main_v4_dt;
dpo_type_t dpo_type;
vlib_node_t *node;
int rc;
sm->vlib_main = vm;
sm->vnet_main = vnet_get_main ();
node = vlib_get_node_by_name (vm, (u8 *) "srv6-t-m-gtp4-dt");
sm->t_m_gtp4_dt_node_index = node->index;
node = vlib_get_node_by_name (vm, (u8 *) "error-drop");
sm->error_node_index = node->index;
dpo_type = dpo_register_new_type (&dpo_vft, dpo_nodes);
rc = sr_policy_register_function (vm, fn_name, keyword_str, def_str, param_str, 128, //prefix len
&dpo_type,
clb_format_srv6_t_m_gtp4_dt,
clb_unformat_srv6_t_m_gtp4_dt,
clb_creation_srv6_t_m_gtp4_dt,
clb_removal_srv6_t_m_gtp4_dt);
if (rc < 0)
clib_error_return (0, "SRv6 Transit GTP4.DT Policy function"
"couldn't be registered");
return 0;
}
/* *INDENT-OFF* */
VNET_FEATURE_INIT (srv6_t_m_gtp4_dt, static) =
{
.arc_name = "ip4-unicast",
.node_name = "srv6-t-m-gtp4-dt",
.runs_before = 0,
};
VLIB_INIT_FUNCTION (srv6_t_m_gtp4_dt_init);
/* *INDENT-ON* */
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/

View File

@ -0,0 +1,199 @@
/*
* srv6_end_m_gtp6_dt.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/fib/fib_table.h>
#include <vnet/plugin/plugin.h>
#include <vpp/app/version.h>
#include <srv6-mobile/mobile.h>
srv6_end_main_v6_dt_t srv6_end_main_v6_dt;
static void
clb_dpo_lock_srv6_end_m_gtp6_dt (dpo_id_t * dpo)
{
}
static void
clb_dpo_unlock_srv6_end_m_gtp6_dt (dpo_id_t * dpo)
{
}
static u8 *
clb_dpo_format_srv6_end_m_gtp6_dt (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_dt,
.dv_unlock = clb_dpo_unlock_srv6_end_m_gtp6_dt,
.dv_format = clb_dpo_format_srv6_end_m_gtp6_dt,
};
const static char *const srv6_end_m_gtp6_dt_nodes[] = {
"srv6-end-m-gtp6-dt",
NULL,
};
const static char *const *const dpo_nodes[DPO_PROTO_NUM] = {
[DPO_PROTO_IP6] = srv6_end_m_gtp6_dt_nodes,
};
static u8 fn_name[] = "SRv6-End.M.GTP6.DT-plugin";
static u8 keyword_str[] = "end.m.gtp6.dt";
static u8 def_str[] = "Endpoint function with DT for IPv6/GTP tunnel";
static u8 param_str[] = "fib-index <index> [local-fib-table <index>]";
static u8 *
clb_format_srv6_end_m_gtp6_dt (u8 * s, va_list * args)
{
srv6_end_gtp6_dt_param_t *ls_mem = va_arg (*args, void *);
s = format (s, "SRv6 End gtp6.dt\n\t");
if (ls_mem->type == SRV6_GTP6_DT4)
s = format (s, " Type GTP6.DT4 fib-table %u\n", ls_mem->fib4_index);
else if (ls_mem->type == SRV6_GTP6_DT6)
s = format (s, " Type GTP6.DT6, fib-table %u, local-fib-table %u\n",
ls_mem->fib6_index, ls_mem->local_fib_index);
else if (ls_mem->type == SRV6_GTP6_DT46)
s = format (s, " Type GTP6.DT46, fib-table %u, local-fib-table %u\n",
ls_mem->fib6_index, ls_mem->local_fib_index);
else
s = format (s, "\n");
return s;
}
static uword
clb_unformat_srv6_end_m_gtp6_dt (unformat_input_t * input, va_list * args)
{
void **plugin_mem_p = va_arg (*args, void **);
srv6_end_gtp6_dt_param_t *ls_mem;
u32 fib_index = 0;
u32 local_fib_index = 0;
u32 type;
if (unformat (input, "end.m.gtp6.dt4 fib-table %u", &fib_index))
{
type = SRV6_GTP6_DT4;
}
else if (unformat (input, "end.m.gtp6.dt6 fib-table %u local-fib-table %u",
&fib_index, &local_fib_index))
{
type = SRV6_GTP6_DT6;
}
else if (unformat (input, "end.m.gtp6.dt46 fib-table %u local-fib-table %u",
&fib_index, &local_fib_index))
{
type = SRV6_GTP6_DT46;
}
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->fib4_index = fib_table_find (FIB_PROTOCOL_IP4, fib_index);
ls_mem->fib6_index = fib_table_find (FIB_PROTOCOL_IP6, fib_index);
if (type == SRV6_GTP6_DT6 || type == SRV6_GTP6_DT46)
{
ls_mem->local_fib_index =
fib_table_find (FIB_PROTOCOL_IP6, local_fib_index);
}
ls_mem->type = type;
return 1;
}
static int
clb_creation_srv6_end_m_gtp6_dt (ip6_sr_localsid_t * localsid)
{
return 0;
}
static int
clb_removal_srv6_end_m_gtp6_dt (ip6_sr_localsid_t * localsid)
{
srv6_end_gtp6_dt_param_t *ls_mem;
ls_mem = localsid->plugin_mem;
clib_mem_free (ls_mem);
return 0;
}
static clib_error_t *
srv6_end_m_gtp6_dt_init (vlib_main_t * vm)
{
srv6_end_main_v6_dt_t *sm = &srv6_end_main_v6_dt;
dpo_type_t dpo_type;
vlib_node_t *node;
int rc;
sm->vlib_main = vm;
sm->vnet_main = vnet_get_main ();
node = vlib_get_node_by_name (vm, (u8 *) "srv6-end-m-gtp6-dt");
sm->end_m_gtp6_dt_node_index = node->index;
node = vlib_get_node_by_name (vm, (u8 *) "error-drop");
sm->error_node_index = node->index;
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_dt,
clb_unformat_srv6_end_m_gtp6_dt,
clb_creation_srv6_end_m_gtp6_dt,
clb_removal_srv6_end_m_gtp6_dt);
if (rc < 0)
clib_error_return (0, "SRv6 Endpoint GTP6.DT LocalSID function"
"couldn't be registered");
return 0;
}
/* *INDENT-OFF* */
VNET_FEATURE_INIT (srv6_end_m_gtp6_dt, static) =
{
.arc_name = "ip6-unicast",
.node_name = "srv6-end-m-gtp6-dt",
.runs_before = 0,
};
VLIB_INIT_FUNCTION (srv6_end_m_gtp6_dt_init);
/* *INDENT-ON* */
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/

View File

@ -42,6 +42,11 @@
#define SRV6_GTP6_DT6 2
#define SRV6_GTP6_DT46 3
#define SRV6_GTP4_UNKNOW 0
#define SRV6_GTP4_DT4 1
#define SRV6_GTP4_DT6 2
#define SRV6_GTP4_DT46 3
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define BITALIGN2(A,B) A; B
#define BITALIGN3(A,B,C) A; B; C
@ -59,6 +64,19 @@
#define SRH_TAG_ERROR_INDICATION 0x0002
#define SRH_TAG_END_MARKER 0x0001
#define GTPU_RECOVERY_IE_TYPE 0x0e
#define GTPU_IE_MAX_SIZ 256
#define SRH_TLV_USER_PLANE_CONTAINER 0x0a /* tentative */
/* *INDENT-OFF* */
typedef struct
{
u8 type;
u8 restart_counter;
} __attribute__ ((packed)) gtpu_recovery_ie;
/* *INDENT-ON* */
/* *INDENT-OFF* */
typedef struct
{
@ -145,6 +163,17 @@ typedef struct
#define GTPU_PT_GTP (1<<4)
/* *INDENT-OFF* */
typedef struct
{
u8 type;
u8 length;
u8 value[0];
} __attribute__ ((packed)) user_plane_sub_tlv_t;
/* *INDENT-ON* */
#define USER_PLANE_SUB_TLV_IE 0x01
typedef struct srv6_end_gtp6_param_s
{
u8 nhtype;
@ -153,6 +182,24 @@ typedef struct srv6_end_gtp6_param_s
u32 sr_prefixlen;
} srv6_end_gtp6_param_t;
typedef struct srv6_end_gtp6_dt_param_s
{
u8 type;
u32 fib4_index;
u32 fib6_index;
u32 local_fib_index;
} srv6_end_gtp6_dt_param_t;
typedef struct srv6_t_gtp4_dt_param_s
{
u8 type;
u32 fib4_index;
u32 fib6_index;
u32 local_fib_index;
} srv6_t_gtp4_dt_param_t;
typedef struct srv6_end_gtp4_param_s
{
u8 nhtype;
@ -238,6 +285,30 @@ typedef struct srv6_end_main_v6_decap_di_s
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;
typedef struct srv6_end_main_v6_dt_s
{
vlib_main_t *vlib_main;
vnet_main_t *vnet_main;
u32 end_m_gtp6_dt_node_index;
u32 error_node_index;
} srv6_end_main_v6_dt_t;
extern srv6_end_main_v6_dt_t srv6_end_main_v6_dt;
extern vlib_node_registration_t srv6_end_m_gtp6_dt;
typedef struct srv6_t_main_v4_dt_s
{
vlib_main_t *vlib_main;
vnet_main_t *vnet_main;
u32 t_m_gtp4_dt_node_index;
u32 error_node_index;
} srv6_t_main_v4_dt_t;
extern srv6_t_main_v4_dt_t srv6_t_main_v4_dt;
extern vlib_node_registration_t srv6_t_m_gtp4_dt;
#endif /* __included_srv6_end_h__ */
/*

File diff suppressed because it is too large Load Diff

View File

@ -1,160 +0,0 @@
#!/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(dump=True))
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(dump=True))
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

File diff suppressed because it is too large Load Diff

View File

@ -149,6 +149,13 @@ typedef struct
ip6_address_t segments[0];
} __attribute__ ((packed)) ip6_sr_header_t;
typedef struct
{
u8 type;
u8 length;
u8 value[0];
} __attribute__ ((packed)) ip6_sr_tlv_t;
/*
* fd.io coding-style-patch-verification: ON
*