aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeale Ranns <nranns@cisco.com>2019-06-19 08:30:46 -0700
committerNeale Ranns <nranns@cisco.com>2019-06-26 16:41:01 +0000
commita83d9d67113dc75d21a68dba14891ed5e1bea7ec (patch)
tree829b8724c836bc48be1a45901291df129d678f69
parentef426ed7714e87cee2097ed5245a78b92a0158ce (diff)
Initial Commit of VPP OPflex Renderer
Change-Id: I6264537538ad2646cddfa404de38a6bbf3abaa35 Signed-off-by: Neale Ranns <nranns@cisco.com>
-rw-r--r--LICENSE201
-rw-r--r--Makefile.am130
-rw-r--r--README.md2
-rwxr-xr-xautogen.sh22
-rw-r--r--configure.ac89
-rw-r--r--m4/ax_boost_base.m4272
-rw-r--r--m4/ax_boost_unit_test_framework.m4137
-rw-r--r--m4/ax_cxx_compile_stdcxx.m4562
-rw-r--r--plugin-renderer-vpp.conf.in110
-rw-r--r--rpm/opflex-agent-renderer-vpp.spec.in75
-rw-r--r--src/.clang-format23
-rw-r--r--src/VppContractManager.cpp289
-rw-r--r--src/VppCrossConnect.cpp148
-rw-r--r--src/VppEndPointGroupManager.cpp592
-rw-r--r--src/VppEndPointManager.cpp741
-rw-r--r--src/VppExtItfManager.cpp188
-rw-r--r--src/VppIdGen.cpp94
-rw-r--r--src/VppInspect.cpp195
-rw-r--r--src/VppLogHandler.cpp44
-rw-r--r--src/VppManager.cpp566
-rw-r--r--src/VppRenderer.cpp325
-rw-r--r--src/VppRouteManager.cpp349
-rw-r--r--src/VppSecurityGroupManager.cpp325
-rw-r--r--src/VppSpineProxy.cpp66
-rw-r--r--src/VppUplink.cpp332
-rw-r--r--src/VppUtil.cpp47
-rw-r--r--src/VppVirtualRouter.cpp31
-rw-r--r--src/include/VppContractManager.hpp47
-rw-r--r--src/include/VppCrossConnect.hpp70
-rw-r--r--src/include/VppEndPointGroupManager.hpp100
-rw-r--r--src/include/VppEndPointManager.hpp63
-rw-r--r--src/include/VppExtItfManager.hpp40
-rw-r--r--src/include/VppIdGen.hpp40
-rw-r--r--src/include/VppInspect.hpp126
-rw-r--r--src/include/VppLog.hpp26
-rw-r--r--src/include/VppLogHandler.hpp56
-rw-r--r--src/include/VppManager.hpp286
-rw-r--r--src/include/VppRenderer.hpp142
-rw-r--r--src/include/VppRouteManager.hpp51
-rw-r--r--src/include/VppRuntime.hpp68
-rw-r--r--src/include/VppSecurityGroupManager.hpp57
-rw-r--r--src/include/VppSpineProxy.hpp62
-rw-r--r--src/include/VppUplink.hpp171
-rw-r--r--src/include/VppUtil.hpp35
-rw-r--r--src/include/VppVirtualRouter.hpp51
-rw-r--r--src/test/VppManager_test.cpp1841
-rw-r--r--src/test/VppRenderer_test.cpp78
-rw-r--r--src/test/vpp_test.cpp13
48 files changed, 9378 insertions, 0 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..88ff701
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,130 @@
+#
+# renderer-vpp: Vpp OpFlex agent renderer plugin
+# Copyright (c) 2018 Cisco Systems, Inc. and others. All rights reserved.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License v1.0 which accompanies this distribution,
+# and is available at http://www.eclipse.org/legal/epl-v10.html
+#
+###########
+#
+# Process this file with automake to produce a Makefile.in
+
+ACLOCAL_AMFLAGS = -I m4
+
+VPP_ADDL_CFLAGS = \
+ -D__STDC_LIMIT_MACROS \
+ -D__STDC_CONSTANT_MACROS \
+ -I/usr/include/vpp_plugins \
+ -fpermissive
+
+# Create a convenience library containing our plugin sources
+noinst_LTLIBRARIES = librenderer_vpp.la
+librenderer_vpp_la_CXXFLAGS = \
+ $(libopflex_agent_CFLAGS) \
+ $(VPP_ADDL_CFLAGS) \
+ -Isrc/include
+librenderer_vpp_la_LIBADD = \
+ $(libopflex_agent_LIBS)
+
+noinst_HEADERS = \
+ src/include/VppContractManager.hpp \
+ src/include/VppCrossConnect.hpp \
+ src/include/VppEndPointGroupManager.hpp \
+ src/include/VppEndPointManager.hpp \
+ src/include/VppExtItfManager.hpp \
+ src/include/VppIdGen.hpp \
+ src/include/VppInspect.hpp \
+ src/include/VppLog.hpp \
+ src/include/VppLogHandler.hpp \
+ src/include/VppManager.hpp \
+ src/include/VppRenderer.hpp \
+ src/include/VppRouteManager.hpp \
+ src/include/VppRuntime.hpp \
+ src/include/VppSecurityGroupManager.hpp \
+ src/include/VppSpineProxy.hpp \
+ src/include/VppUplink.hpp \
+ src/include/VppUtil.hpp \
+ src/include/VppVirtualRouter.hpp
+
+librenderer_vpp_la_SOURCES = \
+ src/VppContractManager.cpp \
+ src/VppCrossConnect.cpp \
+ src/VppEndPointGroupManager.cpp \
+ src/VppEndPointManager.cpp \
+ src/VppExtItfManager.cpp \
+ src/VppIdGen.cpp \
+ src/VppInspect.cpp \
+ src/VppLogHandler.cpp \
+ src/VppManager.cpp \
+ src/VppRenderer.cpp \
+ src/VppRouteManager.cpp \
+ src/VppSecurityGroupManager.cpp \
+ src/VppSpineProxy.cpp \
+ src/VppUplink.cpp \
+ src/VppUtil.cpp \
+ src/VppVirtualRouter.cpp
+
+# Link the convenience library into an installable module
+lib_LTLIBRARIES = libopflex_agent_renderer_vpp.la
+libopflex_agent_renderer_vpp_la_LIBADD = \
+ librenderer_vpp.la
+libopflex_agent_renderer_vpp_la_SOURCES =
+libopflex_agent_renderer_vpp_la_LDFLAGS = \
+ -avoid-version -module -shared
+
+# Install the default plugin configuration file
+pluginconfdir=$(sysconfdir)/opflex-agent-ovs/plugins.conf.d
+pluginconf_DATA = plugin-renderer-vpp.conf
+
+plugin-renderer-vpp.conf: $(top_srcdir)/plugin-renderer-vpp.conf.in
+
+fixstyle:
+ clang-format -i src/*.cpp
+ clang-format -i src/test/*.cpp
+ clang-format -i src/include/*.hpp
+
+# Create a unit test driver that links to the plugin convenience
+# library
+TESTS = vpp_test
+noinst_PROGRAMS = $(TESTS)
+vpp_test_CXXFLAGS = \
+ -I$(top_srcdir)/lib/include \
+ -I$(top_srcdir)/cmd/test/include \
+ -Isrc/include \
+ $(libopflex_agent_CFLAGS) \
+ $(VPP_ADDL_CFLAGS) \
+ -DBOOST_TEST_DYN_LINK
+vpp_test_LDADD = \
+ $(BOOST_FILESYSTEM_LIB) \
+ $(BOOST_SYSTEM_LIB) \
+ $(libopflex_agent_LIBS) \
+ $(BOOST_UNIT_TEST_FRAMEWORK_LIB) \
+ librenderer_vpp.la
+vpp_test_SOURCES = \
+ src/test/vpp_test.cpp \
+ src/test/VppRenderer_test.cpp \
+ src/test/VppManager_test.cpp
+
+clean-local:
+ rm -rf *.rpm
+
+CWD=`pwd`
+RPMFLAGS=--define "_topdir ${CWD}/rpm"
+ARCH=x86_64
+SOURCE_FILE=${PACKAGE}-${VERSION}.tar.gz
+RPMDIRS=rpm/BUILD rpm/SOURCES rpm/RPMS rpm/SRPMS
+rpm: dist rpm/opflex-agent-renderer-vpp.spec
+ mkdir -p ${RPMDIRS}
+ cp ${SOURCE_FILE} rpm/SOURCES/
+ rpmbuild ${RPMFLAGS} -ba rpm/opflex-agent-renderer-vpp.spec
+ cp rpm/RPMS/${ARCH}/*.rpm .
+ cp rpm/SRPMS/*.rpm .
+ rm -rf ${RPMDIRS}
+
+srpm: dist rpm/opflex-agent-renderer-vpp.spec
+ mkdir -p ${RPMDIRS}
+ cp ${SOURCE_FILE} rpm/SOURCES/
+ rpmbuild ${RPMFLAGS} -bs rpm/opflex-agent-renderer-vpp.spec
+ cp rpm/SRPMS/*.rpm .
+ rm -rf ${RPMDIRS}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..303aab9
--- /dev/null
+++ b/README.md
@@ -0,0 +1,2 @@
+# vpp-renderer
+VPP Renderer Plugin for opflex
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..39d4360
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+# sample-renderer: Sample OpFlex agent renderer plugin
+# Copyright (c) 2018 Cisco Systems, Inc. and others. All rights reserved.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License v1.0 which accompanies this distribution,
+# and is available at http://www.eclipse.org/legal/epl-v10.html
+
+# This autogen script will run the autotools to generate the build
+# system. You should run this script in order to initialize a build
+# immediately following a checkout.
+
+for i in m4/*
+do
+ if [ -L "${i}" ]
+ then
+ rm "${i}"
+ fi
+done
+
+autoreconf -fis
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..2aaa7ab
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,89 @@
+#
+# renderer-vpp: VPP OpFlex agent renderer plugin
+#
+###########
+#
+# Process this file with autoconf to produce a configure script
+#
+# If you just want to start a build from source control, run
+# autogen.sh first.
+#
+
+# ---------------------------------------------------------------
+# Initialization
+
+AC_INIT([renderer-vpp], [1.0.0])
+
+# initialize automake and libtool
+AM_INIT_AUTOMAKE([subdir-objects silent-rules foreign])
+AM_CONFIG_HEADER(config.h)
+AC_CONFIG_MACRO_DIR([m4])
+LT_INIT([disable-static])
+
+m4_include([m4/ax_cxx_compile_stdcxx.m4])
+m4_include([m4/ax_boost_unit_test_framework.m4])
+
+# ---------------------------------------------------------------
+# Configure options
+
+# Modify the release/build version
+AC_ARG_WITH(buildversion,
+ AC_HELP_STRING([--with-buildversion],
+ [Version number of build]),
+ [bversion=${withval}],
+ [bversion='private'])
+AC_SUBST(BVERSION, [${bversion}])
+
+# allow to create final builds with assert()s disabled
+AC_HEADER_ASSERT
+
+# ---------------------------------------------------------------
+# Environment introspection
+
+# check for compiler
+AC_PROG_CXX
+AC_PROG_INSTALL
+AM_PROG_AS
+AC_LANG([C++])
+AX_CXX_COMPILE_STDCXX([11], [], [mandatory])
+
+# ---------------------------------------------------------------
+# Dependency checks
+
+# Checks for header files
+AC_STDC_HEADERS
+
+PKG_CHECK_MODULES([libopflex_agent], [libopflex_agent >= 1.7.0])
+AC_SUBST(libopflex_agent_VERSION, [$($PKG_CONFIG --modversion libopflex_agent)])
+# Check for VPP libraries
+AC_CHECK_LIB([svm], [svm_fifo_segment_create], [],
+ [AC_MSG_ERROR([SVM not found])])
+AC_CHECK_LIB([vppinfra], [clib_calljmp], [],
+ [AC_MSG_ERROR([vppinfra not found])])
+AC_CHECK_LIB([vapiclient], [vapi_connect], [],
+ [AC_MSG_ERROR([vapiclient not found])])
+AC_CHECK_LIB(boost_system, main)
+AC_CHECK_LIB(vom, main)
+
+AX_BOOST_BASE([1.53.0], [], AC_MSG_ERROR([Boost is required]))
+AX_BOOST_UNIT_TEST_FRAMEWORK
+if test "x$ax_cv_boost_unit_test_framework" != xyes; then
+ AC_MSG_ERROR([Boost::Test library is required])
+fi
+
+# ---------------------------------------------------------------
+# Output
+
+AC_CONFIG_FILES([\
+ Makefile \
+ plugin-renderer-vpp.conf \
+ rpm/opflex-agent-renderer-vpp.spec
+ ])
+AC_OUTPUT
+
+AC_MSG_NOTICE([
+======================================================================
+Configuration complete
+
+You may now compile the software by running 'make'
+======================================================================])
diff --git a/m4/ax_boost_base.m4 b/m4/ax_boost_base.m4
new file mode 100644
index 0000000..8e6ee9a
--- /dev/null
+++ b/m4/ax_boost_base.m4
@@ -0,0 +1,272 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_boost_base.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_BOOST_BASE([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
+#
+# DESCRIPTION
+#
+# Test for the Boost C++ libraries of a particular version (or newer)
+#
+# If no path to the installed boost library is given the macro searchs
+# under /usr, /usr/local, /opt and /opt/local and evaluates the
+# $BOOST_ROOT environment variable. Further documentation is available at
+# <http://randspringer.de/boost/index.html>.
+#
+# This macro calls:
+#
+# AC_SUBST(BOOST_CPPFLAGS) / AC_SUBST(BOOST_LDFLAGS)
+#
+# And sets:
+#
+# HAVE_BOOST
+#
+# LICENSE
+#
+# Copyright (c) 2008 Thomas Porschberg <thomas@randspringer.de>
+# Copyright (c) 2009 Peter Adolphs
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 23
+
+AC_DEFUN([AX_BOOST_BASE],
+[
+AC_ARG_WITH([boost],
+ [AS_HELP_STRING([--with-boost@<:@=ARG@:>@],
+ [use Boost library from a standard location (ARG=yes),
+ from the specified location (ARG=<path>),
+ or disable it (ARG=no)
+ @<:@ARG=yes@:>@ ])],
+ [
+ if test "$withval" = "no"; then
+ want_boost="no"
+ elif test "$withval" = "yes"; then
+ want_boost="yes"
+ ac_boost_path=""
+ else
+ want_boost="yes"
+ ac_boost_path="$withval"
+ fi
+ ],
+ [want_boost="yes"])
+
+
+AC_ARG_WITH([boost-libdir],
+ AS_HELP_STRING([--with-boost-libdir=LIB_DIR],
+ [Force given directory for boost libraries. Note that this will override library path detection, so use this parameter only if default library detection fails and you know exactly where your boost libraries are located.]),
+ [
+ if test -d "$withval"
+ then
+ ac_boost_lib_path="$withval"
+ else
+ AC_MSG_ERROR(--with-boost-libdir expected directory name)
+ fi
+ ],
+ [ac_boost_lib_path=""]
+)
+
+if test "x$want_boost" = "xyes"; then
+ boost_lib_version_req=ifelse([$1], ,1.20.0,$1)
+ boost_lib_version_req_shorten=`expr $boost_lib_version_req : '\([[0-9]]*\.[[0-9]]*\)'`
+ boost_lib_version_req_major=`expr $boost_lib_version_req : '\([[0-9]]*\)'`
+ boost_lib_version_req_minor=`expr $boost_lib_version_req : '[[0-9]]*\.\([[0-9]]*\)'`
+ boost_lib_version_req_sub_minor=`expr $boost_lib_version_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'`
+ if test "x$boost_lib_version_req_sub_minor" = "x" ; then
+ boost_lib_version_req_sub_minor="0"
+ fi
+ WANT_BOOST_VERSION=`expr $boost_lib_version_req_major \* 100000 \+ $boost_lib_version_req_minor \* 100 \+ $boost_lib_version_req_sub_minor`
+ AC_MSG_CHECKING(for boostlib >= $boost_lib_version_req)
+ succeeded=no
+
+ dnl On 64-bit systems check for system libraries in both lib64 and lib.
+ dnl The former is specified by FHS, but e.g. Debian does not adhere to
+ dnl this (as it rises problems for generic multi-arch support).
+ dnl The last entry in the list is chosen by default when no libraries
+ dnl are found, e.g. when only header-only libraries are installed!
+ libsubdirs="lib"
+ ax_arch=`uname -m`
+ case $ax_arch in
+ x86_64|ppc64|s390x|sparc64|aarch64)
+ libsubdirs="lib64 lib lib64"
+ ;;
+ esac
+
+ dnl allow for real multi-arch paths e.g. /usr/lib/x86_64-linux-gnu. Give
+ dnl them priority over the other paths since, if libs are found there, they
+ dnl are almost assuredly the ones desired.
+ AC_REQUIRE([AC_CANONICAL_HOST])
+ libsubdirs="lib/${host_cpu}-${host_os} $libsubdirs"
+
+ case ${host_cpu} in
+ i?86)
+ libsubdirs="lib/i386-${host_os} $libsubdirs"
+ ;;
+ esac
+
+ dnl first we check the system location for boost libraries
+ dnl this location ist chosen if boost libraries are installed with the --layout=system option
+ dnl or if you install boost with RPM
+ if test "$ac_boost_path" != ""; then
+ BOOST_CPPFLAGS="-I$ac_boost_path/include"
+ for ac_boost_path_tmp in $libsubdirs; do
+ if test -d "$ac_boost_path"/"$ac_boost_path_tmp" ; then
+ BOOST_LDFLAGS="-L$ac_boost_path/$ac_boost_path_tmp"
+ break
+ fi
+ done
+ elif test "$cross_compiling" != yes; then
+ for ac_boost_path_tmp in /usr /usr/local /opt /opt/local ; do
+ if test -d "$ac_boost_path_tmp/include/boost" && test -r "$ac_boost_path_tmp/include/boost"; then
+ for libsubdir in $libsubdirs ; do
+ if ls "$ac_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
+ done
+ BOOST_LDFLAGS="-L$ac_boost_path_tmp/$libsubdir"
+ BOOST_CPPFLAGS="-I$ac_boost_path_tmp/include"
+ break;
+ fi
+ done
+ fi
+
+ dnl overwrite ld flags if we have required special directory with
+ dnl --with-boost-libdir parameter
+ if test "$ac_boost_lib_path" != ""; then
+ BOOST_LDFLAGS="-L$ac_boost_lib_path"
+ fi
+
+ CPPFLAGS_SAVED="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
+ export CPPFLAGS
+
+ LDFLAGS_SAVED="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
+ export LDFLAGS
+
+ AC_REQUIRE([AC_PROG_CXX])
+ AC_LANG_PUSH(C++)
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+ @%:@include <boost/version.hpp>
+ ]], [[
+ #if BOOST_VERSION >= $WANT_BOOST_VERSION
+ // Everything is okay
+ #else
+ # error Boost version is too old
+ #endif
+ ]])],[
+ AC_MSG_RESULT(yes)
+ succeeded=yes
+ found_system=yes
+ ],[
+ ])
+ AC_LANG_POP([C++])
+
+
+
+ dnl if we found no boost with system layout we search for boost libraries
+ dnl built and installed without the --layout=system option or for a staged(not installed) version
+ if test "x$succeeded" != "xyes"; then
+ _version=0
+ if test "$ac_boost_path" != ""; then
+ if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then
+ for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do
+ _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'`
+ V_CHECK=`expr $_version_tmp \> $_version`
+ if test "$V_CHECK" = "1" ; then
+ _version=$_version_tmp
+ fi
+ VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'`
+ BOOST_CPPFLAGS="-I$ac_boost_path/include/boost-$VERSION_UNDERSCORE"
+ done
+ fi
+ else
+ if test "$cross_compiling" != yes; then
+ for ac_boost_path in /usr /usr/local /opt /opt/local ; do
+ if test -d "$ac_boost_path" && test -r "$ac_boost_path"; then
+ for i in `ls -d $ac_boost_path/include/boost-* 2>/dev/null`; do
+ _version_tmp=`echo $i | sed "s#$ac_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'`
+ V_CHECK=`expr $_version_tmp \> $_version`
+ if test "$V_CHECK" = "1" ; then
+ _version=$_version_tmp
+ best_path=$ac_boost_path
+ fi
+ done
+ fi
+ done
+
+ VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'`
+ BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE"
+ if test "$ac_boost_lib_path" = ""; then
+ for libsubdir in $libsubdirs ; do
+ if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
+ done
+ BOOST_LDFLAGS="-L$best_path/$libsubdir"
+ fi
+ fi
+
+ if test "x$BOOST_ROOT" != "x"; then
+ for libsubdir in $libsubdirs ; do
+ if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
+ done
+ if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/$libsubdir" && test -r "$BOOST_ROOT/stage/$libsubdir"; then
+ version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'`
+ stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'`
+ stage_version_shorten=`expr $stage_version : '\([[0-9]]*\.[[0-9]]*\)'`
+ V_CHECK=`expr $stage_version_shorten \>\= $_version`
+ if test "$V_CHECK" = "1" -a "$ac_boost_lib_path" = "" ; then
+ AC_MSG_NOTICE(We will use a staged boost library from $BOOST_ROOT)
+ BOOST_CPPFLAGS="-I$BOOST_ROOT"
+ BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir"
+ fi
+ fi
+ fi
+ fi
+
+ CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
+ export CPPFLAGS
+ LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
+ export LDFLAGS
+
+ AC_LANG_PUSH(C++)
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+ @%:@include <boost/version.hpp>
+ ]], [[
+ #if BOOST_VERSION >= $WANT_BOOST_VERSION
+ // Everything is okay
+ #else
+ # error Boost version is too old
+ #endif
+ ]])],[
+ AC_MSG_RESULT(yes)
+ succeeded=yes
+ found_system=yes
+ ],[
+ ])
+ AC_LANG_POP([C++])
+ fi
+
+ if test "$succeeded" != "yes" ; then
+ if test "$_version" = "0" ; then
+ AC_MSG_NOTICE([[We could not detect the boost libraries (version $boost_lib_version_req_shorten or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in <boost/version.hpp>. See http://randspringer.de/boost for more documentation.]])
+ else
+ AC_MSG_NOTICE([Your boost libraries seems to old (version $_version).])
+ fi
+ # execute ACTION-IF-NOT-FOUND (if present):
+ ifelse([$3], , :, [$3])
+ else
+ AC_SUBST(BOOST_CPPFLAGS)
+ AC_SUBST(BOOST_LDFLAGS)
+ AC_DEFINE(HAVE_BOOST,,[define if the Boost library is available])
+ # execute ACTION-IF-FOUND (if present):
+ ifelse([$2], , :, [$2])
+ fi
+
+ CPPFLAGS="$CPPFLAGS_SAVED"
+ LDFLAGS="$LDFLAGS_SAVED"
+fi
+
+])
diff --git a/m4/ax_boost_unit_test_framework.m4 b/m4/ax_boost_unit_test_framework.m4
new file mode 100644
index 0000000..1115f55
--- /dev/null
+++ b/m4/ax_boost_unit_test_framework.m4
@@ -0,0 +1,137 @@
+# ================================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_boost_unit_test_framework.html
+# ================================================================================
+#
+# SYNOPSIS
+#
+# AX_BOOST_UNIT_TEST_FRAMEWORK
+#
+# DESCRIPTION
+#
+# Test for Unit_Test_Framework library from the Boost C++ libraries. The
+# macro requires a preceding call to AX_BOOST_BASE. Further documentation
+# is available at <http://randspringer.de/boost/index.html>.
+#
+# This macro calls:
+#
+# AC_SUBST(BOOST_UNIT_TEST_FRAMEWORK_LIB)
+#
+# And sets:
+#
+# HAVE_BOOST_UNIT_TEST_FRAMEWORK
+#
+# LICENSE
+#
+# Copyright (c) 2008 Thomas Porschberg <thomas@randspringer.de>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 19
+
+AC_DEFUN([AX_BOOST_UNIT_TEST_FRAMEWORK],
+[
+ AC_ARG_WITH([boost-unit-test-framework],
+ AS_HELP_STRING([--with-boost-unit-test-framework@<:@=special-lib@:>@],
+ [use the Unit_Test_Framework library from boost - it is possible to specify a certain library for the linker
+ e.g. --with-boost-unit-test-framework=boost_unit_test_framework-gcc ]),
+ [
+ if test "$withval" = "no"; then
+ want_boost="no"
+ elif test "$withval" = "yes"; then
+ want_boost="yes"
+ ax_boost_user_unit_test_framework_lib=""
+ else
+ want_boost="yes"
+ ax_boost_user_unit_test_framework_lib="$withval"
+ fi
+ ],
+ [want_boost="yes"]
+ )
+
+ if test "x$want_boost" = "xyes"; then
+ AC_REQUIRE([AC_PROG_CC])
+ CPPFLAGS_SAVED="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
+ export CPPFLAGS
+
+ LDFLAGS_SAVED="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
+ export LDFLAGS
+
+ AC_CACHE_CHECK(whether the Boost::Unit_Test_Framework library is available,
+ ax_cv_boost_unit_test_framework,
+ [AC_LANG_PUSH([C++])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <boost/test/unit_test.hpp>]],
+ [[using boost::unit_test::test_suite;
+ test_suite* test= BOOST_TEST_SUITE( "Unit test example 1" ); return 0;]])],
+ ax_cv_boost_unit_test_framework=yes, ax_cv_boost_unit_test_framework=no)
+ AC_LANG_POP([C++])
+ ])
+ if test "x$ax_cv_boost_unit_test_framework" = "xyes"; then
+ AC_DEFINE(HAVE_BOOST_UNIT_TEST_FRAMEWORK,,[define if the Boost::Unit_Test_Framework library is available])
+ BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'`
+
+ if test "x$ax_boost_user_unit_test_framework_lib" = "x"; then
+ saved_ldflags="${LDFLAGS}"
+ for monitor_library in `ls $BOOSTLIBDIR/libboost_unit_test_framework*.so* $BOOSTLIBDIR/libboost_unit_test_framework*.dylib* $BOOSTLIBDIR/libboost_unit_test_framework*.a* 2>/dev/null` ; do
+ if test -r $monitor_library ; then
+ libextension=`echo $monitor_library | sed 's,.*/,,' | sed -e 's;^lib\(boost_unit_test_framework.*\)\.so.*$;\1;' -e 's;^lib\(boost_unit_test_framework.*\)\.dylib.*$;\1;' -e 's;^lib\(boost_unit_test_framework.*\)\.a.*$;\1;'`
+ ax_lib=${libextension}
+ link_unit_test_framework="yes"
+ else
+ link_unit_test_framework="no"
+ fi
+
+ if test "x$link_unit_test_framework" = "xyes"; then
+ BOOST_UNIT_TEST_FRAMEWORK_LIB="-l$ax_lib"
+ AC_SUBST(BOOST_UNIT_TEST_FRAMEWORK_LIB)
+ break
+ fi
+ done
+ if test "x$link_unit_test_framework" != "xyes"; then
+ for libextension in `ls $BOOSTLIBDIR/boost_unit_test_framework*.dll* $BOOSTLIBDIR/boost_unit_test_framework*.a* 2>/dev/null | sed 's,.*/,,' | sed -e 's;^\(boost_unit_test_framework.*\)\.dll.*$;\1;' -e 's;^\(boost_unit_test_framework.*\)\.a.*$;\1;'` ; do
+ ax_lib=${libextension}
+ AC_CHECK_LIB($ax_lib, exit,
+ [BOOST_UNIT_TEST_FRAMEWORK_LIB="-l$ax_lib"; AC_SUBST(BOOST_UNIT_TEST_FRAMEWORK_LIB) link_unit_test_framework="yes"; break],
+ [link_unit_test_framework="no"])
+ done
+ fi
+ else
+ link_unit_test_framework="no"
+ saved_ldflags="${LDFLAGS}"
+ for ax_lib in boost_unit_test_framework-$ax_boost_user_unit_test_framework_lib $ax_boost_user_unit_test_framework_lib ; do
+ if test "x$link_unit_test_framework" = "xyes"; then
+ break;
+ fi
+ for unittest_library in `ls $BOOSTLIBDIR/lib${ax_lib}.so* $BOOSTLIBDIR/lib${ax_lib}.a* 2>/dev/null` ; do
+ if test -r $unittest_library ; then
+ libextension=`echo $unittest_library | sed 's,.*/,,' | sed -e 's;^lib\(boost_unit_test_framework.*\)\.so.*$;\1;' -e 's;^lib\(boost_unit_test_framework.*\)\.a*$;\1;'`
+ ax_lib=${libextension}
+ link_unit_test_framework="yes"
+ else
+ link_unit_test_framework="no"
+ fi
+
+ if test "x$link_unit_test_framework" = "xyes"; then
+ BOOST_UNIT_TEST_FRAMEWORK_LIB="-l$ax_lib"
+ AC_SUBST(BOOST_UNIT_TEST_FRAMEWORK_LIB)
+ break
+ fi
+ done
+ done
+ fi
+ if test "x$ax_lib" = "x"; then
+ AC_MSG_ERROR(Could not find a version of the library!)
+ fi
+ if test "x$link_unit_test_framework" != "xyes"; then
+ AC_MSG_ERROR(Could not link against $ax_lib !)
+ fi
+ fi
+
+ CPPFLAGS="$CPPFLAGS_SAVED"
+ LDFLAGS="$LDFLAGS_SAVED"
+ fi
+])
diff --git a/m4/ax_cxx_compile_stdcxx.m4 b/m4/ax_cxx_compile_stdcxx.m4
new file mode 100644
index 0000000..2c18e49
--- /dev/null
+++ b/m4/ax_cxx_compile_stdcxx.m4
@@ -0,0 +1,562 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
+#
+# DESCRIPTION
+#
+# Check for baseline language coverage in the compiler for the specified
+# version of the C++ standard. If necessary, add switches to CXX and
+# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard)
+# or '14' (for the C++14 standard).
+#
+# The second argument, if specified, indicates whether you insist on an
+# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
+# -std=c++11). If neither is specified, you get whatever works, with
+# preference for an extended mode.
+#
+# The third argument, if specified 'mandatory' or if left unspecified,
+# indicates that baseline support for the specified C++ standard is
+# required and that the macro should error out if no mode with that
+# support is found. If specified 'optional', then configuration proceeds
+# regardless, after defining HAVE_CXX${VERSION} if and only if a
+# supporting mode is found.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
+# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
+# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
+# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
+# Copyright (c) 2015 Paul Norman <penorman@mac.com>
+# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 4
+
+dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
+dnl (serial version number 13).
+
+AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
+ m4_if([$1], [11], [],
+ [$1], [14], [],
+ [$1], [17], [m4_fatal([support for C++17 not yet implemented in AX_CXX_COMPILE_STDCXX])],
+ [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
+ m4_if([$2], [], [],
+ [$2], [ext], [],
+ [$2], [noext], [],
+ [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl
+ m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],
+ [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],
+ [$3], [optional], [ax_cxx_compile_cxx$1_required=false],
+ [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
+ AC_LANG_PUSH([C++])dnl
+ ac_success=no
+ AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
+ ax_cv_cxx_compile_cxx$1,
+ [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+ [ax_cv_cxx_compile_cxx$1=yes],
+ [ax_cv_cxx_compile_cxx$1=no])])
+ if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
+ ac_success=yes
+ fi
+
+ m4_if([$2], [noext], [], [dnl
+ if test x$ac_success = xno; then
+ for switch in -std=gnu++$1 -std=gnu++0x; do
+ cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
+ AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
+ $cachevar,
+ [ac_save_CXX="$CXX"
+ CXX="$CXX $switch"
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+ [eval $cachevar=yes],
+ [eval $cachevar=no])
+ CXX="$ac_save_CXX"])
+ if eval test x\$$cachevar = xyes; then
+ CXX="$CXX $switch"
+ if test -n "$CXXCPP" ; then
+ CXXCPP="$CXXCPP $switch"
+ fi
+ ac_success=yes
+ break
+ fi
+ done
+ fi])
+
+ m4_if([$2], [ext], [], [dnl
+ if test x$ac_success = xno; then
+ dnl HP's aCC needs +std=c++11 according to:
+ dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
+ dnl Cray's crayCC needs "-h std=c++11"
+ for switch in -std=c++$1 -std=c++0x +std=c++$1 "-h std=c++$1"; do
+ cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
+ AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
+ $cachevar,
+ [ac_save_CXX="$CXX"
+ CXX="$CXX $switch"
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+ [eval $cachevar=yes],
+ [eval $cachevar=no])
+ CXX="$ac_save_CXX"])
+ if eval test x\$$cachevar = xyes; then
+ CXX="$CXX $switch"
+ if test -n "$CXXCPP" ; then
+ CXXCPP="$CXXCPP $switch"
+ fi
+ ac_success=yes
+ break
+ fi
+ done
+ fi])
+ AC_LANG_POP([C++])
+ if test x$ax_cxx_compile_cxx$1_required = xtrue; then
+ if test x$ac_success = xno; then
+ AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
+ fi
+ fi
+ if test x$ac_success = xno; then
+ HAVE_CXX$1=0
+ AC_MSG_NOTICE([No compiler with C++$1 support was found])
+ else
+ HAVE_CXX$1=1
+ AC_DEFINE(HAVE_CXX$1,1,
+ [define if the compiler supports basic C++$1 syntax])
+ fi
+ AC_SUBST(HAVE_CXX$1)
+])
+
+
+dnl Test body for checking C++11 support
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+)
+
+
+dnl Test body for checking C++14 support
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
+)
+
+
+dnl Tests for new features in C++11
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
+
+// If the compiler admits that it is not ready for C++11, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201103L
+
+#error "This is not a C++11 compiler"
+
+#else
+
+namespace cxx11
+{
+
+ namespace test_static_assert
+ {
+
+ template <typename T>
+ struct check
+ {
+ static_assert(sizeof(int) <= sizeof(T), "not big enough");
+ };
+
+ }
+
+ namespace test_final_override
+ {
+
+ struct Base
+ {
+ virtual void f() {}
+ };
+
+ struct Derived : public Base
+ {
+ virtual void f() override {}
+ };
+
+ }
+
+ namespace test_double_right_angle_brackets
+ {
+
+ template < typename T >
+ struct check {};
+
+ typedef check<void> single_type;
+ typedef check<check<void>> double_type;
+ typedef check<check<check<void>>> triple_type;
+ typedef check<check<check<check<void>>>> quadruple_type;
+
+ }
+
+ namespace test_decltype
+ {
+
+ int
+ f()
+ {
+ int a = 1;
+ decltype(a) b = 2;
+ return a + b;
+ }
+
+ }
+
+ namespace test_type_deduction
+ {
+
+ template < typename T1, typename T2 >
+ struct is_same
+ {
+ static const bool value = false;
+ };
+
+ template < typename T >
+ struct is_same<T, T>
+ {
+ static const bool value = true;
+ };
+
+ template < typename T1, typename T2 >
+ auto
+ add(T1 a1, T2 a2) -> decltype(a1 + a2)
+ {
+ return a1 + a2;
+ }
+
+ int
+ test(const int c, volatile int v)
+ {
+ static_assert(is_same<int, decltype(0)>::value == true, "");
+ static_assert(is_same<int, decltype(c)>::value == false, "");
+ static_assert(is_same<int, decltype(v)>::value == false, "");
+ auto ac = c;
+ auto av = v;
+ auto sumi = ac + av + 'x';
+ auto sumf = ac + av + 1.0;
+ static_assert(is_same<int, decltype(ac)>::value == true, "");
+ static_assert(is_same<int, decltype(av)>::value == true, "");
+ static_assert(is_same<int, decltype(sumi)>::value == true, "");
+ static_assert(is_same<int, decltype(sumf)>::value == false, "");
+ static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
+ return (sumf > 0.0) ? sumi : add(c, v);
+ }
+
+ }
+
+ namespace test_noexcept
+ {
+
+ int f() { return 0; }
+ int g() noexcept { return 0; }
+
+ static_assert(noexcept(f()) == false, "");
+ static_assert(noexcept(g()) == true, "");
+
+ }
+
+ namespace test_constexpr
+ {
+
+ template < typename CharT >
+ unsigned long constexpr
+ strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
+ {
+ return *s ? strlen_c_r(s + 1, acc + 1) : acc;
+ }
+
+ template < typename CharT >
+ unsigned long constexpr
+ strlen_c(const CharT *const s) noexcept
+ {
+ return strlen_c_r(s, 0UL);
+ }
+
+ static_assert(strlen_c("") == 0UL, "");
+ static_assert(strlen_c("1") == 1UL, "");
+ static_assert(strlen_c("example") == 7UL, "");
+ static_assert(strlen_c("another\0example") == 7UL, "");
+
+ }
+
+ namespace test_rvalue_references
+ {
+
+ template < int N >
+ struct answer
+ {
+ static constexpr int value = N;
+ };
+
+ answer<1> f(int&) { return answer<1>(); }
+ answer<2> f(const int&) { return answer<2>(); }
+ answer<3> f(int&&) { return answer<3>(); }
+
+ void
+ test()
+ {
+ int i = 0;
+ const int c = 0;
+ static_assert(decltype(f(i))::value == 1, "");
+ static_assert(decltype(f(c))::value == 2, "");
+ static_assert(decltype(f(0))::value == 3, "");
+ }
+
+ }
+
+ namespace test_uniform_initialization
+ {
+
+ struct test
+ {
+ static const int zero {};
+ static const int one {1};
+ };
+
+ static_assert(test::zero == 0, "");
+ static_assert(test::one == 1, "");
+
+ }
+
+ namespace test_lambdas
+ {
+
+ void
+ test1()
+ {
+ auto lambda1 = [](){};
+ auto lambda2 = lambda1;
+ lambda1();
+ lambda2();
+ }
+
+ int
+ test2()
+ {
+ auto a = [](int i, int j){ return i + j; }(1, 2);
+ auto b = []() -> int { return '0'; }();
+ auto c = [=](){ return a + b; }();
+ auto d = [&](){ return c; }();
+ auto e = [a, &b](int x) mutable {
+ const auto identity = [](int y){ return y; };
+ for (auto i = 0; i < a; ++i)
+ a += b--;
+ return x + identity(a + b);
+ }(0);
+ return a + b + c + d + e;
+ }
+
+ int
+ test3()
+ {
+ const auto nullary = [](){ return 0; };
+ const auto unary = [](int x){ return x; };
+ using nullary_t = decltype(nullary);
+ using unary_t = decltype(unary);
+ const auto higher1st = [](nullary_t f){ return f(); };
+ const auto higher2nd = [unary](nullary_t f1){
+ return [unary, f1](unary_t f2){ return f2(unary(f1())); };
+ };
+ return higher1st(nullary) + higher2nd(nullary)(unary);
+ }
+
+ }
+
+ namespace test_variadic_templates
+ {
+
+ template <int...>
+ struct sum;
+
+ template <int N0, int... N1toN>
+ struct sum<N0, N1toN...>
+ {
+ static constexpr auto value = N0 + sum<N1toN...>::value;
+ };
+
+ template <>
+ struct sum<>
+ {
+ static constexpr auto value = 0;
+ };
+
+ static_assert(sum<>::value == 0, "");
+ static_assert(sum<1>::value == 1, "");
+ static_assert(sum<23>::value == 23, "");
+ static_assert(sum<1, 2>::value == 3, "");
+ static_assert(sum<5, 5, 11>::value == 21, "");
+ static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
+
+ }
+
+ // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
+ // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
+ // because of this.
+ namespace test_template_alias_sfinae
+ {
+
+ struct foo {};
+
+ template<typename T>
+ using member = typename T::member_type;
+
+ template<typename T>
+ void func(...) {}
+
+ template<typename T>
+ void func(member<T>*) {}
+
+ void test();
+
+ void test() { func<foo>(0); }
+
+ }
+
+} // namespace cxx11
+
+#endif // __cplusplus >= 201103L
+
+]])
+
+
+dnl Tests for new features in C++14
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
+
+// If the compiler admits that it is not ready for C++14, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201402L
+
+#error "This is not a C++14 compiler"
+
+#else
+
+namespace cxx14
+{
+
+ namespace test_polymorphic_lambdas
+ {
+
+ int
+ test()
+ {
+ const auto lambda = [](auto&&... args){
+ const auto istiny = [](auto x){
+ return (sizeof(x) == 1UL) ? 1 : 0;
+ };
+ const int aretiny[] = { istiny(args)... };
+ return aretiny[0];
+ };
+ return lambda(1, 1L, 1.0f, '1');
+ }
+
+ }
+
+ namespace test_binary_literals
+ {
+
+ constexpr auto ivii = 0b0000000000101010;
+ static_assert(ivii == 42, "wrong value");
+
+ }
+
+ namespace test_generalized_constexpr
+ {
+
+ template < typename CharT >
+ constexpr unsigned long
+ strlen_c(const CharT *const s) noexcept
+ {
+ auto length = 0UL;
+ for (auto p = s; *p; ++p)
+ ++length;
+ return length;
+ }
+
+ static_assert(strlen_c("") == 0UL, "");
+ static_assert(strlen_c("x") == 1UL, "");
+ static_assert(strlen_c("test") == 4UL, "");
+ static_assert(strlen_c("another\0test") == 7UL, "");
+
+ }
+
+ namespace test_lambda_init_capture
+ {
+
+ int
+ test()
+ {
+ auto x = 0;
+ const auto lambda1 = [a = x](int b){ return a + b; };
+ const auto lambda2 = [a = lambda1(x)](){ return a; };
+ return lambda2();
+ }
+
+ }
+
+ namespace test_digit_seperators
+ {
+
+ constexpr auto ten_million = 100'000'000;
+ static_assert(ten_million == 100000000, "");
+
+ }
+
+ namespace test_return_type_deduction
+ {
+
+ auto f(int& x) { return x; }
+ decltype(auto) g(int& x) { return x; }
+
+ template < typename T1, typename T2 >
+ struct is_same
+ {
+ static constexpr auto value = false;
+ };
+
+ template < typename T >
+ struct is_same<T, T>
+ {
+ static constexpr auto value = true;
+ };
+
+ int
+ test()
+ {
+ auto x = 0;
+ static_assert(is_same<int, decltype(f(x))>::value, "");
+ static_assert(is_same<int&, decltype(g(x))>::value, "");
+ return x;
+ }
+
+ }
+
+} // namespace cxx14
+
+#endif // __cplusplus >= 201402L
+
+]])
diff --git a/plugin-renderer-vpp.conf.in b/plugin-renderer-vpp.conf.in
new file mode 100644
index 0000000..0da51bf
--- /dev/null
+++ b/plugin-renderer-vpp.conf.in
@@ -0,0 +1,110 @@
+{
+ "plugins": {
+ // Load vpp renderer plugin
+ "renderer": ["libopflex_agent_renderer_vpp.so"]
+ },
+
+ "renderers": {
+ // vpp renderer configuration
+ // "vpp": {
+ // Put configuration specific to renderer plugin here.
+ // "inspect-socket": "/usr/local/var/run/opflex-agent-vpp-inspect.sock",
+ // "encap": {
+ // "vxlan" : {
+ // "encap-iface": "vpp_vxlan0",
+ // // The name of the interface whose IP should be used
+ // // as the source IP in encapsulated traffic.
+ // "uplink-iface": "team0.4093",
+ //
+ // // The vlan tag, if any, used on the uplink interface.
+ // // Set to zero or omit if the uplink is untagged.
+ // "uplink-vlan": 4093,
+ //
+ // // The IP address used for the destination IP in
+ // // the encapsulated traffic. This should be an
+ // // anycast IP address understood by the upstream
+ // // stiched-mode fabric.
+ // "remote-ip": "10.0.0.32",
+ //
+ // // UDP port number of the encapsulated traffic.
+ // "remote-port": 4789
+ // },
+ // // Encapsulate traffic with a locally-significant VLAN
+ // // tag
+ // "vlan" : {
+ // "encap-iface": "team0",
+ // // The name of the interface VPP will consume for uplink
+ // "uplink-iface": "veth-uplink",
+ // "uplink-iface": "BondEthernet0",
+ // "uplink-slaves" : [
+ // "veth-uplink1",
+ // "veth-uplink2"
+ // ],
+ // // The vlan tag, used on the uplink interface.
+ // // for control traffic.
+ // "uplink-vlan": 4093,
+ // // The dhcp options
+ // "dhcp-opt": [
+ // "opt-1",
+ // "opt-2",
+ // "opt-3"
+ // ]
+ // }
+ // },
+ // "forwarding": {
+ // // Configure the virtual distributed router
+ // "virtual-router": {
+ // // Enable virtual distributed router. Set to true
+ // // to enable or false to disable.
+ // // Default: true.
+ // "enabled": true,
+ //
+ // // Override MAC address for virtual router.
+ // // Default: "00:22:bd:f8:19:ff"
+ // "mac": "00:22:bd:f8:19:ff",
+ //
+ // // Configure IPv6-related settings for the virtual
+ // // router
+ // "ipv6" : {
+ // // Send router advertisement messages in
+ // // response to router solicitation requests as
+ // // well as unsolicited advertisements. This
+ // // is not required in stitched mode since the
+ // // hardware router will send them.
+ // "router-advertisement": false
+ // }
+ // }
+ // },
+ // // Cross connect interfaces
+ // "x-connect" : [
+ // // pair consists of two interfaces. Each interface has name, optional VLAN and IP addresses
+ // {
+ // "east" : {
+ // "iface" : "veth-uplink",
+ // "vlan" : 2222,
+ // "ip-address" : "10.0.0.10",
+ // "tag-rewrite" : "pop"
+ // },
+ // "west" : {
+ // "iface" : "veth-storage-mgmt",
+ // "vlan" : 3333,
+ // "ip-address" : "10.0.0.20"
+ // }
+ // },
+ // {
+ // "east" : {
+ // "iface" : "BondEthernet0",
+ // "vlan" : 2222,
+ // "ip-address" : "10.0.0.10",
+ // "tag-rewrite" : "pop"
+ // },
+ // "west" : {
+ // "iface" : "veth-storage",
+ // "vlan" : 1111,
+ // "ip-address" : "10.0.0.40"
+ // }
+ // }
+ // ]
+ // }
+ }
+}
diff --git a/rpm/opflex-agent-renderer-vpp.spec.in b/rpm/opflex-agent-renderer-vpp.spec.in
new file mode 100644
index 0000000..e6cc93a
--- /dev/null
+++ b/rpm/opflex-agent-renderer-vpp.spec.in
@@ -0,0 +1,75 @@
+#
+# Copyright (c) 2018 Cisco Systems, Inc. and others. All rights reserved.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License v1.0 which accompanies this distribution,
+# and is available at http://www.eclipse.org/legal/epl-v10.html
+#
+
+%{!?gitversion:%define gitversion %(git describe | sed -e 's/-/_/g')}
+%{!?packageversion:%define packageversion @VERSION@}
+%{!?buildversion:%define buildversion @BVERSION@}
+%global _hardened_build 1
+
+Name: renderer-vpp
+Epoch: 1
+Version: %{packageversion}
+Release: %{gitversion}_%{buildversion}%{?dist}
+Summary: VPP renderer module for OpFlex Agent
+
+Group: System Environment/Daemons
+License: EPLv1.0
+URL: https://github.com/opflex-vpp/vpp-renderer
+
+BuildRoot: %{_tmppath}/%{name}-%{version}-root
+Source: %{name}-%{version}.tar.gz
+Requires: opflex-agent = %{epoch}:@libopflex_agent_VERSION@
+Requires: vpp-devel >= 19.01
+Requires: vpp-lib >= 19.01
+BuildRequires: opflex-agent-devel
+BuildRequires: vpp-devel
+BuildRequires: vpp-lib
+BuildRequires: vpp-plugins
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
+Requires(post): systemd-units
+Requires(postun): systemd-units
+
+%description
+The vpp renderer provides a loadable module that demonstrates
+the basic structure of an OpFlex agent renderer plugin,
+
+%prep
+%setup -q
+
+%build
+%configure --disable-assert
+make %{?_smp_mflags}
+
+%install
+%make_install
+
+%check
+%if %{with check}
+ make check
+%endif
+
+%post
+/bin/systemctl daemon-reload >dev/null || :
+
+%postun
+/bin/systemctl daemon-reload >dev/null || :
+
+#%define _unpackaged_files_terminate_build 0
+
+%files
+%defattr(-,root,root)
+%{_libdir}/libopflex_agent_renderer_vpp.so*
+%{_libdir}/libopflex_agent_renderer_vpp.la
+%dir %{_sysconfdir}/opflex-agent-ovs/plugins.conf.d
+%config(noreplace) %{_sysconfdir}/opflex-agent-ovs/plugins.conf.d/plugin-renderer-vpp.conf
+
+%changelog
+* Fri Apr 13 2018 Mohsin Kazmi <sykazmi@cisco.com> - 1:1.0.0
+* Wed Nov 7 2018 Neale Ranns <nranns@cisco.com> - 1:1.1.0
+- New package file
diff --git a/src/.clang-format b/src/.clang-format
new file mode 100644
index 0000000..d362604
--- /dev/null
+++ b/src/.clang-format
@@ -0,0 +1,23 @@
+BasedOnStyle: LLVM
+IndentWidth: 4
+AlignAfterOpenBracket: Align
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+AllowShortIfStatementsOnASingleLine: true
+AlwaysBreakAfterDefinitionReturnType: All
+BinPackParameters: false
+BinPackArguments: false
+BreakBeforeBraces: Custom
+BraceWrapping:
+ AfterEnum: true
+ AfterStruct: false
+ AfterFunction: true
+ AfterNamespace: true
+ BeforeElse: true
+BreakBeforeBraces: Allman
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializersBeforeComma: true
+NamespaceIndentation: None
+UseTab: Never
diff --git a/src/VppContractManager.cpp b/src/VppContractManager.cpp
new file mode 100644
index 0000000..873e77d
--- /dev/null
+++ b/src/VppContractManager.cpp
@@ -0,0 +1,289 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2018 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#include <opflexagent/PolicyManager.h>
+
+#include <modelgbp/gbp/HashingAlgorithmEnumT.hpp>
+#include <modelgbp/gbp/RoutingDomain.hpp>
+
+#include <modelgbp/gbp/ConnTrackEnumT.hpp>
+#include <modelgbp/gbp/DirectionEnumT.hpp>
+#include <modelgbp/l2/EtherTypeEnumT.hpp>
+
+#include <vom/acl_ethertype.hpp>
+#include <vom/gbp_contract.hpp>
+#include <vom/om.hpp>
+
+#include "VppContractManager.hpp"
+#include "VppLog.hpp"
+
+using namespace VOM;
+
+namespace VPP
+{
+ContractManager::ContractManager(opflexagent::Agent &agent, IdGen &id_gen)
+ : m_agent(agent)
+ , m_id_gen(id_gen)
+{
+}
+
+static void
+get_group_sclass(opflexagent::Agent &agent,
+ const std::unordered_set<opflex::modb::URI> &uris,
+ std::unordered_set<uint32_t> &ids)
+{
+ opflexagent::PolicyManager &pm = agent.getPolicyManager();
+ for (auto &u : uris)
+ {
+ boost::optional<uint32_t> sclass = pm.getSclassForGroup(u);
+
+ if (!sclass)
+ {
+ sclass = pm.getSclassForExternalNet(u);
+ }
+ if (sclass)
+ {
+ ids.insert(sclass.get());
+ }
+ else
+ {
+ VLOGW << "No Sclass for: " << u;
+ }
+ }
+}
+
+static uint32_t
+getRdId(IdGen &id_gen,
+ const std::shared_ptr<modelgbp::gbp::RoutingDomain> epgRd)
+{
+ uint32_t rdId = 0;
+ if (epgRd)
+ {
+ boost::optional<opflex::modb::URI> rdURI = epgRd.get()->getURI();
+ if (rdURI)
+ rdId =
+ id_gen.get(modelgbp::gbp::RoutingDomain::CLASS_ID, rdURI.get());
+ }
+ return rdId;
+}
+
+static uint32_t
+getBdId(IdGen &id_gen, const std::shared_ptr<modelgbp::gbp::BridgeDomain> epgBd)
+{
+ uint32_t bdId = 0;
+ if (epgBd)
+ {
+ boost::optional<opflex::modb::URI> bdURI = epgBd.get()->getURI();
+ bdId = id_gen.get(modelgbp::gbp::BridgeDomain::CLASS_ID, bdURI.get());
+ }
+ return bdId;
+}
+
+void
+ContractManager::handle_update(const opflex::modb::URI &uri)
+{
+ VLOGD << "Updating contract " << uri;
+
+ const std::string &uuid = uri.toString();
+
+ OM::mark_n_sweep ms(uuid);
+
+ opflexagent::PolicyManager &polMgr = m_agent.getPolicyManager();
+ if (!polMgr.contractExists(uri))
+ {
+ // Contract removed
+ return;
+ }
+
+ opflexagent::PolicyManager::uri_set_t provURIs;
+ opflexagent::PolicyManager::uri_set_t consURIs;
+ opflexagent::PolicyManager::uri_set_t intraURIs;
+ polMgr.getContractProviders(uri, provURIs);
+ polMgr.getContractConsumers(uri, consURIs);
+ polMgr.getContractIntra(uri, intraURIs);
+
+ typedef std::unordered_set<uint32_t> id_set_t;
+ id_set_t provIds;
+ id_set_t consIds;
+
+ get_group_sclass(m_agent, provURIs, provIds);
+ get_group_sclass(m_agent, consURIs, consIds);
+
+ opflexagent::PolicyManager::rule_list_t rules;
+ polMgr.getContractRules(uri, rules);
+
+ gbp_contract::gbp_rules_t gbp_rules;
+ ACL::l3_list::rules_t in_rules, out_rules;
+ gbp_contract::ethertype_set_t in_ethertypes, out_ethertypes;
+
+ for (auto rule : rules)
+ {
+ uint8_t dir = rule->getDirection();
+ const std::shared_ptr<modelgbp::gbpe::L24Classifier> &cls =
+ rule->getL24Classifier();
+ uint32_t priority = rule->getPriority();
+ const ethertype_t &etherType = ethertype_t::from_numeric_val(
+ cls->getEtherT(modelgbp::l2::EtherTypeEnumT::CONST_UNSPECIFIED));
+ ACL::action_t act = ACL::action_t::from_bool(
+ (rule->getAllow() || rule->getRedirect()),
+ cls->getConnectionTracking(
+ modelgbp::gbp::ConnTrackEnumT::CONST_NORMAL));
+
+ if (dir == modelgbp::gbp::DirectionEnumT::CONST_BIDIRECTIONAL ||
+ dir == modelgbp::gbp::DirectionEnumT::CONST_IN)
+ {
+ auto it = out_ethertypes.find(etherType);
+ if (it == out_ethertypes.end()) out_ethertypes.insert(etherType);
+ }
+ if (dir == modelgbp::gbp::DirectionEnumT::CONST_BIDIRECTIONAL ||
+ dir == modelgbp::gbp::DirectionEnumT::CONST_OUT)
+ {
+ auto it = in_ethertypes.find(etherType);
+ if (it == in_ethertypes.end()) in_ethertypes.insert(etherType);
+ }
+
+ if (etherType != modelgbp::l2::EtherTypeEnumT::CONST_IPV4 &&
+ etherType != modelgbp::l2::EtherTypeEnumT::CONST_IPV6)
+ {
+ VLOGD << "Contract for Protocol " << etherType.to_string()
+ << " ,(IPv4/IPv6)"
+ << " are allowed";
+ continue;
+ }
+
+ route::prefix_t srcIp(route::prefix_t::ZERO);
+ route::prefix_t dstIp(route::prefix_t::ZERO);
+
+ if (etherType == modelgbp::l2::EtherTypeEnumT::CONST_IPV6)
+ {
+ srcIp = route::prefix_t::ZEROv6;
+ dstIp = route::prefix_t::ZEROv6;
+ }
+
+ ACL::l3_rule l3_rule(priority, act, srcIp, dstIp);
+ setParamUpdate(*cls, l3_rule);
+ if (dir == modelgbp::gbp::DirectionEnumT::CONST_BIDIRECTIONAL ||
+ dir == modelgbp::gbp::DirectionEnumT::CONST_IN)
+ {
+ out_rules.insert(l3_rule);
+ }
+ if (dir == modelgbp::gbp::DirectionEnumT::CONST_BIDIRECTIONAL ||
+ dir == modelgbp::gbp::DirectionEnumT::CONST_OUT)
+ {
+ in_rules.insert(l3_rule);
+ }
+
+ if (rule->getRedirect() && rule->getRedirectDestGrpURI())
+ {
+ opflexagent::PolicyManager::redir_dest_list_t redirList;
+ gbp_rule::next_hops_t nhs;
+ uint8_t hashAlgo = 0, resilientHashEnabled = 0;
+ boost::optional<opflex::modb::URI> destGrpUri =
+ rule->getRedirectDestGrpURI();
+ polMgr.getPolicyDestGroup(
+ destGrpUri.get(), redirList, hashAlgo, resilientHashEnabled);
+
+ for (auto dst : redirList)
+ {
+ uint8_t macAddr[6] = {0};
+ dst->getMac().toUIntArray(macAddr);
+ mac_address_t mac(macAddr);
+ gbp_rule::next_hop_t nh(dst->getIp(),
+ mac,
+ getBdId(m_id_gen, dst->getBD()),
+ getRdId(m_id_gen, dst->getRD()));
+ nhs.insert(nh);
+ }
+ if (nhs.size() == 0)
+ {
+ VLOGI << "Redirect Contract with no NHs: " << uri;
+ continue;
+ }
+
+ if (hashAlgo ==
+ modelgbp::gbp::HashingAlgorithmEnumT::CONST_SYMMETRIC)
+ {
+ gbp_rule::next_hop_set_t next_hop_set(
+ gbp_rule::hash_mode_t::SYMMETRIC, nhs);
+ gbp_rule gr(rule->getPriority(),
+ next_hop_set,
+ gbp_rule::action_t::REDIRECT);
+ gbp_rules.insert(gr);
+ }
+ else if (hashAlgo ==
+ modelgbp::gbp::HashingAlgorithmEnumT::CONST_DSTIP)
+ {
+ gbp_rule::next_hop_set_t next_hop_set(
+ gbp_rule::hash_mode_t::DST_IP, nhs);
+ gbp_rule gr(rule->getPriority(),
+ next_hop_set,
+ gbp_rule::action_t::REDIRECT);
+ gbp_rules.insert(gr);
+ }
+ else if (hashAlgo ==
+ modelgbp::gbp::HashingAlgorithmEnumT::CONST_SRCIP)
+ {
+ gbp_rule::next_hop_set_t next_hop_set(
+ gbp_rule::hash_mode_t::SRC_IP, nhs);
+ gbp_rule gr(rule->getPriority(),
+ next_hop_set,
+ gbp_rule::action_t::REDIRECT);
+ gbp_rules.insert(gr);
+ }
+ }
+ else if (rule->getAllow())
+ {
+ gbp_rule gr(rule->getPriority(), gbp_rule::action_t::PERMIT);
+ gbp_rules.insert(gr);
+ }
+ else
+ {
+ gbp_rule gr(rule->getPriority(), gbp_rule::action_t::DENY);
+ gbp_rules.insert(gr);
+ }
+ }
+
+ for (const uint32_t &pvnid : provIds)
+ {
+ for (const uint32_t &cvnid : consIds)
+ {
+ if (pvnid == cvnid) /* intra group is allowed by default */
+ continue;
+
+ VLOGD << "Contract prov:" << pvnid << " cons:" << cvnid;
+
+ if (!in_rules.empty())
+ {
+ ACL::l3_list inAcl(uuid + "in", in_rules);
+ OM::write(uuid, inAcl);
+
+ gbp_contract gbpc_in(
+ pvnid, cvnid, inAcl, gbp_rules, in_ethertypes);
+ OM::write(uuid, gbpc_in);
+ }
+ if (!out_rules.empty())
+ {
+ ACL::l3_list outAcl(uuid + "out", out_rules);
+ OM::write(uuid, outAcl);
+
+ gbp_contract gbpc_out(
+ cvnid, pvnid, outAcl, gbp_rules, out_ethertypes);
+ OM::write(uuid, gbpc_out);
+ }
+ }
+ }
+}
+
+}; // namespace VPP
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
diff --git a/src/VppCrossConnect.cpp b/src/VppCrossConnect.cpp
new file mode 100644
index 0000000..e10f03c
--- /dev/null
+++ b/src/VppCrossConnect.cpp
@@ -0,0 +1,148 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2018 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#include "VppCrossConnect.hpp"
+
+#include <opflexagent/logging.h>
+
+#include "vom/interface.hpp"
+#include "vom/l2_xconnect.hpp"
+#include "vom/sub_interface.hpp"
+#include "vom/tap_interface.hpp"
+
+using namespace VOM;
+
+namespace VPP
+{
+
+static const std::string XCONNECT_KEY = "__xconnect__";
+
+CrossConnect::xconnect_t::xconnect_t(const std::string &name,
+ uint16_t vlan,
+ std::string ip_address,
+ std::string tag_rewrite)
+ : name(name)
+ , vlan(vlan)
+ , ip(boost::asio::ip::address::from_string(ip_address))
+ , tag_rewrite(tag_rewrite)
+{
+}
+
+std::string
+CrossConnect::xconnect_t::to_string() const
+{
+ std::ostringstream s;
+ s << "[itf:" << name << " vlan:" << vlan << " ip:" << ip.to_string()
+ << " tag-rewrite:" << tag_rewrite << "]";
+
+ return (s.str());
+}
+
+CrossConnect::CrossConnect()
+{
+}
+
+void
+CrossConnect::insert_xconnect(CrossConnect::xconnect xconn)
+{
+ this->xconnects.push_back(xconn);
+}
+
+static VOM::interface::type_t
+getIntfTypeFromName(std::string &name)
+{
+ if (name.find("Bond") != std::string::npos)
+ return VOM::interface::type_t::BOND;
+ else if (name.find("Ethernet") != std::string::npos)
+ return VOM::interface::type_t::ETHERNET;
+ else if (name.find("tapv2") != std::string::npos)
+ return VOM::interface::type_t::TAPV2;
+
+ return VOM::interface::type_t::AFPACKET;
+}
+
+void
+CrossConnect::configure_xconnect()
+{
+
+ LOG(opflexagent::INFO) << "configure cross connect";
+
+ for (auto it : xconnects)
+ {
+ std::shared_ptr<interface> itf_ptr, xitf_ptr;
+ VOM::interface::type_t type = getIntfTypeFromName(it.first.name);
+ if (type == VOM::interface::type_t::TAPV2)
+ {
+ VOM::route::prefix_t pfx(it.first.ip, 24);
+ tap_interface itf(it.first.name, interface::admin_state_t::UP, pfx);
+ OM::write(XCONNECT_KEY, itf);
+ itf_ptr = itf.singular();
+ }
+ else
+ {
+ interface itf(it.first.name, type, interface::admin_state_t::UP);
+ OM::write(XCONNECT_KEY, itf);
+ itf_ptr = itf.singular();
+ }
+ if (it.first.vlan)
+ {
+ /*
+ * now create the sub-interface on which control and data traffic
+ * from
+ * the upstream will arrive
+ */
+ sub_interface subitf(
+ *itf_ptr, interface::admin_state_t::UP, it.first.vlan);
+ OM::write(XCONNECT_KEY, subitf);
+ itf_ptr = subitf.singular();
+ }
+ VOM::interface::type_t type2 = getIntfTypeFromName(it.second.name);
+ if (type2 == VOM::interface::type_t::TAPV2)
+ {
+ VOM::route::prefix_t pfx(it.second.ip, 24);
+ tap_interface xitf(
+ it.second.name, interface::admin_state_t::UP, pfx);
+ OM::write(XCONNECT_KEY, xitf);
+ xitf_ptr = xitf.singular();
+ }
+ else
+ {
+ interface xitf(it.second.name, type2, interface::admin_state_t::UP);
+ OM::write(XCONNECT_KEY, xitf);
+ xitf_ptr = xitf.singular();
+ }
+ if (it.second.vlan)
+ {
+ /*
+ * now create the sub-interface on which control and data traffic
+ * from
+ * the upstream will arrive
+ */
+ sub_interface xsubitf(
+ *xitf_ptr, interface::admin_state_t::UP, it.second.vlan);
+ OM::write(XCONNECT_KEY, xsubitf);
+ xitf_ptr = xsubitf.singular();
+ }
+ VOM::l2_xconnect l2_xconn(*itf_ptr, *xitf_ptr);
+ if ((type == VOM::interface::type_t::ETHERNET ||
+ type == VOM::interface::type_t::AFPACKET) &&
+ it.first.vlan &&
+ (it.first.tag_rewrite.find("pop") != std::string::npos))
+ l2_xconn.set(VOM::l2_vtr::option_t::POP_1, it.first.vlan);
+ OM::write(XCONNECT_KEY, l2_xconn);
+ }
+}
+
+} // namespace VPP
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
diff --git a/src/VppEndPointGroupManager.cpp b/src/VppEndPointGroupManager.cpp
new file mode 100644
index 0000000..38c3e27
--- /dev/null
+++ b/src/VppEndPointGroupManager.cpp
@@ -0,0 +1,592 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#include <boost/optional.hpp>
+
+#include <opflexagent/Agent.h>
+#include <opflexagent/PolicyManager.h>
+
+#include <modelgbp/gbp/AddressResModeEnumT.hpp>
+#include <modelgbp/gbp/BcastFloodModeEnumT.hpp>
+#include <modelgbp/gbp/BridgeDomain.hpp>
+#include <modelgbp/gbp/EpLearningModeEnumT.hpp>
+#include <modelgbp/gbp/FloodDomain.hpp>
+#include <modelgbp/gbp/RoutingDomain.hpp>
+#include <modelgbp/gbp/UnknownFloodModeEnumT.hpp>
+
+#include <vom/bridge_domain.hpp>
+#include <vom/bridge_domain_arp_entry.hpp>
+#include <vom/bridge_domain_entry.hpp>
+#include <vom/gbp_endpoint_group.hpp>
+#include <vom/gbp_subnet.hpp>
+#include <vom/gbp_vxlan.hpp>
+#include <vom/igmp_binding.hpp>
+#include <vom/igmp_listen.hpp>
+#include <vom/l2_binding.hpp>
+#include <vom/l3_binding.hpp>
+#include <vom/nat_binding.hpp>
+#include <vom/nat_static.hpp>
+#include <vom/neighbour.hpp>
+#include <vom/om.hpp>
+#include <vom/route_domain.hpp>
+#include <vom/vxlan_tunnel.hpp>
+
+#include "VppEndPointGroupManager.hpp"
+#include "VppLog.hpp"
+#include "VppSpineProxy.hpp"
+
+namespace VPP
+{
+EndPointGroupManager::EndPointGroupManager(Runtime &runtime)
+ : m_runtime(runtime)
+{
+}
+
+EndPointGroupManager::ForwardInfo::ForwardInfo()
+ : vnid(0xfefefefe)
+ , rdId(0xfefefefe)
+ , bdId(0xfefefefe)
+{
+}
+
+EndPointGroupManager::ForwardInfo
+EndPointGroupManager::get_fwd_info_ext_itf(
+ Runtime &runtime, const opflex::modb::URI &uri) throw(NoFowardInfoException)
+{
+ EndPointGroupManager::ForwardInfo fwd;
+ opflexagent::PolicyManager &polMgr = runtime.policy_manager();
+
+ fwd.vnid = 0xdeadbeaf;
+
+ boost::optional<uint32_t> sclass =
+ polMgr.getSclassForExternalInterface(uri);
+
+ if (!sclass)
+ {
+ throw NoFowardInfoException("No Sclass for External-Interface");
+ }
+ fwd.sclass = sclass.get();
+
+ boost::optional<std::shared_ptr<modelgbp::gbp::RoutingDomain>> epgRd =
+ polMgr.getRDForExternalInterface(uri);
+ boost::optional<std::shared_ptr<modelgbp::gbp::ExternalL3BridgeDomain>>
+ epgBd = polMgr.getBDForExternalInterface(uri);
+
+ if (epgRd)
+ {
+ fwd.rdURI = epgRd.get()->getURI();
+ if (fwd.rdURI)
+ fwd.rdId = runtime.id_gen.get(
+ modelgbp::gbp::RoutingDomain::CLASS_ID, fwd.rdURI.get());
+ else
+ throw NoFowardInfoException("No RD-URI for External-Interface");
+ }
+ else
+ {
+ throw NoFowardInfoException("No RD for External-Interface");
+ }
+
+ if (epgBd)
+ {
+ fwd.bdURI = epgBd.get()->getURI();
+ fwd.bdId = runtime.id_gen.get(modelgbp::gbp::BridgeDomain::CLASS_ID,
+ fwd.bdURI.get());
+ }
+ else
+ {
+ throw NoFowardInfoException("No BD for EPG");
+ }
+ return fwd;
+}
+
+EndPointGroupManager::ForwardInfo
+EndPointGroupManager::get_fwd_info(
+ Runtime &runtime, const opflex::modb::URI &uri) throw(NoFowardInfoException)
+{
+ EndPointGroupManager::ForwardInfo fwd;
+ opflexagent::PolicyManager &polMgr = runtime.policy_manager();
+ boost::optional<uint32_t> epgVnid = polMgr.getVnidForGroup(uri);
+
+ if (!epgVnid)
+ {
+ throw NoFowardInfoException("No EPG VNID");
+ }
+ fwd.vnid = epgVnid.get();
+
+ boost::optional<uint32_t> sclass = polMgr.getSclassForGroup(uri);
+
+ if (!sclass)
+ {
+ throw NoFowardInfoException("No EPG Sclass");
+ }
+ fwd.sclass = sclass.get();
+
+ boost::optional<std::shared_ptr<modelgbp::gbp::RoutingDomain>> epgRd =
+ polMgr.getRDForGroup(uri);
+ boost::optional<std::shared_ptr<modelgbp::gbp::BridgeDomain>> epgBd =
+ polMgr.getBDForGroup(uri);
+
+ if (epgRd)
+ {
+ fwd.rdURI = epgRd.get()->getURI();
+ if (fwd.rdURI)
+ fwd.rdId = runtime.id_gen.get(
+ modelgbp::gbp::RoutingDomain::CLASS_ID, fwd.rdURI.get());
+ else
+ throw NoFowardInfoException("No RD-URI for EPG");
+ }
+ else
+ {
+ throw NoFowardInfoException("No RD for EPG");
+ }
+
+ if (epgBd)
+ {
+ fwd.bdURI = epgBd.get()->getURI();
+ fwd.bdId = runtime.id_gen.get(modelgbp::gbp::BridgeDomain::CLASS_ID,
+ fwd.bdURI.get());
+ }
+ else
+ {
+ throw NoFowardInfoException("No BD for EPG");
+ }
+ return fwd;
+}
+
+std::shared_ptr<vxlan_tunnel>
+EndPointGroupManager::mk_mcast_tunnel(Runtime &r,
+ const std::string &key,
+ uint32_t vni,
+ const std::string &maddr)
+{
+ /*
+ * Add the Vxlan mcast tunnel that will carry the broadcast
+ * and multicast traffic
+ */
+ boost::asio::ip::address dst = boost::asio::ip::address::from_string(maddr);
+
+ vxlan_tunnel vt(r.uplink.local_address(),
+ dst,
+ vni,
+ *r.uplink.local_interface(),
+ vxlan_tunnel::mode_t::GBP_L2);
+ OM::write(key, vt);
+
+ /*
+ * add the mcast group to accept via the uplink and
+ * forward locally.
+ */
+ route::path via_uplink(*r.uplink.local_interface(), nh_proto_t::IPV4);
+ route::ip_mroute mroute({dst.to_v4(), 32});
+
+ mroute.add(via_uplink, route::itf_flags_t::ACCEPT);
+ mroute.add({route::path::special_t::LOCAL}, route::itf_flags_t::FORWARD);
+ OM::write(key, mroute);
+
+ /*
+ * join the group on the uplink interface
+ */
+ igmp_binding igmp_b(*r.uplink.local_interface());
+ OM::write(key, igmp_b);
+
+ igmp_listen igmp_l(igmp_b, dst.to_v4());
+ OM::write(key, igmp_l);
+
+ return (vt.singular());
+}
+
+std::shared_ptr<VOM::interface>
+EndPointGroupManager::mk_bvi(Runtime &r,
+ const std::string &key,
+ const bridge_domain &bd,
+ const route_domain &rd,
+ const boost::optional<mac_address_t> &mac)
+{
+ std::shared_ptr<interface> bvi =
+ std::make_shared<interface>("bvi-" + std::to_string(bd.id()),
+ interface::type_t::BVI,
+ interface::admin_state_t::UP,
+ rd);
+ if (mac)
+ {
+ bvi->set(mac.get());
+ }
+ else if (r.vr)
+ {
+ /*
+ * Set the BVI's MAC address to the Virtual Router
+ * address, so packets destined to the VR are handled
+ * by layer 3.
+ */
+ bvi->set(r.vr->mac());
+ }
+ OM::write(key, *bvi);
+
+ /*
+ * Add the BVI to the BD
+ */
+ l2_binding l2_bvi(*bvi, bd);
+ OM::write(key, l2_bvi);
+
+ /*
+ * the bridge is not in learning mode. So add an L2FIB entry for the BVI
+ */
+ bridge_domain_entry be(bd, bvi->l2_address().to_mac(), *bvi);
+ OM::write(key, be);
+
+ return bvi;
+}
+
+std::shared_ptr<VOM::gbp_route_domain>
+EndPointGroupManager::mk_gbp_rd(Runtime &r,
+ const std::string &key,
+ const VOM::route_domain &rd,
+ uint32_t vnid)
+{
+ std::shared_ptr<VOM::gbp_route_domain> grd;
+ std::shared_ptr<SpineProxy> spine_proxy = r.uplink.spine_proxy();
+
+ if (spine_proxy)
+ {
+ std::shared_ptr<vxlan_tunnel> vt_v4, vt_v6;
+
+ vt_v4 = spine_proxy->mk_v4(key, vnid);
+ vt_v6 = spine_proxy->mk_v6(key, vnid);
+
+ grd = std::make_shared<gbp_route_domain>(rd, vt_v4, vt_v6);
+ }
+ else
+ {
+ grd = std::make_shared<gbp_route_domain>(rd);
+ }
+
+ OM::write(key, *grd);
+
+ return grd;
+}
+
+std::shared_ptr<VOM::gbp_endpoint_group>
+EndPointGroupManager::mk_group(Runtime &runtime,
+ const std::string &key,
+ const opflex::modb::URI &uri,
+ bool is_ext)
+{
+ std::shared_ptr<VOM::gbp_endpoint_group> gepg;
+
+ try
+ {
+ /*
+ * default retention policy of 2 minutes.
+ */
+ EndPointGroupManager::ForwardInfo fwd;
+ gbp_endpoint_group::retention_t retention(120);
+
+ if (is_ext)
+ fwd = get_fwd_info_ext_itf(runtime, uri);
+ else
+ fwd = get_fwd_info(runtime, uri);
+
+ boost::optional<std::shared_ptr<modelgbp::gbpe::EndpointRetention>>
+ ret_pol =
+ runtime.policy_manager().getL2EPRetentionPolicyForGroup(uri);
+
+ if (ret_pol)
+ {
+ retention.remote_ep_timeout =
+ ret_pol.get()->getRemoteEpAgingInterval(120);
+ }
+
+ /*
+ * Construct the Bridge and routing Domains
+ */
+ bridge_domain bd(fwd.bdId, bridge_domain::learning_mode_t::OFF);
+ OM::write(key, bd);
+ route_domain rd(fwd.rdId);
+ OM::write(key, rd);
+
+ /*
+ * Create a BVI interface for the EPG and add it to the bridge-domain
+ */
+ std::shared_ptr<interface> bvi = mk_bvi(runtime, key, bd, rd);
+
+ std::shared_ptr<SpineProxy> spine_proxy = runtime.uplink.spine_proxy();
+
+ if (spine_proxy)
+ {
+ /*
+ * TRANSPORT mode
+ * then a route domain that uses the v4 and v6 resp
+ */
+ boost::optional<uint32_t> rd_vnid;
+ boost::optional<uint32_t> bd_vnid;
+ boost::optional<std::string> bd_mcast;
+ if (is_ext)
+ {
+ rd_vnid =
+ runtime.policy_manager().getRDVnidForExternalInterface(uri);
+ bd_vnid =
+ runtime.policy_manager().getBDVnidForExternalInterface(uri);
+ bd_mcast = runtime.policy_manager()
+ .getBDMulticastIPForExternalInterface(uri);
+ }
+ else
+ {
+ rd_vnid = runtime.policy_manager().getRDVnidForGroup(uri);
+ bd_vnid = runtime.policy_manager().getBDVnidForGroup(uri);
+ bd_mcast =
+ runtime.policy_manager().getBDMulticastIPForGroup(uri);
+ }
+
+ if (bd_vnid && rd_vnid && bd_mcast)
+ {
+ std::shared_ptr<vxlan_tunnel> vt_mc, vt_mac;
+ gbp_bridge_domain::flags_t gbd_flags =
+ gbp_bridge_domain::flags_t::NONE;
+
+ boost::optional<std::shared_ptr<modelgbp::gbp::FloodDomain>>
+ flood_domain = runtime.policy_manager().getFDForGroup(uri);
+
+ if (flood_domain)
+ {
+ if (flood_domain.get()->isUnknownFloodModeSet())
+ {
+ if (modelgbp::gbp::UnknownFloodModeEnumT::
+ CONST_HWPROXY ==
+ flood_domain.get()->getUnknownFloodMode(
+ /* flood */ 1))
+ {
+ vt_mac = spine_proxy->mk_mac(key, bd_vnid.get());
+ }
+ else if (modelgbp::gbp::UnknownFloodModeEnumT::
+ CONST_DROP ==
+ flood_domain.get()->getUnknownFloodMode(
+ /* flood */ 1))
+ {
+ gbd_flags |=
+ gbp_bridge_domain::flags_t::UU_FWD_DROP;
+ }
+ }
+ if (flood_domain.get()->isBcastFloodModeSet())
+ {
+ if (modelgbp::gbp::BcastFloodModeEnumT::CONST_DROP ==
+ flood_domain.get()->getBcastFloodMode(
+ /* normal */ 0))
+ {
+ gbd_flags |= gbp_bridge_domain::flags_t::MCAST_DROP;
+ }
+ }
+ if (flood_domain.get()->isEpLearnModeSet())
+ {
+ if (modelgbp::gbp::EpLearningModeEnumT::
+ CONST_DISABLED ==
+ flood_domain.get()->getEpLearnMode(/* enabled */ 1))
+ {
+ gbd_flags |=
+ gbp_bridge_domain::flags_t::DO_NOT_LEARN;
+ }
+ }
+ if (flood_domain.get()->isArpModeSet())
+ {
+ if (modelgbp::gbp::AddressResModeEnumT::CONST_UNICAST ==
+ flood_domain.get()->getArpMode(/* flood */ 1))
+ {
+ gbd_flags |= gbp_bridge_domain::flags_t::UCAST_ARP;
+ if (!vt_mac)
+ vt_mac =
+ spine_proxy->mk_mac(key, bd_vnid.get());
+ }
+ }
+ }
+
+ vt_mc = mk_mcast_tunnel(
+ runtime, key, bd_vnid.get(), bd_mcast.get());
+ l2_binding l2_vxbd(*vt_mc, bd);
+ OM::write(key, l2_vxbd);
+
+ std::shared_ptr<VOM::gbp_route_domain> grd =
+ mk_gbp_rd(runtime, key, rd, rd_vnid.get());
+
+ gbp_vxlan gvx_rd(rd_vnid.get(),
+ *grd,
+ runtime.uplink.local_address().to_v4());
+ OM::write(key, gvx_rd);
+
+ /*
+ * Add the base GBP-vxlan tunnels that will be used to derive
+ * the learned endpoints
+ */
+
+ /*
+ * construct a BD that uses the MAC spine proxy as the
+ * UU-fwd interface
+ */
+ gbp_bridge_domain gbd(bd, bvi, vt_mac, vt_mc, gbd_flags);
+ OM::write(key, gbd);
+
+ /*
+ * base tunnel on which the TEPs derive and EPs are learnt
+ */
+ gbp_vxlan gvx_bd(
+ bd_vnid.get(), gbd, runtime.uplink.local_address().to_v4());
+ OM::write(key, gvx_bd);
+
+ gepg = std::make_shared<gbp_endpoint_group>(
+ fwd.vnid, fwd.sclass, *grd, gbd);
+ }
+ else
+ {
+ VLOGE << "no RD/BD vnid or sclass " << uri;
+ }
+ }
+ else
+ {
+ /*
+ * STITCHED MODE
+ *
+ * make the VLAN based uplink interface for the group
+ */
+ std::shared_ptr<interface> encap_link =
+ runtime.uplink.mk_interface(key, fwd.vnid);
+
+ /*
+ * Add the encap-link to the BD
+ *
+ * If the encap link is a VLAN, then set the pop VTR operation on
+ * the
+ * link so that the VLAN tag is correctly pop/pushed on rx/tx resp.
+ */
+ l2_binding l2_upl(*encap_link, bd);
+ if (interface::type_t::VXLAN != encap_link->type())
+ {
+ l2_upl.set(l2_vtr::option_t::POP_1, fwd.vnid);
+ }
+ OM::write(key, l2_upl);
+
+ gbp_bridge_domain gbd(bd, *bvi);
+ OM::write(key, gbd);
+
+ gbp_route_domain grd(rd);
+ OM::write(key, grd);
+
+ gepg = std::make_shared<gbp_endpoint_group>(
+ fwd.vnid, fwd.sclass, *encap_link, grd, gbd);
+ }
+ /*
+ * GBP Endpoint Group
+ */
+ gepg->set(retention);
+ OM::write(key, *gepg);
+ }
+ catch (EndPointGroupManager::NoFowardInfoException &nofwd)
+ {
+ VLOGD << "NOT Updating endpoint-group: " << nofwd.reason << " : "
+ << uri;
+ }
+
+ return gepg;
+}
+
+void
+EndPointGroupManager::handle_update(const opflex::modb::URI &epgURI)
+{
+ const std::string &epg_uuid = epgURI.toString();
+
+ /*
+ * Mark all of this EPG's state stale. this RAII pattern
+ * will sweep all state that is not updated.
+ */
+ OM::mark_n_sweep ms(epg_uuid);
+
+ VLOGD << "Updating endpoint-group:" << epgURI;
+
+ opflexagent::PolicyManager &pm = m_runtime.policy_manager();
+
+ if (!pm.groupExists(epgURI))
+ {
+ VLOGD << "Deleting endpoint-group:" << epgURI;
+ return;
+ }
+
+ std::shared_ptr<VOM::gbp_endpoint_group> gepg =
+ mk_group(m_runtime, epg_uuid, epgURI);
+
+ if (gepg)
+ {
+ std::shared_ptr<interface> bvi = gepg->get_bridge_domain()->get_bvi();
+ std::shared_ptr<bridge_domain> bd =
+ gepg->get_bridge_domain()->get_bridge_domain();
+ std::shared_ptr<route_domain> rd =
+ gepg->get_route_domain()->get_route_domain();
+
+ /*
+ * The BVI is the NAT inside interface for the VMs
+ */
+ nat_binding nb6(*bvi,
+ direction_t::INPUT,
+ l3_proto_t::IPV6,
+ nat_binding::zone_t::INSIDE);
+ nat_binding nb4(*bvi,
+ direction_t::INPUT,
+ l3_proto_t::IPV4,
+ nat_binding::zone_t::INSIDE);
+ OM::write(epg_uuid, nb4);
+ OM::write(epg_uuid, nb6);
+
+ /*
+ * For each subnet the EPG has
+ */
+ opflexagent::PolicyManager::subnet_vector_t subnets;
+ pm.getSubnetsForGroup(epgURI, subnets);
+
+ for (auto sn : subnets)
+ {
+ boost::optional<boost::asio::ip::address> routerIp =
+ opflexagent::PolicyManager::getRouterIpForSubnet(*sn);
+
+ if (!sn->getPrefixLen() || !sn->getAddress()) continue;
+
+ if (routerIp)
+ {
+ boost::asio::ip::address raddr = routerIp.get();
+ /*
+ * - apply the host prefix on the BVI
+ * - add an entry into the ARP Table for it.
+ */
+ l3_binding l3(*bvi, {raddr});
+ OM::write(epg_uuid, l3);
+
+ bridge_domain_arp_entry bae(
+ *bd, raddr, bvi->l2_address().to_mac());
+ OM::write(epg_uuid, bae);
+ }
+ /*
+ * The subnet is an internal 'GBP subnet' i.e. it is one where
+ * the egress the is the EPG's uplink. And the EPG is chosen
+ * based on the packet's source port
+ */
+ route::prefix_t pfx(sn->getAddress().get(),
+ sn->getPrefixLen().get());
+
+ gbp_subnet gs(*rd,
+ pfx.low(),
+ (gepg->get_route_domain()->get_ip4_uu_fwd()
+ ? gbp_subnet::type_t::TRANSPORT
+ : gbp_subnet::type_t::STITCHED_INTERNAL));
+ OM::write(epg_uuid, gs);
+ }
+ }
+}
+
+}; // namespace VPP
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
diff --git a/src/VppEndPointManager.cpp b/src/VppEndPointManager.cpp
new file mode 100644
index 0000000..4be8f70
--- /dev/null
+++ b/src/VppEndPointManager.cpp
@@ -0,0 +1,741 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2017-2018 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#include <opflexagent/Endpoint.h>
+#include <opflexagent/EndpointManager.h>
+#include <opflexagent/logging.h>
+
+#include <modelgbp/gbp/RoutingModeEnumT.hpp>
+#include <modelgbp/l2/EtherTypeEnumT.hpp>
+
+#include <vom/acl_binding.hpp>
+#include <vom/acl_ethertype.hpp>
+#include <vom/bridge_domain.hpp>
+#include <vom/bridge_domain_arp_entry.hpp>
+#include <vom/bridge_domain_entry.hpp>
+#include <vom/gbp_contract.hpp>
+#include <vom/gbp_endpoint.hpp>
+#include <vom/gbp_endpoint_group.hpp>
+#include <vom/gbp_ext_itf.hpp>
+#include <vom/gbp_recirc.hpp>
+#include <vom/gbp_subnet.hpp>
+#include <vom/l2_binding.hpp>
+#include <vom/l3_binding.hpp>
+#include <vom/nat_binding.hpp>
+#include <vom/nat_static.hpp>
+#include <vom/neighbour.hpp>
+#include <vom/om.hpp>
+#include <vom/route.hpp>
+#include <vom/route_domain.hpp>
+#include <vom/stat_reader.hpp>
+#include <vom/sub_interface.hpp>
+
+#include "VppEndPointGroupManager.hpp"
+#include "VppEndPointManager.hpp"
+#include "VppLog.hpp"
+#include "VppSecurityGroupManager.hpp"
+#include "VppUtil.hpp"
+
+using namespace VOM;
+using namespace boost;
+
+namespace VPP
+{
+EndPointManager::EndPointManager(Runtime &runtime)
+ : m_runtime(runtime)
+{
+}
+
+EndPointManager::~EndPointManager()
+{
+}
+
+std::string
+EndPointManager::get_ep_interface_name(const opflexagent::Endpoint &ep) throw(
+ NoEpInterfaceException)
+{
+ const optional<std::string> &epAccessItf = ep.getAccessInterface();
+ const optional<std::string> &epItf = ep.getInterfaceName();
+ const std::string uuid = ep.getUUID();
+ std::string iname;
+
+ /*
+ * the goal here is to get the name of the interface to which the VM
+ * is attached.
+ */
+ if (epAccessItf)
+ iname = epAccessItf.get();
+ else if (epItf)
+ iname = epItf.get();
+ else
+ throw NoEpInterfaceException();
+
+ return iname;
+}
+
+std::shared_ptr<interface>
+EndPointManager::mk_bd_interface(
+ const opflexagent::Endpoint &ep,
+ const std::shared_ptr<bridge_domain> bd,
+ const std::shared_ptr<route_domain> rd) throw(NoEpInterfaceException)
+{
+ const optional<std::string> &epAccessItf = ep.getAccessInterface();
+ const optional<std::string> &epItf = ep.getInterfaceName();
+ const std::string uuid = ep.getUUID();
+ std::string iname = get_ep_interface_name(ep);
+ std::shared_ptr<interface> itf;
+
+ if (ep.getAccessIfaceVlan())
+ {
+ uint16_t vlan_id;
+ interface intf(iname,
+ getIntfTypeFromName(iname),
+ interface::admin_state_t::UP,
+ uuid);
+ OM::write(uuid, intf);
+
+ vlan_id = ep.getAccessIfaceVlan().get();
+ sub_interface sub_itf(intf, interface::admin_state_t::UP, *rd, vlan_id);
+ OM::write(uuid, sub_itf);
+ itf = sub_itf.singular();
+
+ /*
+ * EP's interface is in the EPG's BD
+ */
+ l2_binding l2itf(*itf, *bd);
+ if (ep.getAccessIfaceVlan())
+ {
+ l2itf.set(l2_vtr::option_t::POP_1, vlan_id);
+ }
+
+ OM::write(uuid, l2itf);
+ }
+ else
+ {
+ interface intf(iname,
+ getIntfTypeFromName(iname),
+ interface::admin_state_t::UP,
+ *rd,
+ uuid);
+ OM::write(uuid, intf);
+ itf = intf.singular();
+ }
+
+ /*
+ * If the interface is not created then we cannot do anymore
+ */
+ if (handle_t::INVALID == itf->handle()) throw NoEpInterfaceException();
+
+ return itf;
+}
+
+static void
+allow_dhcp_request(ACL::l3_list::rules_t &in_rules,
+ ACL::l3_list::rules_t &out_rules,
+ uint16_t etherType)
+{
+
+ ACL::action_t act = ACL::action_t::PERMIT;
+
+ if (etherType == modelgbp::l2::EtherTypeEnumT::CONST_IPV4)
+ {
+ route::prefix_t pfx = route::prefix_t::ZERO;
+
+ ACL::l3_rule rule(200, act, pfx, pfx);
+
+ rule.set_proto(17);
+ rule.set_src_from_port(68);
+ rule.set_src_to_port(68);
+ rule.set_dst_from_port(67);
+ rule.set_dst_to_port(67);
+
+ in_rules.insert(rule);
+
+ ACL::l3_rule out_rule(200, act, pfx, pfx);
+
+ out_rule.set_proto(17);
+ out_rule.set_src_from_port(67);
+ out_rule.set_src_to_port(67);
+ out_rule.set_dst_from_port(68);
+ out_rule.set_dst_to_port(68);
+
+ out_rules.insert(out_rule);
+ }
+ else
+ {
+ route::prefix_t pfx = route::prefix_t::ZEROv6;
+
+ ACL::l3_rule rule(200, act, pfx, pfx);
+
+ rule.set_proto(17);
+ rule.set_src_from_port(546);
+ rule.set_src_to_port(546);
+ rule.set_dst_from_port(547);
+ rule.set_dst_to_port(547);
+
+ in_rules.insert(rule);
+
+ ACL::l3_rule out_rule(200, act, pfx, pfx);
+
+ out_rule.set_proto(17);
+ out_rule.set_src_from_port(547);
+ out_rule.set_src_to_port(547);
+ out_rule.set_dst_from_port(546);
+ out_rule.set_dst_to_port(546);
+
+ out_rules.insert(out_rule);
+ }
+}
+
+static std::vector<asio::ip::address>
+get_ep_ips(const opflexagent::Endpoint &ep)
+{
+ /* check and parse the IP-addresses */
+ system::error_code ec;
+ std::vector<asio::ip::address> ipAddresses;
+
+ const optional<opflex::modb::MAC> mac = ep.getMAC();
+
+ for (const std::string &ipStr : ep.getIPs())
+ {
+ asio::ip::address addr = asio::ip::address::from_string(ipStr, ec);
+ if (ec)
+ {
+ LOG(opflexagent::WARNING) << "Invalid endpoint IP: " << ipStr
+ << ": " << ec.message();
+ }
+ else
+ {
+ ipAddresses.push_back(addr);
+ }
+ }
+
+ if (mac)
+ {
+ asio::ip::address_v6 linkLocalIp(
+ opflexagent::network::construct_link_local_ip_addr(mac.get()));
+ if (ep.getIPs().find(linkLocalIp.to_string()) == ep.getIPs().end())
+ ipAddresses.push_back(linkLocalIp);
+ }
+
+ return ipAddresses;
+}
+
+void
+EndPointManager::handle_interface_stat_i(const interface &itf)
+{
+ VLOGD << "Interface Stat: " << itf.to_string();
+
+ opflexagent::EndpointManager &epMgr = m_runtime.agent.getEndpointManager();
+
+ opflexagent::EndpointManager::EpCounters counters;
+ std::unordered_set<std::string> endpoints;
+ auto &data = itf.get_stats();
+
+ VLOGD << "Stats data: " << data;
+
+ epMgr.getEndpointsByAccessIface(itf.name(), endpoints);
+
+ memset(&counters, 0, sizeof(counters));
+ counters.txPackets = data.m_tx.packets;
+ counters.rxPackets = data.m_rx.packets;
+ counters.txBytes = data.m_tx.bytes;
+ counters.rxBytes = data.m_rx.bytes;
+ counters.rxUnicast = data.m_rx_unicast.packets;
+ counters.txUnicast = data.m_tx_unicast.packets;
+ counters.rxBroadcast = data.m_rx_broadcast.packets;
+ counters.txBroadcast = data.m_tx_broadcast.packets;
+ counters.rxMulticast = data.m_rx_multicast.packets;
+ counters.txMulticast = data.m_tx_multicast.packets;
+ // counters.txDrop = data.tx_dropped;
+ // counters.rxDrop = data.rx_dropped;
+
+ for (const std::string &uuid : endpoints)
+ {
+ if (counters.rxDrop == std::numeric_limits<uint64_t>::max())
+ counters.rxDrop = 0;
+ if (counters.txDrop == std::numeric_limits<uint64_t>::max())
+ counters.txDrop = 0;
+ if (counters.txPackets == std::numeric_limits<uint64_t>::max())
+ counters.txPackets = 0;
+ if (counters.rxPackets == std::numeric_limits<uint64_t>::max())
+ counters.rxPackets = 0;
+ if (counters.txBroadcast == std::numeric_limits<uint64_t>::max())
+ counters.txBroadcast = 0;
+ if (counters.rxBroadcast == std::numeric_limits<uint64_t>::max())
+ counters.rxBroadcast = 0;
+ if (counters.txMulticast == std::numeric_limits<uint64_t>::max())
+ counters.txMulticast = 0;
+ if (counters.rxMulticast == std::numeric_limits<uint64_t>::max())
+ counters.rxMulticast = 0;
+ if (counters.txUnicast == std::numeric_limits<uint64_t>::max())
+ counters.txUnicast = 0;
+ if (counters.rxUnicast == std::numeric_limits<uint64_t>::max())
+ counters.rxUnicast = 0;
+ if (counters.rxBytes == std::numeric_limits<uint64_t>::max())
+ counters.rxBytes = 0;
+ if (counters.txBytes == std::numeric_limits<uint64_t>::max())
+ counters.txBytes = 0;
+ epMgr.updateEndpointCounters(uuid, counters);
+ }
+}
+
+void
+EndPointManager::handle_interface_stat(const interface &itf)
+{
+ handle_interface_stat_i(itf);
+}
+
+void
+EndPointManager::handle_update_i(const std::string &uuid, bool is_external)
+{
+ /*
+ * This is an update to all the state related to this endpoint.
+ * At the end of processing we want all the state related to this endpint,
+ * that we don't touch here, gone.
+ */
+ OM::mark_n_sweep ms(uuid);
+ system::error_code ec;
+ int rv;
+
+ opflexagent::EndpointManager &epMgr = m_runtime.agent.getEndpointManager();
+ std::shared_ptr<const opflexagent::Endpoint> epWrapper =
+ epMgr.getEndpoint(uuid);
+
+ if (!epWrapper)
+ {
+ VLOGD << "Deleting endpoint " << uuid;
+ return;
+ }
+ VLOGD << "Updating endpoint " << uuid;
+
+ optional<opflex::modb::URI> epgURI = epMgr.getComputedEPG(uuid);
+
+ if (!epgURI)
+ {
+ // can't do much without EPG
+ VLOGD << "Endpoint - no EPG " << uuid;
+ return;
+ }
+
+ if (is_external && !epWrapper->isExternal())
+ {
+ VLOGE << "Endpoint - not external " << uuid;
+ return;
+ }
+
+ std::shared_ptr<VOM::gbp_endpoint_group> gepg =
+ EndPointGroupManager::mk_group(
+ m_runtime, uuid, epgURI.get(), is_external);
+
+ if (gepg)
+ {
+ std::shared_ptr<interface> bvi = gepg->get_bridge_domain()->get_bvi();
+ std::shared_ptr<bridge_domain> bd =
+ gepg->get_bridge_domain()->get_bridge_domain();
+ std::shared_ptr<route_domain> rd =
+ gepg->get_route_domain()->get_route_domain();
+
+ /*
+ * We want a veth interface - admin up
+ */
+ std::shared_ptr<interface> itf;
+ try
+ {
+ const opflexagent::Endpoint &ep = *epWrapper.get();
+
+ itf = mk_bd_interface(ep, bd, rd);
+
+ /**
+ * We are interested in getting detailed interface stats from VPP
+ */
+ itf->enable_stats(this);
+
+ /*
+ * Apply Security Groups
+ */
+ const opflexagent::EndpointListener::uri_set_t &secGrps =
+ ep.getSecurityGroups();
+ const std::string secGrpId = SecurityGroupManager::get_id(secGrps);
+ std::hash<std::string> string_hash;
+ const std::string secGrpKey = std::to_string(string_hash(secGrpId));
+
+ ACL::l3_list::rules_t in_rules, out_rules;
+ ACL::acl_ethertype::ethertype_rules_t ethertype_rules;
+
+ optional<opflexagent::Endpoint::DHCPv4Config> v4c =
+ ep.getDHCPv4Config();
+ if (v4c)
+ {
+ ACL::ethertype_rule_t et(ethertype_t::IPV4, direction_t::INPUT);
+ ethertype_rules.insert(et);
+ ACL::ethertype_rule_t out_et(ethertype_t::IPV4,
+ direction_t::OUTPUT);
+ ethertype_rules.insert(out_et);
+ allow_dhcp_request(in_rules,
+ out_rules,
+ modelgbp::l2::EtherTypeEnumT::CONST_IPV4);
+ }
+ optional<opflexagent::Endpoint::DHCPv6Config> v6c =
+ ep.getDHCPv6Config();
+ if (v6c)
+ {
+ ACL::ethertype_rule_t et(ethertype_t::IPV6, direction_t::INPUT);
+ ethertype_rules.insert(et);
+ ACL::ethertype_rule_t out_et(ethertype_t::IPV6,
+ direction_t::OUTPUT);
+ ethertype_rules.insert(out_et);
+ allow_dhcp_request(in_rules,
+ out_rules,
+ modelgbp::l2::EtherTypeEnumT::CONST_IPV6);
+ }
+
+ SecurityGroupManager::build_update(m_runtime.agent,
+ secGrps,
+ secGrpId,
+ in_rules,
+ out_rules,
+ ethertype_rules);
+
+ if (!ethertype_rules.empty())
+ {
+ ACL::acl_ethertype a_e(*itf, ethertype_rules);
+ OM::write(uuid, a_e);
+ }
+ if (!in_rules.empty())
+ {
+ ACL::l3_list in_acl(secGrpKey + "-in", in_rules);
+ OM::write(uuid, in_acl);
+
+ ACL::l3_binding in_binding(direction_t::INPUT, *itf, in_acl);
+ OM::write(uuid, in_binding);
+ }
+ if (!out_rules.empty())
+ {
+ ACL::l3_list out_acl(secGrpKey + "-out", out_rules);
+ OM::write(uuid, out_acl);
+
+ ACL::l3_binding out_binding(direction_t::OUTPUT, *itf, out_acl);
+ OM::write(uuid, out_binding);
+ }
+
+ uint8_t macAddr[6] = {0};
+ bool hasMac = ep.getMAC() != none;
+
+ if (hasMac) ep.getMAC().get().toUIntArray(macAddr);
+
+ /* check and parse the IP-addresses */
+ std::vector<asio::ip::address> ipAddresses = get_ep_ips(ep);
+
+ ACL::l2_list::rules_t rules;
+ if (itf->handle().value())
+ {
+ if (ep.isPromiscuousMode())
+ {
+ ACL::l2_rule rulev6(50,
+ ACL::action_t::PERMIT,
+ route::prefix_t::ZEROv6,
+ macAddr,
+ mac_address_t::ZERO);
+
+ ACL::l2_rule rulev4(51,
+ ACL::action_t::PERMIT,
+ route::prefix_t::ZERO,
+ macAddr,
+ mac_address_t::ZERO);
+ rules.insert(rulev4);
+ rules.insert(rulev6);
+ }
+ else if (hasMac)
+ {
+ ACL::l2_rule rulev6(20,
+ ACL::action_t::PERMIT,
+ route::prefix_t::ZEROv6,
+ macAddr,
+ mac_address_t::ONE);
+
+ ACL::l2_rule rulev4(21,
+ ACL::action_t::PERMIT,
+ route::prefix_t::ZERO,
+ macAddr,
+ mac_address_t::ONE);
+ rules.insert(rulev4);
+ rules.insert(rulev6);
+
+ for (auto &ipAddr : ipAddresses)
+ {
+ // Allow IPv4/IPv6 packets from port with EP IP address
+ route::prefix_t pfx(ipAddr, ipAddr.is_v4() ? 32 : 128);
+ if (ipAddr.is_v6())
+ {
+ ACL::l2_rule rule(30,
+ ACL::action_t::PERMIT,
+ pfx,
+ macAddr,
+ mac_address_t::ONE);
+ rules.insert(rule);
+ }
+ else
+ {
+ ACL::l2_rule rule(31,
+ ACL::action_t::PERMIT,
+ pfx,
+ macAddr,
+ mac_address_t::ONE);
+ rules.insert(rule);
+ }
+ }
+ }
+
+ for (const opflexagent::Endpoint::virt_ip_t &vip :
+ ep.getVirtualIPs())
+ {
+ opflexagent::network::cidr_t vip_cidr;
+ if (!opflexagent::network::cidr_from_string(vip.second,
+ vip_cidr))
+ {
+ LOG(opflexagent::WARNING)
+ << "Invalid endpoint VIP (CIDR): " << vip.second;
+ continue;
+ }
+ uint8_t vmac[6];
+ vip.first.toUIntArray(vmac);
+
+ for (auto &ipAddr : ipAddresses)
+ {
+ if (!opflexagent::network::cidr_contains(vip_cidr,
+ ipAddr))
+ {
+ continue;
+ }
+ route::prefix_t pfx(ipAddr, ipAddr.is_v4() ? 32 : 128);
+ if (ipAddr.is_v6())
+ {
+ ACL::l2_rule rule(60,
+ ACL::action_t::PERMIT,
+ pfx,
+ vmac,
+ mac_address_t::ONE);
+ rules.insert(rule);
+ }
+ else
+ {
+ ACL::l2_rule rule(61,
+ ACL::action_t::PERMIT,
+ pfx,
+ vmac,
+ mac_address_t::ONE);
+ rules.insert(rule);
+ }
+ }
+ }
+
+ ACL::l2_list acl(uuid, rules);
+ OM::write(uuid, acl);
+
+ ACL::l2_binding binding(direction_t::INPUT, *itf, acl);
+ OM::write(uuid, binding);
+ }
+
+ if (hasMac)
+ {
+ mac_address_t vmac(macAddr);
+
+ /*
+ * add a GDBP endpoint
+ */
+ gbp_endpoint gbpe(*itf,
+ ipAddresses,
+ vmac,
+ *gepg,
+ (is_external ? gbp_endpoint::flags_t::EXTERNAL
+ : gbp_endpoint::flags_t::NONE));
+ OM::write(uuid, gbpe);
+
+ /*
+ * Floating IP addresses -> NAT
+ */
+ if (m_runtime.vr &&
+ (modelgbp::gbp::RoutingModeEnumT::CONST_ENABLED ==
+ m_runtime.agent.getPolicyManager().getEffectiveRoutingMode(
+ epgURI.get())))
+ {
+ auto ipms = ep.getIPAddressMappings();
+
+ if (0 != ipms.size())
+ {
+ /*
+ * there are floating IPs, we need a recirulation
+ * interface
+ * for this EP's EPG. These are NAT outside and input
+ * feautre
+ * since packets are sent to these interface in order to
+ * have
+ * the out2in translation applied.
+ */
+ interface recirc_itf("recirc-" +
+ std::to_string(gepg->sclass()),
+ interface::type_t::LOOPBACK,
+ interface::admin_state_t::UP,
+ *rd);
+ OM::write(uuid, recirc_itf);
+
+ l2_binding recirc_l2b(recirc_itf, *bd);
+ OM::write(uuid, recirc_l2b);
+
+ nat_binding recirc_nb4(recirc_itf,
+ direction_t::INPUT,
+ l3_proto_t::IPV4,
+ nat_binding::zone_t::OUTSIDE);
+ OM::write(uuid, recirc_nb4);
+
+ nat_binding recirc_nb6(recirc_itf,
+ direction_t::INPUT,
+ l3_proto_t::IPV6,
+ nat_binding::zone_t::OUTSIDE);
+ OM::write(uuid, recirc_nb6);
+
+ gbp_recirc grecirc(
+ recirc_itf, gbp_recirc::type_t::INTERNAL, *gepg);
+ OM::write(uuid, grecirc);
+
+ for (auto &ipm : ipms)
+ {
+ if (!ipm.getMappedIP() || !ipm.getEgURI()) continue;
+
+ asio::ip::address mappedIp =
+ asio::ip::address::from_string(
+ ipm.getMappedIP().get(), ec);
+ if (ec) continue;
+
+ asio::ip::address floatingIp;
+ if (ipm.getFloatingIP())
+ {
+ floatingIp = asio::ip::address::from_string(
+ ipm.getFloatingIP().get(), ec);
+ if (ec) continue;
+ if (floatingIp.is_v4() != mappedIp.is_v4())
+ continue;
+ }
+
+ EndPointGroupManager::ForwardInfo ffwd;
+
+ try
+ {
+ ffwd = EndPointGroupManager::get_fwd_info(
+ m_runtime, ipm.getEgURI().get());
+
+ VLOGD << "EP:" << uuid << " - add Floating IP"
+ << floatingIp << " => " << mappedIp;
+
+ /*
+ * Route and Bridge Domains and the external EPG
+ */
+ route_domain ext_rd(ffwd.rdId);
+ OM::write(uuid, ext_rd);
+ bridge_domain ext_bd(
+ ffwd.bdId,
+ bridge_domain::learning_mode_t::OFF);
+ OM::write(uuid, ext_bd);
+ interface ext_bvi("bvi-" +
+ std::to_string(ffwd.bdId),
+ interface::type_t::BVI,
+ interface::admin_state_t::UP,
+ ext_rd);
+ OM::write(uuid, ext_bvi);
+
+ /*
+ * Route for the floating IP via the internal
+ * EPG's recirc
+ */
+ route::prefix_t fp_pfx(floatingIp);
+ route::ip_route fp_route(
+ ext_rd,
+ fp_pfx,
+ {recirc_itf,
+ fp_pfx.l3_proto().to_nh_proto(),
+ route::path::flags_t::DVR});
+ OM::write(uuid, fp_route);
+
+ neighbour fp_ne(ext_bvi, floatingIp, {macAddr});
+ OM::write(uuid, fp_ne);
+
+ /*
+ * reply to ARP's for the floating IP
+ */
+ bridge_domain_arp_entry fp_bae(
+ ext_bd, floatingIp, {macAddr});
+ OM::write(uuid, fp_bae);
+
+ /*
+ * Bridge L2 packets addressed to the VM to the
+ * recirc
+ * interface
+ */
+ bridge_domain_entry fp_be(
+ ext_bd, macAddr, recirc_itf);
+ OM::write(uuid, fp_be);
+
+ /*
+ * NAT static mapping
+ */
+ nat_static ns(*rd, mappedIp, floatingIp);
+ OM::write(uuid, ns);
+ }
+ catch (EndPointGroupManager::NoFowardInfoException
+ &nofwd)
+ {
+ VLOGD << "Endpoint Floating IP no fwd: "
+ << nofwd.reason << " : " << uuid;
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (EndPointManager::NoEpInterfaceException &noepitf)
+ {
+ VLOGD << "Endpoint - no interface " << uuid;
+ }
+
+ /*
+ * If the EP is external then its EPG's BD interface is an external
+ * interface
+ */
+ if (is_external)
+ {
+ gbp_ext_itf gei(
+ *bvi, *gepg->get_bridge_domain(), *gepg->get_route_domain());
+ OM::write(uuid, gei);
+ }
+ }
+
+ /*
+ * That's all folks ... destructor of mark_n_sweep calls the
+ * sweep for the stale state
+ */
+}
+void
+EndPointManager::handle_update(const std::string &uuid)
+{
+ handle_update_i(uuid, false);
+}
+void
+EndPointManager::handle_external_update(const std::string &uuid)
+{
+ handle_update_i(uuid, true);
+}
+
+void
+EndPointManager::handle_remote_update(const std::string &uuid)
+{
+ VLOGE << "Endpoint - remote not supported: " << uuid;
+}
+
+}; // namespace VPP
diff --git a/src/VppExtItfManager.cpp b/src/VppExtItfManager.cpp
new file mode 100644
index 0000000..eb8fa9b
--- /dev/null
+++ b/src/VppExtItfManager.cpp
@@ -0,0 +1,188 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2018 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#include <boost/optional.hpp>
+
+#include <opflexagent/PolicyManager.h>
+
+#include <modelgbp/gbp/ExternalInterface.hpp>
+#include <modelgbp/gbp/L3ExternalDomain.hpp>
+#include <modelgbp/gbp/Subnet.hpp>
+
+#include <vom/bridge_domain_arp_entry.hpp>
+#include <vom/gbp_ext_itf.hpp>
+#include <vom/gbp_subnet.hpp>
+#include <vom/l3_binding.hpp>
+#include <vom/route.hpp>
+
+#include "VppEndPointGroupManager.hpp"
+#include "VppExtItfManager.hpp"
+#include "VppLog.hpp"
+#include "VppRouteManager.hpp"
+#include "VppUtil.hpp"
+
+using namespace VOM;
+
+namespace VPP
+{
+ExtItfManager::ExtItfManager(Runtime &runtime)
+ : m_runtime(runtime)
+{
+}
+
+void
+ExtItfManager::handle_update(const opflex::modb::URI &uri)
+{
+ OM::mark_n_sweep ms(uri.toString());
+ const std::string &uuid = uri.toString();
+
+ boost::optional<std::shared_ptr<modelgbp::gbp::ExternalInterface>> ext_itf =
+ modelgbp::gbp::ExternalInterface::resolve(
+ m_runtime.agent.getFramework(), uri);
+
+ if (!ext_itf)
+ {
+ VLOGD << "External-Interface; delete: " << uri;
+ return;
+ }
+ VLOGD << "External-Interface; update: " << uri;
+
+ boost::optional<std::shared_ptr<modelgbp::gbp::ExternalL3BridgeDomain>>
+ op_bd = m_runtime.policy_manager().getBDForExternalInterface(uri);
+
+ if (!op_bd)
+ {
+ VLOGE << "External-Interface; no ExternalBridgeDomain: " << uri;
+ return;
+ }
+
+ boost::optional<std::shared_ptr<modelgbp::gbp::RoutingDomain>> op_rd =
+ m_runtime.policy_manager().getRDForExternalInterface(uri);
+
+ if (!op_rd)
+ {
+ VLOGE << "External-Interface; no RouteDomain: " << uri;
+ return;
+ }
+
+ uint32_t rd_id = m_runtime.id_gen.get(
+ modelgbp::gbp::RoutingDomain::CLASS_ID, op_rd.get()->getURI());
+
+ route_domain rd(rd_id);
+ OM::write(uuid, rd);
+
+ uint32_t bd_id = m_runtime.id_gen.get(modelgbp::gbp::BridgeDomain::CLASS_ID,
+ op_bd.get()->getURI());
+
+ bridge_domain bd(bd_id, bridge_domain::learning_mode_t::OFF);
+ OM::write(uuid, bd);
+
+ /*
+ * Create a BVI interface for the EPG and add it to the bridge-domain
+ */
+ std::shared_ptr<interface> bvi = EndPointGroupManager::mk_bvi(
+ m_runtime, uuid, bd, rd, mac_from_modb(ext_itf.get()->getMac()));
+
+ /*
+ * Add the mcast tunnels for flooding
+ */
+ boost::optional<std::string> maddr =
+ m_runtime.policy_manager().getBDMulticastIPForExternalInterface(uri);
+ boost::optional<uint32_t> bd_vnid =
+ m_runtime.policy_manager().getBDVnidForExternalInterface(uri);
+ boost::optional<uint32_t> rd_vnid =
+ m_runtime.policy_manager().getRDVnidForExternalInterface(uri);
+
+ if (!(rd_vnid && bd_vnid && maddr))
+ {
+ VLOGE << "External-Interface; no VNI/mcast-address: " << uri;
+ return;
+ }
+
+ std::shared_ptr<vxlan_tunnel> vt_mc = EndPointGroupManager::mk_mcast_tunnel(
+ m_runtime, uuid, bd_vnid.get(), maddr.get());
+
+ /*
+ * there's no leanring of EPs in an external BD
+ */
+ gbp_bridge_domain gbd(
+ bd, *bvi, {}, vt_mc, gbp_bridge_domain::flags_t::DO_NOT_LEARN);
+ OM::write(uuid, gbd);
+ std::shared_ptr<VOM::gbp_route_domain> grd =
+ EndPointGroupManager::mk_gbp_rd(m_runtime, uuid, rd, rd_vnid.get());
+
+ /*
+ * the encap on the external-interface is a vlan ID
+ */
+ boost::optional<uint32_t> vlan_id = ext_itf.get()->getEncap();
+
+ if (vlan_id)
+ ;
+
+ /*
+ * Add the /32 to the BVI
+ */
+ boost::optional<const std::string &> s_addr = ext_itf.get()->getAddress();
+
+ if (!s_addr)
+ {
+ VLOGI << "External-Interface; no prefix: " << uri;
+ return;
+ }
+ boost::asio::ip::address p_addr =
+ boost::asio::ip::address::from_string(s_addr.get());
+
+ l3_binding l3b(*bvi, {p_addr});
+ OM::write(uuid, l3b);
+
+ bridge_domain_arp_entry bae(bd, p_addr, bvi->l2_address().to_mac());
+ OM::write(uuid, bae);
+
+ opflexagent::PolicyManager::subnet_vector_t subnets;
+
+ m_runtime.policy_manager().getSubnetsForExternalInterface(uri, subnets);
+
+ for (auto sn : subnets)
+ {
+ if (!sn->getPrefixLen() || !sn->getAddress()) continue;
+
+ route::prefix_t pfx(sn->getAddress().get(), sn->getPrefixLen().get());
+
+ route::ip_route ipr(rd, pfx, {route::path::special_t::DROP});
+ OM::write(uuid, ipr);
+ }
+
+ /*
+ * This BVI is the ExternalInterface
+ */
+ gbp_ext_itf gei(*bvi, gbd, *grd);
+ OM::write(uuid, gei);
+
+ /*
+ * Add any external networks
+ */
+ boost::optional<std::shared_ptr<modelgbp::gbp::L3ExternalDomain>> ext_dom =
+ m_runtime.policy_manager().getExternalDomainForExternalInterface(uri);
+
+ if (!ext_dom)
+ {
+ VLOGI << "External-Interface; no ExternalDomain: " << uri;
+ return;
+ }
+
+ RouteManager::mk_ext_nets(m_runtime, rd, uri, ext_dom.get());
+}
+
+}; // namepsace VPP
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
diff --git a/src/VppIdGen.cpp b/src/VppIdGen.cpp
new file mode 100644
index 0000000..f0833a7
--- /dev/null
+++ b/src/VppIdGen.cpp
@@ -0,0 +1,94 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#include "VppIdGen.hpp"
+
+#include <modelgbp/gbp/BridgeDomain.hpp>
+#include <modelgbp/gbp/Contract.hpp>
+#include <modelgbp/gbp/FloodDomain.hpp>
+#include <modelgbp/gbp/L3ExternalNetwork.hpp>
+#include <modelgbp/gbp/RoutingDomain.hpp>
+
+namespace VPP
+{
+static const char *ID_NAMESPACES[] = {"floodDomain",
+ "bridgeDomain",
+ "routingDomain",
+ "contract",
+ "externalNetwork",
+ "secGroup",
+ "secGroupSet"};
+
+static const char *ID_NMSPC_FD = ID_NAMESPACES[0];
+static const char *ID_NMSPC_BD = ID_NAMESPACES[1];
+static const char *ID_NMSPC_RD = ID_NAMESPACES[2];
+static const char *ID_NMSPC_CON = ID_NAMESPACES[3];
+static const char *ID_NMSPC_EXTNET = ID_NAMESPACES[4];
+static const char *ID_NMSPC_SECGROUP = ID_NAMESPACES[5];
+static const char *ID_NMSPC_SECGROUP_SET = ID_NAMESPACES[6];
+
+IdGen::IdGen(opflexagent::IdGenerator &id_gen)
+ : m_id_gen(id_gen)
+{
+ for (size_t i = 0; i < sizeof(ID_NAMESPACES) / sizeof(char *); i++)
+ {
+ /*
+ * start the namespace ID's at a non-zero offset so the
+ * default tables are never used.
+ */
+ m_id_gen.initNamespace(ID_NAMESPACES[i], 100);
+ }
+}
+
+uint32_t
+IdGen::get(opflex::modb::class_id_t cid, const opflex::modb::URI &uri)
+{
+ return m_id_gen.getId(get_namespace(cid), uri.toString());
+}
+
+void
+IdGen::erase(opflex::modb::class_id_t cid, const opflex::modb::URI &uri)
+{
+ m_id_gen.erase(get_namespace(cid), uri.toString());
+}
+
+const char *
+IdGen::get_namespace(opflex::modb::class_id_t cid)
+{
+ const char *nmspc = NULL;
+ switch (cid)
+ {
+ case modelgbp::gbp::RoutingDomain::CLASS_ID:
+ nmspc = ID_NMSPC_RD;
+ break;
+ case modelgbp::gbp::BridgeDomain::CLASS_ID:
+ nmspc = ID_NMSPC_BD;
+ break;
+ case modelgbp::gbp::FloodDomain::CLASS_ID:
+ nmspc = ID_NMSPC_FD;
+ break;
+ case modelgbp::gbp::Contract::CLASS_ID:
+ nmspc = ID_NMSPC_CON;
+ break;
+ case modelgbp::gbp::L3ExternalNetwork::CLASS_ID:
+ nmspc = ID_NMSPC_EXTNET;
+ break;
+ default:
+ assert(false);
+ }
+ return nmspc;
+}
+
+} // namespace VPP
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
diff --git a/src/VppInspect.cpp b/src/VppInspect.cpp
new file mode 100644
index 0000000..4f8af3f
--- /dev/null
+++ b/src/VppInspect.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#include <boost/algorithm/string.hpp>
+#include <cassert>
+#include <string>
+#include <vector>
+
+#include "VppInspect.hpp"
+#include "vom/inspect.hpp"
+#include <opflexagent/logging.h>
+
+namespace opflexagent
+{
+
+VppInspect::VppInspect(const std::string &sock_name)
+ : mSockName(sock_name)
+ , mInspect()
+{
+ int rc;
+ uv_loop_init(&mServerLoop);
+ mServerLoop.data = this;
+
+ rc = uv_async_init(&mServerLoop, &mAsync, VppInspect::on_cleanup);
+
+ rc = uv_thread_create(&mServerThread, run, this);
+ if (rc < 0)
+ {
+ LOG(ERROR) << "inspect - thread create error:" << uv_strerror(rc);
+ }
+}
+
+VppInspect::~VppInspect()
+{
+ uv_async_send(&mAsync);
+ uv_thread_join(&mServerThread);
+ uv_loop_close(&mServerLoop);
+
+ LOG(INFO) << "inspect - close";
+}
+
+void
+VppInspect::on_cleanup(uv_async_t *handle)
+{
+ VppInspect *ins = static_cast<VppInspect *>(handle->loop->data);
+
+ uv_stop(&ins->mServerLoop);
+}
+
+void
+VppInspect::run(void *ctx)
+{
+ VppInspect *ins = static_cast<VppInspect *>(ctx);
+ uv_pipe_t server;
+ int rv;
+
+ /* remove the request file if it exists already */
+ unlink(ins->mSockName.c_str());
+
+ uv_pipe_init(&ins->mServerLoop, &server, 0);
+
+ LOG(INFO) << "inspect - open:" << ins->mSockName;
+
+ if ((rv = uv_pipe_bind(&server, ins->mSockName.c_str())))
+ {
+ LOG(ERROR) << "inspect - Bind error:" << uv_err_name(rv);
+ return;
+ }
+ if ((rv = uv_listen((uv_stream_t *)&server, 1, on_connection)))
+ {
+ LOG(ERROR) << "inspect - Listen error:" << uv_err_name(rv);
+ return;
+ }
+
+ uv_run(&ins->mServerLoop, UV_RUN_DEFAULT);
+ uv_close((uv_handle_t *)&server, NULL);
+}
+
+VppInspect::write_req_t::~write_req_t()
+{
+ free(buf.base);
+}
+
+VppInspect::write_req_t::write_req_t(std::ostringstream &output)
+{
+ buf.len = output.str().length();
+ buf.base = (char *)malloc(buf.len);
+ memcpy(buf.base, output.str().c_str(), buf.len);
+}
+
+void
+VppInspect::on_alloc_buffer(uv_handle_t *handle, size_t size, uv_buf_t *buf)
+{
+ buf->base = (char *)malloc(size);
+ buf->len = size;
+}
+
+void
+VppInspect::on_write(uv_write_t *req, int status)
+{
+ write_req_t *wr = (write_req_t *)req;
+
+ if (status < 0)
+ {
+ LOG(ERROR) << "inspect - Write error:" << uv_err_name(status);
+ }
+
+ delete wr;
+}
+
+void
+VppInspect::do_write(uv_stream_t *client, std::ostringstream &output)
+{
+ write_req_t *req = new write_req_t(output);
+
+ uv_write((uv_write_t *)req, client, &req->buf, 1, on_write);
+}
+
+void
+VppInspect::on_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf)
+{
+ VppInspect *ins = static_cast<VppInspect *>(client->loop->data);
+
+ if (nread > 0)
+ {
+ std::ostringstream output;
+ std::string message(buf->base);
+ message = message.substr(0, nread);
+ boost::trim(message);
+
+ if (message.length())
+ {
+ ins->mInspect.handle_input(message, output);
+ }
+ output << "# ";
+
+ do_write((uv_stream_t *)client, output);
+ }
+ else if (nread < 0)
+ {
+ if (nread != UV_EOF)
+ {
+ LOG(ERROR) << "inspect - Read error:" << uv_err_name(nread);
+ }
+ uv_close((uv_handle_t *)client, NULL);
+ }
+
+ free(buf->base);
+}
+
+void
+VppInspect::on_connection(uv_stream_t *server, int status)
+{
+ VppInspect *ins = static_cast<VppInspect *>(server->loop->data);
+
+ if (status == -1)
+ {
+ // error!
+ return;
+ }
+
+ uv_pipe_t *client = (uv_pipe_t *)malloc(sizeof(uv_pipe_t));
+ uv_pipe_init(&ins->mServerLoop, client, 0);
+
+ if (uv_accept(server, (uv_stream_t *)client) == 0)
+ {
+ std::ostringstream output;
+
+ output << "Welcome: VPP inspect" << std::endl;
+ output << "# ";
+
+ do_write((uv_stream_t *)client, output);
+
+ uv_read_start((uv_stream_t *)client,
+ VppInspect::on_alloc_buffer,
+ VppInspect::on_read);
+ }
+ else
+ {
+ uv_close((uv_handle_t *)client, NULL);
+ }
+}
+
+} // namespace opflexagent
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
diff --git a/src/VppLogHandler.cpp b/src/VppLogHandler.cpp
new file mode 100644
index 0000000..3fdd214
--- /dev/null
+++ b/src/VppLogHandler.cpp
@@ -0,0 +1,44 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Implementation for VppLogHandler class.
+ *
+ * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#include <iostream>
+
+#include <opflexagent/logging.h>
+
+#include "VppLogHandler.hpp"
+
+namespace VPP
+{
+
+void
+LogHandler::handle_message(const std::string &file,
+ const int line,
+ const std::string &function,
+ const VOM::log_level_t &level,
+ const std::string &message)
+{
+ opflexagent::LogLevel agentLevel = opflexagent::INFO;
+
+ if (VOM::log_level_t::DEBUG == level)
+ agentLevel = opflexagent::DEBUG;
+ else if (VOM::log_level_t::INFO == level)
+ agentLevel = opflexagent::INFO;
+ else if (VOM::log_level_t::WARNING == level)
+ agentLevel = opflexagent::WARNING;
+ else if (VOM::log_level_t::ERROR == level)
+ agentLevel = opflexagent::ERROR;
+ else if (VOM::log_level_t::CRITICAL == level)
+ agentLevel = opflexagent::FATAL;
+
+ LOG1(agentLevel, file.c_str(), line, function.c_str(), message);
+}
+
+} /* namespace opflexagent */
diff --git a/src/VppManager.cpp b/src/VppManager.cpp
new file mode 100644
index 0000000..2040db4
--- /dev/null
+++ b/src/VppManager.cpp
@@ -0,0 +1,566 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2018 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#include <memory>
+#include <sstream>
+#include <string>
+
+#include <boost/asio/ip/host_name.hpp>
+#include <boost/asio/placeholders.hpp>
+#include <boost/functional/hash.hpp>
+#include <boost/system/error_code.hpp>
+
+#include "VppContractManager.hpp"
+#include "VppEndPointGroupManager.hpp"
+#include "VppEndPointManager.hpp"
+#include "VppExtItfManager.hpp"
+#include "VppIdGen.hpp"
+#include "VppLog.hpp"
+#include "VppManager.hpp"
+#include "VppRouteManager.hpp"
+#include "VppSecurityGroupManager.hpp"
+
+#include <opflexagent/EndpointManager.h>
+
+using std::bind;
+using boost::asio::placeholders::error;
+
+namespace VPP
+{
+/**
+ * An owner of the objects VPP learns during boot-up
+ */
+static const std::string BOOT_KEY = "__boot__";
+
+VppManager::VppManager(opflexagent::Agent &agent_,
+ opflexagent::IdGenerator &idGen_,
+ VOM::HW::cmd_q *q,
+ VOM::stat_reader *sr)
+ : m_runtime(agent_, idGen_)
+ , m_task_queue(agent_.getAgentIOService())
+ , stopping(false)
+{
+ VOM::HW::init(q, sr);
+ VOM::OM::init();
+
+ m_runtime.system_name = boost::asio::ip::host_name();
+ m_runtime.agent.getFramework().registerPeerStatusListener(this);
+}
+
+VppManager::~VppManager()
+{
+ VLOGE << "VppManager exiting";
+}
+
+void
+VppManager::start()
+{
+ VLOGI << "start vpp manager; mode:"
+ << (int)m_runtime.agent.getRendererForwardingMode();
+
+ /*
+ * create the update delegators
+ */
+ m_runtime.is_transport_mode =
+ (opflex::ofcore::OFConstants::TRANSPORT_MODE ==
+ m_runtime.agent.getRendererForwardingMode());
+ m_epm = std::make_shared<EndPointManager>(m_runtime);
+ m_epgm = std::make_shared<EndPointGroupManager>(m_runtime);
+ m_sgm = std::make_shared<SecurityGroupManager>(m_runtime.agent);
+ m_cm = std::make_shared<ContractManager>(m_runtime.agent, m_runtime.id_gen);
+ m_rdm = std::make_shared<RouteManager>(m_runtime);
+ m_eim = std::make_shared<ExtItfManager>(m_runtime);
+
+ initPlatformConfig();
+
+ /*
+ * make sure the first event in the task Q is the blocking
+ * connection initiation to VPP ...
+ */
+ m_task_queue.dispatch("init-connection",
+ bind(&VppManager::handleInitConnection, this));
+
+ /**
+ * DO BOOT
+ */
+
+ /**
+ * ... followed by vpp boot dump
+ */
+ m_task_queue.dispatch("boot-dump", bind(&VppManager::handleBoot, this));
+
+ /**
+ * ... followed by uplink configuration
+ */
+ m_task_queue.dispatch("uplink-configure",
+ bind(&VppManager::handleUplinkConfigure, this));
+
+ /**
+ * ... followed by cross connect configuration
+ */
+ m_task_queue.dispatch("xconnect-configure",
+ bind(&VppManager::handleXConnectConfigure, this));
+}
+
+void
+VppManager::handleCloseConnection()
+{
+ if (!hw_connected) return;
+
+ VOM::interface::disable_events();
+ VOM::HW::disconnect();
+
+ VLOGD << "Close VPP connection";
+}
+
+void
+VppManager::handleInitConnection()
+{
+ if (stopping) return;
+
+ VLOGD << "Open VPP connection";
+
+ while (VOM::HW::connect() != true)
+ ;
+
+ hw_connected = true;
+
+ /**
+ * We are insterested in getting interface events from VPP
+ */
+ VOM::interface::enable_events(*this);
+
+ /**
+ * Scehdule a timer to Poll for HW livensss
+ */
+ m_poll_timer.reset(
+ new boost::asio::deadline_timer(m_runtime.agent.getAgentIOService()));
+ m_poll_timer->expires_from_now(boost::posix_time::seconds(3));
+ m_poll_timer->async_wait(bind(&VppManager::handleHWPollTimer, this, error));
+
+ /**
+ * Scehdule a timer for HW stats
+ */
+ m_stats_timer.reset(
+ new boost::asio::deadline_timer(m_runtime.agent.getAgentIOService()));
+ m_stats_timer->expires_from_now(boost::posix_time::seconds(5));
+ m_stats_timer->async_wait(
+ bind(&VppManager::handleHWStatsTimer, this, error));
+}
+
+void
+VppManager::handleUplinkConfigure()
+{
+ if (stopping) return;
+
+ m_runtime.uplink.configure(m_runtime.system_name);
+}
+
+void
+VppManager::handleXConnectConfigure()
+{
+ if (stopping) return;
+
+ m_xconnect.configure_xconnect();
+}
+
+void
+VppManager::handleSweepTimer(const boost::system::error_code &ec)
+{
+ if (stopping || ec) return;
+
+ VLOGI << "sweep boot data";
+
+ /*
+ * the sweep timer was not cancelled, continue with purging old state.
+ */
+ if (hw_connected)
+ VOM::OM::sweep(BOOT_KEY);
+ else if (!stopping)
+ {
+ m_sweep_timer.reset(new boost::asio::deadline_timer(
+ m_runtime.agent.getAgentIOService()));
+ m_sweep_timer->expires_from_now(boost::posix_time::seconds(30));
+ m_sweep_timer->async_wait(
+ bind(&VppManager::handleSweepTimer, this, error));
+ }
+}
+
+void
+VppManager::handleHWPollTimer(const boost::system::error_code &ec)
+{
+ if (stopping || ec) return;
+
+ if (hw_connected && VOM::HW::poll())
+ {
+ /*
+ * re-scehdule a timer to Poll for HW liveness
+ */
+ m_poll_timer.reset(new boost::asio::deadline_timer(
+ m_runtime.agent.getAgentIOService()));
+ m_poll_timer->expires_from_now(boost::posix_time::seconds(3));
+ m_poll_timer->async_wait(
+ bind(&VppManager::handleHWPollTimer, this, error));
+ return;
+ }
+
+ hw_connected = false;
+ VOM::HW::disconnect();
+ VLOGD << "Reconnecting ....";
+ if (VOM::HW::connect())
+ {
+ VLOGD << "Replay the state after reconnecting ...";
+ VOM::OM::replay();
+ hw_connected = true;
+ }
+
+ if (!stopping)
+ {
+ m_poll_timer.reset(new boost::asio::deadline_timer(
+ m_runtime.agent.getAgentIOService()));
+ m_poll_timer->expires_from_now(boost::posix_time::seconds(1));
+ m_poll_timer->async_wait(
+ bind(&VppManager::handleHWPollTimer, this, error));
+ }
+ else
+ {
+ VOM::HW::disconnect();
+ }
+}
+
+void
+VppManager::handleHWStatsTimer(const boost::system::error_code &ec)
+{
+ if (stopping || ec) return;
+
+ VLOGD << "stats reading";
+
+ VOM::HW::read_stats();
+
+ m_stats_timer.reset(
+ new boost::asio::deadline_timer(m_runtime.agent.getAgentIOService()));
+ m_stats_timer->expires_from_now(boost::posix_time::seconds(5));
+ m_stats_timer->async_wait(
+ bind(&VppManager::handleHWStatsTimer, this, error));
+}
+
+void
+VppManager::handleBoot()
+{
+ if (stopping) return;
+
+ /**
+ * Read the state from VPP
+ */
+ VOM::OM::populate(BOOT_KEY);
+}
+
+void
+VppManager::registerModbListeners()
+{
+ // Initialize policy listeners
+ m_runtime.agent.getEndpointManager().registerListener(this);
+ m_runtime.agent.getServiceManager().registerListener(this);
+ m_runtime.agent.getExtraConfigManager().registerListener(this);
+ m_runtime.agent.getPolicyManager().registerListener(this);
+}
+
+void
+VppManager::stop()
+{
+ stopping = true;
+
+ m_runtime.agent.getEndpointManager().unregisterListener(this);
+ m_runtime.agent.getServiceManager().unregisterListener(this);
+ m_runtime.agent.getExtraConfigManager().unregisterListener(this);
+ m_runtime.agent.getPolicyManager().unregisterListener(this);
+
+ if (m_stats_timer)
+ {
+ m_stats_timer->cancel();
+ }
+
+ if (m_sweep_timer)
+ {
+ m_sweep_timer->cancel();
+ }
+
+ if (m_poll_timer)
+ {
+ m_poll_timer->cancel();
+ }
+
+ m_task_queue.dispatch("close-connection",
+ bind(&VppManager::handleCloseConnection, this));
+
+ VLOGD << "stop VppManager";
+}
+
+void
+VppManager::setVirtualRouter(bool virtualRouterEnabled,
+ bool routerAdv,
+ const std::string &virtualRouterMac)
+{
+ if (virtualRouterEnabled)
+ {
+ m_runtime.vr = std::make_shared<VirtualRouter>(virtualRouterMac);
+ }
+}
+
+void
+VppManager::endpointUpdated(const std::string &uuid)
+{
+ if (stopping) return;
+
+ m_task_queue.dispatch(uuid,
+ bind(&EndPointManager::handle_update, m_epm, uuid));
+}
+
+void
+VppManager::externalEndpointUpdated(const std::string &uuid)
+{
+ if (stopping) return;
+
+ m_task_queue.dispatch(
+ uuid, bind(&EndPointManager::handle_external_update, m_epm, uuid));
+}
+
+void
+VppManager::remoteEndpointUpdated(const std::string &uuid)
+{
+ if (stopping) return;
+
+ m_task_queue.dispatch(
+ uuid, bind(&EndPointManager::handle_remote_update, m_epm, uuid));
+}
+
+void
+VppManager::serviceUpdated(const std::string &uuid)
+{
+ if (stopping) return;
+
+ VLOGI << "Service Update Not supported ";
+}
+
+void
+VppManager::rdConfigUpdated(const opflex::modb::URI &rdURI)
+{
+ m_task_queue.dispatch(
+ rdURI.toString(),
+ bind(&RouteManager::handle_domain_update, m_rdm, rdURI));
+}
+
+void
+VppManager::egDomainUpdated(const opflex::modb::URI &egURI)
+{
+ if (stopping) return;
+
+ m_task_queue.dispatch(
+ egURI.toString(),
+ bind(&EndPointGroupManager::handle_update, m_epgm, egURI));
+}
+
+void
+VppManager::domainUpdated(opflex::modb::class_id_t cid,
+ const opflex::modb::URI &domURI)
+{
+ if (stopping) return;
+
+ m_task_queue.dispatch(
+ domURI.toString(),
+ bind(&VppManager::handleDomainUpdate, this, cid, domURI));
+}
+
+void
+VppManager::secGroupSetUpdated(const EndpointListener::uri_set_t &secGrps)
+{
+ if (stopping) return;
+ m_task_queue.dispatch(
+ "setSecGrp:",
+ std::bind(&SecurityGroupManager::handle_set_update, m_sgm, secGrps));
+}
+
+void
+VppManager::secGroupUpdated(const opflex::modb::URI &uri)
+{
+ if (stopping) return;
+ m_task_queue.dispatch(
+ "secGrp:", std::bind(&SecurityGroupManager::handle_update, m_sgm, uri));
+}
+
+void
+VppManager::contractUpdated(const opflex::modb::URI &contractURI)
+{
+ if (stopping) return;
+ m_task_queue.dispatch(
+ contractURI.toString(),
+ bind(&ContractManager::handle_update, m_cm, contractURI));
+}
+
+void
+VppManager::externalInterfaceUpdated(const opflex::modb::URI &uri)
+{
+ if (stopping) return;
+ m_task_queue.dispatch(uri.toString(),
+ bind(&ExtItfManager::handle_update, m_eim, uri));
+}
+
+void
+VppManager::localRouteUpdated(const opflex::modb::URI &uri)
+{
+ if (stopping) return;
+ m_task_queue.dispatch(uri.toString(),
+ bind(&RouteManager::handle_route_update, m_rdm, uri));
+}
+
+void
+VppManager::handle_interface_event(std::vector<VOM::interface::event> e)
+{
+ if (stopping) return;
+ m_task_queue.dispatch("InterfaceEvent",
+ bind(&VppManager::handleInterfaceEvent, this, e));
+}
+
+void
+VppManager::configUpdated(const opflex::modb::URI &configURI)
+{
+ VLOGI << "Config Updated ";
+ if (stopping) return;
+ m_runtime.agent.getAgentIOService().dispatch(
+ bind(&VppManager::handleConfigUpdate, this, configURI));
+}
+
+void
+VppManager::portStatusUpdate(const std::string &portName,
+ uint32_t portNo,
+ bool fromDesc)
+{
+ if (stopping) return;
+ m_runtime.agent.getAgentIOService().dispatch(
+ bind(&VppManager::handlePortStatusUpdate, this, portName, portNo));
+}
+
+void
+VppManager::peerStatusUpdated(const std::string &, int, PeerStatus peerStatus)
+{
+ if (stopping) return;
+}
+
+void
+VppManager::handleDomainUpdate(opflex::modb::class_id_t cid,
+ const opflex::modb::URI &domURI)
+{
+ if (stopping) return;
+
+ VLOGD << "Updating domain: " << domURI;
+
+ switch (cid)
+ {
+ case modelgbp::gbp::RoutingDomain::CLASS_ID:
+ m_rdm->handle_domain_update(domURI);
+ break;
+ case modelgbp::gbp::Subnet::CLASS_ID:
+ if (!modelgbp::gbp::Subnet::resolve(m_runtime.agent.getFramework(),
+ domURI))
+ {
+ VLOGD << "Cleaning up for Subnet: " << domURI;
+ }
+ break;
+ case modelgbp::gbp::BridgeDomain::CLASS_ID:
+ if (!modelgbp::gbp::BridgeDomain::resolve(
+ m_runtime.agent.getFramework(), domURI))
+ {
+ VLOGD << "Cleaning up for BD: " << domURI;
+ m_runtime.id_gen.erase(cid, domURI);
+ }
+ break;
+ case modelgbp::gbp::FloodDomain::CLASS_ID:
+ if (!modelgbp::gbp::FloodDomain::resolve(m_runtime.agent.getFramework(),
+ domURI))
+ {
+ VLOGD << "Cleaning up for FD: " << domURI;
+ m_runtime.id_gen.erase(cid, domURI);
+ }
+ break;
+ case modelgbp::gbp::L3ExternalNetwork::CLASS_ID:
+ if (!modelgbp::gbp::L3ExternalNetwork::resolve(
+ m_runtime.agent.getFramework(), domURI))
+ {
+ VLOGD << "Cleaning up for L3ExtNet: " << domURI;
+ m_runtime.id_gen.erase(cid, domURI);
+ }
+ break;
+ }
+}
+
+void
+VppManager::handleInterfaceEvent(std::vector<VOM::interface::event> events)
+{
+ if (stopping) return;
+
+ for (auto &e : events)
+ {
+ VLOGD << "Interface Event: " << e.itf.to_string()
+ << " state: " << e.state.to_string();
+ }
+}
+
+void
+VppManager::initPlatformConfig()
+{
+ boost::optional<std::shared_ptr<modelgbp::platform::Config>> config =
+ modelgbp::platform::Config::resolve(
+ m_runtime.agent.getFramework(),
+ m_runtime.agent.getPolicyManager().getOpflexDomain());
+}
+
+void
+VppManager::handleConfigUpdate(const opflex::modb::URI &configURI)
+{
+ VLOGD << "Updating platform config " << configURI;
+ if (stopping) return;
+
+ initPlatformConfig();
+
+ /**
+ * Now that we are known to be opflex connected,
+ * Scehdule a timer to sweep the state we read when we first connected
+ * to VPP.
+ */
+ m_sweep_timer.reset(
+ new boost::asio::deadline_timer(m_runtime.agent.getAgentIOService()));
+ m_sweep_timer->expires_from_now(boost::posix_time::seconds(30));
+ m_sweep_timer->async_wait(bind(&VppManager::handleSweepTimer, this, error));
+}
+
+void
+VppManager::handlePortStatusUpdate(const std::string &portName, uint32_t)
+{
+ VLOGD << "Port-status update for " << portName;
+ if (stopping) return;
+}
+
+Uplink &
+VppManager::uplink()
+{
+ return m_runtime.uplink;
+}
+CrossConnect &
+VppManager::crossConnect()
+{
+ return m_xconnect;
+}
+
+}; // namespace opflexagent
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
diff --git a/src/VppRenderer.cpp b/src/VppRenderer.cpp
new file mode 100644
index 0000000..b892595
--- /dev/null
+++ b/src/VppRenderer.cpp
@@ -0,0 +1,325 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Implementation for VPPRenderer class
+ *
+ * Copyright (c) 2018 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+#include <boost/asio/placeholders.hpp>
+
+#include <opflexagent/logging.h>
+
+#include <vom/stat_reader.hpp>
+
+#include "VppLogHandler.hpp"
+#include "VppRenderer.hpp"
+
+namespace VPP
+{
+
+using boost::asio::placeholders::error;
+using boost::property_tree::ptree;
+using opflex::ofcore::OFFramework;
+using std::bind;
+
+VppRendererPlugin::VppRendererPlugin()
+{
+}
+
+std::unordered_set<std::string>
+VppRendererPlugin::getNames() const
+{
+ return {"vpp"};
+}
+
+opflexagent::Renderer *
+VppRendererPlugin::create(opflexagent::Agent &agent) const
+{
+ IdGenerator *idGen = new IdGenerator();
+ VOM::HW::cmd_q *vppQ = new VOM::HW::cmd_q();
+ VOM::stat_reader *vppSR = new stat_reader();
+ VppManager *vppManager = new VppManager(agent, *idGen, vppQ, vppSR);
+ return new VppRenderer(agent, *idGen, vppManager);
+}
+
+VPP::LogHandler vppLogHandler;
+
+static VOM::log_level_t
+agentLevelToVom(opflexagent::LogLevel level)
+{
+ switch (level)
+ {
+ case opflexagent::DEBUG:
+ return (VOM::log_level_t::DEBUG);
+ case opflexagent::INFO:
+ return (VOM::log_level_t::INFO);
+ case opflexagent::WARNING:
+ return (VOM::log_level_t::WARNING);
+ case opflexagent::ERROR:
+ return (VOM::log_level_t::ERROR);
+ case opflexagent::FATAL:
+ return (VOM::log_level_t::CRITICAL);
+ }
+ return (VOM::log_level_t::INFO);
+}
+
+VppRenderer::VppRenderer(opflexagent::Agent &agent,
+ IdGenerator &idGen,
+ VppManager *vppManager)
+ : Renderer(agent)
+ , idGen(idGen)
+ , vppManager(vppManager)
+ , tunnelEpManager(&agent)
+ , started(false)
+{
+ LOG(INFO) << "Vpp Renderer";
+
+ /*
+ * Register the call back handler for VOM logging and set the level
+ * according to the agent's settings
+ */
+ VOM::logger().set(agentLevelToVom(opflexagent::logLevel));
+ VOM::logger().set(&vppLogHandler);
+}
+
+VppRenderer::~VppRenderer()
+{
+ delete vppManager;
+}
+
+void
+VppRenderer::setProperties(const boost::property_tree::ptree &properties)
+{
+ // Set configuration from property tree. This configuration will
+ // be from a "renderers": { "vpp" { } } block from the agent
+ // configuration. Multiple calls are possible; later calls are
+ // merged with prior calls, overwriting any previously-set values.
+ LOG(opflexagent::INFO) << "Setting configuration for vpp renderer";
+ static const std::string ENCAP_VXLAN("encap.vxlan");
+ static const std::string ENCAP_IVXLAN("encap.ivxlan");
+ static const std::string ENCAP_VLAN("encap.vlan");
+ static const std::string UPLINK_IFACE("uplink-iface");
+ static const std::string UPLINK_SLAVES("uplink-slaves");
+ static const std::string UPLINK_VLAN("uplink-vlan");
+ static const std::string DHCP_OPTIONS("dhcp-opt");
+ static const std::string ENCAP_IFACE("encap-iface");
+ static const std::string REMOTE_IP("remote-ip");
+ static const std::string REMOTE_PORT("remote-port");
+ static const std::string VIRTUAL_ROUTER("forwarding"
+ ".virtual-router.enabled");
+ static const std::string VIRTUAL_ROUTER_MAC("forwarding"
+ ".virtual-router.mac");
+ static const std::string VIRTUAL_ROUTER_RA("forwarding.virtual-router"
+ ".ipv6.router-advertisement");
+ static const std::string CROSS_CONNECT("x-connect");
+ static const std::string EAST("east");
+ static const std::string WEST("west");
+ static const std::string EIFACE("iface");
+ static const std::string EVLAN("vlan");
+ static const std::string EIP("ip-address");
+ static const std::string ETAG_REWRITE("tag-rewrite");
+ static const std::string WIFACE("iface");
+ static const std::string WVLAN("vlan");
+ static const std::string WIP("ip-address");
+
+ auto vxlan = properties.get_child_optional(ENCAP_VXLAN);
+ auto ivxlan = properties.get_child_optional(ENCAP_IVXLAN);
+ auto vlan = properties.get_child_optional(ENCAP_VLAN);
+ auto vr = properties.get_child_optional(VIRTUAL_ROUTER);
+ auto x_connect = properties.get_child_optional(CROSS_CONNECT);
+
+ if (vlan)
+ {
+ uplinkIface = vlan.get().get<std::string>(UPLINK_IFACE, "");
+ uplinkVlan = vlan.get().get<uint16_t>(UPLINK_VLAN, 0);
+ vppManager->uplink().set(uplinkIface,
+ uplinkVlan,
+ vlan.get().get<std::string>(ENCAP_IFACE, ""));
+ auto slaves = vlan.get().get_child_optional(UPLINK_SLAVES);
+
+ if (slaves)
+ {
+ for (auto s : slaves.get())
+ {
+ vppManager->uplink().insert_slave_ifaces(s.second.data());
+ LOG(opflexagent::INFO) << s.second.data();
+ }
+ }
+ auto dhcp_options = vlan.get().get_child_optional(DHCP_OPTIONS);
+ if (dhcp_options)
+ {
+ for (auto d : dhcp_options.get())
+ {
+ vppManager->uplink().insert_dhcp_options(d.second.data());
+ LOG(opflexagent::INFO) << d.second.data();
+ }
+ }
+ encapType = encapTypeVlan;
+ }
+ else if (vxlan)
+ {
+ boost::asio::ip::address remote_ip;
+ boost::system::error_code ec;
+
+ remote_ip = boost::asio::ip::address::from_string(
+ vxlan.get().get<std::string>(REMOTE_IP, ""));
+
+ if (ec)
+ {
+ LOG(ERROR) << "Invalid tunnel destination IP: "
+ << vxlan.get().get<std::string>(REMOTE_IP, "") << ": "
+ << ec.message();
+ }
+ else
+ {
+ uplinkIface = vxlan.get().get<std::string>(UPLINK_IFACE, "");
+ uplinkVlan = vxlan.get().get<uint16_t>(UPLINK_VLAN, 0);
+ vppManager->uplink().set(
+ uplinkIface,
+ uplinkVlan,
+ vxlan.get().get<std::string>(ENCAP_IFACE, ""),
+ remote_ip,
+ vxlan.get().get<uint16_t>(REMOTE_PORT, 4789));
+ auto slaves = properties.get_child_optional(UPLINK_SLAVES);
+ }
+ encapType = encapTypeVxlan;
+ }
+ else if (ivxlan)
+ {
+ uplinkIface = ivxlan.get().get<std::string>(UPLINK_IFACE, "");
+ uplinkVlan = ivxlan.get().get<uint16_t>(UPLINK_VLAN, 0);
+ vppManager->uplink().set(
+ uplinkIface,
+ uplinkVlan,
+ ivxlan.get().get<std::string>(ENCAP_IFACE, ""));
+ auto slaves = ivxlan.get().get_child_optional(UPLINK_SLAVES);
+
+ if (slaves)
+ {
+ for (auto s : slaves.get())
+ {
+ vppManager->uplink().insert_slave_ifaces(s.second.data());
+ LOG(opflexagent::INFO) << s.second.data();
+ }
+ }
+ auto dhcp_options = ivxlan.get().get_child_optional(DHCP_OPTIONS);
+ if (dhcp_options)
+ {
+ for (auto d : dhcp_options.get())
+ {
+ vppManager->uplink().insert_dhcp_options(d.second.data());
+ LOG(opflexagent::INFO) << d.second.data();
+ }
+ }
+ encapType = encapTypeIvxlan;
+ }
+ if (vr)
+ {
+ vppManager->setVirtualRouter(
+ vr.get().get<bool>(VIRTUAL_ROUTER, true),
+ vr.get().get<bool>(VIRTUAL_ROUTER_RA, true),
+ vr.get().get<std::string>(VIRTUAL_ROUTER_MAC, "00:22:bd:f8:19:ff"));
+ }
+
+ if (x_connect)
+ {
+ for (auto x : x_connect.get())
+ {
+ auto east = x.second.get_child_optional(EAST);
+ auto west = x.second.get_child_optional(WEST);
+ if (east && west)
+ {
+ std::string ename = east.get().get<std::string>(EIFACE, "");
+ uint16_t evlan = east.get().get<uint16_t>(EVLAN, 0);
+ std::string eip = east.get().get<std::string>(EIP, "0.0.0.0");
+ std::string etag_rewrite =
+ east.get().get<std::string>(ETAG_REWRITE, "");
+ std::string wname = west.get().get<std::string>(WIFACE, "");
+ uint16_t wvlan = west.get().get<uint16_t>(WVLAN, 0);
+ std::string wip = west.get().get<std::string>(WIP, "0.0.0.0");
+ LOG(opflexagent::DEBUG) << "east:[" << ename << " , " << evlan
+ << " , " << eip << " , " << etag_rewrite
+ << "]";
+ LOG(opflexagent::DEBUG) << "west:[" << wname << " , " << wvlan
+ << " , " << wip << "]";
+ if (!ename.empty() && !wname.empty())
+ {
+ VPP::CrossConnect::xconnect_t xcon_east(
+ ename, evlan, eip, etag_rewrite);
+ VPP::CrossConnect::xconnect_t xcon_west(wname, wvlan, wip);
+ vppManager->crossConnect().insert_xconnect(
+ std::make_pair(xcon_east, xcon_west));
+ }
+ }
+ }
+ }
+
+ /*
+ * Are we opening an inspection socket?
+ */
+ auto inspect = properties.get<std::string>("inspect-socket", "");
+
+ if (inspect.length())
+ {
+ inspector.reset(new VppInspect(inspect));
+ }
+}
+
+void
+VppRenderer::start()
+{
+ // Called during agent startup
+ if (started) return;
+ started = true;
+ vppManager->start();
+ vppManager->registerModbListeners();
+ if ((encapType == encapTypeVxlan) || (encapType == encapTypeIvxlan))
+ {
+ tunnelEpManager.setUplinkIface(uplinkIface);
+ tunnelEpManager.setUplinkVlan(uplinkVlan);
+ tunnelEpManager.setParentRenderer(this);
+ tunnelEpManager.start();
+ }
+ LOG(opflexagent::INFO) << "Starting vpp renderer plugin";
+}
+
+void
+VppRenderer::stop()
+{
+ // Called during agent shutdown
+ if (!started) return;
+ started = false;
+ LOG(opflexagent::INFO) << "Stopping vpp renderer plugin";
+ if ((encapType == encapTypeVxlan) || (encapType == encapTypeIvxlan))
+ {
+ tunnelEpManager.stop();
+ }
+ vppManager->stop();
+}
+
+boost::asio::ip::address VppRenderer::getUplinkAddress()
+{
+ const boost::asio::ip::address addr =
+ vppManager->uplink().local_address();
+ return addr;
+}
+
+std::string
+VppRenderer::getUplinkMac()
+{
+ return vppManager->uplink().uplink_l2_address();
+}
+
+} // namespace VPP
+
+extern "C" const opflexagent::RendererPlugin *
+init_renderer_plugin()
+{
+ // Return a plugin implementation, which can ini
+ static const VPP::VppRendererPlugin vppPlugin;
+
+ return &vppPlugin;
+}
diff --git a/src/VppRouteManager.cpp b/src/VppRouteManager.cpp
new file mode 100644
index 0000000..ad9c5ed
--- /dev/null
+++ b/src/VppRouteManager.cpp
@@ -0,0 +1,349 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2018 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#include <boost/optional.hpp>
+
+#include <modelgbp/gbp/L3ExternalDomain.hpp>
+#include <modelgbp/gbp/L3ExternalNetwork.hpp>
+#include <modelgbp/gbp/RemoteRoute.hpp>
+#include <modelgbp/gbp/RoutingDomain.hpp>
+#include <modelgbp/gbp/StaticRoute.hpp>
+
+#include <opflexagent/RDConfig.h>
+
+#include <vom/bridge_domain.hpp>
+#include <vom/gbp_contract.hpp>
+#include <vom/gbp_endpoint.hpp>
+#include <vom/gbp_endpoint_group.hpp>
+#include <vom/gbp_recirc.hpp>
+#include <vom/gbp_route_domain.hpp>
+#include <vom/gbp_subnet.hpp>
+#include <vom/interface.hpp>
+#include <vom/l2_binding.hpp>
+#include <vom/l3_binding.hpp>
+#include <vom/nat_binding.hpp>
+#include <vom/neighbour.hpp>
+#include <vom/om.hpp>
+#include <vom/om.hpp>
+#include <vom/route.hpp>
+#include <vom/route_domain.hpp>
+#include <vom/sub_interface.hpp>
+
+#include "VppEndPointGroupManager.hpp"
+#include "VppLog.hpp"
+#include "VppRouteManager.hpp"
+
+using namespace VOM;
+
+namespace VPP
+{
+RouteManager::RouteManager(Runtime &runtime)
+ : m_runtime(runtime)
+{
+}
+
+opflexagent::network::subnets_t
+get_rd_subnets(opflexagent::Agent &agent, const opflex::modb::URI &uri)
+{
+ /*
+ * this is a cut-n-paste from IntflowManager.
+ */
+ opflexagent::network::subnets_t intSubnets;
+
+ boost::optional<std::shared_ptr<modelgbp::gbp::RoutingDomain>> rd =
+ modelgbp::gbp::RoutingDomain::resolve(agent.getFramework(), uri);
+
+ if (!rd)
+ {
+ return intSubnets;
+ }
+
+ std::vector<std::shared_ptr<modelgbp::gbp::RoutingDomainToIntSubnetsRSrc>>
+ subnets_list;
+ rd.get()->resolveGbpRoutingDomainToIntSubnetsRSrc(subnets_list);
+ for (auto &subnets_ref : subnets_list)
+ {
+ boost::optional<opflex::modb::URI> subnets_uri =
+ subnets_ref->getTargetURI();
+ opflexagent::PolicyManager::resolveSubnets(
+ agent.getFramework(), subnets_uri, intSubnets);
+ }
+ std::shared_ptr<const opflexagent::RDConfig> rdConfig =
+ agent.getExtraConfigManager().getRDConfig(uri);
+ if (rdConfig)
+ {
+ for (const std::string &cidrSn : rdConfig->getInternalSubnets())
+ {
+ opflexagent::network::cidr_t cidr;
+ if (opflexagent::network::cidr_from_string(cidrSn, cidr))
+ {
+ intSubnets.insert(
+ make_pair(cidr.first.to_string(), cidr.second));
+ }
+ else
+ {
+ VLOGE << "Invalid CIDR subnet: " << cidrSn;
+ }
+ }
+ }
+
+ return intSubnets;
+}
+
+void
+RouteManager::mk_ext_nets(
+ Runtime &runtime,
+ route_domain &rd,
+ const opflex::modb::URI &uri,
+ std::shared_ptr<modelgbp::gbp::L3ExternalDomain> ext_dom)
+{
+ const std::string &uuid = uri.toString();
+
+ /* To get all the external networks in an external domain */
+ std::vector<std::shared_ptr<modelgbp::gbp::L3ExternalNetwork>> ext_nets;
+ ext_dom->resolveGbpL3ExternalNetwork(ext_nets);
+
+ for (std::shared_ptr<modelgbp::gbp::L3ExternalNetwork> net : ext_nets)
+ {
+ const opflex::modb::URI net_uri = net->getURI();
+
+ /* For each external network, get the sclass */
+ boost::optional<uint32_t> sclass =
+ runtime.policy_manager().getSclassForExternalNet(net_uri);
+
+ if (!sclass)
+ {
+ VLOGI << "External-Network; no sclass: " << net_uri;
+ continue;
+ }
+
+ /* traverse each subnet in the network */
+ std::vector<std::shared_ptr<modelgbp::gbp::ExternalSubnet>> ext_subs;
+ net->resolveGbpExternalSubnet(ext_subs);
+
+ for (std::shared_ptr<modelgbp::gbp::ExternalSubnet> snet : ext_subs)
+ {
+ VLOGD << "External-Interface; subnet:" << uri
+ << " external:" << ext_dom.get()->getName("n/a")
+ << " external-net:" << net->getName("n/a")
+ << " external-sub:" << snet->getAddress("n/a") << "/"
+ << std::to_string(snet->getPrefixLen(99))
+ << " sclass:" << sclass.get();
+
+ if (!snet->isAddressSet() || !snet->isPrefixLenSet()) continue;
+
+ boost::asio::ip::address addr =
+ boost::asio::ip::address::from_string(snet->getAddress().get());
+
+ gbp_subnet gs(rd, {addr, snet->getPrefixLen().get()}, sclass.get());
+ OM::write(uuid, gs);
+ }
+ }
+}
+
+void
+RouteManager::handle_domain_update(const opflex::modb::URI &uri)
+{
+ OM::mark_n_sweep ms(uri.toString());
+
+ boost::optional<std::shared_ptr<modelgbp::gbp::RoutingDomain>> op_opf_rd =
+ modelgbp::gbp::RoutingDomain::resolve(m_runtime.agent.getFramework(),
+ uri);
+
+ if (!op_opf_rd)
+ {
+ VLOGD << "Cleaning up for RD: " << uri;
+ m_runtime.id_gen.erase(modelgbp::gbp::RoutingDomain::CLASS_ID, uri);
+ return;
+ }
+ boost::optional<std::shared_ptr<modelgbp::gbpe::InstContext>> rd_inst;
+ std::shared_ptr<modelgbp::gbp::RoutingDomain> opf_rd = op_opf_rd.get();
+
+ rd_inst = opf_rd->resolveGbpeInstContext();
+
+ if (!rd_inst || !rd_inst.get()->getEncapId())
+ {
+ VLOGI << "RD-inst not resolved for: " << uri;
+ return;
+ }
+
+ const std::string &rd_uuid = uri.toString();
+
+ VLOGD << "Importing routing domain:" << uri;
+
+ /*
+ * get all the subnets that are internal to this route domain
+ */
+ opflexagent::network::subnets_t intSubnets =
+ get_rd_subnets(m_runtime.agent, uri);
+ boost::system::error_code ec;
+
+ /*
+ * create (or at least own) VPP's route-domain object
+ */
+ uint32_t rdId =
+ m_runtime.id_gen.get(modelgbp::gbp::RoutingDomain::CLASS_ID, uri);
+
+ VOM::route_domain rd(rdId);
+ VOM::OM::write(rd_uuid, rd);
+
+ std::shared_ptr<VOM::gbp_route_domain> v_grd =
+ EndPointGroupManager::mk_gbp_rd(
+ m_runtime, rd_uuid, rd, rd_inst.get()->getEncapId().get());
+
+ /*
+ * For each internal Subnet
+ */
+ for (const auto &sn : intSubnets)
+ {
+ /*
+ * still a little more song and dance before we can get
+ * our hands on an address ...
+ */
+ boost::asio::ip::address addr =
+ boost::asio::ip::address::from_string(sn.first, ec);
+ if (ec) continue;
+
+ VLOGD << "Importing routing domain:" << uri << " subnet:" << addr << "/"
+ << std::to_string(sn.second);
+
+ /*
+ * add a route for the subnet in VPP's route-domain via
+ * the EPG's uplink, DVR styleee
+ */
+ gbp_subnet gs(*v_grd,
+ {addr, sn.second},
+ (m_runtime.is_transport_mode
+ ? gbp_subnet::type_t::TRANSPORT
+ : gbp_subnet::type_t::STITCHED_INTERNAL));
+ OM::write(rd_uuid, gs);
+ }
+
+ /*
+ * for each external subnet
+ */
+ std::vector<std::shared_ptr<modelgbp::gbp::L3ExternalDomain>> extDoms;
+ opf_rd.get()->resolveGbpL3ExternalDomain(extDoms);
+
+ for (std::shared_ptr<modelgbp::gbp::L3ExternalDomain> ext_dom : extDoms)
+ {
+ mk_ext_nets(m_runtime, rd, uri, ext_dom);
+ }
+}
+
+void
+RouteManager::handle_route_update(const opflex::modb::URI &uri)
+{
+ const std::string &uuid = uri.toString();
+
+ OM::mark_n_sweep ms(uuid);
+
+ boost::optional<std::shared_ptr<modelgbp::epdr::LocalRoute>>
+ op_local_route = modelgbp::epdr::LocalRoute::resolve(
+ m_runtime.agent.getFramework(), uri);
+
+ if (!op_local_route)
+ {
+ VLOGD << "Cleaning up for Route: " << uri;
+ return;
+ }
+
+ mac_address_t GBP_ROUTED_DST_MAC("00:0c:0c:0c:0c:0c");
+
+ std::shared_ptr<modelgbp::gbp::RoutingDomain> rd;
+ std::shared_ptr<modelgbp::gbpe::InstContext> rd_inst;
+ boost::asio::ip::address pfx_addr;
+ uint8_t pfx_len;
+ std::list<boost::asio::ip::address> nh_list;
+ bool are_nhs_remote;
+ boost::optional<uint32_t> sclass;
+
+ m_runtime.agent.getPolicyManager().getRoute(
+ modelgbp::epdr::LocalRoute::CLASS_ID,
+ uri,
+ m_runtime.uplink.local_address(),
+ rd,
+ rd_inst,
+ pfx_addr,
+ pfx_len,
+ nh_list,
+ are_nhs_remote,
+ sclass);
+
+ if (!rd || !rd_inst || !rd_inst->getEncapId())
+ {
+ VLOGI << "RD/RD-inst not resolved for Route: " << uri;
+ return;
+ }
+
+ uint32_t rd_id = m_runtime.id_gen.get(
+ modelgbp::gbp::RoutingDomain::CLASS_ID, rd->getURI());
+
+ VOM::route_domain v_rd(rd_id);
+ VOM::OM::write(uuid, v_rd);
+
+ std::shared_ptr<VOM::gbp_route_domain> v_grd =
+ EndPointGroupManager::mk_gbp_rd(
+ m_runtime, uuid, v_rd, rd_inst->getEncapId().get());
+
+ route::prefix_t pfx(pfx_addr, pfx_len);
+ route::ip_route v_route(v_rd, pfx);
+
+ for (auto nh : nh_list)
+ {
+ if (are_nhs_remote)
+ {
+ /*
+ * route via vxlan-gbp-tunnel
+ */
+ vxlan_tunnel vt(m_runtime.uplink.local_address(),
+ nh,
+ rd_inst->getEncapId().get(),
+ v_rd,
+ vxlan_tunnel::mode_t::GBP_L3);
+ OM::write(uuid, vt);
+
+ neighbour::flags_t f =
+ (neighbour::flags_t::STATIC | neighbour::flags_t::NO_FIB_ENTRY);
+
+ neighbour nbr(vt, nh, GBP_ROUTED_DST_MAC, f);
+ VOM::OM::write(uuid, nbr);
+
+ v_route.add({nh, vt});
+ }
+ else
+ {
+ /*
+ * routed via a local next-hop
+ */
+ v_route.add({v_rd, nh});
+ }
+ }
+
+ VOM::OM::write(uuid, v_route);
+
+ /* attach the sclass information to the route */
+ if (sclass)
+ {
+ gbp_subnet v_gs(*v_grd, pfx, sclass.get());
+ VOM::OM::write(uuid, v_gs);
+ }
+ else
+ {
+ VLOGW << "No slcass for: " << uri;
+ }
+}
+
+}; // namepsace VPP
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
diff --git a/src/VppSecurityGroupManager.cpp b/src/VppSecurityGroupManager.cpp
new file mode 100644
index 0000000..5820200
--- /dev/null
+++ b/src/VppSecurityGroupManager.cpp
@@ -0,0 +1,325 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2018 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#include <opflexagent/PolicyManager.h>
+#include <opflexagent/logging.h>
+
+#include <modelgbp/gbp/ConnTrackEnumT.hpp>
+#include <modelgbp/gbp/DirectionEnumT.hpp>
+#include <modelgbp/l2/EtherTypeEnumT.hpp>
+#include <modelgbp/l4/TcpFlagsEnumT.hpp>
+
+#include <vom/acl_binding.hpp>
+
+#include "VppEndPointManager.hpp"
+#include "VppLog.hpp"
+#include "VppSecurityGroupManager.hpp"
+
+namespace VPP
+{
+SecurityGroupManager::SecurityGroupManager(opflexagent::Agent &agent)
+ : m_agent(agent)
+{
+}
+
+void
+setParamUpdate(modelgbp::gbpe::L24Classifier &cls, ACL::l3_rule &rule)
+{
+ using modelgbp::l4::TcpFlagsEnumT;
+
+ if (cls.isArpOpcSet())
+ {
+ rule.set_proto(cls.getArpOpc().get());
+ }
+
+ if (cls.isProtSet())
+ {
+ rule.set_proto(cls.getProt(0));
+ }
+
+ if (cls.isSFromPortSet())
+ {
+ rule.set_src_from_port(cls.getSFromPort(0));
+ }
+ else if (cls.isIcmpTypeSet())
+ {
+ rule.set_src_from_port(cls.getIcmpType(0));
+ }
+
+ if (cls.isSToPortSet())
+ {
+ rule.set_src_to_port(cls.getSToPort(0));
+ }
+ else if (cls.isIcmpTypeSet())
+ {
+ rule.set_src_to_port(cls.getIcmpType(0));
+ }
+
+ if (cls.isDFromPortSet())
+ {
+ rule.set_dst_from_port(cls.getDFromPort(0));
+ }
+ else if (cls.isIcmpCodeSet())
+ {
+ rule.set_dst_from_port(cls.getIcmpCode(0));
+ }
+
+ if (cls.isDToPortSet())
+ {
+ rule.set_dst_to_port(cls.getDToPort(0));
+ }
+ else if (cls.isIcmpCodeSet())
+ {
+ rule.set_dst_to_port(cls.getIcmpCode(0));
+ }
+
+ if (6 == cls.getProt(0) && cls.isTcpFlagsSet())
+ {
+ rule.set_tcp_flags_mask(
+ cls.getTcpFlags(TcpFlagsEnumT::CONST_UNSPECIFIED));
+ rule.set_tcp_flags_value(
+ cls.getTcpFlags(TcpFlagsEnumT::CONST_UNSPECIFIED));
+ }
+
+ if (6 == cls.getProt(0) || 17 == cls.getProt(0))
+ {
+ if (rule.srcport_or_icmptype_last() == 0) rule.set_src_to_port(65535);
+ if (rule.dstport_or_icmpcode_last() == 0) rule.set_dst_to_port(65535);
+ }
+
+ if (1 == cls.getProt(0) || 58 == cls.getProt(0))
+ {
+ if (rule.srcport_or_icmptype_last() == 0) rule.set_src_to_port(255);
+ if (rule.dstport_or_icmpcode_last() == 0) rule.set_dst_to_port(255);
+ }
+}
+
+void
+SecurityGroupManager::build_update(
+ opflexagent::Agent &agent,
+ const opflexagent::EndpointListener::uri_set_t &secGrps,
+ const std::string &secGrpId,
+ ACL::l3_list::rules_t &in_rules,
+ ACL::l3_list::rules_t &out_rules,
+ ACL::acl_ethertype::ethertype_rules_t &ethertype_rules)
+{
+ if (secGrps.empty())
+ {
+ // OM::remove(secGrpId);
+ return;
+ }
+
+ VLOGD << "building security group update";
+
+ for (const opflex::modb::URI &secGrp : secGrps)
+ {
+ opflexagent::PolicyManager::rule_list_t rules;
+ agent.getPolicyManager().getSecGroupRules(secGrp, rules);
+
+ for (auto pc : rules)
+ {
+ uint8_t dir = pc->getDirection();
+ const std::shared_ptr<modelgbp::gbpe::L24Classifier> &cls =
+ pc->getL24Classifier();
+ uint32_t priority = pc->getPriority();
+ const ethertype_t &etherType =
+ ethertype_t::from_numeric_val(cls->getEtherT(
+ modelgbp::l2::EtherTypeEnumT::CONST_UNSPECIFIED));
+ ACL::action_t act = ACL::action_t::from_bool(
+ pc->getAllow(),
+ cls->getConnectionTracking(
+ modelgbp::gbp::ConnTrackEnumT::CONST_NORMAL));
+
+ if (dir == modelgbp::gbp::DirectionEnumT::CONST_BIDIRECTIONAL ||
+ dir == modelgbp::gbp::DirectionEnumT::CONST_IN)
+ {
+ ACL::ethertype_rule_t et(etherType, direction_t::OUTPUT);
+ ethertype_rules.insert(et);
+ }
+ if (dir == modelgbp::gbp::DirectionEnumT::CONST_BIDIRECTIONAL ||
+ dir == modelgbp::gbp::DirectionEnumT::CONST_OUT)
+ {
+ ACL::ethertype_rule_t et(etherType, direction_t::INPUT);
+ ethertype_rules.insert(et);
+ }
+
+ if (etherType != modelgbp::l2::EtherTypeEnumT::CONST_IPV4 &&
+ etherType != modelgbp::l2::EtherTypeEnumT::CONST_IPV6)
+ {
+ VLOGW << "Security Group Rule for Protocol "
+ << etherType.to_string() << " ,(IPv4/IPv6) Security"
+ << "Rules are allowed";
+ continue;
+ }
+
+ if (!pc->getRemoteSubnets().empty())
+ {
+ boost::optional<const opflexagent::network::subnets_t &>
+ remoteSubs;
+ remoteSubs = pc->getRemoteSubnets();
+ for (const opflexagent::network::subnet_t &sub :
+ remoteSubs.get())
+ {
+ bool is_v6 =
+ boost::asio::ip::address::from_string(sub.first)
+ .is_v6();
+
+ if ((etherType ==
+ modelgbp::l2::EtherTypeEnumT::CONST_IPV4 &&
+ is_v6) ||
+ (etherType ==
+ modelgbp::l2::EtherTypeEnumT::CONST_IPV6 &&
+ !is_v6))
+ continue;
+
+ route::prefix_t ip(sub.first, sub.second);
+ route::prefix_t ip2(route::prefix_t::ZERO);
+
+ if (etherType == modelgbp::l2::EtherTypeEnumT::CONST_IPV6)
+ {
+ ip2 = route::prefix_t::ZEROv6;
+ }
+
+ if (dir == modelgbp::gbp::DirectionEnumT::
+ CONST_BIDIRECTIONAL ||
+ dir == modelgbp::gbp::DirectionEnumT::CONST_IN)
+ {
+ ACL::l3_rule rule(priority, act, ip, ip2);
+ setParamUpdate(*cls, rule);
+ out_rules.insert(rule);
+ }
+ if (dir == modelgbp::gbp::DirectionEnumT::
+ CONST_BIDIRECTIONAL ||
+ dir == modelgbp::gbp::DirectionEnumT::CONST_OUT)
+ {
+ ACL::l3_rule rule(priority, act, ip2, ip);
+ setParamUpdate(*cls, rule);
+ in_rules.insert(rule);
+ }
+ }
+ }
+ else
+ {
+ route::prefix_t srcIp(route::prefix_t::ZERO);
+ route::prefix_t dstIp(route::prefix_t::ZERO);
+
+ if (etherType == modelgbp::l2::EtherTypeEnumT::CONST_IPV6)
+ {
+ srcIp = route::prefix_t::ZEROv6;
+ dstIp = route::prefix_t::ZEROv6;
+ }
+
+ ACL::l3_rule rule(priority, act, srcIp, dstIp);
+ setParamUpdate(*cls, rule);
+ if (dir == modelgbp::gbp::DirectionEnumT::CONST_BIDIRECTIONAL ||
+ dir == modelgbp::gbp::DirectionEnumT::CONST_IN)
+ {
+ out_rules.insert(rule);
+ }
+ if (dir == modelgbp::gbp::DirectionEnumT::CONST_BIDIRECTIONAL ||
+ dir == modelgbp::gbp::DirectionEnumT::CONST_OUT)
+ {
+ in_rules.insert(rule);
+ }
+ }
+ }
+ }
+}
+
+std::string
+SecurityGroupManager::get_id(
+ const opflexagent::EndpointListener::uri_set_t &secGrps)
+{
+ std::stringstream ss;
+ bool notfirst = false;
+ for (auto &uri : secGrps)
+ {
+ if (notfirst) ss << ",";
+ notfirst = true;
+ ss << uri.toString();
+ }
+ return ss.str();
+}
+
+void
+SecurityGroupManager::handle_set_update(
+ const opflexagent::EndpointListener::uri_set_t &secGrps)
+{
+ VLOGD << "Updating security group set";
+
+ ACL::l3_list::rules_t in_rules, out_rules;
+ ACL::acl_ethertype::ethertype_rules_t ethertype_rules;
+ const std::string secGrpId = get_id(secGrps);
+ std::shared_ptr<ACL::l3_list> in_acl, out_acl;
+
+ build_update(
+ m_agent, secGrps, secGrpId, in_rules, out_rules, ethertype_rules);
+
+ if (in_rules.empty() && out_rules.empty() && ethertype_rules.empty())
+ {
+ VLOGW << "in and out rules are empty";
+ return;
+ }
+
+ opflexagent::EndpointManager &epMgr = m_agent.getEndpointManager();
+ std::unordered_set<std::string> eps;
+ epMgr.getEndpointsForSecGrps(secGrps, eps);
+
+ for (const std::string &uuid : eps)
+ {
+ OM::mark_n_sweep ms(uuid);
+
+ const opflexagent::Endpoint &endPoint = *epMgr.getEndpoint(uuid).get();
+ const std::string vppInterfaceName =
+ EndPointManager::get_ep_interface_name(endPoint);
+
+ if (0 == vppInterfaceName.length()) continue;
+
+ std::shared_ptr<interface> itf = interface::find(vppInterfaceName);
+
+ if (!itf) continue;
+
+ if (!ethertype_rules.empty())
+ {
+ ACL::acl_ethertype a_e(*itf, ethertype_rules);
+ OM::write(uuid, a_e);
+ }
+ if (!in_rules.empty())
+ {
+ ACL::l3_list inAcl(secGrpId + "in", in_rules);
+ OM::write(uuid, inAcl);
+
+ ACL::l3_binding in_binding(direction_t::INPUT, *itf, inAcl);
+ OM::write(uuid, in_binding);
+ }
+ if (!out_rules.empty())
+ {
+ ACL::l3_list outAcl(secGrpId + "out", out_rules);
+ OM::write(uuid, outAcl);
+ ACL::l3_binding out_binding(direction_t::OUTPUT, *itf, outAcl);
+ OM::write(uuid, out_binding);
+ }
+ }
+}
+
+void
+SecurityGroupManager::handle_update(const opflex::modb::URI &uri)
+{
+ std::unordered_set<opflexagent::EndpointListener::uri_set_t> secGrpSets;
+ m_agent.getEndpointManager().getSecGrpSetsForSecGrp(uri, secGrpSets);
+ for (auto &secGrpSet : secGrpSets)
+ handle_set_update(secGrpSet);
+}
+
+}; // namepsace VPP
+ /*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
diff --git a/src/VppSpineProxy.cpp b/src/VppSpineProxy.cpp
new file mode 100644
index 0000000..b658e2b
--- /dev/null
+++ b/src/VppSpineProxy.cpp
@@ -0,0 +1,66 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2017-2018 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#include <vom/om.hpp>
+#include <vom/vxlan_tunnel.hpp>
+
+#include "VppSpineProxy.hpp"
+
+using namespace VOM;
+
+namespace VPP
+{
+SpineProxy::SpineProxy(const boost::asio::ip::address_v4 &local,
+ const boost::asio::ip::address_v4 &remote_v4,
+ const boost::asio::ip::address_v4 &remote_v6,
+ const boost::asio::ip::address_v4 &remote_mac)
+ : m_local(local)
+ , m_remote_v4(remote_v4)
+ , m_remote_v6(remote_v6)
+ , m_remote_mac(remote_mac)
+{
+}
+
+const std::shared_ptr<VOM::vxlan_tunnel>
+SpineProxy::mk_v4(const std::string &key, uint32_t vnid)
+{
+ return (mk_intf(key, m_local, m_remote_v4, vnid));
+}
+
+const std::shared_ptr<VOM::vxlan_tunnel>
+SpineProxy::mk_v6(const std::string &key, uint32_t vnid)
+{
+ return (mk_intf(key, m_local, m_remote_v6, vnid));
+}
+
+const std::shared_ptr<VOM::vxlan_tunnel>
+SpineProxy::mk_mac(const std::string &key, uint32_t vnid)
+{
+ return (mk_intf(key, m_local, m_remote_mac, vnid));
+}
+
+const std::shared_ptr<VOM::vxlan_tunnel>
+SpineProxy::mk_intf(const std::string &key,
+ boost::asio::ip::address_v4 &src,
+ boost::asio::ip::address_v4 &dst,
+ uint32_t vnid)
+{
+ std::shared_ptr<VOM::vxlan_tunnel> vt = std::make_shared<vxlan_tunnel>(
+ src, dst, vnid, vxlan_tunnel::mode_t::GBP_L2);
+ OM::write(key, *vt);
+
+ return vt;
+}
+};
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
diff --git a/src/VppUplink.cpp b/src/VppUplink.cpp
new file mode 100644
index 0000000..4026d73
--- /dev/null
+++ b/src/VppUplink.cpp
@@ -0,0 +1,332 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#include <opflexagent/logging.h>
+
+#include "vom/arp_proxy_binding.hpp"
+#include "vom/arp_proxy_config.hpp"
+#include "vom/bond_interface.hpp"
+#include "vom/bond_member.hpp"
+#include "vom/interface.hpp"
+#include "vom/ip_punt_redirect.hpp"
+#include "vom/ip_unnumbered.hpp"
+#include "vom/l3_binding.hpp"
+#include "vom/lldp_binding.hpp"
+#include "vom/lldp_global.hpp"
+#include "vom/neighbour.hpp"
+#include "vom/sub_interface.hpp"
+#include <vom/bond_group_binding.hpp>
+
+#include "VppSpineProxy.hpp"
+#include "VppUplink.hpp"
+#include "VppUtil.hpp"
+
+using namespace VOM;
+
+namespace VPP
+{
+
+static const std::string UPLINK_KEY = "__uplink__";
+
+Uplink::Uplink(opflexagent::Agent &agent)
+ : m_type(VLAN)
+ , m_agent(agent)
+{
+}
+
+const std::string &
+Uplink::system_name() const
+{
+ return m_system_name;
+}
+
+std::shared_ptr<VOM::interface>
+Uplink::mk_interface(const std::string &uuid, uint32_t vnid)
+{
+ std::shared_ptr<VOM::interface> sp;
+ switch (m_type)
+ {
+ case VXLAN:
+ {
+ vxlan_tunnel vt(m_vxlan.src, m_vxlan.dst, vnid);
+ VOM::OM::write(uuid, vt);
+
+ return vt.singular();
+ }
+ case VLAN:
+ {
+ sub_interface sb(*m_uplink, interface::admin_state_t::UP, vnid);
+ VOM::OM::write(uuid, sb);
+
+ return sb.singular();
+ }
+ }
+
+ return sp;
+}
+
+void
+Uplink::configure_tap(const route::prefix_t &pfx)
+{
+
+ /**
+ * Create a tap interface with a fixed mac so we can add a
+ * ARP entry for it
+ */
+ mac_address_t tap_mac("00:00:de:ad:be:ef");
+
+ tap_interface itf("tap0", interface::admin_state_t::UP, pfx, tap_mac);
+ VOM::OM::write(UPLINK_KEY, itf);
+
+ neighbour::flags_t f =
+ (neighbour::flags_t::STATIC | neighbour::flags_t::NO_FIB_ENTRY);
+
+ neighbour tap_nbr(itf, pfx.address(), tap_mac, f);
+ VOM::OM::write(UPLINK_KEY, tap_nbr);
+
+ /*
+ * commit and L3 Config to the OM so this uplink owns the
+ * subnet on the interface. If we don't have a representation
+ * of the configured prefix in the OM, we'll sweep it from the
+ * interface if we restart
+ */
+ l3_binding l3(*m_subitf, pfx);
+ OM::commit(UPLINK_KEY, l3);
+
+ ip_unnumbered ipUnnumber(itf, *m_subitf);
+ VOM::OM::write(UPLINK_KEY, ipUnnumber);
+
+ arp_proxy_config arpProxyConfig(pfx.low().address().to_v4(),
+ pfx.high().address().to_v4());
+ VOM::OM::write(UPLINK_KEY, arpProxyConfig);
+
+ arp_proxy_binding arpProxyBinding(itf);
+ VOM::OM::write(UPLINK_KEY, arpProxyBinding);
+
+ ip_punt_redirect ipPunt(*m_subitf, itf, pfx.address());
+ VOM::OM::write(UPLINK_KEY, ipPunt);
+}
+
+void
+Uplink::handle_dhcp_event(std::shared_ptr<VOM::dhcp_client::lease_t> lease)
+{
+ m_agent.getAgentIOService().dispatch(
+ bind(&Uplink::handle_dhcp_event_i, this, lease));
+}
+
+void
+Uplink::handle_dhcp_event_i(std::shared_ptr<dhcp_client::lease_t> lease)
+{
+ LOG(opflexagent::INFO) << "DHCP Event: " << lease->to_string();
+
+ m_pfx = lease->host_prefix;
+
+ /*
+ * Create the TAP interface with the DHCP learn address.
+ * This allows all traffic punt to VPP to arrive at the TAP/agent.
+ */
+ configure_tap(m_pfx);
+
+ /*
+ * VXLAN tunnels use the DHCP address as the source
+ */
+ m_vxlan.src = m_pfx.address();
+}
+
+std::shared_ptr<SpineProxy>
+Uplink::spine_proxy()
+{
+ switch (m_agent.getRendererForwardingMode())
+ {
+ case opflex::ofcore::OFConstants::STITCHED_MODE:
+ break;
+ case opflex::ofcore::OFConstants::TRANSPORT_MODE:
+ {
+ boost::asio::ip::address_v4 v4, v6, mac;
+
+ m_agent.getV4Proxy(v4);
+ m_agent.getV6Proxy(v6);
+ m_agent.getMacProxy(mac);
+
+ return std::make_shared<SpineProxy>(
+ local_address().to_v4(), v4, v6, mac);
+ break;
+ }
+ }
+ return {};
+}
+
+const boost::asio::ip::address &
+Uplink::local_address() const
+{
+ return m_pfx.address();
+}
+
+const std::string
+Uplink::uplink_l2_address() const
+{
+ const std::string str("");
+ if (m_uplink)
+ {
+ return m_uplink->l2_address().to_string();
+ }
+ return str;
+}
+
+const std::shared_ptr<interface>
+Uplink::local_interface() const
+{
+ return m_subitf;
+}
+
+void
+Uplink::configure(const std::string &fqdn)
+{
+ m_system_name = fqdn;
+
+ LOG(opflexagent::INFO) << "configure:" << m_system_name;
+
+ /*
+ * Consruct the uplink physical, so we now 'own' it
+ */
+ VOM::interface::type_t type = getIntfTypeFromName(m_iface);
+ if (VOM::interface::type_t::BOND == type)
+ {
+ bond_interface bitf(m_iface,
+ interface::admin_state_t::UP,
+ bond_interface::mode_t::LACP,
+ bond_interface::lb_t::L2);
+ OM::write(UPLINK_KEY, bitf);
+ bond_group_binding::enslaved_itf_t slave_itfs;
+ for (auto sif : slave_ifaces)
+ {
+ interface sitf(
+ sif, getIntfTypeFromName(sif), interface::admin_state_t::UP);
+ OM::write(UPLINK_KEY, sitf);
+ bond_member bm(
+ sitf, bond_member::mode_t::ACTIVE, bond_member::rate_t::SLOW);
+ slave_itfs.insert(bm);
+ }
+ if (!slave_itfs.empty())
+ {
+ bond_group_binding bgb(bitf, slave_itfs);
+ OM::write(UPLINK_KEY, bgb);
+ }
+ m_uplink = bitf.singular();
+ }
+ else
+ {
+ interface itf(m_iface, type, interface::admin_state_t::UP);
+ OM::write(UPLINK_KEY, itf);
+ m_uplink = itf.singular();
+ }
+
+ /*
+ * Own the v4 and v6 global tables
+ */
+ route_domain v4_gbl(0);
+ OM::write(UPLINK_KEY, v4_gbl);
+ route_domain v6_gbl(0);
+ OM::write(UPLINK_KEY, v6_gbl);
+
+ /**
+ * Enable LLDP on this uplionk
+ */
+ lldp_global lg(m_system_name, 5, 2);
+ OM::write(UPLINK_KEY, lg);
+ lldp_binding lb(*m_uplink, "uplink-interface");
+ OM::write(UPLINK_KEY, lb);
+
+ /*
+ * now create the sub-interface on which control and data traffic from
+ * the upstream leaf will arrive
+ */
+ sub_interface subitf(*m_uplink, interface::admin_state_t::UP, m_vlan);
+ OM::write(UPLINK_KEY, subitf);
+ m_subitf = subitf.singular();
+
+ /**
+ * Strip the fully qualified domain name of any domain name
+ * to get just the hostname.
+ */
+ std::string hostname = fqdn;
+ std::string::size_type n = hostname.find(".");
+ if (n != std::string::npos)
+ {
+ hostname = hostname.substr(0, n);
+ }
+
+ /**
+ * Configure DHCP on the uplink subinterface
+ * We must use the MAC address of the uplink interface as the DHCP client-ID
+ */
+ dhcp_client dc(*m_subitf, hostname, m_uplink->l2_address(), true, this);
+ OM::write(UPLINK_KEY, dc);
+
+ /**
+ * In the case of a agent restart, the DHCP process will already be complete
+ * in VPP and we won't get notified. So check here if the DHCP lease
+ * is already aquired.
+ */
+ std::shared_ptr<dhcp_client::lease_t> lease = dc.singular()->lease();
+
+ if (lease && lease->state != dhcp_client::state_t::DISCOVER)
+ {
+ LOG(opflexagent::INFO) << "DHCP present: " << lease->to_string();
+ configure_tap(lease->host_prefix);
+ m_vxlan.src = lease->host_prefix.address();
+ m_pfx = lease->host_prefix;
+ }
+ else
+ {
+ LOG(opflexagent::DEBUG) << "DHCP awaiting lease";
+ }
+}
+
+void
+Uplink::set(const std::string &uplink,
+ uint16_t uplink_vlan,
+ const std::string &encap_name,
+ const boost::asio::ip::address &remote_ip,
+ uint16_t port)
+{
+ m_type = VXLAN;
+ m_vxlan.dst = remote_ip;
+ m_iface = uplink;
+ m_vlan = uplink_vlan;
+}
+
+void
+Uplink::set(const std::string &uplink,
+ uint16_t uplink_vlan,
+ const std::string &encap_name)
+{
+ m_type = VLAN;
+ m_iface = uplink;
+ m_vlan = uplink_vlan;
+}
+
+void
+Uplink::insert_slave_ifaces(std::string name)
+{
+ this->slave_ifaces.insert(name);
+}
+
+void
+Uplink::insert_dhcp_options(std::string name)
+{
+ this->dhcp_options.insert(name);
+}
+} // namespace VPP
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
diff --git a/src/VppUtil.cpp b/src/VppUtil.cpp
new file mode 100644
index 0000000..9874923
--- /dev/null
+++ b/src/VppUtil.cpp
@@ -0,0 +1,47 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2018 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#include <string>
+
+#include "VppUtil.hpp"
+
+namespace VPP
+{
+const interface::type_t &
+getIntfTypeFromName(const std::string &name)
+{
+ if (name.find("Bond") != std::string::npos)
+ return interface::type_t::BOND;
+ else if (name.find("Ethernet") != std::string::npos)
+ return interface::type_t::ETHERNET;
+ else if ((name.find("tapv2") != std::string::npos) ||
+ (name.find("tap") != std::string::npos))
+ return interface::type_t::TAPV2;
+ else if ((name.find("vhost") != std::string::npos) ||
+ (name.find("vhu") != std::string::npos))
+ return interface::type_t::VHOST;
+
+ return interface::type_t::AFPACKET;
+}
+
+boost::optional<mac_address_t>
+mac_from_modb(boost::optional<const opflex::modb::MAC &> mo)
+{
+ if (!mo) return boost::none;
+
+ return (mac_address_t(mo->toString()));
+}
+
+}; // namespace VPP
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
diff --git a/src/VppVirtualRouter.cpp b/src/VppVirtualRouter.cpp
new file mode 100644
index 0000000..d61c25f
--- /dev/null
+++ b/src/VppVirtualRouter.cpp
@@ -0,0 +1,31 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#include "VppVirtualRouter.hpp"
+
+namespace VPP
+{
+
+VirtualRouter::VirtualRouter(const VOM::mac_address_t &mac)
+ : m_mac(mac)
+{
+}
+
+const VOM::mac_address_t &
+VirtualRouter::mac() const
+{
+ return (m_mac);
+}
+}
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
diff --git a/src/include/VppContractManager.hpp b/src/include/VppContractManager.hpp
new file mode 100644
index 0000000..9b9d002
--- /dev/null
+++ b/src/include/VppContractManager.hpp
@@ -0,0 +1,47 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2018 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#ifndef __VPP_CONTRACT_MANAGER_H__
+#define __VPP_CONTRACT_MANAGER_H__
+
+#include <opflexagent/Agent.h>
+
+#include "VppIdGen.hpp"
+
+#include <vom/acl_l3_rule.hpp>
+
+namespace VPP
+{
+class ContractManager
+{
+ public:
+ ContractManager(opflexagent::Agent &agent, IdGen &id_gen);
+
+ void handle_update(const opflex::modb::URI &uri);
+
+ private:
+ /**
+ * Referene to the uber-agent
+ */
+ opflexagent::Agent &m_agent;
+ IdGen &m_id_gen;
+};
+
+extern void setParamUpdate(modelgbp::gbpe::L24Classifier &cls,
+ VOM::ACL::l3_rule &rule);
+
+}; // namespace VPP
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
+
+#endif
diff --git a/src/include/VppCrossConnect.hpp b/src/include/VppCrossConnect.hpp
new file mode 100644
index 0000000..2130d78
--- /dev/null
+++ b/src/include/VppCrossConnect.hpp
@@ -0,0 +1,70 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2017-2018 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#ifndef OPFLEXAGNET_VPPCROSSCONNECT_H__
+#define OPFLEXAGENT_VPPCROSSCONNECT_H__
+
+#include <list>
+
+#include <boost/asio/ip/address.hpp>
+
+namespace VPP
+{
+/**
+ * A description of the cross connect class.
+ * It will be used for storage, management of storage etc.
+ */
+class CrossConnect
+{
+ public:
+ /**
+ * Default Constructor
+ */
+ CrossConnect();
+
+ struct xconnect_t
+ {
+ xconnect_t(const std::string &name,
+ uint16_t vlan = 0,
+ std::string ip_address = "",
+ std::string tag_rewrite = "");
+ std::string to_string() const;
+ std::string name;
+ uint16_t vlan;
+ boost::asio::ip::address ip;
+ std::string tag_rewrite;
+ };
+
+ typedef std::pair<xconnect_t, xconnect_t> xconnect;
+
+ /**
+ * insert the cross connect interfaces
+ */
+ void insert_xconnect(xconnect xconn);
+
+ /**
+ * configure cross connect on interfaces
+ */
+ void configure_xconnect();
+
+ private:
+ /**
+ * The cross connect pairs set
+ */
+ std::list<xconnect> xconnects;
+};
+}; // namespace VPP
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
+
+#endif
diff --git a/src/include/VppEndPointGroupManager.hpp b/src/include/VppEndPointGroupManager.hpp
new file mode 100644
index 0000000..23bc286
--- /dev/null
+++ b/src/include/VppEndPointGroupManager.hpp
@@ -0,0 +1,100 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#include <boost/optional.hpp>
+
+#include <opflexagent/Agent.h>
+
+#include "VppRuntime.hpp"
+
+namespace VOM
+{
+class gbp_endpoint_group;
+class gbp_bridge_domain;
+class gbp_route_domain;
+class bridge_domain;
+class route_domain;
+class vxlan_tunnel;
+};
+
+namespace VPP
+{
+class EndPointGroupManager
+{
+ public:
+ struct ForwardInfo
+ {
+ ForwardInfo();
+ ~ForwardInfo() = default;
+
+ uint16_t sclass;
+ uint32_t vnid;
+ uint32_t rdId;
+ uint32_t bdId;
+ boost::optional<opflex::modb::URI> rdURI;
+ boost::optional<opflex::modb::URI> bdURI;
+ };
+ struct NoFowardInfoException
+ {
+ NoFowardInfoException(std::string s)
+ : reason(s)
+ {
+ }
+
+ std::string reason;
+ };
+
+ EndPointGroupManager(Runtime &runtime);
+
+ static ForwardInfo
+ get_fwd_info(Runtime &r,
+ const opflex::modb::URI &uri) throw(NoFowardInfoException);
+ static ForwardInfo get_fwd_info_ext_itf(
+ Runtime &r, const opflex::modb::URI &uri) throw(NoFowardInfoException);
+
+ void handle_update(const opflex::modb::URI &epgURI);
+
+ static std::shared_ptr<VOM::gbp_endpoint_group>
+ mk_group(Runtime &r,
+ const std::string &key,
+ const opflex::modb::URI &uri,
+ bool is_ext = false);
+
+ static std::shared_ptr<VOM::gbp_route_domain>
+ mk_gbp_rd(Runtime &r,
+ const std::string &key,
+ const VOM::route_domain &rd,
+ uint32_t vnid);
+
+ static std::shared_ptr<vxlan_tunnel>
+ mk_mcast_tunnel(Runtime &r,
+ const std::string &key,
+ uint32_t vni,
+ const std::string &maddr);
+
+ static std::shared_ptr<VOM::interface>
+ mk_bvi(Runtime &r,
+ const std::string &key,
+ const VOM::bridge_domain &bd,
+ const VOM::route_domain &rd,
+ const boost::optional<mac_address_t> &mac = boost::none);
+
+ private:
+ /**
+ * Referene to runtime data.
+ */
+ Runtime &m_runtime;
+};
+};
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
diff --git a/src/include/VppEndPointManager.hpp b/src/include/VppEndPointManager.hpp
new file mode 100644
index 0000000..33ec5b2
--- /dev/null
+++ b/src/include/VppEndPointManager.hpp
@@ -0,0 +1,63 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2017-2018 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#include <string>
+
+#include "opflexagent/Agent.h"
+
+#include "VppRuntime.hpp"
+
+namespace VOM
+{
+class bridge_domain;
+class route_domain;
+class interface;
+};
+
+namespace VPP
+{
+class EndPointManager : public VOM::interface::stat_listener
+{
+ public:
+ struct NoEpInterfaceException
+ {
+ };
+
+ EndPointManager(Runtime &runtime);
+ virtual ~EndPointManager();
+
+ void handle_update(const std::string &uuid);
+ void handle_external_update(const std::string &uuid);
+ void handle_remote_update(const std::string &uuid);
+
+ static std::string get_ep_interface_name(
+ const opflexagent::Endpoint &ep) throw(NoEpInterfaceException);
+
+ virtual void handle_interface_stat(const interface &);
+
+ private:
+ void handle_update_i(const std::string &uuid, bool is_external);
+
+ /**
+ * Event listener override to get Interface stats
+ */
+ void handle_interface_stat_i(const interface &);
+
+ static std::shared_ptr<interface> mk_bd_interface(
+ const opflexagent::Endpoint &ep,
+ const std::shared_ptr<bridge_domain> bd,
+ const std::shared_ptr<route_domain> rd) throw(NoEpInterfaceException);
+
+ /**
+ * Referene to runtime data.
+ */
+ Runtime &m_runtime;
+};
+
+}; // namespace VPP
diff --git a/src/include/VppExtItfManager.hpp b/src/include/VppExtItfManager.hpp
new file mode 100644
index 0000000..29fc8aa
--- /dev/null
+++ b/src/include/VppExtItfManager.hpp
@@ -0,0 +1,40 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2018 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#ifndef __VPP_EXT_ITF_MANAGER_H__
+#define __VPP_EXT_ITF_MANAGER_H__
+
+#include <opflexagent/Agent.h>
+
+#include "VppRuntime.hpp"
+
+namespace VPP
+{
+class ExtItfManager
+{
+ public:
+ ExtItfManager(Runtime &runtime);
+
+ void handle_update(const opflex::modb::URI &uri);
+
+ private:
+ /**
+ * Referene to the runtime data
+ */
+ Runtime &m_runtime;
+};
+}; // namespace VPP
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
+
+#endif
diff --git a/src/include/VppIdGen.hpp b/src/include/VppIdGen.hpp
new file mode 100644
index 0000000..01295d1
--- /dev/null
+++ b/src/include/VppIdGen.hpp
@@ -0,0 +1,40 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#ifndef __VPP_ID_GEN_H__
+#define __VPP_ID_GEN_H__
+
+#include <opflexagent/IdGenerator.h>
+
+namespace VPP
+{
+class IdGen
+{
+ public:
+ IdGen(opflexagent::IdGenerator &idGen);
+
+ uint32_t get(opflex::modb::class_id_t cid, const opflex::modb::URI &uri);
+
+ void erase(opflex::modb::class_id_t cid, const opflex::modb::URI &uri);
+
+ private:
+ const char *get_namespace(opflex::modb::class_id_t cid);
+
+ opflexagent::IdGenerator &m_id_gen;
+};
+
+} // namespace VPP
+
+#endif
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
diff --git a/src/include/VppInspect.hpp b/src/include/VppInspect.hpp
new file mode 100644
index 0000000..3b765d1
--- /dev/null
+++ b/src/include/VppInspect.hpp
@@ -0,0 +1,126 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#pragma once
+#ifndef VPP_INSPECT_H
+#define VPP_INSPECT_H
+
+#include <map>
+#include <sstream>
+#include <string>
+#include <uv.h>
+#include <vector>
+
+#include "vom/inspect.hpp"
+
+namespace opflexagent
+{
+/**
+ * A means to inspect the state VPP has built, in total, and per-client
+ * To use do:
+ * socat - UNIX-CONNECT:/path/to/sock/in/opflex.conf
+ * and follow the instructions
+ */
+class VppInspect
+{
+ public:
+ /**
+ * Constructor
+ */
+ VppInspect(const std::string &sockname);
+
+ /**
+ * Destructor to tidyup socket resources
+ */
+ ~VppInspect();
+
+ private:
+ /**
+ * Call operator for running in the thread
+ */
+ static void run(void *ctx);
+
+ /**
+ * A write request
+ */
+ struct write_req_t
+ {
+ write_req_t(std::ostringstream &output);
+ ~write_req_t();
+
+ uv_write_t req;
+ uv_buf_t buf;
+ };
+
+ /**
+ * Write a ostream to the client
+ */
+ static void do_write(uv_stream_t *client, std::ostringstream &output);
+
+ /**
+ * Called on creation of a new connection
+ */
+ static void on_connection(uv_stream_t *server, int status);
+
+ /**
+ * Call when data is written
+ */
+ static void on_write(uv_write_t *req, int status);
+
+ /**
+ * Called when data is read
+ */
+ static void
+ on_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf);
+
+ /**
+ * Called to allocate buffer space for data to be read
+ */
+ static void
+ on_alloc_buffer(uv_handle_t *handle, size_t size, uv_buf_t *buf);
+
+ /**
+ * Called to cleanup the thread and socket during destruction
+ */
+ static void on_cleanup(uv_async_t *handle);
+
+ /**
+ * Async handle so we can wakeup the loop
+ */
+ uv_async_t mAsync;
+
+ /**
+ * The libuv loop
+ */
+ uv_loop_t mServerLoop;
+
+ /**
+ * The libuv thread context in which we run the loop
+ */
+ uv_thread_t mServerThread;
+
+ /**
+ * The inspect unix domain socket name, from the config file
+ */
+ std::string mSockName;
+
+ /**
+ * VPP inspect object to handle data read from the socket
+ */
+ VOM::inspect mInspect;
+};
+};
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
+
+#endif
diff --git a/src/include/VppLog.hpp b/src/include/VppLog.hpp
new file mode 100644
index 0000000..46e05ff
--- /dev/null
+++ b/src/include/VppLog.hpp
@@ -0,0 +1,26 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#ifndef __VPP_LOG_H__
+#define __VPP_LOG_H__
+
+#include <opflexagent/logging.h>
+
+#define VLOGD LOG(opflexagent::DEBUG)
+#define VLOGW LOG(opflexagent::WARNING)
+#define VLOGI LOG(opflexagent::INFO)
+#define VLOGE LOG(opflexagent::ERROR)
+
+#endif
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
diff --git a/src/include/VppLogHandler.hpp b/src/include/VppLogHandler.hpp
new file mode 100644
index 0000000..eca4a94
--- /dev/null
+++ b/src/include/VppLogHandler.hpp
@@ -0,0 +1,56 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*!
+ * @file VppLogHandler.h
+ * @brief Interface definition file for AgentLogHandler
+ */
+/*
+ * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#ifndef __VPP_LOG_HANDLER_H__
+#define __VPP_LOG_HANDLER_H__
+
+#include <vom/logger.hpp>
+
+namespace VPP
+{
+
+/**
+ * A VOM log handler that logs to the agnet logging mechanism
+ */
+class LogHandler : public VOM::log_t::handler
+{
+ public:
+ /**
+ * Constructor
+ */
+ LogHandler() = default;
+
+ /**
+ * Desctructor
+ */
+ ~LogHandler() = default;
+
+ /**
+ * Implement log_t::handler::handle_message
+ */
+ void handle_message(const std::string &file,
+ const int line,
+ const std::string &function,
+ const VOM::log_level_t &level,
+ const std::string &message);
+
+ private:
+ /**
+ * Copy Constructor
+ */
+ LogHandler(const LogHandler &) = delete;
+};
+
+} /* namespace opflexagent */
+
+#endif
diff --git a/src/include/VppManager.hpp b/src/include/VppManager.hpp
new file mode 100644
index 0000000..3a692b8
--- /dev/null
+++ b/src/include/VppManager.hpp
@@ -0,0 +1,286 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#ifndef __VPP_MANAGER_H__
+#define __VPP_MANAGER_H__
+
+#include <boost/asio/deadline_timer.hpp>
+#include <boost/asio/ip/address.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/optional.hpp>
+
+#include <vom/hw.hpp>
+#include <vom/interface.hpp>
+
+#include <opflex/ofcore/PeerStatusListener.h>
+
+#include <utility>
+
+#include "opflexagent/Agent.h"
+#include "opflexagent/EndpointManager.h"
+#include "opflexagent/RDConfig.h"
+#include "opflexagent/TaskQueue.h"
+
+#include "VppCrossConnect.hpp"
+#include "VppRuntime.hpp"
+
+namespace VOM
+{
+class stat_reader;
+};
+namespace VPP
+{
+class EndPointManager;
+class EndPointGroupManager;
+class SecurityGroupManager;
+class ContractManager;
+class RouteManager;
+class ExtItfManager;
+
+/**
+ * @brief Makes changes to VPP to be in sync with state of MOs.
+ * Main function is to handling change notifications, generate a set
+ * of config modifications that represent the changes and apply these
+ * modifications.
+ */
+class VppManager : public opflexagent::EndpointListener,
+ public opflexagent::ServiceListener,
+ public opflexagent::ExtraConfigListener,
+ public opflexagent::PolicyListener,
+ public opflex::ofcore::PeerStatusListener,
+ public interface::event_listener,
+ private boost::noncopyable
+{
+ public:
+ /**
+ * Construct a new Vpp manager for the agent
+ * @param agent the agent object
+ * @param idGen the flow ID generator
+ */
+ VppManager(opflexagent::Agent &agent,
+ opflexagent::IdGenerator &idGen,
+ VOM::HW::cmd_q *q,
+ VOM::stat_reader *sr);
+
+ ~VppManager();
+
+ /**
+ * Module start
+ */
+ virtual void start();
+
+ /**
+ * Installs listeners for receiving updates to MODB state.
+ */
+ virtual void registerModbListeners();
+
+ /**
+ * Module stop
+ */
+ virtual void stop();
+
+ /**
+ * Enable or disable the virtual routing
+ *
+ * @param virtualRouterEnabled true to enable the router
+ * @param routerAdv true to enable IPv6 router advertisements
+ * @param mac the MAC address to use as the router MAC formatted
+ * as a colon-separated string of 6 hex-encoded bytes.
+ */
+ void setVirtualRouter(bool virtualRouterEnabled,
+ bool routerAdv,
+ const std::string &mac);
+
+ /* Interface: EndpointListener */
+ virtual void endpointUpdated(const std::string &uuid);
+ virtual void externalEndpointUpdated(const std::string &uuid);
+ virtual void remoteEndpointUpdated(const std::string &uuid);
+ virtual void secGroupSetUpdated(const EndpointListener::uri_set_t &secGrps);
+
+ /* Interface: ServiceListener */
+ virtual void serviceUpdated(const std::string &uuid);
+
+ /* Interface: ExtraConfigListener */
+ virtual void rdConfigUpdated(const opflex::modb::URI &rdURI);
+
+ /* Interface: PolicyListener */
+ virtual void egDomainUpdated(const opflex::modb::URI &egURI);
+ virtual void domainUpdated(opflex::modb::class_id_t cid,
+ const opflex::modb::URI &domURI);
+ virtual void contractUpdated(const opflex::modb::URI &contractURI);
+ virtual void configUpdated(const opflex::modb::URI &configURI);
+ virtual void externalInterfaceUpdated(const opflex::modb::URI &);
+ virtual void localRouteUpdated(const opflex::modb::URI &);
+ virtual void secGroupUpdated(const opflex::modb::URI &);
+
+ /* Interface: PortStatusListener */
+ virtual void portStatusUpdate(const std::string &portName,
+ uint32_t portNo,
+ bool fromDesc);
+
+ /**
+ * Implementation for PeerStatusListener::peerStatusUpdated
+ *
+ * @param peerHostname the host name for the connection
+ * @param peerPort the port number for the connection
+ * @param peerStatus the new status for the connection
+ */
+ virtual void peerStatusUpdated(const std::string &peerHostname,
+ int peerPort,
+ PeerStatus peerStatus);
+
+ /**
+ * Return the uplink object
+ */
+ VPP::Uplink &uplink();
+
+ /**
+ * Return the cross connect object
+ */
+ VPP::CrossConnect &crossConnect();
+
+ private:
+ /**
+ * Handle changes to a forwarding domain; only deals with
+ * cleaning up when these objects are removed.
+ *
+ * @param cid Class of the forwarding domain
+ * @param domURI URI of the changed forwarding domain
+ */
+ void handleDomainUpdate(opflex::modb::class_id_t cid,
+ const opflex::modb::URI &domURI);
+
+ /**
+ * Compare and update changes in platform config
+ *
+ * @param configURI URI of the changed contract
+ */
+ void handleConfigUpdate(const opflex::modb::URI &configURI);
+
+ /**
+ * Handle changes to port-status for endpoints and endpoint groups.
+ *
+ * @param portName Name of the port that changed
+ * @param portNo Port number of the port that changed
+ */
+ void handlePortStatusUpdate(const std::string &portName, uint32_t portNo);
+
+ /**
+ * Event listener override to get Interface events
+ */
+ void handle_interface_event(std::vector<VOM::interface::event> e);
+
+ /**
+ * Handle interface event in the task-queue context
+ */
+ void handleInterfaceEvent(std::vector<VOM::interface::event> e);
+
+ /**
+ * Handle the connect request to VPP
+ */
+ void handleInitConnection();
+
+ /**
+ * Handle a disconnect from VPP request
+ */
+ void handleCloseConnection();
+
+ /**
+ * Handle the connect request to VPP
+ */
+ void handleUplinkConfigure();
+
+ /**
+ * Handle the cross connect requests to VPP
+ */
+ void handleXConnectConfigure();
+
+ /**
+ * Handle the Vpp Boot request
+ */
+ void handleBoot();
+
+ /**
+ * Handle the Vpp sweep timeout
+ */
+ void handleSweepTimer(const boost::system::error_code &ec);
+
+ /**
+ * Handle the HW poll timeout
+ */
+ void handleHWPollTimer(const boost::system::error_code &ec);
+
+ /**
+ * Pull the HW stats
+ */
+ void handleHWStatsTimer(const boost::system::error_code &ec);
+
+ /*
+ * A collection of runtime data that is available to the other managers
+ */
+ Runtime m_runtime;
+
+ /**
+ * The internal task-queue for handling the async upates
+ */
+ opflexagent::TaskQueue m_task_queue;
+
+ /**
+ * The sweep boot state timer.
+ * This is a member here so it has access to the taskQ
+ */
+ std::unique_ptr<boost::asio::deadline_timer> m_sweep_timer;
+
+ /**
+ * CrossConnect interface manager
+ */
+ VPP::CrossConnect m_xconnect;
+
+ /**
+ * The HW poll timer
+ */
+ std::unique_ptr<boost::asio::deadline_timer> m_poll_timer;
+
+ /**
+ * The HW stats timer
+ */
+ std::unique_ptr<boost::asio::deadline_timer> m_stats_timer;
+
+ /**
+ * indicator this manager is stopping
+ */
+ volatile bool stopping;
+
+ /**
+ * indicator for hw liveness
+ */
+ bool hw_connected;
+
+ void initPlatformConfig();
+
+ /**
+ * objects to delegate task queue events to
+ */
+ std::shared_ptr<EndPointManager> m_epm;
+ std::shared_ptr<EndPointGroupManager> m_epgm;
+ std::shared_ptr<SecurityGroupManager> m_sgm;
+ std::shared_ptr<ContractManager> m_cm;
+ std::shared_ptr<RouteManager> m_rdm;
+ std::shared_ptr<ExtItfManager> m_eim;
+};
+
+} // namespace opflexagent
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
+
+#endif // VPPAGENT_VPPMANAGER_H_
diff --git a/src/include/VppRenderer.hpp b/src/include/VppRenderer.hpp
new file mode 100644
index 0000000..e9182f4
--- /dev/null
+++ b/src/include/VppRenderer.hpp
@@ -0,0 +1,142 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Include file for VppRenderer
+ *
+ * Copyright (c) 2018 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#pragma once
+#ifndef __VPP_RENDERER_H__
+#define __VPP_RENDERER_H__
+
+#include <boost/property_tree/ptree.hpp>
+
+#include <opflex/ofcore/OFFramework.h>
+#include <opflexagent/IdGenerator.h>
+#include <opflexagent/Renderer.h>
+#include <opflexagent/TunnelEpManager.h>
+
+#include <vom/hw.hpp>
+
+#include "VppInspect.hpp"
+#include "VppManager.hpp"
+
+using namespace opflexagent;
+
+namespace VPP
+{
+/**
+ * The vpp renderer demonstrates how to create a renderer plugin
+ * for OpFlex agent.
+ */
+class VppRenderer : public opflexagent::Renderer
+{
+ public:
+ /**
+ * Instantiate a vpp renderer
+ *
+ * @param agent the agent object
+ */
+ VppRenderer(opflexagent::Agent &agent,
+ IdGenerator &idGen,
+ VppManager *vppManager);
+
+ /**
+ * Destroy the renderer and clean up all state
+ */
+ virtual ~VppRenderer();
+
+ // ********
+ // Renderer
+ // ********
+
+ virtual void setProperties(const boost::property_tree::ptree &properties);
+ virtual void start();
+ virtual void stop();
+
+ /**
+ * Is uplink address owned by renderer
+ */
+ virtual bool
+ isUplinkAddressImplemented()
+ {
+ return true;
+ }
+
+ /**
+ * Get uplink address from renderer
+ */
+ virtual boost::asio::ip::address getUplinkAddress();
+
+ /**
+ * Get uplink l2 address from renderer
+ */
+ virtual std::string getUplinkMac();
+
+ private:
+ /**
+ * The socket used for inspecting the state built in VPP-manager
+ */
+ std::unique_ptr<VppInspect> inspector;
+
+ /**
+ * ID generator
+ */
+ IdGenerator &idGen;
+
+ /**
+ * Single instance of the VPP manager
+ */
+ VppManager *vppManager;
+
+ /**
+ * Opflex Tunnel EP Manager
+ */
+ TunnelEpManager tunnelEpManager;
+
+ std::string uplinkIface;
+ uint16_t uplinkVlan;
+
+ enum EncapType
+ {
+ encapTypeNone,
+ encapTypeVlan,
+ encapTypeVxlan,
+ encapTypeIvxlan
+ };
+ EncapType encapType;
+
+ /**
+ * has this party started.
+ */
+ bool started;
+};
+
+/**
+ * Plugin implementation for dynamically loading vpp
+ * renderer.
+ */
+class VppRendererPlugin : public opflexagent::RendererPlugin
+{
+ public:
+ VppRendererPlugin();
+
+ // **************
+ // RendererPlugin
+ // **************
+ virtual std::unordered_set<std::string> getNames() const;
+ virtual opflexagent::Renderer *create(opflexagent::Agent &agent) const;
+};
+
+} /* namespace vpprenderer */
+
+/**
+ * Return a non-owning pointer to the renderer plugin instance.
+ */
+extern "C" const opflexagent::RendererPlugin *init_renderer_plugin();
+
+#endif /* __VPP__RENDERER_H__ */
diff --git a/src/include/VppRouteManager.hpp b/src/include/VppRouteManager.hpp
new file mode 100644
index 0000000..eadc3d8
--- /dev/null
+++ b/src/include/VppRouteManager.hpp
@@ -0,0 +1,51 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2018 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#ifndef __VPP_ROUTE_MANAGER_H__
+#define __VPP_ROUTE_MANAGER_H__
+
+#include <opflexagent/Agent.h>
+
+#include <modelgbp/gbp/L3ExternalDomain.hpp>
+
+#include <vom/route_domain.hpp>
+
+#include "VppRuntime.hpp"
+
+namespace VPP
+{
+class RouteManager
+{
+ public:
+ RouteManager(Runtime &runtime);
+
+ void handle_domain_update(const opflex::modb::URI &uri);
+ void handle_route_update(const opflex::modb::URI &uri);
+
+ static void
+ mk_ext_nets(Runtime &runtime,
+ route_domain &rd,
+ const opflex::modb::URI &uri,
+ std::shared_ptr<modelgbp::gbp::L3ExternalDomain> ext_dom);
+
+ private:
+ /**
+ * Reference to the runtime data
+ */
+ Runtime &m_runtime;
+};
+}; // namespace VPP
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
+
+#endif
diff --git a/src/include/VppRuntime.hpp b/src/include/VppRuntime.hpp
new file mode 100644
index 0000000..0232591
--- /dev/null
+++ b/src/include/VppRuntime.hpp
@@ -0,0 +1,68 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2018 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#ifndef __VPP_RUNTIME_H__
+#define __VPP_RUNTIME_H__
+
+#include <opflexagent/Agent.h>
+
+#include "VppIdGen.hpp"
+#include "VppUplink.hpp"
+#include "VppVirtualRouter.hpp"
+
+namespace VPP
+{
+struct Runtime
+{
+ Runtime(opflexagent::Agent &agent_, opflexagent::IdGenerator &idGen)
+ : agent(agent_)
+ , id_gen(idGen)
+ , uplink(agent)
+ {
+ }
+
+ opflexagent::PolicyManager &
+ policy_manager()
+ {
+ return agent.getPolicyManager();
+ }
+
+ /**
+ * Referene to the uber-agent
+ */
+ opflexagent::Agent &agent;
+ /**
+ * ID generator instance
+ */
+ IdGen id_gen;
+ /**
+ * Uplink interface manager
+ */
+ Uplink uplink;
+ /**
+ * Virtual Router Settings
+ */
+ std::shared_ptr<VirtualRouter> vr;
+
+ /**
+ * System Name
+ */
+ std::string system_name;
+
+ /**
+ * Transport or stitch mode
+ */
+ bool is_transport_mode;
+
+ private:
+ Runtime(const Runtime &);
+};
+}; // namespace VPP
+
+#endif
diff --git a/src/include/VppSecurityGroupManager.hpp b/src/include/VppSecurityGroupManager.hpp
new file mode 100644
index 0000000..a9ce2c8
--- /dev/null
+++ b/src/include/VppSecurityGroupManager.hpp
@@ -0,0 +1,57 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2018 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#ifndef __VPP_SECURITY_GROUP_MANAGER_H__
+#define __VPP_SECURITY_GROUP_MANAGER_H__
+
+#include <opflexagent/Agent.h>
+#include <opflexagent/EndpointManager.h>
+
+#include <vom/acl_ethertype.hpp>
+#include <vom/acl_l3_list.hpp>
+
+using namespace VOM;
+
+namespace VPP
+{
+class SecurityGroupManager
+{
+ public:
+ SecurityGroupManager(opflexagent::Agent &agent);
+
+ static void
+ build_update(opflexagent::Agent &agent,
+ const opflexagent::EndpointListener::uri_set_t &secGrps,
+ const std::string &secGrpId,
+ ACL::l3_list::rules_t &in_rules,
+ ACL::l3_list::rules_t &out_rules,
+ ACL::acl_ethertype::ethertype_rules_t &ethertype_rules);
+
+ static std::string
+ get_id(const opflexagent::EndpointListener::uri_set_t &secGrps);
+
+ void
+ handle_set_update(const opflexagent::EndpointListener::uri_set_t &secGrps);
+ void handle_update(const opflex::modb::URI &uri);
+
+ private:
+ /**
+ * Referene to the uber-agent
+ */
+ opflexagent::Agent &m_agent;
+};
+}; // namespace VPP
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
+
+#endif
diff --git a/src/include/VppSpineProxy.hpp b/src/include/VppSpineProxy.hpp
new file mode 100644
index 0000000..d0be72b
--- /dev/null
+++ b/src/include/VppSpineProxy.hpp
@@ -0,0 +1,62 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2017-2018 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#ifndef __VPP_SPINE_PROXY_H__
+#define __VPP_SPINE_PROXY_H__
+
+#include <boost/asio/ip/address.hpp>
+
+namespace VOM
+{
+class vxlan_tunnel;
+};
+
+namespace VPP
+{
+/**
+ * A representation of the Spine prxy where unknown unicast packets are sent
+ */
+class SpineProxy
+{
+ public:
+ /**
+ */
+ SpineProxy(const boost::asio::ip::address_v4 &local,
+ const boost::asio::ip::address_v4 &remote_v4,
+ const boost::asio::ip::address_v4 &remote_v6,
+ const boost::asio::ip::address_v4 &remote_mac);
+
+ const std::shared_ptr<VOM::vxlan_tunnel> mk_v4(const std::string &key,
+ uint32_t vnid);
+ const std::shared_ptr<VOM::vxlan_tunnel> mk_v6(const std::string &key,
+ uint32_t vnid);
+ const std::shared_ptr<VOM::vxlan_tunnel> mk_mac(const std::string &key,
+ uint32_t vnid);
+
+ private:
+ const std::shared_ptr<VOM::vxlan_tunnel>
+ mk_intf(const std::string &key,
+ boost::asio::ip::address_v4 &src,
+ boost::asio::ip::address_v4 &dst,
+ uint32_t vnid);
+
+ boost::asio::ip::address_v4 m_local;
+ boost::asio::ip::address_v4 m_remote_v4;
+ boost::asio::ip::address_v4 m_remote_v6;
+ boost::asio::ip::address_v4 m_remote_mac;
+};
+}; // namespace VPP
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
+
+#endif
diff --git a/src/include/VppUplink.hpp b/src/include/VppUplink.hpp
new file mode 100644
index 0000000..7c8b738
--- /dev/null
+++ b/src/include/VppUplink.hpp
@@ -0,0 +1,171 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2017-2018 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#ifndef __VPP_UPLINK_H__
+#define __VPP_UPLINK_H__
+
+#include <unordered_set>
+
+#include "opflexagent/Agent.h"
+
+#include <vom/dhcp_client.hpp>
+#include <vom/tap_interface.hpp>
+#include <vom/vxlan_tunnel.hpp>
+
+//#include "VppSpineProxy.hpp"
+
+using namespace VOM;
+
+namespace VPP
+{
+class SpineProxy;
+/**
+ * A description of the uplink interface.
+ * Can be one of VLAN< VXLAN or iVXLAN
+ */
+class Uplink : public dhcp_client::event_listener
+{
+ public:
+ /**
+ * The uplink interface's encapsulation type for data traffic.
+ */
+ enum uplink_type_t
+ {
+ /**
+ * VXLAN encap
+ */
+ VXLAN,
+ /**
+ * VLAN encap
+ */
+ VLAN,
+ };
+
+ /**
+ * Default Constructor
+ */
+ Uplink(opflexagent::Agent &agent);
+
+ /**
+ * Given the VNID, create an interface of the appropriate type
+ */
+ std::shared_ptr<interface> mk_interface(const std::string &uuid,
+ uint32_t vnid);
+
+ /**
+ * Set the uplink paramenters for vxlan
+ */
+ void set(const std::string &uplink,
+ uint16_t vlan,
+ const std::string &name,
+ const boost::asio::ip::address &ip,
+ uint16_t port);
+
+ /**
+ * Set the uplink paramenters for vlan
+ */
+ void set(const std::string &uplink, uint16_t vlan, const std::string &name);
+
+ /**
+ * make the control channel/interfaces
+ *
+ * @param fqdn Fully Qualifed Domain name
+ */
+ void configure(const std::string &fqdn);
+
+ /**
+ * insert the new slave interface in the slave_ifaces
+ */
+ void insert_slave_ifaces(std::string name);
+
+ /**
+ * insert the dhcp options
+ */
+ void insert_dhcp_options(std::string name);
+
+ const boost::asio::ip::address &local_address() const;
+ const std::shared_ptr<interface> local_interface() const;
+ const std::string uplink_l2_address() const;
+ /**
+ * Handle notifications about DHCP complete
+ */
+ void handle_dhcp_event(std::shared_ptr<dhcp_client::lease_t> lease);
+
+ std::shared_ptr<SpineProxy> spine_proxy();
+ const std::string &system_name() const;
+
+ private:
+ void handle_dhcp_event_i(std::shared_ptr<dhcp_client::lease_t> lease);
+
+ /**
+ * Configure the tap interface
+ */
+ void configure_tap(const route::prefix_t &pfx);
+
+ /**
+ * VXLAN uplink encap, if used
+ */
+ vxlan_tunnel::endpoint_t m_vxlan;
+
+ /**
+ * A reference to the uplink physical insterface
+ */
+ std::shared_ptr<interface> m_uplink;
+
+ /**
+ * A reference to the uplink vlan insterface
+ */
+ std::shared_ptr<interface> m_subitf;
+
+ /**
+ * the encap type on the uplinnk
+ */
+ uplink_type_t m_type;
+
+ /**
+ * The VLAN used for control traffic
+ */
+ uint16_t m_vlan;
+
+ /**
+ * The name of the uplink interface
+ */
+ std::string m_iface;
+
+ /**
+ * The system name
+ */
+ std::string m_system_name;
+
+ /**
+ * The name of the slave interfaces (in case of Bond)
+ */
+ std::unordered_set<std::string> slave_ifaces;
+
+ /**
+ * The dhcp options for uplink interface
+ */
+ std::unordered_set<std::string> dhcp_options;
+
+ /**
+ * the uber agent
+ */
+ opflexagent::Agent &m_agent;
+
+ route::prefix_t m_pfx;
+};
+};
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
+
+#endif
diff --git a/src/include/VppUtil.hpp b/src/include/VppUtil.hpp
new file mode 100644
index 0000000..bf3a84d
--- /dev/null
+++ b/src/include/VppUtil.hpp
@@ -0,0 +1,35 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2017-2018 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#ifndef __VPP_UTIL_H__
+#define __VPP_UTIL_H__
+
+#include <opflex/modb/MAC.h>
+
+#include <boost/optional.hpp>
+
+#include <vom/interface.hpp>
+
+using namespace VOM;
+
+namespace VPP
+{
+const interface::type_t &getIntfTypeFromName(const std::string &name);
+
+boost::optional<mac_address_t>
+mac_from_modb(boost::optional<const opflex::modb::MAC &>);
+};
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
+
+#endif
diff --git a/src/include/VppVirtualRouter.hpp b/src/include/VppVirtualRouter.hpp
new file mode 100644
index 0000000..f6aef8d
--- /dev/null
+++ b/src/include/VppVirtualRouter.hpp
@@ -0,0 +1,51 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#ifndef __VPP_VIRTUALROUTER_H__
+#define __VPP_VIRTUALROUTER_H__
+
+#include <vom/types.hpp>
+
+using namespace VOM;
+
+namespace VPP
+{
+/**
+ * A description of the VirtualRouter.
+ * Can be one of VLAN< VXLAN or iVXLAN
+ */
+class VirtualRouter
+{
+ public:
+ /**
+ * Default Constructor
+ */
+ VirtualRouter(const VOM::mac_address_t &mac);
+ VirtualRouter(const VirtualRouter &vr) = default;
+
+ /**
+ * Get the router virtual MAC
+ */
+ const VOM::mac_address_t &mac() const;
+
+ private:
+ /**
+ * Virtual Router Mac
+ */
+ VOM::mac_address_t m_mac;
+};
+};
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
+
+#endif
diff --git a/src/test/VppManager_test.cpp b/src/test/VppManager_test.cpp
new file mode 100644
index 0000000..bdae4ba
--- /dev/null
+++ b/src/test/VppManager_test.cpp
@@ -0,0 +1,1841 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Test suite for class VppManager
+ *
+ * Copyright (c) 2017 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#include <memory>
+
+#include <boost/asio/ip/host_name.hpp>
+#include <boost/optional.hpp>
+#include <boost/test/unit_test.hpp>
+
+#include <modelgbp/gbp/HashingAlgorithmEnumT.hpp>
+#include <modelgbp/gbp/L3IfTypeEnumT.hpp>
+#include <modelgbp/gbp/SecGroup.hpp>
+
+#include <vom/acl_ethertype.hpp>
+#include <vom/acl_l2_list.hpp>
+#include <vom/acl_l3_list.hpp>
+#include <vom/bridge_domain.hpp>
+#include <vom/bridge_domain_arp_entry.hpp>
+#include <vom/bridge_domain_entry.hpp>
+#include <vom/dhcp_client.hpp>
+#include <vom/gbp_contract.hpp>
+#include <vom/gbp_endpoint.hpp>
+#include <vom/gbp_endpoint_group.hpp>
+#include <vom/gbp_ext_itf.hpp>
+#include <vom/gbp_subnet.hpp>
+#include <vom/gbp_vxlan.hpp>
+#include <vom/hw.hpp>
+#include <vom/igmp_binding.hpp>
+#include <vom/igmp_listen.hpp>
+#include <vom/inspect.hpp>
+#include <vom/interface.hpp>
+#include <vom/interface_cmds.hpp>
+#include <vom/l2_binding.hpp>
+#include <vom/l2_emulation.hpp>
+#include <vom/l3_binding.hpp>
+#include <vom/lldp_binding.hpp>
+#include <vom/lldp_global.hpp>
+#include <vom/nat_binding.hpp>
+#include <vom/nat_static.hpp>
+#include <vom/neighbour.hpp>
+#include <vom/route.hpp>
+#include <vom/route_domain.hpp>
+#include <vom/stat_reader.hpp>
+#include <vom/sub_interface.hpp>
+
+#include "VppManager.hpp"
+#include "opflexagent/test/ModbFixture.h"
+#include <opflexagent/logging.h>
+
+using namespace VOM;
+using namespace opflexagent;
+using boost::asio::ip::address;
+using boost::asio::ip::address_v4;
+
+BOOST_AUTO_TEST_SUITE(vpp)
+
+struct MockStatReader : public stat_reader
+{
+ int
+ connect()
+ {
+ }
+
+ void
+ disconnect()
+ {
+ }
+
+ void
+ read()
+ {
+ }
+};
+
+class MockCmdQ : public HW::cmd_q
+{
+ public:
+ MockCmdQ()
+ : handle(0)
+ , m_mutex()
+ {
+ }
+ ~MockCmdQ()
+ {
+ }
+
+ void
+ enqueue(cmd *c)
+ {
+ std::shared_ptr<cmd> sp(c);
+ m_cmds.push(sp);
+ }
+ void
+ enqueue(std::queue<cmd *> &cmds)
+ {
+ cmd *c;
+
+ while (!cmds.empty())
+ {
+ c = cmds.front();
+ cmds.pop();
+
+ std::shared_ptr<cmd> sp(c);
+ m_cmds.push(sp);
+ }
+ }
+ void
+ enqueue(std::shared_ptr<cmd> c)
+ {
+ m_cmds.push(c);
+ }
+
+ void
+ dequeue(cmd *f)
+ {
+ }
+
+ void
+ dequeue(std::shared_ptr<cmd> cmd)
+ {
+ }
+
+ rc_t
+ write()
+ {
+ /*
+ * the unit tests are executed in thread x and the VppManager
+ * task queue executes in thread y. both call write() when
+ * objects are destroyed, even though the objects in the
+ * test case do not issue commands. Which thread runs write
+ * is not important.
+ * N.B. this is an artefact of the way the unit-tests are
+ * structered and run, this does not afflict the real system
+ * where *all* objects are created and destroyed with the
+ * VppManager taskQueue context.
+ */
+ std::lock_guard<std::mutex> lg(m_mutex);
+
+ std::shared_ptr<cmd> c;
+
+ while (!m_cmds.empty())
+ {
+ c = m_cmds.front();
+ m_cmds.pop();
+ handle_cmd(c.get());
+ }
+
+ return (rc_t::OK);
+ }
+
+ /**
+ * Blocking Connect to VPP - call once at bootup
+ */
+ bool
+ connect()
+ {
+ return true;
+ }
+
+ void
+ disconnect()
+ {
+ }
+
+ private:
+ void
+ handle_cmd(cmd *c)
+ {
+ {
+ auto ac =
+ dynamic_cast<interface::create_cmd<vapi::Af_packet_create> *>(
+ c);
+ if (NULL != ac)
+ {
+ HW::item<handle_t> res(++handle, rc_t::OK);
+ ac->item() = res;
+ }
+ }
+ {
+ auto ac =
+ dynamic_cast<interface::create_cmd<vapi::Create_vlan_subif> *>(
+ c);
+ if (NULL != ac)
+ {
+ HW::item<handle_t> res(++handle, rc_t::OK);
+ ac->item() = res;
+ }
+ }
+
+ c->succeeded();
+ }
+ uint32_t handle;
+
+ std::mutex m_mutex;
+
+ std::queue<std::shared_ptr<cmd>> m_cmds;
+};
+
+template <typename T>
+bool
+is_match(const T &expected)
+{
+ std::shared_ptr<T> actual = T::find(expected.key());
+
+ if (!actual) return false;
+
+ return (expected == *actual);
+}
+
+template <typename T>
+bool
+is_present(const T &search)
+{
+ std::shared_ptr<T> actual = T::find(search.key());
+
+ if (!actual) return false;
+
+ return (true);
+}
+
+#define WAIT_FOR1(stmt) WAIT_FOR((stmt), 100)
+
+template <typename T>
+static void
+print_obj(const T &obj, const std::string &s)
+{
+ LOG(ERROR) << s << obj.to_string();
+}
+
+#define WAIT_FOR_MATCH(obj) \
+ WAIT_FOR_ONFAIL(is_match(obj), 100, print_obj(obj, "Not Found: "))
+#define WAIT_FOR_NOT_PRESENT(obj) \
+ WAIT_FOR_ONFAIL(!is_present(obj), 100, print_obj(obj, "Still present: "))
+
+class VppManagerFixture : public ModbFixture
+{
+ public:
+ typedef opflex::ofcore::OFConstants::OpflexElementMode opflex_elem_t;
+
+ public:
+ VppManagerFixture(opflex_elem_t mode = opflex_elem_t::INVALID_MODE)
+ : ModbFixture(mode)
+ , vMac{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}
+ , policyMgr(agent.getPolicyManager())
+ , vppQ()
+ , vppSR()
+ , vppManager(agent, idGen, &vppQ, &vppSR)
+ , inspector()
+ {
+ createVppObjects();
+ WAIT_FOR(policyMgr.groupExists(epg0->getURI()), 500);
+ WAIT_FOR(policyMgr.getBDForGroup(epg0->getURI()) != boost::none, 500);
+
+ WAIT_FOR(policyMgr.groupExists(epg1->getURI()), 500);
+ WAIT_FOR(policyMgr.getRDForGroup(epg1->getURI()) != boost::none, 500);
+
+ vppManager.uplink().set("opflex-itf", 4093, "opflex-host");
+ vppManager.setVirtualRouter(true, true, vMac.to_string());
+ }
+
+ virtual ~VppManagerFixture()
+ {
+ vppManager.stop();
+ agent.stop();
+ }
+
+ void
+ createVppObjects()
+ {
+ using opflex::modb::Mutator;
+ using namespace modelgbp;
+ using namespace modelgbp::gbp;
+ using namespace modelgbp::gbpe;
+
+ /*
+ * create EPGs and forwarding objects
+ * VPP Rnederer support the opnstack variant of the opflex model
+ * one EPG per-BD, one subnet per BD.
+ */
+ Mutator mutator(framework, policyOwner);
+ config = universe->addPlatformConfig("default");
+ config->setMulticastGroupIP("224.1.1.1");
+
+ fd0 = space->addGbpFloodDomain("fd0");
+ fd0->setUnknownFloodMode(UnknownFloodModeEnumT::CONST_HWPROXY);
+ fd1 = space->addGbpFloodDomain("fd1");
+ fd1->setUnknownFloodMode(UnknownFloodModeEnumT::CONST_FLOOD);
+ bd0 = space->addGbpBridgeDomain("bd0");
+ bd0->addGbpeInstContext()->setEncapId(0xAA);
+ bd0->addGbpeInstContext()->setMulticastGroupIP("224.1.1.1");
+
+ bd1 = space->addGbpBridgeDomain("bd1");
+ rd0 = space->addGbpRoutingDomain("rd0");
+ rd0->addGbpeInstContext()->setEncapId(0xBB);
+ ret_pol0 = space->addGbpeEndpointRetention("ret-pol0");
+ ret_pol0->setRemoteEpAgingInterval(90);
+
+ fd0->addGbpFloodDomainToNetworkRSrc()->setTargetBridgeDomain(
+ bd0->getURI());
+ fd0ctx = fd0->addGbpeFloodContext();
+ fd1->addGbpFloodDomainToNetworkRSrc()->setTargetBridgeDomain(
+ bd1->getURI());
+
+ bd0->addGbpBridgeDomainToNetworkRSrc()->setTargetRoutingDomain(
+ rd0->getURI());
+ bd1->addGbpBridgeDomainToNetworkRSrc()->setTargetRoutingDomain(
+ rd0->getURI());
+
+ subnetsfd0 = space->addGbpSubnets("subnetsfd0");
+ subnetsfd0_1 = subnetsfd0->addGbpSubnet("subnetsfd0_1");
+ subnetsfd0_1->setAddress("10.20.44.1")
+ .setPrefixLen(24)
+ .setVirtualRouterIp("10.20.44.1");
+ subnetsfd0_2 = subnetsfd0->addGbpSubnet("subnetsfd0_2");
+ subnetsfd0_2->setAddress("2001:db8::")
+ .setPrefixLen(32)
+ .setVirtualRouterIp("2001:db8::1");
+ fd0->addGbpForwardingBehavioralGroupToSubnetsRSrc()->setTargetSubnets(
+ subnetsfd0->getURI());
+ rd0->addGbpRoutingDomainToIntSubnetsRSrc(
+ subnetsfd0->getURI().toString());
+
+ subnetsfd1 = space->addGbpSubnets("subnetsfd1");
+ subnetsfd1_1 = subnetsfd0->addGbpSubnet("subnetsfd1_1");
+ subnetsfd1_1->setAddress("10.20.45.0")
+ .setPrefixLen(24)
+ .setVirtualRouterIp("10.20.45.1");
+ fd1->addGbpForwardingBehavioralGroupToSubnetsRSrc()->setTargetSubnets(
+ subnetsfd1->getURI());
+ rd0->addGbpRoutingDomainToIntSubnetsRSrc(
+ subnetsfd1->getURI().toString());
+
+ epg0 = space->addGbpEpGroup("epg0");
+ epg0->addGbpEpGroupToNetworkRSrc()->setTargetBridgeDomain(
+ bd0->getURI());
+ epg0->addGbpEpGroupToNetworkRSrc()->setTargetFloodDomain(fd0->getURI());
+ epg0->addGbpeInstContext()->setEncapId(0xA0A);
+ epg0->addGbpeInstContext()->setClassid(0xBA);
+ epg0->addGbpeInstContext()
+ ->addGbpeInstContextToEpRetentionRSrc()
+ ->setTargetEndpointRetention(ret_pol0->getURI());
+
+ epg1 = space->addGbpEpGroup("epg1");
+ epg1->addGbpEpGroupToNetworkRSrc()->setTargetBridgeDomain(
+ bd1->getURI());
+ epg1->addGbpeInstContext()->setEncapId(0xA0B);
+ epg1->addGbpeInstContext()->setClassid(0xB0B);
+
+ epg2 = space->addGbpEpGroup("epg2");
+ epg2->addGbpeInstContext()->setClassid(0xCA);
+ epg3 = space->addGbpEpGroup("epg3");
+ epg3->addGbpeInstContext()->setClassid(0xCB);
+
+ /*
+ * L3Out objects
+ */
+ ext_rd0 = space->addGbpRoutingDomain("ext_rd0");
+ ext_rd0->addGbpeInstContext()->setEncapId(0x010203);
+ ext_dom = ext_rd0->addGbpL3ExternalDomain("ext_dom0");
+ ext_net0 = ext_dom->addGbpL3ExternalNetwork("ext_dom0_net0");
+ ext_net0->addGbpeInstContext()->setClassid(1234);
+ ext_net0->addGbpExternalSubnet("ext_dom0_net0_sub0")
+ ->setAddress("105.0.0.0")
+ .setPrefixLen(24);
+ ext_net0->addGbpExternalSubnet("ext_dom0_net0_sub1")
+ ->setAddress("106.0.0.0")
+ .setPrefixLen(24);
+ ext_net1 = ext_dom->addGbpL3ExternalNetwork("ext_dom1_net0");
+ ext_net1->addGbpeInstContext()->setClassid(1235);
+ ext_net1->addGbpExternalSubnet("ext_dom0_net1_sub0")
+ ->setAddress("107.0.0.0")
+ .setPrefixLen(24);
+ ext_net1->addGbpExternalSubnet("ext_dom0_net1_sub1")
+ ->setAddress("108.0.0.0")
+ .setPrefixLen(24);
+ ext_bd0 = space->addGbpExternalL3BridgeDomain("ext_bd0");
+ ext_bd0->addGbpeInstContext()->setEncapId(1133);
+ ext_bd0->addGbpeInstContext()->setClassid(2233);
+ ext_bd0->addGbpeInstContext()->setMulticastGroupIP("224.1.2.2");
+ ext_bd0->addGbpExternalL3BridgeDomainToVrfRSrc()
+ ->setTargetRoutingDomain(ext_rd0->getURI());
+ ext_node0 = space->addGbpExternalNode("ext_node0");
+ ext_itf0 = space->addGbpExternalInterface("ext_itf0");
+ ext_itf0->setAddress("10.30.0.1");
+ ext_itf0->setEncap(1144);
+ // ext_itf0->addGbpeInstContext()->setClassid(2244);
+ ext_itf0->setMac(opflex::modb::MAC("00:00:00:00:80:00"));
+ ext_itf0->setIfInstT(L3IfTypeEnumT::CONST_EXTSVI);
+ ext_itf0->addGbpExternalInterfaceToExtl3bdRSrc()
+ ->setTargetExternalL3BridgeDomain(ext_bd0->getURI());
+ ext_itf0->addGbpExternalInterfaceToL3outRSrc()
+ ->setTargetL3ExternalDomain(ext_dom->getURI());
+
+ /* static_route1 = ext_node0->addGbpStaticRoute("static_route1"); */
+ /* static_route1->addGbpStaticRouteToVrfRSrc()->setTargetRoutingDomain(
+ */
+ /* ext_rd0->getURI()); */
+ /* static_route1->setAddress("101.101.0.0"); */
+ /* static_route1->setPrefixLen(16); */
+ /* static_route1->addGbpStaticNextHop("100.100.100.2"); */
+ /* static_route1->addGbpStaticNextHop("100.100.100.4"); */
+
+ mutator.commit();
+
+ /* create endpoints */
+ ep0.reset(new Endpoint("0-0-0-0"));
+ ep0->setInterfaceName("port80");
+ ep0->setMAC(opflex::modb::MAC("00:00:00:00:80:00"));
+ ep0->addIP("10.20.44.2");
+ ep0->addIP("10.20.44.3");
+ ep0->addIP("2001:db8::2");
+ ep0->addIP("2001:db8::3");
+ ep0->addAnycastReturnIP("10.20.44.2");
+ ep0->addAnycastReturnIP("2001:db8::2");
+ ep0->setEgURI(epg0->getURI());
+ epSrc.updateEndpoint(*ep0);
+
+ ep1.reset(new Endpoint("0-0-0-1"));
+ ep1->setMAC(opflex::modb::MAC("00:00:00:00:00:01"));
+ ep1->addIP("10.20.45.21");
+ ep1->setEgURI(epg0->getURI());
+ epSrc.updateEndpoint(*ep1);
+
+ ep2.reset(new Endpoint("0-0-0-2"));
+ ep2->setMAC(opflex::modb::MAC("00:00:00:00:00:02"));
+ ep2->addIP("10.20.45.21");
+ ep2->setInterfaceName("port11");
+ ep2->setEgURI(epg1->getURI());
+ epSrc.updateEndpoint(*ep2);
+
+ ep3.reset(new Endpoint("0-0-0-3"));
+ ep3->setMAC(opflex::modb::MAC("00:00:00:00:00:03"));
+ ep3->addIP("10.20.45.31");
+ ep3->setInterfaceName("eth3");
+ ep3->setEgURI(epg1->getURI());
+ epSrc.updateEndpoint(*ep3);
+
+ ep4.reset(new Endpoint("0-0-0-4"));
+ ep4->setMAC(opflex::modb::MAC("00:00:00:00:00:04"));
+ ep4->addIP("10.20.45.41");
+ ep4->setInterfaceName("port40");
+ ep4->setAccessIfaceVlan(1000);
+ ep4->setEgURI(epg1->getURI());
+ epSrc.updateEndpoint(*ep4);
+
+ ext_ep0.reset(new Endpoint("0-0-e-0"));
+ ext_ep0->setMAC(opflex::modb::MAC("00:00:00:00:0E:00"));
+ ext_ep0->addIP("10.30.0.2");
+ ext_ep0->setInterfaceName("port-e-00");
+ ext_ep0->setEgURI(ext_itf0->getURI());
+ ext_ep0->setExtInterfaceURI(ext_itf0->getURI());
+ ext_ep0->setExtNodeURI(ext_node0->getURI());
+ ext_ep0->setExternal();
+ epSrc.updateEndpoint(*ext_ep0);
+ }
+
+ void
+ createNatObjects()
+ {
+ using std::shared_ptr;
+ using namespace modelgbp::gbp;
+ using namespace opflex::modb;
+
+ shared_ptr<modelgbp::policy::Space> common;
+ shared_ptr<FloodDomain> fd_ext;
+ shared_ptr<BridgeDomain> bd_ext;
+ shared_ptr<Subnets> subnets_ext;
+ shared_ptr<L3ExternalDomain> l3ext;
+
+ Mutator mutator(framework, policyOwner);
+ common = universe->addPolicySpace("common");
+ bd_ext = common->addGbpBridgeDomain("bd_ext");
+ rd_ext = common->addGbpRoutingDomain("rd_ext");
+ fd_ext = common->addGbpFloodDomain("fd_ext");
+
+ fd_ext->addGbpFloodDomainToNetworkRSrc()->setTargetBridgeDomain(
+ bd_ext->getURI());
+ bd_ext->addGbpBridgeDomainToNetworkRSrc()->setTargetRoutingDomain(
+ rd_ext->getURI());
+
+ subnets_ext = common->addGbpSubnets("subnets_ext");
+ subnets_ext->addGbpSubnet("subnet_ext4")
+ ->setAddress("5.5.5.0")
+ .setPrefixLen(24);
+
+ bd_ext->addGbpForwardingBehavioralGroupToSubnetsRSrc()
+ ->setTargetSubnets(subnets_ext->getURI());
+ rd_ext->addGbpRoutingDomainToIntSubnetsRSrc(
+ subnets_ext->getURI().toString());
+
+ epg_nat = common->addGbpEpGroup("nat-epg");
+ epg_nat->addGbpeInstContext()->setEncapId(0x424);
+ epg_nat->addGbpeInstContext()->setClassid(0x44);
+ epg_nat->addGbpEpGroupToNetworkRSrc()->setTargetFloodDomain(
+ fd_ext->getURI());
+
+ l3ext = rd0->addGbpL3ExternalDomain("ext");
+ l3ext_net = l3ext->addGbpL3ExternalNetwork("outside");
+ l3ext_net->addGbpExternalSubnet("outside")
+ ->setAddress("5.5.0.0")
+ .setPrefixLen(16);
+ mutator.commit();
+
+ Endpoint::IPAddressMapping ipm4("91c5b217-d244-432c-922d-533c6c036ab3");
+ ipm4.setMappedIP("10.20.44.2");
+ ipm4.setFloatingIP("5.5.5.5");
+ ipm4.setEgURI(epg_nat->getURI());
+ ep0->addIPAddressMapping(ipm4);
+ epSrc.updateEndpoint(*ep0);
+
+ WAIT_FOR(policyMgr.getRDForGroup(epg_nat->getURI()) != boost::none,
+ 500);
+ PolicyManager::subnet_vector_t sns;
+ WAIT_FOR_DO(sns.size() == 1, 500, sns.clear();
+ policyMgr.getSubnetsForGroup(epg_nat->getURI(), sns));
+ }
+
+ void
+ assignEpg0ToFd0()
+ {
+ PolicyManager::subnet_vector_t sns;
+ opflex::modb::Mutator mutator(framework, policyOwner);
+ epg0->addGbpEpGroupToNetworkRSrc()->setTargetFloodDomain(fd0->getURI());
+ mutator.commit();
+
+ WAIT_FOR1(policyMgr.getFDForGroup(epg0->getURI()) != boost::none);
+ WAIT_FOR_DO(sns.size() == 3, 500, sns.clear();
+ policyMgr.getSubnetsForGroup(epg0->getURI(), sns));
+ WAIT_FOR1(
+ (PolicyManager::getRouterIpForSubnet(*sns[1]) != boost::none));
+ }
+
+ void
+ do_dhcp()
+ {
+ host = boost::asio::ip::address::from_string("192.168.1.1");
+ router = boost::asio::ip::address::from_string("192.168.1.2");
+
+ route::prefix_t pfx(host, 24);
+ mac_address_t mac("00:00:11:22:33:44");
+
+ /*
+ * boot phase so the VPP/host address is learnt
+ */
+ interface v_phy("opflex-itf",
+ interface::type_t::AFPACKET,
+ interface::admin_state_t::UP);
+ sub_interface v_sub(v_phy, interface::admin_state_t::UP, 4093);
+
+ WAIT_FOR_MATCH(v_phy);
+ WAIT_FOR_MATCH(v_sub);
+
+ std::string fqdn = boost::asio::ip::host_name();
+ WAIT_FOR_MATCH(dhcp_client(v_sub, fqdn));
+ WAIT_FOR_MATCH(lldp_global(fqdn, 5, 2));
+ WAIT_FOR_MATCH(lldp_binding(v_phy, "uplink-interface"));
+
+ std::shared_ptr<dhcp_client::lease_t> lease =
+ std::make_shared<dhcp_client::lease_t>(dhcp_client::state_t::BOUND,
+ v_sub.singular(),
+ router,
+ pfx,
+ boost::asio::ip::host_name(),
+ mac);
+
+ vppManager.uplink().handle_dhcp_event(lease);
+ }
+
+ void
+ removeEpg(std::shared_ptr<modelgbp::gbp::EpGroup> epg)
+ {
+ opflex::modb::Mutator m2(framework, policyOwner);
+ epg->remove();
+ m2.commit();
+ WAIT_FOR1(!policyMgr.groupExists(epg->getURI()));
+ }
+
+ std::vector<boost::asio::ip::address>
+ getEPIps(std::shared_ptr<Endpoint> ep)
+ {
+ std::vector<boost::asio::ip::address> ipAddresses;
+ boost::system::error_code ec;
+
+ for (const std::string &ipStr : ep->getIPs())
+ {
+ boost::asio::ip::address addr =
+ boost::asio::ip::address::from_string(ipStr, ec);
+ if (!ec)
+ {
+ ipAddresses.push_back(addr);
+ }
+ }
+
+ return ipAddresses;
+ }
+
+ address host, router;
+ std::shared_ptr<Endpoint> ep5, ext_ep0;
+ std::shared_ptr<modelgbp::gbp::BridgeDomain> bd2;
+ std::shared_ptr<modelgbp::gbp::EpGroup> epg_nat;
+ std::shared_ptr<modelgbp::gbp::L3ExternalNetwork> l3ext_net;
+ std::shared_ptr<modelgbp::gbp::RoutingDomain> rd_ext, ext_rd0;
+ std::shared_ptr<modelgbp::gbp::ExternalNode> ext_node0;
+ std::shared_ptr<modelgbp::gbp::ExternalL3BridgeDomain> ext_bd0;
+ std::shared_ptr<modelgbp::gbp::ExternalInterface> ext_itf0;
+ std::shared_ptr<modelgbp::epdr::LocalRoute> static_route1;
+ std::shared_ptr<modelgbp::gbp::L3ExternalDomain> ext_dom;
+ std::shared_ptr<modelgbp::gbp::L3ExternalNetwork> ext_net0;
+ std::shared_ptr<modelgbp::gbp::L3ExternalNetwork> ext_net1;
+ std::shared_ptr<modelgbp::gbpe::EndpointRetention> ret_pol0;
+
+ mac_address_t vMac;
+ PolicyManager &policyMgr;
+ IdGenerator idGen;
+ MockCmdQ vppQ;
+ MockStatReader vppSR;
+
+ VPP::VppManager vppManager;
+
+ /**
+ * To assist in checking the state that is present manually do
+ *
+ * inspector.handle_input("all", std::cout);
+ *
+ * in any of the test-cases
+ */
+ inspect inspector;
+};
+
+class VppStitchedManagerFixture : public VppManagerFixture
+{
+ public:
+ VppStitchedManagerFixture()
+ {
+ vppManager.start();
+ }
+ ~VppStitchedManagerFixture()
+ {
+ vppManager.stop();
+ }
+};
+
+class VppTransportManagerFixture : public VppManagerFixture
+{
+ public:
+ VppTransportManagerFixture()
+ : VppManagerFixture(opflex_elem_t::TRANSPORT_MODE)
+ {
+ framework.setElementMode(
+ opflex::ofcore::OFConstants::OpflexElementMode::TRANSPORT_MODE);
+ boost::system::error_code ec;
+ boost::asio::ip::address_v4 proxyAddress;
+
+ proxyAddress =
+ boost::asio::ip::address_v4::from_string("44.44.44.44", ec);
+ framework.setV4Proxy(proxyAddress);
+ proxyAddress =
+ boost::asio::ip::address_v4::from_string("66.66.66.66", ec);
+ framework.setV6Proxy(proxyAddress);
+ proxyAddress =
+ boost::asio::ip::address_v4::from_string("55.55.55.55", ec);
+ framework.setMacProxy(proxyAddress);
+ vppManager.start();
+ }
+ ~VppTransportManagerFixture()
+ {
+ vppManager.stop();
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(start, VppStitchedManagerFixture)
+{
+ /*
+ * Validate the presence of the uplink state built at startup/boot
+ * - the physical unplink interface
+ * - the control VLAN sub-interface
+ * - DHCP configuration on the sub-interface
+ * - LLDP config on the physical interface
+ */
+ interface v_phy("opflex-itf",
+ interface::type_t::AFPACKET,
+ interface::admin_state_t::UP);
+ sub_interface v_sub(v_phy, interface::admin_state_t::UP, 4093);
+
+ WAIT_FOR_MATCH(v_phy);
+ WAIT_FOR_MATCH(v_sub);
+
+ std::string fqdn = boost::asio::ip::host_name();
+ WAIT_FOR_MATCH(dhcp_client(v_sub, fqdn));
+ WAIT_FOR_MATCH(lldp_global(fqdn, 5, 2));
+ WAIT_FOR_MATCH(lldp_binding(v_phy, "uplink-interface"));
+}
+
+BOOST_FIXTURE_TEST_CASE(endpoint_group_add_del, VppStitchedManagerFixture)
+{
+ vppManager.egDomainUpdated(epg0->getURI());
+ // vppManager.domainUpdated(modelgbp::gbp::RoutingDomain::CLASS_ID,
+ // rd0->getURI());
+
+ /*
+ * Check for a bridge domain 100
+ */
+ bridge_domain v_bd_epg0(100, bridge_domain::learning_mode_t::OFF);
+ WAIT_FOR_MATCH(v_bd_epg0);
+
+ /*
+ * check for the presence of a VOM route-domain matching the EPG's
+ * ID's are offset by 100.
+ */
+ route_domain v_rd(100);
+ WAIT_FOR_MATCH(v_rd);
+
+ /*
+ * After waiting for the route-domain to be created
+ * all other state should now be present
+ */
+
+ /*
+ * Find the BVI interface. the BVI's name includes the bridge-domain ID
+ * the interface has a dependency on the route domain, so we 'new' the
+ * interface so we can control its lifetime.
+ */
+ interface *v_bvi_epg0 = new interface(
+ "bvi-100", interface::type_t::BVI, interface::admin_state_t::UP, v_rd);
+ v_bvi_epg0->set(vMac);
+
+ WAIT_FOR_MATCH(*v_bvi_epg0);
+
+ /*
+ * the BVI is put in the bridge-domain
+ */
+ WAIT_FOR_MATCH(l2_binding(*v_bvi_epg0, v_bd_epg0));
+
+ /*
+ * The EPG uplink interface, also bound to BD=1
+ */
+ interface v_phy("opflex-itf",
+ interface::type_t::AFPACKET,
+ interface::admin_state_t::UP);
+ sub_interface v_upl_epg0(v_phy, interface::admin_state_t::UP, 0xA0A);
+ WAIT_FOR_MATCH(v_upl_epg0);
+ WAIT_FOR_MATCH(l2_binding(v_upl_epg0, v_bd_epg0));
+
+ gbp_bridge_domain *v_gbd0 = new gbp_bridge_domain(v_bd_epg0, *v_bvi_epg0);
+ WAIT_FOR_MATCH(*v_gbd0);
+
+ gbp_endpoint_group *v_epg0 =
+ new gbp_endpoint_group(0xA0A, 0xBA, v_upl_epg0, v_rd, *v_gbd0);
+ v_epg0->set({120});
+ WAIT_FOR_MATCH(*v_epg0);
+
+ /*
+ * Add EPG0 into FD0 to assign it subnets
+ */
+ assignEpg0ToFd0();
+ vppManager.egDomainUpdated(epg0->getURI());
+
+ /*
+ * An entry in the L2FIB for the BVI
+ */
+ WAIT_FOR_MATCH(bridge_domain_entry(v_bd_epg0, vMac, *v_bvi_epg0));
+
+ /*
+ * check for an L3 binding and BD ARP for all of the router IPs
+ */
+ WAIT_FOR_MATCH(
+ l3_binding(*v_bvi_epg0, {address::from_string("10.20.44.1")}));
+ WAIT_FOR_MATCH(bridge_domain_arp_entry(
+ v_bd_epg0, address::from_string("10.20.44.1"), vMac));
+ WAIT_FOR_MATCH(
+ l3_binding(*v_bvi_epg0, {address::from_string("2001:db8::1")}));
+ WAIT_FOR_MATCH(bridge_domain_arp_entry(
+ v_bd_epg0, address::from_string("2001:db8::1"), vMac));
+
+ /*
+ * there should be a route for each of those sub-nets via the epg-uplink
+ */
+ WAIT_FOR_MATCH(gbp_subnet(v_rd,
+ {address::from_string("10.20.44.0"), 24},
+ gbp_subnet::type_t::STITCHED_INTERNAL));
+ WAIT_FOR_MATCH(gbp_subnet(v_rd,
+ {address::from_string("2001:db8::"), 32},
+ gbp_subnet::type_t::STITCHED_INTERNAL));
+
+ /*
+ * Routing-domain update. This should be a no-op change. Verify the subnets
+ * still exist.
+ */
+ vppManager.domainUpdated(modelgbp::gbp::RoutingDomain::CLASS_ID,
+ rd0->getURI());
+ WAIT_FOR_MATCH(gbp_subnet(v_rd,
+ {address::from_string("10.20.44.0"), 24},
+ gbp_subnet::type_t::STITCHED_INTERNAL));
+ WAIT_FOR_MATCH(gbp_subnet(v_rd,
+ {address::from_string("2001:db8::"), 32},
+ gbp_subnet::type_t::STITCHED_INTERNAL));
+
+ /*
+ * Add a second group, same BD different RD
+ */
+ vppManager.egDomainUpdated(epg1->getURI());
+ /* //assignEpg0ToFd0(sns); */
+
+ bridge_domain v_bd_epg1(101, bridge_domain::learning_mode_t::OFF);
+ WAIT_FOR_MATCH(v_bd_epg1);
+
+ interface *v_bvi_epg1 = new interface(
+ "bvi-101", interface::type_t::BVI, interface::admin_state_t::UP, v_rd);
+ v_bvi_epg1->set(vMac);
+ WAIT_FOR_MATCH(*v_bvi_epg1);
+
+ sub_interface v_upl_epg1(v_phy, interface::admin_state_t::UP, 0xA0B);
+ WAIT_FOR_MATCH(v_upl_epg1);
+ WAIT_FOR_MATCH(l2_binding(v_upl_epg1, v_bd_epg1));
+ gbp_bridge_domain *v_gbd1 = new gbp_bridge_domain(v_bd_epg1, *v_bvi_epg1);
+ WAIT_FOR_MATCH(*v_gbd1);
+ gbp_endpoint_group *v_epg1 =
+ new gbp_endpoint_group(0xA0B, 0xB0B, v_upl_epg1, v_rd, *v_gbd1);
+ v_epg1->set({120});
+ WAIT_FOR_MATCH(*v_epg1);
+
+ WAIT_FOR_MATCH(gbp_subnet(v_rd,
+ {address::from_string("10.20.44.0"), 24},
+ gbp_subnet::type_t::STITCHED_INTERNAL));
+ WAIT_FOR_MATCH(gbp_subnet(v_rd,
+ {address::from_string("10.20.45.0"), 24},
+ gbp_subnet::type_t::STITCHED_INTERNAL));
+ WAIT_FOR_MATCH(gbp_subnet(v_rd,
+ {address::from_string("2001:db8::"), 32},
+ gbp_subnet::type_t::STITCHED_INTERNAL));
+
+ /*
+ * add a new subnet to the opflex route-domain
+ * we expect the subnet to show up in each of the VPP RDs
+ */
+ opflex::modb::Mutator mutator(framework, policyOwner);
+ std::shared_ptr<modelgbp::gbp::Subnet> subnetsfd1_2;
+ subnetsfd1 = space->addGbpSubnets("subnetsfd1");
+ subnetsfd1_2 = subnetsfd0->addGbpSubnet("subnetsfd1_2");
+ subnetsfd1_2->setAddress("10.20.46.0").setPrefixLen(24);
+ fd1->addGbpForwardingBehavioralGroupToSubnetsRSrc()->setTargetSubnets(
+ subnetsfd1->getURI());
+ rd0->addGbpRoutingDomainToIntSubnetsRSrc(subnetsfd1->getURI().toString());
+ mutator.commit();
+ vppManager.domainUpdated(modelgbp::gbp::RoutingDomain::CLASS_ID,
+ rd0->getURI());
+
+ WAIT_FOR_MATCH(gbp_subnet(v_rd,
+ {address::from_string("10.20.44.0"), 24},
+ gbp_subnet::type_t::STITCHED_INTERNAL));
+ WAIT_FOR_MATCH(gbp_subnet(v_rd,
+ {address::from_string("10.20.45.0"), 24},
+ gbp_subnet::type_t::STITCHED_INTERNAL));
+ WAIT_FOR_MATCH(gbp_subnet(v_rd,
+ {address::from_string("10.20.46.0"), 24},
+ gbp_subnet::type_t::STITCHED_INTERNAL));
+ WAIT_FOR_MATCH(gbp_subnet(v_rd,
+ {address::from_string("2001:db8::"), 32},
+ gbp_subnet::type_t::STITCHED_INTERNAL));
+
+ /*
+ * withdraw the route domain.
+ */
+ opflex::modb::Mutator m1(framework, policyOwner);
+ rd0->remove();
+ m1.commit();
+ vppManager.domainUpdated(modelgbp::gbp::RoutingDomain::CLASS_ID,
+ rd0->getURI());
+
+ /*
+ * Withdraw the EPGs, all the state above should be gone
+ */
+ removeEpg(epg0);
+ vppManager.egDomainUpdated(epg0->getURI());
+ removeEpg(epg1);
+ vppManager.egDomainUpdated(epg1->getURI());
+
+ WAIT_FOR_NOT_PRESENT(gbp_subnet(v_rd,
+ {address::from_string("10.20.44.0"), 24},
+ gbp_subnet::type_t::STITCHED_INTERNAL));
+ WAIT_FOR_NOT_PRESENT(gbp_subnet(v_rd,
+ {address::from_string("10.20.45.0"), 24},
+ gbp_subnet::type_t::STITCHED_INTERNAL));
+ WAIT_FOR_NOT_PRESENT(gbp_subnet(v_rd,
+ {address::from_string("10.20.46.0"), 24},
+ gbp_subnet::type_t::STITCHED_INTERNAL));
+ WAIT_FOR_NOT_PRESENT(gbp_subnet(v_rd,
+ {address::from_string("2001:db8::"), 32},
+ gbp_subnet::type_t::STITCHED_INTERNAL));
+
+ WAIT_FOR_NOT_PRESENT(*v_epg0);
+ delete v_epg0;
+ WAIT_FOR_NOT_PRESENT(*v_epg1);
+ delete v_epg1;
+
+ WAIT_FOR_NOT_PRESENT(*v_gbd0);
+ delete v_gbd0;
+ WAIT_FOR_NOT_PRESENT(*v_gbd1);
+ delete v_gbd1;
+
+ WAIT_FOR_NOT_PRESENT(l2_binding(v_upl_epg0, v_bd_epg0));
+ WAIT_FOR_NOT_PRESENT(l2_binding(*v_bvi_epg0, v_bd_epg0));
+ WAIT_FOR_NOT_PRESENT(*v_bvi_epg0);
+ delete v_bvi_epg0;
+
+ WAIT_FOR_NOT_PRESENT(l2_binding(v_upl_epg1, v_bd_epg1));
+ WAIT_FOR_NOT_PRESENT(l2_binding(*v_bvi_epg1, v_bd_epg1));
+ WAIT_FOR_NOT_PRESENT(*v_bvi_epg1);
+ delete v_bvi_epg1;
+
+ /*
+ * If the RDs have gone we can be sure the routes have too.
+ */
+ WAIT_FOR_NOT_PRESENT(v_upl_epg0);
+ WAIT_FOR_NOT_PRESENT(v_bd_epg0);
+ WAIT_FOR_NOT_PRESENT(v_upl_epg1);
+ WAIT_FOR_NOT_PRESENT(v_bd_epg1);
+ WAIT_FOR_NOT_PRESENT(v_rd);
+}
+
+BOOST_FIXTURE_TEST_CASE(endpoint_add_del, VppStitchedManagerFixture)
+{
+ assignEpg0ToFd0();
+ vppManager.egDomainUpdated(epg0->getURI());
+ vppManager.endpointUpdated(ep0->getUUID());
+
+ mac_address_t v_mac_ep0("00:00:00:00:80:00");
+ mac_address_t v_mac_ep2("00:00:00:00:00:02");
+ mac_address_t v_mac_ep4("00:00:00:00:00:04");
+ /*
+ * Check for a bridge domain 100 and route domain 100.
+ */
+ bridge_domain v_bd_epg0(100, bridge_domain::learning_mode_t::OFF);
+ WAIT_FOR_MATCH(v_bd_epg0);
+ route_domain v_rd(100);
+ WAIT_FOR_MATCH(v_rd);
+ interface v_phy("opflex-itf",
+ interface::type_t::AFPACKET,
+ interface::admin_state_t::UP);
+ sub_interface v_upl_epg0(v_phy, interface::admin_state_t::UP, 0xA0A);
+ WAIT_FOR_MATCH(v_upl_epg0);
+ WAIT_FOR_MATCH(l2_binding(v_upl_epg0, v_bd_epg0));
+
+ interface *v_bvi_epg0 = new interface(
+ "bvi-100", interface::type_t::BVI, interface::admin_state_t::UP, v_rd);
+ v_bvi_epg0->set(vMac);
+ WAIT_FOR_MATCH(*v_bvi_epg0);
+
+ gbp_bridge_domain *v_gbd0 = new gbp_bridge_domain(v_bd_epg0, *v_bvi_epg0);
+ WAIT_FOR_MATCH(*v_gbd0);
+
+ gbp_endpoint_group *v_epg0 =
+ new gbp_endpoint_group(0xA0A, 0xBA, v_upl_epg0, v_rd, *v_gbd0);
+ v_epg0->set({120});
+ WAIT_FOR_MATCH(*v_epg0);
+
+ /*
+ * Find the EP's interface
+ */
+ interface *v_itf_ep0 = new interface("port80",
+ interface::type_t::AFPACKET,
+ interface::admin_state_t::UP,
+ v_rd);
+ WAIT_FOR_MATCH(*v_itf_ep0);
+
+ /*
+ * the Endpoint
+ */
+ WAIT_FOR_MATCH(gbp_endpoint(*v_itf_ep0, getEPIps(ep0), v_mac_ep0, *v_epg0));
+
+ /*
+ * An Another EP in another EPG
+ */
+ vppManager.egDomainUpdated(epg1->getURI());
+ vppManager.endpointUpdated(ep2->getUUID());
+
+ bridge_domain v_bd_epg1(101, bridge_domain::learning_mode_t::OFF);
+ WAIT_FOR_MATCH(v_bd_epg1);
+
+ interface *v_itf_ep2 = new interface("port11",
+ interface::type_t::AFPACKET,
+ interface::admin_state_t::UP,
+ v_rd);
+ WAIT_FOR_MATCH(*v_itf_ep2);
+ interface *v_bvi_epg1 = new interface(
+ "bvi-101", interface::type_t::BVI, interface::admin_state_t::UP, v_rd);
+ v_bvi_epg1->set(vMac);
+ WAIT_FOR_MATCH(*v_bvi_epg1);
+ sub_interface v_upl_epg1(v_phy, interface::admin_state_t::UP, 0xA0B);
+ WAIT_FOR_MATCH(v_upl_epg1);
+
+ gbp_bridge_domain *v_gbd1 = new gbp_bridge_domain(v_bd_epg1, *v_bvi_epg1);
+ WAIT_FOR_MATCH(*v_gbd1);
+ gbp_endpoint_group *v_epg1 =
+ new gbp_endpoint_group(0xA0B, 0xB0B, v_upl_epg1, v_rd, *v_gbd1);
+ v_epg1->set({120});
+ WAIT_FOR_MATCH(*v_epg1);
+
+ WAIT_FOR_MATCH(gbp_endpoint(*v_itf_ep2, getEPIps(ep2), v_mac_ep2, *v_epg1));
+
+ /*
+ * remove EP0
+ */
+ epSrc.removeEndpoint(ep0->getUUID());
+ vppManager.endpointUpdated(ep0->getUUID());
+
+ for (auto &ipAddr : getEPIps(ep0))
+ {
+ WAIT_FOR_NOT_PRESENT(
+ bridge_domain_arp_entry(v_bd_epg0, ipAddr, v_mac_ep0));
+ WAIT_FOR_NOT_PRESENT(neighbour(*v_bvi_epg0, ipAddr, v_mac_ep0));
+ WAIT_FOR_NOT_PRESENT(
+ route::ip_route(v_rd, {ipAddr}, {ipAddr, *v_bvi_epg0}));
+ }
+ WAIT_FOR_NOT_PRESENT(bridge_domain_entry(v_bd_epg0, v_mac_ep0, *v_itf_ep0));
+ WAIT_FOR_NOT_PRESENT(l2_binding(*v_itf_ep0, v_bd_epg0));
+ WAIT_FOR_NOT_PRESENT(*v_itf_ep0);
+ delete v_itf_ep0;
+
+ /*
+ * should still have state from EP2
+ */
+ WAIT_FOR_MATCH(gbp_endpoint(*v_itf_ep2, getEPIps(ep2), v_mac_ep2, *v_epg1));
+
+ /*
+ * remove the rest of the state
+ */
+ epSrc.removeEndpoint(ep2->getUUID());
+ vppManager.endpointUpdated(ep2->getUUID());
+ removeEpg(epg0);
+ vppManager.egDomainUpdated(epg0->getURI());
+
+ /*
+ * An Another EP in another EPG - trunk port
+ */
+ vppManager.egDomainUpdated(epg1->getURI());
+ vppManager.endpointUpdated(ep4->getUUID());
+
+ WAIT_FOR_MATCH(v_bd_epg1);
+
+ interface *v_itf_ep4 = new interface(
+ "port40", interface::type_t::AFPACKET, interface::admin_state_t::UP);
+ WAIT_FOR_MATCH(*v_itf_ep4);
+ interface *v_trunk_itf_ep4 =
+ new sub_interface(*v_itf_ep4, interface::admin_state_t::UP, v_rd, 1000);
+ WAIT_FOR_MATCH(*v_trunk_itf_ep4);
+ WAIT_FOR_MATCH(*v_bvi_epg1);
+ WAIT_FOR_MATCH(v_upl_epg1);
+ WAIT_FOR_MATCH(*v_epg1);
+
+ WAIT_FOR_MATCH(
+ gbp_endpoint(*v_trunk_itf_ep4, getEPIps(ep4), v_mac_ep4, *v_epg1));
+
+ epSrc.removeEndpoint(ep4->getUUID());
+ vppManager.endpointUpdated(ep4->getUUID());
+
+ delete v_itf_ep2;
+ delete v_trunk_itf_ep4;
+ delete v_itf_ep4;
+
+ removeEpg(epg1);
+ vppManager.egDomainUpdated(epg1->getURI());
+
+ /*
+ * withdraw the route domain.
+ */
+ opflex::modb::Mutator m1(framework, policyOwner);
+ rd0->remove();
+ m1.commit();
+
+ vppManager.domainUpdated(modelgbp::gbp::RoutingDomain::CLASS_ID,
+ rd0->getURI());
+
+ WAIT_FOR_NOT_PRESENT(*v_epg0);
+ delete v_epg0;
+ WAIT_FOR_NOT_PRESENT(*v_epg1);
+ delete v_epg1;
+
+ WAIT_FOR_NOT_PRESENT(*v_gbd0);
+ delete v_gbd0;
+ WAIT_FOR_NOT_PRESENT(*v_gbd1);
+ delete v_gbd1;
+
+ WAIT_FOR_NOT_PRESENT(l2_binding(v_upl_epg0, v_bd_epg0));
+ WAIT_FOR_NOT_PRESENT(l2_binding(*v_bvi_epg0, v_bd_epg0));
+ WAIT_FOR_NOT_PRESENT(*v_bvi_epg0);
+ delete v_bvi_epg0;
+
+ WAIT_FOR_NOT_PRESENT(l2_binding(v_upl_epg1, v_bd_epg1));
+ WAIT_FOR_NOT_PRESENT(l2_binding(*v_bvi_epg1, v_bd_epg1));
+ WAIT_FOR_NOT_PRESENT(*v_bvi_epg1);
+ delete v_bvi_epg1;
+
+ /*
+ * if the RD has gone then so have all the rest of the routes.
+ */
+ WAIT_FOR_NOT_PRESENT(v_bd_epg0);
+ WAIT_FOR_NOT_PRESENT(v_bd_epg1);
+ WAIT_FOR_NOT_PRESENT(v_rd);
+}
+
+BOOST_FIXTURE_TEST_CASE(trans_endpoint_group_add_del,
+ VppTransportManagerFixture)
+{
+ address_v4 spine_mac, spine_v4, spine_v6, bd_mc;
+ address host, router;
+
+ host = boost::asio::ip::address::from_string("192.168.1.1");
+ router = boost::asio::ip::address::from_string("192.168.1.2");
+
+ route::prefix_t pfx(host, 24);
+ mac_address_t mac("00:00:11:22:33:44");
+
+ framework.getMacProxy(spine_mac);
+ framework.getV4Proxy(spine_v4);
+ framework.getV6Proxy(spine_v6);
+
+ bd_mc = boost::asio::ip::address_v4::from_string("224.1.1.1");
+ do_dhcp();
+
+ /*
+ * boot phase so the VPP/host address is learnt
+ */
+ interface v_phy("opflex-itf",
+ interface::type_t::AFPACKET,
+ interface::admin_state_t::UP);
+ sub_interface v_sub(v_phy, interface::admin_state_t::UP, 4093);
+
+ WAIT_FOR_MATCH(v_phy);
+ WAIT_FOR_MATCH(v_sub);
+
+ /*
+ * create an endpoint group
+ */
+ vppManager.egDomainUpdated(epg0->getURI());
+
+ /*
+ * Check for a bridge domain 100
+ */
+ bridge_domain v_bd(100, bridge_domain::learning_mode_t::OFF);
+ WAIT_FOR_MATCH(v_bd);
+
+ /*
+ * check for the presence of a VOM route-domain matching the EPG's
+ * ID's are offset by 100.
+ */
+ route_domain v_rd(100);
+ WAIT_FOR_MATCH(v_rd);
+
+ interface *v_bvi = new interface(
+ "bvi-100", interface::type_t::BVI, interface::admin_state_t::UP, v_rd);
+ v_bvi->set(vMac);
+
+ WAIT_FOR_MATCH(*v_bvi);
+
+ /*
+ * the interfaces to the spine proxy.
+ * for the BD with VNI=0xAA and the RD VNI=0xBB
+ */
+ vxlan_tunnel *vt_mac =
+ new vxlan_tunnel(host, spine_mac, 0xAA, vxlan_tunnel::mode_t::GBP_L2);
+ WAIT_FOR_MATCH(*vt_mac);
+ vxlan_tunnel *vt_mc = new vxlan_tunnel(
+ host, bd_mc, 0xAA, v_sub, vxlan_tunnel::mode_t::GBP_L2);
+ WAIT_FOR_MATCH(*vt_mc);
+ vxlan_tunnel *vt_v4 =
+ new vxlan_tunnel(host, spine_v4, 0xBB, vxlan_tunnel::mode_t::GBP_L2);
+ WAIT_FOR_MATCH(*vt_v4);
+ vxlan_tunnel *vt_v6 =
+ new vxlan_tunnel(host, spine_v6, 0xBB, vxlan_tunnel::mode_t::GBP_L2);
+ WAIT_FOR_MATCH(*vt_v6);
+
+ gbp_bridge_domain *v_gbd =
+ new gbp_bridge_domain(v_bd, *v_bvi, *vt_mac, *vt_mc);
+ WAIT_FOR_MATCH(*v_gbd);
+ gbp_route_domain *v_grd = new gbp_route_domain(v_rd, *vt_v4, *vt_v6);
+ WAIT_FOR_MATCH(*v_grd);
+
+ gbp_endpoint_group *v_epg =
+ new gbp_endpoint_group(0xA0A, 0xBA, *v_grd, *v_gbd);
+ v_epg->set({120});
+ WAIT_FOR_MATCH(*v_epg);
+
+ WAIT_FOR_MATCH(gbp_vxlan(0xAA, *v_gbd, host.to_v4()));
+ WAIT_FOR_MATCH(gbp_vxlan(0xBB, *v_grd, host.to_v4()));
+
+ /*
+ * mcast vxlan tunnels bound to BD
+ */
+ boost::asio::ip::address bd_mcast =
+ boost::asio::ip::address::from_string("224.1.1.1");
+
+ vxlan_tunnel vt_bd_mcast(
+ host, bd_mcast, 0xAA, v_sub, vxlan_tunnel::mode_t::GBP_L2);
+ WAIT_FOR_MATCH(vt_bd_mcast);
+ WAIT_FOR_MATCH(l2_binding(vt_bd_mcast, v_bd));
+
+ igmp_binding igmp_b(v_sub);
+ WAIT_FOR_MATCH(igmp_b);
+ WAIT_FOR_MATCH(igmp_listen(igmp_b, bd_mcast.to_v4()));
+
+ removeEpg(epg0);
+ vppManager.egDomainUpdated(epg0->getURI());
+
+ WAIT_FOR_NOT_PRESENT(*v_epg);
+ delete v_epg;
+
+ WAIT_FOR_NOT_PRESENT(*v_gbd);
+ WAIT_FOR_NOT_PRESENT(*v_grd);
+ delete v_gbd;
+ delete v_grd;
+
+ WAIT_FOR_NOT_PRESENT(*v_bvi);
+ WAIT_FOR_NOT_PRESENT(*vt_mac);
+ WAIT_FOR_NOT_PRESENT(*vt_v4);
+ WAIT_FOR_NOT_PRESENT(*vt_v6);
+ delete vt_mac;
+ delete vt_v4;
+ delete vt_v6;
+ delete v_bvi;
+}
+
+BOOST_FIXTURE_TEST_CASE(ext_itf, VppTransportManagerFixture)
+{
+ address_v4 spine_v4, spine_v6;
+
+ framework.getV4Proxy(spine_v4);
+ framework.getV6Proxy(spine_v6);
+
+ do_dhcp();
+ vppManager.externalInterfaceUpdated(ext_itf0->getURI());
+
+ interface v_phy("opflex-itf",
+ interface::type_t::AFPACKET,
+ interface::admin_state_t::UP);
+ sub_interface v_sub(v_phy, interface::admin_state_t::UP, 4093);
+
+ WAIT_FOR_MATCH(v_phy);
+ WAIT_FOR_MATCH(v_sub);
+
+ route_domain v_rd(100);
+ WAIT_FOR_MATCH(v_rd);
+
+ bridge_domain v_bd(100, bridge_domain::learning_mode_t::OFF);
+ WAIT_FOR_MATCH(v_bd);
+
+ std::shared_ptr<interface> v_bvi = std::make_shared<interface>(
+ "bvi-100", interface::type_t::BVI, interface::admin_state_t::UP, v_rd);
+ l2_address_t l2addr(mac_address_t("00:00:00:00:80:00"));
+ v_bvi->set(l2addr);
+
+ WAIT_FOR_MATCH(*v_bvi);
+
+ boost::asio::ip::address bd_mcast =
+ boost::asio::ip::address::from_string("224.1.2.2");
+
+ std::shared_ptr<vxlan_tunnel> vt_bd_mcast = std::make_shared<vxlan_tunnel>(
+ host, bd_mcast, 1133, v_sub, vxlan_tunnel::mode_t::GBP_L2);
+ WAIT_FOR_MATCH(*vt_bd_mcast);
+ igmp_binding igmp_b(v_sub);
+ WAIT_FOR_MATCH(igmp_b);
+ WAIT_FOR_MATCH(igmp_listen(igmp_b, bd_mcast.to_v4()));
+
+ gbp_bridge_domain *v_gbd =
+ new gbp_bridge_domain(v_bd, v_bvi, {}, vt_bd_mcast);
+ WAIT_FOR_MATCH(*v_gbd);
+
+ vxlan_tunnel *vt_v4 = new vxlan_tunnel(
+ host, spine_v4, 0x010203, vxlan_tunnel::mode_t::GBP_L2);
+ WAIT_FOR_MATCH(*vt_v4);
+ vxlan_tunnel *vt_v6 = new vxlan_tunnel(
+ host, spine_v6, 0x010203, vxlan_tunnel::mode_t::GBP_L2);
+ WAIT_FOR_MATCH(*vt_v6);
+
+ gbp_route_domain *v_grd = new gbp_route_domain(v_rd, *vt_v4, *vt_v6);
+ WAIT_FOR_MATCH(*v_grd);
+
+ /* 0x80000064 is the internally generated EPG-ID for each Ext-net */
+ gbp_ext_itf *v_ei = new gbp_ext_itf(*v_bvi, *v_gbd, *v_grd);
+ WAIT_FOR_MATCH(*v_ei);
+
+ WAIT_FOR_MATCH(gbp_subnet(v_rd, {"105.0.0.0", 24}, 1234));
+ WAIT_FOR_MATCH(gbp_subnet(v_rd, {"106.0.0.0", 24}, 1234));
+ WAIT_FOR_MATCH(gbp_subnet(v_rd, {"107.0.0.0", 24}, 1235));
+ WAIT_FOR_MATCH(gbp_subnet(v_rd, {"108.0.0.0", 24}, 1235));
+
+ /* Add an EP */
+ vppManager.externalEndpointUpdated(ext_ep0->getUUID());
+
+ mac_address_t v_mac_ext_ep0("00:00:00:00:0E:00");
+
+ interface *v_itf_ext_ep0 = new interface("port-e-00",
+ interface::type_t::AFPACKET,
+ interface::admin_state_t::UP,
+ v_rd);
+ WAIT_FOR_MATCH(*v_itf_ext_ep0);
+ // VNID of the EXT-itf and sclass of the BD
+ gbp_endpoint_group *v_epg0 =
+ new gbp_endpoint_group(0xdeadbeaf, 2233, *v_grd, *v_gbd);
+ v_epg0->set({120});
+ WAIT_FOR_MATCH(*v_epg0);
+
+ gbp_endpoint *v_ep = new gbp_endpoint(*v_itf_ext_ep0,
+ getEPIps(ext_ep0),
+ v_mac_ext_ep0,
+ *v_epg0,
+ gbp_endpoint::flags_t::EXTERNAL);
+ WAIT_FOR_MATCH(*v_ep);
+
+ /* cleanup */
+ epSrc.removeEndpoint(ext_ep0->getUUID());
+ vppManager.externalEndpointUpdated(ext_ep0->getUUID());
+
+ {
+ opflex::modb::Mutator m2(framework, policyOwner);
+ ext_itf0->remove();
+ m2.commit();
+ }
+ vppManager.externalInterfaceUpdated(ext_itf0->getURI());
+
+ WAIT_FOR_NOT_PRESENT(*v_ep);
+ delete v_ep;
+ WAIT_FOR_NOT_PRESENT(*v_epg0);
+ delete v_epg0;
+ WAIT_FOR_NOT_PRESENT(gbp_subnet(v_rd, {"108.0.0.0", 24}, 1235));
+ WAIT_FOR_NOT_PRESENT(*v_ei);
+ delete v_ei;
+
+ WAIT_FOR_NOT_PRESENT(*v_grd);
+ WAIT_FOR_NOT_PRESENT(*v_gbd);
+
+ delete v_grd;
+ delete vt_v4;
+ delete vt_v6;
+}
+
+/* BOOST_FIXTURE_TEST_CASE(static_route, VppTransportManagerFixture) */
+/* { */
+/* vppManager.localRouteUpdated(static_route1->getURI()); */
+
+/* route_domain v_rd(100); */
+/* WAIT_FOR_MATCH(v_rd); */
+
+/* route::prefix_t pfx(boost::asio::ip::address::from_string("101.101.0.0"),
+ */
+/* 16); */
+
+/* boost::asio::ip::address nh1, nh2, nh3; */
+/* nh1 = boost::asio::ip::address::from_string("100.100.100.2"); */
+/* nh2 = boost::asio::ip::address::from_string("100.100.100.3"); */
+/* nh3 = boost::asio::ip::address::from_string("100.100.100.4"); */
+
+/* route::ip_route v_route(v_rd, pfx); */
+/* v_route.add({v_rd, nh1}); */
+/* v_route.add({v_rd, nh2}); */
+/* v_route.add({v_rd, nh3}); */
+
+/* WAIT_FOR_MATCH(v_route); */
+
+/* opflex::modb::Mutator m1(framework, policyOwner); */
+/* static_route1->remove(); */
+/* m1.commit(); */
+/* vppManager.localRouteUpdated(static_route1->getURI()); */
+
+/* WAIT_FOR_NOT_PRESENT(v_route); */
+/* } */
+
+BOOST_FIXTURE_TEST_CASE(secGroup, VppStitchedManagerFixture)
+{
+ using modelgbp::gbpe::L24Classifier;
+ using namespace modelgbp::gbp;
+ createObjects();
+ createPolicyObjects();
+
+ PolicyManager::rule_list_t lrules;
+ vppManager.egDomainUpdated(epg0->getURI());
+ vppManager.endpointUpdated(ep0->getUUID());
+
+ std::shared_ptr<SecGroup> secGrp1, secGrp2;
+ {
+ opflex::modb::Mutator mutator(framework, policyOwner);
+ secGrp1 = space->addGbpSecGroup("secgrp1");
+ secGrp1->addGbpSecGroupSubject("1_subject1")
+ ->addGbpSecGroupRule("1_1_rule1")
+ ->setDirection(DirectionEnumT::CONST_IN)
+ .setOrder(100)
+ .addGbpRuleToClassifierRSrc(classifier1->getURI().toString());
+ secGrp1->addGbpSecGroupSubject("1_subject1")
+ ->addGbpSecGroupRule("1_1_rule2")
+ ->setDirection(DirectionEnumT::CONST_IN)
+ .setOrder(150)
+ .addGbpRuleToClassifierRSrc(classifier8->getURI().toString());
+ secGrp1->addGbpSecGroupSubject("1_subject1")
+ ->addGbpSecGroupRule("1_1_rule3")
+ ->setDirection(DirectionEnumT::CONST_IN)
+ .setOrder(200)
+ .addGbpRuleToClassifierRSrc(classifier6->getURI().toString());
+ secGrp1->addGbpSecGroupSubject("1_subject1")
+ ->addGbpSecGroupRule("1_1_rule4")
+ ->setDirection(DirectionEnumT::CONST_IN)
+ .setOrder(300)
+ .addGbpRuleToClassifierRSrc(classifier7->getURI().toString());
+ mutator.commit();
+ }
+
+ ep0->addSecurityGroup(secGrp1->getURI());
+ epSrc.updateEndpoint(*ep0);
+
+ WAIT_FOR_DO(lrules.size() == 4, 500, lrules.clear();
+ policyMgr.getSecGroupRules(secGrp1->getURI(), lrules));
+
+ vppManager.endpointUpdated(ep0->getUUID());
+
+ route_domain v_rd(100);
+ WAIT_FOR1(is_match(v_rd));
+
+ /*
+ * Find the EP's interface
+ */
+ interface *v_itf = new interface("port80",
+ interface::type_t::AFPACKET,
+ interface::admin_state_t::UP,
+ v_rd);
+ WAIT_FOR1(is_match(*v_itf));
+
+ ACL::ethertype_rule_t e1(ethertype_t::IPV4, direction_t::OUTPUT);
+ ACL::ethertype_rule_t e2(ethertype_t::IPV6, direction_t::OUTPUT);
+ ACL::ethertype_rule_t e3(ethertype_t::IPV4, direction_t::OUTPUT);
+ ACL::ethertype_rule_t e4(ethertype_t::IPV4, direction_t::OUTPUT);
+
+ ACL::acl_ethertype::ethertype_rules_t e_rules = {e1, e2, e3, e4};
+
+ WAIT_FOR1(is_match(ACL::acl_ethertype(*v_itf, e_rules)));
+
+ ACL::action_t act = ACL::action_t::PERMIT;
+ ACL::l3_rule rule1(8192,
+ act,
+ route::prefix_t::ZERO,
+ route::prefix_t::ZERO,
+ 6,
+ 0,
+ 65535,
+ 80,
+ 65535,
+ 0,
+ 0);
+ ACL::l3_rule rule2(8064,
+ act,
+ route::prefix_t::ZEROv6,
+ route::prefix_t::ZEROv6,
+ 6,
+ 0,
+ 65535,
+ 80,
+ 65535,
+ 0,
+ 0);
+ ACL::l3_rule rule3(7808,
+ act,
+ route::prefix_t::ZERO,
+ route::prefix_t::ZERO,
+ 6,
+ 22,
+ 65535,
+ 0,
+ 65535,
+ 3,
+ 3);
+ ACL::l3_rule rule4(7680,
+ act,
+ route::prefix_t::ZERO,
+ route::prefix_t::ZERO,
+ 6,
+ 21,
+ 65535,
+ 0,
+ 65535,
+ 16,
+ 16);
+ ACL::l3_list::rules_t rules({rule1, rule2, rule3, rule4});
+
+ boost::hash<std::string> string_hash;
+ const std::string secGrpKey =
+ std::to_string(string_hash("/PolicyUniverse/PolicySpace/"
+ "tenant0/GbpSecGroup/secgrp1/"));
+
+ WAIT_FOR1(is_match(ACL::l3_list(secGrpKey + "-out", rules)));
+
+ {
+ opflex::modb::Mutator mutator(framework, policyOwner);
+ secGrp2 = space->addGbpSecGroup("secgrp2");
+ secGrp2->addGbpSecGroupSubject("2_subject1")
+ ->addGbpSecGroupRule("2_1_rule1")
+ ->addGbpRuleToClassifierRSrc(classifier0->getURI().toString());
+ secGrp2->addGbpSecGroupSubject("2_subject1")
+ ->addGbpSecGroupRule("2_1_rule2")
+ ->setDirection(DirectionEnumT::CONST_BIDIRECTIONAL)
+ .setOrder(20)
+ .addGbpRuleToClassifierRSrc(classifier5->getURI().toString());
+ secGrp2->addGbpSecGroupSubject("2_subject1")
+ ->addGbpSecGroupRule("2_1_rule3")
+ ->setDirection(DirectionEnumT::CONST_OUT)
+ .setOrder(30)
+ .addGbpRuleToClassifierRSrc(classifier9->getURI().toString());
+ mutator.commit();
+ }
+
+ ep0->addSecurityGroup(secGrp2->getURI());
+ epSrc.updateEndpoint(*ep0);
+
+ lrules.clear();
+ WAIT_FOR_DO(lrules.size() == 2, 500, lrules.clear();
+ policyMgr.getSecGroupRules(secGrp2->getURI(), lrules));
+
+ vppManager.endpointUpdated(ep0->getUUID());
+
+ ACL::ethertype_rule_t e6(ethertype_t::FCOE, direction_t::OUTPUT);
+ ACL::ethertype_rule_t e7(ethertype_t::FCOE, direction_t::INPUT);
+ ACL::ethertype_rule_t e8(ethertype_t::IPV4, direction_t::INPUT);
+
+ ACL::acl_ethertype::ethertype_rules_t e_rules2 = {
+ e1, e2, e3, e4, e6, e7, e8};
+
+ WAIT_FOR_MATCH(ACL::acl_ethertype(*v_itf, e_rules2));
+
+ act = ACL::action_t::PERMITANDREFLEX;
+ ACL::l3_rule rule5(8064,
+ act,
+ route::prefix_t::ZERO,
+ route::prefix_t::ZERO,
+ 6,
+ 0,
+ 65535,
+ 22,
+ 65535,
+ 0,
+ 0);
+ ACL::l3_list::rules_t rules2({rule5});
+
+ const std::string secGrpKey2 = std::to_string(
+ string_hash("/PolicyUniverse/PolicySpace/"
+ "tenant0/GbpSecGroup/secgrp1/,/PolicyUniverse/"
+ "PolicySpace/tenant0/GbpSecGroup/secgrp2/"));
+
+ WAIT_FOR1(is_match(ACL::l3_list(secGrpKey2 + "-in", rules2)));
+
+ delete v_itf;
+}
+
+BOOST_FIXTURE_TEST_CASE(policy, VppStitchedManagerFixture)
+{
+ createObjects();
+ createPolicyObjects();
+ PolicyManager::uri_set_t egs;
+ WAIT_FOR_DO(egs.size() == 2, 1000, egs.clear();
+ policyMgr.getContractProviders(con1->getURI(), egs));
+ egs.clear();
+ WAIT_FOR_DO(egs.size() == 2, 500, egs.clear();
+ policyMgr.getContractConsumers(con1->getURI(), egs));
+ egs.clear();
+
+ WAIT_FOR_DO(egs.size() == 2, 500, egs.clear();
+ policyMgr.getContractIntra(con2->getURI(), egs));
+
+ /* add con2 */
+ vppManager.contractUpdated(con2->getURI());
+
+ ACL::action_t act = ACL::action_t::PERMIT;
+ ACL::l3_rule rule1(8192,
+ act,
+ route::prefix_t::ZERO,
+ route::prefix_t::ZERO,
+ 6,
+ 0,
+ 65535,
+ 80,
+ 65535,
+ 0,
+ 0);
+ ACL::l3_list::rules_t rules1({rule1});
+
+ gbp_contract::gbp_rules_t grules1 = {{8192, gbp_rule::action_t::PERMIT}};
+ gbp_contract::ethertype_set_t allowed1 = {ethertype_t::IPV4,
+ ethertype_t::FCOE};
+
+ /* add con1 */
+ vppManager.contractUpdated(con1->getURI());
+
+ ACL::l3_rule rule2(8192,
+ act,
+ route::prefix_t::ZERO,
+ route::prefix_t::ZERO,
+ 6,
+ 0,
+ 65535,
+ 80,
+ 65535,
+ 0,
+ 0);
+ ACL::l3_rule rule3(7936,
+ act,
+ route::prefix_t::ZERO,
+ route::prefix_t::ZERO,
+ 6,
+ 22,
+ 65535,
+ 0,
+ 65535,
+ 3,
+ 3);
+ ACL::l3_rule rule4(7808,
+ act,
+ route::prefix_t::ZERO,
+ route::prefix_t::ZERO,
+ 6,
+ 21,
+ 65535,
+ 0,
+ 65535,
+ 16,
+ 16);
+ ACL::l3_list::rules_t rules2({rule2, rule3, rule4});
+
+ ACL::l3_list outAcl2(con1->getURI().toString() + "out", rules2);
+ WAIT_FOR_MATCH(outAcl2);
+
+ gbp_contract::gbp_rules_t grules2 = {{8192, gbp_rule::action_t::PERMIT},
+ {7936, gbp_rule::action_t::PERMIT},
+ {7808, gbp_rule::action_t::PERMIT}};
+ gbp_contract::ethertype_set_t allowed2 = {ethertype_t::IPV4,
+ ethertype_t::ARP};
+
+ WAIT_FOR1(is_match(gbp_contract(202, 186, outAcl2, grules2, allowed2)));
+ WAIT_FOR1(is_match(gbp_contract(202, 187, outAcl2, grules2, allowed2)));
+ WAIT_FOR1(is_match(gbp_contract(203, 186, outAcl2, grules2, allowed2)));
+ WAIT_FOR1(is_match(gbp_contract(203, 187, outAcl2, grules2, allowed2)));
+}
+
+BOOST_FIXTURE_TEST_CASE(policyPortRange, VppStitchedManagerFixture)
+{
+ createObjects();
+ createPolicyObjects();
+
+ PolicyManager::uri_set_t egs;
+ WAIT_FOR_DO(egs.size() == 1, 1000, egs.clear();
+ policyMgr.getContractProviders(con3->getURI(), egs));
+ egs.clear();
+ WAIT_FOR_DO(egs.size() == 1, 500, egs.clear();
+ policyMgr.getContractConsumers(con3->getURI(), egs));
+ PolicyManager::rule_list_t rules;
+ WAIT_FOR_DO(rules.size() == 3, 500, rules.clear();
+ policyMgr.getContractRules(con3->getURI(), rules));
+
+ vppManager.contractUpdated(con3->getURI());
+ ACL::ethertype_rule_t e1(ethertype_t::IPV4, direction_t::OUTPUT);
+ ACL::ethertype_rule_t e2(ethertype_t::IPV4, direction_t::OUTPUT);
+ ACL::ethertype_rule_t e3(ethertype_t::IPV4, direction_t::OUTPUT);
+
+ ACL::acl_ethertype::ethertype_rules_t e_rules = {e1, e2, e3};
+
+ ACL::action_t act = ACL::action_t::PERMIT;
+ ACL::action_t act1 = ACL::action_t::DENY;
+ ACL::l3_rule rule1(8192,
+ act1,
+ route::prefix_t::ZERO,
+ route::prefix_t::ZERO,
+ 6,
+ 0,
+ 65535,
+ 80,
+ 85,
+ 0,
+ 0);
+ ACL::l3_rule rule2(8064,
+ act,
+ route::prefix_t::ZERO,
+ route::prefix_t::ZERO,
+ 6,
+ 66,
+ 69,
+ 94,
+ 95,
+ 0,
+ 0);
+ ACL::l3_rule rule3(7936,
+ act,
+ route::prefix_t::ZERO,
+ route::prefix_t::ZERO,
+ 1,
+ 10,
+ 10,
+ 5,
+ 5,
+ 0,
+ 0);
+ ACL::l3_list::rules_t rules1({rule1, rule2, rule3});
+
+ ACL::l3_list outAcl(con3->getURI().toString() + "out", rules1);
+ WAIT_FOR_MATCH(outAcl);
+
+ gbp_contract::gbp_rules_t grules = {{8192, gbp_rule::action_t::PERMIT},
+ {7936, gbp_rule::action_t::PERMIT},
+ {7808, gbp_rule::action_t::PERMIT}};
+
+ gbp_contract::ethertype_set_t allowed = {ethertype_t::IPV4,
+ ethertype_t::ARP};
+
+ WAIT_FOR1(is_match(gbp_contract(187, 186, outAcl, grules, allowed)));
+}
+
+BOOST_FIXTURE_TEST_CASE(policyRedirect, VppTransportManagerFixture)
+{
+ using modelgbp::gbpe::L24Classifier;
+ using namespace modelgbp;
+ using namespace modelgbp::gbp;
+ using namespace std;
+
+ createObjects();
+ createPolicyObjects();
+
+ shared_ptr<Contract> con5;
+ shared_ptr<L24Classifier> classifier11;
+ shared_ptr<RedirectAction> action3;
+ shared_ptr<RedirectDestGroup> redirDstGrp1;
+ shared_ptr<RedirectDestGroup> redirDstGrp2;
+ shared_ptr<RedirectDest> redirDst1;
+ shared_ptr<RedirectDest> redirDst2;
+ shared_ptr<RedirectDest> redirDst3;
+ shared_ptr<RedirectDest> redirDst4;
+ shared_ptr<RedirectDest> redirDst5;
+
+ opflex::modb::Mutator mutator(framework, policyOwner);
+ classifier11 = space->addGbpeL24Classifier("classifier11");
+ classifier11->setEtherT(l2::EtherTypeEnumT::CONST_IPV4)
+ .setProt(6 /* TCP */)
+ .setDFromPort(80);
+
+ redirDstGrp1 = space->addGbpRedirectDestGroup("redirDstGrp1");
+ redirDstGrp1->setHashAlgo(HashingAlgorithmEnumT::CONST_SYMMETRIC);
+ redirDstGrp1->setResilientHashEnabled(1);
+ redirDst1 = redirDstGrp1->addGbpRedirectDest("redirDst1");
+ redirDst2 = redirDstGrp1->addGbpRedirectDest("redirDst2");
+ opflex::modb::MAC mac1("00:01:02:03:04:05"), mac2("01:02:03:04:05:06");
+ redirDst1->setIp("1.1.1.1");
+ redirDst1->setMac(mac1);
+ redirDst1->addGbpRedirectDestToDomainRSrcBridgeDomain(
+ bd0->getURI().toString());
+ redirDst1->addGbpRedirectDestToDomainRSrcRoutingDomain(
+ rd0->getURI().toString());
+ redirDst2->setIp("2.2.2.2");
+ redirDst2->setMac(mac2);
+ redirDst2->addGbpRedirectDestToDomainRSrcBridgeDomain(
+ bd0->getURI().toString());
+ redirDst2->addGbpRedirectDestToDomainRSrcRoutingDomain(
+ rd0->getURI().toString());
+ action3 = space->addGbpRedirectAction("action3");
+ action3->addGbpRedirectActionToDestGrpRSrc()->setTargetRedirectDestGroup(
+ redirDstGrp1->getURI());
+ redirDstGrp2 = space->addGbpRedirectDestGroup("redirDstGrp2");
+ redirDst4 = redirDstGrp2->addGbpRedirectDest("redirDst4");
+ opflex::modb::MAC mac3("02:03:04:05:06:07"), mac4("03:04:05:06:07:08");
+ redirDst4->setIp("4.4.4.4");
+ redirDst4->setMac(mac4);
+ redirDst4->addGbpRedirectDestToDomainRSrcBridgeDomain(
+ bd0->getURI().toString());
+ redirDst4->addGbpRedirectDestToDomainRSrcRoutingDomain(
+ rd0->getURI().toString());
+
+ con5 = space->addGbpContract("contract5");
+ con5->addGbpSubject("5_subject1")
+ ->addGbpRule("5_1_rule1")
+ ->setDirection(DirectionEnumT::CONST_IN)
+ .setOrder(100)
+ .addGbpRuleToClassifierRSrc(classifier11->getURI().toString());
+ con5->addGbpSubject("5_subject1")
+ ->addGbpRule("5_1_rule1")
+ ->addGbpRuleToActionRSrcRedirectAction(action3->getURI().toString());
+
+ epg0->addGbpEpGroupToProvContractRSrc(con5->getURI().toString());
+ epg1->addGbpEpGroupToConsContractRSrc(con5->getURI().toString());
+ mutator.commit();
+
+ PolicyManager::uri_set_t egs;
+ WAIT_FOR_DO(egs.size() == 1, 1000, egs.clear();
+ policyMgr.getContractProviders(con5->getURI(), egs));
+ egs.clear();
+ WAIT_FOR_DO(egs.size() == 1, 500, egs.clear();
+ policyMgr.getContractConsumers(con5->getURI(), egs));
+
+ vppManager.contractUpdated(con5->getURI());
+ WAIT_FOR(policyMgr.contractExists(con5->getURI()), 500);
+
+ mac_address_t vmac1("00:01:02:03:04:05");
+ mac_address_t vmac2("01:02:03:04:05:06");
+ gbp_rule::next_hop_t nh1(address::from_string("1.1.1.1"), vmac1, 100, 100);
+ gbp_rule::next_hop_t nh2(address::from_string("2.2.2.2"), vmac2, 100, 100);
+ gbp_rule::next_hops_t nhs({nh1, nh2});
+ gbp_rule::next_hop_set_t next_hop_set(gbp_rule::hash_mode_t::SYMMETRIC,
+ nhs);
+ gbp_rule gr(8192, next_hop_set, gbp_rule::action_t::REDIRECT);
+ gbp_contract::gbp_rules_t gbp_rules = {gr};
+
+ gbp_contract::ethertype_set_t e_rules = {ethertype_t::IPV4};
+
+ ACL::action_t act = ACL::action_t::PERMIT;
+ ACL::l3_rule rule1(8192,
+ act,
+ route::prefix_t::ZERO,
+ route::prefix_t::ZERO,
+ 6,
+ 0,
+ 65535,
+ 80,
+ 65535,
+ 0,
+ 0);
+ ACL::l3_list::rules_t rules1({rule1});
+
+ ACL::l3_list outAcl(con5->getURI().toString() + "out", rules1);
+ WAIT_FOR_MATCH(outAcl);
+
+ gbp_contract gbpc(187, 186, outAcl, gbp_rules, e_rules);
+
+ WAIT_FOR1(is_match(gbpc));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "llvm.org")
+ * End:
+ */
diff --git a/src/test/VppRenderer_test.cpp b/src/test/VppRenderer_test.cpp
new file mode 100644
index 0000000..92c655e
--- /dev/null
+++ b/src/test/VppRenderer_test.cpp
@@ -0,0 +1,78 @@
+/*
+ * Test suite for VppRenderer
+ *
+ * Copyright (c) 2018 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#include <boost/test/unit_test.hpp>
+
+#include <opflexagent/test/ModbFixture.h>
+
+#include <vom/stat_reader.hpp>
+
+#include "VppRenderer.hpp"
+
+BOOST_AUTO_TEST_SUITE(VppRenderer_test)
+
+class MockCmdQ : public VOM::HW::cmd_q
+{
+ public:
+ MockCmdQ() = default;
+ ~MockCmdQ() = default;
+};
+class MockStatReader : public VOM::stat_reader
+{
+ public:
+ MockStatReader() = default;
+ ~MockStatReader() = default;
+};
+
+class MockVppManager : public VPP::VppManager
+{
+ public:
+ MockVppManager(Agent &agent,
+ IdGenerator &idGen,
+ VOM::HW::cmd_q *q,
+ VOM::stat_reader *sr)
+ : VppManager(agent, idGen, q, sr)
+ {
+ }
+ ~MockVppManager()
+ {
+ }
+
+ void
+ start()
+ {
+ std::cout << " starting Mock vpp manager" << std::endl;
+ }
+ void
+ registerModbListeners()
+ {
+ std::cout << " registering Mock ModbListeners" << std::endl;
+ }
+ void
+ stop()
+ {
+ std::cout << " stopping Mock vpp manager" << std::endl;
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(vpp, opflexagent::ModbFixture)
+{
+
+ IdGenerator *idGen = new IdGenerator();
+ VOM::HW::cmd_q *vppQ = new MockCmdQ();
+ VOM::stat_reader *vppSR = new MockStatReader();
+ VPP::VppManager *vppManager =
+ new MockVppManager(agent, *idGen, vppQ, vppSR);
+ VPP::VppRenderer vpp(agent, *idGen, vppManager);
+ vpp.start();
+ vpp.stop();
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/vpp_test.cpp b/src/test/vpp_test.cpp
new file mode 100644
index 0000000..08998bf
--- /dev/null
+++ b/src/test/vpp_test.cpp
@@ -0,0 +1,13 @@
+/* -*- C++ -*-; c-basic-offset: 4; indent-tabs-mode: nil */
+/*
+ * Test suite for for vpp plugin
+ *
+ * Copyright (c) 2018 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+#define BOOST_TEST_MODULE "OpFlex Agent VPP Plugin"
+#include <boost/test/unit_test.hpp>