Simple ip4 NAT plugin

Change-Id: Iffe77bf2a05ced41474540ff54a842101aad7c41
Signed-off-by: Dave Barach <dave@barachs.net>
This commit is contained in:
Dave Barach
2016-06-26 10:42:08 -04:00
committed by Keith Burns
parent b8dca74c23
commit 20c02cb133
14 changed files with 2928 additions and 6 deletions

View File

@ -38,6 +38,10 @@ vnet_configure_args_vpp = --with-dpdk
# Set these parameters carefully. The vlib_buffer_t is 128 bytes, i.e.
vlib_configure_args_vpp = --with-pre-data=128
# Enable plugins here, and no place else, via multiple --enable-XXX-plugin
# stanzas.
plugins_configure_args_vpp = --with-dpdk --enable-sixrd-plugin
# DPDK configuration parameters
# vpp_uses_external_dpdk = yes
# vpp_dpdk_inc_dir = /usr/include/dpdk

View File

@ -29,3 +29,7 @@ endif
if ENABLE_VCGN_PLUGIN
SUBDIRS += vcgn-plugin
endif
if ENABLE_SNAT_PLUGIN
SUBDIRS += snat-plugin
endif

View File

@ -17,10 +17,30 @@ AC_ARG_WITH(plugin-toolkit,
[with_plugin_toolkit=${prefix}/include],
[with_plugin_toolkit=.])
AC_ARG_WITH(dpdk,
AC_HELP_STRING([--with-dpdk],[Use the Intel dpdk]),
[with_dpdk=1],
[with_dpdk=0])
AC_SUBST(TOOLKIT_INCLUDE,[${with_plugin_toolkit}])
AM_CONDITIONAL(WITH_PLUGIN_TOOLKIT, test "$with_plugin_toolkit" != ".")
AM_CONDITIONAL(ENABLE_TESTS, test "$enable_tests" = "1")
AM_CONDITIONAL(WITH_DPDK, test "$with_dpdk" = "1")
AC_SUBST(DPDK,["-DDPDK=${with_dpdk}"])
#
# Please DO NOT, UNDER ANY CIRCUMSTANCES enable or disable
# plugins by "clever" manipulation of the arguments to AC_ARG_ENABLE
#
# Instead, please configure the default set of plugins in
# .../build-data/platforms/<platform>.mk, by adding --enable-XXX-plugin
# stanzas to plugins_configure_args_<platform>
# The following per-plugin boilerplate is begging for an additional
# macro, but the first 10 tries at making one didn't work. Another day.
#
# Sample plugin
#
@ -40,8 +60,8 @@ AM_CONDITIONAL(ENABLE_SAMPLE_PLUGIN, test "$enable_sample_plugin" = "1")
#
AC_ARG_ENABLE(sixrd_plugin,
AC_HELP_STRING([--enable-sixrd-plugin], [Build sixrd plugin]),
[],
[enable_sixrd_plugin=1])
[enable_sixrd_plugin=1],
[enable_sixrd_plugin=0])
if test "x$enable_sixrd_plugin" = x1; then
AC_CONFIG_SUBDIRS([sixrd-plugin])
@ -54,8 +74,8 @@ AM_CONDITIONAL(ENABLE_SIXRD_PLUGIN, test "$enable_sixrd_plugin" = "1")
#
AC_ARG_ENABLE(ioam_plugin,
AC_HELP_STRING([--enable-ioam-plugin], [Build ioam plugin]),
[],
[enable_ioam_plugin=1])
[enable_ioam_plugin=1],
[enable_ioam_plugin=0])
if test "x$enable_ioam_plugin" = x1; then
AC_CONFIG_SUBDIRS([ioam-plugin])
@ -77,4 +97,19 @@ fi
AM_CONDITIONAL(ENABLE_VCGN_PLUGIN, test "$enable_vcgn_plugin" = "1")
#
# SNAT plugin
#
AC_ARG_ENABLE(snat_plugin,
AC_HELP_STRING([--enable-snat-plugin], [Build snat plugin]),
[enable_snat_plugin=1],
[enable_snat_plugin=0])
if test "x$enable_snat_plugin" = x1; then
AC_CONFIG_SUBDIRS([snat-plugin])
fi
AM_CONDITIONAL(ENABLE_SNAT_PLUGIN, test "$enable_snat_plugin" = "1")
AC_OUTPUT([Makefile])

View File

@ -0,0 +1,55 @@
# 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.
AUTOMAKE_OPTIONS = foreign subdir-objects
AM_CFLAGS = -Wall -I@TOOLKIT_INCLUDE@ @DPDK@
lib_LTLIBRARIES = snat_plugin.la snat_test_plugin.la
snat_plugin_la_SOURCES = snat/snat.c \
snat/in2out.c \
snat/out2in.c \
snat/snat_plugin.api.h
snat_plugin_la_LDFLAGS = -module
BUILT_SOURCES = snat/snat.api.h
SUFFIXES = .api.h .api
%.api.h: %.api
mkdir -p `dirname $@` ; \
$(CC) $(CPPFLAGS) -E -P -C -x c $^ \
| vppapigen --input - --output $@ --show-name $@
nobase_include_HEADERS = \
snat/snat_all_api_h.h \
snat/snat_msg_enum.h \
snat/snat.api.h
snat_test_plugin_la_SOURCES = \
snat/snat_test.c snat/snat_plugin.api.h
snat_test_plugin_la_LDFLAGS = -module
if WITH_PLUGIN_TOOLKIT
install-data-hook:
mkdir /usr/lib/vpp_plugins || true
mkdir /usr/lib/vpp_api_test_plugins || true
cp -L $(prefix)/lib/snat_plugin.so /usr/lib/vpp_plugins
cp -L $(prefix)/lib/snat_test_plugin.so \
/usr/lib/vpp_api_test_plugins
rm -f $(prefix)/lib/snat_plugin.*
rm -f $(prefix)/lib/snat_test_plugin.*
endif

View File

@ -0,0 +1,27 @@
AC_INIT(snat_plugin, 1.0)
AM_INIT_AUTOMAKE
AC_PROG_LIBTOOL
AM_PROG_AS
AC_PROG_CC
AM_PROG_CC_C_O
AC_ARG_WITH(dpdk,
AC_HELP_STRING([--with-dpdk],[Use the Intel dpdk]),
[with_dpdk=1],
[with_dpdk=0])
AM_CONDITIONAL(WITH_DPDK, test "$with_dpdk" = "1")
AC_SUBST(DPDK,["-DDPDK=${with_dpdk}"])
AC_ARG_WITH(plugin-toolkit,
AC_HELP_STRING([--with-plugin-toolkit],
[build using the vpp toolkit]),
[with_plugin_toolkit=${prefix}/include],
[with_plugin_toolkit=.])
AC_SUBST(TOOLKIT_INCLUDE,[${with_plugin_toolkit}])
AM_CONDITIONAL(WITH_PLUGIN_TOOLKIT, test "$with_plugin_toolkit" != ".")
AC_OUTPUT([Makefile])

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,25 @@
define snat_add_address_range {
u32 client_index;
u32 context;
u8 is_ip4;
u8 first_ip_address[16];
u8 last_ip_address[16];
};
define snat_add_address_range_reply {
u32 context;
i32 retval;
};
define snat_interface_add_del_feature {
u32 client_index;
u32 context;
u8 is_add;
u8 is_inside;
u32 sw_if_index;
};
define snat_interface_add_del_feature_reply {
u32 context;
i32 retval;
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,181 @@
/*
* snat.h - simple nat definitions
*
* 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_snat_h__
#define __included_snat_h__
#include <vnet/vnet.h>
#include <vnet/ip/ip.h>
#include <vnet/ethernet/ethernet.h>
#include <vnet/ip/icmp46_packet.h>
#include <vnet/api_errno.h>
#include <vppinfra/bihash_8_8.h>
#include <vppinfra/dlist.h>
#include <vppinfra/error.h>
#include <vlibapi/api.h>
/* Key */
typedef struct {
union
{
struct
{
ip4_address_t addr;
u16 port;
u16 protocol:3,
fib_index:13;
};
u64 as_u64;
};
} snat_session_key_t;
typedef struct {
union
{
struct
{
ip4_address_t addr;
u32 fib_index;
};
u64 as_u64;
};
} snat_user_key_t;
typedef enum {
SNAT_PROTOCOL_UDP = 0,
SNAT_PROTOCOL_TCP,
SNAT_PROTOCOL_ICMP,
} snat_protocol_t;
typedef CLIB_PACKED(struct {
snat_session_key_t out2in; /* 0-15 */
snat_session_key_t in2out; /* 16-31 */
u32 flags; /* 32-35 */
/* per-user translations */
u32 per_user_index; /* 36-39 */
u32 per_user_list_head_index; /* 40-43 */
/* Last heard timer */
f64 last_heard; /* 44-51 */
u64 total_bytes; /* 52-59 */
u32 total_pkts; /* 60-63 */
/* Outside address */
u32 outside_address_index; /* 64-67 */
}) snat_session_t;
#define SNAT_SESSION_STATIC (1<<0)
typedef struct {
ip4_address_t addr;
u32 sessions_per_user_list_head_index;
u32 nsessions;
} snat_user_t;
typedef struct {
ip4_address_t addr;
u32 busy_ports;
uword * busy_port_bitmap;
} snat_address_t;
typedef struct {
/* Main lookup tables */
clib_bihash_8_8_t out2in;
clib_bihash_8_8_t in2out;
/* Find-a-user => src address lookup */
clib_bihash_8_8_t user_hash;
/* User pool */
snat_user_t * users;
/* Session pool */
snat_session_t * sessions;
/* Vector of outside addresses */
snat_address_t * addresses;
/* Pool of doubly-linked list elements */
dlist_elt_t * list_pool;
/* Randomize port allocation order */
u32 random_seed;
/* ip4 feature path indices */
u32 rx_feature_in2out;
u32 rx_feature_out2in;
/* Config parameters */
u32 translation_buckets;
u32 translation_memory_size;
u32 user_buckets;
u32 user_memory_size;
u32 max_translations_per_user;
u32 outside_vrf_id;
u32 outside_fib_index;
/* API message ID base */
u16 msg_id_base;
/* convenience */
vlib_main_t * vlib_main;
vnet_main_t * vnet_main;
ip4_main_t * ip4_main;
ip_lookup_main_t * ip4_lookup_main;
ethernet_main_t * ethernet_main;
api_main_t * api_main;
} snat_main_t;
extern snat_main_t snat_main;
extern vlib_node_registration_t snat_in2out_node;
extern vlib_node_registration_t snat_out2in_node;
void snat_free_outside_address_and_port (snat_main_t * sm,
snat_session_key_t * k,
u32 address_index);
int snat_alloc_outside_address_and_port (snat_main_t * sm,
snat_session_key_t * k,
u32 * address_indexp);
format_function_t format_snat_user;
typedef struct {
u32 cached_sw_if_index;
u32 cached_ip4_address;
} snat_runtime_t;
/*
* Why is this here? Because we don't need to touch this layer to
* simply reply to an icmp. We need to change id to a unique
* value to NAT an echo request/reply.
*/
typedef struct {
u16 identifier;
u16 sequence;
} icmp_echo_header_t;
#endif /* __included_snat_h__ */

View File

@ -0,0 +1,19 @@
/*
* snat_all_api_h.h - skeleton vpp engine plug-in api #include file
*
* 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.
*/
/* Include the generated file, see BUILT_SOURCES in Makefile.am */
#include <snat/snat.api.h>

View File

@ -0,0 +1,31 @@
/*
* snat_msg_enum.h - skeleton 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_snat_msg_enum_h
#define included_snat_msg_enum_h
#include <vppinfra/byte_order.h>
#define vl_msg_id(n,h) n,
typedef enum {
#include <snat/snat_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_snat_msg_enum_h */

View File

@ -0,0 +1,265 @@
/*
* snat.c - skeleton vpp-api-test plug-in
*
* 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.
*/
#include <vat/vat.h>
#include <vlibapi/api.h>
#include <vlibmemory/api.h>
#include <vlibsocket/api.h>
#include <vppinfra/error.h>
#include <vnet/ip/ip.h>
uword unformat_sw_if_index (unformat_input_t * input, va_list * args);
/* Declare message IDs */
#include <snat/snat_msg_enum.h>
/* define message structures */
#define vl_typedefs
#include <snat/snat_all_api_h.h>
#undef vl_typedefs
/* declare message handlers for each api */
#define vl_endianfun /* define message structures */
#include <snat/snat_all_api_h.h>
#undef vl_endianfun
/* instantiate all the print functions we know about */
#define vl_print(handle, ...)
#define vl_printfun
#include <snat/snat_all_api_h.h>
#undef vl_printfun
/* Get the API version number. */
#define vl_api_version(n,v) static u32 api_version=(v);
#include <snat/snat_all_api_h.h>
#undef vl_api_version
typedef struct {
/* API message ID base */
u16 msg_id_base;
vat_main_t *vat_main;
} snat_test_main_t;
snat_test_main_t snat_test_main;
#define foreach_standard_reply_retval_handler \
_(snat_add_address_range_reply) \
_(snat_interface_add_del_feature_reply)
#define _(n) \
static void vl_api_##n##_t_handler \
(vl_api_##n##_t * mp) \
{ \
vat_main_t * vam = snat_test_main.vat_main; \
i32 retval = ntohl(mp->retval); \
if (vam->async_mode) { \
vam->async_errors += (retval < 0); \
} else { \
vam->retval = retval; \
vam->result_ready = 1; \
} \
}
foreach_standard_reply_retval_handler;
#undef _
/*
* Table of message reply handlers, must include boilerplate handlers
* we just generated
*/
#define foreach_vpe_api_reply_msg \
_(SNAT_ADD_ADDRESS_RANGE_REPLY, snat_add_address_range_reply) \
_(SNAT_INTERFACE_ADD_DEL_FEATURE_REPLY, \
snat_interface_add_del_feature_reply)
/* M: construct, but don't yet send a message */
#define M(T,t) \
do { \
vam->result_ready = 0; \
mp = vl_msg_api_alloc(sizeof(*mp)); \
memset (mp, 0, sizeof (*mp)); \
mp->_vl_msg_id = ntohs (VL_API_##T + sm->msg_id_base); \
mp->client_index = vam->my_client_index; \
} while(0);
#define M2(T,t,n) \
do { \
vam->result_ready = 0; \
mp = vl_msg_api_alloc(sizeof(*mp)+(n)); \
memset (mp, 0, sizeof (*mp)); \
mp->_vl_msg_id = ntohs (VL_API_##T + sm->msg_id_base); \
mp->client_index = vam->my_client_index; \
} while(0);
/* S: send a message */
#define S (vl_msg_api_send_shmem (vam->vl_input_queue, (u8 *)&mp))
/* W: wait for results, with timeout */
#define W \
do { \
timeout = vat_time_now (vam) + 1.0; \
\
while (vat_time_now (vam) < timeout) { \
if (vam->result_ready == 1) { \
return (vam->retval); \
} \
} \
return -99; \
} while(0);
static int api_snat_add_address_range (vat_main_t * vam)
{
snat_test_main_t * sm = &snat_test_main;
unformat_input_t * i = vam->input;
f64 timeout;
ip4_address_t start_addr, end_addr;
u32 start_host_order, end_host_order;
vl_api_snat_add_address_range_t * mp;
int count;
if (unformat (i, "%U - %U",
unformat_ip4_address, &start_addr,
unformat_ip4_address, &end_addr))
;
else if (unformat (i, "%U", unformat_ip4_address, &start_addr))
end_addr = start_addr;
start_host_order = clib_host_to_net_u32 (start_addr.as_u32);
end_host_order = clib_host_to_net_u32 (end_addr.as_u32);
if (end_host_order < start_host_order)
{
errmsg ("end address less than start address\n");
return -99;
}
count = (end_host_order - start_host_order) + 1;
if (count > 1024)
{
errmsg ("%U - %U, %d addresses...\n",
format_ip4_address, &start_addr,
format_ip4_address, &end_addr,
count);
}
M(SNAT_ADD_ADDRESS_RANGE, snat_add_address_range);
memcpy (mp->first_ip_address, &start_addr, 4);
memcpy (mp->last_ip_address, &end_addr, 4);
mp->is_ip4 = 1;
S; W;
/* NOTREACHED */
return 0;
}
static int api_snat_interface_add_del_feature (vat_main_t * vam)
{
snat_test_main_t * sm = &snat_test_main;
unformat_input_t * i = vam->input;
f64 timeout;
vl_api_snat_interface_add_del_feature_t * mp;
u32 sw_if_index;
u8 sw_if_index_set = 0;
u8 is_inside = 1;
u8 is_add = 1;
while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
{
if (unformat (i, "%U", unformat_sw_if_index, vam, &sw_if_index))
sw_if_index_set = 1;
else if (unformat (i, "sw_if_index %d", &sw_if_index))
sw_if_index_set = 1;
else if (unformat (i, "out"))
is_inside = 0;
else if (unformat (i, "in"))
is_inside = 1;
else if (unformat (i, "del"))
is_add = 0;
}
if (sw_if_index_set == 0)
{
errmsg ("interface / sw_if_index required\n");
return -99;
}
M(SNAT_INTERFACE_ADD_DEL_FEATURE, snat_interface_add_del_feature);
mp->sw_if_index = ntohl(sw_if_index);
mp->is_add = is_add;
mp->is_inside = is_inside;
S; W;
/* NOTREACHED */
return 0;
}
/*
* List of messages that the api test plugin sends,
* and that the data plane plugin processes
*/
#define foreach_vpe_api_msg \
_(snat_add_address_range, "<start-addr> [- <end-addr]") \
_(snat_interface_add_del_feature, \
"<intfc> | sw_if_index <id> [in] [out] [del]")
void vat_api_hookup (vat_main_t *vam)
{
snat_test_main_t * sm __attribute__((unused)) = &snat_test_main;
/* Hook up handlers for replies from the data plane plug-in */
#define _(N,n) \
vl_msg_api_set_handlers((VL_API_##N + sm->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_vpe_api_reply_msg;
#undef _
/* API messages we can send */
#define _(n,h) hash_set_mem (vam->function_by_name, #n, api_##n);
foreach_vpe_api_msg;
#undef _
/* Help strings */
#define _(n,h) hash_set_mem (vam->help_by_name, #n, h);
foreach_vpe_api_msg;
#undef _
}
clib_error_t * vat_plugin_register (vat_main_t *vam)
{
snat_test_main_t * sm = &snat_test_main;
u8 * name;
sm->vat_main = vam;
/* Ask the vpp engine for the first assigned message-id */
name = format (0, "snat_%08x%c", api_version, 0);
sm->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name);
if (sm->msg_id_base != (u16) ~0)
vat_api_hookup (vam);
vec_free(name);
return 0;
}

View File

@ -71,6 +71,12 @@ BUILT_SOURCES += vpp_plugin_configure
.PHONY: vpp_plugin_configure
if WITH_DPDK
PLUGIN_DPDK_ARG="--with-dpdk"
else
PLUGIN_DPDK_ARG=""
endif
vpp_plugin_configure:
@echo "PLUGIN CONFIGURE " $@
@echo "#!/bin/bash" > $@
@ -78,10 +84,10 @@ vpp_plugin_configure:
@echo "set +eu" >> $@
@echo " " >> $@
@echo "if [ -f ./configure ] ; then" >> $@
@echo " CFLAGS='$(CFLAGS) $(AM_CFLAGS) -I/usr/include/vpp-dpdk' ./configure --with-plugin-toolkit" >> $@
@echo " CFLAGS='$(CFLAGS) $(AM_CFLAGS) -I/usr/include/vpp-dpdk' ./configure --with-plugin-toolkit $(PLUGIN_DPDK_ARG)" >> $@
@echo "else" >> $@
@echo " if [ -f ../configure ] ; then" >> $@
@echo " CFLAGS='$(CFLAGS) $(AM_CFLAGS) -I/usr/include/vpp-dpdk' ../configure --with-plugin-toolkit" >> $@
@echo " CFLAGS='$(CFLAGS) $(AM_CFLAGS) -I/usr/include/vpp-dpdk' ../configure --with-plugin-toolkit $(PLUGIN_DPDK_ARG)" >> $@
@echo " else" >> $@
@echo " echo Couldnt find ./configure or ../configure " >> $@
@echo " exit 1" >> $@