Add GTP-U plugin. VPP-694

Basic GTP-U feature

Change-Id: I31226f890a92c5303ac06e112ed7820cae52d9bd
Signed-off-by: Hongjun Ni <hongjun.ni@intel.com>
This commit is contained in:
Hongjun Ni
2017-04-12 19:21:16 +08:00
committed by Neale Ranns
parent e50ed1de1e
commit ef486b1545
20 changed files with 4743 additions and 0 deletions

View File

@ -147,6 +147,7 @@ AC_SUBST(AR_FLAGS)
PLUGIN_ENABLED(acl)
PLUGIN_ENABLED(dpdk)
PLUGIN_ENABLED(flowperpkt)
PLUGIN_ENABLED(gtpu)
PLUGIN_ENABLED(ila)
PLUGIN_ENABLED(ioam)
PLUGIN_ENABLED(ixge)

View File

@ -41,6 +41,11 @@ if ENABLE_FLOWPERPKT_PLUGIN
include flowperpkt.am
endif
if ENABLE_GTPU_PLUGIN
include gtpu.am
endif
if ENABLE_ILA_PLUGIN
include ila.am
endif

38
src/plugins/gtpu.am Normal file
View File

@ -0,0 +1,38 @@
# Copyright (c) 2016 Intel 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.
vppapitestplugins_LTLIBRARIES += gtpu_test_plugin.la
vppplugins_LTLIBRARIES += gtpu_plugin.la
gtpu_plugin_la_SOURCES = \
gtpu/gtpu_decap.c \
gtpu/gtpu_encap.c \
gtpu/gtpu.c \
gtpu/gtpu_api.c
BUILT_SOURCES += \
gtpu/gtpu.api.h \
gtpu/gtpu.api.json
API_FILES += gtpu/gtpu.api
noinst_HEADERS += \
gtpu/gtpu_all_api_h.h \
gtpu/gtpu_msg_enum.h \
gtpu/gtpu.api.h
gtpu_test_plugin_la_SOURCES = \
gtpu/gtpu_test.c \
gtpu/gtpu_plugin.api.h
# vi:syntax=automake

120
src/plugins/gtpu/gtpu.api Normal file
View File

@ -0,0 +1,120 @@
/*
* Copyright (c) 2017 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.
* 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.
*/
/** \brief /** \brief Set or delete an GTPU tunnel
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
@param is_add - add address if non-zero, else delete
@param is_ipv6 - src_address and dst_address is ipv6 or not
@param src_address - GTPU tunnel's source address.
@param dst_address - GTPU tunnel's destination address.
@param mcast_sw_if_index - version, O-bit and C-bit (see nsh_packet.h)
@param encap_vrf_id - fib identifier used for outgoing encapsulated packets
@param decap_next_index - the index of the next node if success
@param teid - Local Tunnel Endpoint Identifier
*/
define gtpu_add_del_tunnel
{
u32 client_index;
u32 context;
u8 is_add;
u8 is_ipv6;
u8 src_address[16];
u8 dst_address[16];
u32 mcast_sw_if_index;
u32 encap_vrf_id;
u32 decap_next_index;
u32 teid;
};
/** \brief reply for set or delete an GTPU tunnel
@param context - sender context, to match reply w/ request
@param retval - return code
@param sw_if_index - software index of the interface
*/
define gtpu_add_del_tunnel_reply
{
u32 context;
i32 retval;
u32 sw_if_index;
};
/** \brief Dump GTPU tunnel
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
@param sw_if_index - software index of the interface
*/
define gtpu_tunnel_dump
{
u32 client_index;
u32 context;
u32 sw_if_index;
};
/** \brief /** \brief dump details of an GTPU tunnel
@param context - sender context, to match reply w/ request
@param sw_if_index - software index of the interface
@param is_ipv6 - src_address and dst_address is ipv6 or not
@param src_address - GTPU tunnel's source address.
@param dst_address - GTPU tunnel's destination address.
@param mcast_sw_if_index - version, O-bit and C-bit (see nsh_packet.h)
@param encap_vrf_id - fib identifier used for outgoing encapsulated packets
@param decap_next_index - the index of the next node if success
@param teid - Local Tunnel Endpoint Identifier
*/
define gtpu_tunnel_details
{
u32 context;
u32 sw_if_index;
u8 is_ipv6;
u8 src_address[16];
u8 dst_address[16];
u32 mcast_sw_if_index;
u32 encap_vrf_id;
u32 decap_next_index;
u32 teid;
};
/** \brief Interface set gtpu-bypass request
@param client_index - opaque cookie to identify the sender
@param context - sender context, to match reply w/ request
@param sw_if_index - interface used to reach neighbor
@param is_ipv6 - if non-zero, enable ipv6-gtpu-bypass, else ipv4-gtpu-bypass
@param enable - if non-zero enable, else disable
*/
define sw_interface_set_gtpu_bypass
{
u32 client_index;
u32 context;
u32 sw_if_index;
u8 is_ipv6;
u8 enable;
};
/** \brief Interface set gtpu-bypass response
@param context - sender context, to match reply w/ request
@param retval - return code for the request
*/
define sw_interface_set_gtpu_bypass_reply
{
u32 context;
i32 retval;
};
/*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/

1128
src/plugins/gtpu/gtpu.c Normal file

File diff suppressed because it is too large Load Diff

262
src/plugins/gtpu/gtpu.h Normal file
View File

@ -0,0 +1,262 @@
/*
*------------------------------------------------------------------
* Copyright (c) 2016 Intel 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_vnet_gtpu_h
#define included_vnet_gtpu_h
#include <vppinfra/lock.h>
#include <vppinfra/error.h>
#include <vppinfra/hash.h>
#include <vnet/vnet.h>
#include <vnet/ip/ip.h>
#include <vnet/l2/l2_input.h>
#include <vnet/l2/l2_output.h>
#include <vnet/l2/l2_bd.h>
#include <vnet/ethernet/ethernet.h>
#include <vnet/ip/ip4_packet.h>
#include <vnet/ip/ip6_packet.h>
#include <vnet/udp/udp.h>
#include <vnet/dpo/dpo.h>
#include <vnet/adj/adj_types.h>
#include <vnet/fib/fib_table.h>
/**
* Bits
* Octets 8 7 6 5 4 3 2 1
* 1 Version PT (*) E S PN
* 2 Message Type
* 3 Length (1st Octet)
* 4 Length (2nd Octet)
* 5 Tunnel Endpoint Identifier (1st Octet)
* 6 Tunnel Endpoint Identifier (2nd Octet)
* 7 Tunnel Endpoint Identifier (3rd Octet)
* 8 Tunnel Endpoint Identifier (4th Octet)
* 9 Sequence Number (1st Octet)1) 4)
* 10 Sequence Number (2nd Octet)1) 4)
* 11 N-PDU Number2) 4)
* 12 Next Extension Header Type3) 4)
**/
typedef struct
{
u8 ver_flags;
u8 type;
u16 length; /* length in octets of the payload */
u32 teid;
u16 sequence;
u8 pdu_number;
u8 next_ext_type;
} gtpu_header_t;
#define GTPU_VER_MASK (7<<5)
#define GTPU_PT_BIT (1<<4)
#define GTPU_E_BIT (1<<2)
#define GTPU_S_BIT (1<<1)
#define GTPU_PN_BIT (1<<0)
#define GTPU_E_S_PN_BIT (7<<0)
#define GTPU_V1_VER (1<<5)
#define GTPU_PT_GTP (1<<4)
#define GTPU_TYPE_GTPU 255
/* *INDENT-OFF* */
typedef CLIB_PACKED(struct
{
ip4_header_t ip4; /* 20 bytes */
udp_header_t udp; /* 8 bytes */
gtpu_header_t gtpu; /* 8 bytes */
}) ip4_gtpu_header_t;
/* *INDENT-ON* */
/* *INDENT-OFF* */
typedef CLIB_PACKED(struct
{
ip6_header_t ip6; /* 40 bytes */
udp_header_t udp; /* 8 bytes */
gtpu_header_t gtpu; /* 8 bytes */
}) ip6_gtpu_header_t;
/* *INDENT-ON* */
/* *INDENT-OFF* */
typedef CLIB_PACKED
(struct {
/*
* Key fields: ip src and gtpu teid on incoming gtpu packet
* all fields in NET byte order
*/
union {
struct {
u32 src;
u32 teid;
};
u64 as_u64;
};
}) gtpu4_tunnel_key_t;
/* *INDENT-ON* */
/* *INDENT-OFF* */
typedef CLIB_PACKED
(struct {
/*
* Key fields: ip src and gtpu teid on incoming gtpu packet
* all fields in NET byte order
*/
ip6_address_t src;
u32 teid;
}) gtpu6_tunnel_key_t;
/* *INDENT-ON* */
typedef struct
{
/* Rewrite string */
u8 *rewrite;
/* FIB DPO for IP forwarding of gtpu encap packet */
dpo_id_t next_dpo;
/* gtpu teid in HOST byte order */
u32 teid;
/* tunnel src and dst addresses */
ip46_address_t src;
ip46_address_t dst;
/* mcast packet output intf index (used only if dst is mcast) */
u32 mcast_sw_if_index;
/* decap next index */
u32 decap_next_index;
/* The FIB index for src/dst addresses */
u32 encap_fib_index;
/* vnet intfc index */
u32 sw_if_index;
u32 hw_if_index;
/**
* Linkage into the FIB object graph
*/
fib_node_t node;
/*
* The FIB entry for (depending on gtpu tunnel is unicast or mcast)
* sending unicast gtpu encap packets or receiving mcast gtpu packets
*/
fib_node_index_t fib_entry_index;
adj_index_t mcast_adj_index;
/**
* The tunnel is a child of the FIB entry for its destination. This is
* so it receives updates when the forwarding information for that entry
* changes.
* The tunnels sibling index on the FIB entry's dependency list.
*/
u32 sibling_index;
} gtpu_tunnel_t;
#define foreach_gtpu_input_next \
_(DROP, "error-drop") \
_(L2_INPUT, "l2-input")
typedef enum
{
#define _(s,n) GTPU_INPUT_NEXT_##s,
foreach_gtpu_input_next
#undef _
GTPU_INPUT_N_NEXT,
} gtpu_input_next_t;
typedef enum
{
#define gtpu_error(n,s) GTPU_ERROR_##n,
#include <gtpu/gtpu_error.def>
#undef gtpu_error
GTPU_N_ERROR,
} gtpu_input_error_t;
typedef struct
{
/* vector of encap tunnel instances */
gtpu_tunnel_t *tunnels;
/* lookup tunnel by key */
uword *gtpu4_tunnel_by_key; /* keyed on ipv4.dst + teid */
uword *gtpu6_tunnel_by_key; /* keyed on ipv6.dst + teid */
/* local VTEP IPs ref count used by gtpu-bypass node to check if
received gtpu packet DIP matches any local VTEP address */
uword *vtep4; /* local ip4 VTEPs keyed on their ip4 addr */
uword *vtep6; /* local ip6 VTEPs keyed on their ip6 addr */
/* mcast shared info */
uword *mcast_shared; /* keyed on mcast ip46 addr */
/* Free vlib hw_if_indices */
u32 *free_gtpu_tunnel_hw_if_indices;
/* Mapping from sw_if_index to tunnel index */
u32 *tunnel_index_by_sw_if_index;
/**
* Node type for registering to fib changes.
*/
fib_node_type_t fib_node_type;
/* API message ID base */
u16 msg_id_base;
/* convenience */
vlib_main_t *vlib_main;
vnet_main_t *vnet_main;
} gtpu_main_t;
gtpu_main_t gtpu_main;
extern vlib_node_registration_t gtpu4_input_node;
extern vlib_node_registration_t gtpu6_input_node;
extern vlib_node_registration_t gtpu4_encap_node;
extern vlib_node_registration_t gtpu6_encap_node;
u8 *format_gtpu_encap_trace (u8 * s, va_list * args);
typedef struct
{
u8 is_add;
u8 is_ip6;
ip46_address_t src, dst;
u32 mcast_sw_if_index;
u32 encap_fib_index;
u32 decap_next_index;
u32 teid;
} vnet_gtpu_add_del_tunnel_args_t;
int vnet_gtpu_add_del_tunnel
(vnet_gtpu_add_del_tunnel_args_t * a, u32 * sw_if_indexp);
void vnet_int_gtpu_bypass_mode (u32 sw_if_index, u8 is_ip6, u8 is_enable);
#endif /* included_vnet_gtpu_h */
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/

View File

@ -0,0 +1,18 @@
/*
* gtpu_all_api_h.h - plug-in api #include file
*
* Copyright (c) Intel 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 the generated file, see BUILT_SOURCES in Makefile.am */
#include <gtpu/gtpu.api.h>

256
src/plugins/gtpu/gtpu_api.c Normal file
View File

@ -0,0 +1,256 @@
/*
*------------------------------------------------------------------
* gtpu_api.c - gtpu api
*
* Copyright (c) 2016 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.
* 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/interface.h>
#include <vnet/api_errno.h>
#include <vnet/feature/feature.h>
#include <vnet/fib/fib_table.h>
#include <vppinfra/byte_order.h>
#include <vlibmemory/api.h>
#include <vlibsocket/api.h>
#include <gtpu/gtpu.h>
#define vl_msg_id(n,h) n,
typedef enum
{
#include <gtpu/gtpu.api.h>
/* We'll want to know how many messages IDs we need... */
VL_MSG_FIRST_AVAILABLE,
} vl_msg_id_t;
#undef vl_msg_id
/* define message structures */
#define vl_typedefs
#include <gtpu/gtpu.api.h>
#undef vl_typedefs
/* define generated endian-swappers */
#define vl_endianfun
#include <gtpu/gtpu.api.h>
#undef vl_endianfun
/* instantiate all the print functions we know about */
#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
#define vl_printfun
#include <gtpu/gtpu.api.h>
#undef vl_printfun
/* Get the API version number */
#define vl_api_version(n,v) static u32 api_version=(v);
#include <gtpu/gtpu.api.h>
#undef vl_api_version
#define vl_msg_name_crc_list
#include <gtpu/gtpu.api.h>
#undef vl_msg_name_crc_list
#define REPLY_MSG_ID_BASE gtm->msg_id_base
#include <vlibapi/api_helper_macros.h>
static void
setup_message_id_table (gtpu_main_t * gtm, api_main_t * am)
{
#define _(id,n,crc) \
vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + gtm->msg_id_base);
foreach_vl_msg_name_crc_gtpu;
#undef _
}
#define foreach_gtpu_plugin_api_msg \
_(SW_INTERFACE_SET_GTPU_BYPASS, sw_interface_set_gtpu_bypass) \
_(GTPU_ADD_DEL_TUNNEL, gtpu_add_del_tunnel) \
_(GTPU_TUNNEL_DUMP, gtpu_tunnel_dump)
static void
vl_api_sw_interface_set_gtpu_bypass_t_handler
(vl_api_sw_interface_set_gtpu_bypass_t * mp)
{
vl_api_sw_interface_set_gtpu_bypass_reply_t *rmp;
int rv = 0;
u32 sw_if_index = ntohl (mp->sw_if_index);
gtpu_main_t *gtm = &gtpu_main;
VALIDATE_SW_IF_INDEX (mp);
vnet_int_gtpu_bypass_mode (sw_if_index, mp->is_ipv6, mp->enable);
BAD_SW_IF_INDEX_LABEL;
REPLY_MACRO (VL_API_SW_INTERFACE_SET_GTPU_BYPASS_REPLY);
}
static void vl_api_gtpu_add_del_tunnel_t_handler
(vl_api_gtpu_add_del_tunnel_t * mp)
{
vl_api_gtpu_add_del_tunnel_reply_t *rmp;
int rv = 0;
ip4_main_t *im = &ip4_main;
gtpu_main_t *gtm = &gtpu_main;
uword *p = hash_get (im->fib_index_by_table_id, ntohl (mp->encap_vrf_id));
if (!p)
{
rv = VNET_API_ERROR_NO_SUCH_FIB;
goto out;
}
vnet_gtpu_add_del_tunnel_args_t a = {
.is_add = mp->is_add,
.is_ip6 = mp->is_ipv6,
.mcast_sw_if_index = ntohl (mp->mcast_sw_if_index),
.encap_fib_index = p[0],
.decap_next_index = ntohl (mp->decap_next_index),
.teid = ntohl (mp->teid),
.dst = to_ip46 (mp->is_ipv6, mp->dst_address),
.src = to_ip46 (mp->is_ipv6, mp->src_address),
};
/* Check src & dst are different */
if (ip46_address_cmp (&a.dst, &a.src) == 0)
{
rv = VNET_API_ERROR_SAME_SRC_DST;
goto out;
}
if (ip46_address_is_multicast (&a.dst) &&
!vnet_sw_if_index_is_api_valid (a.mcast_sw_if_index))
{
rv = VNET_API_ERROR_INVALID_SW_IF_INDEX;
goto out;
}
u32 sw_if_index = ~0;
rv = vnet_gtpu_add_del_tunnel (&a, &sw_if_index);
out:
/* *INDENT-OFF* */
REPLY_MACRO2(VL_API_GTPU_ADD_DEL_TUNNEL_REPLY,
({
rmp->sw_if_index = ntohl (sw_if_index);
}));
/* *INDENT-ON* */
}
static void send_gtpu_tunnel_details
(gtpu_tunnel_t * t, unix_shared_memory_queue_t * q, u32 context)
{
vl_api_gtpu_tunnel_details_t *rmp;
ip4_main_t *im4 = &ip4_main;
ip6_main_t *im6 = &ip6_main;
u8 is_ipv6 = !ip46_address_is_ip4 (&t->dst);
rmp = vl_msg_api_alloc (sizeof (*rmp));
memset (rmp, 0, sizeof (*rmp));
rmp->_vl_msg_id = ntohs (VL_API_GTPU_TUNNEL_DETAILS);
if (is_ipv6)
{
memcpy (rmp->src_address, t->src.ip6.as_u8, 16);
memcpy (rmp->dst_address, t->dst.ip6.as_u8, 16);
rmp->encap_vrf_id = htonl (im6->fibs[t->encap_fib_index].ft_table_id);
}
else
{
memcpy (rmp->src_address, t->src.ip4.as_u8, 4);
memcpy (rmp->dst_address, t->dst.ip4.as_u8, 4);
rmp->encap_vrf_id = htonl (im4->fibs[t->encap_fib_index].ft_table_id);
}
rmp->mcast_sw_if_index = htonl (t->mcast_sw_if_index);
rmp->teid = htonl (t->teid);
rmp->decap_next_index = htonl (t->decap_next_index);
rmp->sw_if_index = htonl (t->sw_if_index);
rmp->is_ipv6 = is_ipv6;
rmp->context = context;
vl_msg_api_send_shmem (q, (u8 *) & rmp);
}
static void
vl_api_gtpu_tunnel_dump_t_handler (vl_api_gtpu_tunnel_dump_t * mp)
{
unix_shared_memory_queue_t *q;
gtpu_main_t *gtm = &gtpu_main;
gtpu_tunnel_t *t;
u32 sw_if_index;
q = vl_api_client_index_to_input_queue (mp->client_index);
if (q == 0)
{
return;
}
sw_if_index = ntohl (mp->sw_if_index);
if (~0 == sw_if_index)
{
/* *INDENT-OFF* */
pool_foreach (t, gtm->tunnels,
({
send_gtpu_tunnel_details(t, q, mp->context);
}));
/* *INDENT-ON* */
}
else
{
if ((sw_if_index >= vec_len (gtm->tunnel_index_by_sw_if_index)) ||
(~0 == gtm->tunnel_index_by_sw_if_index[sw_if_index]))
{
return;
}
t = &gtm->tunnels[gtm->tunnel_index_by_sw_if_index[sw_if_index]];
send_gtpu_tunnel_details (t, q, mp->context);
}
}
static clib_error_t *
gtpu_api_hookup (vlib_main_t * vm)
{
gtpu_main_t *gtm = &gtpu_main;
u8 *name = format (0, "gtpu_%08x%c", api_version, 0);
gtm->msg_id_base = vl_msg_api_get_msg_ids
((char *) name, VL_MSG_FIRST_AVAILABLE);
#define _(N,n) \
vl_msg_api_set_handlers((VL_API_##N + gtm->msg_id_base), \
#n, \
vl_api_##n##_t_handler, \
vl_noop_handler, \
vl_api_##n##_t_endian, \
vl_api_##n##_t_print, \
sizeof(vl_api_##n##_t), 1);
foreach_gtpu_plugin_api_msg;
#undef _
/* Add our API messages to the global name_crc hash table */
setup_message_id_table (gtm, &api_main);
return 0;
}
VLIB_API_INIT_FUNCTION (gtpu_api_hookup);
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
/*
* 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.
* 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.
*/
gtpu_error (DECAPSULATED, "good packets decapsulated")
gtpu_error (NO_SUCH_TUNNEL, "no such tunnel packets")
gtpu_error (BAD_VER, "packets with bad version in gtpu header")
gtpu_error (BAD_FLAGS, "packets with bad flags field in gtpu header")

View File

@ -0,0 +1,31 @@
/*
* gtpu_msg_enum.h - vpp engine plug-in message enumeration
*
* Copyright (c) <current-year> <your-organization>
* 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_gtpu_msg_enum_h
#define included_gtpu_msg_enum_h
#include <vppinfra/byte_order.h>
#define vl_msg_id(n,h) n,
typedef enum
{
#include <gtpu/gtpu_all_api_h.h>
/* We'll want to know how many messages IDs we need... */
VL_MSG_FIRST_AVAILABLE,
} vl_msg_id_t;
#undef vl_msg_id
#endif /* included_gtpu_msg_enum_h */

File diff suppressed because it is too large Load Diff

View File

@ -167,6 +167,7 @@ typedef enum mfib_source_t_
MFIB_SOURCE_DHCP,
MFIB_SOURCE_SRv6,
MFIB_SOURCE_DEFAULT_ROUTE,
MFIB_SOURCE_GTPU,
} mfib_source_t;
#define MFIB_SOURCE_NAMES { \
@ -177,6 +178,7 @@ typedef enum mfib_source_t_
[MFIB_SOURCE_VXLAN] = "VXLAN", \
[MFIB_SOURCE_SRv6] = "SRv6", \
[MFIB_SOURCE_DEFAULT_ROUTE] = "Default Route", \
[MFIB_SOURCE_GTPU] = "GTPU", \
}
/**

View File

@ -81,6 +81,7 @@ typedef enum
_ (67, dhcp_to_server) \
_ (68, dhcp_to_client) \
_ (500, ikev2) \
_ (2152, GTPU) \
_ (3784, bfd4) \
_ (3785, bfd_echo4) \
_ (4341, lisp_gpe) \
@ -95,6 +96,7 @@ _ (6633, vpath_3)
#define foreach_udp6_dst_port \
_ (547, dhcpv6_to_server) \
_ (546, dhcpv6_to_client) \
_ (2152, GTPU6) \
_ (3784, bfd6) \
_ (3785, bfd_echo6) \
_ (4341, lisp_gpe6) \

View File

@ -128,6 +128,26 @@ jvpp-acl/io_fd_vpp_jvpp_acl_JVppAclImpl.h: $(jvpp_registry_ok) $(jvpp_acl_json_f
$(call japigen,acl,JVppAclImpl)
endif
#
# GTPU Plugin
#
if ENABLE_GTPU_PLUGIN
noinst_LTLIBRARIES += libjvpp_gtpu.la
libjvpp_gtpu_la_SOURCES = jvpp-gtpu/jvpp_gtpu.c
libjvpp_gtpu_la_CPPFLAGS = -Ijvpp-gtpu
libjvpp_gtpu_la_LIBADD = $(JVPP_LIBS)
libjvpp_gtpu_la_DEPENDENCIES = libjvpp_common.la
BUILT_SOURCES += jvpp-gtpu/io_fd_vpp_jvpp_gtpu_JVppGtpuImpl.h
JAR_FILES += jvpp-gtpu-$(PACKAGE_VERSION).jar
CLEANDIRS += jvpp-gtpu/target
jvpp_gtpu_json_files = @top_builddir@/plugins/gtpu/gtpu.api.json
jvpp-gtpu/io_fd_vpp_jvpp_gtpu_JVppGtpuImpl.h: $(jvpp_registry_ok) $(jvpp_gtpu_json_files)
$(call japigen,gtpu,JVppGtpuImpl)
endif
#
# SNAT Plugin
#

View File

@ -0,0 +1,107 @@
/*
* Copyright (c) 2016 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.
* 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 <gtpu/gtpu_msg_enum.h>
#define vl_typedefs /* define message structures */
#include <gtpu/gtpu_all_api_h.h>
#undef vl_typedefs
#include <vnet/api_errno.h>
#include <vlibapi/api.h>
#include <vlibmemory/api.h>
#if VPPJNI_DEBUG == 1
#define DEBUG_LOG(...) clib_warning(__VA_ARGS__)
#else
#define DEBUG_LOG(...)
#endif
#include <jvpp-common/jvpp_common.h>
#include "jvpp-gtpu/io_fd_vpp_jvpp_gtpu_JVppGtpuImpl.h"
#include "jvpp_gtpu.h"
#include "jvpp-gtpu/jvpp_gtpu_gen.h"
/*
* Class: io_fd_vpp_jvpp_gtpu_JVppgtpuImpl
* Method: init0
* Signature: (JI)V
*/
JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_gtpu_JVppGtpuImpl_init0
(JNIEnv *env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) {
gtpu_main_t * plugin_main = &gtpu_main;
clib_warning ("Java_io_fd_vpp_jvpp_gtpu_JVppGtpuImpl_init0");
plugin_main->my_client_index = my_client_index;
plugin_main->vl_input_queue = (unix_shared_memory_queue_t *)queue_address;
plugin_main->callbackObject = (*env)->NewGlobalRef(env, callback);
plugin_main->callbackClass = (jclass)(*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, callback));
// verify API has not changed since jar generation
#define _(N) \
get_message_id(env, #N); \
foreach_supported_api_message;
#undef _
#define _(N,n) \
vl_msg_api_set_handlers(get_message_id(env, #N), #n, \
vl_api_##n##_t_handler, \
vl_noop_handler, \
vl_noop_handler, \
vl_noop_handler, \
sizeof(vl_api_##n##_t), 1);
foreach_api_reply_handler;
#undef _
}
JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_gtpu_JVppGtpuImpl_close0
(JNIEnv *env, jclass clazz) {
gtpu_main_t * plugin_main = &gtpu_main;
// cleanup:
(*env)->DeleteGlobalRef(env, plugin_main->callbackClass);
(*env)->DeleteGlobalRef(env, plugin_main->callbackObject);
plugin_main->callbackClass = NULL;
plugin_main->callbackObject = NULL;
}
/* Attach thread to JVM and cache class references when initiating JVPP ACL */
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv* env;
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
return JNI_EVERSION;
}
if (cache_class_references(env) != 0) {
clib_warning ("Failed to cache class references\n");
return JNI_ERR;
}
return JNI_VERSION_1_8;
}
/* Clean up cached references when disposing JVPP ACL */
void JNI_OnUnload(JavaVM *vm, void *reserved) {
JNIEnv* env;
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) {
return;
}
delete_class_references(env);
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) 2016 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.
* 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_jvpp_gtpu_h__
#define __included_jvpp_gtpu_h__
#include <vnet/vnet.h>
#include <vnet/ip/ip.h>
#include <vnet/api_errno.h>
#include <vlibapi/api.h>
#include <vlibmemory/api.h>
#include <jni.h>
/* Global state for JVPP-gtpu */
typedef struct {
/* Pointer to shared memory queue */
unix_shared_memory_queue_t * vl_input_queue;
/* VPP api client index */
u32 my_client_index;
/* Callback object and class references enabling asynchronous Java calls */
jobject callbackObject;
jclass callbackClass;
} gtpu_main_t;
gtpu_main_t gtpu_main __attribute__((aligned (64)));
#endif /* __included_jvpp_gtpu_h__ */

289
test/test_gtpu.py Normal file
View File

@ -0,0 +1,289 @@
#!/usr/bin/env python
import socket
from util import ip4n_range
import unittest
from framework import VppTestCase, VppTestRunner
from template_bd import BridgeDomain
from scapy.layers.l2 import Ether, Raw
from scapy.layers.inet import IP, UDP
from scapy.contrib.gtp import GTP_U_Header
from scapy.utils import atol
class TestGtpu(BridgeDomain, VppTestCase):
""" GTPU Test Case """
def __init__(self, *args):
BridgeDomain.__init__(self)
VppTestCase.__init__(self, *args)
def encapsulate(self, pkt, vni):
"""
Encapsulate the original payload frame by adding GTPU header with its
UDP, IP and Ethernet fields
"""
return (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) /
IP(src=self.pg0.remote_ip4, dst=self.pg0.local_ip4) /
UDP(sport=self.dport, dport=self.dport, chksum=0) /
GTP_U_Header(TEID=vni, gtp_type=self.gtp_type, length=150) /
pkt)
def encap_mcast(self, pkt, src_ip, src_mac, vni):
"""
Encapsulate the original payload frame by adding GTPU header with its
UDP, IP and Ethernet fields
"""
return (Ether(src=src_mac, dst=self.mcast_mac) /
IP(src=src_ip, dst=self.mcast_ip4) /
UDP(sport=self.dport, dport=self.dport, chksum=0) /
GTP_U_Header(TEID=vni, gtp_type=self.gtp_type, length=150) /
pkt)
def decapsulate(self, pkt):
"""
Decapsulate the original payload frame by removing GTPU header
"""
return pkt[GTP_U_Header].payload
# Method for checking GTPU encapsulation.
#
def check_encapsulation(self, pkt, vni, local_only=False, mcast_pkt=False):
# Verify source MAC is VPP_MAC and destination MAC is MY_MAC resolved
# by VPP using ARP.
self.assertEqual(pkt[Ether].src, self.pg0.local_mac)
if not local_only:
if not mcast_pkt:
self.assertEqual(pkt[Ether].dst, self.pg0.remote_mac)
else:
self.assertEqual(pkt[Ether].dst, type(self).mcast_mac)
# Verify GTPU tunnel source IP is VPP_IP and destination IP is MY_IP.
self.assertEqual(pkt[IP].src, self.pg0.local_ip4)
if not local_only:
if not mcast_pkt:
self.assertEqual(pkt[IP].dst, self.pg0.remote_ip4)
else:
self.assertEqual(pkt[IP].dst, type(self).mcast_ip4)
# Verify UDP destination port is GTPU 2152, source UDP port could be
# arbitrary.
self.assertEqual(pkt[UDP].dport, type(self).dport)
# Verify TEID
self.assertEqual(pkt[GTP_U_Header].TEID, vni)
def test_encap(self):
""" Encapsulation test
Send frames from pg1
Verify receipt of encapsulated frames on pg0
"""
self.pg1.add_stream([self.frame_reply])
self.pg0.enable_capture()
self.pg_start()
# Pick first received frame and check if it's corectly encapsulated.
out = self.pg0.get_capture(1)
pkt = out[0]
self.check_encapsulation(pkt, self.single_tunnel_bd)
# payload = self.decapsulate(pkt)
# self.assert_eq_pkts(payload, self.frame_reply)
def test_ucast_flood(self):
""" Unicast flood test
Send frames from pg3
Verify receipt of encapsulated frames on pg0
"""
self.pg3.add_stream([self.frame_reply])
self.pg0.enable_capture()
self.pg_start()
# Get packet from each tunnel and assert it's corectly encapsulated.
out = self.pg0.get_capture(self.n_ucast_tunnels)
for pkt in out:
self.check_encapsulation(pkt, self.ucast_flood_bd, True)
# payload = self.decapsulate(pkt)
# self.assert_eq_pkts(payload, self.frame_reply)
def test_mcast_flood(self):
""" Multicast flood test
Send frames from pg2
Verify receipt of encapsulated frames on pg0
"""
self.pg2.add_stream([self.frame_reply])
self.pg0.enable_capture()
self.pg_start()
# Pick first received frame and check if it's corectly encapsulated.
out = self.pg0.get_capture(1)
pkt = out[0]
self.check_encapsulation(pkt, self.mcast_flood_bd,
local_only=False, mcast_pkt=True)
# payload = self.decapsulate(pkt)
# self.assert_eq_pkts(payload, self.frame_reply)
@classmethod
def create_gtpu_flood_test_bd(cls, teid, n_ucast_tunnels):
# Create 10 ucast gtpu tunnels under bd
ip_range_start = 10
ip_range_end = ip_range_start + n_ucast_tunnels
next_hop_address = cls.pg0.remote_ip4n
for dest_ip4n in ip4n_range(next_hop_address, ip_range_start,
ip_range_end):
# add host route so dest_ip4n will not be resolved
cls.vapi.ip_add_del_route(dest_ip4n, 32, next_hop_address)
r = cls.vapi.gtpu_add_del_tunnel(
src_addr=cls.pg0.local_ip4n,
dst_addr=dest_ip4n,
teid=teid)
cls.vapi.sw_interface_set_l2_bridge(r.sw_if_index, bd_id=teid)
@classmethod
def add_del_shared_mcast_dst_load(cls, is_add):
"""
add or del tunnels sharing the same mcast dst
to test gtpu ref_count mechanism
"""
n_shared_dst_tunnels = 20
teid_start = 1000
teid_end = teid_start + n_shared_dst_tunnels
for teid in range(teid_start, teid_end):
r = cls.vapi.gtpu_add_del_tunnel(
src_addr=cls.pg0.local_ip4n,
dst_addr=cls.mcast_ip4n,
mcast_sw_if_index=1,
teid=teid,
is_add=is_add)
if r.sw_if_index == 0xffffffff:
raise "bad sw_if_index"
@classmethod
def add_shared_mcast_dst_load(cls):
cls.add_del_shared_mcast_dst_load(is_add=1)
@classmethod
def del_shared_mcast_dst_load(cls):
cls.add_del_shared_mcast_dst_load(is_add=0)
@classmethod
def add_del_mcast_tunnels_load(cls, is_add):
"""
add or del tunnels to test gtpu stability
"""
n_distinct_dst_tunnels = 20
ip_range_start = 10
ip_range_end = ip_range_start + n_distinct_dst_tunnels
for dest_ip4n in ip4n_range(cls.mcast_ip4n, ip_range_start,
ip_range_end):
teid = bytearray(dest_ip4n)[3]
cls.vapi.gtpu_add_del_tunnel(
src_addr=cls.pg0.local_ip4n,
dst_addr=dest_ip4n,
mcast_sw_if_index=1,
teid=teid,
is_add=is_add)
@classmethod
def add_mcast_tunnels_load(cls):
cls.add_del_mcast_tunnels_load(is_add=1)
@classmethod
def del_mcast_tunnels_load(cls):
cls.add_del_mcast_tunnels_load(is_add=0)
# Class method to start the GTPU test case.
# Overrides setUpClass method in VppTestCase class.
# Python try..except statement is used to ensure that the tear down of
# the class will be executed even if exception is raised.
# @param cls The class pointer.
@classmethod
def setUpClass(cls):
super(TestGtpu, cls).setUpClass()
try:
cls.dport = 2152
cls.gtp_type = 0xff
# Create 2 pg interfaces.
cls.create_pg_interfaces(range(4))
for pg in cls.pg_interfaces:
pg.admin_up()
# Configure IPv4 addresses on VPP pg0.
cls.pg0.config_ip4()
# Resolve MAC address for VPP's IP address on pg0.
cls.pg0.resolve_arp()
# Our Multicast address
cls.mcast_ip4 = '239.1.1.1'
cls.mcast_ip4n = socket.inet_pton(socket.AF_INET, cls.mcast_ip4)
iplong = atol(cls.mcast_ip4)
cls.mcast_mac = "01:00:5e:%02x:%02x:%02x" % (
(iplong >> 16) & 0x7F, (iplong >> 8) & 0xFF, iplong & 0xFF)
# Create GTPU VTEP on VPP pg0, and put gtpu_tunnel0 and pg1
# into BD.
cls.single_tunnel_bd = 11
r = cls.vapi.gtpu_add_del_tunnel(
src_addr=cls.pg0.local_ip4n,
dst_addr=cls.pg0.remote_ip4n,
teid=cls.single_tunnel_bd)
cls.vapi.sw_interface_set_l2_bridge(r.sw_if_index,
bd_id=cls.single_tunnel_bd)
cls.vapi.sw_interface_set_l2_bridge(cls.pg1.sw_if_index,
bd_id=cls.single_tunnel_bd)
# Setup teid 2 to test multicast flooding
cls.n_ucast_tunnels = 10
cls.mcast_flood_bd = 12
cls.create_gtpu_flood_test_bd(cls.mcast_flood_bd,
cls.n_ucast_tunnels)
r = cls.vapi.gtpu_add_del_tunnel(
src_addr=cls.pg0.local_ip4n,
dst_addr=cls.mcast_ip4n,
mcast_sw_if_index=1,
teid=cls.mcast_flood_bd)
cls.vapi.sw_interface_set_l2_bridge(r.sw_if_index,
bd_id=cls.mcast_flood_bd)
cls.vapi.sw_interface_set_l2_bridge(cls.pg2.sw_if_index,
bd_id=cls.mcast_flood_bd)
# Add and delete mcast tunnels to check stability
cls.add_shared_mcast_dst_load()
cls.add_mcast_tunnels_load()
cls.del_shared_mcast_dst_load()
cls.del_mcast_tunnels_load()
# Setup teid 3 to test unicast flooding
cls.ucast_flood_bd = 13
cls.create_gtpu_flood_test_bd(cls.ucast_flood_bd,
cls.n_ucast_tunnels)
cls.vapi.sw_interface_set_l2_bridge(cls.pg3.sw_if_index,
bd_id=cls.ucast_flood_bd)
except Exception:
super(TestGtpu, cls).tearDownClass()
raise
# Method to define VPP actions before tear down of the test case.
# Overrides tearDown method in VppTestCase class.
# @param self The object pointer.
def tearDown(self):
super(TestGtpu, self).tearDown()
if not self.vpp_dead:
self.logger.info(self.vapi.cli("show bridge-domain 11 detail"))
self.logger.info(self.vapi.cli("show bridge-domain 12 detail"))
self.logger.info(self.vapi.cli("show bridge-domain 13 detail"))
self.logger.info(self.vapi.cli("show int"))
self.logger.info(self.vapi.cli("show gtpu tunnel"))
self.logger.info(self.vapi.cli("show trace"))
if __name__ == '__main__':
unittest.main(testRunner=VppTestRunner)

View File

@ -1775,3 +1775,35 @@ class VppPapiProvider(object):
'is_translation': is_translation,
'mtu': mtu
})
def gtpu_add_del_tunnel(
self,
src_addr,
dst_addr,
is_add=1,
is_ipv6=0,
mcast_sw_if_index=0xFFFFFFFF,
encap_vrf_id=0,
decap_next_index=0xFFFFFFFF,
teid=0):
"""
:param is_add: (Default value = 1)
:param is_ipv6: (Default value = 0)
:param src_addr:
:param dst_addr:
:param mcast_sw_if_index: (Default value = 0xFFFFFFFF)
:param encap_vrf_id: (Default value = 0)
:param decap_next_index: (Default value = 0xFFFFFFFF)
:param teid: (Default value = 0)
"""
return self.api(self.papi.gtpu_add_del_tunnel,
{'is_add': is_add,
'is_ipv6': is_ipv6,
'src_address': src_addr,
'dst_address': dst_addr,
'mcast_sw_if_index': mcast_sw_if_index,
'encap_vrf_id': encap_vrf_id,
'decap_next_index': decap_next_index,
'teid': teid})