From cb034b9b374927c7552e36dcbc306d8456b2a0cb Mon Sep 17 00:00:00 2001 From: Damjan Marion Date: Wed, 28 Dec 2016 18:38:59 +0100 Subject: Move java,lua api and remaining plugins to src/ Change-Id: I1c3b87e886603678368428ae56a6bd3327cbc90d Signed-off-by: Damjan Marion --- src/vpp-api/java/Makefile.am | 199 ++++++++++++ src/vpp-api/java/Readme.txt | 236 ++++++++++++++ .../fd/vpp/jvpp/acl/test/AclExpectedDumpData.java | 135 ++++++++ .../io/fd/vpp/jvpp/acl/test/AclTestData.java | 101 ++++++ .../io/fd/vpp/jvpp/acl/test/AclTestRequests.java | 141 +++++++++ .../io/fd/vpp/jvpp/acl/test/FutureApiTest.java | 68 ++++ .../jvpp-acl/io/fd/vpp/jvpp/acl/test/Readme.txt | 1 + src/vpp-api/java/jvpp-acl/jvpp_acl.c | 124 ++++++++ src/vpp-api/java/jvpp-acl/jvpp_acl.h | 45 +++ src/vpp-api/java/jvpp-common/jvpp_common.c | 65 ++++ src/vpp-api/java/jvpp-common/jvpp_common.h | 67 ++++ .../io/fd/vpp/jvpp/core/test/CallbackApiTest.java | 96 ++++++ .../test/CallbackJVppFacadeNotificationTest.java | 87 +++++ .../vpp/jvpp/core/test/CallbackJVppFacadeTest.java | 103 ++++++ .../core/test/CallbackNotificationApiTest.java | 94 ++++++ .../io/fd/vpp/jvpp/core/test/ControlPingTest.java | 68 ++++ .../vpp/jvpp/core/test/CreateSubInterfaceTest.java | 120 +++++++ .../jvpp/core/test/FutureApiNotificationTest.java | 55 ++++ .../io/fd/vpp/jvpp/core/test/FutureApiTest.java | 123 +++++++ .../io/fd/vpp/jvpp/core/test/L2AclTest.java | 195 ++++++++++++ .../fd/vpp/jvpp/core/test/LispAdjacencyTest.java | 124 ++++++++ .../fd/vpp/jvpp/core/test/NotificationUtils.java | 53 ++++ .../jvpp-core/io/fd/vpp/jvpp/core/test/Readme.txt | 17 + src/vpp-api/java/jvpp-core/jvpp_core.c | 117 +++++++ .../jvpp/ioamexport/test/IoamExportApiTest.java | 56 ++++ .../io/fd/vpp/jvpp/ioamexport/test/Readme.txt | 1 + .../java/jvpp-ioamexport/jvpp_ioam_export.c | 124 ++++++++ .../java/jvpp-ioamexport/jvpp_ioam_export.h | 45 +++ .../fd/vpp/jvpp/ioampot/test/IoamPotApiTest.java | 75 +++++ .../io/fd/vpp/jvpp/ioampot/test/Readme.txt | 1 + src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.c | 124 ++++++++ src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.h | 45 +++ .../vpp/jvpp/ioamtrace/test/IoamTraceApiTest.java | 77 +++++ .../io/fd/vpp/jvpp/ioamtrace/test/Readme.txt | 1 + src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.c | 124 ++++++++ src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.h | 45 +++ .../java/jvpp-registry/io/fd/vpp/jvpp/JVpp.java | 56 ++++ .../jvpp-registry/io/fd/vpp/jvpp/JVppRegistry.java | 76 +++++ .../io/fd/vpp/jvpp/JVppRegistryImpl.java | 147 +++++++++ .../io/fd/vpp/jvpp/NativeLibraryLoader.java | 73 +++++ .../io/fd/vpp/jvpp/VppBaseCallException.java | 60 ++++ .../io/fd/vpp/jvpp/VppCallbackException.java | 47 +++ .../io/fd/vpp/jvpp/VppConnection.java | 45 +++ .../io/fd/vpp/jvpp/VppInvocationException.java | 33 ++ .../io/fd/vpp/jvpp/VppJNIConnection.java | 137 ++++++++ .../fd/vpp/jvpp/callback/ControlPingCallback.java | 29 ++ .../io/fd/vpp/jvpp/callback/JVppCallback.java | 29 ++ .../jvpp/callback/JVppNotificationCallback.java | 24 ++ .../io/fd/vpp/jvpp/dto/ControlPing.java | 34 ++ .../io/fd/vpp/jvpp/dto/ControlPingReply.java | 58 ++++ .../jvpp-registry/io/fd/vpp/jvpp/dto/JVppDump.java | 24 ++ .../io/fd/vpp/jvpp/dto/JVppNotification.java | 23 ++ .../io/fd/vpp/jvpp/dto/JVppReply.java | 24 ++ .../io/fd/vpp/jvpp/dto/JVppReplyDump.java | 25 ++ .../io/fd/vpp/jvpp/dto/JVppRequest.java | 34 ++ .../vpp/jvpp/future/AbstractFutureJVppInvoker.java | 141 +++++++++ .../io/fd/vpp/jvpp/future/FutureJVppInvoker.java | 49 +++ .../jvpp/notification/NotificationRegistry.java | 25 ++ .../notification/NotificationRegistryProvider.java | 28 ++ .../io/fd/vpp/jvpp/test/ConnectionTest.java | 44 +++ src/vpp-api/java/jvpp-registry/jvpp_registry.c | 352 +++++++++++++++++++++ .../io/fd/vpp/jvpp/snat/test/CallbackApiTest.java | 68 ++++ .../jvpp-snat/io/fd/vpp/jvpp/snat/test/Readme.txt | 1 + src/vpp-api/java/jvpp-snat/jvpp_snat.c | 124 ++++++++ src/vpp-api/java/jvpp-snat/jvpp_snat.h | 45 +++ src/vpp-api/java/jvpp/gen/jvpp_gen.py | 185 +++++++++++ src/vpp-api/java/jvpp/gen/jvppgen/__init__.py | 0 src/vpp-api/java/jvpp/gen/jvppgen/callback_gen.py | 105 ++++++ src/vpp-api/java/jvpp/gen/jvppgen/dto_gen.py | 308 ++++++++++++++++++ src/vpp-api/java/jvpp/gen/jvppgen/jni_gen.py | 295 +++++++++++++++++ src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py | 343 ++++++++++++++++++++ .../jvpp/gen/jvppgen/jvpp_callback_facade_gen.py | 324 +++++++++++++++++++ .../jvpp/gen/jvppgen/jvpp_future_facade_gen.py | 331 +++++++++++++++++++ src/vpp-api/java/jvpp/gen/jvppgen/jvpp_impl_gen.py | 219 +++++++++++++ .../java/jvpp/gen/jvppgen/notification_gen.py | 199 ++++++++++++ src/vpp-api/java/jvpp/gen/jvppgen/types_gen.py | 227 +++++++++++++ src/vpp-api/java/jvpp/gen/jvppgen/util.py | 220 +++++++++++++ 77 files changed, 7763 insertions(+) create mode 100644 src/vpp-api/java/Makefile.am create mode 100644 src/vpp-api/java/Readme.txt create mode 100644 src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/AclExpectedDumpData.java create mode 100644 src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/AclTestData.java create mode 100644 src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/AclTestRequests.java create mode 100644 src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/FutureApiTest.java create mode 100644 src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/Readme.txt create mode 100644 src/vpp-api/java/jvpp-acl/jvpp_acl.c create mode 100644 src/vpp-api/java/jvpp-acl/jvpp_acl.h create mode 100644 src/vpp-api/java/jvpp-common/jvpp_common.c create mode 100644 src/vpp-api/java/jvpp-common/jvpp_common.h create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackApiTest.java create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackJVppFacadeNotificationTest.java create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackJVppFacadeTest.java create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackNotificationApiTest.java create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/ControlPingTest.java create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CreateSubInterfaceTest.java create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiNotificationTest.java create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/L2AclTest.java create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/LispAdjacencyTest.java create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/NotificationUtils.java create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/Readme.txt create mode 100644 src/vpp-api/java/jvpp-core/jvpp_core.c create mode 100644 src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/IoamExportApiTest.java create mode 100644 src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/Readme.txt create mode 100644 src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.c create mode 100644 src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.h create mode 100644 src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/IoamPotApiTest.java create mode 100644 src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/Readme.txt create mode 100644 src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.c create mode 100644 src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.h create mode 100644 src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/IoamTraceApiTest.java create mode 100644 src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/Readme.txt create mode 100644 src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.c create mode 100644 src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.h create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/JVpp.java create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/JVppRegistry.java create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/JVppRegistryImpl.java create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/NativeLibraryLoader.java create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppBaseCallException.java create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppCallbackException.java create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppConnection.java create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppInvocationException.java create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppJNIConnection.java create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/callback/ControlPingCallback.java create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/callback/JVppCallback.java create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/callback/JVppNotificationCallback.java create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/ControlPing.java create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/ControlPingReply.java create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppDump.java create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppNotification.java create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppReply.java create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppReplyDump.java create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppRequest.java create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/future/AbstractFutureJVppInvoker.java create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/future/FutureJVppInvoker.java create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/notification/NotificationRegistry.java create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/notification/NotificationRegistryProvider.java create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/test/ConnectionTest.java create mode 100644 src/vpp-api/java/jvpp-registry/jvpp_registry.c create mode 100644 src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/test/CallbackApiTest.java create mode 100644 src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/test/Readme.txt create mode 100644 src/vpp-api/java/jvpp-snat/jvpp_snat.c create mode 100644 src/vpp-api/java/jvpp-snat/jvpp_snat.h create mode 100755 src/vpp-api/java/jvpp/gen/jvpp_gen.py create mode 100644 src/vpp-api/java/jvpp/gen/jvppgen/__init__.py create mode 100644 src/vpp-api/java/jvpp/gen/jvppgen/callback_gen.py create mode 100644 src/vpp-api/java/jvpp/gen/jvppgen/dto_gen.py create mode 100644 src/vpp-api/java/jvpp/gen/jvppgen/jni_gen.py create mode 100644 src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py create mode 100644 src/vpp-api/java/jvpp/gen/jvppgen/jvpp_callback_facade_gen.py create mode 100644 src/vpp-api/java/jvpp/gen/jvppgen/jvpp_future_facade_gen.py create mode 100644 src/vpp-api/java/jvpp/gen/jvppgen/jvpp_impl_gen.py create mode 100644 src/vpp-api/java/jvpp/gen/jvppgen/notification_gen.py create mode 100644 src/vpp-api/java/jvpp/gen/jvppgen/types_gen.py create mode 100644 src/vpp-api/java/jvpp/gen/jvppgen/util.py (limited to 'src/vpp-api/java') diff --git a/src/vpp-api/java/Makefile.am b/src/vpp-api/java/Makefile.am new file mode 100644 index 00000000..c7051ec1 --- /dev/null +++ b/src/vpp-api/java/Makefile.am @@ -0,0 +1,199 @@ +# 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. + +AUTOMAKE_OPTIONS = foreign +ACLOCAL_AMFLAGS = -I m4 + +AM_CFLAGS = -Wall -I${top_srcdir} -I${top_builddir} \ + -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux \ + -I@top_srcdir@/plugins -I@top_builddir@/plugins + +AM_LDFLAGS = -module -shared -avoid-version -rpath /none -no-undefined + +BUILT_SOURCES = +bin_PROGRAMS = +noinst_LTLIBRARIES = +JAR_FILES = +CLEANDIRS = + +# +# jvpp-common +# + +noinst_LTLIBRARIES += libjvpp_common.la +libjvpp_common_la_SOURCES = jvpp-common/jvpp_common.c +libjvpp_common_la_LDFLAGS = + +# +# jvpp-registry (connection management + plugin registry) +# + +noinst_LTLIBRARIES += libjvpp_registry.la + +libjvpp_registry_la_SOURCES = jvpp-registry/jvpp_registry.c +libjvpp_registry_la_CPPFLAGS = -Ijvpp-registry +libjvpp_registry_la_LIBAD = libjvpp_common.la + +packagedir_jvpp_registry = io/fd/vpp/jvpp +jvpp_registry_src_files := \ + $(wildcard @srcdir@/jvpp-registry/$(packagedir_jvpp_registry)/*.java) \ + $(wildcard @srcdir@/jvpp-registry/$(packagedir_jvpp_registry)/**/*.java) + +BUILT_SOURCES += jvpp-registry/io_fd_vpp_jvpp_VppJNIConnection.h +CLEANDIRS += jvpp-registry/target +JAR_FILES += jvpp-registry-$(PACKAGE_VERSION).jar + +jvpp_registry_ok = jvpp-registry/io_fd_vpp_jvpp_VppJNIConnection.h + +jvpp-registry/io_fd_vpp_jvpp_VppJNIConnection.h: $(jvpp_registry_src_files) + @echo " JAPIGEN $@" + @rm -rf jvpp-registry/target + @mkdir -p jvpp-registry/target + @$(JAVAC) -d jvpp-registry/target $^ + @$(JAVAH) -force -classpath jvpp-registry/target -d jvpp-registry io.fd.vpp.jvpp.VppJNIConnection + @$(JAVAH) -force -classpath jvpp-registry/target -d jvpp-registry io.fd.vpp.jvpp.JVppRegistryImpl + @touch jvpp-registry.ok + +define japigen + @echo " JAPIGEN $@" + @rm -rf jvpp-$(1)/target + @ @srcdir@/jvpp/gen/jvpp_gen.py --plugin_name $(1) --root_dir jvpp-$(1) \ + -i $(jvpp_$(1)_json_files) > /dev/null + @find jvpp-$(1)/target -name \*.java > jvpp-$(1).files + @$(JAVAC) -classpath jvpp-registry/target \ + -d jvpp-$(1)/target @jvpp-$(1).files + @$(JAVAH) -force \ + -classpath jvpp-registry/target:jvpp-$(1)/target \ + -d jvpp-$(1) io.fd.vpp.jvpp.$(1).$(2) +endef + +# +# jvpp-core (Java wrapper for vpe.api) +# +noinst_LTLIBRARIES += libjvpp_core.la +libjvpp_core_la_SOURCES = jvpp-core/jvpp_core.c jvpp-core/jvpp_core_gen.h +libjvpp_core_la_CPPFLAGS = -Ijvpp-registry -Ijvpp-core +BUILT_SOURCES += jvpp-core/io_fd_vpp_jvpp_core_JVppCoreImpl.h + +JAR_FILES += jvpp-core-$(PACKAGE_VERSION).jar +CLEANDIRS += jvpp-core/target +jvpp_core_json_files = $(shell find @top_builddir@/vnet/ -type f -name '*.api.json') + +jvpp-core/io_fd_vpp_jvpp_core_JVppCoreImpl.h: $(jvpp_registry_ok) $(jvpp_core_json_files) + $(call japigen,core,JVppCoreImpl) + +# +# ACL Plugin +# +if ENABLE_ACL_PLUGIN +noinst_LTLIBRARIES += libjvpp_acl.la +libjvpp_acl_la_SOURCES = jvpp-acl/jvpp_acl.c +libjvpp_acl_la_CPPFLAGS = -Ijvpp-acl + +BUILT_SOURCES += jvpp-acl/io_fd_vpp_jvpp_acl_JVppAclImpl.h + +JAR_FILES += jvpp-acl-$(PACKAGE_VERSION).jar +CLEANDIRS += jvpp-acl/target + +jvpp_acl_json_files = @top_builddir@/plugins/acl/acl.api.json + +jvpp-acl/io_fd_vpp_jvpp_acl_JVppAclImpl.h: $(jvpp_registry_ok) $(jvpp_acl_json_files) + $(call japigen,acl,JVppAclImpl) +endif + +# +# SNAT Plugin +# +if ENABLE_SNAT_PLUGIN +noinst_LTLIBRARIES += libjvpp_snat.la +libjvpp_snat_la_SOURCES = jvpp-snat/jvpp_snat.c +libjvpp_snat_la_CPPFLAGS = -Ijvpp-snat + +BUILT_SOURCES += jvpp-snat/io_fd_vpp_jvpp_snat_JVppSnatImpl.h + +JAR_FILES += jvpp-snat-$(PACKAGE_VERSION).jar +CLEANDIRS += jvpp-snat/target + +jvpp_snat_json_files = @top_builddir@/plugins/snat/snat.api.json + +jvpp-snat/io_fd_vpp_jvpp_snat_JVppSnatImpl.h: $(jvpp_registry_ok) $(jvpp_snat_json_files) + $(call japigen,snat,JVppSnatImpl) +endif + +# +# iOAM Trace Plugin +# +if ENABLE_IOAM_PLUGIN +noinst_LTLIBRARIES += libjvpp_ioamtrace.la +libjvpp_ioamtrace_la_SOURCES = jvpp-ioamtrace/jvpp_ioam_trace.c + +BUILT_SOURCES += jvpp-ioamtrace/io_fd_vpp_jvpp_ioamtrace_JVppIoamtraceImpl.h +JAR_FILES += jvpp-ioamtrace-$(PACKAGE_VERSION).jar +CLEANDIRS += jvpp-ioamtrace/target + +jvpp_ioamtrace_json_files = @top_builddir@/plugins/ioam/lib-trace/trace.api.json + +jvpp-ioamtrace/io_fd_vpp_jvpp_ioamtrace_JVppIoamtraceImpl.h: $(jvpp_registry_ok) $(jvpp_ioamtrace_json_files) + $(call japigen,ioamtrace,JVppIoamtraceImpl) + +# +# iOAM POT Plugin +# +noinst_LTLIBRARIES += libjvpp_ioampot.la +libjvpp_ioampot_la_SOURCES = jvpp-ioampot/jvpp_ioam_pot.c + +BUILT_SOURCES += jvpp-ioampot/io_fd_vpp_jvpp_ioampot_JVppIoampotImpl.h +JAR_FILES += jvpp-ioampot-$(PACKAGE_VERSION).jar +CLEANDIRS += jvpp-ioampot/target + +jvpp_ioampot_json_files = @top_builddir@/plugins/ioam/lib-pot/pot.api.json + +jvpp-ioampot/io_fd_vpp_jvpp_ioampot_JVppIoampotImpl.h: $(jvpp_registry_ok) $(jvpp_ioampot_json_files) + $(call japigen,ioampot,JVppIoampotImpl) + +# +# iOAM Export Plugin +# +noinst_LTLIBRARIES += libjvpp_ioamexport.la +libjvpp_ioamexport_la_SOURCES = jvpp-ioamexport/jvpp_ioam_export.c + +BUILT_SOURCES += jvpp-ioamexport/io_fd_vpp_jvpp_ioamexport_JVppIoamexportImpl.h +JAR_FILES += jvpp-ioamexport-$(PACKAGE_VERSION).jar +CLEANDIRS += jvpp-ioamexport/target + +jvpp_ioamexport_json_files = @top_builddir@/plugins/ioam/export/ioam_export.api.json + +jvpp-ioamexport/io_fd_vpp_jvpp_ioamexport_JVppIoamexportImpl.h: $(jvpp_registry_ok) $(jvpp_ioamexport_json_files) + $(call japigen,ioamexport,JVppIoamexportImpl) +endif + +# +# JAR creation +# +jvpp-%-$(PACKAGE_VERSION).jar: libjvpp_%.la + @echo " JAR $@" + @cp .libs/libjvpp_$*.so jvpp-$*/target + @$(JAR) cf $(JARFLAGS) $@ -C jvpp-$*/target . + +jardir = $(prefix)/share/java +jar_DATA = $(JAR_FILES) + +all-local: $(JAR_FILES) + +# +# Cleanup +# +CLEANFILES = jvpp-registry.ok $(JAR_FILES) $(BUILT_SOURCES) + +clean-local: + rm -rf $(CLEANDIRS) diff --git a/src/vpp-api/java/Readme.txt b/src/vpp-api/java/Readme.txt new file mode 100644 index 00000000..689b9b37 --- /dev/null +++ b/src/vpp-api/java/Readme.txt @@ -0,0 +1,236 @@ += JVpp + +JVpp is JNI based Java API for VPP. + +== Features +It is: + +* Asynchronous +* Fully generated +* Lightweight + +== Architecture + +=== Plugin support + + /-------------\ /--------------\ /---------------\ + | JvppPlugin1 +<-------+ JVppRegistry +--------->+ VppConnection | + \-------------/ inits \--+-----------/ uses \---------------/ + | + /-------------\ | + | JvppPlugin2 +<----------+ inits + \-------------/ | + | + ... | + | + /----------\ | + | JVppCore +<-------------+ + \----------/ + + +VppRegistry opens connection to vpp (VppConnection) and manages jvpp plugins. +Each plugin needs to be registered in the VppRegistry. Registration involves +plugin initialization (providing JNI implementation with JVppCallback reference, +vpp client identifier and vpp shared memory queue address). + +API user sends message by calling a method of appropriate plugin interface. +The call is delegated to JNI implementation provided by the particular plugin. +When JNI code receives reply, it invokes callback method of JVppCallback +that corresponds to the received message reply. + +=== JVppCore as an example of JVpp plugin architecture + + JVpp Java + + /--------------\ /----------\ /------------\ /------\ + | JVppRegistry | | JVppCore | | Callbacks | | DTOs | + \----+---------/ \----+-----/ \------+-----/ \------/ + ^ ^ ^ + | implements | implements | implements + /----+--------------\ /---+----------\ /-----+---------\ + | JVppRegistryImpl* +-------->+ JVppCoreImpl | | JVppCallback | + \-------+-----------/ inits \---+----------/ \-------+-------/ + | | ^ + | | uses | calls back + | | | +----------|--------------------------|-----------------------|--------------------- + | | | + C JNI | +-------------------+ | /-----------------\ + v | | +-->+ jvpp_core_gen.h | + /--------+--------\ | | | \-----------------/ + | jpp_registry.c* +---+ /--------+----+----\ | | | + \-----------------/ | | << shared lib >> | /-+--+---+------\ + + ->+ jvpp_common* <--------+ jvpp_core.c* | + uses \------------------/ uses \---------------/ + + +* Components marked with an asterisk contain manually crafted code, which in addition +to generated classes form jvpp. Exception applies to Callbacks and DTOs, since there are +manually crafted marker interfaces in callback and dto package (dto/JVppRequest, dto/JVppReply, +dto/JVppDump, dto/JVppReplyDump, callback/JVppCallback) + +Note: jvpp_core.c calls back the JVppCallback instance with every response. An instance of the +JVppCallback is provided to jvpp_core.c by JVppRegistryImpl on JVppCoreImpl initialization. + +Part of the JVpp is also Future facade. It is asynchronous API returning Future objects +on top of low level JVpp. It wraps dump reply messages in one DTO using control_ping message +(provided by JVppRegistry). + + +Future facade + + /----------------\ /---------------\ + | FutureJVppCore | +-->+ JVppRegistry* | + \-----+----------/ | \---------------/ + ^ | + | implements | uses + | | + /--------+-------------\ | /------------------------------\ + | FutureJVppCoreFacade +---+--->+ FutureJVppCoreFacadeCallback | + \---------+------------/ uses \-------+----------------------/ + | | +---------------|-----------------------------|------------------------------- + | uses | implements +JVpp Java | | + | | + /----------\ | | + | JVppCore +<-+ | + \----+-----/ | + ^ | + | implements v + /----+---------\ /--------+---------------\ + | JVppCoreImpl | | JVppCoreGlobalCallback | + \--------------/ \------------------------/ + + + +Another useful utility of the JVpp is Callback facade. It is asynchronous API +capable of calling specific callback instance (provided when performing a call) +per call. + + +Callback facade + + /------------------\ /---------------\ + | CallbackJVppCore | +-->+ JVppRegistry* | + \-----+------------/ | \---------------/ + ^ | + | implements | uses + | | + /--------+---------------\ | /--------------------------\ + | CallbackJVppCoreFacade +---+--->+ CallbackJVppCoreCallback | + \---------+--------------/ uses \-----+--------------------/ + | | +---------------|-----------------------------|------------------------------- + | uses | implements +JVpp Java | | + | | + /----------\ | | + | JVppCore +<-+ | + \----+-----/ | + ^ | + | implements v + /----+---------\ /----------+-------------\ + | JVppCoreImpl | | JVppCoreGlobalCallback | + \--------------/ \------------------------/ + + +== Package structure + +* *io.fd.vpp.jvpp* - top level package for generated JVpp interface+ implementation and hand-crafted +VppConnection interface + implementation - packaged as jvpp-registry-version.jar + +* *io.fd.vpp.jvpp.[plugin]* - top level package for generated JVpp interface + implementation ++ plugin's API tests - packaged as jvpp-[plugin]-version.jar + +** *dto* - package for DTOs generated from VPP API structures + base/marker hand-crafted interfaces +(in case of jvpp-registry) +** *callback* - package for low-level JVpp callbacks and a global callback interface implementing each of +the low-level JVppcallbacks +** *future* - package for future based facade on top of JVpp and callbacks +** *callfacade* - package for callback based facade on top of JVpp and callbacks. Allowing +users to provide callback per request +** *test* - package for JVpp standalone tests. Can also serve as samples for JVpp. + +C code is structured into modules: + +* *jvpp_common* - shared library that provides jvpp_main_t reference used by jvpp_registry and plugins. + +* *jvpp_registry* - native library used by JVppRegistryImpl, responsible for: + +** VPP connection open/close +** Rx thread to java thread attach +** control ping message handling + +* *jvpp_core* - native library used by jvpp core plugin: +** *jvpp_core.c* - contains hand crafted code for core plugin initialization +** *jvpp_core_gen.h* - contains generated JNI compatible handlers for all requests and replies defined in vpe.api + +== Code generators +All of the required code except the base/marker interfaces is generated using +simple python2 code generators. The generators use __defs_vpp_papi.py__ input +file produced by __vppapigen__ from vpe.api file. + +=== JNI compatible C code +Produces __jvpp_[plugin]_gen.h__ file containing JNI compatible handlers for each VPP +request and reply. + +[NOTE] +==== +Source: jvpp_c_gen.py +==== + +=== Request/Reply DTOs +For all the structures in __defs_vpp_papi.py__ a POJO DTO is produced. Logically, +there are 4 types of DTOs: + +* Request - requests that can be sent to VPP and only a single response is expected +* DumpRequest - requests that can be sent to VPP and a stream of responses is expected +* Reply - reply to a simple request or a single response from dump triggered response stream +* ReplyDump - collection of replies from a single dump request +* Notifications/Events - Not implemented yet + +[NOTE] +==== +Source: dto_gen.py +==== + +=== JVpp +Produces __JVpp.java__ and __JVppImpl.java__. This is the layer right above JNI compatible C +code. + +[NOTE] +==== +Source: jvpp_impl_gen.py +==== + +=== Callbacks +Produces callback interface for each VPP reply + a global callback interface called +__JVpp[plugin]GlobalCallback.java__ aggregating all of the callback interfaces. The JNI +compatible C code expects only a single instance of this global callback and calls +it with every reply. + +[NOTE] +==== +Source: callback_gen.py +==== + +=== Future facade +Produces an asynchronous facade on top of JVpp and callbacks, which returns a Future that provides +matching reply once VPP invocation finishes. Sources produced: +__FutureJVpp[plugin].java, FutureJVpp[plugin]Facade.java and FutureJVpp[plugin]Callback.java__ + +[NOTE] +==== +Source: jvpp_future_facade_gen.py +==== + +=== Callback facade +Similar to future facade, only this facade takes callback objects as part of the invocation +and the callback is called with result once VPP invocation finishes. Sources produced: +__CallbackJVpp[plugin].java, CallbackJVpp[plugin]Facade.java and CallbackJVpp[plugin]Callback.java__ + +[NOTE] +==== +Source: jvpp_callback_facade_gen.py +==== diff --git a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/AclExpectedDumpData.java b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/AclExpectedDumpData.java new file mode 100644 index 00000000..979edbc4 --- /dev/null +++ b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/AclExpectedDumpData.java @@ -0,0 +1,135 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.acl.test; + + +import static io.fd.vpp.jvpp.acl.test.AclTestData.FIRST_RULE_ADDRESS_2_AS_ARRAY; +import static io.fd.vpp.jvpp.acl.test.AclTestData.FIRST_RULE_ADDRESS_AS_ARRAY; +import static io.fd.vpp.jvpp.acl.test.AclTestData.FIRST_RULE_DST_ICMP_TYPE_END; +import static io.fd.vpp.jvpp.acl.test.AclTestData.FIRST_RULE_DST_ICMP_TYPE_START; +import static io.fd.vpp.jvpp.acl.test.AclTestData.FIRST_RULE_MAC; +import static io.fd.vpp.jvpp.acl.test.AclTestData.FIRST_RULE_MAC_MASK; +import static io.fd.vpp.jvpp.acl.test.AclTestData.FIRST_RULE_PREFIX; +import static io.fd.vpp.jvpp.acl.test.AclTestData.FIRST_RULE_PREFIX_2; +import static io.fd.vpp.jvpp.acl.test.AclTestData.FIRST_RULE_SRC_ICMP_TYPE_END; +import static io.fd.vpp.jvpp.acl.test.AclTestData.FIRST_RULE_SRC_ICMP_TYPE_START; +import static io.fd.vpp.jvpp.acl.test.AclTestData.ICMP_PROTOCOL; +import static io.fd.vpp.jvpp.acl.test.AclTestData.SECOND_RULE_ADDRESS_2_AS_ARRAY; +import static io.fd.vpp.jvpp.acl.test.AclTestData.SECOND_RULE_ADDRESS_AS_ARRAY; +import static io.fd.vpp.jvpp.acl.test.AclTestData.SECOND_RULE_DST_PORT_RANGE_END; +import static io.fd.vpp.jvpp.acl.test.AclTestData.SECOND_RULE_DST_PORT_RANGE_START; +import static io.fd.vpp.jvpp.acl.test.AclTestData.SECOND_RULE_MAC; +import static io.fd.vpp.jvpp.acl.test.AclTestData.SECOND_RULE_MAC_MASK; +import static io.fd.vpp.jvpp.acl.test.AclTestData.SECOND_RULE_PREFIX; +import static io.fd.vpp.jvpp.acl.test.AclTestData.SECOND_RULE_PREFIX_2; +import static io.fd.vpp.jvpp.acl.test.AclTestData.SECOND_RULE_SRC_PORT_RANGE_END; +import static io.fd.vpp.jvpp.acl.test.AclTestData.SECOND_RULE_SRC_PORT_RANGE_START; +import static io.fd.vpp.jvpp.acl.test.AclTestData.UDP_PROTOCOL; + +import io.fd.vpp.jvpp.acl.dto.AclDetails; +import io.fd.vpp.jvpp.acl.dto.AclInterfaceListDetails; +import io.fd.vpp.jvpp.acl.dto.MacipAclDetails; +import io.fd.vpp.jvpp.acl.types.AclRule; +import io.fd.vpp.jvpp.acl.types.MacipAclRule; +import java.util.Arrays; + +class AclExpectedDumpData { + + static void verifyMacIpDump(final MacipAclDetails macipAclDetails) { + // asserting data create by previous call + assertEquals(0, macipAclDetails.aclIndex); + assertEquals(2, macipAclDetails.count); + + final MacipAclRule currentIpv4Rule = macipAclDetails.r[0]; + final MacipAclRule currentIpv6Rule = macipAclDetails.r[1]; + + // Comparing one property at the time to better pointer if something is wrong + //Ipv4 rule + assertEquals(0, currentIpv4Rule.isIpv6); + assertEquals(1, currentIpv4Rule.isPermit); + + // cutting expected ipv4 to 4 bytes,vpp sends it as 16 always + assertArrays(FIRST_RULE_ADDRESS_AS_ARRAY, Arrays.copyOfRange(currentIpv4Rule.srcIpAddr, 0, 4)); + assertEquals(FIRST_RULE_PREFIX, currentIpv4Rule.srcIpPrefixLen); + assertArrays(FIRST_RULE_MAC, currentIpv4Rule.srcMac); + assertArrays(FIRST_RULE_MAC_MASK, currentIpv4Rule.srcMacMask); + + //Ipv6 rule + assertEquals(1, currentIpv6Rule.isIpv6); + assertEquals(0, currentIpv6Rule.isPermit); + assertArrays(SECOND_RULE_ADDRESS_AS_ARRAY, currentIpv6Rule.srcIpAddr); + assertEquals(SECOND_RULE_PREFIX, currentIpv6Rule.srcIpPrefixLen); + assertArrays(SECOND_RULE_MAC, currentIpv6Rule.srcMac); + assertArrays(SECOND_RULE_MAC_MASK, currentIpv6Rule.srcMacMask); + } + + static void verifyAclDump(final AclDetails aclDetails) { + assertEquals(0, aclDetails.aclIndex); + assertEquals(2, aclDetails.count); + + final AclRule currentIpv4Rule = aclDetails.r[0]; + final AclRule currentIpv6Rule = aclDetails.r[1]; + + // Comparing one property at the time to better pointer if something is wrong + //Ipv4 rule + assertEquals(0, currentIpv4Rule.isIpv6); + assertEquals(1, currentIpv4Rule.isPermit); + + // cutting expected ipv4 to 4 bytes,vpp sends it as 16 always + assertArrays(FIRST_RULE_ADDRESS_AS_ARRAY, Arrays.copyOfRange(currentIpv4Rule.srcIpAddr, 0, 4)); + assertEquals(FIRST_RULE_PREFIX, currentIpv4Rule.srcIpPrefixLen); + assertArrays(FIRST_RULE_ADDRESS_2_AS_ARRAY, Arrays.copyOfRange(currentIpv4Rule.dstIpAddr, 0, 4)); + assertEquals(FIRST_RULE_PREFIX_2, currentIpv4Rule.dstIpPrefixLen); + + assertEquals(ICMP_PROTOCOL, currentIpv4Rule.proto); + assertEquals(FIRST_RULE_SRC_ICMP_TYPE_START, currentIpv4Rule.srcportOrIcmptypeFirst); + assertEquals(FIRST_RULE_SRC_ICMP_TYPE_END, currentIpv4Rule.srcportOrIcmptypeLast); + assertEquals(FIRST_RULE_DST_ICMP_TYPE_START, currentIpv4Rule.dstportOrIcmpcodeFirst); + assertEquals(FIRST_RULE_DST_ICMP_TYPE_END, currentIpv4Rule.dstportOrIcmpcodeLast); + + assertArrays(SECOND_RULE_ADDRESS_AS_ARRAY, currentIpv6Rule.srcIpAddr); + assertEquals(SECOND_RULE_PREFIX, currentIpv6Rule.srcIpPrefixLen); + assertArrays(SECOND_RULE_ADDRESS_2_AS_ARRAY, currentIpv6Rule.dstIpAddr); + assertEquals(SECOND_RULE_PREFIX_2, currentIpv6Rule.dstIpPrefixLen); + + assertEquals(UDP_PROTOCOL, currentIpv6Rule.proto); + assertEquals(SECOND_RULE_SRC_PORT_RANGE_START, currentIpv6Rule.srcportOrIcmptypeFirst); + assertEquals(SECOND_RULE_SRC_PORT_RANGE_END, currentIpv6Rule.srcportOrIcmptypeLast); + assertEquals(SECOND_RULE_DST_PORT_RANGE_START, currentIpv6Rule.dstportOrIcmpcodeFirst); + assertEquals(SECOND_RULE_DST_PORT_RANGE_END, currentIpv6Rule.dstportOrIcmpcodeLast); + } + + static void verifyAclInterfaceList(final AclInterfaceListDetails aclInterfaceListDetails) { + assertEquals(1, aclInterfaceListDetails.count); + assertEquals(1, aclInterfaceListDetails.acls[0]); + assertEquals(0, aclInterfaceListDetails.nInput); + assertEquals(0, aclInterfaceListDetails.swIfIndex); + } + + private static void assertArrays(final byte[] expected, final byte[] actual) { + if (!Arrays.equals(expected, actual)) { + throw new IllegalArgumentException( + String.format("Expected[%s]/Actual[%s]", Arrays.toString(expected), Arrays.toString(actual))); + } + } + + private static void assertEquals(final int expected, final int actual) { + if (expected != actual) { + throw new IllegalArgumentException(String.format("Expected[%s]/Actual[%s]", expected, actual)); + } + } +} diff --git a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/AclTestData.java b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/AclTestData.java new file mode 100644 index 00000000..5d228eea --- /dev/null +++ b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/AclTestData.java @@ -0,0 +1,101 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.acl.test; + + +import io.fd.vpp.jvpp.acl.types.AclRule; +import io.fd.vpp.jvpp.acl.types.MacipAclRule; + +class AclTestData { + + static final byte[] FIRST_RULE_ADDRESS_AS_ARRAY = {-64, -88, 2, 1}; + static final byte[] FIRST_RULE_ADDRESS_2_AS_ARRAY = {-64, -88, 2, 3}; + static final byte[] SECOND_RULE_ADDRESS_AS_ARRAY = + {32, 1, 13, -72, 10, 11, 18, -16, 0, 0, 0, 0, 0, 0, 0, 1}; + static final byte[] SECOND_RULE_ADDRESS_2_AS_ARRAY = + {32, 1, 13, -72, 10, 11, 18, -16, 0, 0, 0, 0, 0, 0, 0, 1}; + static final byte[] FIRST_RULE_MAC = {11, 11, 11, 11, 11, 11}; + static final byte[] FIRST_RULE_MAC_MASK = {0, 0, 0, 0, 0, 0}; + static final byte[] SECOND_RULE_MAC = {11, 12, 11, 11, 12, 11}; + static final byte[] SECOND_RULE_MAC_MASK = {(byte) 170, 0, 0, 0, 0, 0}; + static final int FIRST_RULE_PREFIX = 32; + static final int FIRST_RULE_PREFIX_2 = 24; + static final int SECOND_RULE_PREFIX = 64; + static final int SECOND_RULE_PREFIX_2 = 62; + static final int FIRST_RULE_DST_ICMP_TYPE_START = 0; + static final int FIRST_RULE_DST_ICMP_TYPE_END = 8; + static final int FIRST_RULE_SRC_ICMP_TYPE_START = 1; + static final int FIRST_RULE_SRC_ICMP_TYPE_END = 7; + static final int ICMP_PROTOCOL = 1; + static final int SECOND_RULE_DST_PORT_RANGE_START = 2000; + static final int SECOND_RULE_DST_PORT_RANGE_END = 6000; + static final int SECOND_RULE_SRC_PORT_RANGE_START = 400; + static final int SECOND_RULE_SRC_PORT_RANGE_END = 2047; + static final int UDP_PROTOCOL = 17; + + + static MacipAclRule[] createMacipRules() { + MacipAclRule ruleOne = new MacipAclRule(); + ruleOne.isIpv6 = 0; + ruleOne.isPermit = 1; + ruleOne.srcIpAddr = FIRST_RULE_ADDRESS_AS_ARRAY; + ruleOne.srcIpPrefixLen = FIRST_RULE_PREFIX; + ruleOne.srcMac = FIRST_RULE_MAC; + ruleOne.srcMacMask = FIRST_RULE_MAC_MASK;// no mask + + MacipAclRule ruleTwo = new MacipAclRule(); + ruleTwo.isIpv6 = 1; + ruleTwo.isPermit = 0; + ruleTwo.srcIpAddr = SECOND_RULE_ADDRESS_AS_ARRAY; + ruleTwo.srcIpPrefixLen = SECOND_RULE_PREFIX; + ruleTwo.srcMac = SECOND_RULE_MAC; + ruleTwo.srcMacMask = SECOND_RULE_MAC_MASK; + + return new MacipAclRule[]{ruleOne, ruleTwo}; + } + + static AclRule[] createAclRules() { + AclRule ruleOne = new AclRule(); + + ruleOne.isIpv6 = 0; + ruleOne.isPermit = 1; + ruleOne.srcIpAddr = FIRST_RULE_ADDRESS_AS_ARRAY; + ruleOne.srcIpPrefixLen = FIRST_RULE_PREFIX; + ruleOne.dstIpAddr = FIRST_RULE_ADDRESS_2_AS_ARRAY; + ruleOne.dstIpPrefixLen = FIRST_RULE_PREFIX_2; + ruleOne.dstportOrIcmpcodeFirst = FIRST_RULE_DST_ICMP_TYPE_START; + ruleOne.dstportOrIcmpcodeLast = FIRST_RULE_DST_ICMP_TYPE_END; + ruleOne.srcportOrIcmptypeFirst = FIRST_RULE_SRC_ICMP_TYPE_START; + ruleOne.srcportOrIcmptypeLast = FIRST_RULE_SRC_ICMP_TYPE_END; + ruleOne.proto = ICMP_PROTOCOL; //ICMP + + AclRule ruleTwo = new AclRule(); + ruleTwo.isIpv6 = 1; + ruleTwo.isPermit = 0; + ruleTwo.srcIpAddr = SECOND_RULE_ADDRESS_AS_ARRAY; + ruleTwo.srcIpPrefixLen = SECOND_RULE_PREFIX; + ruleTwo.dstIpAddr = SECOND_RULE_ADDRESS_2_AS_ARRAY; + ruleTwo.dstIpPrefixLen = SECOND_RULE_PREFIX_2; + ruleTwo.dstportOrIcmpcodeFirst = SECOND_RULE_DST_PORT_RANGE_START; + ruleTwo.dstportOrIcmpcodeLast = SECOND_RULE_DST_PORT_RANGE_END; + ruleTwo.srcportOrIcmptypeFirst = SECOND_RULE_SRC_PORT_RANGE_START; + ruleTwo.srcportOrIcmptypeLast = SECOND_RULE_SRC_PORT_RANGE_END; + ruleTwo.proto = UDP_PROTOCOL; //UDP + + return new AclRule[]{ruleOne, ruleTwo}; + } +} diff --git a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/AclTestRequests.java b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/AclTestRequests.java new file mode 100644 index 00000000..b580ee8c --- /dev/null +++ b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/AclTestRequests.java @@ -0,0 +1,141 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.acl.test; + +import static io.fd.vpp.jvpp.acl.test.AclTestData.createAclRules; +import static io.fd.vpp.jvpp.acl.test.AclTestData.createMacipRules; + +import io.fd.vpp.jvpp.VppInvocationException; +import io.fd.vpp.jvpp.acl.dto.AclAddReplace; +import io.fd.vpp.jvpp.acl.dto.AclAddReplaceReply; +import io.fd.vpp.jvpp.acl.dto.AclDel; +import io.fd.vpp.jvpp.acl.dto.AclDelReply; +import io.fd.vpp.jvpp.acl.dto.AclDetailsReplyDump; +import io.fd.vpp.jvpp.acl.dto.AclDump; +import io.fd.vpp.jvpp.acl.dto.AclInterfaceListDetailsReplyDump; +import io.fd.vpp.jvpp.acl.dto.AclInterfaceListDump; +import io.fd.vpp.jvpp.acl.dto.AclInterfaceSetAclList; +import io.fd.vpp.jvpp.acl.dto.AclInterfaceSetAclListReply; +import io.fd.vpp.jvpp.acl.dto.MacipAclAdd; +import io.fd.vpp.jvpp.acl.dto.MacipAclAddReply; +import io.fd.vpp.jvpp.acl.dto.MacipAclDel; +import io.fd.vpp.jvpp.acl.dto.MacipAclDelReply; +import io.fd.vpp.jvpp.acl.dto.MacipAclDetailsReplyDump; +import io.fd.vpp.jvpp.acl.dto.MacipAclDump; +import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; +import java.util.concurrent.ExecutionException; + +class AclTestRequests { + + static MacipAclDetailsReplyDump sendMacIpDumpRequest(final FutureJVppAclFacade jvpp) + throws ExecutionException, InterruptedException { + System.out.println("Sending MacipAclDump request..."); + MacipAclDetailsReplyDump dump = jvpp.macipAclDump(new MacipAclDump()).toCompletableFuture().get(); + System.out.println("MacipAclDump returned"); + return dump; + } + + static void sendMacIpAddRequest(final FutureJVppAclFacade jvpp) throws InterruptedException, ExecutionException { + final MacipAclAdd request = createMacIpAddRequest(); + System.out.printf("Sending MacipAclAdd request %s%n", request.toString()); + final MacipAclAddReply reply = jvpp.macipAclAdd(createMacIpAddRequest()).toCompletableFuture().get(); + System.out.printf("MacipAclAdd send result = %s%n", reply); + } + + static void sendMacIpDelRequest(final FutureJVppAclFacade jvpp) throws InterruptedException, ExecutionException { + final MacipAclDel request = new MacipAclDel(); + request.aclIndex = 0; + System.out.printf("Sending MacipAclDel request %s%n", request.toString()); + final MacipAclDelReply reply = jvpp.macipAclDel(request).toCompletableFuture().get(); + System.out.printf("MacipAclDel send result = %s%n", reply); + } + + static void sendAclAddRequest(final FutureJVppAclFacade jvpp) throws InterruptedException, ExecutionException { + final AclAddReplace request = createAclAddRequest(); + System.out.printf("Sending AclAddReplace request %s%n", request.toString()); + final AclAddReplaceReply reply = jvpp.aclAddReplace(request).toCompletableFuture().get(); + System.out.printf("AclAddReplace send result = %s%n", reply); + } + + static AclDetailsReplyDump sendAclDumpRequest(final FutureJVppAclFacade jvpp) + throws InterruptedException, VppInvocationException, ExecutionException { + System.out.println("Sending AclDump request..."); + final AclDetailsReplyDump dump = jvpp.aclDump(new AclDump()).toCompletableFuture().get(); + System.out.printf("AclDump send result = %s%n", dump); + return dump; + } + + static void sendAclDelRequest(final FutureJVppAclFacade jvpp) throws InterruptedException, ExecutionException { + final AclDel request = new AclDel(); + request.aclIndex = 0; + System.out.printf("Sending AclDel request %s%n", request.toString()); + final AclDelReply reply = jvpp.aclDel(request).toCompletableFuture().get(); + System.out.printf("AclDel send result = %s%n", reply); + } + + static AclInterfaceListDetailsReplyDump sendAclInterfaceListDumpRequest(final FutureJVppAclFacade jvpp) + throws InterruptedException, ExecutionException { + final AclInterfaceListDump request = new AclInterfaceListDump(); + request.swIfIndex = 0; + System.out.printf("Sending AclInterfaceListDump request %s%n", request.toString()); + final AclInterfaceListDetailsReplyDump dump = jvpp.aclInterfaceListDump(request).toCompletableFuture().get(); + System.out.printf("AclInterfaceListDump send result = %s%n", dump); + return dump; + } + + static void sendAclInterfaceSetAclList(final FutureJVppAclFacade jvpp) + throws InterruptedException, ExecutionException { + final AclInterfaceSetAclList request = new AclInterfaceSetAclList(); + request.count = 1; + request.acls = new int[]{1}; + request.swIfIndex = 0; + request.nInput = 0; + System.out.printf("Sending AclInterfaceSetAclList request %s%n", request.toString()); + final AclInterfaceSetAclListReply reply = jvpp.aclInterfaceSetAclList(request).toCompletableFuture().get(); + System.out.printf("AclInterfaceSetAclList send result = %s%n", reply); + } + + static void sendAclInterfaceDeleteList(final FutureJVppAclFacade jvpp) + throws InterruptedException, ExecutionException { + // uses same api but sets list to empty + final AclInterfaceSetAclList request = new AclInterfaceSetAclList(); + request.count = 0; + request.acls = new int[]{}; + request.swIfIndex = 0; + request.nInput = 0; + System.out.printf("Sending AclInterfaceSetAclList(Delete) request %s%n", request.toString()); + final AclInterfaceSetAclListReply reply = jvpp.aclInterfaceSetAclList(request).toCompletableFuture().get(); + System.out.printf("AclInterfaceSetAclList(Delete) send result = %s%n", reply); + } + + private static MacipAclAdd createMacIpAddRequest() { + MacipAclAdd request = new MacipAclAdd(); + + request.count = 2; + request.r = createMacipRules(); + return request; + } + + private static AclAddReplace createAclAddRequest() { + AclAddReplace request = new AclAddReplace(); + + request.aclIndex = -1;// to define new one + request.count = 2; + request.r = createAclRules(); + return request; + } +} diff --git a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/FutureApiTest.java b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/FutureApiTest.java new file mode 100644 index 00000000..94490193 --- /dev/null +++ b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/FutureApiTest.java @@ -0,0 +1,68 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.acl.test; + +import static io.fd.vpp.jvpp.acl.test.AclExpectedDumpData.verifyAclDump; +import static io.fd.vpp.jvpp.acl.test.AclExpectedDumpData.verifyAclInterfaceList; +import static io.fd.vpp.jvpp.acl.test.AclExpectedDumpData.verifyMacIpDump; +import static io.fd.vpp.jvpp.acl.test.AclTestRequests.sendAclAddRequest; +import static io.fd.vpp.jvpp.acl.test.AclTestRequests.sendAclDelRequest; +import static io.fd.vpp.jvpp.acl.test.AclTestRequests.sendAclDumpRequest; +import static io.fd.vpp.jvpp.acl.test.AclTestRequests.sendAclInterfaceDeleteList; +import static io.fd.vpp.jvpp.acl.test.AclTestRequests.sendAclInterfaceListDumpRequest; +import static io.fd.vpp.jvpp.acl.test.AclTestRequests.sendAclInterfaceSetAclList; +import static io.fd.vpp.jvpp.acl.test.AclTestRequests.sendMacIpAddRequest; +import static io.fd.vpp.jvpp.acl.test.AclTestRequests.sendMacIpDelRequest; +import static io.fd.vpp.jvpp.acl.test.AclTestRequests.sendMacIpDumpRequest; + +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.acl.JVppAclImpl; +import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; + +public class FutureApiTest { + + public static void main(String[] args) throws Exception { + testCallbackApi(); + } + + private static void testCallbackApi() throws Exception { + System.out.println("Testing Java callback API for acl plugin"); + try (final JVppRegistry registry = new JVppRegistryImpl("macipAclAddTest"); + final FutureJVppAclFacade jvpp = new FutureJVppAclFacade(registry, new JVppAclImpl())) { + + // adds,dump and verifies Mac-Ip acl + sendMacIpAddRequest(jvpp); + verifyMacIpDump(sendMacIpDumpRequest(jvpp).macipAclDetails.get(0)); + + // adds,dumps and verifies Acl acl + sendAclAddRequest(jvpp); + verifyAclDump(sendAclDumpRequest(jvpp).aclDetails.get(0)); + + // adds,dumps and verifies Interface for acl + sendAclInterfaceSetAclList(jvpp); + verifyAclInterfaceList(sendAclInterfaceListDumpRequest(jvpp).aclInterfaceListDetails.get(0)); + + // deletes all created data + sendAclInterfaceDeleteList(jvpp); + sendAclDelRequest(jvpp); + sendMacIpDelRequest(jvpp); + + System.out.println("Disconnecting..."); + } + } +} diff --git a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/Readme.txt b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/Readme.txt new file mode 100644 index 00000000..f68e7aba --- /dev/null +++ b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/Readme.txt @@ -0,0 +1 @@ +sudo java -cp build-vpp-native/vpp-api/java/jvpp-registry-17.01.jar:build-vpp-native/plugins/acl-plugin/jvpp-acl-1.0.jar io.fd.vpp.jvpp.acl.test.FutureApiTest diff --git a/src/vpp-api/java/jvpp-acl/jvpp_acl.c b/src/vpp-api/java/jvpp-acl/jvpp_acl.c new file mode 100644 index 00000000..d56abe3d --- /dev/null +++ b/src/vpp-api/java/jvpp-acl/jvpp_acl.c @@ -0,0 +1,124 @@ +/* + * 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 + +#include +#define vl_typedefs /* define message structures */ +#include +#undef vl_typedefs + +#define vl_endianfun +#include +#undef vl_endianfun + +#define vl_print(handle, ...) +#define vl_printfun +#include +#undef vl_printfun + +/* Get the API version number */ +#define vl_api_version(n,v) static u32 api_version=(v); +#include +#undef vl_api_version + +#include +#include +#include + +#if VPPJNI_DEBUG == 1 + #define DEBUG_LOG(...) clib_warning(__VA_ARGS__) +#else + #define DEBUG_LOG(...) +#endif + +#include + +#include "jvpp-acl/io_fd_vpp_jvpp_acl_JVppAclImpl.h" +#include "jvpp_acl.h" +#include "jvpp-acl/jvpp_acl_gen.h" + +/* + * Class: io_fd_vpp_jvpp_acl_JVppaclImpl + * Method: init0 + * Signature: (JI)V + */ +JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_acl_JVppAclImpl_init0 + (JNIEnv *env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) { + acl_main_t * plugin_main = &acl_main; + u8 * name; + clib_warning ("Java_io_fd_vpp_jvpp_acl_JVppAclImpl_init0"); + + plugin_main->my_client_index = my_client_index; + plugin_main->vl_input_queue = (unix_shared_memory_queue_t *)queue_address; + + name = format (0, "acl_%08x%c", api_version, 0); + plugin_main->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name); + + if (plugin_main->msg_id_base == (u16) ~0) { + jclass exClass = (*env)->FindClass(env, "java/lang/IllegalStateException"); + (*env)->ThrowNew(env, exClass, "acl plugin is not loaded in VPP"); + } else { + plugin_main->callbackObject = (*env)->NewGlobalRef(env, callback); + plugin_main->callbackClass = (jclass)(*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, callback)); + + #define _(N,n) \ + vl_msg_api_set_handlers(VL_API_##N + plugin_main->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_api_reply_handler; + #undef _ + } +} + +JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_acl_JVppAclImpl_close0 +(JNIEnv *env, jclass clazz) { + acl_main_t * plugin_main = &acl_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); +} diff --git a/src/vpp-api/java/jvpp-acl/jvpp_acl.h b/src/vpp-api/java/jvpp-acl/jvpp_acl.h new file mode 100644 index 00000000..2b73d672 --- /dev/null +++ b/src/vpp-api/java/jvpp-acl/jvpp_acl.h @@ -0,0 +1,45 @@ +/* + * 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_acl_h__ +#define __included_jvpp_acl_h__ + +#include +#include +#include +#include +#include +#include + +/* Global state for JVPP-acl */ +typedef struct { + /* Base message index for the acl plugin */ + u16 msg_id_base; + + /* 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; + +} acl_main_t; + +acl_main_t acl_main __attribute__((aligned (64))); + + +#endif /* __included_jvpp_acl_h__ */ diff --git a/src/vpp-api/java/jvpp-common/jvpp_common.c b/src/vpp-api/java/jvpp-common/jvpp_common.c new file mode 100644 index 00000000..a161c09c --- /dev/null +++ b/src/vpp-api/java/jvpp-common/jvpp_common.c @@ -0,0 +1,65 @@ +/* + * 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. + */ +#define _GNU_SOURCE /* for strcasestr(3) */ + +#include "jvpp_common.h" + +#ifndef JVPP_DEBUG +#define JVPP_DEBUG 0 +#endif + +#if JVPP_DEBUG == 1 +#define DEBUG_LOG(...) clib_warning(__VA_ARGS__) +#else +#define DEBUG_LOG(...) +#endif + +/* shared jvpp main structure */ +jvpp_main_t jvpp_main __attribute__((aligned (64))); + +void call_on_error(const char* callName, int contextId, int retval, + jclass callbackClass, jobject callbackObject, + jclass callbackExceptionClass) { + DEBUG_LOG("\nCallOnError : callback=%s, retval=%d, context=%d\n", callName, + clib_net_to_host_u32(retval), clib_net_to_host_u32(context)); + JNIEnv *env = jvpp_main.jenv; + if (!callbackClass) { + DEBUG_LOG("CallOnError : jm->callbackClass is null!\n"); + return; + } + jmethodID excConstructor = (*env)->GetMethodID(env, callbackExceptionClass, + "", "(Ljava/lang/String;II)V"); + if (!excConstructor) { + DEBUG_LOG("CallOnError : excConstructor is null!\n"); + return; + } + jmethodID callbackExcMethod = (*env)->GetMethodID(env, callbackClass, + "onError", "(Lio/fd/vpp/jvpp/VppCallbackException;)V"); + if (!callbackExcMethod) { + DEBUG_LOG("CallOnError : callbackExcMethod is null!\n"); + return; + } + + jobject excObject = (*env)->NewObject(env, callbackExceptionClass, + excConstructor, (*env)->NewStringUTF(env, callName), + clib_net_to_host_u32(contextId), clib_net_to_host_u32(retval)); + if (!excObject) { + DEBUG_LOG("CallOnError : excObject is null!\n"); + return; + } + + (*env)->CallVoidMethod(env, callbackObject, callbackExcMethod, excObject); + DEBUG_LOG("CallOnError : Response sent\n"); +} diff --git a/src/vpp-api/java/jvpp-common/jvpp_common.h b/src/vpp-api/java/jvpp-common/jvpp_common.h new file mode 100644 index 00000000..bbb203ed --- /dev/null +++ b/src/vpp-api/java/jvpp-common/jvpp_common.h @@ -0,0 +1,67 @@ +/* + * 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_common_h__ +#define __included_jvpp_common_h__ +// +#include +#include +#include +#include + +typedef struct { + /* Unique identifier used for matching replays with requests */ + volatile u32 context_id; + + /* Spinlock */ + volatile u32 lock; + u32 tag; + + /* JNI Native Method Interface pointer for message handlers */ + JNIEnv *jenv; + + /* JNI Invoke Interface pointer for attachment of rx thread to java thread */ + JavaVM *jvm; + + /* Convenience */ + unix_shared_memory_queue_t * vl_input_queue; + u32 my_client_index; +} jvpp_main_t; + +extern jvpp_main_t jvpp_main __attribute__((aligned (64))); + +static_always_inline u32 vppjni_get_context_id(jvpp_main_t * jm) { + return __sync_add_and_fetch(&jm->context_id, 1); +} + +static_always_inline void vppjni_lock(jvpp_main_t * jm, u32 tag) { + while (__sync_lock_test_and_set(&jm->lock, 1)) + ; + jm->tag = tag; +} + +static_always_inline void vppjni_unlock(jvpp_main_t * jm) { + jm->tag = 0; + CLIB_MEMORY_BARRIER(); + jm->lock = 0; +} + +/** + * Calls onError callback on callbackObject reference. Passes instance of callbackExceptionClass as parameter. + */ +void call_on_error(const char* callName, int contextId, int retval, + jclass callbackClass, jobject callbackObject, + jclass callbackExceptionClass); + +#endif /* __included_jvpp_common_h__ */ diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackApiTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackApiTest.java new file mode 100644 index 00000000..986993b8 --- /dev/null +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackApiTest.java @@ -0,0 +1,96 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.core.test; + +import io.fd.vpp.jvpp.JVpp; +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.VppCallbackException; +import io.fd.vpp.jvpp.core.JVppCoreImpl; +import io.fd.vpp.jvpp.core.callback.GetNodeIndexCallback; +import io.fd.vpp.jvpp.core.callback.ShowVersionCallback; +import io.fd.vpp.jvpp.core.callback.SwInterfaceCallback; +import io.fd.vpp.jvpp.core.dto.GetNodeIndex; +import io.fd.vpp.jvpp.core.dto.GetNodeIndexReply; +import io.fd.vpp.jvpp.core.dto.ShowVersion; +import io.fd.vpp.jvpp.core.dto.ShowVersionReply; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDetails; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDump; + +public class CallbackApiTest { + + public static void main(String[] args) throws Exception { + testCallbackApi(); + } + + private static void testCallbackApi() throws Exception { + System.out.println("Testing Java callback API with JVppRegistry"); + try (final JVppRegistry registry = new JVppRegistryImpl("CallbackApiTest"); + final JVpp jvpp = new JVppCoreImpl()) { + registry.register(jvpp, new TestCallback()); + + System.out.println("Sending ShowVersion request..."); + final int result = jvpp.send(new ShowVersion()); + System.out.printf("ShowVersion send result = %d%n", result); + + System.out.println("Sending GetNodeIndex request..."); + GetNodeIndex getNodeIndexRequest = new GetNodeIndex(); + getNodeIndexRequest.nodeName = "non-existing-node".getBytes(); + jvpp.send(getNodeIndexRequest); + + System.out.println("Sending SwInterfaceDump request..."); + SwInterfaceDump swInterfaceDumpRequest = new SwInterfaceDump(); + swInterfaceDumpRequest.nameFilterValid = 0; + swInterfaceDumpRequest.nameFilter = "".getBytes(); + jvpp.send(swInterfaceDumpRequest); + + Thread.sleep(1000); + System.out.println("Disconnecting..."); + } + Thread.sleep(1000); + } + + static class TestCallback implements GetNodeIndexCallback, ShowVersionCallback, SwInterfaceCallback { + + @Override + public void onGetNodeIndexReply(final GetNodeIndexReply msg) { + System.out.printf("Received GetNodeIndexReply: %s%n", msg); + } + + @Override + public void onShowVersionReply(final ShowVersionReply msg) { + System.out.printf("Received ShowVersionReply: context=%d, program=%s, version=%s, " + + "buildDate=%s, buildDirectory=%s%n", + msg.context, new String(msg.program), new String(msg.version), + new String(msg.buildDate), new String(msg.buildDirectory)); + } + + @Override + public void onSwInterfaceDetails(final SwInterfaceDetails msg) { + System.out.printf("Received SwInterfaceDetails: interfaceName=%s, l2AddressLength=%d, adminUpDown=%d, " + + "linkUpDown=%d, linkSpeed=%d, linkMtu=%d%n", + new String(msg.interfaceName), msg.l2AddressLength, msg.adminUpDown, + msg.linkUpDown, msg.linkSpeed, (int) msg.linkMtu); + } + + @Override + public void onError(VppCallbackException ex) { + System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", ex.getMethodName(), + ex.getCtxId(), ex.getErrorCode()); + } + } +} diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackJVppFacadeNotificationTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackJVppFacadeNotificationTest.java new file mode 100644 index 00000000..d84cb034 --- /dev/null +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackJVppFacadeNotificationTest.java @@ -0,0 +1,87 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.core.test; + +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.VppCallbackException; +import io.fd.vpp.jvpp.core.JVppCore; +import io.fd.vpp.jvpp.core.JVppCoreImpl; +import io.fd.vpp.jvpp.core.callback.WantInterfaceEventsCallback; +import io.fd.vpp.jvpp.core.callfacade.CallbackJVppCoreFacade; +import io.fd.vpp.jvpp.core.dto.WantInterfaceEventsReply; + +public class CallbackJVppFacadeNotificationTest { + + private static void testCallbackFacade() throws Exception { + System.out.println("Testing CallbackJVppFacade for notifications"); + + try (final JVppRegistry registry = new JVppRegistryImpl("CallbackFacadeTest"); + final JVppCore jvpp = new JVppCoreImpl()) { + final CallbackJVppCoreFacade jvppCallbackFacade = new CallbackJVppCoreFacade(registry, jvpp); + System.out.println("Successfully connected to VPP"); + + final AutoCloseable notificationListenerReg = + jvppCallbackFacade.getNotificationRegistry().registerSwInterfaceSetFlagsNotificationCallback( + NotificationUtils::printNotification + ); + + jvppCallbackFacade.wantInterfaceEvents(NotificationUtils.getEnableInterfaceNotificationsReq(), + new WantInterfaceEventsCallback() { + @Override + public void onWantInterfaceEventsReply(final WantInterfaceEventsReply reply) { + System.out.println("Interface events started"); + } + + @Override + public void onError(final VppCallbackException ex) { + System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", + ex.getMethodName(), ex.getCtxId(), ex.getErrorCode()); + } + }); + + System.out.println("Changing interface configuration"); + NotificationUtils.getChangeInterfaceState().send(jvpp); + + Thread.sleep(1000); + + jvppCallbackFacade.wantInterfaceEvents(NotificationUtils.getDisableInterfaceNotificationsReq(), + new WantInterfaceEventsCallback() { + @Override + public void onWantInterfaceEventsReply(final WantInterfaceEventsReply reply) { + System.out.println("Interface events stopped"); + } + + @Override + public void onError(final VppCallbackException ex) { + System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", + ex.getMethodName(), ex.getCtxId(), ex.getErrorCode()); + } + }); + + notificationListenerReg.close(); + + Thread.sleep(2000); + System.out.println("Disconnecting..."); + } + Thread.sleep(1000); + } + + public static void main(String[] args) throws Exception { + testCallbackFacade(); + } +} diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackJVppFacadeTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackJVppFacadeTest.java new file mode 100644 index 00000000..9f7cb8de --- /dev/null +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackJVppFacadeTest.java @@ -0,0 +1,103 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.core.test; + +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.VppCallbackException; +import io.fd.vpp.jvpp.core.JVppCoreImpl; +import io.fd.vpp.jvpp.core.callback.GetNodeIndexCallback; +import io.fd.vpp.jvpp.core.callback.ShowVersionCallback; +import io.fd.vpp.jvpp.core.callfacade.CallbackJVppCoreFacade; +import io.fd.vpp.jvpp.core.dto.GetNodeIndex; +import io.fd.vpp.jvpp.core.dto.GetNodeIndexReply; +import io.fd.vpp.jvpp.core.dto.ShowVersionReply; + +/** + * CallbackJVppFacade together with CallbackJVppFacadeCallback allow for setting different callback for each request. + * This is more convenient than the approach shown in CallbackApiTest. + */ +public class CallbackJVppFacadeTest { + + private static ShowVersionCallback showVersionCallback1 = new ShowVersionCallback() { + @Override + public void onShowVersionReply(final ShowVersionReply msg) { + System.out.printf("ShowVersionCallback1 received ShowVersionReply: context=%d, program=%s," + + "version=%s, buildDate=%s, buildDirectory=%s%n", msg.context, new String(msg.program), + new String(msg.version), new String(msg.buildDate), new String(msg.buildDirectory)); + } + + @Override + public void onError(VppCallbackException ex) { + System.out.printf("Received onError exception in showVersionCallback1: call=%s, reply=%d, context=%d%n", + ex.getMethodName(), ex.getErrorCode(), ex.getCtxId()); + } + }; + + private static ShowVersionCallback showVersionCallback2 = new ShowVersionCallback() { + @Override + public void onShowVersionReply(final ShowVersionReply msg) { + System.out.printf("ShowVersionCallback2 received ShowVersionReply: context=%d, program=%s," + + "version=%s, buildDate=%s, buildDirectory=%s%n", msg.context, new String(msg.program), + new String(msg.version), new String(msg.buildDate), new String(msg.buildDirectory)); + } + + @Override + public void onError(VppCallbackException ex) { + System.out.printf("Received onError exception in showVersionCallback2: call=%s, reply=%d, context=%d%n", + ex.getMethodName(), ex.getErrorCode(), ex.getCtxId()); + } + + }; + + private static GetNodeIndexCallback getNodeIndexCallback = new GetNodeIndexCallback() { + @Override + public void onGetNodeIndexReply(final GetNodeIndexReply msg) { + System.out.printf("Received GetNodeIndexReply: %s%n", msg); + } + + @Override + public void onError(VppCallbackException ex) { + System.out.printf("Received onError exception in getNodeIndexCallback: call=%s, reply=%d, context=%d%n", + ex.getMethodName(), ex.getErrorCode(), ex.getCtxId()); + } + }; + + private static void testCallbackFacade() throws Exception { + System.out.println("Testing CallbackJVppFacade"); + + try (final JVppRegistry registry = new JVppRegistryImpl("CallbackFacadeTest"); + final CallbackJVppCoreFacade callbackFacade = new CallbackJVppCoreFacade(registry, new JVppCoreImpl())) { + System.out.println("Successfully connected to VPP"); + + callbackFacade.showVersion(showVersionCallback1); + callbackFacade.showVersion(showVersionCallback2); + + GetNodeIndex getNodeIndexRequest = new GetNodeIndex(); + getNodeIndexRequest.nodeName = "dummyNode".getBytes(); + callbackFacade.getNodeIndex(getNodeIndexRequest, getNodeIndexCallback); + + Thread.sleep(2000); + System.out.println("Disconnecting..."); + } + Thread.sleep(1000); + } + + public static void main(String[] args) throws Exception { + testCallbackFacade(); + } +} diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackNotificationApiTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackNotificationApiTest.java new file mode 100644 index 00000000..a9f71f11 --- /dev/null +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackNotificationApiTest.java @@ -0,0 +1,94 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.core.test; + +import static io.fd.vpp.jvpp.core.test.NotificationUtils.getChangeInterfaceState; +import static io.fd.vpp.jvpp.core.test.NotificationUtils.getDisableInterfaceNotificationsReq; +import static io.fd.vpp.jvpp.core.test.NotificationUtils.getEnableInterfaceNotificationsReq; +import static io.fd.vpp.jvpp.core.test.NotificationUtils.printNotification; + +import io.fd.vpp.jvpp.JVpp; +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.VppCallbackException; +import io.fd.vpp.jvpp.core.JVppCoreImpl; +import io.fd.vpp.jvpp.core.callback.SwInterfaceSetFlagsCallback; +import io.fd.vpp.jvpp.core.callback.SwInterfaceSetFlagsNotificationCallback; +import io.fd.vpp.jvpp.core.callback.WantInterfaceEventsCallback; +import io.fd.vpp.jvpp.core.dto.SwInterfaceSetFlagsNotification; +import io.fd.vpp.jvpp.core.dto.SwInterfaceSetFlagsReply; +import io.fd.vpp.jvpp.core.dto.WantInterfaceEventsReply; + +public class CallbackNotificationApiTest { + + private static void testCallbackApi() throws Exception { + System.out.println("Testing Java callback API for notifications"); + try (final JVppRegistry registry = new JVppRegistryImpl("CallbackNotificationTest"); + final JVpp jvpp = new JVppCoreImpl()) { + registry.register(jvpp, new TestCallback()); + System.out.println("Successfully connected to VPP"); + + getEnableInterfaceNotificationsReq().send(jvpp); + System.out.println("Interface notifications started"); + // TODO test ifc dump which also triggers interface flags send + + System.out.println("Changing interface configuration"); + getChangeInterfaceState().send(jvpp); + + // Notifications are received + Thread.sleep(500); + + getDisableInterfaceNotificationsReq().send(jvpp); + System.out.println("Interface events stopped"); + + Thread.sleep(2000); + System.out.println("Disconnecting..."); + } + Thread.sleep(1000); + } + + public static void main(String[] args) throws Exception { + testCallbackApi(); + } + + private static class TestCallback implements SwInterfaceSetFlagsNotificationCallback, + WantInterfaceEventsCallback, SwInterfaceSetFlagsCallback { + + @Override + public void onSwInterfaceSetFlagsNotification( + final SwInterfaceSetFlagsNotification msg) { + printNotification(msg); + } + + @Override + public void onWantInterfaceEventsReply(final WantInterfaceEventsReply wantInterfaceEventsReply) { + System.out.println("Interface notification stream updated"); + } + + @Override + public void onSwInterfaceSetFlagsReply(final SwInterfaceSetFlagsReply swInterfaceSetFlagsReply) { + System.out.println("Interface flags set successfully"); + } + + @Override + public void onError(VppCallbackException ex) { + System.out.printf("Received onError exception in getNodeIndexCallback: call=%s, reply=%d, context=%d%n", + ex.getMethodName(), ex.getErrorCode(), ex.getCtxId()); + + } + } +} diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/ControlPingTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/ControlPingTest.java new file mode 100644 index 00000000..e97f4e3a --- /dev/null +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/ControlPingTest.java @@ -0,0 +1,68 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.core.test; + +import io.fd.vpp.jvpp.JVpp; +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.VppCallbackException; +import io.fd.vpp.jvpp.callback.ControlPingCallback; +import io.fd.vpp.jvpp.core.JVppCoreImpl; +import io.fd.vpp.jvpp.dto.ControlPing; +import io.fd.vpp.jvpp.dto.ControlPingReply; + +public class ControlPingTest { + + private static void testControlPing() throws Exception { + System.out.println("Testing ControlPing using Java callback API"); + try (JVppRegistry registry = new JVppRegistryImpl("ControlPingTest"); + JVpp jvpp = new JVppCoreImpl()) { + + registry.register(jvpp, new ControlPingCallback() { + @Override + public void onControlPingReply(final ControlPingReply reply) { + System.out.printf("Received ControlPingReply: %s%n", reply); + } + + @Override + public void onError(VppCallbackException ex) { + System.out.printf("Received onError exception: call=%s, reply=%d, context=%d ", ex.getMethodName(), + ex.getErrorCode(), ex.getCtxId()); + } + + }); + System.out.println("Successfully connected to VPP"); + Thread.sleep(1000); + + System.out.println("Sending control ping using JVppRegistry"); + registry.controlPing(jvpp.getClass()); + + Thread.sleep(2000); + + System.out.println("Sending control ping using JVpp plugin"); + jvpp.send(new ControlPing()); + + Thread.sleep(2000); + System.out.println("Disconnecting..."); + } + Thread.sleep(1000); + } + + public static void main(String[] args) throws Exception { + testControlPing(); + } +} diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CreateSubInterfaceTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CreateSubInterfaceTest.java new file mode 100644 index 00000000..a96258f4 --- /dev/null +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CreateSubInterfaceTest.java @@ -0,0 +1,120 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.core.test; + +import static java.util.Objects.requireNonNull; + +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.core.JVppCoreImpl; +import io.fd.vpp.jvpp.core.dto.CreateSubif; +import io.fd.vpp.jvpp.core.dto.CreateSubifReply; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDetailsReplyDump; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDump; +import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade; + +/** + *

Tests sub-interface creation.
Equivalent to:
+ * + *

{@code
+ * vppctl create sub GigabitEthernet0/9/0 1 dot1q 100 inner-dot1q any
+ * }
+ * 
+ * + * To verify invoke:
+ *
{@code
+ * vpp_api_test json
+ * vat# sw_interface_dump
+ * }
+ */
+public class CreateSubInterfaceTest {
+
+    private static SwInterfaceDump createSwInterfaceDumpRequest(final String ifaceName) {
+        SwInterfaceDump request = new SwInterfaceDump();
+        request.nameFilter = ifaceName.getBytes();
+        request.nameFilterValid = 1;
+        return request;
+    }
+
+    private static void requireSingleIface(final SwInterfaceDetailsReplyDump response, final String ifaceName) {
+        if (response.swInterfaceDetails.size() != 1) {
+            throw new IllegalStateException(
+                String.format("Expected one interface matching filter %s but was %d", ifaceName,
+                    response.swInterfaceDetails.size()));
+        }
+    }
+
+    private static CreateSubif createSubifRequest(final int swIfIndex, final int subId) {
+        CreateSubif request = new CreateSubif();
+        request.swIfIndex = swIfIndex; // super interface id
+        request.subId = subId;
+        request.noTags = 0;
+        request.oneTag = 0;
+        request.twoTags = 1;
+        request.dot1Ad = 0;
+        request.exactMatch = 1;
+        request.defaultSub = 0;
+        request.outerVlanIdAny = 0;
+        request.innerVlanIdAny = 1;
+        request.outerVlanId = 100;
+        request.innerVlanId = 0;
+        return request;
+    }
+
+    private static void print(CreateSubifReply reply) {
+        System.out.printf("CreateSubifReply: %s%n", reply);
+    }
+
+    private static void testCreateSubInterface() throws Exception {
+        System.out.println("Testing sub-interface creation using Java callback API");
+        try (final JVppRegistry registry = new JVppRegistryImpl("CreateSubInterface");
+             final FutureJVppCoreFacade jvppFacade = new FutureJVppCoreFacade(registry, new JVppCoreImpl())) {
+            System.out.println("Successfully connected to VPP");
+            Thread.sleep(1000);
+
+            final String ifaceName = "GigabitEthernet0/8/0";
+
+            final SwInterfaceDetailsReplyDump swInterfaceDetails =
+                jvppFacade.swInterfaceDump(createSwInterfaceDumpRequest(ifaceName)).toCompletableFuture().get();
+
+            requireNonNull(swInterfaceDetails, "swInterfaceDump returned null");
+            requireNonNull(swInterfaceDetails.swInterfaceDetails, "swInterfaceDetails is null");
+            requireSingleIface(swInterfaceDetails, ifaceName);
+
+            final int swIfIndex = swInterfaceDetails.swInterfaceDetails.get(0).swIfIndex;
+            final int subId = 1;
+
+            final CreateSubifReply createSubifReply =
+                jvppFacade.createSubif(createSubifRequest(swIfIndex, subId)).toCompletableFuture().get();
+            print(createSubifReply);
+
+            final String subIfaceName = "GigabitEthernet0/8/0." + subId;
+            final SwInterfaceDetailsReplyDump subIface =
+                jvppFacade.swInterfaceDump(createSwInterfaceDumpRequest(subIfaceName)).toCompletableFuture().get();
+            requireNonNull(swInterfaceDetails, "swInterfaceDump returned null");
+            requireNonNull(subIface.swInterfaceDetails, "swInterfaceDump returned null");
+            requireSingleIface(swInterfaceDetails, ifaceName);
+
+            System.out.println("Disconnecting...");
+        }
+        Thread.sleep(1000);
+    }
+
+    public static void main(String[] args) throws Exception {
+        testCreateSubInterface();
+    }
+}
diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiNotificationTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiNotificationTest.java
new file mode 100644
index 00000000..9efeae19
--- /dev/null
+++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiNotificationTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package io.fd.vpp.jvpp.core.test;
+
+import static io.fd.vpp.jvpp.core.test.NotificationUtils.getChangeInterfaceState;
+import static io.fd.vpp.jvpp.core.test.NotificationUtils.getDisableInterfaceNotificationsReq;
+import static io.fd.vpp.jvpp.core.test.NotificationUtils.getEnableInterfaceNotificationsReq;
+
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.core.JVppCoreImpl;
+import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade;
+
+public class FutureApiNotificationTest {
+
+    private static void testFutureApi() throws Exception {
+        System.out.println("Testing Java future API for notifications");
+        try (final JVppRegistry registry = new JVppRegistryImpl("FutureApiNotificationTest");
+             final FutureJVppCoreFacade jvppFacade = new FutureJVppCoreFacade(registry, new JVppCoreImpl());
+             final AutoCloseable notificationListenerReg =
+                 jvppFacade.getNotificationRegistry()
+                     .registerSwInterfaceSetFlagsNotificationCallback(NotificationUtils::printNotification)) {
+            System.out.println("Successfully connected to VPP");
+            jvppFacade.wantInterfaceEvents(getEnableInterfaceNotificationsReq()).toCompletableFuture().get();
+            System.out.println("Interface events started");
+
+            System.out.println("Changing interface configuration");
+            jvppFacade.swInterfaceSetFlags(getChangeInterfaceState()).toCompletableFuture().get();
+
+            Thread.sleep(1000);
+
+            jvppFacade.wantInterfaceEvents(getDisableInterfaceNotificationsReq()).toCompletableFuture().get();
+            System.out.println("Interface events stopped");
+            System.out.println("Disconnecting...");
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        testFutureApi();
+    }
+}
diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java
new file mode 100644
index 00000000..f478bab4
--- /dev/null
+++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+
+package io.fd.vpp.jvpp.core.test;
+
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.core.JVppCoreImpl;
+import io.fd.vpp.jvpp.core.dto.BridgeDomainDetailsReplyDump;
+import io.fd.vpp.jvpp.core.dto.BridgeDomainDump;
+import io.fd.vpp.jvpp.core.dto.GetNodeIndex;
+import io.fd.vpp.jvpp.core.dto.GetNodeIndexReply;
+import io.fd.vpp.jvpp.core.dto.ShowVersion;
+import io.fd.vpp.jvpp.core.dto.ShowVersionReply;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceDetails;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceDetailsReplyDump;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceDump;
+import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class FutureApiTest {
+
+    private static final Logger LOG = Logger.getLogger(FutureApiTest.class.getName());
+
+    private static void testShowVersion(final FutureJVppCoreFacade jvpp) throws Exception {
+        LOG.info("Sending ShowVersion request...");
+        final Future replyFuture = jvpp.showVersion(new ShowVersion()).toCompletableFuture();
+        final ShowVersionReply reply = replyFuture.get();
+        LOG.info(
+            String.format(
+                "Received ShowVersionReply: context=%d, program=%s, version=%s, buildDate=%s, buildDirectory=%s%n",
+                reply.context, new String(reply.program), new String(reply.version), new String(reply.buildDate),
+                new String(reply.buildDirectory)));
+    }
+
+    private static void testEmptyBridgeDomainDump(final FutureJVppCoreFacade jvpp) throws Exception {
+        LOG.info("Sending ShowVersion request...");
+        final BridgeDomainDump request = new BridgeDomainDump();
+        request.bdId = -1; // dump call
+
+        final CompletableFuture
+            replyFuture = jvpp.bridgeDomainDump(request).toCompletableFuture();
+        final BridgeDomainDetailsReplyDump reply = replyFuture.get();
+
+        if (reply == null || reply.bridgeDomainDetails == null) {
+            LOG.severe("Received null response for empty dump: " + reply);
+        } else {
+            LOG.info(
+                String.format(
+                    "Received empty bridge-domain dump reply with list of bridge-domains: %s, %s",
+                    reply.bridgeDomainDetails, reply.bridgeDomainSwIfDetails));
+        }
+    }
+
+    private static void testGetNodeIndex(final FutureJVppCoreFacade jvpp) {
+        LOG.info("Sending GetNodeIndex request...");
+        final GetNodeIndex request = new GetNodeIndex();
+        request.nodeName = "non-existing-node".getBytes();
+        final Future replyFuture = jvpp.getNodeIndex(request).toCompletableFuture();
+        try {
+            final GetNodeIndexReply reply = replyFuture.get();
+            LOG.info(
+                String.format(
+                    "Received GetNodeIndexReply: context=%d, nodeIndex=%d%n", reply.context, reply.nodeIndex));
+        } catch (Exception e) {
+            LOG.log(Level.SEVERE, "GetNodeIndex request failed", e);
+        }
+    }
+
+    private static void testSwInterfaceDump(final FutureJVppCoreFacade jvpp) throws Exception {
+        LOG.info("Sending SwInterfaceDump request...");
+        final SwInterfaceDump request = new SwInterfaceDump();
+        request.nameFilterValid = 0;
+        request.nameFilter = "".getBytes();
+
+        final Future replyFuture = jvpp.swInterfaceDump(request).toCompletableFuture();
+        final SwInterfaceDetailsReplyDump reply = replyFuture.get();
+        for (SwInterfaceDetails details : reply.swInterfaceDetails) {
+            Objects.requireNonNull(details, "reply.swInterfaceDetails contains null element!");
+            LOG.info(
+                String.format("Received SwInterfaceDetails: interfaceName=%s, l2AddressLength=%d, adminUpDown=%d, "
+                        + "linkUpDown=%d, linkSpeed=%d, linkMtu=%d%n",
+                    new String(details.interfaceName), details.l2AddressLength, details.adminUpDown,
+                    details.linkUpDown, details.linkSpeed, (int) details.linkMtu));
+        }
+    }
+
+    private static void testFutureApi() throws Exception {
+        LOG.info("Testing Java future API");
+        try (final JVppRegistry registry = new JVppRegistryImpl("FutureApiTest");
+             final FutureJVppCoreFacade jvppFacade = new FutureJVppCoreFacade(registry, new JVppCoreImpl())) {
+            LOG.info("Successfully connected to VPP");
+
+            testEmptyBridgeDomainDump(jvppFacade);
+            testShowVersion(jvppFacade);
+            testGetNodeIndex(jvppFacade);
+            testSwInterfaceDump(jvppFacade);
+
+            LOG.info("Disconnecting...");
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        testFutureApi();
+    }
+}
diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/L2AclTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/L2AclTest.java
new file mode 100644
index 00000000..6b3fa993
--- /dev/null
+++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/L2AclTest.java
@@ -0,0 +1,195 @@
+/*
+ * 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.
+ */
+
+package io.fd.vpp.jvpp.core.test;
+
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.core.JVppCoreImpl;
+import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession;
+import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSessionReply;
+import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable;
+import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTableReply;
+import io.fd.vpp.jvpp.core.dto.ClassifySessionDetailsReplyDump;
+import io.fd.vpp.jvpp.core.dto.ClassifySessionDump;
+import io.fd.vpp.jvpp.core.dto.ClassifyTableByInterface;
+import io.fd.vpp.jvpp.core.dto.ClassifyTableByInterfaceReply;
+import io.fd.vpp.jvpp.core.dto.ClassifyTableIds;
+import io.fd.vpp.jvpp.core.dto.ClassifyTableIdsReply;
+import io.fd.vpp.jvpp.core.dto.ClassifyTableInfo;
+import io.fd.vpp.jvpp.core.dto.ClassifyTableInfoReply;
+import io.fd.vpp.jvpp.core.dto.InputAclSetInterface;
+import io.fd.vpp.jvpp.core.dto.InputAclSetInterfaceReply;
+import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade;
+import javax.xml.bind.DatatypeConverter;
+
+/**
+ * 

Tests L2 ACL creation and read.
Equivalent to the following vppctl commands:
+ * + *

{@code
+ * vppctl classify table mask l2 src
+ * vppctl classify session acl-hit-next deny opaque-index 0 table-index 0 match l2 src 01:02:03:04:05:06
+ * vppctl set int input acl intfc local0 l2-table 0
+ * vppctl sh class table verbose
+ * }
+ * 
+ */ +public class L2AclTest { + + private static final int LOCAL0_IFACE_ID = 0; + + private static ClassifyAddDelTable createClassifyTable() { + ClassifyAddDelTable request = new ClassifyAddDelTable(); + request.isAdd = 1; + request.tableIndex = ~0; // default + request.nbuckets = 2; + request.memorySize = 2 << 20; + request.nextTableIndex = ~0; // default + request.missNextIndex = ~0; // default + request.skipNVectors = 0; + request.matchNVectors = 1; + request.mask = + new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, 0x00, 0x00, 0x00, 0x00}; + return request; + } + + private static ClassifyTableInfo createClassifyTableInfoRequest(final int tableId) { + ClassifyTableInfo request = new ClassifyTableInfo(); + request.tableId = tableId; + return request; + } + + private static ClassifyAddDelSession createClassifySession(final int tableIndex) { + ClassifyAddDelSession request = new ClassifyAddDelSession(); + request.isAdd = 1; + request.tableIndex = tableIndex; + request.hitNextIndex = 0; // deny + request.opaqueIndex = 0; + request.advance = 0; // default + // match 01:02:03:04:05:06 mac address + request.match = + new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, + (byte) 0x05, (byte) 0x06, 0x00, 0x00, 0x00, 0x00}; + return request; + } + + private static ClassifySessionDump createClassifySessionDumpRequest(final int newTableIndex) { + ClassifySessionDump request = new ClassifySessionDump(); + request.tableId = newTableIndex; + return request; + } + + private static InputAclSetInterface aclSetInterface() { + InputAclSetInterface request = new InputAclSetInterface(); + request.isAdd = 1; + request.swIfIndex = LOCAL0_IFACE_ID; + request.ip4TableIndex = ~0; // skip + request.ip6TableIndex = ~0; // skip + request.l2TableIndex = 0; + return request; + } + + private static ClassifyTableByInterface createClassifyTableByInterfaceRequest() { + ClassifyTableByInterface request = new ClassifyTableByInterface(); + request.swIfIndex = LOCAL0_IFACE_ID; + return request; + } + + private static void print(ClassifyAddDelTableReply reply) { + System.out.printf("ClassifyAddDelTableReply: %s%n", reply); + } + + private static void print(ClassifyTableIdsReply reply) { + System.out.printf("ClassifyTableIdsReply: %s%n", reply); + } + + private static void print(final ClassifyTableInfoReply reply) { + System.out.println(reply); + if (reply != null) { + System.out.println("Mask hex: " + DatatypeConverter.printHexBinary(reply.mask)); + } + } + + private static void print(ClassifyAddDelSessionReply reply) { + System.out.printf("ClassifyAddDelSessionReply: context=%s%n", reply); + } + + private static void print(final ClassifySessionDetailsReplyDump reply) { + System.out.println(reply); + reply.classifySessionDetails.forEach(detail -> { + System.out.println(detail); + System.out.println("Match hex: " + DatatypeConverter.printHexBinary(detail.match)); + }); + } + + private static void print(final InputAclSetInterfaceReply reply) { + System.out.printf("InputAclSetInterfaceReply: context=%s%n", reply); + } + + private static void print(final ClassifyTableByInterfaceReply reply) { + System.out.printf("ClassifyAddDelTableReply: %s%n", reply); + } + + private static void testL2Acl() throws Exception { + System.out.println("Testing L2 ACLs using Java callback API"); + try (final JVppRegistry registry = new JVppRegistryImpl("L2AclTest"); + final FutureJVppCoreFacade jvppFacade = new FutureJVppCoreFacade(registry, new JVppCoreImpl())) { + + System.out.println("Successfully connected to VPP"); + Thread.sleep(1000); + + final ClassifyAddDelTableReply classifyAddDelTableReply = + jvppFacade.classifyAddDelTable(createClassifyTable()).toCompletableFuture().get(); + print(classifyAddDelTableReply); + + final ClassifyTableIdsReply classifyTableIdsReply = + jvppFacade.classifyTableIds(new ClassifyTableIds()).toCompletableFuture().get(); + print(classifyTableIdsReply); + + final ClassifyTableInfoReply classifyTableInfoReply = + jvppFacade.classifyTableInfo(createClassifyTableInfoRequest(classifyAddDelTableReply.newTableIndex)) + .toCompletableFuture().get(); + print(classifyTableInfoReply); + + final ClassifyAddDelSessionReply classifyAddDelSessionReply = + jvppFacade.classifyAddDelSession(createClassifySession(classifyAddDelTableReply.newTableIndex)) + .toCompletableFuture().get(); + print(classifyAddDelSessionReply); + + final ClassifySessionDetailsReplyDump classifySessionDetailsReplyDump = + jvppFacade.classifySessionDump(createClassifySessionDumpRequest(classifyAddDelTableReply.newTableIndex)) + .toCompletableFuture().get(); + print(classifySessionDetailsReplyDump); + + final InputAclSetInterfaceReply inputAclSetInterfaceReply = + jvppFacade.inputAclSetInterface(aclSetInterface()).toCompletableFuture().get(); + print(inputAclSetInterfaceReply); + + final ClassifyTableByInterfaceReply classifyTableByInterfaceReply = + jvppFacade.classifyTableByInterface(createClassifyTableByInterfaceRequest()).toCompletableFuture() + .get(); + print(classifyTableByInterfaceReply); + + System.out.println("Disconnecting..."); + } + Thread.sleep(1000); + } + + public static void main(String[] args) throws Exception { + testL2Acl(); + } +} diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/LispAdjacencyTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/LispAdjacencyTest.java new file mode 100644 index 00000000..d7f5039b --- /dev/null +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/LispAdjacencyTest.java @@ -0,0 +1,124 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.core.test; + +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.core.JVppCoreImpl; +import io.fd.vpp.jvpp.core.dto.LispAddDelAdjacency; +import io.fd.vpp.jvpp.core.dto.LispAddDelLocalEid; +import io.fd.vpp.jvpp.core.dto.LispAddDelLocatorSet; +import io.fd.vpp.jvpp.core.dto.LispAddDelRemoteMapping; +import io.fd.vpp.jvpp.core.dto.LispAdjacenciesGet; +import io.fd.vpp.jvpp.core.dto.LispAdjacenciesGetReply; +import io.fd.vpp.jvpp.core.dto.LispEnableDisable; +import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.ExecutionException; +import java.util.logging.Logger; + +/** + * Tests lisp adjacency creation and read (custom vpe.api type support showcase). + */ +public class LispAdjacencyTest { + + private static final Logger LOG = Logger.getLogger(LispAdjacencyTest.class.getName()); + + private static void enableLisp(final FutureJVppCoreFacade jvpp) throws ExecutionException, InterruptedException { + final LispEnableDisable request = new LispEnableDisable(); + request.isEn = 1; + jvpp.lispEnableDisable(request).toCompletableFuture().get(); + LOG.info("Lisp enabled successfully"); + } + + private static void addLocatorSet(final FutureJVppCoreFacade jvpp) throws ExecutionException, InterruptedException { + final LispAddDelLocatorSet request = new LispAddDelLocatorSet(); + request.isAdd = 1; + request.locatorSetName = "ls1".getBytes(StandardCharsets.UTF_8); + jvpp.lispAddDelLocatorSet(request).toCompletableFuture().get(); + LOG.info("Locator set created successfully:" + request.toString()); + } + + private static void addLocalEid(final FutureJVppCoreFacade jvpp) throws ExecutionException, InterruptedException { + final LispAddDelLocalEid request = new LispAddDelLocalEid(); + request.isAdd = 1; + request.locatorSetName = "ls1".getBytes(StandardCharsets.UTF_8); + request.eid = new byte[] {1, 2, 1, 10}; + request.eidType = 0; // ip4 + request.vni = 0; + request.prefixLen = 32; + jvpp.lispAddDelLocalEid(request).toCompletableFuture().get(); + LOG.info("Local EID created successfully:" + request.toString()); + } + + private static void addRemoteMapping(final FutureJVppCoreFacade jvpp) + throws ExecutionException, InterruptedException { + final LispAddDelRemoteMapping request = new LispAddDelRemoteMapping(); + request.isAdd = 1; + request.vni = 0; + request.eid = new byte[] {1, 2, 1, 20}; + request.eidLen = 32; + request.rlocNum = 1; + request.rlocs = new byte[] {1, 1, 1, 1, 2, 1, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + jvpp.lispAddDelRemoteMapping(request).toCompletableFuture().get(); + LOG.info("Remote mapping created successfully:" + request.toString()); + } + + private static void addAdjacency(final FutureJVppCoreFacade jvpp) throws ExecutionException, InterruptedException { + final LispAddDelAdjacency request = new LispAddDelAdjacency(); + request.isAdd = 1; + request.leid = new byte[] {1, 2, 1, 10}; + request.leidLen = 32; + request.reid = new byte[] {1, 2, 1, 20}; + request.reidLen = 32; + request.eidType = 0; // ip4 + request.vni = 0; + jvpp.lispAddDelAdjacency(request).toCompletableFuture().get(); + LOG.info("Lisp adjacency created successfully:" + request.toString()); + } + + private static void showAdjacencies(final FutureJVppCoreFacade jvpp) + throws ExecutionException, InterruptedException { + final LispAdjacenciesGetReply reply = + jvpp.lispAdjacenciesGet(new LispAdjacenciesGet()).toCompletableFuture().get(); + LOG.info("Lisp adjacency received successfully:" + reply.toString()); + } + + private static void testAdjacency(final FutureJVppCoreFacade jvpp) throws Exception { + enableLisp(jvpp); + addLocatorSet(jvpp); + addLocalEid(jvpp); + addRemoteMapping(jvpp); + addAdjacency(jvpp); + showAdjacencies(jvpp); + } + + private static void testFutureApi() throws Exception { + LOG.info("Create lisp adjacency test"); + try (final JVppRegistry registry = new JVppRegistryImpl("LispAdjacencyTest"); + final FutureJVppCoreFacade jvppFacade = new FutureJVppCoreFacade(registry, new JVppCoreImpl())) { + LOG.info("Successfully connected to VPP"); + + testAdjacency(jvppFacade); + LOG.info("Disconnecting..."); + } + } + + public static void main(String[] args) throws Exception { + testFutureApi(); + } +} diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/NotificationUtils.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/NotificationUtils.java new file mode 100644 index 00000000..f82946c3 --- /dev/null +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/NotificationUtils.java @@ -0,0 +1,53 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.core.test; + +import java.io.PrintStream; +import io.fd.vpp.jvpp.core.dto.SwInterfaceSetFlags; +import io.fd.vpp.jvpp.core.dto.SwInterfaceSetFlagsNotification; +import io.fd.vpp.jvpp.core.dto.WantInterfaceEvents; + +final class NotificationUtils { + + private NotificationUtils() {} + + static PrintStream printNotification(final SwInterfaceSetFlagsNotification msg) { + return System.out.printf("Received interface notification: ifc: %s%n", msg); + } + + static SwInterfaceSetFlags getChangeInterfaceState() { + final SwInterfaceSetFlags swInterfaceSetFlags = new SwInterfaceSetFlags(); + swInterfaceSetFlags.swIfIndex = 0; + swInterfaceSetFlags.adminUpDown = 1; + swInterfaceSetFlags.deleted = 0; + return swInterfaceSetFlags; + } + + static WantInterfaceEvents getEnableInterfaceNotificationsReq() { + WantInterfaceEvents wantInterfaceEvents = new WantInterfaceEvents(); + wantInterfaceEvents.pid = 1; + wantInterfaceEvents.enableDisable = 1; + return wantInterfaceEvents; + } + + static WantInterfaceEvents getDisableInterfaceNotificationsReq() { + WantInterfaceEvents wantInterfaceEvents = new WantInterfaceEvents(); + wantInterfaceEvents.pid = 1; + wantInterfaceEvents.enableDisable = 0; + return wantInterfaceEvents; + } +} diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/Readme.txt b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/Readme.txt new file mode 100644 index 00000000..1344dc9e --- /dev/null +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/Readme.txt @@ -0,0 +1,17 @@ +This package contains basic tests for jvpp. To run the tests: + +- Make sure VPP is running +- From VPP's build-root/ folder execute: + - sudo java -cp build-vpp-native/vpp-api/java/jvpp-registry-17.01.jar:build-vpp-native/vpp-api/java/jvpp-core-17.01.jar io.fd.vpp.jvpp.core.test.[test name] + +Available tests: +CallbackApiTest - Similar to ControlPingTest, invokes more complex calls (e.g. interface dump) using low level JVpp APIs +CallbackJVppFacadeNotificationTest - Tests interface notifications using Callback based JVpp facade +CallbackJVppFacadeTest - Execution of more complex calls using Callback based JVpp facade +CallbackNotificationApiTest - Tests interface notifications using low level JVpp APIs +ControlPingTest - Simple test executing a single control ping using low level JVpp APIs +CreateSubInterfaceTest - Tests sub-interface creation +FutureApiNotificationTest - Tests interface notifications using Future based JVpp facade +FutureApiTest - Execution of more complex calls using Future based JVpp facade +L2AclTest - Tests L2 ACL creation +LispAdjacencyTest - Tests lisp adjacency creation and read (custom vpe.api type support showcase) diff --git a/src/vpp-api/java/jvpp-core/jvpp_core.c b/src/vpp-api/java/jvpp-core/jvpp_core.c new file mode 100644 index 00000000..ef4cb8e3 --- /dev/null +++ b/src/vpp-api/java/jvpp-core/jvpp_core.c @@ -0,0 +1,117 @@ +/* + * 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 + +#include +#define vl_typedefs /* define message structures */ +#include +#undef vl_typedefs + +#define vl_endianfun +#include +#undef vl_endianfun + +#define vl_print(handle, ...) +#define vl_printfun +#include +#undef vl_printfun + +#include +#include +#include +#include + +#include + +// TODO: generate jvpp_plugin_name.c files (or at least reuse plugin's main structure) +typedef struct { + /* Base message index for the jvpp-core plugin */ + u16 msg_id_base; + + /* 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; + +} core_main_t; + +core_main_t core_main __attribute__((aligned (64))); + +#include "io_fd_vpp_jvpp_core_JVppCoreImpl.h" +#include "jvpp_core_gen.h" + +JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_core_JVppCoreImpl_init0 +(JNIEnv * env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) { + core_main_t * plugin_main = &core_main; + 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)); + + #define _(N,n) \ + vl_msg_api_set_handlers(VL_API_##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_core_JVppCoreImpl_close0 +(JNIEnv *env, jclass clazz) { + core_main_t * plugin_main = &core_main; + + // cleanup: + (*env)->DeleteGlobalRef(env, plugin_main->callbackClass); + (*env)->DeleteGlobalRef(env, plugin_main->callbackObject); + + plugin_main->callbackClass = NULL; + plugin_main->callbackObject = NULL; +} + +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; +} + +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); +} + + + diff --git a/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/IoamExportApiTest.java b/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/IoamExportApiTest.java new file mode 100644 index 00000000..cb85f005 --- /dev/null +++ b/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/IoamExportApiTest.java @@ -0,0 +1,56 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.ioamexport.test; + +import java.net.InetAddress; + +import io.fd.vpp.jvpp.JVpp; +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.VppCallbackException; +import io.fd.vpp.jvpp.ioamexport.JVppIoamexportImpl; +import io.fd.vpp.jvpp.ioamexport.future.FutureJVppIoamexportFacade; +import io.fd.vpp.jvpp.ioamexport.dto.IoamExportIp6EnableDisable; +import io.fd.vpp.jvpp.ioamexport.dto.IoamExportIp6EnableDisableReply; + +public class IoamExportApiTest { + + public static void main(String[] args) throws Exception { + ioamExportTestApi(); + } + + private static void ioamExportTestApi() throws Exception { + System.out.println("Testing Java API for ioam export plugin"); + try (final JVppRegistry registry = new JVppRegistryImpl("ioamExportApiTest"); + final JVpp jvpp = new JVppIoamexportImpl()) { + FutureJVppIoamexportFacade ioamexportJvpp = new FutureJVppIoamexportFacade(registry,jvpp); + System.out.println("Sending ioam export request..."); + IoamExportIp6EnableDisable request = new IoamExportIp6EnableDisable(); + request.isDisable = 0; + InetAddress collectorAddress = InetAddress.getByName("2001:0DB8:AC10:FE01:0000:0000:0000:0000"); + InetAddress srcAddress = InetAddress.getByName("2001:0DB8:AC10:FE01:0000:0000:0000:0001"); + request.collectorAddress = collectorAddress.getAddress(); + request.srcAddress = srcAddress.getAddress(); + IoamExportIp6EnableDisableReply reply = ioamexportJvpp.ioamExportIp6EnableDisable(request).toCompletableFuture().get(); + System.out.printf("IoamExportIp6EnableDisableReply = "+reply.toString()+"%n"); + + Thread.sleep(1000); + + System.out.println("Disconnecting..."); + } + } +} diff --git a/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/Readme.txt b/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/Readme.txt new file mode 100644 index 00000000..1b38c285 --- /dev/null +++ b/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/Readme.txt @@ -0,0 +1 @@ +sudo java -cp build-vpp_debug-native/vpp-api/java/jvpp-registry-17.01.jar:build-vpp_debug-native/plugins/ioam-plugin/jvpp-ioam-export-1.0.jar io.fd.vpp.jvpp.ioamexport.test.IoamExportApiTest diff --git a/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.c b/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.c new file mode 100644 index 00000000..5cda89d1 --- /dev/null +++ b/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.c @@ -0,0 +1,124 @@ +/* + * 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 + +#include +#define vl_typedefs /* define message structures */ +#include +#undef vl_typedefs + +#define vl_endianfun +#include +#undef vl_endianfun + +#define vl_print(handle, ...) +#define vl_printfun +#include +#undef vl_printfun + +/* Get the API version number */ +#define vl_api_version(n,v) static u32 api_version=(v); +#include +#undef vl_api_version + +#include +#include +#include + +#if VPPJNI_DEBUG == 1 + #define DEBUG_LOG(...) clib_warning(__VA_ARGS__) +#else + #define DEBUG_LOG(...) +#endif + +#include + +#include "jvpp-ioamexport/io_fd_vpp_jvpp_ioamexport_JVppIoamexportImpl.h" +#include "jvpp_ioam_export.h" +#include "jvpp-ioamexport/jvpp_ioamexport_gen.h" + +/* + * Class: io_fd_vpp_jvpp_ioamexport_JVppIoamexportImpl + * Method: init0 + * Signature: (JI)V + */ +JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioamexport_JVppIoamexportImpl_init0 + (JNIEnv *env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) { + ioamexport_main_t * plugin_main = &ioamexport_main; + u8 * name; + clib_warning ("Java_io_fd_vpp_jvpp_ioamexport_JVppIoamexportImpl_init0"); + + plugin_main->my_client_index = my_client_index; + plugin_main->vl_input_queue = (unix_shared_memory_queue_t *)queue_address; + + name = format (0, "ioam_export_%08x%c", api_version, 0); + plugin_main->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name); + + if (plugin_main->msg_id_base == (u16) ~0) { + jclass exClass = (*env)->FindClass(env, "java/lang/IllegalStateException"); + (*env)->ThrowNew(env, exClass, "ioam_export plugin is not loaded in VPP"); + } else { + plugin_main->callbackObject = (*env)->NewGlobalRef(env, callback); + plugin_main->callbackClass = (jclass)(*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, callback)); + + #define _(N,n) \ + vl_msg_api_set_handlers(VL_API_##N + plugin_main->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_api_reply_handler; + #undef _ + } +} + +JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioamexport_JVppIoamexportImpl_close0 +(JNIEnv *env, jclass clazz) { + ioamexport_main_t * plugin_main = &ioamexport_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 iOAM EXPORT */ +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 iOAM EXPORT */ +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); +} diff --git a/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.h b/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.h new file mode 100644 index 00000000..b6c0c16e --- /dev/null +++ b/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.h @@ -0,0 +1,45 @@ +/* + * 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_ioam_export_h__ +#define __included_jvpp_ioam_export_h__ + +#include +#include +#include +#include +#include +#include + +/* Global state for JVPP-IOAM-EXPORT */ +typedef struct { + /* Base message index for the export plugin */ + u16 msg_id_base; + + /* 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; + +} ioamexport_main_t; + +ioamexport_main_t ioamexport_main __attribute__((aligned (64))); + + +#endif /* __included_jvpp_ioam_export_h__ */ diff --git a/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/IoamPotApiTest.java b/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/IoamPotApiTest.java new file mode 100644 index 00000000..74eb86a1 --- /dev/null +++ b/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/IoamPotApiTest.java @@ -0,0 +1,75 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.ioampot.test; + +import io.fd.vpp.jvpp.JVpp; +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.VppCallbackException; +import io.fd.vpp.jvpp.ioampot.JVppIoampotImpl; +import io.fd.vpp.jvpp.ioampot.callback.PotProfileAddCallback; +import io.fd.vpp.jvpp.ioampot.dto.PotProfileAdd; +import io.fd.vpp.jvpp.ioampot.dto.PotProfileAddReply; + +public class IoamPotApiTest { + + static class IoamPotTestCallback implements PotProfileAddCallback { + + @Override + public void onPotProfileAddReply(final PotProfileAddReply reply) { + System.out.printf("Received PotProfileAddReply reply: context=%d%n", + reply.context); + } + + @Override + public void onError(VppCallbackException ex) { + System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", ex.getMethodName(), + ex.getCtxId(), ex.getErrorCode()); + } + } + + public static void main(String[] args) throws Exception { + ioamPotTestApi(); + } + + private static void ioamPotTestApi() throws Exception { + System.out.println("Testing Java API for ioam pot plugin"); + try (final JVppRegistry registry = new JVppRegistryImpl("ioamPotApiTest"); + final JVpp jvpp = new JVppIoampotImpl()) { + registry.register(jvpp, new IoamPotTestCallback()); + + System.out.println("Sending ioam pot profile add request..."); + PotProfileAdd request = new PotProfileAdd(); + request.id = 0; + request.validator = 4; + request.secretKey = 1; + request.secretShare = 2; + request.prime = 1234; + request.maxBits = 53; + request.lpc = 1234; + request.polynomialPublic = 1234; + request.listNameLen = (byte)"test pot profile".getBytes().length; + request.listName = "test pot profile".getBytes(); + final int result = jvpp.send(request); + System.out.printf("PotProfileAdd send result = %d%n", result); + + Thread.sleep(1000); + + System.out.println("Disconnecting..."); + } + } +} diff --git a/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/Readme.txt b/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/Readme.txt new file mode 100644 index 00000000..2323494d --- /dev/null +++ b/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/Readme.txt @@ -0,0 +1 @@ +sudo java -cp build-vpp_debug-native/vpp-api/java/jvpp-registry-16.12.jar:build-vpp_debug-native/plugins/ioam-plugin/jvpp-ioam-pot-1.0.jar io.fd.vpp.jvpp.ioampot.test.IoamPotApiTest diff --git a/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.c b/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.c new file mode 100644 index 00000000..9291dbba --- /dev/null +++ b/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.c @@ -0,0 +1,124 @@ +/* + * 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 + +#include +#define vl_typedefs /* define message structures */ +#include +#undef vl_typedefs + +#define vl_endianfun +#include +#undef vl_endianfun + +#define vl_print(handle, ...) +#define vl_printfun +#include +#undef vl_printfun + +/* Get the API version number */ +#define vl_api_version(n,v) static u32 api_version=(v); +#include +#undef vl_api_version + +#include +#include +#include + +#if VPPJNI_DEBUG == 1 + #define DEBUG_LOG(...) clib_warning(__VA_ARGS__) +#else + #define DEBUG_LOG(...) +#endif + +#include + +#include "jvpp-ioampot/io_fd_vpp_jvpp_ioampot_JVppIoampotImpl.h" +#include "jvpp_ioam_pot.h" +#include "jvpp-ioampot/jvpp_ioampot_gen.h" + +/* + * Class: io_fd_vpp_jvpp_ioampot_JVppIoampotImpl + * Method: init0 + * Signature: (JI)V + */ +JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioampot_JVppIoampotImpl_init0 + (JNIEnv *env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) { + ioampot_main_t * plugin_main = &ioampot_main; + u8 * name; + clib_warning ("Java_io_fd_vpp_jvpp_ioampot_JVppIoampotImpl_init0"); + + plugin_main->my_client_index = my_client_index; + plugin_main->vl_input_queue = (unix_shared_memory_queue_t *)queue_address; + + name = format (0, "ioam_pot_%08x%c", api_version, 0); + plugin_main->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name); + + if (plugin_main->msg_id_base == (u16) ~0) { + jclass exClass = (*env)->FindClass(env, "java/lang/IllegalStateException"); + (*env)->ThrowNew(env, exClass, "ioam_pot plugin is not loaded in VPP"); + } else { + plugin_main->callbackObject = (*env)->NewGlobalRef(env, callback); + plugin_main->callbackClass = (jclass)(*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, callback)); + + #define _(N,n) \ + vl_msg_api_set_handlers(VL_API_##N + plugin_main->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_api_reply_handler; + #undef _ + } +} + +JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioampot_JVppIoampotImpl_close0 +(JNIEnv *env, jclass clazz) { + ioampot_main_t * plugin_main = &ioampot_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 iOAM POT */ +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 iOAM POT */ +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); +} diff --git a/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.h b/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.h new file mode 100644 index 00000000..00aa51db --- /dev/null +++ b/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.h @@ -0,0 +1,45 @@ +/* + * 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_ioam_pot_h__ +#define __included_jvpp_ioam_pot_h__ + +#include +#include +#include +#include +#include +#include + +/* Global state for JVPP-IOAM-POT */ +typedef struct { + /* Base message index for the pot plugin */ + u16 msg_id_base; + + /* 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; + +} ioampot_main_t; + +ioampot_main_t ioampot_main __attribute__((aligned (64))); + + +#endif /* __included_jvpp_ioam_pot_h__ */ diff --git a/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/IoamTraceApiTest.java b/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/IoamTraceApiTest.java new file mode 100644 index 00000000..bc8c1c3a --- /dev/null +++ b/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/IoamTraceApiTest.java @@ -0,0 +1,77 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.ioamtrace.test; + +import io.fd.vpp.jvpp.JVpp; +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.VppCallbackException; +import io.fd.vpp.jvpp.ioamtrace.future.FutureJVppIoamtraceFacade; +import io.fd.vpp.jvpp.ioamtrace.JVppIoamtraceImpl; +import io.fd.vpp.jvpp.ioamtrace.callback.TraceProfileAddCallback; +import io.fd.vpp.jvpp.ioamtrace.dto.TraceProfileAdd; +import io.fd.vpp.jvpp.ioamtrace.dto.TraceProfileAddReply; +import io.fd.vpp.jvpp.ioamtrace.dto.TraceProfileShowConfig; +import io.fd.vpp.jvpp.ioamtrace.dto.TraceProfileShowConfigReply; + +public class IoamTraceApiTest { + + static class IoamTraceTestCallback implements TraceProfileAddCallback { + + @Override + public void onTraceProfileAddReply(final TraceProfileAddReply reply) { + System.out.printf("Received TraceProfileAddReply reply: context=%d%n", + reply.context); + } + + @Override + public void onError(VppCallbackException ex) { + System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", ex.getMethodName(), + ex.getCtxId(), ex.getErrorCode()); + } + } + + public static void main(String[] args) throws Exception { + ioamTraceTestApi(); + } + + private static void ioamTraceTestApi() throws Exception { + System.out.println("Testing Java API for ioam trace plugin"); + try (final JVppRegistry registry = new JVppRegistryImpl("ioamTraceApiTest"); + final JVpp jvpp = new JVppIoamtraceImpl()) { + FutureJVppIoamtraceFacade ioamtraceJvpp = new FutureJVppIoamtraceFacade(registry,jvpp); + + System.out.println("Sending ioam trace profile add request..."); + TraceProfileAdd request = new TraceProfileAdd(); + request.traceType = 0x1f; + request.numElts = 4; + request.nodeId = 1; + request.traceTsp = 2; + request.appData = 1234; + final int result = jvpp.send(request); + System.out.printf("TraceProfileAdd send result = %d%n", result); + + Thread.sleep(1000); + + TraceProfileShowConfig showRequest = new TraceProfileShowConfig(); + TraceProfileShowConfigReply reply = ioamtraceJvpp.traceProfileShowConfig(showRequest).toCompletableFuture().get(); + System.out.printf("TraceProfileShowConfig result = "+ reply.toString()); + + System.out.println("Disconnecting..."); + } + } +} diff --git a/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/Readme.txt b/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/Readme.txt new file mode 100644 index 00000000..17e45a81 --- /dev/null +++ b/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/Readme.txt @@ -0,0 +1 @@ +sudo java -cp build-vpp-native/vpp-api/java/jvpp-registry-17.01.jar:build-vpp-native/plugins/ioam-plugin/jvpp-ioam-trace-1.0.jar io.fd.vpp.jvpp.ioamtrace.test.IoamTraceApiTest diff --git a/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.c b/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.c new file mode 100644 index 00000000..0bf17889 --- /dev/null +++ b/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.c @@ -0,0 +1,124 @@ +/* + * 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 + +#include +#define vl_typedefs /* define message structures */ +#include +#undef vl_typedefs + +#define vl_endianfun +#include +#undef vl_endianfun + +#define vl_print(handle, ...) +#define vl_printfun +#include +#undef vl_printfun + +/* Get the API version number */ +#define vl_api_version(n,v) static u32 api_version=(v); +#include +#undef vl_api_version + +#include +#include +#include + +#if VPPJNI_DEBUG == 1 + #define DEBUG_LOG(...) clib_warning(__VA_ARGS__) +#else + #define DEBUG_LOG(...) +#endif + +#include + +#include "jvpp-ioamtrace/io_fd_vpp_jvpp_ioamtrace_JVppIoamtraceImpl.h" +#include "jvpp_ioam_trace.h" +#include "jvpp-ioamtrace/jvpp_ioamtrace_gen.h" + +/* + * Class: io_fd_vpp_jvpp_ioamtrace_JVppIoamtraceImpl + * Method: init0 + * Signature: (JI)V + */ +JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioamtrace_JVppIoamtraceImpl_init0 + (JNIEnv *env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) { + ioamtrace_main_t * plugin_main = &ioamtrace_main; + u8 * name; + clib_warning ("Java_io_fd_vpp_jvpp_ioamtrace_JVppIoamtraceImpl_init0"); + + plugin_main->my_client_index = my_client_index; + plugin_main->vl_input_queue = (unix_shared_memory_queue_t *)queue_address; + + name = format (0, "ioam_trace_%08x%c", api_version, 0); + plugin_main->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name); + + if (plugin_main->msg_id_base == (u16) ~0) { + jclass exClass = (*env)->FindClass(env, "java/lang/IllegalStateException"); + (*env)->ThrowNew(env, exClass, "ioam_trace plugin is not loaded in VPP"); + } else { + plugin_main->callbackObject = (*env)->NewGlobalRef(env, callback); + plugin_main->callbackClass = (jclass)(*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, callback)); + + #define _(N,n) \ + vl_msg_api_set_handlers(VL_API_##N + plugin_main->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_api_reply_handler; + #undef _ + } +} + +JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioamtrace_JVppIoamtraceImpl_close0 +(JNIEnv *env, jclass clazz) { + ioamtrace_main_t * plugin_main = &ioamtrace_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 iOAM Trace */ +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 iOAM Trace */ +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); +} diff --git a/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.h b/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.h new file mode 100644 index 00000000..9fc16c15 --- /dev/null +++ b/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.h @@ -0,0 +1,45 @@ +/* + * 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_ioam_trace_h__ +#define __included_jvpp_ioam_trace_h__ + +#include +#include +#include +#include +#include +#include + +/* Global state for JVPP-IOAM-TRACE */ +typedef struct { + /* Base message index for the trace plugin */ + u16 msg_id_base; + + /* 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; + +} ioamtrace_main_t; + +ioamtrace_main_t ioamtrace_main __attribute__((aligned (64))); + + +#endif /* __included_jvpp_ioam_trace_h__ */ diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/JVpp.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/JVpp.java new file mode 100644 index 00000000..55f25a7b --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/JVpp.java @@ -0,0 +1,56 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp; + +import io.fd.vpp.jvpp.callback.JVppCallback; +import io.fd.vpp.jvpp.dto.ControlPing; +import io.fd.vpp.jvpp.dto.JVppRequest; + +/** + * Base interface for plugin's Java API. + */ +public interface JVpp extends AutoCloseable { + + /** + * Sends request to vpp. + * + * @param request request to be sent + * @return unique identifer of message in message queue + * @throws VppInvocationException when message could not be sent + */ + int send(final JVppRequest request) throws VppInvocationException; + + /** + * Initializes plugin's Java API. + * + * @param registry plugin registry + * @param callback called by vpe.api message handlers + * @param queueAddress address of vpp shared memory queue + * @param clientIndex vpp client identifier + */ + void init(final JVppRegistry registry, final JVppCallback callback, final long queueAddress, + final int clientIndex); + + /** + * Sends control_ping message. + * + * @param controlPing request DTO + * @return unique identifer of message in message queue + * @throws VppInvocationException when message could not be sent + */ + int controlPing(final ControlPing controlPing) throws VppInvocationException; +} diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/JVppRegistry.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/JVppRegistry.java new file mode 100644 index 00000000..6535db02 --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/JVppRegistry.java @@ -0,0 +1,76 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp; + +import io.fd.vpp.jvpp.callback.JVppCallback; + +/** + * Manages VPP connection and stores plugin callbacks. + */ +public interface JVppRegistry extends AutoCloseable { + + /** + * Vpp connection managed by the registry. + * + * @return representation of vpp connection + */ + VppConnection getConnection(); + + /** + * Registers callback and initializes Java API for given plugin. + * + * @param jvpp plugin name + * @param callback callback provided by the plugin + * @throws NullPointerException if name or callback is null + * @throws IllegalArgumentException if plugin was already registered + */ + void register(final JVpp jvpp, final JVppCallback callback); + + /** + * Unregisters callback for the given plugin. + * + * @param name plugin name + * @throws NullPointerException if name is null + * @throws IllegalArgumentException if plugin was not registered + */ + void unregister(final String name); + + /** + * Returns callback registered for the plugin. + * + * @param name plugin name + * @return callback provided by the plugin + * @throws NullPointerException if name is null + * @throws IllegalArgumentException if plugin was not registered + */ + JVppCallback get(final String name); + + /** + * Sends control ping. Reply handler calls callback registered for give plugin. + * + * Control ping is used for initial RX thread to Java thread attachment + * that takes place in the plugin's JNI lib + * and to wrap dump message replies in one list. + * + * VPP plugins don't have to provide special control ping, therefore + * it is necessary to providing control ping support in JVppRegistry. + + * @param clazz identifies plugin that should receive ping callback + * @return unique identifier of message in message queue + */ + int controlPing(final Class clazz) throws VppInvocationException; +} diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/JVppRegistryImpl.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/JVppRegistryImpl.java new file mode 100644 index 00000000..98ef1c15 --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/JVppRegistryImpl.java @@ -0,0 +1,147 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp; + +import static java.util.Objects.requireNonNull; + +import io.fd.vpp.jvpp.callback.ControlPingCallback; +import io.fd.vpp.jvpp.callback.JVppCallback; +import io.fd.vpp.jvpp.dto.ControlPingReply; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Default implementation of JVppRegistry. + */ +public final class JVppRegistryImpl implements JVppRegistry, ControlPingCallback { + + private static final Logger LOG = Logger.getLogger(JVppRegistryImpl.class.getName()); + + private final VppJNIConnection connection; + // Unguarded concurrent map, no race conditions expected on top of that + private final Map pluginRegistry; + // Guarded by self + private final Map pingCalls; + + public JVppRegistryImpl(final String clientName) throws IOException { + connection = new VppJNIConnection(clientName); + connection.connect(); + pluginRegistry = new ConcurrentHashMap<>(); + pingCalls = new HashMap<>(); + } + + @Override + public VppConnection getConnection() { + return connection; + } + + @Override + public void register(final JVpp jvpp, final JVppCallback callback) { + requireNonNull(jvpp, "jvpp should not be null"); + requireNonNull(callback, "Callback should not be null"); + final String name = jvpp.getClass().getName(); + if (pluginRegistry.containsKey(name)) { + throw new IllegalArgumentException( + String.format("Callback for plugin %s was already registered", name)); + } + jvpp.init(this, callback, connection.getConnectionInfo().queueAddress, + connection.getConnectionInfo().clientIndex); + pluginRegistry.put(name, callback); + } + + @Override + public void unregister(final String name) { + requireNonNull(name, "Plugin name should not be null"); + final JVppCallback previous = pluginRegistry.remove(name); + assertPluginWasRegistered(name, previous); + } + + @Override + public JVppCallback get(final String name) { + requireNonNull(name, "Plugin name should not be null"); + JVppCallback value = pluginRegistry.get(name); + assertPluginWasRegistered(name, value); + return value; + } + + private native int controlPing0() throws VppInvocationException; + + @Override + public int controlPing(final Class clazz) throws VppInvocationException { + connection.checkActive(); + final String name = clazz.getName(); + + final ControlPingCallback callback = (ControlPingCallback) pluginRegistry.get(clazz.getName()); + assertPluginWasRegistered(name, callback); + + synchronized (pingCalls) { + int context = controlPing0(); + if (context < 0) { + throw new VppInvocationException("controlPing", context); + } + + pingCalls.put(context, callback); + return context; + } + } + + @Override + public void onControlPingReply(final ControlPingReply reply) { + final ControlPingCallback callback; + synchronized (pingCalls) { + callback = pingCalls.remove(reply.context); + if (callback == null) { + LOG.log(Level.WARNING, "No callback was registered for reply context=" + reply.context + " Contexts waiting=" + + pingCalls.keySet()); + return; + } + } + // pass the reply to the callback registered by the ping caller + callback.onControlPingReply(reply); + } + + @Override + public void onError(final VppCallbackException ex) { + final int ctxId = ex.getCtxId(); + final ControlPingCallback callback; + + synchronized (pingCalls) { + callback = pingCalls.get(ctxId); + } + if (callback == null) { + LOG.log(Level.WARNING, "No callback was registered for reply id={0} ", ctxId); + return; + } + // pass the error to the callback registered by the ping caller + callback.onError(ex); + } + + private static void assertPluginWasRegistered(final String name, final JVppCallback value) { + if (value == null) { + throw new IllegalArgumentException(String.format("Callback for plugin %s is not registered", name)); + } + } + + @Override + public void close() throws Exception { + connection.close(); + } +} diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/NativeLibraryLoader.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/NativeLibraryLoader.java new file mode 100644 index 00000000..ce6d1bfc --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/NativeLibraryLoader.java @@ -0,0 +1,73 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Utility class for loading JNI libraries. + */ +public final class NativeLibraryLoader { + + private static final Logger LOG = Logger.getLogger(NativeLibraryLoader.class.getName()); + + private NativeLibraryLoader() { + throw new UnsupportedOperationException("This utility class cannot be instantiated."); + } + + /** + * Loads JNI library using class loader of the given class. + * + * @param libName name of the library to be loaded + */ + public static void loadLibrary(final String libName, final Class clazz) throws IOException { + java.util.Objects.requireNonNull(libName, "libName should not be null"); + java.util.Objects.requireNonNull(clazz, "clazz should not be null"); + try (final InputStream is = clazz.getResourceAsStream('/' + libName)) { + if (is == null) { + throw new IOException("Failed to open library resource " + libName); + } + loadStream(libName, is); + } + } + + private static void loadStream(final String libName, final InputStream is) throws IOException { + final Set perms = PosixFilePermissions.fromString("rwxr-x---"); + final Path p = Files.createTempFile(libName, null, PosixFilePermissions.asFileAttribute(perms)); + try { + Files.copy(is, p, StandardCopyOption.REPLACE_EXISTING); + Runtime.getRuntime().load(p.toString()); + } catch (Exception e) { + throw new IOException("Failed to load library " + p, e); + } finally { + try { + Files.deleteIfExists(p); + } catch (IOException e) { + LOG.log(Level.WARNING, String.format("Failed to delete temporary file %s.", p), e); + } + } + } +} diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppBaseCallException.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppBaseCallException.java new file mode 100644 index 00000000..d71e3055 --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppBaseCallException.java @@ -0,0 +1,60 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp; + +/** + * Base exception representing failed operation of JVpp request call + */ +public abstract class VppBaseCallException extends Exception { + private final String methodName; + private final int errorCode; + + /** + * Constructs an VppCallbackException with the specified api method name and error code. + * + * @param methodName name of a method, which invocation or execution failed + * @param errorCode negative error code value associated with this failure + * @throws NullPointerException if apiMethodName is null + */ + public VppBaseCallException(final String methodName, final int errorCode) { + super(String.format("vppApi.%s failed with error code: %d", methodName, errorCode)); + this.methodName = java.util.Objects.requireNonNull(methodName, "apiMethodName is null!"); + this.errorCode = errorCode; + if(errorCode >= 0) { + throw new IllegalArgumentException("Error code must be < 0. Was " + errorCode + + " for " + methodName ); + } + } + + /** + * Returns name of a method, which invocation failed. + * + * @return method name + */ + public String getMethodName() { + return methodName; + } + + /** + * Returns the error code associated with this failure. + * + * @return a negative integer error code + */ + public int getErrorCode() { + return errorCode; + } +} diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppCallbackException.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppCallbackException.java new file mode 100644 index 00000000..ccfcbd3c --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppCallbackException.java @@ -0,0 +1,47 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp; + +/** + * Callback Exception representing failed operation of JVpp request call + */ +public class VppCallbackException extends VppBaseCallException { + private final int ctxId; + + /** + * Constructs an VppCallbackException with the specified api method name and error code. + * + * @param methodName name of a method, which invocation failed. + * @param ctxId api request context identifier + * @param errorCode negative error code value associated with this failure + * @throws NullPointerException if apiMethodName is null + */ + public VppCallbackException(final String methodName, final int ctxId, final int errorCode ){ + super(methodName, errorCode); + this.ctxId = ctxId; + } + + /** + * Returns api request context identifier. + * + * @return value of context identifier + */ + public int getCtxId() { + return ctxId; + } + +} diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppConnection.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppConnection.java new file mode 100644 index 00000000..e6fd3bdb --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppConnection.java @@ -0,0 +1,45 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp; + +import java.io.IOException; + +/** + * Representation of a management connection to VPP. + */ +public interface VppConnection extends AutoCloseable { + + /** + * Opens VppConnection for communication with VPP. + * + * @throws IOException if connection is not established + */ + void connect() throws IOException; + + /** + * Checks if this instance connection is active. + * + * @throws IllegalStateException if this instance was disconnected. + */ + void checkActive(); + + /** + * Closes Vpp connection. + */ + @Override + void close(); +} diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppInvocationException.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppInvocationException.java new file mode 100644 index 00000000..a7ccb197 --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppInvocationException.java @@ -0,0 +1,33 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp; + +/** + * Exception thrown when Vpp jAPI method invocation failed. + */ +public class VppInvocationException extends VppBaseCallException { + /** + * Constructs an VppApiInvocationFailedException with the specified api method name and error code. + * + * @param methodName name of a method, which invocation failed. + * @param errorCode negative error code value associated with this failure + * @throws NullPointerException if apiMethodName is null + */ + public VppInvocationException(final String methodName, final int errorCode) { + super(methodName, errorCode); + } +} diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppJNIConnection.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppJNIConnection.java new file mode 100644 index 00000000..320c1283 --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppJNIConnection.java @@ -0,0 +1,137 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp; + +import static io.fd.vpp.jvpp.NativeLibraryLoader.loadLibrary; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * JNI based representation of a management connection to VPP. + */ +public final class VppJNIConnection implements VppConnection { + private static final Logger LOG = Logger.getLogger(VppJNIConnection.class.getName()); + + static { + final String libName = "libjvpp_registry.so"; + try { + loadLibrary(libName, VppJNIConnection.class); + } catch (IOException e) { + LOG.log(Level.SEVERE, String.format("Can't find vpp jni library: %s", libName), e); + throw new ExceptionInInitializerError(e); + } + } + + private ConnectionInfo connectionInfo; + + private final String clientName; + private volatile boolean disconnected = false; + + /** + * Create VPPJNIConnection instance for client connecting to VPP. + * + * @param clientName client name instance to be used for communication. Single connection per clientName is + * allowed. + */ + public VppJNIConnection(final String clientName) { + this.clientName = Objects.requireNonNull(clientName, "Null clientName"); + } + + /** + * Guarded by VppJNIConnection.class + */ + private static final Map connections = new HashMap<>(); + + /** + * Initiate VPP connection for current instance + * + * Multiple instances are allowed since this class is not a singleton (VPP allows multiple management connections). + * + * However only a single connection per clientName is allowed. + * + * @throws IOException in case the connection could not be established + */ + + @Override + public void connect() throws IOException { + _connect(); + } + + private void _connect() throws IOException { + synchronized (VppJNIConnection.class) { + if (connections.containsKey(clientName)) { + throw new IOException("Client " + clientName + " already connected"); + } + + connectionInfo = clientConnect(clientName); + if (connectionInfo.status != 0) { + throw new IOException("Connection returned error " + connectionInfo.status); + } + connections.put(clientName, this); + } + } + + @Override + public final void checkActive() { + if (disconnected) { + throw new IllegalStateException("Disconnected client " + clientName); + } + } + + @Override + public final synchronized void close() { + if (!disconnected) { + disconnected = true; + try { + clientDisconnect(); + } finally { + synchronized (VppJNIConnection.class) { + connections.remove(clientName); + } + } + } + } + + public ConnectionInfo getConnectionInfo() { + return connectionInfo; + } + + /** + * VPP connection information used by plugins to reuse the connection. + */ + public static final class ConnectionInfo { + public final long queueAddress; + public final int clientIndex; + public final int status; // FIXME throw exception instead + + public ConnectionInfo(long queueAddress, int clientIndex, int status) { + this.queueAddress = queueAddress; + this.clientIndex = clientIndex; + this.status = status; + } + } + + private static native ConnectionInfo clientConnect(String clientName); + + private static native void clientDisconnect(); + +} diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/callback/ControlPingCallback.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/callback/ControlPingCallback.java new file mode 100644 index 00000000..efddfdbb --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/callback/ControlPingCallback.java @@ -0,0 +1,29 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.callback; + +import io.fd.vpp.jvpp.dto.ControlPingReply; + +/** + * Represents callback for control_ping message. + */ +public interface ControlPingCallback extends JVppCallback { + + void onControlPingReply(ControlPingReply reply); + +} + diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/callback/JVppCallback.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/callback/JVppCallback.java new file mode 100644 index 00000000..ae02063b --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/callback/JVppCallback.java @@ -0,0 +1,29 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.callback; +import io.fd.vpp.jvpp.VppCallbackException; + +/** + * Base JVppCallback interface + */ +public interface JVppCallback { + /** + * onError callback handler used to report failing operation + * @param ex VppCallbackException object containing details about failing operation + */ + void onError(VppCallbackException ex); +} diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/callback/JVppNotificationCallback.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/callback/JVppNotificationCallback.java new file mode 100644 index 00000000..8ab0cb21 --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/callback/JVppNotificationCallback.java @@ -0,0 +1,24 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.callback; + +/** +* Notification callback +*/ +public interface JVppNotificationCallback { + +} diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/ControlPing.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/ControlPing.java new file mode 100644 index 00000000..984e1674 --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/ControlPing.java @@ -0,0 +1,34 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.dto; + +import io.fd.vpp.jvpp.JVpp; +import io.fd.vpp.jvpp.VppInvocationException; + +/** + * Represents request DTO for control_ping message. + */ +public final class ControlPing implements JVppRequest { + + @Override + public int send(final JVpp jvpp) throws VppInvocationException { + return jvpp.controlPing(this); + } + +} + + diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/ControlPingReply.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/ControlPingReply.java new file mode 100644 index 00000000..61e4d0e4 --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/ControlPingReply.java @@ -0,0 +1,58 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.dto; + +import java.util.Objects; + +/** + * Represents reply DTO for control_ping message. + */ +public final class ControlPingReply implements JVppReply { + + public int context; + public int clientIndex; + public int vpePid; + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final ControlPingReply that = (ControlPingReply) o; + return context == that.context && + clientIndex == that.clientIndex && + vpePid == that.vpePid; + } + + @Override + public int hashCode() { + return Objects.hash(context, clientIndex, vpePid); + } + + @Override + public String toString() { + return "ControlPingReply{" + + "context=" + context + + ", clientIndex=" + clientIndex + + ", vpePid=" + vpePid + + '}'; + } +} + diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppDump.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppDump.java new file mode 100644 index 00000000..60b98984 --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppDump.java @@ -0,0 +1,24 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.dto; + +/** +* Base interface for all dump requests +*/ +public interface JVppDump extends JVppRequest { + +} diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppNotification.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppNotification.java new file mode 100644 index 00000000..5554f501 --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppNotification.java @@ -0,0 +1,23 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.dto; + +/** +* Base interface for all notification DTOs +*/ +public interface JVppNotification { +} diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppReply.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppReply.java new file mode 100644 index 00000000..73f512d4 --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppReply.java @@ -0,0 +1,24 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.dto; + +/** +* Base interface for all reply DTOs +*/ +public interface JVppReply { + +} diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppReplyDump.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppReplyDump.java new file mode 100644 index 00000000..15111395 --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppReplyDump.java @@ -0,0 +1,25 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.dto; + +/** +* Base interface for all dump replies +*/ +public interface JVppReplyDump> + extends JVppReply { + +} diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppRequest.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppRequest.java new file mode 100644 index 00000000..9b301da2 --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/dto/JVppRequest.java @@ -0,0 +1,34 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.dto; + +import io.fd.vpp.jvpp.JVpp; +import io.fd.vpp.jvpp.VppInvocationException; + +/** +* Base interface for all request DTOs +*/ +public interface JVppRequest { + + /** + * Invoke current operation asynchronously on VPP + * + * @return context id of this request. Can be used to track incoming response + */ + int send(JVpp jvpp) throws VppInvocationException; + +} diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/future/AbstractFutureJVppInvoker.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/future/AbstractFutureJVppInvoker.java new file mode 100644 index 00000000..e7df528a --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/future/AbstractFutureJVppInvoker.java @@ -0,0 +1,141 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.future; + + +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import io.fd.vpp.jvpp.JVpp; +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.VppInvocationException; +import io.fd.vpp.jvpp.dto.JVppDump; +import io.fd.vpp.jvpp.dto.JVppReply; +import io.fd.vpp.jvpp.dto.JVppReplyDump; +import io.fd.vpp.jvpp.dto.JVppRequest; + +/** + * Future facade on top of JVpp + */ +public abstract class AbstractFutureJVppInvoker implements FutureJVppInvoker { + + private final JVpp jvpp; + private final JVppRegistry registry; + + /** + * Guarded by self + */ + private final Map>> requests; + + protected AbstractFutureJVppInvoker(final JVpp jvpp, final JVppRegistry registry, + final Map>> requestMap) { + this.jvpp = Objects.requireNonNull(jvpp, "jvpp should not be null"); + this.registry = Objects.requireNonNull(registry, "registry should not be null"); + // Request map represents the shared state between this facade and it's callback + // where facade puts futures in and callback completes + removes them + this.requests = Objects.requireNonNull(requestMap, "Null requestMap"); + } + + protected final Map>> getRequests() { + synchronized (requests) { + return requests; + } + } + + // TODO use Optional in Future, java8 + + @Override + @SuppressWarnings("unchecked") + public > CompletionStage send(REQ req) { + synchronized(requests) { + try { + final CompletableFuture replyCompletableFuture; + final int contextId = jvpp.send(req); + + if(req instanceof JVppDump) { + throw new IllegalArgumentException("Send with empty reply dump has to be used in case of dump calls"); + } + replyCompletableFuture = new CompletableFuture<>(); + requests.put(contextId, replyCompletableFuture); + + // TODO in case of timeouts/missing replies, requests from the map are not removed + // consider adding cancel method, that would remove requests from the map and cancel + // associated replyCompletableFuture + + return replyCompletableFuture; + } catch (VppInvocationException ex) { + final CompletableFuture replyCompletableFuture = new CompletableFuture<>(); + replyCompletableFuture.completeExceptionally(ex); + return replyCompletableFuture; + } + } + } + + @Override + @SuppressWarnings("unchecked") + public , DUMP extends JVppReplyDump> CompletionStage send( + REQ req, DUMP emptyReplyDump) { + synchronized(requests) { + try { + final CompletableDumpFuture replyCompletableFuture; + final int contextId = jvpp.send(req); + + if(!(req instanceof JVppDump)) { + throw new IllegalArgumentException("Send without empty reply dump has to be used in case of regular calls"); + } + replyCompletableFuture = new CompletableDumpFuture<>(contextId, emptyReplyDump); + + requests.put(contextId, replyCompletableFuture); + requests.put(registry.controlPing(jvpp.getClass()), replyCompletableFuture); + + // TODO in case of timeouts/missing replies, requests from the map are not removed + // consider adding cancel method, that would remove requests from the map and cancel + // associated replyCompletableFuture + + return replyCompletableFuture; + } catch (VppInvocationException ex) { + final CompletableFuture replyCompletableFuture = new CompletableFuture<>(); + replyCompletableFuture.completeExceptionally(ex); + return replyCompletableFuture; + } + } + } + + public static final class CompletableDumpFuture> extends CompletableFuture { + private final T replyDump; + private final int contextId; + + public CompletableDumpFuture(final int contextId, final T emptyDump) { + this.contextId = contextId; + this.replyDump = emptyDump; + } + + public int getContextId() { + return contextId; + } + + public T getReplyDump() { + return replyDump; + } + } + + @Override + public void close() throws Exception { + jvpp.close(); + } +} diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/future/FutureJVppInvoker.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/future/FutureJVppInvoker.java new file mode 100644 index 00000000..7a48e418 --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/future/FutureJVppInvoker.java @@ -0,0 +1,49 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.future; + + +import io.fd.vpp.jvpp.dto.JVppReply; +import io.fd.vpp.jvpp.dto.JVppReplyDump; +import io.fd.vpp.jvpp.dto.JVppRequest; + +import java.util.concurrent.CompletionStage; +import io.fd.vpp.jvpp.notification.NotificationRegistryProvider; + +/** +* Future facade on top of JVpp +*/ +public interface FutureJVppInvoker extends NotificationRegistryProvider, AutoCloseable { + + /** + * Invoke asynchronous operation on VPP + * + * @return CompletionStage with future result of an async VPP call + * @throws io.fd.vpp.jvpp.VppInvocationException when send request failed with details + */ + > CompletionStage send(REQ req); + + + /** + * Invoke asynchronous dump operation on VPP + * + * @return CompletionStage with aggregated future result of an async VPP dump call + * @throws io.fd.vpp.jvpp.VppInvocationException when send request failed with details + */ + , DUMP extends JVppReplyDump> CompletionStage send( + REQ req, DUMP emptyReplyDump); +} diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/notification/NotificationRegistry.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/notification/NotificationRegistry.java new file mode 100644 index 00000000..3c72ff79 --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/notification/NotificationRegistry.java @@ -0,0 +1,25 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.notification; + +/** + * Base registry for notification callbacks. + */ +public interface NotificationRegistry extends AutoCloseable { + + void close(); +} diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/notification/NotificationRegistryProvider.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/notification/NotificationRegistryProvider.java new file mode 100644 index 00000000..4a6e06b7 --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/notification/NotificationRegistryProvider.java @@ -0,0 +1,28 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.notification; + +/** + * Provides notification registry + */ +public interface NotificationRegistryProvider { + + /** + * Get current notification registry instance + */ + NotificationRegistry getNotificationRegistry(); +} diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/test/ConnectionTest.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/test/ConnectionTest.java new file mode 100644 index 00000000..27b4d29f --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/test/ConnectionTest.java @@ -0,0 +1,44 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.test; + +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; + +/** + * Run using: + * sudo java -cp build-vpp-native/vpp-api/java/jvpp-registry-16.09.jar io.fd.vpp.jvpp.test.ConnectionTest + */ +public class ConnectionTest { + + private static void testConnect() throws Exception { + System.out.println("Testing JNI connection with JVppRegistry"); + final JVppRegistry registry = new JVppRegistryImpl("ConnectionTest"); + try { + System.out.println("Successfully connected to vpp"); + Thread.sleep(5000); + System.out.println("Disconnecting..."); + Thread.sleep(1000); + } finally { + registry.close(); + } + } + + public static void main(String[] args) throws Exception { + testConnect(); + } +} diff --git a/src/vpp-api/java/jvpp-registry/jvpp_registry.c b/src/vpp-api/java/jvpp-registry/jvpp_registry.c new file mode 100644 index 00000000..cbd5e0ab --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/jvpp_registry.c @@ -0,0 +1,352 @@ +/* + * 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. + */ +#define _GNU_SOURCE /* for strcasestr(3) */ +#include + +#define vl_api_version(n,v) static u32 vpe_api_version = (v); +#include +#undef vl_api_version + + +#include +#include +#include "io_fd_vpp_jvpp_VppJNIConnection.h" +#include "io_fd_vpp_jvpp_JVppRegistryImpl.h" + +#include +#define vl_typedefs /* define message structures */ +#include +#undef vl_typedefs + +#define vl_endianfun +#include +#undef vl_endianfun + +/* instantiate all the print functions we know about */ +#define vl_print(handle, ...) +#define vl_printfun +#include +#undef vl_printfun + +vlib_main_t vlib_global_main; +vlib_main_t **vlib_mains; + +/* + * The Java runtime isn't compile w/ -fstack-protector, + * so we have to supply missing external references for the + * regular vpp libraries. + */ +void __stack_chk_guard(void) __attribute__((weak)); +void __stack_chk_guard(void) { +} + +typedef struct { + /* UThread attachment */ + volatile u32 control_ping_result_ready; + volatile i32 control_ping_retval; + + /* Control poing callback */ + jobject registryObject; + jclass registryClass; + jclass controlPingReplyClass; + jclass callbackExceptionClass; + + /* Thread cleanup */ + pthread_key_t cleanup_rx_thread_key; + + /* Connected indication */ + volatile u8 is_connected; +} jvpp_registry_main_t; + +jvpp_registry_main_t jvpp_registry_main __attribute__((aligned (64))); + +void vl_client_add_api_signatures(vl_api_memclnt_create_t *mp) { + /* + * Send the main API signature in slot 0. This bit of code must + * match the checks in ../vpe/api/api.c: vl_msg_api_version_check(). + */ + mp->api_versions[0] = clib_host_to_net_u32(vpe_api_version); +} + +/* cleanup handler for RX thread */ +static_always_inline void cleanup_rx_thread(void *arg) { + jvpp_main_t * jm = &jvpp_main; + jvpp_registry_main_t * rm = &jvpp_registry_main; + + vppjni_lock(jm, 99); + + int getEnvStat = (*jm->jvm)->GetEnv(jm->jvm, (void **) &(jm->jenv), + JNI_VERSION_1_8); + if (getEnvStat == JNI_EVERSION) { + clib_warning("Unsupported JNI version\n"); + rm->control_ping_retval = VNET_API_ERROR_UNSUPPORTED_JNI_VERSION; + goto out; + } else if (getEnvStat != JNI_EDETACHED) { + (*jm->jvm)->DetachCurrentThread(jm->jvm); + } + out: vppjni_unlock(jm); +} + +static void vl_api_control_ping_reply_t_handler( + vl_api_control_ping_reply_t * mp) { + jvpp_main_t * jm = &jvpp_main; + jvpp_registry_main_t * rm = &jvpp_registry_main; + char was_thread_connected = 0; + + // attach to java thread if not attached + int getEnvStat = (*jm->jvm)->GetEnv(jm->jvm, (void **) &(jm->jenv), + JNI_VERSION_1_8); + if (getEnvStat == JNI_EDETACHED) { + if ((*jm->jvm)->AttachCurrentThread(jm->jvm, (void **) &(jm->jenv), + NULL) != 0) { + clib_warning("Failed to attach thread\n"); + rm->control_ping_retval = + VNET_API_ERROR_FAILED_TO_ATTACH_TO_JAVA_THREAD; + goto out; + } + + // workaround as we can't use pthread_cleanup_push + pthread_key_create(&rm->cleanup_rx_thread_key, cleanup_rx_thread); + // destructor is only called if the value of key is non null + pthread_setspecific(rm->cleanup_rx_thread_key, (void *) 1); + was_thread_connected = 1; + } else if (getEnvStat == JNI_EVERSION) { + clib_warning("Unsupported JNI version\n"); + rm->control_ping_retval = VNET_API_ERROR_UNSUPPORTED_JNI_VERSION; + goto out; + } + + if (was_thread_connected == 0) { + JNIEnv *env = jm->jenv; + if (mp->retval < 0) { + call_on_error("controlPing", mp->context, mp->retval, + rm->registryClass, rm->registryObject, + rm->callbackExceptionClass); + } else { + jmethodID constructor = (*env)->GetMethodID(env, + rm->controlPingReplyClass, "", "()V"); + jmethodID callbackMethod = (*env)->GetMethodID(env, + rm->registryClass, "onControlPingReply", + "(Lio/fd/vpp/jvpp/dto/ControlPingReply;)V"); + + jobject dto = (*env)->NewObject(env, rm->controlPingReplyClass, + constructor); + + jfieldID contextFieldId = (*env)->GetFieldID(env, + rm->controlPingReplyClass, "context", "I"); + (*env)->SetIntField(env, dto, contextFieldId, + clib_net_to_host_u32(mp->context)); + + jfieldID clientIndexFieldId = (*env)->GetFieldID(env, + rm->controlPingReplyClass, "clientIndex", "I"); + (*env)->SetIntField(env, dto, clientIndexFieldId, + clib_net_to_host_u32(mp->client_index)); + + jfieldID vpePidFieldId = (*env)->GetFieldID(env, + rm->controlPingReplyClass, "vpePid", "I"); + (*env)->SetIntField(env, dto, vpePidFieldId, + clib_net_to_host_u32(mp->vpe_pid)); + + (*env)->CallVoidMethod(env, rm->registryObject, callbackMethod, + dto); + (*env)->DeleteLocalRef(env, dto); + } + } + + out: rm->control_ping_result_ready = 1; +} + +static int send_initial_control_ping() { + f64 timeout; + clib_time_t clib_time; + vl_api_control_ping_t * mp; + jvpp_main_t * jm = &jvpp_main; + jvpp_registry_main_t * rm = &jvpp_registry_main; + + clib_time_init(&clib_time); + + rm->control_ping_result_ready = 0; + mp = vl_msg_api_alloc(sizeof(*mp)); + memset(mp, 0, sizeof(*mp)); + mp->_vl_msg_id = ntohs(VL_API_CONTROL_PING); + mp->client_index = jm->my_client_index; + + // send message: + vl_msg_api_send_shmem(jm->vl_input_queue, (u8 *) &mp); + + // wait for results: Current time + 10 seconds is the timeout + timeout = clib_time_now(&clib_time) + 10.0; + int rv = VNET_API_ERROR_RESPONSE_NOT_READY; + while (clib_time_now(&clib_time) < timeout) { + if (rm->control_ping_result_ready == 1) { + rv = rm->control_ping_retval; + break; + } + } + + if (rv != 0) { + clib_warning("common: first control ping failed: %d", rv); + } + + return rv; +} + +static int connect_to_vpe(char *name) { + jvpp_main_t * jm = &jvpp_main; + api_main_t * am = &api_main; + + if (vl_client_connect_to_vlib("/vpe-api", name, 32) < 0) + return -1; + + jm->my_client_index = am->my_client_index; + + jm->vl_input_queue = am->shmem_hdr->vl_input_queue; + + vl_msg_api_set_handlers(VL_API_CONTROL_PING_REPLY, "control_ping_reply", + vl_api_control_ping_reply_t_handler, vl_noop_handler, + vl_api_control_ping_reply_t_endian, + vl_api_control_ping_reply_t_print, + sizeof(vl_api_control_ping_reply_t), 1); + + return send_initial_control_ping(); +} + +JNIEXPORT jobject JNICALL Java_io_fd_vpp_jvpp_VppJNIConnection_clientConnect( + JNIEnv *env, jclass obj, jstring clientName) { + int rv; + const char *client_name; + void vl_msg_reply_handler_hookup(void); + jvpp_main_t * jm = &jvpp_main; + jvpp_registry_main_t * rm = &jvpp_registry_main; + + jclass connectionInfoClass = (*env)->FindClass(env, + "io/fd/vpp/jvpp/VppJNIConnection$ConnectionInfo"); + jmethodID connectionInfoConstructor = (*env)->GetMethodID(env, + connectionInfoClass, "", "(JII)V"); + + /* + * Bail out now if we're not running as root + */ + if (geteuid() != 0) { + return (*env)->NewObject(env, connectionInfoClass, + connectionInfoConstructor, 0, 0, + VNET_API_ERROR_NOT_RUNNING_AS_ROOT); + } + + if (rm->is_connected) { + return (*env)->NewObject(env, connectionInfoClass, + connectionInfoConstructor, 0, 0, + VNET_API_ERROR_ALREADY_CONNECTED); + } + + client_name = (*env)->GetStringUTFChars(env, clientName, 0); + if (!client_name) { + return (*env)->NewObject(env, connectionInfoClass, + connectionInfoConstructor, 0, 0, VNET_API_ERROR_INVALID_VALUE); + } + + rv = connect_to_vpe((char *) client_name); + + if (rv < 0) + clib_warning("connection failed, rv %d", rv); + + (*env)->ReleaseStringUTFChars(env, clientName, client_name); + + return (*env)->NewObject(env, connectionInfoClass, + connectionInfoConstructor, (jlong) jm->vl_input_queue, + (jint) jm->my_client_index, (jint) rv); +} + +JNIEXPORT jint JNICALL Java_io_fd_vpp_jvpp_JVppRegistryImpl_controlPing0( + JNIEnv *env, jobject regstryObject) { + jvpp_main_t * jm = &jvpp_main; + vl_api_control_ping_t * mp; + u32 my_context_id = vppjni_get_context_id(&jvpp_main); + jvpp_registry_main_t * rm = &jvpp_registry_main; + + if (rm->registryObject == 0) { + rm->registryObject = (*env)->NewGlobalRef(env, regstryObject); + } + if (rm->registryClass == 0) { + rm->registryClass = (jclass) (*env)->NewGlobalRef(env, + (*env)->GetObjectClass(env, regstryObject)); + } + + mp = vl_msg_api_alloc(sizeof(*mp)); + memset(mp, 0, sizeof(*mp)); + mp->_vl_msg_id = ntohs(VL_API_CONTROL_PING); + mp->client_index = jm->my_client_index; + mp->context = clib_host_to_net_u32(my_context_id); + + // send message: + vl_msg_api_send_shmem(jm->vl_input_queue, (u8 *) &mp); + return my_context_id; +} + +JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_VppJNIConnection_clientDisconnect( + JNIEnv *env, jclass clazz) { + jvpp_registry_main_t * rm = &jvpp_registry_main; + rm->is_connected = 0; // TODO make thread safe + vl_client_disconnect_from_vlib(); + + // cleanup: + if (rm->registryObject) { + (*env)->DeleteGlobalRef(env, rm->registryObject); + rm->registryObject = 0; + } + if (rm->registryClass) { + (*env)->DeleteGlobalRef(env, rm->registryClass); + rm->registryClass = 0; + } +} + +jint JNI_OnLoad(JavaVM *vm, void *reserved) { + jvpp_main_t * jm = &jvpp_main; + jvpp_registry_main_t * rm = &jvpp_registry_main; + JNIEnv* env; + + if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) { + return JNI_EVERSION; + } + + rm->controlPingReplyClass = (jclass) (*env)->NewGlobalRef(env, + (*env)->FindClass(env, "io/fd/vpp/jvpp/dto/ControlPingReply")); + if ((*env)->ExceptionCheck(env)) { + (*env)->ExceptionDescribe(env); + clib_warning("Failed to cache class references\n"); + return JNI_ERR; + } + + rm->callbackExceptionClass = (jclass) (*env)->NewGlobalRef(env, + (*env)->FindClass(env, "io/fd/vpp/jvpp/VppCallbackException")); + if ((*env)->ExceptionCheck(env)) { + (*env)->ExceptionDescribe(env); + return JNI_ERR; + } + + jm->jvm = vm; + return JNI_VERSION_1_8; +} + +void JNI_OnUnload(JavaVM *vm, void *reserved) { + jvpp_main_t * jm = &jvpp_main; + JNIEnv* env; + if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_8) != JNI_OK) { + return; + } + + jm->jenv = NULL; + jm->jvm = NULL; +} diff --git a/src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/test/CallbackApiTest.java b/src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/test/CallbackApiTest.java new file mode 100644 index 00000000..32165d96 --- /dev/null +++ b/src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/test/CallbackApiTest.java @@ -0,0 +1,68 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.snat.test; + +import io.fd.vpp.jvpp.JVpp; +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.VppCallbackException; +import io.fd.vpp.jvpp.snat.JVppSnatImpl; +import io.fd.vpp.jvpp.snat.callback.SnatInterfaceAddDelFeatureCallback; +import io.fd.vpp.jvpp.snat.dto.SnatInterfaceAddDelFeature; +import io.fd.vpp.jvpp.snat.dto.SnatInterfaceAddDelFeatureReply; + +public class CallbackApiTest { + + static class TestCallback implements SnatInterfaceAddDelFeatureCallback { + + @Override + public void onSnatInterfaceAddDelFeatureReply(final SnatInterfaceAddDelFeatureReply msg) { + System.out.printf("Received SnatInterfaceAddDelFeatureReply: context=%d%n", + msg.context); + } + + @Override + public void onError(VppCallbackException ex) { + System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", ex.getMethodName(), + ex.getCtxId(), ex.getErrorCode()); + } + } + + public static void main(String[] args) throws Exception { + testCallbackApi(); + } + + private static void testCallbackApi() throws Exception { + System.out.println("Testing Java callback API for snat plugin"); + try (final JVppRegistry registry = new JVppRegistryImpl("SnatCallbackApiTest"); + final JVpp jvpp = new JVppSnatImpl()) { + registry.register(jvpp, new TestCallback()); + + System.out.println("Sending SnatInterfaceAddDelFeature request..."); + SnatInterfaceAddDelFeature request = new SnatInterfaceAddDelFeature(); + request.isAdd = 1; + request.isInside = 1; + request.swIfIndex = 1; + final int result = jvpp.send(request); + System.out.printf("SnatInterfaceAddDelFeature send result = %d%n", result); + + Thread.sleep(1000); + + System.out.println("Disconnecting..."); + } + } +} diff --git a/src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/test/Readme.txt b/src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/test/Readme.txt new file mode 100644 index 00000000..a2b0c41f --- /dev/null +++ b/src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/test/Readme.txt @@ -0,0 +1 @@ +sudo java -cp build-vpp-native/vpp-api/java/jvpp-registry-17.01.jar:build-vpp-native/plugins/snat-plugin/jvpp-snat-1.0.jar io.fd.vpp.jvpp.snat.test.CallbackApiTest diff --git a/src/vpp-api/java/jvpp-snat/jvpp_snat.c b/src/vpp-api/java/jvpp-snat/jvpp_snat.c new file mode 100644 index 00000000..1095b6eb --- /dev/null +++ b/src/vpp-api/java/jvpp-snat/jvpp_snat.c @@ -0,0 +1,124 @@ +/* + * 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 + +#include +#define vl_typedefs /* define message structures */ +#include +#undef vl_typedefs + +#define vl_endianfun +#include +#undef vl_endianfun + +#define vl_print(handle, ...) +#define vl_printfun +#include +#undef vl_printfun + +/* Get the API version number */ +#define vl_api_version(n,v) static u32 api_version=(v); +#include +#undef vl_api_version + +#include +#include +#include + +#if VPPJNI_DEBUG == 1 + #define DEBUG_LOG(...) clib_warning(__VA_ARGS__) +#else + #define DEBUG_LOG(...) +#endif + +#include + +#include "jvpp-snat/io_fd_vpp_jvpp_snat_JVppSnatImpl.h" +#include "jvpp_snat.h" +#include "jvpp-snat/jvpp_snat_gen.h" + +/* + * Class: io_fd_vpp_jvpp_snat_JVppsnatImpl + * Method: init0 + * Signature: (JI)V + */ +JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_snat_JVppSnatImpl_init0 + (JNIEnv *env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) { + snat_main_t * plugin_main = &snat_main; + u8 * name; + clib_warning ("Java_io_fd_vpp_jvpp_snat_JVppSnatImpl_init0"); + + plugin_main->my_client_index = my_client_index; + plugin_main->vl_input_queue = (unix_shared_memory_queue_t *)queue_address; + + name = format (0, "snat_%08x%c", api_version, 0); + plugin_main->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name); + + if (plugin_main->msg_id_base == (u16) ~0) { + jclass exClass = (*env)->FindClass(env, "java/lang/IllegalStateException"); + (*env)->ThrowNew(env, exClass, "snat plugin is not loaded in VPP"); + } else { + plugin_main->callbackObject = (*env)->NewGlobalRef(env, callback); + plugin_main->callbackClass = (jclass)(*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, callback)); + + #define _(N,n) \ + vl_msg_api_set_handlers(VL_API_##N + plugin_main->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_api_reply_handler; + #undef _ + } +} + +JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_snat_JVppSnatImpl_close0 +(JNIEnv *env, jclass clazz) { + snat_main_t * plugin_main = &snat_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 SNAT */ +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 SNAT */ +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); +} diff --git a/src/vpp-api/java/jvpp-snat/jvpp_snat.h b/src/vpp-api/java/jvpp-snat/jvpp_snat.h new file mode 100644 index 00000000..6426bda8 --- /dev/null +++ b/src/vpp-api/java/jvpp-snat/jvpp_snat.h @@ -0,0 +1,45 @@ +/* + * 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_snat_h__ +#define __included_jvpp_snat_h__ + +#include +#include +#include +#include +#include +#include + +/* Global state for JVPP-SNAT */ +typedef struct { + /* Base message index for the nsh plugin */ + u16 msg_id_base; + + /* 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; + +} snat_main_t; + +snat_main_t snat_main __attribute__((aligned (64))); + + +#endif /* __included_jvpp_snat_h__ */ diff --git a/src/vpp-api/java/jvpp/gen/jvpp_gen.py b/src/vpp-api/java/jvpp/gen/jvpp_gen.py new file mode 100755 index 00000000..f51b11d0 --- /dev/null +++ b/src/vpp-api/java/jvpp/gen/jvpp_gen.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python +# +# 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 +# l +# 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. +# + +import argparse +import importlib +import sys +import os +import json + +from jvppgen import types_gen +from jvppgen import callback_gen +from jvppgen import notification_gen +from jvppgen import dto_gen +from jvppgen import jvpp_callback_facade_gen +from jvppgen import jvpp_future_facade_gen +from jvppgen import jvpp_impl_gen +from jvppgen import jvpp_c_gen +from jvppgen import util + +blacklist = [ "memclnt.api", "flowperpkt.api" ] + +# Invocation: +# ~/Projects/vpp/vpp-api/jvpp/gen$ mkdir -p java/io/fd/vpp/jvpp && cd java/io/fd/vpp/jvpp +# ~/Projects/vpp/vpp-api/jvpp/gen/java/io/fd/vpp/jvpp$ ../../../../jvpp_gen.py -idefs_api_vpp_papi.py +# +# Compilation: +# ~/Projects/vpp/vpp-api/jvpp/gen/java/io/fd/vpp/jvpp$ javac *.java dto/*.java callback/*.java +# +# where +# defs_api_vpp_papi.py - vpe.api in python format (generated by vppapigen) + +parser = argparse.ArgumentParser(description='VPP Java API generator') +parser.add_argument('-i', action="store", dest="inputfiles", nargs='+') +parser.add_argument('--plugin_name', action="store", dest="plugin_name") +parser.add_argument('--root_dir', action="store", dest="root_dir") +args = parser.parse_args() + +sys.path.append(".") +cwd = os.getcwd() + +print "Generating Java API for %s" % args.inputfiles +print "inputfiles %s" % args.inputfiles +plugin_name = args.plugin_name +print "plugin_name %s" % plugin_name + +cfg = {} + +base_package = 'io.fd.vpp.jvpp' +plugin_package = base_package + '.' + plugin_name +root_dir = os.path.abspath(args.root_dir) +print "root_dir %s" % root_dir +work_dir = root_dir + "/target/" + plugin_package.replace(".","/") + +try: + os.makedirs(work_dir) +except OSError: + if not os.path.isdir(work_dir): + raise + +os.chdir(work_dir) + +for inputfile in args.inputfiles: + if any(substring in inputfile for substring in blacklist): + print "WARNING: Imput file %s blacklisted" % inputfile + continue + _cfg = json.load(open(cwd + "/" + inputfile, 'r')) + if 'types' in cfg: + cfg['types'].extend(_cfg['types']) + else: + cfg['types'] = _cfg['types'] + if 'messages' in cfg: + cfg['messages'].extend(_cfg['messages']) + else: + cfg['messages'] = _cfg['messages'] + + +def is_request_field(field_name): + return field_name not in {'_vl_msg_id', 'client_index', 'context'} + + +def is_response_field(field_name): + return field_name not in {'_vl_msg_id'} + + +def get_args(t, filter): + arg_list = [] + for i in t: + if is_crc(i): + continue + if not filter(i[1]): + continue + arg_list.append(i[1]) + return arg_list + + +def get_types(t, filter): + types_list = [] + lengths_list = [] + crc = None + for i in t: + if is_crc(i): + crc = ('crc', i['crc'][2:]) + continue + if not filter(i[1]): + continue + if len(i) is 3: # array type + types_list.append(i[0] + '[]') + lengths_list.append((i[2], False)) + elif len(i) is 4: # variable length array type + types_list.append(i[0] + '[]') + lengths_list.append((i[3], True)) + else: # primitive type + types_list.append(i[0]) + lengths_list.append((0, False)) + return types_list, lengths_list, crc + + +def is_crc(arg): + """ Check whether the argument inside message definition is just crc """ + return 'crc' in arg + + +def get_definitions(defs): + # Pass 1 + func_list = [] + func_name = {} + for a in defs: + java_name = util.underscore_to_camelcase(a[0]) + + # For replies include all the arguments except message_id + if util.is_reply(java_name): + types, lengths, crc = get_types(a[1:], is_response_field) + func_name[a[0]] = dict( + [('name', a[0]), ('java_name', java_name), + ('args', get_args(a[1:], is_response_field)), ('full_args', get_args(a[1:], lambda x: True)), + ('types', types), ('lengths', lengths), crc]) + # For requests skip message_id, client_id and context + else: + types, lengths, crc = get_types(a[1:], is_request_field) + func_name[a[0]] = dict( + [('name', a[0]), ('java_name', java_name), + ('args', get_args(a[1:], is_request_field)), ('full_args', get_args(a[1:], lambda x: True)), + ('types', types), ('lengths', lengths), crc]) + + # Indexed by name + func_list.append(func_name[a[0]]) + return func_list, func_name + + +types_package = 'types' +dto_package = 'dto' +callback_package = 'callback' +notification_package = 'notification' +future_package = 'future' +# TODO find better package name +callback_facade_package = 'callfacade' + +types_list, types_name = get_definitions(cfg['types']) + +types_gen.generate_types(types_list, plugin_package, types_package, args.inputfiles) + +func_list, func_name = get_definitions(cfg['messages']) + +dto_gen.generate_dtos(func_list, base_package, plugin_package, plugin_name.title(), dto_package, args.inputfiles) +jvpp_impl_gen.generate_jvpp(func_list, base_package, plugin_package, plugin_name, dto_package, args.inputfiles) +callback_gen.generate_callbacks(func_list, base_package, plugin_package, plugin_name.title(), callback_package, dto_package, args.inputfiles) +notification_gen.generate_notification_registry(func_list, base_package, plugin_package, plugin_name.title(), notification_package, callback_package, dto_package, args.inputfiles) +jvpp_c_gen.generate_jvpp(func_list, plugin_name, args.inputfiles, root_dir) +jvpp_future_facade_gen.generate_jvpp(func_list, base_package, plugin_package, plugin_name.title(), dto_package, callback_package, notification_package, future_package, args.inputfiles) +jvpp_callback_facade_gen.generate_jvpp(func_list, base_package, plugin_package, plugin_name.title(), dto_package, callback_package, notification_package, callback_facade_package, args.inputfiles) + +print "Java API for %s generated successfully" % args.inputfiles diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/__init__.py b/src/vpp-api/java/jvpp/gen/jvppgen/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/callback_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/callback_gen.py new file mode 100644 index 00000000..b3024b9c --- /dev/null +++ b/src/vpp-api/java/jvpp/gen/jvppgen/callback_gen.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python +# +# 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. + +import os +import util +from string import Template + +from util import remove_suffix + +callback_suffix = "Callback" + +callback_template = Template(""" +package $plugin_package.$callback_package; + +/** + *

Represents callback for plugin's api file message. + *
It was generated by callback_gen.py based on $inputfile preparsed data: + *

+$docs
+ * 
+ */ +public interface $cls_name extends $base_package.$callback_package.$callback_type { + + $callback_method + +} +""") + +global_callback_template = Template(""" +package $plugin_package.$callback_package; + +/** + *

Global aggregated callback interface. + *
It was generated by callback_gen.py based on $inputfile + *
(python representation of api file generated by vppapigen). + */ +public interface JVpp${plugin_name}GlobalCallback extends $base_package.$callback_package.ControlPingCallback, $callbacks { +} +""") + + +def generate_callbacks(func_list, base_package, plugin_package, plugin_name, callback_package, dto_package, inputfile): + """ Generates callback interfaces """ + print "Generating Callback interfaces" + + if not os.path.exists(callback_package): + os.mkdir(callback_package) + + callbacks = [] + for func in func_list: + + camel_case_name_with_suffix = util.underscore_to_camelcase_upper(func['name']) + + if util.is_ignored(func['name']) or util.is_control_ping(camel_case_name_with_suffix): + continue + if not util.is_reply(camel_case_name_with_suffix) and not util.is_notification(func['name']): + continue + + if util.is_reply(camel_case_name_with_suffix): + camel_case_name = util.remove_reply_suffix(camel_case_name_with_suffix) + callback_type = "JVppCallback" + else: + camel_case_name_with_suffix = util.add_notification_suffix(camel_case_name_with_suffix) + camel_case_name = camel_case_name_with_suffix + callback_type = "JVppNotificationCallback" + + callbacks.append("{0}.{1}.{2}".format(plugin_package, callback_package, camel_case_name + callback_suffix)) + callback_path = os.path.join(callback_package, camel_case_name + callback_suffix + ".java") + callback_file = open(callback_path, 'w') + + reply_type = "%s.%s.%s" % (plugin_package, dto_package, camel_case_name_with_suffix) + method = "void on{0}({1} reply);".format(camel_case_name_with_suffix, reply_type) + callback_file.write( + callback_template.substitute(inputfile=inputfile, + docs=util.api_message_to_javadoc(func), + cls_name=camel_case_name + callback_suffix, + callback_method=method, + base_package=base_package, + plugin_package=plugin_package, + callback_package=callback_package, + callback_type=callback_type)) + callback_file.flush() + callback_file.close() + + callback_file = open(os.path.join(callback_package, "JVpp%sGlobalCallback.java" % plugin_name), 'w') + callback_file.write(global_callback_template.substitute(inputfile=inputfile, + callbacks=", ".join(callbacks), + base_package=base_package, + plugin_package=plugin_package, + plugin_name=plugin_name, + callback_package=callback_package)) + callback_file.flush() + callback_file.close() diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/dto_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/dto_gen.py new file mode 100644 index 00000000..cfddb9ef --- /dev/null +++ b/src/vpp-api/java/jvpp/gen/jvppgen/dto_gen.py @@ -0,0 +1,308 @@ +#!/usr/bin/env python +# +# 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. + +import os +from string import Template + +import util + +dto_template = Template(""" +package $plugin_package.$dto_package; + +/** + *

This class represents $description. + *
It was generated by dto_gen.py based on $inputfile preparsed data: + *

+$docs
+ * 
+ */ +public final class $cls_name implements $base_package.$dto_package.$base_type { + +$fields +$methods +} +""") + +field_template = Template(""" public $type $name;\n""") + +send_template = Template(""" @Override + public int send(final $base_package.JVpp jvpp) throws io.fd.vpp.jvpp.VppInvocationException { + return (($plugin_package.JVpp${plugin_name})jvpp).$method_name($args); + }""") + + +def generate_dtos(func_list, base_package, plugin_package, plugin_name, dto_package, inputfile): + """ Generates dto objects in a dedicated package """ + print "Generating DTOs" + + if not os.path.exists(dto_package): + os.mkdir(dto_package) + + for func in func_list: + camel_case_dto_name = util.underscore_to_camelcase_upper(func['name']) + camel_case_method_name = util.underscore_to_camelcase(func['name']) + dto_path = os.path.join(dto_package, camel_case_dto_name + ".java") + + if util.is_ignored(func['name']) or util.is_control_ping(camel_case_dto_name): + continue + + fields = generate_dto_fields(camel_case_dto_name, func) + methods = generate_dto_base_methods(camel_case_dto_name, func) + base_type = "" + + # Generate request/reply or dump/dumpReply even if structure can be used as notification + if not util.is_just_notification(func["name"]): + if util.is_reply(camel_case_dto_name): + description = "reply DTO" + request_dto_name = get_request_name(camel_case_dto_name, func['name']) + if util.is_details(camel_case_dto_name): + # FIXME assumption that dump calls end with "Dump" suffix. Not enforced in vpe.api + base_type += "JVppReply<%s.%s.%s>" % (plugin_package, dto_package, request_dto_name + "Dump") + generate_dump_reply_dto(request_dto_name, base_package, plugin_package, dto_package, + camel_case_dto_name, camel_case_method_name, func) + else: + base_type += "JVppReply<%s.%s.%s>" % (plugin_package, dto_package, request_dto_name) + else: + args = "" if fields is "" else "this" + methods += send_template.substitute(method_name=camel_case_method_name, + base_package=base_package, + plugin_package=plugin_package, + plugin_name=plugin_name, + args=args) + if util.is_dump(camel_case_dto_name): + base_type += "JVppDump" + description = "dump request DTO" + else: + base_type += "JVppRequest" + description = "request DTO" + + write_dto_file(base_package, plugin_package, base_type, camel_case_dto_name, description, dto_package, + dto_path, fields, func, inputfile, methods) + + # for structures that are also used as notifications, generate dedicated notification DTO + if util.is_notification(func["name"]): + base_type = "JVppNotification" + description = "notification DTO" + camel_case_dto_name = util.add_notification_suffix(camel_case_dto_name) + dto_path = os.path.join(dto_package, camel_case_dto_name + ".java") + methods = generate_dto_base_methods(camel_case_dto_name, func) + write_dto_file(base_package, plugin_package, base_type, camel_case_dto_name, description, dto_package, + dto_path, fields, func, inputfile, methods) + + flush_dump_reply_dtos(inputfile) + + +def generate_dto_base_methods(camel_case_dto_name, func): + methods = generate_dto_hash(func) + methods += generate_dto_equals(camel_case_dto_name, func) + methods += generate_dto_tostring(camel_case_dto_name, func) + return methods + + +def generate_dto_fields(camel_case_dto_name, func): + fields = "" + for t in zip(func['types'], func['args']): + # for retval don't generate dto field in Reply + field_name = util.underscore_to_camelcase(t[1]) + if util.is_reply(camel_case_dto_name) and util.is_retval_field(field_name): + continue + fields += field_template.substitute(type=util.jni_2_java_type_mapping[t[0]], + name=field_name) + return fields + + +tostring_field_template = Template(""" \"$field_name=\" + $field_name + ", " +\n""") +tostring_array_field_template = Template(""" \"$field_name=\" + java.util.Arrays.toString($field_name) + ", " +\n""") +tostring_template = Template(""" @Override + public String toString() { + return "$cls_name{" + +$fields_tostring "}"; + }\n\n""") + + +def generate_dto_tostring(camel_case_dto_name, func): + tostring_fields = "" + for t in zip(func['types'], func['args']): + + field_name = util.underscore_to_camelcase(t[1]) + # for retval don't generate dto field in Reply + if util.is_retval_field(field_name): + continue + + # handle array types + if util.is_array(util.jni_2_java_type_mapping[t[0]]): + tostring_fields += tostring_array_field_template.substitute(field_name=field_name) + else: + tostring_fields += tostring_field_template.substitute(field_name=field_name) + + return tostring_template.substitute(cls_name=camel_case_dto_name, + fields_tostring=tostring_fields[:-8]) + +equals_other_template = Template(""" + final $cls_name other = ($cls_name) o; +\n""") +equals_field_template = Template(""" if (!java.util.Objects.equals(this.$field_name, other.$field_name)) { + return false; + }\n""") +equals_array_field_template = Template(""" if (!java.util.Arrays.equals(this.$field_name, other.$field_name)) { + return false; + }\n""") +equals_template = Template(""" @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } +$comparisons + return true; + }\n\n""") + + +def generate_dto_equals(camel_case_dto_name, func): + equals_fields = "" + for t in zip(func['types'], func['args']): + field_name = util.underscore_to_camelcase(t[1]) + # for retval don't generate dto field in Reply + if util.is_retval_field(field_name): + continue + + # handle array types + if util.is_array(util.jni_2_java_type_mapping[t[0]]): + equals_fields += equals_array_field_template.substitute(field_name=field_name) + else: + equals_fields += equals_field_template.substitute(field_name=field_name) + + if equals_fields != "": + equals_fields = equals_other_template.substitute(cls_name=camel_case_dto_name) + equals_fields + + return equals_template.substitute(comparisons=equals_fields) + + +hash_template = Template(""" @Override + public int hashCode() { + return java.util.Objects.hash($fields); + }\n\n""") +hash_single_array_type_template = Template(""" @Override + public int hashCode() { + return java.util.Arrays.hashCode($fields); + }\n\n""") + + +def generate_dto_hash(func): + hash_fields = "" + + # Special handling for hashCode in case just a single array field is present. Cannot use Objects.equals since the + # array is mistaken for a varargs parameter. Instead use Arrays.hashCode in such case. + if len(func['args']) == 1: + single_type = func['types'][0] + single_type_name = func['args'][0] + if util.is_array(util.jni_2_java_type_mapping[single_type]): + return hash_single_array_type_template.substitute(fields=util.underscore_to_camelcase(single_type_name)) + + for t in zip(func['types'], func['args']): + field_name = util.underscore_to_camelcase(t[1]) + # for retval don't generate dto field in Reply + if util.is_retval_field(field_name): + continue + + hash_fields += field_name + ", " + + return hash_template.substitute(fields=hash_fields[:-2]) + + +def write_dto_file(base_package, plugin_package, base_type, camel_case_dto_name, description, dto_package, dto_path, + fields, func, inputfile, methods): + dto_file = open(dto_path, 'w') + dto_file.write(dto_template.substitute(inputfile=inputfile, + description=description, + docs=util.api_message_to_javadoc(func), + cls_name=camel_case_dto_name, + fields=fields, + methods=methods, + base_package=base_package, + plugin_package=plugin_package, + base_type=base_type, + dto_package=dto_package)) + dto_file.flush() + dto_file.close() + + +dump_dto_suffix = "ReplyDump" +dump_reply_artificial_dtos = {} + + +# Returns request name or special one from unconventional_naming_rep_req map +def get_request_name(camel_case_dto_name, func_name): + return util.underscore_to_camelcase_upper( + util.unconventional_naming_rep_req[func_name]) if func_name in util.unconventional_naming_rep_req \ + else util.remove_reply_suffix(camel_case_dto_name) + + +def flush_dump_reply_dtos(inputfile): + for dump_reply_artificial_dto in dump_reply_artificial_dtos.values(): + dto_path = os.path.join(dump_reply_artificial_dto['dto_package'], + dump_reply_artificial_dto['cls_name'] + ".java") + dto_file = open(dto_path, 'w') + dto_file.write(dto_template.substitute(inputfile=inputfile, + description="dump reply wrapper", + docs=dump_reply_artificial_dto['docs'], + cls_name=dump_reply_artificial_dto['cls_name'], + fields=dump_reply_artificial_dto['fields'], + methods=dump_reply_artificial_dto['methods'], + plugin_package=dump_reply_artificial_dto['plugin_package'], + base_package=dump_reply_artificial_dto['base_package'], + base_type=dump_reply_artificial_dto['base_type'], + dto_package=dump_reply_artificial_dto['dto_package'])) + dto_file.flush() + dto_file.close() + + +def generate_dump_reply_dto(request_dto_name, base_package, plugin_package, dto_package, camel_case_dto_name, + camel_case_method_name, func): + base_type = "JVppReplyDump<%s.%s.%s, %s.%s.%s>" % ( + plugin_package, dto_package, util.remove_reply_suffix(camel_case_dto_name) + "Dump", + plugin_package, dto_package, camel_case_dto_name) + fields = " public java.util.List<%s> %s = new java.util.ArrayList<>();" % (camel_case_dto_name, camel_case_method_name) + cls_name = camel_case_dto_name + dump_dto_suffix + # using artificial type for fields, just to bypass the is_array check in base methods generators + # the type is not really used + artificial_type = 'u8' + + # In case of already existing artificial reply dump DTO, just update it + # Used for sub-dump dtos + if request_dto_name in dump_reply_artificial_dtos.keys(): + dump_reply_artificial_dtos[request_dto_name]['fields'] += '\n' + fields + dump_reply_artificial_dtos[request_dto_name]['field_names'].append(func['name']) + dump_reply_artificial_dtos[request_dto_name]['field_types'].append(artificial_type) + methods = '\n' + generate_dto_base_methods(dump_reply_artificial_dtos[request_dto_name]['cls_name'], + {'args': dump_reply_artificial_dtos[request_dto_name]['field_names'], + 'types': dump_reply_artificial_dtos[request_dto_name]['field_types']}) + dump_reply_artificial_dtos[request_dto_name]['methods'] = methods + else: + methods = '\n' + generate_dto_base_methods(cls_name, {'args': [func['name']], + 'types': [artificial_type]}) + dump_reply_artificial_dtos[request_dto_name] = ({'docs': util.api_message_to_javadoc(func), + 'cls_name': cls_name, + 'fields': fields, + 'field_names': [func['name']], + 'field_types': [artificial_type], + # strip too many newlines at the end of base method block + 'methods': methods, + 'plugin_package': plugin_package, + 'base_package': base_package, + 'base_type': base_type, + 'dto_package': dto_package}) diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jni_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jni_gen.py new file mode 100644 index 00000000..328cc8d3 --- /dev/null +++ b/src/vpp-api/java/jvpp/gen/jvppgen/jni_gen.py @@ -0,0 +1,295 @@ +#!/usr/bin/env python +# +# 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. + +from string import Template + +import util + +variable_length_array_value_template = Template("""mp->${length_var_name}""") +variable_length_array_template = Template("""clib_net_to_host_${length_field_type}(${value})""") + +dto_field_id_template = Template(""" + jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${field_name}", "${jni_signature}");""") + +default_dto_field_setter_template = Template(""" + (*env)->Set${jni_setter}(env, ${object_name}, ${field_reference_name}FieldId, mp->${c_name}); +""") + +variable_length_array_value_template = Template("""mp->${length_var_name}""") +variable_length_array_template = Template("""clib_net_to_host_${length_field_type}(${value})""") + +u16_dto_field_setter_template = Template(""" + (*env)->Set${jni_setter}(env, ${object_name}, ${field_reference_name}FieldId, clib_net_to_host_u16(mp->${c_name})); +""") + +u32_dto_field_setter_template = Template(""" + (*env)->Set${jni_setter}(env, ${object_name}, ${field_reference_name}FieldId, clib_net_to_host_u32(mp->${c_name})); +""") + +u64_dto_field_setter_template = Template(""" + (*env)->Set${jni_setter}(env, ${object_name}, ${field_reference_name}FieldId, clib_net_to_host_u64(mp->${c_name})); +""") + +u8_array_dto_field_setter_template = Template(""" + jbyteArray ${field_reference_name} = (*env)->NewByteArray(env, ${field_length}); + (*env)->SetByteArrayRegion(env, ${field_reference_name}, 0, ${field_length}, (const jbyte*)mp->${c_name}); + (*env)->SetObjectField(env, ${object_name}, ${field_reference_name}FieldId, ${field_reference_name}); + (*env)->DeleteLocalRef(env, ${field_reference_name}); +""") + +u16_array_dto_field_setter_template = Template(""" + { + jshortArray ${field_reference_name} = (*env)->NewShortArray(env, ${field_length}); + jshort * ${field_reference_name}ArrayElements = (*env)->GetShortArrayElements(env, ${field_reference_name}, NULL); + unsigned int _i; + for (_i = 0; _i < ${field_length}; _i++) { + ${field_reference_name}ArrayElements[_i] = clib_net_to_host_u16(mp->${c_name}[_i]); + } + + (*env)->ReleaseShortArrayElements(env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0); + (*env)->SetObjectField(env, ${object_name}, ${field_reference_name}FieldId, ${field_reference_name}); + (*env)->DeleteLocalRef(env, ${field_reference_name}); + } +""") + +u32_array_dto_field_setter_template = Template(""" + { + jintArray ${field_reference_name} = (*env)->NewIntArray(env, ${field_length}); + jint * ${field_reference_name}ArrayElements = (*env)->GetIntArrayElements(env, ${field_reference_name}, NULL); + unsigned int _i; + for (_i = 0; _i < ${field_length}; _i++) { + ${field_reference_name}ArrayElements[_i] = clib_net_to_host_u32(mp->${c_name}[_i]); + } + + (*env)->ReleaseIntArrayElements(env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0); + (*env)->SetObjectField(env, ${object_name}, ${field_reference_name}FieldId, ${field_reference_name}); + (*env)->DeleteLocalRef(env, ${field_reference_name}); + } +""") + +# For each u64 array we get its elements. Then we convert values to host byte order. +# All changes to jlong* buffer are written to jlongArray (isCopy is set to NULL) +u64_array_dto_field_setter_template = Template(""" + { + jlongArray ${field_reference_name} = (*env)->NewLongArray(env, ${field_length}); + jlong * ${field_reference_name}ArrayElements = (*env)->GetLongArrayElements(env, ${field_reference_name}, NULL); + unsigned int _i; + for (_i = 0; _i < ${field_length}; _i++) { + ${field_reference_name}ArrayElements[_i] = clib_net_to_host_u64(mp->${c_name}[_i]); + } + + (*env)->ReleaseLongArrayElements(env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0); + (*env)->SetObjectField(env, ${object_name}, ${field_reference_name}FieldId, ${field_reference_name}); + (*env)->DeleteLocalRef(env, ${field_reference_name}); + } +""") + +dto_field_setter_templates = {'u8': default_dto_field_setter_template, + 'u16': u16_dto_field_setter_template, + 'u32': u32_dto_field_setter_template, + 'i32': u32_dto_field_setter_template, + 'u64': u64_dto_field_setter_template, + 'f64': default_dto_field_setter_template, # fixme + 'u8[]': u8_array_dto_field_setter_template, + 'u16[]': u16_array_dto_field_setter_template, + 'u32[]': u32_array_dto_field_setter_template, + 'u64[]': u64_array_dto_field_setter_template + } + + +def jni_reply_handler_for_type(handler_name, ref_name, field_type, c_name, field_reference_name, + field_name, field_length, is_variable_len_array, length_field_type, + object_name="dto"): + """ + Generates jni code that initializes a field of java object (dto or custom type). + To be used in reply message handlers. + :param field_type: type of the field to be initialized (as defined in vpe.api) + :param c_name: name of the message struct member that stores initialization value + :param field_reference_name: name of the field reference in generated code + :param field_name: name of the field (camelcase) + :param field_length: integer or name of variable that stores field length + :param object_name: name of the object to be initialized + """ + + # todo move validation to vppapigen + if field_type.endswith('[]') and field_length == '0': + raise Exception('Variable array \'%s\' defined in \'%s\' ' + 'should have defined length (e.g. \'%s[%s_length]\'' + % (c_name, handler_name, c_name, c_name)) + + if is_variable_len_array: + length_var_name = field_length + field_length = variable_length_array_value_template.substitute(length_var_name=length_var_name) + if length_field_type != 'u8': # we need net to host conversion: + field_length = variable_length_array_template.substitute( + length_field_type=length_field_type, value=field_length) + + # for retval don't generate setters + if util.is_retval_field(c_name): + return "" + + jni_signature = util.jni_2_signature_mapping[field_type] + jni_setter = util.jni_field_accessors[field_type] + + result = dto_field_id_template.substitute( + field_reference_name=field_reference_name, + field_name=field_name, + class_ref_name=ref_name, + jni_signature=jni_signature) + + dto_setter_template = dto_field_setter_templates[field_type] + + result += dto_setter_template.substitute( + jni_signature=jni_signature, + object_name=object_name, + field_reference_name=field_reference_name, + c_name=c_name, + jni_setter=jni_setter, + field_length=field_length) + return result + + +request_field_identifier_template = Template(""" + jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${object_name}Class, "${field_name}", "${jni_signature}"); + ${jni_type} ${field_reference_name} = (*env)->Get${jni_getter}(env, ${object_name}, ${field_reference_name}FieldId); + """) + +array_length_enforcement_template = Template(""" + size_t max_size = ${field_length}; + if (cnt > max_size) cnt = max_size;""") + +u8_struct_setter_template = Template(""" + mp->${c_name} = ${field_reference_name};""") + +u16_struct_setter_template = Template(""" + mp->${c_name} = clib_host_to_net_u16(${field_reference_name});""") + +u32_struct_setter_template = Template(""" + mp->${c_name} = clib_host_to_net_u32(${field_reference_name});""") + +i32_struct_setter_template = Template(""" + mp->${c_name} = clib_host_to_net_i32(${field_reference_name});!""") + +u64_struct_setter_template = Template(""" + mp->${c_name} = clib_host_to_net_u64(${field_reference_name});""") + +array_length_enforcement_template = Template(""" + size_t max_size = ${field_length}; + if (cnt > max_size) cnt = max_size;""") + +u8_array_struct_setter_template = Template(""" + if (${field_reference_name}) { + jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name}); + ${field_length_check} + (*env)->GetByteArrayRegion(env, ${field_reference_name}, 0, cnt, (jbyte *)mp->${c_name}); + } +""") + +u16_array_struct_setter_template = Template(""" + if (${field_reference_name}) { + jshort * ${field_reference_name}ArrayElements = (*env)->GetShortArrayElements(env, ${field_reference_name}, NULL); + size_t _i; + jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name}); + ${field_length_check} + for (_i = 0; _i < cnt; _i++) { + mp->${c_name}[_i] = clib_host_to_net_u16(${field_reference_name}ArrayElements[_i]); + } + (*env)->ReleaseShortArrayElements (env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0); + } + """) + +u32_array_struct_setter_template = Template(""" + if (${field_reference_name}) { + jint * ${field_reference_name}ArrayElements = (*env)->GetIntArrayElements(env, ${field_reference_name}, NULL); + size_t _i; + jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name}); + ${field_length_check} + for (_i = 0; _i < cnt; _i++) { + mp->${c_name}[_i] = clib_host_to_net_u32(${field_reference_name}ArrayElements[_i]); + } + (*env)->ReleaseIntArrayElements (env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0); + } + """) + +u64_array_struct_setter_template = Template(""" + if (${field_reference_name}) { + jlong * ${field_reference_name}ArrayElements = (*env)->GetLongArrayElements(env, ${field_reference_name}, NULL); + size_t _i; + jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name}); + ${field_length_check} + for (_i = 0; _i < cnt; _i++) { + mp->${c_name}[_i] = clib_host_to_net_u64(${field_reference_name}ArrayElements[_i]); + } + (*env)->ReleaseLongArrayElements (env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0); + } + """) + +struct_setter_templates = {'u8': u8_struct_setter_template, + 'u16': u16_struct_setter_template, + 'u32': u32_struct_setter_template, + 'i32': u32_struct_setter_template, + 'u64': u64_struct_setter_template, + 'u8[]': u8_array_struct_setter_template, + 'u16[]': u16_array_struct_setter_template, + 'u32[]': u32_array_struct_setter_template, + 'u64[]': u64_array_struct_setter_template + } + + +def jni_request_binding_for_type(field_type, c_name, field_reference_name, field_name, field_length, + is_variable_len_array, object_name="request"): + """ + Generates jni code that initializes C structure that corresponds to a field of java object + (dto or custom type). To be used in request message handlers. + :param field_type: type of the field to be initialized (as defined in vpe.api) + :param c_name: name of the message struct member to be initialized + :param field_reference_name: name of the field reference in generated code + :param field_name: name of the field (camelcase) + :param field_length: integer or name of variable that stores field length + :param object_name: name of the object to be initialized + """ + # field identifiers + jni_type = util.vpp_2_jni_type_mapping[field_type] + jni_signature = util.jni_2_signature_mapping[field_type] + jni_getter = util.jni_field_accessors[field_type] + + # field identifier + msg_initialization = request_field_identifier_template.substitute( + jni_type=jni_type, + field_reference_name=field_reference_name, + field_name=field_name, + jni_signature=jni_signature, + jni_getter=jni_getter, + object_name=object_name) + + # field setter + field_length_check = "" + + # check if we are processing variable length array: + if is_variable_len_array: + field_length = util.underscore_to_camelcase(field_length) + + # enforce max length if array has fixed length or uses variable length syntax + if str(field_length) != "0": + field_length_check = array_length_enforcement_template.substitute(field_length=field_length) + + struct_setter_template = struct_setter_templates[field_type] + + msg_initialization += struct_setter_template.substitute( + c_name=c_name, + field_reference_name=field_reference_name, + field_length_check=field_length_check) + + return msg_initialization diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py new file mode 100644 index 00000000..611171c4 --- /dev/null +++ b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py @@ -0,0 +1,343 @@ +#!/usr/bin/env python +# +# 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 +# l +# 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. +# + +import os, util +from string import Template + +import jni_gen + + +def is_manually_generated(f_name, plugin_name): + return f_name in {'control_ping_reply'} + + +class_reference_template = Template("""jclass ${ref_name}Class; +""") + +find_class_invocation_template = Template(""" + ${ref_name}Class = (jclass)(*env)->NewGlobalRef(env, (*env)->FindClass(env, "io/fd/vpp/jvpp/${plugin_name}/dto/${class_name}")); + if ((*env)->ExceptionCheck(env)) { + (*env)->ExceptionDescribe(env); + return JNI_ERR; + }""") + +find_class_template = Template(""" + ${ref_name}Class = (jclass)(*env)->NewGlobalRef(env, (*env)->FindClass(env, "${class_name}")); + if ((*env)->ExceptionCheck(env)) { + (*env)->ExceptionDescribe(env); + return JNI_ERR; + }""") + +delete_class_invocation_template = Template(""" + if (${ref_name}Class) { + (*env)->DeleteGlobalRef(env, ${ref_name}Class); + }""") + +class_cache_template = Template(""" +$class_references +static int cache_class_references(JNIEnv* env) { + $find_class_invocations + return 0; +} + +static void delete_class_references(JNIEnv* env) { + $delete_class_invocations +}""") + + +def generate_class_cache(func_list, plugin_name): + class_references = [] + find_class_invocations = [] + delete_class_invocations = [] + for f in func_list: + c_name = f['name'] + class_name = util.underscore_to_camelcase_upper(c_name) + ref_name = util.underscore_to_camelcase(c_name) + + if util.is_ignored(c_name) or util.is_control_ping(class_name): + continue + + if util.is_reply(class_name): + class_references.append(class_reference_template.substitute( + ref_name=ref_name)) + find_class_invocations.append(find_class_invocation_template.substitute( + plugin_name=plugin_name, + ref_name=ref_name, + class_name=class_name)) + delete_class_invocations.append(delete_class_invocation_template.substitute(ref_name=ref_name)) + elif util.is_notification(c_name): + class_references.append(class_reference_template.substitute( + ref_name=util.add_notification_suffix(ref_name))) + find_class_invocations.append(find_class_invocation_template.substitute( + plugin_name=plugin_name, + ref_name=util.add_notification_suffix(ref_name), + class_name=util.add_notification_suffix(class_name))) + delete_class_invocations.append(delete_class_invocation_template.substitute( + ref_name=util.add_notification_suffix(ref_name))) + + # add exception class to class cache + ref_name = 'callbackException' + class_name = 'io/fd/vpp/jvpp/VppCallbackException' + class_references.append(class_reference_template.substitute( + ref_name=ref_name)) + find_class_invocations.append(find_class_template.substitute( + ref_name=ref_name, + class_name=class_name)) + delete_class_invocations.append(delete_class_invocation_template.substitute(ref_name=ref_name)) + + return class_cache_template.substitute( + class_references="".join(class_references), find_class_invocations="".join(find_class_invocations), + delete_class_invocations="".join(delete_class_invocations)) + + +# TODO: cache method and field identifiers to achieve better performance +# https://jira.fd.io/browse/HONEYCOMB-42 +request_class_template = Template(""" + jclass requestClass = (*env)->FindClass(env, "io/fd/vpp/jvpp/${plugin_name}/dto/${java_name_upper}");""") + +request_field_identifier_template = Template(""" + jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${object_name}Class, "${field_name}", "${jni_signature}"); + ${jni_type} ${field_reference_name} = (*env)->Get${jni_getter}(env, ${object_name}, ${field_reference_name}FieldId); + """) + + +jni_impl_template = Template(""" +/** + * JNI binding for sending ${c_name} message. + * Generated based on $inputfile preparsed data: +$api_data + */ +JNIEXPORT jint JNICALL Java_io_fd_vpp_jvpp_${plugin_name}_JVpp${java_plugin_name}Impl_${field_name}0 +(JNIEnv * env, jclass clazz$args) { + ${plugin_name}_main_t *plugin_main = &${plugin_name}_main; + vl_api_${c_name}_t * mp; + u32 my_context_id = vppjni_get_context_id (&jvpp_main); + $request_class + + // create message: + mp = vl_msg_api_alloc(sizeof(*mp)); + memset (mp, 0, sizeof (*mp)); + mp->_vl_msg_id = ntohs (VL_API_${c_name_uppercase} + plugin_main->msg_id_base); + mp->client_index = plugin_main->my_client_index; + mp->context = clib_host_to_net_u32 (my_context_id); + + $msg_initialization + + // send message: + vl_msg_api_send_shmem (plugin_main->vl_input_queue, (u8 *)&mp); + if ((*env)->ExceptionCheck(env)) { + return JNI_ERR; + } + return my_context_id; +}""") + +def generate_jni_impl(func_list, plugin_name, inputfile): + jni_impl = [] + for f in func_list: + f_name = f['name'] + camel_case_function_name = util.underscore_to_camelcase(f_name) + if is_manually_generated(f_name, plugin_name) or util.is_reply(camel_case_function_name) \ + or util.is_ignored(f_name) or util.is_just_notification(f_name): + continue + + arguments = '' + request_class = '' + msg_initialization = '' + f_name_uppercase = f_name.upper() + + if f['args']: + arguments = ', jobject request' + camel_case_function_name_upper = util.underscore_to_camelcase_upper(f_name) + + request_class = request_class_template.substitute( + java_name_upper=camel_case_function_name_upper, + plugin_name=plugin_name) + + for t in zip(f['types'], f['args'], f['lengths']): + field_name = util.underscore_to_camelcase(t[1]) + msg_initialization += jni_gen.jni_request_binding_for_type(field_type=t[0], c_name=t[1], + field_reference_name=field_name, + field_name=field_name, + field_length=t[2][0], + is_variable_len_array=t[2][1]) + + jni_impl.append(jni_impl_template.substitute( + inputfile=inputfile, + api_data=util.api_message_to_javadoc(f), + field_reference_name=camel_case_function_name, + field_name=camel_case_function_name, + c_name_uppercase=f_name_uppercase, + c_name=f_name, + plugin_name=plugin_name, + java_plugin_name=plugin_name.title(), + request_class=request_class, + msg_initialization=msg_initialization, + args=arguments)) + + return "\n".join(jni_impl) + +# code fragment for checking result of the operation before sending request reply +callback_err_handler_template = Template(""" + // for negative result don't send callback message but send error callback + if (mp->retval<0) { + call_on_error("${handler_name}", mp->context, mp->retval, plugin_main->callbackClass, plugin_main->callbackObject, callbackExceptionClass); + return; + } + if (mp->retval == VNET_API_ERROR_IN_PROGRESS) { + clib_warning("Result in progress"); + return; + } +""") + +msg_handler_template = Template(""" +/** + * Handler for ${handler_name} message. + * Generated based on $inputfile preparsed data: +$api_data + */ +static void vl_api_${handler_name}_t_handler (vl_api_${handler_name}_t * mp) +{ + ${plugin_name}_main_t *plugin_main = &${plugin_name}_main; + JNIEnv *env = jvpp_main.jenv; + + $err_handler + + jmethodID constructor = (*env)->GetMethodID(env, ${class_ref_name}Class, "", "()V"); + jmethodID callbackMethod = (*env)->GetMethodID(env, plugin_main->callbackClass, "on${dto_name}", "(Lio/fd/vpp/jvpp/${plugin_name}/dto/${dto_name};)V"); + + jobject dto = (*env)->NewObject(env, ${class_ref_name}Class, constructor); + $dto_setters + + (*env)->CallVoidMethod(env, plugin_main->callbackObject, callbackMethod, dto); + // free DTO as per http://stackoverflow.com/questions/1340938/memory-leak-when-calling-java-code-from-c-using-jni + (*env)->DeleteLocalRef(env, dto); +}""") + + +def generate_msg_handlers(func_list, plugin_name, inputfile): + handlers = [] + for f in func_list: + handler_name = f['name'] + dto_name = util.underscore_to_camelcase_upper(handler_name) + ref_name = util.underscore_to_camelcase(handler_name) + + if is_manually_generated(handler_name, plugin_name) or util.is_ignored(handler_name): + continue + + if not util.is_reply(dto_name) and not util.is_notification(handler_name): + continue + + if util.is_notification(handler_name): + dto_name = util.add_notification_suffix(dto_name) + ref_name = util.add_notification_suffix(ref_name) + + dto_setters = '' + err_handler = '' + # dto setters + for t in zip(f['types'], f['args'], f['lengths']): + c_name = t[1] + java_name = util.underscore_to_camelcase(c_name) + field_length = t[2][0] + is_variable_len_array = t[2][1] + length_field_type = None + if is_variable_len_array: + length_field_type = f['types'][f['args'].index(field_length)] + dto_setters += jni_gen.jni_reply_handler_for_type(handler_name=handler_name, ref_name=ref_name, + field_type=t[0], c_name=t[1], + field_reference_name=java_name, + field_name=java_name, field_length=field_length, + is_variable_len_array=is_variable_len_array, + length_field_type=length_field_type) + + # for retval don't generate setters and generate retval check + if util.is_retval_field(c_name): + err_handler = callback_err_handler_template.substitute( + handler_name=handler_name + ) + continue + + handlers.append(msg_handler_template.substitute( + inputfile=inputfile, + api_data=util.api_message_to_javadoc(f), + handler_name=handler_name, + plugin_name=plugin_name, + dto_name=dto_name, + class_ref_name=ref_name, + dto_setters=dto_setters, + err_handler=err_handler)) + + return "\n".join(handlers) + + +handler_registration_template = Template("""_(${upercase_name}, ${name}) \\ +""") + + +def generate_handler_registration(func_list): + handler_registration = ["#define foreach_api_reply_handler \\\n"] + for f in func_list: + name = f['name'] + camelcase_name = util.underscore_to_camelcase(f['name']) + + if (not util.is_reply(camelcase_name) and not util.is_notification(name)) or util.is_ignored(name) \ + or util.is_control_ping(camelcase_name): + continue + + handler_registration.append(handler_registration_template.substitute( + name=name, + upercase_name=name.upper())) + + return "".join(handler_registration) + + +jvpp_c_template = Template("""/** + * This file contains JNI bindings for jvpp Java API. + * It was generated by jvpp_c_gen.py based on $inputfile + * (python representation of api file generated by vppapigen). + */ + +// JAVA class reference cache +$class_cache + +// JNI bindings +$jni_implementations + +// Message handlers +$msg_handlers + +// Registration of message handlers in vlib +$handler_registration +""") + +def generate_jvpp(func_list, plugin_name, inputfile, path): + """ Generates jvpp C file """ + print "Generating jvpp C" + + class_cache = generate_class_cache(func_list, plugin_name) + jni_impl = generate_jni_impl(func_list, plugin_name, inputfile) + msg_handlers = generate_msg_handlers(func_list, plugin_name, inputfile) + handler_registration = generate_handler_registration(func_list) + + jvpp_c_file = open("%s/jvpp_%s_gen.h" % (path, plugin_name), 'w') + jvpp_c_file.write(jvpp_c_template.substitute( + inputfile=inputfile, + class_cache=class_cache, + jni_implementations=jni_impl, + msg_handlers=msg_handlers, + handler_registration=handler_registration)) + jvpp_c_file.flush() + jvpp_c_file.close() + diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_callback_facade_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_callback_facade_gen.py new file mode 100644 index 00000000..ac096a71 --- /dev/null +++ b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_callback_facade_gen.py @@ -0,0 +1,324 @@ +#!/usr/bin/env python +# +# 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. + +import os, util +from string import Template + +import callback_gen +import dto_gen + +jvpp_ifc_template = Template(""" +package $plugin_package.$callback_facade_package; + +/** + *

Callback Java API representation of $plugin_package plugin. + *
It was generated by jvpp_callback_facade_gen.py based on $inputfile + *
(python representation of api file generated by vppapigen). + */ +public interface CallbackJVpp${plugin_name} extends $base_package.$notification_package.NotificationRegistryProvider, java.lang.AutoCloseable { + + // TODO add send + +$methods +} +""") + +jvpp_impl_template = Template(""" +package $plugin_package.$callback_facade_package; + +/** + *

Default implementation of Callback${plugin_name}JVpp interface. + *
It was generated by jvpp_callback_facade_gen.py based on $inputfile + *
(python representation of api file generated by vppapigen). + */ +public final class CallbackJVpp${plugin_name}Facade implements CallbackJVpp${plugin_name} { + + private final $plugin_package.JVpp${plugin_name} jvpp; + private final java.util.Map callbacks; + private final $plugin_package.$notification_package.${plugin_name}NotificationRegistryImpl notificationRegistry = new $plugin_package.$notification_package.${plugin_name}NotificationRegistryImpl(); + /** + *

Create CallbackJVpp${plugin_name}Facade object for provided JVpp instance. + * Constructor internally creates CallbackJVppFacadeCallback class for processing callbacks + * and then connects to provided JVpp instance + * + * @param jvpp provided $base_package.JVpp instance + * + * @throws java.io.IOException in case instance cannot connect to JVPP + */ + public CallbackJVpp${plugin_name}Facade(final $base_package.JVppRegistry registry, final $plugin_package.JVpp${plugin_name} jvpp) throws java.io.IOException { + this.jvpp = java.util.Objects.requireNonNull(jvpp,"jvpp is null"); + this.callbacks = new java.util.HashMap<>(); + java.util.Objects.requireNonNull(registry, "JVppRegistry should not be null"); + registry.register(jvpp, new CallbackJVpp${plugin_name}FacadeCallback(this.callbacks, notificationRegistry)); + } + + @Override + public $plugin_package.$notification_package.${plugin_name}NotificationRegistry getNotificationRegistry() { + return notificationRegistry; + } + + @Override + public void close() throws Exception { + jvpp.close(); + } + + // TODO add send() + +$methods +} +""") + +method_template = Template( + """ void $name($plugin_package.$dto_package.$request request, $plugin_package.$callback_package.$callback callback) throws $base_package.VppInvocationException;""") + +method_impl_template = Template(""" public final void $name($plugin_package.$dto_package.$request request, $plugin_package.$callback_package.$callback callback) throws $base_package.VppInvocationException { + synchronized (callbacks) { + callbacks.put(jvpp.$name(request), callback); + } + } +""") + +no_arg_method_template = Template(""" void $name($plugin_package.$callback_package.$callback callback) throws $base_package.VppInvocationException;""") +no_arg_method_impl_template = Template(""" public final void $name($plugin_package.$callback_package.$callback callback) throws $base_package.VppInvocationException { + synchronized (callbacks) { + callbacks.put(jvpp.$name(), callback); + } + } +""") + + +def generate_jvpp(func_list, base_package, plugin_package, plugin_name, dto_package, callback_package, notification_package, callback_facade_package, inputfile): + """ Generates callback facade """ + print "Generating JVpp callback facade" + + if os.path.exists(callback_facade_package): + util.remove_folder(callback_facade_package) + + os.mkdir(callback_facade_package) + + methods = [] + methods_impl = [] + for func in func_list: + + if util.is_notification(func['name']) or util.is_ignored(func['name']): + continue + + camel_case_name = util.underscore_to_camelcase(func['name']) + camel_case_name_upper = util.underscore_to_camelcase_upper(func['name']) + if util.is_reply(camel_case_name) or util.is_control_ping(camel_case_name): + continue + + # Strip suffix for dump calls + callback_type = get_request_name(camel_case_name_upper, func['name']) + callback_gen.callback_suffix + + if len(func['args']) == 0: + methods.append(no_arg_method_template.substitute(name=camel_case_name, + base_package=base_package, + plugin_package=plugin_package, + dto_package=dto_package, + callback_package=callback_package, + callback=callback_type)) + methods_impl.append(no_arg_method_impl_template.substitute(name=camel_case_name, + base_package=base_package, + plugin_package=plugin_package, + dto_package=dto_package, + callback_package=callback_package, + callback=callback_type)) + else: + methods.append(method_template.substitute(name=camel_case_name, + request=camel_case_name_upper, + base_package=base_package, + plugin_package=plugin_package, + dto_package=dto_package, + callback_package=callback_package, + callback=callback_type)) + methods_impl.append(method_impl_template.substitute(name=camel_case_name, + request=camel_case_name_upper, + base_package=base_package, + plugin_package=plugin_package, + dto_package=dto_package, + callback_package=callback_package, + callback=callback_type)) + + join = os.path.join(callback_facade_package, "CallbackJVpp%s.java" % plugin_name) + jvpp_file = open(join, 'w') + jvpp_file.write( + jvpp_ifc_template.substitute(inputfile=inputfile, + methods="\n".join(methods), + base_package=base_package, + plugin_package=plugin_package, + plugin_name=plugin_name, + dto_package=dto_package, + notification_package=notification_package, + callback_facade_package=callback_facade_package)) + jvpp_file.flush() + jvpp_file.close() + + jvpp_file = open(os.path.join(callback_facade_package, "CallbackJVpp%sFacade.java" % plugin_name), 'w') + jvpp_file.write(jvpp_impl_template.substitute(inputfile=inputfile, + methods="\n".join(methods_impl), + base_package=base_package, + plugin_package=plugin_package, + plugin_name=plugin_name, + dto_package=dto_package, + notification_package=notification_package, + callback_package=callback_package, + callback_facade_package=callback_facade_package)) + jvpp_file.flush() + jvpp_file.close() + + generate_callback(func_list, base_package, plugin_package, plugin_name, dto_package, callback_package, notification_package, callback_facade_package, inputfile) + + +jvpp_facade_callback_template = Template(""" +package $plugin_package.$callback_facade_package; + +/** + *

Implementation of JVppGlobalCallback interface for Java Callback API. + *
It was generated by jvpp_callback_facade_gen.py based on $inputfile + *
(python representation of api file generated by vppapigen). + */ +public final class CallbackJVpp${plugin_name}FacadeCallback implements $plugin_package.$callback_package.JVpp${plugin_name}GlobalCallback { + + private final java.util.Map requests; + private final $plugin_package.$notification_package.Global${plugin_name}NotificationCallback notificationCallback; + private static final java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(CallbackJVpp${plugin_name}FacadeCallback.class.getName()); + + public CallbackJVpp${plugin_name}FacadeCallback(final java.util.Map requestMap, + final $plugin_package.$notification_package.Global${plugin_name}NotificationCallback notificationCallback) { + this.requests = requestMap; + this.notificationCallback = notificationCallback; + } + + @Override + public void onError($base_package.VppCallbackException reply) { + + $base_package.$callback_package.JVppCallback failedCall; + synchronized(requests) { + failedCall = requests.remove(reply.getCtxId()); + } + + if(failedCall != null) { + try { + failedCall.onError(reply); + } catch(RuntimeException ex) { + ex.addSuppressed(reply); + LOG.log(java.util.logging.Level.WARNING, String.format("Callback: %s failed while handling exception: %s", failedCall, reply), ex); + } + } + } + + @Override + @SuppressWarnings("unchecked") + public void onControlPingReply($base_package.$dto_package.ControlPingReply reply) { + + $base_package.$callback_package.ControlPingCallback callback; + synchronized(requests) { + callback = ($base_package.$callback_package.ControlPingCallback) requests.remove(reply.context); + } + + if(callback != null) { + callback.onControlPingReply(reply); + } + } + +$methods +} +""") + +jvpp_facade_callback_method_template = Template(""" + @Override + @SuppressWarnings("unchecked") + public void on$callback_dto($plugin_package.$dto_package.$callback_dto reply) { + + $plugin_package.$callback_package.$callback callback; + synchronized(requests) { + callback = ($plugin_package.$callback_package.$callback) requests.remove(reply.context); + } + + if(callback != null) { + callback.on$callback_dto(reply); + } + } +""") + +jvpp_facade_callback_notification_method_template = Template(""" + @Override + @SuppressWarnings("unchecked") + public void on$callback_dto($plugin_package.$dto_package.$callback_dto notification) { + notificationCallback.on$callback_dto(notification); + } +""") + + +def generate_callback(func_list, base_package, plugin_package, plugin_name, dto_package, callback_package, notification_package, callback_facade_package, inputfile): + callbacks = [] + for func in func_list: + + camel_case_name_with_suffix = util.underscore_to_camelcase_upper(func['name']) + + if util.is_ignored(func['name']) or util.is_control_ping(camel_case_name_with_suffix): + continue + + if util.is_reply(camel_case_name_with_suffix): + callbacks.append(jvpp_facade_callback_method_template.substitute(plugin_package=plugin_package, + dto_package=dto_package, + callback_package=callback_package, + callback=util.remove_reply_suffix(camel_case_name_with_suffix) + callback_gen.callback_suffix, + callback_dto=camel_case_name_with_suffix)) + + if util.is_notification(func["name"]): + with_notification_suffix = util.add_notification_suffix(camel_case_name_with_suffix) + callbacks.append(jvpp_facade_callback_notification_method_template.substitute(plugin_package=plugin_package, + dto_package=dto_package, + callback_package=callback_package, + callback=with_notification_suffix + callback_gen.callback_suffix, + callback_dto=with_notification_suffix)) + + jvpp_file = open(os.path.join(callback_facade_package, "CallbackJVpp%sFacadeCallback.java" % plugin_name), 'w') + jvpp_file.write(jvpp_facade_callback_template.substitute(inputfile=inputfile, + base_package=base_package, + plugin_package=plugin_package, + plugin_name=plugin_name, + dto_package=dto_package, + notification_package=notification_package, + callback_package=callback_package, + methods="".join(callbacks), + callback_facade_package=callback_facade_package)) + jvpp_file.flush() + jvpp_file.close() + + +# Returns request name or special one from unconventional_naming_rep_req map +def get_request_name(camel_case_dto_name, func_name): + if func_name in reverse_dict(util.unconventional_naming_rep_req): + request_name = util.underscore_to_camelcase_upper(reverse_dict(util.unconventional_naming_rep_req)[func_name]) + else: + request_name = camel_case_dto_name + return remove_suffix(request_name) + + +def reverse_dict(map): + return dict((v, k) for k, v in map.iteritems()) + + +def remove_suffix(name): + if util.is_reply(name): + return util.remove_reply_suffix(name) + else: + if util.is_dump(name): + return util.remove_suffix(name, util.dump_suffix) + else: + return name diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_future_facade_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_future_facade_gen.py new file mode 100644 index 00000000..26b31e22 --- /dev/null +++ b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_future_facade_gen.py @@ -0,0 +1,331 @@ +#!/usr/bin/env python +# +# 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. + +import os +from string import Template + +import dto_gen +import util + +jvpp_facade_callback_template = Template(""" +package $plugin_package.$future_package; + +/** + *

Async facade callback setting values to future objects + *
It was generated by jvpp_future_facade_gen.py based on $inputfile + *
(python representation of api file generated by vppapigen). + */ +public final class FutureJVpp${plugin_name}FacadeCallback implements $plugin_package.$callback_package.JVpp${plugin_name}GlobalCallback { + + private final java.util.Map>> requests; + private final $plugin_package.$notification_package.Global${plugin_name}NotificationCallback notificationCallback; + + public FutureJVpp${plugin_name}FacadeCallback( + final java.util.Map>> requestMap, + final $plugin_package.$notification_package.Global${plugin_name}NotificationCallback notificationCallback) { + this.requests = requestMap; + this.notificationCallback = notificationCallback; + } + + @Override + @SuppressWarnings("unchecked") + public void onError($base_package.VppCallbackException reply) { + final java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply> completableFuture; + + synchronized(requests) { + completableFuture = (java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply>) requests.get(reply.getCtxId()); + } + + if(completableFuture != null) { + completableFuture.completeExceptionally(reply); + + synchronized(requests) { + requests.remove(reply.getCtxId()); + } + } + } + + @Override + @SuppressWarnings("unchecked") + public void onControlPingReply($base_package.$dto_package.ControlPingReply reply) { + final java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply> completableFuture; + + synchronized(requests) { + completableFuture = (java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply>) requests.get(reply.context); + } + + if(completableFuture != null) { + // Finish dump call + if (completableFuture instanceof $base_package.$future_package.AbstractFutureJVppInvoker.CompletableDumpFuture) { + completableFuture.complete((($base_package.$future_package.AbstractFutureJVppInvoker.CompletableDumpFuture) completableFuture).getReplyDump()); + // Remove future mapped to dump call context id + synchronized(requests) { + requests.remove((($base_package.$future_package.AbstractFutureJVppInvoker.CompletableDumpFuture) completableFuture).getContextId()); + } + } else { + completableFuture.complete(reply); + } + + synchronized(requests) { + requests.remove(reply.context); + } + } + } + +$methods +} +""") + +jvpp_facade_callback_method_template = Template(""" + @Override + @SuppressWarnings("unchecked") + public void on$callback_dto($plugin_package.$dto_package.$callback_dto reply) { + final java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply> completableFuture; + + synchronized(requests) { + completableFuture = (java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply>) requests.get(reply.context); + } + + if(completableFuture != null) { + completableFuture.complete(reply); + + synchronized(requests) { + requests.remove(reply.context); + } + } + } +""") + +jvpp_facade_callback_notification_method_template = Template(""" + @Override + public void on$callback_dto($plugin_package.$dto_package.$callback_dto notification) { + notificationCallback.on$callback_dto(notification); + } +""") + +jvpp_facade_details_callback_method_template = Template(""" + @Override + @SuppressWarnings("unchecked") + public void on$callback_dto($plugin_package.$dto_package.$callback_dto reply) { + final $base_package.$future_package.AbstractFutureJVppInvoker.CompletableDumpFuture<$plugin_package.$dto_package.$callback_dto_reply_dump> completableFuture; + + synchronized(requests) { + completableFuture = ($base_package.$future_package.AbstractFutureJVppInvoker.CompletableDumpFuture<$plugin_package.$dto_package.$callback_dto_reply_dump>) requests.get(reply.context); + } + + if(completableFuture != null) { + completableFuture.getReplyDump().$callback_dto_field.add(reply); + } + } +""") + + +def generate_jvpp(func_list, base_package, plugin_package, plugin_name, dto_package, callback_package, notification_package, future_facade_package, inputfile): + """ Generates JVpp interface and JNI implementation """ + print "Generating JVpp future facade" + + if not os.path.exists(future_facade_package): + os.mkdir(future_facade_package) + + methods = [] + methods_impl = [] + callbacks = [] + for func in func_list: + camel_case_name_with_suffix = util.underscore_to_camelcase_upper(func['name']) + + if util.is_ignored(func['name']) or util.is_control_ping(camel_case_name_with_suffix): + continue + + if not util.is_reply(camel_case_name_with_suffix) and not util.is_notification(func['name']): + continue + + camel_case_method_name = util.underscore_to_camelcase(func['name']) + + if not util.is_notification(func["name"]): + camel_case_request_method_name = util.remove_reply_suffix(util.underscore_to_camelcase(func['name'])) + if util.is_details(camel_case_name_with_suffix): + camel_case_reply_name = get_standard_dump_reply_name(util.underscore_to_camelcase_upper(func['name']), + func['name']) + callbacks.append(jvpp_facade_details_callback_method_template.substitute(base_package=base_package, + plugin_package=plugin_package, + dto_package=dto_package, + callback_dto=camel_case_name_with_suffix, + callback_dto_field=camel_case_method_name, + callback_dto_reply_dump=camel_case_reply_name + dto_gen.dump_dto_suffix, + future_package=future_facade_package)) + + methods.append(future_jvpp_method_template.substitute(plugin_package=plugin_package, + dto_package=dto_package, + method_name=camel_case_request_method_name + + util.underscore_to_camelcase_upper(util.dump_suffix), + reply_name=camel_case_reply_name + dto_gen.dump_dto_suffix, + request_name=util.remove_reply_suffix(camel_case_reply_name) + + util.underscore_to_camelcase_upper(util.dump_suffix))) + methods_impl.append(future_jvpp_dump_method_impl_template.substitute(plugin_package=plugin_package, + dto_package=dto_package, + method_name=camel_case_request_method_name + + util.underscore_to_camelcase_upper(util.dump_suffix), + reply_name=camel_case_reply_name + dto_gen.dump_dto_suffix, + request_name=util.remove_reply_suffix(camel_case_reply_name) + + util.underscore_to_camelcase_upper(util.dump_suffix))) + else: + request_name = util.underscore_to_camelcase_upper(util.unconventional_naming_rep_req[func['name']]) \ + if func['name'] in util.unconventional_naming_rep_req else util.remove_reply_suffix(camel_case_name_with_suffix) + + methods.append(future_jvpp_method_template.substitute(plugin_package=plugin_package, + dto_package=dto_package, + method_name=camel_case_request_method_name, + reply_name=camel_case_name_with_suffix, + request_name=request_name)) + methods_impl.append(future_jvpp_method_impl_template.substitute(plugin_package=plugin_package, + dto_package=dto_package, + method_name=camel_case_request_method_name, + reply_name=camel_case_name_with_suffix, + request_name=request_name)) + + callbacks.append(jvpp_facade_callback_method_template.substitute(base_package=base_package, + plugin_package=plugin_package, + dto_package=dto_package, + callback_dto=camel_case_name_with_suffix)) + + if util.is_notification(func["name"]): + callbacks.append(jvpp_facade_callback_notification_method_template.substitute(plugin_package=plugin_package, + dto_package=dto_package, + callback_dto=util.add_notification_suffix(camel_case_name_with_suffix))) + + jvpp_file = open(os.path.join(future_facade_package, "FutureJVpp%sFacadeCallback.java" % plugin_name), 'w') + jvpp_file.write(jvpp_facade_callback_template.substitute(inputfile=inputfile, + base_package=base_package, + plugin_package=plugin_package, + plugin_name=plugin_name, + dto_package=dto_package, + notification_package=notification_package, + callback_package=callback_package, + methods="".join(callbacks), + future_package=future_facade_package)) + jvpp_file.flush() + jvpp_file.close() + + jvpp_file = open(os.path.join(future_facade_package, "FutureJVpp%s.java" % plugin_name), 'w') + jvpp_file.write(future_jvpp_template.substitute(inputfile=inputfile, + base_package=base_package, + plugin_package=plugin_package, + plugin_name=plugin_name, + notification_package=notification_package, + methods="".join(methods), + future_package=future_facade_package)) + jvpp_file.flush() + jvpp_file.close() + + jvpp_file = open(os.path.join(future_facade_package, "FutureJVpp%sFacade.java" % plugin_name), 'w') + jvpp_file.write(future_jvpp_facade_template.substitute(inputfile=inputfile, + base_package=base_package, + plugin_package=plugin_package, + plugin_name=plugin_name, + dto_package=dto_package, + notification_package=notification_package, + methods="".join(methods_impl), + future_package=future_facade_package)) + jvpp_file.flush() + jvpp_file.close() + + +future_jvpp_template = Template(''' +package $plugin_package.$future_package; + +/** + *

Async facade extension adding specific methods for each request invocation + *
It was generated by jvpp_future_facade_gen.py based on $inputfile + *
(python representation of api file generated by vppapigen). + */ +public interface FutureJVpp${plugin_name} extends $base_package.$future_package.FutureJVppInvoker { +$methods + + @Override + public $plugin_package.$notification_package.${plugin_name}NotificationRegistry getNotificationRegistry(); + +} +''') + +future_jvpp_method_template = Template(''' + java.util.concurrent.CompletionStage<$plugin_package.$dto_package.$reply_name> $method_name($plugin_package.$dto_package.$request_name request); +''') + + +future_jvpp_facade_template = Template(''' +package $plugin_package.$future_package; + +/** + *

Implementation of FutureJVpp based on AbstractFutureJVppInvoker + *
It was generated by jvpp_future_facade_gen.py based on $inputfile + *
(python representation of api file generated by vppapigen). + */ +public class FutureJVpp${plugin_name}Facade extends $base_package.$future_package.AbstractFutureJVppInvoker implements FutureJVpp${plugin_name} { + + private final $plugin_package.$notification_package.${plugin_name}NotificationRegistryImpl notificationRegistry = new $plugin_package.$notification_package.${plugin_name}NotificationRegistryImpl(); + + /** + *

Create FutureJVpp${plugin_name}Facade object for provided JVpp instance. + * Constructor internally creates FutureJVppFacadeCallback class for processing callbacks + * and then connects to provided JVpp instance + * + * @param jvpp provided $base_package.JVpp instance + * + * @throws java.io.IOException in case instance cannot connect to JVPP + */ + public FutureJVpp${plugin_name}Facade(final $base_package.JVppRegistry registry, final $base_package.JVpp jvpp) throws java.io.IOException { + super(jvpp, registry, new java.util.HashMap<>()); + java.util.Objects.requireNonNull(registry, "JVppRegistry should not be null"); + registry.register(jvpp, new FutureJVpp${plugin_name}FacadeCallback(getRequests(), notificationRegistry)); + } + + @Override + public $plugin_package.$notification_package.${plugin_name}NotificationRegistry getNotificationRegistry() { + return notificationRegistry; + } + +$methods +} +''') + +future_jvpp_method_impl_template = Template(''' + @Override + public java.util.concurrent.CompletionStage<$plugin_package.$dto_package.$reply_name> $method_name($plugin_package.$dto_package.$request_name request) { + return send(request); + } +''') + +future_jvpp_dump_method_impl_template = Template(''' + @Override + public java.util.concurrent.CompletionStage<$plugin_package.$dto_package.$reply_name> $method_name($plugin_package.$dto_package.$request_name request) { + return send(request, new $plugin_package.$dto_package.$reply_name()); + } +''') + + +# Returns request name or special one from unconventional_naming_rep_req map +def get_standard_dump_reply_name(camel_case_dto_name, func_name): + # FIXME this is a hotfix for sub-details callbacks + # FIXME also for L2FibTableEntry + # It's all because unclear mapping between + # request -> reply, + # dump -> reply, details, + # notification_start -> reply, notifications + + # vpe.api needs to be "standardized" so we can parse the information and create maps before generating java code + suffix = func_name.split("_")[-1] + return util.underscore_to_camelcase_upper( + util.unconventional_naming_rep_req[func_name]) + util.underscore_to_camelcase_upper(suffix) if func_name in util.unconventional_naming_rep_req \ + else camel_case_dto_name diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_impl_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_impl_gen.py new file mode 100644 index 00000000..7bf91138 --- /dev/null +++ b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_impl_gen.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python +# +# 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. + +import os, util +from string import Template + +jvpp_ifc_template = Template(""" +package $plugin_package; + +/** + *

Java representation of plugin's api file. + *
It was generated by jvpp_impl_gen.py based on $inputfile + *
(python representation of api file generated by vppapigen). + */ +public interface JVpp${plugin_name} extends $base_package.JVpp { + + /** + * Generic dispatch method for sending requests to VPP + * + * @throws io.fd.vpp.jvpp.VppInvocationException if send request had failed + */ + int send($base_package.$dto_package.JVppRequest request) throws io.fd.vpp.jvpp.VppInvocationException; + +$methods +} +""") + +jvpp_impl_template = Template(""" +package $plugin_package; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; +import java.util.Set; +import java.util.logging.Logger; +import $base_package.callback.JVppCallback; +import $base_package.VppConnection; +import $base_package.JVppRegistry; + +/** + *

Default implementation of JVpp interface. + *
It was generated by jvpp_impl_gen.py based on $inputfile + *
(python representation of api file generated by vppapigen). + */ +public final class JVpp${plugin_name}Impl implements $plugin_package.JVpp${plugin_name} { + + private final static Logger LOG = Logger.getLogger(JVpp${plugin_name}Impl.class.getName()); + private static final String LIBNAME = "libjvpp_${plugin_name_underscore}.so"; + + // FIXME using NativeLibraryLoader makes load fail could not find (WantInterfaceEventsReply). + static { + try { + loadLibrary(); + } catch (Exception e) { + LOG.severe("Can't find jvpp jni library: " + LIBNAME); + throw new ExceptionInInitializerError(e); + } + } + + private static void loadStream(final InputStream is) throws IOException { + final Set perms = PosixFilePermissions.fromString("rwxr-x---"); + final Path p = Files.createTempFile(LIBNAME, null, PosixFilePermissions.asFileAttribute(perms)); + try { + Files.copy(is, p, StandardCopyOption.REPLACE_EXISTING); + + try { + Runtime.getRuntime().load(p.toString()); + } catch (UnsatisfiedLinkError e) { + throw new IOException("Failed to load library " + p, e); + } + } finally { + try { + Files.deleteIfExists(p); + } catch (IOException e) { + } + } + } + + private static void loadLibrary() throws IOException { + try (final InputStream is = JVpp${plugin_name}Impl.class.getResourceAsStream('/' + LIBNAME)) { + if (is == null) { + throw new IOException("Failed to open library resource " + LIBNAME); + } + loadStream(is); + } + } + + private VppConnection connection; + private JVppRegistry registry; + + private static native void init0(final JVppCallback callback, final long queueAddress, final int clientIndex); + @Override + public void init(final JVppRegistry registry, final JVppCallback callback, final long queueAddress, final int clientIndex) { + this.registry = java.util.Objects.requireNonNull(registry, "registry should not be null"); + this.connection = java.util.Objects.requireNonNull(registry.getConnection(), "connection should not be null"); + connection.checkActive(); + init0(callback, queueAddress, clientIndex); + } + + private static native void close0(); + @Override + public void close() { + close0(); + } + + @Override + public int send($base_package.$dto_package.JVppRequest request) throws io.fd.vpp.jvpp.VppInvocationException { + return request.send(this); + } + + @Override + public final int controlPing(final io.fd.vpp.jvpp.dto.ControlPing controlPing) throws io.fd.vpp.jvpp.VppInvocationException { + return registry.controlPing(JVpp${plugin_name}Impl.class); + } + +$methods +} +""") + +method_template = Template(""" int $name($plugin_package.$dto_package.$request request) throws io.fd.vpp.jvpp.VppInvocationException;""") +method_native_template = Template( + """ private static native int ${name}0($plugin_package.$dto_package.$request request);""") +method_impl_template = Template(""" public final int $name($plugin_package.$dto_package.$request request) throws io.fd.vpp.jvpp.VppInvocationException { + java.util.Objects.requireNonNull(request,"Null request object"); + connection.checkActive(); + int result=${name}0(request); + if(result<0){ + throw new io.fd.vpp.jvpp.VppInvocationException("${name}",result); + } + return result; + } +""") + +no_arg_method_template = Template(""" int $name() throws io.fd.vpp.jvpp.VppInvocationException;""") +no_arg_method_native_template = Template(""" private static native int ${name}0() throws io.fd.vpp.jvpp.VppInvocationException;""") +no_arg_method_impl_template = Template(""" public final int $name() throws io.fd.vpp.jvpp.VppInvocationException { + connection.checkActive(); + int result=${name}0(); + if(result<0){ + throw new io.fd.vpp.jvpp.VppInvocationException("${name}",result); + } + return result; + } +""") + + +def generate_jvpp(func_list, base_package, plugin_package, plugin_name_underscore, dto_package, inputfile): + """ Generates JVpp interface and JNI implementation """ + print "Generating JVpp" + plugin_name = util.underscore_to_camelcase_upper(plugin_name_underscore) + + methods = [] + methods_impl = [] + for func in func_list: + + # Skip structures that are used only as notifications + if util.is_just_notification(func['name']) or util.is_ignored(func['name']): + continue + + camel_case_name = util.underscore_to_camelcase(func['name']) + camel_case_name_upper = util.underscore_to_camelcase_upper(func['name']) + if util.is_reply(camel_case_name): + continue + + if len(func['args']) == 0: + methods.append(no_arg_method_template.substitute(name=camel_case_name)) + methods_impl.append(no_arg_method_native_template.substitute(name=camel_case_name)) + methods_impl.append(no_arg_method_impl_template.substitute(name=camel_case_name)) + else: + methods.append(method_template.substitute(name=camel_case_name, + request=camel_case_name_upper, + plugin_package=plugin_package, + dto_package=dto_package)) + methods_impl.append(method_native_template.substitute(name=camel_case_name, + request=camel_case_name_upper, + plugin_package=plugin_package, + dto_package=dto_package)) + methods_impl.append(method_impl_template.substitute(name=camel_case_name, + request=camel_case_name_upper, + plugin_package=plugin_package, + dto_package=dto_package)) + + jvpp_file = open("JVpp%s.java" % plugin_name, 'w') + jvpp_file.write( + jvpp_ifc_template.substitute(inputfile=inputfile, + methods="\n".join(methods), + base_package=base_package, + plugin_package=plugin_package, + plugin_name=plugin_name, + dto_package=dto_package)) + jvpp_file.flush() + jvpp_file.close() + + jvpp_file = open("JVpp%sImpl.java" % plugin_name, 'w') + jvpp_file.write(jvpp_impl_template.substitute(inputfile=inputfile, + methods="\n".join(methods_impl), + base_package=base_package, + plugin_package=plugin_package, + plugin_name=plugin_name, + plugin_name_underscore=plugin_name_underscore, + dto_package=dto_package)) + jvpp_file.flush() + jvpp_file.close() diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/notification_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/notification_gen.py new file mode 100644 index 00000000..94302d56 --- /dev/null +++ b/src/vpp-api/java/jvpp/gen/jvppgen/notification_gen.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python +# +# 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. + +import os + +import callback_gen +import util +from string import Template + +notification_registry_template = Template(""" +package $plugin_package.$notification_package; + +/** + *

Registry for notification callbacks defined in ${plugin_name}. + *
It was generated by notification_gen.py based on $inputfile + *
(python representation of api file generated by vppapigen). + */ +public interface ${plugin_name}NotificationRegistry extends $base_package.$notification_package.NotificationRegistry { + + $register_callback_methods + + @Override + void close(); +} +""") + +global_notification_callback_template = Template(""" +package $plugin_package.$notification_package; + +/** + *

Aggregated callback interface for notifications only. + *
It was generated by notification_gen.py based on $inputfile + *
(python representation of api file generated by vppapigen). + */ +public interface Global${plugin_name}NotificationCallback$callbacks { + +} +""") + +notification_registry_impl_template = Template(""" +package $plugin_package.$notification_package; + +/** + *

Notification registry delegating notification processing to registered callbacks. + *
It was generated by notification_gen.py based on $inputfile + *
(python representation of api file generated by vppapigen). + */ +public final class ${plugin_name}NotificationRegistryImpl implements ${plugin_name}NotificationRegistry, Global${plugin_name}NotificationCallback { + + // TODO add a special NotificationCallback interface and only allow those to be registered + private final java.util.concurrent.ConcurrentMap, $base_package.$callback_package.JVppNotificationCallback> registeredCallbacks = + new java.util.concurrent.ConcurrentHashMap<>(); + + $register_callback_methods + $handler_methods + + @Override + public void close() { + registeredCallbacks.clear(); + } +} +""") + +register_callback_impl_template = Template(""" + public java.lang.AutoCloseable register$callback(final $plugin_package.$callback_package.$callback callback){ + if(null != registeredCallbacks.putIfAbsent($plugin_package.$dto_package.$notification.class, callback)){ + throw new IllegalArgumentException("Callback for " + $plugin_package.$dto_package.$notification.class + + "notification already registered"); + } + return () -> registeredCallbacks.remove($plugin_package.$dto_package.$notification.class); + } +""") + +handler_impl_template = Template(""" + @Override + public void on$notification( + final $plugin_package.$dto_package.$notification notification) { + final $base_package.$callback_package.JVppNotificationCallback jVppNotificationCallback = registeredCallbacks.get($plugin_package.$dto_package.$notification.class); + if (null != jVppNotificationCallback) { + (($plugin_package.$callback_package.$callback) registeredCallbacks + .get($plugin_package.$dto_package.$notification.class)) + .on$notification(notification); + } + } +""") + +notification_provider_template = Template(""" +package $plugin_package.$notification_package; + + /** + * Provides ${plugin_name}NotificationRegistry. + *
The file was generated by notification_gen.py based on $inputfile + *
(python representation of api file generated by vppapigen). + */ +public interface ${plugin_name}NotificationRegistryProvider extends $base_package.$notification_package.NotificationRegistryProvider { + + @Override + public ${plugin_name}NotificationRegistry getNotificationRegistry(); +} +""") + + +def generate_notification_registry(func_list, base_package, plugin_package, plugin_name, notification_package, callback_package, dto_package, inputfile): + """ Generates notification registry interface and implementation """ + print "Generating Notification interfaces and implementation" + + if not os.path.exists(notification_package): + os.mkdir(notification_package) + + callbacks = [] + register_callback_methods = [] + register_callback_methods_impl = [] + handler_methods = [] + for func in func_list: + + if not util.is_notification(func['name']): + continue + + camel_case_name_with_suffix = util.underscore_to_camelcase_upper(func['name']) + notification_dto = util.add_notification_suffix(camel_case_name_with_suffix) + callback_ifc = notification_dto + callback_gen.callback_suffix + fully_qualified_callback_ifc = "{0}.{1}.{2}".format(plugin_package, callback_package, callback_ifc) + callbacks.append(fully_qualified_callback_ifc) + + # TODO create NotificationListenerRegistration and return that instead of AutoCloseable to better indicate + # that the registration should be closed + register_callback_methods.append("java.lang.AutoCloseable register{0}({1} callback);" + .format(callback_ifc, fully_qualified_callback_ifc)) + register_callback_methods_impl.append(register_callback_impl_template.substitute(plugin_package=plugin_package, + callback_package=callback_package, + dto_package=dto_package, + notification=notification_dto, + callback=callback_ifc)) + handler_methods.append(handler_impl_template.substitute(base_package=base_package, + plugin_package=plugin_package, + callback_package=callback_package, + dto_package=dto_package, + notification=notification_dto, + callback=callback_ifc)) + + + callback_file = open(os.path.join(notification_package, "%sNotificationRegistry.java" % plugin_name), 'w') + callback_file.write(notification_registry_template.substitute(inputfile=inputfile, + register_callback_methods="\n ".join(register_callback_methods), + base_package=base_package, + plugin_package=plugin_package, + plugin_name=plugin_name, + notification_package=notification_package)) + callback_file.flush() + callback_file.close() + + callback_file = open(os.path.join(notification_package, "Global%sNotificationCallback.java" % plugin_name), 'w') + + global_notification_callback_callbacks = "" + if (callbacks): + global_notification_callback_callbacks = " extends " + ", ".join(callbacks) + + callback_file.write(global_notification_callback_template.substitute(inputfile=inputfile, + callbacks=global_notification_callback_callbacks, + plugin_package=plugin_package, + plugin_name=plugin_name, + notification_package=notification_package)) + callback_file.flush() + callback_file.close() + + callback_file = open(os.path.join(notification_package, "%sNotificationRegistryImpl.java" % plugin_name), 'w') + callback_file.write(notification_registry_impl_template.substitute(inputfile=inputfile, + callback_package=callback_package, + dto_package=dto_package, + register_callback_methods="".join(register_callback_methods_impl), + handler_methods="".join(handler_methods), + base_package=base_package, + plugin_package=plugin_package, + plugin_name=plugin_name, + notification_package=notification_package)) + callback_file.flush() + callback_file.close() + + callback_file = open(os.path.join(notification_package, "%sNotificationRegistryProvider.java" % plugin_name), 'w') + callback_file.write(notification_provider_template.substitute(inputfile=inputfile, + base_package=base_package, + plugin_package=plugin_package, + plugin_name=plugin_name, + notification_package=notification_package)) + callback_file.flush() + callback_file.close() + diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/types_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/types_gen.py new file mode 100644 index 00000000..7a5eec37 --- /dev/null +++ b/src/vpp-api/java/jvpp/gen/jvppgen/types_gen.py @@ -0,0 +1,227 @@ +#!/usr/bin/env python +# +# 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. + +import os +from string import Template + +import util +import jni_gen +import dto_gen + +type_template = Template(""" +package $plugin_package.$type_package; + +/** + *

This class represents $c_type_name type definition. + *
It was generated by types_gen.py based on $inputfile preparsed data: + *

+$docs
+ * 
+ */ +public final class $java_type_name { +$fields +$methods +} +""") + +field_template = Template(""" public $type $name;\n""") + + +def generate_type_fields(type_definition): + """ + Generates fields for class representing typeonly definition + :param type_definition: python representation of typeonly definition + :return: string representing class fields + """ + fields = "" + for t in zip(type_definition['types'], type_definition['args']): + field_name = util.underscore_to_camelcase(t[1]) + fields += field_template.substitute(type=util.jni_2_java_type_mapping[t[0]], + name=field_name) + return fields + +object_struct_setter_template = Template(""" + { + jclass ${field_reference_name}Class = (*env)->FindClass(env, "${class_FQN}"); + memset (&(mp->${c_name}), 0, sizeof (mp->${c_name})); + ${struct_initialization} + } +""") + +object_array_struct_setter_template = Template(""" + { + jclass ${field_reference_name}ArrayElementClass = (*env)->FindClass(env, "${class_FQN}"); + if (${field_reference_name}) { + size_t _i; + jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name}); + ${field_length_check} + for (_i = 0; _i < cnt; _i++) { + jobject ${field_reference_name}ArrayElement = (*env)->GetObjectArrayElement(env, ${field_reference_name}, _i); + memset (&(mp->${c_name}[_i]), 0, sizeof (mp->${c_name}[_i])); + ${struct_initialization} + } + } + } +""") + +object_dto_field_setter_template = Template(""" + { + jclass ${field_reference_name}Class = (*env)->FindClass(env, "${class_FQN}"); + jmethodID ${field_reference_name}Constructor = (*env)->GetMethodID(env, ${field_reference_name}Class, "", "()V"); + jobject ${field_reference_name} = (*env)->NewObject(env, ${field_reference_name}Class, ${field_reference_name}Constructor); + ${type_initialization} + (*env)->SetObjectField(env, dto, ${field_reference_name}FieldId, ${field_reference_name}); + } +""") + +object_array_dto_field_setter_template = Template(""" + { + jclass ${field_reference_name}Class = (*env)->FindClass(env, "${class_FQN}"); + jobjectArray ${field_reference_name} = (*env)->NewObjectArray(env, ${field_length}, ${field_reference_name}Class, 0); + unsigned int _i; + for (_i = 0; _i < ${field_length}; _i++) { + jmethodID ${field_reference_name}Constructor = (*env)->GetMethodID(env, ${field_reference_name}Class, "", "()V"); + jobject ${field_reference_name}ArrayElement = (*env)->NewObject(env, ${field_reference_name}Class, ${field_reference_name}Constructor); + ${type_initialization} + (*env)->SetObjectArrayElement(env, ${field_reference_name}, _i, ${field_reference_name}ArrayElement); + } + (*env)->SetObjectField(env, dto, ${field_reference_name}FieldId, ${field_reference_name}); + } +""") + + +def generate_struct_initialization(type_def, c_name_prefix, object_name, indent): + struct_initialization = "" + # field identifiers + for t in zip(type_def['types'], type_def['args'], type_def['lengths']): + field_reference_name = "${c_name}" + util.underscore_to_camelcase_upper(t[1]) + field_name = util.underscore_to_camelcase(t[1]) + struct_initialization += jni_gen.jni_request_binding_for_type(field_type=t[0], c_name=c_name_prefix + t[1], + field_reference_name=field_reference_name, + field_name=field_name, + field_length=t[2][0], + is_variable_len_array=t[2][1], + object_name=object_name) + return indent + struct_initialization.replace('\n', '\n' + indent) + + +def generate_type_setter(handler_name, type_def, c_name_prefix, object_name, indent): + type_initialization = "" + for t in zip(type_def['types'], type_def['args'], type_def['lengths']): + field_length = t[2][0] + is_variable_len_array = t[2][1] + length_field_type = None + if is_variable_len_array: + length_field_type = type_def['types'][type_def['args'].index(field_length)] + type_initialization += jni_gen.jni_reply_handler_for_type(handler_name=handler_name, + ref_name="${field_reference_name}", + field_type=t[0], c_name=c_name_prefix + t[1], + field_reference_name="${c_name}" + util.underscore_to_camelcase_upper(t[1]), + field_name=util.underscore_to_camelcase(t[1]), + field_length=field_length, + is_variable_len_array=is_variable_len_array, + length_field_type=length_field_type, + object_name=object_name) + return indent + type_initialization.replace('\n', '\n' + indent) + + +def generate_types(types_list, plugin_package, types_package, inputfile): + """ + Generates Java representation of custom types defined in api file. + """ + + # + if not types_list: + print "Skipping custom types generation (%s does not define custom types)." % inputfile + return + + print "Generating custom types" + + if not os.path.exists(types_package): + os.mkdir(types_package) + + for type in types_list: + c_type_name = type['name'] + java_type_name = util.underscore_to_camelcase_upper(type['name']) + dto_path = os.path.join(types_package, java_type_name + ".java") + + fields = generate_type_fields(type) + + dto_file = open(dto_path, 'w') + dto_file.write(type_template.substitute(plugin_package=plugin_package, + type_package=types_package, + c_type_name=c_type_name, + inputfile=inputfile, + docs=util.api_message_to_javadoc(type), + java_type_name=java_type_name, + fields=fields, + methods=dto_gen.generate_dto_base_methods(java_type_name, type) + )) + + # update type mappings: + # todo fix vpe.api to use type_name instead of vl_api_type_name_t + type_name = "vl_api_" + c_type_name + "_t" + java_fqn = "%s.%s.%s" % (plugin_package, types_package, java_type_name) + util.vpp_2_jni_type_mapping[type_name] = "jobject" + util.vpp_2_jni_type_mapping[type_name + "[]"] = "jobjectArray" + util.jni_2_java_type_mapping[type_name] = java_fqn + util.jni_2_java_type_mapping[type_name + "[]"] = java_fqn + "[]" + jni_name = java_fqn.replace('.', "/") + jni_signature = "L" + jni_name + ";" + util.jni_2_signature_mapping[type_name] = "L" + jni_name + ";" + util.jni_2_signature_mapping[type_name + "[]"] = "[" + jni_signature + util.jni_field_accessors[type_name] = "ObjectField" + util.jni_field_accessors[type_name + "[]"] = "ObjectField" + + jni_gen.struct_setter_templates[type_name] = Template( + object_struct_setter_template.substitute( + c_name="${c_name}", + field_reference_name="${field_reference_name}", + class_FQN=jni_name, + struct_initialization=generate_struct_initialization(type, "${c_name}.", + "${field_reference_name}", ' ' * 4)) + ) + + jni_gen.struct_setter_templates[type_name+ "[]"] = Template( + object_array_struct_setter_template.substitute( + c_name="${c_name}", + field_reference_name="${field_reference_name}", + field_length_check="${field_length_check}", + class_FQN=jni_name, + struct_initialization=generate_struct_initialization(type, "${c_name}[_i].", + "${field_reference_name}ArrayElement", ' ' * 8)) + ) + + jni_gen.dto_field_setter_templates[type_name] = Template( + object_dto_field_setter_template.substitute( + field_reference_name="${field_reference_name}", + field_length="${field_length}", + class_FQN=jni_name, + type_initialization=generate_type_setter(c_type_name, type, "${c_name}.", + "${field_reference_name}", ' ' * 4)) + ) + + jni_gen.dto_field_setter_templates[type_name + "[]"] = Template( + object_array_dto_field_setter_template.substitute( + field_reference_name="${field_reference_name}", + field_length="${field_length}", + class_FQN=jni_name, + type_initialization=generate_type_setter(c_type_name, type, "${c_name}[_i].", + "${field_reference_name}ArrayElement", ' ' * 8)) + ) + + dto_file.flush() + dto_file.close() + diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/util.py b/src/vpp-api/java/jvpp/gen/jvppgen/util.py new file mode 100644 index 00000000..fc971c17 --- /dev/null +++ b/src/vpp-api/java/jvpp/gen/jvppgen/util.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python +# +# 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. + +import os, pprint +from os import removedirs + + +def underscore_to_camelcase(name): + name = name.title().replace("_", "") + return name[0].lower() + name[1:] + + +def underscore_to_camelcase_upper(name): + name = name.title().replace("_", "") + return name[0].upper() + name[1:] + + +def remove_folder(folder): + """ Remove folder with all its files """ + for root, dirs, files in os.walk(folder, topdown=False): + for name in files: + os.remove(os.path.join(root, name)) + removedirs(folder) + + +reply_suffixes = ("reply", "details", "l2fibtableentry") + + +def is_reply(name): + return name.lower().endswith(reply_suffixes) + + +def is_details(name): + return name.lower().endswith(reply_suffixes[1]) or name.lower().endswith(reply_suffixes[2]) + + +def is_retval_field(name): + return name == 'retval' + +dump_suffix = "dump" + + +def is_dump(name): + return name.lower().endswith(dump_suffix) + + +def get_reply_suffix(name): + for reply_suffix in reply_suffixes: + if name.lower().endswith(reply_suffix): + if reply_suffix == reply_suffixes[2]: + # FIXME workaround for l2_fib_table_entry + return 'entry' + else: + return reply_suffix + +# Mapping according to: +# http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html +# +# Unsigned types are converted to signed java types that have the same size. +# It is the API user responsibility to interpret them correctly. +jni_2_java_type_mapping = {'u8': 'byte', + 'u8[]': 'byte[]', + 'i8': 'byte', + 'i8[]': 'byte[]', + 'u16': 'short', + 'u16[]': 'short[]', + 'i16': 'short', + 'i16[]': 'short[]', + 'u32': 'int', + 'u32[]': 'int[]', + 'i32': 'int', + 'i32[]': 'int[]', + 'u64': 'long', + 'u64[]': 'long[]', + 'i64': 'long', + 'i64[]': 'long[]', + 'f64': 'double', + 'f64[]': 'double[]' + } + +vpp_2_jni_type_mapping = {'u8': 'jbyte', + 'u8[]': 'jbyteArray', + 'i8': 'jbyte', + 'u8[]': 'jbyteArray', + 'u16': 'jshort', + 'u16[]': 'jshortArray', + 'i16': 'jshort', + 'i16[]': 'jshortArray', + 'u32': 'jint', + 'u32[]': 'jintArray', + 'i32': 'jint', + 'i32[]': 'jintArray', + 'u64': 'jlong', + 'u64[]': 'jlongArray', + 'i64': 'jlong', + 'i64[]': 'jlongArray', + 'f64': 'jdouble', + 'f64[]': 'jdoubleArray' + } + +# https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html#type_signatures +jni_2_signature_mapping = {'u8': 'B', + 'u8[]': '[B', + 'i8': 'B', + 'i8[]': '[B', + 'u16': 'S', + 'u16[]': '[S', + 'i16': 'S', + 'i16[]': '[S', + 'u32': 'I', + 'u32[]': '[I', + 'i32': 'I', + 'i32[]': '[I', + 'u64': 'J', + 'u64[]': '[J', + 'i64': 'J', + 'i64[]': '[J', + 'f64': 'D', + 'f64[]': '[D' + } + +# https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#Get_type_Field_routines +jni_field_accessors = {'u8': 'ByteField', + 'u8[]': 'ObjectField', + 'i8': 'ByteField', + 'i8[]': 'ObjectField', + 'u16': 'ShortField', + 'u16[]': 'ObjectField', + 'i16': 'ShortField', + 'i16[]': 'ObjectField', + 'u32': 'IntField', + 'u32[]': 'ObjectField', + 'i32': 'IntField', + 'i32[]': 'ObjectField', + 'u64': 'LongField', + 'u64[]': 'ObjectField', + 'i64': 'LongField', + 'i64[]': 'ObjectField', + 'f64': 'DoubleField', + 'f64[]': 'ObjectField' + } + + +# vpe.api calls that do not follow naming conventions and have to be handled exceptionally when finding reply -> request mapping +# FIXME in vpe.api +unconventional_naming_rep_req = { + 'cli_reply': 'cli_request', + 'vnet_summary_stats_reply': 'vnet_get_summary_stats', + # This below is actually a sub-details callback. We cannot derive the mapping of dump request + # belonging to this sub-details from naming conventions. We need special mapping + 'bridge_domain_sw_if_details': 'bridge_domain', + # This is standard dump call + details reply. However it's not called details but entry + 'l2_fib_table_entry': 'l2_fib_table' + } + +# +# FIXME no convention in the naming of events (notifications) in vpe.api +notifications_message_suffixes = ("event", "counters") +notification_messages_reused = ["sw_interface_set_flags"] + +# messages that must be ignored. These messages are INSUFFICIENTLY marked as disabled in vpe.api +# FIXME +ignored_messages = ["is_address_reachable"] + + +def is_notification(name): + """ Returns true if the structure is a notification regardless of its no other use """ + return is_just_notification(name) or name.lower() in notification_messages_reused + + +def is_just_notification(name): + """ Returns true if the structure is just a notification and has no other use """ + return name.lower().endswith(notifications_message_suffixes) + + +def is_ignored(param): + return param.lower() in ignored_messages + + +def remove_reply_suffix(camel_case_name_with_suffix): + return remove_suffix(camel_case_name_with_suffix, get_reply_suffix(camel_case_name_with_suffix)) + + +def remove_suffix(camel_case_name_with_suffix, suffix): + suffix_length = len(suffix) + return camel_case_name_with_suffix[:-suffix_length] if suffix_length != 0 else camel_case_name_with_suffix + + +def is_control_ping(camel_case_name_with_suffix): + return camel_case_name_with_suffix.lower().startswith("controlping"); + + +def api_message_to_javadoc(api_message): + """ Converts vpe.api message description to javadoc """ + str = pprint.pformat(api_message, indent=4, width=120, depth=None) + return " * " + str.replace("\n", "\n * ") + + +notification_dto_suffix = "Notification" + + +def add_notification_suffix(camel_case_dto_name): + camel_case_dto_name += notification_dto_suffix + return camel_case_dto_name + + +def is_array(java_type_as_string): + return java_type_as_string.endswith("[]") -- cgit 1.2.3-korg From 71f8742a1f998948ab3fc9ae4560be1885be2950 Mon Sep 17 00:00:00 2001 From: Damjan Marion Date: Tue, 3 Jan 2017 11:17:19 +0100 Subject: jvpp: install jvpp_common.h into include/ Change-Id: Iaffc619f3219239e753609f0963d396237aae378 Signed-off-by: Damjan Marion --- src/vpp-api/java/Makefile.am | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/vpp-api/java') diff --git a/src/vpp-api/java/Makefile.am b/src/vpp-api/java/Makefile.am index c7051ec1..bc693651 100644 --- a/src/vpp-api/java/Makefile.am +++ b/src/vpp-api/java/Makefile.am @@ -30,6 +30,9 @@ CLEANDIRS = # jvpp-common # +nobase_include_HEADERS = \ + jvpp-common/jvpp_common.h + noinst_LTLIBRARIES += libjvpp_common.la libjvpp_common_la_SOURCES = jvpp-common/jvpp_common.c libjvpp_common_la_LDFLAGS = -- cgit 1.2.3-korg From ef6e54de4a52b1f8b37c6f074b6803e54d5db327 Mon Sep 17 00:00:00 2001 From: Jan Srnicek Date: Thu, 5 Jan 2017 12:42:00 +0100 Subject: Added vpe.api java binding generation added missing path for vpe.api file Change-Id: I61f727002dd663d6a5656b0d01380e77b44c4a42 Signed-off-by: Jan Srnicek --- src/vpp-api/java/Makefile.am | 1 + 1 file changed, 1 insertion(+) (limited to 'src/vpp-api/java') diff --git a/src/vpp-api/java/Makefile.am b/src/vpp-api/java/Makefile.am index bc693651..117616b1 100644 --- a/src/vpp-api/java/Makefile.am +++ b/src/vpp-api/java/Makefile.am @@ -91,6 +91,7 @@ BUILT_SOURCES += jvpp-core/io_fd_vpp_jvpp_core_JVppCoreImpl.h JAR_FILES += jvpp-core-$(PACKAGE_VERSION).jar CLEANDIRS += jvpp-core/target jvpp_core_json_files = $(shell find @top_builddir@/vnet/ -type f -name '*.api.json') +jvpp_core_json_files += @top_builddir@/vpp/api/vpe.api.json jvpp-core/io_fd_vpp_jvpp_core_JVppCoreImpl.h: $(jvpp_registry_ok) $(jvpp_core_json_files) $(call japigen,core,JVppCoreImpl) -- cgit 1.2.3-korg From ffc18f5ef789c525e43706d643ad853cd941f4dd Mon Sep 17 00:00:00 2001 From: Damjan Marion Date: Thu, 5 Jan 2017 22:10:01 +0100 Subject: japi: include tests in jar files Change-Id: Idffaafda8fac06a45edef6f3ef13379293d0faa0 Signed-off-by: Damjan Marion --- src/vpp-api/java/Makefile.am | 1 + 1 file changed, 1 insertion(+) (limited to 'src/vpp-api/java') diff --git a/src/vpp-api/java/Makefile.am b/src/vpp-api/java/Makefile.am index 117616b1..2bb7f669 100644 --- a/src/vpp-api/java/Makefile.am +++ b/src/vpp-api/java/Makefile.am @@ -188,6 +188,7 @@ jvpp-%-$(PACKAGE_VERSION).jar: libjvpp_%.la @echo " JAR $@" @cp .libs/libjvpp_$*.so jvpp-$*/target @$(JAR) cf $(JARFLAGS) $@ -C jvpp-$*/target . + @$(JAR) uf $(JARFLAGS) $@ -C $(srcdir)/jvpp-$* io jardir = $(prefix)/share/java jar_DATA = $(JAR_FILES) -- cgit 1.2.3-korg From 94f8bc12f59d346066f70413911cd491e7cacce7 Mon Sep 17 00:00:00 2001 From: Marek Gradzki Date: Tue, 10 Jan 2017 16:43:53 +0100 Subject: jvpp: compile static Java files and include in generated jars Most Java bindings for VPP APIs provide some tests/examples. The patch includes them in the compilation process. Change-Id: Icef3d061c1afd727e8544a7fe5c0204453b1c1d0 Signed-off-by: Marek Gradzki --- src/vpp-api/java/Makefile.am | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/vpp-api/java') diff --git a/src/vpp-api/java/Makefile.am b/src/vpp-api/java/Makefile.am index 2bb7f669..bfb5665a 100644 --- a/src/vpp-api/java/Makefile.am +++ b/src/vpp-api/java/Makefile.am @@ -72,9 +72,10 @@ define japigen @rm -rf jvpp-$(1)/target @ @srcdir@/jvpp/gen/jvpp_gen.py --plugin_name $(1) --root_dir jvpp-$(1) \ -i $(jvpp_$(1)_json_files) > /dev/null - @find jvpp-$(1)/target -name \*.java > jvpp-$(1).files + @find jvpp-$(1)/target -name \*.java > jvpp-$(1).generated.files + @find @srcdir@/jvpp-$(1) -name \*.java > jvpp-$(1).static.files @$(JAVAC) -classpath jvpp-registry/target \ - -d jvpp-$(1)/target @jvpp-$(1).files + -d jvpp-$(1)/target @jvpp-$(1).generated.files @jvpp-$(1).static.files @$(JAVAH) -force \ -classpath jvpp-registry/target:jvpp-$(1)/target \ -d jvpp-$(1) io.fd.vpp.jvpp.$(1).$(2) @@ -188,7 +189,6 @@ jvpp-%-$(PACKAGE_VERSION).jar: libjvpp_%.la @echo " JAR $@" @cp .libs/libjvpp_$*.so jvpp-$*/target @$(JAR) cf $(JARFLAGS) $@ -C jvpp-$*/target . - @$(JAR) uf $(JARFLAGS) $@ -C $(srcdir)/jvpp-$* io jardir = $(prefix)/share/java jar_DATA = $(JAR_FILES) -- cgit 1.2.3-korg From 724f64ccf691e170973bdcdc6c09bfdc7e2ab5e4 Mon Sep 17 00:00:00 2001 From: Damjan Marion Date: Wed, 11 Jan 2017 11:11:00 +0100 Subject: Makefile.am cleanup - remove unused stuff - add --quiet flag to libtool - avoid building some tests programs when tests are not enabled Change-Id: Ie34aeec1a598ad811256a00354f66cfddae9d0b9 Signed-off-by: Damjan Marion --- build-root/emacs-lisp/README | 2 +- build-root/scripts/make-plugin-toolkit | 40 -------------------------- src/Makefile.am | 1 + src/plugins/Makefile.am | 1 + src/vnet.am | 9 ++---- src/vnet/plugin/p1.c | 52 ---------------------------------- src/vpp-api/java/Makefile.am | 1 + src/vpp-api/python/Makefile.am | 5 ++-- src/vpp.am | 36 ++--------------------- src/vppinfra.am | 8 +----- 10 files changed, 12 insertions(+), 143 deletions(-) delete mode 100755 build-root/scripts/make-plugin-toolkit delete mode 100644 src/vnet/plugin/p1.c (limited to 'src/vpp-api/java') diff --git a/build-root/emacs-lisp/README b/build-root/emacs-lisp/README index 483e1c39..1f09a3fa 100644 --- a/build-root/emacs-lisp/README +++ b/build-root/emacs-lisp/README @@ -66,7 +66,7 @@ Or, generate each file individually: $ mkdir build $ cd build - $ ../configure --with-plugin-toolkit + $ ../configure $ make $ sudo make install diff --git a/build-root/scripts/make-plugin-toolkit b/build-root/scripts/make-plugin-toolkit deleted file mode 100755 index e1d6fcfb..00000000 --- a/build-root/scripts/make-plugin-toolkit +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash - -set -eux - -build_tarball() { - for dir in vppinfra dpdk svm vlib-api vlib vnet vpp vpp-api-test - do - tar -C install-$1/$dir/include -cf - . | tar -C $tmp_dir/include -xf - - done - tar -C ../sample-plugin -cf - . \ - | tar -C $tmp_dir/src/sample-plugin -xf - - cp tools/bin/vppapigen $tmp_dir/tools/bin - echo Created by `id -u -n` on `hostname` at `date` > \ - $tmp_dir/toolkit-version-stamp - cp scripts/vpp-plugin-toolkit-readme $tmp_dir/README - tar -C $tmp_dir -zcf $PWD/vpp-plugin-toolkit-$1.tar.gz . -} - -if [ `basename $PWD` != "build-root" ] ; then - echo Please run this script from build-root - exit 1 -fi - -echo Pull additional tookit repos -make PLATFORM=vpp sample-plugin-find-source - -make PLATFORM=vpp TAG=vpp wipe-all -echo Build vpp forwarder production package -make PLATFORM=vpp TAG=vpp strip_sumbols=yes install-packages - -tmp_dir="`mktemp -d /tmp/plugin-XXXXXX`" -trap "rm -rf $tmp_dir" err - -echo Create vpp forwarder production plugin toolkit tarball -mkdir -p $tmp_dir/tools/bin $tmp_dir/include $tmp_dir/lib64 \ - $tmp_dir/src/sample-plugin -build_tarball vpp-native -rm -rf $tmp_dir - -exit 0 diff --git a/src/Makefile.am b/src/Makefile.am index bba90eae..18a41a15 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -17,6 +17,7 @@ AUTOMAKE_OPTIONS = foreign subdir-objects ACLOCAL_AMFLAGS = -I m4 +AM_LIBTOOLFLAGS = --quiet AM_CFLAGS = -Wall diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index 3e9ab91c..f0c455a5 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -16,6 +16,7 @@ AUTOMAKE_OPTIONS = foreign subdir-objects AM_CFLAGS = -Wall -I${top_srcdir} -I${top_builddir} AM_LDFLAGS = -module -shared -avoid-version +AM_LIBTOOLFLAGS = --quiet SUFFIXES = .api.h .api .api.json API_FILES = BUILT_SOURCES = diff --git a/src/vnet.am b/src/vnet.am index bca56227..665a16ea 100644 --- a/src/vnet.am +++ b/src/vnet.am @@ -11,8 +11,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +lib_LTLIBRARIES += libvnet.la + libvnet_la_SOURCES = -libvnetplugin_la_SOURCES = libvnet_la_LIBADD = libvppinfra.la libsvm.la libvnet_la_DEPENDENCIES = libvppinfra.la libvlib.la libsvmdb.la libsvm.la libvlibapi.la libvlibmemory.la libvlibmemoryclient.la @@ -966,15 +967,9 @@ nobase_include_HEADERS += \ # Plugin client library ######################################## -libvnetplugin_la_SOURCES += \ - vnet/plugin/p1.c - nobase_include_HEADERS += \ vnet/plugin/plugin.h -libvnetdir = ${libdir} -libvnet_LTLIBRARIES = libvnet.la libvnetplugin.la - pcap2pg_SOURCES = \ vnet/unix/pcap2pg.c \ vnet/unix/pcap.h diff --git a/src/vnet/plugin/p1.c b/src/vnet/plugin/p1.c deleted file mode 100644 index 3102ecce..00000000 --- a/src/vnet/plugin/p1.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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. - */ -/* - * This file and in fact the entire directory shouldn't even exist. - * - * Unfortunately, various things malfunction when we try to go there. - * Plugin DLL's end up with their own copies of critical - * data structures. No one of these problems would be tough to fix, - * but there are quite a number of them. - */ - -/* - * Make certain that plugin .dll's which reference the following functions - * can find them... - */ - -#if DPDK > 0 -#define foreach_dpdk_plugin_reference \ -_(rte_calloc) \ -_(rte_free) \ -_(rte_malloc) \ -_(rte_zmalloc) \ -_(rte_malloc_virt2phy) \ -_(rte_eal_get_configuration) -#else -#define foreach_dpdk_plugin_reference -#endif - -#define _(a) void a (void); -foreach_dpdk_plugin_reference -#undef _ - -void *vnet_library_plugin_references[] = - { -#define _(a) &a, - foreach_dpdk_plugin_reference -#undef _ - }; - -void vnet_library_plugin_reference(void) { } diff --git a/src/vpp-api/java/Makefile.am b/src/vpp-api/java/Makefile.am index bfb5665a..00ee5946 100644 --- a/src/vpp-api/java/Makefile.am +++ b/src/vpp-api/java/Makefile.am @@ -13,6 +13,7 @@ AUTOMAKE_OPTIONS = foreign ACLOCAL_AMFLAGS = -I m4 +AM_LIBTOOLFLAGS = --quiet AM_CFLAGS = -Wall -I${top_srcdir} -I${top_builddir} \ -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux \ diff --git a/src/vpp-api/python/Makefile.am b/src/vpp-api/python/Makefile.am index a5dabc3d..16c47627 100644 --- a/src/vpp-api/python/Makefile.am +++ b/src/vpp-api/python/Makefile.am @@ -13,6 +13,7 @@ AUTOMAKE_OPTIONS = foreign ACLOCAL_AMFLAGS = -I m4 +AM_LIBTOOLFLAGS = --quiet AM_CFLAGS = -Wall -I${top_srcdir} -I${top_builddir} BUILT_SOURCES = @@ -48,6 +49,7 @@ install-exec-local: # # Test client # +if ENABLE_TESTS noinst_PROGRAMS += test_pneum test_pneum_SOURCES = pneum/pneum.c pneum/test_pneum.c test_pneum_LDADD = \ @@ -56,5 +58,4 @@ test_pneum_LDADD = \ $(top_builddir)/libvlibapi.la \ $(top_builddir)/libsvm.la \ -lpthread -lm -lrt - - +endif diff --git a/src/vpp.am b/src/vpp.am index 3bb2c1be..be55c400 100644 --- a/src/vpp.am +++ b/src/vpp.am @@ -68,39 +68,6 @@ VPP_VERSION = $(shell $(srcdir)/scripts/version) echo "#define VPP_BUILD_VER \"$(VPP_VERSION)\"" >> $$f ;\ fi - -# Generate a plugin configuration script. Misconfiguring a -# plugin can cause very subtle problems. - -bin_SCRIPTS = vpp_plugin_configure - -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 CFG" $@ - @echo "#!/bin/bash" > $@ - @echo " " >> $@ - @echo "set +eu" >> $@ - @echo " " >> $@ - @echo "if [ -f ./configure ] ; then" >> $@ - @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 $(PLUGIN_DPDK_ARG)" >> $@ - @echo " else" >> $@ - @echo " echo Couldnt find ./configure or ../configure " >> $@ - @echo " exit 1" >> $@ - @echo " fi" >> $@ - @echo "fi" >> $@ - bin_vpp_LDADD = \ libvlibapi.la \ libvlibmemory.la \ @@ -108,12 +75,12 @@ bin_vpp_LDADD = \ libvnet.la \ libsvm.la \ libsvmdb.la \ - libvnetplugin.la \ -lrt bin_vpp_LDFLAGS = $(DPDK_LD_FLAGS) bin_vpp_LDADD += libvppinfra.la -lm -lpthread -ldl $(DPDK_LD_ADD) +if ENABLE_TESTS noinst_PROGRAMS += bin/test_client bin_test_client_SOURCES = \ @@ -138,6 +105,7 @@ bin_test_ha_LDADD = \ libsvm.la \ libvppinfra.la \ -lpthread -lm -lrt +endif noinst_PROGRAMS += bin/summary_stats_client diff --git a/src/vppinfra.am b/src/vppinfra.am index 836c7213..19485d2d 100644 --- a/src/vppinfra.am +++ b/src/vppinfra.am @@ -11,13 +11,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -libvppinfradir = ${libdir} - -if WITH_UNIX - libvppinfra_LTLIBRARIES = libvppinfra.la -endif - -lib_LIBRARIES = +lib_LTLIBRARIES += libvppinfra.la TESTS = -- cgit 1.2.3-korg From ea4fa7526f51f78fa8ec712c002ba55983187ad3 Mon Sep 17 00:00:00 2001 From: Marek Gradzki Date: Tue, 10 Jan 2017 19:58:43 +0100 Subject: jvpp: add missing dependencies on various libs Change-Id: Ifa6845458f7249d2179a20b954fcdd49d6c68aa1 Signed-off-by: Marek Gradzki --- src/vpp-api/java/Makefile.am | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) (limited to 'src/vpp-api/java') diff --git a/src/vpp-api/java/Makefile.am b/src/vpp-api/java/Makefile.am index 00ee5946..c8dba9e2 100644 --- a/src/vpp-api/java/Makefile.am +++ b/src/vpp-api/java/Makefile.am @@ -36,7 +36,14 @@ nobase_include_HEADERS = \ noinst_LTLIBRARIES += libjvpp_common.la libjvpp_common_la_SOURCES = jvpp-common/jvpp_common.c -libjvpp_common_la_LDFLAGS = + +JVPP_LIBS = \ + libjvpp_common.la \ + $(top_builddir)/libvppinfra.la \ + $(top_builddir)/libvlibmemoryclient.la \ + $(top_builddir)/libvlibapi.la \ + $(top_builddir)/libsvm.la \ + -lpthread -lm -lrt # # jvpp-registry (connection management + plugin registry) @@ -46,7 +53,8 @@ noinst_LTLIBRARIES += libjvpp_registry.la libjvpp_registry_la_SOURCES = jvpp-registry/jvpp_registry.c libjvpp_registry_la_CPPFLAGS = -Ijvpp-registry -libjvpp_registry_la_LIBAD = libjvpp_common.la +libjvpp_registry_la_LIBADD = $(JVPP_LIBS) +libjvpp_registry_la_DEPENDENCIES = libjvpp_common.la packagedir_jvpp_registry = io/fd/vpp/jvpp jvpp_registry_src_files := \ @@ -88,8 +96,10 @@ endef noinst_LTLIBRARIES += libjvpp_core.la libjvpp_core_la_SOURCES = jvpp-core/jvpp_core.c jvpp-core/jvpp_core_gen.h libjvpp_core_la_CPPFLAGS = -Ijvpp-registry -Ijvpp-core -BUILT_SOURCES += jvpp-core/io_fd_vpp_jvpp_core_JVppCoreImpl.h +libjvpp_core_la_LIBADD = $(JVPP_LIBS) +libjvpp_core_la_DEPENDENCIES = libjvpp_common.la +BUILT_SOURCES += jvpp-core/io_fd_vpp_jvpp_core_JVppCoreImpl.h JAR_FILES += jvpp-core-$(PACKAGE_VERSION).jar CLEANDIRS += jvpp-core/target jvpp_core_json_files = $(shell find @top_builddir@/vnet/ -type f -name '*.api.json') @@ -105,9 +115,10 @@ if ENABLE_ACL_PLUGIN noinst_LTLIBRARIES += libjvpp_acl.la libjvpp_acl_la_SOURCES = jvpp-acl/jvpp_acl.c libjvpp_acl_la_CPPFLAGS = -Ijvpp-acl +libjvpp_acl_la_LIBADD = $(JVPP_LIBS) +libjvpp_acl_la_DEPENDENCIES = libjvpp_common.la BUILT_SOURCES += jvpp-acl/io_fd_vpp_jvpp_acl_JVppAclImpl.h - JAR_FILES += jvpp-acl-$(PACKAGE_VERSION).jar CLEANDIRS += jvpp-acl/target @@ -124,9 +135,10 @@ if ENABLE_SNAT_PLUGIN noinst_LTLIBRARIES += libjvpp_snat.la libjvpp_snat_la_SOURCES = jvpp-snat/jvpp_snat.c libjvpp_snat_la_CPPFLAGS = -Ijvpp-snat +libjvpp_snat_la_LIBADD = $(JVPP_LIBS) +libjvpp_snat_la_DEPENDENCIES = libjvpp_common.la BUILT_SOURCES += jvpp-snat/io_fd_vpp_jvpp_snat_JVppSnatImpl.h - JAR_FILES += jvpp-snat-$(PACKAGE_VERSION).jar CLEANDIRS += jvpp-snat/target @@ -142,6 +154,8 @@ endif if ENABLE_IOAM_PLUGIN noinst_LTLIBRARIES += libjvpp_ioamtrace.la libjvpp_ioamtrace_la_SOURCES = jvpp-ioamtrace/jvpp_ioam_trace.c +libjvpp_ioamtrace_la_LIBADD = $(JVPP_LIBS) +libjvpp_ioamtrace_la_DEPENDENCIES = libjvpp_common.la BUILT_SOURCES += jvpp-ioamtrace/io_fd_vpp_jvpp_ioamtrace_JVppIoamtraceImpl.h JAR_FILES += jvpp-ioamtrace-$(PACKAGE_VERSION).jar @@ -157,6 +171,8 @@ jvpp-ioamtrace/io_fd_vpp_jvpp_ioamtrace_JVppIoamtraceImpl.h: $(jvpp_registry_ok) # noinst_LTLIBRARIES += libjvpp_ioampot.la libjvpp_ioampot_la_SOURCES = jvpp-ioampot/jvpp_ioam_pot.c +libjvpp_ioampot_la_LIBADD = $(JVPP_LIBS) +libjvpp_ioampot_la_DEPENDENCIES = libjvpp_common.la BUILT_SOURCES += jvpp-ioampot/io_fd_vpp_jvpp_ioampot_JVppIoampotImpl.h JAR_FILES += jvpp-ioampot-$(PACKAGE_VERSION).jar @@ -172,6 +188,8 @@ jvpp-ioampot/io_fd_vpp_jvpp_ioampot_JVppIoampotImpl.h: $(jvpp_registry_ok) $(jvp # noinst_LTLIBRARIES += libjvpp_ioamexport.la libjvpp_ioamexport_la_SOURCES = jvpp-ioamexport/jvpp_ioam_export.c +libjvpp_ioamexport_la_LIBADD = $(JVPP_LIBS) +libjvpp_ioamexport_la_DEPENDENCIES = libjvpp_common.la BUILT_SOURCES += jvpp-ioamexport/io_fd_vpp_jvpp_ioamexport_JVppIoamexportImpl.h JAR_FILES += jvpp-ioamexport-$(PACKAGE_VERSION).jar -- cgit 1.2.3-korg From 3f14e2305755e53d586a791cb5cb197021cd5186 Mon Sep 17 00:00:00 2001 From: Marek Gradzki Date: Thu, 12 Jan 2017 09:16:23 +0100 Subject: jvpp: include jvpp_common in vpp packages Change-Id: Idcf37fc4a031c1c95362aeadf11768103e40d0fd Signed-off-by: Marek Gradzki --- src/vpp-api/java/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/vpp-api/java') diff --git a/src/vpp-api/java/Makefile.am b/src/vpp-api/java/Makefile.am index c8dba9e2..4a805ce0 100644 --- a/src/vpp-api/java/Makefile.am +++ b/src/vpp-api/java/Makefile.am @@ -34,7 +34,7 @@ CLEANDIRS = nobase_include_HEADERS = \ jvpp-common/jvpp_common.h -noinst_LTLIBRARIES += libjvpp_common.la +lib_LTLIBRARIES = libjvpp_common.la libjvpp_common_la_SOURCES = jvpp-common/jvpp_common.c JVPP_LIBS = \ -- cgit 1.2.3-korg From cb365d89e52fd53e6ff047c462f061acc17ec1c6 Mon Sep 17 00:00:00 2001 From: Marek Gradzki Date: Mon, 16 Jan 2017 14:00:56 +0100 Subject: Fix linker warnings for jvpp shared libs Change-Id: I02d01ce210d5bbe11ad2ee70f7703647f9147416 Signed-off-by: Marek Gradzki --- src/vpp-api/java/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/vpp-api/java') diff --git a/src/vpp-api/java/Makefile.am b/src/vpp-api/java/Makefile.am index 4a805ce0..fac420d7 100644 --- a/src/vpp-api/java/Makefile.am +++ b/src/vpp-api/java/Makefile.am @@ -19,7 +19,7 @@ AM_CFLAGS = -Wall -I${top_srcdir} -I${top_builddir} \ -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux \ -I@top_srcdir@/plugins -I@top_builddir@/plugins -AM_LDFLAGS = -module -shared -avoid-version -rpath /none -no-undefined +AM_LDFLAGS = -shared -avoid-version -rpath /none -no-undefined BUILT_SOURCES = bin_PROGRAMS = -- cgit 1.2.3-korg From 07c39da547141f59b37a320cde5616ca652e7cff Mon Sep 17 00:00:00 2001 From: Marek Gradzki Date: Wed, 18 Jan 2017 14:16:13 +0100 Subject: jvpp: include libjvpp_common in rpm packages Change-Id: Idef5f036c9dc679b062931f38fcc311d00fc0017 Signed-off-by: Marek Gradzki --- src/vpp-api/java/Makefile.am | 1 + 1 file changed, 1 insertion(+) (limited to 'src/vpp-api/java') diff --git a/src/vpp-api/java/Makefile.am b/src/vpp-api/java/Makefile.am index fac420d7..3696e5bf 100644 --- a/src/vpp-api/java/Makefile.am +++ b/src/vpp-api/java/Makefile.am @@ -36,6 +36,7 @@ nobase_include_HEADERS = \ lib_LTLIBRARIES = libjvpp_common.la libjvpp_common_la_SOURCES = jvpp-common/jvpp_common.c +libjvpp_common_la_LDFLAGS = shared -rpath /none -no-undefined JVPP_LIBS = \ libjvpp_common.la \ -- cgit 1.2.3-korg From 4746a5d75ebb080d15d5d743dca3a85d0265176f Mon Sep 17 00:00:00 2001 From: Marek Gradzki Date: Fri, 27 Jan 2017 08:57:40 +0100 Subject: jvpp: utilize per-message CRCs (VPP-544) Since messages ids are no longer statically referenced, fixes also VPP-611. Change-Id: Ic8e6ee2b7f1142c185595347984d69350be25ac3 Signed-off-by: Marek Gradzki --- src/vpp-api/java/jvpp-acl/jvpp_acl.c | 8 +++- src/vpp-api/java/jvpp-common/jvpp_common.c | 16 +++++++ src/vpp-api/java/jvpp-common/jvpp_common.h | 7 +++ src/vpp-api/java/jvpp-core/jvpp_core.c | 8 +++- .../java/jvpp-ioamexport/jvpp_ioam_export.c | 8 +++- src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.c | 8 +++- src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.c | 8 +++- src/vpp-api/java/jvpp-registry/jvpp_registry.c | 54 ++++++++++++++++++++-- src/vpp-api/java/jvpp-snat/jvpp_snat.c | 8 +++- src/vpp-api/java/jvpp/gen/jvpp_gen.py | 2 +- src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py | 31 +++++++++++-- 11 files changed, 143 insertions(+), 15 deletions(-) (limited to 'src/vpp-api/java') diff --git a/src/vpp-api/java/jvpp-acl/jvpp_acl.c b/src/vpp-api/java/jvpp-acl/jvpp_acl.c index d56abe3d..9150ce6b 100644 --- a/src/vpp-api/java/jvpp-acl/jvpp_acl.c +++ b/src/vpp-api/java/jvpp-acl/jvpp_acl.c @@ -74,8 +74,14 @@ JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_acl_JVppAclImpl_init0 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(VL_API_##N + plugin_main->msg_id_base, #n, \ + vl_msg_api_set_handlers(get_message_id(env, #N), #n, \ vl_api_##n##_t_handler, \ vl_noop_handler, \ vl_api_##n##_t_endian, \ diff --git a/src/vpp-api/java/jvpp-common/jvpp_common.c b/src/vpp-api/java/jvpp-common/jvpp_common.c index a161c09c..b88c0ea2 100644 --- a/src/vpp-api/java/jvpp-common/jvpp_common.c +++ b/src/vpp-api/java/jvpp-common/jvpp_common.c @@ -63,3 +63,19 @@ void call_on_error(const char* callName, int contextId, int retval, (*env)->CallVoidMethod(env, callbackObject, callbackExcMethod, excObject); DEBUG_LOG("CallOnError : Response sent\n"); } + +u32 get_message_id(JNIEnv *env, const char *key) { + uword *p = hash_get(jvpp_main.messages_hash, key); + if (!p) { + jclass exClass = (*env)->FindClass(env, "java/lang/IllegalStateException"); + char *msgBuf = clib_mem_alloc(strlen(key) + 40); + strcpy(msgBuf, "API mismatch detected: "); + strcat(msgBuf, key); + strcat(msgBuf, " is missing"); + DEBUG_LOG("get_message_id : %s\n", msgBuf); + (*env)->ThrowNew(env, exClass, msgBuf); + clib_mem_free(msgBuf); + return 0; + } + return (u32) p[0]; +} diff --git a/src/vpp-api/java/jvpp-common/jvpp_common.h b/src/vpp-api/java/jvpp-common/jvpp_common.h index bbb203ed..34502d04 100644 --- a/src/vpp-api/java/jvpp-common/jvpp_common.h +++ b/src/vpp-api/java/jvpp-common/jvpp_common.h @@ -37,6 +37,7 @@ typedef struct { /* Convenience */ unix_shared_memory_queue_t * vl_input_queue; u32 my_client_index; + uword *messages_hash; } jvpp_main_t; extern jvpp_main_t jvpp_main __attribute__((aligned (64))); @@ -64,4 +65,10 @@ void call_on_error(const char* callName, int contextId, int retval, jclass callbackClass, jobject callbackObject, jclass callbackExceptionClass); +/** + * Retrieves message id based on message name and crc (key format: name_crc). + * Throws java/lang/IllegalStateException on failure. + */ +u32 get_message_id(JNIEnv *env, const char* key); + #endif /* __included_jvpp_common_h__ */ diff --git a/src/vpp-api/java/jvpp-core/jvpp_core.c b/src/vpp-api/java/jvpp-core/jvpp_core.c index ef4cb8e3..c04666b2 100644 --- a/src/vpp-api/java/jvpp-core/jvpp_core.c +++ b/src/vpp-api/java/jvpp-core/jvpp_core.c @@ -67,8 +67,14 @@ JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_core_JVppCoreImpl_init0 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(VL_API_##N, #n, \ + vl_msg_api_set_handlers(get_message_id(env, #N), #n, \ vl_api_##n##_t_handler, \ vl_noop_handler, \ vl_noop_handler, \ diff --git a/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.c b/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.c index 5cda89d1..068d2ec3 100644 --- a/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.c +++ b/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.c @@ -74,8 +74,14 @@ JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioamexport_JVppIoamexportImpl_init0 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(VL_API_##N + plugin_main->msg_id_base, #n, \ + vl_msg_api_set_handlers(get_message_id(env, #N), #n, \ vl_api_##n##_t_handler, \ vl_noop_handler, \ vl_api_##n##_t_endian, \ diff --git a/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.c b/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.c index 9291dbba..51b6a075 100644 --- a/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.c +++ b/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.c @@ -74,8 +74,14 @@ JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioampot_JVppIoampotImpl_init0 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(VL_API_##N + plugin_main->msg_id_base, #n, \ + vl_msg_api_set_handlers(get_message_id(env, #N), #n, \ vl_api_##n##_t_handler, \ vl_noop_handler, \ vl_api_##n##_t_endian, \ diff --git a/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.c b/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.c index 0bf17889..2f74b5ad 100644 --- a/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.c +++ b/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.c @@ -74,8 +74,14 @@ JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioamtrace_JVppIoamtraceImpl_init0 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(VL_API_##N + plugin_main->msg_id_base, #n, \ + vl_msg_api_set_handlers(get_message_id(env, #N), #n, \ vl_api_##n##_t_handler, \ vl_noop_handler, \ vl_api_##n##_t_endian, \ diff --git a/src/vpp-api/java/jvpp-registry/jvpp_registry.c b/src/vpp-api/java/jvpp-registry/jvpp_registry.c index cbd5e0ab..add872db 100644 --- a/src/vpp-api/java/jvpp-registry/jvpp_registry.c +++ b/src/vpp-api/java/jvpp-registry/jvpp_registry.c @@ -52,16 +52,21 @@ void __stack_chk_guard(void) __attribute__((weak)); void __stack_chk_guard(void) { } +#define CONTROL_PING_MESSAGE "control_ping" +#define CONTROL_PING_REPLY_MESSAGE "control_ping_reply" + typedef struct { /* UThread attachment */ volatile u32 control_ping_result_ready; volatile i32 control_ping_retval; - /* Control poing callback */ + /* Control ping callback */ jobject registryObject; jclass registryClass; jclass controlPingReplyClass; jclass callbackExceptionClass; + int control_ping_msg_id; + int control_ping_reply_msg_id; /* Thread cleanup */ pthread_key_t cleanup_rx_thread_key; @@ -168,6 +173,41 @@ static void vl_api_control_ping_reply_t_handler( out: rm->control_ping_result_ready = 1; } +static int find_ping_id() { + int rv = 0; + jvpp_main_t * jm = &jvpp_main; + jvpp_registry_main_t * rm = &jvpp_registry_main; + api_main_t *am = &api_main; + hash_pair_t *hp; + jm->messages_hash = am->msg_index_by_name_and_crc; + + rm->control_ping_msg_id = -1; + rm->control_ping_reply_msg_id = -1; + + hash_foreach_pair (hp, jm->messages_hash, + ({ + char *key = (char *)hp->key; // key format: name_crc + int msg_name_len = strlen(key) - 9; // ignore crc + if (strlen(CONTROL_PING_MESSAGE) == msg_name_len && + strncmp(CONTROL_PING_MESSAGE, (char *)hp->key, msg_name_len) == 0) { + rm->control_ping_msg_id = (u32)hp->value[0]; + } + if (strlen(CONTROL_PING_REPLY_MESSAGE) == msg_name_len && + strncmp(CONTROL_PING_REPLY_MESSAGE, (char *)hp->key, msg_name_len) == 0) { + rm->control_ping_reply_msg_id = (u32)hp->value[0]; + } + })); + if (rm->control_ping_msg_id == -1) { + clib_warning("failed to find id for %s", CONTROL_PING_MESSAGE); + rv = -1; + } + if (rm->control_ping_reply_msg_id == -1) { + clib_warning("failed to find id for %s", CONTROL_PING_REPLY_MESSAGE); + rv = -1; + } + return rv; +} + static int send_initial_control_ping() { f64 timeout; clib_time_t clib_time; @@ -180,7 +220,7 @@ static int send_initial_control_ping() { rm->control_ping_result_ready = 0; mp = vl_msg_api_alloc(sizeof(*mp)); memset(mp, 0, sizeof(*mp)); - mp->_vl_msg_id = ntohs(VL_API_CONTROL_PING); + mp->_vl_msg_id = ntohs(rm->control_ping_msg_id); mp->client_index = jm->my_client_index; // send message: @@ -197,7 +237,7 @@ static int send_initial_control_ping() { } if (rv != 0) { - clib_warning("common: first control ping failed: %d", rv); + clib_warning("first control ping failed: %d", rv); } return rv; @@ -206,6 +246,7 @@ static int send_initial_control_ping() { static int connect_to_vpe(char *name) { jvpp_main_t * jm = &jvpp_main; api_main_t * am = &api_main; + jvpp_registry_main_t * rm = &jvpp_registry_main; if (vl_client_connect_to_vlib("/vpe-api", name, 32) < 0) return -1; @@ -214,7 +255,10 @@ static int connect_to_vpe(char *name) { jm->vl_input_queue = am->shmem_hdr->vl_input_queue; - vl_msg_api_set_handlers(VL_API_CONTROL_PING_REPLY, "control_ping_reply", + if (find_ping_id() < 0) + return -1; + + vl_msg_api_set_handlers(rm->control_ping_reply_msg_id, CONTROL_PING_REPLY_MESSAGE, vl_api_control_ping_reply_t_handler, vl_noop_handler, vl_api_control_ping_reply_t_endian, vl_api_control_ping_reply_t_print, @@ -286,7 +330,7 @@ JNIEXPORT jint JNICALL Java_io_fd_vpp_jvpp_JVppRegistryImpl_controlPing0( mp = vl_msg_api_alloc(sizeof(*mp)); memset(mp, 0, sizeof(*mp)); - mp->_vl_msg_id = ntohs(VL_API_CONTROL_PING); + mp->_vl_msg_id = ntohs(rm->control_ping_msg_id); mp->client_index = jm->my_client_index; mp->context = clib_host_to_net_u32(my_context_id); diff --git a/src/vpp-api/java/jvpp-snat/jvpp_snat.c b/src/vpp-api/java/jvpp-snat/jvpp_snat.c index 1095b6eb..c4d1afe8 100644 --- a/src/vpp-api/java/jvpp-snat/jvpp_snat.c +++ b/src/vpp-api/java/jvpp-snat/jvpp_snat.c @@ -74,8 +74,14 @@ JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_snat_JVppSnatImpl_init0 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(VL_API_##N + plugin_main->msg_id_base, #n, \ + vl_msg_api_set_handlers(get_message_id(env, #N), #n, \ vl_api_##n##_t_handler, \ vl_noop_handler, \ vl_api_##n##_t_endian, \ diff --git a/src/vpp-api/java/jvpp/gen/jvpp_gen.py b/src/vpp-api/java/jvpp/gen/jvpp_gen.py index f51b11d0..2a5ada98 100755 --- a/src/vpp-api/java/jvpp/gen/jvpp_gen.py +++ b/src/vpp-api/java/jvpp/gen/jvpp_gen.py @@ -6,7 +6,7 @@ # You may obtain a copy of the License at: # # http://www.apache.org/licenses/LICENSE-2.0 -# l +# # 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. diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py index 611171c4..5b0fbc95 100644 --- a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py +++ b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py @@ -130,7 +130,7 @@ JNIEXPORT jint JNICALL Java_io_fd_vpp_jvpp_${plugin_name}_JVpp${java_plugin_name // create message: mp = vl_msg_api_alloc(sizeof(*mp)); memset (mp, 0, sizeof (*mp)); - mp->_vl_msg_id = ntohs (VL_API_${c_name_uppercase} + plugin_main->msg_id_base); + mp->_vl_msg_id = ntohs (get_message_id(env, "${c_name}_${crc}")); mp->client_index = plugin_main->my_client_index; mp->context = clib_host_to_net_u32 (my_context_id); @@ -181,6 +181,7 @@ def generate_jni_impl(func_list, plugin_name, inputfile): field_name=camel_case_function_name, c_name_uppercase=f_name_uppercase, c_name=f_name, + crc=f['crc'], plugin_name=plugin_name, java_plugin_name=plugin_name.title(), request_class=request_class, @@ -282,7 +283,7 @@ def generate_msg_handlers(func_list, plugin_name, inputfile): return "\n".join(handlers) -handler_registration_template = Template("""_(${upercase_name}, ${name}) \\ +handler_registration_template = Template("""_(${name}_${crc}, ${name}) \\ """) @@ -298,11 +299,30 @@ def generate_handler_registration(func_list): handler_registration.append(handler_registration_template.substitute( name=name, - upercase_name=name.upper())) + crc=f['crc'])) return "".join(handler_registration) +api_verification_template = Template("""_(${name}_${crc}) \\ +""") + + +def generate_api_verification(func_list): + api_verification = ["#define foreach_supported_api_message \\\n"] + for f in func_list: + name = f['name'] + + if util.is_ignored(name): + continue + + api_verification.append(api_verification_template.substitute( + name=name, + crc=f['crc'])) + + return "".join(api_verification) + + jvpp_c_template = Template("""/** * This file contains JNI bindings for jvpp Java API. * It was generated by jvpp_c_gen.py based on $inputfile @@ -312,6 +332,9 @@ jvpp_c_template = Template("""/** // JAVA class reference cache $class_cache +// List of supported API messages used for verification +$api_verification + // JNI bindings $jni_implementations @@ -330,11 +353,13 @@ def generate_jvpp(func_list, plugin_name, inputfile, path): jni_impl = generate_jni_impl(func_list, plugin_name, inputfile) msg_handlers = generate_msg_handlers(func_list, plugin_name, inputfile) handler_registration = generate_handler_registration(func_list) + api_verification = generate_api_verification(func_list) jvpp_c_file = open("%s/jvpp_%s_gen.h" % (path, plugin_name), 'w') jvpp_c_file.write(jvpp_c_template.substitute( inputfile=inputfile, class_cache=class_cache, + api_verification=api_verification, jni_implementations=jni_impl, msg_handlers=msg_handlers, handler_registration=handler_registration)) -- cgit 1.2.3-korg From 006eb478bb7a14ba7ba4199fecce29ebc495fb9c Mon Sep 17 00:00:00 2001 From: Burt Silverman Date: Fri, 27 Jan 2017 15:29:54 -0500 Subject: Add files to CLEANFILES for robust make clean. At the least, $(BUILT_SOURCES) should be added to CLEANFILES. Also beneficial is $(api_DATA), and in the case of Java, *.files and *.h. Also there is a vpp/app/version.h, and some grammar and lex files in vppapigen. Change-Id: Ic6d3f2d40ce65e1d9a8b88217fa1f36de393ebb4 Signed-off-by: Burt Silverman --- src/Makefile.am | 3 +++ src/examples/sample-plugin/Makefile.am | 2 ++ src/plugins/Makefile.am | 1 + src/vpp-api/java/Makefile.am | 2 +- src/vpp.am | 2 ++ src/vppapigen.am | 2 ++ 6 files changed, 11 insertions(+), 1 deletion(-) (limited to 'src/vpp-api/java') diff --git a/src/Makefile.am b/src/Makefile.am index 239afeac..7da86fcb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -28,6 +28,7 @@ noinst_HEADERS = dist_bin_SCRIPTS = lib_LTLIBRARIES = BUILT_SOURCES = +CLEANFILES = install-data-local: @echo "Building vppctl command list..." @DIR_SEARCH="$(srcdir)" ; \ @@ -115,3 +116,5 @@ BUILT_SOURCES += \ endif # if ENABLE_VLIB endif # if ENABLE_SVM + +CLEANFILES += $(BUILT_SOURCES) $(api_DATA) diff --git a/src/examples/sample-plugin/Makefile.am b/src/examples/sample-plugin/Makefile.am index 47642055..a105afdd 100644 --- a/src/examples/sample-plugin/Makefile.am +++ b/src/examples/sample-plugin/Makefile.am @@ -54,3 +54,5 @@ sample_test_plugin_la_SOURCES = sample/sample_test.c sample/sample_plugin.api.h install-data-hook: @(cd $(vpppluginsdir) && $(RM) $(vppplugins_LTLIBRARIES)) @(cd $(vppapitestpluginsdir) && $(RM) $(vppapitestplugins_LTLIBRARIES)) + +CLEANFILES = $(BUILT_SOURCES) diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index f0c455a5..06b575d1 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -76,3 +76,4 @@ api_DATA = \ BUILT_SOURCES += \ $(patsubst %.api,%.api.h,$(API_FILES)) +CLEANFILES = $(BUILT_SOURCES) $(api_DATA) diff --git a/src/vpp-api/java/Makefile.am b/src/vpp-api/java/Makefile.am index 3696e5bf..6eb50084 100644 --- a/src/vpp-api/java/Makefile.am +++ b/src/vpp-api/java/Makefile.am @@ -218,7 +218,7 @@ all-local: $(JAR_FILES) # # Cleanup # -CLEANFILES = jvpp-registry.ok $(JAR_FILES) $(BUILT_SOURCES) +CLEANFILES = jvpp-registry.ok $(JAR_FILES) $(BUILT_SOURCES) *.files */*.h clean-local: rm -rf $(CLEANDIRS) diff --git a/src/vpp.am b/src/vpp.am index 0b605ec5..37466c60 100644 --- a/src/vpp.am +++ b/src/vpp.am @@ -132,4 +132,6 @@ bin_vpp_get_metrics_LDADD = \ libvppinfra.la \ -lpthread -lm -lrt +CLEANFILES += vpp/app/version.h + # vi:syntax=automake diff --git a/src/vppapigen.am b/src/vppapigen.am index 3207c83a..5c25e1ec 100644 --- a/src/vppapigen.am +++ b/src/vppapigen.am @@ -27,4 +27,6 @@ vppapigen_SOURCES = tools/vppapigen/gram.y tools/vppapigen/lex_e.c tools/vppapig vppapigen_LDADD = libvppinfra.la vppapigen_LDFLAGS = -static +CLEANFILES += tools/vppapigen/gram.c tools/vppapigen/gram.h +CLEANFILES += tools/vppapigen/lex_e.c # vi:syntax=automake -- cgit 1.2.3-korg From 3b145298421d8fd7678b5687a62925119f35a80d Mon Sep 17 00:00:00 2001 From: Marek Gradzki Date: Tue, 31 Jan 2017 13:25:53 +0100 Subject: jvpp: fix coverity warn: reliance on default encoding Change-Id: I8333b7d19ebdacac5445b4505750dd0a46764b36 Signed-off-by: Marek Gradzki --- .../io/fd/vpp/jvpp/core/test/CallbackApiTest.java | 14 +++++++++----- .../fd/vpp/jvpp/core/test/CallbackJVppFacadeTest.java | 17 ++++++++++++----- .../fd/vpp/jvpp/core/test/CreateSubInterfaceTest.java | 3 ++- .../io/fd/vpp/jvpp/core/test/FutureApiTest.java | 14 +++++++++----- .../io/fd/vpp/jvpp/ioampot/test/IoamPotApiTest.java | 5 +++-- 5 files changed, 35 insertions(+), 18 deletions(-) (limited to 'src/vpp-api/java') diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackApiTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackApiTest.java index 986993b8..8fcef967 100644 --- a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackApiTest.java +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackApiTest.java @@ -30,6 +30,7 @@ import io.fd.vpp.jvpp.core.dto.ShowVersion; import io.fd.vpp.jvpp.core.dto.ShowVersionReply; import io.fd.vpp.jvpp.core.dto.SwInterfaceDetails; import io.fd.vpp.jvpp.core.dto.SwInterfaceDump; +import java.nio.charset.StandardCharsets; public class CallbackApiTest { @@ -49,13 +50,13 @@ public class CallbackApiTest { System.out.println("Sending GetNodeIndex request..."); GetNodeIndex getNodeIndexRequest = new GetNodeIndex(); - getNodeIndexRequest.nodeName = "non-existing-node".getBytes(); + getNodeIndexRequest.nodeName = "non-existing-node".getBytes(StandardCharsets.UTF_8); jvpp.send(getNodeIndexRequest); System.out.println("Sending SwInterfaceDump request..."); SwInterfaceDump swInterfaceDumpRequest = new SwInterfaceDump(); swInterfaceDumpRequest.nameFilterValid = 0; - swInterfaceDumpRequest.nameFilter = "".getBytes(); + swInterfaceDumpRequest.nameFilter = "".getBytes(StandardCharsets.UTF_8); jvpp.send(swInterfaceDumpRequest); Thread.sleep(1000); @@ -75,15 +76,18 @@ public class CallbackApiTest { public void onShowVersionReply(final ShowVersionReply msg) { System.out.printf("Received ShowVersionReply: context=%d, program=%s, version=%s, " + "buildDate=%s, buildDirectory=%s%n", - msg.context, new String(msg.program), new String(msg.version), - new String(msg.buildDate), new String(msg.buildDirectory)); + msg.context, + new String(msg.program, StandardCharsets.UTF_8), + new String(msg.version, StandardCharsets.UTF_8), + new String(msg.buildDate, StandardCharsets.UTF_8), + new String(msg.buildDirectory, StandardCharsets.UTF_8)); } @Override public void onSwInterfaceDetails(final SwInterfaceDetails msg) { System.out.printf("Received SwInterfaceDetails: interfaceName=%s, l2AddressLength=%d, adminUpDown=%d, " + "linkUpDown=%d, linkSpeed=%d, linkMtu=%d%n", - new String(msg.interfaceName), msg.l2AddressLength, msg.adminUpDown, + new String(msg.interfaceName, StandardCharsets.UTF_8), msg.l2AddressLength, msg.adminUpDown, msg.linkUpDown, msg.linkSpeed, (int) msg.linkMtu); } diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackJVppFacadeTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackJVppFacadeTest.java index 9f7cb8de..cf6b0bb3 100644 --- a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackJVppFacadeTest.java +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackJVppFacadeTest.java @@ -26,6 +26,7 @@ import io.fd.vpp.jvpp.core.callfacade.CallbackJVppCoreFacade; import io.fd.vpp.jvpp.core.dto.GetNodeIndex; import io.fd.vpp.jvpp.core.dto.GetNodeIndexReply; import io.fd.vpp.jvpp.core.dto.ShowVersionReply; +import java.nio.charset.StandardCharsets; /** * CallbackJVppFacade together with CallbackJVppFacadeCallback allow for setting different callback for each request. @@ -37,8 +38,11 @@ public class CallbackJVppFacadeTest { @Override public void onShowVersionReply(final ShowVersionReply msg) { System.out.printf("ShowVersionCallback1 received ShowVersionReply: context=%d, program=%s," - + "version=%s, buildDate=%s, buildDirectory=%s%n", msg.context, new String(msg.program), - new String(msg.version), new String(msg.buildDate), new String(msg.buildDirectory)); + + "version=%s, buildDate=%s, buildDirectory=%s%n", msg.context, + new String(msg.program, StandardCharsets.UTF_8), + new String(msg.version, StandardCharsets.UTF_8), + new String(msg.buildDate, StandardCharsets.UTF_8), + new String(msg.buildDirectory, StandardCharsets.UTF_8)); } @Override @@ -52,8 +56,11 @@ public class CallbackJVppFacadeTest { @Override public void onShowVersionReply(final ShowVersionReply msg) { System.out.printf("ShowVersionCallback2 received ShowVersionReply: context=%d, program=%s," - + "version=%s, buildDate=%s, buildDirectory=%s%n", msg.context, new String(msg.program), - new String(msg.version), new String(msg.buildDate), new String(msg.buildDirectory)); + + "version=%s, buildDate=%s, buildDirectory=%s%n", msg.context, + new String(msg.program, StandardCharsets.UTF_8), + new String(msg.version, StandardCharsets.UTF_8), + new String(msg.buildDate, StandardCharsets.UTF_8), + new String(msg.buildDirectory, StandardCharsets.UTF_8)); } @Override @@ -88,7 +95,7 @@ public class CallbackJVppFacadeTest { callbackFacade.showVersion(showVersionCallback2); GetNodeIndex getNodeIndexRequest = new GetNodeIndex(); - getNodeIndexRequest.nodeName = "dummyNode".getBytes(); + getNodeIndexRequest.nodeName = "dummyNode".getBytes(StandardCharsets.UTF_8); callbackFacade.getNodeIndex(getNodeIndexRequest, getNodeIndexCallback); Thread.sleep(2000); diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CreateSubInterfaceTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CreateSubInterfaceTest.java index a96258f4..7684721f 100644 --- a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CreateSubInterfaceTest.java +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CreateSubInterfaceTest.java @@ -26,6 +26,7 @@ import io.fd.vpp.jvpp.core.dto.CreateSubifReply; import io.fd.vpp.jvpp.core.dto.SwInterfaceDetailsReplyDump; import io.fd.vpp.jvpp.core.dto.SwInterfaceDump; import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade; +import java.nio.charset.StandardCharsets; /** *

Tests sub-interface creation.
Equivalent to:
@@ -45,7 +46,7 @@ public class CreateSubInterfaceTest { private static SwInterfaceDump createSwInterfaceDumpRequest(final String ifaceName) { SwInterfaceDump request = new SwInterfaceDump(); - request.nameFilter = ifaceName.getBytes(); + request.nameFilter = ifaceName.getBytes(StandardCharsets.UTF_8); request.nameFilterValid = 1; return request; } diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java index f478bab4..0d7c7471 100644 --- a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java @@ -29,6 +29,7 @@ import io.fd.vpp.jvpp.core.dto.SwInterfaceDetails; import io.fd.vpp.jvpp.core.dto.SwInterfaceDetailsReplyDump; import io.fd.vpp.jvpp.core.dto.SwInterfaceDump; import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade; +import java.nio.charset.StandardCharsets; import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; @@ -46,8 +47,10 @@ public class FutureApiTest { LOG.info( String.format( "Received ShowVersionReply: context=%d, program=%s, version=%s, buildDate=%s, buildDirectory=%s%n", - reply.context, new String(reply.program), new String(reply.version), new String(reply.buildDate), - new String(reply.buildDirectory))); + reply.context, new String(reply.program, StandardCharsets.UTF_8), + new String(reply.version, StandardCharsets.UTF_8), + new String(reply.buildDate, StandardCharsets.UTF_8), + new String(reply.buildDirectory, StandardCharsets.UTF_8))); } private static void testEmptyBridgeDomainDump(final FutureJVppCoreFacade jvpp) throws Exception { @@ -72,7 +75,7 @@ public class FutureApiTest { private static void testGetNodeIndex(final FutureJVppCoreFacade jvpp) { LOG.info("Sending GetNodeIndex request..."); final GetNodeIndex request = new GetNodeIndex(); - request.nodeName = "non-existing-node".getBytes(); + request.nodeName = "non-existing-node".getBytes(StandardCharsets.UTF_8); final Future replyFuture = jvpp.getNodeIndex(request).toCompletableFuture(); try { final GetNodeIndexReply reply = replyFuture.get(); @@ -88,7 +91,7 @@ public class FutureApiTest { LOG.info("Sending SwInterfaceDump request..."); final SwInterfaceDump request = new SwInterfaceDump(); request.nameFilterValid = 0; - request.nameFilter = "".getBytes(); + request.nameFilter = "".getBytes(StandardCharsets.UTF_8); final Future replyFuture = jvpp.swInterfaceDump(request).toCompletableFuture(); final SwInterfaceDetailsReplyDump reply = replyFuture.get(); @@ -97,7 +100,8 @@ public class FutureApiTest { LOG.info( String.format("Received SwInterfaceDetails: interfaceName=%s, l2AddressLength=%d, adminUpDown=%d, " + "linkUpDown=%d, linkSpeed=%d, linkMtu=%d%n", - new String(details.interfaceName), details.l2AddressLength, details.adminUpDown, + new String(details.interfaceName, StandardCharsets.UTF_8), + details.l2AddressLength, details.adminUpDown, details.linkUpDown, details.linkSpeed, (int) details.linkMtu)); } } diff --git a/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/IoamPotApiTest.java b/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/IoamPotApiTest.java index 74eb86a1..391b25fb 100644 --- a/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/IoamPotApiTest.java +++ b/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/IoamPotApiTest.java @@ -24,6 +24,7 @@ import io.fd.vpp.jvpp.ioampot.JVppIoampotImpl; import io.fd.vpp.jvpp.ioampot.callback.PotProfileAddCallback; import io.fd.vpp.jvpp.ioampot.dto.PotProfileAdd; import io.fd.vpp.jvpp.ioampot.dto.PotProfileAddReply; +import java.nio.charset.StandardCharsets; public class IoamPotApiTest { @@ -62,8 +63,8 @@ public class IoamPotApiTest { request.maxBits = 53; request.lpc = 1234; request.polynomialPublic = 1234; - request.listNameLen = (byte)"test pot profile".getBytes().length; - request.listName = "test pot profile".getBytes(); + request.listNameLen = (byte)"test pot profile".getBytes(StandardCharsets.UTF_8).length; + request.listName = "test pot profile".getBytes(StandardCharsets.UTF_8); final int result = jvpp.send(request); System.out.printf("PotProfileAdd send result = %d%n", result); -- cgit 1.2.3-korg From 05a057bb3af7d83f62a2919ccab57aa0a41b04a9 Mon Sep 17 00:00:00 2001 From: Filip Tehlar Date: Wed, 1 Feb 2017 08:50:31 +0100 Subject: LISP: enhance binary part of some APIs Remote mapping and locator set binary APIs uses zero length arrays defined as 'u8 array[0]' in .api file. This path will change such cases to form 'type_t array[count];' in order to enhance maintainability. Change-Id: I98d0252b441020609c550d48186ed0d8338a3f2d Signed-off-by: Filip Tehlar --- src/vat/api_format.c | 18 ++----- src/vnet/lisp-cp/lisp.api | 40 +++++++-------- src/vnet/lisp-cp/lisp_api.c | 39 ++++++--------- .../fd/vpp/jvpp/core/test/LispAdjacencyTest.java | 3 +- src/vpp/api/custom_dump.c | 58 ---------------------- 5 files changed, 41 insertions(+), 117 deletions(-) (limited to 'src/vpp-api/java') diff --git a/src/vat/api_format.c b/src/vat/api_format.c index 26df1aff..0bbefd69 100644 --- a/src/vat/api_format.c +++ b/src/vat/api_format.c @@ -13243,16 +13243,6 @@ lisp_eid_put_vat (u8 * dst, u8 eid[16], u8 type) clib_memcpy (dst, eid, lisp_eid_size_vat (type)); } -/* *INDENT-OFF* */ -/** Used for transferring locators via VPP API */ -typedef CLIB_PACKED(struct -{ - u32 sw_if_index; /**< locator sw_if_index */ - u8 priority; /**< locator priority */ - u8 weight; /**< locator weight */ -}) ls_locator_t; -/* *INDENT-ON* */ - static int api_lisp_add_del_locator_set (vat_main_t * vam) { @@ -13262,7 +13252,7 @@ api_lisp_add_del_locator_set (vat_main_t * vam) u8 is_add = 1; u8 *locator_set_name = NULL; u8 locator_set_name_set = 0; - ls_locator_t locator, *locators = 0; + vl_api_local_locator_t locator, *locators = 0; u32 sw_if_index, priority, weight; u32 data_len = 0; @@ -13315,7 +13305,7 @@ api_lisp_add_del_locator_set (vat_main_t * vam) } vec_add1 (locator_set_name, 0); - data_len = sizeof (ls_locator_t) * vec_len (locators); + data_len = sizeof (vl_api_local_locator_t) * vec_len (locators); /* Construct the API message */ M2 (LISP_ADD_DEL_LOCATOR_SET, lisp_add_del_locator_set, data_len); @@ -14317,7 +14307,7 @@ api_lisp_add_del_remote_mapping (vat_main_t * vam) u32 action = ~0, p, w, data_len; ip4_address_t rloc4; ip6_address_t rloc6; - rloc_t *rlocs = 0, rloc, *curr_rloc = 0; + vl_api_remote_locator_t *rlocs = 0, rloc, *curr_rloc = 0; memset (&rloc, 0, sizeof (rloc)); @@ -14396,7 +14386,7 @@ api_lisp_add_del_remote_mapping (vat_main_t * vam) return -99; } - data_len = vec_len (rlocs) * sizeof (rloc_t); + data_len = vec_len (rlocs) * sizeof (vl_api_remote_locator_t); M2 (LISP_ADD_DEL_REMOTE_MAPPING, lisp_add_del_remote_mapping, data_len); mp->is_add = is_add; diff --git a/src/vnet/lisp-cp/lisp.api b/src/vnet/lisp-cp/lisp.api index f0feafee..a50a5ccb 100644 --- a/src/vnet/lisp-cp/lisp.api +++ b/src/vnet/lisp-cp/lisp.api @@ -13,6 +13,13 @@ * limitations under the License. */ +typeonly manual_print manual_endian define local_locator +{ + u32 sw_if_index; + u8 priority; + u8 weight; +}; + /** \brief add or delete locator_set @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @@ -20,22 +27,15 @@ @param locator_set_name - locator name @param locator_num - number of locators @param locators - LISP locator records - Structure of one locator record is as follows: - - define locator_t { - u32 sw_if_index; - u8 priority; - u8 weight; - } */ -define lisp_add_del_locator_set +manual_endian manual_print define lisp_add_del_locator_set { u32 client_index; u32 context; u8 is_add; u8 locator_set_name[64]; u32 locator_num; - u8 locators[0]; + vl_api_local_locator_t locators[locator_num]; }; /** \brief Reply for locator_set add/del @@ -405,6 +405,14 @@ define show_lisp_map_request_mode_reply u8 mode; }; +typeonly manual_endian manual_print define remote_locator +{ + u8 is_ip4; + u8 priority; + u8 weight; + u8 addr[16]; +}; + /** \brief add or delete remote static mapping @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @@ -421,16 +429,8 @@ define show_lisp_map_request_mode_reply @param seid - src EID, valid only if is_src_dst is enabled @param rloc_num - number of remote locators @param rlocs - remote locator records - Structure of remote locator: - - define rloc_t { - u8 is_ip4; - u8 priority; - u8 weight; - u8 addr[16]; - } */ -define lisp_add_del_remote_mapping +manual_print manual_endian define lisp_add_del_remote_mapping { u32 client_index; u32 context; @@ -445,7 +445,7 @@ define lisp_add_del_remote_mapping u8 seid[16]; u8 seid_len; u32 rloc_num; - u8 rlocs[0]; + vl_api_remote_locator_t rlocs[rloc_num]; }; /** \brief Reply for lisp_add_del_remote_mapping @@ -883,4 +883,4 @@ define show_lisp_pitr_reply * eval: (c-set-style "gnu") * End: */ - \ No newline at end of file + diff --git a/src/vnet/lisp-cp/lisp_api.c b/src/vnet/lisp-cp/lisp_api.c index 6f34d02c..a877540b 100644 --- a/src/vnet/lisp-cp/lisp_api.c +++ b/src/vnet/lisp-cp/lisp_api.c @@ -27,6 +27,16 @@ #include +#define vl_api_remote_locator_t_endian vl_noop_handler +#define vl_api_remote_locator_t_print vl_noop_handler +#define vl_api_local_locator_t_endian vl_noop_handler +#define vl_api_local_locator_t_print vl_noop_handler + +#define vl_api_lisp_add_del_locator_set_t_endian vl_noop_handler +#define vl_api_lisp_add_del_locator_set_t_print vl_noop_handler +#define vl_api_lisp_add_del_remote_mapping_t_endian vl_noop_handler +#define vl_api_lisp_add_del_remote_mapping_t_print vl_noop_handler + #define vl_typedefs /* define message structures */ #include #undef vl_typedefs @@ -76,36 +86,17 @@ _(SHOW_LISP_MAP_REQUEST_MODE, show_lisp_map_request_mode) \ _(LISP_USE_PETR, lisp_use_petr) \ _(SHOW_LISP_USE_PETR, show_lisp_use_petr) \ -/** Used for transferring locators via VPP API */ -/* *INDENT-OFF* */ -typedef CLIB_PACKED (struct { - u8 is_ip4; /**< is locator an IPv4 address */ - u8 priority; /**< locator priority */ - u8 weight; /**< locator weight */ - u8 addr[16]; /**< IPv4/IPv6 address */ -}) rloc_t; -/* *INDENT-ON* */ - -/** Used for transferring locators via VPP API */ -/* *INDENT-OFF* */ -typedef CLIB_PACKED (struct { - u32 sw_if_index; /**< locator sw_if_index */ - u8 priority; /**< locator priority */ - u8 weight; /**< locator weight */ -}) ls_locator_t; -/* *INDENT-ON* */ - static locator_t * -unformat_lisp_locs (void *rmt_locs, u32 rloc_num) +unformat_lisp_locs (vl_api_remote_locator_t * rmt_locs, u32 rloc_num) { u32 i; locator_t *locs = 0, loc; - rloc_t *r; + vl_api_remote_locator_t *r; for (i = 0; i < rloc_num; i++) { /* remote locators */ - r = &((rloc_t *) rmt_locs)[i]; + r = &rmt_locs[i]; memset (&loc, 0, sizeof (loc)); gid_address_ip_set (&loc.address, &r->addr, r->is_ip4 ? IP4 : IP6); @@ -125,7 +116,7 @@ vl_api_lisp_add_del_locator_set_t_handler (vl_api_lisp_add_del_locator_set_t * int rv = 0; vnet_lisp_add_del_locator_set_args_t _a, *a = &_a; locator_t locator; - ls_locator_t *ls_loc; + vl_api_local_locator_t *ls_loc; u32 ls_index = ~0, locator_num; u8 *locator_name = NULL; int i; @@ -142,7 +133,7 @@ vl_api_lisp_add_del_locator_set_t_handler (vl_api_lisp_add_del_locator_set_t * memset (&locator, 0, sizeof (locator)); for (i = 0; i < locator_num; i++) { - ls_loc = &((ls_locator_t *) mp->locators)[i]; + ls_loc = &mp->locators[i]; VALIDATE_SW_IF_INDEX (ls_loc); locator.sw_if_index = htonl (ls_loc->sw_if_index); diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/LispAdjacencyTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/LispAdjacencyTest.java index d7f5039b..e7b17335 100644 --- a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/LispAdjacencyTest.java +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/LispAdjacencyTest.java @@ -73,7 +73,8 @@ public class LispAdjacencyTest { request.eid = new byte[] {1, 2, 1, 20}; request.eidLen = 32; request.rlocNum = 1; - request.rlocs = new byte[] {1, 1, 1, 1, 2, 1, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + // FIXME!!!! + //request.rlocs = new byte[] {1, 1, 1, 1, 2, 1, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; jvpp.lispAddDelRemoteMapping(request).toCompletableFuture().get(); LOG.info("Remote mapping created successfully:" + request.toString()); } diff --git a/src/vpp/api/custom_dump.c b/src/vpp/api/custom_dump.c index 8f59f5ee..a4e97216 100644 --- a/src/vpp/api/custom_dump.c +++ b/src/vpp/api/custom_dump.c @@ -2377,34 +2377,6 @@ format_lisp_flat_eid (u8 * s, va_list * args) return 0; } -/** Used for transferring locators via VPP API */ -typedef CLIB_PACKED (struct - { - u8 is_ip4; - /**< is locator an IPv4 address */ - u8 priority; - /**< locator priority */ - u8 weight; - /**< locator weight */ - u8 addr[16]; - /**< IPv4/IPv6 address */ - }) rloc_t; - -static u8 * -format_rloc (u8 * s, va_list * args) -{ - rloc_t *rloc = va_arg (*args, rloc_t *); - - if (rloc->is_ip4) - s = format (s, "%U ", format_ip4_address, rloc->addr); - else - s = format (s, "%U ", format_ip6_address, rloc->addr); - - s = format (s, "p %d w %d", rloc->priority, rloc->weight); - - return s; -} - static void *vl_api_lisp_add_del_remote_mapping_t_print (vl_api_lisp_add_del_remote_mapping_t * mp, void *handle) { @@ -2432,12 +2404,6 @@ static void *vl_api_lisp_add_del_remote_mapping_t_print if (0 == rloc_num) s = format (s, "action %d", mp->action); - else - { - rloc_t *rloc = (rloc_t *) mp->rlocs; - for (i = 0; i < rloc_num; i++) - s = format (s, "%U ", format_rloc, &rloc[i]); - } FINISH; } @@ -2553,31 +2519,11 @@ static void *vl_api_lisp_gpe_enable_disable_t_print FINISH; } -typedef CLIB_PACKED (struct - { - u32 sw_if_index; - /**< locator sw_if_index */ - u8 priority; - /**< locator priority */ - u8 weight; - /**< locator weight */ - }) ls_locator_t; - -static u8 * -format_locator (u8 * s, va_list * args) -{ - ls_locator_t *l = va_arg (*args, ls_locator_t *); - - return format (s, "sw_if_index %d p %d w %d", - l->sw_if_index, l->priority, l->weight); -} - static void *vl_api_lisp_add_del_locator_set_t_print (vl_api_lisp_add_del_locator_set_t * mp, void *handle) { u8 *s; u32 loc_num = 0, i; - ls_locator_t *locs; s = format (0, "SCRIPT: lisp_add_del_locator_set "); @@ -2587,10 +2533,6 @@ static void *vl_api_lisp_add_del_locator_set_t_print s = format (s, "locator-set %s ", mp->locator_set_name); loc_num = clib_net_to_host_u32 (mp->locator_num); - locs = (ls_locator_t *) mp->locators; - - for (i = 0; i < loc_num; i++) - s = format (s, "%U ", format_locator, &locs[i]); FINISH; } -- cgit 1.2.3-korg From 2291a36008e197423a0f0414f6dcca4afa3ac4c1 Mon Sep 17 00:00:00 2001 From: Marek Gradzki Date: Mon, 20 Feb 2017 09:14:13 +0100 Subject: jvpp: remove unnecessary msg_id_base caching Jvpp code uses CRCs to obtain msg IDs. Checking api_main_t.msg_index_by_name_and_crc is enough to detect API mismatch. Calling vl_client_get_first_plugin_msg_id is not needed. Also fixes VPP-627. Change-Id: Ie3085dfa458795fa11f17615ac94e76197a1c8cd Signed-off-by: Marek Gradzki --- src/vpp-api/java/jvpp-acl/jvpp_acl.c | 50 ++++++++-------------- src/vpp-api/java/jvpp-acl/jvpp_acl.h | 3 -- src/vpp-api/java/jvpp-core/jvpp_core.c | 3 -- .../java/jvpp-ioamexport/jvpp_ioam_export.c | 50 ++++++++-------------- .../java/jvpp-ioamexport/jvpp_ioam_export.h | 3 -- src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.c | 50 ++++++++-------------- src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.h | 3 -- src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.c | 50 ++++++++-------------- src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.h | 3 -- src/vpp-api/java/jvpp-snat/jvpp_snat.c | 50 ++++++++-------------- src/vpp-api/java/jvpp-snat/jvpp_snat.h | 3 -- 11 files changed, 90 insertions(+), 178 deletions(-) (limited to 'src/vpp-api/java') diff --git a/src/vpp-api/java/jvpp-acl/jvpp_acl.c b/src/vpp-api/java/jvpp-acl/jvpp_acl.c index 9150ce6b..b59f5358 100644 --- a/src/vpp-api/java/jvpp-acl/jvpp_acl.c +++ b/src/vpp-api/java/jvpp-acl/jvpp_acl.c @@ -29,11 +29,6 @@ #include #undef vl_printfun -/* Get the API version number */ -#define vl_api_version(n,v) static u32 api_version=(v); -#include -#undef vl_api_version - #include #include #include @@ -58,38 +53,29 @@ JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_acl_JVppAclImpl_init0 (JNIEnv *env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) { acl_main_t * plugin_main = &acl_main; - u8 * name; clib_warning ("Java_io_fd_vpp_jvpp_acl_JVppAclImpl_init0"); plugin_main->my_client_index = my_client_index; plugin_main->vl_input_queue = (unix_shared_memory_queue_t *)queue_address; - name = format (0, "acl_%08x%c", api_version, 0); - plugin_main->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name); - - if (plugin_main->msg_id_base == (u16) ~0) { - jclass exClass = (*env)->FindClass(env, "java/lang/IllegalStateException"); - (*env)->ThrowNew(env, exClass, "acl plugin is not loaded in VPP"); - } else { - 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_api_##n##_t_endian, \ - vl_api_##n##_t_print, \ - sizeof(vl_api_##n##_t), 1); - foreach_api_reply_handler; - #undef _ - } + 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_api_##n##_t_endian, \ + vl_api_##n##_t_print, \ + sizeof(vl_api_##n##_t), 1); + foreach_api_reply_handler; + #undef _ } JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_acl_JVppAclImpl_close0 diff --git a/src/vpp-api/java/jvpp-acl/jvpp_acl.h b/src/vpp-api/java/jvpp-acl/jvpp_acl.h index 2b73d672..726f7298 100644 --- a/src/vpp-api/java/jvpp-acl/jvpp_acl.h +++ b/src/vpp-api/java/jvpp-acl/jvpp_acl.h @@ -24,9 +24,6 @@ /* Global state for JVPP-acl */ typedef struct { - /* Base message index for the acl plugin */ - u16 msg_id_base; - /* Pointer to shared memory queue */ unix_shared_memory_queue_t * vl_input_queue; diff --git a/src/vpp-api/java/jvpp-core/jvpp_core.c b/src/vpp-api/java/jvpp-core/jvpp_core.c index c04666b2..8c579811 100644 --- a/src/vpp-api/java/jvpp-core/jvpp_core.c +++ b/src/vpp-api/java/jvpp-core/jvpp_core.c @@ -38,9 +38,6 @@ // TODO: generate jvpp_plugin_name.c files (or at least reuse plugin's main structure) typedef struct { - /* Base message index for the jvpp-core plugin */ - u16 msg_id_base; - /* Pointer to shared memory queue */ unix_shared_memory_queue_t * vl_input_queue; diff --git a/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.c b/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.c index 068d2ec3..bf0e9f0a 100644 --- a/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.c +++ b/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.c @@ -29,11 +29,6 @@ #include #undef vl_printfun -/* Get the API version number */ -#define vl_api_version(n,v) static u32 api_version=(v); -#include -#undef vl_api_version - #include #include #include @@ -58,38 +53,29 @@ JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioamexport_JVppIoamexportImpl_init0 (JNIEnv *env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) { ioamexport_main_t * plugin_main = &ioamexport_main; - u8 * name; clib_warning ("Java_io_fd_vpp_jvpp_ioamexport_JVppIoamexportImpl_init0"); plugin_main->my_client_index = my_client_index; plugin_main->vl_input_queue = (unix_shared_memory_queue_t *)queue_address; - name = format (0, "ioam_export_%08x%c", api_version, 0); - plugin_main->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name); - - if (plugin_main->msg_id_base == (u16) ~0) { - jclass exClass = (*env)->FindClass(env, "java/lang/IllegalStateException"); - (*env)->ThrowNew(env, exClass, "ioam_export plugin is not loaded in VPP"); - } else { - 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_api_##n##_t_endian, \ - vl_api_##n##_t_print, \ - sizeof(vl_api_##n##_t), 1); - foreach_api_reply_handler; - #undef _ - } + 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_api_##n##_t_endian, \ + vl_api_##n##_t_print, \ + sizeof(vl_api_##n##_t), 1); + foreach_api_reply_handler; + #undef _ } JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioamexport_JVppIoamexportImpl_close0 diff --git a/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.h b/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.h index b6c0c16e..8b243def 100644 --- a/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.h +++ b/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.h @@ -24,9 +24,6 @@ /* Global state for JVPP-IOAM-EXPORT */ typedef struct { - /* Base message index for the export plugin */ - u16 msg_id_base; - /* Pointer to shared memory queue */ unix_shared_memory_queue_t * vl_input_queue; diff --git a/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.c b/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.c index 51b6a075..f7e2b901 100644 --- a/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.c +++ b/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.c @@ -29,11 +29,6 @@ #include #undef vl_printfun -/* Get the API version number */ -#define vl_api_version(n,v) static u32 api_version=(v); -#include -#undef vl_api_version - #include #include #include @@ -58,38 +53,29 @@ JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioampot_JVppIoampotImpl_init0 (JNIEnv *env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) { ioampot_main_t * plugin_main = &ioampot_main; - u8 * name; clib_warning ("Java_io_fd_vpp_jvpp_ioampot_JVppIoampotImpl_init0"); plugin_main->my_client_index = my_client_index; plugin_main->vl_input_queue = (unix_shared_memory_queue_t *)queue_address; - name = format (0, "ioam_pot_%08x%c", api_version, 0); - plugin_main->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name); - - if (plugin_main->msg_id_base == (u16) ~0) { - jclass exClass = (*env)->FindClass(env, "java/lang/IllegalStateException"); - (*env)->ThrowNew(env, exClass, "ioam_pot plugin is not loaded in VPP"); - } else { - 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_api_##n##_t_endian, \ - vl_api_##n##_t_print, \ - sizeof(vl_api_##n##_t), 1); - foreach_api_reply_handler; - #undef _ - } + 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_api_##n##_t_endian, \ + vl_api_##n##_t_print, \ + sizeof(vl_api_##n##_t), 1); + foreach_api_reply_handler; + #undef _ } JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioampot_JVppIoampotImpl_close0 diff --git a/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.h b/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.h index 00aa51db..81e2a1bb 100644 --- a/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.h +++ b/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.h @@ -24,9 +24,6 @@ /* Global state for JVPP-IOAM-POT */ typedef struct { - /* Base message index for the pot plugin */ - u16 msg_id_base; - /* Pointer to shared memory queue */ unix_shared_memory_queue_t * vl_input_queue; diff --git a/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.c b/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.c index 2f74b5ad..5a0fda69 100644 --- a/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.c +++ b/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.c @@ -29,11 +29,6 @@ #include #undef vl_printfun -/* Get the API version number */ -#define vl_api_version(n,v) static u32 api_version=(v); -#include -#undef vl_api_version - #include #include #include @@ -58,38 +53,29 @@ JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioamtrace_JVppIoamtraceImpl_init0 (JNIEnv *env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) { ioamtrace_main_t * plugin_main = &ioamtrace_main; - u8 * name; clib_warning ("Java_io_fd_vpp_jvpp_ioamtrace_JVppIoamtraceImpl_init0"); plugin_main->my_client_index = my_client_index; plugin_main->vl_input_queue = (unix_shared_memory_queue_t *)queue_address; - name = format (0, "ioam_trace_%08x%c", api_version, 0); - plugin_main->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name); - - if (plugin_main->msg_id_base == (u16) ~0) { - jclass exClass = (*env)->FindClass(env, "java/lang/IllegalStateException"); - (*env)->ThrowNew(env, exClass, "ioam_trace plugin is not loaded in VPP"); - } else { - 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_api_##n##_t_endian, \ - vl_api_##n##_t_print, \ - sizeof(vl_api_##n##_t), 1); - foreach_api_reply_handler; - #undef _ - } + 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_api_##n##_t_endian, \ + vl_api_##n##_t_print, \ + sizeof(vl_api_##n##_t), 1); + foreach_api_reply_handler; + #undef _ } JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioamtrace_JVppIoamtraceImpl_close0 diff --git a/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.h b/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.h index 9fc16c15..cb0b27e1 100644 --- a/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.h +++ b/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.h @@ -24,9 +24,6 @@ /* Global state for JVPP-IOAM-TRACE */ typedef struct { - /* Base message index for the trace plugin */ - u16 msg_id_base; - /* Pointer to shared memory queue */ unix_shared_memory_queue_t * vl_input_queue; diff --git a/src/vpp-api/java/jvpp-snat/jvpp_snat.c b/src/vpp-api/java/jvpp-snat/jvpp_snat.c index c4d1afe8..31265772 100644 --- a/src/vpp-api/java/jvpp-snat/jvpp_snat.c +++ b/src/vpp-api/java/jvpp-snat/jvpp_snat.c @@ -29,11 +29,6 @@ #include #undef vl_printfun -/* Get the API version number */ -#define vl_api_version(n,v) static u32 api_version=(v); -#include -#undef vl_api_version - #include #include #include @@ -58,38 +53,29 @@ JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_snat_JVppSnatImpl_init0 (JNIEnv *env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) { snat_main_t * plugin_main = &snat_main; - u8 * name; clib_warning ("Java_io_fd_vpp_jvpp_snat_JVppSnatImpl_init0"); plugin_main->my_client_index = my_client_index; plugin_main->vl_input_queue = (unix_shared_memory_queue_t *)queue_address; - name = format (0, "snat_%08x%c", api_version, 0); - plugin_main->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name); - - if (plugin_main->msg_id_base == (u16) ~0) { - jclass exClass = (*env)->FindClass(env, "java/lang/IllegalStateException"); - (*env)->ThrowNew(env, exClass, "snat plugin is not loaded in VPP"); - } else { - 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_api_##n##_t_endian, \ - vl_api_##n##_t_print, \ - sizeof(vl_api_##n##_t), 1); - foreach_api_reply_handler; - #undef _ - } + 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_api_##n##_t_endian, \ + vl_api_##n##_t_print, \ + sizeof(vl_api_##n##_t), 1); + foreach_api_reply_handler; + #undef _ } JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_snat_JVppSnatImpl_close0 diff --git a/src/vpp-api/java/jvpp-snat/jvpp_snat.h b/src/vpp-api/java/jvpp-snat/jvpp_snat.h index 6426bda8..7739a411 100644 --- a/src/vpp-api/java/jvpp-snat/jvpp_snat.h +++ b/src/vpp-api/java/jvpp-snat/jvpp_snat.h @@ -24,9 +24,6 @@ /* Global state for JVPP-SNAT */ typedef struct { - /* Base message index for the nsh plugin */ - u16 msg_id_base; - /* Pointer to shared memory queue */ unix_shared_memory_queue_t * vl_input_queue; -- cgit 1.2.3-korg From 654ceaff759b89c00d242b7d494abaeeea356f32 Mon Sep 17 00:00:00 2001 From: Dave Barach Date: Wed, 29 Mar 2017 16:59:02 -0400 Subject: Clean up more Debian packaging symbol warnings Change-Id: I6081a38af3817f0957a2faf0e3e41afa4a74f3a4 Signed-off-by: Dave Barach --- src/tests/vnet/lisp-cp/test_cp_serdes.c | 1 - src/tests/vnet/lisp-cp/test_lisp_types.c | 1 - src/uri.am | 4 +- src/vlib-api.am | 39 +++--- src/vlibapi/node_unserialize.c | 227 +++++++++++++++++++++++++++++++ src/vnet.am | 8 +- src/vpp-api-test.am | 1 - src/vpp-api.am | 2 - src/vpp-api/java/Makefile.am | 1 - src/vpp.am | 4 - 10 files changed, 250 insertions(+), 38 deletions(-) create mode 100644 src/vlibapi/node_unserialize.c (limited to 'src/vpp-api/java') diff --git a/src/tests/vnet/lisp-cp/test_cp_serdes.c b/src/tests/vnet/lisp-cp/test_cp_serdes.c index 9d51dc8f..0c7d6fd0 100644 --- a/src/tests/vnet/lisp-cp/test_cp_serdes.c +++ b/src/tests/vnet/lisp-cp/test_cp_serdes.c @@ -23,7 +23,6 @@ /* FIXME */ #include -vpe_api_main_t vpe_api_main; #define _assert(e) \ error = CLIB_ERROR_ASSERT (e); \ diff --git a/src/tests/vnet/lisp-cp/test_lisp_types.c b/src/tests/vnet/lisp-cp/test_lisp_types.c index 5d910f66..fa34a3c6 100644 --- a/src/tests/vnet/lisp-cp/test_lisp_types.c +++ b/src/tests/vnet/lisp-cp/test_lisp_types.c @@ -20,7 +20,6 @@ /* FIXME */ #include -vpe_api_main_t vpe_api_main; #define _assert(e) \ error = CLIB_ERROR_ASSERT (e); \ diff --git a/src/uri.am b/src/uri.am index ad4d65d8..ef5a2977 100644 --- a/src/uri.am +++ b/src/uri.am @@ -14,11 +14,11 @@ noinst_PROGRAMS += uri_udp_test uri_tcp_test uri_socket_test uri_udp_test_SOURCES = uri/uri_udp_test.c -uri_udp_test_LDADD = libvlibmemoryclient.la libvlibapi.la libsvm.la \ +uri_udp_test_LDADD = libvlibmemoryclient.la libsvm.la \ libvppinfra.la -lpthread -lm -lrt uri_tcp_test_SOURCES = uri/uri_tcp_test.c -uri_tcp_test_LDADD = libvlibmemoryclient.la libvlibapi.la libsvm.la \ +uri_tcp_test_LDADD = libvlibmemoryclient.la libsvm.la \ libvppinfra.la -lpthread -lm -lrt uri_socket_test_SOURCES = uri/uri_socket_test.c diff --git a/src/vlib-api.am b/src/vlib-api.am index b4616fe6..eea52420 100644 --- a/src/vlib-api.am +++ b/src/vlib-api.am @@ -11,42 +11,41 @@ # See the License for the specific language governing permissions and # limitations under the License. -lib_LTLIBRARIES += libvlibmemory.la libvlibapi.la libvlibmemoryclient.la \ +lib_LTLIBRARIES += libvlibmemory.la libvlibmemoryclient.la \ libvlibsocket.la -libvlibmemory_la_DEPENDENCIES = libvppinfra.la libsvm.la +libvlibmemory_la_DEPENDENCIES = libvppinfra.la libsvm.la libvlib.la libvlibmemory_la_LIBADD = $(libvlibmemory_la_DEPENDENCIES) -lpthread libvlibmemory_la_SOURCES = \ + vlibapi/api.h \ + vlibapi/api_helper_macros.h \ + vlibapi/api_shared.c \ + vlibapi/node_serialize.c \ vlibmemory/api.h \ vlibmemory/memclnt.api \ vlibmemory/memory_shared.c \ vlibmemory/memory_vlib.c \ - vlibmemory/vl_memory_api_h.h \ - vlibmemory/vl_memory_msg_enum.h \ vlibmemory/unix_shared_memory_queue.c \ - vlibmemory/unix_shared_memory_queue.h - -libvlibapi_la_DEPENDENCIES = libvppinfra.la -libvlibapi_la_LIBADD = $(libvlibapi_la_DEPENDENCIES) -libvlibapi_la_SOURCES = \ - vlibapi/api.h \ - vlibapi/api_helper_macros.h \ - vlibapi/api_shared.c \ - vlibapi/node_serialize.c + vlibmemory/unix_shared_memory_queue.h \ + vlibmemory/vl_memory_api_h.h \ + vlibmemory/vl_memory_msg_enum.h nobase_include_HEADERS += vlibapi/api.h vlibapi/api_helper_macros.h vlibapi/vat_helper_macros.h -libvlibmemoryclient_la_DEPENDENCIES = libvppinfra.la libsvm.la libvlibapi.la +libvlibmemoryclient_la_DEPENDENCIES = libvppinfra.la libsvm.la libvlibmemoryclient_la_LIBADD = $(libvlibmemoryclient_la_DEPENDENCIES) -lpthread libvlibmemoryclient_la_SOURCES = \ + vlibapi/api_helper_macros.h \ + vlibapi/api_shared.c \ + vlibapi/node_unserialize.c \ vlibmemory/api.h \ vlibmemory/memclnt.api \ - vlibmemory/memory_shared.c \ vlibmemory/memory_client.c \ - vlibmemory/vl_memory_api_h.h \ - vlibmemory/vl_memory_msg_enum.h \ + vlibmemory/memory_shared.c \ vlibmemory/unix_shared_memory_queue.c \ - vlibmemory/unix_shared_memory_queue.h + vlibmemory/unix_shared_memory_queue.h \ + vlibmemory/vl_memory_api_h.h \ + vlibmemory/vl_memory_msg_enum.h nobase_include_HEADERS += \ vlibmemory/api.h \ @@ -55,7 +54,7 @@ nobase_include_HEADERS += \ vlibmemory/unix_shared_memory_queue.h \ vlibmemory/memclnt.api.h -libvlibsocket_la_DEPENDENCIES = libvppinfra.la libvlib.la libvlibmemory.la libvlibapi.la +libvlibsocket_la_DEPENDENCIES = libvppinfra.la libvlib.la libvlibmemory.la libvlibsocket_la_LIBADD = $(libvlibsocket_la_DEPENDENCIES) libvlibsocket_la_SOURCES = \ vlibsocket/api.h \ @@ -71,7 +70,7 @@ nobase_include_HEADERS += \ vlibsocket/vl_socket_msg_enum.h \ vlibsocket/sockclnt.api.h -BUILT_SOURCES += \ +BUILT_SOURCES += \ vlibsocket/sockclnt.api.h \ vlibmemory/memclnt.api.h \ vlibmemory/memclnt.api.json diff --git a/src/vlibapi/node_unserialize.c b/src/vlibapi/node_unserialize.c new file mode 100644 index 00000000..bb0edfed --- /dev/null +++ b/src/vlibapi/node_unserialize.c @@ -0,0 +1,227 @@ +/* + * 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 + +#include + +/* serialized representation of state strings */ + +#define foreach_state_string_code \ +_(STATE_DONE, "done") \ +_(STATE_DISABLED, "disabled") \ +_(STATE_TIME_WAIT, "time wait") \ +_(STATE_EVENT_WAIT, "event wait") \ +_(STATE_ANY_WAIT, "any wait") \ +_(STATE_POLLING, "polling") \ +_(STATE_INTERRUPT_WAIT, "interrupt wait") \ +_(STATE_INTERNAL, "internal") + +typedef enum +{ +#define _(a,b) a, + foreach_state_string_code +#undef _ +} state_string_enum_t; + +static char *state_strings[] = { +#define _(a,b) b, + foreach_state_string_code +#undef _ +}; + +vlib_node_t *** +vlib_node_unserialize (u8 * vector) +{ + serialize_main_t _sm, *sm = &_sm; + u32 nnodes, nnexts; + u32 nstat_vms; + vlib_node_t *node; + vlib_node_t **nodes; + vlib_node_t ***nodes_by_thread = 0; + int i, j, k; + u64 l, v, c, d; + state_string_enum_t state_code; + int stats_present; + + serialize_open_vector (sm, vector); + + nstat_vms = unserialize_likely_small_unsigned_integer (sm); + + vec_validate (nodes_by_thread, nstat_vms - 1); + _vec_len (nodes_by_thread) = 0; + + for (i = 0; i < nstat_vms; i++) + { + nnodes = unserialize_likely_small_unsigned_integer (sm); + + nodes = 0; + vec_validate (nodes, nnodes - 1); + vec_add1 (nodes_by_thread, nodes); + + for (j = 0; j < nnodes; j++) + { + node = 0; + vec_validate (node, 0); + nodes[j] = node; + + unserialize_cstring (sm, (char **) &(node->name)); + state_code = unserialize_likely_small_unsigned_integer (sm); + node->state_string = (u8 *) state_strings[state_code]; + + node->type = unserialize_likely_small_unsigned_integer (sm); + nnexts = unserialize_likely_small_unsigned_integer (sm); + if (nnexts > 0) + vec_validate (node->next_nodes, nnexts - 1); + for (k = 0; k < nnexts; k++) + node->next_nodes[k] = + unserialize_likely_small_unsigned_integer (sm); + + stats_present = unserialize_likely_small_unsigned_integer (sm); + + if (stats_present) + { + /* total clocks */ + unserialize_integer (sm, &l, 8); + node->stats_total.clocks = l; + node->stats_last_clear.clocks = 0; + + /* Total calls */ + unserialize_integer (sm, &c, 8); + node->stats_total.calls = c; + + /* Total vectors */ + unserialize_integer (sm, &v, 8); + node->stats_total.vectors = v; + + /* Total suspends */ + unserialize_integer (sm, &d, 8); + node->stats_total.suspends = d; + } + } + } + return nodes_by_thread; +} + +#if TEST_CODE + +static clib_error_t * +test_node_serialize_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + vlib_node_main_t *nm = &vm->node_main; + u8 *vector = 0; + vlib_node_t ***nodes_by_thread; + vlib_node_t **nodes; + vlib_node_t *node; + vlib_node_t *next_node; + int i, j, k; + u32 max_threads = (u32) ~ 0; + int include_nexts = 0; + int include_stats = 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "max-threads %d", &max_threads)) + ; + else if (unformat (input, "stats")) + include_stats = 1; + else if (unformat (input, "nexts")) + include_nexts = 1; + else + break; + } + + /* + * Keep the number of memcpy ops to a minimum (e.g. 1). + * The current size of the serialized vector is + * slightly under 4K. + */ + vec_validate (vector, 16383); + vec_reset_length (vector); + + vector = vlib_node_serialize (nm, vector, max_threads, + include_nexts, include_stats); + + vlib_cli_output (vm, "result vector %d bytes", vec_len (vector)); + + nodes_by_thread = vlib_node_unserialize (vector); + + vec_free (vector); + + for (i = 0; i < vec_len (nodes_by_thread); i++) + { + nodes = nodes_by_thread[i]; + + vlib_cli_output (vm, "thread %d", i); + + for (j = 0; j < vec_len (nodes); j++) + { + node = nodes[j]; + + vlib_cli_output (vm, "[%d] %s state %s", j, node->name, + node->state_string); + + vlib_cli_output + (vm, " clocks %lld calls %lld suspends" + " %lld vectors %lld", + node->stats_total.clocks, + node->stats_total.calls, + node->stats_total.suspends, node->stats_total.vectors); + + for (k = 0; k < vec_len (node->next_nodes); k++) + { + if (node->next_nodes[k] != ~0) + { + next_node = nodes[node->next_nodes[k]]; + vlib_cli_output (vm, " [%d] %s", k, next_node->name); + } + } + } + } + + for (j = 0; j < vec_len (nodes_by_thread); j++) + { + nodes = nodes_by_thread[j]; + + for (i = 0; i < vec_len (nodes); i++) + { + vec_free (nodes[i]->name); + vec_free (nodes[i]->next_nodes); + vec_free (nodes[i]); + } + vec_free (nodes); + } + vec_free (nodes_by_thread); + + return 0; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (test_node_serialize_node, static) = { + .path = "test node serialize", + .short_help = "test node serialize [max-threads NN] nexts stats", + .function = test_node_serialize_command_fn, +}; +/* *INDENT-ON* */ +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/vnet.am b/src/vnet.am index 9c55e336..dcab19da 100644 --- a/src/vnet.am +++ b/src/vnet.am @@ -19,9 +19,7 @@ libvnet_la_DEPENDENCIES = \ libvlib.la \ libsvmdb.la \ libsvm.la \ - libvlibapi.la \ - libvlibmemory.la \ - libvlibmemoryclient.la + libvlibmemory.la libvnet_la_LIBADD = $(libvnet_la_DEPENDENCIES) -lm -lpthread -ldl -lrt @@ -446,8 +444,7 @@ test_map_SOURCES = \ test_map_CPPFLAGS = $(AM_CPPFLAGS) -DCLIB_DEBUG test_map_LDADD = libvnet.la libvppinfra.la libvlib.la \ - -lpthread -lvlibmemory -lvlibapi \ - -ldl -lsvm -lrt + -lpthread -lvlibmemory -ldl -lsvm -lrt test_map_LDFLAGS = -static endif @@ -618,7 +615,6 @@ LDS = \ libvlib.la \ libsvm.la \ libsvmdb.la \ - libvlibapi.la \ libvlibmemory.la \ -lpthread -ldl -lrt -lm diff --git a/src/vpp-api-test.am b/src/vpp-api-test.am index d0df31a0..9465dc68 100644 --- a/src/vpp-api-test.am +++ b/src/vpp-api-test.am @@ -38,7 +38,6 @@ vpp_api_test_LDADD = \ libsvm.la \ libvatplugin.la \ libvppinfra.la \ - libvlibapi.la \ -lpthread -lm -lrt -ldl -lcrypto vpp_api_test_LDFLAGS = -Wl,--export-dynamic diff --git a/src/vpp-api.am b/src/vpp-api.am index 0e05d60d..49e26da1 100644 --- a/src/vpp-api.am +++ b/src/vpp-api.am @@ -19,7 +19,6 @@ libpneum_la_SOURCES = vpp-api/pneum/pneum.c libpneum_la_LIBADD = \ $(top_builddir)/libvppinfra.la \ $(top_builddir)/libvlibmemoryclient.la \ - $(top_builddir)/libvlibapi.la \ $(top_builddir)/libsvm.la \ -lpthread -lm -lrt @@ -37,7 +36,6 @@ test_pneum_SOURCES = vpp-api/pneum/pneum.c vpp-api/pneum/test_pneum.c test_pneum_LDADD = \ $(top_builddir)/libvppinfra.la \ $(top_builddir)/libvlibmemoryclient.la \ - $(top_builddir)/libvlibapi.la \ $(top_builddir)/libsvm.la \ -lpthread -lm -lrt endif diff --git a/src/vpp-api/java/Makefile.am b/src/vpp-api/java/Makefile.am index 6eb50084..f18e0c24 100644 --- a/src/vpp-api/java/Makefile.am +++ b/src/vpp-api/java/Makefile.am @@ -42,7 +42,6 @@ JVPP_LIBS = \ libjvpp_common.la \ $(top_builddir)/libvppinfra.la \ $(top_builddir)/libvlibmemoryclient.la \ - $(top_builddir)/libvlibapi.la \ $(top_builddir)/libsvm.la \ -lpthread -lm -lrt diff --git a/src/vpp.am b/src/vpp.am index 37466c60..8cdc60d9 100644 --- a/src/vpp.am +++ b/src/vpp.am @@ -71,7 +71,6 @@ VPP_VERSION = $(shell $(srcdir)/scripts/version) fi bin_vpp_LDADD = \ - libvlibapi.la \ libvlibmemory.la \ libvlib.la \ libvnet.la \ @@ -90,7 +89,6 @@ bin_test_client_SOURCES = \ bin_test_client_LDADD = \ libvlibmemoryclient.la \ - libvlibapi.la \ libsvm.la \ libvppinfra.la \ -lpthread -lm -lrt @@ -103,7 +101,6 @@ bin_test_ha_SOURCES = \ bin_test_ha_LDADD = \ libvlibmemoryclient.la \ libvlibapi.la \ - libsvmdb.la \ libsvm.la \ libvppinfra.la \ -lpthread -lm -lrt @@ -116,7 +113,6 @@ bin_summary_stats_client_SOURCES = \ bin_summary_stats_client_LDADD = \ libvlibmemoryclient.la \ - libvlibapi.la \ libsvm.la \ libvppinfra.la \ -lpthread -lm -lrt -- cgit 1.2.3-korg From 0eb2b16f95c0c43302be79a1c4df8b828ac97e37 Mon Sep 17 00:00:00 2001 From: Marek Gradzki Date: Thu, 6 Apr 2017 09:37:00 +0200 Subject: jvpp: remove unused print&endian headers (fixes VPP-688) Change-Id: I97e03d98758a08b1f75a9a1f35f0181385a10ae8 Signed-off-by: Marek Gradzki --- src/vpp-api/java/jvpp-acl/jvpp_acl.c | 13 ++----------- src/vpp-api/java/jvpp-core/jvpp_core.c | 9 --------- src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.c | 13 ++----------- src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.c | 13 ++----------- src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.c | 13 ++----------- src/vpp-api/java/jvpp-snat/jvpp_snat.c | 13 ++----------- 6 files changed, 10 insertions(+), 64 deletions(-) (limited to 'src/vpp-api/java') diff --git a/src/vpp-api/java/jvpp-acl/jvpp_acl.c b/src/vpp-api/java/jvpp-acl/jvpp_acl.c index b59f5358..0375356d 100644 --- a/src/vpp-api/java/jvpp-acl/jvpp_acl.c +++ b/src/vpp-api/java/jvpp-acl/jvpp_acl.c @@ -20,15 +20,6 @@ #include #undef vl_typedefs -#define vl_endianfun -#include -#undef vl_endianfun - -#define vl_print(handle, ...) -#define vl_printfun -#include -#undef vl_printfun - #include #include #include @@ -71,8 +62,8 @@ JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_acl_JVppAclImpl_init0 vl_msg_api_set_handlers(get_message_id(env, #N), #n, \ vl_api_##n##_t_handler, \ vl_noop_handler, \ - vl_api_##n##_t_endian, \ - vl_api_##n##_t_print, \ + vl_noop_handler, \ + vl_noop_handler, \ sizeof(vl_api_##n##_t), 1); foreach_api_reply_handler; #undef _ diff --git a/src/vpp-api/java/jvpp-core/jvpp_core.c b/src/vpp-api/java/jvpp-core/jvpp_core.c index 8c579811..627bc10b 100644 --- a/src/vpp-api/java/jvpp-core/jvpp_core.c +++ b/src/vpp-api/java/jvpp-core/jvpp_core.c @@ -20,15 +20,6 @@ #include #undef vl_typedefs -#define vl_endianfun -#include -#undef vl_endianfun - -#define vl_print(handle, ...) -#define vl_printfun -#include -#undef vl_printfun - #include #include #include diff --git a/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.c b/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.c index bf0e9f0a..9131ccb2 100644 --- a/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.c +++ b/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.c @@ -20,15 +20,6 @@ #include #undef vl_typedefs -#define vl_endianfun -#include -#undef vl_endianfun - -#define vl_print(handle, ...) -#define vl_printfun -#include -#undef vl_printfun - #include #include #include @@ -71,8 +62,8 @@ JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioamexport_JVppIoamexportImpl_init0 vl_msg_api_set_handlers(get_message_id(env, #N), #n, \ vl_api_##n##_t_handler, \ vl_noop_handler, \ - vl_api_##n##_t_endian, \ - vl_api_##n##_t_print, \ + vl_noop_handler, \ + vl_noop_handler, \ sizeof(vl_api_##n##_t), 1); foreach_api_reply_handler; #undef _ diff --git a/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.c b/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.c index f7e2b901..5f28344e 100644 --- a/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.c +++ b/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.c @@ -20,15 +20,6 @@ #include #undef vl_typedefs -#define vl_endianfun -#include -#undef vl_endianfun - -#define vl_print(handle, ...) -#define vl_printfun -#include -#undef vl_printfun - #include #include #include @@ -71,8 +62,8 @@ JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioampot_JVppIoampotImpl_init0 vl_msg_api_set_handlers(get_message_id(env, #N), #n, \ vl_api_##n##_t_handler, \ vl_noop_handler, \ - vl_api_##n##_t_endian, \ - vl_api_##n##_t_print, \ + vl_noop_handler, \ + vl_noop_handler, \ sizeof(vl_api_##n##_t), 1); foreach_api_reply_handler; #undef _ diff --git a/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.c b/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.c index 5a0fda69..1c470b53 100644 --- a/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.c +++ b/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.c @@ -20,15 +20,6 @@ #include #undef vl_typedefs -#define vl_endianfun -#include -#undef vl_endianfun - -#define vl_print(handle, ...) -#define vl_printfun -#include -#undef vl_printfun - #include #include #include @@ -71,8 +62,8 @@ JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioamtrace_JVppIoamtraceImpl_init0 vl_msg_api_set_handlers(get_message_id(env, #N), #n, \ vl_api_##n##_t_handler, \ vl_noop_handler, \ - vl_api_##n##_t_endian, \ - vl_api_##n##_t_print, \ + vl_noop_handler, \ + vl_noop_handler, \ sizeof(vl_api_##n##_t), 1); foreach_api_reply_handler; #undef _ diff --git a/src/vpp-api/java/jvpp-snat/jvpp_snat.c b/src/vpp-api/java/jvpp-snat/jvpp_snat.c index 31265772..e994cdb6 100644 --- a/src/vpp-api/java/jvpp-snat/jvpp_snat.c +++ b/src/vpp-api/java/jvpp-snat/jvpp_snat.c @@ -20,15 +20,6 @@ #include #undef vl_typedefs -#define vl_endianfun -#include -#undef vl_endianfun - -#define vl_print(handle, ...) -#define vl_printfun -#include -#undef vl_printfun - #include #include #include @@ -71,8 +62,8 @@ JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_snat_JVppSnatImpl_init0 vl_msg_api_set_handlers(get_message_id(env, #N), #n, \ vl_api_##n##_t_handler, \ vl_noop_handler, \ - vl_api_##n##_t_endian, \ - vl_api_##n##_t_print, \ + vl_noop_handler, \ + vl_noop_handler, \ sizeof(vl_api_##n##_t), 1); foreach_api_reply_handler; #undef _ -- cgit 1.2.3-korg From 31d43481c4a19ab552ff20c43b5390e6e1a10e41 Mon Sep 17 00:00:00 2001 From: Damjan Marion Date: Wed, 26 Apr 2017 14:25:56 +0200 Subject: japi: fix compilation on 32-bit systems Change-Id: Ia7cb986544a13cc17b43b2f0948482deb02e8fb5 Signed-off-by: Damjan Marion --- src/vpp-api/java/jvpp-acl/jvpp_acl.c | 2 +- src/vpp-api/java/jvpp-core/jvpp_core.c | 2 +- src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.c | 2 +- src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.c | 2 +- src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.c | 2 +- src/vpp-api/java/jvpp-registry/jvpp_registry.c | 2 +- src/vpp-api/java/jvpp-snat/jvpp_snat.c | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src/vpp-api/java') diff --git a/src/vpp-api/java/jvpp-acl/jvpp_acl.c b/src/vpp-api/java/jvpp-acl/jvpp_acl.c index 0375356d..f5467e99 100644 --- a/src/vpp-api/java/jvpp-acl/jvpp_acl.c +++ b/src/vpp-api/java/jvpp-acl/jvpp_acl.c @@ -47,7 +47,7 @@ JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_acl_JVppAclImpl_init0 clib_warning ("Java_io_fd_vpp_jvpp_acl_JVppAclImpl_init0"); plugin_main->my_client_index = my_client_index; - plugin_main->vl_input_queue = (unix_shared_memory_queue_t *)queue_address; + plugin_main->vl_input_queue = uword_to_pointer (queue_address, unix_shared_memory_queue_t *); plugin_main->callbackObject = (*env)->NewGlobalRef(env, callback); plugin_main->callbackClass = (jclass)(*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, callback)); diff --git a/src/vpp-api/java/jvpp-core/jvpp_core.c b/src/vpp-api/java/jvpp-core/jvpp_core.c index 627bc10b..e57c62a3 100644 --- a/src/vpp-api/java/jvpp-core/jvpp_core.c +++ b/src/vpp-api/java/jvpp-core/jvpp_core.c @@ -50,7 +50,7 @@ JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_core_JVppCoreImpl_init0 (JNIEnv * env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) { core_main_t * plugin_main = &core_main; plugin_main->my_client_index = my_client_index; - plugin_main->vl_input_queue = (unix_shared_memory_queue_t *)queue_address; + plugin_main->vl_input_queue = uword_to_pointer (queue_address, unix_shared_memory_queue_t *); plugin_main->callbackObject = (*env)->NewGlobalRef(env, callback); plugin_main->callbackClass = (jclass)(*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, callback)); diff --git a/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.c b/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.c index 9131ccb2..cf4499d5 100644 --- a/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.c +++ b/src/vpp-api/java/jvpp-ioamexport/jvpp_ioam_export.c @@ -47,7 +47,7 @@ JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioamexport_JVppIoamexportImpl_init0 clib_warning ("Java_io_fd_vpp_jvpp_ioamexport_JVppIoamexportImpl_init0"); plugin_main->my_client_index = my_client_index; - plugin_main->vl_input_queue = (unix_shared_memory_queue_t *)queue_address; + plugin_main->vl_input_queue = uword_to_pointer (queue_address, unix_shared_memory_queue_t *); plugin_main->callbackObject = (*env)->NewGlobalRef(env, callback); plugin_main->callbackClass = (jclass)(*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, callback)); diff --git a/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.c b/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.c index 5f28344e..8f396989 100644 --- a/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.c +++ b/src/vpp-api/java/jvpp-ioampot/jvpp_ioam_pot.c @@ -47,7 +47,7 @@ JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioampot_JVppIoampotImpl_init0 clib_warning ("Java_io_fd_vpp_jvpp_ioampot_JVppIoampotImpl_init0"); plugin_main->my_client_index = my_client_index; - plugin_main->vl_input_queue = (unix_shared_memory_queue_t *)queue_address; + plugin_main->vl_input_queue = uword_to_pointer (queue_address, unix_shared_memory_queue_t *); plugin_main->callbackObject = (*env)->NewGlobalRef(env, callback); plugin_main->callbackClass = (jclass)(*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, callback)); diff --git a/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.c b/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.c index 1c470b53..f53937e7 100644 --- a/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.c +++ b/src/vpp-api/java/jvpp-ioamtrace/jvpp_ioam_trace.c @@ -47,7 +47,7 @@ JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_ioamtrace_JVppIoamtraceImpl_init0 clib_warning ("Java_io_fd_vpp_jvpp_ioamtrace_JVppIoamtraceImpl_init0"); plugin_main->my_client_index = my_client_index; - plugin_main->vl_input_queue = (unix_shared_memory_queue_t *)queue_address; + plugin_main->vl_input_queue = uword_to_pointer (queue_address, unix_shared_memory_queue_t *); plugin_main->callbackObject = (*env)->NewGlobalRef(env, callback); plugin_main->callbackClass = (jclass)(*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, callback)); diff --git a/src/vpp-api/java/jvpp-registry/jvpp_registry.c b/src/vpp-api/java/jvpp-registry/jvpp_registry.c index add872db..66adfea0 100644 --- a/src/vpp-api/java/jvpp-registry/jvpp_registry.c +++ b/src/vpp-api/java/jvpp-registry/jvpp_registry.c @@ -309,7 +309,7 @@ JNIEXPORT jobject JNICALL Java_io_fd_vpp_jvpp_VppJNIConnection_clientConnect( (*env)->ReleaseStringUTFChars(env, clientName, client_name); return (*env)->NewObject(env, connectionInfoClass, - connectionInfoConstructor, (jlong) jm->vl_input_queue, + connectionInfoConstructor, (jlong) pointer_to_uword (jm->vl_input_queue), (jint) jm->my_client_index, (jint) rv); } diff --git a/src/vpp-api/java/jvpp-snat/jvpp_snat.c b/src/vpp-api/java/jvpp-snat/jvpp_snat.c index e994cdb6..5fd6a88b 100644 --- a/src/vpp-api/java/jvpp-snat/jvpp_snat.c +++ b/src/vpp-api/java/jvpp-snat/jvpp_snat.c @@ -47,7 +47,7 @@ JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_snat_JVppSnatImpl_init0 clib_warning ("Java_io_fd_vpp_jvpp_snat_JVppSnatImpl_init0"); plugin_main->my_client_index = my_client_index; - plugin_main->vl_input_queue = (unix_shared_memory_queue_t *)queue_address; + plugin_main->vl_input_queue = uword_to_pointer (queue_address, unix_shared_memory_queue_t *); plugin_main->callbackObject = (*env)->NewGlobalRef(env, callback); plugin_main->callbackClass = (jclass)(*env)->NewGlobalRef(env, (*env)->GetObjectClass(env, callback)); -- cgit 1.2.3-korg From ed76d5f2e383834d8c6aae8ab3b89cf7d2479d0c Mon Sep 17 00:00:00 2001 From: Marek Gradzki Date: Fri, 12 May 2017 18:24:34 +0200 Subject: jvpp: fix memory allocation for variable lenght messages (VPP-841) Change-Id: I9a46125e3cf9815c08cf8cca17713ec6e9121eae Signed-off-by: Marek Gradzki (cherry picked from commit 307cfd8eb14ff7df04316ffa56f2c2481d650d7e) --- src/vpp-api/java/jvpp/gen/jvpp_gen.py | 14 +++++++++----- src/vpp-api/java/jvpp/gen/jvppgen/jni_gen.py | 22 +++++++++++++++------- src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py | 23 ++++++++++++++++++----- src/vpp-api/java/jvpp/gen/jvppgen/types_gen.py | 12 +++++++----- 4 files changed, 49 insertions(+), 22 deletions(-) (limited to 'src/vpp-api/java') diff --git a/src/vpp-api/java/jvpp/gen/jvpp_gen.py b/src/vpp-api/java/jvpp/gen/jvpp_gen.py index 2a5ada98..7932741d 100755 --- a/src/vpp-api/java/jvpp/gen/jvpp_gen.py +++ b/src/vpp-api/java/jvpp/gen/jvpp_gen.py @@ -96,14 +96,16 @@ def is_response_field(field_name): def get_args(t, filter): - arg_list = [] + arg_names = [] + arg_types = [] for i in t: if is_crc(i): continue if not filter(i[1]): continue - arg_list.append(i[1]) - return arg_list + arg_types.append(i[0]) + arg_names.append(i[1]) + return arg_types, arg_names def get_types(t, filter): @@ -143,16 +145,18 @@ def get_definitions(defs): # For replies include all the arguments except message_id if util.is_reply(java_name): types, lengths, crc = get_types(a[1:], is_response_field) + args = get_args(a[1:], is_response_field) func_name[a[0]] = dict( [('name', a[0]), ('java_name', java_name), - ('args', get_args(a[1:], is_response_field)), ('full_args', get_args(a[1:], lambda x: True)), + ('args', args[1]), ('arg_types', args[0]), ('types', types), ('lengths', lengths), crc]) # For requests skip message_id, client_id and context else: types, lengths, crc = get_types(a[1:], is_request_field) + args = get_args(a[1:], is_request_field) func_name[a[0]] = dict( [('name', a[0]), ('java_name', java_name), - ('args', get_args(a[1:], is_request_field)), ('full_args', get_args(a[1:], lambda x: True)), + ('args', args[1]), ('arg_types', args[0]), ('types', types), ('lengths', lengths), crc]) # Indexed by name diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jni_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jni_gen.py index 328cc8d3..cb0d66e8 100644 --- a/src/vpp-api/java/jvpp/gen/jvppgen/jni_gen.py +++ b/src/vpp-api/java/jvpp/gen/jvppgen/jni_gen.py @@ -248,16 +248,13 @@ struct_setter_templates = {'u8': u8_struct_setter_template, } -def jni_request_binding_for_type(field_type, c_name, field_reference_name, field_name, field_length, - is_variable_len_array, object_name="request"): +def jni_request_identifiers_for_type(field_type, field_reference_name, field_name, object_name="request"): """ - Generates jni code that initializes C structure that corresponds to a field of java object + Generates jni code that defines C variable corresponding to field of java object (dto or custom type). To be used in request message handlers. :param field_type: type of the field to be initialized (as defined in vpe.api) - :param c_name: name of the message struct member to be initialized :param field_reference_name: name of the field reference in generated code :param field_name: name of the field (camelcase) - :param field_length: integer or name of variable that stores field length :param object_name: name of the object to be initialized """ # field identifiers @@ -266,7 +263,7 @@ def jni_request_binding_for_type(field_type, c_name, field_reference_name, field jni_getter = util.jni_field_accessors[field_type] # field identifier - msg_initialization = request_field_identifier_template.substitute( + return request_field_identifier_template.substitute( jni_type=jni_type, field_reference_name=field_reference_name, field_name=field_name, @@ -274,6 +271,17 @@ def jni_request_binding_for_type(field_type, c_name, field_reference_name, field jni_getter=jni_getter, object_name=object_name) + +def jni_request_binding_for_type(field_type, c_name, field_reference_name, field_length, is_variable_len_array): + """ + Generates jni code that initializes C structure that corresponds to a field of java object + (dto or custom type). To be used in request message handlers. + :param field_type: type of the field to be initialized (as defined in vpe.api) + :param c_name: name of the message struct member to be initialized + :param field_reference_name: name of the field reference in generated code + :param field_length: integer or name of variable that stores field length + """ + # field setter field_length_check = "" @@ -287,7 +295,7 @@ def jni_request_binding_for_type(field_type, c_name, field_reference_name, field struct_setter_template = struct_setter_templates[field_type] - msg_initialization += struct_setter_template.substitute( + msg_initialization = struct_setter_template.substitute( c_name=c_name, field_reference_name=field_reference_name, field_length_check=field_length_check) diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py index 5b0fbc95..1425cdb7 100644 --- a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py +++ b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py @@ -113,6 +113,7 @@ request_field_identifier_template = Template(""" ${jni_type} ${field_reference_name} = (*env)->Get${jni_getter}(env, ${object_name}, ${field_reference_name}FieldId); """) +jni_msg_size_template = Template(""" + ${array_length}*sizeof(${element_type})""") jni_impl_template = Template(""" /** @@ -127,9 +128,11 @@ JNIEXPORT jint JNICALL Java_io_fd_vpp_jvpp_${plugin_name}_JVpp${java_plugin_name u32 my_context_id = vppjni_get_context_id (&jvpp_main); $request_class + $jni_identifiers + // create message: - mp = vl_msg_api_alloc(sizeof(*mp)); - memset (mp, 0, sizeof (*mp)); + mp = vl_msg_api_alloc(${msg_size}); + memset (mp, 0, ${msg_size}); mp->_vl_msg_id = ntohs (get_message_id(env, "${c_name}_${crc}")); mp->client_index = plugin_main->my_client_index; mp->context = clib_host_to_net_u32 (my_context_id); @@ -155,8 +158,10 @@ def generate_jni_impl(func_list, plugin_name, inputfile): arguments = '' request_class = '' + jni_identifiers = '' msg_initialization = '' f_name_uppercase = f_name.upper() + msg_size = 'sizeof(*mp)' if f['args']: arguments = ', jobject request' @@ -166,13 +171,19 @@ def generate_jni_impl(func_list, plugin_name, inputfile): java_name_upper=camel_case_function_name_upper, plugin_name=plugin_name) - for t in zip(f['types'], f['args'], f['lengths']): + for t in zip(f['types'], f['args'], f['lengths'], f['arg_types']): field_name = util.underscore_to_camelcase(t[1]) + is_variable_len_array = t[2][1] + if is_variable_len_array: + msg_size += jni_msg_size_template.substitute(array_length=util.underscore_to_camelcase(t[2][0]), + element_type=t[3]) + jni_identifiers += jni_gen.jni_request_identifiers_for_type(field_type=t[0], + field_reference_name=field_name, + field_name=field_name) msg_initialization += jni_gen.jni_request_binding_for_type(field_type=t[0], c_name=t[1], field_reference_name=field_name, - field_name=field_name, field_length=t[2][0], - is_variable_len_array=t[2][1]) + is_variable_len_array=is_variable_len_array) jni_impl.append(jni_impl_template.substitute( inputfile=inputfile, @@ -185,6 +196,8 @@ def generate_jni_impl(func_list, plugin_name, inputfile): plugin_name=plugin_name, java_plugin_name=plugin_name.title(), request_class=request_class, + jni_identifiers=jni_identifiers, + msg_size=msg_size, msg_initialization=msg_initialization, args=arguments)) diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/types_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/types_gen.py index 7a5eec37..93883ba1 100644 --- a/src/vpp-api/java/jvpp/gen/jvppgen/types_gen.py +++ b/src/vpp-api/java/jvpp/gen/jvppgen/types_gen.py @@ -108,12 +108,14 @@ def generate_struct_initialization(type_def, c_name_prefix, object_name, indent) for t in zip(type_def['types'], type_def['args'], type_def['lengths']): field_reference_name = "${c_name}" + util.underscore_to_camelcase_upper(t[1]) field_name = util.underscore_to_camelcase(t[1]) + struct_initialization += jni_gen.jni_request_identifiers_for_type(field_type=t[0], + field_reference_name=field_reference_name, + field_name=field_name, + object_name=object_name) struct_initialization += jni_gen.jni_request_binding_for_type(field_type=t[0], c_name=c_name_prefix + t[1], - field_reference_name=field_reference_name, - field_name=field_name, - field_length=t[2][0], - is_variable_len_array=t[2][1], - object_name=object_name) + field_reference_name=field_reference_name, + field_length=t[2][0], + is_variable_len_array=t[2][1]) return indent + struct_initialization.replace('\n', '\n' + indent) -- cgit 1.2.3-korg From ef486b1545d892f9f0e0d35e7e57cb0ca04d7ff7 Mon Sep 17 00:00:00 2001 From: Hongjun Ni Date: Wed, 12 Apr 2017 19:21:16 +0800 Subject: Add GTP-U plugin. VPP-694 Basic GTP-U feature Change-Id: I31226f890a92c5303ac06e112ed7820cae52d9bd Signed-off-by: Hongjun Ni --- src/configure.ac | 1 + src/plugins/Makefile.am | 5 + src/plugins/gtpu.am | 38 + src/plugins/gtpu/gtpu.api | 120 +++ src/plugins/gtpu/gtpu.c | 1128 +++++++++++++++++++++++++++ src/plugins/gtpu/gtpu.h | 262 +++++++ src/plugins/gtpu/gtpu_all_api_h.h | 18 + src/plugins/gtpu/gtpu_api.c | 256 +++++++ src/plugins/gtpu/gtpu_decap.c | 1305 ++++++++++++++++++++++++++++++++ src/plugins/gtpu/gtpu_encap.c | 569 ++++++++++++++ src/plugins/gtpu/gtpu_error.def | 18 + src/plugins/gtpu/gtpu_msg_enum.h | 31 + src/plugins/gtpu/gtpu_test.c | 498 ++++++++++++ src/vnet/mfib/mfib_types.h | 2 + src/vnet/udp/udp.h | 2 + src/vpp-api/java/Makefile.am | 20 + src/vpp-api/java/jvpp-gtpu/jvpp_gtpu.c | 107 +++ src/vpp-api/java/jvpp-gtpu/jvpp_gtpu.h | 42 + test/test_gtpu.py | 289 +++++++ test/vpp_papi_provider.py | 32 + 20 files changed, 4743 insertions(+) create mode 100644 src/plugins/gtpu.am create mode 100644 src/plugins/gtpu/gtpu.api create mode 100644 src/plugins/gtpu/gtpu.c create mode 100644 src/plugins/gtpu/gtpu.h create mode 100644 src/plugins/gtpu/gtpu_all_api_h.h create mode 100644 src/plugins/gtpu/gtpu_api.c create mode 100644 src/plugins/gtpu/gtpu_decap.c create mode 100644 src/plugins/gtpu/gtpu_encap.c create mode 100644 src/plugins/gtpu/gtpu_error.def create mode 100644 src/plugins/gtpu/gtpu_msg_enum.h create mode 100644 src/plugins/gtpu/gtpu_test.c create mode 100644 src/vpp-api/java/jvpp-gtpu/jvpp_gtpu.c create mode 100644 src/vpp-api/java/jvpp-gtpu/jvpp_gtpu.h create mode 100644 test/test_gtpu.py (limited to 'src/vpp-api/java') diff --git a/src/configure.ac b/src/configure.ac index 581b0968..2a907b2f 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -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) diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index 623892e7..388c9ad2 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -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 diff --git a/src/plugins/gtpu.am b/src/plugins/gtpu.am new file mode 100644 index 00000000..9e5805b7 --- /dev/null +++ b/src/plugins/gtpu.am @@ -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 diff --git a/src/plugins/gtpu/gtpu.api b/src/plugins/gtpu/gtpu.api new file mode 100644 index 00000000..b11670b3 --- /dev/null +++ b/src/plugins/gtpu/gtpu.api @@ -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: + */ diff --git a/src/plugins/gtpu/gtpu.c b/src/plugins/gtpu/gtpu.c new file mode 100644 index 00000000..6a5c5a34 --- /dev/null +++ b/src/plugins/gtpu/gtpu.c @@ -0,0 +1,1128 @@ +/* + *------------------------------------------------------------------ + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +gtpu_main_t gtpu_main; + +static u8 * +format_decap_next (u8 * s, va_list * args) +{ + u32 next_index = va_arg (*args, u32); + + switch (next_index) + { + case GTPU_INPUT_NEXT_DROP: + return format (s, "drop"); + case GTPU_INPUT_NEXT_L2_INPUT: + return format (s, "l2"); + default: + return format (s, "index %d", next_index); + } + return s; +} + +u8 * +format_gtpu_tunnel (u8 * s, va_list * args) +{ + gtpu_tunnel_t *t = va_arg (*args, gtpu_tunnel_t *); + gtpu_main_t *ngm = >pu_main; + + s = format (s, "[%d] src %U dst %U teid %d sw_if_index %d ", + t - ngm->tunnels, + format_ip46_address, &t->src, IP46_TYPE_ANY, + format_ip46_address, &t->dst, IP46_TYPE_ANY, + t->teid, t->sw_if_index); + + if (ip46_address_is_multicast (&t->dst)) + s = format (s, "mcast_sw_if_index %d ", t->mcast_sw_if_index); + + s = format (s, "encap_fib_index %d fib_entry_index %d decap_next %U\n", + t->encap_fib_index, t->fib_entry_index, + format_decap_next, t->decap_next_index); + return s; +} + +static u8 * +format_gtpu_name (u8 * s, va_list * args) +{ + u32 dev_instance = va_arg (*args, u32); + return format (s, "gtpu_tunnel%d", dev_instance); +} + +static uword +dummy_interface_tx (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + clib_warning ("you shouldn't be here, leaking buffers..."); + return frame->n_vectors; +} + +static clib_error_t * +gtpu_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags) +{ + u32 hw_flags = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ? + VNET_HW_INTERFACE_FLAG_LINK_UP : 0; + vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags); + + return /* no error */ 0; +} + +/* *INDENT-OFF* */ +VNET_DEVICE_CLASS (gtpu_device_class,static) = { + .name = "GTPU", + .format_device_name = format_gtpu_name, + .format_tx_trace = format_gtpu_encap_trace, + .tx_function = dummy_interface_tx, + .admin_up_down_function = gtpu_interface_admin_up_down, +}; +/* *INDENT-ON* */ + +static u8 * +format_gtpu_header_with_length (u8 * s, va_list * args) +{ + u32 dev_instance = va_arg (*args, u32); + s = format (s, "unimplemented dev %u", dev_instance); + return s; +} + +/* *INDENT-OFF* */ +VNET_HW_INTERFACE_CLASS (gtpu_hw_class) = +{ + .name = "GTPU", + .format_header = format_gtpu_header_with_length, + .build_rewrite = default_build_rewrite, +}; +/* *INDENT-ON* */ + +static void +gtpu_tunnel_restack_dpo (gtpu_tunnel_t * t) +{ + dpo_id_t dpo = DPO_INVALID; + u32 encap_index = ip46_address_is_ip4 (&t->dst) ? + gtpu4_encap_node.index : gtpu6_encap_node.index; + fib_forward_chain_type_t forw_type = ip46_address_is_ip4 (&t->dst) ? + FIB_FORW_CHAIN_TYPE_UNICAST_IP4 : FIB_FORW_CHAIN_TYPE_UNICAST_IP6; + + fib_entry_contribute_forwarding (t->fib_entry_index, forw_type, &dpo); + dpo_stack_from_node (encap_index, &t->next_dpo, &dpo); + dpo_reset (&dpo); +} + +static gtpu_tunnel_t * +gtpu_tunnel_from_fib_node (fib_node_t * node) +{ + return ((gtpu_tunnel_t *) (((char *) node) - + STRUCT_OFFSET_OF (gtpu_tunnel_t, node))); +} + +/** + * Function definition to backwalk a FIB node - + * Here we will restack the new dpo of GTPU DIP to encap node. + */ +static fib_node_back_walk_rc_t +gtpu_tunnel_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx) +{ + gtpu_tunnel_restack_dpo (gtpu_tunnel_from_fib_node (node)); + return (FIB_NODE_BACK_WALK_CONTINUE); +} + +/** + * Function definition to get a FIB node from its index + */ +static fib_node_t * +gtpu_tunnel_fib_node_get (fib_node_index_t index) +{ + gtpu_tunnel_t *t; + gtpu_main_t *gtm = >pu_main; + + t = pool_elt_at_index (gtm->tunnels, index); + + return (&t->node); +} + +/** + * Function definition to inform the FIB node that its last lock has gone. + */ +static void +gtpu_tunnel_last_lock_gone (fib_node_t * node) +{ + /* + * The GTPU tunnel is a root of the graph. As such + * it never has children and thus is never locked. + */ + ASSERT (0); +} + +/* + * Virtual function table registered by GTPU tunnels + * for participation in the FIB object graph. + */ +const static fib_node_vft_t gtpu_vft = { + .fnv_get = gtpu_tunnel_fib_node_get, + .fnv_last_lock = gtpu_tunnel_last_lock_gone, + .fnv_back_walk = gtpu_tunnel_back_walk, +}; + + +#define foreach_copy_field \ +_(teid) \ +_(mcast_sw_if_index) \ +_(encap_fib_index) \ +_(decap_next_index) \ +_(src) \ +_(dst) + +static void +ip_udp_gtpu_rewrite (gtpu_tunnel_t * t, bool is_ip6) +{ + union + { + ip4_gtpu_header_t *h4; + ip6_gtpu_header_t *h6; + u8 *rw; + } r = + { + .rw = 0}; + int len = is_ip6 ? sizeof *r.h6 : sizeof *r.h4; + + vec_validate_aligned (r.rw, len - 1, CLIB_CACHE_LINE_BYTES); + + udp_header_t *udp; + gtpu_header_t *gtpu; + /* Fixed portion of the (outer) ip header */ + if (!is_ip6) + { + ip4_header_t *ip = &r.h4->ip4; + udp = &r.h4->udp; + gtpu = &r.h4->gtpu; + ip->ip_version_and_header_length = 0x45; + ip->ttl = 254; + ip->protocol = IP_PROTOCOL_UDP; + + ip->src_address = t->src.ip4; + ip->dst_address = t->dst.ip4; + + /* we fix up the ip4 header length and checksum after-the-fact */ + ip->checksum = ip4_header_checksum (ip); + } + else + { + ip6_header_t *ip = &r.h6->ip6; + udp = &r.h6->udp; + gtpu = &r.h6->gtpu; + ip->ip_version_traffic_class_and_flow_label = + clib_host_to_net_u32 (6 << 28); + ip->hop_limit = 255; + ip->protocol = IP_PROTOCOL_UDP; + + ip->src_address = t->src.ip6; + ip->dst_address = t->dst.ip6; + } + + /* UDP header, randomize src port on something, maybe? */ + udp->src_port = clib_host_to_net_u16 (2152); + udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_GTPU); + + /* GTPU header */ + gtpu->ver_flags = GTPU_V1_VER | GTPU_PT_GTP; + gtpu->type = GTPU_TYPE_GTPU; + gtpu->teid = clib_host_to_net_u32 (t->teid); + + t->rewrite = r.rw; + /* Now only support 8-byte gtpu header. TBD */ + _vec_len (t->rewrite) = sizeof (ip4_gtpu_header_t) - 4; + + return; +} + +static bool +gtpu_decap_next_is_valid (gtpu_main_t * gtm, u32 is_ip6, u32 decap_next_index) +{ + vlib_main_t *vm = gtm->vlib_main; + u32 input_idx = (!is_ip6) ? gtpu4_input_node.index : gtpu6_input_node.index; + vlib_node_runtime_t *r = vlib_node_get_runtime (vm, input_idx); + + return decap_next_index < r->n_next_nodes; +} + +static void +hash_set_key_copy (uword ** h, void *key, uword v) +{ + size_t ksz = hash_header (*h)->user; + void *copy = clib_mem_alloc (ksz); + clib_memcpy (copy, key, ksz); + hash_set_mem (*h, copy, v); +} + +static void +hash_unset_key_free (uword ** h, void *key) +{ + hash_pair_t *hp = hash_get_pair_mem (*h, key); + ASSERT (hp); + key = uword_to_pointer (hp->key, void *); + hash_unset_mem (*h, key); + clib_mem_free (key); +} + +static uword +vtep_addr_ref (ip46_address_t * ip) +{ + uword *vtep = ip46_address_is_ip4 (ip) ? + hash_get (gtpu_main.vtep4, ip->ip4.as_u32) : + hash_get_mem (gtpu_main.vtep6, &ip->ip6); + if (vtep) + return ++(*vtep); + ip46_address_is_ip4 (ip) ? + hash_set (gtpu_main.vtep4, ip->ip4.as_u32, 1) : + hash_set_key_copy (>pu_main.vtep6, &ip->ip6, 1); + return 1; +} + +static uword +vtep_addr_unref (ip46_address_t * ip) +{ + uword *vtep = ip46_address_is_ip4 (ip) ? + hash_get (gtpu_main.vtep4, ip->ip4.as_u32) : + hash_get_mem (gtpu_main.vtep6, &ip->ip6); + ASSERT (vtep); + if (--(*vtep) != 0) + return *vtep; + ip46_address_is_ip4 (ip) ? + hash_unset (gtpu_main.vtep4, ip->ip4.as_u32) : + hash_unset_key_free (>pu_main.vtep6, &ip->ip6); + return 0; +} + +typedef CLIB_PACKED (union + { + struct + { + fib_node_index_t mfib_entry_index; + adj_index_t mcast_adj_index; + }; u64 as_u64; + }) mcast_shared_t; + +static inline mcast_shared_t +mcast_shared_get (ip46_address_t * ip) +{ + ASSERT (ip46_address_is_multicast (ip)); + uword *p = hash_get_mem (gtpu_main.mcast_shared, ip); + ASSERT (p); + return (mcast_shared_t) + { + .as_u64 = *p}; +} + +static inline void +mcast_shared_add (ip46_address_t * dst, fib_node_index_t mfei, adj_index_t ai) +{ + mcast_shared_t new_ep = { + .mcast_adj_index = ai, + .mfib_entry_index = mfei, + }; + + hash_set_key_copy (>pu_main.mcast_shared, dst, new_ep.as_u64); +} + +static inline void +mcast_shared_remove (ip46_address_t * dst) +{ + mcast_shared_t ep = mcast_shared_get (dst); + + adj_unlock (ep.mcast_adj_index); + mfib_table_entry_delete_index (ep.mfib_entry_index, MFIB_SOURCE_GTPU); + + hash_unset_key_free (>pu_main.mcast_shared, dst); +} + +static inline fib_protocol_t +fib_ip_proto (bool is_ip6) +{ + return (is_ip6) ? FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4; +} + +int vnet_gtpu_add_del_tunnel + (vnet_gtpu_add_del_tunnel_args_t * a, u32 * sw_if_indexp) +{ + gtpu_main_t *gtm = >pu_main; + gtpu_tunnel_t *t = 0; + vnet_main_t *vnm = gtm->vnet_main; + uword *p; + u32 hw_if_index = ~0; + u32 sw_if_index = ~0; + gtpu4_tunnel_key_t key4; + gtpu6_tunnel_key_t key6; + u32 is_ip6 = a->is_ip6; + + if (!is_ip6) + { + key4.src = a->dst.ip4.as_u32; /* decap src in key is encap dst in config */ + key4.teid = clib_host_to_net_u32 (a->teid); + p = hash_get (gtm->gtpu4_tunnel_by_key, key4.as_u64); + } + else + { + key6.src = a->dst.ip6; + key6.teid = clib_host_to_net_u32 (a->teid); + p = hash_get_mem (gtm->gtpu6_tunnel_by_key, &key6); + } + + if (a->is_add) + { + l2input_main_t *l2im = &l2input_main; + + /* adding a tunnel: tunnel must not already exist */ + if (p) + return VNET_API_ERROR_TUNNEL_EXIST; + + /*if not set explicitly, default to l2 */ + if (a->decap_next_index == ~0) + a->decap_next_index = GTPU_INPUT_NEXT_L2_INPUT; + if (!gtpu_decap_next_is_valid (gtm, is_ip6, a->decap_next_index)) + return VNET_API_ERROR_INVALID_DECAP_NEXT; + + pool_get_aligned (gtm->tunnels, t, CLIB_CACHE_LINE_BYTES); + memset (t, 0, sizeof (*t)); + + /* copy from arg structure */ +#define _(x) t->x = a->x; + foreach_copy_field; +#undef _ + + ip_udp_gtpu_rewrite (t, is_ip6); + + /* copy the key */ + if (is_ip6) + hash_set_key_copy (>m->gtpu6_tunnel_by_key, &key6, + t - gtm->tunnels); + else + hash_set (gtm->gtpu4_tunnel_by_key, key4.as_u64, t - gtm->tunnels); + + vnet_hw_interface_t *hi; + if (vec_len (gtm->free_gtpu_tunnel_hw_if_indices) > 0) + { + vnet_interface_main_t *im = &vnm->interface_main; + hw_if_index = gtm->free_gtpu_tunnel_hw_if_indices + [vec_len (gtm->free_gtpu_tunnel_hw_if_indices) - 1]; + _vec_len (gtm->free_gtpu_tunnel_hw_if_indices) -= 1; + + hi = vnet_get_hw_interface (vnm, hw_if_index); + hi->dev_instance = t - gtm->tunnels; + hi->hw_instance = hi->dev_instance; + + /* clear old stats of freed tunnel before reuse */ + sw_if_index = hi->sw_if_index; + vnet_interface_counter_lock (im); + vlib_zero_combined_counter + (&im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_TX], + sw_if_index); + vlib_zero_combined_counter (&im->combined_sw_if_counters + [VNET_INTERFACE_COUNTER_RX], + sw_if_index); + vlib_zero_simple_counter (&im->sw_if_counters + [VNET_INTERFACE_COUNTER_DROP], + sw_if_index); + vnet_interface_counter_unlock (im); + } + else + { + hw_if_index = vnet_register_interface + (vnm, gtpu_device_class.index, t - gtm->tunnels, + gtpu_hw_class.index, t - gtm->tunnels); + hi = vnet_get_hw_interface (vnm, hw_if_index); + } + + t->hw_if_index = hw_if_index; + t->sw_if_index = sw_if_index = hi->sw_if_index; + + vec_validate_init_empty (gtm->tunnel_index_by_sw_if_index, sw_if_index, + ~0); + gtm->tunnel_index_by_sw_if_index[sw_if_index] = t - gtm->tunnels; + + /* setup l2 input config with l2 feature and bd 0 to drop packet */ + vec_validate (l2im->configs, sw_if_index); + l2im->configs[sw_if_index].feature_bitmap = L2INPUT_FEAT_DROP; + l2im->configs[sw_if_index].bd_index = 0; + + vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index); + si->flags &= ~VNET_SW_INTERFACE_FLAG_HIDDEN; + vnet_sw_interface_set_flags (vnm, sw_if_index, + VNET_SW_INTERFACE_FLAG_ADMIN_UP); + + fib_node_init (&t->node, gtm->fib_node_type); + fib_prefix_t tun_dst_pfx; + u32 encap_index = !is_ip6 ? + gtpu4_encap_node.index : gtpu6_encap_node.index; + vnet_flood_class_t flood_class = VNET_FLOOD_CLASS_TUNNEL_NORMAL; + + fib_prefix_from_ip46_addr (&t->dst, &tun_dst_pfx); + if (!ip46_address_is_multicast (&t->dst)) + { + /* Unicast tunnel - + * source the FIB entry for the tunnel's destination + * and become a child thereof. The tunnel will then get poked + * when the forwarding for the entry updates, and the tunnel can + * re-stack accordingly + */ + vtep_addr_ref (&t->src); + t->fib_entry_index = fib_table_entry_special_add + (t->encap_fib_index, &tun_dst_pfx, FIB_SOURCE_RR, + FIB_ENTRY_FLAG_NONE); + t->sibling_index = fib_entry_child_add + (t->fib_entry_index, gtm->fib_node_type, t - gtm->tunnels); + gtpu_tunnel_restack_dpo (t); + } + else + { + /* Multicast tunnel - + * as the same mcast group can be used for mutiple mcast tunnels + * with different VNIs, create the output fib adjecency only if + * it does not already exist + */ + fib_protocol_t fp = fib_ip_proto (is_ip6); + + if (vtep_addr_ref (&t->dst) == 1) + { + fib_node_index_t mfei; + adj_index_t ai; + fib_route_path_t path = { + .frp_proto = fp, + .frp_addr = zero_addr, + .frp_sw_if_index = 0xffffffff, + .frp_fib_index = ~0, + .frp_weight = 0, + .frp_flags = FIB_ROUTE_PATH_LOCAL, + }; + const mfib_prefix_t mpfx = { + .fp_proto = fp, + .fp_len = (is_ip6 ? 128 : 32), + .fp_grp_addr = tun_dst_pfx.fp_addr, + }; + + /* + * Setup the (*,G) to receive traffic on the mcast group + * - the forwarding interface is for-us + * - the accepting interface is that from the API + */ + mfib_table_entry_path_update (t->encap_fib_index, + &mpfx, + MFIB_SOURCE_GTPU, + &path, MFIB_ITF_FLAG_FORWARD); + + path.frp_sw_if_index = a->mcast_sw_if_index; + path.frp_flags = FIB_ROUTE_PATH_FLAG_NONE; + mfei = mfib_table_entry_path_update (t->encap_fib_index, + &mpfx, + MFIB_SOURCE_GTPU, + &path, + MFIB_ITF_FLAG_ACCEPT); + + /* + * Create the mcast adjacency to send traffic to the group + */ + ai = adj_mcast_add_or_lock (fp, + fib_proto_to_link (fp), + a->mcast_sw_if_index); + + /* + * create a new end-point + */ + mcast_shared_add (&t->dst, mfei, ai); + } + + dpo_id_t dpo = DPO_INVALID; + mcast_shared_t ep = mcast_shared_get (&t->dst); + + /* Stack shared mcast dst mac addr rewrite on encap */ + dpo_set (&dpo, DPO_ADJACENCY_MCAST, + fib_proto_to_dpo (fp), ep.mcast_adj_index); + + dpo_stack_from_node (encap_index, &t->next_dpo, &dpo); + + dpo_reset (&dpo); + flood_class = VNET_FLOOD_CLASS_TUNNEL_MASTER; + } + + /* Set gtpu tunnel output node */ + hi->output_node_index = encap_index; + + vnet_get_sw_interface (vnet_get_main (), sw_if_index)->flood_class = + flood_class; + } + else + { + /* deleting a tunnel: tunnel must exist */ + if (!p) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + t = pool_elt_at_index (gtm->tunnels, p[0]); + sw_if_index = t->sw_if_index; + + vnet_sw_interface_set_flags (vnm, t->sw_if_index, 0 /* down */ ); + vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, t->sw_if_index); + si->flags |= VNET_SW_INTERFACE_FLAG_HIDDEN; + + /* make sure tunnel is removed from l2 bd or xconnect */ + set_int_l2_mode (gtm->vlib_main, vnm, MODE_L3, t->sw_if_index, 0, 0, 0, + 0); + vec_add1 (gtm->free_gtpu_tunnel_hw_if_indices, t->hw_if_index); + + gtm->tunnel_index_by_sw_if_index[t->sw_if_index] = ~0; + + if (!is_ip6) + hash_unset (gtm->gtpu4_tunnel_by_key, key4.as_u64); + else + hash_unset_key_free (>m->gtpu6_tunnel_by_key, &key6); + + if (!ip46_address_is_multicast (&t->dst)) + { + vtep_addr_unref (&t->src); + fib_entry_child_remove (t->fib_entry_index, t->sibling_index); + fib_table_entry_delete_index (t->fib_entry_index, FIB_SOURCE_RR); + } + else if (vtep_addr_unref (&t->dst) == 0) + { + mcast_shared_remove (&t->dst); + } + + fib_node_deinit (&t->node); + vec_free (t->rewrite); + pool_put (gtm->tunnels, t); + } + + if (sw_if_indexp) + *sw_if_indexp = sw_if_index; + + return 0; +} + +static uword +get_decap_next_for_node (u32 node_index, u32 ipv4_set) +{ + gtpu_main_t *gtm = >pu_main; + vlib_main_t *vm = gtm->vlib_main; + uword input_node = (ipv4_set) ? gtpu4_input_node.index : + gtpu6_input_node.index; + + return vlib_node_add_next (vm, input_node, node_index); +} + +static uword +unformat_decap_next (unformat_input_t * input, va_list * args) +{ + u32 *result = va_arg (*args, u32 *); + u32 ipv4_set = va_arg (*args, int); + gtpu_main_t *gtm = >pu_main; + vlib_main_t *vm = gtm->vlib_main; + u32 node_index; + u32 tmp; + + if (unformat (input, "l2")) + *result = GTPU_INPUT_NEXT_L2_INPUT; + else if (unformat (input, "node %U", unformat_vlib_node, vm, &node_index)) + *result = get_decap_next_for_node (node_index, ipv4_set); + else if (unformat (input, "%d", &tmp)) + *result = tmp; + else + return 0; + return 1; +} + +static clib_error_t * +gtpu_add_del_tunnel_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + ip46_address_t src, dst; + u8 is_add = 1; + u8 src_set = 0; + u8 dst_set = 0; + u8 grp_set = 0; + u8 ipv4_set = 0; + u8 ipv6_set = 0; + u32 encap_fib_index = 0; + u32 mcast_sw_if_index = ~0; + u32 decap_next_index = GTPU_INPUT_NEXT_L2_INPUT; + u32 teid = 0; + u32 tmp; + int rv; + vnet_gtpu_add_del_tunnel_args_t _a, *a = &_a; + u32 tunnel_sw_if_index; + clib_error_t *error = NULL; + + /* Cant "universally zero init" (={0}) due to GCC bug 53119 */ + memset (&src, 0, sizeof src); + memset (&dst, 0, sizeof dst); + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "del")) + { + is_add = 0; + } + else if (unformat (line_input, "src %U", + unformat_ip4_address, &src.ip4)) + { + src_set = 1; + ipv4_set = 1; + } + else if (unformat (line_input, "dst %U", + unformat_ip4_address, &dst.ip4)) + { + dst_set = 1; + ipv4_set = 1; + } + else if (unformat (line_input, "src %U", + unformat_ip6_address, &src.ip6)) + { + src_set = 1; + ipv6_set = 1; + } + else if (unformat (line_input, "dst %U", + unformat_ip6_address, &dst.ip6)) + { + dst_set = 1; + ipv6_set = 1; + } + else if (unformat (line_input, "group %U %U", + unformat_ip4_address, &dst.ip4, + unformat_vnet_sw_interface, + vnet_get_main (), &mcast_sw_if_index)) + { + grp_set = dst_set = 1; + ipv4_set = 1; + } + else if (unformat (line_input, "group %U %U", + unformat_ip6_address, &dst.ip6, + unformat_vnet_sw_interface, + vnet_get_main (), &mcast_sw_if_index)) + { + grp_set = dst_set = 1; + ipv6_set = 1; + } + else if (unformat (line_input, "encap-vrf-id %d", &tmp)) + { + encap_fib_index = fib_table_find (fib_ip_proto (ipv6_set), tmp); + if (encap_fib_index == ~0) + { + error = + clib_error_return (0, "nonexistent encap-vrf-id %d", tmp); + goto done; + } + } + else if (unformat (line_input, "decap-next %U", unformat_decap_next, + &decap_next_index, ipv4_set)) + ; + else if (unformat (line_input, "teid %d", &teid)) + ; + else + { + error = clib_error_return (0, "parse error: '%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (src_set == 0) + { + error = clib_error_return (0, "tunnel src address not specified"); + goto done; + } + + if (dst_set == 0) + { + error = clib_error_return (0, "tunnel dst address not specified"); + goto done; + } + + if (grp_set && !ip46_address_is_multicast (&dst)) + { + error = clib_error_return (0, "tunnel group address not multicast"); + goto done; + } + + if (grp_set == 0 && ip46_address_is_multicast (&dst)) + { + error = clib_error_return (0, "dst address must be unicast"); + goto done; + } + + if (grp_set && mcast_sw_if_index == ~0) + { + error = clib_error_return (0, "tunnel nonexistent multicast device"); + goto done; + } + + if (ipv4_set && ipv6_set) + { + error = clib_error_return (0, "both IPv4 and IPv6 addresses specified"); + goto done; + } + + if (ip46_address_cmp (&src, &dst) == 0) + { + error = clib_error_return (0, "src and dst addresses are identical"); + goto done; + } + + if (decap_next_index == ~0) + { + error = clib_error_return (0, "next node not found"); + goto done; + } + + memset (a, 0, sizeof (*a)); + + a->is_add = is_add; + a->is_ip6 = ipv6_set; + +#define _(x) a->x = x; + foreach_copy_field; +#undef _ + + rv = vnet_gtpu_add_del_tunnel (a, &tunnel_sw_if_index); + + switch (rv) + { + case 0: + if (is_add) + vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name, + vnet_get_main (), tunnel_sw_if_index); + break; + + case VNET_API_ERROR_TUNNEL_EXIST: + error = clib_error_return (0, "tunnel already exists..."); + goto done; + + case VNET_API_ERROR_NO_SUCH_ENTRY: + error = clib_error_return (0, "tunnel does not exist..."); + goto done; + + default: + error = clib_error_return + (0, "vnet_gtpu_add_del_tunnel returned %d", rv); + goto done; + } + +done: + unformat_free (line_input); + + return error; +} + +/*? + * Add or delete a GTPU Tunnel. + * + * GTPU provides the features needed to allow L2 bridge domains (BDs) + * to span multiple servers. This is done by building an L2 overlay on + * top of an L3 network underlay using GTPU tunnels. + * + * This makes it possible for servers to be co-located in the same data + * center or be separated geographically as long as they are reachable + * through the underlay L3 network. + * + * You can refer to this kind of L2 overlay bridge domain as a GTPU + * (Virtual eXtensible VLAN) segment. + * + * @cliexpar + * Example of how to create a GTPU Tunnel: + * @cliexcmd{create gtpu tunnel src 10.0.3.1 dst 10.0.3.3 teid 13 encap-vrf-id 7} + * Example of how to delete a GTPU Tunnel: + * @cliexcmd{create gtpu tunnel src 10.0.3.1 dst 10.0.3.3 teid 13 del} + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (create_gtpu_tunnel_command, static) = { + .path = "create gtpu tunnel", + .short_help = + "create gtpu tunnel src " + " {dst |group } teid " + " [encap-vrf-id ] [decap-next [l2|node ]] [del]", + .function = gtpu_add_del_tunnel_command_fn, +}; +/* *INDENT-ON* */ + +static clib_error_t * +show_gtpu_tunnel_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + gtpu_main_t *gtm = >pu_main; + gtpu_tunnel_t *t; + + if (pool_elts (gtm->tunnels) == 0) + vlib_cli_output (vm, "No gtpu tunnels configured..."); + + pool_foreach (t, gtm->tunnels, ( + { + vlib_cli_output (vm, "%U", + format_gtpu_tunnel, t); + } + )); + + return 0; +} + +/*? + * Display all the GTPU Tunnel entries. + * + * @cliexpar + * Example of how to display the GTPU Tunnel entries: + * @cliexstart{show gtpu tunnel} + * [0] src 10.0.3.1 dst 10.0.3.3 teid 13 encap_fib_index 0 sw_if_index 5 decap_next l2 + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (show_gtpu_tunnel_command, static) = { + .path = "show gtpu tunnel", + .short_help = "show gtpu tunnel", + .function = show_gtpu_tunnel_command_fn, +}; +/* *INDENT-ON* */ + +void +vnet_int_gtpu_bypass_mode (u32 sw_if_index, u8 is_ip6, u8 is_enable) +{ + if (is_ip6) + vnet_feature_enable_disable ("ip6-unicast", "ip6-gtpu-bypass", + sw_if_index, is_enable, 0, 0); + else + vnet_feature_enable_disable ("ip4-unicast", "ip4-gtpu-bypass", + sw_if_index, is_enable, 0, 0); +} + +static clib_error_t * +set_ip_gtpu_bypass (u32 is_ip6, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + vnet_main_t *vnm = vnet_get_main (); + clib_error_t *error = 0; + u32 sw_if_index, is_enable; + + sw_if_index = ~0; + is_enable = 1; + + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat_user + (line_input, unformat_vnet_sw_interface, vnm, &sw_if_index)) + ; + else if (unformat (line_input, "del")) + is_enable = 0; + else + { + error = unformat_parse_error (line_input); + goto done; + } + } + + if (~0 == sw_if_index) + { + error = clib_error_return (0, "unknown interface `%U'", + format_unformat_error, line_input); + goto done; + } + + vnet_int_gtpu_bypass_mode (sw_if_index, is_ip6, is_enable); + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +set_ip4_gtpu_bypass (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + return set_ip_gtpu_bypass (0, input, cmd); +} + +/*? + * This command adds the 'ip4-gtpu-bypass' graph node for a given interface. + * By adding the IPv4 gtpu-bypass graph node to an interface, the node checks + * for and validate input gtpu packet and bypass ip4-lookup, ip4-local, + * ip4-udp-lookup nodes to speedup gtpu packet forwarding. This node will + * cause extra overhead to for non-gtpu packets which is kept at a minimum. + * + * @cliexpar + * @parblock + * Example of graph node before ip4-gtpu-bypass is enabled: + * @cliexstart{show vlib graph ip4-gtpu-bypass} + * Name Next Previous + * ip4-gtpu-bypass error-drop [0] + * gtpu4-input [1] + * ip4-lookup [2] + * @cliexend + * + * Example of how to enable ip4-gtpu-bypass on an interface: + * @cliexcmd{set interface ip gtpu-bypass GigabitEthernet2/0/0} + * + * Example of graph node after ip4-gtpu-bypass is enabled: + * @cliexstart{show vlib graph ip4-gtpu-bypass} + * Name Next Previous + * ip4-gtpu-bypass error-drop [0] ip4-input + * gtpu4-input [1] ip4-input-no-checksum + * ip4-lookup [2] + * @cliexend + * + * Example of how to display the feature enabed on an interface: + * @cliexstart{show ip interface features GigabitEthernet2/0/0} + * IP feature paths configured on GigabitEthernet2/0/0... + * ... + * ipv4 unicast: + * ip4-gtpu-bypass + * ip4-lookup + * ... + * @cliexend + * + * Example of how to disable ip4-gtpu-bypass on an interface: + * @cliexcmd{set interface ip gtpu-bypass GigabitEthernet2/0/0 del} + * @endparblock +?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (set_interface_ip_gtpu_bypass_command, static) = { + .path = "set interface ip gtpu-bypass", + .function = set_ip4_gtpu_bypass, + .short_help = "set interface ip gtpu-bypass [del]", +}; +/* *INDENT-ON* */ + +static clib_error_t * +set_ip6_gtpu_bypass (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + return set_ip_gtpu_bypass (1, input, cmd); +} + +/*? + * This command adds the 'ip6-gtpu-bypass' graph node for a given interface. + * By adding the IPv6 gtpu-bypass graph node to an interface, the node checks + * for and validate input gtpu packet and bypass ip6-lookup, ip6-local, + * ip6-udp-lookup nodes to speedup gtpu packet forwarding. This node will + * cause extra overhead to for non-gtpu packets which is kept at a minimum. + * + * @cliexpar + * @parblock + * Example of graph node before ip6-gtpu-bypass is enabled: + * @cliexstart{show vlib graph ip6-gtpu-bypass} + * Name Next Previous + * ip6-gtpu-bypass error-drop [0] + * gtpu6-input [1] + * ip6-lookup [2] + * @cliexend + * + * Example of how to enable ip6-gtpu-bypass on an interface: + * @cliexcmd{set interface ip6 gtpu-bypass GigabitEthernet2/0/0} + * + * Example of graph node after ip6-gtpu-bypass is enabled: + * @cliexstart{show vlib graph ip6-gtpu-bypass} + * Name Next Previous + * ip6-gtpu-bypass error-drop [0] ip6-input + * gtpu6-input [1] ip4-input-no-checksum + * ip6-lookup [2] + * @cliexend + * + * Example of how to display the feature enabed on an interface: + * @cliexstart{show ip interface features GigabitEthernet2/0/0} + * IP feature paths configured on GigabitEthernet2/0/0... + * ... + * ipv6 unicast: + * ip6-gtpu-bypass + * ip6-lookup + * ... + * @cliexend + * + * Example of how to disable ip6-gtpu-bypass on an interface: + * @cliexcmd{set interface ip6 gtpu-bypass GigabitEthernet2/0/0 del} + * @endparblock +?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (set_interface_ip6_gtpu_bypass_command, static) = { + .path = "set interface ip6 gtpu-bypass", + .function = set_ip6_gtpu_bypass, + .short_help = "set interface ip gtpu-bypass [del]", +}; +/* *INDENT-ON* */ + +clib_error_t * +gtpu_init (vlib_main_t * vm) +{ + gtpu_main_t *gtm = >pu_main; + + gtm->vnet_main = vnet_get_main (); + gtm->vlib_main = vm; + + /* initialize the ip6 hash */ + gtm->gtpu6_tunnel_by_key = hash_create_mem (0, + sizeof (gtpu6_tunnel_key_t), + sizeof (uword)); + gtm->vtep6 = hash_create_mem (0, sizeof (ip6_address_t), sizeof (uword)); + gtm->mcast_shared = hash_create_mem (0, + sizeof (ip46_address_t), + sizeof (mcast_shared_t)); + + udp_register_dst_port (vm, UDP_DST_PORT_GTPU, + gtpu4_input_node.index, /* is_ip4 */ 1); + udp_register_dst_port (vm, UDP_DST_PORT_GTPU6, + gtpu6_input_node.index, /* is_ip4 */ 0); + + gtm->fib_node_type = fib_node_register_new_type (>pu_vft); + + return 0; +} + +VLIB_INIT_FUNCTION (gtpu_init); + +/* *INDENT-OFF* */ +VLIB_PLUGIN_REGISTER () = { + .version = VPP_BUILD_VER, + .description = "GTPv1-U", +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/gtpu/gtpu.h b/src/plugins/gtpu/gtpu.h new file mode 100644 index 00000000..86861974 --- /dev/null +++ b/src/plugins/gtpu/gtpu.h @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * 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 +#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: + */ diff --git a/src/plugins/gtpu/gtpu_all_api_h.h b/src/plugins/gtpu/gtpu_all_api_h.h new file mode 100644 index 00000000..a4181ae3 --- /dev/null +++ b/src/plugins/gtpu/gtpu_all_api_h.h @@ -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 diff --git a/src/plugins/gtpu/gtpu_api.c b/src/plugins/gtpu/gtpu_api.c new file mode 100644 index 00000000..58f48c71 --- /dev/null +++ b/src/plugins/gtpu/gtpu_api.c @@ -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 +#include +#include +#include + +#include +#include +#include + +#include + + +#define vl_msg_id(n,h) n, +typedef enum +{ +#include + /* 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 +#undef vl_typedefs + +/* define generated endian-swappers */ +#define vl_endianfun +#include +#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 +#undef vl_printfun + +/* Get the API version number */ +#define vl_api_version(n,v) static u32 api_version=(v); +#include +#undef vl_api_version + +#define vl_msg_name_crc_list +#include +#undef vl_msg_name_crc_list + +#define REPLY_MSG_ID_BASE gtm->msg_id_base +#include + +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 = >pu_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 = >pu_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 = >pu_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 = >m->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 = >pu_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: + */ diff --git a/src/plugins/gtpu/gtpu_decap.c b/src/plugins/gtpu/gtpu_decap.c new file mode 100644 index 00000000..b0be32ec --- /dev/null +++ b/src/plugins/gtpu/gtpu_decap.c @@ -0,0 +1,1305 @@ +/* + * decap.c: gtpu tunnel decap packet processing + * + * Copyright (c) 2013 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 +#include +#include + +vlib_node_registration_t gtpu4_input_node; +vlib_node_registration_t gtpu6_input_node; + +typedef struct { + u32 next_index; + u32 tunnel_index; + u32 error; + u32 teid; +} gtpu_rx_trace_t; + +static u8 * format_gtpu_rx_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 *); + gtpu_rx_trace_t * t = va_arg (*args, gtpu_rx_trace_t *); + + if (t->tunnel_index != ~0) + { + s = format (s, "GTPU decap from gtpu_tunnel%d teid %d next %d error %d", + t->tunnel_index, t->teid, t->next_index, t->error); + } + else + { + s = format (s, "GTPU decap error - tunnel for teid %d does not exist", + t->teid); + } + return s; +} + +always_inline u32 +validate_gtpu_fib (vlib_buffer_t *b, gtpu_tunnel_t *t, u32 is_ip4) +{ + u32 fib_index, sw_if_index; + + sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX]; + + if (is_ip4) + fib_index = (vnet_buffer (b)->sw_if_index[VLIB_TX] == (u32) ~ 0) ? + vec_elt (ip4_main.fib_index_by_sw_if_index, sw_if_index) : + vnet_buffer (b)->sw_if_index[VLIB_TX]; + else + fib_index = (vnet_buffer (b)->sw_if_index[VLIB_TX] == (u32) ~ 0) ? + vec_elt (ip6_main.fib_index_by_sw_if_index, sw_if_index) : + vnet_buffer (b)->sw_if_index[VLIB_TX]; + + return (fib_index == t->encap_fib_index); +} + +always_inline uword +gtpu_input (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame, + u32 is_ip4) +{ + u32 n_left_from, next_index, * from, * to_next; + gtpu_main_t * gtm = >pu_main; + vnet_main_t * vnm = gtm->vnet_main; + vnet_interface_main_t * im = &vnm->interface_main; + u32 last_tunnel_index = ~0; + gtpu4_tunnel_key_t last_key4; + gtpu6_tunnel_key_t last_key6; + u32 pkts_decapsulated = 0; + u32 thread_index = vlib_get_thread_index(); + u32 stats_sw_if_index, stats_n_packets, stats_n_bytes; + + if (is_ip4) + last_key4.as_u64 = ~0; + else + memset (&last_key6, 0xff, sizeof (last_key6)); + + from = vlib_frame_vector_args (from_frame); + n_left_from = from_frame->n_vectors; + + next_index = node->cached_next_index; + stats_sw_if_index = node->runtime_data[0]; + stats_n_packets = stats_n_bytes = 0; + + 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 >= 4 && n_left_to_next >= 2) + { + u32 bi0, bi1; + vlib_buffer_t * b0, * b1; + u32 next0, next1; + ip4_header_t * ip4_0, * ip4_1; + ip6_header_t * ip6_0, * ip6_1; + gtpu_header_t * gtpu0, * gtpu1; + u32 gtpu_hdr_len0 = 0, gtpu_hdr_len1 =0 ; + uword * p0, * p1; + u32 tunnel_index0, tunnel_index1; + gtpu_tunnel_t * t0, * t1, * mt0 = NULL, * mt1 = NULL; + gtpu4_tunnel_key_t key4_0, key4_1; + gtpu6_tunnel_key_t key6_0, key6_1; + u32 error0, error1; + u32 sw_if_index0, sw_if_index1, len0, len1; + + /* Prefetch next iteration. */ + { + vlib_buffer_t * p2, * p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header (p2, LOAD); + vlib_prefetch_buffer_header (p3, LOAD); + + CLIB_PREFETCH (p2->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); + CLIB_PREFETCH (p3->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); + } + + bi0 = from[0]; + bi1 = from[1]; + to_next[0] = bi0; + to_next[1] = bi1; + from += 2; + to_next += 2; + n_left_to_next -= 2; + n_left_from -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + /* udp leaves current_data pointing at the gtpu header */ + gtpu0 = vlib_buffer_get_current (b0); + gtpu1 = vlib_buffer_get_current (b1); + if (is_ip4) { + vlib_buffer_advance + (b0, -(word)(sizeof(udp_header_t)+sizeof(ip4_header_t))); + vlib_buffer_advance + (b1, -(word)(sizeof(udp_header_t)+sizeof(ip4_header_t))); + ip4_0 = vlib_buffer_get_current (b0); + ip4_1 = vlib_buffer_get_current (b1); + } else { + vlib_buffer_advance + (b0, -(word)(sizeof(udp_header_t)+sizeof(ip6_header_t))); + vlib_buffer_advance + (b1, -(word)(sizeof(udp_header_t)+sizeof(ip6_header_t))); + ip6_0 = vlib_buffer_get_current (b0); + ip6_1 = vlib_buffer_get_current (b1); + } + + /* pop (ip, udp, gtpu) */ + if (is_ip4) { + vlib_buffer_advance + (b0, sizeof(*ip4_0)+sizeof(udp_header_t)); + vlib_buffer_advance + (b1, sizeof(*ip4_1)+sizeof(udp_header_t)); + } else { + vlib_buffer_advance + (b0, sizeof(*ip6_0)+sizeof(udp_header_t)); + vlib_buffer_advance + (b1, sizeof(*ip6_1)+sizeof(udp_header_t)); + } + + tunnel_index0 = ~0; + error0 = 0; + + tunnel_index1 = ~0; + error1 = 0; + + if (PREDICT_FALSE ((gtpu0->ver_flags & GTPU_VER_MASK) != GTPU_V1_VER)) + { + error0 = GTPU_ERROR_BAD_VER; + next0 = GTPU_INPUT_NEXT_DROP; + goto trace0; + } + + /* Manipulate packet 0 */ + if (is_ip4) { + key4_0.src = ip4_0->src_address.as_u32; + key4_0.teid = gtpu0->teid; + + /* Make sure GTPU tunnel exist according to packet SIP and teid + * SIP identify a GTPU path, and teid identify a tunnel in a given GTPU path */ + if (PREDICT_FALSE (key4_0.as_u64 != last_key4.as_u64)) + { + p0 = hash_get (gtm->gtpu4_tunnel_by_key, key4_0.as_u64); + if (PREDICT_FALSE (p0 == NULL)) + { + error0 = GTPU_ERROR_NO_SUCH_TUNNEL; + next0 = GTPU_INPUT_NEXT_DROP; + goto trace0; + } + last_key4.as_u64 = key4_0.as_u64; + tunnel_index0 = last_tunnel_index = p0[0]; + } + else + tunnel_index0 = last_tunnel_index; + t0 = pool_elt_at_index (gtm->tunnels, tunnel_index0); + + /* Validate GTPU tunnel encap-fib index agaist packet */ + if (PREDICT_FALSE (validate_gtpu_fib (b0, t0, is_ip4) == 0)) + { + error0 = GTPU_ERROR_NO_SUCH_TUNNEL; + next0 = GTPU_INPUT_NEXT_DROP; + goto trace0; + } + + /* Validate GTPU tunnel SIP against packet DIP */ + if (PREDICT_TRUE (ip4_0->dst_address.as_u32 == t0->src.ip4.as_u32)) + goto next0; /* valid packet */ + if (PREDICT_FALSE (ip4_address_is_multicast (&ip4_0->dst_address))) + { + key4_0.src = ip4_0->dst_address.as_u32; + key4_0.teid = gtpu0->teid; + /* Make sure mcast GTPU tunnel exist by packet DIP and teid */ + p0 = hash_get (gtm->gtpu4_tunnel_by_key, key4_0.as_u64); + if (PREDICT_TRUE (p0 != NULL)) + { + mt0 = pool_elt_at_index (gtm->tunnels, p0[0]); + goto next0; /* valid packet */ + } + } + error0 = GTPU_ERROR_NO_SUCH_TUNNEL; + next0 = GTPU_INPUT_NEXT_DROP; + goto trace0; + + } else /* !is_ip4 */ { + key6_0.src.as_u64[0] = ip6_0->src_address.as_u64[0]; + key6_0.src.as_u64[1] = ip6_0->src_address.as_u64[1]; + key6_0.teid = gtpu0->teid; + + /* Make sure GTPU tunnel exist according to packet SIP and teid + * SIP identify a GTPU path, and teid identify a tunnel in a given GTPU path */ + if (PREDICT_FALSE (memcmp(&key6_0, &last_key6, sizeof(last_key6)) != 0)) + { + p0 = hash_get_mem (gtm->gtpu6_tunnel_by_key, &key6_0); + if (PREDICT_FALSE (p0 == NULL)) + { + error0 = GTPU_ERROR_NO_SUCH_TUNNEL; + next0 = GTPU_INPUT_NEXT_DROP; + goto trace0; + } + clib_memcpy (&last_key6, &key6_0, sizeof(key6_0)); + tunnel_index0 = last_tunnel_index = p0[0]; + } + else + tunnel_index0 = last_tunnel_index; + t0 = pool_elt_at_index (gtm->tunnels, tunnel_index0); + + /* Validate GTPU tunnel encap-fib index agaist packet */ + if (PREDICT_FALSE (validate_gtpu_fib (b0, t0, is_ip4) == 0)) + { + error0 = GTPU_ERROR_NO_SUCH_TUNNEL; + next0 = GTPU_INPUT_NEXT_DROP; + goto trace0; + } + + /* Validate GTPU tunnel SIP against packet DIP */ + if (PREDICT_TRUE (ip6_address_is_equal (&ip6_0->dst_address, + &t0->src.ip6))) + goto next0; /* valid packet */ + if (PREDICT_FALSE (ip6_address_is_multicast (&ip6_0->dst_address))) + { + key6_0.src.as_u64[0] = ip6_0->dst_address.as_u64[0]; + key6_0.src.as_u64[1] = ip6_0->dst_address.as_u64[1]; + key6_0.teid = gtpu0->teid; + p0 = hash_get_mem (gtm->gtpu6_tunnel_by_key, &key6_0); + if (PREDICT_TRUE (p0 != NULL)) + { + mt0 = pool_elt_at_index (gtm->tunnels, p0[0]); + goto next0; /* valid packet */ + } + } + error0 = GTPU_ERROR_NO_SUCH_TUNNEL; + next0 = GTPU_INPUT_NEXT_DROP; + goto trace0; + } + + next0: + /* Manipulate gtpu header */ + if (PREDICT_FALSE((gtpu0->ver_flags & GTPU_E_S_PN_BIT) != 0)) + { + gtpu_hdr_len0 = sizeof(gtpu_header_t); + + /* Manipulate Sequence Number and N-PDU Number */ + /* TBD */ + + /* Manipulate Next Extension Header */ + /* TBD */ + } + else + { + gtpu_hdr_len0 = sizeof(gtpu_header_t) - 4; + } + + /* Pop gtpu header */ + vlib_buffer_advance (b0, gtpu_hdr_len0); + + next0 = t0->decap_next_index; + sw_if_index0 = t0->sw_if_index; + len0 = vlib_buffer_length_in_chain (vm, b0); + + /* Required to make the l2 tag push / pop code work on l2 subifs */ + if (PREDICT_TRUE(next0 == GTPU_INPUT_NEXT_L2_INPUT)) + vnet_update_l2_len (b0); + + /* Set packet input sw_if_index to unicast GTPU tunnel for learning */ + vnet_buffer(b0)->sw_if_index[VLIB_RX] = sw_if_index0; + sw_if_index0 = (mt0) ? mt0->sw_if_index : sw_if_index0; + + pkts_decapsulated ++; + stats_n_packets += 1; + stats_n_bytes += len0; + + /* Batch stats increment on the same gtpu tunnel so counter + is not incremented per packet */ + if (PREDICT_FALSE (sw_if_index0 != stats_sw_if_index)) + { + stats_n_packets -= 1; + stats_n_bytes -= len0; + if (stats_n_packets) + vlib_increment_combined_counter + (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX, + thread_index, stats_sw_if_index, + stats_n_packets, stats_n_bytes); + stats_n_packets = 1; + stats_n_bytes = len0; + stats_sw_if_index = sw_if_index0; + } + + trace0: + b0->error = error0 ? node->errors[error0] : 0; + + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + gtpu_rx_trace_t *tr + = vlib_add_trace (vm, node, b0, sizeof (*tr)); + tr->next_index = next0; + tr->error = error0; + tr->tunnel_index = tunnel_index0; + tr->teid = clib_net_to_host_u32(gtpu0->teid); + } + + if (PREDICT_FALSE ((gtpu1->ver_flags & GTPU_VER_MASK) != GTPU_V1_VER)) + { + error1 = GTPU_ERROR_BAD_VER; + next1 = GTPU_INPUT_NEXT_DROP; + goto trace1; + } + + /* Manipulate packet 1 */ + if (is_ip4) { + key4_1.src = ip4_1->src_address.as_u32; + key4_1.teid = gtpu1->teid; + + /* Make sure GTPU tunnel exist according to packet SIP and teid + * SIP identify a GTPU path, and teid identify a tunnel in a given GTPU path */ + if (PREDICT_FALSE (key4_1.as_u64 != last_key4.as_u64)) + { + p1 = hash_get (gtm->gtpu4_tunnel_by_key, key4_1.as_u64); + if (PREDICT_FALSE (p1 == NULL)) + { + error1 = GTPU_ERROR_NO_SUCH_TUNNEL; + next1 = GTPU_INPUT_NEXT_DROP; + goto trace1; + } + last_key4.as_u64 = key4_1.as_u64; + tunnel_index1 = last_tunnel_index = p1[0]; + } + else + tunnel_index1 = last_tunnel_index; + t1 = pool_elt_at_index (gtm->tunnels, tunnel_index1); + + /* Validate GTPU tunnel encap-fib index agaist packet */ + if (PREDICT_FALSE (validate_gtpu_fib (b1, t1, is_ip4) == 0)) + { + error1 = GTPU_ERROR_NO_SUCH_TUNNEL; + next1 = GTPU_INPUT_NEXT_DROP; + goto trace1; + } + + /* Validate GTPU tunnel SIP against packet DIP */ + if (PREDICT_TRUE (ip4_1->dst_address.as_u32 == t1->src.ip4.as_u32)) + goto next1; /* valid packet */ + if (PREDICT_FALSE (ip4_address_is_multicast (&ip4_1->dst_address))) + { + key4_1.src = ip4_1->dst_address.as_u32; + key4_1.teid = gtpu1->teid; + /* Make sure mcast GTPU tunnel exist by packet DIP and teid */ + p1 = hash_get (gtm->gtpu4_tunnel_by_key, key4_1.as_u64); + if (PREDICT_TRUE (p1 != NULL)) + { + mt1 = pool_elt_at_index (gtm->tunnels, p1[0]); + goto next1; /* valid packet */ + } + } + error1 = GTPU_ERROR_NO_SUCH_TUNNEL; + next1 = GTPU_INPUT_NEXT_DROP; + goto trace1; + + } else /* !is_ip4 */ { + key6_1.src.as_u64[0] = ip6_1->src_address.as_u64[0]; + key6_1.src.as_u64[1] = ip6_1->src_address.as_u64[1]; + key6_1.teid = gtpu1->teid; + + /* Make sure GTPU tunnel exist according to packet SIP and teid + * SIP identify a GTPU path, and teid identify a tunnel in a given GTPU path */ + if (PREDICT_FALSE (memcmp(&key6_1, &last_key6, sizeof(last_key6)) != 0)) + { + p1 = hash_get_mem (gtm->gtpu6_tunnel_by_key, &key6_1); + + if (PREDICT_FALSE (p1 == NULL)) + { + error1 = GTPU_ERROR_NO_SUCH_TUNNEL; + next1 = GTPU_INPUT_NEXT_DROP; + goto trace1; + } + + clib_memcpy (&last_key6, &key6_1, sizeof(key6_1)); + tunnel_index1 = last_tunnel_index = p1[0]; + } + else + tunnel_index1 = last_tunnel_index; + t1 = pool_elt_at_index (gtm->tunnels, tunnel_index1); + + /* Validate GTPU tunnel encap-fib index agaist packet */ + if (PREDICT_FALSE (validate_gtpu_fib (b1, t1, is_ip4) == 0)) + { + error1 = GTPU_ERROR_NO_SUCH_TUNNEL; + next1 = GTPU_INPUT_NEXT_DROP; + goto trace1; + } + + /* Validate GTPU tunnel SIP against packet DIP */ + if (PREDICT_TRUE (ip6_address_is_equal (&ip6_1->dst_address, + &t1->src.ip6))) + goto next1; /* valid packet */ + if (PREDICT_FALSE (ip6_address_is_multicast (&ip6_1->dst_address))) + { + key6_1.src.as_u64[0] = ip6_1->dst_address.as_u64[0]; + key6_1.src.as_u64[1] = ip6_1->dst_address.as_u64[1]; + key6_1.teid = gtpu1->teid; + p1 = hash_get_mem (gtm->gtpu6_tunnel_by_key, &key6_1); + if (PREDICT_TRUE (p1 != NULL)) + { + mt1 = pool_elt_at_index (gtm->tunnels, p1[0]); + goto next1; /* valid packet */ + } + } + error1 = GTPU_ERROR_NO_SUCH_TUNNEL; + next1 = GTPU_INPUT_NEXT_DROP; + goto trace1; + } + + next1: + /* Manipulate gtpu header */ + if (PREDICT_FALSE((gtpu1->ver_flags & GTPU_E_S_PN_BIT) != 0)) + { + gtpu_hdr_len1 = sizeof(gtpu_header_t); + + /* Manipulate Sequence Number and N-PDU Number */ + /* TBD */ + + /* Manipulate Next Extension Header */ + /* TBD */ + } + else + { + gtpu_hdr_len1 = sizeof(gtpu_header_t) - 4; + } + + /* Pop gtpu header */ + vlib_buffer_advance (b1, gtpu_hdr_len1); + + next1 = t1->decap_next_index; + sw_if_index1 = t1->sw_if_index; + len1 = vlib_buffer_length_in_chain (vm, b1); + + /* Required to make the l2 tag push / pop code work on l2 subifs */ + if (PREDICT_TRUE(next1 == GTPU_INPUT_NEXT_L2_INPUT)) + vnet_update_l2_len (b1); + + /* Set packet input sw_if_index to unicast GTPU tunnel for learning */ + vnet_buffer(b1)->sw_if_index[VLIB_RX] = sw_if_index1; + sw_if_index1 = (mt1) ? mt1->sw_if_index : sw_if_index1; + + pkts_decapsulated ++; + stats_n_packets += 1; + stats_n_bytes += len1; + + /* Batch stats increment on the same gtpu tunnel so counter + is not incremented per packet */ + if (PREDICT_FALSE (sw_if_index1 != stats_sw_if_index)) + { + stats_n_packets -= 1; + stats_n_bytes -= len1; + if (stats_n_packets) + vlib_increment_combined_counter + (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX, + thread_index, stats_sw_if_index, + stats_n_packets, stats_n_bytes); + stats_n_packets = 1; + stats_n_bytes = len1; + stats_sw_if_index = sw_if_index1; + } + + trace1: + b1->error = error1 ? node->errors[error1] : 0; + + if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED)) + { + gtpu_rx_trace_t *tr + = vlib_add_trace (vm, node, b1, sizeof (*tr)); + tr->next_index = next1; + tr->error = error1; + tr->tunnel_index = tunnel_index1; + tr->teid = clib_net_to_host_u32(gtpu1->teid); + } + + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, next0, next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t * b0; + u32 next0; + ip4_header_t * ip4_0; + ip6_header_t * ip6_0; + gtpu_header_t * gtpu0; + u32 gtpu_hdr_len0 = 0; + uword * p0; + u32 tunnel_index0; + gtpu_tunnel_t * t0, * mt0 = NULL; + gtpu4_tunnel_key_t key4_0; + gtpu6_tunnel_key_t key6_0; + u32 error0; + u32 sw_if_index0, len0; + + 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); + + /* udp leaves current_data pointing at the gtpu header */ + gtpu0 = vlib_buffer_get_current (b0); + if (is_ip4) { + vlib_buffer_advance + (b0, -(word)(sizeof(udp_header_t)+sizeof(ip4_header_t))); + ip4_0 = vlib_buffer_get_current (b0); + } else { + vlib_buffer_advance + (b0, -(word)(sizeof(udp_header_t)+sizeof(ip6_header_t))); + ip6_0 = vlib_buffer_get_current (b0); + } + + /* pop (ip, udp) */ + if (is_ip4) { + vlib_buffer_advance + (b0, sizeof(*ip4_0)+sizeof(udp_header_t)); + } else { + vlib_buffer_advance + (b0, sizeof(*ip6_0)+sizeof(udp_header_t)); + } + + tunnel_index0 = ~0; + error0 = 0; + if (PREDICT_FALSE ((gtpu0->ver_flags & GTPU_VER_MASK) != GTPU_V1_VER)) + { + error0 = GTPU_ERROR_BAD_VER; + next0 = GTPU_INPUT_NEXT_DROP; + goto trace00; + } + + if (is_ip4) { + key4_0.src = ip4_0->src_address.as_u32; + key4_0.teid = gtpu0->teid; + + /* Make sure GTPU tunnel exist according to packet SIP and teid + * SIP identify a GTPU path, and teid identify a tunnel in a given GTPU path */ + if (PREDICT_FALSE (key4_0.as_u64 != last_key4.as_u64)) + { + p0 = hash_get (gtm->gtpu4_tunnel_by_key, key4_0.as_u64); + if (PREDICT_FALSE (p0 == NULL)) + { + error0 = GTPU_ERROR_NO_SUCH_TUNNEL; + next0 = GTPU_INPUT_NEXT_DROP; + goto trace00; + } + last_key4.as_u64 = key4_0.as_u64; + tunnel_index0 = last_tunnel_index = p0[0]; + } + else + tunnel_index0 = last_tunnel_index; + t0 = pool_elt_at_index (gtm->tunnels, tunnel_index0); + + /* Validate GTPU tunnel encap-fib index agaist packet */ + if (PREDICT_FALSE (validate_gtpu_fib (b0, t0, is_ip4) == 0)) + { + error0 = GTPU_ERROR_NO_SUCH_TUNNEL; + next0 = GTPU_INPUT_NEXT_DROP; + goto trace00; + } + + /* Validate GTPU tunnel SIP against packet DIP */ + if (PREDICT_TRUE (ip4_0->dst_address.as_u32 == t0->src.ip4.as_u32)) + goto next00; /* valid packet */ + if (PREDICT_FALSE (ip4_address_is_multicast (&ip4_0->dst_address))) + { + key4_0.src = ip4_0->dst_address.as_u32; + key4_0.teid = gtpu0->teid; + /* Make sure mcast GTPU tunnel exist by packet DIP and teid */ + p0 = hash_get (gtm->gtpu4_tunnel_by_key, key4_0.as_u64); + if (PREDICT_TRUE (p0 != NULL)) + { + mt0 = pool_elt_at_index (gtm->tunnels, p0[0]); + goto next00; /* valid packet */ + } + } + error0 = GTPU_ERROR_NO_SUCH_TUNNEL; + next0 = GTPU_INPUT_NEXT_DROP; + goto trace00; + + } else /* !is_ip4 */ { + key6_0.src.as_u64[0] = ip6_0->src_address.as_u64[0]; + key6_0.src.as_u64[1] = ip6_0->src_address.as_u64[1]; + key6_0.teid = gtpu0->teid; + + /* Make sure GTPU tunnel exist according to packet SIP and teid + * SIP identify a GTPU path, and teid identify a tunnel in a given GTPU path */ + if (PREDICT_FALSE (memcmp(&key6_0, &last_key6, sizeof(last_key6)) != 0)) + { + p0 = hash_get_mem (gtm->gtpu6_tunnel_by_key, &key6_0); + if (PREDICT_FALSE (p0 == NULL)) + { + error0 = GTPU_ERROR_NO_SUCH_TUNNEL; + next0 = GTPU_INPUT_NEXT_DROP; + goto trace00; + } + clib_memcpy (&last_key6, &key6_0, sizeof(key6_0)); + tunnel_index0 = last_tunnel_index = p0[0]; + } + else + tunnel_index0 = last_tunnel_index; + t0 = pool_elt_at_index (gtm->tunnels, tunnel_index0); + + /* Validate GTPU tunnel encap-fib index agaist packet */ + if (PREDICT_FALSE (validate_gtpu_fib (b0, t0, is_ip4) == 0)) + { + error0 = GTPU_ERROR_NO_SUCH_TUNNEL; + next0 = GTPU_INPUT_NEXT_DROP; + goto trace00; + } + + /* Validate GTPU tunnel SIP against packet DIP */ + if (PREDICT_TRUE (ip6_address_is_equal (&ip6_0->dst_address, + &t0->src.ip6))) + goto next00; /* valid packet */ + if (PREDICT_FALSE (ip6_address_is_multicast (&ip6_0->dst_address))) + { + key6_0.src.as_u64[0] = ip6_0->dst_address.as_u64[0]; + key6_0.src.as_u64[1] = ip6_0->dst_address.as_u64[1]; + key6_0.teid = gtpu0->teid; + p0 = hash_get_mem (gtm->gtpu6_tunnel_by_key, &key6_0); + if (PREDICT_TRUE (p0 != NULL)) + { + mt0 = pool_elt_at_index (gtm->tunnels, p0[0]); + goto next00; /* valid packet */ + } + } + error0 = GTPU_ERROR_NO_SUCH_TUNNEL; + next0 = GTPU_INPUT_NEXT_DROP; + goto trace00; + } + + next00: + /* Manipulate gtpu header */ + if (PREDICT_FALSE((gtpu0->ver_flags & GTPU_E_S_PN_BIT) != 0)) + { + gtpu_hdr_len0 = sizeof(gtpu_header_t); + + /* Manipulate Sequence Number and N-PDU Number */ + /* TBD */ + + /* Manipulate Next Extension Header */ + /* TBD */ + } + else + { + gtpu_hdr_len0 = sizeof(gtpu_header_t) - 4; + } + + /* Pop gtpu header */ + vlib_buffer_advance (b0, gtpu_hdr_len0); + + next0 = t0->decap_next_index; + sw_if_index0 = t0->sw_if_index; + len0 = vlib_buffer_length_in_chain (vm, b0); + + /* Required to make the l2 tag push / pop code work on l2 subifs */ + if (PREDICT_TRUE(next0 == GTPU_INPUT_NEXT_L2_INPUT)) + vnet_update_l2_len (b0); + + /* Set packet input sw_if_index to unicast GTPU tunnel for learning */ + vnet_buffer(b0)->sw_if_index[VLIB_RX] = sw_if_index0; + sw_if_index0 = (mt0) ? mt0->sw_if_index : sw_if_index0; + + pkts_decapsulated ++; + stats_n_packets += 1; + stats_n_bytes += len0; + + /* Batch stats increment on the same gtpu tunnel so counter + is not incremented per packet */ + if (PREDICT_FALSE (sw_if_index0 != stats_sw_if_index)) + { + stats_n_packets -= 1; + stats_n_bytes -= len0; + if (stats_n_packets) + vlib_increment_combined_counter + (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX, + thread_index, stats_sw_if_index, + stats_n_packets, stats_n_bytes); + stats_n_packets = 1; + stats_n_bytes = len0; + stats_sw_if_index = sw_if_index0; + } + + trace00: + b0->error = error0 ? node->errors[error0] : 0; + + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + gtpu_rx_trace_t *tr + = vlib_add_trace (vm, node, b0, sizeof (*tr)); + tr->next_index = next0; + tr->error = error0; + tr->tunnel_index = tunnel_index0; + tr->teid = clib_net_to_host_u32(gtpu0->teid); + } + 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); + } + /* Do we still need this now that tunnel tx stats is kept? */ + vlib_node_increment_counter (vm, is_ip4? + gtpu4_input_node.index:gtpu6_input_node.index, + GTPU_ERROR_DECAPSULATED, + pkts_decapsulated); + + /* Increment any remaining batch stats */ + if (stats_n_packets) + { + vlib_increment_combined_counter + (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX, + thread_index, stats_sw_if_index, stats_n_packets, stats_n_bytes); + node->runtime_data[0] = stats_sw_if_index; + } + + return from_frame->n_vectors; +} + +static uword +gtpu4_input (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + return gtpu_input(vm, node, from_frame, /* is_ip4 */ 1); +} + +static uword +gtpu6_input (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + return gtpu_input(vm, node, from_frame, /* is_ip4 */ 0); +} + +static char * gtpu_error_strings[] = { +#define gtpu_error(n,s) s, +#include +#undef gtpu_error +#undef _ +}; + +VLIB_REGISTER_NODE (gtpu4_input_node) = { + .function = gtpu4_input, + .name = "gtpu4-input", + /* Takes a vector of packets. */ + .vector_size = sizeof (u32), + + .n_errors = GTPU_N_ERROR, + .error_strings = gtpu_error_strings, + + .n_next_nodes = GTPU_INPUT_N_NEXT, + .next_nodes = { +#define _(s,n) [GTPU_INPUT_NEXT_##s] = n, + foreach_gtpu_input_next +#undef _ + }, + +//temp .format_buffer = format_gtpu_header, + .format_trace = format_gtpu_rx_trace, + // $$$$ .unformat_buffer = unformat_gtpu_header, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (gtpu4_input_node, gtpu4_input) + +VLIB_REGISTER_NODE (gtpu6_input_node) = { + .function = gtpu6_input, + .name = "gtpu6-input", + /* Takes a vector of packets. */ + .vector_size = sizeof (u32), + + .n_errors = GTPU_N_ERROR, + .error_strings = gtpu_error_strings, + + .n_next_nodes = GTPU_INPUT_N_NEXT, + .next_nodes = { +#define _(s,n) [GTPU_INPUT_NEXT_##s] = n, + foreach_gtpu_input_next +#undef _ + }, + +//temp .format_buffer = format_gtpu_header, + .format_trace = format_gtpu_rx_trace, + // $$$$ .unformat_buffer = unformat_gtpu_header, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (gtpu6_input_node, gtpu6_input) + + +typedef enum { + IP_GTPU_BYPASS_NEXT_DROP, + IP_GTPU_BYPASS_NEXT_GTPU, + IP_GTPU_BYPASS_N_NEXT, +} ip_vxan_bypass_next_t; + +always_inline uword +ip_gtpu_bypass_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, + u32 is_ip4) +{ + gtpu_main_t * gtm = >pu_main; + u32 * from, * to_next, n_left_from, n_left_to_next, next_index; + vlib_node_runtime_t * error_node = vlib_node_get_runtime (vm, ip4_input_node.index); + ip4_address_t addr4; /* last IPv4 address matching a local VTEP address */ + ip6_address_t addr6; /* last IPv6 address matching a local VTEP address */ + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next_index = node->cached_next_index; + + if (node->flags & VLIB_NODE_FLAG_TRACE) + ip4_forward_next_trace (vm, node, frame, VLIB_TX); + + if (is_ip4) addr4.data_u32 = ~0; + else ip6_address_set_zero (&addr6); + + while (n_left_from > 0) + { + vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next); + + while (n_left_from >= 4 && n_left_to_next >= 2) + { + vlib_buffer_t * b0, * b1; + ip4_header_t * ip40, * ip41; + ip6_header_t * ip60, * ip61; + udp_header_t * udp0, * udp1; + u32 bi0, ip_len0, udp_len0, flags0, next0; + u32 bi1, ip_len1, udp_len1, flags1, next1; + i32 len_diff0, len_diff1; + u8 error0, good_udp0, proto0; + u8 error1, good_udp1, proto1; + + /* Prefetch next iteration. */ + { + vlib_buffer_t * p2, * p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header (p2, LOAD); + vlib_prefetch_buffer_header (p3, LOAD); + + CLIB_PREFETCH (p2->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); + CLIB_PREFETCH (p3->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); + } + + bi0 = to_next[0] = from[0]; + bi1 = to_next[1] = from[1]; + from += 2; + n_left_from -= 2; + to_next += 2; + n_left_to_next -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + if (is_ip4) + { + ip40 = vlib_buffer_get_current (b0); + ip41 = vlib_buffer_get_current (b1); + } + else + { + ip60 = vlib_buffer_get_current (b0); + ip61 = vlib_buffer_get_current (b1); + } + + /* Setup packet for next IP feature */ + vnet_feature_next(vnet_buffer(b0)->sw_if_index[VLIB_RX], &next0, b0); + vnet_feature_next(vnet_buffer(b1)->sw_if_index[VLIB_RX], &next1, b1); + + if (is_ip4) + { + /* Treat IP frag packets as "experimental" protocol for now + until support of IP frag reassembly is implemented */ + proto0 = ip4_is_fragment(ip40) ? 0xfe : ip40->protocol; + proto1 = ip4_is_fragment(ip41) ? 0xfe : ip41->protocol; + } + else + { + proto0 = ip60->protocol; + proto1 = ip61->protocol; + } + + /* Process packet 0 */ + if (proto0 != IP_PROTOCOL_UDP) + goto exit0; /* not UDP packet */ + + if (is_ip4) + udp0 = ip4_next_header (ip40); + else + udp0 = ip6_next_header (ip60); + + if (udp0->dst_port != clib_host_to_net_u16 (UDP_DST_PORT_GTPU)) + goto exit0; /* not GTPU packet */ + + /* Validate DIP against VTEPs*/ + if (is_ip4) + { + if (addr4.as_u32 != ip40->dst_address.as_u32) + { + if (!hash_get (gtm->vtep4, ip40->dst_address.as_u32)) + goto exit0; /* no local VTEP for GTPU packet */ + addr4 = ip40->dst_address; + } + } + else + { + if (!ip6_address_is_equal (&addr6, &ip60->dst_address)) + { + if (!hash_get_mem (gtm->vtep6, &ip60->dst_address)) + goto exit0; /* no local VTEP for GTPU packet */ + addr6 = ip60->dst_address; + } + } + + flags0 = b0->flags; + good_udp0 = (flags0 & IP_BUFFER_L4_CHECKSUM_CORRECT) != 0; + + /* Don't verify UDP checksum for packets with explicit zero checksum. */ + good_udp0 |= udp0->checksum == 0; + + /* Verify UDP length */ + if (is_ip4) + ip_len0 = clib_net_to_host_u16 (ip40->length); + else + ip_len0 = clib_net_to_host_u16 (ip60->payload_length); + udp_len0 = clib_net_to_host_u16 (udp0->length); + len_diff0 = ip_len0 - udp_len0; + + /* Verify UDP checksum */ + if (PREDICT_FALSE (!good_udp0)) + { + if ((flags0 & IP_BUFFER_L4_CHECKSUM_COMPUTED) == 0) + { + if (is_ip4) + flags0 = ip4_tcp_udp_validate_checksum (vm, b0); + else + flags0 = ip6_tcp_udp_icmp_validate_checksum (vm, b0); + good_udp0 = + (flags0 & IP_BUFFER_L4_CHECKSUM_CORRECT) != 0; + } + } + + if (is_ip4) + { + error0 = good_udp0 ? 0 : IP4_ERROR_UDP_CHECKSUM; + error0 = (len_diff0 >= 0) ? error0 : IP4_ERROR_UDP_LENGTH; + } + else + { + error0 = good_udp0 ? 0 : IP6_ERROR_UDP_CHECKSUM; + error0 = (len_diff0 >= 0) ? error0 : IP6_ERROR_UDP_LENGTH; + } + + next0 = error0 ? + IP_GTPU_BYPASS_NEXT_DROP : IP_GTPU_BYPASS_NEXT_GTPU; + b0->error = error0 ? error_node->errors[error0] : 0; + + /* gtpu-input node expect current at GTPU header */ + if (is_ip4) + vlib_buffer_advance (b0, sizeof(ip4_header_t)+sizeof(udp_header_t)); + else + vlib_buffer_advance (b0, sizeof(ip6_header_t)+sizeof(udp_header_t)); + + exit0: + /* Process packet 1 */ + if (proto1 != IP_PROTOCOL_UDP) + goto exit1; /* not UDP packet */ + + if (is_ip4) + udp1 = ip4_next_header (ip41); + else + udp1 = ip6_next_header (ip61); + + if (udp1->dst_port != clib_host_to_net_u16 (UDP_DST_PORT_GTPU)) + goto exit1; /* not GTPU packet */ + + /* Validate DIP against VTEPs*/ + if (is_ip4) + { + if (addr4.as_u32 != ip41->dst_address.as_u32) + { + if (!hash_get (gtm->vtep4, ip41->dst_address.as_u32)) + goto exit1; /* no local VTEP for GTPU packet */ + addr4 = ip41->dst_address; + } + } + else + { + if (!ip6_address_is_equal (&addr6, &ip61->dst_address)) + { + if (!hash_get_mem (gtm->vtep6, &ip61->dst_address)) + goto exit1; /* no local VTEP for GTPU packet */ + addr6 = ip61->dst_address; + } + } + + flags1 = b1->flags; + good_udp1 = (flags1 & IP_BUFFER_L4_CHECKSUM_CORRECT) != 0; + + /* Don't verify UDP checksum for packets with explicit zero checksum. */ + good_udp1 |= udp1->checksum == 0; + + /* Verify UDP length */ + if (is_ip4) + ip_len1 = clib_net_to_host_u16 (ip41->length); + else + ip_len1 = clib_net_to_host_u16 (ip61->payload_length); + udp_len1 = clib_net_to_host_u16 (udp1->length); + len_diff1 = ip_len1 - udp_len1; + + /* Verify UDP checksum */ + if (PREDICT_FALSE (!good_udp1)) + { + if ((flags1 & IP_BUFFER_L4_CHECKSUM_COMPUTED) == 0) + { + if (is_ip4) + flags1 = ip4_tcp_udp_validate_checksum (vm, b1); + else + flags1 = ip6_tcp_udp_icmp_validate_checksum (vm, b1); + good_udp1 = + (flags1 & IP_BUFFER_L4_CHECKSUM_CORRECT) != 0; + } + } + + if (is_ip4) + { + error1 = good_udp1 ? 0 : IP4_ERROR_UDP_CHECKSUM; + error1 = (len_diff1 >= 0) ? error1 : IP4_ERROR_UDP_LENGTH; + } + else + { + error1 = good_udp1 ? 0 : IP6_ERROR_UDP_CHECKSUM; + error1 = (len_diff1 >= 0) ? error1 : IP6_ERROR_UDP_LENGTH; + } + + next1 = error1 ? + IP_GTPU_BYPASS_NEXT_DROP : IP_GTPU_BYPASS_NEXT_GTPU; + b1->error = error1 ? error_node->errors[error1] : 0; + + /* gtpu-input node expect current at GTPU header */ + if (is_ip4) + vlib_buffer_advance (b1, sizeof(ip4_header_t)+sizeof(udp_header_t)); + else + vlib_buffer_advance (b1, sizeof(ip6_header_t)+sizeof(udp_header_t)); + + exit1: + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, next0, next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + vlib_buffer_t * b0; + ip4_header_t * ip40; + ip6_header_t * ip60; + udp_header_t * udp0; + u32 bi0, ip_len0, udp_len0, flags0, next0; + i32 len_diff0; + u8 error0, good_udp0, proto0; + + bi0 = to_next[0] = from[0]; + from += 1; + n_left_from -= 1; + to_next += 1; + n_left_to_next -= 1; + + b0 = vlib_get_buffer (vm, bi0); + if (is_ip4) + ip40 = vlib_buffer_get_current (b0); + else + ip60 = vlib_buffer_get_current (b0); + + /* Setup packet for next IP feature */ + vnet_feature_next(vnet_buffer(b0)->sw_if_index[VLIB_RX], &next0, b0); + + if (is_ip4) + /* Treat IP4 frag packets as "experimental" protocol for now + until support of IP frag reassembly is implemented */ + proto0 = ip4_is_fragment(ip40) ? 0xfe : ip40->protocol; + else + proto0 = ip60->protocol; + + if (proto0 != IP_PROTOCOL_UDP) + goto exit; /* not UDP packet */ + + if (is_ip4) + udp0 = ip4_next_header (ip40); + else + udp0 = ip6_next_header (ip60); + + if (udp0->dst_port != clib_host_to_net_u16 (UDP_DST_PORT_GTPU)) + goto exit; /* not GTPU packet */ + + /* Validate DIP against VTEPs*/ + if (is_ip4) + { + if (addr4.as_u32 != ip40->dst_address.as_u32) + { + if (!hash_get (gtm->vtep4, ip40->dst_address.as_u32)) + goto exit; /* no local VTEP for GTPU packet */ + addr4 = ip40->dst_address; + } + } + else + { + if (!ip6_address_is_equal (&addr6, &ip60->dst_address)) + { + if (!hash_get_mem (gtm->vtep6, &ip60->dst_address)) + goto exit; /* no local VTEP for GTPU packet */ + addr6 = ip60->dst_address; + } + } + + flags0 = b0->flags; + good_udp0 = (flags0 & IP_BUFFER_L4_CHECKSUM_CORRECT) != 0; + + /* Don't verify UDP checksum for packets with explicit zero checksum. */ + good_udp0 |= udp0->checksum == 0; + + /* Verify UDP length */ + if (is_ip4) + ip_len0 = clib_net_to_host_u16 (ip40->length); + else + ip_len0 = clib_net_to_host_u16 (ip60->payload_length); + udp_len0 = clib_net_to_host_u16 (udp0->length); + len_diff0 = ip_len0 - udp_len0; + + /* Verify UDP checksum */ + if (PREDICT_FALSE (!good_udp0)) + { + if ((flags0 & IP_BUFFER_L4_CHECKSUM_COMPUTED) == 0) + { + if (is_ip4) + flags0 = ip4_tcp_udp_validate_checksum (vm, b0); + else + flags0 = ip6_tcp_udp_icmp_validate_checksum (vm, b0); + good_udp0 = + (flags0 & IP_BUFFER_L4_CHECKSUM_CORRECT) != 0; + } + } + + if (is_ip4) + { + error0 = good_udp0 ? 0 : IP4_ERROR_UDP_CHECKSUM; + error0 = (len_diff0 >= 0) ? error0 : IP4_ERROR_UDP_LENGTH; + } + else + { + error0 = good_udp0 ? 0 : IP6_ERROR_UDP_CHECKSUM; + error0 = (len_diff0 >= 0) ? error0 : IP6_ERROR_UDP_LENGTH; + } + + next0 = error0 ? + IP_GTPU_BYPASS_NEXT_DROP : IP_GTPU_BYPASS_NEXT_GTPU; + b0->error = error0 ? error_node->errors[error0] : 0; + + /* gtpu-input node expect current at GTPU header */ + if (is_ip4) + vlib_buffer_advance (b0, sizeof(ip4_header_t)+sizeof(udp_header_t)); + else + vlib_buffer_advance (b0, sizeof(ip6_header_t)+sizeof(udp_header_t)); + + exit: + 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); + } + + return frame->n_vectors; +} + +static uword +ip4_gtpu_bypass (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return ip_gtpu_bypass_inline (vm, node, frame, /* is_ip4 */ 1); +} + +VLIB_REGISTER_NODE (ip4_gtpu_bypass_node) = { + .function = ip4_gtpu_bypass, + .name = "ip4-gtpu-bypass", + .vector_size = sizeof (u32), + + .n_next_nodes = IP_GTPU_BYPASS_N_NEXT, + .next_nodes = { + [IP_GTPU_BYPASS_NEXT_DROP] = "error-drop", + [IP_GTPU_BYPASS_NEXT_GTPU] = "gtpu4-input", + }, + + .format_buffer = format_ip4_header, + .format_trace = format_ip4_forward_next_trace, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (ip4_gtpu_bypass_node,ip4_gtpu_bypass) + +/* Dummy init function to get us linked in. */ +clib_error_t * ip4_gtpu_bypass_init (vlib_main_t * vm) +{ return 0; } + +VLIB_INIT_FUNCTION (ip4_gtpu_bypass_init); + +static uword +ip6_gtpu_bypass (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return ip_gtpu_bypass_inline (vm, node, frame, /* is_ip4 */ 0); +} + +VLIB_REGISTER_NODE (ip6_gtpu_bypass_node) = { + .function = ip6_gtpu_bypass, + .name = "ip6-gtpu-bypass", + .vector_size = sizeof (u32), + + .n_next_nodes = IP_GTPU_BYPASS_N_NEXT, + .next_nodes = { + [IP_GTPU_BYPASS_NEXT_DROP] = "error-drop", + [IP_GTPU_BYPASS_NEXT_GTPU] = "gtpu6-input", + }, + + .format_buffer = format_ip6_header, + .format_trace = format_ip6_forward_next_trace, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (ip6_gtpu_bypass_node,ip6_gtpu_bypass) + +/* Dummy init function to get us linked in. */ +clib_error_t * ip6_gtpu_bypass_init (vlib_main_t * vm) +{ return 0; } + +VLIB_INIT_FUNCTION (ip6_gtpu_bypass_init); diff --git a/src/plugins/gtpu/gtpu_encap.c b/src/plugins/gtpu/gtpu_encap.c new file mode 100644 index 00000000..a2cfc646 --- /dev/null +++ b/src/plugins/gtpu/gtpu_encap.c @@ -0,0 +1,569 @@ +/* + * 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. + */ +#include +#include +#include +#include +#include +#include + +/* Statistics (not all errors) */ +#define foreach_gtpu_encap_error \ +_(ENCAPSULATED, "good packets encapsulated") + +static char * gtpu_encap_error_strings[] = { +#define _(sym,string) string, + foreach_gtpu_encap_error +#undef _ +}; + +typedef enum { +#define _(sym,str) GTPU_ENCAP_ERROR_##sym, + foreach_gtpu_encap_error +#undef _ + GTPU_ENCAP_N_ERROR, +} gtpu_encap_error_t; + +typedef enum { + GTPU_ENCAP_NEXT_DROP, + GTPU_ENCAP_N_NEXT, +} gtpu_encap_next_t; + +typedef struct { + u32 tunnel_index; + u32 teid; +} gtpu_encap_trace_t; + +u8 * format_gtpu_encap_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 *); + gtpu_encap_trace_t * t + = va_arg (*args, gtpu_encap_trace_t *); + + s = format (s, "GTPU encap to gtpu_tunnel%d teid %d", + t->tunnel_index, t->teid); + return s; +} + + +#define foreach_fixed_header4_offset \ + _(0) _(1) _(2) _(3) + +#define foreach_fixed_header6_offset \ + _(0) _(1) _(2) _(3) _(4) _(5) _(6) + +always_inline uword +gtpu_encap_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame, + u32 is_ip4) +{ + u32 n_left_from, next_index, * from, * to_next; + gtpu_main_t * gtm = >pu_main; + vnet_main_t * vnm = gtm->vnet_main; + vnet_interface_main_t * im = &vnm->interface_main; + u32 pkts_encapsulated = 0; + u16 old_l0 = 0, old_l1 = 0; + u32 thread_index = vlib_get_thread_index(); + u32 stats_sw_if_index, stats_n_packets, stats_n_bytes; + u32 sw_if_index0 = 0, sw_if_index1 = 0; + u32 next0 = 0, next1 = 0; + vnet_hw_interface_t * hi0, * hi1; + gtpu_tunnel_t * t0 = NULL, * t1 = NULL; + + from = vlib_frame_vector_args (from_frame); + n_left_from = from_frame->n_vectors; + + next_index = node->cached_next_index; + stats_sw_if_index = node->runtime_data[0]; + stats_n_packets = stats_n_bytes = 0; + + 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 >= 4 && n_left_to_next >= 2) + { + u32 bi0, bi1; + vlib_buffer_t * b0, * b1; + u32 flow_hash0, flow_hash1; + u32 len0, len1; + ip4_header_t * ip4_0, * ip4_1; + ip6_header_t * ip6_0, * ip6_1; + udp_header_t * udp0, * udp1; + gtpu_header_t * gtpu0, * gtpu1; + u64 * copy_src0, * copy_dst0; + u64 * copy_src1, * copy_dst1; + u32 * copy_src_last0, * copy_dst_last0; + u32 * copy_src_last1, * copy_dst_last1; + u16 new_l0, new_l1; + ip_csum_t sum0, sum1; + + /* Prefetch next iteration. */ + { + vlib_buffer_t * p2, * p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header (p2, LOAD); + vlib_prefetch_buffer_header (p3, LOAD); + + CLIB_PREFETCH (p2->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); + CLIB_PREFETCH (p3->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); + } + + bi0 = from[0]; + bi1 = from[1]; + to_next[0] = bi0; + to_next[1] = bi1; + from += 2; + to_next += 2; + n_left_to_next -= 2; + n_left_from -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + flow_hash0 = vnet_l2_compute_flow_hash (b0); + flow_hash1 = vnet_l2_compute_flow_hash (b1); + + /* Get next node index and adj index from tunnel next_dpo */ + if (sw_if_index0 != vnet_buffer(b0)->sw_if_index[VLIB_TX]) + { + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_TX]; + hi0 = vnet_get_sup_hw_interface (vnm, sw_if_index0); + t0 = >m->tunnels[hi0->dev_instance]; + /* Note: change to always set next0 if it may be set to drop */ + next0 = t0->next_dpo.dpoi_next_node; + } + vnet_buffer(b0)->ip.adj_index[VLIB_TX] = t0->next_dpo.dpoi_index; + + /* Get next node index and adj index from tunnel next_dpo */ + if (sw_if_index1 != vnet_buffer(b1)->sw_if_index[VLIB_TX]) + { + sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_TX]; + hi1 = vnet_get_sup_hw_interface (vnm, sw_if_index1); + t1 = >m->tunnels[hi1->dev_instance]; + /* Note: change to always set next1 if it may be set to drop */ + next1 = t1->next_dpo.dpoi_next_node; + } + vnet_buffer(b1)->ip.adj_index[VLIB_TX] = t1->next_dpo.dpoi_index; + + /* Apply the rewrite string. $$$$ vnet_rewrite? */ + vlib_buffer_advance (b0, -(word)_vec_len(t0->rewrite)); + vlib_buffer_advance (b1, -(word)_vec_len(t1->rewrite)); + + if (is_ip4) + { + ip4_0 = vlib_buffer_get_current(b0); + ip4_1 = vlib_buffer_get_current(b1); + + /* Copy the fixed header */ + copy_dst0 = (u64 *) ip4_0; + copy_src0 = (u64 *) t0->rewrite; + copy_dst1 = (u64 *) ip4_1; + copy_src1 = (u64 *) t1->rewrite; + /* Copy first 32 octets 8-bytes at a time */ +#define _(offs) copy_dst0[offs] = copy_src0[offs]; + foreach_fixed_header4_offset; +#undef _ +#define _(offs) copy_dst1[offs] = copy_src1[offs]; + foreach_fixed_header4_offset; +#undef _ + /* Last 4 octets. Hopefully gcc will be our friend */ + copy_dst_last0 = (u32 *)(©_dst0[4]); + copy_src_last0 = (u32 *)(©_src0[4]); + copy_dst_last0[0] = copy_src_last0[0]; + copy_dst_last1 = (u32 *)(©_dst1[4]); + copy_src_last1 = (u32 *)(©_src1[4]); + copy_dst_last1[0] = copy_src_last1[0]; + + /* Fix the IP4 checksum and length */ + sum0 = ip4_0->checksum; + new_l0 = /* old_l0 always 0, see the rewrite setup */ + clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)); + sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t, + length /* changed member */); + ip4_0->checksum = ip_csum_fold (sum0); + ip4_0->length = new_l0; + sum1 = ip4_1->checksum; + new_l1 = /* old_l1 always 0, see the rewrite setup */ + clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1)); + sum1 = ip_csum_update (sum1, old_l1, new_l1, ip4_header_t, + length /* changed member */); + ip4_1->checksum = ip_csum_fold (sum1); + ip4_1->length = new_l1; + + /* Fix UDP length and set source port */ + udp0 = (udp_header_t *)(ip4_0+1); + new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain(vm, b0) + - sizeof (*ip4_0)); + udp0->length = new_l0; + udp0->src_port = flow_hash0; + udp1 = (udp_header_t *)(ip4_1+1); + new_l1 = clib_host_to_net_u16 (vlib_buffer_length_in_chain(vm, b1) + - sizeof (*ip4_1)); + udp1->length = new_l1; + udp1->src_port = flow_hash1; + + /* Fix GTPU length */ + gtpu0 = (gtpu_header_t *)(udp0+1); + new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain(vm, b0) + - sizeof (*ip4_0) - sizeof(*udp0)); + gtpu0->length = new_l0; + gtpu1 = (gtpu_header_t *)(udp1+1); + new_l1 = clib_host_to_net_u16 (vlib_buffer_length_in_chain(vm, b1) + - sizeof (*ip4_1) - sizeof(*udp1)); + gtpu1->length = new_l1; + } + else /* ipv6 */ + { + int bogus = 0; + + ip6_0 = vlib_buffer_get_current(b0); + ip6_1 = vlib_buffer_get_current(b1); + + /* Copy the fixed header */ + copy_dst0 = (u64 *) ip6_0; + copy_src0 = (u64 *) t0->rewrite; + copy_dst1 = (u64 *) ip6_1; + copy_src1 = (u64 *) t1->rewrite; + /* Copy first 56 (ip6) octets 8-bytes at a time */ +#define _(offs) copy_dst0[offs] = copy_src0[offs]; + foreach_fixed_header6_offset; +#undef _ +#define _(offs) copy_dst1[offs] = copy_src1[offs]; + foreach_fixed_header6_offset; +#undef _ + /* Fix IP6 payload length */ + new_l0 = + clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0) + - sizeof(*ip6_0)); + ip6_0->payload_length = new_l0; + new_l1 = + clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b1) + - sizeof(*ip6_1)); + ip6_1->payload_length = new_l1; + + /* Fix UDP length and set source port */ + udp0 = (udp_header_t *)(ip6_0+1); + udp0->length = new_l0; + udp0->src_port = flow_hash0; + udp1 = (udp_header_t *)(ip6_1+1); + udp1->length = new_l1; + udp1->src_port = flow_hash1; + + /* IPv6 UDP checksum is mandatory */ + udp0->checksum = ip6_tcp_udp_icmp_compute_checksum(vm, b0, + ip6_0, &bogus); + if (udp0->checksum == 0) + udp0->checksum = 0xffff; + udp1->checksum = ip6_tcp_udp_icmp_compute_checksum(vm, b1, + ip6_1, &bogus); + if (udp1->checksum == 0) + udp1->checksum = 0xffff; + + /* Fix GTPU length */ + gtpu0 = (gtpu_header_t *)(udp0+1); + new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain(vm, b0) + - sizeof (*ip4_0) - sizeof(*udp0)); + gtpu0->length = new_l0; + gtpu1 = (gtpu_header_t *)(udp1+1); + new_l1 = clib_host_to_net_u16 (vlib_buffer_length_in_chain(vm, b1) + - sizeof (*ip4_1) - sizeof(*udp1)); + gtpu1->length = new_l1; + } + + pkts_encapsulated += 2; + len0 = vlib_buffer_length_in_chain (vm, b0); + len1 = vlib_buffer_length_in_chain (vm, b1); + stats_n_packets += 2; + stats_n_bytes += len0 + len1; + + /* Batch stats increment on the same gtpu tunnel so counter is not + incremented per packet. Note stats are still incremented for deleted + and admin-down tunnel where packets are dropped. It is not worthwhile + to check for this rare case and affect normal path performance. */ + if (PREDICT_FALSE ((sw_if_index0 != stats_sw_if_index) || + (sw_if_index1 != stats_sw_if_index))) + { + stats_n_packets -= 2; + stats_n_bytes -= len0 + len1; + if (sw_if_index0 == sw_if_index1) + { + if (stats_n_packets) + vlib_increment_combined_counter + (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX, + thread_index, stats_sw_if_index, + stats_n_packets, stats_n_bytes); + stats_sw_if_index = sw_if_index0; + stats_n_packets = 2; + stats_n_bytes = len0 + len1; + } + else + { + vlib_increment_combined_counter + (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX, + thread_index, sw_if_index0, 1, len0); + vlib_increment_combined_counter + (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX, + thread_index, sw_if_index1, 1, len1); + } + } + + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + gtpu_encap_trace_t *tr = + vlib_add_trace (vm, node, b0, sizeof (*tr)); + tr->tunnel_index = t0 - gtm->tunnels; + tr->teid = t0->teid; + } + + if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED)) + { + gtpu_encap_trace_t *tr = + vlib_add_trace (vm, node, b1, sizeof (*tr)); + tr->tunnel_index = t1 - gtm->tunnels; + tr->teid = t1->teid; + } + + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, next0, next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t * b0; + u32 flow_hash0; + u32 len0; + ip4_header_t * ip4_0; + ip6_header_t * ip6_0; + udp_header_t * udp0; + gtpu_header_t * gtpu0; + u64 * copy_src0, * copy_dst0; + u32 * copy_src_last0, * copy_dst_last0; + u16 new_l0; + ip_csum_t sum0; + + 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); + + flow_hash0 = vnet_l2_compute_flow_hash(b0); + + /* Get next node index and adj index from tunnel next_dpo */ + if (sw_if_index0 != vnet_buffer(b0)->sw_if_index[VLIB_TX]) + { + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_TX]; + hi0 = vnet_get_sup_hw_interface (vnm, sw_if_index0); + t0 = >m->tunnels[hi0->dev_instance]; + /* Note: change to always set next0 if it may be set to drop */ + next0 = t0->next_dpo.dpoi_next_node; + } + vnet_buffer(b0)->ip.adj_index[VLIB_TX] = t0->next_dpo.dpoi_index; + + /* Apply the rewrite string. $$$$ vnet_rewrite? */ + vlib_buffer_advance (b0, -(word)_vec_len(t0->rewrite)); + + if (is_ip4) + { + ip4_0 = vlib_buffer_get_current(b0); + + /* Copy the fixed header */ + copy_dst0 = (u64 *) ip4_0; + copy_src0 = (u64 *) t0->rewrite; + /* Copy first 32 octets 8-bytes at a time */ +#define _(offs) copy_dst0[offs] = copy_src0[offs]; + foreach_fixed_header4_offset; +#undef _ + /* Last 4 octets. Hopefully gcc will be our friend */ + copy_dst_last0 = (u32 *)(©_dst0[4]); + copy_src_last0 = (u32 *)(©_src0[4]); + copy_dst_last0[0] = copy_src_last0[0]; + + /* Fix the IP4 checksum and length */ + sum0 = ip4_0->checksum; + new_l0 = /* old_l0 always 0, see the rewrite setup */ + clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0)); + sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t, + length /* changed member */); + ip4_0->checksum = ip_csum_fold (sum0); + ip4_0->length = new_l0; + + /* Fix UDP length and set source port */ + udp0 = (udp_header_t *)(ip4_0+1); + new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain(vm, b0) + - sizeof (*ip4_0)); + udp0->length = new_l0; + udp0->src_port = flow_hash0; + + /* Fix GTPU length */ + gtpu0 = (gtpu_header_t *)(udp0+1); + new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain(vm, b0) + - sizeof (*ip4_0) - sizeof(*udp0)); + gtpu0->length = new_l0; + } + + else /* ip6 path */ + { + int bogus = 0; + + ip6_0 = vlib_buffer_get_current(b0); + /* Copy the fixed header */ + copy_dst0 = (u64 *) ip6_0; + copy_src0 = (u64 *) t0->rewrite; + /* Copy first 56 (ip6) octets 8-bytes at a time */ +#define _(offs) copy_dst0[offs] = copy_src0[offs]; + foreach_fixed_header6_offset; +#undef _ + /* Fix IP6 payload length */ + new_l0 = + clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b0) + - sizeof(*ip6_0)); + ip6_0->payload_length = new_l0; + + /* Fix UDP length and set source port */ + udp0 = (udp_header_t *)(ip6_0+1); + udp0->length = new_l0; + udp0->src_port = flow_hash0; + + /* IPv6 UDP checksum is mandatory */ + udp0->checksum = ip6_tcp_udp_icmp_compute_checksum(vm, b0, + ip6_0, &bogus); + if (udp0->checksum == 0) + udp0->checksum = 0xffff; + + /* Fix GTPU length */ + gtpu0 = (gtpu_header_t *)(udp0+1); + new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain(vm, b0) + - sizeof (*ip4_0) - sizeof(*udp0)); + gtpu0->length = new_l0; + } + + pkts_encapsulated ++; + len0 = vlib_buffer_length_in_chain (vm, b0); + stats_n_packets += 1; + stats_n_bytes += len0; + + /* Batch stats increment on the same gtpu tunnel so counter is not + incremented per packet. Note stats are still incremented for deleted + and admin-down tunnel where packets are dropped. It is not worthwhile + to check for this rare case and affect normal path performance. */ + if (PREDICT_FALSE (sw_if_index0 != stats_sw_if_index)) + { + stats_n_packets -= 1; + stats_n_bytes -= len0; + if (stats_n_packets) + vlib_increment_combined_counter + (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX, + thread_index, stats_sw_if_index, + stats_n_packets, stats_n_bytes); + stats_n_packets = 1; + stats_n_bytes = len0; + stats_sw_if_index = sw_if_index0; + } + + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + gtpu_encap_trace_t *tr = + vlib_add_trace (vm, node, b0, sizeof (*tr)); + tr->tunnel_index = t0 - gtm->tunnels; + tr->teid = t0->teid; + } + 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); + } + + /* Do we still need this now that tunnel tx stats is kept? */ + vlib_node_increment_counter (vm, node->node_index, + GTPU_ENCAP_ERROR_ENCAPSULATED, + pkts_encapsulated); + + /* Increment any remaining batch stats */ + if (stats_n_packets) + { + vlib_increment_combined_counter + (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX, + thread_index, stats_sw_if_index, stats_n_packets, stats_n_bytes); + node->runtime_data[0] = stats_sw_if_index; + } + + return from_frame->n_vectors; +} + +static uword +gtpu4_encap (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + return gtpu_encap_inline (vm, node, from_frame, /* is_ip4 */ 1); +} + +static uword +gtpu6_encap (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + return gtpu_encap_inline (vm, node, from_frame, /* is_ip4 */ 0); +} + +VLIB_REGISTER_NODE (gtpu4_encap_node) = { + .function = gtpu4_encap, + .name = "gtpu4-encap", + .vector_size = sizeof (u32), + .format_trace = format_gtpu_encap_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(gtpu_encap_error_strings), + .error_strings = gtpu_encap_error_strings, + .n_next_nodes = GTPU_ENCAP_N_NEXT, + .next_nodes = { + [GTPU_ENCAP_NEXT_DROP] = "error-drop", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (gtpu4_encap_node, gtpu4_encap) + +VLIB_REGISTER_NODE (gtpu6_encap_node) = { + .function = gtpu6_encap, + .name = "gtpu6-encap", + .vector_size = sizeof (u32), + .format_trace = format_gtpu_encap_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(gtpu_encap_error_strings), + .error_strings = gtpu_encap_error_strings, + .n_next_nodes = GTPU_ENCAP_N_NEXT, + .next_nodes = { + [GTPU_ENCAP_NEXT_DROP] = "error-drop", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (gtpu6_encap_node, gtpu6_encap) + diff --git a/src/plugins/gtpu/gtpu_error.def b/src/plugins/gtpu/gtpu_error.def new file mode 100644 index 00000000..a55b2026 --- /dev/null +++ b/src/plugins/gtpu/gtpu_error.def @@ -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") diff --git a/src/plugins/gtpu/gtpu_msg_enum.h b/src/plugins/gtpu/gtpu_msg_enum.h new file mode 100644 index 00000000..6a2ea448 --- /dev/null +++ b/src/plugins/gtpu/gtpu_msg_enum.h @@ -0,0 +1,31 @@ +/* + * gtpu_msg_enum.h - vpp engine plug-in message enumeration + * + * Copyright (c) + * 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 + +#define vl_msg_id(n,h) n, +typedef enum +{ +#include + /* 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 */ diff --git a/src/plugins/gtpu/gtpu_test.c b/src/plugins/gtpu/gtpu_test.c new file mode 100644 index 00000000..91c5c616 --- /dev/null +++ b/src/plugins/gtpu/gtpu_test.c @@ -0,0 +1,498 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#define __plugin_msg_base gtpu_test_main.msg_id_base +#include + + +uword unformat_ip46_address (unformat_input_t * input, va_list * args) +{ + ip46_address_t *ip46 = va_arg (*args, ip46_address_t *); + ip46_type_t type = va_arg (*args, ip46_type_t); + if ((type != IP46_TYPE_IP6) && + unformat(input, "%U", unformat_ip4_address, &ip46->ip4)) { + ip46_address_mask_ip4(ip46); + return 1; + } else if ((type != IP46_TYPE_IP4) && + unformat(input, "%U", unformat_ip6_address, &ip46->ip6)) { + return 1; + } + return 0; +} +uword unformat_ip46_prefix (unformat_input_t * input, va_list * args) +{ + ip46_address_t *ip46 = va_arg (*args, ip46_address_t *); + u8 *len = va_arg (*args, u8 *); + ip46_type_t type = va_arg (*args, ip46_type_t); + + u32 l; + if ((type != IP46_TYPE_IP6) && unformat(input, "%U/%u", unformat_ip4_address, &ip46->ip4, &l)) { + if (l > 32) + return 0; + *len = l + 96; + ip46->pad[0] = ip46->pad[1] = ip46->pad[2] = 0; + } else if ((type != IP46_TYPE_IP4) && unformat(input, "%U/%u", unformat_ip6_address, &ip46->ip6, &l)) { + if (l > 128) + return 0; + *len = l; + } else { + return 0; + } + return 1; +} +///////////////////////// + +#define vl_msg_id(n,h) n, +typedef enum { +#include + /* 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 +#undef vl_typedefs + +/* declare message handlers for each api */ + +#define vl_endianfun /* define message structures */ +#include +#undef vl_endianfun + +/* instantiate all the print functions we know about */ +#define vl_print(handle, ...) +#define vl_printfun +#include +#undef vl_printfun + +/* Get the API version number. */ +#define vl_api_version(n,v) static u32 api_version=(v); +#include +#undef vl_api_version + +typedef struct { + /* API message ID base */ + u16 msg_id_base; + vat_main_t *vat_main; +} gtpu_test_main_t; + +gtpu_test_main_t gtpu_test_main; + +static void vl_api_gtpu_add_del_tunnel_reply_t_handler + (vl_api_gtpu_add_del_tunnel_reply_t * mp) +{ + vat_main_t *vam = &vat_main; + i32 retval = ntohl (mp->retval); + if (vam->async_mode) + { + vam->async_errors += (retval < 0); + } + else + { + vam->retval = retval; + vam->sw_if_index = ntohl (mp->sw_if_index); + vam->result_ready = 1; + } +} + + +#define foreach_standard_reply_retval_handler \ + _(sw_interface_set_gtpu_bypass_reply) + +#define _(n) \ + static void vl_api_##n##_t_handler \ + (vl_api_##n##_t * mp) \ + { \ + vat_main_t * vam = gtpu_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 \ + _(SW_INTERFACE_SET_GTPU_BYPASS_REPLY, sw_interface_set_gtpu_bypass_reply) \ + _(GTPU_ADD_DEL_TUNNEL_REPLY, gtpu_add_del_tunnel_reply) \ + _(GTPU_TUNNEL_DETAILS, gtpu_tunnel_details) + + +static uword +api_unformat_sw_if_index (unformat_input_t * input, va_list * args) +{ + vat_main_t *vam = va_arg (*args, vat_main_t *); + u32 *result = va_arg (*args, u32 *); + u8 *if_name; + uword *p; + + if (!unformat (input, "%s", &if_name)) + return 0; + + p = hash_get_mem (vam->sw_if_index_by_interface_name, if_name); + if (p == 0) + return 0; + *result = p[0]; + return 1; +} + +static int +api_sw_interface_set_gtpu_bypass (vat_main_t * vam) +{ + unformat_input_t *i = vam->input; + vl_api_sw_interface_set_gtpu_bypass_t *mp; + u32 sw_if_index = 0; + u8 sw_if_index_set = 0; + u8 is_enable = 1; + u8 is_ipv6 = 0; + int ret; + + /* Parse args required to build the message */ + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "%U", api_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, "enable")) + is_enable = 1; + else if (unformat (i, "disable")) + is_enable = 0; + else if (unformat (i, "ip4")) + is_ipv6 = 0; + else if (unformat (i, "ip6")) + is_ipv6 = 1; + else + break; + } + + if (sw_if_index_set == 0) + { + errmsg ("missing interface name or sw_if_index"); + return -99; + } + + /* Construct the API message */ + M (SW_INTERFACE_SET_GTPU_BYPASS, mp); + + mp->sw_if_index = ntohl (sw_if_index); + mp->enable = is_enable; + mp->is_ipv6 = is_ipv6; + + /* send it... */ + S (mp); + + /* Wait for a reply... */ + W (ret); + return ret; +} + +static uword unformat_gtpu_decap_next + (unformat_input_t * input, va_list * args) +{ + u32 *result = va_arg (*args, u32 *); + u32 tmp; + + if (unformat (input, "l2")) + *result = GTPU_INPUT_NEXT_L2_INPUT; + else if (unformat (input, "%d", &tmp)) + *result = tmp; + else + return 0; + return 1; +} + +static int +api_gtpu_add_del_tunnel (vat_main_t * vam) +{ + unformat_input_t *line_input = vam->input; + vl_api_gtpu_add_del_tunnel_t *mp; + ip46_address_t src, dst; + u8 is_add = 1; + u8 ipv4_set = 0, ipv6_set = 0; + u8 src_set = 0; + u8 dst_set = 0; + u8 grp_set = 0; + u32 mcast_sw_if_index = ~0; + u32 encap_vrf_id = 0; + u32 decap_next_index = ~0; + u32 teid = 0; + int ret; + + /* Can't "universally zero init" (={0}) due to GCC bug 53119 */ + memset (&src, 0, sizeof src); + memset (&dst, 0, sizeof dst); + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "del")) + is_add = 0; + else + if (unformat (line_input, "src %U", unformat_ip4_address, &src.ip4)) + { + ipv4_set = 1; + src_set = 1; + } + else + if (unformat (line_input, "dst %U", unformat_ip4_address, &dst.ip4)) + { + ipv4_set = 1; + dst_set = 1; + } + else + if (unformat (line_input, "src %U", unformat_ip6_address, &src.ip6)) + { + ipv6_set = 1; + src_set = 1; + } + else + if (unformat (line_input, "dst %U", unformat_ip6_address, &dst.ip6)) + { + ipv6_set = 1; + dst_set = 1; + } + else if (unformat (line_input, "group %U %U", + unformat_ip4_address, &dst.ip4, + api_unformat_sw_if_index, vam, &mcast_sw_if_index)) + { + grp_set = dst_set = 1; + ipv4_set = 1; + } + else if (unformat (line_input, "group %U", + unformat_ip4_address, &dst.ip4)) + { + grp_set = dst_set = 1; + ipv4_set = 1; + } + else if (unformat (line_input, "group %U %U", + unformat_ip6_address, &dst.ip6, + api_unformat_sw_if_index, vam, &mcast_sw_if_index)) + { + grp_set = dst_set = 1; + ipv6_set = 1; + } + else if (unformat (line_input, "group %U", + unformat_ip6_address, &dst.ip6)) + { + grp_set = dst_set = 1; + ipv6_set = 1; + } + else + if (unformat (line_input, "mcast_sw_if_index %u", &mcast_sw_if_index)) + ; + else if (unformat (line_input, "encap-vrf-id %d", &encap_vrf_id)) + ; + else if (unformat (line_input, "decap-next %U", + unformat_gtpu_decap_next, &decap_next_index)) + ; + else if (unformat (line_input, "teid %d", &teid)) + ; + else + { + errmsg ("parse error '%U'", format_unformat_error, line_input); + return -99; + } + } + + if (src_set == 0) + { + errmsg ("tunnel src address not specified"); + return -99; + } + if (dst_set == 0) + { + errmsg ("tunnel dst address not specified"); + return -99; + } + + if (grp_set && !ip46_address_is_multicast (&dst)) + { + errmsg ("tunnel group address not multicast"); + return -99; + } + if (grp_set && mcast_sw_if_index == ~0) + { + errmsg ("tunnel nonexistent multicast device"); + return -99; + } + if (grp_set == 0 && ip46_address_is_multicast (&dst)) + { + errmsg ("tunnel dst address must be unicast"); + return -99; + } + + + if (ipv4_set && ipv6_set) + { + errmsg ("both IPv4 and IPv6 addresses specified"); + return -99; + } + + M (GTPU_ADD_DEL_TUNNEL, mp); + + if (ipv6_set) + { + clib_memcpy (mp->src_address, &src.ip6, sizeof (src.ip6)); + clib_memcpy (mp->dst_address, &dst.ip6, sizeof (dst.ip6)); + } + else + { + clib_memcpy (mp->src_address, &src.ip4, sizeof (src.ip4)); + clib_memcpy (mp->dst_address, &dst.ip4, sizeof (dst.ip4)); + } + mp->encap_vrf_id = ntohl (encap_vrf_id); + mp->decap_next_index = ntohl (decap_next_index); + mp->mcast_sw_if_index = ntohl (mcast_sw_if_index); + mp->teid = ntohl (teid); + mp->is_add = is_add; + mp->is_ipv6 = ipv6_set; + + S (mp); + W (ret); + return ret; +} + +static void vl_api_gtpu_tunnel_details_t_handler + (vl_api_gtpu_tunnel_details_t * mp) +{ + vat_main_t *vam = &vat_main; + ip46_address_t src = to_ip46 (mp->is_ipv6, mp->dst_address); + ip46_address_t dst = to_ip46 (mp->is_ipv6, mp->src_address); + + print (vam->ofp, "%11d%24U%24U%14d%18d%13d%19d", + ntohl (mp->sw_if_index), + format_ip46_address, &src, IP46_TYPE_ANY, + format_ip46_address, &dst, IP46_TYPE_ANY, + ntohl (mp->encap_vrf_id), + ntohl (mp->decap_next_index), ntohl (mp->teid), + ntohl (mp->mcast_sw_if_index)); +} + +static int +api_gtpu_tunnel_dump (vat_main_t * vam) +{ + unformat_input_t *i = vam->input; + vl_api_gtpu_tunnel_dump_t *mp; + u32 sw_if_index; + u8 sw_if_index_set = 0; + int ret; + + /* Parse args required to build the message */ + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "sw_if_index %d", &sw_if_index)) + sw_if_index_set = 1; + else + break; + } + + if (sw_if_index_set == 0) + { + sw_if_index = ~0; + } + + if (!vam->json_output) + { + print (vam->ofp, "%11s%24s%24s%14s%18s%13s%19s", + "sw_if_index", "src_address", "dst_address", + "encap_vrf_id", "decap_next_index", "teid", "mcast_sw_if_index"); + } + + /* Get list of gtpu-tunnel interfaces */ + M (GTPU_TUNNEL_DUMP, mp); + + mp->sw_if_index = htonl (sw_if_index); + + S (mp); + + W (ret); + return ret; +} + +/* + * List of messages that the api test plugin sends, + * and that the data plane plugin processes + */ +#define foreach_vpe_api_msg \ +_(sw_interface_set_gtpu_bypass, \ + " | sw_if_index [ip4 | ip6] [enable | disable]") \ +_(gtpu_add_del_tunnel, \ + "src { dst | group \n" \ + "{ | mcast_sw_if_index } }\n" \ + "teid [encap-vrf-id ] [decap-next ] [del]") \ +_(gtpu_tunnel_dump, "[ | sw_if_index ]") \ + +static void +gtpu_vat_api_hookup (vat_main_t *vam) +{ + gtpu_test_main_t * gtm = >pu_test_main; + /* Hook up handlers for replies from the data plane plug-in */ +#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_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) +{ + gtpu_test_main_t * gtm = >pu_test_main; + + u8 * name; + + gtm->vat_main = vam; + + /* Ask the vpp engine for the first assigned message-id */ + name = format (0, "gtpu_%08x%c", api_version, 0); + gtm->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name); + + if (gtm->msg_id_base != (u16) ~0) + gtpu_vat_api_hookup (vam); + + vec_free(name); + + return 0; +} diff --git a/src/vnet/mfib/mfib_types.h b/src/vnet/mfib/mfib_types.h index fe53aa68..e278af7f 100644 --- a/src/vnet/mfib/mfib_types.h +++ b/src/vnet/mfib/mfib_types.h @@ -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", \ } /** diff --git a/src/vnet/udp/udp.h b/src/vnet/udp/udp.h index bd163e27..5b63f44d 100644 --- a/src/vnet/udp/udp.h +++ b/src/vnet/udp/udp.h @@ -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) \ diff --git a/src/vpp-api/java/Makefile.am b/src/vpp-api/java/Makefile.am index f18e0c24..4f1f7ed2 100644 --- a/src/vpp-api/java/Makefile.am +++ b/src/vpp-api/java/Makefile.am @@ -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 # diff --git a/src/vpp-api/java/jvpp-gtpu/jvpp_gtpu.c b/src/vpp-api/java/jvpp-gtpu/jvpp_gtpu.c new file mode 100644 index 00000000..12b3090e --- /dev/null +++ b/src/vpp-api/java/jvpp-gtpu/jvpp_gtpu.c @@ -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 + +#include +#define vl_typedefs /* define message structures */ +#include +#undef vl_typedefs + +#include +#include +#include + +#if VPPJNI_DEBUG == 1 + #define DEBUG_LOG(...) clib_warning(__VA_ARGS__) +#else + #define DEBUG_LOG(...) +#endif + +#include + +#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 = >pu_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 = >pu_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); +} diff --git a/src/vpp-api/java/jvpp-gtpu/jvpp_gtpu.h b/src/vpp-api/java/jvpp-gtpu/jvpp_gtpu.h new file mode 100644 index 00000000..447776ce --- /dev/null +++ b/src/vpp-api/java/jvpp-gtpu/jvpp_gtpu.h @@ -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 +#include +#include +#include +#include +#include + +/* 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__ */ diff --git a/test/test_gtpu.py b/test/test_gtpu.py new file mode 100644 index 00000000..9411fffc --- /dev/null +++ b/test/test_gtpu.py @@ -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) diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 83c4a83b..ff8e2cfd 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -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}) -- cgit 1.2.3-korg From 01384fe3d4c8f9d5c082cd602087a8eb71facd15 Mon Sep 17 00:00:00 2001 From: Ole Troan Date: Fri, 12 May 2017 11:55:35 +0200 Subject: API: Cleaning up message naming that does not follow the conventions is_address_reachable - Disabled so deleted cli_request - Renamed to cli vnet_summary_stats_reply - Renamed to vnet_get_summary_stats_reply bridge_domain_sw_if_details - Deleted, incorporated in main message l2_fib_table_entry - Renamed to l2_fib_table_details Change-Id: I93b7e8769a3ba7b4989b3c270270f575f386464f Signed-off-by: Ole Troan Signed-off-by: Marek Gradzki Signed-off-by: Ole Troan --- src/vat/api_format.c | 114 +++++++++++---------- src/vnet/l2/l2.api | 32 +++--- src/vnet/l2/l2_api.c | 61 +++++------ .../io/fd/vpp/jvpp/core/test/FutureApiTest.java | 4 +- src/vpp-api/java/jvpp/gen/jvppgen/util.py | 9 +- src/vpp/api/api.c | 85 +-------------- src/vpp/api/custom_dump.c | 8 +- src/vpp/api/summary_stats_client.c | 6 +- src/vpp/api/vpe.api | 23 +---- 9 files changed, 116 insertions(+), 226 deletions(-) (limited to 'src/vpp-api/java') diff --git a/src/vat/api_format.c b/src/vat/api_format.c index efb71ef6..22a91666 100644 --- a/src/vat/api_format.c +++ b/src/vat/api_format.c @@ -1323,6 +1323,9 @@ vl_api_ip6_nd_event_t_handler_json (vl_api_ip6_nd_event_t * mp) /* JSON output not supported */ } +#define vl_api_bridge_domain_details_t_endian vl_noop_handler +#define vl_api_bridge_domain_details_t_print vl_noop_handler + /* * Special-case: build the bridge domain table, maintain * the next bd id vbl. @@ -1332,6 +1335,7 @@ static void vl_api_bridge_domain_details_t_handler { vat_main_t *vam = &vat_main; u32 n_sw_ifs = ntohl (mp->n_sw_ifs); + int i; print (vam->ofp, "\n%-3s %-3s %-3s %-3s %-3s %-3s", " ID", "LRN", "FWD", "FLD", "BVI", "#IF"); @@ -1341,7 +1345,37 @@ static void vl_api_bridge_domain_details_t_handler mp->flood, ntohl (mp->bvi_sw_if_index), n_sw_ifs); if (n_sw_ifs) - print (vam->ofp, "\n\n%s %s %s", "sw_if_index", "SHG", "Interface Name"); + { + vl_api_bridge_domain_sw_if_t *sw_ifs; + print (vam->ofp, "\n\n%s %s %s", "sw_if_index", "SHG", + "Interface Name"); + + sw_ifs = mp->sw_if_details; + for (i = 0; i < n_sw_ifs; i++) + { + u8 *sw_if_name = 0; + u32 sw_if_index; + hash_pair_t *p; + + sw_if_index = ntohl (sw_ifs->sw_if_index); + + /* *INDENT-OFF* */ + hash_foreach_pair (p, vam->sw_if_index_by_interface_name, + ({ + if ((u32) p->value[0] == sw_if_index) + { + sw_if_name = (u8 *)(p->key); + break; + } + })); + /* *INDENT-ON* */ + print (vam->ofp, "%7d %3d %s", sw_if_index, + sw_ifs->shg, sw_if_name ? (char *) sw_if_name : + "sw_if_index not found!"); + + sw_ifs++; + } + } } static void vl_api_bridge_domain_details_t_handler_json @@ -1349,6 +1383,7 @@ static void vl_api_bridge_domain_details_t_handler_json { vat_main_t *vam = &vat_main; vat_json_node_t *node, *array = NULL; + u32 n_sw_ifs = ntohl (mp->n_sw_ifs); if (VAT_JSON_ARRAY != vam->json_tree.type) { @@ -1364,58 +1399,28 @@ static void vl_api_bridge_domain_details_t_handler_json vat_json_object_add_uint (node, "learn", mp->learn); vat_json_object_add_uint (node, "bvi_sw_if_index", ntohl (mp->bvi_sw_if_index)); - vat_json_object_add_uint (node, "n_sw_ifs", ntohl (mp->n_sw_ifs)); + vat_json_object_add_uint (node, "n_sw_ifs", n_sw_ifs); array = vat_json_object_add (node, "sw_if"); vat_json_init_array (array); -} - -/* - * Special-case: build the bridge domain sw if table. - */ -static void vl_api_bridge_domain_sw_if_details_t_handler - (vl_api_bridge_domain_sw_if_details_t * mp) -{ - vat_main_t *vam = &vat_main; - hash_pair_t *p; - u8 *sw_if_name = 0; - u32 sw_if_index; - - sw_if_index = ntohl (mp->sw_if_index); - /* *INDENT-OFF* */ - hash_foreach_pair (p, vam->sw_if_index_by_interface_name, - ({ - if ((u32) p->value[0] == sw_if_index) - { - sw_if_name = (u8 *)(p->key); - break; - } - })); - /* *INDENT-ON* */ - print (vam->ofp, "%7d %3d %s", sw_if_index, - mp->shg, sw_if_name ? (char *) sw_if_name : - "sw_if_index not found!"); -} -static void vl_api_bridge_domain_sw_if_details_t_handler_json - (vl_api_bridge_domain_sw_if_details_t * mp) -{ - vat_main_t *vam = &vat_main; - vat_json_node_t *node = NULL; - uword last_index = 0; - ASSERT (VAT_JSON_ARRAY == vam->json_tree.type); - ASSERT (vec_len (vam->json_tree.array) >= 1); - last_index = vec_len (vam->json_tree.array) - 1; - node = &vam->json_tree.array[last_index]; - node = vat_json_object_get_element (node, "sw_if"); - ASSERT (NULL != node); - node = vat_json_array_add (node); + if (n_sw_ifs) + { + vl_api_bridge_domain_sw_if_t *sw_ifs; + int i; - vat_json_init_object (node); - vat_json_object_add_uint (node, "bd_id", ntohl (mp->bd_id)); - vat_json_object_add_uint (node, "sw_if_index", ntohl (mp->sw_if_index)); - vat_json_object_add_uint (node, "shg", mp->shg); + sw_ifs = mp->sw_if_details; + for (i = 0; i < n_sw_ifs; i++) + { + node = vat_json_array_add (array); + vat_json_init_object (node); + vat_json_object_add_uint (node, "sw_if_index", + ntohl (sw_ifs->sw_if_index)); + vat_json_object_add_uint (node, "shg", sw_ifs->shg); + sw_ifs++; + } + } } static void vl_api_control_ping_reply_t_handler @@ -4334,7 +4339,6 @@ _(SW_INTERFACE_SET_L2_BRIDGE_REPLY, \ sw_interface_set_l2_bridge_reply) \ _(BRIDGE_DOMAIN_ADD_DEL_REPLY, bridge_domain_add_del_reply) \ _(BRIDGE_DOMAIN_DETAILS, bridge_domain_details) \ -_(BRIDGE_DOMAIN_SW_IF_DETAILS, bridge_domain_sw_if_details) \ _(BRIDGE_DOMAIN_SET_MAC_AGE_REPLY, bridge_domain_set_mac_age_reply) \ _(L2FIB_ADD_DEL_REPLY, l2fib_add_del_reply) \ _(L2FIB_FLUSH_INT_REPLY, l2fib_flush_int_reply) \ @@ -4409,7 +4413,7 @@ _(CREATE_VHOST_USER_IF_REPLY, create_vhost_user_if_reply) \ _(MODIFY_VHOST_USER_IF_REPLY, modify_vhost_user_if_reply) \ _(DELETE_VHOST_USER_IF_REPLY, delete_vhost_user_if_reply) \ _(SHOW_VERSION_REPLY, show_version_reply) \ -_(L2_FIB_TABLE_ENTRY, l2_fib_table_entry) \ +_(L2_FIB_TABLE_DETAILS, l2_fib_table_details) \ _(VXLAN_GPE_ADD_DEL_TUNNEL_REPLY, vxlan_gpe_add_del_tunnel_reply) \ _(VXLAN_GPE_TUNNEL_DETAILS, vxlan_gpe_tunnel_details) \ _(INTERFACE_NAME_RENUMBER_REPLY, interface_name_renumber_reply) \ @@ -4935,7 +4939,7 @@ int exec (vat_main_t * vam) { api_main_t *am = &api_main; - vl_api_cli_request_t *mp; + vl_api_cli_t *mp; f64 timeout; void *oldheap; u8 *cmd = 0; @@ -4956,7 +4960,7 @@ exec (vat_main_t * vam) } - M (CLI_REQUEST, mp); + M (CLI, mp); /* * Copy cmd into shared memory. @@ -11896,8 +11900,8 @@ format_l2_fib_mac_address (u8 * s, va_list * args) a[2], a[3], a[4], a[5], a[6], a[7]); } -static void vl_api_l2_fib_table_entry_t_handler - (vl_api_l2_fib_table_entry_t * mp) +static void vl_api_l2_fib_table_details_t_handler + (vl_api_l2_fib_table_details_t * mp) { vat_main_t *vam = &vat_main; @@ -11908,8 +11912,8 @@ static void vl_api_l2_fib_table_entry_t_handler mp->bvi_mac); } -static void vl_api_l2_fib_table_entry_t_handler_json - (vl_api_l2_fib_table_entry_t * mp) +static void vl_api_l2_fib_table_details_t_handler_json + (vl_api_l2_fib_table_details_t * mp) { vat_main_t *vam = &vat_main; vat_json_node_t *node = NULL; diff --git a/src/vnet/l2/l2.api b/src/vnet/l2/l2.api index e9a1f361..bb3990c6 100644 --- a/src/vnet/l2/l2.api +++ b/src/vnet/l2/l2.api @@ -36,7 +36,7 @@ define l2_xconnect_dump u32 context; }; -/** \brief l2 fib table entry structure +/** \brief l2 fib table details structure @param bd_id - the l2 fib / bridge domain table id @param mac - the entry's mac address @param sw_if_index - index of the interface @@ -44,7 +44,7 @@ define l2_xconnect_dump @param filter_mac - the entry is a mac filter entry. @param bvi_mac - the mac address is a bridge virtual interface */ -define l2_fib_table_entry +define l2_fib_table_details { u32 context; u32 bd_id; @@ -212,6 +212,18 @@ define bridge_domain_dump u32 bd_id; }; +/** \brief L2 bridge domain sw interface operational state response + @param bd_id - the bridge domain id + @param sw_if_index - sw_if_index in the domain + @param shg - split horizon group for the interface +*/ +typeonly manual_print manual_endian define bridge_domain_sw_if +{ + u32 context; + u32 sw_if_index; + u8 shg; +}; + /** \brief L2 bridge domain operational state response @param bd_id - the bridge domain id @param flood - bcast/mcast flooding state on all interfaces in the bd @@ -222,7 +234,7 @@ define bridge_domain_dump @param mac_age - mac aging time in min, 0 for disabled @param n_sw_ifs - number of sw_if_index's in the domain */ -define bridge_domain_details +manual_print manual_endian define bridge_domain_details { u32 context; u32 bd_id; @@ -234,19 +246,7 @@ define bridge_domain_details u8 mac_age; u32 bvi_sw_if_index; u32 n_sw_ifs; -}; - -/** \brief L2 bridge domain sw interface operational state response - @param bd_id - the bridge domain id - @param sw_if_index - sw_if_index in the domain - @param shg - split horizon group for the interface -*/ -define bridge_domain_sw_if_details -{ - u32 context; - u32 bd_id; - u32 sw_if_index; - u8 shg; + vl_api_bridge_domain_sw_if_t sw_if_details[n_sw_ifs]; }; /** \brief Set bridge flags (such as L2_LEARN, L2_FWD, L2_FLOOD, diff --git a/src/vnet/l2/l2_api.c b/src/vnet/l2/l2_api.c index 7c4b0423..aa3dcb7e 100644 --- a/src/vnet/l2/l2_api.c +++ b/src/vnet/l2/l2_api.c @@ -36,6 +36,9 @@ #include #undef vl_endianfun +#define vl_api_bridge_domain_details_t_endian vl_noop_handler +#define vl_api_bridge_domain_details_t_print vl_noop_handler + /* instantiate all the print functions we know about */ #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) #define vl_printfun @@ -119,11 +122,11 @@ send_l2fib_table_entry (vpe_api_main_t * am, l2fib_entry_key_t * l2fe_key, l2fib_entry_result_t * l2fe_res, u32 context) { - vl_api_l2_fib_table_entry_t *mp; + vl_api_l2_fib_table_details_t *mp; mp = vl_msg_api_alloc (sizeof (*mp)); memset (mp, 0, sizeof (*mp)); - mp->_vl_msg_id = ntohs (VL_API_L2_FIB_TABLE_ENTRY); + mp->_vl_msg_id = ntohs (VL_API_L2_FIB_TABLE_DETAILS); mp->bd_id = ntohl (l2input_main.bd_configs[l2fe_key->fields.bd_index].bd_id); @@ -358,13 +361,18 @@ vl_api_bridge_domain_add_del_t_handler (vl_api_bridge_domain_add_del_t * mp) } static void -send_bridge_domain_details (unix_shared_memory_queue_t * q, +send_bridge_domain_details (l2input_main_t * l2im, + unix_shared_memory_queue_t * q, l2_bridge_domain_t * bd_config, u32 n_sw_ifs, u32 context) { vl_api_bridge_domain_details_t *mp; + l2_flood_member_t *m; + vl_api_bridge_domain_sw_if_t *sw_ifs; + l2_input_config_t *input_cfg; - mp = vl_msg_api_alloc (sizeof (*mp)); + mp = vl_msg_api_alloc (sizeof (*mp) + + (n_sw_ifs * sizeof (vl_api_bridge_domain_sw_if_t))); memset (mp, 0, sizeof (*mp)); mp->_vl_msg_id = ntohs (VL_API_BRIDGE_DOMAIN_DETAILS); mp->bd_id = ntohl (bd_config->bd_id); @@ -375,28 +383,18 @@ send_bridge_domain_details (unix_shared_memory_queue_t * q, mp->arp_term = bd_feature_arp_term (bd_config); mp->bvi_sw_if_index = ntohl (bd_config->bvi_sw_if_index); mp->mac_age = bd_config->mac_age; - mp->n_sw_ifs = ntohl (n_sw_ifs); mp->context = context; - vl_msg_api_send_shmem (q, (u8 *) & mp); -} - -static void -send_bd_sw_if_details (l2input_main_t * l2im, - unix_shared_memory_queue_t * q, - l2_flood_member_t * member, u32 bd_id, u32 context) -{ - vl_api_bridge_domain_sw_if_details_t *mp; - l2_input_config_t *input_cfg; - - mp = vl_msg_api_alloc (sizeof (*mp)); - memset (mp, 0, sizeof (*mp)); - mp->_vl_msg_id = ntohs (VL_API_BRIDGE_DOMAIN_SW_IF_DETAILS); - mp->bd_id = ntohl (bd_id); - mp->sw_if_index = ntohl (member->sw_if_index); - input_cfg = vec_elt_at_index (l2im->configs, member->sw_if_index); - mp->shg = input_cfg->shg; - mp->context = context; + sw_ifs = (vl_api_bridge_domain_sw_if_t *) mp->sw_if_details; + vec_foreach (m, bd_config->members) + { + sw_ifs->sw_if_index = ntohl (m->sw_if_index); + input_cfg = vec_elt_at_index (l2im->configs, m->sw_if_index); + sw_ifs->shg = input_cfg->shg; + sw_ifs++; + mp->n_sw_ifs++; + } + mp->n_sw_ifs = htonl (mp->n_sw_ifs); vl_msg_api_send_shmem (q, (u8 *) & mp); } @@ -434,18 +432,9 @@ vl_api_bridge_domain_dump_t_handler (vl_api_bridge_domain_dump_t * mp) l2input_bd_config_from_index (l2im, bd_index); /* skip dummy bd_id 0 */ if (bd_config && (bd_config->bd_id > 0)) - { - u32 n_sw_ifs; - l2_flood_member_t *m; - - n_sw_ifs = vec_len (bd_config->members); - send_bridge_domain_details (q, bd_config, n_sw_ifs, mp->context); - - vec_foreach (m, bd_config->members) - { - send_bd_sw_if_details (l2im, q, m, bd_config->bd_id, mp->context); - } - } + send_bridge_domain_details (l2im, q, bd_config, + vec_len (bd_config->members), + mp->context); } } diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java index 0d7c7471..63659f82 100644 --- a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java @@ -67,8 +67,8 @@ public class FutureApiTest { } else { LOG.info( String.format( - "Received empty bridge-domain dump reply with list of bridge-domains: %s, %s", - reply.bridgeDomainDetails, reply.bridgeDomainSwIfDetails)); + "Received bridge-domain dump reply with list of bridge-domains: %s", + reply.bridgeDomainDetails)); } } diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/util.py b/src/vpp-api/java/jvpp/gen/jvppgen/util.py index fc971c17..947fc31d 100644 --- a/src/vpp-api/java/jvpp/gen/jvppgen/util.py +++ b/src/vpp-api/java/jvpp/gen/jvppgen/util.py @@ -156,13 +156,6 @@ jni_field_accessors = {'u8': 'ByteField', # vpe.api calls that do not follow naming conventions and have to be handled exceptionally when finding reply -> request mapping # FIXME in vpe.api unconventional_naming_rep_req = { - 'cli_reply': 'cli_request', - 'vnet_summary_stats_reply': 'vnet_get_summary_stats', - # This below is actually a sub-details callback. We cannot derive the mapping of dump request - # belonging to this sub-details from naming conventions. We need special mapping - 'bridge_domain_sw_if_details': 'bridge_domain', - # This is standard dump call + details reply. However it's not called details but entry - 'l2_fib_table_entry': 'l2_fib_table' } # @@ -172,7 +165,7 @@ notification_messages_reused = ["sw_interface_set_flags"] # messages that must be ignored. These messages are INSUFFICIENTLY marked as disabled in vpe.api # FIXME -ignored_messages = ["is_address_reachable"] +ignored_messages = [] def is_notification(name): diff --git a/src/vpp/api/api.c b/src/vpp/api/api.c index 7e4c341e..60eb5331 100644 --- a/src/vpp/api/api.c +++ b/src/vpp/api/api.c @@ -104,7 +104,6 @@ #define foreach_vpe_api_msg \ _(WANT_OAM_EVENTS, want_oam_events) \ _(OAM_ADD_DEL, oam_add_del) \ -_(IS_ADDRESS_REACHABLE, is_address_reachable) \ _(SW_INTERFACE_SET_MPLS_ENABLE, sw_interface_set_mpls_enable) \ _(SW_INTERFACE_SET_VPATH, sw_interface_set_vpath) \ _(SW_INTERFACE_SET_L2_XCONNECT, sw_interface_set_l2_xconnect) \ @@ -118,7 +117,7 @@ _(RESET_FIB, reset_fib) \ _(CREATE_LOOPBACK, create_loopback) \ _(CREATE_LOOPBACK_INSTANCE, create_loopback_instance) \ _(CONTROL_PING, control_ping) \ -_(CLI_REQUEST, cli_request) \ +_(CLI, cli) \ _(CLI_INBAND, cli_inband) \ _(SET_ARP_NEIGHBOR_LIMIT, set_arp_neighbor_limit) \ _(L2_PATCH_ADD_DEL, l2_patch_add_del) \ @@ -693,82 +692,6 @@ out: REPLY_MACRO (VL_API_PROXY_ARP_INTFC_ENABLE_DISABLE_REPLY); } -static void -vl_api_is_address_reachable_t_handler (vl_api_is_address_reachable_t * mp) -{ -#if 0 - vpe_main_t *rm = &vpe_main; - ip4_main_t *im4 = &ip4_main; - ip6_main_t *im6 = &ip6_main; - ip_lookup_main_t *lm; - union - { - ip4_address_t ip4; - ip6_address_t ip6; - } addr; - u32 adj_index, sw_if_index; - vl_api_is_address_reachable_t *rmp; - ip_adjacency_t *adj; - unix_shared_memory_queue_t *q; - - q = vl_api_client_index_to_input_queue (mp->client_index); - if (!q) - { - increment_missing_api_client_counter (rm->vlib_main); - return; - } - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - clib_memcpy (rmp, mp, sizeof (*rmp)); - - sw_if_index = mp->next_hop_sw_if_index; - clib_memcpy (&addr, mp->address, sizeof (addr)); - if (mp->is_ipv6) - { - lm = &im6->lookup_main; - adj_index = ip6_fib_lookup (im6, sw_if_index, &addr.ip6); - } - else - { - lm = &im4->lookup_main; - // FIXME NOT an ADJ - adj_index = ip4_fib_lookup (im4, sw_if_index, &addr.ip4); - } - if (adj_index == ~0) - { - rmp->is_error = 1; - goto send; - } - adj = ip_get_adjacency (lm, adj_index); - - if (adj->lookup_next_index == IP_LOOKUP_NEXT_REWRITE - && adj->rewrite_header.sw_if_index == sw_if_index) - { - rmp->is_known = 1; - } - else - { - if (adj->lookup_next_index == IP_LOOKUP_NEXT_ARP - && adj->rewrite_header.sw_if_index == sw_if_index) - { - if (mp->is_ipv6) - ip6_probe_neighbor (rm->vlib_main, &addr.ip6, sw_if_index); - else - ip4_probe_neighbor (rm->vlib_main, &addr.ip4, sw_if_index); - } - else if (adj->lookup_next_index == IP_LOOKUP_NEXT_DROP) - { - rmp->is_known = 1; - goto send; - } - rmp->is_known = 0; - } - -send: - vl_msg_api_send_shmem (q, (u8 *) & rmp); -#endif -} - static void vl_api_sw_interface_set_mpls_enable_t_handler (vl_api_sw_interface_set_mpls_enable_t * mp) @@ -828,7 +751,7 @@ vl_api_vnet_get_summary_stats_t_handler (vl_api_vnet_get_summary_stats_t * mp) { stats_main_t *sm = &stats_main; vnet_interface_main_t *im = sm->interface_main; - vl_api_vnet_summary_stats_reply_t *rmp; + vl_api_vnet_get_summary_stats_reply_t *rmp; vlib_combined_counter_main_t *cm; vlib_counter_t v; int i, which; @@ -842,7 +765,7 @@ vl_api_vnet_get_summary_stats_t_handler (vl_api_vnet_get_summary_stats_t * mp) return; rmp = vl_msg_api_alloc (sizeof (*rmp)); - rmp->_vl_msg_id = ntohs (VL_API_VNET_SUMMARY_STATS_REPLY); + rmp->_vl_msg_id = ntohs (VL_API_VNET_GET_SUMMARY_STATS_REPLY); rmp->context = mp->context; rmp->retval = 0; @@ -1115,7 +1038,7 @@ shmem_cli_output (uword arg, u8 * buffer, uword buffer_bytes) static void -vl_api_cli_request_t_handler (vl_api_cli_request_t * mp) +vl_api_cli_t_handler (vl_api_cli_t * mp) { vl_api_cli_reply_t *rp; unix_shared_memory_queue_t *q; diff --git a/src/vpp/api/custom_dump.c b/src/vpp/api/custom_dump.c index c073c52d..9071883b 100644 --- a/src/vpp/api/custom_dump.c +++ b/src/vpp/api/custom_dump.c @@ -1656,12 +1656,12 @@ static void *vl_api_want_interface_events_t_print FINISH; } -static void *vl_api_cli_request_t_print - (vl_api_cli_request_t * mp, void *handle) +static void * +vl_api_cli_t_print (vl_api_cli_t * mp, void *handle) { u8 *s; - s = format (0, "SCRIPT: cli_request "); + s = format (0, "SCRIPT: cli "); FINISH; } @@ -3023,7 +3023,7 @@ _(DELETE_VHOST_USER_IF, delete_vhost_user_if) \ _(SW_INTERFACE_DUMP, sw_interface_dump) \ _(CONTROL_PING, control_ping) \ _(WANT_INTERFACE_EVENTS, want_interface_events) \ -_(CLI_REQUEST, cli_request) \ +_(CLI, cli) \ _(CLI_INBAND, cli_inband) \ _(MEMCLNT_CREATE, memclnt_create) \ _(SW_INTERFACE_VHOST_USER_DUMP, sw_interface_vhost_user_dump) \ diff --git a/src/vpp/api/summary_stats_client.c b/src/vpp/api/summary_stats_client.c index 03999567..2c81d667 100644 --- a/src/vpp/api/summary_stats_client.c +++ b/src/vpp/api/summary_stats_client.c @@ -101,8 +101,8 @@ vlib_cli_output (struct vlib_main_t *vm, char *fmt, ...) static void -vl_api_vnet_summary_stats_reply_t_handler (vl_api_vnet_summary_stats_reply_t * - mp) + vl_api_vnet_get_summary_stats_reply_t_handler + (vl_api_vnet_get_summary_stats_reply_t * mp) { test_main_t *tm = &test_main; static u8 *sb; @@ -134,7 +134,7 @@ vl_api_vnet_summary_stats_reply_t_handler (vl_api_vnet_summary_stats_reply_t * } #define foreach_api_msg \ -_(VNET_SUMMARY_STATS_REPLY, vnet_summary_stats_reply) +_(VNET_GET_SUMMARY_STATS_REPLY, vnet_get_summary_stats_reply) int connect_to_vpe (char *name) diff --git a/src/vpp/api/vpe.api b/src/vpp/api/vpe.api index 99ae4784..d3c7e985 100644 --- a/src/vpp/api/vpe.api +++ b/src/vpp/api/vpe.api @@ -135,25 +135,6 @@ autoreply define reset_vrf u32 vrf_id; }; -/** \brief Is Address Reachable request - DISABLED - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param next_hop_sw_if_index - index of interface used to get to next hop - @param is_ipv6 - 1 for IPv6, 0 for IPv4 - @param is_error - address not found or does not match intf - @param address[] - Address in question -*/ -define is_address_reachable -{ - u32 client_index; /* (api_main_t *) am->my_client_index */ - u32 context; - u32 next_hop_sw_if_index; - u8 is_known; /* on reply, this is the answer */ - u8 is_ipv6; - u8 is_error; /* address not found or does not match intf */ - u8 address[16]; -}; - /** \brief Want Stats, register for stats updates @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @@ -256,7 +237,7 @@ define vnet_get_summary_stats @param total_bytes - @param vector_rate - */ -define vnet_summary_stats_reply +define vnet_get_summary_stats_reply { u32 context; i32 retval; @@ -414,7 +395,7 @@ define control_ping_reply @param context - sender context, to match reply w/ request @param cmd_in_shmem - pointer to cli command string */ -define cli_request +define cli { u32 client_index; u32 context; -- cgit 1.2.3-korg From 966de205fc272737895e12bf7f57856c2cbf0582 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Thu, 25 May 2017 16:18:28 +0200 Subject: Fix JNI templates The JNI templates around array and object handling are wrong in the sense that they fail to delete local references for objects which have been assigned to fields/arrays. Fix this by invoking DeleteLocalRef. Change-Id: I1c31d81f4235d821ccd51c96be7b176f64284928 Signed-off-by: Robert Varga Signed-off-by: Robert Varga --- src/vpp-api/java/jvpp/gen/jvppgen/types_gen.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/vpp-api/java') diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/types_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/types_gen.py index 93883ba1..22018e67 100644 --- a/src/vpp-api/java/jvpp/gen/jvppgen/types_gen.py +++ b/src/vpp-api/java/jvpp/gen/jvppgen/types_gen.py @@ -83,6 +83,7 @@ object_dto_field_setter_template = Template(""" jobject ${field_reference_name} = (*env)->NewObject(env, ${field_reference_name}Class, ${field_reference_name}Constructor); ${type_initialization} (*env)->SetObjectField(env, dto, ${field_reference_name}FieldId, ${field_reference_name}); + (*env)->DeleteLocalRef(env, ${field_reference_name}); } """) @@ -96,8 +97,10 @@ object_array_dto_field_setter_template = Template(""" jobject ${field_reference_name}ArrayElement = (*env)->NewObject(env, ${field_reference_name}Class, ${field_reference_name}Constructor); ${type_initialization} (*env)->SetObjectArrayElement(env, ${field_reference_name}, _i, ${field_reference_name}ArrayElement); + (*env)->DeleteLocalRef(env, ${field_reference_name}ArrayElement); } (*env)->SetObjectField(env, dto, ${field_reference_name}FieldId, ${field_reference_name}); + (*env)->DeleteLocalRef(env, ${field_reference_name}); } """) -- cgit 1.2.3-korg From ed1e242866704d1f8654e44ed32c4b431d2d5795 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Thu, 25 May 2017 19:03:18 +0200 Subject: Improve jvppgen object array member instantiation Since all objects of the array have the same type, the object constructor is a loop invariant. Move the lookup out of the loop, making things faster. Change-Id: I631c72b59c6c63eccd49ede41c6dc0541c325db9 Signed-off-by: Robert Varga Signed-off-by: Robert Varga --- src/vpp-api/java/jvpp/gen/jvppgen/types_gen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/vpp-api/java') diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/types_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/types_gen.py index 22018e67..858ea8ba 100644 --- a/src/vpp-api/java/jvpp/gen/jvppgen/types_gen.py +++ b/src/vpp-api/java/jvpp/gen/jvppgen/types_gen.py @@ -91,9 +91,9 @@ object_array_dto_field_setter_template = Template(""" { jclass ${field_reference_name}Class = (*env)->FindClass(env, "${class_FQN}"); jobjectArray ${field_reference_name} = (*env)->NewObjectArray(env, ${field_length}, ${field_reference_name}Class, 0); + jmethodID ${field_reference_name}Constructor = (*env)->GetMethodID(env, ${field_reference_name}Class, "", "()V"); unsigned int _i; for (_i = 0; _i < ${field_length}; _i++) { - jmethodID ${field_reference_name}Constructor = (*env)->GetMethodID(env, ${field_reference_name}Class, "", "()V"); jobject ${field_reference_name}ArrayElement = (*env)->NewObject(env, ${field_reference_name}Class, ${field_reference_name}Constructor); ${type_initialization} (*env)->SetObjectArrayElement(env, ${field_reference_name}, _i, ${field_reference_name}ArrayElement); -- cgit 1.2.3-korg From 5c749734b14c2d3be8689b0c5b72ae8d1ddec099 Mon Sep 17 00:00:00 2001 From: Ole Troan Date: Mon, 13 Mar 2017 13:39:52 +0100 Subject: Flowprobe: Stateful flows and IPv6, L4 recording Change-Id: I67839281623721bf42f0a918a53356143d9dc78a Signed-off-by: Ole Troan Signed-off-by: Pavel Kotucek Signed-off-by: Ole Troan --- MAINTAINERS | 8 +- doxygen/user_doc.md | 2 +- src/configure.ac | 2 +- src/plugins/Makefile.am | 4 +- src/plugins/flowperpkt.am | 38 - src/plugins/flowperpkt/flowperpkt.api | 40 - src/plugins/flowperpkt/flowperpkt.c | 621 ------------- src/plugins/flowperpkt/flowperpkt.h | 90 -- src/plugins/flowperpkt/flowperpkt_all_api_h.h | 18 - src/plugins/flowperpkt/flowperpkt_msg_enum.h | 31 - src/plugins/flowperpkt/flowperpkt_plugin_doc.md | 13 - src/plugins/flowperpkt/flowperpkt_test.c | 200 ---- src/plugins/flowperpkt/l2_node.c | 561 ------------ src/plugins/flowperpkt/node.c | 574 ------------ src/plugins/flowprobe.am | 37 + src/plugins/flowprobe/flowprobe.api | 40 + src/plugins/flowprobe/flowprobe.c | 1122 +++++++++++++++++++++++ src/plugins/flowprobe/flowprobe.h | 167 ++++ src/plugins/flowprobe/flowprobe_all_api_h.h | 18 + src/plugins/flowprobe/flowprobe_msg_enum.h | 31 + src/plugins/flowprobe/flowprobe_plugin_doc.md | 13 + src/plugins/flowprobe/flowprobe_test.c | 263 ++++++ src/plugins/flowprobe/node.c | 998 ++++++++++++++++++++ src/plugins/ioam/analyse/ioam_summary_export.c | 3 +- src/plugins/ioam/udp-ping/udp_ping_export.c | 3 +- src/plugins/snat/snat_ipfix_logging.c | 6 +- src/vnet/flow/flow_api.c | 2 +- src/vnet/flow/flow_report.c | 21 +- src/vnet/flow/flow_report.h | 3 +- src/vnet/flow/flow_report_classify.c | 2 +- src/vnet/ip/ip6_packet.h | 6 + src/vpp-api/java/jvpp/gen/jvpp_gen.py | 2 +- test/test_flowperpkt.py | 173 ---- test/test_flowprobe.py | 967 +++++++++++++++++++ 34 files changed, 3695 insertions(+), 2384 deletions(-) delete mode 100644 src/plugins/flowperpkt.am delete mode 100644 src/plugins/flowperpkt/flowperpkt.api delete mode 100644 src/plugins/flowperpkt/flowperpkt.c delete mode 100644 src/plugins/flowperpkt/flowperpkt.h delete mode 100644 src/plugins/flowperpkt/flowperpkt_all_api_h.h delete mode 100644 src/plugins/flowperpkt/flowperpkt_msg_enum.h delete mode 100644 src/plugins/flowperpkt/flowperpkt_plugin_doc.md delete mode 100644 src/plugins/flowperpkt/flowperpkt_test.c delete mode 100644 src/plugins/flowperpkt/l2_node.c delete mode 100644 src/plugins/flowperpkt/node.c create mode 100644 src/plugins/flowprobe.am create mode 100644 src/plugins/flowprobe/flowprobe.api create mode 100644 src/plugins/flowprobe/flowprobe.c create mode 100644 src/plugins/flowprobe/flowprobe.h create mode 100644 src/plugins/flowprobe/flowprobe_all_api_h.h create mode 100644 src/plugins/flowprobe/flowprobe_msg_enum.h create mode 100644 src/plugins/flowprobe/flowprobe_plugin_doc.md create mode 100644 src/plugins/flowprobe/flowprobe_test.c create mode 100644 src/plugins/flowprobe/node.c delete mode 100644 test/test_flowperpkt.py create mode 100644 test/test_flowprobe.py (limited to 'src/vpp-api/java') diff --git a/MAINTAINERS b/MAINTAINERS index 2f198319..0f5dfc04 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -112,10 +112,10 @@ VNET VXLAN M: John Lo F: src/vnet/vxlan/ -Plugin - flowperpkt -M: Dave Barach -F: src/plugins/flowperpkt/ -F: src/plugins/flowperpkt.am +Plugin - flowprobe +M: Ole Troan +F: src/plugins/flowprobe/ +F: src/plugins/flowprobe.am Plugin - SIXRD M: Ole Troan diff --git a/doxygen/user_doc.md b/doxygen/user_doc.md index d052c53b..becc2e0a 100644 --- a/doxygen/user_doc.md +++ b/doxygen/user_doc.md @@ -11,7 +11,7 @@ Several modules provide operational, dataplane-user focused documentation. - @subpage lldp_doc - @subpage map_doc - @subpage dpdk_crypto_ipsec_doc -- @subpage flowperpkt_plugin_doc +- @subpage flowprobe_plugin_doc - @subpage qos_doc - @subpage span_doc - @subpage srv6_doc diff --git a/src/configure.ac b/src/configure.ac index 2a907b2f..173b3153 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -146,7 +146,7 @@ AC_SUBST(AR_FLAGS) # Please keep alphabetical order PLUGIN_ENABLED(acl) PLUGIN_ENABLED(dpdk) -PLUGIN_ENABLED(flowperpkt) +PLUGIN_ENABLED(flowprobe) PLUGIN_ENABLED(gtpu) PLUGIN_ENABLED(ila) PLUGIN_ENABLED(ioam) diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index 388c9ad2..f26d0fd2 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -37,8 +37,8 @@ if ENABLE_DPDK_PLUGIN include dpdk.am endif -if ENABLE_FLOWPERPKT_PLUGIN -include flowperpkt.am +if ENABLE_FLOWPROBE_PLUGIN +include flowprobe.am endif diff --git a/src/plugins/flowperpkt.am b/src/plugins/flowperpkt.am deleted file mode 100644 index a400603a..00000000 --- a/src/plugins/flowperpkt.am +++ /dev/null @@ -1,38 +0,0 @@ - -# Copyright (c) -# 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. - -vppplugins_LTLIBRARIES += flowperpkt_plugin.la -vppapitestplugins_LTLIBRARIES += flowperpkt_test_plugin.la - -flowperpkt_plugin_la_SOURCES = flowperpkt/flowperpkt.c \ - flowperpkt/l2_node.c \ - flowperpkt/node.c \ - flowperpkt/flowperpkt_plugin.api.h - -BUILT_SOURCES += \ - flowperpkt/flowperpkt.api.h \ - flowperpkt/flowperpkt.api.json - -noinst_HEADERS += \ - flowperpkt/flowperpkt_all_api_h.h \ - flowperpkt/flowperpkt_msg_enum.h \ - flowperpkt/flowperpkt.api.h - -flowperpkt_test_plugin_la_SOURCES = \ - flowperpkt/flowperpkt_test.c \ - flowperpkt/flowperpkt_plugin.api.h - -API_FILES += flowperpkt/flowperpkt.api - -# vi:syntax=automake diff --git a/src/plugins/flowperpkt/flowperpkt.api b/src/plugins/flowperpkt/flowperpkt.api deleted file mode 100644 index 3ff92dca..00000000 --- a/src/plugins/flowperpkt/flowperpkt.api +++ /dev/null @@ -1,40 +0,0 @@ -/* Define a simple enable-disable binary API to control the feature */ - -/** \file - This file defines the vpp control-plane API messages - used to control the flowperpkt plugin -*/ - -/** \brief Enable / disable per-packet IPFIX recording on an interface - @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 - if non-zero the address is ipv6, else ipv4 - @param sw_if_index - index of the interface -*/ -autoreply manual_print define flowperpkt_tx_interface_add_del -{ - /* Client identifier, set from api_main.my_client_index */ - u32 client_index; - - /* Arbitrary context, so client can match reply to request */ - u32 context; - - /* Enable / disable the feature */ - u8 is_add; - u8 which; /* 0 = ipv4, 1 = l2, 2 = ipv6 */ - - /* Interface handle */ - u32 sw_if_index; -}; - -autoreply define flowperpkt_params -{ - u32 client_index; - u32 context; - u8 record_l2; - u8 record_l3; - u8 record_l4; - u32 active_timer; /* ~0 is off, 0 is default */ - u32 passive_timer; /* ~0 is off, 0 is default */ -}; diff --git a/src/plugins/flowperpkt/flowperpkt.c b/src/plugins/flowperpkt/flowperpkt.c deleted file mode 100644 index 3e5fc8b0..00000000 --- a/src/plugins/flowperpkt/flowperpkt.c +++ /dev/null @@ -1,621 +0,0 @@ -/* - * flowperpkt.c - per-packet data capture flow report plugin - * - * Copyright (c) - * 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. - */ - -/** - * @file - * @brief Per-packet IPFIX flow record generator plugin - * - * This file implements vpp plugin registration mechanics, - * debug CLI, and binary API handling. - */ - -#include -#include -#include -#include - -#include -#include -#include - -/* define message IDs */ -#include - -/* define message structures */ -#define vl_typedefs -#include -#undef vl_typedefs - -/* define generated endian-swappers */ -#define vl_endianfun -#include -#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 -#undef vl_printfun - -flowperpkt_main_t flowperpkt_main; - -/* Get the API version number */ -#define vl_api_version(n,v) static u32 api_version=(v); -#include -#undef vl_api_version - -#define REPLY_MSG_ID_BASE fm->msg_id_base -#include - -/* Define the per-interface configurable features */ -/* *INDENT-OFF* */ -VNET_FEATURE_INIT (flow_perpacket_ipv4, static) = -{ - .arc_name = "ip4-output", - .node_name = "flowperpkt-ipv4", - .runs_before = VNET_FEATURES ("interface-output"), -}; - -VNET_FEATURE_INIT (flow_perpacket_l2, static) = -{ - .arc_name = "interface-output", - .node_name = "flowperpkt-l2", - .runs_before = VNET_FEATURES ("interface-tx"), -}; -/* *INDENT-ON* */ - -/* Macro to finish up custom dump fns */ -#define FINISH \ - vec_add1 (s, 0); \ - vl_print (handle, (char *)s); \ - vec_free (s); \ - return handle; - -/** - * @brief Create an IPFIX template packet rewrite string - * @param frm flow_report_main_t * - * @param fr flow_report_t * - * @param collector_address ip4_address_t * the IPFIX collector address - * @param src_address ip4_address_t * the source address we should use - * @param collector_port u16 the collector port we should use, host byte order - * @returns u8 * vector containing the indicated IPFIX template packet - */ -static inline u8 * -flowperpkt_template_rewrite_inline (flow_report_main_t * frm, - flow_report_t * fr, - ip4_address_t * collector_address, - ip4_address_t * src_address, - u16 collector_port, int variant) -{ - ip4_header_t *ip; - udp_header_t *udp; - ipfix_message_header_t *h; - ipfix_set_header_t *s; - ipfix_template_header_t *t; - ipfix_field_specifier_t *f; - ipfix_field_specifier_t *first_field; - u8 *rewrite = 0; - ip4_ipfix_template_packet_t *tp; - u32 field_count = 0; - flow_report_stream_t *stream; - flowperpkt_main_t *fm = &flowperpkt_main; - - stream = &frm->streams[fr->stream_index]; - - if (variant == FLOW_VARIANT_IPV4) - { - /* - * ip4 Supported Fields: - * - * ingressInterface, TLV type 10, u32 - * egressInterface, TLV type 14, u32 - * sourceIpv4Address, TLV type 8, u32 - * destinationIPv4Address, TLV type 12, u32 - * ipClassOfService, TLV type 5, u8 - * flowStartNanoseconds, TLV type 156, dateTimeNanoseconds (f64) - * Implementation: f64 nanoseconds since VPP started - * warning: wireshark doesn't really understand this TLV - * dataLinkFrameSize, TLV type 312, u16 - * warning: wireshark doesn't understand this TLV at all - */ - - /* Currently 7 fields */ - field_count += 7; - - /* allocate rewrite space */ - vec_validate_aligned - (rewrite, - sizeof (ip4_ipfix_template_packet_t) - + field_count * sizeof (ipfix_field_specifier_t) - 1, - CLIB_CACHE_LINE_BYTES); - } - else if (variant == FLOW_VARIANT_L2) - { - /* - * L2 Supported Fields: - * - * ingressInterface, TLV type 10, u32 - * egressInterface, TLV type 14, u32 - * sourceMacAddress, TLV type 56, u8[6] we hope - * destinationMacAddress, TLV type 57, u8[6] we hope - * ethernetType, TLV type 256, u16 - * flowStartNanoseconds, TLV type 156, dateTimeNanoseconds (f64) - * Implementation: f64 nanoseconds since VPP started - * warning: wireshark doesn't really understand this TLV - * dataLinkFrameSize, TLV type 312, u16 - * warning: wireshark doesn't understand this TLV at all - */ - - /* Currently 7 fields */ - field_count += 7; - - /* allocate rewrite space */ - vec_validate_aligned - (rewrite, - sizeof (ip4_ipfix_template_packet_t) - + field_count * sizeof (ipfix_field_specifier_t) - 1, - CLIB_CACHE_LINE_BYTES); - } - - tp = (ip4_ipfix_template_packet_t *) rewrite; - ip = (ip4_header_t *) & tp->ip4; - udp = (udp_header_t *) (ip + 1); - h = (ipfix_message_header_t *) (udp + 1); - s = (ipfix_set_header_t *) (h + 1); - t = (ipfix_template_header_t *) (s + 1); - first_field = f = (ipfix_field_specifier_t *) (t + 1); - - ip->ip_version_and_header_length = 0x45; - ip->ttl = 254; - ip->protocol = IP_PROTOCOL_UDP; - ip->src_address.as_u32 = src_address->as_u32; - ip->dst_address.as_u32 = collector_address->as_u32; - udp->src_port = clib_host_to_net_u16 (stream->src_port); - udp->dst_port = clib_host_to_net_u16 (collector_port); - udp->length = clib_host_to_net_u16 (vec_len (rewrite) - sizeof (*ip)); - - /* FIXUP: message header export_time */ - /* FIXUP: message header sequence_number */ - h->domain_id = clib_host_to_net_u32 (stream->domain_id); - - /* Add TLVs to the template */ - if (variant == FLOW_VARIANT_IPV4) - { - f->e_id_length = - ipfix_e_id_length (0 /* enterprise */ , ingressInterface, - 4); - f++; - f->e_id_length = - ipfix_e_id_length (0 /* enterprise */ , egressInterface, - 4); - f++; - f->e_id_length = - ipfix_e_id_length (0 /* enterprise */ , sourceIPv4Address, - 4); - f++; - f->e_id_length = - ipfix_e_id_length (0 /* enterprise */ , destinationIPv4Address, 4); - f++; - f->e_id_length = - ipfix_e_id_length (0 /* enterprise */ , ipClassOfService, - 1); - f++; - f->e_id_length = - ipfix_e_id_length (0 /* enterprise */ , flowStartNanoseconds, - 8); - f++; - f->e_id_length = - ipfix_e_id_length (0 /* enterprise */ , dataLinkFrameSize, - 2); - f++; - } - else if (variant == FLOW_VARIANT_L2) - { - f->e_id_length = - ipfix_e_id_length (0 /* enterprise */ , ingressInterface, - 4); - f++; - f->e_id_length = - ipfix_e_id_length (0 /* enterprise */ , egressInterface, - 4); - f++; - f->e_id_length = - ipfix_e_id_length (0 /* enterprise */ , sourceMacAddress, - 6); - f++; - f->e_id_length = - ipfix_e_id_length (0 /* enterprise */ , destinationMacAddress, 6); - f++; - f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , ethernetType, - 2); - f++; - f->e_id_length = - ipfix_e_id_length (0 /* enterprise */ , flowStartNanoseconds, - 8); - f++; - f->e_id_length = - ipfix_e_id_length (0 /* enterprise */ , dataLinkFrameSize, - 2); - f++; - } - - /* Extend in the obvious way, right here... */ - - /* Back to the template packet... */ - ip = (ip4_header_t *) & tp->ip4; - udp = (udp_header_t *) (ip + 1); - - ASSERT (f - first_field); - /* Field count in this template */ - t->id_count = ipfix_id_count (fr->template_id, f - first_field); - - if (variant == FLOW_VARIANT_IPV4) - fm->ipv4_report_id = fr->template_id; - else if (variant == FLOW_VARIANT_L2) - fm->l2_report_id = fr->template_id; - - /* set length in octets */ - s->set_id_length = - ipfix_set_id_length (2 /* set_id */ , (u8 *) f - (u8 *) s); - - /* message length in octets */ - h->version_length = version_length ((u8 *) f - (u8 *) h); - - ip->length = clib_host_to_net_u16 ((u8 *) f - (u8 *) ip); - ip->checksum = ip4_header_checksum (ip); - - return rewrite; -} - -u8 * -flowperpkt_template_rewrite_ipv4 (flow_report_main_t * frm, - flow_report_t * fr, - ip4_address_t * collector_address, - ip4_address_t * src_address, - u16 collector_port) -{ - return flowperpkt_template_rewrite_inline - (frm, fr, collector_address, src_address, collector_port, - FLOW_VARIANT_IPV4); -} - -u8 * -flowperpkt_template_rewrite_l2 (flow_report_main_t * frm, - flow_report_t * fr, - ip4_address_t * collector_address, - ip4_address_t * src_address, - u16 collector_port) -{ - return flowperpkt_template_rewrite_inline - (frm, fr, collector_address, src_address, collector_port, - FLOW_VARIANT_L2); -} - - -/** - * @brief Flush accumulated data - * @param frm flow_report_main_t * - * @param fr flow_report_t * - * @param f vlib_frame_t * - * - * Notes: - * This function must simply return the incoming frame, or no template packets - * will be sent. - */ -vlib_frame_t * -flowperpkt_data_callback_ipv4 (flow_report_main_t * frm, - flow_report_t * fr, - vlib_frame_t * f, u32 * to_next, - u32 node_index) -{ - flowperpkt_flush_callback_ipv4 (); - return f; -} - -vlib_frame_t * -flowperpkt_data_callback_l2 (flow_report_main_t * frm, - flow_report_t * fr, - vlib_frame_t * f, u32 * to_next, u32 node_index) -{ - flowperpkt_flush_callback_l2 (); - return f; -} - -/** - * @brief configure / deconfigure the IPFIX flow-per-packet - * @param fm flowperpkt_main_t * fm - * @param sw_if_index u32 the desired interface - * @param is_add int 1 to enable the feature, 0 to disable it - * @returns 0 if successful, non-zero otherwise - */ - -static int flowperpkt_tx_interface_add_del_feature - (flowperpkt_main_t * fm, u32 sw_if_index, int which, int is_add) -{ - flow_report_main_t *frm = &flow_report_main; - vnet_flow_report_add_del_args_t _a, *a = &_a; - int rv; - - if (which == FLOW_VARIANT_IPV4 && !fm->ipv4_report_created) - { - memset (a, 0, sizeof (*a)); - a->rewrite_callback = flowperpkt_template_rewrite_ipv4; - a->flow_data_callback = flowperpkt_data_callback_ipv4; - a->is_add = 1; - a->domain_id = 1; /*$$$$ config parameter */ - a->src_port = 4739; /*$$$$ config parameter */ - fm->ipv4_report_created = 1; - - rv = vnet_flow_report_add_del (frm, a); - if (rv) - { - clib_warning ("vnet_flow_report_add_del returned %d", rv); - return -1; - } - } - else if (which == FLOW_VARIANT_L2 && !fm->l2_report_created) - { - memset (a, 0, sizeof (*a)); - a->rewrite_callback = flowperpkt_template_rewrite_l2; - a->flow_data_callback = flowperpkt_data_callback_l2; - a->is_add = 1; - a->domain_id = 1; /*$$$$ config parameter */ - a->src_port = 4739; /*$$$$ config parameter */ - fm->l2_report_created = 1; - - rv = vnet_flow_report_add_del (frm, a); - if (rv) - { - clib_warning ("vnet_flow_report_add_del returned %d", rv); - return -1; - } - } - - if (which == FLOW_VARIANT_IPV4) - vnet_feature_enable_disable ("ip4-output", "flowperpkt-ipv4", - sw_if_index, is_add, 0, 0); - else if (which == FLOW_VARIANT_L2) - vnet_feature_enable_disable ("interface-output", "flowperpkt-l2", - sw_if_index, is_add, 0, 0); - - return 0; -} - -/** - * @brief API message handler - * @param mp vl_api_flowperpkt_tx_interface_add_del_t * mp the api message - */ -void vl_api_flowperpkt_tx_interface_add_del_t_handler - (vl_api_flowperpkt_tx_interface_add_del_t * mp) -{ - flowperpkt_main_t *fm = &flowperpkt_main; - vl_api_flowperpkt_tx_interface_add_del_reply_t *rmp; - u32 sw_if_index = ntohl (mp->sw_if_index); - int rv = 0; - - VALIDATE_SW_IF_INDEX (mp); - - if (mp->which != FLOW_VARIANT_IPV4 && mp->which != FLOW_VARIANT_L2) - { - rv = VNET_API_ERROR_UNIMPLEMENTED; - goto out; - } - - rv = flowperpkt_tx_interface_add_del_feature (fm, sw_if_index, mp->which, - mp->is_add); -out: - BAD_SW_IF_INDEX_LABEL; - - REPLY_MACRO (VL_API_FLOWPERPKT_TX_INTERFACE_ADD_DEL_REPLY); -} - -/** - * @brief API message custom-dump function - * @param mp vl_api_flowperpkt_tx_interface_add_del_t * mp the api message - * @param handle void * print function handle - * @returns u8 * output string - */ -static void *vl_api_flowperpkt_tx_interface_add_del_t_print - (vl_api_flowperpkt_tx_interface_add_del_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: flowperpkt_tx_interface_add_del "); - s = format (s, "sw_if_index %d is_add %d which %d ", - clib_host_to_net_u32 (mp->sw_if_index), - (int) mp->is_add, (int) mp->which); - FINISH; -} - -/* List of message types that this plugin understands */ -#define foreach_flowperpkt_plugin_api_msg \ -_(FLOWPERPKT_TX_INTERFACE_ADD_DEL, flowperpkt_tx_interface_add_del) - -/* *INDENT-OFF* */ -VLIB_PLUGIN_REGISTER () = { - .version = VPP_BUILD_VER, - .description = "Flow per Packet", -}; -/* *INDENT-ON* */ - -static clib_error_t * -flowperpkt_tx_interface_add_del_feature_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - flowperpkt_main_t *fm = &flowperpkt_main; - u32 sw_if_index = ~0; - int is_add = 1; - u8 which = FLOW_VARIANT_IPV4; - - int rv; - - while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (input, "disable")) - is_add = 0; - else if (unformat (input, "%U", unformat_vnet_sw_interface, - fm->vnet_main, &sw_if_index)); - else if (unformat (input, "l2")) - which = FLOW_VARIANT_L2; - else - break; - } - - if (sw_if_index == ~0) - return clib_error_return (0, "Please specify an interface..."); - - rv = - flowperpkt_tx_interface_add_del_feature (fm, sw_if_index, which, is_add); - switch (rv) - { - case 0: - break; - - case VNET_API_ERROR_INVALID_SW_IF_INDEX: - return clib_error_return - (0, "Invalid interface, only works on physical ports"); - break; - - case VNET_API_ERROR_UNIMPLEMENTED: - return clib_error_return (0, "ip6 not supported"); - break; - - default: - return clib_error_return (0, "flowperpkt_enable_disable returned %d", - rv); - } - return 0; -} - -/*? - * 'flowperpkt feature add-del' commands to enable/disable - * per-packet IPFIX flow record generation on an interface - * - * @cliexpar - * @parblock - * To enable per-packet IPFIX flow-record generation on an interface: - * @cliexcmd{flowperpkt feature add-del GigabitEthernet2/0/0} - * - * To disable per-packet IPFIX flow-record generation on an interface: - * @cliexcmd{flowperpkt feature add-del GigabitEthernet2/0/0 disable} - * @cliexend - * @endparblock -?*/ -/* *INDENT-OFF* */ -VLIB_CLI_COMMAND (flowperpkt_enable_disable_command, static) = { - .path = "flowperpkt feature add-del", - .short_help = - "flowperpkt feature add-del [disable]", - .function = flowperpkt_tx_interface_add_del_feature_command_fn, -}; -/* *INDENT-ON* */ - -/** - * @brief Set up the API message handling tables - * @param vm vlib_main_t * vlib main data structure pointer - * @returns 0 to indicate all is well - */ -static clib_error_t * -flowperpkt_plugin_api_hookup (vlib_main_t * vm) -{ - flowperpkt_main_t *fm = &flowperpkt_main; -#define _(N,n) \ - vl_msg_api_set_handlers((VL_API_##N + fm->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_flowperpkt_plugin_api_msg; -#undef _ - - return 0; -} - -#define vl_msg_name_crc_list -#include -#undef vl_msg_name_crc_list - -static void -setup_message_id_table (flowperpkt_main_t * fm, api_main_t * am) -{ -#define _(id,n,crc) \ - vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + fm->msg_id_base); - foreach_vl_msg_name_crc_flowperpkt; -#undef _ -} - -/** - * @brief Set up the API message handling tables - * @param vm vlib_main_t * vlib main data structure pointer - * @returns 0 to indicate all is well, or a clib_error_t - */ -static clib_error_t * -flowperpkt_init (vlib_main_t * vm) -{ - flowperpkt_main_t *fm = &flowperpkt_main; - vlib_thread_main_t *tm = &vlib_thread_main; - clib_error_t *error = 0; - u32 num_threads; - u8 *name; - - fm->vnet_main = vnet_get_main (); - - /* Construct the API name */ - name = format (0, "flowperpkt_%08x%c", api_version, 0); - - /* Ask for a correctly-sized block of API message decode slots */ - fm->msg_id_base = vl_msg_api_get_msg_ids - ((char *) name, VL_MSG_FIRST_AVAILABLE); - - /* Hook up message handlers */ - error = flowperpkt_plugin_api_hookup (vm); - - /* Add our API messages to the global name_crc hash table */ - setup_message_id_table (fm, &api_main); - - vec_free (name); - - /* Decide how many worker threads we have */ - num_threads = 1 /* main thread */ + tm->n_threads; - - /* Allocate per worker thread vectors */ - vec_validate (fm->ipv4_buffers_per_worker, num_threads - 1); - vec_validate (fm->l2_buffers_per_worker, num_threads - 1); - vec_validate (fm->ipv4_frames_per_worker, num_threads - 1); - vec_validate (fm->l2_frames_per_worker, num_threads - 1); - vec_validate (fm->ipv4_next_record_offset_per_worker, num_threads - 1); - vec_validate (fm->l2_next_record_offset_per_worker, num_threads - 1); - - /* Set up time reference pair */ - fm->vlib_time_0 = vlib_time_now (vm); - fm->nanosecond_time_0 = unix_time_now_nsec (); - - return error; -} - -VLIB_INIT_FUNCTION (flowperpkt_init); - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/flowperpkt/flowperpkt.h b/src/plugins/flowperpkt/flowperpkt.h deleted file mode 100644 index 20f6939d..00000000 --- a/src/plugins/flowperpkt/flowperpkt.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * flowperpkt.h - skeleton vpp engine plug-in header file - * - * Copyright (c) - * 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_flowperpkt_h__ -#define __included_flowperpkt_h__ - -#include -#include -#include - -#include -#include -#include -#include - -/** - * @file - * @brief flow-per-packet plugin header file - */ -typedef struct -{ - /** API message ID base */ - u16 msg_id_base; - - /** Have the reports [templates] been created? */ - int ipv4_report_created; - int l2_report_created; - - /** stream/template IDs */ - u16 ipv4_report_id; - u16 l2_report_id; - - /** ipfix buffers under construction, per-worker thread */ - vlib_buffer_t **ipv4_buffers_per_worker; - vlib_buffer_t **l2_buffers_per_worker; - - /** frames containing ipfix buffers, per-worker thread */ - vlib_frame_t **ipv4_frames_per_worker; - vlib_frame_t **l2_frames_per_worker; - - /** next record offset, per worker thread */ - u16 *ipv4_next_record_offset_per_worker; - u16 *l2_next_record_offset_per_worker; - - /** Time reference pair */ - u64 nanosecond_time_0; - f64 vlib_time_0; - - /** convenience vlib_main_t pointer */ - vlib_main_t *vlib_main; - /** convenience vnet_main_t pointer */ - vnet_main_t *vnet_main; -} flowperpkt_main_t; - -typedef enum -{ - FLOW_VARIANT_IPV4, - FLOW_VARIANT_L2, - FLOW_N_VARIANTS, -} flowperpkt_variant_t; - -extern flowperpkt_main_t flowperpkt_main; - -extern vlib_node_registration_t flowperpkt_ipv4_node; - -void flowperpkt_flush_callback_ipv4 (void); -void flowperpkt_flush_callback_l2 (void); - -#endif /* __included_flowperpkt_h__ */ - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/flowperpkt/flowperpkt_all_api_h.h b/src/plugins/flowperpkt/flowperpkt_all_api_h.h deleted file mode 100644 index 329c375a..00000000 --- a/src/plugins/flowperpkt/flowperpkt_all_api_h.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * flowperpkt_all_api_h.h - plug-in api #include file - * - * Copyright (c) - * 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 diff --git a/src/plugins/flowperpkt/flowperpkt_msg_enum.h b/src/plugins/flowperpkt/flowperpkt_msg_enum.h deleted file mode 100644 index 3177e77a..00000000 --- a/src/plugins/flowperpkt/flowperpkt_msg_enum.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * flowperpkt_msg_enum.h - vpp engine plug-in message enumeration - * - * Copyright (c) - * 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_flowperpkt_msg_enum_h -#define included_flowperpkt_msg_enum_h - -#include - -#define vl_msg_id(n,h) n, -typedef enum -{ -#include - /* 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_flowperpkt_msg_enum_h */ diff --git a/src/plugins/flowperpkt/flowperpkt_plugin_doc.md b/src/plugins/flowperpkt/flowperpkt_plugin_doc.md deleted file mode 100644 index ed76c45c..00000000 --- a/src/plugins/flowperpkt/flowperpkt_plugin_doc.md +++ /dev/null @@ -1,13 +0,0 @@ -Per-packet IPFIX flow record plugin {#flowperpkt_plugin_doc} -=================================== - -## Introduction - -This plugin generates one ipfix record entry per packet transmitted -on interfaces which have the feature enabled - -## Sample configuration - -set ipfix exporter collector 192.168.6.2 src 192.168.6.1 template-interval 20 port 4739 path-mtu 1500 - -flowperpkt feature add-del GigabitEthernet2/3/0 diff --git a/src/plugins/flowperpkt/flowperpkt_test.c b/src/plugins/flowperpkt/flowperpkt_test.c deleted file mode 100644 index 972a3b07..00000000 --- a/src/plugins/flowperpkt/flowperpkt_test.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * flowperpkt.c - skeleton vpp-api-test plug-in - * - * Copyright (c) - * 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 -#include -#include -#include -#include - -#define __plugin_msg_base flowperpkt_test_main.msg_id_base -#include - -/** - * @file vpp_api_test plugin - */ - -uword unformat_sw_if_index (unformat_input_t * input, va_list * args); - -/* Declare message IDs */ -#include - -/* define message structures */ -#define vl_typedefs -#include -#undef vl_typedefs - -/* declare message handlers for each api */ - -#define vl_endianfun /* define message structures */ -#include -#undef vl_endianfun - -/* instantiate all the print functions we know about */ -#define vl_print(handle, ...) -#define vl_printfun -#include -#undef vl_printfun - -/* Get the API version number. */ -#define vl_api_version(n,v) static u32 api_version=(v); -#include -#undef vl_api_version - -typedef struct -{ - /** API message ID base */ - u16 msg_id_base; - /** vat_main_t pointer */ - vat_main_t *vat_main; -} flowperpkt_test_main_t; - -flowperpkt_test_main_t flowperpkt_test_main; - -#define foreach_standard_reply_retval_handler \ -_(flowperpkt_tx_interface_add_del_reply) - -#define _(n) \ - static void vl_api_##n##_t_handler \ - (vl_api_##n##_t * mp) \ - { \ - vat_main_t * vam = flowperpkt_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 \ -_(FLOWPERPKT_TX_INTERFACE_ADD_DEL_REPLY, \ - flowperpkt_tx_interface_add_del_reply) - -static int -api_flowperpkt_tx_interface_add_del (vat_main_t * vam) -{ - unformat_input_t *i = vam->input; - int enable_disable = 1; - u8 which = 0; /* ipv4 by default */ - u32 sw_if_index = ~0; - vl_api_flowperpkt_tx_interface_add_del_t *mp; - int ret; - - /* Parse args required to build the message */ - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) - { - if (unformat (i, "%U", unformat_sw_if_index, vam, &sw_if_index)) - ; - else if (unformat (i, "sw_if_index %d", &sw_if_index)) - ; - else if (unformat (i, "disable")) - enable_disable = 0; - else if (unformat (i, "l2")) - which = 1; - else - break; - } - - if (sw_if_index == ~0) - { - errmsg ("missing interface name / explicit sw_if_index number \n"); - return -99; - } - - /* Construct the API message */ - M (FLOWPERPKT_TX_INTERFACE_ADD_DEL, mp); - mp->sw_if_index = ntohl (sw_if_index); - mp->is_add = enable_disable; - mp->which = which; - - /* send it... */ - S (mp); - - /* Wait for a reply... */ - W (ret); - return ret; -} - -/* - * List of messages that the api test plugin sends, - * and that the data plane plugin processes - */ -#define foreach_vpe_api_msg \ -_(flowperpkt_tx_interface_add_del, " [disable]") - -static void -flowperpkt_vat_api_hookup (vat_main_t * vam) -{ - flowperpkt_test_main_t *sm = &flowperpkt_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) -{ - flowperpkt_test_main_t *sm = &flowperpkt_test_main; - u8 *name; - - sm->vat_main = vam; - - /* Ask the vpp engine for the first assigned message-id */ - name = format (0, "flowperpkt_%08x%c", api_version, 0); - sm->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name); - - /* Don't attempt to hook up API messages if the data plane plugin is AWOL */ - if (sm->msg_id_base != (u16) ~ 0) - flowperpkt_vat_api_hookup (vam); - - vec_free (name); - - return 0; -} - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/flowperpkt/l2_node.c b/src/plugins/flowperpkt/l2_node.c deleted file mode 100644 index db80e990..00000000 --- a/src/plugins/flowperpkt/l2_node.c +++ /dev/null @@ -1,561 +0,0 @@ -/* - * l2_node.c - l2 ipfix-per-packet graph node - * - * Copyright (c) - * 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 -#include -#include -#include -#include - -/** - * @file l2 flow record generator graph node - */ - -typedef struct -{ - /** interface handle */ - u32 rx_sw_if_index; - u32 tx_sw_if_index; - /** src and dst L2 addresses */ - u8 src_mac[6]; - u8 dst_mac[6]; - /** Ethertype */ - u16 ethertype; - /** packet timestamp */ - u64 timestamp; - /** size of the buffer */ - u16 buffer_size; -} flowperpkt_l2_trace_t; - -/* packet trace format function */ -static u8 * -format_flowperpkt_l2_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 *); - flowperpkt_l2_trace_t *t = va_arg (*args, flowperpkt_l2_trace_t *); - - s = format (s, - "FLOWPERPKT-L2: rx_sw_if_index %d, tx_sw_if_index %d, src %U dst %U ethertype %0x2, timestamp %lld, size %d", - t->rx_sw_if_index, t->tx_sw_if_index, - format_ethernet_address, &t->src_mac, - format_ethernet_address, &t->dst_mac, - t->ethertype, t->timestamp, t->buffer_size); - return s; -} - -vlib_node_registration_t flowperpkt_l2_node; - -/* No counters at the moment */ -#define foreach_flowperpkt_l2_error - -typedef enum -{ -#define _(sym,str) FLOWPERPKT_ERROR_##sym, - foreach_flowperpkt_l2_error -#undef _ - FLOWPERPKT_N_ERROR, -} flowperpkt_l2_error_t; - -static char *flowperpkt_l2_error_strings[] = { -#define _(sym,string) string, - foreach_flowperpkt_l2_error -#undef _ -}; - -typedef enum -{ - FLOWPERPKT_L2_NEXT_DROP, - FLOWPERPKT_L2_NEXT_IP4_LOOKUP, - FLOWPERPKT_L2_N_NEXT, -} flowperpkt_l2_next_t; - -/** - * @brief add an entry to the flow record under construction - * @param vm vlib_main_t * current worker thread main structure pointer - * @param fm flowperpkt_main_t * flow-per-packet main structure pointer - * @param sw_if_index u32 interface handle - * @param tos u8 ToS bits from the packet - * @param timestamp u64 timestamp, nanoseconds since 1/1/70 - * @param length u16 ip length of the packet - * @param do_flush int 1 = flush all cached records, 0 = construct a record - */ - -static inline void -add_to_flow_record_l2 (vlib_main_t * vm, - vlib_node_runtime_t * node, - flowperpkt_main_t * fm, - u32 rx_sw_if_index, u32 tx_sw_if_index, - u8 * src_mac, u8 * dst_mac, - u16 ethertype, u64 timestamp, u16 length, int do_flush) -{ - u32 my_thread_index = vm->thread_index; - flow_report_main_t *frm = &flow_report_main; - ip4_header_t *ip; - udp_header_t *udp; - ip4_ipfix_template_packet_t *tp; - ipfix_message_header_t *h; - ipfix_set_header_t *s; - vlib_frame_t *f; - vlib_buffer_t *b0; - u16 offset; - u32 bi0; - vlib_buffer_free_list_t *fl; - - /* Find or allocate a buffer */ - b0 = fm->l2_buffers_per_worker[my_thread_index]; - - /* Need to allocate a buffer? */ - if (PREDICT_FALSE (b0 == 0)) - { - /* Nothing to flush */ - if (do_flush) - return; - - /* $$$$ drop counter? */ - if (vlib_buffer_alloc (vm, &bi0, 1) != 1) - return; - - /* Initialize the buffer */ - b0 = fm->l2_buffers_per_worker[my_thread_index] = - vlib_get_buffer (vm, bi0); - fl = - vlib_buffer_get_free_list (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX); - vlib_buffer_init_for_free_list (b0, fl); - VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0); - offset = 0; - } - else - { - /* use the current buffer */ - bi0 = vlib_get_buffer_index (vm, b0); - offset = fm->l2_next_record_offset_per_worker[my_thread_index]; - } - - /* Find or allocate a frame */ - f = fm->l2_frames_per_worker[my_thread_index]; - if (PREDICT_FALSE (f == 0)) - { - u32 *to_next; - f = vlib_get_frame_to_node (vm, ip4_lookup_node.index); - fm->l2_frames_per_worker[my_thread_index] = f; - - /* Enqueue the buffer */ - to_next = vlib_frame_vector_args (f); - to_next[0] = bi0; - f->n_vectors = 1; - } - - /* Fresh packet, construct header */ - if (PREDICT_FALSE (offset == 0)) - { - flow_report_stream_t *stream; - - stream = &frm->streams[0]; - - b0->current_data = 0; - b0->current_length = sizeof (*ip) + sizeof (*udp) + sizeof (*h) + - sizeof (*s); - b0->flags |= (VLIB_BUFFER_TOTAL_LENGTH_VALID | VLIB_BUFFER_FLOW_REPORT); - vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0; - vnet_buffer (b0)->sw_if_index[VLIB_TX] = frm->fib_index; - - tp = vlib_buffer_get_current (b0); - ip = (ip4_header_t *) & tp->ip4; - udp = (udp_header_t *) (ip + 1); - h = (ipfix_message_header_t *) (udp + 1); - s = (ipfix_set_header_t *) (h + 1); - - ip->ip_version_and_header_length = 0x45; - ip->ttl = 254; - ip->protocol = IP_PROTOCOL_UDP; - ip->flags_and_fragment_offset = 0; - ip->src_address.as_u32 = frm->src_address.as_u32; - ip->dst_address.as_u32 = frm->ipfix_collector.as_u32; - udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_ipfix); - udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_ipfix); - udp->checksum = 0; - - /* FIXUP: message header export_time */ - h->export_time = (u32) - (((f64) frm->unix_time_0) + - (vlib_time_now (frm->vlib_main) - frm->vlib_time_0)); - h->export_time = clib_host_to_net_u32 (h->export_time); - h->domain_id = clib_host_to_net_u32 (stream->domain_id); - - /* FIXUP: message header sequence_number */ - h->sequence_number = stream->sequence_number++; - h->sequence_number = clib_host_to_net_u32 (h->sequence_number); - - offset = (u32) (((u8 *) (s + 1)) - (u8 *) tp); - } - - /* Add data, unless we're flushing stale data */ - if (PREDICT_TRUE (do_flush == 0)) - { - - /* Add data */ - /* Ingress interface */ - { - u32 ingress_interface = clib_host_to_net_u32 (rx_sw_if_index); - clib_memcpy (b0->data + offset, &ingress_interface, - sizeof (ingress_interface)); - offset += sizeof (ingress_interface); - } - /* Egress interface */ - { - u32 egress_interface = clib_host_to_net_u32 (tx_sw_if_index); - clib_memcpy (b0->data + offset, &egress_interface, - sizeof (egress_interface)); - offset += sizeof (egress_interface); - } - /* src mac address */ - { - clib_memcpy (b0->data + offset, src_mac, 6); - offset += 6; - } - /* dst mac address */ - { - clib_memcpy (b0->data + offset, dst_mac, 6); - offset += 6; - } - - /* ethertype */ - b0->data[offset++] = ethertype >> 8; - b0->data[offset++] = ethertype & 0xFF; - - /* Timestamp */ - clib_memcpy (b0->data + offset, ×tamp, sizeof (f64)); - offset += sizeof (f64); - - /* pkt size */ - { - u16 pkt_size = clib_host_to_net_u16 (length); - clib_memcpy (b0->data + offset, &pkt_size, sizeof (pkt_size)); - offset += sizeof (pkt_size); - } - - b0->current_length += - /* 2*sw_if_index + 2*mac + ethertype + timestamp + length = 32 */ - 2 * sizeof (u32) + 12 + sizeof (u16) + sizeof (f64) + sizeof (u16); - - } - /* Time to flush the buffer? */ - if (PREDICT_FALSE - (do_flush || (offset + 2 * sizeof (u32) + 12 + sizeof (u16) + - +sizeof (f64) + sizeof (u16)) > frm->path_mtu)) - { - tp = vlib_buffer_get_current (b0); - ip = (ip4_header_t *) & tp->ip4; - udp = (udp_header_t *) (ip + 1); - h = (ipfix_message_header_t *) (udp + 1); - s = (ipfix_set_header_t *) (h + 1); - - s->set_id_length = ipfix_set_id_length (fm->l2_report_id, - b0->current_length - - (sizeof (*ip) + sizeof (*udp) + - sizeof (*h))); - h->version_length = version_length (b0->current_length - - (sizeof (*ip) + sizeof (*udp))); - - ip->length = clib_host_to_net_u16 (b0->current_length); - - ip->checksum = ip4_header_checksum (ip); - udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip)); - - if (frm->udp_checksum) - { - /* RFC 7011 section 10.3.2. */ - udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip); - if (udp->checksum == 0) - udp->checksum = 0xffff; - } - - ASSERT (ip->checksum == ip4_header_checksum (ip)); - - if (PREDICT_FALSE (vlib_get_trace_count (vm, node) > 0)) - { - vlib_trace_buffer (vm, node, FLOWPERPKT_L2_NEXT_IP4_LOOKUP, b0, - 0 /* follow chain */ ); - flowperpkt_l2_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - memset (t, 0, sizeof (*t)); - t->rx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; - t->tx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX]; - t->buffer_size = b0->current_length; - } - - vlib_put_frame_to_node (vm, ip4_lookup_node.index, - fm->l2_frames_per_worker[my_thread_index]); - fm->l2_frames_per_worker[my_thread_index] = 0; - fm->l2_buffers_per_worker[my_thread_index] = 0; - offset = 0; - } - - fm->l2_next_record_offset_per_worker[my_thread_index] = offset; -} - -void -flowperpkt_flush_callback_l2 (void) -{ - vlib_main_t *vm = vlib_get_main (); - flowperpkt_main_t *fm = &flowperpkt_main; - vlib_node_runtime_t *node; - node = vlib_node_get_runtime (vm, flowperpkt_l2_node.index); - - add_to_flow_record_l2 (vm, node, fm, 0 /* rx_sw_if_index */ , - 0 /* tx_sw_if_index */ , - 0 /* src mac */ , - 0 /* dst mac */ , - 0 /* ethertype */ , - 0ULL /* timestamp */ , - 0 /* length */ , - 1 /* do_flush */ ); -} - - -static uword -flowperpkt_l2_node_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, vlib_frame_t * frame) -{ - u32 n_left_from, *from, *to_next; - flowperpkt_l2_next_t next_index; - flowperpkt_main_t *fm = &flowperpkt_main; - u64 now; - - now = (u64) ((vlib_time_now (vm) - fm->vlib_time_0) * 1e9); - now += fm->nanosecond_time_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 >= 4 && n_left_to_next >= 2) - { - u32 next0 = FLOWPERPKT_L2_NEXT_DROP; - u32 next1 = FLOWPERPKT_L2_NEXT_DROP; - ethernet_header_t *eh0, *eh1; - u16 len0, len1; - u32 bi0, bi1; - vlib_buffer_t *b0, *b1; - - /* Prefetch next iteration. */ - { - vlib_buffer_t *p2, *p3; - - p2 = vlib_get_buffer (vm, from[2]); - p3 = vlib_get_buffer (vm, from[3]); - - vlib_prefetch_buffer_header (p2, LOAD); - vlib_prefetch_buffer_header (p3, LOAD); - - CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE); - CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE); - } - - /* speculatively enqueue b0 and b1 to the current next frame */ - to_next[0] = bi0 = from[0]; - to_next[1] = bi1 = from[1]; - from += 2; - to_next += 2; - n_left_from -= 2; - n_left_to_next -= 2; - - b0 = vlib_get_buffer (vm, bi0); - b1 = vlib_get_buffer (vm, bi1); - - vnet_feature_next (vnet_buffer (b0)->sw_if_index[VLIB_TX], - &next0, b0); - vnet_feature_next (vnet_buffer (b1)->sw_if_index[VLIB_TX], - &next1, b1); - - eh0 = vlib_buffer_get_current (b0); - len0 = vlib_buffer_length_in_chain (vm, b0); - - if (PREDICT_TRUE ((b0->flags & VLIB_BUFFER_FLOW_REPORT) == 0)) - add_to_flow_record_l2 (vm, node, fm, - vnet_buffer (b0)->sw_if_index[VLIB_RX], - vnet_buffer (b0)->sw_if_index[VLIB_TX], - eh0->src_address, - eh0->dst_address, - eh0->type, now, len0, 0 /* flush */ ); - - eh1 = vlib_buffer_get_current (b0); - len1 = vlib_buffer_length_in_chain (vm, b0); - - if (PREDICT_TRUE ((b1->flags & VLIB_BUFFER_FLOW_REPORT) == 0)) - add_to_flow_record_l2 (vm, node, fm, - vnet_buffer (b1)->sw_if_index[VLIB_RX], - vnet_buffer (b1)->sw_if_index[VLIB_TX], - eh1->src_address, - eh1->dst_address, - eh1->type, now, len1, 0 /* flush */ ); - - if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE))) - { - if (b0->flags & VLIB_BUFFER_IS_TRACED) - { - flowperpkt_l2_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->rx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; - t->tx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX]; - clib_memcpy (t->src_mac, eh0->src_address, 6); - clib_memcpy (t->dst_mac, eh0->dst_address, 6); - t->ethertype = clib_net_to_host_u16 (eh0->type); - t->timestamp = now; - t->buffer_size = len0; - } - if (b1->flags & VLIB_BUFFER_IS_TRACED) - { - flowperpkt_l2_trace_t *t = - vlib_add_trace (vm, node, b1, sizeof (*t)); - t->rx_sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX]; - t->tx_sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_TX]; - clib_memcpy (t->src_mac, eh1->src_address, 6); - clib_memcpy (t->dst_mac, eh1->dst_address, 6); - t->ethertype = clib_net_to_host_u16 (eh1->type); - t->timestamp = now; - t->buffer_size = len1; - } - } - - /* verify speculative enqueues, maybe switch current next frame */ - vlib_validate_buffer_enqueue_x2 (vm, node, next_index, - to_next, n_left_to_next, - bi0, bi1, next0, next1); - } - - while (n_left_from > 0 && n_left_to_next > 0) - { - u32 bi0; - vlib_buffer_t *b0; - u32 next0 = FLOWPERPKT_L2_NEXT_DROP; - ethernet_header_t *eh0; - u16 len0; - - /* speculatively enqueue b0 to the current next frame */ - 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); - - vnet_feature_next (vnet_buffer (b0)->sw_if_index[VLIB_TX], - &next0, b0); - - eh0 = vlib_buffer_get_current (b0); - len0 = vlib_buffer_length_in_chain (vm, b0); - - if (PREDICT_TRUE ((b0->flags & VLIB_BUFFER_FLOW_REPORT) == 0)) - add_to_flow_record_l2 (vm, node, fm, - vnet_buffer (b0)->sw_if_index[VLIB_RX], - vnet_buffer (b0)->sw_if_index[VLIB_TX], - eh0->src_address, - eh0->dst_address, - eh0->type, now, len0, 0 /* flush */ ); - - if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - flowperpkt_l2_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->rx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; - t->tx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX]; - clib_memcpy (t->src_mac, eh0->src_address, 6); - clib_memcpy (t->dst_mac, eh0->dst_address, 6); - t->ethertype = clib_net_to_host_u16 (eh0->type); - t->timestamp = now; - t->buffer_size = len0; - } - - /* verify speculative enqueue, maybe switch current next frame */ - 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); - } - return frame->n_vectors; -} - -/** - * @brief IPFIX l2 flow-per-packet graph node - * @node flowperpkt-l2 - * - * This is the IPFIX flow-record-per-packet node. - * - * @param vm vlib_main_t corresponding to the current thread. - * @param node vlib_node_runtime_t data for this node. - * @param frame vlib_frame_t whose contents should be dispatched. - * - * @par Graph mechanics: buffer metadata, next index usage - * - * Uses: - * - vnet_buffer(b)->ip.save_rewrite_length - * - tells the node the length of the rewrite which was applied in - * ip4/6_rewrite_inline, allows the code to find the IP header without - * having to parse L2 headers, or make stupid assumptions about their - * length. - * - vnet_buffer(b)->flags & VLIB_BUFFER_FLOW_REPORT - * - Used to suppress flow record generation for flow record packets. - * - * Sets: - * - vnet_buffer(b)->flags & VLIB_BUFFER_FLOW_REPORT - * - To suppress flow record generation for flow record packets - * - * Next Index: - * - Next configured output feature on the interface, usually - * "interface-output." Generated flow records head for ip4-lookup - */ - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (flowperpkt_l2_node) = { - .function = flowperpkt_l2_node_fn, - .name = "flowperpkt-l2", - .vector_size = sizeof (u32), - .format_trace = format_flowperpkt_l2_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - - .n_errors = ARRAY_LEN(flowperpkt_l2_error_strings), - .error_strings = flowperpkt_l2_error_strings, - - .n_next_nodes = FLOWPERPKT_L2_N_NEXT, - - /* edit / add dispositions here */ - .next_nodes = { - [FLOWPERPKT_L2_NEXT_DROP] = "error-drop", - [FLOWPERPKT_L2_NEXT_IP4_LOOKUP] = "ip4-lookup", - }, -}; -/* *INDENT-ON* */ - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/flowperpkt/node.c b/src/plugins/flowperpkt/node.c deleted file mode 100644 index 9bac4166..00000000 --- a/src/plugins/flowperpkt/node.c +++ /dev/null @@ -1,574 +0,0 @@ -/* - * node.c - ipv4 ipfix-per-packet graph node - * - * Copyright (c) - * 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 -#include -#include -#include -#include - -/** - * @file ipv4 flow record generator graph node - */ - -typedef struct -{ - /** interface handle */ - u32 rx_sw_if_index; - u32 tx_sw_if_index; - u32 src_address; - u32 dst_address; - /** ToS bits */ - u8 tos; - /** packet timestamp */ - u64 timestamp; - /** size of the buffer */ - u16 buffer_size; -} flowperpkt_ipv4_trace_t; - -/* packet trace format function */ -static u8 * -format_flowperpkt_ipv4_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 *); - flowperpkt_ipv4_trace_t *t = va_arg (*args, flowperpkt_ipv4_trace_t *); - - s = format (s, - "FLOWPERPKT-V4: rx_sw_if_index %d, tx_sw_if_index %d, src %U dst %U tos %0x2, timestamp %lld, size %d", - t->rx_sw_if_index, t->tx_sw_if_index, - format_ip4_address, &t->src_address, - format_ip4_address, &t->dst_address, - t->tos, t->timestamp, t->buffer_size); - return s; -} - -vlib_node_registration_t flowperpkt_ipv4_node; - -/* No counters at the moment */ -#define foreach_flowperpkt_ipv4_error - -typedef enum -{ -#define _(sym,str) FLOWPERPKT_ERROR_##sym, - foreach_flowperpkt_ipv4_error -#undef _ - FLOWPERPKT_N_ERROR, -} flowperpkt_ipv4_error_t; - -static char *flowperpkt_ipv4_error_strings[] = { -#define _(sym,string) string, - foreach_flowperpkt_ipv4_error -#undef _ -}; - -typedef enum -{ - FLOWPERPKT_IPV4_NEXT_DROP, - FLOWPERPKT_IPV4_NEXT_LOOKUP, - FLOWPERPKT_IPV4_N_NEXT, -} flowperpkt_ipv4_next_t; - -/** - * @brief add an entry to the flow record under construction - * @param vm vlib_main_t * current worker thread main structure pointer - * @param fm flowperpkt_main_t * flow-per-packet main structure pointer - * @param sw_if_index u32 interface handle - * @param tos u8 ToS bits from the packet - * @param timestamp u64 timestamp, nanoseconds since 1/1/70 - * @param length u16 ip length of the packet - * @param do_flush int 1 = flush all cached records, 0 = construct a record - */ - -static inline void -add_to_flow_record_ipv4 (vlib_main_t * vm, - vlib_node_runtime_t * node, - flowperpkt_main_t * fm, - u32 rx_sw_if_index, u32 tx_sw_if_index, - u32 src_address, u32 dst_address, - u8 tos, u64 timestamp, u16 length, int do_flush) -{ - u32 my_thread_index = vm->thread_index; - flow_report_main_t *frm = &flow_report_main; - ip4_header_t *ip; - udp_header_t *udp; - ip4_ipfix_template_packet_t *tp; - ipfix_message_header_t *h; - ipfix_set_header_t *s; - vlib_frame_t *f; - vlib_buffer_t *b0; - u16 offset; - u32 bi0; - vlib_buffer_free_list_t *fl; - - /* Find or allocate a buffer */ - b0 = fm->ipv4_buffers_per_worker[my_thread_index]; - - /* Need to allocate a buffer? */ - if (PREDICT_FALSE (b0 == 0)) - { - /* Nothing to flush */ - if (do_flush) - return; - - /* $$$$ drop counter? */ - if (vlib_buffer_alloc (vm, &bi0, 1) != 1) - return; - - /* Initialize the buffer */ - b0 = fm->ipv4_buffers_per_worker[my_thread_index] = - vlib_get_buffer (vm, bi0); - fl = - vlib_buffer_get_free_list (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX); - vlib_buffer_init_for_free_list (b0, fl); - VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0); - offset = 0; - } - else - { - /* use the current buffer */ - bi0 = vlib_get_buffer_index (vm, b0); - offset = fm->ipv4_next_record_offset_per_worker[my_thread_index]; - } - - /* Find or allocate a frame */ - f = fm->ipv4_frames_per_worker[my_thread_index]; - if (PREDICT_FALSE (f == 0)) - { - u32 *to_next; - f = vlib_get_frame_to_node (vm, ip4_lookup_node.index); - fm->ipv4_frames_per_worker[my_thread_index] = f; - - /* Enqueue the buffer */ - to_next = vlib_frame_vector_args (f); - to_next[0] = bi0; - f->n_vectors = 1; - } - - /* Fresh packet, construct header */ - if (PREDICT_FALSE (offset == 0)) - { - flow_report_stream_t *stream; - - stream = &frm->streams[0]; - - b0->current_data = 0; - b0->current_length = sizeof (*ip) + sizeof (*udp) + sizeof (*h) + - sizeof (*s); - b0->flags |= (VLIB_BUFFER_TOTAL_LENGTH_VALID | VLIB_BUFFER_FLOW_REPORT); - vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0; - vnet_buffer (b0)->sw_if_index[VLIB_TX] = frm->fib_index; - - tp = vlib_buffer_get_current (b0); - ip = (ip4_header_t *) & tp->ip4; - udp = (udp_header_t *) (ip + 1); - h = (ipfix_message_header_t *) (udp + 1); - s = (ipfix_set_header_t *) (h + 1); - - ip->ip_version_and_header_length = 0x45; - ip->ttl = 254; - ip->protocol = IP_PROTOCOL_UDP; - ip->flags_and_fragment_offset = 0; - ip->src_address.as_u32 = frm->src_address.as_u32; - ip->dst_address.as_u32 = frm->ipfix_collector.as_u32; - udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_ipfix); - udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_ipfix); - udp->checksum = 0; - - /* FIXUP: message header export_time */ - h->export_time = (u32) - (((f64) frm->unix_time_0) + - (vlib_time_now (frm->vlib_main) - frm->vlib_time_0)); - h->export_time = clib_host_to_net_u32 (h->export_time); - h->domain_id = clib_host_to_net_u32 (stream->domain_id); - - /* FIXUP: message header sequence_number */ - h->sequence_number = stream->sequence_number++; - h->sequence_number = clib_host_to_net_u32 (h->sequence_number); - - offset = (u32) (((u8 *) (s + 1)) - (u8 *) tp); - } - - /* Add data, unless we're flushing stale data */ - if (PREDICT_TRUE (do_flush == 0)) - { - - /* Add data */ - /* Ingress interface */ - { - u32 ingress_interface = clib_host_to_net_u32 (rx_sw_if_index); - clib_memcpy (b0->data + offset, &ingress_interface, - sizeof (ingress_interface)); - offset += sizeof (ingress_interface); - } - /* Egress interface */ - { - u32 egress_interface = clib_host_to_net_u32 (tx_sw_if_index); - clib_memcpy (b0->data + offset, &egress_interface, - sizeof (egress_interface)); - offset += sizeof (egress_interface); - } - /* ip4 src address */ - { - clib_memcpy (b0->data + offset, &src_address, sizeof (src_address)); - offset += sizeof (src_address); - } - /* ip4 dst address */ - { - clib_memcpy (b0->data + offset, &dst_address, sizeof (dst_address)); - offset += sizeof (dst_address); - } - - /* ToS */ - b0->data[offset++] = tos; - - /* Timestamp */ - clib_memcpy (b0->data + offset, ×tamp, sizeof (f64)); - offset += sizeof (f64); - - /* pkt size */ - { - u16 pkt_size = clib_host_to_net_u16 (length); - clib_memcpy (b0->data + offset, &pkt_size, sizeof (pkt_size)); - offset += sizeof (pkt_size); - } - - b0->current_length += - /* sw_if_index + tos + timestamp + length = 15 */ - 4 * sizeof (u32) + sizeof (u8) + sizeof (f64) + sizeof (u16); - - } - /* Time to flush the buffer? */ - if (PREDICT_FALSE - (do_flush || (offset + 4 * sizeof (u32) + sizeof (u8) - + sizeof (f64) + sizeof (u16)) > frm->path_mtu)) - { - tp = vlib_buffer_get_current (b0); - ip = (ip4_header_t *) & tp->ip4; - udp = (udp_header_t *) (ip + 1); - h = (ipfix_message_header_t *) (udp + 1); - s = (ipfix_set_header_t *) (h + 1); - - s->set_id_length = ipfix_set_id_length (fm->ipv4_report_id, - b0->current_length - - (sizeof (*ip) + sizeof (*udp) + - sizeof (*h))); - h->version_length = version_length (b0->current_length - - (sizeof (*ip) + sizeof (*udp))); - - ip->length = clib_host_to_net_u16 (b0->current_length); - - ip->checksum = ip4_header_checksum (ip); - udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip)); - - if (frm->udp_checksum) - { - /* RFC 7011 section 10.3.2. */ - udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip); - if (udp->checksum == 0) - udp->checksum = 0xffff; - } - - ASSERT (ip->checksum == ip4_header_checksum (ip)); - - if (PREDICT_FALSE (vlib_get_trace_count (vm, node) > 0)) - { - vlib_trace_buffer (vm, node, FLOWPERPKT_IPV4_NEXT_LOOKUP, b0, - 0 /* follow chain */ ); - flowperpkt_ipv4_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->rx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; - t->tx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX]; - t->src_address = 0; - t->dst_address = 0; - t->tos = 0; - t->timestamp = 0; - t->buffer_size = b0->current_length; - } - - vlib_put_frame_to_node (vm, ip4_lookup_node.index, - fm->ipv4_frames_per_worker[my_thread_index]); - fm->ipv4_frames_per_worker[my_thread_index] = 0; - fm->ipv4_buffers_per_worker[my_thread_index] = 0; - offset = 0; - } - - fm->ipv4_next_record_offset_per_worker[my_thread_index] = offset; -} - -void -flowperpkt_flush_callback_ipv4 (void) -{ - vlib_main_t *vm = vlib_get_main (); - flowperpkt_main_t *fm = &flowperpkt_main; - vlib_node_runtime_t *node; - node = vlib_node_get_runtime (vm, flowperpkt_ipv4_node.index); - - add_to_flow_record_ipv4 (vm, node, fm, 0 /* rx_sw_if_index */ , - 0 /* tx_sw_if_index */ , - 0 /* src_address */ , - 0 /* dst_address */ , - 0 /* ToS */ , - 0ULL /* timestamp */ , - 0 /* length */ , - 1 /* do_flush */ ); -} - - -static uword -flowperpkt_ipv4_node_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, vlib_frame_t * frame) -{ - u32 n_left_from, *from, *to_next; - flowperpkt_ipv4_next_t next_index; - flowperpkt_main_t *fm = &flowperpkt_main; - u64 now; - - now = (u64) ((vlib_time_now (vm) - fm->vlib_time_0) * 1e9); - now += fm->nanosecond_time_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 >= 4 && n_left_to_next >= 2) - { - u32 next0 = FLOWPERPKT_IPV4_NEXT_DROP; - u32 next1 = FLOWPERPKT_IPV4_NEXT_DROP; - ip4_header_t *ip0, *ip1; - u16 len0, len1; - u32 bi0, bi1; - vlib_buffer_t *b0, *b1; - - /* Prefetch next iteration. */ - { - vlib_buffer_t *p2, *p3; - - p2 = vlib_get_buffer (vm, from[2]); - p3 = vlib_get_buffer (vm, from[3]); - - vlib_prefetch_buffer_header (p2, LOAD); - vlib_prefetch_buffer_header (p3, LOAD); - - CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE); - CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE); - } - - /* speculatively enqueue b0 and b1 to the current next frame */ - to_next[0] = bi0 = from[0]; - to_next[1] = bi1 = from[1]; - from += 2; - to_next += 2; - n_left_from -= 2; - n_left_to_next -= 2; - - b0 = vlib_get_buffer (vm, bi0); - b1 = vlib_get_buffer (vm, bi1); - - vnet_feature_next (vnet_buffer (b0)->sw_if_index[VLIB_TX], - &next0, b0); - vnet_feature_next (vnet_buffer (b1)->sw_if_index[VLIB_TX], - &next1, b1); - - ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0) + - vnet_buffer (b0)->ip.save_rewrite_length); - - len0 = vlib_buffer_length_in_chain (vm, b0); - - if (PREDICT_TRUE ((b0->flags & VLIB_BUFFER_FLOW_REPORT) == 0)) - add_to_flow_record_ipv4 (vm, node, fm, - vnet_buffer (b0)->sw_if_index[VLIB_RX], - vnet_buffer (b0)->sw_if_index[VLIB_TX], - ip0->src_address.as_u32, - ip0->dst_address.as_u32, - ip0->tos, now, len0, 0 /* flush */ ); - - ip1 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b1) + - vnet_buffer (b1)->ip.save_rewrite_length); - len1 = vlib_buffer_length_in_chain (vm, b1); - - if (PREDICT_TRUE ((b1->flags & VLIB_BUFFER_FLOW_REPORT) == 0)) - add_to_flow_record_ipv4 (vm, node, fm, - vnet_buffer (b1)->sw_if_index[VLIB_RX], - vnet_buffer (b1)->sw_if_index[VLIB_TX], - ip1->src_address.as_u32, - ip1->dst_address.as_u32, - ip1->tos, now, len1, 0 /* flush */ ); - - if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE))) - { - if (b0->flags & VLIB_BUFFER_IS_TRACED) - { - flowperpkt_ipv4_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->rx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; - t->tx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX]; - t->src_address = ip0->src_address.as_u32; - t->dst_address = ip0->dst_address.as_u32; - t->tos = ip0->tos; - t->timestamp = now; - t->buffer_size = len0; - } - if (b1->flags & VLIB_BUFFER_IS_TRACED) - { - flowperpkt_ipv4_trace_t *t = - vlib_add_trace (vm, node, b1, sizeof (*t)); - t->rx_sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX]; - t->tx_sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_TX]; - t->src_address = ip1->src_address.as_u32; - t->dst_address = ip1->dst_address.as_u32; - t->tos = ip1->tos; - t->timestamp = now; - t->buffer_size = len1; - } - } - - /* verify speculative enqueues, maybe switch current next frame */ - vlib_validate_buffer_enqueue_x2 (vm, node, next_index, - to_next, n_left_to_next, - bi0, bi1, next0, next1); - } - - while (n_left_from > 0 && n_left_to_next > 0) - { - u32 bi0; - vlib_buffer_t *b0; - u32 next0 = FLOWPERPKT_IPV4_NEXT_DROP; - ip4_header_t *ip0; - u16 len0; - - /* speculatively enqueue b0 to the current next frame */ - 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); - - vnet_feature_next (vnet_buffer (b0)->sw_if_index[VLIB_TX], - &next0, b0); - - ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0) + - vnet_buffer (b0)->ip.save_rewrite_length); - /* - * egressInterface, TLV type 14, u32 - * ipClassOfService, TLV type 5, u8 - * flowStartNanoseconds, TLV type 156, dateTimeNanoseconds (f64) - * Implementation: f64 nanoseconds since VPP started - * dataLinkFrameSize, TLV type 312, u16 - */ - len0 = vlib_buffer_length_in_chain (vm, b0); - - if (PREDICT_TRUE ((b0->flags & VLIB_BUFFER_FLOW_REPORT) == 0)) - add_to_flow_record_ipv4 (vm, node, fm, - vnet_buffer (b0)->sw_if_index[VLIB_RX], - vnet_buffer (b0)->sw_if_index[VLIB_TX], - ip0->src_address.as_u32, - ip0->dst_address.as_u32, - ip0->tos, now, len0, 0 /* flush */ ); - - if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - flowperpkt_ipv4_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->rx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; - t->tx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX]; - t->src_address = ip0->src_address.as_u32; - t->dst_address = ip0->dst_address.as_u32; - t->tos = ip0->tos; - t->timestamp = now; - t->buffer_size = len0; - } - - /* verify speculative enqueue, maybe switch current next frame */ - 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); - } - return frame->n_vectors; -} - -/** - * @brief IPFIX ipv4 flow-per-packet graph node - * @node flowperpkt-ipv4 - * - * This is the IPFIX flow-record-per-packet node. - * - * @param vm vlib_main_t corresponding to the current thread. - * @param node vlib_node_runtime_t data for this node. - * @param frame vlib_frame_t whose contents should be dispatched. - * - * @par Graph mechanics: buffer metadata, next index usage - * - * Uses: - * - vnet_buffer(b)->ip.save_rewrite_length - * - tells the node the length of the rewrite which was applied in - * ip4/6_rewrite_inline, allows the code to find the IP header without - * having to parse L2 headers, or make stupid assumptions about their - * length. - * - vnet_buffer(b)->flags & VLIB_BUFFER_FLOW_REPORT - * - Used to suppress flow record generation for flow record packets. - * - * Sets: - * - vnet_buffer(b)->flags & VLIB_BUFFER_FLOW_REPORT - * - To suppress flow record generation for flow record packets - * - * Next Index: - * - Next configured output feature on the interface, usually - * "interface-output." Generated flow records head for ip4-lookup - */ - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (flowperpkt_ipv4_node) = { - .function = flowperpkt_ipv4_node_fn, - .name = "flowperpkt-ipv4", - .vector_size = sizeof (u32), - .format_trace = format_flowperpkt_ipv4_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - - .n_errors = ARRAY_LEN(flowperpkt_ipv4_error_strings), - .error_strings = flowperpkt_ipv4_error_strings, - - .n_next_nodes = FLOWPERPKT_IPV4_N_NEXT, - - /* edit / add dispositions here */ - .next_nodes = { - [FLOWPERPKT_IPV4_NEXT_DROP] = "error-drop", - /* Used only to trace ipfix data packets */ - [FLOWPERPKT_IPV4_NEXT_LOOKUP] = "ip4-lookup", - }, -}; -/* *INDENT-ON* */ - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/flowprobe.am b/src/plugins/flowprobe.am new file mode 100644 index 00000000..c56e246d --- /dev/null +++ b/src/plugins/flowprobe.am @@ -0,0 +1,37 @@ + +# Copyright (c) +# 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. + +vppplugins_LTLIBRARIES += flowprobe_plugin.la +vppapitestplugins_LTLIBRARIES += flowprobe_test_plugin.la + +flowprobe_plugin_la_SOURCES = flowprobe/flowprobe.c \ + flowprobe/node.c \ + flowprobe/flowprobe_plugin.api.h + +BUILT_SOURCES += \ + flowprobe/flowprobe.api.h \ + flowprobe/flowprobe.api.json + +noinst_HEADERS += \ + flowprobe/flowprobe_all_api_h.h \ + flowprobe/flowprobe_msg_enum.h \ + flowprobe/flowprobe.api.h + +flowprobe_test_plugin_la_SOURCES = \ + flowprobe/flowprobe_test.c \ + flowprobe/flowprobe_plugin.api.h + +API_FILES += flowprobe/flowprobe.api + +# vi:syntax=automake diff --git a/src/plugins/flowprobe/flowprobe.api b/src/plugins/flowprobe/flowprobe.api new file mode 100644 index 00000000..3f8c583b --- /dev/null +++ b/src/plugins/flowprobe/flowprobe.api @@ -0,0 +1,40 @@ +/* Define a simple enable-disable binary API to control the feature */ + +/** \file + This file defines the vpp control-plane API messages + used to control the flowprobe plugin +*/ + +/** \brief Enable / disable per-packet IPFIX recording on an interface + @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 - if non-zero the address is ipv6, else ipv4 + @param sw_if_index - index of the interface +*/ +autoreply manual_print define flowprobe_tx_interface_add_del +{ + /* Client identifier, set from api_main.my_client_index */ + u32 client_index; + + /* Arbitrary context, so client can match reply to request */ + u32 context; + + /* Enable / disable the feature */ + u8 is_add; + u8 which; /* 0 = ipv4, 1 = l2, 2 = ipv6 */ + + /* Interface handle */ + u32 sw_if_index; +}; + +autoreply define flowprobe_params +{ + u32 client_index; + u32 context; + u8 record_l2; + u8 record_l3; + u8 record_l4; + u32 active_timer; /* ~0 is off, 0 is default */ + u32 passive_timer; /* ~0 is off, 0 is default */ +}; diff --git a/src/plugins/flowprobe/flowprobe.c b/src/plugins/flowprobe/flowprobe.c new file mode 100644 index 00000000..8975f89c --- /dev/null +++ b/src/plugins/flowprobe/flowprobe.c @@ -0,0 +1,1122 @@ +/* + * flowprobe.c - ipfix probe plugin + * + * 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. + */ + +/** + * @file + * @brief Per-packet IPFIX flow record generator plugin + * + * This file implements vpp plugin registration mechanics, + * debug CLI, and binary API handling. + */ + +#include +#include +#include +#include + +#include +#include +#include + +/* define message IDs */ +#include + +/* define message structures */ +#define vl_typedefs +#include +#undef vl_typedefs + +/* define generated endian-swappers */ +#define vl_endianfun +#include +#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 +#undef vl_printfun + +flowprobe_main_t flowprobe_main; +vlib_node_registration_t flowprobe_walker_node; +static vlib_node_registration_t flowprobe_timer_node; +uword flowprobe_walker_process (vlib_main_t * vm, vlib_node_runtime_t * rt, + vlib_frame_t * f); + +/* Get the API version number */ +#define vl_api_version(n,v) static u32 api_version=(v); +#include +#undef vl_api_version + +#define REPLY_MSG_ID_BASE fm->msg_id_base +#include + +/* Define the per-interface configurable features */ +/* *INDENT-OFF* */ +VNET_FEATURE_INIT (flow_perpacket_ip4, static) = +{ + .arc_name = "ip4-output", + .node_name = "flowprobe-ip4", + .runs_before = VNET_FEATURES ("interface-output"), +}; + +VNET_FEATURE_INIT (flow_perpacket_ip6, static) = +{ + .arc_name = "ip6-output", + .node_name = "flowprobe-ip6", + .runs_before = VNET_FEATURES ("interface-output"), +}; + +VNET_FEATURE_INIT (flow_perpacket_l2, static) = +{ + .arc_name = "interface-output", + .node_name = "flowprobe-l2", + .runs_before = VNET_FEATURES ("interface-tx"), +}; +/* *INDENT-ON* */ + +/* Macro to finish up custom dump fns */ +#define FINISH \ + vec_add1 (s, 0); \ + vl_print (handle, (char *)s); \ + vec_free (s); \ + return handle; + +static inline ipfix_field_specifier_t * +flowprobe_template_ip4_fields (ipfix_field_specifier_t * f) +{ +#define flowprobe_template_ip4_field_count() 4 + /* sourceIpv4Address, TLV type 8, u32 */ + f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , + sourceIPv4Address, 4); + f++; + /* destinationIPv4Address, TLV type 12, u32 */ + f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , + destinationIPv4Address, 4); + f++; + /* protocolIdentifier, TLV type 4, u8 */ + f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , + protocolIdentifier, 1); + f++; + /* octetDeltaCount, TLV type 1, u64 */ + f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , + octetDeltaCount, 8); + f++; + return f; +} + +static inline ipfix_field_specifier_t * +flowprobe_template_ip6_fields (ipfix_field_specifier_t * f) +{ +#define flowprobe_template_ip6_field_count() 4 + /* sourceIpv6Address, TLV type 27, 16 octets */ + f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , + sourceIPv6Address, 16); + f++; + /* destinationIPv6Address, TLV type 28, 16 octets */ + f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , + destinationIPv6Address, 16); + f++; + /* protocolIdentifier, TLV type 4, u8 */ + f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , + protocolIdentifier, 1); + f++; + /* octetDeltaCount, TLV type 1, u64 */ + f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , + octetDeltaCount, 8); + f++; + return f; +} + +static inline ipfix_field_specifier_t * +flowprobe_template_l2_fields (ipfix_field_specifier_t * f) +{ +#define flowprobe_template_l2_field_count() 3 + /* sourceMacAddress, TLV type 56, u8[6] we hope */ + f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , + sourceMacAddress, 6); + f++; + /* destinationMacAddress, TLV type 80, u8[6] we hope */ + f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , + destinationMacAddress, 6); + f++; + /* ethernetType, TLV type 256, u16 */ + f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , + ethernetType, 2); + f++; + return f; +} + +static inline ipfix_field_specifier_t * +flowprobe_template_common_fields (ipfix_field_specifier_t * f) +{ +#define flowprobe_template_common_field_count() 3 + /* ingressInterface, TLV type 10, u32 */ + f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , + ingressInterface, 4); + f++; + + /* egressInterface, TLV type 14, u32 */ + f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , + egressInterface, 4); + f++; + + /* packetDeltaCount, TLV type 2, u64 */ + f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , + packetDeltaCount, 8); + f++; + + return f; +} + +static inline ipfix_field_specifier_t * +flowprobe_template_l4_fields (ipfix_field_specifier_t * f) +{ +#define flowprobe_template_l4_field_count() 2 + /* sourceTransportPort, TLV type 7, u16 */ + f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , + sourceTransportPort, 2); + f++; + /* destinationTransportPort, TLV type 11, u16 */ + f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , + destinationTransportPort, 2); + f++; + return f; +} + +/** + * @brief Create an IPFIX template packet rewrite string + * @param frm flow_report_main_t * + * @param fr flow_report_t * + * @param collector_address ip4_address_t * the IPFIX collector address + * @param src_address ip4_address_t * the source address we should use + * @param collector_port u16 the collector port we should use, host byte order + * @returns u8 * vector containing the indicated IPFIX template packet + */ +static inline u8 * +flowprobe_template_rewrite_inline (flow_report_main_t * frm, + flow_report_t * fr, + ip4_address_t * collector_address, + ip4_address_t * src_address, + u16 collector_port, + flowprobe_variant_t which) +{ + ip4_header_t *ip; + udp_header_t *udp; + ipfix_message_header_t *h; + ipfix_set_header_t *s; + ipfix_template_header_t *t; + ipfix_field_specifier_t *f; + ipfix_field_specifier_t *first_field; + u8 *rewrite = 0; + ip4_ipfix_template_packet_t *tp; + u32 field_count = 0; + flow_report_stream_t *stream; + flowprobe_main_t *fm = &flowprobe_main; + flowprobe_record_t flags = fr->opaque.as_uword; + bool collect_ip4 = false, collect_ip6 = false; + + stream = &frm->streams[fr->stream_index]; + + if (flags & FLOW_RECORD_L3) + { + collect_ip4 = which == FLOW_VARIANT_L2_IP4 || which == FLOW_VARIANT_IP4; + collect_ip6 = which == FLOW_VARIANT_L2_IP6 || which == FLOW_VARIANT_IP6; + if (which == FLOW_VARIANT_L2_IP4) + flags |= FLOW_RECORD_L2_IP4; + if (which == FLOW_VARIANT_L2_IP6) + flags |= FLOW_RECORD_L2_IP6; + } + + field_count += flowprobe_template_common_field_count (); + if (flags & FLOW_RECORD_L2) + field_count += flowprobe_template_l2_field_count (); + if (collect_ip4) + field_count += flowprobe_template_ip4_field_count (); + if (collect_ip6) + field_count += flowprobe_template_ip6_field_count (); + if (flags & FLOW_RECORD_L4) + field_count += flowprobe_template_l4_field_count (); + + /* allocate rewrite space */ + vec_validate_aligned + (rewrite, sizeof (ip4_ipfix_template_packet_t) + + field_count * sizeof (ipfix_field_specifier_t) - 1, + CLIB_CACHE_LINE_BYTES); + + tp = (ip4_ipfix_template_packet_t *) rewrite; + ip = (ip4_header_t *) & tp->ip4; + udp = (udp_header_t *) (ip + 1); + h = (ipfix_message_header_t *) (udp + 1); + s = (ipfix_set_header_t *) (h + 1); + t = (ipfix_template_header_t *) (s + 1); + first_field = f = (ipfix_field_specifier_t *) (t + 1); + + ip->ip_version_and_header_length = 0x45; + ip->ttl = 254; + ip->protocol = IP_PROTOCOL_UDP; + ip->src_address.as_u32 = src_address->as_u32; + ip->dst_address.as_u32 = collector_address->as_u32; + udp->src_port = clib_host_to_net_u16 (stream->src_port); + udp->dst_port = clib_host_to_net_u16 (collector_port); + udp->length = clib_host_to_net_u16 (vec_len (rewrite) - sizeof (*ip)); + + /* FIXUP: message header export_time */ + /* FIXUP: message header sequence_number */ + h->domain_id = clib_host_to_net_u32 (stream->domain_id); + + /* Add TLVs to the template */ + f = flowprobe_template_common_fields (f); + + if (flags & FLOW_RECORD_L2) + f = flowprobe_template_l2_fields (f); + if (collect_ip4) + f = flowprobe_template_ip4_fields (f); + if (collect_ip6) + f = flowprobe_template_ip6_fields (f); + if (flags & FLOW_RECORD_L4) + f = flowprobe_template_l4_fields (f); + + /* Back to the template packet... */ + ip = (ip4_header_t *) & tp->ip4; + udp = (udp_header_t *) (ip + 1); + + ASSERT (f - first_field); + /* Field count in this template */ + t->id_count = ipfix_id_count (fr->template_id, f - first_field); + + fm->template_size[flags] = (u8 *) f - (u8 *) s; + + /* set length in octets */ + s->set_id_length = + ipfix_set_id_length (2 /* set_id */ , (u8 *) f - (u8 *) s); + + /* message length in octets */ + h->version_length = version_length ((u8 *) f - (u8 *) h); + + ip->length = clib_host_to_net_u16 ((u8 *) f - (u8 *) ip); + ip->checksum = ip4_header_checksum (ip); + + return rewrite; +} + +static u8 * +flowprobe_template_rewrite_ip6 (flow_report_main_t * frm, + flow_report_t * fr, + ip4_address_t * collector_address, + ip4_address_t * src_address, + u16 collector_port) +{ + return flowprobe_template_rewrite_inline + (frm, fr, collector_address, src_address, collector_port, + FLOW_VARIANT_IP6); +} + +static u8 * +flowprobe_template_rewrite_ip4 (flow_report_main_t * frm, + flow_report_t * fr, + ip4_address_t * collector_address, + ip4_address_t * src_address, + u16 collector_port) +{ + return flowprobe_template_rewrite_inline + (frm, fr, collector_address, src_address, collector_port, + FLOW_VARIANT_IP4); +} + +static u8 * +flowprobe_template_rewrite_l2 (flow_report_main_t * frm, + flow_report_t * fr, + ip4_address_t * collector_address, + ip4_address_t * src_address, + u16 collector_port) +{ + return flowprobe_template_rewrite_inline + (frm, fr, collector_address, src_address, collector_port, + FLOW_VARIANT_L2); +} + +static u8 * +flowprobe_template_rewrite_l2_ip4 (flow_report_main_t * frm, + flow_report_t * fr, + ip4_address_t * collector_address, + ip4_address_t * src_address, + u16 collector_port) +{ + return flowprobe_template_rewrite_inline + (frm, fr, collector_address, src_address, collector_port, + FLOW_VARIANT_L2_IP4); +} + +static u8 * +flowprobe_template_rewrite_l2_ip6 (flow_report_main_t * frm, + flow_report_t * fr, + ip4_address_t * collector_address, + ip4_address_t * src_address, + u16 collector_port) +{ + return flowprobe_template_rewrite_inline + (frm, fr, collector_address, src_address, collector_port, + FLOW_VARIANT_L2_IP6); +} + +/** + * @brief Flush accumulated data + * @param frm flow_report_main_t * + * @param fr flow_report_t * + * @param f vlib_frame_t * + * + * Notes: + * This function must simply return the incoming frame, or no template packets + * will be sent. + */ +vlib_frame_t * +flowprobe_data_callback_ip4 (flow_report_main_t * frm, + flow_report_t * fr, + vlib_frame_t * f, u32 * to_next, u32 node_index) +{ + flowprobe_flush_callback_ip4 (); + return f; +} + +vlib_frame_t * +flowprobe_data_callback_ip6 (flow_report_main_t * frm, + flow_report_t * fr, + vlib_frame_t * f, u32 * to_next, u32 node_index) +{ + flowprobe_flush_callback_ip6 (); + return f; +} + +vlib_frame_t * +flowprobe_data_callback_l2 (flow_report_main_t * frm, + flow_report_t * fr, + vlib_frame_t * f, u32 * to_next, u32 node_index) +{ + flowprobe_flush_callback_l2 (); + return f; +} + +static int +flowprobe_template_add_del (u32 domain_id, u16 src_port, + flowprobe_record_t flags, + vnet_flow_data_callback_t * flow_data_callback, + vnet_flow_rewrite_callback_t * rewrite_callback, + bool is_add, u16 * template_id) +{ + flow_report_main_t *frm = &flow_report_main; + vnet_flow_report_add_del_args_t a = { + .rewrite_callback = rewrite_callback, + .flow_data_callback = flow_data_callback, + .is_add = is_add, + .domain_id = domain_id, + .src_port = src_port, + .opaque.as_uword = flags, + }; + return vnet_flow_report_add_del (frm, &a, template_id); +} + +static void +flowprobe_expired_timer_callback (u32 * expired_timers) +{ + vlib_main_t *vm = vlib_get_main (); + flowprobe_main_t *fm = &flowprobe_main; + u32 my_cpu_number = vm->thread_index; + int i; + u32 poolindex; + + for (i = 0; i < vec_len (expired_timers); i++) + { + poolindex = expired_timers[i] & 0x7FFFFFFF; + vec_add1 (fm->expired_passive_per_worker[my_cpu_number], poolindex); + } +} + +static clib_error_t * +flowprobe_create_state_tables (u32 active_timer) +{ + flowprobe_main_t *fm = &flowprobe_main; + vlib_thread_main_t *tm = &vlib_thread_main; + vlib_main_t *vm = vlib_get_main (); + clib_error_t *error = 0; + u32 num_threads; + int i; + + /* Decide how many worker threads we have */ + num_threads = 1 /* main thread */ + tm->n_threads; + + /* Hash table per worker */ + fm->ht_log2len = FLOWPROBE_LOG2_HASHSIZE; + + /* Init per worker flow state and timer wheels */ + if (active_timer) + { + vec_validate (fm->timers_per_worker, num_threads - 1); + vec_validate (fm->expired_passive_per_worker, num_threads - 1); + vec_validate (fm->hash_per_worker, num_threads - 1); + vec_validate (fm->pool_per_worker, num_threads - 1); + + for (i = 0; i < num_threads; i++) + { + int j; + pool_alloc (fm->pool_per_worker[i], 1 << fm->ht_log2len); + vec_resize (fm->hash_per_worker[i], 1 << fm->ht_log2len); + for (j = 0; j < (1 << fm->ht_log2len); j++) + fm->hash_per_worker[i][j] = ~0; + fm->timers_per_worker[i] = + clib_mem_alloc (sizeof (TWT (tw_timer_wheel))); + tw_timer_wheel_init_2t_1w_2048sl (fm->timers_per_worker[i], + flowprobe_expired_timer_callback, + 1.0, 1024); + } + fm->disabled = true; + } + else + { + f64 now = vlib_time_now (vm); + vec_validate (fm->stateless_entry, num_threads - 1); + for (i = 0; i < num_threads; i++) + fm->stateless_entry[i].last_exported = now; + fm->disabled = false; + } + fm->initialized = true; + return error; +} + +static int +validate_feature_on_interface (flowprobe_main_t * fm, u32 sw_if_index, + u8 which) +{ + vec_validate_init_empty (fm->flow_per_interface, sw_if_index, ~0); + + if (fm->flow_per_interface[sw_if_index] == (u8) ~ 0) + return -1; + else if (fm->flow_per_interface[sw_if_index] != which) + return 0; + else + return 1; +} + +/** + * @brief configure / deconfigure the IPFIX flow-per-packet + * @param fm flowprobe_main_t * fm + * @param sw_if_index u32 the desired interface + * @param is_add int 1 to enable the feature, 0 to disable it + * @returns 0 if successful, non-zero otherwise + */ + +static int +flowprobe_tx_interface_add_del_feature (flowprobe_main_t * fm, + u32 sw_if_index, u8 which, int is_add) +{ + vlib_main_t *vm = vlib_get_main (); + int rv = 0; + u16 template_id = 0; + flowprobe_record_t flags = fm->record; + + fm->flow_per_interface[sw_if_index] = (is_add) ? which : (u8) ~ 0; + fm->template_per_flow[which] += (is_add) ? 1 : -1; + if (is_add && fm->template_per_flow[which] > 1) + template_id = fm->template_reports[flags]; + + if ((is_add && fm->template_per_flow[which] == 1) || + (!is_add && fm->template_per_flow[which] == 0)) + { + if (which == FLOW_VARIANT_L2) + { + if (fm->record & FLOW_RECORD_L2) + { + rv = flowprobe_template_add_del (1, UDP_DST_PORT_ipfix, flags, + flowprobe_data_callback_l2, + flowprobe_template_rewrite_l2, + is_add, &template_id); + } + if (fm->record & FLOW_RECORD_L3 || fm->record & FLOW_RECORD_L4) + { + rv = flowprobe_template_add_del (1, UDP_DST_PORT_ipfix, flags, + flowprobe_data_callback_l2, + flowprobe_template_rewrite_l2_ip4, + is_add, &template_id); + fm->template_reports[flags | FLOW_RECORD_L2_IP4] = + (is_add) ? template_id : 0; + rv = + flowprobe_template_add_del (1, UDP_DST_PORT_ipfix, flags, + flowprobe_data_callback_l2, + flowprobe_template_rewrite_l2_ip6, + is_add, &template_id); + fm->template_reports[flags | FLOW_RECORD_L2_IP6] = + (is_add) ? template_id : 0; + + /* Special case L2 */ + fm->context[FLOW_VARIANT_L2_IP4].flags = + flags | FLOW_RECORD_L2_IP4; + fm->context[FLOW_VARIANT_L2_IP6].flags = + flags | FLOW_RECORD_L2_IP6; + + fm->template_reports[flags] = template_id; + } + } + else if (which == FLOW_VARIANT_IP4) + rv = flowprobe_template_add_del (1, UDP_DST_PORT_ipfix, flags, + flowprobe_data_callback_ip4, + flowprobe_template_rewrite_ip4, + is_add, &template_id); + else if (which == FLOW_VARIANT_IP6) + rv = flowprobe_template_add_del (1, UDP_DST_PORT_ipfix, flags, + flowprobe_data_callback_ip6, + flowprobe_template_rewrite_ip6, + is_add, &template_id); + } + if (rv && rv != VNET_API_ERROR_VALUE_EXIST) + { + clib_warning ("vnet_flow_report_add_del returned %d", rv); + return -1; + } + + if (which != (u8) ~ 0) + { + fm->context[which].flags = fm->record; + fm->template_reports[flags] = (is_add) ? template_id : 0; + } + + if (which == FLOW_VARIANT_IP4) + vnet_feature_enable_disable ("ip4-output", "flowprobe-ip4", + sw_if_index, is_add, 0, 0); + else if (which == FLOW_VARIANT_IP6) + vnet_feature_enable_disable ("ip6-output", "flowprobe-ip6", + sw_if_index, is_add, 0, 0); + else if (which == FLOW_VARIANT_L2) + vnet_feature_enable_disable ("interface-output", "flowprobe-l2", + sw_if_index, is_add, 0, 0); + + /* Stateful flow collection */ + if (is_add && !fm->initialized) + { + flowprobe_create_state_tables (fm->active_timer); + if (fm->active_timer) + vlib_process_signal_event (vm, flowprobe_timer_node.index, 1, 0); + } + + return 0; +} + +/** + * @brief API message handler + * @param mp vl_api_flowprobe_tx_interface_add_del_t * mp the api message + */ +void vl_api_flowprobe_tx_interface_add_del_t_handler + (vl_api_flowprobe_tx_interface_add_del_t * mp) +{ + flowprobe_main_t *fm = &flowprobe_main; + vl_api_flowprobe_tx_interface_add_del_reply_t *rmp; + u32 sw_if_index = ntohl (mp->sw_if_index); + int rv = 0; + + VALIDATE_SW_IF_INDEX (mp); + + if (mp->which != FLOW_VARIANT_IP4 && mp->which != FLOW_VARIANT_L2 + && mp->which != FLOW_VARIANT_IP6) + { + rv = VNET_API_ERROR_UNIMPLEMENTED; + goto out; + } + + if (fm->record == 0) + { + clib_warning ("Please specify flowprobe params record first..."); + rv = VNET_API_ERROR_CANNOT_ENABLE_DISABLE_FEATURE; + goto out; + } + + rv = validate_feature_on_interface (fm, sw_if_index, mp->which); + if ((rv == 1 && mp->is_add == 1) || rv == 0) + { + rv = VNET_API_ERROR_CANNOT_ENABLE_DISABLE_FEATURE; + goto out; + } + + rv = flowprobe_tx_interface_add_del_feature + (fm, sw_if_index, mp->which, mp->is_add); + +out: + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_FLOWPROBE_TX_INTERFACE_ADD_DEL_REPLY); +} + +/** + * @brief API message custom-dump function + * @param mp vl_api_flowprobe_tx_interface_add_del_t * mp the api message + * @param handle void * print function handle + * @returns u8 * output string + */ +static void *vl_api_flowprobe_tx_interface_add_del_t_print + (vl_api_flowprobe_tx_interface_add_del_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: flowprobe_tx_interface_add_del "); + s = format (s, "sw_if_index %d is_add %d which %d ", + clib_host_to_net_u32 (mp->sw_if_index), + (int) mp->is_add, (int) mp->which); + FINISH; +} + +#define vec_neg_search(v,E) \ +({ \ + word _v(i) = 0; \ + while (_v(i) < vec_len(v) && v[_v(i)] == E) \ + { \ + _v(i)++; \ + } \ + if (_v(i) == vec_len(v)) \ + _v(i) = ~0; \ + _v(i); \ +}) + +static int +flowprobe_params (flowprobe_main_t * fm, u8 record_l2, + u8 record_l3, u8 record_l4, + u32 active_timer, u32 passive_timer) +{ + flowprobe_record_t flags = 0; + + if (vec_neg_search (fm->flow_per_interface, (u8) ~ 0) != ~0) + return ~0; + + if (record_l2) + flags |= FLOW_RECORD_L2; + if (record_l3) + flags |= FLOW_RECORD_L3; + if (record_l4) + flags |= FLOW_RECORD_L4; + + fm->record = flags; + + /* + * Timers: ~0 is default, 0 is off + */ + fm->active_timer = + (active_timer == (u32) ~ 0 ? FLOWPROBE_TIMER_ACTIVE : active_timer); + fm->passive_timer = + (passive_timer == (u32) ~ 0 ? FLOWPROBE_TIMER_PASSIVE : passive_timer); + + return 0; +} + +void +vl_api_flowprobe_params_t_handler (vl_api_flowprobe_params_t * mp) +{ + flowprobe_main_t *fm = &flowprobe_main; + vl_api_flowprobe_params_reply_t *rmp; + int rv = 0; + + rv = flowprobe_params + (fm, mp->record_l2, mp->record_l3, mp->record_l4, + clib_net_to_host_u32 (mp->active_timer), + clib_net_to_host_u32 (mp->passive_timer)); + + REPLY_MACRO (VL_API_FLOWPROBE_PARAMS_REPLY); +} + +/* List of message types that this plugin understands */ +#define foreach_flowprobe_plugin_api_msg \ +_(FLOWPROBE_TX_INTERFACE_ADD_DEL, flowprobe_tx_interface_add_del) \ +_(FLOWPROBE_PARAMS, flowprobe_params) + +/* *INDENT-OFF* */ +VLIB_PLUGIN_REGISTER () = { + .version = VPP_BUILD_VER, + .description = "Flow per Packet", +}; +/* *INDENT-ON* */ + +u8 * +format_flowprobe_entry (u8 * s, va_list * args) +{ + flowprobe_entry_t *e = va_arg (*args, flowprobe_entry_t *); + s = format (s, " %d/%d", e->key.rx_sw_if_index, e->key.tx_sw_if_index); + + s = format (s, " %U %U", format_ethernet_address, &e->key.src_mac, + format_ethernet_address, &e->key.dst_mac); + s = format (s, " %U -> %U", + format_ip46_address, &e->key.src_address, IP46_TYPE_ANY, + format_ip46_address, &e->key.dst_address, IP46_TYPE_ANY); + s = format (s, " %d", e->key.protocol); + s = format (s, " %d %d\n", clib_net_to_host_u16 (e->key.src_port), + clib_net_to_host_u16 (e->key.dst_port)); + + return s; +} + +static clib_error_t * +flowprobe_show_table_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cm) +{ + flowprobe_main_t *fm = &flowprobe_main; + int i; + flowprobe_entry_t *e; + + vlib_cli_output (vm, "Dumping IPFIX table"); + + for (i = 0; i < vec_len (fm->pool_per_worker); i++) + { + /* *INDENT-OFF* */ + pool_foreach (e, fm->pool_per_worker[i], ( + { + vlib_cli_output (vm, "%U", + format_flowprobe_entry, + e); + })); + /* *INDENT-ON* */ + + } + return 0; +} + +static clib_error_t * +flowprobe_show_stats_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cm) +{ + flowprobe_main_t *fm = &flowprobe_main; + int i; + + vlib_cli_output (vm, "IPFIX table statistics"); + vlib_cli_output (vm, "Flow entry size: %d\n", sizeof (flowprobe_entry_t)); + vlib_cli_output (vm, "Flow pool size per thread: %d\n", + 0x1 << FLOWPROBE_LOG2_HASHSIZE); + + for (i = 0; i < vec_len (fm->pool_per_worker); i++) + vlib_cli_output (vm, "Pool utilisation thread %d is %d%%\n", i, + (100 * pool_elts (fm->pool_per_worker[i])) / + (0x1 << FLOWPROBE_LOG2_HASHSIZE)); + return 0; +} + +static clib_error_t * +flowprobe_tx_interface_add_del_feature_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + flowprobe_main_t *fm = &flowprobe_main; + u32 sw_if_index = ~0; + int is_add = 1; + u8 which = FLOW_VARIANT_IP4; + int rv; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "disable")) + is_add = 0; + else if (unformat (input, "%U", unformat_vnet_sw_interface, + fm->vnet_main, &sw_if_index)); + else if (unformat (input, "ip4")) + which = FLOW_VARIANT_IP4; + else if (unformat (input, "ip6")) + which = FLOW_VARIANT_IP6; + else if (unformat (input, "l2")) + which = FLOW_VARIANT_L2; + else + break; + } + + if (fm->record == 0) + return clib_error_return (0, + "Please specify flowprobe params record first..."); + + if (sw_if_index == ~0) + return clib_error_return (0, "Please specify an interface..."); + + rv = validate_feature_on_interface (fm, sw_if_index, which); + if (rv == 1) + { + if (is_add) + return clib_error_return (0, + "Datapath is already enabled for given interface..."); + } + else if (rv == 0) + return clib_error_return (0, + "Interface has enable different datapath ..."); + + rv = + flowprobe_tx_interface_add_del_feature (fm, sw_if_index, which, is_add); + switch (rv) + { + case 0: + break; + + case VNET_API_ERROR_INVALID_SW_IF_INDEX: + return clib_error_return + (0, "Invalid interface, only works on physical ports"); + break; + + case VNET_API_ERROR_UNIMPLEMENTED: + return clib_error_return (0, "ip6 not supported"); + break; + + default: + return clib_error_return (0, "flowprobe_enable_disable returned %d", + rv); + } + return 0; +} + +static clib_error_t * +flowprobe_params_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + flowprobe_main_t *fm = &flowprobe_main; + bool record_l2 = false, record_l3 = false, record_l4 = false; + u32 active_timer = ~0; + u32 passive_timer = ~0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "active %d", &active_timer)) + ; + else if (unformat (input, "passive %d", &passive_timer)) + ; + else if (unformat (input, "record")) + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "l2")) + record_l2 = true; + else if (unformat (input, "l3")) + record_l3 = true; + else if (unformat (input, "l4")) + record_l4 = true; + else + break; + } + else + break; + } + + if (passive_timer > 0 && active_timer > passive_timer) + return clib_error_return (0, + "Passive timer has to be greater than active one..."); + + if (flowprobe_params (fm, record_l2, record_l3, record_l4, + active_timer, passive_timer)) + return clib_error_return (0, + "Couldn't change flowperpacket params when feature is enabled on some interface ..."); + return 0; +} + +/*? + * 'flowprobe feature add-del' commands to enable/disable + * per-packet IPFIX flow record generation on an interface + * + * @cliexpar + * @parblock + * To enable per-packet IPFIX flow-record generation on an interface: + * @cliexcmd{flowprobe feature add-del GigabitEthernet2/0/0} + * + * To disable per-packet IPFIX flow-record generation on an interface: + * @cliexcmd{flowprobe feature add-del GigabitEthernet2/0/0 disable} + * @cliexend + * @endparblock +?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (flowprobe_enable_disable_command, static) = { + .path = "flowprobe feature add-del", + .short_help = + "flowprobe feature add-del disable", + .function = flowprobe_tx_interface_add_del_feature_command_fn, +}; +VLIB_CLI_COMMAND (flowprobe_params_command, static) = { + .path = "flowprobe params", + .short_help = + "flowprobe params record <[l2] [l3] [l4]> [active passive ]", + .function = flowprobe_params_command_fn, +}; +VLIB_CLI_COMMAND (flowprobe_show_table_command, static) = { + .path = "show flowprobe table", + .short_help = "show flowprobe table", + .function = flowprobe_show_table_fn, +}; +VLIB_CLI_COMMAND (flowprobe_show_stats_command, static) = { + .path = "show flowprobe statistics", + .short_help = "show flowprobe statistics", + .function = flowprobe_show_stats_fn, +}; +/* *INDENT-ON* */ + +/** + * @brief Set up the API message handling tables + * @param vm vlib_main_t * vlib main data structure pointer + * @returns 0 to indicate all is well + */ +static clib_error_t * +flowprobe_plugin_api_hookup (vlib_main_t * vm) +{ + flowprobe_main_t *fm = &flowprobe_main; +#define _(N,n) \ + vl_msg_api_set_handlers((VL_API_##N + fm->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_flowprobe_plugin_api_msg; +#undef _ + + return 0; +} + +#define vl_msg_name_crc_list +#include +#undef vl_msg_name_crc_list + +static void +setup_message_id_table (flowprobe_main_t * fm, api_main_t * am) +{ +#define _(id,n,crc) \ + vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + fm->msg_id_base); + foreach_vl_msg_name_crc_flowprobe; +#undef _ +} + +/* + * Main-core process, sending an interrupt to the per worker input + * process that spins the per worker timer wheel. + */ +static uword +timer_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f) +{ + uword *event_data = 0; + vlib_main_t **worker_vms = 0, *worker_vm; + flowprobe_main_t *fm = &flowprobe_main; + + /* Wait for Godot... */ + vlib_process_wait_for_event_or_clock (vm, 1e9); + uword event_type = vlib_process_get_events (vm, &event_data); + if (event_type != 1) + clib_warning ("bogus kickoff event received, %d", event_type); + vec_reset_length (event_data); + + int i; + if (vec_len (vlib_mains) == 0) + vec_add1 (worker_vms, vm); + else + { + for (i = 0; i < vec_len (vlib_mains); i++) + { + worker_vm = vlib_mains[i]; + if (worker_vm) + vec_add1 (worker_vms, worker_vm); + } + } + f64 sleep_duration = 0.1; + + while (1) + { + /* Send an interrupt to each timer input node */ + sleep_duration = 0.1; + for (i = 0; i < vec_len (worker_vms); i++) + { + worker_vm = worker_vms[i]; + if (worker_vm) + { + vlib_node_set_interrupt_pending (worker_vm, + flowprobe_walker_node.index); + sleep_duration = + (fm->expired_passive_per_worker[i] > 0) ? 1e-4 : 0.1; + } + } + vlib_process_suspend (vm, sleep_duration); + } + return 0; /* or not */ +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (flowprobe_timer_node,static) = { + .function = timer_process, + .name = "flowprobe-timer-process", + .type = VLIB_NODE_TYPE_PROCESS, +}; +/* *INDENT-ON* */ + +/** + * @brief Set up the API message handling tables + * @param vm vlib_main_t * vlib main data structure pointer + * @returns 0 to indicate all is well, or a clib_error_t + */ +static clib_error_t * +flowprobe_init (vlib_main_t * vm) +{ + flowprobe_main_t *fm = &flowprobe_main; + vlib_thread_main_t *tm = &vlib_thread_main; + clib_error_t *error = 0; + u8 *name; + u32 num_threads; + int i; + + fm->vnet_main = vnet_get_main (); + + /* Construct the API name */ + name = format (0, "flowprobe_%08x%c", api_version, 0); + + /* Ask for a correctly-sized block of API message decode slots */ + fm->msg_id_base = vl_msg_api_get_msg_ids + ((char *) name, VL_MSG_FIRST_AVAILABLE); + + /* Hook up message handlers */ + error = flowprobe_plugin_api_hookup (vm); + + /* Add our API messages to the global name_crc hash table */ + setup_message_id_table (fm, &api_main); + + vec_free (name); + + /* Set up time reference pair */ + fm->vlib_time_0 = vlib_time_now (vm); + fm->nanosecond_time_0 = unix_time_now_nsec (); + + memset (fm->template_reports, 0, sizeof (fm->template_reports)); + memset (fm->template_size, 0, sizeof (fm->template_size)); + memset (fm->template_per_flow, 0, sizeof (fm->template_per_flow)); + + /* Decide how many worker threads we have */ + num_threads = 1 /* main thread */ + tm->n_threads; + + /* Allocate per worker thread vectors per flavour */ + for (i = 0; i < FLOW_N_VARIANTS; i++) + { + vec_validate (fm->context[i].buffers_per_worker, num_threads - 1); + vec_validate (fm->context[i].frames_per_worker, num_threads - 1); + vec_validate (fm->context[i].next_record_offset_per_worker, + num_threads - 1); + } + + fm->active_timer = FLOWPROBE_TIMER_ACTIVE; + fm->passive_timer = FLOWPROBE_TIMER_PASSIVE; + + return error; +} + +VLIB_INIT_FUNCTION (flowprobe_init); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/flowprobe/flowprobe.h b/src/plugins/flowprobe/flowprobe.h new file mode 100644 index 00000000..196c92a7 --- /dev/null +++ b/src/plugins/flowprobe/flowprobe.h @@ -0,0 +1,167 @@ +/* + * flowprobe.h - ipfix probe plug-in header file + * + * 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_flowprobe_h__ +#define __included_flowprobe_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include + +/* Default timers in seconds */ +#define FLOWPROBE_TIMER_ACTIVE (15) +#define FLOWPROBE_TIMER_PASSIVE 120 // XXXX: FOR TESTING (30*60) +#define FLOWPROBE_LOG2_HASHSIZE (18) + +typedef enum +{ + FLOW_RECORD_L2 = 1 << 0, + FLOW_RECORD_L3 = 1 << 1, + FLOW_RECORD_L4 = 1 << 2, + FLOW_RECORD_L2_IP4 = 1 << 3, + FLOW_RECORD_L2_IP6 = 1 << 4, + FLOW_N_RECORDS = 1 << 5, +} flowprobe_record_t; + +/* *INDENT-OFF* */ +typedef enum __attribute__ ((__packed__)) +{ + FLOW_VARIANT_IP4, + FLOW_VARIANT_IP6, + FLOW_VARIANT_L2, + FLOW_VARIANT_L2_IP4, + FLOW_VARIANT_L2_IP6, + FLOW_N_VARIANTS, +} flowprobe_variant_t; +/* *INDENT-ON* */ + +STATIC_ASSERT (sizeof (flowprobe_variant_t) == 1, + "flowprobe_variant_t is expected to be 1 byte, " + "revisit padding in flowprobe_key_t"); + +#define FLOW_MAXIMUM_EXPORT_ENTRIES (1024) + +typedef struct +{ + /* what to collect per variant */ + flowprobe_record_t flags; + /** ipfix buffers under construction, per-worker thread */ + vlib_buffer_t **buffers_per_worker; + /** frames containing ipfix buffers, per-worker thread */ + vlib_frame_t **frames_per_worker; + /** next record offset, per worker thread */ + u16 *next_record_offset_per_worker; +} flowprobe_protocol_context_t; + +#define FLOWPROBE_KEY_IN_U32 22 +/* *INDENT-OFF* */ +typedef CLIB_PACKED (union +{ + struct { + u32 rx_sw_if_index; + u32 tx_sw_if_index; + u8 src_mac[6]; + u8 dst_mac[6]; + u16 ethertype; + ip46_address_t src_address; + ip46_address_t dst_address; + u8 protocol; + u16 src_port; + u16 dst_port; + flowprobe_variant_t which; + }; + u32 as_u32[FLOWPROBE_KEY_IN_U32]; +}) flowprobe_key_t; +/* *INDENT-ON* */ + +STATIC_ASSERT (sizeof (flowprobe_key_t) == FLOWPROBE_KEY_IN_U32 * + sizeof (u32), "flowprobe_key_t padding is wrong"); + +typedef struct +{ + flowprobe_key_t key; + u64 packetcount; + u64 octetcount; + f64 last_updated; + f64 last_exported; + u32 passive_timer_handle; +} flowprobe_entry_t; + +/** + * @file + * @brief flow-per-packet plugin header file + */ +typedef struct +{ + /** API message ID base */ + u16 msg_id_base; + + flowprobe_protocol_context_t context[FLOW_N_VARIANTS]; + u16 template_reports[FLOW_N_RECORDS]; + u16 template_size[FLOW_N_RECORDS]; + + /** Time reference pair */ + u64 nanosecond_time_0; + f64 vlib_time_0; + + /** Per CPU flow-state */ + u8 ht_log2len; /* Hash table size is 2^log2len */ + u32 **hash_per_worker; + flowprobe_entry_t **pool_per_worker; + /* *INDENT-OFF* */ + TWT (tw_timer_wheel) ** timers_per_worker; + /* *INDENT-ON* */ + u32 **expired_passive_per_worker; + + flowprobe_record_t record; + u32 active_timer; + u32 passive_timer; + flowprobe_entry_t *stateless_entry; + + bool initialized; + bool disabled; + + u16 template_per_flow[FLOW_N_VARIANTS]; + u8 *flow_per_interface; + + /** convenience vlib_main_t pointer */ + vlib_main_t *vlib_main; + /** convenience vnet_main_t pointer */ + vnet_main_t *vnet_main; +} flowprobe_main_t; + +extern flowprobe_main_t flowprobe_main; + +void flowprobe_flush_callback_ip4 (void); +void flowprobe_flush_callback_ip6 (void); +void flowprobe_flush_callback_l2 (void); +u8 *format_flowprobe_entry (u8 * s, va_list * args); + +#endif + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/flowprobe/flowprobe_all_api_h.h b/src/plugins/flowprobe/flowprobe_all_api_h.h new file mode 100644 index 00000000..1f30eccc --- /dev/null +++ b/src/plugins/flowprobe/flowprobe_all_api_h.h @@ -0,0 +1,18 @@ +/* + * flowprobe_all_api_h.h - plug-in api #include file + * + * Copyright (c) + * 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 diff --git a/src/plugins/flowprobe/flowprobe_msg_enum.h b/src/plugins/flowprobe/flowprobe_msg_enum.h new file mode 100644 index 00000000..bc0b21c9 --- /dev/null +++ b/src/plugins/flowprobe/flowprobe_msg_enum.h @@ -0,0 +1,31 @@ +/* + * flowprobe_msg_enum.h - vpp engine plug-in message enumeration + * + * Copyright (c) + * 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_flowprobe_msg_enum_h +#define included_flowprobe_msg_enum_h + +#include + +#define vl_msg_id(n,h) n, +typedef enum +{ +#include + /* 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_flowprobe_msg_enum_h */ diff --git a/src/plugins/flowprobe/flowprobe_plugin_doc.md b/src/plugins/flowprobe/flowprobe_plugin_doc.md new file mode 100644 index 00000000..4c9b2342 --- /dev/null +++ b/src/plugins/flowprobe/flowprobe_plugin_doc.md @@ -0,0 +1,13 @@ +IPFIX flow record plugin {#flowprobe_plugin_doc} +======================== + +## Introduction + +This plugin generates ipfix flow records on interfaces which have the feature enabled + +## Sample configuration + +set ipfix exporter collector 192.168.6.2 src 192.168.6.1 template-interval 20 port 4739 path-mtu 1500 + +flowprobe params record l3 active 20 passive 120 +flowprobe feature add-del GigabitEthernet2/3/0 l2 \ No newline at end of file diff --git a/src/plugins/flowprobe/flowprobe_test.c b/src/plugins/flowprobe/flowprobe_test.c new file mode 100644 index 00000000..91793f55 --- /dev/null +++ b/src/plugins/flowprobe/flowprobe_test.c @@ -0,0 +1,263 @@ +/* + * flowprobe.c - skeleton vpp-api-test plug-in + * + * Copyright (c) + * 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 +#include +#include +#include +#include +#include + +#define __plugin_msg_base flowprobe_test_main.msg_id_base +#include + +/** + * @file vpp_api_test plugin + */ + +uword unformat_sw_if_index (unformat_input_t * input, va_list * args); + +/* Declare message IDs */ +#include + +/* define message structures */ +#define vl_typedefs +#include +#undef vl_typedefs + +/* declare message handlers for each api */ + +#define vl_endianfun /* define message structures */ +#include +#undef vl_endianfun + +/* instantiate all the print functions we know about */ +#define vl_print(handle, ...) +#define vl_printfun +#include +#undef vl_printfun + +/* Get the API version number. */ +#define vl_api_version(n,v) static u32 api_version=(v); +#include +#undef vl_api_version + +typedef struct +{ + /** API message ID base */ + u16 msg_id_base; + /** vat_main_t pointer */ + vat_main_t *vat_main; +} flowprobe_test_main_t; + +flowprobe_test_main_t flowprobe_test_main; + +#define foreach_standard_reply_retval_handler \ +_(flowprobe_tx_interface_add_del_reply) \ +_(flowprobe_params_reply) + +#define _(n) \ + static void vl_api_##n##_t_handler \ + (vl_api_##n##_t * mp) \ + { \ + vat_main_t * vam = flowprobe_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 \ +_(FLOWPROBE_TX_INTERFACE_ADD_DEL_REPLY, \ + flowprobe_tx_interface_add_del_reply) \ +_(FLOWPROBE_PARAMS_REPLY, flowprobe_params_reply) + +static int +api_flowprobe_tx_interface_add_del (vat_main_t * vam) +{ + unformat_input_t *i = vam->input; + int enable_disable = 1; + u8 which = FLOW_VARIANT_IP4; + u32 sw_if_index = ~0; + vl_api_flowprobe_tx_interface_add_del_t *mp; + int ret; + + /* Parse args required to build the message */ + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "%U", unformat_sw_if_index, vam, &sw_if_index)) + ; + else if (unformat (i, "sw_if_index %d", &sw_if_index)) + ; + else if (unformat (i, "disable")) + enable_disable = 0; + else if (unformat (i, "ip4")) + which = FLOW_VARIANT_IP4; + else if (unformat (i, "ip6")) + which = FLOW_VARIANT_IP6; + else if (unformat (i, "l2")) + which = FLOW_VARIANT_L2; + else + break; + } + + if (sw_if_index == ~0) + { + errmsg ("missing interface name / explicit sw_if_index number \n"); + return -99; + } + + /* Construct the API message */ + M (FLOWPROBE_TX_INTERFACE_ADD_DEL, mp); + mp->sw_if_index = ntohl (sw_if_index); + mp->is_add = enable_disable; + mp->which = which; + + /* send it... */ + S (mp); + + /* Wait for a reply... */ + W (ret); + return ret; +} + +static int +api_flowprobe_params (vat_main_t * vam) +{ + unformat_input_t *i = vam->input; + u8 record_l2 = 0, record_l3 = 0, record_l4 = 0; + u32 active_timer = ~0; + u32 passive_timer = ~0; + vl_api_flowprobe_params_t *mp; + int ret; + + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "active %d", &active_timer)) + ; + else if (unformat (i, "passive %d", &passive_timer)) + ; + else if (unformat (i, "record")) + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "l2")) + record_l2 = 1; + else if (unformat (i, "l3")) + record_l3 = 1; + else if (unformat (i, "l4")) + record_l4 = 1; + else + break; + } + else + break; + } + + if (passive_timer > 0 && active_timer > passive_timer) + { + errmsg ("Passive timer has to be greater than active one...\n"); + return -99; + } + + /* Construct the API message */ + M (FLOWPROBE_PARAMS, mp); + mp->record_l2 = record_l2; + mp->record_l3 = record_l3; + mp->record_l4 = record_l4; + mp->active_timer = ntohl (active_timer); + mp->passive_timer = ntohl (passive_timer); + + /* send it... */ + S (mp); + + /* Wait for a reply... */ + W (ret); + + return ret; +} + +/* + * List of messages that the api test plugin sends, + * and that the data plane plugin processes + */ +#define foreach_vpe_api_msg \ +_(flowprobe_tx_interface_add_del, " [disable]") \ +_(flowprobe_params, "record <[l2] [l3] [l4]> [active passive ]") + +static void +flowprobe_vat_api_hookup (vat_main_t * vam) +{ + flowprobe_test_main_t *sm = &flowprobe_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) +{ + flowprobe_test_main_t *sm = &flowprobe_test_main; + u8 *name; + + sm->vat_main = vam; + + /* Ask the vpp engine for the first assigned message-id */ + name = format (0, "flowprobe_%08x%c", api_version, 0); + sm->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name); + + /* Don't attempt to hook up API messages if the data plane plugin is AWOL */ + if (sm->msg_id_base != (u16) ~ 0) + flowprobe_vat_api_hookup (vam); + + vec_free (name); + + return 0; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/flowprobe/node.c b/src/plugins/flowprobe/node.c new file mode 100644 index 00000000..6a539db9 --- /dev/null +++ b/src/plugins/flowprobe/node.c @@ -0,0 +1,998 @@ +/* + * node.c - ipfix probe graph node + * + * 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. + */ +#include +#include +#include +#include +#include +#include + +static void flowprobe_export_entry (vlib_main_t * vm, flowprobe_entry_t * e); + +/** + * @file flow record generator graph node + */ + +typedef struct +{ + /** interface handle */ + u32 rx_sw_if_index; + u32 tx_sw_if_index; + /** packet timestamp */ + u64 timestamp; + /** size of the buffer */ + u16 buffer_size; + + /** L2 information */ + u8 src_mac[6]; + u8 dst_mac[6]; + /** Ethertype */ + u16 ethertype; + + /** L3 information */ + ip46_address_t src_address; + ip46_address_t dst_address; + u8 protocol; + u8 tos; + + /** L4 information */ + u16 src_port; + u16 dst_port; + + flowprobe_variant_t which; +} flowprobe_trace_t; + +static char *flowprobe_variant_strings[] = { + [FLOW_VARIANT_IP4] = "IP4", + [FLOW_VARIANT_IP6] = "IP6", + [FLOW_VARIANT_L2] = "L2", + [FLOW_VARIANT_L2_IP4] = "L2-IP4", + [FLOW_VARIANT_L2_IP6] = "L2-IP6", +}; + +/* packet trace format function */ +static u8 * +format_flowprobe_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 *); + flowprobe_trace_t *t = va_arg (*args, flowprobe_trace_t *); + uword indent = format_get_indent (s); + + s = format (s, + "FLOWPROBE[%s]: rx_sw_if_index %d, tx_sw_if_index %d, " + "timestamp %lld, size %d", flowprobe_variant_strings[t->which], + t->rx_sw_if_index, t->tx_sw_if_index, + t->timestamp, t->buffer_size); + + if (t->which == FLOW_VARIANT_L2) + s = format (s, "\n%U -> %U", format_white_space, indent, + format_ethernet_address, &t->src_mac, + format_ethernet_address, &t->dst_mac); + + if (t->protocol > 0 + && (t->which == FLOW_VARIANT_L2_IP4 || t->which == FLOW_VARIANT_IP4 + || t->which == FLOW_VARIANT_L2_IP6 || t->which == FLOW_VARIANT_IP6)) + s = + format (s, "\n%U%U: %U -> %U", format_white_space, indent, + format_ip_protocol, t->protocol, format_ip46_address, + &t->src_address, IP46_TYPE_ANY, format_ip46_address, + &t->dst_address, IP46_TYPE_ANY); + return s; +} + +vlib_node_registration_t flowprobe_ip4_node; +vlib_node_registration_t flowprobe_ip6_node; +vlib_node_registration_t flowprobe_l2_node; + +/* No counters at the moment */ +#define foreach_flowprobe_error \ +_(COLLISION, "Hash table collisions") \ +_(BUFFER, "Buffer allocation error") \ +_(EXPORTED_PACKETS, "Exported packets") \ +_(INPATH, "Exported packets in path") + +typedef enum +{ +#define _(sym,str) FLOWPROBE_ERROR_##sym, + foreach_flowprobe_error +#undef _ + FLOWPROBE_N_ERROR, +} flowprobe_error_t; + +static char *flowprobe_error_strings[] = { +#define _(sym,string) string, + foreach_flowprobe_error +#undef _ +}; + +typedef enum +{ + FLOWPROBE_NEXT_DROP, + FLOWPROBE_NEXT_IP4_LOOKUP, + FLOWPROBE_N_NEXT, +} flowprobe_next_t; + +#define FLOWPROBE_NEXT_NODES { \ + [FLOWPROBE_NEXT_DROP] = "error-drop", \ + [FLOWPROBE_NEXT_IP4_LOOKUP] = "ip4-lookup", \ +} + +static inline flowprobe_variant_t +flowprobe_get_variant (flowprobe_variant_t which, + flowprobe_record_t flags, u16 ethertype) +{ + if (which == FLOW_VARIANT_L2 + && (flags & FLOW_RECORD_L3 || flags & FLOW_RECORD_L4)) + return ethertype == ETHERNET_TYPE_IP6 ? FLOW_VARIANT_L2_IP6 : ethertype == + ETHERNET_TYPE_IP4 ? FLOW_VARIANT_L2_IP4 : FLOW_VARIANT_L2; + return which; +} + +static inline u32 +flowprobe_common_add (vlib_buffer_t * to_b, flowprobe_entry_t * e, u16 offset) +{ + u16 start = offset; + + /* Ingress interface */ + u32 rx_if = clib_host_to_net_u32 (e->key.rx_sw_if_index); + clib_memcpy (to_b->data + offset, &rx_if, sizeof (rx_if)); + offset += sizeof (rx_if); + + /* Egress interface */ + u32 tx_if = clib_host_to_net_u32 (e->key.tx_sw_if_index); + clib_memcpy (to_b->data + offset, &tx_if, sizeof (tx_if)); + offset += sizeof (tx_if); + + /* packet delta count */ + u64 packetdelta = clib_host_to_net_u64 (e->packetcount); + clib_memcpy (to_b->data + offset, &packetdelta, sizeof (u64)); + offset += sizeof (u64); + + return offset - start; +} + +static inline u32 +flowprobe_l2_add (vlib_buffer_t * to_b, flowprobe_entry_t * e, u16 offset) +{ + u16 start = offset; + + /* src mac address */ + clib_memcpy (to_b->data + offset, &e->key.src_mac, 6); + offset += 6; + + /* dst mac address */ + clib_memcpy (to_b->data + offset, &e->key.dst_mac, 6); + offset += 6; + + /* ethertype */ + clib_memcpy (to_b->data + offset, &e->key.ethertype, 2); + offset += 2; + + return offset - start; +} + +static inline u32 +flowprobe_l3_ip6_add (vlib_buffer_t * to_b, flowprobe_entry_t * e, u16 offset) +{ + u16 start = offset; + + /* ip6 src address */ + clib_memcpy (to_b->data + offset, &e->key.src_address, + sizeof (ip6_address_t)); + offset += sizeof (ip6_address_t); + + /* ip6 dst address */ + clib_memcpy (to_b->data + offset, &e->key.dst_address, + sizeof (ip6_address_t)); + offset += sizeof (ip6_address_t); + + /* Protocol */ + to_b->data[offset++] = e->key.protocol; + + /* octetDeltaCount */ + u64 octetdelta = clib_host_to_net_u64 (e->octetcount); + clib_memcpy (to_b->data + offset, &octetdelta, sizeof (u64)); + offset += sizeof (u64); + + return offset - start; +} + +static inline u32 +flowprobe_l3_ip4_add (vlib_buffer_t * to_b, flowprobe_entry_t * e, u16 offset) +{ + u16 start = offset; + + /* ip4 src address */ + clib_memcpy (to_b->data + offset, &e->key.src_address.ip4, + sizeof (ip4_address_t)); + offset += sizeof (ip4_address_t); + + /* ip4 dst address */ + clib_memcpy (to_b->data + offset, &e->key.dst_address.ip4, + sizeof (ip4_address_t)); + offset += sizeof (ip4_address_t); + + /* Protocol */ + to_b->data[offset++] = e->key.protocol; + + /* octetDeltaCount */ + u64 octetdelta = clib_host_to_net_u64 (e->octetcount); + clib_memcpy (to_b->data + offset, &octetdelta, sizeof (u64)); + offset += sizeof (u64); + + return offset - start; +} + +static inline u32 +flowprobe_l4_add (vlib_buffer_t * to_b, flowprobe_entry_t * e, u16 offset) +{ + u16 start = offset; + + /* src port */ + clib_memcpy (to_b->data + offset, &e->key.src_port, 2); + offset += 2; + + /* dst port */ + clib_memcpy (to_b->data + offset, &e->key.dst_port, 2); + offset += 2; + + return offset - start; +} + +static inline u32 +flowprobe_hash (flowprobe_key_t * k) +{ + flowprobe_main_t *fm = &flowprobe_main; + int i; + u32 h = 0; + for (i = 0; i < sizeof (k->as_u32) / sizeof (u32); i++) + h = crc_u32 (k->as_u32[i], h); + return h >> (32 - fm->ht_log2len); +} + +flowprobe_entry_t * +flowprobe_lookup (u32 my_cpu_number, flowprobe_key_t * k, u32 * poolindex, + bool * collision) +{ + flowprobe_main_t *fm = &flowprobe_main; + flowprobe_entry_t *e; + u32 h; + + h = (fm->active_timer) ? flowprobe_hash (k) : 0; + + /* Lookup in the flow state pool */ + *poolindex = fm->hash_per_worker[my_cpu_number][h]; + if (*poolindex != ~0) + { + e = pool_elt_at_index (fm->pool_per_worker[my_cpu_number], *poolindex); + if (e) + { + /* Verify key or report collision */ + if (memcmp (k, &e->key, sizeof (flowprobe_key_t))) + *collision = true; + return e; + } + } + + return 0; +} + +flowprobe_entry_t * +flowprobe_create (u32 my_cpu_number, flowprobe_key_t * k, u32 * poolindex) +{ + flowprobe_main_t *fm = &flowprobe_main; + u32 h; + + flowprobe_entry_t *e; + + /* Get my index */ + h = (fm->active_timer) ? flowprobe_hash (k) : 0; + + pool_get (fm->pool_per_worker[my_cpu_number], e); + *poolindex = e - fm->pool_per_worker[my_cpu_number]; + fm->hash_per_worker[my_cpu_number][h] = *poolindex; + + e->key = *k; + + if (fm->passive_timer > 0) + { + e->passive_timer_handle = tw_timer_start_2t_1w_2048sl + (fm->timers_per_worker[my_cpu_number], *poolindex, 0, + fm->passive_timer); + } + return e; +} + +static inline void +add_to_flow_record_state (vlib_main_t * vm, vlib_node_runtime_t * node, + flowprobe_main_t * fm, vlib_buffer_t * b, + u64 timestamp, u16 length, + flowprobe_variant_t which, flowprobe_trace_t * t) +{ + if (fm->disabled) + return; + + u32 my_cpu_number = vm->thread_index; + u16 octets = 0; + + flowprobe_record_t flags = fm->context[which].flags; + bool collect_ip4 = false, collect_ip6 = false; + ASSERT (b); + ethernet_header_t *eth = vlib_buffer_get_current (b); + u16 ethertype = clib_net_to_host_u16 (eth->type); + /* *INDENT-OFF* */ + flowprobe_key_t k = { {0} }; + /* *INDENT-ON* */ + ip4_header_t *ip4 = 0; + ip6_header_t *ip6 = 0; + udp_header_t *udp = 0; + + if (flags & FLOW_RECORD_L3 || flags & FLOW_RECORD_L4) + { + collect_ip4 = which == FLOW_VARIANT_L2_IP4 || which == FLOW_VARIANT_IP4; + collect_ip6 = which == FLOW_VARIANT_L2_IP6 || which == FLOW_VARIANT_IP6; + } + + k.rx_sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX]; + k.tx_sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_TX]; + + k.which = which; + + if (flags & FLOW_RECORD_L2) + { + clib_memcpy (k.src_mac, eth->src_address, 6); + clib_memcpy (k.dst_mac, eth->dst_address, 6); + k.ethertype = ethertype; + } + if (collect_ip6 && ethertype == ETHERNET_TYPE_IP6) + { + ip6 = (ip6_header_t *) (eth + 1); + udp = (udp_header_t *) (ip6 + 1); + if (flags & FLOW_RECORD_L3) + { + k.src_address.as_u64[0] = ip6->src_address.as_u64[0]; + k.src_address.as_u64[1] = ip6->src_address.as_u64[1]; + k.dst_address.as_u64[0] = ip6->dst_address.as_u64[0]; + k.dst_address.as_u64[1] = ip6->dst_address.as_u64[1]; + } + k.protocol = ip6->protocol; + octets = clib_net_to_host_u16 (ip6->payload_length) + + sizeof (ip6_header_t); + } + if (collect_ip4 && ethertype == ETHERNET_TYPE_IP4) + { + ip4 = (ip4_header_t *) (eth + 1); + udp = (udp_header_t *) (ip4 + 1); + if (flags & FLOW_RECORD_L3) + { + k.src_address.ip4.as_u32 = ip4->src_address.as_u32; + k.dst_address.ip4.as_u32 = ip4->dst_address.as_u32; + } + k.protocol = ip4->protocol; + octets = clib_net_to_host_u16 (ip4->length); + } + if ((flags & FLOW_RECORD_L4) && udp && + (k.protocol == IP_PROTOCOL_TCP || k.protocol == IP_PROTOCOL_UDP)) + { + k.src_port = udp->src_port; + k.dst_port = udp->dst_port; + } + + if (t) + { + t->rx_sw_if_index = k.rx_sw_if_index; + t->tx_sw_if_index = k.tx_sw_if_index; + clib_memcpy (t->src_mac, k.src_mac, 6); + clib_memcpy (t->dst_mac, k.dst_mac, 6); + t->ethertype = k.ethertype; + t->src_address.ip4.as_u32 = k.src_address.ip4.as_u32; + t->dst_address.ip4.as_u32 = k.dst_address.ip4.as_u32; + t->protocol = k.protocol; + t->src_port = k.src_port; + t->dst_port = k.dst_port; + t->which = k.which; + } + + flowprobe_entry_t *e = 0; + f64 now = vlib_time_now (vm); + if (fm->active_timer > 0) + { + u32 poolindex = ~0; + bool collision = false; + + e = flowprobe_lookup (my_cpu_number, &k, &poolindex, &collision); + if (collision) + { + /* Flush data and clean up entry for reuse. */ + if (e->packetcount) + flowprobe_export_entry (vm, e); + e->key = k; + vlib_node_increment_counter (vm, node->node_index, + FLOWPROBE_ERROR_COLLISION, 1); + } + if (!e) /* Create new entry */ + { + e = flowprobe_create (my_cpu_number, &k, &poolindex); + e->last_exported = now; + } + } + else + { + e = &fm->stateless_entry[my_cpu_number]; + e->key = k; + } + + if (e) + { + /* Updating entry */ + e->packetcount++; + e->octetcount += octets; + e->last_updated = now; + + if (fm->active_timer == 0 + || (now > e->last_exported + fm->active_timer)) + flowprobe_export_entry (vm, e); + } +} + +static u16 +flowprobe_get_headersize (void) +{ + return sizeof (ip4_header_t) + sizeof (udp_header_t) + + sizeof (ipfix_message_header_t) + sizeof (ipfix_set_header_t); +} + +static void +flowprobe_export_send (vlib_main_t * vm, vlib_buffer_t * b0, + flowprobe_variant_t which) +{ + flowprobe_main_t *fm = &flowprobe_main; + flow_report_main_t *frm = &flow_report_main; + vlib_frame_t *f; + ip4_ipfix_template_packet_t *tp; + ipfix_set_header_t *s; + ipfix_message_header_t *h; + ip4_header_t *ip; + udp_header_t *udp; + flowprobe_record_t flags = fm->context[which].flags; + u32 my_cpu_number = vm->thread_index; + + /* Fill in header */ + flow_report_stream_t *stream; + + /* Nothing to send */ + if (fm->context[which].next_record_offset_per_worker[my_cpu_number] <= + flowprobe_get_headersize ()) + return; + + u32 i, index = vec_len (frm->streams); + for (i = 0; i < index; i++) + if (frm->streams[i].domain_id == 1) + { + index = i; + break; + } + if (i == vec_len (frm->streams)) + { + vec_validate (frm->streams, index); + frm->streams[index].domain_id = 1; + } + stream = &frm->streams[index]; + + tp = vlib_buffer_get_current (b0); + ip = (ip4_header_t *) & tp->ip4; + udp = (udp_header_t *) (ip + 1); + h = (ipfix_message_header_t *) (udp + 1); + s = (ipfix_set_header_t *) (h + 1); + + ip->ip_version_and_header_length = 0x45; + ip->ttl = 254; + ip->protocol = IP_PROTOCOL_UDP; + ip->flags_and_fragment_offset = 0; + ip->src_address.as_u32 = frm->src_address.as_u32; + ip->dst_address.as_u32 = frm->ipfix_collector.as_u32; + udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_ipfix); + udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_ipfix); + udp->checksum = 0; + + /* FIXUP: message header export_time */ + h->export_time = (u32) + (((f64) frm->unix_time_0) + + (vlib_time_now (frm->vlib_main) - frm->vlib_time_0)); + h->export_time = clib_host_to_net_u32 (h->export_time); + h->domain_id = clib_host_to_net_u32 (stream->domain_id); + + /* FIXUP: message header sequence_number */ + h->sequence_number = stream->sequence_number++; + h->sequence_number = clib_host_to_net_u32 (h->sequence_number); + + s->set_id_length = ipfix_set_id_length (fm->template_reports[flags], + b0->current_length - + (sizeof (*ip) + sizeof (*udp) + + sizeof (*h))); + h->version_length = version_length (b0->current_length - + (sizeof (*ip) + sizeof (*udp))); + + ip->length = clib_host_to_net_u16 (b0->current_length); + + ip->checksum = ip4_header_checksum (ip); + udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip)); + + if (frm->udp_checksum) + { + /* RFC 7011 section 10.3.2. */ + udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip); + if (udp->checksum == 0) + udp->checksum = 0xffff; + } + + ASSERT (ip->checksum == ip4_header_checksum (ip)); + + /* Find or allocate a frame */ + f = fm->context[which].frames_per_worker[my_cpu_number]; + if (PREDICT_FALSE (f == 0)) + { + u32 *to_next; + f = vlib_get_frame_to_node (vm, ip4_lookup_node.index); + fm->context[which].frames_per_worker[my_cpu_number] = f; + u32 bi0 = vlib_get_buffer_index (vm, b0); + + /* Enqueue the buffer */ + to_next = vlib_frame_vector_args (f); + to_next[0] = bi0; + f->n_vectors = 1; + } + + vlib_put_frame_to_node (vm, ip4_lookup_node.index, f); + vlib_node_increment_counter (vm, flowprobe_l2_node.index, + FLOWPROBE_ERROR_EXPORTED_PACKETS, 1); + + fm->context[which].frames_per_worker[my_cpu_number] = 0; + fm->context[which].buffers_per_worker[my_cpu_number] = 0; + fm->context[which].next_record_offset_per_worker[my_cpu_number] = + flowprobe_get_headersize (); +} + +static vlib_buffer_t * +flowprobe_get_buffer (vlib_main_t * vm, flowprobe_variant_t which) +{ + flowprobe_main_t *fm = &flowprobe_main; + flow_report_main_t *frm = &flow_report_main; + vlib_buffer_t *b0; + u32 bi0; + vlib_buffer_free_list_t *fl; + u32 my_cpu_number = vm->thread_index; + + /* Find or allocate a buffer */ + b0 = fm->context[which].buffers_per_worker[my_cpu_number]; + + /* Need to allocate a buffer? */ + if (PREDICT_FALSE (b0 == 0)) + { + if (vlib_buffer_alloc (vm, &bi0, 1) != 1) + { + vlib_node_increment_counter (vm, flowprobe_l2_node.index, + FLOWPROBE_ERROR_BUFFER, 1); + return 0; + } + + /* Initialize the buffer */ + b0 = fm->context[which].buffers_per_worker[my_cpu_number] = + vlib_get_buffer (vm, bi0); + fl = + vlib_buffer_get_free_list (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX); + vlib_buffer_init_for_free_list (b0, fl); + VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0); + + b0->current_data = 0; + b0->current_length = flowprobe_get_headersize (); + b0->flags |= (VLIB_BUFFER_TOTAL_LENGTH_VALID | VLIB_BUFFER_FLOW_REPORT); + vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0; + vnet_buffer (b0)->sw_if_index[VLIB_TX] = frm->fib_index; + fm->context[which].next_record_offset_per_worker[my_cpu_number] = + b0->current_length; + } + + return b0; +} + +static void +flowprobe_export_entry (vlib_main_t * vm, flowprobe_entry_t * e) +{ + u32 my_cpu_number = vm->thread_index; + flowprobe_main_t *fm = &flowprobe_main; + flow_report_main_t *frm = &flow_report_main; + vlib_buffer_t *b0; + bool collect_ip4 = false, collect_ip6 = false; + flowprobe_variant_t which = e->key.which; + flowprobe_record_t flags = fm->context[which].flags; + u16 offset = + fm->context[which].next_record_offset_per_worker[my_cpu_number]; + + if (offset < flowprobe_get_headersize ()) + offset = flowprobe_get_headersize (); + + b0 = flowprobe_get_buffer (vm, which); + /* No available buffer, what to do... */ + if (b0 == 0) + return; + + if (flags & FLOW_RECORD_L3) + { + collect_ip4 = which == FLOW_VARIANT_L2_IP4 || which == FLOW_VARIANT_IP4; + collect_ip6 = which == FLOW_VARIANT_L2_IP6 || which == FLOW_VARIANT_IP6; + } + + offset += flowprobe_common_add (b0, e, offset); + + if (flags & FLOW_RECORD_L2) + offset += flowprobe_l2_add (b0, e, offset); + if (collect_ip6) + offset += flowprobe_l3_ip6_add (b0, e, offset); + if (collect_ip4) + offset += flowprobe_l3_ip4_add (b0, e, offset); + if (flags & FLOW_RECORD_L4) + offset += flowprobe_l4_add (b0, e, offset); + + /* Reset per flow-export counters */ + e->packetcount = 0; + e->octetcount = 0; + e->last_exported = vlib_time_now (vm); + + b0->current_length = offset; + + fm->context[which].next_record_offset_per_worker[my_cpu_number] = offset; + /* Time to flush the buffer? */ + if (offset + fm->template_size[flags] > frm->path_mtu) + flowprobe_export_send (vm, b0, which); +} + +uword +flowprobe_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame, + flowprobe_variant_t which) +{ + u32 n_left_from, *from, *to_next; + flowprobe_next_t next_index; + flowprobe_main_t *fm = &flowprobe_main; + u64 now; + + now = (u64) ((vlib_time_now (vm) - fm->vlib_time_0) * 1e9); + now += fm->nanosecond_time_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 >= 4 && n_left_to_next >= 2) + { + u32 next0 = FLOWPROBE_NEXT_DROP; + u32 next1 = FLOWPROBE_NEXT_DROP; + u16 len0, len1; + u32 bi0, bi1; + vlib_buffer_t *b0, *b1; + + /* Prefetch next iteration. */ + { + vlib_buffer_t *p2, *p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header (p2, LOAD); + vlib_prefetch_buffer_header (p3, LOAD); + + CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE); + CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE); + } + + /* speculatively enqueue b0 and b1 to the current next frame */ + to_next[0] = bi0 = from[0]; + to_next[1] = bi1 = from[1]; + from += 2; + to_next += 2; + n_left_from -= 2; + n_left_to_next -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + vnet_feature_next (vnet_buffer (b0)->sw_if_index[VLIB_TX], + &next0, b0); + vnet_feature_next (vnet_buffer (b1)->sw_if_index[VLIB_TX], + &next1, b1); + + len0 = vlib_buffer_length_in_chain (vm, b0); + ethernet_header_t *eh0 = vlib_buffer_get_current (b0); + u16 ethertype0 = clib_net_to_host_u16 (eh0->type); + + if (PREDICT_TRUE ((b0->flags & VLIB_BUFFER_FLOW_REPORT) == 0)) + add_to_flow_record_state (vm, node, fm, b0, now, len0, + flowprobe_get_variant + (which, fm->context[which].flags, + ethertype0), 0); + + len1 = vlib_buffer_length_in_chain (vm, b1); + ethernet_header_t *eh1 = vlib_buffer_get_current (b1); + u16 ethertype1 = clib_net_to_host_u16 (eh1->type); + + if (PREDICT_TRUE ((b1->flags & VLIB_BUFFER_FLOW_REPORT) == 0)) + add_to_flow_record_state (vm, node, fm, b1, now, len1, + flowprobe_get_variant + (which, fm->context[which].flags, + ethertype1), 0); + + /* verify speculative enqueues, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, next0, next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t *b0; + u32 next0 = FLOWPROBE_NEXT_DROP; + u16 len0; + + /* speculatively enqueue b0 to the current next frame */ + 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); + + vnet_feature_next (vnet_buffer (b0)->sw_if_index[VLIB_TX], + &next0, b0); + + len0 = vlib_buffer_length_in_chain (vm, b0); + ethernet_header_t *eh0 = vlib_buffer_get_current (b0); + u16 ethertype0 = clib_net_to_host_u16 (eh0->type); + + if (PREDICT_TRUE ((b0->flags & VLIB_BUFFER_FLOW_REPORT) == 0)) + { + flowprobe_trace_t *t = 0; + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + t = vlib_add_trace (vm, node, b0, sizeof (*t)); + + add_to_flow_record_state (vm, node, fm, b0, now, len0, + flowprobe_get_variant + (which, fm->context[which].flags, + ethertype0), t); + } + + /* verify speculative enqueue, maybe switch current next frame */ + 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); + } + return frame->n_vectors; +} + +static uword +flowprobe_ip4_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return flowprobe_node_fn (vm, node, frame, FLOW_VARIANT_IP4); +} + +static uword +flowprobe_ip6_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return flowprobe_node_fn (vm, node, frame, FLOW_VARIANT_IP6); +} + +static uword +flowprobe_l2_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + return flowprobe_node_fn (vm, node, frame, FLOW_VARIANT_L2); +} + +static inline void +flush_record (flowprobe_variant_t which) +{ + vlib_main_t *vm = vlib_get_main (); + vlib_buffer_t *b = flowprobe_get_buffer (vm, which); + if (b) + flowprobe_export_send (vm, b, which); +} + +void +flowprobe_flush_callback_ip4 (void) +{ + flush_record (FLOW_VARIANT_IP4); +} + +void +flowprobe_flush_callback_ip6 (void) +{ + flush_record (FLOW_VARIANT_IP6); +} + +void +flowprobe_flush_callback_l2 (void) +{ + flush_record (FLOW_VARIANT_L2); + flush_record (FLOW_VARIANT_L2_IP4); + flush_record (FLOW_VARIANT_L2_IP6); +} + + +static void +flowprobe_delete_by_index (u32 my_cpu_number, u32 poolindex) +{ + flowprobe_main_t *fm = &flowprobe_main; + flowprobe_entry_t *e; + u32 h; + + e = pool_elt_at_index (fm->pool_per_worker[my_cpu_number], poolindex); + + /* Get my index */ + h = flowprobe_hash (&e->key); + + /* Reset hash */ + fm->hash_per_worker[my_cpu_number][h] = ~0; + + pool_put_index (fm->pool_per_worker[my_cpu_number], poolindex); +} + + +/* Per worker process processing the active/passive expired entries */ +static uword +flowprobe_walker_process (vlib_main_t * vm, + vlib_node_runtime_t * rt, vlib_frame_t * f) +{ + flowprobe_main_t *fm = &flowprobe_main; + flow_report_main_t *frm = &flow_report_main; + flowprobe_entry_t *e; + + /* + * $$$$ Remove this check from here and track FRM status and disable + * this process if required. + */ + if (frm->ipfix_collector.as_u32 == 0 || frm->src_address.as_u32 == 0) + { + fm->disabled = true; + return 0; + } + fm->disabled = false; + + u32 cpu_index = os_get_thread_index (); + u32 *to_be_removed = 0, *i; + + /* + * Tick the timer when required and process the vector of expired + * timers + */ + f64 start_time = vlib_time_now (vm); + u32 count = 0; + + tw_timer_expire_timers_2t_1w_2048sl (fm->timers_per_worker[cpu_index], + start_time); + + vec_foreach (i, fm->expired_passive_per_worker[cpu_index]) + { + u32 exported = 0; + f64 now = vlib_time_now (vm); + if (now > start_time + 100e-6 + || exported > FLOW_MAXIMUM_EXPORT_ENTRIES - 1) + break; + + if (pool_is_free_index (fm->pool_per_worker[cpu_index], *i)) + { + clib_warning ("Element is %d is freed already\n", *i); + continue; + } + else + e = pool_elt_at_index (fm->pool_per_worker[cpu_index], *i); + + /* Check last update timestamp. If it is longer than passive time nuke + * entry. Otherwise restart timer with what's left + * Premature passive timer by more than 10% + */ + if ((now - e->last_updated) < (fm->passive_timer * 0.9)) + { + f64 delta = fm->passive_timer - (now - e->last_updated); + e->passive_timer_handle = tw_timer_start_2t_1w_2048sl + (fm->timers_per_worker[cpu_index], *i, 0, delta); + } + else /* Nuke entry */ + { + vec_add1 (to_be_removed, *i); + } + /* If anything to report send it to the exporter */ + if (e->packetcount && now > e->last_exported + fm->active_timer) + { + exported++; + flowprobe_export_entry (vm, e); + } + count++; + } + if (count) + vec_delete (fm->expired_passive_per_worker[cpu_index], count, 0); + + vec_foreach (i, to_be_removed) flowprobe_delete_by_index (cpu_index, *i); + vec_free (to_be_removed); + + return 0; +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (flowprobe_ip4_node) = { + .function = flowprobe_ip4_node_fn, + .name = "flowprobe-ip4", + .vector_size = sizeof (u32), + .format_trace = format_flowprobe_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(flowprobe_error_strings), + .error_strings = flowprobe_error_strings, + .n_next_nodes = FLOWPROBE_N_NEXT, + .next_nodes = FLOWPROBE_NEXT_NODES, +}; +VLIB_REGISTER_NODE (flowprobe_ip6_node) = { + .function = flowprobe_ip6_node_fn, + .name = "flowprobe-ip6", + .vector_size = sizeof (u32), + .format_trace = format_flowprobe_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(flowprobe_error_strings), + .error_strings = flowprobe_error_strings, + .n_next_nodes = FLOWPROBE_N_NEXT, + .next_nodes = FLOWPROBE_NEXT_NODES, +}; +VLIB_REGISTER_NODE (flowprobe_l2_node) = { + .function = flowprobe_l2_node_fn, + .name = "flowprobe-l2", + .vector_size = sizeof (u32), + .format_trace = format_flowprobe_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(flowprobe_error_strings), + .error_strings = flowprobe_error_strings, + .n_next_nodes = FLOWPROBE_N_NEXT, + .next_nodes = FLOWPROBE_NEXT_NODES, +}; +VLIB_REGISTER_NODE (flowprobe_walker_node) = { + .function = flowprobe_walker_process, + .name = "flowprobe-walker", + .type = VLIB_NODE_TYPE_INPUT, + .state = VLIB_NODE_STATE_INTERRUPT, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/ioam/analyse/ioam_summary_export.c b/src/plugins/ioam/analyse/ioam_summary_export.c index 9a2667f6..af2d39ab 100644 --- a/src/plugins/ioam/analyse/ioam_summary_export.c +++ b/src/plugins/ioam/analyse/ioam_summary_export.c @@ -398,6 +398,7 @@ ioam_flow_create (u8 del) int rv; u32 domain_id = 0; flow_report_main_t *frm = &flow_report_main; + u16 template_id; memset (&args, 0, sizeof (args)); args.rewrite_callback = ioam_template_rewrite; @@ -405,7 +406,7 @@ ioam_flow_create (u8 del) del ? (args.is_add = 0) : (args.is_add = 1); args.domain_id = domain_id; - rv = vnet_flow_report_add_del (frm, &args); + rv = vnet_flow_report_add_del (frm, &args, &template_id); switch (rv) { diff --git a/src/plugins/ioam/udp-ping/udp_ping_export.c b/src/plugins/ioam/udp-ping/udp_ping_export.c index ce64b606..91cbb4bd 100644 --- a/src/plugins/ioam/udp-ping/udp_ping_export.c +++ b/src/plugins/ioam/udp-ping/udp_ping_export.c @@ -226,6 +226,7 @@ udp_ping_flow_create (u8 del) int rv; u32 domain_id = 0; flow_report_main_t *frm = &flow_report_main; + u16 template_id; memset (&args, 0, sizeof (args)); args.rewrite_callback = udp_ping_template_rewrite; @@ -234,7 +235,7 @@ udp_ping_flow_create (u8 del) args.domain_id = domain_id; args.src_port = UDP_DST_PORT_ipfix; - rv = vnet_flow_report_add_del (frm, &args); + rv = vnet_flow_report_add_del (frm, &args, &template_id); switch (rv) { diff --git a/src/plugins/snat/snat_ipfix_logging.c b/src/plugins/snat/snat_ipfix_logging.c index 1a111863..c68dc540 100644 --- a/src/plugins/snat/snat_ipfix_logging.c +++ b/src/plugins/snat/snat_ipfix_logging.c @@ -784,7 +784,7 @@ snat_ipfix_logging_enable_disable (int enable, u32 domain_id, u16 src_port) a.rewrite_callback = snat_template_rewrite_max_entries_per_usr; a.flow_data_callback = snat_data_callback_max_entries_per_usr; - rv = vnet_flow_report_add_del (frm, &a); + rv = vnet_flow_report_add_del (frm, &a, NULL); if (rv) { clib_warning ("vnet_flow_report_add_del returned %d", rv); @@ -796,7 +796,7 @@ snat_ipfix_logging_enable_disable (int enable, u32 domain_id, u16 src_port) a.rewrite_callback = snat_template_rewrite_nat44_session; a.flow_data_callback = snat_data_callback_nat44_session; - rv = vnet_flow_report_add_del (frm, &a); + rv = vnet_flow_report_add_del (frm, &a, NULL); if (rv) { clib_warning ("vnet_flow_report_add_del returned %d", rv); @@ -806,7 +806,7 @@ snat_ipfix_logging_enable_disable (int enable, u32 domain_id, u16 src_port) a.rewrite_callback = snat_template_rewrite_addr_exhausted; a.flow_data_callback = snat_data_callback_addr_exhausted; - rv = vnet_flow_report_add_del (frm, &a); + rv = vnet_flow_report_add_del (frm, &a, NULL); if (rv) { clib_warning ("vnet_flow_report_add_del returned %d", rv); diff --git a/src/vnet/flow/flow_api.c b/src/vnet/flow/flow_api.c index b975dda1..52a608ca 100644 --- a/src/vnet/flow/flow_api.c +++ b/src/vnet/flow/flow_api.c @@ -296,7 +296,7 @@ static void args.domain_id = fcm->domain_id; args.src_port = fcm->src_port; - rv = vnet_flow_report_add_del (frm, &args); + rv = vnet_flow_report_add_del (frm, &args, NULL); /* If deleting, or add failed */ if (is_add == 0 || (rv && is_add)) diff --git a/src/vnet/flow/flow_report.c b/src/vnet/flow/flow_report.c index c78a78a9..ccc84235 100644 --- a/src/vnet/flow/flow_report.c +++ b/src/vnet/flow/flow_report.c @@ -87,7 +87,6 @@ int send_template_packet (flow_report_main_t *frm, if (frm->ipfix_collector.as_u32 == 0 || frm->src_address.as_u32 == 0) { - clib_warning ("no collector: disabling flow collector process"); vlib_node_set_state (frm->vlib_main, flow_report_process_node.index, VLIB_NODE_STATE_DISABLED); return -1; @@ -238,7 +237,8 @@ VLIB_REGISTER_NODE (flow_report_process_node) = { }; int vnet_flow_report_add_del (flow_report_main_t *frm, - vnet_flow_report_add_del_args_t *a) + vnet_flow_report_add_del_args_t *a, + u16 *template_id) { int i; int found_index = ~0; @@ -260,6 +260,8 @@ int vnet_flow_report_add_del (flow_report_main_t *frm, && fr->flow_data_callback == a->flow_data_callback) { found_index = i; + if (template_id) + *template_id = fr->template_id; break; } } @@ -304,7 +306,10 @@ int vnet_flow_report_add_del (flow_report_main_t *frm, fr->opaque = a->opaque; fr->rewrite_callback = a->rewrite_callback; fr->flow_data_callback = a->flow_data_callback; - + + if (template_id) + *template_id = fr->template_id; + return 0; } @@ -415,10 +420,7 @@ set_ipfix_exporter_command_fn (vlib_main_t * vm, break; } - if (collector.as_u32 == 0) - return clib_error_return (0, "collector address required"); - - if (src.as_u32 == 0) + if (collector.as_u32 != 0 && src.as_u32 == 0) return clib_error_return (0, "src address required"); if (path_mtu > 1450 /* vpp does not support fragmentation */) @@ -441,7 +443,8 @@ set_ipfix_exporter_command_fn (vlib_main_t * vm, frm->template_interval = template_interval; frm->udp_checksum = udp_checksum; - vlib_cli_output (vm, "Collector %U, src address %U, " + if (collector.as_u32) + vlib_cli_output (vm, "Collector %U, src address %U, " "fib index %d, path MTU %u, " "template resend interval %us, " "udp checksum %s", @@ -449,6 +452,8 @@ set_ipfix_exporter_command_fn (vlib_main_t * vm, format_ip4_address, &frm->src_address, fib_index, path_mtu, template_interval, udp_checksum ? "enabled" : "disabled"); + else + vlib_cli_output (vm, "IPFIX Collector is disabled"); /* Turn on the flow reporting process */ vlib_process_signal_event (vm, flow_report_process_node.index, diff --git a/src/vnet/flow/flow_report.h b/src/vnet/flow/flow_report.h index e8ed3818..01859ce5 100644 --- a/src/vnet/flow/flow_report.h +++ b/src/vnet/flow/flow_report.h @@ -130,7 +130,8 @@ typedef struct { } vnet_flow_report_add_del_args_t; int vnet_flow_report_add_del (flow_report_main_t *frm, - vnet_flow_report_add_del_args_t *a); + vnet_flow_report_add_del_args_t *a, + u16 *template_id); clib_error_t * flow_report_add_del_error_to_clib_error (int error); diff --git a/src/vnet/flow/flow_report_classify.c b/src/vnet/flow/flow_report_classify.c index 27f03ddc..d4c30492 100644 --- a/src/vnet/flow/flow_report_classify.c +++ b/src/vnet/flow/flow_report_classify.c @@ -458,7 +458,7 @@ ipfix_classify_table_add_del_command_fn (vlib_main_t * vm, args.domain_id = fcm->domain_id; args.src_port = fcm->src_port; - rv = vnet_flow_report_add_del (frm, &args); + rv = vnet_flow_report_add_del (frm, &args, NULL); error = flow_report_add_del_error_to_clib_error(rv); diff --git a/src/vnet/ip/ip6_packet.h b/src/vnet/ip/ip6_packet.h index cdd7eed5..c0c745e2 100644 --- a/src/vnet/ip/ip6_packet.h +++ b/src/vnet/ip/ip6_packet.h @@ -341,6 +341,12 @@ typedef struct ip6_address_t src_address, dst_address; } ip6_header_t; +always_inline u8 +ip6_traffic_class (ip6_header_t * i) +{ + return (i->ip_version_traffic_class_and_flow_label & 0x0FF00000) >> 20; +} + always_inline void * ip6_next_header (ip6_header_t * i) { diff --git a/src/vpp-api/java/jvpp/gen/jvpp_gen.py b/src/vpp-api/java/jvpp/gen/jvpp_gen.py index 7932741d..6648a4f7 100755 --- a/src/vpp-api/java/jvpp/gen/jvpp_gen.py +++ b/src/vpp-api/java/jvpp/gen/jvpp_gen.py @@ -30,7 +30,7 @@ from jvppgen import jvpp_impl_gen from jvppgen import jvpp_c_gen from jvppgen import util -blacklist = [ "memclnt.api", "flowperpkt.api" ] +blacklist = [ "memclnt.api", "flowprobe.api" ] # Invocation: # ~/Projects/vpp/vpp-api/jvpp/gen$ mkdir -p java/io/fd/vpp/jvpp && cd java/io/fd/vpp/jvpp diff --git a/test/test_flowperpkt.py b/test/test_flowperpkt.py deleted file mode 100644 index b13d0c63..00000000 --- a/test/test_flowperpkt.py +++ /dev/null @@ -1,173 +0,0 @@ -#!/usr/bin/env python - -import unittest - -from framework import VppTestCase, VppTestRunner - -from scapy.packet import Raw -from scapy.layers.l2 import Ether -from scapy.layers.inet import IP, UDP - - -class TestFlowperpkt(VppTestCase): - """ Flow-per-packet plugin: test both L2 and IP4 reporting """ - - def setUp(self): - """ - Set up - - **Config:** - - create three PG interfaces - """ - super(TestFlowperpkt, self).setUp() - - self.create_pg_interfaces(range(3)) - - self.pg_if_packet_sizes = [150] - - self.interfaces = list(self.pg_interfaces) - - for intf in self.interfaces: - intf.admin_up() - intf.config_ip4() - intf.resolve_arp() - - def create_stream(self, src_if, dst_if, packet_sizes): - """Create a packet stream to tickle the plugin - - :param VppInterface src_if: Source interface for packet stream - :param VppInterface src_if: Dst interface for packet stream - :param list packet_sizes: Sizes to test - """ - pkts = [] - for size in packet_sizes: - info = self.create_packet_info(src_if, dst_if) - payload = self.info_to_payload(info) - p = (Ether(src=src_if.remote_mac, dst=src_if.local_mac) / - IP(src=src_if.remote_ip4, dst=dst_if.remote_ip4) / - UDP(sport=1234, dport=4321) / - Raw(payload)) - info.data = p.copy() - self.extend_packet(p, size) - pkts.append(p) - return pkts - - @staticmethod - def compare_with_mask(payload, masked_expected_data): - if len(payload) * 2 != len(masked_expected_data): - return False - - # iterate over pairs: raw byte from payload and ASCII code for that - # byte from masked payload (or XX if masked) - for i in range(len(payload)): - p = payload[i] - m = masked_expected_data[2 * i:2 * i + 2] - if m != "XX": - if "%02x" % ord(p) != m: - return False - return True - - def verify_ipfix(self, collector_if): - """Check the ipfix capture""" - found_data_packet = False - found_template_packet = False - found_l2_data_packet = False - found_l2_template_packet = False - - # Scapy, of course, understands ipfix not at all... - # These data vetted by manual inspection in wireshark - # X'ed out fields are timestamps, which will absolutely - # fail to compare. - - data_udp_string = "1283128300370000000a002fXXXXXXXX000000000000000101"\ - "00001f0000000100000002ac100102ac10020200XXXXXXXXXXXXXXXX0092" - - template_udp_string = "12831283003c0000000a0034XXXXXXXX00000002000000"\ - "010002002401000007000a0004000e000400080004000c000400050001009c00"\ - "0801380002" - - l2_data_udp_string = "12831283003c0000000a0034XXXXXXXX000000010000000"\ - "1010100240000000100000002%s02020000ff020008XXXXXXXXXXX"\ - "XXXXX0092" % self.pg1.local_mac.translate(None, ":") - - l2_template_udp_string = "12831283003c0000000a0034XXXXXXXX00000002000"\ - "000010002002401010007000a0004000e0004003800060050000601000002009"\ - "c000801380002" - - self.logger.info("Look for ipfix packets on %s sw_if_index %d " - % (collector_if.name, collector_if.sw_if_index)) - # expecting 4 packets on collector interface based on traffic on other - # interfaces - capture = collector_if.get_capture(4) - - for p in capture: - ip = p[IP] - udp = p[UDP] - self.logger.info("src %s dst %s" % (ip.src, ip.dst)) - self.logger.info(" udp src_port %s dst_port %s" - % (udp.sport, udp.dport)) - - payload = str(udp) - - if self.compare_with_mask(payload, data_udp_string): - self.logger.info("found ip4 data packet") - found_data_packet = True - elif self.compare_with_mask(payload, template_udp_string): - self.logger.info("found ip4 template packet") - found_template_packet = True - elif self.compare_with_mask(payload, l2_data_udp_string): - self.logger.info("found l2 data packet") - found_l2_data_packet = True - elif self.compare_with_mask(payload, l2_template_udp_string): - self.logger.info("found l2 template packet") - found_l2_template_packet = True - else: - unmasked_payload = "".join(["%02x" % ord(c) for c in payload]) - self.logger.error("unknown pkt '%s'" % unmasked_payload) - - self.assertTrue(found_data_packet, "Data packet not found") - self.assertTrue(found_template_packet, "Template packet not found") - self.assertTrue(found_l2_data_packet, "L2 data packet not found") - self.assertTrue(found_l2_template_packet, - "L2 template packet not found") - - def test_L3_fpp(self): - """ Flow per packet L3 test """ - - # Configure an ipfix report on the [nonexistent] collector - # 172.16.3.2, as if it was connected to the pg2 interface - # Install a FIB entry, so the exporter's work won't turn into - # an ARP request - - self.pg_enable_capture(self.pg_interfaces) - self.pg2.configure_ipv4_neighbors() - self.vapi.set_ipfix_exporter(collector_address=self.pg2.remote_ip4n, - src_address=self.pg2.local_ip4n, - path_mtu=1450, - template_interval=1) - - # Export flow records for all pkts transmitted on pg1 - self.vapi.cli("flowperpkt feature add-del pg1") - self.vapi.cli("flowperpkt feature add-del pg1 l2") - - # Arrange to minimally trace generated ipfix packets - self.vapi.cli("trace add flowperpkt-ipv4 10") - self.vapi.cli("trace add flowperpkt-l2 10") - - # Create a stream from pg0 -> pg1, which causes - # an ipfix packet to be transmitted on pg2 - - pkts = self.create_stream(self.pg0, self.pg1, - self.pg_if_packet_sizes) - self.pg0.add_stream(pkts) - self.pg_start() - - # Flush the ipfix collector, so we don't need any - # asinine time.sleep(5) action - self.vapi.cli("ipfix flush") - - # Make sure the 4 pkts we expect actually showed up - self.verify_ipfix(self.pg2) - -if __name__ == '__main__': - unittest.main(testRunner=VppTestRunner) diff --git a/test/test_flowprobe.py b/test/test_flowprobe.py new file mode 100644 index 00000000..560b44cc --- /dev/null +++ b/test/test_flowprobe.py @@ -0,0 +1,967 @@ +#!/usr/bin/env python +import random +import socket +import unittest +import time + +from scapy.packet import Raw +from scapy.layers.l2 import Ether +from scapy.layers.inet import IP, UDP +from scapy.layers.inet6 import IPv6 + +from framework import VppTestCase, VppTestRunner +from vpp_object import VppObject +from vpp_pg_interface import CaptureTimeoutError +from util import ppp +from ipfix import IPFIX, Set, Template, Data, IPFIXDecoder + + +class VppCFLOW(VppObject): + """CFLOW object for IPFIX exporter and Flowprobe feature""" + + def __init__(self, test, intf='pg2', active=0, passive=0, timeout=300, + mtu=512, datapath='l2', layer='l2 l3 l4'): + self._test = test + self._intf = intf + self._active = active + if passive == 0 or passive < active: + self._passive = active+5 + else: + self._passive = passive + self._datapath = datapath # l2 ip4 ip6 + self._collect = layer # l2 l3 l4 + self._timeout = timeout + self._mtu = mtu + self._configured = False + + def add_vpp_config(self): + self.enable_exporter() + self._test.vapi.ppcli("flowprobe params record %s active %s " + "passive %s" % (self._collect, self._active, + self._passive)) + self.enable_flowprobe_feature() + self._test.vapi.cli("ipfix flush") + self._configured = True + + def remove_vpp_config(self): + self.disable_exporter() + self.disable_flowprobe_feature() + self._test.vapi.cli("ipfix flush") + self._configured = False + + def enable_exporter(self): + self._test.vapi.set_ipfix_exporter( + collector_address=self._test.pg0.remote_ip4n, + src_address=self._test.pg0.local_ip4n, + path_mtu=self._mtu, + template_interval=self._timeout) + + def enable_flowprobe_feature(self): + self._test.vapi.ppcli("flowprobe feature add-del %s %s" % + (self._intf, self._datapath)) + + def disable_exporter(self): + self._test.vapi.cli("set ipfix exporter collector 0.0.0.0") + + def disable_flowprobe_feature(self): + self._test.vapi.cli("flowprobe feature add-del %s %s disable" % + (self._intf, self._datapath)) + + def object_id(self): + return "ipfix-collector-%s" % (self._src, self.dst) + + def query_vpp_config(self): + return self._configured + + def verify_templates(self, decoder=None, timeout=1, count=3): + templates = [] + p = self._test.wait_for_cflow_packet(self._test.collector, 2, timeout) + self._test.assertTrue(p.haslayer(IPFIX)) + if decoder is not None and p.haslayer(Template): + templates.append(p[Template].templateID) + decoder.add_template(p.getlayer(Template)) + if count > 1: + p = self._test.wait_for_cflow_packet(self._test.collector, 2) + self._test.assertTrue(p.haslayer(IPFIX)) + if decoder is not None and p.haslayer(Template): + templates.append(p[Template].templateID) + decoder.add_template(p.getlayer(Template)) + if count > 2: + p = self._test.wait_for_cflow_packet(self._test.collector, 2) + self._test.assertTrue(p.haslayer(IPFIX)) + if decoder is not None and p.haslayer(Template): + templates.append(p[Template].templateID) + decoder.add_template(p.getlayer(Template)) + return templates + + +class MethodHolder(VppTestCase): + """ Flow-per-packet plugin: test L2, IP4, IP6 reporting """ + + # Test variables + debug_print = False + max_number_of_packets = 16 + pkts = [] + + @classmethod + def setUpClass(cls): + """ + Perform standard class setup (defined by class method setUpClass in + class VppTestCase) before running the test case, set test case related + variables and configure VPP. + """ + super(MethodHolder, cls).setUpClass() + try: + # Create pg interfaces + cls.create_pg_interfaces(range(7)) + + # Packet sizes + cls.pg_if_packet_sizes = [64, 512, 1518, 9018] + + # Create BD with MAC learning and unknown unicast flooding disabled + # and put interfaces to this BD + cls.vapi.bridge_domain_add_del(bd_id=1, uu_flood=1, learn=1) + cls.vapi.sw_interface_set_l2_bridge(cls.pg1._sw_if_index, bd_id=1) + cls.vapi.sw_interface_set_l2_bridge(cls.pg2._sw_if_index, bd_id=1) + + # Set up all interfaces + for i in cls.pg_interfaces: + i.admin_up() + + cls.pg0.config_ip4() + cls.pg0.configure_ipv4_neighbors() + cls.collector = cls.pg0 + + cls.pg1.config_ip4() + cls.pg1.resolve_arp() + cls.pg2.config_ip4() + cls.pg2.resolve_arp() + cls.pg3.config_ip4() + cls.pg3.resolve_arp() + cls.pg4.config_ip4() + cls.pg4.resolve_arp() + + cls.pg5.config_ip6() + cls.pg5.resolve_ndp() + cls.pg5.disable_ipv6_ra() + cls.pg6.config_ip6() + cls.pg6.resolve_ndp() + cls.pg6.disable_ipv6_ra() + except Exception: + super(MethodHolder, cls).tearDownClass() + raise + + def create_stream(self, src_if=None, dst_if=None, packets=None, + size=None, ip_ver='v4'): + """Create a packet stream to tickle the plugin + + :param VppInterface src_if: Source interface for packet stream + :param VppInterface src_if: Dst interface for packet stream + """ + if src_if is None: + src_if = self.pg1 + if dst_if is None: + dst_if = self.pg2 + self.pkts = [] + if packets is None: + packets = random.randint(1, self.max_number_of_packets) + pkt_size = size + for p in range(0, packets): + if size is None: + pkt_size = random.choice(self.pg_if_packet_sizes) + info = self.create_packet_info(src_if, dst_if) + payload = self.info_to_payload(info) + p = Ether(src=src_if.remote_mac, dst=src_if.local_mac) + if ip_ver == 'v4': + p /= IP(src=src_if.remote_ip4, dst=dst_if.remote_ip4) + else: + p /= IPv6(src=src_if.remote_ip6, dst=dst_if.remote_ip6) + p /= (UDP(sport=1234, dport=4321) / Raw(payload)) + info.data = p.copy() + self.extend_packet(p, pkt_size) + self.pkts.append(p) + + def verify_cflow_data(self, decoder, capture, cflow): + octets = 0 + packets = 0 + for p in capture: + octets += p[IP].len + packets += 1 + if cflow.haslayer(Data): + data = decoder.decode_data_set(cflow.getlayer(Set)) + for record in data: + self.assertEqual(int(record[1].encode('hex'), 16), octets) + self.assertEqual(int(record[2].encode('hex'), 16), packets) + + def verify_cflow_data_detail(self, decoder, capture, cflow, + data_set={1: 'octets', 2: 'packets'}, + ip_ver='v4'): + if self.debug_print: + print capture[0].show() + if cflow.haslayer(Data): + data = decoder.decode_data_set(cflow.getlayer(Set)) + if self.debug_print: + print data + if ip_ver == 'v4': + ip_layer = capture[0][IP] + else: + ip_layer = capture[0][IPv6] + if data_set is not None: + for record in data: + # skip flow if in/out gress interface is 0 + if int(record[10].encode('hex'), 16) == 0: + continue + if int(record[14].encode('hex'), 16) == 0: + continue + + for field in data_set: + if field not in record.keys(): + continue + value = data_set[field] + if value == 'octets': + value = ip_layer.len + if ip_ver == 'v6': + value += 40 # ??? is this correct + elif value == 'packets': + value = 1 + elif value == 'src_ip': + if ip_ver == 'v4': + ip = socket.inet_pton(socket.AF_INET, + ip_layer.src) + else: + ip = socket.inet_pton(socket.AF_INET6, + ip_layer.src) + value = int(ip.encode('hex'), 16) + elif value == 'dst_ip': + if ip_ver == 'v4': + ip = socket.inet_pton(socket.AF_INET, + ip_layer.dst) + else: + ip = socket.inet_pton(socket.AF_INET6, + ip_layer.dst) + value = int(ip.encode('hex'), 16) + elif value == 'sport': + value = int(capture[0][UDP].sport) + elif value == 'dport': + value = int(capture[0][UDP].dport) + self.assertEqual(int(record[field].encode('hex'), 16), + value) + + def verify_cflow_data_notimer(self, decoder, capture, cflows): + idx = 0 + for cflow in cflows: + if cflow.haslayer(Data): + data = decoder.decode_data_set(cflow.getlayer(Set)) + else: + raise Exception("No CFLOW data") + + for rec in data: + p = capture[idx] + idx += 1 + self.assertEqual(p[IP].len, int(rec[1].encode('hex'), 16)) + self.assertEqual(1, int(rec[2].encode('hex'), 16)) + self.assertEqual(len(capture), idx) + + def wait_for_cflow_packet(self, collector_intf, set_id=2, timeout=1, + expected=True): + """ wait for CFLOW packet and verify its correctness + + :param timeout: how long to wait + + :returns: tuple (packet, time spent waiting for packet) + """ + self.logger.info("IPFIX: Waiting for CFLOW packet") + deadline = time.time() + timeout + counter = 0 + # self.logger.debug(self.vapi.ppcli("show flow table")) + while True: + counter += 1 + # sanity check + self.assert_in_range(counter, 0, 100, "number of packets ignored") + time_left = deadline - time.time() + try: + if time_left < 0 and expected: + # self.logger.debug(self.vapi.ppcli("show flow table")) + raise CaptureTimeoutError( + "Packet did not arrive within timeout") + p = collector_intf.wait_for_packet(timeout=time_left) + except CaptureTimeoutError: + if expected: + # self.logger.debug(self.vapi.ppcli("show flow table")) + raise CaptureTimeoutError( + "Packet did not arrive within timeout") + else: + return + if not expected: + raise CaptureTimeoutError("Packet arrived even not expected") + self.assertEqual(p[Set].setID, set_id) + # self.logger.debug(self.vapi.ppcli("show flow table")) + self.logger.debug(ppp("IPFIX: Got packet:", p)) + break + return p + + def send_packets(self, src_if=None, dst_if=None): + self.sleep(3) + if src_if is None: + src_if = self.pg1 + if dst_if is None: + dst_if = self.pg2 + self.pg_enable_capture([dst_if]) + src_if.add_stream(self.pkts) + self.pg_start() + return dst_if.get_capture(len(self.pkts)) + + +class TestFFP_Timers(MethodHolder): + """Template verification, timer tests""" + + def test_0001(self): + """ receive template data packets""" + self.logger.info("FFP_TEST_START_0001") + self.pg_enable_capture(self.pg_interfaces) + + ipfix = VppCFLOW(test=self, active=10) + ipfix.add_vpp_config() + + # template packet should arrive immediately + ipfix.verify_templates(timeout=3) + + ipfix.remove_vpp_config() + self.logger.info("FFP_TEST_FINISH_0001") + + def test_0002(self): + """ timer=10s, less than template timeout""" + self.logger.info("FFP_TEST_START_0002") + self.pg_enable_capture(self.pg_interfaces) + self.pkts = [] + + ipfix = VppCFLOW(test=self, active=20) + ipfix.add_vpp_config() + + ipfix_decoder = IPFIXDecoder() + # template packet should arrive immediately + templates = ipfix.verify_templates(ipfix_decoder, timeout=3) + + self.create_stream() + capture = self.send_packets() + + # make sure the one packet we expect actually showed up + cflow = self.wait_for_cflow_packet(self.collector, templates[1], 39) + self.verify_cflow_data(ipfix_decoder, capture, cflow) + + ipfix.remove_vpp_config() + self.logger.info("FFP_TEST_FINISH_0002") + + def test_0003(self): + """ timer=30s, greater than template timeout""" + self.logger.info("FFP_TEST_START_0003") + self.pg_enable_capture(self.pg_interfaces) + self.pkts = [] + + ipfix = VppCFLOW(test=self, timeout=20, active=25) + ipfix.add_vpp_config() + + ipfix_decoder = IPFIXDecoder() + # template packet should arrive immediately + ipfix.verify_templates(timeout=3) + + self.create_stream() + capture = self.send_packets() + + # next set of template packet should arrive after 20 seconds + # template packet should arrive within 20 s + templates = ipfix.verify_templates(ipfix_decoder, timeout=25) + + # make sure the one packet we expect actually showed up + cflow = self.wait_for_cflow_packet(self.collector, templates[1], 55) + self.verify_cflow_data(ipfix_decoder, capture, cflow) + + ipfix.remove_vpp_config() + self.logger.info("FFP_TEST_FINISH_0003") + + def test_0004(self): + """ sent packet after first cflow packet arrived""" + self.logger.info("FFP_TEST_START_0004") + self.pg_enable_capture(self.pg_interfaces) + self.pkts = [] + + ipfix = VppCFLOW(test=self, active=20) + ipfix.add_vpp_config() + + ipfix_decoder = IPFIXDecoder() + # template packet should arrive immediately + templates = ipfix.verify_templates(ipfix_decoder, timeout=3) + + self.create_stream() + self.send_packets() + + # make sure the one packet we expect actually showed up + self.wait_for_cflow_packet(self.collector, templates[1], 39) + + self.pg_enable_capture([self.pg2]) + + capture = self.send_packets() + + # make sure the one packet we expect actually showed up + cflow = self.wait_for_cflow_packet(self.collector, templates[1], 30) + self.verify_cflow_data(ipfix_decoder, capture, cflow) + + ipfix.remove_vpp_config() + self.logger.info("FFP_TEST_FINISH_0004") + + +class TestFFP_DatapathL2(MethodHolder): + """collect information on Ethernet datapath""" + + def test_0000(self): + """ verify template on L2 datapath""" + self.logger.info("FFP_TEST_START_0000") + self.pg_enable_capture(self.pg_interfaces) + + ipfix = VppCFLOW(test=self, active=10) + ipfix.add_vpp_config() + + # template packet should arrive immediately + ipfix.verify_templates(timeout=3, count=3) + self.collector.get_capture(3) + + ipfix.remove_vpp_config() + self.logger.info("FFP_TEST_FINISH_0000") + + def test_0001(self): + """ L2 data on L2 datapath""" + self.logger.info("FFP_TEST_START_0001") + self.pg_enable_capture(self.pg_interfaces) + self.pkts = [] + + ipfix = VppCFLOW(test=self, active=10, layer='l2') + ipfix.add_vpp_config() + + ipfix_decoder = IPFIXDecoder() + # template packet should arrive immediately + templates = ipfix.verify_templates(ipfix_decoder, timeout=3, count=1) + + self.create_stream(packets=1) + capture = self.send_packets() + + # make sure the one packet we expect actually showed up + cflow = self.wait_for_cflow_packet(self.collector, templates[0], 39) + self.verify_cflow_data_detail(ipfix_decoder, capture, cflow, + {2: 'packets', 256: 8}) + + # expected two templates and one cflow packet + self.collector.get_capture(2) + + ipfix.remove_vpp_config() + self.logger.info("FFP_TEST_FINISH_0001") + + def test_0002(self): + """ L3 data on L2 datapath""" + self.logger.info("FFP_TEST_START_0002") + self.pg_enable_capture(self.pg_interfaces) + self.pkts = [] + + ipfix = VppCFLOW(test=self, active=10, layer='l3') + ipfix.add_vpp_config() + + ipfix_decoder = IPFIXDecoder() + # template packet should arrive immediately + templates = ipfix.verify_templates(ipfix_decoder, timeout=3, count=2) + + self.create_stream(packets=1) + capture = self.send_packets() + + # make sure the one packet we expect actually showed up + cflow = self.wait_for_cflow_packet(self.collector, templates[0], 39) + self.verify_cflow_data_detail(ipfix_decoder, capture, cflow, + {2: 'packets', 4: 17, + 8: 'src_ip', 12: 'dst_ip'}) + + # expected one template and one cflow packet + self.collector.get_capture(3) + + ipfix.remove_vpp_config() + self.logger.info("FFP_TEST_FINISH_0002") + + def test_0003(self): + """ L4 data on L2 datapath""" + self.logger.info("FFP_TEST_START_0003") + self.pg_enable_capture(self.pg_interfaces) + self.pkts = [] + + ipfix = VppCFLOW(test=self, active=10, layer='l4') + ipfix.add_vpp_config() + + ipfix_decoder = IPFIXDecoder() + # template packet should arrive immediately + templates = ipfix.verify_templates(ipfix_decoder, timeout=3, count=2) + + self.create_stream(packets=1) + capture = self.send_packets() + + # make sure the one packet we expect actually showed up + cflow = self.wait_for_cflow_packet(self.collector, templates[0], 39) + self.verify_cflow_data_detail(ipfix_decoder, capture, cflow, + {2: 'packets', 7: 'sport', 11: 'dport'}) + + # expected one template and one cflow packet + self.collector.get_capture(3) + + ipfix.remove_vpp_config() + self.logger.info("FFP_TEST_FINISH_0003") + + +class TestFFP_DatapathIP4(MethodHolder): + """collect information on IP4 datapath""" + + def test_0000(self): + """ verify templates on IP4 datapath""" + self.logger.info("FFP_TEST_START_0000") + + self.pg_enable_capture(self.pg_interfaces) + + ipfix = VppCFLOW(test=self, active=10, datapath='ip4') + ipfix.add_vpp_config() + + # template packet should arrive immediately + ipfix.verify_templates(timeout=3, count=1) + self.collector.get_capture(1) + + ipfix.remove_vpp_config() + + self.logger.info("FFP_TEST_FINISH_0000") + + def test_0001(self): + """ L2 data on IP4 datapath""" + self.logger.info("FFP_TEST_START_0001") + self.pg_enable_capture(self.pg_interfaces) + self.pkts = [] + + ipfix = VppCFLOW(test=self, intf='pg4', active=10, + layer='l2', datapath='ip4') + ipfix.add_vpp_config() + + ipfix_decoder = IPFIXDecoder() + # template packet should arrive immediately + templates = ipfix.verify_templates(ipfix_decoder, timeout=3, count=1) + + self.create_stream(src_if=self.pg3, dst_if=self.pg4, packets=1) + capture = self.send_packets(src_if=self.pg3, dst_if=self.pg4) + + # make sure the one packet we expect actually showed up + cflow = self.wait_for_cflow_packet(self.collector, templates[0], 39) + self.verify_cflow_data_detail(ipfix_decoder, capture, cflow, + {2: 'packets', 256: 8}) + + # expected two templates and one cflow packet + self.collector.get_capture(2) + + ipfix.remove_vpp_config() + self.logger.info("FFP_TEST_FINISH_0001") + + def test_0002(self): + """ L3 data on IP4 datapath""" + self.logger.info("FFP_TEST_START_0002") + self.pg_enable_capture(self.pg_interfaces) + self.pkts = [] + + ipfix = VppCFLOW(test=self, intf='pg4', active=10, + layer='l3', datapath='ip4') + ipfix.add_vpp_config() + + ipfix_decoder = IPFIXDecoder() + # template packet should arrive immediately + templates = ipfix.verify_templates(ipfix_decoder, timeout=3, count=1) + + self.create_stream(src_if=self.pg3, dst_if=self.pg4, packets=1) + capture = self.send_packets(src_if=self.pg3, dst_if=self.pg4) + + # make sure the one packet we expect actually showed up + cflow = self.wait_for_cflow_packet(self.collector, templates[0], 39) + self.verify_cflow_data_detail(ipfix_decoder, capture, cflow, + {1: 'octets', 2: 'packets', + 8: 'src_ip', 12: 'dst_ip'}) + + # expected two templates and one cflow packet + self.collector.get_capture(2) + + ipfix.remove_vpp_config() + self.logger.info("FFP_TEST_FINISH_0002") + + def test_0003(self): + """ L4 data on IP4 datapath""" + self.logger.info("FFP_TEST_START_0003") + self.pg_enable_capture(self.pg_interfaces) + self.pkts = [] + + ipfix = VppCFLOW(test=self, intf='pg4', active=10, + layer='l4', datapath='ip4') + ipfix.add_vpp_config() + + ipfix_decoder = IPFIXDecoder() + # template packet should arrive immediately + templates = ipfix.verify_templates(ipfix_decoder, timeout=3, count=1) + + self.create_stream(src_if=self.pg3, dst_if=self.pg4, packets=1) + capture = self.send_packets(src_if=self.pg3, dst_if=self.pg4) + + # make sure the one packet we expect actually showed up + cflow = self.wait_for_cflow_packet(self.collector, templates[0], 39) + self.verify_cflow_data_detail(ipfix_decoder, capture, cflow, + {2: 'packets', 7: 'sport', 11: 'dport'}) + + # expected two templates and one cflow packet + self.collector.get_capture(2) + + ipfix.remove_vpp_config() + self.logger.info("FFP_TEST_FINISH_0003") + + +class TestFFP_DatapathIP6(MethodHolder): + """collect information on IP6 datapath""" + + def test_0000(self): + """ verify templates on IP6 datapath""" + self.logger.info("FFP_TEST_START_0000") + self.pg_enable_capture(self.pg_interfaces) + + ipfix = VppCFLOW(test=self, active=10, datapath='ip6') + ipfix.add_vpp_config() + + # template packet should arrive immediately + ipfix.verify_templates(timeout=3, count=1) + self.collector.get_capture(1) + + ipfix.remove_vpp_config() + + self.logger.info("FFP_TEST_FINISH_0000") + + def test_0001(self): + """ L2 data on IP6 datapath""" + self.logger.info("FFP_TEST_START_0001") + self.pg_enable_capture(self.pg_interfaces) + self.pkts = [] + + ipfix = VppCFLOW(test=self, intf='pg6', active=20, + layer='l2', datapath='ip6') + ipfix.add_vpp_config() + + ipfix_decoder = IPFIXDecoder() + # template packet should arrive immediately + templates = ipfix.verify_templates(ipfix_decoder, timeout=3, count=1) + + self.create_stream(src_if=self.pg5, dst_if=self.pg6, packets=1, + ip_ver='IPv6') + capture = self.send_packets(src_if=self.pg5, dst_if=self.pg6) + + # make sure the one packet we expect actually showed up + cflow = self.wait_for_cflow_packet(self.collector, templates[0], 39) + self.verify_cflow_data_detail(ipfix_decoder, capture, cflow, + {2: 'packets', 256: 56710}, + ip_ver='v6') + + # expected two templates and one cflow packet + self.collector.get_capture(2) + + ipfix.remove_vpp_config() + self.logger.info("FFP_TEST_FINISH_0001") + + def test_0002(self): + """ L3 data on IP6 datapath""" + self.logger.info("FFP_TEST_START_0002") + self.pg_enable_capture(self.pg_interfaces) + self.pkts = [] + + ipfix = VppCFLOW(test=self, intf='pg6', active=10, + layer='l3', datapath='ip6') + ipfix.add_vpp_config() + + ipfix_decoder = IPFIXDecoder() + # template packet should arrive immediately + templates = ipfix.verify_templates(ipfix_decoder, timeout=3, count=1) + + self.create_stream(src_if=self.pg5, dst_if=self.pg6, packets=1, + ip_ver='IPv6') + capture = self.send_packets(src_if=self.pg5, dst_if=self.pg6) + + # make sure the one packet we expect actually showed up + cflow = self.wait_for_cflow_packet(self.collector, templates[0], 39) + self.verify_cflow_data_detail(ipfix_decoder, capture, cflow, + {2: 'packets', + 27: 'src_ip', 28: 'dst_ip'}, + ip_ver='v6') + + # expected two templates and one cflow packet + self.collector.get_capture(2) + + ipfix.remove_vpp_config() + self.logger.info("FFP_TEST_FINISH_0002") + + def test_0003(self): + """ L4 data on IP6 datapath""" + self.logger.info("FFP_TEST_START_0003") + self.pg_enable_capture(self.pg_interfaces) + self.pkts = [] + + ipfix = VppCFLOW(test=self, intf='pg6', active=10, + layer='l4', datapath='ip6') + ipfix.add_vpp_config() + + ipfix_decoder = IPFIXDecoder() + # template packet should arrive immediately + templates = ipfix.verify_templates(ipfix_decoder, timeout=3, count=1) + + self.create_stream(src_if=self.pg5, dst_if=self.pg6, packets=1, + ip_ver='IPv6') + capture = self.send_packets(src_if=self.pg5, dst_if=self.pg6) + + # make sure the one packet we expect actually showed up + cflow = self.wait_for_cflow_packet(self.collector, templates[0], 39) + self.verify_cflow_data_detail(ipfix_decoder, capture, cflow, + {2: 'packets', 7: 'sport', 11: 'dport'}, + ip_ver='v6') + + # expected two templates and one cflow packet + self.collector.get_capture(2) + + ipfix.remove_vpp_config() + self.logger.info("FFP_TEST_FINISH_0003") + + +class TestFFP_NoTimers(MethodHolder): + """No timers""" + + def test_0001(self): + """ no timers, one CFLOW packet, 9 Flows inside""" + self.logger.info("FFP_TEST_START_0001") + self.pg_enable_capture(self.pg_interfaces) + self.pkts = [] + + ipfix = VppCFLOW(test=self, active=0) + ipfix.add_vpp_config() + + ipfix_decoder = IPFIXDecoder() + # template packet should arrive immediately + templates = ipfix.verify_templates(ipfix_decoder, timeout=3) + + self.create_stream(packets=9) + capture = self.send_packets() + + # make sure the one packet we expect actually showed up + cflow = self.wait_for_cflow_packet(self.collector, templates[1], 10) + self.verify_cflow_data_notimer(ipfix_decoder, capture, [cflow]) + self.wait_for_cflow_packet(self.collector, templates[1], 10, + expected=False) + self.collector.get_capture(4) + + ipfix.remove_vpp_config() + self.logger.info("FFP_TEST_FINISH_0001") + + def test_0002(self): + """ no timers, two CFLOW packets (mtu=256), 3 Flows in each""" + self.logger.info("FFP_TEST_START_0002") + self.pg_enable_capture(self.pg_interfaces) + self.pkts = [] + + ipfix = VppCFLOW(test=self, active=0, mtu=256) + ipfix.add_vpp_config() + + ipfix_decoder = IPFIXDecoder() + # template packet should arrive immediately + templates = ipfix.verify_templates(ipfix_decoder, timeout=3) + + self.create_stream(packets=6) + capture = self.send_packets() + + # make sure the one packet we expect actually showed up + cflows = [] + cflows.append(self.wait_for_cflow_packet(self.collector, + templates[1], 10)) + cflows.append(self.wait_for_cflow_packet(self.collector, + templates[1], 10)) + self.verify_cflow_data_notimer(ipfix_decoder, capture, cflows) + self.collector.get_capture(5) + + ipfix.remove_vpp_config() + self.logger.info("FFP_TEST_FINISH_0002") + + +class TestFFP_DisableIPFIX(MethodHolder): + """Disable IPFIX""" + + def test_0001(self): + """ disable IPFIX after first packets""" + self.logger.info("FFP_TEST_START_0001") + self.pg_enable_capture(self.pg_interfaces) + self.pkts = [] + + ipfix = VppCFLOW(test=self, active=10) + ipfix.add_vpp_config() + + ipfix_decoder = IPFIXDecoder() + # template packet should arrive immediately + templates = ipfix.verify_templates(ipfix_decoder, timeout=30) + + self.create_stream() + self.send_packets() + + # make sure the one packet we expect actually showed up + self.wait_for_cflow_packet(self.collector, templates[1], 30) + self.collector.get_capture(4) + + # disble IPFIX + ipfix.disable_exporter() + self.pg_enable_capture([self.collector]) + + self.send_packets() + + # make sure no one packet arrived in 1 minute + self.wait_for_cflow_packet(self.collector, templates[1], 30, + expected=False) + self.collector.get_capture(0) + + ipfix.remove_vpp_config() + self.logger.info("FFP_TEST_FINISH_0001") + + +class TestFFP_ReenableIPFIX(MethodHolder): + """Re-enable IPFIX""" + + def test_0001(self): + """ disable IPFIX after first packets and re-enable after few packets + """ + self.logger.info("FFP_TEST_START_0001") + self.pg_enable_capture(self.pg_interfaces) + self.pkts = [] + + ipfix = VppCFLOW(test=self, active=10) + ipfix.add_vpp_config() + + ipfix_decoder = IPFIXDecoder() + # template packet should arrive immediately + templates = ipfix.verify_templates(ipfix_decoder, timeout=3) + + self.create_stream() + self.send_packets() + + # make sure the one packet we expect actually showed up + self.wait_for_cflow_packet(self.collector, templates[1], 30) + self.collector.get_capture(4) + + # disble IPFIX + ipfix.disable_exporter() + self.pg_enable_capture([self.collector]) + + self.send_packets() + + # make sure no one packet arrived in active timer span + self.wait_for_cflow_packet(self.collector, templates[1], 30, + expected=False) + self.collector.get_capture(0) + + # enable IPFIX + ipfix.enable_exporter() + self.vapi.cli("ipfix flush") + templates = ipfix.verify_templates(ipfix_decoder, timeout=3) + + self.send_packets() + + # make sure the next packets (templates and data) we expect actually + # showed up + self.wait_for_cflow_packet(self.collector, templates[1], 30) + self.collector.get_capture(4) + + ipfix.remove_vpp_config() + self.logger.info("FFP_TEST_FINISH_0001") + + +class TestFFP_DisableFFP(MethodHolder): + """Disable Flowprobe feature""" + + def test_0001(self): + """ disable flowprobe feature after first packets""" + self.logger.info("FFP_TEST_START_0001") + self.pg_enable_capture(self.pg_interfaces) + self.pkts = [] + ipfix = VppCFLOW(test=self, active=10) + ipfix.add_vpp_config() + + ipfix_decoder = IPFIXDecoder() + # template packet should arrive immediately + templates = ipfix.verify_templates(ipfix_decoder, timeout=3) + + self.create_stream() + self.send_packets() + + # make sure the one packet we expect actually showed up + self.wait_for_cflow_packet(self.collector, templates[1], 30) + self.collector.get_capture(4) + + # disble IPFIX + ipfix.disable_flowprobe_feature() + self.pg_enable_capture([self.collector]) + + self.send_packets() + + # make sure no one packet arrived in active timer span + self.wait_for_cflow_packet(self.collector, templates[1], 30, + expected=False) + self.collector.get_capture(0) + + ipfix.remove_vpp_config() + self.logger.info("FFP_TEST_FINISH_0001") + + +class TestFFP_ReenableFFP(MethodHolder): + """Re-enable Flowprobe feature""" + + def test_0001(self): + """ disable flowprobe feature after first packets and re-enable + after few packets """ + self.logger.info("FFP_TEST_START_0001") + self.pg_enable_capture(self.pg_interfaces) + self.pkts = [] + + ipfix = VppCFLOW(test=self, active=10) + ipfix.add_vpp_config() + + ipfix_decoder = IPFIXDecoder() + # template packet should arrive immediately + templates = ipfix.verify_templates(ipfix_decoder, timeout=3) + + self.create_stream() + self.send_packets() + + # make sure the one packet we expect actually showed up + self.wait_for_cflow_packet(self.collector, templates[1], 30) + self.collector.get_capture(4) + + # disble FPP feature + ipfix.disable_flowprobe_feature() + self.pg_enable_capture([self.collector]) + + self.send_packets() + + # make sure no one packet arrived in active timer span + self.wait_for_cflow_packet(self.collector, templates[1], 30, + expected=False) + self.collector.get_capture(0) + + # enable FPP feature + ipfix.enable_flowprobe_feature() + self.vapi.cli("ipfix flush") + templates = ipfix.verify_templates(ipfix_decoder, timeout=3) + + self.send_packets() + + # make sure the next packets (templates and data) we expect actually + # showed up + self.wait_for_cflow_packet(self.collector, templates[1], 30) + self.collector.get_capture(4) + + ipfix.remove_vpp_config() + self.logger.info("FFP_TEST_FINISH_0001") + + +if __name__ == '__main__': + unittest.main(testRunner=VppTestRunner) -- cgit 1.2.3-korg From 84f28824763031c4be3f33aed2289a1e2a32d4c4 Mon Sep 17 00:00:00 2001 From: Matej Perina Date: Tue, 18 Jul 2017 13:29:15 +0200 Subject: jvpp: moving current tests to separate folder marked as examples Change-Id: Ib1a13e0a6cba69aba7a26e1bd52f4c55c4ccc027 Signed-off-by: Matej Perina --- .../vpp/jvpp/acl/examples/AclExpectedDumpData.java | 135 ++++++++++++++ .../io/fd/vpp/jvpp/acl/examples/AclTestData.java | 101 +++++++++++ .../fd/vpp/jvpp/acl/examples/AclTestRequests.java | 141 +++++++++++++++ .../fd/vpp/jvpp/acl/examples/FutureApiExample.java | 68 +++++++ .../io/fd/vpp/jvpp/acl/examples/Readme.txt | 1 + .../fd/vpp/jvpp/acl/test/AclExpectedDumpData.java | 135 -------------- .../io/fd/vpp/jvpp/acl/test/AclTestData.java | 101 ----------- .../io/fd/vpp/jvpp/acl/test/AclTestRequests.java | 141 --------------- .../io/fd/vpp/jvpp/acl/test/FutureApiTest.java | 68 ------- .../jvpp-acl/io/fd/vpp/jvpp/acl/test/Readme.txt | 1 - .../vpp/jvpp/core/examples/CallbackApiExample.java | 100 +++++++++++ .../core/examples/CallbackJVppFacadeExample.java | 110 ++++++++++++ .../CallbackJVppFacadeNotificationExample.java | 87 +++++++++ .../examples/CallbackNotificationApiExample.java | 94 ++++++++++ .../core/examples/CreateSubInterfaceExample.java | 121 +++++++++++++ .../vpp/jvpp/core/examples/FutureApiExample.java | 127 ++++++++++++++ .../examples/FutureApiNotificationExample.java | 55 ++++++ .../io/fd/vpp/jvpp/core/examples/L2AclExample.java | 195 +++++++++++++++++++++ .../jvpp/core/examples/LispAdjacencyExample.java | 125 +++++++++++++ .../vpp/jvpp/core/examples/NotificationUtils.java | 53 ++++++ .../io/fd/vpp/jvpp/core/examples/Readme.txt | 16 ++ .../io/fd/vpp/jvpp/core/test/CallbackApiTest.java | 100 ----------- .../test/CallbackJVppFacadeNotificationTest.java | 87 --------- .../vpp/jvpp/core/test/CallbackJVppFacadeTest.java | 110 ------------ .../core/test/CallbackNotificationApiTest.java | 94 ---------- .../io/fd/vpp/jvpp/core/test/ControlPingTest.java | 68 ------- .../vpp/jvpp/core/test/CreateSubInterfaceTest.java | 121 ------------- .../jvpp/core/test/FutureApiNotificationTest.java | 55 ------ .../io/fd/vpp/jvpp/core/test/FutureApiTest.java | 127 -------------- .../io/fd/vpp/jvpp/core/test/L2AclTest.java | 195 --------------------- .../fd/vpp/jvpp/core/test/LispAdjacencyTest.java | 125 ------------- .../fd/vpp/jvpp/core/test/NotificationUtils.java | 53 ------ .../jvpp-core/io/fd/vpp/jvpp/core/test/Readme.txt | 17 -- .../ioamexport/examples/IoamExportApiExample.java | 56 ++++++ .../io/fd/vpp/jvpp/ioamexport/examples/Readme.txt | 1 + .../jvpp/ioamexport/test/IoamExportApiTest.java | 56 ------ .../io/fd/vpp/jvpp/ioamexport/test/Readme.txt | 1 - .../jvpp/ioampot/examples/IoamPotApiExample.java | 76 ++++++++ .../io/fd/vpp/jvpp/ioampot/examples/Readme.txt | 1 + .../fd/vpp/jvpp/ioampot/test/IoamPotApiTest.java | 76 -------- .../io/fd/vpp/jvpp/ioampot/test/Readme.txt | 1 - .../ioamtrace/examples/IoamTraceApiExample.java | 77 ++++++++ .../io/fd/vpp/jvpp/ioamtrace/examples/Readme.txt | 1 + .../vpp/jvpp/ioamtrace/test/IoamTraceApiTest.java | 77 -------- .../io/fd/vpp/jvpp/ioamtrace/test/Readme.txt | 1 - .../vpp/jvpp/snat/examples/CallbackApiExample.java | 68 +++++++ .../io/fd/vpp/jvpp/snat/examples/Readme.txt | 1 + .../io/fd/vpp/jvpp/snat/test/CallbackApiTest.java | 68 ------- .../jvpp-snat/io/fd/vpp/jvpp/snat/test/Readme.txt | 1 - 49 files changed, 1810 insertions(+), 1879 deletions(-) create mode 100644 src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclExpectedDumpData.java create mode 100644 src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclTestData.java create mode 100644 src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclTestRequests.java create mode 100644 src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/FutureApiExample.java create mode 100644 src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/Readme.txt delete mode 100644 src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/AclExpectedDumpData.java delete mode 100644 src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/AclTestData.java delete mode 100644 src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/AclTestRequests.java delete mode 100644 src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/FutureApiTest.java delete mode 100644 src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/Readme.txt create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackApiExample.java create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackJVppFacadeExample.java create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackJVppFacadeNotificationExample.java create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackNotificationApiExample.java create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CreateSubInterfaceExample.java create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiExample.java create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiNotificationExample.java create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/L2AclExample.java create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/LispAdjacencyExample.java create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/NotificationUtils.java create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/Readme.txt delete mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackApiTest.java delete mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackJVppFacadeNotificationTest.java delete mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackJVppFacadeTest.java delete mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackNotificationApiTest.java delete mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/ControlPingTest.java delete mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CreateSubInterfaceTest.java delete mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiNotificationTest.java delete mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java delete mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/L2AclTest.java delete mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/LispAdjacencyTest.java delete mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/NotificationUtils.java delete mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/Readme.txt create mode 100644 src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/examples/IoamExportApiExample.java create mode 100644 src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/examples/Readme.txt delete mode 100644 src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/IoamExportApiTest.java delete mode 100644 src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/Readme.txt create mode 100644 src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/examples/IoamPotApiExample.java create mode 100644 src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/examples/Readme.txt delete mode 100644 src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/IoamPotApiTest.java delete mode 100644 src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/Readme.txt create mode 100644 src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/examples/IoamTraceApiExample.java create mode 100644 src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/examples/Readme.txt delete mode 100644 src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/IoamTraceApiTest.java delete mode 100644 src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/Readme.txt create mode 100644 src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/examples/CallbackApiExample.java create mode 100644 src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/examples/Readme.txt delete mode 100644 src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/test/CallbackApiTest.java delete mode 100644 src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/test/Readme.txt (limited to 'src/vpp-api/java') diff --git a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclExpectedDumpData.java b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclExpectedDumpData.java new file mode 100644 index 00000000..4806052f --- /dev/null +++ b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclExpectedDumpData.java @@ -0,0 +1,135 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.acl.examples; + + +import static io.fd.vpp.jvpp.acl.examples.AclTestData.FIRST_RULE_ADDRESS_2_AS_ARRAY; +import static io.fd.vpp.jvpp.acl.examples.AclTestData.FIRST_RULE_ADDRESS_AS_ARRAY; +import static io.fd.vpp.jvpp.acl.examples.AclTestData.FIRST_RULE_DST_ICMP_TYPE_END; +import static io.fd.vpp.jvpp.acl.examples.AclTestData.FIRST_RULE_DST_ICMP_TYPE_START; +import static io.fd.vpp.jvpp.acl.examples.AclTestData.FIRST_RULE_MAC; +import static io.fd.vpp.jvpp.acl.examples.AclTestData.FIRST_RULE_MAC_MASK; +import static io.fd.vpp.jvpp.acl.examples.AclTestData.FIRST_RULE_PREFIX; +import static io.fd.vpp.jvpp.acl.examples.AclTestData.FIRST_RULE_PREFIX_2; +import static io.fd.vpp.jvpp.acl.examples.AclTestData.FIRST_RULE_SRC_ICMP_TYPE_END; +import static io.fd.vpp.jvpp.acl.examples.AclTestData.FIRST_RULE_SRC_ICMP_TYPE_START; +import static io.fd.vpp.jvpp.acl.examples.AclTestData.ICMP_PROTOCOL; +import static io.fd.vpp.jvpp.acl.examples.AclTestData.SECOND_RULE_ADDRESS_2_AS_ARRAY; +import static io.fd.vpp.jvpp.acl.examples.AclTestData.SECOND_RULE_ADDRESS_AS_ARRAY; +import static io.fd.vpp.jvpp.acl.examples.AclTestData.SECOND_RULE_DST_PORT_RANGE_END; +import static io.fd.vpp.jvpp.acl.examples.AclTestData.SECOND_RULE_DST_PORT_RANGE_START; +import static io.fd.vpp.jvpp.acl.examples.AclTestData.SECOND_RULE_MAC; +import static io.fd.vpp.jvpp.acl.examples.AclTestData.SECOND_RULE_MAC_MASK; +import static io.fd.vpp.jvpp.acl.examples.AclTestData.SECOND_RULE_PREFIX; +import static io.fd.vpp.jvpp.acl.examples.AclTestData.SECOND_RULE_PREFIX_2; +import static io.fd.vpp.jvpp.acl.examples.AclTestData.SECOND_RULE_SRC_PORT_RANGE_END; +import static io.fd.vpp.jvpp.acl.examples.AclTestData.SECOND_RULE_SRC_PORT_RANGE_START; +import static io.fd.vpp.jvpp.acl.examples.AclTestData.UDP_PROTOCOL; + +import io.fd.vpp.jvpp.acl.dto.AclDetails; +import io.fd.vpp.jvpp.acl.dto.AclInterfaceListDetails; +import io.fd.vpp.jvpp.acl.dto.MacipAclDetails; +import io.fd.vpp.jvpp.acl.types.AclRule; +import io.fd.vpp.jvpp.acl.types.MacipAclRule; +import java.util.Arrays; + +class AclExpectedDumpData { + + static void verifyMacIpDump(final MacipAclDetails macipAclDetails) { + // asserting data create by previous call + assertEquals(0, macipAclDetails.aclIndex); + assertEquals(2, macipAclDetails.count); + + final MacipAclRule currentIpv4Rule = macipAclDetails.r[0]; + final MacipAclRule currentIpv6Rule = macipAclDetails.r[1]; + + // Comparing one property at the time to better pointer if something is wrong + //Ipv4 rule + assertEquals(0, currentIpv4Rule.isIpv6); + assertEquals(1, currentIpv4Rule.isPermit); + + // cutting expected ipv4 to 4 bytes,vpp sends it as 16 always + assertArrays(FIRST_RULE_ADDRESS_AS_ARRAY, Arrays.copyOfRange(currentIpv4Rule.srcIpAddr, 0, 4)); + assertEquals(FIRST_RULE_PREFIX, currentIpv4Rule.srcIpPrefixLen); + assertArrays(FIRST_RULE_MAC, currentIpv4Rule.srcMac); + assertArrays(FIRST_RULE_MAC_MASK, currentIpv4Rule.srcMacMask); + + //Ipv6 rule + assertEquals(1, currentIpv6Rule.isIpv6); + assertEquals(0, currentIpv6Rule.isPermit); + assertArrays(SECOND_RULE_ADDRESS_AS_ARRAY, currentIpv6Rule.srcIpAddr); + assertEquals(SECOND_RULE_PREFIX, currentIpv6Rule.srcIpPrefixLen); + assertArrays(SECOND_RULE_MAC, currentIpv6Rule.srcMac); + assertArrays(SECOND_RULE_MAC_MASK, currentIpv6Rule.srcMacMask); + } + + static void verifyAclDump(final AclDetails aclDetails) { + assertEquals(0, aclDetails.aclIndex); + assertEquals(2, aclDetails.count); + + final AclRule currentIpv4Rule = aclDetails.r[0]; + final AclRule currentIpv6Rule = aclDetails.r[1]; + + // Comparing one property at the time to better pointer if something is wrong + //Ipv4 rule + assertEquals(0, currentIpv4Rule.isIpv6); + assertEquals(1, currentIpv4Rule.isPermit); + + // cutting expected ipv4 to 4 bytes,vpp sends it as 16 always + assertArrays(FIRST_RULE_ADDRESS_AS_ARRAY, Arrays.copyOfRange(currentIpv4Rule.srcIpAddr, 0, 4)); + assertEquals(FIRST_RULE_PREFIX, currentIpv4Rule.srcIpPrefixLen); + assertArrays(FIRST_RULE_ADDRESS_2_AS_ARRAY, Arrays.copyOfRange(currentIpv4Rule.dstIpAddr, 0, 4)); + assertEquals(FIRST_RULE_PREFIX_2, currentIpv4Rule.dstIpPrefixLen); + + assertEquals(ICMP_PROTOCOL, currentIpv4Rule.proto); + assertEquals(FIRST_RULE_SRC_ICMP_TYPE_START, currentIpv4Rule.srcportOrIcmptypeFirst); + assertEquals(FIRST_RULE_SRC_ICMP_TYPE_END, currentIpv4Rule.srcportOrIcmptypeLast); + assertEquals(FIRST_RULE_DST_ICMP_TYPE_START, currentIpv4Rule.dstportOrIcmpcodeFirst); + assertEquals(FIRST_RULE_DST_ICMP_TYPE_END, currentIpv4Rule.dstportOrIcmpcodeLast); + + assertArrays(SECOND_RULE_ADDRESS_AS_ARRAY, currentIpv6Rule.srcIpAddr); + assertEquals(SECOND_RULE_PREFIX, currentIpv6Rule.srcIpPrefixLen); + assertArrays(SECOND_RULE_ADDRESS_2_AS_ARRAY, currentIpv6Rule.dstIpAddr); + assertEquals(SECOND_RULE_PREFIX_2, currentIpv6Rule.dstIpPrefixLen); + + assertEquals(UDP_PROTOCOL, currentIpv6Rule.proto); + assertEquals(SECOND_RULE_SRC_PORT_RANGE_START, currentIpv6Rule.srcportOrIcmptypeFirst); + assertEquals(SECOND_RULE_SRC_PORT_RANGE_END, currentIpv6Rule.srcportOrIcmptypeLast); + assertEquals(SECOND_RULE_DST_PORT_RANGE_START, currentIpv6Rule.dstportOrIcmpcodeFirst); + assertEquals(SECOND_RULE_DST_PORT_RANGE_END, currentIpv6Rule.dstportOrIcmpcodeLast); + } + + static void verifyAclInterfaceList(final AclInterfaceListDetails aclInterfaceListDetails) { + assertEquals(1, aclInterfaceListDetails.count); + assertEquals(1, aclInterfaceListDetails.acls[0]); + assertEquals(0, aclInterfaceListDetails.nInput); + assertEquals(0, aclInterfaceListDetails.swIfIndex); + } + + private static void assertArrays(final byte[] expected, final byte[] actual) { + if (!Arrays.equals(expected, actual)) { + throw new IllegalArgumentException( + String.format("Expected[%s]/Actual[%s]", Arrays.toString(expected), Arrays.toString(actual))); + } + } + + private static void assertEquals(final int expected, final int actual) { + if (expected != actual) { + throw new IllegalArgumentException(String.format("Expected[%s]/Actual[%s]", expected, actual)); + } + } +} diff --git a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclTestData.java b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclTestData.java new file mode 100644 index 00000000..199b1b6b --- /dev/null +++ b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclTestData.java @@ -0,0 +1,101 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.acl.examples; + + +import io.fd.vpp.jvpp.acl.types.AclRule; +import io.fd.vpp.jvpp.acl.types.MacipAclRule; + +class AclTestData { + + static final byte[] FIRST_RULE_ADDRESS_AS_ARRAY = {-64, -88, 2, 1}; + static final byte[] FIRST_RULE_ADDRESS_2_AS_ARRAY = {-64, -88, 2, 3}; + static final byte[] SECOND_RULE_ADDRESS_AS_ARRAY = + {32, 1, 13, -72, 10, 11, 18, -16, 0, 0, 0, 0, 0, 0, 0, 1}; + static final byte[] SECOND_RULE_ADDRESS_2_AS_ARRAY = + {32, 1, 13, -72, 10, 11, 18, -16, 0, 0, 0, 0, 0, 0, 0, 1}; + static final byte[] FIRST_RULE_MAC = {11, 11, 11, 11, 11, 11}; + static final byte[] FIRST_RULE_MAC_MASK = {0, 0, 0, 0, 0, 0}; + static final byte[] SECOND_RULE_MAC = {11, 12, 11, 11, 12, 11}; + static final byte[] SECOND_RULE_MAC_MASK = {(byte) 170, 0, 0, 0, 0, 0}; + static final int FIRST_RULE_PREFIX = 32; + static final int FIRST_RULE_PREFIX_2 = 24; + static final int SECOND_RULE_PREFIX = 64; + static final int SECOND_RULE_PREFIX_2 = 62; + static final int FIRST_RULE_DST_ICMP_TYPE_START = 0; + static final int FIRST_RULE_DST_ICMP_TYPE_END = 8; + static final int FIRST_RULE_SRC_ICMP_TYPE_START = 1; + static final int FIRST_RULE_SRC_ICMP_TYPE_END = 7; + static final int ICMP_PROTOCOL = 1; + static final int SECOND_RULE_DST_PORT_RANGE_START = 2000; + static final int SECOND_RULE_DST_PORT_RANGE_END = 6000; + static final int SECOND_RULE_SRC_PORT_RANGE_START = 400; + static final int SECOND_RULE_SRC_PORT_RANGE_END = 2047; + static final int UDP_PROTOCOL = 17; + + + static MacipAclRule[] createMacipRules() { + MacipAclRule ruleOne = new MacipAclRule(); + ruleOne.isIpv6 = 0; + ruleOne.isPermit = 1; + ruleOne.srcIpAddr = FIRST_RULE_ADDRESS_AS_ARRAY; + ruleOne.srcIpPrefixLen = FIRST_RULE_PREFIX; + ruleOne.srcMac = FIRST_RULE_MAC; + ruleOne.srcMacMask = FIRST_RULE_MAC_MASK;// no mask + + MacipAclRule ruleTwo = new MacipAclRule(); + ruleTwo.isIpv6 = 1; + ruleTwo.isPermit = 0; + ruleTwo.srcIpAddr = SECOND_RULE_ADDRESS_AS_ARRAY; + ruleTwo.srcIpPrefixLen = SECOND_RULE_PREFIX; + ruleTwo.srcMac = SECOND_RULE_MAC; + ruleTwo.srcMacMask = SECOND_RULE_MAC_MASK; + + return new MacipAclRule[]{ruleOne, ruleTwo}; + } + + static AclRule[] createAclRules() { + AclRule ruleOne = new AclRule(); + + ruleOne.isIpv6 = 0; + ruleOne.isPermit = 1; + ruleOne.srcIpAddr = FIRST_RULE_ADDRESS_AS_ARRAY; + ruleOne.srcIpPrefixLen = FIRST_RULE_PREFIX; + ruleOne.dstIpAddr = FIRST_RULE_ADDRESS_2_AS_ARRAY; + ruleOne.dstIpPrefixLen = FIRST_RULE_PREFIX_2; + ruleOne.dstportOrIcmpcodeFirst = FIRST_RULE_DST_ICMP_TYPE_START; + ruleOne.dstportOrIcmpcodeLast = FIRST_RULE_DST_ICMP_TYPE_END; + ruleOne.srcportOrIcmptypeFirst = FIRST_RULE_SRC_ICMP_TYPE_START; + ruleOne.srcportOrIcmptypeLast = FIRST_RULE_SRC_ICMP_TYPE_END; + ruleOne.proto = ICMP_PROTOCOL; //ICMP + + AclRule ruleTwo = new AclRule(); + ruleTwo.isIpv6 = 1; + ruleTwo.isPermit = 0; + ruleTwo.srcIpAddr = SECOND_RULE_ADDRESS_AS_ARRAY; + ruleTwo.srcIpPrefixLen = SECOND_RULE_PREFIX; + ruleTwo.dstIpAddr = SECOND_RULE_ADDRESS_2_AS_ARRAY; + ruleTwo.dstIpPrefixLen = SECOND_RULE_PREFIX_2; + ruleTwo.dstportOrIcmpcodeFirst = SECOND_RULE_DST_PORT_RANGE_START; + ruleTwo.dstportOrIcmpcodeLast = SECOND_RULE_DST_PORT_RANGE_END; + ruleTwo.srcportOrIcmptypeFirst = SECOND_RULE_SRC_PORT_RANGE_START; + ruleTwo.srcportOrIcmptypeLast = SECOND_RULE_SRC_PORT_RANGE_END; + ruleTwo.proto = UDP_PROTOCOL; //UDP + + return new AclRule[]{ruleOne, ruleTwo}; + } +} diff --git a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclTestRequests.java b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclTestRequests.java new file mode 100644 index 00000000..1fa4187e --- /dev/null +++ b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclTestRequests.java @@ -0,0 +1,141 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.acl.examples; + +import static io.fd.vpp.jvpp.acl.examples.AclTestData.createAclRules; +import static io.fd.vpp.jvpp.acl.examples.AclTestData.createMacipRules; + +import io.fd.vpp.jvpp.VppInvocationException; +import io.fd.vpp.jvpp.acl.dto.AclAddReplace; +import io.fd.vpp.jvpp.acl.dto.AclAddReplaceReply; +import io.fd.vpp.jvpp.acl.dto.AclDel; +import io.fd.vpp.jvpp.acl.dto.AclDelReply; +import io.fd.vpp.jvpp.acl.dto.AclDetailsReplyDump; +import io.fd.vpp.jvpp.acl.dto.AclDump; +import io.fd.vpp.jvpp.acl.dto.AclInterfaceListDetailsReplyDump; +import io.fd.vpp.jvpp.acl.dto.AclInterfaceListDump; +import io.fd.vpp.jvpp.acl.dto.AclInterfaceSetAclList; +import io.fd.vpp.jvpp.acl.dto.AclInterfaceSetAclListReply; +import io.fd.vpp.jvpp.acl.dto.MacipAclAdd; +import io.fd.vpp.jvpp.acl.dto.MacipAclAddReply; +import io.fd.vpp.jvpp.acl.dto.MacipAclDel; +import io.fd.vpp.jvpp.acl.dto.MacipAclDelReply; +import io.fd.vpp.jvpp.acl.dto.MacipAclDetailsReplyDump; +import io.fd.vpp.jvpp.acl.dto.MacipAclDump; +import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; +import java.util.concurrent.ExecutionException; + +class AclTestRequests { + + static MacipAclDetailsReplyDump sendMacIpDumpRequest(final FutureJVppAclFacade jvpp) + throws ExecutionException, InterruptedException { + System.out.println("Sending MacipAclDump request..."); + MacipAclDetailsReplyDump dump = jvpp.macipAclDump(new MacipAclDump()).toCompletableFuture().get(); + System.out.println("MacipAclDump returned"); + return dump; + } + + static void sendMacIpAddRequest(final FutureJVppAclFacade jvpp) throws InterruptedException, ExecutionException { + final MacipAclAdd request = createMacIpAddRequest(); + System.out.printf("Sending MacipAclAdd request %s%n", request.toString()); + final MacipAclAddReply reply = jvpp.macipAclAdd(createMacIpAddRequest()).toCompletableFuture().get(); + System.out.printf("MacipAclAdd send result = %s%n", reply); + } + + static void sendMacIpDelRequest(final FutureJVppAclFacade jvpp) throws InterruptedException, ExecutionException { + final MacipAclDel request = new MacipAclDel(); + request.aclIndex = 0; + System.out.printf("Sending MacipAclDel request %s%n", request.toString()); + final MacipAclDelReply reply = jvpp.macipAclDel(request).toCompletableFuture().get(); + System.out.printf("MacipAclDel send result = %s%n", reply); + } + + static void sendAclAddRequest(final FutureJVppAclFacade jvpp) throws InterruptedException, ExecutionException { + final AclAddReplace request = createAclAddRequest(); + System.out.printf("Sending AclAddReplace request %s%n", request.toString()); + final AclAddReplaceReply reply = jvpp.aclAddReplace(request).toCompletableFuture().get(); + System.out.printf("AclAddReplace send result = %s%n", reply); + } + + static AclDetailsReplyDump sendAclDumpRequest(final FutureJVppAclFacade jvpp) + throws InterruptedException, VppInvocationException, ExecutionException { + System.out.println("Sending AclDump request..."); + final AclDetailsReplyDump dump = jvpp.aclDump(new AclDump()).toCompletableFuture().get(); + System.out.printf("AclDump send result = %s%n", dump); + return dump; + } + + static void sendAclDelRequest(final FutureJVppAclFacade jvpp) throws InterruptedException, ExecutionException { + final AclDel request = new AclDel(); + request.aclIndex = 0; + System.out.printf("Sending AclDel request %s%n", request.toString()); + final AclDelReply reply = jvpp.aclDel(request).toCompletableFuture().get(); + System.out.printf("AclDel send result = %s%n", reply); + } + + static AclInterfaceListDetailsReplyDump sendAclInterfaceListDumpRequest(final FutureJVppAclFacade jvpp) + throws InterruptedException, ExecutionException { + final AclInterfaceListDump request = new AclInterfaceListDump(); + request.swIfIndex = 0; + System.out.printf("Sending AclInterfaceListDump request %s%n", request.toString()); + final AclInterfaceListDetailsReplyDump dump = jvpp.aclInterfaceListDump(request).toCompletableFuture().get(); + System.out.printf("AclInterfaceListDump send result = %s%n", dump); + return dump; + } + + static void sendAclInterfaceSetAclList(final FutureJVppAclFacade jvpp) + throws InterruptedException, ExecutionException { + final AclInterfaceSetAclList request = new AclInterfaceSetAclList(); + request.count = 1; + request.acls = new int[]{1}; + request.swIfIndex = 0; + request.nInput = 0; + System.out.printf("Sending AclInterfaceSetAclList request %s%n", request.toString()); + final AclInterfaceSetAclListReply reply = jvpp.aclInterfaceSetAclList(request).toCompletableFuture().get(); + System.out.printf("AclInterfaceSetAclList send result = %s%n", reply); + } + + static void sendAclInterfaceDeleteList(final FutureJVppAclFacade jvpp) + throws InterruptedException, ExecutionException { + // uses same api but sets list to empty + final AclInterfaceSetAclList request = new AclInterfaceSetAclList(); + request.count = 0; + request.acls = new int[]{}; + request.swIfIndex = 0; + request.nInput = 0; + System.out.printf("Sending AclInterfaceSetAclList(Delete) request %s%n", request.toString()); + final AclInterfaceSetAclListReply reply = jvpp.aclInterfaceSetAclList(request).toCompletableFuture().get(); + System.out.printf("AclInterfaceSetAclList(Delete) send result = %s%n", reply); + } + + private static MacipAclAdd createMacIpAddRequest() { + MacipAclAdd request = new MacipAclAdd(); + + request.count = 2; + request.r = createMacipRules(); + return request; + } + + private static AclAddReplace createAclAddRequest() { + AclAddReplace request = new AclAddReplace(); + + request.aclIndex = -1;// to define new one + request.count = 2; + request.r = createAclRules(); + return request; + } +} diff --git a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/FutureApiExample.java b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/FutureApiExample.java new file mode 100644 index 00000000..862df8df --- /dev/null +++ b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/FutureApiExample.java @@ -0,0 +1,68 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.acl.examples; + +import static io.fd.vpp.jvpp.acl.examples.AclExpectedDumpData.verifyAclDump; +import static io.fd.vpp.jvpp.acl.examples.AclExpectedDumpData.verifyAclInterfaceList; +import static io.fd.vpp.jvpp.acl.examples.AclExpectedDumpData.verifyMacIpDump; +import static io.fd.vpp.jvpp.acl.examples.AclTestRequests.sendAclAddRequest; +import static io.fd.vpp.jvpp.acl.examples.AclTestRequests.sendAclDelRequest; +import static io.fd.vpp.jvpp.acl.examples.AclTestRequests.sendAclDumpRequest; +import static io.fd.vpp.jvpp.acl.examples.AclTestRequests.sendAclInterfaceDeleteList; +import static io.fd.vpp.jvpp.acl.examples.AclTestRequests.sendAclInterfaceListDumpRequest; +import static io.fd.vpp.jvpp.acl.examples.AclTestRequests.sendAclInterfaceSetAclList; +import static io.fd.vpp.jvpp.acl.examples.AclTestRequests.sendMacIpAddRequest; +import static io.fd.vpp.jvpp.acl.examples.AclTestRequests.sendMacIpDelRequest; +import static io.fd.vpp.jvpp.acl.examples.AclTestRequests.sendMacIpDumpRequest; + +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.acl.JVppAclImpl; +import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; + +public class FutureApiExample { + + public static void main(String[] args) throws Exception { + testCallbackApi(); + } + + private static void testCallbackApi() throws Exception { + System.out.println("Testing Java callback API for acl plugin"); + try (final JVppRegistry registry = new JVppRegistryImpl("macipAclAddTest"); + final FutureJVppAclFacade jvpp = new FutureJVppAclFacade(registry, new JVppAclImpl())) { + + // adds,dump and verifies Mac-Ip acl + sendMacIpAddRequest(jvpp); + verifyMacIpDump(sendMacIpDumpRequest(jvpp).macipAclDetails.get(0)); + + // adds,dumps and verifies Acl acl + sendAclAddRequest(jvpp); + verifyAclDump(sendAclDumpRequest(jvpp).aclDetails.get(0)); + + // adds,dumps and verifies Interface for acl + sendAclInterfaceSetAclList(jvpp); + verifyAclInterfaceList(sendAclInterfaceListDumpRequest(jvpp).aclInterfaceListDetails.get(0)); + + // deletes all created data + sendAclInterfaceDeleteList(jvpp); + sendAclDelRequest(jvpp); + sendMacIpDelRequest(jvpp); + + System.out.println("Disconnecting..."); + } + } +} diff --git a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/Readme.txt b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/Readme.txt new file mode 100644 index 00000000..86b473d8 --- /dev/null +++ b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/Readme.txt @@ -0,0 +1 @@ +sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp-native/vpp/vpp-api/java/jvpp-acl-17.10.jar io.fd.vpp.jvpp.acl.examples.FutureApiExample diff --git a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/AclExpectedDumpData.java b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/AclExpectedDumpData.java deleted file mode 100644 index 979edbc4..00000000 --- a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/AclExpectedDumpData.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * 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. - */ - -package io.fd.vpp.jvpp.acl.test; - - -import static io.fd.vpp.jvpp.acl.test.AclTestData.FIRST_RULE_ADDRESS_2_AS_ARRAY; -import static io.fd.vpp.jvpp.acl.test.AclTestData.FIRST_RULE_ADDRESS_AS_ARRAY; -import static io.fd.vpp.jvpp.acl.test.AclTestData.FIRST_RULE_DST_ICMP_TYPE_END; -import static io.fd.vpp.jvpp.acl.test.AclTestData.FIRST_RULE_DST_ICMP_TYPE_START; -import static io.fd.vpp.jvpp.acl.test.AclTestData.FIRST_RULE_MAC; -import static io.fd.vpp.jvpp.acl.test.AclTestData.FIRST_RULE_MAC_MASK; -import static io.fd.vpp.jvpp.acl.test.AclTestData.FIRST_RULE_PREFIX; -import static io.fd.vpp.jvpp.acl.test.AclTestData.FIRST_RULE_PREFIX_2; -import static io.fd.vpp.jvpp.acl.test.AclTestData.FIRST_RULE_SRC_ICMP_TYPE_END; -import static io.fd.vpp.jvpp.acl.test.AclTestData.FIRST_RULE_SRC_ICMP_TYPE_START; -import static io.fd.vpp.jvpp.acl.test.AclTestData.ICMP_PROTOCOL; -import static io.fd.vpp.jvpp.acl.test.AclTestData.SECOND_RULE_ADDRESS_2_AS_ARRAY; -import static io.fd.vpp.jvpp.acl.test.AclTestData.SECOND_RULE_ADDRESS_AS_ARRAY; -import static io.fd.vpp.jvpp.acl.test.AclTestData.SECOND_RULE_DST_PORT_RANGE_END; -import static io.fd.vpp.jvpp.acl.test.AclTestData.SECOND_RULE_DST_PORT_RANGE_START; -import static io.fd.vpp.jvpp.acl.test.AclTestData.SECOND_RULE_MAC; -import static io.fd.vpp.jvpp.acl.test.AclTestData.SECOND_RULE_MAC_MASK; -import static io.fd.vpp.jvpp.acl.test.AclTestData.SECOND_RULE_PREFIX; -import static io.fd.vpp.jvpp.acl.test.AclTestData.SECOND_RULE_PREFIX_2; -import static io.fd.vpp.jvpp.acl.test.AclTestData.SECOND_RULE_SRC_PORT_RANGE_END; -import static io.fd.vpp.jvpp.acl.test.AclTestData.SECOND_RULE_SRC_PORT_RANGE_START; -import static io.fd.vpp.jvpp.acl.test.AclTestData.UDP_PROTOCOL; - -import io.fd.vpp.jvpp.acl.dto.AclDetails; -import io.fd.vpp.jvpp.acl.dto.AclInterfaceListDetails; -import io.fd.vpp.jvpp.acl.dto.MacipAclDetails; -import io.fd.vpp.jvpp.acl.types.AclRule; -import io.fd.vpp.jvpp.acl.types.MacipAclRule; -import java.util.Arrays; - -class AclExpectedDumpData { - - static void verifyMacIpDump(final MacipAclDetails macipAclDetails) { - // asserting data create by previous call - assertEquals(0, macipAclDetails.aclIndex); - assertEquals(2, macipAclDetails.count); - - final MacipAclRule currentIpv4Rule = macipAclDetails.r[0]; - final MacipAclRule currentIpv6Rule = macipAclDetails.r[1]; - - // Comparing one property at the time to better pointer if something is wrong - //Ipv4 rule - assertEquals(0, currentIpv4Rule.isIpv6); - assertEquals(1, currentIpv4Rule.isPermit); - - // cutting expected ipv4 to 4 bytes,vpp sends it as 16 always - assertArrays(FIRST_RULE_ADDRESS_AS_ARRAY, Arrays.copyOfRange(currentIpv4Rule.srcIpAddr, 0, 4)); - assertEquals(FIRST_RULE_PREFIX, currentIpv4Rule.srcIpPrefixLen); - assertArrays(FIRST_RULE_MAC, currentIpv4Rule.srcMac); - assertArrays(FIRST_RULE_MAC_MASK, currentIpv4Rule.srcMacMask); - - //Ipv6 rule - assertEquals(1, currentIpv6Rule.isIpv6); - assertEquals(0, currentIpv6Rule.isPermit); - assertArrays(SECOND_RULE_ADDRESS_AS_ARRAY, currentIpv6Rule.srcIpAddr); - assertEquals(SECOND_RULE_PREFIX, currentIpv6Rule.srcIpPrefixLen); - assertArrays(SECOND_RULE_MAC, currentIpv6Rule.srcMac); - assertArrays(SECOND_RULE_MAC_MASK, currentIpv6Rule.srcMacMask); - } - - static void verifyAclDump(final AclDetails aclDetails) { - assertEquals(0, aclDetails.aclIndex); - assertEquals(2, aclDetails.count); - - final AclRule currentIpv4Rule = aclDetails.r[0]; - final AclRule currentIpv6Rule = aclDetails.r[1]; - - // Comparing one property at the time to better pointer if something is wrong - //Ipv4 rule - assertEquals(0, currentIpv4Rule.isIpv6); - assertEquals(1, currentIpv4Rule.isPermit); - - // cutting expected ipv4 to 4 bytes,vpp sends it as 16 always - assertArrays(FIRST_RULE_ADDRESS_AS_ARRAY, Arrays.copyOfRange(currentIpv4Rule.srcIpAddr, 0, 4)); - assertEquals(FIRST_RULE_PREFIX, currentIpv4Rule.srcIpPrefixLen); - assertArrays(FIRST_RULE_ADDRESS_2_AS_ARRAY, Arrays.copyOfRange(currentIpv4Rule.dstIpAddr, 0, 4)); - assertEquals(FIRST_RULE_PREFIX_2, currentIpv4Rule.dstIpPrefixLen); - - assertEquals(ICMP_PROTOCOL, currentIpv4Rule.proto); - assertEquals(FIRST_RULE_SRC_ICMP_TYPE_START, currentIpv4Rule.srcportOrIcmptypeFirst); - assertEquals(FIRST_RULE_SRC_ICMP_TYPE_END, currentIpv4Rule.srcportOrIcmptypeLast); - assertEquals(FIRST_RULE_DST_ICMP_TYPE_START, currentIpv4Rule.dstportOrIcmpcodeFirst); - assertEquals(FIRST_RULE_DST_ICMP_TYPE_END, currentIpv4Rule.dstportOrIcmpcodeLast); - - assertArrays(SECOND_RULE_ADDRESS_AS_ARRAY, currentIpv6Rule.srcIpAddr); - assertEquals(SECOND_RULE_PREFIX, currentIpv6Rule.srcIpPrefixLen); - assertArrays(SECOND_RULE_ADDRESS_2_AS_ARRAY, currentIpv6Rule.dstIpAddr); - assertEquals(SECOND_RULE_PREFIX_2, currentIpv6Rule.dstIpPrefixLen); - - assertEquals(UDP_PROTOCOL, currentIpv6Rule.proto); - assertEquals(SECOND_RULE_SRC_PORT_RANGE_START, currentIpv6Rule.srcportOrIcmptypeFirst); - assertEquals(SECOND_RULE_SRC_PORT_RANGE_END, currentIpv6Rule.srcportOrIcmptypeLast); - assertEquals(SECOND_RULE_DST_PORT_RANGE_START, currentIpv6Rule.dstportOrIcmpcodeFirst); - assertEquals(SECOND_RULE_DST_PORT_RANGE_END, currentIpv6Rule.dstportOrIcmpcodeLast); - } - - static void verifyAclInterfaceList(final AclInterfaceListDetails aclInterfaceListDetails) { - assertEquals(1, aclInterfaceListDetails.count); - assertEquals(1, aclInterfaceListDetails.acls[0]); - assertEquals(0, aclInterfaceListDetails.nInput); - assertEquals(0, aclInterfaceListDetails.swIfIndex); - } - - private static void assertArrays(final byte[] expected, final byte[] actual) { - if (!Arrays.equals(expected, actual)) { - throw new IllegalArgumentException( - String.format("Expected[%s]/Actual[%s]", Arrays.toString(expected), Arrays.toString(actual))); - } - } - - private static void assertEquals(final int expected, final int actual) { - if (expected != actual) { - throw new IllegalArgumentException(String.format("Expected[%s]/Actual[%s]", expected, actual)); - } - } -} diff --git a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/AclTestData.java b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/AclTestData.java deleted file mode 100644 index 5d228eea..00000000 --- a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/AclTestData.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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. - */ - -package io.fd.vpp.jvpp.acl.test; - - -import io.fd.vpp.jvpp.acl.types.AclRule; -import io.fd.vpp.jvpp.acl.types.MacipAclRule; - -class AclTestData { - - static final byte[] FIRST_RULE_ADDRESS_AS_ARRAY = {-64, -88, 2, 1}; - static final byte[] FIRST_RULE_ADDRESS_2_AS_ARRAY = {-64, -88, 2, 3}; - static final byte[] SECOND_RULE_ADDRESS_AS_ARRAY = - {32, 1, 13, -72, 10, 11, 18, -16, 0, 0, 0, 0, 0, 0, 0, 1}; - static final byte[] SECOND_RULE_ADDRESS_2_AS_ARRAY = - {32, 1, 13, -72, 10, 11, 18, -16, 0, 0, 0, 0, 0, 0, 0, 1}; - static final byte[] FIRST_RULE_MAC = {11, 11, 11, 11, 11, 11}; - static final byte[] FIRST_RULE_MAC_MASK = {0, 0, 0, 0, 0, 0}; - static final byte[] SECOND_RULE_MAC = {11, 12, 11, 11, 12, 11}; - static final byte[] SECOND_RULE_MAC_MASK = {(byte) 170, 0, 0, 0, 0, 0}; - static final int FIRST_RULE_PREFIX = 32; - static final int FIRST_RULE_PREFIX_2 = 24; - static final int SECOND_RULE_PREFIX = 64; - static final int SECOND_RULE_PREFIX_2 = 62; - static final int FIRST_RULE_DST_ICMP_TYPE_START = 0; - static final int FIRST_RULE_DST_ICMP_TYPE_END = 8; - static final int FIRST_RULE_SRC_ICMP_TYPE_START = 1; - static final int FIRST_RULE_SRC_ICMP_TYPE_END = 7; - static final int ICMP_PROTOCOL = 1; - static final int SECOND_RULE_DST_PORT_RANGE_START = 2000; - static final int SECOND_RULE_DST_PORT_RANGE_END = 6000; - static final int SECOND_RULE_SRC_PORT_RANGE_START = 400; - static final int SECOND_RULE_SRC_PORT_RANGE_END = 2047; - static final int UDP_PROTOCOL = 17; - - - static MacipAclRule[] createMacipRules() { - MacipAclRule ruleOne = new MacipAclRule(); - ruleOne.isIpv6 = 0; - ruleOne.isPermit = 1; - ruleOne.srcIpAddr = FIRST_RULE_ADDRESS_AS_ARRAY; - ruleOne.srcIpPrefixLen = FIRST_RULE_PREFIX; - ruleOne.srcMac = FIRST_RULE_MAC; - ruleOne.srcMacMask = FIRST_RULE_MAC_MASK;// no mask - - MacipAclRule ruleTwo = new MacipAclRule(); - ruleTwo.isIpv6 = 1; - ruleTwo.isPermit = 0; - ruleTwo.srcIpAddr = SECOND_RULE_ADDRESS_AS_ARRAY; - ruleTwo.srcIpPrefixLen = SECOND_RULE_PREFIX; - ruleTwo.srcMac = SECOND_RULE_MAC; - ruleTwo.srcMacMask = SECOND_RULE_MAC_MASK; - - return new MacipAclRule[]{ruleOne, ruleTwo}; - } - - static AclRule[] createAclRules() { - AclRule ruleOne = new AclRule(); - - ruleOne.isIpv6 = 0; - ruleOne.isPermit = 1; - ruleOne.srcIpAddr = FIRST_RULE_ADDRESS_AS_ARRAY; - ruleOne.srcIpPrefixLen = FIRST_RULE_PREFIX; - ruleOne.dstIpAddr = FIRST_RULE_ADDRESS_2_AS_ARRAY; - ruleOne.dstIpPrefixLen = FIRST_RULE_PREFIX_2; - ruleOne.dstportOrIcmpcodeFirst = FIRST_RULE_DST_ICMP_TYPE_START; - ruleOne.dstportOrIcmpcodeLast = FIRST_RULE_DST_ICMP_TYPE_END; - ruleOne.srcportOrIcmptypeFirst = FIRST_RULE_SRC_ICMP_TYPE_START; - ruleOne.srcportOrIcmptypeLast = FIRST_RULE_SRC_ICMP_TYPE_END; - ruleOne.proto = ICMP_PROTOCOL; //ICMP - - AclRule ruleTwo = new AclRule(); - ruleTwo.isIpv6 = 1; - ruleTwo.isPermit = 0; - ruleTwo.srcIpAddr = SECOND_RULE_ADDRESS_AS_ARRAY; - ruleTwo.srcIpPrefixLen = SECOND_RULE_PREFIX; - ruleTwo.dstIpAddr = SECOND_RULE_ADDRESS_2_AS_ARRAY; - ruleTwo.dstIpPrefixLen = SECOND_RULE_PREFIX_2; - ruleTwo.dstportOrIcmpcodeFirst = SECOND_RULE_DST_PORT_RANGE_START; - ruleTwo.dstportOrIcmpcodeLast = SECOND_RULE_DST_PORT_RANGE_END; - ruleTwo.srcportOrIcmptypeFirst = SECOND_RULE_SRC_PORT_RANGE_START; - ruleTwo.srcportOrIcmptypeLast = SECOND_RULE_SRC_PORT_RANGE_END; - ruleTwo.proto = UDP_PROTOCOL; //UDP - - return new AclRule[]{ruleOne, ruleTwo}; - } -} diff --git a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/AclTestRequests.java b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/AclTestRequests.java deleted file mode 100644 index b580ee8c..00000000 --- a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/AclTestRequests.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * 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. - */ - -package io.fd.vpp.jvpp.acl.test; - -import static io.fd.vpp.jvpp.acl.test.AclTestData.createAclRules; -import static io.fd.vpp.jvpp.acl.test.AclTestData.createMacipRules; - -import io.fd.vpp.jvpp.VppInvocationException; -import io.fd.vpp.jvpp.acl.dto.AclAddReplace; -import io.fd.vpp.jvpp.acl.dto.AclAddReplaceReply; -import io.fd.vpp.jvpp.acl.dto.AclDel; -import io.fd.vpp.jvpp.acl.dto.AclDelReply; -import io.fd.vpp.jvpp.acl.dto.AclDetailsReplyDump; -import io.fd.vpp.jvpp.acl.dto.AclDump; -import io.fd.vpp.jvpp.acl.dto.AclInterfaceListDetailsReplyDump; -import io.fd.vpp.jvpp.acl.dto.AclInterfaceListDump; -import io.fd.vpp.jvpp.acl.dto.AclInterfaceSetAclList; -import io.fd.vpp.jvpp.acl.dto.AclInterfaceSetAclListReply; -import io.fd.vpp.jvpp.acl.dto.MacipAclAdd; -import io.fd.vpp.jvpp.acl.dto.MacipAclAddReply; -import io.fd.vpp.jvpp.acl.dto.MacipAclDel; -import io.fd.vpp.jvpp.acl.dto.MacipAclDelReply; -import io.fd.vpp.jvpp.acl.dto.MacipAclDetailsReplyDump; -import io.fd.vpp.jvpp.acl.dto.MacipAclDump; -import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; -import java.util.concurrent.ExecutionException; - -class AclTestRequests { - - static MacipAclDetailsReplyDump sendMacIpDumpRequest(final FutureJVppAclFacade jvpp) - throws ExecutionException, InterruptedException { - System.out.println("Sending MacipAclDump request..."); - MacipAclDetailsReplyDump dump = jvpp.macipAclDump(new MacipAclDump()).toCompletableFuture().get(); - System.out.println("MacipAclDump returned"); - return dump; - } - - static void sendMacIpAddRequest(final FutureJVppAclFacade jvpp) throws InterruptedException, ExecutionException { - final MacipAclAdd request = createMacIpAddRequest(); - System.out.printf("Sending MacipAclAdd request %s%n", request.toString()); - final MacipAclAddReply reply = jvpp.macipAclAdd(createMacIpAddRequest()).toCompletableFuture().get(); - System.out.printf("MacipAclAdd send result = %s%n", reply); - } - - static void sendMacIpDelRequest(final FutureJVppAclFacade jvpp) throws InterruptedException, ExecutionException { - final MacipAclDel request = new MacipAclDel(); - request.aclIndex = 0; - System.out.printf("Sending MacipAclDel request %s%n", request.toString()); - final MacipAclDelReply reply = jvpp.macipAclDel(request).toCompletableFuture().get(); - System.out.printf("MacipAclDel send result = %s%n", reply); - } - - static void sendAclAddRequest(final FutureJVppAclFacade jvpp) throws InterruptedException, ExecutionException { - final AclAddReplace request = createAclAddRequest(); - System.out.printf("Sending AclAddReplace request %s%n", request.toString()); - final AclAddReplaceReply reply = jvpp.aclAddReplace(request).toCompletableFuture().get(); - System.out.printf("AclAddReplace send result = %s%n", reply); - } - - static AclDetailsReplyDump sendAclDumpRequest(final FutureJVppAclFacade jvpp) - throws InterruptedException, VppInvocationException, ExecutionException { - System.out.println("Sending AclDump request..."); - final AclDetailsReplyDump dump = jvpp.aclDump(new AclDump()).toCompletableFuture().get(); - System.out.printf("AclDump send result = %s%n", dump); - return dump; - } - - static void sendAclDelRequest(final FutureJVppAclFacade jvpp) throws InterruptedException, ExecutionException { - final AclDel request = new AclDel(); - request.aclIndex = 0; - System.out.printf("Sending AclDel request %s%n", request.toString()); - final AclDelReply reply = jvpp.aclDel(request).toCompletableFuture().get(); - System.out.printf("AclDel send result = %s%n", reply); - } - - static AclInterfaceListDetailsReplyDump sendAclInterfaceListDumpRequest(final FutureJVppAclFacade jvpp) - throws InterruptedException, ExecutionException { - final AclInterfaceListDump request = new AclInterfaceListDump(); - request.swIfIndex = 0; - System.out.printf("Sending AclInterfaceListDump request %s%n", request.toString()); - final AclInterfaceListDetailsReplyDump dump = jvpp.aclInterfaceListDump(request).toCompletableFuture().get(); - System.out.printf("AclInterfaceListDump send result = %s%n", dump); - return dump; - } - - static void sendAclInterfaceSetAclList(final FutureJVppAclFacade jvpp) - throws InterruptedException, ExecutionException { - final AclInterfaceSetAclList request = new AclInterfaceSetAclList(); - request.count = 1; - request.acls = new int[]{1}; - request.swIfIndex = 0; - request.nInput = 0; - System.out.printf("Sending AclInterfaceSetAclList request %s%n", request.toString()); - final AclInterfaceSetAclListReply reply = jvpp.aclInterfaceSetAclList(request).toCompletableFuture().get(); - System.out.printf("AclInterfaceSetAclList send result = %s%n", reply); - } - - static void sendAclInterfaceDeleteList(final FutureJVppAclFacade jvpp) - throws InterruptedException, ExecutionException { - // uses same api but sets list to empty - final AclInterfaceSetAclList request = new AclInterfaceSetAclList(); - request.count = 0; - request.acls = new int[]{}; - request.swIfIndex = 0; - request.nInput = 0; - System.out.printf("Sending AclInterfaceSetAclList(Delete) request %s%n", request.toString()); - final AclInterfaceSetAclListReply reply = jvpp.aclInterfaceSetAclList(request).toCompletableFuture().get(); - System.out.printf("AclInterfaceSetAclList(Delete) send result = %s%n", reply); - } - - private static MacipAclAdd createMacIpAddRequest() { - MacipAclAdd request = new MacipAclAdd(); - - request.count = 2; - request.r = createMacipRules(); - return request; - } - - private static AclAddReplace createAclAddRequest() { - AclAddReplace request = new AclAddReplace(); - - request.aclIndex = -1;// to define new one - request.count = 2; - request.r = createAclRules(); - return request; - } -} diff --git a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/FutureApiTest.java b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/FutureApiTest.java deleted file mode 100644 index 94490193..00000000 --- a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/FutureApiTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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. - */ - -package io.fd.vpp.jvpp.acl.test; - -import static io.fd.vpp.jvpp.acl.test.AclExpectedDumpData.verifyAclDump; -import static io.fd.vpp.jvpp.acl.test.AclExpectedDumpData.verifyAclInterfaceList; -import static io.fd.vpp.jvpp.acl.test.AclExpectedDumpData.verifyMacIpDump; -import static io.fd.vpp.jvpp.acl.test.AclTestRequests.sendAclAddRequest; -import static io.fd.vpp.jvpp.acl.test.AclTestRequests.sendAclDelRequest; -import static io.fd.vpp.jvpp.acl.test.AclTestRequests.sendAclDumpRequest; -import static io.fd.vpp.jvpp.acl.test.AclTestRequests.sendAclInterfaceDeleteList; -import static io.fd.vpp.jvpp.acl.test.AclTestRequests.sendAclInterfaceListDumpRequest; -import static io.fd.vpp.jvpp.acl.test.AclTestRequests.sendAclInterfaceSetAclList; -import static io.fd.vpp.jvpp.acl.test.AclTestRequests.sendMacIpAddRequest; -import static io.fd.vpp.jvpp.acl.test.AclTestRequests.sendMacIpDelRequest; -import static io.fd.vpp.jvpp.acl.test.AclTestRequests.sendMacIpDumpRequest; - -import io.fd.vpp.jvpp.JVppRegistry; -import io.fd.vpp.jvpp.JVppRegistryImpl; -import io.fd.vpp.jvpp.acl.JVppAclImpl; -import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; - -public class FutureApiTest { - - public static void main(String[] args) throws Exception { - testCallbackApi(); - } - - private static void testCallbackApi() throws Exception { - System.out.println("Testing Java callback API for acl plugin"); - try (final JVppRegistry registry = new JVppRegistryImpl("macipAclAddTest"); - final FutureJVppAclFacade jvpp = new FutureJVppAclFacade(registry, new JVppAclImpl())) { - - // adds,dump and verifies Mac-Ip acl - sendMacIpAddRequest(jvpp); - verifyMacIpDump(sendMacIpDumpRequest(jvpp).macipAclDetails.get(0)); - - // adds,dumps and verifies Acl acl - sendAclAddRequest(jvpp); - verifyAclDump(sendAclDumpRequest(jvpp).aclDetails.get(0)); - - // adds,dumps and verifies Interface for acl - sendAclInterfaceSetAclList(jvpp); - verifyAclInterfaceList(sendAclInterfaceListDumpRequest(jvpp).aclInterfaceListDetails.get(0)); - - // deletes all created data - sendAclInterfaceDeleteList(jvpp); - sendAclDelRequest(jvpp); - sendMacIpDelRequest(jvpp); - - System.out.println("Disconnecting..."); - } - } -} diff --git a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/Readme.txt b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/Readme.txt deleted file mode 100644 index f68e7aba..00000000 --- a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/Readme.txt +++ /dev/null @@ -1 +0,0 @@ -sudo java -cp build-vpp-native/vpp-api/java/jvpp-registry-17.01.jar:build-vpp-native/plugins/acl-plugin/jvpp-acl-1.0.jar io.fd.vpp.jvpp.acl.test.FutureApiTest diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackApiExample.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackApiExample.java new file mode 100644 index 00000000..554a21bd --- /dev/null +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackApiExample.java @@ -0,0 +1,100 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.core.examples; + +import io.fd.vpp.jvpp.JVpp; +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.VppCallbackException; +import io.fd.vpp.jvpp.core.JVppCoreImpl; +import io.fd.vpp.jvpp.core.callback.GetNodeIndexCallback; +import io.fd.vpp.jvpp.core.callback.ShowVersionCallback; +import io.fd.vpp.jvpp.core.callback.SwInterfaceCallback; +import io.fd.vpp.jvpp.core.dto.GetNodeIndex; +import io.fd.vpp.jvpp.core.dto.GetNodeIndexReply; +import io.fd.vpp.jvpp.core.dto.ShowVersion; +import io.fd.vpp.jvpp.core.dto.ShowVersionReply; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDetails; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDump; +import java.nio.charset.StandardCharsets; + +public class CallbackApiExample { + + public static void main(String[] args) throws Exception { + testCallbackApi(); + } + + private static void testCallbackApi() throws Exception { + System.out.println("Testing Java callback API with JVppRegistry"); + try (final JVppRegistry registry = new JVppRegistryImpl("CallbackApiExample"); + final JVpp jvpp = new JVppCoreImpl()) { + registry.register(jvpp, new TestCallback()); + + System.out.println("Sending ShowVersion request..."); + final int result = jvpp.send(new ShowVersion()); + System.out.printf("ShowVersion send result = %d%n", result); + + System.out.println("Sending GetNodeIndex request..."); + GetNodeIndex getNodeIndexRequest = new GetNodeIndex(); + getNodeIndexRequest.nodeName = "non-existing-node".getBytes(StandardCharsets.UTF_8); + jvpp.send(getNodeIndexRequest); + + System.out.println("Sending SwInterfaceDump request..."); + SwInterfaceDump swInterfaceDumpRequest = new SwInterfaceDump(); + swInterfaceDumpRequest.nameFilterValid = 0; + swInterfaceDumpRequest.nameFilter = "".getBytes(StandardCharsets.UTF_8); + jvpp.send(swInterfaceDumpRequest); + + Thread.sleep(1000); + System.out.println("Disconnecting..."); + } + Thread.sleep(1000); + } + + static class TestCallback implements GetNodeIndexCallback, ShowVersionCallback, SwInterfaceCallback { + + @Override + public void onGetNodeIndexReply(final GetNodeIndexReply msg) { + System.out.printf("Received GetNodeIndexReply: %s%n", msg); + } + + @Override + public void onShowVersionReply(final ShowVersionReply msg) { + System.out.printf("Received ShowVersionReply: context=%d, program=%s, version=%s, " + + "buildDate=%s, buildDirectory=%s%n", + msg.context, + new String(msg.program, StandardCharsets.UTF_8), + new String(msg.version, StandardCharsets.UTF_8), + new String(msg.buildDate, StandardCharsets.UTF_8), + new String(msg.buildDirectory, StandardCharsets.UTF_8)); + } + + @Override + public void onSwInterfaceDetails(final SwInterfaceDetails msg) { + System.out.printf("Received SwInterfaceDetails: interfaceName=%s, l2AddressLength=%d, adminUpDown=%d, " + + "linkUpDown=%d, linkSpeed=%d, linkMtu=%d%n", + new String(msg.interfaceName, StandardCharsets.UTF_8), msg.l2AddressLength, msg.adminUpDown, + msg.linkUpDown, msg.linkSpeed, (int) msg.linkMtu); + } + + @Override + public void onError(VppCallbackException ex) { + System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", ex.getMethodName(), + ex.getCtxId(), ex.getErrorCode()); + } + } +} diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackJVppFacadeExample.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackJVppFacadeExample.java new file mode 100644 index 00000000..2f77f0f1 --- /dev/null +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackJVppFacadeExample.java @@ -0,0 +1,110 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.core.examples; + +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.VppCallbackException; +import io.fd.vpp.jvpp.core.JVppCoreImpl; +import io.fd.vpp.jvpp.core.callback.GetNodeIndexCallback; +import io.fd.vpp.jvpp.core.callback.ShowVersionCallback; +import io.fd.vpp.jvpp.core.callfacade.CallbackJVppCoreFacade; +import io.fd.vpp.jvpp.core.dto.GetNodeIndex; +import io.fd.vpp.jvpp.core.dto.GetNodeIndexReply; +import io.fd.vpp.jvpp.core.dto.ShowVersionReply; +import java.nio.charset.StandardCharsets; + +/** + * CallbackJVppFacade together with CallbackJVppFacadeCallback allow for setting different callback for each request. + * This is more convenient than the approach shown in CallbackApiExample. + */ +public class CallbackJVppFacadeExample { + + private static ShowVersionCallback showVersionCallback1 = new ShowVersionCallback() { + @Override + public void onShowVersionReply(final ShowVersionReply msg) { + System.out.printf("ShowVersionCallback1 received ShowVersionReply: context=%d, program=%s," + + "version=%s, buildDate=%s, buildDirectory=%s%n", msg.context, + new String(msg.program, StandardCharsets.UTF_8), + new String(msg.version, StandardCharsets.UTF_8), + new String(msg.buildDate, StandardCharsets.UTF_8), + new String(msg.buildDirectory, StandardCharsets.UTF_8)); + } + + @Override + public void onError(VppCallbackException ex) { + System.out.printf("Received onError exception in showVersionCallback1: call=%s, reply=%d, context=%d%n", + ex.getMethodName(), ex.getErrorCode(), ex.getCtxId()); + } + }; + + private static ShowVersionCallback showVersionCallback2 = new ShowVersionCallback() { + @Override + public void onShowVersionReply(final ShowVersionReply msg) { + System.out.printf("ShowVersionCallback2 received ShowVersionReply: context=%d, program=%s," + + "version=%s, buildDate=%s, buildDirectory=%s%n", msg.context, + new String(msg.program, StandardCharsets.UTF_8), + new String(msg.version, StandardCharsets.UTF_8), + new String(msg.buildDate, StandardCharsets.UTF_8), + new String(msg.buildDirectory, StandardCharsets.UTF_8)); + } + + @Override + public void onError(VppCallbackException ex) { + System.out.printf("Received onError exception in showVersionCallback2: call=%s, reply=%d, context=%d%n", + ex.getMethodName(), ex.getErrorCode(), ex.getCtxId()); + } + + }; + + private static GetNodeIndexCallback getNodeIndexCallback = new GetNodeIndexCallback() { + @Override + public void onGetNodeIndexReply(final GetNodeIndexReply msg) { + System.out.printf("Received GetNodeIndexReply: %s%n", msg); + } + + @Override + public void onError(VppCallbackException ex) { + System.out.printf("Received onError exception in getNodeIndexCallback: call=%s, reply=%d, context=%d%n", + ex.getMethodName(), ex.getErrorCode(), ex.getCtxId()); + } + }; + + private static void testCallbackFacade() throws Exception { + System.out.println("Testing CallbackJVppFacade"); + + try (final JVppRegistry registry = new JVppRegistryImpl("CallbackFacadeExample"); + final CallbackJVppCoreFacade callbackFacade = new CallbackJVppCoreFacade(registry, new JVppCoreImpl())) { + System.out.println("Successfully connected to VPP"); + + callbackFacade.showVersion(showVersionCallback1); + callbackFacade.showVersion(showVersionCallback2); + + GetNodeIndex getNodeIndexRequest = new GetNodeIndex(); + getNodeIndexRequest.nodeName = "dummyNode".getBytes(StandardCharsets.UTF_8); + callbackFacade.getNodeIndex(getNodeIndexRequest, getNodeIndexCallback); + + Thread.sleep(2000); + System.out.println("Disconnecting..."); + } + Thread.sleep(1000); + } + + public static void main(String[] args) throws Exception { + testCallbackFacade(); + } +} diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackJVppFacadeNotificationExample.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackJVppFacadeNotificationExample.java new file mode 100644 index 00000000..b8b108b6 --- /dev/null +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackJVppFacadeNotificationExample.java @@ -0,0 +1,87 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.core.examples; + +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.VppCallbackException; +import io.fd.vpp.jvpp.core.JVppCore; +import io.fd.vpp.jvpp.core.JVppCoreImpl; +import io.fd.vpp.jvpp.core.callback.WantInterfaceEventsCallback; +import io.fd.vpp.jvpp.core.callfacade.CallbackJVppCoreFacade; +import io.fd.vpp.jvpp.core.dto.WantInterfaceEventsReply; + +public class CallbackJVppFacadeNotificationExample { + + private static void testCallbackFacade() throws Exception { + System.out.println("Testing CallbackJVppFacade for notifications"); + + try (final JVppRegistry registry = new JVppRegistryImpl("CallbackFacadeExample"); + final JVppCore jvpp = new JVppCoreImpl()) { + final CallbackJVppCoreFacade jvppCallbackFacade = new CallbackJVppCoreFacade(registry, jvpp); + System.out.println("Successfully connected to VPP"); + + final AutoCloseable notificationListenerReg = + jvppCallbackFacade.getNotificationRegistry().registerSwInterfaceSetFlagsNotificationCallback( + NotificationUtils::printNotification + ); + + jvppCallbackFacade.wantInterfaceEvents(NotificationUtils.getEnableInterfaceNotificationsReq(), + new WantInterfaceEventsCallback() { + @Override + public void onWantInterfaceEventsReply(final WantInterfaceEventsReply reply) { + System.out.println("Interface events started"); + } + + @Override + public void onError(final VppCallbackException ex) { + System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", + ex.getMethodName(), ex.getCtxId(), ex.getErrorCode()); + } + }); + + System.out.println("Changing interface configuration"); + NotificationUtils.getChangeInterfaceState().send(jvpp); + + Thread.sleep(1000); + + jvppCallbackFacade.wantInterfaceEvents(NotificationUtils.getDisableInterfaceNotificationsReq(), + new WantInterfaceEventsCallback() { + @Override + public void onWantInterfaceEventsReply(final WantInterfaceEventsReply reply) { + System.out.println("Interface events stopped"); + } + + @Override + public void onError(final VppCallbackException ex) { + System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", + ex.getMethodName(), ex.getCtxId(), ex.getErrorCode()); + } + }); + + notificationListenerReg.close(); + + Thread.sleep(2000); + System.out.println("Disconnecting..."); + } + Thread.sleep(1000); + } + + public static void main(String[] args) throws Exception { + testCallbackFacade(); + } +} diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackNotificationApiExample.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackNotificationApiExample.java new file mode 100644 index 00000000..6ee2de31 --- /dev/null +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackNotificationApiExample.java @@ -0,0 +1,94 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.core.examples; + +import static io.fd.vpp.jvpp.core.examples.NotificationUtils.getChangeInterfaceState; +import static io.fd.vpp.jvpp.core.examples.NotificationUtils.getDisableInterfaceNotificationsReq; +import static io.fd.vpp.jvpp.core.examples.NotificationUtils.getEnableInterfaceNotificationsReq; +import static io.fd.vpp.jvpp.core.examples.NotificationUtils.printNotification; + +import io.fd.vpp.jvpp.JVpp; +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.VppCallbackException; +import io.fd.vpp.jvpp.core.JVppCoreImpl; +import io.fd.vpp.jvpp.core.callback.SwInterfaceSetFlagsCallback; +import io.fd.vpp.jvpp.core.callback.SwInterfaceSetFlagsNotificationCallback; +import io.fd.vpp.jvpp.core.callback.WantInterfaceEventsCallback; +import io.fd.vpp.jvpp.core.dto.SwInterfaceSetFlagsNotification; +import io.fd.vpp.jvpp.core.dto.SwInterfaceSetFlagsReply; +import io.fd.vpp.jvpp.core.dto.WantInterfaceEventsReply; + +public class CallbackNotificationApiExample { + + private static void testCallbackApi() throws Exception { + System.out.println("Testing Java callback API for notifications"); + try (final JVppRegistry registry = new JVppRegistryImpl("CallbackNotificationApiExample"); + final JVpp jvpp = new JVppCoreImpl()) { + registry.register(jvpp, new TestCallback()); + System.out.println("Successfully connected to VPP"); + + getEnableInterfaceNotificationsReq().send(jvpp); + System.out.println("Interface notifications started"); + // TODO test ifc dump which also triggers interface flags send + + System.out.println("Changing interface configuration"); + getChangeInterfaceState().send(jvpp); + + // Notifications are received + Thread.sleep(500); + + getDisableInterfaceNotificationsReq().send(jvpp); + System.out.println("Interface events stopped"); + + Thread.sleep(2000); + System.out.println("Disconnecting..."); + } + Thread.sleep(1000); + } + + public static void main(String[] args) throws Exception { + testCallbackApi(); + } + + private static class TestCallback implements SwInterfaceSetFlagsNotificationCallback, + WantInterfaceEventsCallback, SwInterfaceSetFlagsCallback { + + @Override + public void onSwInterfaceSetFlagsNotification( + final SwInterfaceSetFlagsNotification msg) { + printNotification(msg); + } + + @Override + public void onWantInterfaceEventsReply(final WantInterfaceEventsReply wantInterfaceEventsReply) { + System.out.println("Interface notification stream updated"); + } + + @Override + public void onSwInterfaceSetFlagsReply(final SwInterfaceSetFlagsReply swInterfaceSetFlagsReply) { + System.out.println("Interface flags set successfully"); + } + + @Override + public void onError(VppCallbackException ex) { + System.out.printf("Received onError exception in getNodeIndexCallback: call=%s, reply=%d, context=%d%n", + ex.getMethodName(), ex.getErrorCode(), ex.getCtxId()); + + } + } +} diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CreateSubInterfaceExample.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CreateSubInterfaceExample.java new file mode 100644 index 00000000..3db6d30a --- /dev/null +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CreateSubInterfaceExample.java @@ -0,0 +1,121 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.core.examples; + +import static java.util.Objects.requireNonNull; + +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.core.JVppCoreImpl; +import io.fd.vpp.jvpp.core.dto.CreateSubif; +import io.fd.vpp.jvpp.core.dto.CreateSubifReply; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDetailsReplyDump; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDump; +import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade; +import java.nio.charset.StandardCharsets; + +/** + *

Tests sub-interface creation.
Equivalent to:
+ * + *

{@code
+ * vppctl create sub GigabitEthernet0/9/0 1 dot1q 100 inner-dot1q any
+ * }
+ * 
+ * + * To verify invoke:
+ *
{@code
+ * vpp_api_test json
+ * vat# sw_interface_dump
+ * }
+ */
+public class CreateSubInterfaceExample {
+
+    private static SwInterfaceDump createSwInterfaceDumpRequest(final String ifaceName) {
+        SwInterfaceDump request = new SwInterfaceDump();
+        request.nameFilter = ifaceName.getBytes(StandardCharsets.UTF_8);
+        request.nameFilterValid = 1;
+        return request;
+    }
+
+    private static void requireSingleIface(final SwInterfaceDetailsReplyDump response, final String ifaceName) {
+        if (response.swInterfaceDetails.size() != 1) {
+            throw new IllegalStateException(
+                String.format("Expected one interface matching filter %s but was %d", ifaceName,
+                    response.swInterfaceDetails.size()));
+        }
+    }
+
+    private static CreateSubif createSubifRequest(final int swIfIndex, final int subId) {
+        CreateSubif request = new CreateSubif();
+        request.swIfIndex = swIfIndex; // super interface id
+        request.subId = subId;
+        request.noTags = 0;
+        request.oneTag = 0;
+        request.twoTags = 1;
+        request.dot1Ad = 0;
+        request.exactMatch = 1;
+        request.defaultSub = 0;
+        request.outerVlanIdAny = 0;
+        request.innerVlanIdAny = 1;
+        request.outerVlanId = 100;
+        request.innerVlanId = 0;
+        return request;
+    }
+
+    private static void print(CreateSubifReply reply) {
+        System.out.printf("CreateSubifReply: %s%n", reply);
+    }
+
+    private static void testCreateSubInterface() throws Exception {
+        System.out.println("Testing sub-interface creation using Java callback API");
+        try (final JVppRegistry registry = new JVppRegistryImpl("CreateSubInterfaceExample");
+             final FutureJVppCoreFacade jvppFacade = new FutureJVppCoreFacade(registry, new JVppCoreImpl())) {
+            System.out.println("Successfully connected to VPP");
+            Thread.sleep(1000);
+
+            final String ifaceName = "Gigabitethernet0/8/0";
+
+            final SwInterfaceDetailsReplyDump swInterfaceDetails =
+                jvppFacade.swInterfaceDump(createSwInterfaceDumpRequest(ifaceName)).toCompletableFuture().get();
+
+            requireNonNull(swInterfaceDetails, "swInterfaceDump returned null");
+            requireNonNull(swInterfaceDetails.swInterfaceDetails, "swInterfaceDetails is null");
+            requireSingleIface(swInterfaceDetails, ifaceName);
+
+            final int swIfIndex = swInterfaceDetails.swInterfaceDetails.get(0).swIfIndex;
+            final int subId = 1;
+
+            final CreateSubifReply createSubifReply =
+                jvppFacade.createSubif(createSubifRequest(swIfIndex, subId)).toCompletableFuture().get();
+            print(createSubifReply);
+
+            final String subIfaceName = "Gigabitethernet0/8/0." + subId;
+            final SwInterfaceDetailsReplyDump subIface =
+                jvppFacade.swInterfaceDump(createSwInterfaceDumpRequest(subIfaceName)).toCompletableFuture().get();
+            requireNonNull(swInterfaceDetails, "swInterfaceDump returned null");
+            requireNonNull(subIface.swInterfaceDetails, "swInterfaceDump returned null");
+            requireSingleIface(swInterfaceDetails, ifaceName);
+
+            System.out.println("Disconnecting...");
+        }
+        Thread.sleep(1000);
+    }
+
+    public static void main(String[] args) throws Exception {
+        testCreateSubInterface();
+    }
+}
diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiExample.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiExample.java
new file mode 100644
index 00000000..931c9b33
--- /dev/null
+++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiExample.java
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+
+package io.fd.vpp.jvpp.core.examples;
+
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.core.JVppCoreImpl;
+import io.fd.vpp.jvpp.core.dto.BridgeDomainDetailsReplyDump;
+import io.fd.vpp.jvpp.core.dto.BridgeDomainDump;
+import io.fd.vpp.jvpp.core.dto.GetNodeIndex;
+import io.fd.vpp.jvpp.core.dto.GetNodeIndexReply;
+import io.fd.vpp.jvpp.core.dto.ShowVersion;
+import io.fd.vpp.jvpp.core.dto.ShowVersionReply;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceDetails;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceDetailsReplyDump;
+import io.fd.vpp.jvpp.core.dto.SwInterfaceDump;
+import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade;
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class FutureApiExample {
+
+    private static final Logger LOG = Logger.getLogger(FutureApiExample.class.getName());
+
+    private static void testShowVersion(final FutureJVppCoreFacade jvpp) throws Exception {
+        LOG.info("Sending ShowVersion request...");
+        final Future replyFuture = jvpp.showVersion(new ShowVersion()).toCompletableFuture();
+        final ShowVersionReply reply = replyFuture.get();
+        LOG.info(
+            String.format(
+                "Received ShowVersionReply: context=%d, program=%s, version=%s, buildDate=%s, buildDirectory=%s%n",
+                reply.context, new String(reply.program, StandardCharsets.UTF_8),
+                new String(reply.version, StandardCharsets.UTF_8),
+                new String(reply.buildDate, StandardCharsets.UTF_8),
+                new String(reply.buildDirectory, StandardCharsets.UTF_8)));
+    }
+
+    private static void testEmptyBridgeDomainDump(final FutureJVppCoreFacade jvpp) throws Exception {
+        LOG.info("Sending ShowVersion request...");
+        final BridgeDomainDump request = new BridgeDomainDump();
+        request.bdId = -1; // dump call
+
+        final CompletableFuture
+            replyFuture = jvpp.bridgeDomainDump(request).toCompletableFuture();
+        final BridgeDomainDetailsReplyDump reply = replyFuture.get();
+
+        if (reply == null || reply.bridgeDomainDetails == null) {
+            LOG.severe("Received null response for empty dump: " + reply);
+        } else {
+            LOG.info(
+                String.format(
+                    "Received bridge-domain dump reply with list of bridge-domains: %s",
+                    reply.bridgeDomainDetails));
+        }
+    }
+
+    private static void testGetNodeIndex(final FutureJVppCoreFacade jvpp) {
+        LOG.info("Sending GetNodeIndex request...");
+        final GetNodeIndex request = new GetNodeIndex();
+        request.nodeName = "non-existing-node".getBytes(StandardCharsets.UTF_8);
+        final Future replyFuture = jvpp.getNodeIndex(request).toCompletableFuture();
+        try {
+            final GetNodeIndexReply reply = replyFuture.get();
+            LOG.info(
+                String.format(
+                    "Received GetNodeIndexReply: context=%d, nodeIndex=%d%n", reply.context, reply.nodeIndex));
+        } catch (Exception e) {
+            LOG.log(Level.SEVERE, "GetNodeIndex request failed", e);
+        }
+    }
+
+    private static void testSwInterfaceDump(final FutureJVppCoreFacade jvpp) throws Exception {
+        LOG.info("Sending SwInterfaceDump request...");
+        final SwInterfaceDump request = new SwInterfaceDump();
+        request.nameFilterValid = 0;
+        request.nameFilter = "".getBytes(StandardCharsets.UTF_8);
+
+        final Future replyFuture = jvpp.swInterfaceDump(request).toCompletableFuture();
+        final SwInterfaceDetailsReplyDump reply = replyFuture.get();
+        for (SwInterfaceDetails details : reply.swInterfaceDetails) {
+            Objects.requireNonNull(details, "reply.swInterfaceDetails contains null element!");
+            LOG.info(
+                String.format("Received SwInterfaceDetails: interfaceName=%s, l2AddressLength=%d, adminUpDown=%d, "
+                        + "linkUpDown=%d, linkSpeed=%d, linkMtu=%d%n",
+                    new String(details.interfaceName, StandardCharsets.UTF_8),
+                    details.l2AddressLength, details.adminUpDown,
+                    details.linkUpDown, details.linkSpeed, (int) details.linkMtu));
+        }
+    }
+
+    private static void testFutureApi() throws Exception {
+        LOG.info("Testing Java future API");
+        try (final JVppRegistry registry = new JVppRegistryImpl("FutureApiExample");
+             final FutureJVppCoreFacade jvppFacade = new FutureJVppCoreFacade(registry, new JVppCoreImpl())) {
+            LOG.info("Successfully connected to VPP");
+
+            testEmptyBridgeDomainDump(jvppFacade);
+            testShowVersion(jvppFacade);
+            testGetNodeIndex(jvppFacade);
+            testSwInterfaceDump(jvppFacade);
+
+            LOG.info("Disconnecting...");
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        testFutureApi();
+    }
+}
diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiNotificationExample.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiNotificationExample.java
new file mode 100644
index 00000000..f445dcc8
--- /dev/null
+++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiNotificationExample.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package io.fd.vpp.jvpp.core.examples;
+
+import static io.fd.vpp.jvpp.core.examples.NotificationUtils.getChangeInterfaceState;
+import static io.fd.vpp.jvpp.core.examples.NotificationUtils.getDisableInterfaceNotificationsReq;
+import static io.fd.vpp.jvpp.core.examples.NotificationUtils.getEnableInterfaceNotificationsReq;
+
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.core.JVppCoreImpl;
+import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade;
+
+public class FutureApiNotificationExample {
+
+    private static void testFutureApi() throws Exception {
+        System.out.println("Testing Java future API for notifications");
+        try (final JVppRegistry registry = new JVppRegistryImpl("FutureApiNotificationExample");
+             final FutureJVppCoreFacade jvppFacade = new FutureJVppCoreFacade(registry, new JVppCoreImpl());
+             final AutoCloseable notificationListenerReg =
+                 jvppFacade.getNotificationRegistry()
+                     .registerSwInterfaceSetFlagsNotificationCallback(NotificationUtils::printNotification)) {
+            System.out.println("Successfully connected to VPP");
+            jvppFacade.wantInterfaceEvents(getEnableInterfaceNotificationsReq()).toCompletableFuture().get();
+            System.out.println("Interface events started");
+
+            System.out.println("Changing interface configuration");
+            jvppFacade.swInterfaceSetFlags(getChangeInterfaceState()).toCompletableFuture().get();
+
+            Thread.sleep(1000);
+
+            jvppFacade.wantInterfaceEvents(getDisableInterfaceNotificationsReq()).toCompletableFuture().get();
+            System.out.println("Interface events stopped");
+            System.out.println("Disconnecting...");
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        testFutureApi();
+    }
+}
diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/L2AclExample.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/L2AclExample.java
new file mode 100644
index 00000000..f89043a3
--- /dev/null
+++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/L2AclExample.java
@@ -0,0 +1,195 @@
+/*
+ * 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.
+ */
+
+package io.fd.vpp.jvpp.core.examples;
+
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.core.JVppCoreImpl;
+import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession;
+import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSessionReply;
+import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable;
+import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTableReply;
+import io.fd.vpp.jvpp.core.dto.ClassifySessionDetailsReplyDump;
+import io.fd.vpp.jvpp.core.dto.ClassifySessionDump;
+import io.fd.vpp.jvpp.core.dto.ClassifyTableByInterface;
+import io.fd.vpp.jvpp.core.dto.ClassifyTableByInterfaceReply;
+import io.fd.vpp.jvpp.core.dto.ClassifyTableIds;
+import io.fd.vpp.jvpp.core.dto.ClassifyTableIdsReply;
+import io.fd.vpp.jvpp.core.dto.ClassifyTableInfo;
+import io.fd.vpp.jvpp.core.dto.ClassifyTableInfoReply;
+import io.fd.vpp.jvpp.core.dto.InputAclSetInterface;
+import io.fd.vpp.jvpp.core.dto.InputAclSetInterfaceReply;
+import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade;
+import javax.xml.bind.DatatypeConverter;
+
+/**
+ * 

Tests L2 ACL creation and read.
Equivalent to the following vppctl commands:
+ * + *

{@code
+ * vppctl classify table mask l2 src
+ * vppctl classify session acl-hit-next deny opaque-index 0 table-index 0 match l2 src 01:02:03:04:05:06
+ * vppctl set int input acl intfc local0 l2-table 0
+ * vppctl sh class table verbose
+ * }
+ * 
+ */ +public class L2AclExample { + + private static final int LOCAL0_IFACE_ID = 0; + + private static ClassifyAddDelTable createClassifyTable() { + ClassifyAddDelTable request = new ClassifyAddDelTable(); + request.isAdd = 1; + request.tableIndex = ~0; // default + request.nbuckets = 2; + request.memorySize = 2 << 20; + request.nextTableIndex = ~0; // default + request.missNextIndex = ~0; // default + request.skipNVectors = 0; + request.matchNVectors = 1; + request.mask = + new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, 0x00, 0x00, 0x00, 0x00}; + return request; + } + + private static ClassifyTableInfo createClassifyTableInfoRequest(final int tableId) { + ClassifyTableInfo request = new ClassifyTableInfo(); + request.tableId = tableId; + return request; + } + + private static ClassifyAddDelSession createClassifySession(final int tableIndex) { + ClassifyAddDelSession request = new ClassifyAddDelSession(); + request.isAdd = 1; + request.tableIndex = tableIndex; + request.hitNextIndex = 0; // deny + request.opaqueIndex = 0; + request.advance = 0; // default + // match 01:02:03:04:05:06 mac address + request.match = + new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, + (byte) 0x05, (byte) 0x06, 0x00, 0x00, 0x00, 0x00}; + return request; + } + + private static ClassifySessionDump createClassifySessionDumpRequest(final int newTableIndex) { + ClassifySessionDump request = new ClassifySessionDump(); + request.tableId = newTableIndex; + return request; + } + + private static InputAclSetInterface aclSetInterface() { + InputAclSetInterface request = new InputAclSetInterface(); + request.isAdd = 1; + request.swIfIndex = LOCAL0_IFACE_ID; + request.ip4TableIndex = ~0; // skip + request.ip6TableIndex = ~0; // skip + request.l2TableIndex = 0; + return request; + } + + private static ClassifyTableByInterface createClassifyTableByInterfaceRequest() { + ClassifyTableByInterface request = new ClassifyTableByInterface(); + request.swIfIndex = LOCAL0_IFACE_ID; + return request; + } + + private static void print(ClassifyAddDelTableReply reply) { + System.out.printf("ClassifyAddDelTableReply: %s%n", reply); + } + + private static void print(ClassifyTableIdsReply reply) { + System.out.printf("ClassifyTableIdsReply: %s%n", reply); + } + + private static void print(final ClassifyTableInfoReply reply) { + System.out.println(reply); + if (reply != null) { + System.out.println("Mask hex: " + DatatypeConverter.printHexBinary(reply.mask)); + } + } + + private static void print(ClassifyAddDelSessionReply reply) { + System.out.printf("ClassifyAddDelSessionReply: context=%s%n", reply); + } + + private static void print(final ClassifySessionDetailsReplyDump reply) { + System.out.println(reply); + reply.classifySessionDetails.forEach(detail -> { + System.out.println(detail); + System.out.println("Match hex: " + DatatypeConverter.printHexBinary(detail.match)); + }); + } + + private static void print(final InputAclSetInterfaceReply reply) { + System.out.printf("InputAclSetInterfaceReply: context=%s%n", reply); + } + + private static void print(final ClassifyTableByInterfaceReply reply) { + System.out.printf("ClassifyAddDelTableReply: %s%n", reply); + } + + private static void testL2Acl() throws Exception { + System.out.println("Testing L2 ACLs using Java callback API"); + try (final JVppRegistry registry = new JVppRegistryImpl("L2AclExample"); + final FutureJVppCoreFacade jvppFacade = new FutureJVppCoreFacade(registry, new JVppCoreImpl())) { + + System.out.println("Successfully connected to VPP"); + Thread.sleep(1000); + + final ClassifyAddDelTableReply classifyAddDelTableReply = + jvppFacade.classifyAddDelTable(createClassifyTable()).toCompletableFuture().get(); + print(classifyAddDelTableReply); + + final ClassifyTableIdsReply classifyTableIdsReply = + jvppFacade.classifyTableIds(new ClassifyTableIds()).toCompletableFuture().get(); + print(classifyTableIdsReply); + + final ClassifyTableInfoReply classifyTableInfoReply = + jvppFacade.classifyTableInfo(createClassifyTableInfoRequest(classifyAddDelTableReply.newTableIndex)) + .toCompletableFuture().get(); + print(classifyTableInfoReply); + + final ClassifyAddDelSessionReply classifyAddDelSessionReply = + jvppFacade.classifyAddDelSession(createClassifySession(classifyAddDelTableReply.newTableIndex)) + .toCompletableFuture().get(); + print(classifyAddDelSessionReply); + + final ClassifySessionDetailsReplyDump classifySessionDetailsReplyDump = + jvppFacade.classifySessionDump(createClassifySessionDumpRequest(classifyAddDelTableReply.newTableIndex)) + .toCompletableFuture().get(); + print(classifySessionDetailsReplyDump); + + final InputAclSetInterfaceReply inputAclSetInterfaceReply = + jvppFacade.inputAclSetInterface(aclSetInterface()).toCompletableFuture().get(); + print(inputAclSetInterfaceReply); + + final ClassifyTableByInterfaceReply classifyTableByInterfaceReply = + jvppFacade.classifyTableByInterface(createClassifyTableByInterfaceRequest()).toCompletableFuture() + .get(); + print(classifyTableByInterfaceReply); + + System.out.println("Disconnecting..."); + } + Thread.sleep(1000); + } + + public static void main(String[] args) throws Exception { + testL2Acl(); + } +} diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/LispAdjacencyExample.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/LispAdjacencyExample.java new file mode 100644 index 00000000..f637669d --- /dev/null +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/LispAdjacencyExample.java @@ -0,0 +1,125 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.core.examples; + +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.core.JVppCoreImpl; +import io.fd.vpp.jvpp.core.dto.LispAddDelAdjacency; +import io.fd.vpp.jvpp.core.dto.LispAddDelLocalEid; +import io.fd.vpp.jvpp.core.dto.LispAddDelLocatorSet; +import io.fd.vpp.jvpp.core.dto.LispAddDelRemoteMapping; +import io.fd.vpp.jvpp.core.dto.LispAdjacenciesGet; +import io.fd.vpp.jvpp.core.dto.LispAdjacenciesGetReply; +import io.fd.vpp.jvpp.core.dto.LispEnableDisable; +import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.ExecutionException; +import java.util.logging.Logger; + +/** + * Tests lisp adjacency creation and read (custom vpe.api type support showcase). + */ +public class LispAdjacencyExample { + + private static final Logger LOG = Logger.getLogger(LispAdjacencyExample.class.getName()); + + private static void enableLisp(final FutureJVppCoreFacade jvpp) throws ExecutionException, InterruptedException { + final LispEnableDisable request = new LispEnableDisable(); + request.isEn = 1; + jvpp.lispEnableDisable(request).toCompletableFuture().get(); + LOG.info("Lisp enabled successfully"); + } + + private static void addLocatorSet(final FutureJVppCoreFacade jvpp) throws ExecutionException, InterruptedException { + final LispAddDelLocatorSet request = new LispAddDelLocatorSet(); + request.isAdd = 1; + request.locatorSetName = "ls1".getBytes(StandardCharsets.UTF_8); + jvpp.lispAddDelLocatorSet(request).toCompletableFuture().get(); + LOG.info("Locator set created successfully:" + request.toString()); + } + + private static void addLocalEid(final FutureJVppCoreFacade jvpp) throws ExecutionException, InterruptedException { + final LispAddDelLocalEid request = new LispAddDelLocalEid(); + request.isAdd = 1; + request.locatorSetName = "ls1".getBytes(StandardCharsets.UTF_8); + request.eid = new byte[] {1, 2, 1, 10}; + request.eidType = 0; // ip4 + request.vni = 0; + request.prefixLen = 32; + jvpp.lispAddDelLocalEid(request).toCompletableFuture().get(); + LOG.info("Local EID created successfully:" + request.toString()); + } + + private static void addRemoteMapping(final FutureJVppCoreFacade jvpp) + throws ExecutionException, InterruptedException { + final LispAddDelRemoteMapping request = new LispAddDelRemoteMapping(); + request.isAdd = 1; + request.vni = 0; + request.eid = new byte[] {1, 2, 1, 20}; + request.eidLen = 32; + request.rlocNum = 1; + // FIXME!!!! + //request.rlocs = new byte[] {1, 1, 1, 1, 2, 1, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + jvpp.lispAddDelRemoteMapping(request).toCompletableFuture().get(); + LOG.info("Remote mapping created successfully:" + request.toString()); + } + + private static void addAdjacency(final FutureJVppCoreFacade jvpp) throws ExecutionException, InterruptedException { + final LispAddDelAdjacency request = new LispAddDelAdjacency(); + request.isAdd = 1; + request.leid = new byte[] {1, 2, 1, 10}; + request.leidLen = 32; + request.reid = new byte[] {1, 2, 1, 20}; + request.reidLen = 32; + request.eidType = 0; // ip4 + request.vni = 0; + jvpp.lispAddDelAdjacency(request).toCompletableFuture().get(); + LOG.info("Lisp adjacency created successfully:" + request.toString()); + } + + private static void showAdjacencies(final FutureJVppCoreFacade jvpp) + throws ExecutionException, InterruptedException { + final LispAdjacenciesGetReply reply = + jvpp.lispAdjacenciesGet(new LispAdjacenciesGet()).toCompletableFuture().get(); + LOG.info("Lisp adjacency received successfully:" + reply.toString()); + } + + private static void testAdjacency(final FutureJVppCoreFacade jvpp) throws Exception { + enableLisp(jvpp); + addLocatorSet(jvpp); + addLocalEid(jvpp); + addRemoteMapping(jvpp); + addAdjacency(jvpp); + showAdjacencies(jvpp); + } + + private static void testFutureApi() throws Exception { + LOG.info("Create lisp adjacency test"); + try (final JVppRegistry registry = new JVppRegistryImpl("LispAdjacencyExample"); + final FutureJVppCoreFacade jvppFacade = new FutureJVppCoreFacade(registry, new JVppCoreImpl())) { + LOG.info("Successfully connected to VPP"); + + testAdjacency(jvppFacade); + LOG.info("Disconnecting..."); + } + } + + public static void main(String[] args) throws Exception { + testFutureApi(); + } +} diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/NotificationUtils.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/NotificationUtils.java new file mode 100644 index 00000000..7791cafe --- /dev/null +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/NotificationUtils.java @@ -0,0 +1,53 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.core.examples; + +import java.io.PrintStream; +import io.fd.vpp.jvpp.core.dto.SwInterfaceSetFlags; +import io.fd.vpp.jvpp.core.dto.SwInterfaceSetFlagsNotification; +import io.fd.vpp.jvpp.core.dto.WantInterfaceEvents; + +final class NotificationUtils { + + private NotificationUtils() {} + + static PrintStream printNotification(final SwInterfaceSetFlagsNotification msg) { + return System.out.printf("Received interface notification: ifc: %s%n", msg); + } + + static SwInterfaceSetFlags getChangeInterfaceState() { + final SwInterfaceSetFlags swInterfaceSetFlags = new SwInterfaceSetFlags(); + swInterfaceSetFlags.swIfIndex = 0; + swInterfaceSetFlags.adminUpDown = 1; + swInterfaceSetFlags.deleted = 0; + return swInterfaceSetFlags; + } + + static WantInterfaceEvents getEnableInterfaceNotificationsReq() { + WantInterfaceEvents wantInterfaceEvents = new WantInterfaceEvents(); + wantInterfaceEvents.pid = 1; + wantInterfaceEvents.enableDisable = 1; + return wantInterfaceEvents; + } + + static WantInterfaceEvents getDisableInterfaceNotificationsReq() { + WantInterfaceEvents wantInterfaceEvents = new WantInterfaceEvents(); + wantInterfaceEvents.pid = 1; + wantInterfaceEvents.enableDisable = 0; + return wantInterfaceEvents; + } +} diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/Readme.txt b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/Readme.txt new file mode 100644 index 00000000..d1c79dd8 --- /dev/null +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/Readme.txt @@ -0,0 +1,16 @@ +This package contains basic examples for jvpp. To run the examples: + +- Make sure VPP is running +- From VPP's build-root/ folder execute: + - sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp-native/vpp/vpp-api/java/jvpp-core-17.10.jar io.fd.vpp.jvpp.core.examples.[test name] + +Available examples: +CallbackApiExample - Similar to ControlPingTest, invokes more complex calls (e.g. interface dump) using low level JVpp APIs +CallbackJVppFacadeNotificationExample - Example of interface notifications using Callback based JVpp facade +CallbackJVppFacadeExample - Execution of more complex calls using Callback based JVpp facade +CallbackNotificationApiExample - Example of interface notifications using low level JVpp APIs +CreateSubInterfaceExample - Example of sub-interface creation +FutureApiNotificationExample - Example of interface notifications using Future based JVpp facade +FutureApiExample - Execution of more complex calls using Future based JVpp facade +L2AclExample - Example of L2 ACL creation +LispAdjacencyExample - Example of lisp adjacency creation and read (custom vpe.api type support showcase) diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackApiTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackApiTest.java deleted file mode 100644 index 8fcef967..00000000 --- a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackApiTest.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * 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. - */ - -package io.fd.vpp.jvpp.core.test; - -import io.fd.vpp.jvpp.JVpp; -import io.fd.vpp.jvpp.JVppRegistry; -import io.fd.vpp.jvpp.JVppRegistryImpl; -import io.fd.vpp.jvpp.VppCallbackException; -import io.fd.vpp.jvpp.core.JVppCoreImpl; -import io.fd.vpp.jvpp.core.callback.GetNodeIndexCallback; -import io.fd.vpp.jvpp.core.callback.ShowVersionCallback; -import io.fd.vpp.jvpp.core.callback.SwInterfaceCallback; -import io.fd.vpp.jvpp.core.dto.GetNodeIndex; -import io.fd.vpp.jvpp.core.dto.GetNodeIndexReply; -import io.fd.vpp.jvpp.core.dto.ShowVersion; -import io.fd.vpp.jvpp.core.dto.ShowVersionReply; -import io.fd.vpp.jvpp.core.dto.SwInterfaceDetails; -import io.fd.vpp.jvpp.core.dto.SwInterfaceDump; -import java.nio.charset.StandardCharsets; - -public class CallbackApiTest { - - public static void main(String[] args) throws Exception { - testCallbackApi(); - } - - private static void testCallbackApi() throws Exception { - System.out.println("Testing Java callback API with JVppRegistry"); - try (final JVppRegistry registry = new JVppRegistryImpl("CallbackApiTest"); - final JVpp jvpp = new JVppCoreImpl()) { - registry.register(jvpp, new TestCallback()); - - System.out.println("Sending ShowVersion request..."); - final int result = jvpp.send(new ShowVersion()); - System.out.printf("ShowVersion send result = %d%n", result); - - System.out.println("Sending GetNodeIndex request..."); - GetNodeIndex getNodeIndexRequest = new GetNodeIndex(); - getNodeIndexRequest.nodeName = "non-existing-node".getBytes(StandardCharsets.UTF_8); - jvpp.send(getNodeIndexRequest); - - System.out.println("Sending SwInterfaceDump request..."); - SwInterfaceDump swInterfaceDumpRequest = new SwInterfaceDump(); - swInterfaceDumpRequest.nameFilterValid = 0; - swInterfaceDumpRequest.nameFilter = "".getBytes(StandardCharsets.UTF_8); - jvpp.send(swInterfaceDumpRequest); - - Thread.sleep(1000); - System.out.println("Disconnecting..."); - } - Thread.sleep(1000); - } - - static class TestCallback implements GetNodeIndexCallback, ShowVersionCallback, SwInterfaceCallback { - - @Override - public void onGetNodeIndexReply(final GetNodeIndexReply msg) { - System.out.printf("Received GetNodeIndexReply: %s%n", msg); - } - - @Override - public void onShowVersionReply(final ShowVersionReply msg) { - System.out.printf("Received ShowVersionReply: context=%d, program=%s, version=%s, " - + "buildDate=%s, buildDirectory=%s%n", - msg.context, - new String(msg.program, StandardCharsets.UTF_8), - new String(msg.version, StandardCharsets.UTF_8), - new String(msg.buildDate, StandardCharsets.UTF_8), - new String(msg.buildDirectory, StandardCharsets.UTF_8)); - } - - @Override - public void onSwInterfaceDetails(final SwInterfaceDetails msg) { - System.out.printf("Received SwInterfaceDetails: interfaceName=%s, l2AddressLength=%d, adminUpDown=%d, " - + "linkUpDown=%d, linkSpeed=%d, linkMtu=%d%n", - new String(msg.interfaceName, StandardCharsets.UTF_8), msg.l2AddressLength, msg.adminUpDown, - msg.linkUpDown, msg.linkSpeed, (int) msg.linkMtu); - } - - @Override - public void onError(VppCallbackException ex) { - System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", ex.getMethodName(), - ex.getCtxId(), ex.getErrorCode()); - } - } -} diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackJVppFacadeNotificationTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackJVppFacadeNotificationTest.java deleted file mode 100644 index d84cb034..00000000 --- a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackJVppFacadeNotificationTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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. - */ - -package io.fd.vpp.jvpp.core.test; - -import io.fd.vpp.jvpp.JVppRegistry; -import io.fd.vpp.jvpp.JVppRegistryImpl; -import io.fd.vpp.jvpp.VppCallbackException; -import io.fd.vpp.jvpp.core.JVppCore; -import io.fd.vpp.jvpp.core.JVppCoreImpl; -import io.fd.vpp.jvpp.core.callback.WantInterfaceEventsCallback; -import io.fd.vpp.jvpp.core.callfacade.CallbackJVppCoreFacade; -import io.fd.vpp.jvpp.core.dto.WantInterfaceEventsReply; - -public class CallbackJVppFacadeNotificationTest { - - private static void testCallbackFacade() throws Exception { - System.out.println("Testing CallbackJVppFacade for notifications"); - - try (final JVppRegistry registry = new JVppRegistryImpl("CallbackFacadeTest"); - final JVppCore jvpp = new JVppCoreImpl()) { - final CallbackJVppCoreFacade jvppCallbackFacade = new CallbackJVppCoreFacade(registry, jvpp); - System.out.println("Successfully connected to VPP"); - - final AutoCloseable notificationListenerReg = - jvppCallbackFacade.getNotificationRegistry().registerSwInterfaceSetFlagsNotificationCallback( - NotificationUtils::printNotification - ); - - jvppCallbackFacade.wantInterfaceEvents(NotificationUtils.getEnableInterfaceNotificationsReq(), - new WantInterfaceEventsCallback() { - @Override - public void onWantInterfaceEventsReply(final WantInterfaceEventsReply reply) { - System.out.println("Interface events started"); - } - - @Override - public void onError(final VppCallbackException ex) { - System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", - ex.getMethodName(), ex.getCtxId(), ex.getErrorCode()); - } - }); - - System.out.println("Changing interface configuration"); - NotificationUtils.getChangeInterfaceState().send(jvpp); - - Thread.sleep(1000); - - jvppCallbackFacade.wantInterfaceEvents(NotificationUtils.getDisableInterfaceNotificationsReq(), - new WantInterfaceEventsCallback() { - @Override - public void onWantInterfaceEventsReply(final WantInterfaceEventsReply reply) { - System.out.println("Interface events stopped"); - } - - @Override - public void onError(final VppCallbackException ex) { - System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", - ex.getMethodName(), ex.getCtxId(), ex.getErrorCode()); - } - }); - - notificationListenerReg.close(); - - Thread.sleep(2000); - System.out.println("Disconnecting..."); - } - Thread.sleep(1000); - } - - public static void main(String[] args) throws Exception { - testCallbackFacade(); - } -} diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackJVppFacadeTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackJVppFacadeTest.java deleted file mode 100644 index cf6b0bb3..00000000 --- a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackJVppFacadeTest.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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. - */ - -package io.fd.vpp.jvpp.core.test; - -import io.fd.vpp.jvpp.JVppRegistry; -import io.fd.vpp.jvpp.JVppRegistryImpl; -import io.fd.vpp.jvpp.VppCallbackException; -import io.fd.vpp.jvpp.core.JVppCoreImpl; -import io.fd.vpp.jvpp.core.callback.GetNodeIndexCallback; -import io.fd.vpp.jvpp.core.callback.ShowVersionCallback; -import io.fd.vpp.jvpp.core.callfacade.CallbackJVppCoreFacade; -import io.fd.vpp.jvpp.core.dto.GetNodeIndex; -import io.fd.vpp.jvpp.core.dto.GetNodeIndexReply; -import io.fd.vpp.jvpp.core.dto.ShowVersionReply; -import java.nio.charset.StandardCharsets; - -/** - * CallbackJVppFacade together with CallbackJVppFacadeCallback allow for setting different callback for each request. - * This is more convenient than the approach shown in CallbackApiTest. - */ -public class CallbackJVppFacadeTest { - - private static ShowVersionCallback showVersionCallback1 = new ShowVersionCallback() { - @Override - public void onShowVersionReply(final ShowVersionReply msg) { - System.out.printf("ShowVersionCallback1 received ShowVersionReply: context=%d, program=%s," - + "version=%s, buildDate=%s, buildDirectory=%s%n", msg.context, - new String(msg.program, StandardCharsets.UTF_8), - new String(msg.version, StandardCharsets.UTF_8), - new String(msg.buildDate, StandardCharsets.UTF_8), - new String(msg.buildDirectory, StandardCharsets.UTF_8)); - } - - @Override - public void onError(VppCallbackException ex) { - System.out.printf("Received onError exception in showVersionCallback1: call=%s, reply=%d, context=%d%n", - ex.getMethodName(), ex.getErrorCode(), ex.getCtxId()); - } - }; - - private static ShowVersionCallback showVersionCallback2 = new ShowVersionCallback() { - @Override - public void onShowVersionReply(final ShowVersionReply msg) { - System.out.printf("ShowVersionCallback2 received ShowVersionReply: context=%d, program=%s," - + "version=%s, buildDate=%s, buildDirectory=%s%n", msg.context, - new String(msg.program, StandardCharsets.UTF_8), - new String(msg.version, StandardCharsets.UTF_8), - new String(msg.buildDate, StandardCharsets.UTF_8), - new String(msg.buildDirectory, StandardCharsets.UTF_8)); - } - - @Override - public void onError(VppCallbackException ex) { - System.out.printf("Received onError exception in showVersionCallback2: call=%s, reply=%d, context=%d%n", - ex.getMethodName(), ex.getErrorCode(), ex.getCtxId()); - } - - }; - - private static GetNodeIndexCallback getNodeIndexCallback = new GetNodeIndexCallback() { - @Override - public void onGetNodeIndexReply(final GetNodeIndexReply msg) { - System.out.printf("Received GetNodeIndexReply: %s%n", msg); - } - - @Override - public void onError(VppCallbackException ex) { - System.out.printf("Received onError exception in getNodeIndexCallback: call=%s, reply=%d, context=%d%n", - ex.getMethodName(), ex.getErrorCode(), ex.getCtxId()); - } - }; - - private static void testCallbackFacade() throws Exception { - System.out.println("Testing CallbackJVppFacade"); - - try (final JVppRegistry registry = new JVppRegistryImpl("CallbackFacadeTest"); - final CallbackJVppCoreFacade callbackFacade = new CallbackJVppCoreFacade(registry, new JVppCoreImpl())) { - System.out.println("Successfully connected to VPP"); - - callbackFacade.showVersion(showVersionCallback1); - callbackFacade.showVersion(showVersionCallback2); - - GetNodeIndex getNodeIndexRequest = new GetNodeIndex(); - getNodeIndexRequest.nodeName = "dummyNode".getBytes(StandardCharsets.UTF_8); - callbackFacade.getNodeIndex(getNodeIndexRequest, getNodeIndexCallback); - - Thread.sleep(2000); - System.out.println("Disconnecting..."); - } - Thread.sleep(1000); - } - - public static void main(String[] args) throws Exception { - testCallbackFacade(); - } -} diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackNotificationApiTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackNotificationApiTest.java deleted file mode 100644 index a9f71f11..00000000 --- a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackNotificationApiTest.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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. - */ - -package io.fd.vpp.jvpp.core.test; - -import static io.fd.vpp.jvpp.core.test.NotificationUtils.getChangeInterfaceState; -import static io.fd.vpp.jvpp.core.test.NotificationUtils.getDisableInterfaceNotificationsReq; -import static io.fd.vpp.jvpp.core.test.NotificationUtils.getEnableInterfaceNotificationsReq; -import static io.fd.vpp.jvpp.core.test.NotificationUtils.printNotification; - -import io.fd.vpp.jvpp.JVpp; -import io.fd.vpp.jvpp.JVppRegistry; -import io.fd.vpp.jvpp.JVppRegistryImpl; -import io.fd.vpp.jvpp.VppCallbackException; -import io.fd.vpp.jvpp.core.JVppCoreImpl; -import io.fd.vpp.jvpp.core.callback.SwInterfaceSetFlagsCallback; -import io.fd.vpp.jvpp.core.callback.SwInterfaceSetFlagsNotificationCallback; -import io.fd.vpp.jvpp.core.callback.WantInterfaceEventsCallback; -import io.fd.vpp.jvpp.core.dto.SwInterfaceSetFlagsNotification; -import io.fd.vpp.jvpp.core.dto.SwInterfaceSetFlagsReply; -import io.fd.vpp.jvpp.core.dto.WantInterfaceEventsReply; - -public class CallbackNotificationApiTest { - - private static void testCallbackApi() throws Exception { - System.out.println("Testing Java callback API for notifications"); - try (final JVppRegistry registry = new JVppRegistryImpl("CallbackNotificationTest"); - final JVpp jvpp = new JVppCoreImpl()) { - registry.register(jvpp, new TestCallback()); - System.out.println("Successfully connected to VPP"); - - getEnableInterfaceNotificationsReq().send(jvpp); - System.out.println("Interface notifications started"); - // TODO test ifc dump which also triggers interface flags send - - System.out.println("Changing interface configuration"); - getChangeInterfaceState().send(jvpp); - - // Notifications are received - Thread.sleep(500); - - getDisableInterfaceNotificationsReq().send(jvpp); - System.out.println("Interface events stopped"); - - Thread.sleep(2000); - System.out.println("Disconnecting..."); - } - Thread.sleep(1000); - } - - public static void main(String[] args) throws Exception { - testCallbackApi(); - } - - private static class TestCallback implements SwInterfaceSetFlagsNotificationCallback, - WantInterfaceEventsCallback, SwInterfaceSetFlagsCallback { - - @Override - public void onSwInterfaceSetFlagsNotification( - final SwInterfaceSetFlagsNotification msg) { - printNotification(msg); - } - - @Override - public void onWantInterfaceEventsReply(final WantInterfaceEventsReply wantInterfaceEventsReply) { - System.out.println("Interface notification stream updated"); - } - - @Override - public void onSwInterfaceSetFlagsReply(final SwInterfaceSetFlagsReply swInterfaceSetFlagsReply) { - System.out.println("Interface flags set successfully"); - } - - @Override - public void onError(VppCallbackException ex) { - System.out.printf("Received onError exception in getNodeIndexCallback: call=%s, reply=%d, context=%d%n", - ex.getMethodName(), ex.getErrorCode(), ex.getCtxId()); - - } - } -} diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/ControlPingTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/ControlPingTest.java deleted file mode 100644 index e97f4e3a..00000000 --- a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/ControlPingTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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. - */ - -package io.fd.vpp.jvpp.core.test; - -import io.fd.vpp.jvpp.JVpp; -import io.fd.vpp.jvpp.JVppRegistry; -import io.fd.vpp.jvpp.JVppRegistryImpl; -import io.fd.vpp.jvpp.VppCallbackException; -import io.fd.vpp.jvpp.callback.ControlPingCallback; -import io.fd.vpp.jvpp.core.JVppCoreImpl; -import io.fd.vpp.jvpp.dto.ControlPing; -import io.fd.vpp.jvpp.dto.ControlPingReply; - -public class ControlPingTest { - - private static void testControlPing() throws Exception { - System.out.println("Testing ControlPing using Java callback API"); - try (JVppRegistry registry = new JVppRegistryImpl("ControlPingTest"); - JVpp jvpp = new JVppCoreImpl()) { - - registry.register(jvpp, new ControlPingCallback() { - @Override - public void onControlPingReply(final ControlPingReply reply) { - System.out.printf("Received ControlPingReply: %s%n", reply); - } - - @Override - public void onError(VppCallbackException ex) { - System.out.printf("Received onError exception: call=%s, reply=%d, context=%d ", ex.getMethodName(), - ex.getErrorCode(), ex.getCtxId()); - } - - }); - System.out.println("Successfully connected to VPP"); - Thread.sleep(1000); - - System.out.println("Sending control ping using JVppRegistry"); - registry.controlPing(jvpp.getClass()); - - Thread.sleep(2000); - - System.out.println("Sending control ping using JVpp plugin"); - jvpp.send(new ControlPing()); - - Thread.sleep(2000); - System.out.println("Disconnecting..."); - } - Thread.sleep(1000); - } - - public static void main(String[] args) throws Exception { - testControlPing(); - } -} diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CreateSubInterfaceTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CreateSubInterfaceTest.java deleted file mode 100644 index 7684721f..00000000 --- a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CreateSubInterfaceTest.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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. - */ - -package io.fd.vpp.jvpp.core.test; - -import static java.util.Objects.requireNonNull; - -import io.fd.vpp.jvpp.JVppRegistry; -import io.fd.vpp.jvpp.JVppRegistryImpl; -import io.fd.vpp.jvpp.core.JVppCoreImpl; -import io.fd.vpp.jvpp.core.dto.CreateSubif; -import io.fd.vpp.jvpp.core.dto.CreateSubifReply; -import io.fd.vpp.jvpp.core.dto.SwInterfaceDetailsReplyDump; -import io.fd.vpp.jvpp.core.dto.SwInterfaceDump; -import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade; -import java.nio.charset.StandardCharsets; - -/** - *

Tests sub-interface creation.
Equivalent to:
- * - *

{@code
- * vppctl create sub GigabitEthernet0/9/0 1 dot1q 100 inner-dot1q any
- * }
- * 
- * - * To verify invoke:
- *
{@code
- * vpp_api_test json
- * vat# sw_interface_dump
- * }
- */
-public class CreateSubInterfaceTest {
-
-    private static SwInterfaceDump createSwInterfaceDumpRequest(final String ifaceName) {
-        SwInterfaceDump request = new SwInterfaceDump();
-        request.nameFilter = ifaceName.getBytes(StandardCharsets.UTF_8);
-        request.nameFilterValid = 1;
-        return request;
-    }
-
-    private static void requireSingleIface(final SwInterfaceDetailsReplyDump response, final String ifaceName) {
-        if (response.swInterfaceDetails.size() != 1) {
-            throw new IllegalStateException(
-                String.format("Expected one interface matching filter %s but was %d", ifaceName,
-                    response.swInterfaceDetails.size()));
-        }
-    }
-
-    private static CreateSubif createSubifRequest(final int swIfIndex, final int subId) {
-        CreateSubif request = new CreateSubif();
-        request.swIfIndex = swIfIndex; // super interface id
-        request.subId = subId;
-        request.noTags = 0;
-        request.oneTag = 0;
-        request.twoTags = 1;
-        request.dot1Ad = 0;
-        request.exactMatch = 1;
-        request.defaultSub = 0;
-        request.outerVlanIdAny = 0;
-        request.innerVlanIdAny = 1;
-        request.outerVlanId = 100;
-        request.innerVlanId = 0;
-        return request;
-    }
-
-    private static void print(CreateSubifReply reply) {
-        System.out.printf("CreateSubifReply: %s%n", reply);
-    }
-
-    private static void testCreateSubInterface() throws Exception {
-        System.out.println("Testing sub-interface creation using Java callback API");
-        try (final JVppRegistry registry = new JVppRegistryImpl("CreateSubInterface");
-             final FutureJVppCoreFacade jvppFacade = new FutureJVppCoreFacade(registry, new JVppCoreImpl())) {
-            System.out.println("Successfully connected to VPP");
-            Thread.sleep(1000);
-
-            final String ifaceName = "GigabitEthernet0/8/0";
-
-            final SwInterfaceDetailsReplyDump swInterfaceDetails =
-                jvppFacade.swInterfaceDump(createSwInterfaceDumpRequest(ifaceName)).toCompletableFuture().get();
-
-            requireNonNull(swInterfaceDetails, "swInterfaceDump returned null");
-            requireNonNull(swInterfaceDetails.swInterfaceDetails, "swInterfaceDetails is null");
-            requireSingleIface(swInterfaceDetails, ifaceName);
-
-            final int swIfIndex = swInterfaceDetails.swInterfaceDetails.get(0).swIfIndex;
-            final int subId = 1;
-
-            final CreateSubifReply createSubifReply =
-                jvppFacade.createSubif(createSubifRequest(swIfIndex, subId)).toCompletableFuture().get();
-            print(createSubifReply);
-
-            final String subIfaceName = "GigabitEthernet0/8/0." + subId;
-            final SwInterfaceDetailsReplyDump subIface =
-                jvppFacade.swInterfaceDump(createSwInterfaceDumpRequest(subIfaceName)).toCompletableFuture().get();
-            requireNonNull(swInterfaceDetails, "swInterfaceDump returned null");
-            requireNonNull(subIface.swInterfaceDetails, "swInterfaceDump returned null");
-            requireSingleIface(swInterfaceDetails, ifaceName);
-
-            System.out.println("Disconnecting...");
-        }
-        Thread.sleep(1000);
-    }
-
-    public static void main(String[] args) throws Exception {
-        testCreateSubInterface();
-    }
-}
diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiNotificationTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiNotificationTest.java
deleted file mode 100644
index 9efeae19..00000000
--- a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiNotificationTest.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.
- */
-
-package io.fd.vpp.jvpp.core.test;
-
-import static io.fd.vpp.jvpp.core.test.NotificationUtils.getChangeInterfaceState;
-import static io.fd.vpp.jvpp.core.test.NotificationUtils.getDisableInterfaceNotificationsReq;
-import static io.fd.vpp.jvpp.core.test.NotificationUtils.getEnableInterfaceNotificationsReq;
-
-import io.fd.vpp.jvpp.JVppRegistry;
-import io.fd.vpp.jvpp.JVppRegistryImpl;
-import io.fd.vpp.jvpp.core.JVppCoreImpl;
-import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade;
-
-public class FutureApiNotificationTest {
-
-    private static void testFutureApi() throws Exception {
-        System.out.println("Testing Java future API for notifications");
-        try (final JVppRegistry registry = new JVppRegistryImpl("FutureApiNotificationTest");
-             final FutureJVppCoreFacade jvppFacade = new FutureJVppCoreFacade(registry, new JVppCoreImpl());
-             final AutoCloseable notificationListenerReg =
-                 jvppFacade.getNotificationRegistry()
-                     .registerSwInterfaceSetFlagsNotificationCallback(NotificationUtils::printNotification)) {
-            System.out.println("Successfully connected to VPP");
-            jvppFacade.wantInterfaceEvents(getEnableInterfaceNotificationsReq()).toCompletableFuture().get();
-            System.out.println("Interface events started");
-
-            System.out.println("Changing interface configuration");
-            jvppFacade.swInterfaceSetFlags(getChangeInterfaceState()).toCompletableFuture().get();
-
-            Thread.sleep(1000);
-
-            jvppFacade.wantInterfaceEvents(getDisableInterfaceNotificationsReq()).toCompletableFuture().get();
-            System.out.println("Interface events stopped");
-            System.out.println("Disconnecting...");
-        }
-    }
-
-    public static void main(String[] args) throws Exception {
-        testFutureApi();
-    }
-}
diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java
deleted file mode 100644
index 63659f82..00000000
--- a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * 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.
- */
-
-package io.fd.vpp.jvpp.core.test;
-
-import io.fd.vpp.jvpp.JVppRegistry;
-import io.fd.vpp.jvpp.JVppRegistryImpl;
-import io.fd.vpp.jvpp.core.JVppCoreImpl;
-import io.fd.vpp.jvpp.core.dto.BridgeDomainDetailsReplyDump;
-import io.fd.vpp.jvpp.core.dto.BridgeDomainDump;
-import io.fd.vpp.jvpp.core.dto.GetNodeIndex;
-import io.fd.vpp.jvpp.core.dto.GetNodeIndexReply;
-import io.fd.vpp.jvpp.core.dto.ShowVersion;
-import io.fd.vpp.jvpp.core.dto.ShowVersionReply;
-import io.fd.vpp.jvpp.core.dto.SwInterfaceDetails;
-import io.fd.vpp.jvpp.core.dto.SwInterfaceDetailsReplyDump;
-import io.fd.vpp.jvpp.core.dto.SwInterfaceDump;
-import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade;
-import java.nio.charset.StandardCharsets;
-import java.util.Objects;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Future;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-public class FutureApiTest {
-
-    private static final Logger LOG = Logger.getLogger(FutureApiTest.class.getName());
-
-    private static void testShowVersion(final FutureJVppCoreFacade jvpp) throws Exception {
-        LOG.info("Sending ShowVersion request...");
-        final Future replyFuture = jvpp.showVersion(new ShowVersion()).toCompletableFuture();
-        final ShowVersionReply reply = replyFuture.get();
-        LOG.info(
-            String.format(
-                "Received ShowVersionReply: context=%d, program=%s, version=%s, buildDate=%s, buildDirectory=%s%n",
-                reply.context, new String(reply.program, StandardCharsets.UTF_8),
-                new String(reply.version, StandardCharsets.UTF_8),
-                new String(reply.buildDate, StandardCharsets.UTF_8),
-                new String(reply.buildDirectory, StandardCharsets.UTF_8)));
-    }
-
-    private static void testEmptyBridgeDomainDump(final FutureJVppCoreFacade jvpp) throws Exception {
-        LOG.info("Sending ShowVersion request...");
-        final BridgeDomainDump request = new BridgeDomainDump();
-        request.bdId = -1; // dump call
-
-        final CompletableFuture
-            replyFuture = jvpp.bridgeDomainDump(request).toCompletableFuture();
-        final BridgeDomainDetailsReplyDump reply = replyFuture.get();
-
-        if (reply == null || reply.bridgeDomainDetails == null) {
-            LOG.severe("Received null response for empty dump: " + reply);
-        } else {
-            LOG.info(
-                String.format(
-                    "Received bridge-domain dump reply with list of bridge-domains: %s",
-                    reply.bridgeDomainDetails));
-        }
-    }
-
-    private static void testGetNodeIndex(final FutureJVppCoreFacade jvpp) {
-        LOG.info("Sending GetNodeIndex request...");
-        final GetNodeIndex request = new GetNodeIndex();
-        request.nodeName = "non-existing-node".getBytes(StandardCharsets.UTF_8);
-        final Future replyFuture = jvpp.getNodeIndex(request).toCompletableFuture();
-        try {
-            final GetNodeIndexReply reply = replyFuture.get();
-            LOG.info(
-                String.format(
-                    "Received GetNodeIndexReply: context=%d, nodeIndex=%d%n", reply.context, reply.nodeIndex));
-        } catch (Exception e) {
-            LOG.log(Level.SEVERE, "GetNodeIndex request failed", e);
-        }
-    }
-
-    private static void testSwInterfaceDump(final FutureJVppCoreFacade jvpp) throws Exception {
-        LOG.info("Sending SwInterfaceDump request...");
-        final SwInterfaceDump request = new SwInterfaceDump();
-        request.nameFilterValid = 0;
-        request.nameFilter = "".getBytes(StandardCharsets.UTF_8);
-
-        final Future replyFuture = jvpp.swInterfaceDump(request).toCompletableFuture();
-        final SwInterfaceDetailsReplyDump reply = replyFuture.get();
-        for (SwInterfaceDetails details : reply.swInterfaceDetails) {
-            Objects.requireNonNull(details, "reply.swInterfaceDetails contains null element!");
-            LOG.info(
-                String.format("Received SwInterfaceDetails: interfaceName=%s, l2AddressLength=%d, adminUpDown=%d, "
-                        + "linkUpDown=%d, linkSpeed=%d, linkMtu=%d%n",
-                    new String(details.interfaceName, StandardCharsets.UTF_8),
-                    details.l2AddressLength, details.adminUpDown,
-                    details.linkUpDown, details.linkSpeed, (int) details.linkMtu));
-        }
-    }
-
-    private static void testFutureApi() throws Exception {
-        LOG.info("Testing Java future API");
-        try (final JVppRegistry registry = new JVppRegistryImpl("FutureApiTest");
-             final FutureJVppCoreFacade jvppFacade = new FutureJVppCoreFacade(registry, new JVppCoreImpl())) {
-            LOG.info("Successfully connected to VPP");
-
-            testEmptyBridgeDomainDump(jvppFacade);
-            testShowVersion(jvppFacade);
-            testGetNodeIndex(jvppFacade);
-            testSwInterfaceDump(jvppFacade);
-
-            LOG.info("Disconnecting...");
-        }
-    }
-
-    public static void main(String[] args) throws Exception {
-        testFutureApi();
-    }
-}
diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/L2AclTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/L2AclTest.java
deleted file mode 100644
index 6b3fa993..00000000
--- a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/L2AclTest.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * 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.
- */
-
-package io.fd.vpp.jvpp.core.test;
-
-import io.fd.vpp.jvpp.JVppRegistry;
-import io.fd.vpp.jvpp.JVppRegistryImpl;
-import io.fd.vpp.jvpp.core.JVppCoreImpl;
-import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession;
-import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSessionReply;
-import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable;
-import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTableReply;
-import io.fd.vpp.jvpp.core.dto.ClassifySessionDetailsReplyDump;
-import io.fd.vpp.jvpp.core.dto.ClassifySessionDump;
-import io.fd.vpp.jvpp.core.dto.ClassifyTableByInterface;
-import io.fd.vpp.jvpp.core.dto.ClassifyTableByInterfaceReply;
-import io.fd.vpp.jvpp.core.dto.ClassifyTableIds;
-import io.fd.vpp.jvpp.core.dto.ClassifyTableIdsReply;
-import io.fd.vpp.jvpp.core.dto.ClassifyTableInfo;
-import io.fd.vpp.jvpp.core.dto.ClassifyTableInfoReply;
-import io.fd.vpp.jvpp.core.dto.InputAclSetInterface;
-import io.fd.vpp.jvpp.core.dto.InputAclSetInterfaceReply;
-import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade;
-import javax.xml.bind.DatatypeConverter;
-
-/**
- * 

Tests L2 ACL creation and read.
Equivalent to the following vppctl commands:
- * - *

{@code
- * vppctl classify table mask l2 src
- * vppctl classify session acl-hit-next deny opaque-index 0 table-index 0 match l2 src 01:02:03:04:05:06
- * vppctl set int input acl intfc local0 l2-table 0
- * vppctl sh class table verbose
- * }
- * 
- */ -public class L2AclTest { - - private static final int LOCAL0_IFACE_ID = 0; - - private static ClassifyAddDelTable createClassifyTable() { - ClassifyAddDelTable request = new ClassifyAddDelTable(); - request.isAdd = 1; - request.tableIndex = ~0; // default - request.nbuckets = 2; - request.memorySize = 2 << 20; - request.nextTableIndex = ~0; // default - request.missNextIndex = ~0; // default - request.skipNVectors = 0; - request.matchNVectors = 1; - request.mask = - new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, - (byte) 0xff, (byte) 0xff, 0x00, 0x00, 0x00, 0x00}; - return request; - } - - private static ClassifyTableInfo createClassifyTableInfoRequest(final int tableId) { - ClassifyTableInfo request = new ClassifyTableInfo(); - request.tableId = tableId; - return request; - } - - private static ClassifyAddDelSession createClassifySession(final int tableIndex) { - ClassifyAddDelSession request = new ClassifyAddDelSession(); - request.isAdd = 1; - request.tableIndex = tableIndex; - request.hitNextIndex = 0; // deny - request.opaqueIndex = 0; - request.advance = 0; // default - // match 01:02:03:04:05:06 mac address - request.match = - new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, - (byte) 0x05, (byte) 0x06, 0x00, 0x00, 0x00, 0x00}; - return request; - } - - private static ClassifySessionDump createClassifySessionDumpRequest(final int newTableIndex) { - ClassifySessionDump request = new ClassifySessionDump(); - request.tableId = newTableIndex; - return request; - } - - private static InputAclSetInterface aclSetInterface() { - InputAclSetInterface request = new InputAclSetInterface(); - request.isAdd = 1; - request.swIfIndex = LOCAL0_IFACE_ID; - request.ip4TableIndex = ~0; // skip - request.ip6TableIndex = ~0; // skip - request.l2TableIndex = 0; - return request; - } - - private static ClassifyTableByInterface createClassifyTableByInterfaceRequest() { - ClassifyTableByInterface request = new ClassifyTableByInterface(); - request.swIfIndex = LOCAL0_IFACE_ID; - return request; - } - - private static void print(ClassifyAddDelTableReply reply) { - System.out.printf("ClassifyAddDelTableReply: %s%n", reply); - } - - private static void print(ClassifyTableIdsReply reply) { - System.out.printf("ClassifyTableIdsReply: %s%n", reply); - } - - private static void print(final ClassifyTableInfoReply reply) { - System.out.println(reply); - if (reply != null) { - System.out.println("Mask hex: " + DatatypeConverter.printHexBinary(reply.mask)); - } - } - - private static void print(ClassifyAddDelSessionReply reply) { - System.out.printf("ClassifyAddDelSessionReply: context=%s%n", reply); - } - - private static void print(final ClassifySessionDetailsReplyDump reply) { - System.out.println(reply); - reply.classifySessionDetails.forEach(detail -> { - System.out.println(detail); - System.out.println("Match hex: " + DatatypeConverter.printHexBinary(detail.match)); - }); - } - - private static void print(final InputAclSetInterfaceReply reply) { - System.out.printf("InputAclSetInterfaceReply: context=%s%n", reply); - } - - private static void print(final ClassifyTableByInterfaceReply reply) { - System.out.printf("ClassifyAddDelTableReply: %s%n", reply); - } - - private static void testL2Acl() throws Exception { - System.out.println("Testing L2 ACLs using Java callback API"); - try (final JVppRegistry registry = new JVppRegistryImpl("L2AclTest"); - final FutureJVppCoreFacade jvppFacade = new FutureJVppCoreFacade(registry, new JVppCoreImpl())) { - - System.out.println("Successfully connected to VPP"); - Thread.sleep(1000); - - final ClassifyAddDelTableReply classifyAddDelTableReply = - jvppFacade.classifyAddDelTable(createClassifyTable()).toCompletableFuture().get(); - print(classifyAddDelTableReply); - - final ClassifyTableIdsReply classifyTableIdsReply = - jvppFacade.classifyTableIds(new ClassifyTableIds()).toCompletableFuture().get(); - print(classifyTableIdsReply); - - final ClassifyTableInfoReply classifyTableInfoReply = - jvppFacade.classifyTableInfo(createClassifyTableInfoRequest(classifyAddDelTableReply.newTableIndex)) - .toCompletableFuture().get(); - print(classifyTableInfoReply); - - final ClassifyAddDelSessionReply classifyAddDelSessionReply = - jvppFacade.classifyAddDelSession(createClassifySession(classifyAddDelTableReply.newTableIndex)) - .toCompletableFuture().get(); - print(classifyAddDelSessionReply); - - final ClassifySessionDetailsReplyDump classifySessionDetailsReplyDump = - jvppFacade.classifySessionDump(createClassifySessionDumpRequest(classifyAddDelTableReply.newTableIndex)) - .toCompletableFuture().get(); - print(classifySessionDetailsReplyDump); - - final InputAclSetInterfaceReply inputAclSetInterfaceReply = - jvppFacade.inputAclSetInterface(aclSetInterface()).toCompletableFuture().get(); - print(inputAclSetInterfaceReply); - - final ClassifyTableByInterfaceReply classifyTableByInterfaceReply = - jvppFacade.classifyTableByInterface(createClassifyTableByInterfaceRequest()).toCompletableFuture() - .get(); - print(classifyTableByInterfaceReply); - - System.out.println("Disconnecting..."); - } - Thread.sleep(1000); - } - - public static void main(String[] args) throws Exception { - testL2Acl(); - } -} diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/LispAdjacencyTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/LispAdjacencyTest.java deleted file mode 100644 index e7b17335..00000000 --- a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/LispAdjacencyTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * 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. - */ - -package io.fd.vpp.jvpp.core.test; - -import io.fd.vpp.jvpp.JVppRegistry; -import io.fd.vpp.jvpp.JVppRegistryImpl; -import io.fd.vpp.jvpp.core.JVppCoreImpl; -import io.fd.vpp.jvpp.core.dto.LispAddDelAdjacency; -import io.fd.vpp.jvpp.core.dto.LispAddDelLocalEid; -import io.fd.vpp.jvpp.core.dto.LispAddDelLocatorSet; -import io.fd.vpp.jvpp.core.dto.LispAddDelRemoteMapping; -import io.fd.vpp.jvpp.core.dto.LispAdjacenciesGet; -import io.fd.vpp.jvpp.core.dto.LispAdjacenciesGetReply; -import io.fd.vpp.jvpp.core.dto.LispEnableDisable; -import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade; -import java.nio.charset.StandardCharsets; -import java.util.concurrent.ExecutionException; -import java.util.logging.Logger; - -/** - * Tests lisp adjacency creation and read (custom vpe.api type support showcase). - */ -public class LispAdjacencyTest { - - private static final Logger LOG = Logger.getLogger(LispAdjacencyTest.class.getName()); - - private static void enableLisp(final FutureJVppCoreFacade jvpp) throws ExecutionException, InterruptedException { - final LispEnableDisable request = new LispEnableDisable(); - request.isEn = 1; - jvpp.lispEnableDisable(request).toCompletableFuture().get(); - LOG.info("Lisp enabled successfully"); - } - - private static void addLocatorSet(final FutureJVppCoreFacade jvpp) throws ExecutionException, InterruptedException { - final LispAddDelLocatorSet request = new LispAddDelLocatorSet(); - request.isAdd = 1; - request.locatorSetName = "ls1".getBytes(StandardCharsets.UTF_8); - jvpp.lispAddDelLocatorSet(request).toCompletableFuture().get(); - LOG.info("Locator set created successfully:" + request.toString()); - } - - private static void addLocalEid(final FutureJVppCoreFacade jvpp) throws ExecutionException, InterruptedException { - final LispAddDelLocalEid request = new LispAddDelLocalEid(); - request.isAdd = 1; - request.locatorSetName = "ls1".getBytes(StandardCharsets.UTF_8); - request.eid = new byte[] {1, 2, 1, 10}; - request.eidType = 0; // ip4 - request.vni = 0; - request.prefixLen = 32; - jvpp.lispAddDelLocalEid(request).toCompletableFuture().get(); - LOG.info("Local EID created successfully:" + request.toString()); - } - - private static void addRemoteMapping(final FutureJVppCoreFacade jvpp) - throws ExecutionException, InterruptedException { - final LispAddDelRemoteMapping request = new LispAddDelRemoteMapping(); - request.isAdd = 1; - request.vni = 0; - request.eid = new byte[] {1, 2, 1, 20}; - request.eidLen = 32; - request.rlocNum = 1; - // FIXME!!!! - //request.rlocs = new byte[] {1, 1, 1, 1, 2, 1, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - jvpp.lispAddDelRemoteMapping(request).toCompletableFuture().get(); - LOG.info("Remote mapping created successfully:" + request.toString()); - } - - private static void addAdjacency(final FutureJVppCoreFacade jvpp) throws ExecutionException, InterruptedException { - final LispAddDelAdjacency request = new LispAddDelAdjacency(); - request.isAdd = 1; - request.leid = new byte[] {1, 2, 1, 10}; - request.leidLen = 32; - request.reid = new byte[] {1, 2, 1, 20}; - request.reidLen = 32; - request.eidType = 0; // ip4 - request.vni = 0; - jvpp.lispAddDelAdjacency(request).toCompletableFuture().get(); - LOG.info("Lisp adjacency created successfully:" + request.toString()); - } - - private static void showAdjacencies(final FutureJVppCoreFacade jvpp) - throws ExecutionException, InterruptedException { - final LispAdjacenciesGetReply reply = - jvpp.lispAdjacenciesGet(new LispAdjacenciesGet()).toCompletableFuture().get(); - LOG.info("Lisp adjacency received successfully:" + reply.toString()); - } - - private static void testAdjacency(final FutureJVppCoreFacade jvpp) throws Exception { - enableLisp(jvpp); - addLocatorSet(jvpp); - addLocalEid(jvpp); - addRemoteMapping(jvpp); - addAdjacency(jvpp); - showAdjacencies(jvpp); - } - - private static void testFutureApi() throws Exception { - LOG.info("Create lisp adjacency test"); - try (final JVppRegistry registry = new JVppRegistryImpl("LispAdjacencyTest"); - final FutureJVppCoreFacade jvppFacade = new FutureJVppCoreFacade(registry, new JVppCoreImpl())) { - LOG.info("Successfully connected to VPP"); - - testAdjacency(jvppFacade); - LOG.info("Disconnecting..."); - } - } - - public static void main(String[] args) throws Exception { - testFutureApi(); - } -} diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/NotificationUtils.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/NotificationUtils.java deleted file mode 100644 index f82946c3..00000000 --- a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/NotificationUtils.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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. - */ - -package io.fd.vpp.jvpp.core.test; - -import java.io.PrintStream; -import io.fd.vpp.jvpp.core.dto.SwInterfaceSetFlags; -import io.fd.vpp.jvpp.core.dto.SwInterfaceSetFlagsNotification; -import io.fd.vpp.jvpp.core.dto.WantInterfaceEvents; - -final class NotificationUtils { - - private NotificationUtils() {} - - static PrintStream printNotification(final SwInterfaceSetFlagsNotification msg) { - return System.out.printf("Received interface notification: ifc: %s%n", msg); - } - - static SwInterfaceSetFlags getChangeInterfaceState() { - final SwInterfaceSetFlags swInterfaceSetFlags = new SwInterfaceSetFlags(); - swInterfaceSetFlags.swIfIndex = 0; - swInterfaceSetFlags.adminUpDown = 1; - swInterfaceSetFlags.deleted = 0; - return swInterfaceSetFlags; - } - - static WantInterfaceEvents getEnableInterfaceNotificationsReq() { - WantInterfaceEvents wantInterfaceEvents = new WantInterfaceEvents(); - wantInterfaceEvents.pid = 1; - wantInterfaceEvents.enableDisable = 1; - return wantInterfaceEvents; - } - - static WantInterfaceEvents getDisableInterfaceNotificationsReq() { - WantInterfaceEvents wantInterfaceEvents = new WantInterfaceEvents(); - wantInterfaceEvents.pid = 1; - wantInterfaceEvents.enableDisable = 0; - return wantInterfaceEvents; - } -} diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/Readme.txt b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/Readme.txt deleted file mode 100644 index 1344dc9e..00000000 --- a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/Readme.txt +++ /dev/null @@ -1,17 +0,0 @@ -This package contains basic tests for jvpp. To run the tests: - -- Make sure VPP is running -- From VPP's build-root/ folder execute: - - sudo java -cp build-vpp-native/vpp-api/java/jvpp-registry-17.01.jar:build-vpp-native/vpp-api/java/jvpp-core-17.01.jar io.fd.vpp.jvpp.core.test.[test name] - -Available tests: -CallbackApiTest - Similar to ControlPingTest, invokes more complex calls (e.g. interface dump) using low level JVpp APIs -CallbackJVppFacadeNotificationTest - Tests interface notifications using Callback based JVpp facade -CallbackJVppFacadeTest - Execution of more complex calls using Callback based JVpp facade -CallbackNotificationApiTest - Tests interface notifications using low level JVpp APIs -ControlPingTest - Simple test executing a single control ping using low level JVpp APIs -CreateSubInterfaceTest - Tests sub-interface creation -FutureApiNotificationTest - Tests interface notifications using Future based JVpp facade -FutureApiTest - Execution of more complex calls using Future based JVpp facade -L2AclTest - Tests L2 ACL creation -LispAdjacencyTest - Tests lisp adjacency creation and read (custom vpe.api type support showcase) diff --git a/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/examples/IoamExportApiExample.java b/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/examples/IoamExportApiExample.java new file mode 100644 index 00000000..2f5b7dbb --- /dev/null +++ b/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/examples/IoamExportApiExample.java @@ -0,0 +1,56 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.ioamexport.examples; + +import java.net.InetAddress; + +import io.fd.vpp.jvpp.JVpp; +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.VppCallbackException; +import io.fd.vpp.jvpp.ioamexport.JVppIoamexportImpl; +import io.fd.vpp.jvpp.ioamexport.future.FutureJVppIoamexportFacade; +import io.fd.vpp.jvpp.ioamexport.dto.IoamExportIp6EnableDisable; +import io.fd.vpp.jvpp.ioamexport.dto.IoamExportIp6EnableDisableReply; + +public class IoamExportApiExample { + + public static void main(String[] args) throws Exception { + ioamExportTestApi(); + } + + private static void ioamExportTestApi() throws Exception { + System.out.println("Testing Java API for ioam export plugin"); + try (final JVppRegistry registry = new JVppRegistryImpl("ioamExportApiExample"); + final JVpp jvpp = new JVppIoamexportImpl()) { + FutureJVppIoamexportFacade ioamexportJvpp = new FutureJVppIoamexportFacade(registry,jvpp); + System.out.println("Sending ioam export request..."); + IoamExportIp6EnableDisable request = new IoamExportIp6EnableDisable(); + request.isDisable = 0; + InetAddress collectorAddress = InetAddress.getByName("2001:0DB8:AC10:FE01:0000:0000:0000:0000"); + InetAddress srcAddress = InetAddress.getByName("2001:0DB8:AC10:FE01:0000:0000:0000:0001"); + request.collectorAddress = collectorAddress.getAddress(); + request.srcAddress = srcAddress.getAddress(); + IoamExportIp6EnableDisableReply reply = ioamexportJvpp.ioamExportIp6EnableDisable(request).toCompletableFuture().get(); + System.out.printf("IoamExportIp6EnableDisableReply = "+reply.toString()+"%n"); + + Thread.sleep(1000); + + System.out.println("Disconnecting..."); + } + } +} diff --git a/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/examples/Readme.txt b/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/examples/Readme.txt new file mode 100644 index 00000000..34236f00 --- /dev/null +++ b/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/examples/Readme.txt @@ -0,0 +1 @@ +sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp-native/vpp/vpp-api/java/jvpp-ioamexport-17.10.jar io.fd.vpp.jvpp.ioamexport.examples.IoamExportApiExample diff --git a/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/IoamExportApiTest.java b/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/IoamExportApiTest.java deleted file mode 100644 index cb85f005..00000000 --- a/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/IoamExportApiTest.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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. - */ - -package io.fd.vpp.jvpp.ioamexport.test; - -import java.net.InetAddress; - -import io.fd.vpp.jvpp.JVpp; -import io.fd.vpp.jvpp.JVppRegistry; -import io.fd.vpp.jvpp.JVppRegistryImpl; -import io.fd.vpp.jvpp.VppCallbackException; -import io.fd.vpp.jvpp.ioamexport.JVppIoamexportImpl; -import io.fd.vpp.jvpp.ioamexport.future.FutureJVppIoamexportFacade; -import io.fd.vpp.jvpp.ioamexport.dto.IoamExportIp6EnableDisable; -import io.fd.vpp.jvpp.ioamexport.dto.IoamExportIp6EnableDisableReply; - -public class IoamExportApiTest { - - public static void main(String[] args) throws Exception { - ioamExportTestApi(); - } - - private static void ioamExportTestApi() throws Exception { - System.out.println("Testing Java API for ioam export plugin"); - try (final JVppRegistry registry = new JVppRegistryImpl("ioamExportApiTest"); - final JVpp jvpp = new JVppIoamexportImpl()) { - FutureJVppIoamexportFacade ioamexportJvpp = new FutureJVppIoamexportFacade(registry,jvpp); - System.out.println("Sending ioam export request..."); - IoamExportIp6EnableDisable request = new IoamExportIp6EnableDisable(); - request.isDisable = 0; - InetAddress collectorAddress = InetAddress.getByName("2001:0DB8:AC10:FE01:0000:0000:0000:0000"); - InetAddress srcAddress = InetAddress.getByName("2001:0DB8:AC10:FE01:0000:0000:0000:0001"); - request.collectorAddress = collectorAddress.getAddress(); - request.srcAddress = srcAddress.getAddress(); - IoamExportIp6EnableDisableReply reply = ioamexportJvpp.ioamExportIp6EnableDisable(request).toCompletableFuture().get(); - System.out.printf("IoamExportIp6EnableDisableReply = "+reply.toString()+"%n"); - - Thread.sleep(1000); - - System.out.println("Disconnecting..."); - } - } -} diff --git a/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/Readme.txt b/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/Readme.txt deleted file mode 100644 index 1b38c285..00000000 --- a/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/Readme.txt +++ /dev/null @@ -1 +0,0 @@ -sudo java -cp build-vpp_debug-native/vpp-api/java/jvpp-registry-17.01.jar:build-vpp_debug-native/plugins/ioam-plugin/jvpp-ioam-export-1.0.jar io.fd.vpp.jvpp.ioamexport.test.IoamExportApiTest diff --git a/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/examples/IoamPotApiExample.java b/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/examples/IoamPotApiExample.java new file mode 100644 index 00000000..e97d24fb --- /dev/null +++ b/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/examples/IoamPotApiExample.java @@ -0,0 +1,76 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.ioampot.examples; + +import io.fd.vpp.jvpp.JVpp; +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.VppCallbackException; +import io.fd.vpp.jvpp.ioampot.JVppIoampotImpl; +import io.fd.vpp.jvpp.ioampot.callback.PotProfileAddCallback; +import io.fd.vpp.jvpp.ioampot.dto.PotProfileAdd; +import io.fd.vpp.jvpp.ioampot.dto.PotProfileAddReply; +import java.nio.charset.StandardCharsets; + +public class IoamPotApiExample { + + static class IoamPotTestCallback implements PotProfileAddCallback { + + @Override + public void onPotProfileAddReply(final PotProfileAddReply reply) { + System.out.printf("Received PotProfileAddReply reply: context=%d%n", + reply.context); + } + + @Override + public void onError(VppCallbackException ex) { + System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", ex.getMethodName(), + ex.getCtxId(), ex.getErrorCode()); + } + } + + public static void main(String[] args) throws Exception { + ioamPotTestApi(); + } + + private static void ioamPotTestApi() throws Exception { + System.out.println("Testing Java API for ioam pot plugin"); + try (final JVppRegistry registry = new JVppRegistryImpl("ioamPotApiExample"); + final JVpp jvpp = new JVppIoampotImpl()) { + registry.register(jvpp, new IoamPotTestCallback()); + + System.out.println("Sending ioam pot profile add request..."); + PotProfileAdd request = new PotProfileAdd(); + request.id = 0; + request.validator = 4; + request.secretKey = 1; + request.secretShare = 2; + request.prime = 1234; + request.maxBits = 53; + request.lpc = 1234; + request.polynomialPublic = 1234; + request.listNameLen = (byte)"test pot profile".getBytes(StandardCharsets.UTF_8).length; + request.listName = "test pot profile".getBytes(StandardCharsets.UTF_8); + final int result = jvpp.send(request); + System.out.printf("PotProfileAdd send result = %d%n", result); + + Thread.sleep(1000); + + System.out.println("Disconnecting..."); + } + } +} diff --git a/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/examples/Readme.txt b/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/examples/Readme.txt new file mode 100644 index 00000000..03f0ab7b --- /dev/null +++ b/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/examples/Readme.txt @@ -0,0 +1 @@ +sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp-native/vpp/vpp-api/java/jvpp-ioampot-17.10.jar io.fd.vpp.jvpp.ioampot.examples.IoamPotApiExample diff --git a/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/IoamPotApiTest.java b/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/IoamPotApiTest.java deleted file mode 100644 index 391b25fb..00000000 --- a/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/IoamPotApiTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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. - */ - -package io.fd.vpp.jvpp.ioampot.test; - -import io.fd.vpp.jvpp.JVpp; -import io.fd.vpp.jvpp.JVppRegistry; -import io.fd.vpp.jvpp.JVppRegistryImpl; -import io.fd.vpp.jvpp.VppCallbackException; -import io.fd.vpp.jvpp.ioampot.JVppIoampotImpl; -import io.fd.vpp.jvpp.ioampot.callback.PotProfileAddCallback; -import io.fd.vpp.jvpp.ioampot.dto.PotProfileAdd; -import io.fd.vpp.jvpp.ioampot.dto.PotProfileAddReply; -import java.nio.charset.StandardCharsets; - -public class IoamPotApiTest { - - static class IoamPotTestCallback implements PotProfileAddCallback { - - @Override - public void onPotProfileAddReply(final PotProfileAddReply reply) { - System.out.printf("Received PotProfileAddReply reply: context=%d%n", - reply.context); - } - - @Override - public void onError(VppCallbackException ex) { - System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", ex.getMethodName(), - ex.getCtxId(), ex.getErrorCode()); - } - } - - public static void main(String[] args) throws Exception { - ioamPotTestApi(); - } - - private static void ioamPotTestApi() throws Exception { - System.out.println("Testing Java API for ioam pot plugin"); - try (final JVppRegistry registry = new JVppRegistryImpl("ioamPotApiTest"); - final JVpp jvpp = new JVppIoampotImpl()) { - registry.register(jvpp, new IoamPotTestCallback()); - - System.out.println("Sending ioam pot profile add request..."); - PotProfileAdd request = new PotProfileAdd(); - request.id = 0; - request.validator = 4; - request.secretKey = 1; - request.secretShare = 2; - request.prime = 1234; - request.maxBits = 53; - request.lpc = 1234; - request.polynomialPublic = 1234; - request.listNameLen = (byte)"test pot profile".getBytes(StandardCharsets.UTF_8).length; - request.listName = "test pot profile".getBytes(StandardCharsets.UTF_8); - final int result = jvpp.send(request); - System.out.printf("PotProfileAdd send result = %d%n", result); - - Thread.sleep(1000); - - System.out.println("Disconnecting..."); - } - } -} diff --git a/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/Readme.txt b/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/Readme.txt deleted file mode 100644 index 2323494d..00000000 --- a/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/Readme.txt +++ /dev/null @@ -1 +0,0 @@ -sudo java -cp build-vpp_debug-native/vpp-api/java/jvpp-registry-16.12.jar:build-vpp_debug-native/plugins/ioam-plugin/jvpp-ioam-pot-1.0.jar io.fd.vpp.jvpp.ioampot.test.IoamPotApiTest diff --git a/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/examples/IoamTraceApiExample.java b/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/examples/IoamTraceApiExample.java new file mode 100644 index 00000000..827466bd --- /dev/null +++ b/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/examples/IoamTraceApiExample.java @@ -0,0 +1,77 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.ioamtrace.examples; + +import io.fd.vpp.jvpp.JVpp; +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.VppCallbackException; +import io.fd.vpp.jvpp.ioamtrace.future.FutureJVppIoamtraceFacade; +import io.fd.vpp.jvpp.ioamtrace.JVppIoamtraceImpl; +import io.fd.vpp.jvpp.ioamtrace.callback.TraceProfileAddCallback; +import io.fd.vpp.jvpp.ioamtrace.dto.TraceProfileAdd; +import io.fd.vpp.jvpp.ioamtrace.dto.TraceProfileAddReply; +import io.fd.vpp.jvpp.ioamtrace.dto.TraceProfileShowConfig; +import io.fd.vpp.jvpp.ioamtrace.dto.TraceProfileShowConfigReply; + +public class IoamTraceApiExample { + + static class IoamTraceTestCallback implements TraceProfileAddCallback { + + @Override + public void onTraceProfileAddReply(final TraceProfileAddReply reply) { + System.out.printf("Received TraceProfileAddReply reply: context=%d%n", + reply.context); + } + + @Override + public void onError(VppCallbackException ex) { + System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", ex.getMethodName(), + ex.getCtxId(), ex.getErrorCode()); + } + } + + public static void main(String[] args) throws Exception { + ioamTraceTestApi(); + } + + private static void ioamTraceTestApi() throws Exception { + System.out.println("Testing Java API for ioam trace plugin"); + try (final JVppRegistry registry = new JVppRegistryImpl("ioamTraceApiTest"); + final JVpp jvpp = new JVppIoamtraceImpl()) { + FutureJVppIoamtraceFacade ioamtraceJvpp = new FutureJVppIoamtraceFacade(registry,jvpp); + + System.out.println("Sending ioam trace profile add request..."); + TraceProfileAdd request = new TraceProfileAdd(); + request.traceType = 0x1f; + request.numElts = 4; + request.nodeId = 1; + request.traceTsp = 2; + request.appData = 1234; + final int result = jvpp.send(request); + System.out.printf("TraceProfileAdd send result = %d%n", result); + + Thread.sleep(1000); + + TraceProfileShowConfig showRequest = new TraceProfileShowConfig(); + TraceProfileShowConfigReply reply = ioamtraceJvpp.traceProfileShowConfig(showRequest).toCompletableFuture().get(); + System.out.printf("TraceProfileShowConfig result = "+ reply.toString()); + + System.out.println("Disconnecting..."); + } + } +} diff --git a/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/examples/Readme.txt b/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/examples/Readme.txt new file mode 100644 index 00000000..8c534155 --- /dev/null +++ b/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/examples/Readme.txt @@ -0,0 +1 @@ +sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp-native/vpp/vpp-api/java/jvpp-ioamtrace-17.10.jar io.fd.vpp.jvpp.ioamtrace.examples.IoamTraceApiExample diff --git a/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/IoamTraceApiTest.java b/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/IoamTraceApiTest.java deleted file mode 100644 index bc8c1c3a..00000000 --- a/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/IoamTraceApiTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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. - */ - -package io.fd.vpp.jvpp.ioamtrace.test; - -import io.fd.vpp.jvpp.JVpp; -import io.fd.vpp.jvpp.JVppRegistry; -import io.fd.vpp.jvpp.JVppRegistryImpl; -import io.fd.vpp.jvpp.VppCallbackException; -import io.fd.vpp.jvpp.ioamtrace.future.FutureJVppIoamtraceFacade; -import io.fd.vpp.jvpp.ioamtrace.JVppIoamtraceImpl; -import io.fd.vpp.jvpp.ioamtrace.callback.TraceProfileAddCallback; -import io.fd.vpp.jvpp.ioamtrace.dto.TraceProfileAdd; -import io.fd.vpp.jvpp.ioamtrace.dto.TraceProfileAddReply; -import io.fd.vpp.jvpp.ioamtrace.dto.TraceProfileShowConfig; -import io.fd.vpp.jvpp.ioamtrace.dto.TraceProfileShowConfigReply; - -public class IoamTraceApiTest { - - static class IoamTraceTestCallback implements TraceProfileAddCallback { - - @Override - public void onTraceProfileAddReply(final TraceProfileAddReply reply) { - System.out.printf("Received TraceProfileAddReply reply: context=%d%n", - reply.context); - } - - @Override - public void onError(VppCallbackException ex) { - System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", ex.getMethodName(), - ex.getCtxId(), ex.getErrorCode()); - } - } - - public static void main(String[] args) throws Exception { - ioamTraceTestApi(); - } - - private static void ioamTraceTestApi() throws Exception { - System.out.println("Testing Java API for ioam trace plugin"); - try (final JVppRegistry registry = new JVppRegistryImpl("ioamTraceApiTest"); - final JVpp jvpp = new JVppIoamtraceImpl()) { - FutureJVppIoamtraceFacade ioamtraceJvpp = new FutureJVppIoamtraceFacade(registry,jvpp); - - System.out.println("Sending ioam trace profile add request..."); - TraceProfileAdd request = new TraceProfileAdd(); - request.traceType = 0x1f; - request.numElts = 4; - request.nodeId = 1; - request.traceTsp = 2; - request.appData = 1234; - final int result = jvpp.send(request); - System.out.printf("TraceProfileAdd send result = %d%n", result); - - Thread.sleep(1000); - - TraceProfileShowConfig showRequest = new TraceProfileShowConfig(); - TraceProfileShowConfigReply reply = ioamtraceJvpp.traceProfileShowConfig(showRequest).toCompletableFuture().get(); - System.out.printf("TraceProfileShowConfig result = "+ reply.toString()); - - System.out.println("Disconnecting..."); - } - } -} diff --git a/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/Readme.txt b/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/Readme.txt deleted file mode 100644 index 17e45a81..00000000 --- a/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/Readme.txt +++ /dev/null @@ -1 +0,0 @@ -sudo java -cp build-vpp-native/vpp-api/java/jvpp-registry-17.01.jar:build-vpp-native/plugins/ioam-plugin/jvpp-ioam-trace-1.0.jar io.fd.vpp.jvpp.ioamtrace.test.IoamTraceApiTest diff --git a/src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/examples/CallbackApiExample.java b/src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/examples/CallbackApiExample.java new file mode 100644 index 00000000..f4a2943f --- /dev/null +++ b/src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/examples/CallbackApiExample.java @@ -0,0 +1,68 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.snat.examples; + +import io.fd.vpp.jvpp.JVpp; +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.VppCallbackException; +import io.fd.vpp.jvpp.snat.JVppSnatImpl; +import io.fd.vpp.jvpp.snat.callback.SnatInterfaceAddDelFeatureCallback; +import io.fd.vpp.jvpp.snat.dto.SnatInterfaceAddDelFeature; +import io.fd.vpp.jvpp.snat.dto.SnatInterfaceAddDelFeatureReply; + +public class CallbackApiExample { + + static class TestCallback implements SnatInterfaceAddDelFeatureCallback { + + @Override + public void onSnatInterfaceAddDelFeatureReply(final SnatInterfaceAddDelFeatureReply msg) { + System.out.printf("Received SnatInterfaceAddDelFeatureReply: context=%d%n", + msg.context); + } + + @Override + public void onError(VppCallbackException ex) { + System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", ex.getMethodName(), + ex.getCtxId(), ex.getErrorCode()); + } + } + + public static void main(String[] args) throws Exception { + testCallbackApi(); + } + + private static void testCallbackApi() throws Exception { + System.out.println("Testing Java callback API for snat plugin"); + try (final JVppRegistry registry = new JVppRegistryImpl("SnatCallbackApiTest"); + final JVpp jvpp = new JVppSnatImpl()) { + registry.register(jvpp, new TestCallback()); + + System.out.println("Sending SnatInterfaceAddDelFeature request..."); + SnatInterfaceAddDelFeature request = new SnatInterfaceAddDelFeature(); + request.isAdd = 1; + request.isInside = 1; + request.swIfIndex = 1; + final int result = jvpp.send(request); + System.out.printf("SnatInterfaceAddDelFeature send result = %d%n", result); + + Thread.sleep(1000); + + System.out.println("Disconnecting..."); + } + } +} diff --git a/src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/examples/Readme.txt b/src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/examples/Readme.txt new file mode 100644 index 00000000..470850ee --- /dev/null +++ b/src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/examples/Readme.txt @@ -0,0 +1 @@ +sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp-native/vpp/vpp-api/java/jvpp-snat-17.10.jar io.fd.vpp.jvpp.snat.examples.CallbackApiExample diff --git a/src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/test/CallbackApiTest.java b/src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/test/CallbackApiTest.java deleted file mode 100644 index 32165d96..00000000 --- a/src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/test/CallbackApiTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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. - */ - -package io.fd.vpp.jvpp.snat.test; - -import io.fd.vpp.jvpp.JVpp; -import io.fd.vpp.jvpp.JVppRegistry; -import io.fd.vpp.jvpp.JVppRegistryImpl; -import io.fd.vpp.jvpp.VppCallbackException; -import io.fd.vpp.jvpp.snat.JVppSnatImpl; -import io.fd.vpp.jvpp.snat.callback.SnatInterfaceAddDelFeatureCallback; -import io.fd.vpp.jvpp.snat.dto.SnatInterfaceAddDelFeature; -import io.fd.vpp.jvpp.snat.dto.SnatInterfaceAddDelFeatureReply; - -public class CallbackApiTest { - - static class TestCallback implements SnatInterfaceAddDelFeatureCallback { - - @Override - public void onSnatInterfaceAddDelFeatureReply(final SnatInterfaceAddDelFeatureReply msg) { - System.out.printf("Received SnatInterfaceAddDelFeatureReply: context=%d%n", - msg.context); - } - - @Override - public void onError(VppCallbackException ex) { - System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", ex.getMethodName(), - ex.getCtxId(), ex.getErrorCode()); - } - } - - public static void main(String[] args) throws Exception { - testCallbackApi(); - } - - private static void testCallbackApi() throws Exception { - System.out.println("Testing Java callback API for snat plugin"); - try (final JVppRegistry registry = new JVppRegistryImpl("SnatCallbackApiTest"); - final JVpp jvpp = new JVppSnatImpl()) { - registry.register(jvpp, new TestCallback()); - - System.out.println("Sending SnatInterfaceAddDelFeature request..."); - SnatInterfaceAddDelFeature request = new SnatInterfaceAddDelFeature(); - request.isAdd = 1; - request.isInside = 1; - request.swIfIndex = 1; - final int result = jvpp.send(request); - System.out.printf("SnatInterfaceAddDelFeature send result = %d%n", result); - - Thread.sleep(1000); - - System.out.println("Disconnecting..."); - } - } -} diff --git a/src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/test/Readme.txt b/src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/test/Readme.txt deleted file mode 100644 index a2b0c41f..00000000 --- a/src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/test/Readme.txt +++ /dev/null @@ -1 +0,0 @@ -sudo java -cp build-vpp-native/vpp-api/java/jvpp-registry-17.01.jar:build-vpp-native/plugins/snat-plugin/jvpp-snat-1.0.jar io.fd.vpp.jvpp.snat.test.CallbackApiTest -- cgit 1.2.3-korg From 63a46fc9becba3a002ac923b7932f574a1bfe809 Mon Sep 17 00:00:00 2001 From: Matej Perina Date: Thu, 20 Jul 2017 15:35:19 +0200 Subject: jvpp: provide more detailed exception logs (VPP-436) Error descriptions provided in api_errno.h are never used, only error tag/name and number make it to enum vnet_api_error_t so new macro is introduced in jvpp_common.c to extract message according to error number and passed to VppCallbackException constuctor. Change-Id: If2a687752807d7250d9226987583df00f151e87f Signed-off-by: Matej Perina Signed-off-by: Marek Gradzki --- src/vpp-api/java/jvpp-common/jvpp_common.c | 17 ++++++++++++++++- .../io/fd/vpp/jvpp/VppBaseCallException.java | 19 +++++++++++++++++++ .../io/fd/vpp/jvpp/VppCallbackException.java | 5 +++-- 3 files changed, 38 insertions(+), 3 deletions(-) (limited to 'src/vpp-api/java') diff --git a/src/vpp-api/java/jvpp-common/jvpp_common.c b/src/vpp-api/java/jvpp-common/jvpp_common.c index b88c0ea2..c00298bf 100644 --- a/src/vpp-api/java/jvpp-common/jvpp_common.c +++ b/src/vpp-api/java/jvpp-common/jvpp_common.c @@ -14,6 +14,7 @@ */ #define _GNU_SOURCE /* for strcasestr(3) */ +#include #include "jvpp_common.h" #ifndef JVPP_DEBUG @@ -26,6 +27,16 @@ #define DEBUG_LOG(...) #endif +#define _(error,errorCode,msg) \ +if (errorCode == code) \ + message = msg; \ +else + +#define get_error_message(errno) \ +int code = errno; \ +foreach_vnet_api_error \ + message = "Reason unknown"; + /* shared jvpp main structure */ jvpp_main_t jvpp_main __attribute__((aligned (64))); @@ -40,7 +51,7 @@ void call_on_error(const char* callName, int contextId, int retval, return; } jmethodID excConstructor = (*env)->GetMethodID(env, callbackExceptionClass, - "", "(Ljava/lang/String;II)V"); + "", "(Ljava/lang/String;Ljava/lang/String;II)V"); if (!excConstructor) { DEBUG_LOG("CallOnError : excConstructor is null!\n"); return; @@ -52,8 +63,11 @@ void call_on_error(const char* callName, int contextId, int retval, return; } + char *message; + get_error_message(clib_net_to_host_u32(retval)); jobject excObject = (*env)->NewObject(env, callbackExceptionClass, excConstructor, (*env)->NewStringUTF(env, callName), + (*env)->NewStringUTF(env, message), clib_net_to_host_u32(contextId), clib_net_to_host_u32(retval)); if (!excObject) { DEBUG_LOG("CallOnError : excObject is null!\n"); @@ -63,6 +77,7 @@ void call_on_error(const char* callName, int contextId, int retval, (*env)->CallVoidMethod(env, callbackObject, callbackExcMethod, excObject); DEBUG_LOG("CallOnError : Response sent\n"); } +#undef _ u32 get_message_id(JNIEnv *env, const char *key) { uword *p = hash_get(jvpp_main.messages_hash, key); diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppBaseCallException.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppBaseCallException.java index d71e3055..7fc1682b 100644 --- a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppBaseCallException.java +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppBaseCallException.java @@ -16,6 +16,7 @@ package io.fd.vpp.jvpp; + /** * Base exception representing failed operation of JVpp request call */ @@ -40,6 +41,24 @@ public abstract class VppBaseCallException extends Exception { } } + /** + * Constructs an VppCallbackException with the specified api method name, error description and error code. + * + * @param methodName name of a method, which invocation or execution failed + * @param message description of error reason + * @param errorCode negative error code value associated with this failure + * @throws NullPointerException if apiMethodName is null + */ + public VppBaseCallException(final String methodName, final String message, final int errorCode) { + super(String.format("vppApi.%s failed: %s (error code: %d)", methodName,message, errorCode)); + this.methodName = java.util.Objects.requireNonNull(methodName, "apiMethodName is null!"); + this.errorCode = errorCode; + if(errorCode >= 0) { + throw new IllegalArgumentException("Error code must be < 0. Was " + errorCode + + " for " + methodName ); + } + } + /** * Returns name of a method, which invocation failed. * diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppCallbackException.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppCallbackException.java index ccfcbd3c..adcc5d26 100644 --- a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppCallbackException.java +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppCallbackException.java @@ -26,12 +26,13 @@ public class VppCallbackException extends VppBaseCallException { * Constructs an VppCallbackException with the specified api method name and error code. * * @param methodName name of a method, which invocation failed. + * @param message description of error reason * @param ctxId api request context identifier * @param errorCode negative error code value associated with this failure * @throws NullPointerException if apiMethodName is null */ - public VppCallbackException(final String methodName, final int ctxId, final int errorCode ){ - super(methodName, errorCode); + public VppCallbackException(final String methodName, final String message, final int ctxId, final int errorCode ){ + super(methodName, message, errorCode); this.ctxId = ctxId; } -- cgit 1.2.3-korg From 62f9cdd82c52dc05cb89a742d21aba013ce526d4 Mon Sep 17 00:00:00 2001 From: Hongjun Ni Date: Tue, 4 Jul 2017 20:11:57 +0800 Subject: Add PPPoE Plugin Supports 64K PPPoE sessions This plugin adds three graph nodes: 1) pppoe-input for PPPoE decapsulation 2) pppoe-encap for PPPoE encapsulation 3) pppoe-tap-dispatch for control plane process Below is the configuration to make PPPoE CP and DP work: vim /etc/vpp/startup.conf tuntap { enable ethernet name newtap } create pppoe tap tap-if-index 1 //Configure it after a subscriber's PPPoE discovery and PPP link establishment succeeds: create pppoe session client-ip 100.1.2.1 session-id 1 client-mac 00:11:01:00:00:01 show pppoe fib show pppoe session Change-Id: I73e724b6bf7c3e4181a9914c5752da1fa72d7e60 Signed-off-by: Hongjun Ni --- MAINTAINERS | 5 + src/configure.ac | 1 + src/plugins/Makefile.am | 4 + src/plugins/pppoe.am | 40 ++ src/plugins/pppoe/pppoe.api | 90 +++++ src/plugins/pppoe/pppoe.c | 660 +++++++++++++++++++++++++++++++ src/plugins/pppoe/pppoe.h | 295 ++++++++++++++ src/plugins/pppoe/pppoe_all_api_h.h | 18 + src/plugins/pppoe/pppoe_api.c | 224 +++++++++++ src/plugins/pppoe/pppoe_decap.c | 422 ++++++++++++++++++++ src/plugins/pppoe/pppoe_encap.c | 384 ++++++++++++++++++ src/plugins/pppoe/pppoe_error.def | 18 + src/plugins/pppoe/pppoe_msg_enum.h | 31 ++ src/plugins/pppoe/pppoe_tap.c | 89 +++++ src/plugins/pppoe/pppoe_tap_node.c | 297 ++++++++++++++ src/plugins/pppoe/pppoe_test.c | 330 ++++++++++++++++ src/vpp-api/java/Makefile.am | 20 + src/vpp-api/java/jvpp-pppoe/jvpp_pppoe.c | 107 +++++ src/vpp-api/java/jvpp-pppoe/jvpp_pppoe.h | 42 ++ test/test_pppoe.py | 605 ++++++++++++++++++++++++++++ test/vpp_papi_provider.py | 26 ++ test/vpp_pppoe_interface.py | 79 ++++ 22 files changed, 3787 insertions(+) create mode 100644 src/plugins/pppoe.am create mode 100644 src/plugins/pppoe/pppoe.api create mode 100644 src/plugins/pppoe/pppoe.c create mode 100644 src/plugins/pppoe/pppoe.h create mode 100644 src/plugins/pppoe/pppoe_all_api_h.h create mode 100644 src/plugins/pppoe/pppoe_api.c create mode 100644 src/plugins/pppoe/pppoe_decap.c create mode 100644 src/plugins/pppoe/pppoe_encap.c create mode 100644 src/plugins/pppoe/pppoe_error.def create mode 100644 src/plugins/pppoe/pppoe_msg_enum.h create mode 100644 src/plugins/pppoe/pppoe_tap.c create mode 100644 src/plugins/pppoe/pppoe_tap_node.c create mode 100644 src/plugins/pppoe/pppoe_test.c create mode 100644 src/vpp-api/java/jvpp-pppoe/jvpp_pppoe.c create mode 100644 src/vpp-api/java/jvpp-pppoe/jvpp_pppoe.h create mode 100644 test/test_pppoe.py create mode 100644 test/vpp_pppoe_interface.py (limited to 'src/vpp-api/java') diff --git a/MAINTAINERS b/MAINTAINERS index 55fe00bf..4f2918fa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -132,6 +132,11 @@ M: Hongjun Ni F: src/plugins/gtpu/ F: src/plugins/gtpu.am +Plugin - PPPoE +M: Hongjun Ni +F: src/plugins/pppoe/ +F: src/plugins/pppoe.am + Test Infrastructure M: Klement Sekera F: test/ diff --git a/src/configure.ac b/src/configure.ac index cb00d0bd..7a038c2e 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -167,6 +167,7 @@ PLUGIN_ENABLED(ioam) PLUGIN_ENABLED(ixge) PLUGIN_ENABLED(lb) PLUGIN_ENABLED(memif) +PLUGIN_ENABLED(pppoe) PLUGIN_ENABLED(sixrd) PLUGIN_ENABLED(snat) diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index f26d0fd2..8c7b3fac 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -66,6 +66,10 @@ if ENABLE_MEMIF_PLUGIN include memif.am endif +if ENABLE_PPPOE_PLUGIN +include pppoe.am +endif + if ENABLE_SIXRD_PLUGIN include sixrd.am endif diff --git a/src/plugins/pppoe.am b/src/plugins/pppoe.am new file mode 100644 index 00000000..28bd20a0 --- /dev/null +++ b/src/plugins/pppoe.am @@ -0,0 +1,40 @@ +# Copyright (c) 2017 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 += pppoe_test_plugin.la +vppplugins_LTLIBRARIES += pppoe_plugin.la + +pppoe_plugin_la_SOURCES = \ + pppoe/pppoe_decap.c \ + pppoe/pppoe_encap.c \ + pppoe/pppoe_tap.c \ + pppoe/pppoe_tap_node.c \ + pppoe/pppoe.c \ + pppoe/pppoe_api.c + +BUILT_SOURCES += \ + pppoe/pppoe.api.h \ + pppoe/pppoe.api.json + +API_FILES += pppoe/pppoe.api + +nobase_apiinclude_HEADERS += \ + pppoe/pppoe_all_api_h.h \ + pppoe/pppoe_msg_enum.h \ + pppoe/pppoe.api.h + +pppoe_test_plugin_la_SOURCES = \ + pppoe/pppoe_test.c \ + pppoe/pppoe_plugin.api.h + +# vi:syntax=automake diff --git a/src/plugins/pppoe/pppoe.api b/src/plugins/pppoe/pppoe.api new file mode 100644 index 00000000..e8cd989f --- /dev/null +++ b/src/plugins/pppoe/pppoe.api @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2017 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. + */ + +/** \brief Set or delete an PPPOE session + @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 - client_ip and dst_address is ipv6 or not + @param session_id - PPPoE session ID + @param client_ip - PPPOE session's client address. + @param decap_vrf_id - the vrf index for pppoe decaped packet + @param client_mac - the client ethernet address +*/ +define pppoe_add_del_session +{ + u32 client_index; + u32 context; + u8 is_add; + u8 is_ipv6; + u16 session_id; + u8 client_ip[16]; + u32 decap_vrf_id; + u8 client_mac[6]; +}; + +/** \brief reply for set or delete an PPPOE session + @param context - sender context, to match reply w/ request + @param retval - return code + @param sw_if_index - software index of the interface +*/ +define pppoe_add_del_session_reply +{ + u32 context; + i32 retval; + u32 sw_if_index; +}; + +/** \brief Dump PPPOE session + @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 pppoe_session_dump +{ + u32 client_index; + u32 context; + u32 sw_if_index; +}; + +/** \brief dump details of an PPPOE session + @param context - sender context, to match reply w/ request + @param sw_if_index - software index of the interface + @param is_ipv6 - client_ip and dst_address is ipv6 or not + @param session_id - PPPoE session ID + @param client_ip - PPPOE session's client address. + @param encap_if_index - the index of tx interface for pppoe encaped packet + @param decap_vrf_id - the vrf index for pppoe decaped packet + @param local_mac - the local ethernet address + @param client_mac - the client ethernet address +*/ +define pppoe_session_details +{ + u32 context; + u32 sw_if_index; + u8 is_ipv6; + u16 session_id; + u8 client_ip[16]; + u32 encap_if_index; + u32 decap_vrf_id; + u8 local_mac[6]; + u8 client_mac[6]; +}; + +/* + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/pppoe/pppoe.c b/src/plugins/pppoe/pppoe.c new file mode 100644 index 00000000..4c63902a --- /dev/null +++ b/src/plugins/pppoe/pppoe.c @@ -0,0 +1,660 @@ +/* + *------------------------------------------------------------------ + * Copyright (c) 2017 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +pppoe_main_t pppoe_main; + +u8 * +format_pppoe_session (u8 * s, va_list * args) +{ + pppoe_session_t *t = va_arg (*args, pppoe_session_t *); + pppoe_main_t *pem = &pppoe_main; + + s = format (s, "[%d] sw-if-index %d client-ip %U session-id %d ", + t - pem->sessions, t->sw_if_index, + format_ip46_address, &t->client_ip, IP46_TYPE_ANY, + t->session_id); + + s = format (s, "encap-if-index %d decap-fib-index %d\n", + t->encap_if_index, t->decap_fib_index); + + s = format (s, " local-mac %U client-mac %U", + format_ethernet_address, t->local_mac, + format_ethernet_address, t->client_mac); + + return s; +} + +static u8 * +format_pppoe_name (u8 * s, va_list * args) +{ + u32 dev_instance = va_arg (*args, u32); + return format (s, "pppoe_session%d", dev_instance); +} + +static uword +dummy_interface_tx (vlib_main_t * vm, + vlib_node_runtime_t * node, vlib_frame_t * frame) +{ + clib_warning ("you shouldn't be here, leaking buffers..."); + return frame->n_vectors; +} + +static clib_error_t * +pppoe_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags) +{ + u32 hw_flags = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ? + VNET_HW_INTERFACE_FLAG_LINK_UP : 0; + vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags); + + return /* no error */ 0; +} + +/* *INDENT-OFF* */ +VNET_DEVICE_CLASS (pppoe_device_class,static) = { + .name = "PPPPOE", + .format_device_name = format_pppoe_name, + .format_tx_trace = format_pppoe_encap_trace, + .tx_function = dummy_interface_tx, + .admin_up_down_function = pppoe_interface_admin_up_down, +}; +/* *INDENT-ON* */ + +static u8 * +format_pppoe_header_with_length (u8 * s, va_list * args) +{ + u32 dev_instance = va_arg (*args, u32); + s = format (s, "unimplemented dev %u", dev_instance); + return s; +} + +/* *INDENT-OFF* */ +VNET_HW_INTERFACE_CLASS (pppoe_hw_class) = +{ + .name = "PPPPOE", + .format_header = format_pppoe_header_with_length, + .build_rewrite = default_build_rewrite, + .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P, +}; +/* *INDENT-ON* */ + +#define foreach_copy_field \ +_(session_id) \ +_(encap_if_index) \ +_(decap_fib_index) \ +_(client_ip) + +static void +eth_pppoe_rewrite (pppoe_session_t * t, bool is_ip6) +{ + u8 *rw = 0; + int len = sizeof (pppoe_header_t) + sizeof (ethernet_header_t); + + vec_validate_aligned (rw, len - 1, CLIB_CACHE_LINE_BYTES); + + ethernet_header_t *eth_hdr = (ethernet_header_t *) rw; + clib_memcpy (eth_hdr->dst_address, t->client_mac, 6); + clib_memcpy (eth_hdr->src_address, t->local_mac, 6); + eth_hdr->type = clib_host_to_net_u16 (ETHERNET_TYPE_PPPOE_SESSION); + + pppoe_header_t *pppoe = (pppoe_header_t *) (eth_hdr + 1); + pppoe->ver_type = PPPOE_VER_TYPE; + pppoe->code = 0; + pppoe->session_id = clib_host_to_net_u16 (t->session_id); + pppoe->length = 0; /* To be filled in at run-time */ + + if (!is_ip6) + { + pppoe->ppp_proto = clib_host_to_net_u16 (PPP_PROTOCOL_ip4); + } + else + { + pppoe->ppp_proto = clib_host_to_net_u16 (PPP_PROTOCOL_ip6); + } + + t->rewrite = rw; + _vec_len (t->rewrite) = len; + + return; +} + +static bool +pppoe_decap_next_is_valid (pppoe_main_t * pem, u32 is_ip6, + u32 decap_fib_index) +{ + vlib_main_t *vm = pem->vlib_main; + u32 input_idx = (!is_ip6) ? ip4_input_node.index : ip6_input_node.index; + vlib_node_runtime_t *r = vlib_node_get_runtime (vm, input_idx); + + return decap_fib_index < r->n_next_nodes; +} + +int vnet_pppoe_add_del_session + (vnet_pppoe_add_del_session_args_t * a, u32 * sw_if_indexp) +{ + pppoe_main_t *pem = &pppoe_main; + pppoe_session_t *t = 0; + vnet_main_t *vnm = pem->vnet_main; + u32 hw_if_index = ~0; + u32 sw_if_index = ~0; + u32 is_ip6 = a->is_ip6; + pppoe_entry_key_t cached_key; + pppoe_entry_result_t cached_result; + u32 bucket; + pppoe_entry_key_t key; + pppoe_entry_result_t result; + vnet_hw_interface_t *hi; + vnet_sw_interface_t *si; + fib_prefix_t pfx; + + cached_key.raw = ~0; + cached_result.raw = ~0; /* warning be gone */ + memset (&pfx, 0, sizeof (pfx)); + + if (!is_ip6) + { + pfx.fp_addr.ip4.as_u32 = a->client_ip.ip4.as_u32; + pfx.fp_len = 32; + pfx.fp_proto = FIB_PROTOCOL_IP4; + } + else + { + pfx.fp_addr.ip6.as_u64[0] = a->client_ip.ip6.as_u64[0]; + pfx.fp_addr.ip6.as_u64[1] = a->client_ip.ip6.as_u64[1]; + pfx.fp_len = 128; + pfx.fp_proto = FIB_PROTOCOL_IP6; + } + + /* Get encap_if_index and local mac address */ + pppoe_lookup_1 (&pem->session_table, &cached_key, &cached_result, + a->client_mac, clib_host_to_net_u16 (a->session_id), + &key, &bucket, &result); + a->encap_if_index = result.fields.sw_if_index; + + if (a->encap_if_index == ~0) + return VNET_API_ERROR_INVALID_SW_IF_INDEX; + + si = vnet_get_sw_interface (vnm, a->encap_if_index); + hi = vnet_get_hw_interface (vnm, si->hw_if_index); + + + if (a->is_add) + { + /* adding a session: session must not already exist */ + if (result.fields.session_index != ~0) + return VNET_API_ERROR_TUNNEL_EXIST; + + /*if not set explicitly, default to ip4 */ + if (!pppoe_decap_next_is_valid (pem, is_ip6, a->decap_fib_index)) + return VNET_API_ERROR_INVALID_DECAP_NEXT; + + pool_get_aligned (pem->sessions, t, CLIB_CACHE_LINE_BYTES); + memset (t, 0, sizeof (*t)); + + clib_memcpy (t->local_mac, hi->hw_address, 6); + + /* copy from arg structure */ +#define _(x) t->x = a->x; + foreach_copy_field; +#undef _ + + clib_memcpy (t->client_mac, a->client_mac, 6); + + eth_pppoe_rewrite (t, is_ip6); + + /* update pppoe fib with session_index */ + result.fields.session_index = t - pem->sessions; + pppoe_update_1 (&pem->session_table, + a->client_mac, clib_host_to_net_u16 (a->session_id), + &key, &bucket, &result); + + vnet_hw_interface_t *hi; + if (vec_len (pem->free_pppoe_session_hw_if_indices) > 0) + { + vnet_interface_main_t *im = &vnm->interface_main; + hw_if_index = pem->free_pppoe_session_hw_if_indices + [vec_len (pem->free_pppoe_session_hw_if_indices) - 1]; + _vec_len (pem->free_pppoe_session_hw_if_indices) -= 1; + + hi = vnet_get_hw_interface (vnm, hw_if_index); + hi->dev_instance = t - pem->sessions; + hi->hw_instance = hi->dev_instance; + + /* clear old stats of freed session before reuse */ + sw_if_index = hi->sw_if_index; + vnet_interface_counter_lock (im); + vlib_zero_combined_counter + (&im->combined_sw_if_counters[VNET_INTERFACE_COUNTER_TX], + sw_if_index); + vlib_zero_combined_counter (&im->combined_sw_if_counters + [VNET_INTERFACE_COUNTER_RX], + sw_if_index); + vlib_zero_simple_counter (&im->sw_if_counters + [VNET_INTERFACE_COUNTER_DROP], + sw_if_index); + vnet_interface_counter_unlock (im); + } + else + { + hw_if_index = vnet_register_interface + (vnm, pppoe_device_class.index, t - pem->sessions, + pppoe_hw_class.index, t - pem->sessions); + hi = vnet_get_hw_interface (vnm, hw_if_index); + } + + t->hw_if_index = hw_if_index; + t->sw_if_index = sw_if_index = hi->sw_if_index; + + vec_validate_init_empty (pem->session_index_by_sw_if_index, sw_if_index, + ~0); + pem->session_index_by_sw_if_index[sw_if_index] = t - pem->sessions; + + vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, sw_if_index); + si->flags &= ~VNET_SW_INTERFACE_FLAG_HIDDEN; + vnet_sw_interface_set_flags (vnm, sw_if_index, + VNET_SW_INTERFACE_FLAG_ADMIN_UP); + + /* Set pppoe session output node */ + hi->output_node_index = pppoe_encap_node.index; + + /* add reverse route for client ip */ + fib_table_entry_path_add (a->decap_fib_index, &pfx, + FIB_SOURCE_PLUGIN_HI, FIB_ENTRY_FLAG_NONE, + pfx.fp_proto, &pfx.fp_addr, sw_if_index, ~0, + 1, NULL, FIB_ROUTE_PATH_FLAG_NONE); + + } + else + { + /* deleting a session: session must exist */ + if (result.fields.session_index == ~0) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + t = pool_elt_at_index (pem->sessions, result.fields.session_index); + sw_if_index = t->sw_if_index; + + vnet_sw_interface_set_flags (vnm, t->sw_if_index, 0 /* down */ ); + vnet_sw_interface_t *si = vnet_get_sw_interface (vnm, t->sw_if_index); + si->flags |= VNET_SW_INTERFACE_FLAG_HIDDEN; + + vec_add1 (pem->free_pppoe_session_hw_if_indices, t->hw_if_index); + + pem->session_index_by_sw_if_index[t->sw_if_index] = ~0; + + /* update pppoe fib with session_inde=~0x */ + result.fields.session_index = ~0; + pppoe_update_1 (&pem->session_table, + a->client_mac, clib_host_to_net_u16 (a->session_id), + &key, &bucket, &result); + + + /* delete reverse route for client ip */ + fib_table_entry_path_remove (a->decap_fib_index, &pfx, + FIB_SOURCE_PLUGIN_HI, + pfx.fp_proto, + &pfx.fp_addr, + sw_if_index, ~0, 1, + FIB_ROUTE_PATH_FLAG_NONE); + + vec_free (t->rewrite); + pool_put (pem->sessions, t); + } + + if (sw_if_indexp) + *sw_if_indexp = sw_if_index; + + return 0; +} + +static clib_error_t * +pppoe_add_del_session_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + u16 session_id = 0; + ip46_address_t client_ip; + u8 is_add = 1; + u8 client_ip_set = 0; + u8 ipv4_set = 0; + u8 ipv6_set = 0; + u32 encap_if_index = 0; + u32 decap_fib_index = 0; + u8 client_mac[6] = { 0 }; + u8 client_mac_set = 0; + int rv; + u32 tmp; + vnet_pppoe_add_del_session_args_t _a, *a = &_a; + u32 session_sw_if_index; + clib_error_t *error = NULL; + + /* Cant "universally zero init" (={0}) due to GCC bug 53119 */ + memset (&client_ip, 0, sizeof client_ip); + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "del")) + { + is_add = 0; + } + else if (unformat (line_input, "session-id %d", &session_id)) + ; + else if (unformat (line_input, "client-ip %U", + unformat_ip4_address, &client_ip.ip4)) + { + client_ip_set = 1; + ipv4_set = 1; + } + else if (unformat (line_input, "client-ip %U", + unformat_ip6_address, &client_ip.ip6)) + { + client_ip_set = 1; + ipv6_set = 1; + } + else if (unformat (line_input, "decap-vrf-id %d", &tmp)) + { + if (ipv6_set) + decap_fib_index = fib_table_find (FIB_PROTOCOL_IP6, tmp); + else + decap_fib_index = fib_table_find (FIB_PROTOCOL_IP4, tmp); + + if (decap_fib_index == ~0) + { + error = + clib_error_return (0, "nonexistent decap fib id %d", tmp); + goto done; + } + } + else + if (unformat + (line_input, "client-mac %U", unformat_ethernet_address, + client_mac)) + client_mac_set = 1; + else + { + error = clib_error_return (0, "parse error: '%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (client_ip_set == 0) + { + error = + clib_error_return (0, "session client ip address not specified"); + goto done; + } + + if (ipv4_set && ipv6_set) + { + error = clib_error_return (0, "both IPv4 and IPv6 addresses specified"); + goto done; + } + + if (client_mac_set == 0) + { + error = clib_error_return (0, "session client mac not specified"); + goto done; + } + + memset (a, 0, sizeof (*a)); + + a->is_add = is_add; + a->is_ip6 = ipv6_set; + +#define _(x) a->x = x; + foreach_copy_field; +#undef _ + + clib_memcpy (a->client_mac, client_mac, 6); + + rv = vnet_pppoe_add_del_session (a, &session_sw_if_index); + + switch (rv) + { + case 0: + if (is_add) + vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name, + vnet_get_main (), session_sw_if_index); + break; + + case VNET_API_ERROR_TUNNEL_EXIST: + error = clib_error_return (0, "session already exists..."); + goto done; + + case VNET_API_ERROR_NO_SUCH_ENTRY: + error = clib_error_return (0, "session does not exist..."); + goto done; + + default: + error = clib_error_return + (0, "vnet_pppoe_add_del_session returned %d", rv); + goto done; + } + +done: + unformat_free (line_input); + + return error; +} + +/*? + * Add or delete a PPPPOE Session. + * + * @cliexpar + * Example of how to create a PPPPOE Session: + * @cliexcmd{create pppoe session client-ip 10.0.3.1 session-id 13 + * client-mac 00:01:02:03:04:05 } + * Example of how to delete a PPPPOE Session: + * @cliexcmd{create pppoe session client-ip 10.0.3.1 session-id 13 + * client-mac 00:01:02:03:04:05 del } + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (create_pppoe_session_command, static) = { + .path = "create pppoe session", + .short_help = + "create pppoe session client-ip session-id " + " client-mac [decap-vrf-id ] [del]", + .function = pppoe_add_del_session_command_fn, +}; +/* *INDENT-ON* */ + +/* *INDENT-OFF* */ +static clib_error_t * +show_pppoe_session_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + pppoe_main_t *pem = &pppoe_main; + pppoe_session_t *t; + + if (pool_elts (pem->sessions) == 0) + vlib_cli_output (vm, "No pppoe sessions configured..."); + + pool_foreach (t, pem->sessions, + ({ + vlib_cli_output (vm, "%U",format_pppoe_session, t); + })); + + return 0; +} +/* *INDENT-ON* */ + +/*? + * Display all the PPPPOE Session entries. + * + * @cliexpar + * Example of how to display the PPPPOE Session entries: + * @cliexstart{show pppoe session} + * [0] client-ip 10.0.3.1 session_id 13 encap-if-index 0 decap-vrf-id 13 sw_if_index 5 + * local-mac a0:b0:c0:d0:e0:f0 client-mac 00:01:02:03:04:05 + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (show_pppoe_session_command, static) = { + .path = "show pppoe session", + .short_help = "show pppoe session", + .function = show_pppoe_session_command_fn, +}; +/* *INDENT-ON* */ + +/** Display the contents of the PPPoE Fib. */ +static clib_error_t * +show_pppoe_fib_command_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + pppoe_main_t *pem = &pppoe_main; + BVT (clib_bihash) * h = &pem->session_table; + BVT (clib_bihash_bucket) * b; + BVT (clib_bihash_value) * v; + pppoe_entry_key_t key; + pppoe_entry_result_t result; + u32 first_entry = 1; + u64 total_entries = 0; + int i, j, k; + u8 *s = 0; + + for (i = 0; i < h->nbuckets; i++) + { + b = &h->buckets[i]; + if (b->offset == 0) + continue; + v = BV (clib_bihash_get_value) (h, b->offset); + for (j = 0; j < (1 << b->log2_pages); j++) + { + for (k = 0; k < BIHASH_KVP_PER_PAGE; k++) + { + if (v->kvp[k].key == ~0ULL && v->kvp[k].value == ~0ULL) + continue; + + if (first_entry) + { + first_entry = 0; + vlib_cli_output (vm, + "%=19s%=12s%=13s%=14s", + "Mac-Address", "session_id", "sw_if_index", + "session_index"); + } + + key.raw = v->kvp[k].key; + result.raw = v->kvp[k].value; + + + vlib_cli_output (vm, + "%=19U%=12d%=13d%=14d", + format_ethernet_address, key.fields.mac, + clib_net_to_host_u16 (key.fields.session_id), + result.fields.sw_if_index == ~0 + ? -1 : result.fields.sw_if_index, + result.fields.session_index == ~0 + ? -1 : result.fields.session_index); + vec_reset_length (s); + total_entries++; + } + v++; + } + } + + if (total_entries == 0) + vlib_cli_output (vm, "no pppoe fib entries"); + else + vlib_cli_output (vm, "%lld pppoe fib entries", total_entries); + + vec_free (s); + return 0; +} + +/*? + * This command dispays the MAC Address entries of the PPPoE FIB table. + * Output can be filtered to just get the number of MAC Addresses or display + * each MAC Address. + * + * @cliexpar + * Example of how to display the number of MAC Address entries in the PPPoE + * FIB table: + * @cliexstart{show pppoe fib} + * Mac Address session_id Interface sw_if_index session_index + * 52:54:00:53:18:33 1 GigabitEthernet0/8/0 2 0 + * 52:54:00:53:18:55 2 GigabitEthernet0/8/1 3 1 + * @cliexend +?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (show_pppoe_fib_command, static) = { + .path = "show pppoe fib", + .short_help = "show pppoe fib", + .function = show_pppoe_fib_command_fn, +}; +/* *INDENT-ON* */ + +clib_error_t * +pppoe_init (vlib_main_t * vm) +{ + pppoe_main_t *pem = &pppoe_main; + + pem->vnet_main = vnet_get_main (); + pem->vlib_main = vm; + + /* Create the hash table */ + BV (clib_bihash_init) (&pem->session_table, "pppoe session table", + PPPOE_NUM_BUCKETS, PPPOE_MEMORY_SIZE); + + ethernet_register_input_type (vm, ETHERNET_TYPE_PPPOE_SESSION, + pppoe_input_node.index); + + ethernet_register_input_type (vm, ETHERNET_TYPE_PPPOE_DISCOVERY, + pppoe_tap_dispatch_node.index); + + return 0; +} + +VLIB_INIT_FUNCTION (pppoe_init); + +/* *INDENT-OFF* */ +VLIB_PLUGIN_REGISTER () = { + .version = VPP_BUILD_VER, + .description = "PPPoE", +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/pppoe/pppoe.h b/src/plugins/pppoe/pppoe.h new file mode 100644 index 00000000..37d628eb --- /dev/null +++ b/src/plugins/pppoe/pppoe.h @@ -0,0 +1,295 @@ +/* + *------------------------------------------------------------------ + * Copyright (c) 2017 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 _PPPOE_H +#define _PPPOE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +typedef struct +{ + u8 ver_type; + u8 code; + u16 session_id; + u16 length; + u16 ppp_proto; +} pppoe_header_t; + +#define PPPOE_VER_TYPE 0x11 +#define PPPOE_PADS 0x65 + +typedef struct +{ + /* Rewrite string */ + u8 *rewrite; + + /* pppoe session_id in HOST byte order */ + u16 session_id; + + /* session client addresses */ + ip46_address_t client_ip; + + /* the index of tx interface for pppoe encaped packet */ + u32 encap_if_index; + + /** FIB indices - inner IP packet lookup here */ + u32 decap_fib_index; + + u8 local_mac[6]; + u8 client_mac[6]; + + /* vnet intfc index */ + u32 sw_if_index; + u32 hw_if_index; + +} pppoe_session_t; + +#define foreach_pppoe_input_next \ +_(DROP, "error-drop") \ +_(IP4_INPUT, "ip4-input") \ +_(IP6_INPUT, "ip6-input" ) \ +_(CP_INPUT, "pppoe-tap-dispatch" ) \ + +typedef enum +{ +#define _(s,n) PPPOE_INPUT_NEXT_##s, + foreach_pppoe_input_next +#undef _ + PPPOE_INPUT_N_NEXT, +} pppoe_input_next_t; + +typedef enum +{ +#define pppoe_error(n,s) PPPOE_ERROR_##n, +#include +#undef pppoe_error + PPPOE_N_ERROR, +} pppoe_input_error_t; + + +#define MTU 1500 +#define MTU_BUFFERS ((MTU + VLIB_BUFFER_DATA_SIZE - 1) / VLIB_BUFFER_DATA_SIZE) +#define NUM_BUFFERS_TO_ALLOC 32 + +/* + * The size of pppoe session table + */ +#define PPPOE_NUM_BUCKETS (128 * 1024) +#define PPPOE_MEMORY_SIZE (16<<20) + +/* *INDENT-OFF* */ +/* + * The PPPoE key is the mac address and session ID + */ +typedef struct +{ + union + { + struct + { + u16 session_id; + u8 mac[6]; + } fields; + struct + { + u32 w0; + u32 w1; + } words; + u64 raw; + }; +} pppoe_entry_key_t; +/* *INDENT-ON* */ + +/* *INDENT-OFF* */ +/* + * The PPPoE entry results + */ +typedef struct +{ + union + { + struct + { + u32 sw_if_index; + + u32 session_index; + + } fields; + u64 raw; + }; +} pppoe_entry_result_t; +/* *INDENT-ON* */ + +typedef struct +{ + /* For DP: vector of encap session instances, */ + pppoe_session_t *sessions; + + /* For CP: vector of CP path */ + BVT (clib_bihash) session_table; + + /* Free vlib hw_if_indices */ + u32 *free_pppoe_session_hw_if_indices; + + /* Mapping from sw_if_index to session index */ + u32 *session_index_by_sw_if_index; + + /* used for pppoe cp path */ + u32 tap_if_index; + + /* API message ID base */ + u16 msg_id_base; + + /* convenience */ + vlib_main_t *vlib_main; + vnet_main_t *vnet_main; + +} pppoe_main_t; + +extern pppoe_main_t pppoe_main; + +extern vlib_node_registration_t pppoe_input_node; +extern vlib_node_registration_t pppoe_encap_node; +extern vlib_node_registration_t pppoe_tap_dispatch_node; + +u8 *format_pppoe_encap_trace (u8 * s, va_list * args); + +typedef struct +{ + u8 is_add; + u8 is_ip6; + u16 session_id; + ip46_address_t client_ip; + u32 encap_if_index; + u32 decap_fib_index; + u8 local_mac[6]; + u8 client_mac[6]; +} vnet_pppoe_add_del_session_args_t; + +int vnet_pppoe_add_del_session + (vnet_pppoe_add_del_session_args_t * a, u32 * sw_if_indexp); + +typedef struct +{ + u8 is_add; + u32 client_if_index; + u32 tap_if_index; +} vnet_pppoe_add_del_tap_args_t; + +always_inline u64 +pppoe_make_key (u8 * mac_address, u16 session_id) +{ + u64 temp; + + /* + * The mac address in memory is A:B:C:D:E:F + * The session_id in register is H:L + */ +#if CLIB_ARCH_IS_LITTLE_ENDIAN + /* + * Create the in-register key as F:E:D:C:B:A:H:L + * In memory the key is L:H:A:B:C:D:E:F + */ + temp = *((u64 *) (mac_address)) << 16; + temp = (temp & ~0xffff) | (u64) (session_id); +#else + /* + * Create the in-register key as H:L:A:B:C:D:E:F + * In memory the key is H:L:A:B:C:D:E:F + */ + temp = *((u64 *) (mac_address)) >> 16; + temp = temp | (((u64) session_id) << 48); +#endif + + return temp; +} + +static_always_inline void +pppoe_lookup_1 (BVT (clib_bihash) * session_table, + pppoe_entry_key_t * cached_key, + pppoe_entry_result_t * cached_result, + u8 * mac0, + u16 session_id0, + pppoe_entry_key_t * key0, + u32 * bucket0, pppoe_entry_result_t * result0) +{ + /* set up key */ + key0->raw = pppoe_make_key (mac0, session_id0); + *bucket0 = ~0; + + if (key0->raw == cached_key->raw) + { + /* Hit in the one-entry cache */ + result0->raw = cached_result->raw; + } + else + { + /* Do a regular session table lookup */ + BVT (clib_bihash_kv) kv; + + kv.key = key0->raw; + kv.value = ~0ULL; + BV (clib_bihash_search_inline) (session_table, &kv); + result0->raw = kv.value; + + /* Update one-entry cache */ + cached_key->raw = key0->raw; + cached_result->raw = result0->raw; + } +} + +static_always_inline void +pppoe_update_1 (BVT (clib_bihash) * session_table, + u8 * mac0, + u16 session_id0, + pppoe_entry_key_t * key0, + u32 * bucket0, pppoe_entry_result_t * result0) +{ + /* set up key */ + key0->raw = pppoe_make_key (mac0, session_id0); + *bucket0 = ~0; + + /* Update the entry */ + BVT (clib_bihash_kv) kv; + kv.key = key0->raw; + kv.value = result0->raw; + BV (clib_bihash_add_del) (session_table, &kv, 1 /* is_add */ ); + +} +#endif /* _PPPOE_H */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/pppoe/pppoe_all_api_h.h b/src/plugins/pppoe/pppoe_all_api_h.h new file mode 100644 index 00000000..393c7680 --- /dev/null +++ b/src/plugins/pppoe/pppoe_all_api_h.h @@ -0,0 +1,18 @@ +/* + * pppoe_all_api_h.h - plug-in api #include file + * + * Copyright (c) 2017 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 diff --git a/src/plugins/pppoe/pppoe_api.c b/src/plugins/pppoe/pppoe_api.c new file mode 100644 index 00000000..9b758460 --- /dev/null +++ b/src/plugins/pppoe/pppoe_api.c @@ -0,0 +1,224 @@ +/* + *------------------------------------------------------------------ + * pppoe_api.c - pppoe api + * + * Copyright (c) 2017 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 +#include +#include +#include + +#include +#include +#include + +#include + + +#define vl_msg_id(n,h) n, +typedef enum +{ +#include + /* 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 +#undef vl_typedefs + +/* define generated endian-swappers */ +#define vl_endianfun +#include +#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 +#undef vl_printfun + +/* Get the API version number */ +#define vl_api_version(n,v) static u32 api_version=(v); +#include +#undef vl_api_version + +#define vl_msg_name_crc_list +#include +#undef vl_msg_name_crc_list + +#define REPLY_MSG_ID_BASE pem->msg_id_base +#include + +static void +setup_message_id_table (pppoe_main_t * pem, api_main_t * am) +{ +#define _(id,n,crc) \ + vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + pem->msg_id_base); + foreach_vl_msg_name_crc_pppoe; +#undef _ +} + +#define foreach_pppoe_plugin_api_msg \ +_(PPPOE_ADD_DEL_SESSION, pppoe_add_del_session) \ +_(PPPOE_SESSION_DUMP, pppoe_session_dump) + +static void vl_api_pppoe_add_del_session_t_handler + (vl_api_pppoe_add_del_session_t * mp) +{ + vl_api_pppoe_add_del_session_reply_t *rmp; + int rv = 0; + u32 decap_fib_index; + ip4_main_t *im = &ip4_main; + pppoe_main_t *pem = &pppoe_main; + + uword *p = hash_get (im->fib_index_by_table_id, ntohl (mp->decap_vrf_id)); + if (!p) + { + rv = VNET_API_ERROR_NO_SUCH_INNER_FIB; + goto out; + } + decap_fib_index = p[0]; + + vnet_pppoe_add_del_session_args_t a = { + .is_add = mp->is_add, + .is_ip6 = mp->is_ipv6, + .decap_fib_index = decap_fib_index, + .session_id = ntohs (mp->session_id), + .client_ip = to_ip46 (mp->is_ipv6, mp->client_ip), + }; + clib_memcpy (a.client_mac, mp->client_mac, 6); + + u32 sw_if_index = ~0; + rv = vnet_pppoe_add_del_session (&a, &sw_if_index); + +out: + /* *INDENT-OFF* */ + REPLY_MACRO2(VL_API_PPPOE_ADD_DEL_SESSION_REPLY, + ({ + rmp->sw_if_index = ntohl (sw_if_index); + })); + /* *INDENT-ON* */ +} + +static void send_pppoe_session_details + (pppoe_session_t * t, unix_shared_memory_queue_t * q, u32 context) +{ + vl_api_pppoe_session_details_t *rmp; + ip4_main_t *im4 = &ip4_main; + ip6_main_t *im6 = &ip6_main; + u8 is_ipv6 = !ip46_address_is_ip4 (&t->client_ip); + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_PPPOE_SESSION_DETAILS); + if (is_ipv6) + { + memcpy (rmp->client_ip, t->client_ip.ip6.as_u8, 16); + rmp->decap_vrf_id = htonl (im6->fibs[t->decap_fib_index].ft_table_id); + } + else + { + memcpy (rmp->client_ip, t->client_ip.ip4.as_u8, 4); + rmp->decap_vrf_id = htonl (im4->fibs[t->decap_fib_index].ft_table_id); + } + rmp->session_id = htons (t->session_id); + rmp->encap_if_index = htonl (t->encap_if_index); + clib_memcpy (rmp->local_mac, t->local_mac, 6); + clib_memcpy (rmp->client_mac, t->client_mac, 6); + 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_pppoe_session_dump_t_handler (vl_api_pppoe_session_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + pppoe_main_t *pem = &pppoe_main; + pppoe_session_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, pem->sessions, + ({ + send_pppoe_session_details(t, q, mp->context); + })); + /* *INDENT-ON* */ + } + else + { + if ((sw_if_index >= vec_len (pem->session_index_by_sw_if_index)) || + (~0 == pem->session_index_by_sw_if_index[sw_if_index])) + { + return; + } + t = &pem->sessions[pem->session_index_by_sw_if_index[sw_if_index]]; + send_pppoe_session_details (t, q, mp->context); + } +} + + +static clib_error_t * +pppoe_api_hookup (vlib_main_t * vm) +{ + pppoe_main_t *pem = &pppoe_main; + + u8 *name = format (0, "pppoe_%08x%c", api_version, 0); + pem->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 + pem->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_pppoe_plugin_api_msg; +#undef _ + + /* Add our API messages to the global name_crc hash table */ + setup_message_id_table (pem, &api_main); + + return 0; +} + +VLIB_API_INIT_FUNCTION (pppoe_api_hookup); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/pppoe/pppoe_decap.c b/src/plugins/pppoe/pppoe_decap.c new file mode 100644 index 00000000..02c82711 --- /dev/null +++ b/src/plugins/pppoe/pppoe_decap.c @@ -0,0 +1,422 @@ +/* + * decap.c: pppoe session decap packet processing + * + * Copyright (c) 2017 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 +#include +#include +#include + +typedef struct { + u32 next_index; + u32 session_index; + u32 session_id; + u32 error; +} pppoe_rx_trace_t; + +static u8 * format_pppoe_rx_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 *); + pppoe_rx_trace_t * t = va_arg (*args, pppoe_rx_trace_t *); + + if (t->session_index != ~0) + { + s = format (s, "PPPoE decap from pppoe_session%d session_id %d next %d error %d", + t->session_index, t->session_id, t->next_index, t->error); + } + else + { + s = format (s, "PPPoE decap error - session for session_id %d does not exist", + t->session_id); + } + return s; +} + +static uword +pppoe_input (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + u32 n_left_from, next_index, * from, * to_next; + pppoe_main_t * pem = &pppoe_main; + vnet_main_t * vnm = pem->vnet_main; + vnet_interface_main_t * im = &vnm->interface_main; + u32 pkts_decapsulated = 0; + u32 thread_index = vlib_get_thread_index(); + u32 stats_sw_if_index, stats_n_packets, stats_n_bytes; + pppoe_entry_key_t cached_key; + pppoe_entry_result_t cached_result; + + from = vlib_frame_vector_args (from_frame); + n_left_from = from_frame->n_vectors; + + /* Clear the one-entry cache in case session table was updated */ + cached_key.raw = ~0; + cached_result.raw = ~0; /* warning be gone */ + + next_index = node->cached_next_index; + stats_sw_if_index = node->runtime_data[0]; + stats_n_packets = stats_n_bytes = 0; + + 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 >= 4 && n_left_to_next >= 2) + { + u32 bi0, bi1; + vlib_buffer_t * b0, * b1; + u32 next0, next1; + ethernet_header_t *h0, *h1; + pppoe_header_t * pppoe0, * pppoe1; + u16 ppp_proto0 = 0, ppp_proto1 = 0; + pppoe_session_t * t0, * t1; + u32 error0, error1; + u32 sw_if_index0, sw_if_index1, len0, len1; + pppoe_entry_key_t key0, key1; + pppoe_entry_result_t result0, result1; + u32 bucket0, bucket1; + + /* Prefetch next iteration. */ + { + vlib_buffer_t * p2, * p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header (p2, LOAD); + vlib_prefetch_buffer_header (p3, LOAD); + + CLIB_PREFETCH (p2->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); + CLIB_PREFETCH (p3->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); + } + + bi0 = from[0]; + bi1 = from[1]; + to_next[0] = bi0; + to_next[1] = bi1; + from += 2; + to_next += 2; + n_left_to_next -= 2; + n_left_from -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + error0 = 0; + error1 = 0; + + /* leaves current_data pointing at the pppoe header */ + pppoe0 = vlib_buffer_get_current (b0); + pppoe1 = vlib_buffer_get_current (b1); + ppp_proto0 = clib_net_to_host_u16(pppoe0->ppp_proto); + ppp_proto1 = clib_net_to_host_u16(pppoe1->ppp_proto); + + /* Manipulate packet 0 */ + if ((ppp_proto0 != PPP_PROTOCOL_ip4) + && (ppp_proto0 != PPP_PROTOCOL_ip6)) + { + error0 = PPPOE_ERROR_CONTROL_PLANE; + next0 = PPPOE_INPUT_NEXT_CP_INPUT; + goto trace0; + } + + /* get client mac */ + vlib_buffer_reset(b0); + h0 = vlib_buffer_get_current (b0); + + pppoe_lookup_1 (&pem->session_table, &cached_key, &cached_result, + h0->src_address, pppoe0->session_id, + &key0, &bucket0, &result0); + if (PREDICT_FALSE (result0.fields.session_index == ~0)) + { + error0 = PPPOE_ERROR_NO_SUCH_SESSION; + next0 = PPPOE_INPUT_NEXT_DROP; + goto trace0; + } + + t0 = pool_elt_at_index (pem->sessions, + result0.fields.session_index); + + /* Pop Eth and PPPPoE header */ + vlib_buffer_advance(b0, sizeof(*h0)+sizeof(*pppoe0)); + + next0 = (ppp_proto0==PPP_PROTOCOL_ip4)? + PPPOE_INPUT_NEXT_IP4_INPUT + : PPPOE_INPUT_NEXT_IP6_INPUT; + + sw_if_index0 = t0->sw_if_index; + len0 = vlib_buffer_length_in_chain (vm, b0); + + pkts_decapsulated ++; + stats_n_packets += 1; + stats_n_bytes += len0; + + /* Batch stats increment on the same pppoe session so counter + is not incremented per packet */ + if (PREDICT_FALSE (sw_if_index0 != stats_sw_if_index)) + { + stats_n_packets -= 1; + stats_n_bytes -= len0; + if (stats_n_packets) + vlib_increment_combined_counter + (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX, + thread_index, stats_sw_if_index, + stats_n_packets, stats_n_bytes); + stats_n_packets = 1; + stats_n_bytes = len0; + stats_sw_if_index = sw_if_index0; + } + + trace0: + b0->error = error0 ? node->errors[error0] : 0; + + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + pppoe_rx_trace_t *tr + = vlib_add_trace (vm, node, b0, sizeof (*tr)); + tr->next_index = next0; + tr->error = error0; + tr->session_index = result0.fields.session_index; + tr->session_id = clib_net_to_host_u32(pppoe0->session_id); + } + + + /* Manipulate packet 1 */ + if ((ppp_proto1 != PPP_PROTOCOL_ip4) + && (ppp_proto1 != PPP_PROTOCOL_ip6)) + { + error1 = PPPOE_ERROR_CONTROL_PLANE; + next1 = PPPOE_INPUT_NEXT_CP_INPUT; + goto trace1; + } + + /* get client mac */ + vlib_buffer_reset(b1); + h1 = vlib_buffer_get_current (b1); + + pppoe_lookup_1 (&pem->session_table, &cached_key, &cached_result, + h1->src_address, pppoe1->session_id, + &key1, &bucket1, &result1); + if (PREDICT_FALSE (result1.fields.session_index == ~0)) + { + error1 = PPPOE_ERROR_NO_SUCH_SESSION; + next1 = PPPOE_INPUT_NEXT_DROP; + goto trace1; + } + + t1 = pool_elt_at_index (pem->sessions, + result1.fields.session_index); + + /* Pop Eth and PPPPoE header */ + vlib_buffer_advance(b1, sizeof(*h1)+sizeof(*pppoe1)); + + next1 = (ppp_proto1==PPP_PROTOCOL_ip4)? + PPPOE_INPUT_NEXT_IP4_INPUT + : PPPOE_INPUT_NEXT_IP6_INPUT; + + sw_if_index1 = t1->sw_if_index; + len1 = vlib_buffer_length_in_chain (vm, b1); + + pkts_decapsulated ++; + stats_n_packets += 1; + stats_n_bytes += len1; + + /* Batch stats increment on the same pppoe session so counter + is not incremented per packet */ + if (PREDICT_FALSE (sw_if_index1 != stats_sw_if_index)) + { + stats_n_packets -= 1; + stats_n_bytes -= len1; + if (stats_n_packets) + vlib_increment_combined_counter + (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX, + thread_index, stats_sw_if_index, + stats_n_packets, stats_n_bytes); + stats_n_packets = 1; + stats_n_bytes = len1; + stats_sw_if_index = sw_if_index1; + } + + trace1: + b1->error = error1 ? node->errors[error1] : 0; + + if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED)) + { + pppoe_rx_trace_t *tr + = vlib_add_trace (vm, node, b1, sizeof (*tr)); + tr->next_index = next1; + tr->error = error1; + tr->session_index = result1.fields.session_index; + tr->session_id = clib_net_to_host_u32(pppoe1->session_id); + } + + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, next0, next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t * b0; + u32 next0; + ethernet_header_t *h0; + pppoe_header_t * pppoe0; + u16 ppp_proto0 = 0; + pppoe_session_t * t0; + u32 error0; + u32 sw_if_index0, len0; + pppoe_entry_key_t key0; + pppoe_entry_result_t result0; + u32 bucket0; + + 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); + error0 = 0; + + /* leaves current_data pointing at the pppoe header */ + pppoe0 = vlib_buffer_get_current (b0); + ppp_proto0 = clib_net_to_host_u16(pppoe0->ppp_proto); + + if ((ppp_proto0 != PPP_PROTOCOL_ip4) + && (ppp_proto0 != PPP_PROTOCOL_ip6)) + { + error0 = PPPOE_ERROR_CONTROL_PLANE; + next0 = PPPOE_INPUT_NEXT_CP_INPUT; + goto trace00; + } + + /* get client mac */ + vlib_buffer_reset(b0); + h0 = vlib_buffer_get_current (b0); + + pppoe_lookup_1 (&pem->session_table, &cached_key, &cached_result, + h0->src_address, pppoe0->session_id, + &key0, &bucket0, &result0); + if (PREDICT_FALSE (result0.fields.session_index == ~0)) + { + error0 = PPPOE_ERROR_NO_SUCH_SESSION; + next0 = PPPOE_INPUT_NEXT_DROP; + goto trace00; + } + + t0 = pool_elt_at_index (pem->sessions, + result0.fields.session_index); + + /* Pop Eth and PPPPoE header */ + vlib_buffer_advance(b0, sizeof(*h0)+sizeof(*pppoe0)); + + next0 = (ppp_proto0==PPP_PROTOCOL_ip4)? + PPPOE_INPUT_NEXT_IP4_INPUT + : PPPOE_INPUT_NEXT_IP6_INPUT; + + sw_if_index0 = t0->sw_if_index; + len0 = vlib_buffer_length_in_chain (vm, b0); + + pkts_decapsulated ++; + stats_n_packets += 1; + stats_n_bytes += len0; + + /* Batch stats increment on the same pppoe session so counter + is not incremented per packet */ + if (PREDICT_FALSE (sw_if_index0 != stats_sw_if_index)) + { + stats_n_packets -= 1; + stats_n_bytes -= len0; + if (stats_n_packets) + vlib_increment_combined_counter + (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX, + thread_index, stats_sw_if_index, + stats_n_packets, stats_n_bytes); + stats_n_packets = 1; + stats_n_bytes = len0; + stats_sw_if_index = sw_if_index0; + } + + trace00: + b0->error = error0 ? node->errors[error0] : 0; + + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + pppoe_rx_trace_t *tr + = vlib_add_trace (vm, node, b0, sizeof (*tr)); + tr->next_index = next0; + tr->error = error0; + tr->session_index = result0.fields.session_index; + tr->session_id = clib_net_to_host_u16(pppoe0->session_id); + } + 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); + } + /* Do we still need this now that session tx stats is kept? */ + vlib_node_increment_counter (vm, pppoe_input_node.index, + PPPOE_ERROR_DECAPSULATED, + pkts_decapsulated); + + /* Increment any remaining batch stats */ + if (stats_n_packets) + { + vlib_increment_combined_counter + (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX, + thread_index, stats_sw_if_index, stats_n_packets, stats_n_bytes); + node->runtime_data[0] = stats_sw_if_index; + } + + return from_frame->n_vectors; +} + +static char * pppoe_error_strings[] = { +#define pppoe_error(n,s) s, +#include +#undef pppoe_error +#undef _ +}; + +VLIB_REGISTER_NODE (pppoe_input_node) = { + .function = pppoe_input, + .name = "pppoe-input", + /* Takes a vector of packets. */ + .vector_size = sizeof (u32), + + .n_errors = PPPOE_N_ERROR, + .error_strings = pppoe_error_strings, + + .n_next_nodes = PPPOE_INPUT_N_NEXT, + .next_nodes = { +#define _(s,n) [PPPOE_INPUT_NEXT_##s] = n, + foreach_pppoe_input_next +#undef _ + }, + + .format_trace = format_pppoe_rx_trace, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (pppoe_input_node, pppoe_input) + + diff --git a/src/plugins/pppoe/pppoe_encap.c b/src/plugins/pppoe/pppoe_encap.c new file mode 100644 index 00000000..69bec61d --- /dev/null +++ b/src/plugins/pppoe/pppoe_encap.c @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2017 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 +#include +#include +#include +#include +#include + +/* Statistics (not all errors) */ +#define foreach_pppoe_encap_error \ +_(ENCAPSULATED, "good packets encapsulated") + +static char * pppoe_encap_error_strings[] = { +#define _(sym,string) string, + foreach_pppoe_encap_error +#undef _ +}; + +typedef enum { +#define _(sym,str) PPPOE_ENCAP_ERROR_##sym, + foreach_pppoe_encap_error +#undef _ + PPPOE_ENCAP_N_ERROR, +} pppoe_encap_error_t; + +#define foreach_pppoe_encap_next \ +_(DROP, "error-drop") \ +_(INTERFACE, "interface-output" ) \ + +typedef enum +{ +#define _(s,n) PPPOE_ENCAP_NEXT_##s, + foreach_pppoe_encap_next +#undef _ + PPPOE_ENCAP_N_NEXT, +} pppoe_encap_next_t; + +typedef struct { + u32 session_index; + u32 session_id; +} pppoe_encap_trace_t; + +u8 * format_pppoe_encap_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 *); + pppoe_encap_trace_t * t + = va_arg (*args, pppoe_encap_trace_t *); + + s = format (s, "PPPOE encap to pppoe_session%d session_id %d", + t->session_index, t->session_id); + return s; +} + + +#define foreach_fixed_header2_offset \ + _(0) _(1) + + +static uword +pppoe_encap (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + u32 n_left_from, next_index, * from, * to_next; + pppoe_main_t * pem = &pppoe_main; + vnet_main_t * vnm = pem->vnet_main; + vnet_interface_main_t * im = &vnm->interface_main; + u32 pkts_encapsulated = 0; + u32 thread_index = vlib_get_thread_index(); + u32 stats_sw_if_index, stats_n_packets, stats_n_bytes; + u32 sw_if_index0 = 0, sw_if_index1 = 0; + u32 next0 = 0, next1 = 0; + pppoe_session_t * t0 = NULL, * t1 = NULL; + + from = vlib_frame_vector_args (from_frame); + n_left_from = from_frame->n_vectors; + + next_index = node->cached_next_index; + stats_sw_if_index = node->runtime_data[0]; + stats_n_packets = stats_n_bytes = 0; + + 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 >= 4 && n_left_to_next >= 2) + { + u32 bi0, bi1; + vlib_buffer_t * b0, * b1; + u32 len0, len1; + ethernet_header_t * eth0, * eth1; + pppoe_header_t * pppoe0, * pppoe1; + u64 * copy_src0, * copy_dst0; + u64 * copy_src1, * copy_dst1; + u16 * copy_src_last0, * copy_dst_last0; + u16 * copy_src_last1, * copy_dst_last1; + u16 new_l0, new_l1; + u32 session_id0, session_id1; + + /* Prefetch next iteration. */ + { + vlib_buffer_t * p2, * p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header (p2, LOAD); + vlib_prefetch_buffer_header (p3, LOAD); + + CLIB_PREFETCH (p2->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); + CLIB_PREFETCH (p3->data, 2*CLIB_CACHE_LINE_BYTES, LOAD); + } + + bi0 = from[0]; + bi1 = from[1]; + to_next[0] = bi0; + to_next[1] = bi1; + from += 2; + to_next += 2; + n_left_to_next -= 2; + n_left_from -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + /* Get next node index and if-index from session */ + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_TX]; + session_id0 = pem->session_index_by_sw_if_index[sw_if_index0]; + t0 = pool_elt_at_index(pem->sessions, session_id0); + next0 = PPPOE_ENCAP_NEXT_INTERFACE; + vnet_buffer(b0)->sw_if_index[VLIB_TX] = t0->encap_if_index; + + /* Get next node index and if-index from session */ + sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_TX]; + session_id1 = pem->session_index_by_sw_if_index[sw_if_index1]; + t1 = pool_elt_at_index(pem->sessions, session_id1); + next1 = PPPOE_ENCAP_NEXT_INTERFACE; + vnet_buffer(b1)->sw_if_index[VLIB_TX] = t1->encap_if_index; + + /* Apply the rewrite string. $$$$ vnet_rewrite? */ + vlib_buffer_advance (b0, -(word)_vec_len(t0->rewrite)); + vlib_buffer_advance (b1, -(word)_vec_len(t1->rewrite)); + + eth0 = (ethernet_header_t *)(vlib_buffer_get_current(b0)); + eth1 = (ethernet_header_t *)(vlib_buffer_get_current(b1)); + + /* Copy the fixed header */ + copy_dst0 = (u64 *) eth0; + copy_src0 = (u64 *) t0->rewrite; + copy_dst1 = (u64 *) eth1; + copy_src1 = (u64 *) t1->rewrite; + /* Copy first 8-bytes at a time */ +#define _(offs) copy_dst0[offs] = copy_src0[offs]; + foreach_fixed_header2_offset; +#undef _ + /* Last 6 octets. Hopefully gcc will be our friend */ + copy_dst_last0 = (u16 *)(©_dst0[2]); + copy_src_last0 = (u16 *)(©_src0[2]); + copy_dst_last0[0] = copy_src_last0[0]; + copy_dst_last0[1] = copy_src_last0[1]; + copy_dst_last0[2] = copy_src_last0[2]; + +#define _(offs) copy_dst1[offs] = copy_src1[offs]; + foreach_fixed_header2_offset; +#undef _ + /* Last 6 octets. Hopefully gcc will be our friend */ + copy_dst_last1 = (u16 *)(©_dst1[2]); + copy_src_last1 = (u16 *)(©_src1[2]); + copy_dst_last1[0] = copy_src_last1[0]; + copy_dst_last1[1] = copy_src_last1[1]; + copy_dst_last1[2] = copy_src_last1[2]; + + /* Fix PPPoE length */ + new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain(vm, b0) + - sizeof (*pppoe0) - sizeof(*eth0)); + pppoe0 = (pppoe_header_t *)(eth0 + 1); + pppoe0->length = new_l0; + + new_l1 = clib_host_to_net_u16 (vlib_buffer_length_in_chain(vm, b1) + - sizeof (*pppoe1) - sizeof(*eth1)); + pppoe1 = (pppoe_header_t *)(eth1 + 1); + pppoe1->length = new_l1; + + pkts_encapsulated += 2; + len0 = vlib_buffer_length_in_chain (vm, b0); + len1 = vlib_buffer_length_in_chain (vm, b1); + stats_n_packets += 2; + stats_n_bytes += len0 + len1; + + /* Batch stats increment on the same pppoe session so counter is not + incremented per packet. Note stats are still incremented for deleted + and admin-down session where packets are dropped. It is not worthwhile + to check for this rare case and affect normal path performance. */ + if (PREDICT_FALSE ((sw_if_index0 != stats_sw_if_index) || + (sw_if_index1 != stats_sw_if_index))) + { + stats_n_packets -= 2; + stats_n_bytes -= len0 + len1; + if (sw_if_index0 == sw_if_index1) + { + if (stats_n_packets) + vlib_increment_combined_counter + (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX, + thread_index, stats_sw_if_index, + stats_n_packets, stats_n_bytes); + stats_sw_if_index = sw_if_index0; + stats_n_packets = 2; + stats_n_bytes = len0 + len1; + } + else + { + vlib_increment_combined_counter + (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX, + thread_index, sw_if_index0, 1, len0); + vlib_increment_combined_counter + (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX, + thread_index, sw_if_index1, 1, len1); + } + } + + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + pppoe_encap_trace_t *tr = + vlib_add_trace (vm, node, b0, sizeof (*tr)); + tr->session_index = t0 - pem->sessions; + tr->session_id = t0->session_id; + } + + if (PREDICT_FALSE(b1->flags & VLIB_BUFFER_IS_TRACED)) + { + pppoe_encap_trace_t *tr = + vlib_add_trace (vm, node, b1, sizeof (*tr)); + tr->session_index = t1 - pem->sessions; + tr->session_id = t1->session_id; + } + + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, next0, next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t * b0; + ethernet_header_t * eth0; + pppoe_header_t * pppoe0; + u64 * copy_src0, * copy_dst0; + u16 * copy_src_last0, * copy_dst_last0; + u16 new_l0; + u32 len0; + u32 session_id0; + + 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); + + /* Get next node index and if-index from session */ + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_TX]; + session_id0 = pem->session_index_by_sw_if_index[sw_if_index0]; + t0 = pool_elt_at_index(pem->sessions, session_id0); + next0 = PPPOE_ENCAP_NEXT_INTERFACE; + vnet_buffer(b0)->sw_if_index[VLIB_TX] = t0->encap_if_index; + + /* Apply the rewrite string. $$$$ vnet_rewrite? */ + vlib_buffer_advance (b0, -(word)_vec_len(t0->rewrite)); + + eth0 = (ethernet_header_t *)(vlib_buffer_get_current(b0)); + /* Copy the fixed header */ + copy_dst0 = (u64 *) eth0; + copy_src0 = (u64 *) t0->rewrite; + + /* Copy first 8-bytes at a time */ +#define _(offs) copy_dst0[offs] = copy_src0[offs]; + foreach_fixed_header2_offset; +#undef _ + /* Last 6 octets. Hopefully gcc will be our friend */ + copy_dst_last0 = (u16 *)(©_dst0[2]); + copy_src_last0 = (u16 *)(©_src0[2]); + copy_dst_last0[0] = copy_src_last0[0]; + copy_dst_last0[1] = copy_src_last0[1]; + copy_dst_last0[2] = copy_src_last0[2]; + + /* Fix PPPoE length */ + new_l0 = clib_host_to_net_u16 (vlib_buffer_length_in_chain(vm, b0) + - sizeof (*pppoe0) - sizeof(*eth0)); + pppoe0 = (pppoe_header_t *)(eth0 + 1); + pppoe0->length = new_l0; + + pkts_encapsulated ++; + len0 = vlib_buffer_length_in_chain (vm, b0); + stats_n_packets += 1; + stats_n_bytes += len0; + + /* Batch stats increment on the same pppoe session so counter is not + incremented per packet. Note stats are still incremented for deleted + and admin-down session where packets are dropped. It is not worthwhile + to check for this rare case and affect normal path performance. */ + if (PREDICT_FALSE (sw_if_index0 != stats_sw_if_index)) + { + stats_n_packets -= 1; + stats_n_bytes -= len0; + if (stats_n_packets) + vlib_increment_combined_counter + (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX, + thread_index, stats_sw_if_index, + stats_n_packets, stats_n_bytes); + stats_n_packets = 1; + stats_n_bytes = len0; + stats_sw_if_index = sw_if_index0; + } + + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + pppoe_encap_trace_t *tr = + vlib_add_trace (vm, node, b0, sizeof (*tr)); + tr->session_index = t0 - pem->sessions; + tr->session_id = t0->session_id; + } + 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); + } + + /* Do we still need this now that session tx stats is kept? */ + vlib_node_increment_counter (vm, node->node_index, + PPPOE_ENCAP_ERROR_ENCAPSULATED, + pkts_encapsulated); + + /* Increment any remaining batch stats */ + if (stats_n_packets) + { + vlib_increment_combined_counter + (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_TX, + thread_index, stats_sw_if_index, stats_n_packets, stats_n_bytes); + node->runtime_data[0] = stats_sw_if_index; + } + + return from_frame->n_vectors; +} + +VLIB_REGISTER_NODE (pppoe_encap_node) = { + .function = pppoe_encap, + .name = "pppoe-encap", + .vector_size = sizeof (u32), + .format_trace = format_pppoe_encap_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(pppoe_encap_error_strings), + .error_strings = pppoe_encap_error_strings, + .n_next_nodes = PPPOE_ENCAP_N_NEXT, + .next_nodes = { +#define _(s,n) [PPPOE_ENCAP_NEXT_##s] = n, + foreach_pppoe_encap_next +#undef _ + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (pppoe_encap_node, pppoe_encap) + diff --git a/src/plugins/pppoe/pppoe_error.def b/src/plugins/pppoe/pppoe_error.def new file mode 100644 index 00000000..a875afd0 --- /dev/null +++ b/src/plugins/pppoe/pppoe_error.def @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2017 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. + */ +pppoe_error (DECAPSULATED, "good packets decapsulated") +pppoe_error (CONTROL_PLANE, "control plane packet") +pppoe_error (NO_SUCH_SESSION, "no such sessions") +pppoe_error (BAD_VER_TYPE, "bad version and type in pppoe header") diff --git a/src/plugins/pppoe/pppoe_msg_enum.h b/src/plugins/pppoe/pppoe_msg_enum.h new file mode 100644 index 00000000..7ca19189 --- /dev/null +++ b/src/plugins/pppoe/pppoe_msg_enum.h @@ -0,0 +1,31 @@ +/* + * pppoe_msg_enum.h - vpp engine plug-in message enumeration + * + * Copyright (c) 2017 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_pppoe_msg_enum_h +#define included_pppoe_msg_enum_h + +#include + +#define vl_msg_id(n,h) n, +typedef enum +{ +#include + /* 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_pppoe_msg_enum_h */ diff --git a/src/plugins/pppoe/pppoe_tap.c b/src/plugins/pppoe/pppoe_tap.c new file mode 100644 index 00000000..60cdaafb --- /dev/null +++ b/src/plugins/pppoe/pppoe_tap.c @@ -0,0 +1,89 @@ +/* + *------------------------------------------------------------------ + * Copyright (c) 2017 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 +#include + +static clib_error_t * +pppoe_add_del_tap_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + pppoe_main_t *pem = &pppoe_main; + u8 is_add = 1; + u8 tap_if_index_set = 0; + u32 tap_if_index = 0; + clib_error_t *error = NULL; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "del")) + { + is_add = 0; + } + else if (unformat (line_input, "tap-if-index %d", &tap_if_index)) + tap_if_index_set = 1; + else + { + error = clib_error_return (0, "parse error: '%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (tap_if_index_set == 0) + { + error = clib_error_return (0, "tap if index not specified"); + goto done; + } + + if (is_add) + { + pem->tap_if_index = tap_if_index; + } + else + { + pem->tap_if_index = ~0; + } + +done: + unformat_free (line_input); + + return error; +} + +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (create_pppoe_tap_cmd, static) = +{ + .path = "create pppoe tap", + .short_help = "create pppoe tap if-name [del]", + .function = pppoe_add_del_tap_command_fn, +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/pppoe/pppoe_tap_node.c b/src/plugins/pppoe/pppoe_tap_node.c new file mode 100644 index 00000000..f1e0a501 --- /dev/null +++ b/src/plugins/pppoe/pppoe_tap_node.c @@ -0,0 +1,297 @@ +/* + *------------------------------------------------------------------ + * Copyright (c) 2017 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 pemplied. + * See the License for the specific language governing permissions and + * lpemitations under the License. + *------------------------------------------------------------------ + */ + +#include +#include +#include + +vlib_node_registration_t pppoe_tap_dispatch_node; + +#define foreach_pppoe_tap_next \ +_(DROP, "error-drop") \ +_(TUNTAP, "tuntap-tx" ) \ +_(INTERFACE, "interface-output" ) \ + +typedef enum +{ +#define _(s,n) PPPOE_TAP_NEXT_##s, + foreach_pppoe_tap_next +#undef _ + PPPOE_TAP_N_NEXT, +} pppoe_tap_next_t; + +typedef struct { + u32 next_index; + u32 sw_if_index; + u32 tap_if_index; + u8 pppoe_code; + u16 ppp_proto; + u32 error; +} pppoe_tap_trace_t; + +static u8 * format_pppoe_tap_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 *); + pppoe_tap_trace_t * t = va_arg (*args, pppoe_tap_trace_t *); + pppoe_main_t * pem = &pppoe_main; + + if (t->sw_if_index != pem->tap_if_index) + { + s = format (s, "PPPoE dispatch from sw_if_index %d next %d error %d \n" + " pppoe_code 0x%x ppp_proto 0x%x", + t->sw_if_index, t->next_index, t->error, + t->pppoe_code, t->ppp_proto); + } + else + { + s = format (s, "PPPoE dispatch from tap_if_index %d next %d error %d \n" + " pppoe_code 0x%x ppp_proto 0x%x", + t->tap_if_index, t->next_index, t->error, + t->pppoe_code, t->ppp_proto); + } + return s; +} + +/** + * Perform learning on one packet based on the mac table lookup result. + * */ +static_always_inline void +pppoe_learn_process (vlib_node_runtime_t * node, + pppoe_main_t * pem, + vlib_buffer_t * b0, + u32 sw_if_index0, + pppoe_entry_key_t * key0, + pppoe_entry_key_t * cached_key, + u32 * bucket0, + pppoe_entry_result_t * result0) +{ + /* Check mac table lookup result */ + if (PREDICT_TRUE (result0->fields.sw_if_index == sw_if_index0)) + { + /* + * The entry was in the table, and the sw_if_index matched, the normal case + */ + return; + } + else if (result0->fields.sw_if_index == ~0) + { + /* The entry was not in table, so add it */ + result0->fields.sw_if_index = sw_if_index0; + result0->fields.session_index = ~0; + cached_key->raw = ~0; /* invalidate the cache */ + } + else + { + /* The entry was in the table, but with the wrong sw_if_index mapping (mac move) */ + result0->fields.sw_if_index = sw_if_index0; + } + + /* Update the entry */ + BVT (clib_bihash_kv) kv; + kv.key = key0->raw; + kv.value = result0->raw; + BV (clib_bihash_add_del) (&pem->session_table, &kv, 1 /* is_add */ ); +} + +static uword +pppoe_tap_dispatch (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * from_frame) +{ + u32 n_left_from, next_index, * from, * to_next; + pppoe_main_t * pem = &pppoe_main; + vnet_main_t * vnm = pem->vnet_main; + vnet_interface_main_t * im = &vnm->interface_main; + u32 pkts_decapsulated = 0; + u32 thread_index = vlib_get_thread_index(); + u32 stats_sw_if_index, stats_n_packets, stats_n_bytes; + pppoe_entry_key_t cached_key; + pppoe_entry_result_t cached_result; + + from = vlib_frame_vector_args (from_frame); + n_left_from = from_frame->n_vectors; + + /* Clear the one-entry cache in case session table was updated */ + cached_key.raw = ~0; + cached_result.raw = ~0; /* warning be gone */ + + next_index = node->cached_next_index; + stats_sw_if_index = node->runtime_data[0]; + stats_n_packets = stats_n_bytes = 0; + + 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; + ethernet_header_t *h0; + pppoe_header_t * pppoe0; + pppoe_entry_key_t key0; + pppoe_entry_result_t result0; + + u32 bucket0; + u32 next0; + u32 error0 = 0; + u32 rx_sw_if_index0=~0, tx_sw_if_index0=~0, len0; + vnet_hw_interface_t *hi; + vnet_sw_interface_t *si; + + 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); + /* leaves current_data pointing at the pppoe header */ + pppoe0 = vlib_buffer_get_current (b0); + rx_sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; + + if (PREDICT_FALSE (pppoe0->ver_type != PPPOE_VER_TYPE)) + { + error0 = PPPOE_ERROR_BAD_VER_TYPE; + next0 = PPPOE_INPUT_NEXT_DROP; + goto trace00; + } + + vlib_buffer_reset(b0); + h0 = vlib_buffer_get_current (b0); + + if(rx_sw_if_index0 == pem->tap_if_index) + { + pppoe_lookup_1 (&pem->session_table, &cached_key, &cached_result, + h0->dst_address, 0, + &key0, &bucket0, &result0); + tx_sw_if_index0 = result0.fields.sw_if_index; + + if (PREDICT_FALSE (tx_sw_if_index0 == ~0)) + { + error0 = PPPOE_ERROR_NO_SUCH_SESSION; + next0 = PPPOE_INPUT_NEXT_DROP; + goto trace00; + } + + next0 = PPPOE_TAP_NEXT_INTERFACE; + vnet_buffer(b0)->sw_if_index[VLIB_TX] = tx_sw_if_index0; + + /* set src mac address */ + si = vnet_get_sw_interface(vnm, tx_sw_if_index0); + hi = vnet_get_hw_interface (vnm, si->hw_if_index); + clib_memcpy (vlib_buffer_get_current (b0)+6, hi->hw_address, 6); + } + else + { + pppoe_lookup_1 (&pem->session_table, &cached_key, &cached_result, + h0->src_address, pppoe0->session_id, + &key0, &bucket0, &result0); + tx_sw_if_index0 = result0.fields.sw_if_index; + + /* learn client session */ + pppoe_learn_process (node, pem, b0, rx_sw_if_index0, + &key0, &cached_key, + &bucket0, &result0); + + next0 = PPPOE_TAP_NEXT_TUNTAP; + vnet_buffer(b0)->sw_if_index[VLIB_TX] = pem->tap_if_index; + } + + len0 = vlib_buffer_length_in_chain (vm, b0); + + pkts_decapsulated ++; + stats_n_packets += 1; + stats_n_bytes += len0; + + /* Batch stats increment on the same pppoe session so counter + is not incremented per packet */ + if (PREDICT_FALSE (rx_sw_if_index0 != stats_sw_if_index)) + { + stats_n_packets -= 1; + stats_n_bytes -= len0; + if (stats_n_packets) + vlib_increment_combined_counter + (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX, + thread_index, stats_sw_if_index, + stats_n_packets, stats_n_bytes); + stats_n_packets = 1; + stats_n_bytes = len0; + stats_sw_if_index = rx_sw_if_index0; + } + + trace00: + b0->error = error0 ? node->errors[error0] : 0; + + if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) + { + pppoe_tap_trace_t *tr + = vlib_add_trace (vm, node, b0, sizeof (*tr)); + tr->next_index = next0; + tr->error = error0; + tr->sw_if_index = tx_sw_if_index0; + tr->tap_if_index = pem->tap_if_index; + tr->pppoe_code = pppoe0->code; + tr->ppp_proto = clib_net_to_host_u16(pppoe0->ppp_proto); + } + 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); + } + /* Do we still need this now that session tx stats is kept? */ + vlib_node_increment_counter (vm, pppoe_input_node.index, + PPPOE_ERROR_DECAPSULATED, + pkts_decapsulated); + + /* Increment any remaining batch stats */ + if (stats_n_packets) + { + vlib_increment_combined_counter + (im->combined_sw_if_counters + VNET_INTERFACE_COUNTER_RX, + thread_index, stats_sw_if_index, stats_n_packets, stats_n_bytes); + node->runtime_data[0] = stats_sw_if_index; + } + + return from_frame->n_vectors; +} + +VLIB_REGISTER_NODE (pppoe_tap_dispatch_node) = { + .function = pppoe_tap_dispatch, + .name = "pppoe-tap-dispatch", + /* Takes a vector of packets. */ + .vector_size = sizeof (u32), + + .n_next_nodes = PPPOE_TAP_N_NEXT, + .next_nodes = { +#define _(s,n) [PPPOE_TAP_NEXT_##s] = n, + foreach_pppoe_tap_next +#undef _ + }, + + .format_trace = format_pppoe_tap_trace, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (pppoe_tap_dispatch_node, pppoe_tap_dispatch) + diff --git a/src/plugins/pppoe/pppoe_test.c b/src/plugins/pppoe/pppoe_test.c new file mode 100644 index 00000000..2b67d989 --- /dev/null +++ b/src/plugins/pppoe/pppoe_test.c @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2017 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 +#include +#include +#include +#include +#include + +#define __plugin_msg_base pppoe_test_main.msg_id_base +#include + + +uword unformat_ip46_address (unformat_input_t * input, va_list * args) +{ + ip46_address_t *ip46 = va_arg (*args, ip46_address_t *); + ip46_type_t type = va_arg (*args, ip46_type_t); + if ((type != IP46_TYPE_IP6) && + unformat(input, "%U", unformat_ip4_address, &ip46->ip4)) { + ip46_address_mask_ip4(ip46); + return 1; + } else if ((type != IP46_TYPE_IP4) && + unformat(input, "%U", unformat_ip6_address, &ip46->ip6)) { + return 1; + } + return 0; +} +uword unformat_ip46_prefix (unformat_input_t * input, va_list * args) +{ + ip46_address_t *ip46 = va_arg (*args, ip46_address_t *); + u8 *len = va_arg (*args, u8 *); + ip46_type_t type = va_arg (*args, ip46_type_t); + + u32 l; + if ((type != IP46_TYPE_IP6) && unformat(input, "%U/%u", unformat_ip4_address, &ip46->ip4, &l)) { + if (l > 32) + return 0; + *len = l + 96; + ip46->pad[0] = ip46->pad[1] = ip46->pad[2] = 0; + } else if ((type != IP46_TYPE_IP4) && unformat(input, "%U/%u", unformat_ip6_address, &ip46->ip6, &l)) { + if (l > 128) + return 0; + *len = l; + } else { + return 0; + } + return 1; +} +///////////////////////// + +#define vl_msg_id(n,h) n, +typedef enum { +#include + /* 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 +#undef vl_typedefs + +/* declare message handlers for each api */ + +#define vl_endianfun /* define message structures */ +#include +#undef vl_endianfun + +/* instantiate all the print functions we know about */ +#define vl_print(handle, ...) +#define vl_printfun +#include +#undef vl_printfun + +/* Get the API version number. */ +#define vl_api_version(n,v) static u32 api_version=(v); +#include +#undef vl_api_version + +typedef struct { + /* API message ID base */ + u16 msg_id_base; + vat_main_t *vat_main; +} pppoe_test_main_t; + +pppoe_test_main_t pppoe_test_main; + +static void vl_api_pppoe_add_del_session_reply_t_handler + (vl_api_pppoe_add_del_session_reply_t * mp) +{ + vat_main_t *vam = &vat_main; + i32 retval = ntohl (mp->retval); + if (vam->async_mode) + { + vam->async_errors += (retval < 0); + } + else + { + vam->retval = retval; + vam->sw_if_index = ntohl (mp->sw_if_index); + vam->result_ready = 1; + } +} + + +/* + * Table of message reply handlers, must include boilerplate handlers + * we just generated + */ +#define foreach_vpe_api_reply_msg \ + _(PPPOE_ADD_DEL_SESSION_REPLY, pppoe_add_del_session_reply) \ + _(PPPOE_SESSION_DETAILS, pppoe_session_details) + + +static int +api_pppoe_add_del_session (vat_main_t * vam) +{ + unformat_input_t *line_input = vam->input; + vl_api_pppoe_add_del_session_t *mp; + u16 session_id = 0; + ip46_address_t client_ip; + u8 is_add = 1; + u8 client_ip_set = 0; + u8 ipv4_set = 0; + u8 ipv6_set = 0; + u32 decap_vrf_id = 0; + u8 client_mac[6] = { 0 }; + u8 client_mac_set = 0; + int ret; + + /* Can't "universally zero init" (={0}) due to GCC bug 53119 */ + memset (&client_ip, 0, sizeof client_ip); + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "del")) + { + is_add = 0; + } + else if (unformat (line_input, "session_id %d", &session_id)) + ; + else if (unformat (line_input, "client-ip %U", + unformat_ip4_address, &client_ip.ip4)) + { + client_ip_set = 1; + ipv4_set = 1; + } + else if (unformat (line_input, "client-ip %U", + unformat_ip6_address, &client_ip.ip6)) + { + client_ip_set = 1; + ipv6_set = 1; + } + else if (unformat (line_input, "decap-vrf-id %d", &decap_vrf_id)) + ; + else if (unformat (line_input, "client-mac %U", unformat_ethernet_address, client_mac)) + client_mac_set = 1; + else + { + return -99; + } + } + + if (client_ip_set == 0) + { + errmsg ("session client_ip address not specified"); + return -99; + } + + if (ipv4_set && ipv6_set) + { + errmsg ("both IPv4 and IPv6 addresses specified"); + return -99; + } + + if (client_mac_set == 0) + { + errmsg("session client mac not specified"); + return -99; + } + + M (PPPOE_ADD_DEL_SESSION, mp); + + if (ipv6_set) + { + clib_memcpy (mp->client_ip, &client_ip.ip6, sizeof (client_ip.ip6)); + } + else + { + clib_memcpy (mp->client_ip, &client_ip.ip4, sizeof (client_ip.ip4)); + } + + mp->decap_vrf_id = ntohl (decap_vrf_id); + mp->session_id = ntohl (session_id); + mp->is_add = is_add; + mp->is_ipv6 = ipv6_set; + memcpy (mp->client_mac, client_mac, 6); + + S (mp); + W (ret); + return ret; +} + +static void vl_api_pppoe_session_details_t_handler + (vl_api_pppoe_session_details_t * mp) +{ + vat_main_t *vam = &vat_main; + ip46_address_t client_ip = to_ip46 (mp->is_ipv6, mp->client_ip); + + print (vam->ofp, "%11d%14d%24U%14d%14d%30U%30U", + ntohl (mp->sw_if_index), ntohl (mp->session_id), + format_ip46_address, &client_ip, IP46_TYPE_ANY, + ntohl (mp->encap_if_index), ntohl (mp->decap_vrf_id), + format_ethernet_address, mp->local_mac, + format_ethernet_address, mp->client_mac); +} + +static int +api_pppoe_session_dump (vat_main_t * vam) +{ + unformat_input_t *i = vam->input; + vl_api_pppoe_session_dump_t *mp; + u32 sw_if_index; + u8 sw_if_index_set = 0; + int ret; + + /* Parse args required to build the message */ + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "sw_if_index %d", &sw_if_index)) + sw_if_index_set = 1; + else + break; + } + + if (sw_if_index_set == 0) + { + sw_if_index = ~0; + } + + if (!vam->json_output) + { + print (vam->ofp, "%11s%24s%14s%14s%14s", + "sw_if_index", "client_ip", "session_id", + "encap_if_index", "decap_fib_index", + "local-mac", "client-mac"); + } + + /* Get list of pppoe-session interfaces */ + M (PPPOE_SESSION_DUMP, mp); + + mp->sw_if_index = htonl (sw_if_index); + + S (mp); + + W (ret); + return ret; +} + +/* + * List of messages that the api test plugin sends, + * and that the data plane plugin processes + */ +#define foreach_vpe_api_msg \ +_(pppoe_add_del_session, \ + " client-addr session-id " \ + " [encap-if-index ] [decap-next [ip4|ip6|node ]]" \ + " local-mac client-mac [del]") \ +_(pppoe_session_dump, "[ | sw_if_index ]") \ + +static void +pppoe_vat_api_hookup (vat_main_t *vam) +{ + pppoe_test_main_t * pem = &pppoe_test_main; + /* Hook up handlers for replies from the data plane plug-in */ +#define _(N,n) \ + vl_msg_api_set_handlers((VL_API_##N + pem->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) +{ + pppoe_test_main_t * pem = &pppoe_test_main; + + u8 * name; + + pem->vat_main = vam; + + /* Ask the vpp engine for the first assigned message-id */ + name = format (0, "pppoe_%08x%c", api_version, 0); + pem->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name); + + if (pem->msg_id_base != (u16) ~0) + pppoe_vat_api_hookup (vam); + + vec_free(name); + + return 0; +} diff --git a/src/vpp-api/java/Makefile.am b/src/vpp-api/java/Makefile.am index 4f1f7ed2..e0f5203d 100644 --- a/src/vpp-api/java/Makefile.am +++ b/src/vpp-api/java/Makefile.am @@ -148,6 +148,26 @@ jvpp-gtpu/io_fd_vpp_jvpp_gtpu_JVppGtpuImpl.h: $(jvpp_registry_ok) $(jvpp_gtpu_js $(call japigen,gtpu,JVppGtpuImpl) endif +# +# PPPOE Plugin +# +if ENABLE_PPPOE_PLUGIN +noinst_LTLIBRARIES += libjvpp_pppoe.la +libjvpp_pppoe_la_SOURCES = jvpp-pppoe/jvpp_pppoe.c +libjvpp_pppoe_la_CPPFLAGS = -Ijvpp-pppoe +libjvpp_pppoe_la_LIBADD = $(JVPP_LIBS) +libjvpp_pppoe_la_DEPENDENCIES = libjvpp_common.la + +BUILT_SOURCES += jvpp-pppoe/io_fd_vpp_jvpp_pppoe_JVppPppoeImpl.h +JAR_FILES += jvpp-pppoe-$(PACKAGE_VERSION).jar +CLEANDIRS += jvpp-pppoe/target + +jvpp_pppoe_json_files = @top_builddir@/plugins/pppoe/pppoe.api.json + +jvpp-pppoe/io_fd_vpp_jvpp_pppoe_JVppPppoeImpl.h: $(jvpp_registry_ok) $(jvpp_pppoe_json_files) + $(call japigen,pppoe,JVppPppoeImpl) +endif + # # SNAT Plugin # diff --git a/src/vpp-api/java/jvpp-pppoe/jvpp_pppoe.c b/src/vpp-api/java/jvpp-pppoe/jvpp_pppoe.c new file mode 100644 index 00000000..c9c30305 --- /dev/null +++ b/src/vpp-api/java/jvpp-pppoe/jvpp_pppoe.c @@ -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 + +#include +#define vl_typedefs /* define message structures */ +#include +#undef vl_typedefs + +#include +#include +#include + +#if VPPJNI_DEBUG == 1 + #define DEBUG_LOG(...) clib_warning(__VA_ARGS__) +#else + #define DEBUG_LOG(...) +#endif + +#include + +#include "jvpp-pppoe/io_fd_vpp_jvpp_pppoe_JVppPppoeImpl.h" +#include "jvpp_pppoe.h" +#include "jvpp-pppoe/jvpp_pppoe_gen.h" + +/* + * Class: io_fd_vpp_jvpp_pppoe_JVpppppoeImpl + * Method: init0 + * Signature: (JI)V + */ +JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_pppoe_JVppPppoeImpl_init0 + (JNIEnv *env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) { + pppoe_main_t * plugin_main = &pppoe_main; + clib_warning ("Java_io_fd_vpp_jvpp_pppoe_JVppPppoeImpl_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_pppoe_JVppPppoeImpl_close0 +(JNIEnv *env, jclass clazz) { + pppoe_main_t * plugin_main = &pppoe_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); +} diff --git a/src/vpp-api/java/jvpp-pppoe/jvpp_pppoe.h b/src/vpp-api/java/jvpp-pppoe/jvpp_pppoe.h new file mode 100644 index 00000000..4523ba9c --- /dev/null +++ b/src/vpp-api/java/jvpp-pppoe/jvpp_pppoe.h @@ -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_pppoe_h__ +#define __included_jvpp_pppoe_h__ + +#include +#include +#include +#include +#include +#include + +/* Global state for JVPP-pppoe */ +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; + +} pppoe_main_t; + +pppoe_main_t pppoe_main __attribute__((aligned (64))); + + +#endif /* __included_jvpp_pppoe_h__ */ diff --git a/test/test_pppoe.py b/test/test_pppoe.py new file mode 100644 index 00000000..0baf4546 --- /dev/null +++ b/test/test_pppoe.py @@ -0,0 +1,605 @@ +#!/usr/bin/env python + +import unittest +from logging import * + +from framework import VppTestCase, VppTestRunner +from vpp_ip_route import VppIpRoute, VppRoutePath +from vpp_pppoe_interface import VppPppoeInterface, VppPppoe6Interface +from vpp_papi_provider import L2_VTR_OP + +from scapy.packet import Raw +from scapy.layers.l2 import Ether +from scapy.layers.ppp import PPPoE, PPPoED, PPP +from scapy.layers.inet import IP, UDP +from scapy.layers.inet6 import IPv6 +from scapy.volatile import RandMAC, RandIP + +from util import ppp, ppc, mactobinary +import socket + + +class TestPPPoE(VppTestCase): + """ PPPoE Test Case """ + + @classmethod + def setUpClass(cls): + super(TestPPPoE, cls).setUpClass() + + cls.session_id = 1 + cls.dst_ip = "100.1.1.100" + cls.dst_ipn = socket.inet_pton(socket.AF_INET, cls.dst_ip) + + def setUp(self): + super(TestPPPoE, self).setUp() + + # create 2 pg interfaces + self.create_pg_interfaces(range(3)) + + for i in self.pg_interfaces: + i.admin_up() + i.config_ip4() + i.resolve_arp() + + def tearDown(self): + super(TestPPPoE, self).tearDown() + + self.logger.info(self.vapi.cli("show int")) + self.logger.info(self.vapi.cli("show pppoe fib")) + self.logger.info(self.vapi.cli("show pppoe session")) + self.logger.info(self.vapi.cli("show ip fib")) + self.logger.info(self.vapi.cli("show trace")) + + for i in self.pg_interfaces: + i.unconfig_ip4() + i.admin_down() + + def create_stream_pppoe_discovery(self, src_if, dst_if, + client_mac, count=1): + packets = [] + for i in range(count): + # create packet info stored in the test case instance + info = self.create_packet_info(src_if, dst_if) + # convert the info into packet payload + payload = self.info_to_payload(info) + # create the packet itself + p = (Ether(dst=src_if.local_mac, src=client_mac) / + PPPoED(sessionid=0) / + Raw(payload)) + # store a copy of the packet in the packet info + info.data = p.copy() + # append the packet to the list + packets.append(p) + + # return the created packet list + return packets + + def create_stream_pppoe_lcp(self, src_if, dst_if, + client_mac, session_id, count=1): + packets = [] + for i in range(count): + # create packet info stored in the test case instance + info = self.create_packet_info(src_if, dst_if) + # convert the info into packet payload + payload = self.info_to_payload(info) + # create the packet itself + p = (Ether(dst=src_if.local_mac, src=client_mac) / + PPPoE(sessionid=session_id) / + PPP(proto=0xc021) / + Raw(payload)) + # store a copy of the packet in the packet info + info.data = p.copy() + # append the packet to the list + packets.append(p) + + # return the created packet list + return packets + + def create_stream_pppoe_ip4(self, src_if, dst_if, + client_mac, session_id, client_ip, count=1): + packets = [] + for i in range(count): + # create packet info stored in the test case instance + info = self.create_packet_info(src_if, dst_if) + # convert the info into packet payload + payload = self.info_to_payload(info) + # create the packet itself + p = (Ether(dst=src_if.local_mac, src=client_mac) / + PPPoE(sessionid=session_id) / + PPP(proto=0x0021) / + IP(src=client_ip, dst=self.dst_ip) / + Raw(payload)) + # store a copy of the packet in the packet info + info.data = p.copy() + # append the packet to the list + packets.append(p) + + # return the created packet list + return packets + + def create_stream_ip4(self, src_if, dst_if, client_ip, dst_ip, count=1): + pkts = [] + for i in range(count): + # create packet info stored in the test case instance + info = self.create_packet_info(src_if, dst_if) + # convert the info into packet payload + payload = self.info_to_payload(info) + # create the packet itself + p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) / + IP(src=dst_ip, dst=client_ip) / + Raw(payload)) + # store a copy of the packet in the packet info + info.data = p.copy() + # append the packet to the list + pkts.append(p) + + # return the created packet list + return pkts + + def verify_decapped_pppoe(self, src_if, capture, sent): + self.assertEqual(len(capture), len(sent)) + + for i in range(len(capture)): + try: + tx = sent[i] + rx = capture[i] + + tx_ip = tx[IP] + rx_ip = rx[IP] + + self.assertEqual(rx_ip.src, tx_ip.src) + self.assertEqual(rx_ip.dst, tx_ip.dst) + + except: + self.logger.error(ppp("Rx:", rx)) + self.logger.error(ppp("Tx:", tx)) + raise + + def verify_encaped_pppoe(self, src_if, capture, sent, session_id): + + self.assertEqual(len(capture), len(sent)) + + for i in range(len(capture)): + try: + tx = sent[i] + rx = capture[i] + + tx_ip = tx[IP] + rx_ip = rx[IP] + + self.assertEqual(rx_ip.src, tx_ip.src) + self.assertEqual(rx_ip.dst, tx_ip.dst) + + rx_pppoe = rx[PPPoE] + + self.assertEqual(rx_pppoe.sessionid, session_id) + + except: + self.logger.error(ppp("Rx:", rx)) + self.logger.error(ppp("Tx:", tx)) + raise + + def test_PPPoE_Decap(self): + """ PPPoE Decap Test """ + + self.vapi.cli("clear trace") + + # + # Add a route that resolves the server's destination + # + route_sever_dst = VppIpRoute(self, "100.1.1.100", 32, + [VppRoutePath(self.pg1.remote_ip4, + self.pg1.sw_if_index)]) + route_sever_dst.add_vpp_config() + + # Send PPPoE Discovery + tx0 = self.create_stream_pppoe_discovery(self.pg0, self.pg1, + self.pg0.remote_mac) + self.pg0.add_stream(tx0) + self.pg_start() + + # Send PPPoE PPP LCP + tx1 = self.create_stream_pppoe_lcp(self.pg0, self.pg1, + self.pg0.remote_mac, + self.session_id) + self.pg0.add_stream(tx1) + self.pg_start() + + # Create PPPoE session + pppoe_if = VppPppoeInterface(self, + self.pg0.remote_ip4, + self.pg0.remote_mac, + self.session_id) + pppoe_if.add_vpp_config() + + # + # Send tunneled packets that match the created tunnel and + # are decapped and forwarded + # + tx2 = self.create_stream_pppoe_ip4(self.pg0, self.pg1, + self.pg0.remote_mac, + self.session_id, + self.pg0.remote_ip4) + self.pg0.add_stream(tx2) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx2 = self.pg1.get_capture(len(tx2)) + self.verify_decapped_pppoe(self.pg0, rx2, tx2) + + self.logger.info(self.vapi.cli("show pppoe fib")) + self.logger.info(self.vapi.cli("show pppoe session")) + self.logger.info(self.vapi.cli("show ip fib")) + + # + # test case cleanup + # + + # Delete PPPoE session + pppoe_if.remove_vpp_config() + + # Delete a route that resolves the server's destination + route_sever_dst.remove_vpp_config() + + def test_PPPoE_Encap(self): + """ PPPoE Encap Test """ + + self.vapi.cli("clear trace") + + # + # Add a route that resolves the server's destination + # + route_sever_dst = VppIpRoute(self, "100.1.1.100", 32, + [VppRoutePath(self.pg1.remote_ip4, + self.pg1.sw_if_index)]) + route_sever_dst.add_vpp_config() + + # Send PPPoE Discovery + tx0 = self.create_stream_pppoe_discovery(self.pg0, self.pg1, + self.pg0.remote_mac) + self.pg0.add_stream(tx0) + self.pg_start() + + # Send PPPoE PPP LCP + tx1 = self.create_stream_pppoe_lcp(self.pg0, self.pg1, + self.pg0.remote_mac, + self.session_id) + self.pg0.add_stream(tx1) + self.pg_start() + + # Create PPPoE session + pppoe_if = VppPppoeInterface(self, + self.pg0.remote_ip4, + self.pg0.remote_mac, + self.session_id) + pppoe_if.add_vpp_config() + + # + # Send a packet stream that is routed into the session + # - packets are PPPoE encapped + # + self.vapi.cli("clear trace") + tx2 = self.create_stream_ip4(self.pg1, self.pg0, + self.pg0.remote_ip4, self.dst_ip) + self.pg1.add_stream(tx2) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx2 = self.pg0.get_capture(len(tx2)) + self.verify_encaped_pppoe(self.pg1, rx2, tx2, self.session_id) + + self.logger.info(self.vapi.cli("show pppoe fib")) + self.logger.info(self.vapi.cli("show pppoe session")) + self.logger.info(self.vapi.cli("show ip fib")) + + # + # test case cleanup + # + + # Delete PPPoE session + pppoe_if.remove_vpp_config() + + # Delete a route that resolves the server's destination + route_sever_dst.remove_vpp_config() + + def test_PPPoE_Add_Twice(self): + """ PPPoE Add Same Session Twice Test """ + + self.vapi.cli("clear trace") + + # + # Add a route that resolves the server's destination + # + route_sever_dst = VppIpRoute(self, "100.1.1.100", 32, + [VppRoutePath(self.pg1.remote_ip4, + self.pg1.sw_if_index)]) + route_sever_dst.add_vpp_config() + + # Send PPPoE Discovery + tx0 = self.create_stream_pppoe_discovery(self.pg0, self.pg1, + self.pg0.remote_mac) + self.pg0.add_stream(tx0) + self.pg_start() + + # Send PPPoE PPP LCP + tx1 = self.create_stream_pppoe_lcp(self.pg0, self.pg1, + self.pg0.remote_mac, + self.session_id) + self.pg0.add_stream(tx1) + self.pg_start() + + # Create PPPoE session + pppoe_if = VppPppoeInterface(self, + self.pg0.remote_ip4, + self.pg0.remote_mac, + self.session_id) + pppoe_if.add_vpp_config() + + # + # The double create (create the same session twice) should fail, + # and we should still be able to use the original + # + try: + gre_if.add_vpp_config() + except Exception: + pass + else: + self.fail("Double GRE tunnel add does not fail") + + # + # test case cleanup + # + + # Delete PPPoE session + pppoe_if.remove_vpp_config() + + # Delete a route that resolves the server's destination + route_sever_dst.remove_vpp_config() + + def test_PPPoE_Del_Twice(self): + """ PPPoE Delete Same Session Twice Test """ + + self.vapi.cli("clear trace") + + # + # Add a route that resolves the server's destination + # + route_sever_dst = VppIpRoute(self, "100.1.1.100", 32, + [VppRoutePath(self.pg1.remote_ip4, + self.pg1.sw_if_index)]) + route_sever_dst.add_vpp_config() + + # Send PPPoE Discovery + tx0 = self.create_stream_pppoe_discovery(self.pg0, self.pg1, + self.pg0.remote_mac) + self.pg0.add_stream(tx0) + self.pg_start() + + # Send PPPoE PPP LCP + tx1 = self.create_stream_pppoe_lcp(self.pg0, self.pg1, + self.pg0.remote_mac, + self.session_id) + self.pg0.add_stream(tx1) + self.pg_start() + + # Create PPPoE session + pppoe_if = VppPppoeInterface(self, + self.pg0.remote_ip4, + self.pg0.remote_mac, + self.session_id) + pppoe_if.add_vpp_config() + + # Delete PPPoE session + pppoe_if.remove_vpp_config() + + # + # The double del (del the same session twice) should fail, + # and we should still be able to use the original + # + try: + gre_if.remove_vpp_config() + except Exception: + pass + else: + self.fail("Double GRE tunnel del does not fail") + + # + # test case cleanup + # + + # Delete a route that resolves the server's destination + route_sever_dst.remove_vpp_config() + + def test_PPPoE_Decap_Multiple(self): + """ PPPoE Decap Multiple Sessions Test """ + + self.vapi.cli("clear trace") + + # + # Add a route that resolves the server's destination + # + route_sever_dst = VppIpRoute(self, "100.1.1.100", 32, + [VppRoutePath(self.pg1.remote_ip4, + self.pg1.sw_if_index)]) + route_sever_dst.add_vpp_config() + + # Send PPPoE Discovery 1 + tx0 = self.create_stream_pppoe_discovery(self.pg0, self.pg1, + self.pg0.remote_mac) + self.pg0.add_stream(tx0) + self.pg_start() + + # Send PPPoE PPP LCP 1 + tx1 = self.create_stream_pppoe_lcp(self.pg0, self.pg1, + self.pg0.remote_mac, + self.session_id) + self.pg0.add_stream(tx1) + self.pg_start() + + # Create PPPoE session 1 + pppoe_if1 = VppPppoeInterface(self, + self.pg0.remote_ip4, + self.pg0.remote_mac, + self.session_id) + pppoe_if1.add_vpp_config() + + # Send PPPoE Discovery 2 + tx3 = self.create_stream_pppoe_discovery(self.pg2, self.pg1, + self.pg2.remote_mac) + self.pg2.add_stream(tx3) + self.pg_start() + + # Send PPPoE PPP LCP 2 + tx4 = self.create_stream_pppoe_lcp(self.pg2, self.pg1, + self.pg2.remote_mac, + self.session_id + 1) + self.pg2.add_stream(tx4) + self.pg_start() + + # Create PPPoE session 2 + pppoe_if2 = VppPppoeInterface(self, + self.pg2.remote_ip4, + self.pg2.remote_mac, + self.session_id + 1) + pppoe_if2.add_vpp_config() + + # + # Send tunneled packets that match the created tunnel and + # are decapped and forwarded + # + tx2 = self.create_stream_pppoe_ip4(self.pg0, self.pg1, + self.pg0.remote_mac, + self.session_id, + self.pg0.remote_ip4) + self.pg0.add_stream(tx2) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx2 = self.pg1.get_capture(len(tx2)) + self.verify_decapped_pppoe(self.pg0, rx2, tx2) + + tx5 = self.create_stream_pppoe_ip4(self.pg2, self.pg1, + self.pg2.remote_mac, + self.session_id + 1, + self.pg2.remote_ip4) + self.pg2.add_stream(tx5) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx5 = self.pg1.get_capture(len(tx5)) + self.verify_decapped_pppoe(self.pg2, rx5, tx5) + + self.logger.info(self.vapi.cli("show pppoe fib")) + self.logger.info(self.vapi.cli("show pppoe session")) + self.logger.info(self.vapi.cli("show ip fib")) + + # + # test case cleanup + # + + # Delete PPPoE session + pppoe_if1.remove_vpp_config() + pppoe_if2.remove_vpp_config() + + # Delete a route that resolves the server's destination + route_sever_dst.remove_vpp_config() + + def test_PPPoE_Encap_Multiple(self): + """ PPPoE Encap Multiple Sessions Test """ + + self.vapi.cli("clear trace") + + # + # Add a route that resolves the server's destination + # + route_sever_dst = VppIpRoute(self, "100.1.1.100", 32, + [VppRoutePath(self.pg1.remote_ip4, + self.pg1.sw_if_index)]) + route_sever_dst.add_vpp_config() + + # Send PPPoE Discovery 1 + tx0 = self.create_stream_pppoe_discovery(self.pg0, self.pg1, + self.pg0.remote_mac) + self.pg0.add_stream(tx0) + self.pg_start() + + # Send PPPoE PPP LCP 1 + tx1 = self.create_stream_pppoe_lcp(self.pg0, self.pg1, + self.pg0.remote_mac, + self.session_id) + self.pg0.add_stream(tx1) + self.pg_start() + + # Create PPPoE session 1 + pppoe_if1 = VppPppoeInterface(self, + self.pg0.remote_ip4, + self.pg0.remote_mac, + self.session_id) + pppoe_if1.add_vpp_config() + + # Send PPPoE Discovery 2 + tx3 = self.create_stream_pppoe_discovery(self.pg2, self.pg1, + self.pg2.remote_mac) + self.pg2.add_stream(tx3) + self.pg_start() + + # Send PPPoE PPP LCP 2 + tx4 = self.create_stream_pppoe_lcp(self.pg2, self.pg1, + self.pg2.remote_mac, + self.session_id + 1) + self.pg2.add_stream(tx4) + self.pg_start() + + # Create PPPoE session 2 + pppoe_if2 = VppPppoeInterface(self, + self.pg2.remote_ip4, + self.pg2.remote_mac, + self.session_id + 1) + pppoe_if2.add_vpp_config() + + # + # Send a packet stream that is routed into the session + # - packets are PPPoE encapped + # + self.vapi.cli("clear trace") + tx2 = self.create_stream_ip4(self.pg1, self.pg0, + self.pg0.remote_ip4, self.dst_ip) + self.pg1.add_stream(tx2) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx2 = self.pg0.get_capture(len(tx2)) + self.verify_encaped_pppoe(self.pg1, rx2, tx2, self.session_id) + + tx5 = self.create_stream_ip4(self.pg1, self.pg2, + self.pg2.remote_ip4, self.dst_ip) + self.pg1.add_stream(tx5) + + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx5 = self.pg2.get_capture(len(tx5)) + self.verify_encaped_pppoe(self.pg1, rx5, tx5, self.session_id + 1) + + self.logger.info(self.vapi.cli("show pppoe fib")) + self.logger.info(self.vapi.cli("show pppoe session")) + self.logger.info(self.vapi.cli("show ip fib")) + + # + # test case cleanup + # + + # Delete PPPoE session + pppoe_if1.remove_vpp_config() + pppoe_if2.remove_vpp_config() + + # Delete a route that resolves the server's destination + route_sever_dst.remove_vpp_config() + +if __name__ == '__main__': + unittest.main(testRunner=VppTestRunner) diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 3ba2ad4a..4d017c1f 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -2087,3 +2087,29 @@ class VppPapiProvider(object): 'decap_vrf_id': decap_vrf_id, 'protocol': protocol, 'vni': vni}) + + def pppoe_add_del_session( + self, + client_ip, + client_mac, + session_id=0, + is_add=1, + is_ipv6=0, + decap_vrf_id=0): + """ + + :param is_add: (Default value = 1) + :param is_ipv6: (Default value = 0) + :param client_ip: + :param session_id: (Default value = 0) + :param client_mac: + :param decap_vrf_id: (Default value = 0) + + """ + return self.api(self.papi.pppoe_add_del_session, + {'is_add': is_add, + 'is_ipv6': is_ipv6, + 'session_id': session_id, + 'client_ip': client_ip, + 'decap_vrf_id': decap_vrf_id, + 'client_mac': client_mac}) diff --git a/test/vpp_pppoe_interface.py b/test/vpp_pppoe_interface.py new file mode 100644 index 00000000..9a8b8699 --- /dev/null +++ b/test/vpp_pppoe_interface.py @@ -0,0 +1,79 @@ + +from vpp_interface import VppInterface +import socket +from util import ppp, ppc, mactobinary + + +class VppPppoeInterface(VppInterface): + """ + VPP Pppoe interface + """ + + def __init__(self, test, client_ip, client_mac, + session_id, decap_vrf_id=0): + """ Create VPP PPPoE4 interface """ + self._sw_if_index = 0 + super(VppPppoeInterface, self).__init__(test) + self._test = test + self.client_ip = client_ip + self.client_mac = client_mac + self.session_id = session_id + self.decap_vrf_id = decap_vrf_id + + def add_vpp_config(self): + cip = socket.inet_pton(socket.AF_INET, self.client_ip) + cmac = mactobinary(self.client_mac) + r = self.test.vapi.pppoe_add_del_session( + cip, cmac, + session_id=self.session_id, + decap_vrf_id=self.decap_vrf_id) + self._sw_if_index = r.sw_if_index + self.generate_remote_hosts() + + def remove_vpp_config(self): + cip = socket.inet_pton(socket.AF_INET, self.client_ip) + cmac = mactobinary(self.client_mac) + self.unconfig() + r = self.test.vapi.pppoe_add_del_session( + cip, cmac, + session_id=self.session_id, + decap_vrf_id=self.decap_vrf_id, + is_add=0) + + +class VppPppoe6Interface(VppInterface): + """ + VPP Pppoe IPv6 interface + """ + + def __init__(self, test, src_ip, dst_ip, outer_fib_id=0, is_teb=0): + """ Create VPP PPPoE6 interface """ + self._sw_if_index = 0 + super(VppPppoe6Interface, self).__init__(test) + self._test = test + self.client_ip = client_ip + self.client_mac = client_mac + self.session_id = session_id + self.decap_vrf_id = decap_vrf_id + + def add_vpp_config(self): + cip = socket.inet_pton(socket.AF_INET6, self.client_ip) + cmac = mactobinary(self.client_mac) + r = self.test.vapi.pppoe_add_del_session( + cip, cmac, + session_id=self.session_id, + decap_vrf_id=self.decap_vrf_id, + is_ip6=1) + self._sw_if_index = r.sw_if_index + self.generate_remote_hosts() + + def remove_vpp_config(self): + cip = socket.inet_pton(socket.AF_INET6, self.client_ip) + cmac = mactobinary(self.client_mac) + self.unconfig() + r = self.test.vapi.pppoe_add_del_session( + cip, cmac, + session_id=self.session_id, + decap_vrf_id=self.decap_vrf_id, + is_add=0, + is_ip6=1) -- cgit 1.2.3-korg From a07bd708002a9c3d3c584f0d692deed1a758b517 Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Mon, 7 Aug 2017 07:53:49 -0700 Subject: Dedicated SW Interface Event Change-Id: I06a10a4291e61aec3f1396d2514ed6fe3901897a Signed-off-by: Neale Ranns Signed-off-by: Marek Gradzki --- src/vat/api_format.c | 17 ++++++----------- src/vnet/devices/virtio/vhost_user_api.c | 6 +++--- src/vnet/interface.api | 19 ++++++++++++++++--- src/vnet/interface_api.c | 8 ++++---- src/vnet/unix/tap_api.c | 6 +++--- .../CallbackJVppFacadeNotificationExample.java | 2 +- .../examples/CallbackNotificationApiExample.java | 18 ++++++------------ .../core/examples/FutureApiNotificationExample.java | 2 +- .../fd/vpp/jvpp/core/examples/NotificationUtils.java | 5 ++--- src/vpp-api/java/jvpp/gen/jvppgen/util.py | 3 +-- src/vpp/api/custom_dump.c | 20 ++++++++++++++++++++ test/vpp_papi_provider.py | 9 ++------- 12 files changed, 65 insertions(+), 50 deletions(-) (limited to 'src/vpp-api/java') diff --git a/src/vat/api_format.c b/src/vat/api_format.c index 009cf173..ddcd5621 100644 --- a/src/vat/api_format.c +++ b/src/vat/api_format.c @@ -972,8 +972,8 @@ static void vl_api_sw_interface_details_t_handler_json } #if VPP_API_TEST_BUILTIN == 0 -static void vl_api_sw_interface_set_flags_t_handler - (vl_api_sw_interface_set_flags_t * mp) +static void vl_api_sw_interface_event_t_handler + (vl_api_sw_interface_event_t * mp) { vat_main_t *vam = &vat_main; if (vam->interface_event_display) @@ -984,8 +984,8 @@ static void vl_api_sw_interface_set_flags_t_handler } #endif -static void vl_api_sw_interface_set_flags_t_handler_json - (vl_api_sw_interface_set_flags_t * mp) +static void vl_api_sw_interface_event_t_handler_json + (vl_api_sw_interface_event_t * mp) { /* JSON output not supported */ } @@ -5026,7 +5026,7 @@ _(LLDP_CONFIG_REPLY, lldp_config_reply) \ _(SW_INTERFACE_SET_LLDP_REPLY, sw_interface_set_lldp_reply) #define foreach_standalone_reply_msg \ -_(SW_INTERFACE_SET_FLAGS, sw_interface_set_flags) \ +_(SW_INTERFACE_EVENT, sw_interface_event) \ _(VNET_INTERFACE_SIMPLE_COUNTERS, vnet_interface_simple_counters) \ _(VNET_INTERFACE_COMBINED_COUNTERS, vnet_interface_combined_counters) \ _(VNET_IP4_FIB_COUNTERS, vnet_ip4_fib_counters) \ @@ -5772,7 +5772,7 @@ api_sw_interface_set_flags (vat_main_t * vam) vl_api_sw_interface_set_flags_t *mp; u32 sw_if_index; u8 sw_if_index_set = 0; - u8 admin_up = 0, link_up = 0; + u8 admin_up = 0; int ret; /* Parse args required to build the message */ @@ -5782,10 +5782,6 @@ api_sw_interface_set_flags (vat_main_t * vam) admin_up = 1; else if (unformat (i, "admin-down")) admin_up = 0; - else if (unformat (i, "link-up")) - link_up = 1; - else if (unformat (i, "link-down")) - link_up = 0; else if (unformat (i, "%U", api_unformat_sw_if_index, vam, &sw_if_index)) sw_if_index_set = 1; @@ -5805,7 +5801,6 @@ api_sw_interface_set_flags (vat_main_t * vam) M (SW_INTERFACE_SET_FLAGS, mp); mp->sw_if_index = ntohl (sw_if_index); mp->admin_up_down = admin_up; - mp->link_up_down = link_up; /* send it... */ S (mp); diff --git a/src/vnet/devices/virtio/vhost_user_api.c b/src/vnet/devices/virtio/vhost_user_api.c index 8dbd032b..3f0aac9e 100644 --- a/src/vnet/devices/virtio/vhost_user_api.c +++ b/src/vnet/devices/virtio/vhost_user_api.c @@ -52,11 +52,11 @@ _(SW_INTERFACE_VHOST_USER_DUMP, sw_interface_vhost_user_dump) * WARNING: replicated pending api refactor completion */ static void -send_sw_interface_flags_deleted (vpe_api_main_t * am, +send_sw_interface_event_deleted (vpe_api_main_t * am, unix_shared_memory_queue_t * q, u32 sw_if_index) { - vl_api_sw_interface_set_flags_t *mp; + vl_api_sw_interface_event_t *mp; mp = vl_msg_api_alloc (sizeof (*mp)); memset (mp, 0, sizeof (*mp)); @@ -143,7 +143,7 @@ vl_api_delete_vhost_user_if_t_handler (vl_api_delete_vhost_user_if_t * mp) return; vnet_clear_sw_interface_tag (vnm, sw_if_index); - send_sw_interface_flags_deleted (vam, q, sw_if_index); + send_sw_interface_event_deleted (vam, q, sw_if_index); } } diff --git a/src/vnet/interface.api b/src/vnet/interface.api index 14ff6d5a..a1890706 100644 --- a/src/vnet/interface.api +++ b/src/vnet/interface.api @@ -4,7 +4,6 @@ @param sw_if_index - index of the interface to set flags on @param admin_up_down - set the admin state, 1 = up, 0 = down @param link_up_down - Oper state sent on change event, not used in config. - @param deleted - interface was deleted */ autoreply define sw_interface_set_flags { @@ -13,8 +12,6 @@ autoreply define sw_interface_set_flags u32 sw_if_index; /* 1 = up, 0 = down */ u8 admin_up_down; - u8 link_up_down; - u8 deleted; }; /** \brief Set interface MTU @@ -31,6 +28,22 @@ autoreply define sw_interface_set_mtu u16 mtu; }; +/** \brief Interface Event generated by want_interface_events + @param context - sender context, to match reply w/ request + @param sw_if_index - index of the interface of the event + @param admin_up_down - The administrative state; 1 = up, 0 = down + @param link_up_down - The operational state; 1 = up, 0 = down + @param deleted - interface was deleted +*/ +define sw_interface_event +{ + u32 context; + u32 sw_if_index; + u8 admin_up_down; + u8 link_up_down; + u8 deleted; +}; + /** \brief Register for interface events @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request diff --git a/src/vnet/interface_api.c b/src/vnet/interface_api.c index ab0b255a..c56fef68 100644 --- a/src/vnet/interface_api.c +++ b/src/vnet/interface_api.c @@ -571,18 +571,18 @@ event_data_cmp (void *a1, void *a2) } static void -send_sw_interface_flags (vpe_api_main_t * am, +send_sw_interface_event (vpe_api_main_t * am, unix_shared_memory_queue_t * q, vnet_sw_interface_t * swif) { - vl_api_sw_interface_set_flags_t *mp; + vl_api_sw_interface_event_t *mp; vnet_main_t *vnm = am->vnet_main; vnet_hw_interface_t *hi = vnet_get_sup_hw_interface (vnm, swif->sw_if_index); mp = vl_msg_api_alloc (sizeof (*mp)); memset (mp, 0, sizeof (*mp)); - mp->_vl_msg_id = ntohs (VL_API_SW_INTERFACE_SET_FLAGS); + mp->_vl_msg_id = ntohs (VL_API_SW_INTERFACE_EVENT); mp->sw_if_index = ntohl (swif->sw_if_index); mp->admin_up_down = (swif->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ? 1 : 0; @@ -638,7 +638,7 @@ link_state_process (vlib_main_t * vm, event_data[i])) { swif = vnet_get_sw_interface (vnm, event_data[i]); - send_sw_interface_flags (vam, q, swif); + send_sw_interface_event (vam, q, swif); } } })); diff --git a/src/vnet/unix/tap_api.c b/src/vnet/unix/tap_api.c index 9b8d52a6..7e812c4f 100644 --- a/src/vnet/unix/tap_api.c +++ b/src/vnet/unix/tap_api.c @@ -59,11 +59,11 @@ _(SW_INTERFACE_TAP_DUMP, sw_interface_tap_dump) * WARNING: replicated pending api refactor completion */ static void -send_sw_interface_flags_deleted (vpe_api_main_t * am, +send_sw_interface_event_deleted (vpe_api_main_t * am, unix_shared_memory_queue_t * q, u32 sw_if_index) { - vl_api_sw_interface_set_flags_t *mp; + vl_api_sw_interface_event_t *mp; mp = vl_msg_api_alloc (sizeof (*mp)); memset (mp, 0, sizeof (*mp)); @@ -196,7 +196,7 @@ vl_api_tap_delete_t_handler (vl_api_tap_delete_t * mp) vl_msg_api_send_shmem (q, (u8 *) & rmp); if (!rv) - send_sw_interface_flags_deleted (vam, q, sw_if_index); + send_sw_interface_event_deleted (vam, q, sw_if_index); } static void diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackJVppFacadeNotificationExample.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackJVppFacadeNotificationExample.java index b8b108b6..308dad9f 100644 --- a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackJVppFacadeNotificationExample.java +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackJVppFacadeNotificationExample.java @@ -36,7 +36,7 @@ public class CallbackJVppFacadeNotificationExample { System.out.println("Successfully connected to VPP"); final AutoCloseable notificationListenerReg = - jvppCallbackFacade.getNotificationRegistry().registerSwInterfaceSetFlagsNotificationCallback( + jvppCallbackFacade.getNotificationRegistry().registerSwInterfaceEventNotificationCallback( NotificationUtils::printNotification ); diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackNotificationApiExample.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackNotificationApiExample.java index 6ee2de31..7d56b7ea 100644 --- a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackNotificationApiExample.java +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/CallbackNotificationApiExample.java @@ -26,10 +26,9 @@ import io.fd.vpp.jvpp.JVppRegistry; import io.fd.vpp.jvpp.JVppRegistryImpl; import io.fd.vpp.jvpp.VppCallbackException; import io.fd.vpp.jvpp.core.JVppCoreImpl; -import io.fd.vpp.jvpp.core.callback.SwInterfaceSetFlagsCallback; -import io.fd.vpp.jvpp.core.callback.SwInterfaceSetFlagsNotificationCallback; +import io.fd.vpp.jvpp.core.callback.SwInterfaceEventNotificationCallback; import io.fd.vpp.jvpp.core.callback.WantInterfaceEventsCallback; -import io.fd.vpp.jvpp.core.dto.SwInterfaceSetFlagsNotification; +import io.fd.vpp.jvpp.core.dto.SwInterfaceEventNotification; import io.fd.vpp.jvpp.core.dto.SwInterfaceSetFlagsReply; import io.fd.vpp.jvpp.core.dto.WantInterfaceEventsReply; @@ -65,12 +64,12 @@ public class CallbackNotificationApiExample { testCallbackApi(); } - private static class TestCallback implements SwInterfaceSetFlagsNotificationCallback, - WantInterfaceEventsCallback, SwInterfaceSetFlagsCallback { + private static class TestCallback implements SwInterfaceEventNotificationCallback, + WantInterfaceEventsCallback { @Override - public void onSwInterfaceSetFlagsNotification( - final SwInterfaceSetFlagsNotification msg) { + public void onSwInterfaceEventNotification( + final SwInterfaceEventNotification msg) { printNotification(msg); } @@ -79,11 +78,6 @@ public class CallbackNotificationApiExample { System.out.println("Interface notification stream updated"); } - @Override - public void onSwInterfaceSetFlagsReply(final SwInterfaceSetFlagsReply swInterfaceSetFlagsReply) { - System.out.println("Interface flags set successfully"); - } - @Override public void onError(VppCallbackException ex) { System.out.printf("Received onError exception in getNodeIndexCallback: call=%s, reply=%d, context=%d%n", diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiNotificationExample.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiNotificationExample.java index f445dcc8..7460401e 100644 --- a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiNotificationExample.java +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/FutureApiNotificationExample.java @@ -33,7 +33,7 @@ public class FutureApiNotificationExample { final FutureJVppCoreFacade jvppFacade = new FutureJVppCoreFacade(registry, new JVppCoreImpl()); final AutoCloseable notificationListenerReg = jvppFacade.getNotificationRegistry() - .registerSwInterfaceSetFlagsNotificationCallback(NotificationUtils::printNotification)) { + .registerSwInterfaceEventNotificationCallback(NotificationUtils::printNotification)) { System.out.println("Successfully connected to VPP"); jvppFacade.wantInterfaceEvents(getEnableInterfaceNotificationsReq()).toCompletableFuture().get(); System.out.println("Interface events started"); diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/NotificationUtils.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/NotificationUtils.java index 7791cafe..d3f9dd2c 100644 --- a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/NotificationUtils.java +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/NotificationUtils.java @@ -18,14 +18,14 @@ package io.fd.vpp.jvpp.core.examples; import java.io.PrintStream; import io.fd.vpp.jvpp.core.dto.SwInterfaceSetFlags; -import io.fd.vpp.jvpp.core.dto.SwInterfaceSetFlagsNotification; +import io.fd.vpp.jvpp.core.dto.SwInterfaceEventNotification; import io.fd.vpp.jvpp.core.dto.WantInterfaceEvents; final class NotificationUtils { private NotificationUtils() {} - static PrintStream printNotification(final SwInterfaceSetFlagsNotification msg) { + static PrintStream printNotification(final SwInterfaceEventNotification msg) { return System.out.printf("Received interface notification: ifc: %s%n", msg); } @@ -33,7 +33,6 @@ final class NotificationUtils { final SwInterfaceSetFlags swInterfaceSetFlags = new SwInterfaceSetFlags(); swInterfaceSetFlags.swIfIndex = 0; swInterfaceSetFlags.adminUpDown = 1; - swInterfaceSetFlags.deleted = 0; return swInterfaceSetFlags; } diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/util.py b/src/vpp-api/java/jvpp/gen/jvppgen/util.py index 947fc31d..42394419 100644 --- a/src/vpp-api/java/jvpp/gen/jvppgen/util.py +++ b/src/vpp-api/java/jvpp/gen/jvppgen/util.py @@ -161,7 +161,6 @@ unconventional_naming_rep_req = { # # FIXME no convention in the naming of events (notifications) in vpe.api notifications_message_suffixes = ("event", "counters") -notification_messages_reused = ["sw_interface_set_flags"] # messages that must be ignored. These messages are INSUFFICIENTLY marked as disabled in vpe.api # FIXME @@ -170,7 +169,7 @@ ignored_messages = [] def is_notification(name): """ Returns true if the structure is a notification regardless of its no other use """ - return is_just_notification(name) or name.lower() in notification_messages_reused + return is_just_notification(name) def is_just_notification(name): diff --git a/src/vpp/api/custom_dump.c b/src/vpp/api/custom_dump.c index a57799cb..0342476a 100644 --- a/src/vpp/api/custom_dump.c +++ b/src/vpp/api/custom_dump.c @@ -103,6 +103,22 @@ static void *vl_api_sw_interface_set_flags_t_print s = format (s, "sw_if_index %d ", ntohl (mp->sw_if_index)); + if (mp->admin_up_down) + s = format (s, "admin-up "); + else + s = format (s, "admin-down "); + + FINISH; +} + +static void *vl_api_sw_interface_event_t_print + (vl_api_sw_interface_event_t * mp, void *handle) +{ + u8 *s; + s = format (0, "SCRIPT: sw_interface_event "); + + s = format (s, "sw_if_index %d ", ntohl (mp->sw_if_index)); + if (mp->admin_up_down) s = format (s, "admin-up "); else @@ -113,6 +129,9 @@ static void *vl_api_sw_interface_set_flags_t_print else s = format (s, "link-down"); + if (mp->deleted) + s = format (s, " deleted"); + FINISH; } @@ -3010,6 +3029,7 @@ foreach_custom_print_no_arg_function _(CREATE_LOOPBACK, create_loopback) \ _(CREATE_LOOPBACK_INSTANCE, create_loopback_instance) \ _(SW_INTERFACE_SET_FLAGS, sw_interface_set_flags) \ +_(SW_INTERFACE_EVENT, sw_interface_event) \ _(SW_INTERFACE_ADD_DEL_ADDRESS, sw_interface_add_del_address) \ _(SW_INTERFACE_SET_TABLE, sw_interface_set_table) \ _(SW_INTERFACE_SET_MPLS_ENABLE, sw_interface_set_mpls_enable) \ diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 4d017c1f..c99d4583 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -550,21 +550,16 @@ class VppPapiProvider(object): 'tag1': tag1, 'tag2': tag2}) - def sw_interface_set_flags(self, sw_if_index, admin_up_down, - link_up_down=0, deleted=0): + def sw_interface_set_flags(self, sw_if_index, admin_up_down): """ :param admin_up_down: :param sw_if_index: - :param link_up_down: (Default value = 0) - :param deleted: (Default value = 0) """ return self.api(self.papi.sw_interface_set_flags, {'sw_if_index': sw_if_index, - 'admin_up_down': admin_up_down, - 'link_up_down': link_up_down, - 'deleted': deleted}) + 'admin_up_down': admin_up_down}) def create_subif(self, sw_if_index, sub_id, outer_vlan, inner_vlan, no_tags=0, one_tag=0, two_tags=0, dot1ad=0, exact_match=0, -- cgit 1.2.3-korg From 5beec81360146536086f1996869b4ee32ca37ddc Mon Sep 17 00:00:00 2001 From: Jan Srnicek Date: Fri, 24 Mar 2017 10:18:11 +0100 Subject: jvpp: make shm_prefix configurable (VPP-591) svm.c - set default map region root path only if root path is not already present memory_shared.c - added option for tests to send memory region name and root path in one variable, if so name and root path are separated here and set to map region structure so find function can find it properly jvpp-registry.c - added parameters shmPrefix to be able pass + removed sudo restriction specific shared memory prefix that is used while starting python tests(see framework.py) JVppRegistyImpl - added option to specify shmPrefix VppJNIConnection - added option to specify shmPrefix Change-Id: I3f89f867fb9b20eef00fbd497cb0e41b25d6eab7 Signed-off-by: Jan Srnicek Signed-off-by: Matej Perina --- src/svm/svm.c | 3 +- src/vlibmemory/memory_shared.c | 19 ++++++++++-- .../io/fd/vpp/jvpp/JVppRegistryImpl.java | 7 +++++ .../io/fd/vpp/jvpp/VppJNIConnection.java | 23 +++++++++++---- src/vpp-api/java/jvpp-registry/jvpp_registry.c | 34 ++++++++++++---------- 5 files changed, 62 insertions(+), 24 deletions(-) (limited to 'src/vpp-api/java') diff --git a/src/svm/svm.c b/src/svm/svm.c index 600fa744..0442ecb2 100644 --- a/src/svm/svm.c +++ b/src/svm/svm.c @@ -862,7 +862,8 @@ svm_region_find_or_create (svm_map_region_args_t * a) ASSERT (mp); /* Map the named region from the correct chroot environment */ - a->root_path = (char *) mp->root_path; + if (a->root_path == NULL) + a->root_path = (char *) mp->root_path; /* * See if this region is already known. If it is, we're diff --git a/src/vlibmemory/memory_shared.c b/src/vlibmemory/memory_shared.c index 41aa1231..9bab6573 100644 --- a/src/vlibmemory/memory_shared.c +++ b/src/vlibmemory/memory_shared.c @@ -341,12 +341,25 @@ vl_map_shmem (const char *region_name, int is_vlib) struct timespec ts, tsrem; u32 vlib_input_queue_length; + memset (a, 0, sizeof (*a)); + + if (strstr (region_name, "-vpe-api")) + { + char root_path[strlen (region_name)]; + strncpy (root_path, region_name, strlen (region_name) - 8); + a->root_path = root_path; + am->root_path = root_path; + } + if (is_vlib == 0) svm_region_init_chroot (am->root_path); - memset (a, 0, sizeof (*a)); - - a->name = region_name; + if (a->root_path != NULL) + { + a->name = "/vpe-api"; + } + else + a->name = region_name; a->size = am->api_size ? am->api_size : (16 << 20); a->flags = SVM_FLAGS_MHEAP; a->uid = am->api_uid; diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/JVppRegistryImpl.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/JVppRegistryImpl.java index 98ef1c15..6e938ae3 100644 --- a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/JVppRegistryImpl.java +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/JVppRegistryImpl.java @@ -48,6 +48,13 @@ public final class JVppRegistryImpl implements JVppRegistry, ControlPingCallback pingCalls = new HashMap<>(); } + public JVppRegistryImpl(final String clientName, final String shmPrefix) throws IOException { + connection = new VppJNIConnection(clientName, shmPrefix); + connection.connect(); + pluginRegistry = new ConcurrentHashMap<>(); + pingCalls = new HashMap<>(); + } + @Override public VppConnection getConnection() { return connection; diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppJNIConnection.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppJNIConnection.java index 320c1283..53eaa790 100644 --- a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppJNIConnection.java +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppJNIConnection.java @@ -17,8 +17,11 @@ package io.fd.vpp.jvpp; import static io.fd.vpp.jvpp.NativeLibraryLoader.loadLibrary; +import static java.lang.String.format; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -30,13 +33,14 @@ import java.util.logging.Logger; */ public final class VppJNIConnection implements VppConnection { private static final Logger LOG = Logger.getLogger(VppJNIConnection.class.getName()); + private static final String DEFAULT_SHM_PREFIX = "/vpe-api"; static { final String libName = "libjvpp_registry.so"; try { loadLibrary(libName, VppJNIConnection.class); } catch (IOException e) { - LOG.log(Level.SEVERE, String.format("Can't find vpp jni library: %s", libName), e); + LOG.log(Level.SEVERE, format("Can't find vpp jni library: %s", libName), e); throw new ExceptionInInitializerError(e); } } @@ -44,6 +48,7 @@ public final class VppJNIConnection implements VppConnection { private ConnectionInfo connectionInfo; private final String clientName; + private final String shmPrefix; private volatile boolean disconnected = false; /** @@ -54,6 +59,12 @@ public final class VppJNIConnection implements VppConnection { */ public VppJNIConnection(final String clientName) { this.clientName = Objects.requireNonNull(clientName, "Null clientName"); + this.shmPrefix = DEFAULT_SHM_PREFIX; + } + + public VppJNIConnection(final String clientName, final String shmPrefix) { + this.clientName = Objects.requireNonNull(clientName, "Null clientName"); + this.shmPrefix = Objects.requireNonNull(shmPrefix, "Null shmPrefix"); } /** @@ -73,16 +84,18 @@ public final class VppJNIConnection implements VppConnection { @Override public void connect() throws IOException { - _connect(); + _connect(shmPrefix); } - private void _connect() throws IOException { + private void _connect(final String shmPrefix) throws IOException { + Objects.requireNonNull(shmPrefix, "Shared memory prefix must be defined"); + synchronized (VppJNIConnection.class) { if (connections.containsKey(clientName)) { throw new IOException("Client " + clientName + " already connected"); } - connectionInfo = clientConnect(clientName); + connectionInfo = clientConnect(shmPrefix, clientName); if (connectionInfo.status != 0) { throw new IOException("Connection returned error " + connectionInfo.status); } @@ -130,7 +143,7 @@ public final class VppJNIConnection implements VppConnection { } } - private static native ConnectionInfo clientConnect(String clientName); + private static native ConnectionInfo clientConnect(String shmPrefix, String clientName); private static native void clientDisconnect(); diff --git a/src/vpp-api/java/jvpp-registry/jvpp_registry.c b/src/vpp-api/java/jvpp-registry/jvpp_registry.c index 66adfea0..1e2c0176 100644 --- a/src/vpp-api/java/jvpp-registry/jvpp_registry.c +++ b/src/vpp-api/java/jvpp-registry/jvpp_registry.c @@ -243,14 +243,13 @@ static int send_initial_control_ping() { return rv; } -static int connect_to_vpe(char *name) { +static int connect_to_vpe(char *shm_prefix, char *name) { jvpp_main_t * jm = &jvpp_main; api_main_t * am = &api_main; jvpp_registry_main_t * rm = &jvpp_registry_main; - if (vl_client_connect_to_vlib("/vpe-api", name, 32) < 0) + if (vl_client_connect_to_vlib(shm_prefix, name, 32) < 0) return -1; - jm->my_client_index = am->my_client_index; jm->vl_input_queue = am->shmem_hdr->vl_input_queue; @@ -268,9 +267,15 @@ static int connect_to_vpe(char *name) { } JNIEXPORT jobject JNICALL Java_io_fd_vpp_jvpp_VppJNIConnection_clientConnect( - JNIEnv *env, jclass obj, jstring clientName) { + JNIEnv *env, jclass obj, jstring shmPrefix, jstring clientName) { + /* + * TODO introducing memory prefix as variable can be used in hc2vpp + * to be able to run without root privileges + * https://jira.fd.io/browse/HC2VPP-176 + */ int rv; const char *client_name; + const char *shm_prefix; void vl_msg_reply_handler_hookup(void); jvpp_main_t * jm = &jvpp_main; jvpp_registry_main_t * rm = &jvpp_registry_main; @@ -280,15 +285,6 @@ JNIEXPORT jobject JNICALL Java_io_fd_vpp_jvpp_VppJNIConnection_clientConnect( jmethodID connectionInfoConstructor = (*env)->GetMethodID(env, connectionInfoClass, "", "(JII)V"); - /* - * Bail out now if we're not running as root - */ - if (geteuid() != 0) { - return (*env)->NewObject(env, connectionInfoClass, - connectionInfoConstructor, 0, 0, - VNET_API_ERROR_NOT_RUNNING_AS_ROOT); - } - if (rm->is_connected) { return (*env)->NewObject(env, connectionInfoClass, connectionInfoConstructor, 0, 0, @@ -296,17 +292,25 @@ JNIEXPORT jobject JNICALL Java_io_fd_vpp_jvpp_VppJNIConnection_clientConnect( } client_name = (*env)->GetStringUTFChars(env, clientName, 0); + shm_prefix = (*env)->GetStringUTFChars(env, shmPrefix, 0); + if (!client_name) { return (*env)->NewObject(env, connectionInfoClass, - connectionInfoConstructor, 0, 0, VNET_API_ERROR_INVALID_VALUE); + connectionInfoConstructor, 0, 0, VNET_API_ERROR_INVALID_VALUE, shmPrefix); + } + + if (!shm_prefix) { + return (*env)->NewObject(env, connectionInfoClass, + connectionInfoConstructor, 0, 0, VNET_API_ERROR_INVALID_VALUE, shmPrefix); } - rv = connect_to_vpe((char *) client_name); + rv = connect_to_vpe((char *) shm_prefix, (char *) client_name); if (rv < 0) clib_warning("connection failed, rv %d", rv); (*env)->ReleaseStringUTFChars(env, clientName, client_name); + (*env)->ReleaseStringUTFChars(env, shmPrefix, shm_prefix); return (*env)->NewObject(env, connectionInfoClass, connectionInfoConstructor, (jlong) pointer_to_uword (jm->vl_input_queue), -- cgit 1.2.3-korg From 3214dc382342573c242782849d98c23009960633 Mon Sep 17 00:00:00 2001 From: Marek Gradzki Date: Thu, 10 Aug 2017 14:57:42 +0200 Subject: jvpp: ignore messages if callback method is missing (VPP-548) Change-Id: I6a06dbcd8339bd6645a6b02ae70154aa0885dcf8 Signed-off-by: Marek Gradzki --- src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/vpp-api/java') diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py index 1425cdb7..7e59cc4c 100644 --- a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py +++ b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py @@ -226,11 +226,19 @@ static void vl_api_${handler_name}_t_handler (vl_api_${handler_name}_t * mp) { ${plugin_name}_main_t *plugin_main = &${plugin_name}_main; JNIEnv *env = jvpp_main.jenv; + jthrowable exc; $err_handler jmethodID constructor = (*env)->GetMethodID(env, ${class_ref_name}Class, "", "()V"); jmethodID callbackMethod = (*env)->GetMethodID(env, plugin_main->callbackClass, "on${dto_name}", "(Lio/fd/vpp/jvpp/${plugin_name}/dto/${dto_name};)V"); + exc = (*env)->ExceptionOccurred(env); + if (exc) { + clib_warning("Unable to extract on${dto_name} method reference from ${plugin_name} plugin's callbackClass. Ignoring message.\\n"); + (*env)->ExceptionDescribe(env); + (*env)->ExceptionClear(env); + return; + } jobject dto = (*env)->NewObject(env, ${class_ref_name}Class, constructor); $dto_setters -- cgit 1.2.3-korg From 5da27c260e538188b0b934f55db98dc29ff4a24a Mon Sep 17 00:00:00 2001 From: Marek Gradzki Date: Tue, 15 Aug 2017 12:25:24 +0200 Subject: jvpp: move JVppReply's id out of synchronized block Should make Coverity stop thinking we try to synchronize reply.context. Change-Id: I97169e46b9c8f594836d6beb75b9f42dfc6e5bad Signed-off-by: Marek Gradzki --- .../jvpp/gen/jvppgen/jvpp_callback_facade_gen.py | 10 ++++++---- .../jvpp/gen/jvppgen/jvpp_future_facade_gen.py | 22 +++++++++++----------- 2 files changed, 17 insertions(+), 15 deletions(-) (limited to 'src/vpp-api/java') diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_callback_facade_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_callback_facade_gen.py index ac096a71..9aaa4c64 100644 --- a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_callback_facade_gen.py +++ b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_callback_facade_gen.py @@ -222,11 +222,12 @@ public final class CallbackJVpp${plugin_name}FacadeCallback implements $plugin_p @Override @SuppressWarnings("unchecked") - public void onControlPingReply($base_package.$dto_package.ControlPingReply reply) { + public void onControlPingReply(final $base_package.$dto_package.ControlPingReply reply) { $base_package.$callback_package.ControlPingCallback callback; + final int replyId = reply.context; synchronized(requests) { - callback = ($base_package.$callback_package.ControlPingCallback) requests.remove(reply.context); + callback = ($base_package.$callback_package.ControlPingCallback) requests.remove(replyId); } if(callback != null) { @@ -241,11 +242,12 @@ $methods jvpp_facade_callback_method_template = Template(""" @Override @SuppressWarnings("unchecked") - public void on$callback_dto($plugin_package.$dto_package.$callback_dto reply) { + public void on$callback_dto(final $plugin_package.$dto_package.$callback_dto reply) { $plugin_package.$callback_package.$callback callback; + final int replyId = reply.context; synchronized(requests) { - callback = ($plugin_package.$callback_package.$callback) requests.remove(reply.context); + callback = ($plugin_package.$callback_package.$callback) requests.remove(replyId); } if(callback != null) { diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_future_facade_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_future_facade_gen.py index 26b31e22..07947e30 100644 --- a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_future_facade_gen.py +++ b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_future_facade_gen.py @@ -59,11 +59,12 @@ public final class FutureJVpp${plugin_name}FacadeCallback implements $plugin_pac @Override @SuppressWarnings("unchecked") - public void onControlPingReply($base_package.$dto_package.ControlPingReply reply) { + public void onControlPingReply(final $base_package.$dto_package.ControlPingReply reply) { final java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply> completableFuture; + final int replyId = reply.context; synchronized(requests) { - completableFuture = (java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply>) requests.get(reply.context); + completableFuture = (java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply>) requests.get(replyId); } if(completableFuture != null) { @@ -77,9 +78,8 @@ public final class FutureJVpp${plugin_name}FacadeCallback implements $plugin_pac } else { completableFuture.complete(reply); } - synchronized(requests) { - requests.remove(reply.context); + requests.remove(replyId); } } } @@ -91,18 +91,18 @@ $methods jvpp_facade_callback_method_template = Template(""" @Override @SuppressWarnings("unchecked") - public void on$callback_dto($plugin_package.$dto_package.$callback_dto reply) { + public void on$callback_dto(final $plugin_package.$dto_package.$callback_dto reply) { final java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply> completableFuture; - + final int replyId = reply.context; synchronized(requests) { - completableFuture = (java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply>) requests.get(reply.context); + completableFuture = (java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply>) requests.get(replyId); } if(completableFuture != null) { completableFuture.complete(reply); synchronized(requests) { - requests.remove(reply.context); + requests.remove(replyId); } } } @@ -118,11 +118,11 @@ jvpp_facade_callback_notification_method_template = Template(""" jvpp_facade_details_callback_method_template = Template(""" @Override @SuppressWarnings("unchecked") - public void on$callback_dto($plugin_package.$dto_package.$callback_dto reply) { + public void on$callback_dto(final $plugin_package.$dto_package.$callback_dto reply) { final $base_package.$future_package.AbstractFutureJVppInvoker.CompletableDumpFuture<$plugin_package.$dto_package.$callback_dto_reply_dump> completableFuture; - + final int replyId = reply.context; synchronized(requests) { - completableFuture = ($base_package.$future_package.AbstractFutureJVppInvoker.CompletableDumpFuture<$plugin_package.$dto_package.$callback_dto_reply_dump>) requests.get(reply.context); + completableFuture = ($base_package.$future_package.AbstractFutureJVppInvoker.CompletableDumpFuture<$plugin_package.$dto_package.$callback_dto_reply_dump>) requests.get(replyId); } if(completableFuture != null) { -- cgit 1.2.3-korg From 89b931fdf9acfb8d78f11e973af69b46cdc7281d Mon Sep 17 00:00:00 2001 From: Marek Gradzki Date: Wed, 16 Aug 2017 15:12:40 +0200 Subject: jvpp: suppress unwritten fields warrning found in DTO's hashCode DTOs fields are initialized by generated JNI code, so we can safely ignore FB.UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD. Coverity uses FindBugs to analyse Java code, so it should be possible to suppress some of the issues that are false positives or intentional. Change-Id: I1375f6123e3eb44db44065d603d9d81726161acb Signed-off-by: Marek Gradzki --- .../fd/vpp/jvpp/coverity/SuppressFBWarnings.java | 40 ++++++++++++++++++++++ src/vpp-api/java/jvpp/gen/jvppgen/dto_gen.py | 2 ++ 2 files changed, 42 insertions(+) create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/coverity/SuppressFBWarnings.java (limited to 'src/vpp-api/java') diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/coverity/SuppressFBWarnings.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/coverity/SuppressFBWarnings.java new file mode 100644 index 00000000..1e780bb4 --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/coverity/SuppressFBWarnings.java @@ -0,0 +1,40 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.coverity; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Used to suppress FindBugs warnings found by Coverity.
+ * We don't want extra dependency, so we define our own annotation version. + * + * @see Findbugs sourceforge + */ +@Retention(RetentionPolicy.CLASS) +public @interface SuppressFBWarnings { + /** + * The set of FindBugs warnings that are to be suppressed in annotated element. The value can be a bug category, + * kind or pattern. + */ + String[] value() default {}; + + /** + * Optional documentation of the reason why the warning is suppressed + */ + String justification() default ""; +} diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/dto_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/dto_gen.py index cfddb9ef..e831557c 100644 --- a/src/vpp-api/java/jvpp/gen/jvppgen/dto_gen.py +++ b/src/vpp-api/java/jvpp/gen/jvppgen/dto_gen.py @@ -193,10 +193,12 @@ def generate_dto_equals(camel_case_dto_name, func): hash_template = Template(""" @Override + @io.fd.vpp.jvpp.coverity.SuppressFBWarnings("UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD") public int hashCode() { return java.util.Objects.hash($fields); }\n\n""") hash_single_array_type_template = Template(""" @Override + @io.fd.vpp.jvpp.coverity.SuppressFBWarnings("UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD") public int hashCode() { return java.util.Arrays.hashCode($fields); }\n\n""") -- cgit 1.2.3-korg From 2ba92e32e0197f676dd905e5edcb4ff3e1bec241 Mon Sep 17 00:00:00 2001 From: Matus Fabian Date: Mon, 21 Aug 2017 07:05:03 -0700 Subject: NAT: Rename snat plugin to nat (VPP-955) Change-Id: I30a7e3da7a4efc6038a91e27b48045d4b07e2764 Signed-off-by: Matus Fabian --- src/configure.ac | 2 +- src/examples/sample-plugin/sample_plugin_doc.md | 2 +- src/plugins/Makefile.am | 4 +- src/plugins/nat.am | 41 + src/plugins/nat/in2out.c | 3454 ++++++++++++++++++ src/plugins/nat/nat.api | 1513 ++++++++ src/plugins/nat/nat.c | 2842 +++++++++++++++ src/plugins/nat/nat.h | 541 +++ src/plugins/nat/nat64.c | 858 +++++ src/plugins/nat/nat64.h | 322 ++ src/plugins/nat/nat64_cli.c | 983 ++++++ src/plugins/nat/nat64_db.c | 603 ++++ src/plugins/nat/nat64_db.h | 307 ++ src/plugins/nat/nat64_doc.md | 73 + src/plugins/nat/nat64_in2out.c | 1118 ++++++ src/plugins/nat/nat64_out2in.c | 494 +++ src/plugins/nat/nat_all_api_h.h | 19 + src/plugins/nat/nat_api.c | 3255 +++++++++++++++++ src/plugins/nat/nat_det.c | 158 + src/plugins/nat/nat_det.h | 196 ++ src/plugins/nat/nat_ipfix_logging.c | 835 +++++ src/plugins/nat/nat_ipfix_logging.h | 79 + src/plugins/nat/nat_msg_enum.h | 31 + src/plugins/nat/nat_test.c | 1167 ++++++ src/plugins/nat/out2in.c | 2294 ++++++++++++ src/plugins/snat.am | 41 - src/plugins/snat/in2out.c | 3454 ------------------ src/plugins/snat/nat64.c | 858 ----- src/plugins/snat/nat64.h | 322 -- src/plugins/snat/nat64_cli.c | 983 ------ src/plugins/snat/nat64_db.c | 603 ---- src/plugins/snat/nat64_db.h | 307 -- src/plugins/snat/nat64_doc.md | 73 - src/plugins/snat/nat64_in2out.c | 1118 ------ src/plugins/snat/nat64_out2in.c | 494 --- src/plugins/snat/out2in.c | 2294 ------------ src/plugins/snat/snat.api | 897 ----- src/plugins/snat/snat.c | 2842 --------------- src/plugins/snat/snat.h | 541 --- src/plugins/snat/snat_all_api_h.h | 19 - src/plugins/snat/snat_api.c | 1970 ----------- src/plugins/snat/snat_det.c | 158 - src/plugins/snat/snat_det.h | 196 -- src/plugins/snat/snat_ipfix_logging.c | 835 ----- src/plugins/snat/snat_ipfix_logging.h | 79 - src/plugins/snat/snat_msg_enum.h | 31 - src/plugins/snat/snat_test.c | 1167 ------ src/scripts/vnet/nat44 | 41 + src/scripts/vnet/nat44_det | 108 + src/scripts/vnet/nat44_static | 44 + src/scripts/vnet/nat44_static_with_port | 44 + src/scripts/vnet/snat | 41 - src/scripts/vnet/snat_det | 108 - src/scripts/vnet/snat_static | 44 - src/scripts/vnet/snat_static_with_port | 44 - src/vpp-api/java/Makefile.am | 26 +- .../vpp/jvpp/nat/examples/CallbackApiExample.java | 68 + .../io/fd/vpp/jvpp/nat/examples/Readme.txt | 1 + src/vpp-api/java/jvpp-nat/jvpp_nat.c | 107 + src/vpp-api/java/jvpp-nat/jvpp_nat.h | 42 + .../vpp/jvpp/snat/examples/CallbackApiExample.java | 68 - .../io/fd/vpp/jvpp/snat/examples/Readme.txt | 1 - src/vpp-api/java/jvpp-snat/jvpp_snat.c | 107 - src/vpp-api/java/jvpp-snat/jvpp_snat.h | 42 - test/test_nat.py | 3720 ++++++++++++++++++++ test/test_snat.py | 3718 ------------------- test/vpp_papi_provider.py | 187 +- 67 files changed, 25462 insertions(+), 23572 deletions(-) create mode 100644 src/plugins/nat.am create mode 100644 src/plugins/nat/in2out.c create mode 100644 src/plugins/nat/nat.api create mode 100644 src/plugins/nat/nat.c create mode 100644 src/plugins/nat/nat.h create mode 100644 src/plugins/nat/nat64.c create mode 100644 src/plugins/nat/nat64.h create mode 100644 src/plugins/nat/nat64_cli.c create mode 100644 src/plugins/nat/nat64_db.c create mode 100644 src/plugins/nat/nat64_db.h create mode 100644 src/plugins/nat/nat64_doc.md create mode 100644 src/plugins/nat/nat64_in2out.c create mode 100644 src/plugins/nat/nat64_out2in.c create mode 100644 src/plugins/nat/nat_all_api_h.h create mode 100644 src/plugins/nat/nat_api.c create mode 100644 src/plugins/nat/nat_det.c create mode 100644 src/plugins/nat/nat_det.h create mode 100644 src/plugins/nat/nat_ipfix_logging.c create mode 100644 src/plugins/nat/nat_ipfix_logging.h create mode 100644 src/plugins/nat/nat_msg_enum.h create mode 100644 src/plugins/nat/nat_test.c create mode 100644 src/plugins/nat/out2in.c delete mode 100644 src/plugins/snat.am delete mode 100644 src/plugins/snat/in2out.c delete mode 100644 src/plugins/snat/nat64.c delete mode 100644 src/plugins/snat/nat64.h delete mode 100644 src/plugins/snat/nat64_cli.c delete mode 100644 src/plugins/snat/nat64_db.c delete mode 100644 src/plugins/snat/nat64_db.h delete mode 100644 src/plugins/snat/nat64_doc.md delete mode 100644 src/plugins/snat/nat64_in2out.c delete mode 100644 src/plugins/snat/nat64_out2in.c delete mode 100644 src/plugins/snat/out2in.c delete mode 100644 src/plugins/snat/snat.api delete mode 100644 src/plugins/snat/snat.c delete mode 100644 src/plugins/snat/snat.h delete mode 100644 src/plugins/snat/snat_all_api_h.h delete mode 100644 src/plugins/snat/snat_api.c delete mode 100644 src/plugins/snat/snat_det.c delete mode 100644 src/plugins/snat/snat_det.h delete mode 100644 src/plugins/snat/snat_ipfix_logging.c delete mode 100644 src/plugins/snat/snat_ipfix_logging.h delete mode 100644 src/plugins/snat/snat_msg_enum.h delete mode 100644 src/plugins/snat/snat_test.c create mode 100644 src/scripts/vnet/nat44 create mode 100644 src/scripts/vnet/nat44_det create mode 100644 src/scripts/vnet/nat44_static create mode 100644 src/scripts/vnet/nat44_static_with_port delete mode 100644 src/scripts/vnet/snat delete mode 100644 src/scripts/vnet/snat_det delete mode 100644 src/scripts/vnet/snat_static delete mode 100644 src/scripts/vnet/snat_static_with_port create mode 100644 src/vpp-api/java/jvpp-nat/io/fd/vpp/jvpp/nat/examples/CallbackApiExample.java create mode 100644 src/vpp-api/java/jvpp-nat/io/fd/vpp/jvpp/nat/examples/Readme.txt create mode 100644 src/vpp-api/java/jvpp-nat/jvpp_nat.c create mode 100644 src/vpp-api/java/jvpp-nat/jvpp_nat.h delete mode 100644 src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/examples/CallbackApiExample.java delete mode 100644 src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/examples/Readme.txt delete mode 100644 src/vpp-api/java/jvpp-snat/jvpp_snat.c delete mode 100644 src/vpp-api/java/jvpp-snat/jvpp_snat.h create mode 100644 test/test_nat.py delete mode 100644 test/test_snat.py (limited to 'src/vpp-api/java') diff --git a/src/configure.ac b/src/configure.ac index 7a038c2e..4c2d3b47 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -169,7 +169,7 @@ PLUGIN_ENABLED(lb) PLUGIN_ENABLED(memif) PLUGIN_ENABLED(pppoe) PLUGIN_ENABLED(sixrd) -PLUGIN_ENABLED(snat) +PLUGIN_ENABLED(nat) ############################################################################### # Dependency checks diff --git a/src/examples/sample-plugin/sample_plugin_doc.md b/src/examples/sample-plugin/sample_plugin_doc.md index 9348094c..501a8dca 100644 --- a/src/examples/sample-plugin/sample_plugin_doc.md +++ b/src/examples/sample-plugin/sample_plugin_doc.md @@ -29,7 +29,7 @@ Now run VPP and make sure the plugin is loaded. ... load_one_plugin:184: Loaded plugin: memif_plugin.so (Packet Memory Interface (experimetal)) load_one_plugin:184: Loaded plugin: sample_plugin.so (Sample of VPP Plugin) - load_one_plugin:184: Loaded plugin: snat_plugin.so (Network Address Translation) + load_one_plugin:184: Loaded plugin: nat_plugin.so (Network Address Translation) ... DBGvpp# diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index 8c7b3fac..205bfe6d 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -74,8 +74,8 @@ if ENABLE_SIXRD_PLUGIN include sixrd.am endif -if ENABLE_SNAT_PLUGIN -include snat.am +if ENABLE_NAT_PLUGIN +include nat.am endif include ../suffix-rules.mk diff --git a/src/plugins/nat.am b/src/plugins/nat.am new file mode 100644 index 00000000..b967a716 --- /dev/null +++ b/src/plugins/nat.am @@ -0,0 +1,41 @@ + +# Copyright (c) +# 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 += nat_test_plugin.la +vppplugins_LTLIBRARIES += nat_plugin.la + +nat_plugin_la_SOURCES = nat/nat.c \ + nat/nat_api.c \ + nat/in2out.c \ + nat/out2in.c \ + nat/nat_plugin.api.h \ + nat/nat_ipfix_logging.c \ + nat/nat_det.c \ + nat/nat64.c \ + nat/nat64_cli.c \ + nat/nat64_in2out.c \ + nat/nat64_out2in.c \ + nat/nat64_db.c + +API_FILES += nat/nat.api + +nobase_apiinclude_HEADERS += \ + nat/nat_all_api_h.h \ + nat/nat_msg_enum.h \ + nat/nat.api.h + +nat_test_plugin_la_SOURCES = \ + nat/nat_test.c nat/nat_plugin.api.h + +# vi:syntax=automake diff --git a/src/plugins/nat/in2out.c b/src/plugins/nat/in2out.c new file mode 100644 index 00000000..62a11170 --- /dev/null +++ b/src/plugins/nat/in2out.c @@ -0,0 +1,3454 @@ +/* + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +typedef struct { + u32 sw_if_index; + u32 next_index; + u32 session_index; + u32 is_slow_path; +} snat_in2out_trace_t; + +typedef struct { + u32 next_worker_index; + u8 do_handoff; +} snat_in2out_worker_handoff_trace_t; + +/* packet trace format function */ +static u8 * format_snat_in2out_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 *); + snat_in2out_trace_t * t = va_arg (*args, snat_in2out_trace_t *); + char * tag; + + tag = t->is_slow_path ? "NAT44_IN2OUT_SLOW_PATH" : "NAT44_IN2OUT_FAST_PATH"; + + s = format (s, "%s: sw_if_index %d, next index %d, session %d", tag, + t->sw_if_index, t->next_index, t->session_index); + + return s; +} + +static u8 * format_snat_in2out_fast_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 *); + snat_in2out_trace_t * t = va_arg (*args, snat_in2out_trace_t *); + + s = format (s, "NAT44_IN2OUT_FAST: sw_if_index %d, next index %d", + t->sw_if_index, t->next_index); + + return s; +} + +static u8 * format_snat_in2out_worker_handoff_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 *); + snat_in2out_worker_handoff_trace_t * t = + va_arg (*args, snat_in2out_worker_handoff_trace_t *); + char * m; + + m = t->do_handoff ? "next worker" : "same worker"; + s = format (s, "NAT44_IN2OUT_WORKER_HANDOFF: %s %d", m, t->next_worker_index); + + return s; +} + +vlib_node_registration_t snat_in2out_node; +vlib_node_registration_t snat_in2out_slowpath_node; +vlib_node_registration_t snat_in2out_fast_node; +vlib_node_registration_t snat_in2out_worker_handoff_node; +vlib_node_registration_t snat_det_in2out_node; +vlib_node_registration_t snat_in2out_output_node; +vlib_node_registration_t snat_in2out_output_slowpath_node; +vlib_node_registration_t snat_in2out_output_worker_handoff_node; +vlib_node_registration_t snat_hairpin_dst_node; +vlib_node_registration_t snat_hairpin_src_node; + + +#define foreach_snat_in2out_error \ +_(UNSUPPORTED_PROTOCOL, "Unsupported protocol") \ +_(IN2OUT_PACKETS, "Good in2out packets processed") \ +_(OUT_OF_PORTS, "Out of ports") \ +_(BAD_OUTSIDE_FIB, "Outside VRF ID not found") \ +_(BAD_ICMP_TYPE, "unsupported ICMP type") \ +_(NO_TRANSLATION, "No translation") + +typedef enum { +#define _(sym,str) SNAT_IN2OUT_ERROR_##sym, + foreach_snat_in2out_error +#undef _ + SNAT_IN2OUT_N_ERROR, +} snat_in2out_error_t; + +static char * snat_in2out_error_strings[] = { +#define _(sym,string) string, + foreach_snat_in2out_error +#undef _ +}; + +typedef enum { + SNAT_IN2OUT_NEXT_LOOKUP, + SNAT_IN2OUT_NEXT_DROP, + SNAT_IN2OUT_NEXT_ICMP_ERROR, + SNAT_IN2OUT_NEXT_SLOW_PATH, + SNAT_IN2OUT_N_NEXT, +} snat_in2out_next_t; + +typedef enum { + SNAT_HAIRPIN_SRC_NEXT_DROP, + SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT, + SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH, + SNAT_HAIRPIN_SRC_NEXT_INTERFACE_OUTPUT, + SNAT_HAIRPIN_SRC_N_NEXT, +} snat_hairpin_next_t; + +/** + * @brief Check if packet should be translated + * + * Packets aimed at outside interface and external addresss with active session + * should be translated. + * + * @param sm NAT main + * @param rt NAT runtime data + * @param sw_if_index0 index of the inside interface + * @param ip0 IPv4 header + * @param proto0 NAT protocol + * @param rx_fib_index0 RX FIB index + * + * @returns 0 if packet should be translated otherwise 1 + */ +static inline int +snat_not_translate_fast (snat_main_t * sm, vlib_node_runtime_t *node, + u32 sw_if_index0, ip4_header_t * ip0, u32 proto0, + u32 rx_fib_index0) +{ + fib_node_index_t fei = FIB_NODE_INDEX_INVALID; + fib_prefix_t pfx = { + .fp_proto = FIB_PROTOCOL_IP4, + .fp_len = 32, + .fp_addr = { + .ip4.as_u32 = ip0->dst_address.as_u32, + }, + }; + + /* Don't NAT packet aimed at the intfc address */ + if (PREDICT_FALSE(is_interface_addr(sm, node, sw_if_index0, + ip0->dst_address.as_u32))) + return 1; + + fei = fib_table_lookup (rx_fib_index0, &pfx); + if (FIB_NODE_INDEX_INVALID != fei) + { + u32 sw_if_index = fib_entry_get_resolving_interface (fei); + if (sw_if_index == ~0) + { + fei = fib_table_lookup (sm->outside_fib_index, &pfx); + if (FIB_NODE_INDEX_INVALID != fei) + sw_if_index = fib_entry_get_resolving_interface (fei); + } + snat_interface_t *i; + pool_foreach (i, sm->interfaces, + ({ + /* NAT packet aimed at outside interface */ + if ((i->is_inside == 0) && (sw_if_index == i->sw_if_index)) + return 0; + })); + } + + return 1; +} + +static inline int +snat_not_translate (snat_main_t * sm, vlib_node_runtime_t *node, + u32 sw_if_index0, ip4_header_t * ip0, u32 proto0, + u32 rx_fib_index0) +{ + udp_header_t * udp0 = ip4_next_header (ip0); + snat_session_key_t key0, sm0; + clib_bihash_kv_8_8_t kv0, value0; + + key0.addr = ip0->dst_address; + key0.port = udp0->dst_port; + key0.protocol = proto0; + key0.fib_index = sm->outside_fib_index; + kv0.key = key0.as_u64; + + /* NAT packet aimed at external address if */ + /* has active sessions */ + if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0)) + { + /* or is static mappings */ + if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0)) + return 0; + } + else + return 0; + + return snat_not_translate_fast(sm, node, sw_if_index0, ip0, proto0, + rx_fib_index0); +} + +static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0, + ip4_header_t * ip0, + u32 rx_fib_index0, + snat_session_key_t * key0, + snat_session_t ** sessionp, + vlib_node_runtime_t * node, + u32 next0, + u32 thread_index) +{ + snat_user_t *u; + snat_user_key_t user_key; + snat_session_t *s; + clib_bihash_kv_8_8_t kv0, value0; + u32 oldest_per_user_translation_list_index; + dlist_elt_t * oldest_per_user_translation_list_elt; + dlist_elt_t * per_user_translation_list_elt; + dlist_elt_t * per_user_list_head_elt; + u32 session_index; + snat_session_key_t key1; + u32 address_index = ~0; + u32 outside_fib_index; + uword * p; + snat_worker_key_t worker_by_out_key; + + p = hash_get (sm->ip4_main->fib_index_by_table_id, sm->outside_vrf_id); + if (! p) + { + b0->error = node->errors[SNAT_IN2OUT_ERROR_BAD_OUTSIDE_FIB]; + return SNAT_IN2OUT_NEXT_DROP; + } + outside_fib_index = p[0]; + + key1.protocol = key0->protocol; + user_key.addr = ip0->src_address; + user_key.fib_index = rx_fib_index0; + kv0.key = user_key.as_u64; + + /* Ever heard of the "user" = src ip4 address before? */ + if (clib_bihash_search_8_8 (&sm->user_hash, &kv0, &value0)) + { + /* no, make a new one */ + pool_get (sm->per_thread_data[thread_index].users, u); + memset (u, 0, sizeof (*u)); + u->addr = ip0->src_address; + u->fib_index = rx_fib_index0; + + pool_get (sm->per_thread_data[thread_index].list_pool, per_user_list_head_elt); + + u->sessions_per_user_list_head_index = per_user_list_head_elt - + sm->per_thread_data[thread_index].list_pool; + + clib_dlist_init (sm->per_thread_data[thread_index].list_pool, + u->sessions_per_user_list_head_index); + + kv0.value = u - sm->per_thread_data[thread_index].users; + + /* add user */ + clib_bihash_add_del_8_8 (&sm->user_hash, &kv0, 1 /* is_add */); + } + else + { + u = pool_elt_at_index (sm->per_thread_data[thread_index].users, + value0.value); + } + + /* Over quota? Recycle the least recently used dynamic translation */ + if (u->nsessions >= sm->max_translations_per_user) + { + /* Remove the oldest dynamic translation */ + do { + oldest_per_user_translation_list_index = + clib_dlist_remove_head (sm->per_thread_data[thread_index].list_pool, + u->sessions_per_user_list_head_index); + + ASSERT (oldest_per_user_translation_list_index != ~0); + + /* add it back to the end of the LRU list */ + clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool, + u->sessions_per_user_list_head_index, + oldest_per_user_translation_list_index); + /* Get the list element */ + oldest_per_user_translation_list_elt = + pool_elt_at_index (sm->per_thread_data[thread_index].list_pool, + oldest_per_user_translation_list_index); + + /* Get the session index from the list element */ + session_index = oldest_per_user_translation_list_elt->value; + + /* Get the session */ + s = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, + session_index); + } while (snat_is_session_static (s)); + + if (snat_is_unk_proto_session (s)) + { + clib_bihash_kv_16_8_t up_kv; + snat_unk_proto_ses_key_t key; + + /* Remove from lookup tables */ + key.l_addr = s->in2out.addr; + key.r_addr = s->ext_host_addr; + key.fib_index = s->in2out.fib_index; + key.proto = s->in2out.port; + up_kv.key[0] = key.as_u64[0]; + up_kv.key[1] = key.as_u64[1]; + if (clib_bihash_add_del_16_8 (&sm->in2out_unk_proto, &up_kv, 0)) + clib_warning ("in2out key del failed"); + + key.l_addr = s->out2in.addr; + key.fib_index = s->out2in.fib_index; + up_kv.key[0] = key.as_u64[0]; + up_kv.key[1] = key.as_u64[1]; + if (clib_bihash_add_del_16_8 (&sm->out2in_unk_proto, &up_kv, 0)) + clib_warning ("out2in key del failed"); + } + else + { + /* Remove in2out, out2in keys */ + kv0.key = s->in2out.as_u64; + if (clib_bihash_add_del_8_8 (&sm->in2out, &kv0, 0 /* is_add */)) + clib_warning ("in2out key delete failed"); + kv0.key = s->out2in.as_u64; + if (clib_bihash_add_del_8_8 (&sm->out2in, &kv0, 0 /* is_add */)) + clib_warning ("out2in key delete failed"); + + /* log NAT event */ + snat_ipfix_logging_nat44_ses_delete(s->in2out.addr.as_u32, + s->out2in.addr.as_u32, + s->in2out.protocol, + s->in2out.port, + s->out2in.port, + s->in2out.fib_index); + + snat_free_outside_address_and_port + (sm, &s->out2in, s->outside_address_index); + } + s->outside_address_index = ~0; + + if (snat_alloc_outside_address_and_port (sm, rx_fib_index0, thread_index, + &key1, &address_index)) + { + ASSERT(0); + + b0->error = node->errors[SNAT_IN2OUT_ERROR_OUT_OF_PORTS]; + return SNAT_IN2OUT_NEXT_DROP; + } + s->outside_address_index = address_index; + } + else + { + u8 static_mapping = 1; + + /* First try to match static mapping by local address and port */ + if (snat_static_mapping_match (sm, *key0, &key1, 0, 0)) + { + static_mapping = 0; + /* Try to create dynamic translation */ + if (snat_alloc_outside_address_and_port (sm, rx_fib_index0, + thread_index, &key1, + &address_index)) + { + b0->error = node->errors[SNAT_IN2OUT_ERROR_OUT_OF_PORTS]; + return SNAT_IN2OUT_NEXT_DROP; + } + } + + /* Create a new session */ + pool_get (sm->per_thread_data[thread_index].sessions, s); + memset (s, 0, sizeof (*s)); + + s->outside_address_index = address_index; + + if (static_mapping) + { + u->nstaticsessions++; + s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; + } + else + { + u->nsessions++; + } + + /* Create list elts */ + pool_get (sm->per_thread_data[thread_index].list_pool, + per_user_translation_list_elt); + clib_dlist_init (sm->per_thread_data[thread_index].list_pool, + per_user_translation_list_elt - + sm->per_thread_data[thread_index].list_pool); + + per_user_translation_list_elt->value = + s - sm->per_thread_data[thread_index].sessions; + s->per_user_index = per_user_translation_list_elt - + sm->per_thread_data[thread_index].list_pool; + s->per_user_list_head_index = u->sessions_per_user_list_head_index; + + clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool, + s->per_user_list_head_index, + per_user_translation_list_elt - + sm->per_thread_data[thread_index].list_pool); + } + + s->in2out = *key0; + s->out2in = key1; + s->out2in.protocol = key0->protocol; + s->out2in.fib_index = outside_fib_index; + s->ext_host_addr.as_u32 = ip0->dst_address.as_u32; + *sessionp = s; + + /* Add to translation hashes */ + kv0.key = s->in2out.as_u64; + kv0.value = s - sm->per_thread_data[thread_index].sessions; + if (clib_bihash_add_del_8_8 (&sm->in2out, &kv0, 1 /* is_add */)) + clib_warning ("in2out key add failed"); + + kv0.key = s->out2in.as_u64; + kv0.value = s - sm->per_thread_data[thread_index].sessions; + + if (clib_bihash_add_del_8_8 (&sm->out2in, &kv0, 1 /* is_add */)) + clib_warning ("out2in key add failed"); + + /* Add to translated packets worker lookup */ + worker_by_out_key.addr = s->out2in.addr; + worker_by_out_key.port = s->out2in.port; + worker_by_out_key.fib_index = s->out2in.fib_index; + kv0.key = worker_by_out_key.as_u64; + kv0.value = thread_index; + clib_bihash_add_del_8_8 (&sm->worker_by_out, &kv0, 1); + + /* log NAT event */ + snat_ipfix_logging_nat44_ses_create(s->in2out.addr.as_u32, + s->out2in.addr.as_u32, + s->in2out.protocol, + s->in2out.port, + s->out2in.port, + s->in2out.fib_index); + return next0; +} + +static_always_inline +snat_in2out_error_t icmp_get_key(ip4_header_t *ip0, + snat_session_key_t *p_key0) +{ + icmp46_header_t *icmp0; + snat_session_key_t key0; + icmp_echo_header_t *echo0, *inner_echo0 = 0; + ip4_header_t *inner_ip0 = 0; + void *l4_header = 0; + icmp46_header_t *inner_icmp0; + + icmp0 = (icmp46_header_t *) ip4_next_header (ip0); + echo0 = (icmp_echo_header_t *)(icmp0+1); + + if (!icmp_is_error_message (icmp0)) + { + key0.protocol = SNAT_PROTOCOL_ICMP; + key0.addr = ip0->src_address; + key0.port = echo0->identifier; + } + else + { + inner_ip0 = (ip4_header_t *)(echo0+1); + l4_header = ip4_next_header (inner_ip0); + key0.protocol = ip_proto_to_snat_proto (inner_ip0->protocol); + key0.addr = inner_ip0->dst_address; + switch (key0.protocol) + { + case SNAT_PROTOCOL_ICMP: + inner_icmp0 = (icmp46_header_t*)l4_header; + inner_echo0 = (icmp_echo_header_t *)(inner_icmp0+1); + key0.port = inner_echo0->identifier; + break; + case SNAT_PROTOCOL_UDP: + case SNAT_PROTOCOL_TCP: + key0.port = ((tcp_udp_header_t*)l4_header)->dst_port; + break; + default: + return SNAT_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL; + } + } + *p_key0 = key0; + return -1; /* success */ +} + +/** + * Get address and port values to be used for ICMP packet translation + * and create session if needed + * + * @param[in,out] sm NAT main + * @param[in,out] node NAT node runtime + * @param[in] thread_index thread index + * @param[in,out] b0 buffer containing packet to be translated + * @param[out] p_proto protocol used for matching + * @param[out] p_value address and port after NAT translation + * @param[out] p_dont_translate if packet should not be translated + * @param d optional parameter + * @param e optional parameter + */ +u32 icmp_match_in2out_slow(snat_main_t *sm, vlib_node_runtime_t *node, + u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + snat_session_key_t *p_value, + u8 *p_dont_translate, void *d, void *e) +{ + ip4_header_t *ip0; + icmp46_header_t *icmp0; + u32 sw_if_index0; + u32 rx_fib_index0; + snat_session_key_t key0; + snat_session_t *s0 = 0; + u8 dont_translate = 0; + clib_bihash_kv_8_8_t kv0, value0; + u32 next0 = ~0; + int err; + u32 iph_offset0 = 0; + + if (PREDICT_FALSE(vnet_buffer(b0)->sw_if_index[VLIB_TX] != ~0)) + { + iph_offset0 = vnet_buffer (b0)->ip.save_rewrite_length; + } + ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0) + iph_offset0); + icmp0 = (icmp46_header_t *) ip4_next_header (ip0); + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; + rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0); + + err = icmp_get_key (ip0, &key0); + if (err != -1) + { + b0->error = node->errors[err]; + next0 = SNAT_IN2OUT_NEXT_DROP; + goto out; + } + key0.fib_index = rx_fib_index0; + + kv0.key = key0.as_u64; + + if (clib_bihash_search_8_8 (&sm->in2out, &kv0, &value0)) + { + if (PREDICT_FALSE(snat_not_translate(sm, node, sw_if_index0, ip0, + IP_PROTOCOL_ICMP, rx_fib_index0) && + vnet_buffer(b0)->sw_if_index[VLIB_TX] == ~0)) + { + dont_translate = 1; + goto out; + } + + if (PREDICT_FALSE(icmp_is_error_message (icmp0))) + { + b0->error = node->errors[SNAT_IN2OUT_ERROR_BAD_ICMP_TYPE]; + next0 = SNAT_IN2OUT_NEXT_DROP; + goto out; + } + + next0 = slow_path (sm, b0, ip0, rx_fib_index0, &key0, + &s0, node, next0, thread_index); + + if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP)) + goto out; + } + else + { + if (PREDICT_FALSE(icmp0->type != ICMP4_echo_request && + icmp0->type != ICMP4_echo_reply && + !icmp_is_error_message (icmp0))) + { + b0->error = node->errors[SNAT_IN2OUT_ERROR_BAD_ICMP_TYPE]; + next0 = SNAT_IN2OUT_NEXT_DROP; + goto out; + } + + s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, + value0.value); + } + +out: + *p_proto = key0.protocol; + if (s0) + *p_value = s0->out2in; + *p_dont_translate = dont_translate; + if (d) + *(snat_session_t**)d = s0; + return next0; +} + +/** + * Get address and port values to be used for ICMP packet translation + * + * @param[in] sm NAT main + * @param[in,out] node NAT node runtime + * @param[in] thread_index thread index + * @param[in,out] b0 buffer containing packet to be translated + * @param[out] p_proto protocol used for matching + * @param[out] p_value address and port after NAT translation + * @param[out] p_dont_translate if packet should not be translated + * @param d optional parameter + * @param e optional parameter + */ +u32 icmp_match_in2out_fast(snat_main_t *sm, vlib_node_runtime_t *node, + u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + snat_session_key_t *p_value, + u8 *p_dont_translate, void *d, void *e) +{ + ip4_header_t *ip0; + icmp46_header_t *icmp0; + u32 sw_if_index0; + u32 rx_fib_index0; + snat_session_key_t key0; + snat_session_key_t sm0; + u8 dont_translate = 0; + u8 is_addr_only; + u32 next0 = ~0; + int err; + + ip0 = vlib_buffer_get_current (b0); + icmp0 = (icmp46_header_t *) ip4_next_header (ip0); + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; + rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0); + + err = icmp_get_key (ip0, &key0); + if (err != -1) + { + b0->error = node->errors[err]; + next0 = SNAT_IN2OUT_NEXT_DROP; + goto out2; + } + key0.fib_index = rx_fib_index0; + + if (snat_static_mapping_match(sm, key0, &sm0, 0, &is_addr_only)) + { + if (PREDICT_FALSE(snat_not_translate_fast(sm, node, sw_if_index0, ip0, + IP_PROTOCOL_ICMP, rx_fib_index0))) + { + dont_translate = 1; + goto out; + } + + if (icmp_is_error_message (icmp0)) + { + next0 = SNAT_IN2OUT_NEXT_DROP; + goto out; + } + + b0->error = node->errors[SNAT_IN2OUT_ERROR_NO_TRANSLATION]; + next0 = SNAT_IN2OUT_NEXT_DROP; + goto out; + } + + if (PREDICT_FALSE(icmp0->type != ICMP4_echo_request && + (icmp0->type != ICMP4_echo_reply || !is_addr_only) && + !icmp_is_error_message (icmp0))) + { + b0->error = node->errors[SNAT_IN2OUT_ERROR_BAD_ICMP_TYPE]; + next0 = SNAT_IN2OUT_NEXT_DROP; + goto out; + } + +out: + *p_value = sm0; +out2: + *p_proto = key0.protocol; + *p_dont_translate = dont_translate; + return next0; +} + +static inline u32 icmp_in2out (snat_main_t *sm, + vlib_buffer_t * b0, + ip4_header_t * ip0, + icmp46_header_t * icmp0, + u32 sw_if_index0, + u32 rx_fib_index0, + vlib_node_runtime_t * node, + u32 next0, + u32 thread_index, + void *d, + void *e) +{ + snat_session_key_t sm0; + u8 protocol; + icmp_echo_header_t *echo0, *inner_echo0 = 0; + ip4_header_t *inner_ip0; + void *l4_header = 0; + icmp46_header_t *inner_icmp0; + u8 dont_translate; + u32 new_addr0, old_addr0; + u16 old_id0, new_id0; + ip_csum_t sum0; + u16 checksum0; + u32 next0_tmp; + + echo0 = (icmp_echo_header_t *)(icmp0+1); + + next0_tmp = sm->icmp_match_in2out_cb(sm, node, thread_index, b0, + &protocol, &sm0, &dont_translate, d, e); + if (next0_tmp != ~0) + next0 = next0_tmp; + if (next0 == SNAT_IN2OUT_NEXT_DROP || dont_translate) + goto out; + + sum0 = ip_incremental_checksum (0, icmp0, + ntohs(ip0->length) - ip4_header_bytes (ip0)); + checksum0 = ~ip_csum_fold (sum0); + if (PREDICT_FALSE(checksum0 != 0 && checksum0 != 0xffff)) + { + next0 = SNAT_IN2OUT_NEXT_DROP; + goto out; + } + + old_addr0 = ip0->src_address.as_u32; + new_addr0 = ip0->src_address.as_u32 = sm0.addr.as_u32; + if (vnet_buffer(b0)->sw_if_index[VLIB_TX] == ~0) + vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index; + + sum0 = ip0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, + src_address /* changed member */); + ip0->checksum = ip_csum_fold (sum0); + + if (!icmp_is_error_message (icmp0)) + { + new_id0 = sm0.port; + if (PREDICT_FALSE(new_id0 != echo0->identifier)) + { + old_id0 = echo0->identifier; + new_id0 = sm0.port; + echo0->identifier = new_id0; + + sum0 = icmp0->checksum; + sum0 = ip_csum_update (sum0, old_id0, new_id0, icmp_echo_header_t, + identifier); + icmp0->checksum = ip_csum_fold (sum0); + } + } + else + { + inner_ip0 = (ip4_header_t *)(echo0+1); + l4_header = ip4_next_header (inner_ip0); + + if (!ip4_header_checksum_is_valid (inner_ip0)) + { + next0 = SNAT_IN2OUT_NEXT_DROP; + goto out; + } + + old_addr0 = inner_ip0->dst_address.as_u32; + inner_ip0->dst_address = sm0.addr; + new_addr0 = inner_ip0->dst_address.as_u32; + + sum0 = icmp0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, + dst_address /* changed member */); + icmp0->checksum = ip_csum_fold (sum0); + + switch (protocol) + { + case SNAT_PROTOCOL_ICMP: + inner_icmp0 = (icmp46_header_t*)l4_header; + inner_echo0 = (icmp_echo_header_t *)(inner_icmp0+1); + + old_id0 = inner_echo0->identifier; + new_id0 = sm0.port; + inner_echo0->identifier = new_id0; + + sum0 = icmp0->checksum; + sum0 = ip_csum_update (sum0, old_id0, new_id0, icmp_echo_header_t, + identifier); + icmp0->checksum = ip_csum_fold (sum0); + break; + case SNAT_PROTOCOL_UDP: + case SNAT_PROTOCOL_TCP: + old_id0 = ((tcp_udp_header_t*)l4_header)->dst_port; + new_id0 = sm0.port; + ((tcp_udp_header_t*)l4_header)->dst_port = new_id0; + + sum0 = icmp0->checksum; + sum0 = ip_csum_update (sum0, old_id0, new_id0, tcp_udp_header_t, + dst_port); + icmp0->checksum = ip_csum_fold (sum0); + break; + default: + ASSERT(0); + } + } + +out: + return next0; +} + +/** + * @brief Hairpinning + * + * Hairpinning allows two endpoints on the internal side of the NAT to + * communicate even if they only use each other's external IP addresses + * and ports. + * + * @param sm NAT main. + * @param b0 Vlib buffer. + * @param ip0 IP header. + * @param udp0 UDP header. + * @param tcp0 TCP header. + * @param proto0 NAT protocol. + */ +static inline void +snat_hairpinning (snat_main_t *sm, + vlib_buffer_t * b0, + ip4_header_t * ip0, + udp_header_t * udp0, + tcp_header_t * tcp0, + u32 proto0) +{ + snat_session_key_t key0, sm0; + snat_worker_key_t k0; + snat_session_t * s0; + clib_bihash_kv_8_8_t kv0, value0; + ip_csum_t sum0; + u32 new_dst_addr0 = 0, old_dst_addr0, ti = 0, si; + u16 new_dst_port0, old_dst_port0; + + key0.addr = ip0->dst_address; + key0.port = udp0->dst_port; + key0.protocol = proto0; + key0.fib_index = sm->outside_fib_index; + kv0.key = key0.as_u64; + + /* Check if destination is in active sessions */ + if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0)) + { + /* or static mappings */ + if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0)) + { + new_dst_addr0 = sm0.addr.as_u32; + new_dst_port0 = sm0.port; + vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index; + } + } + else + { + si = value0.value; + if (sm->num_workers > 1) + { + k0.addr = ip0->dst_address; + k0.port = udp0->dst_port; + k0.fib_index = sm->outside_fib_index; + kv0.key = k0.as_u64; + if (clib_bihash_search_8_8 (&sm->worker_by_out, &kv0, &value0)) + ASSERT(0); + else + ti = value0.value; + } + else + ti = sm->num_workers; + + s0 = pool_elt_at_index (sm->per_thread_data[ti].sessions, si); + new_dst_addr0 = s0->in2out.addr.as_u32; + new_dst_port0 = s0->in2out.port; + vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index; + } + + /* Destination is behind the same NAT, use internal address and port */ + if (new_dst_addr0) + { + old_dst_addr0 = ip0->dst_address.as_u32; + ip0->dst_address.as_u32 = new_dst_addr0; + sum0 = ip0->checksum; + sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0, + ip4_header_t, dst_address); + ip0->checksum = ip_csum_fold (sum0); + + old_dst_port0 = tcp0->dst; + if (PREDICT_TRUE(new_dst_port0 != old_dst_port0)) + { + if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) + { + tcp0->dst = new_dst_port0; + sum0 = tcp0->checksum; + sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0, + ip4_header_t, dst_address); + sum0 = ip_csum_update (sum0, old_dst_port0, new_dst_port0, + ip4_header_t /* cheat */, length); + tcp0->checksum = ip_csum_fold(sum0); + } + else + { + udp0->dst_port = new_dst_port0; + udp0->checksum = 0; + } + } + else + { + if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) + { + sum0 = tcp0->checksum; + sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0, + ip4_header_t, dst_address); + tcp0->checksum = ip_csum_fold(sum0); + } + } + } +} + +static inline void +snat_icmp_hairpinning (snat_main_t *sm, + vlib_buffer_t * b0, + ip4_header_t * ip0, + icmp46_header_t * icmp0) +{ + snat_session_key_t key0, sm0; + clib_bihash_kv_8_8_t kv0, value0; + snat_worker_key_t k0; + u32 new_dst_addr0 = 0, old_dst_addr0, si, ti = 0; + ip_csum_t sum0; + snat_session_t *s0; + + if (!icmp_is_error_message (icmp0)) + { + icmp_echo_header_t *echo0 = (icmp_echo_header_t *)(icmp0+1); + u16 icmp_id0 = echo0->identifier; + key0.addr = ip0->dst_address; + key0.port = icmp_id0; + key0.protocol = SNAT_PROTOCOL_ICMP; + key0.fib_index = sm->outside_fib_index; + kv0.key = key0.as_u64; + + /* Check if destination is in active sessions */ + if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0)) + { + /* or static mappings */ + if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0)) + { + new_dst_addr0 = sm0.addr.as_u32; + vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index; + } + } + else + { + si = value0.value; + if (sm->num_workers > 1) + { + k0.addr = ip0->dst_address; + k0.port = icmp_id0; + k0.fib_index = sm->outside_fib_index; + kv0.key = k0.as_u64; + if (clib_bihash_search_8_8 (&sm->worker_by_out, &kv0, &value0)) + ASSERT(0); + else + ti = value0.value; + } + else + ti = sm->num_workers; + + s0 = pool_elt_at_index (sm->per_thread_data[ti].sessions, si); + new_dst_addr0 = s0->in2out.addr.as_u32; + vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index; + echo0->identifier = s0->in2out.port; + sum0 = icmp0->checksum; + sum0 = ip_csum_update (sum0, icmp_id0, s0->in2out.port, + icmp_echo_header_t, identifier); + icmp0->checksum = ip_csum_fold (sum0); + } + + /* Destination is behind the same NAT, use internal address and port */ + if (new_dst_addr0) + { + old_dst_addr0 = ip0->dst_address.as_u32; + ip0->dst_address.as_u32 = new_dst_addr0; + sum0 = ip0->checksum; + sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0, + ip4_header_t, dst_address); + ip0->checksum = ip_csum_fold (sum0); + } + } + +} + +static inline u32 icmp_in2out_slow_path (snat_main_t *sm, + vlib_buffer_t * b0, + ip4_header_t * ip0, + icmp46_header_t * icmp0, + u32 sw_if_index0, + u32 rx_fib_index0, + vlib_node_runtime_t * node, + u32 next0, + f64 now, + u32 thread_index, + snat_session_t ** p_s0) +{ + next0 = icmp_in2out(sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, + next0, thread_index, p_s0, 0); + snat_session_t * s0 = *p_s0; + if (PREDICT_TRUE(next0 != SNAT_IN2OUT_NEXT_DROP && s0)) + { + /* Hairpinning */ + if (vnet_buffer(b0)->sw_if_index[VLIB_TX] == 0) + snat_icmp_hairpinning(sm, b0, ip0, icmp0); + /* Accounting */ + s0->last_heard = now; + s0->total_pkts++; + s0->total_bytes += vlib_buffer_length_in_chain (sm->vlib_main, b0); + /* Per-user LRU list maintenance for dynamic translations */ + if (!snat_is_session_static (s0)) + { + clib_dlist_remove (sm->per_thread_data[thread_index].list_pool, + s0->per_user_index); + clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool, + s0->per_user_list_head_index, + s0->per_user_index); + } + } + return next0; +} +static inline void +snat_hairpinning_unknown_proto (snat_main_t *sm, + vlib_buffer_t * b, + ip4_header_t * ip) +{ + u32 old_addr, new_addr = 0, ti = 0; + clib_bihash_kv_8_8_t kv, value; + clib_bihash_kv_16_8_t s_kv, s_value; + snat_unk_proto_ses_key_t key; + snat_session_key_t m_key; + snat_worker_key_t w_key; + snat_static_mapping_t *m; + ip_csum_t sum; + snat_session_t *s; + + old_addr = ip->dst_address.as_u32; + key.l_addr.as_u32 = ip->dst_address.as_u32; + key.r_addr.as_u32 = ip->src_address.as_u32; + key.fib_index = sm->outside_fib_index; + key.proto = ip->protocol; + key.rsvd[0] = key.rsvd[1] = key.rsvd[2] = 0; + s_kv.key[0] = key.as_u64[0]; + s_kv.key[1] = key.as_u64[1]; + if (clib_bihash_search_16_8 (&sm->out2in_unk_proto, &s_kv, &s_value)) + { + m_key.addr = ip->dst_address; + m_key.fib_index = sm->outside_fib_index; + m_key.port = 0; + m_key.protocol = 0; + kv.key = m_key.as_u64; + if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) + return; + + m = pool_elt_at_index (sm->static_mappings, value.value); + if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0) + vnet_buffer(b)->sw_if_index[VLIB_TX] = m->fib_index; + new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32; + } + else + { + if (sm->num_workers > 1) + { + w_key.addr = ip->dst_address; + w_key.port = 0; + w_key.fib_index = sm->outside_fib_index; + kv.key = w_key.as_u64; + if (clib_bihash_search_8_8 (&sm->worker_by_out, &kv, &value)) + return; + else + ti = value.value; + } + else + ti = sm->num_workers; + + s = pool_elt_at_index (sm->per_thread_data[ti].sessions, s_value.value); + if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0) + vnet_buffer(b)->sw_if_index[VLIB_TX] = s->in2out.fib_index; + new_addr = ip->dst_address.as_u32 = s->in2out.addr.as_u32; + } + sum = ip->checksum; + sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address); + ip->checksum = ip_csum_fold (sum); +} + +static void +snat_in2out_unknown_proto (snat_main_t *sm, + vlib_buffer_t * b, + ip4_header_t * ip, + u32 rx_fib_index, + u32 thread_index, + f64 now, + vlib_main_t * vm) +{ + clib_bihash_kv_8_8_t kv, value; + clib_bihash_kv_16_8_t s_kv, s_value; + snat_static_mapping_t *m; + snat_session_key_t m_key; + u32 old_addr, new_addr = 0; + ip_csum_t sum; + snat_user_key_t u_key; + snat_user_t *u; + dlist_elt_t *head, *elt, *oldest; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + u32 elt_index, head_index, ses_index, oldest_index; + snat_session_t * s; + snat_unk_proto_ses_key_t key; + u32 address_index = ~0; + int i; + u8 is_sm = 0; + + old_addr = ip->src_address.as_u32; + + key.l_addr = ip->src_address; + key.r_addr = ip->dst_address; + key.fib_index = rx_fib_index; + key.proto = ip->protocol; + key.rsvd[0] = key.rsvd[1] = key.rsvd[2] = 0; + s_kv.key[0] = key.as_u64[0]; + s_kv.key[1] = key.as_u64[1]; + + if (!clib_bihash_search_16_8 (&sm->in2out_unk_proto, &s_kv, &s_value)) + { + s = pool_elt_at_index (tsm->sessions, s_value.value); + new_addr = ip->src_address.as_u32 = s->out2in.addr.as_u32; + } + else + { + u_key.addr = ip->src_address; + u_key.fib_index = rx_fib_index; + kv.key = u_key.as_u64; + + /* Ever heard of the "user" = src ip4 address before? */ + if (clib_bihash_search_8_8 (&sm->user_hash, &kv, &value)) + { + /* no, make a new one */ + pool_get (tsm->users, u); + memset (u, 0, sizeof (*u)); + u->addr = ip->src_address; + u->fib_index = rx_fib_index; + + pool_get (tsm->list_pool, head); + u->sessions_per_user_list_head_index = head - tsm->list_pool; + + clib_dlist_init (tsm->list_pool, + u->sessions_per_user_list_head_index); + + kv.value = u - tsm->users; + + /* add user */ + clib_bihash_add_del_8_8 (&sm->user_hash, &kv, 1); + } + else + { + u = pool_elt_at_index (tsm->users, value.value); + } + + m_key.addr = ip->src_address; + m_key.port = 0; + m_key.protocol = 0; + m_key.fib_index = rx_fib_index; + kv.key = m_key.as_u64; + + /* Try to find static mapping first */ + if (!clib_bihash_search_8_8 (&sm->static_mapping_by_local, &kv, &value)) + { + m = pool_elt_at_index (sm->static_mappings, value.value); + new_addr = ip->src_address.as_u32 = m->external_addr.as_u32; + is_sm = 1; + goto create_ses; + } + /* Fallback to 3-tuple key */ + else + { + /* Choose same out address as for TCP/UDP session to same destination */ + if (!clib_bihash_search_8_8 (&sm->user_hash, &kv, &value)) + { + head_index = u->sessions_per_user_list_head_index; + head = pool_elt_at_index (tsm->list_pool, head_index); + elt_index = head->next; + elt = pool_elt_at_index (tsm->list_pool, elt_index); + ses_index = elt->value; + while (ses_index != ~0) + { + s = pool_elt_at_index (tsm->sessions, ses_index); + elt_index = elt->next; + elt = pool_elt_at_index (tsm->list_pool, elt_index); + ses_index = elt->value; + + if (s->ext_host_addr.as_u32 == ip->dst_address.as_u32) + { + new_addr = ip->src_address.as_u32 = s->out2in.addr.as_u32; + address_index = s->outside_address_index; + + key.fib_index = sm->outside_fib_index; + key.l_addr.as_u32 = new_addr; + s_kv.key[0] = key.as_u64[0]; + s_kv.key[1] = key.as_u64[1]; + if (clib_bihash_search_16_8 (&sm->out2in_unk_proto, &s_kv, &s_value)) + break; + + goto create_ses; + } + } + } + key.fib_index = sm->outside_fib_index; + for (i = 0; i < vec_len (sm->addresses); i++) + { + key.l_addr.as_u32 = sm->addresses[i].addr.as_u32; + s_kv.key[0] = key.as_u64[0]; + s_kv.key[1] = key.as_u64[1]; + if (clib_bihash_search_16_8 (&sm->out2in_unk_proto, &s_kv, &s_value)) + { + new_addr = ip->src_address.as_u32 = key.l_addr.as_u32; + address_index = i; + goto create_ses; + } + } + return; + } + +create_ses: + /* Over quota? Recycle the least recently used dynamic translation */ + if (u->nsessions >= sm->max_translations_per_user && !is_sm) + { + /* Remove the oldest dynamic translation */ + do { + oldest_index = clib_dlist_remove_head ( + tsm->list_pool, u->sessions_per_user_list_head_index); + + ASSERT (oldest_index != ~0); + + /* add it back to the end of the LRU list */ + clib_dlist_addtail (tsm->list_pool, + u->sessions_per_user_list_head_index, + oldest_index); + /* Get the list element */ + oldest = pool_elt_at_index (tsm->list_pool, oldest_index); + + /* Get the session index from the list element */ + ses_index = oldest->value; + + /* Get the session */ + s = pool_elt_at_index (tsm->sessions, ses_index); + } while (snat_is_session_static (s)); + + if (snat_is_unk_proto_session (s)) + { + /* Remove from lookup tables */ + key.l_addr = s->in2out.addr; + key.r_addr = s->ext_host_addr; + key.fib_index = s->in2out.fib_index; + key.proto = s->in2out.port; + s_kv.key[0] = key.as_u64[0]; + s_kv.key[1] = key.as_u64[1]; + if (clib_bihash_add_del_16_8 (&sm->in2out_unk_proto, &s_kv, 0)) + clib_warning ("in2out key del failed"); + + key.l_addr = s->out2in.addr; + key.fib_index = s->out2in.fib_index; + s_kv.key[0] = key.as_u64[0]; + s_kv.key[1] = key.as_u64[1]; + if (clib_bihash_add_del_16_8 (&sm->out2in_unk_proto, &s_kv, 0)) + clib_warning ("out2in key del failed"); + } + else + { + /* log NAT event */ + snat_ipfix_logging_nat44_ses_delete(s->in2out.addr.as_u32, + s->out2in.addr.as_u32, + s->in2out.protocol, + s->in2out.port, + s->out2in.port, + s->in2out.fib_index); + + snat_free_outside_address_and_port (sm, &s->out2in, + s->outside_address_index); + + /* Remove in2out, out2in keys */ + kv.key = s->in2out.as_u64; + if (clib_bihash_add_del_8_8 (&sm->in2out, &kv, 0)) + clib_warning ("in2out key del failed"); + kv.key = s->out2in.as_u64; + if (clib_bihash_add_del_8_8 (&sm->out2in, &kv, 0)) + clib_warning ("out2in key del failed"); + } + } + else + { + /* Create a new session */ + pool_get (tsm->sessions, s); + memset (s, 0, sizeof (*s)); + + /* Create list elts */ + pool_get (tsm->list_pool, elt); + clib_dlist_init (tsm->list_pool, elt - tsm->list_pool); + elt->value = s - tsm->sessions; + s->per_user_index = elt - tsm->list_pool; + s->per_user_list_head_index = u->sessions_per_user_list_head_index; + clib_dlist_addtail (tsm->list_pool, s->per_user_list_head_index, + s->per_user_index); + } + + s->ext_host_addr.as_u32 = ip->dst_address.as_u32; + s->flags |= SNAT_SESSION_FLAG_UNKNOWN_PROTO; + s->outside_address_index = address_index; + s->out2in.addr.as_u32 = new_addr; + s->out2in.fib_index = sm->outside_fib_index; + s->in2out.addr.as_u32 = old_addr; + s->in2out.fib_index = rx_fib_index; + s->in2out.port = s->out2in.port = ip->protocol; + if (is_sm) + { + u->nstaticsessions++; + s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; + } + else + { + u->nsessions++; + } + + /* Add to lookup tables */ + key.l_addr.as_u32 = old_addr; + key.r_addr = ip->dst_address; + key.proto = ip->protocol; + key.fib_index = rx_fib_index; + s_kv.key[0] = key.as_u64[0]; + s_kv.key[1] = key.as_u64[1]; + s_kv.value = s - tsm->sessions; + if (clib_bihash_add_del_16_8 (&sm->in2out_unk_proto, &s_kv, 1)) + clib_warning ("in2out key add failed"); + + key.l_addr.as_u32 = new_addr; + key.fib_index = sm->outside_fib_index; + s_kv.key[0] = key.as_u64[0]; + s_kv.key[1] = key.as_u64[1]; + if (clib_bihash_add_del_16_8 (&sm->out2in_unk_proto, &s_kv, 1)) + clib_warning ("out2in key add failed"); + } + + /* Update IP checksum */ + sum = ip->checksum; + sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, src_address); + ip->checksum = ip_csum_fold (sum); + + /* Accounting */ + s->last_heard = now; + s->total_pkts++; + s->total_bytes += vlib_buffer_length_in_chain (vm, b); + /* Per-user LRU list maintenance */ + clib_dlist_remove (tsm->list_pool, s->per_user_index); + clib_dlist_addtail (tsm->list_pool, s->per_user_list_head_index, + s->per_user_index); + + /* Hairpinning */ + if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0) + snat_hairpinning_unknown_proto(sm, b, ip); + + if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0) + vnet_buffer(b)->sw_if_index[VLIB_TX] = sm->outside_fib_index; +} + +static inline uword +snat_in2out_node_fn_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, int is_slow_path, + int is_output_feature) +{ + u32 n_left_from, * from, * to_next; + snat_in2out_next_t next_index; + u32 pkts_processed = 0; + snat_main_t * sm = &snat_main; + f64 now = vlib_time_now (vm); + u32 stats_node_index; + u32 thread_index = vlib_get_thread_index (); + + stats_node_index = is_slow_path ? snat_in2out_slowpath_node.index : + snat_in2out_node.index; + + 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 >= 4 && n_left_to_next >= 2) + { + u32 bi0, bi1; + vlib_buffer_t * b0, * b1; + u32 next0, next1; + u32 sw_if_index0, sw_if_index1; + ip4_header_t * ip0, * ip1; + ip_csum_t sum0, sum1; + u32 new_addr0, old_addr0, new_addr1, old_addr1; + u16 old_port0, new_port0, old_port1, new_port1; + udp_header_t * udp0, * udp1; + tcp_header_t * tcp0, * tcp1; + icmp46_header_t * icmp0, * icmp1; + snat_session_key_t key0, key1; + u32 rx_fib_index0, rx_fib_index1; + u32 proto0, proto1; + snat_session_t * s0 = 0, * s1 = 0; + clib_bihash_kv_8_8_t kv0, value0, kv1, value1; + u32 iph_offset0 = 0, iph_offset1 = 0; + + /* Prefetch next iteration. */ + { + vlib_buffer_t * p2, * p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header (p2, LOAD); + vlib_prefetch_buffer_header (p3, LOAD); + + CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE); + CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE); + } + + /* speculatively enqueue b0 and b1 to the current next frame */ + to_next[0] = bi0 = from[0]; + to_next[1] = bi1 = from[1]; + from += 2; + to_next += 2; + n_left_from -= 2; + n_left_to_next -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + if (is_output_feature) + iph_offset0 = vnet_buffer (b0)->ip.save_rewrite_length; + + ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0) + + iph_offset0); + + udp0 = ip4_next_header (ip0); + tcp0 = (tcp_header_t *) udp0; + icmp0 = (icmp46_header_t *) udp0; + + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; + rx_fib_index0 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index, + sw_if_index0); + + next0 = next1 = SNAT_IN2OUT_NEXT_LOOKUP; + + if (PREDICT_FALSE(ip0->ttl == 1)) + { + vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, + ICMP4_time_exceeded_ttl_exceeded_in_transit, + 0); + next0 = SNAT_IN2OUT_NEXT_ICMP_ERROR; + goto trace00; + } + + proto0 = ip_proto_to_snat_proto (ip0->protocol); + + /* Next configured feature, probably ip4-lookup */ + if (is_slow_path) + { + if (PREDICT_FALSE (proto0 == ~0)) + { + snat_in2out_unknown_proto (sm, b0, ip0, rx_fib_index0, + thread_index, now, vm); + goto trace00; + } + + if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP)) + { + next0 = icmp_in2out_slow_path + (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, + node, next0, now, thread_index, &s0); + goto trace00; + } + } + else + { + if (PREDICT_FALSE (proto0 == ~0 || proto0 == SNAT_PROTOCOL_ICMP)) + { + next0 = SNAT_IN2OUT_NEXT_SLOW_PATH; + goto trace00; + } + } + + key0.addr = ip0->src_address; + key0.port = udp0->src_port; + key0.protocol = proto0; + key0.fib_index = rx_fib_index0; + + kv0.key = key0.as_u64; + + if (PREDICT_FALSE (clib_bihash_search_8_8 (&sm->in2out, &kv0, &value0) != 0)) + { + if (is_slow_path) + { + if (PREDICT_FALSE(snat_not_translate(sm, node, sw_if_index0, + ip0, proto0, rx_fib_index0)) && !is_output_feature) + goto trace00; + + next0 = slow_path (sm, b0, ip0, rx_fib_index0, &key0, + &s0, node, next0, thread_index); + if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP)) + goto trace00; + } + else + { + next0 = SNAT_IN2OUT_NEXT_SLOW_PATH; + goto trace00; + } + } + else + s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, + value0.value); + + old_addr0 = ip0->src_address.as_u32; + ip0->src_address = s0->out2in.addr; + new_addr0 = ip0->src_address.as_u32; + if (!is_output_feature) + vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->out2in.fib_index; + + sum0 = ip0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, + ip4_header_t, + src_address /* changed member */); + ip0->checksum = ip_csum_fold (sum0); + + if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) + { + old_port0 = tcp0->src_port; + tcp0->src_port = s0->out2in.port; + new_port0 = tcp0->src_port; + + sum0 = tcp0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, + ip4_header_t, + dst_address /* changed member */); + sum0 = ip_csum_update (sum0, old_port0, new_port0, + ip4_header_t /* cheat */, + length /* changed member */); + tcp0->checksum = ip_csum_fold(sum0); + } + else + { + old_port0 = udp0->src_port; + udp0->src_port = s0->out2in.port; + udp0->checksum = 0; + } + + /* Hairpinning */ + if (!is_output_feature) + snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0); + + /* Accounting */ + s0->last_heard = now; + s0->total_pkts++; + s0->total_bytes += vlib_buffer_length_in_chain (vm, b0); + /* Per-user LRU list maintenance for dynamic translation */ + if (!snat_is_session_static (s0)) + { + clib_dlist_remove (sm->per_thread_data[thread_index].list_pool, + s0->per_user_index); + clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool, + s0->per_user_list_head_index, + s0->per_user_index); + } + trace00: + + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + snat_in2out_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->is_slow_path = is_slow_path; + t->sw_if_index = sw_if_index0; + t->next_index = next0; + t->session_index = ~0; + if (s0) + t->session_index = s0 - sm->per_thread_data[thread_index].sessions; + } + + pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP; + + if (is_output_feature) + iph_offset1 = vnet_buffer (b1)->ip.save_rewrite_length; + + ip1 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b1) + + iph_offset1); + + udp1 = ip4_next_header (ip1); + tcp1 = (tcp_header_t *) udp1; + icmp1 = (icmp46_header_t *) udp1; + + sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX]; + rx_fib_index1 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index, + sw_if_index1); + + if (PREDICT_FALSE(ip1->ttl == 1)) + { + vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b1, ICMP4_time_exceeded, + ICMP4_time_exceeded_ttl_exceeded_in_transit, + 0); + next1 = SNAT_IN2OUT_NEXT_ICMP_ERROR; + goto trace01; + } + + proto1 = ip_proto_to_snat_proto (ip1->protocol); + + /* Next configured feature, probably ip4-lookup */ + if (is_slow_path) + { + if (PREDICT_FALSE (proto1 == ~0)) + { + snat_in2out_unknown_proto (sm, b1, ip1, rx_fib_index1, + thread_index, now, vm); + goto trace01; + } + + if (PREDICT_FALSE (proto1 == SNAT_PROTOCOL_ICMP)) + { + next1 = icmp_in2out_slow_path + (sm, b1, ip1, icmp1, sw_if_index1, rx_fib_index1, node, + next1, now, thread_index, &s1); + goto trace01; + } + } + else + { + if (PREDICT_FALSE (proto1 == ~0 || proto1 == SNAT_PROTOCOL_ICMP)) + { + next1 = SNAT_IN2OUT_NEXT_SLOW_PATH; + goto trace01; + } + } + + key1.addr = ip1->src_address; + key1.port = udp1->src_port; + key1.protocol = proto1; + key1.fib_index = rx_fib_index1; + + kv1.key = key1.as_u64; + + if (PREDICT_FALSE(clib_bihash_search_8_8 (&sm->in2out, &kv1, &value1) != 0)) + { + if (is_slow_path) + { + if (PREDICT_FALSE(snat_not_translate(sm, node, sw_if_index1, + ip1, proto1, rx_fib_index1)) && !is_output_feature) + goto trace01; + + next1 = slow_path (sm, b1, ip1, rx_fib_index1, &key1, + &s1, node, next1, thread_index); + if (PREDICT_FALSE (next1 == SNAT_IN2OUT_NEXT_DROP)) + goto trace01; + } + else + { + next1 = SNAT_IN2OUT_NEXT_SLOW_PATH; + goto trace01; + } + } + else + s1 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, + value1.value); + + old_addr1 = ip1->src_address.as_u32; + ip1->src_address = s1->out2in.addr; + new_addr1 = ip1->src_address.as_u32; + if (!is_output_feature) + vnet_buffer(b1)->sw_if_index[VLIB_TX] = s1->out2in.fib_index; + + sum1 = ip1->checksum; + sum1 = ip_csum_update (sum1, old_addr1, new_addr1, + ip4_header_t, + src_address /* changed member */); + ip1->checksum = ip_csum_fold (sum1); + + if (PREDICT_TRUE(proto1 == SNAT_PROTOCOL_TCP)) + { + old_port1 = tcp1->src_port; + tcp1->src_port = s1->out2in.port; + new_port1 = tcp1->src_port; + + sum1 = tcp1->checksum; + sum1 = ip_csum_update (sum1, old_addr1, new_addr1, + ip4_header_t, + dst_address /* changed member */); + sum1 = ip_csum_update (sum1, old_port1, new_port1, + ip4_header_t /* cheat */, + length /* changed member */); + tcp1->checksum = ip_csum_fold(sum1); + } + else + { + old_port1 = udp1->src_port; + udp1->src_port = s1->out2in.port; + udp1->checksum = 0; + } + + /* Hairpinning */ + if (!is_output_feature) + snat_hairpinning (sm, b1, ip1, udp1, tcp1, proto1); + + /* Accounting */ + s1->last_heard = now; + s1->total_pkts++; + s1->total_bytes += vlib_buffer_length_in_chain (vm, b1); + /* Per-user LRU list maintenance for dynamic translation */ + if (!snat_is_session_static (s1)) + { + clib_dlist_remove (sm->per_thread_data[thread_index].list_pool, + s1->per_user_index); + clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool, + s1->per_user_list_head_index, + s1->per_user_index); + } + trace01: + + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b1->flags & VLIB_BUFFER_IS_TRACED))) + { + snat_in2out_trace_t *t = + vlib_add_trace (vm, node, b1, sizeof (*t)); + t->sw_if_index = sw_if_index1; + t->next_index = next1; + t->session_index = ~0; + if (s1) + t->session_index = s1 - sm->per_thread_data[thread_index].sessions; + } + + pkts_processed += next1 != SNAT_IN2OUT_NEXT_DROP; + + /* verify speculative enqueues, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, next0, next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t * b0; + u32 next0; + u32 sw_if_index0; + ip4_header_t * ip0; + ip_csum_t sum0; + u32 new_addr0, old_addr0; + u16 old_port0, new_port0; + udp_header_t * udp0; + tcp_header_t * tcp0; + icmp46_header_t * icmp0; + snat_session_key_t key0; + u32 rx_fib_index0; + u32 proto0; + snat_session_t * s0 = 0; + clib_bihash_kv_8_8_t kv0, value0; + u32 iph_offset0 = 0; + + /* speculatively enqueue b0 to the current next frame */ + 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); + next0 = SNAT_IN2OUT_NEXT_LOOKUP; + + if (is_output_feature) + iph_offset0 = vnet_buffer (b0)->ip.save_rewrite_length; + + ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0) + + iph_offset0); + + udp0 = ip4_next_header (ip0); + tcp0 = (tcp_header_t *) udp0; + icmp0 = (icmp46_header_t *) udp0; + + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; + rx_fib_index0 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index, + sw_if_index0); + + if (PREDICT_FALSE(ip0->ttl == 1)) + { + vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, + ICMP4_time_exceeded_ttl_exceeded_in_transit, + 0); + next0 = SNAT_IN2OUT_NEXT_ICMP_ERROR; + goto trace0; + } + + proto0 = ip_proto_to_snat_proto (ip0->protocol); + + /* Next configured feature, probably ip4-lookup */ + if (is_slow_path) + { + if (PREDICT_FALSE (proto0 == ~0)) + { + snat_in2out_unknown_proto (sm, b0, ip0, rx_fib_index0, + thread_index, now, vm); + goto trace0; + } + + if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP)) + { + next0 = icmp_in2out_slow_path + (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, + next0, now, thread_index, &s0); + goto trace0; + } + } + else + { + if (PREDICT_FALSE (proto0 == ~0 || proto0 == SNAT_PROTOCOL_ICMP)) + { + next0 = SNAT_IN2OUT_NEXT_SLOW_PATH; + goto trace0; + } + } + + key0.addr = ip0->src_address; + key0.port = udp0->src_port; + key0.protocol = proto0; + key0.fib_index = rx_fib_index0; + + kv0.key = key0.as_u64; + + if (clib_bihash_search_8_8 (&sm->in2out, &kv0, &value0)) + { + if (is_slow_path) + { + if (PREDICT_FALSE(snat_not_translate(sm, node, sw_if_index0, + ip0, proto0, rx_fib_index0)) && !is_output_feature) + goto trace0; + + next0 = slow_path (sm, b0, ip0, rx_fib_index0, &key0, + &s0, node, next0, thread_index); + + if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP)) + goto trace0; + } + else + { + next0 = SNAT_IN2OUT_NEXT_SLOW_PATH; + goto trace0; + } + } + else + s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, + value0.value); + + old_addr0 = ip0->src_address.as_u32; + ip0->src_address = s0->out2in.addr; + new_addr0 = ip0->src_address.as_u32; + if (!is_output_feature) + vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->out2in.fib_index; + + sum0 = ip0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, + ip4_header_t, + src_address /* changed member */); + ip0->checksum = ip_csum_fold (sum0); + + if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) + { + old_port0 = tcp0->src_port; + tcp0->src_port = s0->out2in.port; + new_port0 = tcp0->src_port; + + sum0 = tcp0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, + ip4_header_t, + dst_address /* changed member */); + sum0 = ip_csum_update (sum0, old_port0, new_port0, + ip4_header_t /* cheat */, + length /* changed member */); + tcp0->checksum = ip_csum_fold(sum0); + } + else + { + old_port0 = udp0->src_port; + udp0->src_port = s0->out2in.port; + udp0->checksum = 0; + } + + /* Hairpinning */ + if (!is_output_feature) + snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0); + + /* Accounting */ + s0->last_heard = now; + s0->total_pkts++; + s0->total_bytes += vlib_buffer_length_in_chain (vm, b0); + /* Per-user LRU list maintenance for dynamic translation */ + if (!snat_is_session_static (s0)) + { + clib_dlist_remove (sm->per_thread_data[thread_index].list_pool, + s0->per_user_index); + clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool, + s0->per_user_list_head_index, + s0->per_user_index); + } + + trace0: + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + snat_in2out_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->is_slow_path = is_slow_path; + t->sw_if_index = sw_if_index0; + t->next_index = next0; + t->session_index = ~0; + if (s0) + t->session_index = s0 - sm->per_thread_data[thread_index].sessions; + } + + pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP; + + /* verify speculative enqueue, maybe switch current next frame */ + 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, stats_node_index, + SNAT_IN2OUT_ERROR_IN2OUT_PACKETS, + pkts_processed); + return frame->n_vectors; +} + +static uword +snat_in2out_fast_path_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return snat_in2out_node_fn_inline (vm, node, frame, 0 /* is_slow_path */, 0); +} + +VLIB_REGISTER_NODE (snat_in2out_node) = { + .function = snat_in2out_fast_path_fn, + .name = "nat44-in2out", + .vector_size = sizeof (u32), + .format_trace = format_snat_in2out_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(snat_in2out_error_strings), + .error_strings = snat_in2out_error_strings, + + .runtime_data_bytes = sizeof (snat_runtime_t), + + .n_next_nodes = SNAT_IN2OUT_N_NEXT, + + /* edit / add dispositions here */ + .next_nodes = { + [SNAT_IN2OUT_NEXT_DROP] = "error-drop", + [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup", + [SNAT_IN2OUT_NEXT_SLOW_PATH] = "nat44-in2out-slowpath", + [SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_node, snat_in2out_fast_path_fn); + +static uword +snat_in2out_output_fast_path_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return snat_in2out_node_fn_inline (vm, node, frame, 0 /* is_slow_path */, 1); +} + +VLIB_REGISTER_NODE (snat_in2out_output_node) = { + .function = snat_in2out_output_fast_path_fn, + .name = "nat44-in2out-output", + .vector_size = sizeof (u32), + .format_trace = format_snat_in2out_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(snat_in2out_error_strings), + .error_strings = snat_in2out_error_strings, + + .runtime_data_bytes = sizeof (snat_runtime_t), + + .n_next_nodes = SNAT_IN2OUT_N_NEXT, + + /* edit / add dispositions here */ + .next_nodes = { + [SNAT_IN2OUT_NEXT_DROP] = "error-drop", + [SNAT_IN2OUT_NEXT_LOOKUP] = "interface-output", + [SNAT_IN2OUT_NEXT_SLOW_PATH] = "nat44-in2out-output-slowpath", + [SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_output_node, + snat_in2out_output_fast_path_fn); + +static uword +snat_in2out_slow_path_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return snat_in2out_node_fn_inline (vm, node, frame, 1 /* is_slow_path */, 0); +} + +VLIB_REGISTER_NODE (snat_in2out_slowpath_node) = { + .function = snat_in2out_slow_path_fn, + .name = "nat44-in2out-slowpath", + .vector_size = sizeof (u32), + .format_trace = format_snat_in2out_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(snat_in2out_error_strings), + .error_strings = snat_in2out_error_strings, + + .runtime_data_bytes = sizeof (snat_runtime_t), + + .n_next_nodes = SNAT_IN2OUT_N_NEXT, + + /* edit / add dispositions here */ + .next_nodes = { + [SNAT_IN2OUT_NEXT_DROP] = "error-drop", + [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup", + [SNAT_IN2OUT_NEXT_SLOW_PATH] = "nat44-in2out-slowpath", + [SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_slowpath_node, + snat_in2out_slow_path_fn); + +static uword +snat_in2out_output_slow_path_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return snat_in2out_node_fn_inline (vm, node, frame, 1 /* is_slow_path */, 1); +} + +VLIB_REGISTER_NODE (snat_in2out_output_slowpath_node) = { + .function = snat_in2out_output_slow_path_fn, + .name = "nat44-in2out-output-slowpath", + .vector_size = sizeof (u32), + .format_trace = format_snat_in2out_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(snat_in2out_error_strings), + .error_strings = snat_in2out_error_strings, + + .runtime_data_bytes = sizeof (snat_runtime_t), + + .n_next_nodes = SNAT_IN2OUT_N_NEXT, + + /* edit / add dispositions here */ + .next_nodes = { + [SNAT_IN2OUT_NEXT_DROP] = "error-drop", + [SNAT_IN2OUT_NEXT_LOOKUP] = "interface-output", + [SNAT_IN2OUT_NEXT_SLOW_PATH] = "nat44-in2out-output-slowpath", + [SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_output_slowpath_node, + snat_in2out_output_slow_path_fn); + +/**************************/ +/*** deterministic mode ***/ +/**************************/ +static uword +snat_det_in2out_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, * from, * to_next; + snat_in2out_next_t next_index; + u32 pkts_processed = 0; + snat_main_t * sm = &snat_main; + u32 now = (u32) vlib_time_now (vm); + u32 thread_index = vlib_get_thread_index (); + + 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 >= 4 && n_left_to_next >= 2) + { + u32 bi0, bi1; + vlib_buffer_t * b0, * b1; + u32 next0, next1; + u32 sw_if_index0, sw_if_index1; + ip4_header_t * ip0, * ip1; + ip_csum_t sum0, sum1; + ip4_address_t new_addr0, old_addr0, new_addr1, old_addr1; + u16 old_port0, new_port0, lo_port0, i0; + u16 old_port1, new_port1, lo_port1, i1; + udp_header_t * udp0, * udp1; + tcp_header_t * tcp0, * tcp1; + u32 proto0, proto1; + snat_det_out_key_t key0, key1; + snat_det_map_t * dm0, * dm1; + snat_det_session_t * ses0 = 0, * ses1 = 0; + u32 rx_fib_index0, rx_fib_index1; + icmp46_header_t * icmp0, * icmp1; + + /* Prefetch next iteration. */ + { + vlib_buffer_t * p2, * p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header (p2, LOAD); + vlib_prefetch_buffer_header (p3, LOAD); + + CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE); + CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE); + } + + /* speculatively enqueue b0 and b1 to the current next frame */ + to_next[0] = bi0 = from[0]; + to_next[1] = bi1 = from[1]; + from += 2; + to_next += 2; + n_left_from -= 2; + n_left_to_next -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + next0 = SNAT_IN2OUT_NEXT_LOOKUP; + next1 = SNAT_IN2OUT_NEXT_LOOKUP; + + ip0 = vlib_buffer_get_current (b0); + udp0 = ip4_next_header (ip0); + tcp0 = (tcp_header_t *) udp0; + + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; + + if (PREDICT_FALSE(ip0->ttl == 1)) + { + vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, + ICMP4_time_exceeded_ttl_exceeded_in_transit, + 0); + next0 = SNAT_IN2OUT_NEXT_ICMP_ERROR; + goto trace0; + } + + proto0 = ip_proto_to_snat_proto (ip0->protocol); + + if (PREDICT_FALSE(proto0 == SNAT_PROTOCOL_ICMP)) + { + rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0); + icmp0 = (icmp46_header_t *) udp0; + + next0 = icmp_in2out(sm, b0, ip0, icmp0, sw_if_index0, + rx_fib_index0, node, next0, thread_index, + &ses0, &dm0); + goto trace0; + } + + dm0 = snat_det_map_by_user(sm, &ip0->src_address); + if (PREDICT_FALSE(!dm0)) + { + clib_warning("no match for internal host %U", + format_ip4_address, &ip0->src_address); + next0 = SNAT_IN2OUT_NEXT_DROP; + b0->error = node->errors[SNAT_IN2OUT_ERROR_NO_TRANSLATION]; + goto trace0; + } + + snat_det_forward(dm0, &ip0->src_address, &new_addr0, &lo_port0); + + key0.ext_host_addr = ip0->dst_address; + key0.ext_host_port = tcp0->dst; + + ses0 = snat_det_find_ses_by_in(dm0, &ip0->src_address, tcp0->src, key0); + if (PREDICT_FALSE(!ses0)) + { + for (i0 = 0; i0 < dm0->ports_per_host; i0++) + { + key0.out_port = clib_host_to_net_u16 (lo_port0 + + ((i0 + clib_net_to_host_u16 (tcp0->src)) % dm0->ports_per_host)); + + if (snat_det_get_ses_by_out (dm0, &ip0->src_address, key0.as_u64)) + continue; + + ses0 = snat_det_ses_create(dm0, &ip0->src_address, tcp0->src, &key0); + break; + } + if (PREDICT_FALSE(!ses0)) + { + /* too many sessions for user, send ICMP error packet */ + + vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b0, ICMP4_destination_unreachable, + ICMP4_destination_unreachable_destination_unreachable_host, + 0); + next0 = SNAT_IN2OUT_NEXT_ICMP_ERROR; + goto trace0; + } + } + + new_port0 = ses0->out.out_port; + + old_addr0.as_u32 = ip0->src_address.as_u32; + ip0->src_address.as_u32 = new_addr0.as_u32; + vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm->outside_fib_index; + + sum0 = ip0->checksum; + sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32, + ip4_header_t, + src_address /* changed member */); + ip0->checksum = ip_csum_fold (sum0); + + if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) + { + if (tcp0->flags & TCP_FLAG_SYN) + ses0->state = SNAT_SESSION_TCP_SYN_SENT; + else if (tcp0->flags & TCP_FLAG_ACK && ses0->state == SNAT_SESSION_TCP_SYN_SENT) + ses0->state = SNAT_SESSION_TCP_ESTABLISHED; + else if (tcp0->flags & TCP_FLAG_FIN && ses0->state == SNAT_SESSION_TCP_ESTABLISHED) + ses0->state = SNAT_SESSION_TCP_FIN_WAIT; + else if (tcp0->flags & TCP_FLAG_ACK && ses0->state == SNAT_SESSION_TCP_FIN_WAIT) + snat_det_ses_close(dm0, ses0); + else if (tcp0->flags & TCP_FLAG_FIN && ses0->state == SNAT_SESSION_TCP_CLOSE_WAIT) + ses0->state = SNAT_SESSION_TCP_LAST_ACK; + else if (tcp0->flags == 0 && ses0->state == SNAT_SESSION_UNKNOWN) + ses0->state = SNAT_SESSION_TCP_ESTABLISHED; + + old_port0 = tcp0->src; + tcp0->src = new_port0; + + sum0 = tcp0->checksum; + sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32, + ip4_header_t, + dst_address /* changed member */); + sum0 = ip_csum_update (sum0, old_port0, new_port0, + ip4_header_t /* cheat */, + length /* changed member */); + tcp0->checksum = ip_csum_fold(sum0); + } + else + { + ses0->state = SNAT_SESSION_UDP_ACTIVE; + old_port0 = udp0->src_port; + udp0->src_port = new_port0; + udp0->checksum = 0; + } + + switch(ses0->state) + { + case SNAT_SESSION_UDP_ACTIVE: + ses0->expire = now + sm->udp_timeout; + break; + case SNAT_SESSION_TCP_SYN_SENT: + case SNAT_SESSION_TCP_FIN_WAIT: + case SNAT_SESSION_TCP_CLOSE_WAIT: + case SNAT_SESSION_TCP_LAST_ACK: + ses0->expire = now + sm->tcp_transitory_timeout; + break; + case SNAT_SESSION_TCP_ESTABLISHED: + ses0->expire = now + sm->tcp_established_timeout; + break; + } + + trace0: + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + snat_in2out_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->is_slow_path = 0; + t->sw_if_index = sw_if_index0; + t->next_index = next0; + t->session_index = ~0; + if (ses0) + t->session_index = ses0 - dm0->sessions; + } + + pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP; + + ip1 = vlib_buffer_get_current (b1); + udp1 = ip4_next_header (ip1); + tcp1 = (tcp_header_t *) udp1; + + sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX]; + + if (PREDICT_FALSE(ip1->ttl == 1)) + { + vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b1, ICMP4_time_exceeded, + ICMP4_time_exceeded_ttl_exceeded_in_transit, + 0); + next1 = SNAT_IN2OUT_NEXT_ICMP_ERROR; + goto trace1; + } + + proto1 = ip_proto_to_snat_proto (ip1->protocol); + + if (PREDICT_FALSE(proto1 == SNAT_PROTOCOL_ICMP)) + { + rx_fib_index1 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index1); + icmp1 = (icmp46_header_t *) udp1; + + next1 = icmp_in2out(sm, b1, ip1, icmp1, sw_if_index1, + rx_fib_index1, node, next1, thread_index, + &ses1, &dm1); + goto trace1; + } + + dm1 = snat_det_map_by_user(sm, &ip1->src_address); + if (PREDICT_FALSE(!dm1)) + { + clib_warning("no match for internal host %U", + format_ip4_address, &ip0->src_address); + next1 = SNAT_IN2OUT_NEXT_DROP; + b1->error = node->errors[SNAT_IN2OUT_ERROR_NO_TRANSLATION]; + goto trace1; + } + + snat_det_forward(dm1, &ip1->src_address, &new_addr1, &lo_port1); + + key1.ext_host_addr = ip1->dst_address; + key1.ext_host_port = tcp1->dst; + + ses1 = snat_det_find_ses_by_in(dm1, &ip1->src_address, tcp1->src, key1); + if (PREDICT_FALSE(!ses1)) + { + for (i1 = 0; i1 < dm1->ports_per_host; i1++) + { + key1.out_port = clib_host_to_net_u16 (lo_port1 + + ((i1 + clib_net_to_host_u16 (tcp1->src)) % dm1->ports_per_host)); + + if (snat_det_get_ses_by_out (dm1, &ip1->src_address, key1.as_u64)) + continue; + + ses1 = snat_det_ses_create(dm1, &ip1->src_address, tcp1->src, &key1); + break; + } + if (PREDICT_FALSE(!ses1)) + { + /* too many sessions for user, send ICMP error packet */ + + vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b1, ICMP4_destination_unreachable, + ICMP4_destination_unreachable_destination_unreachable_host, + 0); + next1 = SNAT_IN2OUT_NEXT_ICMP_ERROR; + goto trace1; + } + } + + new_port1 = ses1->out.out_port; + + old_addr1.as_u32 = ip1->src_address.as_u32; + ip1->src_address.as_u32 = new_addr1.as_u32; + vnet_buffer(b1)->sw_if_index[VLIB_TX] = sm->outside_fib_index; + + sum1 = ip1->checksum; + sum1 = ip_csum_update (sum1, old_addr1.as_u32, new_addr1.as_u32, + ip4_header_t, + src_address /* changed member */); + ip1->checksum = ip_csum_fold (sum1); + + if (PREDICT_TRUE(proto1 == SNAT_PROTOCOL_TCP)) + { + if (tcp1->flags & TCP_FLAG_SYN) + ses1->state = SNAT_SESSION_TCP_SYN_SENT; + else if (tcp1->flags & TCP_FLAG_ACK && ses1->state == SNAT_SESSION_TCP_SYN_SENT) + ses1->state = SNAT_SESSION_TCP_ESTABLISHED; + else if (tcp1->flags & TCP_FLAG_FIN && ses1->state == SNAT_SESSION_TCP_ESTABLISHED) + ses1->state = SNAT_SESSION_TCP_FIN_WAIT; + else if (tcp1->flags & TCP_FLAG_ACK && ses1->state == SNAT_SESSION_TCP_FIN_WAIT) + snat_det_ses_close(dm1, ses1); + else if (tcp1->flags & TCP_FLAG_FIN && ses1->state == SNAT_SESSION_TCP_CLOSE_WAIT) + ses1->state = SNAT_SESSION_TCP_LAST_ACK; + else if (tcp1->flags == 0 && ses1->state == SNAT_SESSION_UNKNOWN) + ses1->state = SNAT_SESSION_TCP_ESTABLISHED; + + old_port1 = tcp1->src; + tcp1->src = new_port1; + + sum1 = tcp1->checksum; + sum1 = ip_csum_update (sum1, old_addr1.as_u32, new_addr1.as_u32, + ip4_header_t, + dst_address /* changed member */); + sum1 = ip_csum_update (sum1, old_port1, new_port1, + ip4_header_t /* cheat */, + length /* changed member */); + tcp1->checksum = ip_csum_fold(sum1); + } + else + { + ses1->state = SNAT_SESSION_UDP_ACTIVE; + old_port1 = udp1->src_port; + udp1->src_port = new_port1; + udp1->checksum = 0; + } + + switch(ses1->state) + { + case SNAT_SESSION_UDP_ACTIVE: + ses1->expire = now + sm->udp_timeout; + break; + case SNAT_SESSION_TCP_SYN_SENT: + case SNAT_SESSION_TCP_FIN_WAIT: + case SNAT_SESSION_TCP_CLOSE_WAIT: + case SNAT_SESSION_TCP_LAST_ACK: + ses1->expire = now + sm->tcp_transitory_timeout; + break; + case SNAT_SESSION_TCP_ESTABLISHED: + ses1->expire = now + sm->tcp_established_timeout; + break; + } + + trace1: + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b1->flags & VLIB_BUFFER_IS_TRACED))) + { + snat_in2out_trace_t *t = + vlib_add_trace (vm, node, b1, sizeof (*t)); + t->is_slow_path = 0; + t->sw_if_index = sw_if_index1; + t->next_index = next1; + t->session_index = ~0; + if (ses1) + t->session_index = ses1 - dm1->sessions; + } + + pkts_processed += next1 != SNAT_IN2OUT_NEXT_DROP; + + /* verify speculative enqueues, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, next0, next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t * b0; + u32 next0; + u32 sw_if_index0; + ip4_header_t * ip0; + ip_csum_t sum0; + ip4_address_t new_addr0, old_addr0; + u16 old_port0, new_port0, lo_port0, i0; + udp_header_t * udp0; + tcp_header_t * tcp0; + u32 proto0; + snat_det_out_key_t key0; + snat_det_map_t * dm0; + snat_det_session_t * ses0 = 0; + u32 rx_fib_index0; + icmp46_header_t * icmp0; + + /* speculatively enqueue b0 to the current next frame */ + 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); + next0 = SNAT_IN2OUT_NEXT_LOOKUP; + + ip0 = vlib_buffer_get_current (b0); + udp0 = ip4_next_header (ip0); + tcp0 = (tcp_header_t *) udp0; + + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; + + if (PREDICT_FALSE(ip0->ttl == 1)) + { + vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, + ICMP4_time_exceeded_ttl_exceeded_in_transit, + 0); + next0 = SNAT_IN2OUT_NEXT_ICMP_ERROR; + goto trace00; + } + + proto0 = ip_proto_to_snat_proto (ip0->protocol); + + if (PREDICT_FALSE(proto0 == SNAT_PROTOCOL_ICMP)) + { + rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0); + icmp0 = (icmp46_header_t *) udp0; + + next0 = icmp_in2out(sm, b0, ip0, icmp0, sw_if_index0, + rx_fib_index0, node, next0, thread_index, + &ses0, &dm0); + goto trace00; + } + + dm0 = snat_det_map_by_user(sm, &ip0->src_address); + if (PREDICT_FALSE(!dm0)) + { + clib_warning("no match for internal host %U", + format_ip4_address, &ip0->src_address); + next0 = SNAT_IN2OUT_NEXT_DROP; + b0->error = node->errors[SNAT_IN2OUT_ERROR_NO_TRANSLATION]; + goto trace00; + } + + snat_det_forward(dm0, &ip0->src_address, &new_addr0, &lo_port0); + + key0.ext_host_addr = ip0->dst_address; + key0.ext_host_port = tcp0->dst; + + ses0 = snat_det_find_ses_by_in(dm0, &ip0->src_address, tcp0->src, key0); + if (PREDICT_FALSE(!ses0)) + { + for (i0 = 0; i0 < dm0->ports_per_host; i0++) + { + key0.out_port = clib_host_to_net_u16 (lo_port0 + + ((i0 + clib_net_to_host_u16 (tcp0->src)) % dm0->ports_per_host)); + + if (snat_det_get_ses_by_out (dm0, &ip0->src_address, key0.as_u64)) + continue; + + ses0 = snat_det_ses_create(dm0, &ip0->src_address, tcp0->src, &key0); + break; + } + if (PREDICT_FALSE(!ses0)) + { + /* too many sessions for user, send ICMP error packet */ + + vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b0, ICMP4_destination_unreachable, + ICMP4_destination_unreachable_destination_unreachable_host, + 0); + next0 = SNAT_IN2OUT_NEXT_ICMP_ERROR; + goto trace00; + } + } + + new_port0 = ses0->out.out_port; + + old_addr0.as_u32 = ip0->src_address.as_u32; + ip0->src_address.as_u32 = new_addr0.as_u32; + vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm->outside_fib_index; + + sum0 = ip0->checksum; + sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32, + ip4_header_t, + src_address /* changed member */); + ip0->checksum = ip_csum_fold (sum0); + + if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) + { + if (tcp0->flags & TCP_FLAG_SYN) + ses0->state = SNAT_SESSION_TCP_SYN_SENT; + else if (tcp0->flags & TCP_FLAG_ACK && ses0->state == SNAT_SESSION_TCP_SYN_SENT) + ses0->state = SNAT_SESSION_TCP_ESTABLISHED; + else if (tcp0->flags & TCP_FLAG_FIN && ses0->state == SNAT_SESSION_TCP_ESTABLISHED) + ses0->state = SNAT_SESSION_TCP_FIN_WAIT; + else if (tcp0->flags & TCP_FLAG_ACK && ses0->state == SNAT_SESSION_TCP_FIN_WAIT) + snat_det_ses_close(dm0, ses0); + else if (tcp0->flags & TCP_FLAG_FIN && ses0->state == SNAT_SESSION_TCP_CLOSE_WAIT) + ses0->state = SNAT_SESSION_TCP_LAST_ACK; + else if (tcp0->flags == 0 && ses0->state == SNAT_SESSION_UNKNOWN) + ses0->state = SNAT_SESSION_TCP_ESTABLISHED; + + old_port0 = tcp0->src; + tcp0->src = new_port0; + + sum0 = tcp0->checksum; + sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32, + ip4_header_t, + dst_address /* changed member */); + sum0 = ip_csum_update (sum0, old_port0, new_port0, + ip4_header_t /* cheat */, + length /* changed member */); + tcp0->checksum = ip_csum_fold(sum0); + } + else + { + ses0->state = SNAT_SESSION_UDP_ACTIVE; + old_port0 = udp0->src_port; + udp0->src_port = new_port0; + udp0->checksum = 0; + } + + switch(ses0->state) + { + case SNAT_SESSION_UDP_ACTIVE: + ses0->expire = now + sm->udp_timeout; + break; + case SNAT_SESSION_TCP_SYN_SENT: + case SNAT_SESSION_TCP_FIN_WAIT: + case SNAT_SESSION_TCP_CLOSE_WAIT: + case SNAT_SESSION_TCP_LAST_ACK: + ses0->expire = now + sm->tcp_transitory_timeout; + break; + case SNAT_SESSION_TCP_ESTABLISHED: + ses0->expire = now + sm->tcp_established_timeout; + break; + } + + trace00: + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + snat_in2out_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->is_slow_path = 0; + t->sw_if_index = sw_if_index0; + t->next_index = next0; + t->session_index = ~0; + if (ses0) + t->session_index = ses0 - dm0->sessions; + } + + pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP; + + /* verify speculative enqueue, maybe switch current next frame */ + 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, snat_det_in2out_node.index, + SNAT_IN2OUT_ERROR_IN2OUT_PACKETS, + pkts_processed); + return frame->n_vectors; +} + +VLIB_REGISTER_NODE (snat_det_in2out_node) = { + .function = snat_det_in2out_node_fn, + .name = "nat44-det-in2out", + .vector_size = sizeof (u32), + .format_trace = format_snat_in2out_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(snat_in2out_error_strings), + .error_strings = snat_in2out_error_strings, + + .runtime_data_bytes = sizeof (snat_runtime_t), + + .n_next_nodes = 3, + + /* edit / add dispositions here */ + .next_nodes = { + [SNAT_IN2OUT_NEXT_DROP] = "error-drop", + [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup", + [SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (snat_det_in2out_node, snat_det_in2out_node_fn); + +/** + * Get address and port values to be used for ICMP packet translation + * and create session if needed + * + * @param[in,out] sm NAT main + * @param[in,out] node NAT node runtime + * @param[in] thread_index thread index + * @param[in,out] b0 buffer containing packet to be translated + * @param[out] p_proto protocol used for matching + * @param[out] p_value address and port after NAT translation + * @param[out] p_dont_translate if packet should not be translated + * @param d optional parameter + * @param e optional parameter + */ +u32 icmp_match_in2out_det(snat_main_t *sm, vlib_node_runtime_t *node, + u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + snat_session_key_t *p_value, + u8 *p_dont_translate, void *d, void *e) +{ + ip4_header_t *ip0; + icmp46_header_t *icmp0; + u32 sw_if_index0; + u32 rx_fib_index0; + u8 protocol; + snat_det_out_key_t key0; + u8 dont_translate = 0; + u32 next0 = ~0; + icmp_echo_header_t *echo0, *inner_echo0 = 0; + ip4_header_t *inner_ip0; + void *l4_header = 0; + icmp46_header_t *inner_icmp0; + snat_det_map_t * dm0 = 0; + ip4_address_t new_addr0; + u16 lo_port0, i0; + snat_det_session_t * ses0 = 0; + ip4_address_t in_addr; + u16 in_port; + + ip0 = vlib_buffer_get_current (b0); + icmp0 = (icmp46_header_t *) ip4_next_header (ip0); + echo0 = (icmp_echo_header_t *)(icmp0+1); + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; + rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0); + + if (!icmp_is_error_message (icmp0)) + { + protocol = SNAT_PROTOCOL_ICMP; + in_addr = ip0->src_address; + in_port = echo0->identifier; + } + else + { + inner_ip0 = (ip4_header_t *)(echo0+1); + l4_header = ip4_next_header (inner_ip0); + protocol = ip_proto_to_snat_proto (inner_ip0->protocol); + in_addr = inner_ip0->dst_address; + switch (protocol) + { + case SNAT_PROTOCOL_ICMP: + inner_icmp0 = (icmp46_header_t*)l4_header; + inner_echo0 = (icmp_echo_header_t *)(inner_icmp0+1); + in_port = inner_echo0->identifier; + break; + case SNAT_PROTOCOL_UDP: + case SNAT_PROTOCOL_TCP: + in_port = ((tcp_udp_header_t*)l4_header)->dst_port; + break; + default: + b0->error = node->errors[SNAT_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL]; + next0 = SNAT_IN2OUT_NEXT_DROP; + goto out; + } + } + + dm0 = snat_det_map_by_user(sm, &in_addr); + if (PREDICT_FALSE(!dm0)) + { + clib_warning("no match for internal host %U", + format_ip4_address, &in_addr); + if (PREDICT_FALSE(snat_not_translate_fast(sm, node, sw_if_index0, ip0, + IP_PROTOCOL_ICMP, rx_fib_index0))) + { + dont_translate = 1; + goto out; + } + next0 = SNAT_IN2OUT_NEXT_DROP; + b0->error = node->errors[SNAT_IN2OUT_ERROR_NO_TRANSLATION]; + goto out; + } + + snat_det_forward(dm0, &in_addr, &new_addr0, &lo_port0); + + key0.ext_host_addr = ip0->dst_address; + key0.ext_host_port = 0; + + ses0 = snat_det_find_ses_by_in(dm0, &in_addr, in_port, key0); + if (PREDICT_FALSE(!ses0)) + { + if (PREDICT_FALSE(snat_not_translate_fast(sm, node, sw_if_index0, ip0, + IP_PROTOCOL_ICMP, rx_fib_index0))) + { + dont_translate = 1; + goto out; + } + if (icmp0->type != ICMP4_echo_request) + { + b0->error = node->errors[SNAT_IN2OUT_ERROR_BAD_ICMP_TYPE]; + next0 = SNAT_IN2OUT_NEXT_DROP; + goto out; + } + for (i0 = 0; i0 < dm0->ports_per_host; i0++) + { + key0.out_port = clib_host_to_net_u16 (lo_port0 + + ((i0 + clib_net_to_host_u16 (echo0->identifier)) % dm0->ports_per_host)); + + if (snat_det_get_ses_by_out (dm0, &in_addr, key0.as_u64)) + continue; + + ses0 = snat_det_ses_create(dm0, &in_addr, echo0->identifier, &key0); + break; + } + if (PREDICT_FALSE(!ses0)) + { + next0 = SNAT_IN2OUT_NEXT_DROP; + b0->error = node->errors[SNAT_IN2OUT_ERROR_OUT_OF_PORTS]; + goto out; + } + } + + if (PREDICT_FALSE(icmp0->type != ICMP4_echo_request && + !icmp_is_error_message (icmp0))) + { + b0->error = node->errors[SNAT_IN2OUT_ERROR_BAD_ICMP_TYPE]; + next0 = SNAT_IN2OUT_NEXT_DROP; + goto out; + } + + u32 now = (u32) vlib_time_now (sm->vlib_main); + + ses0->state = SNAT_SESSION_ICMP_ACTIVE; + ses0->expire = now + sm->icmp_timeout; + +out: + *p_proto = protocol; + if (ses0) + { + p_value->addr = new_addr0; + p_value->fib_index = sm->outside_fib_index; + p_value->port = ses0->out.out_port; + } + *p_dont_translate = dont_translate; + if (d) + *(snat_det_session_t**)d = ses0; + if (e) + *(snat_det_map_t**)e = dm0; + return next0; +} + +/**********************/ +/*** worker handoff ***/ +/**********************/ +static inline uword +snat_in2out_worker_handoff_fn_inline (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, + u8 is_output) +{ + snat_main_t *sm = &snat_main; + vlib_thread_main_t *tm = vlib_get_thread_main (); + u32 n_left_from, *from, *to_next = 0; + static __thread vlib_frame_queue_elt_t **handoff_queue_elt_by_worker_index; + static __thread vlib_frame_queue_t **congested_handoff_queue_by_worker_index + = 0; + vlib_frame_queue_elt_t *hf = 0; + vlib_frame_t *f = 0; + int i; + u32 n_left_to_next_worker = 0, *to_next_worker = 0; + u32 next_worker_index = 0; + u32 current_worker_index = ~0; + u32 thread_index = vlib_get_thread_index (); + u32 fq_index; + u32 to_node_index; + + ASSERT (vec_len (sm->workers)); + + if (is_output) + { + fq_index = sm->fq_in2out_output_index; + to_node_index = sm->in2out_output_node_index; + } + else + { + fq_index = sm->fq_in2out_index; + to_node_index = sm->in2out_node_index; + } + + if (PREDICT_FALSE (handoff_queue_elt_by_worker_index == 0)) + { + vec_validate (handoff_queue_elt_by_worker_index, tm->n_vlib_mains - 1); + + vec_validate_init_empty (congested_handoff_queue_by_worker_index, + sm->first_worker_index + sm->num_workers - 1, + (vlib_frame_queue_t *) (~0)); + } + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + + while (n_left_from > 0) + { + u32 bi0; + vlib_buffer_t *b0; + u32 sw_if_index0; + u32 rx_fib_index0; + ip4_header_t * ip0; + u8 do_handoff; + + bi0 = from[0]; + from += 1; + n_left_from -= 1; + + b0 = vlib_get_buffer (vm, bi0); + + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0); + + ip0 = vlib_buffer_get_current (b0); + + next_worker_index = sm->worker_in2out_cb(ip0, rx_fib_index0); + + if (PREDICT_FALSE (next_worker_index != thread_index)) + { + do_handoff = 1; + + if (next_worker_index != current_worker_index) + { + if (hf) + hf->n_vectors = VLIB_FRAME_SIZE - n_left_to_next_worker; + + hf = vlib_get_worker_handoff_queue_elt (fq_index, + next_worker_index, + handoff_queue_elt_by_worker_index); + + n_left_to_next_worker = VLIB_FRAME_SIZE - hf->n_vectors; + to_next_worker = &hf->buffer_index[hf->n_vectors]; + current_worker_index = next_worker_index; + } + + /* enqueue to correct worker thread */ + to_next_worker[0] = bi0; + to_next_worker++; + n_left_to_next_worker--; + + if (n_left_to_next_worker == 0) + { + hf->n_vectors = VLIB_FRAME_SIZE; + vlib_put_frame_queue_elt (hf); + current_worker_index = ~0; + handoff_queue_elt_by_worker_index[next_worker_index] = 0; + hf = 0; + } + } + else + { + do_handoff = 0; + /* if this is 1st frame */ + if (!f) + { + f = vlib_get_frame_to_node (vm, to_node_index); + to_next = vlib_frame_vector_args (f); + } + + to_next[0] = bi0; + to_next += 1; + f->n_vectors++; + } + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + snat_in2out_worker_handoff_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->next_worker_index = next_worker_index; + t->do_handoff = do_handoff; + } + } + + if (f) + vlib_put_frame_to_node (vm, to_node_index, f); + + if (hf) + hf->n_vectors = VLIB_FRAME_SIZE - n_left_to_next_worker; + + /* Ship frames to the worker nodes */ + for (i = 0; i < vec_len (handoff_queue_elt_by_worker_index); i++) + { + if (handoff_queue_elt_by_worker_index[i]) + { + hf = handoff_queue_elt_by_worker_index[i]; + /* + * It works better to let the handoff node + * rate-adapt, always ship the handoff queue element. + */ + if (1 || hf->n_vectors == hf->last_n_vectors) + { + vlib_put_frame_queue_elt (hf); + handoff_queue_elt_by_worker_index[i] = 0; + } + else + hf->last_n_vectors = hf->n_vectors; + } + congested_handoff_queue_by_worker_index[i] = + (vlib_frame_queue_t *) (~0); + } + hf = 0; + current_worker_index = ~0; + return frame->n_vectors; +} + +static uword +snat_in2out_worker_handoff_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return snat_in2out_worker_handoff_fn_inline (vm, node, frame, 0); +} + +VLIB_REGISTER_NODE (snat_in2out_worker_handoff_node) = { + .function = snat_in2out_worker_handoff_fn, + .name = "nat44-in2out-worker-handoff", + .vector_size = sizeof (u32), + .format_trace = format_snat_in2out_worker_handoff_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_next_nodes = 1, + + .next_nodes = { + [0] = "error-drop", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_worker_handoff_node, + snat_in2out_worker_handoff_fn); + +static uword +snat_in2out_output_worker_handoff_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return snat_in2out_worker_handoff_fn_inline (vm, node, frame, 1); +} + +VLIB_REGISTER_NODE (snat_in2out_output_worker_handoff_node) = { + .function = snat_in2out_output_worker_handoff_fn, + .name = "nat44-in2out-output-worker-handoff", + .vector_size = sizeof (u32), + .format_trace = format_snat_in2out_worker_handoff_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_next_nodes = 1, + + .next_nodes = { + [0] = "error-drop", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_output_worker_handoff_node, + snat_in2out_output_worker_handoff_fn); + +static_always_inline int +is_hairpinning (snat_main_t *sm, ip4_address_t * dst_addr) +{ + snat_address_t * ap; + clib_bihash_kv_8_8_t kv, value; + snat_session_key_t m_key; + + vec_foreach (ap, sm->addresses) + { + if (ap->addr.as_u32 == dst_addr->as_u32) + return 1; + } + + m_key.addr.as_u32 = dst_addr->as_u32; + m_key.fib_index = sm->outside_fib_index; + m_key.port = 0; + m_key.protocol = 0; + kv.key = m_key.as_u64; + if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) + return 1; + + return 0; +} + +static uword +snat_hairpin_dst_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, * from, * to_next; + snat_in2out_next_t next_index; + u32 pkts_processed = 0; + snat_main_t * sm = &snat_main; + + 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; + u32 next0; + ip4_header_t * ip0; + u32 proto0; + + /* speculatively enqueue b0 to the current next frame */ + 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); + next0 = SNAT_IN2OUT_NEXT_LOOKUP; + ip0 = vlib_buffer_get_current (b0); + + proto0 = ip_proto_to_snat_proto (ip0->protocol); + + vnet_buffer (b0)->snat.flags = 0; + if (PREDICT_FALSE (is_hairpinning (sm, &ip0->dst_address))) + { + if (proto0 == SNAT_PROTOCOL_TCP || proto0 == SNAT_PROTOCOL_UDP) + { + udp_header_t * udp0 = ip4_next_header (ip0); + tcp_header_t * tcp0 = (tcp_header_t *) udp0; + + snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0); + } + else if (proto0 == SNAT_PROTOCOL_ICMP) + { + icmp46_header_t * icmp0 = ip4_next_header (ip0); + + snat_icmp_hairpinning (sm, b0, ip0, icmp0); + } + else + { + snat_hairpinning_unknown_proto (sm, b0, ip0); + } + + vnet_buffer (b0)->snat.flags = SNAT_FLAG_HAIRPINNING; + clib_warning("is hairpinning"); + } + + pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP; + + /* verify speculative enqueue, maybe switch current next frame */ + 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, snat_hairpin_dst_node.index, + SNAT_IN2OUT_ERROR_IN2OUT_PACKETS, + pkts_processed); + return frame->n_vectors; +} + +VLIB_REGISTER_NODE (snat_hairpin_dst_node) = { + .function = snat_hairpin_dst_fn, + .name = "nat44-hairpin-dst", + .vector_size = sizeof (u32), + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(snat_in2out_error_strings), + .error_strings = snat_in2out_error_strings, + .n_next_nodes = 2, + .next_nodes = { + [SNAT_IN2OUT_NEXT_DROP] = "error-drop", + [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (snat_hairpin_dst_node, + snat_hairpin_dst_fn); + +static uword +snat_hairpin_src_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, * from, * to_next; + snat_in2out_next_t next_index; + u32 pkts_processed = 0; + snat_main_t *sm = &snat_main; + + 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; + u32 next0; + + /* speculatively enqueue b0 to the current next frame */ + 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); + next0 = SNAT_HAIRPIN_SRC_NEXT_INTERFACE_OUTPUT; + + if (PREDICT_FALSE ((vnet_buffer (b0)->snat.flags) & SNAT_FLAG_HAIRPINNING)) + { + if (PREDICT_TRUE (sm->num_workers > 1)) + next0 = SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH; + else + next0 = SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT; + } + + pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP; + + /* verify speculative enqueue, maybe switch current next frame */ + 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, snat_hairpin_src_node.index, + SNAT_IN2OUT_ERROR_IN2OUT_PACKETS, + pkts_processed); + return frame->n_vectors; +} + +VLIB_REGISTER_NODE (snat_hairpin_src_node) = { + .function = snat_hairpin_src_fn, + .name = "nat44-hairpin-src", + .vector_size = sizeof (u32), + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN(snat_in2out_error_strings), + .error_strings = snat_in2out_error_strings, + .n_next_nodes = SNAT_HAIRPIN_SRC_N_NEXT, + .next_nodes = { + [SNAT_HAIRPIN_SRC_NEXT_DROP] = "error-drop", + [SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT] = "nat44-in2out-output", + [SNAT_HAIRPIN_SRC_NEXT_INTERFACE_OUTPUT] = "interface-output", + [SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH] = "nat44-in2out-output-worker-handoff", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (snat_hairpin_src_node, + snat_hairpin_src_fn); + +static uword +snat_in2out_fast_static_map_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, * from, * to_next; + snat_in2out_next_t next_index; + u32 pkts_processed = 0; + snat_main_t * sm = &snat_main; + u32 stats_node_index; + + stats_node_index = snat_in2out_fast_node.index; + + 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; + u32 next0; + u32 sw_if_index0; + ip4_header_t * ip0; + ip_csum_t sum0; + u32 new_addr0, old_addr0; + u16 old_port0, new_port0; + udp_header_t * udp0; + tcp_header_t * tcp0; + icmp46_header_t * icmp0; + snat_session_key_t key0, sm0; + u32 proto0; + u32 rx_fib_index0; + + /* speculatively enqueue b0 to the current next frame */ + 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); + next0 = SNAT_IN2OUT_NEXT_LOOKUP; + + ip0 = vlib_buffer_get_current (b0); + udp0 = ip4_next_header (ip0); + tcp0 = (tcp_header_t *) udp0; + icmp0 = (icmp46_header_t *) udp0; + + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; + rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0); + + if (PREDICT_FALSE(ip0->ttl == 1)) + { + vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, + ICMP4_time_exceeded_ttl_exceeded_in_transit, + 0); + next0 = SNAT_IN2OUT_NEXT_ICMP_ERROR; + goto trace0; + } + + proto0 = ip_proto_to_snat_proto (ip0->protocol); + + if (PREDICT_FALSE (proto0 == ~0)) + goto trace0; + + if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP)) + { + next0 = icmp_in2out(sm, b0, ip0, icmp0, sw_if_index0, + rx_fib_index0, node, next0, ~0, 0, 0); + goto trace0; + } + + key0.addr = ip0->src_address; + key0.port = udp0->src_port; + key0.fib_index = rx_fib_index0; + + if (snat_static_mapping_match(sm, key0, &sm0, 0, 0)) + { + b0->error = node->errors[SNAT_IN2OUT_ERROR_NO_TRANSLATION]; + next0= SNAT_IN2OUT_NEXT_DROP; + goto trace0; + } + + new_addr0 = sm0.addr.as_u32; + new_port0 = sm0.port; + vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index; + old_addr0 = ip0->src_address.as_u32; + ip0->src_address.as_u32 = new_addr0; + + sum0 = ip0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, + ip4_header_t, + src_address /* changed member */); + ip0->checksum = ip_csum_fold (sum0); + + if (PREDICT_FALSE(new_port0 != udp0->dst_port)) + { + if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) + { + old_port0 = tcp0->src_port; + tcp0->src_port = new_port0; + + sum0 = tcp0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, + ip4_header_t, + dst_address /* changed member */); + sum0 = ip_csum_update (sum0, old_port0, new_port0, + ip4_header_t /* cheat */, + length /* changed member */); + tcp0->checksum = ip_csum_fold(sum0); + } + else + { + old_port0 = udp0->src_port; + udp0->src_port = new_port0; + udp0->checksum = 0; + } + } + else + { + if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) + { + sum0 = tcp0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, + ip4_header_t, + dst_address /* changed member */); + tcp0->checksum = ip_csum_fold(sum0); + } + } + + /* Hairpinning */ + snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0); + + trace0: + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + snat_in2out_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = sw_if_index0; + t->next_index = next0; + } + + pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP; + + /* verify speculative enqueue, maybe switch current next frame */ + 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, stats_node_index, + SNAT_IN2OUT_ERROR_IN2OUT_PACKETS, + pkts_processed); + return frame->n_vectors; +} + + +VLIB_REGISTER_NODE (snat_in2out_fast_node) = { + .function = snat_in2out_fast_static_map_fn, + .name = "nat44-in2out-fast", + .vector_size = sizeof (u32), + .format_trace = format_snat_in2out_fast_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(snat_in2out_error_strings), + .error_strings = snat_in2out_error_strings, + + .runtime_data_bytes = sizeof (snat_runtime_t), + + .n_next_nodes = SNAT_IN2OUT_N_NEXT, + + /* edit / add dispositions here */ + .next_nodes = { + [SNAT_IN2OUT_NEXT_DROP] = "error-drop", + [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup", + [SNAT_IN2OUT_NEXT_SLOW_PATH] = "nat44-in2out-slowpath", + [SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_fast_node, snat_in2out_fast_static_map_fn); diff --git a/src/plugins/nat/nat.api b/src/plugins/nat/nat.api new file mode 100644 index 00000000..7245cb07 --- /dev/null +++ b/src/plugins/nat/nat.api @@ -0,0 +1,1513 @@ +/* + * 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. + */ +/** + * @file nat.api + * @brief VPP control-plane API messages. + * + * This file defines VPP control-plane API messages which are generally + * called through a shared memory interface. + */ + +/* + * Old "snat" APIs, will be deprecated after 17.10 + */ + +/** \brief Add/del NAT44 address range + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_ip4 - 1 if address type is IPv4 + @param first_ip_address - first IP address + @param last_ip_address - last IP address + @param vrf_id - VRF id of tenant, ~0 means independent of VRF + @param is_add - 1 if add, 0 if delete +*/ +autoreply define snat_add_address_range { + u32 client_index; + u32 context; + u8 is_ip4; + u8 first_ip_address[16]; + u8 last_ip_address[16]; + u32 vrf_id; + u8 is_add; +}; + +/** \brief Dump NAT44 addresses + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define snat_address_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT44 address details response + @param context - sender context, to match reply w/ request + @param is_ip4 - 1 if address type is IPv4 + @param ip_address - IP address + @param vrf_id - VRF id of tenant, ~0 means independent of VRF +*/ +define snat_address_details { + u32 context; + u8 is_ip4; + u8 ip_address[16]; + u32 vrf_id; +}; + +/** \brief Enable/disable NAT44 feature on the interface + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - 1 if add, 0 if delete + @param is_inside - 1 if inside, 0 if outside + @param sw_if_index - software index of the interface +*/ +autoreply define snat_interface_add_del_feature { + u32 client_index; + u32 context; + u8 is_add; + u8 is_inside; + u32 sw_if_index; +}; + +/** \brief Dump interfaces with NAT44 feature + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define snat_interface_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT44 interface details response + @param context - sender context, to match reply w/ request + @param is_inside - 1 if inside, 0 if outside + @param sw_if_index - software index of the interface +*/ +define snat_interface_details { + u32 context; + u8 is_inside; + u32 sw_if_index; +}; + +/** \brief Enable/disbale NAT44 as an interface output feature (postrouting + in2out translation) + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - 1 if add, 0 if delete + @param is_inside - 1 if inside, 0 if outside + @param sw_if_index - software index of the interface +*/ +autoreply define snat_interface_add_del_output_feature { + u32 client_index; + u32 context; + u8 is_add; + u8 is_inside; + u32 sw_if_index; +}; + +/** \brief Dump interfaces with NAT44 output feature + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define snat_interface_output_feature_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT44 interface with output feature details response + @param context - sender context, to match reply w/ request + @param is_inside - 1 if inside, 0 if outside + @param sw_if_index - software index of the interface +*/ +define snat_interface_output_feature_details { + u32 context; + u8 is_inside; + u32 sw_if_index; +}; + +/** \brief Add/delete NAT44 static mapping + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - 1 if add, 0 if delete + @param is_ip4 - 1 if address type is IPv4 + @param addr_only - 1 if address only mapping + @param local_ip_address - local IP address + @param external_ip_address - external IP address + @param protocol - IP protocol + @param local_port - local port number + @param external_port - external port number + @param external_sw_if_index - external interface (if set + external_ip_address is ignored, ~0 means not + used) + @param vfr_id - VRF ID +*/ +autoreply define snat_add_static_mapping { + u32 client_index; + u32 context; + u8 is_add; + u8 is_ip4; + u8 addr_only; + u8 local_ip_address[16]; + u8 external_ip_address[16]; + u8 protocol; + u16 local_port; + u16 external_port; + u32 external_sw_if_index; + u32 vrf_id; +}; + +/** \brief Dump NAT44 static mappings + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define snat_static_mapping_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT44 static mapping details response + @param context - sender context, to match reply w/ request + @param is_ip4 - 1 if address type is IPv4 + @param addr_only - 1 if address only mapping + @param local_ip_address - local IP address + @param external_ip_address - external IP address + @param protocol - IP protocol + @param local_port - local port number + @param external_port - external port number + @param external_sw_if_index - external interface + @param vfr_id - VRF ID +*/ +define snat_static_mapping_details { + u32 context; + u8 is_ip4; + u8 addr_only; + u8 local_ip_address[16]; + u8 external_ip_address[16]; + u8 protocol; + u16 local_port; + u16 external_port; + u32 external_sw_if_index; + u32 vrf_id; +}; + +/** \brief Control ping from client to api server request + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define snat_control_ping +{ + u32 client_index; + u32 context; +}; + +/** \brief Control ping from the client to the server response + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param retval - return code for the request + @param vpe_pid - the pid of the vpe, returned by the server +*/ +define snat_control_ping_reply +{ + u32 context; + i32 retval; + u32 client_index; + u32 vpe_pid; +}; + +/** \brief Show NAT plugin startup config + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define snat_show_config +{ + u32 client_index; + u32 context; +}; + +/** \brief Show NAT plugin startup config reply + @param context - sender context, to match reply w/ request + @param retval - return code for the request + @param static_mapping_only - if 1 dynamic translations disabled + @param static_mapping_connection_tracking - if 1 create session data + @param deterministic - if 1 deterministic mapping + @param translation_buckets - number of translation hash buckets + @param translation_memory_size - translation hash memory size + @param user_buckets - number of user hash buckets + @param user_memory_size - user hash memory size + @param max_translations_per_user - maximum number of translations per user + @param outside_vrf_id - outside VRF id + @param inside_vrf_id - default inside VRF id +*/ +define snat_show_config_reply +{ + u32 context; + i32 retval; + u8 static_mapping_only; + u8 static_mapping_connection_tracking; + u8 deterministic; + u32 translation_buckets; + u32 translation_memory_size; + u32 user_buckets; + u32 user_memory_size; + u32 max_translations_per_user; + u32 outside_vrf_id; + u32 inside_vrf_id; +}; + +/** \brief Set NAT workers + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param worker_mask - NAT workers mask +*/ +autoreply define snat_set_workers { + u32 client_index; + u32 context; + u64 worker_mask; +}; + +/** \brief Dump NAT workers + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define snat_worker_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT workers details response + @param context - sender context, to match reply w/ request + @param worker_index - worker index + @param lcore_id - lcore ID + @param name - worker name +*/ +define snat_worker_details { + u32 context; + u32 worker_index; + u32 lcore_id; + u8 name[64]; +}; + +/** \brief Add/delete NAT44 pool address from specific interfce + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - 1 if add, 0 if delete + @param sw_if_index - software index of the interface +*/ +autoreply define snat_add_del_interface_addr { + u32 client_index; + u32 context; + u8 is_add; + u8 is_inside; + u32 sw_if_index; +}; + +/** \brief Dump NAT44 pool addresses interfaces + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define snat_interface_addr_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT44 pool addresses interfaces details response + @param context - sender context, to match reply w/ request + @param sw_if_index - software index of the interface +*/ +define snat_interface_addr_details { + u32 context; + u32 sw_if_index; +}; + +/** \brief Enable/disable NAT IPFIX logging + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param domain_id - observation domain ID + @param src_port - source port number + @param enable - 1 if enable, 0 if disable +*/ +autoreply define snat_ipfix_enable_disable { + u32 client_index; + u32 context; + u32 domain_id; + u16 src_port; + u8 enable; +}; + +/** \brief Dump NAT44 users + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define snat_user_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT44 users response + @param context - sender context, to match reply w/ request + @vrf_id - VRF ID + @param is_ip4 - 1 if address type is IPv4 + @param ip_adress - IP address + @param nsessions - number of dynamic sessions + @param nstaticsessions - number of static sessions +*/ +define snat_user_details { + u32 context; + u32 vrf_id; + u8 is_ip4; + u8 ip_address[16]; + u32 nsessions; + u32 nstaticsessions; +}; + +/** \brief NAT44 user's sessions + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_ip4 - 1 if address type is IPv4 + @param user_ip - IP address of the user to dump + @param vrf_id - VRF_ID +*/ +define snat_user_session_dump { + u32 client_index; + u32 context; + u8 is_ip4; + u8 ip_address[16]; + u32 vrf_id; +}; + +/** \brief NAT44 user's sessions response + @param context - sender context, to match reply w/ request + @param is_ip4 - 1 if address type is IPv4 + @param outside_ip_address - outside IP address + @param outside_port - outside port + @param inside_ip_address - inside IP address + @param inside_port - inside port + @param protocol - protocol + @param is_static - 1 if session is static + @param last_heard - last heard timer + @param total_bytes - count of bytes sent through session + @param total_pkts - count of pakets sent through session +*/ +define snat_user_session_details { + u32 context; + u8 is_ip4; + u8 outside_ip_address[16]; + u16 outside_port; + u8 inside_ip_address[16]; + u16 inside_port; + u16 protocol; + u8 is_static; + u64 last_heard; + u64 total_bytes; + u32 total_pkts; +}; + +/** \brief Add/delete NAT deterministic mapping + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - 1 if add, 0 if delete + @param is_ip4 - 1 if address type is IPv4 + @param in_addr - inside IP address + @param in_plen - inside IP address prefix length + @param out_addr - outside IP address + @param out_addr - outside IP address prefix length +*/ +autoreply define snat_add_det_map { + u32 client_index; + u32 context; + u8 is_add; + u8 is_ip4; + u8 addr_only; + u8 in_addr[16]; + u8 in_plen; + u8 out_addr[16]; + u8 out_plen; +}; + +/** \brief Get outside address and port range from inside address + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_ip4 - 1 if address type is IPv4 + @param in_addr - inside IP address +*/ +define snat_det_forward { + u32 client_index; + u32 context; + u8 is_ip4; + u8 in_addr[16]; +}; + +/** \brief Get outside address and port range from inside address + @param context - sender context, to match reply w/ request + @param retval - return code + @param out_port_lo - outside port range start + @param out_port_hi - outside port range end + @param is_ip4 - 1 if address type is IPv4 + @param out_addr - outside IP address +*/ +define snat_det_forward_reply { + u32 context; + i32 retval; + u16 out_port_lo; + u16 out_port_hi; + u8 is_ip4; + u8 out_addr[16]; +}; + +/** \brief Get inside address from outside address and port + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param out_port - outside port + @param is_ip4 - 1 if address type is IPv4 + @param out_addr - outside IP address +*/ +define snat_det_reverse { + u32 client_index; + u32 context; + u16 out_port; + u8 is_ip4; + u8 out_addr[16]; +}; + +/** \brief Get inside address from outside address and port reply + @param context - sender context, to match reply w/ request + @param retval - return code + @param is_ip4 - 1 if address type is IPv4 + @param in_addr - inside IP address +*/ +define snat_det_reverse_reply { + u32 context; + i32 retval; + u8 is_ip4; + u8 in_addr[16]; +}; + +/** \brief Dump NAT deterministic mappings + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define snat_det_map_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT users response + @param context - sender context, to match reply w/ request + @param is_ip4 - 1 if address type is IPv4 + @param in_addr - inside IP address + @param in_plen - inside IP address prefix length + @param out_addr - outside IP address + @param out_plen - outside IP address prefix length + @param sharing_ratio - outside to inside address sharing ratio + @param ports_per_host - number of ports available to a host + @param ses_num - number of sessions belonging to this mapping +*/ +define snat_det_map_details { + u32 context; + u8 is_ip4; + u8 in_addr[16]; + u8 in_plen; + u8 out_addr[16]; + u8 out_plen; + u32 sharing_ratio; + u16 ports_per_host; + u32 ses_num; +}; + +/** \brief Set values of timeouts for deterministic NAT (seconds, 0 = default) + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param udp - UDP timeout (default 300sec) + @param tcp_established - TCP established timeout (default 7440sec) + @param tcp_transitory - TCP transitory timeout (default 240sec) + @param icmp - ICMP timeout (default 60sec) +*/ +autoreply define snat_det_set_timeouts { + u32 client_index; + u32 context; + u32 udp; + u32 tcp_established; + u32 tcp_transitory; + u32 icmp; +}; + +/** \brief Get values of timeouts for deterministic NAT (seconds) + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define snat_det_get_timeouts { + u32 client_index; + u32 context; +}; + +/** \brief Get values of timeouts for deterministic NAT reply + @param context - sender context, to match reply w/ request + @param retval - return code + @param udp - UDP timeout (default 300sec) + @param tcp_established - TCP established timeout (default 7440sec) + @param tcp_transitory - TCP transitory timeout (default 240sec) + @param icmp - ICMP timeout (default 60sec) +*/ +define snat_det_get_timeouts_reply { + u32 context; + i32 retval; + u32 udp; + u32 tcp_established; + u32 tcp_transitory; + u32 icmp; +}; + +/** \brief Close deterministic NAT session by outside address and port + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_ip4 - 1 if address type is IPv4 + @param out_addr - outside IP address + @param out_port - outside port + @param ext_addr - external host address + @param ext_port - external host port +*/ +autoreply define snat_det_close_session_out { + u32 client_index; + u32 context; + u8 is_ip4; + u8 out_addr[16]; + u16 out_port; + u8 ext_addr[16]; + u16 ext_port; +}; + +/** \brief Close deterministic NAT session by inside address and port + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_ip4 - 1 if address type is IPv4 + @param in_addr - inside IP address + @param in_port - inside port + @param ext_addr - external host address + @param ext_port - external host port +*/ +autoreply define snat_det_close_session_in { + u32 client_index; + u32 context; + u8 is_ip4; + u8 in_addr[16]; + u16 in_port; + u8 ext_addr[16]; + u16 ext_port; +}; + +/** \brief Dump determinstic NAT sessions + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_ip4 - 1 if address type is IPv4 + @param user_addr - address of an inside user whose sessions to dump +*/ +define snat_det_session_dump { + u32 client_index; + u32 context; + u8 is_ip4; + u8 user_addr[16]; +}; + +/** \brief Deterministic NAT sessions reply + @param context - sender context, to match reply w/ request + @param is_ip4 - 1 if address type is IPv4 + @param in_port - inside port + @param ext_addr - external host address + @param ext_port - external host port + @param out_port - outside NAT port + @param state - session state + @param expire - session expiration timestamp +*/ +define snat_det_session_details { + u32 client_index; + u32 context; + u8 is_ip4; + u16 in_port; + u8 ext_addr[16]; + u16 ext_port; + u16 out_port; + u8 state; + u32 expire; +}; + +/* + * Common NAT plugin APIs + */ + +/** \brief Control ping from client to api server request + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat_control_ping +{ + u32 client_index; + u32 context; +}; + +/** \brief Control ping from the client to the server response + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param retval - return code for the request + @param vpe_pid - the pid of the vpe, returned by the server +*/ +define nat_control_ping_reply +{ + u32 context; + i32 retval; + u32 client_index; + u32 vpe_pid; +}; + +/** \brief Show NAT plugin startup config + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat_show_config +{ + u32 client_index; + u32 context; +}; + +/** \brief Show NAT plugin startup config reply + @param context - sender context, to match reply w/ request + @param retval - return code for the request + @param static_mapping_only - if 1 dynamic translations disabled + @param static_mapping_connection_tracking - if 1 create session data + @param deterministic - if 1 deterministic mapping + @param translation_buckets - number of translation hash buckets + @param translation_memory_size - translation hash memory size + @param user_buckets - number of user hash buckets + @param user_memory_size - user hash memory size + @param max_translations_per_user - maximum number of translations per user + @param outside_vrf_id - outside VRF id + @param inside_vrf_id - default inside VRF id +*/ +define nat_show_config_reply +{ + u32 context; + i32 retval; + u8 static_mapping_only; + u8 static_mapping_connection_tracking; + u8 deterministic; + u32 translation_buckets; + u32 translation_memory_size; + u32 user_buckets; + u32 user_memory_size; + u32 max_translations_per_user; + u32 outside_vrf_id; + u32 inside_vrf_id; +}; + +/** \brief Set NAT workers + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param worker_mask - NAT workers mask +*/ +autoreply define nat_set_workers { + u32 client_index; + u32 context; + u64 worker_mask; +}; + +/** \brief Dump NAT workers + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat_worker_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT workers details response + @param context - sender context, to match reply w/ request + @param worker_index - worker index + @param lcore_id - lcore ID + @param name - worker name +*/ +define nat_worker_details { + u32 context; + u32 worker_index; + u32 lcore_id; + u8 name[64]; +}; + +/** \brief Enable/disable NAT IPFIX logging + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param domain_id - observation domain ID + @param src_port - source port number + @param enable - 1 if enable, 0 if disable +*/ +autoreply define nat_ipfix_enable_disable { + u32 client_index; + u32 context; + u32 domain_id; + u16 src_port; + u8 enable; +}; + +/* + * NAT44 APIs + */ + +/** \brief Add/del NAT44 address range + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param first_ip_address - first IPv4 address + @param last_ip_address - last IPv4 address + @param vrf_id - VRF id of tenant, ~0 means independent of VRF + @param is_add - 1 if add, 0 if delete +*/ +autoreply define nat44_add_del_address_range { + u32 client_index; + u32 context; + u8 first_ip_address[4]; + u8 last_ip_address[4]; + u32 vrf_id; + u8 is_add; +}; + +/** \brief Dump NAT44 addresses + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_address_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT44 address details response + @param context - sender context, to match reply w/ request + @param ip_address - IPv4 address + @param vrf_id - VRF id of tenant, ~0 means independent of VRF +*/ +define nat44_address_details { + u32 context; + u8 ip_address[4]; + u32 vrf_id; +}; + +/** \brief Enable/disable NAT44 feature on the interface + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - 1 if add, 0 if delete + @param is_inside - 1 if inside, 0 if outside + @param sw_if_index - software index of the interface +*/ +autoreply define nat44_interface_add_del_feature { + u32 client_index; + u32 context; + u8 is_add; + u8 is_inside; + u32 sw_if_index; +}; + +/** \brief Dump interfaces with NAT44 feature + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_interface_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT44 interface details response + @param context - sender context, to match reply w/ request + @param is_inside - 1 if inside, 0 if outside + @param sw_if_index - software index of the interface +*/ +define nat44_interface_details { + u32 context; + u8 is_inside; + u32 sw_if_index; +}; + +/** \brief Enable/disbale NAT44 as an interface output feature (postrouting + in2out translation) + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - 1 if add, 0 if delete + @param is_inside - 1 if inside, 0 if outside + @param sw_if_index - software index of the interface +*/ +autoreply define nat44_interface_add_del_output_feature { + u32 client_index; + u32 context; + u8 is_add; + u8 is_inside; + u32 sw_if_index; +}; + +/** \brief Dump interfaces with NAT44 output feature + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_interface_output_feature_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT44 interface with output feature details response + @param context - sender context, to match reply w/ request + @param is_inside - 1 if inside, 0 if outside + @param sw_if_index - software index of the interface +*/ +define nat44_interface_output_feature_details { + u32 context; + u8 is_inside; + u32 sw_if_index; +}; + +/** \brief Add/delete NAT44 static mapping + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - 1 if add, 0 if delete + @param addr_only - 1 if address only mapping + @param local_ip_address - local IPv4 address + @param external_ip_address - external IPv4 address + @param protocol - IP protocol + @param local_port - local port number + @param external_port - external port number + @param external_sw_if_index - external interface (if set + external_ip_address is ignored, ~0 means not + used) + @param vfr_id - VRF ID +*/ +autoreply define nat44_add_del_static_mapping { + u32 client_index; + u32 context; + u8 is_add; + u8 addr_only; + u8 local_ip_address[4]; + u8 external_ip_address[4]; + u8 protocol; + u16 local_port; + u16 external_port; + u32 external_sw_if_index; + u32 vrf_id; +}; + +/** \brief Dump NAT44 static mappings + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_static_mapping_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT44 static mapping details response + @param context - sender context, to match reply w/ request + @param addr_only - 1 if address only mapping + @param local_ip_address - local IPv4 address + @param external_ip_address - external IPv4 address + @param protocol - IP protocol + @param local_port - local port number + @param external_port - external port number + @param external_sw_if_index - external interface + @param vfr_id - VRF ID +*/ +define nat44_static_mapping_details { + u32 context; + u8 addr_only; + u8 local_ip_address[4]; + u8 external_ip_address[4]; + u8 protocol; + u16 local_port; + u16 external_port; + u32 external_sw_if_index; + u32 vrf_id; +}; + +/** \brief Add/delete NAT44 pool address from specific interfce + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - 1 if add, 0 if delete + @param sw_if_index - software index of the interface +*/ +autoreply define nat44_add_del_interface_addr { + u32 client_index; + u32 context; + u8 is_add; + u8 is_inside; + u32 sw_if_index; +}; + +/** \brief Dump NAT44 pool addresses interfaces + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_interface_addr_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT44 pool addresses interfaces details response + @param context - sender context, to match reply w/ request + @param sw_if_index - software index of the interface +*/ +define nat44_interface_addr_details { + u32 context; + u32 sw_if_index; +}; + +/** \brief Dump NAT44 users + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat44_user_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT44 users response + @param context - sender context, to match reply w/ request + @vrf_id - VRF ID + @param ip_adress - IPv4 address + @param nsessions - number of dynamic sessions + @param nstaticsessions - number of static sessions +*/ +define nat44_user_details { + u32 context; + u32 vrf_id; + u8 ip_address[4]; + u32 nsessions; + u32 nstaticsessions; +}; + +/** \brief NAT44 user's sessions + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_ip4 - 1 if address type is IPv4 + @param user_ip - IP address of the user to dump + @param vrf_id - VRF_ID +*/ +define nat44_user_session_dump { + u32 client_index; + u32 context; + u8 ip_address[4]; + u32 vrf_id; +}; + +/** \brief NAT44 user's sessions response + @param context - sender context, to match reply w/ request + @param outside_ip_address - outside IPv4 address + @param outside_port - outside port + @param inside_ip_address - inside IPv4 address + @param inside_port - inside port + @param protocol - protocol + @param is_static - 1 if session is static + @param last_heard - last heard timer + @param total_bytes - count of bytes sent through session + @param total_pkts - count of pakets sent through session +*/ +define nat44_user_session_details { + u32 context; + u8 outside_ip_address[4]; + u16 outside_port; + u8 inside_ip_address[4]; + u16 inside_port; + u16 protocol; + u8 is_static; + u64 last_heard; + u64 total_bytes; + u32 total_pkts; +}; + +/* + * Deterministic NAT (CGN) APIs + */ + +/** \brief Add/delete NAT deterministic mapping + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - 1 if add, 0 if delete + @param is_nat44 - 1 if NAT44 + @param in_addr - inside IP address + @param in_plen - inside IP address prefix length + @param out_addr - outside IPv4 address + @param out_addr - outside IPv4 address prefix length +*/ +autoreply define nat_det_add_del_map { + u32 client_index; + u32 context; + u8 is_add; + u8 is_nat44; + u8 addr_only; + u8 in_addr[16]; + u8 in_plen; + u8 out_addr[4]; + u8 out_plen; +}; + +/** \brief Get outside address and port range from inside address + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_nat44 - 1 if NAT44 + @param in_addr - inside IP address +*/ +define nat_det_forward { + u32 client_index; + u32 context; + u8 is_nat44; + u8 in_addr[16]; +}; + +/** \brief Get outside address and port range from inside address + @param context - sender context, to match reply w/ request + @param retval - return code + @param out_port_lo - outside port range start + @param out_port_hi - outside port range end + @param out_addr - outside IPv4 address +*/ +define nat_det_forward_reply { + u32 context; + i32 retval; + u16 out_port_lo; + u16 out_port_hi; + u8 out_addr[4]; +}; + +/** \brief Get inside address from outside address and port + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param out_port - outside port + @param out_addr - outside IPv4 address +*/ +define nat_det_reverse { + u32 client_index; + u32 context; + u16 out_port; + u8 out_addr[4]; +}; + +/** \brief Get inside address from outside address and port reply + @param context - sender context, to match reply w/ request + @param retval - return code + @param is_nat44 - 1 if NAT44 + @param in_addr - inside IP address +*/ +define nat_det_reverse_reply { + u32 context; + i32 retval; + u8 is_nat44; + u8 in_addr[16]; +}; + +/** \brief Dump NAT deterministic mappings + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat_det_map_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT users response + @param context - sender context, to match reply w/ request + @param is_nat44 - 1 if NAT44 + @param in_addr - inside IP address + @param in_plen - inside IP address prefix length + @param out_addr - outside IPv4 address + @param out_plen - outside IPv4 address prefix length + @param sharing_ratio - outside to inside address sharing ratio + @param ports_per_host - number of ports available to a host + @param ses_num - number of sessions belonging to this mapping +*/ +define nat_det_map_details { + u32 context; + u8 is_nat44; + u8 in_addr[16]; + u8 in_plen; + u8 out_addr[4]; + u8 out_plen; + u32 sharing_ratio; + u16 ports_per_host; + u32 ses_num; +}; + +/** \brief Set values of timeouts for deterministic NAT (seconds, 0 = default) + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param udp - UDP timeout (default 300sec) + @param tcp_established - TCP established timeout (default 7440sec) + @param tcp_transitory - TCP transitory timeout (default 240sec) + @param icmp - ICMP timeout (default 60sec) +*/ +autoreply define nat_det_set_timeouts { + u32 client_index; + u32 context; + u32 udp; + u32 tcp_established; + u32 tcp_transitory; + u32 icmp; +}; + +/** \brief Get values of timeouts for deterministic NAT (seconds) + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat_det_get_timeouts { + u32 client_index; + u32 context; +}; + +/** \brief Get values of timeouts for deterministic NAT reply + @param context - sender context, to match reply w/ request + @param retval - return code + @param udp - UDP timeout (default 300sec) + @param tcp_established - TCP established timeout (default 7440sec) + @param tcp_transitory - TCP transitory timeout (default 240sec) + @param icmp - ICMP timeout (default 60sec) +*/ +define nat_det_get_timeouts_reply { + u32 context; + i32 retval; + u32 udp; + u32 tcp_established; + u32 tcp_transitory; + u32 icmp; +}; + +/** \brief Close deterministic NAT session by outside address and port + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param out_addr - outside IPv4 address + @param out_port - outside port + @param ext_addr - external host IPv4 address + @param ext_port - external host port +*/ +autoreply define nat_det_close_session_out { + u32 client_index; + u32 context; + u8 out_addr[4]; + u16 out_port; + u8 ext_addr[4]; + u16 ext_port; +}; + +/** \brief Close deterministic NAT session by inside address and port + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_nat44 - 1 if NAT44 + @param in_addr - inside IP address + @param in_port - inside port + @param ext_addr - external host IP address + @param ext_port - external host port +*/ +autoreply define nat_det_close_session_in { + u32 client_index; + u32 context; + u8 is_nat44; + u8 in_addr[16]; + u16 in_port; + u8 ext_addr[16]; + u16 ext_port; +}; + +/** \brief Dump determinstic NAT sessions + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_nat44 - 1 if NAT44 + @param user_addr - address of an inside user whose sessions to dump +*/ +define nat_det_session_dump { + u32 client_index; + u32 context; + u8 is_nat44; + u8 user_addr[16]; +}; + +/** \brief Deterministic NAT sessions reply + @param context - sender context, to match reply w/ request + @param in_port - inside port + @param ext_addr - external host address + @param ext_port - external host port + @param out_port - outside NAT port + @param state - session state + @param expire - session expiration timestamp +*/ +define nat_det_session_details { + u32 client_index; + u32 context; + u16 in_port; + u8 ext_addr[4]; + u16 ext_port; + u16 out_port; + u8 state; + u32 expire; +}; + +/* + * NAT64 APIs + */ + +/** \brief Add/delete address range to NAT64 pool + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param start_addr - start address of the range + @param end_addr - end address of the range + @param vrf_id - VRF id of tenant, ~0 means independent of VRF + @param is_add - 1 if add, 0 if delete +*/ +autoreply define nat64_add_del_pool_addr_range { + u32 client_index; + u32 context; + u8 start_addr[4]; + u8 end_addr[4]; + u32 vrf_id; + u8 is_add; +}; + +/** \brief Dump NAT64 pool addresses + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat64_pool_addr_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT64 pool address details response + @param context - sender context, to match reply w/ request + @param address - IPv4 address + @param vfr_id - VRF id of tenant, ~0 means independent of VRF +*/ +define nat64_pool_addr_details { + u32 context; + u8 address[4]; + u32 vrf_id; +}; + +/** \brief Enable/disable NAT64 feature on the interface + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param sw_if_index - index of the interface + @param is_inside - 1 if inside, 0 if outside + @param is_add - 1 if add, 0 if delete +*/ +autoreply define nat64_add_del_interface { + u32 client_index; + u32 context; + u32 sw_if_index; + u8 is_inside; + u8 is_add; +}; + +/** \brief Dump interfaces with NAT64 feature + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat64_interface_dump { + u32 client_index; + u32 context; +}; + +/** \brief NAT64 interface details response + @param context - sender context, to match reply w/ request + @param is_inside - 1 if inside, 0 if outside + @param sw_if_index - index of the interface +*/ +define nat64_interface_details { + u32 context; + u8 is_inside; + u32 sw_if_index; +}; + +/** \brief Add/delete NAT64 static BIB entry + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param i_addr - inside IPv6 address + @param o_addr - outside IPv4 address + @param i_port - inside port number + @param o_port - outside port number + @param vrf_id - VRF id of tenant + @param proto - protocol number + @param is_add - 1 if add, 0 if delete +*/ + autoreply define nat64_add_del_static_bib { + u32 client_index; + u32 context; + u8 i_addr[16]; + u8 o_addr[4]; + u16 i_port; + u16 o_port; + u32 vrf_id; + u8 proto; + u8 is_add; +}; + +/** \brief Dump NAT64 BIB + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param proto - protocol of the BIB: 255 - all BIBs + 6 - TCP BIB + 17 - UDP BIB + 1/58 - ICMP BIB + otherwise - "unknown" protocol BIB +*/ +define nat64_bib_dump { + u32 client_index; + u32 context; + u8 proto; +}; + +/** \brief NAT64 BIB details response + @param context - sender context, to match reply w/ request + @param i_addr - inside IPv6 address + @param o_addr - outside IPv4 address + @param i_port - inside port number + @param o_port - outside port number + @param vrf_id - VRF id of tenant + @param proto - protocol number + @param is_static - 1 if static BIB entry, 0 if dynamic + @param ses_num - number of sessions associated with the BIB entry +*/ +define nat64_bib_details { + u32 context; + u8 i_addr[16]; + u8 o_addr[4]; + u16 i_port; + u16 o_port; + u32 vrf_id; + u8 proto; + u8 is_static; + u32 ses_num; +}; + +/** \brief Set values of timeouts for NAT64 (seconds, 0 = default) + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param udp - UDP timeout (default 300sec) + @param icmp - ICMP timeout (default 60sec) + @param tcp_trans - TCP transitory timeout (default 240sec) + @param tcp_est - TCP established timeout (default 7440sec) + @param tcp_incoming_syn - TCP incoming SYN timeout (default 6sec) +*/ +autoreply define nat64_set_timeouts { + u32 client_index; + u32 context; + u32 udp; + u32 icmp; + u32 tcp_trans; + u32 tcp_est; + u32 tcp_incoming_syn; +}; + +/** \brief Get values of timeouts for NAT64 (seconds) + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat64_get_timeouts { + u32 client_index; + u32 context; +}; + +/** \brief Get values of timeouts for NAT64 reply + @param context - sender context, to match reply w/ request + @param retval - return code + @param udp - UDP timeout + @param icmp - ICMP timeout + @param tcp_trans - TCP transitory timeout + @param tcp_est - TCP established timeout + @param tcp_incoming_syn - TCP incoming SYN timeout +*/ +define nat64_get_timeouts_reply { + u32 context; + i32 retval; + u32 udp; + u32 icmp; + u32 tcp_trans; + u32 tcp_est; + u32 tcp_incoming_syn; +}; + +/** \brief Dump NAT64 session table + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param proto - protocol of the session table: 255 - all STs + 6 - TCP ST + 17 - UDP ST + 1/58 - ICMP ST + otherwise - "unknown" proto ST +*/ +define nat64_st_dump { + u32 client_index; + u32 context; + u8 proto; +}; + +/** \brief NAT64 session table details response + @param context - sender context, to match reply w/ request + @param il_addr - inside IPv6 address of the local host + @param ol_addr - outside IPv4 address of the local host + @param il_port - inside port number id of the local host/inside ICMP id + @param ol_port - outside port number of the local host/outside ICMP id + @param il_addr - inside IPv6 address of the remote host + @param ol_addr - outside IPv4 address of the remote host + @param l_port - port number of the remote host (not used for ICMP) + @param vrf_id - VRF id of tenant + @param proto - protocol number +*/ +define nat64_st_details { + u32 context; + u8 il_addr[16]; + u8 ol_addr[4]; + u16 il_port; + u16 ol_port; + u8 ir_addr[16]; + u8 or_addr[4]; + u16 r_port; + u32 vrf_id; + u8 proto; +}; + +/** \brief Add/del NAT64 prefix + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param prefix - NAT64 prefix + @param prefix - NAT64 prefix length + @param vrf_id - VRF id of tenant + @param is_add - 1 if add, 0 if delete +*/ +autoreply define nat64_add_del_prefix { + u32 client_index; + u32 context; + u8 prefix[16]; + u8 prefix_len; + u32 vrf_id; + u8 is_add; +}; + +/** \brief Dump NAT64 prefix + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define nat64_prefix_dump { + u32 client_index; + u32 context; +}; + +/** \brief Dump NAT64 prefix details response + @param context - sender context, to match reply w/ request + @param prefix - NAT64 prefix + @param prefix - NAT64 prefix length + @param vrf_id - VRF id of tenant +*/ +define nat64_prefix_details { + u32 context; + u8 prefix[16]; + u8 prefix_len; + u32 vrf_id; +}; diff --git a/src/plugins/nat/nat.c b/src/plugins/nat/nat.c new file mode 100644 index 00000000..ac39be95 --- /dev/null +++ b/src/plugins/nat/nat.c @@ -0,0 +1,2842 @@ +/* + * snat.c - simple nat plugin + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +snat_main_t snat_main; + + +/* Hook up input features */ +VNET_FEATURE_INIT (ip4_snat_in2out, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-in2out", + .runs_before = VNET_FEATURES ("nat44-out2in"), +}; +VNET_FEATURE_INIT (ip4_snat_out2in, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-out2in", + .runs_before = VNET_FEATURES ("ip4-lookup"), +}; +VNET_FEATURE_INIT (ip4_snat_det_in2out, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-det-in2out", + .runs_before = VNET_FEATURES ("nat44-det-out2in"), +}; +VNET_FEATURE_INIT (ip4_snat_det_out2in, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-det-out2in", + .runs_before = VNET_FEATURES ("ip4-lookup"), +}; +VNET_FEATURE_INIT (ip4_snat_in2out_worker_handoff, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-in2out-worker-handoff", + .runs_before = VNET_FEATURES ("nat44-out2in-worker-handoff"), +}; +VNET_FEATURE_INIT (ip4_snat_out2in_worker_handoff, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-out2in-worker-handoff", + .runs_before = VNET_FEATURES ("ip4-lookup"), +}; +VNET_FEATURE_INIT (ip4_snat_in2out_fast, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-in2out-fast", + .runs_before = VNET_FEATURES ("nat44-out2in-fast"), +}; +VNET_FEATURE_INIT (ip4_snat_out2in_fast, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-out2in-fast", + .runs_before = VNET_FEATURES ("ip4-lookup"), +}; +VNET_FEATURE_INIT (ip4_snat_hairpin_dst, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat44-hairpin-dst", + .runs_before = VNET_FEATURES ("ip4-lookup"), +}; + +/* Hook up output features */ +VNET_FEATURE_INIT (ip4_snat_in2out_output, static) = { + .arc_name = "ip4-output", + .node_name = "nat44-in2out-output", + .runs_before = VNET_FEATURES ("interface-output"), +}; +VNET_FEATURE_INIT (ip4_snat_in2out_output_worker_handoff, static) = { + .arc_name = "ip4-output", + .node_name = "nat44-in2out-output-worker-handoff", + .runs_before = VNET_FEATURES ("interface-output"), +}; +VNET_FEATURE_INIT (ip4_snat_hairpin_src, static) = { + .arc_name = "ip4-output", + .node_name = "nat44-hairpin-src", + .runs_before = VNET_FEATURES ("interface-output"), +}; + + +/* *INDENT-OFF* */ +VLIB_PLUGIN_REGISTER () = { + .version = VPP_BUILD_VER, + .description = "Network Address Translation", +}; +/* *INDENT-ON* */ + +/** + * @brief Add/del NAT address to FIB. + * + * Add the external NAT address to the FIB as receive entries. This ensures + * that VPP will reply to ARP for this address and we don't need to enable + * proxy ARP on the outside interface. + * + * @param addr IPv4 address. + * @param plen address prefix length + * @param sw_if_index Interface. + * @param is_add If 0 delete, otherwise add. + */ +void +snat_add_del_addr_to_fib (ip4_address_t * addr, u8 p_len, u32 sw_if_index, + int is_add) +{ + fib_prefix_t prefix = { + .fp_len = p_len, + .fp_proto = FIB_PROTOCOL_IP4, + .fp_addr = { + .ip4.as_u32 = addr->as_u32, + }, + }; + u32 fib_index = ip4_fib_table_get_index_for_sw_if_index(sw_if_index); + + if (is_add) + fib_table_entry_update_one_path(fib_index, + &prefix, + FIB_SOURCE_PLUGIN_HI, + (FIB_ENTRY_FLAG_CONNECTED | + FIB_ENTRY_FLAG_LOCAL | + FIB_ENTRY_FLAG_EXCLUSIVE), + DPO_PROTO_IP4, + NULL, + sw_if_index, + ~0, + 1, + NULL, + FIB_ROUTE_PATH_FLAG_NONE); + else + fib_table_entry_delete(fib_index, + &prefix, + FIB_SOURCE_PLUGIN_HI); +} + +void snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id) +{ + snat_address_t * ap; + snat_interface_t *i; + + if (vrf_id != ~0) + sm->vrf_mode = 1; + + /* Check if address already exists */ + vec_foreach (ap, sm->addresses) + { + if (ap->addr.as_u32 == addr->as_u32) + return; + } + + vec_add2 (sm->addresses, ap, 1); + ap->addr = *addr; + if (vrf_id != ~0) + ap->fib_index = + fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id); + else + ap->fib_index = ~0; +#define _(N, i, n, s) \ + clib_bitmap_alloc (ap->busy_##n##_port_bitmap, 65535); + foreach_snat_protocol +#undef _ + + /* Add external address to FIB */ + pool_foreach (i, sm->interfaces, + ({ + if (i->is_inside) + continue; + + snat_add_del_addr_to_fib(addr, 32, i->sw_if_index, 1); + break; + })); + pool_foreach (i, sm->output_feature_interfaces, + ({ + if (i->is_inside) + continue; + + snat_add_del_addr_to_fib(addr, 32, i->sw_if_index, 1); + break; + })); +} + +static int is_snat_address_used_in_static_mapping (snat_main_t *sm, + ip4_address_t addr) +{ + snat_static_mapping_t *m; + pool_foreach (m, sm->static_mappings, + ({ + if (m->external_addr.as_u32 == addr.as_u32) + return 1; + })); + + return 0; +} + +void increment_v4_address (ip4_address_t * a) +{ + u32 v; + + v = clib_net_to_host_u32(a->as_u32) + 1; + a->as_u32 = clib_host_to_net_u32(v); +} + +static void +snat_add_static_mapping_when_resolved (snat_main_t * sm, + ip4_address_t l_addr, + u16 l_port, + u32 sw_if_index, + u16 e_port, + u32 vrf_id, + snat_protocol_t proto, + int addr_only, + int is_add) +{ + snat_static_map_resolve_t *rp; + + vec_add2 (sm->to_resolve, rp, 1); + rp->l_addr.as_u32 = l_addr.as_u32; + rp->l_port = l_port; + rp->sw_if_index = sw_if_index; + rp->e_port = e_port; + rp->vrf_id = vrf_id; + rp->proto = proto; + rp->addr_only = addr_only; + rp->is_add = is_add; +} + +/** + * @brief Add static mapping. + * + * Create static mapping between local addr+port and external addr+port. + * + * @param l_addr Local IPv4 address. + * @param e_addr External IPv4 address. + * @param l_port Local port number. + * @param e_port External port number. + * @param vrf_id VRF ID. + * @param addr_only If 0 address port and pair mapping, otherwise address only. + * @param sw_if_index External port instead of specific IP address. + * @param is_add If 0 delete static mapping, otherwise add. + * + * @returns + */ +int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, + u16 l_port, u16 e_port, u32 vrf_id, int addr_only, + u32 sw_if_index, snat_protocol_t proto, int is_add) +{ + snat_main_t * sm = &snat_main; + snat_static_mapping_t *m; + snat_session_key_t m_key; + clib_bihash_kv_8_8_t kv, value; + snat_address_t *a = 0; + u32 fib_index = ~0; + uword * p; + snat_interface_t *interface; + int i; + + /* If the external address is a specific interface address */ + if (sw_if_index != ~0) + { + ip4_address_t * first_int_addr; + + /* Might be already set... */ + first_int_addr = ip4_interface_first_address + (sm->ip4_main, sw_if_index, 0 /* just want the address*/); + + /* DHCP resolution required? */ + if (first_int_addr == 0) + { + snat_add_static_mapping_when_resolved + (sm, l_addr, l_port, sw_if_index, e_port, vrf_id, proto, + addr_only, is_add); + return 0; + } + else + e_addr.as_u32 = first_int_addr->as_u32; + } + + m_key.addr = e_addr; + m_key.port = addr_only ? 0 : e_port; + m_key.protocol = addr_only ? 0 : proto; + m_key.fib_index = sm->outside_fib_index; + kv.key = m_key.as_u64; + if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) + m = 0; + else + m = pool_elt_at_index (sm->static_mappings, value.value); + + if (is_add) + { + if (m) + return VNET_API_ERROR_VALUE_EXIST; + + /* Convert VRF id to FIB index */ + if (vrf_id != ~0) + { + p = hash_get (sm->ip4_main->fib_index_by_table_id, vrf_id); + if (!p) + return VNET_API_ERROR_NO_SUCH_FIB; + fib_index = p[0]; + } + /* If not specified use inside VRF id from SNAT plugin startup config */ + else + { + fib_index = sm->inside_fib_index; + vrf_id = sm->inside_vrf_id; + } + + /* Find external address in allocated addresses and reserve port for + address and port pair mapping when dynamic translations enabled */ + if (!addr_only && !(sm->static_mapping_only)) + { + for (i = 0; i < vec_len (sm->addresses); i++) + { + if (sm->addresses[i].addr.as_u32 == e_addr.as_u32) + { + a = sm->addresses + i; + /* External port must be unused */ + switch (proto) + { +#define _(N, j, n, s) \ + case SNAT_PROTOCOL_##N: \ + if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, e_port)) \ + return VNET_API_ERROR_INVALID_VALUE; \ + clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 1); \ + if (e_port > 1024) \ + a->busy_##n##_ports++; \ + break; + foreach_snat_protocol +#undef _ + default: + clib_warning("unknown_protocol"); + return VNET_API_ERROR_INVALID_VALUE_2; + } + break; + } + } + /* External address must be allocated */ + if (!a) + return VNET_API_ERROR_NO_SUCH_ENTRY; + } + + pool_get (sm->static_mappings, m); + memset (m, 0, sizeof (*m)); + m->local_addr = l_addr; + m->external_addr = e_addr; + m->addr_only = addr_only; + m->vrf_id = vrf_id; + m->fib_index = fib_index; + if (!addr_only) + { + m->local_port = l_port; + m->external_port = e_port; + m->proto = proto; + } + + m_key.addr = m->local_addr; + m_key.port = m->local_port; + m_key.protocol = m->proto; + m_key.fib_index = m->fib_index; + kv.key = m_key.as_u64; + kv.value = m - sm->static_mappings; + clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 1); + + m_key.addr = m->external_addr; + m_key.port = m->external_port; + m_key.fib_index = sm->outside_fib_index; + kv.key = m_key.as_u64; + kv.value = m - sm->static_mappings; + clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 1); + + /* Assign worker */ + if (sm->workers) + { + snat_user_key_t w_key0; + snat_worker_key_t w_key1; + + w_key0.addr = m->local_addr; + w_key0.fib_index = m->fib_index; + kv.key = w_key0.as_u64; + + if (clib_bihash_search_8_8 (&sm->worker_by_in, &kv, &value)) + { + kv.value = sm->first_worker_index + + sm->workers[sm->next_worker++ % vec_len (sm->workers)]; + + clib_bihash_add_del_8_8 (&sm->worker_by_in, &kv, 1); + } + else + { + kv.value = value.value; + } + + w_key1.addr = m->external_addr; + w_key1.port = clib_host_to_net_u16 (m->external_port); + w_key1.fib_index = sm->outside_fib_index; + kv.key = w_key1.as_u64; + clib_bihash_add_del_8_8 (&sm->worker_by_out, &kv, 1); + } + } + else + { + if (!m) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + /* Free external address port */ + if (!addr_only && !(sm->static_mapping_only)) + { + for (i = 0; i < vec_len (sm->addresses); i++) + { + if (sm->addresses[i].addr.as_u32 == e_addr.as_u32) + { + a = sm->addresses + i; + switch (proto) + { +#define _(N, j, n, s) \ + case SNAT_PROTOCOL_##N: \ + clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 0); \ + if (e_port > 1024) \ + a->busy_##n##_ports--; \ + break; + foreach_snat_protocol +#undef _ + default: + clib_warning("unknown_protocol"); + return VNET_API_ERROR_INVALID_VALUE_2; + } + break; + } + } + } + + m_key.addr = m->local_addr; + m_key.port = m->local_port; + m_key.protocol = m->proto; + m_key.fib_index = m->fib_index; + kv.key = m_key.as_u64; + clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0); + + m_key.addr = m->external_addr; + m_key.port = m->external_port; + m_key.fib_index = sm->outside_fib_index; + kv.key = m_key.as_u64; + clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 0); + + /* Delete session(s) for static mapping if exist */ + if (!(sm->static_mapping_only) || + (sm->static_mapping_only && sm->static_mapping_connection_tracking)) + { + snat_user_key_t u_key; + snat_user_t *u; + dlist_elt_t * head, * elt; + u32 elt_index, head_index, del_elt_index; + u32 ses_index; + u64 user_index; + snat_session_t * s; + snat_main_per_thread_data_t *tsm; + + u_key.addr = m->local_addr; + u_key.fib_index = m->fib_index; + kv.key = u_key.as_u64; + if (!clib_bihash_search_8_8 (&sm->user_hash, &kv, &value)) + { + user_index = value.value; + if (!clib_bihash_search_8_8 (&sm->worker_by_in, &kv, &value)) + tsm = vec_elt_at_index (sm->per_thread_data, value.value); + else + tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); + u = pool_elt_at_index (tsm->users, user_index); + if (u->nstaticsessions) + { + head_index = u->sessions_per_user_list_head_index; + head = pool_elt_at_index (tsm->list_pool, head_index); + elt_index = head->next; + elt = pool_elt_at_index (tsm->list_pool, elt_index); + ses_index = elt->value; + while (ses_index != ~0) + { + s = pool_elt_at_index (tsm->sessions, ses_index); + del_elt_index = elt_index; + elt_index = elt->next; + elt = pool_elt_at_index (tsm->list_pool, elt_index); + ses_index = elt->value; + + if (!addr_only) + { + if ((s->out2in.addr.as_u32 != e_addr.as_u32) && + (clib_net_to_host_u16 (s->out2in.port) != e_port)) + continue; + } + + if (snat_is_unk_proto_session (s)) + { + clib_bihash_kv_16_8_t up_kv; + snat_unk_proto_ses_key_t up_key; + up_key.l_addr = s->in2out.addr; + up_key.r_addr = s->ext_host_addr; + up_key.fib_index = s->in2out.fib_index; + up_key.proto = s->in2out.port; + up_key.rsvd[0] = up_key.rsvd[1] = up_key.rsvd[2] = 0; + up_kv.key[0] = up_key.as_u64[0]; + up_kv.key[1] = up_key.as_u64[1]; + if (clib_bihash_add_del_16_8 (&sm->in2out_unk_proto, + &up_kv, 0)) + clib_warning ("in2out key del failed"); + + up_key.l_addr = s->out2in.addr; + up_key.fib_index = s->out2in.fib_index; + up_kv.key[0] = up_key.as_u64[0]; + up_kv.key[1] = up_key.as_u64[1]; + if (clib_bihash_add_del_16_8 (&sm->out2in_unk_proto, + &up_kv, 0)) + clib_warning ("out2in key del failed"); + + goto delete; + } + /* log NAT event */ + snat_ipfix_logging_nat44_ses_delete(s->in2out.addr.as_u32, + s->out2in.addr.as_u32, + s->in2out.protocol, + s->in2out.port, + s->out2in.port, + s->in2out.fib_index); + + value.key = s->in2out.as_u64; + if (clib_bihash_add_del_8_8 (&sm->in2out, &value, 0)) + clib_warning ("in2out key del failed"); + value.key = s->out2in.as_u64; + if (clib_bihash_add_del_8_8 (&sm->out2in, &value, 0)) + clib_warning ("out2in key del failed"); +delete: + pool_put (tsm->sessions, s); + + clib_dlist_remove (tsm->list_pool, del_elt_index); + pool_put_index (tsm->list_pool, del_elt_index); + u->nstaticsessions--; + + if (!addr_only) + break; + } + if (addr_only) + { + pool_put (tsm->users, u); + clib_bihash_add_del_8_8 (&sm->user_hash, &kv, 0); + } + } + } + } + + /* Delete static mapping from pool */ + pool_put (sm->static_mappings, m); + } + + if (!addr_only) + return 0; + + /* Add/delete external address to FIB */ + pool_foreach (interface, sm->interfaces, + ({ + if (interface->is_inside) + continue; + + snat_add_del_addr_to_fib(&e_addr, 32, interface->sw_if_index, is_add); + break; + })); + pool_foreach (interface, sm->output_feature_interfaces, + ({ + if (interface->is_inside) + continue; + + snat_add_del_addr_to_fib(&e_addr, 32, interface->sw_if_index, is_add); + break; + })); + + return 0; +} + +int snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm) +{ + snat_address_t *a = 0; + snat_session_t *ses; + u32 *ses_to_be_removed = 0, *ses_index; + clib_bihash_kv_8_8_t kv, value; + snat_user_key_t user_key; + snat_user_t *u; + snat_main_per_thread_data_t *tsm; + snat_static_mapping_t *m; + snat_interface_t *interface; + int i; + + /* Find SNAT address */ + for (i=0; i < vec_len (sm->addresses); i++) + { + if (sm->addresses[i].addr.as_u32 == addr.as_u32) + { + a = sm->addresses + i; + break; + } + } + if (!a) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + if (delete_sm) + { + pool_foreach (m, sm->static_mappings, + ({ + if (m->external_addr.as_u32 == addr.as_u32) + (void) snat_add_static_mapping (m->local_addr, m->external_addr, + m->local_port, m->external_port, + m->vrf_id, m->addr_only, ~0, + m->proto, 0); + })); + } + else + { + /* Check if address is used in some static mapping */ + if (is_snat_address_used_in_static_mapping(sm, addr)) + { + clib_warning ("address used in static mapping"); + return VNET_API_ERROR_UNSPECIFIED; + } + } + + if (a->fib_index != ~0) + fib_table_unlock(a->fib_index, FIB_PROTOCOL_IP4); + + /* Delete sessions using address */ + if (a->busy_tcp_ports || a->busy_udp_ports || a->busy_icmp_ports) + { + vec_foreach (tsm, sm->per_thread_data) + { + pool_foreach (ses, tsm->sessions, ({ + if (ses->out2in.addr.as_u32 == addr.as_u32) + { + if (snat_is_unk_proto_session (ses)) + { + clib_bihash_kv_16_8_t up_kv; + snat_unk_proto_ses_key_t up_key; + up_key.l_addr = ses->in2out.addr; + up_key.r_addr = ses->ext_host_addr; + up_key.fib_index = ses->in2out.fib_index; + up_key.proto = ses->in2out.port; + up_key.rsvd[0] = up_key.rsvd[1] = up_key.rsvd[2] = 0; + up_kv.key[0] = up_key.as_u64[0]; + up_kv.key[1] = up_key.as_u64[1]; + if (clib_bihash_add_del_16_8 (&sm->in2out_unk_proto, + &up_kv, 0)) + clib_warning ("in2out key del failed"); + + up_key.l_addr = ses->out2in.addr; + up_key.fib_index = ses->out2in.fib_index; + up_kv.key[0] = up_key.as_u64[0]; + up_kv.key[1] = up_key.as_u64[1]; + if (clib_bihash_add_del_16_8 (&sm->out2in_unk_proto, + &up_kv, 0)) + clib_warning ("out2in key del failed"); + } + else + { + /* log NAT event */ + snat_ipfix_logging_nat44_ses_delete(ses->in2out.addr.as_u32, + ses->out2in.addr.as_u32, + ses->in2out.protocol, + ses->in2out.port, + ses->out2in.port, + ses->in2out.fib_index); + kv.key = ses->in2out.as_u64; + clib_bihash_add_del_8_8 (&sm->in2out, &kv, 0); + kv.key = ses->out2in.as_u64; + clib_bihash_add_del_8_8 (&sm->out2in, &kv, 0); + } + vec_add1 (ses_to_be_removed, ses - tsm->sessions); + clib_dlist_remove (tsm->list_pool, ses->per_user_index); + user_key.addr = ses->in2out.addr; + user_key.fib_index = ses->in2out.fib_index; + kv.key = user_key.as_u64; + if (!clib_bihash_search_8_8 (&sm->user_hash, &kv, &value)) + { + u = pool_elt_at_index (tsm->users, value.value); + u->nsessions--; + } + } + })); + + vec_foreach (ses_index, ses_to_be_removed) + pool_put_index (tsm->sessions, ses_index[0]); + + vec_free (ses_to_be_removed); + } + } + + vec_del1 (sm->addresses, i); + + /* Delete external address from FIB */ + pool_foreach (interface, sm->interfaces, + ({ + if (interface->is_inside) + continue; + + snat_add_del_addr_to_fib(&addr, 32, interface->sw_if_index, 0); + break; + })); + pool_foreach (interface, sm->output_feature_interfaces, + ({ + if (interface->is_inside) + continue; + + snat_add_del_addr_to_fib(&addr, 32, interface->sw_if_index, 0); + break; + })); + + return 0; +} + +int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del) +{ + snat_main_t *sm = &snat_main; + snat_interface_t *i; + const char * feature_name; + snat_address_t * ap; + snat_static_mapping_t * m; + snat_det_map_t * dm; + + if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking)) + feature_name = is_inside ? "nat44-in2out-fast" : "nat44-out2in-fast"; + else + { + if (sm->num_workers > 1 && !sm->deterministic) + feature_name = is_inside ? "nat44-in2out-worker-handoff" : "nat44-out2in-worker-handoff"; + else if (sm->deterministic) + feature_name = is_inside ? "nat44-det-in2out" : "nat44-det-out2in"; + else + feature_name = is_inside ? "nat44-in2out" : "nat44-out2in"; + } + + vnet_feature_enable_disable ("ip4-unicast", feature_name, sw_if_index, + !is_del, 0, 0); + + if (sm->fq_in2out_index == ~0 && !sm->deterministic && sm->num_workers > 1) + sm->fq_in2out_index = vlib_frame_queue_main_init (sm->in2out_node_index, 0); + + if (sm->fq_out2in_index == ~0 && !sm->deterministic && sm->num_workers > 1) + sm->fq_out2in_index = vlib_frame_queue_main_init (sm->out2in_node_index, 0); + + pool_foreach (i, sm->interfaces, + ({ + if (i->sw_if_index == sw_if_index) + { + if (is_del) + pool_put (sm->interfaces, i); + else + return VNET_API_ERROR_VALUE_EXIST; + + goto fib; + } + })); + + if (is_del) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + pool_get (sm->interfaces, i); + i->sw_if_index = sw_if_index; + i->is_inside = is_inside; + + /* Add/delete external addresses to FIB */ +fib: + if (is_inside) + return 0; + + vec_foreach (ap, sm->addresses) + snat_add_del_addr_to_fib(&ap->addr, 32, sw_if_index, !is_del); + + pool_foreach (m, sm->static_mappings, + ({ + if (!(m->addr_only)) + continue; + + snat_add_del_addr_to_fib(&m->external_addr, 32, sw_if_index, !is_del); + })); + + pool_foreach (dm, sm->det_maps, + ({ + snat_add_del_addr_to_fib(&dm->out_addr, dm->out_plen, sw_if_index, !is_del); + })); + + return 0; +} + +int snat_interface_add_del_output_feature (u32 sw_if_index, + u8 is_inside, + int is_del) +{ + snat_main_t *sm = &snat_main; + snat_interface_t *i; + snat_address_t * ap; + snat_static_mapping_t * m; + + if (sm->deterministic || + (sm->static_mapping_only && !(sm->static_mapping_connection_tracking))) + return VNET_API_ERROR_UNSUPPORTED; + + if (is_inside) + { + vnet_feature_enable_disable ("ip4-unicast", "nat44-hairpin-dst", + sw_if_index, !is_del, 0, 0); + vnet_feature_enable_disable ("ip4-output", "nat44-hairpin-src", + sw_if_index, !is_del, 0, 0); + goto fq; + } + + if (sm->num_workers > 1) + { + vnet_feature_enable_disable ("ip4-unicast", "nat44-out2in-worker-handoff", + sw_if_index, !is_del, 0, 0); + vnet_feature_enable_disable ("ip4-output", + "nat44-in2out-output-worker-handoff", + sw_if_index, !is_del, 0, 0); + } + else + { + vnet_feature_enable_disable ("ip4-unicast", "nat44-out2in", sw_if_index, + !is_del, 0, 0); + vnet_feature_enable_disable ("ip4-output", "nat44-in2out-output", + sw_if_index, !is_del, 0, 0); + } + +fq: + if (sm->fq_in2out_output_index == ~0 && sm->num_workers > 1) + sm->fq_in2out_output_index = + vlib_frame_queue_main_init (sm->in2out_output_node_index, 0); + + if (sm->fq_out2in_index == ~0 && sm->num_workers > 1) + sm->fq_out2in_index = vlib_frame_queue_main_init (sm->out2in_node_index, 0); + + pool_foreach (i, sm->output_feature_interfaces, + ({ + if (i->sw_if_index == sw_if_index) + { + if (is_del) + pool_put (sm->output_feature_interfaces, i); + else + return VNET_API_ERROR_VALUE_EXIST; + + goto fib; + } + })); + + if (is_del) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + pool_get (sm->output_feature_interfaces, i); + i->sw_if_index = sw_if_index; + i->is_inside = is_inside; + + /* Add/delete external addresses to FIB */ +fib: + if (is_inside) + return 0; + + vec_foreach (ap, sm->addresses) + snat_add_del_addr_to_fib(&ap->addr, 32, sw_if_index, !is_del); + + pool_foreach (m, sm->static_mappings, + ({ + if (!(m->addr_only)) + continue; + + snat_add_del_addr_to_fib(&m->external_addr, 32, sw_if_index, !is_del); + })); + + return 0; +} + +int snat_set_workers (uword * bitmap) +{ + snat_main_t *sm = &snat_main; + int i, j = 0; + + if (sm->num_workers < 2) + return VNET_API_ERROR_FEATURE_DISABLED; + + if (clib_bitmap_last_set (bitmap) >= sm->num_workers) + return VNET_API_ERROR_INVALID_WORKER; + + vec_free (sm->workers); + clib_bitmap_foreach (i, bitmap, + ({ + vec_add1(sm->workers, i); + sm->per_thread_data[i].snat_thread_index = j; + j++; + })); + + sm->port_per_thread = (0xffff - 1024) / _vec_len (sm->workers); + sm->num_snat_thread = _vec_len (sm->workers); + + return 0; +} + + +static void +snat_ip4_add_del_interface_address_cb (ip4_main_t * im, + uword opaque, + u32 sw_if_index, + ip4_address_t * address, + u32 address_length, + u32 if_address_index, + u32 is_delete); + +static clib_error_t * snat_init (vlib_main_t * vm) +{ + snat_main_t * sm = &snat_main; + clib_error_t * error = 0; + ip4_main_t * im = &ip4_main; + ip_lookup_main_t * lm = &im->lookup_main; + uword *p; + vlib_thread_registration_t *tr; + vlib_thread_main_t *tm = vlib_get_thread_main (); + uword *bitmap = 0; + u32 i; + ip4_add_del_interface_address_callback_t cb4; + + sm->vlib_main = vm; + sm->vnet_main = vnet_get_main(); + sm->ip4_main = im; + sm->ip4_lookup_main = lm; + sm->api_main = &api_main; + sm->first_worker_index = 0; + sm->next_worker = 0; + sm->num_workers = 0; + sm->num_snat_thread = 1; + sm->workers = 0; + sm->port_per_thread = 0xffff - 1024; + sm->fq_in2out_index = ~0; + sm->fq_out2in_index = ~0; + sm->udp_timeout = SNAT_UDP_TIMEOUT; + sm->tcp_established_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT; + sm->tcp_transitory_timeout = SNAT_TCP_TRANSITORY_TIMEOUT; + sm->icmp_timeout = SNAT_ICMP_TIMEOUT; + + p = hash_get_mem (tm->thread_registrations_by_name, "workers"); + if (p) + { + tr = (vlib_thread_registration_t *) p[0]; + if (tr) + { + sm->num_workers = tr->count; + sm->first_worker_index = tr->first_index; + } + } + + vec_validate (sm->per_thread_data, tm->n_vlib_mains - 1); + + /* Use all available workers by default */ + if (sm->num_workers > 1) + { + for (i=0; i < sm->num_workers; i++) + bitmap = clib_bitmap_set (bitmap, i, 1); + snat_set_workers(bitmap); + clib_bitmap_free (bitmap); + } + else + { + sm->per_thread_data[0].snat_thread_index = 0; + } + + error = snat_api_init(vm, sm); + if (error) + return error; + + /* Set up the interface address add/del callback */ + cb4.function = snat_ip4_add_del_interface_address_cb; + cb4.function_opaque = 0; + + vec_add1 (im->add_del_interface_address_callbacks, cb4); + + /* Init IPFIX logging */ + snat_ipfix_logging_init(vm); + + error = nat64_init(vm); + + return error; +} + +VLIB_INIT_FUNCTION (snat_init); + +void snat_free_outside_address_and_port (snat_main_t * sm, + snat_session_key_t * k, + u32 address_index) +{ + snat_address_t *a; + u16 port_host_byte_order = clib_net_to_host_u16 (k->port); + + ASSERT (address_index < vec_len (sm->addresses)); + + a = sm->addresses + address_index; + + switch (k->protocol) + { +#define _(N, i, n, s) \ + case SNAT_PROTOCOL_##N: \ + ASSERT (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \ + port_host_byte_order) == 1); \ + clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \ + port_host_byte_order, 0); \ + a->busy_##n##_ports--; \ + break; + foreach_snat_protocol +#undef _ + default: + clib_warning("unknown_protocol"); + return; + } +} + +/** + * @brief Match NAT44 static mapping. + * + * @param sm NAT main. + * @param match Address and port to match. + * @param mapping External or local address and port of the matched mapping. + * @param by_external If 0 match by local address otherwise match by external + * address. + * @param is_addr_only If matched mapping is address only + * + * @returns 0 if match found otherwise 1. + */ +int snat_static_mapping_match (snat_main_t * sm, + snat_session_key_t match, + snat_session_key_t * mapping, + u8 by_external, + u8 *is_addr_only) +{ + clib_bihash_kv_8_8_t kv, value; + snat_static_mapping_t *m; + snat_session_key_t m_key; + clib_bihash_8_8_t *mapping_hash = &sm->static_mapping_by_local; + + if (by_external) + mapping_hash = &sm->static_mapping_by_external; + + m_key.addr = match.addr; + m_key.port = clib_net_to_host_u16 (match.port); + m_key.protocol = match.protocol; + m_key.fib_index = match.fib_index; + + kv.key = m_key.as_u64; + + if (clib_bihash_search_8_8 (mapping_hash, &kv, &value)) + { + /* Try address only mapping */ + m_key.port = 0; + m_key.protocol = 0; + kv.key = m_key.as_u64; + if (clib_bihash_search_8_8 (mapping_hash, &kv, &value)) + return 1; + } + + m = pool_elt_at_index (sm->static_mappings, value.value); + + if (by_external) + { + mapping->addr = m->local_addr; + /* Address only mapping doesn't change port */ + mapping->port = m->addr_only ? match.port + : clib_host_to_net_u16 (m->local_port); + mapping->fib_index = m->fib_index; + } + else + { + mapping->addr = m->external_addr; + /* Address only mapping doesn't change port */ + mapping->port = m->addr_only ? match.port + : clib_host_to_net_u16 (m->external_port); + mapping->fib_index = sm->outside_fib_index; + } + + if (PREDICT_FALSE(is_addr_only != 0)) + *is_addr_only = m->addr_only; + + return 0; +} + +static_always_inline u16 +snat_random_port (snat_main_t * sm, u16 min, u16 max) +{ + return min + random_u32 (&sm->random_seed) / + (random_u32_max() / (max - min + 1) + 1); +} + +int snat_alloc_outside_address_and_port (snat_main_t * sm, + u32 fib_index, + u32 thread_index, + snat_session_key_t * k, + u32 * address_indexp) +{ + int i; + snat_address_t *a; + u32 portnum; + + for (i = 0; i < vec_len (sm->addresses); i++) + { + a = sm->addresses + i; + if (sm->vrf_mode && a->fib_index != ~0 && a->fib_index != fib_index) + continue; + switch (k->protocol) + { +#define _(N, j, n, s) \ + case SNAT_PROTOCOL_##N: \ + if (a->busy_##n##_ports < (sm->port_per_thread * sm->num_snat_thread)) \ + { \ + while (1) \ + { \ + portnum = (sm->port_per_thread * \ + sm->per_thread_data[thread_index].snat_thread_index) + \ + snat_random_port(sm, 0, sm->port_per_thread) + 1024; \ + if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \ + continue; \ + clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \ + a->busy_##n##_ports++; \ + k->addr = a->addr; \ + k->port = clib_host_to_net_u16(portnum); \ + *address_indexp = i; \ + return 0; \ + } \ + } \ + break; + foreach_snat_protocol +#undef _ + default: + clib_warning("unknown protocol"); + return 1; + } + + } + /* Totally out of translations to use... */ + snat_ipfix_logging_addresses_exhausted(0); + return 1; +} + + +static clib_error_t * +add_address_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + snat_main_t * sm = &snat_main; + ip4_address_t start_addr, end_addr, this_addr; + u32 start_host_order, end_host_order; + u32 vrf_id = ~0; + int i, count; + int is_add = 1; + int rv = 0; + clib_error_t *error = 0; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "%U - %U", + unformat_ip4_address, &start_addr, + unformat_ip4_address, &end_addr)) + ; + else if (unformat (line_input, "tenant-vrf %u", &vrf_id)) + ; + else if (unformat (line_input, "%U", unformat_ip4_address, &start_addr)) + end_addr = start_addr; + else if (unformat (line_input, "del")) + is_add = 0; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (sm->static_mapping_only) + { + error = clib_error_return (0, "static mapping only mode"); + goto done; + } + + 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) + { + error = clib_error_return (0, "end address less than start address"); + goto done; + } + + count = (end_host_order - start_host_order) + 1; + + if (count > 1024) + clib_warning ("%U - %U, %d addresses...", + format_ip4_address, &start_addr, + format_ip4_address, &end_addr, + count); + + this_addr = start_addr; + + for (i = 0; i < count; i++) + { + if (is_add) + snat_add_address (sm, &this_addr, vrf_id); + else + rv = snat_del_address (sm, this_addr, 0); + + switch (rv) + { + case VNET_API_ERROR_NO_SUCH_ENTRY: + error = clib_error_return (0, "S-NAT address not exist."); + goto done; + case VNET_API_ERROR_UNSPECIFIED: + error = clib_error_return (0, "S-NAT address used in static mapping."); + goto done; + default: + break; + } + + increment_v4_address (&this_addr); + } + +done: + unformat_free (line_input); + + return error; +} + +VLIB_CLI_COMMAND (add_address_command, static) = { + .path = "nat44 add address", + .short_help = "nat44 add addresses [- ] " + "[tenant-vrf ] [del]", + .function = add_address_command_fn, +}; + +static clib_error_t * +snat_feature_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + vnet_main_t * vnm = vnet_get_main(); + clib_error_t * error = 0; + u32 sw_if_index; + u32 * inside_sw_if_indices = 0; + u32 * outside_sw_if_indices = 0; + u8 is_output_feature = 0; + int is_del = 0; + int i; + + sw_if_index = ~0; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "in %U", unformat_vnet_sw_interface, + vnm, &sw_if_index)) + vec_add1 (inside_sw_if_indices, sw_if_index); + else if (unformat (line_input, "out %U", unformat_vnet_sw_interface, + vnm, &sw_if_index)) + vec_add1 (outside_sw_if_indices, sw_if_index); + else if (unformat (line_input, "output-feature")) + is_output_feature = 1; + else if (unformat (line_input, "del")) + is_del = 1; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (vec_len (inside_sw_if_indices)) + { + for (i = 0; i < vec_len(inside_sw_if_indices); i++) + { + sw_if_index = inside_sw_if_indices[i]; + if (is_output_feature) + { + if (snat_interface_add_del_output_feature (sw_if_index, 1, is_del)) + { + error = clib_error_return (0, "%s %U failed", + is_del ? "del" : "add", + format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, + sw_if_index)); + goto done; + } + } + else + { + if (snat_interface_add_del (sw_if_index, 1, is_del)) + { + error = clib_error_return (0, "%s %U failed", + is_del ? "del" : "add", + format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, + sw_if_index)); + goto done; + } + } + } + } + + if (vec_len (outside_sw_if_indices)) + { + for (i = 0; i < vec_len(outside_sw_if_indices); i++) + { + sw_if_index = outside_sw_if_indices[i]; + if (is_output_feature) + { + if (snat_interface_add_del_output_feature (sw_if_index, 0, is_del)) + { + error = clib_error_return (0, "%s %U failed", + is_del ? "del" : "add", + format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, + sw_if_index)); + goto done; + } + } + else + { + if (snat_interface_add_del (sw_if_index, 0, is_del)) + { + error = clib_error_return (0, "%s %U failed", + is_del ? "del" : "add", + format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, + sw_if_index)); + goto done; + } + } + } + } + +done: + unformat_free (line_input); + vec_free (inside_sw_if_indices); + vec_free (outside_sw_if_indices); + + return error; +} + +VLIB_CLI_COMMAND (set_interface_snat_command, static) = { + .path = "set interface nat44", + .function = snat_feature_command_fn, + .short_help = "set interface nat44 in out [output-feature] " + "[del]", +}; + +uword +unformat_snat_protocol (unformat_input_t * input, va_list * args) +{ + u32 *r = va_arg (*args, u32 *); + + if (0); +#define _(N, i, n, s) else if (unformat (input, s)) *r = SNAT_PROTOCOL_##N; + foreach_snat_protocol +#undef _ + else + return 0; + return 1; +} + +u8 * +format_snat_protocol (u8 * s, va_list * args) +{ + u32 i = va_arg (*args, u32); + u8 *t = 0; + + switch (i) + { +#define _(N, j, n, str) case SNAT_PROTOCOL_##N: t = (u8 *) str; break; + foreach_snat_protocol +#undef _ + default: + s = format (s, "unknown"); + return s; + } + s = format (s, "%s", t); + return s; +} + +static clib_error_t * +add_static_mapping_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t * error = 0; + ip4_address_t l_addr, e_addr; + u32 l_port = 0, e_port = 0, vrf_id = ~0; + int is_add = 1; + int addr_only = 1; + u32 sw_if_index = ~0; + vnet_main_t * vnm = vnet_get_main(); + int rv; + snat_protocol_t proto; + u8 proto_set = 0; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "local %U %u", unformat_ip4_address, &l_addr, + &l_port)) + addr_only = 0; + else if (unformat (line_input, "local %U", unformat_ip4_address, &l_addr)) + ; + else if (unformat (line_input, "external %U %u", unformat_ip4_address, + &e_addr, &e_port)) + addr_only = 0; + else if (unformat (line_input, "external %U", unformat_ip4_address, + &e_addr)) + ; + else if (unformat (line_input, "external %U %u", + unformat_vnet_sw_interface, vnm, &sw_if_index, + &e_port)) + addr_only = 0; + + else if (unformat (line_input, "external %U", + unformat_vnet_sw_interface, vnm, &sw_if_index)) + ; + else if (unformat (line_input, "vrf %u", &vrf_id)) + ; + else if (unformat (line_input, "%U", unformat_snat_protocol, &proto)) + proto_set = 1; + else if (unformat (line_input, "del")) + is_add = 0; + else + { + error = clib_error_return (0, "unknown input: '%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (!addr_only && !proto_set) + { + error = clib_error_return (0, "missing protocol"); + goto done; + } + + rv = snat_add_static_mapping(l_addr, e_addr, (u16) l_port, (u16) e_port, + vrf_id, addr_only, sw_if_index, proto, is_add); + + switch (rv) + { + case VNET_API_ERROR_INVALID_VALUE: + error = clib_error_return (0, "External port already in use."); + goto done; + case VNET_API_ERROR_NO_SUCH_ENTRY: + if (is_add) + error = clib_error_return (0, "External addres must be allocated."); + else + error = clib_error_return (0, "Mapping not exist."); + goto done; + case VNET_API_ERROR_NO_SUCH_FIB: + error = clib_error_return (0, "No such VRF id."); + goto done; + case VNET_API_ERROR_VALUE_EXIST: + error = clib_error_return (0, "Mapping already exist."); + goto done; + default: + break; + } + +done: + unformat_free (line_input); + + return error; +} + +/*? + * @cliexpar + * @cliexstart{snat add static mapping} + * Static mapping allows hosts on the external network to initiate connection + * to to the local network host. + * To create static mapping between local host address 10.0.0.3 port 6303 and + * external address 4.4.4.4 port 3606 for TCP protocol use: + * vpp# nat44 add static mapping local tcp 10.0.0.3 6303 external 4.4.4.4 3606 + * If not runnig "static mapping only" NAT plugin mode use before: + * vpp# nat44 add address 4.4.4.4 + * To create static mapping between local and external address use: + * vpp# nat44 add static mapping local 10.0.0.3 external 4.4.4.4 + * @cliexend +?*/ +VLIB_CLI_COMMAND (add_static_mapping_command, static) = { + .path = "nat44 add static mapping", + .function = add_static_mapping_command_fn, + .short_help = + "nat44 add static mapping local tcp|udp|icmp [] external [] [vrf ] [del]", +}; + +static clib_error_t * +set_workers_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + uword *bitmap = 0; + int rv = 0; + clib_error_t *error = 0; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "%U", unformat_bitmap_list, &bitmap)) + ; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (bitmap == 0) + { + error = clib_error_return (0, "List of workers must be specified."); + goto done; + } + + rv = snat_set_workers(bitmap); + + clib_bitmap_free (bitmap); + + switch (rv) + { + case VNET_API_ERROR_INVALID_WORKER: + error = clib_error_return (0, "Invalid worker(s)."); + goto done; + case VNET_API_ERROR_FEATURE_DISABLED: + error = clib_error_return (0, + "Supported only if 2 or more workes available."); + goto done; + default: + break; + } + +done: + unformat_free (line_input); + + return error; +} + +/*? + * @cliexpar + * @cliexstart{set snat workers} + * Set NAT workers if 2 or more workers available, use: + * vpp# set snat workers 0-2,5 + * @cliexend +?*/ +VLIB_CLI_COMMAND (set_workers_command, static) = { + .path = "set nat workers", + .function = set_workers_command_fn, + .short_help = + "set nat workers ", +}; + +static clib_error_t * +snat_ipfix_logging_enable_disable_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + unformat_input_t _line_input, *line_input = &_line_input; + u32 domain_id = 0; + u32 src_port = 0; + u8 enable = 1; + int rv = 0; + clib_error_t *error = 0; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "domain %d", &domain_id)) + ; + else if (unformat (line_input, "src-port %d", &src_port)) + ; + else if (unformat (line_input, "disable")) + enable = 0; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + rv = snat_ipfix_logging_enable_disable (enable, domain_id, (u16) src_port); + + if (rv) + { + error = clib_error_return (0, "ipfix logging enable failed"); + goto done; + } + +done: + unformat_free (line_input); + + return error; +} + +/*? + * @cliexpar + * @cliexstart{snat ipfix logging} + * To enable NAT IPFIX logging use: + * vpp# nat ipfix logging + * To set IPFIX exporter use: + * vpp# set ipfix exporter collector 10.10.10.3 src 10.10.10.1 + * @cliexend +?*/ +VLIB_CLI_COMMAND (snat_ipfix_logging_enable_disable_command, static) = { + .path = "nat ipfix logging", + .function = snat_ipfix_logging_enable_disable_command_fn, + .short_help = "nat ipfix logging [domain ] [src-port ] [disable]", +}; + +static u32 +snat_get_worker_in2out_cb (ip4_header_t * ip0, u32 rx_fib_index0) +{ + snat_main_t *sm = &snat_main; + snat_user_key_t key0; + clib_bihash_kv_8_8_t kv0, value0; + u32 next_worker_index = 0; + + key0.addr = ip0->src_address; + key0.fib_index = rx_fib_index0; + + kv0.key = key0.as_u64; + + /* Ever heard of of the "user" before? */ + if (clib_bihash_search_8_8 (&sm->worker_by_in, &kv0, &value0)) + { + /* No, assign next available worker (RR) */ + next_worker_index = sm->first_worker_index; + if (vec_len (sm->workers)) + { + next_worker_index += + sm->workers[sm->next_worker++ % _vec_len (sm->workers)]; + } + + /* add non-traslated packets worker lookup */ + kv0.value = next_worker_index; + clib_bihash_add_del_8_8 (&sm->worker_by_in, &kv0, 1); + } + else + next_worker_index = value0.value; + + return next_worker_index; +} + +static u32 +snat_get_worker_out2in_cb (ip4_header_t * ip0, u32 rx_fib_index0) +{ + snat_main_t *sm = &snat_main; + snat_worker_key_t key0; + clib_bihash_kv_8_8_t kv0, value0; + udp_header_t * udp0; + u32 next_worker_index = 0; + + udp0 = ip4_next_header (ip0); + + key0.addr = ip0->dst_address; + key0.port = udp0->dst_port; + key0.fib_index = rx_fib_index0; + + if (PREDICT_FALSE(ip0->protocol == IP_PROTOCOL_ICMP)) + { + icmp46_header_t * icmp0 = (icmp46_header_t *) udp0; + icmp_echo_header_t *echo0 = (icmp_echo_header_t *)(icmp0+1); + key0.port = echo0->identifier; + } + + kv0.key = key0.as_u64; + + /* Ever heard of of the "user" before? */ + if (clib_bihash_search_8_8 (&sm->worker_by_out, &kv0, &value0)) + { + key0.port = 0; + kv0.key = key0.as_u64; + + if (clib_bihash_search_8_8 (&sm->worker_by_out, &kv0, &value0)) + { + /* No, assign next available worker (RR) */ + next_worker_index = sm->first_worker_index; + if (vec_len (sm->workers)) + { + next_worker_index += + sm->workers[sm->next_worker++ % _vec_len (sm->workers)]; + } + } + else + { + /* Static mapping without port */ + next_worker_index = value0.value; + } + + /* Add to translated packets worker lookup */ + kv0.value = next_worker_index; + clib_bihash_add_del_8_8 (&sm->worker_by_out, &kv0, 1); + } + else + next_worker_index = value0.value; + + return next_worker_index; +} + +static clib_error_t * +snat_config (vlib_main_t * vm, unformat_input_t * input) +{ + snat_main_t * sm = &snat_main; + u32 translation_buckets = 1024; + u32 translation_memory_size = 128<<20; + u32 user_buckets = 128; + u32 user_memory_size = 64<<20; + u32 max_translations_per_user = 100; + u32 outside_vrf_id = 0; + u32 inside_vrf_id = 0; + u32 static_mapping_buckets = 1024; + u32 static_mapping_memory_size = 64<<20; + u8 static_mapping_only = 0; + u8 static_mapping_connection_tracking = 0; + + sm->deterministic = 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "translation hash buckets %d", &translation_buckets)) + ; + else if (unformat (input, "translation hash memory %d", + &translation_memory_size)); + else if (unformat (input, "user hash buckets %d", &user_buckets)) + ; + else if (unformat (input, "user hash memory %d", + &user_memory_size)) + ; + else if (unformat (input, "max translations per user %d", + &max_translations_per_user)) + ; + else if (unformat (input, "outside VRF id %d", + &outside_vrf_id)) + ; + else if (unformat (input, "inside VRF id %d", + &inside_vrf_id)) + ; + else if (unformat (input, "static mapping only")) + { + static_mapping_only = 1; + if (unformat (input, "connection tracking")) + static_mapping_connection_tracking = 1; + } + else if (unformat (input, "deterministic")) + sm->deterministic = 1; + else + return clib_error_return (0, "unknown input '%U'", + format_unformat_error, input); + } + + /* for show commands, etc. */ + sm->translation_buckets = translation_buckets; + sm->translation_memory_size = translation_memory_size; + sm->user_buckets = user_buckets; + sm->user_memory_size = user_memory_size; + sm->max_translations_per_user = max_translations_per_user; + sm->outside_vrf_id = outside_vrf_id; + sm->outside_fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, + outside_vrf_id); + sm->inside_vrf_id = inside_vrf_id; + sm->inside_fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, + inside_vrf_id); + sm->static_mapping_only = static_mapping_only; + sm->static_mapping_connection_tracking = static_mapping_connection_tracking; + + if (sm->deterministic) + { + sm->in2out_node_index = snat_det_in2out_node.index; + sm->in2out_output_node_index = ~0; + sm->out2in_node_index = snat_det_out2in_node.index; + sm->icmp_match_in2out_cb = icmp_match_in2out_det; + sm->icmp_match_out2in_cb = icmp_match_out2in_det; + } + else + { + sm->worker_in2out_cb = snat_get_worker_in2out_cb; + sm->worker_out2in_cb = snat_get_worker_out2in_cb; + sm->in2out_node_index = snat_in2out_node.index; + sm->in2out_output_node_index = snat_in2out_output_node.index; + sm->out2in_node_index = snat_out2in_node.index; + if (!static_mapping_only || + (static_mapping_only && static_mapping_connection_tracking)) + { + sm->icmp_match_in2out_cb = icmp_match_in2out_slow; + sm->icmp_match_out2in_cb = icmp_match_out2in_slow; + + clib_bihash_init_8_8 (&sm->worker_by_in, "worker-by-in", user_buckets, + user_memory_size); + + clib_bihash_init_8_8 (&sm->worker_by_out, "worker-by-out", user_buckets, + user_memory_size); + + clib_bihash_init_8_8 (&sm->in2out, "in2out", translation_buckets, + translation_memory_size); + + clib_bihash_init_8_8 (&sm->out2in, "out2in", translation_buckets, + translation_memory_size); + + clib_bihash_init_8_8 (&sm->user_hash, "users", user_buckets, + user_memory_size); + + clib_bihash_init_16_8 (&sm->in2out_unk_proto, "in2out-unk-proto", + translation_buckets, translation_memory_size); + + clib_bihash_init_16_8 (&sm->out2in_unk_proto, "out2in-unk-proto", + translation_buckets, translation_memory_size); + } + else + { + sm->icmp_match_in2out_cb = icmp_match_in2out_fast; + sm->icmp_match_out2in_cb = icmp_match_out2in_fast; + } + clib_bihash_init_8_8 (&sm->static_mapping_by_local, + "static_mapping_by_local", static_mapping_buckets, + static_mapping_memory_size); + + clib_bihash_init_8_8 (&sm->static_mapping_by_external, + "static_mapping_by_external", static_mapping_buckets, + static_mapping_memory_size); + } + + return 0; +} + +VLIB_CONFIG_FUNCTION (snat_config, "nat"); + +u8 * format_snat_session_state (u8 * s, va_list * args) +{ + u32 i = va_arg (*args, u32); + u8 *t = 0; + + switch (i) + { +#define _(v, N, str) case SNAT_SESSION_##N: t = (u8 *) str; break; + foreach_snat_session_state +#undef _ + default: + t = format (t, "unknown"); + } + s = format (s, "%s", t); + return s; +} + +u8 * format_snat_key (u8 * s, va_list * args) +{ + snat_session_key_t * key = va_arg (*args, snat_session_key_t *); + char * protocol_string = "unknown"; + static char *protocol_strings[] = { + "UDP", + "TCP", + "ICMP", + }; + + if (key->protocol < ARRAY_LEN(protocol_strings)) + protocol_string = protocol_strings[key->protocol]; + + s = format (s, "%U proto %s port %d fib %d", + format_ip4_address, &key->addr, protocol_string, + clib_net_to_host_u16 (key->port), key->fib_index); + return s; +} + +u8 * format_snat_session (u8 * s, va_list * args) +{ + snat_main_t * sm __attribute__((unused)) = va_arg (*args, snat_main_t *); + snat_session_t * sess = va_arg (*args, snat_session_t *); + + if (snat_is_unk_proto_session (sess)) + { + s = format (s, " i2o %U proto %u fib %u\n", + format_ip4_address, &sess->in2out.addr, sess->in2out.port, + sess->in2out.fib_index); + s = format (s, " o2i %U proto %u fib %u\n", + format_ip4_address, &sess->out2in.addr, sess->out2in.port, + sess->out2in.fib_index); + } + else + { + s = format (s, " i2o %U\n", format_snat_key, &sess->in2out); + s = format (s, " o2i %U\n", format_snat_key, &sess->out2in); + } + s = format (s, " last heard %.2f\n", sess->last_heard); + s = format (s, " total pkts %d, total bytes %lld\n", + sess->total_pkts, sess->total_bytes); + if (snat_is_session_static (sess)) + s = format (s, " static translation\n"); + else + s = format (s, " dynamic translation\n"); + + return s; +} + +u8 * format_snat_user (u8 * s, va_list * args) +{ + snat_main_per_thread_data_t * sm = va_arg (*args, snat_main_per_thread_data_t *); + snat_user_t * u = va_arg (*args, snat_user_t *); + int verbose = va_arg (*args, int); + dlist_elt_t * head, * elt; + u32 elt_index, head_index; + u32 session_index; + snat_session_t * sess; + + s = format (s, "%U: %d dynamic translations, %d static translations\n", + format_ip4_address, &u->addr, u->nsessions, u->nstaticsessions); + + if (verbose == 0) + return s; + + if (u->nsessions || u->nstaticsessions) + { + head_index = u->sessions_per_user_list_head_index; + head = pool_elt_at_index (sm->list_pool, head_index); + + elt_index = head->next; + elt = pool_elt_at_index (sm->list_pool, elt_index); + session_index = elt->value; + + while (session_index != ~0) + { + sess = pool_elt_at_index (sm->sessions, session_index); + + s = format (s, " %U\n", format_snat_session, sm, sess); + + elt_index = elt->next; + elt = pool_elt_at_index (sm->list_pool, elt_index); + session_index = elt->value; + } + } + + return s; +} + +u8 * format_snat_static_mapping (u8 * s, va_list * args) +{ + snat_static_mapping_t *m = va_arg (*args, snat_static_mapping_t *); + + if (m->addr_only) + s = format (s, "local %U external %U vrf %d", + format_ip4_address, &m->local_addr, + format_ip4_address, &m->external_addr, + m->vrf_id); + else + s = format (s, "%U local %U:%d external %U:%d vrf %d", + format_snat_protocol, m->proto, + format_ip4_address, &m->local_addr, m->local_port, + format_ip4_address, &m->external_addr, m->external_port, + m->vrf_id); + + return s; +} + +u8 * format_snat_static_map_to_resolve (u8 * s, va_list * args) +{ + snat_static_map_resolve_t *m = va_arg (*args, snat_static_map_resolve_t *); + vnet_main_t *vnm = vnet_get_main(); + + if (m->addr_only) + s = format (s, "local %U external %U vrf %d", + format_ip4_address, &m->l_addr, + format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, m->sw_if_index), + m->vrf_id); + else + s = format (s, "%U local %U:%d external %U:%d vrf %d", + format_snat_protocol, m->proto, + format_ip4_address, &m->l_addr, m->l_port, + format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, m->sw_if_index), m->e_port, + m->vrf_id); + + return s; +} + +u8 * format_det_map_ses (u8 * s, va_list * args) +{ + snat_det_map_t * det_map = va_arg (*args, snat_det_map_t *); + ip4_address_t in_addr, out_addr; + u32 in_offset, out_offset; + snat_det_session_t * ses = va_arg (*args, snat_det_session_t *); + u32 * i = va_arg (*args, u32 *); + + u32 user_index = *i / SNAT_DET_SES_PER_USER; + in_addr.as_u32 = clib_host_to_net_u32 ( + clib_net_to_host_u32(det_map->in_addr.as_u32) + user_index); + in_offset = clib_net_to_host_u32(in_addr.as_u32) - + clib_net_to_host_u32(det_map->in_addr.as_u32); + out_offset = in_offset / det_map->sharing_ratio; + out_addr.as_u32 = clib_host_to_net_u32( + clib_net_to_host_u32(det_map->out_addr.as_u32) + out_offset); + s = format (s, "in %U:%d out %U:%d external host %U:%d state: %U expire: %d\n", + format_ip4_address, &in_addr, + clib_net_to_host_u16 (ses->in_port), + format_ip4_address, &out_addr, + clib_net_to_host_u16 (ses->out.out_port), + format_ip4_address, &ses->out.ext_host_addr, + clib_net_to_host_u16 (ses->out.ext_host_port), + format_snat_session_state, ses->state, + ses->expire); + + return s; +} + +static clib_error_t * +show_snat_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + int verbose = 0; + snat_main_t * sm = &snat_main; + snat_user_t * u; + snat_static_mapping_t *m; + snat_interface_t *i; + snat_address_t * ap; + vnet_main_t *vnm = vnet_get_main(); + snat_main_per_thread_data_t *tsm; + u32 users_num = 0, sessions_num = 0, *worker, *sw_if_index; + uword j = 0; + snat_static_map_resolve_t *rp; + snat_det_map_t * dm; + snat_det_session_t * ses; + + if (unformat (input, "detail")) + verbose = 1; + else if (unformat (input, "verbose")) + verbose = 2; + + if (sm->static_mapping_only) + { + if (sm->static_mapping_connection_tracking) + vlib_cli_output (vm, "NAT plugin mode: static mapping only connection " + "tracking"); + else + vlib_cli_output (vm, "NAT plugin mode: static mapping only"); + } + else if (sm->deterministic) + { + vlib_cli_output (vm, "NAT plugin mode: deterministic mapping"); + } + else + { + vlib_cli_output (vm, "NAT plugin mode: dynamic translations enabled"); + } + + if (verbose > 0) + { + pool_foreach (i, sm->interfaces, + ({ + vlib_cli_output (vm, "%U %s", format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, i->sw_if_index), + i->is_inside ? "in" : "out"); + })); + + pool_foreach (i, sm->output_feature_interfaces, + ({ + vlib_cli_output (vm, "%U output-feature %s", + format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, i->sw_if_index), + i->is_inside ? "in" : "out"); + })); + + if (vec_len (sm->auto_add_sw_if_indices)) + { + vlib_cli_output (vm, "NAT44 pool addresses interfaces:"); + vec_foreach (sw_if_index, sm->auto_add_sw_if_indices) + { + vlib_cli_output (vm, "%U", format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, *sw_if_index)); + } + } + + vec_foreach (ap, sm->addresses) + { + vlib_cli_output (vm, "%U", format_ip4_address, &ap->addr); + if (ap->fib_index != ~0) + vlib_cli_output (vm, " tenant VRF: %u", + ip4_fib_get(ap->fib_index)->table_id); + else + vlib_cli_output (vm, " tenant VRF independent"); +#define _(N, i, n, s) \ + vlib_cli_output (vm, " %d busy %s ports", ap->busy_##n##_ports, s); + foreach_snat_protocol +#undef _ + } + } + + if (sm->num_workers > 1) + { + vlib_cli_output (vm, "%d workers", vec_len (sm->workers)); + if (verbose > 0) + { + vec_foreach (worker, sm->workers) + { + vlib_worker_thread_t *w = + vlib_worker_threads + *worker + sm->first_worker_index; + vlib_cli_output (vm, " %s", w->name); + } + } + } + + if (sm->deterministic) + { + vlib_cli_output (vm, "udp timeout: %dsec", sm->udp_timeout); + vlib_cli_output (vm, "tcp-established timeout: %dsec", + sm->tcp_established_timeout); + vlib_cli_output (vm, "tcp-transitory timeout: %dsec", + sm->tcp_transitory_timeout); + vlib_cli_output (vm, "icmp timeout: %dsec", sm->icmp_timeout); + vlib_cli_output (vm, "%d deterministic mappings", + pool_elts (sm->det_maps)); + if (verbose > 0) + { + pool_foreach (dm, sm->det_maps, + ({ + vlib_cli_output (vm, "in %U/%d out %U/%d\n", + format_ip4_address, &dm->in_addr, dm->in_plen, + format_ip4_address, &dm->out_addr, dm->out_plen); + vlib_cli_output (vm, " outside address sharing ratio: %d\n", + dm->sharing_ratio); + vlib_cli_output (vm, " number of ports per inside host: %d\n", + dm->ports_per_host); + vlib_cli_output (vm, " sessions number: %d\n", dm->ses_num); + if (verbose > 1) + { + vec_foreach_index (j, dm->sessions) + { + ses = vec_elt_at_index (dm->sessions, j); + if (ses->in_port) + vlib_cli_output (vm, " %U", format_det_map_ses, dm, ses, + &j); + } + } + })); + } + } + else + { + if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking)) + { + vlib_cli_output (vm, "%d static mappings", + pool_elts (sm->static_mappings)); + + if (verbose > 0) + { + pool_foreach (m, sm->static_mappings, + ({ + vlib_cli_output (vm, "%U", format_snat_static_mapping, m); + })); + } + } + else + { + vec_foreach (tsm, sm->per_thread_data) + { + users_num += pool_elts (tsm->users); + sessions_num += pool_elts (tsm->sessions); + } + + vlib_cli_output (vm, "%d users, %d outside addresses, %d active sessions," + " %d static mappings", + users_num, + vec_len (sm->addresses), + sessions_num, + pool_elts (sm->static_mappings)); + + if (verbose > 0) + { + vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->in2out, + verbose - 1); + vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->out2in, + verbose - 1); + vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->worker_by_in, + verbose - 1); + vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->worker_by_out, + verbose - 1); + vec_foreach_index (j, sm->per_thread_data) + { + tsm = vec_elt_at_index (sm->per_thread_data, j); + + if (pool_elts (tsm->users) == 0) + continue; + + vlib_worker_thread_t *w = vlib_worker_threads + j; + vlib_cli_output (vm, "Thread %d (%s at lcore %u):", j, w->name, + w->lcore_id); + vlib_cli_output (vm, " %d list pool elements", + pool_elts (tsm->list_pool)); + + pool_foreach (u, tsm->users, + ({ + vlib_cli_output (vm, " %U", format_snat_user, tsm, u, + verbose - 1); + })); + } + + if (pool_elts (sm->static_mappings)) + { + vlib_cli_output (vm, "static mappings:"); + pool_foreach (m, sm->static_mappings, + ({ + vlib_cli_output (vm, "%U", format_snat_static_mapping, m); + })); + for (j = 0; j < vec_len (sm->to_resolve); j++) + { + rp = sm->to_resolve + j; + vlib_cli_output (vm, "%U", + format_snat_static_map_to_resolve, rp); + } + } + } + } + } + return 0; +} + +VLIB_CLI_COMMAND (show_snat_command, static) = { + .path = "show nat44", + .short_help = "show nat44", + .function = show_snat_command_fn, +}; + + +static void +snat_ip4_add_del_interface_address_cb (ip4_main_t * im, + uword opaque, + u32 sw_if_index, + ip4_address_t * address, + u32 address_length, + u32 if_address_index, + u32 is_delete) +{ + snat_main_t *sm = &snat_main; + snat_static_map_resolve_t *rp; + u32 *indices_to_delete = 0; + int i, j; + int rv; + + for (i = 0; i < vec_len(sm->auto_add_sw_if_indices); i++) + { + if (sw_if_index == sm->auto_add_sw_if_indices[i]) + { + if (!is_delete) + { + /* Don't trip over lease renewal, static config */ + for (j = 0; j < vec_len(sm->addresses); j++) + if (sm->addresses[j].addr.as_u32 == address->as_u32) + return; + + snat_add_address (sm, address, ~0); + /* Scan static map resolution vector */ + for (j = 0; j < vec_len (sm->to_resolve); j++) + { + rp = sm->to_resolve + j; + /* On this interface? */ + if (rp->sw_if_index == sw_if_index) + { + /* Add the static mapping */ + rv = snat_add_static_mapping (rp->l_addr, + address[0], + rp->l_port, + rp->e_port, + rp->vrf_id, + rp->addr_only, + ~0 /* sw_if_index */, + rp->proto, + rp->is_add); + if (rv) + clib_warning ("snat_add_static_mapping returned %d", + rv); + vec_add1 (indices_to_delete, j); + } + } + /* If we resolved any of the outstanding static mappings */ + if (vec_len(indices_to_delete)) + { + /* Delete them */ + for (j = vec_len(indices_to_delete)-1; j >= 0; j--) + vec_delete(sm->to_resolve, 1, j); + vec_free(indices_to_delete); + } + return; + } + else + { + (void) snat_del_address(sm, address[0], 1); + return; + } + } + } +} + + +int snat_add_interface_address (snat_main_t *sm, u32 sw_if_index, int is_del) +{ + ip4_main_t * ip4_main = sm->ip4_main; + ip4_address_t * first_int_addr; + snat_static_map_resolve_t *rp; + u32 *indices_to_delete = 0; + int i, j; + + first_int_addr = ip4_interface_first_address (ip4_main, sw_if_index, + 0 /* just want the address*/); + + for (i = 0; i < vec_len(sm->auto_add_sw_if_indices); i++) + { + if (sm->auto_add_sw_if_indices[i] == sw_if_index) + { + if (is_del) + { + /* if have address remove it */ + if (first_int_addr) + (void) snat_del_address (sm, first_int_addr[0], 1); + else + { + for (j = 0; j < vec_len (sm->to_resolve); j++) + { + rp = sm->to_resolve + j; + if (rp->sw_if_index == sw_if_index) + vec_add1 (indices_to_delete, j); + } + if (vec_len(indices_to_delete)) + { + for (j = vec_len(indices_to_delete)-1; j >= 0; j--) + vec_del1(sm->to_resolve, j); + vec_free(indices_to_delete); + } + } + vec_del1(sm->auto_add_sw_if_indices, i); + } + else + return VNET_API_ERROR_VALUE_EXIST; + + return 0; + } + } + + if (is_del) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + /* add to the auto-address list */ + vec_add1(sm->auto_add_sw_if_indices, sw_if_index); + + /* If the address is already bound - or static - add it now */ + if (first_int_addr) + snat_add_address (sm, first_int_addr, ~0); + + return 0; +} + +static clib_error_t * +snat_add_interface_address_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + snat_main_t *sm = &snat_main; + unformat_input_t _line_input, *line_input = &_line_input; + u32 sw_if_index; + int rv; + int is_del = 0; + clib_error_t *error = 0; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "%U", unformat_vnet_sw_interface, + sm->vnet_main, &sw_if_index)) + ; + else if (unformat (line_input, "del")) + is_del = 1; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + rv = snat_add_interface_address (sm, sw_if_index, is_del); + + switch (rv) + { + case 0: + break; + + default: + error = clib_error_return (0, "snat_add_interface_address returned %d", + rv); + goto done; + } + +done: + unformat_free (line_input); + + return error; +} + +VLIB_CLI_COMMAND (snat_add_interface_address_command, static) = { + .path = "nat44 add interface address", + .short_help = "nat44 add interface address [del]", + .function = snat_add_interface_address_command_fn, +}; + +static clib_error_t * +snat_det_map_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + snat_main_t *sm = &snat_main; + unformat_input_t _line_input, *line_input = &_line_input; + ip4_address_t in_addr, out_addr; + u32 in_plen, out_plen; + int is_add = 1, rv; + clib_error_t *error = 0; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "in %U/%u", unformat_ip4_address, &in_addr, &in_plen)) + ; + else if (unformat (line_input, "out %U/%u", unformat_ip4_address, &out_addr, &out_plen)) + ; + else if (unformat (line_input, "del")) + is_add = 0; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + unformat_free (line_input); + + rv = snat_det_add_map(sm, &in_addr, (u8) in_plen, &out_addr, (u8)out_plen, + is_add); + + if (rv) + { + error = clib_error_return (0, "snat_det_add_map return %d", rv); + goto done; + } + +done: + unformat_free (line_input); + + return error; +} + +/*? + * @cliexpar + * @cliexstart{snat deterministic add} + * Create bijective mapping of inside address to outside address and port range + * pairs, with the purpose of enabling deterministic NAT to reduce logging in + * CGN deployments. + * To create deterministic mapping between inside network 10.0.0.0/18 and + * outside network 1.1.1.0/30 use: + * # vpp# nat44 deterministic add in 10.0.0.0/18 out 1.1.1.0/30 + * @cliexend +?*/ +VLIB_CLI_COMMAND (snat_det_map_command, static) = { + .path = "nat44 deterministic add", + .short_help = "nat44 deterministic add in / out / [del]", + .function = snat_det_map_command_fn, +}; + +static clib_error_t * +snat_det_forward_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + snat_main_t *sm = &snat_main; + unformat_input_t _line_input, *line_input = &_line_input; + ip4_address_t in_addr, out_addr; + u16 lo_port; + snat_det_map_t * dm; + clib_error_t *error = 0; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "%U", unformat_ip4_address, &in_addr)) + ; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + unformat_free (line_input); + + dm = snat_det_map_by_user(sm, &in_addr); + if (!dm) + vlib_cli_output (vm, "no match"); + else + { + snat_det_forward (dm, &in_addr, &out_addr, &lo_port); + vlib_cli_output (vm, "%U:<%d-%d>", format_ip4_address, &out_addr, + lo_port, lo_port + dm->ports_per_host - 1); + } + +done: + unformat_free (line_input); + + return error; +} + +/*? + * @cliexpar + * @cliexstart{snat deterministic forward} + * Return outside address and port range from inside address for deterministic + * NAT. + * To obtain outside address and port of inside host use: + * vpp# nat44 deterministic forward 10.0.0.2 + * 1.1.1.0:<1054-1068> + * @cliexend +?*/ +VLIB_CLI_COMMAND (snat_det_forward_command, static) = { + .path = "nat44 deterministic forward", + .short_help = "nat44 deterministic forward ", + .function = snat_det_forward_command_fn, +}; + +static clib_error_t * +snat_det_reverse_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + snat_main_t *sm = &snat_main; + unformat_input_t _line_input, *line_input = &_line_input; + ip4_address_t in_addr, out_addr; + u32 out_port; + snat_det_map_t * dm; + clib_error_t *error = 0; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "%U:%d", unformat_ip4_address, &out_addr, &out_port)) + ; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + } + } + + unformat_free (line_input); + + if (out_port < 1024 || out_port > 65535) + { + error = clib_error_return (0, "wrong port, must be <1024-65535>"); + goto done; + } + + dm = snat_det_map_by_out(sm, &out_addr); + if (!dm) + vlib_cli_output (vm, "no match"); + else + { + snat_det_reverse (dm, &out_addr, (u16) out_port, &in_addr); + vlib_cli_output (vm, "%U", format_ip4_address, &in_addr); + } + +done: + unformat_free (line_input); + + return error; +} + +/*? + * @cliexpar + * @cliexstart{snat deterministic reverse} + * Return inside address from outside address and port for deterministic NAT. + * To obtain inside host address from outside address and port use: + * #vpp nat44 deterministic reverse 1.1.1.1:1276 + * 10.0.16.16 + * @cliexend +?*/ +VLIB_CLI_COMMAND (snat_det_reverse_command, static) = { + .path = "nat44 deterministic reverse", + .short_help = "nat44 deterministic reverse :", + .function = snat_det_reverse_command_fn, +}; + +static clib_error_t * +set_timeout_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + snat_main_t *sm = &snat_main; + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "udp %u", &sm->udp_timeout)) + ; + else if (unformat (line_input, "tcp-established %u", + &sm->tcp_established_timeout)) + ; + else if (unformat (line_input, "tcp-transitory %u", + &sm->tcp_transitory_timeout)) + ; + else if (unformat (line_input, "icmp %u", &sm->icmp_timeout)) + ; + else if (unformat (line_input, "reset")) + { + sm->udp_timeout = SNAT_UDP_TIMEOUT; + sm->tcp_established_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT; + sm->tcp_transitory_timeout = SNAT_TCP_TRANSITORY_TIMEOUT; + sm->icmp_timeout = SNAT_ICMP_TIMEOUT; + } + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + unformat_free (line_input); + +done: + unformat_free (line_input); + + return error; +} + +/*? + * @cliexpar + * @cliexstart{set snat deterministic timeout} + * Set values of timeouts for deterministic NAT (in seconds), use: + * vpp# set nat44 deterministic timeout udp 120 tcp-established 7500 + * tcp-transitory 250 icmp 90 + * To reset default values use: + * vpp# set nat44 deterministic timeout reset + * @cliexend +?*/ +VLIB_CLI_COMMAND (set_timeout_command, static) = { + .path = "set nat44 deterministic timeout", + .function = set_timeout_command_fn, + .short_help = + "set nat44 deterministic timeout [udp | tcp-established " + "tcp-transitory | icmp | reset]", +}; + +static clib_error_t * +snat_det_close_session_out_fn (vlib_main_t *vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + snat_main_t *sm = &snat_main; + unformat_input_t _line_input, *line_input = &_line_input; + ip4_address_t out_addr, ext_addr, in_addr; + u16 out_port, ext_port; + snat_det_map_t * dm; + snat_det_session_t * ses; + snat_det_out_key_t key; + clib_error_t *error = 0; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "%U:%d %U:%d", + unformat_ip4_address, &out_addr, &out_port, + unformat_ip4_address, &ext_addr, &ext_port)) + ; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + unformat_free (line_input); + + dm = snat_det_map_by_out(sm, &out_addr); + if (!dm) + vlib_cli_output (vm, "no match"); + else + { + snat_det_reverse(dm, &ext_addr, out_port, &in_addr); + key.ext_host_addr = out_addr; + key.ext_host_port = ntohs(ext_port); + key.out_port = ntohs(out_port); + ses = snat_det_get_ses_by_out(dm, &out_addr, key.as_u64); + if (!ses) + vlib_cli_output (vm, "no match"); + else + snat_det_ses_close(dm, ses); + } + +done: + unformat_free (line_input); + + return error; +} + +/*? + * @cliexpar + * @cliexstart{snat deterministic close session out} + * Close session using outside ip address and port + * and external ip address and port, use: + * vpp# nat44 deterministic close session out 1.1.1.1:1276 2.2.2.2:2387 + * @cliexend +?*/ +VLIB_CLI_COMMAND (snat_det_close_sesion_out_command, static) = { + .path = "nat44 deterministic close session out", + .short_help = "nat44 deterministic close session out " + ": :", + .function = snat_det_close_session_out_fn, +}; + +static clib_error_t * +snat_det_close_session_in_fn (vlib_main_t *vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + snat_main_t *sm = &snat_main; + unformat_input_t _line_input, *line_input = &_line_input; + ip4_address_t in_addr, ext_addr; + u16 in_port, ext_port; + snat_det_map_t * dm; + snat_det_session_t * ses; + snat_det_out_key_t key; + clib_error_t *error = 0; + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "%U:%d %U:%d", + unformat_ip4_address, &in_addr, &in_port, + unformat_ip4_address, &ext_addr, &ext_port)) + ; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + unformat_free (line_input); + + dm = snat_det_map_by_user (sm, &in_addr); + if (!dm) + vlib_cli_output (vm, "no match"); + else + { + key.ext_host_addr = ext_addr; + key.ext_host_port = ntohs (ext_port); + ses = snat_det_find_ses_by_in (dm, &in_addr, ntohs(in_port), key); + if (!ses) + vlib_cli_output (vm, "no match"); + else + snat_det_ses_close(dm, ses); + } + +done: + unformat_free(line_input); + + return error; +} + +/*? + * @cliexpar + * @cliexstart{snat deterministic close_session_in} + * Close session using inside ip address and port + * and external ip address and port, use: + * vpp# nat44 deterministic close session in 3.3.3.3:3487 2.2.2.2:2387 + * @cliexend +?*/ +VLIB_CLI_COMMAND (snat_det_close_session_in_command, static) = { + .path = "nat44 deterministic close session in", + .short_help = "nat44 deterministic close session in " + ": :", + .function = snat_det_close_session_in_fn, +}; diff --git a/src/plugins/nat/nat.h b/src/plugins/nat/nat.h new file mode 100644 index 00000000..04c466dc --- /dev/null +++ b/src/plugins/nat/nat.h @@ -0,0 +1,541 @@ + +/* + * nat.h - NAT plugin 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_nat_h__ +#define __included_nat_h__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define SNAT_UDP_TIMEOUT 300 +#define SNAT_UDP_TIMEOUT_MIN 120 +#define SNAT_TCP_TRANSITORY_TIMEOUT 240 +#define SNAT_TCP_ESTABLISHED_TIMEOUT 7440 +#define SNAT_TCP_INCOMING_SYN 6 +#define SNAT_ICMP_TIMEOUT 60 + +#define SNAT_FLAG_HAIRPINNING (1 << 0) + +/* 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 l_addr; + ip4_address_t r_addr; + u32 fib_index; + u8 proto; + u8 rsvd[3]; + }; + u64 as_u64[2]; + }; +} snat_unk_proto_ses_key_t; + +typedef struct { + union + { + struct + { + ip4_address_t ext_host_addr; + u16 ext_host_port; + u16 out_port; + }; + u64 as_u64; + }; +} snat_det_out_key_t; + +typedef struct { + union + { + struct + { + ip4_address_t addr; + u32 fib_index; + }; + u64 as_u64; + }; +} snat_user_key_t; + +typedef struct { + union + { + struct + { + ip4_address_t addr; + u16 port; + u16 fib_index; + }; + u64 as_u64; + }; +} snat_worker_key_t; + + +#define foreach_snat_protocol \ + _(UDP, 0, udp, "udp") \ + _(TCP, 1, tcp, "tcp") \ + _(ICMP, 2, icmp, "icmp") + +typedef enum { +#define _(N, i, n, s) SNAT_PROTOCOL_##N = i, + foreach_snat_protocol +#undef _ +} snat_protocol_t; + + +#define foreach_snat_session_state \ + _(0, UNKNOWN, "unknown") \ + _(1, UDP_ACTIVE, "udp-active") \ + _(2, TCP_SYN_SENT, "tcp-syn-sent") \ + _(3, TCP_ESTABLISHED, "tcp-established") \ + _(4, TCP_FIN_WAIT, "tcp-fin-wait") \ + _(5, TCP_CLOSE_WAIT, "tcp-close-wait") \ + _(6, TCP_LAST_ACK, "tcp-last-ack") \ + _(7, ICMP_ACTIVE, "icmp-active") + +typedef enum { +#define _(v, N, s) SNAT_SESSION_##N = v, + foreach_snat_session_state +#undef _ +} snat_session_state_t; + + +#define SNAT_SESSION_FLAG_STATIC_MAPPING 1 +#define SNAT_SESSION_FLAG_UNKNOWN_PROTO 2 + +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 */ + + /* External host address */ + ip4_address_t ext_host_addr; /* 68-71 */ + +}) snat_session_t; + + +typedef struct { + ip4_address_t addr; + u32 fib_index; + u32 sessions_per_user_list_head_index; + u32 nsessions; + u32 nstaticsessions; +} snat_user_t; + +typedef struct { + ip4_address_t addr; + u32 fib_index; +#define _(N, i, n, s) \ + u32 busy_##n##_ports; \ + uword * busy_##n##_port_bitmap; + foreach_snat_protocol +#undef _ +} snat_address_t; + +typedef struct { + u16 in_port; + snat_det_out_key_t out; + u8 state; + u32 expire; +} snat_det_session_t; + +typedef struct { + ip4_address_t in_addr; + u8 in_plen; + ip4_address_t out_addr; + u8 out_plen; + u32 sharing_ratio; + u16 ports_per_host; + u32 ses_num; + /* vector of sessions */ + snat_det_session_t * sessions; +} snat_det_map_t; + +typedef struct { + ip4_address_t local_addr; + ip4_address_t external_addr; + u16 local_port; + u16 external_port; + u8 addr_only; + u32 vrf_id; + u32 fib_index; + snat_protocol_t proto; +} snat_static_mapping_t; + +typedef struct { + u32 sw_if_index; + u8 is_inside; +} snat_interface_t; + +typedef struct { + ip4_address_t l_addr; + u16 l_port; + u16 e_port; + u32 sw_if_index; + u32 vrf_id; + snat_protocol_t proto; + int addr_only; + int is_add; +} snat_static_map_resolve_t; + +typedef struct { + /* User pool */ + snat_user_t * users; + + /* Session pool */ + snat_session_t * sessions; + + /* Pool of doubly-linked list elements */ + dlist_elt_t * list_pool; + + u32 snat_thread_index; +} snat_main_per_thread_data_t; + +struct snat_main_s; + +typedef u32 snat_icmp_match_function_t (struct snat_main_s *sm, + vlib_node_runtime_t *node, + u32 thread_index, + vlib_buffer_t *b0, + u8 *p_proto, + snat_session_key_t *p_value, + u8 *p_dont_translate, + void *d, + void *e); + +typedef u32 (snat_get_worker_function_t) (ip4_header_t * ip, u32 rx_fib_index); + +typedef struct snat_main_s { + /* Main lookup tables */ + clib_bihash_8_8_t out2in; + clib_bihash_8_8_t in2out; + + /* Unknown protocol sessions lookup tables */ + clib_bihash_16_8_t out2in_unk_proto; + clib_bihash_16_8_t in2out_unk_proto; + + /* Find-a-user => src address lookup */ + clib_bihash_8_8_t user_hash; + + /* Non-translated packets worker lookup => src address + VRF */ + clib_bihash_8_8_t worker_by_in; + + /* Translated packets worker lookup => IP address + port number */ + clib_bihash_8_8_t worker_by_out; + + snat_icmp_match_function_t * icmp_match_in2out_cb; + snat_icmp_match_function_t * icmp_match_out2in_cb; + + u32 num_workers; + u32 first_worker_index; + u32 next_worker; + u32 * workers; + snat_get_worker_function_t * worker_in2out_cb; + snat_get_worker_function_t * worker_out2in_cb; + u16 port_per_thread; + u32 num_snat_thread; + + /* Per thread data */ + snat_main_per_thread_data_t * per_thread_data; + + /* Find a static mapping by local */ + clib_bihash_8_8_t static_mapping_by_local; + + /* Find a static mapping by external */ + clib_bihash_8_8_t static_mapping_by_external; + + /* Static mapping pool */ + snat_static_mapping_t * static_mappings; + + /* Interface pool */ + snat_interface_t * interfaces; + snat_interface_t * output_feature_interfaces; + + /* Vector of outside addresses */ + snat_address_t * addresses; + + /* sw_if_indices whose intfc addresses should be auto-added */ + u32 * auto_add_sw_if_indices; + + /* vector of interface address static mappings to resolve. */ + snat_static_map_resolve_t *to_resolve; + + /* Randomize port allocation order */ + u32 random_seed; + + /* Worker handoff index */ + u32 fq_in2out_index; + u32 fq_in2out_output_index; + u32 fq_out2in_index; + + /* in2out and out2in node index */ + u32 in2out_node_index; + u32 in2out_output_node_index; + u32 out2in_node_index; + + /* Deterministic NAT */ + snat_det_map_t * det_maps; + + /* Config parameters */ + u8 static_mapping_only; + u8 static_mapping_connection_tracking; + u8 deterministic; + 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; + u32 inside_vrf_id; + u32 inside_fib_index; + + /* tenant VRF aware address pool activation flag */ + u8 vrf_mode; + + /* values of various timeouts */ + u32 udp_timeout; + u32 tcp_established_timeout; + u32 tcp_transitory_timeout; + u32 icmp_timeout; + + /* 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; + 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_in2out_output_node; +extern vlib_node_registration_t snat_out2in_node; +extern vlib_node_registration_t snat_in2out_fast_node; +extern vlib_node_registration_t snat_out2in_fast_node; +extern vlib_node_registration_t snat_in2out_worker_handoff_node; +extern vlib_node_registration_t snat_in2out_output_worker_handoff_node; +extern vlib_node_registration_t snat_out2in_worker_handoff_node; +extern vlib_node_registration_t snat_det_in2out_node; +extern vlib_node_registration_t snat_det_out2in_node; +extern vlib_node_registration_t snat_hairpin_dst_node; +extern vlib_node_registration_t snat_hairpin_src_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, + u32 fib_index, + u32 thread_index, + snat_session_key_t * k, + u32 * address_indexp); + +int snat_static_mapping_match (snat_main_t * sm, + snat_session_key_t match, + snat_session_key_t * mapping, + u8 by_external, + u8 *is_addr_only); + +void snat_add_del_addr_to_fib (ip4_address_t * addr, + u8 p_len, + u32 sw_if_index, + int is_add); + +format_function_t format_snat_user; + +typedef struct { + u32 cached_sw_if_index; + u32 cached_ip4_address; +} snat_runtime_t; + +/** \brief Check if SNAT session is created from static mapping. + @param s SNAT session + @return 1 if SNAT session is created from static mapping otherwise 0 +*/ +#define snat_is_session_static(s) s->flags & SNAT_SESSION_FLAG_STATIC_MAPPING + +/** \brief Check if SNAT session for unknown protocol. + @param s SNAT session + @return 1 if SNAT session for unknown protocol otherwise 0 +*/ +#define snat_is_unk_proto_session(s) s->flags & SNAT_SESSION_FLAG_UNKNOWN_PROTO + +/* + * 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; + +always_inline u32 +ip_proto_to_snat_proto (u8 ip_proto) +{ + u32 snat_proto = ~0; + + snat_proto = (ip_proto == IP_PROTOCOL_UDP) ? SNAT_PROTOCOL_UDP : snat_proto; + snat_proto = (ip_proto == IP_PROTOCOL_TCP) ? SNAT_PROTOCOL_TCP : snat_proto; + snat_proto = (ip_proto == IP_PROTOCOL_ICMP) ? SNAT_PROTOCOL_ICMP : snat_proto; + snat_proto = (ip_proto == IP_PROTOCOL_ICMP6) ? SNAT_PROTOCOL_ICMP : snat_proto; + + return snat_proto; +} + +always_inline u8 +snat_proto_to_ip_proto (snat_protocol_t snat_proto) +{ + u8 ip_proto = ~0; + + ip_proto = (snat_proto == SNAT_PROTOCOL_UDP) ? IP_PROTOCOL_UDP : ip_proto; + ip_proto = (snat_proto == SNAT_PROTOCOL_TCP) ? IP_PROTOCOL_TCP : ip_proto; + ip_proto = (snat_proto == SNAT_PROTOCOL_ICMP) ? IP_PROTOCOL_ICMP : ip_proto; + + return ip_proto; +} + +typedef struct { + u16 src_port, dst_port; +} tcp_udp_header_t; + +u32 icmp_match_in2out_fast(snat_main_t *sm, vlib_node_runtime_t *node, + u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + snat_session_key_t *p_value, + u8 *p_dont_translate, void *d, void *e); +u32 icmp_match_in2out_slow(snat_main_t *sm, vlib_node_runtime_t *node, + u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + snat_session_key_t *p_value, + u8 *p_dont_translate, void *d, void *e); +u32 icmp_match_in2out_det(snat_main_t *sm, vlib_node_runtime_t *node, + u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + snat_session_key_t *p_value, + u8 *p_dont_translate, void *d, void *e); +u32 icmp_match_out2in_fast(snat_main_t *sm, vlib_node_runtime_t *node, + u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + snat_session_key_t *p_value, + u8 *p_dont_translate, void *d, void *e); +u32 icmp_match_out2in_slow(snat_main_t *sm, vlib_node_runtime_t *node, + u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + snat_session_key_t *p_value, + u8 *p_dont_translate, void *d, void *e); +u32 icmp_match_out2in_det(snat_main_t *sm, vlib_node_runtime_t *node, + u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + snat_session_key_t *p_value, + u8 *p_dont_translate, void *d, void *e); +void increment_v4_address(ip4_address_t * a); +void snat_add_address(snat_main_t *sm, ip4_address_t *addr, u32 vrf_id); +int snat_del_address(snat_main_t *sm, ip4_address_t addr, u8 delete_sm); +int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, + u16 l_port, u16 e_port, u32 vrf_id, int addr_only, + u32 sw_if_index, snat_protocol_t proto, int is_add); +clib_error_t * snat_api_init(vlib_main_t * vm, snat_main_t * sm); +int snat_set_workers (uword * bitmap); +int snat_interface_add_del(u32 sw_if_index, u8 is_inside, int is_del); +int snat_interface_add_del_output_feature(u32 sw_if_index, u8 is_inside, + int is_del); +int snat_add_interface_address(snat_main_t *sm, u32 sw_if_index, int is_del); +uword unformat_snat_protocol(unformat_input_t * input, va_list * args); +u8 * format_snat_protocol(u8 * s, va_list * args); + +static_always_inline u8 +icmp_is_error_message (icmp46_header_t * icmp) +{ + switch(icmp->type) + { + case ICMP4_destination_unreachable: + case ICMP4_time_exceeded: + case ICMP4_parameter_problem: + case ICMP4_source_quench: + case ICMP4_redirect: + case ICMP4_alternate_host_address: + return 1; + } + return 0; +} + +static_always_inline u8 +is_interface_addr(snat_main_t *sm, vlib_node_runtime_t *node, u32 sw_if_index0, + u32 ip4_addr) +{ + snat_runtime_t *rt = (snat_runtime_t *) node->runtime_data; + ip4_address_t * first_int_addr; + + if (PREDICT_FALSE(rt->cached_sw_if_index != sw_if_index0)) + { + first_int_addr = + ip4_interface_first_address (sm->ip4_main, sw_if_index0, + 0 /* just want the address */); + rt->cached_sw_if_index = sw_if_index0; + if (first_int_addr) + rt->cached_ip4_address = first_int_addr->as_u32; + else + rt->cached_ip4_address = 0; + } + + if (PREDICT_FALSE(ip4_addr == rt->cached_ip4_address)) + return 1; + else + return 0; +} + +#endif /* __included_nat_h__ */ diff --git a/src/plugins/nat/nat64.c b/src/plugins/nat/nat64.c new file mode 100644 index 00000000..b04901fa --- /dev/null +++ b/src/plugins/nat/nat64.c @@ -0,0 +1,858 @@ +/* + * 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. + */ +/** + * @file + * @brief NAT64 implementation + */ + +#include +#include +#include + + +nat64_main_t nat64_main; + +/* *INDENT-OFF* */ + +/* Hook up input features */ +VNET_FEATURE_INIT (nat64_in2out, static) = { + .arc_name = "ip6-unicast", + .node_name = "nat64-in2out", + .runs_before = VNET_FEATURES ("ip6-lookup"), +}; +VNET_FEATURE_INIT (nat64_out2in, static) = { + .arc_name = "ip4-unicast", + .node_name = "nat64-out2in", + .runs_before = VNET_FEATURES ("ip4-lookup"), +}; + +static u8 well_known_prefix[] = { + 0x00, 0x64, 0xff, 0x9b, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +/* *INDENT-ON* */ + +clib_error_t * +nat64_init (vlib_main_t * vm) +{ + nat64_main_t *nm = &nat64_main; + clib_error_t *error = 0; + vlib_thread_main_t *tm = vlib_get_thread_main (); + + nm->is_disabled = 0; + + if (tm->n_vlib_mains > 1) + { + nm->is_disabled = 1; + goto error; + } + + if (nat64_db_init (&nm->db)) + { + error = clib_error_return (0, "NAT64 DB init failed"); + goto error; + } + + /* set session timeouts to default values */ + nm->udp_timeout = SNAT_UDP_TIMEOUT; + nm->icmp_timeout = SNAT_ICMP_TIMEOUT; + nm->tcp_trans_timeout = SNAT_TCP_TRANSITORY_TIMEOUT; + nm->tcp_est_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT; + nm->tcp_incoming_syn_timeout = SNAT_TCP_INCOMING_SYN; + +error: + return error; +} + +int +nat64_add_del_pool_addr (ip4_address_t * addr, u32 vrf_id, u8 is_add) +{ + nat64_main_t *nm = &nat64_main; + snat_address_t *a = 0; + snat_interface_t *interface; + int i; + + /* Check if address already exists */ + for (i = 0; i < vec_len (nm->addr_pool); i++) + { + if (nm->addr_pool[i].addr.as_u32 == addr->as_u32) + { + a = nm->addr_pool + i; + break; + } + } + + if (is_add) + { + if (a) + return VNET_API_ERROR_VALUE_EXIST; + + vec_add2 (nm->addr_pool, a, 1); + a->addr = *addr; + a->fib_index = 0; + if (vrf_id != ~0) + a->fib_index = + fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, vrf_id); +#define _(N, i, n, s) \ + clib_bitmap_alloc (a->busy_##n##_port_bitmap, 65535); + foreach_snat_protocol +#undef _ + } + else + { + if (!a) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + if (a->fib_index) + fib_table_unlock (a->fib_index, FIB_PROTOCOL_IP6); + +#define _(N, id, n, s) \ + clib_bitmap_free (a->busy_##n##_port_bitmap); + foreach_snat_protocol +#undef _ + /* Delete sessions using address */ + nat64_db_free_out_addr (&nm->db, &a->addr); + vec_del1 (nm->addr_pool, i); + } + + /* Add/del external address to FIB */ + /* *INDENT-OFF* */ + pool_foreach (interface, nm->interfaces, + ({ + if (interface->is_inside) + continue; + + snat_add_del_addr_to_fib (addr, 32, interface->sw_if_index, is_add); + break; + })); + /* *INDENT-ON* */ + + return 0; +} + +void +nat64_pool_addr_walk (nat64_pool_addr_walk_fn_t fn, void *ctx) +{ + nat64_main_t *nm = &nat64_main; + snat_address_t *a = 0; + + /* *INDENT-OFF* */ + vec_foreach (a, nm->addr_pool) + { + if (fn (a, ctx)) + break; + }; + /* *INDENT-ON* */ +} + +int +nat64_add_del_interface (u32 sw_if_index, u8 is_inside, u8 is_add) +{ + nat64_main_t *nm = &nat64_main; + snat_interface_t *interface = 0, *i; + snat_address_t *ap; + const char *feature_name, *arc_name; + + /* Check if address already exists */ + /* *INDENT-OFF* */ + pool_foreach (i, nm->interfaces, + ({ + if (i->sw_if_index == sw_if_index) + { + interface = i; + break; + } + })); + /* *INDENT-ON* */ + + if (is_add) + { + if (interface) + return VNET_API_ERROR_VALUE_EXIST; + + pool_get (nm->interfaces, interface); + interface->sw_if_index = sw_if_index; + interface->is_inside = is_inside; + + } + else + { + if (!interface) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + pool_put (nm->interfaces, interface); + } + + if (!is_inside) + { + /* *INDENT-OFF* */ + vec_foreach (ap, nm->addr_pool) + snat_add_del_addr_to_fib(&ap->addr, 32, sw_if_index, is_add); + /* *INDENT-ON* */ + } + + arc_name = is_inside ? "ip6-unicast" : "ip4-unicast"; + feature_name = is_inside ? "nat64-in2out" : "nat64-out2in"; + + return vnet_feature_enable_disable (arc_name, feature_name, sw_if_index, + is_add, 0, 0); +} + +void +nat64_interfaces_walk (nat64_interface_walk_fn_t fn, void *ctx) +{ + nat64_main_t *nm = &nat64_main; + snat_interface_t *i = 0; + + /* *INDENT-OFF* */ + pool_foreach (i, nm->interfaces, + ({ + if (fn (i, ctx)) + break; + })); + /* *INDENT-ON* */ +} + +int +nat64_alloc_out_addr_and_port (u32 fib_index, snat_protocol_t proto, + ip4_address_t * addr, u16 * port) +{ + nat64_main_t *nm = &nat64_main; + snat_main_t *sm = &snat_main; + int i; + snat_address_t *a, *ga = 0; + u32 portnum; + + for (i = 0; i < vec_len (nm->addr_pool); i++) + { + a = nm->addr_pool + i; + switch (proto) + { +#define _(N, j, n, s) \ + case SNAT_PROTOCOL_##N: \ + if (a->busy_##n##_ports < (65535-1024)) \ + { \ + if (a->fib_index == fib_index) \ + { \ + while (1) \ + { \ + portnum = random_u32 (&sm->random_seed); \ + portnum &= 0xFFFF; \ + if (portnum < 1024) \ + continue; \ + if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \ + portnum)) \ + continue; \ + clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \ + portnum, 1); \ + a->busy_##n##_ports++; \ + *port = portnum; \ + addr->as_u32 = a->addr.as_u32; \ + return 0; \ + } \ + } \ + else if (a->fib_index == 0) \ + ga = a; \ + } \ + break; + foreach_snat_protocol +#undef _ + default: + clib_warning ("unknown protocol"); + return 1; + } + } + + if (ga) + { + switch (proto) + { +#define _(N, j, n, s) \ + case SNAT_PROTOCOL_##N: \ + while (1) \ + { \ + portnum = random_u32 (&sm->random_seed); \ + portnum &= 0xFFFF; \ + if (portnum < 1024) \ + continue; \ + if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \ + portnum)) \ + continue; \ + clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \ + portnum, 1); \ + a->busy_##n##_ports++; \ + *port = portnum; \ + addr->as_u32 = a->addr.as_u32; \ + return 0; \ + } + break; + foreach_snat_protocol +#undef _ + default: + clib_warning ("unknown protocol"); + return 1; + } + } + + /* Totally out of translations to use... */ + //TODO: IPFix + return 1; +} + +void +nat64_free_out_addr_and_port (ip4_address_t * addr, u16 port, + snat_protocol_t proto) +{ + nat64_main_t *nm = &nat64_main; + int i; + snat_address_t *a; + + for (i = 0; i < vec_len (nm->addr_pool); i++) + { + a = nm->addr_pool + i; + if (addr->as_u32 != a->addr.as_u32) + continue; + switch (proto) + { +#define _(N, j, n, s) \ + case SNAT_PROTOCOL_##N: \ + ASSERT (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \ + port) == 1); \ + clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, port, 0); \ + a->busy_##n##_ports--; \ + break; + foreach_snat_protocol +#undef _ + default: + clib_warning ("unknown protocol"); + return; + } + break; + } +} + +int +nat64_add_del_static_bib_entry (ip6_address_t * in_addr, + ip4_address_t * out_addr, u16 in_port, + u16 out_port, u8 proto, u32 vrf_id, u8 is_add) +{ + nat64_main_t *nm = &nat64_main; + nat64_db_bib_entry_t *bibe; + u32 fib_index = + fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, vrf_id); + snat_protocol_t p = ip_proto_to_snat_proto (proto); + ip46_address_t addr; + int i; + snat_address_t *a; + + addr.as_u64[0] = in_addr->as_u64[0]; + addr.as_u64[1] = in_addr->as_u64[1]; + bibe = + nat64_db_bib_entry_find (&nm->db, &addr, clib_host_to_net_u16 (in_port), + proto, fib_index, 1); + + if (is_add) + { + if (bibe) + return VNET_API_ERROR_VALUE_EXIST; + + for (i = 0; i < vec_len (nm->addr_pool); i++) + { + a = nm->addr_pool + i; + if (out_addr->as_u32 != a->addr.as_u32) + continue; + switch (p) + { +#define _(N, j, n, s) \ + case SNAT_PROTOCOL_##N: \ + if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \ + out_port)) \ + return VNET_API_ERROR_INVALID_VALUE; \ + clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \ + out_port, 1); \ + if (out_port > 1024) \ + a->busy_##n##_ports++; \ + break; + foreach_snat_protocol +#undef _ + default: + memset (&addr, 0, sizeof (addr)); + addr.ip4.as_u32 = out_addr->as_u32; + if (nat64_db_bib_entry_find + (&nm->db, &addr, 0, proto, fib_index, 0)) + return VNET_API_ERROR_INVALID_VALUE; + } + break; + } + bibe = + nat64_db_bib_entry_create (&nm->db, in_addr, out_addr, + clib_host_to_net_u16 (in_port), + clib_host_to_net_u16 (out_port), fib_index, + proto, 1); + if (!bibe) + return VNET_API_ERROR_UNSPECIFIED; + } + else + { + if (!bibe) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + nat64_free_out_addr_and_port (out_addr, out_port, p); + nat64_db_bib_entry_free (&nm->db, bibe); + } + + return 0; +} + +int +nat64_set_udp_timeout (u32 timeout) +{ + nat64_main_t *nm = &nat64_main; + + if (timeout == 0) + nm->udp_timeout = SNAT_UDP_TIMEOUT; + else if (timeout < SNAT_UDP_TIMEOUT_MIN) + return VNET_API_ERROR_INVALID_VALUE; + else + nm->udp_timeout = timeout; + + return 0; +} + +u32 +nat64_get_udp_timeout (void) +{ + nat64_main_t *nm = &nat64_main; + + return nm->udp_timeout; +} + +int +nat64_set_icmp_timeout (u32 timeout) +{ + nat64_main_t *nm = &nat64_main; + + if (timeout == 0) + nm->icmp_timeout = SNAT_ICMP_TIMEOUT; + else + nm->icmp_timeout = timeout; + + return 0; +} + +u32 +nat64_get_icmp_timeout (void) +{ + nat64_main_t *nm = &nat64_main; + + return nm->icmp_timeout; +} + +int +nat64_set_tcp_timeouts (u32 trans, u32 est, u32 incoming_syn) +{ + nat64_main_t *nm = &nat64_main; + + if (trans == 0) + nm->tcp_trans_timeout = SNAT_TCP_TRANSITORY_TIMEOUT; + else + nm->tcp_trans_timeout = trans; + + if (est == 0) + nm->tcp_est_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT; + else + nm->tcp_est_timeout = est; + + if (incoming_syn == 0) + nm->tcp_incoming_syn_timeout = SNAT_TCP_INCOMING_SYN; + else + nm->tcp_incoming_syn_timeout = incoming_syn; + + return 0; +} + +u32 +nat64_get_tcp_trans_timeout (void) +{ + nat64_main_t *nm = &nat64_main; + + return nm->tcp_trans_timeout; +} + +u32 +nat64_get_tcp_est_timeout (void) +{ + nat64_main_t *nm = &nat64_main; + + return nm->tcp_est_timeout; +} + +u32 +nat64_get_tcp_incoming_syn_timeout (void) +{ + nat64_main_t *nm = &nat64_main; + + return nm->tcp_incoming_syn_timeout; +} + +void +nat64_session_reset_timeout (nat64_db_st_entry_t * ste, vlib_main_t * vm) +{ + nat64_main_t *nm = &nat64_main; + u32 now = (u32) vlib_time_now (vm); + + switch (ip_proto_to_snat_proto (ste->proto)) + { + case SNAT_PROTOCOL_ICMP: + ste->expire = now + nm->icmp_timeout; + return; + case SNAT_PROTOCOL_TCP: + { + switch (ste->tcp_state) + { + case NAT64_TCP_STATE_V4_INIT: + case NAT64_TCP_STATE_V6_INIT: + case NAT64_TCP_STATE_V4_FIN_RCV: + case NAT64_TCP_STATE_V6_FIN_RCV: + case NAT64_TCP_STATE_V6_FIN_V4_FIN_RCV: + case NAT64_TCP_STATE_TRANS: + ste->expire = now + nm->tcp_trans_timeout; + return; + case NAT64_TCP_STATE_ESTABLISHED: + ste->expire = now + nm->tcp_est_timeout; + return; + default: + return; + } + } + case SNAT_PROTOCOL_UDP: + ste->expire = now + nm->udp_timeout; + return; + default: + ste->expire = now + nm->udp_timeout; + return; + } +} + +void +nat64_tcp_session_set_state (nat64_db_st_entry_t * ste, tcp_header_t * tcp, + u8 is_ip6) +{ + switch (ste->tcp_state) + { + case NAT64_TCP_STATE_CLOSED: + { + if (tcp->flags & TCP_FLAG_SYN) + { + if (is_ip6) + ste->tcp_state = NAT64_TCP_STATE_V6_INIT; + else + ste->tcp_state = NAT64_TCP_STATE_V4_INIT; + } + return; + } + case NAT64_TCP_STATE_V4_INIT: + { + if (is_ip6 && (tcp->flags & TCP_FLAG_SYN)) + ste->tcp_state = NAT64_TCP_STATE_ESTABLISHED; + return; + } + case NAT64_TCP_STATE_V6_INIT: + { + if (!is_ip6 && (tcp->flags & TCP_FLAG_SYN)) + ste->tcp_state = NAT64_TCP_STATE_ESTABLISHED; + return; + } + case NAT64_TCP_STATE_ESTABLISHED: + { + if (tcp->flags & TCP_FLAG_FIN) + { + if (is_ip6) + ste->tcp_state = NAT64_TCP_STATE_V6_FIN_RCV; + else + ste->tcp_state = NAT64_TCP_STATE_V4_FIN_RCV; + } + else if (tcp->flags & TCP_FLAG_RST) + { + ste->tcp_state = NAT64_TCP_STATE_TRANS; + } + return; + } + case NAT64_TCP_STATE_V4_FIN_RCV: + { + if (is_ip6 && (tcp->flags & TCP_FLAG_FIN)) + ste->tcp_state = NAT64_TCP_STATE_V6_FIN_V4_FIN_RCV; + return; + } + case NAT64_TCP_STATE_V6_FIN_RCV: + { + if (!is_ip6 && (tcp->flags & TCP_FLAG_FIN)) + ste->tcp_state = NAT64_TCP_STATE_V6_FIN_V4_FIN_RCV; + return; + } + case NAT64_TCP_STATE_TRANS: + { + if (!(tcp->flags & TCP_FLAG_RST)) + ste->tcp_state = NAT64_TCP_STATE_ESTABLISHED; + return; + } + default: + return; + } +} + +int +nat64_add_del_prefix (ip6_address_t * prefix, u8 plen, u32 vrf_id, u8 is_add) +{ + nat64_main_t *nm = &nat64_main; + nat64_prefix_t *p = 0; + int i; + + /* Verify prefix length */ + if (plen != 32 && plen != 40 && plen != 48 && plen != 56 && plen != 64 + && plen != 96) + return VNET_API_ERROR_INVALID_VALUE; + + /* Check if tenant already have prefix */ + for (i = 0; i < vec_len (nm->pref64); i++) + { + if (nm->pref64[i].vrf_id == vrf_id) + { + p = nm->pref64 + i; + break; + } + } + + if (is_add) + { + if (!p) + { + vec_add2 (nm->pref64, p, 1); + p->fib_index = + fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, vrf_id); + p->vrf_id = vrf_id; + } + + p->prefix.as_u64[0] = prefix->as_u64[0]; + p->prefix.as_u64[1] = prefix->as_u64[1]; + p->plen = plen; + } + else + { + if (!p) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + vec_del1 (nm->pref64, i); + } + + return 0; +} + +void +nat64_prefix_walk (nat64_prefix_walk_fn_t fn, void *ctx) +{ + nat64_main_t *nm = &nat64_main; + nat64_prefix_t *p = 0; + + /* *INDENT-OFF* */ + vec_foreach (p, nm->pref64) + { + if (fn (p, ctx)) + break; + }; + /* *INDENT-ON* */ +} + +void +nat64_compose_ip6 (ip6_address_t * ip6, ip4_address_t * ip4, u32 fib_index) +{ + nat64_main_t *nm = &nat64_main; + nat64_prefix_t *p, *gp = 0, *prefix = 0; + + /* *INDENT-OFF* */ + vec_foreach (p, nm->pref64) + { + if (p->fib_index == fib_index) + { + prefix = p; + break; + } + + if (p->fib_index == 0) + gp = p; + }; + /* *INDENT-ON* */ + + if (!prefix) + prefix = gp; + + if (prefix) + { + memset (ip6, 0, 16); + memcpy (ip6, &p->prefix, p->plen); + switch (p->plen) + { + case 32: + ip6->as_u32[1] = ip4->as_u32; + break; + case 40: + ip6->as_u8[5] = ip4->as_u8[0]; + ip6->as_u8[6] = ip4->as_u8[1]; + ip6->as_u8[7] = ip4->as_u8[2]; + ip6->as_u8[9] = ip4->as_u8[3]; + break; + case 48: + ip6->as_u8[6] = ip4->as_u8[0]; + ip6->as_u8[7] = ip4->as_u8[1]; + ip6->as_u8[9] = ip4->as_u8[2]; + ip6->as_u8[10] = ip4->as_u8[3]; + break; + case 56: + ip6->as_u8[7] = ip4->as_u8[0]; + ip6->as_u8[9] = ip4->as_u8[1]; + ip6->as_u8[10] = ip4->as_u8[2]; + ip6->as_u8[11] = ip4->as_u8[3]; + break; + case 64: + ip6->as_u8[9] = ip4->as_u8[0]; + ip6->as_u8[10] = ip4->as_u8[1]; + ip6->as_u8[11] = ip4->as_u8[2]; + ip6->as_u8[12] = ip4->as_u8[3]; + break; + case 96: + ip6->as_u32[3] = ip4->as_u32; + break; + default: + clib_warning ("invalid prefix length"); + break; + } + } + else + { + memcpy (ip6, well_known_prefix, 16); + ip6->as_u32[3] = ip4->as_u32; + } +} + +void +nat64_extract_ip4 (ip6_address_t * ip6, ip4_address_t * ip4, u32 fib_index) +{ + nat64_main_t *nm = &nat64_main; + nat64_prefix_t *p, *gp = 0; + u8 plen = 0; + + /* *INDENT-OFF* */ + vec_foreach (p, nm->pref64) + { + if (p->fib_index == fib_index) + { + plen = p->plen; + break; + } + + if (p->vrf_id == 0) + gp = p; + }; + /* *INDENT-ON* */ + + if (!plen) + { + if (gp) + plen = gp->plen; + else + plen = 96; + } + + switch (plen) + { + case 32: + ip4->as_u32 = ip6->as_u32[1]; + break; + case 40: + ip4->as_u8[0] = ip6->as_u8[5]; + ip4->as_u8[1] = ip6->as_u8[6]; + ip4->as_u8[2] = ip6->as_u8[7]; + ip4->as_u8[3] = ip6->as_u8[9]; + break; + case 48: + ip4->as_u8[0] = ip6->as_u8[6]; + ip4->as_u8[1] = ip6->as_u8[7]; + ip4->as_u8[2] = ip6->as_u8[9]; + ip4->as_u8[3] = ip6->as_u8[10]; + break; + case 56: + ip4->as_u8[0] = ip6->as_u8[7]; + ip4->as_u8[1] = ip6->as_u8[9]; + ip4->as_u8[2] = ip6->as_u8[10]; + ip4->as_u8[3] = ip6->as_u8[11]; + break; + case 64: + ip4->as_u8[0] = ip6->as_u8[9]; + ip4->as_u8[1] = ip6->as_u8[10]; + ip4->as_u8[2] = ip6->as_u8[11]; + ip4->as_u8[3] = ip6->as_u8[12]; + break; + case 96: + ip4->as_u32 = ip6->as_u32[3]; + break; + default: + clib_warning ("invalid prefix length"); + break; + } +} + +/** + * @brief The 'nat64-expire-walk' process's main loop. + * + * Check expire time for NAT64 sessions. + */ +static uword +nat64_expire_walk_fn (vlib_main_t * vm, vlib_node_runtime_t * rt, + vlib_frame_t * f) +{ + nat64_main_t *nm = &nat64_main; + + while (!nm->is_disabled) + { + vlib_process_wait_for_event_or_clock (vm, 10.0); + vlib_process_get_events (vm, NULL); + u32 now = (u32) vlib_time_now (vm); + + nad64_db_st_free_expired (&nm->db, now); + } + + return 0; +} + +static vlib_node_registration_t nat64_expire_walk_node; + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (nat64_expire_walk_node, static) = { + .function = nat64_expire_walk_fn, + .type = VLIB_NODE_TYPE_PROCESS, + .name = "nat64-expire-walk", +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat64.h b/src/plugins/nat/nat64.h new file mode 100644 index 00000000..68224cab --- /dev/null +++ b/src/plugins/nat/nat64.h @@ -0,0 +1,322 @@ +/* + * 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. + */ +/** + * @file + * @brief NAT64 global declarations + */ +#ifndef __included_nat64_h__ +#define __included_nat64_h__ + +#include +#include + +#define foreach_nat64_tcp_ses_state \ + _(0, CLOSED, "closed") \ + _(1, V4_INIT, "v4-init") \ + _(2, V6_INIT, "v6-init") \ + _(3, ESTABLISHED, "established") \ + _(4, V4_FIN_RCV, "v4-fin-rcv") \ + _(5, V6_FIN_RCV, "v6-fin-rcv") \ + _(6, V6_FIN_V4_FIN_RCV, "v6-fin-v4-fin-rcv") \ + _(7, TRANS, "trans") + +typedef enum +{ +#define _(v, N, s) NAT64_TCP_STATE_##N = v, + foreach_nat64_tcp_ses_state +#undef _ +} nat64_tcp_ses_state_t; + +typedef struct +{ + ip6_address_t prefix; + u8 plen; + u32 vrf_id; + u32 fib_index; +} nat64_prefix_t; + +typedef struct +{ + /** Interface pool */ + snat_interface_t *interfaces; + + /** Address pool vector */ + snat_address_t *addr_pool; + + /** Pref64 vector */ + nat64_prefix_t *pref64; + + /** BIB and session DB */ + nat64_db_t db; + + /* values of various timeouts */ + u32 udp_timeout; + u32 icmp_timeout; + u32 tcp_trans_timeout; + u32 tcp_est_timeout; + u32 tcp_incoming_syn_timeout; + + u8 is_disabled; + + snat_main_t *sm; +} nat64_main_t; + +extern nat64_main_t nat64_main; +extern vlib_node_registration_t nat64_in2out_node; +extern vlib_node_registration_t nat64_out2in_node; + +/** + * @brief Add/delete address to NAT64 pool. + * + * @param addr IPv4 address. + * @param vrf_id VRF id of tenant, ~0 means independent of VRF. + * @param is_add 1 if add, 0 if delete. + * + * @returns 0 on success, non-zero value otherwise. + */ +int nat64_add_del_pool_addr (ip4_address_t * addr, u32 vrf_id, u8 is_add); + +/** + * @brief Call back function when walking addresses in NAT64 pool, non-zero + * return value stop walk. + */ +typedef int (*nat64_pool_addr_walk_fn_t) (snat_address_t * addr, void *ctx); + +/** + * @brief Walk NAT64 pool. + * + * @param fn The function to invoke on each entry visited. + * @param ctx A context passed in the visit function. + */ +void nat64_pool_addr_walk (nat64_pool_addr_walk_fn_t fn, void *ctx); + +/** + * @brief Enable/disable NAT64 feature on the interface. + * + * @param sw_if_index Index of the interface. + * @param is_inside 1 if inside, 0 if outside. + * @param is_add 1 if add, 0 if delete. + * + * @returns 0 on success, non-zero value otherwise. + */ +int nat64_add_del_interface (u32 sw_if_index, u8 is_inside, u8 is_add); + +/** + * @brief Call back function when walking interfaces with NAT64 feature, + * non-zero return value stop walk. + */ +typedef int (*nat64_interface_walk_fn_t) (snat_interface_t * i, void *ctx); + +/** + * @brief Walk NAT64 interfaces. + * + * @param fn The function to invoke on each entry visited. + * @param ctx A context passed in the visit function. + */ +void nat64_interfaces_walk (nat64_interface_walk_fn_t fn, void *ctx); + +/** + * @brief Initialize NAT64. + * + * @param vm vlib main. + * + * @return error code. + */ +clib_error_t *nat64_init (vlib_main_t * vm); + +/** + * @brief Add/delete static NAT64 BIB entry. + * + * @param in_addr Inside IPv6 address. + * @param out_addr Outside IPv4 address. + * @param in_port Inside port number. + * @param out_port Outside port number. + * @param proto L4 protocol. + * @param vrf_id VRF id of tenant. + * @param is_add 1 if add, 0 if delete. + * + * @returns 0 on success, non-zero value otherwise. + */ +int nat64_add_del_static_bib_entry (ip6_address_t * in_addr, + ip4_address_t * out_addr, u16 in_port, + u16 out_port, u8 proto, u32 vrf_id, + u8 is_add); + +/** + * @brief Alloce IPv4 address and port pair from NAT64 pool. + * + * @param fib_index FIB index of tenant. + * @param proto L4 protocol. + * @param addr Allocated IPv4 address. + * @param port Allocated port number. + * + * @returns 0 on success, non-zero value otherwise. + */ +int nat64_alloc_out_addr_and_port (u32 fib_index, snat_protocol_t proto, + ip4_address_t * addr, u16 * port); + +/** + * @brief Free IPv4 address and port pair from NAT64 pool. + * + * @param addr IPv4 address to free. + * @param port Port number to free. + * @param proto L4 protocol. + * + * @returns 0 on success, non-zero value otherwise. + */ +void nat64_free_out_addr_and_port (ip4_address_t * addr, u16 port, + snat_protocol_t proto); + +/** + * @brief Set UDP session timeout. + * + * @param timeout Timeout value in seconds (if 0 reset to default value 300sec). + * + * @returns 0 on success, non-zero value otherwise. + */ +int nat64_set_udp_timeout (u32 timeout); + +/** + * @brief Get UDP session timeout. + * + * @returns UDP session timeout in seconds. + */ +u32 nat64_get_udp_timeout (void); + +/** + * @brief Set ICMP session timeout. + * + * @param timeout Timeout value in seconds (if 0 reset to default value 60sec). + * + * @returns 0 on success, non-zero value otherwise. + */ +int nat64_set_icmp_timeout (u32 timeout); + +/** + * @brief Get ICMP session timeout. + * + * @returns ICMP session timeout in seconds. + */ +u32 nat64_get_icmp_timeout (void); + +/** + * @brief Set TCP session timeouts. + * + * @param trans Transitory timeout in seconds (if 0 reset to default value 240sec). + * @param est Established timeout in seconds (if 0 reset to default value 7440sec). + * @param incoming_syn Incoming SYN timeout in seconds (if 0 reset to default value 6sec). + * + * @returns 0 on success, non-zero value otherwise. + */ +int nat64_set_tcp_timeouts (u32 trans, u32 est, u32 incoming_syn); + +/** + * @brief Get TCP transitory timeout. + * + * @returns TCP transitory timeout in seconds. + */ +u32 nat64_get_tcp_trans_timeout (void); + +/** + * @brief Get TCP established timeout. + * + * @returns TCP established timeout in seconds. + */ +u32 nat64_get_tcp_est_timeout (void); + +/** + * @brief Get TCP incoming SYN timeout. + * + * @returns TCP incoming SYN timeout in seconds. + */ +u32 nat64_get_tcp_incoming_syn_timeout (void); + +/** + * @brief Reset NAT64 session timeout. + * + * @param ste Session table entry. + * @param vm VLIB main. + **/ +void nat64_session_reset_timeout (nat64_db_st_entry_t * ste, + vlib_main_t * vm); + +/** + * @brief Set NAT64 TCP session state. + * + * @param ste Session table entry. + * @param tcp TCP header. + * @param is_ip6 1 if IPv6 packet, 0 if IPv4. + */ +void nat64_tcp_session_set_state (nat64_db_st_entry_t * ste, + tcp_header_t * tcp, u8 is_ip6); + +/** + * @brief Add/delete NAT64 prefix. + * + * @param prefix NAT64 prefix. + * @param plen Prefix length. + * @param vrf_id VRF id of tenant. + * @param is_add 1 if add, 0 if delete. + * + * @returns 0 on success, non-zero value otherwise. + */ +int nat64_add_del_prefix (ip6_address_t * prefix, u8 plen, u32 vrf_id, + u8 is_add); + +/** + * @brief Call back function when walking addresses in NAT64 prefixes, non-zero + * return value stop walk. + */ +typedef int (*nat64_prefix_walk_fn_t) (nat64_prefix_t * pref64, void *ctx); + +/** + * @brief Walk NAT64 prefixes. + * + * @param fn The function to invoke on each entry visited. + * @param ctx A context passed in the visit function. + */ +void nat64_prefix_walk (nat64_prefix_walk_fn_t fn, void *ctx); + +/** + * Compose IPv4-embedded IPv6 addresses. + * @param ip6 IPv4-embedded IPv6 addresses. + * @param ip4 IPv4 address. + * @param fib_index Tenant FIB index. + */ +void nat64_compose_ip6 (ip6_address_t * ip6, ip4_address_t * ip4, + u32 fib_index); + +/** + * Extract IPv4 address from the IPv4-embedded IPv6 addresses. + * + * @param ip6 IPv4-embedded IPv6 addresses. + * @param ip4 IPv4 address. + * @param fib_index Tenant FIB index. + */ +void nat64_extract_ip4 (ip6_address_t * ip6, ip4_address_t * ip4, + u32 fib_index); + +#define u8_ptr_add(ptr, index) (((u8 *)ptr) + index) +#define u16_net_add(u, val) clib_host_to_net_u16(clib_net_to_host_u16(u) + (val)) + +#endif /* __included_nat64_h__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat64_cli.c b/src/plugins/nat/nat64_cli.c new file mode 100644 index 00000000..bb62ecf2 --- /dev/null +++ b/src/plugins/nat/nat64_cli.c @@ -0,0 +1,983 @@ +/* + * 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. + */ +/** + * @file + * @brief NAT64 CLI + */ + +#include +#include +#include + +static clib_error_t * +nat64_add_del_pool_addr_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + nat64_main_t *nm = &nat64_main; + unformat_input_t _line_input, *line_input = &_line_input; + ip4_address_t start_addr, end_addr, this_addr; + u32 start_host_order, end_host_order; + int i, count, rv; + u32 vrf_id = ~0; + u8 is_add = 1; + clib_error_t *error = 0; + + if (nm->is_disabled) + return clib_error_return (0, + "NAT64 disabled, multi thread not supported"); + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "%U - %U", + unformat_ip4_address, &start_addr, + unformat_ip4_address, &end_addr)) + ; + else if (unformat (line_input, "tenant-vrf %u", &vrf_id)) + ; + else if (unformat (line_input, "%U", unformat_ip4_address, &start_addr)) + end_addr = start_addr; + else if (unformat (line_input, "del")) + is_add = 0; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + 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) + { + error = clib_error_return (0, "end address less than start address"); + goto done; + } + + count = (end_host_order - start_host_order) + 1; + this_addr = start_addr; + + for (i = 0; i < count; i++) + { + rv = nat64_add_del_pool_addr (&this_addr, vrf_id, is_add); + + switch (rv) + { + case VNET_API_ERROR_NO_SUCH_ENTRY: + error = + clib_error_return (0, "NAT64 pool address %U not exist.", + format_ip4_address, &this_addr); + goto done; + case VNET_API_ERROR_VALUE_EXIST: + error = + clib_error_return (0, "NAT64 pool address %U exist.", + format_ip4_address, &this_addr); + goto done; + default: + break; + + } + increment_v4_address (&this_addr); + } + +done: + unformat_free (line_input); + + return error; +} + +static int +nat64_cli_pool_walk (snat_address_t * ap, void *ctx) +{ + vlib_main_t *vm = ctx; + + if (ap->fib_index != ~0) + { + fib_table_t *fib; + fib = fib_table_get (ap->fib_index, FIB_PROTOCOL_IP6); + if (!fib) + return -1; + vlib_cli_output (vm, " %U tenant VRF: %u", format_ip4_address, + &ap->addr, fib->ft_table_id); + } + else + vlib_cli_output (vm, " %U", format_ip4_address, &ap->addr); + + return 0; +} + +static clib_error_t * +nat64_show_pool_command_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + nat64_main_t *nm = &nat64_main; + + if (nm->is_disabled) + return clib_error_return (0, + "NAT64 disabled, multi thread not supported"); + + vlib_cli_output (vm, "NAT64 pool:"); + nat64_pool_addr_walk (nat64_cli_pool_walk, vm); + + return 0; +} + +static clib_error_t * +nat64_interface_feature_command_fn (vlib_main_t * vm, + unformat_input_t * + input, vlib_cli_command_t * cmd) +{ + nat64_main_t *nm = &nat64_main; + unformat_input_t _line_input, *line_input = &_line_input; + vnet_main_t *vnm = vnet_get_main (); + clib_error_t *error = 0; + u32 sw_if_index; + u32 *inside_sw_if_indices = 0; + u32 *outside_sw_if_indices = 0; + u8 is_add = 1; + int i, rv; + + if (nm->is_disabled) + return clib_error_return (0, + "NAT64 disabled, multi thread not supported"); + + /* Get a line of input. */ + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "in %U", unformat_vnet_sw_interface, + vnm, &sw_if_index)) + vec_add1 (inside_sw_if_indices, sw_if_index); + else if (unformat (line_input, "out %U", unformat_vnet_sw_interface, + vnm, &sw_if_index)) + vec_add1 (outside_sw_if_indices, sw_if_index); + else if (unformat (line_input, "del")) + is_add = 0; + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (vec_len (inside_sw_if_indices)) + { + for (i = 0; i < vec_len (inside_sw_if_indices); i++) + { + sw_if_index = inside_sw_if_indices[i]; + rv = nat64_add_del_interface (sw_if_index, 1, is_add); + switch (rv) + { + case VNET_API_ERROR_NO_SUCH_ENTRY: + error = + clib_error_return (0, "%U NAT64 feature not enabled.", + format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, sw_if_index)); + goto done; + case VNET_API_ERROR_VALUE_EXIST: + error = + clib_error_return (0, "%U NAT64 feature already enabled.", + format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, sw_if_index)); + goto done; + case VNET_API_ERROR_INVALID_VALUE: + case VNET_API_ERROR_INVALID_VALUE_2: + error = + clib_error_return (0, + "%U NAT64 feature enable/disable failed.", + format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, sw_if_index)); + goto done; + default: + break; + + } + } + } + + if (vec_len (outside_sw_if_indices)) + { + for (i = 0; i < vec_len (outside_sw_if_indices); i++) + { + sw_if_index = outside_sw_if_indices[i]; + rv = nat64_add_del_interface (sw_if_index, 0, is_add); + switch (rv) + { + case VNET_API_ERROR_NO_SUCH_ENTRY: + error = + clib_error_return (0, "%U NAT64 feature not enabled.", + format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, sw_if_index)); + goto done; + case VNET_API_ERROR_VALUE_EXIST: + error = + clib_error_return (0, "%U NAT64 feature already enabled.", + format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, sw_if_index)); + goto done; + case VNET_API_ERROR_INVALID_VALUE: + case VNET_API_ERROR_INVALID_VALUE_2: + error = + clib_error_return (0, + "%U NAT64 feature enable/disable failed.", + format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, sw_if_index)); + goto done; + default: + break; + + } + } + } + +done: + unformat_free (line_input); + vec_free (inside_sw_if_indices); + vec_free (outside_sw_if_indices); + + return error; +} + +static int +nat64_cli_interface_walk (snat_interface_t * i, void *ctx) +{ + vlib_main_t *vm = ctx; + vnet_main_t *vnm = vnet_get_main (); + + vlib_cli_output (vm, " %U %s", format_vnet_sw_interface_name, vnm, + vnet_get_sw_interface (vnm, i->sw_if_index), + i->is_inside ? "in" : "out"); + return 0; +} + +static clib_error_t * +nat64_show_interfaces_command_fn (vlib_main_t * vm, + unformat_input_t * + input, vlib_cli_command_t * cmd) +{ + nat64_main_t *nm = &nat64_main; + + if (nm->is_disabled) + return clib_error_return (0, + "NAT64 disabled, multi thread not supported"); + + vlib_cli_output (vm, "NAT64 interfaces:"); + nat64_interfaces_walk (nat64_cli_interface_walk, vm); + + return 0; +} + +static clib_error_t * +nat64_add_del_static_bib_command_fn (vlib_main_t * + vm, + unformat_input_t + * input, vlib_cli_command_t * cmd) +{ + nat64_main_t *nm = &nat64_main; + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + u8 is_add = 1; + ip6_address_t in_addr; + ip4_address_t out_addr; + u16 in_port = 0; + u16 out_port = 0; + u32 vrf_id = 0, protocol; + snat_protocol_t proto = 0; + u8 p = 0; + int rv; + + if (nm->is_disabled) + return clib_error_return (0, + "NAT64 disabled, multi thread not supported"); + + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "%U %u", unformat_ip6_address, + &in_addr, &in_port)) + ; + else if (unformat (line_input, "%U %u", unformat_ip4_address, + &out_addr, &out_port)) + ; + else if (unformat (line_input, "vrf %u", &vrf_id)) + ; + else if (unformat (line_input, "%U", unformat_snat_protocol, &proto)) + ; + else + if (unformat + (line_input, "%U %U %u", unformat_ip6_address, &in_addr, + unformat_ip4_address, &out_addr, &protocol)) + p = (u8) protocol; + else if (unformat (line_input, "del")) + is_add = 0; + else + { + error = clib_error_return (0, "unknown input: '%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (!p) + { + if (!in_port) + { + error = + clib_error_return (0, "inside port and address must be set"); + goto done; + } + + if (!out_port) + { + error = + clib_error_return (0, "outside port and address must be set"); + goto done; + } + + p = snat_proto_to_ip_proto (proto); + } + + rv = + nat64_add_del_static_bib_entry (&in_addr, &out_addr, in_port, out_port, p, + vrf_id, is_add); + + switch (rv) + { + case VNET_API_ERROR_NO_SUCH_ENTRY: + error = clib_error_return (0, "NAT64 BIB entry not exist."); + goto done; + case VNET_API_ERROR_VALUE_EXIST: + error = clib_error_return (0, "NAT64 BIB entry exist."); + goto done; + case VNET_API_ERROR_UNSPECIFIED: + error = clib_error_return (0, "Crerate NAT64 BIB entry failed."); + goto done; + case VNET_API_ERROR_INVALID_VALUE: + error = + clib_error_return (0, "Outside addres %U and port %u already in use.", + format_ip4_address, &out_addr, out_port); + goto done; + default: + break; + } + +done: + unformat_free (line_input); + + return error; +} + +static int +nat64_cli_bib_walk (nat64_db_bib_entry_t * bibe, void *ctx) +{ + vlib_main_t *vm = ctx; + fib_table_t *fib; + + fib = fib_table_get (bibe->fib_index, FIB_PROTOCOL_IP6); + if (!fib) + return -1; + + switch (bibe->proto) + { + case IP_PROTOCOL_ICMP: + case IP_PROTOCOL_TCP: + case IP_PROTOCOL_UDP: + vlib_cli_output (vm, " %U %u %U %u protocol %U vrf %u %s %u sessions", + format_ip6_address, &bibe->in_addr, + clib_net_to_host_u16 (bibe->in_port), + format_ip4_address, &bibe->out_addr, + clib_net_to_host_u16 (bibe->out_port), + format_snat_protocol, + ip_proto_to_snat_proto (bibe->proto), fib->ft_table_id, + bibe->is_static ? "static" : "dynamic", bibe->ses_num); + break; + default: + vlib_cli_output (vm, " %U %U protocol %u vrf %u %s %u sessions", + format_ip6_address, &bibe->in_addr, + format_ip4_address, &bibe->out_addr, + bibe->proto, fib->ft_table_id, + bibe->is_static ? "static" : "dynamic", bibe->ses_num); + } + return 0; +} + +static clib_error_t * +nat64_show_bib_command_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + nat64_main_t *nm = &nat64_main; + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + u32 proto = ~0; + u8 p = 255; + + if (nm->is_disabled) + return clib_error_return (0, + "NAT64 disabled, multi thread not supported"); + + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + if (unformat (line_input, "%U", unformat_snat_protocol, &proto)) + p = snat_proto_to_ip_proto (proto); + else if (unformat (line_input, "unknown")) + p = 0; + else if (unformat (line_input, "all")) + ; + else + { + error = clib_error_return (0, "unknown input: '%U'", + format_unformat_error, line_input); + goto done; + } + + if (p == 255) + vlib_cli_output (vm, "NAT64 BIB entries:"); + else + vlib_cli_output (vm, "NAT64 %U BIB entries:", format_snat_protocol, + proto); + nat64_db_bib_walk (&nm->db, p, nat64_cli_bib_walk, vm); + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +nat64_set_timeouts_command_fn (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + nat64_main_t *nm = &nat64_main; + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + u32 timeout, tcp_trans, tcp_est, tcp_incoming_syn; + + tcp_trans = nat64_get_tcp_trans_timeout (); + tcp_est = nat64_get_tcp_est_timeout (); + tcp_incoming_syn = nat64_get_tcp_incoming_syn_timeout (); + + if (nm->is_disabled) + return clib_error_return (0, + "NAT64 disabled, multi thread not supported"); + + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "udp %u", &timeout)) + { + if (nat64_set_udp_timeout (timeout)) + { + error = clib_error_return (0, "Invalid UDP timeout value"); + goto done; + } + } + else if (unformat (line_input, "icmp %u", &timeout)) + { + if (nat64_set_icmp_timeout (timeout)) + { + error = clib_error_return (0, "Invalid ICMP timeout value"); + goto done; + } + } + else if (unformat (line_input, "tcp-trans %u", &tcp_trans)) + { + if (nat64_set_tcp_timeouts (tcp_trans, tcp_est, tcp_incoming_syn)) + { + error = + clib_error_return (0, "Invalid TCP transitory tiemout value"); + goto done; + } + } + else if (unformat (line_input, "tcp-est %u", &tcp_est)) + { + if (nat64_set_tcp_timeouts (tcp_trans, tcp_est, tcp_incoming_syn)) + { + error = + clib_error_return (0, + "Invalid TCP established tiemout value"); + goto done; + } + } + else + if (unformat (line_input, "tcp-incoming-syn %u", &tcp_incoming_syn)) + { + if (nat64_set_tcp_timeouts (tcp_trans, tcp_est, tcp_incoming_syn)) + { + error = + clib_error_return (0, + "Invalid TCP incoming SYN tiemout value"); + goto done; + } + } + else if (unformat (line_input, "reset")) + { + nat64_set_udp_timeout (0); + nat64_set_icmp_timeout (0); + nat64_set_tcp_timeouts (0, 0, 0); + } + else + { + error = clib_error_return (0, "unknown input '%U'", + format_unformat_error, line_input); + goto done; + } + } + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +nat64_show_timeouts_command_fn (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + nat64_main_t *nm = &nat64_main; + + if (nm->is_disabled) + return clib_error_return (0, + "NAT64 disabled, multi thread not supported"); + + vlib_cli_output (vm, "NAT64 session timeouts:"); + vlib_cli_output (vm, " UDP %usec", nat64_get_udp_timeout ()); + vlib_cli_output (vm, " ICMP %usec", nat64_get_icmp_timeout ()); + vlib_cli_output (vm, " TCP transitory %usec", + nat64_get_tcp_trans_timeout ()); + vlib_cli_output (vm, " TCP established %usec", + nat64_get_tcp_est_timeout ()); + vlib_cli_output (vm, " TCP incoming SYN %usec", + nat64_get_tcp_incoming_syn_timeout ()); + + return 0; +} + +static int +nat64_cli_st_walk (nat64_db_st_entry_t * ste, void *ctx) +{ + vlib_main_t *vm = ctx; + nat64_main_t *nm = &nat64_main; + nat64_db_bib_entry_t *bibe; + fib_table_t *fib; + + bibe = nat64_db_bib_entry_by_index (&nm->db, ste->proto, ste->bibe_index); + if (!bibe) + return -1; + + fib = fib_table_get (bibe->fib_index, FIB_PROTOCOL_IP6); + if (!fib) + return -1; + + u32 vrf_id = fib->ft_table_id; + + if (ste->proto == IP_PROTOCOL_ICMP) + vlib_cli_output (vm, " %U %U %u %U %U %u protocol %U vrf %u", + format_ip6_address, &bibe->in_addr, + format_ip6_address, &ste->in_r_addr, + clib_net_to_host_u16 (bibe->in_port), + format_ip4_address, &bibe->out_addr, + format_ip4_address, &ste->out_r_addr, + clib_net_to_host_u16 (bibe->out_port), + format_snat_protocol, + ip_proto_to_snat_proto (bibe->proto), vrf_id); + else if (ste->proto == IP_PROTOCOL_TCP || ste->proto == IP_PROTOCOL_UDP) + vlib_cli_output (vm, " %U %u %U %u %U %u %U %u protcol %U vrf %u", + format_ip6_address, &bibe->in_addr, + clib_net_to_host_u16 (bibe->in_port), + format_ip6_address, &ste->in_r_addr, + clib_net_to_host_u16 (ste->r_port), + format_ip4_address, &bibe->out_addr, + clib_net_to_host_u16 (bibe->out_port), + format_ip4_address, &ste->out_r_addr, + clib_net_to_host_u16 (ste->r_port), + format_snat_protocol, + ip_proto_to_snat_proto (bibe->proto), vrf_id); + else + vlib_cli_output (vm, " %U %U %U %U protocol %u vrf %u", + format_ip6_address, &bibe->in_addr, + format_ip6_address, &ste->in_r_addr, + format_ip4_address, &bibe->out_addr, + format_ip4_address, &ste->out_r_addr, + bibe->proto, vrf_id); + + return 0; +} + +static clib_error_t * +nat64_show_st_command_fn (vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + nat64_main_t *nm = &nat64_main; + unformat_input_t _line_input, *line_input = &_line_input; + clib_error_t *error = 0; + u32 proto = ~0; + u8 p = 255; + + if (nm->is_disabled) + return clib_error_return (0, + "NAT64 disabled, multi thread not supported"); + + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + if (unformat (line_input, "%U", unformat_snat_protocol, &proto)) + p = snat_proto_to_ip_proto (proto); + else if (unformat (line_input, "unknown")) + p = 0; + else if (unformat (line_input, "all")) + ; + else + { + error = clib_error_return (0, "unknown input: '%U'", + format_unformat_error, line_input); + goto done; + } + + if (p == 255) + vlib_cli_output (vm, "NAT64 sessions:"); + else + vlib_cli_output (vm, "NAT64 %U sessions:", format_snat_protocol, proto); + nat64_db_st_walk (&nm->db, p, nat64_cli_st_walk, vm); + +done: + unformat_free (line_input); + + return error; +} + +static clib_error_t * +nat64_add_del_prefix_command_fn (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + nat64_main_t *nm = &nat64_main; + clib_error_t *error = 0; + unformat_input_t _line_input, *line_input = &_line_input; + u8 is_add = 1; + u32 vrf_id = 0; + ip6_address_t prefix; + u32 plen = 0; + int rv; + + if (nm->is_disabled) + return clib_error_return (0, + "NAT64 disabled, multi thread not supported"); + + if (!unformat_user (input, unformat_line_input, line_input)) + return 0; + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat + (line_input, "%U/%u", unformat_ip6_address, &prefix, &plen)) + ; + else if (unformat (line_input, "tenant-vrf %u", &vrf_id)) + ; + else if (unformat (line_input, "del")) + is_add = 0; + else + { + error = clib_error_return (0, "unknown input: '%U'", + format_unformat_error, line_input); + goto done; + } + } + + if (!plen) + { + error = clib_error_return (0, "NAT64 prefix must be set."); + goto done; + } + + rv = nat64_add_del_prefix (&prefix, (u8) plen, vrf_id, is_add); + + switch (rv) + { + case VNET_API_ERROR_NO_SUCH_ENTRY: + error = clib_error_return (0, "NAT64 prefix not exist."); + goto done; + case VNET_API_ERROR_INVALID_VALUE: + error = clib_error_return (0, "Invalid prefix length."); + goto done; + default: + break; + } + +done: + unformat_free (line_input); + + return error; +} + +static int +nat64_cli_prefix_walk (nat64_prefix_t * p, void *ctx) +{ + vlib_main_t *vm = ctx; + + vlib_cli_output (vm, " %U/%u tenant-vrf %u", + format_ip6_address, &p->prefix, p->plen, p->vrf_id); + + return 0; +} + +static clib_error_t * +nat64_show_prefix_command_fn (vlib_main_t * vm, unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + nat64_main_t *nm = &nat64_main; + + if (nm->is_disabled) + return clib_error_return (0, + "NAT64 disabled, multi thread not supported"); + + vlib_cli_output (vm, "NAT64 prefix:"); + nat64_prefix_walk (nat64_cli_prefix_walk, vm); + + return 0; +} + +/* *INDENT-OFF* */ + +/*? + * @cliexpar + * @cliexstart{nat64 add pool address} + * Add/delete NAT64 pool address. + * To add single NAT64 pool address use: + * vpp# nat64 add pool address 10.1.1.10 + * To add NAT64 pool address range use: + * vpp# nat64 add pool address 10.1.1.2 - 10.1.1.5 + * To add NAT64 pool address for specific tenant use: + * vpp# nat64 add pool address 10.1.1.100 tenant-vrf 100 + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat64_add_pool_address_command, static) = { + .path = "nat64 add pool address", + .short_help = "nat64 add pool address [- ] " + "[tenant-vrf ] [del]", + .function = nat64_add_del_pool_addr_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{show nat64 pool} + * Show NAT64 pool. + * vpp# show nat64 pool + * NAT64 pool: + * 10.1.1.3 tenant VRF: 0 + * 10.1.1.10 tenant VRF: 10 + * @cliexend +?*/ +VLIB_CLI_COMMAND (show_nat64_pool_command, static) = { + .path = "show nat64 pool", + .short_help = "show nat64 pool", + .function = nat64_show_pool_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{set interface nat64} + * Enable/disable NAT64 feature on the interface. + * To enable NAT64 feature with local (IPv6) network interface + * GigabitEthernet0/8/0 and external (IPv4) network interface + * GigabitEthernet0/a/0 use: + * vpp# set interface nat64 in GigabitEthernet0/8/0 out GigabitEthernet0/a/0 + * @cliexend +?*/ +VLIB_CLI_COMMAND (set_interface_nat64_command, static) = { + .path = "set interface nat64", + .short_help = "set interface nat64 in|out [del]", + .function = nat64_interface_feature_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{show nat64 interfaces} + * Show interfaces with NAT64 feature. + * To show interfaces with NAT64 feature use: + * vpp# show nat64 interfaces + * NAT64 interfaces: + * GigabitEthernet0/8/0 in + * GigabitEthernet0/a/0 out + * @cliexend +?*/ +VLIB_CLI_COMMAND (show_nat64_interfaces_command, static) = { + .path = "show nat64 interfaces", + .short_help = "show nat64 interfaces", + .function = nat64_show_interfaces_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{nat64 add static bib} + * Add/delete NAT64 static BIB entry. + * To create NAT64 satatic BIB entry use: + * vpp# nat64 add static bib 2001:db8:c000:221:: 1234 10.1.1.3 5678 tcp + * vpp# nat64 add static bib 2001:db8:c000:221:: 1234 10.1.1.3 5678 udp vrf 10 + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat64_add_del_static_bib_command, static) = { + .path = "nat64 add static bib", + .short_help = "nat64 add static bib " + "tcp|udp|icmp [vfr ] [del]", + .function = nat64_add_del_static_bib_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{show nat64 bib} + * Show NAT64 BIB entries. + * To show NAT64 TCP BIB entries use: + * vpp# show nat64 bib tcp + * NAT64 tcp BIB: + * fd01:1::2 6303 10.0.0.3 62303 tcp vrf 0 dynamic 1 sessions + * 2001:db8:c000:221:: 1234 10.1.1.3 5678 tcp vrf 0 static 2 sessions + * To show NAT64 UDP BIB entries use: + * vpp# show nat64 bib udp + * NAT64 udp BIB: + * fd01:1::2 6304 10.0.0.3 10546 udp vrf 0 dynamic 10 sessions + * 2001:db8:c000:221:: 1234 10.1.1.3 5678 udp vrf 10 static 0 sessions + * To show NAT64 ICMP BIB entries use: + * vpp# show nat64 bib icmp + * NAT64 icmp BIB: + * fd01:1::2 6305 10.0.0.3 63209 icmp vrf 10 dynamic 1 sessions + * @cliexend +?*/ +VLIB_CLI_COMMAND (show_nat64_bib_command, static) = { + .path = "show nat64 bib", + .short_help = "show nat64 bib all|tcp|udp|icmp|unknown", + .function = nat64_show_bib_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{set nat64 timeouts} + * Set NAT64 session timeouts (in seconds). + * To set NAT64 session timeoutes use use: + * vpp# set nat64 timeouts udp 200 icmp 30 tcp-trans 250 tcp-est 7450 + * To reset NAT64 session timeoutes to default values use: + * vpp# set nat64 timeouts reset + * @cliexend +?*/ +VLIB_CLI_COMMAND (set_nat64_timeouts_command, static) = { + .path = "set nat64 timeouts", + .short_help = "set nat64 timeouts udp icmp tcp-trans " + "tcp-est tcp-incoming-syn | reset", + .function = nat64_set_timeouts_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{show nat64 tiemouts} + * Show NAT64 session timeouts: + * vpp# show nat64 tiemouts + * NAT64 session timeouts: + * UDP 300sec + * ICMP 60sec + * TCP transitory 240sec + * TCP established 7440sec + * TCP incoming SYN 6sec + * @cliexend +?*/ +VLIB_CLI_COMMAND (show_nat64_timeouts_command, static) = { + .path = "show nat64 tiemouts", + .short_help = "show nat64 tiemouts", + .function = nat64_show_timeouts_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{show nat64 session table} + * Show NAT64 session table. + * To show NAT64 TCP session table use: + * vpp# show nat64 session table tcp + * NAT64 tcp session table: + * fd01:1::2 6303 64:ff9b::ac10:202 20 10.0.0.3 62303 172.16.2.2 20 tcp vrf 0 + * fd01:3::2 6303 64:ff9b::ac10:202 20 10.0.10.3 21300 172.16.2.2 20 tcp vrf 10 + * To show NAT64 UDP session table use: + * #vpp show nat64 session table udp + * NAT64 udp session table: + * fd01:1::2 6304 64:ff9b::ac10:202 20 10.0.0.3 10546 172.16.2.2 20 udp vrf 0 + * fd01:3::2 6304 64:ff9b::ac10:202 20 10.0.10.3 58627 172.16.2.2 20 udp vrf 10 + * fd01:1::2 1235 64:ff9b::a00:3 4023 10.0.0.3 24488 10.0.0.3 4023 udp vrf 0 + * fd01:1::3 23 64:ff9b::a00:3 24488 10.0.0.3 4023 10.0.0.3 24488 udp vrf 0 + * To show NAT64 ICMP session table use: + * #vpp show nat64 session table icmp + * NAT64 icmp session table: + * fd01:1::2 64:ff9b::ac10:202 6305 10.0.0.3 172.16.2.2 63209 icmp vrf 0 + * @cliexend +?*/ +VLIB_CLI_COMMAND (show_nat64_st_command, static) = { + .path = "show nat64 session table", + .short_help = "show nat64 session table all|tcp|udp|icmp|unknown", + .function = nat64_show_st_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{nat64 add prefix} + * Set NAT64 prefix for generating IPv6 representations of IPv4 addresses. + * To set NAT64 global prefix use: + * vpp# nat64 add prefix 2001:db8::/32 + * To set NAT64 prefix for specific tenant use: + * vpp# nat64 add prefix 2001:db8:122:300::/56 tenant-vrf 10 + * @cliexend +?*/ +VLIB_CLI_COMMAND (nat64_add_del_prefix_command, static) = { + .path = "nat64 add prefix", + .short_help = "nat64 add prefix / [tenant-vrf ] " + "[del]", + .function = nat64_add_del_prefix_command_fn, +}; + +/*? + * @cliexpar + * @cliexstart{show nat64 prefix} + * Show NAT64 prefix. + * To show NAT64 prefix use: + * vpp# show nat64 prefix + * NAT64 prefix: + * 2001:db8::/32 tenant-vrf 0 + * 2001:db8:122:300::/56 tenant-vrf 10 + * @cliexend +?*/ +VLIB_CLI_COMMAND (show_nat64_prefix_command, static) = { + .path = "show nat64 prefix", + .short_help = "show nat64 prefix", + .function = nat64_show_prefix_command_fn, +}; + +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat64_db.c b/src/plugins/nat/nat64_db.c new file mode 100644 index 00000000..da73ceee --- /dev/null +++ b/src/plugins/nat/nat64_db.c @@ -0,0 +1,603 @@ +/* + * 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. + */ +/** + * @file + * @brief NAT64 DB + */ +#include + +int +nat64_db_init (nat64_db_t * db) +{ + u32 bib_buckets = 1024; + u32 bib_memory_size = 128 << 20; + u32 st_buckets = 2048; + u32 st_memory_size = 256 << 20; + + clib_bihash_init_24_8 (&db->bib.in2out, "bib-in2out", bib_buckets, + bib_memory_size); + + clib_bihash_init_24_8 (&db->bib.out2in, "bib-out2in", bib_buckets, + bib_memory_size); + + clib_bihash_init_48_8 (&db->st.in2out, "st-in2out", st_buckets, + st_memory_size); + + clib_bihash_init_48_8 (&db->st.out2in, "st-out2in", st_buckets, + st_memory_size); + + return 0; +} + +nat64_db_bib_entry_t * +nat64_db_bib_entry_create (nat64_db_t * db, ip6_address_t * in_addr, + ip4_address_t * out_addr, u16 in_port, + u16 out_port, u32 fib_index, u8 proto, + u8 is_static) +{ + nat64_db_bib_entry_t *bibe; + nat64_db_bib_entry_key_t bibe_key; + clib_bihash_kv_24_8_t kv; + + /* create pool entry */ + switch (ip_proto_to_snat_proto (proto)) + { +/* *INDENT-OFF* */ +#define _(N, i, n, s) \ + case SNAT_PROTOCOL_##N: \ + pool_get (db->bib._##n##_bib, bibe); \ + kv.value = bibe - db->bib._##n##_bib; \ + break; + foreach_snat_protocol +#undef _ +/* *INDENT-ON* */ + default: + pool_get (db->bib._unk_proto_bib, bibe); + kv.value = bibe - db->bib._unk_proto_bib; + break; + } + memset (bibe, 0, sizeof (*bibe)); + bibe->in_addr.as_u64[0] = in_addr->as_u64[0]; + bibe->in_addr.as_u64[1] = in_addr->as_u64[1]; + bibe->in_port = in_port; + bibe->out_addr.as_u32 = out_addr->as_u32; + bibe->out_port = out_port; + bibe->fib_index = fib_index; + bibe->proto = proto; + bibe->is_static = is_static; + + /* create hash lookup */ + bibe_key.addr.as_u64[0] = bibe->in_addr.as_u64[0]; + bibe_key.addr.as_u64[1] = bibe->in_addr.as_u64[1]; + bibe_key.fib_index = bibe->fib_index; + bibe_key.port = bibe->in_port; + bibe_key.proto = bibe->proto; + bibe_key.rsvd = 0; + kv.key[0] = bibe_key.as_u64[0]; + kv.key[1] = bibe_key.as_u64[1]; + kv.key[2] = bibe_key.as_u64[2]; + clib_bihash_add_del_24_8 (&db->bib.in2out, &kv, 1); + + memset (&bibe_key.addr, 0, sizeof (bibe_key.addr)); + bibe_key.addr.ip4.as_u32 = bibe->out_addr.as_u32; + bibe_key.fib_index = 0; + bibe_key.port = bibe->out_port; + kv.key[0] = bibe_key.as_u64[0]; + kv.key[1] = bibe_key.as_u64[1]; + kv.key[2] = bibe_key.as_u64[2]; + clib_bihash_add_del_24_8 (&db->bib.out2in, &kv, 1); + + return bibe; +} + +void +nat64_db_bib_entry_free (nat64_db_t * db, nat64_db_bib_entry_t * bibe) +{ + nat64_db_bib_entry_key_t bibe_key; + clib_bihash_kv_24_8_t kv; + nat64_db_bib_entry_t *bib; + u32 *ste_to_be_free = 0, *ste_index, bibe_index; + nat64_db_st_entry_t *st, *ste; + + switch (ip_proto_to_snat_proto (bibe->proto)) + { +/* *INDENT-OFF* */ +#define _(N, i, n, s) \ + case SNAT_PROTOCOL_##N: \ + bib = db->bib._##n##_bib; \ + st = db->st._##n##_st; \ + break; + foreach_snat_protocol +#undef _ +/* *INDENT-ON* */ + default: + bib = db->bib._unk_proto_bib; + st = db->st._unk_proto_st; + break; + } + + bibe_index = bibe - bib; + + /* delete ST entries for static BIB entry */ + if (bibe->is_static) + { + pool_foreach (ste, st, ( + { + if (ste->bibe_index == bibe_index) + vec_add1 (ste_to_be_free, ste - st);} + )); + vec_foreach (ste_index, ste_to_be_free) + nat64_db_st_entry_free (db, pool_elt_at_index (st, ste_index[0])); + vec_free (ste_to_be_free); + } + + /* delete hash lookup */ + bibe_key.addr.as_u64[0] = bibe->in_addr.as_u64[0]; + bibe_key.addr.as_u64[1] = bibe->in_addr.as_u64[1]; + bibe_key.fib_index = bibe->fib_index; + bibe_key.port = bibe->in_port; + bibe_key.proto = bibe->proto; + bibe_key.rsvd = 0; + kv.key[0] = bibe_key.as_u64[0]; + kv.key[1] = bibe_key.as_u64[1]; + kv.key[2] = bibe_key.as_u64[2]; + clib_bihash_add_del_24_8 (&db->bib.in2out, &kv, 0); + + memset (&bibe_key.addr, 0, sizeof (bibe_key.addr)); + bibe_key.addr.ip4.as_u32 = bibe->out_addr.as_u32; + bibe_key.fib_index = 0; + bibe_key.port = bibe->out_port; + kv.key[0] = bibe_key.as_u64[0]; + kv.key[1] = bibe_key.as_u64[1]; + kv.key[2] = bibe_key.as_u64[2]; + clib_bihash_add_del_24_8 (&db->bib.out2in, &kv, 0); + + /* delete from pool */ + pool_put (bib, bibe); + +} + +nat64_db_bib_entry_t * +nat64_db_bib_entry_find (nat64_db_t * db, ip46_address_t * addr, u16 port, + u8 proto, u32 fib_index, u8 is_ip6) +{ + nat64_db_bib_entry_t *bibe = 0; + nat64_db_bib_entry_key_t bibe_key; + clib_bihash_kv_24_8_t kv, value; + nat64_db_bib_entry_t *bib; + + switch (ip_proto_to_snat_proto (proto)) + { +/* *INDENT-OFF* */ +#define _(N, i, n, s) \ + case SNAT_PROTOCOL_##N: \ + bib = db->bib._##n##_bib; \ + break; + foreach_snat_protocol +#undef _ +/* *INDENT-ON* */ + default: + bib = db->bib._unk_proto_bib; + break; + } + + bibe_key.addr.as_u64[0] = addr->as_u64[0]; + bibe_key.addr.as_u64[1] = addr->as_u64[1]; + bibe_key.fib_index = fib_index; + bibe_key.port = port; + bibe_key.proto = proto; + bibe_key.rsvd = 0; + + kv.key[0] = bibe_key.as_u64[0]; + kv.key[1] = bibe_key.as_u64[1]; + kv.key[2] = bibe_key.as_u64[2]; + + if (!clib_bihash_search_24_8 + (is_ip6 ? &db->bib.in2out : &db->bib.out2in, &kv, &value)) + bibe = pool_elt_at_index (bib, value.value); + + return bibe; +} + +void +nat64_db_bib_walk (nat64_db_t * db, u8 proto, + nat64_db_bib_walk_fn_t fn, void *ctx) +{ + nat64_db_bib_entry_t *bib, *bibe; + + if (proto == 255) + { + /* *INDENT-OFF* */ + #define _(N, i, n, s) \ + bib = db->bib._##n##_bib; \ + pool_foreach (bibe, bib, ({ \ + if (fn (bibe, ctx)) \ + return; \ + })); + foreach_snat_protocol + #undef _ + bib = db->bib._unk_proto_bib; + pool_foreach (bibe, bib, ({ + if (fn (bibe, ctx)) + return; + })); + /* *INDENT-ON* */ + } + else + { + switch (ip_proto_to_snat_proto (proto)) + { + /* *INDENT-OFF* */ + #define _(N, i, n, s) \ + case SNAT_PROTOCOL_##N: \ + bib = db->bib._##n##_bib; \ + break; + foreach_snat_protocol + #undef _ + /* *INDENT-ON* */ + default: + bib = db->bib._unk_proto_bib; + break; + } + + /* *INDENT-OFF* */ + pool_foreach (bibe, bib, + ({ + if (fn (bibe, ctx)) + return; + })); + /* *INDENT-ON* */ + } +} + +nat64_db_bib_entry_t * +nat64_db_bib_entry_by_index (nat64_db_t * db, u8 proto, u32 bibe_index) +{ + nat64_db_bib_entry_t *bib; + + switch (ip_proto_to_snat_proto (proto)) + { +/* *INDENT-OFF* */ +#define _(N, i, n, s) \ + case SNAT_PROTOCOL_##N: \ + bib = db->bib._##n##_bib; \ + break; + foreach_snat_protocol +#undef _ +/* *INDENT-ON* */ + default: + bib = db->bib._unk_proto_bib; + break; + } + + return pool_elt_at_index (bib, bibe_index); +} + +void +nat64_db_st_walk (nat64_db_t * db, u8 proto, + nat64_db_st_walk_fn_t fn, void *ctx) +{ + nat64_db_st_entry_t *st, *ste; + + if (proto == 255) + { + /* *INDENT-OFF* */ + #define _(N, i, n, s) \ + st = db->st._##n##_st; \ + pool_foreach (ste, st, ({ \ + if (fn (ste, ctx)) \ + return; \ + })); + foreach_snat_protocol + #undef _ + st = db->st._unk_proto_st; + pool_foreach (ste, st, ({ + if (fn (ste, ctx)) + return; + })); + /* *INDENT-ON* */ + } + else + { + switch (ip_proto_to_snat_proto (proto)) + { + /* *INDENT-OFF* */ + #define _(N, i, n, s) \ + case SNAT_PROTOCOL_##N: \ + st = db->st._##n##_st; \ + break; + foreach_snat_protocol + #undef _ + /* *INDENT-ON* */ + default: + st = db->st._unk_proto_st; + break; + } + + /* *INDENT-OFF* */ + pool_foreach (ste, st, + ({ + if (fn (ste, ctx)) + return; + })); + /* *INDENT-ON* */ + } +} + +nat64_db_st_entry_t * +nat64_db_st_entry_create (nat64_db_t * db, nat64_db_bib_entry_t * bibe, + ip6_address_t * in_r_addr, + ip4_address_t * out_r_addr, u16 r_port) +{ + nat64_db_st_entry_t *ste; + nat64_db_bib_entry_t *bib; + nat64_db_st_entry_key_t ste_key; + clib_bihash_kv_48_8_t kv; + + /* create pool entry */ + switch (ip_proto_to_snat_proto (bibe->proto)) + { +/* *INDENT-OFF* */ +#define _(N, i, n, s) \ + case SNAT_PROTOCOL_##N: \ + pool_get (db->st._##n##_st, ste); \ + kv.value = ste - db->st._##n##_st; \ + bib = db->bib._##n##_bib; \ + break; + foreach_snat_protocol +#undef _ +/* *INDENT-ON* */ + default: + pool_get (db->st._unk_proto_st, ste); + kv.value = ste - db->st._unk_proto_st; + bib = db->bib._unk_proto_bib; + break; + } + memset (ste, 0, sizeof (*ste)); + ste->in_r_addr.as_u64[0] = in_r_addr->as_u64[0]; + ste->in_r_addr.as_u64[1] = in_r_addr->as_u64[1]; + ste->out_r_addr.as_u32 = out_r_addr->as_u32; + ste->r_port = r_port; + ste->bibe_index = bibe - bib; + ste->proto = bibe->proto; + + /* increment session number for BIB entry */ + bibe->ses_num++; + + /* create hash lookup */ + memset (&ste_key, 0, sizeof (ste_key)); + ste_key.l_addr.as_u64[0] = bibe->in_addr.as_u64[0]; + ste_key.l_addr.as_u64[1] = bibe->in_addr.as_u64[1]; + ste_key.r_addr.as_u64[0] = ste->in_r_addr.as_u64[0]; + ste_key.r_addr.as_u64[1] = ste->in_r_addr.as_u64[1]; + ste_key.fib_index = bibe->fib_index; + ste_key.l_port = bibe->in_port; + ste_key.r_port = ste->r_port; + ste_key.proto = ste->proto; + kv.key[0] = ste_key.as_u64[0]; + kv.key[1] = ste_key.as_u64[1]; + kv.key[2] = ste_key.as_u64[2]; + kv.key[3] = ste_key.as_u64[3]; + kv.key[4] = ste_key.as_u64[4]; + kv.key[5] = ste_key.as_u64[5]; + clib_bihash_add_del_48_8 (&db->st.in2out, &kv, 1); + + memset (&ste_key, 0, sizeof (ste_key)); + ste_key.l_addr.ip4.as_u32 = bibe->out_addr.as_u32; + ste_key.r_addr.ip4.as_u32 = ste->out_r_addr.as_u32; + ste_key.l_port = bibe->out_port; + ste_key.r_port = ste->r_port; + ste_key.proto = ste->proto; + kv.key[0] = ste_key.as_u64[0]; + kv.key[1] = ste_key.as_u64[1]; + kv.key[2] = ste_key.as_u64[2]; + kv.key[3] = ste_key.as_u64[3]; + kv.key[4] = ste_key.as_u64[4]; + kv.key[5] = ste_key.as_u64[5]; + clib_bihash_add_del_48_8 (&db->st.out2in, &kv, 1); + + return ste; +} + +void +nat64_db_st_entry_free (nat64_db_t * db, nat64_db_st_entry_t * ste) +{ + nat64_db_st_entry_t *st; + nat64_db_bib_entry_t *bib, *bibe; + nat64_db_st_entry_key_t ste_key; + clib_bihash_kv_48_8_t kv; + + switch (ip_proto_to_snat_proto (ste->proto)) + { +/* *INDENT-OFF* */ +#define _(N, i, n, s) \ + case SNAT_PROTOCOL_##N: \ + st = db->st._##n##_st; \ + bib = db->bib._##n##_bib; \ + break; + foreach_snat_protocol +#undef _ +/* *INDENT-ON* */ + default: + st = db->st._unk_proto_st; + bib = db->bib._unk_proto_bib; + break; + } + + bibe = pool_elt_at_index (bib, ste->bibe_index); + + /* delete hash lookup */ + memset (&ste_key, 0, sizeof (ste_key)); + ste_key.l_addr.as_u64[0] = bibe->in_addr.as_u64[0]; + ste_key.l_addr.as_u64[1] = bibe->in_addr.as_u64[1]; + ste_key.r_addr.as_u64[0] = ste->in_r_addr.as_u64[0]; + ste_key.r_addr.as_u64[1] = ste->in_r_addr.as_u64[1]; + ste_key.fib_index = bibe->fib_index; + ste_key.l_port = bibe->in_port; + ste_key.r_port = ste->r_port; + ste_key.proto = ste->proto; + kv.key[0] = ste_key.as_u64[0]; + kv.key[1] = ste_key.as_u64[1]; + kv.key[2] = ste_key.as_u64[2]; + kv.key[3] = ste_key.as_u64[3]; + kv.key[4] = ste_key.as_u64[4]; + kv.key[5] = ste_key.as_u64[5]; + clib_bihash_add_del_48_8 (&db->st.in2out, &kv, 0); + + memset (&ste_key, 0, sizeof (ste_key)); + ste_key.l_addr.ip4.as_u32 = bibe->out_addr.as_u32; + ste_key.r_addr.ip4.as_u32 = ste->out_r_addr.as_u32; + ste_key.l_port = bibe->out_port; + ste_key.r_port = ste->r_port; + ste_key.proto = ste->proto; + kv.key[0] = ste_key.as_u64[0]; + kv.key[1] = ste_key.as_u64[1]; + kv.key[2] = ste_key.as_u64[2]; + kv.key[3] = ste_key.as_u64[3]; + kv.key[4] = ste_key.as_u64[4]; + kv.key[5] = ste_key.as_u64[5]; + clib_bihash_add_del_48_8 (&db->st.out2in, &kv, 0); + + /* delete from pool */ + pool_put (st, ste); + + /* decrement session number for BIB entry */ + bibe->ses_num--; + + /* delete BIB entry if last session and dynamic */ + if (!bibe->is_static && !bibe->ses_num) + nat64_db_bib_entry_free (db, bibe); +} + +nat64_db_st_entry_t * +nat64_db_st_entry_find (nat64_db_t * db, ip46_address_t * l_addr, + ip46_address_t * r_addr, u16 l_port, u16 r_port, + u8 proto, u32 fib_index, u8 is_ip6) +{ + nat64_db_st_entry_t *ste = 0; + nat64_db_st_entry_t *st; + nat64_db_st_entry_key_t ste_key; + clib_bihash_kv_48_8_t kv, value; + + switch (ip_proto_to_snat_proto (proto)) + { +/* *INDENT-OFF* */ +#define _(N, i, n, s) \ + case SNAT_PROTOCOL_##N: \ + st = db->st._##n##_st; \ + break; + foreach_snat_protocol +#undef _ +/* *INDENT-ON* */ + default: + st = db->st._unk_proto_st; + break; + } + + memset (&ste_key, 0, sizeof (ste_key)); + ste_key.l_addr.as_u64[0] = l_addr->as_u64[0]; + ste_key.l_addr.as_u64[1] = l_addr->as_u64[1]; + ste_key.r_addr.as_u64[0] = r_addr->as_u64[0]; + ste_key.r_addr.as_u64[1] = r_addr->as_u64[1]; + ste_key.fib_index = fib_index; + ste_key.l_port = l_port; + ste_key.r_port = r_port; + ste_key.proto = proto; + kv.key[0] = ste_key.as_u64[0]; + kv.key[1] = ste_key.as_u64[1]; + kv.key[2] = ste_key.as_u64[2]; + kv.key[3] = ste_key.as_u64[3]; + kv.key[4] = ste_key.as_u64[4]; + kv.key[5] = ste_key.as_u64[5]; + + if (!clib_bihash_search_48_8 + (is_ip6 ? &db->st.in2out : &db->st.out2in, &kv, &value)) + ste = pool_elt_at_index (st, value.value); + + return ste; +} + +void +nad64_db_st_free_expired (nat64_db_t * db, u32 now) +{ + u32 *ste_to_be_free = 0, *ste_index; + nat64_db_st_entry_t *st, *ste; + +/* *INDENT-OFF* */ +#define _(N, i, n, s) \ + st = db->st._##n##_st; \ + pool_foreach (ste, st, ({\ + if (i == SNAT_PROTOCOL_TCP && !ste->tcp_state) \ + continue; \ + if (ste->expire < now) \ + vec_add1 (ste_to_be_free, ste - st); \ + })); \ + vec_foreach (ste_index, ste_to_be_free) \ + nat64_db_st_entry_free (db, pool_elt_at_index(st, ste_index[0])); \ + vec_free (ste_to_be_free); \ + ste_to_be_free = 0; + foreach_snat_protocol +#undef _ + st = db->st._unk_proto_st; + pool_foreach (ste, st, ({ + if (ste->expire < now) + vec_add1 (ste_to_be_free, ste - st); + })); + vec_foreach (ste_index, ste_to_be_free) + nat64_db_st_entry_free (db, pool_elt_at_index(st, ste_index[0])); + vec_free (ste_to_be_free); +/* *INDENT-ON* */ +} + +void +nat64_db_free_out_addr (nat64_db_t * db, ip4_address_t * out_addr) +{ + u32 *ste_to_be_free = 0, *ste_index; + nat64_db_st_entry_t *st, *ste; + nat64_db_bib_entry_t *bibe; + +/* *INDENT-OFF* */ +#define _(N, i, n, s) \ + st = db->st._##n##_st; \ + pool_foreach (ste, st, ({ \ + bibe = pool_elt_at_index (db->bib._##n##_bib, ste->bibe_index); \ + if (bibe->out_addr.as_u32 == out_addr->as_u32) \ + vec_add1 (ste_to_be_free, ste - st); \ + })); \ + vec_foreach (ste_index, ste_to_be_free) \ + nat64_db_st_entry_free (db, pool_elt_at_index(st, ste_index[0])); \ + vec_free (ste_to_be_free); \ + ste_to_be_free = 0; + foreach_snat_protocol +#undef _ + st = db->st._unk_proto_st; + pool_foreach (ste, st, ({ + bibe = pool_elt_at_index (db->bib._unk_proto_bib, ste->bibe_index); + if (bibe->out_addr.as_u32 == out_addr->as_u32) + vec_add1 (ste_to_be_free, ste - st); + })); + vec_foreach (ste_index, ste_to_be_free) + nat64_db_st_entry_free (db, pool_elt_at_index(st, ste_index[0])); + vec_free (ste_to_be_free); +/* *INDENT-ON* */ +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat64_db.h b/src/plugins/nat/nat64_db.h new file mode 100644 index 00000000..394ca875 --- /dev/null +++ b/src/plugins/nat/nat64_db.h @@ -0,0 +1,307 @@ +/* + * 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. + */ +/** + * @file + * @brief NAT64 DB + */ +#ifndef __included_nat64_db_h__ +#define __included_nat64_db_h__ + +#include +#include +#include + + +typedef struct +{ + union + { + struct + { + ip46_address_t addr; + u32 fib_index; + u16 port; + u8 proto; + u8 rsvd; + }; + u64 as_u64[3]; + }; +} nat64_db_bib_entry_key_t; + +/* *INDENT-OFF* */ +typedef CLIB_PACKED(struct +{ + ip6_address_t in_addr; + u16 in_port; + ip4_address_t out_addr; + u16 out_port; + u32 fib_index; + u32 ses_num; + u8 proto; + u8 is_static; +}) nat64_db_bib_entry_t; +/* *INDENT-ON* */ + +typedef struct +{ + /* BIBs */ +/* *INDENT-OFF* */ +#define _(N, i, n, s) \ + nat64_db_bib_entry_t *_##n##_bib; + foreach_snat_protocol +#undef _ +/* *INDENT-ON* */ + nat64_db_bib_entry_t *_unk_proto_bib; + + /* BIB lookup */ + clib_bihash_24_8_t in2out; + clib_bihash_24_8_t out2in; +} nat64_db_bib_t; + +typedef struct +{ + union + { + struct + { + ip46_address_t l_addr; + ip46_address_t r_addr; + u32 fib_index; + u16 l_port; + u16 r_port; + u8 proto; + u8 rsvd[7]; + }; + u64 as_u64[6]; + }; +} nat64_db_st_entry_key_t; + +/* *INDENT-OFF* */ +typedef CLIB_PACKED(struct +{ + ip6_address_t in_r_addr; + ip4_address_t out_r_addr; + u16 r_port; + u32 bibe_index; + u32 expire; + u8 proto; + u8 tcp_state; +}) nat64_db_st_entry_t; +/* *INDENT-ON* */ + +typedef struct +{ + /* session tables */ +/* *INDENT-OFF* */ +#define _(N, i, n, s) \ + nat64_db_st_entry_t *_##n##_st; + foreach_snat_protocol +#undef _ +/* *INDENT-ON* */ + nat64_db_st_entry_t *_unk_proto_st; + + /* session lookup */ + clib_bihash_48_8_t in2out; + clib_bihash_48_8_t out2in; +} nat64_db_st_t; + +typedef struct +{ + nat64_db_bib_t bib; + nat64_db_st_t st; +} nat64_db_t; + +/** + * @brief Initialize NAT64 DB. + * + * @param db NAT64 DB. + * + * @returns 0 on success, non-zero value otherwise. + */ +int nat64_db_init (nat64_db_t * db); + +/** + * @brief Create new NAT64 BIB entry. + * + * @param db NAT64 DB. + * @param in_addr Inside IPv6 address. + * @param out_addr Outside IPv4 address. + * @param in_port Inside port number. + * @param out_port Outside port number. + * @param fib_index FIB index. + * @param proto L4 protocol. + * @param is_static 1 if static, 0 if dynamic. + * + * @returns BIB entry on success, 0 otherwise. + */ +nat64_db_bib_entry_t *nat64_db_bib_entry_create (nat64_db_t * db, + ip6_address_t * in_addr, + ip4_address_t * out_addr, + u16 in_port, u16 out_port, + u32 fib_index, + u8 proto, u8 is_static); + +/** + * @brief Free NAT64 BIB entry. + * + * @param db NAT64 DB. + * @param bibe BIB entry. + */ +void nat64_db_bib_entry_free (nat64_db_t * db, nat64_db_bib_entry_t * bibe); + +/** + * @brief Call back function when walking NAT64 BIB, non-zero + * return value stop walk. + */ +typedef int (*nat64_db_bib_walk_fn_t) (nat64_db_bib_entry_t * bibe, + void *ctx); +/** + * @brief Walk NAT64 BIB. + * + * @param db NAT64 DB. + * @param proto BIB L4 protocol: + * - 255 all BIBs + * - 6 TCP BIB + * - 17 UDP BIB + * - 1/58 ICMP BIB + * - otherwise "unknown" protocol BIB + * @param fn The function to invoke on each entry visited. + * @param ctx A context passed in the visit function. + */ +void nat64_db_bib_walk (nat64_db_t * db, u8 proto, + nat64_db_bib_walk_fn_t fn, void *ctx); + +/** + * @brief Find NAT64 BIB entry. + * + * @param db NAT64 DB. + * @param addr IP address. + * @param port Port number. + * @param proto L4 protocol. + * @param fib_index FIB index. + * @param is_ip6 1 if find by IPv6 (inside) address, 0 by IPv4 (outside). + * + * @return BIB entry if found. + */ +nat64_db_bib_entry_t *nat64_db_bib_entry_find (nat64_db_t * db, + ip46_address_t * addr, + u16 port, + u8 proto, + u32 fib_index, u8 is_ip6); + +/** + * @brief Get BIB entry by index and protocol. + * + * @param db NAT64 DB. + * @param proto L4 protocol. + * @param bibe_index BIB entry index. + * + * @return BIB entry if found. + */ +nat64_db_bib_entry_t *nat64_db_bib_entry_by_index (nat64_db_t * db, + u8 proto, u32 bibe_index); +/** + * @brief Create new NAT64 session table entry. + * + * @param db NAT64 DB. + * @param bibe Corresponding BIB entry. + * @param in_r_addr Inside IPv6 address of the remote host. + * @param out_r_addr Outside IPv4 address of the remote host. + * @param r_port Remote host port number. + * + * @returns BIB entry on success, 0 otherwise. + */ +nat64_db_st_entry_t *nat64_db_st_entry_create (nat64_db_t * db, + nat64_db_bib_entry_t * bibe, + ip6_address_t * in_r_addr, + ip4_address_t * out_r_addr, + u16 r_port); + +/** + * @brief Free NAT64 session table entry. + * + * @param db NAT64 DB. + * @param ste Session table entry. + */ +void nat64_db_st_entry_free (nat64_db_t * db, nat64_db_st_entry_t * ste); + +/** + * @brief Find NAT64 session table entry. + * + * @param db NAT64 DB. + * @param l_addr Local host address. + * @param r_addr Remote host address. + * @param l_port Local host port number. + * @param r_port Remote host port number. + * @param proto L4 protocol. + * @param fib_index FIB index. + * @param is_ip6 1 if find by IPv6 (inside) address, 0 by IPv4 (outside). + * + * @return BIB entry if found. + */ +nat64_db_st_entry_t *nat64_db_st_entry_find (nat64_db_t * db, + ip46_address_t * l_addr, + ip46_address_t * r_addr, + u16 l_port, u16 r_port, + u8 proto, + u32 fib_index, u8 is_ip6); + +/** + * @brief Call back function when walking NAT64 session table, non-zero + * return value stop walk. + */ +typedef int (*nat64_db_st_walk_fn_t) (nat64_db_st_entry_t * ste, void *ctx); + +/** + * @brief Walk NAT64 session table. + * + * @param db NAT64 DB. + * @param proto L4 protocol: + * - 255 all session tables + * - 6 TCP session table + * - 17 UDP session table + * - 1/58 ICMP session table + * - otherwise "unknown" protocol session table + * @param fn The function to invoke on each entry visited. + * @param ctx A context passed in the visit function. + */ +void nat64_db_st_walk (nat64_db_t * db, u8 proto, + nat64_db_st_walk_fn_t fn, void *ctx); + +/** + * @brief Free expired session entries in session tables. + * + * @param db NAT64 DB. + * @param now Current time. + */ +void nad64_db_st_free_expired (nat64_db_t * db, u32 now); + +/** + * @brief Free sessions using specific outside address. + * + * @param db NAT64 DB. + * @param out_addr Outside address to match. + */ +void nat64_db_free_out_addr (nat64_db_t * db, ip4_address_t * out_addr); + +#endif /* __included_nat64_db_h__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat64_doc.md b/src/plugins/nat/nat64_doc.md new file mode 100644 index 00000000..f94467da --- /dev/null +++ b/src/plugins/nat/nat64_doc.md @@ -0,0 +1,73 @@ +# Stateful NAT64: Network Address and Protocol Translation from IPv6 Clients to IPv4 Servers {#nat64_doc} + +## Introduction + +Stateful NAT64 in VPP allows IPv6-only clients to contact IPv4 servers using unicast UDP, TCP, or ICMP based on RFC 6146. + +## Configuration + +### Enable/disable NAT64 feature on the interface + +> set interface nat64 in|out [del] + +in: inside/local/IPv6 network +out: outside/external/IPv4 network +intfc: interface name + +### Add/delete NAT64 pool address + +One or more public IPv4 addresses assigned to a NAT64 are shared among several IPv6-only clients. + +> nat64 add pool address [- ] [tenant-vrf ] [del] + +ip4-range-start: First IPv4 address of the range +ip4-range-end: Last IPv4 address of the range (optional, not used for single address) +tenant-vrf-id: VRF id of the tenant associated with the pool address (optional, if not set pool address is global) + +### Add/delete static BIB entry + +Stateful NAT64 also supports IPv4-initiated communications to a subset of the IPv6 hosts through staticaly configured bindings. + +> nat64 add static bib tcp|udp|icmp [vfr ] [del] + +ip6-addr: inside IPv6 address of the host +in-port: inside port or ICMPv6 identifier +ip4-addr: outside IPv4 address of the host +out-port: outside port or ICMPv4 identifier +table-id: VRF id of the tenant associated with the BIB entry (optional, default use global VRF) + +### Set NAT64 session timeouts + +Session is deleted when timer expires. If all sessions corresponding to a dynamically create BIB entry are deleted, then the BIB entry is also deleted. When packets are flowing sessiom timer is refreshed to keep the session alive. + +> set nat64 timeouts udp icmp tcp-trans tcp-est tcp-incoming-syn | reset + +udp: UDP session timeout value (default 300sec) +icmp: ICMP session timeout value (default 60sec) +tcp-trans: transitory TCP session timeout value (default 240sec) +tcp-est: established TCP session timeout value (default 7440sec) +tcp-incoming-syn: incoming SYN TCP session timeout value (default 6sec) +reset: reset timers to default values + +### Set NAT64 prefix + +Stateful NAT64 support the algorithm for generating IPv6 representations of IPv4 addresses defined in RFC 6052. If no prefix is configured, Well-Known Prefix (64:ff9b::/96) is used. + +> nat64 add prefix / [tenant-vrf ] [del] + +ip6-prefix: IPv6 prefix +plen: prefix length (valid values: 32, 40, 48, 56, 64, or 96) +tenant-vrf: VRF id of the tenant associated with the prefix + +### Show commands + +> show nat64 pool +> show nat64 interfaces +> show nat64 bib tcp|udp|icmp +> show nat64 session table tcp|udp|icmp +> show nat64 tiemouts +> show nat64 prefix + +## Notes + +Multi thread is not supported yet (CLI/API commands are disabled when VPP runs with multiple threads). diff --git a/src/plugins/nat/nat64_in2out.c b/src/plugins/nat/nat64_in2out.c new file mode 100644 index 00000000..f78baff4 --- /dev/null +++ b/src/plugins/nat/nat64_in2out.c @@ -0,0 +1,1118 @@ +/* + * 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. + */ +/** + * @file + * @brief NAT64 IPv6 to IPv4 translation (inside to outside network) + */ + +#include +#include +#include + +typedef struct +{ + u32 sw_if_index; + u32 next_index; + u8 is_slow_path; +} nat64_in2out_trace_t; + +static u8 * +format_nat64_in2out_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 *); + nat64_in2out_trace_t *t = va_arg (*args, nat64_in2out_trace_t *); + char *tag; + + tag = t->is_slow_path ? "NAT64-in2out-slowpath" : "NAT64-in2out"; + + s = + format (s, "%s: sw_if_index %d, next index %d", tag, t->sw_if_index, + t->next_index); + + return s; +} + +vlib_node_registration_t nat64_in2out_node; +vlib_node_registration_t nat64_in2out_slowpath_node; + +#define foreach_nat64_in2out_error \ +_(UNSUPPORTED_PROTOCOL, "unsupported protocol") \ +_(IN2OUT_PACKETS, "good in2out packets processed") \ +_(NO_TRANSLATION, "no translation") \ +_(UNKNOWN, "unknown") + +typedef enum +{ +#define _(sym,str) NAT64_IN2OUT_ERROR_##sym, + foreach_nat64_in2out_error +#undef _ + NAT64_IN2OUT_N_ERROR, +} nat64_in2out_error_t; + +static char *nat64_in2out_error_strings[] = { +#define _(sym,string) string, + foreach_nat64_in2out_error +#undef _ +}; + +typedef enum +{ + NAT64_IN2OUT_NEXT_IP4_LOOKUP, + NAT64_IN2OUT_NEXT_IP6_LOOKUP, + NAT64_IN2OUT_NEXT_DROP, + NAT64_IN2OUT_NEXT_SLOWPATH, + NAT64_IN2OUT_N_NEXT, +} nat64_in2out_next_t; + +typedef struct nat64_in2out_set_ctx_t_ +{ + vlib_buffer_t *b; + vlib_main_t *vm; +} nat64_in2out_set_ctx_t; + +/** + * @brief Check whether is a hairpinning. + * + * If the destination IP address of the packet is an IPv4 address assigned to + * the NAT64 itself, then the packet is a hairpin packet. + * + * param dst_addr Destination address of the packet. + * + * @returns 1 if hairpinning, otherwise 0. + */ +static_always_inline int +is_hairpinning (ip6_address_t * dst_addr) +{ + nat64_main_t *nm = &nat64_main; + int i; + + for (i = 0; i < vec_len (nm->addr_pool); i++) + { + if (nm->addr_pool[i].addr.as_u32 == dst_addr->as_u32[3]) + return 1; + } + + return 0; +} + +static int +nat64_in2out_tcp_udp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, + void *arg) +{ + nat64_main_t *nm = &nat64_main; + nat64_in2out_set_ctx_t *ctx = arg; + nat64_db_bib_entry_t *bibe; + nat64_db_st_entry_t *ste; + ip46_address_t saddr, daddr; + u32 sw_if_index, fib_index; + udp_header_t *udp = ip6_next_header (ip6); + u8 proto = ip6->protocol; + u16 sport = udp->src_port; + u16 dport = udp->dst_port; + + sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX]; + fib_index = + fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index); + + saddr.as_u64[0] = ip6->src_address.as_u64[0]; + saddr.as_u64[1] = ip6->src_address.as_u64[1]; + daddr.as_u64[0] = ip6->dst_address.as_u64[0]; + daddr.as_u64[1] = ip6->dst_address.as_u64[1]; + + ste = + nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto, + fib_index, 1); + + if (ste) + { + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + } + else + { + bibe = + nat64_db_bib_entry_find (&nm->db, &saddr, sport, proto, fib_index, 1); + + if (!bibe) + { + u16 out_port; + ip4_address_t out_addr; + if (nat64_alloc_out_addr_and_port + (fib_index, ip_proto_to_snat_proto (proto), &out_addr, + &out_port)) + return -1; + + bibe = + nat64_db_bib_entry_create (&nm->db, &ip6->src_address, &out_addr, + sport, clib_host_to_net_u16 (out_port), + fib_index, proto, 0); + if (!bibe) + return -1; + } + + nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index); + ste = + nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address, + &daddr.ip4, dport); + if (!ste) + return -1; + } + + nat64_session_reset_timeout (ste, ctx->vm); + + ip4->src_address.as_u32 = bibe->out_addr.as_u32; + udp->src_port = bibe->out_port; + + ip4->dst_address.as_u32 = ste->out_r_addr.as_u32; + + if (proto == IP_PROTOCOL_TCP) + { + u16 *checksum; + ip_csum_t csum; + tcp_header_t *tcp = ip6_next_header (ip6); + + checksum = &tcp->checksum; + csum = ip_csum_sub_even (*checksum, sport); + csum = ip_csum_add_even (csum, udp->src_port); + *checksum = ip_csum_fold (csum); + } + + return 0; +} + +static int +nat64_in2out_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg) +{ + nat64_main_t *nm = &nat64_main; + nat64_in2out_set_ctx_t *ctx = arg; + nat64_db_bib_entry_t *bibe; + nat64_db_st_entry_t *ste; + ip46_address_t saddr, daddr; + u32 sw_if_index, fib_index; + icmp46_header_t *icmp = ip6_next_header (ip6); + + sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX]; + fib_index = + fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index); + + saddr.as_u64[0] = ip6->src_address.as_u64[0]; + saddr.as_u64[1] = ip6->src_address.as_u64[1]; + daddr.as_u64[0] = ip6->dst_address.as_u64[0]; + daddr.as_u64[1] = ip6->dst_address.as_u64[1]; + + if (icmp->type == ICMP4_echo_request || icmp->type == ICMP4_echo_reply) + { + u16 in_id = ((u16 *) (icmp))[2]; + ste = + nat64_db_st_entry_find (&nm->db, &saddr, &daddr, in_id, 0, + IP_PROTOCOL_ICMP, fib_index, 1); + + if (ste) + { + bibe = + nat64_db_bib_entry_by_index (&nm->db, IP_PROTOCOL_ICMP, + ste->bibe_index); + if (!bibe) + return -1; + } + else + { + bibe = + nat64_db_bib_entry_find (&nm->db, &saddr, in_id, + IP_PROTOCOL_ICMP, fib_index, 1); + + if (!bibe) + { + u16 out_id; + ip4_address_t out_addr; + if (nat64_alloc_out_addr_and_port + (fib_index, SNAT_PROTOCOL_ICMP, &out_addr, &out_id)) + return -1; + + bibe = + nat64_db_bib_entry_create (&nm->db, &ip6->src_address, + &out_addr, in_id, + clib_host_to_net_u16 (out_id), + fib_index, IP_PROTOCOL_ICMP, 0); + if (!bibe) + return -1; + } + + nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index); + ste = + nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address, + &daddr.ip4, 0); + if (!ste) + return -1; + } + + nat64_session_reset_timeout (ste, ctx->vm); + + ip4->src_address.as_u32 = bibe->out_addr.as_u32; + ((u16 *) (icmp))[2] = bibe->out_port; + + ip4->dst_address.as_u32 = ste->out_r_addr.as_u32; + } + else + { + if (!vec_len (nm->addr_pool)) + return -1; + + ip4->src_address.as_u32 = nm->addr_pool[0].addr.as_u32; + nat64_extract_ip4 (&ip6->dst_address, &ip4->dst_address, fib_index); + } + + return 0; +} + +static int +nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, + void *arg) +{ + nat64_main_t *nm = &nat64_main; + nat64_in2out_set_ctx_t *ctx = arg; + nat64_db_st_entry_t *ste; + nat64_db_bib_entry_t *bibe; + ip46_address_t saddr, daddr; + u32 sw_if_index, fib_index; + u8 proto = ip6->protocol; + + sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX]; + fib_index = + fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index); + + saddr.as_u64[0] = ip6->src_address.as_u64[0]; + saddr.as_u64[1] = ip6->src_address.as_u64[1]; + daddr.as_u64[0] = ip6->dst_address.as_u64[0]; + daddr.as_u64[1] = ip6->dst_address.as_u64[1]; + + if (proto == IP_PROTOCOL_ICMP6) + { + icmp46_header_t *icmp = ip6_next_header (ip6); + u16 in_id = ((u16 *) (icmp))[2]; + proto = IP_PROTOCOL_ICMP; + + if (! + (icmp->type == ICMP4_echo_request + || icmp->type == ICMP4_echo_reply)) + return -1; + + ste = + nat64_db_st_entry_find (&nm->db, &daddr, &saddr, in_id, 0, proto, + fib_index, 1); + if (!ste) + return -1; + + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + + ip4->dst_address.as_u32 = bibe->out_addr.as_u32; + ((u16 *) (icmp))[2] = bibe->out_port; + ip4->src_address.as_u32 = ste->out_r_addr.as_u32; + } + else + { + udp_header_t *udp = ip6_next_header (ip6); + tcp_header_t *tcp = ip6_next_header (ip6); + u16 *checksum; + ip_csum_t csum; + + u16 sport = udp->src_port; + u16 dport = udp->dst_port; + + ste = + nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto, + fib_index, 1); + if (!ste) + return -1; + + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + + ip4->dst_address.as_u32 = bibe->out_addr.as_u32; + udp->dst_port = bibe->out_port; + ip4->src_address.as_u32 = ste->out_r_addr.as_u32; + + if (proto == IP_PROTOCOL_TCP) + checksum = &tcp->checksum; + else + checksum = &udp->checksum; + csum = ip_csum_sub_even (*checksum, dport); + csum = ip_csum_add_even (csum, udp->dst_port); + *checksum = ip_csum_fold (csum); + } + + return 0; +} + +typedef struct unk_proto_st_walk_ctx_t_ +{ + ip6_address_t src_addr; + ip6_address_t dst_addr; + ip4_address_t out_addr; + u32 fib_index; + u8 proto; +} unk_proto_st_walk_ctx_t; + +static int +unk_proto_st_walk (nat64_db_st_entry_t * ste, void *arg) +{ + nat64_main_t *nm = &nat64_main; + unk_proto_st_walk_ctx_t *ctx = arg; + nat64_db_bib_entry_t *bibe; + ip46_address_t saddr, daddr; + + if (ip46_address_is_equal (&ste->in_r_addr, &ctx->dst_addr)) + { + bibe = + nat64_db_bib_entry_by_index (&nm->db, ste->proto, ste->bibe_index); + if (!bibe) + return -1; + + if (ip46_address_is_equal (&bibe->in_addr, &ctx->src_addr) + && bibe->fib_index == ctx->fib_index) + { + memset (&saddr, 0, sizeof (saddr)); + saddr.ip4.as_u32 = bibe->out_addr.as_u32; + memset (&daddr, 0, sizeof (daddr)); + nat64_extract_ip4 (&ctx->dst_addr, &daddr.ip4, ctx->fib_index); + + if (nat64_db_st_entry_find + (&nm->db, &daddr, &saddr, 0, 0, ctx->proto, ctx->fib_index, 0)) + return -1; + + ctx->out_addr.as_u32 = bibe->out_addr.as_u32; + return 1; + } + } + + return 0; +} + +static int +nat64_in2out_unk_proto_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, + void *arg) +{ + nat64_main_t *nm = &nat64_main; + nat64_in2out_set_ctx_t *ctx = arg; + nat64_db_bib_entry_t *bibe; + nat64_db_st_entry_t *ste; + ip46_address_t saddr, daddr, addr; + u32 sw_if_index, fib_index; + u8 proto = ip6->protocol; + int i; + + sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX]; + fib_index = + fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index); + + saddr.as_u64[0] = ip6->src_address.as_u64[0]; + saddr.as_u64[1] = ip6->src_address.as_u64[1]; + daddr.as_u64[0] = ip6->dst_address.as_u64[0]; + daddr.as_u64[1] = ip6->dst_address.as_u64[1]; + + ste = + nat64_db_st_entry_find (&nm->db, &saddr, &daddr, 0, 0, proto, fib_index, + 1); + + if (ste) + { + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + } + else + { + bibe = + nat64_db_bib_entry_find (&nm->db, &saddr, 0, proto, fib_index, 1); + + if (!bibe) + { + /* Choose same out address as for TCP/UDP session to same dst */ + unk_proto_st_walk_ctx_t ctx = { + .src_addr.as_u64[0] = ip6->src_address.as_u64[0], + .src_addr.as_u64[1] = ip6->src_address.as_u64[1], + .dst_addr.as_u64[0] = ip6->dst_address.as_u64[0], + .dst_addr.as_u64[1] = ip6->dst_address.as_u64[1], + .out_addr.as_u32 = 0, + .fib_index = fib_index, + .proto = proto, + }; + + nat64_db_st_walk (&nm->db, IP_PROTOCOL_TCP, unk_proto_st_walk, + &ctx); + + if (!ctx.out_addr.as_u32) + nat64_db_st_walk (&nm->db, IP_PROTOCOL_UDP, unk_proto_st_walk, + &ctx); + + /* Verify if out address is not already in use for protocol */ + memset (&addr, 0, sizeof (addr)); + addr.ip4.as_u32 = ctx.out_addr.as_u32; + if (nat64_db_bib_entry_find (&nm->db, &addr, 0, proto, 0, 0)) + ctx.out_addr.as_u32 = 0; + + if (!ctx.out_addr.as_u32) + { + for (i = 0; i < vec_len (nm->addr_pool); i++) + { + addr.ip4.as_u32 = nm->addr_pool[i].addr.as_u32; + if (!nat64_db_bib_entry_find + (&nm->db, &addr, 0, proto, 0, 0)) + break; + } + } + + if (!ctx.out_addr.as_u32) + return -1; + + bibe = + nat64_db_bib_entry_create (&nm->db, &ip6->src_address, + &ctx.out_addr, 0, 0, fib_index, proto, + 0); + if (!bibe) + return -1; + } + + nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index); + ste = + nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address, + &daddr.ip4, 0); + if (!ste) + return -1; + } + + nat64_session_reset_timeout (ste, ctx->vm); + + ip4->src_address.as_u32 = bibe->out_addr.as_u32; + ip4->dst_address.as_u32 = ste->out_r_addr.as_u32; + + return 0; +} + + + +static int +nat64_in2out_tcp_udp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b, + ip6_header_t * ip6) +{ + nat64_main_t *nm = &nat64_main; + nat64_db_bib_entry_t *bibe; + nat64_db_st_entry_t *ste; + ip46_address_t saddr, daddr; + u32 sw_if_index, fib_index; + udp_header_t *udp = ip6_next_header (ip6); + tcp_header_t *tcp = ip6_next_header (ip6); + u8 proto = ip6->protocol; + u16 sport = udp->src_port; + u16 dport = udp->dst_port; + u16 *checksum; + ip_csum_t csum; + + sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX]; + fib_index = + fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index); + + saddr.as_u64[0] = ip6->src_address.as_u64[0]; + saddr.as_u64[1] = ip6->src_address.as_u64[1]; + daddr.as_u64[0] = ip6->dst_address.as_u64[0]; + daddr.as_u64[1] = ip6->dst_address.as_u64[1]; + + if (proto == IP_PROTOCOL_UDP) + checksum = &udp->checksum; + else + checksum = &tcp->checksum; + + csum = ip_csum_sub_even (*checksum, ip6->src_address.as_u64[0]); + csum = ip_csum_sub_even (csum, ip6->src_address.as_u64[1]); + csum = ip_csum_sub_even (csum, ip6->dst_address.as_u64[0]); + csum = ip_csum_sub_even (csum, ip6->dst_address.as_u64[1]); + csum = ip_csum_sub_even (csum, sport); + csum = ip_csum_sub_even (csum, dport); + + ste = + nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto, + fib_index, 1); + + if (ste) + { + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + } + else + { + bibe = + nat64_db_bib_entry_find (&nm->db, &saddr, sport, proto, fib_index, 1); + + if (!bibe) + { + u16 out_port; + ip4_address_t out_addr; + if (nat64_alloc_out_addr_and_port + (fib_index, ip_proto_to_snat_proto (proto), &out_addr, + &out_port)) + return -1; + + bibe = + nat64_db_bib_entry_create (&nm->db, &ip6->src_address, &out_addr, + sport, clib_host_to_net_u16 (out_port), + fib_index, proto, 0); + if (!bibe) + return -1; + } + + nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index); + ste = + nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address, + &daddr.ip4, dport); + if (!ste) + return -1; + } + + nat64_session_reset_timeout (ste, vm); + + sport = udp->src_port = bibe->out_port; + nat64_compose_ip6 (&ip6->src_address, &bibe->out_addr, fib_index); + + memset (&saddr, 0, sizeof (saddr)); + memset (&daddr, 0, sizeof (daddr)); + saddr.ip4.as_u32 = bibe->out_addr.as_u32; + daddr.ip4.as_u32 = ste->out_r_addr.as_u32; + + ste = + nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto, 0, + 0); + + if (ste) + { + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + } + else + { + bibe = nat64_db_bib_entry_find (&nm->db, &daddr, dport, proto, 0, 0); + + if (!bibe) + return -1; + + ste = + nat64_db_st_entry_create (&nm->db, bibe, &ip6->src_address, + &saddr.ip4, sport); + } + + ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0]; + ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1]; + udp->dst_port = bibe->in_port; + + csum = ip_csum_add_even (csum, ip6->src_address.as_u64[0]); + csum = ip_csum_add_even (csum, ip6->src_address.as_u64[1]); + csum = ip_csum_add_even (csum, ip6->dst_address.as_u64[0]); + csum = ip_csum_add_even (csum, ip6->dst_address.as_u64[1]); + csum = ip_csum_add_even (csum, udp->src_port); + csum = ip_csum_add_even (csum, udp->dst_port); + *checksum = ip_csum_fold (csum); + + return 0; +} + +static int +nat64_in2out_icmp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b, + ip6_header_t * ip6) +{ + nat64_main_t *nm = &nat64_main; + nat64_db_bib_entry_t *bibe; + nat64_db_st_entry_t *ste; + icmp46_header_t *icmp = ip6_next_header (ip6); + ip6_header_t *inner_ip6; + ip46_address_t saddr, daddr; + u32 sw_if_index, fib_index; + u8 proto; + udp_header_t *udp; + tcp_header_t *tcp; + u16 *checksum, sport, dport; + ip_csum_t csum; + + if (icmp->type == ICMP6_echo_request || icmp->type == ICMP6_echo_reply) + return -1; + + inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8); + + proto = inner_ip6->protocol; + + if (proto == IP_PROTOCOL_ICMP6) + return -1; + + sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX]; + fib_index = + fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index); + + saddr.as_u64[0] = inner_ip6->src_address.as_u64[0]; + saddr.as_u64[1] = inner_ip6->src_address.as_u64[1]; + daddr.as_u64[0] = inner_ip6->dst_address.as_u64[0]; + daddr.as_u64[1] = inner_ip6->dst_address.as_u64[1]; + + udp = ip6_next_header (inner_ip6); + tcp = ip6_next_header (inner_ip6); + + sport = udp->src_port; + dport = udp->dst_port; + + if (proto == IP_PROTOCOL_UDP) + checksum = &udp->checksum; + else + checksum = &tcp->checksum; + + csum = ip_csum_sub_even (*checksum, inner_ip6->src_address.as_u64[0]); + csum = ip_csum_sub_even (csum, inner_ip6->src_address.as_u64[1]); + csum = ip_csum_sub_even (csum, inner_ip6->dst_address.as_u64[0]); + csum = ip_csum_sub_even (csum, inner_ip6->dst_address.as_u64[1]); + csum = ip_csum_sub_even (csum, sport); + csum = ip_csum_sub_even (csum, dport); + + ste = + nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto, + fib_index, 1); + if (!ste) + return -1; + + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + + dport = udp->dst_port = bibe->out_port; + nat64_compose_ip6 (&inner_ip6->dst_address, &bibe->out_addr, fib_index); + + memset (&saddr, 0, sizeof (saddr)); + memset (&daddr, 0, sizeof (daddr)); + saddr.ip4.as_u32 = ste->out_r_addr.as_u32; + daddr.ip4.as_u32 = bibe->out_addr.as_u32; + + ste = + nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto, 0, + 0); + if (!ste) + return -1; + + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + + inner_ip6->src_address.as_u64[0] = bibe->in_addr.as_u64[0]; + inner_ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1]; + udp->src_port = bibe->in_port; + + csum = ip_csum_add_even (csum, inner_ip6->src_address.as_u64[0]); + csum = ip_csum_add_even (csum, inner_ip6->src_address.as_u64[1]); + csum = ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[0]); + csum = ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[1]); + csum = ip_csum_add_even (csum, udp->src_port); + csum = ip_csum_add_even (csum, udp->dst_port); + *checksum = ip_csum_fold (csum); + + if (!vec_len (nm->addr_pool)) + return -1; + + nat64_compose_ip6 (&ip6->src_address, &nm->addr_pool[0].addr, fib_index); + ip6->dst_address.as_u64[0] = inner_ip6->src_address.as_u64[0]; + ip6->dst_address.as_u64[1] = inner_ip6->src_address.as_u64[1]; + + icmp->checksum = 0; + csum = ip_csum_with_carry (0, ip6->payload_length); + csum = ip_csum_with_carry (csum, clib_host_to_net_u16 (ip6->protocol)); + csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[0]); + csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[1]); + csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[0]); + csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[1]); + csum = + ip_incremental_checksum (csum, icmp, + clib_net_to_host_u16 (ip6->payload_length)); + icmp->checksum = ~ip_csum_fold (csum); + + return 0; +} + +static int +nat64_in2out_unk_proto_hairpinning (vlib_main_t * vm, vlib_buffer_t * b, + ip6_header_t * ip6) +{ + nat64_main_t *nm = &nat64_main; + nat64_db_bib_entry_t *bibe; + nat64_db_st_entry_t *ste; + ip46_address_t saddr, daddr, addr; + u32 sw_if_index, fib_index; + u8 proto = ip6->protocol; + int i; + + sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX]; + fib_index = + fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index); + + saddr.as_u64[0] = ip6->src_address.as_u64[0]; + saddr.as_u64[1] = ip6->src_address.as_u64[1]; + daddr.as_u64[0] = ip6->dst_address.as_u64[0]; + daddr.as_u64[1] = ip6->dst_address.as_u64[1]; + + ste = + nat64_db_st_entry_find (&nm->db, &saddr, &daddr, 0, 0, proto, fib_index, + 1); + + if (ste) + { + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + } + else + { + bibe = + nat64_db_bib_entry_find (&nm->db, &saddr, 0, proto, fib_index, 1); + + if (!bibe) + { + /* Choose same out address as for TCP/UDP session to same dst */ + unk_proto_st_walk_ctx_t ctx = { + .src_addr.as_u64[0] = ip6->src_address.as_u64[0], + .src_addr.as_u64[1] = ip6->src_address.as_u64[1], + .dst_addr.as_u64[0] = ip6->dst_address.as_u64[0], + .dst_addr.as_u64[1] = ip6->dst_address.as_u64[1], + .out_addr.as_u32 = 0, + .fib_index = fib_index, + .proto = proto, + }; + + nat64_db_st_walk (&nm->db, IP_PROTOCOL_TCP, unk_proto_st_walk, + &ctx); + + if (!ctx.out_addr.as_u32) + nat64_db_st_walk (&nm->db, IP_PROTOCOL_UDP, unk_proto_st_walk, + &ctx); + + /* Verify if out address is not already in use for protocol */ + memset (&addr, 0, sizeof (addr)); + addr.ip4.as_u32 = ctx.out_addr.as_u32; + if (nat64_db_bib_entry_find (&nm->db, &addr, 0, proto, 0, 0)) + ctx.out_addr.as_u32 = 0; + + if (!ctx.out_addr.as_u32) + { + for (i = 0; i < vec_len (nm->addr_pool); i++) + { + addr.ip4.as_u32 = nm->addr_pool[i].addr.as_u32; + if (!nat64_db_bib_entry_find + (&nm->db, &addr, 0, proto, 0, 0)) + break; + } + } + + if (!ctx.out_addr.as_u32) + return -1; + + bibe = + nat64_db_bib_entry_create (&nm->db, &ip6->src_address, + &ctx.out_addr, 0, 0, fib_index, proto, + 0); + if (!bibe) + return -1; + } + + nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index); + ste = + nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address, + &daddr.ip4, 0); + if (!ste) + return -1; + } + + nat64_session_reset_timeout (ste, vm); + + nat64_compose_ip6 (&ip6->src_address, &bibe->out_addr, fib_index); + + memset (&saddr, 0, sizeof (saddr)); + memset (&daddr, 0, sizeof (daddr)); + saddr.ip4.as_u32 = bibe->out_addr.as_u32; + daddr.ip4.as_u32 = ste->out_r_addr.as_u32; + + ste = nat64_db_st_entry_find (&nm->db, &daddr, &saddr, 0, 0, proto, 0, 0); + + if (ste) + { + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + } + else + { + bibe = nat64_db_bib_entry_find (&nm->db, &daddr, 0, proto, 0, 0); + + if (!bibe) + return -1; + + ste = + nat64_db_st_entry_create (&nm->db, bibe, &ip6->src_address, + &saddr.ip4, 0); + } + + ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0]; + ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1]; + + return 0; +} + +static inline uword +nat64_in2out_node_fn_inline (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame, u8 is_slow_path) +{ + u32 n_left_from, *from, *to_next; + nat64_in2out_next_t next_index; + u32 pkts_processed = 0; + u32 stats_node_index; + + stats_node_index = + is_slow_path ? nat64_in2out_slowpath_node.index : nat64_in2out_node.index; + + 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; + u32 next0; + ip6_header_t *ip60; + u16 l4_offset0, frag_offset0; + u8 l4_protocol0; + u32 proto0; + nat64_in2out_set_ctx_t ctx0; + + /* speculatively enqueue b0 to the current next frame */ + 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); + ip60 = vlib_buffer_get_current (b0); + + ctx0.b = b0; + ctx0.vm = vm; + + next0 = NAT64_IN2OUT_NEXT_IP4_LOOKUP; + + if (PREDICT_FALSE + (ip6_parse + (ip60, b0->current_length, &l4_protocol0, &l4_offset0, + &frag_offset0))) + { + next0 = NAT64_IN2OUT_NEXT_DROP; + b0->error = node->errors[NAT64_IN2OUT_ERROR_UNKNOWN]; + goto trace0; + } + + proto0 = ip_proto_to_snat_proto (l4_protocol0); + if (frag_offset0 != 0) + { + next0 = NAT64_IN2OUT_NEXT_DROP; + b0->error = + node->errors[NAT64_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL]; + goto trace0; + } + + if (is_slow_path) + { + if (PREDICT_TRUE (proto0 == ~0)) + { + if (is_hairpinning (&ip60->dst_address)) + { + next0 = NAT64_IN2OUT_NEXT_IP6_LOOKUP; + if (nat64_in2out_unk_proto_hairpinning (vm, b0, ip60)) + { + next0 = NAT64_IN2OUT_NEXT_DROP; + b0->error = + node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION]; + } + goto trace0; + } + + if (ip6_to_ip4 (b0, nat64_in2out_unk_proto_set_cb, &ctx0)) + { + next0 = NAT64_IN2OUT_NEXT_DROP; + b0->error = + node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION]; + goto trace0; + } + } + goto trace0; + } + else + { + if (PREDICT_FALSE (proto0 == ~0)) + { + next0 = NAT64_IN2OUT_NEXT_SLOWPATH; + goto trace0; + } + } + + if (proto0 == SNAT_PROTOCOL_ICMP) + { + if (is_hairpinning (&ip60->dst_address)) + { + next0 = NAT64_IN2OUT_NEXT_IP6_LOOKUP; + if (nat64_in2out_icmp_hairpinning (vm, b0, ip60)) + { + next0 = NAT64_IN2OUT_NEXT_DROP; + b0->error = + node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION]; + } + goto trace0; + } + + if (icmp6_to_icmp + (b0, nat64_in2out_icmp_set_cb, &ctx0, + nat64_in2out_inner_icmp_set_cb, &ctx0)) + { + next0 = NAT64_IN2OUT_NEXT_DROP; + b0->error = node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION]; + goto trace0; + } + } + else if (proto0 == SNAT_PROTOCOL_TCP || proto0 == SNAT_PROTOCOL_UDP) + { + if (is_hairpinning (&ip60->dst_address)) + { + next0 = NAT64_IN2OUT_NEXT_IP6_LOOKUP; + if (nat64_in2out_tcp_udp_hairpinning (vm, b0, ip60)) + { + next0 = NAT64_IN2OUT_NEXT_DROP; + b0->error = + node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION]; + } + goto trace0; + } + + if (ip6_to_ip4_tcp_udp + (b0, nat64_in2out_tcp_udp_set_cb, &ctx0, 0)) + { + next0 = NAT64_IN2OUT_NEXT_DROP; + b0->error = node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION]; + goto trace0; + } + } + + trace0: + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + nat64_in2out_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + t->next_index = next0; + t->is_slow_path = is_slow_path; + } + + pkts_processed += next0 != NAT64_IN2OUT_NEXT_DROP; + + /* verify speculative enqueue, maybe switch current next frame */ + 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, stats_node_index, + NAT64_IN2OUT_ERROR_IN2OUT_PACKETS, + pkts_processed); + return frame->n_vectors; +} + +static uword +nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return nat64_in2out_node_fn_inline (vm, node, frame, 0); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (nat64_in2out_node) = { + .function = nat64_in2out_node_fn, + .name = "nat64-in2out", + .vector_size = sizeof (u32), + .format_trace = format_nat64_in2out_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (nat64_in2out_error_strings), + .error_strings = nat64_in2out_error_strings, + .n_next_nodes = NAT64_IN2OUT_N_NEXT, + /* edit / add dispositions here */ + .next_nodes = { + [NAT64_IN2OUT_NEXT_DROP] = "error-drop", + [NAT64_IN2OUT_NEXT_IP4_LOOKUP] = "ip4-lookup", + [NAT64_IN2OUT_NEXT_IP6_LOOKUP] = "ip6-lookup", + [NAT64_IN2OUT_NEXT_SLOWPATH] = "nat64-in2out-slowpath", + }, +}; +/* *INDENT-ON* */ + +VLIB_NODE_FUNCTION_MULTIARCH (nat64_in2out_node, nat64_in2out_node_fn); + +static uword +nat64_in2out_slowpath_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return nat64_in2out_node_fn_inline (vm, node, frame, 1); +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (nat64_in2out_slowpath_node) = { + .function = nat64_in2out_slowpath_node_fn, + .name = "nat64-in2out-slowpath", + .vector_size = sizeof (u32), + .format_trace = format_nat64_in2out_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (nat64_in2out_error_strings), + .error_strings = nat64_in2out_error_strings, + .n_next_nodes = NAT64_IN2OUT_N_NEXT, + /* edit / add dispositions here */ + .next_nodes = { + [NAT64_IN2OUT_NEXT_DROP] = "error-drop", + [NAT64_IN2OUT_NEXT_IP4_LOOKUP] = "ip4-lookup", + [NAT64_IN2OUT_NEXT_IP6_LOOKUP] = "ip6-lookup", + [NAT64_IN2OUT_NEXT_SLOWPATH] = "nat64-in2out-slowpath", + }, +}; +/* *INDENT-ON* */ + +VLIB_NODE_FUNCTION_MULTIARCH (nat64_in2out_slowpath_node, + nat64_in2out_slowpath_node_fn); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat64_out2in.c b/src/plugins/nat/nat64_out2in.c new file mode 100644 index 00000000..61e88a7f --- /dev/null +++ b/src/plugins/nat/nat64_out2in.c @@ -0,0 +1,494 @@ +/* + * 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. + */ +/** + * @file + * @brief NAT64 IPv4 to IPv6 translation (otside to inside network) + */ + +#include +#include +#include + +typedef struct +{ + u32 sw_if_index; + u32 next_index; +} nat64_out2in_trace_t; + +static u8 * +format_nat64_out2in_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 *); + nat64_out2in_trace_t *t = va_arg (*args, nat64_out2in_trace_t *); + + s = + format (s, "NAT64-out2in: sw_if_index %d, next index %d", t->sw_if_index, + t->next_index); + + return s; +} + +vlib_node_registration_t nat64_out2in_node; + +#define foreach_nat64_out2in_error \ +_(UNSUPPORTED_PROTOCOL, "Unsupported protocol") \ +_(OUT2IN_PACKETS, "Good out2in packets processed") \ +_(NO_TRANSLATION, "No translation") \ +_(UNKNOWN, "unknown") + +typedef enum +{ +#define _(sym,str) NAT64_OUT2IN_ERROR_##sym, + foreach_nat64_out2in_error +#undef _ + NAT64_OUT2IN_N_ERROR, +} nat64_out2in_error_t; + +static char *nat64_out2in_error_strings[] = { +#define _(sym,string) string, + foreach_nat64_out2in_error +#undef _ +}; + +typedef enum +{ + NAT64_OUT2IN_NEXT_LOOKUP, + NAT64_OUT2IN_NEXT_DROP, + NAT64_OUT2IN_N_NEXT, +} nat64_out2in_next_t; + +typedef struct nat64_out2in_set_ctx_t_ +{ + vlib_buffer_t *b; + vlib_main_t *vm; +} nat64_out2in_set_ctx_t; + +static int +nat64_out2in_tcp_udp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, + void *arg) +{ + nat64_main_t *nm = &nat64_main; + nat64_out2in_set_ctx_t *ctx = arg; + nat64_db_bib_entry_t *bibe; + nat64_db_st_entry_t *ste; + ip46_address_t saddr, daddr; + ip6_address_t ip6_saddr; + udp_header_t *udp = ip4_next_header (ip4); + tcp_header_t *tcp = ip4_next_header (ip4); + u8 proto = ip4->protocol; + u16 dport = udp->dst_port; + u16 sport = udp->src_port; + u32 sw_if_index, fib_index; + u16 *checksum; + ip_csum_t csum; + + sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX]; + fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index); + + memset (&saddr, 0, sizeof (saddr)); + saddr.ip4.as_u32 = ip4->src_address.as_u32; + memset (&daddr, 0, sizeof (daddr)); + daddr.ip4.as_u32 = ip4->dst_address.as_u32; + + ste = + nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto, + fib_index, 0); + if (ste) + { + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + } + else + { + bibe = + nat64_db_bib_entry_find (&nm->db, &daddr, dport, proto, fib_index, 0); + + if (!bibe) + return -1; + + nat64_compose_ip6 (&ip6_saddr, &ip4->src_address, bibe->fib_index); + ste = + nat64_db_st_entry_create (&nm->db, bibe, &ip6_saddr, &saddr.ip4, + sport); + } + + nat64_session_reset_timeout (ste, ctx->vm); + + ip6->src_address.as_u64[0] = ste->in_r_addr.as_u64[0]; + ip6->src_address.as_u64[1] = ste->in_r_addr.as_u64[1]; + + ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0]; + ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1]; + udp->dst_port = bibe->in_port; + + if (proto == IP_PROTOCOL_UDP) + checksum = &udp->checksum; + else + checksum = &tcp->checksum; + csum = ip_csum_sub_even (*checksum, dport); + csum = ip_csum_add_even (csum, udp->dst_port); + *checksum = ip_csum_fold (csum); + + vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index; + + return 0; +} + +static int +nat64_out2in_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *arg) +{ + nat64_main_t *nm = &nat64_main; + nat64_out2in_set_ctx_t *ctx = arg; + nat64_db_bib_entry_t *bibe; + nat64_db_st_entry_t *ste; + ip46_address_t saddr, daddr; + ip6_address_t ip6_saddr; + u32 sw_if_index, fib_index; + icmp46_header_t *icmp = ip4_next_header (ip4); + + sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX]; + fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index); + + memset (&saddr, 0, sizeof (saddr)); + saddr.ip4.as_u32 = ip4->src_address.as_u32; + memset (&daddr, 0, sizeof (daddr)); + daddr.ip4.as_u32 = ip4->dst_address.as_u32; + + if (icmp->type == ICMP6_echo_request || icmp->type == ICMP6_echo_reply) + { + u16 out_id = ((u16 *) (icmp))[2]; + ste = + nat64_db_st_entry_find (&nm->db, &daddr, &saddr, out_id, 0, + IP_PROTOCOL_ICMP, fib_index, 0); + + if (ste) + { + bibe = + nat64_db_bib_entry_by_index (&nm->db, IP_PROTOCOL_ICMP, + ste->bibe_index); + if (!bibe) + return -1; + } + else + { + bibe = + nat64_db_bib_entry_find (&nm->db, &daddr, out_id, + IP_PROTOCOL_ICMP, fib_index, 0); + if (!bibe) + return -1; + + nat64_compose_ip6 (&ip6_saddr, &ip4->src_address, bibe->fib_index); + ste = + nat64_db_st_entry_create (&nm->db, bibe, &ip6_saddr, &saddr.ip4, + 0); + } + + nat64_session_reset_timeout (ste, ctx->vm); + + ip6->src_address.as_u64[0] = ste->in_r_addr.as_u64[0]; + ip6->src_address.as_u64[1] = ste->in_r_addr.as_u64[1]; + + ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0]; + ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1]; + ((u16 *) (icmp))[2] = bibe->in_port; + + vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index; + } + else + { + ip6_header_t *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8); + + nat64_compose_ip6 (&ip6->src_address, &ip4->src_address, + vnet_buffer (ctx->b)->sw_if_index[VLIB_TX]); + ip6->dst_address.as_u64[0] = inner_ip6->src_address.as_u64[0]; + ip6->dst_address.as_u64[1] = inner_ip6->src_address.as_u64[1]; + } + + return 0; +} + +static int +nat64_out2in_inner_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, + void *arg) +{ + nat64_main_t *nm = &nat64_main; + nat64_out2in_set_ctx_t *ctx = arg; + nat64_db_bib_entry_t *bibe; + nat64_db_st_entry_t *ste; + ip46_address_t saddr, daddr; + u32 sw_if_index, fib_index; + u8 proto = ip4->protocol; + + sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX]; + fib_index = + fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index); + + memset (&saddr, 0, sizeof (saddr)); + saddr.ip4.as_u32 = ip4->src_address.as_u32; + memset (&daddr, 0, sizeof (daddr)); + daddr.ip4.as_u32 = ip4->dst_address.as_u32; + + if (proto == IP_PROTOCOL_ICMP6) + { + icmp46_header_t *icmp = ip4_next_header (ip4); + u16 out_id = ((u16 *) (icmp))[2]; + proto = IP_PROTOCOL_ICMP; + + if (! + (icmp->type == ICMP6_echo_request + || icmp->type == ICMP6_echo_reply)) + return -1; + + ste = + nat64_db_st_entry_find (&nm->db, &saddr, &daddr, out_id, 0, proto, + fib_index, 0); + if (!ste) + return -1; + + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + + ip6->dst_address.as_u64[0] = ste->in_r_addr.as_u64[0]; + ip6->dst_address.as_u64[1] = ste->in_r_addr.as_u64[1]; + ip6->src_address.as_u64[0] = bibe->in_addr.as_u64[0]; + ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1]; + ((u16 *) (icmp))[2] = bibe->in_port; + + vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index; + } + else + { + udp_header_t *udp = ip4_next_header (ip4); + tcp_header_t *tcp = ip4_next_header (ip4); + u16 dport = udp->dst_port; + u16 sport = udp->src_port; + u16 *checksum; + ip_csum_t csum; + + ste = + nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto, + fib_index, 0); + if (!ste) + return -1; + + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + + nat64_compose_ip6 (&ip6->dst_address, &daddr.ip4, bibe->fib_index); + ip6->src_address.as_u64[0] = bibe->in_addr.as_u64[0]; + ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1]; + udp->src_port = bibe->in_port; + + if (proto == IP_PROTOCOL_UDP) + checksum = &udp->checksum; + else + checksum = &tcp->checksum; + if (*checksum) + { + csum = ip_csum_sub_even (*checksum, sport); + csum = ip_csum_add_even (csum, udp->src_port); + *checksum = ip_csum_fold (csum); + } + + vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index; + } + + return 0; +} + +static int +nat64_out2in_unk_proto_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, + void *arg) +{ + nat64_main_t *nm = &nat64_main; + nat64_out2in_set_ctx_t *ctx = arg; + nat64_db_bib_entry_t *bibe; + nat64_db_st_entry_t *ste; + ip46_address_t saddr, daddr; + ip6_address_t ip6_saddr; + u32 sw_if_index, fib_index; + u8 proto = ip4->protocol; + + sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX]; + fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index); + + memset (&saddr, 0, sizeof (saddr)); + saddr.ip4.as_u32 = ip4->src_address.as_u32; + memset (&daddr, 0, sizeof (daddr)); + daddr.ip4.as_u32 = ip4->dst_address.as_u32; + + ste = + nat64_db_st_entry_find (&nm->db, &daddr, &saddr, 0, 0, proto, fib_index, + 0); + if (ste) + { + bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); + if (!bibe) + return -1; + } + else + { + bibe = + nat64_db_bib_entry_find (&nm->db, &daddr, 0, proto, fib_index, 0); + + if (!bibe) + return -1; + + nat64_compose_ip6 (&ip6_saddr, &ip4->src_address, bibe->fib_index); + ste = + nat64_db_st_entry_create (&nm->db, bibe, &ip6_saddr, &saddr.ip4, 0); + } + + nat64_session_reset_timeout (ste, ctx->vm); + + ip6->src_address.as_u64[0] = ste->in_r_addr.as_u64[0]; + ip6->src_address.as_u64[1] = ste->in_r_addr.as_u64[1]; + + ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0]; + ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1]; + + vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index; + + return 0; +} + +static uword +nat64_out2in_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, *from, *to_next; + nat64_out2in_next_t next_index; + u32 pkts_processed = 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; + u32 next0; + ip4_header_t *ip40; + u32 proto0; + nat64_out2in_set_ctx_t ctx0; + + /* speculatively enqueue b0 to the current next frame */ + 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); + ip40 = vlib_buffer_get_current (b0); + + ctx0.b = b0; + ctx0.vm = vm; + + next0 = NAT64_OUT2IN_NEXT_LOOKUP; + + proto0 = ip_proto_to_snat_proto (ip40->protocol); + + if (proto0 == SNAT_PROTOCOL_ICMP) + { + if (icmp_to_icmp6 + (b0, nat64_out2in_icmp_set_cb, &ctx0, + nat64_out2in_inner_icmp_set_cb, &ctx0)) + { + next0 = NAT64_OUT2IN_NEXT_DROP; + b0->error = node->errors[NAT64_OUT2IN_ERROR_NO_TRANSLATION]; + goto trace0; + } + } + else if (proto0 == SNAT_PROTOCOL_TCP || proto0 == SNAT_PROTOCOL_UDP) + { + if (ip4_to_ip6_tcp_udp (b0, nat64_out2in_tcp_udp_set_cb, &ctx0)) + { + next0 = NAT64_OUT2IN_NEXT_DROP; + b0->error = node->errors[NAT64_OUT2IN_ERROR_NO_TRANSLATION]; + goto trace0; + } + } + else + { + if (ip4_to_ip6 (b0, nat64_out2in_unk_proto_set_cb, &ctx0)) + { + next0 = NAT64_OUT2IN_NEXT_DROP; + b0->error = node->errors[NAT64_OUT2IN_ERROR_NO_TRANSLATION]; + goto trace0; + } + } + + trace0: + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + nat64_out2in_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + t->next_index = next0; + } + + pkts_processed += next0 != NAT64_OUT2IN_NEXT_DROP; + + /* verify speculative enqueue, maybe switch current next frame */ + 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, nat64_out2in_node.index, + NAT64_OUT2IN_ERROR_OUT2IN_PACKETS, + pkts_processed); + return frame->n_vectors; +} + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (nat64_out2in_node) = { + .function = nat64_out2in_node_fn, + .name = "nat64-out2in", + .vector_size = sizeof (u32), + .format_trace = format_nat64_out2in_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (nat64_out2in_error_strings), + .error_strings = nat64_out2in_error_strings,.n_next_nodes = 2, + /* edit / add dispositions here */ + .next_nodes = { + [NAT64_OUT2IN_NEXT_DROP] = "error-drop", + [NAT64_OUT2IN_NEXT_LOOKUP] = "ip6-lookup", + }, +}; +/* *INDENT-ON* */ + +VLIB_NODE_FUNCTION_MULTIARCH (nat64_out2in_node, nat64_out2in_node_fn); + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat_all_api_h.h b/src/plugins/nat/nat_all_api_h.h new file mode 100644 index 00000000..acd9ba1c --- /dev/null +++ b/src/plugins/nat/nat_all_api_h.h @@ -0,0 +1,19 @@ + +/* + * nat_all_api_h.h - skeleton vpp engine plug-in api #include file + * + * Copyright (c) + * 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 diff --git a/src/plugins/nat/nat_api.c b/src/plugins/nat/nat_api.c new file mode 100644 index 00000000..0a2141f2 --- /dev/null +++ b/src/plugins/nat/nat_api.c @@ -0,0 +1,3255 @@ +/* + * 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. + */ +/** + * @file + * @brief NAT plugin API implementation + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* define message structures */ +#define vl_typedefs +#include +#undef vl_typedefs + +/* define generated endian-swappers */ +#define vl_endianfun +#include +#undef vl_endianfun + +#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) + +#define REPLY_MSG_ID_BASE sm->msg_id_base +#include + +/* Get the API version number */ +#define vl_api_version(n,v) static u32 api_version=(v); +#include +#undef vl_api_version + +/* Macro to finish up custom dump fns */ +#define FINISH \ + vec_add1 (s, 0); \ + vl_print (handle, (char *)s); \ + vec_free (s); \ + return handle; + +static void + vl_api_snat_add_address_range_t_handler + (vl_api_snat_add_address_range_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_snat_add_address_range_reply_t *rmp; + ip4_address_t this_addr; + u32 start_host_order, end_host_order; + u32 vrf_id; + int i, count; + int rv = 0; + u32 *tmp; + + if (mp->is_ip4 != 1) + { + rv = VNET_API_ERROR_UNIMPLEMENTED; + goto send_reply; + } + + if (sm->static_mapping_only) + { + rv = VNET_API_ERROR_FEATURE_DISABLED; + goto send_reply; + } + + tmp = (u32 *) mp->first_ip_address; + start_host_order = clib_host_to_net_u32 (tmp[0]); + tmp = (u32 *) mp->last_ip_address; + end_host_order = clib_host_to_net_u32 (tmp[0]); + + count = (end_host_order - start_host_order) + 1; + + vrf_id = clib_host_to_net_u32 (mp->vrf_id); + + if (count > 1024) + clib_warning ("%U - %U, %d addresses...", + format_ip4_address, mp->first_ip_address, + format_ip4_address, mp->last_ip_address, count); + + memcpy (&this_addr.as_u8, mp->first_ip_address, 4); + + for (i = 0; i < count; i++) + { + if (mp->is_add) + snat_add_address (sm, &this_addr, vrf_id); + else + rv = snat_del_address (sm, this_addr, 0); + + if (rv) + goto send_reply; + + increment_v4_address (&this_addr); + } + +send_reply: + REPLY_MACRO (VL_API_SNAT_ADD_ADDRESS_RANGE_REPLY); +} + +static void *vl_api_snat_add_address_range_t_print + (vl_api_snat_add_address_range_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_add_address_range "); + s = format (s, "%U ", format_ip4_address, mp->first_ip_address); + if (memcmp (mp->first_ip_address, mp->last_ip_address, 4)) + { + s = format (s, " - %U ", format_ip4_address, mp->last_ip_address); + } + FINISH; +} + +static void + send_snat_address_details + (snat_address_t * a, unix_shared_memory_queue_t * q, u32 context) +{ + vl_api_snat_address_details_t *rmp; + snat_main_t *sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_SNAT_ADDRESS_DETAILS + sm->msg_id_base); + rmp->is_ip4 = 1; + clib_memcpy (rmp->ip_address, &(a->addr), 4); + if (a->fib_index != ~0) + { + fib_table_t *fib = fib_table_get (a->fib_index, FIB_PROTOCOL_IP4); + rmp->vrf_id = ntohl (fib->ft_table_id); + } + else + rmp->vrf_id = ~0; + rmp->context = context; + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void +vl_api_snat_address_dump_t_handler (vl_api_snat_address_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + snat_main_t *sm = &snat_main; + snat_address_t *a; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + /* *INDENT-OFF* */ + vec_foreach (a, sm->addresses) + send_snat_address_details (a, q, mp->context); + /* *INDENT-ON* */ +} + +static void *vl_api_snat_address_dump_t_print + (vl_api_snat_address_dump_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_address_dump "); + + FINISH; +} + +static void + vl_api_snat_interface_add_del_feature_t_handler + (vl_api_snat_interface_add_del_feature_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_snat_interface_add_del_feature_reply_t *rmp; + u8 is_del = mp->is_add == 0; + u32 sw_if_index = ntohl (mp->sw_if_index); + int rv = 0; + + VALIDATE_SW_IF_INDEX (mp); + + rv = snat_interface_add_del (sw_if_index, mp->is_inside, is_del); + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_SNAT_INTERFACE_ADD_DEL_FEATURE_REPLY); +} + +static void *vl_api_snat_interface_add_del_feature_t_print + (vl_api_snat_interface_add_del_feature_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_interface_add_del_feature "); + s = format (s, "sw_if_index %d %s %s", + clib_host_to_net_u32 (mp->sw_if_index), + mp->is_inside ? "in" : "out", mp->is_add ? "" : "del"); + + FINISH; +} + +static void + send_snat_interface_details + (snat_interface_t * i, unix_shared_memory_queue_t * q, u32 context) +{ + vl_api_snat_interface_details_t *rmp; + snat_main_t *sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_SNAT_INTERFACE_DETAILS + sm->msg_id_base); + rmp->sw_if_index = ntohl (i->sw_if_index); + rmp->is_inside = i->is_inside; + rmp->context = context; + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void +vl_api_snat_interface_dump_t_handler (vl_api_snat_interface_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + snat_main_t *sm = &snat_main; + snat_interface_t *i; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + /* *INDENT-OFF* */ + pool_foreach (i, sm->interfaces, + ({ + send_snat_interface_details(i, q, mp->context); + })); + /* *INDENT-ON* */ +} + +static void *vl_api_snat_interface_dump_t_print + (vl_api_snat_interface_dump_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_interface_dump "); + + FINISH; +} + +static void + vl_api_snat_interface_add_del_output_feature_t_handler + (vl_api_snat_interface_add_del_output_feature_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_snat_interface_add_del_output_feature_reply_t *rmp; + u8 is_del = mp->is_add == 0; + u32 sw_if_index = ntohl (mp->sw_if_index); + int rv = 0; + + VALIDATE_SW_IF_INDEX (mp); + + rv = snat_interface_add_del_output_feature (sw_if_index, mp->is_inside, + is_del); + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_SNAT_INTERFACE_ADD_DEL_OUTPUT_FEATURE_REPLY); +} + +static void *vl_api_snat_interface_add_del_output_feature_t_print + (vl_api_snat_interface_add_del_output_feature_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_interface_add_del_output_feature "); + s = format (s, "sw_if_index %d %s %s", + clib_host_to_net_u32 (mp->sw_if_index), + mp->is_inside ? "in" : "out", mp->is_add ? "" : "del"); + + FINISH; +} + +static void +send_snat_interface_output_feature_details (snat_interface_t * i, + unix_shared_memory_queue_t * q, + u32 context) +{ + vl_api_snat_interface_output_feature_details_t *rmp; + snat_main_t *sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_SNAT_INTERFACE_OUTPUT_FEATURE_DETAILS + sm->msg_id_base); + rmp->sw_if_index = ntohl (i->sw_if_index); + rmp->context = context; + rmp->is_inside = i->is_inside; + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void + vl_api_snat_interface_output_feature_dump_t_handler + (vl_api_snat_interface_output_feature_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + snat_main_t *sm = &snat_main; + snat_interface_t *i; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + /* *INDENT-OFF* */ + pool_foreach (i, sm->output_feature_interfaces, + ({ + send_snat_interface_output_feature_details(i, q, mp->context); + })); + /* *INDENT-ON* */ +} + +static void *vl_api_snat_interface_output_feature_dump_t_print + (vl_api_snat_interface_output_feature_dump_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_interface_output_feature_dump "); + + FINISH; +} + +static void + vl_api_snat_add_static_mapping_t_handler + (vl_api_snat_add_static_mapping_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_snat_add_static_mapping_reply_t *rmp; + ip4_address_t local_addr, external_addr; + u16 local_port = 0, external_port = 0; + u32 vrf_id, external_sw_if_index; + int rv = 0; + snat_protocol_t proto; + + if (mp->is_ip4 != 1) + { + rv = VNET_API_ERROR_UNIMPLEMENTED; + goto send_reply; + } + + memcpy (&local_addr.as_u8, mp->local_ip_address, 4); + memcpy (&external_addr.as_u8, mp->external_ip_address, 4); + if (mp->addr_only == 0) + { + local_port = clib_net_to_host_u16 (mp->local_port); + external_port = clib_net_to_host_u16 (mp->external_port); + } + vrf_id = clib_net_to_host_u32 (mp->vrf_id); + external_sw_if_index = clib_net_to_host_u32 (mp->external_sw_if_index); + proto = ip_proto_to_snat_proto (mp->protocol); + + rv = snat_add_static_mapping (local_addr, external_addr, local_port, + external_port, vrf_id, mp->addr_only, + external_sw_if_index, proto, mp->is_add); + +send_reply: + REPLY_MACRO (VL_API_SNAT_ADD_ADDRESS_RANGE_REPLY); +} + +static void *vl_api_snat_add_static_mapping_t_print + (vl_api_snat_add_static_mapping_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_add_static_mapping "); + s = format (s, "protocol %d local_addr %U external_addr %U ", + mp->protocol, + format_ip4_address, mp->local_ip_address, + format_ip4_address, mp->external_ip_address); + + if (mp->addr_only == 0) + s = format (s, "local_port %d external_port %d ", + clib_net_to_host_u16 (mp->local_port), + clib_net_to_host_u16 (mp->external_port)); + + if (mp->vrf_id != ~0) + s = format (s, "vrf %d", clib_net_to_host_u32 (mp->vrf_id)); + + if (mp->external_sw_if_index != ~0) + s = format (s, "external_sw_if_index %d", + clib_net_to_host_u32 (mp->external_sw_if_index)); + FINISH; +} + +static void + send_snat_static_mapping_details + (snat_static_mapping_t * m, unix_shared_memory_queue_t * q, u32 context) +{ + vl_api_snat_static_mapping_details_t *rmp; + snat_main_t *sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_SNAT_STATIC_MAPPING_DETAILS + sm->msg_id_base); + rmp->is_ip4 = 1; + rmp->addr_only = m->addr_only; + clib_memcpy (rmp->local_ip_address, &(m->local_addr), 4); + clib_memcpy (rmp->external_ip_address, &(m->external_addr), 4); + rmp->local_port = htons (m->local_port); + rmp->external_port = htons (m->external_port); + rmp->external_sw_if_index = ~0; + rmp->vrf_id = htonl (m->vrf_id); + rmp->protocol = snat_proto_to_ip_proto (m->proto); + rmp->context = context; + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void + send_snat_static_map_resolve_details + (snat_static_map_resolve_t * m, unix_shared_memory_queue_t * q, u32 context) +{ + vl_api_snat_static_mapping_details_t *rmp; + snat_main_t *sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_SNAT_STATIC_MAPPING_DETAILS + sm->msg_id_base); + rmp->is_ip4 = 1; + rmp->addr_only = m->addr_only; + clib_memcpy (rmp->local_ip_address, &(m->l_addr), 4); + rmp->local_port = htons (m->l_port); + rmp->external_port = htons (m->e_port); + rmp->external_sw_if_index = htonl (m->sw_if_index); + rmp->vrf_id = htonl (m->vrf_id); + rmp->protocol = snat_proto_to_ip_proto (m->proto); + rmp->context = context; + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void + vl_api_snat_static_mapping_dump_t_handler + (vl_api_snat_static_mapping_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + snat_main_t *sm = &snat_main; + snat_static_mapping_t *m; + snat_static_map_resolve_t *rp; + int j; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + /* *INDENT-OFF* */ + pool_foreach (m, sm->static_mappings, + ({ + send_snat_static_mapping_details (m, q, mp->context); + })); + /* *INDENT-ON* */ + + for (j = 0; j < vec_len (sm->to_resolve); j++) + { + rp = sm->to_resolve + j; + send_snat_static_map_resolve_details (rp, q, mp->context); + } +} + +static void *vl_api_snat_static_mapping_dump_t_print + (vl_api_snat_static_mapping_dump_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_static_mapping_dump "); + + FINISH; +} + +static void +vl_api_snat_control_ping_t_handler (vl_api_snat_control_ping_t * mp) +{ + vl_api_snat_control_ping_reply_t *rmp; + snat_main_t *sm = &snat_main; + int rv = 0; + + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_SNAT_CONTROL_PING_REPLY, + ({ + rmp->vpe_pid = ntohl (getpid ()); + })); + /* *INDENT-ON* */ +} + +static void *vl_api_snat_control_ping_t_print + (vl_api_snat_control_ping_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_control_ping "); + + FINISH; +} + +static void +vl_api_snat_show_config_t_handler (vl_api_snat_show_config_t * mp) +{ + vl_api_snat_show_config_reply_t *rmp; + snat_main_t *sm = &snat_main; + int rv = 0; + + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_SNAT_SHOW_CONFIG_REPLY, + ({ + rmp->translation_buckets = htonl (sm->translation_buckets); + rmp->translation_memory_size = htonl (sm->translation_memory_size); + rmp->user_buckets = htonl (sm->user_buckets); + rmp->user_memory_size = htonl (sm->user_memory_size); + rmp->max_translations_per_user = htonl (sm->max_translations_per_user); + rmp->outside_vrf_id = htonl (sm->outside_vrf_id); + rmp->inside_vrf_id = htonl (sm->inside_vrf_id); + rmp->static_mapping_only = sm->static_mapping_only; + rmp->static_mapping_connection_tracking = + sm->static_mapping_connection_tracking; + rmp->deterministic = sm->deterministic; + })); + /* *INDENT-ON* */ +} + +static void *vl_api_snat_show_config_t_print + (vl_api_snat_show_config_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_show_config "); + + FINISH; +} + +static void +vl_api_snat_set_workers_t_handler (vl_api_snat_set_workers_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_snat_set_workers_reply_t *rmp; + int rv = 0; + uword *bitmap = 0; + u64 mask = clib_net_to_host_u64 (mp->worker_mask); + + if (sm->num_workers < 2) + { + rv = VNET_API_ERROR_FEATURE_DISABLED; + goto send_reply; + } + + bitmap = clib_bitmap_set_multiple (bitmap, 0, mask, BITS (mask)); + rv = snat_set_workers (bitmap); + clib_bitmap_free (bitmap); + +send_reply: + REPLY_MACRO (VL_API_SNAT_SET_WORKERS_REPLY); +} + +static void *vl_api_snat_set_workers_t_print + (vl_api_snat_set_workers_t * mp, void *handle) +{ + u8 *s; + uword *bitmap = 0; + u8 first = 1; + int i; + u64 mask = clib_net_to_host_u64 (mp->worker_mask); + + s = format (0, "SCRIPT: snat_set_workers "); + bitmap = clib_bitmap_set_multiple (bitmap, 0, mask, BITS (mask)); + /* *INDENT-OFF* */ + clib_bitmap_foreach (i, bitmap, + ({ + if (first) + s = format (s, "%d", i); + else + s = format (s, ",%d", i); + first = 0; + })); + /* *INDENT-ON* */ + clib_bitmap_free (bitmap); + FINISH; +} + +static void + send_snat_worker_details + (u32 worker_index, unix_shared_memory_queue_t * q, u32 context) +{ + vl_api_snat_worker_details_t *rmp; + snat_main_t *sm = &snat_main; + vlib_worker_thread_t *w = + vlib_worker_threads + worker_index + sm->first_worker_index; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_SNAT_WORKER_DETAILS + sm->msg_id_base); + rmp->context = context; + rmp->worker_index = htonl (worker_index); + rmp->lcore_id = htonl (w->lcore_id); + strncpy ((char *) rmp->name, (char *) w->name, ARRAY_LEN (rmp->name) - 1); + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void +vl_api_snat_worker_dump_t_handler (vl_api_snat_worker_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + snat_main_t *sm = &snat_main; + u32 *worker_index; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + /* *INDENT-OFF* */ + vec_foreach (worker_index, sm->workers) + send_snat_worker_details(*worker_index, q, mp->context); + /* *INDENT-ON* */ +} + +static void *vl_api_snat_worker_dump_t_print + (vl_api_snat_worker_dump_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_worker_dump "); + + FINISH; +} + +static void + vl_api_snat_add_del_interface_addr_t_handler + (vl_api_snat_add_del_interface_addr_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_snat_add_del_interface_addr_reply_t *rmp; + u8 is_del = mp->is_add == 0; + u32 sw_if_index = ntohl (mp->sw_if_index); + int rv = 0; + + VALIDATE_SW_IF_INDEX (mp); + + rv = snat_add_interface_address (sm, sw_if_index, is_del); + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_SNAT_ADD_DEL_INTERFACE_ADDR_REPLY); +} + +static void *vl_api_snat_add_del_interface_addr_t_print + (vl_api_snat_add_del_interface_addr_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_add_del_interface_addr "); + s = format (s, "sw_if_index %d %s", + clib_host_to_net_u32 (mp->sw_if_index), + mp->is_add ? "" : "del"); + + FINISH; +} + +static void + send_snat_interface_addr_details + (u32 sw_if_index, unix_shared_memory_queue_t * q, u32 context) +{ + vl_api_snat_interface_addr_details_t *rmp; + snat_main_t *sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_SNAT_INTERFACE_ADDR_DETAILS + sm->msg_id_base); + rmp->sw_if_index = ntohl (sw_if_index); + rmp->context = context; + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void + vl_api_snat_interface_addr_dump_t_handler + (vl_api_snat_interface_addr_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + snat_main_t *sm = &snat_main; + u32 *i; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + /* *INDENT-OFF* */ + vec_foreach (i, sm->auto_add_sw_if_indices) + send_snat_interface_addr_details(*i, q, mp->context); + /* *INDENT-ON* */ +} + +static void *vl_api_snat_interface_addr_dump_t_print + (vl_api_snat_interface_addr_dump_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_interface_addr_dump "); + + FINISH; +} + +static void + vl_api_snat_ipfix_enable_disable_t_handler + (vl_api_snat_ipfix_enable_disable_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_snat_ipfix_enable_disable_reply_t *rmp; + int rv = 0; + + rv = snat_ipfix_logging_enable_disable (mp->enable, + clib_host_to_net_u32 + (mp->domain_id), + clib_host_to_net_u16 + (mp->src_port)); + + REPLY_MACRO (VL_API_SNAT_IPFIX_ENABLE_DISABLE_REPLY); +} + +static void *vl_api_snat_ipfix_enable_disable_t_print + (vl_api_snat_ipfix_enable_disable_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_ipfix_enable_disable "); + if (mp->domain_id) + s = format (s, "domain %d ", clib_net_to_host_u32 (mp->domain_id)); + if (mp->src_port) + s = format (s, "src_port %d ", clib_net_to_host_u16 (mp->src_port)); + if (!mp->enable) + s = format (s, "disable "); + + FINISH; +} + +static void + send_snat_user_details + (snat_user_t * u, unix_shared_memory_queue_t * q, u32 context) +{ + vl_api_snat_user_details_t *rmp; + snat_main_t *sm = &snat_main; + fib_table_t *fib = fib_table_get (u->fib_index, FIB_PROTOCOL_IP4); + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_SNAT_USER_DETAILS + sm->msg_id_base); + + rmp->vrf_id = ntohl (fib->ft_table_id); + + rmp->is_ip4 = 1; + clib_memcpy (rmp->ip_address, &(u->addr), 4); + rmp->nsessions = ntohl (u->nsessions); + rmp->nstaticsessions = ntohl (u->nstaticsessions); + rmp->context = context; + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void +vl_api_snat_user_dump_t_handler (vl_api_snat_user_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + snat_main_t *sm = &snat_main; + snat_main_per_thread_data_t *tsm; + snat_user_t *u; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + /* *INDENT-OFF* */ + vec_foreach (tsm, sm->per_thread_data) + vec_foreach (u, tsm->users) + send_snat_user_details (u, q, mp->context); + /* *INDENT-ON* */ +} + +static void *vl_api_snat_user_dump_t_print + (vl_api_snat_user_dump_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_user_dump "); + + FINISH; +} + +static void + send_snat_user_session_details + (snat_session_t * s, unix_shared_memory_queue_t * q, u32 context) +{ + vl_api_snat_user_session_details_t *rmp; + snat_main_t *sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_SNAT_USER_SESSION_DETAILS + sm->msg_id_base); + rmp->is_ip4 = 1; + clib_memcpy (rmp->outside_ip_address, (&s->out2in.addr), 4); + clib_memcpy (rmp->inside_ip_address, (&s->in2out.addr), 4); + rmp->is_static = s->flags & SNAT_SESSION_FLAG_STATIC_MAPPING ? 1 : 0; + rmp->last_heard = clib_host_to_net_u64 ((u64) s->last_heard); + rmp->total_bytes = clib_host_to_net_u64 (s->total_bytes); + rmp->total_pkts = ntohl (s->total_pkts); + rmp->context = context; + if (snat_is_unk_proto_session (s)) + { + rmp->outside_port = 0; + rmp->inside_port = 0; + rmp->protocol = ntohs (s->in2out.port); + } + else + { + rmp->outside_port = s->out2in.port; + rmp->inside_port = s->in2out.port; + rmp->protocol = ntohs (snat_proto_to_ip_proto (s->in2out.protocol)); + } + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void + vl_api_snat_user_session_dump_t_handler + (vl_api_snat_user_session_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + snat_main_t *sm = &snat_main; + snat_main_per_thread_data_t *tsm; + snat_session_t *s; + clib_bihash_kv_8_8_t key, value; + snat_user_key_t ukey; + snat_user_t *u; + u32 session_index, head_index, elt_index; + dlist_elt_t *head, *elt; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + if (!mp->is_ip4) + return; + + clib_memcpy (&ukey.addr, mp->ip_address, 4); + ukey.fib_index = fib_table_find (FIB_PROTOCOL_IP4, ntohl (mp->vrf_id)); + key.key = ukey.as_u64; + if (!clib_bihash_search_8_8 (&sm->worker_by_in, &key, &value)) + tsm = vec_elt_at_index (sm->per_thread_data, value.value); + else + tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); + if (clib_bihash_search_8_8 (&sm->user_hash, &key, &value)) + return; + u = pool_elt_at_index (tsm->users, value.value); + if (!u->nsessions && !u->nstaticsessions) + return; + + head_index = u->sessions_per_user_list_head_index; + head = pool_elt_at_index (tsm->list_pool, head_index); + elt_index = head->next; + elt = pool_elt_at_index (tsm->list_pool, elt_index); + session_index = elt->value; + while (session_index != ~0) + { + s = pool_elt_at_index (tsm->sessions, session_index); + + send_snat_user_session_details (s, q, mp->context); + + elt_index = elt->next; + elt = pool_elt_at_index (tsm->list_pool, elt_index); + session_index = elt->value; + } +} + +static void *vl_api_snat_user_session_dump_t_print + (vl_api_snat_user_session_dump_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_user_session_dump "); + s = format (s, "ip_address %U vrf_id %d\n", + format_ip4_address, mp->ip_address, + clib_net_to_host_u32 (mp->vrf_id)); + + FINISH; +} + +/******************************************************************/ +/*** detrministic NAT/CGN (old, will be deprecated after 17.10) ***/ +/******************************************************************/ + +static void +vl_api_snat_add_det_map_t_handler (vl_api_snat_add_det_map_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_snat_add_det_map_reply_t *rmp; + int rv = 0; + ip4_address_t in_addr, out_addr; + + clib_memcpy (&in_addr, mp->in_addr, 4); + clib_memcpy (&out_addr, mp->out_addr, 4); + rv = snat_det_add_map (sm, &in_addr, mp->in_plen, &out_addr, + mp->out_plen, mp->is_add); + + REPLY_MACRO (VL_API_SNAT_ADD_DET_MAP_REPLY); +} + +static void *vl_api_snat_add_det_map_t_print + (vl_api_snat_add_det_map_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_add_det_map "); + s = format (s, "inside address %U/%d outside address %U/%d\n", + format_ip4_address, mp->in_addr, mp->in_plen, + format_ip4_address, mp->out_addr, mp->out_plen); + + FINISH; +} + +static void +vl_api_snat_det_forward_t_handler (vl_api_snat_det_forward_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_snat_det_forward_reply_t *rmp; + int rv = 0; + u16 lo_port = 0, hi_port = 0; + snat_det_map_t *dm; + ip4_address_t in_addr, out_addr; + + out_addr.as_u32 = 0; + clib_memcpy (&in_addr, mp->in_addr, 4); + dm = snat_det_map_by_user (sm, &in_addr); + if (!dm) + { + rv = VNET_API_ERROR_NO_SUCH_ENTRY; + goto send_reply; + } + + snat_det_forward (dm, &in_addr, &out_addr, &lo_port); + hi_port = lo_port + dm->ports_per_host - 1; + +send_reply: + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_SNAT_DET_FORWARD_REPLY, + ({ + rmp->out_port_lo = ntohs (lo_port); + rmp->out_port_hi = ntohs (hi_port); + rmp->is_ip4 = 1; + memset (rmp->out_addr, 0, 16); + clib_memcpy (rmp->out_addr, &out_addr, 4); + })) + /* *INDENT-ON* */ +} + +static void *vl_api_snat_det_forward_t_print + (vl_api_snat_det_forward_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: smat_det_forward_t"); + s = format (s, "inside ip address %U\n", format_ip4_address, mp->in_addr); + + FINISH; +} + +static void +vl_api_snat_det_reverse_t_handler (vl_api_snat_det_reverse_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_snat_det_reverse_reply_t *rmp; + int rv = 0; + ip4_address_t out_addr, in_addr; + snat_det_map_t *dm; + + in_addr.as_u32 = 0; + clib_memcpy (&out_addr, mp->out_addr, 4); + dm = snat_det_map_by_out (sm, &out_addr); + if (!dm) + { + rv = VNET_API_ERROR_NO_SUCH_ENTRY; + goto send_reply; + } + + snat_det_reverse (dm, &out_addr, htons (mp->out_port), &in_addr); + +send_reply: + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_SNAT_DET_REVERSE_REPLY, + ({ + rmp->is_ip4 = 1; + memset (rmp->in_addr, 0, 16); + clib_memcpy (rmp->in_addr, &in_addr, 4); + })) + /* *INDENT-ON* */ +} + +static void *vl_api_snat_det_reverse_t_print + (vl_api_snat_det_reverse_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: smat_det_reverse_t"); + s = format (s, "outside ip address %U outside port %d", + format_ip4_address, mp->out_addr, ntohs (mp->out_port)); + + FINISH; +} + +static void + sent_snat_det_map_details + (snat_det_map_t * m, unix_shared_memory_queue_t * q, u32 context) +{ + vl_api_snat_det_map_details_t *rmp; + snat_main_t *sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_SNAT_DET_MAP_DETAILS + sm->msg_id_base); + rmp->is_ip4 = 1; + clib_memcpy (rmp->in_addr, &m->in_addr, 4); + rmp->in_plen = m->in_plen; + clib_memcpy (rmp->out_addr, &m->out_addr, 4); + rmp->out_plen = m->out_plen; + rmp->sharing_ratio = htonl (m->sharing_ratio); + rmp->ports_per_host = htons (m->ports_per_host); + rmp->ses_num = htonl (m->ses_num); + rmp->context = context; + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void +vl_api_snat_det_map_dump_t_handler (vl_api_snat_det_map_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + snat_main_t *sm = &snat_main; + snat_det_map_t *m; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + /* *INDENT-OFF* */ + vec_foreach(m, sm->det_maps) + sent_snat_det_map_details(m, q, mp->context); + /* *INDENT-ON* */ +} + +static void *vl_api_snat_det_map_dump_t_print + (vl_api_snat_det_map_dump_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_det_map_dump "); + + FINISH; +} + +static void +vl_api_snat_det_set_timeouts_t_handler (vl_api_snat_det_set_timeouts_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_snat_det_set_timeouts_reply_t *rmp; + int rv = 0; + + sm->udp_timeout = ntohl (mp->udp); + sm->tcp_established_timeout = ntohl (mp->tcp_established); + sm->tcp_transitory_timeout = ntohl (mp->tcp_transitory); + sm->icmp_timeout = ntohl (mp->icmp); + + REPLY_MACRO (VL_API_SNAT_DET_SET_TIMEOUTS_REPLY); +} + +static void *vl_api_snat_det_set_timeouts_t_print + (vl_api_snat_det_set_timeouts_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_det_set_timeouts "); + s = format (s, "udp %d tcp_established %d tcp_transitory %d icmp %d\n", + ntohl (mp->udp), + ntohl (mp->tcp_established), + ntohl (mp->tcp_transitory), ntohl (mp->icmp)); + + FINISH; +} + +static void +vl_api_snat_det_get_timeouts_t_handler (vl_api_snat_det_get_timeouts_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_snat_det_get_timeouts_reply_t *rmp; + int rv = 0; + + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_SNAT_DET_GET_TIMEOUTS_REPLY, + ({ + rmp->udp = htonl (sm->udp_timeout); + rmp->tcp_established = htonl (sm->tcp_established_timeout); + rmp->tcp_transitory = htonl (sm->tcp_transitory_timeout); + rmp->icmp = htonl (sm->icmp_timeout); + })) + /* *INDENT-ON* */ +} + +static void *vl_api_snat_det_get_timeouts_t_print + (vl_api_snat_det_get_timeouts_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_det_get_timeouts"); + + FINISH; +} + +static void + vl_api_snat_det_close_session_out_t_handler + (vl_api_snat_det_close_session_out_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_snat_det_close_session_out_reply_t *rmp; + ip4_address_t out_addr, ext_addr, in_addr; + snat_det_out_key_t key; + snat_det_map_t *dm; + snat_det_session_t *ses; + int rv = 0; + + clib_memcpy (&out_addr, mp->out_addr, 4); + clib_memcpy (&ext_addr, mp->ext_addr, 4); + + dm = snat_det_map_by_out (sm, &out_addr); + if (!dm) + { + rv = VNET_API_ERROR_NO_SUCH_ENTRY; + goto send_reply; + } + snat_det_reverse (dm, &ext_addr, ntohs (mp->out_port), &in_addr); + key.ext_host_addr = ext_addr; + key.ext_host_port = mp->ext_port; + key.out_port = mp->out_port; + ses = snat_det_get_ses_by_out (dm, &in_addr, key.as_u64); + if (!ses) + { + rv = VNET_API_ERROR_NO_SUCH_ENTRY; + goto send_reply; + } + snat_det_ses_close (dm, ses); + +send_reply: + REPLY_MACRO (VL_API_SNAT_DET_CLOSE_SESSION_OUT_REPLY); +} + +static void *vl_api_snat_det_close_session_out_t_print + (vl_api_snat_det_close_session_out_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_det_close_session_out "); + s = format (s, "out_addr %U out_port %d " + "ext_addr %U ext_port %d\n", + format_ip4_address, mp->out_addr, ntohs (mp->out_port), + format_ip4_address, mp->ext_addr, ntohs (mp->ext_port)); + + FINISH; +} + +static void + vl_api_snat_det_close_session_in_t_handler + (vl_api_snat_det_close_session_in_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_snat_det_close_session_in_reply_t *rmp; + ip4_address_t in_addr, ext_addr; + snat_det_out_key_t key; + snat_det_map_t *dm; + snat_det_session_t *ses; + int rv = 0; + + clib_memcpy (&in_addr, mp->in_addr, 4); + clib_memcpy (&ext_addr, mp->ext_addr, 4); + + dm = snat_det_map_by_user (sm, &in_addr); + if (!dm) + { + rv = VNET_API_ERROR_NO_SUCH_ENTRY; + goto send_reply; + } + key.ext_host_addr = ext_addr; + key.ext_host_port = mp->ext_port; + ses = snat_det_find_ses_by_in (dm, &in_addr, mp->in_port, key); + if (!ses) + { + rv = VNET_API_ERROR_NO_SUCH_ENTRY; + goto send_reply; + } + snat_det_ses_close (dm, ses); + +send_reply: + REPLY_MACRO (VL_API_SNAT_DET_CLOSE_SESSION_OUT_REPLY); +} + +static void *vl_api_snat_det_close_session_in_t_print + (vl_api_snat_det_close_session_in_t * mp, void *handle) +{ + u8 *s; + s = format (0, "SCRIPT: snat_det_close_session_in "); + s = format (s, "in_addr %U in_port %d " + "ext_addr %U ext_port %d\n", + format_ip4_address, mp->in_addr, ntohs (mp->in_port), + format_ip4_address, mp->ext_addr, ntohs (mp->ext_port)); + + FINISH; +} + +static void + send_snat_det_session_details + (snat_det_session_t * s, unix_shared_memory_queue_t * q, u32 context) +{ + vl_api_snat_det_session_details_t *rmp; + snat_main_t *sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_SNAT_DET_SESSION_DETAILS + sm->msg_id_base); + rmp->is_ip4 = 1; + rmp->in_port = s->in_port; + clib_memcpy (rmp->ext_addr, &s->out.ext_host_addr, 4); + rmp->ext_port = s->out.ext_host_port; + rmp->out_port = s->out.out_port; + rmp->state = s->state; + rmp->expire = ntohl (s->expire); + rmp->context = context; + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void +vl_api_snat_det_session_dump_t_handler (vl_api_snat_det_session_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + snat_main_t *sm = &snat_main; + ip4_address_t user_addr; + snat_det_map_t *dm; + snat_det_session_t *s, empty_ses; + u16 i; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + if (!mp->is_ip4) + return; + + memset (&empty_ses, 0, sizeof (empty_ses)); + clib_memcpy (&user_addr, mp->user_addr, 4); + dm = snat_det_map_by_user (sm, &user_addr); + if (!dm) + return; + + s = dm->sessions + snat_det_user_ses_offset (&user_addr, dm->in_plen); + for (i = 0; i < SNAT_DET_SES_PER_USER; i++) + { + if (s->out.as_u64) + send_snat_det_session_details (s, q, mp->context); + s++; + } +} + +static void *vl_api_snat_det_session_dump_t_print + (vl_api_snat_det_session_dump_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_det_session_dump "); + s = format (s, "user_addr %U\n", format_ip4_address, mp->user_addr); + + FINISH; +} + +/******************************/ +/*** Common NAT plugin APIs ***/ +/******************************/ + +static void +vl_api_nat_control_ping_t_handler (vl_api_nat_control_ping_t * mp) +{ + vl_api_nat_control_ping_reply_t *rmp; + snat_main_t *sm = &snat_main; + int rv = 0; + + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_NAT_CONTROL_PING_REPLY, + ({ + rmp->vpe_pid = ntohl (getpid ()); + })); + /* *INDENT-ON* */ +} + +static void * +vl_api_nat_control_ping_t_print (vl_api_nat_control_ping_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat_control_ping "); + + FINISH; +} + +static void +vl_api_nat_show_config_t_handler (vl_api_nat_show_config_t * mp) +{ + vl_api_nat_show_config_reply_t *rmp; + snat_main_t *sm = &snat_main; + int rv = 0; + + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_NAT_SHOW_CONFIG_REPLY, + ({ + rmp->translation_buckets = htonl (sm->translation_buckets); + rmp->translation_memory_size = htonl (sm->translation_memory_size); + rmp->user_buckets = htonl (sm->user_buckets); + rmp->user_memory_size = htonl (sm->user_memory_size); + rmp->max_translations_per_user = htonl (sm->max_translations_per_user); + rmp->outside_vrf_id = htonl (sm->outside_vrf_id); + rmp->inside_vrf_id = htonl (sm->inside_vrf_id); + rmp->static_mapping_only = sm->static_mapping_only; + rmp->static_mapping_connection_tracking = + sm->static_mapping_connection_tracking; + rmp->deterministic = sm->deterministic; + })); + /* *INDENT-ON* */ +} + +static void * +vl_api_nat_show_config_t_print (vl_api_nat_show_config_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat_show_config "); + + FINISH; +} + +static void +vl_api_nat_set_workers_t_handler (vl_api_nat_set_workers_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_snat_set_workers_reply_t *rmp; + int rv = 0; + uword *bitmap = 0; + u64 mask = clib_net_to_host_u64 (mp->worker_mask); + + if (sm->num_workers < 2) + { + rv = VNET_API_ERROR_FEATURE_DISABLED; + goto send_reply; + } + + bitmap = clib_bitmap_set_multiple (bitmap, 0, mask, BITS (mask)); + rv = snat_set_workers (bitmap); + clib_bitmap_free (bitmap); + +send_reply: + REPLY_MACRO (VL_API_NAT_SET_WORKERS_REPLY); +} + +static void * +vl_api_nat_set_workers_t_print (vl_api_nat_set_workers_t * mp, void *handle) +{ + u8 *s; + uword *bitmap = 0; + u8 first = 1; + int i; + u64 mask = clib_net_to_host_u64 (mp->worker_mask); + + s = format (0, "SCRIPT: nat_set_workers "); + bitmap = clib_bitmap_set_multiple (bitmap, 0, mask, BITS (mask)); + /* *INDENT-OFF* */ + clib_bitmap_foreach (i, bitmap, + ({ + if (first) + s = format (s, "%d", i); + else + s = format (s, ",%d", i); + first = 0; + })); + /* *INDENT-ON* */ + clib_bitmap_free (bitmap); + FINISH; +} + +static void +send_nat_worker_details (u32 worker_index, unix_shared_memory_queue_t * q, + u32 context) +{ + vl_api_nat_worker_details_t *rmp; + snat_main_t *sm = &snat_main; + vlib_worker_thread_t *w = + vlib_worker_threads + worker_index + sm->first_worker_index; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_NAT_WORKER_DETAILS + sm->msg_id_base); + rmp->context = context; + rmp->worker_index = htonl (worker_index); + rmp->lcore_id = htonl (w->lcore_id); + strncpy ((char *) rmp->name, (char *) w->name, ARRAY_LEN (rmp->name) - 1); + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void +vl_api_nat_worker_dump_t_handler (vl_api_nat_worker_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + snat_main_t *sm = &snat_main; + u32 *worker_index; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + /* *INDENT-OFF* */ + vec_foreach (worker_index, sm->workers) + send_nat_worker_details(*worker_index, q, mp->context); + /* *INDENT-ON* */ +} + +static void * +vl_api_nat_worker_dump_t_print (vl_api_nat_worker_dump_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat_worker_dump "); + + FINISH; +} + +static void +vl_api_nat_ipfix_enable_disable_t_handler (vl_api_nat_ipfix_enable_disable_t * + mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat_ipfix_enable_disable_reply_t *rmp; + int rv = 0; + + rv = snat_ipfix_logging_enable_disable (mp->enable, + clib_host_to_net_u32 + (mp->domain_id), + clib_host_to_net_u16 + (mp->src_port)); + + REPLY_MACRO (VL_API_NAT_IPFIX_ENABLE_DISABLE_REPLY); +} + +static void * +vl_api_nat_ipfix_enable_disable_t_print (vl_api_nat_ipfix_enable_disable_t * + mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat_ipfix_enable_disable "); + if (mp->domain_id) + s = format (s, "domain %d ", clib_net_to_host_u32 (mp->domain_id)); + if (mp->src_port) + s = format (s, "src_port %d ", clib_net_to_host_u16 (mp->src_port)); + if (!mp->enable) + s = format (s, "disable "); + + FINISH; +} + +/*************/ +/*** NAT44 ***/ +/*************/ +static void + vl_api_nat44_add_del_address_range_t_handler + (vl_api_nat44_add_del_address_range_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat44_add_del_address_range_reply_t *rmp; + ip4_address_t this_addr; + u32 start_host_order, end_host_order; + u32 vrf_id; + int i, count; + int rv = 0; + u32 *tmp; + + if (sm->static_mapping_only) + { + rv = VNET_API_ERROR_FEATURE_DISABLED; + goto send_reply; + } + + tmp = (u32 *) mp->first_ip_address; + start_host_order = clib_host_to_net_u32 (tmp[0]); + tmp = (u32 *) mp->last_ip_address; + end_host_order = clib_host_to_net_u32 (tmp[0]); + + count = (end_host_order - start_host_order) + 1; + + vrf_id = clib_host_to_net_u32 (mp->vrf_id); + + if (count > 1024) + clib_warning ("%U - %U, %d addresses...", + format_ip4_address, mp->first_ip_address, + format_ip4_address, mp->last_ip_address, count); + + memcpy (&this_addr.as_u8, mp->first_ip_address, 4); + + for (i = 0; i < count; i++) + { + if (mp->is_add) + snat_add_address (sm, &this_addr, vrf_id); + else + rv = snat_del_address (sm, this_addr, 0); + + if (rv) + goto send_reply; + + increment_v4_address (&this_addr); + } + +send_reply: + REPLY_MACRO (VL_API_NAT44_ADD_DEL_ADDRESS_RANGE_REPLY); +} + +static void *vl_api_nat44_add_del_address_range_t_print + (vl_api_nat44_add_del_address_range_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat44_add_address_range "); + s = format (s, "%U ", format_ip4_address, mp->first_ip_address); + if (memcmp (mp->first_ip_address, mp->last_ip_address, 4)) + { + s = format (s, " - %U ", format_ip4_address, mp->last_ip_address); + } + FINISH; +} + +static void +send_nat44_address_details (snat_address_t * a, + unix_shared_memory_queue_t * q, u32 context) +{ + vl_api_nat44_address_details_t *rmp; + snat_main_t *sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_NAT44_ADDRESS_DETAILS + sm->msg_id_base); + clib_memcpy (rmp->ip_address, &(a->addr), 4); + if (a->fib_index != ~0) + { + fib_table_t *fib = fib_table_get (a->fib_index, FIB_PROTOCOL_IP4); + rmp->vrf_id = ntohl (fib->ft_table_id); + } + else + rmp->vrf_id = ~0; + rmp->context = context; + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void +vl_api_nat44_address_dump_t_handler (vl_api_nat44_address_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + snat_main_t *sm = &snat_main; + snat_address_t *a; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + /* *INDENT-OFF* */ + vec_foreach (a, sm->addresses) + send_nat44_address_details (a, q, mp->context); + /* *INDENT-ON* */ +} + +static void * +vl_api_nat44_address_dump_t_print (vl_api_nat44_address_dump_t * mp, + void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat44_address_dump "); + + FINISH; +} + +static void + vl_api_nat44_interface_add_del_feature_t_handler + (vl_api_nat44_interface_add_del_feature_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat44_interface_add_del_feature_reply_t *rmp; + u8 is_del = mp->is_add == 0; + u32 sw_if_index = ntohl (mp->sw_if_index); + int rv = 0; + + VALIDATE_SW_IF_INDEX (mp); + + rv = snat_interface_add_del (sw_if_index, mp->is_inside, is_del); + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_NAT44_INTERFACE_ADD_DEL_FEATURE_REPLY); +} + +static void *vl_api_nat44_interface_add_del_feature_t_print + (vl_api_nat44_interface_add_del_feature_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat44_interface_add_del_feature "); + s = format (s, "sw_if_index %d %s %s", + clib_host_to_net_u32 (mp->sw_if_index), + mp->is_inside ? "in" : "out", mp->is_add ? "" : "del"); + + FINISH; +} + +static void +send_nat44_interface_details (snat_interface_t * i, + unix_shared_memory_queue_t * q, u32 context) +{ + vl_api_nat44_interface_details_t *rmp; + snat_main_t *sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_NAT44_INTERFACE_DETAILS + sm->msg_id_base); + rmp->sw_if_index = ntohl (i->sw_if_index); + rmp->is_inside = i->is_inside; + rmp->context = context; + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void +vl_api_nat44_interface_dump_t_handler (vl_api_nat44_interface_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + snat_main_t *sm = &snat_main; + snat_interface_t *i; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + /* *INDENT-OFF* */ + pool_foreach (i, sm->interfaces, + ({ + send_nat44_interface_details(i, q, mp->context); + })); + /* *INDENT-ON* */ +} + +static void * +vl_api_nat44_interface_dump_t_print (vl_api_nat44_interface_dump_t * mp, + void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat44_interface_dump "); + + FINISH; +} + +static void + vl_api_nat44_interface_add_del_output_feature_t_handler + (vl_api_nat44_interface_add_del_output_feature_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat44_interface_add_del_output_feature_reply_t *rmp; + u8 is_del = mp->is_add == 0; + u32 sw_if_index = ntohl (mp->sw_if_index); + int rv = 0; + + VALIDATE_SW_IF_INDEX (mp); + + rv = snat_interface_add_del_output_feature (sw_if_index, mp->is_inside, + is_del); + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_NAT44_INTERFACE_ADD_DEL_OUTPUT_FEATURE_REPLY); +} + +static void *vl_api_nat44_interface_add_del_output_feature_t_print + (vl_api_nat44_interface_add_del_output_feature_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat44_interface_add_del_output_feature "); + s = format (s, "sw_if_index %d %s %s", + clib_host_to_net_u32 (mp->sw_if_index), + mp->is_inside ? "in" : "out", mp->is_add ? "" : "del"); + + FINISH; +} + +static void +send_nat44_interface_output_feature_details (snat_interface_t * i, + unix_shared_memory_queue_t * q, + u32 context) +{ + vl_api_nat44_interface_output_feature_details_t *rmp; + snat_main_t *sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_NAT44_INTERFACE_OUTPUT_FEATURE_DETAILS + sm->msg_id_base); + rmp->sw_if_index = ntohl (i->sw_if_index); + rmp->context = context; + rmp->is_inside = i->is_inside; + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void + vl_api_nat44_interface_output_feature_dump_t_handler + (vl_api_nat44_interface_output_feature_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + snat_main_t *sm = &snat_main; + snat_interface_t *i; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + /* *INDENT-OFF* */ + pool_foreach (i, sm->output_feature_interfaces, + ({ + send_nat44_interface_output_feature_details(i, q, mp->context); + })); + /* *INDENT-ON* */ +} + +static void *vl_api_nat44_interface_output_feature_dump_t_print + (vl_api_nat44_interface_output_feature_dump_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat44_interface_output_feature_dump "); + + FINISH; +} + +static void + vl_api_nat44_add_del_static_mapping_t_handler + (vl_api_nat44_add_del_static_mapping_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat44_add_del_static_mapping_reply_t *rmp; + ip4_address_t local_addr, external_addr; + u16 local_port = 0, external_port = 0; + u32 vrf_id, external_sw_if_index; + int rv = 0; + snat_protocol_t proto; + + memcpy (&local_addr.as_u8, mp->local_ip_address, 4); + memcpy (&external_addr.as_u8, mp->external_ip_address, 4); + if (mp->addr_only == 0) + { + local_port = clib_net_to_host_u16 (mp->local_port); + external_port = clib_net_to_host_u16 (mp->external_port); + } + vrf_id = clib_net_to_host_u32 (mp->vrf_id); + external_sw_if_index = clib_net_to_host_u32 (mp->external_sw_if_index); + proto = ip_proto_to_snat_proto (mp->protocol); + + rv = snat_add_static_mapping (local_addr, external_addr, local_port, + external_port, vrf_id, mp->addr_only, + external_sw_if_index, proto, mp->is_add); + + REPLY_MACRO (VL_API_NAT44_ADD_DEL_STATIC_MAPPING_REPLY); +} + +static void *vl_api_nat44_add_del_static_mapping_t_print + (vl_api_nat44_add_del_static_mapping_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat44_add_del_static_mapping "); + s = format (s, "protocol %d local_addr %U external_addr %U ", + mp->protocol, + format_ip4_address, mp->local_ip_address, + format_ip4_address, mp->external_ip_address); + + if (mp->addr_only == 0) + s = format (s, "local_port %d external_port %d ", + clib_net_to_host_u16 (mp->local_port), + clib_net_to_host_u16 (mp->external_port)); + + if (mp->vrf_id != ~0) + s = format (s, "vrf %d", clib_net_to_host_u32 (mp->vrf_id)); + + if (mp->external_sw_if_index != ~0) + s = format (s, "external_sw_if_index %d", + clib_net_to_host_u32 (mp->external_sw_if_index)); + FINISH; +} + +static void +send_nat44_static_mapping_details (snat_static_mapping_t * m, + unix_shared_memory_queue_t * q, + u32 context) +{ + vl_api_nat44_static_mapping_details_t *rmp; + snat_main_t *sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_NAT44_STATIC_MAPPING_DETAILS + sm->msg_id_base); + rmp->addr_only = m->addr_only; + clib_memcpy (rmp->local_ip_address, &(m->local_addr), 4); + clib_memcpy (rmp->external_ip_address, &(m->external_addr), 4); + rmp->local_port = htons (m->local_port); + rmp->external_port = htons (m->external_port); + rmp->external_sw_if_index = ~0; + rmp->vrf_id = htonl (m->vrf_id); + rmp->protocol = snat_proto_to_ip_proto (m->proto); + rmp->context = context; + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void +send_nat44_static_map_resolve_details (snat_static_map_resolve_t * m, + unix_shared_memory_queue_t * q, + u32 context) +{ + vl_api_nat44_static_mapping_details_t *rmp; + snat_main_t *sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_NAT44_STATIC_MAPPING_DETAILS + sm->msg_id_base); + rmp->addr_only = m->addr_only; + clib_memcpy (rmp->local_ip_address, &(m->l_addr), 4); + rmp->local_port = htons (m->l_port); + rmp->external_port = htons (m->e_port); + rmp->external_sw_if_index = htonl (m->sw_if_index); + rmp->vrf_id = htonl (m->vrf_id); + rmp->protocol = snat_proto_to_ip_proto (m->proto); + rmp->context = context; + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void +vl_api_nat44_static_mapping_dump_t_handler (vl_api_nat44_static_mapping_dump_t + * mp) +{ + unix_shared_memory_queue_t *q; + snat_main_t *sm = &snat_main; + snat_static_mapping_t *m; + snat_static_map_resolve_t *rp; + int j; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + /* *INDENT-OFF* */ + pool_foreach (m, sm->static_mappings, + ({ + send_nat44_static_mapping_details (m, q, mp->context); + })); + /* *INDENT-ON* */ + + for (j = 0; j < vec_len (sm->to_resolve); j++) + { + rp = sm->to_resolve + j; + send_nat44_static_map_resolve_details (rp, q, mp->context); + } +} + +static void * +vl_api_nat44_static_mapping_dump_t_print (vl_api_nat44_static_mapping_dump_t * + mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat44_static_mapping_dump "); + + FINISH; +} + +static void + vl_api_nat44_add_del_interface_addr_t_handler + (vl_api_nat44_add_del_interface_addr_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat44_add_del_interface_addr_reply_t *rmp; + u8 is_del = mp->is_add == 0; + u32 sw_if_index = ntohl (mp->sw_if_index); + int rv = 0; + + VALIDATE_SW_IF_INDEX (mp); + + rv = snat_add_interface_address (sm, sw_if_index, is_del); + + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_NAT44_ADD_DEL_INTERFACE_ADDR_REPLY); +} + +static void *vl_api_nat44_add_del_interface_addr_t_print + (vl_api_nat44_add_del_interface_addr_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat44_add_del_interface_addr "); + s = format (s, "sw_if_index %d %s", + clib_host_to_net_u32 (mp->sw_if_index), + mp->is_add ? "" : "del"); + + FINISH; +} + +static void +send_nat44_interface_addr_details (u32 sw_if_index, + unix_shared_memory_queue_t * q, + u32 context) +{ + vl_api_nat44_interface_addr_details_t *rmp; + snat_main_t *sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_NAT44_INTERFACE_ADDR_DETAILS + sm->msg_id_base); + rmp->sw_if_index = ntohl (sw_if_index); + rmp->context = context; + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void +vl_api_nat44_interface_addr_dump_t_handler (vl_api_nat44_interface_addr_dump_t + * mp) +{ + unix_shared_memory_queue_t *q; + snat_main_t *sm = &snat_main; + u32 *i; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + /* *INDENT-OFF* */ + vec_foreach (i, sm->auto_add_sw_if_indices) + send_nat44_interface_addr_details(*i, q, mp->context); + /* *INDENT-ON* */ +} + +static void * +vl_api_nat44_interface_addr_dump_t_print (vl_api_nat44_interface_addr_dump_t * + mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat44_interface_addr_dump "); + + FINISH; +} + +static void +send_nat44_user_details (snat_user_t * u, unix_shared_memory_queue_t * q, + u32 context) +{ + vl_api_nat44_user_details_t *rmp; + snat_main_t *sm = &snat_main; + fib_table_t *fib = fib_table_get (u->fib_index, FIB_PROTOCOL_IP4); + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_NAT44_USER_DETAILS + sm->msg_id_base); + + rmp->vrf_id = ntohl (fib->ft_table_id); + + clib_memcpy (rmp->ip_address, &(u->addr), 4); + rmp->nsessions = ntohl (u->nsessions); + rmp->nstaticsessions = ntohl (u->nstaticsessions); + rmp->context = context; + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void +vl_api_nat44_user_dump_t_handler (vl_api_nat44_user_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + snat_main_t *sm = &snat_main; + snat_main_per_thread_data_t *tsm; + snat_user_t *u; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + /* *INDENT-OFF* */ + vec_foreach (tsm, sm->per_thread_data) + vec_foreach (u, tsm->users) + send_nat44_user_details (u, q, mp->context); + /* *INDENT-ON* */ +} + +static void * +vl_api_nat44_user_dump_t_print (vl_api_nat44_user_dump_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat44_user_dump "); + + FINISH; +} + +static void +send_nat44_user_session_details (snat_session_t * s, + unix_shared_memory_queue_t * q, u32 context) +{ + vl_api_nat44_user_session_details_t *rmp; + snat_main_t *sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_NAT44_USER_SESSION_DETAILS + sm->msg_id_base); + clib_memcpy (rmp->outside_ip_address, (&s->out2in.addr), 4); + clib_memcpy (rmp->inside_ip_address, (&s->in2out.addr), 4); + rmp->is_static = s->flags & SNAT_SESSION_FLAG_STATIC_MAPPING ? 1 : 0; + rmp->last_heard = clib_host_to_net_u64 ((u64) s->last_heard); + rmp->total_bytes = clib_host_to_net_u64 (s->total_bytes); + rmp->total_pkts = ntohl (s->total_pkts); + rmp->context = context; + if (snat_is_unk_proto_session (s)) + { + rmp->outside_port = 0; + rmp->inside_port = 0; + rmp->protocol = ntohs (s->in2out.port); + } + else + { + rmp->outside_port = s->out2in.port; + rmp->inside_port = s->in2out.port; + rmp->protocol = ntohs (snat_proto_to_ip_proto (s->in2out.protocol)); + } + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void +vl_api_nat44_user_session_dump_t_handler (vl_api_nat44_user_session_dump_t * + mp) +{ + unix_shared_memory_queue_t *q; + snat_main_t *sm = &snat_main; + snat_main_per_thread_data_t *tsm; + snat_session_t *s; + clib_bihash_kv_8_8_t key, value; + snat_user_key_t ukey; + snat_user_t *u; + u32 session_index, head_index, elt_index; + dlist_elt_t *head, *elt; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + clib_memcpy (&ukey.addr, mp->ip_address, 4); + ukey.fib_index = fib_table_find (FIB_PROTOCOL_IP4, ntohl (mp->vrf_id)); + key.key = ukey.as_u64; + if (!clib_bihash_search_8_8 (&sm->worker_by_in, &key, &value)) + tsm = vec_elt_at_index (sm->per_thread_data, value.value); + else + tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); + if (clib_bihash_search_8_8 (&sm->user_hash, &key, &value)) + return; + u = pool_elt_at_index (tsm->users, value.value); + if (!u->nsessions && !u->nstaticsessions) + return; + + head_index = u->sessions_per_user_list_head_index; + head = pool_elt_at_index (tsm->list_pool, head_index); + elt_index = head->next; + elt = pool_elt_at_index (tsm->list_pool, elt_index); + session_index = elt->value; + while (session_index != ~0) + { + s = pool_elt_at_index (tsm->sessions, session_index); + + send_nat44_user_session_details (s, q, mp->context); + + elt_index = elt->next; + elt = pool_elt_at_index (tsm->list_pool, elt_index); + session_index = elt->value; + } +} + +static void * +vl_api_nat44_user_session_dump_t_print (vl_api_nat44_user_session_dump_t * mp, + void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat44_user_session_dump "); + s = format (s, "ip_address %U vrf_id %d\n", + format_ip4_address, mp->ip_address, + clib_net_to_host_u32 (mp->vrf_id)); + + FINISH; +} + +/*******************************/ +/*** Deterministic NAT (CGN) ***/ +/*******************************/ + +static void +vl_api_nat_det_add_del_map_t_handler (vl_api_nat_det_add_del_map_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat_det_add_del_map_reply_t *rmp; + int rv = 0; + ip4_address_t in_addr, out_addr; + + if (!mp->is_nat44) + { + rv = VNET_API_ERROR_UNIMPLEMENTED; + goto send_reply; + } + + clib_memcpy (&in_addr, mp->in_addr, 4); + clib_memcpy (&out_addr, mp->out_addr, 4); + rv = snat_det_add_map (sm, &in_addr, mp->in_plen, &out_addr, + mp->out_plen, mp->is_add); + +send_reply: + REPLY_MACRO (VL_API_NAT_DET_ADD_DEL_MAP_REPLY); +} + +static void * +vl_api_nat_det_add_del_map_t_print (vl_api_nat_det_add_del_map_t * mp, + void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat_det_add_del_map "); + s = format (s, "inside address %U/%d outside address %U/%d\n", + format_ip4_address, mp->in_addr, mp->in_plen, + format_ip4_address, mp->out_addr, mp->out_plen); + + FINISH; +} + +static void +vl_api_nat_det_forward_t_handler (vl_api_nat_det_forward_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat_det_forward_reply_t *rmp; + int rv = 0; + u16 lo_port = 0, hi_port = 0; + snat_det_map_t *dm; + ip4_address_t in_addr, out_addr; + + if (!mp->is_nat44) + { + out_addr.as_u32 = 0; + rv = VNET_API_ERROR_UNIMPLEMENTED; + goto send_reply; + } + + out_addr.as_u32 = 0; + clib_memcpy (&in_addr, mp->in_addr, 4); + dm = snat_det_map_by_user (sm, &in_addr); + if (!dm) + { + rv = VNET_API_ERROR_NO_SUCH_ENTRY; + goto send_reply; + } + + snat_det_forward (dm, &in_addr, &out_addr, &lo_port); + hi_port = lo_port + dm->ports_per_host - 1; + +send_reply: + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_NAT_DET_FORWARD_REPLY, + ({ + rmp->out_port_lo = ntohs (lo_port); + rmp->out_port_hi = ntohs (hi_port); + clib_memcpy (rmp->out_addr, &out_addr, 4); + })) + /* *INDENT-ON* */ +} + +static void * +vl_api_nat_det_forward_t_print (vl_api_nat_det_forward_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat_det_forward"); + s = format (s, "inside ip address %U\n", format_ip4_address, mp->in_addr); + + FINISH; +} + +static void +vl_api_nat_det_reverse_t_handler (vl_api_nat_det_reverse_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat_det_reverse_reply_t *rmp; + int rv = 0; + ip4_address_t out_addr, in_addr; + snat_det_map_t *dm; + + in_addr.as_u32 = 0; + clib_memcpy (&out_addr, mp->out_addr, 4); + dm = snat_det_map_by_out (sm, &out_addr); + if (!dm) + { + rv = VNET_API_ERROR_NO_SUCH_ENTRY; + goto send_reply; + } + + snat_det_reverse (dm, &out_addr, htons (mp->out_port), &in_addr); + +send_reply: + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_NAT_DET_REVERSE_REPLY, + ({ + rmp->is_nat44 = 1; + memset (rmp->in_addr, 0, 16); + clib_memcpy (rmp->in_addr, &in_addr, 4); + })) + /* *INDENT-ON* */ +} + +static void * +vl_api_nat_det_reverse_t_print (vl_api_nat_det_reverse_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat_det_reverse"); + s = format (s, "outside ip address %U outside port %d", + format_ip4_address, mp->out_addr, ntohs (mp->out_port)); + + FINISH; +} + +static void +sent_nat_det_map_details (snat_det_map_t * m, unix_shared_memory_queue_t * q, + u32 context) +{ + vl_api_nat_det_map_details_t *rmp; + snat_main_t *sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_NAT_DET_MAP_DETAILS + sm->msg_id_base); + rmp->is_nat44 = 1; + clib_memcpy (rmp->in_addr, &m->in_addr, 4); + rmp->in_plen = m->in_plen; + clib_memcpy (rmp->out_addr, &m->out_addr, 4); + rmp->out_plen = m->out_plen; + rmp->sharing_ratio = htonl (m->sharing_ratio); + rmp->ports_per_host = htons (m->ports_per_host); + rmp->ses_num = htonl (m->ses_num); + rmp->context = context; + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void +vl_api_nat_det_map_dump_t_handler (vl_api_nat_det_map_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + snat_main_t *sm = &snat_main; + snat_det_map_t *m; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + /* *INDENT-OFF* */ + vec_foreach(m, sm->det_maps) + sent_nat_det_map_details(m, q, mp->context); + /* *INDENT-ON* */ +} + +static void * +vl_api_nat_det_map_dump_t_print (vl_api_nat_det_map_dump_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat_det_map_dump "); + + FINISH; +} + +static void +vl_api_nat_det_set_timeouts_t_handler (vl_api_nat_det_set_timeouts_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat_det_set_timeouts_reply_t *rmp; + int rv = 0; + + sm->udp_timeout = ntohl (mp->udp); + sm->tcp_established_timeout = ntohl (mp->tcp_established); + sm->tcp_transitory_timeout = ntohl (mp->tcp_transitory); + sm->icmp_timeout = ntohl (mp->icmp); + + REPLY_MACRO (VL_API_NAT_DET_SET_TIMEOUTS_REPLY); +} + +static void * +vl_api_nat_det_set_timeouts_t_print (vl_api_nat_det_set_timeouts_t * mp, + void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat_det_set_timeouts "); + s = format (s, "udp %d tcp_established %d tcp_transitory %d icmp %d\n", + ntohl (mp->udp), + ntohl (mp->tcp_established), + ntohl (mp->tcp_transitory), ntohl (mp->icmp)); + + FINISH; +} + +static void +vl_api_nat_det_get_timeouts_t_handler (vl_api_nat_det_get_timeouts_t * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat_det_get_timeouts_reply_t *rmp; + int rv = 0; + + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_NAT_DET_GET_TIMEOUTS_REPLY, + ({ + rmp->udp = htonl (sm->udp_timeout); + rmp->tcp_established = htonl (sm->tcp_established_timeout); + rmp->tcp_transitory = htonl (sm->tcp_transitory_timeout); + rmp->icmp = htonl (sm->icmp_timeout); + })) + /* *INDENT-ON* */ +} + +static void * +vl_api_nat_det_get_timeouts_t_print (vl_api_nat_det_get_timeouts_t * mp, + void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat_det_get_timeouts"); + + FINISH; +} + +static void +vl_api_nat_det_close_session_out_t_handler (vl_api_nat_det_close_session_out_t + * mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat_det_close_session_out_reply_t *rmp; + ip4_address_t out_addr, ext_addr, in_addr; + snat_det_out_key_t key; + snat_det_map_t *dm; + snat_det_session_t *ses; + int rv = 0; + + clib_memcpy (&out_addr, mp->out_addr, 4); + clib_memcpy (&ext_addr, mp->ext_addr, 4); + + dm = snat_det_map_by_out (sm, &out_addr); + if (!dm) + { + rv = VNET_API_ERROR_NO_SUCH_ENTRY; + goto send_reply; + } + snat_det_reverse (dm, &ext_addr, ntohs (mp->out_port), &in_addr); + key.ext_host_addr = ext_addr; + key.ext_host_port = mp->ext_port; + key.out_port = mp->out_port; + ses = snat_det_get_ses_by_out (dm, &in_addr, key.as_u64); + if (!ses) + { + rv = VNET_API_ERROR_NO_SUCH_ENTRY; + goto send_reply; + } + snat_det_ses_close (dm, ses); + +send_reply: + REPLY_MACRO (VL_API_NAT_DET_CLOSE_SESSION_OUT_REPLY); +} + +static void * +vl_api_nat_det_close_session_out_t_print (vl_api_nat_det_close_session_out_t * + mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat_det_close_session_out "); + s = format (s, "out_addr %U out_port %d " + "ext_addr %U ext_port %d\n", + format_ip4_address, mp->out_addr, ntohs (mp->out_port), + format_ip4_address, mp->ext_addr, ntohs (mp->ext_port)); + + FINISH; +} + +static void +vl_api_nat_det_close_session_in_t_handler (vl_api_nat_det_close_session_in_t * + mp) +{ + snat_main_t *sm = &snat_main; + vl_api_nat_det_close_session_in_reply_t *rmp; + ip4_address_t in_addr, ext_addr; + snat_det_out_key_t key; + snat_det_map_t *dm; + snat_det_session_t *ses; + int rv = 0; + + if (!mp->is_nat44) + { + rv = VNET_API_ERROR_UNIMPLEMENTED; + goto send_reply; + } + + clib_memcpy (&in_addr, mp->in_addr, 4); + clib_memcpy (&ext_addr, mp->ext_addr, 4); + + dm = snat_det_map_by_user (sm, &in_addr); + if (!dm) + { + rv = VNET_API_ERROR_NO_SUCH_ENTRY; + goto send_reply; + } + key.ext_host_addr = ext_addr; + key.ext_host_port = mp->ext_port; + ses = snat_det_find_ses_by_in (dm, &in_addr, mp->in_port, key); + if (!ses) + { + rv = VNET_API_ERROR_NO_SUCH_ENTRY; + goto send_reply; + } + snat_det_ses_close (dm, ses); + +send_reply: + REPLY_MACRO (VL_API_NAT_DET_CLOSE_SESSION_OUT_REPLY); +} + +static void * +vl_api_nat_det_close_session_in_t_print (vl_api_nat_det_close_session_in_t * + mp, void *handle) +{ + u8 *s; + s = format (0, "SCRIPT: nat_det_close_session_in "); + s = format (s, "in_addr %U in_port %d ext_addr %U ext_port %d\n", + format_ip4_address, mp->in_addr, ntohs (mp->in_port), + format_ip4_address, mp->ext_addr, ntohs (mp->ext_port)); + + FINISH; +} + +static void +send_nat_det_session_details (snat_det_session_t * s, + unix_shared_memory_queue_t * q, u32 context) +{ + vl_api_nat_det_session_details_t *rmp; + snat_main_t *sm = &snat_main; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_NAT_DET_SESSION_DETAILS + sm->msg_id_base); + rmp->in_port = s->in_port; + clib_memcpy (rmp->ext_addr, &s->out.ext_host_addr, 4); + rmp->ext_port = s->out.ext_host_port; + rmp->out_port = s->out.out_port; + rmp->state = s->state; + rmp->expire = ntohl (s->expire); + rmp->context = context; + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void +vl_api_nat_det_session_dump_t_handler (vl_api_nat_det_session_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + snat_main_t *sm = &snat_main; + ip4_address_t user_addr; + snat_det_map_t *dm; + snat_det_session_t *s, empty_ses; + u16 i; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + if (!mp->is_nat44) + return; + + memset (&empty_ses, 0, sizeof (empty_ses)); + clib_memcpy (&user_addr, mp->user_addr, 4); + dm = snat_det_map_by_user (sm, &user_addr); + if (!dm) + return; + + s = dm->sessions + snat_det_user_ses_offset (&user_addr, dm->in_plen); + for (i = 0; i < SNAT_DET_SES_PER_USER; i++) + { + if (s->out.as_u64) + send_nat_det_session_details (s, q, mp->context); + s++; + } +} + +static void * +vl_api_nat_det_session_dump_t_print (vl_api_nat_det_session_dump_t * mp, + void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat_det_session_dump "); + s = format (s, "user_addr %U\n", format_ip4_address, mp->user_addr); + + FINISH; +} + +/*************/ +/*** NAT64 ***/ +/*************/ + +static void + vl_api_nat64_add_del_pool_addr_range_t_handler + (vl_api_nat64_add_del_pool_addr_range_t * mp) +{ + vl_api_nat64_add_del_pool_addr_range_reply_t *rmp; + snat_main_t *sm = &snat_main; + nat64_main_t *nm = &nat64_main; + int rv = 0; + ip4_address_t this_addr; + u32 start_host_order, end_host_order; + u32 vrf_id; + int i, count; + u32 *tmp; + + if (nm->is_disabled) + { + rv = VNET_API_ERROR_FEATURE_DISABLED; + goto send_reply; + } + + tmp = (u32 *) mp->start_addr; + start_host_order = clib_host_to_net_u32 (tmp[0]); + tmp = (u32 *) mp->end_addr; + end_host_order = clib_host_to_net_u32 (tmp[0]); + + count = (end_host_order - start_host_order) + 1; + + vrf_id = clib_host_to_net_u32 (mp->vrf_id); + + memcpy (&this_addr.as_u8, mp->start_addr, 4); + + for (i = 0; i < count; i++) + { + if ((rv = nat64_add_del_pool_addr (&this_addr, vrf_id, mp->is_add))) + goto send_reply; + + increment_v4_address (&this_addr); + } + +send_reply: + REPLY_MACRO (VL_API_NAT64_ADD_DEL_POOL_ADDR_RANGE_REPLY); +} + +static void *vl_api_nat64_add_del_pool_addr_range_t_print + (vl_api_nat64_add_del_pool_addr_range_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat64_add_del_pool_addr_range "); + s = format (s, "%U - %U vrf_id %u %s\n", + format_ip4_address, mp->start_addr, + format_ip4_address, mp->end_addr, + ntohl (mp->vrf_id), mp->is_add ? "" : "del"); + + FINISH; +} + +typedef struct nat64_api_walk_ctx_t_ +{ + unix_shared_memory_queue_t *q; + u32 context; +} nat64_api_walk_ctx_t; + +static int +nat64_api_pool_walk (snat_address_t * a, void *arg) +{ + vl_api_nat64_pool_addr_details_t *rmp; + snat_main_t *sm = &snat_main; + nat64_api_walk_ctx_t *ctx = arg; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_NAT64_POOL_ADDR_DETAILS + sm->msg_id_base); + clib_memcpy (rmp->address, &(a->addr), 4); + if (a->fib_index != ~0) + { + fib_table_t *fib = fib_table_get (a->fib_index, FIB_PROTOCOL_IP6); + if (!fib) + return -1; + rmp->vrf_id = ntohl (fib->ft_table_id); + } + else + rmp->vrf_id = ~0; + rmp->context = ctx->context; + + vl_msg_api_send_shmem (ctx->q, (u8 *) & rmp); + + return 0; +} + +static void +vl_api_nat64_pool_addr_dump_t_handler (vl_api_nat64_pool_addr_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + nat64_main_t *nm = &nat64_main; + + if (nm->is_disabled) + return; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + nat64_api_walk_ctx_t ctx = { + .q = q, + .context = mp->context, + }; + + nat64_pool_addr_walk (nat64_api_pool_walk, &ctx); +} + +static void * +vl_api_nat64_pool_addr_dump_t_print (vl_api_nat64_pool_addr_dump_t * mp, + void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat64_pool_addr_dump\n"); + + FINISH; +} + +static void +vl_api_nat64_add_del_interface_t_handler (vl_api_nat64_add_del_interface_t * + mp) +{ + snat_main_t *sm = &snat_main; + nat64_main_t *nm = &nat64_main; + vl_api_nat64_add_del_interface_reply_t *rmp; + int rv = 0; + + if (nm->is_disabled) + { + rv = VNET_API_ERROR_FEATURE_DISABLED; + goto send_reply; + } + + VALIDATE_SW_IF_INDEX (mp); + + rv = + nat64_add_del_interface (ntohl (mp->sw_if_index), mp->is_inside, + mp->is_add); + + BAD_SW_IF_INDEX_LABEL; + +send_reply: + REPLY_MACRO (VL_API_NAT64_ADD_DEL_INTERFACE_REPLY); +} + +static void * +vl_api_nat64_add_del_interface_t_print (vl_api_nat64_add_del_interface_t * mp, + void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat64_add_del_interface "); + s = format (s, "sw_if_index %d %s %s", + clib_host_to_net_u32 (mp->sw_if_index), + mp->is_inside ? "in" : "out", mp->is_add ? "" : "del"); + + FINISH; +} + +static int +nat64_api_interface_walk (snat_interface_t * i, void *arg) +{ + vl_api_nat64_interface_details_t *rmp; + snat_main_t *sm = &snat_main; + nat64_api_walk_ctx_t *ctx = arg; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_NAT64_INTERFACE_DETAILS + sm->msg_id_base); + rmp->sw_if_index = ntohl (i->sw_if_index); + rmp->is_inside = i->is_inside; + rmp->context = ctx->context; + + vl_msg_api_send_shmem (ctx->q, (u8 *) & rmp); + + return 0; +} + +static void +vl_api_nat64_interface_dump_t_handler (vl_api_nat64_interface_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + nat64_main_t *nm = &nat64_main; + + if (nm->is_disabled) + return; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + nat64_api_walk_ctx_t ctx = { + .q = q, + .context = mp->context, + }; + + nat64_interfaces_walk (nat64_api_interface_walk, &ctx); +} + +static void * +vl_api_nat64_interface_dump_t_print (vl_api_nat64_interface_dump_t * mp, + void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_interface_dump "); + + FINISH; +} + +static void + vl_api_nat64_add_del_static_bib_t_handler + (vl_api_nat64_add_del_static_bib_t * mp) +{ + snat_main_t *sm = &snat_main; + nat64_main_t *nm = &nat64_main; + vl_api_nat64_add_del_static_bib_reply_t *rmp; + ip6_address_t in_addr; + ip4_address_t out_addr; + int rv = 0; + + if (nm->is_disabled) + { + rv = VNET_API_ERROR_FEATURE_DISABLED; + goto send_reply; + } + + memcpy (&in_addr.as_u8, mp->i_addr, 16); + memcpy (&out_addr.as_u8, mp->o_addr, 4); + + rv = + nat64_add_del_static_bib_entry (&in_addr, &out_addr, + clib_net_to_host_u16 (mp->i_port), + clib_net_to_host_u16 (mp->o_port), + mp->proto, + clib_net_to_host_u32 (mp->vrf_id), + mp->is_add); + +send_reply: + REPLY_MACRO (VL_API_NAT64_ADD_DEL_STATIC_BIB_REPLY); +} + +static void *vl_api_nat64_add_del_static_bib_t_print + (vl_api_nat64_add_del_static_bib_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat64_add_del_static_bib "); + s = format (s, "protocol %d i_addr %U o_addr %U ", + mp->proto, + format_ip6_address, mp->i_addr, format_ip4_address, mp->o_addr); + + if (mp->vrf_id != ~0) + s = format (s, "vrf %d", clib_net_to_host_u32 (mp->vrf_id)); + + FINISH; +} + +static int +nat64_api_bib_walk (nat64_db_bib_entry_t * bibe, void *arg) +{ + vl_api_nat64_bib_details_t *rmp; + snat_main_t *sm = &snat_main; + nat64_api_walk_ctx_t *ctx = arg; + fib_table_t *fib; + + fib = fib_table_get (bibe->fib_index, FIB_PROTOCOL_IP6); + if (!fib) + return -1; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_NAT64_BIB_DETAILS + sm->msg_id_base); + rmp->context = ctx->context; + clib_memcpy (rmp->i_addr, &(bibe->in_addr), 16); + clib_memcpy (rmp->o_addr, &(bibe->out_addr), 4); + rmp->i_port = bibe->in_port; + rmp->o_port = bibe->out_port; + rmp->vrf_id = ntohl (fib->ft_table_id); + rmp->proto = bibe->proto; + rmp->is_static = bibe->is_static; + rmp->ses_num = ntohl (bibe->ses_num); + + vl_msg_api_send_shmem (ctx->q, (u8 *) & rmp); + + return 0; +} + +static void +vl_api_nat64_bib_dump_t_handler (vl_api_nat64_bib_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + nat64_main_t *nm = &nat64_main; + + if (nm->is_disabled) + return; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + nat64_api_walk_ctx_t ctx = { + .q = q, + .context = mp->context, + }; + + nat64_db_bib_walk (&nm->db, mp->proto, nat64_api_bib_walk, &ctx); +} + +static void * +vl_api_nat64_bib_dump_t_print (vl_api_nat64_bib_dump_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_bib_dump protocol %d", mp->proto); + + FINISH; +} + +static void +vl_api_nat64_set_timeouts_t_handler (vl_api_nat64_set_timeouts_t * mp) +{ + snat_main_t *sm = &snat_main; + nat64_main_t *nm = &nat64_main; + vl_api_nat64_set_timeouts_reply_t *rmp; + int rv = 0; + + if (nm->is_disabled) + { + rv = VNET_API_ERROR_FEATURE_DISABLED; + goto send_reply; + } + + rv = nat64_set_icmp_timeout (ntohl (mp->icmp)); + if (rv) + goto send_reply; + rv = nat64_set_udp_timeout (ntohl (mp->udp)); + if (rv) + goto send_reply; + rv = + nat64_set_tcp_timeouts (ntohl (mp->tcp_trans), ntohl (mp->tcp_est), + ntohl (mp->tcp_incoming_syn)); + +send_reply: + REPLY_MACRO (VL_API_NAT64_SET_TIMEOUTS_REPLY); +} + +static void *vl_api_nat64_set_timeouts_t_print + (vl_api_nat64_set_timeouts_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat64_set_timeouts "); + s = + format (s, + "udp %d icmp %d, tcp_trans %d, tcp_est %d, tcp_incoming_syn %d\n", + ntohl (mp->udp), ntohl (mp->icmp), ntohl (mp->tcp_trans), + ntohl (mp->tcp_est), ntohl (mp->tcp_incoming_syn)); + + FINISH; +} + +static void +vl_api_nat64_get_timeouts_t_handler (vl_api_nat64_get_timeouts_t * mp) +{ + snat_main_t *sm = &snat_main; + nat64_main_t *nm = &nat64_main; + vl_api_nat64_get_timeouts_reply_t *rmp; + int rv = 0; + + if (nm->is_disabled) + return; + + /* *INDENT-OFF* */ + REPLY_MACRO2 (VL_API_NAT64_GET_TIMEOUTS_REPLY, + ({ + rmp->udp = htonl (nat64_get_udp_timeout()); + rmp->icmp = htonl (nat64_get_icmp_timeout()); + rmp->tcp_trans = htonl (nat64_get_tcp_trans_timeout()); + rmp->tcp_est = htonl (nat64_get_tcp_est_timeout()); + rmp->tcp_incoming_syn = htonl (nat64_get_tcp_incoming_syn_timeout()); + })) + /* *INDENT-ON* */ +} + +static void *vl_api_nat64_get_timeouts_t_print + (vl_api_nat64_get_timeouts_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat64_get_timeouts"); + + FINISH; +} + +static int +nat64_api_st_walk (nat64_db_st_entry_t * ste, void *arg) +{ + vl_api_nat64_st_details_t *rmp; + snat_main_t *sm = &snat_main; + nat64_api_walk_ctx_t *ctx = arg; + nat64_main_t *nm = &nat64_main; + nat64_db_bib_entry_t *bibe; + fib_table_t *fib; + + bibe = nat64_db_bib_entry_by_index (&nm->db, ste->proto, ste->bibe_index); + if (!bibe) + return -1; + + fib = fib_table_get (bibe->fib_index, FIB_PROTOCOL_IP6); + if (!fib) + return -1; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_NAT64_ST_DETAILS + sm->msg_id_base); + rmp->context = ctx->context; + clib_memcpy (rmp->il_addr, &(bibe->in_addr), 16); + clib_memcpy (rmp->ol_addr, &(bibe->out_addr), 4); + rmp->il_port = bibe->in_port; + rmp->ol_port = bibe->out_port; + clib_memcpy (rmp->ir_addr, &(ste->in_r_addr), 16); + clib_memcpy (rmp->or_addr, &(ste->out_r_addr), 4); + rmp->il_port = ste->r_port; + rmp->vrf_id = ntohl (fib->ft_table_id); + rmp->proto = ste->proto; + + vl_msg_api_send_shmem (ctx->q, (u8 *) & rmp); + + return 0; +} + +static void +vl_api_nat64_st_dump_t_handler (vl_api_nat64_st_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + nat64_main_t *nm = &nat64_main; + + if (nm->is_disabled) + return; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + nat64_api_walk_ctx_t ctx = { + .q = q, + .context = mp->context, + }; + + nat64_db_st_walk (&nm->db, mp->proto, nat64_api_st_walk, &ctx); +} + +static void * +vl_api_nat64_st_dump_t_print (vl_api_nat64_st_dump_t * mp, void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: snat_st_dump protocol %d", mp->proto); + + FINISH; +} + +static void +vl_api_nat64_add_del_prefix_t_handler (vl_api_nat64_add_del_prefix_t * mp) +{ + vl_api_nat64_add_del_prefix_reply_t *rmp; + snat_main_t *sm = &snat_main; + nat64_main_t *nm = &nat64_main; + ip6_address_t prefix; + int rv = 0; + + if (nm->is_disabled) + { + rv = VNET_API_ERROR_FEATURE_DISABLED; + goto send_reply; + } + + memcpy (&prefix.as_u8, mp->prefix, 16); + + rv = + nat64_add_del_prefix (&prefix, mp->prefix_len, + clib_net_to_host_u32 (mp->vrf_id), mp->is_add); +send_reply: + REPLY_MACRO (VL_API_NAT64_ADD_DEL_PREFIX_REPLY); +} + +static void * +vl_api_nat64_add_del_prefix_t_print (vl_api_nat64_add_del_prefix_t * mp, + void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat64_add_del_prefix %U/%u vrf_id %u %s\n", + format_ip6_address, mp->prefix, mp->prefix_len, + ntohl (mp->vrf_id), mp->is_add ? "" : "del"); + + FINISH; +} + +static int +nat64_api_prefix_walk (nat64_prefix_t * p, void *arg) +{ + vl_api_nat64_prefix_details_t *rmp; + snat_main_t *sm = &snat_main; + nat64_api_walk_ctx_t *ctx = arg; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = ntohs (VL_API_NAT64_PREFIX_DETAILS + sm->msg_id_base); + clib_memcpy (rmp->prefix, &(p->prefix), 16); + rmp->prefix_len = p->plen; + rmp->vrf_id = ntohl (p->vrf_id); + rmp->context = ctx->context; + + vl_msg_api_send_shmem (ctx->q, (u8 *) & rmp); + + return 0; +} + +static void +vl_api_nat64_prefix_dump_t_handler (vl_api_nat64_prefix_dump_t * mp) +{ + unix_shared_memory_queue_t *q; + nat64_main_t *nm = &nat64_main; + + if (nm->is_disabled) + return; + + q = vl_api_client_index_to_input_queue (mp->client_index); + if (q == 0) + return; + + nat64_api_walk_ctx_t ctx = { + .q = q, + .context = mp->context, + }; + + nat64_prefix_walk (nat64_api_prefix_walk, &ctx); +} + +static void * +vl_api_nat64_prefix_dump_t_print (vl_api_nat64_prefix_dump_t * mp, + void *handle) +{ + u8 *s; + + s = format (0, "SCRIPT: nat64_prefix_dump\n"); + + FINISH; +} + +/* List of message types that this plugin understands */ +#define foreach_snat_plugin_api_msg \ +_(SNAT_ADD_ADDRESS_RANGE, snat_add_address_range) \ +_(SNAT_INTERFACE_ADD_DEL_FEATURE, snat_interface_add_del_feature) \ +_(SNAT_ADD_STATIC_MAPPING, snat_add_static_mapping) \ +_(SNAT_CONTROL_PING, snat_control_ping) \ +_(SNAT_STATIC_MAPPING_DUMP, snat_static_mapping_dump) \ +_(SNAT_SHOW_CONFIG, snat_show_config) \ +_(SNAT_ADDRESS_DUMP, snat_address_dump) \ +_(SNAT_INTERFACE_DUMP, snat_interface_dump) \ +_(SNAT_SET_WORKERS, snat_set_workers) \ +_(SNAT_WORKER_DUMP, snat_worker_dump) \ +_(SNAT_ADD_DEL_INTERFACE_ADDR, snat_add_del_interface_addr) \ +_(SNAT_INTERFACE_ADDR_DUMP, snat_interface_addr_dump) \ +_(SNAT_IPFIX_ENABLE_DISABLE, snat_ipfix_enable_disable) \ +_(SNAT_USER_DUMP, snat_user_dump) \ +_(SNAT_USER_SESSION_DUMP, snat_user_session_dump) \ +_(SNAT_INTERFACE_ADD_DEL_OUTPUT_FEATURE, \ + snat_interface_add_del_output_feature) \ +_(SNAT_INTERFACE_OUTPUT_FEATURE_DUMP, \ + snat_interface_output_feature_dump) \ +_(SNAT_ADD_DET_MAP, snat_add_det_map) \ +_(SNAT_DET_FORWARD, snat_det_forward) \ +_(SNAT_DET_REVERSE, snat_det_reverse) \ +_(SNAT_DET_MAP_DUMP, snat_det_map_dump) \ +_(SNAT_DET_SET_TIMEOUTS, snat_det_set_timeouts) \ +_(SNAT_DET_GET_TIMEOUTS, snat_det_get_timeouts) \ +_(SNAT_DET_CLOSE_SESSION_OUT, snat_det_close_session_out) \ +_(SNAT_DET_CLOSE_SESSION_IN, snat_det_close_session_in) \ +_(SNAT_DET_SESSION_DUMP, snat_det_session_dump) \ +_(NAT_CONTROL_PING, nat_control_ping) \ +_(NAT_SHOW_CONFIG, nat_show_config) \ +_(NAT_SET_WORKERS, nat_set_workers) \ +_(NAT_WORKER_DUMP, nat_worker_dump) \ +_(NAT_IPFIX_ENABLE_DISABLE, nat_ipfix_enable_disable) \ +_(NAT44_ADD_DEL_ADDRESS_RANGE, nat44_add_del_address_range) \ +_(NAT44_INTERFACE_ADD_DEL_FEATURE, nat44_interface_add_del_feature) \ +_(NAT44_ADD_DEL_STATIC_MAPPING, nat44_add_del_static_mapping) \ +_(NAT44_STATIC_MAPPING_DUMP, nat44_static_mapping_dump) \ +_(NAT44_ADDRESS_DUMP, nat44_address_dump) \ +_(NAT44_INTERFACE_DUMP, nat44_interface_dump) \ +_(NAT44_ADD_DEL_INTERFACE_ADDR, nat44_add_del_interface_addr) \ +_(NAT44_INTERFACE_ADDR_DUMP, nat44_interface_addr_dump) \ +_(NAT44_USER_DUMP, nat44_user_dump) \ +_(NAT44_USER_SESSION_DUMP, nat44_user_session_dump) \ +_(NAT44_INTERFACE_ADD_DEL_OUTPUT_FEATURE, \ + nat44_interface_add_del_output_feature) \ +_(NAT44_INTERFACE_OUTPUT_FEATURE_DUMP, \ + nat44_interface_output_feature_dump) \ +_(NAT_DET_ADD_DEL_MAP, nat_det_add_del_map) \ +_(NAT_DET_FORWARD, nat_det_forward) \ +_(NAT_DET_REVERSE, nat_det_reverse) \ +_(NAT_DET_MAP_DUMP, nat_det_map_dump) \ +_(NAT_DET_SET_TIMEOUTS, nat_det_set_timeouts) \ +_(NAT_DET_GET_TIMEOUTS, nat_det_get_timeouts) \ +_(NAT_DET_CLOSE_SESSION_OUT, nat_det_close_session_out) \ +_(NAT_DET_CLOSE_SESSION_IN, nat_det_close_session_in) \ +_(NAT_DET_SESSION_DUMP, nat_det_session_dump) \ +_(NAT64_ADD_DEL_POOL_ADDR_RANGE, nat64_add_del_pool_addr_range) \ +_(NAT64_POOL_ADDR_DUMP, nat64_pool_addr_dump) \ +_(NAT64_ADD_DEL_INTERFACE, nat64_add_del_interface) \ +_(NAT64_INTERFACE_DUMP, nat64_interface_dump) \ +_(NAT64_ADD_DEL_STATIC_BIB, nat64_add_del_static_bib) \ +_(NAT64_BIB_DUMP, nat64_bib_dump) \ +_(NAT64_SET_TIMEOUTS, nat64_set_timeouts) \ +_(NAT64_GET_TIMEOUTS, nat64_get_timeouts) \ +_(NAT64_ST_DUMP, nat64_st_dump) \ +_(NAT64_ADD_DEL_PREFIX, nat64_add_del_prefix) \ +_(NAT64_PREFIX_DUMP, nat64_prefix_dump) + +/* Set up the API message handling tables */ +static clib_error_t * +snat_plugin_api_hookup (vlib_main_t * vm) +{ + snat_main_t *sm __attribute__ ((unused)) = &snat_main; +#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_snat_plugin_api_msg; +#undef _ + + return 0; +} + +#define vl_msg_name_crc_list +#include +#undef vl_msg_name_crc_list + +static void +setup_message_id_table (snat_main_t * sm, api_main_t * am) +{ +#define _(id,n,crc) \ + vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + sm->msg_id_base); + foreach_vl_msg_name_crc_nat; +#undef _ +} + +static void +plugin_custom_dump_configure (snat_main_t * sm) +{ +#define _(n,f) sm->api_main->msg_print_handlers \ + [VL_API_##n + sm->msg_id_base] \ + = (void *) vl_api_##f##_t_print; + foreach_snat_plugin_api_msg; +#undef _ +} + +clib_error_t * +snat_api_init (vlib_main_t * vm, snat_main_t * sm) +{ + u8 *name; + clib_error_t *error = 0; + + name = format (0, "snat_%08x%c", api_version, 0); + + /* Ask for a correctly-sized block of API message decode slots */ + sm->msg_id_base = + vl_msg_api_get_msg_ids ((char *) name, VL_MSG_FIRST_AVAILABLE); + + error = snat_plugin_api_hookup (vm); + + /* Add our API messages to the global name_crc hash table */ + setup_message_id_table (sm, sm->api_main); + + plugin_custom_dump_configure (sm); + + vec_free (name); + + return error; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat_det.c b/src/plugins/nat/nat_det.c new file mode 100644 index 00000000..3af6698c --- /dev/null +++ b/src/plugins/nat/nat_det.c @@ -0,0 +1,158 @@ +/* + * snat_det.c - deterministic NAT + * + * 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. + */ +/** + * @file + * @brief deterministic NAT + */ + +#include + + +/** + * @brief Add/delete deterministic NAT mapping. + * + * Create bijective mapping of inside address to outside address and port range + * pairs, with the purpose of enabling deterministic NAT to reduce logging in + * CGN deployments. + * + * @param sm SNAT main. + * @param in_addr Inside network address. + * @param in_plen Inside network prefix length. + * @param out_addr Outside network address. + * @param out_plen Outside network prefix length. + * @param is_add If 0 delete, otherwise add. + */ +int +snat_det_add_map (snat_main_t * sm, ip4_address_t * in_addr, u8 in_plen, + ip4_address_t * out_addr, u8 out_plen, int is_add) +{ + snat_det_map_t *det_map; + static snat_det_session_t empty_snat_det_session = { 0 }; + snat_interface_t *i; + ip4_address_t in_cmp, out_cmp; + u8 found = 0; + + in_cmp.as_u32 = in_addr->as_u32 & ip4_main.fib_masks[in_plen]; + out_cmp.as_u32 = out_addr->as_u32 & ip4_main.fib_masks[out_plen]; + vec_foreach (det_map, sm->det_maps) + { + /* Checking for overlapping addresses to be added here */ + if (det_map->in_addr.as_u32 == in_cmp.as_u32 && + det_map->in_plen == in_plen && + det_map->out_addr.as_u32 == out_cmp.as_u32 && + det_map->out_plen == out_plen) + { + found = 1; + break; + } + } + + /* If found, don't add again */ + if (found && is_add) + return VNET_API_ERROR_VALUE_EXIST; + + /* If not found, don't delete */ + if (!found && !is_add) + return VNET_API_ERROR_NO_SUCH_ENTRY; + + if (is_add) + { + pool_get (sm->det_maps, det_map); + memset (det_map, 0, sizeof (*det_map)); + det_map->in_addr.as_u32 = in_cmp.as_u32; + det_map->in_plen = in_plen; + det_map->out_addr.as_u32 = out_cmp.as_u32; + det_map->out_plen = out_plen; + det_map->sharing_ratio = (1 << (32 - in_plen)) / (1 << (32 - out_plen)); + det_map->ports_per_host = (65535 - 1023) / det_map->sharing_ratio; + + vec_validate_init_empty (det_map->sessions, + SNAT_DET_SES_PER_USER * (1 << (32 - in_plen)) - + 1, empty_snat_det_session); + } + else + { + vec_free (det_map->sessions); + vec_del1 (sm->det_maps, det_map - sm->det_maps); + } + + /* Add/del external address range to FIB */ + /* *INDENT-OFF* */ + pool_foreach (i, sm->interfaces, + ({ + if (i->is_inside) + continue; + + snat_add_del_addr_to_fib(out_addr, out_plen, i->sw_if_index, is_add); + break; + })); + /* *INDENT-ON* */ + return 0; +} + +/** + * @brief The 'nat-det-expire-walk' process's main loop. + * + * Check expire time for active sessions. + */ +static uword +snat_det_expire_walk_fn (vlib_main_t * vm, vlib_node_runtime_t * rt, + vlib_frame_t * f) +{ + snat_main_t *sm = &snat_main; + snat_det_map_t *dm; + snat_det_session_t *ses; + + while (sm->deterministic) + { + vlib_process_wait_for_event_or_clock (vm, 10.0); + vlib_process_get_events (vm, NULL); + u32 now = (u32) vlib_time_now (vm); + /* *INDENT-OFF* */ + pool_foreach (dm, sm->det_maps, + ({ + vec_foreach(ses, dm->sessions) + { + /* Delete if session expired */ + if (ses->in_port && (ses->expire < now)) + snat_det_ses_close (dm, ses); + } + })); + /* *INDENT-ON* */ + } + + return 0; +} + +static vlib_node_registration_t snat_det_expire_walk_node; + +/* *INDENT-OFF* */ +VLIB_REGISTER_NODE (snat_det_expire_walk_node, static) = { + .function = snat_det_expire_walk_fn, + .type = VLIB_NODE_TYPE_PROCESS, + .name = + "nat-det-expire-walk", +}; +/* *INDENT-ON* */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat_det.h b/src/plugins/nat/nat_det.h new file mode 100644 index 00000000..2ab7f27e --- /dev/null +++ b/src/plugins/nat/nat_det.h @@ -0,0 +1,196 @@ +/* + * snat_det.h - deterministic NAT definitions + * + * 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. + */ +/** + * @file + * @brief deterministic NAT definitions + */ + +#ifndef __included_nat_det_h__ +#define __included_nat_det_h__ + +#include +#include +#include + + +#define SNAT_DET_SES_PER_USER 1000 + + +int snat_det_add_map (snat_main_t * sm, ip4_address_t * in_addr, u8 in_plen, + ip4_address_t * out_addr, u8 out_plen, int is_add); + +always_inline int +is_addr_in_net (ip4_address_t * addr, ip4_address_t * net, u8 plen) +{ + if (net->as_u32 == (addr->as_u32 & ip4_main.fib_masks[plen])) + return 1; + return 0; +} + +always_inline snat_det_map_t * +snat_det_map_by_user (snat_main_t * sm, ip4_address_t * user_addr) +{ + snat_det_map_t *dm; + + /* *INDENT-OFF* */ + pool_foreach (dm, sm->det_maps, + ({ + if (is_addr_in_net(user_addr, &dm->in_addr, dm->in_plen)) + return dm; + })); + /* *INDENT-ON* */ + return 0; +} + +always_inline snat_det_map_t * +snat_det_map_by_out (snat_main_t * sm, ip4_address_t * out_addr) +{ + snat_det_map_t *dm; + + /* *INDENT-OFF* */ + pool_foreach (dm, sm->det_maps, + ({ + if (is_addr_in_net(out_addr, &dm->out_addr, dm->out_plen)) + return dm; + })); + /* *INDENT-ON* */ + return 0; +} + +always_inline void +snat_det_forward (snat_det_map_t * dm, ip4_address_t * in_addr, + ip4_address_t * out_addr, u16 * lo_port) +{ + u32 in_offset, out_offset; + + in_offset = clib_net_to_host_u32 (in_addr->as_u32) - + clib_net_to_host_u32 (dm->in_addr.as_u32); + out_offset = in_offset / dm->sharing_ratio; + out_addr->as_u32 = + clib_host_to_net_u32 (clib_net_to_host_u32 (dm->out_addr.as_u32) + + out_offset); + *lo_port = 1024 + dm->ports_per_host * (in_offset % dm->sharing_ratio); +} + +always_inline void +snat_det_reverse (snat_det_map_t * dm, ip4_address_t * out_addr, u16 out_port, + ip4_address_t * in_addr) +{ + u32 in_offset1, in_offset2, out_offset; + + out_offset = clib_net_to_host_u32 (out_addr->as_u32) - + clib_net_to_host_u32 (dm->out_addr.as_u32); + in_offset1 = out_offset * dm->sharing_ratio; + in_offset2 = (out_port - 1024) / dm->ports_per_host; + in_addr->as_u32 = + clib_host_to_net_u32 (clib_net_to_host_u32 (dm->in_addr.as_u32) + + in_offset1 + in_offset2); +} + +always_inline u32 +snat_det_user_ses_offset (ip4_address_t * addr, u8 plen) +{ + return (clib_net_to_host_u32 (addr->as_u32) & pow2_mask (32 - plen)) * + SNAT_DET_SES_PER_USER; +} + +always_inline snat_det_session_t * +snat_det_get_ses_by_out (snat_det_map_t * dm, ip4_address_t * in_addr, + u64 out_key) +{ + u32 user_offset; + u16 i; + + user_offset = snat_det_user_ses_offset (in_addr, dm->in_plen); + for (i = 0; i < SNAT_DET_SES_PER_USER; i++) + { + if (dm->sessions[i + user_offset].out.as_u64 == out_key) + return &dm->sessions[i + user_offset]; + } + + return 0; +} + +always_inline snat_det_session_t * +snat_det_find_ses_by_in (snat_det_map_t * dm, ip4_address_t * in_addr, + u16 in_port, snat_det_out_key_t out_key) +{ + snat_det_session_t *ses; + u32 user_offset; + u16 i; + + user_offset = snat_det_user_ses_offset (in_addr, dm->in_plen); + for (i = 0; i < SNAT_DET_SES_PER_USER; i++) + { + ses = &dm->sessions[i + user_offset]; + if (ses->in_port == in_port && + ses->out.ext_host_addr.as_u32 == out_key.ext_host_addr.as_u32 && + ses->out.ext_host_port == out_key.ext_host_port) + return &dm->sessions[i + user_offset]; + } + + return 0; +} + +always_inline snat_det_session_t * +snat_det_ses_create (snat_det_map_t * dm, ip4_address_t * in_addr, + u16 in_port, snat_det_out_key_t * out) +{ + u32 user_offset; + u16 i; + + user_offset = snat_det_user_ses_offset (in_addr, dm->in_plen); + + for (i = 0; i < SNAT_DET_SES_PER_USER; i++) + { + if (!dm->sessions[i + user_offset].in_port) + { + if (__sync_bool_compare_and_swap + (&dm->sessions[i + user_offset].in_port, 0, in_port)) + { + dm->sessions[i + user_offset].out.as_u64 = out->as_u64; + dm->sessions[i + user_offset].state = SNAT_SESSION_UNKNOWN; + dm->sessions[i + user_offset].expire = 0; + __sync_add_and_fetch (&dm->ses_num, 1); + return &dm->sessions[i + user_offset]; + } + } + } + + snat_ipfix_logging_max_entries_per_user (in_addr->as_u32); + return 0; +} + +always_inline void +snat_det_ses_close (snat_det_map_t * dm, snat_det_session_t * ses) +{ + if (__sync_bool_compare_and_swap (&ses->in_port, ses->in_port, 0)) + { + ses->out.as_u64 = 0; + __sync_add_and_fetch (&dm->ses_num, -1); + } +} + +#endif /* __included_nat_det_h__ */ + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "gnu") + * End: + */ diff --git a/src/plugins/nat/nat_ipfix_logging.c b/src/plugins/nat/nat_ipfix_logging.c new file mode 100644 index 00000000..0cc0f82a --- /dev/null +++ b/src/plugins/nat/nat_ipfix_logging.c @@ -0,0 +1,835 @@ +/* + * nat_ipfix_logging.c - NAT Events IPFIX logging + * + * 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 +#include +#include +#include + +snat_ipfix_logging_main_t snat_ipfix_logging_main; + +#define NAT44_SESSION_CREATE_LEN 26 +#define NAT_ADDRESSES_EXHAUTED_LEN 13 +#define MAX_ENTRIES_PER_USER_LEN 17 + +#define NAT44_SESSION_CREATE_FIELD_COUNT 8 +#define NAT_ADDRESSES_EXHAUTED_FIELD_COUNT 3 +#define MAX_ENTRIES_PER_USER_FIELD_COUNT 4 + +typedef struct { + u8 nat_event; + u32 src_ip; + u32 nat_src_ip; + snat_protocol_t snat_proto; + u16 src_port; + u16 nat_src_port; + u32 vrf_id; +} snat_ipfix_logging_nat44_ses_args_t; + +typedef struct { + u32 pool_id; +} snat_ipfix_logging_addr_exhausted_args_t; + +typedef struct { + u32 src_ip; +} snat_ipfix_logging_max_entries_per_user_args_t; + +/** + * @brief Create an IPFIX template packet rewrite string + * + * @param frm flow report main + * @param fr flow report + * @param collector_address collector address + * @param src_address source address + * @param collector_port collector + * @param event NAT event ID + * @param quota_event NAT quota exceeded event ID + * + * @returns template packet + */ +static inline u8 * +snat_template_rewrite (flow_report_main_t * frm, + flow_report_t * fr, + ip4_address_t * collector_address, + ip4_address_t * src_address, + u16 collector_port, + nat_event_t event, + quota_exceed_event_t quota_event) +{ + snat_ipfix_logging_main_t *silm = &snat_ipfix_logging_main; + ip4_header_t *ip; + udp_header_t *udp; + ipfix_message_header_t *h; + ipfix_set_header_t *s; + ipfix_template_header_t *t; + ipfix_field_specifier_t *f; + ipfix_field_specifier_t *first_field; + u8 *rewrite = 0; + ip4_ipfix_template_packet_t *tp; + u32 field_count = 0; + flow_report_stream_t *stream; + + stream = &frm->streams[fr->stream_index]; + silm->stream_index = fr->stream_index; + + if (event == NAT_ADDRESSES_EXHAUTED) + { + field_count = NAT_ADDRESSES_EXHAUTED_FIELD_COUNT; + silm->addr_exhausted_template_id = fr->template_id; + } + else if (event == NAT44_SESSION_CREATE) + { + field_count = NAT44_SESSION_CREATE_FIELD_COUNT; + silm->nat44_session_template_id = fr->template_id; + } + else if (event == QUOTA_EXCEEDED) + { + if (quota_event == MAX_ENTRIES_PER_USER) + { + field_count = MAX_ENTRIES_PER_USER_FIELD_COUNT; + silm->max_entries_per_user_template_id = fr->template_id; + } + } + + /* allocate rewrite space */ + vec_validate_aligned (rewrite, + sizeof (ip4_ipfix_template_packet_t) + + field_count * sizeof (ipfix_field_specifier_t) - 1, + CLIB_CACHE_LINE_BYTES); + + tp = (ip4_ipfix_template_packet_t *) rewrite; + ip = (ip4_header_t *) & tp->ip4; + udp = (udp_header_t *) (ip + 1); + h = (ipfix_message_header_t *) (udp + 1); + s = (ipfix_set_header_t *) (h + 1); + t = (ipfix_template_header_t *) (s + 1); + first_field = f = (ipfix_field_specifier_t *) (t + 1); + + ip->ip_version_and_header_length = 0x45; + ip->ttl = 254; + ip->protocol = IP_PROTOCOL_UDP; + ip->src_address.as_u32 = src_address->as_u32; + ip->dst_address.as_u32 = collector_address->as_u32; + udp->src_port = clib_host_to_net_u16 (stream->src_port); + udp->dst_port = clib_host_to_net_u16 (collector_port); + udp->length = clib_host_to_net_u16 (vec_len (rewrite) - sizeof (*ip)); + + /* FIXUP: message header export_time */ + h->domain_id = clib_host_to_net_u32 (stream->domain_id); + + /* Add TLVs to the template */ + if (event == NAT_ADDRESSES_EXHAUTED) + { + f->e_id_length = ipfix_e_id_length (0, observationTimeMilliseconds, 8); + f++; + f->e_id_length = ipfix_e_id_length (0, natEvent, 1); + f++; + f->e_id_length = ipfix_e_id_length (0, natPoolId, 4); + f++; + } + else if (event == NAT44_SESSION_CREATE) + { + f->e_id_length = ipfix_e_id_length (0, observationTimeMilliseconds, 8); + f++; + f->e_id_length = ipfix_e_id_length (0, natEvent, 1); + f++; + f->e_id_length = ipfix_e_id_length (0, sourceIPv4Address, 4); + f++; + f->e_id_length = ipfix_e_id_length (0, postNATSourceIPv4Address, 4); + f++; + f->e_id_length = ipfix_e_id_length (0, protocolIdentifier, 1); + f++; + f->e_id_length = ipfix_e_id_length (0, sourceTransportPort, 2); + f++; + f->e_id_length = ipfix_e_id_length (0, postNAPTSourceTransportPort, 2); + f++; + f->e_id_length = ipfix_e_id_length (0, ingressVRFID, 4); + f++; + } + else if (event == QUOTA_EXCEEDED) + { + if (quota_event == MAX_ENTRIES_PER_USER) + { + f->e_id_length = ipfix_e_id_length (0, observationTimeMilliseconds, + 8); + f++; + f->e_id_length = ipfix_e_id_length (0, natEvent, 1); + f++; + f->e_id_length = ipfix_e_id_length (0, natQuotaExceededEvent, 4); + f++; + f->e_id_length = ipfix_e_id_length (0, sourceIPv4Address, 4); + f++; + } + } + + /* Back to the template packet... */ + ip = (ip4_header_t *) & tp->ip4; + udp = (udp_header_t *) (ip + 1); + + ASSERT (f - first_field); + /* Field count in this template */ + t->id_count = ipfix_id_count (fr->template_id, f - first_field); + + /* set length in octets */ + s->set_id_length = + ipfix_set_id_length (2 /* set_id */ , (u8 *) f - (u8 *) s); + + /* message length in octets */ + h->version_length = version_length ((u8 *) f - (u8 *) h); + + ip->length = clib_host_to_net_u16 ((u8 *) f - (u8 *) ip); + ip->checksum = ip4_header_checksum (ip); + + return rewrite; +} + +u8 * +snat_template_rewrite_addr_exhausted (flow_report_main_t * frm, + flow_report_t * fr, + ip4_address_t * collector_address, + ip4_address_t * src_address, + u16 collector_port) +{ + return snat_template_rewrite (frm, fr, collector_address, src_address, + collector_port, NAT_ADDRESSES_EXHAUTED, 0); +} + +u8 * +snat_template_rewrite_nat44_session (flow_report_main_t * frm, + flow_report_t * fr, + ip4_address_t * collector_address, + ip4_address_t * src_address, + u16 collector_port) +{ + return snat_template_rewrite (frm, fr, collector_address, src_address, + collector_port, NAT44_SESSION_CREATE, 0); +} + +u8 * +snat_template_rewrite_max_entries_per_usr (flow_report_main_t * frm, + flow_report_t * fr, + ip4_address_t * collector_address, + ip4_address_t * src_address, + u16 collector_port) +{ + return snat_template_rewrite (frm, fr, collector_address, src_address, + collector_port, QUOTA_EXCEEDED, + MAX_ENTRIES_PER_USER); +} + +static inline void +snat_ipfix_header_create (flow_report_main_t * frm, + vlib_buffer_t * b0, + u32 * offset) +{ + snat_ipfix_logging_main_t *silm = &snat_ipfix_logging_main; + flow_report_stream_t *stream; + ip4_ipfix_template_packet_t * tp; + ipfix_message_header_t * h = 0; + ipfix_set_header_t * s = 0; + ip4_header_t * ip; + udp_header_t * udp; + + stream = &frm->streams[silm->stream_index]; + + b0->current_data = 0; + b0->current_length = sizeof (*ip) + sizeof (*udp) + sizeof (*h) + + sizeof (*s); + b0->flags |= (VLIB_BUFFER_TOTAL_LENGTH_VALID | VLIB_BUFFER_FLOW_REPORT); + vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0; + vnet_buffer (b0)->sw_if_index[VLIB_TX] = frm->fib_index; + tp = vlib_buffer_get_current (b0); + ip = (ip4_header_t *) &tp->ip4; + udp = (udp_header_t *) (ip+1); + h = (ipfix_message_header_t *)(udp+1); + s = (ipfix_set_header_t *)(h+1); + + ip->ip_version_and_header_length = 0x45; + ip->ttl = 254; + ip->protocol = IP_PROTOCOL_UDP; + ip->flags_and_fragment_offset = 0; + ip->src_address.as_u32 = frm->src_address.as_u32; + ip->dst_address.as_u32 = frm->ipfix_collector.as_u32; + udp->src_port = clib_host_to_net_u16 (stream->src_port); + udp->dst_port = clib_host_to_net_u16 (frm->collector_port); + udp->checksum = 0; + + h->export_time = clib_host_to_net_u32 ( + (u32) (((f64)frm->unix_time_0) + (vlib_time_now(frm->vlib_main) - + frm->vlib_time_0))); + h->sequence_number = clib_host_to_net_u32 (stream->sequence_number++); + h->domain_id = clib_host_to_net_u32 (stream->domain_id); + + *offset = (u32) (((u8 *)(s+1)) - (u8 *)tp); +} + +static inline void +snat_ipfix_send (flow_report_main_t * frm, + vlib_frame_t * f, + vlib_buffer_t * b0, + u16 template_id) +{ + ip4_ipfix_template_packet_t * tp; + ipfix_message_header_t * h = 0; + ipfix_set_header_t * s = 0; + ip4_header_t * ip; + udp_header_t * udp; + vlib_main_t * vm = frm->vlib_main; + + tp = vlib_buffer_get_current (b0); + ip = (ip4_header_t *) & tp->ip4; + udp = (udp_header_t *) (ip + 1); + h = (ipfix_message_header_t *) (udp + 1); + s = (ipfix_set_header_t *) (h + 1); + + s->set_id_length = ipfix_set_id_length (template_id, + b0->current_length - + (sizeof (*ip) + sizeof (*udp) + + sizeof (*h))); + h->version_length = version_length (b0->current_length - + (sizeof (*ip) + sizeof (*udp))); + + ip->length = clib_host_to_net_u16 (b0->current_length); + ip->checksum = ip4_header_checksum (ip); + udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip)); + + if (frm->udp_checksum) + { + udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip); + if (udp->checksum == 0) + udp->checksum = 0xffff; + } + + ASSERT (ip->checksum == ip4_header_checksum (ip)); + + vlib_put_frame_to_node (vm, ip4_lookup_node.index, f); +} + +static void +snat_ipfix_logging_nat44_ses (u8 nat_event, u32 src_ip, u32 nat_src_ip, + snat_protocol_t snat_proto, u16 src_port, + u16 nat_src_port, u32 vrf_id, int do_flush) +{ + snat_ipfix_logging_main_t *silm = &snat_ipfix_logging_main; + flow_report_main_t *frm = &flow_report_main; + vlib_frame_t *f; + vlib_buffer_t *b0 = 0; + u32 bi0 = ~0; + u32 offset; + vlib_main_t * vm = frm->vlib_main; + u64 now; + vlib_buffer_free_list_t *fl; + u8 proto = ~0; + + if (!silm->enabled) + return; + + proto = snat_proto_to_ip_proto (snat_proto); + + now = (u64) ((vlib_time_now (vm) - silm->vlib_time_0) * 1e3); + now += silm->milisecond_time_0; + + b0 = silm->nat44_session_buffer; + + if (PREDICT_FALSE (b0 == 0)) + { + if (do_flush) + return; + + if (vlib_buffer_alloc (vm, &bi0, 1) != 1) + { + clib_warning ("can't allocate buffer for NAT IPFIX event"); + return; + } + + b0 = silm->nat44_session_buffer = + vlib_get_buffer (vm, bi0); + fl = vlib_buffer_get_free_list (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX); + vlib_buffer_init_for_free_list (b0, fl); + VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0); + offset = 0; + } + else + { + bi0 = vlib_get_buffer_index (vm, b0); + offset = silm->nat44_session_next_record_offset; + } + + f = silm->nat44_session_frame; + if (PREDICT_FALSE (f == 0)) + { + u32 * to_next; + f = vlib_get_frame_to_node (vm, ip4_lookup_node.index); + silm->nat44_session_frame = f; + to_next = vlib_frame_vector_args (f); + to_next[0] = bi0; + f->n_vectors = 1; + } + + if (PREDICT_FALSE (offset == 0)) + snat_ipfix_header_create (frm, b0, &offset); + + if (PREDICT_TRUE (do_flush == 0)) + { + u64 time_stamp = clib_host_to_net_u64 (now); + clib_memcpy (b0->data + offset, &time_stamp, sizeof (time_stamp)); + offset += sizeof (time_stamp); + + clib_memcpy (b0->data + offset, &nat_event, sizeof (nat_event)); + offset += sizeof (nat_event); + + clib_memcpy (b0->data + offset, &src_ip, sizeof (src_ip)); + offset += sizeof (src_ip); + + clib_memcpy (b0->data + offset, &nat_src_ip, sizeof (nat_src_ip)); + offset += sizeof (nat_src_ip); + + clib_memcpy (b0->data + offset, &proto, sizeof (proto)); + offset += sizeof (proto); + + clib_memcpy (b0->data + offset, &src_port, sizeof (src_port)); + offset += sizeof (src_port); + + clib_memcpy (b0->data + offset, &nat_src_port, sizeof (nat_src_port)); + offset += sizeof (nat_src_port); + + clib_memcpy (b0->data + offset, &vrf_id, sizeof(vrf_id)); + offset += sizeof (vrf_id); + + b0->current_length += NAT44_SESSION_CREATE_LEN; + } + + if (PREDICT_FALSE (do_flush || (offset + NAT44_SESSION_CREATE_LEN) > frm->path_mtu)) + { + snat_ipfix_send (frm, f, b0, silm->nat44_session_template_id); + silm->nat44_session_frame = 0; + silm->nat44_session_buffer = 0; + offset = 0; + } + silm->nat44_session_next_record_offset = offset; + } + +static void +snat_ipfix_logging_addr_exhausted (u32 pool_id, int do_flush) +{ + snat_ipfix_logging_main_t *silm = &snat_ipfix_logging_main; + flow_report_main_t *frm = &flow_report_main; + vlib_frame_t *f; + vlib_buffer_t *b0 = 0; + u32 bi0 = ~0; + u32 offset; + vlib_main_t * vm = frm->vlib_main; + u64 now; + vlib_buffer_free_list_t *fl; + u8 nat_event = NAT_ADDRESSES_EXHAUTED; + + if (!silm->enabled) + return; + + now = (u64) ((vlib_time_now (vm) - silm->vlib_time_0) * 1e3); + now += silm->milisecond_time_0; + + b0 = silm->addr_exhausted_buffer; + + if (PREDICT_FALSE (b0 == 0)) + { + if (do_flush) + return; + + if (vlib_buffer_alloc (vm, &bi0, 1) != 1) + { + clib_warning ("can't allocate buffer for NAT IPFIX event"); + return; + } + + b0 = silm->addr_exhausted_buffer = + vlib_get_buffer (vm, bi0); + fl = vlib_buffer_get_free_list (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX); + vlib_buffer_init_for_free_list (b0, fl); + VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0); + offset = 0; + } + else + { + bi0 = vlib_get_buffer_index (vm, b0); + offset = silm->addr_exhausted_next_record_offset; + } + + f = silm->addr_exhausted_frame; + if (PREDICT_FALSE (f == 0)) + { + u32 * to_next; + f = vlib_get_frame_to_node (vm, ip4_lookup_node.index); + silm->addr_exhausted_frame = f; + to_next = vlib_frame_vector_args (f); + to_next[0] = bi0; + f->n_vectors = 1; + } + + if (PREDICT_FALSE (offset == 0)) + snat_ipfix_header_create (frm, b0, &offset); + + if (PREDICT_TRUE (do_flush == 0)) + { + u64 time_stamp = clib_host_to_net_u64 (now); + clib_memcpy (b0->data + offset, &time_stamp, sizeof (time_stamp)); + offset += sizeof (time_stamp); + + clib_memcpy (b0->data + offset, &nat_event, sizeof (nat_event)); + offset += sizeof (nat_event); + + clib_memcpy (b0->data + offset, &pool_id, sizeof(pool_id)); + offset += sizeof (pool_id); + + b0->current_length += NAT_ADDRESSES_EXHAUTED_LEN; + } + + if (PREDICT_FALSE (do_flush || (offset + NAT_ADDRESSES_EXHAUTED_LEN) > frm->path_mtu)) + { + snat_ipfix_send (frm, f, b0, silm->addr_exhausted_template_id); + silm->addr_exhausted_frame = 0; + silm->addr_exhausted_buffer = 0; + offset = 0; + } + silm->addr_exhausted_next_record_offset = offset; +} + +static void +snat_ipfix_logging_max_entries_per_usr (u32 src_ip, int do_flush) +{ + snat_ipfix_logging_main_t *silm = &snat_ipfix_logging_main; + flow_report_main_t *frm = &flow_report_main; + vlib_frame_t *f; + vlib_buffer_t *b0 = 0; + u32 bi0 = ~0; + u32 offset; + vlib_main_t * vm = frm->vlib_main; + u64 now; + vlib_buffer_free_list_t *fl; + u8 nat_event = QUOTA_EXCEEDED; + u32 quota_event = MAX_ENTRIES_PER_USER; + + if (!silm->enabled) + return; + + now = (u64) ((vlib_time_now (vm) - silm->vlib_time_0) * 1e3); + now += silm->milisecond_time_0; + + b0 = silm->max_entries_per_user_buffer; + + if (PREDICT_FALSE (b0 == 0)) + { + if (do_flush) + return; + + if (vlib_buffer_alloc (vm, &bi0, 1) != 1) + { + clib_warning ("can't allocate buffer for NAT IPFIX event"); + return; + } + + b0 = silm->max_entries_per_user_buffer = + vlib_get_buffer (vm, bi0); + fl = vlib_buffer_get_free_list (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX); + vlib_buffer_init_for_free_list (b0, fl); + VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0); + offset = 0; + } + else + { + bi0 = vlib_get_buffer_index (vm, b0); + offset = silm->max_entries_per_user_next_record_offset; + } + + f = silm->max_entries_per_user_frame; + if (PREDICT_FALSE (f == 0)) + { + u32 * to_next; + f = vlib_get_frame_to_node (vm, ip4_lookup_node.index); + silm->max_entries_per_user_frame = f; + to_next = vlib_frame_vector_args (f); + to_next[0] = bi0; + f->n_vectors = 1; + } + + if (PREDICT_FALSE (offset == 0)) + snat_ipfix_header_create (frm, b0, &offset); + + if (PREDICT_TRUE (do_flush == 0)) + { + u64 time_stamp = clib_host_to_net_u64 (now); + clib_memcpy (b0->data + offset, &time_stamp, sizeof (time_stamp)); + offset += sizeof (time_stamp); + + clib_memcpy (b0->data + offset, &nat_event, sizeof (nat_event)); + offset += sizeof (nat_event); + + clib_memcpy (b0->data + offset, "a_event, sizeof(quota_event)); + offset += sizeof (quota_event); + + clib_memcpy (b0->data + offset, &src_ip, sizeof (src_ip)); + offset += sizeof (src_ip); + + b0->current_length += MAX_ENTRIES_PER_USER_LEN; + } + + if (PREDICT_FALSE (do_flush || (offset + MAX_ENTRIES_PER_USER_LEN) > frm->path_mtu)) + { + snat_ipfix_send (frm, f, b0, silm->max_entries_per_user_template_id); + silm->max_entries_per_user_frame = 0; + silm->max_entries_per_user_buffer = 0; + offset = 0; + } + silm->max_entries_per_user_next_record_offset = offset; +} + +static void +snat_ipfix_logging_nat44_ses_rpc_cb (snat_ipfix_logging_nat44_ses_args_t *a) +{ + snat_ipfix_logging_nat44_ses(a->nat_event, a->src_ip, a->nat_src_ip, + a->snat_proto, a->src_port, a->nat_src_port, + a->vrf_id, 0); +} + +/** + * @brief Generate NAT44 session create event + * + * @param src_ip source IPv4 address + * @param nat_src_ip transaltes source IPv4 address + * @param snat_proto NAT transport protocol + * @param src_port source port + * @param nat_src_port translated source port + * @param vrf_id VRF ID + */ +void +snat_ipfix_logging_nat44_ses_create (u32 src_ip, + u32 nat_src_ip, + snat_protocol_t snat_proto, + u16 src_port, + u16 nat_src_port, + u32 vrf_id) +{ + snat_ipfix_logging_nat44_ses_args_t a; + + a.nat_event = NAT44_SESSION_CREATE; + a.src_ip = src_ip; + a.nat_src_ip = nat_src_ip; + a.snat_proto = snat_proto; + a.src_port = src_port; + a.nat_src_port = nat_src_port; + a.vrf_id = vrf_id; + + vl_api_rpc_call_main_thread (snat_ipfix_logging_nat44_ses_rpc_cb, (u8 *) &a, + sizeof (a)); +} + +/** + * @brief Generate NAT44 session delete event + * + * @param src_ip source IPv4 address + * @param nat_src_ip transaltes source IPv4 address + * @param snat_proto NAT transport protocol + * @param src_port source port + * @param nat_src_port translated source port + * @param vrf_id VRF ID + */ +void +snat_ipfix_logging_nat44_ses_delete (u32 src_ip, + u32 nat_src_ip, + snat_protocol_t snat_proto, + u16 src_port, + u16 nat_src_port, + u32 vrf_id) +{ + snat_ipfix_logging_nat44_ses_args_t a; + + a.nat_event = NAT44_SESSION_DELETE; + a.src_ip = src_ip; + a.nat_src_ip = nat_src_ip; + a.snat_proto = snat_proto; + a.src_port = src_port; + a.nat_src_port = nat_src_port; + a.vrf_id = vrf_id; + + vl_api_rpc_call_main_thread (snat_ipfix_logging_nat44_ses_rpc_cb, (u8 *) &a, + sizeof (a)); +} + +vlib_frame_t * +snat_data_callback_nat44_session (flow_report_main_t * frm, + flow_report_t * fr, + vlib_frame_t * f, + u32 * to_next, + u32 node_index) +{ + snat_ipfix_logging_nat44_ses(0, 0, 0, 0, 0, 0, 0, 1); + return f; +} + +static void +snat_ipfix_logging_addr_exhausted_rpc_cb + (snat_ipfix_logging_addr_exhausted_args_t * a) +{ + snat_ipfix_logging_addr_exhausted(a->pool_id, 0); +} + +/** + * @brief Generate NAT addresses exhausted event + * + * @param pool_id NAT pool ID + */ +void +snat_ipfix_logging_addresses_exhausted(u32 pool_id) +{ + //TODO: This event SHOULD be rate limited + snat_ipfix_logging_addr_exhausted_args_t a; + + a.pool_id = pool_id; + + vl_api_rpc_call_main_thread (snat_ipfix_logging_addr_exhausted_rpc_cb, + (u8 *) &a, sizeof (a)); +} + +vlib_frame_t * +snat_data_callback_addr_exhausted (flow_report_main_t * frm, + flow_report_t * fr, + vlib_frame_t * f, + u32 * to_next, + u32 node_index) +{ + snat_ipfix_logging_addr_exhausted(0, 1); + return f; +} + +static void +snat_ipfix_logging_max_entries_per_usr_rpc_cb + (snat_ipfix_logging_max_entries_per_user_args_t * a) +{ + snat_ipfix_logging_max_entries_per_usr(a->src_ip, 0); +} + +/** + * @brief Generate maximum entries per user exceeded event + * + * @param src_ip source IPv4 address + */ +void +snat_ipfix_logging_max_entries_per_user(u32 src_ip) +{ + //TODO: This event SHOULD be rate limited + snat_ipfix_logging_max_entries_per_user_args_t a; + + a.src_ip = src_ip; + + vl_api_rpc_call_main_thread (snat_ipfix_logging_max_entries_per_usr_rpc_cb, + (u8 *) &a, sizeof (a)); +} + +vlib_frame_t * +snat_data_callback_max_entries_per_usr (flow_report_main_t * frm, + flow_report_t * fr, + vlib_frame_t * f, + u32 * to_next, + u32 node_index) +{ + snat_ipfix_logging_max_entries_per_usr(0, 1); + return f; +} + +/** + * @brief Enable/disable NAT plugin IPFIX logging + * + * @param enable 1 if enable, 0 if disable + * @param domain_id observation domain ID + * @param src_port source port number + * + * @returns 0 if success + */ +int +snat_ipfix_logging_enable_disable (int enable, u32 domain_id, u16 src_port) +{ + snat_main_t * sm = &snat_main; + snat_ipfix_logging_main_t *silm = &snat_ipfix_logging_main; + flow_report_main_t *frm = &flow_report_main; + vnet_flow_report_add_del_args_t a; + int rv; + u8 e = enable ? 1 : 0; + + if (silm->enabled == e) + return 0; + + silm->enabled = e; + + memset (&a, 0, sizeof (a)); + a.is_add = enable; + a.domain_id = domain_id ? domain_id : 1; + a.src_port = src_port ? src_port : UDP_DST_PORT_ipfix; + + if (sm->deterministic) + { + a.rewrite_callback = snat_template_rewrite_max_entries_per_usr; + a.flow_data_callback = snat_data_callback_max_entries_per_usr; + + rv = vnet_flow_report_add_del (frm, &a, NULL); + if (rv) + { + clib_warning ("vnet_flow_report_add_del returned %d", rv); + return -1; + } + } + else + { + a.rewrite_callback = snat_template_rewrite_nat44_session; + a.flow_data_callback = snat_data_callback_nat44_session; + + rv = vnet_flow_report_add_del (frm, &a, NULL); + if (rv) + { + clib_warning ("vnet_flow_report_add_del returned %d", rv); + return -1; + } + + a.rewrite_callback = snat_template_rewrite_addr_exhausted; + a.flow_data_callback = snat_data_callback_addr_exhausted; + + rv = vnet_flow_report_add_del (frm, &a, NULL); + if (rv) + { + clib_warning ("vnet_flow_report_add_del returned %d", rv); + return -1; + } + } + + return 0; +} + +/** + * @brief Initialize NAT plugin IPFIX logging + * + * @param vm vlib main + */ +void +snat_ipfix_logging_init (vlib_main_t * vm) +{ + snat_ipfix_logging_main_t *silm = &snat_ipfix_logging_main; + + silm->enabled = 0; + + /* Set up time reference pair */ + silm->vlib_time_0 = vlib_time_now (vm); + silm->milisecond_time_0 = unix_time_now_nsec () * 1e-6; +} diff --git a/src/plugins/nat/nat_ipfix_logging.h b/src/plugins/nat/nat_ipfix_logging.h new file mode 100644 index 00000000..6dbf6627 --- /dev/null +++ b/src/plugins/nat/nat_ipfix_logging.h @@ -0,0 +1,79 @@ +/* + * nat_ipfix_logging.h - NAT Events IPFIX logging + * + * 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_nat_ipfix_logging_h__ +#define __included_nat_ipfix_logging_h__ + +typedef enum { + NAT_ADDRESSES_EXHAUTED = 3, + NAT44_SESSION_CREATE = 4, + NAT44_SESSION_DELETE = 5, + NAT_PORTS_EXHAUSTED = 12, + QUOTA_EXCEEDED = 13, +} nat_event_t; + +typedef enum { + MAX_ENTRIES_PER_USER = 3, +} quota_exceed_event_t; + +typedef struct { + /** NAT plugin IPFIX logging enabled */ + u8 enabled; + + /** ipfix buffers under construction */ + vlib_buffer_t *nat44_session_buffer; + vlib_buffer_t *addr_exhausted_buffer; + vlib_buffer_t *max_entries_per_user_buffer; + + /** frames containing ipfix buffers */ + vlib_frame_t *nat44_session_frame; + vlib_frame_t *addr_exhausted_frame; + vlib_frame_t *max_entries_per_user_frame; + + /** next record offset */ + u32 nat44_session_next_record_offset; + u32 addr_exhausted_next_record_offset; + u32 max_entries_per_user_next_record_offset; + + /** Time reference pair */ + u64 milisecond_time_0; + f64 vlib_time_0; + + /** template IDs */ + u16 nat44_session_template_id; + u16 addr_exhausted_template_id; + u16 max_entries_per_user_template_id; + + /** stream index */ + u32 stream_index; +} snat_ipfix_logging_main_t; + +extern snat_ipfix_logging_main_t snat_ipfix_logging_main; + +void snat_ipfix_logging_init (vlib_main_t * vm); +int snat_ipfix_logging_enable_disable (int enable, u32 domain_id, u16 src_port); +void snat_ipfix_logging_nat44_ses_create (u32 src_ip, u32 nat_src_ip, + snat_protocol_t snat_proto, + u16 src_port, u16 nat_src_port, + u32 vrf_id); +void snat_ipfix_logging_nat44_ses_delete (u32 src_ip, u32 nat_src_ip, + snat_protocol_t snat_proto, + u16 src_port, u16 nat_src_port, + u32 vrf_id); +void snat_ipfix_logging_addresses_exhausted(u32 pool_id); +void snat_ipfix_logging_max_entries_per_user(u32 src_ip); + +#endif /* __included_nat_ipfix_logging_h__ */ diff --git a/src/plugins/nat/nat_msg_enum.h b/src/plugins/nat/nat_msg_enum.h new file mode 100644 index 00000000..710b631c --- /dev/null +++ b/src/plugins/nat/nat_msg_enum.h @@ -0,0 +1,31 @@ + +/* + * nat_msg_enum.h - skeleton vpp engine plug-in message enumeration + * + * Copyright (c) + * 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_nat_msg_enum_h +#define included_nat_msg_enum_h + +#include + +#define vl_msg_id(n,h) n, +typedef enum { +#include + /* 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_nat_msg_enum_h */ diff --git a/src/plugins/nat/nat_test.c b/src/plugins/nat/nat_test.c new file mode 100644 index 00000000..b653b77e --- /dev/null +++ b/src/plugins/nat/nat_test.c @@ -0,0 +1,1167 @@ + +/* + * nat.c - skeleton vpp-api-test plug-in + * + * Copyright (c) + * 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 +#include +#include +#include +#include +#include +#include + +#define __plugin_msg_base snat_test_main.msg_id_base +#include + +uword unformat_sw_if_index (unformat_input_t * input, va_list * args); + +/* Declare message IDs */ +#include + +/* define message structures */ +#define vl_typedefs +#include +#undef vl_typedefs + +/* declare message handlers for each api */ + +#define vl_endianfun /* define message structures */ +#include +#undef vl_endianfun + +/* instantiate all the print functions we know about */ +#define vl_print(handle, ...) +#define vl_printfun +#include +#undef vl_printfun + +/* Get the API version number. */ +#define vl_api_version(n,v) static u32 api_version=(v); +#include +#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) \ +_(snat_add_static_mapping_reply) \ +_(snat_set_workers_reply) \ +_(snat_add_del_interface_addr_reply) \ +_(snat_ipfix_enable_disable_reply) \ +_(snat_add_det_map_reply) \ +_(snat_det_set_timeouts_reply) \ +_(snat_det_close_session_out_reply) \ +_(snat_det_close_session_in_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) \ +_(SNAT_ADD_STATIC_MAPPING_REPLY, snat_add_static_mapping_reply) \ +_(SNAT_CONTROL_PING_REPLY, snat_control_ping_reply) \ +_(SNAT_STATIC_MAPPING_DETAILS, snat_static_mapping_details) \ +_(SNAT_SHOW_CONFIG_REPLY, snat_show_config_reply) \ +_(SNAT_ADDRESS_DETAILS, snat_address_details) \ +_(SNAT_INTERFACE_DETAILS, snat_interface_details) \ +_(SNAT_SET_WORKERS_REPLY, snat_set_workers_reply) \ +_(SNAT_WORKER_DETAILS, snat_worker_details) \ +_(SNAT_ADD_DEL_INTERFACE_ADDR_REPLY, \ + snat_add_del_interface_addr_reply) \ +_(SNAT_INTERFACE_ADDR_DETAILS, snat_interface_addr_details) \ +_(SNAT_IPFIX_ENABLE_DISABLE_REPLY, \ + snat_ipfix_enable_disable_reply) \ +_(SNAT_USER_DETAILS, snat_user_details) \ +_(SNAT_USER_SESSION_DETAILS, snat_user_session_details) \ +_(SNAT_ADD_DET_MAP_REPLY, snat_add_det_map_reply) \ +_(SNAT_DET_FORWARD_REPLY, snat_det_forward_reply) \ +_(SNAT_DET_REVERSE_REPLY, snat_det_reverse_reply) \ +_(SNAT_DET_MAP_DETAILS, snat_det_map_details) \ +_(SNAT_DET_SET_TIMEOUTS_REPLY, snat_det_set_timeouts_reply) \ +_(SNAT_DET_GET_TIMEOUTS_REPLY, snat_det_get_timeouts_reply) \ +_(SNAT_DET_CLOSE_SESSION_OUT_REPLY, \ + snat_det_close_session_out_reply) \ +_(SNAT_DET_CLOSE_SESSION_IN_REPLY, \ + snat_det_close_session_in_reply) \ +_(SNAT_DET_SESSION_DETAILS, snat_det_session_details) + +static int api_snat_add_address_range (vat_main_t * vam) +{ + unformat_input_t * i = vam->input; + ip4_address_t start_addr, end_addr; + u32 start_host_order, end_host_order; + vl_api_snat_add_address_range_t * mp; + u8 is_add = 1; + int count; + int ret; + + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + 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; + else if (unformat (i, "del")) + is_add = 0; + else + { + clib_warning("unknown input '%U'", format_unformat_error, i); + return -99; + } + } + + 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, mp); + + memcpy (mp->first_ip_address, &start_addr, 4); + memcpy (mp->last_ip_address, &end_addr, 4); + mp->is_ip4 = 1; + mp->is_add = is_add; + + S(mp); + W (ret); + return ret; +} + +static int api_snat_interface_add_del_feature (vat_main_t * vam) +{ + unformat_input_t * i = vam->input; + 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; + int ret; + + 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; + else + { + clib_warning("unknown input '%U'", format_unformat_error, i); + return -99; + } + } + + if (sw_if_index_set == 0) + { + errmsg ("interface / sw_if_index required\n"); + return -99; + } + + M(SNAT_INTERFACE_ADD_DEL_FEATURE, mp); + mp->sw_if_index = ntohl(sw_if_index); + mp->is_add = is_add; + mp->is_inside = is_inside; + + S(mp); + W (ret); + return ret; +} + +static int api_snat_add_static_mapping(vat_main_t * vam) +{ + unformat_input_t * i = vam->input; + vl_api_snat_add_static_mapping_t * mp; + u8 external_addr_set = 0; + u8 local_addr_set = 0; + u8 is_add = 1; + u8 addr_only = 1; + ip4_address_t local_addr, external_addr; + u32 local_port = 0, external_port = 0, vrf_id = ~0; + u32 sw_if_index = ~0; + u8 sw_if_index_set = 0; + u32 proto = ~0; + u8 proto_set = 0; + int ret; + + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "local_addr %U", unformat_ip4_address, &local_addr)) + local_addr_set = 1; + else if (unformat (i, "external_addr %U", unformat_ip4_address, + &external_addr)) + external_addr_set = 1; + else if (unformat (i, "local_port %u", &local_port)) + addr_only = 0; + else if (unformat (i, "external_port %u", &external_port)) + addr_only = 0; + else if (unformat (i, "external_if %U", unformat_sw_if_index, vam, + &sw_if_index)) + sw_if_index_set = 1; + else if (unformat (i, "external_sw_if_index %d", &sw_if_index)) + sw_if_index_set = 1; + else if (unformat (i, "vrf %u", &vrf_id)) + ; + else if (unformat (i, "protocol %u", &proto)) + proto_set = 1; + else if (unformat (i, "del")) + is_add = 0; + else + { + clib_warning("unknown input '%U'", format_unformat_error, i); + return -99; + } + } + + if (!addr_only && !proto_set) + { + errmsg ("protocol required\n"); + return -99; + } + + if (!local_addr_set) + { + errmsg ("local addr required\n"); + return -99; + } + if (!external_addr_set && !sw_if_index_set) + { + errmsg ("external addr or interface required\n"); + return -99; + } + + M(SNAT_ADD_STATIC_MAPPING, mp); + mp->is_add = is_add; + mp->is_ip4 = 1; + mp->addr_only = addr_only; + mp->local_port = ntohs ((u16) local_port); + mp->external_port = ntohs ((u16) external_port); + mp->external_sw_if_index = ntohl (sw_if_index); + mp->vrf_id = ntohl (vrf_id); + mp->protocol = (u8) proto; + memcpy (mp->local_ip_address, &local_addr, 4); + memcpy (mp->external_ip_address, &external_addr, 4); + + S(mp); + W (ret); + return ret; +} + +static void vl_api_snat_control_ping_reply_t_handler + (vl_api_snat_control_ping_reply_t * mp) +{ + vat_main_t *vam = &vat_main; + i32 retval = ntohl (mp->retval); + if (vam->async_mode) + { + vam->async_errors += (retval < 0); + } + else + { + vam->retval = retval; + vam->result_ready = 1; + } +} + +static void vl_api_snat_static_mapping_details_t_handler + (vl_api_snat_static_mapping_details_t *mp) +{ + snat_test_main_t * sm = &snat_test_main; + vat_main_t *vam = sm->vat_main; + + if (mp->addr_only && mp->external_sw_if_index != ~0) + fformat (vam->ofp, "%15U%6s%15d%6s%11d%6d\n", + format_ip4_address, &mp->local_ip_address, "", + ntohl (mp->external_sw_if_index), "", + ntohl (mp->vrf_id), + mp->protocol); + else if (mp->addr_only && mp->external_sw_if_index == ~0) + fformat (vam->ofp, "%15U%6s%15U%6s%11d%6d\n", + format_ip4_address, &mp->local_ip_address, "", + format_ip4_address, &mp->external_ip_address, "", + ntohl (mp->vrf_id), + mp->protocol); + else if (!mp->addr_only && mp->external_sw_if_index != ~0) + fformat (vam->ofp, "%15U%6d%15d%6d%11d%6d\n", + format_ip4_address, &mp->local_ip_address, + ntohs (mp->local_port), + ntohl (mp->external_sw_if_index), + ntohs (mp->external_port), + ntohl (mp->vrf_id), + mp->protocol); + else + fformat (vam->ofp, "%15U%6d%15U%6d%11d%6d\n", + format_ip4_address, &mp->local_ip_address, + ntohs (mp->local_port), + format_ip4_address, &mp->external_ip_address, + ntohs (mp->external_port), + ntohl (mp->vrf_id), + mp->protocol); + +} + +static int api_snat_static_mapping_dump(vat_main_t * vam) +{ + vl_api_snat_static_mapping_dump_t * mp; + vl_api_snat_control_ping_t *mp_ping; + int ret; + + if (vam->json_output) + { + clib_warning ("JSON output not supported for snat_static_mapping_dump"); + return -99; + } + + fformat (vam->ofp, "%21s%21s\n", "local", "external"); + fformat (vam->ofp, "%15s%6s%15s%6s%11s%6s\n", "address", "port", + "address/if_idx", "port", "vrf", "proto"); + + M(SNAT_STATIC_MAPPING_DUMP, mp); + S(mp); + + /* Use a control ping for synchronization */ + M(SNAT_CONTROL_PING, mp_ping); + S(mp_ping); + + W (ret); + return ret; +} + +static void vl_api_snat_show_config_reply_t_handler + (vl_api_snat_show_config_reply_t *mp) +{ + snat_test_main_t * sm = &snat_test_main; + vat_main_t *vam = sm->vat_main; + i32 retval = ntohl (mp->retval); + + if (retval >= 0) + { + fformat (vam->ofp, "translation hash buckets %d\n", + ntohl (mp->translation_buckets)); + fformat (vam->ofp, "translation hash memory %d\n", + ntohl (mp->translation_memory_size)); + fformat (vam->ofp, "user hash buckets %d\n", ntohl (mp->user_buckets)); + fformat (vam->ofp, "user hash memory %d\n", ntohl (mp->user_memory_size)); + fformat (vam->ofp, "max translations per user %d\n", + ntohl (mp->max_translations_per_user)); + fformat (vam->ofp, "outside VRF id %d\n", ntohl (mp->outside_vrf_id)); + fformat (vam->ofp, "inside VRF id %d\n", ntohl (mp->inside_vrf_id)); + if (mp->static_mapping_only) + { + fformat (vam->ofp, "static mapping only"); + if (mp->static_mapping_connection_tracking) + fformat (vam->ofp, " connection tracking"); + fformat (vam->ofp, "\n"); + } + } + vam->retval = retval; + vam->result_ready = 1; +} + +static int api_snat_show_config(vat_main_t * vam) +{ + vl_api_snat_show_config_t * mp; + int ret; + + if (vam->json_output) + { + clib_warning ("JSON output not supported for snat_show_config"); + return -99; + } + + M(SNAT_SHOW_CONFIG, mp); + S(mp); + W (ret); + return ret; +} + +static void vl_api_snat_address_details_t_handler + (vl_api_snat_address_details_t *mp) +{ + snat_test_main_t * sm = &snat_test_main; + vat_main_t *vam = sm->vat_main; + + fformat (vam->ofp, "%U\n", format_ip4_address, &mp->ip_address); +} + +static int api_snat_address_dump(vat_main_t * vam) +{ + vl_api_snat_address_dump_t * mp; + vl_api_snat_control_ping_t *mp_ping; + int ret; + + if (vam->json_output) + { + clib_warning ("JSON output not supported for snat_address_dump"); + return -99; + } + + M(SNAT_ADDRESS_DUMP, mp); + S(mp); + + /* Use a control ping for synchronization */ + M(SNAT_CONTROL_PING, mp_ping); + S(mp_ping); + + W (ret); + return ret; +} + +static void vl_api_snat_interface_details_t_handler + (vl_api_snat_interface_details_t *mp) +{ + snat_test_main_t * sm = &snat_test_main; + vat_main_t *vam = sm->vat_main; + + fformat (vam->ofp, "sw_if_index %d %s\n", ntohl (mp->sw_if_index), + mp->is_inside ? "in" : "out"); +} + +static int api_snat_interface_dump(vat_main_t * vam) +{ + vl_api_snat_interface_dump_t * mp; + vl_api_snat_control_ping_t *mp_ping; + int ret; + + if (vam->json_output) + { + clib_warning ("JSON output not supported for snat_address_dump"); + return -99; + } + + M(SNAT_INTERFACE_DUMP, mp); + S(mp); + + /* Use a control ping for synchronization */ + M(SNAT_CONTROL_PING, mp_ping); + S(mp_ping); + + W (ret); + return ret; +} + +static int api_snat_set_workers (vat_main_t * vam) +{ + unformat_input_t * i = vam->input; + vl_api_snat_set_workers_t * mp; + uword *bitmap; + int ret; + + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "%U", unformat_bitmap_list, &bitmap)) + ; + else + { + clib_warning("unknown input '%U'", format_unformat_error, i); + return -99; + } + } + + M(SNAT_SET_WORKERS, mp); + mp->worker_mask = clib_host_to_net_u64 (bitmap[0]); + + S(mp); + W (ret); + return ret; +} + +static void vl_api_snat_worker_details_t_handler + (vl_api_snat_worker_details_t *mp) +{ + snat_test_main_t * sm = &snat_test_main; + vat_main_t *vam = sm->vat_main; + + fformat (vam->ofp, "worker_index %d (%s at lcore %u)\n", + ntohl (mp->worker_index), mp->name, ntohl (mp->lcore_id)); +} + +static int api_snat_worker_dump(vat_main_t * vam) +{ + vl_api_snat_worker_dump_t * mp; + vl_api_snat_control_ping_t *mp_ping; + int ret; + + if (vam->json_output) + { + clib_warning ("JSON output not supported for snat_address_dump"); + return -99; + } + + M(SNAT_WORKER_DUMP, mp); + S(mp); + + /* Use a control ping for synchronization */ + M(SNAT_CONTROL_PING, mp_ping); + S(mp_ping); + + W (ret); + return ret; +} + +static int api_snat_add_del_interface_addr (vat_main_t * vam) +{ + unformat_input_t * i = vam->input; + vl_api_snat_add_del_interface_addr_t * mp; + u32 sw_if_index; + u8 sw_if_index_set = 0; + u8 is_add = 1; + int ret; + + 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, "del")) + is_add = 0; + else + { + clib_warning("unknown input '%U'", format_unformat_error, i); + return -99; + } + } + + if (sw_if_index_set == 0) + { + errmsg ("interface / sw_if_index required\n"); + return -99; + } + + M(SNAT_ADD_DEL_INTERFACE_ADDR, mp); + mp->sw_if_index = ntohl(sw_if_index); + mp->is_add = is_add; + + S(mp); + W (ret); + return ret; +} + +static void vl_api_snat_interface_addr_details_t_handler + (vl_api_snat_interface_addr_details_t *mp) +{ + snat_test_main_t * sm = &snat_test_main; + vat_main_t *vam = sm->vat_main; + + fformat (vam->ofp, "sw_if_index %d\n", ntohl (mp->sw_if_index)); +} + +static int api_snat_interface_addr_dump(vat_main_t * vam) +{ + vl_api_snat_interface_addr_dump_t * mp; + vl_api_snat_control_ping_t *mp_ping; + int ret; + + if (vam->json_output) + { + clib_warning ("JSON output not supported for snat_address_dump"); + return -99; + } + + M(SNAT_INTERFACE_ADDR_DUMP, mp); + S(mp); + + /* Use a control ping for synchronization */ + M(SNAT_CONTROL_PING, mp_ping); + S(mp_ping); + + W (ret); + return ret; +} + +static int api_snat_ipfix_enable_disable (vat_main_t * vam) +{ + unformat_input_t * i = vam->input; + vl_api_snat_ipfix_enable_disable_t * mp; + u32 domain_id = 0; + u32 src_port = 0; + u8 enable = 1; + int ret; + + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "domain %d", &domain_id)) + ; + else if (unformat (i, "src_port %d", &src_port)) + ; + else if (unformat (i, "disable")) + enable = 0; + else + { + clib_warning("unknown input '%U'", format_unformat_error, i); + return -99; + } + } + + M(SNAT_IPFIX_ENABLE_DISABLE, mp); + mp->domain_id = htonl(domain_id); + mp->src_port = htons((u16) src_port); + mp->enable = enable; + + S(mp); + W (ret); + return ret; +} + +static void vl_api_snat_user_session_details_t_handler + (vl_api_snat_user_session_details_t *mp) +{ + snat_test_main_t * sm = &snat_test_main; + vat_main_t *vam = sm->vat_main; + + fformat(vam->ofp, "%s session %U:%d to %U:%d protocol id %d " + "total packets %d total bytes %d\n", + mp->is_static ? "static" : "dynamic", + format_ip4_address, mp->inside_ip_address, ntohl(mp->inside_port), + format_ip4_address, mp->outside_ip_address, ntohl(mp->outside_port), + ntohl(mp->protocol), ntohl(mp->total_pkts), ntohl(mp->total_bytes)); +} + +static int api_snat_user_session_dump(vat_main_t * vam) +{ + unformat_input_t* i = vam->input; + vl_api_snat_user_session_dump_t * mp; + vl_api_snat_control_ping_t *mp_ping; + ip4_address_t addr; + u32 vrf_id = ~0; + int ret; + + if (vam->json_output) + { + clib_warning ("JSON output not supported for snat_address_dump"); + return -99; + } + + if (unformat (i, "ip_address %U vrf_id %d", + unformat_ip4_address, &addr, &vrf_id)) + ; + else + { + clib_warning("unknown input '%U'", format_unformat_error, i); + return -99; + } + + M(SNAT_USER_SESSION_DUMP, mp); + S(mp); + + /* Use a control ping for synchronization */ + M(SNAT_CONTROL_PING, mp_ping); + memset(mp->ip_address, 0, 16); + clib_memcpy(mp->ip_address, &addr, 4); + mp->vrf_id = htonl(vrf_id); + mp->is_ip4 = 1; + S(mp_ping); + + W (ret); + return ret; +} + +static void vl_api_snat_user_details_t_handler + (vl_api_snat_user_details_t *mp) +{ + snat_test_main_t * sm = &snat_test_main; + vat_main_t *vam = sm->vat_main; + + fformat(vam->ofp, "user with ip %U with vrf_id %d " + "with %d sessions and %d static sessions\n", + format_ip4_address, mp->ip_address, ntohl(mp->vrf_id), + ntohl(mp->nsessions), ntohl(mp->nstaticsessions)); +} + +static int api_snat_user_dump(vat_main_t * vam) +{ + vl_api_snat_user_dump_t * mp; + vl_api_snat_control_ping_t *mp_ping; + int ret; + + if (vam->json_output) + { + clib_warning ("JSON output not supported for snat_address_dump"); + return -99; + } + + M(SNAT_USER_DUMP, mp); + S(mp); + + /* Use a control ping for synchronization */ + M(SNAT_CONTROL_PING, mp_ping); + S(mp_ping); + + W (ret); + return ret; +} + +static int api_snat_add_det_map (vat_main_t * vam) +{ + unformat_input_t * i = vam->input; + vl_api_snat_add_det_map_t * mp; + ip4_address_t in_addr, out_addr; + u32 in_plen, out_plen; + u8 is_add = 1; + int ret; + + if (unformat (i, "in %U/%d out %U/%d", + unformat_ip4_address, &in_addr, &in_plen, + unformat_ip4_address, &out_addr, &out_plen)) + ; + else if (unformat (i, "del")) + is_add = 0; + else + { + clib_warning("unknown input '%U'", format_unformat_error, i); + return -99; + } + + M(SNAT_ADD_DET_MAP, mp); + clib_memcpy(mp->in_addr, &in_addr, 4); + mp->in_plen = in_plen; + clib_memcpy(mp->out_addr, &out_addr, 4); + mp->out_plen = out_plen; + mp->is_add = is_add; + + S(mp); + W (ret); + return ret; +} + +static void vl_api_snat_det_forward_reply_t_handler + (vl_api_snat_det_forward_reply_t *mp) +{ + snat_test_main_t * sm = &snat_test_main; + vat_main_t *vam = sm->vat_main; + i32 retval = ntohl(mp->retval); + + if (retval >= 0) + { + fformat (vam->ofp, "outside address %U", format_ip4_address, &mp->out_addr); + fformat (vam->ofp, " outside port range start %d", ntohs(mp->out_port_lo)); + fformat (vam->ofp, " outside port range end %d\n", ntohs(mp->out_port_hi)); + } + + vam->retval = retval; + vam->result_ready = 1; +} + +static int api_snat_det_forward (vat_main_t * vam) +{ + unformat_input_t * i = vam->input; + vl_api_snat_det_forward_t * mp; + ip4_address_t in_addr; + int ret; + + if (unformat (i, "%U", unformat_ip4_address, &in_addr)) + ; + else + { + clib_warning("unknown input '%U'", format_unformat_error, i); + return -99; + } + + M(SNAT_DET_FORWARD, mp); + clib_memcpy(mp->in_addr, &in_addr, 4); + + S(mp); + W(ret); + return ret; +} + +static void vl_api_snat_det_reverse_reply_t_handler + (vl_api_snat_det_reverse_reply_t *mp) +{ + snat_test_main_t * sm = &snat_test_main; + vat_main_t *vam = sm->vat_main; + i32 retval = ntohl(mp->retval); + + if (retval >= 0) + { + fformat (vam->ofp, "inside address %U\n", format_ip4_address, &mp->in_addr); + } + + vam->retval = retval; + vam->result_ready = 1; +} + +static int api_snat_det_reverse (vat_main_t * vam) +{ + unformat_input_t * i = vam->input; + vl_api_snat_det_reverse_t * mp; + ip4_address_t out_addr; + u16 out_port; + int ret; + + if (unformat (i, "%U %d", unformat_ip4_address, &out_addr, &out_port)) + ; + else + { + clib_warning("unknown input '%U'", format_unformat_error, i); + return -99; + } + + M(SNAT_DET_REVERSE, mp); + clib_memcpy(mp->out_addr, &out_addr, 4); + mp->out_port = htons(out_port); + + S(mp); + W(ret); + return ret; +} + +static void vl_api_snat_det_map_details_t_handler + (vl_api_snat_det_map_details_t *mp) +{ + snat_test_main_t * sm = &snat_test_main; + vat_main_t *vam = sm->vat_main; + + fformat (vam->ofp, "Deterministic S-NAT mapping in %U/%d out %U/%d " + "ports per host %d sharing ratio %d " + "number of sessions %d", + format_ip4_address, mp->in_addr, mp->in_plen, + format_ip4_address, mp->out_addr, mp->out_plen, + ntohs(mp->ports_per_host), ntohl(mp->sharing_ratio), + ntohl(mp->ses_num)); +} + +static int api_snat_det_map_dump(vat_main_t * vam) +{ + vl_api_snat_det_map_dump_t * mp; + vl_api_snat_control_ping_t *mp_ping; + int ret; + + if (vam->json_output) + { + clib_warning ("JSON output not supported for snat_det_map_dump"); + return -99; + } + + M(SNAT_DET_MAP_DUMP, mp); + S(mp); + + /* Use a control ping for synchronization */ + M(SNAT_CONTROL_PING, mp_ping); + S(mp_ping); + + W (ret); + return ret; +} + +static int api_snat_det_set_timeouts (vat_main_t * vam) +{ + unformat_input_t * i = vam->input; + vl_api_snat_det_set_timeouts_t * mp; + u32 udp = SNAT_UDP_TIMEOUT; + u32 tcp_established = SNAT_TCP_ESTABLISHED_TIMEOUT; + u32 tcp_transitory = SNAT_TCP_TRANSITORY_TIMEOUT; + u32 icmp = SNAT_ICMP_TIMEOUT; + int ret; + + if (unformat (i, "udp %d", &udp)) + ; + else if (unformat (i, "tcp_established %d", &tcp_established)) + ; + else if (unformat (i, "tcp_transitory %d", &tcp_transitory)) + ; + else if (unformat (i, "icmp %d", &icmp)) + ; + else + { + clib_warning("unknown input '%U'", format_unformat_error, i); + return -99; + } + + M(SNAT_DET_SET_TIMEOUTS, mp); + mp->udp = htonl(udp); + mp->tcp_established = htonl(tcp_established); + mp->tcp_transitory = htonl(tcp_transitory); + mp->icmp = htonl(icmp); + + S(mp); + W (ret); + return ret; +} + +static void vl_api_snat_det_get_timeouts_reply_t_handler + (vl_api_snat_det_get_timeouts_reply_t *mp) +{ + snat_test_main_t * sm = &snat_test_main; + vat_main_t *vam = sm->vat_main; + i32 retval = ntohl (mp->retval); + + if (retval >= 0) + { + fformat (vam->ofp, "udp timeout: %dsec\n", ntohl (mp->udp)); + fformat (vam->ofp, "tcp-established timeout: %dsec", + ntohl (mp->tcp_established)); + fformat (vam->ofp, "tcp-transitory timeout: %dsec", + ntohl (mp->tcp_transitory)); + fformat (vam->ofp, "icmp timeout: %dsec", ntohl (mp->icmp)); + } + vam->retval = retval; + vam->result_ready = 1; +} + +static int api_snat_det_get_timeouts(vat_main_t * vam) +{ + vl_api_snat_det_get_timeouts_t * mp; + int ret; + + if (vam->json_output) + { + clib_warning ("JSON output not supported for snat_show_config"); + return -99; + } + + M(SNAT_DET_GET_TIMEOUTS, mp); + S(mp); + W (ret); + return ret; +} + +static int api_snat_det_close_session_out (vat_main_t * vam) +{ + unformat_input_t * i = vam->input; + vl_api_snat_det_close_session_out_t * mp; + ip4_address_t out_addr, ext_addr; + u16 out_port, ext_port; + int ret; + + if (unformat (i, "%U:%d %U:%d", + unformat_ip4_address, &out_addr, &out_port, + unformat_ip4_address, &ext_addr, &ext_port)) + ; + else + { + clib_warning("unknown input '%U'", format_unformat_error, i); + return -99; + } + + M(SNAT_DET_CLOSE_SESSION_OUT, mp); + clib_memcpy(mp->out_addr, &out_addr, 4); + mp->out_port = ntohs(out_port); + clib_memcpy(mp->ext_addr, &ext_addr, 4); + mp->ext_port = ntohs(ext_port); + + S(mp); + W (ret); + return ret; +} + +static int api_snat_det_close_session_in (vat_main_t * vam) +{ + unformat_input_t * i = vam->input; + vl_api_snat_det_close_session_in_t * mp; + ip4_address_t in_addr, ext_addr; + u16 in_port, ext_port; + int ret; + + if (unformat (i, "%U:%d %U:%d", + unformat_ip4_address, &in_addr, &in_port, + unformat_ip4_address, &ext_addr, &ext_port)) + ; + else + { + clib_warning("unknown input '%U'", format_unformat_error, i); + return -99; + } + + M(SNAT_DET_CLOSE_SESSION_IN, mp); + clib_memcpy(mp->in_addr, &in_addr, 4); + mp->in_port = ntohs(in_port); + clib_memcpy(mp->ext_addr, &ext_addr, 4); + mp->ext_port = ntohs(ext_port); + + S(mp); + W (ret); + return ret; +} + +static void vl_api_snat_det_session_details_t_handler + (vl_api_snat_det_session_details_t *mp) +{ + snat_test_main_t * sm = &snat_test_main; + vat_main_t *vam = sm->vat_main; + + fformat(vam->ofp, "deterministic session, external host address %U, " + "external host port %d, outer port %d, inside port %d", + format_ip4_address, mp->ext_addr, mp->ext_port, + mp->out_port, mp->in_port); +} + +static int api_snat_det_session_dump(vat_main_t * vam) +{ + unformat_input_t* i = vam->input; + vl_api_snat_det_session_dump_t * mp; + vl_api_snat_control_ping_t *mp_ping; + ip4_address_t user_addr; + int ret; + + if (vam->json_output) + { + clib_warning ("JSON output not supported for snat_det_session_dump"); + return -99; + } + + if (unformat (i, "user_addr %U", unformat_ip4_address, &user_addr)) + ; + else + { + clib_warning ("unknown input '%U'", format_unformat_error, i); + return -99; + } + + M(SNAT_DET_SESSION_DUMP, mp); + clib_memcpy (&mp->user_addr, &user_addr, 4); + S(mp); + + /* Use a control ping for synchronization */ + M(SNAT_CONTROL_PING, mp_ping); + S(mp_ping); + + W (ret); + return ret; +} + +/* + * 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, " [- | sw_if_index [in] [out] [del]") \ +_(snat_add_static_mapping, "local_addr (external_addr " \ + " | external_if | external_sw_if_ndex ) " \ + "[local_port ] [external_port ] [vrf ] [del] " \ + "protocol ") \ +_(snat_set_workers, "") \ +_(snat_static_mapping_dump, "") \ +_(snat_show_config, "") \ +_(snat_address_dump, "") \ +_(snat_interface_dump, "") \ +_(snat_worker_dump, "") \ +_(snat_add_del_interface_addr, \ + " | sw_if_index [del]") \ +_(snat_interface_addr_dump, "") \ +_(snat_ipfix_enable_disable, "[domain ] [src_port ] " \ + "[disable]") \ +_(snat_user_dump, "") \ +_(snat_user_session_dump, "ip_address vrf_id ") \ +_(snat_add_det_map, "in / out " \ + "/ [del]") \ +_(snat_det_forward, "") \ +_(snat_det_reverse, " ") \ +_(snat_det_map_dump, "") \ +_(snat_det_set_timeouts, "[udp | tcp_established | " \ + "tcp_transitory | icmp ]") \ +_(snat_det_get_timeouts, "") \ +_(snat_det_close_session_out, ": " \ + ":") \ +_(snat_det_close_session_in, ": " \ + ":") \ +_(snat_det_session_dump, "ip_address ") + +static void +snat_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) + snat_vat_api_hookup (vam); + + vec_free(name); + + return 0; +} diff --git a/src/plugins/nat/out2in.c b/src/plugins/nat/out2in.c new file mode 100644 index 00000000..67950066 --- /dev/null +++ b/src/plugins/nat/out2in.c @@ -0,0 +1,2294 @@ +/* + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +typedef struct { + u32 sw_if_index; + u32 next_index; + u32 session_index; +} snat_out2in_trace_t; + +typedef struct { + u32 next_worker_index; + u8 do_handoff; +} snat_out2in_worker_handoff_trace_t; + +/* packet trace format function */ +static u8 * format_snat_out2in_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 *); + snat_out2in_trace_t * t = va_arg (*args, snat_out2in_trace_t *); + + s = format (s, "NAT44_OUT2IN: sw_if_index %d, next index %d, session index %d", + t->sw_if_index, t->next_index, t->session_index); + return s; +} + +static u8 * format_snat_out2in_fast_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 *); + snat_out2in_trace_t * t = va_arg (*args, snat_out2in_trace_t *); + + s = format (s, "NAT44_OUT2IN_FAST: sw_if_index %d, next index %d", + t->sw_if_index, t->next_index); + return s; +} + +static u8 * format_snat_out2in_worker_handoff_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 *); + snat_out2in_worker_handoff_trace_t * t = + va_arg (*args, snat_out2in_worker_handoff_trace_t *); + char * m; + + m = t->do_handoff ? "next worker" : "same worker"; + s = format (s, "NAT44_OUT2IN_WORKER_HANDOFF: %s %d", m, t->next_worker_index); + + return s; +} + +vlib_node_registration_t snat_out2in_node; +vlib_node_registration_t snat_out2in_fast_node; +vlib_node_registration_t snat_out2in_worker_handoff_node; +vlib_node_registration_t snat_det_out2in_node; + +#define foreach_snat_out2in_error \ +_(UNSUPPORTED_PROTOCOL, "Unsupported protocol") \ +_(OUT2IN_PACKETS, "Good out2in packets processed") \ +_(BAD_ICMP_TYPE, "unsupported ICMP type") \ +_(NO_TRANSLATION, "No translation") + +typedef enum { +#define _(sym,str) SNAT_OUT2IN_ERROR_##sym, + foreach_snat_out2in_error +#undef _ + SNAT_OUT2IN_N_ERROR, +} snat_out2in_error_t; + +static char * snat_out2in_error_strings[] = { +#define _(sym,string) string, + foreach_snat_out2in_error +#undef _ +}; + +typedef enum { + SNAT_OUT2IN_NEXT_DROP, + SNAT_OUT2IN_NEXT_LOOKUP, + SNAT_OUT2IN_NEXT_ICMP_ERROR, + SNAT_OUT2IN_N_NEXT, +} snat_out2in_next_t; + +/** + * @brief Create session for static mapping. + * + * Create NAT session initiated by host from external network with static + * mapping. + * + * @param sm NAT main. + * @param b0 Vlib buffer. + * @param in2out In2out NAT44 session key. + * @param out2in Out2in NAT44 session key. + * @param node Vlib node. + * + * @returns SNAT session if successfully created otherwise 0. + */ +static inline snat_session_t * +create_session_for_static_mapping (snat_main_t *sm, + vlib_buffer_t *b0, + snat_session_key_t in2out, + snat_session_key_t out2in, + vlib_node_runtime_t * node, + u32 thread_index) +{ + snat_user_t *u; + snat_user_key_t user_key; + snat_session_t *s; + clib_bihash_kv_8_8_t kv0, value0; + dlist_elt_t * per_user_translation_list_elt; + dlist_elt_t * per_user_list_head_elt; + ip4_header_t *ip0; + + ip0 = vlib_buffer_get_current (b0); + + user_key.addr = in2out.addr; + user_key.fib_index = in2out.fib_index; + kv0.key = user_key.as_u64; + + /* Ever heard of the "user" = inside ip4 address before? */ + if (clib_bihash_search_8_8 (&sm->user_hash, &kv0, &value0)) + { + /* no, make a new one */ + pool_get (sm->per_thread_data[thread_index].users, u); + memset (u, 0, sizeof (*u)); + u->addr = in2out.addr; + u->fib_index = in2out.fib_index; + + pool_get (sm->per_thread_data[thread_index].list_pool, + per_user_list_head_elt); + + u->sessions_per_user_list_head_index = per_user_list_head_elt - + sm->per_thread_data[thread_index].list_pool; + + clib_dlist_init (sm->per_thread_data[thread_index].list_pool, + u->sessions_per_user_list_head_index); + + kv0.value = u - sm->per_thread_data[thread_index].users; + + /* add user */ + clib_bihash_add_del_8_8 (&sm->user_hash, &kv0, 1 /* is_add */); + + /* add non-traslated packets worker lookup */ + kv0.value = thread_index; + clib_bihash_add_del_8_8 (&sm->worker_by_in, &kv0, 1); + } + else + { + u = pool_elt_at_index (sm->per_thread_data[thread_index].users, + value0.value); + } + + pool_get (sm->per_thread_data[thread_index].sessions, s); + memset (s, 0, sizeof (*s)); + + s->outside_address_index = ~0; + s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; + s->ext_host_addr.as_u32 = ip0->dst_address.as_u32; + u->nstaticsessions++; + + /* Create list elts */ + pool_get (sm->per_thread_data[thread_index].list_pool, + per_user_translation_list_elt); + clib_dlist_init (sm->per_thread_data[thread_index].list_pool, + per_user_translation_list_elt - + sm->per_thread_data[thread_index].list_pool); + + per_user_translation_list_elt->value = + s - sm->per_thread_data[thread_index].sessions; + s->per_user_index = + per_user_translation_list_elt - sm->per_thread_data[thread_index].list_pool; + s->per_user_list_head_index = u->sessions_per_user_list_head_index; + + clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool, + s->per_user_list_head_index, + per_user_translation_list_elt - + sm->per_thread_data[thread_index].list_pool); + + s->in2out = in2out; + s->out2in = out2in; + s->in2out.protocol = out2in.protocol; + + /* Add to translation hashes */ + kv0.key = s->in2out.as_u64; + kv0.value = s - sm->per_thread_data[thread_index].sessions; + if (clib_bihash_add_del_8_8 (&sm->in2out, &kv0, 1 /* is_add */)) + clib_warning ("in2out key add failed"); + + kv0.key = s->out2in.as_u64; + kv0.value = s - sm->per_thread_data[thread_index].sessions; + + if (clib_bihash_add_del_8_8 (&sm->out2in, &kv0, 1 /* is_add */)) + clib_warning ("out2in key add failed"); + + /* log NAT event */ + snat_ipfix_logging_nat44_ses_create(s->in2out.addr.as_u32, + s->out2in.addr.as_u32, + s->in2out.protocol, + s->in2out.port, + s->out2in.port, + s->in2out.fib_index); + return s; +} + +static_always_inline +snat_out2in_error_t icmp_get_key(ip4_header_t *ip0, + snat_session_key_t *p_key0) +{ + icmp46_header_t *icmp0; + snat_session_key_t key0; + icmp_echo_header_t *echo0, *inner_echo0 = 0; + ip4_header_t *inner_ip0; + void *l4_header = 0; + icmp46_header_t *inner_icmp0; + + icmp0 = (icmp46_header_t *) ip4_next_header (ip0); + echo0 = (icmp_echo_header_t *)(icmp0+1); + + if (!icmp_is_error_message (icmp0)) + { + key0.protocol = SNAT_PROTOCOL_ICMP; + key0.addr = ip0->dst_address; + key0.port = echo0->identifier; + } + else + { + inner_ip0 = (ip4_header_t *)(echo0+1); + l4_header = ip4_next_header (inner_ip0); + key0.protocol = ip_proto_to_snat_proto (inner_ip0->protocol); + key0.addr = inner_ip0->src_address; + switch (key0.protocol) + { + case SNAT_PROTOCOL_ICMP: + inner_icmp0 = (icmp46_header_t*)l4_header; + inner_echo0 = (icmp_echo_header_t *)(inner_icmp0+1); + key0.port = inner_echo0->identifier; + break; + case SNAT_PROTOCOL_UDP: + case SNAT_PROTOCOL_TCP: + key0.port = ((tcp_udp_header_t*)l4_header)->src_port; + break; + default: + return SNAT_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL; + } + } + *p_key0 = key0; + return -1; /* success */ +} + +/** + * Get address and port values to be used for ICMP packet translation + * and create session if needed + * + * @param[in,out] sm NAT main + * @param[in,out] node NAT node runtime + * @param[in] thread_index thread index + * @param[in,out] b0 buffer containing packet to be translated + * @param[out] p_proto protocol used for matching + * @param[out] p_value address and port after NAT translation + * @param[out] p_dont_translate if packet should not be translated + * @param d optional parameter + * @param e optional parameter + */ +u32 icmp_match_out2in_slow(snat_main_t *sm, vlib_node_runtime_t *node, + u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + snat_session_key_t *p_value, + u8 *p_dont_translate, void *d, void *e) +{ + ip4_header_t *ip0; + icmp46_header_t *icmp0; + u32 sw_if_index0; + u32 rx_fib_index0; + snat_session_key_t key0; + snat_session_key_t sm0; + snat_session_t *s0 = 0; + u8 dont_translate = 0; + clib_bihash_kv_8_8_t kv0, value0; + u8 is_addr_only; + u32 next0 = ~0; + int err; + + ip0 = vlib_buffer_get_current (b0); + icmp0 = (icmp46_header_t *) ip4_next_header (ip0); + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; + rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0); + + key0.protocol = 0; + + err = icmp_get_key (ip0, &key0); + if (err != -1) + { + b0->error = node->errors[SNAT_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL]; + next0 = SNAT_OUT2IN_NEXT_DROP; + goto out; + } + key0.fib_index = rx_fib_index0; + + kv0.key = key0.as_u64; + + if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0)) + { + /* Try to match static mapping by external address and port, + destination address and port in packet */ + if (snat_static_mapping_match(sm, key0, &sm0, 1, &is_addr_only)) + { + /* Don't NAT packet aimed at the intfc address */ + if (PREDICT_FALSE(is_interface_addr(sm, node, sw_if_index0, + ip0->dst_address.as_u32))) + { + dont_translate = 1; + goto out; + } + b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; + next0 = SNAT_OUT2IN_NEXT_DROP; + goto out; + } + + if (PREDICT_FALSE(icmp0->type != ICMP4_echo_reply && + (icmp0->type != ICMP4_echo_request || !is_addr_only))) + { + b0->error = node->errors[SNAT_OUT2IN_ERROR_BAD_ICMP_TYPE]; + next0 = SNAT_OUT2IN_NEXT_DROP; + goto out; + } + + /* Create session initiated by host from external network */ + s0 = create_session_for_static_mapping(sm, b0, sm0, key0, + node, thread_index); + + if (!s0) + { + next0 = SNAT_OUT2IN_NEXT_DROP; + goto out; + } + } + else + { + if (PREDICT_FALSE(icmp0->type != ICMP4_echo_reply && + icmp0->type != ICMP4_echo_request && + !icmp_is_error_message (icmp0))) + { + b0->error = node->errors[SNAT_OUT2IN_ERROR_BAD_ICMP_TYPE]; + next0 = SNAT_OUT2IN_NEXT_DROP; + goto out; + } + + s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, + value0.value); + } + +out: + *p_proto = key0.protocol; + if (s0) + *p_value = s0->in2out; + *p_dont_translate = dont_translate; + if (d) + *(snat_session_t**)d = s0; + return next0; +} + +/** + * Get address and port values to be used for ICMP packet translation + * + * @param[in] sm NAT main + * @param[in,out] node NAT node runtime + * @param[in] thread_index thread index + * @param[in,out] b0 buffer containing packet to be translated + * @param[out] p_proto protocol used for matching + * @param[out] p_value address and port after NAT translation + * @param[out] p_dont_translate if packet should not be translated + * @param d optional parameter + * @param e optional parameter + */ +u32 icmp_match_out2in_fast(snat_main_t *sm, vlib_node_runtime_t *node, + u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + snat_session_key_t *p_value, + u8 *p_dont_translate, void *d, void *e) +{ + ip4_header_t *ip0; + icmp46_header_t *icmp0; + u32 sw_if_index0; + u32 rx_fib_index0; + snat_session_key_t key0; + snat_session_key_t sm0; + u8 dont_translate = 0; + u8 is_addr_only; + u32 next0 = ~0; + int err; + + ip0 = vlib_buffer_get_current (b0); + icmp0 = (icmp46_header_t *) ip4_next_header (ip0); + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; + rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0); + + err = icmp_get_key (ip0, &key0); + if (err != -1) + { + b0->error = node->errors[err]; + next0 = SNAT_OUT2IN_NEXT_DROP; + goto out2; + } + key0.fib_index = rx_fib_index0; + + if (snat_static_mapping_match(sm, key0, &sm0, 1, &is_addr_only)) + { + /* Don't NAT packet aimed at the intfc address */ + if (is_interface_addr(sm, node, sw_if_index0, ip0->dst_address.as_u32)) + { + dont_translate = 1; + goto out; + } + b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; + next0 = SNAT_OUT2IN_NEXT_DROP; + goto out; + } + + if (PREDICT_FALSE(icmp0->type != ICMP4_echo_reply && + (icmp0->type != ICMP4_echo_request || !is_addr_only) && + !icmp_is_error_message (icmp0))) + { + b0->error = node->errors[SNAT_OUT2IN_ERROR_BAD_ICMP_TYPE]; + next0 = SNAT_OUT2IN_NEXT_DROP; + goto out; + } + +out: + *p_value = sm0; +out2: + *p_proto = key0.protocol; + *p_dont_translate = dont_translate; + return next0; +} + +static inline u32 icmp_out2in (snat_main_t *sm, + vlib_buffer_t * b0, + ip4_header_t * ip0, + icmp46_header_t * icmp0, + u32 sw_if_index0, + u32 rx_fib_index0, + vlib_node_runtime_t * node, + u32 next0, + u32 thread_index, + void *d, + void *e) +{ + snat_session_key_t sm0; + u8 protocol; + icmp_echo_header_t *echo0, *inner_echo0 = 0; + ip4_header_t *inner_ip0 = 0; + void *l4_header = 0; + icmp46_header_t *inner_icmp0; + u8 dont_translate; + u32 new_addr0, old_addr0; + u16 old_id0, new_id0; + ip_csum_t sum0; + u16 checksum0; + u32 next0_tmp; + + echo0 = (icmp_echo_header_t *)(icmp0+1); + + next0_tmp = sm->icmp_match_out2in_cb(sm, node, thread_index, b0, + &protocol, &sm0, &dont_translate, d, e); + if (next0_tmp != ~0) + next0 = next0_tmp; + if (next0 == SNAT_OUT2IN_NEXT_DROP || dont_translate) + goto out; + + sum0 = ip_incremental_checksum (0, icmp0, + ntohs(ip0->length) - ip4_header_bytes (ip0)); + checksum0 = ~ip_csum_fold (sum0); + if (checksum0 != 0 && checksum0 != 0xffff) + { + next0 = SNAT_OUT2IN_NEXT_DROP; + goto out; + } + + old_addr0 = ip0->dst_address.as_u32; + new_addr0 = ip0->dst_address.as_u32 = sm0.addr.as_u32; + vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index; + + sum0 = ip0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, + dst_address /* changed member */); + ip0->checksum = ip_csum_fold (sum0); + + if (!icmp_is_error_message (icmp0)) + { + new_id0 = sm0.port; + if (PREDICT_FALSE(new_id0 != echo0->identifier)) + { + old_id0 = echo0->identifier; + new_id0 = sm0.port; + echo0->identifier = new_id0; + + sum0 = icmp0->checksum; + sum0 = ip_csum_update (sum0, old_id0, new_id0, icmp_echo_header_t, + identifier /* changed member */); + icmp0->checksum = ip_csum_fold (sum0); + } + } + else + { + inner_ip0 = (ip4_header_t *)(echo0+1); + l4_header = ip4_next_header (inner_ip0); + + if (!ip4_header_checksum_is_valid (inner_ip0)) + { + next0 = SNAT_OUT2IN_NEXT_DROP; + goto out; + } + + old_addr0 = inner_ip0->src_address.as_u32; + inner_ip0->src_address = sm0.addr; + new_addr0 = inner_ip0->src_address.as_u32; + + sum0 = icmp0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, + src_address /* changed member */); + icmp0->checksum = ip_csum_fold (sum0); + + switch (protocol) + { + case SNAT_PROTOCOL_ICMP: + inner_icmp0 = (icmp46_header_t*)l4_header; + inner_echo0 = (icmp_echo_header_t *)(inner_icmp0+1); + + old_id0 = inner_echo0->identifier; + new_id0 = sm0.port; + inner_echo0->identifier = new_id0; + + sum0 = icmp0->checksum; + sum0 = ip_csum_update (sum0, old_id0, new_id0, icmp_echo_header_t, + identifier); + icmp0->checksum = ip_csum_fold (sum0); + break; + case SNAT_PROTOCOL_UDP: + case SNAT_PROTOCOL_TCP: + old_id0 = ((tcp_udp_header_t*)l4_header)->src_port; + new_id0 = sm0.port; + ((tcp_udp_header_t*)l4_header)->src_port = new_id0; + + sum0 = icmp0->checksum; + sum0 = ip_csum_update (sum0, old_id0, new_id0, tcp_udp_header_t, + src_port); + icmp0->checksum = ip_csum_fold (sum0); + break; + default: + ASSERT(0); + } + } + +out: + return next0; +} + + +static inline u32 icmp_out2in_slow_path (snat_main_t *sm, + vlib_buffer_t * b0, + ip4_header_t * ip0, + icmp46_header_t * icmp0, + u32 sw_if_index0, + u32 rx_fib_index0, + vlib_node_runtime_t * node, + u32 next0, f64 now, + u32 thread_index, + snat_session_t ** p_s0) +{ + next0 = icmp_out2in(sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, + next0, thread_index, p_s0, 0); + snat_session_t * s0 = *p_s0; + if (PREDICT_TRUE(next0 != SNAT_OUT2IN_NEXT_DROP && s0)) + { + /* Accounting */ + s0->last_heard = now; + s0->total_pkts++; + s0->total_bytes += vlib_buffer_length_in_chain (sm->vlib_main, b0); + /* Per-user LRU list maintenance for dynamic translation */ + if (!snat_is_session_static (s0)) + { + clib_dlist_remove (sm->per_thread_data[thread_index].list_pool, + s0->per_user_index); + clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool, + s0->per_user_list_head_index, + s0->per_user_index); + } + } + return next0; +} + +static void +snat_out2in_unknown_proto (snat_main_t *sm, + vlib_buffer_t * b, + ip4_header_t * ip, + u32 rx_fib_index, + u32 thread_index, + f64 now, + vlib_main_t * vm) +{ + clib_bihash_kv_8_8_t kv, value; + clib_bihash_kv_16_8_t s_kv, s_value; + snat_static_mapping_t *m; + snat_session_key_t m_key; + u32 old_addr, new_addr; + ip_csum_t sum; + snat_unk_proto_ses_key_t key; + snat_session_t * s; + snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; + snat_user_key_t u_key; + snat_user_t *u; + dlist_elt_t *head, *elt; + + old_addr = ip->dst_address.as_u32; + + key.l_addr = ip->dst_address; + key.r_addr = ip->src_address; + key.fib_index = rx_fib_index; + key.proto = ip->protocol; + key.rsvd[0] = key.rsvd[1] = key.rsvd[2] = 0; + s_kv.key[0] = key.as_u64[0]; + s_kv.key[1] = key.as_u64[1]; + + if (!clib_bihash_search_16_8 (&sm->out2in_unk_proto, &s_kv, &s_value)) + { + s = pool_elt_at_index (tsm->sessions, s_value.value); + new_addr = ip->dst_address.as_u32 = s->in2out.addr.as_u32; + } + else + { + m_key.addr = ip->dst_address; + m_key.port = 0; + m_key.protocol = 0; + m_key.fib_index = rx_fib_index; + kv.key = m_key.as_u64; + if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) + return; + + m = pool_elt_at_index (sm->static_mappings, value.value); + + new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32; + + u_key.addr = ip->src_address; + u_key.fib_index = m->fib_index; + kv.key = u_key.as_u64; + + /* Ever heard of the "user" = src ip4 address before? */ + if (clib_bihash_search_8_8 (&sm->user_hash, &kv, &value)) + { + /* no, make a new one */ + pool_get (tsm->users, u); + memset (u, 0, sizeof (*u)); + u->addr = ip->src_address; + u->fib_index = rx_fib_index; + + pool_get (tsm->list_pool, head); + u->sessions_per_user_list_head_index = head - tsm->list_pool; + + clib_dlist_init (tsm->list_pool, + u->sessions_per_user_list_head_index); + + kv.value = u - tsm->users; + + /* add user */ + clib_bihash_add_del_8_8 (&sm->user_hash, &kv, 1); + } + else + { + u = pool_elt_at_index (tsm->users, value.value); + } + + /* Create a new session */ + pool_get (tsm->sessions, s); + memset (s, 0, sizeof (*s)); + + s->ext_host_addr.as_u32 = ip->src_address.as_u32; + s->flags |= SNAT_SESSION_FLAG_UNKNOWN_PROTO; + s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; + s->outside_address_index = ~0; + s->out2in.addr.as_u32 = old_addr; + s->out2in.fib_index = rx_fib_index; + s->in2out.addr.as_u32 = new_addr; + s->in2out.fib_index = m->fib_index; + s->in2out.port = s->out2in.port = ip->protocol; + u->nstaticsessions++; + + /* Create list elts */ + pool_get (tsm->list_pool, elt); + clib_dlist_init (tsm->list_pool, elt - tsm->list_pool); + elt->value = s - tsm->sessions; + s->per_user_index = elt - tsm->list_pool; + s->per_user_list_head_index = u->sessions_per_user_list_head_index; + clib_dlist_addtail (tsm->list_pool, s->per_user_list_head_index, + s->per_user_index); + + /* Add to lookup tables */ + s_kv.value = s - tsm->sessions; + if (clib_bihash_add_del_16_8 (&sm->out2in_unk_proto, &s_kv, 1)) + clib_warning ("out2in key add failed"); + + key.l_addr = ip->dst_address; + key.fib_index = m->fib_index; + s_kv.key[0] = key.as_u64[0]; + s_kv.key[1] = key.as_u64[1]; + if (clib_bihash_add_del_16_8 (&sm->in2out_unk_proto, &s_kv, 1)) + clib_warning ("in2out key add failed"); + } + + /* Update IP checksum */ + sum = ip->checksum; + sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address); + ip->checksum = ip_csum_fold (sum); + + vnet_buffer(b)->sw_if_index[VLIB_TX] = s->in2out.fib_index; + + /* Accounting */ + s->last_heard = now; + s->total_pkts++; + s->total_bytes += vlib_buffer_length_in_chain (vm, b); + /* Per-user LRU list maintenance */ + clib_dlist_remove (tsm->list_pool, s->per_user_index); + clib_dlist_addtail (tsm->list_pool, s->per_user_list_head_index, + s->per_user_index); +} + +static uword +snat_out2in_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, * from, * to_next; + snat_out2in_next_t next_index; + u32 pkts_processed = 0; + snat_main_t * sm = &snat_main; + f64 now = vlib_time_now (vm); + u32 thread_index = vlib_get_thread_index (); + + 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 >= 4 && n_left_to_next >= 2) + { + u32 bi0, bi1; + vlib_buffer_t * b0, * b1; + u32 next0 = SNAT_OUT2IN_NEXT_LOOKUP; + u32 next1 = SNAT_OUT2IN_NEXT_LOOKUP; + u32 sw_if_index0, sw_if_index1; + ip4_header_t * ip0, *ip1; + ip_csum_t sum0, sum1; + u32 new_addr0, old_addr0; + u16 new_port0, old_port0; + u32 new_addr1, old_addr1; + u16 new_port1, old_port1; + udp_header_t * udp0, * udp1; + tcp_header_t * tcp0, * tcp1; + icmp46_header_t * icmp0, * icmp1; + snat_session_key_t key0, key1, sm0, sm1; + u32 rx_fib_index0, rx_fib_index1; + u32 proto0, proto1; + snat_session_t * s0 = 0, * s1 = 0; + clib_bihash_kv_8_8_t kv0, kv1, value0, value1; + + /* Prefetch next iteration. */ + { + vlib_buffer_t * p2, * p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header (p2, LOAD); + vlib_prefetch_buffer_header (p3, LOAD); + + CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE); + CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE); + } + + /* speculatively enqueue b0 and b1 to the current next frame */ + to_next[0] = bi0 = from[0]; + to_next[1] = bi1 = from[1]; + from += 2; + to_next += 2; + n_left_from -= 2; + n_left_to_next -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + vnet_buffer (b0)->snat.flags = 0; + vnet_buffer (b1)->snat.flags = 0; + + ip0 = vlib_buffer_get_current (b0); + udp0 = ip4_next_header (ip0); + tcp0 = (tcp_header_t *) udp0; + icmp0 = (icmp46_header_t *) udp0; + + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; + rx_fib_index0 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index, + sw_if_index0); + + if (PREDICT_FALSE(ip0->ttl == 1)) + { + vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, + ICMP4_time_exceeded_ttl_exceeded_in_transit, + 0); + next0 = SNAT_OUT2IN_NEXT_ICMP_ERROR; + goto trace0; + } + + proto0 = ip_proto_to_snat_proto (ip0->protocol); + + if (PREDICT_FALSE (proto0 == ~0)) + { + snat_out2in_unknown_proto(sm, b0, ip0, rx_fib_index0, + thread_index, now, vm); + goto trace0; + } + + if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP)) + { + next0 = icmp_out2in_slow_path + (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, + next0, now, thread_index, &s0); + goto trace0; + } + + key0.addr = ip0->dst_address; + key0.port = udp0->dst_port; + key0.protocol = proto0; + key0.fib_index = rx_fib_index0; + + kv0.key = key0.as_u64; + + if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0)) + { + /* Try to match static mapping by external address and port, + destination address and port in packet */ + if (snat_static_mapping_match(sm, key0, &sm0, 1, 0)) + { + b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; + /* + * Send DHCP packets to the ipv4 stack, or we won't + * be able to use dhcp client on the outside interface + */ + if (proto0 != SNAT_PROTOCOL_UDP + || (udp0->dst_port + != clib_host_to_net_u16(UDP_DST_PORT_dhcp_to_client))) + next0 = SNAT_OUT2IN_NEXT_DROP; + goto trace0; + } + + /* Create session initiated by host from external network */ + s0 = create_session_for_static_mapping(sm, b0, sm0, key0, node, + thread_index); + if (!s0) + { + b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; + next0 = SNAT_OUT2IN_NEXT_DROP; + goto trace0; + } + } + else + s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, + value0.value); + + old_addr0 = ip0->dst_address.as_u32; + ip0->dst_address = s0->in2out.addr; + new_addr0 = ip0->dst_address.as_u32; + vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index; + + sum0 = ip0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, + ip4_header_t, + dst_address /* changed member */); + ip0->checksum = ip_csum_fold (sum0); + + if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) + { + old_port0 = tcp0->dst_port; + tcp0->dst_port = s0->in2out.port; + new_port0 = tcp0->dst_port; + + sum0 = tcp0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, + ip4_header_t, + dst_address /* changed member */); + + sum0 = ip_csum_update (sum0, old_port0, new_port0, + ip4_header_t /* cheat */, + length /* changed member */); + tcp0->checksum = ip_csum_fold(sum0); + } + else + { + old_port0 = udp0->dst_port; + udp0->dst_port = s0->in2out.port; + udp0->checksum = 0; + } + + /* Accounting */ + s0->last_heard = now; + s0->total_pkts++; + s0->total_bytes += vlib_buffer_length_in_chain (vm, b0); + /* Per-user LRU list maintenance for dynamic translation */ + if (!snat_is_session_static (s0)) + { + clib_dlist_remove (sm->per_thread_data[thread_index].list_pool, + s0->per_user_index); + clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool, + s0->per_user_list_head_index, + s0->per_user_index); + } + trace0: + + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + snat_out2in_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = sw_if_index0; + t->next_index = next0; + t->session_index = ~0; + if (s0) + t->session_index = s0 - sm->per_thread_data[thread_index].sessions; + } + + pkts_processed += next0 != SNAT_OUT2IN_NEXT_DROP; + + + ip1 = vlib_buffer_get_current (b1); + udp1 = ip4_next_header (ip1); + tcp1 = (tcp_header_t *) udp1; + icmp1 = (icmp46_header_t *) udp1; + + sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX]; + rx_fib_index1 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index, + sw_if_index1); + + if (PREDICT_FALSE(ip1->ttl == 1)) + { + vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b1, ICMP4_time_exceeded, + ICMP4_time_exceeded_ttl_exceeded_in_transit, + 0); + next1 = SNAT_OUT2IN_NEXT_ICMP_ERROR; + goto trace1; + } + + proto1 = ip_proto_to_snat_proto (ip1->protocol); + + if (PREDICT_FALSE (proto1 == ~0)) + { + snat_out2in_unknown_proto(sm, b1, ip1, rx_fib_index1, + thread_index, now, vm); + goto trace1; + } + + if (PREDICT_FALSE (proto1 == SNAT_PROTOCOL_ICMP)) + { + next1 = icmp_out2in_slow_path + (sm, b1, ip1, icmp1, sw_if_index1, rx_fib_index1, node, + next1, now, thread_index, &s1); + goto trace1; + } + + key1.addr = ip1->dst_address; + key1.port = udp1->dst_port; + key1.protocol = proto1; + key1.fib_index = rx_fib_index1; + + kv1.key = key1.as_u64; + + if (clib_bihash_search_8_8 (&sm->out2in, &kv1, &value1)) + { + /* Try to match static mapping by external address and port, + destination address and port in packet */ + if (snat_static_mapping_match(sm, key1, &sm1, 1, 0)) + { + b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; + /* + * Send DHCP packets to the ipv4 stack, or we won't + * be able to use dhcp client on the outside interface + */ + if (proto1 != SNAT_PROTOCOL_UDP + || (udp1->dst_port + != clib_host_to_net_u16(UDP_DST_PORT_dhcp_to_client))) + next1 = SNAT_OUT2IN_NEXT_DROP; + goto trace1; + } + + /* Create session initiated by host from external network */ + s1 = create_session_for_static_mapping(sm, b1, sm1, key1, node, + thread_index); + if (!s1) + { + b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; + next1 = SNAT_OUT2IN_NEXT_DROP; + goto trace1; + } + } + else + s1 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, + value1.value); + + old_addr1 = ip1->dst_address.as_u32; + ip1->dst_address = s1->in2out.addr; + new_addr1 = ip1->dst_address.as_u32; + vnet_buffer(b1)->sw_if_index[VLIB_TX] = s1->in2out.fib_index; + + sum1 = ip1->checksum; + sum1 = ip_csum_update (sum1, old_addr1, new_addr1, + ip4_header_t, + dst_address /* changed member */); + ip1->checksum = ip_csum_fold (sum1); + + if (PREDICT_TRUE(proto1 == SNAT_PROTOCOL_TCP)) + { + old_port1 = tcp1->dst_port; + tcp1->dst_port = s1->in2out.port; + new_port1 = tcp1->dst_port; + + sum1 = tcp1->checksum; + sum1 = ip_csum_update (sum1, old_addr1, new_addr1, + ip4_header_t, + dst_address /* changed member */); + + sum1 = ip_csum_update (sum1, old_port1, new_port1, + ip4_header_t /* cheat */, + length /* changed member */); + tcp1->checksum = ip_csum_fold(sum1); + } + else + { + old_port1 = udp1->dst_port; + udp1->dst_port = s1->in2out.port; + udp1->checksum = 0; + } + + /* Accounting */ + s1->last_heard = now; + s1->total_pkts++; + s1->total_bytes += vlib_buffer_length_in_chain (vm, b1); + /* Per-user LRU list maintenance for dynamic translation */ + if (!snat_is_session_static (s1)) + { + clib_dlist_remove (sm->per_thread_data[thread_index].list_pool, + s1->per_user_index); + clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool, + s1->per_user_list_head_index, + s1->per_user_index); + } + trace1: + + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b1->flags & VLIB_BUFFER_IS_TRACED))) + { + snat_out2in_trace_t *t = + vlib_add_trace (vm, node, b1, sizeof (*t)); + t->sw_if_index = sw_if_index1; + t->next_index = next1; + t->session_index = ~0; + if (s1) + t->session_index = s1 - sm->per_thread_data[thread_index].sessions; + } + + pkts_processed += next1 != SNAT_OUT2IN_NEXT_DROP; + + /* verify speculative enqueues, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, next0, next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t * b0; + u32 next0 = SNAT_OUT2IN_NEXT_LOOKUP; + u32 sw_if_index0; + ip4_header_t * ip0; + ip_csum_t sum0; + u32 new_addr0, old_addr0; + u16 new_port0, old_port0; + udp_header_t * udp0; + tcp_header_t * tcp0; + icmp46_header_t * icmp0; + snat_session_key_t key0, sm0; + u32 rx_fib_index0; + u32 proto0; + snat_session_t * s0 = 0; + clib_bihash_kv_8_8_t kv0, value0; + + /* speculatively enqueue b0 to the current next frame */ + 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); + + vnet_buffer (b0)->snat.flags = 0; + + ip0 = vlib_buffer_get_current (b0); + udp0 = ip4_next_header (ip0); + tcp0 = (tcp_header_t *) udp0; + icmp0 = (icmp46_header_t *) udp0; + + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; + rx_fib_index0 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index, + sw_if_index0); + + proto0 = ip_proto_to_snat_proto (ip0->protocol); + + if (PREDICT_FALSE (proto0 == ~0)) + { + snat_out2in_unknown_proto(sm, b0, ip0, rx_fib_index0, + thread_index, now, vm); + goto trace00; + } + + if (PREDICT_FALSE(ip0->ttl == 1)) + { + vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, + ICMP4_time_exceeded_ttl_exceeded_in_transit, + 0); + next0 = SNAT_OUT2IN_NEXT_ICMP_ERROR; + goto trace00; + } + + if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP)) + { + next0 = icmp_out2in_slow_path + (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, + next0, now, thread_index, &s0); + goto trace00; + } + + key0.addr = ip0->dst_address; + key0.port = udp0->dst_port; + key0.protocol = proto0; + key0.fib_index = rx_fib_index0; + + kv0.key = key0.as_u64; + + if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0)) + { + /* Try to match static mapping by external address and port, + destination address and port in packet */ + if (snat_static_mapping_match(sm, key0, &sm0, 1, 0)) + { + b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; + /* + * Send DHCP packets to the ipv4 stack, or we won't + * be able to use dhcp client on the outside interface + */ + if (proto0 != SNAT_PROTOCOL_UDP + || (udp0->dst_port + != clib_host_to_net_u16(UDP_DST_PORT_dhcp_to_client))) + + next0 = SNAT_OUT2IN_NEXT_DROP; + goto trace00; + } + + /* Create session initiated by host from external network */ + s0 = create_session_for_static_mapping(sm, b0, sm0, key0, node, + thread_index); + if (!s0) + { + b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; + next0 = SNAT_OUT2IN_NEXT_DROP; + goto trace00; + } + } + else + s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, + value0.value); + + old_addr0 = ip0->dst_address.as_u32; + ip0->dst_address = s0->in2out.addr; + new_addr0 = ip0->dst_address.as_u32; + vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index; + + sum0 = ip0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, + ip4_header_t, + dst_address /* changed member */); + ip0->checksum = ip_csum_fold (sum0); + + if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) + { + old_port0 = tcp0->dst_port; + tcp0->dst_port = s0->in2out.port; + new_port0 = tcp0->dst_port; + + sum0 = tcp0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, + ip4_header_t, + dst_address /* changed member */); + + sum0 = ip_csum_update (sum0, old_port0, new_port0, + ip4_header_t /* cheat */, + length /* changed member */); + tcp0->checksum = ip_csum_fold(sum0); + } + else + { + old_port0 = udp0->dst_port; + udp0->dst_port = s0->in2out.port; + udp0->checksum = 0; + } + + /* Accounting */ + s0->last_heard = now; + s0->total_pkts++; + s0->total_bytes += vlib_buffer_length_in_chain (vm, b0); + /* Per-user LRU list maintenance for dynamic translation */ + if (!snat_is_session_static (s0)) + { + clib_dlist_remove (sm->per_thread_data[thread_index].list_pool, + s0->per_user_index); + clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool, + s0->per_user_list_head_index, + s0->per_user_index); + } + trace00: + + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + snat_out2in_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = sw_if_index0; + t->next_index = next0; + t->session_index = ~0; + if (s0) + t->session_index = s0 - sm->per_thread_data[thread_index].sessions; + } + + pkts_processed += next0 != SNAT_OUT2IN_NEXT_DROP; + + /* verify speculative enqueue, maybe switch current next frame */ + 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, snat_out2in_node.index, + SNAT_OUT2IN_ERROR_OUT2IN_PACKETS, + pkts_processed); + return frame->n_vectors; +} + +VLIB_REGISTER_NODE (snat_out2in_node) = { + .function = snat_out2in_node_fn, + .name = "nat44-out2in", + .vector_size = sizeof (u32), + .format_trace = format_snat_out2in_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(snat_out2in_error_strings), + .error_strings = snat_out2in_error_strings, + + .runtime_data_bytes = sizeof (snat_runtime_t), + + .n_next_nodes = SNAT_OUT2IN_N_NEXT, + + /* edit / add dispositions here */ + .next_nodes = { + [SNAT_OUT2IN_NEXT_DROP] = "error-drop", + [SNAT_OUT2IN_NEXT_LOOKUP] = "ip4-lookup", + [SNAT_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error", + }, +}; +VLIB_NODE_FUNCTION_MULTIARCH (snat_out2in_node, snat_out2in_node_fn); + +/**************************/ +/*** deterministic mode ***/ +/**************************/ +static uword +snat_det_out2in_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, * from, * to_next; + snat_out2in_next_t next_index; + u32 pkts_processed = 0; + snat_main_t * sm = &snat_main; + u32 thread_index = vlib_get_thread_index (); + + 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 >= 4 && n_left_to_next >= 2) + { + u32 bi0, bi1; + vlib_buffer_t * b0, * b1; + u32 next0 = SNAT_OUT2IN_NEXT_LOOKUP; + u32 next1 = SNAT_OUT2IN_NEXT_LOOKUP; + u32 sw_if_index0, sw_if_index1; + ip4_header_t * ip0, * ip1; + ip_csum_t sum0, sum1; + ip4_address_t new_addr0, old_addr0, new_addr1, old_addr1; + u16 new_port0, old_port0, old_port1, new_port1; + udp_header_t * udp0, * udp1; + tcp_header_t * tcp0, * tcp1; + u32 proto0, proto1; + snat_det_out_key_t key0, key1; + snat_det_map_t * dm0, * dm1; + snat_det_session_t * ses0 = 0, * ses1 = 0; + u32 rx_fib_index0, rx_fib_index1; + icmp46_header_t * icmp0, * icmp1; + + /* Prefetch next iteration. */ + { + vlib_buffer_t * p2, * p3; + + p2 = vlib_get_buffer (vm, from[2]); + p3 = vlib_get_buffer (vm, from[3]); + + vlib_prefetch_buffer_header (p2, LOAD); + vlib_prefetch_buffer_header (p3, LOAD); + + CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE); + CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE); + } + + /* speculatively enqueue b0 and b1 to the current next frame */ + to_next[0] = bi0 = from[0]; + to_next[1] = bi1 = from[1]; + from += 2; + to_next += 2; + n_left_from -= 2; + n_left_to_next -= 2; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + ip0 = vlib_buffer_get_current (b0); + udp0 = ip4_next_header (ip0); + tcp0 = (tcp_header_t *) udp0; + + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; + + if (PREDICT_FALSE(ip0->ttl == 1)) + { + vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, + ICMP4_time_exceeded_ttl_exceeded_in_transit, + 0); + next0 = SNAT_OUT2IN_NEXT_ICMP_ERROR; + goto trace0; + } + + proto0 = ip_proto_to_snat_proto (ip0->protocol); + + if (PREDICT_FALSE(proto0 == SNAT_PROTOCOL_ICMP)) + { + rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0); + icmp0 = (icmp46_header_t *) udp0; + + next0 = icmp_out2in(sm, b0, ip0, icmp0, sw_if_index0, + rx_fib_index0, node, next0, thread_index, + &ses0, &dm0); + goto trace0; + } + + key0.ext_host_addr = ip0->src_address; + key0.ext_host_port = tcp0->src; + key0.out_port = tcp0->dst; + + dm0 = snat_det_map_by_out(sm, &ip0->dst_address); + if (PREDICT_FALSE(!dm0)) + { + clib_warning("unknown dst address: %U", + format_ip4_address, &ip0->dst_address); + next0 = SNAT_OUT2IN_NEXT_DROP; + b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; + goto trace0; + } + + snat_det_reverse(dm0, &ip0->dst_address, + clib_net_to_host_u16(tcp0->dst), &new_addr0); + + ses0 = snat_det_get_ses_by_out (dm0, &new_addr0, key0.as_u64); + if (PREDICT_FALSE(!ses0)) + { + clib_warning("no match src %U:%d dst %U:%d for user %U", + format_ip4_address, &ip0->src_address, + clib_net_to_host_u16 (tcp0->src), + format_ip4_address, &ip0->dst_address, + clib_net_to_host_u16 (tcp0->dst), + format_ip4_address, &new_addr0); + next0 = SNAT_OUT2IN_NEXT_DROP; + b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; + goto trace0; + } + new_port0 = ses0->in_port; + + old_addr0 = ip0->dst_address; + ip0->dst_address = new_addr0; + vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm->inside_fib_index; + + sum0 = ip0->checksum; + sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32, + ip4_header_t, + dst_address /* changed member */); + ip0->checksum = ip_csum_fold (sum0); + + if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) + { + if (tcp0->flags & TCP_FLAG_FIN && ses0->state == SNAT_SESSION_TCP_ESTABLISHED) + ses0->state = SNAT_SESSION_TCP_CLOSE_WAIT; + else if (tcp0->flags & TCP_FLAG_ACK && ses0->state == SNAT_SESSION_TCP_LAST_ACK) + snat_det_ses_close(dm0, ses0); + + old_port0 = tcp0->dst; + tcp0->dst = new_port0; + + sum0 = tcp0->checksum; + sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32, + ip4_header_t, + dst_address /* changed member */); + + sum0 = ip_csum_update (sum0, old_port0, new_port0, + ip4_header_t /* cheat */, + length /* changed member */); + tcp0->checksum = ip_csum_fold(sum0); + } + else + { + old_port0 = udp0->dst_port; + udp0->dst_port = new_port0; + udp0->checksum = 0; + } + + trace0: + + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + snat_out2in_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = sw_if_index0; + t->next_index = next0; + t->session_index = ~0; + if (ses0) + t->session_index = ses0 - dm0->sessions; + } + + pkts_processed += next0 != SNAT_OUT2IN_NEXT_DROP; + + b1 = vlib_get_buffer (vm, bi1); + + ip1 = vlib_buffer_get_current (b1); + udp1 = ip4_next_header (ip1); + tcp1 = (tcp_header_t *) udp1; + + sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX]; + + if (PREDICT_FALSE(ip1->ttl == 1)) + { + vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b1, ICMP4_time_exceeded, + ICMP4_time_exceeded_ttl_exceeded_in_transit, + 0); + next1 = SNAT_OUT2IN_NEXT_ICMP_ERROR; + goto trace1; + } + + proto1 = ip_proto_to_snat_proto (ip1->protocol); + + if (PREDICT_FALSE(proto1 == SNAT_PROTOCOL_ICMP)) + { + rx_fib_index1 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index1); + icmp1 = (icmp46_header_t *) udp1; + + next1 = icmp_out2in(sm, b1, ip1, icmp1, sw_if_index1, + rx_fib_index1, node, next1, thread_index, + &ses1, &dm1); + goto trace1; + } + + key1.ext_host_addr = ip1->src_address; + key1.ext_host_port = tcp1->src; + key1.out_port = tcp1->dst; + + dm1 = snat_det_map_by_out(sm, &ip1->dst_address); + if (PREDICT_FALSE(!dm1)) + { + clib_warning("unknown dst address: %U", + format_ip4_address, &ip1->dst_address); + next1 = SNAT_OUT2IN_NEXT_DROP; + b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; + goto trace1; + } + + snat_det_reverse(dm1, &ip1->dst_address, + clib_net_to_host_u16(tcp1->dst), &new_addr1); + + ses1 = snat_det_get_ses_by_out (dm1, &new_addr1, key1.as_u64); + if (PREDICT_FALSE(!ses1)) + { + clib_warning("no match src %U:%d dst %U:%d for user %U", + format_ip4_address, &ip1->src_address, + clib_net_to_host_u16 (tcp1->src), + format_ip4_address, &ip1->dst_address, + clib_net_to_host_u16 (tcp1->dst), + format_ip4_address, &new_addr1); + next1 = SNAT_OUT2IN_NEXT_DROP; + b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; + goto trace1; + } + new_port1 = ses1->in_port; + + old_addr1 = ip1->dst_address; + ip1->dst_address = new_addr1; + vnet_buffer(b1)->sw_if_index[VLIB_TX] = sm->inside_fib_index; + + sum1 = ip1->checksum; + sum1 = ip_csum_update (sum1, old_addr1.as_u32, new_addr1.as_u32, + ip4_header_t, + dst_address /* changed member */); + ip1->checksum = ip_csum_fold (sum1); + + if (PREDICT_TRUE(proto1 == SNAT_PROTOCOL_TCP)) + { + if (tcp1->flags & TCP_FLAG_FIN && ses1->state == SNAT_SESSION_TCP_ESTABLISHED) + ses1->state = SNAT_SESSION_TCP_CLOSE_WAIT; + else if (tcp1->flags & TCP_FLAG_ACK && ses1->state == SNAT_SESSION_TCP_LAST_ACK) + snat_det_ses_close(dm1, ses1); + + old_port1 = tcp1->dst; + tcp1->dst = new_port1; + + sum1 = tcp1->checksum; + sum1 = ip_csum_update (sum1, old_addr1.as_u32, new_addr1.as_u32, + ip4_header_t, + dst_address /* changed member */); + + sum1 = ip_csum_update (sum1, old_port1, new_port1, + ip4_header_t /* cheat */, + length /* changed member */); + tcp1->checksum = ip_csum_fold(sum1); + } + else + { + old_port1 = udp1->dst_port; + udp1->dst_port = new_port1; + udp1->checksum = 0; + } + + trace1: + + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b1->flags & VLIB_BUFFER_IS_TRACED))) + { + snat_out2in_trace_t *t = + vlib_add_trace (vm, node, b1, sizeof (*t)); + t->sw_if_index = sw_if_index1; + t->next_index = next1; + t->session_index = ~0; + if (ses1) + t->session_index = ses1 - dm1->sessions; + } + + pkts_processed += next1 != SNAT_OUT2IN_NEXT_DROP; + + /* verify speculative enqueues, maybe switch current next frame */ + vlib_validate_buffer_enqueue_x2 (vm, node, next_index, + to_next, n_left_to_next, + bi0, bi1, next0, next1); + } + + while (n_left_from > 0 && n_left_to_next > 0) + { + u32 bi0; + vlib_buffer_t * b0; + u32 next0 = SNAT_OUT2IN_NEXT_LOOKUP; + u32 sw_if_index0; + ip4_header_t * ip0; + ip_csum_t sum0; + ip4_address_t new_addr0, old_addr0; + u16 new_port0, old_port0; + udp_header_t * udp0; + tcp_header_t * tcp0; + u32 proto0; + snat_det_out_key_t key0; + snat_det_map_t * dm0; + snat_det_session_t * ses0 = 0; + u32 rx_fib_index0; + icmp46_header_t * icmp0; + + /* speculatively enqueue b0 to the current next frame */ + 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); + + ip0 = vlib_buffer_get_current (b0); + udp0 = ip4_next_header (ip0); + tcp0 = (tcp_header_t *) udp0; + + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; + + if (PREDICT_FALSE(ip0->ttl == 1)) + { + vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, + ICMP4_time_exceeded_ttl_exceeded_in_transit, + 0); + next0 = SNAT_OUT2IN_NEXT_ICMP_ERROR; + goto trace00; + } + + proto0 = ip_proto_to_snat_proto (ip0->protocol); + + if (PREDICT_FALSE(proto0 == SNAT_PROTOCOL_ICMP)) + { + rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0); + icmp0 = (icmp46_header_t *) udp0; + + next0 = icmp_out2in(sm, b0, ip0, icmp0, sw_if_index0, + rx_fib_index0, node, next0, thread_index, + &ses0, &dm0); + goto trace00; + } + + key0.ext_host_addr = ip0->src_address; + key0.ext_host_port = tcp0->src; + key0.out_port = tcp0->dst; + + dm0 = snat_det_map_by_out(sm, &ip0->dst_address); + if (PREDICT_FALSE(!dm0)) + { + clib_warning("unknown dst address: %U", + format_ip4_address, &ip0->dst_address); + next0 = SNAT_OUT2IN_NEXT_DROP; + b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; + goto trace00; + } + + snat_det_reverse(dm0, &ip0->dst_address, + clib_net_to_host_u16(tcp0->dst), &new_addr0); + + ses0 = snat_det_get_ses_by_out (dm0, &new_addr0, key0.as_u64); + if (PREDICT_FALSE(!ses0)) + { + clib_warning("no match src %U:%d dst %U:%d for user %U", + format_ip4_address, &ip0->src_address, + clib_net_to_host_u16 (tcp0->src), + format_ip4_address, &ip0->dst_address, + clib_net_to_host_u16 (tcp0->dst), + format_ip4_address, &new_addr0); + next0 = SNAT_OUT2IN_NEXT_DROP; + b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; + goto trace00; + } + new_port0 = ses0->in_port; + + old_addr0 = ip0->dst_address; + ip0->dst_address = new_addr0; + vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm->inside_fib_index; + + sum0 = ip0->checksum; + sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32, + ip4_header_t, + dst_address /* changed member */); + ip0->checksum = ip_csum_fold (sum0); + + if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) + { + if (tcp0->flags & TCP_FLAG_FIN && ses0->state == SNAT_SESSION_TCP_ESTABLISHED) + ses0->state = SNAT_SESSION_TCP_CLOSE_WAIT; + else if (tcp0->flags & TCP_FLAG_ACK && ses0->state == SNAT_SESSION_TCP_LAST_ACK) + snat_det_ses_close(dm0, ses0); + + old_port0 = tcp0->dst; + tcp0->dst = new_port0; + + sum0 = tcp0->checksum; + sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32, + ip4_header_t, + dst_address /* changed member */); + + sum0 = ip_csum_update (sum0, old_port0, new_port0, + ip4_header_t /* cheat */, + length /* changed member */); + tcp0->checksum = ip_csum_fold(sum0); + } + else + { + old_port0 = udp0->dst_port; + udp0->dst_port = new_port0; + udp0->checksum = 0; + } + + trace00: + + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + snat_out2in_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = sw_if_index0; + t->next_index = next0; + t->session_index = ~0; + if (ses0) + t->session_index = ses0 - dm0->sessions; + } + + pkts_processed += next0 != SNAT_OUT2IN_NEXT_DROP; + + /* verify speculative enqueue, maybe switch current next frame */ + 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, snat_det_out2in_node.index, + SNAT_OUT2IN_ERROR_OUT2IN_PACKETS, + pkts_processed); + return frame->n_vectors; +} + +VLIB_REGISTER_NODE (snat_det_out2in_node) = { + .function = snat_det_out2in_node_fn, + .name = "nat44-det-out2in", + .vector_size = sizeof (u32), + .format_trace = format_snat_out2in_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(snat_out2in_error_strings), + .error_strings = snat_out2in_error_strings, + + .runtime_data_bytes = sizeof (snat_runtime_t), + + .n_next_nodes = SNAT_OUT2IN_N_NEXT, + + /* edit / add dispositions here */ + .next_nodes = { + [SNAT_OUT2IN_NEXT_DROP] = "error-drop", + [SNAT_OUT2IN_NEXT_LOOKUP] = "ip4-lookup", + [SNAT_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error", + }, +}; +VLIB_NODE_FUNCTION_MULTIARCH (snat_det_out2in_node, snat_det_out2in_node_fn); + +/** + * Get address and port values to be used for ICMP packet translation + * and create session if needed + * + * @param[in,out] sm NAT main + * @param[in,out] node NAT node runtime + * @param[in] thread_index thread index + * @param[in,out] b0 buffer containing packet to be translated + * @param[out] p_proto protocol used for matching + * @param[out] p_value address and port after NAT translation + * @param[out] p_dont_translate if packet should not be translated + * @param d optional parameter + * @param e optional parameter + */ +u32 icmp_match_out2in_det(snat_main_t *sm, vlib_node_runtime_t *node, + u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, + snat_session_key_t *p_value, + u8 *p_dont_translate, void *d, void *e) +{ + ip4_header_t *ip0; + icmp46_header_t *icmp0; + u32 sw_if_index0; + u8 protocol; + snat_det_out_key_t key0; + u8 dont_translate = 0; + u32 next0 = ~0; + icmp_echo_header_t *echo0, *inner_echo0 = 0; + ip4_header_t *inner_ip0; + void *l4_header = 0; + icmp46_header_t *inner_icmp0; + snat_det_map_t * dm0 = 0; + ip4_address_t new_addr0 = {{0}}; + snat_det_session_t * ses0 = 0; + ip4_address_t out_addr; + + ip0 = vlib_buffer_get_current (b0); + icmp0 = (icmp46_header_t *) ip4_next_header (ip0); + echo0 = (icmp_echo_header_t *)(icmp0+1); + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; + + if (!icmp_is_error_message (icmp0)) + { + protocol = SNAT_PROTOCOL_ICMP; + key0.ext_host_addr = ip0->src_address; + key0.ext_host_port = 0; + key0.out_port = echo0->identifier; + out_addr = ip0->dst_address; + } + else + { + inner_ip0 = (ip4_header_t *)(echo0+1); + l4_header = ip4_next_header (inner_ip0); + protocol = ip_proto_to_snat_proto (inner_ip0->protocol); + key0.ext_host_addr = inner_ip0->dst_address; + out_addr = inner_ip0->src_address; + switch (protocol) + { + case SNAT_PROTOCOL_ICMP: + inner_icmp0 = (icmp46_header_t*)l4_header; + inner_echo0 = (icmp_echo_header_t *)(inner_icmp0+1); + key0.ext_host_port = 0; + key0.out_port = inner_echo0->identifier; + break; + case SNAT_PROTOCOL_UDP: + case SNAT_PROTOCOL_TCP: + key0.ext_host_port = ((tcp_udp_header_t*)l4_header)->dst_port; + key0.out_port = ((tcp_udp_header_t*)l4_header)->src_port; + break; + default: + b0->error = node->errors[SNAT_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL]; + next0 = SNAT_OUT2IN_NEXT_DROP; + goto out; + } + } + + dm0 = snat_det_map_by_out(sm, &out_addr); + if (PREDICT_FALSE(!dm0)) + { + /* Don't NAT packet aimed at the intfc address */ + if (PREDICT_FALSE(is_interface_addr(sm, node, sw_if_index0, + ip0->dst_address.as_u32))) + { + dont_translate = 1; + goto out; + } + clib_warning("unknown dst address: %U", + format_ip4_address, &ip0->dst_address); + goto out; + } + + snat_det_reverse(dm0, &ip0->dst_address, + clib_net_to_host_u16(key0.out_port), &new_addr0); + + ses0 = snat_det_get_ses_by_out (dm0, &new_addr0, key0.as_u64); + if (PREDICT_FALSE(!ses0)) + { + /* Don't NAT packet aimed at the intfc address */ + if (PREDICT_FALSE(is_interface_addr(sm, node, sw_if_index0, + ip0->dst_address.as_u32))) + { + dont_translate = 1; + goto out; + } + clib_warning("no match src %U:%d dst %U:%d for user %U", + format_ip4_address, &key0.ext_host_addr, + clib_net_to_host_u16 (key0.ext_host_port), + format_ip4_address, &out_addr, + clib_net_to_host_u16 (key0.out_port), + format_ip4_address, &new_addr0); + b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; + next0 = SNAT_OUT2IN_NEXT_DROP; + goto out; + } + + if (PREDICT_FALSE(icmp0->type != ICMP4_echo_reply && + !icmp_is_error_message (icmp0))) + { + b0->error = node->errors[SNAT_OUT2IN_ERROR_BAD_ICMP_TYPE]; + next0 = SNAT_OUT2IN_NEXT_DROP; + goto out; + } + + goto out; + +out: + *p_proto = protocol; + if (ses0) + { + p_value->addr = new_addr0; + p_value->fib_index = sm->inside_fib_index; + p_value->port = ses0->in_port; + } + *p_dont_translate = dont_translate; + if (d) + *(snat_det_session_t**)d = ses0; + if (e) + *(snat_det_map_t**)e = dm0; + return next0; +} + +/**********************/ +/*** worker handoff ***/ +/**********************/ +static uword +snat_out2in_worker_handoff_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + snat_main_t *sm = &snat_main; + vlib_thread_main_t *tm = vlib_get_thread_main (); + u32 n_left_from, *from, *to_next = 0; + static __thread vlib_frame_queue_elt_t **handoff_queue_elt_by_worker_index; + static __thread vlib_frame_queue_t **congested_handoff_queue_by_worker_index + = 0; + vlib_frame_queue_elt_t *hf = 0; + vlib_frame_t *f = 0; + int i; + u32 n_left_to_next_worker = 0, *to_next_worker = 0; + u32 next_worker_index = 0; + u32 current_worker_index = ~0; + u32 thread_index = vlib_get_thread_index (); + + ASSERT (vec_len (sm->workers)); + + if (PREDICT_FALSE (handoff_queue_elt_by_worker_index == 0)) + { + vec_validate (handoff_queue_elt_by_worker_index, tm->n_vlib_mains - 1); + + vec_validate_init_empty (congested_handoff_queue_by_worker_index, + sm->first_worker_index + sm->num_workers - 1, + (vlib_frame_queue_t *) (~0)); + } + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + + while (n_left_from > 0) + { + u32 bi0; + vlib_buffer_t *b0; + u32 sw_if_index0; + u32 rx_fib_index0; + ip4_header_t * ip0; + u8 do_handoff; + + bi0 = from[0]; + from += 1; + n_left_from -= 1; + + b0 = vlib_get_buffer (vm, bi0); + + sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; + rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0); + + ip0 = vlib_buffer_get_current (b0); + + next_worker_index = sm->worker_out2in_cb(ip0, rx_fib_index0); + + if (PREDICT_FALSE (next_worker_index != thread_index)) + { + do_handoff = 1; + + if (next_worker_index != current_worker_index) + { + if (hf) + hf->n_vectors = VLIB_FRAME_SIZE - n_left_to_next_worker; + + hf = vlib_get_worker_handoff_queue_elt (sm->fq_out2in_index, + next_worker_index, + handoff_queue_elt_by_worker_index); + + n_left_to_next_worker = VLIB_FRAME_SIZE - hf->n_vectors; + to_next_worker = &hf->buffer_index[hf->n_vectors]; + current_worker_index = next_worker_index; + } + + /* enqueue to correct worker thread */ + to_next_worker[0] = bi0; + to_next_worker++; + n_left_to_next_worker--; + + if (n_left_to_next_worker == 0) + { + hf->n_vectors = VLIB_FRAME_SIZE; + vlib_put_frame_queue_elt (hf); + current_worker_index = ~0; + handoff_queue_elt_by_worker_index[next_worker_index] = 0; + hf = 0; + } + } + else + { + do_handoff = 0; + /* if this is 1st frame */ + if (!f) + { + f = vlib_get_frame_to_node (vm, sm->out2in_node_index); + to_next = vlib_frame_vector_args (f); + } + + to_next[0] = bi0; + to_next += 1; + f->n_vectors++; + } + + if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + snat_out2in_worker_handoff_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->next_worker_index = next_worker_index; + t->do_handoff = do_handoff; + } + } + + if (f) + vlib_put_frame_to_node (vm, sm->out2in_node_index, f); + + if (hf) + hf->n_vectors = VLIB_FRAME_SIZE - n_left_to_next_worker; + + /* Ship frames to the worker nodes */ + for (i = 0; i < vec_len (handoff_queue_elt_by_worker_index); i++) + { + if (handoff_queue_elt_by_worker_index[i]) + { + hf = handoff_queue_elt_by_worker_index[i]; + /* + * It works better to let the handoff node + * rate-adapt, always ship the handoff queue element. + */ + if (1 || hf->n_vectors == hf->last_n_vectors) + { + vlib_put_frame_queue_elt (hf); + handoff_queue_elt_by_worker_index[i] = 0; + } + else + hf->last_n_vectors = hf->n_vectors; + } + congested_handoff_queue_by_worker_index[i] = + (vlib_frame_queue_t *) (~0); + } + hf = 0; + current_worker_index = ~0; + return frame->n_vectors; +} + +VLIB_REGISTER_NODE (snat_out2in_worker_handoff_node) = { + .function = snat_out2in_worker_handoff_fn, + .name = "nat44-out2in-worker-handoff", + .vector_size = sizeof (u32), + .format_trace = format_snat_out2in_worker_handoff_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_next_nodes = 1, + + .next_nodes = { + [0] = "error-drop", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (snat_out2in_worker_handoff_node, snat_out2in_worker_handoff_fn); + +static uword +snat_out2in_fast_node_fn (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 n_left_from, * from, * to_next; + snat_out2in_next_t next_index; + u32 pkts_processed = 0; + snat_main_t * sm = &snat_main; + + 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; + u32 next0 = SNAT_OUT2IN_NEXT_DROP; + u32 sw_if_index0; + ip4_header_t * ip0; + ip_csum_t sum0; + u32 new_addr0, old_addr0; + u16 new_port0, old_port0; + udp_header_t * udp0; + tcp_header_t * tcp0; + icmp46_header_t * icmp0; + snat_session_key_t key0, sm0; + u32 proto0; + u32 rx_fib_index0; + + /* speculatively enqueue b0 to the current next frame */ + 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); + + ip0 = vlib_buffer_get_current (b0); + udp0 = ip4_next_header (ip0); + tcp0 = (tcp_header_t *) udp0; + icmp0 = (icmp46_header_t *) udp0; + + sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; + rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0); + + vnet_feature_next (sw_if_index0, &next0, b0); + + if (PREDICT_FALSE(ip0->ttl == 1)) + { + vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; + icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, + ICMP4_time_exceeded_ttl_exceeded_in_transit, + 0); + next0 = SNAT_OUT2IN_NEXT_ICMP_ERROR; + goto trace00; + } + + proto0 = ip_proto_to_snat_proto (ip0->protocol); + + if (PREDICT_FALSE (proto0 == ~0)) + goto trace00; + + if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP)) + { + next0 = icmp_out2in(sm, b0, ip0, icmp0, sw_if_index0, + rx_fib_index0, node, next0, ~0, 0, 0); + goto trace00; + } + + key0.addr = ip0->dst_address; + key0.port = udp0->dst_port; + key0.fib_index = rx_fib_index0; + + if (snat_static_mapping_match(sm, key0, &sm0, 1, 0)) + { + b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; + goto trace00; + } + + new_addr0 = sm0.addr.as_u32; + new_port0 = sm0.port; + vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index; + old_addr0 = ip0->dst_address.as_u32; + ip0->dst_address.as_u32 = new_addr0; + + sum0 = ip0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, + ip4_header_t, + dst_address /* changed member */); + ip0->checksum = ip_csum_fold (sum0); + + if (PREDICT_FALSE(new_port0 != udp0->dst_port)) + { + if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) + { + old_port0 = tcp0->dst_port; + tcp0->dst_port = new_port0; + + sum0 = tcp0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, + ip4_header_t, + dst_address /* changed member */); + + sum0 = ip_csum_update (sum0, old_port0, new_port0, + ip4_header_t /* cheat */, + length /* changed member */); + tcp0->checksum = ip_csum_fold(sum0); + } + else + { + old_port0 = udp0->dst_port; + udp0->dst_port = new_port0; + udp0->checksum = 0; + } + } + else + { + if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) + { + sum0 = tcp0->checksum; + sum0 = ip_csum_update (sum0, old_addr0, new_addr0, + ip4_header_t, + dst_address /* changed member */); + + tcp0->checksum = ip_csum_fold(sum0); + } + } + + trace00: + + if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) + && (b0->flags & VLIB_BUFFER_IS_TRACED))) + { + snat_out2in_trace_t *t = + vlib_add_trace (vm, node, b0, sizeof (*t)); + t->sw_if_index = sw_if_index0; + t->next_index = next0; + } + + pkts_processed += next0 != SNAT_OUT2IN_NEXT_DROP; + + /* verify speculative enqueue, maybe switch current next frame */ + 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, snat_out2in_fast_node.index, + SNAT_OUT2IN_ERROR_OUT2IN_PACKETS, + pkts_processed); + return frame->n_vectors; +} + +VLIB_REGISTER_NODE (snat_out2in_fast_node) = { + .function = snat_out2in_fast_node_fn, + .name = "nat44-out2in-fast", + .vector_size = sizeof (u32), + .format_trace = format_snat_out2in_fast_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + + .n_errors = ARRAY_LEN(snat_out2in_error_strings), + .error_strings = snat_out2in_error_strings, + + .runtime_data_bytes = sizeof (snat_runtime_t), + + .n_next_nodes = SNAT_OUT2IN_N_NEXT, + + /* edit / add dispositions here */ + .next_nodes = { + [SNAT_OUT2IN_NEXT_LOOKUP] = "ip4-lookup", + [SNAT_OUT2IN_NEXT_DROP] = "error-drop", + [SNAT_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error", + }, +}; +VLIB_NODE_FUNCTION_MULTIARCH (snat_out2in_fast_node, snat_out2in_fast_node_fn); diff --git a/src/plugins/snat.am b/src/plugins/snat.am deleted file mode 100644 index 6b75f3d2..00000000 --- a/src/plugins/snat.am +++ /dev/null @@ -1,41 +0,0 @@ - -# Copyright (c) -# 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 += snat_test_plugin.la -vppplugins_LTLIBRARIES += snat_plugin.la - -snat_plugin_la_SOURCES = snat/snat.c \ - snat/snat_api.c \ - snat/in2out.c \ - snat/out2in.c \ - snat/snat_plugin.api.h \ - snat/snat_ipfix_logging.c \ - snat/snat_det.c \ - snat/nat64.c \ - snat/nat64_cli.c \ - snat/nat64_in2out.c \ - snat/nat64_out2in.c \ - snat/nat64_db.c - -API_FILES += snat/snat.api - -nobase_apiinclude_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 - -# vi:syntax=automake diff --git a/src/plugins/snat/in2out.c b/src/plugins/snat/in2out.c deleted file mode 100644 index abe0d9db..00000000 --- a/src/plugins/snat/in2out.c +++ /dev/null @@ -1,3454 +0,0 @@ -/* - * 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 -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -typedef struct { - u32 sw_if_index; - u32 next_index; - u32 session_index; - u32 is_slow_path; -} snat_in2out_trace_t; - -typedef struct { - u32 next_worker_index; - u8 do_handoff; -} snat_in2out_worker_handoff_trace_t; - -/* packet trace format function */ -static u8 * format_snat_in2out_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 *); - snat_in2out_trace_t * t = va_arg (*args, snat_in2out_trace_t *); - char * tag; - - tag = t->is_slow_path ? "SNAT_IN2OUT_SLOW_PATH" : "SNAT_IN2OUT_FAST_PATH"; - - s = format (s, "%s: sw_if_index %d, next index %d, session %d", tag, - t->sw_if_index, t->next_index, t->session_index); - - return s; -} - -static u8 * format_snat_in2out_fast_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 *); - snat_in2out_trace_t * t = va_arg (*args, snat_in2out_trace_t *); - - s = format (s, "SANT_IN2OUT_FAST: sw_if_index %d, next index %d", - t->sw_if_index, t->next_index); - - return s; -} - -static u8 * format_snat_in2out_worker_handoff_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 *); - snat_in2out_worker_handoff_trace_t * t = - va_arg (*args, snat_in2out_worker_handoff_trace_t *); - char * m; - - m = t->do_handoff ? "next worker" : "same worker"; - s = format (s, "SNAT_IN2OUT_WORKER_HANDOFF: %s %d", m, t->next_worker_index); - - return s; -} - -vlib_node_registration_t snat_in2out_node; -vlib_node_registration_t snat_in2out_slowpath_node; -vlib_node_registration_t snat_in2out_fast_node; -vlib_node_registration_t snat_in2out_worker_handoff_node; -vlib_node_registration_t snat_det_in2out_node; -vlib_node_registration_t snat_in2out_output_node; -vlib_node_registration_t snat_in2out_output_slowpath_node; -vlib_node_registration_t snat_in2out_output_worker_handoff_node; -vlib_node_registration_t snat_hairpin_dst_node; -vlib_node_registration_t snat_hairpin_src_node; - - -#define foreach_snat_in2out_error \ -_(UNSUPPORTED_PROTOCOL, "Unsupported protocol") \ -_(IN2OUT_PACKETS, "Good in2out packets processed") \ -_(OUT_OF_PORTS, "Out of ports") \ -_(BAD_OUTSIDE_FIB, "Outside VRF ID not found") \ -_(BAD_ICMP_TYPE, "unsupported ICMP type") \ -_(NO_TRANSLATION, "No translation") - -typedef enum { -#define _(sym,str) SNAT_IN2OUT_ERROR_##sym, - foreach_snat_in2out_error -#undef _ - SNAT_IN2OUT_N_ERROR, -} snat_in2out_error_t; - -static char * snat_in2out_error_strings[] = { -#define _(sym,string) string, - foreach_snat_in2out_error -#undef _ -}; - -typedef enum { - SNAT_IN2OUT_NEXT_LOOKUP, - SNAT_IN2OUT_NEXT_DROP, - SNAT_IN2OUT_NEXT_ICMP_ERROR, - SNAT_IN2OUT_NEXT_SLOW_PATH, - SNAT_IN2OUT_N_NEXT, -} snat_in2out_next_t; - -typedef enum { - SNAT_HAIRPIN_SRC_NEXT_DROP, - SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT, - SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH, - SNAT_HAIRPIN_SRC_NEXT_INTERFACE_OUTPUT, - SNAT_HAIRPIN_SRC_N_NEXT, -} snat_hairpin_next_t; - -/** - * @brief Check if packet should be translated - * - * Packets aimed at outside interface and external addresss with active session - * should be translated. - * - * @param sm SNAT main - * @param rt SNAT runtime data - * @param sw_if_index0 index of the inside interface - * @param ip0 IPv4 header - * @param proto0 SNAT protocol - * @param rx_fib_index0 RX FIB index - * - * @returns 0 if packet should be translated otherwise 1 - */ -static inline int -snat_not_translate_fast (snat_main_t * sm, vlib_node_runtime_t *node, - u32 sw_if_index0, ip4_header_t * ip0, u32 proto0, - u32 rx_fib_index0) -{ - fib_node_index_t fei = FIB_NODE_INDEX_INVALID; - fib_prefix_t pfx = { - .fp_proto = FIB_PROTOCOL_IP4, - .fp_len = 32, - .fp_addr = { - .ip4.as_u32 = ip0->dst_address.as_u32, - }, - }; - - /* Don't NAT packet aimed at the intfc address */ - if (PREDICT_FALSE(is_interface_addr(sm, node, sw_if_index0, - ip0->dst_address.as_u32))) - return 1; - - fei = fib_table_lookup (rx_fib_index0, &pfx); - if (FIB_NODE_INDEX_INVALID != fei) - { - u32 sw_if_index = fib_entry_get_resolving_interface (fei); - if (sw_if_index == ~0) - { - fei = fib_table_lookup (sm->outside_fib_index, &pfx); - if (FIB_NODE_INDEX_INVALID != fei) - sw_if_index = fib_entry_get_resolving_interface (fei); - } - snat_interface_t *i; - pool_foreach (i, sm->interfaces, - ({ - /* NAT packet aimed at outside interface */ - if ((i->is_inside == 0) && (sw_if_index == i->sw_if_index)) - return 0; - })); - } - - return 1; -} - -static inline int -snat_not_translate (snat_main_t * sm, vlib_node_runtime_t *node, - u32 sw_if_index0, ip4_header_t * ip0, u32 proto0, - u32 rx_fib_index0) -{ - udp_header_t * udp0 = ip4_next_header (ip0); - snat_session_key_t key0, sm0; - clib_bihash_kv_8_8_t kv0, value0; - - key0.addr = ip0->dst_address; - key0.port = udp0->dst_port; - key0.protocol = proto0; - key0.fib_index = sm->outside_fib_index; - kv0.key = key0.as_u64; - - /* NAT packet aimed at external address if */ - /* has active sessions */ - if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0)) - { - /* or is static mappings */ - if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0)) - return 0; - } - else - return 0; - - return snat_not_translate_fast(sm, node, sw_if_index0, ip0, proto0, - rx_fib_index0); -} - -static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0, - ip4_header_t * ip0, - u32 rx_fib_index0, - snat_session_key_t * key0, - snat_session_t ** sessionp, - vlib_node_runtime_t * node, - u32 next0, - u32 thread_index) -{ - snat_user_t *u; - snat_user_key_t user_key; - snat_session_t *s; - clib_bihash_kv_8_8_t kv0, value0; - u32 oldest_per_user_translation_list_index; - dlist_elt_t * oldest_per_user_translation_list_elt; - dlist_elt_t * per_user_translation_list_elt; - dlist_elt_t * per_user_list_head_elt; - u32 session_index; - snat_session_key_t key1; - u32 address_index = ~0; - u32 outside_fib_index; - uword * p; - snat_worker_key_t worker_by_out_key; - - p = hash_get (sm->ip4_main->fib_index_by_table_id, sm->outside_vrf_id); - if (! p) - { - b0->error = node->errors[SNAT_IN2OUT_ERROR_BAD_OUTSIDE_FIB]; - return SNAT_IN2OUT_NEXT_DROP; - } - outside_fib_index = p[0]; - - key1.protocol = key0->protocol; - user_key.addr = ip0->src_address; - user_key.fib_index = rx_fib_index0; - kv0.key = user_key.as_u64; - - /* Ever heard of the "user" = src ip4 address before? */ - if (clib_bihash_search_8_8 (&sm->user_hash, &kv0, &value0)) - { - /* no, make a new one */ - pool_get (sm->per_thread_data[thread_index].users, u); - memset (u, 0, sizeof (*u)); - u->addr = ip0->src_address; - u->fib_index = rx_fib_index0; - - pool_get (sm->per_thread_data[thread_index].list_pool, per_user_list_head_elt); - - u->sessions_per_user_list_head_index = per_user_list_head_elt - - sm->per_thread_data[thread_index].list_pool; - - clib_dlist_init (sm->per_thread_data[thread_index].list_pool, - u->sessions_per_user_list_head_index); - - kv0.value = u - sm->per_thread_data[thread_index].users; - - /* add user */ - clib_bihash_add_del_8_8 (&sm->user_hash, &kv0, 1 /* is_add */); - } - else - { - u = pool_elt_at_index (sm->per_thread_data[thread_index].users, - value0.value); - } - - /* Over quota? Recycle the least recently used dynamic translation */ - if (u->nsessions >= sm->max_translations_per_user) - { - /* Remove the oldest dynamic translation */ - do { - oldest_per_user_translation_list_index = - clib_dlist_remove_head (sm->per_thread_data[thread_index].list_pool, - u->sessions_per_user_list_head_index); - - ASSERT (oldest_per_user_translation_list_index != ~0); - - /* add it back to the end of the LRU list */ - clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool, - u->sessions_per_user_list_head_index, - oldest_per_user_translation_list_index); - /* Get the list element */ - oldest_per_user_translation_list_elt = - pool_elt_at_index (sm->per_thread_data[thread_index].list_pool, - oldest_per_user_translation_list_index); - - /* Get the session index from the list element */ - session_index = oldest_per_user_translation_list_elt->value; - - /* Get the session */ - s = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, - session_index); - } while (snat_is_session_static (s)); - - if (snat_is_unk_proto_session (s)) - { - clib_bihash_kv_16_8_t up_kv; - snat_unk_proto_ses_key_t key; - - /* Remove from lookup tables */ - key.l_addr = s->in2out.addr; - key.r_addr = s->ext_host_addr; - key.fib_index = s->in2out.fib_index; - key.proto = s->in2out.port; - up_kv.key[0] = key.as_u64[0]; - up_kv.key[1] = key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->in2out_unk_proto, &up_kv, 0)) - clib_warning ("in2out key del failed"); - - key.l_addr = s->out2in.addr; - key.fib_index = s->out2in.fib_index; - up_kv.key[0] = key.as_u64[0]; - up_kv.key[1] = key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->out2in_unk_proto, &up_kv, 0)) - clib_warning ("out2in key del failed"); - } - else - { - /* Remove in2out, out2in keys */ - kv0.key = s->in2out.as_u64; - if (clib_bihash_add_del_8_8 (&sm->in2out, &kv0, 0 /* is_add */)) - clib_warning ("in2out key delete failed"); - kv0.key = s->out2in.as_u64; - if (clib_bihash_add_del_8_8 (&sm->out2in, &kv0, 0 /* is_add */)) - clib_warning ("out2in key delete failed"); - - /* log NAT event */ - snat_ipfix_logging_nat44_ses_delete(s->in2out.addr.as_u32, - s->out2in.addr.as_u32, - s->in2out.protocol, - s->in2out.port, - s->out2in.port, - s->in2out.fib_index); - - snat_free_outside_address_and_port - (sm, &s->out2in, s->outside_address_index); - } - s->outside_address_index = ~0; - - if (snat_alloc_outside_address_and_port (sm, rx_fib_index0, thread_index, - &key1, &address_index)) - { - ASSERT(0); - - b0->error = node->errors[SNAT_IN2OUT_ERROR_OUT_OF_PORTS]; - return SNAT_IN2OUT_NEXT_DROP; - } - s->outside_address_index = address_index; - } - else - { - u8 static_mapping = 1; - - /* First try to match static mapping by local address and port */ - if (snat_static_mapping_match (sm, *key0, &key1, 0, 0)) - { - static_mapping = 0; - /* Try to create dynamic translation */ - if (snat_alloc_outside_address_and_port (sm, rx_fib_index0, - thread_index, &key1, - &address_index)) - { - b0->error = node->errors[SNAT_IN2OUT_ERROR_OUT_OF_PORTS]; - return SNAT_IN2OUT_NEXT_DROP; - } - } - - /* Create a new session */ - pool_get (sm->per_thread_data[thread_index].sessions, s); - memset (s, 0, sizeof (*s)); - - s->outside_address_index = address_index; - - if (static_mapping) - { - u->nstaticsessions++; - s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; - } - else - { - u->nsessions++; - } - - /* Create list elts */ - pool_get (sm->per_thread_data[thread_index].list_pool, - per_user_translation_list_elt); - clib_dlist_init (sm->per_thread_data[thread_index].list_pool, - per_user_translation_list_elt - - sm->per_thread_data[thread_index].list_pool); - - per_user_translation_list_elt->value = - s - sm->per_thread_data[thread_index].sessions; - s->per_user_index = per_user_translation_list_elt - - sm->per_thread_data[thread_index].list_pool; - s->per_user_list_head_index = u->sessions_per_user_list_head_index; - - clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool, - s->per_user_list_head_index, - per_user_translation_list_elt - - sm->per_thread_data[thread_index].list_pool); - } - - s->in2out = *key0; - s->out2in = key1; - s->out2in.protocol = key0->protocol; - s->out2in.fib_index = outside_fib_index; - s->ext_host_addr.as_u32 = ip0->dst_address.as_u32; - *sessionp = s; - - /* Add to translation hashes */ - kv0.key = s->in2out.as_u64; - kv0.value = s - sm->per_thread_data[thread_index].sessions; - if (clib_bihash_add_del_8_8 (&sm->in2out, &kv0, 1 /* is_add */)) - clib_warning ("in2out key add failed"); - - kv0.key = s->out2in.as_u64; - kv0.value = s - sm->per_thread_data[thread_index].sessions; - - if (clib_bihash_add_del_8_8 (&sm->out2in, &kv0, 1 /* is_add */)) - clib_warning ("out2in key add failed"); - - /* Add to translated packets worker lookup */ - worker_by_out_key.addr = s->out2in.addr; - worker_by_out_key.port = s->out2in.port; - worker_by_out_key.fib_index = s->out2in.fib_index; - kv0.key = worker_by_out_key.as_u64; - kv0.value = thread_index; - clib_bihash_add_del_8_8 (&sm->worker_by_out, &kv0, 1); - - /* log NAT event */ - snat_ipfix_logging_nat44_ses_create(s->in2out.addr.as_u32, - s->out2in.addr.as_u32, - s->in2out.protocol, - s->in2out.port, - s->out2in.port, - s->in2out.fib_index); - return next0; -} - -static_always_inline -snat_in2out_error_t icmp_get_key(ip4_header_t *ip0, - snat_session_key_t *p_key0) -{ - icmp46_header_t *icmp0; - snat_session_key_t key0; - icmp_echo_header_t *echo0, *inner_echo0 = 0; - ip4_header_t *inner_ip0 = 0; - void *l4_header = 0; - icmp46_header_t *inner_icmp0; - - icmp0 = (icmp46_header_t *) ip4_next_header (ip0); - echo0 = (icmp_echo_header_t *)(icmp0+1); - - if (!icmp_is_error_message (icmp0)) - { - key0.protocol = SNAT_PROTOCOL_ICMP; - key0.addr = ip0->src_address; - key0.port = echo0->identifier; - } - else - { - inner_ip0 = (ip4_header_t *)(echo0+1); - l4_header = ip4_next_header (inner_ip0); - key0.protocol = ip_proto_to_snat_proto (inner_ip0->protocol); - key0.addr = inner_ip0->dst_address; - switch (key0.protocol) - { - case SNAT_PROTOCOL_ICMP: - inner_icmp0 = (icmp46_header_t*)l4_header; - inner_echo0 = (icmp_echo_header_t *)(inner_icmp0+1); - key0.port = inner_echo0->identifier; - break; - case SNAT_PROTOCOL_UDP: - case SNAT_PROTOCOL_TCP: - key0.port = ((tcp_udp_header_t*)l4_header)->dst_port; - break; - default: - return SNAT_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL; - } - } - *p_key0 = key0; - return -1; /* success */ -} - -/** - * Get address and port values to be used for packet SNAT translation - * and create session if needed - * - * @param[in,out] sm SNAT main - * @param[in,out] node SNAT node runtime - * @param[in] thread_index thread index - * @param[in,out] b0 buffer containing packet to be translated - * @param[out] p_proto protocol used for matching - * @param[out] p_value address and port after NAT translation - * @param[out] p_dont_translate if packet should not be translated - * @param d optional parameter - * @param e optional parameter - */ -u32 icmp_match_in2out_slow(snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, - snat_session_key_t *p_value, - u8 *p_dont_translate, void *d, void *e) -{ - ip4_header_t *ip0; - icmp46_header_t *icmp0; - u32 sw_if_index0; - u32 rx_fib_index0; - snat_session_key_t key0; - snat_session_t *s0 = 0; - u8 dont_translate = 0; - clib_bihash_kv_8_8_t kv0, value0; - u32 next0 = ~0; - int err; - u32 iph_offset0 = 0; - - if (PREDICT_FALSE(vnet_buffer(b0)->sw_if_index[VLIB_TX] != ~0)) - { - iph_offset0 = vnet_buffer (b0)->ip.save_rewrite_length; - } - ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0) + iph_offset0); - icmp0 = (icmp46_header_t *) ip4_next_header (ip0); - sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; - rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0); - - err = icmp_get_key (ip0, &key0); - if (err != -1) - { - b0->error = node->errors[err]; - next0 = SNAT_IN2OUT_NEXT_DROP; - goto out; - } - key0.fib_index = rx_fib_index0; - - kv0.key = key0.as_u64; - - if (clib_bihash_search_8_8 (&sm->in2out, &kv0, &value0)) - { - if (PREDICT_FALSE(snat_not_translate(sm, node, sw_if_index0, ip0, - IP_PROTOCOL_ICMP, rx_fib_index0) && - vnet_buffer(b0)->sw_if_index[VLIB_TX] == ~0)) - { - dont_translate = 1; - goto out; - } - - if (PREDICT_FALSE(icmp_is_error_message (icmp0))) - { - b0->error = node->errors[SNAT_IN2OUT_ERROR_BAD_ICMP_TYPE]; - next0 = SNAT_IN2OUT_NEXT_DROP; - goto out; - } - - next0 = slow_path (sm, b0, ip0, rx_fib_index0, &key0, - &s0, node, next0, thread_index); - - if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP)) - goto out; - } - else - { - if (PREDICT_FALSE(icmp0->type != ICMP4_echo_request && - icmp0->type != ICMP4_echo_reply && - !icmp_is_error_message (icmp0))) - { - b0->error = node->errors[SNAT_IN2OUT_ERROR_BAD_ICMP_TYPE]; - next0 = SNAT_IN2OUT_NEXT_DROP; - goto out; - } - - s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, - value0.value); - } - -out: - *p_proto = key0.protocol; - if (s0) - *p_value = s0->out2in; - *p_dont_translate = dont_translate; - if (d) - *(snat_session_t**)d = s0; - return next0; -} - -/** - * Get address and port values to be used for packet SNAT translation - * - * @param[in] sm SNAT main - * @param[in,out] node SNAT node runtime - * @param[in] thread_index thread index - * @param[in,out] b0 buffer containing packet to be translated - * @param[out] p_proto protocol used for matching - * @param[out] p_value address and port after NAT translation - * @param[out] p_dont_translate if packet should not be translated - * @param d optional parameter - * @param e optional parameter - */ -u32 icmp_match_in2out_fast(snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, - snat_session_key_t *p_value, - u8 *p_dont_translate, void *d, void *e) -{ - ip4_header_t *ip0; - icmp46_header_t *icmp0; - u32 sw_if_index0; - u32 rx_fib_index0; - snat_session_key_t key0; - snat_session_key_t sm0; - u8 dont_translate = 0; - u8 is_addr_only; - u32 next0 = ~0; - int err; - - ip0 = vlib_buffer_get_current (b0); - icmp0 = (icmp46_header_t *) ip4_next_header (ip0); - sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; - rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0); - - err = icmp_get_key (ip0, &key0); - if (err != -1) - { - b0->error = node->errors[err]; - next0 = SNAT_IN2OUT_NEXT_DROP; - goto out2; - } - key0.fib_index = rx_fib_index0; - - if (snat_static_mapping_match(sm, key0, &sm0, 0, &is_addr_only)) - { - if (PREDICT_FALSE(snat_not_translate_fast(sm, node, sw_if_index0, ip0, - IP_PROTOCOL_ICMP, rx_fib_index0))) - { - dont_translate = 1; - goto out; - } - - if (icmp_is_error_message (icmp0)) - { - next0 = SNAT_IN2OUT_NEXT_DROP; - goto out; - } - - b0->error = node->errors[SNAT_IN2OUT_ERROR_NO_TRANSLATION]; - next0 = SNAT_IN2OUT_NEXT_DROP; - goto out; - } - - if (PREDICT_FALSE(icmp0->type != ICMP4_echo_request && - (icmp0->type != ICMP4_echo_reply || !is_addr_only) && - !icmp_is_error_message (icmp0))) - { - b0->error = node->errors[SNAT_IN2OUT_ERROR_BAD_ICMP_TYPE]; - next0 = SNAT_IN2OUT_NEXT_DROP; - goto out; - } - -out: - *p_value = sm0; -out2: - *p_proto = key0.protocol; - *p_dont_translate = dont_translate; - return next0; -} - -static inline u32 icmp_in2out (snat_main_t *sm, - vlib_buffer_t * b0, - ip4_header_t * ip0, - icmp46_header_t * icmp0, - u32 sw_if_index0, - u32 rx_fib_index0, - vlib_node_runtime_t * node, - u32 next0, - u32 thread_index, - void *d, - void *e) -{ - snat_session_key_t sm0; - u8 protocol; - icmp_echo_header_t *echo0, *inner_echo0 = 0; - ip4_header_t *inner_ip0; - void *l4_header = 0; - icmp46_header_t *inner_icmp0; - u8 dont_translate; - u32 new_addr0, old_addr0; - u16 old_id0, new_id0; - ip_csum_t sum0; - u16 checksum0; - u32 next0_tmp; - - echo0 = (icmp_echo_header_t *)(icmp0+1); - - next0_tmp = sm->icmp_match_in2out_cb(sm, node, thread_index, b0, - &protocol, &sm0, &dont_translate, d, e); - if (next0_tmp != ~0) - next0 = next0_tmp; - if (next0 == SNAT_IN2OUT_NEXT_DROP || dont_translate) - goto out; - - sum0 = ip_incremental_checksum (0, icmp0, - ntohs(ip0->length) - ip4_header_bytes (ip0)); - checksum0 = ~ip_csum_fold (sum0); - if (PREDICT_FALSE(checksum0 != 0 && checksum0 != 0xffff)) - { - next0 = SNAT_IN2OUT_NEXT_DROP; - goto out; - } - - old_addr0 = ip0->src_address.as_u32; - new_addr0 = ip0->src_address.as_u32 = sm0.addr.as_u32; - if (vnet_buffer(b0)->sw_if_index[VLIB_TX] == ~0) - vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index; - - sum0 = ip0->checksum; - sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, - src_address /* changed member */); - ip0->checksum = ip_csum_fold (sum0); - - if (!icmp_is_error_message (icmp0)) - { - new_id0 = sm0.port; - if (PREDICT_FALSE(new_id0 != echo0->identifier)) - { - old_id0 = echo0->identifier; - new_id0 = sm0.port; - echo0->identifier = new_id0; - - sum0 = icmp0->checksum; - sum0 = ip_csum_update (sum0, old_id0, new_id0, icmp_echo_header_t, - identifier); - icmp0->checksum = ip_csum_fold (sum0); - } - } - else - { - inner_ip0 = (ip4_header_t *)(echo0+1); - l4_header = ip4_next_header (inner_ip0); - - if (!ip4_header_checksum_is_valid (inner_ip0)) - { - next0 = SNAT_IN2OUT_NEXT_DROP; - goto out; - } - - old_addr0 = inner_ip0->dst_address.as_u32; - inner_ip0->dst_address = sm0.addr; - new_addr0 = inner_ip0->dst_address.as_u32; - - sum0 = icmp0->checksum; - sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, - dst_address /* changed member */); - icmp0->checksum = ip_csum_fold (sum0); - - switch (protocol) - { - case SNAT_PROTOCOL_ICMP: - inner_icmp0 = (icmp46_header_t*)l4_header; - inner_echo0 = (icmp_echo_header_t *)(inner_icmp0+1); - - old_id0 = inner_echo0->identifier; - new_id0 = sm0.port; - inner_echo0->identifier = new_id0; - - sum0 = icmp0->checksum; - sum0 = ip_csum_update (sum0, old_id0, new_id0, icmp_echo_header_t, - identifier); - icmp0->checksum = ip_csum_fold (sum0); - break; - case SNAT_PROTOCOL_UDP: - case SNAT_PROTOCOL_TCP: - old_id0 = ((tcp_udp_header_t*)l4_header)->dst_port; - new_id0 = sm0.port; - ((tcp_udp_header_t*)l4_header)->dst_port = new_id0; - - sum0 = icmp0->checksum; - sum0 = ip_csum_update (sum0, old_id0, new_id0, tcp_udp_header_t, - dst_port); - icmp0->checksum = ip_csum_fold (sum0); - break; - default: - ASSERT(0); - } - } - -out: - return next0; -} - -/** - * @brief Hairpinning - * - * Hairpinning allows two endpoints on the internal side of the NAT to - * communicate even if they only use each other's external IP addresses - * and ports. - * - * @param sm SNAT main. - * @param b0 Vlib buffer. - * @param ip0 IP header. - * @param udp0 UDP header. - * @param tcp0 TCP header. - * @param proto0 SNAT protocol. - */ -static inline void -snat_hairpinning (snat_main_t *sm, - vlib_buffer_t * b0, - ip4_header_t * ip0, - udp_header_t * udp0, - tcp_header_t * tcp0, - u32 proto0) -{ - snat_session_key_t key0, sm0; - snat_worker_key_t k0; - snat_session_t * s0; - clib_bihash_kv_8_8_t kv0, value0; - ip_csum_t sum0; - u32 new_dst_addr0 = 0, old_dst_addr0, ti = 0, si; - u16 new_dst_port0, old_dst_port0; - - key0.addr = ip0->dst_address; - key0.port = udp0->dst_port; - key0.protocol = proto0; - key0.fib_index = sm->outside_fib_index; - kv0.key = key0.as_u64; - - /* Check if destination is in active sessions */ - if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0)) - { - /* or static mappings */ - if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0)) - { - new_dst_addr0 = sm0.addr.as_u32; - new_dst_port0 = sm0.port; - vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index; - } - } - else - { - si = value0.value; - if (sm->num_workers > 1) - { - k0.addr = ip0->dst_address; - k0.port = udp0->dst_port; - k0.fib_index = sm->outside_fib_index; - kv0.key = k0.as_u64; - if (clib_bihash_search_8_8 (&sm->worker_by_out, &kv0, &value0)) - ASSERT(0); - else - ti = value0.value; - } - else - ti = sm->num_workers; - - s0 = pool_elt_at_index (sm->per_thread_data[ti].sessions, si); - new_dst_addr0 = s0->in2out.addr.as_u32; - new_dst_port0 = s0->in2out.port; - vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index; - } - - /* Destination is behind the same NAT, use internal address and port */ - if (new_dst_addr0) - { - old_dst_addr0 = ip0->dst_address.as_u32; - ip0->dst_address.as_u32 = new_dst_addr0; - sum0 = ip0->checksum; - sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0, - ip4_header_t, dst_address); - ip0->checksum = ip_csum_fold (sum0); - - old_dst_port0 = tcp0->dst; - if (PREDICT_TRUE(new_dst_port0 != old_dst_port0)) - { - if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) - { - tcp0->dst = new_dst_port0; - sum0 = tcp0->checksum; - sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0, - ip4_header_t, dst_address); - sum0 = ip_csum_update (sum0, old_dst_port0, new_dst_port0, - ip4_header_t /* cheat */, length); - tcp0->checksum = ip_csum_fold(sum0); - } - else - { - udp0->dst_port = new_dst_port0; - udp0->checksum = 0; - } - } - else - { - if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) - { - sum0 = tcp0->checksum; - sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0, - ip4_header_t, dst_address); - tcp0->checksum = ip_csum_fold(sum0); - } - } - } -} - -static inline void -snat_icmp_hairpinning (snat_main_t *sm, - vlib_buffer_t * b0, - ip4_header_t * ip0, - icmp46_header_t * icmp0) -{ - snat_session_key_t key0, sm0; - clib_bihash_kv_8_8_t kv0, value0; - snat_worker_key_t k0; - u32 new_dst_addr0 = 0, old_dst_addr0, si, ti = 0; - ip_csum_t sum0; - snat_session_t *s0; - - if (!icmp_is_error_message (icmp0)) - { - icmp_echo_header_t *echo0 = (icmp_echo_header_t *)(icmp0+1); - u16 icmp_id0 = echo0->identifier; - key0.addr = ip0->dst_address; - key0.port = icmp_id0; - key0.protocol = SNAT_PROTOCOL_ICMP; - key0.fib_index = sm->outside_fib_index; - kv0.key = key0.as_u64; - - /* Check if destination is in active sessions */ - if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0)) - { - /* or static mappings */ - if (!snat_static_mapping_match(sm, key0, &sm0, 1, 0)) - { - new_dst_addr0 = sm0.addr.as_u32; - vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index; - } - } - else - { - si = value0.value; - if (sm->num_workers > 1) - { - k0.addr = ip0->dst_address; - k0.port = icmp_id0; - k0.fib_index = sm->outside_fib_index; - kv0.key = k0.as_u64; - if (clib_bihash_search_8_8 (&sm->worker_by_out, &kv0, &value0)) - ASSERT(0); - else - ti = value0.value; - } - else - ti = sm->num_workers; - - s0 = pool_elt_at_index (sm->per_thread_data[ti].sessions, si); - new_dst_addr0 = s0->in2out.addr.as_u32; - vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index; - echo0->identifier = s0->in2out.port; - sum0 = icmp0->checksum; - sum0 = ip_csum_update (sum0, icmp_id0, s0->in2out.port, - icmp_echo_header_t, identifier); - icmp0->checksum = ip_csum_fold (sum0); - } - - /* Destination is behind the same NAT, use internal address and port */ - if (new_dst_addr0) - { - old_dst_addr0 = ip0->dst_address.as_u32; - ip0->dst_address.as_u32 = new_dst_addr0; - sum0 = ip0->checksum; - sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0, - ip4_header_t, dst_address); - ip0->checksum = ip_csum_fold (sum0); - } - } - -} - -static inline u32 icmp_in2out_slow_path (snat_main_t *sm, - vlib_buffer_t * b0, - ip4_header_t * ip0, - icmp46_header_t * icmp0, - u32 sw_if_index0, - u32 rx_fib_index0, - vlib_node_runtime_t * node, - u32 next0, - f64 now, - u32 thread_index, - snat_session_t ** p_s0) -{ - next0 = icmp_in2out(sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, - next0, thread_index, p_s0, 0); - snat_session_t * s0 = *p_s0; - if (PREDICT_TRUE(next0 != SNAT_IN2OUT_NEXT_DROP && s0)) - { - /* Hairpinning */ - if (vnet_buffer(b0)->sw_if_index[VLIB_TX] == 0) - snat_icmp_hairpinning(sm, b0, ip0, icmp0); - /* Accounting */ - s0->last_heard = now; - s0->total_pkts++; - s0->total_bytes += vlib_buffer_length_in_chain (sm->vlib_main, b0); - /* Per-user LRU list maintenance for dynamic translations */ - if (!snat_is_session_static (s0)) - { - clib_dlist_remove (sm->per_thread_data[thread_index].list_pool, - s0->per_user_index); - clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool, - s0->per_user_list_head_index, - s0->per_user_index); - } - } - return next0; -} -static inline void -snat_hairpinning_unknown_proto (snat_main_t *sm, - vlib_buffer_t * b, - ip4_header_t * ip) -{ - u32 old_addr, new_addr = 0, ti = 0; - clib_bihash_kv_8_8_t kv, value; - clib_bihash_kv_16_8_t s_kv, s_value; - snat_unk_proto_ses_key_t key; - snat_session_key_t m_key; - snat_worker_key_t w_key; - snat_static_mapping_t *m; - ip_csum_t sum; - snat_session_t *s; - - old_addr = ip->dst_address.as_u32; - key.l_addr.as_u32 = ip->dst_address.as_u32; - key.r_addr.as_u32 = ip->src_address.as_u32; - key.fib_index = sm->outside_fib_index; - key.proto = ip->protocol; - key.rsvd[0] = key.rsvd[1] = key.rsvd[2] = 0; - s_kv.key[0] = key.as_u64[0]; - s_kv.key[1] = key.as_u64[1]; - if (clib_bihash_search_16_8 (&sm->out2in_unk_proto, &s_kv, &s_value)) - { - m_key.addr = ip->dst_address; - m_key.fib_index = sm->outside_fib_index; - m_key.port = 0; - m_key.protocol = 0; - kv.key = m_key.as_u64; - if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) - return; - - m = pool_elt_at_index (sm->static_mappings, value.value); - if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0) - vnet_buffer(b)->sw_if_index[VLIB_TX] = m->fib_index; - new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32; - } - else - { - if (sm->num_workers > 1) - { - w_key.addr = ip->dst_address; - w_key.port = 0; - w_key.fib_index = sm->outside_fib_index; - kv.key = w_key.as_u64; - if (clib_bihash_search_8_8 (&sm->worker_by_out, &kv, &value)) - return; - else - ti = value.value; - } - else - ti = sm->num_workers; - - s = pool_elt_at_index (sm->per_thread_data[ti].sessions, s_value.value); - if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0) - vnet_buffer(b)->sw_if_index[VLIB_TX] = s->in2out.fib_index; - new_addr = ip->dst_address.as_u32 = s->in2out.addr.as_u32; - } - sum = ip->checksum; - sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address); - ip->checksum = ip_csum_fold (sum); -} - -static void -snat_in2out_unknown_proto (snat_main_t *sm, - vlib_buffer_t * b, - ip4_header_t * ip, - u32 rx_fib_index, - u32 thread_index, - f64 now, - vlib_main_t * vm) -{ - clib_bihash_kv_8_8_t kv, value; - clib_bihash_kv_16_8_t s_kv, s_value; - snat_static_mapping_t *m; - snat_session_key_t m_key; - u32 old_addr, new_addr = 0; - ip_csum_t sum; - snat_user_key_t u_key; - snat_user_t *u; - dlist_elt_t *head, *elt, *oldest; - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; - u32 elt_index, head_index, ses_index, oldest_index; - snat_session_t * s; - snat_unk_proto_ses_key_t key; - u32 address_index = ~0; - int i; - u8 is_sm = 0; - - old_addr = ip->src_address.as_u32; - - key.l_addr = ip->src_address; - key.r_addr = ip->dst_address; - key.fib_index = rx_fib_index; - key.proto = ip->protocol; - key.rsvd[0] = key.rsvd[1] = key.rsvd[2] = 0; - s_kv.key[0] = key.as_u64[0]; - s_kv.key[1] = key.as_u64[1]; - - if (!clib_bihash_search_16_8 (&sm->in2out_unk_proto, &s_kv, &s_value)) - { - s = pool_elt_at_index (tsm->sessions, s_value.value); - new_addr = ip->src_address.as_u32 = s->out2in.addr.as_u32; - } - else - { - u_key.addr = ip->src_address; - u_key.fib_index = rx_fib_index; - kv.key = u_key.as_u64; - - /* Ever heard of the "user" = src ip4 address before? */ - if (clib_bihash_search_8_8 (&sm->user_hash, &kv, &value)) - { - /* no, make a new one */ - pool_get (tsm->users, u); - memset (u, 0, sizeof (*u)); - u->addr = ip->src_address; - u->fib_index = rx_fib_index; - - pool_get (tsm->list_pool, head); - u->sessions_per_user_list_head_index = head - tsm->list_pool; - - clib_dlist_init (tsm->list_pool, - u->sessions_per_user_list_head_index); - - kv.value = u - tsm->users; - - /* add user */ - clib_bihash_add_del_8_8 (&sm->user_hash, &kv, 1); - } - else - { - u = pool_elt_at_index (tsm->users, value.value); - } - - m_key.addr = ip->src_address; - m_key.port = 0; - m_key.protocol = 0; - m_key.fib_index = rx_fib_index; - kv.key = m_key.as_u64; - - /* Try to find static mapping first */ - if (!clib_bihash_search_8_8 (&sm->static_mapping_by_local, &kv, &value)) - { - m = pool_elt_at_index (sm->static_mappings, value.value); - new_addr = ip->src_address.as_u32 = m->external_addr.as_u32; - is_sm = 1; - goto create_ses; - } - /* Fallback to 3-tuple key */ - else - { - /* Choose same out address as for TCP/UDP session to same destination */ - if (!clib_bihash_search_8_8 (&sm->user_hash, &kv, &value)) - { - head_index = u->sessions_per_user_list_head_index; - head = pool_elt_at_index (tsm->list_pool, head_index); - elt_index = head->next; - elt = pool_elt_at_index (tsm->list_pool, elt_index); - ses_index = elt->value; - while (ses_index != ~0) - { - s = pool_elt_at_index (tsm->sessions, ses_index); - elt_index = elt->next; - elt = pool_elt_at_index (tsm->list_pool, elt_index); - ses_index = elt->value; - - if (s->ext_host_addr.as_u32 == ip->dst_address.as_u32) - { - new_addr = ip->src_address.as_u32 = s->out2in.addr.as_u32; - address_index = s->outside_address_index; - - key.fib_index = sm->outside_fib_index; - key.l_addr.as_u32 = new_addr; - s_kv.key[0] = key.as_u64[0]; - s_kv.key[1] = key.as_u64[1]; - if (clib_bihash_search_16_8 (&sm->out2in_unk_proto, &s_kv, &s_value)) - break; - - goto create_ses; - } - } - } - key.fib_index = sm->outside_fib_index; - for (i = 0; i < vec_len (sm->addresses); i++) - { - key.l_addr.as_u32 = sm->addresses[i].addr.as_u32; - s_kv.key[0] = key.as_u64[0]; - s_kv.key[1] = key.as_u64[1]; - if (clib_bihash_search_16_8 (&sm->out2in_unk_proto, &s_kv, &s_value)) - { - new_addr = ip->src_address.as_u32 = key.l_addr.as_u32; - address_index = i; - goto create_ses; - } - } - return; - } - -create_ses: - /* Over quota? Recycle the least recently used dynamic translation */ - if (u->nsessions >= sm->max_translations_per_user && !is_sm) - { - /* Remove the oldest dynamic translation */ - do { - oldest_index = clib_dlist_remove_head ( - tsm->list_pool, u->sessions_per_user_list_head_index); - - ASSERT (oldest_index != ~0); - - /* add it back to the end of the LRU list */ - clib_dlist_addtail (tsm->list_pool, - u->sessions_per_user_list_head_index, - oldest_index); - /* Get the list element */ - oldest = pool_elt_at_index (tsm->list_pool, oldest_index); - - /* Get the session index from the list element */ - ses_index = oldest->value; - - /* Get the session */ - s = pool_elt_at_index (tsm->sessions, ses_index); - } while (snat_is_session_static (s)); - - if (snat_is_unk_proto_session (s)) - { - /* Remove from lookup tables */ - key.l_addr = s->in2out.addr; - key.r_addr = s->ext_host_addr; - key.fib_index = s->in2out.fib_index; - key.proto = s->in2out.port; - s_kv.key[0] = key.as_u64[0]; - s_kv.key[1] = key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->in2out_unk_proto, &s_kv, 0)) - clib_warning ("in2out key del failed"); - - key.l_addr = s->out2in.addr; - key.fib_index = s->out2in.fib_index; - s_kv.key[0] = key.as_u64[0]; - s_kv.key[1] = key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->out2in_unk_proto, &s_kv, 0)) - clib_warning ("out2in key del failed"); - } - else - { - /* log NAT event */ - snat_ipfix_logging_nat44_ses_delete(s->in2out.addr.as_u32, - s->out2in.addr.as_u32, - s->in2out.protocol, - s->in2out.port, - s->out2in.port, - s->in2out.fib_index); - - snat_free_outside_address_and_port (sm, &s->out2in, - s->outside_address_index); - - /* Remove in2out, out2in keys */ - kv.key = s->in2out.as_u64; - if (clib_bihash_add_del_8_8 (&sm->in2out, &kv, 0)) - clib_warning ("in2out key del failed"); - kv.key = s->out2in.as_u64; - if (clib_bihash_add_del_8_8 (&sm->out2in, &kv, 0)) - clib_warning ("out2in key del failed"); - } - } - else - { - /* Create a new session */ - pool_get (tsm->sessions, s); - memset (s, 0, sizeof (*s)); - - /* Create list elts */ - pool_get (tsm->list_pool, elt); - clib_dlist_init (tsm->list_pool, elt - tsm->list_pool); - elt->value = s - tsm->sessions; - s->per_user_index = elt - tsm->list_pool; - s->per_user_list_head_index = u->sessions_per_user_list_head_index; - clib_dlist_addtail (tsm->list_pool, s->per_user_list_head_index, - s->per_user_index); - } - - s->ext_host_addr.as_u32 = ip->dst_address.as_u32; - s->flags |= SNAT_SESSION_FLAG_UNKNOWN_PROTO; - s->outside_address_index = address_index; - s->out2in.addr.as_u32 = new_addr; - s->out2in.fib_index = sm->outside_fib_index; - s->in2out.addr.as_u32 = old_addr; - s->in2out.fib_index = rx_fib_index; - s->in2out.port = s->out2in.port = ip->protocol; - if (is_sm) - { - u->nstaticsessions++; - s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; - } - else - { - u->nsessions++; - } - - /* Add to lookup tables */ - key.l_addr.as_u32 = old_addr; - key.r_addr = ip->dst_address; - key.proto = ip->protocol; - key.fib_index = rx_fib_index; - s_kv.key[0] = key.as_u64[0]; - s_kv.key[1] = key.as_u64[1]; - s_kv.value = s - tsm->sessions; - if (clib_bihash_add_del_16_8 (&sm->in2out_unk_proto, &s_kv, 1)) - clib_warning ("in2out key add failed"); - - key.l_addr.as_u32 = new_addr; - key.fib_index = sm->outside_fib_index; - s_kv.key[0] = key.as_u64[0]; - s_kv.key[1] = key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->out2in_unk_proto, &s_kv, 1)) - clib_warning ("out2in key add failed"); - } - - /* Update IP checksum */ - sum = ip->checksum; - sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, src_address); - ip->checksum = ip_csum_fold (sum); - - /* Accounting */ - s->last_heard = now; - s->total_pkts++; - s->total_bytes += vlib_buffer_length_in_chain (vm, b); - /* Per-user LRU list maintenance */ - clib_dlist_remove (tsm->list_pool, s->per_user_index); - clib_dlist_addtail (tsm->list_pool, s->per_user_list_head_index, - s->per_user_index); - - /* Hairpinning */ - if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0) - snat_hairpinning_unknown_proto(sm, b, ip); - - if (vnet_buffer(b)->sw_if_index[VLIB_TX] == ~0) - vnet_buffer(b)->sw_if_index[VLIB_TX] = sm->outside_fib_index; -} - -static inline uword -snat_in2out_node_fn_inline (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame, int is_slow_path, - int is_output_feature) -{ - u32 n_left_from, * from, * to_next; - snat_in2out_next_t next_index; - u32 pkts_processed = 0; - snat_main_t * sm = &snat_main; - f64 now = vlib_time_now (vm); - u32 stats_node_index; - u32 thread_index = vlib_get_thread_index (); - - stats_node_index = is_slow_path ? snat_in2out_slowpath_node.index : - snat_in2out_node.index; - - 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 >= 4 && n_left_to_next >= 2) - { - u32 bi0, bi1; - vlib_buffer_t * b0, * b1; - u32 next0, next1; - u32 sw_if_index0, sw_if_index1; - ip4_header_t * ip0, * ip1; - ip_csum_t sum0, sum1; - u32 new_addr0, old_addr0, new_addr1, old_addr1; - u16 old_port0, new_port0, old_port1, new_port1; - udp_header_t * udp0, * udp1; - tcp_header_t * tcp0, * tcp1; - icmp46_header_t * icmp0, * icmp1; - snat_session_key_t key0, key1; - u32 rx_fib_index0, rx_fib_index1; - u32 proto0, proto1; - snat_session_t * s0 = 0, * s1 = 0; - clib_bihash_kv_8_8_t kv0, value0, kv1, value1; - u32 iph_offset0 = 0, iph_offset1 = 0; - - /* Prefetch next iteration. */ - { - vlib_buffer_t * p2, * p3; - - p2 = vlib_get_buffer (vm, from[2]); - p3 = vlib_get_buffer (vm, from[3]); - - vlib_prefetch_buffer_header (p2, LOAD); - vlib_prefetch_buffer_header (p3, LOAD); - - CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE); - CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE); - } - - /* speculatively enqueue b0 and b1 to the current next frame */ - to_next[0] = bi0 = from[0]; - to_next[1] = bi1 = from[1]; - from += 2; - to_next += 2; - n_left_from -= 2; - n_left_to_next -= 2; - - b0 = vlib_get_buffer (vm, bi0); - b1 = vlib_get_buffer (vm, bi1); - - if (is_output_feature) - iph_offset0 = vnet_buffer (b0)->ip.save_rewrite_length; - - ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0) + - iph_offset0); - - udp0 = ip4_next_header (ip0); - tcp0 = (tcp_header_t *) udp0; - icmp0 = (icmp46_header_t *) udp0; - - sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; - rx_fib_index0 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index, - sw_if_index0); - - next0 = next1 = SNAT_IN2OUT_NEXT_LOOKUP; - - if (PREDICT_FALSE(ip0->ttl == 1)) - { - vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; - icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, - ICMP4_time_exceeded_ttl_exceeded_in_transit, - 0); - next0 = SNAT_IN2OUT_NEXT_ICMP_ERROR; - goto trace00; - } - - proto0 = ip_proto_to_snat_proto (ip0->protocol); - - /* Next configured feature, probably ip4-lookup */ - if (is_slow_path) - { - if (PREDICT_FALSE (proto0 == ~0)) - { - snat_in2out_unknown_proto (sm, b0, ip0, rx_fib_index0, - thread_index, now, vm); - goto trace00; - } - - if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP)) - { - next0 = icmp_in2out_slow_path - (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, - node, next0, now, thread_index, &s0); - goto trace00; - } - } - else - { - if (PREDICT_FALSE (proto0 == ~0 || proto0 == SNAT_PROTOCOL_ICMP)) - { - next0 = SNAT_IN2OUT_NEXT_SLOW_PATH; - goto trace00; - } - } - - key0.addr = ip0->src_address; - key0.port = udp0->src_port; - key0.protocol = proto0; - key0.fib_index = rx_fib_index0; - - kv0.key = key0.as_u64; - - if (PREDICT_FALSE (clib_bihash_search_8_8 (&sm->in2out, &kv0, &value0) != 0)) - { - if (is_slow_path) - { - if (PREDICT_FALSE(snat_not_translate(sm, node, sw_if_index0, - ip0, proto0, rx_fib_index0)) && !is_output_feature) - goto trace00; - - next0 = slow_path (sm, b0, ip0, rx_fib_index0, &key0, - &s0, node, next0, thread_index); - if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP)) - goto trace00; - } - else - { - next0 = SNAT_IN2OUT_NEXT_SLOW_PATH; - goto trace00; - } - } - else - s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, - value0.value); - - old_addr0 = ip0->src_address.as_u32; - ip0->src_address = s0->out2in.addr; - new_addr0 = ip0->src_address.as_u32; - if (!is_output_feature) - vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->out2in.fib_index; - - sum0 = ip0->checksum; - sum0 = ip_csum_update (sum0, old_addr0, new_addr0, - ip4_header_t, - src_address /* changed member */); - ip0->checksum = ip_csum_fold (sum0); - - if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) - { - old_port0 = tcp0->src_port; - tcp0->src_port = s0->out2in.port; - new_port0 = tcp0->src_port; - - sum0 = tcp0->checksum; - sum0 = ip_csum_update (sum0, old_addr0, new_addr0, - ip4_header_t, - dst_address /* changed member */); - sum0 = ip_csum_update (sum0, old_port0, new_port0, - ip4_header_t /* cheat */, - length /* changed member */); - tcp0->checksum = ip_csum_fold(sum0); - } - else - { - old_port0 = udp0->src_port; - udp0->src_port = s0->out2in.port; - udp0->checksum = 0; - } - - /* Hairpinning */ - if (!is_output_feature) - snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0); - - /* Accounting */ - s0->last_heard = now; - s0->total_pkts++; - s0->total_bytes += vlib_buffer_length_in_chain (vm, b0); - /* Per-user LRU list maintenance for dynamic translation */ - if (!snat_is_session_static (s0)) - { - clib_dlist_remove (sm->per_thread_data[thread_index].list_pool, - s0->per_user_index); - clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool, - s0->per_user_list_head_index, - s0->per_user_index); - } - trace00: - - if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - snat_in2out_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->is_slow_path = is_slow_path; - t->sw_if_index = sw_if_index0; - t->next_index = next0; - t->session_index = ~0; - if (s0) - t->session_index = s0 - sm->per_thread_data[thread_index].sessions; - } - - pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP; - - if (is_output_feature) - iph_offset1 = vnet_buffer (b1)->ip.save_rewrite_length; - - ip1 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b1) + - iph_offset1); - - udp1 = ip4_next_header (ip1); - tcp1 = (tcp_header_t *) udp1; - icmp1 = (icmp46_header_t *) udp1; - - sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX]; - rx_fib_index1 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index, - sw_if_index1); - - if (PREDICT_FALSE(ip1->ttl == 1)) - { - vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0; - icmp4_error_set_vnet_buffer (b1, ICMP4_time_exceeded, - ICMP4_time_exceeded_ttl_exceeded_in_transit, - 0); - next1 = SNAT_IN2OUT_NEXT_ICMP_ERROR; - goto trace01; - } - - proto1 = ip_proto_to_snat_proto (ip1->protocol); - - /* Next configured feature, probably ip4-lookup */ - if (is_slow_path) - { - if (PREDICT_FALSE (proto1 == ~0)) - { - snat_in2out_unknown_proto (sm, b1, ip1, rx_fib_index1, - thread_index, now, vm); - goto trace01; - } - - if (PREDICT_FALSE (proto1 == SNAT_PROTOCOL_ICMP)) - { - next1 = icmp_in2out_slow_path - (sm, b1, ip1, icmp1, sw_if_index1, rx_fib_index1, node, - next1, now, thread_index, &s1); - goto trace01; - } - } - else - { - if (PREDICT_FALSE (proto1 == ~0 || proto1 == SNAT_PROTOCOL_ICMP)) - { - next1 = SNAT_IN2OUT_NEXT_SLOW_PATH; - goto trace01; - } - } - - key1.addr = ip1->src_address; - key1.port = udp1->src_port; - key1.protocol = proto1; - key1.fib_index = rx_fib_index1; - - kv1.key = key1.as_u64; - - if (PREDICT_FALSE(clib_bihash_search_8_8 (&sm->in2out, &kv1, &value1) != 0)) - { - if (is_slow_path) - { - if (PREDICT_FALSE(snat_not_translate(sm, node, sw_if_index1, - ip1, proto1, rx_fib_index1)) && !is_output_feature) - goto trace01; - - next1 = slow_path (sm, b1, ip1, rx_fib_index1, &key1, - &s1, node, next1, thread_index); - if (PREDICT_FALSE (next1 == SNAT_IN2OUT_NEXT_DROP)) - goto trace01; - } - else - { - next1 = SNAT_IN2OUT_NEXT_SLOW_PATH; - goto trace01; - } - } - else - s1 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, - value1.value); - - old_addr1 = ip1->src_address.as_u32; - ip1->src_address = s1->out2in.addr; - new_addr1 = ip1->src_address.as_u32; - if (!is_output_feature) - vnet_buffer(b1)->sw_if_index[VLIB_TX] = s1->out2in.fib_index; - - sum1 = ip1->checksum; - sum1 = ip_csum_update (sum1, old_addr1, new_addr1, - ip4_header_t, - src_address /* changed member */); - ip1->checksum = ip_csum_fold (sum1); - - if (PREDICT_TRUE(proto1 == SNAT_PROTOCOL_TCP)) - { - old_port1 = tcp1->src_port; - tcp1->src_port = s1->out2in.port; - new_port1 = tcp1->src_port; - - sum1 = tcp1->checksum; - sum1 = ip_csum_update (sum1, old_addr1, new_addr1, - ip4_header_t, - dst_address /* changed member */); - sum1 = ip_csum_update (sum1, old_port1, new_port1, - ip4_header_t /* cheat */, - length /* changed member */); - tcp1->checksum = ip_csum_fold(sum1); - } - else - { - old_port1 = udp1->src_port; - udp1->src_port = s1->out2in.port; - udp1->checksum = 0; - } - - /* Hairpinning */ - if (!is_output_feature) - snat_hairpinning (sm, b1, ip1, udp1, tcp1, proto1); - - /* Accounting */ - s1->last_heard = now; - s1->total_pkts++; - s1->total_bytes += vlib_buffer_length_in_chain (vm, b1); - /* Per-user LRU list maintenance for dynamic translation */ - if (!snat_is_session_static (s1)) - { - clib_dlist_remove (sm->per_thread_data[thread_index].list_pool, - s1->per_user_index); - clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool, - s1->per_user_list_head_index, - s1->per_user_index); - } - trace01: - - if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) - && (b1->flags & VLIB_BUFFER_IS_TRACED))) - { - snat_in2out_trace_t *t = - vlib_add_trace (vm, node, b1, sizeof (*t)); - t->sw_if_index = sw_if_index1; - t->next_index = next1; - t->session_index = ~0; - if (s1) - t->session_index = s1 - sm->per_thread_data[thread_index].sessions; - } - - pkts_processed += next1 != SNAT_IN2OUT_NEXT_DROP; - - /* verify speculative enqueues, maybe switch current next frame */ - vlib_validate_buffer_enqueue_x2 (vm, node, next_index, - to_next, n_left_to_next, - bi0, bi1, next0, next1); - } - - while (n_left_from > 0 && n_left_to_next > 0) - { - u32 bi0; - vlib_buffer_t * b0; - u32 next0; - u32 sw_if_index0; - ip4_header_t * ip0; - ip_csum_t sum0; - u32 new_addr0, old_addr0; - u16 old_port0, new_port0; - udp_header_t * udp0; - tcp_header_t * tcp0; - icmp46_header_t * icmp0; - snat_session_key_t key0; - u32 rx_fib_index0; - u32 proto0; - snat_session_t * s0 = 0; - clib_bihash_kv_8_8_t kv0, value0; - u32 iph_offset0 = 0; - - /* speculatively enqueue b0 to the current next frame */ - 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); - next0 = SNAT_IN2OUT_NEXT_LOOKUP; - - if (is_output_feature) - iph_offset0 = vnet_buffer (b0)->ip.save_rewrite_length; - - ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0) + - iph_offset0); - - udp0 = ip4_next_header (ip0); - tcp0 = (tcp_header_t *) udp0; - icmp0 = (icmp46_header_t *) udp0; - - sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; - rx_fib_index0 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index, - sw_if_index0); - - if (PREDICT_FALSE(ip0->ttl == 1)) - { - vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; - icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, - ICMP4_time_exceeded_ttl_exceeded_in_transit, - 0); - next0 = SNAT_IN2OUT_NEXT_ICMP_ERROR; - goto trace0; - } - - proto0 = ip_proto_to_snat_proto (ip0->protocol); - - /* Next configured feature, probably ip4-lookup */ - if (is_slow_path) - { - if (PREDICT_FALSE (proto0 == ~0)) - { - snat_in2out_unknown_proto (sm, b0, ip0, rx_fib_index0, - thread_index, now, vm); - goto trace0; - } - - if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP)) - { - next0 = icmp_in2out_slow_path - (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, - next0, now, thread_index, &s0); - goto trace0; - } - } - else - { - if (PREDICT_FALSE (proto0 == ~0 || proto0 == SNAT_PROTOCOL_ICMP)) - { - next0 = SNAT_IN2OUT_NEXT_SLOW_PATH; - goto trace0; - } - } - - key0.addr = ip0->src_address; - key0.port = udp0->src_port; - key0.protocol = proto0; - key0.fib_index = rx_fib_index0; - - kv0.key = key0.as_u64; - - if (clib_bihash_search_8_8 (&sm->in2out, &kv0, &value0)) - { - if (is_slow_path) - { - if (PREDICT_FALSE(snat_not_translate(sm, node, sw_if_index0, - ip0, proto0, rx_fib_index0)) && !is_output_feature) - goto trace0; - - next0 = slow_path (sm, b0, ip0, rx_fib_index0, &key0, - &s0, node, next0, thread_index); - - if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP)) - goto trace0; - } - else - { - next0 = SNAT_IN2OUT_NEXT_SLOW_PATH; - goto trace0; - } - } - else - s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, - value0.value); - - old_addr0 = ip0->src_address.as_u32; - ip0->src_address = s0->out2in.addr; - new_addr0 = ip0->src_address.as_u32; - if (!is_output_feature) - vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->out2in.fib_index; - - sum0 = ip0->checksum; - sum0 = ip_csum_update (sum0, old_addr0, new_addr0, - ip4_header_t, - src_address /* changed member */); - ip0->checksum = ip_csum_fold (sum0); - - if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) - { - old_port0 = tcp0->src_port; - tcp0->src_port = s0->out2in.port; - new_port0 = tcp0->src_port; - - sum0 = tcp0->checksum; - sum0 = ip_csum_update (sum0, old_addr0, new_addr0, - ip4_header_t, - dst_address /* changed member */); - sum0 = ip_csum_update (sum0, old_port0, new_port0, - ip4_header_t /* cheat */, - length /* changed member */); - tcp0->checksum = ip_csum_fold(sum0); - } - else - { - old_port0 = udp0->src_port; - udp0->src_port = s0->out2in.port; - udp0->checksum = 0; - } - - /* Hairpinning */ - if (!is_output_feature) - snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0); - - /* Accounting */ - s0->last_heard = now; - s0->total_pkts++; - s0->total_bytes += vlib_buffer_length_in_chain (vm, b0); - /* Per-user LRU list maintenance for dynamic translation */ - if (!snat_is_session_static (s0)) - { - clib_dlist_remove (sm->per_thread_data[thread_index].list_pool, - s0->per_user_index); - clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool, - s0->per_user_list_head_index, - s0->per_user_index); - } - - trace0: - if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - snat_in2out_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->is_slow_path = is_slow_path; - t->sw_if_index = sw_if_index0; - t->next_index = next0; - t->session_index = ~0; - if (s0) - t->session_index = s0 - sm->per_thread_data[thread_index].sessions; - } - - pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP; - - /* verify speculative enqueue, maybe switch current next frame */ - 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, stats_node_index, - SNAT_IN2OUT_ERROR_IN2OUT_PACKETS, - pkts_processed); - return frame->n_vectors; -} - -static uword -snat_in2out_fast_path_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - return snat_in2out_node_fn_inline (vm, node, frame, 0 /* is_slow_path */, 0); -} - -VLIB_REGISTER_NODE (snat_in2out_node) = { - .function = snat_in2out_fast_path_fn, - .name = "snat-in2out", - .vector_size = sizeof (u32), - .format_trace = format_snat_in2out_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - - .n_errors = ARRAY_LEN(snat_in2out_error_strings), - .error_strings = snat_in2out_error_strings, - - .runtime_data_bytes = sizeof (snat_runtime_t), - - .n_next_nodes = SNAT_IN2OUT_N_NEXT, - - /* edit / add dispositions here */ - .next_nodes = { - [SNAT_IN2OUT_NEXT_DROP] = "error-drop", - [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup", - [SNAT_IN2OUT_NEXT_SLOW_PATH] = "snat-in2out-slowpath", - [SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", - }, -}; - -VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_node, snat_in2out_fast_path_fn); - -static uword -snat_in2out_output_fast_path_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - return snat_in2out_node_fn_inline (vm, node, frame, 0 /* is_slow_path */, 1); -} - -VLIB_REGISTER_NODE (snat_in2out_output_node) = { - .function = snat_in2out_output_fast_path_fn, - .name = "snat-in2out-output", - .vector_size = sizeof (u32), - .format_trace = format_snat_in2out_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - - .n_errors = ARRAY_LEN(snat_in2out_error_strings), - .error_strings = snat_in2out_error_strings, - - .runtime_data_bytes = sizeof (snat_runtime_t), - - .n_next_nodes = SNAT_IN2OUT_N_NEXT, - - /* edit / add dispositions here */ - .next_nodes = { - [SNAT_IN2OUT_NEXT_DROP] = "error-drop", - [SNAT_IN2OUT_NEXT_LOOKUP] = "interface-output", - [SNAT_IN2OUT_NEXT_SLOW_PATH] = "snat-in2out-output-slowpath", - [SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", - }, -}; - -VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_output_node, - snat_in2out_output_fast_path_fn); - -static uword -snat_in2out_slow_path_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - return snat_in2out_node_fn_inline (vm, node, frame, 1 /* is_slow_path */, 0); -} - -VLIB_REGISTER_NODE (snat_in2out_slowpath_node) = { - .function = snat_in2out_slow_path_fn, - .name = "snat-in2out-slowpath", - .vector_size = sizeof (u32), - .format_trace = format_snat_in2out_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - - .n_errors = ARRAY_LEN(snat_in2out_error_strings), - .error_strings = snat_in2out_error_strings, - - .runtime_data_bytes = sizeof (snat_runtime_t), - - .n_next_nodes = SNAT_IN2OUT_N_NEXT, - - /* edit / add dispositions here */ - .next_nodes = { - [SNAT_IN2OUT_NEXT_DROP] = "error-drop", - [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup", - [SNAT_IN2OUT_NEXT_SLOW_PATH] = "snat-in2out-slowpath", - [SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", - }, -}; - -VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_slowpath_node, - snat_in2out_slow_path_fn); - -static uword -snat_in2out_output_slow_path_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - return snat_in2out_node_fn_inline (vm, node, frame, 1 /* is_slow_path */, 1); -} - -VLIB_REGISTER_NODE (snat_in2out_output_slowpath_node) = { - .function = snat_in2out_output_slow_path_fn, - .name = "snat-in2out-output-slowpath", - .vector_size = sizeof (u32), - .format_trace = format_snat_in2out_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - - .n_errors = ARRAY_LEN(snat_in2out_error_strings), - .error_strings = snat_in2out_error_strings, - - .runtime_data_bytes = sizeof (snat_runtime_t), - - .n_next_nodes = SNAT_IN2OUT_N_NEXT, - - /* edit / add dispositions here */ - .next_nodes = { - [SNAT_IN2OUT_NEXT_DROP] = "error-drop", - [SNAT_IN2OUT_NEXT_LOOKUP] = "interface-output", - [SNAT_IN2OUT_NEXT_SLOW_PATH] = "snat-in2out-output-slowpath", - [SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", - }, -}; - -VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_output_slowpath_node, - snat_in2out_output_slow_path_fn); - -/**************************/ -/*** deterministic mode ***/ -/**************************/ -static uword -snat_det_in2out_node_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - u32 n_left_from, * from, * to_next; - snat_in2out_next_t next_index; - u32 pkts_processed = 0; - snat_main_t * sm = &snat_main; - u32 now = (u32) vlib_time_now (vm); - u32 thread_index = vlib_get_thread_index (); - - 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 >= 4 && n_left_to_next >= 2) - { - u32 bi0, bi1; - vlib_buffer_t * b0, * b1; - u32 next0, next1; - u32 sw_if_index0, sw_if_index1; - ip4_header_t * ip0, * ip1; - ip_csum_t sum0, sum1; - ip4_address_t new_addr0, old_addr0, new_addr1, old_addr1; - u16 old_port0, new_port0, lo_port0, i0; - u16 old_port1, new_port1, lo_port1, i1; - udp_header_t * udp0, * udp1; - tcp_header_t * tcp0, * tcp1; - u32 proto0, proto1; - snat_det_out_key_t key0, key1; - snat_det_map_t * dm0, * dm1; - snat_det_session_t * ses0 = 0, * ses1 = 0; - u32 rx_fib_index0, rx_fib_index1; - icmp46_header_t * icmp0, * icmp1; - - /* Prefetch next iteration. */ - { - vlib_buffer_t * p2, * p3; - - p2 = vlib_get_buffer (vm, from[2]); - p3 = vlib_get_buffer (vm, from[3]); - - vlib_prefetch_buffer_header (p2, LOAD); - vlib_prefetch_buffer_header (p3, LOAD); - - CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE); - CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE); - } - - /* speculatively enqueue b0 and b1 to the current next frame */ - to_next[0] = bi0 = from[0]; - to_next[1] = bi1 = from[1]; - from += 2; - to_next += 2; - n_left_from -= 2; - n_left_to_next -= 2; - - b0 = vlib_get_buffer (vm, bi0); - b1 = vlib_get_buffer (vm, bi1); - - next0 = SNAT_IN2OUT_NEXT_LOOKUP; - next1 = SNAT_IN2OUT_NEXT_LOOKUP; - - ip0 = vlib_buffer_get_current (b0); - udp0 = ip4_next_header (ip0); - tcp0 = (tcp_header_t *) udp0; - - sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; - - if (PREDICT_FALSE(ip0->ttl == 1)) - { - vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; - icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, - ICMP4_time_exceeded_ttl_exceeded_in_transit, - 0); - next0 = SNAT_IN2OUT_NEXT_ICMP_ERROR; - goto trace0; - } - - proto0 = ip_proto_to_snat_proto (ip0->protocol); - - if (PREDICT_FALSE(proto0 == SNAT_PROTOCOL_ICMP)) - { - rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0); - icmp0 = (icmp46_header_t *) udp0; - - next0 = icmp_in2out(sm, b0, ip0, icmp0, sw_if_index0, - rx_fib_index0, node, next0, thread_index, - &ses0, &dm0); - goto trace0; - } - - dm0 = snat_det_map_by_user(sm, &ip0->src_address); - if (PREDICT_FALSE(!dm0)) - { - clib_warning("no match for internal host %U", - format_ip4_address, &ip0->src_address); - next0 = SNAT_IN2OUT_NEXT_DROP; - b0->error = node->errors[SNAT_IN2OUT_ERROR_NO_TRANSLATION]; - goto trace0; - } - - snat_det_forward(dm0, &ip0->src_address, &new_addr0, &lo_port0); - - key0.ext_host_addr = ip0->dst_address; - key0.ext_host_port = tcp0->dst; - - ses0 = snat_det_find_ses_by_in(dm0, &ip0->src_address, tcp0->src, key0); - if (PREDICT_FALSE(!ses0)) - { - for (i0 = 0; i0 < dm0->ports_per_host; i0++) - { - key0.out_port = clib_host_to_net_u16 (lo_port0 + - ((i0 + clib_net_to_host_u16 (tcp0->src)) % dm0->ports_per_host)); - - if (snat_det_get_ses_by_out (dm0, &ip0->src_address, key0.as_u64)) - continue; - - ses0 = snat_det_ses_create(dm0, &ip0->src_address, tcp0->src, &key0); - break; - } - if (PREDICT_FALSE(!ses0)) - { - /* too many sessions for user, send ICMP error packet */ - - vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; - icmp4_error_set_vnet_buffer (b0, ICMP4_destination_unreachable, - ICMP4_destination_unreachable_destination_unreachable_host, - 0); - next0 = SNAT_IN2OUT_NEXT_ICMP_ERROR; - goto trace0; - } - } - - new_port0 = ses0->out.out_port; - - old_addr0.as_u32 = ip0->src_address.as_u32; - ip0->src_address.as_u32 = new_addr0.as_u32; - vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm->outside_fib_index; - - sum0 = ip0->checksum; - sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32, - ip4_header_t, - src_address /* changed member */); - ip0->checksum = ip_csum_fold (sum0); - - if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) - { - if (tcp0->flags & TCP_FLAG_SYN) - ses0->state = SNAT_SESSION_TCP_SYN_SENT; - else if (tcp0->flags & TCP_FLAG_ACK && ses0->state == SNAT_SESSION_TCP_SYN_SENT) - ses0->state = SNAT_SESSION_TCP_ESTABLISHED; - else if (tcp0->flags & TCP_FLAG_FIN && ses0->state == SNAT_SESSION_TCP_ESTABLISHED) - ses0->state = SNAT_SESSION_TCP_FIN_WAIT; - else if (tcp0->flags & TCP_FLAG_ACK && ses0->state == SNAT_SESSION_TCP_FIN_WAIT) - snat_det_ses_close(dm0, ses0); - else if (tcp0->flags & TCP_FLAG_FIN && ses0->state == SNAT_SESSION_TCP_CLOSE_WAIT) - ses0->state = SNAT_SESSION_TCP_LAST_ACK; - else if (tcp0->flags == 0 && ses0->state == SNAT_SESSION_UNKNOWN) - ses0->state = SNAT_SESSION_TCP_ESTABLISHED; - - old_port0 = tcp0->src; - tcp0->src = new_port0; - - sum0 = tcp0->checksum; - sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32, - ip4_header_t, - dst_address /* changed member */); - sum0 = ip_csum_update (sum0, old_port0, new_port0, - ip4_header_t /* cheat */, - length /* changed member */); - tcp0->checksum = ip_csum_fold(sum0); - } - else - { - ses0->state = SNAT_SESSION_UDP_ACTIVE; - old_port0 = udp0->src_port; - udp0->src_port = new_port0; - udp0->checksum = 0; - } - - switch(ses0->state) - { - case SNAT_SESSION_UDP_ACTIVE: - ses0->expire = now + sm->udp_timeout; - break; - case SNAT_SESSION_TCP_SYN_SENT: - case SNAT_SESSION_TCP_FIN_WAIT: - case SNAT_SESSION_TCP_CLOSE_WAIT: - case SNAT_SESSION_TCP_LAST_ACK: - ses0->expire = now + sm->tcp_transitory_timeout; - break; - case SNAT_SESSION_TCP_ESTABLISHED: - ses0->expire = now + sm->tcp_established_timeout; - break; - } - - trace0: - if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - snat_in2out_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->is_slow_path = 0; - t->sw_if_index = sw_if_index0; - t->next_index = next0; - t->session_index = ~0; - if (ses0) - t->session_index = ses0 - dm0->sessions; - } - - pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP; - - ip1 = vlib_buffer_get_current (b1); - udp1 = ip4_next_header (ip1); - tcp1 = (tcp_header_t *) udp1; - - sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX]; - - if (PREDICT_FALSE(ip1->ttl == 1)) - { - vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0; - icmp4_error_set_vnet_buffer (b1, ICMP4_time_exceeded, - ICMP4_time_exceeded_ttl_exceeded_in_transit, - 0); - next1 = SNAT_IN2OUT_NEXT_ICMP_ERROR; - goto trace1; - } - - proto1 = ip_proto_to_snat_proto (ip1->protocol); - - if (PREDICT_FALSE(proto1 == SNAT_PROTOCOL_ICMP)) - { - rx_fib_index1 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index1); - icmp1 = (icmp46_header_t *) udp1; - - next1 = icmp_in2out(sm, b1, ip1, icmp1, sw_if_index1, - rx_fib_index1, node, next1, thread_index, - &ses1, &dm1); - goto trace1; - } - - dm1 = snat_det_map_by_user(sm, &ip1->src_address); - if (PREDICT_FALSE(!dm1)) - { - clib_warning("no match for internal host %U", - format_ip4_address, &ip0->src_address); - next1 = SNAT_IN2OUT_NEXT_DROP; - b1->error = node->errors[SNAT_IN2OUT_ERROR_NO_TRANSLATION]; - goto trace1; - } - - snat_det_forward(dm1, &ip1->src_address, &new_addr1, &lo_port1); - - key1.ext_host_addr = ip1->dst_address; - key1.ext_host_port = tcp1->dst; - - ses1 = snat_det_find_ses_by_in(dm1, &ip1->src_address, tcp1->src, key1); - if (PREDICT_FALSE(!ses1)) - { - for (i1 = 0; i1 < dm1->ports_per_host; i1++) - { - key1.out_port = clib_host_to_net_u16 (lo_port1 + - ((i1 + clib_net_to_host_u16 (tcp1->src)) % dm1->ports_per_host)); - - if (snat_det_get_ses_by_out (dm1, &ip1->src_address, key1.as_u64)) - continue; - - ses1 = snat_det_ses_create(dm1, &ip1->src_address, tcp1->src, &key1); - break; - } - if (PREDICT_FALSE(!ses1)) - { - /* too many sessions for user, send ICMP error packet */ - - vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0; - icmp4_error_set_vnet_buffer (b1, ICMP4_destination_unreachable, - ICMP4_destination_unreachable_destination_unreachable_host, - 0); - next1 = SNAT_IN2OUT_NEXT_ICMP_ERROR; - goto trace1; - } - } - - new_port1 = ses1->out.out_port; - - old_addr1.as_u32 = ip1->src_address.as_u32; - ip1->src_address.as_u32 = new_addr1.as_u32; - vnet_buffer(b1)->sw_if_index[VLIB_TX] = sm->outside_fib_index; - - sum1 = ip1->checksum; - sum1 = ip_csum_update (sum1, old_addr1.as_u32, new_addr1.as_u32, - ip4_header_t, - src_address /* changed member */); - ip1->checksum = ip_csum_fold (sum1); - - if (PREDICT_TRUE(proto1 == SNAT_PROTOCOL_TCP)) - { - if (tcp1->flags & TCP_FLAG_SYN) - ses1->state = SNAT_SESSION_TCP_SYN_SENT; - else if (tcp1->flags & TCP_FLAG_ACK && ses1->state == SNAT_SESSION_TCP_SYN_SENT) - ses1->state = SNAT_SESSION_TCP_ESTABLISHED; - else if (tcp1->flags & TCP_FLAG_FIN && ses1->state == SNAT_SESSION_TCP_ESTABLISHED) - ses1->state = SNAT_SESSION_TCP_FIN_WAIT; - else if (tcp1->flags & TCP_FLAG_ACK && ses1->state == SNAT_SESSION_TCP_FIN_WAIT) - snat_det_ses_close(dm1, ses1); - else if (tcp1->flags & TCP_FLAG_FIN && ses1->state == SNAT_SESSION_TCP_CLOSE_WAIT) - ses1->state = SNAT_SESSION_TCP_LAST_ACK; - else if (tcp1->flags == 0 && ses1->state == SNAT_SESSION_UNKNOWN) - ses1->state = SNAT_SESSION_TCP_ESTABLISHED; - - old_port1 = tcp1->src; - tcp1->src = new_port1; - - sum1 = tcp1->checksum; - sum1 = ip_csum_update (sum1, old_addr1.as_u32, new_addr1.as_u32, - ip4_header_t, - dst_address /* changed member */); - sum1 = ip_csum_update (sum1, old_port1, new_port1, - ip4_header_t /* cheat */, - length /* changed member */); - tcp1->checksum = ip_csum_fold(sum1); - } - else - { - ses1->state = SNAT_SESSION_UDP_ACTIVE; - old_port1 = udp1->src_port; - udp1->src_port = new_port1; - udp1->checksum = 0; - } - - switch(ses1->state) - { - case SNAT_SESSION_UDP_ACTIVE: - ses1->expire = now + sm->udp_timeout; - break; - case SNAT_SESSION_TCP_SYN_SENT: - case SNAT_SESSION_TCP_FIN_WAIT: - case SNAT_SESSION_TCP_CLOSE_WAIT: - case SNAT_SESSION_TCP_LAST_ACK: - ses1->expire = now + sm->tcp_transitory_timeout; - break; - case SNAT_SESSION_TCP_ESTABLISHED: - ses1->expire = now + sm->tcp_established_timeout; - break; - } - - trace1: - if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) - && (b1->flags & VLIB_BUFFER_IS_TRACED))) - { - snat_in2out_trace_t *t = - vlib_add_trace (vm, node, b1, sizeof (*t)); - t->is_slow_path = 0; - t->sw_if_index = sw_if_index1; - t->next_index = next1; - t->session_index = ~0; - if (ses1) - t->session_index = ses1 - dm1->sessions; - } - - pkts_processed += next1 != SNAT_IN2OUT_NEXT_DROP; - - /* verify speculative enqueues, maybe switch current next frame */ - vlib_validate_buffer_enqueue_x2 (vm, node, next_index, - to_next, n_left_to_next, - bi0, bi1, next0, next1); - } - - while (n_left_from > 0 && n_left_to_next > 0) - { - u32 bi0; - vlib_buffer_t * b0; - u32 next0; - u32 sw_if_index0; - ip4_header_t * ip0; - ip_csum_t sum0; - ip4_address_t new_addr0, old_addr0; - u16 old_port0, new_port0, lo_port0, i0; - udp_header_t * udp0; - tcp_header_t * tcp0; - u32 proto0; - snat_det_out_key_t key0; - snat_det_map_t * dm0; - snat_det_session_t * ses0 = 0; - u32 rx_fib_index0; - icmp46_header_t * icmp0; - - /* speculatively enqueue b0 to the current next frame */ - 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); - next0 = SNAT_IN2OUT_NEXT_LOOKUP; - - ip0 = vlib_buffer_get_current (b0); - udp0 = ip4_next_header (ip0); - tcp0 = (tcp_header_t *) udp0; - - sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; - - if (PREDICT_FALSE(ip0->ttl == 1)) - { - vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; - icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, - ICMP4_time_exceeded_ttl_exceeded_in_transit, - 0); - next0 = SNAT_IN2OUT_NEXT_ICMP_ERROR; - goto trace00; - } - - proto0 = ip_proto_to_snat_proto (ip0->protocol); - - if (PREDICT_FALSE(proto0 == SNAT_PROTOCOL_ICMP)) - { - rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0); - icmp0 = (icmp46_header_t *) udp0; - - next0 = icmp_in2out(sm, b0, ip0, icmp0, sw_if_index0, - rx_fib_index0, node, next0, thread_index, - &ses0, &dm0); - goto trace00; - } - - dm0 = snat_det_map_by_user(sm, &ip0->src_address); - if (PREDICT_FALSE(!dm0)) - { - clib_warning("no match for internal host %U", - format_ip4_address, &ip0->src_address); - next0 = SNAT_IN2OUT_NEXT_DROP; - b0->error = node->errors[SNAT_IN2OUT_ERROR_NO_TRANSLATION]; - goto trace00; - } - - snat_det_forward(dm0, &ip0->src_address, &new_addr0, &lo_port0); - - key0.ext_host_addr = ip0->dst_address; - key0.ext_host_port = tcp0->dst; - - ses0 = snat_det_find_ses_by_in(dm0, &ip0->src_address, tcp0->src, key0); - if (PREDICT_FALSE(!ses0)) - { - for (i0 = 0; i0 < dm0->ports_per_host; i0++) - { - key0.out_port = clib_host_to_net_u16 (lo_port0 + - ((i0 + clib_net_to_host_u16 (tcp0->src)) % dm0->ports_per_host)); - - if (snat_det_get_ses_by_out (dm0, &ip0->src_address, key0.as_u64)) - continue; - - ses0 = snat_det_ses_create(dm0, &ip0->src_address, tcp0->src, &key0); - break; - } - if (PREDICT_FALSE(!ses0)) - { - /* too many sessions for user, send ICMP error packet */ - - vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; - icmp4_error_set_vnet_buffer (b0, ICMP4_destination_unreachable, - ICMP4_destination_unreachable_destination_unreachable_host, - 0); - next0 = SNAT_IN2OUT_NEXT_ICMP_ERROR; - goto trace00; - } - } - - new_port0 = ses0->out.out_port; - - old_addr0.as_u32 = ip0->src_address.as_u32; - ip0->src_address.as_u32 = new_addr0.as_u32; - vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm->outside_fib_index; - - sum0 = ip0->checksum; - sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32, - ip4_header_t, - src_address /* changed member */); - ip0->checksum = ip_csum_fold (sum0); - - if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) - { - if (tcp0->flags & TCP_FLAG_SYN) - ses0->state = SNAT_SESSION_TCP_SYN_SENT; - else if (tcp0->flags & TCP_FLAG_ACK && ses0->state == SNAT_SESSION_TCP_SYN_SENT) - ses0->state = SNAT_SESSION_TCP_ESTABLISHED; - else if (tcp0->flags & TCP_FLAG_FIN && ses0->state == SNAT_SESSION_TCP_ESTABLISHED) - ses0->state = SNAT_SESSION_TCP_FIN_WAIT; - else if (tcp0->flags & TCP_FLAG_ACK && ses0->state == SNAT_SESSION_TCP_FIN_WAIT) - snat_det_ses_close(dm0, ses0); - else if (tcp0->flags & TCP_FLAG_FIN && ses0->state == SNAT_SESSION_TCP_CLOSE_WAIT) - ses0->state = SNAT_SESSION_TCP_LAST_ACK; - else if (tcp0->flags == 0 && ses0->state == SNAT_SESSION_UNKNOWN) - ses0->state = SNAT_SESSION_TCP_ESTABLISHED; - - old_port0 = tcp0->src; - tcp0->src = new_port0; - - sum0 = tcp0->checksum; - sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32, - ip4_header_t, - dst_address /* changed member */); - sum0 = ip_csum_update (sum0, old_port0, new_port0, - ip4_header_t /* cheat */, - length /* changed member */); - tcp0->checksum = ip_csum_fold(sum0); - } - else - { - ses0->state = SNAT_SESSION_UDP_ACTIVE; - old_port0 = udp0->src_port; - udp0->src_port = new_port0; - udp0->checksum = 0; - } - - switch(ses0->state) - { - case SNAT_SESSION_UDP_ACTIVE: - ses0->expire = now + sm->udp_timeout; - break; - case SNAT_SESSION_TCP_SYN_SENT: - case SNAT_SESSION_TCP_FIN_WAIT: - case SNAT_SESSION_TCP_CLOSE_WAIT: - case SNAT_SESSION_TCP_LAST_ACK: - ses0->expire = now + sm->tcp_transitory_timeout; - break; - case SNAT_SESSION_TCP_ESTABLISHED: - ses0->expire = now + sm->tcp_established_timeout; - break; - } - - trace00: - if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - snat_in2out_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->is_slow_path = 0; - t->sw_if_index = sw_if_index0; - t->next_index = next0; - t->session_index = ~0; - if (ses0) - t->session_index = ses0 - dm0->sessions; - } - - pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP; - - /* verify speculative enqueue, maybe switch current next frame */ - 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, snat_det_in2out_node.index, - SNAT_IN2OUT_ERROR_IN2OUT_PACKETS, - pkts_processed); - return frame->n_vectors; -} - -VLIB_REGISTER_NODE (snat_det_in2out_node) = { - .function = snat_det_in2out_node_fn, - .name = "snat-det-in2out", - .vector_size = sizeof (u32), - .format_trace = format_snat_in2out_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - - .n_errors = ARRAY_LEN(snat_in2out_error_strings), - .error_strings = snat_in2out_error_strings, - - .runtime_data_bytes = sizeof (snat_runtime_t), - - .n_next_nodes = 3, - - /* edit / add dispositions here */ - .next_nodes = { - [SNAT_IN2OUT_NEXT_DROP] = "error-drop", - [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup", - [SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", - }, -}; - -VLIB_NODE_FUNCTION_MULTIARCH (snat_det_in2out_node, snat_det_in2out_node_fn); - -/** - * Get address and port values to be used for packet SNAT translation - * and create session if needed - * - * @param[in,out] sm SNAT main - * @param[in,out] node SNAT node runtime - * @param[in] thread_index thread index - * @param[in,out] b0 buffer containing packet to be translated - * @param[out] p_proto protocol used for matching - * @param[out] p_value address and port after NAT translation - * @param[out] p_dont_translate if packet should not be translated - * @param d optional parameter - * @param e optional parameter - */ -u32 icmp_match_in2out_det(snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, - snat_session_key_t *p_value, - u8 *p_dont_translate, void *d, void *e) -{ - ip4_header_t *ip0; - icmp46_header_t *icmp0; - u32 sw_if_index0; - u32 rx_fib_index0; - u8 protocol; - snat_det_out_key_t key0; - u8 dont_translate = 0; - u32 next0 = ~0; - icmp_echo_header_t *echo0, *inner_echo0 = 0; - ip4_header_t *inner_ip0; - void *l4_header = 0; - icmp46_header_t *inner_icmp0; - snat_det_map_t * dm0 = 0; - ip4_address_t new_addr0; - u16 lo_port0, i0; - snat_det_session_t * ses0 = 0; - ip4_address_t in_addr; - u16 in_port; - - ip0 = vlib_buffer_get_current (b0); - icmp0 = (icmp46_header_t *) ip4_next_header (ip0); - echo0 = (icmp_echo_header_t *)(icmp0+1); - sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; - rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0); - - if (!icmp_is_error_message (icmp0)) - { - protocol = SNAT_PROTOCOL_ICMP; - in_addr = ip0->src_address; - in_port = echo0->identifier; - } - else - { - inner_ip0 = (ip4_header_t *)(echo0+1); - l4_header = ip4_next_header (inner_ip0); - protocol = ip_proto_to_snat_proto (inner_ip0->protocol); - in_addr = inner_ip0->dst_address; - switch (protocol) - { - case SNAT_PROTOCOL_ICMP: - inner_icmp0 = (icmp46_header_t*)l4_header; - inner_echo0 = (icmp_echo_header_t *)(inner_icmp0+1); - in_port = inner_echo0->identifier; - break; - case SNAT_PROTOCOL_UDP: - case SNAT_PROTOCOL_TCP: - in_port = ((tcp_udp_header_t*)l4_header)->dst_port; - break; - default: - b0->error = node->errors[SNAT_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL]; - next0 = SNAT_IN2OUT_NEXT_DROP; - goto out; - } - } - - dm0 = snat_det_map_by_user(sm, &in_addr); - if (PREDICT_FALSE(!dm0)) - { - clib_warning("no match for internal host %U", - format_ip4_address, &in_addr); - if (PREDICT_FALSE(snat_not_translate_fast(sm, node, sw_if_index0, ip0, - IP_PROTOCOL_ICMP, rx_fib_index0))) - { - dont_translate = 1; - goto out; - } - next0 = SNAT_IN2OUT_NEXT_DROP; - b0->error = node->errors[SNAT_IN2OUT_ERROR_NO_TRANSLATION]; - goto out; - } - - snat_det_forward(dm0, &in_addr, &new_addr0, &lo_port0); - - key0.ext_host_addr = ip0->dst_address; - key0.ext_host_port = 0; - - ses0 = snat_det_find_ses_by_in(dm0, &in_addr, in_port, key0); - if (PREDICT_FALSE(!ses0)) - { - if (PREDICT_FALSE(snat_not_translate_fast(sm, node, sw_if_index0, ip0, - IP_PROTOCOL_ICMP, rx_fib_index0))) - { - dont_translate = 1; - goto out; - } - if (icmp0->type != ICMP4_echo_request) - { - b0->error = node->errors[SNAT_IN2OUT_ERROR_BAD_ICMP_TYPE]; - next0 = SNAT_IN2OUT_NEXT_DROP; - goto out; - } - for (i0 = 0; i0 < dm0->ports_per_host; i0++) - { - key0.out_port = clib_host_to_net_u16 (lo_port0 + - ((i0 + clib_net_to_host_u16 (echo0->identifier)) % dm0->ports_per_host)); - - if (snat_det_get_ses_by_out (dm0, &in_addr, key0.as_u64)) - continue; - - ses0 = snat_det_ses_create(dm0, &in_addr, echo0->identifier, &key0); - break; - } - if (PREDICT_FALSE(!ses0)) - { - next0 = SNAT_IN2OUT_NEXT_DROP; - b0->error = node->errors[SNAT_IN2OUT_ERROR_OUT_OF_PORTS]; - goto out; - } - } - - if (PREDICT_FALSE(icmp0->type != ICMP4_echo_request && - !icmp_is_error_message (icmp0))) - { - b0->error = node->errors[SNAT_IN2OUT_ERROR_BAD_ICMP_TYPE]; - next0 = SNAT_IN2OUT_NEXT_DROP; - goto out; - } - - u32 now = (u32) vlib_time_now (sm->vlib_main); - - ses0->state = SNAT_SESSION_ICMP_ACTIVE; - ses0->expire = now + sm->icmp_timeout; - -out: - *p_proto = protocol; - if (ses0) - { - p_value->addr = new_addr0; - p_value->fib_index = sm->outside_fib_index; - p_value->port = ses0->out.out_port; - } - *p_dont_translate = dont_translate; - if (d) - *(snat_det_session_t**)d = ses0; - if (e) - *(snat_det_map_t**)e = dm0; - return next0; -} - -/**********************/ -/*** worker handoff ***/ -/**********************/ -static inline uword -snat_in2out_worker_handoff_fn_inline (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame, - u8 is_output) -{ - snat_main_t *sm = &snat_main; - vlib_thread_main_t *tm = vlib_get_thread_main (); - u32 n_left_from, *from, *to_next = 0; - static __thread vlib_frame_queue_elt_t **handoff_queue_elt_by_worker_index; - static __thread vlib_frame_queue_t **congested_handoff_queue_by_worker_index - = 0; - vlib_frame_queue_elt_t *hf = 0; - vlib_frame_t *f = 0; - int i; - u32 n_left_to_next_worker = 0, *to_next_worker = 0; - u32 next_worker_index = 0; - u32 current_worker_index = ~0; - u32 thread_index = vlib_get_thread_index (); - u32 fq_index; - u32 to_node_index; - - ASSERT (vec_len (sm->workers)); - - if (is_output) - { - fq_index = sm->fq_in2out_output_index; - to_node_index = sm->in2out_output_node_index; - } - else - { - fq_index = sm->fq_in2out_index; - to_node_index = sm->in2out_node_index; - } - - if (PREDICT_FALSE (handoff_queue_elt_by_worker_index == 0)) - { - vec_validate (handoff_queue_elt_by_worker_index, tm->n_vlib_mains - 1); - - vec_validate_init_empty (congested_handoff_queue_by_worker_index, - sm->first_worker_index + sm->num_workers - 1, - (vlib_frame_queue_t *) (~0)); - } - - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - - while (n_left_from > 0) - { - u32 bi0; - vlib_buffer_t *b0; - u32 sw_if_index0; - u32 rx_fib_index0; - ip4_header_t * ip0; - u8 do_handoff; - - bi0 = from[0]; - from += 1; - n_left_from -= 1; - - b0 = vlib_get_buffer (vm, bi0); - - sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; - rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0); - - ip0 = vlib_buffer_get_current (b0); - - next_worker_index = sm->worker_in2out_cb(ip0, rx_fib_index0); - - if (PREDICT_FALSE (next_worker_index != thread_index)) - { - do_handoff = 1; - - if (next_worker_index != current_worker_index) - { - if (hf) - hf->n_vectors = VLIB_FRAME_SIZE - n_left_to_next_worker; - - hf = vlib_get_worker_handoff_queue_elt (fq_index, - next_worker_index, - handoff_queue_elt_by_worker_index); - - n_left_to_next_worker = VLIB_FRAME_SIZE - hf->n_vectors; - to_next_worker = &hf->buffer_index[hf->n_vectors]; - current_worker_index = next_worker_index; - } - - /* enqueue to correct worker thread */ - to_next_worker[0] = bi0; - to_next_worker++; - n_left_to_next_worker--; - - if (n_left_to_next_worker == 0) - { - hf->n_vectors = VLIB_FRAME_SIZE; - vlib_put_frame_queue_elt (hf); - current_worker_index = ~0; - handoff_queue_elt_by_worker_index[next_worker_index] = 0; - hf = 0; - } - } - else - { - do_handoff = 0; - /* if this is 1st frame */ - if (!f) - { - f = vlib_get_frame_to_node (vm, to_node_index); - to_next = vlib_frame_vector_args (f); - } - - to_next[0] = bi0; - to_next += 1; - f->n_vectors++; - } - - if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - snat_in2out_worker_handoff_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->next_worker_index = next_worker_index; - t->do_handoff = do_handoff; - } - } - - if (f) - vlib_put_frame_to_node (vm, to_node_index, f); - - if (hf) - hf->n_vectors = VLIB_FRAME_SIZE - n_left_to_next_worker; - - /* Ship frames to the worker nodes */ - for (i = 0; i < vec_len (handoff_queue_elt_by_worker_index); i++) - { - if (handoff_queue_elt_by_worker_index[i]) - { - hf = handoff_queue_elt_by_worker_index[i]; - /* - * It works better to let the handoff node - * rate-adapt, always ship the handoff queue element. - */ - if (1 || hf->n_vectors == hf->last_n_vectors) - { - vlib_put_frame_queue_elt (hf); - handoff_queue_elt_by_worker_index[i] = 0; - } - else - hf->last_n_vectors = hf->n_vectors; - } - congested_handoff_queue_by_worker_index[i] = - (vlib_frame_queue_t *) (~0); - } - hf = 0; - current_worker_index = ~0; - return frame->n_vectors; -} - -static uword -snat_in2out_worker_handoff_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - return snat_in2out_worker_handoff_fn_inline (vm, node, frame, 0); -} - -VLIB_REGISTER_NODE (snat_in2out_worker_handoff_node) = { - .function = snat_in2out_worker_handoff_fn, - .name = "snat-in2out-worker-handoff", - .vector_size = sizeof (u32), - .format_trace = format_snat_in2out_worker_handoff_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - - .n_next_nodes = 1, - - .next_nodes = { - [0] = "error-drop", - }, -}; - -VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_worker_handoff_node, - snat_in2out_worker_handoff_fn); - -static uword -snat_in2out_output_worker_handoff_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - return snat_in2out_worker_handoff_fn_inline (vm, node, frame, 1); -} - -VLIB_REGISTER_NODE (snat_in2out_output_worker_handoff_node) = { - .function = snat_in2out_output_worker_handoff_fn, - .name = "snat-in2out-output-worker-handoff", - .vector_size = sizeof (u32), - .format_trace = format_snat_in2out_worker_handoff_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - - .n_next_nodes = 1, - - .next_nodes = { - [0] = "error-drop", - }, -}; - -VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_output_worker_handoff_node, - snat_in2out_output_worker_handoff_fn); - -static_always_inline int -is_hairpinning (snat_main_t *sm, ip4_address_t * dst_addr) -{ - snat_address_t * ap; - clib_bihash_kv_8_8_t kv, value; - snat_session_key_t m_key; - - vec_foreach (ap, sm->addresses) - { - if (ap->addr.as_u32 == dst_addr->as_u32) - return 1; - } - - m_key.addr.as_u32 = dst_addr->as_u32; - m_key.fib_index = sm->outside_fib_index; - m_key.port = 0; - m_key.protocol = 0; - kv.key = m_key.as_u64; - if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) - return 1; - - return 0; -} - -static uword -snat_hairpin_dst_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - u32 n_left_from, * from, * to_next; - snat_in2out_next_t next_index; - u32 pkts_processed = 0; - snat_main_t * sm = &snat_main; - - 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; - u32 next0; - ip4_header_t * ip0; - u32 proto0; - - /* speculatively enqueue b0 to the current next frame */ - 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); - next0 = SNAT_IN2OUT_NEXT_LOOKUP; - ip0 = vlib_buffer_get_current (b0); - - proto0 = ip_proto_to_snat_proto (ip0->protocol); - - vnet_buffer (b0)->snat.flags = 0; - if (PREDICT_FALSE (is_hairpinning (sm, &ip0->dst_address))) - { - if (proto0 == SNAT_PROTOCOL_TCP || proto0 == SNAT_PROTOCOL_UDP) - { - udp_header_t * udp0 = ip4_next_header (ip0); - tcp_header_t * tcp0 = (tcp_header_t *) udp0; - - snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0); - } - else if (proto0 == SNAT_PROTOCOL_ICMP) - { - icmp46_header_t * icmp0 = ip4_next_header (ip0); - - snat_icmp_hairpinning (sm, b0, ip0, icmp0); - } - else - { - snat_hairpinning_unknown_proto (sm, b0, ip0); - } - - vnet_buffer (b0)->snat.flags = SNAT_FLAG_HAIRPINNING; - clib_warning("is hairpinning"); - } - - pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP; - - /* verify speculative enqueue, maybe switch current next frame */ - 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, snat_hairpin_dst_node.index, - SNAT_IN2OUT_ERROR_IN2OUT_PACKETS, - pkts_processed); - return frame->n_vectors; -} - -VLIB_REGISTER_NODE (snat_hairpin_dst_node) = { - .function = snat_hairpin_dst_fn, - .name = "snat-hairpin-dst", - .vector_size = sizeof (u32), - .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(snat_in2out_error_strings), - .error_strings = snat_in2out_error_strings, - .n_next_nodes = 2, - .next_nodes = { - [SNAT_IN2OUT_NEXT_DROP] = "error-drop", - [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup", - }, -}; - -VLIB_NODE_FUNCTION_MULTIARCH (snat_hairpin_dst_node, - snat_hairpin_dst_fn); - -static uword -snat_hairpin_src_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - u32 n_left_from, * from, * to_next; - snat_in2out_next_t next_index; - u32 pkts_processed = 0; - snat_main_t *sm = &snat_main; - - 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; - u32 next0; - - /* speculatively enqueue b0 to the current next frame */ - 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); - next0 = SNAT_HAIRPIN_SRC_NEXT_INTERFACE_OUTPUT; - - if (PREDICT_FALSE ((vnet_buffer (b0)->snat.flags) & SNAT_FLAG_HAIRPINNING)) - { - if (PREDICT_TRUE (sm->num_workers > 1)) - next0 = SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH; - else - next0 = SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT; - } - - pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP; - - /* verify speculative enqueue, maybe switch current next frame */ - 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, snat_hairpin_src_node.index, - SNAT_IN2OUT_ERROR_IN2OUT_PACKETS, - pkts_processed); - return frame->n_vectors; -} - -VLIB_REGISTER_NODE (snat_hairpin_src_node) = { - .function = snat_hairpin_src_fn, - .name = "snat-hairpin-src", - .vector_size = sizeof (u32), - .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(snat_in2out_error_strings), - .error_strings = snat_in2out_error_strings, - .n_next_nodes = SNAT_HAIRPIN_SRC_N_NEXT, - .next_nodes = { - [SNAT_HAIRPIN_SRC_NEXT_DROP] = "error-drop", - [SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT] = "snat-in2out-output", - [SNAT_HAIRPIN_SRC_NEXT_INTERFACE_OUTPUT] = "interface-output", - [SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH] = "snat-in2out-output-worker-handoff", - }, -}; - -VLIB_NODE_FUNCTION_MULTIARCH (snat_hairpin_src_node, - snat_hairpin_src_fn); - -static uword -snat_in2out_fast_static_map_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - u32 n_left_from, * from, * to_next; - snat_in2out_next_t next_index; - u32 pkts_processed = 0; - snat_main_t * sm = &snat_main; - u32 stats_node_index; - - stats_node_index = snat_in2out_fast_node.index; - - 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; - u32 next0; - u32 sw_if_index0; - ip4_header_t * ip0; - ip_csum_t sum0; - u32 new_addr0, old_addr0; - u16 old_port0, new_port0; - udp_header_t * udp0; - tcp_header_t * tcp0; - icmp46_header_t * icmp0; - snat_session_key_t key0, sm0; - u32 proto0; - u32 rx_fib_index0; - - /* speculatively enqueue b0 to the current next frame */ - 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); - next0 = SNAT_IN2OUT_NEXT_LOOKUP; - - ip0 = vlib_buffer_get_current (b0); - udp0 = ip4_next_header (ip0); - tcp0 = (tcp_header_t *) udp0; - icmp0 = (icmp46_header_t *) udp0; - - sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; - rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0); - - if (PREDICT_FALSE(ip0->ttl == 1)) - { - vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; - icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, - ICMP4_time_exceeded_ttl_exceeded_in_transit, - 0); - next0 = SNAT_IN2OUT_NEXT_ICMP_ERROR; - goto trace0; - } - - proto0 = ip_proto_to_snat_proto (ip0->protocol); - - if (PREDICT_FALSE (proto0 == ~0)) - goto trace0; - - if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP)) - { - next0 = icmp_in2out(sm, b0, ip0, icmp0, sw_if_index0, - rx_fib_index0, node, next0, ~0, 0, 0); - goto trace0; - } - - key0.addr = ip0->src_address; - key0.port = udp0->src_port; - key0.fib_index = rx_fib_index0; - - if (snat_static_mapping_match(sm, key0, &sm0, 0, 0)) - { - b0->error = node->errors[SNAT_IN2OUT_ERROR_NO_TRANSLATION]; - next0= SNAT_IN2OUT_NEXT_DROP; - goto trace0; - } - - new_addr0 = sm0.addr.as_u32; - new_port0 = sm0.port; - vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index; - old_addr0 = ip0->src_address.as_u32; - ip0->src_address.as_u32 = new_addr0; - - sum0 = ip0->checksum; - sum0 = ip_csum_update (sum0, old_addr0, new_addr0, - ip4_header_t, - src_address /* changed member */); - ip0->checksum = ip_csum_fold (sum0); - - if (PREDICT_FALSE(new_port0 != udp0->dst_port)) - { - if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) - { - old_port0 = tcp0->src_port; - tcp0->src_port = new_port0; - - sum0 = tcp0->checksum; - sum0 = ip_csum_update (sum0, old_addr0, new_addr0, - ip4_header_t, - dst_address /* changed member */); - sum0 = ip_csum_update (sum0, old_port0, new_port0, - ip4_header_t /* cheat */, - length /* changed member */); - tcp0->checksum = ip_csum_fold(sum0); - } - else - { - old_port0 = udp0->src_port; - udp0->src_port = new_port0; - udp0->checksum = 0; - } - } - else - { - if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) - { - sum0 = tcp0->checksum; - sum0 = ip_csum_update (sum0, old_addr0, new_addr0, - ip4_header_t, - dst_address /* changed member */); - tcp0->checksum = ip_csum_fold(sum0); - } - } - - /* Hairpinning */ - snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0); - - trace0: - if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - snat_in2out_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->sw_if_index = sw_if_index0; - t->next_index = next0; - } - - pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP; - - /* verify speculative enqueue, maybe switch current next frame */ - 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, stats_node_index, - SNAT_IN2OUT_ERROR_IN2OUT_PACKETS, - pkts_processed); - return frame->n_vectors; -} - - -VLIB_REGISTER_NODE (snat_in2out_fast_node) = { - .function = snat_in2out_fast_static_map_fn, - .name = "snat-in2out-fast", - .vector_size = sizeof (u32), - .format_trace = format_snat_in2out_fast_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - - .n_errors = ARRAY_LEN(snat_in2out_error_strings), - .error_strings = snat_in2out_error_strings, - - .runtime_data_bytes = sizeof (snat_runtime_t), - - .n_next_nodes = SNAT_IN2OUT_N_NEXT, - - /* edit / add dispositions here */ - .next_nodes = { - [SNAT_IN2OUT_NEXT_DROP] = "error-drop", - [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup", - [SNAT_IN2OUT_NEXT_SLOW_PATH] = "snat-in2out-slowpath", - [SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error", - }, -}; - -VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_fast_node, snat_in2out_fast_static_map_fn); diff --git a/src/plugins/snat/nat64.c b/src/plugins/snat/nat64.c deleted file mode 100644 index bd915b59..00000000 --- a/src/plugins/snat/nat64.c +++ /dev/null @@ -1,858 +0,0 @@ -/* - * 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. - */ -/** - * @file - * @brief NAT64 implementation - */ - -#include -#include -#include - - -nat64_main_t nat64_main; - -/* *INDENT-OFF* */ - -/* Hook up input features */ -VNET_FEATURE_INIT (nat64_in2out, static) = { - .arc_name = "ip6-unicast", - .node_name = "nat64-in2out", - .runs_before = VNET_FEATURES ("ip6-lookup"), -}; -VNET_FEATURE_INIT (nat64_out2in, static) = { - .arc_name = "ip4-unicast", - .node_name = "nat64-out2in", - .runs_before = VNET_FEATURES ("ip4-lookup"), -}; - -static u8 well_known_prefix[] = { - 0x00, 0x64, 0xff, 0x9b, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 -}; - -/* *INDENT-ON* */ - -clib_error_t * -nat64_init (vlib_main_t * vm) -{ - nat64_main_t *nm = &nat64_main; - clib_error_t *error = 0; - vlib_thread_main_t *tm = vlib_get_thread_main (); - - nm->is_disabled = 0; - - if (tm->n_vlib_mains > 1) - { - nm->is_disabled = 1; - goto error; - } - - if (nat64_db_init (&nm->db)) - { - error = clib_error_return (0, "NAT64 DB init failed"); - goto error; - } - - /* set session timeouts to default values */ - nm->udp_timeout = SNAT_UDP_TIMEOUT; - nm->icmp_timeout = SNAT_ICMP_TIMEOUT; - nm->tcp_trans_timeout = SNAT_TCP_TRANSITORY_TIMEOUT; - nm->tcp_est_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT; - nm->tcp_incoming_syn_timeout = SNAT_TCP_INCOMING_SYN; - -error: - return error; -} - -int -nat64_add_del_pool_addr (ip4_address_t * addr, u32 vrf_id, u8 is_add) -{ - nat64_main_t *nm = &nat64_main; - snat_address_t *a = 0; - snat_interface_t *interface; - int i; - - /* Check if address already exists */ - for (i = 0; i < vec_len (nm->addr_pool); i++) - { - if (nm->addr_pool[i].addr.as_u32 == addr->as_u32) - { - a = nm->addr_pool + i; - break; - } - } - - if (is_add) - { - if (a) - return VNET_API_ERROR_VALUE_EXIST; - - vec_add2 (nm->addr_pool, a, 1); - a->addr = *addr; - a->fib_index = 0; - if (vrf_id != ~0) - a->fib_index = - fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, vrf_id); -#define _(N, i, n, s) \ - clib_bitmap_alloc (a->busy_##n##_port_bitmap, 65535); - foreach_snat_protocol -#undef _ - } - else - { - if (!a) - return VNET_API_ERROR_NO_SUCH_ENTRY; - - if (a->fib_index) - fib_table_unlock (a->fib_index, FIB_PROTOCOL_IP6); - -#define _(N, id, n, s) \ - clib_bitmap_free (a->busy_##n##_port_bitmap); - foreach_snat_protocol -#undef _ - /* Delete sessions using address */ - nat64_db_free_out_addr (&nm->db, &a->addr); - vec_del1 (nm->addr_pool, i); - } - - /* Add/del external address to FIB */ - /* *INDENT-OFF* */ - pool_foreach (interface, nm->interfaces, - ({ - if (interface->is_inside) - continue; - - snat_add_del_addr_to_fib (addr, 32, interface->sw_if_index, is_add); - break; - })); - /* *INDENT-ON* */ - - return 0; -} - -void -nat64_pool_addr_walk (nat64_pool_addr_walk_fn_t fn, void *ctx) -{ - nat64_main_t *nm = &nat64_main; - snat_address_t *a = 0; - - /* *INDENT-OFF* */ - vec_foreach (a, nm->addr_pool) - { - if (fn (a, ctx)) - break; - }; - /* *INDENT-ON* */ -} - -int -nat64_add_del_interface (u32 sw_if_index, u8 is_inside, u8 is_add) -{ - nat64_main_t *nm = &nat64_main; - snat_interface_t *interface = 0, *i; - snat_address_t *ap; - const char *feature_name, *arc_name; - - /* Check if address already exists */ - /* *INDENT-OFF* */ - pool_foreach (i, nm->interfaces, - ({ - if (i->sw_if_index == sw_if_index) - { - interface = i; - break; - } - })); - /* *INDENT-ON* */ - - if (is_add) - { - if (interface) - return VNET_API_ERROR_VALUE_EXIST; - - pool_get (nm->interfaces, interface); - interface->sw_if_index = sw_if_index; - interface->is_inside = is_inside; - - } - else - { - if (!interface) - return VNET_API_ERROR_NO_SUCH_ENTRY; - - pool_put (nm->interfaces, interface); - } - - if (!is_inside) - { - /* *INDENT-OFF* */ - vec_foreach (ap, nm->addr_pool) - snat_add_del_addr_to_fib(&ap->addr, 32, sw_if_index, is_add); - /* *INDENT-ON* */ - } - - arc_name = is_inside ? "ip6-unicast" : "ip4-unicast"; - feature_name = is_inside ? "nat64-in2out" : "nat64-out2in"; - - return vnet_feature_enable_disable (arc_name, feature_name, sw_if_index, - is_add, 0, 0); -} - -void -nat64_interfaces_walk (nat64_interface_walk_fn_t fn, void *ctx) -{ - nat64_main_t *nm = &nat64_main; - snat_interface_t *i = 0; - - /* *INDENT-OFF* */ - pool_foreach (i, nm->interfaces, - ({ - if (fn (i, ctx)) - break; - })); - /* *INDENT-ON* */ -} - -int -nat64_alloc_out_addr_and_port (u32 fib_index, snat_protocol_t proto, - ip4_address_t * addr, u16 * port) -{ - nat64_main_t *nm = &nat64_main; - snat_main_t *sm = &snat_main; - int i; - snat_address_t *a, *ga = 0; - u32 portnum; - - for (i = 0; i < vec_len (nm->addr_pool); i++) - { - a = nm->addr_pool + i; - switch (proto) - { -#define _(N, j, n, s) \ - case SNAT_PROTOCOL_##N: \ - if (a->busy_##n##_ports < (65535-1024)) \ - { \ - if (a->fib_index == fib_index) \ - { \ - while (1) \ - { \ - portnum = random_u32 (&sm->random_seed); \ - portnum &= 0xFFFF; \ - if (portnum < 1024) \ - continue; \ - if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \ - portnum)) \ - continue; \ - clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \ - portnum, 1); \ - a->busy_##n##_ports++; \ - *port = portnum; \ - addr->as_u32 = a->addr.as_u32; \ - return 0; \ - } \ - } \ - else if (a->fib_index == 0) \ - ga = a; \ - } \ - break; - foreach_snat_protocol -#undef _ - default: - clib_warning ("unknown protocol"); - return 1; - } - } - - if (ga) - { - switch (proto) - { -#define _(N, j, n, s) \ - case SNAT_PROTOCOL_##N: \ - while (1) \ - { \ - portnum = random_u32 (&sm->random_seed); \ - portnum &= 0xFFFF; \ - if (portnum < 1024) \ - continue; \ - if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \ - portnum)) \ - continue; \ - clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \ - portnum, 1); \ - a->busy_##n##_ports++; \ - *port = portnum; \ - addr->as_u32 = a->addr.as_u32; \ - return 0; \ - } - break; - foreach_snat_protocol -#undef _ - default: - clib_warning ("unknown protocol"); - return 1; - } - } - - /* Totally out of translations to use... */ - //TODO: IPFix - return 1; -} - -void -nat64_free_out_addr_and_port (ip4_address_t * addr, u16 port, - snat_protocol_t proto) -{ - nat64_main_t *nm = &nat64_main; - int i; - snat_address_t *a; - - for (i = 0; i < vec_len (nm->addr_pool); i++) - { - a = nm->addr_pool + i; - if (addr->as_u32 != a->addr.as_u32) - continue; - switch (proto) - { -#define _(N, j, n, s) \ - case SNAT_PROTOCOL_##N: \ - ASSERT (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \ - port) == 1); \ - clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, port, 0); \ - a->busy_##n##_ports--; \ - break; - foreach_snat_protocol -#undef _ - default: - clib_warning ("unknown protocol"); - return; - } - break; - } -} - -int -nat64_add_del_static_bib_entry (ip6_address_t * in_addr, - ip4_address_t * out_addr, u16 in_port, - u16 out_port, u8 proto, u32 vrf_id, u8 is_add) -{ - nat64_main_t *nm = &nat64_main; - nat64_db_bib_entry_t *bibe; - u32 fib_index = - fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, vrf_id); - snat_protocol_t p = ip_proto_to_snat_proto (proto); - ip46_address_t addr; - int i; - snat_address_t *a; - - addr.as_u64[0] = in_addr->as_u64[0]; - addr.as_u64[1] = in_addr->as_u64[1]; - bibe = - nat64_db_bib_entry_find (&nm->db, &addr, clib_host_to_net_u16 (in_port), - proto, fib_index, 1); - - if (is_add) - { - if (bibe) - return VNET_API_ERROR_VALUE_EXIST; - - for (i = 0; i < vec_len (nm->addr_pool); i++) - { - a = nm->addr_pool + i; - if (out_addr->as_u32 != a->addr.as_u32) - continue; - switch (p) - { -#define _(N, j, n, s) \ - case SNAT_PROTOCOL_##N: \ - if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \ - out_port)) \ - return VNET_API_ERROR_INVALID_VALUE; \ - clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \ - out_port, 1); \ - if (out_port > 1024) \ - a->busy_##n##_ports++; \ - break; - foreach_snat_protocol -#undef _ - default: - memset (&addr, 0, sizeof (addr)); - addr.ip4.as_u32 = out_addr->as_u32; - if (nat64_db_bib_entry_find - (&nm->db, &addr, 0, proto, fib_index, 0)) - return VNET_API_ERROR_INVALID_VALUE; - } - break; - } - bibe = - nat64_db_bib_entry_create (&nm->db, in_addr, out_addr, - clib_host_to_net_u16 (in_port), - clib_host_to_net_u16 (out_port), fib_index, - proto, 1); - if (!bibe) - return VNET_API_ERROR_UNSPECIFIED; - } - else - { - if (!bibe) - return VNET_API_ERROR_NO_SUCH_ENTRY; - - nat64_free_out_addr_and_port (out_addr, out_port, p); - nat64_db_bib_entry_free (&nm->db, bibe); - } - - return 0; -} - -int -nat64_set_udp_timeout (u32 timeout) -{ - nat64_main_t *nm = &nat64_main; - - if (timeout == 0) - nm->udp_timeout = SNAT_UDP_TIMEOUT; - else if (timeout < SNAT_UDP_TIMEOUT_MIN) - return VNET_API_ERROR_INVALID_VALUE; - else - nm->udp_timeout = timeout; - - return 0; -} - -u32 -nat64_get_udp_timeout (void) -{ - nat64_main_t *nm = &nat64_main; - - return nm->udp_timeout; -} - -int -nat64_set_icmp_timeout (u32 timeout) -{ - nat64_main_t *nm = &nat64_main; - - if (timeout == 0) - nm->icmp_timeout = SNAT_ICMP_TIMEOUT; - else - nm->icmp_timeout = timeout; - - return 0; -} - -u32 -nat64_get_icmp_timeout (void) -{ - nat64_main_t *nm = &nat64_main; - - return nm->icmp_timeout; -} - -int -nat64_set_tcp_timeouts (u32 trans, u32 est, u32 incoming_syn) -{ - nat64_main_t *nm = &nat64_main; - - if (trans == 0) - nm->tcp_trans_timeout = SNAT_TCP_TRANSITORY_TIMEOUT; - else - nm->tcp_trans_timeout = trans; - - if (est == 0) - nm->tcp_est_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT; - else - nm->tcp_est_timeout = est; - - if (incoming_syn == 0) - nm->tcp_incoming_syn_timeout = SNAT_TCP_INCOMING_SYN; - else - nm->tcp_incoming_syn_timeout = incoming_syn; - - return 0; -} - -u32 -nat64_get_tcp_trans_timeout (void) -{ - nat64_main_t *nm = &nat64_main; - - return nm->tcp_trans_timeout; -} - -u32 -nat64_get_tcp_est_timeout (void) -{ - nat64_main_t *nm = &nat64_main; - - return nm->tcp_est_timeout; -} - -u32 -nat64_get_tcp_incoming_syn_timeout (void) -{ - nat64_main_t *nm = &nat64_main; - - return nm->tcp_incoming_syn_timeout; -} - -void -nat64_session_reset_timeout (nat64_db_st_entry_t * ste, vlib_main_t * vm) -{ - nat64_main_t *nm = &nat64_main; - u32 now = (u32) vlib_time_now (vm); - - switch (ip_proto_to_snat_proto (ste->proto)) - { - case SNAT_PROTOCOL_ICMP: - ste->expire = now + nm->icmp_timeout; - return; - case SNAT_PROTOCOL_TCP: - { - switch (ste->tcp_state) - { - case NAT64_TCP_STATE_V4_INIT: - case NAT64_TCP_STATE_V6_INIT: - case NAT64_TCP_STATE_V4_FIN_RCV: - case NAT64_TCP_STATE_V6_FIN_RCV: - case NAT64_TCP_STATE_V6_FIN_V4_FIN_RCV: - case NAT64_TCP_STATE_TRANS: - ste->expire = now + nm->tcp_trans_timeout; - return; - case NAT64_TCP_STATE_ESTABLISHED: - ste->expire = now + nm->tcp_est_timeout; - return; - default: - return; - } - } - case SNAT_PROTOCOL_UDP: - ste->expire = now + nm->udp_timeout; - return; - default: - ste->expire = now + nm->udp_timeout; - return; - } -} - -void -nat64_tcp_session_set_state (nat64_db_st_entry_t * ste, tcp_header_t * tcp, - u8 is_ip6) -{ - switch (ste->tcp_state) - { - case NAT64_TCP_STATE_CLOSED: - { - if (tcp->flags & TCP_FLAG_SYN) - { - if (is_ip6) - ste->tcp_state = NAT64_TCP_STATE_V6_INIT; - else - ste->tcp_state = NAT64_TCP_STATE_V4_INIT; - } - return; - } - case NAT64_TCP_STATE_V4_INIT: - { - if (is_ip6 && (tcp->flags & TCP_FLAG_SYN)) - ste->tcp_state = NAT64_TCP_STATE_ESTABLISHED; - return; - } - case NAT64_TCP_STATE_V6_INIT: - { - if (!is_ip6 && (tcp->flags & TCP_FLAG_SYN)) - ste->tcp_state = NAT64_TCP_STATE_ESTABLISHED; - return; - } - case NAT64_TCP_STATE_ESTABLISHED: - { - if (tcp->flags & TCP_FLAG_FIN) - { - if (is_ip6) - ste->tcp_state = NAT64_TCP_STATE_V6_FIN_RCV; - else - ste->tcp_state = NAT64_TCP_STATE_V4_FIN_RCV; - } - else if (tcp->flags & TCP_FLAG_RST) - { - ste->tcp_state = NAT64_TCP_STATE_TRANS; - } - return; - } - case NAT64_TCP_STATE_V4_FIN_RCV: - { - if (is_ip6 && (tcp->flags & TCP_FLAG_FIN)) - ste->tcp_state = NAT64_TCP_STATE_V6_FIN_V4_FIN_RCV; - return; - } - case NAT64_TCP_STATE_V6_FIN_RCV: - { - if (!is_ip6 && (tcp->flags & TCP_FLAG_FIN)) - ste->tcp_state = NAT64_TCP_STATE_V6_FIN_V4_FIN_RCV; - return; - } - case NAT64_TCP_STATE_TRANS: - { - if (!(tcp->flags & TCP_FLAG_RST)) - ste->tcp_state = NAT64_TCP_STATE_ESTABLISHED; - return; - } - default: - return; - } -} - -int -nat64_add_del_prefix (ip6_address_t * prefix, u8 plen, u32 vrf_id, u8 is_add) -{ - nat64_main_t *nm = &nat64_main; - nat64_prefix_t *p = 0; - int i; - - /* Verify prefix length */ - if (plen != 32 && plen != 40 && plen != 48 && plen != 56 && plen != 64 - && plen != 96) - return VNET_API_ERROR_INVALID_VALUE; - - /* Check if tenant already have prefix */ - for (i = 0; i < vec_len (nm->pref64); i++) - { - if (nm->pref64[i].vrf_id == vrf_id) - { - p = nm->pref64 + i; - break; - } - } - - if (is_add) - { - if (!p) - { - vec_add2 (nm->pref64, p, 1); - p->fib_index = - fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, vrf_id); - p->vrf_id = vrf_id; - } - - p->prefix.as_u64[0] = prefix->as_u64[0]; - p->prefix.as_u64[1] = prefix->as_u64[1]; - p->plen = plen; - } - else - { - if (!p) - return VNET_API_ERROR_NO_SUCH_ENTRY; - - vec_del1 (nm->pref64, i); - } - - return 0; -} - -void -nat64_prefix_walk (nat64_prefix_walk_fn_t fn, void *ctx) -{ - nat64_main_t *nm = &nat64_main; - nat64_prefix_t *p = 0; - - /* *INDENT-OFF* */ - vec_foreach (p, nm->pref64) - { - if (fn (p, ctx)) - break; - }; - /* *INDENT-ON* */ -} - -void -nat64_compose_ip6 (ip6_address_t * ip6, ip4_address_t * ip4, u32 fib_index) -{ - nat64_main_t *nm = &nat64_main; - nat64_prefix_t *p, *gp = 0, *prefix = 0; - - /* *INDENT-OFF* */ - vec_foreach (p, nm->pref64) - { - if (p->fib_index == fib_index) - { - prefix = p; - break; - } - - if (p->fib_index == 0) - gp = p; - }; - /* *INDENT-ON* */ - - if (!prefix) - prefix = gp; - - if (prefix) - { - memset (ip6, 0, 16); - memcpy (ip6, &p->prefix, p->plen); - switch (p->plen) - { - case 32: - ip6->as_u32[1] = ip4->as_u32; - break; - case 40: - ip6->as_u8[5] = ip4->as_u8[0]; - ip6->as_u8[6] = ip4->as_u8[1]; - ip6->as_u8[7] = ip4->as_u8[2]; - ip6->as_u8[9] = ip4->as_u8[3]; - break; - case 48: - ip6->as_u8[6] = ip4->as_u8[0]; - ip6->as_u8[7] = ip4->as_u8[1]; - ip6->as_u8[9] = ip4->as_u8[2]; - ip6->as_u8[10] = ip4->as_u8[3]; - break; - case 56: - ip6->as_u8[7] = ip4->as_u8[0]; - ip6->as_u8[9] = ip4->as_u8[1]; - ip6->as_u8[10] = ip4->as_u8[2]; - ip6->as_u8[11] = ip4->as_u8[3]; - break; - case 64: - ip6->as_u8[9] = ip4->as_u8[0]; - ip6->as_u8[10] = ip4->as_u8[1]; - ip6->as_u8[11] = ip4->as_u8[2]; - ip6->as_u8[12] = ip4->as_u8[3]; - break; - case 96: - ip6->as_u32[3] = ip4->as_u32; - break; - default: - clib_warning ("invalid prefix length"); - break; - } - } - else - { - memcpy (ip6, well_known_prefix, 16); - ip6->as_u32[3] = ip4->as_u32; - } -} - -void -nat64_extract_ip4 (ip6_address_t * ip6, ip4_address_t * ip4, u32 fib_index) -{ - nat64_main_t *nm = &nat64_main; - nat64_prefix_t *p, *gp = 0; - u8 plen = 0; - - /* *INDENT-OFF* */ - vec_foreach (p, nm->pref64) - { - if (p->fib_index == fib_index) - { - plen = p->plen; - break; - } - - if (p->vrf_id == 0) - gp = p; - }; - /* *INDENT-ON* */ - - if (!plen) - { - if (gp) - plen = gp->plen; - else - plen = 96; - } - - switch (plen) - { - case 32: - ip4->as_u32 = ip6->as_u32[1]; - break; - case 40: - ip4->as_u8[0] = ip6->as_u8[5]; - ip4->as_u8[1] = ip6->as_u8[6]; - ip4->as_u8[2] = ip6->as_u8[7]; - ip4->as_u8[3] = ip6->as_u8[9]; - break; - case 48: - ip4->as_u8[0] = ip6->as_u8[6]; - ip4->as_u8[1] = ip6->as_u8[7]; - ip4->as_u8[2] = ip6->as_u8[9]; - ip4->as_u8[3] = ip6->as_u8[10]; - break; - case 56: - ip4->as_u8[0] = ip6->as_u8[7]; - ip4->as_u8[1] = ip6->as_u8[9]; - ip4->as_u8[2] = ip6->as_u8[10]; - ip4->as_u8[3] = ip6->as_u8[11]; - break; - case 64: - ip4->as_u8[0] = ip6->as_u8[9]; - ip4->as_u8[1] = ip6->as_u8[10]; - ip4->as_u8[2] = ip6->as_u8[11]; - ip4->as_u8[3] = ip6->as_u8[12]; - break; - case 96: - ip4->as_u32 = ip6->as_u32[3]; - break; - default: - clib_warning ("invalid prefix length"); - break; - } -} - -/** - * @brief The 'nat64-expire-walk' process's main loop. - * - * Check expire time for NAT64 sessions. - */ -static uword -nat64_expire_walk_fn (vlib_main_t * vm, vlib_node_runtime_t * rt, - vlib_frame_t * f) -{ - nat64_main_t *nm = &nat64_main; - - while (!nm->is_disabled) - { - vlib_process_wait_for_event_or_clock (vm, 10.0); - vlib_process_get_events (vm, NULL); - u32 now = (u32) vlib_time_now (vm); - - nad64_db_st_free_expired (&nm->db, now); - } - - return 0; -} - -static vlib_node_registration_t nat64_expire_walk_node; - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (nat64_expire_walk_node, static) = { - .function = nat64_expire_walk_fn, - .type = VLIB_NODE_TYPE_PROCESS, - .name = "nat64-expire-walk", -}; -/* *INDENT-ON* */ - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/snat/nat64.h b/src/plugins/snat/nat64.h deleted file mode 100644 index 771b9075..00000000 --- a/src/plugins/snat/nat64.h +++ /dev/null @@ -1,322 +0,0 @@ -/* - * 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. - */ -/** - * @file - * @brief NAT64 global declarations - */ -#ifndef __included_nat64_h__ -#define __included_nat64_h__ - -#include -#include - -#define foreach_nat64_tcp_ses_state \ - _(0, CLOSED, "closed") \ - _(1, V4_INIT, "v4-init") \ - _(2, V6_INIT, "v6-init") \ - _(3, ESTABLISHED, "established") \ - _(4, V4_FIN_RCV, "v4-fin-rcv") \ - _(5, V6_FIN_RCV, "v6-fin-rcv") \ - _(6, V6_FIN_V4_FIN_RCV, "v6-fin-v4-fin-rcv") \ - _(7, TRANS, "trans") - -typedef enum -{ -#define _(v, N, s) NAT64_TCP_STATE_##N = v, - foreach_nat64_tcp_ses_state -#undef _ -} nat64_tcp_ses_state_t; - -typedef struct -{ - ip6_address_t prefix; - u8 plen; - u32 vrf_id; - u32 fib_index; -} nat64_prefix_t; - -typedef struct -{ - /** Interface pool */ - snat_interface_t *interfaces; - - /** Address pool vector */ - snat_address_t *addr_pool; - - /** Pref64 vector */ - nat64_prefix_t *pref64; - - /** BIB and session DB */ - nat64_db_t db; - - /* values of various timeouts */ - u32 udp_timeout; - u32 icmp_timeout; - u32 tcp_trans_timeout; - u32 tcp_est_timeout; - u32 tcp_incoming_syn_timeout; - - u8 is_disabled; - - snat_main_t *sm; -} nat64_main_t; - -extern nat64_main_t nat64_main; -extern vlib_node_registration_t nat64_in2out_node; -extern vlib_node_registration_t nat64_out2in_node; - -/** - * @brief Add/delete address to NAT64 pool. - * - * @param addr IPv4 address. - * @param vrf_id VRF id of tenant, ~0 means independent of VRF. - * @param is_add 1 if add, 0 if delete. - * - * @returns 0 on success, non-zero value otherwise. - */ -int nat64_add_del_pool_addr (ip4_address_t * addr, u32 vrf_id, u8 is_add); - -/** - * @brief Call back function when walking addresses in NAT64 pool, non-zero - * return value stop walk. - */ -typedef int (*nat64_pool_addr_walk_fn_t) (snat_address_t * addr, void *ctx); - -/** - * @brief Walk NAT64 pool. - * - * @param fn The function to invoke on each entry visited. - * @param ctx A context passed in the visit function. - */ -void nat64_pool_addr_walk (nat64_pool_addr_walk_fn_t fn, void *ctx); - -/** - * @brief Enable/disable NAT64 feature on the interface. - * - * @param sw_if_index Index of the interface. - * @param is_inside 1 if inside, 0 if outside. - * @param is_add 1 if add, 0 if delete. - * - * @returns 0 on success, non-zero value otherwise. - */ -int nat64_add_del_interface (u32 sw_if_index, u8 is_inside, u8 is_add); - -/** - * @brief Call back function when walking interfaces with NAT64 feature, - * non-zero return value stop walk. - */ -typedef int (*nat64_interface_walk_fn_t) (snat_interface_t * i, void *ctx); - -/** - * @brief Walk NAT64 interfaces. - * - * @param fn The function to invoke on each entry visited. - * @param ctx A context passed in the visit function. - */ -void nat64_interfaces_walk (nat64_interface_walk_fn_t fn, void *ctx); - -/** - * @brief Initialize NAT64. - * - * @param vm vlib main. - * - * @return error code. - */ -clib_error_t *nat64_init (vlib_main_t * vm); - -/** - * @brief Add/delete static NAT64 BIB entry. - * - * @param in_addr Inside IPv6 address. - * @param out_addr Outside IPv4 address. - * @param in_port Inside port number. - * @param out_port Outside port number. - * @param proto L4 protocol. - * @param vrf_id VRF id of tenant. - * @param is_add 1 if add, 0 if delete. - * - * @returns 0 on success, non-zero value otherwise. - */ -int nat64_add_del_static_bib_entry (ip6_address_t * in_addr, - ip4_address_t * out_addr, u16 in_port, - u16 out_port, u8 proto, u32 vrf_id, - u8 is_add); - -/** - * @brief Alloce IPv4 address and port pair from NAT64 pool. - * - * @param fib_index FIB index of tenant. - * @param proto L4 protocol. - * @param addr Allocated IPv4 address. - * @param port Allocated port number. - * - * @returns 0 on success, non-zero value otherwise. - */ -int nat64_alloc_out_addr_and_port (u32 fib_index, snat_protocol_t proto, - ip4_address_t * addr, u16 * port); - -/** - * @brief Free IPv4 address and port pair from NAT64 pool. - * - * @param addr IPv4 address to free. - * @param port Port number to free. - * @param proto L4 protocol. - * - * @returns 0 on success, non-zero value otherwise. - */ -void nat64_free_out_addr_and_port (ip4_address_t * addr, u16 port, - snat_protocol_t proto); - -/** - * @brief Set UDP session timeout. - * - * @param timeout Timeout value in seconds (if 0 reset to default value 300sec). - * - * @returns 0 on success, non-zero value otherwise. - */ -int nat64_set_udp_timeout (u32 timeout); - -/** - * @brief Get UDP session timeout. - * - * @returns UDP session timeout in seconds. - */ -u32 nat64_get_udp_timeout (void); - -/** - * @brief Set ICMP session timeout. - * - * @param timeout Timeout value in seconds (if 0 reset to default value 60sec). - * - * @returns 0 on success, non-zero value otherwise. - */ -int nat64_set_icmp_timeout (u32 timeout); - -/** - * @brief Get ICMP session timeout. - * - * @returns ICMP session timeout in seconds. - */ -u32 nat64_get_icmp_timeout (void); - -/** - * @brief Set TCP session timeouts. - * - * @param trans Transitory timeout in seconds (if 0 reset to default value 240sec). - * @param est Established timeout in seconds (if 0 reset to default value 7440sec). - * @param incoming_syn Incoming SYN timeout in seconds (if 0 reset to default value 6sec). - * - * @returns 0 on success, non-zero value otherwise. - */ -int nat64_set_tcp_timeouts (u32 trans, u32 est, u32 incoming_syn); - -/** - * @brief Get TCP transitory timeout. - * - * @returns TCP transitory timeout in seconds. - */ -u32 nat64_get_tcp_trans_timeout (void); - -/** - * @brief Get TCP established timeout. - * - * @returns TCP established timeout in seconds. - */ -u32 nat64_get_tcp_est_timeout (void); - -/** - * @brief Get TCP incoming SYN timeout. - * - * @returns TCP incoming SYN timeout in seconds. - */ -u32 nat64_get_tcp_incoming_syn_timeout (void); - -/** - * @brief Reset NAT64 session timeout. - * - * @param ste Session table entry. - * @param vm VLIB main. - **/ -void nat64_session_reset_timeout (nat64_db_st_entry_t * ste, - vlib_main_t * vm); - -/** - * @brief Set NAT64 TCP session state. - * - * @param ste Session table entry. - * @param tcp TCP header. - * @param is_ip6 1 if IPv6 packet, 0 if IPv4. - */ -void nat64_tcp_session_set_state (nat64_db_st_entry_t * ste, - tcp_header_t * tcp, u8 is_ip6); - -/** - * @brief Add/delete NAT64 prefix. - * - * @param prefix NAT64 prefix. - * @param plen Prefix length. - * @param vrf_id VRF id of tenant. - * @param is_add 1 if add, 0 if delete. - * - * @returns 0 on success, non-zero value otherwise. - */ -int nat64_add_del_prefix (ip6_address_t * prefix, u8 plen, u32 vrf_id, - u8 is_add); - -/** - * @brief Call back function when walking addresses in NAT64 prefixes, non-zero - * return value stop walk. - */ -typedef int (*nat64_prefix_walk_fn_t) (nat64_prefix_t * pref64, void *ctx); - -/** - * @brief Walk NAT64 prefixes. - * - * @param fn The function to invoke on each entry visited. - * @param ctx A context passed in the visit function. - */ -void nat64_prefix_walk (nat64_prefix_walk_fn_t fn, void *ctx); - -/** - * Compose IPv4-embedded IPv6 addresses. - * @param ip6 IPv4-embedded IPv6 addresses. - * @param ip4 IPv4 address. - * @param fib_index Tenant FIB index. - */ -void nat64_compose_ip6 (ip6_address_t * ip6, ip4_address_t * ip4, - u32 fib_index); - -/** - * Extract IPv4 address from the IPv4-embedded IPv6 addresses. - * - * @param ip6 IPv4-embedded IPv6 addresses. - * @param ip4 IPv4 address. - * @param fib_index Tenant FIB index. - */ -void nat64_extract_ip4 (ip6_address_t * ip6, ip4_address_t * ip4, - u32 fib_index); - -#define u8_ptr_add(ptr, index) (((u8 *)ptr) + index) -#define u16_net_add(u, val) clib_host_to_net_u16(clib_net_to_host_u16(u) + (val)) - -#endif /* __included_nat64_h__ */ - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/snat/nat64_cli.c b/src/plugins/snat/nat64_cli.c deleted file mode 100644 index ca60b12c..00000000 --- a/src/plugins/snat/nat64_cli.c +++ /dev/null @@ -1,983 +0,0 @@ -/* - * 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. - */ -/** - * @file - * @brief NAT64 CLI - */ - -#include -#include -#include - -static clib_error_t * -nat64_add_del_pool_addr_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - nat64_main_t *nm = &nat64_main; - unformat_input_t _line_input, *line_input = &_line_input; - ip4_address_t start_addr, end_addr, this_addr; - u32 start_host_order, end_host_order; - int i, count, rv; - u32 vrf_id = ~0; - u8 is_add = 1; - clib_error_t *error = 0; - - if (nm->is_disabled) - return clib_error_return (0, - "NAT64 disabled, multi thread not supported"); - - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (line_input, "%U - %U", - unformat_ip4_address, &start_addr, - unformat_ip4_address, &end_addr)) - ; - else if (unformat (line_input, "tenant-vrf %u", &vrf_id)) - ; - else if (unformat (line_input, "%U", unformat_ip4_address, &start_addr)) - end_addr = start_addr; - else if (unformat (line_input, "del")) - is_add = 0; - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - - 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) - { - error = clib_error_return (0, "end address less than start address"); - goto done; - } - - count = (end_host_order - start_host_order) + 1; - this_addr = start_addr; - - for (i = 0; i < count; i++) - { - rv = nat64_add_del_pool_addr (&this_addr, vrf_id, is_add); - - switch (rv) - { - case VNET_API_ERROR_NO_SUCH_ENTRY: - error = - clib_error_return (0, "NAT64 pool address %U not exist.", - format_ip4_address, &this_addr); - goto done; - case VNET_API_ERROR_VALUE_EXIST: - error = - clib_error_return (0, "NAT64 pool address %U exist.", - format_ip4_address, &this_addr); - goto done; - default: - break; - - } - increment_v4_address (&this_addr); - } - -done: - unformat_free (line_input); - - return error; -} - -static int -nat64_cli_pool_walk (snat_address_t * ap, void *ctx) -{ - vlib_main_t *vm = ctx; - - if (ap->fib_index != ~0) - { - fib_table_t *fib; - fib = fib_table_get (ap->fib_index, FIB_PROTOCOL_IP6); - if (!fib) - return -1; - vlib_cli_output (vm, " %U tenant VRF: %u", format_ip4_address, - &ap->addr, fib->ft_table_id); - } - else - vlib_cli_output (vm, " %U", format_ip4_address, &ap->addr); - - return 0; -} - -static clib_error_t * -nat64_show_pool_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - nat64_main_t *nm = &nat64_main; - - if (nm->is_disabled) - return clib_error_return (0, - "NAT64 disabled, multi thread not supported"); - - vlib_cli_output (vm, "NAT64 pool:"); - nat64_pool_addr_walk (nat64_cli_pool_walk, vm); - - return 0; -} - -static clib_error_t * -nat64_interface_feature_command_fn (vlib_main_t * vm, - unformat_input_t * - input, vlib_cli_command_t * cmd) -{ - nat64_main_t *nm = &nat64_main; - unformat_input_t _line_input, *line_input = &_line_input; - vnet_main_t *vnm = vnet_get_main (); - clib_error_t *error = 0; - u32 sw_if_index; - u32 *inside_sw_if_indices = 0; - u32 *outside_sw_if_indices = 0; - u8 is_add = 1; - int i, rv; - - if (nm->is_disabled) - return clib_error_return (0, - "NAT64 disabled, multi thread not supported"); - - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (line_input, "in %U", unformat_vnet_sw_interface, - vnm, &sw_if_index)) - vec_add1 (inside_sw_if_indices, sw_if_index); - else if (unformat (line_input, "out %U", unformat_vnet_sw_interface, - vnm, &sw_if_index)) - vec_add1 (outside_sw_if_indices, sw_if_index); - else if (unformat (line_input, "del")) - is_add = 0; - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - - if (vec_len (inside_sw_if_indices)) - { - for (i = 0; i < vec_len (inside_sw_if_indices); i++) - { - sw_if_index = inside_sw_if_indices[i]; - rv = nat64_add_del_interface (sw_if_index, 1, is_add); - switch (rv) - { - case VNET_API_ERROR_NO_SUCH_ENTRY: - error = - clib_error_return (0, "%U NAT64 feature not enabled.", - format_vnet_sw_interface_name, vnm, - vnet_get_sw_interface (vnm, sw_if_index)); - goto done; - case VNET_API_ERROR_VALUE_EXIST: - error = - clib_error_return (0, "%U NAT64 feature already enabled.", - format_vnet_sw_interface_name, vnm, - vnet_get_sw_interface (vnm, sw_if_index)); - goto done; - case VNET_API_ERROR_INVALID_VALUE: - case VNET_API_ERROR_INVALID_VALUE_2: - error = - clib_error_return (0, - "%U NAT64 feature enable/disable failed.", - format_vnet_sw_interface_name, vnm, - vnet_get_sw_interface (vnm, sw_if_index)); - goto done; - default: - break; - - } - } - } - - if (vec_len (outside_sw_if_indices)) - { - for (i = 0; i < vec_len (outside_sw_if_indices); i++) - { - sw_if_index = outside_sw_if_indices[i]; - rv = nat64_add_del_interface (sw_if_index, 0, is_add); - switch (rv) - { - case VNET_API_ERROR_NO_SUCH_ENTRY: - error = - clib_error_return (0, "%U NAT64 feature not enabled.", - format_vnet_sw_interface_name, vnm, - vnet_get_sw_interface (vnm, sw_if_index)); - goto done; - case VNET_API_ERROR_VALUE_EXIST: - error = - clib_error_return (0, "%U NAT64 feature already enabled.", - format_vnet_sw_interface_name, vnm, - vnet_get_sw_interface (vnm, sw_if_index)); - goto done; - case VNET_API_ERROR_INVALID_VALUE: - case VNET_API_ERROR_INVALID_VALUE_2: - error = - clib_error_return (0, - "%U NAT64 feature enable/disable failed.", - format_vnet_sw_interface_name, vnm, - vnet_get_sw_interface (vnm, sw_if_index)); - goto done; - default: - break; - - } - } - } - -done: - unformat_free (line_input); - vec_free (inside_sw_if_indices); - vec_free (outside_sw_if_indices); - - return error; -} - -static int -nat64_cli_interface_walk (snat_interface_t * i, void *ctx) -{ - vlib_main_t *vm = ctx; - vnet_main_t *vnm = vnet_get_main (); - - vlib_cli_output (vm, " %U %s", format_vnet_sw_interface_name, vnm, - vnet_get_sw_interface (vnm, i->sw_if_index), - i->is_inside ? "in" : "out"); - return 0; -} - -static clib_error_t * -nat64_show_interfaces_command_fn (vlib_main_t * vm, - unformat_input_t * - input, vlib_cli_command_t * cmd) -{ - nat64_main_t *nm = &nat64_main; - - if (nm->is_disabled) - return clib_error_return (0, - "NAT64 disabled, multi thread not supported"); - - vlib_cli_output (vm, "NAT64 interfaces:"); - nat64_interfaces_walk (nat64_cli_interface_walk, vm); - - return 0; -} - -static clib_error_t * -nat64_add_del_static_bib_command_fn (vlib_main_t * - vm, - unformat_input_t - * input, vlib_cli_command_t * cmd) -{ - nat64_main_t *nm = &nat64_main; - unformat_input_t _line_input, *line_input = &_line_input; - clib_error_t *error = 0; - u8 is_add = 1; - ip6_address_t in_addr; - ip4_address_t out_addr; - u16 in_port = 0; - u16 out_port = 0; - u32 vrf_id = 0, protocol; - snat_protocol_t proto = 0; - u8 p = 0; - int rv; - - if (nm->is_disabled) - return clib_error_return (0, - "NAT64 disabled, multi thread not supported"); - - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (line_input, "%U %u", unformat_ip6_address, - &in_addr, &in_port)) - ; - else if (unformat (line_input, "%U %u", unformat_ip4_address, - &out_addr, &out_port)) - ; - else if (unformat (line_input, "vrf %u", &vrf_id)) - ; - else if (unformat (line_input, "%U", unformat_snat_protocol, &proto)) - ; - else - if (unformat - (line_input, "%U %U %u", unformat_ip6_address, &in_addr, - unformat_ip4_address, &out_addr, &protocol)) - p = (u8) protocol; - else if (unformat (line_input, "del")) - is_add = 0; - else - { - error = clib_error_return (0, "unknown input: '%U'", - format_unformat_error, line_input); - goto done; - } - } - - if (!p) - { - if (!in_port) - { - error = - clib_error_return (0, "inside port and address must be set"); - goto done; - } - - if (!out_port) - { - error = - clib_error_return (0, "outside port and address must be set"); - goto done; - } - - p = snat_proto_to_ip_proto (proto); - } - - rv = - nat64_add_del_static_bib_entry (&in_addr, &out_addr, in_port, out_port, p, - vrf_id, is_add); - - switch (rv) - { - case VNET_API_ERROR_NO_SUCH_ENTRY: - error = clib_error_return (0, "NAT64 BIB entry not exist."); - goto done; - case VNET_API_ERROR_VALUE_EXIST: - error = clib_error_return (0, "NAT64 BIB entry exist."); - goto done; - case VNET_API_ERROR_UNSPECIFIED: - error = clib_error_return (0, "Crerate NAT64 BIB entry failed."); - goto done; - case VNET_API_ERROR_INVALID_VALUE: - error = - clib_error_return (0, "Outside addres %U and port %u already in use.", - format_ip4_address, &out_addr, out_port); - goto done; - default: - break; - } - -done: - unformat_free (line_input); - - return error; -} - -static int -nat64_cli_bib_walk (nat64_db_bib_entry_t * bibe, void *ctx) -{ - vlib_main_t *vm = ctx; - fib_table_t *fib; - - fib = fib_table_get (bibe->fib_index, FIB_PROTOCOL_IP6); - if (!fib) - return -1; - - switch (bibe->proto) - { - case IP_PROTOCOL_ICMP: - case IP_PROTOCOL_TCP: - case IP_PROTOCOL_UDP: - vlib_cli_output (vm, " %U %u %U %u protocol %U vrf %u %s %u sessions", - format_ip6_address, &bibe->in_addr, - clib_net_to_host_u16 (bibe->in_port), - format_ip4_address, &bibe->out_addr, - clib_net_to_host_u16 (bibe->out_port), - format_snat_protocol, - ip_proto_to_snat_proto (bibe->proto), fib->ft_table_id, - bibe->is_static ? "static" : "dynamic", bibe->ses_num); - break; - default: - vlib_cli_output (vm, " %U %U protocol %u vrf %u %s %u sessions", - format_ip6_address, &bibe->in_addr, - format_ip4_address, &bibe->out_addr, - bibe->proto, fib->ft_table_id, - bibe->is_static ? "static" : "dynamic", bibe->ses_num); - } - return 0; -} - -static clib_error_t * -nat64_show_bib_command_fn (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - nat64_main_t *nm = &nat64_main; - unformat_input_t _line_input, *line_input = &_line_input; - clib_error_t *error = 0; - u32 proto = ~0; - u8 p = 255; - - if (nm->is_disabled) - return clib_error_return (0, - "NAT64 disabled, multi thread not supported"); - - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - if (unformat (line_input, "%U", unformat_snat_protocol, &proto)) - p = snat_proto_to_ip_proto (proto); - else if (unformat (line_input, "unknown")) - p = 0; - else if (unformat (line_input, "all")) - ; - else - { - error = clib_error_return (0, "unknown input: '%U'", - format_unformat_error, line_input); - goto done; - } - - if (p == 255) - vlib_cli_output (vm, "NAT64 BIB entries:"); - else - vlib_cli_output (vm, "NAT64 %U BIB entries:", format_snat_protocol, - proto); - nat64_db_bib_walk (&nm->db, p, nat64_cli_bib_walk, vm); - -done: - unformat_free (line_input); - - return error; -} - -static clib_error_t * -nat64_set_timeouts_command_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - nat64_main_t *nm = &nat64_main; - unformat_input_t _line_input, *line_input = &_line_input; - clib_error_t *error = 0; - u32 timeout, tcp_trans, tcp_est, tcp_incoming_syn; - - tcp_trans = nat64_get_tcp_trans_timeout (); - tcp_est = nat64_get_tcp_est_timeout (); - tcp_incoming_syn = nat64_get_tcp_incoming_syn_timeout (); - - if (nm->is_disabled) - return clib_error_return (0, - "NAT64 disabled, multi thread not supported"); - - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (line_input, "udp %u", &timeout)) - { - if (nat64_set_udp_timeout (timeout)) - { - error = clib_error_return (0, "Invalid UDP timeout value"); - goto done; - } - } - else if (unformat (line_input, "icmp %u", &timeout)) - { - if (nat64_set_icmp_timeout (timeout)) - { - error = clib_error_return (0, "Invalid ICMP timeout value"); - goto done; - } - } - else if (unformat (line_input, "tcp-trans %u", &tcp_trans)) - { - if (nat64_set_tcp_timeouts (tcp_trans, tcp_est, tcp_incoming_syn)) - { - error = - clib_error_return (0, "Invalid TCP transitory tiemout value"); - goto done; - } - } - else if (unformat (line_input, "tcp-est %u", &tcp_est)) - { - if (nat64_set_tcp_timeouts (tcp_trans, tcp_est, tcp_incoming_syn)) - { - error = - clib_error_return (0, - "Invalid TCP established tiemout value"); - goto done; - } - } - else - if (unformat (line_input, "tcp-incoming-syn %u", &tcp_incoming_syn)) - { - if (nat64_set_tcp_timeouts (tcp_trans, tcp_est, tcp_incoming_syn)) - { - error = - clib_error_return (0, - "Invalid TCP incoming SYN tiemout value"); - goto done; - } - } - else if (unformat (line_input, "reset")) - { - nat64_set_udp_timeout (0); - nat64_set_icmp_timeout (0); - nat64_set_tcp_timeouts (0, 0, 0); - } - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - -done: - unformat_free (line_input); - - return error; -} - -static clib_error_t * -nat64_show_timeouts_command_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - nat64_main_t *nm = &nat64_main; - - if (nm->is_disabled) - return clib_error_return (0, - "NAT64 disabled, multi thread not supported"); - - vlib_cli_output (vm, "NAT64 session timeouts:"); - vlib_cli_output (vm, " UDP %usec", nat64_get_udp_timeout ()); - vlib_cli_output (vm, " ICMP %usec", nat64_get_icmp_timeout ()); - vlib_cli_output (vm, " TCP transitory %usec", - nat64_get_tcp_trans_timeout ()); - vlib_cli_output (vm, " TCP established %usec", - nat64_get_tcp_est_timeout ()); - vlib_cli_output (vm, " TCP incoming SYN %usec", - nat64_get_tcp_incoming_syn_timeout ()); - - return 0; -} - -static int -nat64_cli_st_walk (nat64_db_st_entry_t * ste, void *ctx) -{ - vlib_main_t *vm = ctx; - nat64_main_t *nm = &nat64_main; - nat64_db_bib_entry_t *bibe; - fib_table_t *fib; - - bibe = nat64_db_bib_entry_by_index (&nm->db, ste->proto, ste->bibe_index); - if (!bibe) - return -1; - - fib = fib_table_get (bibe->fib_index, FIB_PROTOCOL_IP6); - if (!fib) - return -1; - - u32 vrf_id = fib->ft_table_id; - - if (ste->proto == IP_PROTOCOL_ICMP) - vlib_cli_output (vm, " %U %U %u %U %U %u protocol %U vrf %u", - format_ip6_address, &bibe->in_addr, - format_ip6_address, &ste->in_r_addr, - clib_net_to_host_u16 (bibe->in_port), - format_ip4_address, &bibe->out_addr, - format_ip4_address, &ste->out_r_addr, - clib_net_to_host_u16 (bibe->out_port), - format_snat_protocol, - ip_proto_to_snat_proto (bibe->proto), vrf_id); - else if (ste->proto == IP_PROTOCOL_TCP || ste->proto == IP_PROTOCOL_UDP) - vlib_cli_output (vm, " %U %u %U %u %U %u %U %u protcol %U vrf %u", - format_ip6_address, &bibe->in_addr, - clib_net_to_host_u16 (bibe->in_port), - format_ip6_address, &ste->in_r_addr, - clib_net_to_host_u16 (ste->r_port), - format_ip4_address, &bibe->out_addr, - clib_net_to_host_u16 (bibe->out_port), - format_ip4_address, &ste->out_r_addr, - clib_net_to_host_u16 (ste->r_port), - format_snat_protocol, - ip_proto_to_snat_proto (bibe->proto), vrf_id); - else - vlib_cli_output (vm, " %U %U %U %U protocol %u vrf %u", - format_ip6_address, &bibe->in_addr, - format_ip6_address, &ste->in_r_addr, - format_ip4_address, &bibe->out_addr, - format_ip4_address, &ste->out_r_addr, - bibe->proto, vrf_id); - - return 0; -} - -static clib_error_t * -nat64_show_st_command_fn (vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - nat64_main_t *nm = &nat64_main; - unformat_input_t _line_input, *line_input = &_line_input; - clib_error_t *error = 0; - u32 proto = ~0; - u8 p = 255; - - if (nm->is_disabled) - return clib_error_return (0, - "NAT64 disabled, multi thread not supported"); - - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - if (unformat (line_input, "%U", unformat_snat_protocol, &proto)) - p = snat_proto_to_ip_proto (proto); - else if (unformat (line_input, "unknown")) - p = 0; - else if (unformat (line_input, "all")) - ; - else - { - error = clib_error_return (0, "unknown input: '%U'", - format_unformat_error, line_input); - goto done; - } - - if (p == 255) - vlib_cli_output (vm, "NAT64 sessions:"); - else - vlib_cli_output (vm, "NAT64 %U sessions:", format_snat_protocol, proto); - nat64_db_st_walk (&nm->db, p, nat64_cli_st_walk, vm); - -done: - unformat_free (line_input); - - return error; -} - -static clib_error_t * -nat64_add_del_prefix_command_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - nat64_main_t *nm = &nat64_main; - clib_error_t *error = 0; - unformat_input_t _line_input, *line_input = &_line_input; - u8 is_add = 1; - u32 vrf_id = 0; - ip6_address_t prefix; - u32 plen = 0; - int rv; - - if (nm->is_disabled) - return clib_error_return (0, - "NAT64 disabled, multi thread not supported"); - - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat - (line_input, "%U/%u", unformat_ip6_address, &prefix, &plen)) - ; - else if (unformat (line_input, "tenant-vrf %u", &vrf_id)) - ; - else if (unformat (line_input, "del")) - is_add = 0; - else - { - error = clib_error_return (0, "unknown input: '%U'", - format_unformat_error, line_input); - goto done; - } - } - - if (!plen) - { - error = clib_error_return (0, "NAT64 prefix must be set."); - goto done; - } - - rv = nat64_add_del_prefix (&prefix, (u8) plen, vrf_id, is_add); - - switch (rv) - { - case VNET_API_ERROR_NO_SUCH_ENTRY: - error = clib_error_return (0, "NAT64 prefix not exist."); - goto done; - case VNET_API_ERROR_INVALID_VALUE: - error = clib_error_return (0, "Invalid prefix length."); - goto done; - default: - break; - } - -done: - unformat_free (line_input); - - return error; -} - -static int -nat64_cli_prefix_walk (nat64_prefix_t * p, void *ctx) -{ - vlib_main_t *vm = ctx; - - vlib_cli_output (vm, " %U/%u tenant-vrf %u", - format_ip6_address, &p->prefix, p->plen, p->vrf_id); - - return 0; -} - -static clib_error_t * -nat64_show_prefix_command_fn (vlib_main_t * vm, unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - nat64_main_t *nm = &nat64_main; - - if (nm->is_disabled) - return clib_error_return (0, - "NAT64 disabled, multi thread not supported"); - - vlib_cli_output (vm, "NAT64 prefix:"); - nat64_prefix_walk (nat64_cli_prefix_walk, vm); - - return 0; -} - -/* *INDENT-OFF* */ - -/*? - * @cliexpar - * @cliexstart{nat64 add pool address} - * Add/delete NAT64 pool address. - * To add single NAT64 pool address use: - * vpp# nat64 add pool address 10.1.1.10 - * To add NAT64 pool address range use: - * vpp# nat64 add pool address 10.1.1.2 - 10.1.1.5 - * To add NAT64 pool address for specific tenant use: - * vpp# nat64 add pool address 10.1.1.100 tenant-vrf 100 - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat64_add_pool_address_command, static) = { - .path = "nat64 add pool address", - .short_help = "nat64 add pool address [- ] " - "[tenant-vrf ] [del]", - .function = nat64_add_del_pool_addr_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{show nat64 pool} - * Show NAT64 pool. - * vpp# show nat64 pool - * NAT64 pool: - * 10.1.1.3 tenant VRF: 0 - * 10.1.1.10 tenant VRF: 10 - * @cliexend -?*/ -VLIB_CLI_COMMAND (show_nat64_pool_command, static) = { - .path = "show nat64 pool", - .short_help = "show nat64 pool", - .function = nat64_show_pool_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{set interface nat64} - * Enable/disable NAT64 feature on the interface. - * To enable NAT64 feature with local (IPv6) network interface - * GigabitEthernet0/8/0 and external (IPv4) network interface - * GigabitEthernet0/a/0 use: - * vpp# set interface nat64 in GigabitEthernet0/8/0 out GigabitEthernet0/a/0 - * @cliexend -?*/ -VLIB_CLI_COMMAND (set_interface_nat64_command, static) = { - .path = "set interface nat64", - .short_help = "set interface nat64 in|out [del]", - .function = nat64_interface_feature_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{show nat64 interfaces} - * Show interfaces with NAT64 feature. - * To show interfaces with NAT64 feature use: - * vpp# show nat64 interfaces - * NAT64 interfaces: - * GigabitEthernet0/8/0 in - * GigabitEthernet0/a/0 out - * @cliexend -?*/ -VLIB_CLI_COMMAND (show_nat64_interfaces_command, static) = { - .path = "show nat64 interfaces", - .short_help = "show nat64 interfaces", - .function = nat64_show_interfaces_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{nat64 add static bib} - * Add/delete NAT64 static BIB entry. - * To create NAT64 satatic BIB entry use: - * vpp# nat64 add static bib 2001:db8:c000:221:: 1234 10.1.1.3 5678 tcp - * vpp# nat64 add static bib 2001:db8:c000:221:: 1234 10.1.1.3 5678 udp vrf 10 - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat64_add_del_static_bib_command, static) = { - .path = "nat64 add static bib", - .short_help = "nat64 add static bib " - "tcp|udp|icmp [vfr ] [del]", - .function = nat64_add_del_static_bib_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{show nat64 bib} - * Show NAT64 BIB entries. - * To show NAT64 TCP BIB entries use: - * vpp# show nat64 bib tcp - * NAT64 tcp BIB: - * fd01:1::2 6303 10.0.0.3 62303 tcp vrf 0 dynamic 1 sessions - * 2001:db8:c000:221:: 1234 10.1.1.3 5678 tcp vrf 0 static 2 sessions - * To show NAT64 UDP BIB entries use: - * vpp# show nat64 bib udp - * NAT64 udp BIB: - * fd01:1::2 6304 10.0.0.3 10546 udp vrf 0 dynamic 10 sessions - * 2001:db8:c000:221:: 1234 10.1.1.3 5678 udp vrf 10 static 0 sessions - * To show NAT64 ICMP BIB entries use: - * vpp# show nat64 bib icmp - * NAT64 icmp BIB: - * fd01:1::2 6305 10.0.0.3 63209 icmp vrf 10 dynamic 1 sessions - * @cliexend -?*/ -VLIB_CLI_COMMAND (show_nat64_bib_command, static) = { - .path = "show nat64 bib", - .short_help = "show nat64 bib all|tcp|udp|icmp|unknown", - .function = nat64_show_bib_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{set nat64 timeouts} - * Set NAT64 session timeouts (in seconds). - * To set NAT64 session timeoutes use use: - * vpp# set nat64 timeouts udp 200 icmp 30 tcp-trans 250 tcp-est 7450 - * To reset NAT64 session timeoutes to default values use: - * vpp# set nat64 timeouts reset - * @cliexend -?*/ -VLIB_CLI_COMMAND (set_nat64_timeouts_command, static) = { - .path = "set nat64 timeouts", - .short_help = "set nat64 timeouts udp icmp tcp-trans " - "tcp-est tcp-incoming-syn | reset", - .function = nat64_set_timeouts_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{show nat64 tiemouts} - * Show NAT64 session timeouts: - * vpp# show nat64 tiemouts - * NAT64 session timeouts: - * UDP 300sec - * ICMP 60sec - * TCP transitory 240sec - * TCP established 7440sec - * TCP incoming SYN 6sec - * @cliexend -?*/ -VLIB_CLI_COMMAND (show_nat64_timeouts_command, static) = { - .path = "show nat64 tiemouts", - .short_help = "show nat64 tiemouts", - .function = nat64_show_timeouts_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{show nat64 session table} - * Show NAT64 session table. - * To show NAT64 TCP session table use: - * vpp# show nat64 session table tcp - * NAT64 tcp session table: - * fd01:1::2 6303 64:ff9b::ac10:202 20 10.0.0.3 62303 172.16.2.2 20 tcp vrf 0 - * fd01:3::2 6303 64:ff9b::ac10:202 20 10.0.10.3 21300 172.16.2.2 20 tcp vrf 10 - * To show NAT64 UDP session table use: - * #vpp show nat64 session table udp - * NAT64 udp session table: - * fd01:1::2 6304 64:ff9b::ac10:202 20 10.0.0.3 10546 172.16.2.2 20 udp vrf 0 - * fd01:3::2 6304 64:ff9b::ac10:202 20 10.0.10.3 58627 172.16.2.2 20 udp vrf 10 - * fd01:1::2 1235 64:ff9b::a00:3 4023 10.0.0.3 24488 10.0.0.3 4023 udp vrf 0 - * fd01:1::3 23 64:ff9b::a00:3 24488 10.0.0.3 4023 10.0.0.3 24488 udp vrf 0 - * To show NAT64 ICMP session table use: - * #vpp show nat64 session table icmp - * NAT64 icmp session table: - * fd01:1::2 64:ff9b::ac10:202 6305 10.0.0.3 172.16.2.2 63209 icmp vrf 0 - * @cliexend -?*/ -VLIB_CLI_COMMAND (show_nat64_st_command, static) = { - .path = "show nat64 session table", - .short_help = "show nat64 session table all|tcp|udp|icmp|unknown", - .function = nat64_show_st_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{nat64 add prefix} - * Set NAT64 prefix for generating IPv6 representations of IPv4 addresses. - * To set NAT64 global prefix use: - * vpp# nat64 add prefix 2001:db8::/32 - * To set NAT64 prefix for specific tenant use: - * vpp# nat64 add prefix 2001:db8:122:300::/56 tenant-vrf 10 - * @cliexend -?*/ -VLIB_CLI_COMMAND (nat64_add_del_prefix_command, static) = { - .path = "nat64 add prefix", - .short_help = "nat64 add prefix / [tenant-vrf ] " - "[del]", - .function = nat64_add_del_prefix_command_fn, -}; - -/*? - * @cliexpar - * @cliexstart{show nat64 prefix} - * Show NAT64 prefix. - * To show NAT64 prefix use: - * vpp# show nat64 prefix - * NAT64 prefix: - * 2001:db8::/32 tenant-vrf 0 - * 2001:db8:122:300::/56 tenant-vrf 10 - * @cliexend -?*/ -VLIB_CLI_COMMAND (show_nat64_prefix_command, static) = { - .path = "show nat64 prefix", - .short_help = "show nat64 prefix", - .function = nat64_show_prefix_command_fn, -}; - -/* *INDENT-ON* */ - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/snat/nat64_db.c b/src/plugins/snat/nat64_db.c deleted file mode 100644 index 9584827e..00000000 --- a/src/plugins/snat/nat64_db.c +++ /dev/null @@ -1,603 +0,0 @@ -/* - * 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. - */ -/** - * @file - * @brief NAT64 DB - */ -#include - -int -nat64_db_init (nat64_db_t * db) -{ - u32 bib_buckets = 1024; - u32 bib_memory_size = 128 << 20; - u32 st_buckets = 2048; - u32 st_memory_size = 256 << 20; - - clib_bihash_init_24_8 (&db->bib.in2out, "bib-in2out", bib_buckets, - bib_memory_size); - - clib_bihash_init_24_8 (&db->bib.out2in, "bib-out2in", bib_buckets, - bib_memory_size); - - clib_bihash_init_48_8 (&db->st.in2out, "st-in2out", st_buckets, - st_memory_size); - - clib_bihash_init_48_8 (&db->st.out2in, "st-out2in", st_buckets, - st_memory_size); - - return 0; -} - -nat64_db_bib_entry_t * -nat64_db_bib_entry_create (nat64_db_t * db, ip6_address_t * in_addr, - ip4_address_t * out_addr, u16 in_port, - u16 out_port, u32 fib_index, u8 proto, - u8 is_static) -{ - nat64_db_bib_entry_t *bibe; - nat64_db_bib_entry_key_t bibe_key; - clib_bihash_kv_24_8_t kv; - - /* create pool entry */ - switch (ip_proto_to_snat_proto (proto)) - { -/* *INDENT-OFF* */ -#define _(N, i, n, s) \ - case SNAT_PROTOCOL_##N: \ - pool_get (db->bib._##n##_bib, bibe); \ - kv.value = bibe - db->bib._##n##_bib; \ - break; - foreach_snat_protocol -#undef _ -/* *INDENT-ON* */ - default: - pool_get (db->bib._unk_proto_bib, bibe); - kv.value = bibe - db->bib._unk_proto_bib; - break; - } - memset (bibe, 0, sizeof (*bibe)); - bibe->in_addr.as_u64[0] = in_addr->as_u64[0]; - bibe->in_addr.as_u64[1] = in_addr->as_u64[1]; - bibe->in_port = in_port; - bibe->out_addr.as_u32 = out_addr->as_u32; - bibe->out_port = out_port; - bibe->fib_index = fib_index; - bibe->proto = proto; - bibe->is_static = is_static; - - /* create hash lookup */ - bibe_key.addr.as_u64[0] = bibe->in_addr.as_u64[0]; - bibe_key.addr.as_u64[1] = bibe->in_addr.as_u64[1]; - bibe_key.fib_index = bibe->fib_index; - bibe_key.port = bibe->in_port; - bibe_key.proto = bibe->proto; - bibe_key.rsvd = 0; - kv.key[0] = bibe_key.as_u64[0]; - kv.key[1] = bibe_key.as_u64[1]; - kv.key[2] = bibe_key.as_u64[2]; - clib_bihash_add_del_24_8 (&db->bib.in2out, &kv, 1); - - memset (&bibe_key.addr, 0, sizeof (bibe_key.addr)); - bibe_key.addr.ip4.as_u32 = bibe->out_addr.as_u32; - bibe_key.fib_index = 0; - bibe_key.port = bibe->out_port; - kv.key[0] = bibe_key.as_u64[0]; - kv.key[1] = bibe_key.as_u64[1]; - kv.key[2] = bibe_key.as_u64[2]; - clib_bihash_add_del_24_8 (&db->bib.out2in, &kv, 1); - - return bibe; -} - -void -nat64_db_bib_entry_free (nat64_db_t * db, nat64_db_bib_entry_t * bibe) -{ - nat64_db_bib_entry_key_t bibe_key; - clib_bihash_kv_24_8_t kv; - nat64_db_bib_entry_t *bib; - u32 *ste_to_be_free = 0, *ste_index, bibe_index; - nat64_db_st_entry_t *st, *ste; - - switch (ip_proto_to_snat_proto (bibe->proto)) - { -/* *INDENT-OFF* */ -#define _(N, i, n, s) \ - case SNAT_PROTOCOL_##N: \ - bib = db->bib._##n##_bib; \ - st = db->st._##n##_st; \ - break; - foreach_snat_protocol -#undef _ -/* *INDENT-ON* */ - default: - bib = db->bib._unk_proto_bib; - st = db->st._unk_proto_st; - break; - } - - bibe_index = bibe - bib; - - /* delete ST entries for static BIB entry */ - if (bibe->is_static) - { - pool_foreach (ste, st, ( - { - if (ste->bibe_index == bibe_index) - vec_add1 (ste_to_be_free, ste - st);} - )); - vec_foreach (ste_index, ste_to_be_free) - nat64_db_st_entry_free (db, pool_elt_at_index (st, ste_index[0])); - vec_free (ste_to_be_free); - } - - /* delete hash lookup */ - bibe_key.addr.as_u64[0] = bibe->in_addr.as_u64[0]; - bibe_key.addr.as_u64[1] = bibe->in_addr.as_u64[1]; - bibe_key.fib_index = bibe->fib_index; - bibe_key.port = bibe->in_port; - bibe_key.proto = bibe->proto; - bibe_key.rsvd = 0; - kv.key[0] = bibe_key.as_u64[0]; - kv.key[1] = bibe_key.as_u64[1]; - kv.key[2] = bibe_key.as_u64[2]; - clib_bihash_add_del_24_8 (&db->bib.in2out, &kv, 0); - - memset (&bibe_key.addr, 0, sizeof (bibe_key.addr)); - bibe_key.addr.ip4.as_u32 = bibe->out_addr.as_u32; - bibe_key.fib_index = 0; - bibe_key.port = bibe->out_port; - kv.key[0] = bibe_key.as_u64[0]; - kv.key[1] = bibe_key.as_u64[1]; - kv.key[2] = bibe_key.as_u64[2]; - clib_bihash_add_del_24_8 (&db->bib.out2in, &kv, 0); - - /* delete from pool */ - pool_put (bib, bibe); - -} - -nat64_db_bib_entry_t * -nat64_db_bib_entry_find (nat64_db_t * db, ip46_address_t * addr, u16 port, - u8 proto, u32 fib_index, u8 is_ip6) -{ - nat64_db_bib_entry_t *bibe = 0; - nat64_db_bib_entry_key_t bibe_key; - clib_bihash_kv_24_8_t kv, value; - nat64_db_bib_entry_t *bib; - - switch (ip_proto_to_snat_proto (proto)) - { -/* *INDENT-OFF* */ -#define _(N, i, n, s) \ - case SNAT_PROTOCOL_##N: \ - bib = db->bib._##n##_bib; \ - break; - foreach_snat_protocol -#undef _ -/* *INDENT-ON* */ - default: - bib = db->bib._unk_proto_bib; - break; - } - - bibe_key.addr.as_u64[0] = addr->as_u64[0]; - bibe_key.addr.as_u64[1] = addr->as_u64[1]; - bibe_key.fib_index = fib_index; - bibe_key.port = port; - bibe_key.proto = proto; - bibe_key.rsvd = 0; - - kv.key[0] = bibe_key.as_u64[0]; - kv.key[1] = bibe_key.as_u64[1]; - kv.key[2] = bibe_key.as_u64[2]; - - if (!clib_bihash_search_24_8 - (is_ip6 ? &db->bib.in2out : &db->bib.out2in, &kv, &value)) - bibe = pool_elt_at_index (bib, value.value); - - return bibe; -} - -void -nat64_db_bib_walk (nat64_db_t * db, u8 proto, - nat64_db_bib_walk_fn_t fn, void *ctx) -{ - nat64_db_bib_entry_t *bib, *bibe; - - if (proto == 255) - { - /* *INDENT-OFF* */ - #define _(N, i, n, s) \ - bib = db->bib._##n##_bib; \ - pool_foreach (bibe, bib, ({ \ - if (fn (bibe, ctx)) \ - return; \ - })); - foreach_snat_protocol - #undef _ - bib = db->bib._unk_proto_bib; - pool_foreach (bibe, bib, ({ - if (fn (bibe, ctx)) - return; - })); - /* *INDENT-ON* */ - } - else - { - switch (ip_proto_to_snat_proto (proto)) - { - /* *INDENT-OFF* */ - #define _(N, i, n, s) \ - case SNAT_PROTOCOL_##N: \ - bib = db->bib._##n##_bib; \ - break; - foreach_snat_protocol - #undef _ - /* *INDENT-ON* */ - default: - bib = db->bib._unk_proto_bib; - break; - } - - /* *INDENT-OFF* */ - pool_foreach (bibe, bib, - ({ - if (fn (bibe, ctx)) - return; - })); - /* *INDENT-ON* */ - } -} - -nat64_db_bib_entry_t * -nat64_db_bib_entry_by_index (nat64_db_t * db, u8 proto, u32 bibe_index) -{ - nat64_db_bib_entry_t *bib; - - switch (ip_proto_to_snat_proto (proto)) - { -/* *INDENT-OFF* */ -#define _(N, i, n, s) \ - case SNAT_PROTOCOL_##N: \ - bib = db->bib._##n##_bib; \ - break; - foreach_snat_protocol -#undef _ -/* *INDENT-ON* */ - default: - bib = db->bib._unk_proto_bib; - break; - } - - return pool_elt_at_index (bib, bibe_index); -} - -void -nat64_db_st_walk (nat64_db_t * db, u8 proto, - nat64_db_st_walk_fn_t fn, void *ctx) -{ - nat64_db_st_entry_t *st, *ste; - - if (proto == 255) - { - /* *INDENT-OFF* */ - #define _(N, i, n, s) \ - st = db->st._##n##_st; \ - pool_foreach (ste, st, ({ \ - if (fn (ste, ctx)) \ - return; \ - })); - foreach_snat_protocol - #undef _ - st = db->st._unk_proto_st; - pool_foreach (ste, st, ({ - if (fn (ste, ctx)) - return; - })); - /* *INDENT-ON* */ - } - else - { - switch (ip_proto_to_snat_proto (proto)) - { - /* *INDENT-OFF* */ - #define _(N, i, n, s) \ - case SNAT_PROTOCOL_##N: \ - st = db->st._##n##_st; \ - break; - foreach_snat_protocol - #undef _ - /* *INDENT-ON* */ - default: - st = db->st._unk_proto_st; - break; - } - - /* *INDENT-OFF* */ - pool_foreach (ste, st, - ({ - if (fn (ste, ctx)) - return; - })); - /* *INDENT-ON* */ - } -} - -nat64_db_st_entry_t * -nat64_db_st_entry_create (nat64_db_t * db, nat64_db_bib_entry_t * bibe, - ip6_address_t * in_r_addr, - ip4_address_t * out_r_addr, u16 r_port) -{ - nat64_db_st_entry_t *ste; - nat64_db_bib_entry_t *bib; - nat64_db_st_entry_key_t ste_key; - clib_bihash_kv_48_8_t kv; - - /* create pool entry */ - switch (ip_proto_to_snat_proto (bibe->proto)) - { -/* *INDENT-OFF* */ -#define _(N, i, n, s) \ - case SNAT_PROTOCOL_##N: \ - pool_get (db->st._##n##_st, ste); \ - kv.value = ste - db->st._##n##_st; \ - bib = db->bib._##n##_bib; \ - break; - foreach_snat_protocol -#undef _ -/* *INDENT-ON* */ - default: - pool_get (db->st._unk_proto_st, ste); - kv.value = ste - db->st._unk_proto_st; - bib = db->bib._unk_proto_bib; - break; - } - memset (ste, 0, sizeof (*ste)); - ste->in_r_addr.as_u64[0] = in_r_addr->as_u64[0]; - ste->in_r_addr.as_u64[1] = in_r_addr->as_u64[1]; - ste->out_r_addr.as_u32 = out_r_addr->as_u32; - ste->r_port = r_port; - ste->bibe_index = bibe - bib; - ste->proto = bibe->proto; - - /* increment session number for BIB entry */ - bibe->ses_num++; - - /* create hash lookup */ - memset (&ste_key, 0, sizeof (ste_key)); - ste_key.l_addr.as_u64[0] = bibe->in_addr.as_u64[0]; - ste_key.l_addr.as_u64[1] = bibe->in_addr.as_u64[1]; - ste_key.r_addr.as_u64[0] = ste->in_r_addr.as_u64[0]; - ste_key.r_addr.as_u64[1] = ste->in_r_addr.as_u64[1]; - ste_key.fib_index = bibe->fib_index; - ste_key.l_port = bibe->in_port; - ste_key.r_port = ste->r_port; - ste_key.proto = ste->proto; - kv.key[0] = ste_key.as_u64[0]; - kv.key[1] = ste_key.as_u64[1]; - kv.key[2] = ste_key.as_u64[2]; - kv.key[3] = ste_key.as_u64[3]; - kv.key[4] = ste_key.as_u64[4]; - kv.key[5] = ste_key.as_u64[5]; - clib_bihash_add_del_48_8 (&db->st.in2out, &kv, 1); - - memset (&ste_key, 0, sizeof (ste_key)); - ste_key.l_addr.ip4.as_u32 = bibe->out_addr.as_u32; - ste_key.r_addr.ip4.as_u32 = ste->out_r_addr.as_u32; - ste_key.l_port = bibe->out_port; - ste_key.r_port = ste->r_port; - ste_key.proto = ste->proto; - kv.key[0] = ste_key.as_u64[0]; - kv.key[1] = ste_key.as_u64[1]; - kv.key[2] = ste_key.as_u64[2]; - kv.key[3] = ste_key.as_u64[3]; - kv.key[4] = ste_key.as_u64[4]; - kv.key[5] = ste_key.as_u64[5]; - clib_bihash_add_del_48_8 (&db->st.out2in, &kv, 1); - - return ste; -} - -void -nat64_db_st_entry_free (nat64_db_t * db, nat64_db_st_entry_t * ste) -{ - nat64_db_st_entry_t *st; - nat64_db_bib_entry_t *bib, *bibe; - nat64_db_st_entry_key_t ste_key; - clib_bihash_kv_48_8_t kv; - - switch (ip_proto_to_snat_proto (ste->proto)) - { -/* *INDENT-OFF* */ -#define _(N, i, n, s) \ - case SNAT_PROTOCOL_##N: \ - st = db->st._##n##_st; \ - bib = db->bib._##n##_bib; \ - break; - foreach_snat_protocol -#undef _ -/* *INDENT-ON* */ - default: - st = db->st._unk_proto_st; - bib = db->bib._unk_proto_bib; - break; - } - - bibe = pool_elt_at_index (bib, ste->bibe_index); - - /* delete hash lookup */ - memset (&ste_key, 0, sizeof (ste_key)); - ste_key.l_addr.as_u64[0] = bibe->in_addr.as_u64[0]; - ste_key.l_addr.as_u64[1] = bibe->in_addr.as_u64[1]; - ste_key.r_addr.as_u64[0] = ste->in_r_addr.as_u64[0]; - ste_key.r_addr.as_u64[1] = ste->in_r_addr.as_u64[1]; - ste_key.fib_index = bibe->fib_index; - ste_key.l_port = bibe->in_port; - ste_key.r_port = ste->r_port; - ste_key.proto = ste->proto; - kv.key[0] = ste_key.as_u64[0]; - kv.key[1] = ste_key.as_u64[1]; - kv.key[2] = ste_key.as_u64[2]; - kv.key[3] = ste_key.as_u64[3]; - kv.key[4] = ste_key.as_u64[4]; - kv.key[5] = ste_key.as_u64[5]; - clib_bihash_add_del_48_8 (&db->st.in2out, &kv, 0); - - memset (&ste_key, 0, sizeof (ste_key)); - ste_key.l_addr.ip4.as_u32 = bibe->out_addr.as_u32; - ste_key.r_addr.ip4.as_u32 = ste->out_r_addr.as_u32; - ste_key.l_port = bibe->out_port; - ste_key.r_port = ste->r_port; - ste_key.proto = ste->proto; - kv.key[0] = ste_key.as_u64[0]; - kv.key[1] = ste_key.as_u64[1]; - kv.key[2] = ste_key.as_u64[2]; - kv.key[3] = ste_key.as_u64[3]; - kv.key[4] = ste_key.as_u64[4]; - kv.key[5] = ste_key.as_u64[5]; - clib_bihash_add_del_48_8 (&db->st.out2in, &kv, 0); - - /* delete from pool */ - pool_put (st, ste); - - /* decrement session number for BIB entry */ - bibe->ses_num--; - - /* delete BIB entry if last session and dynamic */ - if (!bibe->is_static && !bibe->ses_num) - nat64_db_bib_entry_free (db, bibe); -} - -nat64_db_st_entry_t * -nat64_db_st_entry_find (nat64_db_t * db, ip46_address_t * l_addr, - ip46_address_t * r_addr, u16 l_port, u16 r_port, - u8 proto, u32 fib_index, u8 is_ip6) -{ - nat64_db_st_entry_t *ste = 0; - nat64_db_st_entry_t *st; - nat64_db_st_entry_key_t ste_key; - clib_bihash_kv_48_8_t kv, value; - - switch (ip_proto_to_snat_proto (proto)) - { -/* *INDENT-OFF* */ -#define _(N, i, n, s) \ - case SNAT_PROTOCOL_##N: \ - st = db->st._##n##_st; \ - break; - foreach_snat_protocol -#undef _ -/* *INDENT-ON* */ - default: - st = db->st._unk_proto_st; - break; - } - - memset (&ste_key, 0, sizeof (ste_key)); - ste_key.l_addr.as_u64[0] = l_addr->as_u64[0]; - ste_key.l_addr.as_u64[1] = l_addr->as_u64[1]; - ste_key.r_addr.as_u64[0] = r_addr->as_u64[0]; - ste_key.r_addr.as_u64[1] = r_addr->as_u64[1]; - ste_key.fib_index = fib_index; - ste_key.l_port = l_port; - ste_key.r_port = r_port; - ste_key.proto = proto; - kv.key[0] = ste_key.as_u64[0]; - kv.key[1] = ste_key.as_u64[1]; - kv.key[2] = ste_key.as_u64[2]; - kv.key[3] = ste_key.as_u64[3]; - kv.key[4] = ste_key.as_u64[4]; - kv.key[5] = ste_key.as_u64[5]; - - if (!clib_bihash_search_48_8 - (is_ip6 ? &db->st.in2out : &db->st.out2in, &kv, &value)) - ste = pool_elt_at_index (st, value.value); - - return ste; -} - -void -nad64_db_st_free_expired (nat64_db_t * db, u32 now) -{ - u32 *ste_to_be_free = 0, *ste_index; - nat64_db_st_entry_t *st, *ste; - -/* *INDENT-OFF* */ -#define _(N, i, n, s) \ - st = db->st._##n##_st; \ - pool_foreach (ste, st, ({\ - if (i == SNAT_PROTOCOL_TCP && !ste->tcp_state) \ - continue; \ - if (ste->expire < now) \ - vec_add1 (ste_to_be_free, ste - st); \ - })); \ - vec_foreach (ste_index, ste_to_be_free) \ - nat64_db_st_entry_free (db, pool_elt_at_index(st, ste_index[0])); \ - vec_free (ste_to_be_free); \ - ste_to_be_free = 0; - foreach_snat_protocol -#undef _ - st = db->st._unk_proto_st; - pool_foreach (ste, st, ({ - if (ste->expire < now) - vec_add1 (ste_to_be_free, ste - st); - })); - vec_foreach (ste_index, ste_to_be_free) - nat64_db_st_entry_free (db, pool_elt_at_index(st, ste_index[0])); - vec_free (ste_to_be_free); -/* *INDENT-ON* */ -} - -void -nat64_db_free_out_addr (nat64_db_t * db, ip4_address_t * out_addr) -{ - u32 *ste_to_be_free = 0, *ste_index; - nat64_db_st_entry_t *st, *ste; - nat64_db_bib_entry_t *bibe; - -/* *INDENT-OFF* */ -#define _(N, i, n, s) \ - st = db->st._##n##_st; \ - pool_foreach (ste, st, ({ \ - bibe = pool_elt_at_index (db->bib._##n##_bib, ste->bibe_index); \ - if (bibe->out_addr.as_u32 == out_addr->as_u32) \ - vec_add1 (ste_to_be_free, ste - st); \ - })); \ - vec_foreach (ste_index, ste_to_be_free) \ - nat64_db_st_entry_free (db, pool_elt_at_index(st, ste_index[0])); \ - vec_free (ste_to_be_free); \ - ste_to_be_free = 0; - foreach_snat_protocol -#undef _ - st = db->st._unk_proto_st; - pool_foreach (ste, st, ({ - bibe = pool_elt_at_index (db->bib._unk_proto_bib, ste->bibe_index); - if (bibe->out_addr.as_u32 == out_addr->as_u32) - vec_add1 (ste_to_be_free, ste - st); - })); - vec_foreach (ste_index, ste_to_be_free) - nat64_db_st_entry_free (db, pool_elt_at_index(st, ste_index[0])); - vec_free (ste_to_be_free); -/* *INDENT-ON* */ -} - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/snat/nat64_db.h b/src/plugins/snat/nat64_db.h deleted file mode 100644 index 1e2dcc93..00000000 --- a/src/plugins/snat/nat64_db.h +++ /dev/null @@ -1,307 +0,0 @@ -/* - * 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. - */ -/** - * @file - * @brief NAT64 DB - */ -#ifndef __included_nat64_db_h__ -#define __included_nat64_db_h__ - -#include -#include -#include - - -typedef struct -{ - union - { - struct - { - ip46_address_t addr; - u32 fib_index; - u16 port; - u8 proto; - u8 rsvd; - }; - u64 as_u64[3]; - }; -} nat64_db_bib_entry_key_t; - -/* *INDENT-OFF* */ -typedef CLIB_PACKED(struct -{ - ip6_address_t in_addr; - u16 in_port; - ip4_address_t out_addr; - u16 out_port; - u32 fib_index; - u32 ses_num; - u8 proto; - u8 is_static; -}) nat64_db_bib_entry_t; -/* *INDENT-ON* */ - -typedef struct -{ - /* BIBs */ -/* *INDENT-OFF* */ -#define _(N, i, n, s) \ - nat64_db_bib_entry_t *_##n##_bib; - foreach_snat_protocol -#undef _ -/* *INDENT-ON* */ - nat64_db_bib_entry_t *_unk_proto_bib; - - /* BIB lookup */ - clib_bihash_24_8_t in2out; - clib_bihash_24_8_t out2in; -} nat64_db_bib_t; - -typedef struct -{ - union - { - struct - { - ip46_address_t l_addr; - ip46_address_t r_addr; - u32 fib_index; - u16 l_port; - u16 r_port; - u8 proto; - u8 rsvd[7]; - }; - u64 as_u64[6]; - }; -} nat64_db_st_entry_key_t; - -/* *INDENT-OFF* */ -typedef CLIB_PACKED(struct -{ - ip6_address_t in_r_addr; - ip4_address_t out_r_addr; - u16 r_port; - u32 bibe_index; - u32 expire; - u8 proto; - u8 tcp_state; -}) nat64_db_st_entry_t; -/* *INDENT-ON* */ - -typedef struct -{ - /* session tables */ -/* *INDENT-OFF* */ -#define _(N, i, n, s) \ - nat64_db_st_entry_t *_##n##_st; - foreach_snat_protocol -#undef _ -/* *INDENT-ON* */ - nat64_db_st_entry_t *_unk_proto_st; - - /* session lookup */ - clib_bihash_48_8_t in2out; - clib_bihash_48_8_t out2in; -} nat64_db_st_t; - -typedef struct -{ - nat64_db_bib_t bib; - nat64_db_st_t st; -} nat64_db_t; - -/** - * @brief Initialize NAT64 DB. - * - * @param db NAT64 DB. - * - * @returns 0 on success, non-zero value otherwise. - */ -int nat64_db_init (nat64_db_t * db); - -/** - * @brief Create new NAT64 BIB entry. - * - * @param db NAT64 DB. - * @param in_addr Inside IPv6 address. - * @param out_addr Outside IPv4 address. - * @param in_port Inside port number. - * @param out_port Outside port number. - * @param fib_index FIB index. - * @param proto L4 protocol. - * @param is_static 1 if static, 0 if dynamic. - * - * @returns BIB entry on success, 0 otherwise. - */ -nat64_db_bib_entry_t *nat64_db_bib_entry_create (nat64_db_t * db, - ip6_address_t * in_addr, - ip4_address_t * out_addr, - u16 in_port, u16 out_port, - u32 fib_index, - u8 proto, u8 is_static); - -/** - * @brief Free NAT64 BIB entry. - * - * @param db NAT64 DB. - * @param bibe BIB entry. - */ -void nat64_db_bib_entry_free (nat64_db_t * db, nat64_db_bib_entry_t * bibe); - -/** - * @brief Call back function when walking NAT64 BIB, non-zero - * return value stop walk. - */ -typedef int (*nat64_db_bib_walk_fn_t) (nat64_db_bib_entry_t * bibe, - void *ctx); -/** - * @brief Walk NAT64 BIB. - * - * @param db NAT64 DB. - * @param proto BIB L4 protocol: - * - 255 all BIBs - * - 6 TCP BIB - * - 17 UDP BIB - * - 1/58 ICMP BIB - * - otherwise "unknown" protocol BIB - * @param fn The function to invoke on each entry visited. - * @param ctx A context passed in the visit function. - */ -void nat64_db_bib_walk (nat64_db_t * db, u8 proto, - nat64_db_bib_walk_fn_t fn, void *ctx); - -/** - * @brief Find NAT64 BIB entry. - * - * @param db NAT64 DB. - * @param addr IP address. - * @param port Port number. - * @param proto L4 protocol. - * @param fib_index FIB index. - * @param is_ip6 1 if find by IPv6 (inside) address, 0 by IPv4 (outside). - * - * @return BIB entry if found. - */ -nat64_db_bib_entry_t *nat64_db_bib_entry_find (nat64_db_t * db, - ip46_address_t * addr, - u16 port, - u8 proto, - u32 fib_index, u8 is_ip6); - -/** - * @brief Get BIB entry by index and protocol. - * - * @param db NAT64 DB. - * @param proto L4 protocol. - * @param bibe_index BIB entry index. - * - * @return BIB entry if found. - */ -nat64_db_bib_entry_t *nat64_db_bib_entry_by_index (nat64_db_t * db, - u8 proto, u32 bibe_index); -/** - * @brief Create new NAT64 session table entry. - * - * @param db NAT64 DB. - * @param bibe Corresponding BIB entry. - * @param in_r_addr Inside IPv6 address of the remote host. - * @param out_r_addr Outside IPv4 address of the remote host. - * @param r_port Remote host port number. - * - * @returns BIB entry on success, 0 otherwise. - */ -nat64_db_st_entry_t *nat64_db_st_entry_create (nat64_db_t * db, - nat64_db_bib_entry_t * bibe, - ip6_address_t * in_r_addr, - ip4_address_t * out_r_addr, - u16 r_port); - -/** - * @brief Free NAT64 session table entry. - * - * @param db NAT64 DB. - * @param ste Session table entry. - */ -void nat64_db_st_entry_free (nat64_db_t * db, nat64_db_st_entry_t * ste); - -/** - * @brief Find NAT64 session table entry. - * - * @param db NAT64 DB. - * @param l_addr Local host address. - * @param r_addr Remote host address. - * @param l_port Local host port number. - * @param r_port Remote host port number. - * @param proto L4 protocol. - * @param fib_index FIB index. - * @param is_ip6 1 if find by IPv6 (inside) address, 0 by IPv4 (outside). - * - * @return BIB entry if found. - */ -nat64_db_st_entry_t *nat64_db_st_entry_find (nat64_db_t * db, - ip46_address_t * l_addr, - ip46_address_t * r_addr, - u16 l_port, u16 r_port, - u8 proto, - u32 fib_index, u8 is_ip6); - -/** - * @brief Call back function when walking NAT64 session table, non-zero - * return value stop walk. - */ -typedef int (*nat64_db_st_walk_fn_t) (nat64_db_st_entry_t * ste, void *ctx); - -/** - * @brief Walk NAT64 session table. - * - * @param db NAT64 DB. - * @param proto L4 protocol: - * - 255 all session tables - * - 6 TCP session table - * - 17 UDP session table - * - 1/58 ICMP session table - * - otherwise "unknown" protocol session table - * @param fn The function to invoke on each entry visited. - * @param ctx A context passed in the visit function. - */ -void nat64_db_st_walk (nat64_db_t * db, u8 proto, - nat64_db_st_walk_fn_t fn, void *ctx); - -/** - * @brief Free expired session entries in session tables. - * - * @param db NAT64 DB. - * @param now Current time. - */ -void nad64_db_st_free_expired (nat64_db_t * db, u32 now); - -/** - * @brief Free sessions using specific outside address. - * - * @param db NAT64 DB. - * @param out_addr Outside address to match. - */ -void nat64_db_free_out_addr (nat64_db_t * db, ip4_address_t * out_addr); - -#endif /* __included_nat64_db_h__ */ - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/snat/nat64_doc.md b/src/plugins/snat/nat64_doc.md deleted file mode 100644 index f94467da..00000000 --- a/src/plugins/snat/nat64_doc.md +++ /dev/null @@ -1,73 +0,0 @@ -# Stateful NAT64: Network Address and Protocol Translation from IPv6 Clients to IPv4 Servers {#nat64_doc} - -## Introduction - -Stateful NAT64 in VPP allows IPv6-only clients to contact IPv4 servers using unicast UDP, TCP, or ICMP based on RFC 6146. - -## Configuration - -### Enable/disable NAT64 feature on the interface - -> set interface nat64 in|out [del] - -in: inside/local/IPv6 network -out: outside/external/IPv4 network -intfc: interface name - -### Add/delete NAT64 pool address - -One or more public IPv4 addresses assigned to a NAT64 are shared among several IPv6-only clients. - -> nat64 add pool address [- ] [tenant-vrf ] [del] - -ip4-range-start: First IPv4 address of the range -ip4-range-end: Last IPv4 address of the range (optional, not used for single address) -tenant-vrf-id: VRF id of the tenant associated with the pool address (optional, if not set pool address is global) - -### Add/delete static BIB entry - -Stateful NAT64 also supports IPv4-initiated communications to a subset of the IPv6 hosts through staticaly configured bindings. - -> nat64 add static bib tcp|udp|icmp [vfr ] [del] - -ip6-addr: inside IPv6 address of the host -in-port: inside port or ICMPv6 identifier -ip4-addr: outside IPv4 address of the host -out-port: outside port or ICMPv4 identifier -table-id: VRF id of the tenant associated with the BIB entry (optional, default use global VRF) - -### Set NAT64 session timeouts - -Session is deleted when timer expires. If all sessions corresponding to a dynamically create BIB entry are deleted, then the BIB entry is also deleted. When packets are flowing sessiom timer is refreshed to keep the session alive. - -> set nat64 timeouts udp icmp tcp-trans tcp-est tcp-incoming-syn | reset - -udp: UDP session timeout value (default 300sec) -icmp: ICMP session timeout value (default 60sec) -tcp-trans: transitory TCP session timeout value (default 240sec) -tcp-est: established TCP session timeout value (default 7440sec) -tcp-incoming-syn: incoming SYN TCP session timeout value (default 6sec) -reset: reset timers to default values - -### Set NAT64 prefix - -Stateful NAT64 support the algorithm for generating IPv6 representations of IPv4 addresses defined in RFC 6052. If no prefix is configured, Well-Known Prefix (64:ff9b::/96) is used. - -> nat64 add prefix / [tenant-vrf ] [del] - -ip6-prefix: IPv6 prefix -plen: prefix length (valid values: 32, 40, 48, 56, 64, or 96) -tenant-vrf: VRF id of the tenant associated with the prefix - -### Show commands - -> show nat64 pool -> show nat64 interfaces -> show nat64 bib tcp|udp|icmp -> show nat64 session table tcp|udp|icmp -> show nat64 tiemouts -> show nat64 prefix - -## Notes - -Multi thread is not supported yet (CLI/API commands are disabled when VPP runs with multiple threads). diff --git a/src/plugins/snat/nat64_in2out.c b/src/plugins/snat/nat64_in2out.c deleted file mode 100644 index 8c67fec2..00000000 --- a/src/plugins/snat/nat64_in2out.c +++ /dev/null @@ -1,1118 +0,0 @@ -/* - * 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. - */ -/** - * @file - * @brief NAT64 IPv6 to IPv4 translation (inside to outside network) - */ - -#include -#include -#include - -typedef struct -{ - u32 sw_if_index; - u32 next_index; - u8 is_slow_path; -} nat64_in2out_trace_t; - -static u8 * -format_nat64_in2out_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 *); - nat64_in2out_trace_t *t = va_arg (*args, nat64_in2out_trace_t *); - char *tag; - - tag = t->is_slow_path ? "NAT64-in2out-slowpath" : "NAT64-in2out"; - - s = - format (s, "%s: sw_if_index %d, next index %d", tag, t->sw_if_index, - t->next_index); - - return s; -} - -vlib_node_registration_t nat64_in2out_node; -vlib_node_registration_t nat64_in2out_slowpath_node; - -#define foreach_nat64_in2out_error \ -_(UNSUPPORTED_PROTOCOL, "unsupported protocol") \ -_(IN2OUT_PACKETS, "good in2out packets processed") \ -_(NO_TRANSLATION, "no translation") \ -_(UNKNOWN, "unknown") - -typedef enum -{ -#define _(sym,str) NAT64_IN2OUT_ERROR_##sym, - foreach_nat64_in2out_error -#undef _ - NAT64_IN2OUT_N_ERROR, -} nat64_in2out_error_t; - -static char *nat64_in2out_error_strings[] = { -#define _(sym,string) string, - foreach_nat64_in2out_error -#undef _ -}; - -typedef enum -{ - NAT64_IN2OUT_NEXT_IP4_LOOKUP, - NAT64_IN2OUT_NEXT_IP6_LOOKUP, - NAT64_IN2OUT_NEXT_DROP, - NAT64_IN2OUT_NEXT_SLOWPATH, - NAT64_IN2OUT_N_NEXT, -} nat64_in2out_next_t; - -typedef struct nat64_in2out_set_ctx_t_ -{ - vlib_buffer_t *b; - vlib_main_t *vm; -} nat64_in2out_set_ctx_t; - -/** - * @brief Check whether is a hairpinning. - * - * If the destination IP address of the packet is an IPv4 address assigned to - * the NAT64 itself, then the packet is a hairpin packet. - * - * param dst_addr Destination address of the packet. - * - * @returns 1 if hairpinning, otherwise 0. - */ -static_always_inline int -is_hairpinning (ip6_address_t * dst_addr) -{ - nat64_main_t *nm = &nat64_main; - int i; - - for (i = 0; i < vec_len (nm->addr_pool); i++) - { - if (nm->addr_pool[i].addr.as_u32 == dst_addr->as_u32[3]) - return 1; - } - - return 0; -} - -static int -nat64_in2out_tcp_udp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, - void *arg) -{ - nat64_main_t *nm = &nat64_main; - nat64_in2out_set_ctx_t *ctx = arg; - nat64_db_bib_entry_t *bibe; - nat64_db_st_entry_t *ste; - ip46_address_t saddr, daddr; - u32 sw_if_index, fib_index; - udp_header_t *udp = ip6_next_header (ip6); - u8 proto = ip6->protocol; - u16 sport = udp->src_port; - u16 dport = udp->dst_port; - - sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX]; - fib_index = - fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index); - - saddr.as_u64[0] = ip6->src_address.as_u64[0]; - saddr.as_u64[1] = ip6->src_address.as_u64[1]; - daddr.as_u64[0] = ip6->dst_address.as_u64[0]; - daddr.as_u64[1] = ip6->dst_address.as_u64[1]; - - ste = - nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto, - fib_index, 1); - - if (ste) - { - bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); - if (!bibe) - return -1; - } - else - { - bibe = - nat64_db_bib_entry_find (&nm->db, &saddr, sport, proto, fib_index, 1); - - if (!bibe) - { - u16 out_port; - ip4_address_t out_addr; - if (nat64_alloc_out_addr_and_port - (fib_index, ip_proto_to_snat_proto (proto), &out_addr, - &out_port)) - return -1; - - bibe = - nat64_db_bib_entry_create (&nm->db, &ip6->src_address, &out_addr, - sport, clib_host_to_net_u16 (out_port), - fib_index, proto, 0); - if (!bibe) - return -1; - } - - nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index); - ste = - nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address, - &daddr.ip4, dport); - if (!ste) - return -1; - } - - nat64_session_reset_timeout (ste, ctx->vm); - - ip4->src_address.as_u32 = bibe->out_addr.as_u32; - udp->src_port = bibe->out_port; - - ip4->dst_address.as_u32 = ste->out_r_addr.as_u32; - - if (proto == IP_PROTOCOL_TCP) - { - u16 *checksum; - ip_csum_t csum; - tcp_header_t *tcp = ip6_next_header (ip6); - - checksum = &tcp->checksum; - csum = ip_csum_sub_even (*checksum, sport); - csum = ip_csum_add_even (csum, udp->src_port); - *checksum = ip_csum_fold (csum); - } - - return 0; -} - -static int -nat64_in2out_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, void *arg) -{ - nat64_main_t *nm = &nat64_main; - nat64_in2out_set_ctx_t *ctx = arg; - nat64_db_bib_entry_t *bibe; - nat64_db_st_entry_t *ste; - ip46_address_t saddr, daddr; - u32 sw_if_index, fib_index; - icmp46_header_t *icmp = ip6_next_header (ip6); - - sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX]; - fib_index = - fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index); - - saddr.as_u64[0] = ip6->src_address.as_u64[0]; - saddr.as_u64[1] = ip6->src_address.as_u64[1]; - daddr.as_u64[0] = ip6->dst_address.as_u64[0]; - daddr.as_u64[1] = ip6->dst_address.as_u64[1]; - - if (icmp->type == ICMP4_echo_request || icmp->type == ICMP4_echo_reply) - { - u16 in_id = ((u16 *) (icmp))[2]; - ste = - nat64_db_st_entry_find (&nm->db, &saddr, &daddr, in_id, 0, - IP_PROTOCOL_ICMP, fib_index, 1); - - if (ste) - { - bibe = - nat64_db_bib_entry_by_index (&nm->db, IP_PROTOCOL_ICMP, - ste->bibe_index); - if (!bibe) - return -1; - } - else - { - bibe = - nat64_db_bib_entry_find (&nm->db, &saddr, in_id, - IP_PROTOCOL_ICMP, fib_index, 1); - - if (!bibe) - { - u16 out_id; - ip4_address_t out_addr; - if (nat64_alloc_out_addr_and_port - (fib_index, SNAT_PROTOCOL_ICMP, &out_addr, &out_id)) - return -1; - - bibe = - nat64_db_bib_entry_create (&nm->db, &ip6->src_address, - &out_addr, in_id, - clib_host_to_net_u16 (out_id), - fib_index, IP_PROTOCOL_ICMP, 0); - if (!bibe) - return -1; - } - - nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index); - ste = - nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address, - &daddr.ip4, 0); - if (!ste) - return -1; - } - - nat64_session_reset_timeout (ste, ctx->vm); - - ip4->src_address.as_u32 = bibe->out_addr.as_u32; - ((u16 *) (icmp))[2] = bibe->out_port; - - ip4->dst_address.as_u32 = ste->out_r_addr.as_u32; - } - else - { - if (!vec_len (nm->addr_pool)) - return -1; - - ip4->src_address.as_u32 = nm->addr_pool[0].addr.as_u32; - nat64_extract_ip4 (&ip6->dst_address, &ip4->dst_address, fib_index); - } - - return 0; -} - -static int -nat64_in2out_inner_icmp_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, - void *arg) -{ - nat64_main_t *nm = &nat64_main; - nat64_in2out_set_ctx_t *ctx = arg; - nat64_db_st_entry_t *ste; - nat64_db_bib_entry_t *bibe; - ip46_address_t saddr, daddr; - u32 sw_if_index, fib_index; - u8 proto = ip6->protocol; - - sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX]; - fib_index = - fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index); - - saddr.as_u64[0] = ip6->src_address.as_u64[0]; - saddr.as_u64[1] = ip6->src_address.as_u64[1]; - daddr.as_u64[0] = ip6->dst_address.as_u64[0]; - daddr.as_u64[1] = ip6->dst_address.as_u64[1]; - - if (proto == IP_PROTOCOL_ICMP6) - { - icmp46_header_t *icmp = ip6_next_header (ip6); - u16 in_id = ((u16 *) (icmp))[2]; - proto = IP_PROTOCOL_ICMP; - - if (! - (icmp->type == ICMP4_echo_request - || icmp->type == ICMP4_echo_reply)) - return -1; - - ste = - nat64_db_st_entry_find (&nm->db, &daddr, &saddr, in_id, 0, proto, - fib_index, 1); - if (!ste) - return -1; - - bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); - if (!bibe) - return -1; - - ip4->dst_address.as_u32 = bibe->out_addr.as_u32; - ((u16 *) (icmp))[2] = bibe->out_port; - ip4->src_address.as_u32 = ste->out_r_addr.as_u32; - } - else - { - udp_header_t *udp = ip6_next_header (ip6); - tcp_header_t *tcp = ip6_next_header (ip6); - u16 *checksum; - ip_csum_t csum; - - u16 sport = udp->src_port; - u16 dport = udp->dst_port; - - ste = - nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto, - fib_index, 1); - if (!ste) - return -1; - - bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); - if (!bibe) - return -1; - - ip4->dst_address.as_u32 = bibe->out_addr.as_u32; - udp->dst_port = bibe->out_port; - ip4->src_address.as_u32 = ste->out_r_addr.as_u32; - - if (proto == IP_PROTOCOL_TCP) - checksum = &tcp->checksum; - else - checksum = &udp->checksum; - csum = ip_csum_sub_even (*checksum, dport); - csum = ip_csum_add_even (csum, udp->dst_port); - *checksum = ip_csum_fold (csum); - } - - return 0; -} - -typedef struct unk_proto_st_walk_ctx_t_ -{ - ip6_address_t src_addr; - ip6_address_t dst_addr; - ip4_address_t out_addr; - u32 fib_index; - u8 proto; -} unk_proto_st_walk_ctx_t; - -static int -unk_proto_st_walk (nat64_db_st_entry_t * ste, void *arg) -{ - nat64_main_t *nm = &nat64_main; - unk_proto_st_walk_ctx_t *ctx = arg; - nat64_db_bib_entry_t *bibe; - ip46_address_t saddr, daddr; - - if (ip46_address_is_equal (&ste->in_r_addr, &ctx->dst_addr)) - { - bibe = - nat64_db_bib_entry_by_index (&nm->db, ste->proto, ste->bibe_index); - if (!bibe) - return -1; - - if (ip46_address_is_equal (&bibe->in_addr, &ctx->src_addr) - && bibe->fib_index == ctx->fib_index) - { - memset (&saddr, 0, sizeof (saddr)); - saddr.ip4.as_u32 = bibe->out_addr.as_u32; - memset (&daddr, 0, sizeof (daddr)); - nat64_extract_ip4 (&ctx->dst_addr, &daddr.ip4, ctx->fib_index); - - if (nat64_db_st_entry_find - (&nm->db, &daddr, &saddr, 0, 0, ctx->proto, ctx->fib_index, 0)) - return -1; - - ctx->out_addr.as_u32 = bibe->out_addr.as_u32; - return 1; - } - } - - return 0; -} - -static int -nat64_in2out_unk_proto_set_cb (ip6_header_t * ip6, ip4_header_t * ip4, - void *arg) -{ - nat64_main_t *nm = &nat64_main; - nat64_in2out_set_ctx_t *ctx = arg; - nat64_db_bib_entry_t *bibe; - nat64_db_st_entry_t *ste; - ip46_address_t saddr, daddr, addr; - u32 sw_if_index, fib_index; - u8 proto = ip6->protocol; - int i; - - sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX]; - fib_index = - fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index); - - saddr.as_u64[0] = ip6->src_address.as_u64[0]; - saddr.as_u64[1] = ip6->src_address.as_u64[1]; - daddr.as_u64[0] = ip6->dst_address.as_u64[0]; - daddr.as_u64[1] = ip6->dst_address.as_u64[1]; - - ste = - nat64_db_st_entry_find (&nm->db, &saddr, &daddr, 0, 0, proto, fib_index, - 1); - - if (ste) - { - bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); - if (!bibe) - return -1; - } - else - { - bibe = - nat64_db_bib_entry_find (&nm->db, &saddr, 0, proto, fib_index, 1); - - if (!bibe) - { - /* Choose same out address as for TCP/UDP session to same dst */ - unk_proto_st_walk_ctx_t ctx = { - .src_addr.as_u64[0] = ip6->src_address.as_u64[0], - .src_addr.as_u64[1] = ip6->src_address.as_u64[1], - .dst_addr.as_u64[0] = ip6->dst_address.as_u64[0], - .dst_addr.as_u64[1] = ip6->dst_address.as_u64[1], - .out_addr.as_u32 = 0, - .fib_index = fib_index, - .proto = proto, - }; - - nat64_db_st_walk (&nm->db, IP_PROTOCOL_TCP, unk_proto_st_walk, - &ctx); - - if (!ctx.out_addr.as_u32) - nat64_db_st_walk (&nm->db, IP_PROTOCOL_UDP, unk_proto_st_walk, - &ctx); - - /* Verify if out address is not already in use for protocol */ - memset (&addr, 0, sizeof (addr)); - addr.ip4.as_u32 = ctx.out_addr.as_u32; - if (nat64_db_bib_entry_find (&nm->db, &addr, 0, proto, 0, 0)) - ctx.out_addr.as_u32 = 0; - - if (!ctx.out_addr.as_u32) - { - for (i = 0; i < vec_len (nm->addr_pool); i++) - { - addr.ip4.as_u32 = nm->addr_pool[i].addr.as_u32; - if (!nat64_db_bib_entry_find - (&nm->db, &addr, 0, proto, 0, 0)) - break; - } - } - - if (!ctx.out_addr.as_u32) - return -1; - - bibe = - nat64_db_bib_entry_create (&nm->db, &ip6->src_address, - &ctx.out_addr, 0, 0, fib_index, proto, - 0); - if (!bibe) - return -1; - } - - nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index); - ste = - nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address, - &daddr.ip4, 0); - if (!ste) - return -1; - } - - nat64_session_reset_timeout (ste, ctx->vm); - - ip4->src_address.as_u32 = bibe->out_addr.as_u32; - ip4->dst_address.as_u32 = ste->out_r_addr.as_u32; - - return 0; -} - - - -static int -nat64_in2out_tcp_udp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b, - ip6_header_t * ip6) -{ - nat64_main_t *nm = &nat64_main; - nat64_db_bib_entry_t *bibe; - nat64_db_st_entry_t *ste; - ip46_address_t saddr, daddr; - u32 sw_if_index, fib_index; - udp_header_t *udp = ip6_next_header (ip6); - tcp_header_t *tcp = ip6_next_header (ip6); - u8 proto = ip6->protocol; - u16 sport = udp->src_port; - u16 dport = udp->dst_port; - u16 *checksum; - ip_csum_t csum; - - sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX]; - fib_index = - fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index); - - saddr.as_u64[0] = ip6->src_address.as_u64[0]; - saddr.as_u64[1] = ip6->src_address.as_u64[1]; - daddr.as_u64[0] = ip6->dst_address.as_u64[0]; - daddr.as_u64[1] = ip6->dst_address.as_u64[1]; - - if (proto == IP_PROTOCOL_UDP) - checksum = &udp->checksum; - else - checksum = &tcp->checksum; - - csum = ip_csum_sub_even (*checksum, ip6->src_address.as_u64[0]); - csum = ip_csum_sub_even (csum, ip6->src_address.as_u64[1]); - csum = ip_csum_sub_even (csum, ip6->dst_address.as_u64[0]); - csum = ip_csum_sub_even (csum, ip6->dst_address.as_u64[1]); - csum = ip_csum_sub_even (csum, sport); - csum = ip_csum_sub_even (csum, dport); - - ste = - nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto, - fib_index, 1); - - if (ste) - { - bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); - if (!bibe) - return -1; - } - else - { - bibe = - nat64_db_bib_entry_find (&nm->db, &saddr, sport, proto, fib_index, 1); - - if (!bibe) - { - u16 out_port; - ip4_address_t out_addr; - if (nat64_alloc_out_addr_and_port - (fib_index, ip_proto_to_snat_proto (proto), &out_addr, - &out_port)) - return -1; - - bibe = - nat64_db_bib_entry_create (&nm->db, &ip6->src_address, &out_addr, - sport, clib_host_to_net_u16 (out_port), - fib_index, proto, 0); - if (!bibe) - return -1; - } - - nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index); - ste = - nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address, - &daddr.ip4, dport); - if (!ste) - return -1; - } - - nat64_session_reset_timeout (ste, vm); - - sport = udp->src_port = bibe->out_port; - nat64_compose_ip6 (&ip6->src_address, &bibe->out_addr, fib_index); - - memset (&saddr, 0, sizeof (saddr)); - memset (&daddr, 0, sizeof (daddr)); - saddr.ip4.as_u32 = bibe->out_addr.as_u32; - daddr.ip4.as_u32 = ste->out_r_addr.as_u32; - - ste = - nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto, 0, - 0); - - if (ste) - { - bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); - if (!bibe) - return -1; - } - else - { - bibe = nat64_db_bib_entry_find (&nm->db, &daddr, dport, proto, 0, 0); - - if (!bibe) - return -1; - - ste = - nat64_db_st_entry_create (&nm->db, bibe, &ip6->src_address, - &saddr.ip4, sport); - } - - ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0]; - ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1]; - udp->dst_port = bibe->in_port; - - csum = ip_csum_add_even (csum, ip6->src_address.as_u64[0]); - csum = ip_csum_add_even (csum, ip6->src_address.as_u64[1]); - csum = ip_csum_add_even (csum, ip6->dst_address.as_u64[0]); - csum = ip_csum_add_even (csum, ip6->dst_address.as_u64[1]); - csum = ip_csum_add_even (csum, udp->src_port); - csum = ip_csum_add_even (csum, udp->dst_port); - *checksum = ip_csum_fold (csum); - - return 0; -} - -static int -nat64_in2out_icmp_hairpinning (vlib_main_t * vm, vlib_buffer_t * b, - ip6_header_t * ip6) -{ - nat64_main_t *nm = &nat64_main; - nat64_db_bib_entry_t *bibe; - nat64_db_st_entry_t *ste; - icmp46_header_t *icmp = ip6_next_header (ip6); - ip6_header_t *inner_ip6; - ip46_address_t saddr, daddr; - u32 sw_if_index, fib_index; - u8 proto; - udp_header_t *udp; - tcp_header_t *tcp; - u16 *checksum, sport, dport; - ip_csum_t csum; - - if (icmp->type == ICMP6_echo_request || icmp->type == ICMP6_echo_reply) - return -1; - - inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8); - - proto = inner_ip6->protocol; - - if (proto == IP_PROTOCOL_ICMP6) - return -1; - - sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX]; - fib_index = - fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index); - - saddr.as_u64[0] = inner_ip6->src_address.as_u64[0]; - saddr.as_u64[1] = inner_ip6->src_address.as_u64[1]; - daddr.as_u64[0] = inner_ip6->dst_address.as_u64[0]; - daddr.as_u64[1] = inner_ip6->dst_address.as_u64[1]; - - udp = ip6_next_header (inner_ip6); - tcp = ip6_next_header (inner_ip6); - - sport = udp->src_port; - dport = udp->dst_port; - - if (proto == IP_PROTOCOL_UDP) - checksum = &udp->checksum; - else - checksum = &tcp->checksum; - - csum = ip_csum_sub_even (*checksum, inner_ip6->src_address.as_u64[0]); - csum = ip_csum_sub_even (csum, inner_ip6->src_address.as_u64[1]); - csum = ip_csum_sub_even (csum, inner_ip6->dst_address.as_u64[0]); - csum = ip_csum_sub_even (csum, inner_ip6->dst_address.as_u64[1]); - csum = ip_csum_sub_even (csum, sport); - csum = ip_csum_sub_even (csum, dport); - - ste = - nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto, - fib_index, 1); - if (!ste) - return -1; - - bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); - if (!bibe) - return -1; - - dport = udp->dst_port = bibe->out_port; - nat64_compose_ip6 (&inner_ip6->dst_address, &bibe->out_addr, fib_index); - - memset (&saddr, 0, sizeof (saddr)); - memset (&daddr, 0, sizeof (daddr)); - saddr.ip4.as_u32 = ste->out_r_addr.as_u32; - daddr.ip4.as_u32 = bibe->out_addr.as_u32; - - ste = - nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto, 0, - 0); - if (!ste) - return -1; - - bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); - if (!bibe) - return -1; - - inner_ip6->src_address.as_u64[0] = bibe->in_addr.as_u64[0]; - inner_ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1]; - udp->src_port = bibe->in_port; - - csum = ip_csum_add_even (csum, inner_ip6->src_address.as_u64[0]); - csum = ip_csum_add_even (csum, inner_ip6->src_address.as_u64[1]); - csum = ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[0]); - csum = ip_csum_add_even (csum, inner_ip6->dst_address.as_u64[1]); - csum = ip_csum_add_even (csum, udp->src_port); - csum = ip_csum_add_even (csum, udp->dst_port); - *checksum = ip_csum_fold (csum); - - if (!vec_len (nm->addr_pool)) - return -1; - - nat64_compose_ip6 (&ip6->src_address, &nm->addr_pool[0].addr, fib_index); - ip6->dst_address.as_u64[0] = inner_ip6->src_address.as_u64[0]; - ip6->dst_address.as_u64[1] = inner_ip6->src_address.as_u64[1]; - - icmp->checksum = 0; - csum = ip_csum_with_carry (0, ip6->payload_length); - csum = ip_csum_with_carry (csum, clib_host_to_net_u16 (ip6->protocol)); - csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[0]); - csum = ip_csum_with_carry (csum, ip6->src_address.as_u64[1]); - csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[0]); - csum = ip_csum_with_carry (csum, ip6->dst_address.as_u64[1]); - csum = - ip_incremental_checksum (csum, icmp, - clib_net_to_host_u16 (ip6->payload_length)); - icmp->checksum = ~ip_csum_fold (csum); - - return 0; -} - -static int -nat64_in2out_unk_proto_hairpinning (vlib_main_t * vm, vlib_buffer_t * b, - ip6_header_t * ip6) -{ - nat64_main_t *nm = &nat64_main; - nat64_db_bib_entry_t *bibe; - nat64_db_st_entry_t *ste; - ip46_address_t saddr, daddr, addr; - u32 sw_if_index, fib_index; - u8 proto = ip6->protocol; - int i; - - sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_RX]; - fib_index = - fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index); - - saddr.as_u64[0] = ip6->src_address.as_u64[0]; - saddr.as_u64[1] = ip6->src_address.as_u64[1]; - daddr.as_u64[0] = ip6->dst_address.as_u64[0]; - daddr.as_u64[1] = ip6->dst_address.as_u64[1]; - - ste = - nat64_db_st_entry_find (&nm->db, &saddr, &daddr, 0, 0, proto, fib_index, - 1); - - if (ste) - { - bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); - if (!bibe) - return -1; - } - else - { - bibe = - nat64_db_bib_entry_find (&nm->db, &saddr, 0, proto, fib_index, 1); - - if (!bibe) - { - /* Choose same out address as for TCP/UDP session to same dst */ - unk_proto_st_walk_ctx_t ctx = { - .src_addr.as_u64[0] = ip6->src_address.as_u64[0], - .src_addr.as_u64[1] = ip6->src_address.as_u64[1], - .dst_addr.as_u64[0] = ip6->dst_address.as_u64[0], - .dst_addr.as_u64[1] = ip6->dst_address.as_u64[1], - .out_addr.as_u32 = 0, - .fib_index = fib_index, - .proto = proto, - }; - - nat64_db_st_walk (&nm->db, IP_PROTOCOL_TCP, unk_proto_st_walk, - &ctx); - - if (!ctx.out_addr.as_u32) - nat64_db_st_walk (&nm->db, IP_PROTOCOL_UDP, unk_proto_st_walk, - &ctx); - - /* Verify if out address is not already in use for protocol */ - memset (&addr, 0, sizeof (addr)); - addr.ip4.as_u32 = ctx.out_addr.as_u32; - if (nat64_db_bib_entry_find (&nm->db, &addr, 0, proto, 0, 0)) - ctx.out_addr.as_u32 = 0; - - if (!ctx.out_addr.as_u32) - { - for (i = 0; i < vec_len (nm->addr_pool); i++) - { - addr.ip4.as_u32 = nm->addr_pool[i].addr.as_u32; - if (!nat64_db_bib_entry_find - (&nm->db, &addr, 0, proto, 0, 0)) - break; - } - } - - if (!ctx.out_addr.as_u32) - return -1; - - bibe = - nat64_db_bib_entry_create (&nm->db, &ip6->src_address, - &ctx.out_addr, 0, 0, fib_index, proto, - 0); - if (!bibe) - return -1; - } - - nat64_extract_ip4 (&ip6->dst_address, &daddr.ip4, fib_index); - ste = - nat64_db_st_entry_create (&nm->db, bibe, &ip6->dst_address, - &daddr.ip4, 0); - if (!ste) - return -1; - } - - nat64_session_reset_timeout (ste, vm); - - nat64_compose_ip6 (&ip6->src_address, &bibe->out_addr, fib_index); - - memset (&saddr, 0, sizeof (saddr)); - memset (&daddr, 0, sizeof (daddr)); - saddr.ip4.as_u32 = bibe->out_addr.as_u32; - daddr.ip4.as_u32 = ste->out_r_addr.as_u32; - - ste = nat64_db_st_entry_find (&nm->db, &daddr, &saddr, 0, 0, proto, 0, 0); - - if (ste) - { - bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); - if (!bibe) - return -1; - } - else - { - bibe = nat64_db_bib_entry_find (&nm->db, &daddr, 0, proto, 0, 0); - - if (!bibe) - return -1; - - ste = - nat64_db_st_entry_create (&nm->db, bibe, &ip6->src_address, - &saddr.ip4, 0); - } - - ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0]; - ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1]; - - return 0; -} - -static inline uword -nat64_in2out_node_fn_inline (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * frame, u8 is_slow_path) -{ - u32 n_left_from, *from, *to_next; - nat64_in2out_next_t next_index; - u32 pkts_processed = 0; - u32 stats_node_index; - - stats_node_index = - is_slow_path ? nat64_in2out_slowpath_node.index : nat64_in2out_node.index; - - 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; - u32 next0; - ip6_header_t *ip60; - u16 l4_offset0, frag_offset0; - u8 l4_protocol0; - u32 proto0; - nat64_in2out_set_ctx_t ctx0; - - /* speculatively enqueue b0 to the current next frame */ - 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); - ip60 = vlib_buffer_get_current (b0); - - ctx0.b = b0; - ctx0.vm = vm; - - next0 = NAT64_IN2OUT_NEXT_IP4_LOOKUP; - - if (PREDICT_FALSE - (ip6_parse - (ip60, b0->current_length, &l4_protocol0, &l4_offset0, - &frag_offset0))) - { - next0 = NAT64_IN2OUT_NEXT_DROP; - b0->error = node->errors[NAT64_IN2OUT_ERROR_UNKNOWN]; - goto trace0; - } - - proto0 = ip_proto_to_snat_proto (l4_protocol0); - if (frag_offset0 != 0) - { - next0 = NAT64_IN2OUT_NEXT_DROP; - b0->error = - node->errors[NAT64_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL]; - goto trace0; - } - - if (is_slow_path) - { - if (PREDICT_TRUE (proto0 == ~0)) - { - if (is_hairpinning (&ip60->dst_address)) - { - next0 = NAT64_IN2OUT_NEXT_IP6_LOOKUP; - if (nat64_in2out_unk_proto_hairpinning (vm, b0, ip60)) - { - next0 = NAT64_IN2OUT_NEXT_DROP; - b0->error = - node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION]; - } - goto trace0; - } - - if (ip6_to_ip4 (b0, nat64_in2out_unk_proto_set_cb, &ctx0)) - { - next0 = NAT64_IN2OUT_NEXT_DROP; - b0->error = - node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION]; - goto trace0; - } - } - goto trace0; - } - else - { - if (PREDICT_FALSE (proto0 == ~0)) - { - next0 = NAT64_IN2OUT_NEXT_SLOWPATH; - goto trace0; - } - } - - if (proto0 == SNAT_PROTOCOL_ICMP) - { - if (is_hairpinning (&ip60->dst_address)) - { - next0 = NAT64_IN2OUT_NEXT_IP6_LOOKUP; - if (nat64_in2out_icmp_hairpinning (vm, b0, ip60)) - { - next0 = NAT64_IN2OUT_NEXT_DROP; - b0->error = - node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION]; - } - goto trace0; - } - - if (icmp6_to_icmp - (b0, nat64_in2out_icmp_set_cb, &ctx0, - nat64_in2out_inner_icmp_set_cb, &ctx0)) - { - next0 = NAT64_IN2OUT_NEXT_DROP; - b0->error = node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION]; - goto trace0; - } - } - else if (proto0 == SNAT_PROTOCOL_TCP || proto0 == SNAT_PROTOCOL_UDP) - { - if (is_hairpinning (&ip60->dst_address)) - { - next0 = NAT64_IN2OUT_NEXT_IP6_LOOKUP; - if (nat64_in2out_tcp_udp_hairpinning (vm, b0, ip60)) - { - next0 = NAT64_IN2OUT_NEXT_DROP; - b0->error = - node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION]; - } - goto trace0; - } - - if (ip6_to_ip4_tcp_udp - (b0, nat64_in2out_tcp_udp_set_cb, &ctx0, 0)) - { - next0 = NAT64_IN2OUT_NEXT_DROP; - b0->error = node->errors[NAT64_IN2OUT_ERROR_NO_TRANSLATION]; - goto trace0; - } - } - - trace0: - if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - nat64_in2out_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; - t->next_index = next0; - t->is_slow_path = is_slow_path; - } - - pkts_processed += next0 != NAT64_IN2OUT_NEXT_DROP; - - /* verify speculative enqueue, maybe switch current next frame */ - 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, stats_node_index, - NAT64_IN2OUT_ERROR_IN2OUT_PACKETS, - pkts_processed); - return frame->n_vectors; -} - -static uword -nat64_in2out_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - return nat64_in2out_node_fn_inline (vm, node, frame, 0); -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (nat64_in2out_node) = { - .function = nat64_in2out_node_fn, - .name = "nat64-in2out", - .vector_size = sizeof (u32), - .format_trace = format_nat64_in2out_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN (nat64_in2out_error_strings), - .error_strings = nat64_in2out_error_strings, - .n_next_nodes = NAT64_IN2OUT_N_NEXT, - /* edit / add dispositions here */ - .next_nodes = { - [NAT64_IN2OUT_NEXT_DROP] = "error-drop", - [NAT64_IN2OUT_NEXT_IP4_LOOKUP] = "ip4-lookup", - [NAT64_IN2OUT_NEXT_IP6_LOOKUP] = "ip6-lookup", - [NAT64_IN2OUT_NEXT_SLOWPATH] = "nat64-in2out-slowpath", - }, -}; -/* *INDENT-ON* */ - -VLIB_NODE_FUNCTION_MULTIARCH (nat64_in2out_node, nat64_in2out_node_fn); - -static uword -nat64_in2out_slowpath_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - return nat64_in2out_node_fn_inline (vm, node, frame, 1); -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (nat64_in2out_slowpath_node) = { - .function = nat64_in2out_slowpath_node_fn, - .name = "nat64-in2out-slowpath", - .vector_size = sizeof (u32), - .format_trace = format_nat64_in2out_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN (nat64_in2out_error_strings), - .error_strings = nat64_in2out_error_strings, - .n_next_nodes = NAT64_IN2OUT_N_NEXT, - /* edit / add dispositions here */ - .next_nodes = { - [NAT64_IN2OUT_NEXT_DROP] = "error-drop", - [NAT64_IN2OUT_NEXT_IP4_LOOKUP] = "ip4-lookup", - [NAT64_IN2OUT_NEXT_IP6_LOOKUP] = "ip6-lookup", - [NAT64_IN2OUT_NEXT_SLOWPATH] = "nat64-in2out-slowpath", - }, -}; -/* *INDENT-ON* */ - -VLIB_NODE_FUNCTION_MULTIARCH (nat64_in2out_slowpath_node, - nat64_in2out_slowpath_node_fn); - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/snat/nat64_out2in.c b/src/plugins/snat/nat64_out2in.c deleted file mode 100644 index cd5b253a..00000000 --- a/src/plugins/snat/nat64_out2in.c +++ /dev/null @@ -1,494 +0,0 @@ -/* - * 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. - */ -/** - * @file - * @brief NAT64 IPv4 to IPv6 translation (otside to inside network) - */ - -#include -#include -#include - -typedef struct -{ - u32 sw_if_index; - u32 next_index; -} nat64_out2in_trace_t; - -static u8 * -format_nat64_out2in_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 *); - nat64_out2in_trace_t *t = va_arg (*args, nat64_out2in_trace_t *); - - s = - format (s, "NAT64-out2in: sw_if_index %d, next index %d", t->sw_if_index, - t->next_index); - - return s; -} - -vlib_node_registration_t nat64_out2in_node; - -#define foreach_nat64_out2in_error \ -_(UNSUPPORTED_PROTOCOL, "Unsupported protocol") \ -_(OUT2IN_PACKETS, "Good out2in packets processed") \ -_(NO_TRANSLATION, "No translation") \ -_(UNKNOWN, "unknown") - -typedef enum -{ -#define _(sym,str) NAT64_OUT2IN_ERROR_##sym, - foreach_nat64_out2in_error -#undef _ - NAT64_OUT2IN_N_ERROR, -} nat64_out2in_error_t; - -static char *nat64_out2in_error_strings[] = { -#define _(sym,string) string, - foreach_nat64_out2in_error -#undef _ -}; - -typedef enum -{ - NAT64_OUT2IN_NEXT_LOOKUP, - NAT64_OUT2IN_NEXT_DROP, - NAT64_OUT2IN_N_NEXT, -} nat64_out2in_next_t; - -typedef struct nat64_out2in_set_ctx_t_ -{ - vlib_buffer_t *b; - vlib_main_t *vm; -} nat64_out2in_set_ctx_t; - -static int -nat64_out2in_tcp_udp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, - void *arg) -{ - nat64_main_t *nm = &nat64_main; - nat64_out2in_set_ctx_t *ctx = arg; - nat64_db_bib_entry_t *bibe; - nat64_db_st_entry_t *ste; - ip46_address_t saddr, daddr; - ip6_address_t ip6_saddr; - udp_header_t *udp = ip4_next_header (ip4); - tcp_header_t *tcp = ip4_next_header (ip4); - u8 proto = ip4->protocol; - u16 dport = udp->dst_port; - u16 sport = udp->src_port; - u32 sw_if_index, fib_index; - u16 *checksum; - ip_csum_t csum; - - sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX]; - fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index); - - memset (&saddr, 0, sizeof (saddr)); - saddr.ip4.as_u32 = ip4->src_address.as_u32; - memset (&daddr, 0, sizeof (daddr)); - daddr.ip4.as_u32 = ip4->dst_address.as_u32; - - ste = - nat64_db_st_entry_find (&nm->db, &daddr, &saddr, dport, sport, proto, - fib_index, 0); - if (ste) - { - bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); - if (!bibe) - return -1; - } - else - { - bibe = - nat64_db_bib_entry_find (&nm->db, &daddr, dport, proto, fib_index, 0); - - if (!bibe) - return -1; - - nat64_compose_ip6 (&ip6_saddr, &ip4->src_address, bibe->fib_index); - ste = - nat64_db_st_entry_create (&nm->db, bibe, &ip6_saddr, &saddr.ip4, - sport); - } - - nat64_session_reset_timeout (ste, ctx->vm); - - ip6->src_address.as_u64[0] = ste->in_r_addr.as_u64[0]; - ip6->src_address.as_u64[1] = ste->in_r_addr.as_u64[1]; - - ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0]; - ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1]; - udp->dst_port = bibe->in_port; - - if (proto == IP_PROTOCOL_UDP) - checksum = &udp->checksum; - else - checksum = &tcp->checksum; - csum = ip_csum_sub_even (*checksum, dport); - csum = ip_csum_add_even (csum, udp->dst_port); - *checksum = ip_csum_fold (csum); - - vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index; - - return 0; -} - -static int -nat64_out2in_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, void *arg) -{ - nat64_main_t *nm = &nat64_main; - nat64_out2in_set_ctx_t *ctx = arg; - nat64_db_bib_entry_t *bibe; - nat64_db_st_entry_t *ste; - ip46_address_t saddr, daddr; - ip6_address_t ip6_saddr; - u32 sw_if_index, fib_index; - icmp46_header_t *icmp = ip4_next_header (ip4); - - sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX]; - fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index); - - memset (&saddr, 0, sizeof (saddr)); - saddr.ip4.as_u32 = ip4->src_address.as_u32; - memset (&daddr, 0, sizeof (daddr)); - daddr.ip4.as_u32 = ip4->dst_address.as_u32; - - if (icmp->type == ICMP6_echo_request || icmp->type == ICMP6_echo_reply) - { - u16 out_id = ((u16 *) (icmp))[2]; - ste = - nat64_db_st_entry_find (&nm->db, &daddr, &saddr, out_id, 0, - IP_PROTOCOL_ICMP, fib_index, 0); - - if (ste) - { - bibe = - nat64_db_bib_entry_by_index (&nm->db, IP_PROTOCOL_ICMP, - ste->bibe_index); - if (!bibe) - return -1; - } - else - { - bibe = - nat64_db_bib_entry_find (&nm->db, &daddr, out_id, - IP_PROTOCOL_ICMP, fib_index, 0); - if (!bibe) - return -1; - - nat64_compose_ip6 (&ip6_saddr, &ip4->src_address, bibe->fib_index); - ste = - nat64_db_st_entry_create (&nm->db, bibe, &ip6_saddr, &saddr.ip4, - 0); - } - - nat64_session_reset_timeout (ste, ctx->vm); - - ip6->src_address.as_u64[0] = ste->in_r_addr.as_u64[0]; - ip6->src_address.as_u64[1] = ste->in_r_addr.as_u64[1]; - - ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0]; - ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1]; - ((u16 *) (icmp))[2] = bibe->in_port; - - vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index; - } - else - { - ip6_header_t *inner_ip6 = (ip6_header_t *) u8_ptr_add (icmp, 8); - - nat64_compose_ip6 (&ip6->src_address, &ip4->src_address, - vnet_buffer (ctx->b)->sw_if_index[VLIB_TX]); - ip6->dst_address.as_u64[0] = inner_ip6->src_address.as_u64[0]; - ip6->dst_address.as_u64[1] = inner_ip6->src_address.as_u64[1]; - } - - return 0; -} - -static int -nat64_out2in_inner_icmp_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, - void *arg) -{ - nat64_main_t *nm = &nat64_main; - nat64_out2in_set_ctx_t *ctx = arg; - nat64_db_bib_entry_t *bibe; - nat64_db_st_entry_t *ste; - ip46_address_t saddr, daddr; - u32 sw_if_index, fib_index; - u8 proto = ip4->protocol; - - sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX]; - fib_index = - fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6, sw_if_index); - - memset (&saddr, 0, sizeof (saddr)); - saddr.ip4.as_u32 = ip4->src_address.as_u32; - memset (&daddr, 0, sizeof (daddr)); - daddr.ip4.as_u32 = ip4->dst_address.as_u32; - - if (proto == IP_PROTOCOL_ICMP6) - { - icmp46_header_t *icmp = ip4_next_header (ip4); - u16 out_id = ((u16 *) (icmp))[2]; - proto = IP_PROTOCOL_ICMP; - - if (! - (icmp->type == ICMP6_echo_request - || icmp->type == ICMP6_echo_reply)) - return -1; - - ste = - nat64_db_st_entry_find (&nm->db, &saddr, &daddr, out_id, 0, proto, - fib_index, 0); - if (!ste) - return -1; - - bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); - if (!bibe) - return -1; - - ip6->dst_address.as_u64[0] = ste->in_r_addr.as_u64[0]; - ip6->dst_address.as_u64[1] = ste->in_r_addr.as_u64[1]; - ip6->src_address.as_u64[0] = bibe->in_addr.as_u64[0]; - ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1]; - ((u16 *) (icmp))[2] = bibe->in_port; - - vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index; - } - else - { - udp_header_t *udp = ip4_next_header (ip4); - tcp_header_t *tcp = ip4_next_header (ip4); - u16 dport = udp->dst_port; - u16 sport = udp->src_port; - u16 *checksum; - ip_csum_t csum; - - ste = - nat64_db_st_entry_find (&nm->db, &saddr, &daddr, sport, dport, proto, - fib_index, 0); - if (!ste) - return -1; - - bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); - if (!bibe) - return -1; - - nat64_compose_ip6 (&ip6->dst_address, &daddr.ip4, bibe->fib_index); - ip6->src_address.as_u64[0] = bibe->in_addr.as_u64[0]; - ip6->src_address.as_u64[1] = bibe->in_addr.as_u64[1]; - udp->src_port = bibe->in_port; - - if (proto == IP_PROTOCOL_UDP) - checksum = &udp->checksum; - else - checksum = &tcp->checksum; - if (*checksum) - { - csum = ip_csum_sub_even (*checksum, sport); - csum = ip_csum_add_even (csum, udp->src_port); - *checksum = ip_csum_fold (csum); - } - - vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index; - } - - return 0; -} - -static int -nat64_out2in_unk_proto_set_cb (ip4_header_t * ip4, ip6_header_t * ip6, - void *arg) -{ - nat64_main_t *nm = &nat64_main; - nat64_out2in_set_ctx_t *ctx = arg; - nat64_db_bib_entry_t *bibe; - nat64_db_st_entry_t *ste; - ip46_address_t saddr, daddr; - ip6_address_t ip6_saddr; - u32 sw_if_index, fib_index; - u8 proto = ip4->protocol; - - sw_if_index = vnet_buffer (ctx->b)->sw_if_index[VLIB_RX]; - fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index); - - memset (&saddr, 0, sizeof (saddr)); - saddr.ip4.as_u32 = ip4->src_address.as_u32; - memset (&daddr, 0, sizeof (daddr)); - daddr.ip4.as_u32 = ip4->dst_address.as_u32; - - ste = - nat64_db_st_entry_find (&nm->db, &daddr, &saddr, 0, 0, proto, fib_index, - 0); - if (ste) - { - bibe = nat64_db_bib_entry_by_index (&nm->db, proto, ste->bibe_index); - if (!bibe) - return -1; - } - else - { - bibe = - nat64_db_bib_entry_find (&nm->db, &daddr, 0, proto, fib_index, 0); - - if (!bibe) - return -1; - - nat64_compose_ip6 (&ip6_saddr, &ip4->src_address, bibe->fib_index); - ste = - nat64_db_st_entry_create (&nm->db, bibe, &ip6_saddr, &saddr.ip4, 0); - } - - nat64_session_reset_timeout (ste, ctx->vm); - - ip6->src_address.as_u64[0] = ste->in_r_addr.as_u64[0]; - ip6->src_address.as_u64[1] = ste->in_r_addr.as_u64[1]; - - ip6->dst_address.as_u64[0] = bibe->in_addr.as_u64[0]; - ip6->dst_address.as_u64[1] = bibe->in_addr.as_u64[1]; - - vnet_buffer (ctx->b)->sw_if_index[VLIB_TX] = bibe->fib_index; - - return 0; -} - -static uword -nat64_out2in_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - u32 n_left_from, *from, *to_next; - nat64_out2in_next_t next_index; - u32 pkts_processed = 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; - u32 next0; - ip4_header_t *ip40; - u32 proto0; - nat64_out2in_set_ctx_t ctx0; - - /* speculatively enqueue b0 to the current next frame */ - 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); - ip40 = vlib_buffer_get_current (b0); - - ctx0.b = b0; - ctx0.vm = vm; - - next0 = NAT64_OUT2IN_NEXT_LOOKUP; - - proto0 = ip_proto_to_snat_proto (ip40->protocol); - - if (proto0 == SNAT_PROTOCOL_ICMP) - { - if (icmp_to_icmp6 - (b0, nat64_out2in_icmp_set_cb, &ctx0, - nat64_out2in_inner_icmp_set_cb, &ctx0)) - { - next0 = NAT64_OUT2IN_NEXT_DROP; - b0->error = node->errors[NAT64_OUT2IN_ERROR_NO_TRANSLATION]; - goto trace0; - } - } - else if (proto0 == SNAT_PROTOCOL_TCP || proto0 == SNAT_PROTOCOL_UDP) - { - if (ip4_to_ip6_tcp_udp (b0, nat64_out2in_tcp_udp_set_cb, &ctx0)) - { - next0 = NAT64_OUT2IN_NEXT_DROP; - b0->error = node->errors[NAT64_OUT2IN_ERROR_NO_TRANSLATION]; - goto trace0; - } - } - else - { - if (ip4_to_ip6 (b0, nat64_out2in_unk_proto_set_cb, &ctx0)) - { - next0 = NAT64_OUT2IN_NEXT_DROP; - b0->error = node->errors[NAT64_OUT2IN_ERROR_NO_TRANSLATION]; - goto trace0; - } - } - - trace0: - if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - nat64_out2in_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX]; - t->next_index = next0; - } - - pkts_processed += next0 != NAT64_OUT2IN_NEXT_DROP; - - /* verify speculative enqueue, maybe switch current next frame */ - 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, nat64_out2in_node.index, - NAT64_OUT2IN_ERROR_OUT2IN_PACKETS, - pkts_processed); - return frame->n_vectors; -} - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (nat64_out2in_node) = { - .function = nat64_out2in_node_fn, - .name = "nat64-out2in", - .vector_size = sizeof (u32), - .format_trace = format_nat64_out2in_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN (nat64_out2in_error_strings), - .error_strings = nat64_out2in_error_strings,.n_next_nodes = 2, - /* edit / add dispositions here */ - .next_nodes = { - [NAT64_OUT2IN_NEXT_DROP] = "error-drop", - [NAT64_OUT2IN_NEXT_LOOKUP] = "ip6-lookup", - }, -}; -/* *INDENT-ON* */ - -VLIB_NODE_FUNCTION_MULTIARCH (nat64_out2in_node, nat64_out2in_node_fn); - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/snat/out2in.c b/src/plugins/snat/out2in.c deleted file mode 100644 index 329d67dc..00000000 --- a/src/plugins/snat/out2in.c +++ /dev/null @@ -1,2294 +0,0 @@ -/* - * 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 -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -typedef struct { - u32 sw_if_index; - u32 next_index; - u32 session_index; -} snat_out2in_trace_t; - -typedef struct { - u32 next_worker_index; - u8 do_handoff; -} snat_out2in_worker_handoff_trace_t; - -/* packet trace format function */ -static u8 * format_snat_out2in_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 *); - snat_out2in_trace_t * t = va_arg (*args, snat_out2in_trace_t *); - - s = format (s, "SNAT_OUT2IN: sw_if_index %d, next index %d, session index %d", - t->sw_if_index, t->next_index, t->session_index); - return s; -} - -static u8 * format_snat_out2in_fast_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 *); - snat_out2in_trace_t * t = va_arg (*args, snat_out2in_trace_t *); - - s = format (s, "SNAT_OUT2IN_FAST: sw_if_index %d, next index %d", - t->sw_if_index, t->next_index); - return s; -} - -static u8 * format_snat_out2in_worker_handoff_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 *); - snat_out2in_worker_handoff_trace_t * t = - va_arg (*args, snat_out2in_worker_handoff_trace_t *); - char * m; - - m = t->do_handoff ? "next worker" : "same worker"; - s = format (s, "SNAT_OUT2IN_WORKER_HANDOFF: %s %d", m, t->next_worker_index); - - return s; -} - -vlib_node_registration_t snat_out2in_node; -vlib_node_registration_t snat_out2in_fast_node; -vlib_node_registration_t snat_out2in_worker_handoff_node; -vlib_node_registration_t snat_det_out2in_node; - -#define foreach_snat_out2in_error \ -_(UNSUPPORTED_PROTOCOL, "Unsupported protocol") \ -_(OUT2IN_PACKETS, "Good out2in packets processed") \ -_(BAD_ICMP_TYPE, "unsupported ICMP type") \ -_(NO_TRANSLATION, "No translation") - -typedef enum { -#define _(sym,str) SNAT_OUT2IN_ERROR_##sym, - foreach_snat_out2in_error -#undef _ - SNAT_OUT2IN_N_ERROR, -} snat_out2in_error_t; - -static char * snat_out2in_error_strings[] = { -#define _(sym,string) string, - foreach_snat_out2in_error -#undef _ -}; - -typedef enum { - SNAT_OUT2IN_NEXT_DROP, - SNAT_OUT2IN_NEXT_LOOKUP, - SNAT_OUT2IN_NEXT_ICMP_ERROR, - SNAT_OUT2IN_N_NEXT, -} snat_out2in_next_t; - -/** - * @brief Create session for static mapping. - * - * Create NAT session initiated by host from external network with static - * mapping. - * - * @param sm SNAT main. - * @param b0 Vlib buffer. - * @param in2out In2out SNAT session key. - * @param out2in Out2in SNAT session key. - * @param node Vlib node. - * - * @returns SNAT session if successfully created otherwise 0. - */ -static inline snat_session_t * -create_session_for_static_mapping (snat_main_t *sm, - vlib_buffer_t *b0, - snat_session_key_t in2out, - snat_session_key_t out2in, - vlib_node_runtime_t * node, - u32 thread_index) -{ - snat_user_t *u; - snat_user_key_t user_key; - snat_session_t *s; - clib_bihash_kv_8_8_t kv0, value0; - dlist_elt_t * per_user_translation_list_elt; - dlist_elt_t * per_user_list_head_elt; - ip4_header_t *ip0; - - ip0 = vlib_buffer_get_current (b0); - - user_key.addr = in2out.addr; - user_key.fib_index = in2out.fib_index; - kv0.key = user_key.as_u64; - - /* Ever heard of the "user" = inside ip4 address before? */ - if (clib_bihash_search_8_8 (&sm->user_hash, &kv0, &value0)) - { - /* no, make a new one */ - pool_get (sm->per_thread_data[thread_index].users, u); - memset (u, 0, sizeof (*u)); - u->addr = in2out.addr; - u->fib_index = in2out.fib_index; - - pool_get (sm->per_thread_data[thread_index].list_pool, - per_user_list_head_elt); - - u->sessions_per_user_list_head_index = per_user_list_head_elt - - sm->per_thread_data[thread_index].list_pool; - - clib_dlist_init (sm->per_thread_data[thread_index].list_pool, - u->sessions_per_user_list_head_index); - - kv0.value = u - sm->per_thread_data[thread_index].users; - - /* add user */ - clib_bihash_add_del_8_8 (&sm->user_hash, &kv0, 1 /* is_add */); - - /* add non-traslated packets worker lookup */ - kv0.value = thread_index; - clib_bihash_add_del_8_8 (&sm->worker_by_in, &kv0, 1); - } - else - { - u = pool_elt_at_index (sm->per_thread_data[thread_index].users, - value0.value); - } - - pool_get (sm->per_thread_data[thread_index].sessions, s); - memset (s, 0, sizeof (*s)); - - s->outside_address_index = ~0; - s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; - s->ext_host_addr.as_u32 = ip0->dst_address.as_u32; - u->nstaticsessions++; - - /* Create list elts */ - pool_get (sm->per_thread_data[thread_index].list_pool, - per_user_translation_list_elt); - clib_dlist_init (sm->per_thread_data[thread_index].list_pool, - per_user_translation_list_elt - - sm->per_thread_data[thread_index].list_pool); - - per_user_translation_list_elt->value = - s - sm->per_thread_data[thread_index].sessions; - s->per_user_index = - per_user_translation_list_elt - sm->per_thread_data[thread_index].list_pool; - s->per_user_list_head_index = u->sessions_per_user_list_head_index; - - clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool, - s->per_user_list_head_index, - per_user_translation_list_elt - - sm->per_thread_data[thread_index].list_pool); - - s->in2out = in2out; - s->out2in = out2in; - s->in2out.protocol = out2in.protocol; - - /* Add to translation hashes */ - kv0.key = s->in2out.as_u64; - kv0.value = s - sm->per_thread_data[thread_index].sessions; - if (clib_bihash_add_del_8_8 (&sm->in2out, &kv0, 1 /* is_add */)) - clib_warning ("in2out key add failed"); - - kv0.key = s->out2in.as_u64; - kv0.value = s - sm->per_thread_data[thread_index].sessions; - - if (clib_bihash_add_del_8_8 (&sm->out2in, &kv0, 1 /* is_add */)) - clib_warning ("out2in key add failed"); - - /* log NAT event */ - snat_ipfix_logging_nat44_ses_create(s->in2out.addr.as_u32, - s->out2in.addr.as_u32, - s->in2out.protocol, - s->in2out.port, - s->out2in.port, - s->in2out.fib_index); - return s; -} - -static_always_inline -snat_out2in_error_t icmp_get_key(ip4_header_t *ip0, - snat_session_key_t *p_key0) -{ - icmp46_header_t *icmp0; - snat_session_key_t key0; - icmp_echo_header_t *echo0, *inner_echo0 = 0; - ip4_header_t *inner_ip0; - void *l4_header = 0; - icmp46_header_t *inner_icmp0; - - icmp0 = (icmp46_header_t *) ip4_next_header (ip0); - echo0 = (icmp_echo_header_t *)(icmp0+1); - - if (!icmp_is_error_message (icmp0)) - { - key0.protocol = SNAT_PROTOCOL_ICMP; - key0.addr = ip0->dst_address; - key0.port = echo0->identifier; - } - else - { - inner_ip0 = (ip4_header_t *)(echo0+1); - l4_header = ip4_next_header (inner_ip0); - key0.protocol = ip_proto_to_snat_proto (inner_ip0->protocol); - key0.addr = inner_ip0->src_address; - switch (key0.protocol) - { - case SNAT_PROTOCOL_ICMP: - inner_icmp0 = (icmp46_header_t*)l4_header; - inner_echo0 = (icmp_echo_header_t *)(inner_icmp0+1); - key0.port = inner_echo0->identifier; - break; - case SNAT_PROTOCOL_UDP: - case SNAT_PROTOCOL_TCP: - key0.port = ((tcp_udp_header_t*)l4_header)->src_port; - break; - default: - return SNAT_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL; - } - } - *p_key0 = key0; - return -1; /* success */ -} - -/** - * Get address and port values to be used for packet SNAT translation - * and create session if needed - * - * @param[in,out] sm SNAT main - * @param[in,out] node SNAT node runtime - * @param[in] thread_index thread index - * @param[in,out] b0 buffer containing packet to be translated - * @param[out] p_proto protocol used for matching - * @param[out] p_value address and port after NAT translation - * @param[out] p_dont_translate if packet should not be translated - * @param d optional parameter - * @param e optional parameter - */ -u32 icmp_match_out2in_slow(snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, - snat_session_key_t *p_value, - u8 *p_dont_translate, void *d, void *e) -{ - ip4_header_t *ip0; - icmp46_header_t *icmp0; - u32 sw_if_index0; - u32 rx_fib_index0; - snat_session_key_t key0; - snat_session_key_t sm0; - snat_session_t *s0 = 0; - u8 dont_translate = 0; - clib_bihash_kv_8_8_t kv0, value0; - u8 is_addr_only; - u32 next0 = ~0; - int err; - - ip0 = vlib_buffer_get_current (b0); - icmp0 = (icmp46_header_t *) ip4_next_header (ip0); - sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; - rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0); - - key0.protocol = 0; - - err = icmp_get_key (ip0, &key0); - if (err != -1) - { - b0->error = node->errors[SNAT_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL]; - next0 = SNAT_OUT2IN_NEXT_DROP; - goto out; - } - key0.fib_index = rx_fib_index0; - - kv0.key = key0.as_u64; - - if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0)) - { - /* Try to match static mapping by external address and port, - destination address and port in packet */ - if (snat_static_mapping_match(sm, key0, &sm0, 1, &is_addr_only)) - { - /* Don't NAT packet aimed at the intfc address */ - if (PREDICT_FALSE(is_interface_addr(sm, node, sw_if_index0, - ip0->dst_address.as_u32))) - { - dont_translate = 1; - goto out; - } - b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; - next0 = SNAT_OUT2IN_NEXT_DROP; - goto out; - } - - if (PREDICT_FALSE(icmp0->type != ICMP4_echo_reply && - (icmp0->type != ICMP4_echo_request || !is_addr_only))) - { - b0->error = node->errors[SNAT_OUT2IN_ERROR_BAD_ICMP_TYPE]; - next0 = SNAT_OUT2IN_NEXT_DROP; - goto out; - } - - /* Create session initiated by host from external network */ - s0 = create_session_for_static_mapping(sm, b0, sm0, key0, - node, thread_index); - - if (!s0) - { - next0 = SNAT_OUT2IN_NEXT_DROP; - goto out; - } - } - else - { - if (PREDICT_FALSE(icmp0->type != ICMP4_echo_reply && - icmp0->type != ICMP4_echo_request && - !icmp_is_error_message (icmp0))) - { - b0->error = node->errors[SNAT_OUT2IN_ERROR_BAD_ICMP_TYPE]; - next0 = SNAT_OUT2IN_NEXT_DROP; - goto out; - } - - s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, - value0.value); - } - -out: - *p_proto = key0.protocol; - if (s0) - *p_value = s0->in2out; - *p_dont_translate = dont_translate; - if (d) - *(snat_session_t**)d = s0; - return next0; -} - -/** - * Get address and port values to be used for packet SNAT translation - * - * @param[in] sm SNAT main - * @param[in,out] node SNAT node runtime - * @param[in] thread_index thread index - * @param[in,out] b0 buffer containing packet to be translated - * @param[out] p_proto protocol used for matching - * @param[out] p_value address and port after NAT translation - * @param[out] p_dont_translate if packet should not be translated - * @param d optional parameter - * @param e optional parameter - */ -u32 icmp_match_out2in_fast(snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, - snat_session_key_t *p_value, - u8 *p_dont_translate, void *d, void *e) -{ - ip4_header_t *ip0; - icmp46_header_t *icmp0; - u32 sw_if_index0; - u32 rx_fib_index0; - snat_session_key_t key0; - snat_session_key_t sm0; - u8 dont_translate = 0; - u8 is_addr_only; - u32 next0 = ~0; - int err; - - ip0 = vlib_buffer_get_current (b0); - icmp0 = (icmp46_header_t *) ip4_next_header (ip0); - sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; - rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0); - - err = icmp_get_key (ip0, &key0); - if (err != -1) - { - b0->error = node->errors[err]; - next0 = SNAT_OUT2IN_NEXT_DROP; - goto out2; - } - key0.fib_index = rx_fib_index0; - - if (snat_static_mapping_match(sm, key0, &sm0, 1, &is_addr_only)) - { - /* Don't NAT packet aimed at the intfc address */ - if (is_interface_addr(sm, node, sw_if_index0, ip0->dst_address.as_u32)) - { - dont_translate = 1; - goto out; - } - b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; - next0 = SNAT_OUT2IN_NEXT_DROP; - goto out; - } - - if (PREDICT_FALSE(icmp0->type != ICMP4_echo_reply && - (icmp0->type != ICMP4_echo_request || !is_addr_only) && - !icmp_is_error_message (icmp0))) - { - b0->error = node->errors[SNAT_OUT2IN_ERROR_BAD_ICMP_TYPE]; - next0 = SNAT_OUT2IN_NEXT_DROP; - goto out; - } - -out: - *p_value = sm0; -out2: - *p_proto = key0.protocol; - *p_dont_translate = dont_translate; - return next0; -} - -static inline u32 icmp_out2in (snat_main_t *sm, - vlib_buffer_t * b0, - ip4_header_t * ip0, - icmp46_header_t * icmp0, - u32 sw_if_index0, - u32 rx_fib_index0, - vlib_node_runtime_t * node, - u32 next0, - u32 thread_index, - void *d, - void *e) -{ - snat_session_key_t sm0; - u8 protocol; - icmp_echo_header_t *echo0, *inner_echo0 = 0; - ip4_header_t *inner_ip0 = 0; - void *l4_header = 0; - icmp46_header_t *inner_icmp0; - u8 dont_translate; - u32 new_addr0, old_addr0; - u16 old_id0, new_id0; - ip_csum_t sum0; - u16 checksum0; - u32 next0_tmp; - - echo0 = (icmp_echo_header_t *)(icmp0+1); - - next0_tmp = sm->icmp_match_out2in_cb(sm, node, thread_index, b0, - &protocol, &sm0, &dont_translate, d, e); - if (next0_tmp != ~0) - next0 = next0_tmp; - if (next0 == SNAT_OUT2IN_NEXT_DROP || dont_translate) - goto out; - - sum0 = ip_incremental_checksum (0, icmp0, - ntohs(ip0->length) - ip4_header_bytes (ip0)); - checksum0 = ~ip_csum_fold (sum0); - if (checksum0 != 0 && checksum0 != 0xffff) - { - next0 = SNAT_OUT2IN_NEXT_DROP; - goto out; - } - - old_addr0 = ip0->dst_address.as_u32; - new_addr0 = ip0->dst_address.as_u32 = sm0.addr.as_u32; - vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index; - - sum0 = ip0->checksum; - sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, - dst_address /* changed member */); - ip0->checksum = ip_csum_fold (sum0); - - if (!icmp_is_error_message (icmp0)) - { - new_id0 = sm0.port; - if (PREDICT_FALSE(new_id0 != echo0->identifier)) - { - old_id0 = echo0->identifier; - new_id0 = sm0.port; - echo0->identifier = new_id0; - - sum0 = icmp0->checksum; - sum0 = ip_csum_update (sum0, old_id0, new_id0, icmp_echo_header_t, - identifier /* changed member */); - icmp0->checksum = ip_csum_fold (sum0); - } - } - else - { - inner_ip0 = (ip4_header_t *)(echo0+1); - l4_header = ip4_next_header (inner_ip0); - - if (!ip4_header_checksum_is_valid (inner_ip0)) - { - next0 = SNAT_OUT2IN_NEXT_DROP; - goto out; - } - - old_addr0 = inner_ip0->src_address.as_u32; - inner_ip0->src_address = sm0.addr; - new_addr0 = inner_ip0->src_address.as_u32; - - sum0 = icmp0->checksum; - sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, - src_address /* changed member */); - icmp0->checksum = ip_csum_fold (sum0); - - switch (protocol) - { - case SNAT_PROTOCOL_ICMP: - inner_icmp0 = (icmp46_header_t*)l4_header; - inner_echo0 = (icmp_echo_header_t *)(inner_icmp0+1); - - old_id0 = inner_echo0->identifier; - new_id0 = sm0.port; - inner_echo0->identifier = new_id0; - - sum0 = icmp0->checksum; - sum0 = ip_csum_update (sum0, old_id0, new_id0, icmp_echo_header_t, - identifier); - icmp0->checksum = ip_csum_fold (sum0); - break; - case SNAT_PROTOCOL_UDP: - case SNAT_PROTOCOL_TCP: - old_id0 = ((tcp_udp_header_t*)l4_header)->src_port; - new_id0 = sm0.port; - ((tcp_udp_header_t*)l4_header)->src_port = new_id0; - - sum0 = icmp0->checksum; - sum0 = ip_csum_update (sum0, old_id0, new_id0, tcp_udp_header_t, - src_port); - icmp0->checksum = ip_csum_fold (sum0); - break; - default: - ASSERT(0); - } - } - -out: - return next0; -} - - -static inline u32 icmp_out2in_slow_path (snat_main_t *sm, - vlib_buffer_t * b0, - ip4_header_t * ip0, - icmp46_header_t * icmp0, - u32 sw_if_index0, - u32 rx_fib_index0, - vlib_node_runtime_t * node, - u32 next0, f64 now, - u32 thread_index, - snat_session_t ** p_s0) -{ - next0 = icmp_out2in(sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, - next0, thread_index, p_s0, 0); - snat_session_t * s0 = *p_s0; - if (PREDICT_TRUE(next0 != SNAT_OUT2IN_NEXT_DROP && s0)) - { - /* Accounting */ - s0->last_heard = now; - s0->total_pkts++; - s0->total_bytes += vlib_buffer_length_in_chain (sm->vlib_main, b0); - /* Per-user LRU list maintenance for dynamic translation */ - if (!snat_is_session_static (s0)) - { - clib_dlist_remove (sm->per_thread_data[thread_index].list_pool, - s0->per_user_index); - clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool, - s0->per_user_list_head_index, - s0->per_user_index); - } - } - return next0; -} - -static void -snat_out2in_unknown_proto (snat_main_t *sm, - vlib_buffer_t * b, - ip4_header_t * ip, - u32 rx_fib_index, - u32 thread_index, - f64 now, - vlib_main_t * vm) -{ - clib_bihash_kv_8_8_t kv, value; - clib_bihash_kv_16_8_t s_kv, s_value; - snat_static_mapping_t *m; - snat_session_key_t m_key; - u32 old_addr, new_addr; - ip_csum_t sum; - snat_unk_proto_ses_key_t key; - snat_session_t * s; - snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index]; - snat_user_key_t u_key; - snat_user_t *u; - dlist_elt_t *head, *elt; - - old_addr = ip->dst_address.as_u32; - - key.l_addr = ip->dst_address; - key.r_addr = ip->src_address; - key.fib_index = rx_fib_index; - key.proto = ip->protocol; - key.rsvd[0] = key.rsvd[1] = key.rsvd[2] = 0; - s_kv.key[0] = key.as_u64[0]; - s_kv.key[1] = key.as_u64[1]; - - if (!clib_bihash_search_16_8 (&sm->out2in_unk_proto, &s_kv, &s_value)) - { - s = pool_elt_at_index (tsm->sessions, s_value.value); - new_addr = ip->dst_address.as_u32 = s->in2out.addr.as_u32; - } - else - { - m_key.addr = ip->dst_address; - m_key.port = 0; - m_key.protocol = 0; - m_key.fib_index = rx_fib_index; - kv.key = m_key.as_u64; - if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) - return; - - m = pool_elt_at_index (sm->static_mappings, value.value); - - new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32; - - u_key.addr = ip->src_address; - u_key.fib_index = m->fib_index; - kv.key = u_key.as_u64; - - /* Ever heard of the "user" = src ip4 address before? */ - if (clib_bihash_search_8_8 (&sm->user_hash, &kv, &value)) - { - /* no, make a new one */ - pool_get (tsm->users, u); - memset (u, 0, sizeof (*u)); - u->addr = ip->src_address; - u->fib_index = rx_fib_index; - - pool_get (tsm->list_pool, head); - u->sessions_per_user_list_head_index = head - tsm->list_pool; - - clib_dlist_init (tsm->list_pool, - u->sessions_per_user_list_head_index); - - kv.value = u - tsm->users; - - /* add user */ - clib_bihash_add_del_8_8 (&sm->user_hash, &kv, 1); - } - else - { - u = pool_elt_at_index (tsm->users, value.value); - } - - /* Create a new session */ - pool_get (tsm->sessions, s); - memset (s, 0, sizeof (*s)); - - s->ext_host_addr.as_u32 = ip->src_address.as_u32; - s->flags |= SNAT_SESSION_FLAG_UNKNOWN_PROTO; - s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING; - s->outside_address_index = ~0; - s->out2in.addr.as_u32 = old_addr; - s->out2in.fib_index = rx_fib_index; - s->in2out.addr.as_u32 = new_addr; - s->in2out.fib_index = m->fib_index; - s->in2out.port = s->out2in.port = ip->protocol; - u->nstaticsessions++; - - /* Create list elts */ - pool_get (tsm->list_pool, elt); - clib_dlist_init (tsm->list_pool, elt - tsm->list_pool); - elt->value = s - tsm->sessions; - s->per_user_index = elt - tsm->list_pool; - s->per_user_list_head_index = u->sessions_per_user_list_head_index; - clib_dlist_addtail (tsm->list_pool, s->per_user_list_head_index, - s->per_user_index); - - /* Add to lookup tables */ - s_kv.value = s - tsm->sessions; - if (clib_bihash_add_del_16_8 (&sm->out2in_unk_proto, &s_kv, 1)) - clib_warning ("out2in key add failed"); - - key.l_addr = ip->dst_address; - key.fib_index = m->fib_index; - s_kv.key[0] = key.as_u64[0]; - s_kv.key[1] = key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->in2out_unk_proto, &s_kv, 1)) - clib_warning ("in2out key add failed"); - } - - /* Update IP checksum */ - sum = ip->checksum; - sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address); - ip->checksum = ip_csum_fold (sum); - - vnet_buffer(b)->sw_if_index[VLIB_TX] = s->in2out.fib_index; - - /* Accounting */ - s->last_heard = now; - s->total_pkts++; - s->total_bytes += vlib_buffer_length_in_chain (vm, b); - /* Per-user LRU list maintenance */ - clib_dlist_remove (tsm->list_pool, s->per_user_index); - clib_dlist_addtail (tsm->list_pool, s->per_user_list_head_index, - s->per_user_index); -} - -static uword -snat_out2in_node_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - u32 n_left_from, * from, * to_next; - snat_out2in_next_t next_index; - u32 pkts_processed = 0; - snat_main_t * sm = &snat_main; - f64 now = vlib_time_now (vm); - u32 thread_index = vlib_get_thread_index (); - - 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 >= 4 && n_left_to_next >= 2) - { - u32 bi0, bi1; - vlib_buffer_t * b0, * b1; - u32 next0 = SNAT_OUT2IN_NEXT_LOOKUP; - u32 next1 = SNAT_OUT2IN_NEXT_LOOKUP; - u32 sw_if_index0, sw_if_index1; - ip4_header_t * ip0, *ip1; - ip_csum_t sum0, sum1; - u32 new_addr0, old_addr0; - u16 new_port0, old_port0; - u32 new_addr1, old_addr1; - u16 new_port1, old_port1; - udp_header_t * udp0, * udp1; - tcp_header_t * tcp0, * tcp1; - icmp46_header_t * icmp0, * icmp1; - snat_session_key_t key0, key1, sm0, sm1; - u32 rx_fib_index0, rx_fib_index1; - u32 proto0, proto1; - snat_session_t * s0 = 0, * s1 = 0; - clib_bihash_kv_8_8_t kv0, kv1, value0, value1; - - /* Prefetch next iteration. */ - { - vlib_buffer_t * p2, * p3; - - p2 = vlib_get_buffer (vm, from[2]); - p3 = vlib_get_buffer (vm, from[3]); - - vlib_prefetch_buffer_header (p2, LOAD); - vlib_prefetch_buffer_header (p3, LOAD); - - CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE); - CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE); - } - - /* speculatively enqueue b0 and b1 to the current next frame */ - to_next[0] = bi0 = from[0]; - to_next[1] = bi1 = from[1]; - from += 2; - to_next += 2; - n_left_from -= 2; - n_left_to_next -= 2; - - b0 = vlib_get_buffer (vm, bi0); - b1 = vlib_get_buffer (vm, bi1); - - vnet_buffer (b0)->snat.flags = 0; - vnet_buffer (b1)->snat.flags = 0; - - ip0 = vlib_buffer_get_current (b0); - udp0 = ip4_next_header (ip0); - tcp0 = (tcp_header_t *) udp0; - icmp0 = (icmp46_header_t *) udp0; - - sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; - rx_fib_index0 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index, - sw_if_index0); - - if (PREDICT_FALSE(ip0->ttl == 1)) - { - vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; - icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, - ICMP4_time_exceeded_ttl_exceeded_in_transit, - 0); - next0 = SNAT_OUT2IN_NEXT_ICMP_ERROR; - goto trace0; - } - - proto0 = ip_proto_to_snat_proto (ip0->protocol); - - if (PREDICT_FALSE (proto0 == ~0)) - { - snat_out2in_unknown_proto(sm, b0, ip0, rx_fib_index0, - thread_index, now, vm); - goto trace0; - } - - if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP)) - { - next0 = icmp_out2in_slow_path - (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, - next0, now, thread_index, &s0); - goto trace0; - } - - key0.addr = ip0->dst_address; - key0.port = udp0->dst_port; - key0.protocol = proto0; - key0.fib_index = rx_fib_index0; - - kv0.key = key0.as_u64; - - if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0)) - { - /* Try to match static mapping by external address and port, - destination address and port in packet */ - if (snat_static_mapping_match(sm, key0, &sm0, 1, 0)) - { - b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; - /* - * Send DHCP packets to the ipv4 stack, or we won't - * be able to use dhcp client on the outside interface - */ - if (proto0 != SNAT_PROTOCOL_UDP - || (udp0->dst_port - != clib_host_to_net_u16(UDP_DST_PORT_dhcp_to_client))) - next0 = SNAT_OUT2IN_NEXT_DROP; - goto trace0; - } - - /* Create session initiated by host from external network */ - s0 = create_session_for_static_mapping(sm, b0, sm0, key0, node, - thread_index); - if (!s0) - { - b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; - next0 = SNAT_OUT2IN_NEXT_DROP; - goto trace0; - } - } - else - s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, - value0.value); - - old_addr0 = ip0->dst_address.as_u32; - ip0->dst_address = s0->in2out.addr; - new_addr0 = ip0->dst_address.as_u32; - vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index; - - sum0 = ip0->checksum; - sum0 = ip_csum_update (sum0, old_addr0, new_addr0, - ip4_header_t, - dst_address /* changed member */); - ip0->checksum = ip_csum_fold (sum0); - - if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) - { - old_port0 = tcp0->dst_port; - tcp0->dst_port = s0->in2out.port; - new_port0 = tcp0->dst_port; - - sum0 = tcp0->checksum; - sum0 = ip_csum_update (sum0, old_addr0, new_addr0, - ip4_header_t, - dst_address /* changed member */); - - sum0 = ip_csum_update (sum0, old_port0, new_port0, - ip4_header_t /* cheat */, - length /* changed member */); - tcp0->checksum = ip_csum_fold(sum0); - } - else - { - old_port0 = udp0->dst_port; - udp0->dst_port = s0->in2out.port; - udp0->checksum = 0; - } - - /* Accounting */ - s0->last_heard = now; - s0->total_pkts++; - s0->total_bytes += vlib_buffer_length_in_chain (vm, b0); - /* Per-user LRU list maintenance for dynamic translation */ - if (!snat_is_session_static (s0)) - { - clib_dlist_remove (sm->per_thread_data[thread_index].list_pool, - s0->per_user_index); - clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool, - s0->per_user_list_head_index, - s0->per_user_index); - } - trace0: - - if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - snat_out2in_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->sw_if_index = sw_if_index0; - t->next_index = next0; - t->session_index = ~0; - if (s0) - t->session_index = s0 - sm->per_thread_data[thread_index].sessions; - } - - pkts_processed += next0 != SNAT_OUT2IN_NEXT_DROP; - - - ip1 = vlib_buffer_get_current (b1); - udp1 = ip4_next_header (ip1); - tcp1 = (tcp_header_t *) udp1; - icmp1 = (icmp46_header_t *) udp1; - - sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX]; - rx_fib_index1 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index, - sw_if_index1); - - if (PREDICT_FALSE(ip1->ttl == 1)) - { - vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0; - icmp4_error_set_vnet_buffer (b1, ICMP4_time_exceeded, - ICMP4_time_exceeded_ttl_exceeded_in_transit, - 0); - next1 = SNAT_OUT2IN_NEXT_ICMP_ERROR; - goto trace1; - } - - proto1 = ip_proto_to_snat_proto (ip1->protocol); - - if (PREDICT_FALSE (proto1 == ~0)) - { - snat_out2in_unknown_proto(sm, b1, ip1, rx_fib_index1, - thread_index, now, vm); - goto trace1; - } - - if (PREDICT_FALSE (proto1 == SNAT_PROTOCOL_ICMP)) - { - next1 = icmp_out2in_slow_path - (sm, b1, ip1, icmp1, sw_if_index1, rx_fib_index1, node, - next1, now, thread_index, &s1); - goto trace1; - } - - key1.addr = ip1->dst_address; - key1.port = udp1->dst_port; - key1.protocol = proto1; - key1.fib_index = rx_fib_index1; - - kv1.key = key1.as_u64; - - if (clib_bihash_search_8_8 (&sm->out2in, &kv1, &value1)) - { - /* Try to match static mapping by external address and port, - destination address and port in packet */ - if (snat_static_mapping_match(sm, key1, &sm1, 1, 0)) - { - b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; - /* - * Send DHCP packets to the ipv4 stack, or we won't - * be able to use dhcp client on the outside interface - */ - if (proto1 != SNAT_PROTOCOL_UDP - || (udp1->dst_port - != clib_host_to_net_u16(UDP_DST_PORT_dhcp_to_client))) - next1 = SNAT_OUT2IN_NEXT_DROP; - goto trace1; - } - - /* Create session initiated by host from external network */ - s1 = create_session_for_static_mapping(sm, b1, sm1, key1, node, - thread_index); - if (!s1) - { - b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; - next1 = SNAT_OUT2IN_NEXT_DROP; - goto trace1; - } - } - else - s1 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, - value1.value); - - old_addr1 = ip1->dst_address.as_u32; - ip1->dst_address = s1->in2out.addr; - new_addr1 = ip1->dst_address.as_u32; - vnet_buffer(b1)->sw_if_index[VLIB_TX] = s1->in2out.fib_index; - - sum1 = ip1->checksum; - sum1 = ip_csum_update (sum1, old_addr1, new_addr1, - ip4_header_t, - dst_address /* changed member */); - ip1->checksum = ip_csum_fold (sum1); - - if (PREDICT_TRUE(proto1 == SNAT_PROTOCOL_TCP)) - { - old_port1 = tcp1->dst_port; - tcp1->dst_port = s1->in2out.port; - new_port1 = tcp1->dst_port; - - sum1 = tcp1->checksum; - sum1 = ip_csum_update (sum1, old_addr1, new_addr1, - ip4_header_t, - dst_address /* changed member */); - - sum1 = ip_csum_update (sum1, old_port1, new_port1, - ip4_header_t /* cheat */, - length /* changed member */); - tcp1->checksum = ip_csum_fold(sum1); - } - else - { - old_port1 = udp1->dst_port; - udp1->dst_port = s1->in2out.port; - udp1->checksum = 0; - } - - /* Accounting */ - s1->last_heard = now; - s1->total_pkts++; - s1->total_bytes += vlib_buffer_length_in_chain (vm, b1); - /* Per-user LRU list maintenance for dynamic translation */ - if (!snat_is_session_static (s1)) - { - clib_dlist_remove (sm->per_thread_data[thread_index].list_pool, - s1->per_user_index); - clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool, - s1->per_user_list_head_index, - s1->per_user_index); - } - trace1: - - if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) - && (b1->flags & VLIB_BUFFER_IS_TRACED))) - { - snat_out2in_trace_t *t = - vlib_add_trace (vm, node, b1, sizeof (*t)); - t->sw_if_index = sw_if_index1; - t->next_index = next1; - t->session_index = ~0; - if (s1) - t->session_index = s1 - sm->per_thread_data[thread_index].sessions; - } - - pkts_processed += next1 != SNAT_OUT2IN_NEXT_DROP; - - /* verify speculative enqueues, maybe switch current next frame */ - vlib_validate_buffer_enqueue_x2 (vm, node, next_index, - to_next, n_left_to_next, - bi0, bi1, next0, next1); - } - - while (n_left_from > 0 && n_left_to_next > 0) - { - u32 bi0; - vlib_buffer_t * b0; - u32 next0 = SNAT_OUT2IN_NEXT_LOOKUP; - u32 sw_if_index0; - ip4_header_t * ip0; - ip_csum_t sum0; - u32 new_addr0, old_addr0; - u16 new_port0, old_port0; - udp_header_t * udp0; - tcp_header_t * tcp0; - icmp46_header_t * icmp0; - snat_session_key_t key0, sm0; - u32 rx_fib_index0; - u32 proto0; - snat_session_t * s0 = 0; - clib_bihash_kv_8_8_t kv0, value0; - - /* speculatively enqueue b0 to the current next frame */ - 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); - - vnet_buffer (b0)->snat.flags = 0; - - ip0 = vlib_buffer_get_current (b0); - udp0 = ip4_next_header (ip0); - tcp0 = (tcp_header_t *) udp0; - icmp0 = (icmp46_header_t *) udp0; - - sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; - rx_fib_index0 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index, - sw_if_index0); - - proto0 = ip_proto_to_snat_proto (ip0->protocol); - - if (PREDICT_FALSE (proto0 == ~0)) - { - snat_out2in_unknown_proto(sm, b0, ip0, rx_fib_index0, - thread_index, now, vm); - goto trace00; - } - - if (PREDICT_FALSE(ip0->ttl == 1)) - { - vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; - icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, - ICMP4_time_exceeded_ttl_exceeded_in_transit, - 0); - next0 = SNAT_OUT2IN_NEXT_ICMP_ERROR; - goto trace00; - } - - if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP)) - { - next0 = icmp_out2in_slow_path - (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node, - next0, now, thread_index, &s0); - goto trace00; - } - - key0.addr = ip0->dst_address; - key0.port = udp0->dst_port; - key0.protocol = proto0; - key0.fib_index = rx_fib_index0; - - kv0.key = key0.as_u64; - - if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0)) - { - /* Try to match static mapping by external address and port, - destination address and port in packet */ - if (snat_static_mapping_match(sm, key0, &sm0, 1, 0)) - { - b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; - /* - * Send DHCP packets to the ipv4 stack, or we won't - * be able to use dhcp client on the outside interface - */ - if (proto0 != SNAT_PROTOCOL_UDP - || (udp0->dst_port - != clib_host_to_net_u16(UDP_DST_PORT_dhcp_to_client))) - - next0 = SNAT_OUT2IN_NEXT_DROP; - goto trace00; - } - - /* Create session initiated by host from external network */ - s0 = create_session_for_static_mapping(sm, b0, sm0, key0, node, - thread_index); - if (!s0) - { - b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; - next0 = SNAT_OUT2IN_NEXT_DROP; - goto trace00; - } - } - else - s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions, - value0.value); - - old_addr0 = ip0->dst_address.as_u32; - ip0->dst_address = s0->in2out.addr; - new_addr0 = ip0->dst_address.as_u32; - vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index; - - sum0 = ip0->checksum; - sum0 = ip_csum_update (sum0, old_addr0, new_addr0, - ip4_header_t, - dst_address /* changed member */); - ip0->checksum = ip_csum_fold (sum0); - - if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) - { - old_port0 = tcp0->dst_port; - tcp0->dst_port = s0->in2out.port; - new_port0 = tcp0->dst_port; - - sum0 = tcp0->checksum; - sum0 = ip_csum_update (sum0, old_addr0, new_addr0, - ip4_header_t, - dst_address /* changed member */); - - sum0 = ip_csum_update (sum0, old_port0, new_port0, - ip4_header_t /* cheat */, - length /* changed member */); - tcp0->checksum = ip_csum_fold(sum0); - } - else - { - old_port0 = udp0->dst_port; - udp0->dst_port = s0->in2out.port; - udp0->checksum = 0; - } - - /* Accounting */ - s0->last_heard = now; - s0->total_pkts++; - s0->total_bytes += vlib_buffer_length_in_chain (vm, b0); - /* Per-user LRU list maintenance for dynamic translation */ - if (!snat_is_session_static (s0)) - { - clib_dlist_remove (sm->per_thread_data[thread_index].list_pool, - s0->per_user_index); - clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool, - s0->per_user_list_head_index, - s0->per_user_index); - } - trace00: - - if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - snat_out2in_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->sw_if_index = sw_if_index0; - t->next_index = next0; - t->session_index = ~0; - if (s0) - t->session_index = s0 - sm->per_thread_data[thread_index].sessions; - } - - pkts_processed += next0 != SNAT_OUT2IN_NEXT_DROP; - - /* verify speculative enqueue, maybe switch current next frame */ - 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, snat_out2in_node.index, - SNAT_OUT2IN_ERROR_OUT2IN_PACKETS, - pkts_processed); - return frame->n_vectors; -} - -VLIB_REGISTER_NODE (snat_out2in_node) = { - .function = snat_out2in_node_fn, - .name = "snat-out2in", - .vector_size = sizeof (u32), - .format_trace = format_snat_out2in_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - - .n_errors = ARRAY_LEN(snat_out2in_error_strings), - .error_strings = snat_out2in_error_strings, - - .runtime_data_bytes = sizeof (snat_runtime_t), - - .n_next_nodes = SNAT_OUT2IN_N_NEXT, - - /* edit / add dispositions here */ - .next_nodes = { - [SNAT_OUT2IN_NEXT_DROP] = "error-drop", - [SNAT_OUT2IN_NEXT_LOOKUP] = "ip4-lookup", - [SNAT_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error", - }, -}; -VLIB_NODE_FUNCTION_MULTIARCH (snat_out2in_node, snat_out2in_node_fn); - -/**************************/ -/*** deterministic mode ***/ -/**************************/ -static uword -snat_det_out2in_node_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - u32 n_left_from, * from, * to_next; - snat_out2in_next_t next_index; - u32 pkts_processed = 0; - snat_main_t * sm = &snat_main; - u32 thread_index = vlib_get_thread_index (); - - 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 >= 4 && n_left_to_next >= 2) - { - u32 bi0, bi1; - vlib_buffer_t * b0, * b1; - u32 next0 = SNAT_OUT2IN_NEXT_LOOKUP; - u32 next1 = SNAT_OUT2IN_NEXT_LOOKUP; - u32 sw_if_index0, sw_if_index1; - ip4_header_t * ip0, * ip1; - ip_csum_t sum0, sum1; - ip4_address_t new_addr0, old_addr0, new_addr1, old_addr1; - u16 new_port0, old_port0, old_port1, new_port1; - udp_header_t * udp0, * udp1; - tcp_header_t * tcp0, * tcp1; - u32 proto0, proto1; - snat_det_out_key_t key0, key1; - snat_det_map_t * dm0, * dm1; - snat_det_session_t * ses0 = 0, * ses1 = 0; - u32 rx_fib_index0, rx_fib_index1; - icmp46_header_t * icmp0, * icmp1; - - /* Prefetch next iteration. */ - { - vlib_buffer_t * p2, * p3; - - p2 = vlib_get_buffer (vm, from[2]); - p3 = vlib_get_buffer (vm, from[3]); - - vlib_prefetch_buffer_header (p2, LOAD); - vlib_prefetch_buffer_header (p3, LOAD); - - CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE); - CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE); - } - - /* speculatively enqueue b0 and b1 to the current next frame */ - to_next[0] = bi0 = from[0]; - to_next[1] = bi1 = from[1]; - from += 2; - to_next += 2; - n_left_from -= 2; - n_left_to_next -= 2; - - b0 = vlib_get_buffer (vm, bi0); - b1 = vlib_get_buffer (vm, bi1); - - ip0 = vlib_buffer_get_current (b0); - udp0 = ip4_next_header (ip0); - tcp0 = (tcp_header_t *) udp0; - - sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; - - if (PREDICT_FALSE(ip0->ttl == 1)) - { - vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; - icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, - ICMP4_time_exceeded_ttl_exceeded_in_transit, - 0); - next0 = SNAT_OUT2IN_NEXT_ICMP_ERROR; - goto trace0; - } - - proto0 = ip_proto_to_snat_proto (ip0->protocol); - - if (PREDICT_FALSE(proto0 == SNAT_PROTOCOL_ICMP)) - { - rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0); - icmp0 = (icmp46_header_t *) udp0; - - next0 = icmp_out2in(sm, b0, ip0, icmp0, sw_if_index0, - rx_fib_index0, node, next0, thread_index, - &ses0, &dm0); - goto trace0; - } - - key0.ext_host_addr = ip0->src_address; - key0.ext_host_port = tcp0->src; - key0.out_port = tcp0->dst; - - dm0 = snat_det_map_by_out(sm, &ip0->dst_address); - if (PREDICT_FALSE(!dm0)) - { - clib_warning("unknown dst address: %U", - format_ip4_address, &ip0->dst_address); - next0 = SNAT_OUT2IN_NEXT_DROP; - b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; - goto trace0; - } - - snat_det_reverse(dm0, &ip0->dst_address, - clib_net_to_host_u16(tcp0->dst), &new_addr0); - - ses0 = snat_det_get_ses_by_out (dm0, &new_addr0, key0.as_u64); - if (PREDICT_FALSE(!ses0)) - { - clib_warning("no match src %U:%d dst %U:%d for user %U", - format_ip4_address, &ip0->src_address, - clib_net_to_host_u16 (tcp0->src), - format_ip4_address, &ip0->dst_address, - clib_net_to_host_u16 (tcp0->dst), - format_ip4_address, &new_addr0); - next0 = SNAT_OUT2IN_NEXT_DROP; - b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; - goto trace0; - } - new_port0 = ses0->in_port; - - old_addr0 = ip0->dst_address; - ip0->dst_address = new_addr0; - vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm->inside_fib_index; - - sum0 = ip0->checksum; - sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32, - ip4_header_t, - dst_address /* changed member */); - ip0->checksum = ip_csum_fold (sum0); - - if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) - { - if (tcp0->flags & TCP_FLAG_FIN && ses0->state == SNAT_SESSION_TCP_ESTABLISHED) - ses0->state = SNAT_SESSION_TCP_CLOSE_WAIT; - else if (tcp0->flags & TCP_FLAG_ACK && ses0->state == SNAT_SESSION_TCP_LAST_ACK) - snat_det_ses_close(dm0, ses0); - - old_port0 = tcp0->dst; - tcp0->dst = new_port0; - - sum0 = tcp0->checksum; - sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32, - ip4_header_t, - dst_address /* changed member */); - - sum0 = ip_csum_update (sum0, old_port0, new_port0, - ip4_header_t /* cheat */, - length /* changed member */); - tcp0->checksum = ip_csum_fold(sum0); - } - else - { - old_port0 = udp0->dst_port; - udp0->dst_port = new_port0; - udp0->checksum = 0; - } - - trace0: - - if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - snat_out2in_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->sw_if_index = sw_if_index0; - t->next_index = next0; - t->session_index = ~0; - if (ses0) - t->session_index = ses0 - dm0->sessions; - } - - pkts_processed += next0 != SNAT_OUT2IN_NEXT_DROP; - - b1 = vlib_get_buffer (vm, bi1); - - ip1 = vlib_buffer_get_current (b1); - udp1 = ip4_next_header (ip1); - tcp1 = (tcp_header_t *) udp1; - - sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX]; - - if (PREDICT_FALSE(ip1->ttl == 1)) - { - vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0; - icmp4_error_set_vnet_buffer (b1, ICMP4_time_exceeded, - ICMP4_time_exceeded_ttl_exceeded_in_transit, - 0); - next1 = SNAT_OUT2IN_NEXT_ICMP_ERROR; - goto trace1; - } - - proto1 = ip_proto_to_snat_proto (ip1->protocol); - - if (PREDICT_FALSE(proto1 == SNAT_PROTOCOL_ICMP)) - { - rx_fib_index1 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index1); - icmp1 = (icmp46_header_t *) udp1; - - next1 = icmp_out2in(sm, b1, ip1, icmp1, sw_if_index1, - rx_fib_index1, node, next1, thread_index, - &ses1, &dm1); - goto trace1; - } - - key1.ext_host_addr = ip1->src_address; - key1.ext_host_port = tcp1->src; - key1.out_port = tcp1->dst; - - dm1 = snat_det_map_by_out(sm, &ip1->dst_address); - if (PREDICT_FALSE(!dm1)) - { - clib_warning("unknown dst address: %U", - format_ip4_address, &ip1->dst_address); - next1 = SNAT_OUT2IN_NEXT_DROP; - b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; - goto trace1; - } - - snat_det_reverse(dm1, &ip1->dst_address, - clib_net_to_host_u16(tcp1->dst), &new_addr1); - - ses1 = snat_det_get_ses_by_out (dm1, &new_addr1, key1.as_u64); - if (PREDICT_FALSE(!ses1)) - { - clib_warning("no match src %U:%d dst %U:%d for user %U", - format_ip4_address, &ip1->src_address, - clib_net_to_host_u16 (tcp1->src), - format_ip4_address, &ip1->dst_address, - clib_net_to_host_u16 (tcp1->dst), - format_ip4_address, &new_addr1); - next1 = SNAT_OUT2IN_NEXT_DROP; - b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; - goto trace1; - } - new_port1 = ses1->in_port; - - old_addr1 = ip1->dst_address; - ip1->dst_address = new_addr1; - vnet_buffer(b1)->sw_if_index[VLIB_TX] = sm->inside_fib_index; - - sum1 = ip1->checksum; - sum1 = ip_csum_update (sum1, old_addr1.as_u32, new_addr1.as_u32, - ip4_header_t, - dst_address /* changed member */); - ip1->checksum = ip_csum_fold (sum1); - - if (PREDICT_TRUE(proto1 == SNAT_PROTOCOL_TCP)) - { - if (tcp1->flags & TCP_FLAG_FIN && ses1->state == SNAT_SESSION_TCP_ESTABLISHED) - ses1->state = SNAT_SESSION_TCP_CLOSE_WAIT; - else if (tcp1->flags & TCP_FLAG_ACK && ses1->state == SNAT_SESSION_TCP_LAST_ACK) - snat_det_ses_close(dm1, ses1); - - old_port1 = tcp1->dst; - tcp1->dst = new_port1; - - sum1 = tcp1->checksum; - sum1 = ip_csum_update (sum1, old_addr1.as_u32, new_addr1.as_u32, - ip4_header_t, - dst_address /* changed member */); - - sum1 = ip_csum_update (sum1, old_port1, new_port1, - ip4_header_t /* cheat */, - length /* changed member */); - tcp1->checksum = ip_csum_fold(sum1); - } - else - { - old_port1 = udp1->dst_port; - udp1->dst_port = new_port1; - udp1->checksum = 0; - } - - trace1: - - if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) - && (b1->flags & VLIB_BUFFER_IS_TRACED))) - { - snat_out2in_trace_t *t = - vlib_add_trace (vm, node, b1, sizeof (*t)); - t->sw_if_index = sw_if_index1; - t->next_index = next1; - t->session_index = ~0; - if (ses1) - t->session_index = ses1 - dm1->sessions; - } - - pkts_processed += next1 != SNAT_OUT2IN_NEXT_DROP; - - /* verify speculative enqueues, maybe switch current next frame */ - vlib_validate_buffer_enqueue_x2 (vm, node, next_index, - to_next, n_left_to_next, - bi0, bi1, next0, next1); - } - - while (n_left_from > 0 && n_left_to_next > 0) - { - u32 bi0; - vlib_buffer_t * b0; - u32 next0 = SNAT_OUT2IN_NEXT_LOOKUP; - u32 sw_if_index0; - ip4_header_t * ip0; - ip_csum_t sum0; - ip4_address_t new_addr0, old_addr0; - u16 new_port0, old_port0; - udp_header_t * udp0; - tcp_header_t * tcp0; - u32 proto0; - snat_det_out_key_t key0; - snat_det_map_t * dm0; - snat_det_session_t * ses0 = 0; - u32 rx_fib_index0; - icmp46_header_t * icmp0; - - /* speculatively enqueue b0 to the current next frame */ - 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); - - ip0 = vlib_buffer_get_current (b0); - udp0 = ip4_next_header (ip0); - tcp0 = (tcp_header_t *) udp0; - - sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; - - if (PREDICT_FALSE(ip0->ttl == 1)) - { - vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; - icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, - ICMP4_time_exceeded_ttl_exceeded_in_transit, - 0); - next0 = SNAT_OUT2IN_NEXT_ICMP_ERROR; - goto trace00; - } - - proto0 = ip_proto_to_snat_proto (ip0->protocol); - - if (PREDICT_FALSE(proto0 == SNAT_PROTOCOL_ICMP)) - { - rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0); - icmp0 = (icmp46_header_t *) udp0; - - next0 = icmp_out2in(sm, b0, ip0, icmp0, sw_if_index0, - rx_fib_index0, node, next0, thread_index, - &ses0, &dm0); - goto trace00; - } - - key0.ext_host_addr = ip0->src_address; - key0.ext_host_port = tcp0->src; - key0.out_port = tcp0->dst; - - dm0 = snat_det_map_by_out(sm, &ip0->dst_address); - if (PREDICT_FALSE(!dm0)) - { - clib_warning("unknown dst address: %U", - format_ip4_address, &ip0->dst_address); - next0 = SNAT_OUT2IN_NEXT_DROP; - b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; - goto trace00; - } - - snat_det_reverse(dm0, &ip0->dst_address, - clib_net_to_host_u16(tcp0->dst), &new_addr0); - - ses0 = snat_det_get_ses_by_out (dm0, &new_addr0, key0.as_u64); - if (PREDICT_FALSE(!ses0)) - { - clib_warning("no match src %U:%d dst %U:%d for user %U", - format_ip4_address, &ip0->src_address, - clib_net_to_host_u16 (tcp0->src), - format_ip4_address, &ip0->dst_address, - clib_net_to_host_u16 (tcp0->dst), - format_ip4_address, &new_addr0); - next0 = SNAT_OUT2IN_NEXT_DROP; - b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; - goto trace00; - } - new_port0 = ses0->in_port; - - old_addr0 = ip0->dst_address; - ip0->dst_address = new_addr0; - vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm->inside_fib_index; - - sum0 = ip0->checksum; - sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32, - ip4_header_t, - dst_address /* changed member */); - ip0->checksum = ip_csum_fold (sum0); - - if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) - { - if (tcp0->flags & TCP_FLAG_FIN && ses0->state == SNAT_SESSION_TCP_ESTABLISHED) - ses0->state = SNAT_SESSION_TCP_CLOSE_WAIT; - else if (tcp0->flags & TCP_FLAG_ACK && ses0->state == SNAT_SESSION_TCP_LAST_ACK) - snat_det_ses_close(dm0, ses0); - - old_port0 = tcp0->dst; - tcp0->dst = new_port0; - - sum0 = tcp0->checksum; - sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32, - ip4_header_t, - dst_address /* changed member */); - - sum0 = ip_csum_update (sum0, old_port0, new_port0, - ip4_header_t /* cheat */, - length /* changed member */); - tcp0->checksum = ip_csum_fold(sum0); - } - else - { - old_port0 = udp0->dst_port; - udp0->dst_port = new_port0; - udp0->checksum = 0; - } - - trace00: - - if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - snat_out2in_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->sw_if_index = sw_if_index0; - t->next_index = next0; - t->session_index = ~0; - if (ses0) - t->session_index = ses0 - dm0->sessions; - } - - pkts_processed += next0 != SNAT_OUT2IN_NEXT_DROP; - - /* verify speculative enqueue, maybe switch current next frame */ - 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, snat_det_out2in_node.index, - SNAT_OUT2IN_ERROR_OUT2IN_PACKETS, - pkts_processed); - return frame->n_vectors; -} - -VLIB_REGISTER_NODE (snat_det_out2in_node) = { - .function = snat_det_out2in_node_fn, - .name = "snat-det-out2in", - .vector_size = sizeof (u32), - .format_trace = format_snat_out2in_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - - .n_errors = ARRAY_LEN(snat_out2in_error_strings), - .error_strings = snat_out2in_error_strings, - - .runtime_data_bytes = sizeof (snat_runtime_t), - - .n_next_nodes = SNAT_OUT2IN_N_NEXT, - - /* edit / add dispositions here */ - .next_nodes = { - [SNAT_OUT2IN_NEXT_DROP] = "error-drop", - [SNAT_OUT2IN_NEXT_LOOKUP] = "ip4-lookup", - [SNAT_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error", - }, -}; -VLIB_NODE_FUNCTION_MULTIARCH (snat_det_out2in_node, snat_det_out2in_node_fn); - -/** - * Get address and port values to be used for packet SNAT translation - * and create session if needed - * - * @param[in,out] sm SNAT main - * @param[in,out] node SNAT node runtime - * @param[in] thread_index thread index - * @param[in,out] b0 buffer containing packet to be translated - * @param[out] p_proto protocol used for matching - * @param[out] p_value address and port after NAT translation - * @param[out] p_dont_translate if packet should not be translated - * @param d optional parameter - * @param e optional parameter - */ -u32 icmp_match_out2in_det(snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, - snat_session_key_t *p_value, - u8 *p_dont_translate, void *d, void *e) -{ - ip4_header_t *ip0; - icmp46_header_t *icmp0; - u32 sw_if_index0; - u8 protocol; - snat_det_out_key_t key0; - u8 dont_translate = 0; - u32 next0 = ~0; - icmp_echo_header_t *echo0, *inner_echo0 = 0; - ip4_header_t *inner_ip0; - void *l4_header = 0; - icmp46_header_t *inner_icmp0; - snat_det_map_t * dm0 = 0; - ip4_address_t new_addr0 = {{0}}; - snat_det_session_t * ses0 = 0; - ip4_address_t out_addr; - - ip0 = vlib_buffer_get_current (b0); - icmp0 = (icmp46_header_t *) ip4_next_header (ip0); - echo0 = (icmp_echo_header_t *)(icmp0+1); - sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; - - if (!icmp_is_error_message (icmp0)) - { - protocol = SNAT_PROTOCOL_ICMP; - key0.ext_host_addr = ip0->src_address; - key0.ext_host_port = 0; - key0.out_port = echo0->identifier; - out_addr = ip0->dst_address; - } - else - { - inner_ip0 = (ip4_header_t *)(echo0+1); - l4_header = ip4_next_header (inner_ip0); - protocol = ip_proto_to_snat_proto (inner_ip0->protocol); - key0.ext_host_addr = inner_ip0->dst_address; - out_addr = inner_ip0->src_address; - switch (protocol) - { - case SNAT_PROTOCOL_ICMP: - inner_icmp0 = (icmp46_header_t*)l4_header; - inner_echo0 = (icmp_echo_header_t *)(inner_icmp0+1); - key0.ext_host_port = 0; - key0.out_port = inner_echo0->identifier; - break; - case SNAT_PROTOCOL_UDP: - case SNAT_PROTOCOL_TCP: - key0.ext_host_port = ((tcp_udp_header_t*)l4_header)->dst_port; - key0.out_port = ((tcp_udp_header_t*)l4_header)->src_port; - break; - default: - b0->error = node->errors[SNAT_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL]; - next0 = SNAT_OUT2IN_NEXT_DROP; - goto out; - } - } - - dm0 = snat_det_map_by_out(sm, &out_addr); - if (PREDICT_FALSE(!dm0)) - { - /* Don't NAT packet aimed at the intfc address */ - if (PREDICT_FALSE(is_interface_addr(sm, node, sw_if_index0, - ip0->dst_address.as_u32))) - { - dont_translate = 1; - goto out; - } - clib_warning("unknown dst address: %U", - format_ip4_address, &ip0->dst_address); - goto out; - } - - snat_det_reverse(dm0, &ip0->dst_address, - clib_net_to_host_u16(key0.out_port), &new_addr0); - - ses0 = snat_det_get_ses_by_out (dm0, &new_addr0, key0.as_u64); - if (PREDICT_FALSE(!ses0)) - { - /* Don't NAT packet aimed at the intfc address */ - if (PREDICT_FALSE(is_interface_addr(sm, node, sw_if_index0, - ip0->dst_address.as_u32))) - { - dont_translate = 1; - goto out; - } - clib_warning("no match src %U:%d dst %U:%d for user %U", - format_ip4_address, &key0.ext_host_addr, - clib_net_to_host_u16 (key0.ext_host_port), - format_ip4_address, &out_addr, - clib_net_to_host_u16 (key0.out_port), - format_ip4_address, &new_addr0); - b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; - next0 = SNAT_OUT2IN_NEXT_DROP; - goto out; - } - - if (PREDICT_FALSE(icmp0->type != ICMP4_echo_reply && - !icmp_is_error_message (icmp0))) - { - b0->error = node->errors[SNAT_OUT2IN_ERROR_BAD_ICMP_TYPE]; - next0 = SNAT_OUT2IN_NEXT_DROP; - goto out; - } - - goto out; - -out: - *p_proto = protocol; - if (ses0) - { - p_value->addr = new_addr0; - p_value->fib_index = sm->inside_fib_index; - p_value->port = ses0->in_port; - } - *p_dont_translate = dont_translate; - if (d) - *(snat_det_session_t**)d = ses0; - if (e) - *(snat_det_map_t**)e = dm0; - return next0; -} - -/**********************/ -/*** worker handoff ***/ -/**********************/ -static uword -snat_out2in_worker_handoff_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - snat_main_t *sm = &snat_main; - vlib_thread_main_t *tm = vlib_get_thread_main (); - u32 n_left_from, *from, *to_next = 0; - static __thread vlib_frame_queue_elt_t **handoff_queue_elt_by_worker_index; - static __thread vlib_frame_queue_t **congested_handoff_queue_by_worker_index - = 0; - vlib_frame_queue_elt_t *hf = 0; - vlib_frame_t *f = 0; - int i; - u32 n_left_to_next_worker = 0, *to_next_worker = 0; - u32 next_worker_index = 0; - u32 current_worker_index = ~0; - u32 thread_index = vlib_get_thread_index (); - - ASSERT (vec_len (sm->workers)); - - if (PREDICT_FALSE (handoff_queue_elt_by_worker_index == 0)) - { - vec_validate (handoff_queue_elt_by_worker_index, tm->n_vlib_mains - 1); - - vec_validate_init_empty (congested_handoff_queue_by_worker_index, - sm->first_worker_index + sm->num_workers - 1, - (vlib_frame_queue_t *) (~0)); - } - - from = vlib_frame_vector_args (frame); - n_left_from = frame->n_vectors; - - while (n_left_from > 0) - { - u32 bi0; - vlib_buffer_t *b0; - u32 sw_if_index0; - u32 rx_fib_index0; - ip4_header_t * ip0; - u8 do_handoff; - - bi0 = from[0]; - from += 1; - n_left_from -= 1; - - b0 = vlib_get_buffer (vm, bi0); - - sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX]; - rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0); - - ip0 = vlib_buffer_get_current (b0); - - next_worker_index = sm->worker_out2in_cb(ip0, rx_fib_index0); - - if (PREDICT_FALSE (next_worker_index != thread_index)) - { - do_handoff = 1; - - if (next_worker_index != current_worker_index) - { - if (hf) - hf->n_vectors = VLIB_FRAME_SIZE - n_left_to_next_worker; - - hf = vlib_get_worker_handoff_queue_elt (sm->fq_out2in_index, - next_worker_index, - handoff_queue_elt_by_worker_index); - - n_left_to_next_worker = VLIB_FRAME_SIZE - hf->n_vectors; - to_next_worker = &hf->buffer_index[hf->n_vectors]; - current_worker_index = next_worker_index; - } - - /* enqueue to correct worker thread */ - to_next_worker[0] = bi0; - to_next_worker++; - n_left_to_next_worker--; - - if (n_left_to_next_worker == 0) - { - hf->n_vectors = VLIB_FRAME_SIZE; - vlib_put_frame_queue_elt (hf); - current_worker_index = ~0; - handoff_queue_elt_by_worker_index[next_worker_index] = 0; - hf = 0; - } - } - else - { - do_handoff = 0; - /* if this is 1st frame */ - if (!f) - { - f = vlib_get_frame_to_node (vm, sm->out2in_node_index); - to_next = vlib_frame_vector_args (f); - } - - to_next[0] = bi0; - to_next += 1; - f->n_vectors++; - } - - if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - snat_out2in_worker_handoff_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->next_worker_index = next_worker_index; - t->do_handoff = do_handoff; - } - } - - if (f) - vlib_put_frame_to_node (vm, sm->out2in_node_index, f); - - if (hf) - hf->n_vectors = VLIB_FRAME_SIZE - n_left_to_next_worker; - - /* Ship frames to the worker nodes */ - for (i = 0; i < vec_len (handoff_queue_elt_by_worker_index); i++) - { - if (handoff_queue_elt_by_worker_index[i]) - { - hf = handoff_queue_elt_by_worker_index[i]; - /* - * It works better to let the handoff node - * rate-adapt, always ship the handoff queue element. - */ - if (1 || hf->n_vectors == hf->last_n_vectors) - { - vlib_put_frame_queue_elt (hf); - handoff_queue_elt_by_worker_index[i] = 0; - } - else - hf->last_n_vectors = hf->n_vectors; - } - congested_handoff_queue_by_worker_index[i] = - (vlib_frame_queue_t *) (~0); - } - hf = 0; - current_worker_index = ~0; - return frame->n_vectors; -} - -VLIB_REGISTER_NODE (snat_out2in_worker_handoff_node) = { - .function = snat_out2in_worker_handoff_fn, - .name = "snat-out2in-worker-handoff", - .vector_size = sizeof (u32), - .format_trace = format_snat_out2in_worker_handoff_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - - .n_next_nodes = 1, - - .next_nodes = { - [0] = "error-drop", - }, -}; - -VLIB_NODE_FUNCTION_MULTIARCH (snat_out2in_worker_handoff_node, snat_out2in_worker_handoff_fn); - -static uword -snat_out2in_fast_node_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, - vlib_frame_t * frame) -{ - u32 n_left_from, * from, * to_next; - snat_out2in_next_t next_index; - u32 pkts_processed = 0; - snat_main_t * sm = &snat_main; - - 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; - u32 next0 = SNAT_OUT2IN_NEXT_DROP; - u32 sw_if_index0; - ip4_header_t * ip0; - ip_csum_t sum0; - u32 new_addr0, old_addr0; - u16 new_port0, old_port0; - udp_header_t * udp0; - tcp_header_t * tcp0; - icmp46_header_t * icmp0; - snat_session_key_t key0, sm0; - u32 proto0; - u32 rx_fib_index0; - - /* speculatively enqueue b0 to the current next frame */ - 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); - - ip0 = vlib_buffer_get_current (b0); - udp0 = ip4_next_header (ip0); - tcp0 = (tcp_header_t *) udp0; - icmp0 = (icmp46_header_t *) udp0; - - sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX]; - rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0); - - vnet_feature_next (sw_if_index0, &next0, b0); - - if (PREDICT_FALSE(ip0->ttl == 1)) - { - vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0; - icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded, - ICMP4_time_exceeded_ttl_exceeded_in_transit, - 0); - next0 = SNAT_OUT2IN_NEXT_ICMP_ERROR; - goto trace00; - } - - proto0 = ip_proto_to_snat_proto (ip0->protocol); - - if (PREDICT_FALSE (proto0 == ~0)) - goto trace00; - - if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP)) - { - next0 = icmp_out2in(sm, b0, ip0, icmp0, sw_if_index0, - rx_fib_index0, node, next0, ~0, 0, 0); - goto trace00; - } - - key0.addr = ip0->dst_address; - key0.port = udp0->dst_port; - key0.fib_index = rx_fib_index0; - - if (snat_static_mapping_match(sm, key0, &sm0, 1, 0)) - { - b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION]; - goto trace00; - } - - new_addr0 = sm0.addr.as_u32; - new_port0 = sm0.port; - vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index; - old_addr0 = ip0->dst_address.as_u32; - ip0->dst_address.as_u32 = new_addr0; - - sum0 = ip0->checksum; - sum0 = ip_csum_update (sum0, old_addr0, new_addr0, - ip4_header_t, - dst_address /* changed member */); - ip0->checksum = ip_csum_fold (sum0); - - if (PREDICT_FALSE(new_port0 != udp0->dst_port)) - { - if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) - { - old_port0 = tcp0->dst_port; - tcp0->dst_port = new_port0; - - sum0 = tcp0->checksum; - sum0 = ip_csum_update (sum0, old_addr0, new_addr0, - ip4_header_t, - dst_address /* changed member */); - - sum0 = ip_csum_update (sum0, old_port0, new_port0, - ip4_header_t /* cheat */, - length /* changed member */); - tcp0->checksum = ip_csum_fold(sum0); - } - else - { - old_port0 = udp0->dst_port; - udp0->dst_port = new_port0; - udp0->checksum = 0; - } - } - else - { - if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP)) - { - sum0 = tcp0->checksum; - sum0 = ip_csum_update (sum0, old_addr0, new_addr0, - ip4_header_t, - dst_address /* changed member */); - - tcp0->checksum = ip_csum_fold(sum0); - } - } - - trace00: - - if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE) - && (b0->flags & VLIB_BUFFER_IS_TRACED))) - { - snat_out2in_trace_t *t = - vlib_add_trace (vm, node, b0, sizeof (*t)); - t->sw_if_index = sw_if_index0; - t->next_index = next0; - } - - pkts_processed += next0 != SNAT_OUT2IN_NEXT_DROP; - - /* verify speculative enqueue, maybe switch current next frame */ - 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, snat_out2in_fast_node.index, - SNAT_OUT2IN_ERROR_OUT2IN_PACKETS, - pkts_processed); - return frame->n_vectors; -} - -VLIB_REGISTER_NODE (snat_out2in_fast_node) = { - .function = snat_out2in_fast_node_fn, - .name = "snat-out2in-fast", - .vector_size = sizeof (u32), - .format_trace = format_snat_out2in_fast_trace, - .type = VLIB_NODE_TYPE_INTERNAL, - - .n_errors = ARRAY_LEN(snat_out2in_error_strings), - .error_strings = snat_out2in_error_strings, - - .runtime_data_bytes = sizeof (snat_runtime_t), - - .n_next_nodes = SNAT_OUT2IN_N_NEXT, - - /* edit / add dispositions here */ - .next_nodes = { - [SNAT_OUT2IN_NEXT_LOOKUP] = "ip4-lookup", - [SNAT_OUT2IN_NEXT_DROP] = "error-drop", - [SNAT_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error", - }, -}; -VLIB_NODE_FUNCTION_MULTIARCH (snat_out2in_fast_node, snat_out2in_fast_node_fn); diff --git a/src/plugins/snat/snat.api b/src/plugins/snat/snat.api deleted file mode 100644 index 3c493dda..00000000 --- a/src/plugins/snat/snat.api +++ /dev/null @@ -1,897 +0,0 @@ -/* - * 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. - */ -/** - * @file snat.api - * @brief VPP control-plane API messages. - * - * This file defines VPP control-plane API messages which are generally - * called through a shared memory interface. - */ - -/** \brief Add/del S-NAT address range - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param is_ip4 - 1 if address type is IPv4 - @param first_ip_address - first IP address - @param last_ip_address - last IP address - @param vrf_id - VRF id of tenant, ~0 means independent of VRF - @param is_add - 1 if add, 0 if delete -*/ -autoreply define snat_add_address_range { - u32 client_index; - u32 context; - u8 is_ip4; - u8 first_ip_address[16]; - u8 last_ip_address[16]; - u32 vrf_id; - u8 is_add; -}; - -/** \brief Dump S-NAT addresses - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define snat_address_dump { - u32 client_index; - u32 context; -}; - -/** \brief S-NAT address details response - @param context - sender context, to match reply w/ request - @param is_ip4 - 1 if address type is IPv4 - @param ip_address - IP address - @param vrf_id - VRF id of tenant, ~0 means independent of VRF -*/ -define snat_address_details { - u32 context; - u8 is_ip4; - u8 ip_address[16]; - u32 vrf_id; -}; - -/** \brief Enable/disable S-NAT feature on the interface - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param is_add - 1 if add, 0 if delete - @param is_inside - 1 if inside, 0 if outside - @param sw_if_index - software index of the interface -*/ -autoreply define snat_interface_add_del_feature { - u32 client_index; - u32 context; - u8 is_add; - u8 is_inside; - u32 sw_if_index; -}; - -/** \brief Dump interfaces with S-NAT feature - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define snat_interface_dump { - u32 client_index; - u32 context; -}; - -/** \brief S-NAT interface details response - @param context - sender context, to match reply w/ request - @param is_inside - 1 if inside, 0 if outside - @param sw_if_index - software index of the interface -*/ -define snat_interface_details { - u32 context; - u8 is_inside; - u32 sw_if_index; -}; - -/** \brief Enable/disbale S-NAT as an interface output feature (postrouting - in2out translation) - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param is_add - 1 if add, 0 if delete - @param is_inside - 1 if inside, 0 if outside - @param sw_if_index - software index of the interface -*/ -autoreply define snat_interface_add_del_output_feature { - u32 client_index; - u32 context; - u8 is_add; - u8 is_inside; - u32 sw_if_index; -}; - -/** \brief Dump interfaces with S-NAT output feature - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define snat_interface_output_feature_dump { - u32 client_index; - u32 context; -}; - -/** \brief S-NAT interface with output feature details response - @param context - sender context, to match reply w/ request - @param is_inside - 1 if inside, 0 if outside - @param sw_if_index - software index of the interface -*/ -define snat_interface_output_feature_details { - u32 context; - u8 is_inside; - u32 sw_if_index; -}; - -/** \brief Add/delete S-NAT static mapping - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param is_add - 1 if add, 0 if delete - @param is_ip4 - 1 if address type is IPv4 - @param addr_only - 1 if address only mapping - @param local_ip_address - local IP address - @param external_ip_address - external IP address - @param protocol - IP protocol - @param local_port - local port number - @param external_port - external port number - @param external_sw_if_index - external interface (if set - external_ip_address is ignored, ~0 means not - used) - @param vfr_id - VRF ID -*/ -autoreply define snat_add_static_mapping { - u32 client_index; - u32 context; - u8 is_add; - u8 is_ip4; - u8 addr_only; - u8 local_ip_address[16]; - u8 external_ip_address[16]; - u8 protocol; - u16 local_port; - u16 external_port; - u32 external_sw_if_index; - u32 vrf_id; -}; - -/** \brief Dump S-NAT static mappings - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define snat_static_mapping_dump { - u32 client_index; - u32 context; -}; - -/** \brief S-NAT static mapping details response - @param context - sender context, to match reply w/ request - @param is_ip4 - 1 if address type is IPv4 - @param addr_only - 1 if address only mapping - @param local_ip_address - local IP address - @param external_ip_address - external IP address - @param protocol - IP protocol - @param local_port - local port number - @param external_port - external port number - @param external_sw_if_index - external interface - @param vfr_id - VRF ID -*/ -define snat_static_mapping_details { - u32 context; - u8 is_ip4; - u8 addr_only; - u8 local_ip_address[16]; - u8 external_ip_address[16]; - u8 protocol; - u16 local_port; - u16 external_port; - u32 external_sw_if_index; - u32 vrf_id; -}; - -/** \brief Control ping from client to api server request - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define snat_control_ping -{ - u32 client_index; - u32 context; -}; - -/** \brief Control ping from the client to the server response - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param retval - return code for the request - @param vpe_pid - the pid of the vpe, returned by the server -*/ -define snat_control_ping_reply -{ - u32 context; - i32 retval; - u32 client_index; - u32 vpe_pid; -}; - -/** \brief Show S-NAT plugin startup config - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define snat_show_config -{ - u32 client_index; - u32 context; -}; - -/** \brief Show S-NAT plugin startup config reply - @param context - sender context, to match reply w/ request - @param retval - return code for the request - @param static_mapping_only - if 1 dynamic translations disabled - @param static_mapping_connection_tracking - if 1 create session data - @param deterministic - if 1 deterministic mapping - @param translation_buckets - number of translation hash buckets - @param translation_memory_size - translation hash memory size - @param user_buckets - number of user hash buckets - @param user_memory_size - user hash memory size - @param max_translations_per_user - maximum number of translations per user - @param outside_vrf_id - outside VRF id - @param inside_vrf_id - default inside VRF id -*/ -define snat_show_config_reply -{ - u32 context; - i32 retval; - u8 static_mapping_only; - u8 static_mapping_connection_tracking; - u8 deterministic; - u32 translation_buckets; - u32 translation_memory_size; - u32 user_buckets; - u32 user_memory_size; - u32 max_translations_per_user; - u32 outside_vrf_id; - u32 inside_vrf_id; -}; - -/** \brief Set S-NAT workers - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param worker_mask - S-NAT workers mask -*/ -autoreply define snat_set_workers { - u32 client_index; - u32 context; - u64 worker_mask; -}; - -/** \brief Dump S-NAT workers - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define snat_worker_dump { - u32 client_index; - u32 context; -}; - -/** \brief S-NAT workers details response - @param context - sender context, to match reply w/ request - @param worker_index - worker index - @param lcore_id - lcore ID - @param name - worker name -*/ -define snat_worker_details { - u32 context; - u32 worker_index; - u32 lcore_id; - u8 name[64]; -}; - -/** \brief Add/delete S-NAT pool address from specific interfce - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param is_add - 1 if add, 0 if delete - @param sw_if_index - software index of the interface -*/ -autoreply define snat_add_del_interface_addr { - u32 client_index; - u32 context; - u8 is_add; - u8 is_inside; - u32 sw_if_index; -}; - -/** \brief Dump S-NAT pool addresses interfaces - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define snat_interface_addr_dump { - u32 client_index; - u32 context; -}; - -/** \brief S-NAT pool addresses interfaces details response - @param context - sender context, to match reply w/ request - @param sw_if_index - software index of the interface -*/ -define snat_interface_addr_details { - u32 context; - u32 sw_if_index; -}; - -/** \brief Enable/disable S-NAT IPFIX logging - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param domain_id - observation domain ID - @param src_port - source port number - @param enable - 1 if enable, 0 if disable -*/ -autoreply define snat_ipfix_enable_disable { - u32 client_index; - u32 context; - u32 domain_id; - u16 src_port; - u8 enable; -}; - -/** \brief Dump S-NAT users - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define snat_user_dump { - u32 client_index; - u32 context; -}; - -/** \brief S-NAT users response - @param context - sender context, to match reply w/ request - @vrf_id - VRF ID - @param is_ip4 - 1 if address type is IPv4 - @param ip_adress - IP address - @param nsessions - number of dynamic sessions - @param nstaticsessions - number of static sessions -*/ -define snat_user_details { - u32 context; - u32 vrf_id; - u8 is_ip4; - u8 ip_address[16]; - u32 nsessions; - u32 nstaticsessions; -}; - -/** \brief S-NAT user's sessions - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param is_ip4 - 1 if address type is IPv4 - @param user_ip - IP address of the user to dump - @param vrf_id - VRF_ID -*/ -define snat_user_session_dump { - u32 client_index; - u32 context; - u8 is_ip4; - u8 ip_address[16]; - u32 vrf_id; -}; - -/** \brief S-NAT user's sessions response - @param context - sender context, to match reply w/ request - @param is_ip4 - 1 if address type is IPv4 - @param outside_ip_address - outside IP address - @param outside_port - outside port - @param inside_ip_address - inside IP address - @param inside_port - inside port - @param protocol - protocol - @param is_static - 1 if session is static - @param last_heard - last heard timer - @param total_bytes - count of bytes sent through session - @param total_pkts - count of pakets sent through session -*/ -define snat_user_session_details { - u32 context; - u8 is_ip4; - u8 outside_ip_address[16]; - u16 outside_port; - u8 inside_ip_address[16]; - u16 inside_port; - u16 protocol; - u8 is_static; - u64 last_heard; - u64 total_bytes; - u32 total_pkts; -}; - -/** \brief Add/delete S-NAT deterministic mapping - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param is_add - 1 if add, 0 if delete - @param is_ip4 - 1 if address type is IPv4 - @param in_addr - inside IP address - @param in_plen - inside IP address prefix length - @param out_addr - outside IP address - @param out_addr - outside IP address prefix length -*/ -autoreply define snat_add_det_map { - u32 client_index; - u32 context; - u8 is_add; - u8 is_ip4; - u8 addr_only; - u8 in_addr[16]; - u8 in_plen; - u8 out_addr[16]; - u8 out_plen; -}; - -/** \brief Get outside address and port range from inside address - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param is_ip4 - 1 if address type is IPv4 - @param in_addr - inside IP address -*/ -define snat_det_forward { - u32 client_index; - u32 context; - u8 is_ip4; - u8 in_addr[16]; -}; - -/** \brief Get outside address and port range from inside address - @param context - sender context, to match reply w/ request - @param retval - return code - @param out_port_lo - outside port range start - @param out_port_hi - outside port range end - @param is_ip4 - 1 if address type is IPv4 - @param out_addr - outside IP address -*/ -define snat_det_forward_reply { - u32 context; - i32 retval; - u16 out_port_lo; - u16 out_port_hi; - u8 is_ip4; - u8 out_addr[16]; -}; - -/** \brief Get inside address from outside address and port - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param out_port - outside port - @param is_ip4 - 1 if address type is IPv4 - @param out_addr - outside IP address -*/ -define snat_det_reverse { - u32 client_index; - u32 context; - u16 out_port; - u8 is_ip4; - u8 out_addr[16]; -}; - -/** \brief Get inside address from outside address and port reply - @param context - sender context, to match reply w/ request - @param retval - return code - @param is_ip4 - 1 if address type is IPv4 - @param in_addr - inside IP address -*/ -define snat_det_reverse_reply { - u32 context; - i32 retval; - u8 is_ip4; - u8 in_addr[16]; -}; - -/** \brief Dump S-NAT deterministic mappings - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define snat_det_map_dump { - u32 client_index; - u32 context; -}; - -/** \brief S-NAT users response - @param context - sender context, to match reply w/ request - @param is_ip4 - 1 if address type is IPv4 - @param in_addr - inside IP address - @param in_plen - inside IP address prefix length - @param out_addr - outside IP address - @param out_plen - outside IP address prefix length - @param sharing_ratio - outside to inside address sharing ratio - @param ports_per_host - number of ports available to a host - @param ses_num - number of sessions belonging to this mapping -*/ -define snat_det_map_details { - u32 context; - u8 is_ip4; - u8 in_addr[16]; - u8 in_plen; - u8 out_addr[16]; - u8 out_plen; - u32 sharing_ratio; - u16 ports_per_host; - u32 ses_num; -}; - -/** \brief Set values of timeouts for deterministic NAT (seconds, 0 = default) - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param udp - UDP timeout (default 300sec) - @param tcp_established - TCP established timeout (default 7440sec) - @param tcp_transitory - TCP transitory timeout (default 240sec) - @param icmp - ICMP timeout (default 60sec) -*/ -autoreply define snat_det_set_timeouts { - u32 client_index; - u32 context; - u32 udp; - u32 tcp_established; - u32 tcp_transitory; - u32 icmp; -}; - -/** \brief Get values of timeouts for deterministic NAT (seconds) - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define snat_det_get_timeouts { - u32 client_index; - u32 context; -}; - -/** \brief Get values of timeouts for deterministic NAT reply - @param context - sender context, to match reply w/ request - @param retval - return code - @param udp - UDP timeout (default 300sec) - @param tcp_established - TCP established timeout (default 7440sec) - @param tcp_transitory - TCP transitory timeout (default 240sec) - @param icmp - ICMP timeout (default 60sec) -*/ -define snat_det_get_timeouts_reply { - u32 context; - i32 retval; - u32 udp; - u32 tcp_established; - u32 tcp_transitory; - u32 icmp; -}; - -/** \brief Close CGNAT session by outside address and port - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param is_ip4 - 1 if address type is IPv4 - @param out_addr - outside IP address - @param out_port - outside port - @param ext_addr - external host address - @param ext_port - external host port -*/ -autoreply define snat_det_close_session_out { - u32 client_index; - u32 context; - u8 is_ip4; - u8 out_addr[16]; - u16 out_port; - u8 ext_addr[16]; - u16 ext_port; -}; - -/** \brief Close CGNAT session by inside address and port - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param is_ip4 - 1 if address type is IPv4 - @param in_addr - inside IP address - @param in_port - inside port - @param ext_addr - external host address - @param ext_port - external host port -*/ -autoreply define snat_det_close_session_in { - u32 client_index; - u32 context; - u8 is_ip4; - u8 in_addr[16]; - u16 in_port; - u8 ext_addr[16]; - u16 ext_port; -}; - -/** \brief Dump S-NAT deterministic sessions - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param is_ip4 - 1 if address type is IPv4 - @param user_addr - address of an inside user whose sessions to dump -*/ -define snat_det_session_dump { - u32 client_index; - u32 context; - u8 is_ip4; - u8 user_addr[16]; -}; - -/** \brief S-NAT deterministic sessions reply - @param context - sender context, to match reply w/ request - @param is_ip4 - 1 if address type is IPv4 - @param in_port - inside port - @param ext_addr - external host address - @param ext_port - external host port - @param out_port - outside NAT port - @param state - session state - @param expire - session expiration timestamp -*/ -define snat_det_session_details { - u32 client_index; - u32 context; - u8 is_ip4; - u16 in_port; - u8 ext_addr[16]; - u16 ext_port; - u16 out_port; - u8 state; - u32 expire; -}; - -/** \brief Add/delete address range to NAT64 pool - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param start_addr - start address of the range - @param end_addr - end address of the range - @param vrf_id - VRF id of tenant, ~0 means independent of VRF - @param is_add - 1 if add, 0 if delete -*/ -autoreply define nat64_add_del_pool_addr_range { - u32 client_index; - u32 context; - u8 start_addr[4]; - u8 end_addr[4]; - u32 vrf_id; - u8 is_add; -}; - -/** \brief Dump NAT64 pool addresses - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define nat64_pool_addr_dump { - u32 client_index; - u32 context; -}; - -/** \brief NAT64 pool address details response - @param context - sender context, to match reply w/ request - @param address - IPv4 address - @param vfr_id - VRF id of tenant, ~0 means independent of VRF -*/ -define nat64_pool_addr_details { - u32 context; - u8 address[4]; - u32 vrf_id; -}; - -/** \brief Enable/disable NAT64 feature on the interface - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param sw_if_index - index of the interface - @param is_inside - 1 if inside, 0 if outside - @param is_add - 1 if add, 0 if delete -*/ -autoreply define nat64_add_del_interface { - u32 client_index; - u32 context; - u32 sw_if_index; - u8 is_inside; - u8 is_add; -}; - -/** \brief Dump interfaces with NAT64 feature - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define nat64_interface_dump { - u32 client_index; - u32 context; -}; - -/** \brief NAT64 interface details response - @param context - sender context, to match reply w/ request - @param is_inside - 1 if inside, 0 if outside - @param sw_if_index - index of the interface -*/ -define nat64_interface_details { - u32 context; - u8 is_inside; - u32 sw_if_index; -}; - -/** \brief Add/delete NAT64 static BIB entry - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param i_addr - inside IPv6 address - @param o_addr - outside IPv4 address - @param i_port - inside port number - @param o_port - outside port number - @param vrf_id - VRF id of tenant - @param proto - protocol number - @param is_add - 1 if add, 0 if delete -*/ - autoreply define nat64_add_del_static_bib { - u32 client_index; - u32 context; - u8 i_addr[16]; - u8 o_addr[4]; - u16 i_port; - u16 o_port; - u32 vrf_id; - u8 proto; - u8 is_add; -}; - -/** \brief Dump NAT64 BIB - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param proto - protocol of the BIB: 255 - all BIBs - 6 - TCP BIB - 17 - UDP BIB - 1/58 - ICMP BIB - otherwise - "unknown" protocol BIB -*/ -define nat64_bib_dump { - u32 client_index; - u32 context; - u8 proto; -}; - -/** \brief NAT64 BIB details response - @param context - sender context, to match reply w/ request - @param i_addr - inside IPv6 address - @param o_addr - outside IPv4 address - @param i_port - inside port number - @param o_port - outside port number - @param vrf_id - VRF id of tenant - @param proto - protocol number - @param is_static - 1 if static BIB entry, 0 if dynamic - @param ses_num - number of sessions associated with the BIB entry -*/ -define nat64_bib_details { - u32 context; - u8 i_addr[16]; - u8 o_addr[4]; - u16 i_port; - u16 o_port; - u32 vrf_id; - u8 proto; - u8 is_static; - u32 ses_num; -}; - -/** \brief Set values of timeouts for NAT64 (seconds, 0 = default) - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param udp - UDP timeout (default 300sec) - @param icmp - ICMP timeout (default 60sec) - @param tcp_trans - TCP transitory timeout (default 240sec) - @param tcp_est - TCP established timeout (default 7440sec) - @param tcp_incoming_syn - TCP incoming SYN timeout (default 6sec) -*/ -autoreply define nat64_set_timeouts { - u32 client_index; - u32 context; - u32 udp; - u32 icmp; - u32 tcp_trans; - u32 tcp_est; - u32 tcp_incoming_syn; -}; - -/** \brief Get values of timeouts for NAT64 (seconds) - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define nat64_get_timeouts { - u32 client_index; - u32 context; -}; - -/** \brief Get values of timeouts for NAT64 reply - @param context - sender context, to match reply w/ request - @param retval - return code - @param udp - UDP timeout - @param icmp - ICMP timeout - @param tcp_trans - TCP transitory timeout - @param tcp_est - TCP established timeout - @param tcp_incoming_syn - TCP incoming SYN timeout -*/ -define nat64_get_timeouts_reply { - u32 context; - i32 retval; - u32 udp; - u32 icmp; - u32 tcp_trans; - u32 tcp_est; - u32 tcp_incoming_syn; -}; - -/** \brief Dump NAT64 session table - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param proto - protocol of the session table: 255 - all STs - 6 - TCP ST - 17 - UDP ST - 1/58 - ICMP ST - otherwise - "unknown" proto ST -*/ -define nat64_st_dump { - u32 client_index; - u32 context; - u8 proto; -}; - -/** \brief NAT64 session table details response - @param context - sender context, to match reply w/ request - @param il_addr - inside IPv6 address of the local host - @param ol_addr - outside IPv4 address of the local host - @param il_port - inside port number id of the local host/inside ICMP id - @param ol_port - outside port number of the local host/outside ICMP id - @param il_addr - inside IPv6 address of the remote host - @param ol_addr - outside IPv4 address of the remote host - @param l_port - port number of the remote host (not used for ICMP) - @param vrf_id - VRF id of tenant - @param proto - protocol number -*/ -define nat64_st_details { - u32 context; - u8 il_addr[16]; - u8 ol_addr[4]; - u16 il_port; - u16 ol_port; - u8 ir_addr[16]; - u8 or_addr[4]; - u16 r_port; - u32 vrf_id; - u8 proto; -}; - -/** \brief Add/del NAT64 prefix - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request - @param prefix - NAT64 prefix - @param prefix - NAT64 prefix length - @param vrf_id - VRF id of tenant - @param is_add - 1 if add, 0 if delete -*/ -autoreply define nat64_add_del_prefix { - u32 client_index; - u32 context; - u8 prefix[16]; - u8 prefix_len; - u32 vrf_id; - u8 is_add; -}; - -/** \brief Dump NAT64 prefix - @param client_index - opaque cookie to identify the sender - @param context - sender context, to match reply w/ request -*/ -define nat64_prefix_dump { - u32 client_index; - u32 context; -}; - -/** \brief Dump NAT64 prefix details response - @param context - sender context, to match reply w/ request - @param prefix - NAT64 prefix - @param prefix - NAT64 prefix length - @param vrf_id - VRF id of tenant -*/ -define nat64_prefix_details { - u32 context; - u8 prefix[16]; - u8 prefix_len; - u32 vrf_id; -}; diff --git a/src/plugins/snat/snat.c b/src/plugins/snat/snat.c deleted file mode 100644 index 315cec8a..00000000 --- a/src/plugins/snat/snat.c +++ /dev/null @@ -1,2842 +0,0 @@ -/* - * snat.c - simple nat plugin - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -snat_main_t snat_main; - - -/* Hook up input features */ -VNET_FEATURE_INIT (ip4_snat_in2out, static) = { - .arc_name = "ip4-unicast", - .node_name = "snat-in2out", - .runs_before = VNET_FEATURES ("snat-out2in"), -}; -VNET_FEATURE_INIT (ip4_snat_out2in, static) = { - .arc_name = "ip4-unicast", - .node_name = "snat-out2in", - .runs_before = VNET_FEATURES ("ip4-lookup"), -}; -VNET_FEATURE_INIT (ip4_snat_det_in2out, static) = { - .arc_name = "ip4-unicast", - .node_name = "snat-det-in2out", - .runs_before = VNET_FEATURES ("snat-det-out2in"), -}; -VNET_FEATURE_INIT (ip4_snat_det_out2in, static) = { - .arc_name = "ip4-unicast", - .node_name = "snat-det-out2in", - .runs_before = VNET_FEATURES ("ip4-lookup"), -}; -VNET_FEATURE_INIT (ip4_snat_in2out_worker_handoff, static) = { - .arc_name = "ip4-unicast", - .node_name = "snat-in2out-worker-handoff", - .runs_before = VNET_FEATURES ("snat-out2in-worker-handoff"), -}; -VNET_FEATURE_INIT (ip4_snat_out2in_worker_handoff, static) = { - .arc_name = "ip4-unicast", - .node_name = "snat-out2in-worker-handoff", - .runs_before = VNET_FEATURES ("ip4-lookup"), -}; -VNET_FEATURE_INIT (ip4_snat_in2out_fast, static) = { - .arc_name = "ip4-unicast", - .node_name = "snat-in2out-fast", - .runs_before = VNET_FEATURES ("snat-out2in-fast"), -}; -VNET_FEATURE_INIT (ip4_snat_out2in_fast, static) = { - .arc_name = "ip4-unicast", - .node_name = "snat-out2in-fast", - .runs_before = VNET_FEATURES ("ip4-lookup"), -}; -VNET_FEATURE_INIT (ip4_snat_hairpin_dst, static) = { - .arc_name = "ip4-unicast", - .node_name = "snat-hairpin-dst", - .runs_before = VNET_FEATURES ("ip4-lookup"), -}; - -/* Hook up output features */ -VNET_FEATURE_INIT (ip4_snat_in2out_output, static) = { - .arc_name = "ip4-output", - .node_name = "snat-in2out-output", - .runs_before = VNET_FEATURES ("interface-output"), -}; -VNET_FEATURE_INIT (ip4_snat_in2out_output_worker_handoff, static) = { - .arc_name = "ip4-output", - .node_name = "snat-in2out-output-worker-handoff", - .runs_before = VNET_FEATURES ("interface-output"), -}; -VNET_FEATURE_INIT (ip4_snat_hairpin_src, static) = { - .arc_name = "ip4-output", - .node_name = "snat-hairpin-src", - .runs_before = VNET_FEATURES ("interface-output"), -}; - - -/* *INDENT-OFF* */ -VLIB_PLUGIN_REGISTER () = { - .version = VPP_BUILD_VER, - .description = "Network Address Translation", -}; -/* *INDENT-ON* */ - -/** - * @brief Add/del NAT address to FIB. - * - * Add the external NAT address to the FIB as receive entries. This ensures - * that VPP will reply to ARP for this address and we don't need to enable - * proxy ARP on the outside interface. - * - * @param addr IPv4 address. - * @param plen address prefix length - * @param sw_if_index Interface. - * @param is_add If 0 delete, otherwise add. - */ -void -snat_add_del_addr_to_fib (ip4_address_t * addr, u8 p_len, u32 sw_if_index, - int is_add) -{ - fib_prefix_t prefix = { - .fp_len = p_len, - .fp_proto = FIB_PROTOCOL_IP4, - .fp_addr = { - .ip4.as_u32 = addr->as_u32, - }, - }; - u32 fib_index = ip4_fib_table_get_index_for_sw_if_index(sw_if_index); - - if (is_add) - fib_table_entry_update_one_path(fib_index, - &prefix, - FIB_SOURCE_PLUGIN_HI, - (FIB_ENTRY_FLAG_CONNECTED | - FIB_ENTRY_FLAG_LOCAL | - FIB_ENTRY_FLAG_EXCLUSIVE), - DPO_PROTO_IP4, - NULL, - sw_if_index, - ~0, - 1, - NULL, - FIB_ROUTE_PATH_FLAG_NONE); - else - fib_table_entry_delete(fib_index, - &prefix, - FIB_SOURCE_PLUGIN_HI); -} - -void snat_add_address (snat_main_t *sm, ip4_address_t *addr, u32 vrf_id) -{ - snat_address_t * ap; - snat_interface_t *i; - - if (vrf_id != ~0) - sm->vrf_mode = 1; - - /* Check if address already exists */ - vec_foreach (ap, sm->addresses) - { - if (ap->addr.as_u32 == addr->as_u32) - return; - } - - vec_add2 (sm->addresses, ap, 1); - ap->addr = *addr; - if (vrf_id != ~0) - ap->fib_index = - fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, vrf_id); - else - ap->fib_index = ~0; -#define _(N, i, n, s) \ - clib_bitmap_alloc (ap->busy_##n##_port_bitmap, 65535); - foreach_snat_protocol -#undef _ - - /* Add external address to FIB */ - pool_foreach (i, sm->interfaces, - ({ - if (i->is_inside) - continue; - - snat_add_del_addr_to_fib(addr, 32, i->sw_if_index, 1); - break; - })); - pool_foreach (i, sm->output_feature_interfaces, - ({ - if (i->is_inside) - continue; - - snat_add_del_addr_to_fib(addr, 32, i->sw_if_index, 1); - break; - })); -} - -static int is_snat_address_used_in_static_mapping (snat_main_t *sm, - ip4_address_t addr) -{ - snat_static_mapping_t *m; - pool_foreach (m, sm->static_mappings, - ({ - if (m->external_addr.as_u32 == addr.as_u32) - return 1; - })); - - return 0; -} - -void increment_v4_address (ip4_address_t * a) -{ - u32 v; - - v = clib_net_to_host_u32(a->as_u32) + 1; - a->as_u32 = clib_host_to_net_u32(v); -} - -static void -snat_add_static_mapping_when_resolved (snat_main_t * sm, - ip4_address_t l_addr, - u16 l_port, - u32 sw_if_index, - u16 e_port, - u32 vrf_id, - snat_protocol_t proto, - int addr_only, - int is_add) -{ - snat_static_map_resolve_t *rp; - - vec_add2 (sm->to_resolve, rp, 1); - rp->l_addr.as_u32 = l_addr.as_u32; - rp->l_port = l_port; - rp->sw_if_index = sw_if_index; - rp->e_port = e_port; - rp->vrf_id = vrf_id; - rp->proto = proto; - rp->addr_only = addr_only; - rp->is_add = is_add; -} - -/** - * @brief Add static mapping. - * - * Create static mapping between local addr+port and external addr+port. - * - * @param l_addr Local IPv4 address. - * @param e_addr External IPv4 address. - * @param l_port Local port number. - * @param e_port External port number. - * @param vrf_id VRF ID. - * @param addr_only If 0 address port and pair mapping, otherwise address only. - * @param sw_if_index External port instead of specific IP address. - * @param is_add If 0 delete static mapping, otherwise add. - * - * @returns - */ -int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, - u16 l_port, u16 e_port, u32 vrf_id, int addr_only, - u32 sw_if_index, snat_protocol_t proto, int is_add) -{ - snat_main_t * sm = &snat_main; - snat_static_mapping_t *m; - snat_session_key_t m_key; - clib_bihash_kv_8_8_t kv, value; - snat_address_t *a = 0; - u32 fib_index = ~0; - uword * p; - snat_interface_t *interface; - int i; - - /* If the external address is a specific interface address */ - if (sw_if_index != ~0) - { - ip4_address_t * first_int_addr; - - /* Might be already set... */ - first_int_addr = ip4_interface_first_address - (sm->ip4_main, sw_if_index, 0 /* just want the address*/); - - /* DHCP resolution required? */ - if (first_int_addr == 0) - { - snat_add_static_mapping_when_resolved - (sm, l_addr, l_port, sw_if_index, e_port, vrf_id, proto, - addr_only, is_add); - return 0; - } - else - e_addr.as_u32 = first_int_addr->as_u32; - } - - m_key.addr = e_addr; - m_key.port = addr_only ? 0 : e_port; - m_key.protocol = addr_only ? 0 : proto; - m_key.fib_index = sm->outside_fib_index; - kv.key = m_key.as_u64; - if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value)) - m = 0; - else - m = pool_elt_at_index (sm->static_mappings, value.value); - - if (is_add) - { - if (m) - return VNET_API_ERROR_VALUE_EXIST; - - /* Convert VRF id to FIB index */ - if (vrf_id != ~0) - { - p = hash_get (sm->ip4_main->fib_index_by_table_id, vrf_id); - if (!p) - return VNET_API_ERROR_NO_SUCH_FIB; - fib_index = p[0]; - } - /* If not specified use inside VRF id from SNAT plugin startup config */ - else - { - fib_index = sm->inside_fib_index; - vrf_id = sm->inside_vrf_id; - } - - /* Find external address in allocated addresses and reserve port for - address and port pair mapping when dynamic translations enabled */ - if (!addr_only && !(sm->static_mapping_only)) - { - for (i = 0; i < vec_len (sm->addresses); i++) - { - if (sm->addresses[i].addr.as_u32 == e_addr.as_u32) - { - a = sm->addresses + i; - /* External port must be unused */ - switch (proto) - { -#define _(N, j, n, s) \ - case SNAT_PROTOCOL_##N: \ - if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, e_port)) \ - return VNET_API_ERROR_INVALID_VALUE; \ - clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 1); \ - if (e_port > 1024) \ - a->busy_##n##_ports++; \ - break; - foreach_snat_protocol -#undef _ - default: - clib_warning("unknown_protocol"); - return VNET_API_ERROR_INVALID_VALUE_2; - } - break; - } - } - /* External address must be allocated */ - if (!a) - return VNET_API_ERROR_NO_SUCH_ENTRY; - } - - pool_get (sm->static_mappings, m); - memset (m, 0, sizeof (*m)); - m->local_addr = l_addr; - m->external_addr = e_addr; - m->addr_only = addr_only; - m->vrf_id = vrf_id; - m->fib_index = fib_index; - if (!addr_only) - { - m->local_port = l_port; - m->external_port = e_port; - m->proto = proto; - } - - m_key.addr = m->local_addr; - m_key.port = m->local_port; - m_key.protocol = m->proto; - m_key.fib_index = m->fib_index; - kv.key = m_key.as_u64; - kv.value = m - sm->static_mappings; - clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 1); - - m_key.addr = m->external_addr; - m_key.port = m->external_port; - m_key.fib_index = sm->outside_fib_index; - kv.key = m_key.as_u64; - kv.value = m - sm->static_mappings; - clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 1); - - /* Assign worker */ - if (sm->workers) - { - snat_user_key_t w_key0; - snat_worker_key_t w_key1; - - w_key0.addr = m->local_addr; - w_key0.fib_index = m->fib_index; - kv.key = w_key0.as_u64; - - if (clib_bihash_search_8_8 (&sm->worker_by_in, &kv, &value)) - { - kv.value = sm->first_worker_index + - sm->workers[sm->next_worker++ % vec_len (sm->workers)]; - - clib_bihash_add_del_8_8 (&sm->worker_by_in, &kv, 1); - } - else - { - kv.value = value.value; - } - - w_key1.addr = m->external_addr; - w_key1.port = clib_host_to_net_u16 (m->external_port); - w_key1.fib_index = sm->outside_fib_index; - kv.key = w_key1.as_u64; - clib_bihash_add_del_8_8 (&sm->worker_by_out, &kv, 1); - } - } - else - { - if (!m) - return VNET_API_ERROR_NO_SUCH_ENTRY; - - /* Free external address port */ - if (!addr_only && !(sm->static_mapping_only)) - { - for (i = 0; i < vec_len (sm->addresses); i++) - { - if (sm->addresses[i].addr.as_u32 == e_addr.as_u32) - { - a = sm->addresses + i; - switch (proto) - { -#define _(N, j, n, s) \ - case SNAT_PROTOCOL_##N: \ - clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, e_port, 0); \ - if (e_port > 1024) \ - a->busy_##n##_ports--; \ - break; - foreach_snat_protocol -#undef _ - default: - clib_warning("unknown_protocol"); - return VNET_API_ERROR_INVALID_VALUE_2; - } - break; - } - } - } - - m_key.addr = m->local_addr; - m_key.port = m->local_port; - m_key.protocol = m->proto; - m_key.fib_index = m->fib_index; - kv.key = m_key.as_u64; - clib_bihash_add_del_8_8(&sm->static_mapping_by_local, &kv, 0); - - m_key.addr = m->external_addr; - m_key.port = m->external_port; - m_key.fib_index = sm->outside_fib_index; - kv.key = m_key.as_u64; - clib_bihash_add_del_8_8(&sm->static_mapping_by_external, &kv, 0); - - /* Delete session(s) for static mapping if exist */ - if (!(sm->static_mapping_only) || - (sm->static_mapping_only && sm->static_mapping_connection_tracking)) - { - snat_user_key_t u_key; - snat_user_t *u; - dlist_elt_t * head, * elt; - u32 elt_index, head_index, del_elt_index; - u32 ses_index; - u64 user_index; - snat_session_t * s; - snat_main_per_thread_data_t *tsm; - - u_key.addr = m->local_addr; - u_key.fib_index = m->fib_index; - kv.key = u_key.as_u64; - if (!clib_bihash_search_8_8 (&sm->user_hash, &kv, &value)) - { - user_index = value.value; - if (!clib_bihash_search_8_8 (&sm->worker_by_in, &kv, &value)) - tsm = vec_elt_at_index (sm->per_thread_data, value.value); - else - tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); - u = pool_elt_at_index (tsm->users, user_index); - if (u->nstaticsessions) - { - head_index = u->sessions_per_user_list_head_index; - head = pool_elt_at_index (tsm->list_pool, head_index); - elt_index = head->next; - elt = pool_elt_at_index (tsm->list_pool, elt_index); - ses_index = elt->value; - while (ses_index != ~0) - { - s = pool_elt_at_index (tsm->sessions, ses_index); - del_elt_index = elt_index; - elt_index = elt->next; - elt = pool_elt_at_index (tsm->list_pool, elt_index); - ses_index = elt->value; - - if (!addr_only) - { - if ((s->out2in.addr.as_u32 != e_addr.as_u32) && - (clib_net_to_host_u16 (s->out2in.port) != e_port)) - continue; - } - - if (snat_is_unk_proto_session (s)) - { - clib_bihash_kv_16_8_t up_kv; - snat_unk_proto_ses_key_t up_key; - up_key.l_addr = s->in2out.addr; - up_key.r_addr = s->ext_host_addr; - up_key.fib_index = s->in2out.fib_index; - up_key.proto = s->in2out.port; - up_key.rsvd[0] = up_key.rsvd[1] = up_key.rsvd[2] = 0; - up_kv.key[0] = up_key.as_u64[0]; - up_kv.key[1] = up_key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->in2out_unk_proto, - &up_kv, 0)) - clib_warning ("in2out key del failed"); - - up_key.l_addr = s->out2in.addr; - up_key.fib_index = s->out2in.fib_index; - up_kv.key[0] = up_key.as_u64[0]; - up_kv.key[1] = up_key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->out2in_unk_proto, - &up_kv, 0)) - clib_warning ("out2in key del failed"); - - goto delete; - } - /* log NAT event */ - snat_ipfix_logging_nat44_ses_delete(s->in2out.addr.as_u32, - s->out2in.addr.as_u32, - s->in2out.protocol, - s->in2out.port, - s->out2in.port, - s->in2out.fib_index); - - value.key = s->in2out.as_u64; - if (clib_bihash_add_del_8_8 (&sm->in2out, &value, 0)) - clib_warning ("in2out key del failed"); - value.key = s->out2in.as_u64; - if (clib_bihash_add_del_8_8 (&sm->out2in, &value, 0)) - clib_warning ("out2in key del failed"); -delete: - pool_put (tsm->sessions, s); - - clib_dlist_remove (tsm->list_pool, del_elt_index); - pool_put_index (tsm->list_pool, del_elt_index); - u->nstaticsessions--; - - if (!addr_only) - break; - } - if (addr_only) - { - pool_put (tsm->users, u); - clib_bihash_add_del_8_8 (&sm->user_hash, &kv, 0); - } - } - } - } - - /* Delete static mapping from pool */ - pool_put (sm->static_mappings, m); - } - - if (!addr_only) - return 0; - - /* Add/delete external address to FIB */ - pool_foreach (interface, sm->interfaces, - ({ - if (interface->is_inside) - continue; - - snat_add_del_addr_to_fib(&e_addr, 32, interface->sw_if_index, is_add); - break; - })); - pool_foreach (interface, sm->output_feature_interfaces, - ({ - if (interface->is_inside) - continue; - - snat_add_del_addr_to_fib(&e_addr, 32, interface->sw_if_index, is_add); - break; - })); - - return 0; -} - -int snat_del_address (snat_main_t *sm, ip4_address_t addr, u8 delete_sm) -{ - snat_address_t *a = 0; - snat_session_t *ses; - u32 *ses_to_be_removed = 0, *ses_index; - clib_bihash_kv_8_8_t kv, value; - snat_user_key_t user_key; - snat_user_t *u; - snat_main_per_thread_data_t *tsm; - snat_static_mapping_t *m; - snat_interface_t *interface; - int i; - - /* Find SNAT address */ - for (i=0; i < vec_len (sm->addresses); i++) - { - if (sm->addresses[i].addr.as_u32 == addr.as_u32) - { - a = sm->addresses + i; - break; - } - } - if (!a) - return VNET_API_ERROR_NO_SUCH_ENTRY; - - if (delete_sm) - { - pool_foreach (m, sm->static_mappings, - ({ - if (m->external_addr.as_u32 == addr.as_u32) - (void) snat_add_static_mapping (m->local_addr, m->external_addr, - m->local_port, m->external_port, - m->vrf_id, m->addr_only, ~0, - m->proto, 0); - })); - } - else - { - /* Check if address is used in some static mapping */ - if (is_snat_address_used_in_static_mapping(sm, addr)) - { - clib_warning ("address used in static mapping"); - return VNET_API_ERROR_UNSPECIFIED; - } - } - - if (a->fib_index != ~0) - fib_table_unlock(a->fib_index, FIB_PROTOCOL_IP4); - - /* Delete sessions using address */ - if (a->busy_tcp_ports || a->busy_udp_ports || a->busy_icmp_ports) - { - vec_foreach (tsm, sm->per_thread_data) - { - pool_foreach (ses, tsm->sessions, ({ - if (ses->out2in.addr.as_u32 == addr.as_u32) - { - if (snat_is_unk_proto_session (ses)) - { - clib_bihash_kv_16_8_t up_kv; - snat_unk_proto_ses_key_t up_key; - up_key.l_addr = ses->in2out.addr; - up_key.r_addr = ses->ext_host_addr; - up_key.fib_index = ses->in2out.fib_index; - up_key.proto = ses->in2out.port; - up_key.rsvd[0] = up_key.rsvd[1] = up_key.rsvd[2] = 0; - up_kv.key[0] = up_key.as_u64[0]; - up_kv.key[1] = up_key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->in2out_unk_proto, - &up_kv, 0)) - clib_warning ("in2out key del failed"); - - up_key.l_addr = ses->out2in.addr; - up_key.fib_index = ses->out2in.fib_index; - up_kv.key[0] = up_key.as_u64[0]; - up_kv.key[1] = up_key.as_u64[1]; - if (clib_bihash_add_del_16_8 (&sm->out2in_unk_proto, - &up_kv, 0)) - clib_warning ("out2in key del failed"); - } - else - { - /* log NAT event */ - snat_ipfix_logging_nat44_ses_delete(ses->in2out.addr.as_u32, - ses->out2in.addr.as_u32, - ses->in2out.protocol, - ses->in2out.port, - ses->out2in.port, - ses->in2out.fib_index); - kv.key = ses->in2out.as_u64; - clib_bihash_add_del_8_8 (&sm->in2out, &kv, 0); - kv.key = ses->out2in.as_u64; - clib_bihash_add_del_8_8 (&sm->out2in, &kv, 0); - } - vec_add1 (ses_to_be_removed, ses - tsm->sessions); - clib_dlist_remove (tsm->list_pool, ses->per_user_index); - user_key.addr = ses->in2out.addr; - user_key.fib_index = ses->in2out.fib_index; - kv.key = user_key.as_u64; - if (!clib_bihash_search_8_8 (&sm->user_hash, &kv, &value)) - { - u = pool_elt_at_index (tsm->users, value.value); - u->nsessions--; - } - } - })); - - vec_foreach (ses_index, ses_to_be_removed) - pool_put_index (tsm->sessions, ses_index[0]); - - vec_free (ses_to_be_removed); - } - } - - vec_del1 (sm->addresses, i); - - /* Delete external address from FIB */ - pool_foreach (interface, sm->interfaces, - ({ - if (interface->is_inside) - continue; - - snat_add_del_addr_to_fib(&addr, 32, interface->sw_if_index, 0); - break; - })); - pool_foreach (interface, sm->output_feature_interfaces, - ({ - if (interface->is_inside) - continue; - - snat_add_del_addr_to_fib(&addr, 32, interface->sw_if_index, 0); - break; - })); - - return 0; -} - -int snat_interface_add_del (u32 sw_if_index, u8 is_inside, int is_del) -{ - snat_main_t *sm = &snat_main; - snat_interface_t *i; - const char * feature_name; - snat_address_t * ap; - snat_static_mapping_t * m; - snat_det_map_t * dm; - - if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking)) - feature_name = is_inside ? "snat-in2out-fast" : "snat-out2in-fast"; - else - { - if (sm->num_workers > 1 && !sm->deterministic) - feature_name = is_inside ? "snat-in2out-worker-handoff" : "snat-out2in-worker-handoff"; - else if (sm->deterministic) - feature_name = is_inside ? "snat-det-in2out" : "snat-det-out2in"; - else - feature_name = is_inside ? "snat-in2out" : "snat-out2in"; - } - - vnet_feature_enable_disable ("ip4-unicast", feature_name, sw_if_index, - !is_del, 0, 0); - - if (sm->fq_in2out_index == ~0 && !sm->deterministic && sm->num_workers > 1) - sm->fq_in2out_index = vlib_frame_queue_main_init (sm->in2out_node_index, 0); - - if (sm->fq_out2in_index == ~0 && !sm->deterministic && sm->num_workers > 1) - sm->fq_out2in_index = vlib_frame_queue_main_init (sm->out2in_node_index, 0); - - pool_foreach (i, sm->interfaces, - ({ - if (i->sw_if_index == sw_if_index) - { - if (is_del) - pool_put (sm->interfaces, i); - else - return VNET_API_ERROR_VALUE_EXIST; - - goto fib; - } - })); - - if (is_del) - return VNET_API_ERROR_NO_SUCH_ENTRY; - - pool_get (sm->interfaces, i); - i->sw_if_index = sw_if_index; - i->is_inside = is_inside; - - /* Add/delete external addresses to FIB */ -fib: - if (is_inside) - return 0; - - vec_foreach (ap, sm->addresses) - snat_add_del_addr_to_fib(&ap->addr, 32, sw_if_index, !is_del); - - pool_foreach (m, sm->static_mappings, - ({ - if (!(m->addr_only)) - continue; - - snat_add_del_addr_to_fib(&m->external_addr, 32, sw_if_index, !is_del); - })); - - pool_foreach (dm, sm->det_maps, - ({ - snat_add_del_addr_to_fib(&dm->out_addr, dm->out_plen, sw_if_index, !is_del); - })); - - return 0; -} - -int snat_interface_add_del_output_feature (u32 sw_if_index, - u8 is_inside, - int is_del) -{ - snat_main_t *sm = &snat_main; - snat_interface_t *i; - snat_address_t * ap; - snat_static_mapping_t * m; - - if (sm->deterministic || - (sm->static_mapping_only && !(sm->static_mapping_connection_tracking))) - return VNET_API_ERROR_UNSUPPORTED; - - if (is_inside) - { - vnet_feature_enable_disable ("ip4-unicast", "snat-hairpin-dst", - sw_if_index, !is_del, 0, 0); - vnet_feature_enable_disable ("ip4-output", "snat-hairpin-src", - sw_if_index, !is_del, 0, 0); - goto fq; - } - - if (sm->num_workers > 1) - { - vnet_feature_enable_disable ("ip4-unicast", "snat-out2in-worker-handoff", - sw_if_index, !is_del, 0, 0); - vnet_feature_enable_disable ("ip4-output", - "snat-in2out-output-worker-handoff", - sw_if_index, !is_del, 0, 0); - } - else - { - vnet_feature_enable_disable ("ip4-unicast", "snat-out2in", sw_if_index, - !is_del, 0, 0); - vnet_feature_enable_disable ("ip4-output", "snat-in2out-output", - sw_if_index, !is_del, 0, 0); - } - -fq: - if (sm->fq_in2out_output_index == ~0 && sm->num_workers > 1) - sm->fq_in2out_output_index = - vlib_frame_queue_main_init (sm->in2out_output_node_index, 0); - - if (sm->fq_out2in_index == ~0 && sm->num_workers > 1) - sm->fq_out2in_index = vlib_frame_queue_main_init (sm->out2in_node_index, 0); - - pool_foreach (i, sm->output_feature_interfaces, - ({ - if (i->sw_if_index == sw_if_index) - { - if (is_del) - pool_put (sm->output_feature_interfaces, i); - else - return VNET_API_ERROR_VALUE_EXIST; - - goto fib; - } - })); - - if (is_del) - return VNET_API_ERROR_NO_SUCH_ENTRY; - - pool_get (sm->output_feature_interfaces, i); - i->sw_if_index = sw_if_index; - i->is_inside = is_inside; - - /* Add/delete external addresses to FIB */ -fib: - if (is_inside) - return 0; - - vec_foreach (ap, sm->addresses) - snat_add_del_addr_to_fib(&ap->addr, 32, sw_if_index, !is_del); - - pool_foreach (m, sm->static_mappings, - ({ - if (!(m->addr_only)) - continue; - - snat_add_del_addr_to_fib(&m->external_addr, 32, sw_if_index, !is_del); - })); - - return 0; -} - -int snat_set_workers (uword * bitmap) -{ - snat_main_t *sm = &snat_main; - int i, j = 0; - - if (sm->num_workers < 2) - return VNET_API_ERROR_FEATURE_DISABLED; - - if (clib_bitmap_last_set (bitmap) >= sm->num_workers) - return VNET_API_ERROR_INVALID_WORKER; - - vec_free (sm->workers); - clib_bitmap_foreach (i, bitmap, - ({ - vec_add1(sm->workers, i); - sm->per_thread_data[i].snat_thread_index = j; - j++; - })); - - sm->port_per_thread = (0xffff - 1024) / _vec_len (sm->workers); - sm->num_snat_thread = _vec_len (sm->workers); - - return 0; -} - - -static void -snat_ip4_add_del_interface_address_cb (ip4_main_t * im, - uword opaque, - u32 sw_if_index, - ip4_address_t * address, - u32 address_length, - u32 if_address_index, - u32 is_delete); - -static clib_error_t * snat_init (vlib_main_t * vm) -{ - snat_main_t * sm = &snat_main; - clib_error_t * error = 0; - ip4_main_t * im = &ip4_main; - ip_lookup_main_t * lm = &im->lookup_main; - uword *p; - vlib_thread_registration_t *tr; - vlib_thread_main_t *tm = vlib_get_thread_main (); - uword *bitmap = 0; - u32 i; - ip4_add_del_interface_address_callback_t cb4; - - sm->vlib_main = vm; - sm->vnet_main = vnet_get_main(); - sm->ip4_main = im; - sm->ip4_lookup_main = lm; - sm->api_main = &api_main; - sm->first_worker_index = 0; - sm->next_worker = 0; - sm->num_workers = 0; - sm->num_snat_thread = 1; - sm->workers = 0; - sm->port_per_thread = 0xffff - 1024; - sm->fq_in2out_index = ~0; - sm->fq_out2in_index = ~0; - sm->udp_timeout = SNAT_UDP_TIMEOUT; - sm->tcp_established_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT; - sm->tcp_transitory_timeout = SNAT_TCP_TRANSITORY_TIMEOUT; - sm->icmp_timeout = SNAT_ICMP_TIMEOUT; - - p = hash_get_mem (tm->thread_registrations_by_name, "workers"); - if (p) - { - tr = (vlib_thread_registration_t *) p[0]; - if (tr) - { - sm->num_workers = tr->count; - sm->first_worker_index = tr->first_index; - } - } - - vec_validate (sm->per_thread_data, tm->n_vlib_mains - 1); - - /* Use all available workers by default */ - if (sm->num_workers > 1) - { - for (i=0; i < sm->num_workers; i++) - bitmap = clib_bitmap_set (bitmap, i, 1); - snat_set_workers(bitmap); - clib_bitmap_free (bitmap); - } - else - { - sm->per_thread_data[0].snat_thread_index = 0; - } - - error = snat_api_init(vm, sm); - if (error) - return error; - - /* Set up the interface address add/del callback */ - cb4.function = snat_ip4_add_del_interface_address_cb; - cb4.function_opaque = 0; - - vec_add1 (im->add_del_interface_address_callbacks, cb4); - - /* Init IPFIX logging */ - snat_ipfix_logging_init(vm); - - error = nat64_init(vm); - - return error; -} - -VLIB_INIT_FUNCTION (snat_init); - -void snat_free_outside_address_and_port (snat_main_t * sm, - snat_session_key_t * k, - u32 address_index) -{ - snat_address_t *a; - u16 port_host_byte_order = clib_net_to_host_u16 (k->port); - - ASSERT (address_index < vec_len (sm->addresses)); - - a = sm->addresses + address_index; - - switch (k->protocol) - { -#define _(N, i, n, s) \ - case SNAT_PROTOCOL_##N: \ - ASSERT (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, \ - port_host_byte_order) == 1); \ - clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, \ - port_host_byte_order, 0); \ - a->busy_##n##_ports--; \ - break; - foreach_snat_protocol -#undef _ - default: - clib_warning("unknown_protocol"); - return; - } -} - -/** - * @brief Match SNAT static mapping. - * - * @param sm SNAT main. - * @param match Address and port to match. - * @param mapping External or local address and port of the matched mapping. - * @param by_external If 0 match by local address otherwise match by external - * address. - * @param is_addr_only If matched mapping is address only - * - * @returns 0 if match found otherwise 1. - */ -int snat_static_mapping_match (snat_main_t * sm, - snat_session_key_t match, - snat_session_key_t * mapping, - u8 by_external, - u8 *is_addr_only) -{ - clib_bihash_kv_8_8_t kv, value; - snat_static_mapping_t *m; - snat_session_key_t m_key; - clib_bihash_8_8_t *mapping_hash = &sm->static_mapping_by_local; - - if (by_external) - mapping_hash = &sm->static_mapping_by_external; - - m_key.addr = match.addr; - m_key.port = clib_net_to_host_u16 (match.port); - m_key.protocol = match.protocol; - m_key.fib_index = match.fib_index; - - kv.key = m_key.as_u64; - - if (clib_bihash_search_8_8 (mapping_hash, &kv, &value)) - { - /* Try address only mapping */ - m_key.port = 0; - m_key.protocol = 0; - kv.key = m_key.as_u64; - if (clib_bihash_search_8_8 (mapping_hash, &kv, &value)) - return 1; - } - - m = pool_elt_at_index (sm->static_mappings, value.value); - - if (by_external) - { - mapping->addr = m->local_addr; - /* Address only mapping doesn't change port */ - mapping->port = m->addr_only ? match.port - : clib_host_to_net_u16 (m->local_port); - mapping->fib_index = m->fib_index; - } - else - { - mapping->addr = m->external_addr; - /* Address only mapping doesn't change port */ - mapping->port = m->addr_only ? match.port - : clib_host_to_net_u16 (m->external_port); - mapping->fib_index = sm->outside_fib_index; - } - - if (PREDICT_FALSE(is_addr_only != 0)) - *is_addr_only = m->addr_only; - - return 0; -} - -static_always_inline u16 -snat_random_port (snat_main_t * sm, u16 min, u16 max) -{ - return min + random_u32 (&sm->random_seed) / - (random_u32_max() / (max - min + 1) + 1); -} - -int snat_alloc_outside_address_and_port (snat_main_t * sm, - u32 fib_index, - u32 thread_index, - snat_session_key_t * k, - u32 * address_indexp) -{ - int i; - snat_address_t *a; - u32 portnum; - - for (i = 0; i < vec_len (sm->addresses); i++) - { - a = sm->addresses + i; - if (sm->vrf_mode && a->fib_index != ~0 && a->fib_index != fib_index) - continue; - switch (k->protocol) - { -#define _(N, j, n, s) \ - case SNAT_PROTOCOL_##N: \ - if (a->busy_##n##_ports < (sm->port_per_thread * sm->num_snat_thread)) \ - { \ - while (1) \ - { \ - portnum = (sm->port_per_thread * \ - sm->per_thread_data[thread_index].snat_thread_index) + \ - snat_random_port(sm, 0, sm->port_per_thread) + 1024; \ - if (clib_bitmap_get_no_check (a->busy_##n##_port_bitmap, portnum)) \ - continue; \ - clib_bitmap_set_no_check (a->busy_##n##_port_bitmap, portnum, 1); \ - a->busy_##n##_ports++; \ - k->addr = a->addr; \ - k->port = clib_host_to_net_u16(portnum); \ - *address_indexp = i; \ - return 0; \ - } \ - } \ - break; - foreach_snat_protocol -#undef _ - default: - clib_warning("unknown protocol"); - return 1; - } - - } - /* Totally out of translations to use... */ - snat_ipfix_logging_addresses_exhausted(0); - return 1; -} - - -static clib_error_t * -add_address_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - snat_main_t * sm = &snat_main; - ip4_address_t start_addr, end_addr, this_addr; - u32 start_host_order, end_host_order; - u32 vrf_id = ~0; - int i, count; - int is_add = 1; - int rv = 0; - clib_error_t *error = 0; - - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (line_input, "%U - %U", - unformat_ip4_address, &start_addr, - unformat_ip4_address, &end_addr)) - ; - else if (unformat (line_input, "tenant-vrf %u", &vrf_id)) - ; - else if (unformat (line_input, "%U", unformat_ip4_address, &start_addr)) - end_addr = start_addr; - else if (unformat (line_input, "del")) - is_add = 0; - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - - if (sm->static_mapping_only) - { - error = clib_error_return (0, "static mapping only mode"); - goto done; - } - - 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) - { - error = clib_error_return (0, "end address less than start address"); - goto done; - } - - count = (end_host_order - start_host_order) + 1; - - if (count > 1024) - clib_warning ("%U - %U, %d addresses...", - format_ip4_address, &start_addr, - format_ip4_address, &end_addr, - count); - - this_addr = start_addr; - - for (i = 0; i < count; i++) - { - if (is_add) - snat_add_address (sm, &this_addr, vrf_id); - else - rv = snat_del_address (sm, this_addr, 0); - - switch (rv) - { - case VNET_API_ERROR_NO_SUCH_ENTRY: - error = clib_error_return (0, "S-NAT address not exist."); - goto done; - case VNET_API_ERROR_UNSPECIFIED: - error = clib_error_return (0, "S-NAT address used in static mapping."); - goto done; - default: - break; - } - - increment_v4_address (&this_addr); - } - -done: - unformat_free (line_input); - - return error; -} - -VLIB_CLI_COMMAND (add_address_command, static) = { - .path = "snat add address", - .short_help = "snat add addresses [- ] " - "[tenant-vrf ] [del]", - .function = add_address_command_fn, -}; - -static clib_error_t * -snat_feature_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - vnet_main_t * vnm = vnet_get_main(); - clib_error_t * error = 0; - u32 sw_if_index; - u32 * inside_sw_if_indices = 0; - u32 * outside_sw_if_indices = 0; - u8 is_output_feature = 0; - int is_del = 0; - int i; - - sw_if_index = ~0; - - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (line_input, "in %U", unformat_vnet_sw_interface, - vnm, &sw_if_index)) - vec_add1 (inside_sw_if_indices, sw_if_index); - else if (unformat (line_input, "out %U", unformat_vnet_sw_interface, - vnm, &sw_if_index)) - vec_add1 (outside_sw_if_indices, sw_if_index); - else if (unformat (line_input, "output-feature")) - is_output_feature = 1; - else if (unformat (line_input, "del")) - is_del = 1; - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - - if (vec_len (inside_sw_if_indices)) - { - for (i = 0; i < vec_len(inside_sw_if_indices); i++) - { - sw_if_index = inside_sw_if_indices[i]; - if (is_output_feature) - { - if (snat_interface_add_del_output_feature (sw_if_index, 1, is_del)) - { - error = clib_error_return (0, "%s %U failed", - is_del ? "del" : "add", - format_vnet_sw_interface_name, vnm, - vnet_get_sw_interface (vnm, - sw_if_index)); - goto done; - } - } - else - { - if (snat_interface_add_del (sw_if_index, 1, is_del)) - { - error = clib_error_return (0, "%s %U failed", - is_del ? "del" : "add", - format_vnet_sw_interface_name, vnm, - vnet_get_sw_interface (vnm, - sw_if_index)); - goto done; - } - } - } - } - - if (vec_len (outside_sw_if_indices)) - { - for (i = 0; i < vec_len(outside_sw_if_indices); i++) - { - sw_if_index = outside_sw_if_indices[i]; - if (is_output_feature) - { - if (snat_interface_add_del_output_feature (sw_if_index, 0, is_del)) - { - error = clib_error_return (0, "%s %U failed", - is_del ? "del" : "add", - format_vnet_sw_interface_name, vnm, - vnet_get_sw_interface (vnm, - sw_if_index)); - goto done; - } - } - else - { - if (snat_interface_add_del (sw_if_index, 0, is_del)) - { - error = clib_error_return (0, "%s %U failed", - is_del ? "del" : "add", - format_vnet_sw_interface_name, vnm, - vnet_get_sw_interface (vnm, - sw_if_index)); - goto done; - } - } - } - } - -done: - unformat_free (line_input); - vec_free (inside_sw_if_indices); - vec_free (outside_sw_if_indices); - - return error; -} - -VLIB_CLI_COMMAND (set_interface_snat_command, static) = { - .path = "set interface snat", - .function = snat_feature_command_fn, - .short_help = "set interface snat in out [output-feature] " - "[del]", -}; - -uword -unformat_snat_protocol (unformat_input_t * input, va_list * args) -{ - u32 *r = va_arg (*args, u32 *); - - if (0); -#define _(N, i, n, s) else if (unformat (input, s)) *r = SNAT_PROTOCOL_##N; - foreach_snat_protocol -#undef _ - else - return 0; - return 1; -} - -u8 * -format_snat_protocol (u8 * s, va_list * args) -{ - u32 i = va_arg (*args, u32); - u8 *t = 0; - - switch (i) - { -#define _(N, j, n, str) case SNAT_PROTOCOL_##N: t = (u8 *) str; break; - foreach_snat_protocol -#undef _ - default: - s = format (s, "unknown"); - return s; - } - s = format (s, "%s", t); - return s; -} - -static clib_error_t * -add_static_mapping_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - clib_error_t * error = 0; - ip4_address_t l_addr, e_addr; - u32 l_port = 0, e_port = 0, vrf_id = ~0; - int is_add = 1; - int addr_only = 1; - u32 sw_if_index = ~0; - vnet_main_t * vnm = vnet_get_main(); - int rv; - snat_protocol_t proto; - u8 proto_set = 0; - - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (line_input, "local %U %u", unformat_ip4_address, &l_addr, - &l_port)) - addr_only = 0; - else if (unformat (line_input, "local %U", unformat_ip4_address, &l_addr)) - ; - else if (unformat (line_input, "external %U %u", unformat_ip4_address, - &e_addr, &e_port)) - addr_only = 0; - else if (unformat (line_input, "external %U", unformat_ip4_address, - &e_addr)) - ; - else if (unformat (line_input, "external %U %u", - unformat_vnet_sw_interface, vnm, &sw_if_index, - &e_port)) - addr_only = 0; - - else if (unformat (line_input, "external %U", - unformat_vnet_sw_interface, vnm, &sw_if_index)) - ; - else if (unformat (line_input, "vrf %u", &vrf_id)) - ; - else if (unformat (line_input, "%U", unformat_snat_protocol, &proto)) - proto_set = 1; - else if (unformat (line_input, "del")) - is_add = 0; - else - { - error = clib_error_return (0, "unknown input: '%U'", - format_unformat_error, line_input); - goto done; - } - } - - if (!addr_only && !proto_set) - { - error = clib_error_return (0, "missing protocol"); - goto done; - } - - rv = snat_add_static_mapping(l_addr, e_addr, (u16) l_port, (u16) e_port, - vrf_id, addr_only, sw_if_index, proto, is_add); - - switch (rv) - { - case VNET_API_ERROR_INVALID_VALUE: - error = clib_error_return (0, "External port already in use."); - goto done; - case VNET_API_ERROR_NO_SUCH_ENTRY: - if (is_add) - error = clib_error_return (0, "External addres must be allocated."); - else - error = clib_error_return (0, "Mapping not exist."); - goto done; - case VNET_API_ERROR_NO_SUCH_FIB: - error = clib_error_return (0, "No such VRF id."); - goto done; - case VNET_API_ERROR_VALUE_EXIST: - error = clib_error_return (0, "Mapping already exist."); - goto done; - default: - break; - } - -done: - unformat_free (line_input); - - return error; -} - -/*? - * @cliexpar - * @cliexstart{snat add static mapping} - * Static mapping allows hosts on the external network to initiate connection - * to to the local network host. - * To create static mapping between local host address 10.0.0.3 port 6303 and - * external address 4.4.4.4 port 3606 for TCP protocol use: - * vpp# snat add static mapping local tcp 10.0.0.3 6303 external 4.4.4.4 3606 - * If not runnig "static mapping only" S-NAT plugin mode use before: - * vpp# snat add address 4.4.4.4 - * To create static mapping between local and external address use: - * vpp# snat add static mapping local 10.0.0.3 external 4.4.4.4 - * @cliexend -?*/ -VLIB_CLI_COMMAND (add_static_mapping_command, static) = { - .path = "snat add static mapping", - .function = add_static_mapping_command_fn, - .short_help = - "snat add static mapping local tcp|udp|icmp [] external [] [vrf ] [del]", -}; - -static clib_error_t * -set_workers_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - uword *bitmap = 0; - int rv = 0; - clib_error_t *error = 0; - - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (line_input, "%U", unformat_bitmap_list, &bitmap)) - ; - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - - if (bitmap == 0) - { - error = clib_error_return (0, "List of workers must be specified."); - goto done; - } - - rv = snat_set_workers(bitmap); - - clib_bitmap_free (bitmap); - - switch (rv) - { - case VNET_API_ERROR_INVALID_WORKER: - error = clib_error_return (0, "Invalid worker(s)."); - goto done; - case VNET_API_ERROR_FEATURE_DISABLED: - error = clib_error_return (0, - "Supported only if 2 or more workes available."); - goto done; - default: - break; - } - -done: - unformat_free (line_input); - - return error; -} - -/*? - * @cliexpar - * @cliexstart{set snat workers} - * Set SNAT workers if 2 or more workers available, use: - * vpp# set snat workers 0-2,5 - * @cliexend -?*/ -VLIB_CLI_COMMAND (set_workers_command, static) = { - .path = "set snat workers", - .function = set_workers_command_fn, - .short_help = - "set snat workers ", -}; - -static clib_error_t * -snat_ipfix_logging_enable_disable_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - unformat_input_t _line_input, *line_input = &_line_input; - u32 domain_id = 0; - u32 src_port = 0; - u8 enable = 1; - int rv = 0; - clib_error_t *error = 0; - - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (line_input, "domain %d", &domain_id)) - ; - else if (unformat (line_input, "src-port %d", &src_port)) - ; - else if (unformat (line_input, "disable")) - enable = 0; - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - - rv = snat_ipfix_logging_enable_disable (enable, domain_id, (u16) src_port); - - if (rv) - { - error = clib_error_return (0, "ipfix logging enable failed"); - goto done; - } - -done: - unformat_free (line_input); - - return error; -} - -/*? - * @cliexpar - * @cliexstart{snat ipfix logging} - * To enable SNAT IPFIX logging use: - * vpp# snat ipfix logging - * To set IPFIX exporter use: - * vpp# set ipfix exporter collector 10.10.10.3 src 10.10.10.1 - * @cliexend -?*/ -VLIB_CLI_COMMAND (snat_ipfix_logging_enable_disable_command, static) = { - .path = "snat ipfix logging", - .function = snat_ipfix_logging_enable_disable_command_fn, - .short_help = "snat ipfix logging [domain ] [src-port ] [disable]", -}; - -static u32 -snat_get_worker_in2out_cb (ip4_header_t * ip0, u32 rx_fib_index0) -{ - snat_main_t *sm = &snat_main; - snat_user_key_t key0; - clib_bihash_kv_8_8_t kv0, value0; - u32 next_worker_index = 0; - - key0.addr = ip0->src_address; - key0.fib_index = rx_fib_index0; - - kv0.key = key0.as_u64; - - /* Ever heard of of the "user" before? */ - if (clib_bihash_search_8_8 (&sm->worker_by_in, &kv0, &value0)) - { - /* No, assign next available worker (RR) */ - next_worker_index = sm->first_worker_index; - if (vec_len (sm->workers)) - { - next_worker_index += - sm->workers[sm->next_worker++ % _vec_len (sm->workers)]; - } - - /* add non-traslated packets worker lookup */ - kv0.value = next_worker_index; - clib_bihash_add_del_8_8 (&sm->worker_by_in, &kv0, 1); - } - else - next_worker_index = value0.value; - - return next_worker_index; -} - -static u32 -snat_get_worker_out2in_cb (ip4_header_t * ip0, u32 rx_fib_index0) -{ - snat_main_t *sm = &snat_main; - snat_worker_key_t key0; - clib_bihash_kv_8_8_t kv0, value0; - udp_header_t * udp0; - u32 next_worker_index = 0; - - udp0 = ip4_next_header (ip0); - - key0.addr = ip0->dst_address; - key0.port = udp0->dst_port; - key0.fib_index = rx_fib_index0; - - if (PREDICT_FALSE(ip0->protocol == IP_PROTOCOL_ICMP)) - { - icmp46_header_t * icmp0 = (icmp46_header_t *) udp0; - icmp_echo_header_t *echo0 = (icmp_echo_header_t *)(icmp0+1); - key0.port = echo0->identifier; - } - - kv0.key = key0.as_u64; - - /* Ever heard of of the "user" before? */ - if (clib_bihash_search_8_8 (&sm->worker_by_out, &kv0, &value0)) - { - key0.port = 0; - kv0.key = key0.as_u64; - - if (clib_bihash_search_8_8 (&sm->worker_by_out, &kv0, &value0)) - { - /* No, assign next available worker (RR) */ - next_worker_index = sm->first_worker_index; - if (vec_len (sm->workers)) - { - next_worker_index += - sm->workers[sm->next_worker++ % _vec_len (sm->workers)]; - } - } - else - { - /* Static mapping without port */ - next_worker_index = value0.value; - } - - /* Add to translated packets worker lookup */ - kv0.value = next_worker_index; - clib_bihash_add_del_8_8 (&sm->worker_by_out, &kv0, 1); - } - else - next_worker_index = value0.value; - - return next_worker_index; -} - -static clib_error_t * -snat_config (vlib_main_t * vm, unformat_input_t * input) -{ - snat_main_t * sm = &snat_main; - u32 translation_buckets = 1024; - u32 translation_memory_size = 128<<20; - u32 user_buckets = 128; - u32 user_memory_size = 64<<20; - u32 max_translations_per_user = 100; - u32 outside_vrf_id = 0; - u32 inside_vrf_id = 0; - u32 static_mapping_buckets = 1024; - u32 static_mapping_memory_size = 64<<20; - u8 static_mapping_only = 0; - u8 static_mapping_connection_tracking = 0; - - sm->deterministic = 0; - - while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (input, "translation hash buckets %d", &translation_buckets)) - ; - else if (unformat (input, "translation hash memory %d", - &translation_memory_size)); - else if (unformat (input, "user hash buckets %d", &user_buckets)) - ; - else if (unformat (input, "user hash memory %d", - &user_memory_size)) - ; - else if (unformat (input, "max translations per user %d", - &max_translations_per_user)) - ; - else if (unformat (input, "outside VRF id %d", - &outside_vrf_id)) - ; - else if (unformat (input, "inside VRF id %d", - &inside_vrf_id)) - ; - else if (unformat (input, "static mapping only")) - { - static_mapping_only = 1; - if (unformat (input, "connection tracking")) - static_mapping_connection_tracking = 1; - } - else if (unformat (input, "deterministic")) - sm->deterministic = 1; - else - return clib_error_return (0, "unknown input '%U'", - format_unformat_error, input); - } - - /* for show commands, etc. */ - sm->translation_buckets = translation_buckets; - sm->translation_memory_size = translation_memory_size; - sm->user_buckets = user_buckets; - sm->user_memory_size = user_memory_size; - sm->max_translations_per_user = max_translations_per_user; - sm->outside_vrf_id = outside_vrf_id; - sm->outside_fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, - outside_vrf_id); - sm->inside_vrf_id = inside_vrf_id; - sm->inside_fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, - inside_vrf_id); - sm->static_mapping_only = static_mapping_only; - sm->static_mapping_connection_tracking = static_mapping_connection_tracking; - - if (sm->deterministic) - { - sm->in2out_node_index = snat_det_in2out_node.index; - sm->in2out_output_node_index = ~0; - sm->out2in_node_index = snat_det_out2in_node.index; - sm->icmp_match_in2out_cb = icmp_match_in2out_det; - sm->icmp_match_out2in_cb = icmp_match_out2in_det; - } - else - { - sm->worker_in2out_cb = snat_get_worker_in2out_cb; - sm->worker_out2in_cb = snat_get_worker_out2in_cb; - sm->in2out_node_index = snat_in2out_node.index; - sm->in2out_output_node_index = snat_in2out_output_node.index; - sm->out2in_node_index = snat_out2in_node.index; - if (!static_mapping_only || - (static_mapping_only && static_mapping_connection_tracking)) - { - sm->icmp_match_in2out_cb = icmp_match_in2out_slow; - sm->icmp_match_out2in_cb = icmp_match_out2in_slow; - - clib_bihash_init_8_8 (&sm->worker_by_in, "worker-by-in", user_buckets, - user_memory_size); - - clib_bihash_init_8_8 (&sm->worker_by_out, "worker-by-out", user_buckets, - user_memory_size); - - clib_bihash_init_8_8 (&sm->in2out, "in2out", translation_buckets, - translation_memory_size); - - clib_bihash_init_8_8 (&sm->out2in, "out2in", translation_buckets, - translation_memory_size); - - clib_bihash_init_8_8 (&sm->user_hash, "users", user_buckets, - user_memory_size); - - clib_bihash_init_16_8 (&sm->in2out_unk_proto, "in2out-unk-proto", - translation_buckets, translation_memory_size); - - clib_bihash_init_16_8 (&sm->out2in_unk_proto, "out2in-unk-proto", - translation_buckets, translation_memory_size); - } - else - { - sm->icmp_match_in2out_cb = icmp_match_in2out_fast; - sm->icmp_match_out2in_cb = icmp_match_out2in_fast; - } - clib_bihash_init_8_8 (&sm->static_mapping_by_local, - "static_mapping_by_local", static_mapping_buckets, - static_mapping_memory_size); - - clib_bihash_init_8_8 (&sm->static_mapping_by_external, - "static_mapping_by_external", static_mapping_buckets, - static_mapping_memory_size); - } - - return 0; -} - -VLIB_CONFIG_FUNCTION (snat_config, "snat"); - -u8 * format_snat_session_state (u8 * s, va_list * args) -{ - u32 i = va_arg (*args, u32); - u8 *t = 0; - - switch (i) - { -#define _(v, N, str) case SNAT_SESSION_##N: t = (u8 *) str; break; - foreach_snat_session_state -#undef _ - default: - t = format (t, "unknown"); - } - s = format (s, "%s", t); - return s; -} - -u8 * format_snat_key (u8 * s, va_list * args) -{ - snat_session_key_t * key = va_arg (*args, snat_session_key_t *); - char * protocol_string = "unknown"; - static char *protocol_strings[] = { - "UDP", - "TCP", - "ICMP", - }; - - if (key->protocol < ARRAY_LEN(protocol_strings)) - protocol_string = protocol_strings[key->protocol]; - - s = format (s, "%U proto %s port %d fib %d", - format_ip4_address, &key->addr, protocol_string, - clib_net_to_host_u16 (key->port), key->fib_index); - return s; -} - -u8 * format_snat_session (u8 * s, va_list * args) -{ - snat_main_t * sm __attribute__((unused)) = va_arg (*args, snat_main_t *); - snat_session_t * sess = va_arg (*args, snat_session_t *); - - if (snat_is_unk_proto_session (sess)) - { - s = format (s, " i2o %U proto %u fib %u\n", - format_ip4_address, &sess->in2out.addr, sess->in2out.port, - sess->in2out.fib_index); - s = format (s, " o2i %U proto %u fib %u\n", - format_ip4_address, &sess->out2in.addr, sess->out2in.port, - sess->out2in.fib_index); - } - else - { - s = format (s, " i2o %U\n", format_snat_key, &sess->in2out); - s = format (s, " o2i %U\n", format_snat_key, &sess->out2in); - } - s = format (s, " last heard %.2f\n", sess->last_heard); - s = format (s, " total pkts %d, total bytes %lld\n", - sess->total_pkts, sess->total_bytes); - if (snat_is_session_static (sess)) - s = format (s, " static translation\n"); - else - s = format (s, " dynamic translation\n"); - - return s; -} - -u8 * format_snat_user (u8 * s, va_list * args) -{ - snat_main_per_thread_data_t * sm = va_arg (*args, snat_main_per_thread_data_t *); - snat_user_t * u = va_arg (*args, snat_user_t *); - int verbose = va_arg (*args, int); - dlist_elt_t * head, * elt; - u32 elt_index, head_index; - u32 session_index; - snat_session_t * sess; - - s = format (s, "%U: %d dynamic translations, %d static translations\n", - format_ip4_address, &u->addr, u->nsessions, u->nstaticsessions); - - if (verbose == 0) - return s; - - if (u->nsessions || u->nstaticsessions) - { - head_index = u->sessions_per_user_list_head_index; - head = pool_elt_at_index (sm->list_pool, head_index); - - elt_index = head->next; - elt = pool_elt_at_index (sm->list_pool, elt_index); - session_index = elt->value; - - while (session_index != ~0) - { - sess = pool_elt_at_index (sm->sessions, session_index); - - s = format (s, " %U\n", format_snat_session, sm, sess); - - elt_index = elt->next; - elt = pool_elt_at_index (sm->list_pool, elt_index); - session_index = elt->value; - } - } - - return s; -} - -u8 * format_snat_static_mapping (u8 * s, va_list * args) -{ - snat_static_mapping_t *m = va_arg (*args, snat_static_mapping_t *); - - if (m->addr_only) - s = format (s, "local %U external %U vrf %d", - format_ip4_address, &m->local_addr, - format_ip4_address, &m->external_addr, - m->vrf_id); - else - s = format (s, "%U local %U:%d external %U:%d vrf %d", - format_snat_protocol, m->proto, - format_ip4_address, &m->local_addr, m->local_port, - format_ip4_address, &m->external_addr, m->external_port, - m->vrf_id); - - return s; -} - -u8 * format_snat_static_map_to_resolve (u8 * s, va_list * args) -{ - snat_static_map_resolve_t *m = va_arg (*args, snat_static_map_resolve_t *); - vnet_main_t *vnm = vnet_get_main(); - - if (m->addr_only) - s = format (s, "local %U external %U vrf %d", - format_ip4_address, &m->l_addr, - format_vnet_sw_interface_name, vnm, - vnet_get_sw_interface (vnm, m->sw_if_index), - m->vrf_id); - else - s = format (s, "%U local %U:%d external %U:%d vrf %d", - format_snat_protocol, m->proto, - format_ip4_address, &m->l_addr, m->l_port, - format_vnet_sw_interface_name, vnm, - vnet_get_sw_interface (vnm, m->sw_if_index), m->e_port, - m->vrf_id); - - return s; -} - -u8 * format_det_map_ses (u8 * s, va_list * args) -{ - snat_det_map_t * det_map = va_arg (*args, snat_det_map_t *); - ip4_address_t in_addr, out_addr; - u32 in_offset, out_offset; - snat_det_session_t * ses = va_arg (*args, snat_det_session_t *); - u32 * i = va_arg (*args, u32 *); - - u32 user_index = *i / SNAT_DET_SES_PER_USER; - in_addr.as_u32 = clib_host_to_net_u32 ( - clib_net_to_host_u32(det_map->in_addr.as_u32) + user_index); - in_offset = clib_net_to_host_u32(in_addr.as_u32) - - clib_net_to_host_u32(det_map->in_addr.as_u32); - out_offset = in_offset / det_map->sharing_ratio; - out_addr.as_u32 = clib_host_to_net_u32( - clib_net_to_host_u32(det_map->out_addr.as_u32) + out_offset); - s = format (s, "in %U:%d out %U:%d external host %U:%d state: %U expire: %d\n", - format_ip4_address, &in_addr, - clib_net_to_host_u16 (ses->in_port), - format_ip4_address, &out_addr, - clib_net_to_host_u16 (ses->out.out_port), - format_ip4_address, &ses->out.ext_host_addr, - clib_net_to_host_u16 (ses->out.ext_host_port), - format_snat_session_state, ses->state, - ses->expire); - - return s; -} - -static clib_error_t * -show_snat_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - int verbose = 0; - snat_main_t * sm = &snat_main; - snat_user_t * u; - snat_static_mapping_t *m; - snat_interface_t *i; - snat_address_t * ap; - vnet_main_t *vnm = vnet_get_main(); - snat_main_per_thread_data_t *tsm; - u32 users_num = 0, sessions_num = 0, *worker, *sw_if_index; - uword j = 0; - snat_static_map_resolve_t *rp; - snat_det_map_t * dm; - snat_det_session_t * ses; - - if (unformat (input, "detail")) - verbose = 1; - else if (unformat (input, "verbose")) - verbose = 2; - - if (sm->static_mapping_only) - { - if (sm->static_mapping_connection_tracking) - vlib_cli_output (vm, "SNAT mode: static mapping only connection " - "tracking"); - else - vlib_cli_output (vm, "SNAT mode: static mapping only"); - } - else if (sm->deterministic) - { - vlib_cli_output (vm, "SNAT mode: deterministic mapping"); - } - else - { - vlib_cli_output (vm, "SNAT mode: dynamic translations enabled"); - } - - if (verbose > 0) - { - pool_foreach (i, sm->interfaces, - ({ - vlib_cli_output (vm, "%U %s", format_vnet_sw_interface_name, vnm, - vnet_get_sw_interface (vnm, i->sw_if_index), - i->is_inside ? "in" : "out"); - })); - - pool_foreach (i, sm->output_feature_interfaces, - ({ - vlib_cli_output (vm, "%U output-feature %s", - format_vnet_sw_interface_name, vnm, - vnet_get_sw_interface (vnm, i->sw_if_index), - i->is_inside ? "in" : "out"); - })); - - if (vec_len (sm->auto_add_sw_if_indices)) - { - vlib_cli_output (vm, "SNAT pool addresses interfaces:"); - vec_foreach (sw_if_index, sm->auto_add_sw_if_indices) - { - vlib_cli_output (vm, "%U", format_vnet_sw_interface_name, vnm, - vnet_get_sw_interface (vnm, *sw_if_index)); - } - } - - vec_foreach (ap, sm->addresses) - { - vlib_cli_output (vm, "%U", format_ip4_address, &ap->addr); - if (ap->fib_index != ~0) - vlib_cli_output (vm, " tenant VRF: %u", - ip4_fib_get(ap->fib_index)->table_id); - else - vlib_cli_output (vm, " tenant VRF independent"); -#define _(N, i, n, s) \ - vlib_cli_output (vm, " %d busy %s ports", ap->busy_##n##_ports, s); - foreach_snat_protocol -#undef _ - } - } - - if (sm->num_workers > 1) - { - vlib_cli_output (vm, "%d workers", vec_len (sm->workers)); - if (verbose > 0) - { - vec_foreach (worker, sm->workers) - { - vlib_worker_thread_t *w = - vlib_worker_threads + *worker + sm->first_worker_index; - vlib_cli_output (vm, " %s", w->name); - } - } - } - - if (sm->deterministic) - { - vlib_cli_output (vm, "udp timeout: %dsec", sm->udp_timeout); - vlib_cli_output (vm, "tcp-established timeout: %dsec", - sm->tcp_established_timeout); - vlib_cli_output (vm, "tcp-transitory timeout: %dsec", - sm->tcp_transitory_timeout); - vlib_cli_output (vm, "icmp timeout: %dsec", sm->icmp_timeout); - vlib_cli_output (vm, "%d deterministic mappings", - pool_elts (sm->det_maps)); - if (verbose > 0) - { - pool_foreach (dm, sm->det_maps, - ({ - vlib_cli_output (vm, "in %U/%d out %U/%d\n", - format_ip4_address, &dm->in_addr, dm->in_plen, - format_ip4_address, &dm->out_addr, dm->out_plen); - vlib_cli_output (vm, " outside address sharing ratio: %d\n", - dm->sharing_ratio); - vlib_cli_output (vm, " number of ports per inside host: %d\n", - dm->ports_per_host); - vlib_cli_output (vm, " sessions number: %d\n", dm->ses_num); - if (verbose > 1) - { - vec_foreach_index (j, dm->sessions) - { - ses = vec_elt_at_index (dm->sessions, j); - if (ses->in_port) - vlib_cli_output (vm, " %U", format_det_map_ses, dm, ses, - &j); - } - } - })); - } - } - else - { - if (sm->static_mapping_only && !(sm->static_mapping_connection_tracking)) - { - vlib_cli_output (vm, "%d static mappings", - pool_elts (sm->static_mappings)); - - if (verbose > 0) - { - pool_foreach (m, sm->static_mappings, - ({ - vlib_cli_output (vm, "%U", format_snat_static_mapping, m); - })); - } - } - else - { - vec_foreach (tsm, sm->per_thread_data) - { - users_num += pool_elts (tsm->users); - sessions_num += pool_elts (tsm->sessions); - } - - vlib_cli_output (vm, "%d users, %d outside addresses, %d active sessions," - " %d static mappings", - users_num, - vec_len (sm->addresses), - sessions_num, - pool_elts (sm->static_mappings)); - - if (verbose > 0) - { - vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->in2out, - verbose - 1); - vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->out2in, - verbose - 1); - vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->worker_by_in, - verbose - 1); - vlib_cli_output (vm, "%U", format_bihash_8_8, &sm->worker_by_out, - verbose - 1); - vec_foreach_index (j, sm->per_thread_data) - { - tsm = vec_elt_at_index (sm->per_thread_data, j); - - if (pool_elts (tsm->users) == 0) - continue; - - vlib_worker_thread_t *w = vlib_worker_threads + j; - vlib_cli_output (vm, "Thread %d (%s at lcore %u):", j, w->name, - w->lcore_id); - vlib_cli_output (vm, " %d list pool elements", - pool_elts (tsm->list_pool)); - - pool_foreach (u, tsm->users, - ({ - vlib_cli_output (vm, " %U", format_snat_user, tsm, u, - verbose - 1); - })); - } - - if (pool_elts (sm->static_mappings)) - { - vlib_cli_output (vm, "static mappings:"); - pool_foreach (m, sm->static_mappings, - ({ - vlib_cli_output (vm, "%U", format_snat_static_mapping, m); - })); - for (j = 0; j < vec_len (sm->to_resolve); j++) - { - rp = sm->to_resolve + j; - vlib_cli_output (vm, "%U", - format_snat_static_map_to_resolve, rp); - } - } - } - } - } - return 0; -} - -VLIB_CLI_COMMAND (show_snat_command, static) = { - .path = "show snat", - .short_help = "show snat", - .function = show_snat_command_fn, -}; - - -static void -snat_ip4_add_del_interface_address_cb (ip4_main_t * im, - uword opaque, - u32 sw_if_index, - ip4_address_t * address, - u32 address_length, - u32 if_address_index, - u32 is_delete) -{ - snat_main_t *sm = &snat_main; - snat_static_map_resolve_t *rp; - u32 *indices_to_delete = 0; - int i, j; - int rv; - - for (i = 0; i < vec_len(sm->auto_add_sw_if_indices); i++) - { - if (sw_if_index == sm->auto_add_sw_if_indices[i]) - { - if (!is_delete) - { - /* Don't trip over lease renewal, static config */ - for (j = 0; j < vec_len(sm->addresses); j++) - if (sm->addresses[j].addr.as_u32 == address->as_u32) - return; - - snat_add_address (sm, address, ~0); - /* Scan static map resolution vector */ - for (j = 0; j < vec_len (sm->to_resolve); j++) - { - rp = sm->to_resolve + j; - /* On this interface? */ - if (rp->sw_if_index == sw_if_index) - { - /* Add the static mapping */ - rv = snat_add_static_mapping (rp->l_addr, - address[0], - rp->l_port, - rp->e_port, - rp->vrf_id, - rp->addr_only, - ~0 /* sw_if_index */, - rp->proto, - rp->is_add); - if (rv) - clib_warning ("snat_add_static_mapping returned %d", - rv); - vec_add1 (indices_to_delete, j); - } - } - /* If we resolved any of the outstanding static mappings */ - if (vec_len(indices_to_delete)) - { - /* Delete them */ - for (j = vec_len(indices_to_delete)-1; j >= 0; j--) - vec_delete(sm->to_resolve, 1, j); - vec_free(indices_to_delete); - } - return; - } - else - { - (void) snat_del_address(sm, address[0], 1); - return; - } - } - } -} - - -int snat_add_interface_address (snat_main_t *sm, u32 sw_if_index, int is_del) -{ - ip4_main_t * ip4_main = sm->ip4_main; - ip4_address_t * first_int_addr; - snat_static_map_resolve_t *rp; - u32 *indices_to_delete = 0; - int i, j; - - first_int_addr = ip4_interface_first_address (ip4_main, sw_if_index, - 0 /* just want the address*/); - - for (i = 0; i < vec_len(sm->auto_add_sw_if_indices); i++) - { - if (sm->auto_add_sw_if_indices[i] == sw_if_index) - { - if (is_del) - { - /* if have address remove it */ - if (first_int_addr) - (void) snat_del_address (sm, first_int_addr[0], 1); - else - { - for (j = 0; j < vec_len (sm->to_resolve); j++) - { - rp = sm->to_resolve + j; - if (rp->sw_if_index == sw_if_index) - vec_add1 (indices_to_delete, j); - } - if (vec_len(indices_to_delete)) - { - for (j = vec_len(indices_to_delete)-1; j >= 0; j--) - vec_del1(sm->to_resolve, j); - vec_free(indices_to_delete); - } - } - vec_del1(sm->auto_add_sw_if_indices, i); - } - else - return VNET_API_ERROR_VALUE_EXIST; - - return 0; - } - } - - if (is_del) - return VNET_API_ERROR_NO_SUCH_ENTRY; - - /* add to the auto-address list */ - vec_add1(sm->auto_add_sw_if_indices, sw_if_index); - - /* If the address is already bound - or static - add it now */ - if (first_int_addr) - snat_add_address (sm, first_int_addr, ~0); - - return 0; -} - -static clib_error_t * -snat_add_interface_address_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - snat_main_t *sm = &snat_main; - unformat_input_t _line_input, *line_input = &_line_input; - u32 sw_if_index; - int rv; - int is_del = 0; - clib_error_t *error = 0; - - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (line_input, "%U", unformat_vnet_sw_interface, - sm->vnet_main, &sw_if_index)) - ; - else if (unformat (line_input, "del")) - is_del = 1; - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - - rv = snat_add_interface_address (sm, sw_if_index, is_del); - - switch (rv) - { - case 0: - break; - - default: - error = clib_error_return (0, "snat_add_interface_address returned %d", - rv); - goto done; - } - -done: - unformat_free (line_input); - - return error; -} - -VLIB_CLI_COMMAND (snat_add_interface_address_command, static) = { - .path = "snat add interface address", - .short_help = "snat add interface address [del]", - .function = snat_add_interface_address_command_fn, -}; - -static clib_error_t * -snat_det_map_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - snat_main_t *sm = &snat_main; - unformat_input_t _line_input, *line_input = &_line_input; - ip4_address_t in_addr, out_addr; - u32 in_plen, out_plen; - int is_add = 1, rv; - clib_error_t *error = 0; - - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (line_input, "in %U/%u", unformat_ip4_address, &in_addr, &in_plen)) - ; - else if (unformat (line_input, "out %U/%u", unformat_ip4_address, &out_addr, &out_plen)) - ; - else if (unformat (line_input, "del")) - is_add = 0; - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - - unformat_free (line_input); - - rv = snat_det_add_map(sm, &in_addr, (u8) in_plen, &out_addr, (u8)out_plen, - is_add); - - if (rv) - { - error = clib_error_return (0, "snat_det_add_map return %d", rv); - goto done; - } - -done: - unformat_free (line_input); - - return error; -} - -/*? - * @cliexpar - * @cliexstart{snat deterministic add} - * Create bijective mapping of inside address to outside address and port range - * pairs, with the purpose of enabling deterministic NAT to reduce logging in - * CGN deployments. - * To create deterministic mapping between inside network 10.0.0.0/18 and - * outside network 1.1.1.0/30 use: - * # vpp# snat deterministic add in 10.0.0.0/18 out 1.1.1.0/30 - * @cliexend -?*/ -VLIB_CLI_COMMAND (snat_det_map_command, static) = { - .path = "snat deterministic add", - .short_help = "snat deterministic add in / out / [del]", - .function = snat_det_map_command_fn, -}; - -static clib_error_t * -snat_det_forward_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - snat_main_t *sm = &snat_main; - unformat_input_t _line_input, *line_input = &_line_input; - ip4_address_t in_addr, out_addr; - u16 lo_port; - snat_det_map_t * dm; - clib_error_t *error = 0; - - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (line_input, "%U", unformat_ip4_address, &in_addr)) - ; - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - - unformat_free (line_input); - - dm = snat_det_map_by_user(sm, &in_addr); - if (!dm) - vlib_cli_output (vm, "no match"); - else - { - snat_det_forward (dm, &in_addr, &out_addr, &lo_port); - vlib_cli_output (vm, "%U:<%d-%d>", format_ip4_address, &out_addr, - lo_port, lo_port + dm->ports_per_host - 1); - } - -done: - unformat_free (line_input); - - return error; -} - -/*? - * @cliexpar - * @cliexstart{snat deterministic forward} - * Return outside address and port range from inside address for deterministic - * NAT. - * To obtain outside address and port of inside host use: - * vpp# snat deterministic forward 10.0.0.2 - * 1.1.1.0:<1054-1068> - * @cliexend -?*/ -VLIB_CLI_COMMAND (snat_det_forward_command, static) = { - .path = "snat deterministic forward", - .short_help = "snat deterministic forward ", - .function = snat_det_forward_command_fn, -}; - -static clib_error_t * -snat_det_reverse_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - snat_main_t *sm = &snat_main; - unformat_input_t _line_input, *line_input = &_line_input; - ip4_address_t in_addr, out_addr; - u32 out_port; - snat_det_map_t * dm; - clib_error_t *error = 0; - - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (line_input, "%U:%d", unformat_ip4_address, &out_addr, &out_port)) - ; - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - } - } - - unformat_free (line_input); - - if (out_port < 1024 || out_port > 65535) - { - error = clib_error_return (0, "wrong port, must be <1024-65535>"); - goto done; - } - - dm = snat_det_map_by_out(sm, &out_addr); - if (!dm) - vlib_cli_output (vm, "no match"); - else - { - snat_det_reverse (dm, &out_addr, (u16) out_port, &in_addr); - vlib_cli_output (vm, "%U", format_ip4_address, &in_addr); - } - -done: - unformat_free (line_input); - - return error; -} - -/*? - * @cliexpar - * @cliexstart{snat deterministic reverse} - * Return inside address from outside address and port for deterministic NAT. - * To obtain inside host address from outside address and port use: - * #vpp snat deterministic reverse 1.1.1.1:1276 - * 10.0.16.16 - * @cliexend -?*/ -VLIB_CLI_COMMAND (snat_det_reverse_command, static) = { - .path = "snat deterministic reverse", - .short_help = "snat deterministic reverse :", - .function = snat_det_reverse_command_fn, -}; - -static clib_error_t * -set_timeout_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - snat_main_t *sm = &snat_main; - unformat_input_t _line_input, *line_input = &_line_input; - clib_error_t *error = 0; - - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (line_input, "udp %u", &sm->udp_timeout)) - ; - else if (unformat (line_input, "tcp-established %u", - &sm->tcp_established_timeout)) - ; - else if (unformat (line_input, "tcp-transitory %u", - &sm->tcp_transitory_timeout)) - ; - else if (unformat (line_input, "icmp %u", &sm->icmp_timeout)) - ; - else if (unformat (line_input, "reset")) - { - sm->udp_timeout = SNAT_UDP_TIMEOUT; - sm->tcp_established_timeout = SNAT_TCP_ESTABLISHED_TIMEOUT; - sm->tcp_transitory_timeout = SNAT_TCP_TRANSITORY_TIMEOUT; - sm->icmp_timeout = SNAT_ICMP_TIMEOUT; - } - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - - unformat_free (line_input); - -done: - unformat_free (line_input); - - return error; -} - -/*? - * @cliexpar - * @cliexstart{set snat deterministic timeout} - * Set values of timeouts for deterministic NAT (in seconds), use: - * vpp# set snat deterministic timeout udp 120 tcp-established 7500 - * tcp-transitory 250 icmp 90 - * To reset default values use: - * vpp# set snat deterministic timeout reset - * @cliexend -?*/ -VLIB_CLI_COMMAND (set_timeout_command, static) = { - .path = "set snat deterministic timeout", - .function = set_timeout_command_fn, - .short_help = - "set snat deterministic timeout [udp | tcp-established " - "tcp-transitory | icmp | reset]", -}; - -static clib_error_t * -snat_det_close_session_out_fn (vlib_main_t *vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - snat_main_t *sm = &snat_main; - unformat_input_t _line_input, *line_input = &_line_input; - ip4_address_t out_addr, ext_addr, in_addr; - u16 out_port, ext_port; - snat_det_map_t * dm; - snat_det_session_t * ses; - snat_det_out_key_t key; - clib_error_t *error = 0; - - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (line_input, "%U:%d %U:%d", - unformat_ip4_address, &out_addr, &out_port, - unformat_ip4_address, &ext_addr, &ext_port)) - ; - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - - unformat_free (line_input); - - dm = snat_det_map_by_out(sm, &out_addr); - if (!dm) - vlib_cli_output (vm, "no match"); - else - { - snat_det_reverse(dm, &ext_addr, out_port, &in_addr); - key.ext_host_addr = out_addr; - key.ext_host_port = ntohs(ext_port); - key.out_port = ntohs(out_port); - ses = snat_det_get_ses_by_out(dm, &out_addr, key.as_u64); - if (!ses) - vlib_cli_output (vm, "no match"); - else - snat_det_ses_close(dm, ses); - } - -done: - unformat_free (line_input); - - return error; -} - -/*? - * @cliexpar - * @cliexstart{snat deterministic close session out} - * Close session using outside ip address and port - * and external ip address and port, use: - * vpp# snat deterministic close session out 1.1.1.1:1276 2.2.2.2:2387 - * @cliexend -?*/ -VLIB_CLI_COMMAND (snat_det_close_sesion_out_command, static) = { - .path = "snat deterministic close session out", - .short_help = "snat deterministic close session out " - ": :", - .function = snat_det_close_session_out_fn, -}; - -static clib_error_t * -snat_det_close_session_in_fn (vlib_main_t *vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) -{ - snat_main_t *sm = &snat_main; - unformat_input_t _line_input, *line_input = &_line_input; - ip4_address_t in_addr, ext_addr; - u16 in_port, ext_port; - snat_det_map_t * dm; - snat_det_session_t * ses; - snat_det_out_key_t key; - clib_error_t *error = 0; - - /* Get a line of input. */ - if (!unformat_user (input, unformat_line_input, line_input)) - return 0; - - while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) - { - if (unformat (line_input, "%U:%d %U:%d", - unformat_ip4_address, &in_addr, &in_port, - unformat_ip4_address, &ext_addr, &ext_port)) - ; - else - { - error = clib_error_return (0, "unknown input '%U'", - format_unformat_error, line_input); - goto done; - } - } - - unformat_free (line_input); - - dm = snat_det_map_by_user (sm, &in_addr); - if (!dm) - vlib_cli_output (vm, "no match"); - else - { - key.ext_host_addr = ext_addr; - key.ext_host_port = ntohs (ext_port); - ses = snat_det_find_ses_by_in (dm, &in_addr, ntohs(in_port), key); - if (!ses) - vlib_cli_output (vm, "no match"); - else - snat_det_ses_close(dm, ses); - } - -done: - unformat_free(line_input); - - return error; -} - -/*? - * @cliexpar - * @cliexstart{snat deterministic close_session_in} - * Close session using inside ip address and port - * and external ip address and port, use: - * vpp# snat deterministic close session in 3.3.3.3:3487 2.2.2.2:2387 - * @cliexend -?*/ -VLIB_CLI_COMMAND (snat_det_close_session_in_command, static) = { - .path = "snat deterministic close session in", - .short_help = "snat deterministic close session in " - ": :", - .function = snat_det_close_session_in_fn, -}; diff --git a/src/plugins/snat/snat.h b/src/plugins/snat/snat.h deleted file mode 100644 index aa0f82fc..00000000 --- a/src/plugins/snat/snat.h +++ /dev/null @@ -1,541 +0,0 @@ - -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#define SNAT_UDP_TIMEOUT 300 -#define SNAT_UDP_TIMEOUT_MIN 120 -#define SNAT_TCP_TRANSITORY_TIMEOUT 240 -#define SNAT_TCP_ESTABLISHED_TIMEOUT 7440 -#define SNAT_TCP_INCOMING_SYN 6 -#define SNAT_ICMP_TIMEOUT 60 - -#define SNAT_FLAG_HAIRPINNING (1 << 0) - -/* 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 l_addr; - ip4_address_t r_addr; - u32 fib_index; - u8 proto; - u8 rsvd[3]; - }; - u64 as_u64[2]; - }; -} snat_unk_proto_ses_key_t; - -typedef struct { - union - { - struct - { - ip4_address_t ext_host_addr; - u16 ext_host_port; - u16 out_port; - }; - u64 as_u64; - }; -} snat_det_out_key_t; - -typedef struct { - union - { - struct - { - ip4_address_t addr; - u32 fib_index; - }; - u64 as_u64; - }; -} snat_user_key_t; - -typedef struct { - union - { - struct - { - ip4_address_t addr; - u16 port; - u16 fib_index; - }; - u64 as_u64; - }; -} snat_worker_key_t; - - -#define foreach_snat_protocol \ - _(UDP, 0, udp, "udp") \ - _(TCP, 1, tcp, "tcp") \ - _(ICMP, 2, icmp, "icmp") - -typedef enum { -#define _(N, i, n, s) SNAT_PROTOCOL_##N = i, - foreach_snat_protocol -#undef _ -} snat_protocol_t; - - -#define foreach_snat_session_state \ - _(0, UNKNOWN, "unknown") \ - _(1, UDP_ACTIVE, "udp-active") \ - _(2, TCP_SYN_SENT, "tcp-syn-sent") \ - _(3, TCP_ESTABLISHED, "tcp-established") \ - _(4, TCP_FIN_WAIT, "tcp-fin-wait") \ - _(5, TCP_CLOSE_WAIT, "tcp-close-wait") \ - _(6, TCP_LAST_ACK, "tcp-last-ack") \ - _(7, ICMP_ACTIVE, "icmp-active") - -typedef enum { -#define _(v, N, s) SNAT_SESSION_##N = v, - foreach_snat_session_state -#undef _ -} snat_session_state_t; - - -#define SNAT_SESSION_FLAG_STATIC_MAPPING 1 -#define SNAT_SESSION_FLAG_UNKNOWN_PROTO 2 - -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 */ - - /* External host address */ - ip4_address_t ext_host_addr; /* 68-71 */ - -}) snat_session_t; - - -typedef struct { - ip4_address_t addr; - u32 fib_index; - u32 sessions_per_user_list_head_index; - u32 nsessions; - u32 nstaticsessions; -} snat_user_t; - -typedef struct { - ip4_address_t addr; - u32 fib_index; -#define _(N, i, n, s) \ - u32 busy_##n##_ports; \ - uword * busy_##n##_port_bitmap; - foreach_snat_protocol -#undef _ -} snat_address_t; - -typedef struct { - u16 in_port; - snat_det_out_key_t out; - u8 state; - u32 expire; -} snat_det_session_t; - -typedef struct { - ip4_address_t in_addr; - u8 in_plen; - ip4_address_t out_addr; - u8 out_plen; - u32 sharing_ratio; - u16 ports_per_host; - u32 ses_num; - /* vector of sessions */ - snat_det_session_t * sessions; -} snat_det_map_t; - -typedef struct { - ip4_address_t local_addr; - ip4_address_t external_addr; - u16 local_port; - u16 external_port; - u8 addr_only; - u32 vrf_id; - u32 fib_index; - snat_protocol_t proto; -} snat_static_mapping_t; - -typedef struct { - u32 sw_if_index; - u8 is_inside; -} snat_interface_t; - -typedef struct { - ip4_address_t l_addr; - u16 l_port; - u16 e_port; - u32 sw_if_index; - u32 vrf_id; - snat_protocol_t proto; - int addr_only; - int is_add; -} snat_static_map_resolve_t; - -typedef struct { - /* User pool */ - snat_user_t * users; - - /* Session pool */ - snat_session_t * sessions; - - /* Pool of doubly-linked list elements */ - dlist_elt_t * list_pool; - - u32 snat_thread_index; -} snat_main_per_thread_data_t; - -struct snat_main_s; - -typedef u32 snat_icmp_match_function_t (struct snat_main_s *sm, - vlib_node_runtime_t *node, - u32 thread_index, - vlib_buffer_t *b0, - u8 *p_proto, - snat_session_key_t *p_value, - u8 *p_dont_translate, - void *d, - void *e); - -typedef u32 (snat_get_worker_function_t) (ip4_header_t * ip, u32 rx_fib_index); - -typedef struct snat_main_s { - /* Main lookup tables */ - clib_bihash_8_8_t out2in; - clib_bihash_8_8_t in2out; - - /* Unknown protocol sessions lookup tables */ - clib_bihash_16_8_t out2in_unk_proto; - clib_bihash_16_8_t in2out_unk_proto; - - /* Find-a-user => src address lookup */ - clib_bihash_8_8_t user_hash; - - /* Non-translated packets worker lookup => src address + VRF */ - clib_bihash_8_8_t worker_by_in; - - /* Translated packets worker lookup => IP address + port number */ - clib_bihash_8_8_t worker_by_out; - - snat_icmp_match_function_t * icmp_match_in2out_cb; - snat_icmp_match_function_t * icmp_match_out2in_cb; - - u32 num_workers; - u32 first_worker_index; - u32 next_worker; - u32 * workers; - snat_get_worker_function_t * worker_in2out_cb; - snat_get_worker_function_t * worker_out2in_cb; - u16 port_per_thread; - u32 num_snat_thread; - - /* Per thread data */ - snat_main_per_thread_data_t * per_thread_data; - - /* Find a static mapping by local */ - clib_bihash_8_8_t static_mapping_by_local; - - /* Find a static mapping by external */ - clib_bihash_8_8_t static_mapping_by_external; - - /* Static mapping pool */ - snat_static_mapping_t * static_mappings; - - /* Interface pool */ - snat_interface_t * interfaces; - snat_interface_t * output_feature_interfaces; - - /* Vector of outside addresses */ - snat_address_t * addresses; - - /* sw_if_indices whose intfc addresses should be auto-added */ - u32 * auto_add_sw_if_indices; - - /* vector of interface address static mappings to resolve. */ - snat_static_map_resolve_t *to_resolve; - - /* Randomize port allocation order */ - u32 random_seed; - - /* Worker handoff index */ - u32 fq_in2out_index; - u32 fq_in2out_output_index; - u32 fq_out2in_index; - - /* in2out and out2in node index */ - u32 in2out_node_index; - u32 in2out_output_node_index; - u32 out2in_node_index; - - /* Deterministic NAT */ - snat_det_map_t * det_maps; - - /* Config parameters */ - u8 static_mapping_only; - u8 static_mapping_connection_tracking; - u8 deterministic; - 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; - u32 inside_vrf_id; - u32 inside_fib_index; - - /* tenant VRF aware address pool activation flag */ - u8 vrf_mode; - - /* values of various timeouts */ - u32 udp_timeout; - u32 tcp_established_timeout; - u32 tcp_transitory_timeout; - u32 icmp_timeout; - - /* 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; - 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_in2out_output_node; -extern vlib_node_registration_t snat_out2in_node; -extern vlib_node_registration_t snat_in2out_fast_node; -extern vlib_node_registration_t snat_out2in_fast_node; -extern vlib_node_registration_t snat_in2out_worker_handoff_node; -extern vlib_node_registration_t snat_in2out_output_worker_handoff_node; -extern vlib_node_registration_t snat_out2in_worker_handoff_node; -extern vlib_node_registration_t snat_det_in2out_node; -extern vlib_node_registration_t snat_det_out2in_node; -extern vlib_node_registration_t snat_hairpin_dst_node; -extern vlib_node_registration_t snat_hairpin_src_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, - u32 fib_index, - u32 thread_index, - snat_session_key_t * k, - u32 * address_indexp); - -int snat_static_mapping_match (snat_main_t * sm, - snat_session_key_t match, - snat_session_key_t * mapping, - u8 by_external, - u8 *is_addr_only); - -void snat_add_del_addr_to_fib (ip4_address_t * addr, - u8 p_len, - u32 sw_if_index, - int is_add); - -format_function_t format_snat_user; - -typedef struct { - u32 cached_sw_if_index; - u32 cached_ip4_address; -} snat_runtime_t; - -/** \brief Check if SNAT session is created from static mapping. - @param s SNAT session - @return 1 if SNAT session is created from static mapping otherwise 0 -*/ -#define snat_is_session_static(s) s->flags & SNAT_SESSION_FLAG_STATIC_MAPPING - -/** \brief Check if SNAT session for unknown protocol. - @param s SNAT session - @return 1 if SNAT session for unknown protocol otherwise 0 -*/ -#define snat_is_unk_proto_session(s) s->flags & SNAT_SESSION_FLAG_UNKNOWN_PROTO - -/* - * 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; - -always_inline u32 -ip_proto_to_snat_proto (u8 ip_proto) -{ - u32 snat_proto = ~0; - - snat_proto = (ip_proto == IP_PROTOCOL_UDP) ? SNAT_PROTOCOL_UDP : snat_proto; - snat_proto = (ip_proto == IP_PROTOCOL_TCP) ? SNAT_PROTOCOL_TCP : snat_proto; - snat_proto = (ip_proto == IP_PROTOCOL_ICMP) ? SNAT_PROTOCOL_ICMP : snat_proto; - snat_proto = (ip_proto == IP_PROTOCOL_ICMP6) ? SNAT_PROTOCOL_ICMP : snat_proto; - - return snat_proto; -} - -always_inline u8 -snat_proto_to_ip_proto (snat_protocol_t snat_proto) -{ - u8 ip_proto = ~0; - - ip_proto = (snat_proto == SNAT_PROTOCOL_UDP) ? IP_PROTOCOL_UDP : ip_proto; - ip_proto = (snat_proto == SNAT_PROTOCOL_TCP) ? IP_PROTOCOL_TCP : ip_proto; - ip_proto = (snat_proto == SNAT_PROTOCOL_ICMP) ? IP_PROTOCOL_ICMP : ip_proto; - - return ip_proto; -} - -typedef struct { - u16 src_port, dst_port; -} tcp_udp_header_t; - -u32 icmp_match_in2out_fast(snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, - snat_session_key_t *p_value, - u8 *p_dont_translate, void *d, void *e); -u32 icmp_match_in2out_slow(snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, - snat_session_key_t *p_value, - u8 *p_dont_translate, void *d, void *e); -u32 icmp_match_in2out_det(snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, - snat_session_key_t *p_value, - u8 *p_dont_translate, void *d, void *e); -u32 icmp_match_out2in_fast(snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, - snat_session_key_t *p_value, - u8 *p_dont_translate, void *d, void *e); -u32 icmp_match_out2in_slow(snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, - snat_session_key_t *p_value, - u8 *p_dont_translate, void *d, void *e); -u32 icmp_match_out2in_det(snat_main_t *sm, vlib_node_runtime_t *node, - u32 thread_index, vlib_buffer_t *b0, u8 *p_proto, - snat_session_key_t *p_value, - u8 *p_dont_translate, void *d, void *e); -void increment_v4_address(ip4_address_t * a); -void snat_add_address(snat_main_t *sm, ip4_address_t *addr, u32 vrf_id); -int snat_del_address(snat_main_t *sm, ip4_address_t addr, u8 delete_sm); -int snat_add_static_mapping(ip4_address_t l_addr, ip4_address_t e_addr, - u16 l_port, u16 e_port, u32 vrf_id, int addr_only, - u32 sw_if_index, snat_protocol_t proto, int is_add); -clib_error_t * snat_api_init(vlib_main_t * vm, snat_main_t * sm); -int snat_set_workers (uword * bitmap); -int snat_interface_add_del(u32 sw_if_index, u8 is_inside, int is_del); -int snat_interface_add_del_output_feature(u32 sw_if_index, u8 is_inside, - int is_del); -int snat_add_interface_address(snat_main_t *sm, u32 sw_if_index, int is_del); -uword unformat_snat_protocol(unformat_input_t * input, va_list * args); -u8 * format_snat_protocol(u8 * s, va_list * args); - -static_always_inline u8 -icmp_is_error_message (icmp46_header_t * icmp) -{ - switch(icmp->type) - { - case ICMP4_destination_unreachable: - case ICMP4_time_exceeded: - case ICMP4_parameter_problem: - case ICMP4_source_quench: - case ICMP4_redirect: - case ICMP4_alternate_host_address: - return 1; - } - return 0; -} - -static_always_inline u8 -is_interface_addr(snat_main_t *sm, vlib_node_runtime_t *node, u32 sw_if_index0, - u32 ip4_addr) -{ - snat_runtime_t *rt = (snat_runtime_t *) node->runtime_data; - ip4_address_t * first_int_addr; - - if (PREDICT_FALSE(rt->cached_sw_if_index != sw_if_index0)) - { - first_int_addr = - ip4_interface_first_address (sm->ip4_main, sw_if_index0, - 0 /* just want the address */); - rt->cached_sw_if_index = sw_if_index0; - if (first_int_addr) - rt->cached_ip4_address = first_int_addr->as_u32; - else - rt->cached_ip4_address = 0; - } - - if (PREDICT_FALSE(ip4_addr == rt->cached_ip4_address)) - return 1; - else - return 0; -} - -#endif /* __included_snat_h__ */ diff --git a/src/plugins/snat/snat_all_api_h.h b/src/plugins/snat/snat_all_api_h.h deleted file mode 100644 index 49017700..00000000 --- a/src/plugins/snat/snat_all_api_h.h +++ /dev/null @@ -1,19 +0,0 @@ - -/* - * snat_all_api_h.h - skeleton vpp engine plug-in api #include file - * - * Copyright (c) - * 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 diff --git a/src/plugins/snat/snat_api.c b/src/plugins/snat/snat_api.c deleted file mode 100644 index 227074f9..00000000 --- a/src/plugins/snat/snat_api.c +++ /dev/null @@ -1,1970 +0,0 @@ -/* - * 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. - */ -/** - * @file - * @brief SNAT plugin API implementation - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -/* define message structures */ -#define vl_typedefs -#include -#undef vl_typedefs - -/* define generated endian-swappers */ -#define vl_endianfun -#include -#undef vl_endianfun - -#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) - -#define REPLY_MSG_ID_BASE sm->msg_id_base -#include - -/* Get the API version number */ -#define vl_api_version(n,v) static u32 api_version=(v); -#include -#undef vl_api_version - -/* Macro to finish up custom dump fns */ -#define FINISH \ - vec_add1 (s, 0); \ - vl_print (handle, (char *)s); \ - vec_free (s); \ - return handle; - -static void - vl_api_snat_add_address_range_t_handler - (vl_api_snat_add_address_range_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_snat_add_address_range_reply_t *rmp; - ip4_address_t this_addr; - u32 start_host_order, end_host_order; - u32 vrf_id; - int i, count; - int rv = 0; - u32 *tmp; - - if (mp->is_ip4 != 1) - { - rv = VNET_API_ERROR_UNIMPLEMENTED; - goto send_reply; - } - - if (sm->static_mapping_only) - { - rv = VNET_API_ERROR_FEATURE_DISABLED; - goto send_reply; - } - - tmp = (u32 *) mp->first_ip_address; - start_host_order = clib_host_to_net_u32 (tmp[0]); - tmp = (u32 *) mp->last_ip_address; - end_host_order = clib_host_to_net_u32 (tmp[0]); - - count = (end_host_order - start_host_order) + 1; - - vrf_id = clib_host_to_net_u32 (mp->vrf_id); - - if (count > 1024) - clib_warning ("%U - %U, %d addresses...", - format_ip4_address, mp->first_ip_address, - format_ip4_address, mp->last_ip_address, count); - - memcpy (&this_addr.as_u8, mp->first_ip_address, 4); - - for (i = 0; i < count; i++) - { - if (mp->is_add) - snat_add_address (sm, &this_addr, vrf_id); - else - rv = snat_del_address (sm, this_addr, 0); - - if (rv) - goto send_reply; - - increment_v4_address (&this_addr); - } - -send_reply: - REPLY_MACRO (VL_API_SNAT_ADD_ADDRESS_RANGE_REPLY); -} - -static void *vl_api_snat_add_address_range_t_print - (vl_api_snat_add_address_range_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: snat_add_address_range "); - s = format (s, "%U ", format_ip4_address, mp->first_ip_address); - if (memcmp (mp->first_ip_address, mp->last_ip_address, 4)) - { - s = format (s, " - %U ", format_ip4_address, mp->last_ip_address); - } - FINISH; -} - -static void - send_snat_address_details - (snat_address_t * a, unix_shared_memory_queue_t * q, u32 context) -{ - vl_api_snat_address_details_t *rmp; - snat_main_t *sm = &snat_main; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = ntohs (VL_API_SNAT_ADDRESS_DETAILS + sm->msg_id_base); - rmp->is_ip4 = 1; - clib_memcpy (rmp->ip_address, &(a->addr), 4); - if (a->fib_index != ~0) - { - fib_table_t *fib = fib_table_get (a->fib_index, FIB_PROTOCOL_IP4); - rmp->vrf_id = ntohl (fib->ft_table_id); - } - else - rmp->vrf_id = ~0; - rmp->context = context; - - vl_msg_api_send_shmem (q, (u8 *) & rmp); -} - -static void -vl_api_snat_address_dump_t_handler (vl_api_snat_address_dump_t * mp) -{ - unix_shared_memory_queue_t *q; - snat_main_t *sm = &snat_main; - snat_address_t *a; - - q = vl_api_client_index_to_input_queue (mp->client_index); - if (q == 0) - return; - - /* *INDENT-OFF* */ - vec_foreach (a, sm->addresses) - send_snat_address_details (a, q, mp->context); - /* *INDENT-ON* */ -} - -static void *vl_api_snat_address_dump_t_print - (vl_api_snat_address_dump_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: snat_address_dump "); - - FINISH; -} - -static void - vl_api_snat_interface_add_del_feature_t_handler - (vl_api_snat_interface_add_del_feature_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_snat_interface_add_del_feature_reply_t *rmp; - u8 is_del = mp->is_add == 0; - u32 sw_if_index = ntohl (mp->sw_if_index); - int rv = 0; - - VALIDATE_SW_IF_INDEX (mp); - - rv = snat_interface_add_del (sw_if_index, mp->is_inside, is_del); - - BAD_SW_IF_INDEX_LABEL; - - REPLY_MACRO (VL_API_SNAT_INTERFACE_ADD_DEL_FEATURE_REPLY); -} - -static void *vl_api_snat_interface_add_del_feature_t_print - (vl_api_snat_interface_add_del_feature_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: snat_interface_add_del_feature "); - s = format (s, "sw_if_index %d %s %s", - clib_host_to_net_u32 (mp->sw_if_index), - mp->is_inside ? "in" : "out", mp->is_add ? "" : "del"); - - FINISH; -} - -static void - send_snat_interface_details - (snat_interface_t * i, unix_shared_memory_queue_t * q, u32 context) -{ - vl_api_snat_interface_details_t *rmp; - snat_main_t *sm = &snat_main; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = ntohs (VL_API_SNAT_INTERFACE_DETAILS + sm->msg_id_base); - rmp->sw_if_index = ntohl (i->sw_if_index); - rmp->is_inside = i->is_inside; - rmp->context = context; - - vl_msg_api_send_shmem (q, (u8 *) & rmp); -} - -static void -vl_api_snat_interface_dump_t_handler (vl_api_snat_interface_dump_t * mp) -{ - unix_shared_memory_queue_t *q; - snat_main_t *sm = &snat_main; - snat_interface_t *i; - - q = vl_api_client_index_to_input_queue (mp->client_index); - if (q == 0) - return; - - /* *INDENT-OFF* */ - pool_foreach (i, sm->interfaces, - ({ - send_snat_interface_details(i, q, mp->context); - })); - /* *INDENT-ON* */ -} - -static void *vl_api_snat_interface_dump_t_print - (vl_api_snat_interface_dump_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: snat_interface_dump "); - - FINISH; -} - -static void - vl_api_snat_interface_add_del_output_feature_t_handler - (vl_api_snat_interface_add_del_output_feature_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_snat_interface_add_del_output_feature_reply_t *rmp; - u8 is_del = mp->is_add == 0; - u32 sw_if_index = ntohl (mp->sw_if_index); - int rv = 0; - - VALIDATE_SW_IF_INDEX (mp); - - rv = snat_interface_add_del_output_feature (sw_if_index, mp->is_inside, - is_del); - - BAD_SW_IF_INDEX_LABEL; - - REPLY_MACRO (VL_API_SNAT_INTERFACE_ADD_DEL_OUTPUT_FEATURE_REPLY); -} - -static void *vl_api_snat_interface_add_del_output_feature_t_print - (vl_api_snat_interface_add_del_output_feature_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: snat_interface_add_del_output_feature "); - s = format (s, "sw_if_index %d %s %s", - clib_host_to_net_u32 (mp->sw_if_index), - mp->is_inside ? "in" : "out", mp->is_add ? "" : "del"); - - FINISH; -} - -static void -send_snat_interface_output_feature_details (snat_interface_t * i, - unix_shared_memory_queue_t * q, - u32 context) -{ - vl_api_snat_interface_output_feature_details_t *rmp; - snat_main_t *sm = &snat_main; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = - ntohs (VL_API_SNAT_INTERFACE_OUTPUT_FEATURE_DETAILS + sm->msg_id_base); - rmp->sw_if_index = ntohl (i->sw_if_index); - rmp->context = context; - rmp->is_inside = i->is_inside; - - vl_msg_api_send_shmem (q, (u8 *) & rmp); -} - -static void - vl_api_snat_interface_output_feature_dump_t_handler - (vl_api_snat_interface_output_feature_dump_t * mp) -{ - unix_shared_memory_queue_t *q; - snat_main_t *sm = &snat_main; - snat_interface_t *i; - - q = vl_api_client_index_to_input_queue (mp->client_index); - if (q == 0) - return; - - /* *INDENT-OFF* */ - pool_foreach (i, sm->output_feature_interfaces, - ({ - send_snat_interface_output_feature_details(i, q, mp->context); - })); - /* *INDENT-ON* */ -} - -static void *vl_api_snat_interface_output_feature_dump_t_print - (vl_api_snat_interface_output_feature_dump_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: snat_interface_output_feature_dump "); - - FINISH; -} - -static void - vl_api_snat_add_static_mapping_t_handler - (vl_api_snat_add_static_mapping_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_snat_add_static_mapping_reply_t *rmp; - ip4_address_t local_addr, external_addr; - u16 local_port = 0, external_port = 0; - u32 vrf_id, external_sw_if_index; - int rv = 0; - snat_protocol_t proto; - - if (mp->is_ip4 != 1) - { - rv = VNET_API_ERROR_UNIMPLEMENTED; - goto send_reply; - } - - memcpy (&local_addr.as_u8, mp->local_ip_address, 4); - memcpy (&external_addr.as_u8, mp->external_ip_address, 4); - if (mp->addr_only == 0) - { - local_port = clib_net_to_host_u16 (mp->local_port); - external_port = clib_net_to_host_u16 (mp->external_port); - } - vrf_id = clib_net_to_host_u32 (mp->vrf_id); - external_sw_if_index = clib_net_to_host_u32 (mp->external_sw_if_index); - proto = ip_proto_to_snat_proto (mp->protocol); - - rv = snat_add_static_mapping (local_addr, external_addr, local_port, - external_port, vrf_id, mp->addr_only, - external_sw_if_index, proto, mp->is_add); - -send_reply: - REPLY_MACRO (VL_API_SNAT_ADD_ADDRESS_RANGE_REPLY); -} - -static void *vl_api_snat_add_static_mapping_t_print - (vl_api_snat_add_static_mapping_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: snat_add_static_mapping "); - s = format (s, "protocol %d local_addr %U external_addr %U ", - mp->protocol, - format_ip4_address, mp->local_ip_address, - format_ip4_address, mp->external_ip_address); - - if (mp->addr_only == 0) - s = format (s, "local_port %d external_port %d ", - clib_net_to_host_u16 (mp->local_port), - clib_net_to_host_u16 (mp->external_port)); - - if (mp->vrf_id != ~0) - s = format (s, "vrf %d", clib_net_to_host_u32 (mp->vrf_id)); - - if (mp->external_sw_if_index != ~0) - s = format (s, "external_sw_if_index %d", - clib_net_to_host_u32 (mp->external_sw_if_index)); - FINISH; -} - -static void - send_snat_static_mapping_details - (snat_static_mapping_t * m, unix_shared_memory_queue_t * q, u32 context) -{ - vl_api_snat_static_mapping_details_t *rmp; - snat_main_t *sm = &snat_main; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = - ntohs (VL_API_SNAT_STATIC_MAPPING_DETAILS + sm->msg_id_base); - rmp->is_ip4 = 1; - rmp->addr_only = m->addr_only; - clib_memcpy (rmp->local_ip_address, &(m->local_addr), 4); - clib_memcpy (rmp->external_ip_address, &(m->external_addr), 4); - rmp->local_port = htons (m->local_port); - rmp->external_port = htons (m->external_port); - rmp->external_sw_if_index = ~0; - rmp->vrf_id = htonl (m->vrf_id); - rmp->protocol = snat_proto_to_ip_proto (m->proto); - rmp->context = context; - - vl_msg_api_send_shmem (q, (u8 *) & rmp); -} - -static void - send_snat_static_map_resolve_details - (snat_static_map_resolve_t * m, unix_shared_memory_queue_t * q, u32 context) -{ - vl_api_snat_static_mapping_details_t *rmp; - snat_main_t *sm = &snat_main; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = - ntohs (VL_API_SNAT_STATIC_MAPPING_DETAILS + sm->msg_id_base); - rmp->is_ip4 = 1; - rmp->addr_only = m->addr_only; - clib_memcpy (rmp->local_ip_address, &(m->l_addr), 4); - rmp->local_port = htons (m->l_port); - rmp->external_port = htons (m->e_port); - rmp->external_sw_if_index = htonl (m->sw_if_index); - rmp->vrf_id = htonl (m->vrf_id); - rmp->protocol = snat_proto_to_ip_proto (m->proto); - rmp->context = context; - - vl_msg_api_send_shmem (q, (u8 *) & rmp); -} - -static void - vl_api_snat_static_mapping_dump_t_handler - (vl_api_snat_static_mapping_dump_t * mp) -{ - unix_shared_memory_queue_t *q; - snat_main_t *sm = &snat_main; - snat_static_mapping_t *m; - snat_static_map_resolve_t *rp; - int j; - - q = vl_api_client_index_to_input_queue (mp->client_index); - if (q == 0) - return; - - /* *INDENT-OFF* */ - pool_foreach (m, sm->static_mappings, - ({ - send_snat_static_mapping_details (m, q, mp->context); - })); - /* *INDENT-ON* */ - - for (j = 0; j < vec_len (sm->to_resolve); j++) - { - rp = sm->to_resolve + j; - send_snat_static_map_resolve_details (rp, q, mp->context); - } -} - -static void *vl_api_snat_static_mapping_dump_t_print - (vl_api_snat_static_mapping_dump_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: snat_static_mapping_dump "); - - FINISH; -} - -static void -vl_api_snat_control_ping_t_handler (vl_api_snat_control_ping_t * mp) -{ - vl_api_snat_control_ping_reply_t *rmp; - snat_main_t *sm = &snat_main; - int rv = 0; - - /* *INDENT-OFF* */ - REPLY_MACRO2 (VL_API_SNAT_CONTROL_PING_REPLY, - ({ - rmp->vpe_pid = ntohl (getpid ()); - })); - /* *INDENT-ON* */ -} - -static void *vl_api_snat_control_ping_t_print - (vl_api_snat_control_ping_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: snat_control_ping "); - - FINISH; -} - -static void -vl_api_snat_show_config_t_handler (vl_api_snat_show_config_t * mp) -{ - vl_api_snat_show_config_reply_t *rmp; - snat_main_t *sm = &snat_main; - int rv = 0; - - /* *INDENT-OFF* */ - REPLY_MACRO2 (VL_API_SNAT_SHOW_CONFIG_REPLY, - ({ - rmp->translation_buckets = htonl (sm->translation_buckets); - rmp->translation_memory_size = htonl (sm->translation_memory_size); - rmp->user_buckets = htonl (sm->user_buckets); - rmp->user_memory_size = htonl (sm->user_memory_size); - rmp->max_translations_per_user = htonl (sm->max_translations_per_user); - rmp->outside_vrf_id = htonl (sm->outside_vrf_id); - rmp->inside_vrf_id = htonl (sm->inside_vrf_id); - rmp->static_mapping_only = sm->static_mapping_only; - rmp->static_mapping_connection_tracking = - sm->static_mapping_connection_tracking; - rmp->deterministic = sm->deterministic; - })); - /* *INDENT-ON* */ -} - -static void *vl_api_snat_show_config_t_print - (vl_api_snat_show_config_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: snat_show_config "); - - FINISH; -} - -static void -vl_api_snat_set_workers_t_handler (vl_api_snat_set_workers_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_snat_set_workers_reply_t *rmp; - int rv = 0; - uword *bitmap = 0; - u64 mask = clib_net_to_host_u64 (mp->worker_mask); - - if (sm->num_workers < 2) - { - rv = VNET_API_ERROR_FEATURE_DISABLED; - goto send_reply; - } - - bitmap = clib_bitmap_set_multiple (bitmap, 0, mask, BITS (mask)); - rv = snat_set_workers (bitmap); - clib_bitmap_free (bitmap); - -send_reply: - REPLY_MACRO (VL_API_SNAT_SET_WORKERS_REPLY); -} - -static void *vl_api_snat_set_workers_t_print - (vl_api_snat_set_workers_t * mp, void *handle) -{ - u8 *s; - uword *bitmap = 0; - u8 first = 1; - int i; - u64 mask = clib_net_to_host_u64 (mp->worker_mask); - - s = format (0, "SCRIPT: snat_set_workers "); - bitmap = clib_bitmap_set_multiple (bitmap, 0, mask, BITS (mask)); - /* *INDENT-OFF* */ - clib_bitmap_foreach (i, bitmap, - ({ - if (first) - s = format (s, "%d", i); - else - s = format (s, ",%d", i); - first = 0; - })); - /* *INDENT-ON* */ - clib_bitmap_free (bitmap); - FINISH; -} - -static void - send_snat_worker_details - (u32 worker_index, unix_shared_memory_queue_t * q, u32 context) -{ - vl_api_snat_worker_details_t *rmp; - snat_main_t *sm = &snat_main; - vlib_worker_thread_t *w = - vlib_worker_threads + worker_index + sm->first_worker_index; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = ntohs (VL_API_SNAT_WORKER_DETAILS + sm->msg_id_base); - rmp->context = context; - rmp->worker_index = htonl (worker_index); - rmp->lcore_id = htonl (w->lcore_id); - strncpy ((char *) rmp->name, (char *) w->name, ARRAY_LEN (rmp->name) - 1); - - vl_msg_api_send_shmem (q, (u8 *) & rmp); -} - -static void -vl_api_snat_worker_dump_t_handler (vl_api_snat_worker_dump_t * mp) -{ - unix_shared_memory_queue_t *q; - snat_main_t *sm = &snat_main; - u32 *worker_index; - - q = vl_api_client_index_to_input_queue (mp->client_index); - if (q == 0) - return; - - /* *INDENT-OFF* */ - vec_foreach (worker_index, sm->workers) - send_snat_worker_details(*worker_index, q, mp->context); - /* *INDENT-ON* */ -} - -static void *vl_api_snat_worker_dump_t_print - (vl_api_snat_worker_dump_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: snat_worker_dump "); - - FINISH; -} - -static void - vl_api_snat_add_del_interface_addr_t_handler - (vl_api_snat_add_del_interface_addr_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_snat_add_del_interface_addr_reply_t *rmp; - u8 is_del = mp->is_add == 0; - u32 sw_if_index = ntohl (mp->sw_if_index); - int rv = 0; - - VALIDATE_SW_IF_INDEX (mp); - - rv = snat_add_interface_address (sm, sw_if_index, is_del); - - BAD_SW_IF_INDEX_LABEL; - - REPLY_MACRO (VL_API_SNAT_ADD_DEL_INTERFACE_ADDR_REPLY); -} - -static void *vl_api_snat_add_del_interface_addr_t_print - (vl_api_snat_add_del_interface_addr_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: snat_add_del_interface_addr "); - s = format (s, "sw_if_index %d %s", - clib_host_to_net_u32 (mp->sw_if_index), - mp->is_add ? "" : "del"); - - FINISH; -} - -static void - send_snat_interface_addr_details - (u32 sw_if_index, unix_shared_memory_queue_t * q, u32 context) -{ - vl_api_snat_interface_addr_details_t *rmp; - snat_main_t *sm = &snat_main; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = - ntohs (VL_API_SNAT_INTERFACE_ADDR_DETAILS + sm->msg_id_base); - rmp->sw_if_index = ntohl (sw_if_index); - rmp->context = context; - - vl_msg_api_send_shmem (q, (u8 *) & rmp); -} - -static void - vl_api_snat_interface_addr_dump_t_handler - (vl_api_snat_interface_addr_dump_t * mp) -{ - unix_shared_memory_queue_t *q; - snat_main_t *sm = &snat_main; - u32 *i; - - q = vl_api_client_index_to_input_queue (mp->client_index); - if (q == 0) - return; - - /* *INDENT-OFF* */ - vec_foreach (i, sm->auto_add_sw_if_indices) - send_snat_interface_addr_details(*i, q, mp->context); - /* *INDENT-ON* */ -} - -static void *vl_api_snat_interface_addr_dump_t_print - (vl_api_snat_interface_addr_dump_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: snat_interface_addr_dump "); - - FINISH; -} - -static void - vl_api_snat_ipfix_enable_disable_t_handler - (vl_api_snat_ipfix_enable_disable_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_snat_ipfix_enable_disable_reply_t *rmp; - int rv = 0; - - rv = snat_ipfix_logging_enable_disable (mp->enable, - clib_host_to_net_u32 - (mp->domain_id), - clib_host_to_net_u16 - (mp->src_port)); - - REPLY_MACRO (VL_API_SNAT_IPFIX_ENABLE_DISABLE_REPLY); -} - -static void *vl_api_snat_ipfix_enable_disable_t_print - (vl_api_snat_ipfix_enable_disable_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: snat_ipfix_enable_disable "); - if (mp->domain_id) - s = format (s, "domain %d ", clib_net_to_host_u32 (mp->domain_id)); - if (mp->src_port) - s = format (s, "src_port %d ", clib_net_to_host_u16 (mp->src_port)); - if (!mp->enable) - s = format (s, "disable "); - - FINISH; -} - -static void - send_snat_user_details - (snat_user_t * u, unix_shared_memory_queue_t * q, u32 context) -{ - vl_api_snat_user_details_t *rmp; - snat_main_t *sm = &snat_main; - fib_table_t *fib = fib_table_get (u->fib_index, FIB_PROTOCOL_IP4); - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = ntohs (VL_API_SNAT_USER_DETAILS + sm->msg_id_base); - - rmp->vrf_id = ntohl (fib->ft_table_id); - - rmp->is_ip4 = 1; - clib_memcpy (rmp->ip_address, &(u->addr), 4); - rmp->nsessions = ntohl (u->nsessions); - rmp->nstaticsessions = ntohl (u->nstaticsessions); - rmp->context = context; - - vl_msg_api_send_shmem (q, (u8 *) & rmp); -} - -static void -vl_api_snat_user_dump_t_handler (vl_api_snat_user_dump_t * mp) -{ - unix_shared_memory_queue_t *q; - snat_main_t *sm = &snat_main; - snat_main_per_thread_data_t *tsm; - snat_user_t *u; - - q = vl_api_client_index_to_input_queue (mp->client_index); - if (q == 0) - return; - - /* *INDENT-OFF* */ - vec_foreach (tsm, sm->per_thread_data) - vec_foreach (u, tsm->users) - send_snat_user_details (u, q, mp->context); - /* *INDENT-ON* */ -} - -static void *vl_api_snat_user_dump_t_print - (vl_api_snat_user_dump_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: snat_user_dump "); - - FINISH; -} - -static void - send_snat_user_session_details - (snat_session_t * s, unix_shared_memory_queue_t * q, u32 context) -{ - vl_api_snat_user_session_details_t *rmp; - snat_main_t *sm = &snat_main; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = - ntohs (VL_API_SNAT_USER_SESSION_DETAILS + sm->msg_id_base); - rmp->is_ip4 = 1; - clib_memcpy (rmp->outside_ip_address, (&s->out2in.addr), 4); - clib_memcpy (rmp->inside_ip_address, (&s->in2out.addr), 4); - rmp->is_static = s->flags & SNAT_SESSION_FLAG_STATIC_MAPPING ? 1 : 0; - rmp->last_heard = clib_host_to_net_u64 ((u64) s->last_heard); - rmp->total_bytes = clib_host_to_net_u64 (s->total_bytes); - rmp->total_pkts = ntohl (s->total_pkts); - rmp->context = context; - if (snat_is_unk_proto_session (s)) - { - rmp->outside_port = 0; - rmp->inside_port = 0; - rmp->protocol = ntohs (s->in2out.port); - } - else - { - rmp->outside_port = s->out2in.port; - rmp->inside_port = s->in2out.port; - rmp->protocol = ntohs (snat_proto_to_ip_proto (s->in2out.protocol)); - } - - vl_msg_api_send_shmem (q, (u8 *) & rmp); -} - -static void - vl_api_snat_user_session_dump_t_handler - (vl_api_snat_user_session_dump_t * mp) -{ - unix_shared_memory_queue_t *q; - snat_main_t *sm = &snat_main; - snat_main_per_thread_data_t *tsm; - snat_session_t *s; - clib_bihash_kv_8_8_t key, value; - snat_user_key_t ukey; - snat_user_t *u; - u32 session_index, head_index, elt_index; - dlist_elt_t *head, *elt; - - q = vl_api_client_index_to_input_queue (mp->client_index); - if (q == 0) - return; - if (!mp->is_ip4) - return; - - clib_memcpy (&ukey.addr, mp->ip_address, 4); - ukey.fib_index = fib_table_find (FIB_PROTOCOL_IP4, ntohl (mp->vrf_id)); - key.key = ukey.as_u64; - if (!clib_bihash_search_8_8 (&sm->worker_by_in, &key, &value)) - tsm = vec_elt_at_index (sm->per_thread_data, value.value); - else - tsm = vec_elt_at_index (sm->per_thread_data, sm->num_workers); - if (clib_bihash_search_8_8 (&sm->user_hash, &key, &value)) - return; - u = pool_elt_at_index (tsm->users, value.value); - if (!u->nsessions && !u->nstaticsessions) - return; - - head_index = u->sessions_per_user_list_head_index; - head = pool_elt_at_index (tsm->list_pool, head_index); - elt_index = head->next; - elt = pool_elt_at_index (tsm->list_pool, elt_index); - session_index = elt->value; - while (session_index != ~0) - { - s = pool_elt_at_index (tsm->sessions, session_index); - - send_snat_user_session_details (s, q, mp->context); - - elt_index = elt->next; - elt = pool_elt_at_index (tsm->list_pool, elt_index); - session_index = elt->value; - } -} - -static void *vl_api_snat_user_session_dump_t_print - (vl_api_snat_user_session_dump_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: snat_user_session_dump "); - s = format (s, "ip_address %U vrf_id %d\n", - format_ip4_address, mp->ip_address, - clib_net_to_host_u32 (mp->vrf_id)); - - FINISH; -} - -/****************************/ -/*** detrministic NAT/CGN ***/ -/****************************/ - -static void -vl_api_snat_add_det_map_t_handler (vl_api_snat_add_det_map_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_snat_add_det_map_reply_t *rmp; - int rv = 0; - ip4_address_t in_addr, out_addr; - - clib_memcpy (&in_addr, mp->in_addr, 4); - clib_memcpy (&out_addr, mp->out_addr, 4); - rv = snat_det_add_map (sm, &in_addr, mp->in_plen, &out_addr, - mp->out_plen, mp->is_add); - - REPLY_MACRO (VL_API_SNAT_ADD_DET_MAP_REPLY); -} - -static void *vl_api_snat_add_det_map_t_print - (vl_api_snat_add_det_map_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: snat_add_det_map "); - s = format (s, "inside address %U/%d outside address %U/%d\n", - format_ip4_address, mp->in_addr, mp->in_plen, - format_ip4_address, mp->out_addr, mp->out_plen); - - FINISH; -} - -static void -vl_api_snat_det_forward_t_handler (vl_api_snat_det_forward_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_snat_det_forward_reply_t *rmp; - int rv = 0; - u16 lo_port = 0, hi_port = 0; - snat_det_map_t *dm; - ip4_address_t in_addr, out_addr; - - out_addr.as_u32 = 0; - clib_memcpy (&in_addr, mp->in_addr, 4); - dm = snat_det_map_by_user (sm, &in_addr); - if (!dm) - { - rv = VNET_API_ERROR_NO_SUCH_ENTRY; - goto send_reply; - } - - snat_det_forward (dm, &in_addr, &out_addr, &lo_port); - hi_port = lo_port + dm->ports_per_host - 1; - -send_reply: - /* *INDENT-OFF* */ - REPLY_MACRO2 (VL_API_SNAT_DET_FORWARD_REPLY, - ({ - rmp->out_port_lo = ntohs (lo_port); - rmp->out_port_hi = ntohs (hi_port); - rmp->is_ip4 = 1; - memset (rmp->out_addr, 0, 16); - clib_memcpy (rmp->out_addr, &out_addr, 4); - })) - /* *INDENT-ON* */ -} - -static void *vl_api_snat_det_forward_t_print - (vl_api_snat_det_forward_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: smat_det_forward_t"); - s = format (s, "inside ip address %U\n", format_ip4_address, mp->in_addr); - - FINISH; -} - -static void -vl_api_snat_det_reverse_t_handler (vl_api_snat_det_reverse_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_snat_det_reverse_reply_t *rmp; - int rv = 0; - ip4_address_t out_addr, in_addr; - snat_det_map_t *dm; - - in_addr.as_u32 = 0; - clib_memcpy (&out_addr, mp->out_addr, 4); - dm = snat_det_map_by_out (sm, &out_addr); - if (!dm) - { - rv = VNET_API_ERROR_NO_SUCH_ENTRY; - goto send_reply; - } - - snat_det_reverse (dm, &out_addr, htons (mp->out_port), &in_addr); - -send_reply: - /* *INDENT-OFF* */ - REPLY_MACRO2 (VL_API_SNAT_DET_REVERSE_REPLY, - ({ - rmp->is_ip4 = 1; - memset (rmp->in_addr, 0, 16); - clib_memcpy (rmp->in_addr, &in_addr, 4); - })) - /* *INDENT-ON* */ -} - -static void *vl_api_snat_det_reverse_t_print - (vl_api_snat_det_reverse_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: smat_det_reverse_t"); - s = format (s, "outside ip address %U outside port %d", - format_ip4_address, mp->out_addr, ntohs (mp->out_port)); - - FINISH; -} - -static void - sent_snat_det_map_details - (snat_det_map_t * m, unix_shared_memory_queue_t * q, u32 context) -{ - vl_api_snat_det_map_details_t *rmp; - snat_main_t *sm = &snat_main; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = ntohs (VL_API_SNAT_DET_MAP_DETAILS + sm->msg_id_base); - rmp->is_ip4 = 1; - clib_memcpy (rmp->in_addr, &m->in_addr, 4); - rmp->in_plen = m->in_plen; - clib_memcpy (rmp->out_addr, &m->out_addr, 4); - rmp->out_plen = m->out_plen; - rmp->sharing_ratio = htonl (m->sharing_ratio); - rmp->ports_per_host = htons (m->ports_per_host); - rmp->ses_num = htonl (m->ses_num); - rmp->context = context; - - vl_msg_api_send_shmem (q, (u8 *) & rmp); -} - -static void -vl_api_snat_det_map_dump_t_handler (vl_api_snat_det_map_dump_t * mp) -{ - unix_shared_memory_queue_t *q; - snat_main_t *sm = &snat_main; - snat_det_map_t *m; - - q = vl_api_client_index_to_input_queue (mp->client_index); - if (q == 0) - return; - - /* *INDENT-OFF* */ - vec_foreach(m, sm->det_maps) - sent_snat_det_map_details(m, q, mp->context); - /* *INDENT-ON* */ -} - -static void *vl_api_snat_det_map_dump_t_print - (vl_api_snat_det_map_dump_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: snat_det_map_dump "); - - FINISH; -} - -static void -vl_api_snat_det_set_timeouts_t_handler (vl_api_snat_det_set_timeouts_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_snat_det_set_timeouts_reply_t *rmp; - int rv = 0; - - sm->udp_timeout = ntohl (mp->udp); - sm->tcp_established_timeout = ntohl (mp->tcp_established); - sm->tcp_transitory_timeout = ntohl (mp->tcp_transitory); - sm->icmp_timeout = ntohl (mp->icmp); - - REPLY_MACRO (VL_API_SNAT_DET_SET_TIMEOUTS_REPLY); -} - -static void *vl_api_snat_det_set_timeouts_t_print - (vl_api_snat_det_set_timeouts_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: snat_det_set_timeouts "); - s = format (s, "udp %d tcp_established %d tcp_transitory %d icmp %d\n", - ntohl (mp->udp), - ntohl (mp->tcp_established), - ntohl (mp->tcp_transitory), ntohl (mp->icmp)); - - FINISH; -} - -static void -vl_api_snat_det_get_timeouts_t_handler (vl_api_snat_det_get_timeouts_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_snat_det_get_timeouts_reply_t *rmp; - int rv = 0; - - /* *INDENT-OFF* */ - REPLY_MACRO2 (VL_API_SNAT_DET_GET_TIMEOUTS_REPLY, - ({ - rmp->udp = htonl (sm->udp_timeout); - rmp->tcp_established = htonl (sm->tcp_established_timeout); - rmp->tcp_transitory = htonl (sm->tcp_transitory_timeout); - rmp->icmp = htonl (sm->icmp_timeout); - })) - /* *INDENT-ON* */ -} - -static void *vl_api_snat_det_get_timeouts_t_print - (vl_api_snat_det_get_timeouts_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: snat_det_get_timeouts"); - - FINISH; -} - -static void - vl_api_snat_det_close_session_out_t_handler - (vl_api_snat_det_close_session_out_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_snat_det_close_session_out_reply_t *rmp; - ip4_address_t out_addr, ext_addr, in_addr; - snat_det_out_key_t key; - snat_det_map_t *dm; - snat_det_session_t *ses; - int rv = 0; - - clib_memcpy (&out_addr, mp->out_addr, 4); - clib_memcpy (&ext_addr, mp->ext_addr, 4); - - dm = snat_det_map_by_out (sm, &out_addr); - if (!dm) - { - rv = VNET_API_ERROR_NO_SUCH_ENTRY; - goto send_reply; - } - snat_det_reverse (dm, &ext_addr, ntohs (mp->out_port), &in_addr); - key.ext_host_addr = ext_addr; - key.ext_host_port = mp->ext_port; - key.out_port = mp->out_port; - ses = snat_det_get_ses_by_out (dm, &in_addr, key.as_u64); - if (!ses) - { - rv = VNET_API_ERROR_NO_SUCH_ENTRY; - goto send_reply; - } - snat_det_ses_close (dm, ses); - -send_reply: - REPLY_MACRO (VL_API_SNAT_DET_CLOSE_SESSION_OUT_REPLY); -} - -static void *vl_api_snat_det_close_session_out_t_print - (vl_api_snat_det_close_session_out_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: snat_det_close_session_out "); - s = format (s, "out_addr %U out_port %d " - "ext_addr %U ext_port %d\n", - format_ip4_address, mp->out_addr, ntohs (mp->out_port), - format_ip4_address, mp->ext_addr, ntohs (mp->ext_port)); - - FINISH; -} - -static void - vl_api_snat_det_close_session_in_t_handler - (vl_api_snat_det_close_session_in_t * mp) -{ - snat_main_t *sm = &snat_main; - vl_api_snat_det_close_session_in_reply_t *rmp; - ip4_address_t in_addr, ext_addr; - snat_det_out_key_t key; - snat_det_map_t *dm; - snat_det_session_t *ses; - int rv = 0; - - clib_memcpy (&in_addr, mp->in_addr, 4); - clib_memcpy (&ext_addr, mp->ext_addr, 4); - - dm = snat_det_map_by_user (sm, &in_addr); - if (!dm) - { - rv = VNET_API_ERROR_NO_SUCH_ENTRY; - goto send_reply; - } - key.ext_host_addr = ext_addr; - key.ext_host_port = mp->ext_port; - ses = snat_det_find_ses_by_in (dm, &in_addr, mp->in_port, key); - if (!ses) - { - rv = VNET_API_ERROR_NO_SUCH_ENTRY; - goto send_reply; - } - snat_det_ses_close (dm, ses); - -send_reply: - REPLY_MACRO (VL_API_SNAT_DET_CLOSE_SESSION_OUT_REPLY); -} - -static void *vl_api_snat_det_close_session_in_t_print - (vl_api_snat_det_close_session_in_t * mp, void *handle) -{ - u8 *s; - s = format (0, "SCRIPT: snat_det_close_session_in "); - s = format (s, "in_addr %U in_port %d " - "ext_addr %U ext_port %d\n", - format_ip4_address, mp->in_addr, ntohs (mp->in_port), - format_ip4_address, mp->ext_addr, ntohs (mp->ext_port)); - - FINISH; -} - -static void - send_snat_det_session_details - (snat_det_session_t * s, unix_shared_memory_queue_t * q, u32 context) -{ - vl_api_snat_det_session_details_t *rmp; - snat_main_t *sm = &snat_main; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = ntohs (VL_API_SNAT_DET_SESSION_DETAILS + sm->msg_id_base); - rmp->is_ip4 = 1; - rmp->in_port = s->in_port; - clib_memcpy (rmp->ext_addr, &s->out.ext_host_addr, 4); - rmp->ext_port = s->out.ext_host_port; - rmp->out_port = s->out.out_port; - rmp->state = s->state; - rmp->expire = ntohl (s->expire); - rmp->context = context; - - vl_msg_api_send_shmem (q, (u8 *) & rmp); -} - -static void -vl_api_snat_det_session_dump_t_handler (vl_api_snat_det_session_dump_t * mp) -{ - unix_shared_memory_queue_t *q; - snat_main_t *sm = &snat_main; - ip4_address_t user_addr; - snat_det_map_t *dm; - snat_det_session_t *s, empty_ses; - u16 i; - - q = vl_api_client_index_to_input_queue (mp->client_index); - if (q == 0) - return; - if (!mp->is_ip4) - return; - - memset (&empty_ses, 0, sizeof (empty_ses)); - clib_memcpy (&user_addr, mp->user_addr, 4); - dm = snat_det_map_by_user (sm, &user_addr); - if (!dm) - return; - - s = dm->sessions + snat_det_user_ses_offset (&user_addr, dm->in_plen); - for (i = 0; i < SNAT_DET_SES_PER_USER; i++) - { - if (s->out.as_u64) - send_snat_det_session_details (s, q, mp->context); - s++; - } -} - -static void *vl_api_snat_det_session_dump_t_print - (vl_api_snat_det_session_dump_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: snat_det_session_dump "); - s = format (s, "user_addr %U\n", format_ip4_address, mp->user_addr); - - FINISH; -} - -/*************/ -/*** NAT64 ***/ -/*************/ - -static void - vl_api_nat64_add_del_pool_addr_range_t_handler - (vl_api_nat64_add_del_pool_addr_range_t * mp) -{ - vl_api_nat64_add_del_pool_addr_range_reply_t *rmp; - snat_main_t *sm = &snat_main; - nat64_main_t *nm = &nat64_main; - int rv = 0; - ip4_address_t this_addr; - u32 start_host_order, end_host_order; - u32 vrf_id; - int i, count; - u32 *tmp; - - if (nm->is_disabled) - { - rv = VNET_API_ERROR_FEATURE_DISABLED; - goto send_reply; - } - - tmp = (u32 *) mp->start_addr; - start_host_order = clib_host_to_net_u32 (tmp[0]); - tmp = (u32 *) mp->end_addr; - end_host_order = clib_host_to_net_u32 (tmp[0]); - - count = (end_host_order - start_host_order) + 1; - - vrf_id = clib_host_to_net_u32 (mp->vrf_id); - - memcpy (&this_addr.as_u8, mp->start_addr, 4); - - for (i = 0; i < count; i++) - { - if ((rv = nat64_add_del_pool_addr (&this_addr, vrf_id, mp->is_add))) - goto send_reply; - - increment_v4_address (&this_addr); - } - -send_reply: - REPLY_MACRO (VL_API_NAT64_ADD_DEL_POOL_ADDR_RANGE_REPLY); -} - -static void *vl_api_nat64_add_del_pool_addr_range_t_print - (vl_api_nat64_add_del_pool_addr_range_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: nat64_add_del_pool_addr_range "); - s = format (s, "%U - %U vrf_id %u %s\n", - format_ip4_address, mp->start_addr, - format_ip4_address, mp->end_addr, - ntohl (mp->vrf_id), mp->is_add ? "" : "del"); - - FINISH; -} - -typedef struct nat64_api_walk_ctx_t_ -{ - unix_shared_memory_queue_t *q; - u32 context; -} nat64_api_walk_ctx_t; - -static int -nat64_api_pool_walk (snat_address_t * a, void *arg) -{ - vl_api_nat64_pool_addr_details_t *rmp; - snat_main_t *sm = &snat_main; - nat64_api_walk_ctx_t *ctx = arg; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = ntohs (VL_API_NAT64_POOL_ADDR_DETAILS + sm->msg_id_base); - clib_memcpy (rmp->address, &(a->addr), 4); - if (a->fib_index != ~0) - { - fib_table_t *fib = fib_table_get (a->fib_index, FIB_PROTOCOL_IP6); - if (!fib) - return -1; - rmp->vrf_id = ntohl (fib->ft_table_id); - } - else - rmp->vrf_id = ~0; - rmp->context = ctx->context; - - vl_msg_api_send_shmem (ctx->q, (u8 *) & rmp); - - return 0; -} - -static void -vl_api_nat64_pool_addr_dump_t_handler (vl_api_nat64_pool_addr_dump_t * mp) -{ - unix_shared_memory_queue_t *q; - nat64_main_t *nm = &nat64_main; - - if (nm->is_disabled) - return; - - q = vl_api_client_index_to_input_queue (mp->client_index); - if (q == 0) - return; - - nat64_api_walk_ctx_t ctx = { - .q = q, - .context = mp->context, - }; - - nat64_pool_addr_walk (nat64_api_pool_walk, &ctx); -} - -static void * -vl_api_nat64_pool_addr_dump_t_print (vl_api_nat64_pool_addr_dump_t * mp, - void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: nat64_pool_addr_dump\n"); - - FINISH; -} - -static void -vl_api_nat64_add_del_interface_t_handler (vl_api_nat64_add_del_interface_t * - mp) -{ - snat_main_t *sm = &snat_main; - nat64_main_t *nm = &nat64_main; - vl_api_nat64_add_del_interface_reply_t *rmp; - int rv = 0; - - if (nm->is_disabled) - { - rv = VNET_API_ERROR_FEATURE_DISABLED; - goto send_reply; - } - - VALIDATE_SW_IF_INDEX (mp); - - rv = - nat64_add_del_interface (ntohl (mp->sw_if_index), mp->is_inside, - mp->is_add); - - BAD_SW_IF_INDEX_LABEL; - -send_reply: - REPLY_MACRO (VL_API_NAT64_ADD_DEL_INTERFACE_REPLY); -} - -static void * -vl_api_nat64_add_del_interface_t_print (vl_api_nat64_add_del_interface_t * mp, - void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: nat64_add_del_interface "); - s = format (s, "sw_if_index %d %s %s", - clib_host_to_net_u32 (mp->sw_if_index), - mp->is_inside ? "in" : "out", mp->is_add ? "" : "del"); - - FINISH; -} - -static int -nat64_api_interface_walk (snat_interface_t * i, void *arg) -{ - vl_api_nat64_interface_details_t *rmp; - snat_main_t *sm = &snat_main; - nat64_api_walk_ctx_t *ctx = arg; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = ntohs (VL_API_NAT64_INTERFACE_DETAILS + sm->msg_id_base); - rmp->sw_if_index = ntohl (i->sw_if_index); - rmp->is_inside = i->is_inside; - rmp->context = ctx->context; - - vl_msg_api_send_shmem (ctx->q, (u8 *) & rmp); - - return 0; -} - -static void -vl_api_nat64_interface_dump_t_handler (vl_api_nat64_interface_dump_t * mp) -{ - unix_shared_memory_queue_t *q; - nat64_main_t *nm = &nat64_main; - - if (nm->is_disabled) - return; - - q = vl_api_client_index_to_input_queue (mp->client_index); - if (q == 0) - return; - - nat64_api_walk_ctx_t ctx = { - .q = q, - .context = mp->context, - }; - - nat64_interfaces_walk (nat64_api_interface_walk, &ctx); -} - -static void * -vl_api_nat64_interface_dump_t_print (vl_api_nat64_interface_dump_t * mp, - void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: snat_interface_dump "); - - FINISH; -} - -static void - vl_api_nat64_add_del_static_bib_t_handler - (vl_api_nat64_add_del_static_bib_t * mp) -{ - snat_main_t *sm = &snat_main; - nat64_main_t *nm = &nat64_main; - vl_api_nat64_add_del_static_bib_reply_t *rmp; - ip6_address_t in_addr; - ip4_address_t out_addr; - int rv = 0; - - if (nm->is_disabled) - { - rv = VNET_API_ERROR_FEATURE_DISABLED; - goto send_reply; - } - - memcpy (&in_addr.as_u8, mp->i_addr, 16); - memcpy (&out_addr.as_u8, mp->o_addr, 4); - - rv = - nat64_add_del_static_bib_entry (&in_addr, &out_addr, - clib_net_to_host_u16 (mp->i_port), - clib_net_to_host_u16 (mp->o_port), - mp->proto, - clib_net_to_host_u32 (mp->vrf_id), - mp->is_add); - -send_reply: - REPLY_MACRO (VL_API_NAT64_ADD_DEL_STATIC_BIB_REPLY); -} - -static void *vl_api_nat64_add_del_static_bib_t_print - (vl_api_nat64_add_del_static_bib_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: nat64_add_del_static_bib "); - s = format (s, "protocol %d i_addr %U o_addr %U ", - mp->proto, - format_ip6_address, mp->i_addr, format_ip4_address, mp->o_addr); - - if (mp->vrf_id != ~0) - s = format (s, "vrf %d", clib_net_to_host_u32 (mp->vrf_id)); - - FINISH; -} - -static int -nat64_api_bib_walk (nat64_db_bib_entry_t * bibe, void *arg) -{ - vl_api_nat64_bib_details_t *rmp; - snat_main_t *sm = &snat_main; - nat64_api_walk_ctx_t *ctx = arg; - fib_table_t *fib; - - fib = fib_table_get (bibe->fib_index, FIB_PROTOCOL_IP6); - if (!fib) - return -1; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = ntohs (VL_API_NAT64_BIB_DETAILS + sm->msg_id_base); - rmp->context = ctx->context; - clib_memcpy (rmp->i_addr, &(bibe->in_addr), 16); - clib_memcpy (rmp->o_addr, &(bibe->out_addr), 4); - rmp->i_port = bibe->in_port; - rmp->o_port = bibe->out_port; - rmp->vrf_id = ntohl (fib->ft_table_id); - rmp->proto = bibe->proto; - rmp->is_static = bibe->is_static; - rmp->ses_num = ntohl (bibe->ses_num); - - vl_msg_api_send_shmem (ctx->q, (u8 *) & rmp); - - return 0; -} - -static void -vl_api_nat64_bib_dump_t_handler (vl_api_nat64_bib_dump_t * mp) -{ - unix_shared_memory_queue_t *q; - nat64_main_t *nm = &nat64_main; - - if (nm->is_disabled) - return; - - q = vl_api_client_index_to_input_queue (mp->client_index); - if (q == 0) - return; - - nat64_api_walk_ctx_t ctx = { - .q = q, - .context = mp->context, - }; - - nat64_db_bib_walk (&nm->db, mp->proto, nat64_api_bib_walk, &ctx); -} - -static void * -vl_api_nat64_bib_dump_t_print (vl_api_nat64_bib_dump_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: snat_bib_dump protocol %d", mp->proto); - - FINISH; -} - -static void -vl_api_nat64_set_timeouts_t_handler (vl_api_nat64_set_timeouts_t * mp) -{ - snat_main_t *sm = &snat_main; - nat64_main_t *nm = &nat64_main; - vl_api_nat64_set_timeouts_reply_t *rmp; - int rv = 0; - - if (nm->is_disabled) - { - rv = VNET_API_ERROR_FEATURE_DISABLED; - goto send_reply; - } - - rv = nat64_set_icmp_timeout (ntohl (mp->icmp)); - if (rv) - goto send_reply; - rv = nat64_set_udp_timeout (ntohl (mp->udp)); - if (rv) - goto send_reply; - rv = - nat64_set_tcp_timeouts (ntohl (mp->tcp_trans), ntohl (mp->tcp_est), - ntohl (mp->tcp_incoming_syn)); - -send_reply: - REPLY_MACRO (VL_API_NAT64_SET_TIMEOUTS_REPLY); -} - -static void *vl_api_nat64_set_timeouts_t_print - (vl_api_nat64_set_timeouts_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: nat64_set_timeouts "); - s = - format (s, - "udp %d icmp %d, tcp_trans %d, tcp_est %d, tcp_incoming_syn %d\n", - ntohl (mp->udp), ntohl (mp->icmp), ntohl (mp->tcp_trans), - ntohl (mp->tcp_est), ntohl (mp->tcp_incoming_syn)); - - FINISH; -} - -static void -vl_api_nat64_get_timeouts_t_handler (vl_api_nat64_get_timeouts_t * mp) -{ - snat_main_t *sm = &snat_main; - nat64_main_t *nm = &nat64_main; - vl_api_nat64_get_timeouts_reply_t *rmp; - int rv = 0; - - if (nm->is_disabled) - return; - - /* *INDENT-OFF* */ - REPLY_MACRO2 (VL_API_NAT64_GET_TIMEOUTS_REPLY, - ({ - rmp->udp = htonl (nat64_get_udp_timeout()); - rmp->icmp = htonl (nat64_get_icmp_timeout()); - rmp->tcp_trans = htonl (nat64_get_tcp_trans_timeout()); - rmp->tcp_est = htonl (nat64_get_tcp_est_timeout()); - rmp->tcp_incoming_syn = htonl (nat64_get_tcp_incoming_syn_timeout()); - })) - /* *INDENT-ON* */ -} - -static void *vl_api_nat64_get_timeouts_t_print - (vl_api_nat64_get_timeouts_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: nat64_get_timeouts"); - - FINISH; -} - -static int -nat64_api_st_walk (nat64_db_st_entry_t * ste, void *arg) -{ - vl_api_nat64_st_details_t *rmp; - snat_main_t *sm = &snat_main; - nat64_api_walk_ctx_t *ctx = arg; - nat64_main_t *nm = &nat64_main; - nat64_db_bib_entry_t *bibe; - fib_table_t *fib; - - bibe = nat64_db_bib_entry_by_index (&nm->db, ste->proto, ste->bibe_index); - if (!bibe) - return -1; - - fib = fib_table_get (bibe->fib_index, FIB_PROTOCOL_IP6); - if (!fib) - return -1; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = ntohs (VL_API_NAT64_ST_DETAILS + sm->msg_id_base); - rmp->context = ctx->context; - clib_memcpy (rmp->il_addr, &(bibe->in_addr), 16); - clib_memcpy (rmp->ol_addr, &(bibe->out_addr), 4); - rmp->il_port = bibe->in_port; - rmp->ol_port = bibe->out_port; - clib_memcpy (rmp->ir_addr, &(ste->in_r_addr), 16); - clib_memcpy (rmp->or_addr, &(ste->out_r_addr), 4); - rmp->il_port = ste->r_port; - rmp->vrf_id = ntohl (fib->ft_table_id); - rmp->proto = ste->proto; - - vl_msg_api_send_shmem (ctx->q, (u8 *) & rmp); - - return 0; -} - -static void -vl_api_nat64_st_dump_t_handler (vl_api_nat64_st_dump_t * mp) -{ - unix_shared_memory_queue_t *q; - nat64_main_t *nm = &nat64_main; - - if (nm->is_disabled) - return; - - q = vl_api_client_index_to_input_queue (mp->client_index); - if (q == 0) - return; - - nat64_api_walk_ctx_t ctx = { - .q = q, - .context = mp->context, - }; - - nat64_db_st_walk (&nm->db, mp->proto, nat64_api_st_walk, &ctx); -} - -static void * -vl_api_nat64_st_dump_t_print (vl_api_nat64_st_dump_t * mp, void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: snat_st_dump protocol %d", mp->proto); - - FINISH; -} - -static void -vl_api_nat64_add_del_prefix_t_handler (vl_api_nat64_add_del_prefix_t * mp) -{ - vl_api_nat64_add_del_prefix_reply_t *rmp; - snat_main_t *sm = &snat_main; - nat64_main_t *nm = &nat64_main; - ip6_address_t prefix; - int rv = 0; - - if (nm->is_disabled) - { - rv = VNET_API_ERROR_FEATURE_DISABLED; - goto send_reply; - } - - memcpy (&prefix.as_u8, mp->prefix, 16); - - rv = - nat64_add_del_prefix (&prefix, mp->prefix_len, - clib_net_to_host_u32 (mp->vrf_id), mp->is_add); -send_reply: - REPLY_MACRO (VL_API_NAT64_ADD_DEL_PREFIX_REPLY); -} - -static void * -vl_api_nat64_add_del_prefix_t_print (vl_api_nat64_add_del_prefix_t * mp, - void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: nat64_add_del_prefix %U/%u vrf_id %u %s\n", - format_ip6_address, mp->prefix, mp->prefix_len, - ntohl (mp->vrf_id), mp->is_add ? "" : "del"); - - FINISH; -} - -static int -nat64_api_prefix_walk (nat64_prefix_t * p, void *arg) -{ - vl_api_nat64_prefix_details_t *rmp; - snat_main_t *sm = &snat_main; - nat64_api_walk_ctx_t *ctx = arg; - - rmp = vl_msg_api_alloc (sizeof (*rmp)); - memset (rmp, 0, sizeof (*rmp)); - rmp->_vl_msg_id = ntohs (VL_API_NAT64_PREFIX_DETAILS + sm->msg_id_base); - clib_memcpy (rmp->prefix, &(p->prefix), 16); - rmp->prefix_len = p->plen; - rmp->vrf_id = ntohl (p->vrf_id); - rmp->context = ctx->context; - - vl_msg_api_send_shmem (ctx->q, (u8 *) & rmp); - - return 0; -} - -static void -vl_api_nat64_prefix_dump_t_handler (vl_api_nat64_prefix_dump_t * mp) -{ - unix_shared_memory_queue_t *q; - nat64_main_t *nm = &nat64_main; - - if (nm->is_disabled) - return; - - q = vl_api_client_index_to_input_queue (mp->client_index); - if (q == 0) - return; - - nat64_api_walk_ctx_t ctx = { - .q = q, - .context = mp->context, - }; - - nat64_prefix_walk (nat64_api_prefix_walk, &ctx); -} - -static void * -vl_api_nat64_prefix_dump_t_print (vl_api_nat64_prefix_dump_t * mp, - void *handle) -{ - u8 *s; - - s = format (0, "SCRIPT: nat64_prefix_dump\n"); - - FINISH; -} - -/* List of message types that this plugin understands */ -#define foreach_snat_plugin_api_msg \ -_(SNAT_ADD_ADDRESS_RANGE, snat_add_address_range) \ -_(SNAT_INTERFACE_ADD_DEL_FEATURE, snat_interface_add_del_feature) \ -_(SNAT_ADD_STATIC_MAPPING, snat_add_static_mapping) \ -_(SNAT_CONTROL_PING, snat_control_ping) \ -_(SNAT_STATIC_MAPPING_DUMP, snat_static_mapping_dump) \ -_(SNAT_SHOW_CONFIG, snat_show_config) \ -_(SNAT_ADDRESS_DUMP, snat_address_dump) \ -_(SNAT_INTERFACE_DUMP, snat_interface_dump) \ -_(SNAT_SET_WORKERS, snat_set_workers) \ -_(SNAT_WORKER_DUMP, snat_worker_dump) \ -_(SNAT_ADD_DEL_INTERFACE_ADDR, snat_add_del_interface_addr) \ -_(SNAT_INTERFACE_ADDR_DUMP, snat_interface_addr_dump) \ -_(SNAT_IPFIX_ENABLE_DISABLE, snat_ipfix_enable_disable) \ -_(SNAT_USER_DUMP, snat_user_dump) \ -_(SNAT_USER_SESSION_DUMP, snat_user_session_dump) \ -_(SNAT_INTERFACE_ADD_DEL_OUTPUT_FEATURE, \ - snat_interface_add_del_output_feature) \ -_(SNAT_INTERFACE_OUTPUT_FEATURE_DUMP, \ - snat_interface_output_feature_dump) \ -_(SNAT_ADD_DET_MAP, snat_add_det_map) \ -_(SNAT_DET_FORWARD, snat_det_forward) \ -_(SNAT_DET_REVERSE, snat_det_reverse) \ -_(SNAT_DET_MAP_DUMP, snat_det_map_dump) \ -_(SNAT_DET_SET_TIMEOUTS, snat_det_set_timeouts) \ -_(SNAT_DET_GET_TIMEOUTS, snat_det_get_timeouts) \ -_(SNAT_DET_CLOSE_SESSION_OUT, snat_det_close_session_out) \ -_(SNAT_DET_CLOSE_SESSION_IN, snat_det_close_session_in) \ -_(SNAT_DET_SESSION_DUMP, snat_det_session_dump) \ -_(NAT64_ADD_DEL_POOL_ADDR_RANGE, nat64_add_del_pool_addr_range) \ -_(NAT64_POOL_ADDR_DUMP, nat64_pool_addr_dump) \ -_(NAT64_ADD_DEL_INTERFACE, nat64_add_del_interface) \ -_(NAT64_INTERFACE_DUMP, nat64_interface_dump) \ -_(NAT64_ADD_DEL_STATIC_BIB, nat64_add_del_static_bib) \ -_(NAT64_BIB_DUMP, nat64_bib_dump) \ -_(NAT64_SET_TIMEOUTS, nat64_set_timeouts) \ -_(NAT64_GET_TIMEOUTS, nat64_get_timeouts) \ -_(NAT64_ST_DUMP, nat64_st_dump) \ -_(NAT64_ADD_DEL_PREFIX, nat64_add_del_prefix) \ -_(NAT64_PREFIX_DUMP, nat64_prefix_dump) - -/* Set up the API message handling tables */ -static clib_error_t * -snat_plugin_api_hookup (vlib_main_t * vm) -{ - snat_main_t *sm __attribute__ ((unused)) = &snat_main; -#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_snat_plugin_api_msg; -#undef _ - - return 0; -} - -#define vl_msg_name_crc_list -#include -#undef vl_msg_name_crc_list - -static void -setup_message_id_table (snat_main_t * sm, api_main_t * am) -{ -#define _(id,n,crc) \ - vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + sm->msg_id_base); - foreach_vl_msg_name_crc_snat; -#undef _ -} - -static void -plugin_custom_dump_configure (snat_main_t * sm) -{ -#define _(n,f) sm->api_main->msg_print_handlers \ - [VL_API_##n + sm->msg_id_base] \ - = (void *) vl_api_##f##_t_print; - foreach_snat_plugin_api_msg; -#undef _ -} - -clib_error_t * -snat_api_init (vlib_main_t * vm, snat_main_t * sm) -{ - u8 *name; - clib_error_t *error = 0; - - name = format (0, "snat_%08x%c", api_version, 0); - - /* Ask for a correctly-sized block of API message decode slots */ - sm->msg_id_base = - vl_msg_api_get_msg_ids ((char *) name, VL_MSG_FIRST_AVAILABLE); - - error = snat_plugin_api_hookup (vm); - - /* Add our API messages to the global name_crc hash table */ - setup_message_id_table (sm, sm->api_main); - - plugin_custom_dump_configure (sm); - - vec_free (name); - - return error; -} - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/snat/snat_det.c b/src/plugins/snat/snat_det.c deleted file mode 100644 index 2d6fce85..00000000 --- a/src/plugins/snat/snat_det.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * snat_det.c - deterministic NAT - * - * 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. - */ -/** - * @file - * @brief deterministic NAT - */ - -#include - - -/** - * @brief Add/delete deterministic NAT mapping. - * - * Create bijective mapping of inside address to outside address and port range - * pairs, with the purpose of enabling deterministic NAT to reduce logging in - * CGN deployments. - * - * @param sm SNAT main. - * @param in_addr Inside network address. - * @param in_plen Inside network prefix length. - * @param out_addr Outside network address. - * @param out_plen Outside network prefix length. - * @param is_add If 0 delete, otherwise add. - */ -int -snat_det_add_map (snat_main_t * sm, ip4_address_t * in_addr, u8 in_plen, - ip4_address_t * out_addr, u8 out_plen, int is_add) -{ - snat_det_map_t *det_map; - static snat_det_session_t empty_snat_det_session = { 0 }; - snat_interface_t *i; - ip4_address_t in_cmp, out_cmp; - u8 found = 0; - - in_cmp.as_u32 = in_addr->as_u32 & ip4_main.fib_masks[in_plen]; - out_cmp.as_u32 = out_addr->as_u32 & ip4_main.fib_masks[out_plen]; - vec_foreach (det_map, sm->det_maps) - { - /* Checking for overlapping addresses to be added here */ - if (det_map->in_addr.as_u32 == in_cmp.as_u32 && - det_map->in_plen == in_plen && - det_map->out_addr.as_u32 == out_cmp.as_u32 && - det_map->out_plen == out_plen) - { - found = 1; - break; - } - } - - /* If found, don't add again */ - if (found && is_add) - return VNET_API_ERROR_VALUE_EXIST; - - /* If not found, don't delete */ - if (!found && !is_add) - return VNET_API_ERROR_NO_SUCH_ENTRY; - - if (is_add) - { - pool_get (sm->det_maps, det_map); - memset (det_map, 0, sizeof (*det_map)); - det_map->in_addr.as_u32 = in_cmp.as_u32; - det_map->in_plen = in_plen; - det_map->out_addr.as_u32 = out_cmp.as_u32; - det_map->out_plen = out_plen; - det_map->sharing_ratio = (1 << (32 - in_plen)) / (1 << (32 - out_plen)); - det_map->ports_per_host = (65535 - 1023) / det_map->sharing_ratio; - - vec_validate_init_empty (det_map->sessions, - SNAT_DET_SES_PER_USER * (1 << (32 - in_plen)) - - 1, empty_snat_det_session); - } - else - { - vec_free (det_map->sessions); - vec_del1 (sm->det_maps, det_map - sm->det_maps); - } - - /* Add/del external address range to FIB */ - /* *INDENT-OFF* */ - pool_foreach (i, sm->interfaces, - ({ - if (i->is_inside) - continue; - - snat_add_del_addr_to_fib(out_addr, out_plen, i->sw_if_index, is_add); - break; - })); - /* *INDENT-ON* */ - return 0; -} - -/** - * @brief The 'snat-det-expire-walk' process's main loop. - * - * Check expire time for active sessions. - */ -static uword -snat_det_expire_walk_fn (vlib_main_t * vm, vlib_node_runtime_t * rt, - vlib_frame_t * f) -{ - snat_main_t *sm = &snat_main; - snat_det_map_t *dm; - snat_det_session_t *ses; - - while (sm->deterministic) - { - vlib_process_wait_for_event_or_clock (vm, 10.0); - vlib_process_get_events (vm, NULL); - u32 now = (u32) vlib_time_now (vm); - /* *INDENT-OFF* */ - pool_foreach (dm, sm->det_maps, - ({ - vec_foreach(ses, dm->sessions) - { - /* Delete if session expired */ - if (ses->in_port && (ses->expire < now)) - snat_det_ses_close (dm, ses); - } - })); - /* *INDENT-ON* */ - } - - return 0; -} - -static vlib_node_registration_t snat_det_expire_walk_node; - -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (snat_det_expire_walk_node, static) = { - .function = snat_det_expire_walk_fn, - .type = VLIB_NODE_TYPE_PROCESS, - .name = - "snat-det-expire-walk", -}; -/* *INDENT-ON* */ - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/snat/snat_det.h b/src/plugins/snat/snat_det.h deleted file mode 100644 index f4fdb256..00000000 --- a/src/plugins/snat/snat_det.h +++ /dev/null @@ -1,196 +0,0 @@ -/* - * snat_det.h - deterministic nat definitions - * - * 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. - */ -/** - * @file - * @brief deterministic NAT definitions - */ - -#ifndef __included_snat_det_h__ -#define __included_snat_det_h__ - -#include -#include -#include - - -#define SNAT_DET_SES_PER_USER 1000 - - -int snat_det_add_map (snat_main_t * sm, ip4_address_t * in_addr, u8 in_plen, - ip4_address_t * out_addr, u8 out_plen, int is_add); - -always_inline int -is_addr_in_net (ip4_address_t * addr, ip4_address_t * net, u8 plen) -{ - if (net->as_u32 == (addr->as_u32 & ip4_main.fib_masks[plen])) - return 1; - return 0; -} - -always_inline snat_det_map_t * -snat_det_map_by_user (snat_main_t * sm, ip4_address_t * user_addr) -{ - snat_det_map_t *dm; - - /* *INDENT-OFF* */ - pool_foreach (dm, sm->det_maps, - ({ - if (is_addr_in_net(user_addr, &dm->in_addr, dm->in_plen)) - return dm; - })); - /* *INDENT-ON* */ - return 0; -} - -always_inline snat_det_map_t * -snat_det_map_by_out (snat_main_t * sm, ip4_address_t * out_addr) -{ - snat_det_map_t *dm; - - /* *INDENT-OFF* */ - pool_foreach (dm, sm->det_maps, - ({ - if (is_addr_in_net(out_addr, &dm->out_addr, dm->out_plen)) - return dm; - })); - /* *INDENT-ON* */ - return 0; -} - -always_inline void -snat_det_forward (snat_det_map_t * dm, ip4_address_t * in_addr, - ip4_address_t * out_addr, u16 * lo_port) -{ - u32 in_offset, out_offset; - - in_offset = clib_net_to_host_u32 (in_addr->as_u32) - - clib_net_to_host_u32 (dm->in_addr.as_u32); - out_offset = in_offset / dm->sharing_ratio; - out_addr->as_u32 = - clib_host_to_net_u32 (clib_net_to_host_u32 (dm->out_addr.as_u32) + - out_offset); - *lo_port = 1024 + dm->ports_per_host * (in_offset % dm->sharing_ratio); -} - -always_inline void -snat_det_reverse (snat_det_map_t * dm, ip4_address_t * out_addr, u16 out_port, - ip4_address_t * in_addr) -{ - u32 in_offset1, in_offset2, out_offset; - - out_offset = clib_net_to_host_u32 (out_addr->as_u32) - - clib_net_to_host_u32 (dm->out_addr.as_u32); - in_offset1 = out_offset * dm->sharing_ratio; - in_offset2 = (out_port - 1024) / dm->ports_per_host; - in_addr->as_u32 = - clib_host_to_net_u32 (clib_net_to_host_u32 (dm->in_addr.as_u32) + - in_offset1 + in_offset2); -} - -always_inline u32 -snat_det_user_ses_offset (ip4_address_t * addr, u8 plen) -{ - return (clib_net_to_host_u32 (addr->as_u32) & pow2_mask (32 - plen)) * - SNAT_DET_SES_PER_USER; -} - -always_inline snat_det_session_t * -snat_det_get_ses_by_out (snat_det_map_t * dm, ip4_address_t * in_addr, - u64 out_key) -{ - u32 user_offset; - u16 i; - - user_offset = snat_det_user_ses_offset (in_addr, dm->in_plen); - for (i = 0; i < SNAT_DET_SES_PER_USER; i++) - { - if (dm->sessions[i + user_offset].out.as_u64 == out_key) - return &dm->sessions[i + user_offset]; - } - - return 0; -} - -always_inline snat_det_session_t * -snat_det_find_ses_by_in (snat_det_map_t * dm, ip4_address_t * in_addr, - u16 in_port, snat_det_out_key_t out_key) -{ - snat_det_session_t *ses; - u32 user_offset; - u16 i; - - user_offset = snat_det_user_ses_offset (in_addr, dm->in_plen); - for (i = 0; i < SNAT_DET_SES_PER_USER; i++) - { - ses = &dm->sessions[i + user_offset]; - if (ses->in_port == in_port && - ses->out.ext_host_addr.as_u32 == out_key.ext_host_addr.as_u32 && - ses->out.ext_host_port == out_key.ext_host_port) - return &dm->sessions[i + user_offset]; - } - - return 0; -} - -always_inline snat_det_session_t * -snat_det_ses_create (snat_det_map_t * dm, ip4_address_t * in_addr, - u16 in_port, snat_det_out_key_t * out) -{ - u32 user_offset; - u16 i; - - user_offset = snat_det_user_ses_offset (in_addr, dm->in_plen); - - for (i = 0; i < SNAT_DET_SES_PER_USER; i++) - { - if (!dm->sessions[i + user_offset].in_port) - { - if (__sync_bool_compare_and_swap - (&dm->sessions[i + user_offset].in_port, 0, in_port)) - { - dm->sessions[i + user_offset].out.as_u64 = out->as_u64; - dm->sessions[i + user_offset].state = SNAT_SESSION_UNKNOWN; - dm->sessions[i + user_offset].expire = 0; - __sync_add_and_fetch (&dm->ses_num, 1); - return &dm->sessions[i + user_offset]; - } - } - } - - snat_ipfix_logging_max_entries_per_user (in_addr->as_u32); - return 0; -} - -always_inline void -snat_det_ses_close (snat_det_map_t * dm, snat_det_session_t * ses) -{ - if (__sync_bool_compare_and_swap (&ses->in_port, ses->in_port, 0)) - { - ses->out.as_u64 = 0; - __sync_add_and_fetch (&dm->ses_num, -1); - } -} - -#endif /* __included_snat_det_h__ */ - -/* - * fd.io coding-style-patch-verification: ON - * - * Local Variables: - * eval: (c-set-style "gnu") - * End: - */ diff --git a/src/plugins/snat/snat_ipfix_logging.c b/src/plugins/snat/snat_ipfix_logging.c deleted file mode 100644 index c68dc540..00000000 --- a/src/plugins/snat/snat_ipfix_logging.c +++ /dev/null @@ -1,835 +0,0 @@ -/* - * snat_ipfix_logging.c - NAT Events IPFIX logging - * - * 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 -#include -#include -#include - -snat_ipfix_logging_main_t snat_ipfix_logging_main; - -#define NAT44_SESSION_CREATE_LEN 26 -#define NAT_ADDRESSES_EXHAUTED_LEN 13 -#define MAX_ENTRIES_PER_USER_LEN 17 - -#define NAT44_SESSION_CREATE_FIELD_COUNT 8 -#define NAT_ADDRESSES_EXHAUTED_FIELD_COUNT 3 -#define MAX_ENTRIES_PER_USER_FIELD_COUNT 4 - -typedef struct { - u8 nat_event; - u32 src_ip; - u32 nat_src_ip; - snat_protocol_t snat_proto; - u16 src_port; - u16 nat_src_port; - u32 vrf_id; -} snat_ipfix_logging_nat44_ses_args_t; - -typedef struct { - u32 pool_id; -} snat_ipfix_logging_addr_exhausted_args_t; - -typedef struct { - u32 src_ip; -} snat_ipfix_logging_max_entries_per_user_args_t; - -/** - * @brief Create an IPFIX template packet rewrite string - * - * @param frm flow report main - * @param fr flow report - * @param collector_address collector address - * @param src_address source address - * @param collector_port collector - * @param event NAT event ID - * @param quota_event NAT quota exceeded event ID - * - * @returns template packet - */ -static inline u8 * -snat_template_rewrite (flow_report_main_t * frm, - flow_report_t * fr, - ip4_address_t * collector_address, - ip4_address_t * src_address, - u16 collector_port, - nat_event_t event, - quota_exceed_event_t quota_event) -{ - snat_ipfix_logging_main_t *silm = &snat_ipfix_logging_main; - ip4_header_t *ip; - udp_header_t *udp; - ipfix_message_header_t *h; - ipfix_set_header_t *s; - ipfix_template_header_t *t; - ipfix_field_specifier_t *f; - ipfix_field_specifier_t *first_field; - u8 *rewrite = 0; - ip4_ipfix_template_packet_t *tp; - u32 field_count = 0; - flow_report_stream_t *stream; - - stream = &frm->streams[fr->stream_index]; - silm->stream_index = fr->stream_index; - - if (event == NAT_ADDRESSES_EXHAUTED) - { - field_count = NAT_ADDRESSES_EXHAUTED_FIELD_COUNT; - silm->addr_exhausted_template_id = fr->template_id; - } - else if (event == NAT44_SESSION_CREATE) - { - field_count = NAT44_SESSION_CREATE_FIELD_COUNT; - silm->nat44_session_template_id = fr->template_id; - } - else if (event == QUOTA_EXCEEDED) - { - if (quota_event == MAX_ENTRIES_PER_USER) - { - field_count = MAX_ENTRIES_PER_USER_FIELD_COUNT; - silm->max_entries_per_user_template_id = fr->template_id; - } - } - - /* allocate rewrite space */ - vec_validate_aligned (rewrite, - sizeof (ip4_ipfix_template_packet_t) - + field_count * sizeof (ipfix_field_specifier_t) - 1, - CLIB_CACHE_LINE_BYTES); - - tp = (ip4_ipfix_template_packet_t *) rewrite; - ip = (ip4_header_t *) & tp->ip4; - udp = (udp_header_t *) (ip + 1); - h = (ipfix_message_header_t *) (udp + 1); - s = (ipfix_set_header_t *) (h + 1); - t = (ipfix_template_header_t *) (s + 1); - first_field = f = (ipfix_field_specifier_t *) (t + 1); - - ip->ip_version_and_header_length = 0x45; - ip->ttl = 254; - ip->protocol = IP_PROTOCOL_UDP; - ip->src_address.as_u32 = src_address->as_u32; - ip->dst_address.as_u32 = collector_address->as_u32; - udp->src_port = clib_host_to_net_u16 (stream->src_port); - udp->dst_port = clib_host_to_net_u16 (collector_port); - udp->length = clib_host_to_net_u16 (vec_len (rewrite) - sizeof (*ip)); - - /* FIXUP: message header export_time */ - h->domain_id = clib_host_to_net_u32 (stream->domain_id); - - /* Add TLVs to the template */ - if (event == NAT_ADDRESSES_EXHAUTED) - { - f->e_id_length = ipfix_e_id_length (0, observationTimeMilliseconds, 8); - f++; - f->e_id_length = ipfix_e_id_length (0, natEvent, 1); - f++; - f->e_id_length = ipfix_e_id_length (0, natPoolId, 4); - f++; - } - else if (event == NAT44_SESSION_CREATE) - { - f->e_id_length = ipfix_e_id_length (0, observationTimeMilliseconds, 8); - f++; - f->e_id_length = ipfix_e_id_length (0, natEvent, 1); - f++; - f->e_id_length = ipfix_e_id_length (0, sourceIPv4Address, 4); - f++; - f->e_id_length = ipfix_e_id_length (0, postNATSourceIPv4Address, 4); - f++; - f->e_id_length = ipfix_e_id_length (0, protocolIdentifier, 1); - f++; - f->e_id_length = ipfix_e_id_length (0, sourceTransportPort, 2); - f++; - f->e_id_length = ipfix_e_id_length (0, postNAPTSourceTransportPort, 2); - f++; - f->e_id_length = ipfix_e_id_length (0, ingressVRFID, 4); - f++; - } - else if (event == QUOTA_EXCEEDED) - { - if (quota_event == MAX_ENTRIES_PER_USER) - { - f->e_id_length = ipfix_e_id_length (0, observationTimeMilliseconds, - 8); - f++; - f->e_id_length = ipfix_e_id_length (0, natEvent, 1); - f++; - f->e_id_length = ipfix_e_id_length (0, natQuotaExceededEvent, 4); - f++; - f->e_id_length = ipfix_e_id_length (0, sourceIPv4Address, 4); - f++; - } - } - - /* Back to the template packet... */ - ip = (ip4_header_t *) & tp->ip4; - udp = (udp_header_t *) (ip + 1); - - ASSERT (f - first_field); - /* Field count in this template */ - t->id_count = ipfix_id_count (fr->template_id, f - first_field); - - /* set length in octets */ - s->set_id_length = - ipfix_set_id_length (2 /* set_id */ , (u8 *) f - (u8 *) s); - - /* message length in octets */ - h->version_length = version_length ((u8 *) f - (u8 *) h); - - ip->length = clib_host_to_net_u16 ((u8 *) f - (u8 *) ip); - ip->checksum = ip4_header_checksum (ip); - - return rewrite; -} - -u8 * -snat_template_rewrite_addr_exhausted (flow_report_main_t * frm, - flow_report_t * fr, - ip4_address_t * collector_address, - ip4_address_t * src_address, - u16 collector_port) -{ - return snat_template_rewrite (frm, fr, collector_address, src_address, - collector_port, NAT_ADDRESSES_EXHAUTED, 0); -} - -u8 * -snat_template_rewrite_nat44_session (flow_report_main_t * frm, - flow_report_t * fr, - ip4_address_t * collector_address, - ip4_address_t * src_address, - u16 collector_port) -{ - return snat_template_rewrite (frm, fr, collector_address, src_address, - collector_port, NAT44_SESSION_CREATE, 0); -} - -u8 * -snat_template_rewrite_max_entries_per_usr (flow_report_main_t * frm, - flow_report_t * fr, - ip4_address_t * collector_address, - ip4_address_t * src_address, - u16 collector_port) -{ - return snat_template_rewrite (frm, fr, collector_address, src_address, - collector_port, QUOTA_EXCEEDED, - MAX_ENTRIES_PER_USER); -} - -static inline void -snat_ipfix_header_create (flow_report_main_t * frm, - vlib_buffer_t * b0, - u32 * offset) -{ - snat_ipfix_logging_main_t *silm = &snat_ipfix_logging_main; - flow_report_stream_t *stream; - ip4_ipfix_template_packet_t * tp; - ipfix_message_header_t * h = 0; - ipfix_set_header_t * s = 0; - ip4_header_t * ip; - udp_header_t * udp; - - stream = &frm->streams[silm->stream_index]; - - b0->current_data = 0; - b0->current_length = sizeof (*ip) + sizeof (*udp) + sizeof (*h) + - sizeof (*s); - b0->flags |= (VLIB_BUFFER_TOTAL_LENGTH_VALID | VLIB_BUFFER_FLOW_REPORT); - vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0; - vnet_buffer (b0)->sw_if_index[VLIB_TX] = frm->fib_index; - tp = vlib_buffer_get_current (b0); - ip = (ip4_header_t *) &tp->ip4; - udp = (udp_header_t *) (ip+1); - h = (ipfix_message_header_t *)(udp+1); - s = (ipfix_set_header_t *)(h+1); - - ip->ip_version_and_header_length = 0x45; - ip->ttl = 254; - ip->protocol = IP_PROTOCOL_UDP; - ip->flags_and_fragment_offset = 0; - ip->src_address.as_u32 = frm->src_address.as_u32; - ip->dst_address.as_u32 = frm->ipfix_collector.as_u32; - udp->src_port = clib_host_to_net_u16 (stream->src_port); - udp->dst_port = clib_host_to_net_u16 (frm->collector_port); - udp->checksum = 0; - - h->export_time = clib_host_to_net_u32 ( - (u32) (((f64)frm->unix_time_0) + (vlib_time_now(frm->vlib_main) - - frm->vlib_time_0))); - h->sequence_number = clib_host_to_net_u32 (stream->sequence_number++); - h->domain_id = clib_host_to_net_u32 (stream->domain_id); - - *offset = (u32) (((u8 *)(s+1)) - (u8 *)tp); -} - -static inline void -snat_ipfix_send (flow_report_main_t * frm, - vlib_frame_t * f, - vlib_buffer_t * b0, - u16 template_id) -{ - ip4_ipfix_template_packet_t * tp; - ipfix_message_header_t * h = 0; - ipfix_set_header_t * s = 0; - ip4_header_t * ip; - udp_header_t * udp; - vlib_main_t * vm = frm->vlib_main; - - tp = vlib_buffer_get_current (b0); - ip = (ip4_header_t *) & tp->ip4; - udp = (udp_header_t *) (ip + 1); - h = (ipfix_message_header_t *) (udp + 1); - s = (ipfix_set_header_t *) (h + 1); - - s->set_id_length = ipfix_set_id_length (template_id, - b0->current_length - - (sizeof (*ip) + sizeof (*udp) + - sizeof (*h))); - h->version_length = version_length (b0->current_length - - (sizeof (*ip) + sizeof (*udp))); - - ip->length = clib_host_to_net_u16 (b0->current_length); - ip->checksum = ip4_header_checksum (ip); - udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip)); - - if (frm->udp_checksum) - { - udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip); - if (udp->checksum == 0) - udp->checksum = 0xffff; - } - - ASSERT (ip->checksum == ip4_header_checksum (ip)); - - vlib_put_frame_to_node (vm, ip4_lookup_node.index, f); -} - -static void -snat_ipfix_logging_nat44_ses (u8 nat_event, u32 src_ip, u32 nat_src_ip, - snat_protocol_t snat_proto, u16 src_port, - u16 nat_src_port, u32 vrf_id, int do_flush) -{ - snat_ipfix_logging_main_t *silm = &snat_ipfix_logging_main; - flow_report_main_t *frm = &flow_report_main; - vlib_frame_t *f; - vlib_buffer_t *b0 = 0; - u32 bi0 = ~0; - u32 offset; - vlib_main_t * vm = frm->vlib_main; - u64 now; - vlib_buffer_free_list_t *fl; - u8 proto = ~0; - - if (!silm->enabled) - return; - - proto = snat_proto_to_ip_proto (snat_proto); - - now = (u64) ((vlib_time_now (vm) - silm->vlib_time_0) * 1e3); - now += silm->milisecond_time_0; - - b0 = silm->nat44_session_buffer; - - if (PREDICT_FALSE (b0 == 0)) - { - if (do_flush) - return; - - if (vlib_buffer_alloc (vm, &bi0, 1) != 1) - { - clib_warning ("can't allocate buffer for NAT IPFIX event"); - return; - } - - b0 = silm->nat44_session_buffer = - vlib_get_buffer (vm, bi0); - fl = vlib_buffer_get_free_list (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX); - vlib_buffer_init_for_free_list (b0, fl); - VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0); - offset = 0; - } - else - { - bi0 = vlib_get_buffer_index (vm, b0); - offset = silm->nat44_session_next_record_offset; - } - - f = silm->nat44_session_frame; - if (PREDICT_FALSE (f == 0)) - { - u32 * to_next; - f = vlib_get_frame_to_node (vm, ip4_lookup_node.index); - silm->nat44_session_frame = f; - to_next = vlib_frame_vector_args (f); - to_next[0] = bi0; - f->n_vectors = 1; - } - - if (PREDICT_FALSE (offset == 0)) - snat_ipfix_header_create (frm, b0, &offset); - - if (PREDICT_TRUE (do_flush == 0)) - { - u64 time_stamp = clib_host_to_net_u64 (now); - clib_memcpy (b0->data + offset, &time_stamp, sizeof (time_stamp)); - offset += sizeof (time_stamp); - - clib_memcpy (b0->data + offset, &nat_event, sizeof (nat_event)); - offset += sizeof (nat_event); - - clib_memcpy (b0->data + offset, &src_ip, sizeof (src_ip)); - offset += sizeof (src_ip); - - clib_memcpy (b0->data + offset, &nat_src_ip, sizeof (nat_src_ip)); - offset += sizeof (nat_src_ip); - - clib_memcpy (b0->data + offset, &proto, sizeof (proto)); - offset += sizeof (proto); - - clib_memcpy (b0->data + offset, &src_port, sizeof (src_port)); - offset += sizeof (src_port); - - clib_memcpy (b0->data + offset, &nat_src_port, sizeof (nat_src_port)); - offset += sizeof (nat_src_port); - - clib_memcpy (b0->data + offset, &vrf_id, sizeof(vrf_id)); - offset += sizeof (vrf_id); - - b0->current_length += NAT44_SESSION_CREATE_LEN; - } - - if (PREDICT_FALSE (do_flush || (offset + NAT44_SESSION_CREATE_LEN) > frm->path_mtu)) - { - snat_ipfix_send (frm, f, b0, silm->nat44_session_template_id); - silm->nat44_session_frame = 0; - silm->nat44_session_buffer = 0; - offset = 0; - } - silm->nat44_session_next_record_offset = offset; - } - -static void -snat_ipfix_logging_addr_exhausted (u32 pool_id, int do_flush) -{ - snat_ipfix_logging_main_t *silm = &snat_ipfix_logging_main; - flow_report_main_t *frm = &flow_report_main; - vlib_frame_t *f; - vlib_buffer_t *b0 = 0; - u32 bi0 = ~0; - u32 offset; - vlib_main_t * vm = frm->vlib_main; - u64 now; - vlib_buffer_free_list_t *fl; - u8 nat_event = NAT_ADDRESSES_EXHAUTED; - - if (!silm->enabled) - return; - - now = (u64) ((vlib_time_now (vm) - silm->vlib_time_0) * 1e3); - now += silm->milisecond_time_0; - - b0 = silm->addr_exhausted_buffer; - - if (PREDICT_FALSE (b0 == 0)) - { - if (do_flush) - return; - - if (vlib_buffer_alloc (vm, &bi0, 1) != 1) - { - clib_warning ("can't allocate buffer for NAT IPFIX event"); - return; - } - - b0 = silm->addr_exhausted_buffer = - vlib_get_buffer (vm, bi0); - fl = vlib_buffer_get_free_list (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX); - vlib_buffer_init_for_free_list (b0, fl); - VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0); - offset = 0; - } - else - { - bi0 = vlib_get_buffer_index (vm, b0); - offset = silm->addr_exhausted_next_record_offset; - } - - f = silm->addr_exhausted_frame; - if (PREDICT_FALSE (f == 0)) - { - u32 * to_next; - f = vlib_get_frame_to_node (vm, ip4_lookup_node.index); - silm->addr_exhausted_frame = f; - to_next = vlib_frame_vector_args (f); - to_next[0] = bi0; - f->n_vectors = 1; - } - - if (PREDICT_FALSE (offset == 0)) - snat_ipfix_header_create (frm, b0, &offset); - - if (PREDICT_TRUE (do_flush == 0)) - { - u64 time_stamp = clib_host_to_net_u64 (now); - clib_memcpy (b0->data + offset, &time_stamp, sizeof (time_stamp)); - offset += sizeof (time_stamp); - - clib_memcpy (b0->data + offset, &nat_event, sizeof (nat_event)); - offset += sizeof (nat_event); - - clib_memcpy (b0->data + offset, &pool_id, sizeof(pool_id)); - offset += sizeof (pool_id); - - b0->current_length += NAT_ADDRESSES_EXHAUTED_LEN; - } - - if (PREDICT_FALSE (do_flush || (offset + NAT_ADDRESSES_EXHAUTED_LEN) > frm->path_mtu)) - { - snat_ipfix_send (frm, f, b0, silm->addr_exhausted_template_id); - silm->addr_exhausted_frame = 0; - silm->addr_exhausted_buffer = 0; - offset = 0; - } - silm->addr_exhausted_next_record_offset = offset; -} - -static void -snat_ipfix_logging_max_entries_per_usr (u32 src_ip, int do_flush) -{ - snat_ipfix_logging_main_t *silm = &snat_ipfix_logging_main; - flow_report_main_t *frm = &flow_report_main; - vlib_frame_t *f; - vlib_buffer_t *b0 = 0; - u32 bi0 = ~0; - u32 offset; - vlib_main_t * vm = frm->vlib_main; - u64 now; - vlib_buffer_free_list_t *fl; - u8 nat_event = QUOTA_EXCEEDED; - u32 quota_event = MAX_ENTRIES_PER_USER; - - if (!silm->enabled) - return; - - now = (u64) ((vlib_time_now (vm) - silm->vlib_time_0) * 1e3); - now += silm->milisecond_time_0; - - b0 = silm->max_entries_per_user_buffer; - - if (PREDICT_FALSE (b0 == 0)) - { - if (do_flush) - return; - - if (vlib_buffer_alloc (vm, &bi0, 1) != 1) - { - clib_warning ("can't allocate buffer for NAT IPFIX event"); - return; - } - - b0 = silm->max_entries_per_user_buffer = - vlib_get_buffer (vm, bi0); - fl = vlib_buffer_get_free_list (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX); - vlib_buffer_init_for_free_list (b0, fl); - VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0); - offset = 0; - } - else - { - bi0 = vlib_get_buffer_index (vm, b0); - offset = silm->max_entries_per_user_next_record_offset; - } - - f = silm->max_entries_per_user_frame; - if (PREDICT_FALSE (f == 0)) - { - u32 * to_next; - f = vlib_get_frame_to_node (vm, ip4_lookup_node.index); - silm->max_entries_per_user_frame = f; - to_next = vlib_frame_vector_args (f); - to_next[0] = bi0; - f->n_vectors = 1; - } - - if (PREDICT_FALSE (offset == 0)) - snat_ipfix_header_create (frm, b0, &offset); - - if (PREDICT_TRUE (do_flush == 0)) - { - u64 time_stamp = clib_host_to_net_u64 (now); - clib_memcpy (b0->data + offset, &time_stamp, sizeof (time_stamp)); - offset += sizeof (time_stamp); - - clib_memcpy (b0->data + offset, &nat_event, sizeof (nat_event)); - offset += sizeof (nat_event); - - clib_memcpy (b0->data + offset, "a_event, sizeof(quota_event)); - offset += sizeof (quota_event); - - clib_memcpy (b0->data + offset, &src_ip, sizeof (src_ip)); - offset += sizeof (src_ip); - - b0->current_length += MAX_ENTRIES_PER_USER_LEN; - } - - if (PREDICT_FALSE (do_flush || (offset + MAX_ENTRIES_PER_USER_LEN) > frm->path_mtu)) - { - snat_ipfix_send (frm, f, b0, silm->max_entries_per_user_template_id); - silm->max_entries_per_user_frame = 0; - silm->max_entries_per_user_buffer = 0; - offset = 0; - } - silm->max_entries_per_user_next_record_offset = offset; -} - -static void -snat_ipfix_logging_nat44_ses_rpc_cb (snat_ipfix_logging_nat44_ses_args_t *a) -{ - snat_ipfix_logging_nat44_ses(a->nat_event, a->src_ip, a->nat_src_ip, - a->snat_proto, a->src_port, a->nat_src_port, - a->vrf_id, 0); -} - -/** - * @brief Generate NAT44 session create event - * - * @param src_ip source IPv4 address - * @param nat_src_ip transaltes source IPv4 address - * @param snat_proto SNAT transport protocol - * @param src_port source port - * @param nat_src_port translated source port - * @param vrf_id VRF ID - */ -void -snat_ipfix_logging_nat44_ses_create (u32 src_ip, - u32 nat_src_ip, - snat_protocol_t snat_proto, - u16 src_port, - u16 nat_src_port, - u32 vrf_id) -{ - snat_ipfix_logging_nat44_ses_args_t a; - - a.nat_event = NAT44_SESSION_CREATE; - a.src_ip = src_ip; - a.nat_src_ip = nat_src_ip; - a.snat_proto = snat_proto; - a.src_port = src_port; - a.nat_src_port = nat_src_port; - a.vrf_id = vrf_id; - - vl_api_rpc_call_main_thread (snat_ipfix_logging_nat44_ses_rpc_cb, (u8 *) &a, - sizeof (a)); -} - -/** - * @brief Generate NAT44 session delete event - * - * @param src_ip source IPv4 address - * @param nat_src_ip transaltes source IPv4 address - * @param snat_proto SNAT transport protocol - * @param src_port source port - * @param nat_src_port translated source port - * @param vrf_id VRF ID - */ -void -snat_ipfix_logging_nat44_ses_delete (u32 src_ip, - u32 nat_src_ip, - snat_protocol_t snat_proto, - u16 src_port, - u16 nat_src_port, - u32 vrf_id) -{ - snat_ipfix_logging_nat44_ses_args_t a; - - a.nat_event = NAT44_SESSION_DELETE; - a.src_ip = src_ip; - a.nat_src_ip = nat_src_ip; - a.snat_proto = snat_proto; - a.src_port = src_port; - a.nat_src_port = nat_src_port; - a.vrf_id = vrf_id; - - vl_api_rpc_call_main_thread (snat_ipfix_logging_nat44_ses_rpc_cb, (u8 *) &a, - sizeof (a)); -} - -vlib_frame_t * -snat_data_callback_nat44_session (flow_report_main_t * frm, - flow_report_t * fr, - vlib_frame_t * f, - u32 * to_next, - u32 node_index) -{ - snat_ipfix_logging_nat44_ses(0, 0, 0, 0, 0, 0, 0, 1); - return f; -} - -static void -snat_ipfix_logging_addr_exhausted_rpc_cb - (snat_ipfix_logging_addr_exhausted_args_t * a) -{ - snat_ipfix_logging_addr_exhausted(a->pool_id, 0); -} - -/** - * @brief Generate NAT addresses exhausted event - * - * @param pool_id NAT pool ID - */ -void -snat_ipfix_logging_addresses_exhausted(u32 pool_id) -{ - //TODO: This event SHOULD be rate limited - snat_ipfix_logging_addr_exhausted_args_t a; - - a.pool_id = pool_id; - - vl_api_rpc_call_main_thread (snat_ipfix_logging_addr_exhausted_rpc_cb, - (u8 *) &a, sizeof (a)); -} - -vlib_frame_t * -snat_data_callback_addr_exhausted (flow_report_main_t * frm, - flow_report_t * fr, - vlib_frame_t * f, - u32 * to_next, - u32 node_index) -{ - snat_ipfix_logging_addr_exhausted(0, 1); - return f; -} - -static void -snat_ipfix_logging_max_entries_per_usr_rpc_cb - (snat_ipfix_logging_max_entries_per_user_args_t * a) -{ - snat_ipfix_logging_max_entries_per_usr(a->src_ip, 0); -} - -/** - * @brief Generate maximum entries per user exceeded event - * - * @param src_ip source IPv4 address - */ -void -snat_ipfix_logging_max_entries_per_user(u32 src_ip) -{ - //TODO: This event SHOULD be rate limited - snat_ipfix_logging_max_entries_per_user_args_t a; - - a.src_ip = src_ip; - - vl_api_rpc_call_main_thread (snat_ipfix_logging_max_entries_per_usr_rpc_cb, - (u8 *) &a, sizeof (a)); -} - -vlib_frame_t * -snat_data_callback_max_entries_per_usr (flow_report_main_t * frm, - flow_report_t * fr, - vlib_frame_t * f, - u32 * to_next, - u32 node_index) -{ - snat_ipfix_logging_max_entries_per_usr(0, 1); - return f; -} - -/** - * @brief Enable/disable SNAT IPFIX logging - * - * @param enable 1 if enable, 0 if disable - * @param domain_id observation domain ID - * @param src_port source port number - * - * @returns 0 if success - */ -int -snat_ipfix_logging_enable_disable (int enable, u32 domain_id, u16 src_port) -{ - snat_main_t * sm = &snat_main; - snat_ipfix_logging_main_t *silm = &snat_ipfix_logging_main; - flow_report_main_t *frm = &flow_report_main; - vnet_flow_report_add_del_args_t a; - int rv; - u8 e = enable ? 1 : 0; - - if (silm->enabled == e) - return 0; - - silm->enabled = e; - - memset (&a, 0, sizeof (a)); - a.is_add = enable; - a.domain_id = domain_id ? domain_id : 1; - a.src_port = src_port ? src_port : UDP_DST_PORT_ipfix; - - if (sm->deterministic) - { - a.rewrite_callback = snat_template_rewrite_max_entries_per_usr; - a.flow_data_callback = snat_data_callback_max_entries_per_usr; - - rv = vnet_flow_report_add_del (frm, &a, NULL); - if (rv) - { - clib_warning ("vnet_flow_report_add_del returned %d", rv); - return -1; - } - } - else - { - a.rewrite_callback = snat_template_rewrite_nat44_session; - a.flow_data_callback = snat_data_callback_nat44_session; - - rv = vnet_flow_report_add_del (frm, &a, NULL); - if (rv) - { - clib_warning ("vnet_flow_report_add_del returned %d", rv); - return -1; - } - - a.rewrite_callback = snat_template_rewrite_addr_exhausted; - a.flow_data_callback = snat_data_callback_addr_exhausted; - - rv = vnet_flow_report_add_del (frm, &a, NULL); - if (rv) - { - clib_warning ("vnet_flow_report_add_del returned %d", rv); - return -1; - } - } - - return 0; -} - -/** - * @brief Initialize SNAT IPFIX logging - * - * @param vm vlib main - */ -void -snat_ipfix_logging_init (vlib_main_t * vm) -{ - snat_ipfix_logging_main_t *silm = &snat_ipfix_logging_main; - - silm->enabled = 0; - - /* Set up time reference pair */ - silm->vlib_time_0 = vlib_time_now (vm); - silm->milisecond_time_0 = unix_time_now_nsec () * 1e-6; -} diff --git a/src/plugins/snat/snat_ipfix_logging.h b/src/plugins/snat/snat_ipfix_logging.h deleted file mode 100644 index 45c1a7bf..00000000 --- a/src/plugins/snat/snat_ipfix_logging.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * snat_ipfix_logging.h - NAT Events IPFIX logging - * - * 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_ipfix_logging_h__ -#define __included_snat_ipfix_logging_h__ - -typedef enum { - NAT_ADDRESSES_EXHAUTED = 3, - NAT44_SESSION_CREATE = 4, - NAT44_SESSION_DELETE = 5, - NAT_PORTS_EXHAUSTED = 12, - QUOTA_EXCEEDED = 13, -} nat_event_t; - -typedef enum { - MAX_ENTRIES_PER_USER = 3, -} quota_exceed_event_t; - -typedef struct { - /** S-NAT IPFIX logging enabled */ - u8 enabled; - - /** ipfix buffers under construction */ - vlib_buffer_t *nat44_session_buffer; - vlib_buffer_t *addr_exhausted_buffer; - vlib_buffer_t *max_entries_per_user_buffer; - - /** frames containing ipfix buffers */ - vlib_frame_t *nat44_session_frame; - vlib_frame_t *addr_exhausted_frame; - vlib_frame_t *max_entries_per_user_frame; - - /** next record offset */ - u32 nat44_session_next_record_offset; - u32 addr_exhausted_next_record_offset; - u32 max_entries_per_user_next_record_offset; - - /** Time reference pair */ - u64 milisecond_time_0; - f64 vlib_time_0; - - /** template IDs */ - u16 nat44_session_template_id; - u16 addr_exhausted_template_id; - u16 max_entries_per_user_template_id; - - /** stream index */ - u32 stream_index; -} snat_ipfix_logging_main_t; - -extern snat_ipfix_logging_main_t snat_ipfix_logging_main; - -void snat_ipfix_logging_init (vlib_main_t * vm); -int snat_ipfix_logging_enable_disable (int enable, u32 domain_id, u16 src_port); -void snat_ipfix_logging_nat44_ses_create (u32 src_ip, u32 nat_src_ip, - snat_protocol_t snat_proto, - u16 src_port, u16 nat_src_port, - u32 vrf_id); -void snat_ipfix_logging_nat44_ses_delete (u32 src_ip, u32 nat_src_ip, - snat_protocol_t snat_proto, - u16 src_port, u16 nat_src_port, - u32 vrf_id); -void snat_ipfix_logging_addresses_exhausted(u32 pool_id); -void snat_ipfix_logging_max_entries_per_user(u32 src_ip); - -#endif /* __included_snat_ipfix_logging_h__ */ diff --git a/src/plugins/snat/snat_msg_enum.h b/src/plugins/snat/snat_msg_enum.h deleted file mode 100644 index 2c76fd51..00000000 --- a/src/plugins/snat/snat_msg_enum.h +++ /dev/null @@ -1,31 +0,0 @@ - -/* - * snat_msg_enum.h - skeleton vpp engine plug-in message enumeration - * - * Copyright (c) - * 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 - -#define vl_msg_id(n,h) n, -typedef enum { -#include - /* 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 */ diff --git a/src/plugins/snat/snat_test.c b/src/plugins/snat/snat_test.c deleted file mode 100644 index 905b8fac..00000000 --- a/src/plugins/snat/snat_test.c +++ /dev/null @@ -1,1167 +0,0 @@ - -/* - * snat.c - skeleton vpp-api-test plug-in - * - * Copyright (c) - * 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 -#include -#include -#include -#include -#include -#include - -#define __plugin_msg_base snat_test_main.msg_id_base -#include - -uword unformat_sw_if_index (unformat_input_t * input, va_list * args); - -/* Declare message IDs */ -#include - -/* define message structures */ -#define vl_typedefs -#include -#undef vl_typedefs - -/* declare message handlers for each api */ - -#define vl_endianfun /* define message structures */ -#include -#undef vl_endianfun - -/* instantiate all the print functions we know about */ -#define vl_print(handle, ...) -#define vl_printfun -#include -#undef vl_printfun - -/* Get the API version number. */ -#define vl_api_version(n,v) static u32 api_version=(v); -#include -#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) \ -_(snat_add_static_mapping_reply) \ -_(snat_set_workers_reply) \ -_(snat_add_del_interface_addr_reply) \ -_(snat_ipfix_enable_disable_reply) \ -_(snat_add_det_map_reply) \ -_(snat_det_set_timeouts_reply) \ -_(snat_det_close_session_out_reply) \ -_(snat_det_close_session_in_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) \ -_(SNAT_ADD_STATIC_MAPPING_REPLY, snat_add_static_mapping_reply) \ -_(SNAT_CONTROL_PING_REPLY, snat_control_ping_reply) \ -_(SNAT_STATIC_MAPPING_DETAILS, snat_static_mapping_details) \ -_(SNAT_SHOW_CONFIG_REPLY, snat_show_config_reply) \ -_(SNAT_ADDRESS_DETAILS, snat_address_details) \ -_(SNAT_INTERFACE_DETAILS, snat_interface_details) \ -_(SNAT_SET_WORKERS_REPLY, snat_set_workers_reply) \ -_(SNAT_WORKER_DETAILS, snat_worker_details) \ -_(SNAT_ADD_DEL_INTERFACE_ADDR_REPLY, \ - snat_add_del_interface_addr_reply) \ -_(SNAT_INTERFACE_ADDR_DETAILS, snat_interface_addr_details) \ -_(SNAT_IPFIX_ENABLE_DISABLE_REPLY, \ - snat_ipfix_enable_disable_reply) \ -_(SNAT_USER_DETAILS, snat_user_details) \ -_(SNAT_USER_SESSION_DETAILS, snat_user_session_details) \ -_(SNAT_ADD_DET_MAP_REPLY, snat_add_det_map_reply) \ -_(SNAT_DET_FORWARD_REPLY, snat_det_forward_reply) \ -_(SNAT_DET_REVERSE_REPLY, snat_det_reverse_reply) \ -_(SNAT_DET_MAP_DETAILS, snat_det_map_details) \ -_(SNAT_DET_SET_TIMEOUTS_REPLY, snat_det_set_timeouts_reply) \ -_(SNAT_DET_GET_TIMEOUTS_REPLY, snat_det_get_timeouts_reply) \ -_(SNAT_DET_CLOSE_SESSION_OUT_REPLY, \ - snat_det_close_session_out_reply) \ -_(SNAT_DET_CLOSE_SESSION_IN_REPLY, \ - snat_det_close_session_in_reply) \ -_(SNAT_DET_SESSION_DETAILS, snat_det_session_details) - -static int api_snat_add_address_range (vat_main_t * vam) -{ - unformat_input_t * i = vam->input; - ip4_address_t start_addr, end_addr; - u32 start_host_order, end_host_order; - vl_api_snat_add_address_range_t * mp; - u8 is_add = 1; - int count; - int ret; - - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) - { - 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; - else if (unformat (i, "del")) - is_add = 0; - else - { - clib_warning("unknown input '%U'", format_unformat_error, i); - return -99; - } - } - - 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, mp); - - memcpy (mp->first_ip_address, &start_addr, 4); - memcpy (mp->last_ip_address, &end_addr, 4); - mp->is_ip4 = 1; - mp->is_add = is_add; - - S(mp); - W (ret); - return ret; -} - -static int api_snat_interface_add_del_feature (vat_main_t * vam) -{ - unformat_input_t * i = vam->input; - 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; - int ret; - - 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; - else - { - clib_warning("unknown input '%U'", format_unformat_error, i); - return -99; - } - } - - if (sw_if_index_set == 0) - { - errmsg ("interface / sw_if_index required\n"); - return -99; - } - - M(SNAT_INTERFACE_ADD_DEL_FEATURE, mp); - mp->sw_if_index = ntohl(sw_if_index); - mp->is_add = is_add; - mp->is_inside = is_inside; - - S(mp); - W (ret); - return ret; -} - -static int api_snat_add_static_mapping(vat_main_t * vam) -{ - unformat_input_t * i = vam->input; - vl_api_snat_add_static_mapping_t * mp; - u8 external_addr_set = 0; - u8 local_addr_set = 0; - u8 is_add = 1; - u8 addr_only = 1; - ip4_address_t local_addr, external_addr; - u32 local_port = 0, external_port = 0, vrf_id = ~0; - u32 sw_if_index = ~0; - u8 sw_if_index_set = 0; - u32 proto = ~0; - u8 proto_set = 0; - int ret; - - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) - { - if (unformat (i, "local_addr %U", unformat_ip4_address, &local_addr)) - local_addr_set = 1; - else if (unformat (i, "external_addr %U", unformat_ip4_address, - &external_addr)) - external_addr_set = 1; - else if (unformat (i, "local_port %u", &local_port)) - addr_only = 0; - else if (unformat (i, "external_port %u", &external_port)) - addr_only = 0; - else if (unformat (i, "external_if %U", unformat_sw_if_index, vam, - &sw_if_index)) - sw_if_index_set = 1; - else if (unformat (i, "external_sw_if_index %d", &sw_if_index)) - sw_if_index_set = 1; - else if (unformat (i, "vrf %u", &vrf_id)) - ; - else if (unformat (i, "protocol %u", &proto)) - proto_set = 1; - else if (unformat (i, "del")) - is_add = 0; - else - { - clib_warning("unknown input '%U'", format_unformat_error, i); - return -99; - } - } - - if (!addr_only && !proto_set) - { - errmsg ("protocol required\n"); - return -99; - } - - if (!local_addr_set) - { - errmsg ("local addr required\n"); - return -99; - } - if (!external_addr_set && !sw_if_index_set) - { - errmsg ("external addr or interface required\n"); - return -99; - } - - M(SNAT_ADD_STATIC_MAPPING, mp); - mp->is_add = is_add; - mp->is_ip4 = 1; - mp->addr_only = addr_only; - mp->local_port = ntohs ((u16) local_port); - mp->external_port = ntohs ((u16) external_port); - mp->external_sw_if_index = ntohl (sw_if_index); - mp->vrf_id = ntohl (vrf_id); - mp->protocol = (u8) proto; - memcpy (mp->local_ip_address, &local_addr, 4); - memcpy (mp->external_ip_address, &external_addr, 4); - - S(mp); - W (ret); - return ret; -} - -static void vl_api_snat_control_ping_reply_t_handler - (vl_api_snat_control_ping_reply_t * mp) -{ - vat_main_t *vam = &vat_main; - i32 retval = ntohl (mp->retval); - if (vam->async_mode) - { - vam->async_errors += (retval < 0); - } - else - { - vam->retval = retval; - vam->result_ready = 1; - } -} - -static void vl_api_snat_static_mapping_details_t_handler - (vl_api_snat_static_mapping_details_t *mp) -{ - snat_test_main_t * sm = &snat_test_main; - vat_main_t *vam = sm->vat_main; - - if (mp->addr_only && mp->external_sw_if_index != ~0) - fformat (vam->ofp, "%15U%6s%15d%6s%11d%6d\n", - format_ip4_address, &mp->local_ip_address, "", - ntohl (mp->external_sw_if_index), "", - ntohl (mp->vrf_id), - mp->protocol); - else if (mp->addr_only && mp->external_sw_if_index == ~0) - fformat (vam->ofp, "%15U%6s%15U%6s%11d%6d\n", - format_ip4_address, &mp->local_ip_address, "", - format_ip4_address, &mp->external_ip_address, "", - ntohl (mp->vrf_id), - mp->protocol); - else if (!mp->addr_only && mp->external_sw_if_index != ~0) - fformat (vam->ofp, "%15U%6d%15d%6d%11d%6d\n", - format_ip4_address, &mp->local_ip_address, - ntohs (mp->local_port), - ntohl (mp->external_sw_if_index), - ntohs (mp->external_port), - ntohl (mp->vrf_id), - mp->protocol); - else - fformat (vam->ofp, "%15U%6d%15U%6d%11d%6d\n", - format_ip4_address, &mp->local_ip_address, - ntohs (mp->local_port), - format_ip4_address, &mp->external_ip_address, - ntohs (mp->external_port), - ntohl (mp->vrf_id), - mp->protocol); - -} - -static int api_snat_static_mapping_dump(vat_main_t * vam) -{ - vl_api_snat_static_mapping_dump_t * mp; - vl_api_snat_control_ping_t *mp_ping; - int ret; - - if (vam->json_output) - { - clib_warning ("JSON output not supported for snat_static_mapping_dump"); - return -99; - } - - fformat (vam->ofp, "%21s%21s\n", "local", "external"); - fformat (vam->ofp, "%15s%6s%15s%6s%11s%6s\n", "address", "port", - "address/if_idx", "port", "vrf", "proto"); - - M(SNAT_STATIC_MAPPING_DUMP, mp); - S(mp); - - /* Use a control ping for synchronization */ - M(SNAT_CONTROL_PING, mp_ping); - S(mp_ping); - - W (ret); - return ret; -} - -static void vl_api_snat_show_config_reply_t_handler - (vl_api_snat_show_config_reply_t *mp) -{ - snat_test_main_t * sm = &snat_test_main; - vat_main_t *vam = sm->vat_main; - i32 retval = ntohl (mp->retval); - - if (retval >= 0) - { - fformat (vam->ofp, "translation hash buckets %d\n", - ntohl (mp->translation_buckets)); - fformat (vam->ofp, "translation hash memory %d\n", - ntohl (mp->translation_memory_size)); - fformat (vam->ofp, "user hash buckets %d\n", ntohl (mp->user_buckets)); - fformat (vam->ofp, "user hash memory %d\n", ntohl (mp->user_memory_size)); - fformat (vam->ofp, "max translations per user %d\n", - ntohl (mp->max_translations_per_user)); - fformat (vam->ofp, "outside VRF id %d\n", ntohl (mp->outside_vrf_id)); - fformat (vam->ofp, "inside VRF id %d\n", ntohl (mp->inside_vrf_id)); - if (mp->static_mapping_only) - { - fformat (vam->ofp, "static mapping only"); - if (mp->static_mapping_connection_tracking) - fformat (vam->ofp, " connection tracking"); - fformat (vam->ofp, "\n"); - } - } - vam->retval = retval; - vam->result_ready = 1; -} - -static int api_snat_show_config(vat_main_t * vam) -{ - vl_api_snat_show_config_t * mp; - int ret; - - if (vam->json_output) - { - clib_warning ("JSON output not supported for snat_show_config"); - return -99; - } - - M(SNAT_SHOW_CONFIG, mp); - S(mp); - W (ret); - return ret; -} - -static void vl_api_snat_address_details_t_handler - (vl_api_snat_address_details_t *mp) -{ - snat_test_main_t * sm = &snat_test_main; - vat_main_t *vam = sm->vat_main; - - fformat (vam->ofp, "%U\n", format_ip4_address, &mp->ip_address); -} - -static int api_snat_address_dump(vat_main_t * vam) -{ - vl_api_snat_address_dump_t * mp; - vl_api_snat_control_ping_t *mp_ping; - int ret; - - if (vam->json_output) - { - clib_warning ("JSON output not supported for snat_address_dump"); - return -99; - } - - M(SNAT_ADDRESS_DUMP, mp); - S(mp); - - /* Use a control ping for synchronization */ - M(SNAT_CONTROL_PING, mp_ping); - S(mp_ping); - - W (ret); - return ret; -} - -static void vl_api_snat_interface_details_t_handler - (vl_api_snat_interface_details_t *mp) -{ - snat_test_main_t * sm = &snat_test_main; - vat_main_t *vam = sm->vat_main; - - fformat (vam->ofp, "sw_if_index %d %s\n", ntohl (mp->sw_if_index), - mp->is_inside ? "in" : "out"); -} - -static int api_snat_interface_dump(vat_main_t * vam) -{ - vl_api_snat_interface_dump_t * mp; - vl_api_snat_control_ping_t *mp_ping; - int ret; - - if (vam->json_output) - { - clib_warning ("JSON output not supported for snat_address_dump"); - return -99; - } - - M(SNAT_INTERFACE_DUMP, mp); - S(mp); - - /* Use a control ping for synchronization */ - M(SNAT_CONTROL_PING, mp_ping); - S(mp_ping); - - W (ret); - return ret; -} - -static int api_snat_set_workers (vat_main_t * vam) -{ - unformat_input_t * i = vam->input; - vl_api_snat_set_workers_t * mp; - uword *bitmap; - int ret; - - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) - { - if (unformat (i, "%U", unformat_bitmap_list, &bitmap)) - ; - else - { - clib_warning("unknown input '%U'", format_unformat_error, i); - return -99; - } - } - - M(SNAT_SET_WORKERS, mp); - mp->worker_mask = clib_host_to_net_u64 (bitmap[0]); - - S(mp); - W (ret); - return ret; -} - -static void vl_api_snat_worker_details_t_handler - (vl_api_snat_worker_details_t *mp) -{ - snat_test_main_t * sm = &snat_test_main; - vat_main_t *vam = sm->vat_main; - - fformat (vam->ofp, "worker_index %d (%s at lcore %u)\n", - ntohl (mp->worker_index), mp->name, ntohl (mp->lcore_id)); -} - -static int api_snat_worker_dump(vat_main_t * vam) -{ - vl_api_snat_worker_dump_t * mp; - vl_api_snat_control_ping_t *mp_ping; - int ret; - - if (vam->json_output) - { - clib_warning ("JSON output not supported for snat_address_dump"); - return -99; - } - - M(SNAT_WORKER_DUMP, mp); - S(mp); - - /* Use a control ping for synchronization */ - M(SNAT_CONTROL_PING, mp_ping); - S(mp_ping); - - W (ret); - return ret; -} - -static int api_snat_add_del_interface_addr (vat_main_t * vam) -{ - unformat_input_t * i = vam->input; - vl_api_snat_add_del_interface_addr_t * mp; - u32 sw_if_index; - u8 sw_if_index_set = 0; - u8 is_add = 1; - int ret; - - 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, "del")) - is_add = 0; - else - { - clib_warning("unknown input '%U'", format_unformat_error, i); - return -99; - } - } - - if (sw_if_index_set == 0) - { - errmsg ("interface / sw_if_index required\n"); - return -99; - } - - M(SNAT_ADD_DEL_INTERFACE_ADDR, mp); - mp->sw_if_index = ntohl(sw_if_index); - mp->is_add = is_add; - - S(mp); - W (ret); - return ret; -} - -static void vl_api_snat_interface_addr_details_t_handler - (vl_api_snat_interface_addr_details_t *mp) -{ - snat_test_main_t * sm = &snat_test_main; - vat_main_t *vam = sm->vat_main; - - fformat (vam->ofp, "sw_if_index %d\n", ntohl (mp->sw_if_index)); -} - -static int api_snat_interface_addr_dump(vat_main_t * vam) -{ - vl_api_snat_interface_addr_dump_t * mp; - vl_api_snat_control_ping_t *mp_ping; - int ret; - - if (vam->json_output) - { - clib_warning ("JSON output not supported for snat_address_dump"); - return -99; - } - - M(SNAT_INTERFACE_ADDR_DUMP, mp); - S(mp); - - /* Use a control ping for synchronization */ - M(SNAT_CONTROL_PING, mp_ping); - S(mp_ping); - - W (ret); - return ret; -} - -static int api_snat_ipfix_enable_disable (vat_main_t * vam) -{ - unformat_input_t * i = vam->input; - vl_api_snat_ipfix_enable_disable_t * mp; - u32 domain_id = 0; - u32 src_port = 0; - u8 enable = 1; - int ret; - - while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) - { - if (unformat (i, "domain %d", &domain_id)) - ; - else if (unformat (i, "src_port %d", &src_port)) - ; - else if (unformat (i, "disable")) - enable = 0; - else - { - clib_warning("unknown input '%U'", format_unformat_error, i); - return -99; - } - } - - M(SNAT_IPFIX_ENABLE_DISABLE, mp); - mp->domain_id = htonl(domain_id); - mp->src_port = htons((u16) src_port); - mp->enable = enable; - - S(mp); - W (ret); - return ret; -} - -static void vl_api_snat_user_session_details_t_handler - (vl_api_snat_user_session_details_t *mp) -{ - snat_test_main_t * sm = &snat_test_main; - vat_main_t *vam = sm->vat_main; - - fformat(vam->ofp, "%s session %U:%d to %U:%d protocol id %d " - "total packets %d total bytes %d\n", - mp->is_static ? "static" : "dynamic", - format_ip4_address, mp->inside_ip_address, ntohl(mp->inside_port), - format_ip4_address, mp->outside_ip_address, ntohl(mp->outside_port), - ntohl(mp->protocol), ntohl(mp->total_pkts), ntohl(mp->total_bytes)); -} - -static int api_snat_user_session_dump(vat_main_t * vam) -{ - unformat_input_t* i = vam->input; - vl_api_snat_user_session_dump_t * mp; - vl_api_snat_control_ping_t *mp_ping; - ip4_address_t addr; - u32 vrf_id = ~0; - int ret; - - if (vam->json_output) - { - clib_warning ("JSON output not supported for snat_address_dump"); - return -99; - } - - if (unformat (i, "ip_address %U vrf_id %d", - unformat_ip4_address, &addr, &vrf_id)) - ; - else - { - clib_warning("unknown input '%U'", format_unformat_error, i); - return -99; - } - - M(SNAT_USER_SESSION_DUMP, mp); - S(mp); - - /* Use a control ping for synchronization */ - M(SNAT_CONTROL_PING, mp_ping); - memset(mp->ip_address, 0, 16); - clib_memcpy(mp->ip_address, &addr, 4); - mp->vrf_id = htonl(vrf_id); - mp->is_ip4 = 1; - S(mp_ping); - - W (ret); - return ret; -} - -static void vl_api_snat_user_details_t_handler - (vl_api_snat_user_details_t *mp) -{ - snat_test_main_t * sm = &snat_test_main; - vat_main_t *vam = sm->vat_main; - - fformat(vam->ofp, "user with ip %U with vrf_id %d " - "with %d sessions and %d static sessions\n", - format_ip4_address, mp->ip_address, ntohl(mp->vrf_id), - ntohl(mp->nsessions), ntohl(mp->nstaticsessions)); -} - -static int api_snat_user_dump(vat_main_t * vam) -{ - vl_api_snat_user_dump_t * mp; - vl_api_snat_control_ping_t *mp_ping; - int ret; - - if (vam->json_output) - { - clib_warning ("JSON output not supported for snat_address_dump"); - return -99; - } - - M(SNAT_USER_DUMP, mp); - S(mp); - - /* Use a control ping for synchronization */ - M(SNAT_CONTROL_PING, mp_ping); - S(mp_ping); - - W (ret); - return ret; -} - -static int api_snat_add_det_map (vat_main_t * vam) -{ - unformat_input_t * i = vam->input; - vl_api_snat_add_det_map_t * mp; - ip4_address_t in_addr, out_addr; - u32 in_plen, out_plen; - u8 is_add = 1; - int ret; - - if (unformat (i, "in %U/%d out %U/%d", - unformat_ip4_address, &in_addr, &in_plen, - unformat_ip4_address, &out_addr, &out_plen)) - ; - else if (unformat (i, "del")) - is_add = 0; - else - { - clib_warning("unknown input '%U'", format_unformat_error, i); - return -99; - } - - M(SNAT_ADD_DET_MAP, mp); - clib_memcpy(mp->in_addr, &in_addr, 4); - mp->in_plen = in_plen; - clib_memcpy(mp->out_addr, &out_addr, 4); - mp->out_plen = out_plen; - mp->is_add = is_add; - - S(mp); - W (ret); - return ret; -} - -static void vl_api_snat_det_forward_reply_t_handler - (vl_api_snat_det_forward_reply_t *mp) -{ - snat_test_main_t * sm = &snat_test_main; - vat_main_t *vam = sm->vat_main; - i32 retval = ntohl(mp->retval); - - if (retval >= 0) - { - fformat (vam->ofp, "outside address %U", format_ip4_address, &mp->out_addr); - fformat (vam->ofp, " outside port range start %d", ntohs(mp->out_port_lo)); - fformat (vam->ofp, " outside port range end %d\n", ntohs(mp->out_port_hi)); - } - - vam->retval = retval; - vam->result_ready = 1; -} - -static int api_snat_det_forward (vat_main_t * vam) -{ - unformat_input_t * i = vam->input; - vl_api_snat_det_forward_t * mp; - ip4_address_t in_addr; - int ret; - - if (unformat (i, "%U", unformat_ip4_address, &in_addr)) - ; - else - { - clib_warning("unknown input '%U'", format_unformat_error, i); - return -99; - } - - M(SNAT_DET_FORWARD, mp); - clib_memcpy(mp->in_addr, &in_addr, 4); - - S(mp); - W(ret); - return ret; -} - -static void vl_api_snat_det_reverse_reply_t_handler - (vl_api_snat_det_reverse_reply_t *mp) -{ - snat_test_main_t * sm = &snat_test_main; - vat_main_t *vam = sm->vat_main; - i32 retval = ntohl(mp->retval); - - if (retval >= 0) - { - fformat (vam->ofp, "inside address %U\n", format_ip4_address, &mp->in_addr); - } - - vam->retval = retval; - vam->result_ready = 1; -} - -static int api_snat_det_reverse (vat_main_t * vam) -{ - unformat_input_t * i = vam->input; - vl_api_snat_det_reverse_t * mp; - ip4_address_t out_addr; - u16 out_port; - int ret; - - if (unformat (i, "%U %d", unformat_ip4_address, &out_addr, &out_port)) - ; - else - { - clib_warning("unknown input '%U'", format_unformat_error, i); - return -99; - } - - M(SNAT_DET_REVERSE, mp); - clib_memcpy(mp->out_addr, &out_addr, 4); - mp->out_port = htons(out_port); - - S(mp); - W(ret); - return ret; -} - -static void vl_api_snat_det_map_details_t_handler - (vl_api_snat_det_map_details_t *mp) -{ - snat_test_main_t * sm = &snat_test_main; - vat_main_t *vam = sm->vat_main; - - fformat (vam->ofp, "Deterministic S-NAT mapping in %U/%d out %U/%d " - "ports per host %d sharing ratio %d " - "number of sessions %d", - format_ip4_address, mp->in_addr, mp->in_plen, - format_ip4_address, mp->out_addr, mp->out_plen, - ntohs(mp->ports_per_host), ntohl(mp->sharing_ratio), - ntohl(mp->ses_num)); -} - -static int api_snat_det_map_dump(vat_main_t * vam) -{ - vl_api_snat_det_map_dump_t * mp; - vl_api_snat_control_ping_t *mp_ping; - int ret; - - if (vam->json_output) - { - clib_warning ("JSON output not supported for snat_det_map_dump"); - return -99; - } - - M(SNAT_DET_MAP_DUMP, mp); - S(mp); - - /* Use a control ping for synchronization */ - M(SNAT_CONTROL_PING, mp_ping); - S(mp_ping); - - W (ret); - return ret; -} - -static int api_snat_det_set_timeouts (vat_main_t * vam) -{ - unformat_input_t * i = vam->input; - vl_api_snat_det_set_timeouts_t * mp; - u32 udp = SNAT_UDP_TIMEOUT; - u32 tcp_established = SNAT_TCP_ESTABLISHED_TIMEOUT; - u32 tcp_transitory = SNAT_TCP_TRANSITORY_TIMEOUT; - u32 icmp = SNAT_ICMP_TIMEOUT; - int ret; - - if (unformat (i, "udp %d", &udp)) - ; - else if (unformat (i, "tcp_established %d", &tcp_established)) - ; - else if (unformat (i, "tcp_transitory %d", &tcp_transitory)) - ; - else if (unformat (i, "icmp %d", &icmp)) - ; - else - { - clib_warning("unknown input '%U'", format_unformat_error, i); - return -99; - } - - M(SNAT_DET_SET_TIMEOUTS, mp); - mp->udp = htonl(udp); - mp->tcp_established = htonl(tcp_established); - mp->tcp_transitory = htonl(tcp_transitory); - mp->icmp = htonl(icmp); - - S(mp); - W (ret); - return ret; -} - -static void vl_api_snat_det_get_timeouts_reply_t_handler - (vl_api_snat_det_get_timeouts_reply_t *mp) -{ - snat_test_main_t * sm = &snat_test_main; - vat_main_t *vam = sm->vat_main; - i32 retval = ntohl (mp->retval); - - if (retval >= 0) - { - fformat (vam->ofp, "udp timeout: %dsec\n", ntohl (mp->udp)); - fformat (vam->ofp, "tcp-established timeout: %dsec", - ntohl (mp->tcp_established)); - fformat (vam->ofp, "tcp-transitory timeout: %dsec", - ntohl (mp->tcp_transitory)); - fformat (vam->ofp, "icmp timeout: %dsec", ntohl (mp->icmp)); - } - vam->retval = retval; - vam->result_ready = 1; -} - -static int api_snat_det_get_timeouts(vat_main_t * vam) -{ - vl_api_snat_det_get_timeouts_t * mp; - int ret; - - if (vam->json_output) - { - clib_warning ("JSON output not supported for snat_show_config"); - return -99; - } - - M(SNAT_DET_GET_TIMEOUTS, mp); - S(mp); - W (ret); - return ret; -} - -static int api_snat_det_close_session_out (vat_main_t * vam) -{ - unformat_input_t * i = vam->input; - vl_api_snat_det_close_session_out_t * mp; - ip4_address_t out_addr, ext_addr; - u16 out_port, ext_port; - int ret; - - if (unformat (i, "%U:%d %U:%d", - unformat_ip4_address, &out_addr, &out_port, - unformat_ip4_address, &ext_addr, &ext_port)) - ; - else - { - clib_warning("unknown input '%U'", format_unformat_error, i); - return -99; - } - - M(SNAT_DET_CLOSE_SESSION_OUT, mp); - clib_memcpy(mp->out_addr, &out_addr, 4); - mp->out_port = ntohs(out_port); - clib_memcpy(mp->ext_addr, &ext_addr, 4); - mp->ext_port = ntohs(ext_port); - - S(mp); - W (ret); - return ret; -} - -static int api_snat_det_close_session_in (vat_main_t * vam) -{ - unformat_input_t * i = vam->input; - vl_api_snat_det_close_session_in_t * mp; - ip4_address_t in_addr, ext_addr; - u16 in_port, ext_port; - int ret; - - if (unformat (i, "%U:%d %U:%d", - unformat_ip4_address, &in_addr, &in_port, - unformat_ip4_address, &ext_addr, &ext_port)) - ; - else - { - clib_warning("unknown input '%U'", format_unformat_error, i); - return -99; - } - - M(SNAT_DET_CLOSE_SESSION_IN, mp); - clib_memcpy(mp->in_addr, &in_addr, 4); - mp->in_port = ntohs(in_port); - clib_memcpy(mp->ext_addr, &ext_addr, 4); - mp->ext_port = ntohs(ext_port); - - S(mp); - W (ret); - return ret; -} - -static void vl_api_snat_det_session_details_t_handler - (vl_api_snat_det_session_details_t *mp) -{ - snat_test_main_t * sm = &snat_test_main; - vat_main_t *vam = sm->vat_main; - - fformat(vam->ofp, "deterministic session, external host address %U, " - "external host port %d, outer port %d, inside port %d", - format_ip4_address, mp->ext_addr, mp->ext_port, - mp->out_port, mp->in_port); -} - -static int api_snat_det_session_dump(vat_main_t * vam) -{ - unformat_input_t* i = vam->input; - vl_api_snat_det_session_dump_t * mp; - vl_api_snat_control_ping_t *mp_ping; - ip4_address_t user_addr; - int ret; - - if (vam->json_output) - { - clib_warning ("JSON output not supported for snat_det_session_dump"); - return -99; - } - - if (unformat (i, "user_addr %U", unformat_ip4_address, &user_addr)) - ; - else - { - clib_warning ("unknown input '%U'", format_unformat_error, i); - return -99; - } - - M(SNAT_DET_SESSION_DUMP, mp); - clib_memcpy (&mp->user_addr, &user_addr, 4); - S(mp); - - /* Use a control ping for synchronization */ - M(SNAT_CONTROL_PING, mp_ping); - S(mp_ping); - - W (ret); - return ret; -} - -/* - * 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, " [- | sw_if_index [in] [out] [del]") \ -_(snat_add_static_mapping, "local_addr (external_addr " \ - " | external_if | external_sw_if_ndex ) " \ - "[local_port ] [external_port ] [vrf ] [del] " \ - "protocol ") \ -_(snat_set_workers, "") \ -_(snat_static_mapping_dump, "") \ -_(snat_show_config, "") \ -_(snat_address_dump, "") \ -_(snat_interface_dump, "") \ -_(snat_worker_dump, "") \ -_(snat_add_del_interface_addr, \ - " | sw_if_index [del]") \ -_(snat_interface_addr_dump, "") \ -_(snat_ipfix_enable_disable, "[domain ] [src_port ] " \ - "[disable]") \ -_(snat_user_dump, "") \ -_(snat_user_session_dump, "ip_address vrf_id ") \ -_(snat_add_det_map, "in / out " \ - "/ [del]") \ -_(snat_det_forward, "") \ -_(snat_det_reverse, " ") \ -_(snat_det_map_dump, "") \ -_(snat_det_set_timeouts, "[udp | tcp_established | " \ - "tcp_transitory | icmp ]") \ -_(snat_det_get_timeouts, "") \ -_(snat_det_close_session_out, ": " \ - ":") \ -_(snat_det_close_session_in, ": " \ - ":") \ -_(snat_det_session_dump, "ip_address ") - -static void -snat_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) - snat_vat_api_hookup (vam); - - vec_free(name); - - return 0; -} diff --git a/src/scripts/vnet/nat44 b/src/scripts/vnet/nat44 new file mode 100644 index 00000000..3292b565 --- /dev/null +++ b/src/scripts/vnet/nat44 @@ -0,0 +1,41 @@ +create packet-generator interface pg0 +create packet-generator interface pg1 + +packet-generator new { + name f1 + limit 1000000 + node ip4-input + size 64-64 + no-recycle + interface pg0 + data { + UDP: 10.0.0.3 -> 172.16.1.2 + UDP: 3000 -> 3001 + length 128 checksum 0 incrementing 1 + } +} + + +packet-generator new { + name f2 + limit 1000000 + node ip4-input + size 64-64 + no-recycle + interface pg0 + data { + UDP: 10.0.0.3 -> 172.16.1.2 + UDP: 3005 -> 3006 + length 128 checksum 0 incrementing 1 + } +} + +nat44 add address 172.16.1.3 +set int ip address pg0 10.0.0.1/24 +set int ip address pg1 172.16.1.1/24 +set int state pg0 up +set int state pg1 up +set ip arp static pg0 10.0.0.3 abcd.abcd.abcd +set ip arp static pg0 10.0.0.4 abcd.abcd.abcd +set ip arp static pg1 172.16.1.2 cdef.abcd.abcd +set int nat44 in pg0 out pg1 diff --git a/src/scripts/vnet/nat44_det b/src/scripts/vnet/nat44_det new file mode 100644 index 00000000..629772e6 --- /dev/null +++ b/src/scripts/vnet/nat44_det @@ -0,0 +1,108 @@ +create packet-generator interface pg0 +create packet-generator interface pg1 + +packet-generator new { + name f1 + limit 1000000 + node ip4-input + size 64-64 + no-recycle + worker 0 + interface pg0 + data { + UDP: 10.0.0.3 -> 172.16.1.2 + UDP: 3000 -> 3001 + length 128 checksum 0 incrementing 1 + } +} + + +packet-generator new { + name f2 + limit 1000000 + node ip4-input + size 64-64 + no-recycle + worker 1 + interface pg0 + data { + UDP: 10.0.0.3 -> 172.16.1.2 + UDP: 3005 -> 3006 + length 128 checksum 0 incrementing 1 + } +} + + +packet-generator new { + name f3 + limit 1000000 + node ip4-input + size 64-64 + no-recycle + worker 0 + interface pg1 + data { + UDP: 172.16.1.2 -> 1.1.1.2 + UDP: 3001 -> 1141 + length 128 checksum 0 incrementing 1 + } +} + + +packet-generator new { + name f4 + limit 1000000 + node ip4-input + size 64-64 + no-recycle + worker 1 + interface pg1 + data { + UDP: 172.16.1.2 -> 1.1.1.2 + UDP: 3006 -> 1146 + length 128 checksum 0 incrementing 1 + } +} + + +packet-generator new { + name f5 + limit 1000000 + node ip4-input + size 64-64 + no-recycle + worker 2 + interface pg0 + data { + UDP: 10.0.0.4 -> 172.16.1.2 + UDP: 3005 -> 3006 + length 128 checksum 0 incrementing 1 + } +} + + +packet-generator new { + name f6 + limit 1000000 + node ip4-input + size 64-64 + no-recycle + worker 1 + interface pg1 + data { + UDP: 172.16.1.2 -> 1.1.1.2 + UDP: 3006 -> 1177 + length 128 checksum 0 incrementing 1 + } +} + +nat44 deterministic add in 10.0.0.0/21 out 1.1.1.2/32 +set int ip address pg0 10.0.0.1/24 +set int ip address pg1 172.16.1.1/24 +set int state pg0 up +set int state pg1 up +set ip arp static pg0 10.0.0.3 abcd.abcd.abcd +set ip arp static pg0 10.0.0.4 abcd.abcd.abcd +set ip arp static pg1 172.16.1.2 cdef.abcd.abcd +set int nat44 in pg0 out pg1 +trace add pg-input 10 diff --git a/src/scripts/vnet/nat44_static b/src/scripts/vnet/nat44_static new file mode 100644 index 00000000..2b8f25ec --- /dev/null +++ b/src/scripts/vnet/nat44_static @@ -0,0 +1,44 @@ +create packet-generator interface pg0 +create packet-generator interface pg1 + +packet-generator new { + name f1 + limit 1000000 + node ip4-input + size 64-64 + no-recycle + worker 0 + interface pg0 + data { + UDP: 10.0.0.3 -> 172.16.1.2 + UDP: 3000 -> 3001 + length 128 checksum 0 incrementing 1 + } +} + + +packet-generator new { + name f2 + limit 1000000 + node ip4-input + size 64-64 + no-recycle + worker 1 + interface pg1 + data { + UDP: 172.16.1.2 -> 172.16.1.3 + UDP: 3001 -> 3000 + length 128 checksum 0 incrementing 1 + } +} + +nat44 add address 172.16.1.3 +nat44 add static mapping local 10.0.0.3 external 172.16.1.3 +set int ip address pg0 10.0.0.1/24 +set int ip address pg1 172.16.1.1/24 +set int state pg0 up +set int state pg1 up +set ip arp static pg0 10.0.0.3 abcd.abcd.abcd +set ip arp static pg1 172.16.1.2 cdef.abcd.abcd +set int nat44 in pg0 out pg1 +trace add pg-input 10 diff --git a/src/scripts/vnet/nat44_static_with_port b/src/scripts/vnet/nat44_static_with_port new file mode 100644 index 00000000..15bef1be --- /dev/null +++ b/src/scripts/vnet/nat44_static_with_port @@ -0,0 +1,44 @@ +create packet-generator interface pg0 +create packet-generator interface pg1 + +packet-generator new { + name f1 + limit 1000000 + node ip4-input + size 64-64 + no-recycle + worker 0 + interface pg0 + data { + UDP: 10.0.0.3 -> 172.16.1.2 + UDP: 3000 -> 3001 + length 128 checksum 0 incrementing 1 + } +} + + +packet-generator new { + name f2 + limit 1000000 + node ip4-input + size 64-64 + no-recycle + worker 1 + interface pg1 + data { + UDP: 172.16.1.2 -> 172.16.1.3 + UDP: 3001 -> 3000 + length 128 checksum 0 incrementing 1 + } +} + +nat44 add address 172.16.1.3 +nat44 add static mapping local 10.0.0.3 3000 external 172.16.1.3 3000 +set int ip address pg0 10.0.0.1/24 +set int ip address pg1 172.16.1.1/24 +set int state pg0 up +set int state pg1 up +set ip arp static pg0 10.0.0.3 abcd.abcd.abcd +set ip arp static pg1 172.16.1.2 cdef.abcd.abcd +set int nat44 in pg0 out pg1 +trace add pg-input 10 diff --git a/src/scripts/vnet/snat b/src/scripts/vnet/snat deleted file mode 100644 index a711519e..00000000 --- a/src/scripts/vnet/snat +++ /dev/null @@ -1,41 +0,0 @@ -create packet-generator interface pg0 -create packet-generator interface pg1 - -packet-generator new { - name f1 - limit 1000000 - node ip4-input - size 64-64 - no-recycle - interface pg0 - data { - UDP: 10.0.0.3 -> 172.16.1.2 - UDP: 3000 -> 3001 - length 128 checksum 0 incrementing 1 - } -} - - -packet-generator new { - name f2 - limit 1000000 - node ip4-input - size 64-64 - no-recycle - interface pg0 - data { - UDP: 10.0.0.3 -> 172.16.1.2 - UDP: 3005 -> 3006 - length 128 checksum 0 incrementing 1 - } -} - -snat add address 172.16.1.3 -set int ip address pg0 10.0.0.1/24 -set int ip address pg1 172.16.1.1/24 -set int state pg0 up -set int state pg1 up -set ip arp static pg0 10.0.0.3 abcd.abcd.abcd -set ip arp static pg0 10.0.0.4 abcd.abcd.abcd -set ip arp static pg1 172.16.1.2 cdef.abcd.abcd -set int snat in pg0 out pg1 diff --git a/src/scripts/vnet/snat_det b/src/scripts/vnet/snat_det deleted file mode 100644 index d1361bb1..00000000 --- a/src/scripts/vnet/snat_det +++ /dev/null @@ -1,108 +0,0 @@ -create packet-generator interface pg0 -create packet-generator interface pg1 - -packet-generator new { - name f1 - limit 1000000 - node ip4-input - size 64-64 - no-recycle - worker 0 - interface pg0 - data { - UDP: 10.0.0.3 -> 172.16.1.2 - UDP: 3000 -> 3001 - length 128 checksum 0 incrementing 1 - } -} - - -packet-generator new { - name f2 - limit 1000000 - node ip4-input - size 64-64 - no-recycle - worker 1 - interface pg0 - data { - UDP: 10.0.0.3 -> 172.16.1.2 - UDP: 3005 -> 3006 - length 128 checksum 0 incrementing 1 - } -} - - -packet-generator new { - name f3 - limit 1000000 - node ip4-input - size 64-64 - no-recycle - worker 0 - interface pg1 - data { - UDP: 172.16.1.2 -> 1.1.1.2 - UDP: 3001 -> 1141 - length 128 checksum 0 incrementing 1 - } -} - - -packet-generator new { - name f4 - limit 1000000 - node ip4-input - size 64-64 - no-recycle - worker 1 - interface pg1 - data { - UDP: 172.16.1.2 -> 1.1.1.2 - UDP: 3006 -> 1146 - length 128 checksum 0 incrementing 1 - } -} - - -packet-generator new { - name f5 - limit 1000000 - node ip4-input - size 64-64 - no-recycle - worker 2 - interface pg0 - data { - UDP: 10.0.0.4 -> 172.16.1.2 - UDP: 3005 -> 3006 - length 128 checksum 0 incrementing 1 - } -} - - -packet-generator new { - name f6 - limit 1000000 - node ip4-input - size 64-64 - no-recycle - worker 1 - interface pg1 - data { - UDP: 172.16.1.2 -> 1.1.1.2 - UDP: 3006 -> 1177 - length 128 checksum 0 incrementing 1 - } -} - -snat deterministic add in 10.0.0.0/21 out 1.1.1.2/32 -set int ip address pg0 10.0.0.1/24 -set int ip address pg1 172.16.1.1/24 -set int state pg0 up -set int state pg1 up -set ip arp static pg0 10.0.0.3 abcd.abcd.abcd -set ip arp static pg0 10.0.0.4 abcd.abcd.abcd -set ip arp static pg1 172.16.1.2 cdef.abcd.abcd -set int snat in pg0 out pg1 -trace add pg-input 10 diff --git a/src/scripts/vnet/snat_static b/src/scripts/vnet/snat_static deleted file mode 100644 index 8fe48bff..00000000 --- a/src/scripts/vnet/snat_static +++ /dev/null @@ -1,44 +0,0 @@ -create packet-generator interface pg0 -create packet-generator interface pg1 - -packet-generator new { - name f1 - limit 1000000 - node ip4-input - size 64-64 - no-recycle - worker 0 - interface pg0 - data { - UDP: 10.0.0.3 -> 172.16.1.2 - UDP: 3000 -> 3001 - length 128 checksum 0 incrementing 1 - } -} - - -packet-generator new { - name f2 - limit 1000000 - node ip4-input - size 64-64 - no-recycle - worker 1 - interface pg1 - data { - UDP: 172.16.1.2 -> 172.16.1.3 - UDP: 3001 -> 3000 - length 128 checksum 0 incrementing 1 - } -} - -snat add address 172.16.1.3 -snat add static mapping local 10.0.0.3 external 172.16.1.3 -set int ip address pg0 10.0.0.1/24 -set int ip address pg1 172.16.1.1/24 -set int state pg0 up -set int state pg1 up -set ip arp static pg0 10.0.0.3 abcd.abcd.abcd -set ip arp static pg1 172.16.1.2 cdef.abcd.abcd -set int snat in pg0 out pg1 -trace add pg-input 10 diff --git a/src/scripts/vnet/snat_static_with_port b/src/scripts/vnet/snat_static_with_port deleted file mode 100644 index f646145a..00000000 --- a/src/scripts/vnet/snat_static_with_port +++ /dev/null @@ -1,44 +0,0 @@ -create packet-generator interface pg0 -create packet-generator interface pg1 - -packet-generator new { - name f1 - limit 1000000 - node ip4-input - size 64-64 - no-recycle - worker 0 - interface pg0 - data { - UDP: 10.0.0.3 -> 172.16.1.2 - UDP: 3000 -> 3001 - length 128 checksum 0 incrementing 1 - } -} - - -packet-generator new { - name f2 - limit 1000000 - node ip4-input - size 64-64 - no-recycle - worker 1 - interface pg1 - data { - UDP: 172.16.1.2 -> 172.16.1.3 - UDP: 3001 -> 3000 - length 128 checksum 0 incrementing 1 - } -} - -snat add address 172.16.1.3 -snat add static mapping local 10.0.0.3 3000 external 172.16.1.3 3000 -set int ip address pg0 10.0.0.1/24 -set int ip address pg1 172.16.1.1/24 -set int state pg0 up -set int state pg1 up -set ip arp static pg0 10.0.0.3 abcd.abcd.abcd -set ip arp static pg1 172.16.1.2 cdef.abcd.abcd -set int snat in pg0 out pg1 -trace add pg-input 10 diff --git a/src/vpp-api/java/Makefile.am b/src/vpp-api/java/Makefile.am index e0f5203d..637bb774 100644 --- a/src/vpp-api/java/Makefile.am +++ b/src/vpp-api/java/Makefile.am @@ -169,23 +169,23 @@ jvpp-pppoe/io_fd_vpp_jvpp_pppoe_JVppPppoeImpl.h: $(jvpp_registry_ok) $(jvpp_pppo endif # -# SNAT Plugin +# NAT Plugin # -if ENABLE_SNAT_PLUGIN -noinst_LTLIBRARIES += libjvpp_snat.la -libjvpp_snat_la_SOURCES = jvpp-snat/jvpp_snat.c -libjvpp_snat_la_CPPFLAGS = -Ijvpp-snat -libjvpp_snat_la_LIBADD = $(JVPP_LIBS) -libjvpp_snat_la_DEPENDENCIES = libjvpp_common.la +if ENABLE_NAT_PLUGIN +noinst_LTLIBRARIES += libjvpp_nat.la +libjvpp_nat_la_SOURCES = jvpp-nat/jvpp_nat.c +libjvpp_nat_la_CPPFLAGS = -Ijvpp-nat +libjvpp_nat_la_LIBADD = $(JVPP_LIBS) +libjvpp_nat_la_DEPENDENCIES = libjvpp_common.la -BUILT_SOURCES += jvpp-snat/io_fd_vpp_jvpp_snat_JVppSnatImpl.h -JAR_FILES += jvpp-snat-$(PACKAGE_VERSION).jar -CLEANDIRS += jvpp-snat/target +BUILT_SOURCES += jvpp-nat/io_fd_vpp_jvpp_nat_JVppNatImpl.h +JAR_FILES += jvpp-nat-$(PACKAGE_VERSION).jar +CLEANDIRS += jvpp-nat/target -jvpp_snat_json_files = @top_builddir@/plugins/snat/snat.api.json +jvpp_nat_json_files = @top_builddir@/plugins/nat/nat.api.json -jvpp-snat/io_fd_vpp_jvpp_snat_JVppSnatImpl.h: $(jvpp_registry_ok) $(jvpp_snat_json_files) - $(call japigen,snat,JVppSnatImpl) +jvpp-nat/io_fd_vpp_jvpp_nat_JVppNatImpl.h: $(jvpp_registry_ok) $(jvpp_nat_json_files) + $(call japigen,nat,JVppNatImpl) endif # diff --git a/src/vpp-api/java/jvpp-nat/io/fd/vpp/jvpp/nat/examples/CallbackApiExample.java b/src/vpp-api/java/jvpp-nat/io/fd/vpp/jvpp/nat/examples/CallbackApiExample.java new file mode 100644 index 00000000..e4d5cb33 --- /dev/null +++ b/src/vpp-api/java/jvpp-nat/io/fd/vpp/jvpp/nat/examples/CallbackApiExample.java @@ -0,0 +1,68 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.nat.examples; + +import io.fd.vpp.jvpp.JVpp; +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.VppCallbackException; +import io.fd.vpp.jvpp.nat.JVppNatImpl; +import io.fd.vpp.jvpp.nat.callback.Nat44InterfaceAddDelFeatureCallback; +import io.fd.vpp.jvpp.nat.dto.Nat44InterfaceAddDelFeature; +import io.fd.vpp.jvpp.nat.dto.Nat44InterfaceAddDelFeatureReply; + +public class CallbackApiExample { + + static class TestCallback implements Nat44InterfaceAddDelFeatureCallback { + + @Override + public void onNat44InterfaceAddDelFeatureReply(final Nat44InterfaceAddDelFeatureReply msg) { + System.out.printf("Received Nat44InterfaceAddDelFeatureReply: context=%d%n", + msg.context); + } + + @Override + public void onError(VppCallbackException ex) { + System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", ex.getMethodName(), + ex.getCtxId(), ex.getErrorCode()); + } + } + + public static void main(String[] args) throws Exception { + testCallbackApi(); + } + + private static void testCallbackApi() throws Exception { + System.out.println("Testing Java callback API for nat plugin"); + try (final JVppRegistry registry = new JVppRegistryImpl("NatCallbackApiTest"); + final JVpp jvpp = new JVppNatImpl()) { + registry.register(jvpp, new TestCallback()); + + System.out.println("Sending Nat44InterfaceAddDelFeature request..."); + Nat44InterfaceAddDelFeature request = new Nat44InterfaceAddDelFeature(); + request.isAdd = 1; + request.isInside = 1; + request.swIfIndex = 1; + final int result = jvpp.send(request); + System.out.printf("Nat44InterfaceAddDelFeature send result = %d%n", result); + + Thread.sleep(1000); + + System.out.println("Disconnecting..."); + } + } +} diff --git a/src/vpp-api/java/jvpp-nat/io/fd/vpp/jvpp/nat/examples/Readme.txt b/src/vpp-api/java/jvpp-nat/io/fd/vpp/jvpp/nat/examples/Readme.txt new file mode 100644 index 00000000..ac75e04e --- /dev/null +++ b/src/vpp-api/java/jvpp-nat/io/fd/vpp/jvpp/nat/examples/Readme.txt @@ -0,0 +1 @@ +sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp-native/vpp/vpp-api/java/jvpp-nat-17.10.jar io.fd.vpp.jvpp.nat.examples.CallbackApiExample diff --git a/src/vpp-api/java/jvpp-nat/jvpp_nat.c b/src/vpp-api/java/jvpp-nat/jvpp_nat.c new file mode 100644 index 00000000..85217f04 --- /dev/null +++ b/src/vpp-api/java/jvpp-nat/jvpp_nat.c @@ -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 + +#include +#define vl_typedefs /* define message structures */ +#include +#undef vl_typedefs + +#include +#include +#include + +#if VPPJNI_DEBUG == 1 + #define DEBUG_LOG(...) clib_warning(__VA_ARGS__) +#else + #define DEBUG_LOG(...) +#endif + +#include + +#include "jvpp-nat/io_fd_vpp_jvpp_nat_JVppNatImpl.h" +#include "jvpp_nat.h" +#include "jvpp-nat/jvpp_nat_gen.h" + +/* + * Class: io_fd_vpp_jvpp_nat_JVppNatImpl + * Method: init0 + * Signature: (JI)V + */ +JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_nat_JVppNatImpl_init0 + (JNIEnv *env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) { + nat_main_t * plugin_main = &nat_main; + clib_warning ("Java_io_fd_vpp_jvpp_nat_JVppNatImpl_init0"); + + plugin_main->my_client_index = my_client_index; + plugin_main->vl_input_queue = uword_to_pointer (queue_address, unix_shared_memory_queue_t *); + + 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_nat_JVppNatImpl_close0 +(JNIEnv *env, jclass clazz) { + nat_main_t * plugin_main = &nat_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 SNAT */ +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 SNAT */ +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); +} diff --git a/src/vpp-api/java/jvpp-nat/jvpp_nat.h b/src/vpp-api/java/jvpp-nat/jvpp_nat.h new file mode 100644 index 00000000..c8f6b683 --- /dev/null +++ b/src/vpp-api/java/jvpp-nat/jvpp_nat.h @@ -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_nat_h__ +#define __included_jvpp_nat_h__ + +#include +#include +#include +#include +#include +#include + +/* Global state for JVPP-NAT */ +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; + +} nat_main_t; + +nat_main_t nat_main __attribute__((aligned (64))); + + +#endif /* __included_jvpp_nat_h__ */ diff --git a/src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/examples/CallbackApiExample.java b/src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/examples/CallbackApiExample.java deleted file mode 100644 index f4a2943f..00000000 --- a/src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/examples/CallbackApiExample.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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. - */ - -package io.fd.vpp.jvpp.snat.examples; - -import io.fd.vpp.jvpp.JVpp; -import io.fd.vpp.jvpp.JVppRegistry; -import io.fd.vpp.jvpp.JVppRegistryImpl; -import io.fd.vpp.jvpp.VppCallbackException; -import io.fd.vpp.jvpp.snat.JVppSnatImpl; -import io.fd.vpp.jvpp.snat.callback.SnatInterfaceAddDelFeatureCallback; -import io.fd.vpp.jvpp.snat.dto.SnatInterfaceAddDelFeature; -import io.fd.vpp.jvpp.snat.dto.SnatInterfaceAddDelFeatureReply; - -public class CallbackApiExample { - - static class TestCallback implements SnatInterfaceAddDelFeatureCallback { - - @Override - public void onSnatInterfaceAddDelFeatureReply(final SnatInterfaceAddDelFeatureReply msg) { - System.out.printf("Received SnatInterfaceAddDelFeatureReply: context=%d%n", - msg.context); - } - - @Override - public void onError(VppCallbackException ex) { - System.out.printf("Received onError exception: call=%s, context=%d, retval=%d%n", ex.getMethodName(), - ex.getCtxId(), ex.getErrorCode()); - } - } - - public static void main(String[] args) throws Exception { - testCallbackApi(); - } - - private static void testCallbackApi() throws Exception { - System.out.println("Testing Java callback API for snat plugin"); - try (final JVppRegistry registry = new JVppRegistryImpl("SnatCallbackApiTest"); - final JVpp jvpp = new JVppSnatImpl()) { - registry.register(jvpp, new TestCallback()); - - System.out.println("Sending SnatInterfaceAddDelFeature request..."); - SnatInterfaceAddDelFeature request = new SnatInterfaceAddDelFeature(); - request.isAdd = 1; - request.isInside = 1; - request.swIfIndex = 1; - final int result = jvpp.send(request); - System.out.printf("SnatInterfaceAddDelFeature send result = %d%n", result); - - Thread.sleep(1000); - - System.out.println("Disconnecting..."); - } - } -} diff --git a/src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/examples/Readme.txt b/src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/examples/Readme.txt deleted file mode 100644 index 470850ee..00000000 --- a/src/vpp-api/java/jvpp-snat/io/fd/vpp/jvpp/snat/examples/Readme.txt +++ /dev/null @@ -1 +0,0 @@ -sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp-native/vpp/vpp-api/java/jvpp-snat-17.10.jar io.fd.vpp.jvpp.snat.examples.CallbackApiExample diff --git a/src/vpp-api/java/jvpp-snat/jvpp_snat.c b/src/vpp-api/java/jvpp-snat/jvpp_snat.c deleted file mode 100644 index 5fd6a88b..00000000 --- a/src/vpp-api/java/jvpp-snat/jvpp_snat.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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 - -#include -#define vl_typedefs /* define message structures */ -#include -#undef vl_typedefs - -#include -#include -#include - -#if VPPJNI_DEBUG == 1 - #define DEBUG_LOG(...) clib_warning(__VA_ARGS__) -#else - #define DEBUG_LOG(...) -#endif - -#include - -#include "jvpp-snat/io_fd_vpp_jvpp_snat_JVppSnatImpl.h" -#include "jvpp_snat.h" -#include "jvpp-snat/jvpp_snat_gen.h" - -/* - * Class: io_fd_vpp_jvpp_snat_JVppsnatImpl - * Method: init0 - * Signature: (JI)V - */ -JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_snat_JVppSnatImpl_init0 - (JNIEnv *env, jclass clazz, jobject callback, jlong queue_address, jint my_client_index) { - snat_main_t * plugin_main = &snat_main; - clib_warning ("Java_io_fd_vpp_jvpp_snat_JVppSnatImpl_init0"); - - plugin_main->my_client_index = my_client_index; - plugin_main->vl_input_queue = uword_to_pointer (queue_address, unix_shared_memory_queue_t *); - - 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_snat_JVppSnatImpl_close0 -(JNIEnv *env, jclass clazz) { - snat_main_t * plugin_main = &snat_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 SNAT */ -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 SNAT */ -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); -} diff --git a/src/vpp-api/java/jvpp-snat/jvpp_snat.h b/src/vpp-api/java/jvpp-snat/jvpp_snat.h deleted file mode 100644 index 7739a411..00000000 --- a/src/vpp-api/java/jvpp-snat/jvpp_snat.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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_snat_h__ -#define __included_jvpp_snat_h__ - -#include -#include -#include -#include -#include -#include - -/* Global state for JVPP-SNAT */ -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; - -} snat_main_t; - -snat_main_t snat_main __attribute__((aligned (64))); - - -#endif /* __included_jvpp_snat_h__ */ diff --git a/test/test_nat.py b/test/test_nat.py new file mode 100644 index 00000000..0d622b08 --- /dev/null +++ b/test/test_nat.py @@ -0,0 +1,3720 @@ +#!/usr/bin/env python + +import socket +import unittest +import struct + +from framework import VppTestCase, VppTestRunner, running_extended_tests +from scapy.layers.inet import IP, TCP, UDP, ICMP +from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror +from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest, ICMPv6EchoReply +from scapy.layers.inet6 import ICMPv6DestUnreach, IPerror6 +from scapy.layers.l2 import Ether, ARP, GRE +from scapy.data import IP_PROTOS +from scapy.packet import bind_layers +from util import ppp +from ipfix import IPFIX, Set, Template, Data, IPFIXDecoder +from time import sleep + + +class MethodHolder(VppTestCase): + """ NAT create capture and verify method holder """ + + @classmethod + def setUpClass(cls): + super(MethodHolder, cls).setUpClass() + + def tearDown(self): + super(MethodHolder, self).tearDown() + + def check_ip_checksum(self, pkt): + """ + Check IP checksum of the packet + + :param pkt: Packet to check IP checksum + """ + new = pkt.__class__(str(pkt)) + del new['IP'].chksum + new = new.__class__(str(new)) + self.assertEqual(new['IP'].chksum, pkt['IP'].chksum) + + def check_tcp_checksum(self, pkt): + """ + Check TCP checksum in IP packet + + :param pkt: Packet to check TCP checksum + """ + new = pkt.__class__(str(pkt)) + del new['TCP'].chksum + new = new.__class__(str(new)) + self.assertEqual(new['TCP'].chksum, pkt['TCP'].chksum) + + def check_udp_checksum(self, pkt): + """ + Check UDP checksum in IP packet + + :param pkt: Packet to check UDP checksum + """ + new = pkt.__class__(str(pkt)) + del new['UDP'].chksum + new = new.__class__(str(new)) + self.assertEqual(new['UDP'].chksum, pkt['UDP'].chksum) + + def check_icmp_errror_embedded(self, pkt): + """ + Check ICMP error embeded packet checksum + + :param pkt: Packet to check ICMP error embeded packet checksum + """ + if pkt.haslayer(IPerror): + new = pkt.__class__(str(pkt)) + del new['IPerror'].chksum + new = new.__class__(str(new)) + self.assertEqual(new['IPerror'].chksum, pkt['IPerror'].chksum) + + if pkt.haslayer(TCPerror): + new = pkt.__class__(str(pkt)) + del new['TCPerror'].chksum + new = new.__class__(str(new)) + self.assertEqual(new['TCPerror'].chksum, pkt['TCPerror'].chksum) + + if pkt.haslayer(UDPerror): + if pkt['UDPerror'].chksum != 0: + new = pkt.__class__(str(pkt)) + del new['UDPerror'].chksum + new = new.__class__(str(new)) + self.assertEqual(new['UDPerror'].chksum, + pkt['UDPerror'].chksum) + + if pkt.haslayer(ICMPerror): + del new['ICMPerror'].chksum + new = new.__class__(str(new)) + self.assertEqual(new['ICMPerror'].chksum, pkt['ICMPerror'].chksum) + + def check_icmp_checksum(self, pkt): + """ + Check ICMP checksum in IPv4 packet + + :param pkt: Packet to check ICMP checksum + """ + new = pkt.__class__(str(pkt)) + del new['ICMP'].chksum + new = new.__class__(str(new)) + self.assertEqual(new['ICMP'].chksum, pkt['ICMP'].chksum) + if pkt.haslayer(IPerror): + self.check_icmp_errror_embedded(pkt) + + def check_icmpv6_checksum(self, pkt): + """ + Check ICMPv6 checksum in IPv4 packet + + :param pkt: Packet to check ICMPv6 checksum + """ + new = pkt.__class__(str(pkt)) + if pkt.haslayer(ICMPv6DestUnreach): + del new['ICMPv6DestUnreach'].cksum + new = new.__class__(str(new)) + self.assertEqual(new['ICMPv6DestUnreach'].cksum, + pkt['ICMPv6DestUnreach'].cksum) + self.check_icmp_errror_embedded(pkt) + if pkt.haslayer(ICMPv6EchoRequest): + del new['ICMPv6EchoRequest'].cksum + new = new.__class__(str(new)) + self.assertEqual(new['ICMPv6EchoRequest'].cksum, + pkt['ICMPv6EchoRequest'].cksum) + if pkt.haslayer(ICMPv6EchoReply): + del new['ICMPv6EchoReply'].cksum + new = new.__class__(str(new)) + self.assertEqual(new['ICMPv6EchoReply'].cksum, + pkt['ICMPv6EchoReply'].cksum) + + def create_stream_in(self, in_if, out_if, ttl=64): + """ + Create packet stream for inside network + + :param in_if: Inside interface + :param out_if: Outside interface + :param ttl: TTL of generated packets + """ + pkts = [] + # TCP + p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / + IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl) / + TCP(sport=self.tcp_port_in, dport=20)) + pkts.append(p) + + # UDP + p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / + IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl) / + UDP(sport=self.udp_port_in, dport=20)) + pkts.append(p) + + # ICMP + p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / + IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl) / + ICMP(id=self.icmp_id_in, type='echo-request')) + pkts.append(p) + + return pkts + + def compose_ip6(self, ip4, pref, plen): + """ + Compose IPv4-embedded IPv6 addresses + + :param ip4: IPv4 address + :param pref: IPv6 prefix + :param plen: IPv6 prefix length + :returns: IPv4-embedded IPv6 addresses + """ + pref_n = list(socket.inet_pton(socket.AF_INET6, pref)) + ip4_n = list(socket.inet_pton(socket.AF_INET, ip4)) + if plen == 32: + pref_n[4] = ip4_n[0] + pref_n[5] = ip4_n[1] + pref_n[6] = ip4_n[2] + pref_n[7] = ip4_n[3] + elif plen == 40: + pref_n[5] = ip4_n[0] + pref_n[6] = ip4_n[1] + pref_n[7] = ip4_n[2] + pref_n[9] = ip4_n[3] + elif plen == 48: + pref_n[6] = ip4_n[0] + pref_n[7] = ip4_n[1] + pref_n[9] = ip4_n[2] + pref_n[10] = ip4_n[3] + elif plen == 56: + pref_n[7] = ip4_n[0] + pref_n[9] = ip4_n[1] + pref_n[10] = ip4_n[2] + pref_n[11] = ip4_n[3] + elif plen == 64: + pref_n[9] = ip4_n[0] + pref_n[10] = ip4_n[1] + pref_n[11] = ip4_n[2] + pref_n[12] = ip4_n[3] + elif plen == 96: + pref_n[12] = ip4_n[0] + pref_n[13] = ip4_n[1] + pref_n[14] = ip4_n[2] + pref_n[15] = ip4_n[3] + return socket.inet_ntop(socket.AF_INET6, ''.join(pref_n)) + + def create_stream_in_ip6(self, in_if, out_if, hlim=64, pref=None, plen=0): + """ + Create IPv6 packet stream for inside network + + :param in_if: Inside interface + :param out_if: Outside interface + :param ttl: Hop Limit of generated packets + :param pref: NAT64 prefix + :param plen: NAT64 prefix length + """ + pkts = [] + if pref is None: + dst = ''.join(['64:ff9b::', out_if.remote_ip4]) + else: + dst = self.compose_ip6(out_if.remote_ip4, pref, plen) + + # TCP + p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / + IPv6(src=in_if.remote_ip6, dst=dst, hlim=hlim) / + TCP(sport=self.tcp_port_in, dport=20)) + pkts.append(p) + + # UDP + p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / + IPv6(src=in_if.remote_ip6, dst=dst, hlim=hlim) / + UDP(sport=self.udp_port_in, dport=20)) + pkts.append(p) + + # ICMP + p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / + IPv6(src=in_if.remote_ip6, dst=dst, hlim=hlim) / + ICMPv6EchoRequest(id=self.icmp_id_in)) + pkts.append(p) + + return pkts + + def create_stream_out(self, out_if, dst_ip=None, ttl=64): + """ + Create packet stream for outside network + + :param out_if: Outside interface + :param dst_ip: Destination IP address (Default use global NAT address) + :param ttl: TTL of generated packets + """ + if dst_ip is None: + dst_ip = self.nat_addr + pkts = [] + # TCP + p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) / + IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) / + TCP(dport=self.tcp_port_out, sport=20)) + pkts.append(p) + + # UDP + p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) / + IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) / + UDP(dport=self.udp_port_out, sport=20)) + pkts.append(p) + + # ICMP + p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) / + IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) / + ICMP(id=self.icmp_id_out, type='echo-reply')) + pkts.append(p) + + return pkts + + def verify_capture_out(self, capture, nat_ip=None, same_port=False, + packet_num=3, dst_ip=None): + """ + Verify captured packets on outside network + + :param capture: Captured packets + :param nat_ip: Translated IP address (Default use global NAT address) + :param same_port: Sorce port number is not translated (Default False) + :param packet_num: Expected number of packets (Default 3) + :param dst_ip: Destination IP address (Default do not verify) + """ + if nat_ip is None: + nat_ip = self.nat_addr + self.assertEqual(packet_num, len(capture)) + for packet in capture: + try: + self.check_ip_checksum(packet) + self.assertEqual(packet[IP].src, nat_ip) + if dst_ip is not None: + self.assertEqual(packet[IP].dst, dst_ip) + if packet.haslayer(TCP): + if same_port: + self.assertEqual(packet[TCP].sport, self.tcp_port_in) + else: + self.assertNotEqual( + packet[TCP].sport, self.tcp_port_in) + self.tcp_port_out = packet[TCP].sport + self.check_tcp_checksum(packet) + elif packet.haslayer(UDP): + if same_port: + self.assertEqual(packet[UDP].sport, self.udp_port_in) + else: + self.assertNotEqual( + packet[UDP].sport, self.udp_port_in) + self.udp_port_out = packet[UDP].sport + else: + if same_port: + self.assertEqual(packet[ICMP].id, self.icmp_id_in) + else: + self.assertNotEqual(packet[ICMP].id, self.icmp_id_in) + self.icmp_id_out = packet[ICMP].id + self.check_icmp_checksum(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet " + "(outside network):", packet)) + raise + + def verify_capture_in(self, capture, in_if, packet_num=3): + """ + Verify captured packets on inside network + + :param capture: Captured packets + :param in_if: Inside interface + :param packet_num: Expected number of packets (Default 3) + """ + self.assertEqual(packet_num, len(capture)) + for packet in capture: + try: + self.check_ip_checksum(packet) + self.assertEqual(packet[IP].dst, in_if.remote_ip4) + if packet.haslayer(TCP): + self.assertEqual(packet[TCP].dport, self.tcp_port_in) + self.check_tcp_checksum(packet) + elif packet.haslayer(UDP): + self.assertEqual(packet[UDP].dport, self.udp_port_in) + else: + self.assertEqual(packet[ICMP].id, self.icmp_id_in) + self.check_icmp_checksum(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet " + "(inside network):", packet)) + raise + + def verify_capture_in_ip6(self, capture, src_ip, dst_ip, packet_num=3): + """ + Verify captured IPv6 packets on inside network + + :param capture: Captured packets + :param src_ip: Source IP + :param dst_ip: Destination IP address + :param packet_num: Expected number of packets (Default 3) + """ + self.assertEqual(packet_num, len(capture)) + for packet in capture: + try: + self.assertEqual(packet[IPv6].src, src_ip) + self.assertEqual(packet[IPv6].dst, dst_ip) + if packet.haslayer(TCP): + self.assertEqual(packet[TCP].dport, self.tcp_port_in) + self.check_tcp_checksum(packet) + elif packet.haslayer(UDP): + self.assertEqual(packet[UDP].dport, self.udp_port_in) + self.check_udp_checksum(packet) + else: + self.assertEqual(packet[ICMPv6EchoReply].id, + self.icmp_id_in) + self.check_icmpv6_checksum(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet " + "(inside network):", packet)) + raise + + def verify_capture_no_translation(self, capture, ingress_if, egress_if): + """ + Verify captured packet that don't have to be translated + + :param capture: Captured packets + :param ingress_if: Ingress interface + :param egress_if: Egress interface + """ + for packet in capture: + try: + self.assertEqual(packet[IP].src, ingress_if.remote_ip4) + self.assertEqual(packet[IP].dst, egress_if.remote_ip4) + if packet.haslayer(TCP): + self.assertEqual(packet[TCP].sport, self.tcp_port_in) + elif packet.haslayer(UDP): + self.assertEqual(packet[UDP].sport, self.udp_port_in) + else: + self.assertEqual(packet[ICMP].id, self.icmp_id_in) + except: + self.logger.error(ppp("Unexpected or invalid packet " + "(inside network):", packet)) + raise + + def verify_capture_out_with_icmp_errors(self, capture, src_ip=None, + packet_num=3, icmp_type=11): + """ + Verify captured packets with ICMP errors on outside network + + :param capture: Captured packets + :param src_ip: Translated IP address or IP address of VPP + (Default use global NAT address) + :param packet_num: Expected number of packets (Default 3) + :param icmp_type: Type of error ICMP packet + we are expecting (Default 11) + """ + if src_ip is None: + src_ip = self.nat_addr + self.assertEqual(packet_num, len(capture)) + for packet in capture: + try: + self.assertEqual(packet[IP].src, src_ip) + self.assertTrue(packet.haslayer(ICMP)) + icmp = packet[ICMP] + self.assertEqual(icmp.type, icmp_type) + self.assertTrue(icmp.haslayer(IPerror)) + inner_ip = icmp[IPerror] + if inner_ip.haslayer(TCPerror): + self.assertEqual(inner_ip[TCPerror].dport, + self.tcp_port_out) + elif inner_ip.haslayer(UDPerror): + self.assertEqual(inner_ip[UDPerror].dport, + self.udp_port_out) + else: + self.assertEqual(inner_ip[ICMPerror].id, self.icmp_id_out) + except: + self.logger.error(ppp("Unexpected or invalid packet " + "(outside network):", packet)) + raise + + def verify_capture_in_with_icmp_errors(self, capture, in_if, packet_num=3, + icmp_type=11): + """ + Verify captured packets with ICMP errors on inside network + + :param capture: Captured packets + :param in_if: Inside interface + :param packet_num: Expected number of packets (Default 3) + :param icmp_type: Type of error ICMP packet + we are expecting (Default 11) + """ + self.assertEqual(packet_num, len(capture)) + for packet in capture: + try: + self.assertEqual(packet[IP].dst, in_if.remote_ip4) + self.assertTrue(packet.haslayer(ICMP)) + icmp = packet[ICMP] + self.assertEqual(icmp.type, icmp_type) + self.assertTrue(icmp.haslayer(IPerror)) + inner_ip = icmp[IPerror] + if inner_ip.haslayer(TCPerror): + self.assertEqual(inner_ip[TCPerror].sport, + self.tcp_port_in) + elif inner_ip.haslayer(UDPerror): + self.assertEqual(inner_ip[UDPerror].sport, + self.udp_port_in) + else: + self.assertEqual(inner_ip[ICMPerror].id, self.icmp_id_in) + except: + self.logger.error(ppp("Unexpected or invalid packet " + "(inside network):", packet)) + raise + + def verify_ipfix_nat44_ses(self, data): + """ + Verify IPFIX NAT44 session create/delete event + + :param data: Decoded IPFIX data records + """ + nat44_ses_create_num = 0 + nat44_ses_delete_num = 0 + self.assertEqual(6, len(data)) + for record in data: + # natEvent + self.assertIn(ord(record[230]), [4, 5]) + if ord(record[230]) == 4: + nat44_ses_create_num += 1 + else: + nat44_ses_delete_num += 1 + # sourceIPv4Address + self.assertEqual(self.pg0.remote_ip4n, record[8]) + # postNATSourceIPv4Address + self.assertEqual(socket.inet_pton(socket.AF_INET, self.nat_addr), + record[225]) + # ingressVRFID + self.assertEqual(struct.pack("!I", 0), record[234]) + # protocolIdentifier/sourceTransportPort/postNAPTSourceTransportPort + if IP_PROTOS.icmp == ord(record[4]): + self.assertEqual(struct.pack("!H", self.icmp_id_in), record[7]) + self.assertEqual(struct.pack("!H", self.icmp_id_out), + record[227]) + elif IP_PROTOS.tcp == ord(record[4]): + self.assertEqual(struct.pack("!H", self.tcp_port_in), + record[7]) + self.assertEqual(struct.pack("!H", self.tcp_port_out), + record[227]) + elif IP_PROTOS.udp == ord(record[4]): + self.assertEqual(struct.pack("!H", self.udp_port_in), + record[7]) + self.assertEqual(struct.pack("!H", self.udp_port_out), + record[227]) + else: + self.fail("Invalid protocol") + self.assertEqual(3, nat44_ses_create_num) + self.assertEqual(3, nat44_ses_delete_num) + + def verify_ipfix_addr_exhausted(self, data): + """ + Verify IPFIX NAT addresses event + + :param data: Decoded IPFIX data records + """ + self.assertEqual(1, len(data)) + record = data[0] + # natEvent + self.assertEqual(ord(record[230]), 3) + # natPoolID + self.assertEqual(struct.pack("!I", 0), record[283]) + + +class TestNAT44(MethodHolder): + """ NAT44 Test Cases """ + + @classmethod + def setUpClass(cls): + super(TestNAT44, cls).setUpClass() + + try: + cls.tcp_port_in = 6303 + cls.tcp_port_out = 6303 + cls.udp_port_in = 6304 + cls.udp_port_out = 6304 + cls.icmp_id_in = 6305 + cls.icmp_id_out = 6305 + cls.nat_addr = '10.0.0.3' + cls.ipfix_src_port = 4739 + cls.ipfix_domain_id = 1 + + cls.create_pg_interfaces(range(9)) + cls.interfaces = list(cls.pg_interfaces[0:4]) + + for i in cls.interfaces: + i.admin_up() + i.config_ip4() + i.resolve_arp() + + cls.pg0.generate_remote_hosts(3) + cls.pg0.configure_ipv4_neighbors() + + cls.overlapping_interfaces = list(list(cls.pg_interfaces[4:7])) + + cls.pg4._local_ip4 = "172.16.255.1" + cls.pg4._local_ip4n = socket.inet_pton(socket.AF_INET, i.local_ip4) + cls.pg4._remote_hosts[0]._ip4 = "172.16.255.2" + cls.pg4.set_table_ip4(10) + cls.pg5._local_ip4 = "172.17.255.3" + cls.pg5._local_ip4n = socket.inet_pton(socket.AF_INET, i.local_ip4) + cls.pg5._remote_hosts[0]._ip4 = "172.17.255.4" + cls.pg5.set_table_ip4(10) + cls.pg6._local_ip4 = "172.16.255.1" + cls.pg6._local_ip4n = socket.inet_pton(socket.AF_INET, i.local_ip4) + cls.pg6._remote_hosts[0]._ip4 = "172.16.255.2" + cls.pg6.set_table_ip4(20) + for i in cls.overlapping_interfaces: + i.config_ip4() + i.admin_up() + i.resolve_arp() + + cls.pg7.admin_up() + cls.pg8.admin_up() + + except Exception: + super(TestNAT44, cls).tearDownClass() + raise + + def clear_nat44(self): + """ + Clear NAT44 configuration. + """ + # I found no elegant way to do this + self.vapi.ip_add_del_route(dst_address=self.pg7.remote_ip4n, + dst_address_length=32, + next_hop_address=self.pg7.remote_ip4n, + next_hop_sw_if_index=self.pg7.sw_if_index, + is_add=0) + self.vapi.ip_add_del_route(dst_address=self.pg8.remote_ip4n, + dst_address_length=32, + next_hop_address=self.pg8.remote_ip4n, + next_hop_sw_if_index=self.pg8.sw_if_index, + is_add=0) + + for intf in [self.pg7, self.pg8]: + neighbors = self.vapi.ip_neighbor_dump(intf.sw_if_index) + for n in neighbors: + self.vapi.ip_neighbor_add_del(intf.sw_if_index, + n.mac_address, + n.ip_address, + is_add=0) + + if self.pg7.has_ip4_config: + self.pg7.unconfig_ip4() + + interfaces = self.vapi.nat44_interface_addr_dump() + for intf in interfaces: + self.vapi.nat44_add_interface_addr(intf.sw_if_index, is_add=0) + + self.vapi.nat_ipfix(enable=0, src_port=self.ipfix_src_port, + domain_id=self.ipfix_domain_id) + self.ipfix_src_port = 4739 + self.ipfix_domain_id = 1 + + interfaces = self.vapi.nat44_interface_dump() + for intf in interfaces: + self.vapi.nat44_interface_add_del_feature(intf.sw_if_index, + intf.is_inside, + is_add=0) + + interfaces = self.vapi.nat44_interface_output_feature_dump() + for intf in interfaces: + self.vapi.nat44_interface_add_del_output_feature(intf.sw_if_index, + intf.is_inside, + is_add=0) + + static_mappings = self.vapi.nat44_static_mapping_dump() + for sm in static_mappings: + self.vapi.nat44_add_del_static_mapping( + sm.local_ip_address, + sm.external_ip_address, + local_port=sm.local_port, + external_port=sm.external_port, + addr_only=sm.addr_only, + vrf_id=sm.vrf_id, + protocol=sm.protocol, + is_add=0) + + adresses = self.vapi.nat44_address_dump() + for addr in adresses: + self.vapi.nat44_add_del_address_range(addr.ip_address, + addr.ip_address, + is_add=0) + + def nat44_add_static_mapping(self, local_ip, external_ip='0.0.0.0', + local_port=0, external_port=0, vrf_id=0, + is_add=1, external_sw_if_index=0xFFFFFFFF, + proto=0): + """ + Add/delete NAT44 static mapping + + :param local_ip: Local IP address + :param external_ip: External IP address + :param local_port: Local port number (Optional) + :param external_port: External port number (Optional) + :param vrf_id: VRF ID (Default 0) + :param is_add: 1 if add, 0 if delete (Default add) + :param external_sw_if_index: External interface instead of IP address + :param proto: IP protocol (Mandatory if port specified) + """ + addr_only = 1 + if local_port and external_port: + addr_only = 0 + l_ip = socket.inet_pton(socket.AF_INET, local_ip) + e_ip = socket.inet_pton(socket.AF_INET, external_ip) + self.vapi.nat44_add_del_static_mapping( + l_ip, + e_ip, + external_sw_if_index, + local_port, + external_port, + addr_only, + vrf_id, + proto, + is_add) + + def nat44_add_address(self, ip, is_add=1, vrf_id=0xFFFFFFFF): + """ + Add/delete NAT44 address + + :param ip: IP address + :param is_add: 1 if add, 0 if delete (Default add) + """ + nat_addr = socket.inet_pton(socket.AF_INET, ip) + self.vapi.nat44_add_del_address_range(nat_addr, nat_addr, is_add, + vrf_id=vrf_id) + + def test_dynamic(self): + """ NAT44 dynamic translation test """ + + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + # in2out + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture) + + # out2in + pkts = self.create_stream_out(self.pg1) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg0) + + def test_dynamic_icmp_errors_in2out_ttl_1(self): + """ NAT44 handling of client packets with TTL=1 """ + + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + # Client side - generate traffic + pkts = self.create_stream_in(self.pg0, self.pg1, ttl=1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # Client side - verify ICMP type 11 packets + capture = self.pg0.get_capture(len(pkts)) + self.verify_capture_in_with_icmp_errors(capture, self.pg0) + + def test_dynamic_icmp_errors_out2in_ttl_1(self): + """ NAT44 handling of server packets with TTL=1 """ + + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + # Client side - create sessions + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # Server side - generate traffic + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture) + pkts = self.create_stream_out(self.pg1, ttl=1) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # Server side - verify ICMP type 11 packets + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out_with_icmp_errors(capture, + src_ip=self.pg1.local_ip4) + + def test_dynamic_icmp_errors_in2out_ttl_2(self): + """ NAT44 handling of error responses to client packets with TTL=2 """ + + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + # Client side - generate traffic + pkts = self.create_stream_in(self.pg0, self.pg1, ttl=2) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # Server side - simulate ICMP type 11 response + capture = self.pg1.get_capture(len(pkts)) + pkts = [Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + ICMP(type=11) / packet[IP] for packet in capture] + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # Client side - verify ICMP type 11 packets + capture = self.pg0.get_capture(len(pkts)) + self.verify_capture_in_with_icmp_errors(capture, self.pg0) + + def test_dynamic_icmp_errors_out2in_ttl_2(self): + """ NAT44 handling of error responses to server packets with TTL=2 """ + + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + # Client side - create sessions + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # Server side - generate traffic + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture) + pkts = self.create_stream_out(self.pg1, ttl=2) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # Client side - simulate ICMP type 11 response + capture = self.pg0.get_capture(len(pkts)) + pkts = [Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + ICMP(type=11) / packet[IP] for packet in capture] + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # Server side - verify ICMP type 11 packets + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out_with_icmp_errors(capture) + + def test_ping_out_interface_from_outside(self): + """ Ping NAT44 out interface from outside network """ + + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src=self.pg1.remote_ip4, dst=self.pg1.local_ip4) / + ICMP(id=self.icmp_id_out, type='echo-request')) + pkts = [p] + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.assertEqual(1, len(capture)) + packet = capture[0] + try: + self.assertEqual(packet[IP].src, self.pg1.local_ip4) + self.assertEqual(packet[IP].dst, self.pg1.remote_ip4) + self.assertEqual(packet[ICMP].id, self.icmp_id_in) + self.assertEqual(packet[ICMP].type, 0) # echo reply + except: + self.logger.error(ppp("Unexpected or invalid packet " + "(outside network):", packet)) + raise + + def test_ping_internal_host_from_outside(self): + """ Ping internal host from outside network """ + + self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + # out2in + pkt = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr, ttl=64) / + ICMP(id=self.icmp_id_out, type='echo-request')) + self.pg1.add_stream(pkt) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(1) + self.verify_capture_in(capture, self.pg0, packet_num=1) + self.assert_equal(capture[0][IP].proto, IP_PROTOS.icmp) + + # in2out + pkt = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, ttl=64) / + ICMP(id=self.icmp_id_in, type='echo-reply')) + self.pg0.add_stream(pkt) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(1) + self.verify_capture_out(capture, same_port=True, packet_num=1) + self.assert_equal(capture[0][IP].proto, IP_PROTOS.icmp) + + def test_static_in(self): + """ 1:1 NAT initialized from inside network """ + + nat_ip = "10.0.0.10" + self.tcp_port_out = 6303 + self.udp_port_out = 6304 + self.icmp_id_out = 6305 + + self.nat44_add_static_mapping(self.pg0.remote_ip4, nat_ip) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + # in2out + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip, True) + + # out2in + pkts = self.create_stream_out(self.pg1, nat_ip) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg0) + + def test_static_out(self): + """ 1:1 NAT initialized from outside network """ + + nat_ip = "10.0.0.20" + self.tcp_port_out = 6303 + self.udp_port_out = 6304 + self.icmp_id_out = 6305 + + self.nat44_add_static_mapping(self.pg0.remote_ip4, nat_ip) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + # out2in + pkts = self.create_stream_out(self.pg1, nat_ip) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg0) + + # in2out + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip, True) + + def test_static_with_port_in(self): + """ 1:1 NAPT initialized from inside network """ + + self.tcp_port_out = 3606 + self.udp_port_out = 3607 + self.icmp_id_out = 3608 + + self.nat44_add_address(self.nat_addr) + self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, + self.tcp_port_in, self.tcp_port_out, + proto=IP_PROTOS.tcp) + self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, + self.udp_port_in, self.udp_port_out, + proto=IP_PROTOS.udp) + self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, + self.icmp_id_in, self.icmp_id_out, + proto=IP_PROTOS.icmp) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + # in2out + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture) + + # out2in + pkts = self.create_stream_out(self.pg1) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg0) + + def test_static_with_port_out(self): + """ 1:1 NAPT initialized from outside network """ + + self.tcp_port_out = 30606 + self.udp_port_out = 30607 + self.icmp_id_out = 30608 + + self.nat44_add_address(self.nat_addr) + self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, + self.tcp_port_in, self.tcp_port_out, + proto=IP_PROTOS.tcp) + self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, + self.udp_port_in, self.udp_port_out, + proto=IP_PROTOS.udp) + self.nat44_add_static_mapping(self.pg0.remote_ip4, self.nat_addr, + self.icmp_id_in, self.icmp_id_out, + proto=IP_PROTOS.icmp) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + # out2in + pkts = self.create_stream_out(self.pg1) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg0) + + # in2out + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture) + + def test_static_vrf_aware(self): + """ 1:1 NAT VRF awareness """ + + nat_ip1 = "10.0.0.30" + nat_ip2 = "10.0.0.40" + self.tcp_port_out = 6303 + self.udp_port_out = 6304 + self.icmp_id_out = 6305 + + self.nat44_add_static_mapping(self.pg4.remote_ip4, nat_ip1, + vrf_id=10) + self.nat44_add_static_mapping(self.pg0.remote_ip4, nat_ip2, + vrf_id=10) + self.vapi.nat44_interface_add_del_feature(self.pg3.sw_if_index, + is_inside=0) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg4.sw_if_index) + + # inside interface VRF match NAT44 static mapping VRF + pkts = self.create_stream_in(self.pg4, self.pg3) + self.pg4.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg3.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip1, True) + + # inside interface VRF don't match NAT44 static mapping VRF (packets + # are dropped) + pkts = self.create_stream_in(self.pg0, self.pg3) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg3.assert_nothing_captured() + + def test_multiple_inside_interfaces(self): + """ NAT44 multiple non-overlapping address space inside interfaces """ + + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg3.sw_if_index, + is_inside=0) + + # between two NAT44 inside interfaces (no translation) + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_no_translation(capture, self.pg0, self.pg1) + + # from NAT44 inside to interface without NAT44 feature (no translation) + pkts = self.create_stream_in(self.pg0, self.pg2) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg2.get_capture(len(pkts)) + self.verify_capture_no_translation(capture, self.pg0, self.pg2) + + # in2out 1st interface + pkts = self.create_stream_in(self.pg0, self.pg3) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg3.get_capture(len(pkts)) + self.verify_capture_out(capture) + + # out2in 1st interface + pkts = self.create_stream_out(self.pg3) + self.pg3.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg0) + + # in2out 2nd interface + pkts = self.create_stream_in(self.pg1, self.pg3) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg3.get_capture(len(pkts)) + self.verify_capture_out(capture) + + # out2in 2nd interface + pkts = self.create_stream_out(self.pg3) + self.pg3.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg1) + + def test_inside_overlapping_interfaces(self): + """ NAT44 multiple inside interfaces with overlapping address space """ + + static_nat_ip = "10.0.0.10" + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_feature(self.pg3.sw_if_index, + is_inside=0) + self.vapi.nat44_interface_add_del_feature(self.pg4.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg5.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg6.sw_if_index) + self.nat44_add_static_mapping(self.pg6.remote_ip4, static_nat_ip, + vrf_id=20) + + # between NAT44 inside interfaces with same VRF (no translation) + pkts = self.create_stream_in(self.pg4, self.pg5) + self.pg4.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg5.get_capture(len(pkts)) + self.verify_capture_no_translation(capture, self.pg4, self.pg5) + + # between NAT44 inside interfaces with different VRF (hairpinning) + p = (Ether(src=self.pg4.remote_mac, dst=self.pg4.local_mac) / + IP(src=self.pg4.remote_ip4, dst=static_nat_ip) / + TCP(sport=1234, dport=5678)) + self.pg4.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg6.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, self.nat_addr) + self.assertEqual(ip.dst, self.pg6.remote_ip4) + self.assertNotEqual(tcp.sport, 1234) + self.assertEqual(tcp.dport, 5678) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # in2out 1st interface + pkts = self.create_stream_in(self.pg4, self.pg3) + self.pg4.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg3.get_capture(len(pkts)) + self.verify_capture_out(capture) + + # out2in 1st interface + pkts = self.create_stream_out(self.pg3) + self.pg3.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg4.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg4) + + # in2out 2nd interface + pkts = self.create_stream_in(self.pg5, self.pg3) + self.pg5.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg3.get_capture(len(pkts)) + self.verify_capture_out(capture) + + # out2in 2nd interface + pkts = self.create_stream_out(self.pg3) + self.pg3.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg5.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg5) + + # pg5 session dump + addresses = self.vapi.nat44_address_dump() + self.assertEqual(len(addresses), 1) + sessions = self.vapi.nat44_user_session_dump(self.pg5.remote_ip4n, 10) + self.assertEqual(len(sessions), 3) + for session in sessions: + self.assertFalse(session.is_static) + self.assertEqual(session.inside_ip_address[0:4], + self.pg5.remote_ip4n) + self.assertEqual(session.outside_ip_address, + addresses[0].ip_address) + self.assertEqual(sessions[0].protocol, IP_PROTOS.tcp) + self.assertEqual(sessions[1].protocol, IP_PROTOS.udp) + self.assertEqual(sessions[2].protocol, IP_PROTOS.icmp) + self.assertEqual(sessions[0].inside_port, self.tcp_port_in) + self.assertEqual(sessions[1].inside_port, self.udp_port_in) + self.assertEqual(sessions[2].inside_port, self.icmp_id_in) + self.assertEqual(sessions[0].outside_port, self.tcp_port_out) + self.assertEqual(sessions[1].outside_port, self.udp_port_out) + self.assertEqual(sessions[2].outside_port, self.icmp_id_out) + + # in2out 3rd interface + pkts = self.create_stream_in(self.pg6, self.pg3) + self.pg6.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg3.get_capture(len(pkts)) + self.verify_capture_out(capture, static_nat_ip, True) + + # out2in 3rd interface + pkts = self.create_stream_out(self.pg3, static_nat_ip) + self.pg3.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg6.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg6) + + # general user and session dump verifications + users = self.vapi.nat44_user_dump() + self.assertTrue(len(users) >= 3) + addresses = self.vapi.nat44_address_dump() + self.assertEqual(len(addresses), 1) + for user in users: + sessions = self.vapi.nat44_user_session_dump(user.ip_address, + user.vrf_id) + for session in sessions: + self.assertEqual(user.ip_address, session.inside_ip_address) + self.assertTrue(session.total_bytes > session.total_pkts > 0) + self.assertTrue(session.protocol in + [IP_PROTOS.tcp, IP_PROTOS.udp, + IP_PROTOS.icmp]) + + # pg4 session dump + sessions = self.vapi.nat44_user_session_dump(self.pg4.remote_ip4n, 10) + self.assertTrue(len(sessions) >= 4) + for session in sessions: + self.assertFalse(session.is_static) + self.assertEqual(session.inside_ip_address[0:4], + self.pg4.remote_ip4n) + self.assertEqual(session.outside_ip_address, + addresses[0].ip_address) + + # pg6 session dump + sessions = self.vapi.nat44_user_session_dump(self.pg6.remote_ip4n, 20) + self.assertTrue(len(sessions) >= 3) + for session in sessions: + self.assertTrue(session.is_static) + self.assertEqual(session.inside_ip_address[0:4], + self.pg6.remote_ip4n) + self.assertEqual(map(ord, session.outside_ip_address[0:4]), + map(int, static_nat_ip.split('.'))) + self.assertTrue(session.inside_port in + [self.tcp_port_in, self.udp_port_in, + self.icmp_id_in]) + + def test_hairpinning(self): + """ NAT44 hairpinning - 1:1 NAPT """ + + host = self.pg0.remote_hosts[0] + server = self.pg0.remote_hosts[1] + host_in_port = 1234 + host_out_port = 0 + server_in_port = 5678 + server_out_port = 8765 + + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + # add static mapping for server + self.nat44_add_static_mapping(server.ip4, self.nat_addr, + server_in_port, server_out_port, + proto=IP_PROTOS.tcp) + + # send packet from host to server + p = (Ether(src=host.mac, dst=self.pg0.local_mac) / + IP(src=host.ip4, dst=self.nat_addr) / + TCP(sport=host_in_port, dport=server_out_port)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, self.nat_addr) + self.assertEqual(ip.dst, server.ip4) + self.assertNotEqual(tcp.sport, host_in_port) + self.assertEqual(tcp.dport, server_in_port) + self.check_tcp_checksum(p) + host_out_port = tcp.sport + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # send reply from server to host + p = (Ether(src=server.mac, dst=self.pg0.local_mac) / + IP(src=server.ip4, dst=self.nat_addr) / + TCP(sport=server_in_port, dport=host_out_port)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, self.nat_addr) + self.assertEqual(ip.dst, host.ip4) + self.assertEqual(tcp.sport, server_out_port) + self.assertEqual(tcp.dport, host_in_port) + self.check_tcp_checksum(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:"), p) + raise + + def test_hairpinning2(self): + """ NAT44 hairpinning - 1:1 NAT""" + + server1_nat_ip = "10.0.0.10" + server2_nat_ip = "10.0.0.11" + host = self.pg0.remote_hosts[0] + server1 = self.pg0.remote_hosts[1] + server2 = self.pg0.remote_hosts[2] + server_tcp_port = 22 + server_udp_port = 20 + + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + # add static mapping for servers + self.nat44_add_static_mapping(server1.ip4, server1_nat_ip) + self.nat44_add_static_mapping(server2.ip4, server2_nat_ip) + + # host to server1 + pkts = [] + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=host.ip4, dst=server1_nat_ip) / + TCP(sport=self.tcp_port_in, dport=server_tcp_port)) + pkts.append(p) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=host.ip4, dst=server1_nat_ip) / + UDP(sport=self.udp_port_in, dport=server_udp_port)) + pkts.append(p) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=host.ip4, dst=server1_nat_ip) / + ICMP(id=self.icmp_id_in, type='echo-request')) + pkts.append(p) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + for packet in capture: + try: + self.assertEqual(packet[IP].src, self.nat_addr) + self.assertEqual(packet[IP].dst, server1.ip4) + if packet.haslayer(TCP): + self.assertNotEqual(packet[TCP].sport, self.tcp_port_in) + self.assertEqual(packet[TCP].dport, server_tcp_port) + self.tcp_port_out = packet[TCP].sport + self.check_tcp_checksum(packet) + elif packet.haslayer(UDP): + self.assertNotEqual(packet[UDP].sport, self.udp_port_in) + self.assertEqual(packet[UDP].dport, server_udp_port) + self.udp_port_out = packet[UDP].sport + else: + self.assertNotEqual(packet[ICMP].id, self.icmp_id_in) + self.icmp_id_out = packet[ICMP].id + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + # server1 to host + pkts = [] + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=server1.ip4, dst=self.nat_addr) / + TCP(sport=server_tcp_port, dport=self.tcp_port_out)) + pkts.append(p) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=server1.ip4, dst=self.nat_addr) / + UDP(sport=server_udp_port, dport=self.udp_port_out)) + pkts.append(p) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=server1.ip4, dst=self.nat_addr) / + ICMP(id=self.icmp_id_out, type='echo-reply')) + pkts.append(p) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + for packet in capture: + try: + self.assertEqual(packet[IP].src, server1_nat_ip) + self.assertEqual(packet[IP].dst, host.ip4) + if packet.haslayer(TCP): + self.assertEqual(packet[TCP].dport, self.tcp_port_in) + self.assertEqual(packet[TCP].sport, server_tcp_port) + self.check_tcp_checksum(packet) + elif packet.haslayer(UDP): + self.assertEqual(packet[UDP].dport, self.udp_port_in) + self.assertEqual(packet[UDP].sport, server_udp_port) + else: + self.assertEqual(packet[ICMP].id, self.icmp_id_in) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + # server2 to server1 + pkts = [] + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=server2.ip4, dst=server1_nat_ip) / + TCP(sport=self.tcp_port_in, dport=server_tcp_port)) + pkts.append(p) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=server2.ip4, dst=server1_nat_ip) / + UDP(sport=self.udp_port_in, dport=server_udp_port)) + pkts.append(p) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=server2.ip4, dst=server1_nat_ip) / + ICMP(id=self.icmp_id_in, type='echo-request')) + pkts.append(p) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + for packet in capture: + try: + self.assertEqual(packet[IP].src, server2_nat_ip) + self.assertEqual(packet[IP].dst, server1.ip4) + if packet.haslayer(TCP): + self.assertEqual(packet[TCP].sport, self.tcp_port_in) + self.assertEqual(packet[TCP].dport, server_tcp_port) + self.tcp_port_out = packet[TCP].sport + self.check_tcp_checksum(packet) + elif packet.haslayer(UDP): + self.assertEqual(packet[UDP].sport, self.udp_port_in) + self.assertEqual(packet[UDP].dport, server_udp_port) + self.udp_port_out = packet[UDP].sport + else: + self.assertEqual(packet[ICMP].id, self.icmp_id_in) + self.icmp_id_out = packet[ICMP].id + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + # server1 to server2 + pkts = [] + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=server1.ip4, dst=server2_nat_ip) / + TCP(sport=server_tcp_port, dport=self.tcp_port_out)) + pkts.append(p) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=server1.ip4, dst=server2_nat_ip) / + UDP(sport=server_udp_port, dport=self.udp_port_out)) + pkts.append(p) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=server1.ip4, dst=server2_nat_ip) / + ICMP(id=self.icmp_id_out, type='echo-reply')) + pkts.append(p) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + for packet in capture: + try: + self.assertEqual(packet[IP].src, server1_nat_ip) + self.assertEqual(packet[IP].dst, server2.ip4) + if packet.haslayer(TCP): + self.assertEqual(packet[TCP].dport, self.tcp_port_in) + self.assertEqual(packet[TCP].sport, server_tcp_port) + self.check_tcp_checksum(packet) + elif packet.haslayer(UDP): + self.assertEqual(packet[UDP].dport, self.udp_port_in) + self.assertEqual(packet[UDP].sport, server_udp_port) + else: + self.assertEqual(packet[ICMP].id, self.icmp_id_in) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + def test_max_translations_per_user(self): + """ MAX translations per user - recycle the least recently used """ + + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + # get maximum number of translations per user + nat44_config = self.vapi.nat_show_config() + + # send more than maximum number of translations per user packets + pkts_num = nat44_config.max_translations_per_user + 5 + pkts = [] + for port in range(0, pkts_num): + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=1025 + port)) + pkts.append(p) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # verify number of translated packet + self.pg1.get_capture(pkts_num) + + def test_interface_addr(self): + """ Acquire NAT44 addresses from interface """ + self.vapi.nat44_add_interface_addr(self.pg7.sw_if_index) + + # no address in NAT pool + adresses = self.vapi.nat44_address_dump() + self.assertEqual(0, len(adresses)) + + # configure interface address and check NAT address pool + self.pg7.config_ip4() + adresses = self.vapi.nat44_address_dump() + self.assertEqual(1, len(adresses)) + self.assertEqual(adresses[0].ip_address[0:4], self.pg7.local_ip4n) + + # remove interface address and check NAT address pool + self.pg7.unconfig_ip4() + adresses = self.vapi.nat44_address_dump() + self.assertEqual(0, len(adresses)) + + def test_interface_addr_static_mapping(self): + """ Static mapping with addresses from interface """ + self.vapi.nat44_add_interface_addr(self.pg7.sw_if_index) + self.nat44_add_static_mapping( + '1.2.3.4', + external_sw_if_index=self.pg7.sw_if_index) + + # static mappings with external interface + static_mappings = self.vapi.nat44_static_mapping_dump() + self.assertEqual(1, len(static_mappings)) + self.assertEqual(self.pg7.sw_if_index, + static_mappings[0].external_sw_if_index) + + # configure interface address and check static mappings + self.pg7.config_ip4() + static_mappings = self.vapi.nat44_static_mapping_dump() + self.assertEqual(1, len(static_mappings)) + self.assertEqual(static_mappings[0].external_ip_address[0:4], + self.pg7.local_ip4n) + self.assertEqual(0xFFFFFFFF, static_mappings[0].external_sw_if_index) + + # remove interface address and check static mappings + self.pg7.unconfig_ip4() + static_mappings = self.vapi.nat44_static_mapping_dump() + self.assertEqual(0, len(static_mappings)) + + def test_ipfix_nat44_sess(self): + """ IPFIX logging NAT44 session created/delted """ + self.ipfix_domain_id = 10 + self.ipfix_src_port = 20202 + colector_port = 30303 + bind_layers(UDP, IPFIX, dport=30303) + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + self.vapi.set_ipfix_exporter(collector_address=self.pg3.remote_ip4n, + src_address=self.pg3.local_ip4n, + path_mtu=512, + template_interval=10, + collector_port=colector_port) + self.vapi.nat_ipfix(domain_id=self.ipfix_domain_id, + src_port=self.ipfix_src_port) + + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture) + self.nat44_add_address(self.nat_addr, is_add=0) + self.vapi.cli("ipfix flush") # FIXME this should be an API call + capture = self.pg3.get_capture(3) + ipfix = IPFIXDecoder() + # first load template + for p in capture: + self.assertTrue(p.haslayer(IPFIX)) + self.assertEqual(p[IP].src, self.pg3.local_ip4) + self.assertEqual(p[IP].dst, self.pg3.remote_ip4) + self.assertEqual(p[UDP].sport, self.ipfix_src_port) + self.assertEqual(p[UDP].dport, colector_port) + self.assertEqual(p[IPFIX].observationDomainID, + self.ipfix_domain_id) + if p.haslayer(Template): + ipfix.add_template(p.getlayer(Template)) + # verify events in data set + for p in capture: + if p.haslayer(Data): + data = ipfix.decode_data_set(p.getlayer(Set)) + self.verify_ipfix_nat44_ses(data) + + def test_ipfix_addr_exhausted(self): + """ IPFIX logging NAT addresses exhausted """ + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + self.vapi.set_ipfix_exporter(collector_address=self.pg3.remote_ip4n, + src_address=self.pg3.local_ip4n, + path_mtu=512, + template_interval=10) + self.vapi.nat_ipfix(domain_id=self.ipfix_domain_id, + src_port=self.ipfix_src_port) + + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=3025)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(0) + self.vapi.cli("ipfix flush") # FIXME this should be an API call + capture = self.pg3.get_capture(3) + ipfix = IPFIXDecoder() + # first load template + for p in capture: + self.assertTrue(p.haslayer(IPFIX)) + self.assertEqual(p[IP].src, self.pg3.local_ip4) + self.assertEqual(p[IP].dst, self.pg3.remote_ip4) + self.assertEqual(p[UDP].sport, self.ipfix_src_port) + self.assertEqual(p[UDP].dport, 4739) + self.assertEqual(p[IPFIX].observationDomainID, + self.ipfix_domain_id) + if p.haslayer(Template): + ipfix.add_template(p.getlayer(Template)) + # verify events in data set + for p in capture: + if p.haslayer(Data): + data = ipfix.decode_data_set(p.getlayer(Set)) + self.verify_ipfix_addr_exhausted(data) + + def test_pool_addr_fib(self): + """ NAT44 add pool addresses to FIB """ + static_addr = '10.0.0.10' + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + self.nat44_add_static_mapping(self.pg0.remote_ip4, static_addr) + + # NAT44 address + p = (Ether(src=self.pg1.remote_mac, dst='ff:ff:ff:ff:ff:ff') / + ARP(op=ARP.who_has, pdst=self.nat_addr, + psrc=self.pg1.remote_ip4, hwsrc=self.pg1.remote_mac)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(1) + self.assertTrue(capture[0].haslayer(ARP)) + self.assertTrue(capture[0][ARP].op, ARP.is_at) + + # 1:1 NAT address + p = (Ether(src=self.pg1.remote_mac, dst='ff:ff:ff:ff:ff:ff') / + ARP(op=ARP.who_has, pdst=static_addr, + psrc=self.pg1.remote_ip4, hwsrc=self.pg1.remote_mac)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(1) + self.assertTrue(capture[0].haslayer(ARP)) + self.assertTrue(capture[0][ARP].op, ARP.is_at) + + # send ARP to non-NAT44 interface + p = (Ether(src=self.pg2.remote_mac, dst='ff:ff:ff:ff:ff:ff') / + ARP(op=ARP.who_has, pdst=self.nat_addr, + psrc=self.pg2.remote_ip4, hwsrc=self.pg2.remote_mac)) + self.pg2.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(0) + + # remove addresses and verify + self.nat44_add_address(self.nat_addr, is_add=0) + self.nat44_add_static_mapping(self.pg0.remote_ip4, static_addr, + is_add=0) + + p = (Ether(src=self.pg1.remote_mac, dst='ff:ff:ff:ff:ff:ff') / + ARP(op=ARP.who_has, pdst=self.nat_addr, + psrc=self.pg1.remote_ip4, hwsrc=self.pg1.remote_mac)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(0) + + p = (Ether(src=self.pg1.remote_mac, dst='ff:ff:ff:ff:ff:ff') / + ARP(op=ARP.who_has, pdst=static_addr, + psrc=self.pg1.remote_ip4, hwsrc=self.pg1.remote_mac)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(0) + + def test_vrf_mode(self): + """ NAT44 tenant VRF aware address pool mode """ + + vrf_id1 = 1 + vrf_id2 = 2 + nat_ip1 = "10.0.0.10" + nat_ip2 = "10.0.0.11" + + self.pg0.unconfig_ip4() + self.pg1.unconfig_ip4() + self.pg0.set_table_ip4(vrf_id1) + self.pg1.set_table_ip4(vrf_id2) + self.pg0.config_ip4() + self.pg1.config_ip4() + + self.nat44_add_address(nat_ip1, vrf_id=vrf_id1) + self.nat44_add_address(nat_ip2, vrf_id=vrf_id2) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg2.sw_if_index, + is_inside=0) + + # first VRF + pkts = self.create_stream_in(self.pg0, self.pg2) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg2.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip1) + + # second VRF + pkts = self.create_stream_in(self.pg1, self.pg2) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg2.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip2) + + def test_vrf_feature_independent(self): + """ NAT44 tenant VRF independent address pool mode """ + + nat_ip1 = "10.0.0.10" + nat_ip2 = "10.0.0.11" + + self.nat44_add_address(nat_ip1) + self.nat44_add_address(nat_ip2) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg2.sw_if_index, + is_inside=0) + + # first VRF + pkts = self.create_stream_in(self.pg0, self.pg2) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg2.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip1) + + # second VRF + pkts = self.create_stream_in(self.pg1, self.pg2) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg2.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip1) + + def test_dynamic_ipless_interfaces(self): + """ NAT44 interfaces without configured IP address """ + + self.vapi.ip_neighbor_add_del(self.pg7.sw_if_index, + self.pg7.remote_mac, + self.pg7.remote_ip4n, + is_static=1) + self.vapi.ip_neighbor_add_del(self.pg8.sw_if_index, + self.pg8.remote_mac, + self.pg8.remote_ip4n, + is_static=1) + + self.vapi.ip_add_del_route(dst_address=self.pg7.remote_ip4n, + dst_address_length=32, + next_hop_address=self.pg7.remote_ip4n, + next_hop_sw_if_index=self.pg7.sw_if_index) + self.vapi.ip_add_del_route(dst_address=self.pg8.remote_ip4n, + dst_address_length=32, + next_hop_address=self.pg8.remote_ip4n, + next_hop_sw_if_index=self.pg8.sw_if_index) + + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_feature(self.pg7.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg8.sw_if_index, + is_inside=0) + + # in2out + pkts = self.create_stream_in(self.pg7, self.pg8) + self.pg7.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg8.get_capture(len(pkts)) + self.verify_capture_out(capture) + + # out2in + pkts = self.create_stream_out(self.pg8, self.nat_addr) + self.pg8.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg7.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg7) + + def test_static_ipless_interfaces(self): + """ NAT44 interfaces without configured IP address - 1:1 NAT """ + + self.vapi.ip_neighbor_add_del(self.pg7.sw_if_index, + self.pg7.remote_mac, + self.pg7.remote_ip4n, + is_static=1) + self.vapi.ip_neighbor_add_del(self.pg8.sw_if_index, + self.pg8.remote_mac, + self.pg8.remote_ip4n, + is_static=1) + + self.vapi.ip_add_del_route(dst_address=self.pg7.remote_ip4n, + dst_address_length=32, + next_hop_address=self.pg7.remote_ip4n, + next_hop_sw_if_index=self.pg7.sw_if_index) + self.vapi.ip_add_del_route(dst_address=self.pg8.remote_ip4n, + dst_address_length=32, + next_hop_address=self.pg8.remote_ip4n, + next_hop_sw_if_index=self.pg8.sw_if_index) + + self.nat44_add_static_mapping(self.pg7.remote_ip4, self.nat_addr) + self.vapi.nat44_interface_add_del_feature(self.pg7.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg8.sw_if_index, + is_inside=0) + + # out2in + pkts = self.create_stream_out(self.pg8) + self.pg8.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg7.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg7) + + # in2out + pkts = self.create_stream_in(self.pg7, self.pg8) + self.pg7.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg8.get_capture(len(pkts)) + self.verify_capture_out(capture, self.nat_addr, True) + + def test_static_with_port_ipless_interfaces(self): + """ NAT44 interfaces without configured IP address - 1:1 NAPT """ + + self.tcp_port_out = 30606 + self.udp_port_out = 30607 + self.icmp_id_out = 30608 + + self.vapi.ip_neighbor_add_del(self.pg7.sw_if_index, + self.pg7.remote_mac, + self.pg7.remote_ip4n, + is_static=1) + self.vapi.ip_neighbor_add_del(self.pg8.sw_if_index, + self.pg8.remote_mac, + self.pg8.remote_ip4n, + is_static=1) + + self.vapi.ip_add_del_route(dst_address=self.pg7.remote_ip4n, + dst_address_length=32, + next_hop_address=self.pg7.remote_ip4n, + next_hop_sw_if_index=self.pg7.sw_if_index) + self.vapi.ip_add_del_route(dst_address=self.pg8.remote_ip4n, + dst_address_length=32, + next_hop_address=self.pg8.remote_ip4n, + next_hop_sw_if_index=self.pg8.sw_if_index) + + self.nat44_add_address(self.nat_addr) + self.nat44_add_static_mapping(self.pg7.remote_ip4, self.nat_addr, + self.tcp_port_in, self.tcp_port_out, + proto=IP_PROTOS.tcp) + self.nat44_add_static_mapping(self.pg7.remote_ip4, self.nat_addr, + self.udp_port_in, self.udp_port_out, + proto=IP_PROTOS.udp) + self.nat44_add_static_mapping(self.pg7.remote_ip4, self.nat_addr, + self.icmp_id_in, self.icmp_id_out, + proto=IP_PROTOS.icmp) + self.vapi.nat44_interface_add_del_feature(self.pg7.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg8.sw_if_index, + is_inside=0) + + # out2in + pkts = self.create_stream_out(self.pg8) + self.pg8.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg7.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg7) + + # in2out + pkts = self.create_stream_in(self.pg7, self.pg8) + self.pg7.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg8.get_capture(len(pkts)) + self.verify_capture_out(capture) + + def test_static_unknown_proto(self): + """ 1:1 NAT translate packet with unknown protocol """ + nat_ip = "10.0.0.10" + self.nat44_add_static_mapping(self.pg0.remote_ip4, nat_ip) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + # in2out + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + GRE() / + IP(src=self.pg2.remote_ip4, dst=self.pg3.remote_ip4) / + TCP(sport=1234, dport=1234)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg1.get_capture(1) + packet = p[0] + try: + self.assertEqual(packet[IP].src, nat_ip) + self.assertEqual(packet[IP].dst, self.pg1.remote_ip4) + self.assertTrue(packet.haslayer(GRE)) + self.check_ip_checksum(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + # out2in + p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src=self.pg1.remote_ip4, dst=nat_ip) / + GRE() / + IP(src=self.pg3.remote_ip4, dst=self.pg2.remote_ip4) / + TCP(sport=1234, dport=1234)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg0.get_capture(1) + packet = p[0] + try: + self.assertEqual(packet[IP].src, self.pg1.remote_ip4) + self.assertEqual(packet[IP].dst, self.pg0.remote_ip4) + self.assertTrue(packet.haslayer(GRE)) + self.check_ip_checksum(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + def test_hairpinning_static_unknown_proto(self): + """ 1:1 NAT translate packet with unknown protocol - hairpinning """ + + host = self.pg0.remote_hosts[0] + server = self.pg0.remote_hosts[1] + + host_nat_ip = "10.0.0.10" + server_nat_ip = "10.0.0.11" + + self.nat44_add_static_mapping(host.ip4, host_nat_ip) + self.nat44_add_static_mapping(server.ip4, server_nat_ip) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + # host to server + p = (Ether(dst=self.pg0.local_mac, src=host.mac) / + IP(src=host.ip4, dst=server_nat_ip) / + GRE() / + IP(src=self.pg2.remote_ip4, dst=self.pg3.remote_ip4) / + TCP(sport=1234, dport=1234)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg0.get_capture(1) + packet = p[0] + try: + self.assertEqual(packet[IP].src, host_nat_ip) + self.assertEqual(packet[IP].dst, server.ip4) + self.assertTrue(packet.haslayer(GRE)) + self.check_ip_checksum(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + # server to host + p = (Ether(dst=self.pg0.local_mac, src=server.mac) / + IP(src=server.ip4, dst=host_nat_ip) / + GRE() / + IP(src=self.pg3.remote_ip4, dst=self.pg2.remote_ip4) / + TCP(sport=1234, dport=1234)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg0.get_capture(1) + packet = p[0] + try: + self.assertEqual(packet[IP].src, server_nat_ip) + self.assertEqual(packet[IP].dst, host.ip4) + self.assertTrue(packet.haslayer(GRE)) + self.check_ip_checksum(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + def test_unknown_proto(self): + """ NAT44 translate packet with unknown protocol """ + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + # in2out + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=20)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg1.get_capture(1) + + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + GRE() / + IP(src=self.pg2.remote_ip4, dst=self.pg3.remote_ip4) / + TCP(sport=1234, dport=1234)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg1.get_capture(1) + packet = p[0] + try: + self.assertEqual(packet[IP].src, self.nat_addr) + self.assertEqual(packet[IP].dst, self.pg1.remote_ip4) + self.assertTrue(packet.haslayer(GRE)) + self.check_ip_checksum(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + # out2in + p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + GRE() / + IP(src=self.pg3.remote_ip4, dst=self.pg2.remote_ip4) / + TCP(sport=1234, dport=1234)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg0.get_capture(1) + packet = p[0] + try: + self.assertEqual(packet[IP].src, self.pg1.remote_ip4) + self.assertEqual(packet[IP].dst, self.pg0.remote_ip4) + self.assertTrue(packet.haslayer(GRE)) + self.check_ip_checksum(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + def test_hairpinning_unknown_proto(self): + """ NAT44 translate packet with unknown protocol - hairpinning """ + host = self.pg0.remote_hosts[0] + server = self.pg0.remote_hosts[1] + host_in_port = 1234 + host_out_port = 0 + server_in_port = 5678 + server_out_port = 8765 + server_nat_ip = "10.0.0.11" + + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + # add static mapping for server + self.nat44_add_static_mapping(server.ip4, server_nat_ip) + + # host to server + p = (Ether(src=host.mac, dst=self.pg0.local_mac) / + IP(src=host.ip4, dst=server_nat_ip) / + TCP(sport=host_in_port, dport=server_out_port)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(1) + + p = (Ether(dst=self.pg0.local_mac, src=host.mac) / + IP(src=host.ip4, dst=server_nat_ip) / + GRE() / + IP(src=self.pg2.remote_ip4, dst=self.pg3.remote_ip4) / + TCP(sport=1234, dport=1234)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg0.get_capture(1) + packet = p[0] + try: + self.assertEqual(packet[IP].src, self.nat_addr) + self.assertEqual(packet[IP].dst, server.ip4) + self.assertTrue(packet.haslayer(GRE)) + self.check_ip_checksum(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + # server to host + p = (Ether(dst=self.pg0.local_mac, src=server.mac) / + IP(src=server.ip4, dst=self.nat_addr) / + GRE() / + IP(src=self.pg3.remote_ip4, dst=self.pg2.remote_ip4) / + TCP(sport=1234, dport=1234)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg0.get_capture(1) + packet = p[0] + try: + self.assertEqual(packet[IP].src, server_nat_ip) + self.assertEqual(packet[IP].dst, host.ip4) + self.assertTrue(packet.haslayer(GRE)) + self.check_ip_checksum(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + def test_output_feature(self): + """ NAT44 interface output feature (in2out postrouting) """ + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_output_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_output_feature(self.pg1.sw_if_index, + is_inside=0) + + # in2out + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture) + + # out2in + pkts = self.create_stream_out(self.pg1) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg0) + + def test_output_feature_vrf_aware(self): + """ NAT44 interface output feature VRF aware (in2out postrouting) """ + nat_ip_vrf10 = "10.0.0.10" + nat_ip_vrf20 = "10.0.0.20" + + self.vapi.ip_add_del_route(dst_address=self.pg3.remote_ip4n, + dst_address_length=32, + next_hop_address=self.pg3.remote_ip4n, + next_hop_sw_if_index=self.pg3.sw_if_index, + table_id=10) + self.vapi.ip_add_del_route(dst_address=self.pg3.remote_ip4n, + dst_address_length=32, + next_hop_address=self.pg3.remote_ip4n, + next_hop_sw_if_index=self.pg3.sw_if_index, + table_id=20) + + self.nat44_add_address(nat_ip_vrf10, vrf_id=10) + self.nat44_add_address(nat_ip_vrf20, vrf_id=20) + self.vapi.nat44_interface_add_del_output_feature(self.pg4.sw_if_index) + self.vapi.nat44_interface_add_del_output_feature(self.pg6.sw_if_index) + self.vapi.nat44_interface_add_del_output_feature(self.pg3.sw_if_index, + is_inside=0) + + # in2out VRF 10 + pkts = self.create_stream_in(self.pg4, self.pg3) + self.pg4.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg3.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip=nat_ip_vrf10) + + # out2in VRF 10 + pkts = self.create_stream_out(self.pg3, dst_ip=nat_ip_vrf10) + self.pg3.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg4.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg4) + + # in2out VRF 20 + pkts = self.create_stream_in(self.pg6, self.pg3) + self.pg6.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg3.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip=nat_ip_vrf20) + + # out2in VRF 20 + pkts = self.create_stream_out(self.pg3, dst_ip=nat_ip_vrf20) + self.pg3.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg6.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg6) + + def test_output_feature_hairpinning(self): + """ NAT44 interface output feature hairpinning (in2out postrouting) """ + host = self.pg0.remote_hosts[0] + server = self.pg0.remote_hosts[1] + host_in_port = 1234 + host_out_port = 0 + server_in_port = 5678 + server_out_port = 8765 + + self.nat44_add_address(self.nat_addr) + self.vapi.nat44_interface_add_del_output_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_output_feature(self.pg1.sw_if_index, + is_inside=0) + + # add static mapping for server + self.nat44_add_static_mapping(server.ip4, self.nat_addr, + server_in_port, server_out_port, + proto=IP_PROTOS.tcp) + + # send packet from host to server + p = (Ether(src=host.mac, dst=self.pg0.local_mac) / + IP(src=host.ip4, dst=self.nat_addr) / + TCP(sport=host_in_port, dport=server_out_port)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, self.nat_addr) + self.assertEqual(ip.dst, server.ip4) + self.assertNotEqual(tcp.sport, host_in_port) + self.assertEqual(tcp.dport, server_in_port) + self.check_tcp_checksum(p) + host_out_port = tcp.sport + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # send reply from server to host + p = (Ether(src=server.mac, dst=self.pg0.local_mac) / + IP(src=server.ip4, dst=self.nat_addr) / + TCP(sport=server_in_port, dport=host_out_port)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, self.nat_addr) + self.assertEqual(ip.dst, host.ip4) + self.assertEqual(tcp.sport, server_out_port) + self.assertEqual(tcp.dport, host_in_port) + self.check_tcp_checksum(p) + except: + self.logger.error(ppp("Unexpected or invalid packet:"), p) + raise + + def tearDown(self): + super(TestNAT44, self).tearDown() + if not self.vpp_dead: + self.logger.info(self.vapi.cli("show nat44 verbose")) + self.clear_nat44() + + +class TestDeterministicNAT(MethodHolder): + """ Deterministic NAT Test Cases """ + + @classmethod + def setUpConstants(cls): + super(TestDeterministicNAT, cls).setUpConstants() + cls.vpp_cmdline.extend(["nat", "{", "deterministic", "}"]) + + @classmethod + def setUpClass(cls): + super(TestDeterministicNAT, cls).setUpClass() + + try: + cls.tcp_port_in = 6303 + cls.tcp_external_port = 6303 + cls.udp_port_in = 6304 + cls.udp_external_port = 6304 + cls.icmp_id_in = 6305 + cls.nat_addr = '10.0.0.3' + + cls.create_pg_interfaces(range(3)) + cls.interfaces = list(cls.pg_interfaces) + + for i in cls.interfaces: + i.admin_up() + i.config_ip4() + i.resolve_arp() + + cls.pg0.generate_remote_hosts(2) + cls.pg0.configure_ipv4_neighbors() + + except Exception: + super(TestDeterministicNAT, cls).tearDownClass() + raise + + def create_stream_in(self, in_if, out_if, ttl=64): + """ + Create packet stream for inside network + + :param in_if: Inside interface + :param out_if: Outside interface + :param ttl: TTL of generated packets + """ + pkts = [] + # TCP + p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / + IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port)) + pkts.append(p) + + # UDP + p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / + IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl) / + UDP(sport=self.udp_port_in, dport=self.udp_external_port)) + pkts.append(p) + + # ICMP + p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / + IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl) / + ICMP(id=self.icmp_id_in, type='echo-request')) + pkts.append(p) + + return pkts + + def create_stream_out(self, out_if, dst_ip=None, ttl=64): + """ + Create packet stream for outside network + + :param out_if: Outside interface + :param dst_ip: Destination IP address (Default use global NAT address) + :param ttl: TTL of generated packets + """ + if dst_ip is None: + dst_ip = self.nat_addr + pkts = [] + # TCP + p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) / + IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) / + TCP(dport=self.tcp_port_out, sport=self.tcp_external_port)) + pkts.append(p) + + # UDP + p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) / + IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) / + UDP(dport=self.udp_port_out, sport=self.udp_external_port)) + pkts.append(p) + + # ICMP + p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) / + IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) / + ICMP(id=self.icmp_external_id, type='echo-reply')) + pkts.append(p) + + return pkts + + def verify_capture_out(self, capture, nat_ip=None, packet_num=3): + """ + Verify captured packets on outside network + + :param capture: Captured packets + :param nat_ip: Translated IP address (Default use global NAT address) + :param same_port: Sorce port number is not translated (Default False) + :param packet_num: Expected number of packets (Default 3) + """ + if nat_ip is None: + nat_ip = self.nat_addr + self.assertEqual(packet_num, len(capture)) + for packet in capture: + try: + self.assertEqual(packet[IP].src, nat_ip) + if packet.haslayer(TCP): + self.tcp_port_out = packet[TCP].sport + elif packet.haslayer(UDP): + self.udp_port_out = packet[UDP].sport + else: + self.icmp_external_id = packet[ICMP].id + except: + self.logger.error(ppp("Unexpected or invalid packet " + "(outside network):", packet)) + raise + + def initiate_tcp_session(self, in_if, out_if): + """ + Initiates TCP session + + :param in_if: Inside interface + :param out_if: Outside interface + """ + try: + # SYN packet in->out + p = (Ether(src=in_if.remote_mac, dst=in_if.local_mac) / + IP(src=in_if.remote_ip4, dst=out_if.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="S")) + in_if.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = out_if.get_capture(1) + p = capture[0] + self.tcp_port_out = p[TCP].sport + + # SYN + ACK packet out->in + p = (Ether(src=out_if.remote_mac, dst=out_if.local_mac) / + IP(src=out_if.remote_ip4, dst=self.nat_addr) / + TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, + flags="SA")) + out_if.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + in_if.get_capture(1) + + # ACK packet in->out + p = (Ether(src=in_if.remote_mac, dst=in_if.local_mac) / + IP(src=in_if.remote_ip4, dst=out_if.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="A")) + in_if.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + out_if.get_capture(1) + + except: + self.logger.error("TCP 3 way handshake failed") + raise + + def verify_ipfix_max_entries_per_user(self, data): + """ + Verify IPFIX maximum entries per user exceeded event + + :param data: Decoded IPFIX data records + """ + self.assertEqual(1, len(data)) + record = data[0] + # natEvent + self.assertEqual(ord(record[230]), 13) + # natQuotaExceededEvent + self.assertEqual('\x03\x00\x00\x00', record[466]) + # sourceIPv4Address + self.assertEqual(self.pg0.remote_ip4n, record[8]) + + def test_deterministic_mode(self): + """ NAT plugin run deterministic mode """ + in_addr = '172.16.255.0' + out_addr = '172.17.255.50' + in_addr_t = '172.16.255.20' + in_addr_n = socket.inet_aton(in_addr) + out_addr_n = socket.inet_aton(out_addr) + in_addr_t_n = socket.inet_aton(in_addr_t) + in_plen = 24 + out_plen = 32 + + nat_config = self.vapi.nat_show_config() + self.assertEqual(1, nat_config.deterministic) + + self.vapi.nat_det_add_del_map(in_addr_n, in_plen, out_addr_n, out_plen) + + rep1 = self.vapi.nat_det_forward(in_addr_t_n) + self.assertEqual(rep1.out_addr[:4], out_addr_n) + rep2 = self.vapi.nat_det_reverse(out_addr_n, rep1.out_port_hi) + self.assertEqual(rep2.in_addr[:4], in_addr_t_n) + + deterministic_mappings = self.vapi.nat_det_map_dump() + self.assertEqual(len(deterministic_mappings), 1) + dsm = deterministic_mappings[0] + self.assertEqual(in_addr_n, dsm.in_addr[:4]) + self.assertEqual(in_plen, dsm.in_plen) + self.assertEqual(out_addr_n, dsm.out_addr[:4]) + self.assertEqual(out_plen, dsm.out_plen) + + self.clear_nat_det() + deterministic_mappings = self.vapi.nat_det_map_dump() + self.assertEqual(len(deterministic_mappings), 0) + + def test_set_timeouts(self): + """ Set deterministic NAT timeouts """ + timeouts_before = self.vapi.nat_det_get_timeouts() + + self.vapi.nat_det_set_timeouts(timeouts_before.udp + 10, + timeouts_before.tcp_established + 10, + timeouts_before.tcp_transitory + 10, + timeouts_before.icmp + 10) + + timeouts_after = self.vapi.nat_det_get_timeouts() + + self.assertNotEqual(timeouts_before.udp, timeouts_after.udp) + self.assertNotEqual(timeouts_before.icmp, timeouts_after.icmp) + self.assertNotEqual(timeouts_before.tcp_established, + timeouts_after.tcp_established) + self.assertNotEqual(timeouts_before.tcp_transitory, + timeouts_after.tcp_transitory) + + def test_det_in(self): + """ Deterministic NAT translation test (TCP, UDP, ICMP) """ + + nat_ip = "10.0.0.10" + + self.vapi.nat_det_add_del_map(self.pg0.remote_ip4n, + 32, + socket.inet_aton(nat_ip), + 32) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + # in2out + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip) + + # out2in + pkts = self.create_stream_out(self.pg1, nat_ip) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + self.verify_capture_in(capture, self.pg0) + + # session dump test + sessions = self.vapi.nat_det_session_dump(self.pg0.remote_ip4n) + self.assertEqual(len(sessions), 3) + + # TCP session + s = sessions[0] + self.assertEqual(s.ext_addr[:4], self.pg1.remote_ip4n) + self.assertEqual(s.in_port, self.tcp_port_in) + self.assertEqual(s.out_port, self.tcp_port_out) + self.assertEqual(s.ext_port, self.tcp_external_port) + + # UDP session + s = sessions[1] + self.assertEqual(s.ext_addr[:4], self.pg1.remote_ip4n) + self.assertEqual(s.in_port, self.udp_port_in) + self.assertEqual(s.out_port, self.udp_port_out) + self.assertEqual(s.ext_port, self.udp_external_port) + + # ICMP session + s = sessions[2] + self.assertEqual(s.ext_addr[:4], self.pg1.remote_ip4n) + self.assertEqual(s.in_port, self.icmp_id_in) + self.assertEqual(s.out_port, self.icmp_external_id) + + def test_multiple_users(self): + """ Deterministic NAT multiple users """ + + nat_ip = "10.0.0.10" + port_in = 80 + external_port = 6303 + + host0 = self.pg0.remote_hosts[0] + host1 = self.pg0.remote_hosts[1] + + self.vapi.nat_det_add_del_map(host0.ip4n, + 24, + socket.inet_aton(nat_ip), + 32) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + # host0 to out + p = (Ether(src=host0.mac, dst=self.pg0.local_mac) / + IP(src=host0.ip4, dst=self.pg1.remote_ip4) / + TCP(sport=port_in, dport=external_port)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, nat_ip) + self.assertEqual(ip.dst, self.pg1.remote_ip4) + self.assertEqual(tcp.dport, external_port) + port_out0 = tcp.sport + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # host1 to out + p = (Ether(src=host1.mac, dst=self.pg0.local_mac) / + IP(src=host1.ip4, dst=self.pg1.remote_ip4) / + TCP(sport=port_in, dport=external_port)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, nat_ip) + self.assertEqual(ip.dst, self.pg1.remote_ip4) + self.assertEqual(tcp.dport, external_port) + port_out1 = tcp.sport + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + dms = self.vapi.nat_det_map_dump() + self.assertEqual(1, len(dms)) + self.assertEqual(2, dms[0].ses_num) + + # out to host0 + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=nat_ip) / + TCP(sport=external_port, dport=port_out0)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, self.pg1.remote_ip4) + self.assertEqual(ip.dst, host0.ip4) + self.assertEqual(tcp.dport, port_in) + self.assertEqual(tcp.sport, external_port) + except: + self.logger.error(ppp("Unexpected or invalid packet:", p)) + raise + + # out to host1 + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=nat_ip) / + TCP(sport=external_port, dport=port_out1)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(1) + p = capture[0] + try: + ip = p[IP] + tcp = p[TCP] + self.assertEqual(ip.src, self.pg1.remote_ip4) + self.assertEqual(ip.dst, host1.ip4) + self.assertEqual(tcp.dport, port_in) + self.assertEqual(tcp.sport, external_port) + except: + self.logger.error(ppp("Unexpected or invalid packet", p)) + raise + + # session close api test + self.vapi.nat_det_close_session_out(socket.inet_aton(nat_ip), + port_out1, + self.pg1.remote_ip4n, + external_port) + dms = self.vapi.nat_det_map_dump() + self.assertEqual(dms[0].ses_num, 1) + + self.vapi.nat_det_close_session_in(host0.ip4n, + port_in, + self.pg1.remote_ip4n, + external_port) + dms = self.vapi.nat_det_map_dump() + self.assertEqual(dms[0].ses_num, 0) + + def test_tcp_session_close_detection_in(self): + """ Deterministic NAT TCP session close from inside network """ + self.vapi.nat_det_add_del_map(self.pg0.remote_ip4n, + 32, + socket.inet_aton(self.nat_addr), + 32) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + self.initiate_tcp_session(self.pg0, self.pg1) + + # close the session from inside + try: + # FIN packet in -> out + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="F")) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(1) + + pkts = [] + + # ACK packet out -> in + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, + flags="A")) + pkts.append(p) + + # FIN packet out -> in + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, + flags="F")) + pkts.append(p) + + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.get_capture(2) + + # ACK packet in -> out + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="A")) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(1) + + # Check if deterministic NAT44 closed the session + dms = self.vapi.nat_det_map_dump() + self.assertEqual(0, dms[0].ses_num) + except: + self.logger.error("TCP session termination failed") + raise + + def test_tcp_session_close_detection_out(self): + """ Deterministic NAT TCP session close from outside network """ + self.vapi.nat_det_add_del_map(self.pg0.remote_ip4n, + 32, + socket.inet_aton(self.nat_addr), + 32) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + self.initiate_tcp_session(self.pg0, self.pg1) + + # close the session from outside + try: + # FIN packet out -> in + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, + flags="F")) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.get_capture(1) + + pkts = [] + + # ACK packet in -> out + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="A")) + pkts.append(p) + + # ACK packet in -> out + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, + flags="F")) + pkts.append(p) + + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg1.get_capture(2) + + # ACK packet out -> in + p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, + flags="A")) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + self.pg0.get_capture(1) + + # Check if deterministic NAT44 closed the session + dms = self.vapi.nat_det_map_dump() + self.assertEqual(0, dms[0].ses_num) + except: + self.logger.error("TCP session termination failed") + raise + + @unittest.skipUnless(running_extended_tests(), "part of extended tests") + def test_session_timeout(self): + """ Deterministic NAT session timeouts """ + self.vapi.nat_det_add_del_map(self.pg0.remote_ip4n, + 32, + socket.inet_aton(self.nat_addr), + 32) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + + self.initiate_tcp_session(self.pg0, self.pg1) + self.vapi.nat_det_set_timeouts(5, 5, 5, 5) + pkts = self.create_stream_in(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + sleep(15) + + dms = self.vapi.nat_det_map_dump() + self.assertEqual(0, dms[0].ses_num) + + @unittest.skipUnless(running_extended_tests(), "part of extended tests") + def test_session_limit_per_user(self): + """ Deterministic NAT maximum sessions per user limit """ + self.vapi.nat_det_add_del_map(self.pg0.remote_ip4n, + 32, + socket.inet_aton(self.nat_addr), + 32) + self.vapi.nat44_interface_add_del_feature(self.pg0.sw_if_index) + self.vapi.nat44_interface_add_del_feature(self.pg1.sw_if_index, + is_inside=0) + self.vapi.set_ipfix_exporter(collector_address=self.pg2.remote_ip4n, + src_address=self.pg2.local_ip4n, + path_mtu=512, + template_interval=10) + self.vapi.nat_ipfix() + + pkts = [] + for port in range(1025, 2025): + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + UDP(sport=port, dport=port)) + pkts.append(p) + + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + + p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / + UDP(sport=3001, dport=3002)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.assert_nothing_captured() + + # verify ICMP error packet + capture = self.pg0.get_capture(1) + p = capture[0] + self.assertTrue(p.haslayer(ICMP)) + icmp = p[ICMP] + self.assertEqual(icmp.type, 3) + self.assertEqual(icmp.code, 1) + self.assertTrue(icmp.haslayer(IPerror)) + inner_ip = icmp[IPerror] + self.assertEqual(inner_ip[UDPerror].sport, 3001) + self.assertEqual(inner_ip[UDPerror].dport, 3002) + + dms = self.vapi.nat_det_map_dump() + + self.assertEqual(1000, dms[0].ses_num) + + # verify IPFIX logging + self.vapi.cli("ipfix flush") # FIXME this should be an API call + sleep(1) + capture = self.pg2.get_capture(2) + ipfix = IPFIXDecoder() + # first load template + for p in capture: + self.assertTrue(p.haslayer(IPFIX)) + if p.haslayer(Template): + ipfix.add_template(p.getlayer(Template)) + # verify events in data set + for p in capture: + if p.haslayer(Data): + data = ipfix.decode_data_set(p.getlayer(Set)) + self.verify_ipfix_max_entries_per_user(data) + + def clear_nat_det(self): + """ + Clear deterministic NAT configuration. + """ + self.vapi.nat_ipfix(enable=0) + self.vapi.nat_det_set_timeouts() + deterministic_mappings = self.vapi.nat_det_map_dump() + for dsm in deterministic_mappings: + self.vapi.nat_det_add_del_map(dsm.in_addr, + dsm.in_plen, + dsm.out_addr, + dsm.out_plen, + is_add=0) + + interfaces = self.vapi.nat44_interface_dump() + for intf in interfaces: + self.vapi.nat44_interface_add_del_feature(intf.sw_if_index, + intf.is_inside, + is_add=0) + + def tearDown(self): + super(TestDeterministicNAT, self).tearDown() + if not self.vpp_dead: + self.logger.info(self.vapi.cli("show nat44 detail")) + self.clear_nat_det() + + +class TestNAT64(MethodHolder): + """ NAT64 Test Cases """ + + @classmethod + def setUpClass(cls): + super(TestNAT64, cls).setUpClass() + + try: + cls.tcp_port_in = 6303 + cls.tcp_port_out = 6303 + cls.udp_port_in = 6304 + cls.udp_port_out = 6304 + cls.icmp_id_in = 6305 + cls.icmp_id_out = 6305 + cls.nat_addr = '10.0.0.3' + cls.nat_addr_n = socket.inet_pton(socket.AF_INET, cls.nat_addr) + cls.vrf1_id = 10 + cls.vrf1_nat_addr = '10.0.10.3' + cls.vrf1_nat_addr_n = socket.inet_pton(socket.AF_INET, + cls.vrf1_nat_addr) + + cls.create_pg_interfaces(range(3)) + cls.ip6_interfaces = list(cls.pg_interfaces[0:1]) + cls.ip6_interfaces.append(cls.pg_interfaces[2]) + cls.ip4_interfaces = list(cls.pg_interfaces[1:2]) + + cls.pg_interfaces[2].set_table_ip6(cls.vrf1_id) + + cls.pg0.generate_remote_hosts(2) + + for i in cls.ip6_interfaces: + i.admin_up() + i.config_ip6() + i.configure_ipv6_neighbors() + + for i in cls.ip4_interfaces: + i.admin_up() + i.config_ip4() + i.resolve_arp() + + except Exception: + super(TestNAT64, cls).tearDownClass() + raise + + def test_pool(self): + """ Add/delete address to NAT64 pool """ + nat_addr = socket.inet_pton(socket.AF_INET, '1.2.3.4') + + self.vapi.nat64_add_del_pool_addr_range(nat_addr, nat_addr) + + addresses = self.vapi.nat64_pool_addr_dump() + self.assertEqual(len(addresses), 1) + self.assertEqual(addresses[0].address, nat_addr) + + self.vapi.nat64_add_del_pool_addr_range(nat_addr, nat_addr, is_add=0) + + addresses = self.vapi.nat64_pool_addr_dump() + self.assertEqual(len(addresses), 0) + + def test_interface(self): + """ Enable/disable NAT64 feature on the interface """ + self.vapi.nat64_add_del_interface(self.pg0.sw_if_index) + self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0) + + interfaces = self.vapi.nat64_interface_dump() + self.assertEqual(len(interfaces), 2) + pg0_found = False + pg1_found = False + for intf in interfaces: + if intf.sw_if_index == self.pg0.sw_if_index: + self.assertEqual(intf.is_inside, 1) + pg0_found = True + elif intf.sw_if_index == self.pg1.sw_if_index: + self.assertEqual(intf.is_inside, 0) + pg1_found = True + self.assertTrue(pg0_found) + self.assertTrue(pg1_found) + + features = self.vapi.cli("show interface features pg0") + self.assertNotEqual(features.find('nat64-in2out'), -1) + features = self.vapi.cli("show interface features pg1") + self.assertNotEqual(features.find('nat64-out2in'), -1) + + self.vapi.nat64_add_del_interface(self.pg0.sw_if_index, is_add=0) + self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_add=0) + + interfaces = self.vapi.nat64_interface_dump() + self.assertEqual(len(interfaces), 0) + + def test_static_bib(self): + """ Add/delete static BIB entry """ + in_addr = socket.inet_pton(socket.AF_INET6, + '2001:db8:85a3::8a2e:370:7334') + out_addr = socket.inet_pton(socket.AF_INET, '10.1.1.3') + in_port = 1234 + out_port = 5678 + proto = IP_PROTOS.tcp + + self.vapi.nat64_add_del_static_bib(in_addr, + out_addr, + in_port, + out_port, + proto) + bib = self.vapi.nat64_bib_dump(IP_PROTOS.tcp) + static_bib_num = 0 + for bibe in bib: + if bibe.is_static: + static_bib_num += 1 + self.assertEqual(bibe.i_addr, in_addr) + self.assertEqual(bibe.o_addr, out_addr) + self.assertEqual(bibe.i_port, in_port) + self.assertEqual(bibe.o_port, out_port) + self.assertEqual(static_bib_num, 1) + + self.vapi.nat64_add_del_static_bib(in_addr, + out_addr, + in_port, + out_port, + proto, + is_add=0) + bib = self.vapi.nat64_bib_dump(IP_PROTOS.tcp) + static_bib_num = 0 + for bibe in bib: + if bibe.is_static: + static_bib_num += 1 + self.assertEqual(static_bib_num, 0) + + def test_set_timeouts(self): + """ Set NAT64 timeouts """ + # verify default values + timeouts = self.vapi.nat64_get_timeouts() + self.assertEqual(timeouts.udp, 300) + self.assertEqual(timeouts.icmp, 60) + self.assertEqual(timeouts.tcp_trans, 240) + self.assertEqual(timeouts.tcp_est, 7440) + self.assertEqual(timeouts.tcp_incoming_syn, 6) + + # set and verify custom values + self.vapi.nat64_set_timeouts(udp=200, icmp=30, tcp_trans=250, + tcp_est=7450, tcp_incoming_syn=10) + timeouts = self.vapi.nat64_get_timeouts() + self.assertEqual(timeouts.udp, 200) + self.assertEqual(timeouts.icmp, 30) + self.assertEqual(timeouts.tcp_trans, 250) + self.assertEqual(timeouts.tcp_est, 7450) + self.assertEqual(timeouts.tcp_incoming_syn, 10) + + def test_dynamic(self): + """ NAT64 dynamic translation test """ + self.tcp_port_in = 6303 + self.udp_port_in = 6304 + self.icmp_id_in = 6305 + + ses_num_start = self.nat64_get_ses_num() + + self.vapi.nat64_add_del_pool_addr_range(self.nat_addr_n, + self.nat_addr_n) + self.vapi.nat64_add_del_interface(self.pg0.sw_if_index) + self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0) + + # in2out + pkts = self.create_stream_in_ip6(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip=self.nat_addr, + dst_ip=self.pg1.remote_ip4) + + # out2in + pkts = self.create_stream_out(self.pg1, dst_ip=self.nat_addr) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + ip = IPv6(src=''.join(['64:ff9b::', self.pg1.remote_ip4])) + self.verify_capture_in_ip6(capture, ip[IPv6].src, self.pg0.remote_ip6) + + # in2out + pkts = self.create_stream_in_ip6(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip=self.nat_addr, + dst_ip=self.pg1.remote_ip4) + + # out2in + pkts = self.create_stream_out(self.pg1, dst_ip=self.nat_addr) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + self.verify_capture_in_ip6(capture, ip[IPv6].src, self.pg0.remote_ip6) + + ses_num_end = self.nat64_get_ses_num() + + self.assertEqual(ses_num_end - ses_num_start, 3) + + # tenant with specific VRF + self.vapi.nat64_add_del_pool_addr_range(self.vrf1_nat_addr_n, + self.vrf1_nat_addr_n, + vrf_id=self.vrf1_id) + self.vapi.nat64_add_del_interface(self.pg2.sw_if_index) + + pkts = self.create_stream_in_ip6(self.pg2, self.pg1) + self.pg2.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip=self.vrf1_nat_addr, + dst_ip=self.pg1.remote_ip4) + + pkts = self.create_stream_out(self.pg1, dst_ip=self.vrf1_nat_addr) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg2.get_capture(len(pkts)) + self.verify_capture_in_ip6(capture, ip[IPv6].src, self.pg2.remote_ip6) + + def test_static(self): + """ NAT64 static translation test """ + self.tcp_port_in = 60303 + self.udp_port_in = 60304 + self.icmp_id_in = 60305 + self.tcp_port_out = 60303 + self.udp_port_out = 60304 + self.icmp_id_out = 60305 + + ses_num_start = self.nat64_get_ses_num() + + self.vapi.nat64_add_del_pool_addr_range(self.nat_addr_n, + self.nat_addr_n) + self.vapi.nat64_add_del_interface(self.pg0.sw_if_index) + self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0) + + self.vapi.nat64_add_del_static_bib(self.pg0.remote_ip6n, + self.nat_addr_n, + self.tcp_port_in, + self.tcp_port_out, + IP_PROTOS.tcp) + self.vapi.nat64_add_del_static_bib(self.pg0.remote_ip6n, + self.nat_addr_n, + self.udp_port_in, + self.udp_port_out, + IP_PROTOS.udp) + self.vapi.nat64_add_del_static_bib(self.pg0.remote_ip6n, + self.nat_addr_n, + self.icmp_id_in, + self.icmp_id_out, + IP_PROTOS.icmp) + + # in2out + pkts = self.create_stream_in_ip6(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip=self.nat_addr, + dst_ip=self.pg1.remote_ip4, same_port=True) + + # out2in + pkts = self.create_stream_out(self.pg1, dst_ip=self.nat_addr) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + ip = IPv6(src=''.join(['64:ff9b::', self.pg1.remote_ip4])) + self.verify_capture_in_ip6(capture, ip[IPv6].src, self.pg0.remote_ip6) + + ses_num_end = self.nat64_get_ses_num() + + self.assertEqual(ses_num_end - ses_num_start, 3) + + @unittest.skipUnless(running_extended_tests(), "part of extended tests") + def test_session_timeout(self): + """ NAT64 session timeout """ + self.icmp_id_in = 1234 + self.vapi.nat64_add_del_pool_addr_range(self.nat_addr_n, + self.nat_addr_n) + self.vapi.nat64_add_del_interface(self.pg0.sw_if_index) + self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0) + self.vapi.nat64_set_timeouts(icmp=5) + + pkts = self.create_stream_in_ip6(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + + ses_num_before_timeout = self.nat64_get_ses_num() + + sleep(15) + + # ICMP session after timeout + ses_num_after_timeout = self.nat64_get_ses_num() + self.assertNotEqual(ses_num_before_timeout, ses_num_after_timeout) + + def test_icmp_error(self): + """ NAT64 ICMP Error message translation """ + self.tcp_port_in = 6303 + self.udp_port_in = 6304 + self.icmp_id_in = 6305 + + ses_num_start = self.nat64_get_ses_num() + + self.vapi.nat64_add_del_pool_addr_range(self.nat_addr_n, + self.nat_addr_n) + self.vapi.nat64_add_del_interface(self.pg0.sw_if_index) + self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0) + + # send some packets to create sessions + pkts = self.create_stream_in_ip6(self.pg0, self.pg1) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture_ip4 = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture_ip4, + nat_ip=self.nat_addr, + dst_ip=self.pg1.remote_ip4) + + pkts = self.create_stream_out(self.pg1, dst_ip=self.nat_addr) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture_ip6 = self.pg0.get_capture(len(pkts)) + ip = IPv6(src=''.join(['64:ff9b::', self.pg1.remote_ip4])) + self.verify_capture_in_ip6(capture_ip6, ip[IPv6].src, + self.pg0.remote_ip6) + + # in2out + pkts = [Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=self.pg0.remote_ip6, dst=ip[IPv6].src) / + ICMPv6DestUnreach(code=1) / + packet[IPv6] for packet in capture_ip6] + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + for packet in capture: + try: + self.assertEqual(packet[IP].src, self.nat_addr) + self.assertEqual(packet[IP].dst, self.pg1.remote_ip4) + self.assertEqual(packet[ICMP].type, 3) + self.assertEqual(packet[ICMP].code, 13) + inner = packet[IPerror] + self.assertEqual(inner.src, self.pg1.remote_ip4) + self.assertEqual(inner.dst, self.nat_addr) + self.check_icmp_checksum(packet) + if inner.haslayer(TCPerror): + self.assertEqual(inner[TCPerror].dport, self.tcp_port_out) + elif inner.haslayer(UDPerror): + self.assertEqual(inner[UDPerror].dport, self.udp_port_out) + else: + self.assertEqual(inner[ICMPerror].id, self.icmp_id_out) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + # out2in + pkts = [Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + ICMP(type=3, code=13) / + packet[IP] for packet in capture_ip4] + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + for packet in capture: + try: + self.assertEqual(packet[IPv6].src, ip.src) + self.assertEqual(packet[IPv6].dst, self.pg0.remote_ip6) + icmp = packet[ICMPv6DestUnreach] + self.assertEqual(icmp.code, 1) + inner = icmp[IPerror6] + self.assertEqual(inner.src, self.pg0.remote_ip6) + self.assertEqual(inner.dst, ip.src) + self.check_icmpv6_checksum(packet) + if inner.haslayer(TCPerror): + self.assertEqual(inner[TCPerror].sport, self.tcp_port_in) + elif inner.haslayer(UDPerror): + self.assertEqual(inner[UDPerror].sport, self.udp_port_in) + else: + self.assertEqual(inner[ICMPv6EchoRequest].id, + self.icmp_id_in) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + def test_hairpinning(self): + """ NAT64 hairpinning """ + + client = self.pg0.remote_hosts[0] + server = self.pg0.remote_hosts[1] + server_tcp_in_port = 22 + server_tcp_out_port = 4022 + server_udp_in_port = 23 + server_udp_out_port = 4023 + client_tcp_in_port = 1234 + client_udp_in_port = 1235 + client_tcp_out_port = 0 + client_udp_out_port = 0 + ip = IPv6(src=''.join(['64:ff9b::', self.nat_addr])) + nat_addr_ip6 = ip.src + + self.vapi.nat64_add_del_pool_addr_range(self.nat_addr_n, + self.nat_addr_n) + self.vapi.nat64_add_del_interface(self.pg0.sw_if_index) + self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0) + + self.vapi.nat64_add_del_static_bib(server.ip6n, + self.nat_addr_n, + server_tcp_in_port, + server_tcp_out_port, + IP_PROTOS.tcp) + self.vapi.nat64_add_del_static_bib(server.ip6n, + self.nat_addr_n, + server_udp_in_port, + server_udp_out_port, + IP_PROTOS.udp) + + # client to server + pkts = [] + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=client.ip6, dst=nat_addr_ip6) / + TCP(sport=client_tcp_in_port, dport=server_tcp_out_port)) + pkts.append(p) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=client.ip6, dst=nat_addr_ip6) / + UDP(sport=client_udp_in_port, dport=server_udp_out_port)) + pkts.append(p) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + for packet in capture: + try: + self.assertEqual(packet[IPv6].src, nat_addr_ip6) + self.assertEqual(packet[IPv6].dst, server.ip6) + if packet.haslayer(TCP): + self.assertNotEqual(packet[TCP].sport, client_tcp_in_port) + self.assertEqual(packet[TCP].dport, server_tcp_in_port) + self.check_tcp_checksum(packet) + client_tcp_out_port = packet[TCP].sport + else: + self.assertNotEqual(packet[UDP].sport, client_udp_in_port) + self.assertEqual(packet[UDP].dport, server_udp_in_port) + self.check_udp_checksum(packet) + client_udp_out_port = packet[UDP].sport + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + # server to client + pkts = [] + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=server.ip6, dst=nat_addr_ip6) / + TCP(sport=server_tcp_in_port, dport=client_tcp_out_port)) + pkts.append(p) + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=server.ip6, dst=nat_addr_ip6) / + UDP(sport=server_udp_in_port, dport=client_udp_out_port)) + pkts.append(p) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + for packet in capture: + try: + self.assertEqual(packet[IPv6].src, nat_addr_ip6) + self.assertEqual(packet[IPv6].dst, client.ip6) + if packet.haslayer(TCP): + self.assertEqual(packet[TCP].sport, server_tcp_out_port) + self.assertEqual(packet[TCP].dport, client_tcp_in_port) + self.check_tcp_checksum(packet) + else: + self.assertEqual(packet[UDP].sport, server_udp_out_port) + self.assertEqual(packet[UDP].dport, client_udp_in_port) + self.check_udp_checksum(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + # ICMP error + pkts = [] + pkts = [Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=client.ip6, dst=nat_addr_ip6) / + ICMPv6DestUnreach(code=1) / + packet[IPv6] for packet in capture] + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + for packet in capture: + try: + self.assertEqual(packet[IPv6].src, nat_addr_ip6) + self.assertEqual(packet[IPv6].dst, server.ip6) + icmp = packet[ICMPv6DestUnreach] + self.assertEqual(icmp.code, 1) + inner = icmp[IPerror6] + self.assertEqual(inner.src, server.ip6) + self.assertEqual(inner.dst, nat_addr_ip6) + self.check_icmpv6_checksum(packet) + if inner.haslayer(TCPerror): + self.assertEqual(inner[TCPerror].sport, server_tcp_in_port) + self.assertEqual(inner[TCPerror].dport, + client_tcp_out_port) + else: + self.assertEqual(inner[UDPerror].sport, server_udp_in_port) + self.assertEqual(inner[UDPerror].dport, + client_udp_out_port) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + def test_prefix(self): + """ NAT64 Network-Specific Prefix """ + + self.vapi.nat64_add_del_pool_addr_range(self.nat_addr_n, + self.nat_addr_n) + self.vapi.nat64_add_del_interface(self.pg0.sw_if_index) + self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0) + self.vapi.nat64_add_del_pool_addr_range(self.vrf1_nat_addr_n, + self.vrf1_nat_addr_n, + vrf_id=self.vrf1_id) + self.vapi.nat64_add_del_interface(self.pg2.sw_if_index) + + # Add global prefix + global_pref64 = "2001:db8::" + global_pref64_n = socket.inet_pton(socket.AF_INET6, global_pref64) + global_pref64_len = 32 + self.vapi.nat64_add_del_prefix(global_pref64_n, global_pref64_len) + + prefix = self.vapi.nat64_prefix_dump() + self.assertEqual(len(prefix), 1) + self.assertEqual(prefix[0].prefix, global_pref64_n) + self.assertEqual(prefix[0].prefix_len, global_pref64_len) + self.assertEqual(prefix[0].vrf_id, 0) + + # Add tenant specific prefix + vrf1_pref64 = "2001:db8:122:300::" + vrf1_pref64_n = socket.inet_pton(socket.AF_INET6, vrf1_pref64) + vrf1_pref64_len = 56 + self.vapi.nat64_add_del_prefix(vrf1_pref64_n, + vrf1_pref64_len, + vrf_id=self.vrf1_id) + prefix = self.vapi.nat64_prefix_dump() + self.assertEqual(len(prefix), 2) + + # Global prefix + pkts = self.create_stream_in_ip6(self.pg0, + self.pg1, + pref=global_pref64, + plen=global_pref64_len) + self.pg0.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip=self.nat_addr, + dst_ip=self.pg1.remote_ip4) + + pkts = self.create_stream_out(self.pg1, dst_ip=self.nat_addr) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg0.get_capture(len(pkts)) + dst_ip = self.compose_ip6(self.pg1.remote_ip4, + global_pref64, + global_pref64_len) + self.verify_capture_in_ip6(capture, dst_ip, self.pg0.remote_ip6) + + # Tenant specific prefix + pkts = self.create_stream_in_ip6(self.pg2, + self.pg1, + pref=vrf1_pref64, + plen=vrf1_pref64_len) + self.pg2.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg1.get_capture(len(pkts)) + self.verify_capture_out(capture, nat_ip=self.vrf1_nat_addr, + dst_ip=self.pg1.remote_ip4) + + pkts = self.create_stream_out(self.pg1, dst_ip=self.vrf1_nat_addr) + self.pg1.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + capture = self.pg2.get_capture(len(pkts)) + dst_ip = self.compose_ip6(self.pg1.remote_ip4, + vrf1_pref64, + vrf1_pref64_len) + self.verify_capture_in_ip6(capture, dst_ip, self.pg2.remote_ip6) + + def test_unknown_proto(self): + """ NAT64 translate packet with unknown protocol """ + + self.vapi.nat64_add_del_pool_addr_range(self.nat_addr_n, + self.nat_addr_n) + self.vapi.nat64_add_del_interface(self.pg0.sw_if_index) + self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0) + remote_ip6 = self.compose_ip6(self.pg1.remote_ip4, '64:ff9b::', 96) + + # in2out + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=self.pg0.remote_ip6, dst=remote_ip6) / + TCP(sport=self.tcp_port_in, dport=20)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg1.get_capture(1) + + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=self.pg0.remote_ip6, dst=remote_ip6, nh=47) / + GRE() / + IP(src=self.pg2.local_ip4, dst=self.pg2.remote_ip4) / + TCP(sport=1234, dport=1234)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg1.get_capture(1) + packet = p[0] + try: + self.assertEqual(packet[IP].src, self.nat_addr) + self.assertEqual(packet[IP].dst, self.pg1.remote_ip4) + self.assertTrue(packet.haslayer(GRE)) + self.check_ip_checksum(packet) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + # out2in + p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / + IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / + GRE() / + IP(src=self.pg2.remote_ip4, dst=self.pg2.local_ip4) / + TCP(sport=1234, dport=1234)) + self.pg1.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg0.get_capture(1) + packet = p[0] + try: + self.assertEqual(packet[IPv6].src, remote_ip6) + self.assertEqual(packet[IPv6].dst, self.pg0.remote_ip6) + self.assertEqual(packet[IPv6].nh, 47) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + def test_hairpinning_unknown_proto(self): + """ NAT64 translate packet with unknown protocol - hairpinning """ + + client = self.pg0.remote_hosts[0] + server = self.pg0.remote_hosts[1] + server_tcp_in_port = 22 + server_tcp_out_port = 4022 + client_tcp_in_port = 1234 + client_tcp_out_port = 1235 + server_nat_ip = "10.0.0.100" + client_nat_ip = "10.0.0.110" + server_nat_ip_n = socket.inet_pton(socket.AF_INET, server_nat_ip) + client_nat_ip_n = socket.inet_pton(socket.AF_INET, client_nat_ip) + server_nat_ip6 = self.compose_ip6(server_nat_ip, '64:ff9b::', 96) + client_nat_ip6 = self.compose_ip6(client_nat_ip, '64:ff9b::', 96) + + self.vapi.nat64_add_del_pool_addr_range(server_nat_ip_n, + client_nat_ip_n) + self.vapi.nat64_add_del_interface(self.pg0.sw_if_index) + self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0) + + self.vapi.nat64_add_del_static_bib(server.ip6n, + server_nat_ip_n, + server_tcp_in_port, + server_tcp_out_port, + IP_PROTOS.tcp) + + self.vapi.nat64_add_del_static_bib(server.ip6n, + server_nat_ip_n, + 0, + 0, + IP_PROTOS.gre) + + self.vapi.nat64_add_del_static_bib(client.ip6n, + client_nat_ip_n, + client_tcp_in_port, + client_tcp_out_port, + IP_PROTOS.tcp) + + # client to server + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=client.ip6, dst=server_nat_ip6) / + TCP(sport=client_tcp_in_port, dport=server_tcp_out_port)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg0.get_capture(1) + + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=client.ip6, dst=server_nat_ip6, nh=IP_PROTOS.gre) / + GRE() / + IP(src=self.pg2.local_ip4, dst=self.pg2.remote_ip4) / + TCP(sport=1234, dport=1234)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg0.get_capture(1) + packet = p[0] + try: + self.assertEqual(packet[IPv6].src, client_nat_ip6) + self.assertEqual(packet[IPv6].dst, server.ip6) + self.assertEqual(packet[IPv6].nh, IP_PROTOS.gre) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + # server to client + p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src=server.ip6, dst=client_nat_ip6, nh=IP_PROTOS.gre) / + GRE() / + IP(src=self.pg2.remote_ip4, dst=self.pg2.local_ip4) / + TCP(sport=1234, dport=1234)) + self.pg0.add_stream(p) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + p = self.pg0.get_capture(1) + packet = p[0] + try: + self.assertEqual(packet[IPv6].src, server_nat_ip6) + self.assertEqual(packet[IPv6].dst, client.ip6) + self.assertEqual(packet[IPv6].nh, IP_PROTOS.gre) + except: + self.logger.error(ppp("Unexpected or invalid packet:", packet)) + raise + + def nat64_get_ses_num(self): + """ + Return number of active NAT64 sessions. + """ + st = self.vapi.nat64_st_dump() + return len(st) + + def clear_nat64(self): + """ + Clear NAT64 configuration. + """ + self.vapi.nat64_set_timeouts() + + interfaces = self.vapi.nat64_interface_dump() + for intf in interfaces: + self.vapi.nat64_add_del_interface(intf.sw_if_index, + intf.is_inside, + is_add=0) + + bib = self.vapi.nat64_bib_dump(IP_PROTOS.tcp) + for bibe in bib: + if bibe.is_static: + self.vapi.nat64_add_del_static_bib(bibe.i_addr, + bibe.o_addr, + bibe.i_port, + bibe.o_port, + bibe.proto, + bibe.vrf_id, + is_add=0) + + bib = self.vapi.nat64_bib_dump(IP_PROTOS.udp) + for bibe in bib: + if bibe.is_static: + self.vapi.nat64_add_del_static_bib(bibe.i_addr, + bibe.o_addr, + bibe.i_port, + bibe.o_port, + bibe.proto, + bibe.vrf_id, + is_add=0) + + bib = self.vapi.nat64_bib_dump(IP_PROTOS.icmp) + for bibe in bib: + if bibe.is_static: + self.vapi.nat64_add_del_static_bib(bibe.i_addr, + bibe.o_addr, + bibe.i_port, + bibe.o_port, + bibe.proto, + bibe.vrf_id, + is_add=0) + + adresses = self.vapi.nat64_pool_addr_dump() + for addr in adresses: + self.vapi.nat64_add_del_pool_addr_range(addr.address, + addr.address, + vrf_id=addr.vrf_id, + is_add=0) + + prefixes = self.vapi.nat64_prefix_dump() + for prefix in prefixes: + self.vapi.nat64_add_del_prefix(prefix.prefix, + prefix.prefix_len, + vrf_id=prefix.vrf_id, + is_add=0) + + def tearDown(self): + super(TestNAT64, self).tearDown() + if not self.vpp_dead: + self.logger.info(self.vapi.cli("show nat64 pool")) + self.logger.info(self.vapi.cli("show nat64 interfaces")) + self.logger.info(self.vapi.cli("show nat64 prefix")) + self.logger.info(self.vapi.cli("show nat64 bib all")) + self.logger.info(self.vapi.cli("show nat64 session table all")) + self.clear_nat64() + +if __name__ == '__main__': + unittest.main(testRunner=VppTestRunner) diff --git a/test/test_snat.py b/test/test_snat.py deleted file mode 100644 index eb47bbb8..00000000 --- a/test/test_snat.py +++ /dev/null @@ -1,3718 +0,0 @@ -#!/usr/bin/env python - -import socket -import unittest -import struct - -from framework import VppTestCase, VppTestRunner, running_extended_tests -from scapy.layers.inet import IP, TCP, UDP, ICMP -from scapy.layers.inet import IPerror, TCPerror, UDPerror, ICMPerror -from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest, ICMPv6EchoReply -from scapy.layers.inet6 import ICMPv6DestUnreach, IPerror6 -from scapy.layers.l2 import Ether, ARP, GRE -from scapy.data import IP_PROTOS -from scapy.packet import bind_layers -from util import ppp -from ipfix import IPFIX, Set, Template, Data, IPFIXDecoder -from time import sleep - - -class MethodHolder(VppTestCase): - """ SNAT create capture and verify method holder """ - - @classmethod - def setUpClass(cls): - super(MethodHolder, cls).setUpClass() - - def tearDown(self): - super(MethodHolder, self).tearDown() - - def check_ip_checksum(self, pkt): - """ - Check IP checksum of the packet - - :param pkt: Packet to check IP checksum - """ - new = pkt.__class__(str(pkt)) - del new['IP'].chksum - new = new.__class__(str(new)) - self.assertEqual(new['IP'].chksum, pkt['IP'].chksum) - - def check_tcp_checksum(self, pkt): - """ - Check TCP checksum in IP packet - - :param pkt: Packet to check TCP checksum - """ - new = pkt.__class__(str(pkt)) - del new['TCP'].chksum - new = new.__class__(str(new)) - self.assertEqual(new['TCP'].chksum, pkt['TCP'].chksum) - - def check_udp_checksum(self, pkt): - """ - Check UDP checksum in IP packet - - :param pkt: Packet to check UDP checksum - """ - new = pkt.__class__(str(pkt)) - del new['UDP'].chksum - new = new.__class__(str(new)) - self.assertEqual(new['UDP'].chksum, pkt['UDP'].chksum) - - def check_icmp_errror_embedded(self, pkt): - """ - Check ICMP error embeded packet checksum - - :param pkt: Packet to check ICMP error embeded packet checksum - """ - if pkt.haslayer(IPerror): - new = pkt.__class__(str(pkt)) - del new['IPerror'].chksum - new = new.__class__(str(new)) - self.assertEqual(new['IPerror'].chksum, pkt['IPerror'].chksum) - - if pkt.haslayer(TCPerror): - new = pkt.__class__(str(pkt)) - del new['TCPerror'].chksum - new = new.__class__(str(new)) - self.assertEqual(new['TCPerror'].chksum, pkt['TCPerror'].chksum) - - if pkt.haslayer(UDPerror): - if pkt['UDPerror'].chksum != 0: - new = pkt.__class__(str(pkt)) - del new['UDPerror'].chksum - new = new.__class__(str(new)) - self.assertEqual(new['UDPerror'].chksum, - pkt['UDPerror'].chksum) - - if pkt.haslayer(ICMPerror): - del new['ICMPerror'].chksum - new = new.__class__(str(new)) - self.assertEqual(new['ICMPerror'].chksum, pkt['ICMPerror'].chksum) - - def check_icmp_checksum(self, pkt): - """ - Check ICMP checksum in IPv4 packet - - :param pkt: Packet to check ICMP checksum - """ - new = pkt.__class__(str(pkt)) - del new['ICMP'].chksum - new = new.__class__(str(new)) - self.assertEqual(new['ICMP'].chksum, pkt['ICMP'].chksum) - if pkt.haslayer(IPerror): - self.check_icmp_errror_embedded(pkt) - - def check_icmpv6_checksum(self, pkt): - """ - Check ICMPv6 checksum in IPv4 packet - - :param pkt: Packet to check ICMPv6 checksum - """ - new = pkt.__class__(str(pkt)) - if pkt.haslayer(ICMPv6DestUnreach): - del new['ICMPv6DestUnreach'].cksum - new = new.__class__(str(new)) - self.assertEqual(new['ICMPv6DestUnreach'].cksum, - pkt['ICMPv6DestUnreach'].cksum) - self.check_icmp_errror_embedded(pkt) - if pkt.haslayer(ICMPv6EchoRequest): - del new['ICMPv6EchoRequest'].cksum - new = new.__class__(str(new)) - self.assertEqual(new['ICMPv6EchoRequest'].cksum, - pkt['ICMPv6EchoRequest'].cksum) - if pkt.haslayer(ICMPv6EchoReply): - del new['ICMPv6EchoReply'].cksum - new = new.__class__(str(new)) - self.assertEqual(new['ICMPv6EchoReply'].cksum, - pkt['ICMPv6EchoReply'].cksum) - - def create_stream_in(self, in_if, out_if, ttl=64): - """ - Create packet stream for inside network - - :param in_if: Inside interface - :param out_if: Outside interface - :param ttl: TTL of generated packets - """ - pkts = [] - # TCP - p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / - IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl) / - TCP(sport=self.tcp_port_in, dport=20)) - pkts.append(p) - - # UDP - p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / - IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl) / - UDP(sport=self.udp_port_in, dport=20)) - pkts.append(p) - - # ICMP - p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / - IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl) / - ICMP(id=self.icmp_id_in, type='echo-request')) - pkts.append(p) - - return pkts - - def compose_ip6(self, ip4, pref, plen): - """ - Compose IPv4-embedded IPv6 addresses - - :param ip4: IPv4 address - :param pref: IPv6 prefix - :param plen: IPv6 prefix length - :returns: IPv4-embedded IPv6 addresses - """ - pref_n = list(socket.inet_pton(socket.AF_INET6, pref)) - ip4_n = list(socket.inet_pton(socket.AF_INET, ip4)) - if plen == 32: - pref_n[4] = ip4_n[0] - pref_n[5] = ip4_n[1] - pref_n[6] = ip4_n[2] - pref_n[7] = ip4_n[3] - elif plen == 40: - pref_n[5] = ip4_n[0] - pref_n[6] = ip4_n[1] - pref_n[7] = ip4_n[2] - pref_n[9] = ip4_n[3] - elif plen == 48: - pref_n[6] = ip4_n[0] - pref_n[7] = ip4_n[1] - pref_n[9] = ip4_n[2] - pref_n[10] = ip4_n[3] - elif plen == 56: - pref_n[7] = ip4_n[0] - pref_n[9] = ip4_n[1] - pref_n[10] = ip4_n[2] - pref_n[11] = ip4_n[3] - elif plen == 64: - pref_n[9] = ip4_n[0] - pref_n[10] = ip4_n[1] - pref_n[11] = ip4_n[2] - pref_n[12] = ip4_n[3] - elif plen == 96: - pref_n[12] = ip4_n[0] - pref_n[13] = ip4_n[1] - pref_n[14] = ip4_n[2] - pref_n[15] = ip4_n[3] - return socket.inet_ntop(socket.AF_INET6, ''.join(pref_n)) - - def create_stream_in_ip6(self, in_if, out_if, hlim=64, pref=None, plen=0): - """ - Create IPv6 packet stream for inside network - - :param in_if: Inside interface - :param out_if: Outside interface - :param ttl: Hop Limit of generated packets - :param pref: NAT64 prefix - :param plen: NAT64 prefix length - """ - pkts = [] - if pref is None: - dst = ''.join(['64:ff9b::', out_if.remote_ip4]) - else: - dst = self.compose_ip6(out_if.remote_ip4, pref, plen) - - # TCP - p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / - IPv6(src=in_if.remote_ip6, dst=dst, hlim=hlim) / - TCP(sport=self.tcp_port_in, dport=20)) - pkts.append(p) - - # UDP - p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / - IPv6(src=in_if.remote_ip6, dst=dst, hlim=hlim) / - UDP(sport=self.udp_port_in, dport=20)) - pkts.append(p) - - # ICMP - p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / - IPv6(src=in_if.remote_ip6, dst=dst, hlim=hlim) / - ICMPv6EchoRequest(id=self.icmp_id_in)) - pkts.append(p) - - return pkts - - def create_stream_out(self, out_if, dst_ip=None, ttl=64): - """ - Create packet stream for outside network - - :param out_if: Outside interface - :param dst_ip: Destination IP address (Default use global SNAT address) - :param ttl: TTL of generated packets - """ - if dst_ip is None: - dst_ip = self.snat_addr - pkts = [] - # TCP - p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) / - IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) / - TCP(dport=self.tcp_port_out, sport=20)) - pkts.append(p) - - # UDP - p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) / - IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) / - UDP(dport=self.udp_port_out, sport=20)) - pkts.append(p) - - # ICMP - p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) / - IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) / - ICMP(id=self.icmp_id_out, type='echo-reply')) - pkts.append(p) - - return pkts - - def verify_capture_out(self, capture, nat_ip=None, same_port=False, - packet_num=3, dst_ip=None): - """ - Verify captured packets on outside network - - :param capture: Captured packets - :param nat_ip: Translated IP address (Default use global SNAT address) - :param same_port: Sorce port number is not translated (Default False) - :param packet_num: Expected number of packets (Default 3) - :param dst_ip: Destination IP address (Default do not verify) - """ - if nat_ip is None: - nat_ip = self.snat_addr - self.assertEqual(packet_num, len(capture)) - for packet in capture: - try: - self.check_ip_checksum(packet) - self.assertEqual(packet[IP].src, nat_ip) - if dst_ip is not None: - self.assertEqual(packet[IP].dst, dst_ip) - if packet.haslayer(TCP): - if same_port: - self.assertEqual(packet[TCP].sport, self.tcp_port_in) - else: - self.assertNotEqual( - packet[TCP].sport, self.tcp_port_in) - self.tcp_port_out = packet[TCP].sport - self.check_tcp_checksum(packet) - elif packet.haslayer(UDP): - if same_port: - self.assertEqual(packet[UDP].sport, self.udp_port_in) - else: - self.assertNotEqual( - packet[UDP].sport, self.udp_port_in) - self.udp_port_out = packet[UDP].sport - else: - if same_port: - self.assertEqual(packet[ICMP].id, self.icmp_id_in) - else: - self.assertNotEqual(packet[ICMP].id, self.icmp_id_in) - self.icmp_id_out = packet[ICMP].id - self.check_icmp_checksum(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet " - "(outside network):", packet)) - raise - - def verify_capture_in(self, capture, in_if, packet_num=3): - """ - Verify captured packets on inside network - - :param capture: Captured packets - :param in_if: Inside interface - :param packet_num: Expected number of packets (Default 3) - """ - self.assertEqual(packet_num, len(capture)) - for packet in capture: - try: - self.check_ip_checksum(packet) - self.assertEqual(packet[IP].dst, in_if.remote_ip4) - if packet.haslayer(TCP): - self.assertEqual(packet[TCP].dport, self.tcp_port_in) - self.check_tcp_checksum(packet) - elif packet.haslayer(UDP): - self.assertEqual(packet[UDP].dport, self.udp_port_in) - else: - self.assertEqual(packet[ICMP].id, self.icmp_id_in) - self.check_icmp_checksum(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet " - "(inside network):", packet)) - raise - - def verify_capture_in_ip6(self, capture, src_ip, dst_ip, packet_num=3): - """ - Verify captured IPv6 packets on inside network - - :param capture: Captured packets - :param src_ip: Source IP - :param dst_ip: Destination IP address - :param packet_num: Expected number of packets (Default 3) - """ - self.assertEqual(packet_num, len(capture)) - for packet in capture: - try: - self.assertEqual(packet[IPv6].src, src_ip) - self.assertEqual(packet[IPv6].dst, dst_ip) - if packet.haslayer(TCP): - self.assertEqual(packet[TCP].dport, self.tcp_port_in) - self.check_tcp_checksum(packet) - elif packet.haslayer(UDP): - self.assertEqual(packet[UDP].dport, self.udp_port_in) - self.check_udp_checksum(packet) - else: - self.assertEqual(packet[ICMPv6EchoReply].id, - self.icmp_id_in) - self.check_icmpv6_checksum(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet " - "(inside network):", packet)) - raise - - def verify_capture_no_translation(self, capture, ingress_if, egress_if): - """ - Verify captured packet that don't have to be translated - - :param capture: Captured packets - :param ingress_if: Ingress interface - :param egress_if: Egress interface - """ - for packet in capture: - try: - self.assertEqual(packet[IP].src, ingress_if.remote_ip4) - self.assertEqual(packet[IP].dst, egress_if.remote_ip4) - if packet.haslayer(TCP): - self.assertEqual(packet[TCP].sport, self.tcp_port_in) - elif packet.haslayer(UDP): - self.assertEqual(packet[UDP].sport, self.udp_port_in) - else: - self.assertEqual(packet[ICMP].id, self.icmp_id_in) - except: - self.logger.error(ppp("Unexpected or invalid packet " - "(inside network):", packet)) - raise - - def verify_capture_out_with_icmp_errors(self, capture, src_ip=None, - packet_num=3, icmp_type=11): - """ - Verify captured packets with ICMP errors on outside network - - :param capture: Captured packets - :param src_ip: Translated IP address or IP address of VPP - (Default use global SNAT address) - :param packet_num: Expected number of packets (Default 3) - :param icmp_type: Type of error ICMP packet - we are expecting (Default 11) - """ - if src_ip is None: - src_ip = self.snat_addr - self.assertEqual(packet_num, len(capture)) - for packet in capture: - try: - self.assertEqual(packet[IP].src, src_ip) - self.assertTrue(packet.haslayer(ICMP)) - icmp = packet[ICMP] - self.assertEqual(icmp.type, icmp_type) - self.assertTrue(icmp.haslayer(IPerror)) - inner_ip = icmp[IPerror] - if inner_ip.haslayer(TCPerror): - self.assertEqual(inner_ip[TCPerror].dport, - self.tcp_port_out) - elif inner_ip.haslayer(UDPerror): - self.assertEqual(inner_ip[UDPerror].dport, - self.udp_port_out) - else: - self.assertEqual(inner_ip[ICMPerror].id, self.icmp_id_out) - except: - self.logger.error(ppp("Unexpected or invalid packet " - "(outside network):", packet)) - raise - - def verify_capture_in_with_icmp_errors(self, capture, in_if, packet_num=3, - icmp_type=11): - """ - Verify captured packets with ICMP errors on inside network - - :param capture: Captured packets - :param in_if: Inside interface - :param packet_num: Expected number of packets (Default 3) - :param icmp_type: Type of error ICMP packet - we are expecting (Default 11) - """ - self.assertEqual(packet_num, len(capture)) - for packet in capture: - try: - self.assertEqual(packet[IP].dst, in_if.remote_ip4) - self.assertTrue(packet.haslayer(ICMP)) - icmp = packet[ICMP] - self.assertEqual(icmp.type, icmp_type) - self.assertTrue(icmp.haslayer(IPerror)) - inner_ip = icmp[IPerror] - if inner_ip.haslayer(TCPerror): - self.assertEqual(inner_ip[TCPerror].sport, - self.tcp_port_in) - elif inner_ip.haslayer(UDPerror): - self.assertEqual(inner_ip[UDPerror].sport, - self.udp_port_in) - else: - self.assertEqual(inner_ip[ICMPerror].id, self.icmp_id_in) - except: - self.logger.error(ppp("Unexpected or invalid packet " - "(inside network):", packet)) - raise - - def verify_ipfix_nat44_ses(self, data): - """ - Verify IPFIX NAT44 session create/delete event - - :param data: Decoded IPFIX data records - """ - nat44_ses_create_num = 0 - nat44_ses_delete_num = 0 - self.assertEqual(6, len(data)) - for record in data: - # natEvent - self.assertIn(ord(record[230]), [4, 5]) - if ord(record[230]) == 4: - nat44_ses_create_num += 1 - else: - nat44_ses_delete_num += 1 - # sourceIPv4Address - self.assertEqual(self.pg0.remote_ip4n, record[8]) - # postNATSourceIPv4Address - self.assertEqual(socket.inet_pton(socket.AF_INET, self.snat_addr), - record[225]) - # ingressVRFID - self.assertEqual(struct.pack("!I", 0), record[234]) - # protocolIdentifier/sourceTransportPort/postNAPTSourceTransportPort - if IP_PROTOS.icmp == ord(record[4]): - self.assertEqual(struct.pack("!H", self.icmp_id_in), record[7]) - self.assertEqual(struct.pack("!H", self.icmp_id_out), - record[227]) - elif IP_PROTOS.tcp == ord(record[4]): - self.assertEqual(struct.pack("!H", self.tcp_port_in), - record[7]) - self.assertEqual(struct.pack("!H", self.tcp_port_out), - record[227]) - elif IP_PROTOS.udp == ord(record[4]): - self.assertEqual(struct.pack("!H", self.udp_port_in), - record[7]) - self.assertEqual(struct.pack("!H", self.udp_port_out), - record[227]) - else: - self.fail("Invalid protocol") - self.assertEqual(3, nat44_ses_create_num) - self.assertEqual(3, nat44_ses_delete_num) - - def verify_ipfix_addr_exhausted(self, data): - """ - Verify IPFIX NAT addresses event - - :param data: Decoded IPFIX data records - """ - self.assertEqual(1, len(data)) - record = data[0] - # natEvent - self.assertEqual(ord(record[230]), 3) - # natPoolID - self.assertEqual(struct.pack("!I", 0), record[283]) - - -class TestSNAT(MethodHolder): - """ SNAT Test Cases """ - - @classmethod - def setUpClass(cls): - super(TestSNAT, cls).setUpClass() - - try: - cls.tcp_port_in = 6303 - cls.tcp_port_out = 6303 - cls.udp_port_in = 6304 - cls.udp_port_out = 6304 - cls.icmp_id_in = 6305 - cls.icmp_id_out = 6305 - cls.snat_addr = '10.0.0.3' - cls.ipfix_src_port = 4739 - cls.ipfix_domain_id = 1 - - cls.create_pg_interfaces(range(9)) - cls.interfaces = list(cls.pg_interfaces[0:4]) - - for i in cls.interfaces: - i.admin_up() - i.config_ip4() - i.resolve_arp() - - cls.pg0.generate_remote_hosts(3) - cls.pg0.configure_ipv4_neighbors() - - cls.overlapping_interfaces = list(list(cls.pg_interfaces[4:7])) - - cls.pg4._local_ip4 = "172.16.255.1" - cls.pg4._local_ip4n = socket.inet_pton(socket.AF_INET, i.local_ip4) - cls.pg4._remote_hosts[0]._ip4 = "172.16.255.2" - cls.pg4.set_table_ip4(10) - cls.pg5._local_ip4 = "172.17.255.3" - cls.pg5._local_ip4n = socket.inet_pton(socket.AF_INET, i.local_ip4) - cls.pg5._remote_hosts[0]._ip4 = "172.17.255.4" - cls.pg5.set_table_ip4(10) - cls.pg6._local_ip4 = "172.16.255.1" - cls.pg6._local_ip4n = socket.inet_pton(socket.AF_INET, i.local_ip4) - cls.pg6._remote_hosts[0]._ip4 = "172.16.255.2" - cls.pg6.set_table_ip4(20) - for i in cls.overlapping_interfaces: - i.config_ip4() - i.admin_up() - i.resolve_arp() - - cls.pg7.admin_up() - cls.pg8.admin_up() - - except Exception: - super(TestSNAT, cls).tearDownClass() - raise - - def clear_snat(self): - """ - Clear SNAT configuration. - """ - # I found no elegant way to do this - self.vapi.ip_add_del_route(dst_address=self.pg7.remote_ip4n, - dst_address_length=32, - next_hop_address=self.pg7.remote_ip4n, - next_hop_sw_if_index=self.pg7.sw_if_index, - is_add=0) - self.vapi.ip_add_del_route(dst_address=self.pg8.remote_ip4n, - dst_address_length=32, - next_hop_address=self.pg8.remote_ip4n, - next_hop_sw_if_index=self.pg8.sw_if_index, - is_add=0) - - for intf in [self.pg7, self.pg8]: - neighbors = self.vapi.ip_neighbor_dump(intf.sw_if_index) - for n in neighbors: - self.vapi.ip_neighbor_add_del(intf.sw_if_index, - n.mac_address, - n.ip_address, - is_add=0) - - if self.pg7.has_ip4_config: - self.pg7.unconfig_ip4() - - interfaces = self.vapi.snat_interface_addr_dump() - for intf in interfaces: - self.vapi.snat_add_interface_addr(intf.sw_if_index, is_add=0) - - self.vapi.snat_ipfix(enable=0, src_port=self.ipfix_src_port, - domain_id=self.ipfix_domain_id) - self.ipfix_src_port = 4739 - self.ipfix_domain_id = 1 - - interfaces = self.vapi.snat_interface_dump() - for intf in interfaces: - self.vapi.snat_interface_add_del_feature(intf.sw_if_index, - intf.is_inside, - is_add=0) - - interfaces = self.vapi.snat_interface_output_feature_dump() - for intf in interfaces: - self.vapi.snat_interface_add_del_output_feature(intf.sw_if_index, - intf.is_inside, - is_add=0) - - static_mappings = self.vapi.snat_static_mapping_dump() - for sm in static_mappings: - self.vapi.snat_add_static_mapping(sm.local_ip_address, - sm.external_ip_address, - local_port=sm.local_port, - external_port=sm.external_port, - addr_only=sm.addr_only, - vrf_id=sm.vrf_id, - protocol=sm.protocol, - is_add=0) - - adresses = self.vapi.snat_address_dump() - for addr in adresses: - self.vapi.snat_add_address_range(addr.ip_address, - addr.ip_address, - is_add=0) - - def snat_add_static_mapping(self, local_ip, external_ip='0.0.0.0', - local_port=0, external_port=0, vrf_id=0, - is_add=1, external_sw_if_index=0xFFFFFFFF, - proto=0): - """ - Add/delete S-NAT static mapping - - :param local_ip: Local IP address - :param external_ip: External IP address - :param local_port: Local port number (Optional) - :param external_port: External port number (Optional) - :param vrf_id: VRF ID (Default 0) - :param is_add: 1 if add, 0 if delete (Default add) - :param external_sw_if_index: External interface instead of IP address - :param proto: IP protocol (Mandatory if port specified) - """ - addr_only = 1 - if local_port and external_port: - addr_only = 0 - l_ip = socket.inet_pton(socket.AF_INET, local_ip) - e_ip = socket.inet_pton(socket.AF_INET, external_ip) - self.vapi.snat_add_static_mapping( - l_ip, - e_ip, - external_sw_if_index, - local_port, - external_port, - addr_only, - vrf_id, - proto, - is_add) - - def snat_add_address(self, ip, is_add=1, vrf_id=0xFFFFFFFF): - """ - Add/delete S-NAT address - - :param ip: IP address - :param is_add: 1 if add, 0 if delete (Default add) - """ - snat_addr = socket.inet_pton(socket.AF_INET, ip) - self.vapi.snat_add_address_range(snat_addr, snat_addr, is_add, - vrf_id=vrf_id) - - def test_dynamic(self): - """ SNAT dynamic translation test """ - - self.snat_add_address(self.snat_addr) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - - # in2out - pkts = self.create_stream_in(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out(capture) - - # out2in - pkts = self.create_stream_out(self.pg1) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg0) - - def test_dynamic_icmp_errors_in2out_ttl_1(self): - """ SNAT handling of client packets with TTL=1 """ - - self.snat_add_address(self.snat_addr) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - - # Client side - generate traffic - pkts = self.create_stream_in(self.pg0, self.pg1, ttl=1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - - # Client side - verify ICMP type 11 packets - capture = self.pg0.get_capture(len(pkts)) - self.verify_capture_in_with_icmp_errors(capture, self.pg0) - - def test_dynamic_icmp_errors_out2in_ttl_1(self): - """ SNAT handling of server packets with TTL=1 """ - - self.snat_add_address(self.snat_addr) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - - # Client side - create sessions - pkts = self.create_stream_in(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - - # Server side - generate traffic - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out(capture) - pkts = self.create_stream_out(self.pg1, ttl=1) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - - # Server side - verify ICMP type 11 packets - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out_with_icmp_errors(capture, - src_ip=self.pg1.local_ip4) - - def test_dynamic_icmp_errors_in2out_ttl_2(self): - """ SNAT handling of error responses to client packets with TTL=2 """ - - self.snat_add_address(self.snat_addr) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - - # Client side - generate traffic - pkts = self.create_stream_in(self.pg0, self.pg1, ttl=2) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - - # Server side - simulate ICMP type 11 response - capture = self.pg1.get_capture(len(pkts)) - pkts = [Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / - IP(src=self.pg1.remote_ip4, dst=self.snat_addr) / - ICMP(type=11) / packet[IP] for packet in capture] - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - - # Client side - verify ICMP type 11 packets - capture = self.pg0.get_capture(len(pkts)) - self.verify_capture_in_with_icmp_errors(capture, self.pg0) - - def test_dynamic_icmp_errors_out2in_ttl_2(self): - """ SNAT handling of error responses to server packets with TTL=2 """ - - self.snat_add_address(self.snat_addr) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - - # Client side - create sessions - pkts = self.create_stream_in(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - - # Server side - generate traffic - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out(capture) - pkts = self.create_stream_out(self.pg1, ttl=2) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - - # Client side - simulate ICMP type 11 response - capture = self.pg0.get_capture(len(pkts)) - pkts = [Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - ICMP(type=11) / packet[IP] for packet in capture] - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - - # Server side - verify ICMP type 11 packets - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out_with_icmp_errors(capture) - - def test_ping_out_interface_from_outside(self): - """ Ping SNAT out interface from outside network """ - - self.snat_add_address(self.snat_addr) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - - p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / - IP(src=self.pg1.remote_ip4, dst=self.pg1.local_ip4) / - ICMP(id=self.icmp_id_out, type='echo-request')) - pkts = [p] - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - self.assertEqual(1, len(capture)) - packet = capture[0] - try: - self.assertEqual(packet[IP].src, self.pg1.local_ip4) - self.assertEqual(packet[IP].dst, self.pg1.remote_ip4) - self.assertEqual(packet[ICMP].id, self.icmp_id_in) - self.assertEqual(packet[ICMP].type, 0) # echo reply - except: - self.logger.error(ppp("Unexpected or invalid packet " - "(outside network):", packet)) - raise - - def test_ping_internal_host_from_outside(self): - """ Ping internal host from outside network """ - - self.snat_add_static_mapping(self.pg0.remote_ip4, self.snat_addr) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - - # out2in - pkt = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / - IP(src=self.pg1.remote_ip4, dst=self.snat_addr, ttl=64) / - ICMP(id=self.icmp_id_out, type='echo-request')) - self.pg1.add_stream(pkt) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(1) - self.verify_capture_in(capture, self.pg0, packet_num=1) - self.assert_equal(capture[0][IP].proto, IP_PROTOS.icmp) - - # in2out - pkt = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4, ttl=64) / - ICMP(id=self.icmp_id_in, type='echo-reply')) - self.pg0.add_stream(pkt) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(1) - self.verify_capture_out(capture, same_port=True, packet_num=1) - self.assert_equal(capture[0][IP].proto, IP_PROTOS.icmp) - - def test_static_in(self): - """ SNAT 1:1 NAT initialized from inside network """ - - nat_ip = "10.0.0.10" - self.tcp_port_out = 6303 - self.udp_port_out = 6304 - self.icmp_id_out = 6305 - - self.snat_add_static_mapping(self.pg0.remote_ip4, nat_ip) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - - # in2out - pkts = self.create_stream_in(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out(capture, nat_ip, True) - - # out2in - pkts = self.create_stream_out(self.pg1, nat_ip) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg0) - - def test_static_out(self): - """ SNAT 1:1 NAT initialized from outside network """ - - nat_ip = "10.0.0.20" - self.tcp_port_out = 6303 - self.udp_port_out = 6304 - self.icmp_id_out = 6305 - - self.snat_add_static_mapping(self.pg0.remote_ip4, nat_ip) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - - # out2in - pkts = self.create_stream_out(self.pg1, nat_ip) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg0) - - # in2out - pkts = self.create_stream_in(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out(capture, nat_ip, True) - - def test_static_with_port_in(self): - """ SNAT 1:1 NAT with port initialized from inside network """ - - self.tcp_port_out = 3606 - self.udp_port_out = 3607 - self.icmp_id_out = 3608 - - self.snat_add_address(self.snat_addr) - self.snat_add_static_mapping(self.pg0.remote_ip4, self.snat_addr, - self.tcp_port_in, self.tcp_port_out, - proto=IP_PROTOS.tcp) - self.snat_add_static_mapping(self.pg0.remote_ip4, self.snat_addr, - self.udp_port_in, self.udp_port_out, - proto=IP_PROTOS.udp) - self.snat_add_static_mapping(self.pg0.remote_ip4, self.snat_addr, - self.icmp_id_in, self.icmp_id_out, - proto=IP_PROTOS.icmp) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - - # in2out - pkts = self.create_stream_in(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out(capture) - - # out2in - pkts = self.create_stream_out(self.pg1) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg0) - - def test_static_with_port_out(self): - """ SNAT 1:1 NAT with port initialized from outside network """ - - self.tcp_port_out = 30606 - self.udp_port_out = 30607 - self.icmp_id_out = 30608 - - self.snat_add_address(self.snat_addr) - self.snat_add_static_mapping(self.pg0.remote_ip4, self.snat_addr, - self.tcp_port_in, self.tcp_port_out, - proto=IP_PROTOS.tcp) - self.snat_add_static_mapping(self.pg0.remote_ip4, self.snat_addr, - self.udp_port_in, self.udp_port_out, - proto=IP_PROTOS.udp) - self.snat_add_static_mapping(self.pg0.remote_ip4, self.snat_addr, - self.icmp_id_in, self.icmp_id_out, - proto=IP_PROTOS.icmp) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - - # out2in - pkts = self.create_stream_out(self.pg1) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg0) - - # in2out - pkts = self.create_stream_in(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out(capture) - - def test_static_vrf_aware(self): - """ SNAT 1:1 NAT VRF awareness """ - - nat_ip1 = "10.0.0.30" - nat_ip2 = "10.0.0.40" - self.tcp_port_out = 6303 - self.udp_port_out = 6304 - self.icmp_id_out = 6305 - - self.snat_add_static_mapping(self.pg4.remote_ip4, nat_ip1, - vrf_id=10) - self.snat_add_static_mapping(self.pg0.remote_ip4, nat_ip2, - vrf_id=10) - self.vapi.snat_interface_add_del_feature(self.pg3.sw_if_index, - is_inside=0) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg4.sw_if_index) - - # inside interface VRF match SNAT static mapping VRF - pkts = self.create_stream_in(self.pg4, self.pg3) - self.pg4.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg3.get_capture(len(pkts)) - self.verify_capture_out(capture, nat_ip1, True) - - # inside interface VRF don't match SNAT static mapping VRF (packets - # are dropped) - pkts = self.create_stream_in(self.pg0, self.pg3) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg3.assert_nothing_captured() - - def test_multiple_inside_interfaces(self): - """ SNAT multiple inside interfaces (non-overlapping address space) """ - - self.snat_add_address(self.snat_addr) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg3.sw_if_index, - is_inside=0) - - # between two S-NAT inside interfaces (no translation) - pkts = self.create_stream_in(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_no_translation(capture, self.pg0, self.pg1) - - # from S-NAT inside to interface without S-NAT feature (no translation) - pkts = self.create_stream_in(self.pg0, self.pg2) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg2.get_capture(len(pkts)) - self.verify_capture_no_translation(capture, self.pg0, self.pg2) - - # in2out 1st interface - pkts = self.create_stream_in(self.pg0, self.pg3) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg3.get_capture(len(pkts)) - self.verify_capture_out(capture) - - # out2in 1st interface - pkts = self.create_stream_out(self.pg3) - self.pg3.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg0) - - # in2out 2nd interface - pkts = self.create_stream_in(self.pg1, self.pg3) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg3.get_capture(len(pkts)) - self.verify_capture_out(capture) - - # out2in 2nd interface - pkts = self.create_stream_out(self.pg3) - self.pg3.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg1) - - def test_inside_overlapping_interfaces(self): - """ SNAT multiple inside interfaces with overlapping address space """ - - static_nat_ip = "10.0.0.10" - self.snat_add_address(self.snat_addr) - self.vapi.snat_interface_add_del_feature(self.pg3.sw_if_index, - is_inside=0) - self.vapi.snat_interface_add_del_feature(self.pg4.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg5.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg6.sw_if_index) - self.snat_add_static_mapping(self.pg6.remote_ip4, static_nat_ip, - vrf_id=20) - - # between S-NAT inside interfaces with same VRF (no translation) - pkts = self.create_stream_in(self.pg4, self.pg5) - self.pg4.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg5.get_capture(len(pkts)) - self.verify_capture_no_translation(capture, self.pg4, self.pg5) - - # between S-NAT inside interfaces with different VRF (hairpinning) - p = (Ether(src=self.pg4.remote_mac, dst=self.pg4.local_mac) / - IP(src=self.pg4.remote_ip4, dst=static_nat_ip) / - TCP(sport=1234, dport=5678)) - self.pg4.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg6.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, self.snat_addr) - self.assertEqual(ip.dst, self.pg6.remote_ip4) - self.assertNotEqual(tcp.sport, 1234) - self.assertEqual(tcp.dport, 5678) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # in2out 1st interface - pkts = self.create_stream_in(self.pg4, self.pg3) - self.pg4.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg3.get_capture(len(pkts)) - self.verify_capture_out(capture) - - # out2in 1st interface - pkts = self.create_stream_out(self.pg3) - self.pg3.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg4.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg4) - - # in2out 2nd interface - pkts = self.create_stream_in(self.pg5, self.pg3) - self.pg5.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg3.get_capture(len(pkts)) - self.verify_capture_out(capture) - - # out2in 2nd interface - pkts = self.create_stream_out(self.pg3) - self.pg3.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg5.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg5) - - # pg5 session dump - addresses = self.vapi.snat_address_dump() - self.assertEqual(len(addresses), 1) - sessions = self.vapi.snat_user_session_dump(self.pg5.remote_ip4n, 10) - self.assertEqual(len(sessions), 3) - for session in sessions: - self.assertFalse(session.is_static) - self.assertEqual(session.inside_ip_address[0:4], - self.pg5.remote_ip4n) - self.assertEqual(session.outside_ip_address, - addresses[0].ip_address) - self.assertEqual(sessions[0].protocol, IP_PROTOS.tcp) - self.assertEqual(sessions[1].protocol, IP_PROTOS.udp) - self.assertEqual(sessions[2].protocol, IP_PROTOS.icmp) - self.assertEqual(sessions[0].inside_port, self.tcp_port_in) - self.assertEqual(sessions[1].inside_port, self.udp_port_in) - self.assertEqual(sessions[2].inside_port, self.icmp_id_in) - self.assertEqual(sessions[0].outside_port, self.tcp_port_out) - self.assertEqual(sessions[1].outside_port, self.udp_port_out) - self.assertEqual(sessions[2].outside_port, self.icmp_id_out) - - # in2out 3rd interface - pkts = self.create_stream_in(self.pg6, self.pg3) - self.pg6.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg3.get_capture(len(pkts)) - self.verify_capture_out(capture, static_nat_ip, True) - - # out2in 3rd interface - pkts = self.create_stream_out(self.pg3, static_nat_ip) - self.pg3.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg6.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg6) - - # general user and session dump verifications - users = self.vapi.snat_user_dump() - self.assertTrue(len(users) >= 3) - addresses = self.vapi.snat_address_dump() - self.assertEqual(len(addresses), 1) - for user in users: - sessions = self.vapi.snat_user_session_dump(user.ip_address, - user.vrf_id) - for session in sessions: - self.assertEqual(user.ip_address, session.inside_ip_address) - self.assertTrue(session.total_bytes > session.total_pkts > 0) - self.assertTrue(session.protocol in - [IP_PROTOS.tcp, IP_PROTOS.udp, - IP_PROTOS.icmp]) - - # pg4 session dump - sessions = self.vapi.snat_user_session_dump(self.pg4.remote_ip4n, 10) - self.assertTrue(len(sessions) >= 4) - for session in sessions: - self.assertFalse(session.is_static) - self.assertEqual(session.inside_ip_address[0:4], - self.pg4.remote_ip4n) - self.assertEqual(session.outside_ip_address, - addresses[0].ip_address) - - # pg6 session dump - sessions = self.vapi.snat_user_session_dump(self.pg6.remote_ip4n, 20) - self.assertTrue(len(sessions) >= 3) - for session in sessions: - self.assertTrue(session.is_static) - self.assertEqual(session.inside_ip_address[0:4], - self.pg6.remote_ip4n) - self.assertEqual(map(ord, session.outside_ip_address[0:4]), - map(int, static_nat_ip.split('.'))) - self.assertTrue(session.inside_port in - [self.tcp_port_in, self.udp_port_in, - self.icmp_id_in]) - - def test_hairpinning(self): - """ SNAT hairpinning - 1:1 NAT with port""" - - host = self.pg0.remote_hosts[0] - server = self.pg0.remote_hosts[1] - host_in_port = 1234 - host_out_port = 0 - server_in_port = 5678 - server_out_port = 8765 - - self.snat_add_address(self.snat_addr) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - # add static mapping for server - self.snat_add_static_mapping(server.ip4, self.snat_addr, - server_in_port, server_out_port, - proto=IP_PROTOS.tcp) - - # send packet from host to server - p = (Ether(src=host.mac, dst=self.pg0.local_mac) / - IP(src=host.ip4, dst=self.snat_addr) / - TCP(sport=host_in_port, dport=server_out_port)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, self.snat_addr) - self.assertEqual(ip.dst, server.ip4) - self.assertNotEqual(tcp.sport, host_in_port) - self.assertEqual(tcp.dport, server_in_port) - self.check_tcp_checksum(p) - host_out_port = tcp.sport - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # send reply from server to host - p = (Ether(src=server.mac, dst=self.pg0.local_mac) / - IP(src=server.ip4, dst=self.snat_addr) / - TCP(sport=server_in_port, dport=host_out_port)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, self.snat_addr) - self.assertEqual(ip.dst, host.ip4) - self.assertEqual(tcp.sport, server_out_port) - self.assertEqual(tcp.dport, host_in_port) - self.check_tcp_checksum(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:"), p) - raise - - def test_hairpinning2(self): - """ SNAT hairpinning - 1:1 NAT""" - - server1_nat_ip = "10.0.0.10" - server2_nat_ip = "10.0.0.11" - host = self.pg0.remote_hosts[0] - server1 = self.pg0.remote_hosts[1] - server2 = self.pg0.remote_hosts[2] - server_tcp_port = 22 - server_udp_port = 20 - - self.snat_add_address(self.snat_addr) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - - # add static mapping for servers - self.snat_add_static_mapping(server1.ip4, server1_nat_ip) - self.snat_add_static_mapping(server2.ip4, server2_nat_ip) - - # host to server1 - pkts = [] - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=host.ip4, dst=server1_nat_ip) / - TCP(sport=self.tcp_port_in, dport=server_tcp_port)) - pkts.append(p) - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=host.ip4, dst=server1_nat_ip) / - UDP(sport=self.udp_port_in, dport=server_udp_port)) - pkts.append(p) - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=host.ip4, dst=server1_nat_ip) / - ICMP(id=self.icmp_id_in, type='echo-request')) - pkts.append(p) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - for packet in capture: - try: - self.assertEqual(packet[IP].src, self.snat_addr) - self.assertEqual(packet[IP].dst, server1.ip4) - if packet.haslayer(TCP): - self.assertNotEqual(packet[TCP].sport, self.tcp_port_in) - self.assertEqual(packet[TCP].dport, server_tcp_port) - self.tcp_port_out = packet[TCP].sport - self.check_tcp_checksum(packet) - elif packet.haslayer(UDP): - self.assertNotEqual(packet[UDP].sport, self.udp_port_in) - self.assertEqual(packet[UDP].dport, server_udp_port) - self.udp_port_out = packet[UDP].sport - else: - self.assertNotEqual(packet[ICMP].id, self.icmp_id_in) - self.icmp_id_out = packet[ICMP].id - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - # server1 to host - pkts = [] - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=server1.ip4, dst=self.snat_addr) / - TCP(sport=server_tcp_port, dport=self.tcp_port_out)) - pkts.append(p) - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=server1.ip4, dst=self.snat_addr) / - UDP(sport=server_udp_port, dport=self.udp_port_out)) - pkts.append(p) - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=server1.ip4, dst=self.snat_addr) / - ICMP(id=self.icmp_id_out, type='echo-reply')) - pkts.append(p) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - for packet in capture: - try: - self.assertEqual(packet[IP].src, server1_nat_ip) - self.assertEqual(packet[IP].dst, host.ip4) - if packet.haslayer(TCP): - self.assertEqual(packet[TCP].dport, self.tcp_port_in) - self.assertEqual(packet[TCP].sport, server_tcp_port) - self.check_tcp_checksum(packet) - elif packet.haslayer(UDP): - self.assertEqual(packet[UDP].dport, self.udp_port_in) - self.assertEqual(packet[UDP].sport, server_udp_port) - else: - self.assertEqual(packet[ICMP].id, self.icmp_id_in) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - # server2 to server1 - pkts = [] - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=server2.ip4, dst=server1_nat_ip) / - TCP(sport=self.tcp_port_in, dport=server_tcp_port)) - pkts.append(p) - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=server2.ip4, dst=server1_nat_ip) / - UDP(sport=self.udp_port_in, dport=server_udp_port)) - pkts.append(p) - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=server2.ip4, dst=server1_nat_ip) / - ICMP(id=self.icmp_id_in, type='echo-request')) - pkts.append(p) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - for packet in capture: - try: - self.assertEqual(packet[IP].src, server2_nat_ip) - self.assertEqual(packet[IP].dst, server1.ip4) - if packet.haslayer(TCP): - self.assertEqual(packet[TCP].sport, self.tcp_port_in) - self.assertEqual(packet[TCP].dport, server_tcp_port) - self.tcp_port_out = packet[TCP].sport - self.check_tcp_checksum(packet) - elif packet.haslayer(UDP): - self.assertEqual(packet[UDP].sport, self.udp_port_in) - self.assertEqual(packet[UDP].dport, server_udp_port) - self.udp_port_out = packet[UDP].sport - else: - self.assertEqual(packet[ICMP].id, self.icmp_id_in) - self.icmp_id_out = packet[ICMP].id - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - # server1 to server2 - pkts = [] - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=server1.ip4, dst=server2_nat_ip) / - TCP(sport=server_tcp_port, dport=self.tcp_port_out)) - pkts.append(p) - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=server1.ip4, dst=server2_nat_ip) / - UDP(sport=server_udp_port, dport=self.udp_port_out)) - pkts.append(p) - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=server1.ip4, dst=server2_nat_ip) / - ICMP(id=self.icmp_id_out, type='echo-reply')) - pkts.append(p) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - for packet in capture: - try: - self.assertEqual(packet[IP].src, server1_nat_ip) - self.assertEqual(packet[IP].dst, server2.ip4) - if packet.haslayer(TCP): - self.assertEqual(packet[TCP].dport, self.tcp_port_in) - self.assertEqual(packet[TCP].sport, server_tcp_port) - self.check_tcp_checksum(packet) - elif packet.haslayer(UDP): - self.assertEqual(packet[UDP].dport, self.udp_port_in) - self.assertEqual(packet[UDP].sport, server_udp_port) - else: - self.assertEqual(packet[ICMP].id, self.icmp_id_in) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - def test_max_translations_per_user(self): - """ MAX translations per user - recycle the least recently used """ - - self.snat_add_address(self.snat_addr) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - - # get maximum number of translations per user - snat_config = self.vapi.snat_show_config() - - # send more than maximum number of translations per user packets - pkts_num = snat_config.max_translations_per_user + 5 - pkts = [] - for port in range(0, pkts_num): - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=1025 + port)) - pkts.append(p) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - - # verify number of translated packet - self.pg1.get_capture(pkts_num) - - def test_interface_addr(self): - """ Acquire SNAT addresses from interface """ - self.vapi.snat_add_interface_addr(self.pg7.sw_if_index) - - # no address in NAT pool - adresses = self.vapi.snat_address_dump() - self.assertEqual(0, len(adresses)) - - # configure interface address and check NAT address pool - self.pg7.config_ip4() - adresses = self.vapi.snat_address_dump() - self.assertEqual(1, len(adresses)) - self.assertEqual(adresses[0].ip_address[0:4], self.pg7.local_ip4n) - - # remove interface address and check NAT address pool - self.pg7.unconfig_ip4() - adresses = self.vapi.snat_address_dump() - self.assertEqual(0, len(adresses)) - - def test_interface_addr_static_mapping(self): - """ Static mapping with addresses from interface """ - self.vapi.snat_add_interface_addr(self.pg7.sw_if_index) - self.snat_add_static_mapping('1.2.3.4', - external_sw_if_index=self.pg7.sw_if_index) - - # static mappings with external interface - static_mappings = self.vapi.snat_static_mapping_dump() - self.assertEqual(1, len(static_mappings)) - self.assertEqual(self.pg7.sw_if_index, - static_mappings[0].external_sw_if_index) - - # configure interface address and check static mappings - self.pg7.config_ip4() - static_mappings = self.vapi.snat_static_mapping_dump() - self.assertEqual(1, len(static_mappings)) - self.assertEqual(static_mappings[0].external_ip_address[0:4], - self.pg7.local_ip4n) - self.assertEqual(0xFFFFFFFF, static_mappings[0].external_sw_if_index) - - # remove interface address and check static mappings - self.pg7.unconfig_ip4() - static_mappings = self.vapi.snat_static_mapping_dump() - self.assertEqual(0, len(static_mappings)) - - def test_ipfix_nat44_sess(self): - """ S-NAT IPFIX logging NAT44 session created/delted """ - self.ipfix_domain_id = 10 - self.ipfix_src_port = 20202 - colector_port = 30303 - bind_layers(UDP, IPFIX, dport=30303) - self.snat_add_address(self.snat_addr) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - self.vapi.set_ipfix_exporter(collector_address=self.pg3.remote_ip4n, - src_address=self.pg3.local_ip4n, - path_mtu=512, - template_interval=10, - collector_port=colector_port) - self.vapi.snat_ipfix(domain_id=self.ipfix_domain_id, - src_port=self.ipfix_src_port) - - pkts = self.create_stream_in(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out(capture) - self.snat_add_address(self.snat_addr, is_add=0) - self.vapi.cli("ipfix flush") # FIXME this should be an API call - capture = self.pg3.get_capture(3) - ipfix = IPFIXDecoder() - # first load template - for p in capture: - self.assertTrue(p.haslayer(IPFIX)) - self.assertEqual(p[IP].src, self.pg3.local_ip4) - self.assertEqual(p[IP].dst, self.pg3.remote_ip4) - self.assertEqual(p[UDP].sport, self.ipfix_src_port) - self.assertEqual(p[UDP].dport, colector_port) - self.assertEqual(p[IPFIX].observationDomainID, - self.ipfix_domain_id) - if p.haslayer(Template): - ipfix.add_template(p.getlayer(Template)) - # verify events in data set - for p in capture: - if p.haslayer(Data): - data = ipfix.decode_data_set(p.getlayer(Set)) - self.verify_ipfix_nat44_ses(data) - - def test_ipfix_addr_exhausted(self): - """ S-NAT IPFIX logging NAT addresses exhausted """ - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - self.vapi.set_ipfix_exporter(collector_address=self.pg3.remote_ip4n, - src_address=self.pg3.local_ip4n, - path_mtu=512, - template_interval=10) - self.vapi.snat_ipfix(domain_id=self.ipfix_domain_id, - src_port=self.ipfix_src_port) - - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=3025)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(0) - self.vapi.cli("ipfix flush") # FIXME this should be an API call - capture = self.pg3.get_capture(3) - ipfix = IPFIXDecoder() - # first load template - for p in capture: - self.assertTrue(p.haslayer(IPFIX)) - self.assertEqual(p[IP].src, self.pg3.local_ip4) - self.assertEqual(p[IP].dst, self.pg3.remote_ip4) - self.assertEqual(p[UDP].sport, self.ipfix_src_port) - self.assertEqual(p[UDP].dport, 4739) - self.assertEqual(p[IPFIX].observationDomainID, - self.ipfix_domain_id) - if p.haslayer(Template): - ipfix.add_template(p.getlayer(Template)) - # verify events in data set - for p in capture: - if p.haslayer(Data): - data = ipfix.decode_data_set(p.getlayer(Set)) - self.verify_ipfix_addr_exhausted(data) - - def test_pool_addr_fib(self): - """ S-NAT add pool addresses to FIB """ - static_addr = '10.0.0.10' - self.snat_add_address(self.snat_addr) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - self.snat_add_static_mapping(self.pg0.remote_ip4, static_addr) - - # SNAT address - p = (Ether(src=self.pg1.remote_mac, dst='ff:ff:ff:ff:ff:ff') / - ARP(op=ARP.who_has, pdst=self.snat_addr, - psrc=self.pg1.remote_ip4, hwsrc=self.pg1.remote_mac)) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(1) - self.assertTrue(capture[0].haslayer(ARP)) - self.assertTrue(capture[0][ARP].op, ARP.is_at) - - # 1:1 NAT address - p = (Ether(src=self.pg1.remote_mac, dst='ff:ff:ff:ff:ff:ff') / - ARP(op=ARP.who_has, pdst=static_addr, - psrc=self.pg1.remote_ip4, hwsrc=self.pg1.remote_mac)) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(1) - self.assertTrue(capture[0].haslayer(ARP)) - self.assertTrue(capture[0][ARP].op, ARP.is_at) - - # send ARP to non-SNAT interface - p = (Ether(src=self.pg2.remote_mac, dst='ff:ff:ff:ff:ff:ff') / - ARP(op=ARP.who_has, pdst=self.snat_addr, - psrc=self.pg2.remote_ip4, hwsrc=self.pg2.remote_mac)) - self.pg2.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(0) - - # remove addresses and verify - self.snat_add_address(self.snat_addr, is_add=0) - self.snat_add_static_mapping(self.pg0.remote_ip4, static_addr, - is_add=0) - - p = (Ether(src=self.pg1.remote_mac, dst='ff:ff:ff:ff:ff:ff') / - ARP(op=ARP.who_has, pdst=self.snat_addr, - psrc=self.pg1.remote_ip4, hwsrc=self.pg1.remote_mac)) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(0) - - p = (Ether(src=self.pg1.remote_mac, dst='ff:ff:ff:ff:ff:ff') / - ARP(op=ARP.who_has, pdst=static_addr, - psrc=self.pg1.remote_ip4, hwsrc=self.pg1.remote_mac)) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(0) - - def test_vrf_mode(self): - """ S-NAT tenant VRF aware address pool mode """ - - vrf_id1 = 1 - vrf_id2 = 2 - nat_ip1 = "10.0.0.10" - nat_ip2 = "10.0.0.11" - - self.pg0.unconfig_ip4() - self.pg1.unconfig_ip4() - self.pg0.set_table_ip4(vrf_id1) - self.pg1.set_table_ip4(vrf_id2) - self.pg0.config_ip4() - self.pg1.config_ip4() - - self.snat_add_address(nat_ip1, vrf_id=vrf_id1) - self.snat_add_address(nat_ip2, vrf_id=vrf_id2) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg2.sw_if_index, - is_inside=0) - - # first VRF - pkts = self.create_stream_in(self.pg0, self.pg2) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg2.get_capture(len(pkts)) - self.verify_capture_out(capture, nat_ip1) - - # second VRF - pkts = self.create_stream_in(self.pg1, self.pg2) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg2.get_capture(len(pkts)) - self.verify_capture_out(capture, nat_ip2) - - def test_vrf_feature_independent(self): - """ S-NAT tenant VRF independent address pool mode """ - - nat_ip1 = "10.0.0.10" - nat_ip2 = "10.0.0.11" - - self.snat_add_address(nat_ip1) - self.snat_add_address(nat_ip2) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg2.sw_if_index, - is_inside=0) - - # first VRF - pkts = self.create_stream_in(self.pg0, self.pg2) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg2.get_capture(len(pkts)) - self.verify_capture_out(capture, nat_ip1) - - # second VRF - pkts = self.create_stream_in(self.pg1, self.pg2) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg2.get_capture(len(pkts)) - self.verify_capture_out(capture, nat_ip1) - - def test_dynamic_ipless_interfaces(self): - """ SNAT interfaces without configured ip dynamic map """ - - self.vapi.ip_neighbor_add_del(self.pg7.sw_if_index, - self.pg7.remote_mac, - self.pg7.remote_ip4n, - is_static=1) - self.vapi.ip_neighbor_add_del(self.pg8.sw_if_index, - self.pg8.remote_mac, - self.pg8.remote_ip4n, - is_static=1) - - self.vapi.ip_add_del_route(dst_address=self.pg7.remote_ip4n, - dst_address_length=32, - next_hop_address=self.pg7.remote_ip4n, - next_hop_sw_if_index=self.pg7.sw_if_index) - self.vapi.ip_add_del_route(dst_address=self.pg8.remote_ip4n, - dst_address_length=32, - next_hop_address=self.pg8.remote_ip4n, - next_hop_sw_if_index=self.pg8.sw_if_index) - - self.snat_add_address(self.snat_addr) - self.vapi.snat_interface_add_del_feature(self.pg7.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg8.sw_if_index, - is_inside=0) - - # in2out - pkts = self.create_stream_in(self.pg7, self.pg8) - self.pg7.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg8.get_capture(len(pkts)) - self.verify_capture_out(capture) - - # out2in - pkts = self.create_stream_out(self.pg8, self.snat_addr) - self.pg8.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg7.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg7) - - def test_static_ipless_interfaces(self): - """ SNAT 1:1 NAT interfaces without configured ip """ - - self.vapi.ip_neighbor_add_del(self.pg7.sw_if_index, - self.pg7.remote_mac, - self.pg7.remote_ip4n, - is_static=1) - self.vapi.ip_neighbor_add_del(self.pg8.sw_if_index, - self.pg8.remote_mac, - self.pg8.remote_ip4n, - is_static=1) - - self.vapi.ip_add_del_route(dst_address=self.pg7.remote_ip4n, - dst_address_length=32, - next_hop_address=self.pg7.remote_ip4n, - next_hop_sw_if_index=self.pg7.sw_if_index) - self.vapi.ip_add_del_route(dst_address=self.pg8.remote_ip4n, - dst_address_length=32, - next_hop_address=self.pg8.remote_ip4n, - next_hop_sw_if_index=self.pg8.sw_if_index) - - self.snat_add_static_mapping(self.pg7.remote_ip4, self.snat_addr) - self.vapi.snat_interface_add_del_feature(self.pg7.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg8.sw_if_index, - is_inside=0) - - # out2in - pkts = self.create_stream_out(self.pg8) - self.pg8.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg7.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg7) - - # in2out - pkts = self.create_stream_in(self.pg7, self.pg8) - self.pg7.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg8.get_capture(len(pkts)) - self.verify_capture_out(capture, self.snat_addr, True) - - def test_static_with_port_ipless_interfaces(self): - """ SNAT 1:1 NAT with port interfaces without configured ip """ - - self.tcp_port_out = 30606 - self.udp_port_out = 30607 - self.icmp_id_out = 30608 - - self.vapi.ip_neighbor_add_del(self.pg7.sw_if_index, - self.pg7.remote_mac, - self.pg7.remote_ip4n, - is_static=1) - self.vapi.ip_neighbor_add_del(self.pg8.sw_if_index, - self.pg8.remote_mac, - self.pg8.remote_ip4n, - is_static=1) - - self.vapi.ip_add_del_route(dst_address=self.pg7.remote_ip4n, - dst_address_length=32, - next_hop_address=self.pg7.remote_ip4n, - next_hop_sw_if_index=self.pg7.sw_if_index) - self.vapi.ip_add_del_route(dst_address=self.pg8.remote_ip4n, - dst_address_length=32, - next_hop_address=self.pg8.remote_ip4n, - next_hop_sw_if_index=self.pg8.sw_if_index) - - self.snat_add_address(self.snat_addr) - self.snat_add_static_mapping(self.pg7.remote_ip4, self.snat_addr, - self.tcp_port_in, self.tcp_port_out, - proto=IP_PROTOS.tcp) - self.snat_add_static_mapping(self.pg7.remote_ip4, self.snat_addr, - self.udp_port_in, self.udp_port_out, - proto=IP_PROTOS.udp) - self.snat_add_static_mapping(self.pg7.remote_ip4, self.snat_addr, - self.icmp_id_in, self.icmp_id_out, - proto=IP_PROTOS.icmp) - self.vapi.snat_interface_add_del_feature(self.pg7.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg8.sw_if_index, - is_inside=0) - - # out2in - pkts = self.create_stream_out(self.pg8) - self.pg8.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg7.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg7) - - # in2out - pkts = self.create_stream_in(self.pg7, self.pg8) - self.pg7.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg8.get_capture(len(pkts)) - self.verify_capture_out(capture) - - def test_static_unknown_proto(self): - """ 1:1 NAT translate packet with unknown protocol """ - nat_ip = "10.0.0.10" - self.snat_add_static_mapping(self.pg0.remote_ip4, nat_ip) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - - # in2out - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - GRE() / - IP(src=self.pg2.remote_ip4, dst=self.pg3.remote_ip4) / - TCP(sport=1234, dport=1234)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg1.get_capture(1) - packet = p[0] - try: - self.assertEqual(packet[IP].src, nat_ip) - self.assertEqual(packet[IP].dst, self.pg1.remote_ip4) - self.assertTrue(packet.haslayer(GRE)) - self.check_ip_checksum(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - # out2in - p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / - IP(src=self.pg1.remote_ip4, dst=nat_ip) / - GRE() / - IP(src=self.pg3.remote_ip4, dst=self.pg2.remote_ip4) / - TCP(sport=1234, dport=1234)) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg0.get_capture(1) - packet = p[0] - try: - self.assertEqual(packet[IP].src, self.pg1.remote_ip4) - self.assertEqual(packet[IP].dst, self.pg0.remote_ip4) - self.assertTrue(packet.haslayer(GRE)) - self.check_ip_checksum(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - def test_hairpinning_static_unknown_proto(self): - """ 1:1 NAT translate packet with unknown protocol - hairpinning """ - - host = self.pg0.remote_hosts[0] - server = self.pg0.remote_hosts[1] - - host_nat_ip = "10.0.0.10" - server_nat_ip = "10.0.0.11" - - self.snat_add_static_mapping(host.ip4, host_nat_ip) - self.snat_add_static_mapping(server.ip4, server_nat_ip) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - - # host to server - p = (Ether(dst=self.pg0.local_mac, src=host.mac) / - IP(src=host.ip4, dst=server_nat_ip) / - GRE() / - IP(src=self.pg2.remote_ip4, dst=self.pg3.remote_ip4) / - TCP(sport=1234, dport=1234)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg0.get_capture(1) - packet = p[0] - try: - self.assertEqual(packet[IP].src, host_nat_ip) - self.assertEqual(packet[IP].dst, server.ip4) - self.assertTrue(packet.haslayer(GRE)) - self.check_ip_checksum(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - # server to host - p = (Ether(dst=self.pg0.local_mac, src=server.mac) / - IP(src=server.ip4, dst=host_nat_ip) / - GRE() / - IP(src=self.pg3.remote_ip4, dst=self.pg2.remote_ip4) / - TCP(sport=1234, dport=1234)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg0.get_capture(1) - packet = p[0] - try: - self.assertEqual(packet[IP].src, server_nat_ip) - self.assertEqual(packet[IP].dst, host.ip4) - self.assertTrue(packet.haslayer(GRE)) - self.check_ip_checksum(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - def test_unknown_proto(self): - """ SNAT translate packet with unknown protocol """ - self.snat_add_address(self.snat_addr) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - - # in2out - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=20)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg1.get_capture(1) - - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - GRE() / - IP(src=self.pg2.remote_ip4, dst=self.pg3.remote_ip4) / - TCP(sport=1234, dport=1234)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg1.get_capture(1) - packet = p[0] - try: - self.assertEqual(packet[IP].src, self.snat_addr) - self.assertEqual(packet[IP].dst, self.pg1.remote_ip4) - self.assertTrue(packet.haslayer(GRE)) - self.check_ip_checksum(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - # out2in - p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / - IP(src=self.pg1.remote_ip4, dst=self.snat_addr) / - GRE() / - IP(src=self.pg3.remote_ip4, dst=self.pg2.remote_ip4) / - TCP(sport=1234, dport=1234)) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg0.get_capture(1) - packet = p[0] - try: - self.assertEqual(packet[IP].src, self.pg1.remote_ip4) - self.assertEqual(packet[IP].dst, self.pg0.remote_ip4) - self.assertTrue(packet.haslayer(GRE)) - self.check_ip_checksum(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - def test_hairpinning_unknown_proto(self): - """ SNAT translate packet with unknown protocol - hairpinning """ - host = self.pg0.remote_hosts[0] - server = self.pg0.remote_hosts[1] - host_in_port = 1234 - host_out_port = 0 - server_in_port = 5678 - server_out_port = 8765 - server_nat_ip = "10.0.0.11" - - self.snat_add_address(self.snat_addr) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - - # add static mapping for server - self.snat_add_static_mapping(server.ip4, server_nat_ip) - - # host to server - p = (Ether(src=host.mac, dst=self.pg0.local_mac) / - IP(src=host.ip4, dst=server_nat_ip) / - TCP(sport=host_in_port, dport=server_out_port)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(1) - - p = (Ether(dst=self.pg0.local_mac, src=host.mac) / - IP(src=host.ip4, dst=server_nat_ip) / - GRE() / - IP(src=self.pg2.remote_ip4, dst=self.pg3.remote_ip4) / - TCP(sport=1234, dport=1234)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg0.get_capture(1) - packet = p[0] - try: - self.assertEqual(packet[IP].src, self.snat_addr) - self.assertEqual(packet[IP].dst, server.ip4) - self.assertTrue(packet.haslayer(GRE)) - self.check_ip_checksum(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - # server to host - p = (Ether(dst=self.pg0.local_mac, src=server.mac) / - IP(src=server.ip4, dst=self.snat_addr) / - GRE() / - IP(src=self.pg3.remote_ip4, dst=self.pg2.remote_ip4) / - TCP(sport=1234, dport=1234)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg0.get_capture(1) - packet = p[0] - try: - self.assertEqual(packet[IP].src, server_nat_ip) - self.assertEqual(packet[IP].dst, host.ip4) - self.assertTrue(packet.haslayer(GRE)) - self.check_ip_checksum(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - def test_output_feature(self): - """ S-NAT interface output feature (in2out postrouting) """ - self.snat_add_address(self.snat_addr) - self.vapi.snat_interface_add_del_output_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_output_feature(self.pg1.sw_if_index, - is_inside=0) - - # in2out - pkts = self.create_stream_in(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out(capture) - - # out2in - pkts = self.create_stream_out(self.pg1) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg0) - - def test_output_feature_vrf_aware(self): - """ S-NAT interface output feature VRF aware (in2out postrouting) """ - nat_ip_vrf10 = "10.0.0.10" - nat_ip_vrf20 = "10.0.0.20" - - self.vapi.ip_add_del_route(dst_address=self.pg3.remote_ip4n, - dst_address_length=32, - next_hop_address=self.pg3.remote_ip4n, - next_hop_sw_if_index=self.pg3.sw_if_index, - table_id=10) - self.vapi.ip_add_del_route(dst_address=self.pg3.remote_ip4n, - dst_address_length=32, - next_hop_address=self.pg3.remote_ip4n, - next_hop_sw_if_index=self.pg3.sw_if_index, - table_id=20) - - self.snat_add_address(nat_ip_vrf10, vrf_id=10) - self.snat_add_address(nat_ip_vrf20, vrf_id=20) - self.vapi.snat_interface_add_del_output_feature(self.pg4.sw_if_index) - self.vapi.snat_interface_add_del_output_feature(self.pg6.sw_if_index) - self.vapi.snat_interface_add_del_output_feature(self.pg3.sw_if_index, - is_inside=0) - - # in2out VRF 10 - pkts = self.create_stream_in(self.pg4, self.pg3) - self.pg4.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg3.get_capture(len(pkts)) - self.verify_capture_out(capture, nat_ip=nat_ip_vrf10) - - # out2in VRF 10 - pkts = self.create_stream_out(self.pg3, dst_ip=nat_ip_vrf10) - self.pg3.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg4.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg4) - - # in2out VRF 20 - pkts = self.create_stream_in(self.pg6, self.pg3) - self.pg6.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg3.get_capture(len(pkts)) - self.verify_capture_out(capture, nat_ip=nat_ip_vrf20) - - # out2in VRF 20 - pkts = self.create_stream_out(self.pg3, dst_ip=nat_ip_vrf20) - self.pg3.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg6.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg6) - - def test_output_feature_hairpinning(self): - """ S-NAT interface output feature hairpinning (in2out postrouting) """ - host = self.pg0.remote_hosts[0] - server = self.pg0.remote_hosts[1] - host_in_port = 1234 - host_out_port = 0 - server_in_port = 5678 - server_out_port = 8765 - - self.snat_add_address(self.snat_addr) - self.vapi.snat_interface_add_del_output_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_output_feature(self.pg1.sw_if_index, - is_inside=0) - - # add static mapping for server - self.snat_add_static_mapping(server.ip4, self.snat_addr, - server_in_port, server_out_port, - proto=IP_PROTOS.tcp) - - # send packet from host to server - p = (Ether(src=host.mac, dst=self.pg0.local_mac) / - IP(src=host.ip4, dst=self.snat_addr) / - TCP(sport=host_in_port, dport=server_out_port)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, self.snat_addr) - self.assertEqual(ip.dst, server.ip4) - self.assertNotEqual(tcp.sport, host_in_port) - self.assertEqual(tcp.dport, server_in_port) - self.check_tcp_checksum(p) - host_out_port = tcp.sport - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # send reply from server to host - p = (Ether(src=server.mac, dst=self.pg0.local_mac) / - IP(src=server.ip4, dst=self.snat_addr) / - TCP(sport=server_in_port, dport=host_out_port)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, self.snat_addr) - self.assertEqual(ip.dst, host.ip4) - self.assertEqual(tcp.sport, server_out_port) - self.assertEqual(tcp.dport, host_in_port) - self.check_tcp_checksum(p) - except: - self.logger.error(ppp("Unexpected or invalid packet:"), p) - raise - - def tearDown(self): - super(TestSNAT, self).tearDown() - if not self.vpp_dead: - self.logger.info(self.vapi.cli("show snat verbose")) - self.clear_snat() - - -class TestDeterministicNAT(MethodHolder): - """ Deterministic NAT Test Cases """ - - @classmethod - def setUpConstants(cls): - super(TestDeterministicNAT, cls).setUpConstants() - cls.vpp_cmdline.extend(["snat", "{", "deterministic", "}"]) - - @classmethod - def setUpClass(cls): - super(TestDeterministicNAT, cls).setUpClass() - - try: - cls.tcp_port_in = 6303 - cls.tcp_external_port = 6303 - cls.udp_port_in = 6304 - cls.udp_external_port = 6304 - cls.icmp_id_in = 6305 - cls.snat_addr = '10.0.0.3' - - cls.create_pg_interfaces(range(3)) - cls.interfaces = list(cls.pg_interfaces) - - for i in cls.interfaces: - i.admin_up() - i.config_ip4() - i.resolve_arp() - - cls.pg0.generate_remote_hosts(2) - cls.pg0.configure_ipv4_neighbors() - - except Exception: - super(TestDeterministicNAT, cls).tearDownClass() - raise - - def create_stream_in(self, in_if, out_if, ttl=64): - """ - Create packet stream for inside network - - :param in_if: Inside interface - :param out_if: Outside interface - :param ttl: TTL of generated packets - """ - pkts = [] - # TCP - p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / - IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl) / - TCP(sport=self.tcp_port_in, dport=self.tcp_external_port)) - pkts.append(p) - - # UDP - p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / - IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl) / - UDP(sport=self.udp_port_in, dport=self.udp_external_port)) - pkts.append(p) - - # ICMP - p = (Ether(dst=in_if.local_mac, src=in_if.remote_mac) / - IP(src=in_if.remote_ip4, dst=out_if.remote_ip4, ttl=ttl) / - ICMP(id=self.icmp_id_in, type='echo-request')) - pkts.append(p) - - return pkts - - def create_stream_out(self, out_if, dst_ip=None, ttl=64): - """ - Create packet stream for outside network - - :param out_if: Outside interface - :param dst_ip: Destination IP address (Default use global SNAT address) - :param ttl: TTL of generated packets - """ - if dst_ip is None: - dst_ip = self.snat_addr - pkts = [] - # TCP - p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) / - IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) / - TCP(dport=self.tcp_port_out, sport=self.tcp_external_port)) - pkts.append(p) - - # UDP - p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) / - IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) / - UDP(dport=self.udp_port_out, sport=self.udp_external_port)) - pkts.append(p) - - # ICMP - p = (Ether(dst=out_if.local_mac, src=out_if.remote_mac) / - IP(src=out_if.remote_ip4, dst=dst_ip, ttl=ttl) / - ICMP(id=self.icmp_external_id, type='echo-reply')) - pkts.append(p) - - return pkts - - def verify_capture_out(self, capture, nat_ip=None, packet_num=3): - """ - Verify captured packets on outside network - - :param capture: Captured packets - :param nat_ip: Translated IP address (Default use global SNAT address) - :param same_port: Sorce port number is not translated (Default False) - :param packet_num: Expected number of packets (Default 3) - """ - if nat_ip is None: - nat_ip = self.snat_addr - self.assertEqual(packet_num, len(capture)) - for packet in capture: - try: - self.assertEqual(packet[IP].src, nat_ip) - if packet.haslayer(TCP): - self.tcp_port_out = packet[TCP].sport - elif packet.haslayer(UDP): - self.udp_port_out = packet[UDP].sport - else: - self.icmp_external_id = packet[ICMP].id - except: - self.logger.error(ppp("Unexpected or invalid packet " - "(outside network):", packet)) - raise - - def initiate_tcp_session(self, in_if, out_if): - """ - Initiates TCP session - - :param in_if: Inside interface - :param out_if: Outside interface - """ - try: - # SYN packet in->out - p = (Ether(src=in_if.remote_mac, dst=in_if.local_mac) / - IP(src=in_if.remote_ip4, dst=out_if.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, - flags="S")) - in_if.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = out_if.get_capture(1) - p = capture[0] - self.tcp_port_out = p[TCP].sport - - # SYN + ACK packet out->in - p = (Ether(src=out_if.remote_mac, dst=out_if.local_mac) / - IP(src=out_if.remote_ip4, dst=self.snat_addr) / - TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, - flags="SA")) - out_if.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - in_if.get_capture(1) - - # ACK packet in->out - p = (Ether(src=in_if.remote_mac, dst=in_if.local_mac) / - IP(src=in_if.remote_ip4, dst=out_if.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, - flags="A")) - in_if.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - out_if.get_capture(1) - - except: - self.logger.error("TCP 3 way handshake failed") - raise - - def verify_ipfix_max_entries_per_user(self, data): - """ - Verify IPFIX maximum entries per user exceeded event - - :param data: Decoded IPFIX data records - """ - self.assertEqual(1, len(data)) - record = data[0] - # natEvent - self.assertEqual(ord(record[230]), 13) - # natQuotaExceededEvent - self.assertEqual('\x03\x00\x00\x00', record[466]) - # sourceIPv4Address - self.assertEqual(self.pg0.remote_ip4n, record[8]) - - def test_deterministic_mode(self): - """ S-NAT run deterministic mode """ - in_addr = '172.16.255.0' - out_addr = '172.17.255.50' - in_addr_t = '172.16.255.20' - in_addr_n = socket.inet_aton(in_addr) - out_addr_n = socket.inet_aton(out_addr) - in_addr_t_n = socket.inet_aton(in_addr_t) - in_plen = 24 - out_plen = 32 - - snat_config = self.vapi.snat_show_config() - self.assertEqual(1, snat_config.deterministic) - - self.vapi.snat_add_det_map(in_addr_n, in_plen, out_addr_n, out_plen) - - rep1 = self.vapi.snat_det_forward(in_addr_t_n) - self.assertEqual(rep1.out_addr[:4], out_addr_n) - rep2 = self.vapi.snat_det_reverse(out_addr_n, rep1.out_port_hi) - self.assertEqual(rep2.in_addr[:4], in_addr_t_n) - - deterministic_mappings = self.vapi.snat_det_map_dump() - self.assertEqual(len(deterministic_mappings), 1) - dsm = deterministic_mappings[0] - self.assertEqual(in_addr_n, dsm.in_addr[:4]) - self.assertEqual(in_plen, dsm.in_plen) - self.assertEqual(out_addr_n, dsm.out_addr[:4]) - self.assertEqual(out_plen, dsm.out_plen) - - self.clear_snat() - deterministic_mappings = self.vapi.snat_det_map_dump() - self.assertEqual(len(deterministic_mappings), 0) - - def test_set_timeouts(self): - """ Set deterministic NAT timeouts """ - timeouts_before = self.vapi.snat_det_get_timeouts() - - self.vapi.snat_det_set_timeouts(timeouts_before.udp + 10, - timeouts_before.tcp_established + 10, - timeouts_before.tcp_transitory + 10, - timeouts_before.icmp + 10) - - timeouts_after = self.vapi.snat_det_get_timeouts() - - self.assertNotEqual(timeouts_before.udp, timeouts_after.udp) - self.assertNotEqual(timeouts_before.icmp, timeouts_after.icmp) - self.assertNotEqual(timeouts_before.tcp_established, - timeouts_after.tcp_established) - self.assertNotEqual(timeouts_before.tcp_transitory, - timeouts_after.tcp_transitory) - - def test_det_in(self): - """ CGNAT translation test (TCP, UDP, ICMP) """ - - nat_ip = "10.0.0.10" - - self.vapi.snat_add_det_map(self.pg0.remote_ip4n, - 32, - socket.inet_aton(nat_ip), - 32) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - - # in2out - pkts = self.create_stream_in(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out(capture, nat_ip) - - # out2in - pkts = self.create_stream_out(self.pg1, nat_ip) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - self.verify_capture_in(capture, self.pg0) - - # session dump test - sessions = self.vapi.snat_det_session_dump(self.pg0.remote_ip4n) - self.assertEqual(len(sessions), 3) - - # TCP session - s = sessions[0] - self.assertEqual(s.ext_addr[:4], self.pg1.remote_ip4n) - self.assertEqual(s.in_port, self.tcp_port_in) - self.assertEqual(s.out_port, self.tcp_port_out) - self.assertEqual(s.ext_port, self.tcp_external_port) - - # UDP session - s = sessions[1] - self.assertEqual(s.ext_addr[:4], self.pg1.remote_ip4n) - self.assertEqual(s.in_port, self.udp_port_in) - self.assertEqual(s.out_port, self.udp_port_out) - self.assertEqual(s.ext_port, self.udp_external_port) - - # ICMP session - s = sessions[2] - self.assertEqual(s.ext_addr[:4], self.pg1.remote_ip4n) - self.assertEqual(s.in_port, self.icmp_id_in) - self.assertEqual(s.out_port, self.icmp_external_id) - - def test_multiple_users(self): - """ CGNAT multiple users """ - - nat_ip = "10.0.0.10" - port_in = 80 - external_port = 6303 - - host0 = self.pg0.remote_hosts[0] - host1 = self.pg0.remote_hosts[1] - - self.vapi.snat_add_det_map(host0.ip4n, - 24, - socket.inet_aton(nat_ip), - 32) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - - # host0 to out - p = (Ether(src=host0.mac, dst=self.pg0.local_mac) / - IP(src=host0.ip4, dst=self.pg1.remote_ip4) / - TCP(sport=port_in, dport=external_port)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, nat_ip) - self.assertEqual(ip.dst, self.pg1.remote_ip4) - self.assertEqual(tcp.dport, external_port) - port_out0 = tcp.sport - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # host1 to out - p = (Ether(src=host1.mac, dst=self.pg0.local_mac) / - IP(src=host1.ip4, dst=self.pg1.remote_ip4) / - TCP(sport=port_in, dport=external_port)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, nat_ip) - self.assertEqual(ip.dst, self.pg1.remote_ip4) - self.assertEqual(tcp.dport, external_port) - port_out1 = tcp.sport - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - dms = self.vapi.snat_det_map_dump() - self.assertEqual(1, len(dms)) - self.assertEqual(2, dms[0].ses_num) - - # out to host0 - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=nat_ip) / - TCP(sport=external_port, dport=port_out0)) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, self.pg1.remote_ip4) - self.assertEqual(ip.dst, host0.ip4) - self.assertEqual(tcp.dport, port_in) - self.assertEqual(tcp.sport, external_port) - except: - self.logger.error(ppp("Unexpected or invalid packet:", p)) - raise - - # out to host1 - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=nat_ip) / - TCP(sport=external_port, dport=port_out1)) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(1) - p = capture[0] - try: - ip = p[IP] - tcp = p[TCP] - self.assertEqual(ip.src, self.pg1.remote_ip4) - self.assertEqual(ip.dst, host1.ip4) - self.assertEqual(tcp.dport, port_in) - self.assertEqual(tcp.sport, external_port) - except: - self.logger.error(ppp("Unexpected or invalid packet", p)) - raise - - # session close api test - self.vapi.snat_det_close_session_out(socket.inet_aton(nat_ip), - port_out1, - self.pg1.remote_ip4n, - external_port) - dms = self.vapi.snat_det_map_dump() - self.assertEqual(dms[0].ses_num, 1) - - self.vapi.snat_det_close_session_in(host0.ip4n, - port_in, - self.pg1.remote_ip4n, - external_port) - dms = self.vapi.snat_det_map_dump() - self.assertEqual(dms[0].ses_num, 0) - - def test_tcp_session_close_detection_in(self): - """ CGNAT TCP session close initiated from inside network """ - self.vapi.snat_add_det_map(self.pg0.remote_ip4n, - 32, - socket.inet_aton(self.snat_addr), - 32) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - - self.initiate_tcp_session(self.pg0, self.pg1) - - # close the session from inside - try: - # FIN packet in -> out - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, - flags="F")) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.get_capture(1) - - pkts = [] - - # ACK packet out -> in - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=self.snat_addr) / - TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, - flags="A")) - pkts.append(p) - - # FIN packet out -> in - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=self.snat_addr) / - TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, - flags="F")) - pkts.append(p) - - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg0.get_capture(2) - - # ACK packet in -> out - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, - flags="A")) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.get_capture(1) - - # Check if snat closed the session - dms = self.vapi.snat_det_map_dump() - self.assertEqual(0, dms[0].ses_num) - except: - self.logger.error("TCP session termination failed") - raise - - def test_tcp_session_close_detection_out(self): - """ CGNAT TCP session close initiated from outside network """ - self.vapi.snat_add_det_map(self.pg0.remote_ip4n, - 32, - socket.inet_aton(self.snat_addr), - 32) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - - self.initiate_tcp_session(self.pg0, self.pg1) - - # close the session from outside - try: - # FIN packet out -> in - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=self.snat_addr) / - TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, - flags="F")) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg0.get_capture(1) - - pkts = [] - - # ACK packet in -> out - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, - flags="A")) - pkts.append(p) - - # ACK packet in -> out - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - TCP(sport=self.tcp_port_in, dport=self.tcp_external_port, - flags="F")) - pkts.append(p) - - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg1.get_capture(2) - - # ACK packet out -> in - p = (Ether(src=self.pg1.remote_mac, dst=self.pg1.local_mac) / - IP(src=self.pg1.remote_ip4, dst=self.snat_addr) / - TCP(sport=self.tcp_external_port, dport=self.tcp_port_out, - flags="A")) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - self.pg0.get_capture(1) - - # Check if snat closed the session - dms = self.vapi.snat_det_map_dump() - self.assertEqual(0, dms[0].ses_num) - except: - self.logger.error("TCP session termination failed") - raise - - @unittest.skipUnless(running_extended_tests(), "part of extended tests") - def test_session_timeout(self): - """ CGNAT session timeouts """ - self.vapi.snat_add_det_map(self.pg0.remote_ip4n, - 32, - socket.inet_aton(self.snat_addr), - 32) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - - self.initiate_tcp_session(self.pg0, self.pg1) - self.vapi.snat_det_set_timeouts(5, 5, 5, 5) - pkts = self.create_stream_in(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - sleep(15) - - dms = self.vapi.snat_det_map_dump() - self.assertEqual(0, dms[0].ses_num) - - @unittest.skipUnless(running_extended_tests(), "part of extended tests") - def test_session_limit_per_user(self): - """ CGNAT maximum 1000 sessions per user should be created """ - self.vapi.snat_add_det_map(self.pg0.remote_ip4n, - 32, - socket.inet_aton(self.snat_addr), - 32) - self.vapi.snat_interface_add_del_feature(self.pg0.sw_if_index) - self.vapi.snat_interface_add_del_feature(self.pg1.sw_if_index, - is_inside=0) - self.vapi.set_ipfix_exporter(collector_address=self.pg2.remote_ip4n, - src_address=self.pg2.local_ip4n, - path_mtu=512, - template_interval=10) - self.vapi.snat_ipfix() - - pkts = [] - for port in range(1025, 2025): - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - UDP(sport=port, dport=port)) - pkts.append(p) - - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - - p = (Ether(src=self.pg0.remote_mac, dst=self.pg0.local_mac) / - IP(src=self.pg0.remote_ip4, dst=self.pg1.remote_ip4) / - UDP(sport=3001, dport=3002)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.assert_nothing_captured() - - # verify ICMP error packet - capture = self.pg0.get_capture(1) - p = capture[0] - self.assertTrue(p.haslayer(ICMP)) - icmp = p[ICMP] - self.assertEqual(icmp.type, 3) - self.assertEqual(icmp.code, 1) - self.assertTrue(icmp.haslayer(IPerror)) - inner_ip = icmp[IPerror] - self.assertEqual(inner_ip[UDPerror].sport, 3001) - self.assertEqual(inner_ip[UDPerror].dport, 3002) - - dms = self.vapi.snat_det_map_dump() - - self.assertEqual(1000, dms[0].ses_num) - - # verify IPFIX logging - self.vapi.cli("ipfix flush") # FIXME this should be an API call - sleep(1) - capture = self.pg2.get_capture(2) - ipfix = IPFIXDecoder() - # first load template - for p in capture: - self.assertTrue(p.haslayer(IPFIX)) - if p.haslayer(Template): - ipfix.add_template(p.getlayer(Template)) - # verify events in data set - for p in capture: - if p.haslayer(Data): - data = ipfix.decode_data_set(p.getlayer(Set)) - self.verify_ipfix_max_entries_per_user(data) - - def clear_snat(self): - """ - Clear SNAT configuration. - """ - self.vapi.snat_ipfix(enable=0) - self.vapi.snat_det_set_timeouts() - deterministic_mappings = self.vapi.snat_det_map_dump() - for dsm in deterministic_mappings: - self.vapi.snat_add_det_map(dsm.in_addr, - dsm.in_plen, - dsm.out_addr, - dsm.out_plen, - is_add=0) - - interfaces = self.vapi.snat_interface_dump() - for intf in interfaces: - self.vapi.snat_interface_add_del_feature(intf.sw_if_index, - intf.is_inside, - is_add=0) - - def tearDown(self): - super(TestDeterministicNAT, self).tearDown() - if not self.vpp_dead: - self.logger.info(self.vapi.cli("show snat detail")) - self.clear_snat() - - -class TestNAT64(MethodHolder): - """ NAT64 Test Cases """ - - @classmethod - def setUpClass(cls): - super(TestNAT64, cls).setUpClass() - - try: - cls.tcp_port_in = 6303 - cls.tcp_port_out = 6303 - cls.udp_port_in = 6304 - cls.udp_port_out = 6304 - cls.icmp_id_in = 6305 - cls.icmp_id_out = 6305 - cls.nat_addr = '10.0.0.3' - cls.nat_addr_n = socket.inet_pton(socket.AF_INET, cls.nat_addr) - cls.vrf1_id = 10 - cls.vrf1_nat_addr = '10.0.10.3' - cls.vrf1_nat_addr_n = socket.inet_pton(socket.AF_INET, - cls.vrf1_nat_addr) - - cls.create_pg_interfaces(range(3)) - cls.ip6_interfaces = list(cls.pg_interfaces[0:1]) - cls.ip6_interfaces.append(cls.pg_interfaces[2]) - cls.ip4_interfaces = list(cls.pg_interfaces[1:2]) - - cls.pg_interfaces[2].set_table_ip6(cls.vrf1_id) - - cls.pg0.generate_remote_hosts(2) - - for i in cls.ip6_interfaces: - i.admin_up() - i.config_ip6() - i.configure_ipv6_neighbors() - - for i in cls.ip4_interfaces: - i.admin_up() - i.config_ip4() - i.resolve_arp() - - except Exception: - super(TestNAT64, cls).tearDownClass() - raise - - def test_pool(self): - """ Add/delete address to NAT64 pool """ - nat_addr = socket.inet_pton(socket.AF_INET, '1.2.3.4') - - self.vapi.nat64_add_del_pool_addr_range(nat_addr, nat_addr) - - addresses = self.vapi.nat64_pool_addr_dump() - self.assertEqual(len(addresses), 1) - self.assertEqual(addresses[0].address, nat_addr) - - self.vapi.nat64_add_del_pool_addr_range(nat_addr, nat_addr, is_add=0) - - addresses = self.vapi.nat64_pool_addr_dump() - self.assertEqual(len(addresses), 0) - - def test_interface(self): - """ Enable/disable NAT64 feature on the interface """ - self.vapi.nat64_add_del_interface(self.pg0.sw_if_index) - self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0) - - interfaces = self.vapi.nat64_interface_dump() - self.assertEqual(len(interfaces), 2) - pg0_found = False - pg1_found = False - for intf in interfaces: - if intf.sw_if_index == self.pg0.sw_if_index: - self.assertEqual(intf.is_inside, 1) - pg0_found = True - elif intf.sw_if_index == self.pg1.sw_if_index: - self.assertEqual(intf.is_inside, 0) - pg1_found = True - self.assertTrue(pg0_found) - self.assertTrue(pg1_found) - - features = self.vapi.cli("show interface features pg0") - self.assertNotEqual(features.find('nat64-in2out'), -1) - features = self.vapi.cli("show interface features pg1") - self.assertNotEqual(features.find('nat64-out2in'), -1) - - self.vapi.nat64_add_del_interface(self.pg0.sw_if_index, is_add=0) - self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_add=0) - - interfaces = self.vapi.nat64_interface_dump() - self.assertEqual(len(interfaces), 0) - - def test_static_bib(self): - """ Add/delete static BIB entry """ - in_addr = socket.inet_pton(socket.AF_INET6, - '2001:db8:85a3::8a2e:370:7334') - out_addr = socket.inet_pton(socket.AF_INET, '10.1.1.3') - in_port = 1234 - out_port = 5678 - proto = IP_PROTOS.tcp - - self.vapi.nat64_add_del_static_bib(in_addr, - out_addr, - in_port, - out_port, - proto) - bib = self.vapi.nat64_bib_dump(IP_PROTOS.tcp) - static_bib_num = 0 - for bibe in bib: - if bibe.is_static: - static_bib_num += 1 - self.assertEqual(bibe.i_addr, in_addr) - self.assertEqual(bibe.o_addr, out_addr) - self.assertEqual(bibe.i_port, in_port) - self.assertEqual(bibe.o_port, out_port) - self.assertEqual(static_bib_num, 1) - - self.vapi.nat64_add_del_static_bib(in_addr, - out_addr, - in_port, - out_port, - proto, - is_add=0) - bib = self.vapi.nat64_bib_dump(IP_PROTOS.tcp) - static_bib_num = 0 - for bibe in bib: - if bibe.is_static: - static_bib_num += 1 - self.assertEqual(static_bib_num, 0) - - def test_set_timeouts(self): - """ Set NAT64 timeouts """ - # verify default values - timeouts = self.vapi.nat64_get_timeouts() - self.assertEqual(timeouts.udp, 300) - self.assertEqual(timeouts.icmp, 60) - self.assertEqual(timeouts.tcp_trans, 240) - self.assertEqual(timeouts.tcp_est, 7440) - self.assertEqual(timeouts.tcp_incoming_syn, 6) - - # set and verify custom values - self.vapi.nat64_set_timeouts(udp=200, icmp=30, tcp_trans=250, - tcp_est=7450, tcp_incoming_syn=10) - timeouts = self.vapi.nat64_get_timeouts() - self.assertEqual(timeouts.udp, 200) - self.assertEqual(timeouts.icmp, 30) - self.assertEqual(timeouts.tcp_trans, 250) - self.assertEqual(timeouts.tcp_est, 7450) - self.assertEqual(timeouts.tcp_incoming_syn, 10) - - def test_dynamic(self): - """ NAT64 dynamic translation test """ - self.tcp_port_in = 6303 - self.udp_port_in = 6304 - self.icmp_id_in = 6305 - - ses_num_start = self.nat64_get_ses_num() - - self.vapi.nat64_add_del_pool_addr_range(self.nat_addr_n, - self.nat_addr_n) - self.vapi.nat64_add_del_interface(self.pg0.sw_if_index) - self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0) - - # in2out - pkts = self.create_stream_in_ip6(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out(capture, nat_ip=self.nat_addr, - dst_ip=self.pg1.remote_ip4) - - # out2in - pkts = self.create_stream_out(self.pg1, dst_ip=self.nat_addr) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - ip = IPv6(src=''.join(['64:ff9b::', self.pg1.remote_ip4])) - self.verify_capture_in_ip6(capture, ip[IPv6].src, self.pg0.remote_ip6) - - # in2out - pkts = self.create_stream_in_ip6(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out(capture, nat_ip=self.nat_addr, - dst_ip=self.pg1.remote_ip4) - - # out2in - pkts = self.create_stream_out(self.pg1, dst_ip=self.nat_addr) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - self.verify_capture_in_ip6(capture, ip[IPv6].src, self.pg0.remote_ip6) - - ses_num_end = self.nat64_get_ses_num() - - self.assertEqual(ses_num_end - ses_num_start, 3) - - # tenant with specific VRF - self.vapi.nat64_add_del_pool_addr_range(self.vrf1_nat_addr_n, - self.vrf1_nat_addr_n, - vrf_id=self.vrf1_id) - self.vapi.nat64_add_del_interface(self.pg2.sw_if_index) - - pkts = self.create_stream_in_ip6(self.pg2, self.pg1) - self.pg2.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out(capture, nat_ip=self.vrf1_nat_addr, - dst_ip=self.pg1.remote_ip4) - - pkts = self.create_stream_out(self.pg1, dst_ip=self.vrf1_nat_addr) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg2.get_capture(len(pkts)) - self.verify_capture_in_ip6(capture, ip[IPv6].src, self.pg2.remote_ip6) - - def test_static(self): - """ NAT64 static translation test """ - self.tcp_port_in = 60303 - self.udp_port_in = 60304 - self.icmp_id_in = 60305 - self.tcp_port_out = 60303 - self.udp_port_out = 60304 - self.icmp_id_out = 60305 - - ses_num_start = self.nat64_get_ses_num() - - self.vapi.nat64_add_del_pool_addr_range(self.nat_addr_n, - self.nat_addr_n) - self.vapi.nat64_add_del_interface(self.pg0.sw_if_index) - self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0) - - self.vapi.nat64_add_del_static_bib(self.pg0.remote_ip6n, - self.nat_addr_n, - self.tcp_port_in, - self.tcp_port_out, - IP_PROTOS.tcp) - self.vapi.nat64_add_del_static_bib(self.pg0.remote_ip6n, - self.nat_addr_n, - self.udp_port_in, - self.udp_port_out, - IP_PROTOS.udp) - self.vapi.nat64_add_del_static_bib(self.pg0.remote_ip6n, - self.nat_addr_n, - self.icmp_id_in, - self.icmp_id_out, - IP_PROTOS.icmp) - - # in2out - pkts = self.create_stream_in_ip6(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out(capture, nat_ip=self.nat_addr, - dst_ip=self.pg1.remote_ip4, same_port=True) - - # out2in - pkts = self.create_stream_out(self.pg1, dst_ip=self.nat_addr) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - ip = IPv6(src=''.join(['64:ff9b::', self.pg1.remote_ip4])) - self.verify_capture_in_ip6(capture, ip[IPv6].src, self.pg0.remote_ip6) - - ses_num_end = self.nat64_get_ses_num() - - self.assertEqual(ses_num_end - ses_num_start, 3) - - @unittest.skipUnless(running_extended_tests(), "part of extended tests") - def test_session_timeout(self): - """ NAT64 session timeout """ - self.icmp_id_in = 1234 - self.vapi.nat64_add_del_pool_addr_range(self.nat_addr_n, - self.nat_addr_n) - self.vapi.nat64_add_del_interface(self.pg0.sw_if_index) - self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0) - self.vapi.nat64_set_timeouts(icmp=5) - - pkts = self.create_stream_in_ip6(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - - ses_num_before_timeout = self.nat64_get_ses_num() - - sleep(15) - - # ICMP session after timeout - ses_num_after_timeout = self.nat64_get_ses_num() - self.assertNotEqual(ses_num_before_timeout, ses_num_after_timeout) - - def test_icmp_error(self): - """ NAT64 ICMP Error message translation """ - self.tcp_port_in = 6303 - self.udp_port_in = 6304 - self.icmp_id_in = 6305 - - ses_num_start = self.nat64_get_ses_num() - - self.vapi.nat64_add_del_pool_addr_range(self.nat_addr_n, - self.nat_addr_n) - self.vapi.nat64_add_del_interface(self.pg0.sw_if_index) - self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0) - - # send some packets to create sessions - pkts = self.create_stream_in_ip6(self.pg0, self.pg1) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture_ip4 = self.pg1.get_capture(len(pkts)) - self.verify_capture_out(capture_ip4, - nat_ip=self.nat_addr, - dst_ip=self.pg1.remote_ip4) - - pkts = self.create_stream_out(self.pg1, dst_ip=self.nat_addr) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture_ip6 = self.pg0.get_capture(len(pkts)) - ip = IPv6(src=''.join(['64:ff9b::', self.pg1.remote_ip4])) - self.verify_capture_in_ip6(capture_ip6, ip[IPv6].src, - self.pg0.remote_ip6) - - # in2out - pkts = [Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IPv6(src=self.pg0.remote_ip6, dst=ip[IPv6].src) / - ICMPv6DestUnreach(code=1) / - packet[IPv6] for packet in capture_ip6] - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - for packet in capture: - try: - self.assertEqual(packet[IP].src, self.nat_addr) - self.assertEqual(packet[IP].dst, self.pg1.remote_ip4) - self.assertEqual(packet[ICMP].type, 3) - self.assertEqual(packet[ICMP].code, 13) - inner = packet[IPerror] - self.assertEqual(inner.src, self.pg1.remote_ip4) - self.assertEqual(inner.dst, self.nat_addr) - self.check_icmp_checksum(packet) - if inner.haslayer(TCPerror): - self.assertEqual(inner[TCPerror].dport, self.tcp_port_out) - elif inner.haslayer(UDPerror): - self.assertEqual(inner[UDPerror].dport, self.udp_port_out) - else: - self.assertEqual(inner[ICMPerror].id, self.icmp_id_out) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - # out2in - pkts = [Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / - IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / - ICMP(type=3, code=13) / - packet[IP] for packet in capture_ip4] - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - for packet in capture: - try: - self.assertEqual(packet[IPv6].src, ip.src) - self.assertEqual(packet[IPv6].dst, self.pg0.remote_ip6) - icmp = packet[ICMPv6DestUnreach] - self.assertEqual(icmp.code, 1) - inner = icmp[IPerror6] - self.assertEqual(inner.src, self.pg0.remote_ip6) - self.assertEqual(inner.dst, ip.src) - self.check_icmpv6_checksum(packet) - if inner.haslayer(TCPerror): - self.assertEqual(inner[TCPerror].sport, self.tcp_port_in) - elif inner.haslayer(UDPerror): - self.assertEqual(inner[UDPerror].sport, self.udp_port_in) - else: - self.assertEqual(inner[ICMPv6EchoRequest].id, - self.icmp_id_in) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - def test_hairpinning(self): - """ NAT64 hairpinning """ - - client = self.pg0.remote_hosts[0] - server = self.pg0.remote_hosts[1] - server_tcp_in_port = 22 - server_tcp_out_port = 4022 - server_udp_in_port = 23 - server_udp_out_port = 4023 - client_tcp_in_port = 1234 - client_udp_in_port = 1235 - client_tcp_out_port = 0 - client_udp_out_port = 0 - ip = IPv6(src=''.join(['64:ff9b::', self.nat_addr])) - nat_addr_ip6 = ip.src - - self.vapi.nat64_add_del_pool_addr_range(self.nat_addr_n, - self.nat_addr_n) - self.vapi.nat64_add_del_interface(self.pg0.sw_if_index) - self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0) - - self.vapi.nat64_add_del_static_bib(server.ip6n, - self.nat_addr_n, - server_tcp_in_port, - server_tcp_out_port, - IP_PROTOS.tcp) - self.vapi.nat64_add_del_static_bib(server.ip6n, - self.nat_addr_n, - server_udp_in_port, - server_udp_out_port, - IP_PROTOS.udp) - - # client to server - pkts = [] - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IPv6(src=client.ip6, dst=nat_addr_ip6) / - TCP(sport=client_tcp_in_port, dport=server_tcp_out_port)) - pkts.append(p) - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IPv6(src=client.ip6, dst=nat_addr_ip6) / - UDP(sport=client_udp_in_port, dport=server_udp_out_port)) - pkts.append(p) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - for packet in capture: - try: - self.assertEqual(packet[IPv6].src, nat_addr_ip6) - self.assertEqual(packet[IPv6].dst, server.ip6) - if packet.haslayer(TCP): - self.assertNotEqual(packet[TCP].sport, client_tcp_in_port) - self.assertEqual(packet[TCP].dport, server_tcp_in_port) - self.check_tcp_checksum(packet) - client_tcp_out_port = packet[TCP].sport - else: - self.assertNotEqual(packet[UDP].sport, client_udp_in_port) - self.assertEqual(packet[UDP].dport, server_udp_in_port) - self.check_udp_checksum(packet) - client_udp_out_port = packet[UDP].sport - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - # server to client - pkts = [] - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IPv6(src=server.ip6, dst=nat_addr_ip6) / - TCP(sport=server_tcp_in_port, dport=client_tcp_out_port)) - pkts.append(p) - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IPv6(src=server.ip6, dst=nat_addr_ip6) / - UDP(sport=server_udp_in_port, dport=client_udp_out_port)) - pkts.append(p) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - for packet in capture: - try: - self.assertEqual(packet[IPv6].src, nat_addr_ip6) - self.assertEqual(packet[IPv6].dst, client.ip6) - if packet.haslayer(TCP): - self.assertEqual(packet[TCP].sport, server_tcp_out_port) - self.assertEqual(packet[TCP].dport, client_tcp_in_port) - self.check_tcp_checksum(packet) - else: - self.assertEqual(packet[UDP].sport, server_udp_out_port) - self.assertEqual(packet[UDP].dport, client_udp_in_port) - self.check_udp_checksum(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - # ICMP error - pkts = [] - pkts = [Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IPv6(src=client.ip6, dst=nat_addr_ip6) / - ICMPv6DestUnreach(code=1) / - packet[IPv6] for packet in capture] - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - for packet in capture: - try: - self.assertEqual(packet[IPv6].src, nat_addr_ip6) - self.assertEqual(packet[IPv6].dst, server.ip6) - icmp = packet[ICMPv6DestUnreach] - self.assertEqual(icmp.code, 1) - inner = icmp[IPerror6] - self.assertEqual(inner.src, server.ip6) - self.assertEqual(inner.dst, nat_addr_ip6) - self.check_icmpv6_checksum(packet) - if inner.haslayer(TCPerror): - self.assertEqual(inner[TCPerror].sport, server_tcp_in_port) - self.assertEqual(inner[TCPerror].dport, - client_tcp_out_port) - else: - self.assertEqual(inner[UDPerror].sport, server_udp_in_port) - self.assertEqual(inner[UDPerror].dport, - client_udp_out_port) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - def test_prefix(self): - """ NAT64 Network-Specific Prefix """ - - self.vapi.nat64_add_del_pool_addr_range(self.nat_addr_n, - self.nat_addr_n) - self.vapi.nat64_add_del_interface(self.pg0.sw_if_index) - self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0) - self.vapi.nat64_add_del_pool_addr_range(self.vrf1_nat_addr_n, - self.vrf1_nat_addr_n, - vrf_id=self.vrf1_id) - self.vapi.nat64_add_del_interface(self.pg2.sw_if_index) - - # Add global prefix - global_pref64 = "2001:db8::" - global_pref64_n = socket.inet_pton(socket.AF_INET6, global_pref64) - global_pref64_len = 32 - self.vapi.nat64_add_del_prefix(global_pref64_n, global_pref64_len) - - prefix = self.vapi.nat64_prefix_dump() - self.assertEqual(len(prefix), 1) - self.assertEqual(prefix[0].prefix, global_pref64_n) - self.assertEqual(prefix[0].prefix_len, global_pref64_len) - self.assertEqual(prefix[0].vrf_id, 0) - - # Add tenant specific prefix - vrf1_pref64 = "2001:db8:122:300::" - vrf1_pref64_n = socket.inet_pton(socket.AF_INET6, vrf1_pref64) - vrf1_pref64_len = 56 - self.vapi.nat64_add_del_prefix(vrf1_pref64_n, - vrf1_pref64_len, - vrf_id=self.vrf1_id) - prefix = self.vapi.nat64_prefix_dump() - self.assertEqual(len(prefix), 2) - - # Global prefix - pkts = self.create_stream_in_ip6(self.pg0, - self.pg1, - pref=global_pref64, - plen=global_pref64_len) - self.pg0.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out(capture, nat_ip=self.nat_addr, - dst_ip=self.pg1.remote_ip4) - - pkts = self.create_stream_out(self.pg1, dst_ip=self.nat_addr) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg0.get_capture(len(pkts)) - dst_ip = self.compose_ip6(self.pg1.remote_ip4, - global_pref64, - global_pref64_len) - self.verify_capture_in_ip6(capture, dst_ip, self.pg0.remote_ip6) - - # Tenant specific prefix - pkts = self.create_stream_in_ip6(self.pg2, - self.pg1, - pref=vrf1_pref64, - plen=vrf1_pref64_len) - self.pg2.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg1.get_capture(len(pkts)) - self.verify_capture_out(capture, nat_ip=self.vrf1_nat_addr, - dst_ip=self.pg1.remote_ip4) - - pkts = self.create_stream_out(self.pg1, dst_ip=self.vrf1_nat_addr) - self.pg1.add_stream(pkts) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - capture = self.pg2.get_capture(len(pkts)) - dst_ip = self.compose_ip6(self.pg1.remote_ip4, - vrf1_pref64, - vrf1_pref64_len) - self.verify_capture_in_ip6(capture, dst_ip, self.pg2.remote_ip6) - - def test_unknown_proto(self): - """ NAT64 translate packet with unknown protocol """ - - self.vapi.nat64_add_del_pool_addr_range(self.nat_addr_n, - self.nat_addr_n) - self.vapi.nat64_add_del_interface(self.pg0.sw_if_index) - self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0) - remote_ip6 = self.compose_ip6(self.pg1.remote_ip4, '64:ff9b::', 96) - - # in2out - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IPv6(src=self.pg0.remote_ip6, dst=remote_ip6) / - TCP(sport=self.tcp_port_in, dport=20)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg1.get_capture(1) - - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IPv6(src=self.pg0.remote_ip6, dst=remote_ip6, nh=47) / - GRE() / - IP(src=self.pg2.local_ip4, dst=self.pg2.remote_ip4) / - TCP(sport=1234, dport=1234)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg1.get_capture(1) - packet = p[0] - try: - self.assertEqual(packet[IP].src, self.nat_addr) - self.assertEqual(packet[IP].dst, self.pg1.remote_ip4) - self.assertTrue(packet.haslayer(GRE)) - self.check_ip_checksum(packet) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - # out2in - p = (Ether(dst=self.pg1.local_mac, src=self.pg1.remote_mac) / - IP(src=self.pg1.remote_ip4, dst=self.nat_addr) / - GRE() / - IP(src=self.pg2.remote_ip4, dst=self.pg2.local_ip4) / - TCP(sport=1234, dport=1234)) - self.pg1.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg0.get_capture(1) - packet = p[0] - try: - self.assertEqual(packet[IPv6].src, remote_ip6) - self.assertEqual(packet[IPv6].dst, self.pg0.remote_ip6) - self.assertEqual(packet[IPv6].nh, 47) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - def test_hairpinning_unknown_proto(self): - """ NAT64 translate packet with unknown protocol - hairpinning """ - - client = self.pg0.remote_hosts[0] - server = self.pg0.remote_hosts[1] - server_tcp_in_port = 22 - server_tcp_out_port = 4022 - client_tcp_in_port = 1234 - client_tcp_out_port = 1235 - server_nat_ip = "10.0.0.100" - client_nat_ip = "10.0.0.110" - server_nat_ip_n = socket.inet_pton(socket.AF_INET, server_nat_ip) - client_nat_ip_n = socket.inet_pton(socket.AF_INET, client_nat_ip) - server_nat_ip6 = self.compose_ip6(server_nat_ip, '64:ff9b::', 96) - client_nat_ip6 = self.compose_ip6(client_nat_ip, '64:ff9b::', 96) - - self.vapi.nat64_add_del_pool_addr_range(server_nat_ip_n, - client_nat_ip_n) - self.vapi.nat64_add_del_interface(self.pg0.sw_if_index) - self.vapi.nat64_add_del_interface(self.pg1.sw_if_index, is_inside=0) - - self.vapi.nat64_add_del_static_bib(server.ip6n, - server_nat_ip_n, - server_tcp_in_port, - server_tcp_out_port, - IP_PROTOS.tcp) - - self.vapi.nat64_add_del_static_bib(server.ip6n, - server_nat_ip_n, - 0, - 0, - IP_PROTOS.gre) - - self.vapi.nat64_add_del_static_bib(client.ip6n, - client_nat_ip_n, - client_tcp_in_port, - client_tcp_out_port, - IP_PROTOS.tcp) - - # client to server - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IPv6(src=client.ip6, dst=server_nat_ip6) / - TCP(sport=client_tcp_in_port, dport=server_tcp_out_port)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg0.get_capture(1) - - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IPv6(src=client.ip6, dst=server_nat_ip6, nh=IP_PROTOS.gre) / - GRE() / - IP(src=self.pg2.local_ip4, dst=self.pg2.remote_ip4) / - TCP(sport=1234, dport=1234)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg0.get_capture(1) - packet = p[0] - try: - self.assertEqual(packet[IPv6].src, client_nat_ip6) - self.assertEqual(packet[IPv6].dst, server.ip6) - self.assertEqual(packet[IPv6].nh, IP_PROTOS.gre) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - # server to client - p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / - IPv6(src=server.ip6, dst=client_nat_ip6, nh=IP_PROTOS.gre) / - GRE() / - IP(src=self.pg2.remote_ip4, dst=self.pg2.local_ip4) / - TCP(sport=1234, dport=1234)) - self.pg0.add_stream(p) - self.pg_enable_capture(self.pg_interfaces) - self.pg_start() - p = self.pg0.get_capture(1) - packet = p[0] - try: - self.assertEqual(packet[IPv6].src, server_nat_ip6) - self.assertEqual(packet[IPv6].dst, client.ip6) - self.assertEqual(packet[IPv6].nh, IP_PROTOS.gre) - except: - self.logger.error(ppp("Unexpected or invalid packet:", packet)) - raise - - def nat64_get_ses_num(self): - """ - Return number of active NAT64 sessions. - """ - st = self.vapi.nat64_st_dump() - return len(st) - - def clear_nat64(self): - """ - Clear NAT64 configuration. - """ - self.vapi.nat64_set_timeouts() - - interfaces = self.vapi.nat64_interface_dump() - for intf in interfaces: - self.vapi.nat64_add_del_interface(intf.sw_if_index, - intf.is_inside, - is_add=0) - - bib = self.vapi.nat64_bib_dump(IP_PROTOS.tcp) - for bibe in bib: - if bibe.is_static: - self.vapi.nat64_add_del_static_bib(bibe.i_addr, - bibe.o_addr, - bibe.i_port, - bibe.o_port, - bibe.proto, - bibe.vrf_id, - is_add=0) - - bib = self.vapi.nat64_bib_dump(IP_PROTOS.udp) - for bibe in bib: - if bibe.is_static: - self.vapi.nat64_add_del_static_bib(bibe.i_addr, - bibe.o_addr, - bibe.i_port, - bibe.o_port, - bibe.proto, - bibe.vrf_id, - is_add=0) - - bib = self.vapi.nat64_bib_dump(IP_PROTOS.icmp) - for bibe in bib: - if bibe.is_static: - self.vapi.nat64_add_del_static_bib(bibe.i_addr, - bibe.o_addr, - bibe.i_port, - bibe.o_port, - bibe.proto, - bibe.vrf_id, - is_add=0) - - adresses = self.vapi.nat64_pool_addr_dump() - for addr in adresses: - self.vapi.nat64_add_del_pool_addr_range(addr.address, - addr.address, - vrf_id=addr.vrf_id, - is_add=0) - - prefixes = self.vapi.nat64_prefix_dump() - for prefix in prefixes: - self.vapi.nat64_add_del_prefix(prefix.prefix, - prefix.prefix_len, - vrf_id=prefix.vrf_id, - is_add=0) - - def tearDown(self): - super(TestNAT64, self).tearDown() - if not self.vpp_dead: - self.logger.info(self.vapi.cli("show nat64 pool")) - self.logger.info(self.vapi.cli("show nat64 interfaces")) - self.logger.info(self.vapi.cli("show nat64 prefix")) - self.logger.info(self.vapi.cli("show nat64 bib all")) - self.logger.info(self.vapi.cli("show nat64 session table all")) - self.clear_nat64() - -if __name__ == '__main__': - unittest.main(testRunner=VppTestRunner) diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 95de0be6..61db4d6b 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -1061,41 +1061,41 @@ class VppPapiProvider(object): 'mt_next_hop_table_id': next_hop_table_id, 'mt_next_hop_out_label_stack': next_hop_out_label_stack}) - def snat_interface_add_del_feature( + def nat44_interface_add_del_feature( self, sw_if_index, is_inside=1, is_add=1): - """Enable/disable S-NAT feature on the interface + """Enable/disable NAT44 feature on the interface :param sw_if_index: Software index of the interface :param is_inside: 1 if inside, 0 if outside (Default value = 1) :param is_add: 1 if add, 0 if delete (Default value = 1) """ return self.api( - self.papi.snat_interface_add_del_feature, + self.papi.nat44_interface_add_del_feature, {'is_add': is_add, 'is_inside': is_inside, 'sw_if_index': sw_if_index}) - def snat_interface_add_del_output_feature( + def nat44_interface_add_del_output_feature( self, sw_if_index, is_inside=1, is_add=1): - """Enable/disable S-NAT output feature on the interface + """Enable/disable NAT44 output feature on the interface :param sw_if_index: Software index of the interface :param is_inside: 1 if inside, 0 if outside (Default value = 1) :param is_add: 1 if add, 0 if delete (Default value = 1) """ return self.api( - self.papi.snat_interface_add_del_output_feature, + self.papi.nat44_interface_add_del_output_feature, {'is_add': is_add, 'is_inside': is_inside, 'sw_if_index': sw_if_index}) - def snat_add_static_mapping( + def nat44_add_del_static_mapping( self, local_ip, external_ip=0, @@ -1105,9 +1105,8 @@ class VppPapiProvider(object): addr_only=1, vrf_id=0, protocol=0, - is_add=1, - is_ip4=1): - """Add/delete S-NAT static mapping + is_add=1): + """Add/delete NAT44 static mapping :param local_ip: Local IP address :param external_ip: External IP address @@ -1118,12 +1117,10 @@ class VppPapiProvider(object): :param vrf_id: VRF ID :param protocol: IP protocol (Default value = 0) :param is_add: 1 if add, 0 if delete (Default value = 1) - :param is_ip4: 1 if address type is IPv4 (Default value = 1) """ return self.api( - self.papi.snat_add_static_mapping, + self.papi.nat44_add_del_static_mapping, {'is_add': is_add, - 'is_ip4': is_ip4, 'addr_only': addr_only, 'local_ip_address': local_ip, 'external_ip_address': external_ip, @@ -1133,100 +1130,96 @@ class VppPapiProvider(object): 'vrf_id': vrf_id, 'protocol': protocol}) - def snat_add_address_range( + def nat44_add_del_address_range( self, first_ip_address, last_ip_address, is_add=1, - is_ip4=1, vrf_id=0xFFFFFFFF): - """Add/del S-NAT address range + """Add/del NAT44 address range :param first_ip_address: First IP address :param last_ip_address: Last IP address :param vrf_id: VRF id for the address range :param is_add: 1 if add, 0 if delete (Default value = 1) - :param is_ip4: 1 if address type is IPv4 (Default value = 1) """ return self.api( - self.papi.snat_add_address_range, - {'is_ip4': is_ip4, - 'first_ip_address': first_ip_address, + self.papi.nat44_add_del_address_range, + {'first_ip_address': first_ip_address, 'last_ip_address': last_ip_address, 'vrf_id': vrf_id, 'is_add': is_add}) - def snat_address_dump(self): - """Dump S-NAT addresses - :return: Dictionary of S-NAT addresses + def nat44_address_dump(self): + """Dump NAT44 addresses + :return: Dictionary of NAT44 addresses """ - return self.api(self.papi.snat_address_dump, {}) + return self.api(self.papi.nat44_address_dump, {}) - def snat_interface_dump(self): - """Dump interfaces with S-NAT feature - :return: Dictionary of interfaces with S-NAT feature + def nat44_interface_dump(self): + """Dump interfaces with NAT44 feature + :return: Dictionary of interfaces with NAT44 feature """ - return self.api(self.papi.snat_interface_dump, {}) + return self.api(self.papi.nat44_interface_dump, {}) - def snat_interface_output_feature_dump(self): - """Dump interfaces with S-NAT output feature - :return: Dictionary of interfaces with S-NAT output feature + def nat44_interface_output_feature_dump(self): + """Dump interfaces with NAT44 output feature + :return: Dictionary of interfaces with NAT44 output feature """ - return self.api(self.papi.snat_interface_output_feature_dump, {}) + return self.api(self.papi.nat44_interface_output_feature_dump, {}) - def snat_static_mapping_dump(self): - """Dump S-NAT static mappings - :return: Dictionary of S-NAT static mappings + def nat44_static_mapping_dump(self): + """Dump NAT44 static mappings + :return: Dictionary of NAT44 static mappings """ - return self.api(self.papi.snat_static_mapping_dump, {}) + return self.api(self.papi.nat44_static_mapping_dump, {}) - def snat_show_config(self): - """Show S-NAT config - :return: S-NAT config parameters + def nat_show_config(self): + """Show NAT plugin config + :return: NAT plugin config parameters """ - return self.api(self.papi.snat_show_config, {}) + return self.api(self.papi.nat_show_config, {}) - def snat_add_interface_addr( + def nat44_add_interface_addr( self, sw_if_index, is_add=1): - """Add/del S-NAT address from interface + """Add/del NAT44 address from interface :param sw_if_index: Software index of the interface :param is_add: 1 if add, 0 if delete (Default value = 1) """ - return self.api(self.papi.snat_add_del_interface_addr, + return self.api(self.papi.nat44_add_del_interface_addr, {'is_add': is_add, 'sw_if_index': sw_if_index}) - def snat_interface_addr_dump(self): - """Dump S-NAT addresses interfaces - :return: Dictionary of S-NAT addresses interfaces + def nat44_interface_addr_dump(self): + """Dump NAT44 addresses interfaces + :return: Dictionary of NAT44 addresses interfaces """ - return self.api(self.papi.snat_interface_addr_dump, {}) + return self.api(self.papi.nat44_interface_addr_dump, {}) - def snat_ipfix( + def nat_ipfix( self, domain_id=1, src_port=4739, enable=1): - """Enable/disable S-NAT IPFIX logging + """Enable/disable NAT IPFIX logging :param domain_id: Observation domain ID (Default value = 1) :param src_port: Source port number (Default value = 4739) :param enable: 1 if enable, 0 if disable (Default value = 1) """ return self.api( - self.papi.snat_ipfix_enable_disable, + self.papi.nat_ipfix_enable_disable, {'domain_id': domain_id, 'src_port': src_port, 'enable': enable}) - def snat_user_session_dump( + def nat44_user_session_dump( self, ip_address, - vrf_id, - is_ip4=1): - """Dump S-NAT user's sessions + vrf_id): + """Dump NAT44 user's sessions :param ip_address: ip adress of the user to be dumped :param cpu_index: cpu_index on which the user is @@ -1234,26 +1227,25 @@ class VppPapiProvider(object): :return: Dictionary of S-NAT sessions """ return self.api( - self.papi.snat_user_session_dump, + self.papi.nat44_user_session_dump, {'ip_address': ip_address, - 'vrf_id': vrf_id, - 'is_ip4': is_ip4}) + 'vrf_id': vrf_id}) - def snat_user_dump(self): - """Dump S-NAT users + def nat44_user_dump(self): + """Dump NAT44 users - :return: Dictionary of S-NAT users + :return: Dictionary of NAT44 users """ - return self.api(self.papi.snat_user_dump, {}) + return self.api(self.papi.nat44_user_dump, {}) - def snat_add_det_map( + def nat_det_add_del_map( self, in_addr, in_plen, out_addr, out_plen, is_add=1): - """Add/delete S-NAT deterministic mapping + """Add/delete deterministic NAT mapping :param is_add - 1 if add, 0 if delete :param in_addr - inside IP address @@ -1262,14 +1254,15 @@ class VppPapiProvider(object): :param out_plen - outside IP address prefix length """ return self.api( - self.papi.snat_add_det_map, + self.papi.nat_det_add_del_map, {'is_add': is_add, + 'is_nat44': 1, 'in_addr': in_addr, 'in_plen': in_plen, 'out_addr': out_addr, 'out_plen': out_plen}) - def snat_det_forward( + def nat_det_forward( self, in_addr): """Get outside address and port range from inside address @@ -1277,10 +1270,11 @@ class VppPapiProvider(object): :param in_addr - inside IP address """ return self.api( - self.papi.snat_det_forward, - {'in_addr': in_addr}) + self.papi.nat_det_forward, + {'in_addr': in_addr, + 'is_nat44': 1}) - def snat_det_reverse( + def nat_det_reverse( self, out_addr, out_port): @@ -1290,18 +1284,18 @@ class VppPapiProvider(object): :param out_port - outside port """ return self.api( - self.papi.snat_det_reverse, + self.papi.nat_det_reverse, {'out_addr': out_addr, 'out_port': out_port}) - def snat_det_map_dump(self): - """Dump S-NAT deterministic mappings + def nat_det_map_dump(self): + """Dump deterministic NAT mappings - :return: Dictionary of S-NAT deterministic mappings + :return: Dictionary of deterministic NAT mappings """ - return self.api(self.papi.snat_det_map_dump, {}) + return self.api(self.papi.nat_det_map_dump, {}) - def snat_det_set_timeouts( + def nat_det_set_timeouts( self, udp=300, tcp_established=7440, @@ -1315,78 +1309,71 @@ class VppPapiProvider(object): :param icmp - ICMP timeout (Default value = 60) """ return self.api( - self.papi.snat_det_set_timeouts, + self.papi.nat_det_set_timeouts, {'udp': udp, 'tcp_established': tcp_established, 'tcp_transitory': tcp_transitory, 'icmp': icmp}) - def snat_det_get_timeouts(self): + def nat_det_get_timeouts(self): """Get values of timeouts for deterministic NAT :return: Timeouts for deterministic NAT (in seconds) """ - return self.api(self.papi.snat_det_get_timeouts, {}) + return self.api(self.papi.nat_det_get_timeouts, {}) - def snat_det_close_session_out( + def nat_det_close_session_out( self, out_addr, out_port, ext_addr, - ext_port, - is_ip4=1): - """Close CGN session using outside address and port + ext_port): + """Close deterministic NAT session using outside address and port :param out_addr - outside IP address :param out_port - outside port :param ext_addr - external host IP address :param ext_port - external host port - :param is_ip4: 1 if address type is IPv4 (Default value = 1) """ return self.api( - self.papi.snat_det_close_session_out, + self.papi.nat_det_close_session_out, {'out_addr': out_addr, 'out_port': out_port, 'ext_addr': ext_addr, - 'ext_port': ext_port, - 'is_ip4': is_ip4}) + 'ext_port': ext_port}) - def snat_det_close_session_in( + def nat_det_close_session_in( self, in_addr, in_port, ext_addr, - ext_port, - is_ip4=1): - """Close CGN session using inside address and port + ext_port): + """Close deterministic NAT session using inside address and port :param in_addr - inside IP address :param in_port - inside port :param ext_addr - external host IP address :param ext_port - external host port - :param is_ip4: 1 if address type is IPv4 (Default value = 1) """ return self.api( - self.papi.snat_det_close_session_in, + self.papi.nat_det_close_session_in, {'in_addr': in_addr, 'in_port': in_port, 'ext_addr': ext_addr, 'ext_port': ext_port, - 'is_ip4': is_ip4}) + 'is_nat44': 1}) - def snat_det_session_dump( + def nat_det_session_dump( self, - user_addr, - is_ip4=1): - """Dump S-NAT deterministic sessions belonging to a user + user_addr): + """Dump deterministic NAT sessions belonging to a user :param user_addr - inside IP address of the user - :param is_ip4: - 1 if address type is IPv4 (Default value = 1) - :return: Dictionary of S-NAT deterministic sessions + :return: Dictionary of deterministic NAT sessions """ return self.api( - self.papi.snat_det_session_dump, - {'is_ip4': is_ip4, + self.papi.nat_det_session_dump, + {'is_nat44': 1, 'user_addr': user_addr}) def nat64_add_del_pool_addr_range( -- cgit 1.2.3-korg From 5c04ad09603f40f071e4b4ad79add8d2619977bf Mon Sep 17 00:00:00 2001 From: Marek Gradzki Date: Thu, 24 Aug 2017 08:57:43 +0200 Subject: jvpp: use (*env)->ExceptionClear before calling (*env)->ExceptionOccurred Change-Id: I6cca455ead986cb8a507c84957a97a40b733b16c Signed-off-by: Marek Gradzki --- src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/vpp-api/java') diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py index 7e59cc4c..8761eb13 100644 --- a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py +++ b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py @@ -227,10 +227,13 @@ static void vl_api_${handler_name}_t_handler (vl_api_${handler_name}_t * mp) ${plugin_name}_main_t *plugin_main = &${plugin_name}_main; JNIEnv *env = jvpp_main.jenv; jthrowable exc; - $err_handler jmethodID constructor = (*env)->GetMethodID(env, ${class_ref_name}Class, "", "()V"); + + // User does not have to provide callbacks for all VPP messages. + // We are ignoring messages that are not supported by user. + (*env)->ExceptionClear(env); // just in case exception occurred in different place and was not properly cleared jmethodID callbackMethod = (*env)->GetMethodID(env, plugin_main->callbackClass, "on${dto_name}", "(Lio/fd/vpp/jvpp/${plugin_name}/dto/${dto_name};)V"); exc = (*env)->ExceptionOccurred(env); if (exc) { -- cgit 1.2.3-korg From d135c19a1fde609b82be5a30413d6b9ab43811e3 Mon Sep 17 00:00:00 2001 From: Matej Perina Date: Tue, 18 Jul 2017 13:59:41 +0200 Subject: jvpp: introducing callback api and future api tests for all plugins (VPP-591) test can be run with: make test TEST=test_jvpp memory_shared.c: declaring and assigning variable in if statement makes it usage outside statement impossible. Looks like memory space assigned to variable declared in statement is freed when statement ends svm.c: - fixed case when root path can have a "/" at beggining - added option for test to operate over shared memory space with /vpe-api name and not create new one with name consisting of root path and region name which would require root permisions Change-Id: Iff1170dc6a5c1be134c152f2757c7ab9b919a8ed Signed-off-by: Matej Perina --- src/svm/svm.c | 2 +- .../io/fd/vpp/jvpp/acl/test/CallbackApiTest.java | 33 +++++++++ .../io/fd/vpp/jvpp/acl/test/FutureApiTest.java | 62 ++++++++++++++++ .../jvpp-acl/io/fd/vpp/jvpp/acl/test/Readme.txt | 4 ++ .../io/fd/vpp/jvpp/core/test/CallbackApiTest.java | 33 +++++++++ .../io/fd/vpp/jvpp/core/test/FutureApiTest.java | 79 +++++++++++++++++++++ .../jvpp-core/io/fd/vpp/jvpp/core/test/Readme.txt | 18 +++++ .../vpp/jvpp/ioamexport/test/CallbackApiTest.java | 33 +++++++++ .../fd/vpp/jvpp/ioamexport/test/FutureApiTest.java | 60 ++++++++++++++++ .../io/fd/vpp/jvpp/ioamexport/test/Readme.txt | 4 ++ .../fd/vpp/jvpp/ioampot/test/CallbackApiTest.java | 33 +++++++++ .../io/fd/vpp/jvpp/ioampot/test/FutureApiTest.java | 66 +++++++++++++++++ .../io/fd/vpp/jvpp/ioampot/test/Readme.txt | 4 ++ .../vpp/jvpp/ioamtrace/test/CallbackApiTest.java | 33 +++++++++ .../fd/vpp/jvpp/ioamtrace/test/FutureApiTest.java | 60 ++++++++++++++++ .../io/fd/vpp/jvpp/ioamtrace/test/Readme.txt | 4 ++ .../io/fd/vpp/jvpp/nat/test/CallbackApiTest.java | 33 +++++++++ .../io/fd/vpp/jvpp/nat/test/FutureApiTest.java | 66 +++++++++++++++++ .../jvpp-nat/io/fd/vpp/jvpp/nat/test/Readme.txt | 4 ++ .../io/fd/vpp/jvpp/AbstractCallbackApiTest.java | 63 +++++++++++++++++ .../jvpp-registry/io/fd/vpp/jvpp/Assertions.java | 32 +++++++++ test/jvpp_connection.py | 54 ++++++++++++++ test/test_jvpp.py | 82 ++++++++++++++++++++++ 23 files changed, 861 insertions(+), 1 deletion(-) create mode 100644 src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/CallbackApiTest.java create mode 100644 src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/FutureApiTest.java create mode 100644 src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/Readme.txt create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackApiTest.java create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java create mode 100644 src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/Readme.txt create mode 100644 src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/CallbackApiTest.java create mode 100644 src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/FutureApiTest.java create mode 100644 src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/Readme.txt create mode 100644 src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/CallbackApiTest.java create mode 100644 src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/FutureApiTest.java create mode 100644 src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/Readme.txt create mode 100644 src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/CallbackApiTest.java create mode 100644 src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/FutureApiTest.java create mode 100644 src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/Readme.txt create mode 100644 src/vpp-api/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/CallbackApiTest.java create mode 100644 src/vpp-api/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/FutureApiTest.java create mode 100644 src/vpp-api/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/Readme.txt create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/AbstractCallbackApiTest.java create mode 100644 src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/Assertions.java create mode 100644 test/jvpp_connection.py create mode 100644 test/test_jvpp.py (limited to 'src/vpp-api/java') diff --git a/src/svm/svm.c b/src/svm/svm.c index 663324e0..14c5bd9b 100644 --- a/src/svm/svm.c +++ b/src/svm/svm.c @@ -426,7 +426,7 @@ shm_name_from_svm_map_region_args (svm_map_region_args_t * a) if (a->name[0] == '/') name_offset = 1; - shm_name = format (0, "/%s-%s%c", a->root_path, + shm_name = format (0, "/%s-%s%c", &a->root_path[root_path_offset], &a->name[name_offset], 0); } else diff --git a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/CallbackApiTest.java b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/CallbackApiTest.java new file mode 100644 index 00000000..a7bbb7f4 --- /dev/null +++ b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/CallbackApiTest.java @@ -0,0 +1,33 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.acl.test; + +import io.fd.vpp.jvpp.AbstractCallbackApiTest; +import io.fd.vpp.jvpp.acl.JVppAclImpl; + +import java.util.logging.Logger; + +public class CallbackApiTest extends AbstractCallbackApiTest { + + private static Logger LOG = Logger.getLogger(CallbackApiTest.class.getName()); + + + public static void main(String[] args) throws Exception { + LOG.info("Testing ControlPing using Java callback API for core plugin"); + testControlPing(args[0], new JVppAclImpl()); + } +} diff --git a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/FutureApiTest.java b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/FutureApiTest.java new file mode 100644 index 00000000..ff1c73c4 --- /dev/null +++ b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/FutureApiTest.java @@ -0,0 +1,62 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.acl.test; + +import io.fd.vpp.jvpp.Assertions; +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.acl.JVppAclImpl; +import io.fd.vpp.jvpp.acl.dto.AclDetailsReplyDump; +import io.fd.vpp.jvpp.acl.dto.AclDump; +import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; + +import java.util.concurrent.CompletableFuture; +import java.util.logging.Logger; + +public class FutureApiTest { + + private static final Logger LOG = Logger.getLogger(FutureApiTest.class.getName()); + + public static void main(String[] args) throws Exception { + testFutureApi(args); + } + + private static void testFutureApi(String[] args) throws Exception { + LOG.info("Testing Java future API for core plugin"); + try (final JVppRegistry registry = new JVppRegistryImpl("FutureApiTest", args[0]); + final FutureJVppAclFacade jvppFacade = new FutureJVppAclFacade(registry, new JVppAclImpl())) { + LOG.info("Successfully connected to VPP"); + + testAclDump(jvppFacade); + + LOG.info("Disconnecting..."); + } + } + + private static void testAclDump(final FutureJVppAclFacade jvpp) throws Exception { + LOG.info("Sending AclDump request..."); + final AclDump request = new AclDump(); + + final CompletableFuture + replyFuture = jvpp.aclDump(request).toCompletableFuture(); + final AclDetailsReplyDump reply = replyFuture.get(); + + Assertions.assertNotNull(reply); + } + + +} diff --git a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/Readme.txt b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/Readme.txt new file mode 100644 index 00000000..1b465851 --- /dev/null +++ b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/test/Readme.txt @@ -0,0 +1,4 @@ +release version: +sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp-native/vpp/vpp-api/java/jvpp-acl-17.10.jar io.fd.vpp.jvpp.acl.test.[test-name] +debug version: +sudo java -cp build-vpp_debug-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-acl-17.10.jar io.fd.vpp.jvpp.acl.test.[test-name] diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackApiTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackApiTest.java new file mode 100644 index 00000000..493116c8 --- /dev/null +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/CallbackApiTest.java @@ -0,0 +1,33 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.core.test; + +import io.fd.vpp.jvpp.AbstractCallbackApiTest; +import io.fd.vpp.jvpp.core.JVppCoreImpl; + +import java.util.logging.Logger; + +public class CallbackApiTest extends AbstractCallbackApiTest { + + private static Logger LOG = Logger.getLogger(CallbackApiTest.class.getName()); + + + public static void main(String[] args) throws Exception { + LOG.info("Testing ControlPing using Java callback API for core plugin"); + testControlPing(args[0], new JVppCoreImpl()); + } +} diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java new file mode 100644 index 00000000..d3acecc2 --- /dev/null +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/FutureApiTest.java @@ -0,0 +1,79 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.core.test; + +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.core.JVppCoreImpl; +import io.fd.vpp.jvpp.core.dto.BridgeDomainDetailsReplyDump; +import io.fd.vpp.jvpp.core.dto.BridgeDomainDump; +import io.fd.vpp.jvpp.core.dto.GetNodeIndex; +import io.fd.vpp.jvpp.core.dto.GetNodeIndexReply; +import io.fd.vpp.jvpp.core.dto.ShowVersion; +import io.fd.vpp.jvpp.core.dto.ShowVersionReply; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDetails; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDetailsReplyDump; +import io.fd.vpp.jvpp.core.dto.SwInterfaceDump; +import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade; +import java.nio.charset.StandardCharsets; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class FutureApiTest { + + private static final Logger LOG = Logger.getLogger(FutureApiTest.class.getName()); + + public static void main(String[] args) throws Exception { + testFutureApi(args); + } + + private static void testFutureApi(String[] args) throws Exception { + LOG.info("Testing Java future API for core plugin"); + try (final JVppRegistry registry = new JVppRegistryImpl("FutureApiTest", args[0]); + final FutureJVppCoreFacade jvppFacade = new FutureJVppCoreFacade(registry, new JVppCoreImpl())) { + LOG.info("Successfully connected to VPP"); + + testEmptyBridgeDomainDump(jvppFacade); + + LOG.info("Disconnecting..."); + } + } + + private static void testEmptyBridgeDomainDump(final FutureJVppCoreFacade jvpp) throws Exception { + LOG.info("Sending BridgeDomainDump request..."); + final BridgeDomainDump request = new BridgeDomainDump(); + request.bdId = -1; // dump call + + final CompletableFuture + replyFuture = jvpp.bridgeDomainDump(request).toCompletableFuture(); + final BridgeDomainDetailsReplyDump reply = replyFuture.get(); + + if (reply == null || reply.bridgeDomainDetails == null) { + throw new IllegalStateException("Received null response for empty dump: " + reply); + } else { + LOG.info( + String.format( + "Received bridge-domain dump reply with list of bridge-domains: %s", + reply.bridgeDomainDetails)); + } + } + + +} diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/Readme.txt b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/Readme.txt new file mode 100644 index 00000000..b74cf60a --- /dev/null +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/Readme.txt @@ -0,0 +1,18 @@ +This package contains basic tests for jvpp. To run the tests: + +- Make sure VPP is running +- From VPP's build-root/ folder execute: + - release version: sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp-native/vpp/vpp-api/java/jvpp-core-17.10.jar io.fd.vpp.jvpp.core.test.[test name] + - debug version: sudo java -cp build-vpp_debug-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-core-17.10.jar io.fd.vpp.jvpp.core.test.[test name] + +Available tests: +CallbackApiTest - Similar to ControlPingTest, invokes more complex calls (e.g. interface dump) using low level JVpp APIs +CallbackJVppFacadeNotificationTest - Tests interface notifications using Callback based JVpp facade +CallbackJVppFacadeTest - Execution of more complex calls using Callback based JVpp facade +CallbackNotificationApiTest - Tests interface notifications using low level JVpp APIs +ControlPingTest - Simple test executing a single control ping using low level JVpp APIs +CreateSubInterfaceTest - Tests sub-interface creation +FutureApiNotificationTest - Tests interface notifications using Future based JVpp facade +FutureApiTest - Execution of more complex calls using Future based JVpp facade +L2AclTest - Tests L2 ACL creation +LispAdjacencyTest - Tests lisp adjacency creation and read (custom vpe.api type support showcase) diff --git a/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/CallbackApiTest.java b/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/CallbackApiTest.java new file mode 100644 index 00000000..ba49d77d --- /dev/null +++ b/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/CallbackApiTest.java @@ -0,0 +1,33 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.ioamexport.test; + +import io.fd.vpp.jvpp.AbstractCallbackApiTest; +import io.fd.vpp.jvpp.ioamexport.JVppIoamexportImpl; + +import java.util.logging.Logger; + + +public class CallbackApiTest extends AbstractCallbackApiTest { + + private static Logger LOG = Logger.getLogger(CallbackApiTest.class.getName()); + + public static void main(String[] args) throws Exception { + LOG.info("Testing ControlPing using Java callback API for ioamexport plugin"); + testControlPing(args[0], new JVppIoamexportImpl()); + } +} diff --git a/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/FutureApiTest.java b/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/FutureApiTest.java new file mode 100644 index 00000000..048d2445 --- /dev/null +++ b/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/FutureApiTest.java @@ -0,0 +1,60 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.ioamexport.test; + + +import io.fd.vpp.jvpp.Assertions; +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.ioamexport.JVppIoamexportImpl; +import io.fd.vpp.jvpp.ioamexport.dto.IoamExportIp6EnableDisable; +import io.fd.vpp.jvpp.ioamexport.dto.IoamExportIp6EnableDisableReply; +import io.fd.vpp.jvpp.ioamexport.future.FutureJVppIoamexportFacade; + +import java.util.concurrent.Future; +import java.util.logging.Logger; + +public class FutureApiTest { + + private static final Logger LOG = Logger.getLogger(FutureApiTest.class.getName()); + + public static void main(String[] args) throws Exception { + testCallbackApi(args); + } + + private static void testCallbackApi(String[] args) throws Exception { + LOG.info("Testing Java callback API for ioamexport plugin"); + try (final JVppRegistry registry = new JVppRegistryImpl("FutureApiTest", args[0]); + final FutureJVppIoamexportFacade jvpp = new FutureJVppIoamexportFacade(registry, new JVppIoamexportImpl())) { + LOG.info("Successfully connected to VPP"); + + testIoamExportIp6EnableDisable(jvpp); + + LOG.info("Disconnecting..."); + } + } + + private static void testIoamExportIp6EnableDisable(FutureJVppIoamexportFacade jvpp) throws Exception { + LOG.info("Sending IoamExportIp6EnableDisable request..."); + final IoamExportIp6EnableDisable request = new IoamExportIp6EnableDisable(); + + final Future replyFuture = jvpp.ioamExportIp6EnableDisable(request).toCompletableFuture(); + final IoamExportIp6EnableDisableReply reply = replyFuture.get(); + + Assertions.assertNotNull(reply); + } +} diff --git a/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/Readme.txt b/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/Readme.txt new file mode 100644 index 00000000..820071a8 --- /dev/null +++ b/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/test/Readme.txt @@ -0,0 +1,4 @@ +release version: +sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-ioamexport-17.10.jar io.fd.vpp.jvpp.ioamexport.test.[test-name] +debug version: +sudo java -cp build-vpp_debug-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-ioamexport-17.10.jar io.fd.vpp.jvpp.ioamexport.test.[test-name] diff --git a/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/CallbackApiTest.java b/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/CallbackApiTest.java new file mode 100644 index 00000000..20b85d89 --- /dev/null +++ b/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/CallbackApiTest.java @@ -0,0 +1,33 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.ioampot.test; + +import io.fd.vpp.jvpp.AbstractCallbackApiTest; +import io.fd.vpp.jvpp.ioampot.JVppIoampotImpl; + +import java.util.logging.Logger; + +public class CallbackApiTest extends AbstractCallbackApiTest { + + private static Logger LOG = Logger.getLogger(CallbackApiTest.class.getName()); + + + public static void main(String[] args) throws Exception { + LOG.info("Testing ControlPing using Java callback API for ioampot plugin"); + testControlPing(args[0], new JVppIoampotImpl()); + } +} diff --git a/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/FutureApiTest.java b/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/FutureApiTest.java new file mode 100644 index 00000000..6401c678 --- /dev/null +++ b/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/FutureApiTest.java @@ -0,0 +1,66 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.ioampot.test; + + +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.ioampot.JVppIoampotImpl; +import io.fd.vpp.jvpp.ioampot.dto.PotProfileShowConfigDetailsReplyDump; +import io.fd.vpp.jvpp.ioampot.dto.PotProfileShowConfigDump; +import io.fd.vpp.jvpp.ioampot.future.FutureJVppIoampotFacade; + +import java.util.concurrent.Future; +import java.util.logging.Logger; + +public class FutureApiTest { + + private static final Logger LOG = Logger.getLogger(io.fd.vpp.jvpp.ioampot.test.FutureApiTest.class.getName()); + + public static void main(String[] args) throws Exception { + testCallbackApi(args); + } + + private static void testCallbackApi(String[] args) throws Exception { + LOG.info("Testing Java callback API for ioampot plugin"); + try (final JVppRegistry registry = new JVppRegistryImpl("FutureApiTest", args[0]); + final FutureJVppIoampotFacade jvpp = new FutureJVppIoampotFacade(registry, new JVppIoampotImpl())) { + LOG.info("Successfully connected to VPP"); + + testPotProfileShowConfigDump(jvpp); + + LOG.info("Disconnecting..."); + } + } + + private static void testPotProfileShowConfigDump(FutureJVppIoampotFacade jvpp) throws Exception { + LOG.info("Sending PotProfileShowConfigDump request..."); + final PotProfileShowConfigDump request = new PotProfileShowConfigDump(); + + final Future replyFuture = jvpp.potProfileShowConfigDump(request).toCompletableFuture(); + final PotProfileShowConfigDetailsReplyDump reply = replyFuture.get(); + + if (reply == null || reply.potProfileShowConfigDetails == null) { + throw new IllegalStateException("Received null response for empty dump: " + reply); + } else { + LOG.info( + String.format( + "Received pot profile show config dump reply: %s", + reply.potProfileShowConfigDetails)); + } + } +} diff --git a/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/Readme.txt b/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/Readme.txt new file mode 100644 index 00000000..f3cae262 --- /dev/null +++ b/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/test/Readme.txt @@ -0,0 +1,4 @@ +release version: +sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-ioampot-17.10.jar io.fd.vpp.jvpp.ioampot.test.[test-name] +debug version: +sudo java -cp build-vpp_debug-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-ioampot-17.10.jar io.fd.vpp.jvpp.ioampot.test.[test-name] diff --git a/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/CallbackApiTest.java b/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/CallbackApiTest.java new file mode 100644 index 00000000..4a71db52 --- /dev/null +++ b/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/CallbackApiTest.java @@ -0,0 +1,33 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.ioamtrace.test; + +import io.fd.vpp.jvpp.AbstractCallbackApiTest; +import io.fd.vpp.jvpp.ioamtrace.JVppIoamtraceImpl; + +import java.util.logging.Logger; + +public class CallbackApiTest extends AbstractCallbackApiTest { + + private static Logger LOG = Logger.getLogger(CallbackApiTest.class.getName()); + + + public static void main(String[] args) throws Exception { + LOG.info("Testing ControlPing using Java callback API for ioamtrace plugin"); + testControlPing(args[0], new JVppIoamtraceImpl()); + } +} diff --git a/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/FutureApiTest.java b/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/FutureApiTest.java new file mode 100644 index 00000000..4e13ed1f --- /dev/null +++ b/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/FutureApiTest.java @@ -0,0 +1,60 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.ioamtrace.test; + + +import io.fd.vpp.jvpp.Assertions; +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.ioamtrace.JVppIoamtraceImpl; +import io.fd.vpp.jvpp.ioamtrace.dto.TraceProfileShowConfig; +import io.fd.vpp.jvpp.ioamtrace.dto.TraceProfileShowConfigReply; +import io.fd.vpp.jvpp.ioamtrace.future.FutureJVppIoamtraceFacade; + +import java.util.concurrent.Future; +import java.util.logging.Logger; + +public class FutureApiTest { + + private static final Logger LOG = Logger.getLogger(io.fd.vpp.jvpp.ioamtrace.test.FutureApiTest.class.getName()); + + public static void main(String[] args) throws Exception { + testCallbackApi(args); + } + + private static void testCallbackApi(String[] args) throws Exception { + LOG.info("Testing Java callback API for ioamtrace plugin"); + try (final JVppRegistry registry = new JVppRegistryImpl("FutureApiTest", args[0]); + final FutureJVppIoamtraceFacade jvpp = new FutureJVppIoamtraceFacade(registry, new JVppIoamtraceImpl())) { + LOG.info("Successfully connected to VPP"); + + testTraceProfileShowConfig(jvpp); + + LOG.info("Disconnecting..."); + } + } + + private static void testTraceProfileShowConfig(FutureJVppIoamtraceFacade jvpp) throws Exception { + LOG.info("Sending TraceProfileShowConfig request..."); + final TraceProfileShowConfig request = new TraceProfileShowConfig(); + + final Future replyFuture = jvpp.traceProfileShowConfig(request).toCompletableFuture(); + final TraceProfileShowConfigReply reply = replyFuture.get(); + + Assertions.assertNotNull(reply); + } +} diff --git a/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/Readme.txt b/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/Readme.txt new file mode 100644 index 00000000..9a1ba829 --- /dev/null +++ b/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/test/Readme.txt @@ -0,0 +1,4 @@ +release version: +sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-ioamtrace-17.10.jar io.fd.vpp.jvpp.ioamtrace.test.[test-name] +debug version: +sudo java -cp build-vpp_debug-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-ioamtrace-17.10.jar io.fd.vpp.jvpp.ioamtrace.test.[test-name] diff --git a/src/vpp-api/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/CallbackApiTest.java b/src/vpp-api/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/CallbackApiTest.java new file mode 100644 index 00000000..a6f82148 --- /dev/null +++ b/src/vpp-api/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/CallbackApiTest.java @@ -0,0 +1,33 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.nat.test; + +import io.fd.vpp.jvpp.AbstractCallbackApiTest; +import io.fd.vpp.jvpp.nat.JVppNatImpl; + +import java.util.logging.Logger; + +public class CallbackApiTest extends AbstractCallbackApiTest { + + private static Logger LOG = Logger.getLogger(CallbackApiTest.class.getName()); + + + public static void main(String[] args) throws Exception { + LOG.info("Testing ControlPing using Java callback API for core plugin"); + testControlPing(args[0], new JVppNatImpl()); + } +} diff --git a/src/vpp-api/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/FutureApiTest.java b/src/vpp-api/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/FutureApiTest.java new file mode 100644 index 00000000..8643dcf4 --- /dev/null +++ b/src/vpp-api/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/FutureApiTest.java @@ -0,0 +1,66 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp.nat.test; + + +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.JVppRegistryImpl; +import io.fd.vpp.jvpp.nat.JVppNatImpl; +import io.fd.vpp.jvpp.nat.dto.SnatAddressDetailsReplyDump; +import io.fd.vpp.jvpp.nat.dto.SnatAddressDump; +import io.fd.vpp.jvpp.nat.future.FutureJVppNatFacade; + +import java.util.concurrent.Future; +import java.util.logging.Logger; + +public class FutureApiTest { + + private static final Logger LOG = Logger.getLogger(io.fd.vpp.jvpp.nat.test.FutureApiTest.class.getName()); + + public static void main(String[] args) throws Exception { + testCallbackApi(args); + } + + private static void testCallbackApi(String[] args) throws Exception { + LOG.info("Testing Java callback API for snat plugin"); + try (final JVppRegistry registry = new JVppRegistryImpl("FutureApiTest", args[0]); + final FutureJVppNatFacade jvpp = new FutureJVppNatFacade(registry, new JVppNatImpl())) { + LOG.info("Successfully connected to VPP"); + + testAclDump(jvpp); + + LOG.info("Disconnecting..."); + } + } + + private static void testAclDump(FutureJVppNatFacade jvpp) throws Exception { + LOG.info("Sending SnatAddressDump request..."); + final SnatAddressDump request = new SnatAddressDump(); + + final Future replyFuture = jvpp.snatAddressDump(request).toCompletableFuture(); + final SnatAddressDetailsReplyDump reply = replyFuture.get(); + + if (reply == null || reply.snatAddressDetails == null) { + throw new IllegalStateException("Received null response for empty dump: " + reply); + } else { + LOG.info( + String.format( + "Received snat address dump reply with list of snat address: %s", + reply.snatAddressDetails)); + } + } +} diff --git a/src/vpp-api/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/Readme.txt b/src/vpp-api/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/Readme.txt new file mode 100644 index 00000000..6f758089 --- /dev/null +++ b/src/vpp-api/java/jvpp-nat/io/fd/vpp/jvpp/nat/test/Readme.txt @@ -0,0 +1,4 @@ +release version: +sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-nat-17.10.jar io.fd.vpp.jvpp.nat.test.[test-name] +debug version: +sudo java -cp build-vpp_debug-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-nat-17.10.jar io.fd.vpp.jvpp.nat.test.[test-name] diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/AbstractCallbackApiTest.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/AbstractCallbackApiTest.java new file mode 100644 index 00000000..d221d1e0 --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/AbstractCallbackApiTest.java @@ -0,0 +1,63 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp; + +import io.fd.vpp.jvpp.callback.ControlPingCallback; +import io.fd.vpp.jvpp.dto.ControlPing; +import io.fd.vpp.jvpp.dto.ControlPingReply; + +public abstract class AbstractCallbackApiTest { + + private static int receivedPingCount = 0; + private static int errorPingCount = 0; + + public static void testControlPing(String shm_prefix, JVpp jvpp) throws Exception { + try (JVppRegistry registry = new JVppRegistryImpl("CallbackApiTest", shm_prefix)) { + + registry.register(jvpp, new ControlPingCallback() { + @Override + public void onControlPingReply(final ControlPingReply reply) { + System.out.printf("Received ControlPingReply: %s%n", reply); + receivedPingCount++; + } + + @Override + public void onError(VppCallbackException ex) { + System.out.printf("Received onError exception: call=%s, reply=%d, context=%d ", ex.getMethodName(), + ex.getErrorCode(), ex.getCtxId()); + errorPingCount++; + } + + }); + System.out.println("Successfully connected to VPP"); + Thread.sleep(1000); + + System.out.println("Sending control ping using JVppRegistry"); + registry.controlPing(jvpp.getClass()); + + Thread.sleep(2000); + + System.out.println("Sending control ping using JVpp plugin"); + jvpp.send(new ControlPing()); + + Thread.sleep(2000); + System.out.println("Disconnecting..."); + Assertions.assertEquals(2, receivedPingCount); + Assertions.assertEquals(0, errorPingCount); + } + } +} diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/Assertions.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/Assertions.java new file mode 100644 index 00000000..f8b591f5 --- /dev/null +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/Assertions.java @@ -0,0 +1,32 @@ +/* + * 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. + */ + +package io.fd.vpp.jvpp; + +public class Assertions { + + public static void assertEquals(final int expected, final int actual) { + if (expected != actual) { + throw new IllegalArgumentException(String.format("Expected[%s]/Actual[%s]", expected, actual)); + } + } + + public static void assertNotNull(final Object value) { + if (value == null) { + throw new IllegalArgumentException("Variable is null"); + } + } +} diff --git a/test/jvpp_connection.py b/test/jvpp_connection.py new file mode 100644 index 00000000..bb48745c --- /dev/null +++ b/test/jvpp_connection.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +import os +import subprocess +from vpp_papi_provider import VppPapiProvider +from threading import Timer + +from framework import VppTestCase + +# Api files path +API_FILES_PATH = "vpp/vpp-api/java" + +# Registry jar file name prefix +REGISTRY_JAR_PREFIX = "jvpp-registry" + + +class TestJVppConnection(VppTestCase): + + def full_jar_name(self, install_dir, jar_name, version): + return os.path.join(install_dir, API_FILES_PATH, + "{0}-{1}.jar".format(jar_name, version)) + + def jvpp_connection_test(self, api_jar_name, test_class_name, timeout): + install_dir = os.getenv('VPP_TEST_BUILD_DIR') + print("Install directory : {0}".format(install_dir)) + + version_reply = self.vapi.show_version() + version = version_reply.version.split("-")[0] + registry_jar_path = self.full_jar_name(install_dir, + REGISTRY_JAR_PREFIX, version) + print("JVpp Registry jar path : {0}".format(registry_jar_path)) + + api_jar_path = self.full_jar_name(install_dir, api_jar_name, version) + print("Api jar path : {0}".format(api_jar_path)) + + # passes shm prefix as parameter to create connection with same value + command = ["java", "-cp", + "{0}:{1}".format(registry_jar_path, api_jar_path), + test_class_name, "/{0}-vpe-api".format(self.shm_prefix)] + print("Test Command : {0}, Timeout : {1}".format(command, timeout)) + + self.process = subprocess.Popen(command, shell=False, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, bufsize=1, + universal_newlines=True) + + out, err = self.process.communicate() + print("Process output : {0}{1}".format(os.linesep, out)) + print("Process error output : {0}{1}".format(os.linesep, err)) + self.assert_equal(self.process.returncode, 0, "process return code") + + def tearDown(self): + print("Tearing down jvpp test") + if self.process.poll() is None: + self.process.kill() diff --git a/test/test_jvpp.py b/test/test_jvpp.py new file mode 100644 index 00000000..664ed2f9 --- /dev/null +++ b/test/test_jvpp.py @@ -0,0 +1,82 @@ +from jvpp_connection import TestJVppConnection + + +class TestJVpp(TestJVppConnection): + """ JVPP Core Test Case """ + + def invoke_for_jvpp_core(self, api_jar_name, test_class_name): + self.jvpp_connection_test(api_jar_name=api_jar_name, + test_class_name=test_class_name, + timeout=10) + + def test_vpp_core_callback_api(self): + """ JVPP Core Callback Api Test Case """ + self.invoke_for_jvpp_core(api_jar_name="jvpp-core", + test_class_name="io.fd.vpp.jvpp.core.test." + "CallbackApiTest") + + def test_vpp_core_future_api(self): + """JVPP Core Future Api Test Case""" + self.invoke_for_jvpp_core(api_jar_name="jvpp-core", + test_class_name="io.fd.vpp.jvpp.core.test." + "FutureApiTest") + + def test_vpp_acl_callback_api(self): + """ JVPP Acl Callback Api Test Case """ + self.invoke_for_jvpp_core(api_jar_name="jvpp-acl", + test_class_name="io.fd.vpp.jvpp.acl.test." + "CallbackApiTest") + + def test_vpp_acl_future_api(self): + """JVPP Acl Future Api Test Case""" + self.invoke_for_jvpp_core(api_jar_name="jvpp-acl", + test_class_name="io.fd.vpp.jvpp.acl.test." + "FutureApiTest") + + def test_vpp_ioamexport_callback_api(self): + """ JVPP Ioamexport Callback Api Test Case """ + self.invoke_for_jvpp_core(api_jar_name="jvpp-ioamexport", + test_class_name="io.fd.vpp.jvpp.ioamexport." + "test.CallbackApiTest") + + def test_vpp_ioamexport_future_api(self): + """JVPP Ioamexport Future Api Test Case""" + self.invoke_for_jvpp_core(api_jar_name="jvpp-ioamexport", + test_class_name="io.fd.vpp.jvpp.ioamexport." + "test.FutureApiTest") + + def test_vpp_ioampot_callback_api(self): + """ JVPP Ioampot Callback Api Test Case """ + self.invoke_for_jvpp_core(api_jar_name="jvpp-ioampot", + test_class_name="io.fd.vpp.jvpp.ioampot." + "test.CallbackApiTest") + + def test_vpp_ioampot_future_api(self): + """JVPP Ioampot Future Api Test Case""" + self.invoke_for_jvpp_core(api_jar_name="jvpp-ioampot", + test_class_name="io.fd.vpp.jvpp.ioampot." + "test.FutureApiTest") + + def test_vpp_ioamtrace_callback_api(self): + """ JVPP Ioamtrace Callback Api Test Case """ + self.invoke_for_jvpp_core(api_jar_name="jvpp-ioamtrace", + test_class_name="io.fd.vpp.jvpp.ioamtrace." + "test.CallbackApiTest") + + def test_vpp_ioamtrace_future_api(self): + """JVPP Ioamtrace Future Api Test Case""" + self.invoke_for_jvpp_core(api_jar_name="jvpp-ioamtrace", + test_class_name="io.fd.vpp.jvpp.ioamtrace." + "test.FutureApiTest") + + def test_vpp_snat_callback_api(self): + """ JVPP Snat Callback Api Test Case """ + self.invoke_for_jvpp_core(api_jar_name="jvpp-nat", + test_class_name="io.fd.vpp.jvpp.nat.test." + "CallbackApiTest") + + def test_vpp_snat_future_api(self): + """JVPP Snat Future Api Test Case""" + self.invoke_for_jvpp_core(api_jar_name="jvpp-nat", + test_class_name="io.fd.vpp.jvpp.nat.test." + "FutureApiTest") -- cgit 1.2.3-korg From d05282983af7728a5b3980eb25bf4a14c663b15a Mon Sep 17 00:00:00 2001 From: Matej Perina Date: Wed, 23 Aug 2017 12:48:46 +0200 Subject: jvpp: adding debug usage into test documentation added debug version commands to Readme files for example tests Change-Id: Icc5d5cdd33efb6d7c7512c7a78e22d9dc3d31fdc Signed-off-by: Matej Perina --- src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/Readme.txt | 3 +++ src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/Readme.txt | 3 ++- .../java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/examples/Readme.txt | 3 +++ .../java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/examples/Readme.txt | 3 +++ .../java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/examples/Readme.txt | 3 +++ 5 files changed, 14 insertions(+), 1 deletion(-) (limited to 'src/vpp-api/java') diff --git a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/Readme.txt b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/Readme.txt index 86b473d8..d17fbfc2 100644 --- a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/Readme.txt +++ b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/Readme.txt @@ -1 +1,4 @@ +release version: sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp-native/vpp/vpp-api/java/jvpp-acl-17.10.jar io.fd.vpp.jvpp.acl.examples.FutureApiExample +debug version: +sudo java -cp build-vpp_debug-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp-debug-native/vpp/vpp-api/java/jvpp-acl-17.10.jar io.fd.vpp.jvpp.acl.examples.FutureApiExample diff --git a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/Readme.txt b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/Readme.txt index d1c79dd8..10c603f5 100644 --- a/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/Readme.txt +++ b/src/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/examples/Readme.txt @@ -2,7 +2,8 @@ This package contains basic examples for jvpp. To run the examples: - Make sure VPP is running - From VPP's build-root/ folder execute: - - sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp-native/vpp/vpp-api/java/jvpp-core-17.10.jar io.fd.vpp.jvpp.core.examples.[test name] + - release version: sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp-native/vpp/vpp-api/java/jvpp-core-17.10.jar io.fd.vpp.jvpp.core.examples.[test name] + - debug version: sudo java -cp build-vpp_debug-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-core-17.10.jar io.fd.vpp.jvpp.core.examples.[test name] Available examples: CallbackApiExample - Similar to ControlPingTest, invokes more complex calls (e.g. interface dump) using low level JVpp APIs diff --git a/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/examples/Readme.txt b/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/examples/Readme.txt index 34236f00..f2dfe917 100644 --- a/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/examples/Readme.txt +++ b/src/vpp-api/java/jvpp-ioamexport/io/fd/vpp/jvpp/ioamexport/examples/Readme.txt @@ -1 +1,4 @@ +release version: sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp-native/vpp/vpp-api/java/jvpp-ioamexport-17.10.jar io.fd.vpp.jvpp.ioamexport.examples.IoamExportApiExample +debug vresion: +sudo java -cp build-vpp_debug-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-ioamexport-17.10.jar io.fd.vpp.jvpp.ioamexport.examples.IoamExportApiExample diff --git a/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/examples/Readme.txt b/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/examples/Readme.txt index 03f0ab7b..e91550bf 100644 --- a/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/examples/Readme.txt +++ b/src/vpp-api/java/jvpp-ioampot/io/fd/vpp/jvpp/ioampot/examples/Readme.txt @@ -1 +1,4 @@ +release version: sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp-native/vpp/vpp-api/java/jvpp-ioampot-17.10.jar io.fd.vpp.jvpp.ioampot.examples.IoamPotApiExample +debug vresion: +sudo java -cp build-vpp_debug-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-ioampot-17.10.jar io.fd.vpp.jvpp.ioampot.examples.IoamPotApiExample diff --git a/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/examples/Readme.txt b/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/examples/Readme.txt index 8c534155..e8c3907e 100644 --- a/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/examples/Readme.txt +++ b/src/vpp-api/java/jvpp-ioamtrace/io/fd/vpp/jvpp/ioamtrace/examples/Readme.txt @@ -1 +1,4 @@ +release version: sudo java -cp build-vpp-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp-native/vpp/vpp-api/java/jvpp-ioamtrace-17.10.jar io.fd.vpp.jvpp.ioamtrace.examples.IoamTraceApiExample +debug vresion: +sudo java -cp build-vpp_debug-native/vpp/vpp-api/java/jvpp-registry-17.10.jar:build-vpp_debug-native/vpp/vpp-api/java/jvpp-ioamtrace-17.10.jar io.fd.vpp.jvpp.ioamtrace.examples.IoamTraceApiExample -- cgit 1.2.3-korg From c29940c58de3e44c0c1dd5c4eda5e0268d963b14 Mon Sep 17 00:00:00 2001 From: Pavel Kotucek Date: Thu, 7 Sep 2017 08:17:31 +0200 Subject: ACL-plugin add "replace" semantics for adding a new MacIP acl Change-Id: Ia5c869b2d8b8ad012b9e89fb6720c9c32d9ee065 Signed-off-by: Pavel Kotucek --- src/plugins/acl/acl.api | 52 +++++-- src/plugins/acl/acl.c | 59 +++++++- src/plugins/acl/acl_test.c | 160 ++++++++++++++++++++- src/plugins/acl/manual_fns.h | 42 ++++++ .../fd/vpp/jvpp/acl/examples/AclTestRequests.java | 18 +++ test/test_acl_plugin_macip.py | 118 ++++++++++++--- test/vpp_papi_provider.py | 20 ++- 7 files changed, 427 insertions(+), 42 deletions(-) (limited to 'src/vpp-api/java') diff --git a/src/plugins/acl/acl.api b/src/plugins/acl/acl.api index 48d6aece..a0de24a2 100644 --- a/src/plugins/acl/acl.api +++ b/src/plugins/acl/acl.api @@ -305,7 +305,7 @@ define acl_interface_list_details @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @param tag - descriptive value for this MACIP ACL - @param count - number of rules in this ACL + @param count - number of rules in this MACIP ACL @param r - vector of MACIP ACL rules */ @@ -320,7 +320,7 @@ manual_endian manual_print define macip_acl_add /** \brief Reply to add MACIP ACL @param context - returned sender context, to match reply w/ request - @param acl_index - index of the newly created ACL + @param acl_index - index of the newly created MACIP ACL @param retval 0 - no error */ @@ -331,6 +331,38 @@ define macip_acl_add_reply i32 retval; }; +/** \brief Add/Replace a MACIP ACL + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param acl_index - an existing MACIP ACL entry (0..0xfffffffe) to replace, or 0xffffffff to make new MACIP ACL + @param tag - descriptive value for this MACIP ACL + @param count - number of rules in this MACIP ACL + @param r - vector of MACIP ACL rules +*/ + +manual_endian manual_print define macip_acl_add_replace +{ + u32 client_index; + u32 context; + u32 acl_index; /* ~0 to add, existing MACIP ACL# to replace */ + u8 tag[64]; + u32 count; + vl_api_macip_acl_rule_t r[count]; +}; + +/** \brief Reply to add/replace MACIP ACL + @param context - returned sender context, to match reply w/ request + @param acl_index - index of the newly created MACIP ACL + @param retval 0 - no error +*/ + +define macip_acl_add_replace_reply +{ + u32 context; + u32 acl_index; + i32 retval; +}; + /** \brief Delete a MACIP ACL @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @@ -347,7 +379,7 @@ autoreply manual_print define macip_acl_del /** \brief Add or delete a MACIP ACL to/from interface @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request - @param is_add - add (1) or delete (0) ACL from being used on an interface + @param is_add - add (1) or delete (0) MACIP ACL from being used on an interface @param sw_if_index - interface to apply the action to @param acl_index - MACIP ACL index */ @@ -357,7 +389,7 @@ autoreply manual_print define macip_acl_interface_add_del u32 client_index; u32 context; u8 is_add; - /* macip ACLs are always input */ + /* MACIP ACLs are always input */ u32 sw_if_index; u32 acl_index; }; @@ -365,7 +397,7 @@ autoreply manual_print define macip_acl_interface_add_del /** \brief Dump one or all defined MACIP ACLs @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request - @param acl_index - MACIP ACL index or ~0 to dump all ACLs + @param acl_index - MACIP ACL index or ~0 to dump all MACIP ACLs */ define macip_acl_dump @@ -380,7 +412,7 @@ define macip_acl_dump @param acl_index - index of this MACIP ACL @param tag - descriptive tag which was supplied during the creation @param count - length of the vector of MACIP ACL rules - @param r - rules comprising this ACL + @param r - rules comprising this MACIP ACL */ manual_endian manual_print define macip_acl_details @@ -406,7 +438,7 @@ define macip_acl_interface_get /** \brief Reply with the vector of MACIP ACLs by sw_if_index @param context - returned sender context, to match reply w/ request @param count - total number of elements in the vector - @param acls - the vector of active MACACL indices per sw_if_index + @param acls - the vector of active MACIP ACL indices per sw_if_index */ define macip_acl_interface_get_reply @@ -419,7 +451,7 @@ define macip_acl_interface_get_reply /** \brief Dump the list(s) of MACIP ACLs applied to specific or all interfaces @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request - @param sw_if_index - interface to dump the ACL list for + @param sw_if_index - interface to dump the MACIP ACL list for */ define macip_acl_interface_list_dump @@ -431,9 +463,9 @@ define macip_acl_interface_list_dump /** \brief Details about a single MACIP ACL contents @param context - returned sender context, to match reply w/ request - @param sw_if_index - interface for which the list of ACLs is applied + @param sw_if_index - interface for which the list of MACIP ACLs is applied @param count - total length of acl indices vector - @param acls - the vector of ACL indices + @param acls - the vector of MACIP ACL indices */ define macip_acl_interface_list_details diff --git a/src/plugins/acl/acl.c b/src/plugins/acl/acl.c index bf484f6c..611efbb7 100644 --- a/src/plugins/acl/acl.c +++ b/src/plugins/acl/acl.c @@ -71,6 +71,7 @@ _(ACL_INTERFACE_SET_ACL_LIST, acl_interface_set_acl_list) \ _(ACL_DUMP, acl_dump) \ _(ACL_INTERFACE_LIST_DUMP, acl_interface_list_dump) \ _(MACIP_ACL_ADD, macip_acl_add) \ +_(MACIP_ACL_ADD_REPLACE, macip_acl_add_replace) \ _(MACIP_ACL_DEL, macip_acl_del) \ _(MACIP_ACL_INTERFACE_ADD_DEL, macip_acl_interface_add_del) \ _(MACIP_ACL_DUMP, macip_acl_dump) \ @@ -1158,6 +1159,18 @@ macip_acl_add_list (u32 count, vl_api_macip_acl_rule_t rules[], macip_acl_rule_t *r; macip_acl_rule_t *acl_new_rules = 0; int i; + + if (*acl_list_index != ~0) + { + /* They supplied some number, let's see if this MACIP ACL exists */ + if (pool_is_free_index (am->macip_acls, *acl_list_index)) + { + /* tried to replace a non-existent ACL, no point doing anything */ + clib_warning("acl-plugin-error: Trying to replace nonexistent MACIP ACL %d (tag %s)", *acl_list_index, tag); + return -1; + } + } + if (0 == count) { clib_warning("acl-plugin-warning: Trying to create empty MACIP ACL (tag %s)", tag); } @@ -1180,11 +1193,23 @@ macip_acl_add_list (u32 count, vl_api_macip_acl_rule_t rules[], r->src_prefixlen = rules[i].src_ip_prefix_len; } - /* Get ACL index */ - pool_get_aligned (am->macip_acls, a, CLIB_CACHE_LINE_BYTES); - memset (a, 0, sizeof (*a)); - /* Will return the newly allocated ACL index */ - *acl_list_index = a - am->macip_acls; + if (~0 == *acl_list_index) + { + /* Get ACL index */ + pool_get_aligned (am->macip_acls, a, CLIB_CACHE_LINE_BYTES); + memset (a, 0, sizeof (*a)); + /* Will return the newly allocated ACL index */ + *acl_list_index = a - am->macip_acls; + } + else + { + a = &am->macip_acls[*acl_list_index]; + if (a->rules) + { + vec_free (a->rules); + } + macip_destroy_classify_tables (am, *acl_list_index); + } a->rules = acl_new_rules; a->count = count; @@ -1633,6 +1658,30 @@ vl_api_macip_acl_add_t_handler (vl_api_macip_acl_add_t * mp) /* *INDENT-ON* */ } +static void +vl_api_macip_acl_add_replace_t_handler (vl_api_macip_acl_add_replace_t * mp) +{ + vl_api_macip_acl_add_replace_reply_t *rmp; + acl_main_t *am = &acl_main; + int rv; + u32 acl_list_index = ntohl (mp->acl_index); + u32 acl_count = ntohl (mp->count); + u32 expected_len = sizeof(*mp) + acl_count*sizeof(mp->r[0]); + + if (verify_message_len(mp, expected_len, "macip_acl_add_replace")) { + rv = macip_acl_add_list (acl_count, mp->r, &acl_list_index, mp->tag); + } else { + rv = VNET_API_ERROR_INVALID_VALUE; + } + + /* *INDENT-OFF* */ + REPLY_MACRO2(VL_API_MACIP_ACL_ADD_REPLACE_REPLY, + ({ + rmp->acl_index = htonl(acl_list_index); + })); + /* *INDENT-ON* */ +} + static void vl_api_macip_acl_del_t_handler (vl_api_macip_acl_del_t * mp) { diff --git a/src/plugins/acl/acl_test.c b/src/plugins/acl/acl_test.c index 2b517585..abb9643e 100644 --- a/src/plugins/acl/acl_test.c +++ b/src/plugins/acl/acl_test.c @@ -72,7 +72,8 @@ _(macip_acl_del_reply) #define foreach_reply_retval_aclindex_handler \ _(acl_add_replace_reply) \ -_(macip_acl_add_reply) +_(macip_acl_add_reply) \ +_(macip_acl_add_replace_reply) #define _(n) \ static void vl_api_##n##_t_handler \ @@ -272,6 +273,7 @@ _(ACL_INTERFACE_SET_ACL_LIST_REPLY, acl_interface_set_acl_list_reply) \ _(ACL_INTERFACE_LIST_DETAILS, acl_interface_list_details) \ _(ACL_DETAILS, acl_details) \ _(MACIP_ACL_ADD_REPLY, macip_acl_add_reply) \ +_(MACIP_ACL_ADD_REPLACE_REPLY, macip_acl_add_replace_reply) \ _(MACIP_ACL_DEL_REPLY, macip_acl_del_reply) \ _(MACIP_ACL_DETAILS, macip_acl_details) \ _(MACIP_ACL_INTERFACE_ADD_DEL_REPLY, macip_acl_interface_add_del_reply) \ @@ -965,8 +967,6 @@ static int api_macip_acl_add (vat_main_t * vam) if(rules) n_rules = vec_len(rules); - else - n_rules = 0; if (n_rules_override >= 0) n_rules = n_rules_override; @@ -1000,6 +1000,159 @@ static int api_macip_acl_add (vat_main_t * vam) return ret; } +static int api_macip_acl_add_replace (vat_main_t * vam) +{ + acl_test_main_t * sm = &acl_test_main; + unformat_input_t * i = vam->input; + vl_api_macip_acl_add_replace_t * mp; + u32 acl_index = ~0; + u32 msg_size = sizeof (*mp); /* without the rules */ + + vl_api_macip_acl_rule_t *rules = 0; + int rule_idx = 0; + int n_rules = 0; + int n_rules_override = -1; + u32 src_prefix_length = 0; + u32 action = 0; + ip4_address_t src_v4address; + ip6_address_t src_v6address; + u8 src_mac[6]; + u8 *tag = 0; + u8 mac_mask_all_1[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + int ret; + + if (!unformat (i, "%d", &acl_index)) { + /* Just assume -1 */ + } + + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "ipv6")) + { + vec_validate_macip_acl_rules(rules, rule_idx); + rules[rule_idx].is_ipv6 = 1; + } + else if (unformat (i, "ipv4")) + { + vec_validate_macip_acl_rules(rules, rule_idx); + rules[rule_idx].is_ipv6 = 0; + } + else if (unformat (i, "permit")) + { + vec_validate_macip_acl_rules(rules, rule_idx); + rules[rule_idx].is_permit = 1; + } + else if (unformat (i, "deny")) + { + vec_validate_macip_acl_rules(rules, rule_idx); + rules[rule_idx].is_permit = 0; + } + else if (unformat (i, "count %d", &n_rules_override)) + { + /* we will use this later */ + } + else if (unformat (i, "action %d", &action)) + { + vec_validate_macip_acl_rules(rules, rule_idx); + rules[rule_idx].is_permit = action; + } + else if (unformat (i, "ip %U/%d", + unformat_ip4_address, &src_v4address, &src_prefix_length) || + unformat (i, "ip %U", + unformat_ip4_address, &src_v4address)) + { + if (src_prefix_length == 0) + src_prefix_length = 32; + vec_validate_macip_acl_rules(rules, rule_idx); + memcpy (rules[rule_idx].src_ip_addr, &src_v4address, 4); + rules[rule_idx].src_ip_prefix_len = src_prefix_length; + rules[rule_idx].is_ipv6 = 0; + } + else if (unformat (i, "src")) + { + /* Everything in MACIP is "source" but allow this verbosity */ + } + else if (unformat (i, "ip %U/%d", + unformat_ip6_address, &src_v6address, &src_prefix_length) || + unformat (i, "ip %U", + unformat_ip6_address, &src_v6address)) + { + if (src_prefix_length == 0) + src_prefix_length = 128; + vec_validate_macip_acl_rules(rules, rule_idx); + memcpy (rules[rule_idx].src_ip_addr, &src_v6address, 16); + rules[rule_idx].src_ip_prefix_len = src_prefix_length; + rules[rule_idx].is_ipv6 = 1; + } + else if (unformat (i, "mac %U", + my_unformat_mac_address, &src_mac)) + { + vec_validate_macip_acl_rules(rules, rule_idx); + memcpy (rules[rule_idx].src_mac, &src_mac, 6); + memcpy (rules[rule_idx].src_mac_mask, &mac_mask_all_1, 6); + } + else if (unformat (i, "mask %U", + my_unformat_mac_address, &src_mac)) + { + vec_validate_macip_acl_rules(rules, rule_idx); + memcpy (rules[rule_idx].src_mac_mask, &src_mac, 6); + } + else if (unformat (i, "tag %s", &tag)) + { + } + else if (unformat (i, ",")) + { + rule_idx++; + vec_validate_macip_acl_rules(rules, rule_idx); + } + else + break; + } + + if (!rules) + { + errmsg ("rule/s required\n"); + return -99; + } + /* Construct the API message */ + vam->result_ready = 0; + + if(rules) + n_rules = vec_len(rules); + + if (n_rules_override >= 0) + n_rules = n_rules_override; + + msg_size += n_rules*sizeof(rules[0]); + + mp = vl_msg_api_alloc_as_if_client(msg_size); + memset (mp, 0, msg_size); + mp->_vl_msg_id = ntohs (VL_API_MACIP_ACL_ADD_REPLACE + sm->msg_id_base); + mp->client_index = vam->my_client_index; + if ((n_rules > 0) && rules) + clib_memcpy(mp->r, rules, n_rules*sizeof (mp->r[0])); + if (tag) + { + if (vec_len(tag) >= sizeof(mp->tag)) + { + tag[sizeof(mp->tag)-1] = 0; + _vec_len(tag) = sizeof(mp->tag); + } + clib_memcpy(mp->tag, tag, vec_len(tag)); + vec_free(tag); + } + + mp->acl_index = ntohl(acl_index); + mp->count = htonl(n_rules); + + /* send it... */ + S(mp); + + /* Wait for a reply... */ + W (ret); + return ret; +} + /* * List of messages that the api test plugin sends, * and that the data plane plugin processes @@ -1013,6 +1166,7 @@ _(acl_interface_add_del, " | sw_if_index [add|del] [input|output _(acl_interface_set_acl_list, " | sw_if_index input [acl-idx list] output [acl-idx list]") \ _(acl_interface_list_dump, "[ | sw_if_index ]") \ _(macip_acl_add, "...") \ +_(macip_acl_add_replace, " [ [count ] [src] ip mac mask , ... , ...") \ _(macip_acl_del, "")\ _(macip_acl_dump, "[]") \ _(macip_acl_interface_add_del, " | sw_if_index [add|del] acl ") \ diff --git a/src/plugins/acl/manual_fns.h b/src/plugins/acl/manual_fns.h index c37d14b6..e00f1abc 100644 --- a/src/plugins/acl/manual_fns.h +++ b/src/plugins/acl/manual_fns.h @@ -89,6 +89,18 @@ vl_api_macip_acl_add_t_endian (vl_api_macip_acl_add_t * a) vl_api_macip_acl_rule_t_array_endian (a->r, a->count); } +static inline void +vl_api_macip_acl_add_replace_t_endian (vl_api_macip_acl_add_replace_t * a) +{ + a->_vl_msg_id = clib_net_to_host_u16 (a->_vl_msg_id); + a->client_index = clib_net_to_host_u32 (a->client_index); + a->context = clib_net_to_host_u32 (a->context); + a->acl_index = clib_net_to_host_u32 (a->acl_index); + /* a->tag[0..63] = a->tag[0..63] (no-op) */ + a->count = clib_net_to_host_u32 (a->count); + vl_api_macip_acl_rule_t_array_endian (a->r, a->count); +} + static inline u8 * format_acl_action(u8 *s, u8 action) { @@ -292,6 +304,36 @@ vl_api_macip_acl_add_t_print (vl_api_macip_acl_add_t * a, void *handle) return handle; } +static inline void * +vl_api_macip_acl_add_replace_t_print (vl_api_macip_acl_add_replace_t * a, void *handle) +{ + u8 *s = 0; + int i; + u32 acl_index = clib_net_to_host_u32 (a->acl_index); + u32 count = clib_net_to_host_u32 (a->count); + if (count > 0x100000) + { + s = format (s, "WARN: macip_acl_add_replace count endianness wrong? Fixup to avoid long loop.\n"); + count = a->count; + } + + s = format (s, "SCRIPT: macip_acl_add_replace %d count %d ", + acl_index, count); + if (a->tag[0]) + s = format (s, "tag %s ", a->tag); + + s = format (s, "count %d \\\n", count); + + PRINT_S; + + for (i = 0; i < count; i++) + vl_api_macip_acl_rule_t_print (&a->r[i], handle); + + s = format (0, "\n"); + PRINT_S; + + return handle; +} static inline void * vl_api_acl_interface_set_acl_list_t_print (vl_api_acl_interface_set_acl_list_t diff --git a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclTestRequests.java b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclTestRequests.java index 1fa4187e..149ea46e 100644 --- a/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclTestRequests.java +++ b/src/vpp-api/java/jvpp-acl/io/fd/vpp/jvpp/acl/examples/AclTestRequests.java @@ -32,6 +32,8 @@ import io.fd.vpp.jvpp.acl.dto.AclInterfaceSetAclList; import io.fd.vpp.jvpp.acl.dto.AclInterfaceSetAclListReply; import io.fd.vpp.jvpp.acl.dto.MacipAclAdd; import io.fd.vpp.jvpp.acl.dto.MacipAclAddReply; +import io.fd.vpp.jvpp.acl.dto.MacipAclAddReplace; +import io.fd.vpp.jvpp.acl.dto.MacipAclAddReplaceReply; import io.fd.vpp.jvpp.acl.dto.MacipAclDel; import io.fd.vpp.jvpp.acl.dto.MacipAclDelReply; import io.fd.vpp.jvpp.acl.dto.MacipAclDetailsReplyDump; @@ -56,6 +58,13 @@ class AclTestRequests { System.out.printf("MacipAclAdd send result = %s%n", reply); } + static void sendMacIpAddReplaceRequest(final FutureJVppAclFacade jvpp) throws InterruptedException, ExecutionException { + final MacipAclAddReplace request = createMacIpAddReplaceRequest(); + System.out.printf("Sending MacipAclAddReplace request %s%n", request.toString()); + final MacipAclAddReplaceReply reply = jvpp.macipAclAddReplace(createMacIpAddReplaceRequest()).toCompletableFuture().get(); + System.out.printf("MacipAclAddReplace send result = %s%n", reply); + } + static void sendMacIpDelRequest(final FutureJVppAclFacade jvpp) throws InterruptedException, ExecutionException { final MacipAclDel request = new MacipAclDel(); request.aclIndex = 0; @@ -130,6 +139,15 @@ class AclTestRequests { return request; } + private static MacipAclAddReplace createMacIpAddReplaceRequest() { + MacipAclAddReplace request = new MacipAclAddReplace(); + + request.count = 2; + request.aclIndex = 0; + request.r = createMacipRules(); + return request; + } + private static AclAddReplace createAclAddRequest() { AclAddReplace request = new AclAddReplace(); diff --git a/test/test_acl_plugin_macip.py b/test/test_acl_plugin_macip.py index de57f6bb..3f41313b 100644 --- a/test/test_acl_plugin_macip.py +++ b/test/test_acl_plugin_macip.py @@ -130,13 +130,14 @@ class TestMACIP(VppTestCase): acls = self.vapi.macip_acl_dump() if self.DEBUG: for acl in acls: + print "ACL #"+str(acl.acl_index) for r in acl.r: rule = "ACTION" if r.is_permit == 1: rule = "PERMIT" elif r.is_permit == 0: rule = "DENY " - print "IP6" if r.is_ipv6 else "IP4", \ + print " IP6" if r.is_ipv6 else " IP4", \ rule, \ r.src_mac.encode('hex'), \ r.src_mac_mask.encode('hex'),\ @@ -144,10 +145,12 @@ class TestMACIP(VppTestCase): r.src_ip_prefix_len return acls - def create_acls(self, mac_type, ip_type, acl_count, rules_count): - rules = [] + def create_rules(self, mac_type=EXACT_MAC, ip_type=EXACT_IP, + acl_count=1, rules_count=[1]): + acls = [] src_mac = int("220000dead00", 16) for acl in range(2, (acl_count+1) * 2): + rules = [] host = random.choice(self.loop0.remote_hosts) is_ip6 = acl % 2 ip4 = host.ip4.split('.') @@ -210,12 +213,15 @@ class TestMACIP(VppTestCase): if ip_type == self.WILD_IP: break - reply = self.vapi.macip_acl_add_replace(rules) + acls.append(rules) + src_mac += 1099511627776 + return acls + + def apply_rules(self, acls): + for acl in acls: + reply = self.vapi.macip_acl_add(acl) self.assertEqual(reply.retval, 0) self.ACLS.append(reply.acl_index) - del rules[:] - - src_mac += 1099511627776 def verify_acls(self, acl_count, rules_count, expected_count=2): reply = self.macip_acl_dump_debug() @@ -459,7 +465,8 @@ class TestMACIP(VppTestCase): # data = p[Raw].load.split(':',1)[1] # print p[p_l3].src, data - def run_traffic(self, mac_type, ip_type, bridged_routed, is_ip6, packets): + def run_traffic(self, mac_type, ip_type, bridged_routed, is_ip6, packets, + do_not_expected_capture=False): self.reset_packet_infos() tx_if = self.pg0 if bridged_routed else self.pg2 @@ -469,7 +476,7 @@ class TestMACIP(VppTestCase): self.pg2, self.loop0, bridged_routed, is_ip6) - reply = self.vapi.macip_acl_add_replace(test_dict['rules']) + reply = self.vapi.macip_acl_add(test_dict['rules']) self.assertEqual(reply.retval, 0) acl_index = reply.acl_index @@ -483,15 +490,20 @@ class TestMACIP(VppTestCase): self.pg_enable_capture(self.pg_interfaces) self.pg_start() - packet_count = self.get_packet_count_for_if_idx(self.loop0.sw_if_index) - if mac_type == self.WILD_MAC and ip_type == self.WILD_IP: - packet_count = packets - capture = rx_if.get_capture(packet_count) - self.verify_capture(test_dict['stream'], capture, is_ip6) + if do_not_expected_capture: + rx_if.get_capture(0) + else: + packet_count = self.get_packet_count_for_if_idx( + self.loop0.sw_if_index) + if mac_type == self.WILD_MAC and ip_type == self.WILD_IP: + packet_count = packets + capture = rx_if.get_capture(packet_count) + self.verify_capture(test_dict['stream'], capture, is_ip6) def run_test_acls(self, mac_type, ip_type, acl_count, rules_count, traffic=None, ip=None): - self.create_acls(mac_type, ip_type, acl_count, rules_count) + self.apply_rules(self.create_rules(mac_type, ip_type, acl_count, + rules_count)) self.verify_acls(acl_count, rules_count) if traffic is not None: @@ -687,14 +699,84 @@ class TestMACIP(VppTestCase): [100, 120, 140, 160, 180, 200, 210, 220, 230, 240], self.BRIDGED, self.IS_IP6) + def test_acl_replace(self): + """ MACIP replace ACL + """ + + r1 = self.create_rules(acl_count=3, rules_count=[2, 2, 2]) + r2 = self.create_rules(mac_type=self.OUI_MAC, ip_type=self.SUBNET_IP) + self.apply_rules(r1) + + acls_before = self.macip_acl_dump_debug() + + # replace acls #2, #3 with new + reply = self.vapi.macip_acl_add_replace(r2[0], 2) + self.assertEqual(reply.retval, 0) + self.assertEqual(reply.acl_index, 2) + reply = self.vapi.macip_acl_add_replace(r2[1], 3) + self.assertEqual(reply.retval, 0) + self.assertEqual(reply.acl_index, 3) + + acls_after = self.macip_acl_dump_debug() + + # verify changes + self.assertEqual(len(acls_before), len(acls_after)) + for acl1, acl2 in zip( + acls_before[:2]+acls_before[4:], + acls_after[:2]+acls_after[4:]): + self.assertEqual(len(acl1), len(acl2)) + + self.assertEqual(len(acl1.r), len(acl2.r)) + for r1, r2 in zip(acl1.r, acl2.r): + self.assertEqual(len(acl1.r), len(acl2.r)) + self.assertEqual(acl1.r, acl2.r) + for acl1, acl2 in zip( + acls_before[2:4], + acls_after[2:4]): + self.assertEqual(len(acl1), len(acl2)) + + self.assertNotEqual(len(acl1.r), len(acl2.r)) + for r1, r2 in zip(acl1.r, acl2.r): + self.assertNotEqual(len(acl1.r), len(acl2.r)) + self.assertNotEqual(acl1.r, acl2.r) + + def test_acl_replace_traffic_ip4(self): + """ MACIP replace ACL with IP4 traffic + """ + self.run_traffic(self.OUI_MAC, self.SUBNET_IP, + self.BRIDGED, self.IS_IP4, 9) + + r = self.create_rules() + # replace acls #2, #3 with new + reply = self.vapi.macip_acl_add_replace(r[0], 0) + self.assertEqual(reply.retval, 0) + self.assertEqual(reply.acl_index, 0) + + self.run_traffic(self.EXACT_MAC, self.EXACT_IP, + self.BRIDGED, self.IS_IP4, 9, True) + + def test_acl_replace_traffic_ip6(self): + """ MACIP replace ACL with IP6 traffic + """ + self.run_traffic(self.OUI_MAC, self.SUBNET_IP, + self.BRIDGED, self.IS_IP6, 9) + + r = self.create_rules() + # replace acls #2, #3 with new + reply = self.vapi.macip_acl_add_replace(r[0], 0) + self.assertEqual(reply.retval, 0) + self.assertEqual(reply.acl_index, 0) + + self.run_traffic(self.EXACT_MAC, self.EXACT_IP, + self.BRIDGED, self.IS_IP6, 9, True) + def test_delete_intf(self): """ MACIP ACL delete intf with acl """ intf_count = len(self.interfaces)+1 - rules_count = [3, 5, 4] intf = [] - self.create_acls(self.EXACT_IP, self.EXACT_MAC, 3, rules_count) + self.apply_rules(self.create_rules(acl_count=3, rules_count=[3, 5, 4])) intf.append(VppLoInterface(self, 0)) intf.append(VppLoInterface(self, 1)) @@ -748,7 +830,7 @@ class TestMACIP(VppTestCase): self.assertEqual(len([x for x in reply.acls if x != 4294967295]), 0) @unittest.skipUnless(running_extended_tests(), "part of extended tests") - def test_check(self): + def test_routed(self): """ MACIP with routed traffic """ # TODO: routed do not work yet !!! diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index 519aff80..b63a2658 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -2266,23 +2266,31 @@ class VppPapiProvider(object): 'traffic_type': traffic_type }) - def macip_acl_add_replace(self, rules, acl_index=0xFFFFFFFF, tag=""): + def macip_acl_add(self, rules, tag=""): """ Add MACIP acl :param rules: list of rules for given acl :param tag: acl tag """ - # return self.api(self.papi.macip_acl_add_replace, - # {'acl_index': acl_index, - # 'r': rules, - # 'count': len(rules), - # 'tag': tag}) return self.api(self.papi.macip_acl_add, {'r': rules, 'count': len(rules), 'tag': tag}) + def macip_acl_add_replace(self, rules, acl_index=0xFFFFFFFF, tag=""): + """ Add MACIP acl + + :param rules: list of rules for given acl + :param tag: acl tag + """ + + return self.api(self.papi.macip_acl_add_replace, + {'acl_index': acl_index, + 'r': rules, + 'count': len(rules), + 'tag': tag}) + def macip_acl_del(self, acl_index): """ -- cgit 1.2.3-korg From 3e233678f13bc175e652ac0ea4a038054771ee97 Mon Sep 17 00:00:00 2001 From: Matej Perina Date: Thu, 14 Sep 2017 16:14:14 +0200 Subject: jvpp: add vpe_pid to ConnectionInfo ConnectionInfo now contains pid of vpp process pid is aquired through initial control ping during connection creation Change-Id: I68457770e07792d11c47bc7d8cbd0d2fd722d9b4 Signed-off-by: Matej Perina --- .../jvpp-registry/io/fd/vpp/jvpp/VppJNIConnection.java | 4 +++- src/vpp-api/java/jvpp-registry/jvpp_registry.c | 15 ++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) (limited to 'src/vpp-api/java') diff --git a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppJNIConnection.java b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppJNIConnection.java index 53eaa790..6a414f36 100644 --- a/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppJNIConnection.java +++ b/src/vpp-api/java/jvpp-registry/io/fd/vpp/jvpp/VppJNIConnection.java @@ -135,11 +135,13 @@ public final class VppJNIConnection implements VppConnection { public final long queueAddress; public final int clientIndex; public final int status; // FIXME throw exception instead + public final int pid; - public ConnectionInfo(long queueAddress, int clientIndex, int status) { + public ConnectionInfo(long queueAddress, int clientIndex, int status, int pid) { this.queueAddress = queueAddress; this.clientIndex = clientIndex; this.status = status; + this.pid = pid; } } diff --git a/src/vpp-api/java/jvpp-registry/jvpp_registry.c b/src/vpp-api/java/jvpp-registry/jvpp_registry.c index 1e2c0176..e8264cbf 100644 --- a/src/vpp-api/java/jvpp-registry/jvpp_registry.c +++ b/src/vpp-api/java/jvpp-registry/jvpp_registry.c @@ -73,6 +73,7 @@ typedef struct { /* Connected indication */ volatile u8 is_connected; + u32 vpe_pid; } jvpp_registry_main_t; jvpp_registry_main_t jvpp_registry_main __attribute__((aligned (64))); @@ -170,7 +171,8 @@ static void vl_api_control_ping_reply_t_handler( } } - out: rm->control_ping_result_ready = 1; + out: rm->vpe_pid = clib_net_to_host_u32(mp->vpe_pid); + rm->control_ping_result_ready = 1; } static int find_ping_id() { @@ -239,7 +241,6 @@ static int send_initial_control_ping() { if (rv != 0) { clib_warning("first control ping failed: %d", rv); } - return rv; } @@ -283,12 +284,12 @@ JNIEXPORT jobject JNICALL Java_io_fd_vpp_jvpp_VppJNIConnection_clientConnect( jclass connectionInfoClass = (*env)->FindClass(env, "io/fd/vpp/jvpp/VppJNIConnection$ConnectionInfo"); jmethodID connectionInfoConstructor = (*env)->GetMethodID(env, - connectionInfoClass, "", "(JII)V"); + connectionInfoClass, "", "(JIII)V"); if (rm->is_connected) { return (*env)->NewObject(env, connectionInfoClass, connectionInfoConstructor, 0, 0, - VNET_API_ERROR_ALREADY_CONNECTED); + VNET_API_ERROR_ALREADY_CONNECTED, 0); } client_name = (*env)->GetStringUTFChars(env, clientName, 0); @@ -296,12 +297,12 @@ JNIEXPORT jobject JNICALL Java_io_fd_vpp_jvpp_VppJNIConnection_clientConnect( if (!client_name) { return (*env)->NewObject(env, connectionInfoClass, - connectionInfoConstructor, 0, 0, VNET_API_ERROR_INVALID_VALUE, shmPrefix); + connectionInfoConstructor, 0, 0, VNET_API_ERROR_INVALID_VALUE, 0, shmPrefix); } if (!shm_prefix) { return (*env)->NewObject(env, connectionInfoClass, - connectionInfoConstructor, 0, 0, VNET_API_ERROR_INVALID_VALUE, shmPrefix); + connectionInfoConstructor, 0, 0, VNET_API_ERROR_INVALID_VALUE, 0, shmPrefix); } rv = connect_to_vpe((char *) shm_prefix, (char *) client_name); @@ -314,7 +315,7 @@ JNIEXPORT jobject JNICALL Java_io_fd_vpp_jvpp_VppJNIConnection_clientConnect( return (*env)->NewObject(env, connectionInfoClass, connectionInfoConstructor, (jlong) pointer_to_uword (jm->vl_input_queue), - (jint) jm->my_client_index, (jint) rv); + (jint) jm->my_client_index, (jint) rv, (jint) rm->vpe_pid); } JNIEXPORT jint JNICALL Java_io_fd_vpp_jvpp_JVppRegistryImpl_controlPing0( -- cgit 1.2.3-korg From 75a17ecddc9dc579a3aecfc5b53cbb60e993965f Mon Sep 17 00:00:00 2001 From: Matej Perina Date: Thu, 21 Sep 2017 17:03:27 +0200 Subject: VPP-990 remove registered handler if control ping fails Change-Id: I5ca5763f0dc0a73cc6f014b855426b7ac180f356 Signed-off-by: Matej Perina --- src/vlibapi/api_common.h | 1 + src/vlibapi/api_shared.c | 12 ++++++++++++ src/vpp-api/java/jvpp-registry/jvpp_registry.c | 1 + 3 files changed, 14 insertions(+) (limited to 'src/vpp-api/java') diff --git a/src/vlibapi/api_common.h b/src/vlibapi/api_common.h index 770cf47d..8e6ab0ff 100644 --- a/src/vlibapi/api_common.h +++ b/src/vlibapi/api_common.h @@ -144,6 +144,7 @@ void vl_msg_api_set_handlers (int msg_id, char *msg_name, void *cleanup, void *endian, void *print, int msg_size, int traced); +void vl_msg_api_clean_handlers (int msg_id); void vl_msg_api_config (vl_msg_api_msg_config_t *); void vl_msg_api_set_cleanup_handler (int msg_id, void *fp); void vl_msg_api_queue_handler (unix_shared_memory_queue_t * q); diff --git a/src/vlibapi/api_shared.c b/src/vlibapi/api_shared.c index 0c4952b1..27898c3b 100644 --- a/src/vlibapi/api_shared.c +++ b/src/vlibapi/api_shared.c @@ -716,6 +716,18 @@ vl_msg_api_set_handlers (int id, char *name, void *handler, void *cleanup, vl_msg_api_config (c); } +void +vl_msg_api_clean_handlers (int msg_id) +{ + vl_msg_api_msg_config_t cfg; + vl_msg_api_msg_config_t *c = &cfg; + + memset (c, 0, sizeof (*c)); + + c->id = msg_id; + vl_msg_api_config (c); +} + void vl_msg_api_set_cleanup_handler (int msg_id, void *fp) { diff --git a/src/vpp-api/java/jvpp-registry/jvpp_registry.c b/src/vpp-api/java/jvpp-registry/jvpp_registry.c index e8264cbf..c90822d7 100644 --- a/src/vpp-api/java/jvpp-registry/jvpp_registry.c +++ b/src/vpp-api/java/jvpp-registry/jvpp_registry.c @@ -239,6 +239,7 @@ static int send_initial_control_ping() { } if (rv != 0) { + vl_msg_api_clean_handlers(rm->control_ping_reply_msg_id); clib_warning("first control ping failed: %d", rv); } return rv; -- cgit 1.2.3-korg