summaryrefslogtreecommitdiffstats
path: root/extras
diff options
context:
space:
mode:
authorDamjan Marion <damarion@cisco.com>2018-04-25 19:11:15 +0200
committerNeale Ranns <nranns@cisco.com>2018-04-27 11:35:07 +0000
commit164e5f8c63652028ecb9c3570e1ea8618b163071 (patch)
tree7423f5abbc61b761df9a679c7974f17613736020 /extras
parentaa682a39b76ee043f65313f23e134bf18fe7a47e (diff)
Move VOM to extras/vom
Change-Id: Iea174f03dfba3bd06024db0f0cc373532300dcae Signed-off-by: Damjan Marion <damarion@cisco.com>
Diffstat (limited to 'extras')
-rw-r--r--extras/vom/Makefile.am5
-rw-r--r--extras/vom/configure.ac20
-rw-r--r--extras/vom/vom/.clang-format3
-rw-r--r--extras/vom/vom/Makefile.am232
-rw-r--r--extras/vom/vom/acl_binding.cpp175
-rw-r--r--extras/vom/vom/acl_binding.hpp244
-rw-r--r--extras/vom/vom/acl_binding_cmds.cpp197
-rw-r--r--extras/vom/vom/acl_binding_cmds.hpp195
-rw-r--r--extras/vom/vom/acl_ethertype.cpp251
-rw-r--r--extras/vom/vom/acl_ethertype.hpp247
-rw-r--r--extras/vom/vom/acl_ethertype_cmds.cpp169
-rw-r--r--extras/vom/vom/acl_ethertype_cmds.hpp146
-rw-r--r--extras/vom/vom/acl_l2_rule.cpp99
-rw-r--r--extras/vom/vom/acl_l2_rule.hpp115
-rw-r--r--extras/vom/vom/acl_l3_rule.cpp213
-rw-r--r--extras/vom/vom/acl_l3_rule.hpp196
-rw-r--r--extras/vom/vom/acl_list.cpp233
-rw-r--r--extras/vom/vom/acl_list.hpp307
-rw-r--r--extras/vom/vom/acl_list_cmds.cpp195
-rw-r--r--extras/vom/vom/acl_list_cmds.hpp223
-rw-r--r--extras/vom/vom/acl_types.cpp59
-rw-r--r--extras/vom/vom/acl_types.hpp68
-rw-r--r--extras/vom/vom/arp_proxy_binding.cpp147
-rw-r--r--extras/vom/vom/arp_proxy_binding.hpp164
-rw-r--r--extras/vom/vom/arp_proxy_binding_cmds.cpp107
-rw-r--r--extras/vom/vom/arp_proxy_binding_cmds.hpp101
-rw-r--r--extras/vom/vom/arp_proxy_config.cpp147
-rw-r--r--extras/vom/vom/arp_proxy_config.hpp165
-rw-r--r--extras/vom/vom/arp_proxy_config_cmds.cpp120
-rw-r--r--extras/vom/vom/arp_proxy_config_cmds.hpp108
-rw-r--r--extras/vom/vom/bond_group_binding.cpp179
-rw-r--r--extras/vom/vom/bond_group_binding.hpp186
-rw-r--r--extras/vom/vom/bond_group_binding_cmds.cpp147
-rw-r--r--extras/vom/vom/bond_group_binding_cmds.hpp138
-rw-r--r--extras/vom/vom/bond_interface.cpp197
-rw-r--r--extras/vom/vom/bond_interface.hpp197
-rw-r--r--extras/vom/vom/bond_interface_cmds.cpp138
-rw-r--r--extras/vom/vom/bond_interface_cmds.hpp111
-rw-r--r--extras/vom/vom/bond_member.cpp130
-rw-r--r--extras/vom/vom/bond_member.hpp147
-rw-r--r--extras/vom/vom/bridge_domain.cpp257
-rw-r--r--extras/vom/vom/bridge_domain.hpp264
-rw-r--r--extras/vom/vom/bridge_domain_arp_entry.cpp193
-rw-r--r--extras/vom/vom/bridge_domain_arp_entry.hpp195
-rw-r--r--extras/vom/vom/bridge_domain_arp_entry_cmds.cpp125
-rw-r--r--extras/vom/vom/bridge_domain_arp_entry_cmds.hpp107
-rw-r--r--extras/vom/vom/bridge_domain_cmds.cpp148
-rw-r--r--extras/vom/vom/bridge_domain_cmds.hpp146
-rw-r--r--extras/vom/vom/bridge_domain_entry.cpp224
-rw-r--r--extras/vom/vom/bridge_domain_entry.hpp192
-rw-r--r--extras/vom/vom/bridge_domain_entry_cmds.cpp157
-rw-r--r--extras/vom/vom/bridge_domain_entry_cmds.hpp143
-rw-r--r--extras/vom/vom/client_db.cpp59
-rw-r--r--extras/vom/vom/client_db.hpp89
-rw-r--r--extras/vom/vom/cmd.cpp37
-rw-r--r--extras/vom/vom/cmd.hpp80
-rw-r--r--extras/vom/vom/connection.cpp61
-rw-r--r--extras/vom/vom/connection.hpp82
-rw-r--r--extras/vom/vom/dhcp_config.cpp194
-rw-r--r--extras/vom/vom/dhcp_config.hpp234
-rw-r--r--extras/vom/vom/dhcp_config_cmds.cpp178
-rw-r--r--extras/vom/vom/dhcp_config_cmds.hpp172
-rw-r--r--extras/vom/vom/dump_cmd.hpp155
-rw-r--r--extras/vom/vom/enum_base.hpp105
-rw-r--r--extras/vom/vom/event_cmd.hpp117
-rw-r--r--extras/vom/vom/gbp_contract.cpp194
-rw-r--r--extras/vom/vom/gbp_contract.hpp188
-rw-r--r--extras/vom/vom/gbp_contract_cmds.cpp145
-rw-r--r--extras/vom/vom/gbp_contract_cmds.hpp137
-rw-r--r--extras/vom/vom/gbp_endpoint.cpp188
-rw-r--r--extras/vom/vom/gbp_endpoint.hpp193
-rw-r--r--extras/vom/vom/gbp_endpoint_cmds.cpp152
-rw-r--r--extras/vom/vom/gbp_endpoint_cmds.hpp142
-rw-r--r--extras/vom/vom/gbp_endpoint_group.cpp198
-rw-r--r--extras/vom/vom/gbp_endpoint_group.hpp205
-rw-r--r--extras/vom/vom/gbp_endpoint_group_cmds.cpp147
-rw-r--r--extras/vom/vom/gbp_endpoint_group_cmds.hpp139
-rw-r--r--extras/vom/vom/gbp_recirc.cpp200
-rw-r--r--extras/vom/vom/gbp_recirc.hpp209
-rw-r--r--extras/vom/vom/gbp_recirc_cmds.cpp142
-rw-r--r--extras/vom/vom/gbp_recirc_cmds.hpp137
-rw-r--r--extras/vom/vom/gbp_subnet.cpp242
-rw-r--r--extras/vom/vom/gbp_subnet.hpp218
-rw-r--r--extras/vom/vom/gbp_subnet_cmds.cpp160
-rw-r--r--extras/vom/vom/gbp_subnet_cmds.hpp144
-rw-r--r--extras/vom/vom/hw.cpp281
-rw-r--r--extras/vom/vom/hw.hpp379
-rw-r--r--extras/vom/vom/hw_cmds.cpp56
-rw-r--r--extras/vom/vom/hw_cmds.hpp64
-rw-r--r--extras/vom/vom/inspect.cpp107
-rw-r--r--extras/vom/vom/inspect.hpp96
-rw-r--r--extras/vom/vom/interface.cpp643
-rw-r--r--extras/vom/vom/interface.hpp659
-rw-r--r--extras/vom/vom/interface_cmds.cpp757
-rw-r--r--extras/vom/vom/interface_cmds.hpp615
-rw-r--r--extras/vom/vom/interface_factory.cpp172
-rw-r--r--extras/vom/vom/interface_factory.hpp57
-rw-r--r--extras/vom/vom/interface_ip6_nd.hpp382
-rw-r--r--extras/vom/vom/interface_ip6_nd_cmds.cpp102
-rw-r--r--extras/vom/vom/interface_span.cpp219
-rw-r--r--extras/vom/vom/interface_span.hpp222
-rw-r--r--extras/vom/vom/interface_span_cmds.cpp155
-rw-r--r--extras/vom/vom/interface_span_cmds.hpp154
-rw-r--r--extras/vom/vom/interface_types.cpp115
-rw-r--r--extras/vom/vom/ip_unnumbered.cpp142
-rw-r--r--extras/vom/vom/ip_unnumbered.hpp175
-rw-r--r--extras/vom/vom/ip_unnumbered_cmds.cpp117
-rw-r--r--extras/vom/vom/ip_unnumbered_cmds.hpp114
-rw-r--r--extras/vom/vom/l2_binding.cpp241
-rw-r--r--extras/vom/vom/l2_binding.hpp221
-rw-r--r--extras/vom/vom/l2_binding_cmds.cpp169
-rw-r--r--extras/vom/vom/l2_binding_cmds.hpp170
-rw-r--r--extras/vom/vom/l2_emulation.cpp168
-rw-r--r--extras/vom/vom/l2_emulation.hpp180
-rw-r--r--extras/vom/vom/l2_emulation_cmds.cpp107
-rw-r--r--extras/vom/vom/l2_emulation_cmds.hpp102
-rw-r--r--extras/vom/vom/l3_binding.cpp229
-rw-r--r--extras/vom/vom/l3_binding.hpp214
-rw-r--r--extras/vom/vom/l3_binding_cmds.cpp158
-rw-r--r--extras/vom/vom/l3_binding_cmds.hpp158
-rw-r--r--extras/vom/vom/lldp_binding.cpp163
-rw-r--r--extras/vom/vom/lldp_binding.hpp182
-rw-r--r--extras/vom/vom/lldp_binding_cmds.cpp114
-rw-r--r--extras/vom/vom/lldp_binding_cmds.hpp112
-rw-r--r--extras/vom/vom/lldp_global.cpp162
-rw-r--r--extras/vom/vom/lldp_global.hpp183
-rw-r--r--extras/vom/vom/lldp_global_cmds.cpp76
-rw-r--r--extras/vom/vom/lldp_global_cmds.hpp80
-rw-r--r--extras/vom/vom/logger.cpp170
-rw-r--r--extras/vom/vom/logger.hpp194
-rw-r--r--extras/vom/vom/nat_binding.cpp280
-rw-r--r--extras/vom/vom/nat_binding.hpp232
-rw-r--r--extras/vom/vom/nat_binding_cmds.cpp377
-rw-r--r--extras/vom/vom/nat_binding_cmds.hpp383
-rw-r--r--extras/vom/vom/nat_static.cpp236
-rw-r--r--extras/vom/vom/nat_static.hpp195
-rw-r--r--extras/vom/vom/nat_static_cmds.cpp278
-rw-r--r--extras/vom/vom/nat_static_cmds.hpp248
-rw-r--r--extras/vom/vom/neighbour.cpp217
-rw-r--r--extras/vom/vom/neighbour.hpp193
-rw-r--r--extras/vom/vom/neighbour_cmds.cpp165
-rw-r--r--extras/vom/vom/neighbour_cmds.hpp145
-rw-r--r--extras/vom/vom/object_base.cpp70
-rw-r--r--extras/vom/vom/object_base.hpp146
-rw-r--r--extras/vom/vom/om.cpp156
-rw-r--r--extras/vom/vom/om.hpp356
-rw-r--r--extras/vom/vom/prefix.cpp431
-rw-r--r--extras/vom/vom/prefix.hpp267
-rw-r--r--extras/vom/vom/ra_config.cpp88
-rw-r--r--extras/vom/vom/ra_config.hpp145
-rw-r--r--extras/vom/vom/ra_prefix.cpp88
-rw-r--r--extras/vom/vom/ra_prefix.hpp134
-rw-r--r--extras/vom/vom/route.cpp547
-rw-r--r--extras/vom/vom/route.hpp407
-rw-r--r--extras/vom/vom/route_cmds.cpp227
-rw-r--r--extras/vom/vom/route_cmds.hpp173
-rw-r--r--extras/vom/vom/route_domain.cpp196
-rw-r--r--extras/vom/vom/route_domain.hpp193
-rw-r--r--extras/vom/vom/route_domain_cmds.cpp113
-rw-r--r--extras/vom/vom/route_domain_cmds.hpp114
-rw-r--r--extras/vom/vom/rpc_cmd.hpp152
-rw-r--r--extras/vom/vom/singular_db.hpp148
-rw-r--r--extras/vom/vom/singular_db_funcs.hpp52
-rw-r--r--extras/vom/vom/sub_interface.cpp114
-rw-r--r--extras/vom/vom/sub_interface.hpp110
-rw-r--r--extras/vom/vom/sub_interface_cmds.cpp115
-rw-r--r--extras/vom/vom/sub_interface_cmds.hpp108
-rw-r--r--extras/vom/vom/tap_interface.cpp151
-rw-r--r--extras/vom/vom/tap_interface.hpp120
-rw-r--r--extras/vom/vom/tap_interface_cmds.cpp132
-rw-r--r--extras/vom/vom/tap_interface_cmds.hpp102
-rw-r--r--extras/vom/vom/types.cpp335
-rw-r--r--extras/vom/vom/types.hpp408
-rw-r--r--extras/vom/vom/vxlan_tunnel.cpp266
-rw-r--r--extras/vom/vom/vxlan_tunnel.hpp234
-rw-r--r--extras/vom/vom/vxlan_tunnel_cmds.cpp157
-rw-r--r--extras/vom/vom/vxlan_tunnel_cmds.hpp135
177 files changed, 32390 insertions, 0 deletions
diff --git a/extras/vom/Makefile.am b/extras/vom/Makefile.am
new file mode 100644
index 00000000000..51e8dab63de
--- /dev/null
+++ b/extras/vom/Makefile.am
@@ -0,0 +1,5 @@
+
+
+AUTOMAKE_OPTIONS = foreign subdir-objects
+
+SUBDIRS = vom
diff --git a/extras/vom/configure.ac b/extras/vom/configure.ac
new file mode 100644
index 00000000000..0e15095e117
--- /dev/null
+++ b/extras/vom/configure.ac
@@ -0,0 +1,20 @@
+AC_INIT(vom, 18.07)
+LT_INIT
+AM_INIT_AUTOMAKE
+AM_SILENT_RULES([yes])
+AC_PREFIX_DEFAULT([/usr])
+
+PKG_CHECK_MODULES([CHECK], [check], [HAVE_CHECK=1], [HAVE_CHECK=0])
+AM_CONDITIONAL([USE_CHECK],[test "$HAVE_CHECK" -eq 1])
+
+AC_PROG_CC
+AC_PROG_CXX
+
+AM_CONDITIONAL(ENABLE_ACL_PLUGIN, test "yes" = "yes")
+AM_CONDITIONAL(ENABLE_NAT_PLUGIN, test "yes" = "yes")
+AM_CONDITIONAL(ENABLE_L2E_PLUGIN, test "yes" = "yes")
+AM_CONDITIONAL(ENABLE_GBP_PLUGIN, test "yes" = "yes")
+
+AC_OUTPUT([Makefile vom/Makefile])
+
+AC_CONFIG_MACRO_DIR([m4])
diff --git a/extras/vom/vom/.clang-format b/extras/vom/vom/.clang-format
new file mode 100644
index 00000000000..917dceb793d
--- /dev/null
+++ b/extras/vom/vom/.clang-format
@@ -0,0 +1,3 @@
+
+BasedOnStyle: mozilla
+BinPackParameters: false
diff --git a/extras/vom/vom/Makefile.am b/extras/vom/vom/Makefile.am
new file mode 100644
index 00000000000..892f437976e
--- /dev/null
+++ b/extras/vom/vom/Makefile.am
@@ -0,0 +1,232 @@
+# 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 -Werror -std=gnu++11 -I${top_srcdir}
+
+bin_PROGRAMS =
+noinst_LTLIBRARIES =
+CLEANDIRS =
+
+lib_LTLIBRARIES = libvom.la
+
+libvom_la_DEPENDENCIES =
+libvom_la_LIBADD = \
+ -lvapiclient \
+ -lpthread \
+ -lboost_thread \
+ $(BOOST_SYSTEM_LIB) \
+ $(BOOST_FILESYSTEM_LIB) \
+ $(BOOST_ASIO_LIB) \
+ -lm -lrt
+
+ACL_SOURCES =
+if ENABLE_ACL_PLUGIN
+ACL_SOURCES += \
+ acl_binding_cmds.cpp \
+ acl_binding.cpp \
+ acl_ethertype_cmds.cpp \
+ acl_ethertype.cpp \
+ acl_l2_rule.cpp \
+ acl_l3_rule.cpp \
+ acl_list_cmds.cpp \
+ acl_list.cpp \
+ acl_types.cpp
+endif
+
+NAT_SOURCES =
+if ENABLE_NAT_PLUGIN
+NAT_SOURCES += \
+ nat_static.cpp \
+ nat_static_cmds.cpp \
+ nat_binding.cpp \
+ nat_binding_cmds.cpp
+endif
+
+L2E_SOURCES =
+if ENABLE_L2E_PLUGIN
+L2E_SOURCES += \
+ l2_emulation_cmds.cpp \
+ l2_emulation.cpp
+endif
+
+GBP_SOURCES =
+if ENABLE_GBP_PLUGIN
+GBP_SOURCES += \
+ gbp_recirc_cmds.cpp \
+ gbp_recirc.cpp \
+ gbp_subnet_cmds.cpp \
+ gbp_subnet.cpp \
+ gbp_endpoint_cmds.cpp \
+ gbp_endpoint.cpp \
+ gbp_endpoint_group_cmds.cpp \
+ gbp_endpoint_group.cpp \
+ gbp_contract_cmds.cpp \
+ gbp_contract.cpp
+endif
+
+libvom_la_SOURCES = \
+ types.cpp \
+ arp_proxy_binding_cmds.cpp \
+ arp_proxy_binding.cpp \
+ arp_proxy_config_cmds.cpp \
+ arp_proxy_config.cpp \
+ bond_group_binding_cmds.cpp \
+ bond_group_binding.cpp \
+ bond_interface_cmds.cpp \
+ bond_interface.cpp \
+ bond_member.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_cmds.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 \
+ 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 \
+ vxlan_tunnel_cmds.cpp \
+ vxlan_tunnel.cpp \
+ $(ACL_SOURCES) \
+ $(NAT_SOURCES) \
+ $(L2E_SOURCES) \
+ $(GBP_SOURCES)
+
+
+vomincludedir = $(includedir)/vom
+
+ACL_INCLUDES =
+if ENABLE_ACL_PLUGIN
+ACL_INCLUDES += \
+ acl_binding.hpp \
+ acl_ethertype.hpp \
+ acl_l2_rule.hpp \
+ acl_l3_rule.hpp \
+ acl_list.hpp \
+ acl_types.hpp
+endif
+
+NAT_INCLUDES =
+if ENABLE_NAT_PLUGIN
+NAT_INCLUDES += \
+ nat_static.hpp \
+ nat_binding.hpp
+endif
+
+L2E_INCLUDES =
+if ENABLE_L2E_PLUGIN
+L2E_INCLUDES += \
+ l2_emulation.hpp
+endif
+
+GBP_INCLUDES =
+if ENABLE_GBP_PLUGIN
+GBP_INCLUDES += \
+ gbp_endpoint.hpp \
+ gbp_endpoint_group.hpp \
+ gbp_subnet.hpp \
+ gbp_recirc.hpp \
+ gbp_contract.hpp
+endif
+
+vominclude_HEADERS = \
+ arp_proxy_binding.hpp \
+ arp_proxy_config.hpp \
+ bond_group_binding.hpp \
+ bond_interface.hpp \
+ bond_member.hpp \
+ bridge_domain.hpp \
+ bridge_domain_arp_entry.hpp \
+ bridge_domain_entry.hpp \
+ client_db.hpp \
+ cmd.hpp \
+ connection.hpp \
+ dhcp_config.hpp \
+ dhcp_config_cmds.hpp \
+ dump_cmd.hpp \
+ enum_base.hpp \
+ event_cmd.hpp \
+ hw.hpp \
+ inspect.hpp \
+ interface.hpp \
+ interface_cmds.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 \
+ 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 \
+ singular_db_funcs.hpp \
+ sub_interface.hpp \
+ tap_interface.hpp \
+ types.hpp \
+ vxlan_tunnel.hpp \
+ $(ACL_INCLUDES) \
+ $(NAT_INCLUDES) \
+ $(L2E_INCLUDES) \
+ $(GBP_INCLUDES)
+
+# vi:syntax=automake
diff --git a/extras/vom/vom/acl_binding.cpp b/extras/vom/vom/acl_binding.cpp
new file mode 100644
index 00000000000..73f015df377
--- /dev/null
+++ b/extras/vom/vom/acl_binding.cpp
@@ -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.
+ */
+
+#include "vom/acl_binding.hpp"
+#include "vom/acl_binding_cmds.hpp"
+
+namespace VOM {
+namespace ACL {
+template <>
+l2_binding::event_handler::event_handler()
+{
+ OM::register_listener(this);
+ inspect::register_handler({ "l2-acl-binding" }, "L2 ACL bindings", this);
+}
+
+template <>
+void
+l2_binding::event_handler::handle_populate(const client_db::key_t& key)
+{
+ /*
+ * dump VPP Bridge domains
+ */
+ std::shared_ptr<binding_cmds::l2_dump_cmd> cmd =
+ std::make_shared<binding_cmds::l2_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 <>
+l3_binding::event_handler::event_handler()
+{
+ OM::register_listener(this);
+ inspect::register_handler({ "l3-acl-binding" }, "L3 ACL bindings", this);
+}
+
+template <>
+void
+l3_binding::event_handler::handle_populate(const client_db::key_t& key)
+{
+ std::shared_ptr<binding_cmds::l3_dump_cmd> cmd =
+ std::make_shared<binding_cmds::l3_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);
+ }
+ }
+ }
+}
+
+template <>
+void
+l3_binding::update(const binding& obj)
+{
+ if (!m_binding) {
+ HW::enqueue(new binding_cmds::l3_bind_cmd(
+ m_binding, m_direction, m_itf->handle(), m_acl->handle()));
+ }
+ HW::write();
+}
+
+template <>
+void
+l3_binding::sweep(void)
+{
+ if (m_binding) {
+ HW::enqueue(new binding_cmds::l3_unbind_cmd(
+ m_binding, m_direction, m_itf->handle(), m_acl->handle()));
+ }
+ HW::write();
+}
+
+template <>
+void
+l3_binding::replay(void)
+{
+ if (m_binding) {
+ HW::enqueue(new binding_cmds::l3_bind_cmd(
+ m_binding, m_direction, m_itf->handle(), m_acl->handle()));
+ }
+}
+
+template <>
+void
+l2_binding::update(const binding& obj)
+{
+ if (!m_binding) {
+ HW::enqueue(new binding_cmds::l2_bind_cmd(
+ m_binding, m_direction, m_itf->handle(), m_acl->handle()));
+ }
+ HW::write();
+}
+
+template <>
+void
+l2_binding::sweep(void)
+{
+ if (m_binding) {
+ HW::enqueue(new binding_cmds::l2_unbind_cmd(
+ m_binding, m_direction, m_itf->handle(), m_acl->handle()));
+ }
+ HW::write();
+}
+
+template <>
+void
+l2_binding::replay(void)
+{
+ if (m_binding) {
+ HW::enqueue(new binding_cmds::l2_bind_cmd(
+ m_binding, m_direction, m_itf->handle(), m_acl->handle()));
+ }
+}
+};
+
+std::ostream&
+operator<<(std::ostream& os,
+ const std::pair<direction_t, interface::key_t>& 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/extras/vom/vom/acl_binding.hpp b/extras/vom/vom/acl_binding.hpp
new file mode 100644
index 00000000000..89db0eea219
--- /dev/null
+++ b/extras/vom/vom/acl_binding.hpp
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_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/singular_db.hpp"
+#include "vom/singular_db_funcs.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>
+class binding : public object_base
+{
+public:
+ /**
+ * The key for a binding is the direction and the interface
+ */
+ typedef std::pair<direction_t, interface::key_t> 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(false)
+ {
+ 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(o.m_binding)
+ {
+ }
+
+ /**
+ * 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); }
+
+ static dependency_t order() { return m_evh.order(); }
+
+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() { m_db.replay(); }
+
+ /**
+ * Show the object in the Singular DB
+ */
+ void show(std::ostream& os) { db_dump(m_db, 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);
+
+ /**
+ * 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);
+
+ /**
+ * Replay the objects state to HW
+ */
+ void replay(void);
+
+ /**
+ * 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> l3_binding;
+
+/**
+ * Typedef the L2 binding type
+ */
+typedef binding<l2_list> l2_binding;
+
+/**
+ * Definition of the static Singular DB for ACL bindings
+ */
+template <typename LIST>
+singular_db<typename ACL::binding<LIST>::key_t, ACL::binding<LIST>>
+ binding<LIST>::m_db;
+
+template <typename LIST>
+typename ACL::binding<LIST>::event_handler binding<LIST>::m_evh;
+
+namespace {
+const static dependency_t __attribute__((unused)) l2o = l2_binding::order();
+const static dependency_t __attribute__((unused)) l3o = l3_binding::order();
+};
+};
+
+std::ostream& operator<<(std::ostream& os,
+ const std::pair<direction_t, interface::key_t>& key);
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/acl_binding_cmds.cpp b/extras/vom/vom/acl_binding_cmds.cpp
new file mode 100644
index 00000000000..8c33cd47b79
--- /dev/null
+++ b/extras/vom/vom/acl_binding_cmds.cpp
@@ -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.
+ */
+
+#include "vom/acl_binding_cmds.hpp"
+
+DEFINE_VAPI_MSG_IDS_ACL_API_JSON;
+
+namespace VOM {
+namespace ACL {
+namespace binding_cmds {
+template <>
+rc_t
+l3_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 <>
+std::string
+l3_bind_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "l3-acl-bind:[" << m_direction.to_string()
+ << " itf:" << m_itf.to_string() << " acl:" << m_acl.to_string() << "]";
+
+ return (s.str());
+}
+
+template <>
+rc_t
+l3_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 <>
+std::string
+l3_unbind_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "l3-acl-unbind:[" << m_direction.to_string()
+ << " itf:" << m_itf.to_string() << " acl:" << m_acl.to_string() << "]";
+
+ return (s.str());
+}
+
+template <>
+rc_t
+l3_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 <>
+std::string
+l3_dump_cmd::to_string() const
+{
+ return ("l3-acl-bind-dump");
+}
+
+template <>
+rc_t
+l2_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.acl_index = m_acl.value();
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return rc_t::OK;
+}
+
+template <>
+std::string
+l2_bind_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "l2-acl-bind:[" << m_direction.to_string()
+ << " itf:" << m_itf.to_string() << " acl:" << m_acl.to_string() << "]";
+
+ return (s.str());
+}
+
+template <>
+rc_t
+l2_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.acl_index = m_acl.value();
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return rc_t::OK;
+}
+
+template <>
+std::string
+l2_unbind_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "l2-acl-unbind:[" << m_direction.to_string()
+ << " itf:" << m_itf.to_string() << " acl:" << m_acl.to_string() << "]";
+
+ return (s.str());
+}
+
+template <>
+rc_t
+l2_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 <>
+std::string
+l2_dump_cmd::to_string() const
+{
+ return ("l2-acl-bind-dump");
+}
+
+}; // namespace binding_cmds
+}; // namespace ACL
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/acl_binding_cmds.hpp b/extras/vom/vom/acl_binding_cmds.hpp
new file mode 100644
index 00000000000..b9af66e70c8
--- /dev/null
+++ b/extras/vom/vom/acl_binding_cmds.hpp
@@ -0,0 +1,195 @@
+/*
+ * 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_CMDS_H__
+#define __VOM_ACL_BINDING_CMDS_H__
+
+#include "vom/acl_binding.hpp"
+#include "vom/dump_cmd.hpp"
+#include "vom/rpc_cmd.hpp"
+
+#include <vapi/acl.api.vapi.hpp>
+
+namespace VOM {
+namespace ACL {
+namespace binding_cmds {
+/**
+ * A command class that binds the ACL to the interface
+ */
+template <typename BIND>
+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;
+
+ /**
+ * 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
+ */
+template <typename BIND>
+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;
+
+ /**
+ * 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
+ */
+template <typename DUMP>
+class dump_cmd : public VOM::dump_cmd<DUMP>
+{
+public:
+ /**
+ * Constructor
+ */
+ dump_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;
+
+private:
+ /**
+ * HW reutrn code
+ */
+ HW::item<bool> item;
+};
+
+/**
+ * Typedef the L3 ACL binding commands
+ */
+typedef bind_cmd<vapi::Acl_interface_add_del> l3_bind_cmd;
+typedef unbind_cmd<vapi::Acl_interface_add_del> l3_unbind_cmd;
+typedef dump_cmd<vapi::Acl_interface_list_dump> l3_dump_cmd;
+
+/**
+ * Typedef the L2 binding type
+ */
+typedef bind_cmd<vapi::Macip_acl_interface_add_del> l2_bind_cmd;
+typedef unbind_cmd<vapi::Macip_acl_interface_add_del> l2_unbind_cmd;
+typedef dump_cmd<vapi::Macip_acl_interface_list_dump> l2_dump_cmd;
+
+}; // namespace binding_cmds
+}; // namespace ACL
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/acl_ethertype.cpp b/extras/vom/vom/acl_ethertype.cpp
new file mode 100644
index 00000000000..9092517194e
--- /dev/null
+++ b/extras/vom/vom/acl_ethertype.cpp
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_ethertype.hpp"
+#include "vom/acl_ethertype_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+namespace ACL {
+
+ethertype_rule_t::ethertype_rule_t(const ethertype_t& eth,
+ const direction_t& dir)
+ : m_eth(eth)
+ , m_dir(dir)
+{
+}
+
+std::string
+ethertype_rule_t::to_string() const
+{
+ std::ostringstream s;
+
+ s << "["
+ << "ethertype:" << m_eth.to_string() << " dir:" << m_dir.to_string()
+ << "],";
+
+ return (s.str());
+}
+
+bool
+ethertype_rule_t::operator<(const ethertype_rule_t& other) const
+{
+ return (m_dir > other.m_dir);
+}
+
+bool
+ethertype_rule_t::operator==(const ethertype_rule_t& other) const
+{
+ return (m_dir == other.m_dir && m_eth == other.m_eth);
+}
+
+uint16_t
+ethertype_rule_t::getEthertype() const
+{
+ return m_eth.value();
+}
+
+const direction_t&
+ethertype_rule_t::getDirection() const
+{
+ return m_dir;
+}
+
+/**
+ * A DB of all acl ethertype bindings configs
+ */
+singular_db<interface::key_t, acl_ethertype> acl_ethertype::m_db;
+
+acl_ethertype::event_handler acl_ethertype::m_evh;
+
+acl_ethertype::acl_ethertype(const interface& itf,
+ const acl_ethertype::ethertype_rules_t& le)
+ : m_itf(itf.singular())
+ , m_le(le)
+ , m_binding(true)
+{
+}
+
+acl_ethertype::acl_ethertype(const acl_ethertype& o)
+ : m_itf(o.m_itf)
+ , m_le(o.m_le)
+ , m_binding(o.m_binding)
+{
+}
+
+acl_ethertype::~acl_ethertype()
+{
+ sweep();
+
+ // not in the DB anymore.
+ m_db.release(m_itf->key(), this);
+}
+
+void
+acl_ethertype::sweep()
+{
+ if (m_binding) {
+ HW::enqueue(new acl_ethertype_cmds::unbind_cmd(m_binding, m_itf->handle()));
+ }
+ HW::write();
+}
+
+const acl_ethertype::key_t&
+acl_ethertype::key() const
+{
+ return (m_itf->key());
+}
+
+bool
+acl_ethertype::operator==(const acl_ethertype& other) const
+{
+ return (m_itf->key() == other.m_itf->key() && m_le == other.m_le);
+}
+
+std::shared_ptr<acl_ethertype>
+acl_ethertype::find(const key_t& key)
+{
+ return (m_db.find(key));
+}
+
+void
+acl_ethertype::dump(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+
+void
+acl_ethertype::replay()
+{
+ if (m_binding) {
+ HW::enqueue(
+ new acl_ethertype_cmds::bind_cmd(m_binding, m_itf->handle(), m_le));
+ }
+}
+
+std::string
+acl_ethertype::to_string() const
+{
+ std::ostringstream s;
+ s << "Acl-Ethertype:" << m_itf->to_string() << " ethertype-rules:";
+ auto it = m_le.cbegin();
+ while (it != m_le.cend()) {
+ s << it->to_string();
+ ++it;
+ }
+ s << " rules-size:" << m_le.size();
+
+ return (s.str());
+}
+
+void
+acl_ethertype::update(const acl_ethertype& desired)
+{
+ /*
+ * always update the instance with the latest rules
+ */
+ if (!m_binding || desired.m_le != m_le) {
+ HW::enqueue(
+ new acl_ethertype_cmds::bind_cmd(m_binding, m_itf->handle(), m_le));
+ }
+
+ m_le = desired.m_le;
+}
+
+std::shared_ptr<acl_ethertype>
+acl_ethertype::find_or_add(const acl_ethertype& temp)
+{
+ return (m_db.find_or_add(temp.m_itf->key(), temp));
+}
+
+std::shared_ptr<acl_ethertype>
+acl_ethertype::singular() const
+{
+ return find_or_add(*this);
+}
+
+acl_ethertype::event_handler::event_handler()
+{
+ OM::register_listener(this);
+ inspect::register_handler({ "acl-ethertype" }, "ACL Ethertype bindings",
+ this);
+}
+
+void
+acl_ethertype::event_handler::handle_replay()
+{
+ m_db.replay();
+}
+
+void
+acl_ethertype::event_handler::handle_populate(const client_db::key_t& key)
+{
+ /*
+ * dump VPP acl ethertypes
+ */
+ std::shared_ptr<acl_ethertype_cmds::dump_cmd> cmd =
+ std::make_shared<acl_ethertype_cmds::dump_cmd>(~0);
+
+ HW::enqueue(cmd);
+ HW::write();
+
+ for (auto& record : *cmd) {
+ auto& payload = record.get_payload();
+ handle_t hdl(payload.sw_if_index);
+ std::shared_ptr<interface> itf = interface::find(hdl);
+ uint8_t n_input = payload.n_input;
+ uint8_t count = payload.count;
+ ethertype_rules_t ler;
+ if (itf) {
+ for (int i = 0; i < count; i++) {
+ ethertype_t e = ethertype_t::from_numeric_val(payload.whitelist[i]);
+ if (n_input) {
+ ethertype_rule_t er(e, direction_t::INPUT);
+ ler.insert(er);
+ n_input--;
+ } else {
+ ethertype_rule_t er(e, direction_t::OUTPUT);
+ ler.insert(er);
+ }
+ }
+ if (!ler.empty()) {
+ acl_ethertype a_e(*itf, ler);
+ VOM_LOG(log_level_t::DEBUG) << "ethertype dump: " << a_e.to_string();
+ OM::commit(key, a_e);
+ }
+ }
+ }
+}
+
+dependency_t
+acl_ethertype::event_handler::order() const
+{
+ return (dependency_t::BINDING);
+}
+
+void
+acl_ethertype::event_handler::show(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+};
+};
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/acl_ethertype.hpp b/extras/vom/vom/acl_ethertype.hpp
new file mode 100644
index 00000000000..98d3ce20707
--- /dev/null
+++ b/extras/vom/vom/acl_ethertype.hpp
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_ETHERTYPE_H__
+#define __VOM_ACL_ETHERTYPE_H__
+
+#include <set>
+
+#include "vom/hw.hpp"
+#include "vom/inspect.hpp"
+#include "vom/interface.hpp"
+#include "vom/object_base.hpp"
+#include "vom/om.hpp"
+#include "vom/singular_db.hpp"
+
+namespace VOM {
+namespace ACL {
+/**
+ * An ACL ethertype list comprises a set of inbound ether types and out bound
+ * ether types
+ * to be applied to packets.
+ * A list is bound to a given interface.
+ */
+
+struct ethertype_rule_t
+{
+public:
+ /**
+ * Constructor
+ */
+ ethertype_rule_t(const ethertype_t& eth, const direction_t& dir);
+
+ /**
+ * Destructor
+ */
+ ~ethertype_rule_t() = default;
+
+ /**
+ * convert to string
+ */
+ std::string to_string() const;
+
+ /**
+ * comparision operator
+ */
+ bool operator<(const ethertype_rule_t& other) const;
+
+ /**
+ * comparision operator (for testing)
+ */
+ bool operator==(const ethertype_rule_t& other) const;
+
+ /**
+ * get the ether value
+ */
+ uint16_t getEthertype(void) const;
+
+ /**
+ * get the direction
+ */
+ const direction_t& getDirection(void) const;
+
+private:
+ /**
+ * ethertype for this rule
+ */
+ const ethertype_t m_eth;
+
+ /**
+ * direction in which ethertype will be applied w.r.t. intf
+ */
+ const direction_t m_dir;
+};
+
+class acl_ethertype : public object_base
+{
+public:
+ /**
+ * The KEY can be used to uniquely identify the ACL ethertype.
+ * (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 ethertype container
+ */
+ typedef std::multiset<ethertype_rule_t> ethertype_rules_t;
+
+ /**
+ * Construct a new object matching the desried state
+ */
+ acl_ethertype(const interface& itf, const ethertype_rules_t& le);
+
+ /**
+ * Copy Constructor
+ */
+ acl_ethertype(const acl_ethertype& o);
+
+ /**
+ * Destructor
+ */
+ ~acl_ethertype();
+
+ /**
+ * Return the binding's key
+ */
+ const key_t& key() const;
+
+ /**
+ * comparision operator (for testing)
+ */
+ bool operator==(const acl_ethertype& o) const;
+
+ /**
+ * Return the 'singular' of the acl ethertype that matches this object
+ */
+ std::shared_ptr<acl_ethertype> singular() const;
+
+ /**
+ * convert to string format for debug purposes
+ */
+ std::string to_string() const;
+
+ /**
+ * Dump all acl ethertype into the stream provided
+ */
+ static void dump(std::ostream& os);
+
+ /**
+ * Static function to find the acl_ethertype in the model
+ */
+ static std::shared_ptr<acl_ethertype> find(const key_t& key);
+
+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;
+
+ /**
+ * Enque commands to the VPP command Q for the update
+ */
+ void update(const acl_ethertype& obj);
+
+ /**
+ * Find or add acl ethertype to the OM
+ */
+ static std::shared_ptr<acl_ethertype> find_or_add(const acl_ethertype& 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<interface::key_t, acl_ethertype>;
+
+ /**
+ * 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 acl ethertype
+ * resides. By holding the reference here, we can guarantee that
+ * this object will outlive the interface
+ */
+ const std::shared_ptr<interface> m_itf;
+
+ /**
+ * Inbound and outbound ethers list applied on given interface
+ */
+ ethertype_rules_t m_le;
+
+ /**
+ * HW configuration for the binding. The bool representing the
+ * do/don't bind.
+ */
+ HW::item<bool> m_binding;
+
+ /**
+ * A map of all acl ethertype keyed against the interface.
+ */
+ static singular_db<interface::key_t, acl_ethertype> m_db;
+};
+};
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/acl_ethertype_cmds.cpp b/extras/vom/vom/acl_ethertype_cmds.cpp
new file mode 100644
index 00000000000..c05a428c725
--- /dev/null
+++ b/extras/vom/vom/acl_ethertype_cmds.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_ethertype_cmds.hpp"
+
+namespace VOM {
+namespace ACL {
+namespace acl_ethertype_cmds {
+
+bind_cmd::bind_cmd(HW::item<bool>& item,
+ const handle_t& itf,
+ const acl_ethertype::ethertype_rules_t& le)
+ : rpc_cmd(item)
+ , m_itf(itf)
+ , m_le(le)
+{
+}
+
+bool
+bind_cmd::operator==(const bind_cmd& other) const
+{
+ return (m_itf == other.m_itf && m_le == other.m_le);
+}
+
+rc_t
+bind_cmd::issue(connection& con)
+{
+ msg_t req(con.ctx(), m_le.size(), std::ref(*this));
+ uint32_t i = 0;
+ uint8_t n_input = 0;
+
+ auto& payload = req.get_request().get_payload();
+ payload.sw_if_index = m_itf.value();
+ payload.count = m_le.size();
+
+ auto it = m_le.cbegin();
+ while (it != m_le.cend()) {
+ payload.whitelist[i] = it->getEthertype();
+ if (it->getDirection() == direction_t::INPUT)
+ n_input++;
+ ++it;
+ ++i;
+ }
+
+ payload.n_input = n_input;
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return rc_t::OK;
+}
+
+std::string
+bind_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "ACL-Ethertype: " << m_hw_item.to_string()
+ << " itf:" << m_itf.to_string() << " ethertype-rules:";
+ auto it = m_le.cbegin();
+ while (it != m_le.cend()) {
+ s << it->to_string();
+ ++it;
+ }
+
+ s << " rules-size:" << m_le.size();
+
+ return (s.str());
+}
+
+unbind_cmd::unbind_cmd(HW::item<bool>& item, const handle_t& itf)
+ : rpc_cmd(item)
+ , m_itf(itf)
+{
+}
+
+bool
+unbind_cmd::operator==(const unbind_cmd& other) const
+{
+ return (m_itf == other.m_itf);
+}
+
+rc_t
+unbind_cmd::issue(connection& con)
+{
+ msg_t req(con.ctx(), 0, std::ref(*this));
+
+ auto& payload = req.get_request().get_payload();
+ payload.sw_if_index = m_itf.value();
+ payload.count = 0;
+
+ payload.n_input = 0;
+
+ VAPI_CALL(req.execute());
+
+ wait();
+ m_hw_item.set(rc_t::NOOP);
+
+ return rc_t::OK;
+}
+
+std::string
+unbind_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "ACL-Ethertype-Unbind: " << m_hw_item.to_string()
+ << " itf:" << m_itf.to_string();
+ return (s.str());
+}
+
+dump_cmd::dump_cmd(const handle_t& hdl)
+ : m_itf(hdl)
+{
+}
+
+dump_cmd::dump_cmd(const dump_cmd& d)
+ : m_itf(d.m_itf)
+{
+}
+
+bool
+dump_cmd::operator==(const dump_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+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();
+
+ VAPI_CALL(m_dump->execute());
+
+ wait();
+
+ return rc_t::OK;
+}
+
+std::string
+dump_cmd::to_string() const
+{
+ return ("acl-ethertype-dump");
+}
+}; // namespace acl_ethertype_cmds
+}; // namespace ACL
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/acl_ethertype_cmds.hpp b/extras/vom/vom/acl_ethertype_cmds.hpp
new file mode 100644
index 00000000000..f72a3fb97b5
--- /dev/null
+++ b/extras/vom/vom/acl_ethertype_cmds.hpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_ETHERTYPE_CMDS_H__
+#define __VOM_ACL_ETHERTYPE_CMDS_H__
+
+#include "vom/acl_ethertype.hpp"
+#include "vom/dump_cmd.hpp"
+#include "vom/rpc_cmd.hpp"
+
+#include <vapi/acl.api.vapi.hpp>
+
+namespace VOM {
+namespace ACL {
+namespace acl_ethertype_cmds {
+/**
+ * A command class that binds the ethertype list to the interface
+ */
+class bind_cmd : public rpc_cmd<HW::item<bool>,
+ rc_t,
+ vapi::Acl_interface_set_etype_whitelist>
+{
+public:
+ /**
+ * Constructor
+ */
+ bind_cmd(HW::item<bool>& item,
+ const handle_t& itf,
+ const acl_ethertype::ethertype_rules_t& le);
+
+ /**
+ * 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;
+
+ /**
+ * Ethertype list applied to interface
+ */
+ const acl_ethertype::ethertype_rules_t& m_le;
+};
+
+/**
+ * A command class that unbinds the ethertype list to the interface
+ */
+class unbind_cmd : public rpc_cmd<HW::item<bool>,
+ rc_t,
+ vapi::Acl_interface_set_etype_whitelist>
+{
+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 bind
+ */
+ const handle_t m_itf;
+};
+
+/**
+ * A cmd class that Dumps all the acl ethertypes on given interface
+ */
+class dump_cmd : public VOM::dump_cmd<vapi::Acl_interface_etype_whitelist_dump>
+{
+public:
+ /**
+ * Constructor
+ */
+ dump_cmd(const handle_t& itf);
+ 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:
+ /**
+ * The interface to get the addresses for
+ */
+ const handle_t m_itf;
+};
+};
+};
+};
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/acl_l2_rule.cpp b/extras/vom/vom/acl_l2_rule.cpp
new file mode 100644
index 00000000000..2b12e68c184
--- /dev/null
+++ b/extras/vom/vom/acl_l2_rule.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <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);
+}
+
+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());
+}
+
+uint32_t
+l2_rule::priority() const
+{
+ return m_priority;
+}
+
+const action_t&
+l2_rule::action() const
+{
+ return m_action;
+}
+
+const route::prefix_t&
+l2_rule::src_ip() const
+{
+ return m_src_ip;
+}
+
+const mac_address_t&
+l2_rule::mac() const
+{
+ return m_mac;
+}
+
+const mac_address_t&
+l2_rule::mac_mask() const
+{
+ return m_mac_mask;
+}
+}
+}
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/acl_l2_rule.hpp b/extras/vom/vom/acl_l2_rule.hpp
new file mode 100644
index 00000000000..8c094aef5f4
--- /dev/null
+++ b/extras/vom/vom/acl_l2_rule.hpp
@@ -0,0 +1,115 @@
+/*
+ * 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"
+
+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;
+
+ /**
+ * Getters
+ */
+ uint32_t priority() const;
+ const action_t& action() const;
+ const route::prefix_t& src_ip() const;
+ const mac_address_t& mac() const;
+ const mac_address_t& mac_mask() 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/extras/vom/vom/acl_l3_rule.cpp b/extras/vom/vom/acl_l3_rule.cpp
new file mode 100644
index 00000000000..417dc5f2eb7
--- /dev/null
+++ b/extras/vom/vom/acl_l3_rule.cpp
@@ -0,0 +1,213 @@
+/*
+ * 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,
+ uint8_t proto,
+ uint16_t srcport_or_icmptype_first,
+ uint16_t srcport_or_icmptype_last,
+ uint16_t dstport_or_icmpcode_first,
+ uint16_t dstport_or_icmpcode_last,
+ uint8_t tcp_flags_mask,
+ uint8_t tcp_flags_value)
+ : m_priority(priority)
+ , m_action(action)
+ , m_src(src)
+ , m_dst(dst)
+ , m_proto(proto)
+ , m_srcport_or_icmptype_first(srcport_or_icmptype_first)
+ , m_srcport_or_icmptype_last(srcport_or_icmptype_last)
+ , m_dstport_or_icmpcode_first(dstport_or_icmpcode_first)
+ , m_dstport_or_icmpcode_last(dstport_or_icmpcode_last)
+ , m_tcp_flags_mask(tcp_flags_mask)
+ , m_tcp_flags_value(tcp_flags_value)
+{
+}
+
+bool
+l3_rule::operator<(const l3_rule& other) const
+{
+ return (other.m_priority < m_priority);
+}
+
+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:" << std::to_string(m_tcp_flags_mask)
+ << " tcpflagvalue:" << std::to_string(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;
+}
+
+const route::prefix_t&
+l3_rule::src() const
+{
+ return m_src;
+}
+
+uint32_t
+l3_rule::priority() const
+{
+ return m_priority;
+}
+
+const action_t&
+l3_rule::action() const
+{
+ return m_action;
+}
+
+const route::prefix_t&
+l3_rule::dst() const
+{
+ return m_dst;
+}
+
+uint8_t
+l3_rule::proto() const
+{
+ return m_proto;
+}
+
+uint16_t
+l3_rule::srcport_or_icmptype_first() const
+{
+ return m_srcport_or_icmptype_first;
+}
+
+uint16_t
+l3_rule::srcport_or_icmptype_last() const
+{
+ return m_srcport_or_icmptype_last;
+}
+
+uint16_t
+l3_rule::dstport_or_icmpcode_first() const
+{
+ return m_dstport_or_icmpcode_first;
+}
+
+uint16_t
+l3_rule::dstport_or_icmpcode_last() const
+{
+ return m_dstport_or_icmpcode_last;
+}
+
+uint8_t
+l3_rule::tcp_flags_mask() const
+{
+ return m_tcp_flags_mask;
+}
+
+uint8_t
+l3_rule::tcp_flags_value() const
+{
+ return m_tcp_flags_value;
+}
+
+}; // namespace ACL
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/acl_l3_rule.hpp b/extras/vom/vom/acl_l3_rule.hpp
new file mode 100644
index 00000000000..c1f1ceea5a1
--- /dev/null
+++ b/extras/vom/vom/acl_l3_rule.hpp
@@ -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.
+ */
+
+#ifndef __VOM_L3_ACL_RULE_H__
+#define __VOM_L3_ACL_RULE_H__
+
+#include "vom/acl_types.hpp"
+#include "vom/prefix.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,
+ uint8_t proto = 0,
+ uint16_t srcport_or_icmptype_first = 0,
+ uint16_t srcport_or_icmptype_last = 0,
+ uint16_t dstport_or_icmpcode_first = 0,
+ uint16_t dstport_or_icmpcode_last = 0,
+ uint8_t tcp_flags_mask = 0,
+ uint8_t tcp_flags_value = 0);
+
+ /**
+ * 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;
+
+ /**
+ * 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);
+
+ /**
+ * Getters
+ */
+ const route::prefix_t& src() const;
+ uint32_t priority() const;
+ const action_t& action() const;
+ const route::prefix_t& dst() const;
+ uint8_t proto() const;
+ uint16_t srcport_or_icmptype_first() const;
+ uint16_t srcport_or_icmptype_last() const;
+ uint16_t dstport_or_icmpcode_first() const;
+ uint16_t dstport_or_icmpcode_last() const;
+ uint8_t tcp_flags_mask() const;
+ uint8_t tcp_flags_value() 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;
+
+ /**
+ * 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/extras/vom/vom/acl_list.cpp b/extras/vom/vom/acl_list.cpp
new file mode 100644
index 00000000000..651eb87a4a9
--- /dev/null
+++ b/extras/vom/vom/acl_list.cpp
@@ -0,0 +1,233 @@
+/*
+ * 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/acl_list_cmds.hpp"
+#include "vom/logger.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+namespace ACL {
+
+template <>
+l2_list::event_handler::event_handler()
+{
+ OM::register_listener(this);
+ inspect::register_handler({ "l2-acl-list" }, "L2 ACL lists", this);
+}
+
+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<list_cmds::l2_dump_cmd> cmd =
+ std::make_shared<list_cmds::l2_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
+l2_list::event_handler::show(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+
+template <>
+l3_list::event_handler::event_handler()
+{
+ OM::register_listener(this);
+ inspect::register_handler({ "l3-acl-list" }, "L3 ACL lists", this);
+}
+
+template <>
+void
+l3_list::event_handler::handle_populate(const client_db::key_t& key)
+{
+ /* hack to get this function instantiated */
+ m_evh.order();
+
+ /*
+ * dump L3 ACLs Bridge domains
+ */
+ std::shared_ptr<list_cmds::l3_dump_cmd> cmd =
+ std::make_shared<list_cmds::l3_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);
+
+ rule.set_proto(payload.r[ii].proto);
+ rule.set_src_from_port(payload.r[ii].srcport_or_icmptype_first);
+ rule.set_src_to_port(payload.r[ii].srcport_or_icmptype_last);
+ rule.set_dst_from_port(payload.r[ii].dstport_or_icmpcode_first);
+ rule.set_dst_to_port(payload.r[ii].dstport_or_icmpcode_last);
+ rule.set_tcp_flags_mask(payload.r[ii].tcp_flags_mask);
+ rule.set_tcp_flags_value(payload.r[ii].tcp_flags_value);
+
+ 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::show(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+
+template <>
+void
+l3_list::update(const l3_list& obj)
+{
+ /*
+ * always update the instance with the latest rule set
+ */
+ if (rc_t::OK != m_hdl.rc() || obj.m_rules != m_rules) {
+ HW::enqueue(new list_cmds::l3_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;
+}
+template <>
+void
+l2_list::update(const l2_list& obj)
+{
+ /*
+ * always update the instance with the latest rule set
+ */
+ if (rc_t::OK != m_hdl.rc() || obj.m_rules != m_rules) {
+ HW::enqueue(new list_cmds::l2_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;
+}
+/**
+ * Sweep/reap the object if still stale
+ */
+template <>
+void
+l3_list::sweep(void)
+{
+ if (m_hdl) {
+ HW::enqueue(new list_cmds::l3_delete_cmd(m_hdl));
+ }
+ HW::write();
+}
+template <>
+void
+l2_list::sweep(void)
+{
+ if (m_hdl) {
+ HW::enqueue(new list_cmds::l2_delete_cmd(m_hdl));
+ }
+ HW::write();
+}
+
+/**
+ * Replay the objects state to HW
+ */
+template <>
+void
+l3_list::replay(void)
+{
+ if (m_hdl) {
+ m_hdl.data().reset();
+ HW::enqueue(new list_cmds::l3_update_cmd(m_hdl, m_key, m_rules));
+ }
+}
+template <>
+void
+l2_list::replay(void)
+{
+ if (m_hdl) {
+ m_hdl.data().reset();
+ HW::enqueue(new list_cmds::l2_update_cmd(m_hdl, m_key, m_rules));
+ }
+}
+
+}; // namespace ACL
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/acl_list.hpp b/extras/vom/vom/acl_list.hpp
new file mode 100644
index 00000000000..eff5e1c5a52
--- /dev/null
+++ b/extras/vom/vom/acl_list.hpp
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#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/hw.hpp"
+#include "vom/inspect.hpp"
+#include "vom/om.hpp"
+#include "vom/singular_db.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>
+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_hdl(handle_t::INVALID)
+ , 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_hdl(handle_t::INVALID)
+ , m_key(key)
+ , m_rules(rules)
+ {
+ }
+
+ /**
+ * 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 (singular()->handle_i()); }
+
+ 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 key_t& key, const HW::item<handle_t>& item)
+ {
+ std::shared_ptr<list> sp = find(key);
+
+ if (sp && item) {
+ m_hdl_db[item.data()] = sp;
+ }
+ }
+
+ static void remove(const HW::item<handle_t>& item)
+ {
+ m_hdl_db.erase(item.data());
+ }
+
+ const key_t& key() const { return m_key; }
+
+ const rules_t& rules() const { return m_rules; }
+
+ /**
+ * Comparison operator - for UT
+ */
+ bool operator==(const list& l) const
+ {
+ return (key() == l.key() && rules() == l.rules());
+ }
+
+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() { m_db.replay(); }
+
+ /**
+ * Show the object in the Singular DB
+ */
+ void show(std::ostream& 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;
+
+ /**
+ * Enqueue commands to the VPP command Q for the update
+ */
+ void update(const list& obj);
+
+ /**
+ * 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.key(), temp));
+ }
+
+ /**
+ * return the acl-list's handle in the singular instance
+ */
+ const handle_t& handle_i() const { return (m_hdl.data()); }
+
+ /*
+ * 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);
+
+ /**
+ * Replay the objects state to HW
+ */
+ void replay(void);
+
+ /**
+ * 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<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> l3_list;
+
+/**
+ * Typedef the L2 ACL type
+ */
+typedef list<l2_rule> l2_list;
+
+/**
+ * Definition of the static singular_db for ACL Lists
+ */
+template <typename RULE>
+singular_db<typename ACL::list<RULE>::key_t, ACL::list<RULE>> list<RULE>::m_db;
+
+/**
+ * Definition of the static per-handle DB for ACL Lists
+ */
+template <typename RULE>
+std::map<handle_t, std::weak_ptr<ACL::list<RULE>>> list<RULE>::m_hdl_db;
+
+template <typename RULE>
+typename ACL::list<RULE>::event_handler list<RULE>::m_evh;
+};
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/acl_list_cmds.cpp b/extras/vom/vom/acl_list_cmds.cpp
new file mode 100644
index 00000000000..2e59763f4fe
--- /dev/null
+++ b/extras/vom/vom/acl_list_cmds.cpp
@@ -0,0 +1,195 @@
+/*
+ * 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_cmds.hpp"
+
+namespace VOM {
+namespace ACL {
+namespace list_cmds {
+/*
+ * Jumping through hoops to not expose the VAPI types publically
+ */
+static void
+to_vpp(const l2_rule& rule, vapi_type_macip_acl_rule& payload)
+{
+ payload.is_permit = rule.action().value();
+ rule.src_ip().to_vpp(&payload.is_ipv6, payload.src_ip_addr,
+ &payload.src_ip_prefix_len);
+ rule.mac().to_bytes(payload.src_mac, 6);
+ rule.mac_mask().to_bytes(payload.src_mac_mask, 6);
+}
+
+static void
+to_vpp(const l3_rule& rule, vapi_type_acl_rule& payload)
+{
+ payload.is_permit = rule.action().value();
+ rule.src().to_vpp(&payload.is_ipv6, payload.src_ip_addr,
+ &payload.src_ip_prefix_len);
+ rule.dst().to_vpp(&payload.is_ipv6, payload.dst_ip_addr,
+ &payload.dst_ip_prefix_len);
+
+ payload.proto = rule.proto();
+ payload.srcport_or_icmptype_first = rule.srcport_or_icmptype_first();
+ payload.srcport_or_icmptype_last = rule.srcport_or_icmptype_last();
+ payload.dstport_or_icmpcode_first = rule.dstport_or_icmpcode_first();
+ payload.dstport_or_icmpcode_last = rule.dstport_or_icmpcode_last();
+
+ payload.tcp_flags_mask = rule.tcp_flags_mask();
+ payload.tcp_flags_value = rule.tcp_flags_value();
+}
+
+template <>
+rc_t
+l3_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()) {
+ to_vpp(*it, payload.r[ii]);
+ ++it;
+ ++ii;
+ }
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item = wait();
+ if (m_hw_item.rc() == rc_t::OK)
+ insert_acl();
+
+ return rc_t::OK;
+}
+
+template <>
+rc_t
+l3_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);
+
+ remove_acl();
+
+ return rc_t::OK;
+}
+
+template <>
+rc_t
+l3_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_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()) {
+ to_vpp(*it, payload.r[ii]);
+ ++it;
+ ++ii;
+ }
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item = wait();
+ if (m_hw_item.rc() == rc_t::OK)
+ insert_acl();
+
+ return rc_t::OK;
+}
+
+template <>
+rc_t
+l2_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);
+
+ remove_acl();
+
+ return rc_t::OK;
+}
+
+template <>
+rc_t
+l2_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;
+}
+
+}; // namespace list_cmds
+}; // namespace ACL
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/acl_list_cmds.hpp b/extras/vom/vom/acl_list_cmds.hpp
new file mode 100644
index 00000000000..d24e75229de
--- /dev/null
+++ b/extras/vom/vom/acl_list_cmds.hpp
@@ -0,0 +1,223 @@
+/*
+ * 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_CMDS_H__
+#define __VOM_ACL_LIST_CMDS_H__
+
+#include "vom/acl_list.hpp"
+#include "vom/dump_cmd.hpp"
+#include "vom/rpc_cmd.hpp"
+
+#include <vapi/acl.api.vapi.hpp>
+
+namespace VOM {
+namespace ACL {
+namespace list_cmds {
+/**
+ * A command class that Create the list
+ */
+template <typename RULE, typename UPDATE>
+class update_cmd
+ : public rpc_cmd<HW::item<handle_t>, HW::item<handle_t>, UPDATE>
+{
+public:
+ typedef typename list<RULE>::rules_t cmd_rules_t;
+ typedef typename list<RULE>::key_t cmd_key_t;
+
+ /**
+ * Constructor
+ */
+ update_cmd(HW::item<handle_t>& item,
+ const cmd_key_t& key,
+ const cmd_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() << " rule-list:[";
+ for (auto rule : m_rules) {
+ s << " " << rule.to_string();
+ }
+ s << "]]";
+
+ 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 succeeded()
+ {
+ rpc_cmd<HW::item<handle_t>, HW::item<handle_t>, UPDATE>::succeeded();
+ list<RULE>::add(m_key, this->item());
+ }
+
+ /**
+ * 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:" << retval
+ << " acl_index:" << acl_index;
+
+ rc_t rc = rc_t::from_vpp_retval(retval);
+ handle_t handle(acl_index);
+
+ HW::item<handle_t> res(handle, rc);
+
+ this->fulfill(res);
+
+ return (VAPI_OK);
+ }
+
+private:
+ /**
+ * add the created acl to the DB
+ */
+ void insert_acl() { list<RULE>::add(m_key, this->item()); }
+
+ /**
+ * The key.
+ */
+ const cmd_key_t& m_key;
+
+ /**
+ * The rules
+ */
+ const cmd_rules_t& m_rules;
+};
+
+/**
+ * A cmd class that Deletes an ACL
+ */
+template <typename RULE, typename DELETE>
+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());
+ }
+
+private:
+ /**
+ * remove the acl from the DB
+ */
+ void remove_acl() { list<RULE>::remove(this->item()); }
+};
+
+/**
+ * A cmd class that Dumps all the ACLs
+ */
+template <typename DUMP>
+class dump_cmd : public VOM::dump_cmd<DUMP>
+{
+public:
+ /**
+ * Constructor
+ */
+ dump_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 { return ("acl-list-dump"); }
+
+ /**
+ * Comparison operator - only used for UT
+ */
+ bool operator==(const dump_cmd& i) const { return true; }
+};
+
+/**
+ * Typedef the L3 ACL commands
+ */
+typedef update_cmd<l3_rule, vapi::Acl_add_replace> l3_update_cmd;
+typedef delete_cmd<l3_rule, vapi::Acl_del> l3_delete_cmd;
+typedef dump_cmd<vapi::Acl_dump> l3_dump_cmd;
+
+/**
+ * Typedef the L2 ACL commands
+ */
+typedef update_cmd<l2_rule, vapi::Macip_acl_add> l2_update_cmd;
+typedef delete_cmd<l2_rule, vapi::Macip_acl_del> l2_delete_cmd;
+typedef dump_cmd<vapi::Macip_acl_dump> l2_dump_cmd;
+
+}; // namespace list_cmds
+}; // namespace ACL
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/acl_types.cpp b/extras/vom/vom/acl_types.cpp
new file mode 100644
index 00000000000..b2c0c7f0b9d
--- /dev/null
+++ b/extras/vom/vom/acl_types.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/acl_types.hpp"
+
+namespace VOM {
+namespace ACL {
+const action_t action_t::PERMITANDREFLEX(2, "permitandreflex");
+const action_t action_t::PERMIT(1, "permit");
+const action_t action_t::DENY(0, "deny");
+
+action_t::action_t(int v, const std::string s)
+ : enum_base(v, s)
+{
+}
+
+const action_t&
+action_t::from_int(uint8_t i)
+{
+ if (i == 2)
+ return action_t::PERMITANDREFLEX;
+ else if (i)
+ return action_t::PERMIT;
+
+ return action_t::DENY;
+}
+
+const action_t&
+action_t::from_bool(bool b, uint8_t c)
+{
+ if (b) {
+ if (c)
+ return action_t::PERMITANDREFLEX;
+ return action_t::PERMIT;
+ }
+ return action_t::DENY;
+}
+}
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/acl_types.hpp b/extras/vom/vom/acl_types.hpp
new file mode 100644
index 00000000000..cf5bee3e478
--- /dev/null
+++ b/extras/vom/vom/acl_types.hpp
@@ -0,0 +1,68 @@
+/*
+ * 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>
+{
+ /**
+ * 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);
+
+private:
+ action_t(int v, const std::string s);
+};
+};
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/arp_proxy_binding.cpp b/extras/vom/vom/arp_proxy_binding.cpp
new file mode 100644
index 00000000000..73d3d844f53
--- /dev/null
+++ b/extras/vom/vom/arp_proxy_binding.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/arp_proxy_binding.hpp"
+#include "vom/arp_proxy_binding_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+
+/**
+ * A DB of all ARP proxy bindings configs
+ */
+singular_db<interface::key_t, 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 arp_proxy_binding_cmds::unbind_cmd(m_binding, m_itf->handle()));
+ }
+ HW::write();
+}
+
+void
+arp_proxy_binding::dump(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+
+void
+arp_proxy_binding::replay()
+{
+ if (m_binding) {
+ HW::enqueue(
+ new arp_proxy_binding_cmds::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 arp_proxy_binding_cmds::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-binding" }, "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)
+{
+ db_dump(m_db, os);
+}
+}
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/arp_proxy_binding.hpp b/extras/vom/vom/arp_proxy_binding.hpp
new file mode 100644
index 00000000000..f57f6971991
--- /dev/null
+++ b/extras/vom/vom/arp_proxy_binding.hpp
@@ -0,0 +1,164 @@
+/*
+ * 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/hw.hpp"
+#include "vom/inspect.hpp"
+#include "vom/interface.hpp"
+#include "vom/object_base.hpp"
+#include "vom/om.hpp"
+#include "vom/singular_db.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);
+
+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_t, 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_t, arp_proxy_binding> m_db;
+};
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/arp_proxy_binding_cmds.cpp b/extras/vom/vom/arp_proxy_binding_cmds.cpp
new file mode 100644
index 00000000000..675feef9b8d
--- /dev/null
+++ b/extras/vom/vom/arp_proxy_binding_cmds.cpp
@@ -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.
+ */
+
+#include "vom/arp_proxy_binding_cmds.hpp"
+
+namespace VOM {
+namespace arp_proxy_binding_cmds {
+
+bind_cmd::bind_cmd(HW::item<bool>& item, const handle_t& itf)
+ : rpc_cmd(item)
+ , m_itf(itf)
+{
+}
+
+bool
+bind_cmd::operator==(const bind_cmd& other) const
+{
+ return (m_itf == other.m_itf);
+}
+
+rc_t
+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
+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());
+}
+
+unbind_cmd::unbind_cmd(HW::item<bool>& item, const handle_t& itf)
+ : rpc_cmd(item)
+ , m_itf(itf)
+{
+}
+
+bool
+unbind_cmd::operator==(const unbind_cmd& other) const
+{
+ return (m_itf == other.m_itf);
+}
+
+rc_t
+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
+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());
+}
+
+}; // namespace arp_proxy_binding_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/arp_proxy_binding_cmds.hpp b/extras/vom/vom/arp_proxy_binding_cmds.hpp
new file mode 100644
index 00000000000..c73bb13aee3
--- /dev/null
+++ b/extras/vom/vom/arp_proxy_binding_cmds.hpp
@@ -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.
+ */
+
+#ifndef __VOM_ARP_PROXY_BINDING_CMDS_H__
+#define __VOM_ARP_PROXY_BINDING_CMDS_H__
+
+#include "vom/arp_proxy_binding.hpp"
+
+#include <vapi/vpe.api.vapi.hpp>
+
+namespace VOM {
+namespace arp_proxy_binding_cmds {
+/**
+ * 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;
+};
+};
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/arp_proxy_config.cpp b/extras/vom/vom/arp_proxy_config.cpp
new file mode 100644
index 00000000000..3973eba55f9
--- /dev/null
+++ b/extras/vom/vom/arp_proxy_config.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/arp_proxy_config.hpp"
+#include "vom/arp_proxy_config_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+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 arp_proxy_config_cmds::unconfig_cmd(m_config, m_low, m_high));
+ }
+ HW::write();
+}
+
+void
+arp_proxy_config::dump(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+
+void
+arp_proxy_config::replay()
+{
+ if (m_config) {
+ HW::enqueue(new arp_proxy_config_cmds::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 arp_proxy_config_cmds::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-config" }, "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)
+{
+ db_dump(m_db, 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/extras/vom/vom/arp_proxy_config.hpp b/extras/vom/vom/arp_proxy_config.hpp
new file mode 100644
index 00000000000..3a50633de4d
--- /dev/null
+++ b/extras/vom/vom/arp_proxy_config.hpp
@@ -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.
+ */
+
+#ifndef __VOM_ARP_PROXY_CONFIG_H__
+#define __VOM_ARP_PROXY_CONFIG_H__
+
+#include "vom/hw.hpp"
+#include "vom/inspect.hpp"
+#include "vom/object_base.hpp"
+#include "vom/om.hpp"
+#include "vom/singular_db.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);
+
+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/extras/vom/vom/arp_proxy_config_cmds.cpp b/extras/vom/vom/arp_proxy_config_cmds.cpp
new file mode 100644
index 00000000000..cf7fad5d90d
--- /dev/null
+++ b/extras/vom/vom/arp_proxy_config_cmds.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/arp_proxy_config_cmds.hpp"
+
+namespace VOM {
+namespace arp_proxy_config_cmds {
+
+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
+config_cmd::operator==(const config_cmd& o) const
+{
+ return ((m_low == o.m_low) && (m_high == o.m_high));
+}
+
+rc_t
+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
+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());
+}
+
+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
+unconfig_cmd::operator==(const unconfig_cmd& o) const
+{
+ return ((m_low == o.m_low) && (m_high == o.m_high));
+}
+
+rc_t
+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
+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/extras/vom/vom/arp_proxy_config_cmds.hpp b/extras/vom/vom/arp_proxy_config_cmds.hpp
new file mode 100644
index 00000000000..ac0e1fd1702
--- /dev/null
+++ b/extras/vom/vom/arp_proxy_config_cmds.hpp
@@ -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.
+ */
+
+#ifndef __VOM_ARP_PROXY_CONFIG_CMDS_H__
+#define __VOM_ARP_PROXY_CONFIG_CMDS_H__
+
+#include "vom/arp_proxy_config.hpp"
+#include "vom/dump_cmd.hpp"
+#include "vom/rpc_cmd.hpp"
+
+#include <vapi/ip.api.vapi.hpp>
+
+namespace VOM {
+namespace arp_proxy_config_cmds {
+/**
+ * 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;
+};
+};
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/bond_group_binding.cpp b/extras/vom/vom/bond_group_binding.cpp
new file mode 100644
index 00000000000..60721dd4265
--- /dev/null
+++ b/extras/vom/vom/bond_group_binding.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/bond_group_binding.hpp"
+#include "vom/bond_group_binding_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+
+/**
+ * A DB of all bond interface binding
+ */
+singular_db<bond_group_binding::key_t, bond_group_binding>
+ bond_group_binding::m_db;
+
+bond_group_binding::event_handler bond_group_binding::m_evh;
+
+bond_group_binding::bond_group_binding(const bond_interface& itf,
+ const enslaved_itf_t& itfs)
+ : m_itf(itf.singular())
+ , m_mem_itfs(itfs)
+ , m_binding(false)
+{
+}
+
+bond_group_binding::bond_group_binding(const bond_group_binding& o)
+ : m_itf(o.m_itf)
+ , m_mem_itfs(o.m_mem_itfs)
+ , m_binding(o.m_binding)
+{
+}
+
+bond_group_binding::~bond_group_binding()
+{
+ sweep();
+
+ // not in the DB anymore.
+ m_db.release(key(), this);
+}
+
+const bond_group_binding::key_t
+bond_group_binding::key() const
+{
+ return (m_itf->key() + "-binding");
+}
+
+void
+bond_group_binding::sweep()
+{
+
+ auto it = m_mem_itfs.cbegin();
+ while (it != m_mem_itfs.cend()) {
+ if (m_binding) {
+ HW::enqueue(
+ new bond_group_binding_cmds::unbind_cmd(m_binding, it->hdl()));
+ }
+ HW::write();
+ ++it;
+ }
+}
+
+void
+bond_group_binding::dump(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+
+void
+bond_group_binding::replay()
+{
+ auto it = m_mem_itfs.cbegin();
+ while (it != m_mem_itfs.cend()) {
+ if (m_binding) {
+ HW::enqueue(
+ new bond_group_binding_cmds::bind_cmd(m_binding, m_itf->handle(), *it));
+ }
+ HW::write();
+ ++it;
+ }
+}
+
+std::string
+bond_group_binding::to_string() const
+{
+ auto it = m_mem_itfs.cbegin();
+ std::ostringstream s;
+ s << "bond-interface-binding: " << m_itf->to_string() << " slave-itfs: [";
+ while (it != m_mem_itfs.cend()) {
+ s << " " << it->to_string();
+ ++it;
+ }
+ s << "]";
+ return (s.str());
+}
+
+void
+bond_group_binding::update(const bond_group_binding& desired)
+{
+ /*
+ * the desired state is always that the interface should be created
+ */
+ auto it = m_mem_itfs.cbegin();
+ while (it != m_mem_itfs.cend()) {
+ if (!m_binding) {
+ HW::enqueue(
+ new bond_group_binding_cmds::bind_cmd(m_binding, m_itf->handle(), *it));
+ }
+ ++it;
+ }
+}
+
+std::shared_ptr<bond_group_binding>
+bond_group_binding::find_or_add(const bond_group_binding& temp)
+{
+ return (m_db.find_or_add(temp.key(), temp));
+}
+
+std::shared_ptr<bond_group_binding>
+bond_group_binding::singular() const
+{
+ return find_or_add(*this);
+}
+
+bond_group_binding::event_handler::event_handler()
+{
+ OM::register_listener(this);
+ inspect::register_handler({ "bond-intf-binding" }, "Bond interface binding",
+ this);
+}
+
+void
+bond_group_binding::event_handler::handle_replay()
+{
+ m_db.replay();
+}
+
+void
+bond_group_binding::event_handler::handle_populate(const client_db::key_t& key)
+{
+ /*
+ * handle it in interface class
+ */
+}
+
+dependency_t
+bond_group_binding::event_handler::order() const
+{
+ /*
+ * We want enslaved interfaces bind to bond after interface
+ * but before anything else.
+ */
+ return (dependency_t::BOND_BINDING);
+}
+
+void
+bond_group_binding::event_handler::show(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+}
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/bond_group_binding.hpp b/extras/vom/vom/bond_group_binding.hpp
new file mode 100644
index 00000000000..bfac4881a44
--- /dev/null
+++ b/extras/vom/vom/bond_group_binding.hpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_BOND_GROUP_BINDING_H__
+#define __VOM_BOND_GROUP_BINDING_H__
+
+#include <set>
+
+#include "vom/bond_interface.hpp"
+#include "vom/bond_member.hpp"
+#include "vom/hw.hpp"
+#include "vom/inspect.hpp"
+#include "vom/interface.hpp"
+#include "vom/object_base.hpp"
+#include "vom/om.hpp"
+#include "vom/singular_db.hpp"
+
+namespace VOM {
+/**
+ * A representation of bond interface binding
+ */
+class bond_group_binding : public object_base
+{
+public:
+ /**
+ * The KEY can be used to uniquely identify the Bond Binding.
+ * (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 container type for enslaved itfs
+ */
+ typedef std::set<bond_member> enslaved_itf_t;
+
+ /**
+ * Construct a new object matching the desried state
+ */
+ bond_group_binding(const bond_interface& itf, const enslaved_itf_t& mem);
+
+ /**
+ * Copy Constructor
+ */
+ bond_group_binding(const bond_group_binding& o);
+
+ /**
+ * Destructor
+ */
+ ~bond_group_binding();
+
+ /**
+ * Return the 'singular' of the bond interface binding that matches this
+ * object
+ */
+ std::shared_ptr<bond_group_binding> singular() const;
+
+ /**
+ * convert to string format for debug purposes
+ */
+ std::string to_string() const;
+
+ /**
+ * get the key to this object
+ */
+ const key_t key() const;
+
+ /**
+ * Dump all bond interface bindings into the stream provided
+ */
+ static void dump(std::ostream& os);
+
+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;
+
+ /**
+ * Enqueue command to the VPP command Q for the update
+ */
+ void update(const bond_group_binding& obj);
+
+ /**
+ * Find or add bond interface binding to the OM
+ */
+ static std::shared_ptr<bond_group_binding> find_or_add(
+ const bond_group_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<key_t, bond_group_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 bond interface.
+ * By holding the reference here, we can guarantee that
+ * this object will outlive the interface
+ */
+ std::shared_ptr<bond_interface> m_itf;
+
+ /**
+ * A list of member interfaces.
+ */
+ const enslaved_itf_t m_mem_itfs;
+
+ /**
+ * HW configuration for the binding. The bool representing the
+ * do/don't bind.
+ */
+ HW::item<bool> m_binding;
+
+ /**
+ * A map of all bond interface bindings keyed against the interface +
+ * "binding".
+ */
+ static singular_db<key_t, bond_group_binding> m_db;
+};
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/bond_group_binding_cmds.cpp b/extras/vom/vom/bond_group_binding_cmds.cpp
new file mode 100644
index 00000000000..3ffe9810173
--- /dev/null
+++ b/extras/vom/vom/bond_group_binding_cmds.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/bond_group_binding_cmds.hpp"
+
+namespace VOM {
+namespace bond_group_binding_cmds {
+
+bind_cmd::bind_cmd(HW::item<bool>& item,
+ const handle_t& bond_itf,
+ const bond_member& itf)
+ : rpc_cmd(item)
+ , m_bond_itf(bond_itf)
+ , m_itf(itf)
+{
+}
+
+bool
+bind_cmd::operator==(const bind_cmd& other) const
+{
+ return ((m_bond_itf == other.m_bond_itf) && (m_itf == other.m_itf));
+}
+
+rc_t
+bind_cmd::issue(connection& con)
+{
+ msg_t req(con.ctx(), std::ref(*this));
+
+ auto& payload = req.get_request().get_payload();
+
+ m_itf.to_vpp(payload);
+ payload.bond_sw_if_index = m_bond_itf.value();
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return rc_t::OK;
+}
+
+std::string
+bind_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "bond-itf-bind: " << m_hw_item.to_string()
+ << " bond-itf:" << m_bond_itf.to_string()
+ << " slave-itf:" << m_itf.hdl().to_string();
+
+ return (s.str());
+}
+
+unbind_cmd::unbind_cmd(HW::item<bool>& item, const handle_t& itf)
+ : rpc_cmd(item)
+ , m_itf(itf)
+{
+}
+
+bool
+unbind_cmd::operator==(const unbind_cmd& other) const
+{
+ return (m_itf == other.m_itf);
+}
+
+rc_t
+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();
+
+ VAPI_CALL(req.execute());
+
+ wait();
+ m_hw_item.set(rc_t::NOOP);
+
+ return rc_t::OK;
+}
+
+std::string
+unbind_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "bond-itf-unbind: " << m_hw_item.to_string()
+ << " slave-itf:" << m_itf.to_string();
+
+ return (s.str());
+}
+
+dump_cmd::dump_cmd(const handle_t& hdl)
+ : m_itf(hdl)
+{
+}
+
+dump_cmd::dump_cmd(const dump_cmd& d)
+ : m_itf(d.m_itf)
+{
+}
+
+bool
+dump_cmd::operator==(const dump_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+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();
+
+ VAPI_CALL(m_dump->execute());
+
+ wait();
+
+ return rc_t::OK;
+}
+
+std::string
+dump_cmd::to_string() const
+{
+ return ("bond-slave-itfs-dump");
+}
+
+}; // namespace bond_group_binding_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/bond_group_binding_cmds.hpp b/extras/vom/vom/bond_group_binding_cmds.hpp
new file mode 100644
index 00000000000..71c4f9fad64
--- /dev/null
+++ b/extras/vom/vom/bond_group_binding_cmds.hpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_BOND_GROUP_BINDING_CMDS_H__
+#define __VOM_BOND_GROUP_BINDING_CMDS_H__
+
+#include "vom/bond_group_binding.hpp"
+#include "vom/dump_cmd.hpp"
+
+#include <vapi/bond.api.vapi.hpp>
+
+namespace VOM {
+namespace bond_group_binding_cmds {
+/**
+ * A command class that binds the slave interface to the bond interface
+ */
+class bind_cmd : public rpc_cmd<HW::item<bool>, rc_t, vapi::Bond_enslave>
+{
+public:
+ /**
+ * Constructor
+ */
+ bind_cmd(HW::item<bool>& item,
+ const handle_t& bond_itf,
+ const bond_member& 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:
+ /**
+ * sw_if_index of bond interface
+ */
+ const handle_t m_bond_itf;
+
+ /**
+ * member interface of bond group
+ */
+ const bond_member m_itf;
+};
+
+/**
+ * A cmd class that detach slave from a bond interface
+ */
+class unbind_cmd : public rpc_cmd<HW::item<bool>, rc_t, vapi::Bond_detach_slave>
+{
+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:
+ /**
+ * slave interface of bond group
+ */
+ const handle_t m_itf;
+};
+
+/**
+ * A cmd class that Dumps slave itfs
+ */
+class dump_cmd : public VOM::dump_cmd<vapi::Sw_interface_slave_dump>
+{
+public:
+ /**
+ * Default Constructor
+ */
+ dump_cmd(const handle_t& itf);
+ 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:
+ /**
+ * The interface to get the addresses for
+ */
+ const handle_t m_itf;
+};
+};
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/bond_interface.cpp b/extras/vom/vom/bond_interface.cpp
new file mode 100644
index 00000000000..32a00ad7f43
--- /dev/null
+++ b/extras/vom/vom/bond_interface.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/bond_interface.hpp"
+#include "vom/bond_group_binding.hpp"
+#include "vom/bond_group_binding_cmds.hpp"
+#include "vom/bond_interface_cmds.hpp"
+
+namespace VOM {
+/**
+ * Construct a new object matching the desried state
+ */
+bond_interface::bond_interface(const std::string& name,
+ admin_state_t state,
+ mode_t mode,
+ lb_t lb)
+ : interface(name, type_t::BOND, state)
+ , m_l2_address(l2_address_t::ZERO)
+ , m_mode(mode)
+ , m_lb(lb)
+{
+}
+
+bond_interface::bond_interface(const std::string& name,
+ admin_state_t state,
+ const l2_address_t& l2_address,
+ mode_t mode,
+ lb_t lb)
+ : interface(name, type_t::BOND, state)
+ , m_l2_address(l2_address)
+ , m_mode(mode)
+ , m_lb(lb)
+{
+}
+
+bond_interface::~bond_interface()
+{
+ sweep();
+ release();
+}
+
+bond_interface::bond_interface(const bond_interface& o)
+ : interface(o)
+ , m_l2_address(o.m_l2_address)
+ , m_mode(o.m_mode)
+ , m_lb(o.m_lb)
+{
+}
+
+std::shared_ptr<bond_interface>
+bond_interface::find(const handle_t& hdl)
+{
+ return std::dynamic_pointer_cast<bond_interface>(interface::find(hdl));
+}
+
+void
+bond_interface::set(bond_interface::mode_t mode)
+{
+ m_mode = mode;
+}
+
+void
+bond_interface::set(bond_interface::lb_t lb)
+{
+ m_lb = lb;
+}
+
+std::string
+bond_interface::to_string() const
+{
+ std::ostringstream s;
+
+ s << this->interface::to_string() << " mode:" << m_mode.to_string()
+ << " lb:" << m_lb.to_string();
+
+ return (s.str());
+}
+
+std::queue<cmd*>&
+bond_interface::mk_create_cmd(std::queue<cmd*>& q)
+{
+ q.push(new bond_interface_cmds::create_cmd(m_hdl, name(), m_mode, m_lb,
+ m_l2_address));
+
+ return (q);
+}
+
+std::queue<cmd*>&
+bond_interface::mk_delete_cmd(std::queue<cmd*>& q)
+{
+ q.push(new bond_interface_cmds::delete_cmd(m_hdl));
+
+ return (q);
+}
+
+std::shared_ptr<bond_interface>
+bond_interface::singular() const
+{
+ return std::dynamic_pointer_cast<bond_interface>(singular_i());
+}
+
+std::shared_ptr<interface>
+bond_interface::singular_i() const
+{
+ return m_db.find_or_add(name(), *this);
+}
+
+void
+bond_interface::set(handle_t& handle)
+{
+ this->interface::set(handle);
+}
+
+const bond_interface::mode_t bond_interface::mode_t::ROUND_ROBIN(1,
+ "round-robin");
+const bond_interface::mode_t bond_interface::mode_t::ACTIVE_BACKUP(
+ 2,
+ "active-backup");
+const bond_interface::mode_t bond_interface::mode_t::XOR(3, "xor");
+const bond_interface::mode_t bond_interface::mode_t::BROADCAST(4, "broadcast");
+const bond_interface::mode_t bond_interface::mode_t::LACP(5, "lacp");
+const bond_interface::mode_t bond_interface::mode_t::UNSPECIFIED(0,
+ "unspecified");
+
+const bond_interface::mode_t
+bond_interface::mode_t::from_numeric_val(uint8_t numeric)
+{
+ if (1 == numeric) {
+ return (bond_interface::mode_t::ROUND_ROBIN);
+ }
+ if (2 == numeric) {
+ return (bond_interface::mode_t::ACTIVE_BACKUP);
+ }
+ if (3 == numeric) {
+ return (bond_interface::mode_t::XOR);
+ }
+ if (4 == numeric) {
+ return (bond_interface::mode_t::BROADCAST);
+ }
+ if (5 == numeric) {
+ return (bond_interface::mode_t::LACP);
+ }
+
+ return (bond_interface::mode_t::UNSPECIFIED);
+}
+
+bond_interface::mode_t::mode_t(int v, const std::string& s)
+ : enum_base<bond_interface::mode_t>(v, s)
+{
+}
+
+const bond_interface::lb_t bond_interface::lb_t::L2(0, "l2");
+const bond_interface::lb_t bond_interface::lb_t::L34(1, "l34");
+const bond_interface::lb_t bond_interface::lb_t::L23(2, "l23");
+const bond_interface::lb_t bond_interface::lb_t::UNSPECIFIED(~0, "unspecified");
+
+const bond_interface::lb_t
+bond_interface::lb_t::from_numeric_val(uint8_t numeric)
+{
+ if (0 == numeric) {
+ return (bond_interface::lb_t::L2);
+ }
+ if (1 == numeric) {
+ return (bond_interface::lb_t::L34);
+ }
+ if (2 == numeric) {
+ return (bond_interface::lb_t::L23);
+ }
+
+ return (bond_interface::lb_t::UNSPECIFIED);
+}
+
+bond_interface::lb_t::lb_t(int v, const std::string& s)
+ : enum_base<bond_interface::lb_t>(v, s)
+{
+}
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/bond_interface.hpp b/extras/vom/vom/bond_interface.hpp
new file mode 100644
index 00000000000..4584bd14be2
--- /dev/null
+++ b/extras/vom/vom/bond_interface.hpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_BOND_INTERFACE_H__
+#define __VOM_BOND_INTERFACE_H__
+
+#include "vom/interface.hpp"
+
+namespace VOM {
+/**
+ * A bond-interface. e.g. a bond interface
+ */
+class bond_interface : public interface
+{
+public:
+ /**
+ * A bond interface mode
+ */
+ struct mode_t : enum_base<mode_t>
+ {
+ /**
+ * Round-Robin bond interface mode
+ */
+ const static mode_t ROUND_ROBIN;
+ /**
+ * Active-backup bond interface mode
+ */
+ const static mode_t ACTIVE_BACKUP;
+ /**
+ * XOR bond interface mode
+ */
+ const static mode_t XOR;
+ /**
+ * Broadcast bond interface mode
+ */
+ const static mode_t BROADCAST;
+ /**
+ * LACP bond interface mode
+ */
+ const static mode_t LACP;
+ /**
+ * Unspecificed bond interface mode
+ */
+ const static mode_t UNSPECIFIED;
+
+ /**
+ * Convert VPP's value of the bond to a mode
+ */
+ static const mode_t from_numeric_val(uint8_t v);
+
+ private:
+ /**
+ * Private constructor taking the value and the string name
+ */
+ mode_t(int v, const std::string& s);
+ };
+
+ /**
+ * A bond interface load balance
+ */
+ struct lb_t : enum_base<lb_t>
+ {
+ /**
+ * L2 bond interface lb
+ */
+ const static lb_t L2;
+ /**
+ * L34 bond interface lb
+ */
+ const static lb_t L34;
+ /**
+ * L23 bond interface lb
+ */
+ const static lb_t L23;
+ /**
+ * Unspecificed bond interface lb
+ */
+ const static lb_t UNSPECIFIED;
+
+ /**
+ * Convert VPP's value of the bond to a lb
+ */
+ static const lb_t from_numeric_val(uint8_t v);
+
+ private:
+ /**
+ * Private constructor taking the value and the string name
+ */
+ lb_t(int v, const std::string& s);
+ };
+
+ bond_interface(const std::string& name,
+ admin_state_t state,
+ mode_t mode,
+ lb_t lb = lb_t::UNSPECIFIED);
+
+ bond_interface(const std::string& name,
+ admin_state_t state,
+ const l2_address_t& l2_address,
+ mode_t mode,
+ lb_t lb = lb_t::UNSPECIFIED);
+
+ ~bond_interface();
+ bond_interface(const bond_interface& o);
+
+ /**
+ * The the singular instance of the bond interface in the DB by handle
+ */
+ static std::shared_ptr<bond_interface> find(const handle_t& hdl);
+
+ /**
+ * Return the matching 'singular instance' of the BOND interface
+ */
+ std::shared_ptr<bond_interface> singular() const;
+
+ /**
+ * set the mode
+ */
+ void set(mode_t mode);
+
+ /**
+ * set the lb
+ */
+ void set(lb_t lb);
+
+ /**
+ * convert to string
+ */
+ virtual std::string to_string() const;
+
+protected:
+ /**
+ * set the handle
+ */
+ void set(handle_t& handle);
+ friend class interface_factory;
+
+private:
+ /**
+ * l2 address on bond interface
+ */
+ l2_address_t m_l2_address;
+
+ /**
+ * mode on bond interface
+ */
+ mode_t m_mode;
+
+ /**
+ * lb mode on bond interface
+ */
+ lb_t m_lb;
+
+ /**
+ * 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/extras/vom/vom/bond_interface_cmds.cpp b/extras/vom/vom/bond_interface_cmds.cpp
new file mode 100644
index 00000000000..d59560d4f5f
--- /dev/null
+++ b/extras/vom/vom/bond_interface_cmds.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/bond_interface_cmds.hpp"
+
+DEFINE_VAPI_MSG_IDS_BOND_API_JSON;
+
+namespace VOM {
+namespace bond_interface_cmds {
+create_cmd::create_cmd(HW::item<handle_t>& item,
+ const std::string& name,
+ const bond_interface::mode_t& mode,
+ const bond_interface::lb_t& lb,
+ const l2_address_t& l2_address)
+ : interface::create_cmd<vapi::Bond_create>(item, name)
+ , m_mode(mode)
+ , m_lb(lb)
+ , m_l2_address(l2_address)
+{
+}
+
+rc_t
+create_cmd::issue(connection& con)
+{
+ msg_t req(con.ctx(), std::ref(*this));
+
+ auto& payload = req.get_request().get_payload();
+
+ if (m_l2_address != l2_address_t::ZERO) {
+ m_l2_address.to_bytes(payload.mac_address, 6);
+ payload.use_custom_mac = 1;
+ }
+
+ payload.mode = m_mode.value();
+ if ((m_mode == bond_interface::mode_t::XOR ||
+ m_mode == bond_interface::mode_t::LACP) &&
+ m_lb != bond_interface::lb_t::UNSPECIFIED)
+ payload.lb = m_lb.value();
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item = wait();
+ if (m_hw_item.rc() == rc_t::OK) {
+ insert_interface();
+ }
+
+ return rc_t::OK;
+}
+
+std::string
+create_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "bond-intf-create: " << m_hw_item.to_string();
+
+ return (s.str());
+}
+
+delete_cmd::delete_cmd(HW::item<handle_t>& item)
+ : interface::delete_cmd<vapi::Bond_delete>(item)
+{
+}
+
+rc_t
+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);
+ remove_interface();
+
+ return rc_t::OK;
+}
+
+std::string
+delete_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "bond-itf-delete: " << m_hw_item.to_string();
+
+ return (s.str());
+}
+
+dump_cmd::dump_cmd()
+{
+}
+
+bool
+dump_cmd::operator==(const dump_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+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::OK;
+}
+
+std::string
+dump_cmd::to_string() const
+{
+ return ("bond-itf-dump");
+}
+} // namespace bond_interface_cmds
+} // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/bond_interface_cmds.hpp b/extras/vom/vom/bond_interface_cmds.hpp
new file mode 100644
index 00000000000..06a8ac15009
--- /dev/null
+++ b/extras/vom/vom/bond_interface_cmds.hpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_BOND_INTERFACE_CMDS_H__
+#define __VOM_BOND_INTERFACE_CMDS_H__
+
+#include "vom/bond_interface.hpp"
+#include "vom/dump_cmd.hpp"
+#include "vom/interface.hpp"
+#include "vom/rpc_cmd.hpp"
+
+#include <vapi/bond.api.vapi.hpp>
+#include <vapi/interface.api.vapi.hpp>
+
+namespace VOM {
+namespace bond_interface_cmds {
+
+/**
+ * A functor class that creates an interface
+ */
+class create_cmd : public interface::create_cmd<vapi::Bond_create>
+{
+public:
+ create_cmd(HW::item<handle_t>& item,
+ const std::string& name,
+ const bond_interface::mode_t& mode,
+ const bond_interface::lb_t& lb,
+ 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:
+ const bond_interface::mode_t m_mode;
+ const bond_interface::lb_t m_lb;
+ const l2_address_t m_l2_address;
+};
+
+/**
+ * A functor class that deletes a Tap interface
+ */
+class delete_cmd : public interface::delete_cmd<vapi::Bond_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_bond_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;
+};
+
+}; // namespace bond_interface_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+#endif
diff --git a/extras/vom/vom/bond_member.cpp b/extras/vom/vom/bond_member.cpp
new file mode 100644
index 00000000000..f1a27b3d1c6
--- /dev/null
+++ b/extras/vom/vom/bond_member.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/bond_member.hpp"
+
+namespace VOM {
+/**
+ * Construct a new object matching the desried state
+ */
+bond_member::bond_member(const interface& itf, mode_t mode, rate_t rate)
+ : m_itf(itf.singular())
+ , m_mode(mode)
+ , m_rate(rate)
+{
+}
+
+bond_member::~bond_member()
+{
+}
+
+bond_member::bond_member(const bond_member& o)
+ : m_itf(o.m_itf)
+ , m_mode(o.m_mode)
+ , m_rate(o.m_rate)
+{
+}
+
+void
+bond_member::to_vpp(vapi_payload_bond_enslave& bond_enslave) const
+{
+ bond_enslave.sw_if_index = m_itf->handle().value();
+ bond_enslave.is_passive = (m_mode == mode_t::PASSIVE) ? 1 : 0;
+ bond_enslave.is_long_timeout = (m_rate == rate_t::SLOW) ? 1 : 0;
+}
+
+std::string
+bond_member::to_string() const
+{
+ std::ostringstream s;
+
+ s << m_itf->to_string() << " mode:" << m_mode.to_string()
+ << " rate:" << m_rate.to_string();
+
+ return (s.str());
+}
+
+bool
+bond_member::operator<(const bond_member& itf) const
+{
+ return (m_itf->handle() < itf.m_itf->handle());
+}
+
+void
+bond_member::set(mode_t mode)
+{
+ m_mode = mode;
+}
+
+void
+bond_member::set(rate_t rate)
+{
+ m_rate = rate;
+}
+
+const handle_t
+bond_member::hdl(void) const
+{
+ return m_itf->handle();
+}
+
+bool
+bond_member::operator==(const bond_member& b) const
+{
+ return ((m_itf == b.m_itf) && (m_mode == b.m_mode) && (m_rate == b.m_rate));
+}
+
+const bond_member::mode_t bond_member::mode_t::ACTIVE(0, "active");
+const bond_member::mode_t bond_member::mode_t::PASSIVE(1, "passive");
+
+const bond_member::mode_t
+bond_member::mode_t::from_numeric_val(uint8_t numeric)
+{
+ if (0 == numeric)
+ return (bond_member::mode_t::ACTIVE);
+
+ return (bond_member::mode_t::PASSIVE);
+}
+
+bond_member::mode_t::mode_t(int v, const std::string& s)
+ : enum_base<bond_member::mode_t>(v, s)
+{
+}
+
+const bond_member::rate_t bond_member::rate_t::FAST(0, "fast");
+const bond_member::rate_t bond_member::rate_t::SLOW(1, "slow");
+
+const bond_member::rate_t
+bond_member::rate_t::from_numeric_val(uint8_t numeric)
+{
+ if (0 == numeric)
+ return (bond_member::rate_t::FAST);
+
+ return (bond_member::rate_t::SLOW);
+}
+
+bond_member::rate_t::rate_t(int v, const std::string& s)
+ : enum_base<bond_member::rate_t>(v, s)
+{
+}
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/bond_member.hpp b/extras/vom/vom/bond_member.hpp
new file mode 100644
index 00000000000..066933b74d7
--- /dev/null
+++ b/extras/vom/vom/bond_member.hpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_BOND_MEMBER_H__
+#define __VOM_BOND_MEMBER_H__
+
+#include "vom/interface.hpp"
+#include <vapi/bond.api.vapi.hpp>
+
+namespace VOM {
+/**
+ * A bond-member. e.g. a bond_member interface
+ */
+class bond_member
+{
+public:
+ /**
+ * A member interface mode
+ */
+ struct mode_t : enum_base<mode_t>
+ {
+ /**
+ * Active member interface mode
+ */
+ const static mode_t ACTIVE;
+ /**
+ * Passive member interface mode
+ */
+ const static mode_t PASSIVE;
+
+ /**
+ * Convert VPP's value of the bond to a mode
+ */
+ static const mode_t from_numeric_val(uint8_t v);
+
+ private:
+ /**
+ * Private constructor taking the value and the string name
+ */
+ mode_t(int v, const std::string& s);
+ };
+
+ /**
+ * A member interface rate
+ */
+ struct rate_t : enum_base<rate_t>
+ {
+ /**
+ * Fast member interface rate
+ */
+ const static rate_t FAST;
+ /**
+ * SLOW member interface rate
+ */
+ const static rate_t SLOW;
+
+ /**
+ * Convert VPP's value of the bond to a mode
+ */
+ static const rate_t from_numeric_val(uint8_t v);
+
+ private:
+ /**
+ * Private constructor taking the value and the string name
+ */
+ rate_t(int v, const std::string& s);
+ };
+
+ bond_member(const interface& itf, mode_t mode, rate_t rate);
+
+ ~bond_member();
+ bond_member(const bond_member& o);
+
+ /**
+ * convert to VPP
+ */
+ void to_vpp(vapi_payload_bond_enslave& bond_enslave) const;
+
+ /**
+ * set the mode
+ */
+ void set(mode_t mode);
+
+ /**
+ * set the rate
+ */
+ void set(rate_t rate);
+
+ /**
+ * convert to string
+ */
+ std::string to_string(void) const;
+
+ /**
+ * less-than operator
+ */
+ bool operator<(const bond_member& mem_itf) const;
+
+ /**
+ * Get the interface handle
+ */
+ const handle_t hdl(void) const;
+
+ /**
+ * equality operator
+ */
+ bool operator==(const bond_member& i) const;
+
+private:
+ /**
+ * Refernece conter lock on the parent
+ */
+ const std::shared_ptr<interface> m_itf;
+
+ /**
+ * passive vs active member
+ */
+ mode_t m_mode;
+
+ /**
+ * slow 90sec. vs. fast 3sec timeout
+ */
+ rate_t m_rate;
+};
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/bridge_domain.cpp b/extras/vom/vom/bridge_domain.cpp
new file mode 100644
index 00000000000..b8c89e10bd9
--- /dev/null
+++ b/extras/vom/vom/bridge_domain.cpp
@@ -0,0 +1,257 @@
+/*
+ * 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/bridge_domain_cmds.hpp"
+#include "vom/interface.hpp"
+#include "vom/l2_binding.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+
+const bridge_domain::learning_mode_t bridge_domain::learning_mode_t::ON(1,
+ "on");
+const bridge_domain::learning_mode_t bridge_domain::learning_mode_t::OFF(0,
+ "off");
+
+bridge_domain::learning_mode_t::learning_mode_t(int v, const std::string& s)
+ : enum_base<bridge_domain::learning_mode_t>(v, s)
+{
+}
+
+const bridge_domain::flood_mode_t bridge_domain::flood_mode_t::ON(1, "on");
+const bridge_domain::flood_mode_t bridge_domain::flood_mode_t::OFF(0, "off");
+
+bridge_domain::flood_mode_t::flood_mode_t(int v, const std::string& s)
+ : enum_base<bridge_domain::flood_mode_t>(v, s)
+{
+}
+
+const bridge_domain::mac_age_mode_t bridge_domain::mac_age_mode_t::ON(1, "on");
+const bridge_domain::mac_age_mode_t bridge_domain::mac_age_mode_t::OFF(0,
+ "off");
+
+bridge_domain::mac_age_mode_t::mac_age_mode_t(int v, const std::string& s)
+ : enum_base<bridge_domain::mac_age_mode_t>(v, s)
+{
+}
+
+const bridge_domain::arp_term_mode_t bridge_domain::arp_term_mode_t::ON(1,
+ "on");
+const bridge_domain::arp_term_mode_t bridge_domain::arp_term_mode_t::OFF(0,
+ "off");
+
+bridge_domain::arp_term_mode_t::arp_term_mode_t(int v, const std::string& s)
+ : enum_base<bridge_domain::arp_term_mode_t>(v, s)
+{
+}
+
+/**
+ * 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,
+ const learning_mode_t& lmode,
+ const arp_term_mode_t& amode,
+ const flood_mode_t& fmode,
+ const mac_age_mode_t& mmode)
+ : m_id(id)
+ , m_learning_mode(lmode)
+ , m_arp_term_mode(amode)
+ , m_flood_mode(fmode)
+ , m_mac_age_mode(mmode)
+{
+}
+
+bridge_domain::bridge_domain(const bridge_domain& o)
+ : m_id(o.m_id)
+ , m_learning_mode(o.m_learning_mode)
+ , m_arp_term_mode(o.m_arp_term_mode)
+ , m_flood_mode(o.m_flood_mode)
+ , m_mac_age_mode(o.m_mac_age_mode)
+{
+}
+
+const bridge_domain::key_t&
+bridge_domain::key() const
+{
+ return (m_id.data());
+}
+
+uint32_t
+bridge_domain::id() const
+{
+ return (m_id.data());
+}
+
+bool
+bridge_domain::operator==(const bridge_domain& b) const
+{
+ return ((m_learning_mode == b.m_learning_mode) &&
+ (m_flood_mode == b.m_flood_mode) &&
+ (m_mac_age_mode == b.m_mac_age_mode) &&
+ (m_arp_term_mode == b.m_arp_term_mode) && id() == b.id());
+}
+
+void
+bridge_domain::sweep()
+{
+ if (rc_t::OK == m_id.rc()) {
+ HW::enqueue(new bridge_domain_cmds::delete_cmd(m_id));
+ }
+ HW::write();
+}
+
+void
+bridge_domain::replay()
+{
+ if (rc_t::OK == m_id.rc()) {
+ HW::enqueue(new bridge_domain_cmds::create_cmd(
+ m_id, m_learning_mode, m_arp_term_mode, m_flood_mode, m_mac_age_mode));
+ }
+}
+
+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()
+ << " learning-mode:" << m_learning_mode.to_string() << "]";
+
+ return (s.str());
+}
+
+std::shared_ptr<bridge_domain>
+bridge_domain::find(const key_t& key)
+{
+ return (m_db.find(key));
+}
+
+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 bridge_domain_cmds::create_cmd(
+ m_id, m_learning_mode, m_arp_term_mode, m_flood_mode, m_mac_age_mode));
+ }
+}
+
+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)
+{
+ db_dump(m_db, os);
+}
+
+void
+bridge_domain::event_handler::handle_populate(const client_db::key_t& key)
+{
+ /*
+ * dump VPP Bridge domains
+ */
+ std::shared_ptr<bridge_domain_cmds::dump_cmd> cmd =
+ std::make_shared<bridge_domain_cmds::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 bridge-domains 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);
+ if (itf) {
+ 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)
+{
+ db_dump(m_db, os);
+}
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/bridge_domain.hpp b/extras/vom/vom/bridge_domain.hpp
new file mode 100644
index 00000000000..d345da238ca
--- /dev/null
+++ b/extras/vom/vom/bridge_domain.hpp
@@ -0,0 +1,264 @@
+/*
+ * 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/enum_base.hpp"
+#include "vom/hw.hpp"
+#include "vom/inspect.hpp"
+#include "vom/object_base.hpp"
+#include "vom/om.hpp"
+#include "vom/singular_db.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:
+ /**
+ * Key Type for Bridge Domains in the sigular DB
+ */
+ typedef uint32_t key_t;
+
+ /**
+ * Bridge Domain Learning mode
+ */
+ struct learning_mode_t : enum_base<learning_mode_t>
+ {
+ const static learning_mode_t ON;
+ const static learning_mode_t OFF;
+
+ private:
+ /**
+ * Private constructor taking the value and the string name
+ */
+ learning_mode_t(int v, const std::string& s);
+ };
+
+ /**
+ * Bridge Domain ARP termination mode
+ */
+ struct arp_term_mode_t : enum_base<arp_term_mode_t>
+ {
+ const static arp_term_mode_t ON;
+ const static arp_term_mode_t OFF;
+
+ private:
+ /**
+ * Private constructor taking the value and the string name
+ */
+ arp_term_mode_t(int v, const std::string& s);
+ };
+
+ /**
+ * Bridge Domain MAC aging mode
+ */
+ struct mac_age_mode_t : enum_base<mac_age_mode_t>
+ {
+ const static mac_age_mode_t ON;
+ const static mac_age_mode_t OFF;
+
+ private:
+ /**
+ * Private constructor taking the value and the string name
+ */
+ mac_age_mode_t(int v, const std::string& s);
+ };
+
+ /**
+ * Bridge Domain Learning mode
+ */
+ struct flood_mode_t : enum_base<flood_mode_t>
+ {
+ const static flood_mode_t ON;
+ const static flood_mode_t OFF;
+
+ private:
+ /**
+ * Private constructor taking the value and the string name
+ */
+ flood_mode_t(int v, const std::string& s);
+ };
+
+ /**
+ * 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,
+ const learning_mode_t& lmode = learning_mode_t::ON,
+ const arp_term_mode_t& amode = arp_term_mode_t::ON,
+ const flood_mode_t& fmode = flood_mode_t::ON,
+ const mac_age_mode_t& mmode = mac_age_mode_t::OFF);
+
+ /**
+ * Copy Constructor
+ */
+ bridge_domain(const bridge_domain& o);
+
+ /**
+ * Destructor
+ */
+ ~bridge_domain();
+
+ /**
+ * Comparison operator - for UT
+ */
+ bool operator==(const bridge_domain& b) const;
+
+ /**
+ * Return the bridge domain's VPP ID
+ */
+ uint32_t id() const;
+
+ /**
+ * Return the bridge domain's key
+ */
+ const key_t& key() const;
+
+ /**
+ * 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;
+
+ /**
+ * Static function to find the bridge_domain in the model
+ */
+ static std::shared_ptr<bridge_domain> find(const key_t& key);
+
+ /**
+ * Dump all bridge-doamin into the stream provided
+ */
+ static void dump(std::ostream& os);
+
+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<key_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;
+
+ /**
+ * The learning mode of the bridge
+ */
+ learning_mode_t m_learning_mode;
+
+ /**
+ * The ARP termination mode of the bridge
+ */
+ arp_term_mode_t m_arp_term_mode;
+
+ /**
+ * The flood mode of the bridge
+ */
+ flood_mode_t m_flood_mode;
+
+ /**
+ * The MAC aging mode of the bridge
+ */
+ mac_age_mode_t m_mac_age_mode;
+
+ /**
+ * A map of all interfaces key against the interface's name
+ */
+ static singular_db<key_t, bridge_domain> m_db;
+};
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/bridge_domain_arp_entry.cpp b/extras/vom/vom/bridge_domain_arp_entry.cpp
new file mode 100644
index 00000000000..a203a76744e
--- /dev/null
+++ b/extras/vom/vom/bridge_domain_arp_entry.cpp
@@ -0,0 +1,193 @@
+/*
+ * 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"
+#include "vom/bridge_domain_arp_entry_cmds.hpp"
+#include "vom/singular_db_funcs.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 boost::asio::ip::address& ip_addr,
+ const mac_address_t& mac)
+ : m_hw(false)
+ , m_bd(bd.singular())
+ , m_ip_addr(ip_addr)
+ , m_mac(mac)
+{
+}
+
+bridge_domain_arp_entry::bridge_domain_arp_entry(
+ const boost::asio::ip::address& ip_addr,
+ const mac_address_t& mac)
+ : m_hw(false)
+ , m_bd(nullptr)
+ , m_ip_addr(ip_addr)
+ , m_mac(mac)
+{
+ /*
+ * 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_ip_addr(bde.m_ip_addr)
+ , m_mac(bde.m_mac)
+{
+}
+
+bridge_domain_arp_entry::~bridge_domain_arp_entry()
+{
+ sweep();
+
+ // not in the DB anymore.
+ m_db.release(key(), this);
+}
+
+const bridge_domain_arp_entry::key_t
+bridge_domain_arp_entry::key() const
+{
+ return (std::make_pair(m_bd->key(), m_ip_addr));
+}
+
+bool
+bridge_domain_arp_entry::operator==(const bridge_domain_arp_entry& bdae) const
+{
+ return ((key() == bdae.key()) && (m_mac == bdae.m_mac));
+}
+
+void
+bridge_domain_arp_entry::sweep()
+{
+ if (m_hw) {
+ HW::enqueue(new bridge_domain_arp_entry_cmds::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 bridge_domain_arp_entry_cmds::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 bridge_domain_arp_entry_cmds::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(temp.key(), temp));
+}
+
+std::shared_ptr<bridge_domain_arp_entry>
+bridge_domain_arp_entry::find(const key_t& k)
+{
+ return (m_db.find(k));
+}
+
+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)
+{
+ db_dump(m_db, 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)
+{
+ db_dump(m_db, os);
+}
+
+std::ostream&
+operator<<(std::ostream& os, const bridge_domain_arp_entry::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/extras/vom/vom/bridge_domain_arp_entry.hpp b/extras/vom/vom/bridge_domain_arp_entry.hpp
new file mode 100644
index 00000000000..b4af6a0f62e
--- /dev/null
+++ b/extras/vom/vom/bridge_domain_arp_entry.hpp
@@ -0,0 +1,195 @@
+/*
+ * 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"
+
+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::pair<bridge_domain::key_t, boost::asio::ip::address> key_t;
+
+ /**
+ * Construct a bridge domain ARP Entry in the given bridge domain
+ */
+ bridge_domain_arp_entry(const bridge_domain& bd,
+ const boost::asio::ip::address& ip_addr,
+ const mac_address_t& mac);
+
+ /**
+ * Construct a bridge domain ARP entry in the default table
+ */
+ bridge_domain_arp_entry(const boost::asio::ip::address& ip_addr,
+ const mac_address_t& mac);
+
+ /**
+ * Copy Construct
+ */
+ bridge_domain_arp_entry(const bridge_domain_arp_entry& r);
+
+ /**
+ * Destructor
+ */
+ ~bridge_domain_arp_entry();
+
+ /**
+ * Return the object's key
+ */
+ const key_t key() const;
+
+ /**
+ * comparison operator
+ */
+ bool operator==(const bridge_domain_arp_entry& bdae) const;
+
+ /**
+ * 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 key_t& k);
+
+ /**
+ * 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;
+
+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 IP address
+ */
+ boost::asio::ip::address m_ip_addr;
+
+ /**
+ * The mac to return
+ */
+ mac_address_t m_mac;
+
+ /**
+ * 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);
+}; // namespace
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/bridge_domain_arp_entry_cmds.cpp b/extras/vom/vom/bridge_domain_arp_entry_cmds.cpp
new file mode 100644
index 00000000000..824dcadad02
--- /dev/null
+++ b/extras/vom/vom/bridge_domain_arp_entry_cmds.cpp
@@ -0,0 +1,125 @@
+/*
+ * 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_cmds.hpp"
+
+namespace VOM {
+namespace bridge_domain_arp_entry_cmds {
+
+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
+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
+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
+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());
+}
+
+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
+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
+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
+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());
+}
+
+}; // namespace bridge_domain_arp_entry
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/bridge_domain_arp_entry_cmds.hpp b/extras/vom/vom/bridge_domain_arp_entry_cmds.hpp
new file mode 100644
index 00000000000..094de4c7fb6
--- /dev/null
+++ b/extras/vom/vom/bridge_domain_arp_entry_cmds.hpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_BRIDGE_DOMAIN_ARP_ENTRY_CMDS_H__
+#define __VOM_BRIDGE_DOMAIN_ARP_ENTRY_CMDS_H__
+
+#include "vom/bridge_domain_arp_entry.hpp"
+
+#include <vapi/l2.api.vapi.hpp>
+#include <vapi/vpe.api.vapi.hpp>
+
+namespace VOM {
+namespace bridge_domain_arp_entry_cmds {
+
+/**
+* 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;
+};
+};
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/bridge_domain_cmds.cpp b/extras/vom/vom/bridge_domain_cmds.cpp
new file mode 100644
index 00000000000..d1d536f6d39
--- /dev/null
+++ b/extras/vom/vom/bridge_domain_cmds.cpp
@@ -0,0 +1,148 @@
+/*
+ * 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_cmds.hpp"
+
+DEFINE_VAPI_MSG_IDS_L2_API_JSON;
+
+namespace VOM {
+namespace bridge_domain_cmds {
+create_cmd::create_cmd(HW::item<uint32_t>& item,
+ const bridge_domain::learning_mode_t& lmode,
+ const bridge_domain::arp_term_mode_t& amode,
+ const bridge_domain::flood_mode_t& fmode,
+ const bridge_domain::mac_age_mode_t& mmode)
+ : rpc_cmd(item)
+ , m_learning_mode(lmode)
+ , m_arp_term_mode(amode)
+ , m_flood_mode(fmode)
+ , m_mac_age_mode(mmode)
+{
+}
+
+bool
+create_cmd::operator==(const create_cmd& other) const
+{
+ return (m_hw_item.data() == other.m_hw_item.data());
+}
+
+rc_t
+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 = m_flood_mode.value();
+ payload.uu_flood = m_flood_mode.value();
+ payload.forward = 1;
+ payload.learn = m_learning_mode.value();
+ payload.arp_term = m_arp_term_mode.value();
+ payload.mac_age = m_mac_age_mode.value();
+ payload.is_add = 1;
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return (rc_t::OK);
+}
+
+std::string
+create_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "bridge-domain-create: " << m_hw_item.to_string();
+
+ return (s.str());
+}
+
+delete_cmd::delete_cmd(HW::item<uint32_t>& item)
+ : rpc_cmd(item)
+{
+}
+
+bool
+delete_cmd::operator==(const delete_cmd& other) const
+{
+ return (m_hw_item == other.m_hw_item);
+}
+
+rc_t
+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
+delete_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "bridge-domain-delete: " << m_hw_item.to_string();
+
+ return (s.str());
+}
+
+dump_cmd::dump_cmd()
+{
+}
+
+bool
+dump_cmd::operator==(const dump_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+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
+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/extras/vom/vom/bridge_domain_cmds.hpp b/extras/vom/vom/bridge_domain_cmds.hpp
new file mode 100644
index 00000000000..0216236d42c
--- /dev/null
+++ b/extras/vom/vom/bridge_domain_cmds.hpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_BRIDGE_DOMAIN_CMDS_H__
+#define __VOM_BRIDGE_DOMAIN_CMDS_H__
+
+#include "vom/bridge_domain.hpp"
+#include "vom/dump_cmd.hpp"
+#include "vom/rpc_cmd.hpp"
+
+#include <vapi/l2.api.vapi.hpp>
+
+namespace VOM {
+namespace bridge_domain_cmds {
+/**
+ * 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,
+ const bridge_domain::learning_mode_t& lmode,
+ const bridge_domain::arp_term_mode_t& amode,
+ const bridge_domain::flood_mode_t& fmode,
+ const bridge_domain::mac_age_mode_t& mmode);
+
+ /**
+ * 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:
+ /**
+ * the learning mode for the bridge
+ */
+ bridge_domain::learning_mode_t m_learning_mode;
+ /**
+ * the learning mode for the bridge
+ */
+ bridge_domain::arp_term_mode_t m_arp_term_mode;
+ /**
+ * the flood mode for the bridge
+ */
+ bridge_domain::flood_mode_t m_flood_mode;
+ /**
+ * the flood mode for the bridge
+ */
+ bridge_domain::mac_age_mode_t m_mac_age_mode;
+};
+
+/**
+ * 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;
+};
+};
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/bridge_domain_entry.cpp b/extras/vom/vom/bridge_domain_entry.cpp
new file mode 100644
index 00000000000..241de953771
--- /dev/null
+++ b/extras/vom/vom/bridge_domain_entry.cpp
@@ -0,0 +1,224 @@
+/*
+ * 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"
+#include "vom/bridge_domain_entry_cmds.hpp"
+#include "vom/singular_db_funcs.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 entry goes in the default bridge-domain
+ */
+ 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)
+{
+}
+
+const bridge_domain_entry::key_t
+bridge_domain_entry::key() const
+{
+ return (std::make_pair(m_bd->key(), m_mac));
+}
+
+bool
+bridge_domain_entry::operator==(const bridge_domain_entry& bde) const
+{
+ return ((key() == bde.key()) && (m_tx_itf == bde.m_tx_itf));
+}
+
+bridge_domain_entry::~bridge_domain_entry()
+{
+ sweep();
+
+ // not in the DB anymore.
+ m_db.release(key(), this);
+}
+
+void
+bridge_domain_entry::sweep()
+{
+ if (m_hw) {
+ HW::enqueue(new bridge_domain_entry_cmds::delete_cmd(
+ m_hw, m_mac, m_bd->id(), interface::type_t::BVI == m_tx_itf->type()));
+ }
+ HW::write();
+}
+
+void
+bridge_domain_entry::replay()
+{
+ if (m_hw) {
+ HW::enqueue(new bridge_domain_entry_cmds::create_cmd(
+ m_hw, m_mac, m_bd->id(), m_tx_itf->handle(),
+ interface::type_t::BVI == m_tx_itf->type()));
+ }
+}
+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 bridge_domain_entry_cmds::create_cmd(
+ m_hw, m_mac, m_bd->id(), m_tx_itf->handle(),
+ interface::type_t::BVI == m_tx_itf->type()));
+ }
+}
+
+std::shared_ptr<bridge_domain_entry>
+bridge_domain_entry::find_or_add(const bridge_domain_entry& temp)
+{
+ return (m_db.find_or_add(temp.key(), temp));
+}
+
+std::shared_ptr<bridge_domain_entry>
+bridge_domain_entry::find(const key_t& k)
+{
+ return (m_db.find(k));
+}
+
+std::shared_ptr<bridge_domain_entry>
+bridge_domain_entry::singular() const
+{
+ return find_or_add(*this);
+}
+
+void
+bridge_domain_entry::dump(std::ostream& os)
+{
+ db_dump(m_db, 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_cmds::dump_cmd> cmd =
+ std::make_shared<bridge_domain_entry_cmds::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);
+
+ if (!bd || !itf) {
+ VOM_LOG(log_level_t::ERROR) << "bridge-domain-entry dump:"
+ << " itf:" << payload.sw_if_index
+ << " bd:" << payload.bd_id;
+ continue;
+ }
+
+ mac_address_t mac(payload.mac);
+ bridge_domain_entry bd_e(*bd, mac, *itf);
+
+ VOM_LOG(log_level_t::DEBUG) << "bridge-domain-entry dump:"
+ << " " << bd->to_string() << " "
+ << itf->to_string() << " mac:["
+ << mac.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)
+{
+ db_dump(m_db, 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/extras/vom/vom/bridge_domain_entry.hpp b/extras/vom/vom/bridge_domain_entry.hpp
new file mode 100644
index 00000000000..2aef697ebca
--- /dev/null
+++ b/extras/vom/vom/bridge_domain_entry.hpp
@@ -0,0 +1,192 @@
+/*
+ * 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"
+
+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<bridge_domain::key_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 object's key
+ */
+ const key_t key() const;
+
+ /**
+ * comparison operator
+ */
+ bool operator==(const bridge_domain_entry& be) const;
+
+ /**
+ * 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 key_t& k);
+
+ /**
+ * 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;
+
+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/extras/vom/vom/bridge_domain_entry_cmds.cpp b/extras/vom/vom/bridge_domain_entry_cmds.cpp
new file mode 100644
index 00000000000..f2a3ed9c2cc
--- /dev/null
+++ b/extras/vom/vom/bridge_domain_entry_cmds.cpp
@@ -0,0 +1,157 @@
+/*
+ * 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_cmds.hpp"
+
+namespace VOM {
+namespace bridge_domain_entry_cmds {
+create_cmd::create_cmd(HW::item<bool>& item,
+ const mac_address_t& mac,
+ uint32_t bd,
+ handle_t tx_itf,
+ bool is_bvi)
+ : rpc_cmd(item)
+ , m_mac(mac)
+ , m_bd(bd)
+ , m_tx_itf(tx_itf)
+ , m_is_bvi(is_bvi)
+{
+}
+
+bool
+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) && (m_is_bvi == other.m_is_bvi));
+}
+
+rc_t
+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, 6);
+ payload.sw_if_index = m_tx_itf.value();
+ payload.bvi_mac = m_is_bvi;
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return rc_t::OK;
+}
+
+std::string
+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
+ << " bvi:" << m_is_bvi;
+
+ return (s.str());
+}
+
+delete_cmd::delete_cmd(HW::item<bool>& item,
+ const mac_address_t& mac,
+ uint32_t bd,
+ bool is_bvi)
+ : rpc_cmd(item)
+ , m_mac(mac)
+ , m_bd(bd)
+ , m_is_bvi(is_bvi)
+{
+}
+
+bool
+delete_cmd::operator==(const delete_cmd& other) const
+{
+ return ((m_mac == other.m_mac) && (m_bd == other.m_bd) &&
+ (m_is_bvi == other.m_is_bvi));
+}
+
+rc_t
+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, 6);
+ payload.sw_if_index = ~0;
+ payload.bvi_mac = m_is_bvi;
+
+ VAPI_CALL(req.execute());
+
+ wait();
+ m_hw_item.set(rc_t::NOOP);
+
+ return rc_t::OK;
+}
+
+std::string
+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() << " bvi:" << m_is_bvi;
+
+ return (s.str());
+}
+
+dump_cmd::dump_cmd()
+{
+}
+
+bool
+dump_cmd::operator==(const dump_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+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
+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/extras/vom/vom/bridge_domain_entry_cmds.hpp b/extras/vom/vom/bridge_domain_entry_cmds.hpp
new file mode 100644
index 00000000000..dc46719ffda
--- /dev/null
+++ b/extras/vom/vom/bridge_domain_entry_cmds.hpp
@@ -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.
+ */
+
+#ifndef __VOM_BRIDGE_DOMAIN_ENTRY_CMDS_H__
+#define __VOM_BRIDGE_DOMAIN_ENTRY_CMDS_H__
+
+#include "vom/bridge_domain_entry.hpp"
+#include "vom/dump_cmd.hpp"
+#include "vom/rpc_cmd.hpp"
+
+#include <vapi/l2.api.vapi.hpp>
+
+namespace VOM {
+namespace bridge_domain_entry_cmds {
+
+/**
+* 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,
+ 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 create_cmd& i) const;
+
+private:
+ mac_address_t m_mac;
+ uint32_t m_bd;
+ handle_t m_tx_itf;
+ bool m_is_bvi;
+};
+
+/**
+ * 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,
+ 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 delete_cmd& i) const;
+
+private:
+ mac_address_t m_mac;
+ uint32_t m_bd;
+ bool m_is_bvi;
+};
+
+/**
+ * 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;
+};
+};
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/client_db.cpp b/extras/vom/vom/client_db.cpp
new file mode 100644
index 00000000000..41463d1dd74
--- /dev/null
+++ b/extras/vom/vom/client_db.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/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)
+{
+ auto found = m_objs.find(k);
+
+ if (found != m_objs.end())
+ m_objs.erase(found);
+}
+
+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/extras/vom/vom/client_db.hpp b/extras/vom/vom/client_db.hpp
new file mode 100644
index 00000000000..34204c1d839
--- /dev/null
+++ b/extras/vom/vom/client_db.hpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_KEY_DB_H__
+#define __VOM_KEY_DB_H__
+
+#include <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/extras/vom/vom/cmd.cpp b/extras/vom/vom/cmd.cpp
new file mode 100644
index 00000000000..5623507a63f
--- /dev/null
+++ b/extras/vom/vom/cmd.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/cmd.hpp"
+
+namespace VOM {
+/**
+ * Free ostream function to print a command
+ */
+std::ostream&
+operator<<(std::ostream& os, const cmd& cmd)
+{
+ os << cmd.to_string();
+
+ return (os);
+}
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/cmd.hpp b/extras/vom/vom/cmd.hpp
new file mode 100644
index 00000000000..9c87d31218a
--- /dev/null
+++ b/extras/vom/vom/cmd.hpp
@@ -0,0 +1,80 @@
+/*
+ * 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"
+
+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/extras/vom/vom/connection.cpp b/extras/vom/vom/connection.cpp
new file mode 100644
index 00000000000..c03ef99c172
--- /dev/null
+++ b/extras/vom/vom/connection.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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 <vapi/vapi.hpp>
+
+#include "vom/connection.hpp"
+
+namespace VOM {
+connection::connection()
+ : m_vapi_conn(new vapi::Connection())
+ , m_app_name("VOM")
+{
+}
+
+connection::~connection()
+{
+}
+
+void
+connection::disconnect()
+{
+ m_vapi_conn->disconnect();
+}
+
+int
+connection::connect()
+{
+ vapi_error_e rv;
+
+ rv = m_vapi_conn->connect(m_app_name.c_str(),
+ NULL, // m_api_prefix.c_str(),
+ 128, 128);
+ return 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/extras/vom/vom/connection.hpp b/extras/vom/vom/connection.hpp
new file mode 100644
index 00000000000..06fe840232d
--- /dev/null
+++ b/extras/vom/vom/connection.hpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_CONNECTION_H__
+#define __VOM_CONNECTION_H__
+
+#include <memory>
+#include <string>
+
+/**
+ * Forward declarations
+ */
+namespace vapi {
+class Connection;
+};
+
+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.
+ */
+ int connect();
+
+ /**
+ * Blocking disconnect
+ */
+ void disconnect();
+
+ /**
+ * Retrun the VAPI context the commands will use
+ */
+ vapi::Connection& ctx();
+
+private:
+ /**
+ * The VAPI connection context
+ */
+ std::unique_ptr<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/extras/vom/vom/dhcp_config.cpp b/extras/vom/vom/dhcp_config.cpp
new file mode 100644
index 00000000000..7d97fa15d6e
--- /dev/null
+++ b/extras/vom/vom/dhcp_config.cpp
@@ -0,0 +1,194 @@
+/*
+ * 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/dhcp_config_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+/**
+ * A DB of all DHCP configs
+ */
+singular_db<interface::key_t, 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,
+ bool set_broadcast_flag)
+ : m_itf(itf.singular())
+ , m_hostname(hostname)
+ , m_client_id(l2_address_t::ZERO)
+ , m_set_broadcast_flag(set_broadcast_flag)
+ , m_binding(0)
+{
+}
+
+dhcp_config::dhcp_config(const interface& itf,
+ const std::string& hostname,
+ const l2_address_t& client_id,
+ bool set_broadcast_flag)
+ : m_itf(itf.singular())
+ , m_hostname(hostname)
+ , m_client_id(client_id)
+ , m_set_broadcast_flag(set_broadcast_flag)
+ , 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_set_broadcast_flag(o.m_set_broadcast_flag)
+ , m_binding(0)
+{
+}
+
+dhcp_config::~dhcp_config()
+{
+ sweep();
+
+ // not in the DB anymore.
+ m_db.release(m_itf->key(), this);
+}
+
+bool
+dhcp_config::operator==(const dhcp_config& l) const
+{
+ return ((key() == l.key()) && (m_hostname == l.m_hostname) &&
+ (m_client_id == l.m_client_id));
+}
+
+const dhcp_config::key_t&
+dhcp_config::key() const
+{
+ return (m_itf->key());
+}
+
+void
+dhcp_config::sweep()
+{
+ if (m_binding) {
+ HW::enqueue(
+ new dhcp_config_cmds::unbind_cmd(m_binding, m_itf->handle(), m_hostname));
+ }
+ HW::write();
+}
+
+void
+dhcp_config::dump(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+
+void
+dhcp_config::replay()
+{
+ if (m_binding) {
+ HW::enqueue(new dhcp_config_cmds::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 dhcp_config_cmds::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::find(const key_t& k)
+{
+ return (m_db.find(k));
+}
+
+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)
+{
+ db_dump(m_db, os);
+}
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/dhcp_config.hpp b/extras/vom/vom/dhcp_config.hpp
new file mode 100644
index 00000000000..8ea608d809d
--- /dev/null
+++ b/extras/vom/vom/dhcp_config.hpp
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_DHCP_CONFIG_H__
+#define __VOM_DHCP_CONFIG_H__
+
+#include "vom/hw.hpp"
+#include "vom/inspect.hpp"
+#include "vom/interface.hpp"
+#include "vom/object_base.hpp"
+#include "vom/om.hpp"
+#include "vom/singular_db.hpp"
+
+namespace VOM {
+namespace dhcp_config_cmds {
+class events_cmd;
+};
+/**
+ * A representation of DHCP client configuration on an interface
+ */
+class dhcp_config : public object_base
+{
+public:
+ /**
+ * typedef for the DHCP config key type
+ */
+ typedef interface::key_t key_t;
+
+ /**
+ * Construct a new object matching the desried state
+ */
+ dhcp_config(const interface& itf,
+ const std::string& hostname,
+ bool set_broadcast_flag = true);
+
+ /**
+ * Construct a new object matching the desried state
+ */
+ dhcp_config(const interface& itf,
+ const std::string& hostname,
+ const l2_address_t& client_id,
+ bool set_broadcast_flag = true);
+
+ /**
+ * Copy Constructor
+ */
+ dhcp_config(const dhcp_config& o);
+
+ /**
+ * Destructor
+ */
+ ~dhcp_config();
+
+ /**
+ * Comparison operator - for UT
+ */
+ bool operator==(const dhcp_config& d) const;
+
+ /**
+ * Return the object's key
+ */
+ const key_t& key() const;
+
+ /**
+ * 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);
+
+ /**
+ * Find a DHCP config from its key
+ */
+ static std::shared_ptr<dhcp_config> find(const key_t& k);
+
+ /**
+ * 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(dhcp_config_cmds::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;
+ };
+
+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<key_t, 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;
+
+ /**
+ * Flag to control the setting the of DHCP discover's broadcast flag
+ */
+ const bool m_set_broadcast_flag;
+
+ /**
+ * 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<key_t, dhcp_config> m_db;
+};
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/dhcp_config_cmds.cpp b/extras/vom/vom/dhcp_config_cmds.cpp
new file mode 100644
index 00000000000..76ce58b6b92
--- /dev/null
+++ b/extras/vom/vom/dhcp_config_cmds.cpp
@@ -0,0 +1,178 @@
+/*
+ * 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_cmds.hpp"
+
+DEFINE_VAPI_MSG_IDS_DHCP_API_JSON;
+
+namespace VOM {
+namespace dhcp_config_cmds {
+
+bind_cmd::bind_cmd(HW::item<bool>& item,
+ const handle_t& itf,
+ const std::string& hostname,
+ const l2_address_t& client_id,
+ bool set_broadcast_flag)
+ : rpc_cmd(item)
+ , m_itf(itf)
+ , m_hostname(hostname)
+ , m_client_id(client_id)
+ , m_set_broadcast_flag(set_broadcast_flag)
+{
+}
+
+bool
+bind_cmd::operator==(const bind_cmd& other) const
+{
+ return ((m_itf == other.m_itf) && (m_hostname == other.m_hostname));
+}
+
+rc_t
+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;
+ payload.set_broadcast_flag = m_set_broadcast_flag;
+
+ memset(payload.hostname, 0, sizeof(payload.hostname));
+ 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
+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());
+}
+
+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
+unbind_cmd::operator==(const unbind_cmd& other) const
+{
+ return ((m_itf == other.m_itf) && (m_hostname == other.m_hostname));
+}
+
+rc_t
+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
+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());
+}
+
+events_cmd::events_cmd(dhcp_config::event_listener& el)
+ : event_cmd(el.status())
+ , m_listener(el)
+{
+}
+
+bool
+events_cmd::operator==(const events_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+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::OK);
+}
+
+void
+events_cmd::retire(connection& con)
+{
+}
+
+void
+events_cmd::notify()
+{
+ m_listener.handle_dhcp_event(this);
+}
+
+std::string
+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/extras/vom/vom/dhcp_config_cmds.hpp b/extras/vom/vom/dhcp_config_cmds.hpp
new file mode 100644
index 00000000000..726ff992577
--- /dev/null
+++ b/extras/vom/vom/dhcp_config_cmds.hpp
@@ -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.
+ */
+
+#ifndef __VOM_DHCP_CONFIG_CMDS_H__
+#define __VOM_DHCP_CONFIG_CMDS_H__
+
+#include "vom/dhcp_config.hpp"
+#include "vom/event_cmd.hpp"
+
+#include <vapi/dhcp.api.vapi.hpp>
+#include <vapi/vpe.api.vapi.hpp>
+
+namespace VOM {
+namespace dhcp_config_cmds {
+
+/**
+ * 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,
+ bool set_braodcast_flag = false);
+
+ /**
+ * 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;
+
+ /**
+ * Flag to control the setting the of DHCP discover's broadcast flag
+ */
+ const bool m_set_broadcast_flag;
+};
+
+/**
+ * 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;
+};
+
+/**
+ * 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(dhcp_config::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
+ */
+ dhcp_config::event_listener& m_listener;
+};
+};
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/dump_cmd.hpp b/extras/vom/vom/dump_cmd.hpp
new file mode 100644
index 00000000000..4dad02b911e
--- /dev/null
+++ b/extras/vom/vom/dump_cmd.hpp
@@ -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.
+ */
+
+#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() {}
+
+ dump_cmd(const dump_cmd& d) = default;
+
+ /**
+ * Constant iterator to the start of the records retunred during the dump
+ */
+ const_iterator begin()
+ {
+ /*
+ * m_dump is NULL during client UT when the commands are not issued.
+ */
+ if (!m_dump)
+ return const_iterator();
+ return (m_dump->get_result_set().begin());
+ }
+
+ /**
+ * Constant iterator to the end of the records retunred during the dump
+ */
+ const_iterator end()
+ {
+ if (!m_dump)
+ return const_iterator();
+ 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/extras/vom/vom/enum_base.hpp b/extras/vom/vom/enum_base.hpp
new file mode 100644
index 00000000000..6756e2498de
--- /dev/null
+++ b/extras/vom/vom/enum_base.hpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_ENUM_H__
+#define __VOM_ENUM_H__
+
+#include <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
+ */
+ operator int() const { return (m_value); }
+
+ /**
+ * Return the value of the enum - same as integer conversion
+ */
+ int value() const { return (m_value); }
+
+protected:
+ /**
+ * Constructor of an enum - takes value and string description
+ */
+ 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/extras/vom/vom/event_cmd.hpp b/extras/vom/vom/event_cmd.hpp
new file mode 100644
index 00000000000..a0e9b4af112
--- /dev/null
+++ b/extras/vom/vom/event_cmd.hpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_EVENT_CMD_H__
+#define __VOM_EVENT_CMD_H__
+
+#include <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/extras/vom/vom/gbp_contract.cpp b/extras/vom/vom/gbp_contract.cpp
new file mode 100644
index 00000000000..8b27269249c
--- /dev/null
+++ b/extras/vom/vom/gbp_contract.cpp
@@ -0,0 +1,194 @@
+/*
+ * 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/gbp_contract.hpp"
+#include "vom/gbp_contract_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+
+singular_db<gbp_contract::key_t, gbp_contract> gbp_contract::m_db;
+
+gbp_contract::event_handler gbp_contract::m_evh;
+
+gbp_contract::gbp_contract(epg_id_t src_epg_id,
+ epg_id_t dst_epg_id,
+ const ACL::l3_list& acl)
+ : m_hw(false)
+ , m_src_epg_id(src_epg_id)
+ , m_dst_epg_id(dst_epg_id)
+ , m_acl(acl.singular())
+{
+}
+
+gbp_contract::gbp_contract(const gbp_contract& gbpc)
+ : m_hw(gbpc.m_hw)
+ , m_src_epg_id(gbpc.m_src_epg_id)
+ , m_dst_epg_id(gbpc.m_dst_epg_id)
+ , m_acl(gbpc.m_acl)
+{
+}
+
+gbp_contract::~gbp_contract()
+{
+ sweep();
+
+ // not in the DB anymore.
+ m_db.release(key(), this);
+}
+
+const gbp_contract::key_t
+gbp_contract::key() const
+{
+ return (std::make_pair(m_src_epg_id, m_dst_epg_id));
+}
+
+bool
+gbp_contract::operator==(const gbp_contract& gbpc) const
+{
+ return ((key() == gbpc.key()) && (m_acl->handle() == gbpc.m_acl->handle()));
+}
+
+void
+gbp_contract::sweep()
+{
+ if (m_hw) {
+ HW::enqueue(
+ new gbp_contract_cmds::delete_cmd(m_hw, m_src_epg_id, m_dst_epg_id));
+ }
+ HW::write();
+}
+
+void
+gbp_contract::replay()
+{
+ if (m_hw) {
+ HW::enqueue(new gbp_contract_cmds::create_cmd(
+ m_hw, m_src_epg_id, m_dst_epg_id, m_acl->handle()));
+ }
+}
+
+std::string
+gbp_contract::to_string() const
+{
+ std::ostringstream s;
+ s << "gbp-contract:[{" << m_src_epg_id << ", " << m_dst_epg_id << "}, "
+ << m_acl->to_string() << "]";
+
+ return (s.str());
+}
+
+void
+gbp_contract::update(const gbp_contract& r)
+{
+ /*
+ * create the table if it is not yet created
+ */
+ if (rc_t::OK != m_hw.rc()) {
+ HW::enqueue(new gbp_contract_cmds::create_cmd(
+ m_hw, m_src_epg_id, m_dst_epg_id, m_acl->handle()));
+ }
+}
+
+std::shared_ptr<gbp_contract>
+gbp_contract::find_or_add(const gbp_contract& temp)
+{
+ return (m_db.find_or_add(temp.key(), temp));
+}
+
+std::shared_ptr<gbp_contract>
+gbp_contract::find(const key_t& k)
+{
+ return (m_db.find(k));
+}
+
+std::shared_ptr<gbp_contract>
+gbp_contract::singular() const
+{
+ return find_or_add(*this);
+}
+
+void
+gbp_contract::dump(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+
+gbp_contract::event_handler::event_handler()
+{
+ OM::register_listener(this);
+ inspect::register_handler({ "gbp-contract" }, "GBP Contract", this);
+}
+
+void
+gbp_contract::event_handler::handle_replay()
+{
+ m_db.replay();
+}
+
+void
+gbp_contract::event_handler::handle_populate(const client_db::key_t& key)
+{
+ std::shared_ptr<gbp_contract_cmds::dump_cmd> cmd =
+ std::make_shared<gbp_contract_cmds::dump_cmd>();
+
+ HW::enqueue(cmd);
+ HW::write();
+
+ for (auto& record : *cmd) {
+ auto& payload = record.get_payload();
+
+ std::shared_ptr<ACL::l3_list> acl =
+ ACL::l3_list::find(payload.contract.acl_index);
+
+ if (acl) {
+ gbp_contract gbpc(payload.contract.src_epg, payload.contract.dst_epg,
+ *acl);
+ OM::commit(key, gbpc);
+
+ VOM_LOG(log_level_t::DEBUG) << "read: " << gbpc.to_string();
+ }
+ }
+}
+
+dependency_t
+gbp_contract::event_handler::order() const
+{
+ return (dependency_t::ENTRY);
+}
+
+void
+gbp_contract::event_handler::show(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+
+std::ostream&
+operator<<(std::ostream& os, const gbp_contract::key_t& key)
+{
+ os << "{ " << key.first << "," << key.second << "}";
+
+ return (os);
+}
+
+} // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/gbp_contract.hpp b/extras/vom/vom/gbp_contract.hpp
new file mode 100644
index 00000000000..7a0696de7b3
--- /dev/null
+++ b/extras/vom/vom/gbp_contract.hpp
@@ -0,0 +1,188 @@
+/*
+ * 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_GBP_CONTRACT_H__
+#define __VOM_GBP_CONTRACT_H__
+
+#include "vom/acl_list.hpp"
+#include "vom/gbp_endpoint.hpp"
+#include "vom/interface.hpp"
+#include "vom/singular_db.hpp"
+#include "vom/types.hpp"
+
+namespace VOM {
+
+/**
+ * A entry in the ARP termination table of a Bridge Domain
+ */
+class gbp_contract : public object_base
+{
+public:
+ /**
+ * The key for a contract is the pari of EPG-IDs
+ */
+ typedef std::pair<epg_id_t, epg_id_t> key_t;
+
+ /**
+ * Construct a GBP contract
+ */
+ gbp_contract(epg_id_t src_epg_id,
+ epg_id_t dst_epg_id,
+ const ACL::l3_list& acl);
+
+ /**
+ * Copy Construct
+ */
+ gbp_contract(const gbp_contract& r);
+
+ /**
+ * Destructor
+ */
+ ~gbp_contract();
+
+ /**
+ * Return the object's key
+ */
+ const key_t key() const;
+
+ /**
+ * comparison operator
+ */
+ bool operator==(const gbp_contract& bdae) const;
+
+ /**
+ * Return the matching 'singular instance'
+ */
+ std::shared_ptr<gbp_contract> singular() const;
+
+ /**
+ * Find the instnace of the bridge_domain domain in the OM
+ */
+ static std::shared_ptr<gbp_contract> find(const key_t& k);
+
+ /**
+ * 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;
+
+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 gbp_contract& obj);
+
+ /**
+ * Find or add the instance of the contract domain in the OM
+ */
+ static std::shared_ptr<gbp_contract> find_or_add(const gbp_contract& 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, gbp_contract>;
+
+ /**
+ * Sweep/reap the object if still stale
+ */
+ void sweep(void);
+
+ /**
+ * HW configuration for the result of creating the endpoint
+ */
+ HW::item<bool> m_hw;
+
+ /**
+ * The source EPG ID
+ */
+ epg_id_t m_src_epg_id;
+
+ /**
+ * The destination EPG ID
+ */
+ epg_id_t m_dst_epg_id;
+
+ /**
+ * The ACL applied to traffic between the gourps
+ */
+ std::shared_ptr<ACL::l3_list> m_acl;
+
+ /**
+ * A map of all bridge_domains
+ */
+ static singular_db<key_t, gbp_contract> m_db;
+};
+
+std::ostream& operator<<(std::ostream& os, const gbp_contract::key_t& key);
+}; // namespace
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/gbp_contract_cmds.cpp b/extras/vom/vom/gbp_contract_cmds.cpp
new file mode 100644
index 00000000000..a98dc62bc59
--- /dev/null
+++ b/extras/vom/vom/gbp_contract_cmds.cpp
@@ -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.
+ */
+
+#include "vom/gbp_contract_cmds.hpp"
+
+namespace VOM {
+namespace gbp_contract_cmds {
+
+create_cmd::create_cmd(HW::item<bool>& item,
+ epg_id_t src_epg_id,
+ epg_id_t dst_epg_id,
+ const handle_t& acl)
+ : rpc_cmd(item)
+ , m_src_epg_id(src_epg_id)
+ , m_dst_epg_id(dst_epg_id)
+ , m_acl(acl)
+{
+}
+
+bool
+create_cmd::operator==(const create_cmd& other) const
+{
+ return ((m_acl == other.m_acl) && (m_src_epg_id == other.m_src_epg_id) &&
+ (m_dst_epg_id == other.m_dst_epg_id));
+}
+
+rc_t
+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.contract.acl_index = m_acl.value();
+ payload.contract.src_epg = m_src_epg_id;
+ payload.contract.dst_epg = m_dst_epg_id;
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return rc_t::OK;
+}
+
+std::string
+create_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "gbp-contract-create: " << m_hw_item.to_string()
+ << " src-epg-id:" << m_src_epg_id << " dst-epg-id:" << m_dst_epg_id
+ << " acl:" << m_acl;
+
+ return (s.str());
+}
+
+delete_cmd::delete_cmd(HW::item<bool>& item,
+ epg_id_t src_epg_id,
+ epg_id_t dst_epg_id)
+ : rpc_cmd(item)
+ , m_src_epg_id(src_epg_id)
+ , m_dst_epg_id(dst_epg_id)
+{
+}
+
+bool
+delete_cmd::operator==(const delete_cmd& other) const
+{
+ return ((m_src_epg_id == other.m_src_epg_id) &&
+ (m_dst_epg_id == other.m_dst_epg_id));
+}
+
+rc_t
+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.contract.acl_index = ~0;
+ payload.contract.src_epg = m_src_epg_id;
+ payload.contract.dst_epg = m_dst_epg_id;
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return rc_t::OK;
+}
+
+std::string
+delete_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "gbp-contract-delete: " << m_hw_item.to_string()
+ << " src-epg-id:" << m_src_epg_id << " dst-epg-id:" << m_dst_epg_id;
+
+ return (s.str());
+}
+
+bool
+dump_cmd::operator==(const dump_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+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::OK;
+}
+
+std::string
+dump_cmd::to_string() const
+{
+ return ("gbp-contract-dump");
+}
+
+}; // namespace gbp_contract_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/gbp_contract_cmds.hpp b/extras/vom/vom/gbp_contract_cmds.hpp
new file mode 100644
index 00000000000..705c1a0a3db
--- /dev/null
+++ b/extras/vom/vom/gbp_contract_cmds.hpp
@@ -0,0 +1,137 @@
+/*
+ * 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_GBP_CONTRACT_CMDS_H__
+#define __VOM_GBP_CONTRACT_CMDS_H__
+
+#include "vom/dump_cmd.hpp"
+#include "vom/gbp_contract.hpp"
+
+#include <vapi/gbp.api.vapi.hpp>
+
+namespace VOM {
+namespace gbp_contract_cmds {
+
+/**
+* A command class that creates or updates the GBP contract
+*/
+class create_cmd
+ : public rpc_cmd<HW::item<bool>, rc_t, vapi::Gbp_contract_add_del>
+{
+public:
+ /**
+ * Constructor
+ */
+ create_cmd(HW::item<bool>& item,
+ epg_id_t src_epg_id,
+ epg_id_t dst_epg_id,
+ const handle_t& acl);
+
+ /**
+ * 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:
+ const epg_id_t m_src_epg_id;
+ const epg_id_t m_dst_epg_id;
+ const handle_t m_acl;
+};
+
+/**
+ * A cmd class that deletes a GBP contract
+ */
+class delete_cmd
+ : public rpc_cmd<HW::item<bool>, rc_t, vapi::Gbp_contract_add_del>
+{
+public:
+ /**
+ * Constructor
+ */
+ delete_cmd(HW::item<bool>& item, epg_id_t src_epg_id, epg_id_t dst_epg_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:
+ const epg_id_t m_src_epg_id;
+ const epg_id_t m_dst_epg_id;
+};
+
+/**
+ * A cmd class that Dumps all the GBP endpoints
+ */
+class dump_cmd : public VOM::dump_cmd<vapi::Gbp_contract_dump>
+{
+public:
+ /**
+ * Constructor
+ */
+ dump_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;
+
+ /**
+ * Comparison operator - only used for UT
+ */
+ bool operator==(const dump_cmd& i) const;
+
+private:
+ /**
+ * HW reutrn code
+ */
+ HW::item<bool> item;
+};
+}; // namespace gbp_contract_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/gbp_endpoint.cpp b/extras/vom/vom/gbp_endpoint.cpp
new file mode 100644
index 00000000000..9762a91429a
--- /dev/null
+++ b/extras/vom/vom/gbp_endpoint.cpp
@@ -0,0 +1,188 @@
+/*
+ * 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/gbp_endpoint.hpp"
+#include "vom/gbp_endpoint_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+
+singular_db<gbp_endpoint::key_t, gbp_endpoint> gbp_endpoint::m_db;
+
+gbp_endpoint::event_handler gbp_endpoint::m_evh;
+
+gbp_endpoint::gbp_endpoint(const interface& itf,
+ const boost::asio::ip::address& ip_addr,
+ const mac_address_t& mac,
+ const gbp_endpoint_group& epg)
+ : m_hw(false)
+ , m_itf(itf.singular())
+ , m_ip(ip_addr)
+ , m_mac(mac)
+ , m_epg(epg.singular())
+{
+}
+
+gbp_endpoint::gbp_endpoint(const gbp_endpoint& gbpe)
+ : m_hw(gbpe.m_hw)
+ , m_itf(gbpe.m_itf)
+ , m_ip(gbpe.m_ip)
+ , m_mac(gbpe.m_mac)
+ , m_epg(gbpe.m_epg)
+{
+}
+
+gbp_endpoint::~gbp_endpoint()
+{
+ sweep();
+ m_db.release(key(), this);
+}
+
+const gbp_endpoint::key_t
+gbp_endpoint::key() const
+{
+ return (std::make_pair(m_itf->key(), m_ip));
+}
+
+bool
+gbp_endpoint::operator==(const gbp_endpoint& gbpe) const
+{
+ return ((key() == gbpe.key()) && (m_epg == gbpe.m_epg));
+}
+
+void
+gbp_endpoint::sweep()
+{
+ if (m_hw) {
+ HW::enqueue(new gbp_endpoint_cmds::delete_cmd(m_hw, m_itf->handle(), m_ip));
+ }
+ HW::write();
+}
+
+void
+gbp_endpoint::replay()
+{
+ if (m_hw) {
+ HW::enqueue(new gbp_endpoint_cmds::create_cmd(m_hw, m_itf->handle(), m_ip,
+ m_mac, m_epg->id()));
+ }
+}
+
+std::string
+gbp_endpoint::to_string() const
+{
+ std::ostringstream s;
+ s << "gbp-endpoint:[" << m_itf->to_string() << ", " << m_ip.to_string()
+ << ", " << m_mac.to_string() << ", epg:" << m_epg->to_string() << "]";
+
+ return (s.str());
+}
+
+void
+gbp_endpoint::update(const gbp_endpoint& r)
+{
+ if (rc_t::OK != m_hw.rc()) {
+ HW::enqueue(new gbp_endpoint_cmds::create_cmd(m_hw, m_itf->handle(), m_ip,
+ m_mac, m_epg->id()));
+ }
+}
+
+std::shared_ptr<gbp_endpoint>
+gbp_endpoint::find_or_add(const gbp_endpoint& temp)
+{
+ return (m_db.find_or_add(temp.key(), temp));
+}
+
+std::shared_ptr<gbp_endpoint>
+gbp_endpoint::find(const key_t& k)
+{
+ return (m_db.find(k));
+}
+
+std::shared_ptr<gbp_endpoint>
+gbp_endpoint::singular() const
+{
+ return find_or_add(*this);
+}
+
+void
+gbp_endpoint::dump(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+
+gbp_endpoint::event_handler::event_handler()
+{
+ OM::register_listener(this);
+ inspect::register_handler({ "gbp-endpoint" }, "GBP Endpoints", this);
+}
+
+void
+gbp_endpoint::event_handler::handle_replay()
+{
+ m_db.replay();
+}
+
+void
+gbp_endpoint::event_handler::handle_populate(const client_db::key_t& key)
+{
+ std::shared_ptr<gbp_endpoint_cmds::dump_cmd> cmd =
+ std::make_shared<gbp_endpoint_cmds::dump_cmd>();
+
+ HW::enqueue(cmd);
+ HW::write();
+
+ for (auto& record : *cmd) {
+ auto& payload = record.get_payload();
+
+ boost::asio::ip::address address =
+ from_bytes(payload.endpoint.is_ip6, payload.endpoint.address);
+ std::shared_ptr<interface> itf =
+ interface::find(payload.endpoint.sw_if_index);
+ std::shared_ptr<gbp_endpoint_group> epg =
+ gbp_endpoint_group::find(payload.endpoint.epg_id);
+ mac_address_t mac(payload.endpoint.mac);
+
+ VOM_LOG(log_level_t::DEBUG) << "data: " << payload.endpoint.sw_if_index;
+
+ if (itf && epg) {
+ gbp_endpoint gbpe(*itf, address, mac, *epg);
+ OM::commit(key, gbpe);
+
+ VOM_LOG(log_level_t::DEBUG) << "read: " << gbpe.to_string();
+ }
+ }
+}
+
+dependency_t
+gbp_endpoint::event_handler::order() const
+{
+ return (dependency_t::ENTRY);
+}
+
+void
+gbp_endpoint::event_handler::show(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+} // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/gbp_endpoint.hpp b/extras/vom/vom/gbp_endpoint.hpp
new file mode 100644
index 00000000000..f6466a6077d
--- /dev/null
+++ b/extras/vom/vom/gbp_endpoint.hpp
@@ -0,0 +1,193 @@
+/*
+ * 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_GBP_ENDPOINT_H__
+#define __VOM_GBP_ENDPOINT_H__
+
+#include <ostream>
+
+#include "vom/gbp_endpoint_group.hpp"
+#include "vom/interface.hpp"
+#include "vom/singular_db.hpp"
+
+namespace VOM {
+/**
+ * A GBP Enpoint (i.e. a VM)
+ */
+class gbp_endpoint : public object_base
+{
+public:
+ /**
+ * The key for a GBP endpoint; interface and IP
+ */
+ typedef std::pair<interface::key_t, boost::asio::ip::address> key_t;
+
+ /**
+ * Construct a GBP endpoint
+ */
+ gbp_endpoint(const interface& itf,
+ const boost::asio::ip::address& ip_addr,
+ const mac_address_t& mac,
+ const gbp_endpoint_group& epg);
+
+ /**
+ * Copy Construct
+ */
+ gbp_endpoint(const gbp_endpoint& r);
+
+ /**
+ * Destructor
+ */
+ ~gbp_endpoint();
+
+ /**
+ * Return the object's key
+ */
+ const key_t key() const;
+
+ /**
+ * comparison operator
+ */
+ bool operator==(const gbp_endpoint& bdae) const;
+
+ /**
+ * Return the matching 'singular instance'
+ */
+ std::shared_ptr<gbp_endpoint> singular() const;
+
+ /**
+ * Find the instnace of the bridge_domain domain in the OM
+ */
+ static std::shared_ptr<gbp_endpoint> find(const key_t& k);
+
+ /**
+ * 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;
+
+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 gbp_endpoint& obj);
+
+ /**
+ * Find or add the instnace of the bridge_domain domain in the OM
+ */
+ static std::shared_ptr<gbp_endpoint> find_or_add(const gbp_endpoint& 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, gbp_endpoint>;
+
+ /**
+ * Sweep/reap the object if still stale
+ */
+ void sweep(void);
+
+ /**
+ * HW configuration for the result of creating the endpoint
+ */
+ HW::item<bool> m_hw;
+
+ /**
+ * The interface the endpoint is attached to.
+ */
+ std::shared_ptr<interface> m_itf;
+
+ /**
+ * The IP address of the endpoint
+ */
+ boost::asio::ip::address m_ip;
+
+ /**
+ * The MAC address of the endpoint
+ */
+ mac_address_t m_mac;
+
+ /**
+ * The EPG the endpoint is in
+ */
+ std::shared_ptr<gbp_endpoint_group> m_epg;
+
+ /**
+ * A map of all bridge_domains
+ */
+ static singular_db<key_t, gbp_endpoint> m_db;
+};
+
+std::ostream& operator<<(std::ostream& os, const gbp_endpoint::key_t& key);
+}; // namespace
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/gbp_endpoint_cmds.cpp b/extras/vom/vom/gbp_endpoint_cmds.cpp
new file mode 100644
index 00000000000..88d2f377bc7
--- /dev/null
+++ b/extras/vom/vom/gbp_endpoint_cmds.cpp
@@ -0,0 +1,152 @@
+/*
+ * 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/gbp_endpoint_cmds.hpp"
+
+DEFINE_VAPI_MSG_IDS_GBP_API_JSON;
+
+namespace VOM {
+namespace gbp_endpoint_cmds {
+
+create_cmd::create_cmd(HW::item<bool>& item,
+ const handle_t& itf,
+ const boost::asio::ip::address& ip_addr,
+ const mac_address_t& mac,
+ epg_id_t epg_id)
+ : rpc_cmd(item)
+ , m_itf(itf)
+ , m_ip_addr(ip_addr)
+ , m_mac(mac)
+ , m_epg_id(epg_id)
+{
+}
+
+bool
+create_cmd::operator==(const create_cmd& other) const
+{
+ return ((m_itf == other.m_itf) && (m_ip_addr == other.m_ip_addr) &&
+ (m_mac == other.m_mac) && (m_epg_id == other.m_epg_id));
+}
+
+rc_t
+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.endpoint.sw_if_index = m_itf.value();
+ payload.endpoint.epg_id = m_epg_id;
+ to_bytes(m_ip_addr, &payload.endpoint.is_ip6, payload.endpoint.address);
+ m_mac.to_bytes(payload.endpoint.mac, 6);
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return rc_t::OK;
+}
+
+std::string
+create_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "gbp-endpoint-create: " << m_hw_item.to_string() << " itf:" << m_itf
+ << " ip:" << m_ip_addr.to_string() << " epg-id:" << m_epg_id;
+
+ return (s.str());
+}
+
+delete_cmd::delete_cmd(HW::item<bool>& item,
+ const handle_t& itf,
+ const boost::asio::ip::address& ip_addr)
+ : rpc_cmd(item)
+ , m_itf(itf)
+ , m_ip_addr(ip_addr)
+{
+}
+
+bool
+delete_cmd::operator==(const delete_cmd& other) const
+{
+ return ((m_itf == other.m_itf) && (m_ip_addr == other.m_ip_addr));
+}
+
+rc_t
+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.endpoint.sw_if_index = m_itf.value();
+ payload.endpoint.epg_id = ~0;
+ to_bytes(m_ip_addr, &payload.endpoint.is_ip6, payload.endpoint.address);
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return rc_t::OK;
+}
+
+std::string
+delete_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "gbp-endpoint-delete: " << m_hw_item.to_string() << " itf:" << m_itf
+ << " ip:" << m_ip_addr.to_string();
+
+ return (s.str());
+}
+
+dump_cmd::dump_cmd()
+{
+}
+
+bool
+dump_cmd::operator==(const dump_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+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::OK;
+}
+
+std::string
+dump_cmd::to_string() const
+{
+ return ("gbp-endpoint-dump");
+}
+
+}; // namespace gbp_endpoint_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/gbp_endpoint_cmds.hpp b/extras/vom/vom/gbp_endpoint_cmds.hpp
new file mode 100644
index 00000000000..2893ef51eec
--- /dev/null
+++ b/extras/vom/vom/gbp_endpoint_cmds.hpp
@@ -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.
+ */
+
+#ifndef __VOM_GBP_ENDPOINT_CMDS_H__
+#define __VOM_GBP_ENDPOINT_CMDS_H__
+
+#include "vom/dump_cmd.hpp"
+#include "vom/gbp_endpoint.hpp"
+
+#include <vapi/gbp.api.vapi.hpp>
+
+namespace VOM {
+namespace gbp_endpoint_cmds {
+
+/**
+* A command class that creates or updates the GBP endpoint
+*/
+class create_cmd
+ : public rpc_cmd<HW::item<bool>, rc_t, vapi::Gbp_endpoint_add_del>
+{
+public:
+ /**
+ * Constructor
+ */
+ create_cmd(HW::item<bool>& item,
+ const handle_t& itf,
+ const boost::asio::ip::address& ip_addr,
+ const mac_address_t& mac,
+ epg_id_t epg_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:
+ const handle_t m_itf;
+ const boost::asio::ip::address m_ip_addr;
+ const mac_address_t m_mac;
+ const epg_id_t m_epg_id;
+};
+
+/**
+ * A cmd class that deletes a GBP endpoint
+ */
+class delete_cmd
+ : public rpc_cmd<HW::item<bool>, rc_t, vapi::Gbp_endpoint_add_del>
+{
+public:
+ /**
+ * Constructor
+ */
+ delete_cmd(HW::item<bool>& item,
+ const handle_t& itf,
+ 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:
+ const handle_t m_itf;
+ const boost::asio::ip::address m_ip_addr;
+};
+
+/**
+ * A cmd class that Dumps all the GBP endpoints
+ */
+class dump_cmd : public VOM::dump_cmd<vapi::Gbp_endpoint_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;
+};
+}; // namespace gbp_enpoint_cms
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/gbp_endpoint_group.cpp b/extras/vom/vom/gbp_endpoint_group.cpp
new file mode 100644
index 00000000000..d9f0d38d594
--- /dev/null
+++ b/extras/vom/vom/gbp_endpoint_group.cpp
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/gbp_endpoint_group.hpp"
+#include "vom/gbp_endpoint_group_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+
+singular_db<gbp_endpoint_group::key_t, gbp_endpoint_group>
+ gbp_endpoint_group::m_db;
+
+gbp_endpoint_group::event_handler gbp_endpoint_group::m_evh;
+
+gbp_endpoint_group::gbp_endpoint_group(epg_id_t epg_id,
+ const interface& itf,
+ const route_domain& rd,
+ const bridge_domain& bd)
+ : m_hw(false)
+ , m_epg_id(epg_id)
+ , m_itf(itf.singular())
+ , m_rd(rd.singular())
+ , m_bd(bd.singular())
+{
+}
+
+gbp_endpoint_group::gbp_endpoint_group(const gbp_endpoint_group& epg)
+ : m_hw(epg.m_hw)
+ , m_epg_id(epg.m_epg_id)
+ , m_itf(epg.m_itf)
+ , m_rd(epg.m_rd)
+ , m_bd(epg.m_bd)
+{
+}
+
+gbp_endpoint_group::~gbp_endpoint_group()
+{
+ sweep();
+ m_db.release(key(), this);
+}
+
+const gbp_endpoint_group::key_t
+gbp_endpoint_group::key() const
+{
+ return (m_epg_id);
+}
+
+epg_id_t
+gbp_endpoint_group::id() const
+{
+ return (m_epg_id);
+}
+
+bool
+gbp_endpoint_group::operator==(const gbp_endpoint_group& gbpe) const
+{
+ return (key() == gbpe.key() && (m_itf == gbpe.m_itf) && (m_rd == gbpe.m_rd) &&
+ (m_bd == gbpe.m_bd));
+}
+
+void
+gbp_endpoint_group::sweep()
+{
+ if (m_hw) {
+ HW::enqueue(new gbp_endpoint_group_cmds::delete_cmd(m_hw, m_epg_id));
+ }
+ HW::write();
+}
+
+void
+gbp_endpoint_group::replay()
+{
+ if (m_hw) {
+ HW::enqueue(new gbp_endpoint_group_cmds::create_cmd(
+ m_hw, m_epg_id, m_bd->id(), m_rd->table_id(), m_itf->handle()));
+ }
+}
+
+std::string
+gbp_endpoint_group::to_string() const
+{
+ std::ostringstream s;
+ s << "gbp-endpoint-group:["
+ << "epg:" << m_epg_id << ", " << m_itf->to_string() << ", "
+ << m_bd->to_string() << ", " << m_rd->to_string() << "]";
+
+ return (s.str());
+}
+
+void
+gbp_endpoint_group::update(const gbp_endpoint_group& r)
+{
+ if (rc_t::OK != m_hw.rc()) {
+ HW::enqueue(new gbp_endpoint_group_cmds::create_cmd(
+ m_hw, m_epg_id, m_bd->id(), m_rd->table_id(), m_itf->handle()));
+ }
+}
+
+std::shared_ptr<gbp_endpoint_group>
+gbp_endpoint_group::find_or_add(const gbp_endpoint_group& temp)
+{
+ return (m_db.find_or_add(temp.key(), temp));
+}
+
+std::shared_ptr<gbp_endpoint_group>
+gbp_endpoint_group::find(const key_t& k)
+{
+ return (m_db.find(k));
+}
+
+std::shared_ptr<gbp_endpoint_group>
+gbp_endpoint_group::singular() const
+{
+ return find_or_add(*this);
+}
+
+void
+gbp_endpoint_group::dump(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+
+gbp_endpoint_group::event_handler::event_handler()
+{
+ OM::register_listener(this);
+ inspect::register_handler({ "gbp-endpoint-group" }, "GBP Endpoint_Groups",
+ this);
+}
+
+void
+gbp_endpoint_group::event_handler::handle_replay()
+{
+ m_db.replay();
+}
+
+void
+gbp_endpoint_group::event_handler::handle_populate(const client_db::key_t& key)
+{
+ std::shared_ptr<gbp_endpoint_group_cmds::dump_cmd> cmd =
+ std::make_shared<gbp_endpoint_group_cmds::dump_cmd>();
+
+ HW::enqueue(cmd);
+ HW::write();
+
+ for (auto& record : *cmd) {
+ auto& payload = record.get_payload();
+
+ std::shared_ptr<interface> itf =
+ interface::find(payload.epg.uplink_sw_if_index);
+ std::shared_ptr<route_domain> rd =
+ route_domain::find(payload.epg.ip4_table_id);
+ std::shared_ptr<bridge_domain> bd = bridge_domain::find(payload.epg.bd_id);
+
+ VOM_LOG(log_level_t::DEBUG) << "data: [" << payload.epg.uplink_sw_if_index
+ << ", " << payload.epg.ip4_table_id << ", "
+ << payload.epg.bd_id << "]";
+
+ if (itf && bd && rd) {
+ gbp_endpoint_group gbpe(payload.epg.epg_id, *itf, *rd, *bd);
+ OM::commit(key, gbpe);
+
+ VOM_LOG(log_level_t::DEBUG) << "read: " << gbpe.to_string();
+ }
+ }
+}
+
+dependency_t
+gbp_endpoint_group::event_handler::order() const
+{
+ return (dependency_t::ACL);
+}
+
+void
+gbp_endpoint_group::event_handler::show(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+} // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/gbp_endpoint_group.hpp b/extras/vom/vom/gbp_endpoint_group.hpp
new file mode 100644
index 00000000000..f7c900f20be
--- /dev/null
+++ b/extras/vom/vom/gbp_endpoint_group.hpp
@@ -0,0 +1,205 @@
+/*
+ * 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_GBP_ENDPOINT_GROUP_H__
+#define __VOM_GBP_ENDPOINT_GROUP_H__
+
+#include "vom/interface.hpp"
+#include "vom/singular_db.hpp"
+#include "vom/types.hpp"
+
+#include "vom/bridge_domain.hpp"
+#include "vom/route_domain.hpp"
+
+namespace VOM {
+
+/**
+ * EPG IDs are 32 bit integers
+ */
+typedef uint32_t epg_id_t;
+
+/**
+ * A entry in the ARP termination table of a Bridge Domain
+ */
+class gbp_endpoint_group : public object_base
+{
+public:
+ /**
+ * The key for a GBP endpoint group is its ID
+ */
+ typedef epg_id_t key_t;
+
+ /**
+ * Construct a GBP endpoint_group
+ */
+ gbp_endpoint_group(epg_id_t epg_id,
+ const interface& itf,
+ const route_domain& rd,
+ const bridge_domain& bd);
+
+ /**
+ * Copy Construct
+ */
+ gbp_endpoint_group(const gbp_endpoint_group& r);
+
+ /**
+ * Destructor
+ */
+ ~gbp_endpoint_group();
+
+ /**
+ * Return the object's key
+ */
+ const key_t key() const;
+
+ /**
+ * comparison operator
+ */
+ bool operator==(const gbp_endpoint_group& bdae) const;
+
+ /**
+ * Return the matching 'singular instance'
+ */
+ std::shared_ptr<gbp_endpoint_group> singular() const;
+
+ /**
+ * Find the instnace of the bridge_domain domain in the OM
+ */
+ static std::shared_ptr<gbp_endpoint_group> find(const key_t& k);
+
+ /**
+ * 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;
+
+ /**
+ * Get the ID of the EPG
+ */
+ epg_id_t id() 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_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 gbp_endpoint_group& obj);
+
+ /**
+ * Find or add the instnace of the bridge_domain domain in the OM
+ */
+ static std::shared_ptr<gbp_endpoint_group> find_or_add(
+ const gbp_endpoint_group& 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, gbp_endpoint_group>;
+
+ /**
+ * Sweep/reap the object if still stale
+ */
+ void sweep(void);
+
+ /**
+ * HW configuration for the result of creating the endpoint_group
+ */
+ HW::item<bool> m_hw;
+
+ /**
+ * The EPG ID
+ */
+ epg_id_t m_epg_id;
+
+ /**
+ * The uplink interface for the endpoint group
+ */
+ std::shared_ptr<interface> m_itf;
+
+ /**
+ * The route-domain the EPG uses
+ */
+ std::shared_ptr<route_domain> m_rd;
+
+ /**
+ * The bridge-domain the EPG uses
+ */
+ std::shared_ptr<bridge_domain> m_bd;
+
+ /**
+ * A map of all bridge_domains
+ */
+ static singular_db<key_t, gbp_endpoint_group> m_db;
+};
+
+}; // namespace
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/gbp_endpoint_group_cmds.cpp b/extras/vom/vom/gbp_endpoint_group_cmds.cpp
new file mode 100644
index 00000000000..55e81d3a528
--- /dev/null
+++ b/extras/vom/vom/gbp_endpoint_group_cmds.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/gbp_endpoint_group_cmds.hpp"
+
+namespace VOM {
+namespace gbp_endpoint_group_cmds {
+
+create_cmd::create_cmd(HW::item<bool>& item,
+ epg_id_t epg_id,
+ uint32_t bd_id,
+ route::table_id_t rd_id,
+ const handle_t& itf)
+ : rpc_cmd(item)
+ , m_epg_id(epg_id)
+ , m_bd_id(bd_id)
+ , m_rd_id(rd_id)
+ , m_itf(itf)
+{
+}
+
+bool
+create_cmd::operator==(const create_cmd& other) const
+{
+ return ((m_itf == other.m_itf) && (m_bd_id == other.m_bd_id) &&
+ (m_rd_id == other.m_rd_id) && (m_epg_id == other.m_epg_id));
+}
+
+rc_t
+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.epg.uplink_sw_if_index = m_itf.value();
+ payload.epg.epg_id = m_epg_id;
+ payload.epg.bd_id = m_bd_id;
+ payload.epg.ip4_table_id = m_rd_id;
+ payload.epg.ip6_table_id = m_rd_id;
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return rc_t::OK;
+}
+
+std::string
+create_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "gbp-endpoint-group-create: " << m_hw_item.to_string()
+ << " epg-id:" << m_epg_id << " bd-id:" << m_bd_id << " rd-id:" << m_rd_id
+ << " itf:" << m_itf;
+
+ return (s.str());
+}
+
+delete_cmd::delete_cmd(HW::item<bool>& item, epg_id_t epg_id)
+ : rpc_cmd(item)
+ , m_epg_id(epg_id)
+{
+}
+
+bool
+delete_cmd::operator==(const delete_cmd& other) const
+{
+ return (m_epg_id == other.m_epg_id);
+}
+
+rc_t
+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.epg.epg_id = m_epg_id;
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return rc_t::OK;
+}
+
+std::string
+delete_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "gbp-endpoint-group-delete: " << m_hw_item.to_string()
+ << " epg:" << m_epg_id;
+
+ return (s.str());
+}
+
+dump_cmd::dump_cmd()
+{
+}
+
+bool
+dump_cmd::operator==(const dump_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+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::OK;
+}
+
+std::string
+dump_cmd::to_string() const
+{
+ return ("gbp-endpoint-group-dump");
+}
+
+}; // namespace gbp_endpoint_group_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/gbp_endpoint_group_cmds.hpp b/extras/vom/vom/gbp_endpoint_group_cmds.hpp
new file mode 100644
index 00000000000..4da3a4247b4
--- /dev/null
+++ b/extras/vom/vom/gbp_endpoint_group_cmds.hpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_GBP_ENDPOINT_GROUP_CMDS_H__
+#define __VOM_GBP_ENDPOINT_GROUP_CMDS_H__
+
+#include "vom/dump_cmd.hpp"
+#include "vom/gbp_endpoint_group.hpp"
+
+#include <vapi/gbp.api.vapi.hpp>
+
+namespace VOM {
+namespace gbp_endpoint_group_cmds {
+
+/**
+* A command class that creates or updates the GBP endpoint_group
+*/
+class create_cmd
+ : public rpc_cmd<HW::item<bool>, rc_t, vapi::Gbp_endpoint_group_add_del>
+{
+public:
+ /**
+ * Constructor
+ */
+ create_cmd(HW::item<bool>& item,
+ epg_id_t epg_id,
+ uint32_t bd_id,
+ route::table_id_t rd_id,
+ 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 create_cmd& i) const;
+
+private:
+ const epg_id_t m_epg_id;
+ const uint32_t m_bd_id;
+ const route::table_id_t m_rd_id;
+ const handle_t m_itf;
+};
+
+/**
+ * A cmd class that deletes a GBP endpoint_group
+ */
+class delete_cmd
+ : public rpc_cmd<HW::item<bool>, rc_t, vapi::Gbp_endpoint_group_add_del>
+{
+public:
+ /**
+ * Constructor
+ */
+ delete_cmd(HW::item<bool>& item, epg_id_t epg_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:
+ const epg_id_t m_epg_id;
+};
+
+/**
+ * A cmd class that Dumps all the GBP endpoint_groups
+ */
+class dump_cmd : public VOM::dump_cmd<vapi::Gbp_endpoint_group_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;
+};
+}; // namespace gbp_enpoint_cms
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/gbp_recirc.cpp b/extras/vom/vom/gbp_recirc.cpp
new file mode 100644
index 00000000000..250e3048f8a
--- /dev/null
+++ b/extras/vom/vom/gbp_recirc.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/gbp_recirc.hpp"
+#include "vom/gbp_recirc_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+
+gbp_recirc::type_t::type_t(int v, const std::string s)
+ : enum_base<gbp_recirc::type_t>(v, s)
+{
+}
+
+const gbp_recirc::type_t gbp_recirc::type_t::INTERNAL(0, "internal");
+const gbp_recirc::type_t gbp_recirc::type_t::EXTERNAL(1, "external");
+
+singular_db<gbp_recirc::key_t, gbp_recirc> gbp_recirc::m_db;
+
+gbp_recirc::event_handler gbp_recirc::m_evh;
+
+gbp_recirc::gbp_recirc(const interface& itf,
+ const type_t& type,
+ const gbp_endpoint_group& epg)
+ : m_hw(false)
+ , m_itf(itf.singular())
+ , m_type(type)
+ , m_epg(epg.singular())
+{
+}
+
+gbp_recirc::gbp_recirc(const gbp_recirc& gbpe)
+ : m_hw(gbpe.m_hw)
+ , m_itf(gbpe.m_itf)
+ , m_type(gbpe.m_type)
+ , m_epg(gbpe.m_epg)
+{
+}
+
+gbp_recirc::~gbp_recirc()
+{
+ sweep();
+ m_db.release(key(), this);
+}
+
+const gbp_recirc::key_t
+gbp_recirc::key() const
+{
+ return (m_itf->key());
+}
+
+const handle_t&
+gbp_recirc::handle() const
+{
+ return m_itf->handle();
+}
+
+bool
+gbp_recirc::operator==(const gbp_recirc& gbpe) const
+{
+ return ((key() == gbpe.key()) && (m_type == gbpe.m_type) &&
+ (m_itf == gbpe.m_itf) && (m_epg == gbpe.m_epg));
+}
+
+void
+gbp_recirc::sweep()
+{
+ if (m_hw) {
+ HW::enqueue(new gbp_recirc_cmds::delete_cmd(m_hw, m_itf->handle()));
+ }
+ HW::write();
+}
+
+void
+gbp_recirc::replay()
+{
+ if (m_hw) {
+ HW::enqueue(new gbp_recirc_cmds::create_cmd(
+ m_hw, m_itf->handle(), (m_type == type_t::EXTERNAL), m_epg->id()));
+ }
+}
+
+std::string
+gbp_recirc::to_string() const
+{
+ std::ostringstream s;
+ s << "gbp-recirc:[" << m_itf->to_string() << ", type:" << m_type.to_string()
+ << ", " << m_epg->to_string() << "]";
+
+ return (s.str());
+}
+
+void
+gbp_recirc::update(const gbp_recirc& r)
+{
+ if (rc_t::OK != m_hw.rc()) {
+ HW::enqueue(new gbp_recirc_cmds::create_cmd(
+ m_hw, m_itf->handle(), (m_type == type_t::EXTERNAL), m_epg->id()));
+ }
+}
+
+std::shared_ptr<gbp_recirc>
+gbp_recirc::find_or_add(const gbp_recirc& temp)
+{
+ return (m_db.find_or_add(temp.key(), temp));
+}
+
+std::shared_ptr<gbp_recirc>
+gbp_recirc::find(const key_t& k)
+{
+ return (m_db.find(k));
+}
+
+std::shared_ptr<gbp_recirc>
+gbp_recirc::singular() const
+{
+ return find_or_add(*this);
+}
+
+void
+gbp_recirc::dump(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+
+gbp_recirc::event_handler::event_handler()
+{
+ OM::register_listener(this);
+ inspect::register_handler({ "gbp-recirc" }, "GBP Recircs", this);
+}
+
+void
+gbp_recirc::event_handler::handle_replay()
+{
+ m_db.replay();
+}
+
+void
+gbp_recirc::event_handler::handle_populate(const client_db::key_t& key)
+{
+ std::shared_ptr<gbp_recirc_cmds::dump_cmd> cmd =
+ std::make_shared<gbp_recirc_cmds::dump_cmd>();
+
+ HW::enqueue(cmd);
+ HW::write();
+
+ for (auto& record : *cmd) {
+ auto& payload = record.get_payload();
+
+ std::shared_ptr<interface> itf =
+ interface::find(payload.recirc.sw_if_index);
+ std::shared_ptr<gbp_endpoint_group> epg =
+ gbp_endpoint_group::find(payload.recirc.epg_id);
+
+ VOM_LOG(log_level_t::DEBUG) << "data: [" << payload.recirc.sw_if_index
+ << ", " << payload.recirc.epg_id << "]";
+
+ if (itf && epg) {
+ gbp_recirc recirc(
+ *itf, (payload.recirc.is_ext ? type_t::EXTERNAL : type_t::INTERNAL),
+ *epg);
+ OM::commit(key, recirc);
+
+ VOM_LOG(log_level_t::DEBUG) << "read: " << recirc.to_string();
+ }
+ }
+}
+
+dependency_t
+gbp_recirc::event_handler::order() const
+{
+ return (dependency_t::BINDING);
+}
+
+void
+gbp_recirc::event_handler::show(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+} // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/gbp_recirc.hpp b/extras/vom/vom/gbp_recirc.hpp
new file mode 100644
index 00000000000..fee4f6c2502
--- /dev/null
+++ b/extras/vom/vom/gbp_recirc.hpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_GBP_RECIRC_H__
+#define __VOM_GBP_RECIRC_H__
+
+#include "vom/gbp_endpoint_group.hpp"
+#include "vom/interface.hpp"
+#include "vom/singular_db.hpp"
+
+namespace VOM {
+/**
+ * A recirculation interface for GBP use pre/post NAT
+ */
+class gbp_recirc : public object_base
+{
+public:
+ /**
+ * The key for a GBP recirc interface
+ */
+ typedef interface::key_t key_t;
+
+ struct type_t : public enum_base<type_t>
+ {
+ /**
+ * Internal recirclation interfaces accept per-NAT translation
+ * traffic from the external/NAT EPG and inject into the
+ * private/NAT-inside EPG
+ */
+ const static type_t INTERNAL;
+
+ /**
+ * External recirculation interfaces accept post-NAT translation
+ * traffic from the internal EPG and inject into the
+ * NAT EPG
+ */
+ const static type_t EXTERNAL;
+
+ private:
+ type_t(int v, const std::string s);
+ };
+
+ /**
+ * Construct a GBP recirc
+ */
+ gbp_recirc(const interface& itf,
+ const type_t& type,
+ const gbp_endpoint_group& epg);
+
+ /**
+ * Copy Construct
+ */
+ gbp_recirc(const gbp_recirc& r);
+
+ /**
+ * Destructor
+ */
+ ~gbp_recirc();
+
+ /**
+ * Return the object's key
+ */
+ const key_t key() const;
+
+ /**
+ * comparison operator
+ */
+ bool operator==(const gbp_recirc& bdae) const;
+
+ /**
+ * Return the matching 'singular instance'
+ */
+ std::shared_ptr<gbp_recirc> singular() const;
+
+ /**
+ * Find the instnace of the recirc interface in the OM
+ */
+ static std::shared_ptr<gbp_recirc> find(const key_t& k);
+
+ /**
+ * 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;
+
+ /**
+ * return the recirculation interface's handle
+ */
+ const handle_t& handle() 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_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 gbp_recirc& obj);
+
+ /**
+ * Find or add the instnace of the bridge_domain domain in the OM
+ */
+ static std::shared_ptr<gbp_recirc> find_or_add(const gbp_recirc& 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, gbp_recirc>;
+
+ /**
+ * Sweep/reap the object if still stale
+ */
+ void sweep(void);
+
+ /**
+ * HW configuration for the result of creating the recirc
+ */
+ HW::item<bool> m_hw;
+
+ /**
+ * The interface the recirc is attached to.
+ */
+ std::shared_ptr<interface> m_itf;
+
+ /**
+ * Is the reicrc for the external (i.e. post-NAT) or internal
+ */
+ type_t m_type;
+
+ /**
+ * The EPG the recirc is in
+ */
+ std::shared_ptr<gbp_endpoint_group> m_epg;
+
+ /**
+ * A map of all bridge_domains
+ */
+ static singular_db<key_t, gbp_recirc> m_db;
+};
+
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/gbp_recirc_cmds.cpp b/extras/vom/vom/gbp_recirc_cmds.cpp
new file mode 100644
index 00000000000..757fcb99065
--- /dev/null
+++ b/extras/vom/vom/gbp_recirc_cmds.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/gbp_recirc_cmds.hpp"
+
+namespace VOM {
+namespace gbp_recirc_cmds {
+
+create_cmd::create_cmd(HW::item<bool>& item,
+ const handle_t& itf,
+ bool is_ext,
+ epg_id_t epg_id)
+ : rpc_cmd(item)
+ , m_itf(itf)
+ , m_is_ext(is_ext)
+ , m_epg_id(epg_id)
+{
+}
+
+bool
+create_cmd::operator==(const create_cmd& other) const
+{
+ return ((m_itf == other.m_itf) && (m_is_ext == other.m_is_ext) &&
+ (m_epg_id == other.m_epg_id));
+}
+
+rc_t
+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.recirc.sw_if_index = m_itf.value();
+ payload.recirc.epg_id = m_epg_id;
+ payload.recirc.is_ext = m_is_ext;
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return rc_t::OK;
+}
+
+std::string
+create_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "gbp-recirc-create: " << m_hw_item.to_string() << " itf:" << m_itf
+ << " ext:" << m_is_ext << " epg-id:" << m_epg_id;
+
+ return (s.str());
+}
+
+delete_cmd::delete_cmd(HW::item<bool>& item, const handle_t& itf)
+ : rpc_cmd(item)
+ , m_itf(itf)
+{
+}
+
+bool
+delete_cmd::operator==(const delete_cmd& other) const
+{
+ return (m_itf == other.m_itf);
+}
+
+rc_t
+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.recirc.sw_if_index = m_itf.value();
+ payload.recirc.epg_id = ~0;
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return rc_t::OK;
+}
+
+std::string
+delete_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "gbp-recirc-delete: " << m_hw_item.to_string() << " itf:" << m_itf;
+
+ return (s.str());
+}
+
+dump_cmd::dump_cmd()
+{
+}
+
+bool
+dump_cmd::operator==(const dump_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+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::OK;
+}
+
+std::string
+dump_cmd::to_string() const
+{
+ return ("gbp-recirc-dump");
+}
+
+}; // namespace gbp_recirc_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/gbp_recirc_cmds.hpp b/extras/vom/vom/gbp_recirc_cmds.hpp
new file mode 100644
index 00000000000..fe17834ebf5
--- /dev/null
+++ b/extras/vom/vom/gbp_recirc_cmds.hpp
@@ -0,0 +1,137 @@
+/*
+ * 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_GBP_RECIRC_CMDS_H__
+#define __VOM_GBP_RECIRC_CMDS_H__
+
+#include "vom/dump_cmd.hpp"
+#include "vom/gbp_recirc.hpp"
+
+#include <vapi/gbp.api.vapi.hpp>
+
+namespace VOM {
+namespace gbp_recirc_cmds {
+
+/**
+* A command class that creates or updates the GBP recirc
+*/
+class create_cmd
+ : public rpc_cmd<HW::item<bool>, rc_t, vapi::Gbp_recirc_add_del>
+{
+public:
+ /**
+ * Constructor
+ */
+ create_cmd(HW::item<bool>& item,
+ const handle_t& itf,
+ bool is_ext,
+ epg_id_t epg_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:
+ const handle_t m_itf;
+ bool m_is_ext;
+ const epg_id_t m_epg_id;
+};
+
+/**
+ * A cmd class that deletes a GBP recirc
+ */
+class delete_cmd
+ : public rpc_cmd<HW::item<bool>, rc_t, vapi::Gbp_recirc_add_del>
+{
+public:
+ /**
+ * Constructor
+ */
+ delete_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 delete_cmd& i) const;
+
+private:
+ const handle_t m_itf;
+};
+
+/**
+ * A cmd class that Dumps all the GBP recircs
+ */
+class dump_cmd : public VOM::dump_cmd<vapi::Gbp_recirc_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;
+};
+}; // namespace gbp_enpoint_cms
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/gbp_subnet.cpp b/extras/vom/vom/gbp_subnet.cpp
new file mode 100644
index 00000000000..1a9ee86e06b
--- /dev/null
+++ b/extras/vom/vom/gbp_subnet.cpp
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/gbp_subnet.hpp"
+#include "vom/gbp_subnet_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+
+gbp_subnet::type_t::type_t(int v, const std::string s)
+ : enum_base<gbp_subnet::type_t>(v, s)
+{
+}
+
+const gbp_subnet::type_t gbp_subnet::type_t::INTERNAL(0, "internal");
+const gbp_subnet::type_t gbp_subnet::type_t::EXTERNAL(1, "external");
+
+singular_db<gbp_subnet::key_t, gbp_subnet> gbp_subnet::m_db;
+
+gbp_subnet::event_handler gbp_subnet::m_evh;
+
+gbp_subnet::gbp_subnet(const route_domain& rd, const route::prefix_t& prefix)
+ : m_hw(false)
+ , m_rd(rd.singular())
+ , m_prefix(prefix)
+ , m_type(type_t::INTERNAL)
+ , m_recirc(nullptr)
+ , m_epg(nullptr)
+{
+}
+
+gbp_subnet::gbp_subnet(const route_domain& rd,
+ const route::prefix_t& prefix,
+ const gbp_recirc& recirc,
+ const gbp_endpoint_group& epg)
+ : m_hw(false)
+ , m_rd(rd.singular())
+ , m_prefix(prefix)
+ , m_type(type_t::EXTERNAL)
+ , m_recirc(recirc.singular())
+ , m_epg(epg.singular())
+{
+}
+
+gbp_subnet::gbp_subnet(const gbp_subnet& o)
+ : m_hw(o.m_hw)
+ , m_rd(o.m_rd)
+ , m_prefix(o.m_prefix)
+ , m_type(o.m_type)
+ , m_recirc(o.m_recirc)
+ , m_epg(o.m_epg)
+{
+}
+
+gbp_subnet::~gbp_subnet()
+{
+ sweep();
+ m_db.release(key(), this);
+}
+
+const gbp_subnet::key_t
+gbp_subnet::key() const
+{
+ return (std::make_pair(m_rd->key(), m_prefix));
+}
+
+bool
+gbp_subnet::operator==(const gbp_subnet& gs) const
+{
+ return ((key() == gs.key()) && (m_type == gs.m_type) &&
+ (m_recirc == gs.m_recirc) && (m_epg == gs.m_epg));
+}
+
+void
+gbp_subnet::sweep()
+{
+ if (m_hw) {
+ HW::enqueue(
+ new gbp_subnet_cmds::delete_cmd(m_hw, m_rd->table_id(), m_prefix));
+ }
+ HW::write();
+}
+
+void
+gbp_subnet::replay()
+{
+ if (m_hw) {
+ HW::enqueue(new gbp_subnet_cmds::create_cmd(
+ m_hw, m_rd->table_id(), m_prefix, (m_type == type_t::INTERNAL),
+ (m_recirc ? m_recirc->handle() : handle_t::INVALID),
+ (m_epg ? m_epg->id() : ~0)));
+ }
+}
+
+std::string
+gbp_subnet::to_string() const
+{
+ std::ostringstream s;
+ s << "gbp-subnet:[" << m_type.to_string() << ", " << m_rd->to_string() << ":"
+ << m_prefix.to_string();
+ if (m_recirc)
+ s << ", " << m_recirc->to_string();
+ if (m_epg)
+ s << ", " << m_epg->to_string();
+
+ s << "]";
+
+ return (s.str());
+}
+
+void
+gbp_subnet::update(const gbp_subnet& r)
+{
+ if (rc_t::OK != m_hw.rc()) {
+ HW::enqueue(new gbp_subnet_cmds::create_cmd(
+ m_hw, m_rd->table_id(), m_prefix, (m_type == type_t::INTERNAL),
+ (m_recirc ? m_recirc->handle() : handle_t::INVALID),
+ (m_epg ? m_epg->id() : ~0)));
+ } else {
+ if (m_type != r.m_type) {
+ m_epg = r.m_epg;
+ m_recirc = r.m_recirc;
+ m_type = r.m_type;
+
+ HW::enqueue(new gbp_subnet_cmds::create_cmd(
+ m_hw, m_rd->table_id(), m_prefix, (m_type == type_t::INTERNAL),
+ (m_recirc ? m_recirc->handle() : handle_t::INVALID),
+ (m_epg ? m_epg->id() : ~0)));
+ }
+ }
+}
+
+std::shared_ptr<gbp_subnet>
+gbp_subnet::find_or_add(const gbp_subnet& temp)
+{
+ return (m_db.find_or_add(temp.key(), temp));
+}
+
+std::shared_ptr<gbp_subnet>
+gbp_subnet::find(const key_t& k)
+{
+ return (m_db.find(k));
+}
+
+std::shared_ptr<gbp_subnet>
+gbp_subnet::singular() const
+{
+ return find_or_add(*this);
+}
+
+void
+gbp_subnet::dump(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+
+gbp_subnet::event_handler::event_handler()
+{
+ OM::register_listener(this);
+ inspect::register_handler({ "gbp-subnet" }, "GBP Subnets", this);
+}
+
+void
+gbp_subnet::event_handler::handle_replay()
+{
+ m_db.replay();
+}
+
+void
+gbp_subnet::event_handler::handle_populate(const client_db::key_t& key)
+{
+ std::shared_ptr<gbp_subnet_cmds::dump_cmd> cmd =
+ std::make_shared<gbp_subnet_cmds::dump_cmd>();
+
+ HW::enqueue(cmd);
+ HW::write();
+
+ for (auto& record : *cmd) {
+ auto& payload = record.get_payload();
+
+ route::prefix_t pfx(payload.subnet.is_ip6, payload.subnet.address,
+ payload.subnet.address_length);
+ std::shared_ptr<route_domain> rd =
+ route_domain::find(payload.subnet.table_id);
+
+ if (rd) {
+ if (payload.subnet.is_internal) {
+ gbp_subnet gs(*rd, pfx);
+ OM::commit(key, gs);
+ VOM_LOG(log_level_t::DEBUG) << "read: " << gs.to_string();
+ } else {
+ std::shared_ptr<interface> itf =
+ interface::find(payload.subnet.sw_if_index);
+ std::shared_ptr<gbp_endpoint_group> epg =
+ gbp_endpoint_group::find(payload.subnet.epg_id);
+
+ if (itf && epg) {
+ std::shared_ptr<gbp_recirc> recirc = gbp_recirc::find(itf->key());
+
+ if (recirc) {
+ gbp_subnet gs(*rd, pfx, *recirc, *epg);
+ OM::commit(key, gs);
+ VOM_LOG(log_level_t::DEBUG) << "read: " << gs.to_string();
+ }
+ }
+ }
+ }
+ }
+}
+
+dependency_t
+gbp_subnet::event_handler::order() const
+{
+ return (dependency_t::ENTRY);
+}
+
+void
+gbp_subnet::event_handler::show(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+} // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/gbp_subnet.hpp b/extras/vom/vom/gbp_subnet.hpp
new file mode 100644
index 00000000000..b4adb40ae45
--- /dev/null
+++ b/extras/vom/vom/gbp_subnet.hpp
@@ -0,0 +1,218 @@
+/*
+ * 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_GBP_SUBNET_H__
+#define __VOM_GBP_SUBNET_H__
+
+#include "vom/gbp_endpoint_group.hpp"
+#include "vom/gbp_recirc.hpp"
+#include "vom/route.hpp"
+#include "vom/singular_db.hpp"
+
+namespace VOM {
+/**
+ * A GBP Enpoint (i.e. a VM)
+ */
+class gbp_subnet : public object_base
+{
+public:
+ /**
+ * The key for a GBP subnet; table and prefix
+ */
+ typedef std::pair<route_domain::key_t, route::prefix_t> key_t;
+
+ /**
+ * Construct an internal GBP subnet
+ */
+ gbp_subnet(const route_domain& rd, const route::prefix_t& prefix);
+
+ /**
+ * Construct an external GBP subnet
+ */
+ gbp_subnet(const route_domain& rd,
+ const route::prefix_t& prefix,
+ const gbp_recirc& recirc,
+ const gbp_endpoint_group& epg);
+
+ /**
+ * Copy Construct
+ */
+ gbp_subnet(const gbp_subnet& r);
+
+ /**
+ * Destructor
+ */
+ ~gbp_subnet();
+
+ /**
+ * Return the object's key
+ */
+ const key_t key() const;
+
+ /**
+ * comparison operator
+ */
+ bool operator==(const gbp_subnet& bdae) const;
+
+ /**
+ * Return the matching 'singular instance'
+ */
+ std::shared_ptr<gbp_subnet> singular() const;
+
+ /**
+ * Find the instnace of the bridge_domain domain in the OM
+ */
+ static std::shared_ptr<gbp_subnet> find(const key_t& k);
+
+ /**
+ * 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;
+
+private:
+ struct type_t : public enum_base<type_t>
+ {
+ /**
+ * Internal subnet is reachable through the source EPG's
+ * uplink interface.
+ */
+ const static type_t INTERNAL;
+
+ /**
+ * External subnet requires NAT translation before egress.
+ */
+ const static type_t EXTERNAL;
+
+ private:
+ type_t(int v, const std::string s);
+ };
+
+ /**
+ * 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 gbp_subnet& obj);
+
+ /**
+ * Find or add the instnace of the bridge_domain domain in the OM
+ */
+ static std::shared_ptr<gbp_subnet> find_or_add(const gbp_subnet& 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, gbp_subnet>;
+
+ /**
+ * Sweep/reap the object if still stale
+ */
+ void sweep(void);
+
+ /**
+ * HW configuration for the result of creating the subnet
+ */
+ HW::item<bool> m_hw;
+
+ /**
+ * the route domain the prefix is in
+ */
+ const std::shared_ptr<route_domain> m_rd;
+
+ /**
+ * prefix to match
+ */
+ const route::prefix_t m_prefix;
+
+ /*
+ * Subnet type
+ */
+ type_t m_type;
+
+ /**
+ * The interface the prefix is reachable through
+ */
+ std::shared_ptr<gbp_recirc> m_recirc;
+
+ /**
+ * The EPG the subnet is in
+ */
+ std::shared_ptr<gbp_endpoint_group> m_epg;
+
+ /**
+ * A map of all bridge_domains
+ */
+ static singular_db<key_t, gbp_subnet> m_db;
+};
+
+}; // namespace
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/gbp_subnet_cmds.cpp b/extras/vom/vom/gbp_subnet_cmds.cpp
new file mode 100644
index 00000000000..d087e5c67d8
--- /dev/null
+++ b/extras/vom/vom/gbp_subnet_cmds.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/gbp_subnet_cmds.hpp"
+
+namespace VOM {
+namespace gbp_subnet_cmds {
+
+create_cmd::create_cmd(HW::item<bool>& item,
+ route::table_id_t rd,
+ const route::prefix_t& prefix,
+ bool internal,
+ const handle_t& itf,
+ epg_id_t epg_id)
+ : rpc_cmd(item)
+ , m_rd(rd)
+ , m_prefix(prefix)
+ , m_internal(internal)
+ , m_itf(itf)
+ , m_epg_id(epg_id)
+{
+}
+
+bool
+create_cmd::operator==(const create_cmd& other) const
+{
+ return ((m_itf == other.m_itf) && (m_rd == other.m_rd) &&
+ (m_prefix == other.m_prefix) && (m_itf == other.m_itf) &&
+ (m_epg_id == other.m_epg_id));
+}
+
+rc_t
+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.subnet.is_internal = m_internal;
+ payload.subnet.table_id = m_rd;
+ payload.subnet.sw_if_index = m_itf.value();
+ payload.subnet.epg_id = m_epg_id;
+ m_prefix.to_vpp(&payload.subnet.is_ip6, payload.subnet.address,
+ &payload.subnet.address_length);
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return rc_t::OK;
+}
+
+std::string
+create_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "gbp-subnet-create: " << m_hw_item.to_string()
+ << "internal:" << m_internal << ", " << m_rd << ":" << m_prefix.to_string()
+ << " itf:" << m_itf << " epg-id:" << m_epg_id;
+
+ return (s.str());
+}
+
+delete_cmd::delete_cmd(HW::item<bool>& item,
+ route::table_id_t rd,
+ const route::prefix_t& prefix)
+ : rpc_cmd(item)
+ , m_rd(rd)
+ , m_prefix(prefix)
+{
+}
+
+bool
+delete_cmd::operator==(const delete_cmd& other) const
+{
+ return ((m_rd == other.m_rd) && (m_prefix == other.m_prefix));
+}
+
+rc_t
+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.subnet.table_id = m_rd;
+ m_prefix.to_vpp(&payload.subnet.is_ip6, payload.subnet.address,
+ &payload.subnet.address_length);
+
+ payload.subnet.is_internal = 0;
+ payload.subnet.sw_if_index = ~0;
+ payload.subnet.epg_id = ~0;
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return rc_t::OK;
+}
+
+std::string
+delete_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "gbp-subnet-delete: " << m_hw_item.to_string() << ", " << m_rd << ":"
+ << m_prefix.to_string();
+
+ return (s.str());
+}
+
+dump_cmd::dump_cmd()
+{
+}
+
+bool
+dump_cmd::operator==(const dump_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+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::OK;
+}
+
+std::string
+dump_cmd::to_string() const
+{
+ return ("gbp-subnet-dump");
+}
+
+}; // namespace gbp_subnet_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/gbp_subnet_cmds.hpp b/extras/vom/vom/gbp_subnet_cmds.hpp
new file mode 100644
index 00000000000..3dbc8db2359
--- /dev/null
+++ b/extras/vom/vom/gbp_subnet_cmds.hpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_GBP_SUBNET_CMDS_H__
+#define __VOM_GBP_SUBNET_CMDS_H__
+
+#include "vom/dump_cmd.hpp"
+#include "vom/gbp_subnet.hpp"
+
+#include <vapi/gbp.api.vapi.hpp>
+
+namespace VOM {
+namespace gbp_subnet_cmds {
+
+/**
+* A command class that creates or updates the GBP subnet
+*/
+class create_cmd
+ : public rpc_cmd<HW::item<bool>, rc_t, vapi::Gbp_subnet_add_del>
+{
+public:
+ /**
+ * Constructor
+ */
+ create_cmd(HW::item<bool>& item,
+ route::table_id_t rd,
+ const route::prefix_t& prefix,
+ bool internal,
+ const handle_t& itf,
+ epg_id_t epg_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:
+ const route::table_id_t m_rd;
+ const route::prefix_t m_prefix;
+ const bool m_internal;
+ const handle_t m_itf;
+ const epg_id_t m_epg_id;
+};
+
+/**
+ * A cmd class that deletes a GBP subnet
+ */
+class delete_cmd
+ : public rpc_cmd<HW::item<bool>, rc_t, vapi::Gbp_subnet_add_del>
+{
+public:
+ /**
+ * Constructor
+ */
+ delete_cmd(HW::item<bool>& item,
+ route::table_id_t rd,
+ const route::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:
+ const route::table_id_t m_rd;
+ const route::prefix_t m_prefix;
+};
+
+/**
+ * A cmd class that Dumps all the GBP subnets
+ */
+class dump_cmd : public VOM::dump_cmd<vapi::Gbp_subnet_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;
+};
+}; // namespace gbp_enpoint_cms
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/hw.cpp b/extras/vom/vom/hw.cpp
new file mode 100644
index 00000000000..0952b60f9d9
--- /dev/null
+++ b/extras/vom/vom/hw.cpp
@@ -0,0 +1,281 @@
+/*
+ * 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/hw_cmds.hpp"
+#include "vom/logger.hpp"
+
+namespace VOM {
+HW::cmd_q::cmd_q()
+ : m_enabled(true)
+ , m_connected(false)
+ , m_conn()
+{
+}
+
+HW::cmd_q::~cmd_q()
+{
+}
+
+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();
+ }
+}
+
+bool
+HW::cmd_q::connect()
+{
+ if (m_connected)
+ return m_connected;
+
+ if (0 == m_conn.connect()) {
+ m_connected = true;
+ m_rx_thread.reset(new std::thread(&HW::cmd_q::rx_run, this));
+ }
+ return (m_connected);
+}
+
+void
+HW::cmd_q::disconnect()
+{
+
+ if (!m_connected)
+ return;
+
+ m_connected = false;
+
+ if (m_rx_thread && m_rx_thread->joinable()) {
+ m_rx_thread->join();
+ }
+
+ m_conn.disconnect();
+}
+
+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
+ */
+ rc = c->issue(m_conn);
+
+ if (rc_t::OK == rc) {
+ /*
+ * move to the next
+ */
+ } else {
+ /*
+ * barf out without issuing the rest
+ */
+ VOM_LOG(log_level_t::ERROR) << "Failed to execute: " << c->to_string();
+ 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);
+}
+
+bool
+HW::connect()
+{
+ return m_cmdQ->connect();
+}
+
+void
+HW::disconnect()
+{
+ m_cmdQ->disconnect();
+}
+
+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 hw_cmds::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());
+}
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/hw.hpp b/extras/vom/vom/hw.hpp
new file mode 100644
index 00000000000..9ba47505619
--- /dev/null
+++ b/extras/vom/vom/hw.hpp
@@ -0,0 +1,379 @@
+/*
+ * 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 <queue>
+#include <sstream>
+#include <string>
+#include <thread>
+
+#include "vom/cmd.hpp"
+#include "vom/connection.hpp"
+#include "vom/types.hpp"
+
+namespace VOM {
+
+class cmd;
+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_data()
+ , item_rc(rc_t::UNSET)
+ {
+ }
+
+ /**
+ * Constructor
+ */
+ item(rc_t rc)
+ : item_data()
+ , item_rc(rc)
+ {
+ }
+
+ /**
+ * Constructor
+ */
+ item(const T& data, rc_t rc)
+ : item_data(data)
+ , item_rc(rc)
+ {
+ }
+
+ /**
+ * Destructor
+ */
+ ~item() = default;
+
+ /**
+ * 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);
+
+ /**
+ * Write all the commands to HW
+ */
+ virtual rc_t write();
+
+ /**
+ * Blocking Connect to VPP - call once at bootup
+ */
+ virtual bool connect();
+
+ /**
+ * Disconnect to VPP
+ */
+ virtual void disconnect();
+
+ /**
+ * 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);
+
+ /**
+ * Write/Execute all commands hitherto enqueued.
+ */
+ static rc_t write();
+
+ /**
+ * Blocking Connect to VPP
+ */
+ static bool connect();
+
+ /**
+ * Disconnect to VPP
+ */
+ static void disconnect();
+
+ /**
+ * 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;
+};
+
+/**
+ * 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/extras/vom/vom/hw_cmds.cpp b/extras/vom/vom/hw_cmds.cpp
new file mode 100644
index 00000000000..e628c354d71
--- /dev/null
+++ b/extras/vom/vom/hw_cmds.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/hw_cmds.hpp"
+
+namespace VOM {
+namespace hw_cmds {
+
+poll::poll(HW::item<bool>& item)
+ : rpc_cmd(item)
+{
+}
+
+rc_t
+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
+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/extras/vom/vom/hw_cmds.hpp b/extras/vom/vom/hw_cmds.hpp
new file mode 100644
index 00000000000..b499ccecca0
--- /dev/null
+++ b/extras/vom/vom/hw_cmds.hpp
@@ -0,0 +1,64 @@
+/*
+ * 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_CMDS_H__
+#define __VOM_HW_CMDS_H__
+
+#include <vapi/vapi.hpp>
+#include <vapi/vpe.api.vapi.hpp>
+
+#include "vom/hw.hpp"
+#include "vom/rpc_cmd.hpp"
+
+namespace VOM {
+namespace hw_cmds {
+/**
+*A command poll 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;
+};
+};
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/inspect.cpp b/extras/vom/vom/inspect.cpp
new file mode 100644
index 00000000000..605a921b5ac
--- /dev/null
+++ b/extras/vom/vom/inspect.cpp
@@ -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.
+ */
+
+#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/extras/vom/vom/inspect.hpp b/extras/vom/vom/inspect.hpp
new file mode 100644
index 00000000000..d5bca3040d2
--- /dev/null
+++ b/extras/vom/vom/inspect.hpp
@@ -0,0 +1,96 @@
+/*
+ * 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 <memory>
+#include <ostream>
+#include <string>
+#include <vector>
+
+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/extras/vom/vom/interface.cpp b/extras/vom/vom/interface.cpp
new file mode 100644
index 00000000000..6faf3491e6f
--- /dev/null
+++ b/extras/vom/vom/interface.cpp
@@ -0,0 +1,643 @@
+/*
+ * 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/bond_group_binding.hpp"
+#include "vom/bond_group_binding_cmds.hpp"
+#include "vom/bond_interface_cmds.hpp"
+#include "vom/interface_cmds.hpp"
+#include "vom/interface_factory.hpp"
+#include "vom/l3_binding_cmds.hpp"
+#include "vom/logger.hpp"
+#include "vom/prefix.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+/**
+ * A DB of all the interfaces, key on the name
+ */
+singular_db<interface::key_t, 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,
+ const std::string& tag)
+ : 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_stats_type(stats_type_t::NORMAL)
+ , m_oper(oper_state_t::DOWN)
+ , m_tag(tag)
+{
+}
+
+interface::interface(const std::string& name,
+ interface::type_t itf_type,
+ interface::admin_state_t itf_state,
+ const route_domain& rd,
+ const std::string& tag)
+ : 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_stats_type(stats_type_t::NORMAL)
+ , m_oper(oper_state_t::DOWN)
+ , m_tag(tag)
+{
+}
+
+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_stats_type(o.m_stats_type)
+ , m_oper(o.m_oper)
+ , m_tag(o.m_tag)
+{
+}
+
+bool
+interface::operator==(const interface& i) const
+{
+ return ((key() == i.key()) &&
+ (m_l2_address.data() == i.m_l2_address.data()) &&
+ (m_state == i.m_state) && (m_rd == i.m_rd) && (m_type == i.m_type) &&
+ (m_oper == i.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 (singular()->handle_i());
+}
+
+const handle_t&
+interface::handle_i() 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.begin();
+}
+
+interface::const_iterator_t
+interface::cend()
+{
+ return m_db.end();
+}
+
+void
+interface::sweep()
+{
+ if (m_table_id && (m_table_id.data() != route::DEFAULT_TABLE)) {
+ m_table_id.data() = route::DEFAULT_TABLE;
+ HW::enqueue(
+ new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV4, m_hdl));
+ HW::enqueue(
+ new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV6, m_hdl));
+ }
+
+ if (m_stats) {
+ if (stats_type_t::DETAILED == m_stats_type) {
+ HW::enqueue(new interface_cmds::collect_detail_stats_change_cmd(
+ m_stats_type, handle_i(), false));
+ }
+ HW::enqueue(new interface_cmds::stats_disable_cmd(m_hdl.data()));
+ m_stats.reset();
+ }
+
+ // 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 interface_cmds::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 interface_cmds::state_change_cmd(m_state, m_hdl));
+ }
+
+ if (m_stats) {
+ if (stats_type_t::DETAILED == m_stats_type) {
+ m_stats_type.set(rc_t::NOOP);
+ HW::enqueue(new interface_cmds::collect_detail_stats_change_cmd(
+ m_stats_type, handle_i(), true));
+ }
+ stat_listener& listener = m_stats->listener();
+ listener.status().set(rc_t::NOOP);
+ m_stats.reset(new interface_cmds::stats_enable_cmd(listener, handle_i()));
+ HW::enqueue(m_stats);
+ }
+
+ if (m_table_id && (m_table_id.data() != route::DEFAULT_TABLE)) {
+ HW::enqueue(
+ new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV4, m_hdl));
+ HW::enqueue(
+ new interface_cmds::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();
+
+ if (!m_tag.empty()) {
+ s << " tag:[" << m_tag << "]";
+ }
+
+ s << "]";
+
+ return (s.str());
+}
+
+const std::string&
+interface::name() const
+{
+ return (m_name);
+}
+
+const interface::key_t&
+interface::key() const
+{
+ return (name());
+}
+
+std::queue<cmd*>&
+interface::mk_create_cmd(std::queue<cmd*>& q)
+{
+ if ((type_t::LOOPBACK == m_type) || (type_t::BVI == m_type)) {
+ q.push(new interface_cmds::loopback_create_cmd(m_hdl, m_name));
+ q.push(new interface_cmds::set_tag(m_hdl, m_name));
+ /*
+ * set the m_tag for pretty-print
+ */
+ m_tag = m_name;
+ } else if (type_t::AFPACKET == m_type) {
+ q.push(new interface_cmds::af_packet_create_cmd(m_hdl, m_name));
+ if (!m_tag.empty())
+ q.push(new interface_cmds::set_tag(m_hdl, m_tag));
+ } else if (type_t::TAP == m_type) {
+ q.push(new interface_cmds::tap_create_cmd(m_hdl, m_name));
+ if (!m_tag.empty())
+ q.push(new interface_cmds::set_tag(m_hdl, m_tag));
+ } else if (type_t::VHOST == m_type) {
+ q.push(new interface_cmds::vhost_create_cmd(m_hdl, m_name, m_tag));
+ } else {
+ m_hdl.set(rc_t::OK);
+ }
+
+ 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 interface_cmds::loopback_delete_cmd(m_hdl));
+ } else if (type_t::AFPACKET == m_type) {
+ q.push(new interface_cmds::af_packet_delete_cmd(m_hdl, m_name));
+ } else if (type_t::TAP == m_type) {
+ q.push(new interface_cmds::tap_delete_cmd(m_hdl));
+ } else if (type_t::VHOST == m_type) {
+ q.push(new interface_cmds::vhost_delete_cmd(m_hdl, m_name));
+ }
+
+ 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));
+ /*
+ * interface create now, so we can barf early if it fails
+ */
+ HW::write();
+ }
+
+ /*
+ * If the interface is not created do other commands should be issued
+ */
+ if (rc_t::OK != m_hdl.rc())
+ return;
+
+ /*
+ * change the interface state to that which is deisred
+ */
+ if (m_state.update(desired.m_state)) {
+ HW::enqueue(new interface_cmds::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 interface_cmds::set_mac_cmd(m_l2_address, m_hdl));
+ }
+
+ /*
+ * If the interface is mapped into a route domain, set VPP's
+ * table ID
+ */
+ if (m_rd != desired.m_rd) {
+ /*
+ * changing route domains. need to remove all L3 bindings, swap the table
+ * then reapply the bindings.
+ */
+ auto it = l3_binding::cbegin();
+
+ while (it != l3_binding::cend()) {
+ if (it->second.lock()->itf().key() == key())
+ it->second.lock()->sweep();
+ ++it;
+ }
+ m_rd = desired.m_rd;
+ m_table_id.update(m_rd ? m_rd->table_id() : route::DEFAULT_TABLE);
+ HW::enqueue(
+ new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV4, m_hdl));
+ HW::enqueue(
+ new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV6, m_hdl));
+ HW::write();
+
+ it = l3_binding::cbegin();
+ while (it != l3_binding::cend()) {
+ if (it->second.lock()->itf().key() == key())
+ it->second.lock()->replay(); //(*it->second.lock());
+ ++it;
+ }
+ } else if (!m_table_id && m_rd) {
+ HW::enqueue(
+ new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV4, m_hdl));
+ HW::enqueue(
+ new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV6, m_hdl));
+ }
+}
+
+void
+interface::set(const admin_state_t& state)
+{
+ m_state = state;
+}
+
+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 handle_t& hdl)
+{
+ m_hdl = hdl;
+}
+
+void
+interface::set(const oper_state_t& state)
+{
+ m_oper = state;
+}
+
+void
+interface::set(const std::string& tag)
+{
+ m_tag = tag;
+}
+
+void
+interface::enable_stats_i(interface::stat_listener& el, const stats_type_t& st)
+{
+ if (!m_stats) {
+ if (stats_type_t::DETAILED == st) {
+ m_stats_type = st;
+ HW::enqueue(new interface_cmds::collect_detail_stats_change_cmd(
+ m_stats_type, handle_i(), true));
+ }
+ m_stats.reset(new interface_cmds::stats_enable_cmd(el, handle_i()));
+ HW::enqueue(m_stats);
+ HW::write();
+ }
+}
+
+void
+interface::enable_stats(interface::stat_listener& el, const stats_type_t& st)
+{
+ singular()->enable_stats_i(el, st);
+}
+
+std::shared_ptr<interface>
+interface::singular_i() const
+{
+ return (m_db.find_or_add(key(), *this));
+}
+
+std::shared_ptr<interface>
+interface::singular() const
+{
+ return singular_i();
+}
+
+std::shared_ptr<interface>
+interface::find(const key_t& k)
+{
+ return (m_db.find(k));
+}
+
+std::shared_ptr<interface>
+interface::find(const handle_t& handle)
+{
+ return (m_hdl_db[handle].lock());
+}
+
+void
+interface::add(const key_t& key, const HW::item<handle_t>& item)
+{
+ std::shared_ptr<interface> sp = find(key);
+
+ 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)
+{
+ db_dump(m_db, os);
+}
+
+void
+interface::event_handler::handle_populate(const client_db::key_t& key)
+{
+ /*
+ * dump VPP current states
+ */
+ std::shared_ptr<interface_cmds::vhost_dump_cmd> vcmd =
+ std::make_shared<interface_cmds::vhost_dump_cmd>();
+
+ HW::enqueue(vcmd);
+ HW::write();
+
+ for (auto& vhost_itf_record : *vcmd) {
+ std::shared_ptr<interface> vitf =
+ interface_factory::new_vhost_user_interface(
+ vhost_itf_record.get_payload());
+ VOM_LOG(log_level_t::DEBUG) << " vhost-dump: " << vitf->to_string();
+ OM::commit(key, *vitf);
+ }
+
+ std::shared_ptr<interface_cmds::dump_cmd> cmd =
+ std::make_shared<interface_cmds::dump_cmd>();
+
+ HW::enqueue(cmd);
+ HW::write();
+
+ for (auto& itf_record : *cmd) {
+ auto payload = itf_record.get_payload();
+ VOM_LOG(log_level_t::DEBUG) << "dump: [" << payload.sw_if_index
+ << " name:" << (char*)payload.interface_name
+ << " tag:" << (char*)payload.tag << "]";
+
+ std::shared_ptr<interface> itf = interface_factory::new_interface(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_cmds::dump_v4_cmd> dcmd =
+ std::make_shared<l3_binding_cmds::dump_v4_cmd>(
+ l3_binding_cmds::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);
+ }
+ }
+ }
+
+ std::shared_ptr<bond_interface_cmds::dump_cmd> bcmd =
+ std::make_shared<bond_interface_cmds::dump_cmd>();
+
+ HW::enqueue(bcmd);
+ HW::write();
+
+ for (auto& bond_itf_record : *bcmd) {
+ std::shared_ptr<bond_interface> bond_itf =
+ interface_factory::new_bond_interface(bond_itf_record.get_payload());
+
+ VOM_LOG(log_level_t::DEBUG) << " bond-dump:" << bond_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, *bond_itf);
+
+ std::shared_ptr<bond_group_binding_cmds::dump_cmd> scmd =
+ std::make_shared<bond_group_binding_cmds::dump_cmd>(
+ bond_group_binding_cmds::dump_cmd(bond_itf->handle()));
+
+ HW::enqueue(scmd);
+ HW::write();
+
+ bond_group_binding::enslaved_itf_t enslaved_itfs;
+
+ for (auto& slave_itf_record : *scmd) {
+ bond_member slave_itf = interface_factory::new_bond_member_interface(
+ slave_itf_record.get_payload());
+
+ VOM_LOG(log_level_t::DEBUG) << " slave-dump:" << slave_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(slave_itf->key(), *slave_itf);
+ enslaved_itfs.insert(slave_itf);
+ }
+
+ if (!enslaved_itfs.empty()) {
+ bond_group_binding bid(*bond_itf, enslaved_itfs);
+ /*
+ * 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, bid);
+ }
+ }
+}
+
+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)
+{
+ db_dump(m_db, os);
+}
+
+} // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/interface.hpp b/extras/vom/vom/interface.hpp
new file mode 100644
index 00000000000..f6708b30176
--- /dev/null
+++ b/extras/vom/vom/interface.hpp
@@ -0,0 +1,659 @@
+/*
+ * 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/enum_base.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"
+
+namespace VOM {
+/**
+ * Forward declaration of the stats and events command
+ */
+namespace interface_cmds {
+class stats_enable_cmd;
+class events_cmd;
+};
+
+/**
+ * A representation of an interface in VPP
+ */
+class interface : public object_base
+{
+public:
+ struct stats_type_t : public enum_base<stats_type_t>
+ {
+ const static stats_type_t DETAILED;
+ const static stats_type_t NORMAL;
+
+ private:
+ stats_type_t(int v, const std::string& s);
+ };
+
+ /**
+ * The key for interface's key
+ */
+ typedef std::string key_t;
+
+ /**
+ * 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>
+ {
+ /**
+ * Unknown 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;
+
+ /**
+ * vhost-user interface type
+ */
+ const static type_t VHOST;
+
+ /**
+ * bond interface type
+ */
+ const static type_t BOND;
+
+ /**
+ * 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,
+ const std::string& tag = "");
+ /**
+ * 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,
+ const std::string& tag = "");
+ /**
+ * 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_t& key() const;
+
+ /**
+ * Return the L2 Address
+ */
+ const l2_address_t& l2_address() const;
+
+ /**
+ * Set the admin state of the interface
+ */
+ void set(const admin_state_t& state);
+
+ /**
+ * 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);
+
+ /**
+ * Set the tag to the interface
+ */
+ void set(const std::string& tag);
+
+ /**
+ * Comparison operator - only used for UT
+ */
+ virtual bool operator==(const interface& i) const;
+
+ /**
+ * 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());
+ }
+
+ /**
+ * add the created interface to the DB
+ */
+ void insert_interface() { 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;
+ };
+
+ /**
+ * 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() {}
+
+ /**
+ * remove the deleted interface from the DB
+ */
+ void remove_interface() { interface::remove(this->item()); }
+
+ protected:
+ /**
+ * The name of the interface to be created
+ */
+ const std::string m_name;
+ };
+
+ /**
+ * 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(interface_cmds::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 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(
+ interface_cmds::stats_enable_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;
+ };
+
+ /**
+ * The the singular instance of the interface in the DB by handle
+ */
+ static std::shared_ptr<interface> find(const handle_t& h);
+
+ /**
+ * The the singular instance of the interface in the DB by key
+ */
+ static std::shared_ptr<interface> find(const key_t& k);
+
+ /**
+ * Dump all interfaces into the stream provided
+ */
+ static void dump(std::ostream& os);
+
+ /**
+ * Enable stats for this interface
+ */
+ void enable_stats(stat_listener& el,
+ const stats_type_t& st = stats_type_t::NORMAL);
+
+protected:
+ /**
+ * Set the handle of an interface object. Only called by the interface
+ * factory during the populate
+ */
+ void set(const handle_t& handle);
+ friend class interface_factory;
+
+ /**
+ * 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<key_t, interface> m_db;
+
+ /**
+ * Add an interface to the DB keyed on handle
+ */
+ static void add(const key_t& 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;
+
+ /**
+ * enable the interface stats in the singular instance
+ */
+ void enable_stats_i(stat_listener& el, const stats_type_t& st);
+
+ /**
+ * Commit the acculmulated changes into VPP. i.e. to a 'HW" write.
+ */
+ void update(const interface& obj);
+
+ /*
+ * return the interface's handle in the singular instance
+ */
+ const handle_t& handle_i() const;
+
+ /*
+ * It's the OM class that calls singular()
+ */
+ friend class OM;
+
+ /**
+ * It's the singular_db class that calls replay()
+ */
+ friend class singular_db<key_t, 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 the default table
+ */
+ std::shared_ptr<route_domain> m_rd;
+
+ /**
+ * shared pointer to the stats object for this interface.
+ */
+ std::shared_ptr<interface_cmds::stats_enable_cmd> m_stats;
+
+ /**
+ * 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;
+
+ /**
+ * The state of the detailed stats collection
+ */
+ HW::item<stats_type_t> m_stats_type;
+
+ /**
+ * Operational state of the interface
+ */
+ oper_state_t m_oper;
+
+ /**
+ * tag of the interface
+ */
+ std::string m_tag;
+
+ /**
+ * 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/extras/vom/vom/interface_cmds.cpp b/extras/vom/vom/interface_cmds.cpp
new file mode 100644
index 00000000000..49a24c1301b
--- /dev/null
+++ b/extras/vom/vom/interface_cmds.cpp
@@ -0,0 +1,757 @@
+/*
+ * 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_cmds.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_VHOST_USER_API_JSON;
+DEFINE_VAPI_MSG_IDS_STATS_API_JSON;
+
+namespace VOM {
+namespace interface_cmds {
+loopback_create_cmd::loopback_create_cmd(HW::item<handle_t>& item,
+ const std::string& name)
+ : create_cmd(item, name)
+{
+}
+
+rc_t
+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) {
+ insert_interface();
+ }
+
+ return rc_t::OK;
+}
+std::string
+loopback_create_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "loopback-itf-create: " << m_hw_item.to_string() << " name:" << m_name;
+
+ return (s.str());
+}
+
+af_packet_create_cmd::af_packet_create_cmd(HW::item<handle_t>& item,
+ const std::string& name)
+ : create_cmd(item, name)
+{
+}
+
+rc_t
+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) {
+ insert_interface();
+ }
+
+ return rc_t::OK;
+}
+std::string
+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());
+}
+
+tap_create_cmd::tap_create_cmd(HW::item<handle_t>& item,
+ const std::string& name)
+ : create_cmd(item, name)
+{
+}
+
+rc_t
+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) {
+ insert_interface();
+ }
+
+ return rc_t::OK;
+}
+
+std::string
+tap_create_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "tap-intf-create: " << m_hw_item.to_string() << " name:" << m_name;
+
+ return (s.str());
+}
+
+vhost_create_cmd::vhost_create_cmd(HW::item<handle_t>& item,
+ const std::string& name,
+ const std::string& tag)
+ : create_cmd(item, name)
+ , m_tag(tag)
+{
+}
+
+rc_t
+vhost_create_cmd::issue(connection& con)
+{
+ msg_t req(con.ctx(), std::ref(*this));
+
+ auto& payload = req.get_request().get_payload();
+ memset(payload.sock_filename, 0, sizeof(payload.sock_filename));
+ memcpy(payload.sock_filename, m_name.c_str(),
+ std::min(m_name.length(), sizeof(payload.sock_filename)));
+ memset(payload.tag, 0, sizeof(payload.tag));
+
+ if (!m_tag.empty())
+ memcpy(payload.tag, m_tag.c_str(),
+ std::min(m_tag.length(), sizeof(payload.tag)));
+
+ payload.is_server = 0;
+ payload.use_custom_mac = 0;
+ payload.renumber = 0;
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item = wait();
+
+ if (m_hw_item.rc() == rc_t::OK) {
+ insert_interface();
+ }
+
+ return rc_t::OK;
+}
+
+std::string
+vhost_create_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "vhost-intf-create: " << m_hw_item.to_string() << " name:" << m_name
+ << " tag:" << m_tag;
+
+ return (s.str());
+}
+
+loopback_delete_cmd::loopback_delete_cmd(HW::item<handle_t>& item)
+ : delete_cmd(item)
+{
+}
+
+rc_t
+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);
+
+ remove_interface();
+ return rc_t::OK;
+}
+
+std::string
+loopback_delete_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "loopback-itf-delete: " << m_hw_item.to_string();
+
+ return (s.str());
+}
+
+af_packet_delete_cmd::af_packet_delete_cmd(HW::item<handle_t>& item,
+ const std::string& name)
+ : delete_cmd(item, name)
+{
+}
+
+rc_t
+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);
+
+ remove_interface();
+ return rc_t::OK;
+}
+std::string
+af_packet_delete_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "af_packet-itf-delete: " << m_hw_item.to_string();
+
+ return (s.str());
+}
+
+tap_delete_cmd::tap_delete_cmd(HW::item<handle_t>& item)
+ : delete_cmd(item)
+{
+}
+
+rc_t
+tap_delete_cmd::issue(connection& con)
+{
+ // finally... call VPP
+
+ remove_interface();
+ return rc_t::OK;
+}
+std::string
+tap_delete_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "tap-itf-delete: " << m_hw_item.to_string();
+
+ return (s.str());
+}
+
+vhost_delete_cmd::vhost_delete_cmd(HW::item<handle_t>& item,
+ const std::string& name)
+ : delete_cmd(item, name)
+{
+}
+
+rc_t
+vhost_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();
+ remove_interface();
+
+ return rc_t::OK;
+}
+std::string
+vhost_delete_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "vhost-itf-delete: " << m_hw_item.to_string() << " name:" << m_name;
+
+ return (s.str());
+}
+
+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
+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
+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
+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());
+}
+
+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
+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
+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
+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());
+}
+
+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
+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
+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
+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());
+}
+
+collect_detail_stats_change_cmd::collect_detail_stats_change_cmd(
+ HW::item<interface::stats_type_t>& item,
+ const handle_t& hdl,
+ bool enable)
+ : rpc_cmd(item)
+ , m_hdl(hdl)
+ , m_enable(enable)
+{
+}
+
+bool
+collect_detail_stats_change_cmd::operator==(
+ const collect_detail_stats_change_cmd& other) const
+{
+ return ((m_hdl == other.m_hdl) && (m_hw_item == other.m_hw_item) &&
+ (m_enable == other.m_enable));
+}
+
+rc_t
+collect_detail_stats_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.value();
+ payload.enable_disable = m_enable;
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return (rc_t::OK);
+}
+
+std::string
+collect_detail_stats_change_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "itf-stats: " << m_hw_item.to_string() << " hdl:" << m_hdl.to_string();
+ return (s.str());
+}
+
+events_cmd::events_cmd(interface::event_listener& el)
+ : event_cmd(el.status())
+ , m_listener(el)
+{
+}
+
+bool
+events_cmd::operator==(const events_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+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::OK);
+}
+
+void
+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
+events_cmd::notify()
+{
+ m_listener.handle_interface_event(this);
+}
+
+std::string
+events_cmd::to_string() const
+{
+ return ("itf-events");
+}
+
+/**
+ * Interface statistics
+ */
+stats_enable_cmd::stats_enable_cmd(interface::stat_listener& el,
+ const handle_t& handle)
+ : event_cmd(el.status())
+ , m_listener(el)
+ , m_swifindex(handle)
+{
+}
+
+bool
+stats_enable_cmd::operator==(const stats_enable_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+stats_enable_cmd::issue(connection& con)
+{
+ /*
+ * First set the call back to handle the interface stats
+ */
+ 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(), 1, std::ref(*(static_cast<rpc_cmd*>(this))));
+
+ auto& payload = req.get_request().get_payload();
+ payload.enable_disable = 1;
+ payload.pid = getpid();
+ payload.num = 1;
+
+ payload.sw_ifs[0] = m_swifindex.value();
+
+ VAPI_CALL(req.execute());
+
+ wait();
+
+ return (rc_t::OK);
+}
+
+void
+stats_enable_cmd::retire(connection& con)
+{
+ /*
+ * disable interface stats.
+ */
+ msg_t req(con.ctx(), 1, std::ref(*(static_cast<rpc_cmd*>(this))));
+
+ auto& payload = req.get_request().get_payload();
+ payload.enable_disable = 0;
+ payload.pid = getpid();
+ payload.num = 1;
+ payload.sw_ifs[0] = m_swifindex.value();
+
+ VAPI_CALL(req.execute());
+
+ wait();
+}
+
+interface::stat_listener&
+stats_enable_cmd::listener() const
+{
+ return m_listener;
+}
+
+void
+stats_enable_cmd::set(const rc_t& rc)
+{
+ m_listener.status().set(rc);
+}
+
+void
+stats_enable_cmd::notify()
+{
+ m_listener.handle_interface_stat(this);
+}
+
+std::string
+stats_enable_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "itf-stats-enable itf:" << m_swifindex.to_string();
+ return (s.str());
+}
+
+stats_disable_cmd::stats_disable_cmd(const handle_t& handle)
+ : rpc_cmd(m_res)
+ , m_swifindex(handle)
+{
+}
+
+bool
+stats_disable_cmd::operator==(const stats_disable_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+stats_disable_cmd::issue(connection& con)
+{
+ /*
+ * then send the request to enable them
+ */
+ msg_t req(con.ctx(), 1, std::ref(*this));
+
+ auto& payload = req.get_request().get_payload();
+ payload.enable_disable = 0;
+ payload.pid = getpid();
+ payload.num = 1;
+
+ payload.sw_ifs[0] = m_swifindex.value();
+
+ VAPI_CALL(req.execute());
+
+ wait();
+
+ return (rc_t::OK);
+}
+
+std::string
+stats_disable_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "itf-stats-disable itf:" << m_swifindex.to_string();
+ return (s.str());
+}
+
+dump_cmd::dump_cmd()
+{
+}
+
+bool
+dump_cmd::operator==(const dump_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+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
+dump_cmd::to_string() const
+{
+ return ("itf-dump");
+}
+
+vhost_dump_cmd::vhost_dump_cmd()
+{
+}
+
+bool
+vhost_dump_cmd::operator==(const vhost_dump_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+vhost_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::OK;
+}
+
+std::string
+vhost_dump_cmd::to_string() const
+{
+ return ("vhost-itf-dump");
+}
+
+set_tag::set_tag(HW::item<handle_t>& item, const std::string& name)
+ : rpc_cmd(item)
+ , m_name(name)
+{
+}
+
+rc_t
+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
+set_tag::to_string() const
+{
+ std::ostringstream s;
+ s << "itf-set-tag: " << m_hw_item.to_string() << " name:" << m_name;
+
+ return (s.str());
+}
+
+bool
+set_tag::operator==(const set_tag& o) const
+{
+ return ((m_name == o.m_name) && (m_hw_item.data() == o.m_hw_item.data()));
+}
+}; // namespace interface_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/interface_cmds.hpp b/extras/vom/vom/interface_cmds.hpp
new file mode 100644
index 00000000000..7a0040deb0a
--- /dev/null
+++ b/extras/vom/vom/interface_cmds.hpp
@@ -0,0 +1,615 @@
+/*
+ * 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_CMDS_H__
+#define __VOM_INTERFACE_CMDS_H__
+
+#include <vapi/vapi.hpp>
+
+#include "vom/dump_cmd.hpp"
+#include "vom/event_cmd.hpp"
+#include "vom/interface.hpp"
+#include "vom/rpc_cmd.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/vhost_user.api.vapi.hpp>
+#include <vapi/vpe.api.vapi.hpp>
+
+namespace VOM {
+
+namespace interface_cmds {
+/**
+ * Factory method to construct a new interface from the VPP record
+ */
+std::unique_ptr<interface> new_interface(
+ const vapi_payload_sw_interface_details& vd);
+
+/**
+ * A command class to create Loopback interfaces in VPP
+ */
+class loopback_create_cmd : public interface::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 interface::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 interface::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;
+};
+
+/**
+ * A functor class that creates an interface
+ */
+class vhost_create_cmd
+ : public interface::create_cmd<vapi::Create_vhost_user_if>
+{
+public:
+ vhost_create_cmd(HW::item<handle_t>& item,
+ const std::string& name,
+ const std::string& tag);
+
+ /**
+ * Issue the command to VPP/HW
+ */
+ rc_t issue(connection& con);
+ /**
+ * convert to string format for debug purposes
+ */
+ std::string to_string() const;
+
+private:
+ const std::string m_tag;
+};
+
+/**
+ * A command class to delete loopback interfaces in VPP
+ */
+class loopback_delete_cmd : public interface::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 interface::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 interface::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 functor class that deletes a Vhost interface
+ */
+class vhost_delete_cmd
+ : public interface::delete_cmd<vapi::Delete_vhost_user_if>
+{
+public:
+ vhost_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 set tag on interfaces
+ */
+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<interface::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<interface::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 changes the MAC address on an interface
+ */
+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;
+};
+
+/**
+ * A command class that enables detailed stats collection on an interface
+ */
+class collect_detail_stats_change_cmd
+ : public rpc_cmd<HW::item<interface::stats_type_t>,
+ rc_t,
+ vapi::Collect_detailed_interface_stats>
+{
+public:
+ /**
+ * Constructor taking the HW::item to update
+ * and the handle of the interface
+ */
+ collect_detail_stats_change_cmd(HW::item<interface::stats_type_t>& item,
+ const handle_t& h,
+ bool enable);
+
+ /**
+ * 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 collect_detail_stats_change_cmd& i) const;
+
+private:
+ /**
+ * the handle of the interface to update
+ */
+ const handle_t& m_hdl;
+
+ /**
+ * enable or disable the detailed stats collection
+ */
+ bool m_enable;
+};
+
+/**
+ * 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(interface::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
+ */
+ interface::event_listener& m_listener;
+};
+
+/**
+ * A command class represents our desire to recieve interface stats
+ */
+class stats_enable_cmd
+ : public event_cmd<vapi::Want_per_interface_combined_stats,
+ vapi::Vnet_per_interface_combined_counters>
+{
+public:
+ /**
+ * Constructor taking the listner to notify
+ */
+ stats_enable_cmd(interface::stat_listener& el, const handle_t& handle);
+
+ /**
+ * 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;
+
+ /**
+ * (re)set status
+ */
+ void set(const rc_t& rc);
+
+ /**
+ * get listener
+ */
+ interface::stat_listener& listener() const;
+
+ /**
+ * Comparison operator - only used for UT
+ */
+ bool operator==(const stats_enable_cmd& i) const;
+
+ /**
+ * Called when it's time to poke the listeners
+ */
+ void notify();
+
+private:
+ /**
+ * The listeners to notify when data/stats arrive
+ */
+ interface::stat_listener& m_listener;
+
+ /**
+ * The interface on which we are enabling states
+ */
+ const handle_t& m_swifindex;
+};
+
+/**
+ * A command class represents our desire to recieve interface stats
+ */
+class stats_disable_cmd
+ : public rpc_cmd<HW::item<bool>,
+ rc_t,
+ vapi::Want_per_interface_combined_stats>
+{
+public:
+ /**
+ * Constructor taking the listner to notify
+ */
+ stats_disable_cmd(const handle_t& handle);
+
+ /**
+ * 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 stats_disable_cmd& i) const;
+
+private:
+ HW::item<bool> m_res;
+ /**
+ * The interface on which we are disabling states
+ */
+ 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;
+};
+
+/**
+ * A cmd class that Dumps all the Vpp Interfaces
+ */
+class vhost_dump_cmd : public VOM::dump_cmd<vapi::Sw_interface_vhost_user_dump>
+{
+public:
+ /**
+ * Default Constructor
+ */
+ vhost_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 vhost_dump_cmd& i) const;
+};
+};
+};
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+#endif
diff --git a/extras/vom/vom/interface_factory.cpp b/extras/vom/vom/interface_factory.cpp
new file mode 100644
index 00000000000..417f4775a7a
--- /dev/null
+++ b/extras/vom/vom/interface_factory.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <boost/algorithm/string.hpp>
+
+#include "vom/bond_interface.hpp"
+#include "vom/bond_member.hpp"
+#include "vom/interface_factory.hpp"
+#include "vom/sub_interface.hpp"
+#include "vom/tap_interface.hpp"
+
+namespace VOM {
+std::shared_ptr<interface>
+interface_factory::new_interface(const vapi_payload_sw_interface_details& vd)
+{
+ std::shared_ptr<interface> sp;
+
+ /**
+ * Determine the interface type from the name and VLAN attributes
+ */
+ std::string name = reinterpret_cast<const char*>(vd.interface_name);
+ interface::type_t type = interface::type_t::from_string(name);
+ interface::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);
+ std::string tag = "";
+
+ if (interface::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) {
+ tag = std::string(reinterpret_cast<const char*>(vd.tag));
+ }
+
+ if (!tag.empty() && interface::type_t::LOOPBACK == type) {
+ name = tag;
+ type = interface::type_t::from_string(name);
+ }
+
+ /*
+ * pull out the other special cases
+ */
+ if (interface::type_t::TAP == type) {
+ /*
+ * TAP interface
+ */
+ sp = tap_interface(name, state, route::prefix_t()).singular();
+ if (sp && !tag.empty())
+ sp->set(tag);
+ } 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, tag);
+ sp = sub_interface(parent, state, vd.sub_id).singular();
+ } else if (interface::type_t::VXLAN == type) {
+ /*
+ * there's not enough information in a SW interface record to
+ * construct a VXLAN tunnel. so skip it. They have
+ * their own dump routines
+ */
+ } else if (interface::type_t::VHOST == type) {
+ /*
+ * vhost interface already exist in db, look for it using
+ * sw_if_index
+ */
+ sp = interface::find(hdl);
+ if (sp) {
+ sp->set(state);
+ sp->set(l2_address);
+ if (!tag.empty())
+ sp->set(tag);
+ }
+ } else if (interface::type_t::BOND == type) {
+ sp = bond_interface(name, state, l2_address,
+ bond_interface::mode_t::UNSPECIFIED)
+ .singular();
+ } else {
+ sp = interface(name, type, state, tag).singular();
+ sp->set(l2_address);
+ }
+
+ /*
+ * set the handle on the intterface - N.B. this is the sigluar instance
+ * not a stack local.
+ */
+ if (sp)
+ sp->set(hdl);
+
+ return (sp);
+}
+
+std::shared_ptr<interface>
+interface_factory::new_vhost_user_interface(
+ const vapi_payload_sw_interface_vhost_user_details& vd)
+{
+ std::shared_ptr<interface> sp;
+ std::string name = reinterpret_cast<const char*>(vd.sock_filename);
+ interface::type_t type = interface::type_t::from_string(name);
+ handle_t hdl(vd.sw_if_index);
+
+ sp = interface(name, type, interface::admin_state_t::DOWN).singular();
+ sp->set(hdl);
+ return (sp);
+}
+
+std::shared_ptr<bond_interface>
+interface_factory::new_bond_interface(
+ const vapi_payload_sw_interface_bond_details& vd)
+{
+ std::shared_ptr<bond_interface> sp;
+ std::string name = reinterpret_cast<const char*>(vd.interface_name);
+ handle_t hdl(vd.sw_if_index);
+ bond_interface::mode_t mode =
+ bond_interface::mode_t::from_numeric_val(vd.mode);
+ bond_interface::lb_t lb = bond_interface::lb_t::from_numeric_val(vd.lb);
+ sp = bond_interface::find(hdl);
+ if (sp) {
+ sp->set(mode);
+ sp->set(lb);
+ }
+ return (sp);
+}
+
+bond_member
+interface_factory::new_bond_member_interface(
+ const vapi_payload_sw_interface_slave_details& vd)
+{
+ std::shared_ptr<bond_member> sp;
+ std::string name = reinterpret_cast<const char*>(vd.interface_name);
+ handle_t hdl(vd.sw_if_index);
+ bond_member::mode_t mode =
+ bond_member::mode_t::from_numeric_val(vd.is_passive);
+ bond_member::rate_t rate =
+ bond_member::rate_t::from_numeric_val(vd.is_long_timeout);
+ std::shared_ptr<interface> itf = interface::find(hdl);
+ bond_member bm(*itf, mode, rate);
+ return (bm);
+}
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/interface_factory.hpp b/extras/vom/vom/interface_factory.hpp
new file mode 100644
index 00000000000..dda52752352
--- /dev/null
+++ b/extras/vom/vom/interface_factory.hpp
@@ -0,0 +1,57 @@
+/*
+ * 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_FACTORY_H__
+#define __VOM_INTERFACE_FACTORY_H__
+
+#include <vapi/vapi.hpp>
+
+#include "vom/bond_member.hpp"
+#include "vom/interface.hpp"
+
+#include <vapi/bond.api.vapi.hpp>
+#include <vapi/interface.api.vapi.hpp>
+#include <vapi/vhost_user.api.vapi.hpp>
+
+namespace VOM {
+
+class interface_factory
+{
+public:
+ /**
+ * Factory method to construct a new interface from the VPP record
+ */
+ static std::shared_ptr<interface> new_interface(
+ const vapi_payload_sw_interface_details& vd);
+
+ static std::shared_ptr<interface> new_vhost_user_interface(
+ const vapi_payload_sw_interface_vhost_user_details& vd);
+
+ static std::shared_ptr<bond_interface> new_bond_interface(
+ const vapi_payload_sw_interface_bond_details& vd);
+
+ static bond_member new_bond_member_interface(
+ const vapi_payload_sw_interface_slave_details& vd);
+};
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+#endif
diff --git a/extras/vom/vom/interface_ip6_nd.hpp b/extras/vom/vom/interface_ip6_nd.hpp
new file mode 100644
index 00000000000..22f545acdbb
--- /dev/null
+++ b/extras/vom/vom/interface_ip6_nd.hpp
@@ -0,0 +1,382 @@
+/*
+ * 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"
+
+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_t 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/extras/vom/vom/interface_ip6_nd_cmds.cpp b/extras/vom/vom/interface_ip6_nd_cmds.cpp
new file mode 100644
index 00000000000..c6f53f04919
--- /dev/null
+++ b/extras/vom/vom/interface_ip6_nd_cmds.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/interface_ip6_nd.hpp"
+
+#include <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/extras/vom/vom/interface_span.cpp b/extras/vom/vom/interface_span.cpp
new file mode 100644
index 00000000000..283ea1ece54
--- /dev/null
+++ b/extras/vom/vom/interface_span.cpp
@@ -0,0 +1,219 @@
+/*
+ * 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/interface_span_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+/**
+ * A DB of all interface_span config
+ */
+singular_db<interface_span::key_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 interface_span_cmds::unconfig_cmd(
+ m_config, m_itf_from->handle(), m_itf_to->handle()));
+ }
+ HW::write();
+}
+
+void
+interface_span::dump(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+
+void
+interface_span::replay()
+{
+ if (m_config) {
+ HW::enqueue(new interface_span_cmds::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 interface_span_cmds::config_cmd(
+ m_config, m_itf_from->handle(), m_itf_to->handle(), m_state));
+ }
+}
+
+std::ostream&
+operator<<(std::ostream& os, const interface_span::key_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_cmds::dump_cmd> cmd =
+ std::make_shared<interface_span_cmds::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)
+{
+ db_dump(m_db, 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/extras/vom/vom/interface_span.hpp b/extras/vom/vom/interface_span.hpp
new file mode 100644
index 00000000000..baa2054a530
--- /dev/null
+++ b/extras/vom/vom/interface_span.hpp
@@ -0,0 +1,222 @@
+/*
+ * 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/hw.hpp"
+#include "vom/inspect.hpp"
+#include "vom/interface.hpp"
+#include "vom/object_base.hpp"
+#include "vom/om.hpp"
+#include "vom/singular_db.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_t, interface::key_t> key_t;
+
+ /**
+ * Find a singular instance in the DB for the interface passed
+ */
+ static std::shared_ptr<interface_span> find(const interface& i);
+
+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_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_t, interface_span> m_db;
+};
+
+/**
+ * Ostream output for the key
+ */
+std::ostream& operator<<(std::ostream& os, const interface_span::key_t& key);
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/interface_span_cmds.cpp b/extras/vom/vom/interface_span_cmds.cpp
new file mode 100644
index 00000000000..3b8b8d25ec7
--- /dev/null
+++ b/extras/vom/vom/interface_span_cmds.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/interface_span_cmds.hpp"
+
+DEFINE_VAPI_MSG_IDS_SPAN_API_JSON;
+
+namespace VOM {
+namespace interface_span_cmds {
+
+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
+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
+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
+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());
+}
+
+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
+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
+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
+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());
+}
+
+dump_cmd::dump_cmd()
+{
+}
+
+bool
+dump_cmd::operator==(const dump_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+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
+dump_cmd::to_string() const
+{
+ return ("interface-span-dump");
+}
+
+}; // namespace interface_span_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/interface_span_cmds.hpp b/extras/vom/vom/interface_span_cmds.hpp
new file mode 100644
index 00000000000..fa8096ebb10
--- /dev/null
+++ b/extras/vom/vom/interface_span_cmds.hpp
@@ -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.
+ */
+
+#ifndef __VOM_INTERFACE_SPAN_CMDS_H__
+#define __VOM_INTERFACE_SPAN_CMDS_H__
+
+#include "vom/dump_cmd.hpp"
+#include "vom/interface_span.hpp"
+#include "vom/rpc_cmd.hpp"
+
+#include <vapi/span.api.vapi.hpp>
+
+namespace VOM {
+namespace interface_span_cmds {
+
+/**
+ * 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 interface_span::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 interface_span::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;
+};
+};
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/interface_types.cpp b/extras/vom/vom/interface_types.cpp
new file mode 100644
index 00000000000..139bdd52f9b
--- /dev/null
+++ b/extras/vom/vom/interface_types.cpp
@@ -0,0 +1,115 @@
+/*
+ * 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, "Ethernet");
+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::type_t interface::type_t::VHOST(8, "VHOST");
+const interface::type_t interface::type_t::BOND(9, "Bond");
+
+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");
+
+const interface::stats_type_t interface::stats_type_t::DETAILED(0, "detailed");
+const interface::stats_type_t interface::stats_type_t::NORMAL(1, "normal");
+
+interface::type_t
+interface::type_t::from_string(const std::string& str)
+{
+ if ((str.find("Virtual") != std::string::npos) ||
+ (str.find("vhost") != std::string::npos) ||
+ (str.find("vhu") != std::string::npos)) {
+ return interface::type_t::VHOST;
+ } else if (str.find("Bond") != std::string::npos) {
+ return interface::type_t::BOND;
+ } else 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) ||
+ (str.find("recirc") != 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::stats_type_t::stats_type_t(int v, const std::string& s)
+ : enum_base<interface::stats_type_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/extras/vom/vom/ip_unnumbered.cpp b/extras/vom/vom/ip_unnumbered.cpp
new file mode 100644
index 00000000000..caeeb419993
--- /dev/null
+++ b/extras/vom/vom/ip_unnumbered.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/ip_unnumbered.hpp"
+#include "vom/ip_unnumbered_cmds.hpp"
+#include "vom/singular_db_funcs.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 ip_unnumbered_cmds::unconfig_cmd(m_config, m_itf->handle(),
+ m_l3_itf->handle()));
+ }
+ HW::write();
+}
+
+void
+ip_unnumbered::dump(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+
+void
+ip_unnumbered::replay()
+{
+ if (m_config) {
+ HW::enqueue(new ip_unnumbered_cmds::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 ip_unnumbered_cmds::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)
+{
+ db_dump(m_db, os);
+}
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/ip_unnumbered.hpp b/extras/vom/vom/ip_unnumbered.hpp
new file mode 100644
index 00000000000..b736919744c
--- /dev/null
+++ b/extras/vom/vom/ip_unnumbered.hpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_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/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_t key_t;
+
+ /**
+ * Find an singular instance in the DB for the interface passed
+ */
+ static std::shared_ptr<ip_unnumbered> find(const interface& i);
+
+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/extras/vom/vom/ip_unnumbered_cmds.cpp b/extras/vom/vom/ip_unnumbered_cmds.cpp
new file mode 100644
index 00000000000..4c1fcf6d465
--- /dev/null
+++ b/extras/vom/vom/ip_unnumbered_cmds.cpp
@@ -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.
+ */
+
+#include "vom/ip_unnumbered_cmds.hpp"
+
+#include <vapi/vpe.api.vapi.hpp>
+
+namespace VOM {
+namespace ip_unnumbered_cmds {
+
+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
+config_cmd::operator==(const config_cmd& o) const
+{
+ return ((m_itf == o.m_itf) && (m_l3_itf == o.m_l3_itf));
+}
+
+rc_t
+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
+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());
+}
+
+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
+unconfig_cmd::operator==(const unconfig_cmd& o) const
+{
+ return ((m_itf == o.m_itf) && (m_l3_itf == o.m_l3_itf));
+}
+
+rc_t
+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
+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());
+}
+
+}; // namespace ip_unnumbered_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/ip_unnumbered_cmds.hpp b/extras/vom/vom/ip_unnumbered_cmds.hpp
new file mode 100644
index 00000000000..0bb70276e45
--- /dev/null
+++ b/extras/vom/vom/ip_unnumbered_cmds.hpp
@@ -0,0 +1,114 @@
+/*
+ * 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_CMDS_H__
+#define __VOM_IP_UNNUMBERED_CMDS_H__
+
+#include "vom/ip_unnumbered.hpp"
+#include "vom/rpc_cmd.hpp"
+
+#include <vapi/interface.api.vapi.hpp>
+
+namespace VOM {
+namespace ip_unnumbered_cmds {
+
+/**
+*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;
+};
+
+}; // namespace ip_unnumbered_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/l2_binding.cpp b/extras/vom/vom/l2_binding.cpp
new file mode 100644
index 00000000000..4118f74065a
--- /dev/null
+++ b/extras/vom/vom/l2_binding.cpp
@@ -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.
+ */
+
+#include "vom/l2_binding.hpp"
+#include "vom/l2_binding_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+/**
+ * A DB of all the L2 Configs
+ */
+singular_db<l2_binding::key_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)
+{
+}
+
+const l2_binding::key_t&
+l2_binding::key() const
+{
+ return (m_itf->key());
+}
+
+bool
+l2_binding::operator==(const l2_binding& l) const
+{
+ return ((*m_itf == *l.m_itf) && (*m_bd == *l.m_bd));
+}
+
+std::shared_ptr<l2_binding>
+l2_binding::find(const key_t& key)
+{
+ return (m_db.find(key));
+}
+
+void
+l2_binding::sweep()
+{
+ if (m_binding && handle_t::INVALID != m_itf->handle()) {
+ HW::enqueue(
+ new l2_binding_cmds::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 l2_binding_cmds::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 l2_binding_cmds::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->key(), this);
+}
+
+std::string
+l2_binding::to_string() const
+{
+ std::ostringstream s;
+ s << "L2-binding:[" << 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 l2_binding_cmds::bind_cmd(m_binding, m_itf->handle(), m_bd->id(),
+ interface::type_t::BVI == m_itf->type()));
+ } else if (!(*m_bd == *desired.m_bd)) {
+ /*
+ * re-binding to a different BD. do unbind, bind.
+ */
+ HW::enqueue(
+ new l2_binding_cmds::unbind_cmd(m_binding, m_itf->handle(), m_bd->id(),
+ interface::type_t::BVI == m_itf->type()));
+ m_bd = desired.m_bd;
+ HW::enqueue(
+ new l2_binding_cmds::bind_cmd(m_binding, m_itf->handle(), m_bd->id(),
+ interface::type_t::BVI == m_itf->type()));
+ }
+
+ /*
+ * set the VTR operation if request
+ */
+ if (m_vtr_op.update(desired.m_vtr_op)) {
+ HW::enqueue(new l2_binding_cmds::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->key(), temp));
+}
+
+std::shared_ptr<l2_binding>
+l2_binding::singular() const
+{
+ return find_or_add(*this);
+}
+
+void
+l2_binding::dump(std::ostream& os)
+{
+ db_dump(m_db, 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)
+{
+ db_dump(m_db, os);
+}
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/l2_binding.hpp b/extras/vom/vom/l2_binding.hpp
new file mode 100644
index 00000000000..d24f69ee33d
--- /dev/null
+++ b/extras/vom/vom/l2_binding.hpp
@@ -0,0 +1,221 @@
+/*
+ * 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/singular_db.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:
+ /**
+ * Key type for an L2 binding in the singular DB
+ */
+ typedef interface::key_t key_t;
+
+ 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 binding's key
+ */
+ const key_t& key() const;
+
+ /**
+ * Comparison operator - for UT
+ */
+ bool operator==(const l2_binding& l) const;
+
+ /**
+ * 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);
+
+ /**
+ * Static function to find the bridge_domain in the model
+ */
+ static std::shared_ptr<l2_binding> find(const key_t& key);
+
+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<key_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.
+ */
+ 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<key_t, l2_binding> m_db;
+};
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/l2_binding_cmds.cpp b/extras/vom/vom/l2_binding_cmds.cpp
new file mode 100644
index 00000000000..0f91e4b3b7a
--- /dev/null
+++ b/extras/vom/vom/l2_binding_cmds.cpp
@@ -0,0 +1,169 @@
+/*
+ * 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_cmds.hpp"
+
+namespace VOM {
+namespace l2_binding_cmds {
+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
+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
+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
+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());
+}
+
+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
+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
+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
+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());
+}
+
+set_vtr_op_cmd::set_vtr_op_cmd(HW::item<l2_binding::l2_vtr_op_t>& item,
+ const handle_t& itf,
+ uint16_t tag)
+ : rpc_cmd(item)
+ , m_itf(itf)
+ , m_tag(tag)
+{
+}
+
+bool
+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
+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();
+
+ return (rc_t::OK);
+}
+
+std::string
+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());
+}
+
+}; // namespace l2_binding_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/l2_binding_cmds.hpp b/extras/vom/vom/l2_binding_cmds.hpp
new file mode 100644
index 00000000000..d105c18a40d
--- /dev/null
+++ b/extras/vom/vom/l2_binding_cmds.hpp
@@ -0,0 +1,170 @@
+/*
+ * 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_CMDS_H__
+#define __VOM_L2_BINDING_CMDS_H__
+
+#include "vom/l2_binding.hpp"
+#include "vom/rpc_cmd.hpp"
+
+#include <vapi/l2.api.vapi.hpp>
+#include <vapi/vpe.api.vapi.hpp>
+
+namespace VOM {
+namespace l2_binding_cmds {
+
+/**
+ * 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_binding::l2_vtr_op_t>,
+ rc_t,
+ vapi::L2_interface_vlan_tag_rewrite>
+{
+public:
+ /**
+ * Constructor
+ */
+ set_vtr_op_cmd(HW::item<l2_binding::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;
+};
+
+}; // namespace l2_binding_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/l2_emulation.cpp b/extras/vom/vom/l2_emulation.cpp
new file mode 100644
index 00000000000..7bc7ed657d4
--- /dev/null
+++ b/extras/vom/vom/l2_emulation.cpp
@@ -0,0 +1,168 @@
+/*
+ * 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_emulation.hpp"
+#include "vom/l2_emulation_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+/**
+ * A DB of all the L2 Configs
+ */
+singular_db<l2_emulation::key_t, l2_emulation> l2_emulation::m_db;
+
+l2_emulation::event_handler l2_emulation::m_evh;
+
+/**
+ * Construct a new object matching the desried state
+ */
+l2_emulation::l2_emulation(const interface& itf)
+ : m_itf(itf.singular())
+ , m_emulation(0)
+{
+}
+
+l2_emulation::l2_emulation(const l2_emulation& o)
+ : m_itf(o.m_itf)
+ , m_emulation(0)
+{
+}
+
+const l2_emulation::key_t&
+l2_emulation::key() const
+{
+ return (m_itf->key());
+}
+
+bool
+l2_emulation::operator==(const l2_emulation& l) const
+{
+ return ((*m_itf == *l.m_itf));
+}
+
+std::shared_ptr<l2_emulation>
+l2_emulation::find(const key_t& key)
+{
+ return (m_db.find(key));
+}
+
+void
+l2_emulation::sweep()
+{
+ if (m_emulation && handle_t::INVALID != m_itf->handle()) {
+ HW::enqueue(
+ new l2_emulation_cmds::disable_cmd(m_emulation, m_itf->handle()));
+ }
+
+ // no need to undo the VTR operation.
+ HW::write();
+}
+
+void
+l2_emulation::replay()
+{
+ if (m_emulation && handle_t::INVALID != m_itf->handle()) {
+ HW::enqueue(
+ new l2_emulation_cmds::enable_cmd(m_emulation, m_itf->handle()));
+ }
+}
+
+l2_emulation::~l2_emulation()
+{
+ sweep();
+
+ // not in the DB anymore.
+ m_db.release(m_itf->key(), this);
+}
+
+std::string
+l2_emulation::to_string() const
+{
+ std::ostringstream s;
+ s << "L2-emulation:[" << m_itf->to_string() << "]";
+
+ return (s.str());
+}
+
+void
+l2_emulation::update(const l2_emulation& desired)
+{
+ /*
+ * the desired state is always that the interface should be created
+ */
+ if (rc_t::OK != m_emulation.rc()) {
+ HW::enqueue(
+ new l2_emulation_cmds::enable_cmd(m_emulation, m_itf->handle()));
+ }
+}
+
+std::shared_ptr<l2_emulation>
+l2_emulation::find_or_add(const l2_emulation& temp)
+{
+ return (m_db.find_or_add(temp.m_itf->key(), temp));
+}
+
+std::shared_ptr<l2_emulation>
+l2_emulation::singular() const
+{
+ return find_or_add(*this);
+}
+
+void
+l2_emulation::dump(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+
+l2_emulation::event_handler::event_handler()
+{
+ OM::register_listener(this);
+ inspect::register_handler({ "l2e" }, "L2 Emulation", this);
+}
+
+void
+l2_emulation::event_handler::handle_replay()
+{
+ m_db.replay();
+}
+
+void
+l2_emulation::event_handler::handle_populate(const client_db::key_t& key)
+{
+ /**
+ * This is done while populating the bridge-domain
+ */
+}
+
+dependency_t
+l2_emulation::event_handler::order() const
+{
+ return (dependency_t::BINDING);
+}
+
+void
+l2_emulation::event_handler::show(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/l2_emulation.hpp b/extras/vom/vom/l2_emulation.hpp
new file mode 100644
index 00000000000..faf4df8b0a7
--- /dev/null
+++ b/extras/vom/vom/l2_emulation.hpp
@@ -0,0 +1,180 @@
+/*
+ * 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_EMULATION_H__
+#define __VOM_L2_EMULATION_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/singular_db.hpp"
+
+namespace VOM {
+/**
+ * A Clas representing the binding of an L2 interface to a bridge-domain
+ * and the properties of that binding.
+ */
+class l2_emulation : public object_base
+{
+public:
+ /**
+ * Key type for an L2 emulation in the singular DB
+ */
+ typedef interface::key_t key_t;
+
+ /**
+ * Construct a new object matching the desried state
+ */
+ l2_emulation(const interface& itf);
+
+ /**
+ * Copy Constructor
+ */
+ l2_emulation(const l2_emulation& o);
+
+ /**
+ * Destructor
+ */
+ ~l2_emulation();
+
+ /**
+ * Return the binding's key
+ */
+ const key_t& key() const;
+
+ /**
+ * Comparison operator - for UT
+ */
+ bool operator==(const l2_emulation& l) const;
+
+ /**
+ * Return the 'singular instance' of the L2 config that matches this
+ * object
+ */
+ std::shared_ptr<l2_emulation> singular() const;
+
+ /**
+ * convert to string format for debug purposes
+ */
+ std::string to_string() const;
+
+ /**
+ * Dump all l2_emulations into the stream provided
+ */
+ static void dump(std::ostream& os);
+
+ /**
+ * Static function to find the bridge_domain in the model
+ */
+ static std::shared_ptr<l2_emulation> find(const key_t& key);
+
+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_emulation& obj);
+
+ /**
+ * Find or Add the singular instance in the DB
+ */
+ static std::shared_ptr<l2_emulation> find_or_add(const l2_emulation& 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<key_t, l2_emulation>;
+
+ /**
+ * 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;
+
+ /**
+ * HW configuration for the emulation. The bool representing the
+ * enable/disable.
+ */
+ HW::item<bool> m_emulation;
+
+ /**
+ * A map of all L2 emulation configurations
+ */
+ static singular_db<key_t, l2_emulation> m_db;
+};
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/l2_emulation_cmds.cpp b/extras/vom/vom/l2_emulation_cmds.cpp
new file mode 100644
index 00000000000..27f84830191
--- /dev/null
+++ b/extras/vom/vom/l2_emulation_cmds.cpp
@@ -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.
+ */
+
+#include "vom/l2_emulation_cmds.hpp"
+
+DEFINE_VAPI_MSG_IDS_L2E_API_JSON;
+
+namespace VOM {
+namespace l2_emulation_cmds {
+enable_cmd::enable_cmd(HW::item<bool>& item, const handle_t& itf)
+ : rpc_cmd(item)
+ , m_itf(itf)
+{
+}
+
+bool
+enable_cmd::operator==(const enable_cmd& other) const
+{
+ return (m_itf == other.m_itf);
+}
+
+rc_t
+enable_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;
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return (rc_t::OK);
+}
+
+std::string
+enable_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "L2-emulation-enable: " << m_hw_item.to_string()
+ << " itf:" << m_itf.to_string();
+
+ return (s.str());
+}
+
+disable_cmd::disable_cmd(HW::item<bool>& item, const handle_t& itf)
+ : rpc_cmd(item)
+ , m_itf(itf)
+{
+}
+
+bool
+disable_cmd::operator==(const disable_cmd& other) const
+{
+ return (m_itf == other.m_itf);
+}
+
+rc_t
+disable_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();
+
+ return (rc_t::OK);
+}
+
+std::string
+disable_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "L2-emulation-disable: " << m_hw_item.to_string()
+ << " itf:" << m_itf.to_string();
+
+ return (s.str());
+}
+
+}; // namespace l2_emulation_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/l2_emulation_cmds.hpp b/extras/vom/vom/l2_emulation_cmds.hpp
new file mode 100644
index 00000000000..aeff3a8ddd7
--- /dev/null
+++ b/extras/vom/vom/l2_emulation_cmds.hpp
@@ -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.
+ */
+
+#ifndef __VOM_L2_EMULATION_CMDS_H__
+#define __VOM_L2_EMULATION_CMDS_H__
+
+#include "vom/l2_emulation.hpp"
+#include "vom/rpc_cmd.hpp"
+
+#include <vapi/l2e.api.vapi.hpp>
+
+namespace VOM {
+namespace l2_emulation_cmds {
+
+/**
+ * A functor class that enable L2 emulation to an interface
+ */
+class enable_cmd : public rpc_cmd<HW::item<bool>, rc_t, vapi::L2_emulation>
+{
+public:
+ /**
+ * Constructor
+ */
+ enable_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 enable_cmd& i) const;
+
+private:
+ /**
+ * The interface to bind
+ */
+ const handle_t m_itf;
+};
+
+/**
+ * A cmd class that Unbinds L2 configuration from an interface
+ */
+class disable_cmd : public rpc_cmd<HW::item<bool>, rc_t, vapi::L2_emulation>
+{
+public:
+ /**
+ * Constructor
+ */
+ disable_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 disable_cmd& i) const;
+
+private:
+ /**
+ * The interface to bind
+ */
+ const handle_t m_itf;
+};
+
+}; // namespace l2_emulation_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/l3_binding.cpp b/extras/vom/vom/l3_binding.cpp
new file mode 100644
index 00000000000..13bc1ffd575
--- /dev/null
+++ b/extras/vom/vom/l3_binding.cpp
@@ -0,0 +1,229 @@
+/*
+ * 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/l3_binding_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+singular_db<l3_binding::key_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, rc_t::NOOP)
+{
+}
+
+l3_binding::l3_binding(const l3_binding& o)
+ : m_itf(o.m_itf)
+ , m_pfx(o.m_pfx)
+ , m_binding(o.m_binding)
+{
+}
+
+l3_binding::~l3_binding()
+{
+ sweep();
+
+ // not in the DB anymore.
+ m_db.release(key(), this);
+}
+
+bool
+l3_binding::operator==(const l3_binding& l) const
+{
+ return ((m_pfx == l.m_pfx) && (*m_itf == *l.m_itf));
+}
+
+const l3_binding::key_t
+l3_binding::key() const
+{
+ return (make_pair(m_itf->key(), m_pfx));
+}
+
+void
+l3_binding::sweep()
+{
+ if (m_binding) {
+ HW::enqueue(
+ new l3_binding_cmds::unbind_cmd(m_binding, m_itf->handle(), m_pfx));
+ }
+ HW::write();
+}
+
+void
+l3_binding::replay()
+{
+ if (m_binding) {
+ HW::enqueue(
+ new l3_binding_cmds::bind_cmd(m_binding, m_itf->handle(), m_pfx));
+ }
+}
+
+const route::prefix_t&
+l3_binding::prefix() const
+{
+ return (m_pfx);
+}
+
+const interface&
+l3_binding::itf() const
+{
+ return (*m_itf);
+}
+
+l3_binding::const_iterator_t
+l3_binding::cbegin()
+{
+ return m_db.begin();
+}
+
+l3_binding::const_iterator_t
+l3_binding::cend()
+{
+ return m_db.end();
+}
+
+std::string
+l3_binding::to_string() const
+{
+ std::ostringstream s;
+ s << "L3-binding:[" << m_itf->to_string() << " prefix:" << m_pfx.to_string()
+ << " " << m_binding.to_string() << "]";
+
+ return (s.str());
+}
+
+void
+l3_binding::update(const l3_binding& desired)
+{
+ /*
+ * no updates for the binding. chaning the interface or the prefix is a change
+ * to the
+ * key, hence a new object
+ */
+ if (!m_binding) {
+ HW::enqueue(
+ new l3_binding_cmds::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(temp.key(), temp));
+}
+
+std::shared_ptr<l3_binding>
+l3_binding::find(const key_t& k)
+{
+ return (m_db.find(k));
+}
+
+std::shared_ptr<l3_binding>
+l3_binding::singular() const
+{
+ return find_or_add(*this);
+}
+
+void
+l3_binding::dump(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+
+std::ostream&
+operator<<(std::ostream& os, const l3_binding::key_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.begin();
+
+ while (it != m_db.end()) {
+ /*
+ * 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)
+{
+ db_dump(m_db, os);
+}
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/l3_binding.hpp b/extras/vom/vom/l3_binding.hpp
new file mode 100644
index 00000000000..0177e56ea2b
--- /dev/null
+++ b/extras/vom/vom/l3_binding.hpp
@@ -0,0 +1,214 @@
+/*
+ * 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/hw.hpp"
+#include "vom/inspect.hpp"
+#include "vom/interface.hpp"
+#include "vom/object_base.hpp"
+#include "vom/om.hpp"
+#include "vom/singular_db.hpp"
+
+namespace VOM {
+/**
+ * A representation of L3 configuration on an interface
+ */
+class l3_binding : public object_base
+{
+public:
+ /**
+ * The key type for l3_bindings
+ */
+ typedef std::pair<interface::key_t, route::prefix_t> key_t;
+
+ /**
+ * 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();
+
+ /**
+ * Comparison operator
+ */
+ bool operator==(const l3_binding& l) const;
+
+ /**
+ * Get the object's key
+ */
+ const key_t key() const;
+
+ /**
+ * The iterator type
+ */
+ typedef singular_db<key_t, l3_binding>::const_iterator const_iterator_t;
+
+ static const_iterator_t cbegin();
+ static const_iterator_t cend();
+
+ /**
+ * 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 L3 binding
+ */
+ const route::prefix_t& prefix() const;
+
+ /**
+ * Return the interface associated with this L3 binding
+ */
+ const interface& itf() const;
+
+ /**
+ * Dump all l3_bindings into the stream provided
+ */
+ static void dump(std::ostream& os);
+
+ /**
+ * Find all bindings in the DB for the interface passed
+ */
+ static std::deque<std::shared_ptr<l3_binding>> find(const interface& i);
+
+ /**
+ * Find a binding from its key
+ */
+ static std::shared_ptr<l3_binding> find(const key_t& k);
+
+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_t, l3_binding>;
+
+ /**
+ * Sweep/reap the object if still stale
+ */
+ void sweep(void);
+
+ /**
+ * replay the object to create it in hardware
+ */
+ void replay(void);
+
+ friend class interface;
+
+ /**
+ * 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_t, l3_binding> m_db;
+};
+
+/**
+ * Ostream output for the key
+ */
+std::ostream& operator<<(std::ostream& os, const l3_binding::key_t& key);
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/l3_binding_cmds.cpp b/extras/vom/vom/l3_binding_cmds.cpp
new file mode 100644
index 00000000000..66691f86059
--- /dev/null
+++ b/extras/vom/vom/l3_binding_cmds.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/l3_binding_cmds.hpp"
+
+DEFINE_VAPI_MSG_IDS_IP_API_JSON;
+
+namespace VOM {
+namespace l3_binding_cmds {
+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
+bind_cmd::operator==(const bind_cmd& other) const
+{
+ return ((m_itf == other.m_itf) && (m_pfx == other.m_pfx));
+}
+
+rc_t
+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
+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());
+}
+
+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
+unbind_cmd::operator==(const unbind_cmd& other) const
+{
+ return ((m_itf == other.m_itf) && (m_pfx == other.m_pfx));
+}
+
+rc_t
+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
+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());
+}
+
+dump_v4_cmd::dump_v4_cmd(const handle_t& hdl)
+ : m_itf(hdl)
+{
+}
+
+dump_v4_cmd::dump_v4_cmd(const dump_v4_cmd& d)
+ : m_itf(d.m_itf)
+{
+}
+
+bool
+dump_v4_cmd::operator==(const dump_v4_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+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
+dump_v4_cmd::to_string() const
+{
+ return ("L3-binding-dump");
+}
+
+}; // namespace l3_binding_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/l3_binding_cmds.hpp b/extras/vom/vom/l3_binding_cmds.hpp
new file mode 100644
index 00000000000..1387bc47f5d
--- /dev/null
+++ b/extras/vom/vom/l3_binding_cmds.hpp
@@ -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.
+ */
+
+#ifndef __VOM_L3_BINDING_CMDS_H__
+#define __VOM_L3_BINDING_CMDS_H__
+
+#include "vom/dump_cmd.hpp"
+#include "vom/l3_binding.hpp"
+#include "vom/rpc_cmd.hpp"
+
+#include <vapi/interface.api.vapi.hpp>
+#include <vapi/ip.api.vapi.hpp>
+
+namespace VOM {
+namespace l3_binding_cmds {
+
+/**
+ * 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;
+};
+
+}; // namespace l3_binding_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/lldp_binding.cpp b/extras/vom/vom/lldp_binding.cpp
new file mode 100644
index 00000000000..69b30c2ce94
--- /dev/null
+++ b/extras/vom/vom/lldp_binding.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/lldp_binding.hpp"
+#include "vom/lldp_binding_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+/**
+ * A DB of all LLDP configs
+ */
+singular_db<interface::key_t, 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);
+}
+
+bool
+lldp_binding::operator==(const lldp_binding& l) const
+{
+ return ((key() == l.key()) && (m_port_desc == l.m_port_desc));
+}
+
+const lldp_binding::key_t&
+lldp_binding::key() const
+{
+ return (m_itf->key());
+}
+
+void
+lldp_binding::sweep()
+{
+ if (m_binding) {
+ HW::enqueue(new lldp_binding_cmds::unbind_cmd(m_binding, m_itf->handle()));
+ }
+ HW::write();
+}
+
+void
+lldp_binding::dump(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+
+void
+lldp_binding::replay()
+{
+ if (m_binding) {
+ HW::enqueue(
+ new lldp_binding_cmds::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 lldp_binding_cmds::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::find(const key_t& k)
+{
+ return (m_db.find(k));
+}
+
+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)
+{
+ db_dump(m_db, os);
+}
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/lldp_binding.hpp b/extras/vom/vom/lldp_binding.hpp
new file mode 100644
index 00000000000..0c5610013ac
--- /dev/null
+++ b/extras/vom/vom/lldp_binding.hpp
@@ -0,0 +1,182 @@
+/*
+ * 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/hw.hpp"
+#include "vom/inspect.hpp"
+#include "vom/interface.hpp"
+#include "vom/object_base.hpp"
+#include "vom/om.hpp"
+#include "vom/singular_db.hpp"
+
+namespace VOM {
+/**
+ * A representation of LLDP client configuration on an interface
+ */
+class lldp_binding : public object_base
+{
+public:
+ /**
+ * Typedef for the key of a LLDP binding
+ */
+ typedef interface::key_t key_t;
+
+ /**
+ * 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();
+
+ /**
+ * Comparison operator
+ */
+ bool operator==(const lldp_binding& b) const;
+
+ /**
+ * Return this object's key
+ */
+ const key_t& key() const;
+
+ /**
+ * 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);
+
+ /**
+ * Find or add LLDP binding based on its key
+ */
+ static std::shared_ptr<lldp_binding> find(const key_t& k);
+
+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<key_t, 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<key_t, lldp_binding> m_db;
+};
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/lldp_binding_cmds.cpp b/extras/vom/vom/lldp_binding_cmds.cpp
new file mode 100644
index 00000000000..7dacdafe20f
--- /dev/null
+++ b/extras/vom/vom/lldp_binding_cmds.cpp
@@ -0,0 +1,114 @@
+/*
+ * 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_cmds.hpp"
+
+DEFINE_VAPI_MSG_IDS_LLDP_API_JSON;
+
+namespace VOM {
+namespace lldp_binding_cmds {
+
+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
+bind_cmd::operator==(const bind_cmd& other) const
+{
+ return ((m_itf == other.m_itf) && (m_port_desc == other.m_port_desc));
+}
+
+rc_t
+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
+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());
+}
+
+unbind_cmd::unbind_cmd(HW::item<bool>& item, const handle_t& itf)
+ : rpc_cmd(item)
+ , m_itf(itf)
+{
+}
+
+bool
+unbind_cmd::operator==(const unbind_cmd& other) const
+{
+ return (m_itf == other.m_itf);
+}
+
+rc_t
+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
+unbind_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "Lldp-unbind: " << m_hw_item.to_string() << " itf:" << m_itf.to_string();
+
+ return (s.str());
+}
+
+}; // namespace lldp_binding_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/lldp_binding_cmds.hpp b/extras/vom/vom/lldp_binding_cmds.hpp
new file mode 100644
index 00000000000..b3038017e06
--- /dev/null
+++ b/extras/vom/vom/lldp_binding_cmds.hpp
@@ -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.
+ */
+
+#ifndef __VOM_LLDP_BINDING_CMDS_H__
+#define __VOM_LLDP_BINDING_CMDS_H__
+
+#include "vom/dump_cmd.hpp"
+#include "vom/lldp_binding.hpp"
+#include "vom/rpc_cmd.hpp"
+
+#include <vapi/lldp.api.vapi.hpp>
+
+namespace VOM {
+namespace lldp_binding_cmds {
+
+/**
+*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;
+};
+
+}; // namespace lldp_binding_cmds
+}; // naemspace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/lldp_global.cpp b/extras/vom/vom/lldp_global.cpp
new file mode 100644
index 00000000000..6bae7993651
--- /dev/null
+++ b/extras/vom/vom/lldp_global.cpp
@@ -0,0 +1,162 @@
+/*
+ * 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/lldp_global_cmds.hpp"
+#include "vom/singular_db_funcs.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);
+}
+
+const lldp_global::key_t&
+lldp_global::key() const
+{
+ return (m_system_name);
+}
+
+bool
+lldp_global::operator==(const lldp_global& l) const
+{
+ return ((key() == l.key()) && (m_tx_hold == l.m_tx_hold) &&
+ (m_tx_interval == l.m_tx_interval));
+}
+
+void
+lldp_global::sweep()
+{
+ // no means to remove this in VPP
+}
+
+void
+lldp_global::dump(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+
+void
+lldp_global::replay()
+{
+ if (m_binding) {
+ HW::enqueue(new lldp_global_cmds::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 lldp_global_cmds::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.key(), temp));
+}
+
+std::shared_ptr<lldp_global>
+lldp_global::find(const key_t& k)
+{
+ return (m_db.find(k));
+}
+
+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)
+{
+ db_dump(m_db, os);
+}
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/lldp_global.hpp b/extras/vom/vom/lldp_global.hpp
new file mode 100644
index 00000000000..f545a2828cc
--- /dev/null
+++ b/extras/vom/vom/lldp_global.hpp
@@ -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.
+ */
+
+#ifndef __VOM_LLDP_GLOBAL_H__
+#define __VOM_LLDP_GLOBAL_H__
+
+#include "vom/hw.hpp"
+#include "vom/inspect.hpp"
+#include "vom/object_base.hpp"
+#include "vom/om.hpp"
+#include "vom/singular_db.hpp"
+
+namespace VOM {
+/**
+ * A representation of LLDP global configuration
+ */
+class lldp_global : public object_base
+{
+public:
+ /**
+ * The key for the global conifugration is the 'system' namse
+ */
+ typedef std::string key_t;
+
+ /**
+ * 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();
+
+ /**
+ * Get this objects key
+ */
+ const key_t& key() const;
+
+ /**
+ * Comparison operator
+ */
+ bool operator==(const lldp_global& l) const;
+
+ /**
+ * 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);
+
+ /**
+ * Find LLDP global config from its key
+ */
+ static std::shared_ptr<lldp_global> find(const key_t& k);
+
+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<key_t, 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<key_t, lldp_global> m_db;
+};
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/lldp_global_cmds.cpp b/extras/vom/vom/lldp_global_cmds.cpp
new file mode 100644
index 00000000000..8237114f789
--- /dev/null
+++ b/extras/vom/vom/lldp_global_cmds.cpp
@@ -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.
+ */
+
+#include "vom/lldp_global_cmds.hpp"
+
+namespace VOM {
+namespace lldp_global_cmds {
+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
+config_cmd::operator==(const config_cmd& other) const
+{
+ return (m_system_name == other.m_system_name);
+}
+
+rc_t
+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
+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());
+}
+
+}; // namespace lldp_global_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/lldp_global_cmds.hpp b/extras/vom/vom/lldp_global_cmds.hpp
new file mode 100644
index 00000000000..621e73ff71d
--- /dev/null
+++ b/extras/vom/vom/lldp_global_cmds.hpp
@@ -0,0 +1,80 @@
+/*
+ * 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_CMDS_H__
+#define __VOM_LLDP_GLOBAL_CMDS_H__
+
+#include "vom/dump_cmd.hpp"
+#include "vom/lldp_global.hpp"
+#include "vom/rpc_cmd.hpp"
+
+#include <vapi/lldp.api.vapi.hpp>
+
+namespace VOM {
+namespace lldp_global_cmds {
+
+/**
+ * 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;
+};
+
+}; // namespace lldp_global_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/logger.cpp b/extras/vom/vom/logger.cpp
new file mode 100644
index 00000000000..80f2d92c603
--- /dev/null
+++ b/extras/vom/vom/logger.cpp
@@ -0,0 +1,170 @@
+/*
+ * 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 <vector>
+
+#include <boost/algorithm/string.hpp>
+
+#include "vom/logger.hpp"
+
+namespace VOM {
+const log_level_t log_level_t::CRITICAL(4, "critical");
+const log_level_t log_level_t::ERROR(3, "error");
+const log_level_t log_level_t::WARNING(2, "warning");
+const log_level_t log_level_t::INFO(1, "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_handler(new cout_handler())
+{
+}
+
+void
+log_t::set(const log_level_t& level)
+{
+ m_level = level;
+}
+
+void
+log_t::set(handler* h)
+{
+ m_handler = h;
+}
+
+void
+log_t::write(const std::string& file,
+ const int line,
+ const std::string& function,
+ const log_level_t& level,
+ const std::string& message)
+{
+ m_handler->handle_message(file, line, function, level, message);
+}
+
+/**
+ * The configured level
+ */
+const log_level_t&
+log_t::level() const
+{
+ return (m_level);
+}
+
+static std::string
+get_filename(const std::string& file)
+{
+ std::vector<std::string> dirs;
+ boost::split(dirs, file, boost::is_any_of("/"));
+
+ return dirs.back();
+}
+
+log_t::entry::entry(const char* file,
+ const char* function,
+ int line,
+ const log_level_t& level)
+ : m_file(get_filename(file))
+ , m_function(function)
+ , m_level(level)
+ , m_line(line)
+{
+}
+
+log_t::entry::~entry()
+{
+ logger().write(m_file, m_line, m_function, m_level, m_stream.str());
+}
+
+std::stringstream&
+log_t::entry::stream()
+{
+ return (m_stream);
+}
+
+static std::string
+get_timestamp()
+{
+ 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();
+
+ return (display);
+}
+
+file_handler::file_handler(const std::string& ofile)
+{
+ m_file_stream.open(ofile);
+}
+
+file_handler::~file_handler()
+{
+ m_file_stream.close();
+}
+
+void
+file_handler::handle_message(const std::string& file,
+ const int line,
+ const std::string& function,
+ const log_level_t& level,
+ const std::string& message)
+{
+ m_file_stream << get_timestamp();
+ m_file_stream << " [" << level.to_string() << "]" << file << ":" << line
+ << " " << function << "() " << message << std::endl;
+}
+
+void
+cout_handler::handle_message(const std::string& file,
+ const int line,
+ const std::string& function,
+ const log_level_t& level,
+ const std::string& message)
+{
+ std::cout << get_timestamp();
+ std::cout << " [" << level.to_string() << "]" << file << ":" << line << " "
+ << function << "() " << message << std::endl;
+}
+
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/logger.hpp b/extras/vom/vom/logger.hpp
new file mode 100644
index 00000000000..6d2e3dd82f6
--- /dev/null
+++ b/extras/vom/vom/logger.hpp
@@ -0,0 +1,194 @@
+/*
+ * 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 <sstream>
+
+#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);
+
+ /*
+ * not allowed to construct
+ */
+ log_level_t() = delete;
+};
+
+/**
+ * Ideally we'd use the boost logger but that is not prevelent
+ * in many distros. So something simple here instead.
+ */
+class log_t
+{
+public:
+ /**
+ *
+ */
+ class handler
+ {
+ public:
+ /**
+ * Default Constructor
+ */
+ handler() = default;
+
+ /**
+ * Default Destructor
+ */
+ virtual ~handler() = default;
+
+ /**
+ * Handle a log message
+ */
+ virtual void handle_message(const std::string& file,
+ const int line,
+ const std::string& function,
+ const log_level_t& level,
+ const std::string& message) = 0;
+ };
+
+ /**
+ * Construct a logger
+ */
+ log_t(handler* h);
+ log_t();
+
+ /**
+ * 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(handler* h);
+
+ /**
+ * An entry in the log
+ */
+ class entry
+ {
+ public:
+ entry(const char* file,
+ const char* function,
+ int line,
+ const log_level_t& level);
+ ~entry();
+
+ std::stringstream& stream();
+
+ private:
+ const std::string m_file;
+ const std::string m_function;
+ const log_level_t m_level;
+ const int m_line;
+
+ std::stringstream m_stream;
+ };
+ /**
+ * Register a log handler to receive the log output
+ */
+ void register_handler(handler& h);
+
+private:
+ void write(const std::string& file,
+ const int line,
+ const std::string& function,
+ const log_level_t& level,
+ const std::string& message);
+
+ /**
+ * the configured logging level
+ */
+ log_level_t m_level;
+
+ /**
+ * Pointer to a registered handler. Null if no handler registerd
+ */
+ handler* m_handler;
+};
+
+class file_handler : public log_t::handler
+{
+public:
+ file_handler(const std::string& ofile);
+ ~file_handler();
+
+ virtual void handle_message(const std::string& file,
+ const int line,
+ const std::string& function,
+ const log_level_t& level,
+ const std::string& message);
+
+private:
+ /**
+ * Opened file for debugging
+ */
+ std::ofstream m_file_stream;
+};
+
+class cout_handler : public log_t::handler
+{
+public:
+ cout_handler() = default;
+ ~cout_handler() = default;
+ virtual void handle_message(const std::string& file,
+ const int line,
+ const std::string& function,
+ const log_level_t& level,
+ const std::string& message);
+};
+
+/**
+ * Return a log object into which VPP objects can write
+ */
+log_t& logger();
+
+#define VOM_LOG(lvl) \
+ if (lvl >= logger().level()) \
+ log_t::entry(__FILE__, __FUNCTION__, __LINE__, lvl).stream()
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/nat_binding.cpp b/extras/vom/vom/nat_binding.cpp
new file mode 100644
index 00000000000..64b7a1007ac
--- /dev/null
+++ b/extras/vom/vom/nat_binding.cpp
@@ -0,0 +1,280 @@
+/*
+ * 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"
+#include "vom/nat_binding_cmds.hpp"
+#include "vom/singular_db_funcs.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)
+{
+}
+const nat_binding::zone_t&
+nat_binding::zone_t::from_vpp(u8 is_inside)
+{
+ if (is_inside)
+ return zone_t::INSIDE;
+ return zone_t::OUTSIDE;
+}
+
+/**
+ * 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(key(), this);
+}
+
+const nat_binding::key_t
+nat_binding::key() const
+{
+ return (make_tuple(m_itf->key(), m_dir, m_proto));
+}
+
+bool
+nat_binding::operator==(const nat_binding& n) const
+{
+ return ((key() == n.key()) && (m_zone == n.m_zone));
+}
+
+void
+nat_binding::sweep()
+{
+ if (m_binding) {
+ if (direction_t::INPUT == m_dir) {
+ if (l3_proto_t::IPV4 == m_proto) {
+ HW::enqueue(new nat_binding_cmds::unbind_44_input_cmd(
+ m_binding, m_itf->handle(), m_zone));
+ } else {
+ HW::enqueue(new nat_binding_cmds::unbind_66_input_cmd(
+ m_binding, m_itf->handle(), m_zone));
+ }
+ } else {
+ if (l3_proto_t::IPV4 == m_proto) {
+ HW::enqueue(new nat_binding_cmds::unbind_44_output_cmd(
+ m_binding, m_itf->handle(), m_zone));
+ } else {
+ VOM_LOG(log_level_t::ERROR) << "NAT 66 output feature not supported";
+ }
+ }
+ }
+ HW::write();
+}
+
+void
+nat_binding::replay()
+{
+ if (m_binding) {
+ if (direction_t::INPUT == m_dir) {
+ if (l3_proto_t::IPV4 == m_proto) {
+ HW::enqueue(new nat_binding_cmds::bind_44_input_cmd(
+ m_binding, m_itf->handle(), m_zone));
+ } else {
+ HW::enqueue(new nat_binding_cmds::bind_66_input_cmd(
+ m_binding, m_itf->handle(), m_zone));
+ }
+ } else {
+ if (l3_proto_t::IPV4 == m_proto) {
+ HW::enqueue(new nat_binding_cmds::bind_44_output_cmd(
+ m_binding, m_itf->handle(), m_zone));
+ } else {
+ VOM_LOG(log_level_t::ERROR) << "NAT 66 output feature not supported";
+ }
+ }
+ }
+}
+
+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) {
+ if (l3_proto_t::IPV4 == m_proto) {
+ HW::enqueue(new nat_binding_cmds::bind_44_input_cmd(
+ m_binding, m_itf->handle(), m_zone));
+ } else {
+ HW::enqueue(new nat_binding_cmds::bind_66_input_cmd(
+ m_binding, m_itf->handle(), m_zone));
+ }
+ } else {
+ if (l3_proto_t::IPV4 == m_proto) {
+ HW::enqueue(new nat_binding_cmds::bind_44_output_cmd(
+ m_binding, m_itf->handle(), m_zone));
+ } else {
+ VOM_LOG(log_level_t::ERROR) << "NAT 66 output feature not supported";
+ }
+ }
+ }
+}
+
+std::string
+nat_binding::to_string() const
+{
+ std::ostringstream s;
+ s << "nat-binding:[" << m_itf->to_string()
+ << " direction:" << m_dir.to_string() << " proto:" << m_proto.to_string()
+ << " zone:" << 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(temp.key(), temp));
+}
+
+std::shared_ptr<nat_binding>
+nat_binding::find(const key_t& key)
+{
+ return (m_db.find(key));
+}
+
+std::shared_ptr<nat_binding>
+nat_binding::singular() const
+{
+ return find_or_add(*this);
+}
+
+void
+nat_binding::dump(std::ostream& os)
+{
+ db_dump(m_db, 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)
+{
+ std::shared_ptr<nat_binding_cmds::dump_input_44_cmd> icmd =
+ std::make_shared<nat_binding_cmds::dump_input_44_cmd>();
+
+ HW::enqueue(icmd);
+ HW::write();
+
+ for (auto& record : *icmd) {
+ auto& payload = record.get_payload();
+
+ std::shared_ptr<interface> itf = interface::find(payload.sw_if_index);
+ nat_binding nb(*itf, direction_t::INPUT, l3_proto_t::IPV4,
+ zone_t::from_vpp(payload.is_inside));
+ OM::commit(key, nb);
+ }
+
+ std::shared_ptr<nat_binding_cmds::dump_output_44_cmd> ocmd =
+ std::make_shared<nat_binding_cmds::dump_output_44_cmd>();
+
+ HW::enqueue(ocmd);
+ HW::write();
+
+ for (auto& record : *ocmd) {
+ auto& payload = record.get_payload();
+
+ std::shared_ptr<interface> itf = interface::find(payload.sw_if_index);
+ nat_binding nb(*itf, direction_t::OUTPUT, l3_proto_t::IPV4,
+ zone_t::from_vpp(payload.is_inside));
+ OM::commit(key, nb);
+ }
+
+ std::shared_ptr<nat_binding_cmds::dump_input_66_cmd> i6cmd =
+ std::make_shared<nat_binding_cmds::dump_input_66_cmd>();
+
+ HW::enqueue(i6cmd);
+ HW::write();
+
+ for (auto& record : *i6cmd) {
+ auto& payload = record.get_payload();
+
+ std::shared_ptr<interface> itf = interface::find(payload.sw_if_index);
+ nat_binding nb(*itf, direction_t::INPUT, l3_proto_t::IPV6,
+ zone_t::from_vpp(payload.is_inside));
+ OM::commit(key, nb);
+ }
+}
+
+dependency_t
+nat_binding::event_handler::order() const
+{
+ return (dependency_t::BINDING);
+}
+
+void
+nat_binding::event_handler::show(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/nat_binding.hpp b/extras/vom/vom/nat_binding.hpp
new file mode 100644
index 00000000000..a99d23af601
--- /dev/null
+++ b/extras/vom/vom/nat_binding.hpp
@@ -0,0 +1,232 @@
+/*
+ * 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/singular_db.hpp"
+
+namespace VOM {
+/**
+ * A Class 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;
+
+ const static zone_t& from_vpp(u8 is_inside);
+ };
+
+ /**
+ * 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_t, 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();
+
+ /**
+ * Comparison operator - for UT
+ */
+ bool operator==(const nat_binding& n) const;
+
+ /**
+ * Return the binding's key
+ */
+ const key_t key() const;
+
+ /**
+ * 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;
+
+ /**
+ * Static function to find the bridge_domain in the model
+ */
+ static std::shared_ptr<nat_binding> find(const key_t& key);
+
+ /**
+ * Dump all nat_bindings into the stream provided
+ */
+ static void dump(std::ostream& os);
+
+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/extras/vom/vom/nat_binding_cmds.cpp b/extras/vom/vom/nat_binding_cmds.cpp
new file mode 100644
index 00000000000..4d98268249a
--- /dev/null
+++ b/extras/vom/vom/nat_binding_cmds.cpp
@@ -0,0 +1,377 @@
+/*
+ * 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_cmds.hpp"
+
+namespace VOM {
+namespace nat_binding_cmds {
+bind_44_input_cmd::bind_44_input_cmd(HW::item<bool>& item,
+ const handle_t& itf,
+ const nat_binding::zone_t& zone)
+ : rpc_cmd(item)
+ , m_itf(itf)
+ , m_zone(zone)
+{
+}
+
+bool
+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
+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 = (nat_binding::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
+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());
+}
+
+unbind_44_input_cmd::unbind_44_input_cmd(HW::item<bool>& item,
+ const handle_t& itf,
+ const nat_binding::zone_t& zone)
+ : rpc_cmd(item)
+ , m_itf(itf)
+ , m_zone(zone)
+{
+}
+
+bool
+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
+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 = (nat_binding::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
+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());
+}
+
+bind_44_output_cmd::bind_44_output_cmd(HW::item<bool>& item,
+ const handle_t& itf,
+ const nat_binding::zone_t& zone)
+ : rpc_cmd(item)
+ , m_itf(itf)
+ , m_zone(zone)
+{
+}
+
+bool
+bind_44_output_cmd::operator==(const bind_44_output_cmd& other) const
+{
+ return ((m_itf == other.m_itf) && (m_zone == other.m_zone));
+}
+
+rc_t
+bind_44_output_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 = (nat_binding::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
+bind_44_output_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "nat-44-output-binding-create: " << m_hw_item.to_string()
+ << " itf:" << m_itf << " " << m_zone.to_string();
+
+ return (s.str());
+}
+
+unbind_44_output_cmd::unbind_44_output_cmd(HW::item<bool>& item,
+ const handle_t& itf,
+ const nat_binding::zone_t& zone)
+ : rpc_cmd(item)
+ , m_itf(itf)
+ , m_zone(zone)
+{
+}
+
+bool
+unbind_44_output_cmd::operator==(const unbind_44_output_cmd& other) const
+{
+ return ((m_itf == other.m_itf) && (m_zone == other.m_zone));
+}
+
+rc_t
+unbind_44_output_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 = (nat_binding::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
+unbind_44_output_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "nat-44-output-binding-create: " << m_hw_item.to_string()
+ << " itf:" << m_itf << " " << m_zone.to_string();
+
+ return (s.str());
+}
+
+dump_input_44_cmd::dump_input_44_cmd()
+{
+}
+
+dump_input_44_cmd::dump_input_44_cmd(const dump_input_44_cmd& d)
+{
+}
+
+bool
+dump_input_44_cmd::operator==(const dump_input_44_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+dump_input_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
+dump_input_44_cmd::to_string() const
+{
+ return ("nat-44-input-binding-dump");
+}
+
+dump_output_44_cmd::dump_output_44_cmd()
+{
+}
+
+dump_output_44_cmd::dump_output_44_cmd(const dump_output_44_cmd& d)
+{
+}
+
+bool
+dump_output_44_cmd::operator==(const dump_output_44_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+dump_output_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
+dump_output_44_cmd::to_string() const
+{
+ return ("nat-44-output-binding-dump");
+}
+
+bind_66_input_cmd::bind_66_input_cmd(HW::item<bool>& item,
+ const handle_t& itf,
+ const nat_binding::zone_t& zone)
+ : rpc_cmd(item)
+ , m_itf(itf)
+ , m_zone(zone)
+{
+}
+
+bool
+bind_66_input_cmd::operator==(const bind_66_input_cmd& other) const
+{
+ return ((m_itf == other.m_itf) && (m_zone == other.m_zone));
+}
+
+rc_t
+bind_66_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 = (nat_binding::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
+bind_66_input_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "nat-66-input-binding-create: " << m_hw_item.to_string()
+ << " itf:" << m_itf << " " << m_zone.to_string();
+
+ return (s.str());
+}
+
+unbind_66_input_cmd::unbind_66_input_cmd(HW::item<bool>& item,
+ const handle_t& itf,
+ const nat_binding::zone_t& zone)
+ : rpc_cmd(item)
+ , m_itf(itf)
+ , m_zone(zone)
+{
+}
+
+bool
+unbind_66_input_cmd::operator==(const unbind_66_input_cmd& other) const
+{
+ return ((m_itf == other.m_itf) && (m_zone == other.m_zone));
+}
+
+rc_t
+unbind_66_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 = (nat_binding::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
+unbind_66_input_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "nat-66-input-binding-create: " << m_hw_item.to_string()
+ << " itf:" << m_itf << " " << m_zone.to_string();
+
+ return (s.str());
+}
+
+dump_input_66_cmd::dump_input_66_cmd()
+{
+}
+
+dump_input_66_cmd::dump_input_66_cmd(const dump_input_66_cmd& d)
+{
+}
+
+bool
+dump_input_66_cmd::operator==(const dump_input_66_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+dump_input_66_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
+dump_input_66_cmd::to_string() const
+{
+ return ("nat-66-input-binding-dump");
+}
+
+}; // namespace nat_binding_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/nat_binding_cmds.hpp b/extras/vom/vom/nat_binding_cmds.hpp
new file mode 100644
index 00000000000..aa5a200515e
--- /dev/null
+++ b/extras/vom/vom/nat_binding_cmds.hpp
@@ -0,0 +1,383 @@
+/*
+ * 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_CMDS_H__
+#define __VOM_NAT_BINDING_CMDS_H__
+
+#include "vom/dump_cmd.hpp"
+#include "vom/nat_binding.hpp"
+#include "vom/rpc_cmd.hpp"
+
+#include <vapi/nat.api.vapi.hpp>
+
+namespace VOM {
+namespace nat_binding_cmds {
+/**
+ * A functor class that binds a NAT configuration to an input 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 nat_binding::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 nat_binding::zone_t m_zone;
+};
+
+/**
+ * A cmd class that unbinds a NAT configuration from an input 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 nat_binding::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 nat_binding::zone_t m_zone;
+};
+
+/**
+ * A functor class that binds a NAT configuration to an output interface
+ */
+class bind_44_output_cmd
+ : public rpc_cmd<HW::item<bool>,
+ rc_t,
+ vapi::Nat44_interface_add_del_output_feature>
+{
+public:
+ /**
+ * Constructor
+ */
+ bind_44_output_cmd(HW::item<bool>& item,
+ const handle_t& itf,
+ const nat_binding::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_output_cmd& i) const;
+
+private:
+ /**
+ * The interface to bind
+ */
+ const handle_t m_itf;
+
+ /**
+ * The zone the interface is in
+ */
+ const nat_binding::zone_t m_zone;
+};
+
+/**
+ * A cmd class that unbinds a NAT configuration from an output interface
+ */
+class unbind_44_output_cmd
+ : public rpc_cmd<HW::item<bool>,
+ rc_t,
+ vapi::Nat44_interface_add_del_output_feature>
+{
+public:
+ /**
+ * Constructor
+ */
+ unbind_44_output_cmd(HW::item<bool>& item,
+ const handle_t& itf,
+ const nat_binding::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_output_cmd& i) const;
+
+private:
+ /**
+ * The interface to bind
+ */
+ const handle_t m_itf;
+
+ /**
+ * The zone the interface is in
+ */
+ const nat_binding::zone_t m_zone;
+};
+
+/**
+ * A cmd class that Dumps all the nat_statics
+ */
+class dump_input_44_cmd : public dump_cmd<vapi::Nat44_interface_dump>
+{
+public:
+ /**
+ * Constructor
+ */
+ dump_input_44_cmd();
+ dump_input_44_cmd(const dump_input_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_input_44_cmd& i) const;
+
+private:
+ /**
+ * HW reutrn code
+ */
+ HW::item<bool> item;
+};
+
+/**
+ * A cmd class that Dumps all the nat_statics
+ */
+class dump_output_44_cmd
+ : public dump_cmd<vapi::Nat44_interface_output_feature_dump>
+{
+public:
+ /**
+ * Constructor
+ */
+ dump_output_44_cmd();
+ dump_output_44_cmd(const dump_output_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_output_44_cmd& i) const;
+
+private:
+ /**
+ * HW reutrn code
+ */
+ HW::item<bool> item;
+};
+
+/////
+/**
+* A functor class that binds a NAT configuration to an input interface
+*/
+class bind_66_input_cmd
+ : public rpc_cmd<HW::item<bool>, rc_t, vapi::Nat66_add_del_interface>
+{
+public:
+ /**
+ * Constructor
+ */
+ bind_66_input_cmd(HW::item<bool>& item,
+ const handle_t& itf,
+ const nat_binding::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_66_input_cmd& i) const;
+
+private:
+ /**
+ * The interface to bind
+ */
+ const handle_t m_itf;
+
+ /**
+ * The zone the interface is in
+ */
+ const nat_binding::zone_t m_zone;
+};
+
+/**
+ * A cmd class that unbinds a NAT configuration from an input interface
+ */
+class unbind_66_input_cmd
+ : public rpc_cmd<HW::item<bool>, rc_t, vapi::Nat66_add_del_interface>
+{
+public:
+ /**
+ * Constructor
+ */
+ unbind_66_input_cmd(HW::item<bool>& item,
+ const handle_t& itf,
+ const nat_binding::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_66_input_cmd& i) const;
+
+private:
+ /**
+ * The interface to bind
+ */
+ const handle_t m_itf;
+
+ /**
+ * The zone the interface is in
+ */
+ const nat_binding::zone_t m_zone;
+};
+
+/**
+ * A cmd class that Dumps all the nat_statics
+ */
+class dump_input_66_cmd : public dump_cmd<vapi::Nat66_interface_dump>
+{
+public:
+ /**
+ * Constructor
+ */
+ dump_input_66_cmd();
+ dump_input_66_cmd(const dump_input_66_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_input_66_cmd& i) const;
+
+private:
+ /**
+ * HW reutrn code
+ */
+ HW::item<bool> item;
+};
+
+}; // namespace nat_binding_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/nat_static.cpp b/extras/vom/vom/nat_static.cpp
new file mode 100644
index 00000000000..bf8573d333e
--- /dev/null
+++ b/extras/vom/vom/nat_static.cpp
@@ -0,0 +1,236 @@
+/*
+ * 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"
+#include "vom/nat_static_cmds.hpp"
+#include "vom/singular_db_funcs.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& 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& 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(key(), this);
+}
+
+const nat_static::key_t
+nat_static::key() const
+{
+ return (std::make_pair(m_rd->key(), m_outside));
+}
+
+bool
+nat_static::operator==(const nat_static& n) const
+{
+ return ((key() == n.key()) && (m_inside == n.m_inside));
+}
+
+void
+nat_static::sweep()
+{
+ if (m_hw) {
+ if (m_inside.is_v4()) {
+ HW::enqueue(new nat_static_cmds::delete_44_cmd(
+ m_hw, m_rd->table_id(), m_inside.to_v4(), m_outside.to_v4()));
+ } else {
+ HW::enqueue(new nat_static_cmds::delete_66_cmd(
+ m_hw, m_rd->table_id(), m_inside.to_v6(), m_outside.to_v6()));
+ }
+ }
+ HW::write();
+}
+
+void
+nat_static::replay()
+{
+ if (m_hw) {
+ if (m_inside.is_v4()) {
+ HW::enqueue(new nat_static_cmds::create_44_cmd(
+ m_hw, m_rd->table_id(), m_inside.to_v4(), m_outside.to_v4()));
+ } else {
+ HW::enqueue(new nat_static_cmds::create_66_cmd(
+ m_hw, m_rd->table_id(), m_inside.to_v6(), m_outside.to_v6()));
+ }
+ }
+}
+
+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 nat_static_cmds::create_44_cmd(
+ m_hw, m_rd->table_id(), m_inside.to_v4(), m_outside.to_v4()));
+ } else {
+ HW::enqueue(new nat_static_cmds::create_66_cmd(
+ m_hw, m_rd->table_id(), m_inside.to_v6(), m_outside.to_v6()));
+ }
+ }
+}
+
+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(temp.key(), temp));
+}
+
+std::shared_ptr<nat_static>
+nat_static::find(const key_t& key)
+{
+ return (m_db.find(key));
+}
+
+std::shared_ptr<nat_static>
+nat_static::singular() const
+{
+ return find_or_add(*this);
+}
+
+void
+nat_static::dump(std::ostream& os)
+{
+ db_dump(m_db, 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::event_handler::handle_populate(const client_db::key_t& key)
+{
+ /*
+ * dump VPP current states
+ */
+ std::shared_ptr<nat_static_cmds::dump_44_cmd> cmd44 =
+ std::make_shared<nat_static_cmds::dump_44_cmd>();
+
+ HW::enqueue(cmd44);
+ HW::write();
+
+ for (auto& record : *cmd44) {
+
+ auto& payload = record.get_payload();
+
+ boost::asio::ip::address inside = from_bytes(0, payload.local_ip_address);
+ boost::asio::ip::address outside =
+ from_bytes(0, payload.external_ip_address);
+ nat_static n(route_domain(payload.vrf_id), inside, outside);
+
+ /*
+ * Write each of the discovered mappings into the OM,
+ * but disable the HW Command q whilst we do, so that no
+ * commands are sent to VPP
+ */
+ OM::commit(key, n);
+ }
+
+ std::shared_ptr<nat_static_cmds::dump_66_cmd> cmd66 =
+ std::make_shared<nat_static_cmds::dump_66_cmd>();
+
+ HW::enqueue(cmd66);
+ HW::write();
+
+ for (auto& record : *cmd66) {
+
+ auto& payload = record.get_payload();
+
+ boost::asio::ip::address inside = from_bytes(1, payload.local_ip_address);
+ boost::asio::ip::address outside =
+ from_bytes(1, payload.external_ip_address);
+ nat_static n(route_domain(payload.vrf_id), inside, outside);
+
+ /*
+ * Write each of the discovered mappings into the OM,
+ * but disable the HW Command q whilst we do, so that no
+ * commands are sent to VPP
+ */
+ OM::commit(key, n);
+ }
+}
+
+dependency_t
+nat_static::event_handler::order() const
+{
+ return (dependency_t::ENTRY);
+}
+
+void
+nat_static::event_handler::show(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/nat_static.hpp b/extras/vom/vom/nat_static.hpp
new file mode 100644
index 00000000000..2dcadb3c904
--- /dev/null
+++ b/extras/vom/vom/nat_static.hpp
@@ -0,0 +1,195 @@
+/*
+ * 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/route.hpp"
+#include "vom/singular_db.hpp"
+#include "vom/types.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& 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& outside);
+
+ /**
+ * Copy Construct
+ */
+ nat_static(const nat_static& r);
+
+ /**
+ * Destructor
+ */
+ ~nat_static();
+
+ /**
+ * Comparison operator - for UT
+ */
+ bool operator==(const nat_static& n) const;
+
+ /**
+ * Return the object's key
+ */
+ const key_t key() const;
+
+ /**
+ * 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 key_t& key);
+
+ /**
+ * 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;
+
+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
+ */
+ const boost::asio::ip::address 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/extras/vom/vom/nat_static_cmds.cpp b/extras/vom/vom/nat_static_cmds.cpp
new file mode 100644
index 00000000000..a80e47416ea
--- /dev/null
+++ b/extras/vom/vom/nat_static_cmds.cpp
@@ -0,0 +1,278 @@
+/*
+ * 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_cmds.hpp"
+
+DEFINE_VAPI_MSG_IDS_NAT_API_JSON;
+
+namespace VOM {
+namespace nat_static_cmds {
+
+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
+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
+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
+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());
+}
+
+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
+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
+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
+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());
+}
+
+bool
+dump_44_cmd::operator==(const dump_44_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+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
+dump_44_cmd::to_string() const
+{
+ return ("nat-44-static-dump");
+}
+
+create_66_cmd::create_66_cmd(HW::item<bool>& item,
+ route::table_id_t id,
+ const boost::asio::ip::address_v6& inside,
+ const boost::asio::ip::address_v6& outside)
+ : rpc_cmd(item)
+ , m_id(id)
+ , m_inside(inside)
+ , m_outside(outside)
+{
+}
+
+bool
+create_66_cmd::operator==(const create_66_cmd& other) const
+{
+ return ((m_id == other.m_id) && (m_inside == other.m_inside) &&
+ (m_outside == other.m_outside));
+}
+
+rc_t
+create_66_cmd::issue(connection& con)
+{
+ msg_t req(con.ctx(), std::ref(*this));
+
+ auto& payload = req.get_request().get_payload();
+ payload.is_add = 1;
+ payload.vrf_id = m_id;
+ 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
+create_66_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "nat-66-static-create: " << m_hw_item.to_string() << " table:" << m_id
+ << " inside:" << m_inside.to_string()
+ << " outside:" << m_outside.to_string();
+
+ return (s.str());
+}
+
+delete_66_cmd::delete_66_cmd(HW::item<bool>& item,
+ route::table_id_t id,
+ const boost::asio::ip::address_v6& inside,
+ const boost::asio::ip::address_v6& outside)
+ : rpc_cmd(item)
+ , m_id(id)
+ , m_inside(inside)
+ , m_outside(outside)
+{
+}
+
+bool
+delete_66_cmd::operator==(const delete_66_cmd& other) const
+{
+ return ((m_id == other.m_id) && (m_inside == other.m_inside) &&
+ (m_outside == other.m_outside));
+}
+
+rc_t
+delete_66_cmd::issue(connection& con)
+{
+ msg_t req(con.ctx(), std::ref(*this));
+
+ auto& payload = req.get_request().get_payload();
+ payload.is_add = 0;
+ payload.vrf_id = m_id;
+ 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
+delete_66_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "nat-66-static-delete: " << m_hw_item.to_string() << " table:" << m_id
+ << " inside:" << m_inside.to_string()
+ << " outside:" << m_outside.to_string();
+
+ return (s.str());
+}
+
+bool
+dump_66_cmd::operator==(const dump_66_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+dump_66_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
+dump_66_cmd::to_string() const
+{
+ return ("nat-static-dump");
+}
+
+}; // namespace nat_static_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/nat_static_cmds.hpp b/extras/vom/vom/nat_static_cmds.hpp
new file mode 100644
index 00000000000..95061cae1ad
--- /dev/null
+++ b/extras/vom/vom/nat_static_cmds.hpp
@@ -0,0 +1,248 @@
+/*
+ * 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_CMDS_H__
+#define __VOM_NAT_STATIC_CMDS_H__
+
+#include "nat_static.hpp"
+#include "vom/dump_cmd.hpp"
+
+#include <vapi/nat.api.vapi.hpp>
+
+namespace VOM {
+namespace nat_static_cmds {
+
+/**
+ * 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() = default;
+ ~dump_44_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;
+
+ /**
+ * Comparison operator - only used for UT
+ */
+ bool operator==(const dump_44_cmd& i) const;
+
+private:
+ /**
+ * HW reutrn code
+ */
+ HW::item<bool> item;
+};
+
+/**
+ * A command class that creates NAT 66 static mapping
+ */
+class create_66_cmd
+ : public rpc_cmd<HW::item<bool>, rc_t, vapi::Nat66_add_del_static_mapping>
+{
+public:
+ /**
+ * Constructor
+ */
+ create_66_cmd(HW::item<bool>& item,
+ route::table_id_t id,
+ const boost::asio::ip::address_v6& inside,
+ const boost::asio::ip::address_v6& 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_66_cmd& i) const;
+
+private:
+ route::table_id_t m_id;
+ const boost::asio::ip::address_v6 m_inside;
+ const boost::asio::ip::address_v6 m_outside;
+};
+
+/**
+ * A cmd class that deletes a NAT 66 static mapping
+ */
+class delete_66_cmd
+ : public rpc_cmd<HW::item<bool>, rc_t, vapi::Nat66_add_del_static_mapping>
+{
+public:
+ /**
+ * Constructor
+ */
+ delete_66_cmd(HW::item<bool>& item,
+ route::table_id_t id,
+ const boost::asio::ip::address_v6& inside,
+ const boost::asio::ip::address_v6& 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_66_cmd& i) const;
+
+private:
+ route::table_id_t m_id;
+ const boost::asio::ip::address_v6 m_inside;
+ const boost::asio::ip::address_v6 m_outside;
+};
+
+/**
+ * A cmd class that Dumps all the nat_statics
+ */
+class dump_66_cmd : public dump_cmd<vapi::Nat66_static_mapping_dump>
+{
+public:
+ /**
+ * Constructor
+ */
+ dump_66_cmd() = default;
+ ~dump_66_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;
+
+ /**
+ * Comparison operator - only used for UT
+ */
+ bool operator==(const dump_66_cmd& i) const;
+
+private:
+ /**
+ * HW reutrn code
+ */
+ HW::item<bool> item;
+};
+
+}; // namespace nat_static_cmds
+}; // namespace vom
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/neighbour.cpp b/extras/vom/vom/neighbour.cpp
new file mode 100644
index 00000000000..44e2760a1ec
--- /dev/null
+++ b/extras/vom/vom/neighbour.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/neighbour.hpp"
+#include "vom/neighbour_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+singular_db<neighbour::key_t, neighbour> neighbour::m_db;
+neighbour::event_handler neighbour::m_evh;
+
+neighbour::neighbour(const interface& itf,
+ const boost::asio::ip::address& ip_addr,
+ const mac_address_t& mac)
+ : m_hw(false)
+ , m_itf(itf.singular())
+ , m_ip_addr(ip_addr)
+ , m_mac(mac)
+{
+}
+
+neighbour::neighbour(const neighbour& bde)
+ : m_hw(bde.m_hw)
+ , m_itf(bde.m_itf)
+ , m_ip_addr(bde.m_ip_addr)
+ , m_mac(bde.m_mac)
+{
+}
+
+neighbour::~neighbour()
+{
+ sweep();
+
+ // not in the DB anymore.
+ m_db.release(key(), this);
+}
+
+bool
+neighbour::operator==(const neighbour& n) const
+{
+ return ((key() == n.key()) && (m_mac == n.m_mac));
+}
+
+const neighbour::key_t
+neighbour::key() const
+{
+ return (std::make_pair(m_itf->key(), m_ip_addr));
+}
+
+void
+neighbour::sweep()
+{
+ if (m_hw) {
+ HW::enqueue(
+ new neighbour_cmds::delete_cmd(m_hw, m_itf->handle(), m_mac, m_ip_addr));
+ }
+ HW::write();
+}
+
+void
+neighbour::replay()
+{
+ if (m_hw) {
+ HW::enqueue(
+ new neighbour_cmds::create_cmd(m_hw, m_itf->handle(), m_mac, m_ip_addr));
+ }
+}
+
+std::string
+neighbour::to_string() const
+{
+ std::ostringstream s;
+ s << "neighbour:[" << 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 neighbour_cmds::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(temp.key(), temp));
+}
+
+std::shared_ptr<neighbour>
+neighbour::find(const key_t& k)
+{
+ return (m_db.find(k));
+}
+
+std::shared_ptr<neighbour>
+neighbour::singular() const
+{
+ return find_or_add(*this);
+}
+
+void
+neighbour::dump(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+
+std::ostream&
+operator<<(std::ostream& os, const neighbour::key_t& key)
+{
+ os << "[" << key.first << ", " << key.second << "]";
+
+ 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_cmds::dump_cmd> cmd =
+ std::make_shared<neighbour_cmds::dump_cmd>(
+ neighbour_cmds::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, ip_addr, mac);
+
+ 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)
+{
+ db_dump(m_db, os);
+}
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/neighbour.hpp b/extras/vom/vom/neighbour.hpp
new file mode 100644
index 00000000000..500f03d0a61
--- /dev/null
+++ b/extras/vom/vom/neighbour.hpp
@@ -0,0 +1,193 @@
+/*
+ * 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/interface.hpp"
+#include "vom/singular_db.hpp"
+#include "vom/types.hpp"
+
+namespace VOM {
+/**
+ * A entry in the neighbour entry (ARP or IPv6 ND)
+ */
+class neighbour : public object_base
+{
+public:
+ /**
+ * The key for a neighbour entry;
+ * the interface and IP address
+ */
+ typedef std::pair<interface::key_t, boost::asio::ip::address> key_t;
+
+ /**
+ * Construct an ARP entry
+ */
+ neighbour(const interface& itf,
+ const boost::asio::ip::address& ip_addr,
+ const mac_address_t& mac);
+
+ /**
+ * Copy Construct
+ */
+ neighbour(const neighbour& r);
+
+ /**
+ * Destructor
+ */
+ ~neighbour();
+
+ /**
+ * Return the object's key
+ */
+ const key_t key() const;
+
+ /**
+ * Comparison operator
+ */
+ bool operator==(const neighbour& n) const;
+
+ /**
+ * Return the matching 'singular instance'
+ */
+ std::shared_ptr<neighbour> singular() const;
+
+ /**
+ * Find the neighbour fromits key
+ */
+ static std::shared_ptr<neighbour> find(const key_t& k);
+
+ /**
+ * Dump all neighbours 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;
+
+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 neighbour 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 IP address
+ */
+ boost::asio::ip::address m_ip_addr;
+
+ /**
+ * The mac to match
+ */
+ mac_address_t m_mac;
+
+ /**
+ * 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/extras/vom/vom/neighbour_cmds.cpp b/extras/vom/vom/neighbour_cmds.cpp
new file mode 100644
index 00000000000..2f3c200d5fb
--- /dev/null
+++ b/extras/vom/vom/neighbour_cmds.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/neighbour_cmds.hpp"
+
+namespace VOM {
+namespace neighbour_cmds {
+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
+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
+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
+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());
+}
+
+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
+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
+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
+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());
+}
+
+dump_cmd::dump_cmd(const handle_t& hdl, const l3_proto_t& proto)
+ : m_itf(hdl)
+ , m_proto(proto)
+{
+}
+
+dump_cmd::dump_cmd(const dump_cmd& d)
+ : m_itf(d.m_itf)
+ , m_proto(d.m_proto)
+{
+}
+
+bool
+dump_cmd::operator==(const dump_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+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
+dump_cmd::to_string() const
+{
+ return ("neighbour-dump");
+}
+} // namespace neighbour_cmds
+} // namespace vom
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/neighbour_cmds.hpp b/extras/vom/vom/neighbour_cmds.hpp
new file mode 100644
index 00000000000..fe6dd155cd9
--- /dev/null
+++ b/extras/vom/vom/neighbour_cmds.hpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_NEIGHBOUR_CMDS_H__
+#define __VOM_NEIGHBOUR_CMDS_H__
+
+#include "vom/dump_cmd.hpp"
+#include "neighbour.hpp"
+
+#include <vapi/ip.api.vapi.hpp>
+
+namespace VOM {
+namespace neighbour_cmds {
+
+/**
+ * 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;
+};
+
+}; // namespace neighbour_cmds
+}; // namespace vom
+#endif
+
diff --git a/extras/vom/vom/object_base.cpp b/extras/vom/vom/object_base.cpp
new file mode 100644
index 00000000000..6ab4ee5cadc
--- /dev/null
+++ b/extras/vom/vom/object_base.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/object_base.hpp"
+
+namespace VOM {
+object_ref::object_ref(std::shared_ptr<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/extras/vom/vom/object_base.hpp b/extras/vom/vom/object_base.hpp
new file mode 100644
index 00000000000..2edafc58466
--- /dev/null
+++ b/extras/vom/vom/object_base.hpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_OBJECT_H__
+#define __VOM_OBJECT_H__
+
+#include <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/extras/vom/vom/om.cpp b/extras/vom/vom/om.cpp
new file mode 100644
index 00000000000..edfc046e740
--- /dev/null
+++ b/extras/vom/vom/om.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+
+#include "vom/logger.hpp"
+#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()
+{
+ VOM_LOG(log_level_t::INFO) << "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)
+{
+ VOM_LOG(log_level_t::INFO) << "populate";
+
+ /*
+ * 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/extras/vom/vom/om.hpp b/extras/vom/vom/om.hpp
new file mode 100644
index 00000000000..e68d5ea0017
--- /dev/null
+++ b/extras/vom/vom/om.hpp
@@ -0,0 +1,356 @@
+/*
+ * 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 <algorithm>
+#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/extras/vom/vom/prefix.cpp b/extras/vom/vom/prefix.cpp
new file mode 100644
index 00000000000..abd589eef9f
--- /dev/null
+++ b/extras/vom/vom/prefix.cpp
@@ -0,0 +1,431 @@
+/*
+ * 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;
+}
+
+const nh_proto_t&
+l3_proto_t::to_nh_proto() const
+{
+ if (*this == IPV4)
+ return nh_proto_t::IPV4;
+ else if (*this == IPV6)
+ return nh_proto_t::IPV6;
+ else if (*this == MPLS)
+ return nh_proto_t::MPLS;
+
+ return nh_proto_t::IPV4;
+}
+
+std::ostream&
+operator<<(std::ostream& os, const l3_proto_t& l3p)
+{
+ os << l3p.to_string();
+ return os;
+}
+
+/*
+ * 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_v6
+operator|(const boost::asio::ip::address_v6& addr1,
+ const boost::asio::ip::address_v6& addr2)
+{
+ boost::asio::ip::address_v6::bytes_type b1 = addr1.to_bytes();
+ boost::asio::ip::address_v6::bytes_type b2 = addr2.to_bytes();
+
+ for (boost::asio::ip::address_v6::bytes_type::size_type ii = 0;
+ ii < b1.max_size(); ii++) {
+ b1[ii] |= b2[ii];
+ }
+
+ boost::asio::ip::address_v6 addr(b1);
+ return (addr);
+}
+
+boost::asio::ip::address_v6 operator&(const boost::asio::ip::address_v6& addr1,
+ const boost::asio::ip::address_v6& addr2)
+{
+ boost::asio::ip::address_v6::bytes_type b1 = addr1.to_bytes();
+ boost::asio::ip::address_v6::bytes_type b2 = addr2.to_bytes();
+
+ for (boost::asio::ip::address_v6::bytes_type::size_type ii = 0;
+ ii < b1.max_size(); ii++) {
+ b1[ii] &= b2[ii];
+ }
+
+ boost::asio::ip::address_v6 addr(b1);
+ return (addr);
+}
+
+boost::asio::ip::address_v6 operator~(const boost::asio::ip::address_v6& addr1)
+{
+ boost::asio::ip::address_v6::bytes_type b1 = addr1.to_bytes();
+
+ for (boost::asio::ip::address_v6::bytes_type::size_type ii = 0;
+ ii < b1.max_size(); ii++) {
+ b1[ii] = ~b1[ii];
+ }
+
+ boost::asio::ip::address_v6 addr(b1);
+ return (addr);
+}
+boost::asio::ip::address
+operator|(const boost::asio::ip::address& addr1,
+ const boost::asio::ip::address& addr2)
+{
+ if (addr1.is_v6())
+ return (addr1.to_v6() | addr2.to_v6());
+ else
+ return (addr1.to_v4() | addr2.to_v4());
+}
+
+boost::asio::ip::address operator&(const boost::asio::ip::address& addr1,
+ const boost::asio::ip::address& addr2)
+{
+ if (addr1.is_v6())
+ return (addr1.to_v6() & addr2.to_v6());
+ else
+ return (addr1.to_v4() & addr2.to_v4());
+}
+
+boost::asio::ip::address operator~(const boost::asio::ip::address& addr1)
+{
+ if (addr1.is_v6())
+ return ~(addr1.to_v6());
+ else
+ return ~(addr1.to_v4());
+}
+
+boost::asio::ip::address
+route::prefix_t::mask() const
+{
+ if (m_addr.is_v6()) {
+ boost::asio::ip::address_v6::bytes_type b =
+ boost::asio::ip::address_v6::any().to_bytes();
+
+ uint8_t n_bits = mask_width();
+
+ for (boost::asio::ip::address_v6::bytes_type::size_type ii = 0;
+ ii < b.max_size(); ii++) {
+ for (int8_t bit = 7; bit >= 0 && n_bits; bit--) {
+ b[ii] |= (1 << bit);
+ n_bits--;
+ }
+ if (!n_bits)
+ break;
+ }
+
+ return (boost::asio::ip::address_v6(b));
+ } else {
+ uint32_t a;
+
+ a = ~((1 << (32 - mask_width())) - 1);
+
+ return (boost::asio::ip::address_v4(a));
+ }
+}
+
+route::prefix_t
+route::prefix_t::low() const
+{
+ prefix_t pfx(*this);
+
+ pfx.m_addr = pfx.m_addr & pfx.mask();
+
+ return (pfx);
+}
+
+route::prefix_t
+route::prefix_t::high() const
+{
+ prefix_t pfx(*this);
+
+ pfx.m_addr = pfx.m_addr | ~pfx.mask();
+
+ return (pfx);
+}
+
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/prefix.hpp b/extras/vom/vom/prefix.hpp
new file mode 100644
index 00000000000..3950f6fd036
--- /dev/null
+++ b/extras/vom/vom/prefix.hpp
@@ -0,0 +1,267 @@
+/*
+ * 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
+ */
+
+/**
+ * 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);
+};
+
+/**
+ * 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);
+
+ const nh_proto_t& to_nh_proto() const;
+
+private:
+ /**
+ * Private constructor taking the value and the string name
+ */
+ l3_proto_t(int v, const std::string& s);
+};
+
+/**
+ * Ostream output for l3_proto_t
+ */
+std::ostream& operator<<(std::ostream& os, const l3_proto_t& l3p);
+
+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 mask() const;
+
+ /**
+ * get the lowest address in the prefix
+ */
+ prefix_t low() const;
+
+ /**
+ * Get the highest address in the prefix
+ */
+ prefix_t 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);
+
+boost::asio::ip::address_v6 operator|(const boost::asio::ip::address_v6& addr1,
+ const boost::asio::ip::address_v6& addr2);
+
+boost::asio::ip::address_v6 operator&(const boost::asio::ip::address_v6& addr1,
+ const boost::asio::ip::address_v6& addr2);
+
+boost::asio::ip::address_v6 operator~(const boost::asio::ip::address_v6& addr1);
+
+boost::asio::ip::address operator|(const boost::asio::ip::address& addr1,
+ const boost::asio::ip::address& addr2);
+
+boost::asio::ip::address operator&(const boost::asio::ip::address& addr1,
+ const boost::asio::ip::address& addr2);
+
+boost::asio::ip::address operator~(const boost::asio::ip::address& 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/extras/vom/vom/ra_config.cpp b/extras/vom/vom/ra_config.cpp
new file mode 100644
index 00000000000..de424a4d436
--- /dev/null
+++ b/extras/vom/vom/ra_config.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <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/extras/vom/vom/ra_config.hpp b/extras/vom/vom/ra_config.hpp
new file mode 100644
index 00000000000..505ccb3c499
--- /dev/null
+++ b/extras/vom/vom/ra_config.hpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_RA_CONFIG_H__
+#define __VOM_RA_CONFIG_H__
+
+#include <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/extras/vom/vom/ra_prefix.cpp b/extras/vom/vom/ra_prefix.cpp
new file mode 100644
index 00000000000..1cf09637082
--- /dev/null
+++ b/extras/vom/vom/ra_prefix.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <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/extras/vom/vom/ra_prefix.hpp b/extras/vom/vom/ra_prefix.hpp
new file mode 100644
index 00000000000..580aae146fd
--- /dev/null
+++ b/extras/vom/vom/ra_prefix.hpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_RA_PREFIX_H__
+#define __VOM_RA_PREFIX_H__
+
+#include "vom/prefix.hpp"
+
+#include <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/extras/vom/vom/route.cpp b/extras/vom/vom/route.cpp
new file mode 100644
index 00000000000..ec56c44a0d7
--- /dev/null
+++ b/extras/vom/vom/route.cpp
@@ -0,0 +1,547 @@
+/*
+ * 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/route_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+namespace route {
+ip_route::event_handler ip_route::m_evh;
+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)
+{
+}
+
+const path::flags_t path::flags_t::NONE(0, "none");
+const path::flags_t path::flags_t::DVR((1 << 0), "dvr");
+
+path::flags_t::flags_t(int v, const std::string& s)
+ : enum_base<path::flags_t>(v, s)
+{
+}
+
+path::path(special_t special)
+ : m_type(special)
+ , m_nh_proto(nh_proto_t::IPV4)
+ , m_flags(flags_t::NONE)
+ , 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_flags(flags_t::NONE)
+ , 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_flags(flags_t::NONE)
+ , 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,
+ const flags_t& flags,
+ uint8_t weight,
+ uint8_t preference)
+ : m_type(special_t::STANDARD)
+ , m_nh_proto(proto)
+ , m_flags(flags)
+ , 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_flags(p.m_flags)
+ , 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_nh_proto < p.m_nh_proto)
+ return true;
+ if (m_flags < p.m_flags)
+ return true;
+ if (m_type < p.m_type)
+ return true;
+ if (m_rd && !p.m_rd)
+ return false;
+ if (!m_rd && p.m_rd)
+ 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 && !p.m_interface)
+ return false;
+ if (!m_interface && p.m_interface)
+ return true;
+ if (m_interface->handle() < p.m_interface->handle())
+ return true;
+
+ return (false);
+}
+
+path::~path()
+{
+}
+
+bool
+path::operator==(const path& p) const
+{
+ bool result = true;
+ if (m_rd && !p.m_rd)
+ return false;
+ if (!m_rd && p.m_rd)
+ return false;
+ if (m_rd && p.m_rd)
+ result &= (*m_rd == *p.m_rd);
+ if (m_interface && !p.m_interface)
+ return false;
+ if (!m_interface && p.m_interface)
+ return false;
+ if (m_interface && p.m_interface)
+ result &= (*m_interface == *p.m_interface);
+ return (result && (m_type == p.m_type) && (m_nh == p.m_nh) &&
+ (m_nh_proto == p.m_nh_proto) && (m_flags == p.m_flags));
+}
+
+std::string
+path::to_string() const
+{
+ std::ostringstream s;
+
+ s << "path:["
+ << "type:" << m_type.to_string() << " proto:" << m_nh_proto.to_string()
+ << " flags:" << m_flags.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());
+}
+
+path::special_t
+path::type() const
+{
+ return m_type;
+}
+
+nh_proto_t
+path::nh_proto() const
+{
+ return m_nh_proto;
+}
+
+path::flags_t
+path::flags() const
+{
+ return m_flags;
+}
+
+const boost::asio::ip::address&
+path::nh() const
+{
+ return m_nh;
+}
+
+std::shared_ptr<route_domain>
+path::rd() const
+{
+ return m_rd;
+}
+
+std::shared_ptr<interface>
+path::itf() const
+{
+ return m_interface;
+}
+
+uint8_t
+path::weight() const
+{
+ return m_weight;
+}
+
+uint8_t
+path::preference() const
+{
+ return m_preference;
+}
+
+ip_route::ip_route(const prefix_t& prefix, const path& p)
+ : m_hw(false)
+ , m_rd(route_domain::get_default())
+ , m_prefix(prefix)
+ , m_paths({ p })
+{
+}
+
+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(const route_domain& rd,
+ const prefix_t& prefix,
+ const path& p)
+ : m_hw(false)
+ , m_rd(rd.singular())
+ , m_prefix(prefix)
+ , m_paths({ p })
+{
+}
+
+ip_route::~ip_route()
+{
+ sweep();
+
+ // not in the DB anymore.
+ m_db.release(key(), this);
+ m_paths.clear();
+}
+
+const ip_route::key_t
+ip_route::key() const
+{
+ return (std::make_pair(m_rd->table_id(), m_prefix));
+}
+
+bool
+ip_route::operator==(const ip_route& i) const
+{
+ return ((key() == i.key()) && (m_paths == i.m_paths));
+}
+
+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 ip_route_cmds::delete_cmd(m_hw, m_rd->table_id(), m_prefix));
+ }
+ HW::write();
+}
+
+void
+ip_route::replay()
+{
+ if (m_hw) {
+ HW::enqueue(
+ new ip_route_cmds::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 ip_route_cmds::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(temp.key(), temp));
+}
+
+std::shared_ptr<ip_route>
+ip_route::find(const key_t& k)
+{
+ return (m_db.find(k));
+}
+
+std::shared_ptr<ip_route>
+ip_route::singular() const
+{
+ return find_or_add(*this);
+}
+
+void
+ip_route::dump(std::ostream& os)
+{
+ db_dump(m_db, 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_cmds::dump_v4_cmd> cmd_v4 =
+ std::make_shared<ip_route_cmds::dump_v4_cmd>();
+ std::shared_ptr<ip_route_cmds::dump_v6_cmd> cmd_v6 =
+ std::make_shared<ip_route_cmds::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(payload.table_id);
+ 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 {
+ boost::asio::ip::address address = from_bytes(0, p.next_hop);
+ std::shared_ptr<interface> itf = interface::find(p.sw_if_index);
+ if (itf) {
+ if (p.is_dvr) {
+ path path_v4(*itf, nh_proto_t::IPV4, route::path::flags_t::DVR,
+ p.weight, p.preference);
+ ip_r.add(path_v4);
+ } else {
+ path path_v4(address, *itf, p.weight, p.preference);
+ ip_r.add(path_v4);
+ }
+ } else {
+ path path_v4(rd_temp, address, 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(payload.table_id);
+ 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);
+ if (itf) {
+ if (p.is_dvr) {
+ path path_v6(*itf, nh_proto_t::IPV6, route::path::flags_t::DVR,
+ p.weight, p.preference);
+ ip_r.add(path_v6);
+ } else {
+ path path_v6(address, *itf, p.weight, p.preference);
+ ip_r.add(path_v6);
+ }
+ } else {
+ path path_v6(rd_temp, address, 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::TABLE);
+}
+
+void
+ip_route::event_handler::show(std::ostream& os)
+{
+ db_dump(m_db, 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/extras/vom/vom/route.hpp b/extras/vom/vom/route.hpp
new file mode 100644
index 00000000000..65797b7c2f9
--- /dev/null
+++ b/extras/vom/vom/route.hpp
@@ -0,0 +1,407 @@
+/*
+ * 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"
+
+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);
+ };
+
+ /**
+ * Path flags
+ */
+ class flags_t : public enum_base<flags_t>
+ {
+ public:
+ /**
+ * No flags
+ */
+ const static flags_t NONE;
+
+ /**
+ * A path that resolves via a DVR next-hop
+ */
+ const static flags_t DVR;
+
+ private:
+ /**
+ * Private constructor taking the value and the string name
+ */
+ flags_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,
+ const flags_t& flags = flags_t::NONE,
+ uint8_t weight = 1,
+ uint8_t preference = 0);
+
+ /**
+ * Copy Constructor
+ */
+ path(const path& p);
+
+ /**
+ * Destructor
+ */
+ ~path();
+
+ /**
+ * comparison operator
+ */
+ bool operator==(const path& p) 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;
+
+ /**
+ * Getters
+ */
+ special_t type() const;
+ nh_proto_t nh_proto() const;
+ flags_t flags() const;
+ const boost::asio::ip::address& nh() const;
+ std::shared_ptr<route_domain> rd() const;
+ std::shared_ptr<interface> itf() const;
+ uint8_t weight() const;
+ uint8_t preference() const;
+
+private:
+ /**
+ * The special path tpye
+ */
+ special_t m_type;
+
+ /**
+ * The next-hop protocol
+ */
+ nh_proto_t m_nh_proto;
+
+ /**
+ * Flags for the path
+ */
+ flags_t m_flags;
+
+ /**
+ * 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);
+
+ /**
+ * Construct a route with a path
+ */
+ ip_route(const prefix_t& prefix, const path& p);
+
+ /**
+ * 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);
+
+ /**
+ * Construct a route in the given route domain with a path
+ */
+ ip_route(const route_domain& rd, const prefix_t& prefix, const path& p);
+
+ /**
+ * Destructor
+ */
+ ~ip_route();
+
+ /**
+ * Get the route's key
+ */
+ const key_t key() const;
+
+ /**
+ * Comparison operator
+ */
+ bool operator==(const ip_route& i) const;
+
+ /**
+ * 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;
+
+ /**
+ * Return the matching 'singular instance'
+ */
+ static std::shared_ptr<ip_route> find(const key_t& k);
+
+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/extras/vom/vom/route_cmds.cpp b/extras/vom/vom/route_cmds.cpp
new file mode 100644
index 00000000000..83d44ccc01d
--- /dev/null
+++ b/extras/vom/vom/route_cmds.cpp
@@ -0,0 +1,227 @@
+/*
+ * 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_cmds.hpp"
+
+namespace VOM {
+namespace route {
+namespace ip_route_cmds {
+
+static void
+to_vpp(const route::path& p, vapi_payload_ip_add_del_route& payload)
+{
+ 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 (route::path::flags_t::DVR & p.flags()) {
+ payload.is_dvr = 1;
+ }
+
+ if (route::path::special_t::STANDARD == p.type()) {
+ uint8_t path_v6;
+ to_bytes(p.nh(), &path_v6, payload.next_hop_address);
+
+ if (p.rd()) {
+ payload.next_hop_table_id = p.rd()->table_id();
+ }
+ if (p.itf()) {
+ payload.next_hop_sw_if_index = p.itf()->handle().value();
+ }
+ } else if (route::path::special_t::DROP == p.type()) {
+ payload.is_drop = 1;
+ } else if (route::path::special_t::UNREACH == p.type()) {
+ payload.is_unreach = 1;
+ } else if (route::path::special_t::PROHIBIT == p.type()) {
+ payload.is_prohibit = 1;
+ } else if (route::path::special_t::LOCAL == p.type()) {
+ payload.is_local = 1;
+ }
+ payload.next_hop_weight = p.weight();
+ payload.next_hop_preference = p.preference();
+ payload.next_hop_via_label = 0;
+ payload.classify_table_index = 0;
+}
+
+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
+update_cmd::operator==(const update_cmd& other) const
+{
+ return ((m_prefix == other.m_prefix) && (m_id == other.m_id));
+}
+
+rc_t
+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)
+ to_vpp(p, payload);
+
+ VAPI_CALL(req.execute());
+
+ m_hw_item.set(wait());
+
+ return rc_t::OK;
+}
+
+std::string
+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());
+}
+
+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
+delete_cmd::operator==(const delete_cmd& other) const
+{
+ return ((m_prefix == other.m_prefix) && (m_id == other.m_id));
+}
+
+rc_t
+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
+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());
+}
+
+dump_v4_cmd::dump_v4_cmd()
+{
+}
+
+bool
+dump_v4_cmd::operator==(const dump_v4_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+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
+dump_v4_cmd::to_string() const
+{
+ return ("ip-route-v4-dump");
+}
+
+dump_v6_cmd::dump_v6_cmd()
+{
+}
+
+bool
+dump_v6_cmd::operator==(const dump_v6_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+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
+dump_v6_cmd::to_string() const
+{
+ return ("ip-route-v6-dump");
+}
+} // namespace ip_route_cmds
+} // namespace route
+} // namespace vom
+ /*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/route_cmds.hpp b/extras/vom/vom/route_cmds.hpp
new file mode 100644
index 00000000000..f2c10ff60ad
--- /dev/null
+++ b/extras/vom/vom/route_cmds.hpp
@@ -0,0 +1,173 @@
+/*
+ * 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_CMDS_H__
+#define __VOM_ROUTE_CMDS_H__
+
+#include "vom/dump_cmd.hpp"
+#include "vom/route.hpp"
+#include "vom/rpc_cmd.hpp"
+
+#include <vapi/ip.api.vapi.hpp>
+
+namespace VOM {
+namespace route {
+namespace ip_route_cmds {
+
+/**
+ * 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;
+};
+
+}; // namespace ip_route_cmds
+}; // namespace route
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/route_domain.cpp b/extras/vom/vom/route_domain.cpp
new file mode 100644
index 00000000000..b97faf6ae49
--- /dev/null
+++ b/extras/vom/vom/route_domain.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/route_domain.hpp"
+#include "vom/cmd.hpp"
+#include "vom/route_domain_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+
+route_domain::event_handler route_domain::m_evh;
+
+/**
+ * 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)
+{
+}
+
+bool
+route_domain::operator==(const route_domain& r) const
+{
+ return (m_table_id == r.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 route_domain_cmds::delete_cmd(m_hw_v4, l3_proto_t::IPV4, m_table_id));
+ }
+ if (m_hw_v6) {
+ HW::enqueue(
+ new route_domain_cmds::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 route_domain_cmds::create_cmd(m_hw_v4, l3_proto_t::IPV4, m_table_id));
+ }
+ if (m_hw_v6) {
+ HW::enqueue(
+ new route_domain_cmds::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.to_string()
+ << " v6:" << m_hw_v6.to_string() << "]";
+
+ return (s.str());
+}
+
+std::shared_ptr<route_domain>
+route_domain::find(const key_t& k)
+{
+ return (m_db.find(k));
+}
+
+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 route_domain_cmds::create_cmd(m_hw_v4, l3_proto_t::IPV4, m_table_id));
+ }
+ if (rc_t::OK != m_hw_v6.rc()) {
+ HW::enqueue(
+ new route_domain_cmds::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)
+{
+ db_dump(m_db, os);
+}
+
+void
+route_domain::event_handler::handle_populate(const client_db::key_t& key)
+{
+}
+
+route_domain::event_handler::event_handler()
+{
+ OM::register_listener(this);
+ inspect::register_handler({ "rd", "route-domain" }, "Route Domains", this);
+}
+
+void
+route_domain::event_handler::handle_replay()
+{
+ m_db.replay();
+}
+
+dependency_t
+route_domain::event_handler::order() const
+{
+ return (dependency_t::TABLE);
+}
+
+void
+route_domain::event_handler::show(std::ostream& os)
+{
+ db_dump(m_db, os);
+}
+
+}; // namespace VOPM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/route_domain.hpp b/extras/vom/vom/route_domain.hpp
new file mode 100644
index 00000000000..19a3c18dddc
--- /dev/null
+++ b/extras/vom/vom/route_domain.hpp
@@ -0,0 +1,193 @@
+/*
+ * 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/inspect.hpp"
+#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();
+
+ /**
+ * comparison operator - for UT
+ */
+ bool operator==(const route_domain& r) const;
+
+ /**
+ * 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 key_t& 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);
+
+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 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;
+};
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/route_domain_cmds.cpp b/extras/vom/vom/route_domain_cmds.cpp
new file mode 100644
index 00000000000..845497f5989
--- /dev/null
+++ b/extras/vom/vom/route_domain_cmds.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/route_domain_cmds.hpp"
+
+namespace VOM {
+namespace route_domain_cmds {
+
+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
+create_cmd::operator==(const create_cmd& other) const
+{
+ return (m_id == other.m_id);
+}
+
+rc_t
+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
+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());
+}
+
+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
+delete_cmd::operator==(const delete_cmd& other) const
+{
+ return (m_id == other.m_id);
+}
+
+rc_t
+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
+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());
+}
+} // namespace route_domain_cmds
+} // namespace VOM
+ /*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/route_domain_cmds.hpp b/extras/vom/vom/route_domain_cmds.hpp
new file mode 100644
index 00000000000..f3911bd59f0
--- /dev/null
+++ b/extras/vom/vom/route_domain_cmds.hpp
@@ -0,0 +1,114 @@
+/*
+ * 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_CMDS_H__
+#define __VOM_ROUTE_DOMAIN_CMDS_H__
+
+#include "vom/route_domain.hpp"
+#include "vom/rpc_cmd.hpp"
+
+#include <vapi/ip.api.vapi.hpp>
+
+namespace VOM {
+namespace route_domain_cmds {
+
+/**
+ * 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;
+};
+
+}; // namespace route_domain_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/rpc_cmd.hpp b/extras/vom/vom/rpc_cmd.hpp
new file mode 100644
index 00000000000..84b6717c53b
--- /dev/null
+++ b/extras/vom/vom/rpc_cmd.hpp
@@ -0,0 +1,152 @@
+/*
+ * 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);
+
+ /*
+ * we reset the promise after setting the value to reuse it
+ * when we run the retire command from the same cmd object
+ */
+ // m_promise = std::promise<DATA>();
+ }
+
+ /**
+ * 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);
+ VOM_LOG(log_level_t::DEBUG) << to_string();
+ }
+
+ /**
+ * 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/extras/vom/vom/singular_db.hpp b/extras/vom/vom/singular_db.hpp
new file mode 100644
index 00000000000..afca9a3509e
--- /dev/null
+++ b/extras/vom/vom/singular_db.hpp
@@ -0,0 +1,148 @@
+/*
+ * 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 <map>
+#include <memory>
+#include <ostream>
+
+#include "vom/logger.hpp"
+
+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 begin() const { return m_map.cbegin(); }
+
+ /**
+ * Get iterator to the beginning of the DB
+ */
+ const_iterator end() const { 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; }
+
+ /**
+ * 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<const 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/extras/vom/vom/singular_db_funcs.hpp b/extras/vom/vom/singular_db_funcs.hpp
new file mode 100644
index 00000000000..dddc6e4c229
--- /dev/null
+++ b/extras/vom/vom/singular_db_funcs.hpp
@@ -0,0 +1,52 @@
+/*
+ * 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_FUNCS_H__
+#define __VOM_INST_DB_FUNCS_H__
+
+#include <ostream>
+
+#include "singular_db.hpp"
+
+/**
+ * A set of helper function to iterate over objects in the DB.
+ * These functions are delcared not as DB member functions so that
+ * the template instatiation of the DB does not require the definitions
+ * of the functions used to be declared.
+ */
+namespace VOM {
+/**
+ * Print each of the objects in the DB into the stream provided
+ */
+template <typename DB>
+void
+db_dump(const DB& db, std::ostream& os)
+{
+ for (const auto entry : db) {
+ os << "key: " << entry.first << std::endl;
+ os << " " << entry.second.lock()->to_string() << std::endl;
+ }
+}
+};
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/sub_interface.cpp b/extras/vom/vom/sub_interface.cpp
new file mode 100644
index 00000000000..42f75791968
--- /dev/null
+++ b/extras/vom/vom/sub_interface.cpp
@@ -0,0 +1,114 @@
+/*
+ * 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/sub_interface_cmds.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)
+{
+}
+
+/**
+ * Construct a new object matching the desried state
+ */
+sub_interface::sub_interface(const interface& parent,
+ admin_state_t state,
+ const route_domain& rd,
+ vlan_id_t vlan)
+ : interface(mk_name(parent, vlan), parent.type(), state, rd)
+ , 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)
+{
+}
+
+bool
+sub_interface::operator==(const sub_interface& s) const
+{
+ return (interface::operator==(s) && (m_parent->key() == s.m_parent->key()) &&
+ (m_vlan == s.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 sub_interface_cmds::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 sub_interface_cmds::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(key(), *this);
+}
+
+std::shared_ptr<sub_interface>
+sub_interface::find(const key_t& k)
+{
+ return std::dynamic_pointer_cast<sub_interface>(m_db.find(k));
+}
+
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/sub_interface.hpp b/extras/vom/vom/sub_interface.hpp
new file mode 100644
index 00000000000..506133c900d
--- /dev/null
+++ b/extras/vom/vom/sub_interface.hpp
@@ -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.
+ */
+
+#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);
+
+ sub_interface(const interface& parent,
+ admin_state_t state,
+ const route_domain& rd,
+ vlan_id_t vlan);
+
+ /**
+ * Destructor
+ */
+ ~sub_interface();
+ /**
+ * Copy Constructor
+ */
+ sub_interface(const sub_interface& o);
+
+ /**
+ * comparison operator - for UT
+ */
+ bool operator==(const sub_interface& s) const;
+
+ /**
+ * Return the matching 'singular instance' of the sub-interface
+ */
+ std::shared_ptr<sub_interface> singular() const;
+
+ /**
+ * Find a subinterface from its key
+ */
+ static std::shared_ptr<sub_interface> find(const key_t& k);
+
+private:
+ /**
+ * 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/extras/vom/vom/sub_interface_cmds.cpp b/extras/vom/vom/sub_interface_cmds.cpp
new file mode 100644
index 00000000000..47af69268d1
--- /dev/null
+++ b/extras/vom/vom/sub_interface_cmds.cpp
@@ -0,0 +1,115 @@
+/*
+ * 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_cmds.hpp"
+#include "vom/cmd.hpp"
+
+#include <vapi/vpe.api.vapi.hpp>
+
+namespace VOM {
+namespace sub_interface_cmds {
+
+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
+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
+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) {
+ insert_interface();
+ }
+
+ return rc_t::OK;
+}
+
+std::string
+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());
+}
+
+delete_cmd::delete_cmd(HW::item<handle_t>& item)
+ : interface::delete_cmd<vapi::Delete_subif>(item)
+{
+}
+
+bool
+delete_cmd::operator==(const delete_cmd& other) const
+{
+ return (m_hw_item == other.m_hw_item);
+}
+
+rc_t
+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);
+
+ remove_interface();
+ return (rc_t::OK);
+}
+
+std::string
+delete_cmd::to_string() const
+{
+ std::ostringstream s;
+
+ s << "sub-itf-delete: " << m_hw_item.to_string();
+
+ return (s.str());
+}
+} // namespace sub_interface_cmds
+} // namespace VOM
+ /*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/sub_interface_cmds.hpp b/extras/vom/vom/sub_interface_cmds.hpp
new file mode 100644
index 00000000000..a21323200bb
--- /dev/null
+++ b/extras/vom/vom/sub_interface_cmds.hpp
@@ -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.
+ */
+
+#ifndef __VOM_SUB_INTERFACE_CMDS_H__
+#define __VOM_SUB_INTERFACE_CMDS_H__
+
+#include "vom/dump_cmd.hpp"
+#include "vom/rpc_cmd.hpp"
+#include "vom/sub_interface.hpp"
+
+#include <vapi/interface.api.vapi.hpp>
+
+namespace VOM {
+namespace sub_interface_cmds {
+
+/**
+ * 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;
+};
+
+}; // namespace sub_interface_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/tap_interface.cpp b/extras/vom/vom/tap_interface.cpp
new file mode 100644
index 00000000000..d7f16f5c132
--- /dev/null
+++ b/extras/vom/vom/tap_interface.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/tap_interface.hpp"
+#include "vom/tap_interface_cmds.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()
+{
+ 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 tap_interface_cmds::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 tap_interface_cmds::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_cmds::dump_cmd> cmd =
+ std::make_shared<tap_interface_cmds::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()
+{
+ // It will be replayed by interface handler
+}
+
+dependency_t
+tap_interface::event_handler::order() const
+{
+ return (dependency_t::INTERFACE);
+}
+
+void
+tap_interface::event_handler::show(std::ostream& os)
+{
+ // dumped by the interface handler
+}
+
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/tap_interface.hpp b/extras/vom/vom/tap_interface.hpp
new file mode 100644
index 00000000000..d9df9a92a8d
--- /dev/null
+++ b/extras/vom/vom/tap_interface.hpp
@@ -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.
+ */
+
+#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;
+
+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;
+
+ /**
+ * 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/extras/vom/vom/tap_interface_cmds.cpp b/extras/vom/vom/tap_interface_cmds.cpp
new file mode 100644
index 00000000000..b0885607742
--- /dev/null
+++ b/extras/vom/vom/tap_interface_cmds.cpp
@@ -0,0 +1,132 @@
+/*
+ * 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_cmds.hpp"
+
+#include <vapi/tap.api.vapi.hpp>
+
+namespace VOM {
+namespace tap_interface_cmds {
+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
+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
+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());
+}
+
+delete_cmd::delete_cmd(HW::item<handle_t>& item)
+ : interface::delete_cmd<vapi::Tap_delete>(item)
+{
+}
+
+rc_t
+delete_cmd::issue(connection& con)
+{
+ // finally... call VPP
+
+ return rc_t::OK;
+}
+std::string
+delete_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "tap-itf-delete: " << m_hw_item.to_string();
+
+ return (s.str());
+}
+
+dump_cmd::dump_cmd()
+{
+}
+
+bool
+dump_cmd::operator==(const dump_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+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::OK;
+}
+
+std::string
+dump_cmd::to_string() const
+{
+ return ("tap-itf-dump");
+}
+} // namespace tap_interface_cmds
+} // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/tap_interface_cmds.hpp b/extras/vom/vom/tap_interface_cmds.hpp
new file mode 100644
index 00000000000..1c1a3468c9b
--- /dev/null
+++ b/extras/vom/vom/tap_interface_cmds.hpp
@@ -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.
+ */
+
+#ifndef __VOM_TAP_INTERFACE_CMDS_H__
+#define __VOM_TAP_INTERFACE_CMDS_H__
+
+#include "vom/interface.hpp"
+#include "vom/tap_interface.hpp"
+#include "vom/dump_cmd.hpp"
+#include "vom/rpc_cmd.hpp"
+
+#include <vapi/interface.api.vapi.hpp>
+#include <vapi/tap.api.vapi.hpp>
+
+namespace VOM {
+namespace tap_interface_cmds {
+
+/**
+ * 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;
+};
+
+/**
+ * A functor class that deletes a Tap interface
+ */
+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;
+};
+
+}; // namespace tap_interface_cmds
+}; // namespace VOM
+
+#endif
diff --git a/extras/vom/vom/types.cpp b/extras/vom/vom/types.cpp
new file mode 100644
index 00000000000..c6093ebd15d
--- /dev/null
+++ b/extras/vom/vom/types.cpp
@@ -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.
+ */
+
+#include <algorithm>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+
+#include <boost/algorithm/string.hpp>
+
+#include "vom/types.hpp"
+
+namespace VOM {
+
+rc_t::rc_t(int v, const std::string s)
+ : enum_base<rc_t>(v, s)
+{
+}
+
+const rc_t&
+rc_t::from_vpp_retval(int32_t rv)
+{
+ if (0 == rv) {
+ return (rc_t::OK);
+ }
+ if (-68 == rv) {
+ // sub interface already exists
+ return (rc_t::OK);
+ }
+ if (-79 == rv) {
+ // interface already 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::INVALID(3, "invalid");
+const rc_t rc_t::TIMEOUT(4, "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);
+}
+
+void
+handle_t::reset()
+{
+ m_value = ~0;
+}
+
+std::ostream&
+operator<<(std::ostream& os, const handle_t& h)
+{
+ os << h.value();
+
+ return (os);
+}
+
+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));
+}
+
+mac_address_t::mac_address_t(const std::string& str)
+{
+ std::vector<std::string> parts;
+
+ boost::split(parts, str, boost::is_any_of(":"));
+
+ size_t n_bytes = std::min(bytes.size(), parts.size());
+
+ for (uint32_t ii = 0; ii < n_bytes; ii++) {
+ bytes[ii] = std::stoul(parts[ii], nullptr, 16);
+ }
+}
+
+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];
+ }
+}
+
+std::string
+mac_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
+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)
+{
+}
+std::ostream&
+operator<<(std::ostream& os, const direction_t& dir)
+{
+ os << dir.to_string();
+ return os;
+}
+
+const ethertype_t ethertype_t::ARP(0x0806, "arp");
+const ethertype_t ethertype_t::FCOE(0x8906, "fcoe");
+const ethertype_t ethertype_t::IPV4(0x0800, "ipv4");
+const ethertype_t ethertype_t::IPV6(0x86DD, "ipv6");
+const ethertype_t ethertype_t::MAC_SECURITY(0x88E5, "mac-security");
+const ethertype_t ethertype_t::MPLS_UNICAST(0x8847, "mpls-unicast");
+const ethertype_t ethertype_t::TRILL(0x22F3, "trill");
+const ethertype_t ethertype_t::UNSPECIFIED(0x0, "unspecified");
+
+ethertype_t::ethertype_t(int v, const std::string s)
+ : enum_base(v, s)
+{
+}
+
+std::ostream&
+operator<<(std::ostream& os, const ethertype_t& ether)
+{
+ os << ether.to_string();
+ return os;
+}
+
+const ethertype_t&
+ethertype_t::from_numeric_val(uint16_t numeric)
+{
+ if (0x0806 == numeric) {
+ return (ethertype_t::ARP);
+ }
+ if (0x8906 == numeric) {
+ return (ethertype_t::FCOE);
+ }
+ if (0x0800 == numeric) {
+ return (ethertype_t::IPV4);
+ }
+ if (0x86DD == numeric) {
+ return (ethertype_t::IPV6);
+ }
+ if (0x88E5 == numeric) {
+ return (ethertype_t::MAC_SECURITY);
+ }
+ if (0x8847 == numeric) {
+ return (ethertype_t::MPLS_UNICAST);
+ }
+ if (0x22F3 == numeric) {
+ return (ethertype_t::TRILL);
+ }
+
+ return (ethertype_t::UNSPECIFIED);
+}
+
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/types.hpp b/extras/vom/vom/types.hpp
new file mode 100644
index 00000000000..53654c51ac5
--- /dev/null
+++ b/extras/vom/vom/types.hpp
@@ -0,0 +1,408 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_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,
+
+ /**
+ * bond group binding is after interfaces but before
+ * anything else
+ */
+ BOND_BINDING,
+
+ /**
+ * 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() = default;
+
+ /**
+ * 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 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;
+};
+
+/**
+ * Output ostream for direction_t
+ */
+std::ostream& operator<<(std::ostream& os, const direction_t& dir);
+
+/**
+ * Feature Ethertype
+ */
+struct ethertype_t : public enum_base<ethertype_t>
+{
+ /**
+ * Constructor
+ */
+ ethertype_t(int v, const std::string s);
+
+ /**
+ * Destructor
+ */
+ ~ethertype_t() = default;
+
+ /**
+ * Ethertype Arp
+ */
+ const static ethertype_t ARP;
+
+ /**
+ * Ethertype FCoE
+ */
+ const static ethertype_t FCOE;
+
+ /**
+ * Ethertype IPv4
+ */
+ const static ethertype_t IPV4;
+
+ /**
+ * Ethertype Ipv6
+ */
+ const static ethertype_t IPV6;
+
+ /**
+ * Ethertype MAC Security
+ */
+ const static ethertype_t MAC_SECURITY;
+
+ /**
+ * Ethertype MPLS unicast
+ */
+ const static ethertype_t MPLS_UNICAST;
+
+ /**
+ * Ethertype TRILL
+ */
+ const static ethertype_t TRILL;
+
+ /**
+ * Ethertype Unspecified
+ */
+ const static ethertype_t UNSPECIFIED;
+
+ /**
+ * Get the ethertype from the numeric value
+ */
+ static const ethertype_t& from_numeric_val(uint16_t numeric);
+};
+
+/**
+ * Output ostream for ethertype_t
+ */
+std::ostream& operator<<(std::ostream& os, const ethertype_t& eth);
+
+/**
+ * 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;
+
+ /**
+ * reset the value of the handle to ~0
+ */
+ void reset();
+
+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(uint8_t bytes[6]);
+ mac_address_t(const std::string& str);
+ 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;
+
+ /**
+ * 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/extras/vom/vom/vxlan_tunnel.cpp b/extras/vom/vom/vxlan_tunnel.cpp
new file mode 100644
index 00000000000..3d5ed64c5e0
--- /dev/null
+++ b/extras/vom/vom/vxlan_tunnel.cpp
@@ -0,0 +1,266 @@
+/*
+ * 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"
+#include "vom/singular_db_funcs.hpp"
+#include "vom/vxlan_tunnel_cmds.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 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 vxlan_tunnel_cmds::delete_cmd(m_hdl, m_tep));
+ }
+ HW::write();
+}
+
+void
+vxlan_tunnel::replay()
+{
+ if (m_hdl) {
+ HW::enqueue(new vxlan_tunnel_cmds::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 vxlan_tunnel_cmds::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)
+{
+ db_dump(m_db, os);
+}
+
+void
+vxlan_tunnel::event_handler::handle_populate(const client_db::key_t& key)
+{
+ /*
+ * dump VPP current states
+ */
+ std::shared_ptr<vxlan_tunnel_cmds::dump_cmd> cmd =
+ std::make_shared<vxlan_tunnel_cmds::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);
+
+ std::shared_ptr<vxlan_tunnel> vt =
+ vxlan_tunnel(src, dst, payload.vni).singular();
+ vt->set(hdl);
+
+ 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)
+{
+ // dumped by the interface handler
+}
+
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/vxlan_tunnel.hpp b/extras/vom/vom/vxlan_tunnel.hpp
new file mode 100644
index 00000000000..a0b3e9afa3e
--- /dev/null
+++ b/extras/vom/vom/vxlan_tunnel.hpp
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_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/singular_db.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);
+
+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);
+
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/vxlan_tunnel_cmds.cpp b/extras/vom/vom/vxlan_tunnel_cmds.cpp
new file mode 100644
index 00000000000..ca4ccf3c38a
--- /dev/null
+++ b/extras/vom/vom/vxlan_tunnel_cmds.cpp
@@ -0,0 +1,157 @@
+/*
+ * 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_cmds.hpp"
+
+DEFINE_VAPI_MSG_IDS_VXLAN_API_JSON;
+
+namespace VOM {
+namespace vxlan_tunnel_cmds {
+
+create_cmd::create_cmd(HW::item<handle_t>& item,
+ const std::string& name,
+ const vxlan_tunnel::endpoint_t& ep)
+ : interface::create_cmd<vapi::Vxlan_add_del_tunnel>(item, name)
+ , m_ep(ep)
+{
+}
+
+bool
+create_cmd::operator==(const create_cmd& other) const
+{
+ return (m_ep == other.m_ep);
+}
+
+rc_t
+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) {
+ insert_interface();
+ }
+
+ return rc_t::OK;
+}
+
+std::string
+create_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "vxlan-tunnel-create: " << m_hw_item.to_string() << m_ep.to_string();
+
+ return (s.str());
+}
+
+delete_cmd::delete_cmd(HW::item<handle_t>& item,
+ const vxlan_tunnel::endpoint_t& ep)
+ : interface::delete_cmd<vapi::Vxlan_add_del_tunnel>(item)
+ , m_ep(ep)
+{
+}
+
+bool
+delete_cmd::operator==(const delete_cmd& other) const
+{
+ return (m_ep == other.m_ep);
+}
+
+rc_t
+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);
+
+ remove_interface();
+ return (rc_t::OK);
+}
+
+std::string
+delete_cmd::to_string() const
+{
+ std::ostringstream s;
+ s << "vxlan-tunnel-delete: " << m_hw_item.to_string() << m_ep.to_string();
+
+ return (s.str());
+}
+
+dump_cmd::dump_cmd()
+{
+}
+
+bool
+dump_cmd::operator==(const dump_cmd& other) const
+{
+ return (true);
+}
+
+rc_t
+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
+dump_cmd::to_string() const
+{
+ return ("Vpp-vxlan_tunnels-Dump");
+}
+} // namespace vxlan_tunnel_cmds
+} // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/vxlan_tunnel_cmds.hpp b/extras/vom/vom/vxlan_tunnel_cmds.hpp
new file mode 100644
index 00000000000..4a8e5990391
--- /dev/null
+++ b/extras/vom/vom/vxlan_tunnel_cmds.hpp
@@ -0,0 +1,135 @@
+/*
+ * 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_CMDS_H__
+#define __VOM_VXLAN_TUNNEL_CMDS_H__
+
+#include "vom/dump_cmd.hpp"
+#include "vom/rpc_cmd.hpp"
+#include "vom/vxlan_tunnel.hpp"
+
+#include <vapi/vapi.hpp>
+#include <vapi/vxlan.api.vapi.hpp>
+
+namespace VOM {
+namespace vxlan_tunnel_cmds {
+
+/**
+ * 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 vxlan_tunnel::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 vxlan_tunnel::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 vxlan_tunnel::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 vxlan_tunnel::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;
+};
+
+}; // namespace vxlan_tunnel_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif