From 812ed39f9da336310e815c361ab5a9f118657d94 Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Mon, 16 Oct 2017 04:20:13 -0700 Subject: VPP Object Model (VOM) The VOM is a C++ library for use by clients/agents of VPP for programming state. It uses the binary APIs to do so. Various other common client side functions are also provided. Please see om.hpp for a more detailed description. Change-Id: Ib756bfe99817093815a9e26ccf464aa5583fc523 Signed-off-by: Neale Ranns Co-authored-by: Mohsin Kazmi --- .gitignore | 1 + Makefile | 2 + src/Makefile.am | 2 + src/configure.ac | 3 +- src/vpp-api/vapi/vapi.hpp | 4 +- src/vpp-api/vom/.clang-format | 3 + src/vpp-api/vom/Makefile.am | 151 +++ src/vpp-api/vom/acl_binding.cpp | 95 ++ src/vpp-api/vom/acl_binding.hpp | 422 +++++++ src/vpp-api/vom/acl_binding_cmds.cpp | 138 +++ src/vpp-api/vom/acl_l2_rule.cpp | 78 ++ src/vpp-api/vom/acl_l2_rule.hpp | 113 ++ src/vpp-api/vom/acl_l3_rule.cpp | 156 +++ src/vpp-api/vom/acl_l3_rule.hpp | 181 +++ src/vpp-api/vom/acl_list.cpp | 113 ++ src/vpp-api/vom/acl_list.hpp | 475 +++++++ src/vpp-api/vom/acl_list_cmds.cpp | 153 +++ src/vpp-api/vom/acl_types.cpp | 59 + src/vpp-api/vom/acl_types.hpp | 75 ++ src/vpp-api/vom/arp_proxy_binding.cpp | 142 +++ src/vpp-api/vom/arp_proxy_binding.hpp | 234 ++++ src/vpp-api/vom/arp_proxy_binding_cmds.cpp | 104 ++ src/vpp-api/vom/arp_proxy_config.cpp | 147 +++ src/vpp-api/vom/arp_proxy_config.hpp | 241 ++++ src/vpp-api/vom/arp_proxy_config_cmds.cpp | 120 ++ src/vpp-api/vom/bridge_domain.cpp | 210 ++++ src/vpp-api/vom/bridge_domain.hpp | 254 ++++ src/vpp-api/vom/bridge_domain_arp_entry.cpp | 172 +++ src/vpp-api/vom/bridge_domain_arp_entry.hpp | 260 ++++ src/vpp-api/vom/bridge_domain_arp_entry_cmds.cpp | 124 ++ src/vpp-api/vom/bridge_domain_cmds.cpp | 139 +++ src/vpp-api/vom/bridge_domain_entry.cpp | 189 +++ src/vpp-api/vom/bridge_domain_entry.hpp | 284 +++++ src/vpp-api/vom/bridge_domain_entry_cmds.cpp | 147 +++ src/vpp-api/vom/client_db.cpp | 56 + src/vpp-api/vom/client_db.hpp | 89 ++ src/vpp-api/vom/cmd.cpp | 37 + src/vpp-api/vom/cmd.hpp | 82 ++ src/vpp-api/vom/connection.cpp | 60 + src/vpp-api/vom/connection.hpp | 76 ++ src/vpp-api/vom/dhcp_config.cpp | 167 +++ src/vpp-api/vom/dhcp_config.hpp | 343 +++++ src/vpp-api/vom/dhcp_config_cmds.cpp | 172 +++ src/vpp-api/vom/dump_cmd.hpp | 134 ++ src/vpp-api/vom/enum_base.hpp | 105 ++ src/vpp-api/vom/event_cmd.hpp | 117 ++ src/vpp-api/vom/hw.cpp | 337 +++++ src/vpp-api/vom/hw.hpp | 408 ++++++ src/vpp-api/vom/inspect.cpp | 108 ++ src/vpp-api/vom/inspect.hpp | 94 ++ src/vpp-api/vom/interface.cpp | 434 +++++++ src/vpp-api/vom/interface.hpp | 1002 +++++++++++++++ src/vpp-api/vom/interface_cmds.cpp | 544 ++++++++ src/vpp-api/vom/interface_factory.cpp | 90 ++ src/vpp-api/vom/interface_ip6_nd.hpp | 385 ++++++ src/vpp-api/vom/interface_ip6_nd_cmds.cpp | 102 ++ src/vpp-api/vom/interface_span.cpp | 217 ++++ src/vpp-api/vom/interface_span.hpp | 345 ++++++ src/vpp-api/vom/interface_span_cmds.cpp | 151 +++ src/vpp-api/vom/interface_types.cpp | 99 ++ src/vpp-api/vom/ip_unnumbered.cpp | 139 +++ src/vpp-api/vom/ip_unnumbered.hpp | 254 ++++ src/vpp-api/vom/ip_unnumbered_cmds.cpp | 113 ++ src/vpp-api/vom/l2_binding.cpp | 206 +++ src/vpp-api/vom/l2_binding.hpp | 337 +++++ src/vpp-api/vom/l2_binding_cmds.cpp | 167 +++ src/vpp-api/vom/l3_binding.cpp | 187 +++ src/vpp-api/vom/l3_binding.hpp | 306 +++++ src/vpp-api/vom/l3_binding_cmds.cpp | 155 +++ src/vpp-api/vom/lldp_binding.cpp | 142 +++ src/vpp-api/vom/lldp_binding.hpp | 239 ++++ src/vpp-api/vom/lldp_binding_cmds.cpp | 110 ++ src/vpp-api/vom/lldp_global.cpp | 142 +++ src/vpp-api/vom/lldp_global.hpp | 210 ++++ src/vpp-api/vom/lldp_global_cmds.cpp | 73 ++ src/vpp-api/vom/logger.cpp | 101 ++ src/vpp-api/vom/logger.hpp | 107 ++ src/vpp-api/vom/nat_binding.cpp | 179 +++ src/vpp-api/vom/nat_binding.hpp | 335 +++++ src/vpp-api/vom/nat_binding_cmds.cpp | 143 +++ src/vpp-api/vom/nat_static.cpp | 220 ++++ src/vpp-api/vom/nat_static.hpp | 293 +++++ src/vpp-api/vom/nat_static_cmds.cpp | 165 +++ src/vpp-api/vom/neighbour.cpp | 196 +++ src/vpp-api/vom/neighbour.hpp | 304 +++++ src/vpp-api/vom/neighbour_cmds.cpp | 163 +++ src/vpp-api/vom/object_base.cpp | 70 ++ src/vpp-api/vom/object_base.hpp | 146 +++ src/vpp-api/vom/om.cpp | 153 +++ src/vpp-api/vom/om.hpp | 355 ++++++ src/vpp-api/vom/prefix.cpp | 316 +++++ src/vpp-api/vom/prefix.hpp | 244 ++++ src/vpp-api/vom/ra_config.cpp | 88 ++ src/vpp-api/vom/ra_config.hpp | 145 +++ src/vpp-api/vom/ra_prefix.cpp | 88 ++ src/vpp-api/vom/ra_prefix.hpp | 134 ++ src/vpp-api/vom/route.cpp | 427 +++++++ src/vpp-api/vom/route.hpp | 473 +++++++ src/vpp-api/vom/route_cmds.cpp | 183 +++ src/vpp-api/vom/route_domain.cpp | 167 +++ src/vpp-api/vom/route_domain.hpp | 230 ++++ src/vpp-api/vom/route_domain_cmds.cpp | 111 ++ src/vpp-api/vom/rpc_cmd.hpp | 139 +++ src/vpp-api/vom/singular_db.hpp | 156 +++ src/vpp-api/vom/sub_interface.cpp | 99 ++ src/vpp-api/vom/sub_interface.hpp | 175 +++ src/vpp-api/vom/sub_interface_cmds.cpp | 112 ++ src/vpp-api/vom/tap_interface.cpp | 158 +++ src/vpp-api/vom/tap_interface.hpp | 197 +++ src/vpp-api/vom/tap_interface_cmds.cpp | 131 ++ src/vpp-api/vom/types.cpp | 287 +++++ src/vpp-api/vom/types.hpp | 337 +++++ src/vpp-api/vom/vxlan_tunnel.cpp | 272 ++++ src/vpp-api/vom/vxlan_tunnel.hpp | 331 +++++ src/vpp-api/vom/vxlan_tunnel_cmds.cpp | 154 +++ test/ext/Makefile | 59 +- test/ext/vom_test.cpp | 1447 ++++++++++++++++++++++ test/framework.py | 30 + test/test_vapi.py | 33 +- test/test_vom.py | 48 + 120 files changed, 23171 insertions(+), 55 deletions(-) create mode 100644 src/vpp-api/vom/.clang-format create mode 100644 src/vpp-api/vom/Makefile.am create mode 100644 src/vpp-api/vom/acl_binding.cpp create mode 100644 src/vpp-api/vom/acl_binding.hpp create mode 100644 src/vpp-api/vom/acl_binding_cmds.cpp create mode 100644 src/vpp-api/vom/acl_l2_rule.cpp create mode 100644 src/vpp-api/vom/acl_l2_rule.hpp create mode 100644 src/vpp-api/vom/acl_l3_rule.cpp create mode 100644 src/vpp-api/vom/acl_l3_rule.hpp create mode 100644 src/vpp-api/vom/acl_list.cpp create mode 100644 src/vpp-api/vom/acl_list.hpp create mode 100644 src/vpp-api/vom/acl_list_cmds.cpp create mode 100644 src/vpp-api/vom/acl_types.cpp create mode 100644 src/vpp-api/vom/acl_types.hpp create mode 100644 src/vpp-api/vom/arp_proxy_binding.cpp create mode 100644 src/vpp-api/vom/arp_proxy_binding.hpp create mode 100644 src/vpp-api/vom/arp_proxy_binding_cmds.cpp create mode 100644 src/vpp-api/vom/arp_proxy_config.cpp create mode 100644 src/vpp-api/vom/arp_proxy_config.hpp create mode 100644 src/vpp-api/vom/arp_proxy_config_cmds.cpp create mode 100644 src/vpp-api/vom/bridge_domain.cpp create mode 100644 src/vpp-api/vom/bridge_domain.hpp create mode 100644 src/vpp-api/vom/bridge_domain_arp_entry.cpp create mode 100644 src/vpp-api/vom/bridge_domain_arp_entry.hpp create mode 100644 src/vpp-api/vom/bridge_domain_arp_entry_cmds.cpp create mode 100644 src/vpp-api/vom/bridge_domain_cmds.cpp create mode 100644 src/vpp-api/vom/bridge_domain_entry.cpp create mode 100644 src/vpp-api/vom/bridge_domain_entry.hpp create mode 100644 src/vpp-api/vom/bridge_domain_entry_cmds.cpp create mode 100644 src/vpp-api/vom/client_db.cpp create mode 100644 src/vpp-api/vom/client_db.hpp create mode 100644 src/vpp-api/vom/cmd.cpp create mode 100644 src/vpp-api/vom/cmd.hpp create mode 100644 src/vpp-api/vom/connection.cpp create mode 100644 src/vpp-api/vom/connection.hpp create mode 100644 src/vpp-api/vom/dhcp_config.cpp create mode 100644 src/vpp-api/vom/dhcp_config.hpp create mode 100644 src/vpp-api/vom/dhcp_config_cmds.cpp create mode 100644 src/vpp-api/vom/dump_cmd.hpp create mode 100644 src/vpp-api/vom/enum_base.hpp create mode 100644 src/vpp-api/vom/event_cmd.hpp create mode 100644 src/vpp-api/vom/hw.cpp create mode 100644 src/vpp-api/vom/hw.hpp create mode 100644 src/vpp-api/vom/inspect.cpp create mode 100644 src/vpp-api/vom/inspect.hpp create mode 100644 src/vpp-api/vom/interface.cpp create mode 100644 src/vpp-api/vom/interface.hpp create mode 100644 src/vpp-api/vom/interface_cmds.cpp create mode 100644 src/vpp-api/vom/interface_factory.cpp create mode 100644 src/vpp-api/vom/interface_ip6_nd.hpp create mode 100644 src/vpp-api/vom/interface_ip6_nd_cmds.cpp create mode 100644 src/vpp-api/vom/interface_span.cpp create mode 100644 src/vpp-api/vom/interface_span.hpp create mode 100644 src/vpp-api/vom/interface_span_cmds.cpp create mode 100644 src/vpp-api/vom/interface_types.cpp create mode 100644 src/vpp-api/vom/ip_unnumbered.cpp create mode 100644 src/vpp-api/vom/ip_unnumbered.hpp create mode 100644 src/vpp-api/vom/ip_unnumbered_cmds.cpp create mode 100644 src/vpp-api/vom/l2_binding.cpp create mode 100644 src/vpp-api/vom/l2_binding.hpp create mode 100644 src/vpp-api/vom/l2_binding_cmds.cpp create mode 100644 src/vpp-api/vom/l3_binding.cpp create mode 100644 src/vpp-api/vom/l3_binding.hpp create mode 100644 src/vpp-api/vom/l3_binding_cmds.cpp create mode 100644 src/vpp-api/vom/lldp_binding.cpp create mode 100644 src/vpp-api/vom/lldp_binding.hpp create mode 100644 src/vpp-api/vom/lldp_binding_cmds.cpp create mode 100644 src/vpp-api/vom/lldp_global.cpp create mode 100644 src/vpp-api/vom/lldp_global.hpp create mode 100644 src/vpp-api/vom/lldp_global_cmds.cpp create mode 100644 src/vpp-api/vom/logger.cpp create mode 100644 src/vpp-api/vom/logger.hpp create mode 100644 src/vpp-api/vom/nat_binding.cpp create mode 100644 src/vpp-api/vom/nat_binding.hpp create mode 100644 src/vpp-api/vom/nat_binding_cmds.cpp create mode 100644 src/vpp-api/vom/nat_static.cpp create mode 100644 src/vpp-api/vom/nat_static.hpp create mode 100644 src/vpp-api/vom/nat_static_cmds.cpp create mode 100644 src/vpp-api/vom/neighbour.cpp create mode 100644 src/vpp-api/vom/neighbour.hpp create mode 100644 src/vpp-api/vom/neighbour_cmds.cpp create mode 100644 src/vpp-api/vom/object_base.cpp create mode 100644 src/vpp-api/vom/object_base.hpp create mode 100644 src/vpp-api/vom/om.cpp create mode 100644 src/vpp-api/vom/om.hpp create mode 100644 src/vpp-api/vom/prefix.cpp create mode 100644 src/vpp-api/vom/prefix.hpp create mode 100644 src/vpp-api/vom/ra_config.cpp create mode 100644 src/vpp-api/vom/ra_config.hpp create mode 100644 src/vpp-api/vom/ra_prefix.cpp create mode 100644 src/vpp-api/vom/ra_prefix.hpp create mode 100644 src/vpp-api/vom/route.cpp create mode 100644 src/vpp-api/vom/route.hpp create mode 100644 src/vpp-api/vom/route_cmds.cpp create mode 100644 src/vpp-api/vom/route_domain.cpp create mode 100644 src/vpp-api/vom/route_domain.hpp create mode 100644 src/vpp-api/vom/route_domain_cmds.cpp create mode 100644 src/vpp-api/vom/rpc_cmd.hpp create mode 100644 src/vpp-api/vom/singular_db.hpp create mode 100644 src/vpp-api/vom/sub_interface.cpp create mode 100644 src/vpp-api/vom/sub_interface.hpp create mode 100644 src/vpp-api/vom/sub_interface_cmds.cpp create mode 100644 src/vpp-api/vom/tap_interface.cpp create mode 100644 src/vpp-api/vom/tap_interface.hpp create mode 100644 src/vpp-api/vom/tap_interface_cmds.cpp create mode 100644 src/vpp-api/vom/types.cpp create mode 100644 src/vpp-api/vom/types.hpp create mode 100644 src/vpp-api/vom/vxlan_tunnel.cpp create mode 100644 src/vpp-api/vom/vxlan_tunnel.hpp create mode 100644 src/vpp-api/vom/vxlan_tunnel_cmds.cpp create mode 100644 test/ext/vom_test.cpp create mode 100644 test/test_vom.py diff --git a/.gitignore b/.gitignore index 5a6266d79fa..2810cb51385 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ /build-root/test-cov/ /build-root/python/ /build-root/vapi_test/ +/build-root/vom_test/ /build-config.mk /dpdk/*.tar.gz /dpdk/*.tar.xz diff --git a/Makefile b/Makefile index 182156cc454..799fa7e974f 100644 --- a/Makefile +++ b/Makefile @@ -69,6 +69,7 @@ DEB_DEPENDS += debhelper dkms git libtool libapr1-dev dh-systemd DEB_DEPENDS += libconfuse-dev git-review exuberant-ctags cscope pkg-config DEB_DEPENDS += lcov chrpath autoconf nasm indent clang-format libnuma-dev DEB_DEPENDS += python-all python-dev python-virtualenv python-pip libffi6 check +DEB_DEPENDS += libboost-all-dev ifeq ($(OS_VERSION_ID),14.04) DEB_DEPENDS += openjdk-8-jdk-headless DEB_DEPENDS += libssl-dev @@ -88,6 +89,7 @@ RPM_DEPENDS = redhat-lsb glibc-static java-1.8.0-openjdk-devel yum-utils RPM_DEPENDS += apr-devel RPM_DEPENDS += numactl-devel RPM_DEPENDS += check check-devel +RPM_DEPENDS += boost boost-devel ifeq ($(OS_ID)-$(OS_VERSION_ID),fedora-25) RPM_DEPENDS += subunit subunit-devel diff --git a/src/Makefile.am b/src/Makefile.am index c1b6b0e9d89..7b6818f2098 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,6 +20,7 @@ ACLOCAL_AMFLAGS = -I m4 AM_LIBTOOLFLAGS = --quiet AM_CFLAGS = -Wall +AM_CXXFLAGS = -Wall -std=gnu++11 SUBDIRS = . SUFFIXES = .api.h .api .api.json @@ -82,6 +83,7 @@ SUBDIRS += vpp-api/java endif SUBDIRS += vpp-api/vapi +SUBDIRS += vpp-api/vom ############################################################################### # API diff --git a/src/configure.ac b/src/configure.ac index f90ff9686af..150930f03e2 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -3,11 +3,12 @@ LT_INIT AC_CONFIG_AUX_DIR([.]) AM_INIT_AUTOMAKE([subdir-objects]) AM_SILENT_RULES([yes]) -AC_CONFIG_FILES([Makefile plugins/Makefile vpp-api/python/Makefile vpp-api/java/Makefile vpp-api/vapi/Makefile]) +AC_CONFIG_FILES([Makefile plugins/Makefile vpp-api/python/Makefile vpp-api/java/Makefile vpp-api/vapi/Makefile vpp-api/vom/Makefile]) AC_CONFIG_MACRO_DIR([m4]) AC_PROG_CC AC_PROG_CXX +AC_PROG_CPP AM_PROG_AS AM_PROG_LIBTOOL AC_PROG_YACC diff --git a/src/vpp-api/vapi/vapi.hpp b/src/vpp-api/vapi/vapi.hpp index a3ac2ce7103..e0fd2e5051c 100644 --- a/src/vpp-api/vapi/vapi.hpp +++ b/src/vpp-api/vapi/vapi.hpp @@ -105,7 +105,7 @@ public: private: Connection &con; - Common_req (Connection &con) : con{con}, response_state{RESPONSE_NOT_READY} + Common_req (Connection &con) : con (con), response_state{RESPONSE_NOT_READY} { } @@ -759,7 +759,7 @@ private: } } - Result_set (Connection &con) : con{con}, complete{false} + Result_set (Connection &con) : con (con), complete{false} { } diff --git a/src/vpp-api/vom/.clang-format b/src/vpp-api/vom/.clang-format new file mode 100644 index 00000000000..917dceb793d --- /dev/null +++ b/src/vpp-api/vom/.clang-format @@ -0,0 +1,3 @@ + +BasedOnStyle: mozilla +BinPackParameters: false diff --git a/src/vpp-api/vom/Makefile.am b/src/vpp-api/vom/Makefile.am new file mode 100644 index 00000000000..17b846fa7a4 --- /dev/null +++ b/src/vpp-api/vom/Makefile.am @@ -0,0 +1,151 @@ +# Copyright (c) 2017 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +AUTOMAKE_OPTIONS = foreign +ACLOCAL_AMFLAGS = -I m4 +AM_LIBTOOLFLAGS = --quiet + +AM_CXXFLAGS = -Wall -std=gnu++11 -I${top_srcdir} -I${top_builddir}/vpp-api/vapi/ -I$(top_srcdir)/vpp-api/ -I${libdir}/../include +AM_LDFLAGS = -shared -avoid-version -no-undefined + +bin_PROGRAMS = +noinst_LTLIBRARIES = +CLEANDIRS = + +lib_LTLIBRARIES = libvom.la + +libvom_la_DEPENDENCIES = +libvom_la_LIBADD = \ + $(top_builddir)/vpp-api/vapi/libvapiclient.la \ + -lpthread \ + -lboost_thread \ + $(BOOST_SYSTEM_LIB) \ + $(BOOST_FILESYSTEM_LIB) \ + $(BOOST_ASIO_LIB) \ + -lm -lrt + +libvom_la_SOURCES = \ + acl_binding_cmds.cpp \ + acl_binding.cpp \ + acl_l2_rule.cpp \ + acl_l3_rule.cpp \ + acl_list_cmds.cpp \ + acl_list.cpp \ + acl_types.cpp \ + arp_proxy_binding_cmds.cpp \ + arp_proxy_binding.cpp \ + arp_proxy_config_cmds.cpp \ + arp_proxy_config.cpp \ + bridge_domain_cmds.cpp \ + bridge_domain.cpp \ + bridge_domain_arp_entry.cpp \ + bridge_domain_arp_entry_cmds.cpp \ + bridge_domain_entry_cmds.cpp \ + bridge_domain_entry.cpp \ + client_db.cpp \ + cmd.cpp \ + connection.cpp \ + dhcp_config_cmds.cpp \ + dhcp_config.cpp \ + hw.cpp \ + inspect.cpp \ + interface_cmds.cpp \ + interface.cpp \ + interface_factory.cpp \ + interface_ip6_nd_cmds.cpp \ + interface_span_cmds.cpp \ + interface_span.cpp \ + interface_types.cpp \ + ip_unnumbered_cmds.cpp \ + ip_unnumbered.cpp \ + l2_binding_cmds.cpp \ + l2_binding.cpp \ + l3_binding_cmds.cpp \ + l3_binding.cpp \ + lldp_binding_cmds.cpp \ + lldp_binding.cpp \ + lldp_global_cmds.cpp \ + lldp_global.cpp \ + logger.cpp \ + nat_static.cpp \ + nat_static_cmds.cpp \ + nat_binding.cpp \ + nat_binding_cmds.cpp \ + neighbour.cpp \ + neighbour_cmds.cpp \ + object_base.cpp \ + om.cpp \ + prefix.cpp \ + ra_config.cpp \ + ra_prefix.cpp \ + route.cpp \ + route_cmds.cpp \ + route_domain.cpp \ + route_domain_cmds.cpp \ + sub_interface_cmds.cpp \ + sub_interface.cpp \ + tap_interface.cpp \ + tap_interface_cmds.cpp \ + types.cpp \ + vxlan_tunnel_cmds.cpp \ + vxlan_tunnel.cpp + +vomincludedir = $(includedir)/vom + +vominclude_HEADERS = \ + acl_binding.hpp \ + acl_l2_rule.hpp \ + acl_l3_rule.hpp \ + acl_list.hpp \ + acl_types.hpp \ + arp_proxy_binding.hpp \ + arp_proxy_config.hpp \ + bridge_domain.hpp \ + bridge_domain_arp_entry.hpp \ + bridge_domain_entry.hpp \ + client_db.hpp \ + cmd.hpp \ + connection.hpp \ + dhcp_config.hpp \ + dump_cmd.hpp \ + enum_base.hpp \ + event_cmd.hpp \ + hw.hpp \ + inspect.hpp \ + interface.hpp \ + interface_ip6_nd.hpp \ + interface_span.hpp \ + ip_unnumbered.hpp \ + l2_binding.hpp \ + l3_binding.hpp \ + lldp_binding.hpp \ + lldp_global.hpp \ + logger.hpp \ + nat_static.hpp \ + nat_binding.hpp \ + neighbour.hpp \ + object_base.hpp \ + om.hpp \ + prefix.hpp \ + ra_config.hpp \ + ra_prefix.hpp \ + route.hpp \ + route_domain.hpp \ + rpc_cmd.hpp \ + singular_db.hpp \ + sub_interface.hpp \ + tap_interface.hpp \ + types.hpp \ + vxlan_tunnel.hpp + +# vi:syntax=automake diff --git a/src/vpp-api/vom/acl_binding.cpp b/src/vpp-api/vom/acl_binding.cpp new file mode 100644 index 00000000000..1ebefda6650 --- /dev/null +++ b/src/vpp-api/vom/acl_binding.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/acl_binding.hpp" +#include "vom/om.hpp" + +namespace VOM { +namespace ACL { +template <> +void +l2_binding::event_handler::handle_populate(const client_db::key_t& key) +{ + /* +* dump VPP Bridge domains +*/ + std::shared_ptr cmd(new l2_binding::dump_cmd()); + + HW::enqueue(cmd); + HW::write(); + + for (auto& record : *cmd) { + auto& payload = record.get_payload(); + + std::shared_ptr itf = interface::find(payload.sw_if_index); + + for (int ii = 0; ii < payload.count; ii++) { + std::shared_ptr acl = l2_list::find(payload.acls[ii]); + + l2_binding binding(direction_t::INPUT, *itf, *acl); + + OM::commit(key, binding); + } + } +} + +template <> +void +l3_binding::event_handler::handle_populate(const client_db::key_t& key) +{ + std::shared_ptr cmd(new l3_binding::dump_cmd()); + + HW::enqueue(cmd); + HW::write(); + + for (auto& record : *cmd) { + auto& payload = record.get_payload(); + + std::shared_ptr itf = interface::find(payload.sw_if_index); + uint8_t n_input = payload.n_input; + + for (int ii = 0; ii < payload.count; ii++) { + std::shared_ptr acl = l3_list::find(payload.acls[ii]); + + if (n_input) { + l3_binding binding(direction_t::INPUT, *itf, *acl); + n_input--; + OM::commit(key, binding); + } else { + l3_binding binding(direction_t::OUTPUT, *itf, *acl); + OM::commit(key, binding); + } + } + } +} +}; + +std::ostream& +operator<<(std::ostream& os, + const std::pair& key) +{ + os << "[" << key.first.to_string() << " " << key.second << "]"; + + return (os); +} +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/acl_binding.hpp b/src/vpp-api/vom/acl_binding.hpp new file mode 100644 index 00000000000..faf9b245cae --- /dev/null +++ b/src/vpp-api/vom/acl_binding.hpp @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_ACL_BINDING_H__ +#define __VOM_ACL_BINDING_H__ + +#include + +#include "vom/acl_list.hpp" +#include "vom/acl_types.hpp" +#include "vom/hw.hpp" +#include "vom/inspect.hpp" +#include "vom/interface.hpp" +#include "vom/object_base.hpp" +#include "vom/om.hpp" +#include "vom/rpc_cmd.hpp" +#include "vom/singular_db.hpp" + +namespace VOM { +namespace ACL { +/** + * A binding between an ACL and an interface. + * A representation of the application of the ACL to the interface. + */ +template +class binding : public object_base +{ +public: + /** + * The key for a binding is the direction and the interface + */ + typedef std::pair key_t; + + /** + * Construct a new object matching the desried state + */ + binding(const direction_t& direction, const interface& itf, const LIST& acl) + : m_direction(direction) + , m_itf(itf.singular()) + , m_acl(acl.singular()) + , m_binding(0) + { + m_evh.order(); + } + + /** + * Copy Constructor + */ + binding(const binding& o) + : m_direction(o.m_direction) + , m_itf(o.m_itf) + , m_acl(o.m_acl) + , m_binding(0) + { + } + + /** + * Destructor + */ + ~binding() + { + sweep(); + m_db.release(std::make_pair(m_direction, m_itf->key()), this); + } + + /** + * Return the 'singular instance' of the L2 config that matches this + * object + */ + std::shared_ptr singular() const { return find_or_add(*this); } + + /** + * convert to string format for debug purposes + */ + std::string to_string() const + { + std::ostringstream s; + s << "acl-binding:[" << m_direction.to_string() << " " << m_itf->to_string() + << " " << m_acl->to_string() << " " << m_binding.to_string() << "]"; + + return (s.str()); + } + + /** + * Dump all bindings into the stream provided + */ + static void dump(std::ostream& os) { m_db.dump(os); } + + /** + * A command class that binds the ACL to the interface + */ + class bind_cmd : public rpc_cmd, rc_t, BIND> + { + public: + /** + * Constructor + */ + bind_cmd(HW::item& item, + const direction_t& direction, + const handle_t& itf, + const handle_t& acl) + : rpc_cmd, rc_t, BIND>(item) + , m_direction(direction) + , m_itf(itf) + , m_acl(acl) + { + } + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const + { + std::ostringstream s; + s << "acl-bind:[" << m_direction.to_string() + << " itf:" << m_itf.to_string() << " acl:" << m_acl.to_string() << "]"; + + return (s.str()); + } + + /** + * Comparison operator - only used for UT + */ + bool operator==(const bind_cmd& other) const + { + return ((m_itf == other.m_itf) && (m_acl == m_acl)); + } + + private: + /** + * The direction of the binding + */ + const direction_t m_direction; + + /** + * The interface to bind to + */ + const handle_t m_itf; + + /** + * The ACL to bind + */ + const handle_t m_acl; + }; + + /** + * A command class that binds the ACL to the interface + */ + class unbind_cmd : public rpc_cmd, rc_t, BIND> + { + public: + /** + * Constructor + */ + unbind_cmd(HW::item& item, + const direction_t& direction, + const handle_t& itf, + const handle_t& acl) + : rpc_cmd, rc_t, BIND>(item) + , m_direction(direction) + , m_itf(itf) + , m_acl(acl) + { + } + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const + { + std::ostringstream s; + s << "acl-unbind:[" << m_direction.to_string() + << " itf:" << m_itf.to_string() << " acl:" << m_acl.to_string() << "]"; + + return (s.str()); + } + + /** + * Comparison operator - only used for UT + */ + bool operator==(const unbind_cmd& other) const + { + return ((m_itf == other.m_itf) && (m_acl == m_acl)); + } + + private: + /** + * The direction of the binding + */ + const direction_t m_direction; + + /** + * The interface to bind to + */ + const handle_t m_itf; + + /** + * The ACL to bind + */ + const handle_t m_acl; + }; + + /** + * A cmd class that Dumps all the ACLs + */ + class dump_cmd : public VOM::dump_cmd + { + public: + /** + * Constructor + */ + dump_cmd() = default; + dump_cmd(const dump_cmd& d) = default; + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const { return ("acl-bind-dump"); } + + private: + /** + * HW reutrn code + */ + HW::item item; + }; + +private: + /** + * Class definition for listeners to OM events + */ + class event_handler : public OM::listener, public inspect::command_handler + { + public: + event_handler() + { + OM::register_listener(this); + inspect::register_handler({ "acl-binding" }, "ACL bindings", this); + } + virtual ~event_handler() = default; + + /** + * Handle a populate event + */ + void handle_populate(const client_db::key_t& key); + + /** + * Handle a replay event + */ + void handle_replay() { m_db.replay(); } + + /** + * Show the object in the Singular DB + */ + void show(std::ostream& os) { m_db.dump(os); } + + /** + * Get the sortable Id of the listener + */ + dependency_t order() const { return (dependency_t::BINDING); } + }; + + /** + * event_handler to register with OM + */ + static event_handler m_evh; + + /** + * Enquue commonds to the VPP command Q for the update + */ + void update(const binding& obj) + { + if (!m_binding) { + HW::enqueue( + new bind_cmd(m_binding, m_direction, m_itf->handle(), m_acl->handle())); + } + HW::write(); + } + + /** + * Find or Add the instance in the DB + */ + static std::shared_ptr find_or_add(const binding& temp) + { + return (m_db.find_or_add( + std::make_pair(temp.m_direction, temp.m_itf->key()), temp)); + } + + /* + * It's the OM class that calls singular() + */ + friend class VOM::OM; + + /** + * It's the singular_db class that calls replay() + */ + friend class singular_db; + + /** + * Sweep/reap the object if still stale + */ + void sweep(void) + { + if (m_binding) { + HW::enqueue(new unbind_cmd(m_binding, m_direction, m_itf->handle(), + m_acl->handle())); + } + HW::write(); + } + + /** + * Replay the objects state to HW + */ + void replay(void) + { + if (m_binding) { + HW::enqueue( + new bind_cmd(m_binding, m_direction, m_itf->handle(), m_acl->handle())); + } + } + + /** + * The direction the of the packets on which to apply the ACL + * input or output + */ + const direction_t m_direction; + + /** + * A reference counting pointer the interface that this L3 layer + * represents. By holding the reference here, we can guarantee that + * this object will outlive the interface + */ + const std::shared_ptr m_itf; + + /** + * A reference counting pointer the ACL that this + * interface is bound to. By holding the reference here, we can + * guarantee that this object will outlive the BD. + */ + const std::shared_ptr m_acl; + + /** + * HW configuration for the binding. The bool representing the + * do/don't bind. + */ + HW::item m_binding; + + /** + * A map of all L2 interfaces key against the interface's handle_t + */ + static singular_db m_db; +}; + +/** + * Typedef the L3 binding type + */ +typedef binding + l3_binding; + +/** + * Typedef the L2 binding type + */ +typedef binding + l2_binding; + +/** + * Definition of the static Singular DB for ACL bindings + */ +template +singular_db::key_t, + ACL::binding> + binding::m_db; + +template +typename ACL::binding::event_handler + binding::m_evh; +}; + +std::ostream& operator<<( + std::ostream& os, + const std::pair& key); +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/acl_binding_cmds.cpp b/src/vpp-api/vom/acl_binding_cmds.cpp new file mode 100644 index 00000000000..27d50434c3b --- /dev/null +++ b/src/vpp-api/vom/acl_binding_cmds.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/acl_binding.hpp" + +DEFINE_VAPI_MSG_IDS_ACL_API_JSON; + +namespace VOM { +namespace ACL { +template <> +rc_t +l3_binding::bind_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_itf.value(); + payload.is_add = 1; + payload.is_input = (m_direction == direction_t::INPUT ? 1 : 0); + payload.acl_index = m_acl.value(); + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return rc_t::OK; +} + +template <> +rc_t +l3_binding::unbind_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_itf.value(); + payload.is_add = 0; + payload.is_input = (m_direction == direction_t::INPUT ? 1 : 0); + payload.acl_index = m_acl.value(); + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return rc_t::OK; +} + +template <> +rc_t +l3_binding::dump_cmd::issue(connection& con) +{ + m_dump.reset(new msg_t(con.ctx(), std::ref(*this))); + + auto& payload = m_dump->get_request().get_payload(); + payload.sw_if_index = ~0; + + VAPI_CALL(m_dump->execute()); + + wait(); + + return rc_t::OK; +} + +template <> +rc_t +l2_binding::bind_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_itf.value(); + payload.is_add = 1; + // payload.is_input = (m_direction == direction_t::INPUT ? 1 : 0); + payload.acl_index = m_acl.value(); + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return rc_t::OK; +} + +template <> +rc_t +l2_binding::unbind_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_itf.value(); + payload.is_add = 0; + // payload.is_input = (m_direction == direction_t::INPUT ? 1 : 0); + payload.acl_index = m_acl.value(); + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return rc_t::OK; +} + +template <> +rc_t +l2_binding::dump_cmd::issue(connection& con) +{ + m_dump.reset(new msg_t(con.ctx(), std::ref(*this))); + + auto& payload = m_dump->get_request().get_payload(); + payload.sw_if_index = ~0; + + VAPI_CALL(m_dump->execute()); + + wait(); + + return rc_t::OK; +} +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/acl_l2_rule.cpp b/src/vpp-api/vom/acl_l2_rule.cpp new file mode 100644 index 00000000000..1f7f9fdabc8 --- /dev/null +++ b/src/vpp-api/vom/acl_l2_rule.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "vom/acl_l2_rule.hpp" + +namespace VOM { +namespace ACL { + +l2_rule::l2_rule(uint32_t priority, + const action_t& action, + const route::prefix_t& ip, + const mac_address_t& mac, + const mac_address_t& mac_mask) + : m_priority(priority) + , m_action(action) + , m_src_ip(ip) + , m_mac(mac) + , m_mac_mask(mac_mask) +{ +} + +bool +l2_rule::operator<(const l2_rule& other) const +{ + return (other.m_priority < m_priority); +} + +void +l2_rule::to_vpp(vapi_type_macip_acl_rule& rule) const +{ + rule.is_permit = m_action.value(); + m_src_ip.to_vpp(&rule.is_ipv6, rule.src_ip_addr, &rule.src_ip_prefix_len); + m_mac.to_bytes(rule.src_mac, 6); + m_mac_mask.to_bytes(rule.src_mac_mask, 6); +} + +bool +l2_rule::operator==(const l2_rule& rule) const +{ + return ((m_action == rule.m_action) && (m_src_ip == rule.m_src_ip) && + (m_mac == rule.m_mac) && (m_mac_mask == rule.m_mac_mask)); +} + +std::string +l2_rule::to_string() const +{ + std::ostringstream s; + + s << "L2-rule:[" + << "priority:" << m_priority << " action:" << m_action.to_string() + << " ip:" << m_src_ip.to_string() << " mac:" << m_mac + << " mac-mask:" << m_mac_mask << "]"; + + return (s.str()); +} +} +} +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/acl_l2_rule.hpp b/src/vpp-api/vom/acl_l2_rule.hpp new file mode 100644 index 00000000000..cc869173011 --- /dev/null +++ b/src/vpp-api/vom/acl_l2_rule.hpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_L2_ACL_RULE_H__ +#define __VOM_L2_ACL_RULE_H__ + +#include "vom/acl_types.hpp" +#include "vom/prefix.hpp" + +#include + +namespace VOM { +namespace ACL { +/** + * An ACL rule is the building block of an ACL. An ACL, which is + * the object applied to an interface, is comprised of an ordersed + * sequence of ACL rules. + * This class is a wrapper around the VAPI generated struct and exports + * an API with better types. + */ +class l2_rule +{ +public: + /** + * Construct a new object matching the desried state + */ + l2_rule(uint32_t priority, + const action_t& action, + const route::prefix_t& ip, + const mac_address_t& mac, + const mac_address_t& mac_mask); + + /** + * Copy Constructor + */ + l2_rule(const l2_rule& o) = default; + + /** + * Destructor + */ + ~l2_rule() = default; + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * less-than operator + */ + bool operator<(const l2_rule& rule) const; + + /** + * comparison operator (for testing) + */ + bool operator==(const l2_rule& rule) const; + + /** + * Convert to VPP API fromat + */ + void to_vpp(vapi_type_macip_acl_rule& rule) const; + +private: + /** + * Priority. Used to sort the rules in a list in the order + * in which they are applied + */ + uint32_t m_priority; + + /** + * Action on match + */ + action_t m_action; + + /** + * Source Prefix + */ + route::prefix_t m_src_ip; + + /** + * Source Mac + */ + mac_address_t m_mac; + + /** + * Source MAC mask + */ + mac_address_t m_mac_mask; +}; +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/acl_l3_rule.cpp b/src/vpp-api/vom/acl_l3_rule.cpp new file mode 100644 index 00000000000..e4b40920dca --- /dev/null +++ b/src/vpp-api/vom/acl_l3_rule.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "vom/acl_l3_rule.hpp" + +namespace VOM { +namespace ACL { +l3_rule::l3_rule(uint32_t priority, + const action_t& action, + const route::prefix_t& src, + const route::prefix_t& dst) + : m_priority(priority) + , m_action(action) + , m_src(src) + , m_dst(dst) + , m_proto(0) + , m_srcport_or_icmptype_first(0) + , m_srcport_or_icmptype_last(0) + , m_dstport_or_icmpcode_first(0) + , m_dstport_or_icmpcode_last(0) + , m_tcp_flags_mask(0) + , m_tcp_flags_value(0) +{ +} + +bool +l3_rule::operator<(const l3_rule& other) const +{ + return (other.m_priority < m_priority); +} + +void +l3_rule::to_vpp(vapi_type_acl_rule& rule) const +{ + rule.is_permit = m_action.value(); + m_src.to_vpp(&rule.is_ipv6, rule.src_ip_addr, &rule.src_ip_prefix_len); + m_dst.to_vpp(&rule.is_ipv6, rule.dst_ip_addr, &rule.dst_ip_prefix_len); + + rule.proto = m_proto; + rule.srcport_or_icmptype_first = m_srcport_or_icmptype_first; + rule.srcport_or_icmptype_last = m_srcport_or_icmptype_last; + rule.dstport_or_icmpcode_first = m_dstport_or_icmpcode_first; + rule.dstport_or_icmpcode_last = m_dstport_or_icmpcode_last; + + rule.tcp_flags_mask = m_tcp_flags_mask; + rule.tcp_flags_value = m_tcp_flags_value; +} + +bool +l3_rule::operator==(const l3_rule& rule) const +{ + return ((m_action == rule.m_action) && (m_src == rule.m_src) && + (m_dst == rule.m_dst) && (m_proto == rule.m_proto) && + (m_srcport_or_icmptype_first == rule.m_srcport_or_icmptype_first) && + (m_srcport_or_icmptype_last == rule.m_srcport_or_icmptype_last) && + (m_dstport_or_icmpcode_first == rule.m_dstport_or_icmpcode_first) && + (m_dstport_or_icmpcode_last == rule.m_dstport_or_icmpcode_last) && + (m_tcp_flags_mask == rule.m_tcp_flags_mask) && + (m_tcp_flags_value == rule.m_tcp_flags_value)); +} + +std::string +l3_rule::to_string() const +{ + std::ostringstream s; + + s << "L3-rule:[" + << "priority:" << m_priority << " action:" << m_action.to_string() + << " src:" << m_src.to_string() << " dst:" << m_dst.to_string() + << " proto:" << std::to_string(m_proto) + << " srcportfrom:" << m_srcport_or_icmptype_first + << " srcportto: " << m_srcport_or_icmptype_last + << " dstportfrom:" << m_dstport_or_icmpcode_first + << " dstportto:" << m_dstport_or_icmpcode_last + << " tcpflagmask:" << m_tcp_flags_mask + << " tcpflagvalue:" << m_tcp_flags_value << "]"; + + return (s.str()); +} + +void +l3_rule::set_src_ip(route::prefix_t src) +{ + m_src = src; +} + +void +l3_rule::set_dst_ip(route::prefix_t dst) +{ + m_dst = dst; +} + +void +l3_rule::set_proto(uint8_t proto) +{ + m_proto = proto; +} +void +l3_rule::set_src_from_port(uint16_t srcport_or_icmptype_first) +{ + m_srcport_or_icmptype_first = srcport_or_icmptype_first; +} + +void +l3_rule::set_src_to_port(uint16_t srcport_or_icmptype_last) +{ + m_srcport_or_icmptype_last = srcport_or_icmptype_last; +} + +void +l3_rule::set_dst_from_port(uint16_t dstport_or_icmpcode_first) +{ + m_dstport_or_icmpcode_first = dstport_or_icmpcode_first; +} + +void +l3_rule::set_dst_to_port(uint16_t dstport_or_icmpcode_last) +{ + m_dstport_or_icmpcode_last = dstport_or_icmpcode_last; +} + +void +l3_rule::set_tcp_flags_mask(uint8_t tcp_flags_mask) +{ + m_tcp_flags_mask = tcp_flags_mask; +} + +void +l3_rule::set_tcp_flags_value(uint8_t tcp_flags_value) +{ + m_tcp_flags_value = tcp_flags_value; +} +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/acl_l3_rule.hpp b/src/vpp-api/vom/acl_l3_rule.hpp new file mode 100644 index 00000000000..db3ddb41639 --- /dev/null +++ b/src/vpp-api/vom/acl_l3_rule.hpp @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_L3_ACL_RULE_H__ +#define __VOM_L3_ACL_RULE_H__ + +#include "vom/acl_types.hpp" +#include "vom/prefix.hpp" + +#include + +namespace VOM { +namespace ACL { +/** + * An ACL rule is the building block of an ACL. An ACL, which is + * the object applied to an interface, is comprised of an ordersed + * sequence of ACL rules. + * This class is a wrapper around the VAPI generated struct and exports + * an API with better types. + */ +class l3_rule +{ +public: + /** + * Construct a new object matching the desried state + */ + l3_rule(uint32_t priority, + const action_t& action, + const route::prefix_t& src, + const route::prefix_t& dst); + + /** + * Copy Constructor + */ + l3_rule(const l3_rule& o) = default; + + /** + * Destructor + */ + ~l3_rule() = default; + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * less-than operator + */ + bool operator<(const l3_rule& rule) const; + + /** + * comparison operator (for testing) + */ + bool operator==(const l3_rule& rule) const; + + /** + * Convert to VPP API fromat + */ + void to_vpp(vapi_type_acl_rule& rule) const; + + /** + * Set Src Ip Address + */ + void set_src_ip(route::prefix_t src); + + /** + * Set Dst Ip Address + */ + void set_dst_ip(route::prefix_t dst); + + /** + *Set proto + */ + void set_proto(uint8_t proto); + + /** + * Set Src port or ICMP Type first + */ + void set_src_from_port(uint16_t srcport_or_icmptype_first); + + /** + * Set Src port or ICMP Type last + */ + void set_src_to_port(uint16_t srcport_or_icmptype_last); + + /** + * Set Dst port or ICMP code first + */ + void set_dst_from_port(uint16_t dstport_or_icmpcode_first); + + /** + * Set Dst port or ICMP code last + */ + void set_dst_to_port(uint16_t dstport_or_icmpcode_last); + + /** + * Set TCP flags mask + */ + void set_tcp_flags_mask(uint8_t tcp_flags_mask); + + /** + * Set TCP flags value + */ + void set_tcp_flags_value(uint8_t tcp_flags_value); + +private: + /** + * Priority. Used to sort the rules in a list in the order + * in which they are applied + */ + uint32_t m_priority; + + /** + * Action on match + */ + action_t m_action; + + /** + * Source Prefix + */ + route::prefix_t m_src; + + /** + * Destination Prefix + */ + route::prefix_t m_dst; + + /** + * L4 protocol. IANA number. 1 = ICMP, 58 = ICMPv6, 6 = TCP, 17 = + * UDP. + * 0 => ignore L4 and ignore the ports/tcpflags when matching. + */ + uint8_t m_proto; + + /** + * If the L4 protocol is TCP or UDP, the below + * hold ranges of ports, else if the L4 is ICMP/ICMPv6 + * they hold ranges of ICMP(v6) types/codes. + * + * Ranges are inclusive, i.e. to match "any" TCP/UDP port, + * use first=0,last=65535. For ICMP(v6), + * use first=0,last=255. + */ + uint16_t m_srcport_or_icmptype_first; + uint16_t m_srcport_or_icmptype_last; + uint16_t m_dstport_or_icmpcode_first; + uint16_t m_dstport_or_icmpcode_last; + + /* + * for proto = 6, this matches if the + * TCP flags in the packet, ANDed with tcp_flags_mask, + * is equal to tcp_flags_value. + */ + uint8_t m_tcp_flags_mask; + uint8_t m_tcp_flags_value; +}; +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/acl_list.cpp b/src/vpp-api/vom/acl_list.cpp new file mode 100644 index 00000000000..129be8faefe --- /dev/null +++ b/src/vpp-api/vom/acl_list.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/acl_list.hpp" +#include "vom/logger.hpp" + +namespace VOM { +namespace ACL { +template <> +void +l2_list::event_handler::handle_populate(const client_db::key_t& key) +{ + /* hack to get this function instantiated */ + m_evh.order(); + + /* +* dump VPP Bridge domains +*/ + std::shared_ptr cmd(new l2_list::dump_cmd()); + + HW::enqueue(cmd); + HW::write(); + + for (auto& record : *cmd) { + auto& payload = record.get_payload(); + + const handle_t hdl(payload.acl_index); + l2_list acl(hdl, std::string(reinterpret_cast(payload.tag))); + + for (unsigned int ii = 0; ii < payload.count; ii++) { + const route::prefix_t pfx(payload.r[ii].is_ipv6, + payload.r[ii].src_ip_addr, + payload.r[ii].src_ip_prefix_len); + l2_rule rule(ii, action_t::from_int(payload.r[ii].is_permit), pfx, + { payload.r[ii].src_mac }, { payload.r[ii].src_mac_mask }); + + acl.insert(rule); + } + VOM_LOG(log_level_t::DEBUG) << "dump: " << acl.to_string(); + + /* +* Write each of the discovered ACLs into the OM, +* but disable the HW Command q whilst we do, so that no +* commands are sent to VPP +*/ + OM::commit(key, acl); + } +} + +template <> +void +l3_list::event_handler::handle_populate(const client_db::key_t& key) +{ + /* hack to get this function instantiated */ + m_evh.order(); + + /* +* dump VPP Bridge domains +*/ + std::shared_ptr cmd(new l3_list::dump_cmd()); + + HW::enqueue(cmd); + HW::write(); + + for (auto& record : *cmd) { + auto& payload = record.get_payload(); + + const handle_t hdl(payload.acl_index); + l3_list acl(hdl, std::string(reinterpret_cast(payload.tag))); + + for (unsigned int ii = 0; ii < payload.count; ii++) { + const route::prefix_t src(payload.r[ii].is_ipv6, + payload.r[ii].src_ip_addr, + payload.r[ii].src_ip_prefix_len); + const route::prefix_t dst(payload.r[ii].is_ipv6, + payload.r[ii].dst_ip_addr, + payload.r[ii].dst_ip_prefix_len); + l3_rule rule(ii, action_t::from_int(payload.r[ii].is_permit), src, dst); + + acl.insert(rule); + } + VOM_LOG(log_level_t::DEBUG) << "dump: " << acl.to_string(); + + /* +* Write each of the discovered ACLs into the OM, +* but disable the HW Command q whilst we do, so that no +* commands are sent to VPP +*/ + OM::commit(key, acl); + } +} +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/acl_list.hpp b/src/vpp-api/vom/acl_list.hpp new file mode 100644 index 00000000000..a2b512727d7 --- /dev/null +++ b/src/vpp-api/vom/acl_list.hpp @@ -0,0 +1,475 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_ACL_LIST_H__ +#define __VOM_ACL_LIST_H__ + +#include + +#include "vom/acl_l2_rule.hpp" +#include "vom/acl_l3_rule.hpp" +#include "vom/acl_types.hpp" +#include "vom/dump_cmd.hpp" +#include "vom/hw.hpp" +#include "vom/inspect.hpp" +#include "vom/om.hpp" +#include "vom/rpc_cmd.hpp" +#include "vom/singular_db.hpp" + +#include + +namespace VOM { +namespace ACL { +/** + * An ACL list comprises a set of match actions rules to be applied to + * packets. + * A list is bound to a given interface. + */ +template +class list : public object_base +{ +public: + /** + * The KEY can be used to uniquely identify the ACL. + * (other choices for keys, like the summation of the properties + * of the rules, are rather too cumbersome to use + */ + typedef std::string key_t; + + /** + * The rule container type + */ + typedef std::multiset rules_t; + + /** + * Construct a new object matching the desried state + */ + list(const key_t& key) + : m_key(key) + { + } + + list(const handle_t& hdl, const key_t& key) + : m_hdl(hdl) + , m_key(key) + { + } + + list(const key_t& key, const rules_t& rules) + : m_key(key) + , m_rules(rules) + { + m_evh.order(); + } + + /** + * Copy Constructor + */ + list(const list& o) + : m_hdl(o.m_hdl) + , m_key(o.m_key) + , m_rules(o.m_rules) + { + } + + /** + * Destructor + */ + ~list() + { + sweep(); + m_db.release(m_key, this); + } + + /** + * Return the 'sigular instance' of the ACL that matches this object + */ + std::shared_ptr singular() const { return find_or_add(*this); } + + /** + * Dump all ACLs into the stream provided + */ + static void dump(std::ostream& os) { m_db.dump(os); } + + /** + * convert to string format for debug purposes + */ + std::string to_string() const + { + std::ostringstream s; + s << "acl-list:[" << m_key << " " << m_hdl.to_string() << " rules:["; + + for (auto rule : m_rules) { + s << rule.to_string() << " "; + } + + s << "]]"; + + return (s.str()); + } + + /** + * Insert priority sorted a rule into the list + */ + void insert(const RULE& rule) { m_rules.insert(rule); } + + /** + * Remove a rule from the list + */ + void remove(const RULE& rule) { m_rules.erase(rule); } + + /** + * Return the VPP assign handle + */ + const handle_t& handle() const { return m_hdl.data(); } + + /** + * A command class that Create the list + */ + class update_cmd + : public rpc_cmd, HW::item, UPDATE> + { + public: + /** + * Constructor + */ + update_cmd(HW::item& item, const key_t& key, const rules_t& rules) + : rpc_cmd, HW::item, UPDATE>(item) + , m_key(key) + , m_rules(rules) + { + } + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const + { + std::ostringstream s; + s << "ACL-list-update: " << this->item().to_string(); + + return (s.str()); + } + + /** + * Comparison operator - only used for UT + */ + bool operator==(const update_cmd& other) const + { + return ((m_key == other.m_key) && (m_rules == other.m_rules)); + } + + void complete() + { + std::shared_ptr sp = find(m_key); + if (sp && this->item()) { + list::add(this->item().data(), sp); + } + } + + void succeeded() + { + rpc_cmd, HW::item, UPDATE>::succeeded(); + complete(); + } + + /** + * A callback function for handling ACL creates + */ + virtual vapi_error_e operator()(UPDATE& reply) + { + int acl_index = reply.get_response().get_payload().acl_index; + int retval = reply.get_response().get_payload().retval; + + VOM_LOG(log_level_t::DEBUG) << this->to_string() << " " << retval; + + HW::item res(acl_index, rc_t::from_vpp_retval(retval)); + + this->fulfill(res); + + return (VAPI_OK); + } + + private: + /** + * The key. + */ + const key_t& m_key; + + /** + * The rules + */ + const rules_t& m_rules; + }; + + /** + * A cmd class that Deletes an ACL + */ + class delete_cmd : public rpc_cmd, rc_t, DELETE> + { + public: + /** + * Constructor + */ + delete_cmd(HW::item& item) + : rpc_cmd, rc_t, DELETE>(item) + { + } + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con) { return (rc_t::INVALID); } + + /** + * convert to string format for debug purposes + */ + std::string to_string() const + { + std::ostringstream s; + s << "ACL-list-delete: " << this->item().to_string(); + + return (s.str()); + } + + /** + * Comparison operator - only used for UT + */ + bool operator==(const delete_cmd& other) const + { + return (this->item().data() == other.item().data()); + } + }; + + /** + * A cmd class that Dumps all the ACLs + */ + class dump_cmd : public VOM::dump_cmd + { + public: + /** + * Constructor + */ + dump_cmd() = default; + dump_cmd(const dump_cmd& d) = default; + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con) { return rc_t::INVALID; } + + /** + * convert to string format for debug purposes + */ + std::string to_string() const { return ("acl-list-dump"); } + + private: + /** + * HW reutrn code + */ + HW::item item; + }; + + static std::shared_ptr find(const handle_t& handle) + { + return (m_hdl_db[handle].lock()); + } + + static std::shared_ptr find(const key_t& key) + { + return (m_db.find(key)); + } + + static void add(const handle_t& handle, std::shared_ptr sp) + { + m_hdl_db[handle] = sp; + } + + static void remove(const handle_t& handle) { m_hdl_db.erase(handle); } + +private: + /** + * Class definition for listeners to OM events + */ + class event_handler : public OM::listener, public inspect::command_handler + { + public: + event_handler() + { + OM::register_listener(this); + inspect::register_handler({ "acl" }, "ACL lists", this); + } + virtual ~event_handler() = default; + + /** + * Handle a populate event + */ + void handle_populate(const client_db::key_t& key); + + /** + * Handle a replay event + */ + void handle_replay() { m_db.replay(); } + + /** + * Show the object in the Singular DB + */ + void show(std::ostream& os) { m_db.dump(os); } + + /** + * Get the sortable Id of the listener + */ + dependency_t order() const { return (dependency_t::ACL); } + }; + + /** + * event_handler to register with OM + */ + static event_handler m_evh; + + /** + * Enquue commonds to the VPP command Q for the update + */ + void update(const list& obj) + { + /* + * always update the instance with the latest rule set + */ + if (!m_hdl || obj.m_rules != m_rules) { + HW::enqueue(new update_cmd(m_hdl, m_key, m_rules)); + } + /* + * We don't, can't, read the priority from VPP, + * so the is equals check above does not include the priorty. + * but we save it now. + */ + m_rules = obj.m_rules; + } + + /** + * HW assigned handle + */ + HW::item m_hdl; + + /** + * Find or add the sigular instance in the DB + */ + static std::shared_ptr find_or_add(const list& temp) + { + return (m_db.find_or_add(temp.m_key, temp)); + } + + /* + * It's the VOM::OM class that updates call update + */ + friend class VOM::OM; + + /** + * It's the VOM::singular_db class that calls replay() + */ + friend class singular_db; + + /** + * Sweep/reap the object if still stale + */ + void sweep(void) + { + if (m_hdl) { + HW::enqueue(new delete_cmd(m_hdl)); + } + HW::write(); + } + + /** + * Replay the objects state to HW + */ + void replay(void) + { + if (m_hdl) { + HW::enqueue(new update_cmd(m_hdl, m_key, m_rules)); + } + } + + /** + * A map of all ACL's against the client's key + */ + static singular_db m_db; + + /** + * A map of all ACLs keyed against VPP's handle + */ + static std::map> m_hdl_db; + + /** + * The Key is a user defined identifer for this ACL + */ + const key_t m_key; + + /** + * A sorted list of the rules + */ + rules_t m_rules; +}; + +/** + * Typedef the L3 ACL type + */ +typedef list + l3_list; + +/** + * Typedef the L2 ACL type + */ +typedef list + l2_list; + +/** + * Definition of the static singular_db for ACL Lists + */ +template +singular_db::key_t, + ACL::list> + list::m_db; + +/** + * Definition of the static per-handle DB for ACL Lists + */ +template +std::map>> + list::m_hdl_db; + +template +typename ACL::list::event_handler + list::m_evh; +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/acl_list_cmds.cpp b/src/vpp-api/vom/acl_list_cmds.cpp new file mode 100644 index 00000000000..9bbe2cd76c4 --- /dev/null +++ b/src/vpp-api/vom/acl_list_cmds.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/acl_list.hpp" + +namespace VOM { +namespace ACL { +template <> +rc_t +l3_list::update_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), m_rules.size(), std::ref(*this)); + uint32_t ii = 0; + + auto& payload = req.get_request().get_payload(); + payload.acl_index = m_hw_item.data().value(); + payload.count = m_rules.size(); + memset(payload.tag, 0, sizeof(payload.tag)); + memcpy(payload.tag, m_key.c_str(), + std::min(m_key.length(), sizeof(payload.tag))); + + auto it = m_rules.cbegin(); + + while (it != m_rules.cend()) { + it->to_vpp(payload.r[ii]); + ++it; + ++ii; + } + + VAPI_CALL(req.execute()); + + m_hw_item = wait(); + complete(); + + return rc_t::OK; +} + +template <> +rc_t +l3_list::delete_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.acl_index = m_hw_item.data().value(); + + VAPI_CALL(req.execute()); + + wait(); + m_hw_item.set(rc_t::NOOP); + + return rc_t::OK; +} + +template <> +rc_t +l3_list::dump_cmd::issue(connection& con) +{ + m_dump.reset(new msg_t(con.ctx(), std::ref(*this))); + + auto& payload = m_dump->get_request().get_payload(); + payload.acl_index = ~0; + + VAPI_CALL(m_dump->execute()); + + wait(); + + return rc_t::OK; +} + +template <> +rc_t +l2_list::update_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), m_rules.size(), std::ref(*this)); + uint32_t ii = 0; + + auto& payload = req.get_request().get_payload(); + // payload.acl_index = m_hw_item.data().value(); + payload.count = m_rules.size(); + memset(payload.tag, 0, sizeof(payload.tag)); + memcpy(payload.tag, m_key.c_str(), + std::min(m_key.length(), sizeof(payload.tag))); + + auto it = m_rules.cbegin(); + + while (it != m_rules.cend()) { + it->to_vpp(payload.r[ii]); + ++it; + ++ii; + } + + VAPI_CALL(req.execute()); + + m_hw_item = wait(); + + return rc_t::OK; +} + +template <> +rc_t +l2_list::delete_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.acl_index = m_hw_item.data().value(); + + VAPI_CALL(req.execute()); + + wait(); + m_hw_item.set(rc_t::NOOP); + + return rc_t::OK; +} + +template <> +rc_t +l2_list::dump_cmd::issue(connection& con) +{ + m_dump.reset(new msg_t(con.ctx(), std::ref(*this))); + + auto& payload = m_dump->get_request().get_payload(); + payload.acl_index = ~0; + + VAPI_CALL(m_dump->execute()); + + wait(); + + return rc_t::OK; +} +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/acl_types.cpp b/src/vpp-api/vom/acl_types.cpp new file mode 100644 index 00000000000..b2c0c7f0b9d --- /dev/null +++ b/src/vpp-api/vom/acl_types.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/acl_types.hpp" + +namespace VOM { +namespace ACL { +const action_t action_t::PERMITANDREFLEX(2, "permitandreflex"); +const action_t action_t::PERMIT(1, "permit"); +const action_t action_t::DENY(0, "deny"); + +action_t::action_t(int v, const std::string s) + : enum_base(v, s) +{ +} + +const action_t& +action_t::from_int(uint8_t i) +{ + if (i == 2) + return action_t::PERMITANDREFLEX; + else if (i) + return action_t::PERMIT; + + return action_t::DENY; +} + +const action_t& +action_t::from_bool(bool b, uint8_t c) +{ + if (b) { + if (c) + return action_t::PERMITANDREFLEX; + return action_t::PERMIT; + } + return action_t::DENY; +} +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/acl_types.hpp b/src/vpp-api/vom/acl_types.hpp new file mode 100644 index 00000000000..ccf0a1c0231 --- /dev/null +++ b/src/vpp-api/vom/acl_types.hpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_ACL_TYPES_H__ +#define __VOM_ACL_TYPES_H__ + +#include "vom/types.hpp" + +namespace VOM { +namespace ACL { +/** + * ACL Actions + */ +struct action_t : public enum_base +{ + /** + * Constructor + */ + action_t(int v, const std::string s); + + /** + * Destructor + */ + ~action_t() = default; + + /** + * Permit and Reflexive + */ + const static action_t PERMITANDREFLEX; + + /** + * Permit Action + */ + const static action_t PERMIT; + + /** + * Deny Action + */ + const static action_t DENY; + + /** + * Get the enum type from a VPP integer value + */ + static const action_t& from_int(uint8_t i); + + /** + *Get the enum type from a bool value and optional uint8_t value + *which implements the connection tracking .... + */ + static const action_t& from_bool(bool b, uint8_t c); +}; +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/arp_proxy_binding.cpp b/src/vpp-api/vom/arp_proxy_binding.cpp new file mode 100644 index 00000000000..77b1bfaacde --- /dev/null +++ b/src/vpp-api/vom/arp_proxy_binding.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/arp_proxy_binding.hpp" +#include "vom/cmd.hpp" + +namespace VOM { + +/** + * A DB of all LLDP configs + */ +singular_db arp_proxy_binding::m_db; + +arp_proxy_binding::event_handler arp_proxy_binding::m_evh; + +arp_proxy_binding::arp_proxy_binding(const interface& itf, + const arp_proxy_config& proxy_cfg) + : m_itf(itf.singular()) + , m_arp_proxy_cfg(proxy_cfg.singular()) + , m_binding(true) +{ +} + +arp_proxy_binding::arp_proxy_binding(const arp_proxy_binding& o) + : m_itf(o.m_itf) + , m_arp_proxy_cfg(o.m_arp_proxy_cfg) + , m_binding(o.m_binding) +{ +} + +arp_proxy_binding::~arp_proxy_binding() +{ + sweep(); + + // not in the DB anymore. + m_db.release(m_itf->key(), this); +} + +void +arp_proxy_binding::sweep() +{ + if (m_binding) { + HW::enqueue(new unbind_cmd(m_binding, m_itf->handle())); + } + HW::write(); +} + +void +arp_proxy_binding::dump(std::ostream& os) +{ + m_db.dump(os); +} + +void +arp_proxy_binding::replay() +{ + if (m_binding) { + HW::enqueue(new bind_cmd(m_binding, m_itf->handle())); + } +} + +std::string +arp_proxy_binding::to_string() const +{ + std::ostringstream s; + s << "ArpProxy-binding: " << m_itf->to_string(); + + return (s.str()); +} + +void +arp_proxy_binding::update(const arp_proxy_binding& desired) +{ + /* + * the desired state is always that the interface should be created + */ + if (!m_binding) { + HW::enqueue(new bind_cmd(m_binding, m_itf->handle())); + } +} + +std::shared_ptr +arp_proxy_binding::find_or_add(const arp_proxy_binding& temp) +{ + return (m_db.find_or_add(temp.m_itf->key(), temp)); +} + +std::shared_ptr +arp_proxy_binding::singular() const +{ + return find_or_add(*this); +} + +arp_proxy_binding::event_handler::event_handler() +{ + OM::register_listener(this); + inspect::register_handler({ "arp-proxy" }, "ARP proxy bindings", this); +} + +void +arp_proxy_binding::event_handler::handle_replay() +{ + m_db.replay(); +} + +void +arp_proxy_binding::event_handler::handle_populate(const client_db::key_t& key) +{ + // FIXME +} + +dependency_t +arp_proxy_binding::event_handler::order() const +{ + return (dependency_t::BINDING); +} + +void +arp_proxy_binding::event_handler::show(std::ostream& os) +{ + m_db.dump(os); +} +} +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/arp_proxy_binding.hpp b/src/vpp-api/vom/arp_proxy_binding.hpp new file mode 100644 index 00000000000..0d316476a8f --- /dev/null +++ b/src/vpp-api/vom/arp_proxy_binding.hpp @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_ARP_PROXY_BINDING_H__ +#define __VOM_ARP_PROXY_BINDING_H__ + +#include "vom/arp_proxy_config.hpp" +#include "vom/dump_cmd.hpp" +#include "vom/hw.hpp" +#include "vom/inspect.hpp" +#include "vom/interface.hpp" +#include "vom/object_base.hpp" +#include "vom/om.hpp" +#include "vom/rpc_cmd.hpp" +#include "vom/singular_db.hpp" + +#include + +namespace VOM { +/** + * A representation of LLDP client configuration on an interface + */ +class arp_proxy_binding : public object_base +{ +public: + /** + * Construct a new object matching the desried state + */ + arp_proxy_binding(const interface& itf, const arp_proxy_config& proxy_cfg); + + /** + * Copy Constructor + */ + arp_proxy_binding(const arp_proxy_binding& o); + + /** + * Destructor + */ + ~arp_proxy_binding(); + + /** + * Return the 'singular' of the LLDP binding that matches this object + */ + std::shared_ptr singular() const; + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Dump all LLDP bindings into the stream provided + */ + static void dump(std::ostream& os); + + /** + * A command class that binds the LLDP config to the interface + */ + class bind_cmd + : public rpc_cmd, rc_t, vapi::Proxy_arp_intfc_enable_disable> + { + public: + /** + * Constructor + */ + bind_cmd(HW::item& item, const handle_t& itf); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const bind_cmd& i) const; + + private: + /** + * Reference to the HW::item of the interface to bind + */ + const handle_t& m_itf; + }; + + /** + * A cmd class that Unbinds ArpProxy Config from an interface + */ + class unbind_cmd + : public rpc_cmd, rc_t, vapi::Proxy_arp_intfc_enable_disable> + { + public: + /** + * Constructor + */ + unbind_cmd(HW::item& item, const handle_t& itf); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const unbind_cmd& i) const; + + private: + /** + * Reference to the HW::item of the interface to unbind + */ + const handle_t& m_itf; + }; + +private: + /** + * Class definition for listeners to OM events + */ + class event_handler : public OM::listener, public inspect::command_handler + { + public: + event_handler(); + virtual ~event_handler() = default; + + /** + * Handle a populate event + */ + void handle_populate(const client_db::key_t& key); + + /** + * Handle a replay event + */ + void handle_replay(); + + /** + * Show the object in the Singular DB + */ + void show(std::ostream& os); + + /** + * Get the sortable Id of the listener + */ + dependency_t order() const; + }; + + /** + * event_handler to register with OM + */ + static event_handler m_evh; + + /** + * Enquue commonds to the VPP command Q for the update + */ + void update(const arp_proxy_binding& obj); + + /** + * Find or add LLDP binding to the OM + */ + static std::shared_ptr find_or_add( + const arp_proxy_binding& temp); + + /* + * It's the OM class that calls singular() + */ + friend class OM; + + /** + * It's the singular_db class that calls replay() + */ + friend class singular_db; + + /** + * Sweep/reap the object if still stale + */ + void sweep(void); + + /** + * replay the object to create it in hardware + */ + void replay(void); + + /** + * A reference counting pointer to the interface on which LLDP config + * resides. By holding the reference here, we can guarantee that + * this object will outlive the interface + */ + const std::shared_ptr m_itf; + + /** + * A reference counting pointer to the prxy config. + */ + const std::shared_ptr m_arp_proxy_cfg; + + /** + * HW configuration for the binding. The bool representing the + * do/don't bind. + */ + HW::item m_binding; + + /** + * A map of all ArpProxy bindings keyed against the interface. + */ + static singular_db m_db; +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/arp_proxy_binding_cmds.cpp b/src/vpp-api/vom/arp_proxy_binding_cmds.cpp new file mode 100644 index 00000000000..d7f31883885 --- /dev/null +++ b/src/vpp-api/vom/arp_proxy_binding_cmds.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/arp_proxy_binding.hpp" + +namespace VOM { + +arp_proxy_binding::bind_cmd::bind_cmd(HW::item& item, const handle_t& itf) + : rpc_cmd(item) + , m_itf(itf) +{ +} + +bool +arp_proxy_binding::bind_cmd::operator==(const bind_cmd& other) const +{ + return (m_itf == other.m_itf); +} + +rc_t +arp_proxy_binding::bind_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_itf.value(); + payload.enable_disable = 1; + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return rc_t::OK; +} + +std::string +arp_proxy_binding::bind_cmd::to_string() const +{ + std::ostringstream s; + s << "ARP-proxy-bind: " << m_hw_item.to_string() + << " itf:" << m_itf.to_string(); + + return (s.str()); +} + +arp_proxy_binding::unbind_cmd::unbind_cmd(HW::item& item, + const handle_t& itf) + : rpc_cmd(item) + , m_itf(itf) +{ +} + +bool +arp_proxy_binding::unbind_cmd::operator==(const unbind_cmd& other) const +{ + return (m_itf == other.m_itf); +} + +rc_t +arp_proxy_binding::unbind_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_itf.value(); + payload.enable_disable = 0; + + VAPI_CALL(req.execute()); + + wait(); + m_hw_item.set(rc_t::NOOP); + + return rc_t::OK; +} + +std::string +arp_proxy_binding::unbind_cmd::to_string() const +{ + std::ostringstream s; + s << "ARP-proxy-unbind: " << m_hw_item.to_string() + << " itf:" << m_itf.to_string(); + + return (s.str()); +} +} +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/arp_proxy_config.cpp b/src/vpp-api/vom/arp_proxy_config.cpp new file mode 100644 index 00000000000..c07eef3c6c6 --- /dev/null +++ b/src/vpp-api/vom/arp_proxy_config.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/arp_proxy_config.hpp" +#include "vom/cmd.hpp" + +namespace VOM { +/** + * A DB of all LLDP configs + */ +singular_db arp_proxy_config::m_db; + +arp_proxy_config::event_handler arp_proxy_config::m_evh; + +arp_proxy_config::arp_proxy_config(const boost::asio::ip::address_v4& low, + const boost::asio::ip::address_v4& high) + : m_low(low) + , m_high(high) + , m_config(true) +{ +} + +arp_proxy_config::arp_proxy_config(const arp_proxy_config& o) + : m_low(o.m_low) + , m_high(o.m_high) + , m_config(o.m_config) +{ +} + +arp_proxy_config::~arp_proxy_config() +{ + sweep(); + + // not in the DB anymore. + m_db.release(std::make_pair(m_low, m_high), this); +} + +void +arp_proxy_config::sweep() +{ + if (m_config) { + HW::enqueue(new unconfig_cmd(m_config, m_low, m_high)); + } + HW::write(); +} + +void +arp_proxy_config::dump(std::ostream& os) +{ + m_db.dump(os); +} + +void +arp_proxy_config::replay() +{ + if (m_config) { + HW::enqueue(new config_cmd(m_config, m_low, m_high)); + } +} + +std::string +arp_proxy_config::to_string() const +{ + std::ostringstream s; + s << "ARP-proxy:" + << " low:" << m_low.to_string() << " high:" << m_high.to_string(); + + return (s.str()); +} + +void +arp_proxy_config::update(const arp_proxy_config& desired) +{ + if (!m_config) { + HW::enqueue(new config_cmd(m_config, m_low, m_high)); + } +} + +std::shared_ptr +arp_proxy_config::find_or_add(const arp_proxy_config& temp) +{ + return (m_db.find_or_add(std::make_pair(temp.m_low, temp.m_high), temp)); +} + +std::shared_ptr +arp_proxy_config::singular() const +{ + return find_or_add(*this); +} + +arp_proxy_config::event_handler::event_handler() +{ + OM::register_listener(this); + inspect::register_handler({ "arp-proxy" }, "ARP Proxy configurations", this); +} + +void +arp_proxy_config::event_handler::handle_replay() +{ + m_db.replay(); +} + +void +arp_proxy_config::event_handler::handle_populate(const client_db::key_t& key) +{ + // VPP provides no dump for ARP proxy. +} + +dependency_t +arp_proxy_config::event_handler::order() const +{ + return (dependency_t::GLOBAL); +} + +void +arp_proxy_config::event_handler::show(std::ostream& os) +{ + m_db.dump(os); +} + +std::ostream& +operator<<(std::ostream& os, const arp_proxy_config::key_t& key) +{ + os << "[" << key.first << ", " << key.second << "]"; + + return (os); +} +} +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/arp_proxy_config.hpp b/src/vpp-api/vom/arp_proxy_config.hpp new file mode 100644 index 00000000000..78fc81a894a --- /dev/null +++ b/src/vpp-api/vom/arp_proxy_config.hpp @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_ARP_PROXY_CONFIG_H__ +#define __VOM_ARP_PROXY_CONFIG_H__ + +#include "vom/dump_cmd.hpp" +#include "vom/hw.hpp" +#include "vom/inspect.hpp" +#include "vom/object_base.hpp" +#include "vom/om.hpp" +#include "vom/rpc_cmd.hpp" +#include "vom/singular_db.hpp" + +#include + +namespace VOM { +/** + * A representation of LLDP client configuration on an interface + */ +class arp_proxy_config : public object_base +{ +public: + /** + * Key type + */ + typedef std::pair + key_t; + + /** + * Construct a new object matching the desried state + */ + arp_proxy_config(const boost::asio::ip::address_v4& low, + const boost::asio::ip::address_v4& high); + + /** + * Copy Constructor + */ + arp_proxy_config(const arp_proxy_config& o); + + /** + * Destructor + */ + ~arp_proxy_config(); + + /** + * Return the 'singular' of the LLDP config that matches this object + */ + std::shared_ptr singular() const; + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Dump all LLDP configs into the stream provided + */ + static void dump(std::ostream& os); + + /** + * A command class that adds the ARP Proxy config + */ + class config_cmd + : public rpc_cmd, rc_t, vapi::Proxy_arp_add_del> + { + public: + /** + * Constructor + */ + config_cmd(HW::item& item, + const boost::asio::ip::address_v4& lo, + const boost::asio::ip::address_v4& high); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const config_cmd& i) const; + + private: + /** + * Address range + */ + const boost::asio::ip::address_v4 m_low; + const boost::asio::ip::address_v4 m_high; + }; + + /** + * A cmd class that Unconfigs ArpProxy Config from an interface + */ + class unconfig_cmd + : public rpc_cmd, rc_t, vapi::Proxy_arp_add_del> + { + public: + /** + * Constructor + */ + unconfig_cmd(HW::item& item, + const boost::asio::ip::address_v4& lo, + const boost::asio::ip::address_v4& hig); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const unconfig_cmd& i) const; + + private: + /** + * Address range + */ + const boost::asio::ip::address_v4 m_low; + const boost::asio::ip::address_v4 m_high; + }; + +private: + /** + * Class definition for listeners to OM events + */ + class event_handler : public OM::listener, public inspect::command_handler + { + public: + event_handler(); + virtual ~event_handler() = default; + + /** + * Handle a populate event + */ + void handle_populate(const client_db::key_t& key); + + /** + * Handle a replay event + */ + void handle_replay(); + + /** + * Show the object in the Singular DB + */ + void show(std::ostream& os); + + /** + * Get the sortable Id of the listener + */ + dependency_t order() const; + }; + + /** + * event_handler to register with OM + */ + static event_handler m_evh; + + /** + * Enquue commonds to the VPP command Q for the update + */ + void update(const arp_proxy_config& obj); + + /** + * Find or add LLDP config to the OM + */ + static std::shared_ptr find_or_add( + const arp_proxy_config& temp); + + /* + * It's the OM class that calls singular() + */ + friend class OM; + + /** + * It's the singular_db class that calls replay() + */ + friend class singular_db; + + /** + * Sweep/reap the object if still stale + */ + void sweep(void); + + /** + * replay the object to create it in hardware + */ + void replay(void); + + /** + * Address range + */ + const boost::asio::ip::address_v4 m_low; + const boost::asio::ip::address_v4 m_high; + + /** + * A map of all ArpProxy configs keyed against the interface. + */ + static singular_db m_db; + + /** + * HW configuration for the config. The bool representing the + * do/don't configured/unconfigured. + */ + HW::item m_config; +}; + +std::ostream& operator<<(std::ostream& os, const arp_proxy_config::key_t& key); +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/arp_proxy_config_cmds.cpp b/src/vpp-api/vom/arp_proxy_config_cmds.cpp new file mode 100644 index 00000000000..0a546d3c1b0 --- /dev/null +++ b/src/vpp-api/vom/arp_proxy_config_cmds.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/arp_proxy_config.hpp" + +#include + +namespace VOM { +arp_proxy_config::config_cmd::config_cmd( + HW::item& item, + const boost::asio::ip::address_v4& low, + const boost::asio::ip::address_v4& high) + : rpc_cmd(item) + , m_low(low) + , m_high(high) +{ +} + +bool +arp_proxy_config::config_cmd::operator==(const config_cmd& o) const +{ + return ((m_low == o.m_low) && (m_high == o.m_high)); +} + +rc_t +arp_proxy_config::config_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.is_add = 1; + + std::copy_n(std::begin(m_low.to_bytes()), m_low.to_bytes().size(), + payload.low_address); + std::copy_n(std::begin(m_high.to_bytes()), m_high.to_bytes().size(), + payload.hi_address); + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return (rc_t::OK); +} + +std::string +arp_proxy_config::config_cmd::to_string() const +{ + std::ostringstream s; + s << "ARP-proxy-config: " << m_hw_item.to_string() + << " low:" << m_low.to_string() << " high:" << m_high.to_string(); + + return (s.str()); +} + +arp_proxy_config::unconfig_cmd::unconfig_cmd( + HW::item& item, + const boost::asio::ip::address_v4& low, + const boost::asio::ip::address_v4& high) + : rpc_cmd(item) + , m_low(low) + , m_high(high) +{ +} + +bool +arp_proxy_config::unconfig_cmd::operator==(const unconfig_cmd& o) const +{ + return ((m_low == o.m_low) && (m_high == o.m_high)); +} + +rc_t +arp_proxy_config::unconfig_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.is_add = 0; + + std::copy_n(std::begin(m_low.to_bytes()), m_low.to_bytes().size(), + payload.low_address); + std::copy_n(std::begin(m_high.to_bytes()), m_high.to_bytes().size(), + payload.hi_address); + + VAPI_CALL(req.execute()); + + wait(); + m_hw_item.set(rc_t::NOOP); + + return (rc_t::OK); +} + +std::string +arp_proxy_config::unconfig_cmd::to_string() const +{ + std::ostringstream s; + s << "ARP-proxy-unconfig: " << m_hw_item.to_string() + << " low:" << m_low.to_string() << " high:" << m_high.to_string(); + + return (s.str()); +} +} +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/bridge_domain.cpp b/src/vpp-api/vom/bridge_domain.cpp new file mode 100644 index 00000000000..5831b2ae224 --- /dev/null +++ b/src/vpp-api/vom/bridge_domain.cpp @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/bridge_domain.hpp" +#include "vom/cmd.hpp" +#include "vom/interface.hpp" +#include "vom/l2_binding.hpp" +#include "vom/logger.hpp" + +namespace VOM { +/** + * A DB of al the interfaces, key on the name + */ +singular_db bridge_domain::m_db; + +bridge_domain::event_handler bridge_domain::m_evh; + +/** + * Construct a new object matching the desried state + */ +bridge_domain::bridge_domain(uint32_t id) + : m_id(id) +{ +} + +bridge_domain::bridge_domain(const bridge_domain& o) + : m_id(o.m_id) +{ +} + +uint32_t +bridge_domain::id() const +{ + return (m_id.data()); +} + +void +bridge_domain::sweep() +{ + if (rc_t::OK == m_id.rc()) { + HW::enqueue(new delete_cmd(m_id)); + } + HW::write(); +} + +void +bridge_domain::replay() +{ + if (rc_t::OK == m_id.rc()) { + HW::enqueue(new create_cmd(m_id)); + } +} + +bridge_domain::~bridge_domain() +{ + sweep(); + + // not in the DB anymore. + m_db.release(m_id.data(), this); +} + +std::string +bridge_domain::to_string() const +{ + std::ostringstream s; + s << "bridge-domain:[" << m_id.to_string() << "]"; + + return (s.str()); +} + +std::shared_ptr +bridge_domain::find(uint32_t id) +{ + /* + * Loop throught the entire map looking for matching interface. + * not the most efficient algorithm, but it will do for now. The + * number of L3 configs is low and this is only called during bootup + */ + std::shared_ptr bd; + + auto it = m_db.cbegin(); + + while (it != m_db.cend()) { + /* + * The key in the DB is a pair of the interface's name and prefix. + * If the keys match, save the L3-config + */ + auto key = it->first; + + if (id == key) { + bd = it->second.lock(); + break; + } + + ++it; + } + + return (bd); +} + +void +bridge_domain::update(const bridge_domain& desired) +{ + /* + * the desired state is always that the interface should be created + */ + if (rc_t::OK != m_id.rc()) { + HW::enqueue(new create_cmd(m_id)); + } +} + +std::shared_ptr +bridge_domain::find_or_add(const bridge_domain& temp) +{ + return (m_db.find_or_add(temp.m_id.data(), temp)); +} + +std::shared_ptr +bridge_domain::singular() const +{ + return find_or_add(*this); +} + +void +bridge_domain::dump(std::ostream& os) +{ + m_db.dump(os); +} + +void +bridge_domain::event_handler::handle_populate(const client_db::key_t& key) +{ + /* + * dump VPP Bridge domains + */ + std::shared_ptr cmd(new bridge_domain::dump_cmd()); + + HW::enqueue(cmd); + HW::write(); + + for (auto& record : *cmd) { + auto& payload = record.get_payload(); + + bridge_domain bd(payload.bd_id); + + VOM_LOG(log_level_t::DEBUG) << "dump: " << bd.to_string(); + + /* + * Write each of the discovered interfaces into the OM, + * but disable the HW Command q whilst we do, so that no + * commands are sent to VPP + */ + OM::commit(key, bd); + + /** + * For each interface in the BD construct an l2_binding + */ + for (unsigned int ii = 0; ii < payload.n_sw_ifs; ii++) { + std::shared_ptr itf = + interface::find(payload.sw_if_details[ii].sw_if_index); + l2_binding l2(*itf, bd); + OM::commit(key, l2); + } + } +} + +bridge_domain::event_handler::event_handler() +{ + OM::register_listener(this); + inspect::register_handler({ "bd", "bridge" }, "Bridge Domains", this); +} + +void +bridge_domain::event_handler::handle_replay() +{ + m_db.replay(); +} + +dependency_t +bridge_domain::event_handler::order() const +{ + return (dependency_t::TABLE); +} + +void +bridge_domain::event_handler::show(std::ostream& os) +{ + m_db.dump(os); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/bridge_domain.hpp b/src/vpp-api/vom/bridge_domain.hpp new file mode 100644 index 00000000000..70371fa0f46 --- /dev/null +++ b/src/vpp-api/vom/bridge_domain.hpp @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_BRIDGE_DOMAIN_H__ +#define __VOM_BRIDGE_DOMAIN_H__ + +#include "vom/dump_cmd.hpp" +#include "vom/enum_base.hpp" +#include "vom/hw.hpp" +#include "vom/inspect.hpp" +#include "vom/object_base.hpp" +#include "vom/om.hpp" +#include "vom/rpc_cmd.hpp" +#include "vom/singular_db.hpp" + +#include + +namespace VOM { +/** + * A base class for all object_base in the VPP object_base-Model. + * provides the abstract interface. + */ +class bridge_domain : public object_base +{ +public: + /** + * The value of the defaultbridge domain + */ + const static uint32_t DEFAULT_TABLE = 0; + + /** + * Construct a new object matching the desried state + */ + bridge_domain(uint32_t id); + /** + * Copy Constructor + */ + bridge_domain(const bridge_domain& o); + /** + * Destructor + */ + ~bridge_domain(); + + /** + * Return the matchin 'singular' instance of the bridge-domain + */ + std::shared_ptr singular() const; + + /** + * convert to string format for debug purposes + */ + std::string to_string(void) const; + + /** + * Return VPP's handle for this obejct + */ + uint32_t id() const; + + /** + * Static function to find the bridge_domain in the model + */ + static std::shared_ptr find(uint32_t id); + + /** + * Dump all bridge-doamin into the stream provided + */ + static void dump(std::ostream& os); + + /** + * A command class that creates an Bridge-Domain + */ + class create_cmd + : public rpc_cmd, rc_t, vapi::Bridge_domain_add_del> + { + public: + /** + * Constructor + */ + create_cmd(HW::item& item); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const create_cmd& i) const; + }; + + /** + * A cmd class that Delete an Bridge-Domain + */ + class delete_cmd + : public rpc_cmd, rc_t, vapi::Bridge_domain_add_del> + { + public: + /** + * Constructor + */ + delete_cmd(HW::item& item); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const delete_cmd& i) const; + }; + + /** + * A cmd class that Dumps all the IPv4 L3 configs + */ + class dump_cmd : public VOM::dump_cmd + { + public: + /** + * Constructor + */ + dump_cmd(); + dump_cmd(const dump_cmd& d); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const dump_cmd& i) const; + + private: + /** + * HW reutrn code + */ + HW::item item; + }; + +private: + /** + * Class definition for listeners to OM events + */ + class event_handler : public OM::listener, public inspect::command_handler + { + public: + event_handler(); + virtual ~event_handler() = default; + + /** + * Handle a populate event + */ + void handle_populate(const client_db::key_t& key); + + /** + * Handle a replay event + */ + void handle_replay(); + + /** + * Show the object in the Singular DB + */ + void show(std::ostream& os); + + /** + * Get the sortable Id of the listener + */ + dependency_t order() const; + }; + + /** + * Instance of the event handler to register with OM + */ + static event_handler m_evh; + + /** + * Commit the acculmulated changes into VPP. i.e. to a 'HW" write. + */ + void update(const bridge_domain& obj); + + /** + * Find or add an singular of a Bridge-Domain in the object_base Model + */ + static std::shared_ptr find_or_add(const bridge_domain& temp); + + /* + * It's the OM class that calls singular() + */ + friend class OM; + + /** + * It's the singular_db class that calls replay() + */ + friend class singular_db; + + /** + * Sweep/reap the object if still stale + */ + void sweep(void); + + /** + * replay the object to create it in hardware + */ + void replay(void); + + /** + * The ID we assign to this BD and the HW result in VPP + */ + HW::item m_id; + + /** + * A map of all interfaces key against the interface's name + */ + static singular_db m_db; +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/bridge_domain_arp_entry.cpp b/src/vpp-api/vom/bridge_domain_arp_entry.cpp new file mode 100644 index 00000000000..e2bf6d5e97c --- /dev/null +++ b/src/vpp-api/vom/bridge_domain_arp_entry.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/bridge_domain_arp_entry.hpp" + +namespace VOM { + +singular_db + bridge_domain_arp_entry::m_db; + +bridge_domain_arp_entry::event_handler bridge_domain_arp_entry::m_evh; + +bridge_domain_arp_entry::bridge_domain_arp_entry( + const bridge_domain& bd, + const mac_address_t& mac, + const boost::asio::ip::address& ip_addr) + : m_hw(false) + , m_bd(bd.singular()) + , m_mac(mac) + , m_ip_addr(ip_addr) +{ +} + +bridge_domain_arp_entry::bridge_domain_arp_entry( + const mac_address_t& mac, + const boost::asio::ip::address& ip_addr) + : m_hw(false) + , m_bd(nullptr) + , m_mac(mac) + , m_ip_addr(ip_addr) +{ + /* + * the route goes in the default table + */ + bridge_domain bd(bridge_domain::DEFAULT_TABLE); + + m_bd = bd.singular(); +} + +bridge_domain_arp_entry::bridge_domain_arp_entry( + const bridge_domain_arp_entry& bde) + : m_hw(bde.m_hw) + , m_bd(bde.m_bd) + , m_mac(bde.m_mac) + , m_ip_addr(bde.m_ip_addr) +{ +} + +bridge_domain_arp_entry::~bridge_domain_arp_entry() +{ + sweep(); + + // not in the DB anymore. + m_db.release(std::make_tuple(m_bd->id(), m_mac, m_ip_addr), this); +} + +void +bridge_domain_arp_entry::sweep() +{ + if (m_hw) { + HW::enqueue(new delete_cmd(m_hw, m_bd->id(), m_mac, m_ip_addr)); + } + HW::write(); +} + +void +bridge_domain_arp_entry::replay() +{ + if (m_hw) { + HW::enqueue(new create_cmd(m_hw, m_bd->id(), m_mac, m_ip_addr)); + } +} + +std::string +bridge_domain_arp_entry::to_string() const +{ + std::ostringstream s; + s << "bridge-domain-arp-entry:[" << m_bd->to_string() << ", " + << m_mac.to_string() << ", " << m_ip_addr.to_string() << "]"; + + return (s.str()); +} + +void +bridge_domain_arp_entry::update(const bridge_domain_arp_entry& r) +{ + /* + * create the table if it is not yet created + */ + if (rc_t::OK != m_hw.rc()) { + HW::enqueue(new create_cmd(m_hw, m_bd->id(), m_mac, m_ip_addr)); + } +} + +std::shared_ptr +bridge_domain_arp_entry::find_or_add(const bridge_domain_arp_entry& temp) +{ + return (m_db.find_or_add( + std::make_tuple(temp.m_bd->id(), temp.m_mac, temp.m_ip_addr), temp)); +} + +std::shared_ptr +bridge_domain_arp_entry::singular() const +{ + return find_or_add(*this); +} + +void +bridge_domain_arp_entry::dump(std::ostream& os) +{ + m_db.dump(os); +} + +std::ostream& +operator<<(std::ostream& os, const bridge_domain_arp_entry::key_t& key) +{ + os << "[" << std::get<0>(key) << ", " << std::get<1>(key) << ", " + << std::get<2>(key) << "]"; + + return (os); +} + +bridge_domain_arp_entry::event_handler::event_handler() +{ + OM::register_listener(this); + inspect::register_handler({ "bd-arp" }, + "bridge domain ARP termination entries", this); +} + +void +bridge_domain_arp_entry::event_handler::handle_replay() +{ + m_db.replay(); +} + +void +bridge_domain_arp_entry::event_handler::handle_populate( + const client_db::key_t& key) +{ +} + +dependency_t +bridge_domain_arp_entry::event_handler::order() const +{ + return (dependency_t::ENTRY); +} + +void +bridge_domain_arp_entry::event_handler::show(std::ostream& os) +{ + m_db.dump(os); +} +} +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/bridge_domain_arp_entry.hpp b/src/vpp-api/vom/bridge_domain_arp_entry.hpp new file mode 100644 index 00000000000..181afe6c8a5 --- /dev/null +++ b/src/vpp-api/vom/bridge_domain_arp_entry.hpp @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_BRIDGE_DOMAIN_ARP_ENTRY_H__ +#define __VOM_BRIDGE_DOMAIN_ARP_ENTRY_H__ + +#include "vom/bridge_domain.hpp" +#include "vom/interface.hpp" +#include "vom/singular_db.hpp" +#include "vom/types.hpp" + +#include + +namespace VOM { +/** + * A entry in the ARP termination table of a Bridge Domain + */ +class bridge_domain_arp_entry : public object_base +{ +public: + /** + * The key for a bridge_domain ARP entry; + * the BD, IP address and MAC address + */ + typedef std::tuple key_t; + + /** + * Construct a bridge_domain in the given bridge domain + */ + bridge_domain_arp_entry(const bridge_domain& bd, + const mac_address_t& mac, + const boost::asio::ip::address& ip_addr); + + /** + * Construct a bridge_domain in the default table + */ + bridge_domain_arp_entry(const mac_address_t& mac, + const boost::asio::ip::address& ip_addr); + + /** + * Copy Construct + */ + bridge_domain_arp_entry(const bridge_domain_arp_entry& r); + + /** + * Destructor + */ + ~bridge_domain_arp_entry(); + + /** + * Return the matching 'singular instance' + */ + std::shared_ptr singular() const; + + /** + * Find the instnace of the bridge_domain domain in the OM + */ + static std::shared_ptr find( + const bridge_domain_arp_entry& temp); + + /** + * Dump all bridge_domain-doamin into the stream provided + */ + static void dump(std::ostream& os); + + /** + * replay the object to create it in hardware + */ + void replay(void); + + /** + * Convert to string for debugging + */ + std::string to_string() const; + + /** + * A command class that creates or updates the bridge domain ARP Entry + */ + class create_cmd + : public rpc_cmd, rc_t, vapi::Bd_ip_mac_add_del> + { + public: + /** + * Constructor + */ + create_cmd(HW::item& item, + uint32_t id, + const mac_address_t& mac, + const boost::asio::ip::address& ip_addr); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const create_cmd& i) const; + + private: + uint32_t m_bd; + mac_address_t m_mac; + boost::asio::ip::address m_ip_addr; + }; + + /** + * A cmd class that deletes a bridge domain ARP entry + */ + class delete_cmd + : public rpc_cmd, rc_t, vapi::Bd_ip_mac_add_del> + { + public: + /** + * Constructor + */ + delete_cmd(HW::item& item, + uint32_t id, + const mac_address_t& mac, + const boost::asio::ip::address& ip_addr); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const delete_cmd& i) const; + + private: + uint32_t m_bd; + mac_address_t m_mac; + boost::asio::ip::address m_ip_addr; + }; + +private: + /** + * Class definition for listeners to OM events + */ + class event_handler : public OM::listener, public inspect::command_handler + { + public: + event_handler(); + virtual ~event_handler() = default; + + /** + * Handle a populate event + */ + void handle_populate(const client_db::key_t& key); + + /** + * Handle a replay event + */ + void handle_replay(); + + /** + * Show the object in the Singular DB + */ + void show(std::ostream& os); + + /** + * Get the sortable Id of the listener + */ + dependency_t order() const; + }; + + /** + * event_handler to register with OM + */ + static event_handler m_evh; + + /** + * Commit the acculmulated changes into VPP. i.e. to a 'HW" write. + */ + void update(const bridge_domain_arp_entry& obj); + + /** + * Find or add the instnace of the bridge_domain domain in the OM + */ + static std::shared_ptr find_or_add( + const bridge_domain_arp_entry& temp); + + /* + * It's the VPPHW class that updates the objects in HW + */ + friend class OM; + + /** + * It's the singular_db class that calls replay() + */ + friend class singular_db; + + /** + * Sweep/reap the object if still stale + */ + void sweep(void); + + /** + * HW configuration for the result of creating the bridge_domain + */ + HW::item m_hw; + + /** + * The bridge_domain domain the bridge_domain is in. + */ + std::shared_ptr m_bd; + + /** + * The mac to match + */ + mac_address_t m_mac; + + /** + * The IP address + */ + boost::asio::ip::address m_ip_addr; + + /** + * A map of all bridge_domains + */ + static singular_db m_db; +}; + +std::ostream& operator<<(std::ostream& os, + const bridge_domain_arp_entry::key_t& key); +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/bridge_domain_arp_entry_cmds.cpp b/src/vpp-api/vom/bridge_domain_arp_entry_cmds.cpp new file mode 100644 index 00000000000..3ea63adb09f --- /dev/null +++ b/src/vpp-api/vom/bridge_domain_arp_entry_cmds.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/bridge_domain_arp_entry.hpp" + +namespace VOM { + +bridge_domain_arp_entry::create_cmd::create_cmd( + HW::item& item, + uint32_t bd, + const mac_address_t& mac, + const boost::asio::ip::address& ip_addr) + : rpc_cmd(item) + , m_bd(bd) + , m_mac(mac) + , m_ip_addr(ip_addr) +{ +} + +bool +bridge_domain_arp_entry::create_cmd::operator==(const create_cmd& other) const +{ + return ((m_mac == other.m_mac) && (m_ip_addr == other.m_ip_addr) && + (m_bd == other.m_bd)); +} + +rc_t +bridge_domain_arp_entry::create_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.bd_id = m_bd; + payload.is_add = 1; + m_mac.to_bytes(payload.mac_address, 6); + to_bytes(m_ip_addr, &payload.is_ipv6, payload.ip_address); + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return rc_t::OK; +} + +std::string +bridge_domain_arp_entry::create_cmd::to_string() const +{ + std::ostringstream s; + s << "bridge-domain-arp-entry-create: " << m_hw_item.to_string() + << " bd:" << m_bd << " mac:" << m_mac.to_string() + << " ip:" << m_ip_addr.to_string(); + + return (s.str()); +} + +bridge_domain_arp_entry::delete_cmd::delete_cmd( + HW::item& item, + uint32_t bd, + const mac_address_t& mac, + const boost::asio::ip::address& ip_addr) + : rpc_cmd(item) + , m_bd(bd) + , m_mac(mac) + , m_ip_addr(ip_addr) +{ +} + +bool +bridge_domain_arp_entry::delete_cmd::operator==(const delete_cmd& other) const +{ + return ((m_mac == other.m_mac) && (m_ip_addr == other.m_ip_addr) && + (m_bd == other.m_bd)); +} + +rc_t +bridge_domain_arp_entry::delete_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.bd_id = m_bd; + payload.is_add = 0; + m_mac.to_bytes(payload.mac_address, 6); + to_bytes(m_ip_addr, &payload.is_ipv6, payload.ip_address); + + VAPI_CALL(req.execute()); + + wait(); + m_hw_item.set(rc_t::NOOP); + + return rc_t::OK; +} + +std::string +bridge_domain_arp_entry::delete_cmd::to_string() const +{ + std::ostringstream s; + s << "bridge-domain-arp-entry-delete: " << m_hw_item.to_string() + << " bd:" << m_bd << " mac:" << m_mac.to_string() + << " ip:" << m_ip_addr.to_string(); + + return (s.str()); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/bridge_domain_cmds.cpp b/src/vpp-api/vom/bridge_domain_cmds.cpp new file mode 100644 index 00000000000..91173393cce --- /dev/null +++ b/src/vpp-api/vom/bridge_domain_cmds.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/bridge_domain.hpp" +#include "vom/cmd.hpp" + +DEFINE_VAPI_MSG_IDS_L2_API_JSON; + +namespace VOM { +bridge_domain::create_cmd::create_cmd(HW::item& item) + : rpc_cmd(item) +{ +} + +bool +bridge_domain::create_cmd::operator==(const create_cmd& other) const +{ + return (m_hw_item.data() == other.m_hw_item.data()); +} + +rc_t +bridge_domain::create_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.bd_id = m_hw_item.data(); + payload.flood = 1; + payload.uu_flood = 1; + payload.forward = 1; + payload.learn = 1; + payload.arp_term = 1; + payload.mac_age = 0; + payload.is_add = 1; + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return (rc_t::OK); +} + +std::string +bridge_domain::create_cmd::to_string() const +{ + std::ostringstream s; + s << "bridge-domain-create: " << m_hw_item.to_string(); + + return (s.str()); +} + +bridge_domain::delete_cmd::delete_cmd(HW::item& item) + : rpc_cmd(item) +{ +} + +bool +bridge_domain::delete_cmd::operator==(const delete_cmd& other) const +{ + return (m_hw_item == other.m_hw_item); +} + +rc_t +bridge_domain::delete_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.bd_id = m_hw_item.data(); + payload.is_add = 0; + + VAPI_CALL(req.execute()); + + wait(); + m_hw_item.set(rc_t::NOOP); + + return (rc_t::OK); +} + +std::string +bridge_domain::delete_cmd::to_string() const +{ + std::ostringstream s; + s << "bridge-domain-delete: " << m_hw_item.to_string(); + + return (s.str()); +} + +bridge_domain::dump_cmd::dump_cmd() +{ +} + +bool +bridge_domain::dump_cmd::operator==(const dump_cmd& other) const +{ + return (true); +} + +rc_t +bridge_domain::dump_cmd::issue(connection& con) +{ + m_dump.reset(new msg_t(con.ctx(), std::ref(*this))); + + auto& payload = m_dump->get_request().get_payload(); + payload.bd_id = ~0; + + VAPI_CALL(m_dump->execute()); + + wait(); + + return rc_t::OK; +} + +std::string +bridge_domain::dump_cmd::to_string() const +{ + return ("bridge-domain-dump"); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/bridge_domain_entry.cpp b/src/vpp-api/vom/bridge_domain_entry.cpp new file mode 100644 index 00000000000..4041e315352 --- /dev/null +++ b/src/vpp-api/vom/bridge_domain_entry.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/bridge_domain_entry.hpp" + +namespace VOM { +singular_db + bridge_domain_entry::m_db; + +bridge_domain_entry::event_handler bridge_domain_entry::m_evh; + +bridge_domain_entry::bridge_domain_entry(const bridge_domain& bd, + const mac_address_t& mac, + const interface& tx_itf) + : m_hw(false) + , m_mac(mac) + , m_bd(bd.singular()) + , m_tx_itf(tx_itf.singular()) +{ +} + +bridge_domain_entry::bridge_domain_entry(const mac_address_t& mac, + const interface& tx_itf) + : m_hw(false) + , m_mac(mac) + , m_bd(nullptr) + , m_tx_itf(tx_itf.singular()) +{ + /* + * the route goes in the default table + */ + bridge_domain bd(bridge_domain::DEFAULT_TABLE); + + m_bd = bd.singular(); +} + +bridge_domain_entry::bridge_domain_entry(const bridge_domain_entry& bde) + : m_hw(bde.m_hw) + , m_mac(bde.m_mac) + , m_bd(bde.m_bd) + , m_tx_itf(bde.m_tx_itf) +{ +} + +bridge_domain_entry::~bridge_domain_entry() +{ + sweep(); + + // not in the DB anymore. + m_db.release(std::make_pair(m_bd->id(), m_mac), this); +} + +void +bridge_domain_entry::sweep() +{ + if (m_hw) { + HW::enqueue(new delete_cmd(m_hw, m_mac, m_bd->id())); + } + HW::write(); +} + +void +bridge_domain_entry::replay() +{ + if (m_hw) { + HW::enqueue(new create_cmd(m_hw, m_mac, m_bd->id(), m_tx_itf->handle())); + } +} +std::string +bridge_domain_entry::to_string() const +{ + std::ostringstream s; + s << "bridge-domain-entry:[" << m_bd->to_string() << ", " << m_mac.to_string() + << ", tx:" << m_tx_itf->name() << "]"; + + return (s.str()); +} + +void +bridge_domain_entry::update(const bridge_domain_entry& r) +{ + /* + * create the table if it is not yet created + */ + if (rc_t::OK != m_hw.rc()) { + HW::enqueue(new create_cmd(m_hw, m_mac, m_bd->id(), m_tx_itf->handle())); + } +} + +std::shared_ptr +bridge_domain_entry::find_or_add(const bridge_domain_entry& temp) +{ + return (m_db.find_or_add(std::make_pair(temp.m_bd->id(), temp.m_mac), temp)); +} + +std::shared_ptr +bridge_domain_entry::singular() const +{ + return find_or_add(*this); +} + +void +bridge_domain_entry::dump(std::ostream& os) +{ + m_db.dump(os); +} + +bridge_domain_entry::event_handler::event_handler() +{ + OM::register_listener(this); + inspect::register_handler({ "bd-entry" }, + "bridge domain entry configurations", this); +} + +void +bridge_domain_entry::event_handler::handle_replay() +{ + m_db.replay(); +} + +void +bridge_domain_entry::event_handler::handle_populate(const client_db::key_t& key) +{ + std::shared_ptr cmd( + new bridge_domain_entry::dump_cmd()); + + HW::enqueue(cmd); + HW::write(); + + for (auto& record : *cmd) { + auto& payload = record.get_payload(); + + std::shared_ptr itf = interface::find(payload.sw_if_index); + std::shared_ptr bd = bridge_domain::find(payload.bd_id); + mac_address_t mac(payload.mac); + bridge_domain_entry bd_e(*bd, mac, *itf); + + VOM_LOG(log_level_t::DEBUG) << "bd-entry-dump: " << bd->to_string() + << mac.to_string() << itf->to_string(); + + /* + * Write each of the discovered interfaces into the OM, + * but disable the HW Command q whilst we do, so that no + * commands are sent to VPP + */ + OM::commit(key, bd_e); + } +} + +dependency_t +bridge_domain_entry::event_handler::order() const +{ + return (dependency_t::ENTRY); +} + +void +bridge_domain_entry::event_handler::show(std::ostream& os) +{ + m_db.dump(os); +} + +std::ostream& +operator<<(std::ostream& os, const bridge_domain_entry::key_t& key) +{ + os << "[" << key.first << ", " << key.second.to_string() << "]"; + + return (os); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/bridge_domain_entry.hpp b/src/vpp-api/vom/bridge_domain_entry.hpp new file mode 100644 index 00000000000..073b2d37a1d --- /dev/null +++ b/src/vpp-api/vom/bridge_domain_entry.hpp @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_BRIDGE_DOMAIN_ENTRY_H__ +#define __VOM_BRIDGE_DOMAIN_ENTRY_H__ + +#include "vom/bridge_domain.hpp" +#include "vom/interface.hpp" +#include "vom/singular_db.hpp" + +#include + +namespace VOM { +/** + * A MAC forwarding entry in the bridge-domain/L2-FIB + */ +class bridge_domain_entry : public object_base +{ +public: + /** + * The key for a bridge_domain + */ + typedef std::pair key_t; + + /** + * Construct a bridge_domain in the given bridge domain + */ + bridge_domain_entry(const bridge_domain& bd, + const mac_address_t& mac, + const interface& tx_itf); + + /** + * Construct a bridge_domain in the default table + */ + bridge_domain_entry(const mac_address_t& mac, const interface& tx_itf); + + /** + * Copy Construct + */ + bridge_domain_entry(const bridge_domain_entry& r); + + /** + * Destructor + */ + ~bridge_domain_entry(); + + /** + * Return the matching 'singular instance' + */ + std::shared_ptr singular() const; + + /** + * Find the instnace of the bridge_domain domain in the OM + */ + static std::shared_ptr find( + const bridge_domain_entry& temp); + + /** + * Dump all bridge_domain-doamin into the stream provided + */ + static void dump(std::ostream& os); + + /** + * replay the object to create it in hardware + */ + void replay(void); + + /** + * Convert to string for debugging + */ + std::string to_string() const; + + /** + * A command class that creates or updates the bridge_domain + */ + class create_cmd : public rpc_cmd, rc_t, vapi::L2fib_add_del> + { + public: + /** + * Constructor + */ + create_cmd(HW::item& item, + const mac_address_t& mac, + uint32_t id, + handle_t tx_intf); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const create_cmd& i) const; + + private: + mac_address_t m_mac; + uint32_t m_bd; + handle_t m_tx_itf; + }; + + /** + * A cmd class that deletes a bridge_domain + */ + class delete_cmd : public rpc_cmd, rc_t, vapi::L2fib_add_del> + { + public: + /** + * Constructor + */ + delete_cmd(HW::item& item, const mac_address_t& mac, uint32_t id); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const delete_cmd& i) const; + + private: + mac_address_t m_mac; + uint32_t m_bd; + }; + + /** + * A cmd class that Dumps all the interface spans + */ + class dump_cmd : public VOM::dump_cmd + { + public: + /** + * Constructor + */ + dump_cmd(); + dump_cmd(const dump_cmd& d); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const dump_cmd& i) const; + + private: + /** + * HW reutrn code + */ + HW::item item; + }; + +private: + /** + * Class definition for listeners to OM events + */ + class event_handler : public OM::listener, public inspect::command_handler + { + public: + event_handler(); + virtual ~event_handler() = default; + + /** + * Handle a populate event + */ + void handle_populate(const client_db::key_t& key); + + /** + * Handle a replay event + */ + void handle_replay(); + + /** + * Show the object in the Singular DB + */ + void show(std::ostream& os); + + /** + * Get the sortable Id of the listener + */ + dependency_t order() const; + }; + + /** + * event_handler to register with OM + */ + static event_handler m_evh; + + /** + * Commit the acculmulated changes into VPP. i.e. to a 'HW" write. + */ + void update(const bridge_domain_entry& obj); + + /** + * Find or add the instnace of the bridge_domain domain in the OM + */ + static std::shared_ptr find_or_add( + const bridge_domain_entry& temp); + + /* + * It's the VPPHW class that updates the objects in HW + */ + friend class OM; + + /** + * It's the singular_db class that calls replay() + */ + friend class singular_db; + + /** + * Sweep/reap the object if still stale + */ + void sweep(void); + + /** + * HW configuration for the result of creating the bridge_domain + */ + HW::item m_hw; + + /** + * The mac to match + */ + mac_address_t m_mac; + + /** + * The bridge_domain domain the bridge_domain is in. + */ + std::shared_ptr m_bd; + + /** + * The set of paths + */ + std::shared_ptr m_tx_itf; + + /** + * A map of all bridge_domains + */ + static singular_db m_db; +}; + +std::ostream& operator<<(std::ostream& os, + const bridge_domain_entry::key_t& key); +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/bridge_domain_entry_cmds.cpp b/src/vpp-api/vom/bridge_domain_entry_cmds.cpp new file mode 100644 index 00000000000..ad0786faa01 --- /dev/null +++ b/src/vpp-api/vom/bridge_domain_entry_cmds.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/bridge_domain_entry.hpp" + +namespace VOM { +bridge_domain_entry::create_cmd::create_cmd(HW::item& item, + const mac_address_t& mac, + uint32_t bd, + handle_t tx_itf) + : rpc_cmd(item) + , m_mac(mac) + , m_bd(bd) + , m_tx_itf(tx_itf) +{ +} + +bool +bridge_domain_entry::create_cmd::operator==(const create_cmd& other) const +{ + return ((m_mac == other.m_mac) && (m_tx_itf == other.m_tx_itf) && + (m_bd == other.m_bd)); +} + +rc_t +bridge_domain_entry::create_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.bd_id = m_bd; + payload.is_add = 1; + payload.mac = m_mac.to_u64(); + payload.sw_if_index = m_tx_itf.value(); + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return rc_t::OK; +} + +std::string +bridge_domain_entry::create_cmd::to_string() const +{ + std::ostringstream s; + s << "bridge-domain-entry-create: " << m_hw_item.to_string() << " bd:" << m_bd + << " mac:" << m_mac.to_string() << " tx:" << m_tx_itf; + + return (s.str()); +} + +bridge_domain_entry::delete_cmd::delete_cmd(HW::item& item, + const mac_address_t& mac, + uint32_t bd) + : rpc_cmd(item) + , m_mac(mac) + , m_bd(bd) +{ +} + +bool +bridge_domain_entry::delete_cmd::operator==(const delete_cmd& other) const +{ + return ((m_mac == other.m_mac) && (m_bd == other.m_bd)); +} + +rc_t +bridge_domain_entry::delete_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.bd_id = m_bd; + payload.is_add = 1; + payload.mac = m_mac.to_u64(); + payload.sw_if_index = ~0; + + VAPI_CALL(req.execute()); + + wait(); + m_hw_item.set(rc_t::NOOP); + + return rc_t::OK; +} + +std::string +bridge_domain_entry::delete_cmd::to_string() const +{ + std::ostringstream s; + s << "bridge-domain-entry-delete: " << m_hw_item.to_string() << " bd:" << m_bd + << " mac:" << m_mac.to_string(); + + return (s.str()); +} + +bridge_domain_entry::dump_cmd::dump_cmd() +{ +} + +bool +bridge_domain_entry::dump_cmd::operator==(const dump_cmd& other) const +{ + return (true); +} + +rc_t +bridge_domain_entry::dump_cmd::issue(connection& con) +{ + m_dump.reset(new msg_t(con.ctx(), std::ref(*this))); + + auto& payload = m_dump->get_request().get_payload(); + payload.bd_id = ~0; + + VAPI_CALL(m_dump->execute()); + + wait(); + + return rc_t::OK; +} + +std::string +bridge_domain_entry::dump_cmd::to_string() const +{ + return ("bridge-domain-entry-dump"); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/client_db.cpp b/src/vpp-api/vom/client_db.cpp new file mode 100644 index 00000000000..fad2c13be91 --- /dev/null +++ b/src/vpp-api/vom/client_db.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/client_db.hpp" + +namespace VOM { +object_ref_list& +client_db::find(const client_db::key_t& k) +{ + return (m_objs[k]); +} + +void +client_db::flush(const client_db::key_t& k) +{ + m_objs.erase(m_objs.find(k)); +} + +void +client_db::dump(const key_t& key, std::ostream& os) +{ + object_ref_list& orlist = find(key); + + for (auto entry : orlist) { + os << " " << entry.obj()->to_string() << std::endl; + } +} + +void +client_db::dump(std::ostream& os) +{ + for (auto entry : m_objs) { + os << " key:[" << entry.first << "]" << std::endl; + } +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/client_db.hpp b/src/vpp-api/vom/client_db.hpp new file mode 100644 index 00000000000..34204c1d839 --- /dev/null +++ b/src/vpp-api/vom/client_db.hpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_KEY_DB_H__ +#define __VOM_KEY_DB_H__ + +#include +#include + +#include "vom/object_base.hpp" + +namespace VOM { +/** + * A convenitent typedef for set of objects owned. + * A set of shared pointers. This is how the reference counting + * of an object in the model it managed. Once all these shared ptr + * and hence references are gone, the object is deleted and any state + * in VPP is removed. + */ +typedef std::set object_ref_list; + +/** + * A DB storing the objects that each owner/key owns. + * Each object is reference counter by each key that owns it. When + * no more references exist the object is destroyed. + */ +class client_db +{ +public: + /** + * In the opflex world each entity is known by a URI which can be + * converted + * into a string. We use the string type, since it allows us to keep + * this VPP + * specific code independent of opflex types. I might consider making + * this + * a template parameter one day... + */ + typedef const std::string key_t; + + /** + * Find the objects owned by the key + */ + object_ref_list& find(const key_t& k); + + /** + * flush, i.e. un-reference, all objects owned by the key + */ + void flush(const key_t& k); + + /** + * Print each of the object in the DB into the stream provided + */ + void dump(const key_t& key, std::ostream& os); + + /** + * Print each KEY + */ + void dump(std::ostream& os); + +private: + /** + * A map of keys versus the object they reference + */ + std::map m_objs; +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/cmd.cpp b/src/vpp-api/vom/cmd.cpp new file mode 100644 index 00000000000..5623507a63f --- /dev/null +++ b/src/vpp-api/vom/cmd.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/cmd.hpp" + +namespace VOM { +/** + * Free ostream function to print a command + */ +std::ostream& +operator<<(std::ostream& os, const cmd& cmd) +{ + os << cmd.to_string(); + + return (os); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/cmd.hpp b/src/vpp-api/vom/cmd.hpp new file mode 100644 index 00000000000..814355320a7 --- /dev/null +++ b/src/vpp-api/vom/cmd.hpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_CMD_H__ +#define __VOM_CMD_H__ + +#include + +#include "vom/types.hpp" + +#include + +namespace VOM { +/** + * Forward declaration of the connection class + */ +class connection; + +/** + * A representation of a method call to VPP + */ +class cmd +{ +public: + /** + * Default constructor + */ + cmd() {} + /** + * Virtual destructor + */ + virtual ~cmd() {} + + /** + * Issue the command to VPP/HW + */ + virtual rc_t issue(connection& con) = 0; + + /** + * Retire/cancel a long running command + */ + virtual void retire(connection& con) = 0; + + /** + * Invoked on a Command when the HW queue is disabled to indicate + * that the commnad can be considered successful + */ + virtual void succeeded() = 0; + + /** + * convert to string format for debug purposes + */ + virtual std::string to_string() const = 0; +}; + +/** + * Free ostream function to print a command + */ +std::ostream& operator<<(std::ostream& os, const cmd& cmd); +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/connection.cpp b/src/vpp-api/vom/connection.cpp new file mode 100644 index 00000000000..3d965ea863f --- /dev/null +++ b/src/vpp-api/vom/connection.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/connection.hpp" + +namespace VOM { +connection::connection() + : m_app_name("vpp-OM") +{ +} + +connection::~connection() +{ + disconnect(); +} + +void +connection::disconnect() +{ + m_vapi_conn.disconnect(); +} + +void +connection::connect() +{ + vapi_error_e rv; + + do { + rv = m_vapi_conn.connect(m_app_name.c_str(), + NULL, // m_api_prefix.c_str(), + 128, 128); + } while (VAPI_OK != rv); +} + +vapi::Connection& +connection::ctx() +{ + return (m_vapi_conn); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/connection.hpp b/src/vpp-api/vom/connection.hpp new file mode 100644 index 00000000000..0dc01845eb8 --- /dev/null +++ b/src/vpp-api/vom/connection.hpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_CONNECTION_H__ +#define __VOM_CONNECTION_H__ + +#include + +#include + +namespace VOM { +/** + * A representation of the connection to VPP + */ +class connection +{ +public: + /** + * Constructor + */ + connection(); + /** + * Destructor + */ + ~connection(); + + /** + * Blocking [re]connect call - always eventually succeeds, or the + * universe expires. Not much this system can do without one. + */ + void connect(); + + /** + * Blocking disconnect + */ + void disconnect(); + + /** + * Retrun the VAPI context the commands will use + */ + vapi::Connection& ctx(); + +private: + /** + * The VAPI connection context + */ + vapi::Connection m_vapi_conn; + + /** + * The name of this application + */ + const std::string m_app_name; +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/dhcp_config.cpp b/src/vpp-api/vom/dhcp_config.cpp new file mode 100644 index 00000000000..bf9b036b919 --- /dev/null +++ b/src/vpp-api/vom/dhcp_config.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/dhcp_config.hpp" +#include "vom/cmd.hpp" + +namespace VOM { +/** + * A DB of all DHCP configs + */ +singular_db dhcp_config::m_db; + +dhcp_config::event_handler dhcp_config::m_evh; + +dhcp_config::dhcp_config(const interface& itf, const std::string& hostname) + : m_itf(itf.singular()) + , m_hostname(hostname) + , m_client_id(l2_address_t::ZERO) + , m_binding(0) +{ +} + +dhcp_config::dhcp_config(const interface& itf, + const std::string& hostname, + const l2_address_t& client_id) + : m_itf(itf.singular()) + , m_hostname(hostname) + , m_client_id(client_id) + , m_binding(0) +{ +} + +dhcp_config::dhcp_config(const dhcp_config& o) + : m_itf(o.m_itf) + , m_hostname(o.m_hostname) + , m_client_id(o.m_client_id) + , m_binding(0) +{ +} + +dhcp_config::~dhcp_config() +{ + sweep(); + + // not in the DB anymore. + m_db.release(m_itf->key(), this); +} + +void +dhcp_config::sweep() +{ + if (m_binding) { + HW::enqueue(new unbind_cmd(m_binding, m_itf->handle(), m_hostname)); + } + HW::write(); +} + +void +dhcp_config::dump(std::ostream& os) +{ + m_db.dump(os); +} + +void +dhcp_config::replay() +{ + if (m_binding) { + HW::enqueue( + new bind_cmd(m_binding, m_itf->handle(), m_hostname, m_client_id)); + } +} + +std::string +dhcp_config::to_string() const +{ + std::ostringstream s; + s << "Dhcp-config: " << m_itf->to_string() << " hostname:" << m_hostname + << " client_id:[" << m_client_id << "] " << m_binding.to_string(); + + return (s.str()); +} + +void +dhcp_config::update(const dhcp_config& desired) +{ + /* + * the desired state is always that the interface should be created + */ + if (!m_binding) { + HW::enqueue( + new bind_cmd(m_binding, m_itf->handle(), m_hostname, m_client_id)); + } +} + +std::shared_ptr +dhcp_config::find_or_add(const dhcp_config& temp) +{ + return (m_db.find_or_add(temp.m_itf->key(), temp)); +} + +std::shared_ptr +dhcp_config::singular() const +{ + return find_or_add(*this); +} + +dhcp_config::event_listener::event_listener() + : m_status(rc_t::NOOP) +{ +} + +HW::item& +dhcp_config::event_listener::status() +{ + return (m_status); +} + +dhcp_config::event_handler::event_handler() +{ + OM::register_listener(this); + inspect::register_handler({ "dhcp" }, "DHCP configurations", this); +} + +void +dhcp_config::event_handler::handle_replay() +{ + m_db.replay(); +} + +void +dhcp_config::event_handler::handle_populate(const client_db::key_t& key) +{ + // FIXME +} + +dependency_t +dhcp_config::event_handler::order() const +{ + return (dependency_t::BINDING); +} + +void +dhcp_config::event_handler::show(std::ostream& os) +{ + m_db.dump(os); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/dhcp_config.hpp b/src/vpp-api/vom/dhcp_config.hpp new file mode 100644 index 00000000000..9ce0635c939 --- /dev/null +++ b/src/vpp-api/vom/dhcp_config.hpp @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_DHCP_INTERFACE_H__ +#define __VOM_DHCP_INTERFACE_H__ + +#include "vom/dump_cmd.hpp" +#include "vom/hw.hpp" +#include "vom/inspect.hpp" +#include "vom/interface.hpp" +#include "vom/object_base.hpp" +#include "vom/om.hpp" +#include "vom/rpc_cmd.hpp" +#include "vom/singular_db.hpp" +#include "vom/sub_interface.hpp" + +#include + +namespace VOM { +/** + * A representation of DHCP client configuration on an interface + */ +class dhcp_config : public object_base +{ +public: + /** + * Construct a new object matching the desried state + */ + dhcp_config(const interface& itf, const std::string& hostname); + + /** + * Construct a new object matching the desried state + */ + dhcp_config(const interface& itf, + const std::string& hostname, + const l2_address_t& client_id); + + /** + * Copy Constructor + */ + dhcp_config(const dhcp_config& o); + + /** + * Destructor + */ + ~dhcp_config(); + + /** + * Return the 'singular' of the DHCP config that matches this object + */ + std::shared_ptr singular() const; + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Dump all DHCP configs into the stream provided + */ + static void dump(std::ostream& os); + + /** + * A command class that binds the DHCP config to the interface + */ + class bind_cmd + : public rpc_cmd, rc_t, vapi::Dhcp_client_config> + { + public: + /** + * Constructor + */ + bind_cmd(HW::item& item, + const handle_t& itf, + const std::string& hostname, + const l2_address_t& client_id); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const bind_cmd& i) const; + + private: + /** + * Reference to the HW::item of the interface to bind + */ + const handle_t& m_itf; + + /** + * The DHCP client's hostname + */ + const std::string m_hostname; + + /** + * The DHCP client's ID + */ + const l2_address_t m_client_id; + }; + + /** + * A cmd class that Unbinds Dhcp Config from an interface + */ + class unbind_cmd + : public rpc_cmd, rc_t, vapi::Dhcp_client_config> + { + public: + /** + * Constructor + */ + unbind_cmd(HW::item& item, + const handle_t& itf, + const std::string& hostname); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const unbind_cmd& i) const; + + private: + /** + * Reference to the HW::item of the interface to unbind + */ + const handle_t& m_itf; + + /** + * The DHCP client's hostname + */ + const std::string m_hostname; + }; + + /** + * Forward declartion of the Event Command + */ + class events_cmd; + + /** + * A class that listens to DHCP Events + */ + class event_listener + { + public: + /** + * Constructor + */ + event_listener(); + + /** + * listener's virtual function invoked when a DHCP event is + * available to read + */ + virtual void handle_dhcp_event(events_cmd* cmd) = 0; + + /** + * Return the HW::item associated with this command + */ + HW::item& status(); + + protected: + /** + * The HW::item associated with this command + */ + HW::item m_status; + }; + + /** + * A functor class represents our desire to recieve interface events + */ + class events_cmd + : public event_cmd + { + public: + /** + * Constructor + */ + events_cmd(event_listener& el); + + /** + * Issue the command to VPP/HW - subscribe to DHCP events + */ + rc_t issue(connection& con); + + /** + * Retire the command - unsubscribe + */ + void retire(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const events_cmd& i) const; + + /** + * called in the VAPI RX thread when data is available. + */ + void notify(); + + private: + void succeeded() {} + /** + * The listner of this command + */ + event_listener& m_listener; + }; + +private: + /** + * Class definition for listeners to OM events + */ + class event_handler : public OM::listener, public inspect::command_handler + { + public: + event_handler(); + virtual ~event_handler() = default; + + /** + * Handle a populate event + */ + void handle_populate(const client_db::key_t& key); + + /** + * Handle a replay event + */ + void handle_replay(); + + /** + * Show the object in the Singular DB + */ + void show(std::ostream& os); + + /** + * Get the sortable Id of the listener + */ + dependency_t order() const; + }; + + /** + * event_handler to register with OM + */ + static event_handler m_evh; + + /** + * Enquue commonds to the VPP command Q for the update + */ + void update(const dhcp_config& obj); + + /** + * Find or add DHCP config to the OM + */ + static std::shared_ptr find_or_add(const dhcp_config& temp); + + /* + * It's the OM class that calls singular() + */ + friend class OM; + + /** + * It's the singular_db class that calls replay() + */ + friend class singular_db; + + /** + * Sweep/reap the object if still stale + */ + void sweep(void); + + /** + * replay the object to create it in hardware + */ + void replay(void); + + /** + * A reference counting pointer to the interface on which DHCP config + * resides. By holding the reference here, we can guarantee that + * this object will outlive the interface + */ + const std::shared_ptr m_itf; + + /** + * The hostname in the DHCP configuration + */ + const std::string m_hostname; + + /** + * The option-61 client_id in the DHCP configuration + */ + const l2_address_t m_client_id; + + /** + * HW configuration for the binding. The bool representing the + * do/don't bind. + */ + HW::item m_binding; + + /** + * A map of all Dhcp configs keyed against the interface. + */ + static singular_db m_db; +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/dhcp_config_cmds.cpp b/src/vpp-api/vom/dhcp_config_cmds.cpp new file mode 100644 index 00000000000..53262a3cac6 --- /dev/null +++ b/src/vpp-api/vom/dhcp_config_cmds.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/dhcp_config.hpp" + +DEFINE_VAPI_MSG_IDS_DHCP_API_JSON; + +namespace VOM { +dhcp_config::bind_cmd::bind_cmd(HW::item& item, + const handle_t& itf, + const std::string& hostname, + const l2_address_t& client_id) + : rpc_cmd(item) + , m_itf(itf) + , m_hostname(hostname) + , m_client_id(client_id) +{ +} + +bool +dhcp_config::bind_cmd::operator==(const bind_cmd& other) const +{ + return ((m_itf == other.m_itf) && (m_hostname == other.m_hostname)); +} + +rc_t +dhcp_config::bind_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_itf.value(); + payload.is_add = 1; + payload.pid = getpid(); + payload.want_dhcp_event = 1; + + memcpy(payload.hostname, m_hostname.c_str(), + std::min(sizeof(payload.hostname), m_hostname.length())); + + memset(payload.client_id, 0, sizeof(payload.client_id)); + payload.client_id[0] = 1; + std::copy_n(begin(m_client_id.bytes), + std::min(sizeof(payload.client_id), m_client_id.bytes.size()), + payload.client_id + 1); + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return rc_t::OK; +} + +std::string +dhcp_config::bind_cmd::to_string() const +{ + std::ostringstream s; + s << "Dhcp-config-bind: " << m_hw_item.to_string() + << " itf:" << m_itf.to_string() << " hostname:" << m_hostname; + + return (s.str()); +} + +dhcp_config::unbind_cmd::unbind_cmd(HW::item& item, + const handle_t& itf, + const std::string& hostname) + : rpc_cmd(item) + , m_itf(itf) + , m_hostname(hostname) +{ +} + +bool +dhcp_config::unbind_cmd::operator==(const unbind_cmd& other) const +{ + return ((m_itf == other.m_itf) && (m_hostname == other.m_hostname)); +} + +rc_t +dhcp_config::unbind_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_itf.value(); + payload.is_add = 0; + payload.pid = getpid(); + payload.want_dhcp_event = 0; + + memcpy(payload.hostname, m_hostname.c_str(), + std::min(sizeof(payload.hostname), m_hostname.length())); + + VAPI_CALL(req.execute()); + + wait(); + m_hw_item.set(rc_t::NOOP); + + return rc_t::OK; +} + +std::string +dhcp_config::unbind_cmd::to_string() const +{ + std::ostringstream s; + s << "Dhcp-config-unbind: " << m_hw_item.to_string() + << " itf:" << m_itf.to_string() << " hostname:" << m_hostname; + + return (s.str()); +} + +dhcp_config::events_cmd::events_cmd(event_listener& el) + : event_cmd(el.status()) + , m_listener(el) +{ +} + +bool +dhcp_config::events_cmd::operator==(const events_cmd& other) const +{ + return (true); +} + +rc_t +dhcp_config::events_cmd::issue(connection& con) +{ + /* + * Set the call back to handle DHCP complete envets. + */ + m_reg.reset(new reg_t(con.ctx(), std::ref(*this))); + + /* + * return in-progress so the command stays in the pending list. + */ + return (rc_t::INPROGRESS); +} + +void +dhcp_config::events_cmd::retire(connection& con) +{ +} + +void +dhcp_config::events_cmd::notify() +{ + m_listener.handle_dhcp_event(this); +} + +std::string +dhcp_config::events_cmd::to_string() const +{ + return ("dhcp-events"); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/dump_cmd.hpp b/src/vpp-api/vom/dump_cmd.hpp new file mode 100644 index 00000000000..cc255ac0daf --- /dev/null +++ b/src/vpp-api/vom/dump_cmd.hpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_DUMP_CMD_H__ +#define __VOM_DUMP_CMD_H__ + +#include + +#include "vom/cmd.hpp" +#include "vom/hw.hpp" + +#include + +namespace VOM { +/** + * A function type def for calculating a message's size + */ +typedef unsigned int (*get_msg_size_t)(void*); + +/** + * A base class for VPP dump commands. + * Dump commands are one of the sub-set of command types to VPP. Here the + * client + * makes a read request on the resource and VPP responds with all the + * records. + * This command is executed synchronously. Once complete the client can + * 'pop' + * the records from the command object + */ +template +class dump_cmd : public cmd +{ +public: + typedef MSG msg_t; + typedef typename MSG::resp_type record_t; + + typedef typename vapi::Result_set::const_iterator + const_iterator; + + /** + * Default Constructor + */ + dump_cmd() + : cmd() + { + } + + /** + * Destructor + */ + virtual ~dump_cmd() {} + + const_iterator begin() { return (m_dump->get_result_set().begin()); } + + const_iterator end() { return (m_dump->get_result_set().end()); } + + /** + * Wait for the issue of the command to complete + */ + rc_t wait() + { + std::future_status status; + std::future result; + + result = m_promise.get_future(); + status = result.wait_for(std::chrono::seconds(5)); + + if (status != std::future_status::ready) { + return (rc_t::TIMEOUT); + } + + return (result.get()); + } + + /** + * Call operator called when the dump is complete + */ + vapi_error_e operator()(MSG& d) + { + m_promise.set_value(rc_t::OK); + + return (VAPI_OK); + } + + /** + * Retire/cancel a long running command + */ + virtual void retire(connection& con) {} + +protected: + /** + * The underlying promise that implements the synchornous nature + * of the command issue + */ + std::promise m_promise; + + /** + * Dump commands should not be issued whilst the HW is disabled + */ + void succeeded() {} + + /** + * The HW::cmd_q is a friend so it can call suceedded. + */ + friend class HW::cmd_q; + + /** + * The VAPI event registration + */ + std::unique_ptr m_dump; +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/enum_base.hpp b/src/vpp-api/vom/enum_base.hpp new file mode 100644 index 00000000000..4184cebc986 --- /dev/null +++ b/src/vpp-api/vom/enum_base.hpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_ENUM_H__ +#define __VOM_ENUM_H__ + +#include + +namespace VOM { +/** + * A template base class for all enum types. + * This enum type exists to associate an enum value with a string for + * display/debug purposes. + * Concrete enum types use the CRTP. Derived classes thus inherit this + * base's function, but are not polymorphic. + */ +template +class enum_base +{ +public: + /** + * convert to string format for debug purposes + */ + const std::string& to_string() const { return (m_desc); } + + /** + * Comparison operator + */ + bool operator==(const enum_base& e) const { return (e.m_value == m_value); } + + /** + * Assignment + */ + enum_base& operator=(const enum_base& e) + { + m_value = e.m_value; + m_desc = e.m_desc; + + return (*this); + } + + /** + * Comparison operator + */ + bool operator!=(const enum_base& e) const { return (e.m_value != m_value); } + + /** + * integer conversion operator + */ + constexpr operator int() const { return (m_value); } + + /** + * Return the value of the enum - same as integer conversion + */ + constexpr int value() const { return (m_value); } + +protected: + /** + * Constructor of an enum - takes value and string description + */ + constexpr enum_base(int value, const std::string desc) + : m_value(value) + , m_desc(desc) + { + } + + /** + * Constructor + */ + virtual ~enum_base() {} + +private: + /** + * Integer value of the enum + */ + int m_value; + + /** + * String description + */ + std::string m_desc; +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/event_cmd.hpp b/src/vpp-api/vom/event_cmd.hpp new file mode 100644 index 00000000000..a0e9b4af112 --- /dev/null +++ b/src/vpp-api/vom/event_cmd.hpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_EVENT_CMD_H__ +#define __VOM_EVENT_CMD_H__ + +#include + +#include "vom/rpc_cmd.hpp" + +#include + +namespace VOM { +/** + * An Event command base class. + * Events are one of the sub-set of command type to VPP. + * A client performs a one time 'registration/subsription' to VPP for the + * event in question and then is notified asynchronously when those events + * occur. + * The model here then is that the lifetime of the event command represensts + * the during of the clients subscription. When the command is 'issued' the + * subscription begins, when it is 'retired' the subscription ends. For the + * subscription duration the client will be notified as events are recieved. + * The client can then 'pop' these events from this command object. + */ +template +class event_cmd : public rpc_cmd, rc_t, WANT> +{ +public: + /** + * Default constructor + */ + event_cmd(HW::item& b) + : rpc_cmd, rc_t, WANT>(b) + { + } + + /** + * Default destructor + */ + virtual ~event_cmd() {} + + /** + * Typedef for the event type + */ + typedef typename vapi::Event_registration::resp_type event_t; + typedef typename vapi::Event_registration reg_t; + + typedef typename vapi::Result_set::const_iterator + const_iterator; + + const_iterator begin() { return (m_reg->get_result_set().begin()); } + + const_iterator end() { return (m_reg->get_result_set().end()); } + + void lock() { m_mutex.lock(); } + void unlock() { m_mutex.unlock(); } + + /** + * flush/free all the events thus far reeived. + * Call with the lock held! + */ + void flush() { m_reg->get_result_set().free_all_responses(); } + + /** + * Retire the command. This is only appropriate for Event Commands + * As they persist until retired. + */ + virtual void retire(connection& con) = 0; + + vapi_error_e operator()(reg_t& dl) + { + notify(); + + return (VAPI_OK); + } + +protected: + /** + * Notify the command that data from VPP has arrived and been stored. + * The command should now inform its clients/listeners. + */ + virtual void notify() = 0; + + /** + * The VAPI event registration + */ + std::unique_ptr> m_reg; + + /** + * Mutex protection for the events + */ + std::mutex m_mutex; +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/hw.cpp b/src/vpp-api/vom/hw.cpp new file mode 100644 index 00000000000..56885525494 --- /dev/null +++ b/src/vpp-api/vom/hw.cpp @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/hw.hpp" +#include "vom/logger.hpp" + +#include + +namespace VOM { +HW::cmd_q::cmd_q() + : m_enabled(true) + , m_connected(false) + , m_conn() +{ +} + +HW::cmd_q::~cmd_q() +{ + m_connected = false; + + if (m_rx_thread && m_rx_thread->joinable()) { + m_rx_thread->join(); + } +} + +HW::cmd_q& +HW::cmd_q::operator=(const HW::cmd_q& f) +{ + return (*this); +} + +/** + * Run the connect/dispatch thread. + */ +void +HW::cmd_q::rx_run() +{ + while (m_connected) { + m_conn.ctx().dispatch(); + } +} + +void +HW::cmd_q::enqueue(cmd* c) +{ + std::shared_ptr sp(c); + + m_queue.push_back(sp); +} + +void +HW::cmd_q::enqueue(std::shared_ptr c) +{ + m_queue.push_back(c); +} + +void +HW::cmd_q::enqueue(std::queue& cmds) +{ + while (cmds.size()) { + std::shared_ptr sp(cmds.front()); + + m_queue.push_back(sp); + cmds.pop(); + } +} + +void +HW::cmd_q::dequeue(cmd* c) +{ + c->retire(m_conn); + m_pending.erase(c); +} + +void +HW::cmd_q::dequeue(std::shared_ptr c) +{ + c->retire(m_conn); + m_pending.erase(c.get()); +} + +void +HW::cmd_q::connect() +{ + if (m_connected) { + m_conn.disconnect(); + } + + m_connected = false; + + if (m_rx_thread && m_rx_thread->joinable()) { + m_rx_thread->join(); + } + + m_conn.connect(); + + m_connected = true; + m_rx_thread.reset(new std::thread(&HW::cmd_q::rx_run, this)); +} + +void +HW::cmd_q::enable() +{ + m_enabled = true; +} + +void +HW::cmd_q::disable() +{ + m_enabled = false; +} + +rc_t +HW::cmd_q::write() +{ + rc_t rc = rc_t::OK; + + /* + * The queue is enabled, Execute each command in the queue. + * If one execution fails, abort the rest + */ + auto it = m_queue.begin(); + + while (it != m_queue.end()) { + std::shared_ptr c = *it; + + VOM_LOG(log_level_t::DEBUG) << *c; + + if (m_enabled) { + /* + * before we issue the command we must move it to the pending + * store + * ince a async event can be recieved before the command + * completes + */ + m_pending[c.get()] = c; + + rc = c->issue(m_conn); + + if (rc_t::INPROGRESS == rc) { + /* + * this command completes asynchronously + * leave the command in the pending store + */ + } else { + /* + * the command completed, remove from the pending store + */ + m_pending.erase(c.get()); + + if (rc_t::OK == rc) { + /* + * move to the next + */ + } else { + /* + * barf out without issuing the rest + */ + break; + } + } + } else { + /* + * The HW is disabled, so set each command as succeeded + */ + c->succeeded(); + } + + ++it; + } + + /* + * erase all objects in the queue + */ + m_queue.erase(m_queue.begin(), m_queue.end()); + + return (rc); +} + +/* + * The single Command Queue + */ +HW::cmd_q* HW::m_cmdQ; +HW::item HW::m_poll_state; + +/** + * Initialse the connection to VPP + */ +void +HW::init(HW::cmd_q* f) +{ + m_cmdQ = f; +} + +/** + * Initialse the connection to VPP + */ +void +HW::init() +{ + m_cmdQ = new cmd_q(); +} + +void +HW::enqueue(cmd* cmd) +{ + m_cmdQ->enqueue(cmd); +} + +void +HW::enqueue(std::shared_ptr cmd) +{ + m_cmdQ->enqueue(cmd); +} + +void +HW::enqueue(std::queue& cmds) +{ + m_cmdQ->enqueue(cmds); +} + +void +HW::dequeue(cmd* cmd) +{ + m_cmdQ->dequeue(cmd); +} + +void +HW::dequeue(std::shared_ptr cmd) +{ + m_cmdQ->dequeue(cmd); +} + +void +HW::connect() +{ + m_cmdQ->connect(); +} + +void +HW::enable() +{ + m_cmdQ->enable(); +} + +void +HW::disable() +{ + m_cmdQ->disable(); +} + +rc_t +HW::write() +{ + return (m_cmdQ->write()); +} + +bool +HW::poll() +{ + std::shared_ptr poll(new Poll(m_poll_state)); + + HW::enqueue(poll); + HW::write(); + + return (m_poll_state); +} + +template <> +std::string +HW::item::to_string() const +{ + std::ostringstream os; + + os << "hw-item:[" + << "rc:" << item_rc.to_string() << " data:" << item_data << "]"; + return (os.str()); +} + +template <> +std::string +HW::item::to_string() const +{ + std::ostringstream os; + + os << "hw-item:[" + << "rc:" << item_rc.to_string() << " data:" << item_data << "]"; + return (os.str()); +} + +HW::Poll::Poll(HW::item& item) + : rpc_cmd(item) +{ +} + +rc_t +HW::Poll::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return (rc_t::OK); +} + +std::string +HW::Poll::to_string() const +{ + std::ostringstream s; + + s << "poll: " << m_hw_item.to_string(); + + return (s.str()); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/hw.hpp b/src/vpp-api/vom/hw.hpp new file mode 100644 index 00000000000..00ab2d647f1 --- /dev/null +++ b/src/vpp-api/vom/hw.hpp @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_HW_H__ +#define __VOM_HW_H__ + +#include +#include +#include +#include +#include + +#include +#include + +#include "vom/connection.hpp" +#include "vom/rpc_cmd.hpp" + +namespace VOM { +class HW +{ +public: + /** + * A HW::item is data that is either to be written to or read from + * VPP/HW. + * The item is a pair of the data written/read and the result of that + * operation. + */ + template + class item + { + public: + /** + * Constructor + */ + item(const T& data) + : item_data(data) + , item_rc(rc_t::NOOP) + { + } + /** + * Constructor + */ + item() + : item_rc(rc_t::UNSET) + { + } + + /** + * Constructor + */ + item(rc_t rc) + : item_rc(rc) + { + } + + /** + * Constructor + */ + item(const T& data, rc_t rc) + : item_data(data) + , item_rc(rc) + { + } + + /** + * Comparison operator + */ + bool operator==(const item& i) const + { + return (item_data == i.item_data); + } + + /** + * Copy assignment + */ + item& operator=(const item& other) + { + item_data = other.item_data; + item_rc = other.item_rc; + + return (*this); + } + + /** + * Return the data read/written + */ + T& data() { return (item_data); } + + /** + * Const reference to the data + */ + const T& data() const { return (item_data); } + + /** + * Get the HW return code + */ + rc_t rc() const { return (item_rc); } + + /** + * Set the HW return code - should only be called from the + * family of Command objects + */ + void set(const rc_t& rc) { item_rc = rc; } + + /** + * Return true if the HW item is configred in HW + */ + operator bool() const { return (rc_t::OK == item_rc); } + + /** + * update the item to the desired state. + * return true if a HW update is required + */ + bool update(const item& desired) + { + bool need_hw_update = false; + + /* + * if the deisred set is unset (i.e. defaulted, we've + * no update to make + */ + if (rc_t::UNSET == desired.rc()) { + return (false); + } + /* + * A HW update is needed if thestate is different + * or the state is not yet in HW + */ + need_hw_update = (item_data != desired.data() || rc_t::OK != rc()); + + item_data = desired.data(); + + return (need_hw_update); + } + + /** + * convert to string format for debug purposes + */ + std::string to_string() const + { + std::ostringstream os; + + os << "hw-item:[" + << "rc:" << item_rc.to_string() << " data:" << item_data.to_string() + << "]"; + + return (os.str()); + } + + private: + /** + * The data + */ + T item_data; + + /** + * The result when the item was written + */ + rc_t item_rc; + }; + + /** + * The pipe to VPP into which we write the commands + */ + class cmd_q + { + public: + /** + * Constructor + */ + cmd_q(); + /** + * Destructor + */ + ~cmd_q(); + + /** + * Copy assignement - only used in UT + */ + cmd_q& operator=(const cmd_q& f); + + /** + * Enqueue a command into the Q. + */ + virtual void enqueue(cmd* c); + /** + * Enqueue a command into the Q. + */ + virtual void enqueue(std::shared_ptr c); + + /** + * Enqueue a set of commands + */ + virtual void enqueue(std::queue& c); + + /** + * dequeue a command from the Q. + */ + virtual void dequeue(cmd* c); + + /** + * dequeue a command from the Q. + */ + virtual void dequeue(std::shared_ptr c); + + /** + * Write all the commands to HW + */ + virtual rc_t write(); + + /** + * Blocking Connect to VPP - call once at bootup + */ + void connect(); + + /** + * Disable the passing of commands to VPP. Whilst disabled all + * writes will be discarded. Use this during the reset phase. + */ + void disable(); + + /** + * Enable the passing of commands to VPP - undoes the disable. + * The Q is enabled by default. + */ + void enable(); + + private: + /** + * A queue of enqueued commands, ready to be written + */ + std::deque> m_queue; + + /** + * A map of issued, but uncompleted, commands. + * i.e. those that we are waiting, async stylee, + * for VPP to complete + */ + std::map> m_pending; + + /** + * VPP Q poll function + */ + void rx_run(); + + /** + * The thread object running the poll/dispatch/connect thread + */ + std::unique_ptr m_rx_thread; + + /** + * A flag indicating the client has disabled the cmd Q. + */ + bool m_enabled; + + /** + * A flag for the thread to poll to see if the queue is still alive + */ + bool m_connected; + + /** + * The connection to VPP + */ + connection m_conn; + }; + + /** + * Initialise the HW connection to VPP - the UT version passing + * a mock Q. + */ + static void init(cmd_q* f); + + /** + * Initialise the HW + */ + static void init(); + + /** + * Enqueue A command for execution + */ + static void enqueue(cmd* f); + + /** + * Enqueue A command for execution + */ + static void enqueue(std::shared_ptr c); + + /** + * Enqueue A set of commands for execution + */ + static void enqueue(std::queue& c); + + /** + * dequeue A command for execution + */ + static void dequeue(cmd* f); + + /** + * dequeue A command for execution + */ + static void dequeue(std::shared_ptr c); + + /** + * Write/Execute all commands hitherto enqueued. + */ + static rc_t write(); + + /** + * Blocking Connect to VPP + */ + static void connect(); + + /** + * Blocking pool of the HW connection + */ + static bool poll(); + +private: + /** + * The command Q toward HW + */ + static cmd_q* m_cmdQ; + + /** + * HW::item representing the connection state as determined by polling + */ + static HW::item m_poll_state; + + /** + * Disable the passing of commands to VPP. Whilst disabled all writes + * will be discarded. Use this during the reset phase. + */ + static void disable(); + + /** + * Enable the passing of commands to VPP - undoes the disable. + * The Q is enabled by default. + */ + static void enable(); + + /** + * Only the OM can enable/disable HW + */ + friend class OM; + + /** + * A command pool the HW for liveness + */ + class Poll : public rpc_cmd, rc_t, vapi::Control_ping> + { + public: + /** + * Constructor taking the HW::item to update + */ + Poll(HW::item& item); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const Poll& i) const; + }; +}; + +/** + * bool Specialisation for HW::item to_string + */ +template <> +std::string HW::item::to_string() const; + +/** + * uint Specialisation for HW::item to_string + */ +template <> +std::string HW::item::to_string() const; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/inspect.cpp b/src/vpp-api/vom/inspect.cpp new file mode 100644 index 00000000000..c78128c2bb6 --- /dev/null +++ b/src/vpp-api/vom/inspect.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "vom/inspect.hpp" +#include "vom/logger.hpp" +#include "vom/om.hpp" + +namespace VOM { +std::unique_ptr> + inspect::m_cmd_handlers; + +std::unique_ptr, std::string>>> + inspect::m_help_handlers; + +void +inspect::handle_input(const std::string& message, std::ostream& output) +{ + if (message.length()) { + if (message.find("help") != std::string::npos) { + output << "Command Options: " << std::endl; + output << " keys - Show all keys owning objects" + << std::endl; + output << " key:XXX - Show all object referenced by " + "key XXX" + << std::endl; + output << " all - Show All objects" << std::endl; + output << "Individual object_base Types:" << std::endl; + + for (auto h : *m_help_handlers) { + output << " {"; + + for (auto s : h.first) { + output << s << " "; + } + output << "} - \t"; + output << h.second; + output << std::endl; + } + } else if (message.find("keys") != std::string::npos) { + OM::dump(output); + } else if (message.find("key") != std::string::npos) { + std::vector results; + boost::split(results, message, boost::is_any_of(":\n")); + + OM::dump(results[1], output); + } else if (message.find("all") != std::string::npos) { + /* + * get the unique set of handlers, then invoke each + */ + std::set hdlrs; + for (auto h : *m_cmd_handlers) { + hdlrs.insert(h.second); + } + for (auto h : hdlrs) { + h->show(output); + } + } else { + auto it = m_cmd_handlers->find(message); + + if (it != m_cmd_handlers->end()) { + it->second->show(output); + } else { + output << "Unknown Command: " << message << std::endl; + } + } + } +} + +void +inspect::register_handler(const std::vector& cmds, + const std::string& help, + command_handler* handler) +{ + if (!m_cmd_handlers) { + m_cmd_handlers.reset(new std::map); + m_help_handlers.reset( + new std::deque, std::string>>); + } + + for (auto cmd : cmds) { + (*m_cmd_handlers)[cmd] = handler; + } + m_help_handlers->push_front(std::make_pair(cmds, help)); +} +} +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/inspect.hpp b/src/vpp-api/vom/inspect.hpp new file mode 100644 index 00000000000..fb9528decc0 --- /dev/null +++ b/src/vpp-api/vom/inspect.hpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_INSPECT_H__ +#define __VOM_INSPECT_H__ + +#include +#include +#include +#include + +namespace VOM { +/** + * A means to inspect the state VPP has built, in total, and per-client + */ +class inspect +{ +public: + /** + * Constructor + */ + inspect() = default; + + /** + * Destructor to tidyup socket resources + */ + ~inspect() = default; + + /** + * handle input from the requester + * + * @param input command + * @param output output + */ + void handle_input(const std::string& input, std::ostream& output); + + /** + * inspect command handler Handler + */ + class command_handler + { + public: + command_handler() = default; + virtual ~command_handler() = default; + + /** + * Show each object + */ + virtual void show(std::ostream& os) = 0; + }; + + /** + * Register a command handler for inspection + */ + static void register_handler(const std::vector& cmds, + const std::string& help, + command_handler* ch); + +private: + /** + * command handler list + */ + static std::unique_ptr> + m_cmd_handlers; + /** + * help handler list + */ + static std::unique_ptr< + std::deque, std::string>>> + m_help_handlers; +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/interface.cpp b/src/vpp-api/vom/interface.cpp new file mode 100644 index 00000000000..83da275f61f --- /dev/null +++ b/src/vpp-api/vom/interface.cpp @@ -0,0 +1,434 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/interface.hpp" +#include "vom/cmd.hpp" +#include "vom/l3_binding.hpp" +#include "vom/logger.hpp" +#include "vom/prefix.hpp" + +namespace VOM { +/** + * A DB of all the interfaces, key on the name + */ +singular_db interface::m_db; + +/** + * A DB of all the interfaces, key on VPP's handle + */ +std::map> interface::m_hdl_db; + +interface::event_handler interface::m_evh; + +/** + * Construct a new object matching the desried state + */ +interface::interface(const std::string& name, + interface::type_t itf_type, + interface::admin_state_t itf_state) + : m_hdl(handle_t::INVALID) + , m_name(name) + , m_type(itf_type) + , m_state(itf_state) + , m_table_id(route::DEFAULT_TABLE) + , m_l2_address(l2_address_t::ZERO, rc_t::UNSET) + , m_oper(oper_state_t::DOWN) +{ +} + +interface::interface(const handle_t& handle, + const l2_address_t& l2_address, + const std::string& name, + interface::type_t type, + interface::admin_state_t state) + : m_hdl(handle) + , m_name(name) + , m_type(type) + , m_state(state) + , m_table_id(route::DEFAULT_TABLE) + , m_l2_address(l2_address) + , m_oper(oper_state_t::DOWN) +{ +} + +interface::interface(const std::string& name, + interface::type_t itf_type, + interface::admin_state_t itf_state, + const route_domain& rd) + : m_hdl(handle_t::INVALID) + , m_name(name) + , m_type(itf_type) + , m_rd(rd.singular()) + , m_state(itf_state) + , m_table_id(m_rd->table_id()) + , m_l2_address(l2_address_t::ZERO, rc_t::UNSET) + , m_oper(oper_state_t::DOWN) +{ +} + +interface::interface(const interface& o) + : m_hdl(o.m_hdl) + , m_name(o.m_name) + , m_type(o.m_type) + , m_rd(o.m_rd) + , m_state(o.m_state) + , m_table_id(o.m_table_id) + , m_l2_address(o.m_l2_address) + , m_oper(o.m_oper) +{ +} + +interface::event_listener::event_listener() + : m_status(rc_t::NOOP) +{ +} + +HW::item& +interface::event_listener::status() +{ + return (m_status); +} + +interface::stat_listener::stat_listener() + : m_status(rc_t::NOOP) +{ +} + +HW::item& +interface::stat_listener::status() +{ + return (m_status); +} + +/** + * Return the interface type + */ +const interface::type_t& +interface::type() const +{ + return (m_type); +} + +const handle_t& +interface::handle() const +{ + return (m_hdl.data()); +} + +const l2_address_t& +interface::l2_address() const +{ + return (m_l2_address.data()); +} + +interface::const_iterator_t +interface::cbegin() +{ + return m_db.cbegin(); +} + +interface::const_iterator_t +interface::cend() +{ + return m_db.cend(); +} + +void +interface::sweep() +{ + if (m_table_id) { + m_table_id.data() = route::DEFAULT_TABLE; + HW::enqueue(new set_table_cmd(m_table_id, l3_proto_t::IPV4, m_hdl)); + HW::enqueue(new set_table_cmd(m_table_id, l3_proto_t::IPV6, m_hdl)); + } + + // If the interface is up, bring it down + if (m_state && interface::admin_state_t::UP == m_state.data()) { + m_state.data() = interface::admin_state_t::DOWN; + HW::enqueue(new state_change_cmd(m_state, m_hdl)); + } + if (m_hdl) { + std::queue cmds; + HW::enqueue(mk_delete_cmd(cmds)); + } + HW::write(); +} + +void +interface::replay() +{ + if (m_hdl) { + std::queue cmds; + HW::enqueue(mk_create_cmd(cmds)); + } + + if (m_state && interface::admin_state_t::UP == m_state.data()) { + HW::enqueue(new state_change_cmd(m_state, m_hdl)); + } + + if (m_table_id) { + HW::enqueue(new set_table_cmd(m_table_id, l3_proto_t::IPV4, m_hdl)); + HW::enqueue(new set_table_cmd(m_table_id, l3_proto_t::IPV6, m_hdl)); + } +} + +interface::~interface() +{ + sweep(); + release(); +} + +void +interface::release() +{ + // not in the DB anymore. + m_db.release(m_name, this); +} + +std::string +interface::to_string() const +{ + std::ostringstream s; + s << "interface:[" << m_name << " type:" << m_type.to_string() + << " hdl:" << m_hdl.to_string() + << " l2-address:" << m_l2_address.to_string(); + + if (m_rd) { + s << " rd:" << m_rd->to_string(); + } + + s << " admin-state:" << m_state.to_string() + << " oper-state:" << m_oper.to_string() << "]"; + + return (s.str()); +} + +const std::string& +interface::name() const +{ + return (m_name); +} + +const interface::key_type& +interface::key() const +{ + return (name()); +} + +std::queue& +interface::mk_create_cmd(std::queue& q) +{ + if (type_t::LOOPBACK == m_type) { + q.push(new loopback_create_cmd(m_hdl, m_name)); + } else if (type_t::BVI == m_type) { + q.push(new loopback_create_cmd(m_hdl, m_name)); + q.push(new set_tag(m_hdl, m_name)); + } else if (type_t::AFPACKET == m_type) { + q.push(new af_packet_create_cmd(m_hdl, m_name)); + } else if (type_t::TAP == m_type) { + q.push(new tap_create_cmd(m_hdl, m_name)); + } + + return (q); +} + +std::queue& +interface::mk_delete_cmd(std::queue& q) +{ + if ((type_t::LOOPBACK == m_type) || (type_t::BVI == m_type)) { + q.push(new loopback_delete_cmd(m_hdl)); + } else if (type_t::AFPACKET == m_type) { + q.push(new af_packet_delete_cmd(m_hdl, m_name)); + } else if (type_t::TAP == m_type) { + q.push(new tap_delete_cmd(m_hdl)); + } + + return (q); +} + +void +interface::update(const interface& desired) +{ + /* + * the desired state is always that the interface should be created + */ + if (rc_t::OK != m_hdl.rc()) { + std::queue cmds; + HW::enqueue(mk_create_cmd(cmds)); + } + + /* + * change the interface state to that which is deisred + */ + if (m_state.update(desired.m_state)) { + HW::enqueue(new state_change_cmd(m_state, m_hdl)); + } + + /* + * change the interface state to that which is deisred + */ + if (m_l2_address.update(desired.m_l2_address)) { + HW::enqueue(new set_mac_cmd(m_l2_address, m_hdl)); + } + + /* + * If the interface is mapped into a route domain, set VPP's + * table ID + */ + if (!m_table_id && m_rd) { + HW::enqueue(new set_table_cmd(m_table_id, l3_proto_t::IPV4, m_hdl)); + HW::enqueue(new set_table_cmd(m_table_id, l3_proto_t::IPV6, m_hdl)); + } +} + +void +interface::set(const l2_address_t& addr) +{ + assert(rc_t::UNSET == m_l2_address.rc()); + m_l2_address.set(rc_t::NOOP); + m_l2_address.update(addr); +} + +void +interface::set(const oper_state_t& state) +{ + m_oper = state; +} + +std::shared_ptr +interface::singular_i() const +{ + return (m_db.find_or_add(name(), *this)); +} + +std::shared_ptr +interface::singular() const +{ + return singular_i(); +} + +std::shared_ptr +interface::find(const std::string& name) +{ + return (m_db.find(name)); +} + +std::shared_ptr +interface::find(const handle_t& handle) +{ + return (m_hdl_db[handle].lock()); +} + +void +interface::add(const std::string& name, const HW::item& item) +{ + std::shared_ptr sp = find(name); + + if (sp && item) { + m_hdl_db[item.data()] = sp; + } +} + +void +interface::remove(const HW::item& item) +{ + m_hdl_db.erase(item.data()); +} + +void +interface::dump(std::ostream& os) +{ + m_db.dump(os); +} + +void +interface::event_handler::handle_populate(const client_db::key_t& key) +{ + /* + * dump VPP current states + */ + std::shared_ptr cmd(new interface::dump_cmd()); + + HW::enqueue(cmd); + HW::write(); + + for (auto& itf_record : *cmd) { + std::unique_ptr itf = + interface::new_interface(itf_record.get_payload()); + + if (itf && interface::type_t::LOCAL != itf->type()) { + VOM_LOG(log_level_t::DEBUG) << "dump: " << itf->to_string(); + /* + * Write each of the discovered interfaces into the OM, + * but disable the HW Command q whilst we do, so that no + * commands are sent to VPP + */ + OM::commit(key, *itf); + + /** + * Get the address configured on the interface + */ + std::shared_ptr dcmd = + std::make_shared( + l3_binding::dump_v4_cmd(itf->handle())); + + HW::enqueue(dcmd); + HW::write(); + + for (auto& l3_record : *dcmd) { + auto& payload = l3_record.get_payload(); + const route::prefix_t pfx(payload.is_ipv6, payload.ip, + payload.prefix_length); + + VOM_LOG(log_level_t::DEBUG) << "dump: " << pfx.to_string(); + + l3_binding l3(*itf, pfx); + OM::commit(key, l3); + } + } + } +} + +interface::event_handler::event_handler() +{ + OM::register_listener(this); + inspect::register_handler({ "interface", "intf" }, "interfaces", this); +} + +void +interface::event_handler::handle_replay() +{ + m_db.replay(); +} + +dependency_t +interface::event_handler::order() const +{ + return (dependency_t::INTERFACE); +} + +void +interface::event_handler::show(std::ostream& os) +{ + m_db.dump(os); +} +} +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/interface.hpp b/src/vpp-api/vom/interface.hpp new file mode 100644 index 00000000000..8863bc94868 --- /dev/null +++ b/src/vpp-api/vom/interface.hpp @@ -0,0 +1,1002 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_INTERFACE_H__ +#define __VOM_INTERFACE_H__ + +#include "vom/dump_cmd.hpp" +#include "vom/enum_base.hpp" +#include "vom/event_cmd.hpp" +#include "vom/hw.hpp" +#include "vom/inspect.hpp" +#include "vom/object_base.hpp" +#include "vom/om.hpp" +#include "vom/prefix.hpp" +#include "vom/route_domain.hpp" +#include "vom/rpc_cmd.hpp" +#include "vom/singular_db.hpp" + +#include +#include +#include +#include +#include +#include + +namespace VOM { +/** + * A representation of an interface in VPP + */ +class interface : public object_base +{ +public: + /** + * The key for interface's key + */ + typedef std::string key_type; + + /** + * The iterator type + */ + typedef singular_db::const_iterator + const_iterator_t; + + /** + * An interface type + */ + struct type_t : enum_base + { + /** + * Unkown type + */ + const static type_t UNKNOWN; + /** + * A brideged Virtual interface (aka SVI or IRB) + */ + const static type_t BVI; + /** + * VXLAN interface + */ + const static type_t VXLAN; + /** + * Ethernet interface type + */ + const static type_t ETHERNET; + /** + * AF-Packet interface type + */ + const static type_t AFPACKET; + /** + * loopback interface type + */ + const static type_t LOOPBACK; + /** + * Local interface type (specific to VPP) + */ + const static type_t LOCAL; + /** + * TAP interface type + */ + const static type_t TAP; + + /** + * Convert VPP's name of the interface to a type + */ + static type_t from_string(const std::string& str); + + private: + /** + * Private constructor taking the value and the string name + */ + type_t(int v, const std::string& s); + }; + + /** + * The admin state of the interface + */ + struct admin_state_t : enum_base + { + /** + * Admin DOWN state + */ + const static admin_state_t DOWN; + /** + * Admin UP state + */ + const static admin_state_t UP; + + /** + * Convert VPP's numerical value to enum type + */ + static admin_state_t from_int(uint8_t val); + + private: + /** + * Private constructor taking the value and the string name + */ + admin_state_t(int v, const std::string& s); + }; + + /** + * The oper state of the interface + */ + struct oper_state_t : enum_base + { + /** + * Operational DOWN state + */ + const static oper_state_t DOWN; + /** + * Operational UP state + */ + const static oper_state_t UP; + + /** + * Convert VPP's numerical value to enum type + */ + static oper_state_t from_int(uint8_t val); + + private: + /** + * Private constructor taking the value and the string name + */ + oper_state_t(int v, const std::string& s); + }; + + /** + * Construct a new object matching the desried state + */ + interface(const std::string& name, type_t type, admin_state_t state); + /** + * Construct a new object matching the desried state mapped + * to a specific route_domain + */ + interface(const std::string& name, + type_t type, + admin_state_t state, + const route_domain& rd); + /** + * Destructor + */ + virtual ~interface(); + + /** + * Copy Constructor + */ + interface(const interface& o); + + static const_iterator_t cbegin(); + static const_iterator_t cend(); + + /** + * Return the matching'singular' of the interface + */ + std::shared_ptr singular() const; + + /** + * convert to string format for debug purposes + */ + virtual std::string to_string(void) const; + + /** + * Return VPP's handle to this object + */ + const handle_t& handle() const; + + /** + * Return the interface type + */ + const type_t& type() const; + + /** + * Return the interface type + */ + const std::string& name() const; + + /** + * Return the interface type + */ + const key_type& key() const; + + /** + * Return the L2 Address + */ + const l2_address_t& l2_address() const; + + /** + * Set the L2 Address + */ + void set(const l2_address_t& addr); + + /** + * Set the operational state of the interface, as reported by VPP + */ + void set(const oper_state_t& state); + + /** + * A base class for interface Create commands + */ + template + class create_cmd : public rpc_cmd, HW::item, MSG> + { + public: + create_cmd(HW::item& item, const std::string& name) + : rpc_cmd, HW::item, MSG>(item) + , m_name(name) + { + } + + /** + * Destructor + */ + virtual ~create_cmd() = default; + + /** + * Comparison operator - only used for UT + */ + virtual bool operator==(const create_cmd& o) const + { + return (m_name == o.m_name); + } + + /** + * Indicate the succeeded, when the HW Q is disabled. + */ + void succeeded() + { + rpc_cmd, HW::item, MSG>::succeeded(); + interface::add(m_name, this->item()); + } + + virtual vapi_error_e operator()(MSG& reply) + { + int sw_if_index = reply.get_response().get_payload().sw_if_index; + int retval = reply.get_response().get_payload().retval; + + VOM_LOG(log_level_t::DEBUG) << this->to_string() << " " << retval; + + rc_t rc = rc_t::from_vpp_retval(retval); + handle_t handle = handle_t::INVALID; + + if (rc_t::OK == rc) { + handle = sw_if_index; + } + + HW::item res(handle, rc); + + this->fulfill(res); + + return (VAPI_OK); + } + + protected: + /** + * The name of the interface to be created + */ + const std::string& m_name; + }; + + /** + * A command class to create Loopback interfaces in VPP + */ + class loopback_create_cmd : public create_cmd + { + public: + /** + * Constructor taking the HW::item to update + * and the name of the interface to create + */ + loopback_create_cmd(HW::item& item, const std::string& name); + ~loopback_create_cmd() = default; + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + }; + + /** + * A command class to create af_packet interfaces in VPP + */ + class af_packet_create_cmd : public create_cmd + { + public: + /** + * Constructor taking the HW::item to update + * and the name of the interface to create + */ + af_packet_create_cmd(HW::item& item, const std::string& name); + ~af_packet_create_cmd() = default; + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + }; + + /** + * A command class to create TAP interfaces in VPP + */ + class tap_create_cmd : public create_cmd + { + public: + /** + * Constructor taking the HW::item to update + * and the name of the interface to create + */ + tap_create_cmd(HW::item& item, const std::string& name); + ~tap_create_cmd() = default; + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + }; + + /** + * Base class for intterface Delete commands + */ + template + class delete_cmd : public rpc_cmd, HW::item, MSG> + { + public: + delete_cmd(HW::item& item, const std::string& name) + : rpc_cmd, HW::item, MSG>(item) + , m_name(name) + { + } + + delete_cmd(HW::item& item) + : rpc_cmd, HW::item, MSG>(item) + , m_name() + { + } + + /** + * Destructor + */ + virtual ~delete_cmd() = default; + + /** + * Comparison operator - only used for UT + */ + virtual bool operator==(const delete_cmd& o) const + { + return (this->m_hw_item == o.m_hw_item); + } + + /** + * Indicate the succeeded, when the HW Q is disabled. + */ + void succeeded() {} + + protected: + /** + * The name of the interface to be created + */ + const std::string m_name; + }; + + /** + * A command class to delete loopback interfaces in VPP + */ + class loopback_delete_cmd : public delete_cmd + { + public: + /** + * Constructor taking the HW::item to update + */ + loopback_delete_cmd(HW::item& item); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + }; + + /** + * A command class to delete af-packet interfaces in VPP + */ + class af_packet_delete_cmd : public delete_cmd + { + public: + /** + * Constructor taking the HW::item to update + * and the name of the interface to delete + */ + af_packet_delete_cmd(HW::item& item, const std::string& name); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + }; + + /** + * A command class to delete TAP interfaces in VPP + */ + class tap_delete_cmd : public delete_cmd + { + public: + /** + * Constructor taking the HW::item to update + */ + tap_delete_cmd(HW::item& item); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + }; + + /** + * A command class to delete TAP interfaces in VPP + */ + class set_tag + : public rpc_cmd, rc_t, vapi::Sw_interface_tag_add_del> + { + public: + /** + * Constructor taking the HW::item to update + */ + set_tag(HW::item& item, const std::string& name); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const set_tag& i) const; + + private: + /** + * The tag to add + */ + const std::string m_name; + }; + + /** + * A cmd class that changes the admin state + */ + class state_change_cmd : public rpc_cmd, + rc_t, + vapi::Sw_interface_set_flags> + { + public: + /** + * Constructor taking the HW::item to update + * and the name handle of the interface whose state is to change + */ + state_change_cmd(HW::item& s, const HW::item& h); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const state_change_cmd& i) const; + + private: + /** + * the handle of the interface to update + */ + const HW::item& m_hdl; + }; + + /** + * A command class that binds an interface to an L3 table + */ + class set_table_cmd : public rpc_cmd, + rc_t, + vapi::Sw_interface_set_table> + { + public: + /** + * Constructor taking the HW::item to update + * and the name handle of the interface whose table is to change + */ + set_table_cmd(HW::item& item, + const l3_proto_t& proto, + const HW::item& h); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const set_table_cmd& i) const; + + private: + /** + * the handle of the interface to update + */ + const HW::item& m_hdl; + + /** + * The L3 protocol of the table + */ + l3_proto_t m_proto; + }; + + /** + * A command class that binds an interface to an L3 table + */ + class set_mac_cmd : public rpc_cmd, + rc_t, + vapi::Sw_interface_set_mac_address> + { + public: + /** + * Constructor taking the HW::item to update + * and the handle of the interface + */ + set_mac_cmd(HW::item& item, const HW::item& h); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const set_mac_cmd& i) const; + + private: + /** + * the handle of the interface to update + */ + const HW::item& m_hdl; + }; + + /** + * Forward declaration of the Event command + */ + class events_cmd; + + /** + * A class that listens to interface Events + */ + class event_listener + { + public: + /** + * Default Constructor + */ + event_listener(); + + /** + * Virtual function called on the listener when the command has data + * ready to process + */ + virtual void handle_interface_event(events_cmd* cmd) = 0; + + /** + * Return the HW::item representing the status + */ + HW::item& status(); + + protected: + /** + * The status of the subscription + */ + HW::item m_status; + }; + + /** + * A command class represents our desire to recieve interface events + */ + class events_cmd + : public event_cmd + { + public: + /** + * Constructor taking the listner to notify + */ + events_cmd(event_listener& el); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * Retires the command - unsubscribe from the events. + */ + void retire(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const events_cmd& i) const; + + /** + * Called when it's time to poke the listeners + */ + void notify(); + + private: + /** + * The listeners to notify when data/events arrive + */ + event_listener& m_listener; + }; + + /** + * Forward declaration of the stat command + */ + class stats_cmd; + + /** + * A class that listens to interface Stats + */ + class stat_listener + { + public: + /** + * Default Constructor + */ + stat_listener(); + + /** + * Virtual function called on the listener when the command has data + * ready to process + */ + virtual void handle_interface_stat(stats_cmd* cmd) = 0; + + /** + * Return the HW::item representing the status + */ + HW::item& status(); + + protected: + /** + * The status of the subscription + */ + HW::item m_status; + }; + + /** + * A command class represents our desire to recieve interface stats + */ + class stats_cmd : public event_cmd + { + public: + /** + * Constructor taking the listner to notify + */ + stats_cmd(stat_listener& el, const std::vector& interfaces); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * Retires the command - unsubscribe from the stats. + */ + void retire(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const stats_cmd& i) const; + + /** + * Called when it's time to poke the listeners + */ + void notify(); + + private: + /** + * The listeners to notify when data/stats arrive + */ + stat_listener& m_listener; + + std::vector m_swifindex; + }; + + /** + * A cmd class that Dumps all the Vpp interfaces + */ + class dump_cmd : public VOM::dump_cmd + { + public: + /** + * Default Constructor + */ + dump_cmd(); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const dump_cmd& i) const; + }; + + /** + * The the singular instance of the interface in the object_base-Model + */ + static std::shared_ptr find(const interface& temp); + + /** + * The the singular instance of the interface in the object_base-Model + * by handle + */ + static std::shared_ptr find(const handle_t& h); + + /** + * The the singular instance of the interface in the object_base-Model + * by name + */ + static std::shared_ptr find(const std::string& s); + + /** + * Dump all interfaces into the stream provided + */ + static void dump(std::ostream& os); + + /** + * Factory method to construct a new interface from the VPP record + */ + static std::unique_ptr new_interface( + const vapi_payload_sw_interface_details& vd); + +protected: + /** + * Construct an interface object with a handle and a HW address + */ + interface(const handle_t& handle, + const l2_address_t& l2_address, + const std::string& name, + type_t type, + admin_state_t state); + + /** + * The SW interface handle VPP has asigned to the interface + */ + HW::item m_hdl; + + /** + * Return the matching 'singular' of the interface + */ + virtual std::shared_ptr singular_i() const; + + /** + * release/remove an interface form the singular store + */ + void release(); + + /** + * Virtual functions to construct an interface create commands. + * Overridden in derived classes like the sub_interface + */ + virtual std::queue& mk_create_cmd(std::queue& cmds); + + /** + * Virtual functions to construct an interface delete commands. + * Overridden in derived classes like the sub_interface + */ + virtual std::queue& mk_delete_cmd(std::queue& cmds); + + /** + * Sweep/reap the object if still stale + */ + virtual void sweep(void); + + /** + * A map of all interfaces key against the interface's name + */ + static singular_db m_db; + + /** + * Add an interface to the DB keyed on handle + */ + static void add(const std::string& name, const HW::item& item); + + /** + * remove an interface from the DB keyed on handle + */ + static void remove(const HW::item& item); + +private: + /** + * Class definition for listeners to OM events + */ + class event_handler : public OM::listener, public inspect::command_handler + { + public: + event_handler(); + virtual ~event_handler() = default; + + /** + * Handle a populate event + */ + void handle_populate(const client_db::key_t& key); + + /** + * Handle a replay event + */ + void handle_replay(); + + /** + * Show the object in the Singular DB + */ + void show(std::ostream& os); + + /** + * Get the sortable Id of the listener + */ + dependency_t order() const; + }; + + static event_handler m_evh; + + /** + * Commit the acculmulated changes into VPP. i.e. to a 'HW" write. + */ + void update(const interface& obj); + + /* + * It's the OM class that calls singular() + */ + friend class OM; + + /** + * It's the singular_db class that calls replay() + */ + friend class singular_db; + + /** + * The interfaces name + */ + const std::string m_name; + + /** + * The interface type. clearly this cannot be changed + * once the interface has been created. + */ + const type_t m_type; + + /** + * shared pointer to the routeDoamin the interface is in. + * NULL is not mapped - i.e. in eht default table + */ + const std::shared_ptr m_rd; + + /** + * The state of the interface + */ + HW::item m_state; + + /** + * HW state of the VPP table mapping + */ + HW::item m_table_id; + + /** + * HW state of the L2 address + */ + HW::item m_l2_address; + + /** + * Operational state of the interface + */ + oper_state_t m_oper; + + /** + * A map of all interfaces keyed against VPP's handle + */ + static std::map> m_hdl_db; + + /** + * replay the object to create it in hardware + */ + virtual void replay(void); + + /** + * Create commands are firends so they can add interfaces to the + * handle store. + */ + template + friend class create_cmd; + + /** + * Create commands are firends so they can remove interfaces from the + * handle store. + */ + template + friend class delete_cmd; +}; +}; +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ +#endif diff --git a/src/vpp-api/vom/interface_cmds.cpp b/src/vpp-api/vom/interface_cmds.cpp new file mode 100644 index 00000000000..306f987429b --- /dev/null +++ b/src/vpp-api/vom/interface_cmds.cpp @@ -0,0 +1,544 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/interface.hpp" +#include "vom/cmd.hpp" + +DEFINE_VAPI_MSG_IDS_VPE_API_JSON; +DEFINE_VAPI_MSG_IDS_INTERFACE_API_JSON; +DEFINE_VAPI_MSG_IDS_AF_PACKET_API_JSON; +DEFINE_VAPI_MSG_IDS_TAP_API_JSON; +DEFINE_VAPI_MSG_IDS_STATS_API_JSON; + +namespace VOM { +interface::loopback_create_cmd::loopback_create_cmd(HW::item& item, + const std::string& name) + : create_cmd(item, name) +{ +} + +rc_t +interface::loopback_create_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + VAPI_CALL(req.execute()); + + m_hw_item = wait(); + + if (m_hw_item.rc() == rc_t::OK) { + interface::add(m_name, m_hw_item); + } + + return rc_t::OK; +} +std::string +interface::loopback_create_cmd::to_string() const +{ + std::ostringstream s; + s << "loopback-itf-create: " << m_hw_item.to_string() << " name:" << m_name; + + return (s.str()); +} + +interface::af_packet_create_cmd::af_packet_create_cmd(HW::item& item, + const std::string& name) + : create_cmd(item, name) +{ +} + +rc_t +interface::af_packet_create_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + + payload.use_random_hw_addr = 1; + memset(payload.host_if_name, 0, sizeof(payload.host_if_name)); + memcpy(payload.host_if_name, m_name.c_str(), + std::min(m_name.length(), sizeof(payload.host_if_name))); + + VAPI_CALL(req.execute()); + + m_hw_item = wait(); + + if (m_hw_item.rc() == rc_t::OK) { + interface::add(m_name, m_hw_item); + } + + return rc_t::OK; +} +std::string +interface::af_packet_create_cmd::to_string() const +{ + std::ostringstream s; + s << "af-packet-itf-create: " << m_hw_item.to_string() << " name:" << m_name; + + return (s.str()); +} + +interface::tap_create_cmd::tap_create_cmd(HW::item& item, + const std::string& name) + : create_cmd(item, name) +{ +} + +rc_t +interface::tap_create_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + + memset(payload.tap_name, 0, sizeof(payload.tap_name)); + memcpy(payload.tap_name, m_name.c_str(), + std::min(m_name.length(), sizeof(payload.tap_name))); + payload.use_random_mac = 1; + + VAPI_CALL(req.execute()); + + m_hw_item = wait(); + + if (m_hw_item.rc() == rc_t::OK) { + interface::add(m_name, m_hw_item); + } + + return rc_t::OK; +} + +std::string +interface::tap_create_cmd::to_string() const +{ + std::ostringstream s; + s << "tap-intf-create: " << m_hw_item.to_string() << " name:" << m_name; + + return (s.str()); +} + +interface::loopback_delete_cmd::loopback_delete_cmd(HW::item& item) + : delete_cmd(item) +{ +} + +rc_t +interface::loopback_delete_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_hw_item.data().value(); + + VAPI_CALL(req.execute()); + + wait(); + m_hw_item.set(rc_t::NOOP); + + interface::remove(m_hw_item); + return rc_t::OK; +} + +std::string +interface::loopback_delete_cmd::to_string() const +{ + std::ostringstream s; + s << "loopback-itf-delete: " << m_hw_item.to_string(); + + return (s.str()); +} + +interface::af_packet_delete_cmd::af_packet_delete_cmd(HW::item& item, + const std::string& name) + : delete_cmd(item, name) +{ +} + +rc_t +interface::af_packet_delete_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + memset(payload.host_if_name, 0, sizeof(payload.host_if_name)); + memcpy(payload.host_if_name, m_name.c_str(), + std::min(m_name.length(), sizeof(payload.host_if_name))); + + VAPI_CALL(req.execute()); + + wait(); + m_hw_item.set(rc_t::NOOP); + + interface::remove(m_hw_item); + return rc_t::OK; +} +std::string +interface::af_packet_delete_cmd::to_string() const +{ + std::ostringstream s; + s << "af_packet-itf-delete: " << m_hw_item.to_string(); + + return (s.str()); +} + +interface::tap_delete_cmd::tap_delete_cmd(HW::item& item) + : delete_cmd(item) +{ +} + +rc_t +interface::tap_delete_cmd::issue(connection& con) +{ + // finally... call VPP + + interface::remove(m_hw_item); + return rc_t::OK; +} +std::string +interface::tap_delete_cmd::to_string() const +{ + std::ostringstream s; + s << "tap-itf-delete: " << m_hw_item.to_string(); + + return (s.str()); +} + +interface::state_change_cmd::state_change_cmd( + HW::item& state, + const HW::item& hdl) + : rpc_cmd(state) + , m_hdl(hdl) +{ +} + +bool +interface::state_change_cmd::operator==(const state_change_cmd& other) const +{ + return ((m_hdl == other.m_hdl) && (m_hw_item == other.m_hw_item)); +} + +rc_t +interface::state_change_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_hdl.data().value(); + payload.admin_up_down = m_hw_item.data().value(); + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return rc_t::OK; +} + +std::string +interface::state_change_cmd::to_string() const +{ + std::ostringstream s; + s << "itf-state-change: " << m_hw_item.to_string() + << " hdl:" << m_hdl.to_string(); + return (s.str()); +} + +interface::set_table_cmd::set_table_cmd(HW::item& table, + const l3_proto_t& proto, + const HW::item& hdl) + : rpc_cmd(table) + , m_hdl(hdl) + , m_proto(proto) +{ +} + +bool +interface::set_table_cmd::operator==(const set_table_cmd& other) const +{ + return ((m_hdl == other.m_hdl) && (m_proto == other.m_proto) && + (m_hw_item == other.m_hw_item)); +} + +rc_t +interface::set_table_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_hdl.data().value(); + payload.is_ipv6 = m_proto.is_ipv6(); + payload.vrf_id = m_hw_item.data(); + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return (rc_t::OK); +} + +std::string +interface::set_table_cmd::to_string() const +{ + std::ostringstream s; + s << "itf-set-table: " << m_hw_item.to_string() + << " proto:" << m_proto.to_string() << " hdl:" << m_hdl.to_string(); + return (s.str()); +} + +interface::set_mac_cmd::set_mac_cmd(HW::item& mac, + const HW::item& hdl) + : rpc_cmd(mac) + , m_hdl(hdl) +{ +} + +bool +interface::set_mac_cmd::operator==(const set_mac_cmd& other) const +{ + return ((m_hdl == other.m_hdl) && (m_hw_item == other.m_hw_item)); +} + +rc_t +interface::set_mac_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_hdl.data().value(); + m_hw_item.data().to_mac().to_bytes(payload.mac_address, + sizeof(payload.mac_address)); + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return (rc_t::OK); +} + +std::string +interface::set_mac_cmd::to_string() const +{ + std::ostringstream s; + s << "itf-set-mac: " << m_hw_item.to_string() << " hdl:" << m_hdl.to_string(); + return (s.str()); +} + +interface::events_cmd::events_cmd(event_listener& el) + : event_cmd(el.status()) + , m_listener(el) +{ +} + +bool +interface::events_cmd::operator==(const events_cmd& other) const +{ + return (true); +} + +rc_t +interface::events_cmd::issue(connection& con) +{ + /* + * First set the call back to handle the interface events + */ + m_reg.reset(new reg_t(con.ctx(), std::ref(*(static_cast(this))))); + + /* + * then send the request to enable them + */ + msg_t req(con.ctx(), std::ref(*(static_cast(this)))); + + auto& payload = req.get_request().get_payload(); + payload.enable_disable = 1; + payload.pid = getpid(); + + VAPI_CALL(req.execute()); + + wait(); + + return (rc_t::INPROGRESS); +} + +void +interface::events_cmd::retire(connection& con) +{ + /* + * disable interface events. + */ + msg_t req(con.ctx(), std::ref(*(static_cast(this)))); + + auto& payload = req.get_request().get_payload(); + payload.enable_disable = 0; + payload.pid = getpid(); + + VAPI_CALL(req.execute()); + + wait(); +} + +void +interface::events_cmd::notify() +{ + m_listener.handle_interface_event(this); +} + +std::string +interface::events_cmd::to_string() const +{ + return ("itf-events"); +} + +/** + * Interface statistics + */ +interface::stats_cmd::stats_cmd(stat_listener& el, + const std::vector& interfaces) + : event_cmd(el.status()) + , m_listener(el) + , m_swifindex(interfaces) +{ +} + +bool +interface::stats_cmd::operator==(const stats_cmd& other) const +{ + return (true); +} + +rc_t +interface::stats_cmd::issue(connection& con) +{ + /* + * First set the clal back to handle the interface stats + */ + m_reg.reset(new reg_t(con.ctx(), std::ref(*(static_cast(this))))); + // m_reg->execute(); + + /* + * then send the request to enable them + */ + msg_t req(con.ctx(), m_swifindex.size(), + std::ref(*(static_cast(this)))); + + auto& payload = req.get_request().get_payload(); + payload.enable_disable = 1; + payload.pid = getpid(); + payload.num = m_swifindex.size(); + + auto it = m_swifindex.cbegin(); + uint32_t ii = 0; + while (it != m_swifindex.cend()) { + payload.sw_ifs[ii] = it->value(); + ++it; + ++ii; + } + + VAPI_CALL(req.execute()); + + wait(); + + return (rc_t::INPROGRESS); +} + +void +interface::stats_cmd::retire(connection& con) +{ +} + +void +interface::stats_cmd::notify() +{ + m_listener.handle_interface_stat(this); +} + +std::string +interface::stats_cmd::to_string() const +{ + return ("itf-stats"); +} + +interface::dump_cmd::dump_cmd() +{ +} + +bool +interface::dump_cmd::operator==(const dump_cmd& other) const +{ + return (true); +} + +rc_t +interface::dump_cmd::issue(connection& con) +{ + m_dump.reset(new msg_t(con.ctx(), std::ref(*this))); + + auto& payload = m_dump->get_request().get_payload(); + payload.name_filter_valid = 0; + + VAPI_CALL(m_dump->execute()); + + wait(); + + return rc_t::OK; +} + +std::string +interface::dump_cmd::to_string() const +{ + return ("itf-dump"); +} + +interface::set_tag::set_tag(HW::item& item, const std::string& name) + : rpc_cmd(item) + , m_name(name) +{ +} + +rc_t +interface::set_tag::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.is_add = 1; + payload.sw_if_index = m_hw_item.data().value(); + memcpy(payload.tag, m_name.c_str(), m_name.length()); + + VAPI_CALL(req.execute()); + + wait(); + + return rc_t::OK; +} +std::string +interface::set_tag::to_string() const +{ + std::ostringstream s; + s << "itf-set-tag: " << m_hw_item.to_string() << " name:" << m_name; + + return (s.str()); +} + +bool +interface::set_tag::operator==(const set_tag& o) const +{ + return ((m_name == o.m_name) && (m_hw_item.data() == o.m_hw_item.data())); +} +} +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/interface_factory.cpp b/src/vpp-api/vom/interface_factory.cpp new file mode 100644 index 00000000000..aac968c1b3a --- /dev/null +++ b/src/vpp-api/vom/interface_factory.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "vom/interface.hpp" +#include "vom/sub_interface.hpp" +#include "vom/tap_interface.hpp" + +namespace VOM { +std::unique_ptr +interface::new_interface(const vapi_payload_sw_interface_details& vd) +{ + std::unique_ptr up_itf; + + /** + * Determine the interface type from the name and VLAN attributes + */ + std::string name = reinterpret_cast(vd.interface_name); + type_t type = interface::type_t::from_string(name); + admin_state_t state = interface::admin_state_t::from_int(vd.link_up_down); + handle_t hdl(vd.sw_if_index); + l2_address_t l2_address(vd.l2_address, vd.l2_address_length); + + if (type_t::AFPACKET == type) { + /* + * need to strip VPP's "host-" prefix from the interface name + */ + name = name.substr(5); + } + /** + * if the tag is set, then we wrote that to specify a name to make + * the interface type more specific + */ + if (vd.tag[0] != 0) { + name = std::string(reinterpret_cast(vd.tag)); + type = interface::type_t::from_string(name); + } + + /* + * pull out the other special cases + */ + if (type_t::TAP == type) { + /* + * TAP interface + */ + up_itf.reset(new tap_interface(hdl, name, state, route::prefix_t())); + } else if ((name.find(".") != std::string::npos) && (0 != vd.sub_id)) { + /* + * Sub-interface + * split the name into the parent and VLAN + */ + std::vector parts; + boost::split(parts, name, boost::is_any_of(".")); + + interface parent(parts[0], type, state); + up_itf.reset(new sub_interface(hdl, parent, state, vd.sub_id)); + } else if (type_t::VXLAN == type) { + /* + * there's not enough inforation in a SW interface record to + * construct + * a VXLAN tunnel. so skip it. + */ + } else { + up_itf.reset(new interface(hdl, l2_address, name, type, state)); + } + + return (up_itf); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/interface_ip6_nd.hpp b/src/vpp-api/vom/interface_ip6_nd.hpp new file mode 100644 index 00000000000..38845e36870 --- /dev/null +++ b/src/vpp-api/vom/interface_ip6_nd.hpp @@ -0,0 +1,385 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_INTERFACE_IP6_ND_H__ +#define __VOM_INTERFACE_IP6_ND_H__ + +#include "vom/dump_cmd.hpp" +#include "vom/hw.hpp" +#include "vom/inspect.hpp" +#include "vom/interface.hpp" +#include "vom/object_base.hpp" +#include "vom/om.hpp" +#include "vom/ra_config.hpp" +#include "vom/ra_prefix.hpp" +#include "vom/rpc_cmd.hpp" +#include "vom/singular_db.hpp" +#include "vom/sub_interface.hpp" + +#include + +namespace VOM { +/** + * A representation of L3 configuration on an interface + */ +template +class interface_ip6_nd : public object_base +{ +public: + typedef CLASS class_t; + /** + * Construct a new object matching the desried state + */ + interface_ip6_nd(const interface& itf, const class_t cls) + : m_itf(itf.singular()) + , m_cls(cls) + , m_config(true) + { + } + + /** + * Copy Constructor + */ + interface_ip6_nd(const interface_ip6_nd& o) + : m_itf(o.m_itf) + , m_cls(o.m_cls) + , m_config(o.m_config) + { + } + + /** + * Destructor + */ + ~interface_ip6_nd() + { + sweep(); + m_db.release(m_itf->key(), this); + } + + /** + * Return the 'singular instance' of the interface ip6nd that matches + * this object + */ + std::shared_ptr singular() const + { + return find_or_add(*this); + } + + /** + * convert to string format for debug purposes + */ + std::string to_string() const + { + std::ostringstream s; + s << "interface-ip6-nd:[" + << " itf:" << m_itf->to_string() << " " << m_cls.to_string() << " " + << m_config.to_string() << "]"; + + return (s.str()); + } + + /** + * Dump all config into the stream provided + */ + static void dump(std::ostream& os) { m_db.dump(os); } + + /** + * The key type for interface ip6 nd + */ + typedef interface::key_type key_t; + + /** + * Find an singular instance in the DB for the interface passed + */ + static std::shared_ptr find(const interface& i) + { + /* + * Loop throught the entire map looking for matching interface. + * not the most efficient algorithm, but it will do for now. The + * number of ra configs is low. + */ + std::deque> rac; + + auto it = m_db.cbegin(); + + while (it != m_db.cend()) { + /* + * The key in the DB is a pair of the interface's name. + * If the keys match, save the ra-config + */ + auto key = it->first; + + if (i.key() == key.first) { + rac.push_back(it->second.lock()); + } + + ++it; + } + + return (rac); + } + + /** + * A functor class that binds the ra config to the interface + */ + class config_cmd : public rpc_cmd, rc_t, CMD> + { + public: + /** + * Constructor + */ + config_cmd(HW::item& item, const handle_t& itf, const class_t& cls) + : rpc_cmd, rc_t, CMD>(item) + , m_itf(itf) + , m_cls(cls) + { + } + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const + { + std::ostringstream s; + s << "interface-ip6-nd: " << this->item().to_string() + << " itf:" << m_itf.to_string() << " " << m_cls.to_string(); + + return (s.str()); + } + + /** + * Comparison operator - only used for UT + */ + bool operator==(const config_cmd& other) const + { + return ((m_itf == other.m_itf) && (m_cls == other.m_cls)); + } + + private: + /** + * Reference to the interface to bind to + */ + const handle_t& m_itf; + + /** + * Reference to the config class + */ + const class_t& m_cls; + }; + + /** + * A cmd class that Unbinds L3 Config from an interface + */ + class unconfig_cmd : public rpc_cmd, rc_t, CMD> + { + public: + /** + * Constructor + */ + unconfig_cmd(HW::item& item, const handle_t& itf, const class_t& cls) + : rpc_cmd, rc_t, CMD>(item) + , m_itf(itf) + , m_cls(cls) + { + } + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const + { + std::ostringstream s; + s << "interface-ip6-nd: " << this->item().to_string() + << " itf:" << m_itf.to_string() << " " << m_cls.to_string(); + + return (s.str()); + } + + /** + * Comparison operator - only used for UT + */ + bool operator==(const unconfig_cmd& other) const + { + return ((m_itf == other.m_itf) && (m_cls == other.m_cls)); + } + + private: + /** + * Reference to the interface to unbind fomr + */ + const handle_t& m_itf; + + /** + * Reference to the config class to undo configurations + */ + const class_t& m_cls; + }; + +private: + /** + * Class definition for listeners to OM events + */ + class event_handler : public OM::listener, public inspect::command_handler + { + public: + event_handler() + { + OM::register_listener(this); + inspect::register_handler({ "ip6_nd " }, "interface ip6 nd", this); + } + + virtual ~event_handler() = default; + + /** + * Handle a populate event + */ + void handle_populate(const client_db::key_t& key) + { + /** + * VPP provides no dump for ra config + */ + } + + /** + * Handle a replay event + */ + void handle_replay() { m_db.replay(); } + + /** + * Show the object in the Singular DB + */ + void show(std::ostream& os) { m_db.dump(os); } + + /** + * Get the sortable Id of the listener + */ + dependency_t order() const { return (dependency_t::BINDING); } + }; + + /** + * event_handler to register with OM + */ + static event_handler m_evh; + + /** + * Enqueue commands to the VPP for the update + */ + void update(const interface_ip6_nd& obj) + { + if (!m_config) { + HW::enqueue(new config_cmd(m_config, m_itf->handle(), m_cls)); + } + } + + void sweep() + { + if (m_config) { + HW::enqueue(new unconfig_cmd(m_config, m_itf->handle(), m_cls)); + } + HW::write(); + } + + /** + * Replay the objects state to HW + */ + void replay(void) + { + if (m_config) { + HW::enqueue(new config_cmd(m_config, m_itf->handle(), m_cls)); + } + } + + /** + * Find or add the singular instance in the DB + */ + static std::shared_ptr find_or_add( + const interface_ip6_nd& temp) + { + return (m_db.find_or_add(temp.m_itf->key(), temp)); + } + + /* + * It's the VPPHW class that updates the objects in HW + */ + friend class OM; + + /** + * It's the singular_db class that calls replay() + */ + friend class singular_db; + + const std::shared_ptr m_itf; + + const class_t m_cls; + + const key_t m_key; + + /** + * HW configuration for the binding. The bool representing the + * do/don't bind. + */ + HW::item m_config; + + /** + * A map of all interface ip6 nd keyed against a combination of the + * interface and subnet's keys. + */ + static singular_db m_db; +}; + +/** + * Typedef the ip6nd_ra_config + */ +typedef interface_ip6_nd + ip6nd_ra_config; + +/** + * Typedef the ip6nd_ra_prefix + */ +typedef interface_ip6_nd + ip6nd_ra_prefix; + +/** + * Definition of the static singular_db for ACL Lists + */ +template +singular_db::key_t, + interface_ip6_nd> + interface_ip6_nd::m_db; + +template +typename interface_ip6_nd::event_handler + interface_ip6_nd::m_evh; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/interface_ip6_nd_cmds.cpp b/src/vpp-api/vom/interface_ip6_nd_cmds.cpp new file mode 100644 index 00000000000..c6f53f04919 --- /dev/null +++ b/src/vpp-api/vom/interface_ip6_nd_cmds.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/interface_ip6_nd.hpp" + +#include + +namespace VOM { +template <> +rc_t +ip6nd_ra_config::config_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_itf.value(); + m_cls.to_vpp(payload); + payload.is_no = 0; + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return rc_t::OK; +} + +template <> +rc_t +ip6nd_ra_config::unconfig_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_itf.value(); + m_cls.to_vpp(payload); + payload.is_no = 1; + + VAPI_CALL(req.execute()); + + wait(); + m_hw_item.set(rc_t::NOOP); + + return rc_t::OK; +} + +template <> +rc_t +ip6nd_ra_prefix::config_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_itf.value(); + m_cls.to_vpp(payload); + payload.is_no = 0; + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return rc_t::OK; +} + +template <> +rc_t +ip6nd_ra_prefix::unconfig_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_itf.value(); + m_cls.to_vpp(payload); + payload.is_no = 1; + + VAPI_CALL(req.execute()); + + wait(); + m_hw_item.set(rc_t::NOOP); + + return rc_t::OK; +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/interface_span.cpp b/src/vpp-api/vom/interface_span.cpp new file mode 100644 index 00000000000..88bec50ac3d --- /dev/null +++ b/src/vpp-api/vom/interface_span.cpp @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/interface_span.hpp" +#include "vom/cmd.hpp" + +namespace VOM { +/** + * A DB of all interface_span config + */ +singular_db interface_span::m_db; + +interface_span::event_handler interface_span::m_evh; + +interface_span::interface_span(const interface& itf_from, + const interface& itf_to, + interface_span::state_t state) + : m_itf_from(itf_from.singular()) + , m_itf_to(itf_to.singular()) + , m_state(state) + , m_config(true) +{ +} + +interface_span::interface_span(const interface_span& o) + : m_itf_from(o.m_itf_from) + , m_itf_to(o.m_itf_to) + , m_state(o.m_state) + , m_config(o.m_config) +{ +} + +interface_span::~interface_span() +{ + sweep(); + + // not in the DB anymore. + m_db.release(make_pair(m_itf_from->key(), m_itf_to->key()), this); +} + +void +interface_span::sweep() +{ + if (m_config) { + HW::enqueue( + new unconfig_cmd(m_config, m_itf_from->handle(), m_itf_to->handle())); + } + HW::write(); +} + +void +interface_span::dump(std::ostream& os) +{ + m_db.dump(os); +} + +void +interface_span::replay() +{ + if (m_config) { + HW::enqueue(new config_cmd(m_config, m_itf_from->handle(), + m_itf_to->handle(), m_state)); + } +} + +std::string +interface_span::to_string() const +{ + std::ostringstream s; + s << "Itf Span-config:" + << " itf-from:" << m_itf_from->to_string() + << " itf-to:" << m_itf_to->to_string() << " state:" << m_state.to_string(); + + return (s.str()); +} + +void +interface_span::update(const interface_span& desired) +{ + if (!m_config) { + HW::enqueue(new config_cmd(m_config, m_itf_from->handle(), + m_itf_to->handle(), m_state)); + } +} + +std::ostream& +operator<<(std::ostream& os, const interface_span::key_type_t& key) +{ + os << "[" << key.first << ", " << key.second << "]"; + + return (os); +} + +std::shared_ptr +interface_span::find_or_add(const interface_span& temp) +{ + return (m_db.find_or_add( + make_pair(temp.m_itf_from->key(), temp.m_itf_to->key()), temp)); +} + +std::shared_ptr +interface_span::singular() const +{ + return find_or_add(*this); +} + +interface_span::event_handler::event_handler() +{ + OM::register_listener(this); + inspect::register_handler({ "itf-span" }, "interface span configurations", + this); +} + +void +interface_span::event_handler::handle_replay() +{ + m_db.replay(); +} + +void +interface_span::event_handler::handle_populate(const client_db::key_t& key) +{ + std::shared_ptr cmd(new interface_span::dump_cmd()); + + HW::enqueue(cmd); + HW::write(); + + for (auto& record : *cmd) { + auto& payload = record.get_payload(); + + std::shared_ptr itf_from = + interface::find(payload.sw_if_index_from); + std::shared_ptr itf_to = interface::find(payload.sw_if_index_to); + + interface_span itf_span(*itf_from, *itf_to, + state_t::from_int(payload.state)); + + VOM_LOG(log_level_t::DEBUG) << "span-dump: " << itf_from->to_string() + << itf_to->to_string() + << state_t::from_int(payload.state).to_string(); + + /* + * Write each of the discovered interfaces into the OM, + * but disable the HW Command q whilst we do, so that no + * commands are sent to VPP + */ + OM::commit(key, itf_span); + } +} + +dependency_t +interface_span::event_handler::order() const +{ + return (dependency_t::BINDING); +} + +void +interface_span::event_handler::show(std::ostream& os) +{ + m_db.dump(os); +} + +const interface_span::state_t interface_span::state_t::DISABLED(0, "disable"); +const interface_span::state_t interface_span::state_t::RX_ENABLED(1, + "rx-enable"); +const interface_span::state_t interface_span::state_t::TX_ENABLED(2, + "tx-enable"); +const interface_span::state_t interface_span::state_t::TX_RX_ENABLED( + 3, + "tx-rx-enable"); + +interface_span::state_t::state_t(int v, const std::string& s) + : enum_base(v, s) +{ +} + +interface_span::state_t +interface_span::state_t::from_int(uint8_t i) +{ + switch (i) { + case 0: + return interface_span::state_t::DISABLED; + break; + case 1: + return interface_span::state_t::RX_ENABLED; + break; + case 2: + return interface_span::state_t::TX_ENABLED; + break; + case 3: + default: + break; + } + + return interface_span::state_t::TX_RX_ENABLED; +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/interface_span.hpp b/src/vpp-api/vom/interface_span.hpp new file mode 100644 index 00000000000..b70c3164853 --- /dev/null +++ b/src/vpp-api/vom/interface_span.hpp @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_INTERFACE_SPAN_H__ +#define __VOM_INTERFACE_SPAN_H__ + +#include "vom/dump_cmd.hpp" +#include "vom/hw.hpp" +#include "vom/inspect.hpp" +#include "vom/interface.hpp" +#include "vom/object_base.hpp" +#include "vom/om.hpp" +#include "vom/rpc_cmd.hpp" +#include "vom/singular_db.hpp" + +#include + +namespace VOM { +/** + * A representation of interface span configuration + */ +class interface_span : public object_base +{ +public: + /** + * The state of the interface - rx/tx or both to be mirrored + */ + struct state_t : enum_base + { + /** + * DISABLED state + */ + const static state_t DISABLED; + /** + * RX enable state + */ + const static state_t RX_ENABLED; + /** + * TX enable state + */ + const static state_t TX_ENABLED; + /** + * TX and RX enable state + */ + const static state_t TX_RX_ENABLED; + + /** + * Convert VPP's numerical value to enum type + */ + static state_t from_int(uint8_t val); + + private: + /** + * Private constructor taking the value and the string name + */ + state_t(int v, const std::string& s); + }; + + /** + * Construct a new object matching the desried state + * + * @param itf_from - The interface to be mirrored + * @param itf_to - The interface where the traffic is mirrored + */ + interface_span(const interface& itf_from, + const interface& itf_to, + state_t state); + + /** + * Copy Constructor + */ + interface_span(const interface_span& o); + + /** + * Destructor + */ + ~interface_span(); + + /** + * Return the 'singular instance' of the interface_span that matches + * this object + */ + std::shared_ptr singular() const; + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Dump all interface_spans into the stream provided + */ + static void dump(std::ostream& os); + + /** + * The key type for interface_spans + */ + typedef std::pair key_type_t; + + /** + * Find a singular instance in the DB for the interface passed + */ + static std::shared_ptr find(const interface& i); + + /** + * A command class that configures the interface span + */ + class config_cmd : public rpc_cmd, + rc_t, + vapi::Sw_interface_span_enable_disable> + { + public: + /** + * Constructor + */ + config_cmd(HW::item& item, + const handle_t& itf_from, + const handle_t& itf_to, + const state_t& state); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const config_cmd& i) const; + + private: + /** + * Reference to the interface to be mirrored + */ + const handle_t& m_itf_from; + /** + * Reference to the interface where the traffic is mirrored + */ + const handle_t& m_itf_to; + /** + * the state (rx, tx or both) of the interface to be mirrored + */ + const state_t& m_state; + }; + + /** + * A cmd class that Unconfigs interface span + */ + class unconfig_cmd : public rpc_cmd, + rc_t, + vapi::Sw_interface_span_enable_disable> + { + public: + /** + * Constructor + */ + unconfig_cmd(HW::item& item, + const handle_t& itf_from, + const handle_t& itf_to); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const unconfig_cmd& i) const; + + private: + /** + * Reference to the interface to be mirrored + */ + const handle_t& m_itf_from; + /** + * Reference to the interface where the traffic is mirrored + */ + const handle_t& m_itf_to; + }; + + /** + * A cmd class that Dumps all the interface spans + */ + class dump_cmd : public VOM::dump_cmd + { + public: + /** + * Constructor + */ + dump_cmd(); + dump_cmd(const dump_cmd& d); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const dump_cmd& i) const; + + private: + /** + * HW reutrn code + */ + HW::item item; + }; + +private: + /** + * Class definition for listeners to OM events + */ + class event_handler : public OM::listener, public inspect::command_handler + { + public: + event_handler(); + virtual ~event_handler() = default; + + /** + * Handle a populate event + */ + void handle_populate(const client_db::key_t& key); + + /** + * Handle a replay event + */ + void handle_replay(); + + /** + * Show the object in the Singular DB + */ + void show(std::ostream& os); + + /** + * Get the sortable Id of the listener + */ + dependency_t order() const; + }; + + /** + * event_handler to register with OM + */ + static event_handler m_evh; + + /** + * Enquue commonds to the VPP command Q for the update + */ + void update(const interface_span& obj); + + /** + * Find or add the singular instance in the DB + */ + static std::shared_ptr find_or_add( + const interface_span& temp); + + /* + * It's the VPPHW class that updates the objects in HW + */ + friend class OM; + + /** + e* It's the singular_db class that calls replay() + */ + friend class singular_db; + + /** + * Sweep/reap the object if still stale + */ + void sweep(void); + + /** + * replay the object to create it in hardware + */ + void replay(void); + + /** + * A reference counting pointer the interface to be mirrored + */ + const std::shared_ptr m_itf_from; + /** + * A reference counting pointer the interface where the traffic is + * mirrored + */ + const std::shared_ptr m_itf_to; + + /** + * the state (rx, tx or both) of the interface to be mirrored + */ + const state_t m_state; + + /** + * HW configuration for the binding. The bool representing the + * do/don't bind. + */ + HW::item m_config; + + /** + * A map of all interface span keyed against the interface to be + * mirrored. + */ + static singular_db m_db; +}; + +/** + * Ostream output for the key + */ +std::ostream& operator<<(std::ostream& os, + const interface_span::key_type_t& key); +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/interface_span_cmds.cpp b/src/vpp-api/vom/interface_span_cmds.cpp new file mode 100644 index 00000000000..27fec801b89 --- /dev/null +++ b/src/vpp-api/vom/interface_span_cmds.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/interface_span.hpp" + +DEFINE_VAPI_MSG_IDS_SPAN_API_JSON; + +namespace VOM { +interface_span::config_cmd::config_cmd(HW::item& item, + const handle_t& itf_from, + const handle_t& itf_to, + const interface_span::state_t& state) + : rpc_cmd(item) + , m_itf_from(itf_from) + , m_itf_to(itf_to) + , m_state(state) +{ +} + +bool +interface_span::config_cmd::operator==(const config_cmd& o) const +{ + return ((m_itf_from == o.m_itf_from) && (m_itf_to == o.m_itf_to) && + (m_state == o.m_state)); +} + +rc_t +interface_span::config_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.is_l2 = 0; + payload.sw_if_index_from = m_itf_from.value(); + payload.sw_if_index_to = m_itf_to.value(); + payload.state = m_state.value(); + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return rc_t::OK; +} + +std::string +interface_span::config_cmd::to_string() const +{ + std::ostringstream s; + s << "itf-span-config: " << m_hw_item.to_string() + << " itf-from:" << m_itf_from.to_string() + << " itf-to:" << m_itf_to.to_string() << " state:" << m_state.to_string(); + + return (s.str()); +} + +interface_span::unconfig_cmd::unconfig_cmd(HW::item& item, + const handle_t& itf_from, + const handle_t& itf_to) + : rpc_cmd(item) + , m_itf_from(itf_from) + , m_itf_to(itf_to) +{ +} + +bool +interface_span::unconfig_cmd::operator==(const unconfig_cmd& o) const +{ + return ((m_itf_from == o.m_itf_from) && (m_itf_to == o.m_itf_to)); +} + +rc_t +interface_span::unconfig_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.is_l2 = 0; + payload.sw_if_index_from = m_itf_from.value(); + payload.sw_if_index_to = m_itf_to.value(); + payload.state = 0; + + VAPI_CALL(req.execute()); + + wait(); + m_hw_item.set(rc_t::NOOP); + + return rc_t::OK; +} + +std::string +interface_span::unconfig_cmd::to_string() const +{ + std::ostringstream s; + s << "itf-span-unconfig: " << m_hw_item.to_string() + << " itf-from:" << m_itf_from.to_string() + << " itf-to:" << m_itf_to.to_string(); + + return (s.str()); +} + +interface_span::dump_cmd::dump_cmd() +{ +} + +bool +interface_span::dump_cmd::operator==(const dump_cmd& other) const +{ + return (true); +} + +rc_t +interface_span::dump_cmd::issue(connection& con) +{ + m_dump.reset(new msg_t(con.ctx(), std::ref(*this))); + + auto& payload = m_dump->get_request().get_payload(); + payload.is_l2 = 0; + + VAPI_CALL(m_dump->execute()); + + wait(); + + return rc_t::OK; +} + +std::string +interface_span::dump_cmd::to_string() const +{ + return ("interface-span-dump"); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/interface_types.cpp b/src/vpp-api/vom/interface_types.cpp new file mode 100644 index 00000000000..6afec8077a5 --- /dev/null +++ b/src/vpp-api/vom/interface_types.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/interface.hpp" + +namespace VOM { +/* + * constants and enums + */ +const interface::type_t interface::type_t::UNKNOWN(0, "unknown"); +const interface::type_t interface::type_t::BVI(1, "BVI"); +const interface::type_t interface::type_t::ETHERNET(2, "Ehternet"); +const interface::type_t interface::type_t::VXLAN(3, "VXLAN"); +const interface::type_t interface::type_t::AFPACKET(4, "AFPACKET"); +const interface::type_t interface::type_t::LOOPBACK(5, "LOOPBACK"); +const interface::type_t interface::type_t::LOCAL(6, "LOCAL"); +const interface::type_t interface::type_t::TAP(7, "TAP"); + +const interface::oper_state_t interface::oper_state_t::DOWN(0, "down"); +const interface::oper_state_t interface::oper_state_t::UP(1, "up"); + +const interface::admin_state_t interface::admin_state_t::DOWN(0, "down"); +const interface::admin_state_t interface::admin_state_t::UP(1, "up"); + +interface::type_t +interface::type_t::from_string(const std::string& str) +{ + if (str.find("Ethernet") != std::string::npos) { + return interface::type_t::ETHERNET; + } else if (str.find("vxlan") != std::string::npos) { + return interface::type_t::VXLAN; + } else if (str.find("loop") != std::string::npos) { + return interface::type_t::LOOPBACK; + } else if (str.find("host-") != std::string::npos) { + return interface::type_t::AFPACKET; + } else if (str.find("local") != std::string::npos) { + return interface::type_t::LOCAL; + } else if (str.find("tap") != std::string::npos) { + return interface::type_t::TAP; + } else if (str.find("bvi") != std::string::npos) { + return interface::type_t::BVI; + } + + return interface::type_t::UNKNOWN; +} + +interface::type_t::type_t(int v, const std::string& s) + : enum_base(v, s) +{ +} + +interface::oper_state_t::oper_state_t(int v, const std::string& s) + : enum_base(v, s) +{ +} + +interface::admin_state_t::admin_state_t(int v, const std::string& s) + : enum_base(v, s) +{ +} + +interface::admin_state_t +interface::admin_state_t::from_int(uint8_t v) +{ + if (0 == v) { + return (interface::admin_state_t::DOWN); + } + return (interface::admin_state_t::UP); +} + +interface::oper_state_t +interface::oper_state_t::from_int(uint8_t v) +{ + if (0 == v) { + return (interface::oper_state_t::DOWN); + } + return (interface::oper_state_t::UP); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/ip_unnumbered.cpp b/src/vpp-api/vom/ip_unnumbered.cpp new file mode 100644 index 00000000000..7df391c0b69 --- /dev/null +++ b/src/vpp-api/vom/ip_unnumbered.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/ip_unnumbered.hpp" +#include "vom/cmd.hpp" + +namespace VOM { +/** + * A DB of all LLDP configs + */ +singular_db ip_unnumbered::m_db; + +ip_unnumbered::event_handler ip_unnumbered::m_evh; + +ip_unnumbered::ip_unnumbered(const interface& itf, const interface& l3_itf) + : m_itf(itf.singular()) + , m_l3_itf(l3_itf.singular()) +{ +} + +ip_unnumbered::ip_unnumbered(const ip_unnumbered& o) + : m_itf(o.m_itf) + , m_l3_itf(o.m_l3_itf) + , m_config(o.m_config) +{ +} + +ip_unnumbered::~ip_unnumbered() +{ + sweep(); + + // not in the DB anymore. + m_db.release(m_itf->key(), this); +} + +void +ip_unnumbered::sweep() +{ + if (m_config) { + HW::enqueue( + new unconfig_cmd(m_config, m_itf->handle(), m_l3_itf->handle())); + } + HW::write(); +} + +void +ip_unnumbered::dump(std::ostream& os) +{ + m_db.dump(os); +} + +void +ip_unnumbered::replay() +{ + if (m_config) { + HW::enqueue(new config_cmd(m_config, m_itf->handle(), m_l3_itf->handle())); + } +} + +std::string +ip_unnumbered::to_string() const +{ + std::ostringstream s; + s << "IP Unnumbered-config:" + << " itf:" << m_itf->to_string() << " l3-itf:" << m_l3_itf->to_string(); + + return (s.str()); +} + +void +ip_unnumbered::update(const ip_unnumbered& desired) +{ + if (!m_config) { + HW::enqueue(new config_cmd(m_config, m_itf->handle(), m_l3_itf->handle())); + } +} + +std::shared_ptr +ip_unnumbered::find_or_add(const ip_unnumbered& temp) +{ + return (m_db.find_or_add(temp.m_itf->key(), temp)); +} + +std::shared_ptr +ip_unnumbered::singular() const +{ + return find_or_add(*this); +} + +ip_unnumbered::event_handler::event_handler() +{ + OM::register_listener(this); + inspect::register_handler({ "ip-un" }, "IP unnumbered configurations", this); +} + +void +ip_unnumbered::event_handler::handle_replay() +{ + m_db.replay(); +} + +void +ip_unnumbered::event_handler::handle_populate(const client_db::key_t& key) +{ + // VPP provides no dump for IP unnumbered +} + +dependency_t +ip_unnumbered::event_handler::order() const +{ + return (dependency_t::BINDING); +} + +void +ip_unnumbered::event_handler::show(std::ostream& os) +{ + m_db.dump(os); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/ip_unnumbered.hpp b/src/vpp-api/vom/ip_unnumbered.hpp new file mode 100644 index 00000000000..e6f5ef4399b --- /dev/null +++ b/src/vpp-api/vom/ip_unnumbered.hpp @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_IP_UNNUMBERED_H__ +#define __VOM_IP_UNNUMBERED_H__ + +#include "vom/hw.hpp" +#include "vom/inspect.hpp" +#include "vom/interface.hpp" +#include "vom/object_base.hpp" +#include "vom/om.hpp" +#include "vom/rpc_cmd.hpp" +#include "vom/singular_db.hpp" + +namespace VOM { +/** + * A representation of IP unnumbered configuration on an interface + */ +class ip_unnumbered : public object_base +{ +public: + /** + * Construct a new object matching the desried state + * + * @param itf - The interface with no IP address + * @param l3_itf - The interface that has the IP address we wish to + * share. + */ + ip_unnumbered(const interface& itf, const interface& l3_itf); + + /** + * Copy Constructor + */ + ip_unnumbered(const ip_unnumbered& o); + + /** + * Destructor + */ + ~ip_unnumbered(); + + /** + * Return the 'singular instance' of the L3-Config that matches this + * object + */ + std::shared_ptr singular() const; + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Dump all ip_unnumbereds into the stream provided + */ + static void dump(std::ostream& os); + + /** + * The key type for ip_unnumbereds + */ + typedef interface::key_type key_t; + + /** + * Find an singular instance in the DB for the interface passed + */ + static std::shared_ptr find(const interface& i); + + /** + * A command class that configures the IP unnumbered + */ + class config_cmd + : public rpc_cmd, rc_t, vapi::Sw_interface_set_unnumbered> + { + public: + /** + * Constructor + */ + config_cmd(HW::item& item, + const handle_t& itf, + const handle_t& l3_itf); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const config_cmd& i) const; + + private: + /** + * Reference to the interface for which the address is required + */ + const handle_t& m_itf; + /** + * Reference to the interface which has an address + */ + const handle_t& m_l3_itf; + }; + + /** + * A cmd class that Unconfigs L3 Config from an interface + */ + class unconfig_cmd + : public rpc_cmd, rc_t, vapi::Sw_interface_set_unnumbered> + { + public: + /** + * Constructor + */ + unconfig_cmd(HW::item& item, + const handle_t& itf, + const handle_t& l3_itf); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const unconfig_cmd& i) const; + + private: + /** + * Reference to the interface for which the address is required + */ + const handle_t& m_itf; + /** + * Reference to the interface which has an address + */ + const handle_t& m_l3_itf; + }; + +private: + /** + * Class definition for listeners to OM events + */ + class event_handler : public OM::listener, public inspect::command_handler + { + public: + event_handler(); + virtual ~event_handler() = default; + + /** + * Handle a populate event + */ + void handle_populate(const client_db::key_t& key); + + /** + * Handle a replay event + */ + void handle_replay(); + + /** + * Show the object in the Singular DB + */ + void show(std::ostream& os); + + /** + * Get the sortable Id of the listener + */ + dependency_t order() const; + }; + + /** + * event_handler to register with OM + */ + static event_handler m_evh; + + /** + * Enquue commonds to the VPP command Q for the update + */ + void update(const ip_unnumbered& obj); + + /** + * Find or add the singular instance in the DB + */ + static std::shared_ptr find_or_add(const ip_unnumbered& temp); + + /* + * It's the VPPHW class that updates the objects in HW + */ + friend class OM; + + /** + * It's the singular_db class that calls replay + */ + friend class singular_db; + + /** + * Sweep/reap the object if still stale + */ + void sweep(void); + + /** + * replay the object to create it in hardware + */ + void replay(void); + + /** + * A reference counting pointer the interface that requires an address. + */ + const std::shared_ptr m_itf; + /** + * A reference counting pointer the interface that has an address. + */ + const std::shared_ptr m_l3_itf; + + /** + * HW configuration for the binding. The bool representing the + * do/don't bind. + */ + HW::item m_config; + + /** + * A map of all L3 configs keyed against a combination of the interface + * and subnet's keys. + */ + static singular_db m_db; +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/ip_unnumbered_cmds.cpp b/src/vpp-api/vom/ip_unnumbered_cmds.cpp new file mode 100644 index 00000000000..768d3562b18 --- /dev/null +++ b/src/vpp-api/vom/ip_unnumbered_cmds.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/ip_unnumbered.hpp" + +#include + +namespace VOM { +ip_unnumbered::config_cmd::config_cmd(HW::item& item, + const handle_t& itf, + const handle_t& l3_itf) + : rpc_cmd(item) + , m_itf(itf) + , m_l3_itf(l3_itf) +{ +} + +bool +ip_unnumbered::config_cmd::operator==(const config_cmd& o) const +{ + return ((m_itf == o.m_itf) && (m_l3_itf == o.m_l3_itf)); +} + +rc_t +ip_unnumbered::config_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.is_add = 1; + payload.sw_if_index = m_l3_itf.value(); + payload.unnumbered_sw_if_index = m_itf.value(); + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return rc_t::OK; +} + +std::string +ip_unnumbered::config_cmd::to_string() const +{ + std::ostringstream s; + s << "IP-unnumberd-config: " << m_hw_item.to_string() + << " itf:" << m_itf.to_string() << " l3-itf:" << m_l3_itf.to_string(); + + return (s.str()); +} + +ip_unnumbered::unconfig_cmd::unconfig_cmd(HW::item& item, + const handle_t& itf, + const handle_t& l3_itf) + : rpc_cmd(item) + , m_itf(itf) + , m_l3_itf(l3_itf) +{ +} + +bool +ip_unnumbered::unconfig_cmd::operator==(const unconfig_cmd& o) const +{ + return ((m_itf == o.m_itf) && (m_l3_itf == o.m_l3_itf)); +} + +rc_t +ip_unnumbered::unconfig_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.is_add = 0; + payload.sw_if_index = m_l3_itf.value(); + payload.unnumbered_sw_if_index = m_itf.value(); + + VAPI_CALL(req.execute()); + + wait(); + m_hw_item.set(rc_t::NOOP); + + return rc_t::OK; +} + +std::string +ip_unnumbered::unconfig_cmd::to_string() const +{ + std::ostringstream s; + s << "IP-unnumberd-unconfig: " << m_hw_item.to_string() + << " itf:" << m_itf.to_string() << " l3-itf:" << m_l3_itf.to_string(); + + return (s.str()); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/l2_binding.cpp b/src/vpp-api/vom/l2_binding.cpp new file mode 100644 index 00000000000..3bdc8f9cd0b --- /dev/null +++ b/src/vpp-api/vom/l2_binding.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/l2_binding.hpp" +#include "vom/cmd.hpp" + +namespace VOM { +/** + * A DB of all the L2 Configs + */ +singular_db l2_binding::m_db; + +l2_binding::event_handler l2_binding::m_evh; + +/* + * Make sure these are in sync with the smae enum in VPP + */ +const l2_binding::l2_vtr_op_t l2_binding::l2_vtr_op_t::L2_VTR_DISABLED( + 0, + "disabled"); +const l2_binding::l2_vtr_op_t l2_binding::l2_vtr_op_t::L2_VTR_PUSH_1(1, + "push-1"); +const l2_binding::l2_vtr_op_t l2_binding::l2_vtr_op_t::L2_VTR_PUSH_2(2, + "push-2"); +const l2_binding::l2_vtr_op_t l2_binding::l2_vtr_op_t::L2_VTR_POP_1(3, "pop-1"); +const l2_binding::l2_vtr_op_t l2_binding::l2_vtr_op_t::L2_VTR_POP_2(4, "pop-2"); +const l2_binding::l2_vtr_op_t l2_binding::l2_vtr_op_t::L2_VTR_TRANSLATE_1_1( + 5, + "translate-1-1"); +const l2_binding::l2_vtr_op_t l2_binding::l2_vtr_op_t::L2_VTR_TRANSLATE_1_2( + 6, + "translate-1-2"); +const l2_binding::l2_vtr_op_t l2_binding::l2_vtr_op_t::L2_VTR_TRANSLATE_2_1( + 7, + "translate-2-1"); +const l2_binding::l2_vtr_op_t l2_binding::l2_vtr_op_t::L2_VTR_TRANSLATE_2_2( + 5, + "translate-2-2"); + +l2_binding::l2_vtr_op_t::l2_vtr_op_t(int v, const std::string s) + : enum_base(v, s) +{ +} + +/** + * Construct a new object matching the desried state + */ +l2_binding::l2_binding(const interface& itf, const bridge_domain& bd) + : m_itf(itf.singular()) + , m_bd(bd.singular()) + , m_binding(0) + , m_vtr_op(l2_vtr_op_t::L2_VTR_DISABLED, rc_t::UNSET) + , m_vtr_op_tag(0) +{ +} + +l2_binding::l2_binding(const l2_binding& o) + : m_itf(o.m_itf) + , m_bd(o.m_bd) + , m_binding(0) + , m_vtr_op(o.m_vtr_op) + , m_vtr_op_tag(o.m_vtr_op_tag) +{ +} + +void +l2_binding::sweep() +{ + if (m_binding && handle_t::INVALID != m_itf->handle()) { + HW::enqueue(new unbind_cmd(m_binding, m_itf->handle(), m_bd->id(), + interface::type_t::BVI == m_itf->type())); + } + + // no need to undo the VTR operation. + HW::write(); +} + +void +l2_binding::replay() +{ + if (m_binding && handle_t::INVALID != m_itf->handle()) { + HW::enqueue(new bind_cmd(m_binding, m_itf->handle(), m_bd->id(), + interface::type_t::BVI == m_itf->type())); + } + + if (m_vtr_op && handle_t::INVALID != m_itf->handle()) { + HW::enqueue(new set_vtr_op_cmd(m_vtr_op, m_itf->handle(), m_vtr_op_tag)); + } +} + +l2_binding::~l2_binding() +{ + sweep(); + + // not in the DB anymore. + m_db.release(m_itf->handle(), this); +} + +std::string +l2_binding::to_string() const +{ + std::ostringstream s; + s << "L2-config:[" << m_itf->to_string() << " " << m_bd->to_string() << " " + << m_binding.to_string() << "]"; + + return (s.str()); +} + +void +l2_binding::set(const l2_vtr_op_t& op, uint16_t tag) +{ + assert(rc_t::UNSET == m_vtr_op.rc()); + m_vtr_op.set(rc_t::NOOP); + m_vtr_op.update(op); + m_vtr_op_tag = tag; +} + +void +l2_binding::update(const l2_binding& desired) +{ + /* + * the desired state is always that the interface should be created + */ + if (rc_t::OK != m_binding.rc()) { + HW::enqueue(new bind_cmd(m_binding, m_itf->handle(), m_bd->id(), + interface::type_t::BVI == m_itf->type())); + } + + /* + * set the VTR operation is request + */ + if (m_vtr_op.update(desired.m_vtr_op)) { + HW::enqueue(new set_vtr_op_cmd(m_vtr_op, m_itf->handle(), m_vtr_op_tag)); + } +} + +std::shared_ptr +l2_binding::find_or_add(const l2_binding& temp) +{ + return (m_db.find_or_add(temp.m_itf->handle(), temp)); +} + +std::shared_ptr +l2_binding::singular() const +{ + return find_or_add(*this); +} + +void +l2_binding::dump(std::ostream& os) +{ + m_db.dump(os); +} + +l2_binding::event_handler::event_handler() +{ + OM::register_listener(this); + inspect::register_handler({ "l2" }, "L2 bindings", this); +} + +void +l2_binding::event_handler::handle_replay() +{ + m_db.replay(); +} + +void +l2_binding::event_handler::handle_populate(const client_db::key_t& key) +{ + /** + * This is done while populating the bridge-domain + */ +} + +dependency_t +l2_binding::event_handler::order() const +{ + return (dependency_t::BINDING); +} + +void +l2_binding::event_handler::show(std::ostream& os) +{ + m_db.dump(os); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/l2_binding.hpp b/src/vpp-api/vom/l2_binding.hpp new file mode 100644 index 00000000000..2f65e38b46e --- /dev/null +++ b/src/vpp-api/vom/l2_binding.hpp @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_L2_BINDING_H__ +#define __VOM_L2_BINDING_H__ + +#include "vom/bridge_domain.hpp" +#include "vom/hw.hpp" +#include "vom/inspect.hpp" +#include "vom/interface.hpp" +#include "vom/object_base.hpp" +#include "vom/om.hpp" +#include "vom/rpc_cmd.hpp" +#include "vom/singular_db.hpp" +#include "vom/vxlan_tunnel.hpp" + +namespace VOM { +/** + * A Clas representing the binding of an L2 interface to a bridge-domain + * and the properties of that binding. + */ +class l2_binding : public object_base +{ +public: + struct l2_vtr_op_t : public enum_base + { + l2_vtr_op_t(const l2_vtr_op_t& l) = default; + ~l2_vtr_op_t() = default; + + const static l2_vtr_op_t L2_VTR_DISABLED; + const static l2_vtr_op_t L2_VTR_PUSH_1; + const static l2_vtr_op_t L2_VTR_PUSH_2; + const static l2_vtr_op_t L2_VTR_POP_1; + const static l2_vtr_op_t L2_VTR_POP_2; + const static l2_vtr_op_t L2_VTR_TRANSLATE_1_1; + const static l2_vtr_op_t L2_VTR_TRANSLATE_1_2; + const static l2_vtr_op_t L2_VTR_TRANSLATE_2_1; + const static l2_vtr_op_t L2_VTR_TRANSLATE_2_2; + + private: + l2_vtr_op_t(int v, const std::string s); + }; + + /** + * Construct a new object matching the desried state + */ + l2_binding(const interface& itf, const bridge_domain& bd); + + /** + * Copy Constructor + */ + l2_binding(const l2_binding& o); + + /** + * Destructor + */ + ~l2_binding(); + + /** + * Return the 'singular instance' of the L2 config that matches this + * object + */ + std::shared_ptr singular() const; + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Dump all l2_bindings into the stream provided + */ + static void dump(std::ostream& os); + + /** + * Set the VTR operation on the binding/interface + */ + void set(const l2_vtr_op_t& op, uint16_t tag); + + /** + * A functor class that binds L2 configuration to an interface + */ + class bind_cmd + : public rpc_cmd, rc_t, vapi::Sw_interface_set_l2_bridge> + { + public: + /** + * Constructor + */ + bind_cmd(HW::item& item, + const handle_t& itf, + uint32_t bd, + bool is_bvi); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const bind_cmd& i) const; + + private: + /** + * The interface to bind + */ + const handle_t m_itf; + + /** + * The bridge-domain to bind to + */ + uint32_t m_bd; + + /** + * Is it a BVI interface that is being bound + */ + bool m_is_bvi; + }; + + /** + * A cmd class that Unbinds L2 configuration from an interface + */ + class unbind_cmd + : public rpc_cmd, rc_t, vapi::Sw_interface_set_l2_bridge> + { + public: + /** + * Constructor + */ + unbind_cmd(HW::item& item, + const handle_t& itf, + uint32_t bd, + bool is_bvi); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const unbind_cmd& i) const; + + private: + /** + * The interface to bind + */ + const handle_t m_itf; + + /** + * The bridge-domain to bind to + */ + uint32_t m_bd; + + /** + * Is it a BVI interface that is being bound + */ + bool m_is_bvi; + }; + + /** + * A cmd class sets the VTR operation + */ + class set_vtr_op_cmd : public rpc_cmd, + rc_t, + vapi::L2_interface_vlan_tag_rewrite> + { + public: + /** + * Constructor + */ + set_vtr_op_cmd(HW::item& item, + const handle_t& itf, + uint16_t tag); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const set_vtr_op_cmd& i) const; + + private: + /** + * The interface to bind + */ + const handle_t m_itf; + + /** + * The tag for the operation + */ + uint16_t m_tag; + }; + +private: + /** + * Class definition for listeners to OM events + */ + class event_handler : public OM::listener, public inspect::command_handler + { + public: + event_handler(); + virtual ~event_handler() = default; + + /** + * Handle a populate event + */ + void handle_populate(const client_db::key_t& key); + + /** + * Handle a replay event + */ + void handle_replay(); + + /** + * Show the object in the Singular DB + */ + void show(std::ostream& os); + + /** + * Get the sortable Id of the listener + */ + dependency_t order() const; + }; + + /** + * event_handler to register with OM + */ + static event_handler m_evh; + + /** + * Enquue commonds to the VPP command Q for the update + */ + void update(const l2_binding& obj); + + /** + * Find or Add the singular instance in the DB + */ + static std::shared_ptr find_or_add(const l2_binding& temp); + + /* + * It's the OM class that calls singular() + */ + friend class OM; + + /** + * It's the singular_db class that calls replay() + */ + friend class singular_db; + + /** + * Sweep/reap the object if still stale + */ + void sweep(void); + + /** + * replay the object to create it in hardware + */ + void replay(void); + + /** + * A reference counting pointer the interface that this L2 layer + * represents. By holding the reference here, we can guarantee that + * this object will outlive the interface + */ + const std::shared_ptr m_itf; + + /** + * A reference counting pointer the Bridge-Domain that this L2 + * interface is bound to. By holding the reference here, we can + * guarantee that this object will outlive the BD. + */ + const std::shared_ptr m_bd; + + /** + * HW configuration for the binding. The bool representing the + * do/don't bind. + */ + HW::item m_binding; + + /** + * HW configuration for the VTR option + */ + HW::item m_vtr_op; + + /** + * The Dot1q tag for the VTR operation + */ + uint16_t m_vtr_op_tag; + + /** + * A map of all L2 interfaces key against the interface's handle_t + */ + static singular_db m_db; +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/l2_binding_cmds.cpp b/src/vpp-api/vom/l2_binding_cmds.cpp new file mode 100644 index 00000000000..a6ed136842c --- /dev/null +++ b/src/vpp-api/vom/l2_binding_cmds.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/l2_binding.hpp" + +namespace VOM { +l2_binding::bind_cmd::bind_cmd(HW::item& item, + const handle_t& itf, + uint32_t bd, + bool is_bvi) + : rpc_cmd(item) + , m_itf(itf) + , m_bd(bd) + , m_is_bvi(is_bvi) +{ +} + +bool +l2_binding::bind_cmd::operator==(const bind_cmd& other) const +{ + return ((m_itf == other.m_itf) && (m_bd == other.m_bd) && + (m_is_bvi == other.m_is_bvi)); +} + +rc_t +l2_binding::bind_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.rx_sw_if_index = m_itf.value(); + payload.bd_id = m_bd; + payload.shg = 0; + payload.bvi = m_is_bvi; + payload.enable = 1; + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return (rc_t::OK); +} + +std::string +l2_binding::bind_cmd::to_string() const +{ + std::ostringstream s; + s << "L2-bind: " << m_hw_item.to_string() << " itf:" << m_itf.to_string() + << " bd:" << m_bd; + + return (s.str()); +} + +l2_binding::unbind_cmd::unbind_cmd(HW::item& item, + const handle_t& itf, + uint32_t bd, + bool is_bvi) + : rpc_cmd(item) + , m_itf(itf) + , m_bd(bd) + , m_is_bvi(is_bvi) +{ +} + +bool +l2_binding::unbind_cmd::operator==(const unbind_cmd& other) const +{ + return ((m_itf == other.m_itf) && (m_bd == other.m_bd) && + (m_is_bvi == other.m_is_bvi)); +} + +rc_t +l2_binding::unbind_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.rx_sw_if_index = m_itf.value(); + payload.bd_id = m_bd; + payload.shg = 0; + payload.bvi = m_is_bvi; + payload.enable = 0; + + VAPI_CALL(req.execute()); + + wait(); + m_hw_item.set(rc_t::NOOP); + + return (rc_t::OK); +} + +std::string +l2_binding::unbind_cmd::to_string() const +{ + std::ostringstream s; + s << "L2-unbind: " << m_hw_item.to_string() << " itf:" << m_itf.to_string() + << " bd:" << m_bd; + + return (s.str()); +} + +l2_binding::set_vtr_op_cmd::set_vtr_op_cmd(HW::item& item, + const handle_t& itf, + uint16_t tag) + : rpc_cmd(item) + , m_itf(itf) + , m_tag(tag) +{ +} + +bool +l2_binding::set_vtr_op_cmd::operator==(const set_vtr_op_cmd& other) const +{ + return ( + (m_hw_item.data() == other.m_hw_item.data() && m_itf == other.m_itf) && + (m_tag == other.m_tag)); +} + +rc_t +l2_binding::set_vtr_op_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_itf.value(); + payload.vtr_op = m_hw_item.data().value(); + payload.push_dot1q = 1; + payload.tag1 = m_tag; + + VAPI_CALL(req.execute()); + + wait(); + m_hw_item.set(rc_t::NOOP); + + return (rc_t::OK); +} + +std::string +l2_binding::set_vtr_op_cmd::to_string() const +{ + std::ostringstream s; + s << "L2-set-vtr-op: " << m_hw_item.to_string() + << " itf:" << m_itf.to_string() << " tag:" << m_tag; + + return (s.str()); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/l3_binding.cpp b/src/vpp-api/vom/l3_binding.cpp new file mode 100644 index 00000000000..0d5f4149e26 --- /dev/null +++ b/src/vpp-api/vom/l3_binding.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/l3_binding.hpp" +#include "vom/cmd.hpp" + +namespace VOM { +singular_db l3_binding::m_db; + +l3_binding::event_handler l3_binding::m_evh; + +/** + * Construct a new object matching the desried state + */ +l3_binding::l3_binding(const interface& itf, const route::prefix_t& pfx) + : m_itf(itf.singular()) + , m_pfx(pfx) + , m_binding(true) +{ +} + +l3_binding::l3_binding(const l3_binding& o) + : m_itf(o.m_itf) + , m_pfx(o.m_pfx) + , m_binding(true) +{ +} + +l3_binding::~l3_binding() +{ + sweep(); + + // not in the DB anymore. + m_db.release(make_pair(m_itf->key(), m_pfx), this); +} + +void +l3_binding::sweep() +{ + if (m_binding) { + HW::enqueue(new unbind_cmd(m_binding, m_itf->handle(), m_pfx)); + } + HW::write(); +} + +void +l3_binding::replay() +{ + if (m_binding) { + HW::enqueue(new bind_cmd(m_binding, m_itf->handle(), m_pfx)); + } +} + +const route::prefix_t& +l3_binding::prefix() const +{ + return (m_pfx); +} + +std::string +l3_binding::to_string() const +{ + std::ostringstream s; + s << "L3-config:[" << m_itf->to_string() << " prefix:" << m_pfx.to_string() + << " " << m_binding.to_string() << "]"; + + return (s.str()); +} + +void +l3_binding::update(const l3_binding& desired) +{ + /* + * the desired state is always that the interface should be created + */ + if (!m_binding) { + HW::enqueue(new bind_cmd(m_binding, m_itf->handle(), m_pfx)); + } +} + +std::shared_ptr +l3_binding::find_or_add(const l3_binding& temp) +{ + return (m_db.find_or_add(make_pair(temp.m_itf->key(), temp.m_pfx), temp)); +} + +std::shared_ptr +l3_binding::singular() const +{ + return find_or_add(*this); +} + +void +l3_binding::dump(std::ostream& os) +{ + m_db.dump(os); +} + +std::ostream& +operator<<(std::ostream& os, const l3_binding::key_type_t& key) +{ + os << "[" << key.first << ", " << key.second << "]"; + + return (os); +} + +std::deque> +l3_binding::find(const interface& i) +{ + /* + * Loop throught the entire map looking for matching interface. + * not the most efficient algorithm, but it will do for now. The + * number of L3 configs is low and this is only called during bootup + */ + std::deque> l3s; + + auto it = m_db.cbegin(); + + while (it != m_db.cend()) { + /* + * The key in the DB is a pair of the interface's name and prefix. + * If the keys match, save the L3-config + */ + auto key = it->first; + + if (i.key() == key.first) { + l3s.push_back(it->second.lock()); + } + + ++it; + } + + return (l3s); +} + +l3_binding::event_handler::event_handler() +{ + OM::register_listener(this); + inspect::register_handler({ "l3" }, "L3 bindings", this); +} + +void +l3_binding::event_handler::handle_replay() +{ + m_db.replay(); +} + +void +l3_binding::event_handler::handle_populate(const client_db::key_t& key) +{ + /** + * This is done while populating the interfaces + */ +} + +dependency_t +l3_binding::event_handler::order() const +{ + return (dependency_t::BINDING); +} + +void +l3_binding::event_handler::show(std::ostream& os) +{ + m_db.dump(os); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/l3_binding.hpp b/src/vpp-api/vom/l3_binding.hpp new file mode 100644 index 00000000000..2166b44bb94 --- /dev/null +++ b/src/vpp-api/vom/l3_binding.hpp @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_L3_BINDING_H__ +#define __VOM_L3_BINDING_H__ + +#include "vom/dump_cmd.hpp" +#include "vom/hw.hpp" +#include "vom/inspect.hpp" +#include "vom/interface.hpp" +#include "vom/object_base.hpp" +#include "vom/om.hpp" +#include "vom/rpc_cmd.hpp" +#include "vom/singular_db.hpp" + +#include + +namespace VOM { +/** + * A representation of L3 configuration on an interface + */ +class l3_binding : public object_base +{ +public: + /** + * Construct a new object matching the desried state + */ + l3_binding(const interface& itf, const route::prefix_t& pfx); + + /** + * Copy Constructor + */ + l3_binding(const l3_binding& o); + + /** + * Destructor + */ + ~l3_binding(); + + /** + * Return the 'singular instance' of the L3-Config that matches this + * object + */ + std::shared_ptr singular() const; + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Return the prefix associated with this L3config + */ + const route::prefix_t& prefix() const; + + /** + * Dump all l3_bindings into the stream provided + */ + static void dump(std::ostream& os); + + /** + * The key type for l3_bindings + */ + typedef std::pair key_type_t; + + /** + * Find an singular instance in the DB for the interface passed + */ + static std::deque> find(const interface& i); + + /** + * A functor class that binds the L3 config to the interface + */ + class bind_cmd + : public rpc_cmd, rc_t, vapi::Sw_interface_add_del_address> + { + public: + /** + * Constructor + */ + bind_cmd(HW::item& item, + const handle_t& itf, + const route::prefix_t& pfx); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const bind_cmd& i) const; + + private: + /** + * Reference to the interface to bind to + */ + const handle_t& m_itf; + + /** + * The prefix to bind + */ + const route::prefix_t& m_pfx; + }; + + /** + * A cmd class that Unbinds L3 Config from an interface + */ + class unbind_cmd + : public rpc_cmd, rc_t, vapi::Sw_interface_add_del_address> + { + public: + /** + * Constructor + */ + unbind_cmd(HW::item& item, + const handle_t& itf, + const route::prefix_t& pfx); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const unbind_cmd& i) const; + + private: + /** + * Reference to the interface to unbind fomr + */ + const handle_t& m_itf; + + /** + * The prefix to unbind + */ + const route::prefix_t& m_pfx; + }; + + /** + * A cmd class that Dumps all the IPv4 L3 configs + */ + class dump_v4_cmd : public dump_cmd + { + public: + /** + * Constructor + */ + dump_v4_cmd(const handle_t& itf); + dump_v4_cmd(const dump_v4_cmd& d); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const dump_v4_cmd& i) const; + + private: + /** + * HW reutrn code + */ + HW::item item; + + /** + * The interface to get the addresses for + */ + const handle_t& m_itf; + }; + +private: + /** + * Class definition for listeners to OM events + */ + class event_handler : public OM::listener, public inspect::command_handler + { + public: + event_handler(); + virtual ~event_handler() = default; + + /** + * Handle a populate event + */ + void handle_populate(const client_db::key_t& key); + + /** + * Handle a replay event + */ + void handle_replay(); + + /** + * Show the object in the Singular DB + */ + void show(std::ostream& os); + + /** + * Get the sortable Id of the listener + */ + dependency_t order() const; + }; + + /** + * event_handler to register with OM + */ + static event_handler m_evh; + + /** + * Enquue commonds to the VPP command Q for the update + */ + void update(const l3_binding& obj); + + /** + * Find or add the singular instance in the DB + */ + static std::shared_ptr find_or_add(const l3_binding& temp); + + /* + * It's the VPPHW class that updates the objects in HW + */ + friend class OM; + + /** + e* It's the singular_db class that calls replay() + */ + friend class singular_db; + + /** + * Sweep/reap the object if still stale + */ + void sweep(void); + + /** + * replay the object to create it in hardware + */ + void replay(void); + + /** + * A reference counting pointer the interface that this L3 layer + * represents. By holding the reference here, we can guarantee that + * this object will outlive the interface + */ + const std::shared_ptr m_itf; + + /** + * The prefix for this L3 configuration + */ + const route::prefix_t& m_pfx; + + /** + * HW configuration for the binding. The bool representing the + * do/don't bind. + */ + HW::item m_binding; + + /** + * A map of all L3 configs keyed against a combination of the interface + * and subnet's keys. + */ + static singular_db m_db; +}; + +/** + * Ostream output for the key + */ +std::ostream& operator<<(std::ostream& os, const l3_binding::key_type_t& key); +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/l3_binding_cmds.cpp b/src/vpp-api/vom/l3_binding_cmds.cpp new file mode 100644 index 00000000000..788d30f892f --- /dev/null +++ b/src/vpp-api/vom/l3_binding_cmds.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/l3_binding.hpp" + +DEFINE_VAPI_MSG_IDS_IP_API_JSON; + +namespace VOM { +l3_binding::bind_cmd::bind_cmd(HW::item& item, + const handle_t& itf, + const route::prefix_t& pfx) + : rpc_cmd(item) + , m_itf(itf) + , m_pfx(pfx) +{ +} + +bool +l3_binding::bind_cmd::operator==(const bind_cmd& other) const +{ + return ((m_itf == other.m_itf) && (m_pfx == other.m_pfx)); +} + +rc_t +l3_binding::bind_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_itf.value(); + payload.is_add = 1; + payload.del_all = 0; + + m_pfx.to_vpp(&payload.is_ipv6, payload.address, &payload.address_length); + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return rc_t::OK; +} + +std::string +l3_binding::bind_cmd::to_string() const +{ + std::ostringstream s; + s << "L3-bind: " << m_hw_item.to_string() << " itf:" << m_itf.to_string() + << " pfx:" << m_pfx.to_string(); + + return (s.str()); +} + +l3_binding::unbind_cmd::unbind_cmd(HW::item& item, + const handle_t& itf, + const route::prefix_t& pfx) + : rpc_cmd(item) + , m_itf(itf) + , m_pfx(pfx) +{ +} + +bool +l3_binding::unbind_cmd::operator==(const unbind_cmd& other) const +{ + return ((m_itf == other.m_itf) && (m_pfx == other.m_pfx)); +} + +rc_t +l3_binding::unbind_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_itf.value(); + payload.is_add = 0; + payload.del_all = 0; + + m_pfx.to_vpp(&payload.is_ipv6, payload.address, &payload.address_length); + + VAPI_CALL(req.execute()); + + wait(); + m_hw_item.set(rc_t::NOOP); + + return rc_t::OK; +} + +std::string +l3_binding::unbind_cmd::to_string() const +{ + std::ostringstream s; + s << "L3-unbind: " << m_hw_item.to_string() << " itf:" << m_itf.to_string() + << " pfx:" << m_pfx.to_string(); + + return (s.str()); +} + +l3_binding::dump_v4_cmd::dump_v4_cmd(const handle_t& hdl) + : m_itf(hdl) +{ +} + +l3_binding::dump_v4_cmd::dump_v4_cmd(const dump_v4_cmd& d) + : m_itf(d.m_itf) +{ +} + +bool +l3_binding::dump_v4_cmd::operator==(const dump_v4_cmd& other) const +{ + return (true); +} + +rc_t +l3_binding::dump_v4_cmd::issue(connection& con) +{ + m_dump.reset(new msg_t(con.ctx(), std::ref(*this))); + + auto& payload = m_dump->get_request().get_payload(); + payload.sw_if_index = m_itf.value(); + payload.is_ipv6 = 0; + + VAPI_CALL(m_dump->execute()); + + wait(); + + return rc_t::OK; +} + +std::string +l3_binding::dump_v4_cmd::to_string() const +{ + return ("L3-binding-dump"); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/lldp_binding.cpp b/src/vpp-api/vom/lldp_binding.cpp new file mode 100644 index 00000000000..453f57d37db --- /dev/null +++ b/src/vpp-api/vom/lldp_binding.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/lldp_binding.hpp" +#include "vom/cmd.hpp" + +namespace VOM { +/** + * A DB of all LLDP configs + */ +singular_db lldp_binding::m_db; + +lldp_binding::event_handler lldp_binding::m_evh; + +lldp_binding::lldp_binding(const interface& itf, const std::string& port_desc) + : m_itf(itf.singular()) + , m_port_desc(port_desc) + , m_binding(0) +{ +} + +lldp_binding::lldp_binding(const lldp_binding& o) + : m_itf(o.m_itf) + , m_port_desc(o.m_port_desc) + , m_binding(0) +{ +} + +lldp_binding::~lldp_binding() +{ + sweep(); + + // not in the DB anymore. + m_db.release(m_itf->key(), this); +} + +void +lldp_binding::sweep() +{ + if (m_binding) { + HW::enqueue(new unbind_cmd(m_binding, m_itf->handle())); + } + HW::write(); +} + +void +lldp_binding::dump(std::ostream& os) +{ + m_db.dump(os); +} + +void +lldp_binding::replay() +{ + if (m_binding) { + HW::enqueue(new bind_cmd(m_binding, m_itf->handle(), m_port_desc)); + } +} + +std::string +lldp_binding::to_string() const +{ + std::ostringstream s; + s << "Lldp-binding: " << m_itf->to_string() << " port_desc:" << m_port_desc + << " " << m_binding.to_string(); + + return (s.str()); +} + +void +lldp_binding::update(const lldp_binding& desired) +{ + /* + * the desired state is always that the interface should be created + */ + if (!m_binding) { + HW::enqueue(new bind_cmd(m_binding, m_itf->handle(), m_port_desc)); + } +} + +std::shared_ptr +lldp_binding::find_or_add(const lldp_binding& temp) +{ + return (m_db.find_or_add(temp.m_itf->key(), temp)); +} + +std::shared_ptr +lldp_binding::singular() const +{ + return find_or_add(*this); +} + +lldp_binding::event_handler::event_handler() +{ + OM::register_listener(this); + inspect::register_handler({ "lldp" }, "LLDP bindings", this); +} + +void +lldp_binding::event_handler::handle_replay() +{ + m_db.replay(); +} + +void +lldp_binding::event_handler::handle_populate(const client_db::key_t& key) +{ + // FIXME +} + +dependency_t +lldp_binding::event_handler::order() const +{ + return (dependency_t::BINDING); +} + +void +lldp_binding::event_handler::show(std::ostream& os) +{ + m_db.dump(os); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/lldp_binding.hpp b/src/vpp-api/vom/lldp_binding.hpp new file mode 100644 index 00000000000..993a2fd3d4b --- /dev/null +++ b/src/vpp-api/vom/lldp_binding.hpp @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_LLDP_BINDING_H__ +#define __VOM_LLDP_BINDING_H__ + +#include "vom/dump_cmd.hpp" +#include "vom/hw.hpp" +#include "vom/inspect.hpp" +#include "vom/interface.hpp" +#include "vom/object_base.hpp" +#include "vom/om.hpp" +#include "vom/rpc_cmd.hpp" +#include "vom/singular_db.hpp" +#include "vom/sub_interface.hpp" + +#include + +namespace VOM { +/** + * A representation of LLDP client configuration on an interface + */ +class lldp_binding : public object_base +{ +public: + /** + * Construct a new object matching the desried state + */ + lldp_binding(const interface& itf, const std::string& hostname); + + /** + * Copy Constructor + */ + lldp_binding(const lldp_binding& o); + /** + * Destructor + */ + ~lldp_binding(); + + /** + * Return the 'singular' of the LLDP binding that matches this object + */ + std::shared_ptr singular() const; + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Dump all LLDP bindings into the stream provided + */ + static void dump(std::ostream& os); + + /** + * A command class that binds the LLDP config to the interface + */ + class bind_cmd + : public rpc_cmd, rc_t, vapi::Sw_interface_set_lldp> + { + public: + /** + * Constructor + */ + bind_cmd(HW::item& item, + const handle_t& itf, + const std::string& port_desc); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const bind_cmd& i) const; + + private: + /** + * Reference to the HW::item of the interface to bind + */ + const handle_t& m_itf; + + /** + * The LLDP client's hostname + */ + const std::string m_port_desc; + }; + + /** + * A cmd class that Unbinds Lldp Config from an interface + */ + class unbind_cmd + : public rpc_cmd, rc_t, vapi::Sw_interface_set_lldp> + { + public: + /** + * Constructor + */ + unbind_cmd(HW::item& item, const handle_t& itf); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const unbind_cmd& i) const; + + private: + /** + * Reference to the HW::item of the interface to unbind + */ + const handle_t& m_itf; + }; + +private: + /** + * Class definition for listeners to OM events + */ + class event_handler : public OM::listener, public inspect::command_handler + { + public: + event_handler(); + virtual ~event_handler() = default; + + /** + * Handle a populate event + */ + void handle_populate(const client_db::key_t& key); + + /** + * Handle a replay event + */ + void handle_replay(); + + /** + * Show the object in the Singular DB + */ + void show(std::ostream& os); + + /** + * Get the sortable Id of the listener + */ + dependency_t order() const; + }; + + /** + * event_handler to register with OM + */ + static event_handler m_evh; + + /** + * Enquue commonds to the VPP command Q for the update + */ + void update(const lldp_binding& obj); + + /** + * Find or add LLDP binding to the OM + */ + static std::shared_ptr find_or_add(const lldp_binding& temp); + + /* + * It's the OM class that calls singular() + */ + friend class OM; + + /** + * It's the singular_db class that calls replay() + */ + friend class singular_db; + + /** + * Sweep/reap the object if still stale + */ + void sweep(void); + + /** + * replay the object to create it in hardware + */ + void replay(void); + + /** + * A reference counting pointer to the interface on which LLDP config + * resides. By holding the reference here, we can guarantee that + * this object will outlive the interface + */ + const std::shared_ptr m_itf; + + /** + * The port-description in the LLDP configuration + */ + const std::string m_port_desc; + + /** + * HW configuration for the binding. The bool representing the + * do/don't bind. + */ + HW::item m_binding; + + /** + * A map of all Lldp bindings keyed against the interface. + */ + static singular_db m_db; +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/lldp_binding_cmds.cpp b/src/vpp-api/vom/lldp_binding_cmds.cpp new file mode 100644 index 00000000000..9718c59b8f0 --- /dev/null +++ b/src/vpp-api/vom/lldp_binding_cmds.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/lldp_binding.hpp" + +DEFINE_VAPI_MSG_IDS_LLDP_API_JSON; + +namespace VOM { +lldp_binding::bind_cmd::bind_cmd(HW::item& item, + const handle_t& itf, + const std::string& port_desc) + : rpc_cmd(item) + , m_itf(itf) + , m_port_desc(port_desc) +{ +} + +bool +lldp_binding::bind_cmd::operator==(const bind_cmd& other) const +{ + return ((m_itf == other.m_itf) && (m_port_desc == other.m_port_desc)); +} + +rc_t +lldp_binding::bind_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_itf.value(); + payload.enable = 1; + + memcpy(payload.port_desc, m_port_desc.c_str(), + std::min(sizeof(payload.port_desc), m_port_desc.length())); + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return rc_t::OK; +} + +std::string +lldp_binding::bind_cmd::to_string() const +{ + std::ostringstream s; + s << "Lldp-bind: " << m_hw_item.to_string() << " itf:" << m_itf.to_string() + << " port_desc:" << m_port_desc; + + return (s.str()); +} + +lldp_binding::unbind_cmd::unbind_cmd(HW::item& item, const handle_t& itf) + : rpc_cmd(item) + , m_itf(itf) +{ +} + +bool +lldp_binding::unbind_cmd::operator==(const unbind_cmd& other) const +{ + return (m_itf == other.m_itf); +} + +rc_t +lldp_binding::unbind_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_itf.value(); + payload.enable = 0; + + VAPI_CALL(req.execute()); + + wait(); + m_hw_item.set(rc_t::NOOP); + + return rc_t::OK; +} + +std::string +lldp_binding::unbind_cmd::to_string() const +{ + std::ostringstream s; + s << "Lldp-unbind: " << m_hw_item.to_string() << " itf:" << m_itf.to_string(); + + return (s.str()); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/lldp_global.cpp b/src/vpp-api/vom/lldp_global.cpp new file mode 100644 index 00000000000..92526189d34 --- /dev/null +++ b/src/vpp-api/vom/lldp_global.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/lldp_global.hpp" +#include "vom/cmd.hpp" + +namespace VOM { +/** + * A DB of all LLDP configs + */ +singular_db lldp_global::m_db; + +lldp_global::event_handler lldp_global::m_evh; + +lldp_global::lldp_global(const std::string& system_name, + uint32_t tx_hold, + uint32_t tx_interval) + : m_system_name(system_name) + , m_tx_hold(tx_hold) + , m_tx_interval(tx_interval) +{ +} + +lldp_global::lldp_global(const lldp_global& o) + : m_system_name(o.m_system_name) + , m_tx_hold(o.m_tx_hold) + , m_tx_interval(o.m_tx_interval) +{ +} + +lldp_global::~lldp_global() +{ + sweep(); + + // not in the DB anymore. + m_db.release(m_system_name, this); +} + +void +lldp_global::sweep() +{ + // no means to remove this in VPP +} + +void +lldp_global::dump(std::ostream& os) +{ + m_db.dump(os); +} + +void +lldp_global::replay() +{ + if (m_binding) { + HW::enqueue( + new config_cmd(m_binding, m_system_name, m_tx_hold, m_tx_interval)); + } +} + +std::string +lldp_global::to_string() const +{ + std::ostringstream s; + s << "LLDP-global:" + << " system_name:" << m_system_name << " tx-hold:" << m_tx_hold + << " tx-interval:" << m_tx_interval; + + return (s.str()); +} + +void +lldp_global::update(const lldp_global& desired) +{ + if (!m_binding) { + HW::enqueue( + new config_cmd(m_binding, m_system_name, m_tx_hold, m_tx_interval)); + } +} + +std::shared_ptr +lldp_global::find_or_add(const lldp_global& temp) +{ + return (m_db.find_or_add(temp.m_system_name, temp)); +} + +std::shared_ptr +lldp_global::singular() const +{ + return find_or_add(*this); +} + +lldp_global::event_handler::event_handler() +{ + OM::register_listener(this); + inspect::register_handler({ "lldp-global" }, "LLDP global configurations", + this); +} + +void +lldp_global::event_handler::handle_replay() +{ + m_db.replay(); +} + +void +lldp_global::event_handler::handle_populate(const client_db::key_t& key) +{ + // FIXME +} + +dependency_t +lldp_global::event_handler::order() const +{ + return (dependency_t::GLOBAL); +} + +void +lldp_global::event_handler::show(std::ostream& os) +{ + m_db.dump(os); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/lldp_global.hpp b/src/vpp-api/vom/lldp_global.hpp new file mode 100644 index 00000000000..9426ba001ae --- /dev/null +++ b/src/vpp-api/vom/lldp_global.hpp @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_LLDP_GLOBAL_H__ +#define __VOM_LLDP_GLOBAL_H__ + +#include "vom/dump_cmd.hpp" +#include "vom/hw.hpp" +#include "vom/inspect.hpp" +#include "vom/interface.hpp" +#include "vom/object_base.hpp" +#include "vom/om.hpp" +#include "vom/rpc_cmd.hpp" +#include "vom/singular_db.hpp" +#include "vom/sub_interface.hpp" + +#include + +namespace VOM { +/** + * A representation of LLDP global configuration + */ +class lldp_global : public object_base +{ +public: + /** + * Construct a new object matching the desried state + */ + lldp_global(const std::string& system_name, + uint32_t tx_hold, + uint32_t tx_interval); + + /** + * Copy Constructor + */ + lldp_global(const lldp_global& o); + + /** + * Destructor + */ + ~lldp_global(); + + /** + * Return the 'singular' of the LLDP global that matches this object + */ + std::shared_ptr singular() const; + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Dump all LLDP globals into the stream provided + */ + static void dump(std::ostream& os); + + /** + * A command class that binds the LLDP global to the interface + */ + class config_cmd : public rpc_cmd, rc_t, vapi::Lldp_config> + { + public: + /** + * Constructor + */ + config_cmd(HW::item& item, + const std::string& system_name, + uint32_t tx_hold, + uint32_t tx_interval); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const config_cmd& i) const; + + private: + /** + * The system name + */ + const std::string m_system_name; + + /** + * TX timer configs + */ + uint32_t m_tx_hold; + uint32_t m_tx_interval; + }; + +private: + /** + * Class definition for listeners to OM events + */ + class event_handler : public OM::listener, public inspect::command_handler + { + public: + event_handler(); + virtual ~event_handler() = default; + + /** + * Handle a populate event + */ + void handle_populate(const client_db::key_t& key); + + /** + * Handle a replay event + */ + void handle_replay(); + + /** + * Show the object in the Singular DB + */ + void show(std::ostream& os); + + /** + * Get the sortable Id of the listener + */ + dependency_t order() const; + }; + + /** + * event_handler to register with OM + */ + static event_handler m_evh; + + /** + * Enquue commonds to the VPP command Q for the update + */ + void update(const lldp_global& obj); + + /** + * Find or add LLDP global to the OM + */ + static std::shared_ptr find_or_add(const lldp_global& temp); + + /* + * It's the OM class that calls singular() + */ + friend class OM; + + /** + * It's the singular_db class that calls replay() + */ + friend class singular_db; + + /** + * Sweep/reap the object if still stale + */ + void sweep(void); + + /** + * replay the object to create it in hardware + */ + void replay(void); + + /** + * The system name + */ + const std::string m_system_name; + + /** + * TX timer configs + */ + uint32_t m_tx_hold; + uint32_t m_tx_interval; + + /** + * HW globaluration for the binding. The bool representing the + * do/don't bind. + */ + HW::item m_binding; + + /** + * A map of all Lldp globals keyed against the system name. + * there needs to be some sort of key, that will do. + */ + static singular_db m_db; +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/lldp_global_cmds.cpp b/src/vpp-api/vom/lldp_global_cmds.cpp new file mode 100644 index 00000000000..9d44a7cbca1 --- /dev/null +++ b/src/vpp-api/vom/lldp_global_cmds.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/lldp_global.hpp" + +namespace VOM { +lldp_global::config_cmd::config_cmd(HW::item& item, + const std::string& system_name, + uint32_t tx_hold, + uint32_t tx_interval) + : rpc_cmd(item) + , m_system_name(system_name) + , m_tx_hold(tx_hold) + , m_tx_interval(tx_interval) +{ +} + +bool +lldp_global::config_cmd::operator==(const config_cmd& other) const +{ + return (m_system_name == other.m_system_name); +} + +rc_t +lldp_global::config_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.tx_hold = m_tx_hold; + payload.tx_interval = m_tx_interval; + + memcpy(payload.system_name, m_system_name.c_str(), + std::min(sizeof(payload.system_name), m_system_name.length())); + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return rc_t::OK; +} + +std::string +lldp_global::config_cmd::to_string() const +{ + std::ostringstream s; + s << "Lldp-global-config: " << m_hw_item.to_string() + << " system_name:" << m_system_name << " tx-hold:" << m_tx_hold + << " tx-interval:" << m_tx_interval; + + return (s.str()); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/logger.cpp b/src/vpp-api/vom/logger.cpp new file mode 100644 index 00000000000..871473a8212 --- /dev/null +++ b/src/vpp-api/vom/logger.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include "vom/logger.hpp" + +namespace VOM { +const log_level_t log_level_t::CRITICAL(0, "critical"); +const log_level_t log_level_t::ERROR(0, "error"); +const log_level_t log_level_t::WARNING(0, "warning"); +const log_level_t log_level_t::INFO(0, "info"); +const log_level_t log_level_t::DEBUG(0, "debug"); + +log_level_t::log_level_t(int v, const std::string& s) + : enum_base(v, s) +{ +} + +static log_t slog; + +log_t& +logger() +{ + return slog; +} + +log_t::log_t() + : m_level(log_level_t::ERROR) + , m_o_stream(&std::cout) +{ +} + +void +log_t::set(const log_level_t& level) +{ + m_level = level; +} + +void +log_t::set(const std::string& ofile) +{ + m_file_stream.open(ofile); + m_o_stream = &m_file_stream; +} + +std::ostream& +log_t::stream(const char* file, int line) +{ + auto end = std::chrono::system_clock::now(); + auto end_time = std::chrono::system_clock::to_time_t(end); + + /* + * put-time is not support in gcc in 4.8 + * so we play this dance with ctime + */ + std::string display = std::ctime(&end_time); + display.pop_back(); + + std::vector dirs; + boost::split(dirs, file, boost::is_any_of("/")); + + *m_o_stream << std::endl + << display << "]" + << " " << dirs.back() << ":" << line << ": "; + + return (*m_o_stream); +} + +/** + * The configured level + */ +const log_level_t& +log_t::level() const +{ + return (m_level); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/logger.hpp b/src/vpp-api/vom/logger.hpp new file mode 100644 index 00000000000..0adc6770e2c --- /dev/null +++ b/src/vpp-api/vom/logger.hpp @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_LOGGER_H__ +#define __VOM_LOGGER_H__ + +#include +#include + +#include "vom/enum_base.hpp" + +namespace VOM { +struct log_level_t : enum_base +{ + const static log_level_t CRITICAL; + const static log_level_t ERROR; + const static log_level_t WARNING; + const static log_level_t INFO; + const static log_level_t DEBUG; + +private: + /** + * Private constructor taking the value and the string name + */ + log_level_t(int v, const std::string& s); +}; + +/** + * Ideally we'd use the boost logger but that is not prevelent + * in many distros. So something simple here instead. + */ +class log_t +{ +public: + /** + * Construct a logger + */ + log_t(); + + /** + * Return the stream + */ + std::ostream& stream(const char* file, int line); + + /** + * The configured level + */ + const log_level_t& level() const; + + /** + * set the logging level + */ + void set(const log_level_t& level); + + /** + * set a file to receive the logging data + */ + void set(const std::string& ofile); + +private: + /** + * the configured logging level + */ + log_level_t m_level; + + /** + * Opened file for debugging + */ + std::ofstream m_file_stream; + + /** + * Pointer to the output stream + */ + std::ostream* m_o_stream; +}; + +/** + * Return a log object into which VPP objects can write + */ +log_t& logger(); + +#define VOM_LOG(lvl) \ + if (lvl >= logger().level()) \ + logger().stream(__FILE__, __LINE__) +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/nat_binding.cpp b/src/vpp-api/vom/nat_binding.cpp new file mode 100644 index 00000000000..beb76bd8494 --- /dev/null +++ b/src/vpp-api/vom/nat_binding.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/nat_binding.hpp" +#include "vom/cmd.hpp" + +namespace VOM { +singular_db nat_binding::m_db; + +nat_binding::event_handler nat_binding::m_evh; + +const nat_binding::zone_t nat_binding::zone_t::INSIDE(0, "inside"); +const nat_binding::zone_t nat_binding::zone_t::OUTSIDE(0, "outside"); + +nat_binding::zone_t::zone_t(int v, const std::string s) + : enum_base(v, s) +{ +} + +/** + * Construct a new object matching the desried state + */ +nat_binding::nat_binding(const interface& itf, + const direction_t& dir, + const l3_proto_t& proto, + const zone_t& zone) + : m_binding(false) + , m_itf(itf.singular()) + , m_dir(dir) + , m_proto(proto) + , m_zone(zone) +{ +} + +nat_binding::nat_binding(const nat_binding& o) + : m_binding(o.m_binding) + , m_itf(o.m_itf) + , m_dir(o.m_dir) + , m_proto(o.m_proto) + , m_zone(o.m_zone) +{ +} + +nat_binding::~nat_binding() +{ + sweep(); + m_db.release(make_tuple(m_itf->key(), m_dir, m_proto), this); +} + +void +nat_binding::sweep() +{ + if (m_binding) { + if (direction_t::INPUT == m_dir) { + HW::enqueue(new unbind_44_input_cmd(m_binding, m_itf->handle(), m_zone)); + } else { + assert(!"Unimplemented"); + } + } + HW::write(); +} + +void +nat_binding::replay() +{ + if (m_binding) { + if (direction_t::INPUT == m_dir) { + HW::enqueue(new bind_44_input_cmd(m_binding, m_itf->handle(), m_zone)); + } else { + assert(!"Unimplemented"); + } + } +} + +void +nat_binding::update(const nat_binding& desired) +{ + /* + * the desired state is always that the interface should be created + */ + if (!m_binding) { + if (direction_t::INPUT == m_dir) { + HW::enqueue(new bind_44_input_cmd(m_binding, m_itf->handle(), m_zone)); + } else { + assert(!"Unimplemented"); + } + } +} + +std::string +nat_binding::to_string() const +{ + std::ostringstream s; + s << "nat-binding:[" << m_itf->to_string() << " " << m_dir.to_string() << " " + << m_proto.to_string() << " " << m_zone.to_string() << "]"; + + return (s.str()); +} + +std::shared_ptr +nat_binding::find_or_add(const nat_binding& temp) +{ + return (m_db.find_or_add( + make_tuple(temp.m_itf->key(), temp.m_dir, temp.m_proto), temp)); +} + +std::shared_ptr +nat_binding::singular() const +{ + return find_or_add(*this); +} + +void +nat_binding::dump(std::ostream& os) +{ + m_db.dump(os); +} + +std::ostream& +operator<<(std::ostream& os, const nat_binding::key_t& key) +{ + os << "[" << std::get<0>(key) << ", " << std::get<1>(key) << ", " + << std::get<2>(key) << "]"; + + return (os); +} + +nat_binding::event_handler::event_handler() +{ + OM::register_listener(this); + inspect::register_handler({ "nat-binding" }, "NAT bindings", this); +} + +void +nat_binding::event_handler::handle_replay() +{ + m_db.replay(); +} + +void +nat_binding::event_handler::handle_populate(const client_db::key_t& key) +{ + /** + * This is done while populating the interfaces + */ +} + +dependency_t +nat_binding::event_handler::order() const +{ + return (dependency_t::BINDING); +} + +void +nat_binding::event_handler::show(std::ostream& os) +{ + m_db.dump(os); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/nat_binding.hpp b/src/vpp-api/vom/nat_binding.hpp new file mode 100644 index 00000000000..6e871f9aa91 --- /dev/null +++ b/src/vpp-api/vom/nat_binding.hpp @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_NAT_BINDING_H__ +#define __VOM_NAT_BINDING_H__ + +#include "vom/hw.hpp" +#include "vom/interface.hpp" +#include "vom/object_base.hpp" +#include "vom/om.hpp" +#include "vom/rpc_cmd.hpp" +#include "vom/singular_db.hpp" + +#include + +namespace VOM { +/** + * A Clas representing the binding of an L2 interface to a bridge-domain + * and the properties of that binding. + */ +class nat_binding : public object_base +{ +public: + /** + * NAT Zoness + */ + struct zone_t : public enum_base + { + /** + * Constructor + */ + zone_t(int v, const std::string s); + + /** + * Destructor + */ + ~zone_t() = default; + + /** + * Permit Zone + */ + const static zone_t INSIDE; + + /** + * Deny Zone + */ + const static zone_t OUTSIDE; + }; + + /** + * The key for a NAT Binding. + * The zoe is not included, since the same interface is never inside + * and outside. + */ + typedef std::tuple key_t; + + /** + * Construct a new object matching the desried state + * @param itf The interface onto which we bind/apply the feature + * @param dir The direction (input/output) + * @param proto The L3 proto used inside. + * @param zone The NAT zone for the link + */ + nat_binding(const interface& itf, + const direction_t& dir, + const l3_proto_t& proto, + const zone_t& zone); + + /** + * Copy Constructor + */ + nat_binding(const nat_binding& o); + + /** + * Destructor + */ + ~nat_binding(); + + /** + * Return the 'singular instance' of the L2 config that matches this + * object + */ + std::shared_ptr singular() const; + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Dump all nat_bindings into the stream provided + */ + static void dump(std::ostream& os); + + /** + * A functor class that binds L2 configuration to an interface + */ + class bind_44_input_cmd + : public rpc_cmd, + rc_t, + vapi::Nat44_interface_add_del_feature> + { + public: + /** + * Constructor + */ + bind_44_input_cmd(HW::item& item, + const handle_t& itf, + const zone_t& zone); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const bind_44_input_cmd& i) const; + + private: + /** + * The interface to bind + */ + const handle_t m_itf; + + /** + * The zone the interface is in + */ + const zone_t m_zone; + }; + + /** + * A cmd class that Unbinds L2 configuration from an interface + */ + class unbind_44_input_cmd + : public rpc_cmd, + rc_t, + vapi::Nat44_interface_add_del_feature> + { + public: + /** + * Constructor + */ + unbind_44_input_cmd(HW::item& item, + const handle_t& itf, + const zone_t& zone); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const unbind_44_input_cmd& i) const; + + private: + /** + * The interface to bind + */ + const handle_t m_itf; + + /** + * The zone the interface is in + */ + const zone_t m_zone; + }; + + /** + * A cmd class that Dumps all the nat_statics + */ + class dump_44_cmd : public dump_cmd + { + public: + /** + * Constructor + */ + dump_44_cmd(); + dump_44_cmd(const dump_44_cmd& d); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const dump_44_cmd& i) const; + + private: + /** + * HW reutrn code + */ + HW::item item; + }; + +private: + /** + * Class definition for listeners to OM events + */ + class event_handler : public OM::listener, public inspect::command_handler + { + public: + event_handler(); + virtual ~event_handler() = default; + + /** + * Handle a populate event + */ + void handle_populate(const client_db::key_t& key); + + /** + * Handle a replay event + */ + void handle_replay(); + + /** + * Show the object in the Singular DB + */ + void show(std::ostream& os); + + /** + * Get the sortable Id of the listener + */ + dependency_t order() const; + }; + + /** + * event_handler to register with OM + */ + static event_handler m_evh; + + /** + * Enquue commonds to the VPP command Q for the update + */ + void update(const nat_binding& obj); + + /** + * Find or Add the singular instance in the DB + */ + static std::shared_ptr find_or_add(const nat_binding& temp); + + /* + * It's the OM class that calls singular() + */ + friend class OM; + + /** + * It's the singular_db class that calls replay() + */ + friend class singular_db; + + /** + * Sweep/reap the object if still stale + */ + void sweep(void); + + /** + * replay the object to create it in hardware + */ + void replay(void); + + /** + * HW configuration for the binding. The bool representing the + * do/don't bind. + */ + HW::item m_binding; + + /** + * A reference counting pointer the interface that this NAT binding + * represents. By holding the reference here, we can guarantee that + * this object will outlive the interface + */ + const std::shared_ptr m_itf; + + /** + * The direction in which the feature applies + */ + direction_t m_dir; + + /** + * The L3 protocol used on the inside + */ + l3_proto_t m_proto; + + /** + * The NAT zone the interface is in + */ + zone_t m_zone; + + /** + * A map of all L2 interfaces key against the interface's handle_t + */ + static singular_db m_db; +}; + +std::ostream& operator<<(std::ostream& os, const nat_binding::key_t& key); +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/nat_binding_cmds.cpp b/src/vpp-api/vom/nat_binding_cmds.cpp new file mode 100644 index 00000000000..38630c13c42 --- /dev/null +++ b/src/vpp-api/vom/nat_binding_cmds.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/nat_binding.hpp" + +namespace VOM { +nat_binding::bind_44_input_cmd::bind_44_input_cmd(HW::item& item, + const handle_t& itf, + const zone_t& zone) + : rpc_cmd(item) + , m_itf(itf) + , m_zone(zone) +{ +} + +bool +nat_binding::bind_44_input_cmd::operator==(const bind_44_input_cmd& other) const +{ + return ((m_itf == other.m_itf) && (m_zone == other.m_zone)); +} + +rc_t +nat_binding::bind_44_input_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.is_add = 1; + payload.is_inside = (zone_t::INSIDE == m_zone ? 1 : 0); + payload.sw_if_index = m_itf.value(); + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return rc_t::OK; +} + +std::string +nat_binding::bind_44_input_cmd::to_string() const +{ + std::ostringstream s; + s << "nat-44-input-binding-create: " << m_hw_item.to_string() + << " itf:" << m_itf << " " << m_zone.to_string(); + + return (s.str()); +} + +nat_binding::unbind_44_input_cmd::unbind_44_input_cmd(HW::item& item, + const handle_t& itf, + const zone_t& zone) + : rpc_cmd(item) + , m_itf(itf) + , m_zone(zone) +{ +} + +bool +nat_binding::unbind_44_input_cmd::operator==( + const unbind_44_input_cmd& other) const +{ + return ((m_itf == other.m_itf) && (m_zone == other.m_zone)); +} + +rc_t +nat_binding::unbind_44_input_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.is_add = 0; + payload.is_inside = (zone_t::INSIDE == m_zone ? 1 : 0); + payload.sw_if_index = m_itf.value(); + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return rc_t::OK; +} + +std::string +nat_binding::unbind_44_input_cmd::to_string() const +{ + std::ostringstream s; + s << "nat-44-input-binding-create: " << m_hw_item.to_string() + << " itf:" << m_itf << " " << m_zone.to_string(); + + return (s.str()); +} + +nat_binding::dump_44_cmd::dump_44_cmd() +{ +} + +nat_binding::dump_44_cmd::dump_44_cmd(const dump_44_cmd& d) +{ +} + +bool +nat_binding::dump_44_cmd::operator==(const dump_44_cmd& other) const +{ + return (true); +} + +rc_t +nat_binding::dump_44_cmd::issue(connection& con) +{ + m_dump.reset(new msg_t(con.ctx(), std::ref(*this))); + + VAPI_CALL(m_dump->execute()); + + wait(); + + return rc_t::OK; +} + +std::string +nat_binding::dump_44_cmd::to_string() const +{ + return ("nat-binding-dump"); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/nat_static.cpp b/src/vpp-api/vom/nat_static.cpp new file mode 100644 index 00000000000..afc46f2457a --- /dev/null +++ b/src/vpp-api/vom/nat_static.cpp @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/nat_static.hpp" + +namespace VOM { +singular_db nat_static::m_db; +nat_static::event_handler nat_static::m_evh; + +nat_static::nat_static(const boost::asio::ip::address& inside, + const boost::asio::ip::address_v4& outside) + : m_hw(false) + , m_rd(route_domain::get_default()) + , m_inside(inside) + , m_outside(outside) +{ +} + +nat_static::nat_static(const route_domain& rd, + const boost::asio::ip::address& inside, + const boost::asio::ip::address_v4& outside) + : m_hw(false) + , m_rd(rd.singular()) + , m_inside(inside) + , m_outside(outside) +{ +} + +nat_static::nat_static(const nat_static& ns) + : m_hw(ns.m_hw) + , m_rd(ns.m_rd) + , m_inside(ns.m_inside) + , m_outside(ns.m_outside) +{ +} + +nat_static::~nat_static() +{ + sweep(); + + // not in the DB anymore. + m_db.release(std::make_pair(m_rd->key(), m_outside), this); +} + +void +nat_static::sweep() +{ + if (m_hw) { + if (m_inside.is_v4()) { + HW::enqueue( + new delete_44_cmd(m_hw, m_rd->table_id(), m_inside.to_v4(), m_outside)); + } + } + HW::write(); +} + +void +nat_static::replay() +{ + if (m_hw) { + if (m_inside.is_v4()) { + HW::enqueue( + new create_44_cmd(m_hw, m_rd->table_id(), m_inside.to_v4(), m_outside)); + } + } +} + +void +nat_static::update(const nat_static& r) +{ + /* + * create the table if it is not yet created + */ + if (rc_t::OK != m_hw.rc()) { + if (m_inside.is_v4()) { + HW::enqueue( + new create_44_cmd(m_hw, m_rd->table_id(), m_inside.to_v4(), m_outside)); + } + } +} + +std::string +nat_static::to_string() const +{ + std::ostringstream s; + s << "nat-static:[" + << "table:" << m_rd->to_string() << " inside: " << m_inside.to_string() + << " outside " << m_outside.to_string() << "]"; + + return (s.str()); +} + +std::shared_ptr +nat_static::find_or_add(const nat_static& temp) +{ + return ( + m_db.find_or_add(std::make_pair(temp.m_rd->key(), temp.m_outside), temp)); +} + +std::shared_ptr +nat_static::singular() const +{ + return find_or_add(*this); +} + +void +nat_static::dump(std::ostream& os) +{ + m_db.dump(os); +} + +std::ostream& +operator<<(std::ostream& os, const nat_static::key_t& key) +{ + os << "[" << key.first << ", " << key.second << "]"; + + return (os); +} + +nat_static::event_handler::event_handler() +{ + OM::register_listener(this); + inspect::register_handler({ "nat-static" }, "NAT Statics", this); +} + +void +nat_static::event_handler::handle_replay() +{ + m_db.replay(); +} + +/* void nat_static::populate_i(const client_db::key_t &key, */ +/* std::shared_ptr itf, */ +/* const l3_proto_t &proto) */ +/* { */ +/* /\* */ +/* * dump VPP current states */ +/* *\/ */ +/* std::shared_ptr cmd = */ +/* std::make_shared(nat_static::dump_cmd(itf->handle(), + * proto)); */ + +/* HW::enqueue(cmd); */ +/* HW::write(); */ + +/* for (auto & record : *cmd) */ +/* { */ +/* /\* */ +/* * construct a nat_static from each recieved record. */ +/* *\/ */ +/* auto &payload = record.get_payload(); */ + +/* mac_address_t mac(payload.mac_address); */ +/* boost::asio::ip::address ip_addr = from_bytes(payload.is_ipv6, */ +/* payload.ip_address); + */ +/* nat_static n(*itf, mac, ip_addr); */ + +/* VOM_LOG(log_level_t::DEBUG) << "nat_static-dump: " */ +/* << itf->to_string() */ +/* << mac.to_string() */ +/* << ip_addr.to_string(); */ + +/* /\* */ +/* * Write each of the discovered interfaces into the OM, */ +/* * but disable the HW Command q whilst we do, so that no */ +/* * commands are sent to VPP */ +/* *\/ */ +/* OM::commit(key, n); */ +/* } */ +/* } */ + +void +nat_static::event_handler::handle_populate(const client_db::key_t& key) +{ + /* auto it = interface::cbegin(); */ + + /* while (it != interface::cend()) */ + /* { */ + /* nat_static::populate_i(key, it->second.lock(), l3_proto_t::IPV4); + */ + /* nat_static::populate_i(key, it->second.lock(), l3_proto_t::IPV6); + */ + + /* ++it; */ + /* } */ +} + +dependency_t +nat_static::event_handler::order() const +{ + return (dependency_t::ENTRY); +} + +void +nat_static::event_handler::show(std::ostream& os) +{ + m_db.dump(os); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/nat_static.hpp b/src/vpp-api/vom/nat_static.hpp new file mode 100644 index 00000000000..1aa4a5e79c3 --- /dev/null +++ b/src/vpp-api/vom/nat_static.hpp @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_NAT_STATIC_H__ +#define __VOM_NAT_STATIC_H__ + +#include "vom/dump_cmd.hpp" +#include "vom/route.hpp" +#include "vom/singular_db.hpp" +#include "vom/types.hpp" + +#include + +namespace VOM { +/** + * A entry in the ARP termination table of a Bridge Domain + */ +class nat_static : public object_base +{ +public: + /** + * The key for a NAT static mapping. + * So far only model the address only case. The address + * is the outside. + */ + typedef std::pair key_t; + + /** + * Construct an NAT Static binding with the outside address in default + * table + */ + nat_static(const boost::asio::ip::address& inside, + const boost::asio::ip::address_v4& outside); + + /** + * Construct an NAT Static binding with the outside address in + * route-domain specified + */ + nat_static(const route_domain& rd, + const boost::asio::ip::address& inside, + const boost::asio::ip::address_v4& outside); + + /** + * Copy Construct + */ + nat_static(const nat_static& r); + + /** + * Destructor + */ + ~nat_static(); + + /** + * Return the matching 'singular instance' + */ + std::shared_ptr singular() const; + + /** + * Find the instnace of the bridge_domain domain in the OM + */ + static std::shared_ptr find(const nat_static& temp); + + /** + * Dump all bridge_domain-doamin into the stream provided + */ + static void dump(std::ostream& os); + + /** + * replay the object to create it in hardware + */ + void replay(void); + + /** + * Convert to string for debugging + */ + std::string to_string() const; + + /** + * A command class that creates NAT 44 static mapping + */ + class create_44_cmd + : public rpc_cmd, rc_t, vapi::Nat44_add_del_static_mapping> + { + public: + /** + * Constructor + */ + create_44_cmd(HW::item& item, + route::table_id_t id, + const boost::asio::ip::address_v4& inside, + const boost::asio::ip::address_v4& outside); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const create_44_cmd& i) const; + + private: + route::table_id_t m_id; + const boost::asio::ip::address_v4 m_inside; + const boost::asio::ip::address_v4 m_outside; + }; + + /** + * A cmd class that deletes a NAT 44 static mapping + */ + class delete_44_cmd + : public rpc_cmd, rc_t, vapi::Nat44_add_del_static_mapping> + { + public: + /** + * Constructor + */ + delete_44_cmd(HW::item& item, + route::table_id_t id, + const boost::asio::ip::address_v4& inside, + const boost::asio::ip::address_v4& outside); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const delete_44_cmd& i) const; + + private: + route::table_id_t m_id; + const boost::asio::ip::address_v4 m_inside; + const boost::asio::ip::address_v4 m_outside; + }; + + /** + * A cmd class that Dumps all the nat_statics + */ + class dump_44_cmd : public dump_cmd + { + public: + /** + * Constructor + */ + dump_44_cmd(); + dump_44_cmd(const dump_44_cmd& d); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const dump_44_cmd& i) const; + + private: + /** + * HW reutrn code + */ + HW::item item; + }; + +private: + /** + * Class definition for listeners to OM events + */ + class event_handler : public OM::listener, public inspect::command_handler + { + public: + event_handler(); + virtual ~event_handler() = default; + + /** + * Handle a populate event + */ + void handle_populate(const client_db::key_t& key); + + /** + * Handle a replay event + */ + void handle_replay(); + + /** + * Show the object in the Singular DB + */ + void show(std::ostream& os); + + /** + * Get the sortable Id of the listener + */ + dependency_t order() const; + }; + + /** + * event_handler to register with OM + */ + static event_handler m_evh; + + /** + * Commit the acculmulated changes into VPP. i.e. to a 'HW" write. + */ + void update(const nat_static& obj); + + /** + * Find or add the instnace of the bridge_domain domain in the OM + */ + static std::shared_ptr find_or_add(const nat_static& temp); + + /* + * It's the VPPHW class that updates the objects in HW + */ + friend class OM; + + /** + * It's the singular_db class that calls replay() + */ + friend class singular_db; + + /** + * Sweep/reap the object if still stale + */ + void sweep(void); + + /** + * HW configuration for the result of creating the bridge_domain + */ + HW::item m_hw; + + /** + * The table-ID the outside address resides in + */ + std::shared_ptr m_rd; + + /** + * The 'inside' IP address, could be v4 or v6 + */ + const boost::asio::ip::address& m_inside; + + /** + * The 'outside' IP address - always v4 + */ + const boost::asio::ip::address_v4& m_outside; + + /** + * A map of all NAT statics + */ + static singular_db m_db; +}; + +std::ostream& operator<<(std::ostream& os, const nat_static::key_t& key); +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/nat_static_cmds.cpp b/src/vpp-api/vom/nat_static_cmds.cpp new file mode 100644 index 00000000000..4325f3703f3 --- /dev/null +++ b/src/vpp-api/vom/nat_static_cmds.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/nat_static.hpp" + +DEFINE_VAPI_MSG_IDS_NAT_API_JSON; + +namespace VOM { +nat_static::create_44_cmd::create_44_cmd( + HW::item& item, + route::table_id_t id, + const boost::asio::ip::address_v4& inside, + const boost::asio::ip::address_v4& outside) + : rpc_cmd(item) + , m_id(id) + , m_inside(inside) + , m_outside(outside) +{ +} + +bool +nat_static::create_44_cmd::operator==(const create_44_cmd& other) const +{ + return ((m_id == other.m_id) && (m_inside == other.m_inside) && + (m_outside == other.m_outside)); +} + +rc_t +nat_static::create_44_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.is_add = 1; + payload.addr_only = 1; + payload.local_port = 0; + payload.external_port = 0; + payload.vrf_id = m_id; + payload.external_sw_if_index = ~0; + to_bytes(m_inside, payload.local_ip_address); + to_bytes(m_outside, payload.external_ip_address); + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return rc_t::OK; +} + +std::string +nat_static::create_44_cmd::to_string() const +{ + std::ostringstream s; + s << "nat-44-static-create: " << m_hw_item.to_string() << " table:" << m_id + << " inside:" << m_inside.to_string() + << " outside:" << m_outside.to_string(); + + return (s.str()); +} + +nat_static::delete_44_cmd::delete_44_cmd( + HW::item& item, + route::table_id_t id, + const boost::asio::ip::address_v4& inside, + const boost::asio::ip::address_v4& outside) + : rpc_cmd(item) + , m_id(id) + , m_inside(inside) + , m_outside(outside) +{ +} + +bool +nat_static::delete_44_cmd::operator==(const delete_44_cmd& other) const +{ + return ((m_id == other.m_id) && (m_inside == other.m_inside) && + (m_outside == other.m_outside)); +} + +rc_t +nat_static::delete_44_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.is_add = 0; + payload.addr_only = 1; + payload.local_port = 0; + payload.external_port = 0; + payload.vrf_id = m_id; + payload.external_sw_if_index = ~0; + to_bytes(m_inside, payload.local_ip_address); + to_bytes(m_outside, payload.external_ip_address); + + VAPI_CALL(req.execute()); + + wait(); + m_hw_item.set(rc_t::NOOP); + + return rc_t::OK; +} + +std::string +nat_static::delete_44_cmd::to_string() const +{ + std::ostringstream s; + s << "nat-44-static-delete: " << m_hw_item.to_string() << " table:" << m_id + << " inside:" << m_inside.to_string() + << " outside:" << m_outside.to_string(); + + return (s.str()); +} + +nat_static::dump_44_cmd::dump_44_cmd() +{ +} + +nat_static::dump_44_cmd::dump_44_cmd(const dump_44_cmd& d) +{ +} + +bool +nat_static::dump_44_cmd::operator==(const dump_44_cmd& other) const +{ + return (true); +} + +rc_t +nat_static::dump_44_cmd::issue(connection& con) +{ + m_dump.reset(new msg_t(con.ctx(), std::ref(*this))); + + VAPI_CALL(m_dump->execute()); + + wait(); + + return rc_t::OK; +} + +std::string +nat_static::dump_44_cmd::to_string() const +{ + return ("nat-static-dump"); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/neighbour.cpp b/src/vpp-api/vom/neighbour.cpp new file mode 100644 index 00000000000..c4dcf1f81df --- /dev/null +++ b/src/vpp-api/vom/neighbour.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/neighbour.hpp" + +namespace VOM { +singular_db neighbour::m_db; +neighbour::event_handler neighbour::m_evh; + +neighbour::neighbour(const interface& itf, + const mac_address_t& mac, + const boost::asio::ip::address& ip_addr) + : m_hw(false) + , m_itf(itf.singular()) + , m_mac(mac) + , m_ip_addr(ip_addr) +{ +} + +neighbour::neighbour(const neighbour& bde) + : m_hw(bde.m_hw) + , m_itf(bde.m_itf) + , m_mac(bde.m_mac) + , m_ip_addr(bde.m_ip_addr) +{ +} + +neighbour::~neighbour() +{ + sweep(); + + // not in the DB anymore. + m_db.release(std::make_tuple(m_itf->key(), m_mac, m_ip_addr), this); +} + +void +neighbour::sweep() +{ + if (m_hw) { + HW::enqueue(new delete_cmd(m_hw, m_itf->handle(), m_mac, m_ip_addr)); + } + HW::write(); +} + +void +neighbour::replay() +{ + if (m_hw) { + HW::enqueue(new create_cmd(m_hw, m_itf->handle(), m_mac, m_ip_addr)); + } +} + +std::string +neighbour::to_string() const +{ + std::ostringstream s; + s << "arp-entry:[" << m_itf->to_string() << ", " << m_mac.to_string() << ", " + << m_ip_addr.to_string() << "]"; + + return (s.str()); +} + +void +neighbour::update(const neighbour& r) +{ + /* + * create the table if it is not yet created + */ + if (rc_t::OK != m_hw.rc()) { + HW::enqueue(new create_cmd(m_hw, m_itf->handle(), m_mac, m_ip_addr)); + } +} + +std::shared_ptr +neighbour::find_or_add(const neighbour& temp) +{ + return (m_db.find_or_add( + std::make_tuple(temp.m_itf->key(), temp.m_mac, temp.m_ip_addr), temp)); +} + +std::shared_ptr +neighbour::singular() const +{ + return find_or_add(*this); +} + +void +neighbour::dump(std::ostream& os) +{ + m_db.dump(os); +} + +std::ostream& +operator<<(std::ostream& os, const neighbour::key_t& key) +{ + os << "[" << std::get<0>(key) << ", " << std::get<1>(key) << ", " + << std::get<2>(key) << "]"; + + return (os); +} + +neighbour::event_handler::event_handler() +{ + OM::register_listener(this); + inspect::register_handler({ "neighbour" }, "Neighbours", this); +} + +void +neighbour::event_handler::handle_replay() +{ + m_db.replay(); +} + +void +neighbour::populate_i(const client_db::key_t& key, + std::shared_ptr itf, + const l3_proto_t& proto) +{ + /* + * dump VPP current states + */ + std::shared_ptr cmd = + std::make_shared( + neighbour::dump_cmd(itf->handle(), proto)); + + HW::enqueue(cmd); + HW::write(); + + for (auto& record : *cmd) { + /* + * construct a neighbour from each recieved record. + */ + auto& payload = record.get_payload(); + + mac_address_t mac(payload.mac_address); + boost::asio::ip::address ip_addr = + from_bytes(payload.is_ipv6, payload.ip_address); + neighbour n(*itf, mac, ip_addr); + + VOM_LOG(log_level_t::DEBUG) << "neighbour-dump: " << itf->to_string() + << mac.to_string() << ip_addr.to_string(); + + /* + * Write each of the discovered interfaces into the OM, + * but disable the HW Command q whilst we do, so that no + * commands are sent to VPP + */ + OM::commit(key, n); + } +} + +void +neighbour::event_handler::handle_populate(const client_db::key_t& key) +{ + auto it = interface::cbegin(); + + while (it != interface::cend()) { + neighbour::populate_i(key, it->second.lock(), l3_proto_t::IPV4); + neighbour::populate_i(key, it->second.lock(), l3_proto_t::IPV6); + + ++it; + } +} + +dependency_t +neighbour::event_handler::order() const +{ + return (dependency_t::ENTRY); +} + +void +neighbour::event_handler::show(std::ostream& os) +{ + m_db.dump(os); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/neighbour.hpp b/src/vpp-api/vom/neighbour.hpp new file mode 100644 index 00000000000..141b98b672f --- /dev/null +++ b/src/vpp-api/vom/neighbour.hpp @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_NEIGHBOUR_H__ +#define __VOM_NEIGHBOUR_H__ + +#include "vom/dump_cmd.hpp" +#include "vom/interface.hpp" +#include "vom/singular_db.hpp" +#include "vom/types.hpp" + +#include + +namespace VOM { +/** + * A entry in the ARP termination table of a Bridge Domain + */ +class neighbour : public object_base +{ +public: + /** + * The key for a bridge_domain ARP entry; + * the BD, IP address and MAC address + */ + typedef std::tuple + key_t; + + /** + * Construct an ARP entry + */ + neighbour(const interface& itf, + const mac_address_t& mac, + const boost::asio::ip::address& ip_addr); + + /** + * Copy Construct + */ + neighbour(const neighbour& r); + + /** + * Destructor + */ + ~neighbour(); + + /** + * Return the matching 'singular instance' + */ + std::shared_ptr singular() const; + + /** + * Find the instnace of the bridge_domain domain in the OM + */ + static std::shared_ptr find(const neighbour& temp); + + /** + * Dump all bridge_domain-doamin into the stream provided + */ + static void dump(std::ostream& os); + + /** + * replay the object to create it in hardware + */ + void replay(void); + + /** + * Convert to string for debugging + */ + std::string to_string() const; + + /** + * A command class that creates or updates the bridge domain ARP Entry + */ + class create_cmd + : public rpc_cmd, rc_t, vapi::Ip_neighbor_add_del> + { + public: + /** + * Constructor + */ + create_cmd(HW::item& item, + handle_t itf, + const mac_address_t& mac, + const boost::asio::ip::address& ip_addr); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const create_cmd& i) const; + + private: + handle_t m_itf; + mac_address_t m_mac; + boost::asio::ip::address m_ip_addr; + }; + + /** + * A cmd class that deletes a bridge domain ARP entry + */ + class delete_cmd + : public rpc_cmd, rc_t, vapi::Ip_neighbor_add_del> + { + public: + /** + * Constructor + */ + delete_cmd(HW::item& item, + handle_t itf, + const mac_address_t& mac, + const boost::asio::ip::address& ip_addr); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const delete_cmd& i) const; + + private: + handle_t m_itf; + mac_address_t m_mac; + boost::asio::ip::address m_ip_addr; + }; + + /** + * A cmd class that Dumps all the neighbours + */ + class dump_cmd : public VOM::dump_cmd + { + public: + /** + * Constructor + */ + dump_cmd(const handle_t& itf, const l3_proto_t& proto); + dump_cmd(const dump_cmd& d); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const dump_cmd& i) const; + + private: + /** + * HW reutrn code + */ + HW::item item; + + /** + * The interface to dump + */ + handle_t m_itf; + + /** + * V4 or V6 + */ + l3_proto_t m_proto; + }; + +private: + /** + * Class definition for listeners to OM events + */ + class event_handler : public OM::listener, public inspect::command_handler + { + public: + event_handler(); + virtual ~event_handler() = default; + + /** + * Handle a populate event + */ + void handle_populate(const client_db::key_t& key); + + /** + * Handle a replay event + */ + void handle_replay(); + + /** + * Show the object in the Singular DB + */ + void show(std::ostream& os); + + /** + * Get the sortable Id of the listener + */ + dependency_t order() const; + }; + + /** + * event_handler to register with OM + */ + static event_handler m_evh; + + /** + * Commit the acculmulated changes into VPP. i.e. to a 'HW" write. + */ + void update(const neighbour& obj); + + /** + * Do the populate work + */ + static void populate_i(const client_db::key_t& key, + std::shared_ptr itf, + const l3_proto_t& proto); + + /** + * Find or add the instnace of the bridge_domain domain in the OM + */ + static std::shared_ptr find_or_add(const neighbour& temp); + + /* + * It's the VPPHW class that updates the objects in HW + */ + friend class OM; + + /** + * It's the singular_db class that calls replay() + */ + friend class singular_db; + + /** + * Sweep/reap the object if still stale + */ + void sweep(void); + + /** + * HW configuration for the result of creating the bridge_domain + */ + HW::item m_hw; + + /** + * The bridge_domain domain the bridge_domain is in. + */ + std::shared_ptr m_itf; + + /** + * The mac to match + */ + mac_address_t m_mac; + + /** + * The IP address + */ + boost::asio::ip::address m_ip_addr; + + /** + * A map of all bridge_domains + */ + static singular_db m_db; +}; + +std::ostream& operator<<(std::ostream& os, const neighbour::key_t& key); +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/neighbour_cmds.cpp b/src/vpp-api/vom/neighbour_cmds.cpp new file mode 100644 index 00000000000..2062bc3ee0d --- /dev/null +++ b/src/vpp-api/vom/neighbour_cmds.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/neighbour.hpp" + +namespace VOM { +neighbour::create_cmd::create_cmd(HW::item& item, + handle_t itf, + const mac_address_t& mac, + const boost::asio::ip::address& ip_addr) + : rpc_cmd(item) + , m_itf(itf) + , m_mac(mac) + , m_ip_addr(ip_addr) +{ +} + +bool +neighbour::create_cmd::operator==(const create_cmd& other) const +{ + return ((m_mac == other.m_mac) && (m_ip_addr == other.m_ip_addr) && + (m_itf == other.m_itf)); +} + +rc_t +neighbour::create_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_itf.value(); + payload.is_add = 1; + payload.is_static = 1; + m_mac.to_bytes(payload.mac_address, 6); + to_bytes(m_ip_addr, &payload.is_ipv6, payload.dst_address); + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return rc_t::OK; +} + +std::string +neighbour::create_cmd::to_string() const +{ + std::ostringstream s; + s << "nieghbour-create: " << m_hw_item.to_string() + << " itf:" << m_itf.to_string() << " mac:" << m_mac.to_string() + << " ip:" << m_ip_addr.to_string(); + + return (s.str()); +} + +neighbour::delete_cmd::delete_cmd(HW::item& item, + handle_t itf, + const mac_address_t& mac, + const boost::asio::ip::address& ip_addr) + : rpc_cmd(item) + , m_itf(itf) + , m_mac(mac) + , m_ip_addr(ip_addr) +{ +} + +bool +neighbour::delete_cmd::operator==(const delete_cmd& other) const +{ + return ((m_mac == other.m_mac) && (m_ip_addr == other.m_ip_addr) && + (m_itf == other.m_itf)); +} + +rc_t +neighbour::delete_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_itf.value(); + payload.is_add = 0; + payload.is_static = 1; + m_mac.to_bytes(payload.mac_address, 6); + to_bytes(m_ip_addr, &payload.is_ipv6, payload.dst_address); + + VAPI_CALL(req.execute()); + + wait(); + m_hw_item.set(rc_t::NOOP); + + return rc_t::OK; +} + +std::string +neighbour::delete_cmd::to_string() const +{ + std::ostringstream s; + s << "neighbour-delete: " << m_hw_item.to_string() + << " itf:" << m_itf.to_string() << " mac:" << m_mac.to_string() + << " ip:" << m_ip_addr.to_string(); + + return (s.str()); +} + +neighbour::dump_cmd::dump_cmd(const handle_t& hdl, const l3_proto_t& proto) + : m_itf(hdl) + , m_proto(proto) +{ +} + +neighbour::dump_cmd::dump_cmd(const dump_cmd& d) + : m_itf(d.m_itf) + , m_proto(d.m_proto) +{ +} + +bool +neighbour::dump_cmd::operator==(const dump_cmd& other) const +{ + return (true); +} + +rc_t +neighbour::dump_cmd::issue(connection& con) +{ + m_dump.reset(new msg_t(con.ctx(), std::ref(*this))); + + auto& payload = m_dump->get_request().get_payload(); + payload.sw_if_index = m_itf.value(); + payload.is_ipv6 = m_proto.is_ipv6(); + + VAPI_CALL(m_dump->execute()); + + wait(); + + return rc_t::OK; +} + +std::string +neighbour::dump_cmd::to_string() const +{ + return ("neighbour-dump"); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/object_base.cpp b/src/vpp-api/vom/object_base.cpp new file mode 100644 index 00000000000..6ab4ee5cadc --- /dev/null +++ b/src/vpp-api/vom/object_base.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/object_base.hpp" + +namespace VOM { +object_ref::object_ref(std::shared_ptr obj) + : m_obj(obj) + , m_state(OBJECT_STATE_NONE) +{ +} + +bool +object_ref::operator<(const object_ref& other) const +{ + return (m_obj.get() < other.m_obj.get()); +} + +std::shared_ptr +object_ref::obj() const +{ + return (m_obj); +} + +void +object_ref::mark() const +{ + m_state = OBJECT_STATE_STALE; +} + +void +object_ref::clear() const +{ + m_state = OBJECT_STATE_NONE; +} + +bool +object_ref::stale() const +{ + return (m_state == OBJECT_STATE_STALE); +} + +std::ostream& +operator<<(std::ostream& os, const object_base& o) +{ + os << o.to_string(); + + return (os); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/object_base.hpp b/src/vpp-api/vom/object_base.hpp new file mode 100644 index 00000000000..2edafc58466 --- /dev/null +++ b/src/vpp-api/vom/object_base.hpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_OBJECT_H__ +#define __VOM_OBJECT_H__ + +#include +#include + +#include "vom/types.hpp" + +namespace VOM { +/** + * A base class for all object_base in the VPP object_base-Model. + * provides the abstract interface. + */ +class object_base +{ +public: + /** + * convert to string format for debug purposes + */ + virtual std::string to_string() const = 0; + + /** + * Sweep/reap the object if still stale + */ + virtual void sweep(void) = 0; + + /** + * replay the object to create it in hardware + */ + virtual void replay(void) = 0; + +protected: + /** + * Constructable by derived classes only + */ + object_base() = default; + /** + * Destructor + */ + virtual ~object_base() = default; + +private: + /** + * note we are not maintaining dependencies back to the + * keys. i.e. this object does not know all the keys that + * refer to it. + */ +}; + +/** + * object state + */ +enum obj_state_t +{ + OBJECT_STATE_NONE = 0, + /** + * indicates the object is stale. This flag is set + * when a new epoch is declared. the flag is cleared + * when the object is updated in the new epoch. If the + * flag is still set after convergence is declared then + * the object is deleted + */ + OBJECT_STATE_STALE, +}; + +/** + * A represenation of a reference to a VPP object. + * the reference counting is held through the use of shared pointers. + * We also maintain the state of the object ready for mark n' sweep. + */ +class object_ref +{ +public: + /** + * Constructor + */ + object_ref(std::shared_ptr obj); + + /** + * less than operator + */ + bool operator<(const object_ref& other) const; + + /** + * Return the shared pointer + */ + std::shared_ptr obj() const; + + /** + * Mark the reference object as stale + */ + void mark() const; + + /** + * Clear the stale flag on the object + */ + void clear() const; + + /** + * Query if the object is stale + */ + bool stale() const; + +private: + /** + * The reference object + */ + std::shared_ptr m_obj; + + /** + * Not part of the key (in the set) so we can change it + * whilst iterating + */ + mutable obj_state_t m_state; +}; + +/** + * ostream print of a VPP Obect + */ +std::ostream& operator<<(std::ostream& os, const object_base& o); +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/om.cpp b/src/vpp-api/vom/om.cpp new file mode 100644 index 00000000000..f82fee3e376 --- /dev/null +++ b/src/vpp-api/vom/om.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "vom/om.hpp" + +namespace VOM { +client_db* OM::m_db; + +std::unique_ptr OM::m_listeners; + +/** + * Initialse the connection to VPP + */ +void +OM::init() +{ + m_db = new client_db(); +} + +void +OM::mark(const client_db::key_t& key) +{ + /* + * Find if the object already stored on behalf of this key. + * and mark them stale + */ + object_ref_list& objs = m_db->find(key); + + auto mark_obj = [](const object_ref& oref) { oref.mark(); }; + + std::for_each(objs.begin(), objs.end(), mark_obj); +} + +void +OM::sweep(const client_db::key_t& key) +{ + /* + * Find if the object already stored on behalf of this key. + * and mark them stale + */ + object_ref_list& objs = m_db->find(key); + + for (auto it = objs.begin(); it != objs.end();) { + if (it->stale()) { + it = objs.erase(it); + } else { + ++it; + } + } + + HW::write(); +} + +void +OM::remove(const client_db::key_t& key) +{ + /* + * Simply reset the list for this key. This will desctruct the + * object list and shared_ptrs therein. When the last shared_ptr + * goes the objects desctructor is called and the object is + * removed from OM + */ + m_db->flush(key); + + HW::write(); +} + +void +OM::replay() +{ + /* + * the listeners are sorted in dependency order + */ + for (listener* l : *m_listeners) { + l->handle_replay(); + } + + HW::write(); +} + +void +OM::dump(const client_db::key_t& key, std::ostream& os) +{ + m_db->dump(key, os); +} + +void +OM::dump(std::ostream& os) +{ + m_db->dump(os); +} + +void +OM::populate(const client_db::key_t& key) +{ + /* + * the listeners are sorted in dependency order + */ + for (listener* l : *m_listeners) { + l->handle_populate(key); + } + + /* + * once we have it all, mark it stale. + */ + mark(key); +} + +bool +OM::register_listener(OM::listener* listener) +{ + if (!m_listeners) { + m_listeners.reset(new listener_list); + } + + m_listeners->insert(listener); + + return (true); +} + +OM::mark_n_sweep::mark_n_sweep(const client_db::key_t& key) + : m_key(key) +{ + OM::mark(m_key); +} + +OM::mark_n_sweep::~mark_n_sweep() +{ + OM::sweep(m_key); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/om.hpp b/src/vpp-api/vom/om.hpp new file mode 100644 index 00000000000..f470c5ee185 --- /dev/null +++ b/src/vpp-api/vom/om.hpp @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_OM_H__ +#define __VOM_OM_H__ + +#include +#include + +#include "vom/client_db.hpp" +#include "vom/hw.hpp" + +/** + +The VPP Object Model (VOM) library. + +Before we begin, a glossary of terms: + - Agent or client: A user mode process that links to and uses the VOM library + to programme VPP + - VPP: A running instance of VPP + - High Availability (HA): Scenarios where the client and/or VPP restart with + minimal service interruption. + - CReate, Update, Delete (CRUD): An API style where the producer issues + notifications to changes to objects + +The VOM is a C++ library that models entities in VPP as C++ classes. The + relationships between VOM objects and VPP entities is not always 1:1. Some + effort has been made to construct a higher level, more abstract API to VPP + programming*. +The client programming model is simple (or at least I intended it to be..). The +client deals in ‘desired’ state, that is, it expresses the objects it wants to +exists (in VPP) and the properties that the object should have, i.e**; + Interface af1(“my-af-packet-1”, AFPACKET, admin::UP); +Then the client ‘writes’ this object into the ‘model’ + OM::write(“clients-thing-1”, af1); + +“clients-thing-1” is a description of the entity within the client’s domain that +‘owns’ (or has locked or has a reference to) the VOM object. There can be many +owners of each VOM object. It will be the last owner’s update that will be +programmed in VPP. This model means that the client is not burdened with +maintaining which of its objects have created which VOM objects. If the client +is itself driven by a CRUD API, then create notifications are implemented as + above. Update notifications add two extra statements; + OM::mark(“clients-thing-1”); + … do writes …. + OM::sweep(“clients-thing-1”); +These ‘mark’ and ‘sweep’ statements are indications to OM that firstly, indicate +that all the objects owned by “clients-thing-1” are now stale, i.e that the +client may no longer need them. If one of the subsequent writes should update a +stale object, then it is no longer stale. The sweep statement will ‘remove’ all +the remaining stale objects. In this model, the client does not need to maintain +the mapping of VOM objects to its own objects – it can simply express what it +needs now. +The delete notification is simply: + OM::remove(“clients-thing-1”); +Which will remove all the objects in VOM that are owned by “clients-thing-1”. +Where ‘remove’ in this sense means unlock and unreference, the VOM object, and +VPP state, will only be truly removed once there are no more owners. This is +equivalent to a mark & sweep with no intermediate writes. + +To provide this client side model the VOM is a stateful library, meaning that +for each entity it creates in VPP, VOM maintains its own representation of that +object. VOM can therefore be memory hungry. The desired state is expressed by +the client, the ‘actual’ state is maintained by VOM. VOM will consolidate the +two states when the client writes to the OM and thus issue VPP only the changes +required. + +The concepts of ownership and statefulness also allow the support for HA +scenarios. +VPP restart: When VPP restarts, VOM will reconnect and ‘replay’ its state, in +dependency order, to VPP. The client does not need to regenerate its desired +state. +Client restart: when the client restarts, VOM will read/dump the current state +of all VPP objects and store them in the OM owned by the special owner “boot”. +As the client reprogrammes its desired state, objects will become owned by both +the boot process and the client. At the point in time, as determined by the +client, all stale state, that owned only by boot, can be purged. Hence the +system reaches the correct final state, with no interruption to VPP forwarding. + + +Basic Design: + +Each object in VOM (i.e. an interface, route, bridge-domain, etc) is stored in a +per-type object database, with an object-type specific key. This ‘singular’ DB +has a value-type of a weak pointer to the object. I use the term ‘singular’ to +refer to the instance of the object stored in these databases, to be distinct +from the instances the client constructs to represent desired state. +The ‘client’ DB maintains the mapping of owner to object. The value type of the +client DB is a shared pointer to the singular instance of the owned object. +Once all the owners are gone, and all the shared pointers are destroyed, the +singular instance is also destroyed. + +Each VOM object has some basic behaviour: + update: issue to VPP an update to this object’s state. This could include the + create + sweep: delete the VPP entity – called when the object is destroyed. + replay: issue to VPP all the commands needed to re-programme (VPP restart HA + scenario) + populate: read state from VPP and add it to the OM (client restart HA +scenario) + +The object code is boiler-plate, in some cases (like the ACLs) even template. +The objects are purposefully left as simple, functionality free as possible. + +Communication with VPP is through a ‘queue’ of ‘commands’. A command is +essentially an object wrapper around a VPP binary API call (although we do use +the VAPI C++ bindings too). Commands come in three flavours: + RPC: do this; done. + DUMP: give me all of these things; here you go + EVENT; tell me about these events; here’s one …. Here’s one…. Oh here’s + another….. etc. + +RPC and DUMP commands are handled synchronously. Therefore on return from +OM::write(…) VPP has been issued with the request and responded. EVENTs are +asynchronous and will be delivered to the listeners in a different thread – so +beware!! + +* As such VOM provides some level of insulation to the changes to the VPP + binary API. +** some of the type names are shorten for brevity’s sake. + +*/ +namespace VOM { +/** + * The interface to writing objects into VPP OM. + */ +class OM +{ +public: + /** + * A class providing the RAII pattern for mark and sweep + */ + class mark_n_sweep + { + public: + /** + * Constructor - will call mark on the key + */ + mark_n_sweep(const client_db::key_t& key); + + /** + * Destructor - will call sweep on the key + */ + ~mark_n_sweep(); + + private: + /** + * no copies + */ + mark_n_sweep(const mark_n_sweep& ms) = delete; + + /** + * The client whose state we are guarding. + */ + client_db::key_t m_key; + }; + + /** + * Init + */ + static void init(); + + /** + * populate the OM with state read from HW. + */ + static void populate(const client_db::key_t& key); + + /** + * Mark all state owned by this key as stale + */ + static void mark(const client_db::key_t& key); + + /** + * Sweep all the key's objects that are stale + */ + static void sweep(const client_db::key_t& key); + + /** + * Replay all of the objects to HW. + */ + static void replay(void); + + /** + * Make the State in VPP reflect the expressed desired state. + * But don't call the HW - use this whilst processing dumped + * data from HW + */ + template + static rc_t commit(const client_db::key_t& key, const OBJ& obj) + { + rc_t rc = rc_t::OK; + + HW::disable(); + rc = OM::write(key, obj); + HW::enable(); + + return (rc); + } + + /** + * Make the State in VPP reflect the expressed desired state. + * After processing all the objects in the queue, in FIFO order, + * any remaining state owned by the client_db::key_t is purged. + * This is a template function so the object's update() function is + * always called with the derived type. + */ + template + static rc_t write(const client_db::key_t& key, const OBJ& obj) + { + rc_t rc = rc_t::OK; + + /* + * Find the singular instance another owner may have created. + * this always returns something. + */ + std::shared_ptr inst = obj.singular(); + + /* + * Update the existing object with the new desired state + */ + inst->update(obj); + + /* + * Find if the object already stored on behalf of this key. + * and mark them stale + */ + object_ref_list& objs = m_db->find(key); + + /* + * Iterate through this list to find a matchin' object + * to the one requested. + */ + auto match_ptr = [inst](const object_ref& oref) { + return (inst == oref.obj()); + }; + auto it = std::find_if(objs.begin(), objs.end(), match_ptr); + + if (it != objs.end()) { + /* + * yes, this key already owns this object. + */ + it->clear(); + } else { + /* + * Add the singular instance to the owners list + */ + objs.insert(object_ref(inst)); + } + + return (HW::write()); + } + + /** + * Remove all object in the OM referenced by the key + */ + static void remove(const client_db::key_t& key); + + /** + * Print each of the object in the DB into the stream provided + */ + static void dump(const client_db::key_t& key, std::ostream& os); + + /** + * Print each of the KEYS + */ + static void dump(std::ostream& os); + + /** + * Class definition for listeners to OM events + */ + class listener + { + public: + listener() = default; + virtual ~listener() = default; + + /** + * Handle a populate event + */ + virtual void handle_populate(const client_db::key_t& key) = 0; + + /** + * Handle a replay event + */ + virtual void handle_replay() = 0; + + /** + * Get the sortable Id of the listener + */ + virtual dependency_t order() const = 0; + + /** + * less than operator for set sorting + */ + bool operator<(const listener& listener) const + { + return (order() < listener.order()); + } + }; + + /** + * Register a listener of events + */ + static bool register_listener(listener* listener); + +private: + /** + * Database of object state created for each key + */ + static client_db* m_db; + + /** + * Comparator to keep the pointers to listeners in sorted order + */ + struct listener_comparator_t + { + bool operator()(const listener* l1, const listener* l2) const + { + return (l1->order() < l2->order()); + } + }; + + /** + * convenient typedef for the sorted set of listeners + */ + typedef std::multiset listener_list; + + /** + * The listeners for events + */ + static std::unique_ptr m_listeners; +}; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/prefix.cpp b/src/vpp-api/vom/prefix.cpp new file mode 100644 index 00000000000..24fb57b34d2 --- /dev/null +++ b/src/vpp-api/vom/prefix.cpp @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "vom/prefix.hpp" + +namespace VOM { +/* + * Keep this in sync with VPP's fib_protocol_t + */ +const l3_proto_t l3_proto_t::IPV4(0, "ipv4"); +const l3_proto_t l3_proto_t::IPV6(1, "ipv6"); +const l3_proto_t l3_proto_t::MPLS(2, "mpls"); + +l3_proto_t::l3_proto_t(int v, const std::string& s) + : enum_base(v, s) +{ +} + +bool +l3_proto_t::is_ipv6() +{ + return (*this == IPV6); +} + +bool +l3_proto_t::is_ipv4() +{ + return (*this == IPV4); +} + +const l3_proto_t& +l3_proto_t::from_address(const boost::asio::ip::address& addr) +{ + if (addr.is_v6()) { + return IPV6; + } + + return IPV4; +} + +/* + * Keep this in sync with VPP's dpo_proto_t + */ +const nh_proto_t nh_proto_t::IPV4(0, "ipv4"); +const nh_proto_t nh_proto_t::IPV6(1, "ipv6"); +const nh_proto_t nh_proto_t::MPLS(2, "mpls"); +const nh_proto_t nh_proto_t::ETHERNET(3, "ethernet"); + +nh_proto_t::nh_proto_t(int v, const std::string& s) + : enum_base(v, s) +{ +} + +const nh_proto_t& +nh_proto_t::from_address(const boost::asio::ip::address& addr) +{ + if (addr.is_v6()) { + return IPV6; + } + + return IPV4; +} + +/** + * The all Zeros prefix + */ +const route::prefix_t route::prefix_t::ZERO("0.0.0.0", 0); +const route::prefix_t route::prefix_t::ZEROv6("::", 0); + +route::prefix_t::prefix_t(const boost::asio::ip::address& addr, uint8_t len) + : m_addr(addr) + , m_len(len) +{ +} + +route::prefix_t::prefix_t(const boost::asio::ip::address& addr) + : m_addr(addr) + , m_len(VOM::mask_width(addr)) +{ +} + +route::prefix_t::prefix_t(const std::string& s, uint8_t len) + : m_addr(boost::asio::ip::address::from_string(s)) + , m_len(len) +{ +} + +route::prefix_t::prefix_t(const prefix_t& o) + : m_addr(o.m_addr) + , m_len(o.m_len) +{ +} + +route::prefix_t::prefix_t() + : m_addr() + , m_len(0) +{ +} + +route::prefix_t::~prefix_t() +{ +} + +route::prefix_t& +route::prefix_t::operator=(const route::prefix_t& o) +{ + m_addr = o.m_addr; + m_len = o.m_len; + + return (*this); +} + +const boost::asio::ip::address& +route::prefix_t::address() const +{ + return (m_addr); +} + +uint8_t +route::prefix_t::mask_width() const +{ + return (m_len); +} + +bool +route::prefix_t::operator<(const route::prefix_t& o) const +{ + if (m_len == o.m_len) { + return (m_addr < o.m_addr); + } else { + return (m_len < o.m_len); + } +} + +bool +route::prefix_t::operator==(const route::prefix_t& o) const +{ + return (m_len == o.m_len && m_addr == o.m_addr); +} + +bool +route::prefix_t::operator!=(const route::prefix_t& o) const +{ + return (!(*this == o)); +} + +std::string +route::prefix_t::to_string() const +{ + std::ostringstream s; + + s << m_addr.to_string() << "/" << std::to_string(m_len); + + return (s.str()); +} + +boost::asio::ip::address +from_bytes(uint8_t is_ip6, uint8_t* bytes) +{ + boost::asio::ip::address addr; + + if (is_ip6) { + std::array a; + std::copy(bytes, bytes + 16, std::begin(a)); + boost::asio::ip::address_v6 v6(a); + addr = v6; + } else { + std::array a; + std::copy(bytes, bytes + 4, std::begin(a)); + boost::asio::ip::address_v4 v4(a); + addr = v4; + } + + return (addr); +} + +route::prefix_t::prefix_t(uint8_t is_ip6, uint8_t* addr, uint8_t len) + : m_addr(from_bytes(is_ip6, addr)) + , m_len(len) +{ +} +void +to_bytes(const boost::asio::ip::address_v6& addr, uint8_t* array) +{ + memcpy(array, addr.to_bytes().data(), 16); +} + +void +to_bytes(const boost::asio::ip::address_v4& addr, uint8_t* array) +{ + memcpy(array, addr.to_bytes().data(), 4); +} + +void +to_bytes(const boost::asio::ip::address& addr, uint8_t* is_ip6, uint8_t* array) +{ + if (addr.is_v6()) { + *is_ip6 = 1; + to_bytes(addr.to_v6(), array); + } else { + *is_ip6 = 0; + to_bytes(addr.to_v4(), array); + } +} + +uint32_t +mask_width(const boost::asio::ip::address& addr) +{ + if (addr.is_v6()) { + return 128; + } + return 32; +} + +void +route::prefix_t::to_vpp(uint8_t* is_ip6, uint8_t* addr, uint8_t* len) const +{ + *len = m_len; + to_bytes(m_addr, is_ip6, addr); +} + +l3_proto_t +route::prefix_t::l3_proto() const +{ + if (m_addr.is_v6()) { + return (l3_proto_t::IPV6); + } else { + return (l3_proto_t::IPV4); + } + + return (l3_proto_t::IPV4); +} + +std::ostream& +operator<<(std::ostream& os, const route::prefix_t& pfx) +{ + os << pfx.to_string(); + + return (os); +} + +boost::asio::ip::address_v4 +operator|(const boost::asio::ip::address_v4& addr1, + const boost::asio::ip::address_v4& addr2) +{ + uint32_t a; + a = addr1.to_ulong() | addr2.to_ulong(); + boost::asio::ip::address_v4 addr(a); + return (addr); +} + +boost::asio::ip::address_v4 operator&(const boost::asio::ip::address_v4& addr1, + const boost::asio::ip::address_v4& addr2) +{ + uint32_t a; + a = addr1.to_ulong() & addr2.to_ulong(); + boost::asio::ip::address_v4 addr(a); + return (addr); +} + +boost::asio::ip::address_v4 operator~(const boost::asio::ip::address_v4& addr1) +{ + uint32_t a; + a = ~addr1.to_ulong(); + boost::asio::ip::address_v4 addr(a); + return (addr); +} + +boost::asio::ip::address_v4 +route::prefix_t::mask() const +{ + uint32_t a; + + a = ~((1 << mask_width()) - 1); + boost::asio::ip::address_v4 addr(a); + return (addr); +} + +boost::asio::ip::address_v4 +route::prefix_t::low() const +{ + boost::asio::ip::address_v4 low; + low = address().to_v4() & mask(); + return (low); +} + +boost::asio::ip::address_v4 +route::prefix_t::high() const +{ + boost::asio::ip::address_v4 high; + high = address().to_v4() | ~mask(); + return (high); +} +} +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/prefix.hpp b/src/vpp-api/vom/prefix.hpp new file mode 100644 index 00000000000..3277929bcce --- /dev/null +++ b/src/vpp-api/vom/prefix.hpp @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_PREFIX_H__ +#define __VOM_PREFIX_H__ + +#include + +#include "vom/enum_base.hpp" + +namespace VOM { +/** + * Types belonging to Routing + */ + +/** + * An L3 protocol can be used to construct a prefix that is used + * to match packets are part of a route. + */ +class l3_proto_t : public enum_base +{ +public: + const static l3_proto_t IPV4; + const static l3_proto_t IPV6; + const static l3_proto_t MPLS; + + bool is_ipv4(); + bool is_ipv6(); + + static const l3_proto_t& from_address(const boost::asio::ip::address& addr); + +private: + /** + * Private constructor taking the value and the string name + */ + l3_proto_t(int v, const std::string& s); +}; + +/** + * A next-hop protocol describes the protocol of a peer to which packets + * are sent after matching a route. + */ +class nh_proto_t : public enum_base +{ +public: + const static nh_proto_t IPV4; + const static nh_proto_t IPV6; + const static nh_proto_t MPLS; + const static nh_proto_t ETHERNET; + + static const nh_proto_t& from_address(const boost::asio::ip::address& addr); + +private: + /** + * Private constructor taking the value and the string name + */ + nh_proto_t(int v, const std::string& s); +}; + +namespace route { +/** + * type def the table-id + */ +typedef uint32_t table_id_t; + +/** + * The table-id for the default table + */ +const static table_id_t DEFAULT_TABLE = 0; + +/** + * A prefix defintion. Address + length + */ +class prefix_t +{ +public: + /** + * Default Constructor - creates ::/0 + */ + prefix_t(); + /** + * Constructor with address and length + */ + prefix_t(const boost::asio::ip::address& addr, uint8_t len); + /** + * Constructor with just the address, this creates a + * host prefix + */ + prefix_t(const boost::asio::ip::address& addr); + + /** + * Constructor with string and length + */ + prefix_t(const std::string& s, uint8_t len); + /** + * Copy Constructor + */ + prefix_t(const prefix_t&); + /** + * Constructor with VPP API prefix representation + */ + prefix_t(uint8_t is_ip6, uint8_t* addr, uint8_t len); + /** + * Destructor + */ + ~prefix_t(); + + /** + * Get the address + */ + const boost::asio::ip::address& address() const; + + /** + * Get the network mask width + */ + uint8_t mask_width() const; + + /** + * Assignement + */ + prefix_t& operator=(const prefix_t&); + + /** + * Less than operator + */ + bool operator<(const prefix_t& o) const; + + /** + * equals operator + */ + bool operator==(const prefix_t& o) const; + + /** + * not equal opartor + */ + bool operator!=(const prefix_t& o) const; + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * The all Zeros prefix + */ + const static prefix_t ZERO; + + /** + * The all Zeros v6 prefix + */ + const static prefix_t ZEROv6; + + /** + * Convert the prefix into VPP API parameters + */ + void to_vpp(uint8_t* is_ip6, uint8_t* addr, uint8_t* len) const; + + /** + * Return a address representation of the mask, e.g. 255.255.0.0 + */ + boost::asio::ip::address_v4 mask() const; + + /** + * get the lowest address in the prefix + */ + boost::asio::ip::address_v4 low() const; + + /** + * Get the highest address in the prefix + */ + boost::asio::ip::address_v4 high() const; + + /** + * Get the L3 protocol + */ + l3_proto_t l3_proto() const; + +private: + /** + * The address + */ + boost::asio::ip::address m_addr; + + /** + * The prefix length + */ + uint8_t m_len; +}; +}; + +boost::asio::ip::address_v4 operator|(const boost::asio::ip::address_v4& addr1, + const boost::asio::ip::address_v4& addr2); + +boost::asio::ip::address_v4 operator&(const boost::asio::ip::address_v4& addr1, + const boost::asio::ip::address_v4& addr2); + +boost::asio::ip::address_v4 operator~(const boost::asio::ip::address_v4& addr1); + +/** + * Ostream printer for prefix_t + */ +std::ostream& operator<<(std::ostream& os, const route::prefix_t& pfx); + +/** + * Convert a boost address into a VPP bytes string + */ +void to_bytes(const boost::asio::ip::address& addr, + uint8_t* is_ip6, + uint8_t* array); +void to_bytes(const boost::asio::ip::address_v4& addr, uint8_t* array); +void to_bytes(const boost::asio::ip::address_v6& addr, uint8_t* array); + +/** + * Get the prefix mask length of a host route from the boost address + */ +uint32_t mask_width(const boost::asio::ip::address& addr); + +/** + * Convert a VPP byte stinrg into a boost addresss + */ +boost::asio::ip::address from_bytes(uint8_t is_ip6, uint8_t* array); +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/ra_config.cpp b/src/vpp-api/vom/ra_config.cpp new file mode 100644 index 00000000000..de424a4d436 --- /dev/null +++ b/src/vpp-api/vom/ra_config.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "vom/ra_config.hpp" + +namespace VOM { + +/** + * Construct a new object matching the desried state + */ +ra_config::ra_config(uint8_t suppress, + uint8_t send_unicast, + uint8_t default_router, + uint32_t max_interval) + : m_suppress(suppress) + , m_managed(0) + , m_other(0) + , m_ll_option(0) + , m_send_unicast(send_unicast) + , m_cease(0) + , m_default_router(default_router) + , m_max_interval(max_interval) + , m_min_interval((max_interval * 3) / 4) + , m_lifetime(600) + , m_initial_count(3) + , m_initial_interval(16) +{ +} + +void +ra_config::to_vpp(vapi_payload_sw_interface_ip6nd_ra_config& ra_config) const +{ + ra_config.suppress = m_suppress; + ra_config.managed = m_managed; + ra_config.other = m_other; + ra_config.ll_option = m_ll_option; + ra_config.send_unicast = m_send_unicast; + ra_config.cease = m_cease; + ra_config.max_interval = m_max_interval; + ra_config.min_interval = m_min_interval; + ra_config.lifetime = m_lifetime; + ra_config.initial_count = m_initial_count; + ra_config.initial_interval = m_initial_interval; +} + +bool +ra_config::operator==(const ra_config& other) const +{ + return ((m_suppress == other.m_suppress) && + (m_send_unicast == other.m_send_unicast) && + (m_default_router == other.m_default_router) && + (m_max_interval == other.m_max_interval)); +} + +std::string +ra_config::to_string() const +{ + std::ostringstream s; + + s << "ra-config:[" + << " suppress:" << m_suppress << " send-unicast:" << m_send_unicast + << " default-router:" << m_default_router + << " max_interval:" << m_max_interval << "]"; + + return (s.str()); +} +} +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/ra_config.hpp b/src/vpp-api/vom/ra_config.hpp new file mode 100644 index 00000000000..505ccb3c499 --- /dev/null +++ b/src/vpp-api/vom/ra_config.hpp @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_RA_CONFIG_H__ +#define __VOM_RA_CONFIG_H__ + +#include + +namespace VOM { +/** + * A representation of Router Advertisement configuration + */ +class ra_config +{ +public: + /** + * Construct a new object matching the desried state + */ + ra_config(uint8_t suppress, + uint8_t send_unicast, + uint8_t default_router, + uint32_t max_interval); + + /** + * Copy Constructor + */ + ra_config(const ra_config& o) = default; + + /** + * Destructor + */ + ~ra_config() = default; + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const ra_config& ra_config) const; + + /** + * convert the ra config to VPP API + */ + void to_vpp(vapi_payload_sw_interface_ip6nd_ra_config& ra_config) const; + +private: + /** + * Disables sending ICMPv6 router-advertisement messages. + */ + uint8_t m_suppress; + + /** + * Advertises in ICMPv6 router-advertisement messages to use + * stateful address auto-configuration to obtain address information. + */ + uint8_t m_managed; + + /** + * Indicates in ICMPv6 router-advertisement messages that + * hosts use stateful auto configuration to obtain nonaddress + * related information. + */ + uint8_t m_other; + + /** + * Indicates not to include the optional source link-layer + * address in the ICMPv6 router-advertisement messages. + */ + uint8_t m_ll_option; + + /** + * Use the source address of the router-solicitation message if + * availiable. + */ + uint8_t m_send_unicast; + + /** + * Cease sending ICMPv6 router-advertisement messages. + */ + uint8_t m_cease; + + /** + * .... ? + */ + uint8_t m_default_router; + + /** + * Configures the interval between sending ICMPv6 router-advertisement + * messages. The range for max-interval is from 4 to 200 seconds. + */ + uint32_t m_max_interval; + + /** + * min-interval can not be more than 75% of max-interval. + * If not set, min-interval will be set to 75% of max-interval. + * The range for min-interval is from 3 to 150 seconds. + */ + uint32_t m_min_interval; + + /** + * Advertises the lifetime of a default router in ICMPv6 + * router-advertisement messages. The range is from 0 to 9000 seconds. + * '' must be greater than ''. + * The default value is 600 seconds + */ + uint32_t m_lifetime; + + /** + * Number of initial ICMPv6 router-advertisement messages sent. + * Range for count is 1 - 3 and default is 3. + */ + uint32_t m_initial_count; + + /** + * The interval between each initial messages. + * Range for interval is 1 to 16 seconds, and default is 16 seconds. + */ + uint32_t m_initial_interval; +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/ra_prefix.cpp b/src/vpp-api/vom/ra_prefix.cpp new file mode 100644 index 00000000000..1cf09637082 --- /dev/null +++ b/src/vpp-api/vom/ra_prefix.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "vom/ra_prefix.hpp" + +namespace VOM { +ra_prefix::ra_prefix(const route::prefix_t& pfx, + uint8_t use_default, + uint8_t no_advertise, + uint32_t val_lifetime, + uint32_t pref_lifetime) + : m_pfx(pfx) + , m_use_default(use_default) + , m_no_advertise(no_advertise) + , m_off_link(0) + , m_no_autoconfig(0) + , m_no_onlink(0) + , m_val_lifetime(val_lifetime) + , m_pref_lifetime(pref_lifetime) +{ +} + +void +ra_prefix::to_vpp(vapi_payload_sw_interface_ip6nd_ra_prefix& ra_prefix) const +{ + uint8_t is_ipv6 = 0; + + m_pfx.to_vpp(&is_ipv6, ra_prefix.address, &ra_prefix.address_length); + + ra_prefix.use_default = m_use_default; + ra_prefix.no_advertise = m_no_advertise; + ra_prefix.off_link = m_off_link; + ra_prefix.no_autoconfig = m_no_autoconfig; + ra_prefix.no_onlink = m_no_onlink; + ra_prefix.val_lifetime = m_val_lifetime; + ra_prefix.pref_lifetime = m_pref_lifetime; +} + +bool +ra_prefix::operator==(const ra_prefix& other) const +{ + return ((m_pfx == other.m_pfx) && (m_use_default == other.m_use_default) && + (m_no_advertise == other.m_no_advertise) && + (m_val_lifetime == other.m_val_lifetime) && + (m_pref_lifetime == other.m_pref_lifetime)); +} + +std::string +ra_prefix::to_string() const +{ + std::ostringstream s; + + s << "ra-pfx-config:[" + << " pfx:" << m_pfx.to_string() << " use-default:" << m_use_default + << " no-advertise:" << m_no_advertise << " val-lifetime:" << m_val_lifetime + << " pref-lifetime:" << m_pref_lifetime << "]"; + + return (s.str()); +} + +const route::prefix_t& +ra_prefix::prefix() const +{ + return (m_pfx); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/ra_prefix.hpp b/src/vpp-api/vom/ra_prefix.hpp new file mode 100644 index 00000000000..580aae146fd --- /dev/null +++ b/src/vpp-api/vom/ra_prefix.hpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_RA_PREFIX_H__ +#define __VOM_RA_PREFIX_H__ + +#include "vom/prefix.hpp" + +#include + +namespace VOM { +/** + * A representation of RA prefix configuration on given interface + */ +class ra_prefix +{ +public: + /** + * Construct a new object matching the desried state + */ + ra_prefix(const route::prefix_t& pfx, + uint8_t use_default, + uint8_t no_advertise, + uint32_t val_lifetime, + uint32_t pref_lifetime); + + /** + * Copy Constructor + */ + ra_prefix(const ra_prefix& o) = default; + + /** + * Destructor + */ + ~ra_prefix() = default; + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Return the prefix associated with this ra prefix + */ + const route::prefix_t& prefix() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const ra_prefix& ra_prefix) const; + + /** + * Convert the ra prefix configuration to Vpp Api + */ + void to_vpp(vapi_payload_sw_interface_ip6nd_ra_prefix& ra_prefix) const; + +private: + /** + * The prefix to be advertised. + */ + route::prefix_t m_pfx; + + /** + * Revert to default settings. + */ + uint8_t m_use_default; + + /** + * Do not send full router address in prefix advertisement. + * Default is to advertise. + */ + uint8_t m_no_advertise; + + /** + * Prefix is off-link. Default is on-link. + */ + uint8_t m_off_link; + + /** + * Do not use prefix for autoconfiguration. + * Default is autoconfig. + */ + uint8_t m_no_autoconfig; + + /** + * Do not use prefix for onlink determination. + * Default is on-link (this flag is off). + */ + uint8_t m_no_onlink; + + /** + * ' is the length of time in seconds during what + * the prefix is valid for the purpose of on-link determination. + * + * Range is 7203 to 2592000 seconds and default is 2592000 seconds. + * A value of all one bits (0xffffffff) represents infinity (no + * timeout). + */ + uint32_t m_val_lifetime; + + /** + * '' is the prefered-lifetime and is the length of + * time in seconds during what addresses generated from the prefix + * remain preferred. + * + * Range is 0 to 604800 seconds and default is 604800 seconds. + * A value of all one bits (0xffffffff) represents infinity (no + * timeout). + */ + uint32_t m_pref_lifetime; +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/route.cpp b/src/vpp-api/vom/route.cpp new file mode 100644 index 00000000000..e239f8c4f62 --- /dev/null +++ b/src/vpp-api/vom/route.cpp @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/route.hpp" +#include "vom/singular_db.hpp" + +#include + +namespace VOM { +namespace route { +singular_db ip_route::m_db; + +const path::special_t path::special_t::STANDARD(0, "standard"); +const path::special_t path::special_t::LOCAL(0, "local"); +const path::special_t path::special_t::DROP(0, "standard"); +const path::special_t path::special_t::UNREACH(0, "unreachable"); +const path::special_t path::special_t::PROHIBIT(0, "prohibit"); + +path::special_t::special_t(int v, const std::string& s) + : enum_base(v, s) +{ +} + +path::path(special_t special) + : m_type(special) + , m_nh_proto(nh_proto_t::IPV4) + , m_nh() + , m_rd(nullptr) + , m_interface(nullptr) + , m_weight(1) + , m_preference(0) +{ +} + +path::path(const boost::asio::ip::address& nh, + const interface& interface, + uint8_t weight, + uint8_t preference) + : m_type(special_t::STANDARD) + , m_nh_proto(nh_proto_t::from_address(nh)) + , m_nh(nh) + , m_rd(nullptr) + , m_interface(interface.singular()) + , m_weight(weight) + , m_preference(preference) +{ +} + +path::path(const route_domain& rd, + const boost::asio::ip::address& nh, + uint8_t weight, + uint8_t preference) + : m_type(special_t::STANDARD) + , m_nh_proto(nh_proto_t::from_address(nh)) + , m_nh(nh) + , m_rd(rd.singular()) + , m_interface(nullptr) + , m_weight(weight) + , m_preference(preference) +{ +} + +path::path(const interface& interface, + const nh_proto_t& proto, + uint8_t weight, + uint8_t preference) + : m_type(special_t::STANDARD) + , m_nh_proto(proto) + , m_nh() + , m_rd(nullptr) + , m_interface(interface.singular()) + , m_weight(weight) + , m_preference(preference) +{ +} + +path::path(const path& p) + : m_type(p.m_type) + , m_nh_proto(p.m_nh_proto) + , m_nh(p.m_nh) + , m_rd(p.m_rd) + , m_interface(p.m_interface) + , m_weight(p.m_weight) + , m_preference(p.m_preference) +{ +} + +bool +path::operator<(const path& p) const +{ + if (m_type < p.m_type) + return true; + if (m_rd->table_id() < p.m_rd->table_id()) + return true; + if (m_nh < p.m_nh) + return true; + if (m_interface->handle() < p.m_interface->handle()) + return true; + + return (false); +} + +void +path::to_vpp(vapi_payload_ip_add_del_route& payload) const +{ + payload.is_drop = 0; + payload.is_unreach = 0; + payload.is_prohibit = 0; + payload.is_local = 0; + payload.is_classify = 0; + payload.is_multipath = 0; + payload.is_resolve_host = 0; + payload.is_resolve_attached = 0; + + if (nh_proto_t::ETHERNET == m_nh_proto) { + payload.is_l2_bridged = 1; + } + + if (special_t::STANDARD == m_type) { + uint8_t path_v6; + to_bytes(m_nh, &path_v6, payload.next_hop_address); + + if (m_rd) { + payload.next_hop_table_id = m_rd->table_id(); + } + if (m_interface) { + payload.next_hop_sw_if_index = m_interface->handle().value(); + } + } else if (special_t::DROP == m_type) { + payload.is_drop = 1; + } else if (special_t::UNREACH == m_type) { + payload.is_unreach = 1; + } else if (special_t::PROHIBIT == m_type) { + payload.is_prohibit = 1; + } else if (special_t::LOCAL == m_type) { + payload.is_local = 1; + } + payload.next_hop_weight = m_weight; + payload.next_hop_preference = m_preference; + payload.next_hop_via_label = 0; + payload.classify_table_index = 0; +} + +std::string +path::to_string() const +{ + std::ostringstream s; + + s << "path:[" + << "type:" << m_type.to_string() << " proto:" << m_nh_proto.to_string() + << " neighbour:" << m_nh.to_string(); + if (m_rd) { + s << " " << m_rd->to_string(); + } + if (m_interface) { + s << " " << m_interface->to_string(); + } + s << " weight:" << static_cast(m_weight) + << " preference:" << static_cast(m_preference) << "]"; + + return (s.str()); +} + +ip_route::ip_route(const prefix_t& prefix) + : m_hw(false) + , m_rd(route_domain::get_default()) + , m_prefix(prefix) + , m_paths() +{ +} + +ip_route::ip_route(const ip_route& r) + : m_hw(r.m_hw) + , m_rd(r.m_rd) + , m_prefix(r.m_prefix) + , m_paths(r.m_paths) +{ +} + +ip_route::ip_route(const route_domain& rd, const prefix_t& prefix) + : m_hw(false) + , m_rd(rd.singular()) + , m_prefix(prefix) + , m_paths() +{ +} + +ip_route::~ip_route() +{ + sweep(); + + // not in the DB anymore. + m_db.release(std::make_pair(m_rd->table_id(), m_prefix), this); +} + +void +ip_route::add(const path& path) +{ + m_paths.insert(path); +} + +void +ip_route::remove(const path& path) +{ + m_paths.erase(path); +} + +void +ip_route::sweep() +{ + if (m_hw) { + HW::enqueue(new delete_cmd(m_hw, m_rd->table_id(), m_prefix)); + } + HW::write(); +} + +void +ip_route::replay() +{ + if (m_hw) { + HW::enqueue(new update_cmd(m_hw, m_rd->table_id(), m_prefix, m_paths)); + } +} +std::string +ip_route::to_string() const +{ + std::ostringstream s; + s << "route:[" << m_rd->to_string() << ", " << m_prefix.to_string() << " [" + << m_paths << "]" + << "]"; + + return (s.str()); +} + +void +ip_route::update(const ip_route& r) +{ + /* +* create the table if it is not yet created +*/ + if (rc_t::OK != m_hw.rc()) { + HW::enqueue(new update_cmd(m_hw, m_rd->table_id(), m_prefix, m_paths)); + } +} + +std::shared_ptr +ip_route::find_or_add(const ip_route& temp) +{ + return (m_db.find_or_add(std::make_pair(temp.m_rd->table_id(), temp.m_prefix), + temp)); +} + +std::shared_ptr +ip_route::singular() const +{ + return find_or_add(*this); +} + +void +ip_route::dump(std::ostream& os) +{ + m_db.dump(os); +} + +ip_route::event_handler::event_handler() +{ + OM::register_listener(this); + inspect::register_handler({ "ip-route" }, "ip route configurations", this); +} + +void +ip_route::event_handler::handle_replay() +{ + m_db.replay(); +} + +void +ip_route::event_handler::handle_populate(const client_db::key_t& key) +{ + std::shared_ptr cmd_v4(new ip_route::dump_v4_cmd()); + std::shared_ptr cmd_v6(new ip_route::dump_v6_cmd()); + + HW::enqueue(cmd_v4); + HW::enqueue(cmd_v6); + HW::write(); + + for (auto& record : *cmd_v4) { + auto& payload = record.get_payload(); + + prefix_t pfx(0, payload.address, payload.address_length); + + /** +* populating the route domain here +*/ + route_domain rd_temp(payload.table_id); + std::shared_ptr rd = route_domain::find(rd_temp); + if (!rd) { + OM::commit(key, rd_temp); + } + ip_route ip_r(rd_temp, pfx); + + for (unsigned int i = 0; i < payload.count; i++) { + vapi_type_fib_path p = payload.path[i]; + if (p.is_local) { + path path_v4(path::special_t::LOCAL); + ip_r.add(path_v4); + } else if (p.is_drop) { + path path_v4(path::special_t::DROP); + ip_r.add(path_v4); + } else if (p.is_unreach) { + path path_v4(path::special_t::UNREACH); + ip_r.add(path_v4); + } else if (p.is_prohibit) { + path path_v4(path::special_t::PROHIBIT); + ip_r.add(path_v4); + } else { + std::shared_ptr itf = interface::find(p.sw_if_index); + boost::asio::ip::address address = from_bytes(0, p.next_hop); + path path_v4(address, *itf, p.weight, p.preference); + ip_r.add(path_v4); + } + } + VOM_LOG(log_level_t::DEBUG) << "ip-route-dump: " << ip_r.to_string(); + + /* +* Write each of the discovered interfaces into the OM, +* but disable the HW Command q whilst we do, so that no +* commands are sent to VPP +*/ + OM::commit(key, ip_r); + } + + for (auto& record : *cmd_v6) { + auto& payload = record.get_payload(); + + prefix_t pfx(1, payload.address, payload.address_length); + route_domain rd_temp(payload.table_id); + std::shared_ptr rd = route_domain::find(rd_temp); + if (!rd) { + OM::commit(key, rd_temp); + } + ip_route ip_r(rd_temp, pfx); + + for (unsigned int i = 0; i < payload.count; i++) { + vapi_type_fib_path p = payload.path[i]; + if (p.is_local) { + path path_v6(path::special_t::LOCAL); + ip_r.add(path_v6); + } else if (p.is_drop) { + path path_v6(path::special_t::DROP); + ip_r.add(path_v6); + } else if (p.is_unreach) { + path path_v6(path::special_t::UNREACH); + ip_r.add(path_v6); + } else if (p.is_prohibit) { + path path_v6(path::special_t::PROHIBIT); + ip_r.add(path_v6); + } else { + std::shared_ptr itf = interface::find(p.sw_if_index); + boost::asio::ip::address address = from_bytes(1, p.next_hop); + path path_v6(address, *itf, p.weight, p.preference); + ip_r.add(path_v6); + } + } + VOM_LOG(log_level_t::DEBUG) << "ip-route-dump: " << ip_r.to_string(); + + /* +* Write each of the discovered interfaces into the OM, +* but disable the HW Command q whilst we do, so that no +* commands are sent to VPP +*/ + OM::commit(key, ip_r); + } +} + +dependency_t +ip_route::event_handler::order() const +{ + return (dependency_t::BINDING); +} + +void +ip_route::event_handler::show(std::ostream& os) +{ + m_db.dump(os); +} + +std::ostream& +operator<<(std::ostream& os, const ip_route::key_t& key) +{ + os << "[" << key.first << ", " << key.second.to_string() << "]"; + + return (os); +} + +std::ostream& +operator<<(std::ostream& os, const path_list_t& key) +{ + os << "["; + for (auto k : key) { + os << k.to_string() << " "; + } + os << "]"; + + return (os); +} +} +} +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/route.hpp b/src/vpp-api/vom/route.hpp new file mode 100644 index 00000000000..d175bee80eb --- /dev/null +++ b/src/vpp-api/vom/route.hpp @@ -0,0 +1,473 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_ROUTE_H__ +#define __VOM_ROUTE_H__ + +#include "vom/interface.hpp" +#include "vom/prefix.hpp" +#include "vom/route_domain.hpp" +#include "vom/singular_db.hpp" + +#include + +namespace VOM { +/** + * Types belonging to Routing + */ +namespace route { +/** + * A path for IP or MPLS routes + */ +class path +{ +public: + /** + * Special path types + */ + class special_t : public enum_base + { + public: + /** + * A standard path type. this includes path types + * that use the next-hop and interface + */ + const static special_t STANDARD; + + /** + * A local/for-us/recieve + */ + const static special_t LOCAL; + + /** + * drop path + */ + const static special_t DROP; + + /** + * a path will return ICMP unreachables + */ + const static special_t UNREACH; + + /** + * a path will return ICMP prohibit + */ + const static special_t PROHIBIT; + + private: + /** + * Private constructor taking the value and the string name + */ + special_t(int v, const std::string& s); + }; + + /** + * constructor for special paths + */ + path(special_t special); + + /** + * Constructor for standard non-recursive paths + */ + path(const boost::asio::ip::address& nh, + const interface& interface, + uint8_t weight = 1, + uint8_t preference = 0); + + /** + * Constructor for standard recursive paths + */ + path(const route_domain& rd, + const boost::asio::ip::address& nh, + uint8_t weight = 1, + uint8_t preference = 0); + + /** + * Constructor for DVR paths or attached paths. + */ + path(const interface& interface, + const nh_proto_t& proto, + uint8_t weight = 1, + uint8_t preference = 0); + + /** + * Copy Constructor + */ + path(const path& p); + + /** + * Convert the path into the VPP API representation + */ + void to_vpp(vapi_type_fib_path& path) const; + void to_vpp(vapi_payload_ip_add_del_route& payload) const; + + /** + * Less than operator for set insertion + */ + bool operator<(const path& p) const; + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + +private: + /** + * The special path tpye + */ + special_t m_type; + + /** + * The next-hop protocol + */ + nh_proto_t m_nh_proto; + + /** + * The next-hop + */ + boost::asio::ip::address m_nh; + + /** + * For recursive routes, this is the table in which the + * the next-hop exists. + */ + std::shared_ptr m_rd; + + /** + * The next-hop interface [if present]. + */ + std::shared_ptr m_interface; + + /** + * UCMP weight + */ + uint8_t m_weight; + + /** + * Path preference + */ + uint8_t m_preference; +}; + +/** + * A path-list is a set of paths + */ +typedef std::set path_list_t; + +/** + * ostream output for iterator + */ +std::ostream& operator<<(std::ostream& os, const path_list_t& path_list); + +/** + * A IP route + */ +class ip_route : public object_base +{ +public: + /** + * The key for a route + */ + typedef std::pair key_t; + + /** + * Construct a route in the default table + */ + ip_route(const prefix_t& prefix); + + /** + * Copy Construct + */ + ip_route(const ip_route& r); + + /** + * Construct a route in the given route domain + */ + ip_route(const route_domain& rd, const prefix_t& prefix); + + /** + * Destructor + */ + ~ip_route(); + + /** + * Return the matching 'singular instance' + */ + std::shared_ptr singular() const; + + /** + * Add a path. + */ + void add(const path& path); + + /** + * remove a path. + */ + void remove(const path& path); + + /** + * Find the instnace of the route domain in the OM + */ + static std::shared_ptr find(const ip_route& temp); + + /** + * Dump all route-doamin into the stream provided + */ + static void dump(std::ostream& os); + + /** + * replay the object to create it in hardware + */ + void replay(void); + + /** + * Convert to string for debugging + */ + std::string to_string() const; + + /** + * A command class that creates or updates the route + */ + class update_cmd + : public rpc_cmd, rc_t, vapi::Ip_add_del_route> + { + public: + /** + * Constructor + */ + update_cmd(HW::item& item, + table_id_t id, + const prefix_t& prefix, + const path_list_t& paths); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const update_cmd& i) const; + + private: + route::table_id_t m_id; + prefix_t m_prefix; + const path_list_t m_paths; + }; + + /** + * A cmd class that deletes a route + */ + class delete_cmd + : public rpc_cmd, rc_t, vapi::Ip_add_del_route> + { + public: + /** + * Constructor + */ + delete_cmd(HW::item& item, table_id_t id, const prefix_t& prefix); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const delete_cmd& i) const; + + private: + route::table_id_t m_id; + prefix_t m_prefix; + }; + + /** + * A cmd class that Dumps ipv4 fib + */ + class dump_v4_cmd : public VOM::dump_cmd + { + public: + /** + * Constructor + */ + dump_v4_cmd(); + dump_v4_cmd(const dump_cmd& d); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const dump_v4_cmd& i) const; + + private: + /** + * HW reutrn code + */ + HW::item item; + }; + + /** + * A cmd class that Dumps ipv6 fib + */ + class dump_v6_cmd : public VOM::dump_cmd + { + public: + /** + * Constructor + */ + dump_v6_cmd(); + dump_v6_cmd(const dump_cmd& d); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const dump_v6_cmd& i) const; + + private: + /** + * HW reutrn code + */ + HW::item item; + }; + +private: + /** + * Class definition for listeners to OM events + */ + class event_handler : public OM::listener, public inspect::command_handler + { + public: + event_handler(); + virtual ~event_handler() = default; + + /** + * Handle a populate event + */ + void handle_populate(const client_db::key_t& key); + + /** + * Handle a replay event + */ + void handle_replay(); + + /** + * Show the object in the Singular DB + */ + void show(std::ostream& os); + + /** + * Get the sortable Id of the listener + */ + dependency_t order() const; + }; + + /** + * event_handler to register with OM + */ + static event_handler m_evh; + + /** + * Find or add the instnace of the route domain in the OM + */ + static std::shared_ptr find_or_add(const ip_route& temp); + + /* + * It's the OM class that updates the objects in HW + */ + friend class VOM::OM; + + /** + * It's the singular_db class that calls replay() + */ + friend class singular_db; + + /** + * Commit the acculmulated changes into VPP. i.e. to a 'HW" write. + */ + void update(const ip_route& obj); + + /** + * Sweep/reap the object if still stale + */ + void sweep(void); + + /** + * HW configuration for the result of creating the route + */ + HW::item m_hw; + + /** + * The route domain the route is in. + */ + std::shared_ptr m_rd; + + /** + * The prefix to match + */ + prefix_t m_prefix; + + /** + * The set of paths + */ + path_list_t m_paths; + + /** + * A map of all routes + */ + static singular_db m_db; +}; + +std::ostream& operator<<(std::ostream& os, const ip_route::key_t& key); +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/route_cmds.cpp b/src/vpp-api/vom/route_cmds.cpp new file mode 100644 index 00000000000..66dd2865e1a --- /dev/null +++ b/src/vpp-api/vom/route_cmds.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "vom/route.hpp" + +namespace VOM { +namespace route { +ip_route::update_cmd::update_cmd(HW::item& item, + table_id_t id, + const prefix_t& prefix, + const path_list_t& paths) + : rpc_cmd(item) + , m_id(id) + , m_prefix(prefix) + , m_paths(paths) +{ + // no multipath yet. + assert(paths.size() == 1); +} + +bool +ip_route::update_cmd::operator==(const update_cmd& other) const +{ + return ((m_prefix == other.m_prefix) && (m_id == other.m_id)); +} + +rc_t +ip_route::update_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), 0, std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + + payload.table_id = m_id; + payload.is_add = 1; + payload.is_multipath = 0; + + m_prefix.to_vpp(&payload.is_ipv6, payload.dst_address, + &payload.dst_address_length); + + for (auto& p : m_paths) + p.to_vpp(payload); + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return rc_t::OK; +} + +std::string +ip_route::update_cmd::to_string() const +{ + std::ostringstream s; + s << "ip-route-create: " << m_hw_item.to_string() << " table-id:" << m_id + << " prefix:" << m_prefix.to_string() << " paths:" << m_paths; + + return (s.str()); +} + +ip_route::delete_cmd::delete_cmd(HW::item& item, + table_id_t id, + const prefix_t& prefix) + : rpc_cmd(item) + , m_id(id) + , m_prefix(prefix) +{ +} + +bool +ip_route::delete_cmd::operator==(const delete_cmd& other) const +{ + return ((m_prefix == other.m_prefix) && (m_id == other.m_id)); +} + +rc_t +ip_route::delete_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), 0, std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.table_id = m_id; + payload.is_add = 0; + + m_prefix.to_vpp(&payload.is_ipv6, payload.dst_address, + &payload.dst_address_length); + + VAPI_CALL(req.execute()); + + wait(); + m_hw_item.set(rc_t::NOOP); + + return rc_t::OK; +} + +std::string +ip_route::delete_cmd::to_string() const +{ + std::ostringstream s; + s << "ip-route-delete: " << m_hw_item.to_string() << " id:" << m_id + << " prefix:" << m_prefix.to_string(); + + return (s.str()); +} + +ip_route::dump_v4_cmd::dump_v4_cmd() +{ +} + +bool +ip_route::dump_v4_cmd::operator==(const dump_v4_cmd& other) const +{ + return (true); +} + +rc_t +ip_route::dump_v4_cmd::issue(connection& con) +{ + m_dump.reset(new msg_t(con.ctx(), std::ref(*this))); + + VAPI_CALL(m_dump->execute()); + + wait(); + + return rc_t::OK; +} + +std::string +ip_route::dump_v4_cmd::to_string() const +{ + return ("ip-route-v4-dump"); +} + +ip_route::dump_v6_cmd::dump_v6_cmd() +{ +} + +bool +ip_route::dump_v6_cmd::operator==(const dump_v6_cmd& other) const +{ + return (true); +} + +rc_t +ip_route::dump_v6_cmd::issue(connection& con) +{ + m_dump.reset(new msg_t(con.ctx(), std::ref(*this))); + + VAPI_CALL(m_dump->execute()); + + wait(); + + return rc_t::OK; +} + +std::string +ip_route::dump_v6_cmd::to_string() const +{ + return ("ip-route-v6-dump"); +} +} +} +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/route_domain.cpp b/src/vpp-api/vom/route_domain.cpp new file mode 100644 index 00000000000..8ae5785e9f6 --- /dev/null +++ b/src/vpp-api/vom/route_domain.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/route_domain.hpp" +#include "vom/cmd.hpp" + +namespace VOM { +/** + * A DB of al the interfaces, key on the name + */ +singular_db route_domain::m_db; + +/** + * Construct a new object matching the desried state + */ +route_domain::route_domain(route::table_id_t id) + : m_hw_v4(true) + , m_hw_v6(true) + , m_table_id(id) +{ +} + +route_domain::route_domain(const route_domain& o) + : m_hw_v4(o.m_hw_v4) + , m_hw_v6(o.m_hw_v6) + , m_table_id(o.m_table_id) +{ +} + +route::table_id_t +route_domain::table_id() const +{ + return (m_table_id); +} + +route_domain::key_t +route_domain::key() const +{ + return (table_id()); +} + +void +route_domain::sweep() +{ + if (m_hw_v4) { + HW::enqueue(new delete_cmd(m_hw_v4, l3_proto_t::IPV4, m_table_id)); + } + if (m_hw_v6) { + HW::enqueue(new delete_cmd(m_hw_v6, l3_proto_t::IPV6, m_table_id)); + } + HW::write(); +} + +void +route_domain::replay() +{ + if (m_hw_v4) { + HW::enqueue(new create_cmd(m_hw_v4, l3_proto_t::IPV4, m_table_id)); + } + if (m_hw_v6) { + HW::enqueue(new create_cmd(m_hw_v6, l3_proto_t::IPV6, m_table_id)); + } +} + +route_domain::~route_domain() +{ + sweep(); + + // not in the DB anymore. + m_db.release(m_table_id, this); +} + +std::string +route_domain::to_string() const +{ + std::ostringstream s; + s << "route-domain:[" + << "table-id:" << m_table_id << " v4:" << m_hw_v4 << " v6:" << m_hw_v6 + << "]"; + + return (s.str()); +} + +std::shared_ptr +route_domain::find(const route_domain& temp) +{ + std::shared_ptr rd; + + auto it = m_db.cbegin(); + + while (it != m_db.cend()) { + /* + * The key in the DB is a pair of the interface's name and prefix. + * If the keys match, save the L3-config + */ + auto key = it->first; + + if (temp.table_id() == key) { + rd = it->second.lock(); + break; + } + + ++it; + } + + return (rd); +} + +void +route_domain::update(const route_domain& desired) +{ + /* + * create the table if it is not yet created + */ + if (rc_t::OK != m_hw_v4.rc()) { + HW::enqueue(new create_cmd(m_hw_v4, l3_proto_t::IPV4, m_table_id)); + } + if (rc_t::OK != m_hw_v6.rc()) { + HW::enqueue(new create_cmd(m_hw_v6, l3_proto_t::IPV6, m_table_id)); + } +} + +std::shared_ptr +route_domain::get_default() +{ + route_domain rd(route::DEFAULT_TABLE); + + return (find_or_add(rd)); +} + +std::shared_ptr +route_domain::find_or_add(const route_domain& temp) +{ + return (m_db.find_or_add(temp.m_table_id, temp)); +} + +std::shared_ptr +route_domain::singular() const +{ + return find_or_add(*this); +} + +void +route_domain::dump(std::ostream& os) +{ + m_db.dump(os); +} +} +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/route_domain.hpp b/src/vpp-api/vom/route_domain.hpp new file mode 100644 index 00000000000..5d3b8914aca --- /dev/null +++ b/src/vpp-api/vom/route_domain.hpp @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_ROUTE_DOMAIN_H__ +#define __VOM_ROUTE_DOMAIN_H__ + +#include "vom/object_base.hpp" +#include "vom/om.hpp" +#include "vom/prefix.hpp" +#include "vom/singular_db.hpp" + +#include + +namespace VOM { +/** + * A route-domain is a VRF. + * creating a route-domain object will construct both an IPv4 + * and IPv6 table. + */ +class route_domain : public object_base +{ +public: + /** + * The Key for a route-domain + */ + typedef route::table_id_t key_t; + + /** + * Construct a new object matching the desried state + */ + route_domain(route::table_id_t id); + + /** + * Copy Constructor + */ + route_domain(const route_domain& o); + + /** + * Destructor + */ + ~route_domain(); + + /** + * Return the matching 'singular instance' + */ + std::shared_ptr singular() const; + + /** + * Debug print function + */ + std::string to_string() const; + + /** + * Get the table ID + */ + route::table_id_t table_id() const; + + /** + * Get the route-domain's key + */ + key_t key() const; + + /** + * Find the instnace of the route domain in the OM + */ + static std::shared_ptr find(const route_domain& temp); + + /** + * Dump all route-doamin into the stream provided + */ + static void dump(std::ostream& os); + + /** + * Return the sigular instance for the default table + */ + static std::shared_ptr get_default(); + + /** + * replay the object to create it in hardware + */ + void replay(void); + + /** + * A command class that creates the IP table + */ + class create_cmd + : public rpc_cmd, rc_t, vapi::Ip_table_add_del> + { + public: + /** + * Constructor + */ + create_cmd(HW::item& item, l3_proto_t proto, route::table_id_t id); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const create_cmd& i) const; + + private: + /** + * table-ID to create + */ + route::table_id_t m_id; + + /** + * L3 protocol of the table + */ + l3_proto_t m_proto; + }; + + /** + * A cmd class that Deletes the IP Table + */ + class delete_cmd + : public rpc_cmd, rc_t, vapi::Ip_table_add_del> + { + public: + /** + * Constructor + */ + delete_cmd(HW::item& item, l3_proto_t proto, route::table_id_t id); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const delete_cmd& i) const; + + private: + /** + * table-ID to create + */ + route::table_id_t m_id; + + /** + * L3 protocol of the table + */ + l3_proto_t m_proto; + }; + +private: + /** + * Commit the acculmulated changes into VPP. i.e. to a 'HW" write. + */ + void update(const route_domain& obj); + + /** + * Find or add the instnace of the route domain in the OM + */ + static std::shared_ptr find_or_add(const route_domain& temp); + + /* + * It's the OM class that updates the objects in HW + */ + friend class OM; + + /** + * It's the singular_db class that calls replay() + */ + friend class singular_db; + + /** + * Sweep/reap the object if still stale + */ + void sweep(void); + + /** + * HW configuration for the result of creating the v4 table + */ + HW::item m_hw_v4; + + /** + * HW configuration for the result of creating the v6 table + */ + HW::item m_hw_v6; + + /** + * VPP understands Table-IDs not table names. + * The table IDs for V4 and V6 are the same. + */ + route::table_id_t m_table_id; + + /** + * A map of all interfaces key against the interface's name + */ + static singular_db m_db; +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/route_domain_cmds.cpp b/src/vpp-api/vom/route_domain_cmds.cpp new file mode 100644 index 00000000000..c3f817f79f1 --- /dev/null +++ b/src/vpp-api/vom/route_domain_cmds.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/route_domain.hpp" + +namespace VOM { + +route_domain::create_cmd::create_cmd(HW::item& item, + l3_proto_t proto, + route::table_id_t id) + : rpc_cmd(item) + , m_id(id) + , m_proto(proto) +{ +} + +bool +route_domain::create_cmd::operator==(const create_cmd& other) const +{ + return (m_id == other.m_id); +} + +rc_t +route_domain::create_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.table_id = m_id; + payload.is_add = 1; + payload.is_ipv6 = m_proto.is_ipv6(); + + VAPI_CALL(req.execute()); + + m_hw_item.set(wait()); + + return (rc_t::OK); +} + +std::string +route_domain::create_cmd::to_string() const +{ + std::ostringstream s; + s << "ip-table-create: " << m_hw_item.to_string() << " id:" << m_id + << " af:" << m_proto.to_string(); + + return (s.str()); +} + +route_domain::delete_cmd::delete_cmd(HW::item& item, + l3_proto_t proto, + route::table_id_t id) + : rpc_cmd(item) + , m_id(id) + , m_proto(proto) +{ +} + +bool +route_domain::delete_cmd::operator==(const delete_cmd& other) const +{ + return (m_id == other.m_id); +} + +rc_t +route_domain::delete_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.table_id = m_id; + payload.is_add = 0; + payload.is_ipv6 = m_proto.is_ipv6(); + + VAPI_CALL(req.execute()); + + wait(); + m_hw_item.set(rc_t::NOOP); + + return (rc_t::OK); +} + +std::string +route_domain::delete_cmd::to_string() const +{ + std::ostringstream s; + s << "ip-table-delete: " << m_hw_item.to_string() << " id:" << m_id + << " af:" << m_proto.to_string(); + + return (s.str()); +} +} +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/rpc_cmd.hpp b/src/vpp-api/vom/rpc_cmd.hpp new file mode 100644 index 00000000000..60dbd47c8b3 --- /dev/null +++ b/src/vpp-api/vom/rpc_cmd.hpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_RPC_CMD_H__ +#define __VOM_RPC_CMD_H__ + +#include + +#include "vom/cmd.hpp" +#include "vom/logger.hpp" + +namespace VOM { +/** + * A base class for all RPC commands to VPP. + * RPC commands are one of the sub-set of command types to VPP + * that modify/create state in VPP and thus return an error code. + * Commands are issued in one thread context, but read in another. The + * command has an associated std::promise that is met by the RX thread. + * this allows the sender, which waits on the promise's future, to + * experience a synchronous command. + * + * The command is templatised on the type of the HW::item to be set by + * the command, and the data returned in the promise, + */ +template +class rpc_cmd : public cmd +{ +public: + /** + * convenient typedef + */ + typedef MSG msg_t; + + /** + * Constructor taking the HW item that will be updated by the command + */ + rpc_cmd(HWITEM& item) + : cmd() + , m_hw_item(item) + , m_promise() + { + } + + /** + * Desructor + */ + virtual ~rpc_cmd() {} + + /** + * return the HW item the command updates + */ + HWITEM& item() { return m_hw_item; } + + /** + * return the const HW item the command updates + */ + const HWITEM& item() const { return m_hw_item; } + + /** + * Fulfill the commands promise. Called from the RX thread + */ + void fulfill(const DATA& d) { m_promise.set_value(d); } + + /** + * Wait on the commands promise. i.e. block on the completion + * of the command. + */ + DATA wait() + { + std::future_status status; + std::future result; + + result = m_promise.get_future(); + status = result.wait_for(std::chrono::seconds(5)); + + if (status != std::future_status::ready) { + return (DATA(rc_t::TIMEOUT)); + } + + return (result.get()); + } + + /** + * Called by the HW Command Q when it is disabled to indicate the + * command can be considered successful without issuing it to HW + */ + virtual void succeeded() { m_hw_item.set(rc_t::OK); } + + /** + * call operator used as a callback by VAPI when the reply is available + */ + virtual vapi_error_e operator()(MSG& reply) + { + int retval = reply.get_response().get_payload().retval; + VOM_LOG(log_level_t::DEBUG) << to_string() << " " << retval; + fulfill(rc_t::from_vpp_retval(retval)); + + return (VAPI_OK); + } + + /** + * Retire/cancel a long running command + */ + virtual void retire(connection& con) {} + +protected: + /** + * A reference to an object's HW::item that the command will update + */ + HWITEM& m_hw_item; + + /** + * The promise that implements the synchronous issue + */ + std::promise m_promise; +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/singular_db.hpp b/src/vpp-api/vom/singular_db.hpp new file mode 100644 index 00000000000..707389b5844 --- /dev/null +++ b/src/vpp-api/vom/singular_db.hpp @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_INST_DB_H__ +#define __VOM_INST_DB_H__ + +#include +#include + +namespace VOM { +/** + * A Database to store the unique 'singular' instances of a single object + * type. + * The instances are stored as weak pointers. So the DB does not own these + * objects, they are owned by object in the client_db. + */ +template +class singular_db +{ +public: + /** + * Constructor + */ + singular_db() {} + + /** + * Iterator + */ + typedef + typename std::map>::const_iterator const_iterator; + + /** + * Get iterator to the beginning of the DB + */ + const_iterator cbegin() { return m_map.cbegin(); } + + /** + * Get iterator to the beginning of the DB + */ + const_iterator cend() { return m_map.cend(); } + + /** + * Find or add the object to the store. + * The object passed is deisred state. A new instance will be copy + * constructed from it. This function is templatised on the object type + * passed, which may be drrived from, the object type stored. this + * prevents slicing during the make_shared construction. + */ + template + std::shared_ptr find_or_add(const KEY& key, const DERIVED& obj) + { + auto search = m_map.find(key); + + if (search == m_map.end()) { + std::shared_ptr sp = std::make_shared(obj); + + m_map[key] = sp; + + VOM_LOG(log_level_t::DEBUG) << *sp; + return (sp); + } + + return (search->second.lock()); + } + + /** + * Find the object to the store. + */ + std::shared_ptr find(const KEY& key) + { + auto search = m_map.find(key); + + if (search == m_map.end()) { + std::shared_ptr sp(NULL); + + return (sp); + } + + return (search->second.lock()); + } + + /** + * Release the object from the DB store, if it's the one we have stored + */ + void release(const KEY& key, const OBJ* obj) + { + auto search = m_map.find(key); + + if (search != m_map.end()) { + if (search->second.expired()) { + m_map.erase(key); + } else { + std::shared_ptr sp = m_map[key].lock(); + + if (sp.get() == obj) { + m_map.erase(key); + } + } + } + } + + /** + * Find the object to the store. + */ + void add(const KEY& key, std::shared_ptr sp) { m_map[key] = sp; } + + /** + * Print each of the object in the DB into the stream provided + */ + void dump(std::ostream& os) + { + for (auto entry : m_map) { + os << "key: " << entry.first << std::endl; + os << " " << entry.second.lock()->to_string() << std::endl; + } + } + + /** + * Populate VPP from current state, on VPP restart + */ + void replay() + { + for (auto entry : m_map) { + entry.second.lock()->replay(); + } + } + +private: + /** + * the map of objects against their key + */ + std::map> m_map; +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/sub_interface.cpp b/src/vpp-api/vom/sub_interface.cpp new file mode 100644 index 00000000000..de56bf2fbac --- /dev/null +++ b/src/vpp-api/vom/sub_interface.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/sub_interface.hpp" + +namespace VOM { +/** + * Construct a new object matching the desried state + */ +sub_interface::sub_interface(const interface& parent, + admin_state_t state, + vlan_id_t vlan) + : interface(mk_name(parent, vlan), parent.type(), state) + , m_parent(parent.singular()) + , m_vlan(vlan) +{ +} + +sub_interface::sub_interface(const handle_t& handle, + const interface& parent, + admin_state_t state, + vlan_id_t vlan) + : interface(handle, + l2_address_t::ZERO, + mk_name(parent, vlan), + parent.type(), + state) + , m_parent(parent.singular()) + , m_vlan(vlan) +{ +} + +sub_interface::~sub_interface() +{ + sweep(); + release(); +} + +sub_interface::sub_interface(const sub_interface& o) + : interface(o) + , m_parent(o.m_parent) + , m_vlan(o.m_vlan) +{ +} + +std::string +sub_interface::mk_name(const interface& parent, vlan_id_t vlan) +{ + return (parent.name() + "." + std::to_string(vlan)); +} + +std::queue& +sub_interface::mk_create_cmd(std::queue& q) +{ + q.push(new create_cmd(m_hdl, name(), m_parent->handle(), m_vlan)); + + return (q); +} + +std::queue& +sub_interface::mk_delete_cmd(std::queue& q) +{ + q.push(new delete_cmd(m_hdl)); + + return (q); +} + +std::shared_ptr +sub_interface::singular() const +{ + return std::dynamic_pointer_cast(singular_i()); +} + +std::shared_ptr +sub_interface::singular_i() const +{ + return m_db.find_or_add(name(), *this); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/sub_interface.hpp b/src/vpp-api/vom/sub_interface.hpp new file mode 100644 index 00000000000..d780c8a070b --- /dev/null +++ b/src/vpp-api/vom/sub_interface.hpp @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_SUB_INTERFACE_H__ +#define __VOM_SUB_INTERFACE_H__ + +#include "vom/interface.hpp" + +namespace VOM { +/** + * A Sub-interface. e.g. a VLAN sub-interface on an Ethernet interface + */ +class sub_interface : public interface +{ + /* + * Typedef for VLAN ID + */ + typedef uint16_t vlan_id_t; + +public: + /** + * Construct a new object matching the desried state + */ + sub_interface(const interface& parent, admin_state_t state, vlan_id_t vlan); + /** + * Destructor + */ + ~sub_interface(); + /** + * Copy Constructor + */ + sub_interface(const sub_interface& o); + + /** + * Return the matching 'singular instance' of the sub-interface + */ + std::shared_ptr singular() const; + + /** + * A functor class that creates an interface + */ + class create_cmd : public interface::create_cmd + { + public: + /** + * Cstrunctor taking the reference to the parent + * and the sub-interface's VLAN + */ + create_cmd(HW::item& item, + const std::string& name, + const handle_t& parent, + uint16_t vlan); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const create_cmd& i) const; + + private: + /** + * Refernece to the parents handle + */ + const handle_t& m_parent; + + /** + * The VLAN of the sub-interface + */ + uint16_t m_vlan; + }; + + /** + * A cmd class that Delete an interface + */ + class delete_cmd : public interface::delete_cmd + { + public: + /** + * Constructor + */ + delete_cmd(HW::item& item); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const delete_cmd& i) const; + }; + +private: + /** + * Construct with handle + */ + sub_interface(const handle_t& handle, + const interface& parent, + admin_state_t state, + vlan_id_t vlan); + /** + * The interface class can construct interfaces with handles + */ + friend class interface; + + /** + * Return the matching 'instance' of the sub-interface + * over-ride from the base class + */ + std::shared_ptr singular_i() const; + + /** + * Virtual functions to construct an interface create commands. + */ + virtual std::queue& mk_create_cmd(std::queue& cmds); + + /** + * Virtual functions to construct an interface delete commands. + */ + virtual std::queue& mk_delete_cmd(std::queue& cmds); + + /** + * From the name of the parent and the vlan, + * construct the sub-interface's name + */ + static std::string mk_name(const interface& parent, vlan_id_t vlan); + + /** + * Refernece conter lock on the parent + */ + const std::shared_ptr m_parent; + + /** + * VLAN ID + */ + vlan_id_t m_vlan; +}; +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/sub_interface_cmds.cpp b/src/vpp-api/vom/sub_interface_cmds.cpp new file mode 100644 index 00000000000..2d417c7b138 --- /dev/null +++ b/src/vpp-api/vom/sub_interface_cmds.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/sub_interface.hpp" +#include "vom/cmd.hpp" + +#include + +namespace VOM { +sub_interface::create_cmd::create_cmd(HW::item& item, + const std::string& name, + const handle_t& parent, + uint16_t vlan) + : interface::create_cmd(item, name) + , m_parent(parent) + , m_vlan(vlan) +{ +} + +bool +sub_interface::create_cmd::operator==(const create_cmd& other) const +{ + return ((m_name == other.m_name) && (m_parent == other.m_parent) && + (m_vlan == other.m_vlan)); +} + +rc_t +sub_interface::create_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_parent.value(); + payload.vlan_id = m_vlan; + + VAPI_CALL(req.execute()); + + m_hw_item = wait(); + + if (m_hw_item.rc() == rc_t::OK) { + interface::add(m_name, m_hw_item); + } + + return rc_t::OK; +} + +std::string +sub_interface::create_cmd::to_string() const +{ + std::ostringstream s; + s << "sub-itf-create: " << m_hw_item.to_string() << " parent:" << m_parent + << " vlan:" << m_vlan; + return (s.str()); +} + +sub_interface::delete_cmd::delete_cmd(HW::item& item) + : interface::delete_cmd(item) +{ +} + +bool +sub_interface::delete_cmd::operator==(const delete_cmd& other) const +{ + return (m_hw_item == other.m_hw_item); +} + +rc_t +sub_interface::delete_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.sw_if_index = m_hw_item.data().value(); + + VAPI_CALL(req.execute()); + + wait(); + m_hw_item.set(rc_t::NOOP); + + interface::remove(m_hw_item); + return (rc_t::OK); +} + +std::string +sub_interface::delete_cmd::to_string() const +{ + std::ostringstream s; + + s << "sub-itf-delete: " << m_hw_item.to_string(); + + return (s.str()); +} +} +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/tap_interface.cpp b/src/vpp-api/vom/tap_interface.cpp new file mode 100644 index 00000000000..d776c486c72 --- /dev/null +++ b/src/vpp-api/vom/tap_interface.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/tap_interface.hpp" +#include "vom/cmd.hpp" + +#include + +namespace VOM { +tap_interface::event_handler tap_interface::m_evh; + +/** + * Construct a new object matching the desried state + */ +tap_interface::tap_interface(const std::string& name, + admin_state_t state, + route::prefix_t prefix) + : interface(name, type_t::TAP, state) + , m_prefix(prefix) + , m_l2_address(l2_address_t::ZERO) +{ +} + +tap_interface::tap_interface(const std::string& name, + admin_state_t state, + route::prefix_t prefix, + const l2_address_t& l2_address) + : interface(name, type_t::TAP, state) + , m_prefix(prefix) + , m_l2_address(l2_address) +{ +} + +tap_interface::tap_interface(const handle_t& hdl, + const std::string& name, + admin_state_t state, + route::prefix_t prefix) + : interface(hdl, l2_address_t::ZERO, name, type_t::TAP, state) + , m_prefix(prefix) + , m_l2_address(l2_address_t::ZERO) +{ +} + +tap_interface::~tap_interface() +{ + sweep(); + release(); +} + +tap_interface::tap_interface(const tap_interface& o) + : interface(o) + , m_prefix(o.m_prefix) + , m_l2_address(o.m_l2_address) +{ +} + +std::queue& +tap_interface::mk_create_cmd(std::queue& q) +{ + q.push(new create_cmd(m_hdl, name(), m_prefix, m_l2_address)); + + return (q); +} + +std::queue& +tap_interface::mk_delete_cmd(std::queue& q) +{ + q.push(new delete_cmd(m_hdl)); + + return (q); +} + +std::shared_ptr +tap_interface::singular() const +{ + return std::dynamic_pointer_cast(singular_i()); +} + +std::shared_ptr +tap_interface::singular_i() const +{ + return m_db.find_or_add(name(), *this); +} + +void +tap_interface::event_handler::handle_populate(const client_db::key_t& key) +{ + /* + * dump VPP current states + */ + std::shared_ptr cmd(new tap_interface::dump_cmd()); + + HW::enqueue(cmd); + HW::write(); + + for (auto& record : *cmd) { + auto& payload = record.get_payload(); + + std::string name = reinterpret_cast(payload.dev_name); + + tap_interface itf(name, interface::admin_state_t::UP, + route::prefix_t::ZERO); + + VOM_LOG(log_level_t::DEBUG) << "tap-dump: " << itf.to_string(); + + /* + * Write each of the discovered interfaces into the OM, + * but disable the HW Command q whilst we do, so that no + * commands are sent to VPP + */ + OM::commit(key, itf); + } +} + +tap_interface::event_handler::event_handler() +{ + OM::register_listener(this); + inspect::register_handler({ "tap" }, "tap_interfaces", this); +} + +void +tap_interface::event_handler::handle_replay() +{ + m_db.replay(); +} + +dependency_t +tap_interface::event_handler::order() const +{ + return (dependency_t::INTERFACE); +} + +void +tap_interface::event_handler::show(std::ostream& os) +{ + m_db.dump(os); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/tap_interface.hpp b/src/vpp-api/vom/tap_interface.hpp new file mode 100644 index 00000000000..af90b7bb9d6 --- /dev/null +++ b/src/vpp-api/vom/tap_interface.hpp @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_TAP_INTERFACE_H__ +#define __VOM_TAP_INTERFACE_H__ + +#include "vom/interface.hpp" + +namespace VOM { +/** + * A tap-interface. e.g. a tap interface + */ +class tap_interface : public interface +{ +public: + tap_interface(const std::string& name, + admin_state_t state, + route::prefix_t prefix); + + tap_interface(const std::string& name, + admin_state_t state, + route::prefix_t prefix, + const l2_address_t& l2_address); + + ~tap_interface(); + tap_interface(const tap_interface& o); + + /** + * Return the matching 'singular instance' of the TAP interface + */ + std::shared_ptr singular() const; + + /** + * A functor class that creates an interface + */ + class create_cmd : public interface::create_cmd + { + public: + create_cmd(HW::item& item, + const std::string& name, + route::prefix_t& prefix, + const l2_address_t& l2_address); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + private: + route::prefix_t& m_prefix; + const l2_address_t& m_l2_address; + }; + + /** + * + */ + class delete_cmd : public interface::delete_cmd + { + public: + delete_cmd(HW::item& item); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + }; + + /** + * A cmd class that Dumps all the Vpp Interfaces + */ + class dump_cmd : public VOM::dump_cmd + { + public: + /** + * Default Constructor + */ + dump_cmd(); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const dump_cmd& i) const; + }; + +private: + /** + * Class definition for listeners to OM events + */ + class event_handler : public OM::listener, public inspect::command_handler + { + public: + event_handler(); + virtual ~event_handler() = default; + + /** + * Handle a populate event + */ + void handle_populate(const client_db::key_t& key); + + /** + * Handle a replay event + */ + void handle_replay(); + + /** + * Show the object in the Singular DB + */ + void show(std::ostream& os); + + /** + * Get the sortable Id of the listener + */ + dependency_t order() const; + }; + static event_handler m_evh; + + /** + * Construct with a handle + */ + tap_interface(const handle_t& hdl, + const std::string& name, + admin_state_t state, + route::prefix_t prefix); + + /** + * Ip Prefix + */ + route::prefix_t m_prefix; + + l2_address_t m_l2_address; + + /** + * interface is a friend so it can construct with handles + */ + friend class interface; + + /** + * Return the matching 'instance' of the sub-interface + * over-ride from the base class + */ + std::shared_ptr singular_i() const; + + /** + * Virtual functions to construct an interface create commands. + */ + virtual std::queue& mk_create_cmd(std::queue& cmds); + + /** + * Virtual functions to construct an interface delete commands. + */ + virtual std::queue& mk_delete_cmd(std::queue& cmds); + + /* + * It's the OM class that call singular() + */ + friend class OM; +}; +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/tap_interface_cmds.cpp b/src/vpp-api/vom/tap_interface_cmds.cpp new file mode 100644 index 00000000000..f616fd0b4ab --- /dev/null +++ b/src/vpp-api/vom/tap_interface_cmds.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/tap_interface.hpp" +#include "vom/cmd.hpp" + +#include + +namespace VOM { +tap_interface::create_cmd::create_cmd(HW::item& item, + const std::string& name, + route::prefix_t& prefix, + const l2_address_t& l2_address) + : interface::create_cmd(item, name) + , m_prefix(prefix) + , m_l2_address(l2_address) +{ +} + +rc_t +tap_interface::create_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + memset(payload.tap_name, 0, sizeof(payload.tap_name)); + memcpy(payload.tap_name, m_name.c_str(), + std::min(m_name.length(), sizeof(payload.tap_name))); + if (m_prefix != route::prefix_t::ZERO) { + if (m_prefix.address().is_v6()) { + m_prefix.to_vpp(&payload.ip6_address_set, payload.ip6_address, + &payload.ip6_mask_width); + } else { + m_prefix.to_vpp(&payload.ip4_address_set, payload.ip4_address, + &payload.ip4_mask_width); + payload.ip4_address_set = 1; + } + } + + if (m_l2_address != l2_address_t::ZERO) { + m_l2_address.to_bytes(payload.mac_address, 6); + } else { + payload.use_random_mac = 1; + } + + VAPI_CALL(req.execute()); + + m_hw_item = wait(); + + return rc_t::OK; +} + +std::string +tap_interface::create_cmd::to_string() const +{ + std::ostringstream s; + s << "tap-intf-create: " << m_hw_item.to_string() + << " ip-prefix:" << m_prefix.to_string(); + + return (s.str()); +} + +tap_interface::delete_cmd::delete_cmd(HW::item& item) + : interface::delete_cmd(item) +{ +} + +rc_t +tap_interface::delete_cmd::issue(connection& con) +{ + // finally... call VPP + + return rc_t::OK; +} +std::string +tap_interface::delete_cmd::to_string() const +{ + std::ostringstream s; + s << "tap-itf-delete: " << m_hw_item.to_string(); + + return (s.str()); +} + +tap_interface::dump_cmd::dump_cmd() +{ +} + +bool +tap_interface::dump_cmd::operator==(const dump_cmd& other) const +{ + return (true); +} + +rc_t +tap_interface::dump_cmd::issue(connection& con) +{ + m_dump.reset(new msg_t(con.ctx(), std::ref(*this))); + + VAPI_CALL(m_dump->execute()); + + wait(); + + return rc_t::INPROGRESS; +} + +std::string +tap_interface::dump_cmd::to_string() const +{ + return ("tap-itf-dump"); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/types.cpp b/src/vpp-api/vom/types.cpp new file mode 100644 index 00000000000..c362a608275 --- /dev/null +++ b/src/vpp-api/vom/types.cpp @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include "vom/types.hpp" + +namespace VOM { + +rc_t::rc_t(int v, const std::string s) + : enum_base(v, s) +{ +} +rc_t::~rc_t() +{ +} + +const rc_t& +rc_t::from_vpp_retval(int32_t rv) +{ + if (0 == rv) { + return (rc_t::OK); + } + if (-68 == rv) { + // interface laready exists + return (rc_t::OK); + } + + return (rc_t::INVALID); +} + +const rc_t rc_t::UNSET(0, "un-set"); +const rc_t rc_t::NOOP(1, "no-op"); +const rc_t rc_t::OK(2, "ok"); +const rc_t rc_t::INPROGRESS(3, "in-progess"); +const rc_t rc_t::INVALID(4, "invalid"); +const rc_t rc_t::TIMEOUT(5, "timeout"); + +const handle_t handle_t::INVALID(~0); + +handle_t::handle_t(int value) + : m_value(value) +{ +} + +handle_t::handle_t() + : m_value(~0) +{ +} + +std::string +handle_t::to_string() const +{ + return (std::to_string(m_value)); +} + +bool +handle_t::operator==(const handle_t& other) const +{ + return (m_value == other.m_value); +} + +bool +handle_t::operator!=(const handle_t& other) const +{ + return (!(*this == other)); +} + +bool +handle_t::operator<(const handle_t& other) const +{ + return (m_value < other.m_value); +} + +uint32_t +handle_t::value() const +{ + return (m_value); +} + +std::ostream& +operator<<(std::ostream& os, const handle_t& h) +{ + os << h.value(); + + return (os); +} + +mac_address_t::mac_address_t(uint64_t address) +{ + uint8_t mac[6]; + + std::memcpy(mac, &address, 6); + for (int i = 0; i < 6; i++) { + bytes[i] = mac[5 - i]; + } +} + +mac_address_t::mac_address_t(uint8_t b[6]) +{ + std::copy(b, b + 6, std::begin(bytes)); +} + +mac_address_t::mac_address_t(std::initializer_list i) +{ + std::copy(i.begin(), i.end(), std::begin(bytes)); +} + +const mac_address_t mac_address_t::ONE({ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }); + +const mac_address_t mac_address_t::ZERO({ 0x0 }); + +void +mac_address_t::to_bytes(uint8_t* array, uint8_t len) const +{ + for (int i = 0; i < 6 && i < len; i++) { + array[i] = bytes[i]; + } +} + +uint64_t +mac_address_t::to_u64() const +{ + uint64_t mac6 = 0; + uint8_t* b = reinterpret_cast(&mac6); + + // whack hack. the vapi will byte swap. + b[2] = bytes[5]; + b[3] = bytes[4]; + b[4] = bytes[3]; + b[5] = bytes[2]; + b[6] = bytes[1]; + b[7] = bytes[0]; + + return (mac6); +} + +std::string +mac_address_t::to_string() const +{ + std::ostringstream s; + bool first = true; + + s.fill('0'); + s << std::hex; + s << "mac:["; + for (auto byte : bytes) { + if (first) + first = false; + else + s << ":"; + s << std::setw(2) << static_cast(byte); + } + s << "]"; + + return (s.str()); +} + +bool +mac_address_t::operator==(const mac_address_t& mac) const +{ + return (bytes == mac.bytes); +} +bool +mac_address_t::operator<(const mac_address_t& m) const +{ + return (bytes < m.bytes); +} + +std::ostream& +operator<<(std::ostream& os, const mac_address_t& mac) +{ + os << mac.to_string(); + + return (os); +} + +l2_address_t::l2_address_t(const uint8_t b[8], uint8_t n_bytes) + : bytes(n_bytes) +{ + std::copy_n(b, n_bytes, std::begin(bytes)); +} + +l2_address_t::l2_address_t(std::initializer_list i) + : bytes(i) +{ +} + +l2_address_t::l2_address_t(const mac_address_t& mac) + : bytes(6) +{ + std::copy(begin(mac.bytes), std::end(mac.bytes), std::begin(bytes)); +} + +const l2_address_t l2_address_t::ONE({ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff }); + +const l2_address_t l2_address_t::ZERO({ 0x0 }); + +void +l2_address_t::to_bytes(uint8_t* array, uint8_t len) const +{ + for (uint8_t i = 0; i < bytes.size() && i < len; i++) { + array[i] = bytes[i]; + } +} + +mac_address_t +l2_address_t::to_mac() const +{ + mac_address_t mac({}); + + std::copy_n(bytes.begin(), mac.bytes.size(), mac.bytes.begin()); + + return (mac); +} + +std::string +l2_address_t::to_string() const +{ + std::ostringstream s; + bool first = true; + + s.fill('0'); + s << std::hex; + for (auto byte : bytes) { + if (first) + first = false; + else + s << ":"; + s << std::setw(2) << static_cast(byte); + } + + return (s.str()); +} + +bool +l2_address_t::operator==(const l2_address_t& l2) const +{ + return (bytes == l2.bytes); +} + +bool +l2_address_t::operator!=(const l2_address_t& l2) const +{ + return (bytes != l2.bytes); +} + +std::ostream& +operator<<(std::ostream& os, const l2_address_t& l2) +{ + os << l2.to_string(); + + return (os); +} + +const direction_t direction_t::INPUT(1, "input"); +const direction_t direction_t::OUTPUT(0, "output"); + +direction_t::direction_t(int v, const std::string s) + : enum_base(v, s) +{ +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/types.hpp b/src/vpp-api/vom/types.hpp new file mode 100644 index 00000000000..fd3b24b64ca --- /dev/null +++ b/src/vpp-api/vom/types.hpp @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_TYPES_H__ +#define __VOM_TYPES_H__ + +#include +#include + +#include + +#include "vom/enum_base.hpp" + +/** + * Convenince wrapper macro for error handling in VAPI sends + */ +#define VAPI_CALL(_stmt) \ + { \ + vapi_error_e _rv; \ + do { \ + _rv = (_stmt); \ + } while (VAPI_OK != _rv); \ + } + +namespace VOM { +/** + * There needs to be a strict order in which object types are read from VPP + * (at boot time) and replayed to VPP (if VPP restarts). That ordering is + * defined in this enum types + */ +enum class dependency_t +{ + /** + * Global Configuration has no dependency + */ + GLOBAL = 0, + + /** + * interfaces are the root of the dependency graph + */ + INTERFACE, + + /** + * Tunnel or virtual interfaces next + */ + TUNNEL, + + /** + * Tables in which entries are added, e.g bridge/route-domains + */ + TABLE, + + /** + * ACLs + */ + ACL, + + /** + * Then L2/objects that bind to interfaces, BD, ACLS, etc + */ + BINDING, + + /** + * Entries in Tables + */ + ENTRY, +}; + +/** + * Error codes that VPP will return during a HW write + */ +struct rc_t : public enum_base +{ + rc_t(const rc_t& rc) = default; + + /** + * Destructor + */ + ~rc_t(); + + /** + * The value un-set + */ + const static rc_t UNSET; + + /** + * The HW write/update action was/has not been attempted + */ + const static rc_t NOOP; + + /** + * The HW write was successfull + */ + const static rc_t OK; + + /** + * HW write is in progress. Also used for the 'want' events + * that never complete + */ + const static rc_t INPROGRESS; + + /** + * HW write reported invalid input + */ + const static rc_t INVALID; + + /** + * HW write timedout - VPP did not respond within a timely manner + */ + const static rc_t TIMEOUT; + + /** + * Get the rc_t from the VPP API value + */ + static const rc_t& from_vpp_retval(int32_t rv); + +private: + /** + * Constructor + */ + rc_t(int v, const std::string s); +}; + +/** + * Feature Directions + */ +struct direction_t : public enum_base +{ + /** + * Constructor + */ + direction_t(int v, const std::string s); + + /** + * Destructor + */ + ~direction_t() = default; + + /** + * Permit Direction + */ + const static direction_t INPUT; + + /** + * Deny Direction + */ + const static direction_t OUTPUT; +}; + +/** + * A type declaration of an interface handle in VPP + */ +struct handle_t +{ + /** + * Constructor + */ + handle_t(int value); + + /** + * Constructor + */ + handle_t(); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator + */ + bool operator==(const handle_t& other) const; + + /** + * Comparison operator + */ + bool operator!=(const handle_t& other) const; + + /** + * less than operator + */ + bool operator<(const handle_t& other) const; + + /** + * A value of an interface handle_t that means the itf does not exist + */ + const static handle_t INVALID; + + /** + * get the value of the handle + */ + uint32_t value() const; + +private: + /** + * VPP's handle value + */ + uint32_t m_value; +}; + +/** + * ostream print of a handle_t + */ +std::ostream& operator<<(std::ostream& os, const handle_t& h); + +/** + * Type def of a Ethernet address + */ +struct mac_address_t +{ + mac_address_t(uint64_t address); + mac_address_t(uint8_t bytes[6]); + mac_address_t(std::initializer_list bytes); + /** + * Convert to byte array + */ + void to_bytes(uint8_t* array, uint8_t len) const; + + /** + * An all 1's MAC address + */ + const static mac_address_t ONE; + + /** + * An all 0's MAC address + */ + const static mac_address_t ZERO; + + /** + * Comparison operator + */ + bool operator==(const mac_address_t& m) const; + + /** + * less than operator + */ + bool operator<(const mac_address_t& m) const; + + /** + * String conversion + */ + std::string to_string() const; + + /** + * U64 conversion + */ + uint64_t to_u64() const; + + /** + * Underlying bytes array + */ + std::array bytes; +}; + +/** + * Type def of a L2 address as read from VPP + */ +struct l2_address_t +{ + l2_address_t(const uint8_t bytes[8], uint8_t n_bytes); + l2_address_t(std::initializer_list bytes); + l2_address_t(const mac_address_t& mac); + + /** + * Convert to byte array + */ + void to_bytes(uint8_t* array, uint8_t len) const; + + /** + * An all 1's L2 address + */ + const static l2_address_t ONE; + + /** + * An all 0's L2 address + */ + const static l2_address_t ZERO; + + /** + * Comparison operator + */ + bool operator==(const l2_address_t& m) const; + + /** + * Comparison operator + */ + bool operator!=(const l2_address_t& m) const; + + /** + * String conversion + */ + std::string to_string() const; + + /** + * MAC address conversion + */ + mac_address_t to_mac() const; + + /** + * Underlying bytes array - filled from least to most significant + */ + std::vector bytes; +}; + +/** + * Ostream operator for a MAC address + */ +std::ostream& operator<<(std::ostream& os, const mac_address_t& mac); + +/** + * Ostream operator for a MAC address + */ +std::ostream& operator<<(std::ostream& os, const l2_address_t& l2); +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/vxlan_tunnel.cpp b/src/vpp-api/vom/vxlan_tunnel.cpp new file mode 100644 index 00000000000..7079021ee6e --- /dev/null +++ b/src/vpp-api/vom/vxlan_tunnel.cpp @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/vxlan_tunnel.hpp" +#include "vom/logger.hpp" + +namespace VOM { +const std::string VXLAN_TUNNEL_NAME = "vxlan-tunnel-itf"; + +vxlan_tunnel::event_handler vxlan_tunnel::m_evh; + +/** + * A DB of all vxlan_tunnels + * this does not register as a listener for replay events, since the tunnels + * are also in the base-class interface DB and so will be poked from there. + */ +singular_db vxlan_tunnel::m_db; + +vxlan_tunnel::endpoint_t::endpoint_t(const boost::asio::ip::address& src, + const boost::asio::ip::address& dst, + uint32_t vni) + : src(src) + , dst(dst) + , vni(vni) +{ +} + +vxlan_tunnel::endpoint_t::endpoint_t() + : src() + , dst() + , vni(0) +{ +} + +bool +vxlan_tunnel::endpoint_t::operator==(const endpoint_t& other) const +{ + return ((src == other.src) && (dst == other.dst) && (vni == other.vni)); +} + +bool +vxlan_tunnel::endpoint_t::operator<(const vxlan_tunnel::endpoint_t& o) const +{ + if (src < o.src) + return true; + if (dst < o.dst) + return true; + if (vni < o.vni) + return true; + + return false; +} + +std::string +vxlan_tunnel::endpoint_t::to_string() const +{ + std::ostringstream s; + + s << "ep:[" + << "src:" << src.to_string() << " dst:" << dst.to_string() << " vni:" << vni + << "]"; + + return (s.str()); +} + +std::ostream& +operator<<(std::ostream& os, const vxlan_tunnel::endpoint_t& ep) +{ + os << ep.to_string(); + + return (os); +} + +std::string +vxlan_tunnel::mk_name(const boost::asio::ip::address& src, + const boost::asio::ip::address& dst, + uint32_t vni) +{ + std::ostringstream s; + + s << VXLAN_TUNNEL_NAME << "-" << src << "-" << dst << ":" << vni; + + return (s.str()); +} + +vxlan_tunnel::vxlan_tunnel(const boost::asio::ip::address& src, + const boost::asio::ip::address& dst, + uint32_t vni) + : interface(mk_name(src, dst, vni), + interface::type_t::VXLAN, + interface::admin_state_t::UP) + , m_tep(src, dst, vni) +{ +} + +vxlan_tunnel::vxlan_tunnel(const handle_t& hdl, + const boost::asio::ip::address& src, + const boost::asio::ip::address& dst, + uint32_t vni) + : interface(hdl, + l2_address_t::ZERO, + mk_name(src, dst, vni), + interface::type_t::VXLAN, + interface::admin_state_t::UP) + , m_tep(src, dst, vni) +{ +} + +vxlan_tunnel::vxlan_tunnel(const vxlan_tunnel& o) + : interface(o) + , m_tep(o.m_tep) +{ +} + +const handle_t& +vxlan_tunnel::handle() const +{ + return (m_hdl.data()); +} + +void +vxlan_tunnel::sweep() +{ + if (m_hdl) { + HW::enqueue(new delete_cmd(m_hdl, m_tep)); + } + HW::write(); +} + +void +vxlan_tunnel::replay() +{ + if (m_hdl) { + HW::enqueue(new create_cmd(m_hdl, name(), m_tep)); + } +} + +vxlan_tunnel::~vxlan_tunnel() +{ + sweep(); + + /* + * release from both DBs + */ + release(); + m_db.release(m_tep, this); +} + +std::string +vxlan_tunnel::to_string() const +{ + std::ostringstream s; + s << "vxlan-tunnel: " << m_hdl.to_string() << " " << m_tep.to_string(); + + return (s.str()); +} + +void +vxlan_tunnel::update(const vxlan_tunnel& desired) +{ + /* + * the desired state is always that the interface should be created + */ + if (!m_hdl) { + HW::enqueue(new create_cmd(m_hdl, name(), m_tep)); + } +} + +std::shared_ptr +vxlan_tunnel::find_or_add(const vxlan_tunnel& temp) +{ + /* + * a VXLAN tunnel needs to be in both the interface-find-by-name + * and the vxlan_tunnel-find-by-endpoint singular databases + */ + std::shared_ptr sp; + + sp = m_db.find_or_add(temp.m_tep, temp); + + interface::m_db.add(temp.name(), sp); + + return (sp); +} + +std::shared_ptr +vxlan_tunnel::singular() const +{ + return (find_or_add(*this)); +} + +std::shared_ptr +vxlan_tunnel::singular_i() const +{ + return find_or_add(*this); +} + +void +vxlan_tunnel::dump(std::ostream& os) +{ + m_db.dump(os); +} + +void +vxlan_tunnel::event_handler::handle_populate(const client_db::key_t& key) +{ + /* + * dump VPP current states + */ + std::shared_ptr cmd(new vxlan_tunnel::dump_cmd()); + + HW::enqueue(cmd); + HW::write(); + + for (auto& record : *cmd) { + auto& payload = record.get_payload(); + handle_t hdl(payload.sw_if_index); + boost::asio::ip::address src = + from_bytes(payload.is_ipv6, payload.src_address); + boost::asio::ip::address dst = + from_bytes(payload.is_ipv6, payload.dst_address); + + vxlan_tunnel vt(hdl, src, dst, payload.vni); + + VOM_LOG(log_level_t::DEBUG) << "dump: " << vt.to_string(); + + OM::commit(key, vt); + } +} + +vxlan_tunnel::event_handler::event_handler() +{ + OM::register_listener(this); + inspect::register_handler({ "vxlan" }, "VXLAN Tunnels", this); +} + +void +vxlan_tunnel::event_handler::handle_replay() +{ + // replay is handled from the interface DB +} + +dependency_t +vxlan_tunnel::event_handler::order() const +{ + return (dependency_t::TUNNEL); +} + +void +vxlan_tunnel::event_handler::show(std::ostream& os) +{ + m_db.dump(os); +} +} +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/src/vpp-api/vom/vxlan_tunnel.hpp b/src/vpp-api/vom/vxlan_tunnel.hpp new file mode 100644 index 00000000000..845cd6aee45 --- /dev/null +++ b/src/vpp-api/vom/vxlan_tunnel.hpp @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOM_VXLAN_TUNNEL_H__ +#define __VOM_VXLAN_TUNNEL_H__ + +#include "vom/hw.hpp" +#include "vom/inspect.hpp" +#include "vom/interface.hpp" +#include "vom/object_base.hpp" +#include "vom/om.hpp" +#include "vom/prefix.hpp" +#include "vom/route_domain.hpp" +#include "vom/rpc_cmd.hpp" +#include "vom/singular_db.hpp" + +#include + +namespace VOM { +/** + * A representation of a VXLAN Tunnel in VPP + */ +class vxlan_tunnel : public interface +{ +public: + /** + * Combaintion of attributes that are a unique key + * for a VXLAN tunnel + */ + struct endpoint_t + { + /** + * Default constructor + */ + endpoint_t(); + /** + * Constructor taking endpoint values + */ + endpoint_t(const boost::asio::ip::address& src, + const boost::asio::ip::address& dst, + uint32_t vni); + + /** + * less-than operator for map storage + */ + bool operator<(const endpoint_t& o) const; + + /** + * Comparison operator + */ + bool operator==(const endpoint_t& o) const; + + /** + * Debug print function + */ + std::string to_string() const; + + /** + * The src IP address of the endpoint + */ + boost::asio::ip::address src; + + /** + * The destination IP address of the endpoint + */ + boost::asio::ip::address dst; + + /** + * The VNI of the endpoint + */ + uint32_t vni; + }; + + /** + * Construct a new object matching the desried state + */ + vxlan_tunnel(const boost::asio::ip::address& src, + const boost::asio::ip::address& dst, + uint32_t vni); + + /** + * Construct a new object matching the desried state with a handle + * read from VPP + */ + vxlan_tunnel(const handle_t& hdl, + const boost::asio::ip::address& src, + const boost::asio::ip::address& dst, + uint32_t vni); + + /* + * Destructor + */ + ~vxlan_tunnel(); + + /** + * Copy constructor + */ + vxlan_tunnel(const vxlan_tunnel& o); + + /** + * Return the matching 'singular instance' + */ + std::shared_ptr singular() const; + + /** + * Debug rpint function + */ + virtual std::string to_string() const; + + /** + * Return VPP's handle to this object + */ + const handle_t& handle() const; + + /** + * Dump all L3Configs into the stream provided + */ + static void dump(std::ostream& os); + + /** + * A Command class that creates an VXLAN tunnel + */ + class create_cmd : public interface::create_cmd + { + public: + /** + * Create command constructor taking HW item to update and the + * endpoint values + */ + create_cmd(HW::item& item, + const std::string& name, + const endpoint_t& ep); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const create_cmd& i) const; + + private: + /** + * Enpoint values of the tunnel to be created + */ + const endpoint_t m_ep; + }; + + /** + * A functor class that creates an VXLAN tunnel + */ + class delete_cmd : public interface::delete_cmd + { + public: + /** + * delete command constructor taking HW item to update and the + * endpoint values + */ + delete_cmd(HW::item& item, const endpoint_t& ep); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const delete_cmd& i) const; + + private: + /** + * Enpoint values of the tunnel to be deleted + */ + const endpoint_t m_ep; + }; + + /** + * A cmd class that Dumps all the Vpp interfaces + */ + class dump_cmd : public VOM::dump_cmd + { + public: + /** + * Default Constructor + */ + dump_cmd(); + + /** + * Issue the command to VPP/HW + */ + rc_t issue(connection& con); + /** + * convert to string format for debug purposes + */ + std::string to_string() const; + + /** + * Comparison operator - only used for UT + */ + bool operator==(const dump_cmd& i) const; + }; + +private: + /** + * Class definition for listeners to OM events + */ + class event_handler : public OM::listener, public inspect::command_handler + { + public: + event_handler(); + virtual ~event_handler() = default; + + /** + * Handle a populate event + */ + void handle_populate(const client_db::key_t& key); + + /** + * Handle a replay event + */ + void handle_replay(); + + /** + * Show the object in the Singular DB + */ + void show(std::ostream& os); + + /** + * Get the sortable Id of the listener + */ + dependency_t order() const; + }; + + /** + * Event handle to register with OM + */ + static event_handler m_evh; + + /** + * Commit the acculmulated changes into VPP. i.e. to a 'HW" write. + */ + void update(const vxlan_tunnel& obj); + + /** + * Return the matching 'instance' of the sub-interface + * over-ride from the base class + */ + std::shared_ptr singular_i() const; + + /** + * Find the VXLAN tunnel in the OM + */ + static std::shared_ptr find_or_add(const vxlan_tunnel& temp); + + /* + * It's the VPPHW class that updates the objects in HW + */ + friend class OM; + + /** + * It's the singular_db class that calls replay() + */ + friend class singular_db; + + /** + * Sweep/reap the object if still stale + */ + void sweep(void); + + /** + * replay the object to create it in hardware + */ + void replay(void); + + /** + * Tunnel enpoint/key + */ + endpoint_t m_tep; + + /** + * A map of all VLAN tunnela against thier key + */ + static singular_db m_db; + + /** + * Construct a unique name for the tunnel + */ + static std::string mk_name(const boost::asio::ip::address& src, + const boost::asio::ip::address& dst, + uint32_t vni); +}; + +/** + * Ostream output for a tunnel endpoint + */ +std::ostream& operator<<(std::ostream& os, const vxlan_tunnel::endpoint_t& ep); +}; + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ + +#endif diff --git a/src/vpp-api/vom/vxlan_tunnel_cmds.cpp b/src/vpp-api/vom/vxlan_tunnel_cmds.cpp new file mode 100644 index 00000000000..9a4a1312c4a --- /dev/null +++ b/src/vpp-api/vom/vxlan_tunnel_cmds.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2017 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "vom/vxlan_tunnel.hpp" + +DEFINE_VAPI_MSG_IDS_VXLAN_API_JSON; + +namespace VOM { +vxlan_tunnel::create_cmd::create_cmd(HW::item& item, + const std::string& name, + const endpoint_t& ep) + : interface::create_cmd(item, name) + , m_ep(ep) +{ +} + +bool +vxlan_tunnel::create_cmd::operator==(const create_cmd& other) const +{ + return (m_ep == other.m_ep); +} + +rc_t +vxlan_tunnel::create_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto& payload = req.get_request().get_payload(); + payload.is_add = 1; + payload.is_ipv6 = 0; + to_bytes(m_ep.src, &payload.is_ipv6, payload.src_address); + to_bytes(m_ep.dst, &payload.is_ipv6, payload.dst_address); + payload.mcast_sw_if_index = ~0; + payload.encap_vrf_id = 0; + payload.decap_next_index = ~0; + payload.vni = m_ep.vni; + + VAPI_CALL(req.execute()); + + m_hw_item = wait(); + + if (m_hw_item) { + interface::add(m_name, m_hw_item); + } + + return rc_t::OK; +} + +std::string +vxlan_tunnel::create_cmd::to_string() const +{ + std::ostringstream s; + s << "vxlan-tunnel-create: " << m_hw_item.to_string() << m_ep.to_string(); + + return (s.str()); +} + +vxlan_tunnel::delete_cmd::delete_cmd(HW::item& item, + const endpoint_t& ep) + : interface::delete_cmd(item) + , m_ep(ep) +{ +} + +bool +vxlan_tunnel::delete_cmd::operator==(const delete_cmd& other) const +{ + return (m_ep == other.m_ep); +} + +rc_t +vxlan_tunnel::delete_cmd::issue(connection& con) +{ + msg_t req(con.ctx(), std::ref(*this)); + + auto payload = req.get_request().get_payload(); + payload.is_add = 0; + payload.is_ipv6 = 0; + to_bytes(m_ep.src, &payload.is_ipv6, payload.src_address); + to_bytes(m_ep.dst, &payload.is_ipv6, payload.dst_address); + payload.mcast_sw_if_index = ~0; + payload.encap_vrf_id = 0; + payload.decap_next_index = ~0; + payload.vni = m_ep.vni; + + VAPI_CALL(req.execute()); + + wait(); + m_hw_item.set(rc_t::NOOP); + + interface::remove(m_hw_item); + return (rc_t::OK); +} + +std::string +vxlan_tunnel::delete_cmd::to_string() const +{ + std::ostringstream s; + s << "vxlan-tunnel-delete: " << m_hw_item.to_string() << m_ep.to_string(); + + return (s.str()); +} + +vxlan_tunnel::dump_cmd::dump_cmd() +{ +} + +bool +vxlan_tunnel::dump_cmd::operator==(const dump_cmd& other) const +{ + return (true); +} + +rc_t +vxlan_tunnel::dump_cmd::issue(connection& con) +{ + m_dump.reset(new msg_t(con.ctx(), std::ref(*this))); + + auto& payload = m_dump->get_request().get_payload(); + payload.sw_if_index = ~0; + + VAPI_CALL(m_dump->execute()); + + wait(); + + return rc_t::OK; +} + +std::string +vxlan_tunnel::dump_cmd::to_string() const +{ + return ("Vpp-vxlan_tunnels-Dump"); +} +} + +/* + * fd.io coding-style-patch-verification: ON + * + * Local Variables: + * eval: (c-set-style "mozilla") + * End: + */ diff --git a/test/ext/Makefile b/test/ext/Makefile index 1bd035daa13..58015539d92 100644 --- a/test/ext/Makefile +++ b/test/ext/Makefile @@ -1,38 +1,57 @@ -BINDIR = $(BR)/vapi_test/ -CBIN = $(addprefix $(BINDIR), vapi_c_test) -CPPBIN = $(addprefix $(BINDIR), vapi_cpp_test) +VAPI_BINDIR = $(BR)/vapi_test/ +VAPI_CBIN = $(addprefix $(VAPI_BINDIR), vapi_c_test) +VAPI_CPPBIN = $(addprefix $(VAPI_BINDIR), vapi_cpp_test) +VOM_BINDIR = $(BR)/vom_test/ +VOM_BIN = $(addprefix $(VOM_BINDIR), vom_test) ifeq ($(filter centos,$(OS_ID)),$(OS_ID)) -CPPBIN= +VAPI_CPPBIN= endif -LIBS = -L$(VPP_TEST_BUILD_DIR)/vpp/.libs/ -L$(VPP_TEST_BUILD_DIR)/vpp/vpp-api/vapi/.libs/ -lvppinfra -lvlibmemoryclient -lsvm -lpthread -lcheck -lrt -lm -lvapiclient +VAPI_LIBS = -L$(VPP_TEST_BUILD_DIR)/vpp/.libs/ -L$(VPP_TEST_BUILD_DIR)/vpp/vpp-api/vapi/.libs/ -lvppinfra -lvlibmemoryclient -lsvm -lpthread -lcheck -lrt -lm -lvapiclient ifneq ($(filter opensuse,$(OS_ID)),$(OS_ID)) -LIBS += -lsubunit +VAPI_LIBS += -lsubunit endif -CFLAGS = -std=gnu99 -g -Wall -pthread -I$(WS_ROOT)/src -I$(VPP_TEST_INSTALL_PATH)/vpp/include -I$(BINDIR) -CPPFLAGS = -std=c++11 -g -Wall -pthread -I$(WS_ROOT)/src -I$(VPP_TEST_INSTALL_PATH)/vpp/include -I$(BINDIR) +CFLAGS = -std=gnu99 -g -Wall -pthread -I$(WS_ROOT)/src -I$(VPP_TEST_INSTALL_PATH)/vpp/include -I$(VAPI_BINDIR) +CPPFLAGS = -std=c++11 -g -Wall -pthread -I$(WS_ROOT)/src -I$(VPP_TEST_INSTALL_PATH)/vpp/include -I$(VAPI_BINDIR) -all: $(CBIN) $(CPPBIN) +all: $(VAPI_CBIN) $(VAPI_CPPBIN) $(VOM_BINDIR) $(VOM_BIN) -$(BINDIR): - mkdir -p $(BINDIR) +$(VAPI_BINDIR): + mkdir -p $(VAPI_BINDIR) CSRC = vapi_c_test.c -$(BINDIR)/fake.api.vapi.h: fake.api.json $(WS_ROOT)/src/vpp-api/vapi/vapi_c_gen.py | $(BINDIR) - $(WS_ROOT)/src/vpp-api/vapi/vapi_c_gen.py --prefix $(BINDIR) $< +$(VAPI_BINDIR)/fake.api.vapi.h: fake.api.json $(WS_ROOT)/src/vpp-api/vapi/vapi_c_gen.py | $(VAPI_BINDIR) + $(WS_ROOT)/src/vpp-api/vapi/vapi_c_gen.py --prefix $(VAPI_BINDIR) $< -$(BINDIR)/fake.api.vapi.hpp: fake.api.json $(WS_ROOT)/src/vpp-api/vapi/vapi_cpp_gen.py | $(BINDIR) - $(WS_ROOT)/src/vpp-api/vapi/vapi_cpp_gen.py --prefix $(BINDIR) $< +$(VAPI_BINDIR)/fake.api.vapi.hpp: fake.api.json $(WS_ROOT)/src/vpp-api/vapi/vapi_cpp_gen.py | $(VAPI_BINDIR) + $(WS_ROOT)/src/vpp-api/vapi/vapi_cpp_gen.py --prefix $(VAPI_BINDIR) $< -$(CBIN): $(CSRC) $(VPP_TEST_BUILD_DIR)/vpp/vpp-api/vapi/.libs/libvapiclient.so $(VPP_TEST_BUILD_DIR)/vpp/.libs/libvppinfra.so $(VPP_TEST_BUILD_DIR)/vpp/.libs/libvlibmemoryclient.so $(VPP_TEST_BUILD_DIR)/vpp/.libs/libsvm.so $(BINDIR)/fake.api.vapi.h - $(CC) -o $@ $(CFLAGS) $(CSRC) $(LIBS) +$(VAPI_CBIN): $(CSRC) $(VPP_TEST_BUILD_DIR)/vpp/vpp-api/vapi/.libs/libvapiclient.so $(VPP_TEST_BUILD_DIR)/vpp/.libs/libvppinfra.so $(VPP_TEST_BUILD_DIR)/vpp/.libs/libvlibmemoryclient.so $(VPP_TEST_BUILD_DIR)/vpp/.libs/libsvm.so $(VAPI_BINDIR)/fake.api.vapi.h + $(CC) -o $@ $(CFLAGS) $(CSRC) $(VAPI_LIBS) CPPSRC = vapi_cpp_test.cpp -$(CPPBIN): $(CPPSRC) $(VPP_TEST_BUILD_DIR)/vpp/vpp-api/vapi/.libs/libvapiclient.so $(VPP_TEST_BUILD_DIR)/vpp/.libs/libvppinfra.so $(VPP_TEST_BUILD_DIR)/vpp/.libs/libvlibmemoryclient.so $(VPP_TEST_BUILD_DIR)/vpp/.libs/libsvm.so $(BINDIR)/fake.api.vapi.hpp - $(CXX) -o $@ $(CPPFLAGS) $(CPPSRC) $(LIBS) +$(VAPI_CPPBIN): $(CPPSRC) $(VPP_TEST_BUILD_DIR)/vpp/vpp-api/vapi/.libs/libvapiclient.so $(VPP_TEST_BUILD_DIR)/vpp/.libs/libvppinfra.so $(VPP_TEST_BUILD_DIR)/vpp/.libs/libvlibmemoryclient.so $(VPP_TEST_BUILD_DIR)/vpp/.libs/libsvm.so $(VAPI_BINDIR)/fake.api.vapi.hpp + $(CXX) -o $@ $(CPPFLAGS) $(CPPSRC) $(VAPI_LIBS) + +VOM_CPPSRC = vom_test.cpp + +$(VOM_BINDIR): + mkdir -p $(VOM_BINDIR) + +LIB_VOM = $(VPP_TEST_BUILD_DIR)/vpp/vpp-api/vom/.libs/libvom.so +VOM_LIBS = $(LIB_VOM) \ + -lboost_log \ + -lboost_thread \ + -lboost_system \ + -lboost_filesystem \ + -lboost_unit_test_framework \ + $(VAPI_LIBS) + +$(VOM_BIN): $(VOM_CPPSRC) $(VOM_BINDIR) $(LIB_VOM) $(VPP_TEST_BUILD_DIR)/vpp/vpp-api/vapi/.libs/libvapiclient.so + $(CXX) -o $@ $(CPPFLAGS) -DBOOST_LOG_DYN_LINK -O0 -g $(VOM_CPPSRC) $(VOM_LIBS) clean: - rm -rf $(BINDIR) + rm -rf $(VAPI_BINDIR) $(VOM_BINDIR) diff --git a/test/ext/vom_test.cpp b/test/ext/vom_test.cpp new file mode 100644 index 00000000000..4c72fbc1aaa --- /dev/null +++ b/test/ext/vom_test.cpp @@ -0,0 +1,1447 @@ +/* + * Test suite for class VppOM + * + * 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 + */ +#define BOOST_TEST_MODULE "VPP OBJECT MODEL" +#define BOOST_TEST_DYN_LINK + +#include +#include + + +#include +#include + +#include "vom/om.hpp" +#include "vom/interface.hpp" +#include "vom/l2_binding.hpp" +#include "vom/l3_binding.hpp" +#include "vom/bridge_domain.hpp" +#include "vom/bridge_domain_entry.hpp" +#include "vom/bridge_domain_arp_entry.hpp" +#include "vom/prefix.hpp" +#include "vom/route.hpp" +#include "vom/route_domain.hpp" +#include "vom/vxlan_tunnel.hpp" +#include "vom/sub_interface.hpp" +#include "vom/acl_list.hpp" +#include "vom/acl_binding.hpp" +#include "vom/acl_l3_rule.hpp" +#include "vom/acl_l2_rule.hpp" +#include "vom/arp_proxy_config.hpp" +#include "vom/arp_proxy_binding.hpp" +#include "vom/ip_unnumbered.hpp" +#include "vom/interface_ip6_nd.hpp" +#include "vom/interface_span.hpp" +#include "vom/neighbour.hpp" +#include "vom/nat_static.hpp" +#include "vom/nat_binding.hpp" + +using namespace boost; +using namespace VOM; + +/** + * An expectation exception + */ +class ExpException +{ +public: + ExpException(unsigned int number) + { + // a neat place to add a break point + std::cout << " ExpException here: " << number << std::endl; + } +}; + +class MockListener : public interface::event_listener, + public interface::stat_listener +{ + void handle_interface_stat(interface::stats_cmd *cmd) + { + } + void handle_interface_event(interface::events_cmd *cmd) + { + } +}; + +class MockCmdQ : public HW::cmd_q +{ +public: + MockCmdQ(): + m_strict_order(true) + { + } + virtual ~MockCmdQ() + { + } + void expect(cmd *f) + { + m_exp_queue.push_back(f); + } + void enqueue(cmd *f) + { + m_act_queue.push_back(f); + } + void enqueue(std::queue &cmds) + { + while (cmds.size()) + { + m_act_queue.push_back(cmds.front()); + cmds.pop(); + } + } + void enqueue(std::shared_ptr f) + { + m_act_queue.push_back(f.get()); + } + + void dequeue(cmd *f) + { + } + + void dequeue(std::shared_ptr cmd) + { + } + + void strict_order(bool on) + { + m_strict_order = on; + } + + bool is_empty() + { + return ((0 == m_exp_queue.size()) && + (0 == m_act_queue.size())); + } + + rc_t write() + { + cmd *f_exp, *f_act; + rc_t rc = rc_t::OK; + + while (m_act_queue.size()) + { + bool matched = false; + auto it_exp = m_exp_queue.begin(); + auto it_act = m_act_queue.begin(); + + f_act = *it_act; + + std::cout << " Act: " << f_act->to_string() << std::endl; + while (it_exp != m_exp_queue.end()) + { + f_exp = *it_exp; + try + { + std::cout << " Exp: " << f_exp->to_string() << std::endl; + + if (typeid(*f_exp) != typeid(*f_act)) + { + throw ExpException(1); + } + + if (typeid(*f_exp) == typeid(interface::af_packet_create_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(interface::loopback_create_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(interface::loopback_delete_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(interface::af_packet_delete_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(interface::state_change_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(interface::set_table_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(interface::set_mac_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(interface::set_tag)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(route_domain::create_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(route_domain::delete_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(route::ip_route::update_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(route::ip_route::delete_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(neighbour::create_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(neighbour::delete_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(l3_binding::bind_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(l3_binding::unbind_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(bridge_domain::create_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(bridge_domain::delete_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(bridge_domain_entry::create_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(bridge_domain_entry::delete_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(bridge_domain_arp_entry::create_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(bridge_domain_arp_entry::delete_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(l2_binding::bind_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(l2_binding::unbind_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(l2_binding::set_vtr_op_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(vxlan_tunnel::create_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(vxlan_tunnel::delete_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(sub_interface::create_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(sub_interface::delete_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(ACL::l3_list::update_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(ACL::l3_list::delete_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(ACL::l3_binding::bind_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(ACL::l3_binding::unbind_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(ACL::l2_list::update_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(ACL::l2_list::delete_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(ACL::l2_binding::bind_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(ACL::l2_binding::unbind_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(arp_proxy_binding::bind_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(arp_proxy_binding::unbind_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(arp_proxy_config::config_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(arp_proxy_config::unconfig_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(ip_unnumbered::config_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(ip_unnumbered::unconfig_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(ip6nd_ra_config::config_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(ip6nd_ra_config::unconfig_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(ip6nd_ra_prefix::config_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(ip6nd_ra_prefix::unconfig_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(interface_span::config_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(interface_span::unconfig_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(nat_static::create_44_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(nat_static::delete_44_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(nat_binding::bind_44_input_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(nat_binding::unbind_44_input_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else if (typeid(*f_exp) == typeid(interface::events_cmd)) + { + rc = handle_derived(f_exp, f_act); + } + else + { + throw ExpException(2); + } + + // if we get here then we found the match. + m_exp_queue.erase(it_exp); + m_act_queue.erase(it_act); + delete f_exp; + delete f_act; + + // return any injected failures to the agent + if (rc_t::OK != rc && rc_t::NOOP != rc) + { + return (rc); + } + + matched = true; + break; + } + catch (ExpException &e) + { + // The expected and actual do not match + if (m_strict_order) + { + // in strict ordering mode this is fatal, so rethrow + throw e; + } + else + { + // move the iterator onto the next in the expected list and + // check for a match + ++it_exp; + } + } + } + + if (!matched) + throw ExpException(3); + } + + return (rc); + } +private: + + template + rc_t handle_derived(const cmd *f_exp, cmd *f_act) + { + const T *i_exp; + T *i_act; + + i_exp = dynamic_cast(f_exp); + i_act = dynamic_cast(f_act); + if (!(*i_exp == *i_act)) + { + throw ExpException(4); + } + // pass the data and return code to the agent + i_act->item() = i_exp->item(); + + return (i_act->item().rc()); + } + + // The Q to push the expectations on + std::deque m_exp_queue; + + // the queue to push the actual events on + std::deque m_act_queue; + + // control whether the expected queue is strictly ordered. + bool m_strict_order; +}; + +class VppInit { +public: + std::string name; + MockCmdQ *f; + + VppInit() + : name("vpp-ut"), + f(new MockCmdQ()) + { + HW::init(f); + OM::init(); + logger().set(log_level_t::DEBUG); + } + + ~VppInit() { + delete f; + } +}; + +BOOST_AUTO_TEST_SUITE(VppOM_test) + +#define TRY_CHECK_RC(stmt) \ +{ \ + try { \ + BOOST_CHECK(rc_t::OK == stmt); \ + } \ + catch (ExpException &e) \ + { \ + BOOST_CHECK(false); \ + } \ + BOOST_CHECK(vi.f->is_empty()); \ +} + +#define TRY_CHECK(stmt) \ +{ \ + try { \ + stmt; \ + } \ + catch (ExpException &e) \ + { \ + BOOST_CHECK(false); \ + } \ + BOOST_CHECK(vi.f->is_empty()); \ +} + +#define ADD_EXPECT(stmt) \ + vi.f->expect(new stmt) + +#define STRICT_ORDER_OFF() \ + vi.f->strict_order(false) + +BOOST_AUTO_TEST_CASE(test_interface) { + VppInit vi; + const std::string go = "GeorgeOrwell"; + const std::string js = "JohnSteinbeck"; + rc_t rc = rc_t::OK; + + /* + * George creates and deletes the interface + */ + std::string itf1_name = "afpacket1"; + interface itf1(itf1_name, + interface::type_t::AFPACKET, + interface::admin_state_t::UP); + + /* + * set the expectation for a afpacket interface create. + * 2 is the interface handle VPP [mock] assigns + */ + HW::item hw_ifh(2, rc_t::OK); + ADD_EXPECT(interface::af_packet_create_cmd(hw_ifh, itf1_name)); + + HW::item hw_as_up(interface::admin_state_t::UP, rc_t::OK); + ADD_EXPECT(interface::state_change_cmd(hw_as_up, hw_ifh)); + + TRY_CHECK_RC(OM::write(go, itf1)); + + HW::item hw_as_down(interface::admin_state_t::DOWN, rc_t::OK); + ADD_EXPECT(interface::state_change_cmd(hw_as_down, hw_ifh)); + ADD_EXPECT(interface::af_packet_delete_cmd(hw_ifh, itf1_name)); + + TRY_CHECK(OM::remove(go)); + + /* + * George creates the interface, then John brings it down. + * George's remove is a no-op, sice John also owns the interface + */ + interface itf1b(itf1_name, + interface::type_t::AFPACKET, + interface::admin_state_t::DOWN); + + ADD_EXPECT(interface::af_packet_create_cmd(hw_ifh, itf1_name)); + ADD_EXPECT(interface::state_change_cmd(hw_as_up, hw_ifh)); + TRY_CHECK_RC(OM::write(go, itf1)); + + ADD_EXPECT(interface::state_change_cmd(hw_as_down, hw_ifh)); + TRY_CHECK_RC(OM::write(js, itf1b)); + + TRY_CHECK(OM::remove(go)); + + ADD_EXPECT(interface::af_packet_delete_cmd(hw_ifh, itf1_name)); + TRY_CHECK(OM::remove(js)); + + /* + * George adds an interface, then we flush all of Geroge's state + */ + ADD_EXPECT(interface::af_packet_create_cmd(hw_ifh, itf1_name)); + ADD_EXPECT(interface::state_change_cmd(hw_as_up, hw_ifh)); + TRY_CHECK_RC(OM::write(go, itf1)); + + TRY_CHECK(OM::mark(go)); + + ADD_EXPECT(interface::state_change_cmd(hw_as_down, hw_ifh)); + ADD_EXPECT(interface::af_packet_delete_cmd(hw_ifh, itf1_name)); + TRY_CHECK(OM::sweep(go)); + + /* + * George adds an interface. mark stale. update the same interface. sweep + * and expect no delete + */ + ADD_EXPECT(interface::af_packet_create_cmd(hw_ifh, itf1_name)); + ADD_EXPECT(interface::state_change_cmd(hw_as_down, hw_ifh)); + TRY_CHECK_RC(OM::write(go, itf1b)); + + TRY_CHECK(OM::mark(go)); + + ADD_EXPECT(interface::state_change_cmd(hw_as_up, hw_ifh)); + TRY_CHECK_RC(OM::write(go, itf1)); + + TRY_CHECK(OM::sweep(go)); + + ADD_EXPECT(interface::state_change_cmd(hw_as_down, hw_ifh)); + ADD_EXPECT(interface::af_packet_delete_cmd(hw_ifh, itf1_name)); + TRY_CHECK(OM::remove(go)); + + /* + * George adds an insterface, then we mark that state. Add a second interface + * an flush the first that is now stale. + */ + ADD_EXPECT(interface::af_packet_create_cmd(hw_ifh, itf1_name)); + ADD_EXPECT(interface::state_change_cmd(hw_as_up, hw_ifh)); + TRY_CHECK_RC(OM::write(go, itf1)); + + TRY_CHECK(OM::mark(go)); + + std::string itf2_name = "afpacket2"; + interface itf2(itf2_name, + interface::type_t::AFPACKET, + interface::admin_state_t::UP); + HW::item hw_ifh2(3, rc_t::OK); + + ADD_EXPECT(interface::af_packet_create_cmd(hw_ifh2, itf2_name)); + ADD_EXPECT(interface::state_change_cmd(hw_as_up, hw_ifh2)); + TRY_CHECK_RC(OM::write(go, itf2)); + + ADD_EXPECT(interface::state_change_cmd(hw_as_down, hw_ifh)); + ADD_EXPECT(interface::af_packet_delete_cmd(hw_ifh, itf1_name)); + TRY_CHECK(OM::sweep(go)); + + TRY_CHECK(OM::mark(go)); + + ADD_EXPECT(interface::state_change_cmd(hw_as_down, hw_ifh2)); + ADD_EXPECT(interface::af_packet_delete_cmd(hw_ifh2, itf2_name)); + TRY_CHECK(OM::sweep(go)); +} + +BOOST_AUTO_TEST_CASE(test_bvi) { + VppInit vi; + const std::string ernest = "ErnestHemmingway"; + const std::string graham = "GrahamGreene"; + rc_t rc = rc_t::OK; + l3_binding *l3; + + HW::item hw_as_up(interface::admin_state_t::UP, + rc_t::OK); + HW::item hw_as_down(interface::admin_state_t::DOWN, + rc_t::OK); + + /* + * Enrest creates a BVI with address 10.10.10.10/24 + */ + route::prefix_t pfx_10("10.10.10.10", 24); + + const std::string bvi_name = "bvi1"; + interface itf(bvi_name, + interface::type_t::BVI, + interface::admin_state_t::UP); + HW::item hw_ifh(4, rc_t::OK); + HW::item hw_pfx_10(pfx_10, rc_t::OK); + + ADD_EXPECT(interface::loopback_create_cmd(hw_ifh, bvi_name)); + ADD_EXPECT(interface::set_tag(hw_ifh, bvi_name)); + ADD_EXPECT(interface::state_change_cmd(hw_as_up, hw_ifh)); + TRY_CHECK_RC(OM::write(ernest, itf)); + + l3 = new l3_binding(itf, pfx_10); + HW::item hw_l3_bind(true, rc_t::OK); + HW::item hw_l3_unbind(false, rc_t::OK); + ADD_EXPECT(l3_binding::bind_cmd(hw_l3_bind, hw_ifh.data(), pfx_10)); + TRY_CHECK_RC(OM::write(ernest, *l3)); + + // change the MAC address on the BVI + interface itf_new_mac(bvi_name, + interface::type_t::BVI, + interface::admin_state_t::UP); + l2_address_t l2_addr({0,1,2,3,4,5}); + HW::item hw_mac(l2_addr, rc_t::OK); + itf_new_mac.set(l2_addr); + ADD_EXPECT(interface::set_mac_cmd(hw_mac, hw_ifh)); + TRY_CHECK_RC(OM::write(ernest, itf_new_mac)); + + // create/write the interface to the OM again but with an unset MAC + // this should not generate a MAC address update + TRY_CHECK_RC(OM::write(ernest, itf)); + + // change the MAC address on the BVI - again + interface itf_new_mac2(bvi_name, + interface::type_t::BVI, + interface::admin_state_t::UP); + l2_address_t l2_addr2({0,1,2,3,4,6}); + HW::item hw_mac2(l2_addr2, rc_t::OK); + itf_new_mac2.set(l2_addr2); + ADD_EXPECT(interface::set_mac_cmd(hw_mac2, hw_ifh)); + TRY_CHECK_RC(OM::write(ernest, itf_new_mac2)); + + delete l3; + ADD_EXPECT(l3_binding::unbind_cmd(hw_l3_unbind, hw_ifh.data(), pfx_10)); + ADD_EXPECT(interface::state_change_cmd(hw_as_down, hw_ifh)); + ADD_EXPECT(interface::loopback_delete_cmd(hw_ifh)); + TRY_CHECK(OM::remove(ernest)); + + /* + * Graham creates a BVI with address 10.10.10.10/24 in Routing Domain + */ + route_domain rd(1); + HW::item hw_rd4_create(true, rc_t::OK); + HW::item hw_rd4_delete(false, rc_t::OK); + HW::item hw_rd6_create(true, rc_t::OK); + HW::item hw_rd6_delete(false, rc_t::OK); + HW::item hw_rd4_bind(1, rc_t::OK); + HW::item hw_rd4_unbind(route::DEFAULT_TABLE, rc_t::OK); + HW::item hw_rd6_bind(1, rc_t::OK); + HW::item hw_rd6_unbind(route::DEFAULT_TABLE, rc_t::OK); + ADD_EXPECT(route_domain::create_cmd(hw_rd4_create, l3_proto_t::IPV4, 1)); + ADD_EXPECT(route_domain::create_cmd(hw_rd6_create, l3_proto_t::IPV6, 1)); + TRY_CHECK_RC(OM::write(graham, rd)); + + const std::string bvi2_name = "bvi2"; + interface *itf2 = new interface(bvi2_name, + interface::type_t::BVI, + interface::admin_state_t::UP, + rd); + HW::item hw_ifh2(5, rc_t::OK); + + ADD_EXPECT(interface::loopback_create_cmd(hw_ifh2, bvi2_name)); + ADD_EXPECT(interface::set_tag(hw_ifh2, bvi2_name)); + ADD_EXPECT(interface::state_change_cmd(hw_as_up, hw_ifh2)); + ADD_EXPECT(interface::set_table_cmd(hw_rd4_bind, l3_proto_t::IPV4, hw_ifh2)); + ADD_EXPECT(interface::set_table_cmd(hw_rd6_bind, l3_proto_t::IPV6, hw_ifh2)); + + TRY_CHECK_RC(OM::write(graham, *itf2)); + + l3 = new l3_binding(*itf2, pfx_10); + ADD_EXPECT(l3_binding::bind_cmd(hw_l3_bind, hw_ifh2.data(), pfx_10)); + TRY_CHECK_RC(OM::write(graham, *l3)); + + delete l3; + delete itf2; + + ADD_EXPECT(l3_binding::unbind_cmd(hw_l3_unbind, hw_ifh2.data(), pfx_10)); + ADD_EXPECT(interface::set_table_cmd(hw_rd4_unbind, l3_proto_t::IPV4, hw_ifh2)); + ADD_EXPECT(interface::set_table_cmd(hw_rd6_unbind, l3_proto_t::IPV6, hw_ifh2)); + ADD_EXPECT(interface::state_change_cmd(hw_as_down, hw_ifh2)); + ADD_EXPECT(interface::loopback_delete_cmd(hw_ifh2)); + ADD_EXPECT(route_domain::delete_cmd(hw_rd4_delete, l3_proto_t::IPV4, 1)); + ADD_EXPECT(route_domain::delete_cmd(hw_rd6_delete, l3_proto_t::IPV6, 1)); + TRY_CHECK(OM::remove(graham)); +} + +BOOST_AUTO_TEST_CASE(test_bridge) { + VppInit vi; + const std::string franz = "FranzKafka"; + const std::string dante = "Dante"; + rc_t rc = rc_t::OK; + + /* + * Franz creates an interface, Bridge-domain, then binds the two + */ + + // interface create + std::string itf1_name = "afpacket1"; + interface itf1(itf1_name, + interface::type_t::AFPACKET, + interface::admin_state_t::UP); + + HW::item hw_ifh(3, rc_t::OK); + HW::item hw_as_up(interface::admin_state_t::UP, + rc_t::OK); + ADD_EXPECT(interface::af_packet_create_cmd(hw_ifh, itf1_name)); + ADD_EXPECT(interface::state_change_cmd(hw_as_up, hw_ifh)); + + TRY_CHECK_RC(OM::write(franz, itf1)); + + // bridge-domain create + bridge_domain bd1(33); + + HW::item hw_bd(33, rc_t::OK); + ADD_EXPECT(bridge_domain::create_cmd(hw_bd)); + + TRY_CHECK_RC(OM::write(franz, bd1)); + + // L2-interface create and bind + // this needs to be delete'd before the flush below, since it too maintains + // references to the BD and Interface + l2_binding *l2itf = new l2_binding(itf1, bd1); + HW::item hw_l2_bind(true, rc_t::OK); + + ADD_EXPECT(l2_binding::bind_cmd(hw_l2_bind, hw_ifh.data(), hw_bd.data(), false)); + TRY_CHECK_RC(OM::write(franz, *l2itf)); + + /* + * Dante adds an interface to the same BD + */ + std::string itf2_name = "afpacket2"; + interface itf2(itf2_name, + interface::type_t::AFPACKET, + interface::admin_state_t::UP); + + HW::item hw_ifh2(4, rc_t::OK); + ADD_EXPECT(interface::af_packet_create_cmd(hw_ifh2, itf2_name)); + ADD_EXPECT(interface::state_change_cmd(hw_as_up, hw_ifh2)); + TRY_CHECK_RC(OM::write(dante, itf2)); + + // BD add is a no-op since it exists + TRY_CHECK_RC(OM::write(dante, bd1)); + + l2_binding *l2itf2 = new l2_binding(itf2, bd1); + HW::item hw_set_vtr(l2_binding::l2_vtr_op_t::L2_VTR_POP_1, rc_t::OK); + l2itf2->set(l2_binding::l2_vtr_op_t::L2_VTR_POP_1, 68); + + ADD_EXPECT(l2_binding::bind_cmd(hw_l2_bind, hw_ifh2.data(), hw_bd.data(), false)); + ADD_EXPECT(l2_binding::set_vtr_op_cmd(hw_set_vtr, hw_ifh2.data(), 68)); + TRY_CHECK_RC(OM::write(dante, *l2itf2)); + + // Add some static entries to the bridge-domain + HW::item hw_be1(true, rc_t::OK); + mac_address_t mac1({0,1,2,3,4,5}); + bridge_domain_entry *be1 = new bridge_domain_entry(bd1, mac1, itf2); + ADD_EXPECT(bridge_domain_entry::create_cmd(hw_be1, mac1, bd1.id(), hw_ifh2.data())); + TRY_CHECK_RC(OM::write(dante, *be1)); + + // Add some entries to the bridge-domain ARP termination table + HW::item hw_bea1(true, rc_t::OK); + boost::asio::ip::address ip1 = boost::asio::ip::address::from_string("10.10.10.10"); + + bridge_domain_arp_entry *bea1 = new bridge_domain_arp_entry(bd1, mac1, ip1); + ADD_EXPECT(bridge_domain_arp_entry::create_cmd(hw_be1, bd1.id(), mac1, ip1)); + TRY_CHECK_RC(OM::write(dante, *bea1)); + + // flush Franz's state + delete l2itf; + HW::item hw_as_down(interface::admin_state_t::DOWN, + rc_t::OK); + ADD_EXPECT(l2_binding::unbind_cmd(hw_l2_bind, hw_ifh.data(), hw_bd.data(), false)); + ADD_EXPECT(interface::state_change_cmd(hw_as_down, hw_ifh)); + ADD_EXPECT(interface::af_packet_delete_cmd(hw_ifh, itf1_name)); + TRY_CHECK(OM::remove(franz)); + + // flush Dante's state - the order the interface and BD are deleted + // is an uncontrollable artifact of the C++ object destruction. + delete l2itf2; + delete be1; + delete bea1; + STRICT_ORDER_OFF(); + ADD_EXPECT(l2_binding::unbind_cmd(hw_l2_bind, hw_ifh2.data(), hw_bd.data(), false)); + ADD_EXPECT(interface::state_change_cmd(hw_as_down, hw_ifh2)); + ADD_EXPECT(interface::af_packet_delete_cmd(hw_ifh2, itf2_name)); + ADD_EXPECT(bridge_domain_entry::delete_cmd(hw_be1, mac1, bd1.id())); + ADD_EXPECT(bridge_domain_arp_entry::delete_cmd(hw_be1, bd1.id(), mac1, ip1)); + ADD_EXPECT(bridge_domain::delete_cmd(hw_bd)); + TRY_CHECK(OM::remove(dante)); +} + +BOOST_AUTO_TEST_CASE(test_vxlan) { + VppInit vi; + const std::string franz = "FranzKafka"; + rc_t rc = rc_t::OK; + + /* + * Franz creates an interface, Bridge-domain, then binds the two + */ + + // VXLAN create + vxlan_tunnel::endpoint_t ep(boost::asio::ip::address::from_string("10.10.10.10"), + boost::asio::ip::address::from_string("10.10.10.11"), + 322); + + vxlan_tunnel vxt(ep.src, ep.dst, ep.vni); + + HW::item hw_vxt(3, rc_t::OK); + ADD_EXPECT(vxlan_tunnel::create_cmd(hw_vxt, "don't-care", ep)); + + TRY_CHECK_RC(OM::write(franz, vxt)); + + // bridge-domain create + bridge_domain bd1(33); + + HW::item hw_bd(33, rc_t::OK); + ADD_EXPECT(bridge_domain::create_cmd(hw_bd)); + + TRY_CHECK_RC(OM::write(franz, bd1)); + + // L2-interface create and bind + // this needs to be delete'd before the flush below, since it too maintains + // references to the BD and Interface + l2_binding *l2itf = new l2_binding(vxt, bd1); + HW::item hw_l2_bind(true, rc_t::OK); + + ADD_EXPECT(l2_binding::bind_cmd(hw_l2_bind, hw_vxt.data(), hw_bd.data(), false)); + TRY_CHECK_RC(OM::write(franz, *l2itf)); + + // flush Franz's state + delete l2itf; + HW::item hw_vxtdel(3, rc_t::NOOP); + STRICT_ORDER_OFF(); + ADD_EXPECT(l2_binding::unbind_cmd(hw_l2_bind, hw_vxt.data(), hw_bd.data(), false)); + ADD_EXPECT(bridge_domain::delete_cmd(hw_bd)); + ADD_EXPECT(vxlan_tunnel::delete_cmd(hw_vxtdel, ep)); + TRY_CHECK(OM::remove(franz)); +} + +BOOST_AUTO_TEST_CASE(test_vlan) { + VppInit vi; + const std::string noam = "NoamChomsky"; + rc_t rc = rc_t::OK; + + std::string itf1_name = "host1"; + interface itf1(itf1_name, + interface::type_t::AFPACKET, + interface::admin_state_t::UP); + + HW::item hw_ifh(2, rc_t::OK); + ADD_EXPECT(interface::af_packet_create_cmd(hw_ifh, itf1_name)); + + HW::item hw_as_up(interface::admin_state_t::UP, rc_t::OK); + ADD_EXPECT(interface::state_change_cmd(hw_as_up, hw_ifh)); + + TRY_CHECK_RC(OM::write(noam, itf1)); + + sub_interface *vl33 = new sub_interface(itf1, + interface::admin_state_t::UP, + 33); + + HW::item hw_vl33(3, rc_t::OK); + ADD_EXPECT(sub_interface::create_cmd(hw_vl33, itf1_name+".33", hw_ifh.data(), 33)); + ADD_EXPECT(interface::state_change_cmd(hw_as_up, hw_vl33)); + + TRY_CHECK_RC(OM::write(noam, *vl33)); + + delete vl33; + HW::item hw_as_down(interface::admin_state_t::DOWN, rc_t::OK); + HW::item hw_vl33_down(3, rc_t::NOOP); + ADD_EXPECT(interface::state_change_cmd(hw_as_down, hw_vl33)); + ADD_EXPECT(sub_interface::delete_cmd(hw_vl33_down)); + ADD_EXPECT(interface::state_change_cmd(hw_as_down, hw_ifh)); + ADD_EXPECT(interface::af_packet_delete_cmd(hw_ifh, itf1_name)); + + TRY_CHECK(OM::remove(noam)); +} + +BOOST_AUTO_TEST_CASE(test_acl) { + VppInit vi; + const std::string fyodor = "FyodorDostoyevsky"; + const std::string leo = "LeoTolstoy"; + rc_t rc = rc_t::OK; + + /* + * Fyodor adds an ACL in the input direction + */ + std::string itf1_name = "host1"; + interface itf1(itf1_name, + interface::type_t::AFPACKET, + interface::admin_state_t::UP); + HW::item hw_ifh(2, rc_t::OK); + HW::item hw_as_up(interface::admin_state_t::UP, rc_t::OK); + ADD_EXPECT(interface::af_packet_create_cmd(hw_ifh, itf1_name)); + ADD_EXPECT(interface::state_change_cmd(hw_as_up, hw_ifh)); + TRY_CHECK_RC(OM::write(fyodor, itf1)); + + route::prefix_t src("10.10.10.10", 32); + ACL::l3_rule r1(10, ACL::action_t::PERMIT, src, route::prefix_t::ZERO); + ACL::l3_rule r2(20, ACL::action_t::DENY, route::prefix_t::ZERO, route::prefix_t::ZERO); + + std::string acl_name = "acl1"; + ACL::l3_list acl1(acl_name); + acl1.insert(r2); + acl1.insert(r1); + ACL::l3_list::rules_t rules = {r1, r2}; + + HW::item hw_acl(2, rc_t::OK); + ADD_EXPECT(ACL::l3_list::update_cmd(hw_acl, acl_name, rules)); + TRY_CHECK_RC(OM::write(fyodor, acl1)); + + ACL::l3_binding *l3b = new ACL::l3_binding(direction_t::INPUT, itf1, acl1); + HW::item hw_binding(true, rc_t::OK); + ADD_EXPECT(ACL::l3_binding::bind_cmd(hw_binding, direction_t::INPUT, + hw_ifh.data(), hw_acl.data())); + TRY_CHECK_RC(OM::write(fyodor, *l3b)); + + /** + * Leo adds an L2 ACL in the output direction + */ + TRY_CHECK_RC(OM::write(leo, itf1)); + + std::string l2_acl_name = "l2_acl1"; + mac_address_t mac({0x0, 0x0, 0x1, 0x2, 0x3, 0x4}); + mac_address_t mac_mask({0xff, 0xff, 0xff, 0x0, 0x0, 0x0}); + ACL::l2_rule l2_r1(10, ACL::action_t::PERMIT, src, mac, mac_mask); + ACL::l2_rule l2_r2(20, ACL::action_t::DENY, src, {}, {}); + + ACL::l2_list l2_acl(l2_acl_name); + l2_acl.insert(l2_r2); + l2_acl.insert(l2_r1); + + ACL::l2_list::rules_t l2_rules = {l2_r1, l2_r2}; + + HW::item l2_hw_acl(3, rc_t::OK); + ADD_EXPECT(ACL::l2_list::update_cmd(l2_hw_acl, l2_acl_name, l2_rules)); + TRY_CHECK_RC(OM::write(leo, l2_acl)); + + ACL::l2_binding *l2b = new ACL::l2_binding(direction_t::OUTPUT, itf1, l2_acl); + HW::item l2_hw_binding(true, rc_t::OK); + ADD_EXPECT(ACL::l2_binding::bind_cmd(l2_hw_binding, direction_t::OUTPUT, + hw_ifh.data(), l2_hw_acl.data())); + TRY_CHECK_RC(OM::write(leo, *l2b)); + + delete l2b; + ADD_EXPECT(ACL::l2_binding::unbind_cmd(l2_hw_binding, direction_t::OUTPUT, + hw_ifh.data(), l2_hw_acl.data())); + ADD_EXPECT(ACL::l2_list::delete_cmd(l2_hw_acl)); + TRY_CHECK(OM::remove(leo)); + + delete l3b; + HW::item hw_as_down(interface::admin_state_t::DOWN, + rc_t::OK); + STRICT_ORDER_OFF(); + ADD_EXPECT(ACL::l3_binding::unbind_cmd(hw_binding, direction_t::INPUT, + hw_ifh.data(), hw_acl.data())); + ADD_EXPECT(ACL::l3_list::delete_cmd(hw_acl)); + ADD_EXPECT(interface::state_change_cmd(hw_as_down, hw_ifh)); + ADD_EXPECT(interface::af_packet_delete_cmd(hw_ifh, itf1_name)); + + TRY_CHECK(OM::remove(fyodor)); +} + +BOOST_AUTO_TEST_CASE(test_arp_proxy) { + VppInit vi; + const std::string kurt = "KurtVonnegut"; + rc_t rc = rc_t::OK; + + asio::ip::address_v4 low = asio::ip::address_v4::from_string("10.0.0.0"); + asio::ip::address_v4 high = asio::ip::address_v4::from_string("10.0.0.255"); + + arp_proxy_config ap(low, high); + HW::item hw_ap_cfg(true, rc_t::OK); + ADD_EXPECT(arp_proxy_config::config_cmd(hw_ap_cfg, low, high)); + TRY_CHECK_RC(OM::write(kurt, ap)); + + std::string itf3_name = "host3"; + interface itf3(itf3_name, + interface::type_t::AFPACKET, + interface::admin_state_t::UP); + HW::item hw_ifh(2, rc_t::OK); + HW::item hw_as_up(interface::admin_state_t::UP, rc_t::OK); + ADD_EXPECT(interface::af_packet_create_cmd(hw_ifh, itf3_name)); + ADD_EXPECT(interface::state_change_cmd(hw_as_up, hw_ifh)); + TRY_CHECK_RC(OM::write(kurt, itf3)); + + arp_proxy_binding *apb = new arp_proxy_binding(itf3, ap); + HW::item hw_binding(true, rc_t::OK); + ADD_EXPECT(arp_proxy_binding::bind_cmd(hw_binding, hw_ifh.data())); + TRY_CHECK_RC(OM::write(kurt, *apb)); + + delete apb; + + HW::item hw_as_down(interface::admin_state_t::DOWN, + rc_t::OK); + STRICT_ORDER_OFF(); + ADD_EXPECT(arp_proxy_binding::unbind_cmd(hw_binding, hw_ifh.data())); + ADD_EXPECT(interface::state_change_cmd(hw_as_down, hw_ifh)); + ADD_EXPECT(interface::af_packet_delete_cmd(hw_ifh, itf3_name)); + ADD_EXPECT(arp_proxy_config::unconfig_cmd(hw_ap_cfg, low, high)); + + TRY_CHECK(OM::remove(kurt)); +} + +BOOST_AUTO_TEST_CASE(test_ip_unnumbered) { + VppInit vi; + const std::string eric = "EricAmbler"; + rc_t rc = rc_t::OK; + + /* + * Interface 1 has the L3 address + */ + std::string itf1_name = "host1"; + interface itf1(itf1_name, + interface::type_t::AFPACKET, + interface::admin_state_t::UP); + HW::item hw_ifh(2, rc_t::OK); + HW::item hw_as_up(interface::admin_state_t::UP, rc_t::OK); + ADD_EXPECT(interface::af_packet_create_cmd(hw_ifh, itf1_name)); + ADD_EXPECT(interface::state_change_cmd(hw_as_up, hw_ifh)); + TRY_CHECK_RC(OM::write(eric, itf1)); + + route::prefix_t pfx_10("10.10.10.10", 24); + l3_binding *l3 = new l3_binding(itf1, pfx_10); + HW::item hw_l3_bind(true, rc_t::OK); + HW::item hw_l3_unbind(false, rc_t::OK); + ADD_EXPECT(l3_binding::bind_cmd(hw_l3_bind, hw_ifh.data(), pfx_10)); + TRY_CHECK_RC(OM::write(eric, *l3)); + + /* + * Interface 2 is unnumbered + */ + std::string itf2_name = "host2"; + interface itf2(itf2_name, + interface::type_t::AFPACKET, + interface::admin_state_t::UP); + + HW::item hw_ifh2(4, rc_t::OK); + ADD_EXPECT(interface::af_packet_create_cmd(hw_ifh2, itf2_name)); + ADD_EXPECT(interface::state_change_cmd(hw_as_up, hw_ifh2)); + TRY_CHECK_RC(OM::write(eric, itf2)); + + ip_unnumbered *ipun = new ip_unnumbered(itf2, itf1); + HW::item hw_ip_cfg(true, rc_t::OK); + HW::item hw_ip_uncfg(false, rc_t::OK); + ADD_EXPECT(ip_unnumbered::config_cmd(hw_ip_cfg, hw_ifh2.data(), hw_ifh.data())); + TRY_CHECK_RC(OM::write(eric, *ipun)); + + delete l3; + delete ipun; + + HW::item hw_as_down(interface::admin_state_t::DOWN, rc_t::OK); + STRICT_ORDER_OFF(); + ADD_EXPECT(ip_unnumbered::unconfig_cmd(hw_ip_uncfg, hw_ifh2.data(), hw_ifh.data())); + ADD_EXPECT(l3_binding::unbind_cmd(hw_l3_unbind, hw_ifh.data(), pfx_10)); + ADD_EXPECT(interface::state_change_cmd(hw_as_down, hw_ifh2)); + ADD_EXPECT(interface::af_packet_delete_cmd(hw_ifh2, itf2_name)); + ADD_EXPECT(interface::state_change_cmd(hw_as_down, hw_ifh)); + ADD_EXPECT(interface::af_packet_delete_cmd(hw_ifh, itf1_name)); + + TRY_CHECK(OM::remove(eric)); +} + +BOOST_AUTO_TEST_CASE(test_ip6nd) { + VppInit vi; + const std::string paulo = "PauloCoelho"; + rc_t rc = rc_t::OK; + + /* + * ra config + */ + std::string itf_name = "host_ip6nd"; + interface itf(itf_name, + interface::type_t::AFPACKET, + interface::admin_state_t::UP); + HW::item hw_ifh(3, rc_t::OK); + HW::item hw_as_up(interface::admin_state_t::UP, rc_t::OK); + ADD_EXPECT(interface::af_packet_create_cmd(hw_ifh, itf_name)); + ADD_EXPECT(interface::state_change_cmd(hw_as_up, hw_ifh)); + TRY_CHECK_RC(OM::write(paulo, itf)); + + route::prefix_t pfx_10("fd8f:69d8:c12c:ca62::3", 128); + l3_binding *l3 = new l3_binding(itf, pfx_10); + HW::item hw_l3_bind(true, rc_t::OK); + HW::item hw_l3_unbind(false, rc_t::OK); + ADD_EXPECT(l3_binding::bind_cmd(hw_l3_bind, hw_ifh.data(), pfx_10)); + TRY_CHECK_RC(OM::write(paulo, *l3)); + + ra_config ra(0, 1, 0, 4); + ip6nd_ra_config *ip6ra = new ip6nd_ra_config(itf, ra); + HW::item hw_ip6nd_ra_config_config(true, rc_t::OK); + HW::item hw_ip6nd_ra_config_unconfig(false, rc_t::OK); + ADD_EXPECT(ip6nd_ra_config::config_cmd(hw_ip6nd_ra_config_config, hw_ifh.data(), ra)); + TRY_CHECK_RC(OM::write(paulo, *ip6ra)); + + /* + * ra prefix + */ + ra_prefix ra_pfx(pfx_10, 0, 0, 2592000, 604800); + ip6nd_ra_prefix *ip6pfx = new ip6nd_ra_prefix(itf, ra_pfx); + HW::item hw_ip6nd_ra_prefix_config(true, rc_t::OK); + HW::item hw_ip6nd_ra_prefix_unconfig(false, rc_t::OK); + ADD_EXPECT(ip6nd_ra_prefix::config_cmd(hw_ip6nd_ra_prefix_config, hw_ifh.data(), ra_pfx)); + TRY_CHECK_RC(OM::write(paulo, *ip6pfx)); + + delete ip6pfx; + + ADD_EXPECT(ip6nd_ra_prefix::unconfig_cmd(hw_ip6nd_ra_prefix_unconfig, hw_ifh.data(), ra_pfx)); + + delete ip6ra; + delete l3; + + HW::item hw_as_down(interface::admin_state_t::DOWN, rc_t::OK); + + STRICT_ORDER_OFF(); + ADD_EXPECT(ip6nd_ra_config::unconfig_cmd(hw_ip6nd_ra_config_unconfig, hw_ifh.data(), ra)); + ADD_EXPECT(l3_binding::unbind_cmd(hw_l3_unbind, hw_ifh.data(), pfx_10)); + ADD_EXPECT(interface::state_change_cmd(hw_as_down, hw_ifh)); + ADD_EXPECT(interface::af_packet_delete_cmd(hw_ifh, itf_name)); + + TRY_CHECK(OM::remove(paulo)); +} + +BOOST_AUTO_TEST_CASE(test_interface_span) { + VppInit vi; + const std::string elif = "ElifShafak"; + rc_t rc = rc_t::OK; + + /* + * Interface 1 to be mirrored + */ + std::string itf1_name = "port-from"; + interface itf1(itf1_name, + interface::type_t::AFPACKET, + interface::admin_state_t::UP); + HW::item hw_ifh(2, rc_t::OK); + HW::item hw_as_up(interface::admin_state_t::UP, rc_t::OK); + ADD_EXPECT(interface::af_packet_create_cmd(hw_ifh, itf1_name)); + ADD_EXPECT(interface::state_change_cmd(hw_as_up, hw_ifh)); + TRY_CHECK_RC(OM::write(elif, itf1)); + + /* + * Interface 2 where traffic is mirrored + */ + std::string itf2_name = "port-to"; + interface itf2(itf2_name, + interface::type_t::AFPACKET, + interface::admin_state_t::UP); + + HW::item hw_ifh2(4, rc_t::OK); + HW::item hw_as_up2(interface::admin_state_t::UP, rc_t::OK); + + ADD_EXPECT(interface::af_packet_create_cmd(hw_ifh2, itf2_name)); + ADD_EXPECT(interface::state_change_cmd(hw_as_up2, hw_ifh2)); + TRY_CHECK_RC(OM::write(elif, itf2)); + + interface_span *itf_span = new interface_span(itf1, itf2, interface_span::state_t::TX_RX_ENABLED); + HW::item hw_is_cfg(true, rc_t::OK); + HW::item hw_is_uncfg(true, rc_t::OK); + ADD_EXPECT(interface_span::config_cmd(hw_is_cfg, hw_ifh.data(), hw_ifh2.data(), interface_span::state_t::TX_RX_ENABLED)); + TRY_CHECK_RC(OM::write(elif, *itf_span)); + + HW::item hw_as_down(interface::admin_state_t::DOWN, rc_t::OK); + HW::item hw_as_down2(interface::admin_state_t::DOWN, rc_t::OK); + + delete itf_span; + STRICT_ORDER_OFF(); + ADD_EXPECT(interface_span::unconfig_cmd(hw_is_uncfg, hw_ifh.data(), hw_ifh2.data())); + ADD_EXPECT(interface::state_change_cmd(hw_as_down, hw_ifh)); + ADD_EXPECT(interface::af_packet_delete_cmd(hw_ifh, itf1_name)); + ADD_EXPECT(interface::state_change_cmd(hw_as_down2, hw_ifh2)); + ADD_EXPECT(interface::af_packet_delete_cmd(hw_ifh2, itf2_name)); + + TRY_CHECK(OM::remove(elif)); +} + +BOOST_AUTO_TEST_CASE(test_routing) { + VppInit vi; + const std::string ian = "IanFleming"; + rc_t rc = rc_t::OK; + + /* + * non-default route domain + */ + route_domain rd4(1); + HW::item hw_rd4_create(true, rc_t::OK); + HW::item hw_rd4_delete(false, rc_t::OK); + HW::item hw_rd6_create(true, rc_t::OK); + HW::item hw_rd6_delete(false, rc_t::OK); + HW::item hw_rd4_bind(1, rc_t::OK); + HW::item hw_rd4_unbind(route::DEFAULT_TABLE, rc_t::OK); + HW::item hw_rd6_bind(1, rc_t::OK); + HW::item hw_rd7_unbind(route::DEFAULT_TABLE, rc_t::OK); + ADD_EXPECT(route_domain::create_cmd(hw_rd4_create, l3_proto_t::IPV4, 1)); + ADD_EXPECT(route_domain::create_cmd(hw_rd6_create, l3_proto_t::IPV6, 1)); + TRY_CHECK_RC(OM::write(ian, rd4)); + + /* + * a couple of interfaces + */ + std::string itf1_name = "af1"; + interface itf1(itf1_name, + interface::type_t::AFPACKET, + interface::admin_state_t::UP); + HW::item hw_ifh(2, rc_t::OK); + HW::item hw_as_up(interface::admin_state_t::UP, rc_t::OK); + HW::item hw_as_down(interface::admin_state_t::DOWN, rc_t::OK); + ADD_EXPECT(interface::af_packet_create_cmd(hw_ifh, itf1_name)); + ADD_EXPECT(interface::state_change_cmd(hw_as_up, hw_ifh)); + TRY_CHECK_RC(OM::write(ian, itf1)); + + std::string itf2_name = "af2"; + interface *itf2 = new interface(itf2_name, + interface::type_t::AFPACKET, + interface::admin_state_t::UP, + rd4); + + HW::item hw_ifh2(4, rc_t::OK); + HW::item hw_as_up2(interface::admin_state_t::UP, rc_t::OK); + HW::item hw_as_down2(interface::admin_state_t::DOWN, rc_t::OK); + ADD_EXPECT(interface::af_packet_create_cmd(hw_ifh2, itf2_name)); + ADD_EXPECT(interface::state_change_cmd(hw_as_up2, hw_ifh2)); + ADD_EXPECT(interface::set_table_cmd(hw_rd4_bind, l3_proto_t::IPV4, hw_ifh2)); + ADD_EXPECT(interface::set_table_cmd(hw_rd6_bind, l3_proto_t::IPV6, hw_ifh2)); + TRY_CHECK_RC(OM::write(ian, *itf2)); + + /* + * prefix on each interface + */ + route::prefix_t pfx_10("10.10.10.10", 24); + l3_binding *l3_10 = new l3_binding(itf1, pfx_10); + HW::item hw_l3_10_bind(true, rc_t::OK); + HW::item hw_l3_10_unbind(false, rc_t::OK); + ADD_EXPECT(l3_binding::bind_cmd(hw_l3_10_bind, hw_ifh.data(), pfx_10)); + TRY_CHECK_RC(OM::write(ian, *l3_10)); + route::prefix_t pfx_11("11.11.11.11", 24); + l3_binding *l3_11 = new l3_binding(*itf2, pfx_11); + HW::item hw_l3_11_bind(true, rc_t::OK); + HW::item hw_l3_11_unbind(false, rc_t::OK); + ADD_EXPECT(l3_binding::bind_cmd(hw_l3_11_bind, hw_ifh2.data(), pfx_11)); + TRY_CHECK_RC(OM::write(ian, *l3_11)); + + /* + * A route via interface 1 in the default table + */ + route::prefix_t pfx_5("5.5.5.5", 32); + boost::asio::ip::address nh_10 = boost::asio::ip::address::from_string("10.10.10.11"); + route::path *path_10 = new route::path(nh_10, itf1); + route::ip_route *route_5 = new route::ip_route(pfx_5); + route_5->add(*path_10); + HW::item hw_route_5(true, rc_t::OK); + ADD_EXPECT(route::ip_route::update_cmd(hw_route_5, 0, pfx_5, {*path_10})); + TRY_CHECK_RC(OM::write(ian, *route_5)); + + /* + * A route via interface 2 in the non-default table + */ + boost::asio::ip::address nh_11 = boost::asio::ip::address::from_string("11.11.11.10"); + route::path *path_11 = new route::path(nh_11, *itf2); + route::ip_route *route_5_2 = new route::ip_route(rd4, pfx_5); + route_5_2->add(*path_11); + HW::item hw_route_5_2(true, rc_t::OK); + ADD_EXPECT(route::ip_route::update_cmd(hw_route_5_2, 1, pfx_5, {*path_11})); + TRY_CHECK_RC(OM::write(ian, *route_5_2)); + + /* + * An ARP entry for the neighbour on itf1 + */ + HW::item hw_neighbour(true, rc_t::OK); + mac_address_t mac_n({0,1,2,4,5,6}); + neighbour *ne = new neighbour(itf1, mac_n, nh_10); + ADD_EXPECT(neighbour::create_cmd(hw_neighbour, hw_ifh.data(), mac_n, nh_10)); + TRY_CHECK_RC(OM::write(ian, *ne)); + + /* + * A DVR route + */ + route::prefix_t pfx_6("6.6.6.6", 32); + route::path *path_l2 = new route::path(*itf2, nh_proto_t::ETHERNET); + route::ip_route *route_dvr = new route::ip_route(pfx_6); + route_dvr->add(*path_l2); + HW::item hw_route_dvr(true, rc_t::OK); + ADD_EXPECT(route::ip_route::update_cmd(hw_route_dvr, 0, pfx_6, {*path_l2})); + TRY_CHECK_RC(OM::write(ian, *route_dvr)); + + STRICT_ORDER_OFF(); + // delete the stack objects that hold references to others + // so the OM::remove is the call that removes the last reference + delete l3_11; + delete l3_10; + delete itf2; + delete route_5; + delete path_10; + delete route_5_2; + delete path_11; + delete route_dvr; + delete path_l2; + delete ne; + ADD_EXPECT(neighbour::delete_cmd(hw_neighbour, hw_ifh.data(), mac_n, nh_10)); + ADD_EXPECT(route::ip_route::delete_cmd(hw_route_dvr, 0, pfx_6)); + ADD_EXPECT(route::ip_route::delete_cmd(hw_route_5_2, 1, pfx_5)); + ADD_EXPECT(route::ip_route::delete_cmd(hw_route_5, 0, pfx_5)); + ADD_EXPECT(l3_binding::unbind_cmd(hw_l3_10_unbind, hw_ifh.data(), pfx_10)); + ADD_EXPECT(l3_binding::unbind_cmd(hw_l3_11_unbind, hw_ifh2.data(), pfx_11)); + ADD_EXPECT(interface::state_change_cmd(hw_as_down, hw_ifh)); + ADD_EXPECT(interface::af_packet_delete_cmd(hw_ifh, itf1_name)); + ADD_EXPECT(interface::set_table_cmd(hw_rd4_unbind, l3_proto_t::IPV4, hw_ifh2)); + ADD_EXPECT(interface::set_table_cmd(hw_rd4_unbind, l3_proto_t::IPV6, hw_ifh2)); + ADD_EXPECT(interface::state_change_cmd(hw_as_down2, hw_ifh2)); + ADD_EXPECT(interface::af_packet_delete_cmd(hw_ifh2, itf2_name)); + ADD_EXPECT(route_domain::delete_cmd(hw_rd4_delete, l3_proto_t::IPV4, 1)); + ADD_EXPECT(route_domain::delete_cmd(hw_rd6_delete, l3_proto_t::IPV6, 1)); + + TRY_CHECK(OM::remove(ian)); +} + +BOOST_AUTO_TEST_CASE(test_nat) { + VppInit vi; + const std::string gs = "GeorgeSimenon"; + rc_t rc = rc_t::OK; + + /* + * Inside Interface + */ + std::string itf_in_name = "inside"; + interface itf_in(itf_in_name, + interface::type_t::AFPACKET, + interface::admin_state_t::UP); + HW::item hw_ifh(2, rc_t::OK); + HW::item hw_as_up(interface::admin_state_t::UP, rc_t::OK); + HW::item hw_as_down(interface::admin_state_t::DOWN, rc_t::OK); + ADD_EXPECT(interface::af_packet_create_cmd(hw_ifh, itf_in_name)); + ADD_EXPECT(interface::state_change_cmd(hw_as_up, hw_ifh)); + TRY_CHECK_RC(OM::write(gs, itf_in)); + + /* + * outside + */ + std::string itf_out_name = "port-to"; + interface itf_out(itf_out_name, + interface::type_t::AFPACKET, + interface::admin_state_t::UP); + + HW::item hw_ifh2(4, rc_t::OK); + HW::item hw_as_up2(interface::admin_state_t::UP, rc_t::OK); + HW::item hw_as_down2(interface::admin_state_t::DOWN, rc_t::OK); + + ADD_EXPECT(interface::af_packet_create_cmd(hw_ifh2, itf_out_name)); + ADD_EXPECT(interface::state_change_cmd(hw_as_up2, hw_ifh2)); + TRY_CHECK_RC(OM::write(gs, itf_out)); + + /* + * A NAT static mapping + */ + boost::asio::ip::address in_addr = boost::asio::ip::address::from_string("10.0.0.1"); + boost::asio::ip::address_v4 out_addr = boost::asio::ip::address_v4::from_string("1.1.1.1"); + + nat_static ns(in_addr, out_addr); + HW::item hw_ns(true, rc_t::OK); + + ADD_EXPECT(nat_static::create_44_cmd(hw_ns, 0, in_addr.to_v4(), out_addr)); + TRY_CHECK_RC(OM::write(gs, ns)); + + /* + * bind nat inside and out + */ + nat_binding *nb_in = new nat_binding(itf_in, + direction_t::INPUT, + l3_proto_t::IPV4, + nat_binding::zone_t::INSIDE); + HW::item hw_nb_in(true, rc_t::OK); + + ADD_EXPECT(nat_binding::bind_44_input_cmd(hw_nb_in, hw_ifh.data().value(), + nat_binding::zone_t::INSIDE)); + TRY_CHECK_RC(OM::write(gs, *nb_in)); + + nat_binding *nb_out = new nat_binding(itf_out, + direction_t::INPUT, + l3_proto_t::IPV4, + nat_binding::zone_t::OUTSIDE); + HW::item hw_nb_out(true, rc_t::OK); + + ADD_EXPECT(nat_binding::bind_44_input_cmd(hw_nb_out, hw_ifh2.data().value(), + nat_binding::zone_t::OUTSIDE)); + TRY_CHECK_RC(OM::write(gs, *nb_out)); + + + STRICT_ORDER_OFF(); + delete nb_in; + delete nb_out; + ADD_EXPECT(nat_binding::unbind_44_input_cmd(hw_nb_in, hw_ifh.data().value(), + nat_binding::zone_t::INSIDE)); + ADD_EXPECT(nat_binding::unbind_44_input_cmd(hw_nb_out, hw_ifh2.data().value(), + nat_binding::zone_t::OUTSIDE)); + ADD_EXPECT(nat_static::delete_44_cmd(hw_ns, 0, in_addr.to_v4(), out_addr)); + ADD_EXPECT(interface::state_change_cmd(hw_as_down, hw_ifh)); + ADD_EXPECT(interface::af_packet_delete_cmd(hw_ifh, itf_in_name)); + ADD_EXPECT(interface::state_change_cmd(hw_as_down2, hw_ifh2)); + ADD_EXPECT(interface::af_packet_delete_cmd(hw_ifh2, itf_out_name)); + + TRY_CHECK(OM::remove(gs)); +} + +BOOST_AUTO_TEST_CASE(test_interface_events) { + VppInit vi; + MockListener ml; + + HW::item hw_want(true, rc_t::OK); + + ADD_EXPECT(interface::events_cmd(ml)); + cmd* itf = new interface::events_cmd(ml); + + HW::enqueue(itf); + HW::write(); + + HW::dequeue(itf); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/framework.py b/test/framework.py index 6446265773d..0c85bfbfcf5 100644 --- a/test/framework.py +++ b/test/framework.py @@ -1051,3 +1051,33 @@ class VppTestRunner(unittest.TextTestRunner): if not running_extended_tests(): print("Not running extended tests (some tests will be skipped)") return super(VppTestRunner, self).run(filtered) + + +class Worker(Thread): + def __init__(self, args, logger): + self.logger = logger + self.args = args + self.result = None + super(Worker, self).__init__() + + def run(self): + executable = self.args[0] + self.logger.debug("Running executable w/args `%s'" % self.args) + env = os.environ.copy() + env["CK_LOG_FILE_NAME"] = "-" + self.process = subprocess.Popen( + self.args, shell=False, env=env, preexec_fn=os.setpgrp, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + out, err = self.process.communicate() + self.logger.debug("Finished running `%s'" % executable) + self.logger.info("Return code is `%s'" % self.process.returncode) + self.logger.info(single_line_delim) + self.logger.info("Executable `%s' wrote to stdout:" % executable) + self.logger.info(single_line_delim) + self.logger.info(out) + self.logger.info(single_line_delim) + self.logger.info("Executable `%s' wrote to stderr:" % executable) + self.logger.info(single_line_delim) + self.logger.error(err) + self.logger.info(single_line_delim) + self.result = self.process.returncode diff --git a/test/test_vapi.py b/test/test_vapi.py index 5f972323c61..b5820fa181b 100644 --- a/test/test_vapi.py +++ b/test/test_vapi.py @@ -1,7 +1,6 @@ #!/usr/bin/env python """ VAPI test """ -from __future__ import division import unittest import os import signal @@ -9,37 +8,7 @@ import subprocess from threading import Thread from log import single_line_delim from framework import VppTestCase, running_extended_tests, \ - running_on_centos, VppTestRunner - - -class Worker(Thread): - def __init__(self, args, logger): - self.logger = logger - self.args = args - self.result = None - super(Worker, self).__init__() - - def run(self): - executable = self.args[0] - self.logger.debug("Running executable w/args `%s'" % self.args) - env = os.environ.copy() - env["CK_LOG_FILE_NAME"] = "-" - self.process = subprocess.Popen( - self.args, shell=False, env=env, preexec_fn=os.setpgrp, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - out, err = self.process.communicate() - self.logger.debug("Finished running `%s'" % executable) - self.logger.info("Return code is `%s'" % self.process.returncode) - self.logger.info(single_line_delim) - self.logger.info("Executable `%s' wrote to stdout:" % executable) - self.logger.info(single_line_delim) - self.logger.info(out) - self.logger.info(single_line_delim) - self.logger.info("Executable `%s' wrote to stderr:" % executable) - self.logger.info(single_line_delim) - self.logger.error(err) - self.logger.info(single_line_delim) - self.result = self.process.returncode + running_on_centos, VppTestRunner, Worker @unittest.skipUnless(running_extended_tests(), "part of extended tests") diff --git a/test/test_vom.py b/test/test_vom.py new file mode 100644 index 00000000000..bfd7007fbec --- /dev/null +++ b/test/test_vom.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +""" VAPI test """ + +import unittest +import os +import signal +import subprocess +from threading import Thread +from log import single_line_delim +from framework import VppTestCase, running_extended_tests, \ + running_on_centos, VppTestRunner, Worker + + +@unittest.skipUnless(running_extended_tests(), "part of extended tests") +class VOMTestCase(VppTestCase): + """ VPP Object Model Test """ + + def test_vom_cpp(self): + """ run C++ VOM tests """ + var = "BR" + built_root = os.getenv(var, None) + self.assertIsNotNone(built_root, + "Environment variable `%s' not set" % var) + executable = "%s/vom_test/vom_test" % built_root + worker = Worker( + [executable, "vpp object model", self.shm_prefix], self.logger) + worker.start() + timeout = 120 + worker.join(timeout) + self.logger.info("Worker result is `%s'" % worker.result) + error = False + if worker.result is None: + try: + error = True + self.logger.error( + "Timeout! Worker did not finish in %ss" % timeout) + os.killpg(os.getpgid(worker.process.pid), signal.SIGTERM) + worker.join() + except: + raise Exception("Couldn't kill worker-spawned process") + if error: + raise Exception( + "Timeout! Worker did not finish in %ss" % timeout) + self.assert_equal(worker.result, 0, "Binary test return code") + + +if __name__ == '__main__': + unittest.main(testRunner=VppTestRunner) -- cgit 1.2.3-korg