VPP Object Model (VOM) 36/8836/17
authorNeale Ranns <neale.ranns@cisco.com>
Mon, 16 Oct 2017 11:20:13 +0000 (04:20 -0700)
committerDamjan Marion <dmarion.lists@gmail.com>
Wed, 1 Nov 2017 09:28:06 +0000 (09:28 +0000)
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 <neale.ranns@cisco.com>
Co-authored-by: Mohsin Kazmi <sykazmi@cisco.com>
120 files changed:
.gitignore
Makefile
src/Makefile.am
src/configure.ac
src/vpp-api/vapi/vapi.hpp
src/vpp-api/vom/.clang-format [new file with mode: 0644]
src/vpp-api/vom/Makefile.am [new file with mode: 0644]
src/vpp-api/vom/acl_binding.cpp [new file with mode: 0644]
src/vpp-api/vom/acl_binding.hpp [new file with mode: 0644]
src/vpp-api/vom/acl_binding_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/acl_l2_rule.cpp [new file with mode: 0644]
src/vpp-api/vom/acl_l2_rule.hpp [new file with mode: 0644]
src/vpp-api/vom/acl_l3_rule.cpp [new file with mode: 0644]
src/vpp-api/vom/acl_l3_rule.hpp [new file with mode: 0644]
src/vpp-api/vom/acl_list.cpp [new file with mode: 0644]
src/vpp-api/vom/acl_list.hpp [new file with mode: 0644]
src/vpp-api/vom/acl_list_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/acl_types.cpp [new file with mode: 0644]
src/vpp-api/vom/acl_types.hpp [new file with mode: 0644]
src/vpp-api/vom/arp_proxy_binding.cpp [new file with mode: 0644]
src/vpp-api/vom/arp_proxy_binding.hpp [new file with mode: 0644]
src/vpp-api/vom/arp_proxy_binding_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/arp_proxy_config.cpp [new file with mode: 0644]
src/vpp-api/vom/arp_proxy_config.hpp [new file with mode: 0644]
src/vpp-api/vom/arp_proxy_config_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/bridge_domain.cpp [new file with mode: 0644]
src/vpp-api/vom/bridge_domain.hpp [new file with mode: 0644]
src/vpp-api/vom/bridge_domain_arp_entry.cpp [new file with mode: 0644]
src/vpp-api/vom/bridge_domain_arp_entry.hpp [new file with mode: 0644]
src/vpp-api/vom/bridge_domain_arp_entry_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/bridge_domain_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/bridge_domain_entry.cpp [new file with mode: 0644]
src/vpp-api/vom/bridge_domain_entry.hpp [new file with mode: 0644]
src/vpp-api/vom/bridge_domain_entry_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/client_db.cpp [new file with mode: 0644]
src/vpp-api/vom/client_db.hpp [new file with mode: 0644]
src/vpp-api/vom/cmd.cpp [new file with mode: 0644]
src/vpp-api/vom/cmd.hpp [new file with mode: 0644]
src/vpp-api/vom/connection.cpp [new file with mode: 0644]
src/vpp-api/vom/connection.hpp [new file with mode: 0644]
src/vpp-api/vom/dhcp_config.cpp [new file with mode: 0644]
src/vpp-api/vom/dhcp_config.hpp [new file with mode: 0644]
src/vpp-api/vom/dhcp_config_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/dump_cmd.hpp [new file with mode: 0644]
src/vpp-api/vom/enum_base.hpp [new file with mode: 0644]
src/vpp-api/vom/event_cmd.hpp [new file with mode: 0644]
src/vpp-api/vom/hw.cpp [new file with mode: 0644]
src/vpp-api/vom/hw.hpp [new file with mode: 0644]
src/vpp-api/vom/inspect.cpp [new file with mode: 0644]
src/vpp-api/vom/inspect.hpp [new file with mode: 0644]
src/vpp-api/vom/interface.cpp [new file with mode: 0644]
src/vpp-api/vom/interface.hpp [new file with mode: 0644]
src/vpp-api/vom/interface_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/interface_factory.cpp [new file with mode: 0644]
src/vpp-api/vom/interface_ip6_nd.hpp [new file with mode: 0644]
src/vpp-api/vom/interface_ip6_nd_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/interface_span.cpp [new file with mode: 0644]
src/vpp-api/vom/interface_span.hpp [new file with mode: 0644]
src/vpp-api/vom/interface_span_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/interface_types.cpp [new file with mode: 0644]
src/vpp-api/vom/ip_unnumbered.cpp [new file with mode: 0644]
src/vpp-api/vom/ip_unnumbered.hpp [new file with mode: 0644]
src/vpp-api/vom/ip_unnumbered_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/l2_binding.cpp [new file with mode: 0644]
src/vpp-api/vom/l2_binding.hpp [new file with mode: 0644]
src/vpp-api/vom/l2_binding_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/l3_binding.cpp [new file with mode: 0644]
src/vpp-api/vom/l3_binding.hpp [new file with mode: 0644]
src/vpp-api/vom/l3_binding_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/lldp_binding.cpp [new file with mode: 0644]
src/vpp-api/vom/lldp_binding.hpp [new file with mode: 0644]
src/vpp-api/vom/lldp_binding_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/lldp_global.cpp [new file with mode: 0644]
src/vpp-api/vom/lldp_global.hpp [new file with mode: 0644]
src/vpp-api/vom/lldp_global_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/logger.cpp [new file with mode: 0644]
src/vpp-api/vom/logger.hpp [new file with mode: 0644]
src/vpp-api/vom/nat_binding.cpp [new file with mode: 0644]
src/vpp-api/vom/nat_binding.hpp [new file with mode: 0644]
src/vpp-api/vom/nat_binding_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/nat_static.cpp [new file with mode: 0644]
src/vpp-api/vom/nat_static.hpp [new file with mode: 0644]
src/vpp-api/vom/nat_static_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/neighbour.cpp [new file with mode: 0644]
src/vpp-api/vom/neighbour.hpp [new file with mode: 0644]
src/vpp-api/vom/neighbour_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/object_base.cpp [new file with mode: 0644]
src/vpp-api/vom/object_base.hpp [new file with mode: 0644]
src/vpp-api/vom/om.cpp [new file with mode: 0644]
src/vpp-api/vom/om.hpp [new file with mode: 0644]
src/vpp-api/vom/prefix.cpp [new file with mode: 0644]
src/vpp-api/vom/prefix.hpp [new file with mode: 0644]
src/vpp-api/vom/ra_config.cpp [new file with mode: 0644]
src/vpp-api/vom/ra_config.hpp [new file with mode: 0644]
src/vpp-api/vom/ra_prefix.cpp [new file with mode: 0644]
src/vpp-api/vom/ra_prefix.hpp [new file with mode: 0644]
src/vpp-api/vom/route.cpp [new file with mode: 0644]
src/vpp-api/vom/route.hpp [new file with mode: 0644]
src/vpp-api/vom/route_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/route_domain.cpp [new file with mode: 0644]
src/vpp-api/vom/route_domain.hpp [new file with mode: 0644]
src/vpp-api/vom/route_domain_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/rpc_cmd.hpp [new file with mode: 0644]
src/vpp-api/vom/singular_db.hpp [new file with mode: 0644]
src/vpp-api/vom/sub_interface.cpp [new file with mode: 0644]
src/vpp-api/vom/sub_interface.hpp [new file with mode: 0644]
src/vpp-api/vom/sub_interface_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/tap_interface.cpp [new file with mode: 0644]
src/vpp-api/vom/tap_interface.hpp [new file with mode: 0644]
src/vpp-api/vom/tap_interface_cmds.cpp [new file with mode: 0644]
src/vpp-api/vom/types.cpp [new file with mode: 0644]
src/vpp-api/vom/types.hpp [new file with mode: 0644]
src/vpp-api/vom/vxlan_tunnel.cpp [new file with mode: 0644]
src/vpp-api/vom/vxlan_tunnel.hpp [new file with mode: 0644]
src/vpp-api/vom/vxlan_tunnel_cmds.cpp [new file with mode: 0644]
test/ext/Makefile
test/ext/vom_test.cpp [new file with mode: 0644]
test/framework.py
test/test_vapi.py
test/test_vom.py [new file with mode: 0644]

index 5a6266d..2810cb5 100644 (file)
@@ -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
index 182156c..799fa7e 100644 (file)
--- 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
index c1b6b0e..7b6818f 100644 (file)
@@ -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
index f90ff96..150930f 100644 (file)
@@ -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
index a3ac2ce..e0fd2e5 100644 (file)
@@ -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 (file)
index 0000000..917dceb
--- /dev/null
@@ -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 (file)
index 0000000..17b846f
--- /dev/null
@@ -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 (file)
index 0000000..1ebefda
--- /dev/null
@@ -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<l2_binding::dump_cmd> cmd(new l2_binding::dump_cmd());
+
+  HW::enqueue(cmd);
+  HW::write();
+
+  for (auto& record : *cmd) {
+    auto& payload = record.get_payload();
+
+    std::shared_ptr<interface> itf = interface::find(payload.sw_if_index);
+
+    for (int ii = 0; ii < payload.count; ii++) {
+      std::shared_ptr<l2_list> 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<l3_binding::dump_cmd> cmd(new l3_binding::dump_cmd());
+
+  HW::enqueue(cmd);
+  HW::write();
+
+  for (auto& record : *cmd) {
+    auto& payload = record.get_payload();
+
+    std::shared_ptr<interface> 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<l3_list> 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<direction_t, interface::key_type>& 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 (file)
index 0000000..faf9b24
--- /dev/null
@@ -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 <ostream>
+
+#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 <typename LIST, typename BIND, typename DUMP>
+class binding : public object_base
+{
+public:
+  /**
+   * The key for a binding is the direction and the interface
+   */
+  typedef std::pair<direction_t, interface::key_type> 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<binding> 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<HW::item<bool>, rc_t, BIND>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    bind_cmd(HW::item<bool>& item,
+             const direction_t& direction,
+             const handle_t& itf,
+             const handle_t& acl)
+      : rpc_cmd<HW::item<bool>, 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<HW::item<bool>, rc_t, BIND>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    unbind_cmd(HW::item<bool>& item,
+               const direction_t& direction,
+               const handle_t& itf,
+               const handle_t& acl)
+      : rpc_cmd<HW::item<bool>, 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<DUMP>
+  {
+  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<bool> 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<binding> 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<key_t, binding>;
+
+  /**
+   * 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<interface> 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<LIST> m_acl;
+
+  /**
+   * HW configuration for the binding. The bool representing the
+   * do/don't bind.
+   */
+  HW::item<bool> m_binding;
+
+  /**
+   * A map of all L2 interfaces key against the interface's handle_t
+   */
+  static singular_db<key_t, binding> m_db;
+};
+
+/**
+ * Typedef the L3 binding type
+ */
+typedef binding<l3_list,
+                vapi::Acl_interface_add_del,
+                vapi::Acl_interface_list_dump>
+  l3_binding;
+
+/**
+ * Typedef the L2 binding type
+ */
+typedef binding<l2_list,
+                vapi::Macip_acl_interface_add_del,
+                vapi::Macip_acl_interface_list_dump>
+  l2_binding;
+
+/**
+ * Definition of the static Singular DB for ACL bindings
+ */
+template <typename LIST, typename BIND, typename DUMP>
+singular_db<typename ACL::binding<LIST, BIND, DUMP>::key_t,
+            ACL::binding<LIST, BIND, DUMP>>
+  binding<LIST, BIND, DUMP>::m_db;
+
+template <typename LIST, typename BIND, typename DUMP>
+typename ACL::binding<LIST, BIND, DUMP>::event_handler
+  binding<LIST, BIND, DUMP>::m_evh;
+};
+
+std::ostream& operator<<(
+  std::ostream& os,
+  const std::pair<direction_t, interface::key_type>& 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 (file)
index 0000000..27d5043
--- /dev/null
@@ -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 (file)
index 0000000..1f7f9fd
--- /dev/null
@@ -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 <sstream>
+
+#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 (file)
index 0000000..cc86917
--- /dev/null
@@ -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 <vapi/acl.api.vapi.hpp>
+
+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 (file)
index 0000000..e4b4092
--- /dev/null
@@ -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 <sstream>
+
+#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 (file)
index 0000000..db3ddb4
--- /dev/null
@@ -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 <vapi/acl.api.vapi.hpp>
+
+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 (file)
index 0000000..129be8f
--- /dev/null
@@ -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<l2_list::dump_cmd> 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<const char*>(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<l3_list::dump_cmd> 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<const char*>(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 (file)
index 0000000..a2b5127
--- /dev/null
@@ -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 <set>
+
+#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 <vapi/acl.api.vapi.hpp>
+
+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 <typename RULE, typename UPDATE, typename DELETE, typename DUMP>
+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<RULE> 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<list> 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<handle_t>, HW::item<handle_t>, UPDATE>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    update_cmd(HW::item<handle_t>& item, const key_t& key, const rules_t& rules)
+      : rpc_cmd<HW::item<handle_t>, HW::item<handle_t>, 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<list> sp = find(m_key);
+      if (sp && this->item()) {
+        list::add(this->item().data(), sp);
+      }
+    }
+
+    void succeeded()
+    {
+      rpc_cmd<HW::item<handle_t>, HW::item<handle_t>, 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<handle_t> 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<HW::item<handle_t>, rc_t, DELETE>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    delete_cmd(HW::item<handle_t>& item)
+      : rpc_cmd<HW::item<handle_t>, 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<DUMP>
+  {
+  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<bool> item;
+  };
+
+  static std::shared_ptr<list> find(const handle_t& handle)
+  {
+    return (m_hdl_db[handle].lock());
+  }
+
+  static std::shared_ptr<list> find(const key_t& key)
+  {
+    return (m_db.find(key));
+  }
+
+  static void add(const handle_t& handle, std::shared_ptr<list> 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<handle_t> m_hdl;
+
+  /**
+   * Find or add the sigular instance in the DB
+   */
+  static std::shared_ptr<list> 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<key_t, list>;
+
+  /**
+   * 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<key_t, list> m_db;
+
+  /**
+   * A map of all ACLs keyed against VPP's handle
+   */
+  static std::map<const handle_t, std::weak_ptr<list>> 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_rule, vapi::Acl_add_replace, vapi::Acl_del, vapi::Acl_dump>
+  l3_list;
+
+/**
+ * Typedef the L2 ACL type
+ */
+typedef list<l2_rule,
+             vapi::Macip_acl_add,
+             vapi::Macip_acl_del,
+             vapi::Macip_acl_dump>
+  l2_list;
+
+/**
+ * Definition of the static singular_db for ACL Lists
+ */
+template <typename RULE, typename UPDATE, typename DELETE, typename DUMP>
+singular_db<typename ACL::list<RULE, UPDATE, DELETE, DUMP>::key_t,
+            ACL::list<RULE, UPDATE, DELETE, DUMP>>
+  list<RULE, UPDATE, DELETE, DUMP>::m_db;
+
+/**
+ * Definition of the static per-handle DB for ACL Lists
+ */
+template <typename RULE, typename UPDATE, typename DELETE, typename DUMP>
+std::map<const handle_t, std::weak_ptr<ACL::list<RULE, UPDATE, DELETE, DUMP>>>
+  list<RULE, UPDATE, DELETE, DUMP>::m_hdl_db;
+
+template <typename RULE, typename UPDATE, typename DELETE, typename DUMP>
+typename ACL::list<RULE, UPDATE, DELETE, DUMP>::event_handler
+  list<RULE, UPDATE, DELETE, DUMP>::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 (file)
index 0000000..9bbe2cd
--- /dev/null
@@ -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 (file)
index 0000000..b2c0c7f
--- /dev/null
@@ -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 (file)
index 0000000..ccf0a1c
--- /dev/null
@@ -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<action_t>
+{
+  /**
+   * 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 (file)
index 0000000..77b1bfa
--- /dev/null
@@ -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<interface::key_type, arp_proxy_binding> 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>
+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>
+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 (file)
index 0000000..0d31647
--- /dev/null
@@ -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 <vapi/vpe.api.vapi.hpp>
+
+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<arp_proxy_binding> 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<HW::item<bool>, rc_t, vapi::Proxy_arp_intfc_enable_disable>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    bind_cmd(HW::item<bool>& 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<HW::item<bool>, rc_t, vapi::Proxy_arp_intfc_enable_disable>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    unbind_cmd(HW::item<bool>& 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<arp_proxy_binding> 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<interface::key_type, arp_proxy_binding>;
+
+  /**
+   * 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<interface> m_itf;
+
+  /**
+   * A reference counting pointer to the prxy config.
+   */
+  const std::shared_ptr<arp_proxy_config> m_arp_proxy_cfg;
+
+  /**
+   * HW configuration for the binding. The bool representing the
+   * do/don't bind.
+   */
+  HW::item<bool> m_binding;
+
+  /**
+   * A map of all ArpProxy bindings keyed against the interface.
+   */
+  static singular_db<interface::key_type, arp_proxy_binding> 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 (file)
index 0000000..d7f3188
--- /dev/null
@@ -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<bool>& 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<bool>& 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 (file)
index 0000000..c07eef3
--- /dev/null
@@ -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::key_t, arp_proxy_config> 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>
+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>
+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 (file)
index 0000000..78fc81a
--- /dev/null
@@ -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 <vapi/vpe.api.vapi.hpp>
+
+namespace VOM {
+/**
+ * A representation of LLDP client configuration on an interface
+ */
+class arp_proxy_config : public object_base
+{
+public:
+  /**
+   * Key type
+   */
+  typedef std::pair<boost::asio::ip::address_v4, boost::asio::ip::address_v4>
+    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<arp_proxy_config> 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<HW::item<bool>, rc_t, vapi::Proxy_arp_add_del>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    config_cmd(HW::item<bool>& 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<HW::item<bool>, rc_t, vapi::Proxy_arp_add_del>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    unconfig_cmd(HW::item<bool>& 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<arp_proxy_config> 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<arp_proxy_config::key_t, arp_proxy_config>;
+
+  /**
+   * 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<arp_proxy_config::key_t, arp_proxy_config> m_db;
+
+  /**
+   * HW configuration for the config. The bool representing the
+   * do/don't configured/unconfigured.
+   */
+  HW::item<bool> 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 (file)
index 0000000..0a546d3
--- /dev/null
@@ -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 <vapi/vpe.api.vapi.hpp>
+
+namespace VOM {
+arp_proxy_config::config_cmd::config_cmd(
+  HW::item<bool>& 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<bool>& 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 (file)
index 0000000..5831b2a
--- /dev/null
@@ -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<uint32_t, bridge_domain> 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>
+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<bridge_domain> 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>
+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>
+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<bridge_domain::dump_cmd> 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<interface> 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 (file)
index 0000000..70371fa
--- /dev/null
@@ -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 <vapi/l2.api.vapi.hpp>
+
+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<bridge_domain> 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<bridge_domain> 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<HW::item<uint32_t>, rc_t, vapi::Bridge_domain_add_del>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    create_cmd(HW::item<uint32_t>& 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<HW::item<uint32_t>, rc_t, vapi::Bridge_domain_add_del>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    delete_cmd(HW::item<uint32_t>& 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<vapi::Bridge_domain_dump>
+  {
+  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<bool> 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<bridge_domain> 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<uint32_t, bridge_domain>;
+
+  /**
+   * 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<uint32_t> m_id;
+
+  /**
+   * A map of all interfaces key against the interface's name
+   */
+  static singular_db<uint32_t, bridge_domain> 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 (file)
index 0000000..e2bf6d5
--- /dev/null
@@ -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::key_t, bridge_domain_arp_entry>
+  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>
+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>
+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 (file)
index 0000000..181afe6
--- /dev/null
@@ -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 <vapi/l2.api.vapi.hpp>
+
+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<uint32_t, mac_address_t, boost::asio::ip::address> 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<bridge_domain_arp_entry> singular() const;
+
+  /**
+   * Find the instnace of the bridge_domain domain in the OM
+   */
+  static std::shared_ptr<bridge_domain_arp_entry> 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<HW::item<bool>, rc_t, vapi::Bd_ip_mac_add_del>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    create_cmd(HW::item<bool>& 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<HW::item<bool>, rc_t, vapi::Bd_ip_mac_add_del>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    delete_cmd(HW::item<bool>& 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<bridge_domain_arp_entry> 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<key_t, bridge_domain_arp_entry>;
+
+  /**
+   * Sweep/reap the object if still stale
+   */
+  void sweep(void);
+
+  /**
+   * HW configuration for the result of creating the bridge_domain
+   */
+  HW::item<bool> m_hw;
+
+  /**
+   * The bridge_domain domain the bridge_domain is in.
+   */
+  std::shared_ptr<bridge_domain> 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<key_t, bridge_domain_arp_entry> 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 (file)
index 0000000..3ea63ad
--- /dev/null
@@ -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<bool>& 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<bool>& 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 (file)
index 0000000..9117339
--- /dev/null
@@ -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<uint32_t>& 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<uint32_t>& 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 (file)
index 0000000..4041e31
--- /dev/null
@@ -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::key_t, bridge_domain_entry>
+  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>
+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>
+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<bridge_domain_entry::dump_cmd> cmd(
+    new bridge_domain_entry::dump_cmd());
+
+  HW::enqueue(cmd);
+  HW::write();
+
+  for (auto& record : *cmd) {
+    auto& payload = record.get_payload();
+
+    std::shared_ptr<interface> itf = interface::find(payload.sw_if_index);
+    std::shared_ptr<bridge_domain> 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 (file)
index 0000000..073b2d3
--- /dev/null
@@ -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 <vapi/l2.api.vapi.hpp>
+
+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<uint32_t, mac_address_t> 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<bridge_domain_entry> singular() const;
+
+  /**
+   * Find the instnace of the bridge_domain domain in the OM
+   */
+  static std::shared_ptr<bridge_domain_entry> 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<HW::item<bool>, rc_t, vapi::L2fib_add_del>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    create_cmd(HW::item<bool>& 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<HW::item<bool>, rc_t, vapi::L2fib_add_del>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    delete_cmd(HW::item<bool>& 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<vapi::L2_fib_table_dump>
+  {
+  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<bool> 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<bridge_domain_entry> 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<key_t, bridge_domain_entry>;
+
+  /**
+   * Sweep/reap the object if still stale
+   */
+  void sweep(void);
+
+  /**
+   * HW configuration for the result of creating the bridge_domain
+   */
+  HW::item<bool> m_hw;
+
+  /**
+   * The mac to match
+   */
+  mac_address_t m_mac;
+
+  /**
+   * The bridge_domain domain the bridge_domain is in.
+   */
+  std::shared_ptr<bridge_domain> m_bd;
+
+  /**
+   * The set of paths
+   */
+  std::shared_ptr<interface> m_tx_itf;
+
+  /**
+   * A map of all bridge_domains
+   */
+  static singular_db<key_t, bridge_domain_entry> 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 (file)
index 0000000..ad0786f
--- /dev/null
@@ -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<bool>& 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<bool>& 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 (file)
index 0000000..fad2c13
--- /dev/null
@@ -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 (file)
index 0000000..34204c1
--- /dev/null
@@ -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 <map>
+#include <set>
+
+#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> 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<key_t, object_ref_list> 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 (file)
index 0000000..5623507
--- /dev/null
@@ -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 (file)
index 0000000..8143553
--- /dev/null
@@ -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 <string>
+
+#include "vom/types.hpp"
+
+#include <vapi/vapi.hpp>
+
+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 (file)
index 0000000..3d965ea
--- /dev/null
@@ -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 (file)
index 0000000..0dc0184
--- /dev/null
@@ -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 <string>
+
+#include <vapi/vapi.hpp>
+
+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 (file)
index 0000000..bf9b036
--- /dev/null
@@ -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<interface::key_type, dhcp_config> 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>
+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>
+dhcp_config::singular() const
+{
+  return find_or_add(*this);
+}
+
+dhcp_config::event_listener::event_listener()
+  : m_status(rc_t::NOOP)
+{
+}
+
+HW::item<bool>&
+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 (file)
index 0000000..9ce0635
--- /dev/null
@@ -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 <vapi/dhcp.api.vapi.hpp>
+
+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<dhcp_config> 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<HW::item<bool>, rc_t, vapi::Dhcp_client_config>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    bind_cmd(HW::item<bool>& 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<HW::item<bool>, rc_t, vapi::Dhcp_client_config>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    unbind_cmd(HW::item<bool>& 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<bool>& status();
+
+  protected:
+    /**
+     * The HW::item associated with this command
+     */
+    HW::item<bool> m_status;
+  };
+
+  /**
+   * A functor class represents our desire to recieve interface events
+   */
+  class events_cmd
+    : public event_cmd<vapi::Control_ping, vapi::Dhcp_compl_event>
+  {
+  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<dhcp_config> 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<interface::key_type, dhcp_config>;
+
+  /**
+   * 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<interface> 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<bool> m_binding;
+
+  /**
+   * A map of all Dhcp configs keyed against the interface.
+   */
+  static singular_db<interface::key_type, dhcp_config> 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 (file)
index 0000000..53262a3
--- /dev/null
@@ -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<bool>& 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<bool>& 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 (file)
index 0000000..cc255ac
--- /dev/null
@@ -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 <future>
+
+#include "vom/cmd.hpp"
+#include "vom/hw.hpp"
+
+#include <vapi/vapi.hpp>
+
+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 <typename MSG>
+class dump_cmd : public cmd
+{
+public:
+  typedef MSG msg_t;
+  typedef typename MSG::resp_type record_t;
+
+  typedef typename vapi::Result_set<typename MSG::resp_type>::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<rc_t> 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<rc_t> 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<MSG> 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 (file)
index 0000000..4184ceb
--- /dev/null
@@ -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 <string>
+
+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 <typename T>
+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 (file)
index 0000000..a0e9b4a
--- /dev/null
@@ -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 <mutex>
+
+#include "vom/rpc_cmd.hpp"
+
+#include <vapi/vapi.hpp>
+
+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 <typename WANT, typename EVENT>
+class event_cmd : public rpc_cmd<HW::item<bool>, rc_t, WANT>
+{
+public:
+  /**
+   * Default constructor
+   */
+  event_cmd(HW::item<bool>& b)
+    : rpc_cmd<HW::item<bool>, rc_t, WANT>(b)
+  {
+  }
+
+  /**
+   * Default destructor
+   */
+  virtual ~event_cmd() {}
+
+  /**
+   * Typedef for the event type
+   */
+  typedef typename vapi::Event_registration<EVENT>::resp_type event_t;
+  typedef typename vapi::Event_registration<EVENT> reg_t;
+
+  typedef typename vapi::Result_set<typename reg_t::resp_type>::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<vapi::Event_registration<EVENT>> 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 (file)
index 0000000..5688552
--- /dev/null
@@ -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 <vapi/vpe.api.vapi.hpp>
+
+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<cmd> sp(c);
+
+  m_queue.push_back(sp);
+}
+
+void
+HW::cmd_q::enqueue(std::shared_ptr<cmd> c)
+{
+  m_queue.push_back(c);
+}
+
+void
+HW::cmd_q::enqueue(std::queue<cmd*>& cmds)
+{
+  while (cmds.size()) {
+    std::shared_ptr<cmd> 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<cmd> 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<cmd> 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<bool> 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> cmd)
+{
+  m_cmdQ->enqueue(cmd);
+}
+
+void
+HW::enqueue(std::queue<cmd*>& cmds)
+{
+  m_cmdQ->enqueue(cmds);
+}
+
+void
+HW::dequeue(cmd* cmd)
+{
+  m_cmdQ->dequeue(cmd);
+}
+
+void
+HW::dequeue(std::shared_ptr<cmd> 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<cmd> poll(new Poll(m_poll_state));
+
+  HW::enqueue(poll);
+  HW::write();
+
+  return (m_poll_state);
+}
+
+template <>
+std::string
+HW::item<bool>::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<unsigned int>::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<bool>& 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 (file)
index 0000000..00ab2d6
--- /dev/null
@@ -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 <deque>
+#include <map>
+#include <sstream>
+#include <string>
+#include <thread>
+
+#include <vapi/vapi.hpp>
+#include <vapi/vpe.api.vapi.hpp>
+
+#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 <typename T>
+  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<T>& 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<cmd> c);
+
+    /**
+     * Enqueue a set of commands
+     */
+    virtual void enqueue(std::queue<cmd*>& c);
+
+    /**
+     * dequeue a command from the Q.
+     */
+    virtual void dequeue(cmd* c);
+
+    /**
+     * dequeue a command from the Q.
+     */
+    virtual void dequeue(std::shared_ptr<cmd> 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<std::shared_ptr<cmd>> m_queue;
+
+    /**
+     * A map of issued, but uncompleted, commands.
+     *  i.e. those that we are waiting, async stylee,
+     * for VPP to complete
+     */
+    std::map<cmd*, std::shared_ptr<cmd>> m_pending;
+
+    /**
+     * VPP Q poll function
+     */
+    void rx_run();
+
+    /**
+     * The thread object running the poll/dispatch/connect thread
+     */
+    std::unique_ptr<std::thread> 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<cmd> c);
+
+  /**
+   * Enqueue A set of commands for execution
+   */
+  static void enqueue(std::queue<cmd*>& c);
+
+  /**
+   * dequeue A command for execution
+   */
+  static void dequeue(cmd* f);
+
+  /**
+   * dequeue A command for execution
+   */
+  static void dequeue(std::shared_ptr<cmd> 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<bool> 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<HW::item<bool>, rc_t, vapi::Control_ping>
+  {
+  public:
+    /**
+     * Constructor taking the HW::item to update
+     */
+    Poll(HW::item<bool>& 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<bool>::to_string() const;
+
+/**
+ * uint Specialisation for HW::item to_string
+ */
+template <>
+std::string HW::item<unsigned int>::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 (file)
index 0000000..c78128c
--- /dev/null
@@ -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 <boost/algorithm/string.hpp>
+#include <string>
+#include <vector>
+
+#include "vom/inspect.hpp"
+#include "vom/logger.hpp"
+#include "vom/om.hpp"
+
+namespace VOM {
+std::unique_ptr<std::map<std::string, inspect::command_handler*>>
+  inspect::m_cmd_handlers;
+
+std::unique_ptr<std::deque<std::pair<std::vector<std::string>, 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<std::string> 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<command_handler*> 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<std::string>& cmds,
+                          const std::string& help,
+                          command_handler* handler)
+{
+  if (!m_cmd_handlers) {
+    m_cmd_handlers.reset(new std::map<std::string, command_handler*>);
+    m_help_handlers.reset(
+      new std::deque<std::pair<std::vector<std::string>, 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 (file)
index 0000000..fb9528d
--- /dev/null
@@ -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 <deque>
+#include <map>
+#include <ostream>
+#include <string>
+
+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<std::string>& cmds,
+                               const std::string& help,
+                               command_handler* ch);
+
+private:
+  /**
+   * command handler list
+   */
+  static std::unique_ptr<std::map<std::string, command_handler*>>
+    m_cmd_handlers;
+  /**
+   * help handler list
+   */
+  static std::unique_ptr<
+    std::deque<std::pair<std::vector<std::string>, 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 (file)
index 0000000..83da275
--- /dev/null
@@ -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<const std::string, interface> interface::m_db;
+
+/**
+ * A DB of all the interfaces, key on VPP's handle
+ */
+std::map<handle_t, std::weak_ptr<interface>> 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<bool>&
+interface::event_listener::status()
+{
+  return (m_status);
+}
+
+interface::stat_listener::stat_listener()
+  : m_status(rc_t::NOOP)
+{
+}
+
+HW::item<bool>&
+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<cmd*> cmds;
+    HW::enqueue(mk_delete_cmd(cmds));
+  }
+  HW::write();
+}
+
+void
+interface::replay()
+{
+  if (m_hdl) {
+    std::queue<cmd*> 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<cmd*>&
+interface::mk_create_cmd(std::queue<cmd*>& 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<cmd*>&
+interface::mk_delete_cmd(std::queue<cmd*>& 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<cmd*> 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>
+interface::singular_i() const
+{
+  return (m_db.find_or_add(name(), *this));
+}
+
+std::shared_ptr<interface>
+interface::singular() const
+{
+  return singular_i();
+}
+
+std::shared_ptr<interface>
+interface::find(const std::string& name)
+{
+  return (m_db.find(name));
+}
+
+std::shared_ptr<interface>
+interface::find(const handle_t& handle)
+{
+  return (m_hdl_db[handle].lock());
+}
+
+void
+interface::add(const std::string& name, const HW::item<handle_t>& item)
+{
+  std::shared_ptr<interface> sp = find(name);
+
+  if (sp && item) {
+    m_hdl_db[item.data()] = sp;
+  }
+}
+
+void
+interface::remove(const HW::item<handle_t>& 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<interface::dump_cmd> cmd(new interface::dump_cmd());
+
+  HW::enqueue(cmd);
+  HW::write();
+
+  for (auto& itf_record : *cmd) {
+    std::unique_ptr<interface> 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<l3_binding::dump_v4_cmd> dcmd =
+        std::make_shared<l3_binding::dump_v4_cmd>(
+          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 (file)
index 0000000..8863bc9
--- /dev/null
@@ -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 <vapi/af_packet.api.vapi.hpp>
+#include <vapi/interface.api.vapi.hpp>
+#include <vapi/stats.api.vapi.hpp>
+#include <vapi/tap.api.vapi.hpp>
+#include <vapi/vapi.hpp>
+#include <vapi/vpe.api.vapi.hpp>
+
+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 std::string, interface>::const_iterator
+    const_iterator_t;
+
+  /**
+   * An interface type
+   */
+  struct type_t : enum_base<type_t>
+  {
+    /**
+     * 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_state_t>
+  {
+    /**
+     * 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<oper_state_t>
+  {
+    /**
+     * 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<interface> 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 <typename MSG>
+  class create_cmd : public rpc_cmd<HW::item<handle_t>, HW::item<handle_t>, MSG>
+  {
+  public:
+    create_cmd(HW::item<handle_t>& item, const std::string& name)
+      : rpc_cmd<HW::item<handle_t>, HW::item<handle_t>, 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<handle_t>, HW::item<handle_t>, 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<handle_t> 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<vapi::Create_loopback>
+  {
+  public:
+    /**
+     * Constructor taking the HW::item to update
+     * and the name of the interface to create
+     */
+    loopback_create_cmd(HW::item<handle_t>& 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<vapi::Af_packet_create>
+  {
+  public:
+    /**
+     * Constructor taking the HW::item to update
+     * and the name of the interface to create
+     */
+    af_packet_create_cmd(HW::item<handle_t>& 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<vapi::Tap_connect>
+  {
+  public:
+    /**
+     * Constructor taking the HW::item to update
+     * and the name of the interface to create
+     */
+    tap_create_cmd(HW::item<handle_t>& 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 <typename MSG>
+  class delete_cmd : public rpc_cmd<HW::item<handle_t>, HW::item<handle_t>, MSG>
+  {
+  public:
+    delete_cmd(HW::item<handle_t>& item, const std::string& name)
+      : rpc_cmd<HW::item<handle_t>, HW::item<handle_t>, MSG>(item)
+      , m_name(name)
+    {
+    }
+
+    delete_cmd(HW::item<handle_t>& item)
+      : rpc_cmd<HW::item<handle_t>, HW::item<handle_t>, 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<vapi::Delete_loopback>
+  {
+  public:
+    /**
+     * Constructor taking the HW::item to update
+     */
+    loopback_delete_cmd(HW::item<handle_t>& 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<vapi::Af_packet_delete>
+  {
+  public:
+    /**
+     * Constructor taking the HW::item to update
+     * and the name of the interface to delete
+     */
+    af_packet_delete_cmd(HW::item<handle_t>& 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<vapi::Tap_delete>
+  {
+  public:
+    /**
+     * Constructor taking the HW::item to update
+     */
+    tap_delete_cmd(HW::item<handle_t>& 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<HW::item<handle_t>, rc_t, vapi::Sw_interface_tag_add_del>
+  {
+  public:
+    /**
+     * Constructor taking the HW::item to update
+     */
+    set_tag(HW::item<handle_t>& 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<HW::item<admin_state_t>,
+                                          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<admin_state_t>& s, const HW::item<handle_t>& 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<handle_t>& m_hdl;
+  };
+
+  /**
+   * A command class that binds an interface to an L3 table
+   */
+  class set_table_cmd : public rpc_cmd<HW::item<route::table_id_t>,
+                                       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<route::table_id_t>& item,
+                  const l3_proto_t& proto,
+                  const HW::item<handle_t>& 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<handle_t>& 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<HW::item<l2_address_t>,
+                                     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<l2_address_t>& item, const HW::item<handle_t>& 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<handle_t>& 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<bool>& status();
+
+  protected:
+    /**
+     * The status of the subscription
+     */
+    HW::item<bool> m_status;
+  };
+
+  /**
+   * A command class represents our desire to recieve interface events
+   */
+  class events_cmd
+    : public event_cmd<vapi::Want_interface_events, vapi::Sw_interface_event>
+  {
+  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<bool>& status();
+
+  protected:
+    /**
+     * The status of the subscription
+     */
+    HW::item<bool> m_status;
+  };
+
+  /**
+   * A command class represents our desire to recieve interface stats
+   */
+  class stats_cmd : public event_cmd<vapi::Want_per_interface_combined_stats,
+                                     vapi::Vnet_per_interface_combined_counters>
+  {
+  public:
+    /**
+     * Constructor taking the listner to notify
+     */
+    stats_cmd(stat_listener& el, const std::vector<handle_t>& 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<handle_t> m_swifindex;
+  };
+
+  /**
+   * A cmd class that Dumps all the Vpp interfaces
+   */
+  class dump_cmd : public VOM::dump_cmd<vapi::Sw_interface_dump>
+  {
+  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<interface> find(const interface& temp);
+
+  /**
+   * The the singular instance of the interface in the object_base-Model
+   * by handle
+   */
+  static std::shared_ptr<interface> find(const handle_t& h);
+
+  /**
+   * The the singular instance of the interface in the object_base-Model
+   * by name
+   */
+  static std::shared_ptr<interface> 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<interface> 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<handle_t> m_hdl;
+
+  /**
+   * Return the matching 'singular' of the interface
+   */
+  virtual std::shared_ptr<interface> 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<cmd*>& mk_create_cmd(std::queue<cmd*>& cmds);
+
+  /**
+   * Virtual functions to construct an interface delete commands.
+   * Overridden in derived classes like the sub_interface
+   */
+  virtual std::queue<cmd*>& mk_delete_cmd(std::queue<cmd*>& 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<const std::string, interface> m_db;
+
+  /**
+   * Add an interface to the DB keyed on handle
+   */
+  static void add(const std::string& name, const HW::item<handle_t>& item);
+
+  /**
+   * remove an interface from the DB keyed on handle
+   */
+  static void remove(const HW::item<handle_t>& 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<const std::string, interface>;
+
+  /**
+   * 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<route_domain> m_rd;
+
+  /**
+   * The state of the interface
+   */
+  HW::item<admin_state_t> m_state;
+
+  /**
+   * HW state of the VPP table mapping
+   */
+  HW::item<route::table_id_t> m_table_id;
+
+  /**
+   * HW state of the L2 address
+   */
+  HW::item<l2_address_t> 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<handle_t, std::weak_ptr<interface>> 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 <typename MSG>
+  friend class create_cmd;
+
+  /**
+   * Create commands are firends so they can remove interfaces from the
+   * handle store.
+   */
+  template <typename MSG>
+  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 (file)
index 0000000..306f987
--- /dev/null
@@ -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<handle_t>& 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<handle_t>& 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<handle_t>& 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<handle_t>& 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<handle_t>& 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<handle_t>& 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<interface::admin_state_t>& state,
+  const HW::item<handle_t>& 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<route::table_id_t>& table,
+                                        const l3_proto_t& proto,
+                                        const HW::item<handle_t>& 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<l2_address_t>& mac,
+                                    const HW::item<handle_t>& 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<event_cmd*>(this)))));
+
+  /*
+ * then send the request to enable them
+ */
+  msg_t req(con.ctx(), std::ref(*(static_cast<rpc_cmd*>(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<rpc_cmd*>(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<handle_t>& 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<event_cmd*>(this)))));
+  // m_reg->execute();
+
+  /*
+ * then send the request to enable them
+ */
+  msg_t req(con.ctx(), m_swifindex.size(),
+            std::ref(*(static_cast<rpc_cmd*>(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<handle_t>& 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 (file)
index 0000000..aac968c
--- /dev/null
@@ -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 <boost/algorithm/string.hpp>
+
+#include "vom/interface.hpp"
+#include "vom/sub_interface.hpp"
+#include "vom/tap_interface.hpp"
+
+namespace VOM {
+std::unique_ptr<interface>
+interface::new_interface(const vapi_payload_sw_interface_details& vd)
+{
+  std::unique_ptr<interface> up_itf;
+
+  /**
+ * Determine the interface type from the name and VLAN attributes
+ */
+  std::string name = reinterpret_cast<const char*>(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<const char*>(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<std::string> 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 (file)
index 0000000..38845e3
--- /dev/null
@@ -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 <vapi/ip.api.vapi.hpp>
+
+namespace VOM {
+/**
+ * A representation of L3 configuration on an interface
+ */
+template <typename CLASS, typename CMD>
+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<interface_ip6_nd> 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<interface_ip6_nd> 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<std::shared_ptr<interface_ip6_nd>> 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<HW::item<bool>, rc_t, CMD>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    config_cmd(HW::item<bool>& item, const handle_t& itf, const class_t& cls)
+      : rpc_cmd<HW::item<bool>, 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<HW::item<bool>, rc_t, CMD>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    unconfig_cmd(HW::item<bool>& item, const handle_t& itf, const class_t& cls)
+      : rpc_cmd<HW::item<bool>, 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<interface_ip6_nd> 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<key_t, interface_ip6_nd>;
+
+  const std::shared_ptr<interface> 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<bool> m_config;
+
+  /**
+   * A map of all interface ip6 nd keyed against a combination of the
+   * interface and subnet's keys.
+   */
+  static singular_db<key_t, interface_ip6_nd> m_db;
+};
+
+/**
+ * Typedef the ip6nd_ra_config
+ */
+typedef interface_ip6_nd<ra_config, vapi::Sw_interface_ip6nd_ra_config>
+  ip6nd_ra_config;
+
+/**
+ * Typedef the ip6nd_ra_prefix
+ */
+typedef interface_ip6_nd<ra_prefix, vapi::Sw_interface_ip6nd_ra_prefix>
+  ip6nd_ra_prefix;
+
+/**
+ * Definition of the static singular_db for ACL Lists
+ */
+template <typename CLASS, typename CMD>
+singular_db<typename interface_ip6_nd<CLASS, CMD>::key_t,
+            interface_ip6_nd<CLASS, CMD>>
+  interface_ip6_nd<CLASS, CMD>::m_db;
+
+template <typename CLASS, typename CMD>
+typename interface_ip6_nd<CLASS, CMD>::event_handler
+  interface_ip6_nd<CLASS, CMD>::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 (file)
index 0000000..c6f53f0
--- /dev/null
@@ -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 <vapi/vpe.api.vapi.hpp>
+
+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 (file)
index 0000000..88bec50
--- /dev/null
@@ -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::key_type_t, interface_span> 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>
+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>
+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<interface_span::dump_cmd> cmd(new interface_span::dump_cmd());
+
+  HW::enqueue(cmd);
+  HW::write();
+
+  for (auto& record : *cmd) {
+    auto& payload = record.get_payload();
+
+    std::shared_ptr<interface> itf_from =
+      interface::find(payload.sw_if_index_from);
+    std::shared_ptr<interface> 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<interface_span::state_t>(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 (file)
index 0000000..b70c316
--- /dev/null
@@ -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 <vapi/span.api.vapi.hpp>
+
+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<state_t>
+  {
+    /**
+     * 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<interface_span> 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<interface::key_type, interface::key_type> key_type_t;
+
+  /**
+   * Find a singular instance in the DB for the interface passed
+   */
+  static std::shared_ptr<interface_span> find(const interface& i);
+
+  /**
+   * A command class that configures the interface span
+   */
+  class config_cmd : public rpc_cmd<HW::item<bool>,
+                                    rc_t,
+                                    vapi::Sw_interface_span_enable_disable>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    config_cmd(HW::item<bool>& 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<HW::item<bool>,
+                                      rc_t,
+                                      vapi::Sw_interface_span_enable_disable>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    unconfig_cmd(HW::item<bool>& 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<vapi::Sw_interface_span_dump>
+  {
+  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<bool> 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<interface_span> 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<key_type_t, interface_span>;
+
+  /**
+   * 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<interface> m_itf_from;
+  /**
+   * A reference counting pointer the interface where the traffic is
+   * mirrored
+ */
+  const std::shared_ptr<interface> 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<bool> m_config;
+
+  /**
+   * A map of all interface span keyed against the interface to be
+   * mirrored.
+ */
+  static singular_db<key_type_t, interface_span> 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 (file)
index 0000000..27fec80
--- /dev/null
@@ -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<bool>& 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<bool>& 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 (file)
index 0000000..6afec80
--- /dev/null
@@ -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<interface::type_t>(v, s)
+{
+}
+
+interface::oper_state_t::oper_state_t(int v, const std::string& s)
+  : enum_base<interface::oper_state_t>(v, s)
+{
+}
+
+interface::admin_state_t::admin_state_t(int v, const std::string& s)
+  : enum_base<interface::admin_state_t>(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 (file)
index 0000000..7df391c
--- /dev/null
@@ -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::key_t, ip_unnumbered> 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>
+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>
+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 (file)
index 0000000..e6f5ef4
--- /dev/null
@@ -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<ip_unnumbered> 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<ip_unnumbered> find(const interface& i);
+
+  /**
+   * A command class that configures the IP unnumbered
+   */
+  class config_cmd
+    : public rpc_cmd<HW::item<bool>, rc_t, vapi::Sw_interface_set_unnumbered>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    config_cmd(HW::item<bool>& 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<HW::item<bool>, rc_t, vapi::Sw_interface_set_unnumbered>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    unconfig_cmd(HW::item<bool>& 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<ip_unnumbered> 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<key_t, ip_unnumbered>;
+
+  /**
+   * 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<interface> m_itf;
+  /**
+   * A reference counting pointer the interface that has an address.
+   */
+  const std::shared_ptr<interface> m_l3_itf;
+
+  /**
+   * HW configuration for the binding. The bool representing the
+   * do/don't bind.
+   */
+  HW::item<bool> m_config;
+
+  /**
+   * A map of all L3 configs keyed against a combination of the interface
+   * and subnet's keys.
+   */
+  static singular_db<key_t, ip_unnumbered> 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 (file)
index 0000000..768d356
--- /dev/null
@@ -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 <vapi/vpe.api.vapi.hpp>
+
+namespace VOM {
+ip_unnumbered::config_cmd::config_cmd(HW::item<bool>& 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<bool>& 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 (file)
index 0000000..3bdc8f9
--- /dev/null
@@ -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<const handle_t, l2_binding> 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<l2_binding::l2_vtr_op_t>(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>
+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>
+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 (file)
index 0000000..2f65e38
--- /dev/null
@@ -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>
+  {
+    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<l2_binding> 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<HW::item<bool>, rc_t, vapi::Sw_interface_set_l2_bridge>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    bind_cmd(HW::item<bool>& 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<HW::item<bool>, rc_t, vapi::Sw_interface_set_l2_bridge>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    unbind_cmd(HW::item<bool>& 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<HW::item<l2_vtr_op_t>,
+                                        rc_t,
+                                        vapi::L2_interface_vlan_tag_rewrite>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    set_vtr_op_cmd(HW::item<l2_vtr_op_t>& 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<l2_binding> 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<const handle_t, l2_binding>;
+
+  /**
+   * 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<interface> 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<bridge_domain> m_bd;
+
+  /**
+   * HW configuration for the binding. The bool representing the
+   * do/don't bind.
+ */
+  HW::item<bool> m_binding;
+
+  /**
+   * HW configuration for the VTR option
+   */
+  HW::item<l2_vtr_op_t> 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<const handle_t, l2_binding> 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 (file)
index 0000000..a6ed136
--- /dev/null
@@ -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<bool>& 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<bool>& 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<l2_vtr_op_t>& 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 (file)
index 0000000..0d5f414
--- /dev/null
@@ -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::key_type_t, l3_binding> 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>
+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>
+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<std::shared_ptr<l3_binding>>
+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<std::shared_ptr<l3_binding>> 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 (file)
index 0000000..2166b44
--- /dev/null
@@ -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 <vapi/ip.api.vapi.hpp>
+
+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<l3_binding> 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<interface::key_type, route::prefix_t> key_type_t;
+
+  /**
+   * Find an singular instance in the DB for the interface passed
+   */
+  static std::deque<std::shared_ptr<l3_binding>> find(const interface& i);
+
+  /**
+   * A functor class that binds the L3 config to the interface
+   */
+  class bind_cmd
+    : public rpc_cmd<HW::item<bool>, rc_t, vapi::Sw_interface_add_del_address>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    bind_cmd(HW::item<bool>& 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<HW::item<bool>, rc_t, vapi::Sw_interface_add_del_address>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    unbind_cmd(HW::item<bool>& 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<vapi::Ip_address_dump>
+  {
+  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<bool> 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<l3_binding> 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<key_type_t, l3_binding>;
+
+  /**
+   * 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<interface> 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<bool> m_binding;
+
+  /**
+   * A map of all L3 configs keyed against a combination of the interface
+   * and subnet's keys.
+   */
+  static singular_db<key_type_t, l3_binding> 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 (file)
index 0000000..788d30f
--- /dev/null
@@ -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<bool>& 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<bool>& 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 (file)
index 0000000..453f57d
--- /dev/null
@@ -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<interface::key_type, lldp_binding> 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>
+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>
+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 (file)
index 0000000..993a2fd
--- /dev/null
@@ -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 <vapi/lldp.api.vapi.hpp>
+
+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<lldp_binding> 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<HW::item<bool>, rc_t, vapi::Sw_interface_set_lldp>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    bind_cmd(HW::item<bool>& 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<HW::item<bool>, rc_t, vapi::Sw_interface_set_lldp>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    unbind_cmd(HW::item<bool>& 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<lldp_binding> 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<interface::key_type, lldp_binding>;
+
+  /**
+   * 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<interface> 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<bool> m_binding;
+
+  /**
+   * A map of all Lldp bindings keyed against the interface.
+   */
+  static singular_db<interface::key_type, lldp_binding> 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 (file)
index 0000000..9718c59
--- /dev/null
@@ -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<bool>& 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<bool>& 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 (file)
index 0000000..9252618
--- /dev/null
@@ -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<std::string, lldp_global> 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>
+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>
+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 (file)
index 0000000..9426ba0
--- /dev/null
@@ -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 <vapi/lldp.api.vapi.hpp>
+
+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<lldp_global> 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<HW::item<bool>, rc_t, vapi::Lldp_config>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    config_cmd(HW::item<bool>& 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<lldp_global> 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<interface::key_type, lldp_global>;
+
+  /**
+   * 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<bool> 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<std::string, lldp_global> 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 (file)
index 0000000..9d44a7c
--- /dev/null
@@ -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<bool>& 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 (file)
index 0000000..871473a
--- /dev/null
@@ -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 <chrono>
+#include <ctime>
+
+#include <boost/algorithm/string.hpp>
+
+#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<log_level_t>(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<std::string> 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 (file)
index 0000000..0adc677
--- /dev/null
@@ -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 <fstream>
+#include <iostream>
+
+#include "vom/enum_base.hpp"
+
+namespace VOM {
+struct log_level_t : enum_base<log_level_t>
+{
+  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 (file)
index 0000000..beb76bd
--- /dev/null
@@ -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<const nat_binding::key_t, nat_binding> 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>
+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>
+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 (file)
index 0000000..6e871f9
--- /dev/null
@@ -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 <vapi/nat.api.vapi.hpp>
+
+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<zone_t>
+  {
+    /**
+     * 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<interface::key_type, direction_t, l3_proto_t> 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<nat_binding> 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<HW::item<bool>,
+                     rc_t,
+                     vapi::Nat44_interface_add_del_feature>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    bind_44_input_cmd(HW::item<bool>& 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<HW::item<bool>,
+                     rc_t,
+                     vapi::Nat44_interface_add_del_feature>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    unbind_44_input_cmd(HW::item<bool>& 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<vapi::Nat44_interface_dump>
+  {
+  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<bool> 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<nat_binding> 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<const key_t, nat_binding>;
+
+  /**
+   * 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<bool> 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<interface> 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<const key_t, nat_binding> 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 (file)
index 0000000..38630c1
--- /dev/null
@@ -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<bool>& 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<bool>& 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 (file)
index 0000000..afc46f2
--- /dev/null
@@ -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::key_t, nat_static> 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>
+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>
+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<interface> itf, */
+/*                            const l3_proto_t &proto) */
+/* { */
+/*     /\* */
+/*      * dump VPP current states */
+/*      *\/ */
+/*     std::shared_ptr<nat_static::dump_cmd> cmd = */
+/*         std::make_shared<nat_static::dump_cmd>(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 (file)
index 0000000..1aa4a5e
--- /dev/null
@@ -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 <vapi/nat.api.vapi.hpp>
+
+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<route::table_id_t, boost::asio::ip::address> 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<nat_static> singular() const;
+
+  /**
+   * Find the instnace of the bridge_domain domain in the OM
+   */
+  static std::shared_ptr<nat_static> 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<HW::item<bool>, rc_t, vapi::Nat44_add_del_static_mapping>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    create_44_cmd(HW::item<bool>& 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<HW::item<bool>, rc_t, vapi::Nat44_add_del_static_mapping>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    delete_44_cmd(HW::item<bool>& 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<vapi::Nat44_static_mapping_dump>
+  {
+  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<bool> 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<nat_static> 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<key_t, nat_static>;
+
+  /**
+   * Sweep/reap the object if still stale
+   */
+  void sweep(void);
+
+  /**
+   * HW configuration for the result of creating the bridge_domain
+   */
+  HW::item<bool> m_hw;
+
+  /**
+   * The table-ID the outside address resides in
+   */
+  std::shared_ptr<route_domain> 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<key_t, nat_static> 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 (file)
index 0000000..4325f37
--- /dev/null
@@ -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<bool>& 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<bool>& 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 (file)
index 0000000..c4dcf1f
--- /dev/null
@@ -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::key_t, neighbour> 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>
+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>
+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<interface> itf,
+                      const l3_proto_t& proto)
+{
+  /*
+ * dump VPP current states
+ */
+  std::shared_ptr<neighbour::dump_cmd> cmd =
+    std::make_shared<neighbour::dump_cmd>(
+      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 (file)
index 0000000..141b98b
--- /dev/null
@@ -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 <vapi/l2.api.vapi.hpp>
+
+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<interface::key_type,
+                     mac_address_t,
+                     boost::asio::ip::address>
+    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<neighbour> singular() const;
+
+  /**
+   * Find the instnace of the bridge_domain domain in the OM
+   */
+  static std::shared_ptr<neighbour> 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<HW::item<bool>, rc_t, vapi::Ip_neighbor_add_del>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    create_cmd(HW::item<bool>& 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<HW::item<bool>, rc_t, vapi::Ip_neighbor_add_del>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    delete_cmd(HW::item<bool>& 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<vapi::Ip_neighbor_dump>
+  {
+  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<bool> 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<interface> itf,
+                         const l3_proto_t& proto);
+
+  /**
+   * Find or add the instnace of the bridge_domain domain in the OM
+   */
+  static std::shared_ptr<neighbour> 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<key_t, neighbour>;
+
+  /**
+   * Sweep/reap the object if still stale
+   */
+  void sweep(void);
+
+  /**
+   * HW configuration for the result of creating the bridge_domain
+   */
+  HW::item<bool> m_hw;
+
+  /**
+   * The bridge_domain domain the bridge_domain is in.
+   */
+  std::shared_ptr<interface> 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<key_t, neighbour> 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 (file)
index 0000000..2062bc3
--- /dev/null
@@ -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<bool>& 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<bool>& 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 (file)
index 0000000..6ab4ee5
--- /dev/null
@@ -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<object_base> 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_base>
+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 (file)
index 0000000..2edafc5
--- /dev/null
@@ -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 <memory>
+#include <string>
+
+#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<object_base> obj);
+
+  /**
+   * less than operator
+   */
+  bool operator<(const object_ref& other) const;
+
+  /**
+   * Return the shared pointer
+   */
+  std::shared_ptr<object_base> 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<object_base> 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 (file)
index 0000000..f82fee3
--- /dev/null
@@ -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 <algorithm>
+
+#include "vom/om.hpp"
+
+namespace VOM {
+client_db* OM::m_db;
+
+std::unique_ptr<OM::listener_list> 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 (file)
index 0000000..f470c5e
--- /dev/null
@@ -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 <memory>
+#include <set>
+
+#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 <typename OBJ>
+  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 <typename OBJ>
+  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<OBJ> 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*, listener_comparator_t> listener_list;
+
+  /**
+   * The listeners for events
+   */
+  static std::unique_ptr<listener_list> 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 (file)
index 0000000..24fb57b
--- /dev/null
@@ -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 <boost/algorithm/string.hpp>
+#include <sstream>
+
+#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<l3_proto_t>(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<nh_proto_t>(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<uint8_t, 16> a;
+    std::copy(bytes, bytes + 16, std::begin(a));
+    boost::asio::ip::address_v6 v6(a);
+    addr = v6;
+  } else {
+    std::array<uint8_t, 4> 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 (file)
index 0000000..3277929
--- /dev/null
@@ -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 <boost/asio/ip/address.hpp>
+
+#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<l3_proto_t>
+{
+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<nh_proto_t>
+{
+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 (file)
index 0000000..de424a4
--- /dev/null
@@ -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 <sstream>
+
+#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 (file)
index 0000000..505ccb3
--- /dev/null
@@ -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 <vapi/ip.api.vapi.hpp>
+
+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.
+   * '<lifetime>' must be greater than '<max-interval>'.
+   * 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 (file)
index 0000000..1cf0963
--- /dev/null
@@ -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 <sstream>
+
+#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 (file)
index 0000000..580aae1
--- /dev/null
@@ -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 <vapi/ip.api.vapi.hpp>
+
+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;
+
+  /**
+   * <valid-lifetime>' 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;
+
+  /**
+   * '<pref-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 (file)
index 0000000..e239f8c
--- /dev/null
@@ -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 <vapi/ip.api.vapi.hpp>
+
+namespace VOM {
+namespace route {
+singular_db<ip_route::key_t, ip_route> 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<path::special_t>(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<int>(m_weight)
+    << " preference:" << static_cast<int>(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>
+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>
+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<ip_route::dump_v4_cmd> cmd_v4(new ip_route::dump_v4_cmd());
+  std::shared_ptr<ip_route::dump_v6_cmd> 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<route_domain> 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<interface> 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<route_domain> 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<interface> 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 (file)
index 0000000..d175bee
--- /dev/null
@@ -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 <vapi/ip.api.vapi.hpp>
+
+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<special_t>
+  {
+  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<route_domain> m_rd;
+
+  /**
+   * The next-hop interface [if present].
+   */
+  std::shared_ptr<interface> 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> 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<route::table_id_t, prefix_t> 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<ip_route> 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<ip_route> 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<HW::item<bool>, rc_t, vapi::Ip_add_del_route>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    update_cmd(HW::item<bool>& 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<HW::item<bool>, rc_t, vapi::Ip_add_del_route>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    delete_cmd(HW::item<bool>& 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<vapi::Ip_fib_dump>
+  {
+  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<bool> item;
+  };
+
+  /**
+   * A cmd class that Dumps ipv6 fib
+   */
+  class dump_v6_cmd : public VOM::dump_cmd<vapi::Ip6_fib_dump>
+  {
+  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<bool> 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<ip_route> 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<key_t, ip_route>;
+
+  /**
+   * 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<bool> m_hw;
+
+  /**
+   * The route domain the route is in.
+   */
+  std::shared_ptr<route_domain> 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<key_t, ip_route> 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 (file)
index 0000000..66dd286
--- /dev/null
@@ -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 <sstream>
+
+#include "vom/route.hpp"
+
+namespace VOM {
+namespace route {
+ip_route::update_cmd::update_cmd(HW::item<bool>& 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<bool>& 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 (file)
index 0000000..8ae5785
--- /dev/null
@@ -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::table_id_t, route_domain> 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>
+route_domain::find(const route_domain& temp)
+{
+  std::shared_ptr<route_domain> 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>
+route_domain::get_default()
+{
+  route_domain rd(route::DEFAULT_TABLE);
+
+  return (find_or_add(rd));
+}
+
+std::shared_ptr<route_domain>
+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>
+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 (file)
index 0000000..5d3b891
--- /dev/null
@@ -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 <vapi/ip.api.vapi.hpp>
+
+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<route_domain> 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<route_domain> 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<route_domain> 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<HW::item<bool>, rc_t, vapi::Ip_table_add_del>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    create_cmd(HW::item<bool>& 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<HW::item<bool>, rc_t, vapi::Ip_table_add_del>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    delete_cmd(HW::item<bool>& 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<route_domain> 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<route::table_id_t, route_domain>;
+
+  /**
+   * Sweep/reap the object if still stale
+   */
+  void sweep(void);
+
+  /**
+   * HW configuration for the result of creating the v4 table
+   */
+  HW::item<bool> m_hw_v4;
+
+  /**
+   * HW configuration for the result of creating the v6 table
+   */
+  HW::item<bool> 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<route::table_id_t, route_domain> 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 (file)
index 0000000..c3f817f
--- /dev/null
@@ -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<bool>& 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<bool>& 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 (file)
index 0000000..60dbd47
--- /dev/null
@@ -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 <future>
+
+#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 <typename HWITEM, typename DATA, typename MSG>
+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<DATA> 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<DATA> 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 (file)
index 0000000..707389b
--- /dev/null
@@ -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 <memory>
+#include <ostream>
+
+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 <typename KEY, typename OBJ>
+class singular_db
+{
+public:
+  /**
+   * Constructor
+   */
+  singular_db() {}
+
+  /**
+   * Iterator
+   */
+  typedef
+    typename std::map<KEY, std::weak_ptr<OBJ>>::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 <typename DERIVED>
+  std::shared_ptr<OBJ> find_or_add(const KEY& key, const DERIVED& obj)
+  {
+    auto search = m_map.find(key);
+
+    if (search == m_map.end()) {
+      std::shared_ptr<OBJ> sp = std::make_shared<DERIVED>(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<OBJ> find(const KEY& key)
+  {
+    auto search = m_map.find(key);
+
+    if (search == m_map.end()) {
+      std::shared_ptr<OBJ> 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<OBJ> 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<OBJ> 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<KEY, std::weak_ptr<OBJ>> 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 (file)
index 0000000..de56bf2
--- /dev/null
@@ -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<cmd*>&
+sub_interface::mk_create_cmd(std::queue<cmd*>& q)
+{
+  q.push(new create_cmd(m_hdl, name(), m_parent->handle(), m_vlan));
+
+  return (q);
+}
+
+std::queue<cmd*>&
+sub_interface::mk_delete_cmd(std::queue<cmd*>& q)
+{
+  q.push(new delete_cmd(m_hdl));
+
+  return (q);
+}
+
+std::shared_ptr<sub_interface>
+sub_interface::singular() const
+{
+  return std::dynamic_pointer_cast<sub_interface>(singular_i());
+}
+
+std::shared_ptr<interface>
+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 (file)
index 0000000..d780c8a
--- /dev/null
@@ -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<sub_interface> singular() const;
+
+  /**
+   * A functor class that creates an interface
+   */
+  class create_cmd : public interface::create_cmd<vapi::Create_vlan_subif>
+  {
+  public:
+    /**
+     * Cstrunctor taking the reference to the parent
+     * and the sub-interface's VLAN
+     */
+    create_cmd(HW::item<handle_t>& 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<vapi::Delete_subif>
+  {
+  public:
+    /**
+     * Constructor
+     */
+    delete_cmd(HW::item<handle_t>& 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<interface> singular_i() const;
+
+  /**
+   * Virtual functions to construct an interface create commands.
+   */
+  virtual std::queue<cmd*>& mk_create_cmd(std::queue<cmd*>& cmds);
+
+  /**
+   * Virtual functions to construct an interface delete commands.
+   */
+  virtual std::queue<cmd*>& mk_delete_cmd(std::queue<cmd*>& 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<interface> 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 (file)
index 0000000..2d417c7
--- /dev/null
@@ -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 <vapi/vpe.api.vapi.hpp>
+
+namespace VOM {
+sub_interface::create_cmd::create_cmd(HW::item<handle_t>& item,
+                                      const std::string& name,
+                                      const handle_t& parent,
+                                      uint16_t vlan)
+  : interface::create_cmd<vapi::Create_vlan_subif>(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<handle_t>& item)
+  : interface::delete_cmd<vapi::Delete_subif>(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 (file)
index 0000000..d776c48
--- /dev/null
@@ -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 <vapi/vpe.api.vapi.hpp>
+
+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<cmd*>&
+tap_interface::mk_create_cmd(std::queue<cmd*>& q)
+{
+  q.push(new create_cmd(m_hdl, name(), m_prefix, m_l2_address));
+
+  return (q);
+}
+
+std::queue<cmd*>&
+tap_interface::mk_delete_cmd(std::queue<cmd*>& q)
+{
+  q.push(new delete_cmd(m_hdl));
+
+  return (q);
+}
+
+std::shared_ptr<tap_interface>
+tap_interface::singular() const
+{
+  return std::dynamic_pointer_cast<tap_interface>(singular_i());
+}
+
+std::shared_ptr<interface>
+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<tap_interface::dump_cmd> 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<const char*>(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 (file)
index 0000000..af90b7b
--- /dev/null
@@ -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<tap_interface> singular() const;
+
+  /**
+   * A functor class that creates an interface
+   */
+  class create_cmd : public interface::create_cmd<vapi::Tap_connect>
+  {
+  public:
+    create_cmd(HW::item<handle_t>& 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<vapi::Tap_delete>
+  {
+  public:
+    delete_cmd(HW::item<handle_t>& 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<vapi::Sw_interface_tap_dump>
+  {
+  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<interface> singular_i() const;
+
+  /**
+   * Virtual functions to construct an interface create commands.
+   */
+  virtual std::queue<cmd*>& mk_create_cmd(std::queue<cmd*>& cmds);
+
+  /**
+   * Virtual functions to construct an interface delete commands.
+   */
+  virtual std::queue<cmd*>& mk_delete_cmd(std::queue<cmd*>& 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 (file)
index 0000000..f616fd0
--- /dev/null
@@ -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 <vapi/tap.api.vapi.hpp>
+
+namespace VOM {
+tap_interface::create_cmd::create_cmd(HW::item<handle_t>& item,
+                                      const std::string& name,
+                                      route::prefix_t& prefix,
+                                      const l2_address_t& l2_address)
+  : interface::create_cmd<vapi::Tap_connect>(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<handle_t>& item)
+  : interface::delete_cmd<vapi::Tap_delete>(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 (file)
index 0000000..c362a60
--- /dev/null
@@ -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 <algorithm>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+
+#include "vom/types.hpp"
+
+namespace VOM {
+
+rc_t::rc_t(int v, const std::string s)
+  : enum_base<rc_t>(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<uint8_t> 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<uint8_t*>(&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<unsigned int>(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<uint8_t> 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<unsigned int>(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 (file)
index 0000000..fd3b24b
--- /dev/null
@@ -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 <array>
+#include <vector>
+
+#include <boost/asio/ip/address.hpp>
+
+#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>
+{
+  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<direction_t>
+{
+  /**
+   * 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<uint8_t> 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<uint8_t, 6> 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<uint8_t> 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<uint8_t> 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 (file)
index 0000000..7079021
--- /dev/null
@@ -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::endpoint_t, vxlan_tunnel> 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>
+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<vxlan_tunnel> 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>
+vxlan_tunnel::singular() const
+{
+  return (find_or_add(*this));
+}
+
+std::shared_ptr<interface>
+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<vxlan_tunnel::dump_cmd> 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 (file)
index 0000000..845cd6a
--- /dev/null
@@ -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 <vapi/vxlan.api.vapi.hpp>
+
+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<vxlan_tunnel> 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<vapi::Vxlan_add_del_tunnel>
+  {
+  public:
+    /**
+     * Create command constructor taking HW item to update and the
+     * endpoint values
+     */
+    create_cmd(HW::item<handle_t>& 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<vapi::Vxlan_add_del_tunnel>
+  {
+  public:
+    /**
+     * delete command constructor taking HW item to update and the
+     * endpoint values
+     */
+    delete_cmd(HW::item<handle_t>& 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<vapi::Vxlan_tunnel_dump>
+  {
+  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<interface> singular_i() const;
+
+  /**
+   * Find the VXLAN tunnel in the OM
+   */
+  static std::shared_ptr<vxlan_tunnel> 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<endpoint_t, vxlan_tunnel>;
+
+  /**
+   * 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<endpoint_t, vxlan_tunnel> 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 (file)
index 0000000..9a4a131
--- /dev/null
@@ -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<handle_t>& item,
+                                     const std::string& name,
+                                     const endpoint_t& ep)
+  : interface::create_cmd<vapi::Vxlan_add_del_tunnel>(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<handle_t>& item,
+                                     const endpoint_t& ep)
+  : interface::delete_cmd<vapi::Vxlan_add_del_tunnel>(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:
+ */
index 1bd035d..5801553 100644 (file)
@@ -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 (file)
index 0000000..4c72fbc
--- /dev/null
@@ -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 <boost/test/unit_test.hpp>
+#include <boost/assign/list_inserter.hpp>
+
+
+#include <iostream>
+#include <deque>
+
+#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<cmd*> &cmds)
+    {
+        while (cmds.size())
+        {
+            m_act_queue.push_back(cmds.front());
+            cmds.pop();
+        }
+    }
+    void enqueue(std::shared_ptr<cmd> f)
+    {
+        m_act_queue.push_back(f.get());
+    }
+
+    void dequeue(cmd *f)
+    {
+    }
+
+    void dequeue(std::shared_ptr<cmd> 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<interface::af_packet_create_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(interface::loopback_create_cmd))
+                    {
+                        rc = handle_derived<interface::loopback_create_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(interface::loopback_delete_cmd))
+                    {
+                        rc = handle_derived<interface::loopback_delete_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(interface::af_packet_delete_cmd))
+                    {
+                        rc = handle_derived<interface::af_packet_delete_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(interface::state_change_cmd))
+                    {
+                        rc = handle_derived<interface::state_change_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(interface::set_table_cmd))
+                    {
+                        rc = handle_derived<interface::set_table_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(interface::set_mac_cmd))
+                    {
+                        rc = handle_derived<interface::set_mac_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(interface::set_tag))
+                    {
+                        rc = handle_derived<interface::set_tag>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(route_domain::create_cmd))
+                    {
+                       rc = handle_derived<route_domain::create_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(route_domain::delete_cmd))
+                    {
+                        rc = handle_derived<route_domain::delete_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(route::ip_route::update_cmd))
+                    {
+                       rc = handle_derived<route::ip_route::update_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(route::ip_route::delete_cmd))
+                    {
+                        rc = handle_derived<route::ip_route::delete_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(neighbour::create_cmd))
+                    {
+                       rc = handle_derived<neighbour::create_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(neighbour::delete_cmd))
+                    {
+                        rc = handle_derived<neighbour::delete_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(l3_binding::bind_cmd))
+                    {
+                        rc = handle_derived<l3_binding::bind_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(l3_binding::unbind_cmd))
+                    {
+                        rc = handle_derived<l3_binding::unbind_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(bridge_domain::create_cmd))
+                    {
+                        rc = handle_derived<bridge_domain::create_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(bridge_domain::delete_cmd))
+                    {
+                        rc = handle_derived<bridge_domain::delete_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(bridge_domain_entry::create_cmd))
+                    {
+                        rc = handle_derived<bridge_domain_entry::create_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(bridge_domain_entry::delete_cmd))
+                    {
+                        rc = handle_derived<bridge_domain_entry::delete_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(bridge_domain_arp_entry::create_cmd))
+                    {
+                        rc = handle_derived<bridge_domain_arp_entry::create_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(bridge_domain_arp_entry::delete_cmd))
+                    {
+                        rc = handle_derived<bridge_domain_arp_entry::delete_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(l2_binding::bind_cmd))
+                    {
+                        rc = handle_derived<l2_binding::bind_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(l2_binding::unbind_cmd))
+                    {
+                        rc = handle_derived<l2_binding::unbind_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(l2_binding::set_vtr_op_cmd))
+                    {
+                        rc = handle_derived<l2_binding::set_vtr_op_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(vxlan_tunnel::create_cmd))
+                    {
+                        rc = handle_derived<vxlan_tunnel::create_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(vxlan_tunnel::delete_cmd))
+                    {
+                        rc = handle_derived<vxlan_tunnel::delete_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(sub_interface::create_cmd))
+                    {
+                        rc = handle_derived<sub_interface::create_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(sub_interface::delete_cmd))
+                    {
+                        rc = handle_derived<sub_interface::delete_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(ACL::l3_list::update_cmd))
+                    {
+                        rc = handle_derived<ACL::l3_list::update_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(ACL::l3_list::delete_cmd))
+                    {
+                        rc = handle_derived<ACL::l3_list::delete_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(ACL::l3_binding::bind_cmd))
+                    {
+                        rc = handle_derived<ACL::l3_binding::bind_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(ACL::l3_binding::unbind_cmd))
+                    {
+                        rc = handle_derived<ACL::l3_binding::unbind_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(ACL::l2_list::update_cmd))
+                    {
+                        rc = handle_derived<ACL::l2_list::update_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(ACL::l2_list::delete_cmd))
+                    {
+                        rc = handle_derived<ACL::l2_list::delete_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(ACL::l2_binding::bind_cmd))
+                    {
+                        rc = handle_derived<ACL::l2_binding::bind_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(ACL::l2_binding::unbind_cmd))
+                    {
+                        rc = handle_derived<ACL::l2_binding::unbind_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(arp_proxy_binding::bind_cmd))
+                    {
+                        rc = handle_derived<arp_proxy_binding::bind_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(arp_proxy_binding::unbind_cmd))
+                    {
+                        rc = handle_derived<arp_proxy_binding::unbind_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(arp_proxy_config::config_cmd))
+                    {
+                        rc = handle_derived<arp_proxy_config::config_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(arp_proxy_config::unconfig_cmd))
+                    {
+                        rc = handle_derived<arp_proxy_config::unconfig_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(ip_unnumbered::config_cmd))
+                    {
+                        rc = handle_derived<ip_unnumbered::config_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(ip_unnumbered::unconfig_cmd))
+                    {
+                        rc = handle_derived<ip_unnumbered::unconfig_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(ip6nd_ra_config::config_cmd))
+                    {
+                        rc = handle_derived<ip6nd_ra_config::config_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(ip6nd_ra_config::unconfig_cmd))
+                    {
+                        rc = handle_derived<ip6nd_ra_config::unconfig_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(ip6nd_ra_prefix::config_cmd))
+                    {
+                        rc = handle_derived<ip6nd_ra_prefix::config_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(ip6nd_ra_prefix::unconfig_cmd))
+                    {
+                        rc = handle_derived<ip6nd_ra_prefix::unconfig_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(interface_span::config_cmd))
+                    {
+                        rc = handle_derived<interface_span::config_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(interface_span::unconfig_cmd))
+                    {
+                        rc = handle_derived<interface_span::unconfig_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(nat_static::create_44_cmd))
+                    {
+                        rc = handle_derived<nat_static::create_44_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(nat_static::delete_44_cmd))
+                    {
+                        rc = handle_derived<nat_static::delete_44_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(nat_binding::bind_44_input_cmd))
+                    {
+                        rc = handle_derived<nat_binding::bind_44_input_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(nat_binding::unbind_44_input_cmd))
+                    {
+                        rc = handle_derived<nat_binding::unbind_44_input_cmd>(f_exp, f_act);
+                    }
+                    else if (typeid(*f_exp) == typeid(interface::events_cmd))
+                    {
+                        rc = handle_derived<interface::events_cmd>(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 <typename T>
+    rc_t handle_derived(const cmd *f_exp, cmd *f_act)
+    {
+        const T *i_exp;
+        T *i_act;
+
+        i_exp = dynamic_cast<const T*>(f_exp);
+        i_act = dynamic_cast<T*>(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<cmd*> m_exp_queue;
+
+    // the queue to push the actual events on
+    std::deque<cmd*> 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<handle_t> hw_ifh(2, rc_t::OK);
+    ADD_EXPECT(interface::af_packet_create_cmd(hw_ifh, itf1_name));
+
+    HW::item<interface::admin_state_t> 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<interface::admin_state_t> 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<handle_t> 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<interface::admin_state_t> hw_as_up(interface::admin_state_t::UP,
+                                                rc_t::OK);
+    HW::item<interface::admin_state_t> 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<handle_t> hw_ifh(4, rc_t::OK);
+    HW::item<route::prefix_t> 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<bool> hw_l3_bind(true, rc_t::OK);
+    HW::item<bool> 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<l2_address_t> 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<l2_address_t> 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<bool> hw_rd4_create(true, rc_t::OK);
+    HW::item<bool> hw_rd4_delete(false, rc_t::OK);
+    HW::item<bool> hw_rd6_create(true, rc_t::OK);
+    HW::item<bool> hw_rd6_delete(false, rc_t::OK);
+    HW::item<route::table_id_t> hw_rd4_bind(1, rc_t::OK);
+    HW::item<route::table_id_t> hw_rd4_unbind(route::DEFAULT_TABLE, rc_t::OK);
+    HW::item<route::table_id_t> hw_rd6_bind(1, rc_t::OK);
+    HW::item<route::table_id_t> 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<handle_t> 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<handle_t> hw_ifh(3, rc_t::OK);
+    HW::item<interface::admin_state_t> 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<uint32_t> 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<bool> 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<handle_t> 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<l2_binding::l2_vtr_op_t> 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<bool> 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<bool> 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<interface::admin_state_t> 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<handle_t> 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<uint32_t> 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<bool> 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<handle_t> 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<handle_t> hw_ifh(2, rc_t::OK);
+    ADD_EXPECT(interface::af_packet_create_cmd(hw_ifh, itf1_name));
+
+    HW::item<interface::admin_state_t> 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<handle_t> 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<interface::admin_state_t> hw_as_down(interface::admin_state_t::DOWN, rc_t::OK);
+    HW::item<handle_t> 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<handle_t> hw_ifh(2, rc_t::OK);
+    HW::item<interface::admin_state_t> 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<handle_t> 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<bool> 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<handle_t> 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<bool> 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<interface::admin_state_t> 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<bool> 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<handle_t> hw_ifh(2, rc_t::OK);
+    HW::item<interface::admin_state_t> 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<bool> 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<interface::admin_state_t> 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<handle_t> hw_ifh(2, rc_t::OK);
+    HW::item<interface::admin_state_t> 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<bool> hw_l3_bind(true, rc_t::OK);
+    HW::item<bool> 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<handle_t> 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<bool> hw_ip_cfg(true, rc_t::OK);
+    HW::item<bool> 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<interface::admin_state_t> 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<handle_t> hw_ifh(3, rc_t::OK);
+    HW::item<interface::admin_state_t> 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<bool> hw_l3_bind(true, rc_t::OK);
+    HW::item<bool> 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<bool> hw_ip6nd_ra_config_config(true, rc_t::OK);
+    HW::item<bool> 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<bool> hw_ip6nd_ra_prefix_config(true, rc_t::OK);
+    HW::item<bool> 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<interface::admin_state_t> 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<handle_t> hw_ifh(2, rc_t::OK);
+    HW::item<interface::admin_state_t> 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<handle_t> hw_ifh2(4, rc_t::OK);
+    HW::item<interface::admin_state_t> 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<bool> hw_is_cfg(true, rc_t::OK);
+    HW::item<bool> 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<interface::admin_state_t> hw_as_down(interface::admin_state_t::DOWN, rc_t::OK);
+    HW::item<interface::admin_state_t> 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<bool> hw_rd4_create(true, rc_t::OK);
+    HW::item<bool> hw_rd4_delete(false, rc_t::OK);
+    HW::item<bool> hw_rd6_create(true, rc_t::OK);
+    HW::item<bool> hw_rd6_delete(false, rc_t::OK);
+    HW::item<route::table_id_t> hw_rd4_bind(1, rc_t::OK);
+    HW::item<route::table_id_t> hw_rd4_unbind(route::DEFAULT_TABLE, rc_t::OK);
+    HW::item<route::table_id_t> hw_rd6_bind(1, rc_t::OK);
+    HW::item<route::table_id_t> 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<handle_t> hw_ifh(2, rc_t::OK);
+    HW::item<interface::admin_state_t> hw_as_up(interface::admin_state_t::UP, rc_t::OK);
+    HW::item<interface::admin_state_t> 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<handle_t> hw_ifh2(4, rc_t::OK);
+    HW::item<interface::admin_state_t> hw_as_up2(interface::admin_state_t::UP, rc_t::OK);
+    HW::item<interface::admin_state_t> 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<bool> hw_l3_10_bind(true, rc_t::OK);
+    HW::item<bool> 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<bool> hw_l3_11_bind(true, rc_t::OK);
+    HW::item<bool> 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<bool> 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<bool> 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<bool> 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<bool> 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<handle_t> hw_ifh(2, rc_t::OK);
+    HW::item<interface::admin_state_t> hw_as_up(interface::admin_state_t::UP, rc_t::OK);
+    HW::item<interface::admin_state_t> 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<handle_t> hw_ifh2(4, rc_t::OK);
+    HW::item<interface::admin_state_t> hw_as_up2(interface::admin_state_t::UP, rc_t::OK);
+    HW::item<interface::admin_state_t> 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<bool> 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<bool> 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<bool> 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<bool> 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()
index 6446265..0c85bfb 100644 (file)
@@ -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
index 5f97232..b5820fa 100644 (file)
@@ -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 (file)
index 0000000..bfd7007
--- /dev/null
@@ -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)