GBP V2

update the GBP plugin to implement the full NAT feature set of opflex agent

Change-Id: Ic06a039c889445ed0b9087fa1f292634192b0f8d
Signed-off-by: Neale Ranns <neale.ranns@cisco.com>
diff --git a/src/vpp-api/vom/Makefile.am b/src/vpp-api/vom/Makefile.am
index dad4863..f802849 100644
--- a/src/vpp-api/vom/Makefile.am
+++ b/src/vpp-api/vom/Makefile.am
@@ -66,8 +66,14 @@
 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
@@ -142,7 +148,7 @@
 
 ACL_INCLUDES =
 if ENABLE_ACL_PLUGIN
-ACL_INCLUDES += 				\
+ACL_INCLUDES += 			\
 	acl_binding.hpp			\
 	acl_ethertype.hpp		\
 	acl_l2_rule.hpp			\
@@ -153,7 +159,7 @@
 
 NAT_INCLUDES =
 if ENABLE_NAT_PLUGIN
-NAT_INCLUDES += 				\
+NAT_INCLUDES += 			\
 	nat_static.hpp			\
 	nat_binding.hpp
 endif
@@ -166,8 +172,11 @@
 
 GBP_INCLUDES =
 if ENABLE_GBP_PLUGIN
-GBP_INCLUDES += 				\
+GBP_INCLUDES += 			\
 	gbp_endpoint.hpp		\
+	gbp_endpoint_group.hpp		\
+	gbp_subnet.hpp			\
+	gbp_recirc.hpp			\
 	gbp_contract.hpp
 endif
 
diff --git a/src/vpp-api/vom/acl_l2_rule.cpp b/src/vpp-api/vom/acl_l2_rule.cpp
index 1fb06e2..2b12e68 100644
--- a/src/vpp-api/vom/acl_l2_rule.cpp
+++ b/src/vpp-api/vom/acl_l2_rule.cpp
@@ -65,7 +65,7 @@
   return m_priority;
 }
 
-action_t
+const action_t&
 l2_rule::action() const
 {
   return m_action;
diff --git a/src/vpp-api/vom/acl_l2_rule.hpp b/src/vpp-api/vom/acl_l2_rule.hpp
index 4faa628..8c094ae 100644
--- a/src/vpp-api/vom/acl_l2_rule.hpp
+++ b/src/vpp-api/vom/acl_l2_rule.hpp
@@ -69,7 +69,7 @@
    * Getters
    */
   uint32_t priority() const;
-  action_t action() 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;
diff --git a/src/vpp-api/vom/acl_l3_rule.cpp b/src/vpp-api/vom/acl_l3_rule.cpp
index 4b96cae..417dc5f 100644
--- a/src/vpp-api/vom/acl_l3_rule.cpp
+++ b/src/vpp-api/vom/acl_l3_rule.cpp
@@ -147,7 +147,7 @@
   return m_priority;
 }
 
-action_t
+const action_t&
 l3_rule::action() const
 {
   return m_action;
diff --git a/src/vpp-api/vom/acl_l3_rule.hpp b/src/vpp-api/vom/acl_l3_rule.hpp
index 25a2a47..c1f1cee 100644
--- a/src/vpp-api/vom/acl_l3_rule.hpp
+++ b/src/vpp-api/vom/acl_l3_rule.hpp
@@ -121,7 +121,7 @@
    */
   const route::prefix_t& src() const;
   uint32_t priority() const;
-  action_t action() const;
+  const action_t& action() const;
   const route::prefix_t& dst() const;
   uint8_t proto() const;
   uint16_t srcport_or_icmptype_first() const;
diff --git a/src/vpp-api/vom/acl_types.hpp b/src/vpp-api/vom/acl_types.hpp
index ccf0a1c..cf5bee3 100644
--- a/src/vpp-api/vom/acl_types.hpp
+++ b/src/vpp-api/vom/acl_types.hpp
@@ -26,16 +26,6 @@
 struct action_t : public enum_base<action_t>
 {
   /**
-   * Constructor
-   */
-  action_t(int v, const std::string s);
-
-  /**
-   * Destructor
-   */
-  ~action_t() = default;
-
-  /**
    * Permit and Reflexive
    */
   const static action_t PERMITANDREFLEX;
@@ -60,6 +50,9 @@
    *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);
 };
 };
 };
diff --git a/src/vpp-api/vom/bridge_domain.cpp b/src/vpp-api/vom/bridge_domain.cpp
index be520f5..b8c89e1 100644
--- a/src/vpp-api/vom/bridge_domain.cpp
+++ b/src/vpp-api/vom/bridge_domain.cpp
@@ -31,6 +31,33 @@
 {
 }
 
+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
  */
@@ -41,15 +68,25 @@
 /**
  * Construct a new object matching the desried state
  */
-bridge_domain::bridge_domain(uint32_t id, const learning_mode_t& lmode)
+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)
 {
 }
 
@@ -68,7 +105,10 @@
 bool
 bridge_domain::operator==(const bridge_domain& b) const
 {
-  return ((m_learning_mode == b.m_learning_mode) && id() == b.id());
+  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
@@ -84,7 +124,8 @@
 bridge_domain::replay()
 {
   if (rc_t::OK == m_id.rc()) {
-    HW::enqueue(new bridge_domain_cmds::create_cmd(m_id, m_learning_mode));
+    HW::enqueue(new bridge_domain_cmds::create_cmd(
+      m_id, m_learning_mode, m_arp_term_mode, m_flood_mode, m_mac_age_mode));
   }
 }
 
@@ -119,7 +160,8 @@
    * 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));
+    HW::enqueue(new bridge_domain_cmds::create_cmd(
+      m_id, m_learning_mode, m_arp_term_mode, m_flood_mode, m_mac_age_mode));
   }
 }
 
@@ -173,8 +215,10 @@
     for (unsigned int ii = 0; ii < payload.n_sw_ifs; ii++) {
       std::shared_ptr<interface> itf =
         interface::find(payload.sw_if_details[ii].sw_if_index);
-      l2_binding l2(*itf, bd);
-      OM::commit(key, l2);
+      if (itf) {
+        l2_binding l2(*itf, bd);
+        OM::commit(key, l2);
+      }
     }
   }
 }
diff --git a/src/vpp-api/vom/bridge_domain.hpp b/src/vpp-api/vom/bridge_domain.hpp
index c7f84e9..d345da2 100644
--- a/src/vpp-api/vom/bridge_domain.hpp
+++ b/src/vpp-api/vom/bridge_domain.hpp
@@ -52,6 +52,51 @@
   };
 
   /**
+   * 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;
@@ -60,7 +105,10 @@
    * Construct a new object matching the desried state
    */
   bridge_domain(uint32_t id,
-                const learning_mode_t& lmode = learning_mode_t::ON);
+                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
@@ -179,11 +227,26 @@
   HW::item<uint32_t> m_id;
 
   /**
-   * The leanring mode of the bridge
+   * 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;
diff --git a/src/vpp-api/vom/bridge_domain_cmds.cpp b/src/vpp-api/vom/bridge_domain_cmds.cpp
index 498569f..d1d536f 100644
--- a/src/vpp-api/vom/bridge_domain_cmds.cpp
+++ b/src/vpp-api/vom/bridge_domain_cmds.cpp
@@ -20,9 +20,15 @@
 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::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)
 {
 }
 
@@ -39,12 +45,12 @@
 
   auto& payload = req.get_request().get_payload();
   payload.bd_id = m_hw_item.data();
-  payload.flood = 1;
-  payload.uu_flood = 1;
+  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 = 1;
-  payload.mac_age = 0;
+  payload.arp_term = m_arp_term_mode.value();
+  payload.mac_age = m_mac_age_mode.value();
   payload.is_add = 1;
 
   VAPI_CALL(req.execute());
diff --git a/src/vpp-api/vom/bridge_domain_cmds.hpp b/src/vpp-api/vom/bridge_domain_cmds.hpp
index f263b32..0216236 100644
--- a/src/vpp-api/vom/bridge_domain_cmds.hpp
+++ b/src/vpp-api/vom/bridge_domain_cmds.hpp
@@ -35,7 +35,10 @@
    * Constructor
    */
   create_cmd(HW::item<uint32_t>& item,
-             const bridge_domain::learning_mode_t& lmode);
+             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
@@ -56,6 +59,18 @@
    * 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;
 };
 
 /**
diff --git a/src/vpp-api/vom/gbp_endpoint.cpp b/src/vpp-api/vom/gbp_endpoint.cpp
index cd5d7e1..9762a91 100644
--- a/src/vpp-api/vom/gbp_endpoint.cpp
+++ b/src/vpp-api/vom/gbp_endpoint.cpp
@@ -25,48 +25,48 @@
 
 gbp_endpoint::gbp_endpoint(const interface& itf,
                            const boost::asio::ip::address& ip_addr,
-                           epg_id_t epg_id)
+                           const mac_address_t& mac,
+                           const gbp_endpoint_group& epg)
   : m_hw(false)
   , m_itf(itf.singular())
-  , m_ip_addr(ip_addr)
-  , m_epg_id(epg_id)
+  , 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_addr(gbpe.m_ip_addr)
-  , m_epg_id(gbpe.m_epg_id)
+  , m_ip(gbpe.m_ip)
+  , m_mac(gbpe.m_mac)
+  , m_epg(gbpe.m_epg)
 {
 }
 
 gbp_endpoint::~gbp_endpoint()
 {
   sweep();
-
-  // not in the DB anymore.
   m_db.release(key(), this);
 }
 
 const gbp_endpoint::key_t
 gbp_endpoint::key() const
 {
-  return (std::make_pair(m_itf->key(), m_ip_addr));
+  return (std::make_pair(m_itf->key(), m_ip));
 }
 
 bool
 gbp_endpoint::operator==(const gbp_endpoint& gbpe) const
 {
-  return ((key() == gbpe.key()) && (m_epg_id == gbpe.m_epg_id));
+  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_addr));
+    HW::enqueue(new gbp_endpoint_cmds::delete_cmd(m_hw, m_itf->handle(), m_ip));
   }
   HW::write();
 }
@@ -75,8 +75,8 @@
 gbp_endpoint::replay()
 {
   if (m_hw) {
-    HW::enqueue(new gbp_endpoint_cmds::create_cmd(m_hw, m_itf->handle(),
-                                                  m_ip_addr, m_epg_id));
+    HW::enqueue(new gbp_endpoint_cmds::create_cmd(m_hw, m_itf->handle(), m_ip,
+                                                  m_mac, m_epg->id()));
   }
 }
 
@@ -84,8 +84,8 @@
 gbp_endpoint::to_string() const
 {
   std::ostringstream s;
-  s << "gbp-endpoint:[" << m_itf->to_string() << ", " << m_ip_addr.to_string()
-    << ", epg-id:" << m_epg_id << "]";
+  s << "gbp-endpoint:[" << m_itf->to_string() << ", " << m_ip.to_string()
+    << ", " << m_mac.to_string() << ", epg:" << m_epg->to_string() << "]";
 
   return (s.str());
 }
@@ -93,12 +93,9 @@
 void
 gbp_endpoint::update(const gbp_endpoint& r)
 {
-  /*
- * create the table if it is not yet created
- */
   if (rc_t::OK != m_hw.rc()) {
-    HW::enqueue(new gbp_endpoint_cmds::create_cmd(m_hw, m_itf->handle(),
-                                                  m_ip_addr, m_epg_id));
+    HW::enqueue(new gbp_endpoint_cmds::create_cmd(m_hw, m_itf->handle(), m_ip,
+                                                  m_mac, m_epg->id()));
   }
 }
 
@@ -154,11 +151,14 @@
       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) {
-      gbp_endpoint gbpe(*itf, address, payload.endpoint.epg_id);
+    if (itf && epg) {
+      gbp_endpoint gbpe(*itf, address, mac, *epg);
       OM::commit(key, gbpe);
 
       VOM_LOG(log_level_t::DEBUG) << "read: " << gbpe.to_string();
diff --git a/src/vpp-api/vom/gbp_endpoint.hpp b/src/vpp-api/vom/gbp_endpoint.hpp
index 6ece4fa..f6466a6 100644
--- a/src/vpp-api/vom/gbp_endpoint.hpp
+++ b/src/vpp-api/vom/gbp_endpoint.hpp
@@ -18,19 +18,13 @@
 
 #include <ostream>
 
+#include "vom/gbp_endpoint_group.hpp"
 #include "vom/interface.hpp"
 #include "vom/singular_db.hpp"
-#include "vom/types.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
+ * A GBP Enpoint (i.e. a VM)
  */
 class gbp_endpoint : public object_base
 {
@@ -45,7 +39,8 @@
    */
   gbp_endpoint(const interface& itf,
                const boost::asio::ip::address& ip_addr,
-               epg_id_t epg_id);
+               const mac_address_t& mac,
+               const gbp_endpoint_group& epg);
 
   /**
    * Copy Construct
@@ -166,12 +161,17 @@
   /**
    * The IP address of the endpoint
    */
-  boost::asio::ip::address m_ip_addr;
+  boost::asio::ip::address m_ip;
 
   /**
-   * The EPG ID
+   * The MAC address of the endpoint
    */
-  epg_id_t m_epg_id;
+  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
diff --git a/src/vpp-api/vom/gbp_endpoint_cmds.cpp b/src/vpp-api/vom/gbp_endpoint_cmds.cpp
index 4f85b7e..88d2f37 100644
--- a/src/vpp-api/vom/gbp_endpoint_cmds.cpp
+++ b/src/vpp-api/vom/gbp_endpoint_cmds.cpp
@@ -23,10 +23,12 @@
 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)
 {
 }
@@ -35,7 +37,7 @@
 create_cmd::operator==(const create_cmd& other) const
 {
   return ((m_itf == other.m_itf) && (m_ip_addr == other.m_ip_addr) &&
-          (m_epg_id == other.m_epg_id));
+          (m_mac == other.m_mac) && (m_epg_id == other.m_epg_id));
 }
 
 rc_t
@@ -48,6 +50,7 @@
   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());
 
diff --git a/src/vpp-api/vom/gbp_endpoint_cmds.hpp b/src/vpp-api/vom/gbp_endpoint_cmds.hpp
index cc78849..2893ef5 100644
--- a/src/vpp-api/vom/gbp_endpoint_cmds.hpp
+++ b/src/vpp-api/vom/gbp_endpoint_cmds.hpp
@@ -37,6 +37,7 @@
   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);
 
   /**
@@ -57,6 +58,7 @@
 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;
 };
 
diff --git a/src/vpp-api/vom/gbp_endpoint_group.cpp b/src/vpp-api/vom/gbp_endpoint_group.cpp
new file mode 100644
index 0000000..d9f0d38
--- /dev/null
+++ b/src/vpp-api/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/src/vpp-api/vom/gbp_endpoint_group.hpp b/src/vpp-api/vom/gbp_endpoint_group.hpp
new file mode 100644
index 0000000..f7c900f
--- /dev/null
+++ b/src/vpp-api/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/src/vpp-api/vom/gbp_endpoint_group_cmds.cpp b/src/vpp-api/vom/gbp_endpoint_group_cmds.cpp
new file mode 100644
index 0000000..55e81d3
--- /dev/null
+++ b/src/vpp-api/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/src/vpp-api/vom/gbp_endpoint_group_cmds.hpp b/src/vpp-api/vom/gbp_endpoint_group_cmds.hpp
new file mode 100644
index 0000000..4da3a42
--- /dev/null
+++ b/src/vpp-api/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/src/vpp-api/vom/gbp_recirc.cpp b/src/vpp-api/vom/gbp_recirc.cpp
new file mode 100644
index 0000000..250e304
--- /dev/null
+++ b/src/vpp-api/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/src/vpp-api/vom/gbp_recirc.hpp b/src/vpp-api/vom/gbp_recirc.hpp
new file mode 100644
index 0000000..fee4f6c
--- /dev/null
+++ b/src/vpp-api/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/src/vpp-api/vom/gbp_recirc_cmds.cpp b/src/vpp-api/vom/gbp_recirc_cmds.cpp
new file mode 100644
index 0000000..757fcb9
--- /dev/null
+++ b/src/vpp-api/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/src/vpp-api/vom/gbp_recirc_cmds.hpp b/src/vpp-api/vom/gbp_recirc_cmds.hpp
new file mode 100644
index 0000000..fe17834
--- /dev/null
+++ b/src/vpp-api/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/src/vpp-api/vom/gbp_subnet.cpp b/src/vpp-api/vom/gbp_subnet.cpp
new file mode 100644
index 0000000..84dbd22
--- /dev/null
+++ b/src/vpp-api/vom/gbp_subnet.cpp
@@ -0,0 +1,231 @@
+/*
+ * 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& gbpe) const
+{
+  return ((key() == gbpe.key()) && (m_recirc == gbpe.m_recirc) &&
+          (m_epg == gbpe.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)));
+  }
+}
+
+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/src/vpp-api/vom/gbp_subnet.hpp b/src/vpp-api/vom/gbp_subnet.hpp
new file mode 100644
index 0000000..9c9166e
--- /dev/null
+++ b/src/vpp-api/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
+   */
+  std::shared_ptr<route_domain> m_rd;
+
+  /**
+   * prefix to match
+   */
+  const route::prefix_t m_prefix;
+
+  /*
+   * Subnet type
+   */
+  const 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/src/vpp-api/vom/gbp_subnet_cmds.cpp b/src/vpp-api/vom/gbp_subnet_cmds.cpp
new file mode 100644
index 0000000..d087e5c
--- /dev/null
+++ b/src/vpp-api/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/src/vpp-api/vom/gbp_subnet_cmds.hpp b/src/vpp-api/vom/gbp_subnet_cmds.hpp
new file mode 100644
index 0000000..3dbc8db
--- /dev/null
+++ b/src/vpp-api/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/src/vpp-api/vom/interface.cpp b/src/vpp-api/vom/interface.cpp
index e9b7a1a..6faf349 100644
--- a/src/vpp-api/vom/interface.cpp
+++ b/src/vpp-api/vom/interface.cpp
@@ -275,9 +275,7 @@
 std::queue<cmd*>&
 interface::mk_create_cmd(std::queue<cmd*>& q)
 {
-  if (type_t::LOOPBACK == m_type) {
-    q.push(new interface_cmds::loopback_create_cmd(m_hdl, m_name));
-  } else if (type_t::BVI == m_type) {
+  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));
     /*
@@ -516,8 +514,12 @@
   HW::write();
 
   for (auto& itf_record : *cmd) {
-    std::shared_ptr<interface> itf =
-      interface_factory::new_interface(itf_record.get_payload());
+    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();
diff --git a/src/vpp-api/vom/interface_types.cpp b/src/vpp-api/vom/interface_types.cpp
index 911282d..139bdd5 100644
--- a/src/vpp-api/vom/interface_types.cpp
+++ b/src/vpp-api/vom/interface_types.cpp
@@ -51,7 +51,8 @@
     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) {
+  } 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;
diff --git a/src/vpp-api/vom/nat_binding_cmds.hpp b/src/vpp-api/vom/nat_binding_cmds.hpp
index bb94048..1b51192 100644
--- a/src/vpp-api/vom/nat_binding_cmds.hpp
+++ b/src/vpp-api/vom/nat_binding_cmds.hpp
@@ -255,6 +255,238 @@
   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 functor class that binds a NAT configuration to an output interface
+ */
+/* class bind_66_output_cmd */
+/*   : public rpc_cmd<HW::item<bool>, */
+/*                    rc_t, */
+/*                    vapi::Nat66_interface_add_del_output_feature> */
+/* { */
+/* public: */
+/*   /\** */
+/*    * Constructor */
+/*    *\/ */
+/*   bind_66_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_66_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_66_output_cmd */
+/*   : public rpc_cmd<HW::item<bool>, */
+/*                    rc_t, */
+/*                    vapi::Nat66_interface_add_del_output_feature> */
+/* { */
+/* public: */
+/*   /\** */
+/*    * Constructor */
+/*    *\/ */
+/*   unbind_66_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_66_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_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;
+};
+
+/**
+ * A cmd class that Dumps all the nat_statics
+ */
+/* class dump_output_66_cmd */
+/*   : public dump_cmd<vapi::Nat66_interface_output_feature_dump> */
+/* { */
+/* public: */
+/*   /\** */
+/*    * Constructor */
+/*    *\/ */
+/*   dump_output_66_cmd(); */
+/*   dump_output_66_cmd(const dump_output_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_output_66_cmd& i) const; */
+
+/* private: */
+/*   /\** */
+/*    * HW reutrn code */
+/*    *\/ */
+/*   HW::item<bool> item; */
+/* }; */
+
 }; // namespace nat_binding_cmds
 }; // namespace VOM
 
diff --git a/src/vpp-api/vom/nat_static.cpp b/src/vpp-api/vom/nat_static.cpp
index 3185b56..bf8573d 100644
--- a/src/vpp-api/vom/nat_static.cpp
+++ b/src/vpp-api/vom/nat_static.cpp
@@ -22,7 +22,7 @@
 nat_static::event_handler nat_static::m_evh;
 
 nat_static::nat_static(const boost::asio::ip::address& inside,
-                       const boost::asio::ip::address_v4& outside)
+                       const boost::asio::ip::address& outside)
   : m_hw(false)
   , m_rd(route_domain::get_default())
   , m_inside(inside)
@@ -32,7 +32,7 @@
 
 nat_static::nat_static(const route_domain& rd,
                        const boost::asio::ip::address& inside,
-                       const boost::asio::ip::address_v4& outside)
+                       const boost::asio::ip::address& outside)
   : m_hw(false)
   , m_rd(rd.singular())
   , m_inside(inside)
@@ -74,7 +74,10 @@
   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));
+        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();
@@ -86,7 +89,10 @@
   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));
+        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()));
     }
   }
 }
@@ -100,7 +106,10 @@
   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));
+        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()));
     }
   }
 }
@@ -158,20 +167,43 @@
   /*
    * dump VPP current states
    */
-  std::shared_ptr<nat_static_cmds::dump_44_cmd> cmd =
+  std::shared_ptr<nat_static_cmds::dump_44_cmd> cmd44 =
     std::make_shared<nat_static_cmds::dump_44_cmd>();
 
-  HW::enqueue(cmd);
+  HW::enqueue(cmd44);
   HW::write();
 
-  for (auto& record : *cmd) {
+  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.to_v4());
+    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,
diff --git a/src/vpp-api/vom/nat_static.hpp b/src/vpp-api/vom/nat_static.hpp
index 3b0d2d0..2dcadb3 100644
--- a/src/vpp-api/vom/nat_static.hpp
+++ b/src/vpp-api/vom/nat_static.hpp
@@ -39,7 +39,7 @@
    * table
    */
   nat_static(const boost::asio::ip::address& inside,
-             const boost::asio::ip::address_v4& outside);
+             const boost::asio::ip::address& outside);
 
   /**
    * Construct an NAT Static binding with the outside address in
@@ -47,7 +47,7 @@
    */
   nat_static(const route_domain& rd,
              const boost::asio::ip::address& inside,
-             const boost::asio::ip::address_v4& outside);
+             const boost::asio::ip::address& outside);
 
   /**
    * Copy Construct
@@ -171,9 +171,9 @@
   const boost::asio::ip::address m_inside;
 
   /**
-   * The 'outside' IP address - always v4
+   * The 'outside' IP address
    */
-  const boost::asio::ip::address_v4 m_outside;
+  const boost::asio::ip::address m_outside;
 
   /**
    * A map of all NAT statics
diff --git a/src/vpp-api/vom/nat_static_cmds.cpp b/src/vpp-api/vom/nat_static_cmds.cpp
index facc7c6..a80e474 100644
--- a/src/vpp-api/vom/nat_static_cmds.cpp
+++ b/src/vpp-api/vom/nat_static_cmds.cpp
@@ -144,6 +144,125 @@
 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");
 }
 
diff --git a/src/vpp-api/vom/nat_static_cmds.hpp b/src/vpp-api/vom/nat_static_cmds.hpp
index a4adcef..95061ca 100644
--- a/src/vpp-api/vom/nat_static_cmds.hpp
+++ b/src/vpp-api/vom/nat_static_cmds.hpp
@@ -129,6 +129,111 @@
   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
 
diff --git a/src/vpp-api/vom/route.cpp b/src/vpp-api/vom/route.cpp
index 247afa0..ec56c44 100644
--- a/src/vpp-api/vom/route.cpp
+++ b/src/vpp-api/vom/route.cpp
@@ -508,7 +508,7 @@
 dependency_t
 ip_route::event_handler::order() const
 {
-  return (dependency_t::ENTRY);
+  return (dependency_t::TABLE);
 }
 
 void
diff --git a/src/vpp-api/vom/types.cpp b/src/vpp-api/vom/types.cpp
index 44e0dd0..c6093eb 100644
--- a/src/vpp-api/vom/types.cpp
+++ b/src/vpp-api/vom/types.cpp
@@ -28,9 +28,6 @@
   : enum_base<rc_t>(v, s)
 {
 }
-rc_t::~rc_t()
-{
-}
 
 const rc_t&
 rc_t::from_vpp_retval(int32_t rv)
diff --git a/src/vpp-api/vom/types.hpp b/src/vpp-api/vom/types.hpp
index 302e5ee..53654c5 100644
--- a/src/vpp-api/vom/types.hpp
+++ b/src/vpp-api/vom/types.hpp
@@ -94,7 +94,7 @@
   /**
    * Destructor
    */
-  ~rc_t();
+  ~rc_t() = default;
 
   /**
    * The value un-set