GBP Endpoint Learning 23/15223/12
authorNeale Ranns <neale.ranns@cisco.com>
Wed, 10 Oct 2018 14:22:51 +0000 (07:22 -0700)
committerDamjan Marion <dmarion@me.com>
Wed, 7 Nov 2018 12:00:10 +0000 (12:00 +0000)
Learning GBP endpoints over vxlan-gbp tunnels

Change-Id: I1db9fda5a16802d9ad8b4efd4e475614f3b21502
Signed-off-by: Neale Ranns <neale.ranns@cisco.com>
74 files changed:
extras/vom/vom/CMakeLists.txt
extras/vom/vom/gbp_bridge_domain.cpp [new file with mode: 0644]
extras/vom/vom/gbp_bridge_domain.hpp [new file with mode: 0644]
extras/vom/vom/gbp_bridge_domain_cmds.cpp [new file with mode: 0644]
extras/vom/vom/gbp_bridge_domain_cmds.hpp [new file with mode: 0644]
extras/vom/vom/gbp_contract_cmds.hpp
extras/vom/vom/gbp_endpoint_group.cpp
extras/vom/vom/gbp_endpoint_group.hpp
extras/vom/vom/gbp_endpoint_group_cmds.cpp
extras/vom/vom/gbp_endpoint_group_cmds.hpp
extras/vom/vom/gbp_route_domain.cpp [new file with mode: 0644]
extras/vom/vom/gbp_route_domain.hpp [new file with mode: 0644]
extras/vom/vom/gbp_route_domain_cmds.cpp [new file with mode: 0644]
extras/vom/vom/gbp_route_domain_cmds.hpp [new file with mode: 0644]
extras/vom/vom/gbp_subnet.cpp
extras/vom/vom/gbp_subnet.hpp
extras/vom/vom/gbp_subnet_cmds.cpp
extras/vom/vom/gbp_subnet_cmds.hpp
extras/vom/vom/gbp_vxlan.cpp [new file with mode: 0644]
extras/vom/vom/gbp_vxlan.hpp [new file with mode: 0644]
extras/vom/vom/gbp_vxlan_cmds.cpp [new file with mode: 0644]
extras/vom/vom/gbp_vxlan_cmds.hpp [new file with mode: 0644]
extras/vom/vom/gbp_vxlan_tunnel.cpp [new file with mode: 0644]
extras/vom/vom/interface_factory.cpp
src/plugins/gbp/CMakeLists.txt
src/plugins/gbp/gbp.api
src/plugins/gbp/gbp_api.c
src/plugins/gbp/gbp_bridge_domain.c [new file with mode: 0644]
src/plugins/gbp/gbp_bridge_domain.h [new file with mode: 0644]
src/plugins/gbp/gbp_classify.c
src/plugins/gbp/gbp_endpoint.c
src/plugins/gbp/gbp_endpoint.h
src/plugins/gbp/gbp_endpoint_group.c
src/plugins/gbp/gbp_endpoint_group.h
src/plugins/gbp/gbp_itf.c [new file with mode: 0644]
src/plugins/gbp/gbp_itf.h [new file with mode: 0644]
src/plugins/gbp/gbp_learn.c [new file with mode: 0644]
src/plugins/gbp/gbp_learn.h [new file with mode: 0644]
src/plugins/gbp/gbp_policy.c
src/plugins/gbp/gbp_policy_dpo.c
src/plugins/gbp/gbp_recirc.c
src/plugins/gbp/gbp_recirc.h
src/plugins/gbp/gbp_route_domain.c [new file with mode: 0644]
src/plugins/gbp/gbp_route_domain.h [new file with mode: 0644]
src/plugins/gbp/gbp_scanner.c [new file with mode: 0644]
src/plugins/gbp/gbp_scanner.h [new file with mode: 0644]
src/plugins/gbp/gbp_subnet.c
src/plugins/gbp/gbp_subnet.h
src/plugins/gbp/gbp_vxlan.c [new file with mode: 0644]
src/plugins/gbp/gbp_vxlan.h [new file with mode: 0644]
src/vnet/CMakeLists.txt
src/vnet/ethernet/mac_address.h
src/vnet/interface_funcs.h
src/vnet/ip/ip_types_api.c
src/vnet/l2/l2.api
src/vnet/l2/l2_input.h
src/vnet/l2/l2_output.h
src/vnet/vxlan-gbp/decap.c
src/vnet/vxlan-gbp/vxlan_gbp.api
src/vnet/vxlan-gbp/vxlan_gbp.c
src/vnet/vxlan-gbp/vxlan_gbp.h
src/vnet/vxlan-gbp/vxlan_gbp_api.c
src/vnet/vxlan-gbp/vxlan_gbp_packet.c [new file with mode: 0644]
src/vnet/vxlan-gbp/vxlan_gbp_packet.h
test/framework.py
test/test_gbp.py
test/test_l2_flood.py
test/vpp_interface.py
test/vpp_ip.py
test/vpp_ip_route.py
test/vpp_l2.py [new file with mode: 0644]
test/vpp_mac.py
test/vpp_papi_provider.py
test/vpp_vxlan_gbp_tunnel.py [new file with mode: 0644]

index 5a5e5a7..a2c35ad 100644 (file)
@@ -68,16 +68,22 @@ endif()
 
 if(GBP_FILE)
   list(APPEND VOM_SOURCES
-    gbp_recirc_cmds.cpp
-    gbp_recirc.cpp
-    gbp_subnet_cmds.cpp
-    gbp_subnet.cpp
+    gbp_contract_cmds.cpp
+    gbp_contract.cpp
+    gbp_bridge_domain_cmds.cpp
+    gbp_bridge_domain.cpp
     gbp_endpoint_cmds.cpp
     gbp_endpoint.cpp
     gbp_endpoint_group_cmds.cpp
     gbp_endpoint_group.cpp
-    gbp_contract_cmds.cpp
-    gbp_contract.cpp
+    gbp_recirc_cmds.cpp
+    gbp_recirc.cpp
+    gbp_route_domain_cmds.cpp
+    gbp_route_domain.cpp
+    gbp_subnet_cmds.cpp
+    gbp_subnet.cpp
+    gbp_vxlan.cpp
+    gbp_vxlan_cmds.cpp
   )
 endif()
 
diff --git a/extras/vom/vom/gbp_bridge_domain.cpp b/extras/vom/vom/gbp_bridge_domain.cpp
new file mode 100644 (file)
index 0000000..6cad195
--- /dev/null
@@ -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.
+ */
+
+#include "vom/gbp_bridge_domain.hpp"
+#include "vom/gbp_bridge_domain_cmds.hpp"
+#include "vom/interface.hpp"
+#include "vom/l2_binding.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+
+/**
+ * A DB of al the interfaces, key on the name
+ */
+singular_db<uint32_t, gbp_bridge_domain> gbp_bridge_domain::m_db;
+
+gbp_bridge_domain::event_handler gbp_bridge_domain::m_evh;
+
+/**
+ * Construct a new object matching the desried state
+ */
+gbp_bridge_domain::gbp_bridge_domain(const bridge_domain& bd)
+  : m_id(bd.id())
+  , m_bd(bd.singular())
+{
+}
+
+gbp_bridge_domain::gbp_bridge_domain(const bridge_domain& bd,
+                                     const interface& bvi,
+                                     const interface& uu_fwd)
+  : m_id(bd.id())
+  , m_bd(bd.singular())
+  , m_bvi(bvi.singular())
+  , m_uu_fwd(uu_fwd.singular())
+{
+}
+
+gbp_bridge_domain::gbp_bridge_domain(const gbp_bridge_domain& bd)
+  : m_id(bd.id())
+  , m_bd(bd.m_bd)
+{
+}
+
+const gbp_bridge_domain::key_t
+gbp_bridge_domain::key() const
+{
+  return (m_bd->key());
+}
+
+uint32_t
+gbp_bridge_domain::id() const
+{
+  return (m_bd->id());
+}
+
+bool
+gbp_bridge_domain::operator==(const gbp_bridge_domain& b) const
+{
+  bool equal = true;
+
+  if (m_bvi && b.m_bvi)
+    equal &= (m_bvi->key() == b.m_bvi->key());
+  else if (!m_bvi && !b.m_bvi)
+    ;
+  else
+    equal = false;
+
+  if (m_uu_fwd && b.m_uu_fwd)
+    equal &= (m_uu_fwd->key() == b.m_uu_fwd->key());
+  else if (!m_uu_fwd && !b.m_uu_fwd)
+    ;
+  else
+    equal = false;
+
+  return ((m_bd->key() == b.m_bd->key()) && equal);
+}
+
+void
+gbp_bridge_domain::sweep()
+{
+  if (rc_t::OK == m_id.rc()) {
+    HW::enqueue(new gbp_bridge_domain_cmds::delete_cmd(m_id));
+  }
+  HW::write();
+}
+
+void
+gbp_bridge_domain::replay()
+{
+  if (rc_t::OK == m_id.rc()) {
+    if (m_bvi && m_uu_fwd)
+      HW::enqueue(new gbp_bridge_domain_cmds::create_cmd(m_id, m_bvi->handle(),
+                                                         m_uu_fwd->handle()));
+    else
+      HW::enqueue(new gbp_bridge_domain_cmds::create_cmd(
+        m_id, handle_t::INVALID, handle_t::INVALID));
+  }
+}
+
+gbp_bridge_domain::~gbp_bridge_domain()
+{
+  sweep();
+
+  // not in the DB anymore.
+  m_db.release(m_id.data(), this);
+}
+
+std::string
+gbp_bridge_domain::to_string() const
+{
+  std::ostringstream s;
+  s << "gbp-bridge-domain:[" << m_bd->to_string() << "]";
+
+  return (s.str());
+}
+
+std::shared_ptr<gbp_bridge_domain>
+gbp_bridge_domain::find(const key_t& key)
+{
+  return (m_db.find(key));
+}
+
+void
+gbp_bridge_domain::update(const gbp_bridge_domain& desired)
+{
+  /*
+   * the desired state is always that the interface should be created
+   */
+  if (rc_t::OK != m_id.rc()) {
+    if (m_bvi && m_uu_fwd)
+      HW::enqueue(new gbp_bridge_domain_cmds::create_cmd(m_id, m_bvi->handle(),
+                                                         m_uu_fwd->handle()));
+    else
+      HW::enqueue(new gbp_bridge_domain_cmds::create_cmd(
+        m_id, handle_t::INVALID, handle_t::INVALID));
+  }
+}
+
+std::shared_ptr<gbp_bridge_domain>
+gbp_bridge_domain::find_or_add(const gbp_bridge_domain& temp)
+{
+  return (m_db.find_or_add(temp.m_id.data(), temp));
+}
+
+std::shared_ptr<gbp_bridge_domain>
+gbp_bridge_domain::singular() const
+{
+  return find_or_add(*this);
+}
+
+void
+gbp_bridge_domain::dump(std::ostream& os)
+{
+  db_dump(m_db, os);
+}
+
+void
+gbp_bridge_domain::event_handler::handle_populate(const client_db::key_t& key)
+{
+  /*
+   * dump VPP Bridge domains
+   */
+  std::shared_ptr<gbp_bridge_domain_cmds::dump_cmd> cmd =
+    std::make_shared<gbp_bridge_domain_cmds::dump_cmd>();
+
+  HW::enqueue(cmd);
+  HW::write();
+
+  for (auto& record : *cmd) {
+    auto& payload = record.get_payload();
+
+    std::shared_ptr<interface> uu_fwd =
+      interface::find(payload.bd.uu_fwd_sw_if_index);
+    std::shared_ptr<interface> bvi =
+      interface::find(payload.bd.bvi_sw_if_index);
+
+    if (uu_fwd && bvi) {
+      gbp_bridge_domain bd(payload.bd.bd_id, *bvi, *uu_fwd);
+      OM::commit(key, bd);
+      VOM_LOG(log_level_t::DEBUG) << "dump: " << bd.to_string();
+    } else {
+      gbp_bridge_domain bd(payload.bd.bd_id);
+      OM::commit(key, bd);
+      VOM_LOG(log_level_t::DEBUG) << "dump: " << bd.to_string();
+    }
+  }
+}
+
+gbp_bridge_domain::event_handler::event_handler()
+{
+  OM::register_listener(this);
+  inspect::register_handler({ "gbd", "gbridge" }, "GBP Bridge Domains", this);
+}
+
+void
+gbp_bridge_domain::event_handler::handle_replay()
+{
+  m_db.replay();
+}
+
+dependency_t
+gbp_bridge_domain::event_handler::order() const
+{
+  return (dependency_t::TABLE);
+}
+
+void
+gbp_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/gbp_bridge_domain.hpp b/extras/vom/vom/gbp_bridge_domain.hpp
new file mode 100644 (file)
index 0000000..0d7d58e
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_GBP_BRIDGE_DOMAIN_H__
+#define __VOM_GBP_BRIDGE_DOMAIN_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 gbp_bridge_domain : public object_base
+{
+public:
+  /**
+   * The key for a bridge_domain is the pari of EPG-IDs
+   */
+  typedef bridge_domain::key_t key_t;
+
+  /**
+   * Construct a GBP bridge_domain
+   */
+  gbp_bridge_domain(const bridge_domain& bd);
+
+  gbp_bridge_domain(const bridge_domain& bd,
+                    const interface& bvi,
+                    const interface& uu_fwd);
+
+  /**
+   * Copy Construct
+   */
+  gbp_bridge_domain(const gbp_bridge_domain& r);
+
+  /**
+   * Destructor
+   */
+  ~gbp_bridge_domain();
+
+  /**
+   * Return the object's key
+   */
+  const key_t key() const;
+
+  /**
+   * Return the bridge domain's VPP ID
+   */
+  uint32_t id() const;
+
+  /**
+   * comparison operator
+   */
+  bool operator==(const gbp_bridge_domain& bdae) const;
+
+  /**
+   * Return the matching 'singular instance'
+   */
+  std::shared_ptr<gbp_bridge_domain> singular() const;
+
+  /**
+   * Find the instnace of the bridge_domain domain in the OM
+   */
+  static std::shared_ptr<gbp_bridge_domain> 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_bridge_domain& obj);
+
+  /**
+   * Find or add the instance of the bridge_domain domain in the OM
+   */
+  static std::shared_ptr<gbp_bridge_domain> find_or_add(
+    const gbp_bridge_domain& 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_bridge_domain>;
+
+  /**
+   * Sweep/reap the object if still stale
+   */
+  void sweep(void);
+
+  /**
+   * HW configuration for the result of creating the endpoint
+   */
+  HW::item<uint32_t> m_id;
+
+  std::shared_ptr<bridge_domain> m_bd;
+  std::shared_ptr<interface> m_bvi;
+  std::shared_ptr<interface> m_uu_fwd;
+
+  /**
+   * A map of all bridge_domains
+   */
+  static singular_db<key_t, gbp_bridge_domain> 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_bridge_domain_cmds.cpp b/extras/vom/vom/gbp_bridge_domain_cmds.cpp
new file mode 100644 (file)
index 0000000..60f7cdd
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_bridge_domain_cmds.hpp"
+
+namespace VOM {
+namespace gbp_bridge_domain_cmds {
+
+create_cmd::create_cmd(HW::item<uint32_t>& item,
+                       const handle_t bvi,
+                       const handle_t uu_fwd)
+  : rpc_cmd(item)
+  , m_bvi(bvi)
+  , m_uu_fwd(uu_fwd)
+{
+}
+
+bool
+create_cmd::operator==(const create_cmd& other) const
+{
+  return ((m_hw_item.data() == other.m_hw_item.data()) &&
+          (m_bvi == other.m_bvi) && (m_uu_fwd == other.m_uu_fwd));
+}
+
+rc_t
+create_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), std::ref(*this));
+
+  auto& payload = req.get_request().get_payload();
+
+  payload.bd.bd_id = m_hw_item.data();
+  payload.bd.bvi_sw_if_index = m_bvi.value();
+  payload.bd.uu_fwd_sw_if_index = m_uu_fwd.value();
+
+  VAPI_CALL(req.execute());
+
+  return (wait());
+}
+
+std::string
+create_cmd::to_string() const
+{
+  std::ostringstream s;
+  s << "gbp-bridge-domain: " << m_hw_item.to_string()
+    << " bvi:" << m_bvi.to_string() << " uu-fwd:" << m_uu_fwd.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.data() == other.m_hw_item.data());
+}
+
+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();
+
+  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 << "gbp-bridge-domain: " << m_hw_item.to_string();
+
+  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-bridge-domain-dump");
+}
+
+}; // namespace gbp_bridge_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/gbp_bridge_domain_cmds.hpp b/extras/vom/vom/gbp_bridge_domain_cmds.hpp
new file mode 100644 (file)
index 0000000..e7c501f
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_GBP_BRIDGE_DOMAIN_CMDS_H__
+#define __VOM_GBP_BRIDGE_DOMAIN_CMDS_H__
+
+#include "vom/dump_cmd.hpp"
+#include "vom/gbp_bridge_domain.hpp"
+#include "vom/rpc_cmd.hpp"
+
+#include <vapi/gbp.api.vapi.hpp>
+
+namespace VOM {
+namespace gbp_bridge_domain_cmds {
+/**
+ * A command class that creates an Bridge-Domain
+ */
+class create_cmd
+  : public rpc_cmd<HW::item<uint32_t>, vapi::Gbp_bridge_domain_add>
+{
+public:
+  /**
+   * Constructor
+   */
+  create_cmd(HW::item<uint32_t>& item,
+             const handle_t bvi,
+             const handle_t uu_fwd);
+
+  /**
+   * 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_bvi;
+  const handle_t m_uu_fwd;
+};
+
+/**
+ * A cmd class that Delete an Bridge-Domain
+ */
+class delete_cmd
+  : public rpc_cmd<HW::item<uint32_t>, vapi::Gbp_bridge_domain_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 bridge domains
+ */
+class dump_cmd : public VOM::dump_cmd<vapi::Gbp_bridge_domain_dump>
+{
+public:
+  /**
+   * Constructor
+   */
+  dump_cmd() = default;
+  dump_cmd(const dump_cmd& d) = default;
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * Comparison operator - only used for UT
+   */
+  bool operator==(const dump_cmd& i) const;
+
+private:
+  /**
+   * HW reutrn code
+   */
+  HW::item<bool> item;
+};
+
+}; // gbp_bridge_domain_cmds
+}; // VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
index 31b9a00..7e44476 100644 (file)
@@ -91,7 +91,7 @@ private:
 };
 
 /**
- * A cmd class that Dumps all the GBP endpoints
+ * A cmd class that Dumps all the GBP contracts
  */
 class dump_cmd : public VOM::dump_cmd<vapi::Gbp_contract_dump>
 {
index d9f0d38..bd68d77 100644 (file)
@@ -26,8 +26,8 @@ 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)
+                                       const gbp_route_domain& rd,
+                                       const gbp_bridge_domain& bd)
   : m_hw(false)
   , m_epg_id(epg_id)
   , m_itf(itf.singular())
@@ -64,10 +64,10 @@ gbp_endpoint_group::id() const
 }
 
 bool
-gbp_endpoint_group::operator==(const gbp_endpoint_group& gbpe) const
+gbp_endpoint_group::operator==(const gbp_endpoint_group& gg) const
 {
-  return (key() == gbpe.key() && (m_itf == gbpe.m_itf) && (m_rd == gbpe.m_rd) &&
-          (m_bd == gbpe.m_bd));
+  return (key() == gg.key() && (m_itf == gg.m_itf) && (m_rd == gg.m_rd) &&
+          (m_bd == gg.m_bd));
 }
 
 void
@@ -84,7 +84,7 @@ 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()));
+      m_hw, m_epg_id, m_bd->id(), m_rd->id(), m_itf->handle()));
   }
 }
 
@@ -104,7 +104,7 @@ 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()));
+      m_hw, m_epg_id, m_bd->id(), m_rd->id(), m_itf->handle()));
   }
 }
 
@@ -159,12 +159,13 @@ gbp_endpoint_group::event_handler::handle_populate(const client_db::key_t& key)
 
     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);
+    std::shared_ptr<gbp_route_domain> rd =
+      gbp_route_domain::find(payload.epg.rd_id);
+    std::shared_ptr<gbp_bridge_domain> bd =
+      gbp_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.rd_id << ", "
                                 << payload.epg.bd_id << "]";
 
     if (itf && bd && rd) {
index f7c900f..d609b89 100644 (file)
@@ -20,8 +20,8 @@
 #include "vom/singular_db.hpp"
 #include "vom/types.hpp"
 
-#include "vom/bridge_domain.hpp"
-#include "vom/route_domain.hpp"
+#include "vom/gbp_bridge_domain.hpp"
+#include "vom/gbp_route_domain.hpp"
 
 namespace VOM {
 
@@ -46,8 +46,11 @@ public:
    */
   gbp_endpoint_group(epg_id_t epg_id,
                      const interface& itf,
-                     const route_domain& rd,
-                     const bridge_domain& bd);
+                     const gbp_route_domain& rd,
+                     const gbp_bridge_domain& bd);
+  gbp_endpoint_group(epg_id_t epg_id,
+                     const gbp_route_domain& rd,
+                     const gbp_bridge_domain& bd);
 
   /**
    * Copy Construct
@@ -179,12 +182,12 @@ private:
   /**
    * The route-domain the EPG uses
    */
-  std::shared_ptr<route_domain> m_rd;
+  std::shared_ptr<gbp_route_domain> m_rd;
 
   /**
    * The bridge-domain the EPG uses
    */
-  std::shared_ptr<bridge_domain> m_bd;
+  std::shared_ptr<gbp_bridge_domain> m_bd;
 
   /**
    * A map of all bridge_domains
index a7b46f8..45523a6 100644 (file)
@@ -44,12 +44,10 @@ 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;
+  payload.epg.rd_id = m_rd_id;
 
   VAPI_CALL(req.execute());
 
@@ -85,8 +83,7 @@ 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;
+  payload.epg_id = m_epg_id;
 
   VAPI_CALL(req.execute());
 
index 4cf88cf..39f69e0 100644 (file)
@@ -27,8 +27,7 @@ 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>, vapi::Gbp_endpoint_group_add_del>
+class create_cmd : public rpc_cmd<HW::item<bool>, vapi::Gbp_endpoint_group_add>
 {
 public:
   /**
@@ -65,8 +64,7 @@ private:
 /**
  * A cmd class that deletes a GBP endpoint_group
  */
-class delete_cmd
-  : public rpc_cmd<HW::item<bool>, vapi::Gbp_endpoint_group_add_del>
+class delete_cmd : public rpc_cmd<HW::item<bool>, vapi::Gbp_endpoint_group_del>
 {
 public:
   /**
diff --git a/extras/vom/vom/gbp_route_domain.cpp b/extras/vom/vom/gbp_route_domain.cpp
new file mode 100644 (file)
index 0000000..2786297
--- /dev/null
@@ -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.
+ */
+
+#include "vom/gbp_route_domain.hpp"
+#include "vom/gbp_route_domain_cmds.hpp"
+#include "vom/interface.hpp"
+#include "vom/l2_binding.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+
+/**
+ * A DB of al the interfaces, key on the name
+ */
+singular_db<uint32_t, gbp_route_domain> gbp_route_domain::m_db;
+
+gbp_route_domain::event_handler gbp_route_domain::m_evh;
+
+/**
+ * Construct a new object matching the desried state
+ */
+gbp_route_domain::gbp_route_domain(const gbp_route_domain& rd)
+  : m_id(rd.id())
+  , m_rd(rd.m_rd)
+{
+}
+
+gbp_route_domain::gbp_route_domain(const route_domain& rd,
+                                   const interface& ip4_uu_fwd,
+                                   const interface& ip6_uu_fwd)
+  : m_id(rd.table_id())
+  , m_rd(rd.singular())
+  , m_ip4_uu_fwd(ip4_uu_fwd.singular())
+  , m_ip6_uu_fwd(ip6_uu_fwd.singular())
+{
+}
+
+gbp_route_domain::gbp_route_domain(const route_domain& rd)
+  : m_id(rd.table_id())
+  , m_rd(rd.singular())
+{
+}
+
+const gbp_route_domain::key_t
+gbp_route_domain::key() const
+{
+  return (m_rd->key());
+}
+
+uint32_t
+gbp_route_domain::id() const
+{
+  return (m_rd->table_id());
+}
+
+bool
+gbp_route_domain::operator==(const gbp_route_domain& b) const
+{
+  bool equal = true;
+
+  if (m_ip4_uu_fwd && b.m_ip4_uu_fwd)
+    equal &= (m_ip4_uu_fwd->key() == b.m_ip4_uu_fwd->key());
+  else if (!m_ip4_uu_fwd && !b.m_ip4_uu_fwd)
+    ;
+  else
+    equal = false;
+
+  if (m_ip6_uu_fwd && b.m_ip6_uu_fwd)
+    equal &= (m_ip6_uu_fwd->key() == b.m_ip6_uu_fwd->key());
+  else if (!m_ip6_uu_fwd && !b.m_ip6_uu_fwd)
+    ;
+  else
+    equal = false;
+
+  return ((m_rd->key() == b.m_rd->key()) && equal);
+}
+
+void
+gbp_route_domain::sweep()
+{
+  if (rc_t::OK == m_id.rc()) {
+    HW::enqueue(new gbp_route_domain_cmds::delete_cmd(m_id));
+  }
+  HW::write();
+}
+
+void
+gbp_route_domain::replay()
+{
+  if (rc_t::OK == m_id.rc()) {
+    if (m_ip4_uu_fwd && m_ip6_uu_fwd)
+      HW::enqueue(new gbp_route_domain_cmds::create_cmd(
+        m_id, m_ip4_uu_fwd->handle(), m_ip6_uu_fwd->handle()));
+    else
+      HW::enqueue(new gbp_route_domain_cmds::create_cmd(m_id, handle_t::INVALID,
+                                                        handle_t::INVALID));
+  }
+}
+
+gbp_route_domain::~gbp_route_domain()
+{
+  sweep();
+
+  // not in the DB anymore.
+  m_db.release(m_id.data(), this);
+}
+
+std::string
+gbp_route_domain::to_string() const
+{
+  std::ostringstream s;
+  s << "gbp-route-domain:[" << m_rd->to_string() << "]";
+
+  return (s.str());
+}
+
+std::shared_ptr<gbp_route_domain>
+gbp_route_domain::find(const key_t& key)
+{
+  return (m_db.find(key));
+}
+
+void
+gbp_route_domain::update(const gbp_route_domain& desired)
+{
+  /*
+   * the desired state is always that the interface should be created
+   */
+  if (rc_t::OK != m_id.rc()) {
+    if (m_ip4_uu_fwd && m_ip6_uu_fwd)
+      HW::enqueue(new gbp_route_domain_cmds::create_cmd(
+        m_id, m_ip4_uu_fwd->handle(), m_ip6_uu_fwd->handle()));
+    else
+      HW::enqueue(new gbp_route_domain_cmds::create_cmd(m_id, handle_t::INVALID,
+                                                        handle_t::INVALID));
+  }
+}
+
+std::shared_ptr<gbp_route_domain>
+gbp_route_domain::find_or_add(const gbp_route_domain& temp)
+{
+  return (m_db.find_or_add(temp.m_id.data(), temp));
+}
+
+std::shared_ptr<gbp_route_domain>
+gbp_route_domain::singular() const
+{
+  return find_or_add(*this);
+}
+
+void
+gbp_route_domain::dump(std::ostream& os)
+{
+  db_dump(m_db, os);
+}
+
+void
+gbp_route_domain::event_handler::handle_populate(const client_db::key_t& key)
+{
+  /*
+   * dump VPP Route domains
+   */
+  std::shared_ptr<gbp_route_domain_cmds::dump_cmd> cmd =
+    std::make_shared<gbp_route_domain_cmds::dump_cmd>();
+
+  HW::enqueue(cmd);
+  HW::write();
+
+  for (auto& record : *cmd) {
+    auto& payload = record.get_payload();
+
+    std::shared_ptr<interface> ip6_uu_fwd =
+      interface::find(payload.rd.ip6_uu_sw_if_index);
+    std::shared_ptr<interface> ip4_uu_fwd =
+      interface::find(payload.rd.ip4_uu_sw_if_index);
+
+    if (ip6_uu_fwd && ip4_uu_fwd) {
+      gbp_route_domain rd(payload.rd.rd_id, *ip4_uu_fwd, *ip6_uu_fwd);
+      OM::commit(key, rd);
+      VOM_LOG(log_level_t::DEBUG) << "dump: " << rd.to_string();
+    } else {
+      gbp_route_domain rd(payload.rd.rd_id);
+      OM::commit(key, rd);
+      VOM_LOG(log_level_t::DEBUG) << "dump: " << rd.to_string();
+    }
+  }
+}
+
+gbp_route_domain::event_handler::event_handler()
+{
+  OM::register_listener(this);
+  inspect::register_handler({ "grd", "groute" }, "GBP Route Domains", this);
+}
+
+void
+gbp_route_domain::event_handler::handle_replay()
+{
+  m_db.replay();
+}
+
+dependency_t
+gbp_route_domain::event_handler::order() const
+{
+  return (dependency_t::TABLE);
+}
+
+void
+gbp_route_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/gbp_route_domain.hpp b/extras/vom/vom/gbp_route_domain.hpp
new file mode 100644 (file)
index 0000000..6dc37d1
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_GBP_ROUTE_DOMAIN_H__
+#define __VOM_GBP_ROUTE_DOMAIN_H__
+
+#include "vom/interface.hpp"
+#include "vom/route_domain.hpp"
+#include "vom/singular_db.hpp"
+#include "vom/types.hpp"
+
+namespace VOM {
+
+/**
+ * A entry in the ARP termination table of a Route Domain
+ */
+class gbp_route_domain : public object_base
+{
+public:
+  /**
+   * The key for a route_domain is the pari of EPG-IDs
+   */
+  typedef route_domain::key_t key_t;
+
+  /**
+   * Construct a GBP route_domain
+   */
+  gbp_route_domain(const route_domain& rd);
+
+  gbp_route_domain(const route_domain& rd,
+                   const interface& ip4_uu_fwd,
+                   const interface& ip6_uu_fwd);
+
+  /**
+   * Copy Construct
+   */
+  gbp_route_domain(const gbp_route_domain& r);
+
+  /**
+   * Destructor
+   */
+  ~gbp_route_domain();
+
+  /**
+   * Return the object's key
+   */
+  const key_t key() const;
+
+  /**
+   * Return the route domain's VPP ID
+   */
+  uint32_t id() const;
+
+  /**
+   * comparison operator
+   */
+  bool operator==(const gbp_route_domain& rdae) const;
+
+  /**
+   * Return the matching 'singular instance'
+   */
+  std::shared_ptr<gbp_route_domain> singular() const;
+
+  /**
+   * Find the instnace of the route_domain domain in the OM
+   */
+  static std::shared_ptr<gbp_route_domain> find(const key_t& k);
+
+  /**
+   * Dump all route_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_route_domain& obj);
+
+  /**
+   * Find or add the instance of the route_domain domain in the OM
+   */
+  static std::shared_ptr<gbp_route_domain> find_or_add(
+    const gbp_route_domain& 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_route_domain>;
+
+  /**
+   * Sweep/reap the object if still stale
+   */
+  void sweep(void);
+
+  /**
+   * HW configuration for the result of creating the endpoint
+   */
+  HW::item<uint32_t> m_id;
+
+  std::shared_ptr<route_domain> m_rd;
+  std::shared_ptr<interface> m_ip4_uu_fwd;
+  std::shared_ptr<interface> m_ip6_uu_fwd;
+
+  /**
+   * A map of all route_domains
+   */
+  static singular_db<key_t, gbp_route_domain> 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_route_domain_cmds.cpp b/extras/vom/vom/gbp_route_domain_cmds.cpp
new file mode 100644 (file)
index 0000000..0862d8a
--- /dev/null
@@ -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.
+ */
+
+#include "vom/gbp_route_domain_cmds.hpp"
+
+namespace VOM {
+namespace gbp_route_domain_cmds {
+
+create_cmd::create_cmd(HW::item<uint32_t>& item,
+                       const handle_t ip4_uu_fwd,
+                       const handle_t ip6_uu_fwd)
+  : rpc_cmd(item)
+  , m_ip4_uu_fwd(ip4_uu_fwd)
+  , m_ip6_uu_fwd(ip6_uu_fwd)
+{
+}
+
+bool
+create_cmd::operator==(const create_cmd& other) const
+{
+  return ((m_hw_item.data() == other.m_hw_item.data()) &&
+          (m_ip4_uu_fwd == other.m_ip4_uu_fwd) &&
+          (m_ip6_uu_fwd == other.m_ip6_uu_fwd));
+}
+
+rc_t
+create_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), std::ref(*this));
+
+  auto& payload = req.get_request().get_payload();
+
+  payload.rd.rd_id = m_hw_item.data();
+  payload.rd.ip4_uu_sw_if_index = m_ip4_uu_fwd.value();
+  payload.rd.ip6_uu_sw_if_index = m_ip6_uu_fwd.value();
+
+  VAPI_CALL(req.execute());
+
+  return (wait());
+}
+
+std::string
+create_cmd::to_string() const
+{
+  std::ostringstream s;
+  s << "gbp-route-domain: " << m_hw_item.to_string()
+    << " ip4-uu-fwd:" << m_ip4_uu_fwd.to_string()
+    << " ip6-uu-fwd:" << m_ip6_uu_fwd.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.data() == other.m_hw_item.data());
+}
+
+rc_t
+delete_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), std::ref(*this));
+
+  auto& payload = req.get_request().get_payload();
+
+  payload.rd_id = m_hw_item.data();
+
+  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 << "gbp-route-domain: " << m_hw_item.to_string();
+
+  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-route-domain-dump");
+}
+
+}; // namespace gbp_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/gbp_route_domain_cmds.hpp b/extras/vom/vom/gbp_route_domain_cmds.hpp
new file mode 100644 (file)
index 0000000..249ba90
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_GBP_ROUTE_DOMAIN_CMDS_H__
+#define __VOM_GBP_ROUTE_DOMAIN_CMDS_H__
+
+#include "vom/dump_cmd.hpp"
+#include "vom/gbp_route_domain.hpp"
+#include "vom/rpc_cmd.hpp"
+
+#include <vapi/gbp.api.vapi.hpp>
+
+namespace VOM {
+namespace gbp_route_domain_cmds {
+/**
+ * A command class that creates an Route-Domain
+ */
+class create_cmd
+  : public rpc_cmd<HW::item<uint32_t>, vapi::Gbp_route_domain_add>
+{
+public:
+  /**
+   * Constructor
+   */
+  create_cmd(HW::item<uint32_t>& item,
+             const handle_t ip4_uu_fwd,
+             const handle_t ip6_uu_fwd);
+
+  /**
+   * 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_ip4_uu_fwd;
+  const handle_t m_ip6_uu_fwd;
+};
+
+/**
+ * A cmd class that Delete an Route-Domain
+ */
+class delete_cmd
+  : public rpc_cmd<HW::item<uint32_t>, vapi::Gbp_route_domain_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 route domains
+ */
+class dump_cmd : public VOM::dump_cmd<vapi::Gbp_route_domain_dump>
+{
+public:
+  /**
+   * Constructor
+   */
+  dump_cmd() = default;
+  dump_cmd(const dump_cmd& d) = default;
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * Comparison operator - only used for UT
+   */
+  bool operator==(const dump_cmd& i) const;
+
+private:
+  /**
+   * HW reutrn code
+   */
+  HW::item<bool> item;
+};
+
+}; // gbp_route_domain_cmds
+}; // VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
index 1bc024d..2221c61 100644 (file)
@@ -25,31 +25,38 @@ gbp_subnet::type_t::type_t(int v, const std::string 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");
+const gbp_subnet::type_t gbp_subnet::type_t::STITCHED_INTERNAL(
+  0,
+  "stitched-internal");
+const gbp_subnet::type_t gbp_subnet::type_t::STITCHED_EXTERNAL(
+  1,
+  "stitched-external");
+const gbp_subnet::type_t gbp_subnet::type_t::TRANSPORT(1, "transport");
 
 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)
+gbp_subnet::gbp_subnet(const gbp_route_domain& rd,
+                       const route::prefix_t& prefix,
+                       const type_t& type)
   : m_hw(false)
   , m_rd(rd.singular())
   , m_prefix(prefix)
-  , m_type(type_t::INTERNAL)
+  , m_type(type)
   , m_recirc(nullptr)
   , m_epg(nullptr)
 {
 }
 
-gbp_subnet::gbp_subnet(const route_domain& rd,
+gbp_subnet::gbp_subnet(const gbp_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_type(type_t::STITCHED_EXTERNAL)
   , m_recirc(recirc.singular())
   , m_epg(epg.singular())
 {
@@ -88,8 +95,7 @@ void
 gbp_subnet::sweep()
 {
   if (m_hw) {
-    HW::enqueue(
-      new gbp_subnet_cmds::delete_cmd(m_hw, m_rd->table_id(), m_prefix));
+    HW::enqueue(new gbp_subnet_cmds::delete_cmd(m_hw, m_rd->id(), m_prefix));
   }
   HW::write();
 }
@@ -99,7 +105,7 @@ 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_hw, m_rd->id(), m_prefix, m_type,
       (m_recirc ? m_recirc->handle() : handle_t::INVALID),
       (m_epg ? m_epg->id() : ~0)));
   }
@@ -126,7 +132,7 @@ 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_hw, m_rd->id(), m_prefix, m_type,
       (m_recirc ? m_recirc->handle() : handle_t::INVALID),
       (m_epg ? m_epg->id() : ~0)));
   } else {
@@ -136,7 +142,7 @@ gbp_subnet::update(const gbp_subnet& r)
       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_hw, m_rd->id(), m_prefix, m_type,
         (m_recirc ? m_recirc->handle() : handle_t::INVALID),
         (m_epg ? m_epg->id() : ~0)));
     }
@@ -192,27 +198,37 @@ gbp_subnet::event_handler::handle_populate(const client_db::key_t& key)
     auto& payload = record.get_payload();
 
     route::prefix_t pfx = from_api(payload.subnet.prefix);
-    std::shared_ptr<route_domain> rd =
-      route_domain::find(payload.subnet.table_id);
+    std::shared_ptr<gbp_route_domain> rd =
+      gbp_route_domain::find(payload.subnet.rd_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();
+      switch (payload.subnet.type) {
+        case GBP_API_SUBNET_TRANSPORT: {
+          gbp_subnet gs(*rd, pfx, type_t::TRANSPORT);
+          OM::commit(key, gs);
+          VOM_LOG(log_level_t::DEBUG) << "read: " << gs.to_string();
+          break;
+        }
+        case GBP_API_SUBNET_STITCHED_INTERNAL: {
+          gbp_subnet gs(*rd, pfx, type_t::STITCHED_INTERNAL);
+          OM::commit(key, gs);
+          VOM_LOG(log_level_t::DEBUG) << "read: " << gs.to_string();
+          break;
+        }
+        case GBP_API_SUBNET_STITCHED_EXTERNAL: {
+          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();
+            }
           }
         }
       }
@@ -231,6 +247,15 @@ gbp_subnet::event_handler::show(std::ostream& os)
 {
   db_dump(m_db, os);
 }
+
+std::ostream&
+operator<<(std::ostream& os, const gbp_subnet::key_t& key)
+{
+  os << "[" << key.first << ", " << key.second << "]";
+
+  return os;
+}
+
 } // namespace VOM
 
 /*
index b4adb40..e08f1a2 100644 (file)
 #ifndef __VOM_GBP_SUBNET_H__
 #define __VOM_GBP_SUBNET_H__
 
+#include <ostream>
+
 #include "vom/gbp_endpoint_group.hpp"
 #include "vom/gbp_recirc.hpp"
-#include "vom/route.hpp"
+#include "vom/gbp_route_domain.hpp"
 #include "vom/singular_db.hpp"
 
 namespace VOM {
@@ -31,17 +33,41 @@ public:
   /**
    * The key for a GBP subnet; table and prefix
    */
-  typedef std::pair<route_domain::key_t, route::prefix_t> key_t;
+  typedef std::pair<gbp_route_domain::key_t, route::prefix_t> key_t;
+
+  struct type_t : public enum_base<type_t>
+  {
+    /**
+     * Internal subnet is reachable through the source EPG's
+     * uplink interface.
+     */
+    const static type_t STITCHED_INTERNAL;
+
+    /**
+     * External subnet requires NAT translation before egress.
+     */
+    const static type_t STITCHED_EXTERNAL;
+
+    /**
+     * A transport subnet, sent via the RD's UU-fwd interface
+     */
+    const static type_t TRANSPORT;
+
+  private:
+    type_t(int v, const std::string s);
+  };
 
   /**
-    * Construct an internal GBP subnet
-    */
-  gbp_subnet(const route_domain& rd, const route::prefix_t& prefix);
+  * Construct an internal GBP subnet
+  */
+  gbp_subnet(const gbp_route_domain& rd,
+             const route::prefix_t& prefix,
+             const type_t& type);
 
   /**
    * Construct an external GBP subnet
    */
-  gbp_subnet(const route_domain& rd,
+  gbp_subnet(const gbp_route_domain& rd,
              const route::prefix_t& prefix,
              const gbp_recirc& recirc,
              const gbp_endpoint_group& epg);
@@ -92,23 +118,6 @@ public:
   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
    */
@@ -177,7 +186,7 @@ private:
   /**
    * the route domain the prefix is in
    */
-  const std::shared_ptr<route_domain> m_rd;
+  const std::shared_ptr<gbp_route_domain> m_rd;
 
   /**
    * prefix to match
@@ -205,6 +214,8 @@ private:
   static singular_db<key_t, gbp_subnet> m_db;
 };
 
+std::ostream& operator<<(std::ostream& os, const gbp_subnet::key_t& key);
+
 }; // namespace
 
 /*
index 79fdf17..3dcd652 100644 (file)
@@ -22,13 +22,13 @@ 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 gbp_subnet::type_t& type,
                        const handle_t& itf,
                        epg_id_t epg_id)
   : rpc_cmd(item)
   , m_rd(rd)
   , m_prefix(prefix)
-  , m_internal(internal)
+  , m_type(type)
   , m_itf(itf)
   , m_epg_id(epg_id)
 {
@@ -38,8 +38,21 @@ 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));
+          (m_prefix == other.m_prefix) && (m_type == other.m_type) &&
+          (m_itf == other.m_itf) && (m_epg_id == other.m_epg_id));
+}
+
+static vapi_enum_gbp_subnet_type
+gbp_subnet_type_to_api(const gbp_subnet::type_t& type)
+{
+  if (gbp_subnet::type_t::STITCHED_INTERNAL == type)
+    return (GBP_API_SUBNET_STITCHED_INTERNAL);
+  if (gbp_subnet::type_t::STITCHED_EXTERNAL == type)
+    return (GBP_API_SUBNET_STITCHED_EXTERNAL);
+  if (gbp_subnet::type_t::TRANSPORT == type)
+    return (GBP_API_SUBNET_TRANSPORT);
+
+  return (GBP_API_SUBNET_STITCHED_INTERNAL);
 }
 
 rc_t
@@ -49,8 +62,8 @@ create_cmd::issue(connection& con)
 
   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.type = gbp_subnet_type_to_api(m_type);
+  payload.subnet.rd_id = m_rd;
   payload.subnet.sw_if_index = m_itf.value();
   payload.subnet.epg_id = m_epg_id;
   payload.subnet.prefix = to_api(m_prefix);
@@ -64,9 +77,9 @@ 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;
+  s << "gbp-subnet-create: " << m_hw_item.to_string() << "type:" << m_type
+    << ", " << m_rd << ":" << m_prefix.to_string() << " itf:" << m_itf
+    << " epg-id:" << m_epg_id;
 
   return (s.str());
 }
@@ -93,13 +106,9 @@ delete_cmd::issue(connection& con)
 
   auto& payload = req.get_request().get_payload();
   payload.is_add = 0;
-  payload.subnet.table_id = m_rd;
+  payload.subnet.rd_id = m_rd;
   payload.subnet.prefix = to_api(m_prefix);
 
-  payload.subnet.is_internal = 0;
-  payload.subnet.sw_if_index = ~0;
-  payload.subnet.epg_id = ~0;
-
   VAPI_CALL(req.execute());
 
   return (wait());
index 118303b..da2a4c5 100644 (file)
@@ -36,7 +36,7 @@ public:
   create_cmd(HW::item<bool>& item,
              route::table_id_t rd,
              const route::prefix_t& prefix,
-             bool internal,
+             const gbp_subnet::type_t& type,
              const handle_t& itf,
              epg_id_t epg_id);
 
@@ -58,7 +58,7 @@ public:
 private:
   const route::table_id_t m_rd;
   const route::prefix_t m_prefix;
-  const bool m_internal;
+  const gbp_subnet::type_t& m_type;
   const handle_t m_itf;
   const epg_id_t m_epg_id;
 };
diff --git a/extras/vom/vom/gbp_vxlan.cpp b/extras/vom/vom/gbp_vxlan.cpp
new file mode 100644 (file)
index 0000000..af4467a
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_vxlan.hpp"
+#include "vom/gbp_vxlan_cmds.hpp"
+#include "vom/interface.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+
+const std::string GBP_VXLAN_NAME = "gbp-vxlan";
+
+/**
+ * A DB of al the interfaces, key on the name
+ */
+singular_db<gbp_vxlan::key_t, gbp_vxlan> gbp_vxlan::m_db;
+
+gbp_vxlan::event_handler gbp_vxlan::m_evh;
+
+gbp_vxlan::gbp_vxlan(uint32_t vni, const gbp_route_domain& grd)
+  : interface(mk_name(vni),
+              interface::type_t::UNKNOWN,
+              interface::admin_state_t::UP)
+  , m_vni(vni)
+  , m_gbd()
+  , m_grd(grd.singular())
+{
+}
+gbp_vxlan::gbp_vxlan(uint32_t vni, const gbp_bridge_domain& gbd)
+  : interface(mk_name(vni),
+              interface::type_t::UNKNOWN,
+              interface::admin_state_t::UP)
+  , m_vni(vni)
+  , m_gbd(gbd.singular())
+  , m_grd()
+{
+}
+
+gbp_vxlan::gbp_vxlan(const gbp_vxlan& vt)
+  : interface(vt)
+  , m_vni(vt.m_vni)
+  , m_gbd(vt.m_gbd)
+  , m_grd(vt.m_grd)
+{
+}
+
+std::string
+gbp_vxlan::mk_name(uint32_t vni)
+{
+  std::ostringstream s;
+
+  s << GBP_VXLAN_NAME << "-" << vni;
+
+  return (s.str());
+}
+
+const gbp_vxlan::key_t
+gbp_vxlan::key() const
+{
+  return (m_vni);
+}
+
+bool
+gbp_vxlan::operator==(const gbp_vxlan& vt) const
+{
+  return (m_vni == vt.m_vni);
+}
+
+void
+gbp_vxlan::sweep()
+{
+  if (rc_t::OK == m_hdl) {
+    HW::enqueue(new gbp_vxlan_cmds::delete_cmd(m_hdl, m_vni));
+  }
+  HW::write();
+}
+
+void
+gbp_vxlan::replay()
+{
+  if (rc_t::OK == m_hdl) {
+    if (m_grd)
+      HW::enqueue(new gbp_vxlan_cmds::create_cmd(m_hdl, name(), m_vni, false,
+                                                 m_grd->id()));
+    else if (m_gbd)
+      HW::enqueue(new gbp_vxlan_cmds::create_cmd(m_hdl, name(), m_vni, true,
+                                                 m_gbd->id()));
+  }
+}
+
+gbp_vxlan::~gbp_vxlan()
+{
+  sweep();
+  m_db.release(key(), this);
+}
+
+std::string
+gbp_vxlan::to_string() const
+{
+  std::ostringstream s;
+  s << "gbp-vxlan:[" << m_vni << "]";
+
+  return (s.str());
+}
+
+std::shared_ptr<gbp_vxlan>
+gbp_vxlan::find(const key_t key)
+{
+  return (m_db.find(key));
+}
+
+void
+gbp_vxlan::update(const gbp_vxlan& desired)
+{
+  /*
+   * the desired state is always that the interface should be created
+   */
+  if (rc_t::OK != m_hdl) {
+    if (m_grd)
+      HW::enqueue(new gbp_vxlan_cmds::create_cmd(m_hdl, name(), m_vni, false,
+                                                 m_grd->id()));
+    else if (m_gbd)
+      HW::enqueue(new gbp_vxlan_cmds::create_cmd(m_hdl, name(), m_vni, true,
+                                                 m_gbd->id()));
+  }
+}
+
+std::shared_ptr<gbp_vxlan>
+gbp_vxlan::find_or_add(const gbp_vxlan& temp)
+{
+  return (m_db.find_or_add(temp.key(), temp));
+}
+
+std::shared_ptr<gbp_vxlan>
+gbp_vxlan::singular() const
+{
+  return find_or_add(*this);
+}
+
+std::shared_ptr<interface>
+gbp_vxlan::singular_i() const
+{
+  return find_or_add(*this);
+}
+
+void
+gbp_vxlan::dump(std::ostream& os)
+{
+  db_dump(m_db, os);
+}
+
+void
+gbp_vxlan::event_handler::handle_populate(const client_db::key_t& key)
+{
+  /*
+   * dump VPP Bridge domains
+   */
+  std::shared_ptr<gbp_vxlan_cmds::dump_cmd> cmd =
+    std::make_shared<gbp_vxlan_cmds::dump_cmd>();
+
+  HW::enqueue(cmd);
+  HW::write();
+
+  for (auto& record : *cmd) {
+    auto& payload = record.get_payload();
+
+    if (GBP_VXLAN_TUNNEL_MODE_L3 == payload.tunnel.mode) {
+      auto rd = gbp_route_domain::find(payload.tunnel.bd_rd_id);
+
+      if (rd) {
+        gbp_vxlan vt(payload.tunnel.vni, *rd);
+        OM::commit(key, vt);
+        VOM_LOG(log_level_t::DEBUG) << "dump: " << vt.to_string();
+      }
+    } else {
+      auto bd = gbp_bridge_domain::find(payload.tunnel.bd_rd_id);
+
+      if (bd) {
+        gbp_vxlan vt(payload.tunnel.vni, *bd);
+        OM::commit(key, vt);
+        VOM_LOG(log_level_t::DEBUG) << "dump: " << vt.to_string();
+      }
+    }
+  }
+}
+
+gbp_vxlan::event_handler::event_handler()
+{
+  OM::register_listener(this);
+  inspect::register_handler({ "gvt", "gbp-vxlan-tunnel" }, "GBP VXLAN Tunnels",
+                            this);
+}
+
+void
+gbp_vxlan::event_handler::handle_replay()
+{
+  m_db.replay();
+}
+
+dependency_t
+gbp_vxlan::event_handler::order() const
+{
+  return (dependency_t::BINDING);
+}
+
+void
+gbp_vxlan::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/gbp_vxlan.hpp b/extras/vom/vom/gbp_vxlan.hpp
new file mode 100644 (file)
index 0000000..cae67d8
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_VXLAN_H__
+#define __VOM_GBP_VXLAN_H__
+
+#include "vom/gbp_bridge_domain.hpp"
+#include "vom/gbp_route_domain.hpp"
+#include "vom/hw.hpp"
+#include "vom/inspect.hpp"
+#include "vom/interface.hpp"
+#include "vom/singular_db.hpp"
+
+namespace VOM {
+/**
+ * A representation of a GBP_VXLAN Tunnel in VPP
+ */
+class gbp_vxlan : public interface
+{
+public:
+  /**
+   * The VNI is the key
+   */
+  typedef uint32_t key_t;
+
+  /**
+   * Construct a new object matching the desried state
+   */
+  gbp_vxlan(uint32_t vni, const gbp_bridge_domain& gbd);
+  gbp_vxlan(uint32_t vni, const gbp_route_domain& grd);
+
+  /*
+   * Destructor
+   */
+  ~gbp_vxlan();
+
+  /**
+   * Copy constructor
+   */
+  gbp_vxlan(const gbp_vxlan& o);
+
+  bool operator==(const gbp_vxlan& vt) const;
+
+  /**
+     * Return the matching 'singular instance'
+     */
+  std::shared_ptr<gbp_vxlan> singular() const;
+
+  /**
+   * Return the object's key
+   */
+  const key_t key() 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);
+
+  /**
+   * Find the GBP_VXLAN tunnel in the OM
+   */
+  static std::shared_ptr<gbp_vxlan> 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 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 gbp_vxlan& obj);
+
+  /**
+   * Return the matching 'instance' of the sub-interface
+   *  over-ride from the base class
+   */
+  std::shared_ptr<interface> singular_i() const;
+
+  /**
+   * Find the GBP_VXLAN tunnel in the OM
+   */
+  static std::shared_ptr<gbp_vxlan> find_or_add(const gbp_vxlan& 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_vxlan>;
+
+  /**
+   * Sweep/reap the object if still stale
+   */
+  void sweep(void);
+
+  /**
+   * replay the object to create it in hardware
+   */
+  void replay(void);
+
+  /**
+   * Tunnel VNI/key
+   */
+  uint32_t m_vni;
+  std::shared_ptr<gbp_bridge_domain> m_gbd;
+  std::shared_ptr<gbp_route_domain> m_grd;
+
+  /**
+   * A map of all VLAN tunnela against thier key
+   */
+  static singular_db<key_t, gbp_vxlan> m_db;
+
+  /**
+   * Construct a unique name for the tunnel
+   */
+  static std::string mk_name(uint32_t vni);
+};
+
+}; // 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_vxlan_cmds.cpp b/extras/vom/vom/gbp_vxlan_cmds.cpp
new file mode 100644 (file)
index 0000000..90a77fb
--- /dev/null
@@ -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.
+ */
+
+#include "vom/gbp_vxlan_cmds.hpp"
+
+#include <vapi/tap.api.vapi.hpp>
+
+namespace VOM {
+namespace gbp_vxlan_cmds {
+create_cmd::create_cmd(HW::item<handle_t>& item,
+                       const std::string& name,
+                       uint32_t vni,
+                       bool is_l2,
+                       uint32_t bd_rd)
+  : interface::create_cmd<vapi::Gbp_vxlan_tunnel_add>(item, name)
+  , m_vni(vni)
+  , m_is_l2(is_l2)
+  , m_bd_rd(bd_rd)
+{
+}
+
+rc_t
+create_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), std::ref(*this));
+
+  auto& payload = req.get_request().get_payload();
+
+  payload.tunnel.vni = m_vni;
+  payload.tunnel.bd_rd_id = m_bd_rd;
+  if (m_is_l2)
+    payload.tunnel.mode = GBP_VXLAN_TUNNEL_MODE_L2;
+  else
+    payload.tunnel.mode = GBP_VXLAN_TUNNEL_MODE_L3;
+
+  VAPI_CALL(req.execute());
+
+  wait();
+  if (m_hw_item.rc() == rc_t::OK) {
+    insert_interface();
+  }
+
+  return (m_hw_item.rc());
+}
+
+std::string
+create_cmd::to_string() const
+{
+  std::ostringstream s;
+  s << "gbp-vxlan-create: " << m_hw_item.to_string() << " vni:" << m_vni
+    << " bd/rd:" << m_bd_rd;
+
+  return (s.str());
+}
+
+delete_cmd::delete_cmd(HW::item<handle_t>& item, uint32_t vni)
+  : interface::delete_cmd<vapi::Gbp_vxlan_tunnel_del>(item)
+  , m_vni(vni)
+{
+}
+
+rc_t
+delete_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), std::ref(*this));
+
+  auto& payload = req.get_request().get_payload();
+  payload.vni = m_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 << "gbp-vxlan-delete: " << m_hw_item.to_string() << " vni:" << m_vni;
+
+  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-vxlan-dump");
+}
+
+} // namespace gbp_vxlan_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_vxlan_cmds.hpp b/extras/vom/vom/gbp_vxlan_cmds.hpp
new file mode 100644 (file)
index 0000000..a42a653
--- /dev/null
@@ -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_GBP_VXLAN_CMDS_H__
+#define __VOM_GBP_VXLAN_CMDS_H__
+
+#include "vom/dump_cmd.hpp"
+#include "vom/gbp_vxlan.hpp"
+#include "vom/interface.hpp"
+
+#include <vapi/gbp.api.vapi.hpp>
+
+namespace VOM {
+namespace gbp_vxlan_cmds {
+/**
+ * A command class that creates an Bridge-Domain
+ */
+class create_cmd : public interface::create_cmd<vapi::Gbp_vxlan_tunnel_add>
+{
+public:
+  /**
+   * Constructor
+   */
+  create_cmd(HW::item<handle_t>& item,
+             const std::string& name,
+             uint32_t vni,
+             bool is_l2,
+             uint32_t bd_rd);
+
+  /**
+   * 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_vni;
+  bool m_is_l2;
+  uint32_t m_bd_rd;
+};
+
+/**
+ * A cmd class that Delete an Bridge-Domain
+ */
+class delete_cmd : public interface::delete_cmd<vapi::Gbp_vxlan_tunnel_del>
+{
+public:
+  /**
+   * Constructor
+   */
+  delete_cmd(HW::item<handle_t>& item, uint32_t vni);
+
+  /**
+   * 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_vni;
+};
+
+/**
+ * A cmd class that Dumps all the bridge domains
+ */
+class dump_cmd : public VOM::dump_cmd<vapi::Gbp_vxlan_tunnel_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;
+};
+
+}; // gbp_vxlan_cmds
+}; // VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/gbp_vxlan_tunnel.cpp b/extras/vom/vom/gbp_vxlan_tunnel.cpp
new file mode 100644 (file)
index 0000000..2219c04
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_vxlan_tunnel.hpp"
+#include "vom/gbp_vxlan_tunnel_cmds.hpp"
+#include "vom/interface.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+
+/**
+ * A DB of al the interfaces, key on the name
+ */
+singular_db<uint32_t, gbp_vxlan_tunnel> gbp_vxlan_tunnel::m_db;
+
+gbp_vxlan_tunnel::event_handler gbp_vxlan_tunnel::m_evh;
+
+/**
+ * Construct a new object matching the desried state
+ */
+gbp_vxlan_tunnel::gbp_vxlan_tunnel(const vxlan_tunnel& vt)
+  : interface(vt)
+  , m_vni(vt.m_vni)
+{
+}
+
+gbp_vxlan_tunnel::gbp_vxlan_tunnel(uint32_t vni)
+  : interface(mk_name(vni),
+              interface::type_t::UNKNOWN,
+              interface::admin_state_t::UP)
+  , m_vni(vt.m_vni)
+{
+}
+
+const gbp_vxlan_tunnel::key_t
+gbp_vxlan_tunnel::key() const
+{
+  return (m_vni);
+}
+
+bool
+gbp_vxlan_tunnel::operator==(const gbp_vxlan_tunnel& vt) const
+{
+  return (m_vni == vt.m_vni);
+}
+
+void
+gbp_vxlan_tunnel::sweep()
+{
+  if (rc_t::OK == m_id.rc()) {
+    HW::enqueue(new gbp_vxlan_tunnel_cmds::delete_cmd(m_vni));
+  }
+  HW::write();
+}
+
+void
+gbp_vxlan_tunnel::replay()
+{
+  if (rc_t::OK == m_hdl) {
+    HW::enqueue(new gbp_vxlan_tunnel_cmds::create_cmd(m_vni));
+  }
+}
+
+gbp_vxlan_tunnel::~gbp_vxlan_tunnel()
+{
+  sweep();
+  m_db.release(m_id.data(), this);
+}
+
+std::string
+gbp_vxlan_tunnel::to_string() const
+{
+  std::ostringstream s;
+  s << "gbp-vxlan:[" << m_vni << "]";
+
+  return (s.str());
+}
+
+std::shared_ptr<gbp_vxlan_tunnel>
+gbp_vxlan_tunnel::find(const key_t& key)
+{
+  return (m_db.find(key));
+}
+
+void
+gbp_vxlan_tunnel::update(const gbp_vxlan_tunnel& desired)
+{
+  /*
+   * the desired state is always that the interface should be created
+   */
+  if (rc_t::OK != m_hdl) {
+    HW::enqueue(new gbp_vxlan_tunnel_cmds::create_cmd(m_vni));
+  }
+}
+
+std::shared_ptr<gbp_vxlan_tunnel>
+gbp_vxlan_tunnel::find_or_add(const gbp_vxlan_tunnel& temp)
+{
+  return (m_db.find_or_add(temp.m_id.data(), temp));
+}
+
+std::shared_ptr<gbp_vxlan_tunnel>
+gbp_vxlan_tunnel::singular() const
+{
+  return find_or_add(*this);
+}
+
+void
+gbp_vxlan_tunnel::dump(std::ostream& os)
+{
+  db_dump(m_db, os);
+}
+
+void
+gbp_vxlan_tunnel::event_handler::handle_populate(const client_db::key_t& key)
+{
+  /*
+   * dump VPP Bridge domains
+   */
+  std::shared_ptr<gbp_vxlan_tunnel_cmds::dump_cmd> cmd =
+    std::make_shared<gbp_vxlan_tunnel_cmds::dump_cmd>();
+
+  HW::enqueue(cmd);
+  HW::write();
+
+  for (auto& record : *cmd) {
+    auto& payload = record.get_payload();
+
+    gbp_vxlan_tunnel vt(payload.tunnel.vni, );
+    OM::commit(key, vt);
+    VOM_LOG(log_level_t::DEBUG) << "dump: " << vt.to_string();
+  }
+  else
+  {
+    gbp_vxlan_tunnel vt(payload.vt.vt_id);
+    OM::commit(key, vt);
+    VOM_LOG(log_level_t::DEBUG) << "dump: " << vt.to_string();
+  }
+}
+}
+
+gbp_vxlan_tunnel::event_handler::event_handler()
+{
+  OM::register_listener(this);
+  inspect::register_handler({ "gvt", "gbp-vxlan-tunnel" }, "GBP VXLAN Tunnels",
+                            this);
+}
+
+void
+gbp_vxlan_tunnel::event_handler::handle_replay()
+{
+  m_db.replay();
+}
+
+dependency_t
+gbp_vxlan_tunnel::event_handler::order() const
+{
+  return (dependency_t::INTERFACE);
+}
+
+void
+gbp_vxlan_tunnel::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:
+ */
index f442513..fd135f5 100644 (file)
@@ -38,6 +38,10 @@ interface_factory::new_interface(const vapi_payload_sw_interface_details& vd)
   l2_address_t l2_address(vd.l2_address, vd.l2_address_length);
   std::string tag = "";
 
+  if (interface::type_t::UNKNOWN == type) {
+    return sp;
+  }
+
   sp = interface::find(hdl);
   if (sp) {
     sp->set(state);
index c099060..377197a 100644 (file)
 
 add_vpp_plugin(gbp
   SOURCES
-  gbp_subnet.c
+  gbp_api.c
+  gbp_bridge_domain.c
+  gbp_classify.c
   gbp_contract.c
   gbp_endpoint.c
   gbp_endpoint_group.c
-  gbp_classify.c
-  gbp_recirc.c
-  gbp_policy.c
-  gbp_policy_dpo.c
   gbp_fwd.c
   gbp_fwd_dpo.c
-  gbp_api.c
+  gbp_itf.c
+  gbp_learn.c
+  gbp_policy.c
+  gbp_policy_dpo.c
+  gbp_recirc.c
+  gbp_route_domain.c
+  gbp_scanner.c
+  gbp_subnet.c
+  gbp_vxlan.c
 
   API_FILES
   gbp.api
index d7c6d83..bf42243 100644 (file)
@@ -19,16 +19,96 @@ option version = "2.0.0";
 import "vnet/ip/ip_types.api";
 import "vnet/ethernet/ethernet_types.api";
 
+typedef gbp_bridge_domain
+{
+  u32 bd_id;
+  u32 bvi_sw_if_index;
+  u32 uu_fwd_sw_if_index;
+};
+
+autoreply define gbp_bridge_domain_add
+{
+  u32 client_index;
+  u32 context;
+  vl_api_gbp_bridge_domain_t bd;
+};
+autoreply define gbp_bridge_domain_del
+{
+  u32 client_index;
+  u32 context;
+  u32 bd_id;
+};
+autoreply define gbp_bridge_domain_dump
+{
+  u32 client_index;
+  u32 context;
+};
+define gbp_bridge_domain_details
+{
+  u32 context;
+  vl_api_gbp_bridge_domain_t bd;
+};
+
+typedef gbp_route_domain
+{
+  u32 rd_id;
+  u32 ip4_table_id;
+  u32 ip6_table_id;
+  u32 ip4_uu_sw_if_index;
+  u32 ip6_uu_sw_if_index;
+};
+
+autoreply define gbp_route_domain_add
+{
+  u32 client_index;
+  u32 context;
+  vl_api_gbp_route_domain_t rd;
+};
+autoreply define gbp_route_domain_del
+{
+  u32 client_index;
+  u32 context;
+  u32 rd_id;
+};
+autoreply define gbp_route_domain_dump
+{
+  u32 client_index;
+  u32 context;
+};
+define gbp_route_domain_details
+{
+  u32 context;
+  vl_api_gbp_route_domain_t rd;
+};
+
 /** \brief Endpoint
     @param client_index - opaque cookie to identify the sender
     @param context - sender context, to match reply w/ request
 */
 
+enum gbp_endpoint_flags
+{
+  NONE = 0,
+  BOUNCE = 0x1,
+  REMOTE = 0x2,
+  LEARNT = 0x4,
+  /* hey Ole WTF */
+  REMOTE_LEARNT = 0x6,
+};
+
+typedef gbp_endpoint_tun
+{
+  vl_api_address_t src;
+  vl_api_address_t dst;
+};
+
 typedef gbp_endpoint
 {
   u32 sw_if_index;
   u16 epg_id;
+  vl_api_gbp_endpoint_flags_t flags;
   vl_api_mac_address_t mac;
+  vl_api_gbp_endpoint_tun_t tun;
   u8 n_ips;
   vl_api_address_t ips[n_ips];
 };
@@ -63,6 +143,8 @@ define gbp_endpoint_dump
 define gbp_endpoint_details
 {
   u32 context;
+  f64 age;
+  u32 handle;
   vl_api_gbp_endpoint_t endpoint;
 };
 
@@ -70,18 +152,22 @@ typeonly define gbp_endpoint_group
 {
   u16 epg_id;
   u32 bd_id;
-  u32 ip4_table_id;
-  u32 ip6_table_id;
+  u32 rd_id;
   u32 uplink_sw_if_index;
 };
 
-autoreply define gbp_endpoint_group_add_del
+autoreply define gbp_endpoint_group_add
 {
   u32 client_index;
   u32 context;
-  u8  is_add;
   vl_api_gbp_endpoint_group_t epg;
 };
+autoreply define gbp_endpoint_group_del
+{
+  u32 client_index;
+  u32 context;
+  u16 epg_id;
+};
 
 define gbp_endpoint_group_dump
 {
@@ -122,12 +208,19 @@ define gbp_recirc_details
   vl_api_gbp_recirc_t recirc;
 };
 
+enum gbp_subnet_type
+{
+  GBP_API_SUBNET_TRANSPORT,
+  GBP_API_SUBNET_STITCHED_INTERNAL,
+  GBP_API_SUBNET_STITCHED_EXTERNAL,
+};
+
 typeonly define gbp_subnet
 {
-  u32 table_id;
+  u32 rd_id;
   u32 sw_if_index;
   u16 epg_id;
-  u8  is_internal;
+  vl_api_gbp_subnet_type_t type;
   vl_api_prefix_t prefix;
 };
 
@@ -178,6 +271,70 @@ define gbp_contract_details
   vl_api_gbp_contract_t contract;
 };
 
+/**
+ * @brief Set the time throeshold after which an endpoint is
+          considered inative and is aged/reaped by the scanner
+ * @param threshold In seconds
+ */
+autoreply define gbp_endpoint_learn_set_inactive_threshold
+{
+  u32 client_index;
+  u32 context;
+  u32 threshold;
+};
+
+/**
+ * @brief Configure a 'base' tunnel from which learned tunnels
+ *        are permitted to derive
+ *        A base tunnel consists only of the VNI, any src,dst IP
+ *        pair is thus allowed.
+ */
+enum gbp_vxlan_tunnel_mode
+{
+  GBP_VXLAN_TUNNEL_MODE_L2,
+  GBP_VXLAN_TUNNEL_MODE_L3,
+};
+
+typedef gbp_vxlan_tunnel
+{
+  u32 vni;
+  vl_api_gbp_vxlan_tunnel_mode_t mode;
+  u32 bd_rd_id;
+};
+
+define gbp_vxlan_tunnel_add
+{
+  u32 client_index;
+  u32 context;
+  vl_api_gbp_vxlan_tunnel_t tunnel;
+};
+
+define gbp_vxlan_tunnel_add_reply
+{
+  u32 context;
+  i32 retval;
+  u32 sw_if_index;
+};
+
+autoreply define gbp_vxlan_tunnel_del
+{
+  u32 client_index;
+  u32 context;
+  u32 vni;
+};
+
+define gbp_vxlan_tunnel_dump
+{
+  u32 client_index;
+  u32 context;
+};
+
+define gbp_vxlan_tunnel_details
+{
+  u32 context;
+  vl_api_gbp_vxlan_tunnel_t tunnel;
+};
+
 /*
  * Local Variables:
  * eval: (c-set-style "gnu")
index 6bd1abc..faf036e 100644 (file)
 #include <vpp/app/version.h>
 
 #include <gbp/gbp.h>
+#include <gbp/gbp_learn.h>
+#include <gbp/gbp_itf.h>
+#include <gbp/gbp_vxlan.h>
+#include <gbp/gbp_bridge_domain.h>
+#include <gbp/gbp_route_domain.h>
 
 #include <vlibapi/api.h>
 #include <vlibmemory/api.h>
   _(GBP_ENDPOINT_DUMP, gbp_endpoint_dump)                   \
   _(GBP_SUBNET_ADD_DEL, gbp_subnet_add_del)                 \
   _(GBP_SUBNET_DUMP, gbp_subnet_dump)                       \
-  _(GBP_ENDPOINT_GROUP_ADD_DEL, gbp_endpoint_group_add_del) \
+  _(GBP_ENDPOINT_GROUP_ADD, gbp_endpoint_group_add)         \
+  _(GBP_ENDPOINT_GROUP_DEL, gbp_endpoint_group_del)         \
   _(GBP_ENDPOINT_GROUP_DUMP, gbp_endpoint_group_dump)       \
+  _(GBP_BRIDGE_DOMAIN_ADD, gbp_bridge_domain_add)           \
+  _(GBP_BRIDGE_DOMAIN_DEL, gbp_bridge_domain_del)           \
+  _(GBP_BRIDGE_DOMAIN_DUMP, gbp_bridge_domain_dump)         \
+  _(GBP_ROUTE_DOMAIN_ADD, gbp_route_domain_add)             \
+  _(GBP_ROUTE_DOMAIN_DEL, gbp_route_domain_del)             \
+  _(GBP_ROUTE_DOMAIN_DUMP, gbp_route_domain_dump)           \
   _(GBP_RECIRC_ADD_DEL, gbp_recirc_add_del)                 \
   _(GBP_RECIRC_DUMP, gbp_recirc_dump)                       \
   _(GBP_CONTRACT_ADD_DEL, gbp_contract_add_del)             \
-  _(GBP_CONTRACT_DUMP, gbp_contract_dump)
+  _(GBP_CONTRACT_DUMP, gbp_contract_dump)                   \
+  _(GBP_ENDPOINT_LEARN_SET_INACTIVE_THRESHOLD, gbp_endpoint_learn_set_inactive_threshold) \
+  _(GBP_VXLAN_TUNNEL_ADD, gbp_vxlan_tunnel_add)                         \
+  _(GBP_VXLAN_TUNNEL_DEL, gbp_vxlan_tunnel_del)                         \
+  _(GBP_VXLAN_TUNNEL_DUMP, gbp_vxlan_tunnel_dump)
 
 gbp_main_t gbp_main;
 
@@ -72,10 +88,46 @@ static u16 msg_id_base;
 
 #define GBP_MSG_BASE msg_id_base
 
+static gbp_endpoint_flags_t
+gbp_endpoint_flags_decode (vl_api_gbp_endpoint_flags_t v)
+{
+  gbp_endpoint_flags_t f = GBP_ENDPOINT_FLAG_NONE;
+
+  v = ntohl (v);
+
+  if (v & BOUNCE)
+    f |= GBP_ENDPOINT_FLAG_BOUNCE;
+  if (v & REMOTE)
+    f |= GBP_ENDPOINT_FLAG_REMOTE;
+  if (v & LEARNT)
+    f |= GBP_ENDPOINT_FLAG_LEARNT;
+
+  return (f);
+}
+
+static vl_api_gbp_endpoint_flags_t
+gbp_endpoint_flags_encode (gbp_endpoint_flags_t f)
+{
+  vl_api_gbp_endpoint_flags_t v = 0;
+
+
+  if (f & GBP_ENDPOINT_FLAG_BOUNCE)
+    v |= BOUNCE;
+  if (f & GBP_ENDPOINT_FLAG_REMOTE)
+    v |= REMOTE;
+  if (f & GBP_ENDPOINT_FLAG_LEARNT)
+    v |= LEARNT;
+
+  v = htonl (v);
+
+  return (v);
+}
+
 static void
 vl_api_gbp_endpoint_add_t_handler (vl_api_gbp_endpoint_add_t * mp)
 {
   vl_api_gbp_endpoint_add_reply_t *rmp;
+  gbp_endpoint_flags_t gef;
   u32 sw_if_index, handle;
   ip46_address_t *ips;
   mac_address_t mac;
@@ -83,10 +135,9 @@ vl_api_gbp_endpoint_add_t_handler (vl_api_gbp_endpoint_add_t * mp)
 
   VALIDATE_SW_IF_INDEX (&(mp->endpoint));
 
+  gef = gbp_endpoint_flags_decode (mp->endpoint.flags), ips = NULL;
   sw_if_index = ntohl (mp->endpoint.sw_if_index);
 
-  ips = NULL;
-
   if (mp->endpoint.n_ips)
     {
       vec_validate (ips, mp->endpoint.n_ips - 1);
@@ -98,11 +149,23 @@ vl_api_gbp_endpoint_add_t_handler (vl_api_gbp_endpoint_add_t * mp)
     }
   mac_address_decode (&mp->endpoint.mac, &mac);
 
-  rv = gbp_endpoint_update (sw_if_index, ips, &mac,
-                           ntohs (mp->endpoint.epg_id), &handle);
+  if (GBP_ENDPOINT_FLAG_REMOTE & gef)
+    {
+      ip46_address_t tun_src, tun_dst;
 
-  vec_free (ips);
+      ip_address_decode (&mp->endpoint.tun.src, &tun_src);
+      ip_address_decode (&mp->endpoint.tun.dst, &tun_dst);
 
+      rv = gbp_endpoint_update (sw_if_index, ips, &mac,
+                               ntohs (mp->endpoint.epg_id),
+                               gef, &tun_src, &tun_dst, &handle);
+    }
+  else
+    {
+      rv = gbp_endpoint_update (sw_if_index, ips, &mac,
+                               ntohs (mp->endpoint.epg_id),
+                               gef, NULL, NULL, &handle);
+    }
   BAD_SW_IF_INDEX_LABEL;
 
   /* *INDENT-OFF* */
@@ -124,6 +187,19 @@ vl_api_gbp_endpoint_del_t_handler (vl_api_gbp_endpoint_del_t * mp)
   REPLY_MACRO (VL_API_GBP_ENDPOINT_DEL_REPLY + GBP_MSG_BASE);
 }
 
+static void
+  vl_api_gbp_endpoint_learn_set_inactive_threshold_t_handler
+  (vl_api_gbp_endpoint_learn_set_inactive_threshold_t * mp)
+{
+  vl_api_gbp_endpoint_learn_set_inactive_threshold_reply_t *rmp;
+  int rv = 0;
+
+  gbp_learn_set_inactive_threshold (ntohl (mp->threshold));
+
+  REPLY_MACRO (VL_API_GBP_ENDPOINT_LEARN_SET_INACTIVE_THRESHOLD_REPLY +
+              GBP_MSG_BASE);
+}
+
 typedef struct gbp_walk_ctx_t_
 {
   vl_api_registration_t *reg;
@@ -131,14 +207,17 @@ typedef struct gbp_walk_ctx_t_
 } gbp_walk_ctx_t;
 
 static walk_rc_t
-gbp_endpoint_send_details (gbp_endpoint_t * gbpe, void *args)
+gbp_endpoint_send_details (index_t gei, void *args)
 {
   vl_api_gbp_endpoint_details_t *mp;
+  gbp_endpoint_t *ge;
   gbp_walk_ctx_t *ctx;
   u8 n_ips, ii;
 
   ctx = args;
-  n_ips = vec_len (gbpe->ge_ips);
+  ge = gbp_endpoint_get (gei);
+
+  n_ips = vec_len (ge->ge_ips);
   mp = vl_msg_api_alloc (sizeof (*mp) + (sizeof (*mp->endpoint.ips) * n_ips));
   if (!mp)
     return 1;
@@ -147,15 +226,28 @@ gbp_endpoint_send_details (gbp_endpoint_t * gbpe, void *args)
   mp->_vl_msg_id = ntohs (VL_API_GBP_ENDPOINT_DETAILS + GBP_MSG_BASE);
   mp->context = ctx->context;
 
-  mp->endpoint.sw_if_index = ntohl (gbpe->ge_sw_if_index);
-  mp->endpoint.epg_id = ntohs (gbpe->ge_epg_id);
+  if (gbp_endpoint_is_remote (ge))
+    {
+      mp->endpoint.sw_if_index = ntohl (ge->tun.ge_parent_sw_if_index);
+      ip_address_encode (&ge->tun.ge_src, IP46_TYPE_ANY,
+                        &mp->endpoint.tun.src);
+      ip_address_encode (&ge->tun.ge_dst, IP46_TYPE_ANY,
+                        &mp->endpoint.tun.dst);
+    }
+  else
+    {
+      mp->endpoint.sw_if_index = ntohl (ge->ge_sw_if_index);
+    }
+  mp->endpoint.epg_id = ntohs (ge->ge_epg_id);
   mp->endpoint.n_ips = n_ips;
-  mac_address_encode (&gbpe->ge_mac, &mp->endpoint.mac);
+  mp->endpoint.flags = gbp_endpoint_flags_encode (ge->ge_flags);
+  mp->handle = htonl (gei);
+  mp->age = vlib_time_now (vlib_get_main ()) - ge->ge_last_time;
+  mac_address_encode (&ge->ge_mac, &mp->endpoint.mac);
 
-  vec_foreach_index (ii, gbpe->ge_ips)
+  vec_foreach_index (ii, ge->ge_ips)
   {
-    ip_address_encode (&gbpe->ge_ips[ii], IP46_TYPE_ANY,
-                      &mp->endpoint.ips[ii]);
+    ip_address_encode (&ge->ge_ips[ii], IP46_TYPE_ANY, &mp->endpoint.ips[ii]);
   }
 
   vl_api_send_msg (ctx->reg, (u8 *) mp);
@@ -181,58 +273,158 @@ vl_api_gbp_endpoint_dump_t_handler (vl_api_gbp_endpoint_dump_t * mp)
 }
 
 static void
-  vl_api_gbp_endpoint_group_add_del_t_handler
-  (vl_api_gbp_endpoint_group_add_del_t * mp)
+  vl_api_gbp_endpoint_group_add_t_handler
+  (vl_api_gbp_endpoint_group_add_t * mp)
 {
-  vl_api_gbp_endpoint_group_add_del_reply_t *rmp;
-  u32 uplink_sw_if_index;
+  vl_api_gbp_endpoint_group_add_reply_t *rmp;
   int rv = 0;
 
-  uplink_sw_if_index = ntohl (mp->epg.uplink_sw_if_index);
-  if (!vnet_sw_if_index_is_api_valid (uplink_sw_if_index))
-    goto bad_sw_if_index;
+  rv = gbp_endpoint_group_add_and_lock (ntohs (mp->epg.epg_id),
+                                       ntohl (mp->epg.bd_id),
+                                       ntohl (mp->epg.rd_id),
+                                       ntohl (mp->epg.uplink_sw_if_index));
 
-  if (mp->is_add)
-    {
-      rv = gbp_endpoint_group_add (ntohs (mp->epg.epg_id),
-                                  ntohl (mp->epg.bd_id),
-                                  ntohl (mp->epg.ip4_table_id),
-                                  ntohl (mp->epg.ip6_table_id),
-                                  uplink_sw_if_index);
-    }
-  else
+  REPLY_MACRO (VL_API_GBP_ENDPOINT_GROUP_ADD_REPLY + GBP_MSG_BASE);
+}
+
+static void
+  vl_api_gbp_endpoint_group_del_t_handler
+  (vl_api_gbp_endpoint_group_del_t * mp)
+{
+  vl_api_gbp_endpoint_group_del_reply_t *rmp;
+  int rv = 0;
+
+  rv = gbp_endpoint_group_delete (ntohs (mp->epg_id));
+
+  REPLY_MACRO (VL_API_GBP_ENDPOINT_GROUP_DEL_REPLY + GBP_MSG_BASE);
+}
+
+static void
+vl_api_gbp_bridge_domain_add_t_handler (vl_api_gbp_bridge_domain_add_t * mp)
+{
+  vl_api_gbp_bridge_domain_add_reply_t *rmp;
+  int rv = 0;
+
+  rv = gbp_bridge_domain_add_and_lock (ntohl (mp->bd.bd_id),
+                                      ntohl (mp->bd.bvi_sw_if_index),
+                                      ntohl (mp->bd.uu_fwd_sw_if_index));
+
+  REPLY_MACRO (VL_API_GBP_BRIDGE_DOMAIN_ADD_REPLY + GBP_MSG_BASE);
+}
+
+static void
+vl_api_gbp_bridge_domain_del_t_handler (vl_api_gbp_bridge_domain_del_t * mp)
+{
+  vl_api_gbp_bridge_domain_del_reply_t *rmp;
+  int rv = 0;
+
+  rv = gbp_bridge_domain_delete (ntohl (mp->bd_id));
+
+  REPLY_MACRO (VL_API_GBP_BRIDGE_DOMAIN_DEL_REPLY + GBP_MSG_BASE);
+}
+
+static void
+vl_api_gbp_route_domain_add_t_handler (vl_api_gbp_route_domain_add_t * mp)
+{
+  vl_api_gbp_route_domain_add_reply_t *rmp;
+  int rv = 0;
+
+  rv = gbp_route_domain_add_and_lock (ntohl (mp->rd.rd_id),
+                                     ntohl (mp->rd.ip4_table_id),
+                                     ntohl (mp->rd.ip6_table_id),
+                                     ntohl (mp->rd.ip4_uu_sw_if_index),
+                                     ntohl (mp->rd.ip6_uu_sw_if_index));
+
+  REPLY_MACRO (VL_API_GBP_ROUTE_DOMAIN_ADD_REPLY + GBP_MSG_BASE);
+}
+
+static void
+vl_api_gbp_route_domain_del_t_handler (vl_api_gbp_route_domain_del_t * mp)
+{
+  vl_api_gbp_route_domain_del_reply_t *rmp;
+  int rv = 0;
+
+  rv = gbp_route_domain_delete (ntohl (mp->rd_id));
+
+  REPLY_MACRO (VL_API_GBP_ROUTE_DOMAIN_DEL_REPLY + GBP_MSG_BASE);
+}
+
+static int
+gub_subnet_type_from_api (vl_api_gbp_subnet_type_t a, gbp_subnet_type_t * t)
+{
+  a = clib_net_to_host_u32 (a);
+
+  switch (a)
     {
-      gbp_endpoint_group_delete (ntohs (mp->epg.epg_id));
+    case GBP_API_SUBNET_TRANSPORT:
+      *t = GBP_SUBNET_TRANSPORT;
+      return (0);
+    case GBP_API_SUBNET_STITCHED_INTERNAL:
+      *t = GBP_SUBNET_STITCHED_INTERNAL;
+      return (0);
+    case GBP_API_SUBNET_STITCHED_EXTERNAL:
+      *t = GBP_SUBNET_STITCHED_EXTERNAL;
+      return (0);
     }
 
-  BAD_SW_IF_INDEX_LABEL;
-
-  REPLY_MACRO (VL_API_GBP_ENDPOINT_GROUP_ADD_DEL_REPLY + GBP_MSG_BASE);
+  return (-1);
 }
 
 static void
 vl_api_gbp_subnet_add_del_t_handler (vl_api_gbp_subnet_add_del_t * mp)
 {
   vl_api_gbp_subnet_add_del_reply_t *rmp;
+  gbp_subnet_type_t type;
   fib_prefix_t pfx;
   int rv = 0;
 
   ip_prefix_decode (&mp->subnet.prefix, &pfx);
 
-  rv = gbp_subnet_add_del (ntohl (mp->subnet.table_id),
-                          &pfx,
-                          ntohl (mp->subnet.sw_if_index),
-                          ntohs (mp->subnet.epg_id),
-                          mp->is_add, mp->subnet.is_internal);
+  rv = gub_subnet_type_from_api (mp->subnet.type, &type);
+
+  if (0 != rv)
+    goto out;
 
+  if (mp->is_add)
+    rv = gbp_subnet_add (ntohl (mp->subnet.rd_id),
+                        &pfx, type,
+                        ntohl (mp->subnet.sw_if_index),
+                        ntohs (mp->subnet.epg_id));
+  else
+    rv = gbp_subnet_del (ntohl (mp->subnet.rd_id), &pfx);
+
+out:
   REPLY_MACRO (VL_API_GBP_SUBNET_ADD_DEL_REPLY + GBP_MSG_BASE);
 }
 
-static int
-gbp_subnet_send_details (u32 table_id,
+static vl_api_gbp_subnet_type_t
+gub_subnet_type_to_api (gbp_subnet_type_t t)
+{
+  vl_api_gbp_subnet_type_t a = 0;
+
+  switch (t)
+    {
+    case GBP_SUBNET_TRANSPORT:
+      a = GBP_API_SUBNET_TRANSPORT;
+      break;
+    case GBP_SUBNET_STITCHED_INTERNAL:
+      a = GBP_API_SUBNET_STITCHED_INTERNAL;
+      break;
+    case GBP_SUBNET_STITCHED_EXTERNAL:
+      a = GBP_API_SUBNET_STITCHED_EXTERNAL;
+      break;
+    }
+
+  a = clib_host_to_net_u32 (a);
+
+  return (a);
+}
+
+static walk_rc_t
+gbp_subnet_send_details (u32 rd_id,
                         const fib_prefix_t * pfx,
-                        u32 sw_if_index,
-                        epg_id_t epg, u8 is_internal, void *args)
+                        gbp_subnet_type_t type,
+                        u32 sw_if_index, epg_id_t epg, void *args)
 {
   vl_api_gbp_subnet_details_t *mp;
   gbp_walk_ctx_t *ctx;
@@ -246,15 +438,15 @@ gbp_subnet_send_details (u32 table_id,
   mp->_vl_msg_id = ntohs (VL_API_GBP_SUBNET_DETAILS + GBP_MSG_BASE);
   mp->context = ctx->context;
 
-  mp->subnet.is_internal = is_internal;
+  mp->subnet.type = gub_subnet_type_to_api (type);
   mp->subnet.sw_if_index = ntohl (sw_if_index);
   mp->subnet.epg_id = ntohs (epg);
-  mp->subnet.table_id = ntohl (table_id);
+  mp->subnet.rd_id = ntohl (rd_id);
   ip_prefix_encode (pfx, &mp->subnet.prefix);
 
   vl_api_send_msg (ctx->reg, (u8 *) mp);
 
-  return (1);
+  return (WALK_CONTINUE);
 }
 
 static void
@@ -275,7 +467,7 @@ vl_api_gbp_subnet_dump_t_handler (vl_api_gbp_subnet_dump_t * mp)
 }
 
 static int
-gbp_endpoint_group_send_details (gbp_endpoint_group_t * gepg, void *args)
+gbp_endpoint_group_send_details (gbp_endpoint_group_t * gg, void *args)
 {
   vl_api_gbp_endpoint_group_details_t *mp;
   gbp_walk_ctx_t *ctx;
@@ -289,11 +481,10 @@ gbp_endpoint_group_send_details (gbp_endpoint_group_t * gepg, void *args)
   mp->_vl_msg_id = ntohs (VL_API_GBP_ENDPOINT_GROUP_DETAILS + GBP_MSG_BASE);
   mp->context = ctx->context;
 
-  mp->epg.uplink_sw_if_index = ntohl (gepg->gepg_uplink_sw_if_index);
-  mp->epg.epg_id = ntohs (gepg->gepg_id);
-  mp->epg.bd_id = ntohl (gepg->gepg_bd);
-  mp->epg.ip4_table_id = ntohl (gepg->gepg_rd[FIB_PROTOCOL_IP4]);
-  mp->epg.ip6_table_id = ntohl (gepg->gepg_rd[FIB_PROTOCOL_IP6]);
+  mp->epg.uplink_sw_if_index = ntohl (gg->gg_uplink_sw_if_index);
+  mp->epg.epg_id = ntohs (gg->gg_id);
+  mp->epg.bd_id = ntohl (gbp_endpoint_group_get_bd_id (gg));
+  mp->epg.rd_id = ntohl (gg->gg_rd);
 
   vl_api_send_msg (ctx->reg, (u8 *) mp);
 
@@ -318,6 +509,90 @@ vl_api_gbp_endpoint_group_dump_t_handler (vl_api_gbp_endpoint_group_dump_t *
   gbp_endpoint_group_walk (gbp_endpoint_group_send_details, &ctx);
 }
 
+static int
+gbp_bridge_domain_send_details (gbp_bridge_domain_t * gb, void *args)
+{
+  vl_api_gbp_bridge_domain_details_t *mp;
+  gbp_walk_ctx_t *ctx;
+
+  ctx = args;
+  mp = vl_msg_api_alloc (sizeof (*mp));
+  if (!mp)
+    return 1;
+
+  memset (mp, 0, sizeof (*mp));
+  mp->_vl_msg_id = ntohs (VL_API_GBP_BRIDGE_DOMAIN_DETAILS + GBP_MSG_BASE);
+  mp->context = ctx->context;
+
+  mp->bd.bd_id = ntohl (gb->gb_bd_id);
+  mp->bd.bvi_sw_if_index = ntohl (gb->gb_bvi_sw_if_index);
+  mp->bd.uu_fwd_sw_if_index = ntohl (gb->gb_uu_fwd_sw_if_index);
+
+  vl_api_send_msg (ctx->reg, (u8 *) mp);
+
+  return (1);
+}
+
+static void
+vl_api_gbp_bridge_domain_dump_t_handler (vl_api_gbp_bridge_domain_dump_t * mp)
+{
+  vl_api_registration_t *reg;
+
+  reg = vl_api_client_index_to_registration (mp->client_index);
+  if (!reg)
+    return;
+
+  gbp_walk_ctx_t ctx = {
+    .reg = reg,
+    .context = mp->context,
+  };
+
+  gbp_bridge_domain_walk (gbp_bridge_domain_send_details, &ctx);
+}
+
+static int
+gbp_route_domain_send_details (gbp_route_domain_t * grd, void *args)
+{
+  vl_api_gbp_route_domain_details_t *mp;
+  gbp_walk_ctx_t *ctx;
+
+  ctx = args;
+  mp = vl_msg_api_alloc (sizeof (*mp));
+  if (!mp)
+    return 1;
+
+  memset (mp, 0, sizeof (*mp));
+  mp->_vl_msg_id = ntohs (VL_API_GBP_ROUTE_DOMAIN_DETAILS + GBP_MSG_BASE);
+  mp->context = ctx->context;
+
+  mp->rd.rd_id = ntohl (grd->grd_id);
+  mp->rd.ip4_uu_sw_if_index =
+    ntohl (grd->grd_uu_sw_if_index[FIB_PROTOCOL_IP4]);
+  mp->rd.ip6_uu_sw_if_index =
+    ntohl (grd->grd_uu_sw_if_index[FIB_PROTOCOL_IP6]);
+
+  vl_api_send_msg (ctx->reg, (u8 *) mp);
+
+  return (1);
+}
+
+static void
+vl_api_gbp_route_domain_dump_t_handler (vl_api_gbp_route_domain_dump_t * mp)
+{
+  vl_api_registration_t *reg;
+
+  reg = vl_api_client_index_to_registration (mp->client_index);
+  if (!reg)
+    return;
+
+  gbp_walk_ctx_t ctx = {
+    .reg = reg,
+    .context = mp->context,
+  };
+
+  gbp_route_domain_walk (gbp_route_domain_send_details, &ctx);
+}
+
 static void
 vl_api_gbp_recirc_add_del_t_handler (vl_api_gbp_recirc_add_del_t * mp)
 {
@@ -439,6 +714,121 @@ vl_api_gbp_contract_dump_t_handler (vl_api_gbp_contract_dump_t * mp)
   gbp_contract_walk (gbp_contract_send_details, &ctx);
 }
 
+static int
+gbp_vxlan_tunnel_mode_2_layer (vl_api_gbp_vxlan_tunnel_mode_t mode,
+                              gbp_vxlan_tunnel_layer_t * l)
+{
+  mode = clib_net_to_host_u32 (mode);
+
+  switch (mode)
+    {
+    case GBP_VXLAN_TUNNEL_MODE_L2:
+      *l = GBP_VXLAN_TUN_L2;
+      return (0);
+    case GBP_VXLAN_TUNNEL_MODE_L3:
+      *l = GBP_VXLAN_TUN_L3;
+      return (0);
+    }
+  return (-1);
+}
+
+static void
+vl_api_gbp_vxlan_tunnel_add_t_handler (vl_api_gbp_vxlan_tunnel_add_t * mp)
+{
+  vl_api_gbp_vxlan_tunnel_add_reply_t *rmp;
+  gbp_vxlan_tunnel_layer_t layer;
+  u32 sw_if_index;
+  int rv = 0;
+
+  rv = gbp_vxlan_tunnel_mode_2_layer (mp->tunnel.mode, &layer);
+
+  if (0 != rv)
+    goto out;
+
+  rv = gbp_vxlan_tunnel_add (ntohl (mp->tunnel.vni),
+                            layer,
+                            ntohl (mp->tunnel.bd_rd_id), &sw_if_index);
+
+out:
+  /* *INDENT-OFF* */
+  REPLY_MACRO2 (VL_API_GBP_VXLAN_TUNNEL_ADD_REPLY + GBP_MSG_BASE,
+  ({
+    rmp->sw_if_index = htonl (sw_if_index);
+  }));
+  /* *INDENT-ON* */
+}
+
+static void
+vl_api_gbp_vxlan_tunnel_del_t_handler (vl_api_gbp_vxlan_tunnel_add_t * mp)
+{
+  vl_api_gbp_vxlan_tunnel_del_reply_t *rmp;
+  int rv = 0;
+
+  rv = gbp_vxlan_tunnel_del (ntohl (mp->tunnel.vni));
+
+  REPLY_MACRO (VL_API_GBP_VXLAN_TUNNEL_DEL_REPLY + GBP_MSG_BASE);
+}
+
+static vl_api_gbp_vxlan_tunnel_mode_t
+gbp_vxlan_tunnel_layer_2_mode (gbp_vxlan_tunnel_layer_t layer)
+{
+  vl_api_gbp_vxlan_tunnel_mode_t mode = GBP_VXLAN_TUNNEL_MODE_L2;
+
+  switch (layer)
+    {
+    case GBP_VXLAN_TUN_L2:
+      mode = GBP_VXLAN_TUNNEL_MODE_L2;
+      break;
+    case GBP_VXLAN_TUN_L3:
+      mode = GBP_VXLAN_TUNNEL_MODE_L3;
+      break;
+    }
+  mode = clib_host_to_net_u32 (mode);
+
+  return (mode);
+}
+
+static walk_rc_t
+gbp_vxlan_tunnel_send_details (gbp_vxlan_tunnel_t * gt, void *args)
+{
+  vl_api_gbp_vxlan_tunnel_details_t *mp;
+  gbp_walk_ctx_t *ctx;
+
+  ctx = args;
+  mp = vl_msg_api_alloc (sizeof (*mp));
+  if (!mp)
+    return 1;
+
+  memset (mp, 0, sizeof (*mp));
+  mp->_vl_msg_id = htons (VL_API_GBP_VXLAN_TUNNEL_DETAILS + GBP_MSG_BASE);
+  mp->context = ctx->context;
+
+  mp->tunnel.vni = htonl (gt->gt_vni);
+  mp->tunnel.mode = gbp_vxlan_tunnel_layer_2_mode (gt->gt_layer);
+  mp->tunnel.bd_rd_id = htonl (gt->gt_bd_rd_id);
+
+  vl_api_send_msg (ctx->reg, (u8 *) mp);
+
+  return (1);
+}
+
+static void
+vl_api_gbp_vxlan_tunnel_dump_t_handler (vl_api_gbp_vxlan_tunnel_dump_t * mp)
+{
+  vl_api_registration_t *reg;
+
+  reg = vl_api_client_index_to_registration (mp->client_index);
+  if (!reg)
+    return;
+
+  gbp_walk_ctx_t ctx = {
+    .reg = reg,
+    .context = mp->context,
+  };
+
+  gbp_vxlan_walk (gbp_vxlan_tunnel_send_details, &ctx);
+}
+
 /*
  * gbp_api_hookup
  * Add vpe's API message handlers to the table.
diff --git a/src/plugins/gbp/gbp_bridge_domain.c b/src/plugins/gbp/gbp_bridge_domain.c
new file mode 100644 (file)
index 0000000..b7812eb
--- /dev/null
@@ -0,0 +1,368 @@
+/*
+ * 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 <plugins/gbp/gbp_bridge_domain.h>
+#include <plugins/gbp/gbp_endpoint.h>
+
+#include <vnet/dpo/dvr_dpo.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/l2/l2_input.h>
+#include <vnet/l2/feat_bitmap.h>
+#include <vnet/l2/l2_bvi.h>
+#include <vnet/l2/l2_fib.h>
+
+/**
+ * Pool of GBP bridge_domains
+ */
+gbp_bridge_domain_t *gbp_bridge_domain_pool;
+
+/**
+ * DB of bridge_domains
+ */
+typedef struct gbp_bridge_domain_db_t
+{
+  uword *gbd_by_bd_id;
+} gbp_bridge_domain_db_t;
+
+static gbp_bridge_domain_db_t gbp_bridge_domain_db;
+
+/**
+ * logger
+ */
+vlib_log_class_t gb_logger;
+
+#define GBP_BD_DBG(...)                           \
+    vlib_log_debug (gb_logger, __VA_ARGS__);
+
+gbp_bridge_domain_t *
+gbp_bridge_domain_get (index_t i)
+{
+  return (pool_elt_at_index (gbp_bridge_domain_pool, i));
+}
+
+static void
+gbp_bridge_domain_lock (index_t i)
+{
+  gbp_bridge_domain_t *gb;
+
+  gb = gbp_bridge_domain_get (i);
+  gb->gb_locks++;
+}
+
+static index_t
+gbp_bridge_domain_find (u32 bd_id)
+{
+  uword *p;
+
+  p = hash_get (gbp_bridge_domain_db.gbd_by_bd_id, bd_id);
+
+  if (NULL != p)
+    return p[0];
+
+  return (INDEX_INVALID);
+}
+
+index_t
+gbp_bridge_domain_find_and_lock (u32 bd_id)
+{
+  uword *p;
+
+  p = hash_get (gbp_bridge_domain_db.gbd_by_bd_id, bd_id);
+
+  if (NULL != p)
+    {
+      gbp_bridge_domain_lock (p[0]);
+      return p[0];
+    }
+  return (INDEX_INVALID);
+}
+
+static void
+gbp_bridge_domain_db_add (gbp_bridge_domain_t * gb)
+{
+  index_t gbi = gb - gbp_bridge_domain_pool;
+
+  hash_set (gbp_bridge_domain_db.gbd_by_bd_id, gb->gb_bd_id, gbi);
+}
+
+static void
+gbp_bridge_domain_db_remove (gbp_bridge_domain_t * gb)
+{
+  hash_unset (gbp_bridge_domain_db.gbd_by_bd_id, gb->gb_bd_id);
+}
+
+int
+gbp_bridge_domain_add_and_lock (u32 bd_id,
+                               u32 bvi_sw_if_index, u32 uu_fwd_sw_if_index)
+{
+  gbp_bridge_domain_t *gb;
+  index_t gbi;
+
+  gbi = gbp_bridge_domain_find (bd_id);
+
+  if (INDEX_INVALID == gbi)
+    {
+      u32 bd_index;
+
+      bd_index = bd_find_index (&bd_main, bd_id);
+
+      if (~0 == bd_index)
+       return (VNET_API_ERROR_BD_NOT_MODIFIABLE);
+
+      /*
+       * unset learning in the bridge
+       */
+      bd_set_flags (vlib_get_main (), bd_index, L2_LEARN, 0);
+
+      pool_get (gbp_bridge_domain_pool, gb);
+      memset (gb, 0, sizeof (*gb));
+
+      gb->gb_bd_id = bd_id;
+      gb->gb_bd_index = bd_index;
+      gb->gb_uu_fwd_sw_if_index = uu_fwd_sw_if_index;
+      gb->gb_bvi_sw_if_index = bvi_sw_if_index;
+      gb->gb_locks = 1;
+
+      /*
+       * Set the BVI and uu-flood interfaces into the BD
+       */
+      set_int_l2_mode (vlib_get_main (), vnet_get_main (),
+                      MODE_L2_BRIDGE, gb->gb_bvi_sw_if_index,
+                      bd_index, L2_BD_PORT_TYPE_BVI, 0, 0);
+      if (~0 != gb->gb_uu_fwd_sw_if_index)
+       set_int_l2_mode (vlib_get_main (), vnet_get_main (),
+                        MODE_L2_BRIDGE, gb->gb_uu_fwd_sw_if_index,
+                        bd_index, L2_BD_PORT_TYPE_UU_FWD, 0, 0);
+
+      /*
+       * Add the BVI's MAC to the L2FIB
+       */
+      l2fib_add_entry (vnet_sw_interface_get_hw_address
+                      (vnet_get_main (), gb->gb_bvi_sw_if_index),
+                      gb->gb_bd_index, gb->gb_bvi_sw_if_index,
+                      (L2FIB_ENTRY_RESULT_FLAG_STATIC |
+                       L2FIB_ENTRY_RESULT_FLAG_BVI));
+
+      gbp_bridge_domain_db_add (gb);
+    }
+  else
+    {
+      gb = gbp_bridge_domain_get (gbi);
+      gb->gb_locks++;
+    }
+
+  GBP_BD_DBG ("add: %U", format_gbp_bridge_domain, gb);
+
+  return (0);
+}
+
+void
+gbp_bridge_domain_unlock (index_t index)
+{
+  gbp_bridge_domain_t *gb;
+
+  gb = gbp_bridge_domain_get (index);
+
+  gb->gb_locks--;
+
+  if (0 == gb->gb_locks)
+    {
+      GBP_BD_DBG ("destroy: %U", format_gbp_bridge_domain, gb);
+
+      l2fib_del_entry (vnet_sw_interface_get_hw_address
+                      (vnet_get_main (), gb->gb_bvi_sw_if_index),
+                      gb->gb_bd_index, gb->gb_bvi_sw_if_index);
+
+      set_int_l2_mode (vlib_get_main (), vnet_get_main (),
+                      MODE_L3, gb->gb_bvi_sw_if_index,
+                      gb->gb_bd_index, L2_BD_PORT_TYPE_BVI, 0, 0);
+      if (~0 != gb->gb_uu_fwd_sw_if_index)
+       set_int_l2_mode (vlib_get_main (), vnet_get_main (),
+                        MODE_L3, gb->gb_uu_fwd_sw_if_index,
+                        gb->gb_bd_index, L2_BD_PORT_TYPE_UU_FWD, 0, 0);
+
+      gbp_bridge_domain_db_remove (gb);
+
+      pool_put (gbp_bridge_domain_pool, gb);
+    }
+}
+
+int
+gbp_bridge_domain_delete (u32 bd_id)
+{
+  index_t gbi;
+
+  GBP_BD_DBG ("del: %d", bd_id);
+  gbi = gbp_bridge_domain_find (bd_id);
+
+  if (INDEX_INVALID != gbi)
+    {
+      GBP_BD_DBG ("del: %U", format_gbp_bridge_domain,
+                 gbp_bridge_domain_get (gbi));
+      gbp_bridge_domain_unlock (gbi);
+
+      return (0);
+    }
+
+  return (VNET_API_ERROR_NO_SUCH_ENTRY);
+}
+
+void
+gbp_bridge_domain_walk (gbp_bridge_domain_cb_t cb, void *ctx)
+{
+  gbp_bridge_domain_t *gbpe;
+
+  /* *INDENT-OFF* */
+  pool_foreach(gbpe, gbp_bridge_domain_pool,
+  {
+    if (!cb(gbpe, ctx))
+      break;
+  });
+  /* *INDENT-ON* */
+}
+
+static clib_error_t *
+gbp_bridge_domain_cli (vlib_main_t * vm,
+                      unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  vnet_main_t *vnm = vnet_get_main ();
+  u32 uu_fwd_sw_if_index = ~0;
+  u32 bvi_sw_if_index = ~0;
+  u32 bd_id = ~0;
+  u8 add = 1;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "bvi %U", unformat_vnet_sw_interface,
+                   vnm, &bvi_sw_if_index))
+       ;
+      else if (unformat (input, "uu-flood %U", unformat_vnet_sw_interface,
+                        vnm, &uu_fwd_sw_if_index))
+       ;
+      else if (unformat (input, "add"))
+       add = 1;
+      else if (unformat (input, "del"))
+       add = 0;
+      else if (unformat (input, "bd %d", &bd_id))
+       ;
+      else
+       break;
+    }
+
+  if (~0 == bd_id)
+    return clib_error_return (0, "EPG-ID must be specified");
+
+  if (add)
+    {
+      if (~0 == bvi_sw_if_index)
+       return clib_error_return (0, "interface must be specified");
+
+      gbp_bridge_domain_add_and_lock (bd_id,
+                                     bvi_sw_if_index, uu_fwd_sw_if_index);
+    }
+  else
+    gbp_bridge_domain_delete (bd_id);
+
+  return (NULL);
+}
+
+/*?
+ * Configure a GBP bridge-domain
+ *
+ * @cliexpar
+ * @cliexstart{set gbp bridge-domain [del] bd <ID> bvi <interface> uu-flood <interface>}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_bridge_domain_cli_node, static) = {
+  .path = "gbp bridge-domain",
+  .short_help = "gbp bridge-domain [del] epg bd <ID> bvi <interface> uu-flood <interface>",
+  .function = gbp_bridge_domain_cli,
+};
+
+u8 *
+format_gbp_bridge_domain (u8 * s, va_list * args)
+{
+  gbp_bridge_domain_t *gb = va_arg (*args, gbp_bridge_domain_t*);
+  vnet_main_t *vnm = vnet_get_main ();
+
+  if (NULL != gb)
+    s = format (s, "[%d] bd:[%d,%d], bvi:%U uu-flood:%U locks:%d",
+                gb - gbp_bridge_domain_pool,
+                gb->gb_bd_id,
+                gb->gb_bd_index,
+                format_vnet_sw_if_index_name, vnm, gb->gb_bvi_sw_if_index,
+                format_vnet_sw_if_index_name, vnm, gb->gb_uu_fwd_sw_if_index,
+                gb->gb_locks);
+  else
+    s = format (s, "NULL");
+
+  return (s);
+}
+
+static int
+gbp_bridge_domain_show_one (gbp_bridge_domain_t *gb, void *ctx)
+{
+  vlib_main_t *vm;
+
+  vm = ctx;
+  vlib_cli_output (vm, "  %U",format_gbp_bridge_domain, gb);
+
+  return (1);
+}
+
+static clib_error_t *
+gbp_bridge_domain_show (vlib_main_t * vm,
+                  unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  vlib_cli_output (vm, "Bridge-Domains:");
+  gbp_bridge_domain_walk (gbp_bridge_domain_show_one, vm);
+
+  return (NULL);
+}
+
+
+/*?
+ * Show Group Based Policy Bridge_Domains and derived information
+ *
+ * @cliexpar
+ * @cliexstart{show gbp bridge_domain}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_bridge_domain_show_node, static) = {
+  .path = "show gbp bridge-domain",
+  .short_help = "show gbp bridge-domain\n",
+  .function = gbp_bridge_domain_show,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+gbp_bridge_domain_init (vlib_main_t * vm)
+{
+  gb_logger = vlib_log_register_class ("gbp", "bd");
+
+  return (NULL);
+}
+
+VLIB_INIT_FUNCTION (gbp_bridge_domain_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/gbp/gbp_bridge_domain.h b/src/plugins/gbp/gbp_bridge_domain.h
new file mode 100644 (file)
index 0000000..992900b
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * 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 __GBP_BRIDGE_DOMAIN_H__
+#define __GBP_BRIDGE_DOMAIN_H__
+
+#include <plugins/gbp/gbp_types.h>
+
+#include <vnet/fib/fib_types.h>
+
+/**
+ * A bridge Domain Representation.
+ * This is a standard bridge-domain plus all the attributes it must
+ * have to supprt the GBP model.
+ */
+typedef struct gpb_bridge_domain_t_
+{
+  /**
+   * Bridge-domain ID
+   */
+  u32 gb_bd_id;
+  u32 gb_bd_index;
+
+  /**
+   * The BD's BVI interface (obligatory)
+   */
+  u32 gb_bvi_sw_if_index;
+
+  /**
+   * The BD's MAC spine-proxy interface (optional)
+   */
+  u32 gb_uu_fwd_sw_if_index;
+
+  /**
+   * The BD's VNI interface on which packets from unkown endpoints
+   * arrive
+   */
+  u32 gb_vni_sw_if_index;
+
+  /**
+   * locks/references to the BD so it does not get deleted (from the API)
+   * whilst it is still being used
+   */
+  u32 gb_locks;
+} gbp_bridge_domain_t;
+
+extern int gbp_bridge_domain_add_and_lock (u32 bd_id,
+                                          u32 bvi_sw_if_index,
+                                          u32 uu_fwd_sw_if_index);
+extern void gbp_bridge_domain_unlock (index_t gbi);
+extern index_t gbp_bridge_domain_find_and_lock (u32 bd_id);
+extern int gbp_bridge_domain_delete (u32 bd_id);
+extern gbp_bridge_domain_t *gbp_bridge_domain_get (index_t i);
+
+typedef int (*gbp_bridge_domain_cb_t) (gbp_bridge_domain_t * gb, void *ctx);
+extern void gbp_bridge_domain_walk (gbp_bridge_domain_cb_t bgpe, void *ctx);
+
+extern u8 *format_gbp_bridge_domain (u8 * s, va_list * args);
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
index 3dc6699..fb57426 100644 (file)
@@ -18,6 +18,8 @@
 #include <plugins/gbp/gbp.h>
 #include <vnet/l2/l2_input.h>
 #include <vnet/l2/feat_bitmap.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/vxlan-gbp/vxlan_gbp_packet.h>
 
 typedef enum gbp_src_classify_type_t_
 {
@@ -56,7 +58,7 @@ always_inline uword
 gbp_classify_inline (vlib_main_t * vm,
                     vlib_node_runtime_t * node,
                     vlib_frame_t * frame,
-                    gbp_src_classify_type_t type, u8 is_l3)
+                    gbp_src_classify_type_t type, dpo_proto_t dproto)
 {
   gbp_src_classify_main_t *gscm = &gbp_src_classify_main;
   u32 n_left_from, *from, *to_next;
@@ -75,7 +77,7 @@ gbp_classify_inline (vlib_main_t * vm,
       while (n_left_from > 0 && n_left_to_next > 0)
        {
          u32 next0, bi0, src_epg, sw_if_index0;
-         const gbp_endpoint_t *gep0;
+         const gbp_endpoint_t *ge0;
          vlib_buffer_t *b0;
 
          bi0 = from[0];
@@ -88,6 +90,7 @@ gbp_classify_inline (vlib_main_t * vm,
          b0 = vlib_get_buffer (vm, bi0);
 
          sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+         vnet_buffer2 (b0)->gbp.flags = VXLAN_GBP_GPFLAGS_NONE;
 
          if (GBP_SRC_CLASSIFY_NULL == type)
            {
@@ -98,10 +101,46 @@ gbp_classify_inline (vlib_main_t * vm,
            }
          else
            {
-             gep0 = gbp_endpoint_get_itf (sw_if_index0);
-             src_epg = gep0->ge_epg_id;
-             if (is_l3)
+             if (DPO_PROTO_ETHERNET == dproto)
                {
+                 const ethernet_header_t *h0;
+
+                 h0 = vlib_buffer_get_current (b0);
+                 next0 =
+                   vnet_l2_feature_next (b0, gscm->l2_input_feat_next[type],
+                                         L2INPUT_FEAT_GBP_SRC_CLASSIFY);
+                 ge0 = gbp_endpoint_find_mac (h0->src_address,
+                                              vnet_buffer (b0)->l2.bd_index);
+               }
+             else if (DPO_PROTO_IP4 == dproto)
+               {
+                 const ip4_header_t *h0;
+
+                 h0 = vlib_buffer_get_current (b0);
+
+                 ge0 = gbp_endpoint_find_ip4
+                   (&h0->src_address,
+                    fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4,
+                                                         sw_if_index0));
+
+
+                 /*
+                  * Go straight to looukp, do not pass go, do not collect $200
+                  */
+                 next0 = 0;
+               }
+             else if (DPO_PROTO_IP6 == dproto)
+               {
+                 const ip6_header_t *h0;
+
+                 h0 = vlib_buffer_get_current (b0);
+
+                 ge0 = gbp_endpoint_find_ip6
+                   (&h0->src_address,
+                    fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6,
+                                                         sw_if_index0));
+
+
                  /*
                   * Go straight to lookup, do not pass go, do not collect $200
                   */
@@ -109,10 +148,15 @@ gbp_classify_inline (vlib_main_t * vm,
                }
              else
                {
-                 next0 =
-                   vnet_l2_feature_next (b0, gscm->l2_input_feat_next[type],
-                                         L2INPUT_FEAT_GBP_SRC_CLASSIFY);
+                 ge0 = NULL;
+                 next0 = 0;
+                 ASSERT (0);
                }
+
+             if (PREDICT_TRUE (NULL != ge0))
+               src_epg = ge0->ge_epg_id;
+             else
+               src_epg = EPG_INVALID;
            }
 
          vnet_buffer2 (b0)->gbp.src_epg = src_epg;
@@ -139,28 +183,32 @@ static uword
 gbp_src_classify (vlib_main_t * vm,
                  vlib_node_runtime_t * node, vlib_frame_t * frame)
 {
-  return (gbp_classify_inline (vm, node, frame, GBP_SRC_CLASSIFY_PORT, 0));
+  return (gbp_classify_inline (vm, node, frame,
+                              GBP_SRC_CLASSIFY_PORT, DPO_PROTO_ETHERNET));
 }
 
 static uword
 gbp_null_classify (vlib_main_t * vm,
                   vlib_node_runtime_t * node, vlib_frame_t * frame)
 {
-  return (gbp_classify_inline (vm, node, frame, GBP_SRC_CLASSIFY_NULL, 0));
+  return (gbp_classify_inline (vm, node, frame,
+                              GBP_SRC_CLASSIFY_NULL, DPO_PROTO_ETHERNET));
 }
 
 static uword
 gbp_ip4_src_classify (vlib_main_t * vm,
                      vlib_node_runtime_t * node, vlib_frame_t * frame)
 {
-  return (gbp_classify_inline (vm, node, frame, 0, 1));
+  return (gbp_classify_inline (vm, node, frame,
+                              GBP_SRC_CLASSIFY_PORT, DPO_PROTO_IP4));
 }
 
 static uword
 gbp_ip6_src_classify (vlib_main_t * vm,
                      vlib_node_runtime_t * node, vlib_frame_t * frame)
 {
-  return (gbp_classify_inline (vm, node, frame, 0, 1));
+  return (gbp_classify_inline (vm, node, frame,
+                              GBP_SRC_CLASSIFY_PORT, DPO_PROTO_IP6));
 }
 
 
index a261527..79c140f 100644 (file)
 
 #include <plugins/gbp/gbp_endpoint.h>
 #include <plugins/gbp/gbp_endpoint_group.h>
-
-#include <vnet/ethernet/arp_packet.h>
+#include <plugins/gbp/gbp_itf.h>
+#include <plugins/gbp/gbp_scanner.h>
+#include <plugins/gbp/gbp_bridge_domain.h>
+#include <plugins/gbp/gbp_route_domain.h>
+#include <plugins/gbp/gbp_policy_dpo.h>
+#include <plugins/gbp/gbp_vxlan.h>
+
+#include <vnet/ethernet/arp.h>
 #include <vnet/l2/l2_input.h>
 #include <vnet/l2/l2_output.h>
 #include <vnet/l2/feat_bitmap.h>
+#include <vnet/l2/l2_fib.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/ip/ip_neighbor.h>
+
+static const char *gbp_endpoint_attr_names[] = GBP_ENDPOINT_ATTR_NAMES;
+
+/**
+ * EP DBs
+ */
+gbp_ep_db_t gbp_ep_db;
+
+vlib_log_class_t gbp_ep_logger;
 
-gbp_ep_by_itf_db_t gbp_ep_by_itf_db;
-gbp_ep_by_mac_itf_db_t gbp_ep_by_mac_itf_db;
-gbp_ep_by_ip_itf_db_t gbp_ep_by_ip_itf_db;
+#define GBP_ENDPOINT_DBG(...)                           \
+    vlib_log_debug (gbp_ep_logger, __VA_ARGS__);
+
+#define GBP_ENDPOINT_INFO(...)                          \
+    vlib_log_notice (gbp_ep_logger, __VA_ARGS__);
+
+/**
+ * GBP Endpoint inactive timeout (in seconds)
+ * If a dynamically learned Endpoint has not been heard from in this
+ * amount of time it is considered inactive and discarded
+ */
+static u32 GBP_ENDPOINT_INACTIVE_TIME = 30;
 
 /**
  * Pool of GBP endpoints
  */
 gbp_endpoint_t *gbp_endpoint_pool;
 
-/* void */
-/* gbp_itf_epg_update (u32 sw_if_index, epg_id_t src_epg, u8 do_policy) */
-/* { */
-/*   vec_validate_init_empty (gbp_itf_to_epg_db.gte_vec, */
-/*                        sw_if_index, ITF_INVALID); */
-
-/*   if (0 == gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count) */
-/*     { */
-/*       l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_SRC_CLASSIFY, */
-/*                               1); */
-/*       l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_FWD, 1); */
-/*       if (do_policy) */
-/*     l2output_intf_bitmap_enable (sw_if_index, L2OUTPUT_FEAT_GBP_POLICY, */
-/*                                  1); */
-/*     } */
-/*   gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg = src_epg; */
-/*   gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count++; */
-/* } */
-
-/* void */
-/* gbp_itf_epg_delete (u32 sw_if_index) */
-/* { */
-/*   if (vec_len (gbp_itf_to_epg_db.gte_vec) <= sw_if_index) */
-/*     return; */
-
-/*   if (1 == gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count) */
-/*     { */
-/*       gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_epg = EPG_INVALID; */
-
-/*       l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_SRC_CLASSIFY, */
-/*                               0); */
-/*       l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_FWD, 0); */
-/*       l2output_intf_bitmap_enable (sw_if_index, L2OUTPUT_FEAT_GBP_POLICY, 0); */
-/*     } */
-/*   gbp_itf_to_epg_db.gte_vec[sw_if_index].gi_ref_count--; */
-/* } */
-
-static void
-gbp_endpoint_mk_key_mac_itf (const mac_address_t * mac,
-                            u32 sw_if_index, clib_bihash_kv_16_8_t * key)
-{
-  key->key[0] = mac_address_as_u64 (mac);
-  key->key[1] = sw_if_index;
-}
+/**
+ * A count of the number of dynamic entries
+ */
+static u32 gbp_n_learnt_endpoints;
 
-static void
-gbp_endpoint_extract_key_mac_itf (const clib_bihash_kv_16_8_t * key,
-                                 mac_address_t * mac, u32 * sw_if_index)
-{
-  mac_address_from_u64 (key->key[0], mac);
-  *sw_if_index = key->key[1];
-}
+#define FOR_EACH_GBP_ENDPOINT_ATTR(_item)              \
+    for (_item = GBP_ENDPOINT_ATTR_FIRST;              \
+        _item < GBP_ENDPOINT_ATTR_LAST;                \
+        _item++)
 
-gbp_endpoint_t *
-gbp_endpoint_find_mac_itf (const mac_address_t * mac, u32 sw_if_index)
+u8 *
+format_gbp_endpoint_flags (u8 * s, va_list * args)
 {
-  clib_bihash_kv_16_8_t key, value;
-  int rv;
-
-  gbp_endpoint_mk_key_mac_itf (mac, sw_if_index, &key);
+  gbp_endpoint_attr_t attr;
+  gbp_endpoint_flags_t flags = va_arg (*args, gbp_endpoint_flags_t);
 
-  rv =
-    clib_bihash_search_16_8 (&gbp_ep_by_mac_itf_db.gte_table, &key, &value);
+  FOR_EACH_GBP_ENDPOINT_ATTR (attr)
+  {
+    if ((1 << attr) & flags)
+      {
+       s = format (s, "%s,", gbp_endpoint_attr_names[attr]);
+      }
+  }
 
-  if (0 != rv)
-    return NULL;
+  return (s);
+}
 
-  return (gbp_endpoint_get (value.value));
+int
+gbp_endpoint_is_remote (const gbp_endpoint_t * ge)
+{
+  return (ge->ge_flags & GBP_ENDPOINT_FLAG_REMOTE);
 }
 
 static void
-gbp_endpoint_mk_key_ip_itf (const ip46_address_t * ip,
-                           u32 sw_if_index, clib_bihash_kv_24_8_t * key)
+gbp_endpoint_extract_key_mac_itf (const clib_bihash_kv_16_8_t * key,
+                                 mac_address_t * mac, u32 * sw_if_index)
 {
-  key->key[0] = ip->as_u64[0];
-  key->key[1] = ip->as_u64[1];
-  key->key[2] = sw_if_index;
+  mac_address_from_u64 (key->key[0], mac);
+  *sw_if_index = key->key[1];
 }
 
 static void
@@ -121,14 +110,14 @@ gbp_endpoint_extract_key_ip_itf (const clib_bihash_kv_24_8_t * key,
 }
 
 gbp_endpoint_t *
-gbp_endpoint_find_ip_itf (const ip46_address_t * ip, u32 sw_if_index)
+gbp_endpoint_find_ip (const ip46_address_t * ip, u32 fib_index)
 {
   clib_bihash_kv_24_8_t key, value;
   int rv;
 
-  gbp_endpoint_mk_key_ip_itf (ip, sw_if_index, &key);
+  gbp_endpoint_mk_key_ip (ip, fib_index, &key);
 
-  rv = clib_bihash_search_24_8 (&gbp_ep_by_ip_itf_db.gte_table, &key, &value);
+  rv = clib_bihash_search_24_8 (&gbp_ep_db.ged_by_ip_rd, &key, &value);
 
   if (0 != rv)
     return NULL;
@@ -136,238 +125,552 @@ gbp_endpoint_find_ip_itf (const ip46_address_t * ip, u32 sw_if_index)
   return (gbp_endpoint_get (value.value));
 }
 
-gbp_endpoint_t *
-gbp_endpoint_find_itf (u32 sw_if_index)
+static void
+gbp_endpoint_add_itf (u32 sw_if_index, index_t gei)
 {
-  /* if (vec_len(gbp_ep_by_itf_db.gte_vec) >= sw_if_index) */
-  /*   return NULL; */
+  vec_validate_init_empty (gbp_ep_db.ged_by_sw_if_index, sw_if_index, ~0);
 
-  /* vec_search(gbp_ep_by_itf_db.gte_vec[sw_if_index],  */
-  /* return (gbp_endpoint_get(gbp_ep_by_itf_db.gte_vec[sw_if_index][0])); */
-  return (NULL);
+  gbp_ep_db.ged_by_sw_if_index[sw_if_index] = gei;
 }
 
 static bool
-gbp_endpoint_add_mac_itf (const mac_address_t * mac,
-                         u32 sw_if_index, index_t gbpei)
+gbp_endpoint_add_mac (const mac_address_t * mac, u32 bd_index, index_t gei)
 {
   clib_bihash_kv_16_8_t key;
   int rv;
 
-  gbp_endpoint_mk_key_mac_itf (mac, sw_if_index, &key);
-  key.value = gbpei;
+  gbp_endpoint_mk_key_mac (mac->bytes, bd_index, &key);
+  key.value = gei;
+
+  rv = clib_bihash_add_del_16_8 (&gbp_ep_db.ged_by_mac_bd, &key, 1);
 
-  rv = clib_bihash_add_del_16_8 (&gbp_ep_by_mac_itf_db.gte_table, &key, 1);
 
   return (0 == rv);
 }
 
 static bool
-gbp_endpoint_add_ip_itf (const ip46_address_t * ip,
-                        u32 sw_if_index, index_t gbpei)
+gbp_endpoint_add_ip (const ip46_address_t * ip, u32 fib_index, index_t gei)
 {
   clib_bihash_kv_24_8_t key;
   int rv;
 
-  gbp_endpoint_mk_key_ip_itf (ip, sw_if_index, &key);
-  key.value = gbpei;
+  gbp_endpoint_mk_key_ip (ip, fib_index, &key);
+  key.value = gei;
 
-  rv = clib_bihash_add_del_24_8 (&gbp_ep_by_ip_itf_db.gte_table, &key, 1);
+  rv = clib_bihash_add_del_24_8 (&gbp_ep_db.ged_by_ip_rd, &key, 1);
 
   return (0 == rv);
 }
 
 static void
-gbp_endpoint_add_itf (u32 sw_if_index, index_t gbpei)
-{
-  vec_validate_init_empty (gbp_ep_by_itf_db.gte_vec, sw_if_index,
-                          INDEX_INVALID);
-
-  if (INDEX_INVALID == gbp_ep_by_itf_db.gte_vec[sw_if_index])
-    {
-      l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_SRC_CLASSIFY,
-                                 1);
-      l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_FWD, 1);
-      l2output_intf_bitmap_enable (sw_if_index, L2OUTPUT_FEAT_GBP_POLICY, 1);
-    }
-  gbp_ep_by_itf_db.gte_vec[sw_if_index] = gbpei;
-}
-
-static void
-gbp_endpoint_del_mac_itf (const mac_address_t * mac, u32 sw_if_index)
+gbp_endpoint_del_mac (const mac_address_t * mac, u32 bd_index)
 {
   clib_bihash_kv_16_8_t key;
 
-  gbp_endpoint_mk_key_mac_itf (mac, sw_if_index, &key);
+  gbp_endpoint_mk_key_mac (mac->bytes, bd_index, &key);
 
-  clib_bihash_add_del_16_8 (&gbp_ep_by_mac_itf_db.gte_table, &key, 0);
+  clib_bihash_add_del_16_8 (&gbp_ep_db.ged_by_mac_bd, &key, 0);
 }
 
 static void
-gbp_endpoint_del_ip_itf (const ip46_address_t * ip, u32 sw_if_index)
+gbp_endpoint_del_ip (const ip46_address_t * ip, u32 fib_index)
 {
   clib_bihash_kv_24_8_t key;
 
-  gbp_endpoint_mk_key_ip_itf (ip, sw_if_index, &key);
+  gbp_endpoint_mk_key_ip (ip, fib_index, &key);
 
-  clib_bihash_add_del_24_8 (&gbp_ep_by_ip_itf_db.gte_table, &key, 0);
+  clib_bihash_add_del_24_8 (&gbp_ep_db.ged_by_ip_rd, &key, 0);
 }
 
-static void
-gbp_endpoint_del_itf (u32 sw_if_index)
+static index_t
+gbp_endpoint_index (const gbp_endpoint_t * ge)
 {
-  if (vec_len (gbp_ep_by_itf_db.gte_vec) <= sw_if_index)
-    return;
+  return (ge - gbp_endpoint_pool);
+}
 
-  l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_SRC_CLASSIFY, 0);
-  l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_FWD, 0);
-  l2output_intf_bitmap_enable (sw_if_index, L2OUTPUT_FEAT_GBP_POLICY, 0);
+static ip46_type_t
+ip46_address_get_type (const ip46_address_t * a)
+{
+  return (ip46_address_is_ip4 (a) ? IP46_TYPE_IP4 : IP46_TYPE_IP6);
+}
 
-  gbp_ep_by_itf_db.gte_vec[sw_if_index] = INDEX_INVALID;
+static ip46_type_t
+ip46_address_get_len (const ip46_address_t * a)
+{
+  return (ip46_address_is_ip4 (a) ? 32 : 128);
 }
 
-static index_t
-gbp_endpoint_index (const gbp_endpoint_t * gbpe)
+static gbp_endpoint_t *
+gbp_endpoint_alloc (epg_id_t epg_id,
+                   index_t ggi, u32 sw_if_index, gbp_endpoint_flags_t flags,
+                   const ip46_address_t * tun_src,
+                   const ip46_address_t * tun_dst)
 {
-  return (gbpe - gbp_endpoint_pool);
+  gbp_endpoint_t *ge;
+
+  pool_get_zero (gbp_endpoint_pool, ge);
+
+  ge->ge_epg = ggi;
+  ge->ge_epg_id = epg_id;
+  ge->ge_flags = flags;
+  ge->ge_sw_if_index = sw_if_index;
+  ge->ge_last_time = vlib_time_now (vlib_get_main ());
+
+  gbp_endpoint_group_find_and_lock (epg_id);
+
+  if (gbp_endpoint_is_remote (ge))
+    {
+      if (NULL != tun_src)
+       ip46_address_copy (&ge->tun.ge_src, tun_src);
+      if (NULL != tun_dst)
+       ip46_address_copy (&ge->tun.ge_dst, tun_dst);
+
+      /*
+       * the input interface may be the parent GBP-vxlan interface,
+       * create a child vlxan-gbp tunnel and use that as the endpoint's
+       * interface.
+       */
+      switch (gbp_vxlan_tunnel_get_type (sw_if_index))
+       {
+       case GBP_VXLAN_TEMPLATE_TUNNEL:
+         ge->tun.ge_parent_sw_if_index = sw_if_index;
+         ge->ge_sw_if_index =
+           gbp_vxlan_tunnel_clone_and_lock (sw_if_index, tun_src, tun_dst);
+         break;
+       case VXLAN_GBP_TUNNEL:
+         ge->tun.ge_parent_sw_if_index =
+           vxlan_gbp_tunnel_get_parent (sw_if_index);
+         ge->ge_sw_if_index = sw_if_index;
+         vxlan_gbp_tunnel_lock (ge->ge_sw_if_index);
+         break;
+       }
+    }
+
+  return (ge);
 }
 
 int
 gbp_endpoint_update (u32 sw_if_index,
                     const ip46_address_t * ips,
-                    const mac_address_t * mac, epg_id_t epg_id, u32 * handle)
+                    const mac_address_t * mac,
+                    epg_id_t epg_id,
+                    gbp_endpoint_flags_t flags,
+                    const ip46_address_t * tun_src,
+                    const ip46_address_t * tun_dst, u32 * handle)
 {
-  gbp_endpoint_group_t *gepg;
-  const ip46_address_t *ip;
-  gbp_endpoint_t *gbpe;
+  gbp_endpoint_group_t *gg;
+  gbp_endpoint_t *ge;
+  index_t ggi, gei;
+
+  if (~0 == sw_if_index)
+    return (VNET_API_ERROR_INVALID_SW_IF_INDEX);
 
-  gbpe = NULL;
-  gepg = gbp_endpoint_group_find (epg_id);
+  ge = NULL;
+  ggi = gbp_endpoint_group_find_and_lock (epg_id);
 
-  if (NULL == gepg)
+  if (INDEX_INVALID == ggi)
     return (VNET_API_ERROR_NO_SUCH_ENTRY);
 
+  gg = gbp_endpoint_group_get (ggi);
+
   /*
-   * find an existing endpoint matching one of the key types
+   * L2 EP
    */
-  if (NULL != mac)
-    {
-      gbpe = gbp_endpoint_find_mac_itf (mac, sw_if_index);
-    }
-  if (NULL == gbpe && NULL != ips)
-    {
-      vec_foreach (ip, ips)
-      {
-       gbpe = gbp_endpoint_find_ip_itf (ip, sw_if_index);
-
-       if (NULL != gbpe)
-         break;
-      }
-    }
-  if (NULL == gbpe)
+  if (NULL != mac && !mac_address_is_zero (mac))
     {
-      gbpe = gbp_endpoint_find_itf (sw_if_index);
+      /*
+       * find an existing endpoint matching one of the key types
+       */
+      ge = gbp_endpoint_find_mac (mac->bytes, gg->gg_bd_index);
+      if (NULL == ge)
+       {
+         /*
+          * new entry
+          */
+         ge = gbp_endpoint_alloc (epg_id, ggi, sw_if_index, flags,
+                                  tun_src, tun_dst);
+         gei = gbp_endpoint_index (ge);
+         mac_address_copy (&ge->ge_mac, mac);
+
+         ge->ge_itf = gbp_itf_add_and_lock (ge->ge_sw_if_index,
+                                            gg->gg_bd_index);
+
+         gbp_itf_set_l2_input_feature (ge->ge_itf, gei,
+                                       L2INPUT_FEAT_GBP_FWD);
+
+         if (gbp_endpoint_is_remote (ge))
+           {
+             gbp_itf_set_l2_output_feature (ge->ge_itf, gei,
+                                            L2OUTPUT_FEAT_GBP_POLICY_MAC);
+           }
+         else
+           {
+             gbp_endpoint_add_itf (ge->ge_sw_if_index, gei);
+             gbp_itf_set_l2_output_feature (ge->ge_itf, gei,
+                                            L2OUTPUT_FEAT_GBP_POLICY_PORT);
+           }
+
+         gbp_endpoint_add_mac (mac, gg->gg_bd_index, gei);
+
+         l2fib_add_entry (mac->bytes, gg->gg_bd_index, ge->ge_sw_if_index,
+                          L2FIB_ENTRY_RESULT_FLAG_STATIC);
+       }
+      else
+       {
+         /*
+          * update existing entry..
+          */
+         ge->ge_flags = flags;
+         gei = gbp_endpoint_index (ge);
+         goto out;
+       }
     }
 
-  if (NULL == gbpe)
+  /*
+   * L3 EP
+   */
+  if (NULL != ips && !ip46_address_is_zero (ips))
     {
-      index_t gbpei;
+      const ip46_address_t *ip;
+      fib_protocol_t fproto;
+      gbp_endpoint_t *l3_ge;
       u32 ii;
+
       /*
-       * new entry
+       * look for a matching EP by any of the address
+       * An EP's IP addresses cannot change so we can search based on
+       * the first
        */
-      pool_get (gbp_endpoint_pool, gbpe);
-      gbpei = gbp_endpoint_index (gbpe);
+      fproto = fib_proto_from_ip46 (ip46_address_get_type (&ips[0]));
 
-      gbpe->ge_epg_id = epg_id;
-      gbpe->ge_sw_if_index = sw_if_index;
-      gbp_endpoint_add_itf (gbpe->ge_sw_if_index, gbpei);
-
-      if (NULL != mac)
+      l3_ge = gbp_endpoint_find_ip (&ips[0],
+                                   gbp_endpoint_group_get_fib_index (gg,
+                                                                     fproto));
+      if (NULL == l3_ge)
        {
-         gbpe->ge_mac = *mac;
-
-         // FIXME ERROR
-         gbp_endpoint_add_mac_itf (mac, sw_if_index, gbpei);
+         if (NULL == ge)
+           {
+             ge = gbp_endpoint_alloc (epg_id, ggi, sw_if_index, flags,
+                                      tun_src, tun_dst);
+             ge->ge_itf = gbp_itf_add_and_lock (sw_if_index, ~0);
+           }
+         else
+           /* L2 EP with IPs */
+           gei = gbp_endpoint_index (ge);
        }
-
-      if (NULL != ips)
+      else
        {
-         vec_validate (gbpe->ge_ips, vec_len (ips) - 1);
-         vec_foreach_index (ii, ips)
+         /* modify */
+         ge = l3_ge;
+         ge->ge_flags = flags;
+         gei = gbp_endpoint_index (ge);
+         goto out;
+       }
+
+      gei = gbp_endpoint_index (ge);
+      ge->ge_ips = ips;
+      vec_validate (ge->ge_adjs, vec_len (ips) - 1);
+
+      vec_foreach_index (ii, ge->ge_ips)
+      {
+       ethernet_header_t *eth;
+       ip46_type_t ip_type;
+       u32 ip_sw_if_index;
+       u8 *rewrite;
+
+       rewrite = NULL;
+       ip = &ge->ge_ips[ii];
+       ip_type = ip46_address_get_type (ip);
+       fproto = fib_proto_from_ip46 (ip_type);
+
+       bd_add_del_ip_mac (gg->gg_bd_index, ip_type, ip, &ge->ge_mac, 1);
+
+       // FIXME - check error
+       gbp_endpoint_add_ip (ip,
+                            gbp_endpoint_group_get_fib_index (gg, fproto),
+                            gei);
+
+       /*
+        * add a host route via the EPG's BVI we need this because the
+        * adj fib does not install, due to cover refinement check, since
+        * the BVI's prefix is /32
+        */
+       fib_prefix_t pfx = {
+         .fp_proto = fproto,
+         .fp_len = ip46_address_get_len (ip),
+         .fp_addr = *ip,
+       };
+       vec_validate (rewrite, sizeof (*eth) - 1);
+       eth = (ethernet_header_t *) rewrite;
+
+       eth->type = clib_host_to_net_u16 ((fproto == FIB_PROTOCOL_IP4 ?
+                                          ETHERNET_TYPE_IP4 :
+                                          ETHERNET_TYPE_IP6));
+
+       if (gbp_endpoint_is_remote (ge))
          {
-           ip46_address_copy (&gbpe->ge_ips[ii], &ips[ii]);
+           /*
+            * for dynamic EPs we msut add the IP adjacency via the learned
+            * tunnel since the BD will not contain the EP's MAC since it was
+            * L3 learned. The dst MAC address used is the 'BD's MAC'.
+            */
+           ip_sw_if_index = ge->ge_sw_if_index;
+
+           mac_address_to_bytes (gbp_route_domain_get_local_mac (),
+                                 eth->src_address);
+           mac_address_to_bytes (gbp_route_domain_get_remote_mac (),
+                                 eth->dst_address);
+         }
+       else
+         {
+           /*
+            * for the static EPs we add the IP adjacency via the BVI
+            * knowing that the BD has the MAC address to route to and
+            * that policy will be applied on egress to the EP's port
+            */
+           ip_sw_if_index = gbp_endpoint_group_get_bvi (gg);
+
+           clib_memcpy (eth->src_address,
+                        vnet_sw_interface_get_hw_address (vnet_get_main (),
+                                                          ip_sw_if_index),
+                        sizeof (eth->src_address));
+           mac_address_to_bytes (&ge->ge_mac, eth->dst_address);
+         }
+
+       fib_table_entry_path_add
+         (gbp_endpoint_group_get_fib_index (gg, fproto),
+          &pfx, FIB_SOURCE_PLUGIN_LOW,
+          FIB_ENTRY_FLAG_NONE,
+          fib_proto_to_dpo (fproto), ip, ip_sw_if_index,
+          ~0, 1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
 
-           // FIXME ERROR
-           gbp_endpoint_add_ip_itf (&ips[ii], sw_if_index, gbpei);
+       ge->ge_adjs[ii] = adj_nbr_add_or_lock_w_rewrite (fproto,
+                                                        fib_proto_to_link
+                                                        (fproto), ip,
+                                                        ip_sw_if_index,
+                                                        rewrite);
+
+       if (gbp_endpoint_is_remote (ge))
+         {
+           dpo_id_t policy_dpo = DPO_INVALID;
 
            /*
-            * send a gratuitous ARP on the EPG's uplink. this is done so
-            * that if this EP has moved from some other place in the
-            * 'fabric', upstream devices are informed
+            * interpose a policy DPO from the endpoint so that policy
+            * is applied
             */
-           if (ip46_address_is_ip4 (&ips[ii]))
+           gbp_policy_dpo_add_or_lock (fib_proto_to_dpo (fproto),
+                                       gg->gg_id, ~0, &policy_dpo);
+
+           fib_table_entry_special_dpo_add
+             (gbp_endpoint_group_get_fib_index (gg, fproto),
+              &pfx,
+              FIB_SOURCE_PLUGIN_HI, FIB_ENTRY_FLAG_INTERPOSE, &policy_dpo);
+         }
+
+       /*
+        * send a gratuitous ARP on the EPG's uplink. this is done so
+        * that if this EP has moved from some other place in the
+        * 'fabric', upstream devices are informed
+        */
+       if (!(gbp_endpoint_is_remote (ge)) && ~0 != gg->gg_uplink_sw_if_index)
+         {
+           gbp_endpoint_add_itf (sw_if_index, gei);
+           if (ip46_address_is_ip4 (ip))
              send_ip4_garp_w_addr (vlib_get_main (),
-                                   &ips[ii].ip4,
-                                   gepg->gepg_uplink_sw_if_index);
+                                   &ip->ip4, gg->gg_uplink_sw_if_index);
            else
              send_ip6_na_w_addr (vlib_get_main (),
-                                 &ips[ii].ip6,
-                                 gepg->gepg_uplink_sw_if_index);
+                                 &ip->ip6, gg->gg_uplink_sw_if_index);
          }
+      }
+    }
+
+  if (NULL == ge)
+    return (0);
+
+  /*
+   * count the number of dynamic entries and kick off the scanner
+   * process is this is our first.
+   */
+  if (gbp_endpoint_is_remote (ge))
+    {
+      gbp_n_learnt_endpoints++;
+
+      if (1 == gbp_n_learnt_endpoints)
+       {
+         vlib_process_signal_event (vlib_get_main (),
+                                    gbp_scanner_node.index,
+                                    GBP_ENDPOINT_SCAN_START, 0);
        }
     }
   else
     {
       /*
-       * update existing entry..
+       * non-remote endpoints (i.e. those not arriving on iVXLAN
+       * tunnels) need to be classifed based on the the input interface.
+       * We enable the GBP-FWD feature only is the group has an uplink
+       * interface (on which the GBP-FWD feature would send UU traffic).
        */
-      ASSERT (0);
+      l2input_feat_masks_t feats = L2INPUT_FEAT_GBP_SRC_CLASSIFY;
+
+      if (~0 != gg->gg_uplink_sw_if_index)
+       feats |= L2INPUT_FEAT_GBP_FWD;
+      gbp_itf_set_l2_input_feature (ge->ge_itf, gbp_endpoint_index (ge),
+                                   feats);
     }
+out:
+
+  if (handle)
+    *handle = (ge - gbp_endpoint_pool);
 
-  *handle = (gbpe - gbp_endpoint_pool);
+  gbp_endpoint_group_unlock (ggi);
+  GBP_ENDPOINT_INFO ("update: %U", format_gbp_endpoint, gei);
 
   return (0);
 }
 
 void
-gbp_endpoint_delete (u32 handle)
+gbp_endpoint_delete (index_t gei)
 {
-  gbp_endpoint_t *gbpe;
+  gbp_endpoint_group_t *gg;
+  gbp_endpoint_t *ge;
 
-  if (pool_is_free_index (gbp_endpoint_pool, handle))
+  if (pool_is_free_index (gbp_endpoint_pool, gei))
     return;
 
-  gbpe = pool_elt_at_index (gbp_endpoint_pool, handle);
+  GBP_ENDPOINT_INFO ("delete: %U", format_gbp_endpoint, gei);
 
-  gbp_endpoint_del_itf (gbpe->ge_sw_if_index);
+  ge = gbp_endpoint_get (gei);
+  gg = gbp_endpoint_group_get (ge->ge_epg);
 
-  if (!mac_address_is_zero (&gbpe->ge_mac))
-    {
-      gbp_endpoint_del_mac_itf (&gbpe->ge_mac, gbpe->ge_sw_if_index);
-    }
+  gbp_endpoint_del_mac (&ge->ge_mac, gg->gg_bd_index);
+  l2fib_del_entry (ge->ge_mac.bytes, gg->gg_bd_index, ge->ge_sw_if_index);
+  gbp_itf_set_l2_input_feature (ge->ge_itf, gei, (L2INPUT_FEAT_NONE));
+  gbp_itf_set_l2_output_feature (ge->ge_itf, gei, L2OUTPUT_FEAT_NONE);
 
-  if (NULL != gbpe->ge_ips)
+  if (NULL != ge->ge_ips)
     {
       const ip46_address_t *ip;
+      index_t *ai;
 
-      vec_foreach (ip, gbpe->ge_ips)
+      vec_foreach (ai, ge->ge_adjs)
       {
-       gbp_endpoint_del_ip_itf (ip, gbpe->ge_sw_if_index);
+       adj_unlock (*ai);
+      }
+      vec_foreach (ip, ge->ge_ips)
+      {
+       fib_protocol_t fproto;
+       ip46_type_t ip_type;
+
+       ip_type = ip46_address_get_type (ip);
+       fproto = fib_proto_from_ip46 (ip_type);
+
+       gbp_endpoint_del_ip (ip,
+                            gbp_endpoint_group_get_fib_index (gg, fproto));
+
+       bd_add_del_ip_mac (gg->gg_bd_index, ip_type, ip, &ge->ge_mac, 0);
+
+       /*
+        * remove a host route via the EPG's BVI
+        */
+       fib_prefix_t pfx = {
+         .fp_proto = fproto,
+         .fp_len = ip46_address_get_len (ip),
+         .fp_addr = *ip,
+       };
+
+       if (gbp_endpoint_is_remote (ge))
+         {
+           fib_table_entry_special_remove
+             (gbp_endpoint_group_get_fib_index (gg, fproto),
+              &pfx, FIB_SOURCE_PLUGIN_HI);
+         }
+
+       fib_table_entry_path_remove
+         (gbp_endpoint_group_get_fib_index (gg, fproto),
+          &pfx, FIB_SOURCE_PLUGIN_LOW,
+          fib_proto_to_dpo (fproto), ip,
+          (gbp_endpoint_is_remote (ge) ?
+           ge->ge_sw_if_index :
+           gbp_endpoint_group_get_bvi (gg)),
+          ~0, 1, FIB_ROUTE_PATH_FLAG_NONE);
       }
     }
-  pool_put (gbp_endpoint_pool, gbpe);
+
+  if (ge->ge_flags & GBP_ENDPOINT_FLAG_LEARNT)
+    {
+      gbp_n_learnt_endpoints--;
+
+      if (0 == gbp_n_learnt_endpoints)
+       {
+         vlib_process_signal_event (vlib_get_main (),
+                                    gbp_scanner_node.index,
+                                    GBP_ENDPOINT_SCAN_STOP, 0);
+       }
+    }
+
+  gbp_itf_unlock (ge->ge_itf);
+  if (gbp_endpoint_is_remote (ge))
+    {
+      vxlan_gbp_tunnel_unlock (ge->ge_sw_if_index);
+    }
+  gbp_endpoint_group_unlock (ge->ge_epg);
+  pool_put (gbp_endpoint_pool, ge);
+}
+
+typedef struct gbp_endpoint_flush_ctx_t_
+{
+  u32 sw_if_index;
+  index_t *geis;
+} gbp_endpoint_flush_ctx_t;
+
+static walk_rc_t
+gbp_endpoint_flush_cb (index_t gei, void *args)
+{
+  gbp_endpoint_flush_ctx_t *ctx = args;
+  gbp_endpoint_t *ge;
+
+  ge = gbp_endpoint_get (gei);
+
+  if (gbp_endpoint_is_remote (ge) &&
+      ctx->sw_if_index == ge->tun.ge_parent_sw_if_index)
+    {
+      vec_add1 (ctx->geis, gei);
+    }
+
+  return (WALK_CONTINUE);
+}
+
+/**
+ * remove all learnt endpoints using the interface
+ */
+void
+gbp_endpoint_flush (u32 sw_if_index)
+{
+  gbp_endpoint_flush_ctx_t ctx = {
+    .sw_if_index = sw_if_index,
+  };
+  index_t *gei;
+
+  gbp_endpoint_walk (gbp_endpoint_flush_cb, &ctx);
+
+  vec_foreach (gei, ctx.geis)
+  {
+    gbp_endpoint_delete (*gei);
+  }
+
+  vec_free (ctx.geis);
 }
 
 void
 gbp_endpoint_walk (gbp_endpoint_cb_t cb, void *ctx)
 {
-  gbp_endpoint_t *gbpe;
+  u32 index;
 
   /* *INDENT-OFF* */
-  pool_foreach(gbpe, gbp_endpoint_pool,
+  pool_foreach_index(index, gbp_endpoint_pool,
   {
-    if (!cb(gbpe, ctx))
+    if (!cb(index, ctx))
       break;
   });
   /* *INDENT-ON* */
@@ -380,7 +683,7 @@ gbp_endpoint_cli (vlib_main_t * vm,
   ip46_address_t ip = ip46_address_initializer, *ips = NULL;
   mac_address_t mac = ZERO_MAC_ADDRESS;
   vnet_main_t *vnm = vnet_get_main ();
-  epg_id_t epg_id = EPG_INVALID;
+  u32 epg_id = EPG_INVALID;
   u32 handle = INDEX_INVALID;
   u32 sw_if_index = ~0;
   u8 add = 1;
@@ -418,7 +721,9 @@ gbp_endpoint_cli (vlib_main_t * vm,
       if (EPG_INVALID == epg_id)
        return clib_error_return (0, "EPG-ID must be specified");
 
-      rv = gbp_endpoint_update (sw_if_index, ips, &mac, epg_id, &handle);
+      rv =
+       gbp_endpoint_update (sw_if_index, ips, &mac, epg_id,
+                            GBP_ENDPOINT_FLAG_NONE, NULL, NULL, &handle);
 
       if (rv)
        return clib_error_return (0, "GBP Endpoint update returned %d", rv);
@@ -457,37 +762,41 @@ VLIB_CLI_COMMAND (gbp_endpoint_cli_node, static) = {
 u8 *
 format_gbp_endpoint (u8 * s, va_list * args)
 {
-  index_t gbpei = va_arg (*args, index_t);
-  vnet_main_t *vnm = vnet_get_main ();
+  index_t gei = va_arg (*args, index_t);
   const ip46_address_t *ip;
-  gbp_endpoint_t *gbpe;
+  gbp_endpoint_t *ge;
 
-  gbpe = gbp_endpoint_get (gbpei);
+  ge = gbp_endpoint_get (gei);
 
-  s = format (s, "[@%d] ", gbpei);
-  s =
-    format (s, "%U", format_vnet_sw_if_index_name, vnm, gbpe->ge_sw_if_index);
-  s = format (s, ", IPs:[");
+  s = format (s, "[@%d] ", gei);
+  s = format (s, "IPs:[");
 
-  vec_foreach (ip, gbpe->ge_ips)
+  vec_foreach (ip, ge->ge_ips)
   {
     s = format (s, "%U, ", format_ip46_address, ip, IP46_TYPE_ANY);
   }
   s = format (s, "]");
 
-  s = format (s, " MAC:%U", format_mac_address_t, &gbpe->ge_mac);
-  s = format (s, " EPG-ID:%d", gbpe->ge_epg_id);
+  s = format (s, " MAC:%U", format_mac_address_t, &ge->ge_mac);
+  s = format (s, " EPG-ID:%d", ge->ge_epg_id);
+  if (GBP_ENDPOINT_FLAG_NONE != ge->ge_flags)
+    {
+      s = format (s, " flags:%U", format_gbp_endpoint_flags, ge->ge_flags);
+    }
+
+  s = format (s, " itf:[%U]", format_gbp_itf, ge->ge_itf);
+  s = format (s, " last-time:[%f]", ge->ge_last_time);
 
   return s;
 }
 
 static walk_rc_t
-gbp_endpoint_show_one (gbp_endpoint_t * gbpe, void *ctx)
+gbp_endpoint_show_one (index_t gei, void *ctx)
 {
   vlib_main_t *vm;
 
   vm = ctx;
-  vlib_cli_output (vm, " %U", format_gbp_endpoint, gbp_endpoint_index (gbpe));
+  vlib_cli_output (vm, " %U", format_gbp_endpoint, gei);
 
   return (WALK_CONTINUE);
 }
@@ -530,7 +839,7 @@ static clib_error_t *
 gbp_endpoint_show (vlib_main_t * vm,
                   unformat_input_t * input, vlib_cli_command_t * cmd)
 {
-  u32 sw_if_index, show_dbs, handle;
+  u32 show_dbs, handle;
 
   handle = INDEX_INVALID;
   show_dbs = 0;
@@ -539,7 +848,7 @@ gbp_endpoint_show (vlib_main_t * vm,
     {
       if (unformat (input, "%d", &handle))
        ;
-      else if (unformat (input, "db", &handle))
+      else if (unformat (input, "db"))
        show_dbs = 1;
       else
        break;
@@ -552,19 +861,10 @@ gbp_endpoint_show (vlib_main_t * vm,
   else if (show_dbs)
     {
       vlib_cli_output (vm, "\nDatabases:");
-      clib_bihash_foreach_key_value_pair_24_8 (&gbp_ep_by_ip_itf_db.gte_table,
+      clib_bihash_foreach_key_value_pair_24_8 (&gbp_ep_db.ged_by_ip_rd,
                                               gbp_endpoint_walk_ip_itf, vm);
       clib_bihash_foreach_key_value_pair_16_8
-       (&gbp_ep_by_mac_itf_db.gte_table, gbp_endpoint_walk_mac_itf, vm);
-
-      vec_foreach_index (sw_if_index, gbp_ep_by_itf_db.gte_vec)
-      {
-       if (INDEX_INVALID != gbp_ep_by_itf_db.gte_vec[sw_if_index])
-         vlib_cli_output (vm, " {%U} -> %d",
-                          format_vnet_sw_if_index_name, vnet_get_main (),
-                          sw_if_index,
-                          gbp_ep_by_itf_db.gte_vec[sw_if_index]);
-      }
+       (&gbp_ep_db.ged_by_mac_bd, gbp_endpoint_walk_mac_itf, vm);
     }
   else
     {
@@ -590,20 +890,161 @@ VLIB_CLI_COMMAND (gbp_endpoint_show_node, static) = {
 };
 /* *INDENT-ON* */
 
+static void
+gbp_endpoint_check (index_t gei, f64 start_time)
+{
+  gbp_endpoint_t *ge;
+
+  ge = gbp_endpoint_get (gei);
+
+  GBP_ENDPOINT_DBG ("scan at:%f -> %U", start_time, format_gbp_endpoint, gei);
+
+  if ((ge->ge_flags & GBP_ENDPOINT_FLAG_LEARNT) &&
+      ((start_time - ge->ge_last_time) > GBP_ENDPOINT_INACTIVE_TIME))
+    {
+      gbp_endpoint_delete (gei);
+    }
+}
+
+static void
+gbp_endpoint_scan_l2 (vlib_main_t * vm)
+{
+  clib_bihash_16_8_t *gte_table = &gbp_ep_db.ged_by_mac_bd;
+  f64 last_start, start_time, delta_t;
+  int i, j, k;
+
+  delta_t = 0;
+  last_start = start_time = vlib_time_now (vm);
+
+  for (i = 0; i < gte_table->nbuckets; i++)
+    {
+      clib_bihash_bucket_16_8_t *b;
+      clib_bihash_value_16_8_t *v;
+
+      /* allow no more than 20us without a pause */
+      delta_t = vlib_time_now (vm) - last_start;
+      if (delta_t > 20e-6)
+       {
+         /* suspend for 100 us */
+         vlib_process_suspend (vm, 100e-6);
+         last_start = vlib_time_now (vm);
+       }
+
+      b = &gte_table->buckets[i];
+      if (b->offset == 0)
+       continue;
+      v = clib_bihash_get_value_16_8 (gte_table, b->offset);
+
+      for (j = 0; j < (1 << b->log2_pages); j++)
+       {
+         for (k = 0; k < BIHASH_KVP_PER_PAGE; k++)
+           {
+             if (clib_bihash_is_free_16_8 (&v->kvp[k]))
+               continue;
+
+             gbp_endpoint_check (v->kvp[k].value, start_time);
+
+             /*
+              * Note: we may have just freed the bucket's backing
+              * storage, so check right here...
+              */
+             if (b->offset == 0)
+               goto doublebreak;
+           }
+         v++;
+       }
+    doublebreak:
+      ;
+    }
+}
+
+static void
+gbp_endpoint_scan_l3 (vlib_main_t * vm)
+{
+  clib_bihash_24_8_t *gte_table = &gbp_ep_db.ged_by_ip_rd;
+  f64 last_start, start_time, delta_t;
+  int i, j, k;
+
+  delta_t = 0;
+  last_start = start_time = vlib_time_now (vm);
+
+  for (i = 0; i < gte_table->nbuckets; i++)
+    {
+      clib_bihash_bucket_24_8_t *b;
+      clib_bihash_value_24_8_t *v;
+
+      /* allow no more than 20us without a pause */
+      delta_t = vlib_time_now (vm) - last_start;
+      if (delta_t > 20e-6)
+       {
+         /* suspend for 100 us */
+         vlib_process_suspend (vm, 100e-6);
+         last_start = vlib_time_now (vm);
+       }
+
+      b = &gte_table->buckets[i];
+      if (b->offset == 0)
+       continue;
+      v = clib_bihash_get_value_24_8 (gte_table, b->offset);
+
+      for (j = 0; j < (1 << b->log2_pages); j++)
+       {
+         for (k = 0; k < BIHASH_KVP_PER_PAGE; k++)
+           {
+             if (clib_bihash_is_free_24_8 (&v->kvp[k]))
+               continue;
+
+             gbp_endpoint_check (v->kvp[k].value, start_time);
+
+             /*
+              * Note: we may have just freed the bucket's backing
+              * storage, so check right here...
+              */
+             if (b->offset == 0)
+               goto doublebreak;
+           }
+         v++;
+       }
+    doublebreak:
+      ;
+    }
+}
+
+void
+gbp_endpoint_scan (vlib_main_t * vm)
+{
+  gbp_endpoint_scan_l2 (vm);
+  gbp_endpoint_scan_l3 (vm);
+}
+
+void
+gbp_learn_set_inactive_threshold (u32 threshold)
+{
+  GBP_ENDPOINT_INACTIVE_TIME = threshold;
+}
+
+f64
+gbp_endpoint_scan_threshold (void)
+{
+  return (GBP_ENDPOINT_INACTIVE_TIME);
+}
+
 #define GBP_EP_HASH_NUM_BUCKETS (2 * 1024)
 #define GBP_EP_HASH_MEMORY_SIZE (1 << 20)
 
 static clib_error_t *
 gbp_endpoint_init (vlib_main_t * vm)
 {
-  clib_bihash_init_24_8 (&gbp_ep_by_ip_itf_db.gte_table,
-                        "GBP Endpoints - IP/Interface",
+  clib_bihash_init_24_8 (&gbp_ep_db.ged_by_ip_rd,
+                        "GBP Endpoints - IP/RD",
                         GBP_EP_HASH_NUM_BUCKETS, GBP_EP_HASH_MEMORY_SIZE);
 
-  clib_bihash_init_16_8 (&gbp_ep_by_mac_itf_db.gte_table,
-                        "GBP Endpoints - MAC/Interface",
+  clib_bihash_init_16_8 (&gbp_ep_db.ged_by_mac_bd,
+                        "GBP Endpoints - MAC/BD",
                         GBP_EP_HASH_NUM_BUCKETS, GBP_EP_HASH_MEMORY_SIZE);
 
+  gbp_ep_logger = vlib_log_register_class ("gbp", "ep");
+
   return (NULL);
 }
 
index c92b217..bd157c9 100644 (file)
 /**
  * Flags for each endpoint
  */
+typedef enum gbp_endpoint_attr_t_
+{
+  GBP_ENDPOINT_ATTR_FIRST = 0,
+  GBP_ENDPOINT_ATTR_BOUNCE = GBP_ENDPOINT_ATTR_FIRST,
+  GBP_ENDPOINT_ATTR_REMOTE = 1,
+  GBP_ENDPOINT_ATTR_LEARNT = 2,
+  GBP_ENDPOINT_ATTR_LAST,
+} gbp_endpoint_attr_t;
+
 typedef enum gbp_endpoint_flags_t_
 {
   GBP_ENDPOINT_FLAG_NONE = 0,
-  GBP_ENDPOINT_FLAG_BOUNCE = (1 << 0),
-  GBP_ENDPOINT_FLAG_DYNAMIC = (1 << 1),
+  GBP_ENDPOINT_FLAG_BOUNCE = (1 << GBP_ENDPOINT_ATTR_BOUNCE),
+  GBP_ENDPOINT_FLAG_REMOTE = (1 << GBP_ENDPOINT_ATTR_REMOTE),
+  GBP_ENDPOINT_FLAG_LEARNT = (1 << GBP_ENDPOINT_ATTR_LEARNT),
 } gbp_endpoint_flags_t;
 
+#define GBP_ENDPOINT_ATTR_NAMES {                 \
+    [GBP_ENDPOINT_ATTR_BOUNCE] = "bounce",        \
+    [GBP_ENDPOINT_ATTR_REMOTE] = "remote",        \
+    [GBP_ENDPOINT_ATTR_LEARNT] = "learnt",        \
+}
+
+extern u8 *format_gbp_endpoint_flags (u8 * s, va_list * args);
+
 /**
  * A Group Based Policy Endpoint.
  * This is typically a VM or container. If the endpoint is local (i.e. on
@@ -48,12 +66,13 @@ typedef struct gbp_endpoint_t_
   /**
    * The interface on which the EP is connected
    */
+  index_t ge_itf;
   u32 ge_sw_if_index;
 
   /**
    * A vector of ip addresses that below to the endpoint
    */
-  ip46_address_t *ge_ips;
+  const ip46_address_t *ge_ips;
 
   /**
    * MAC address of the endpoint
@@ -61,52 +80,74 @@ typedef struct gbp_endpoint_t_
   mac_address_t ge_mac;
 
   /**
-   * The endpoint's designated EPG
+   * Index of the Endpoint's Group
+   */
+  index_t ge_epg;
+
+  /**
+   * Endpoint Group's ID
    */
-  epg_id_t ge_epg_id;
+  index_t ge_epg_id;
 
   /**
    * Endpoint flags
    */
   gbp_endpoint_flags_t ge_flags;
+
+  /**
+   * The L3 adj, if created
+   */
+  index_t *ge_adjs;
+
+  /**
+   * The last time a packet from seen from this end point
+   */
+  f64 ge_last_time;
+
+  /**
+   * Tunnel info for remote endpoints
+   */
+  struct
+  {
+    u32 ge_parent_sw_if_index;
+    ip46_address_t ge_src;
+    ip46_address_t ge_dst;
+  } tun;
 } gbp_endpoint_t;
 
 extern u8 *format_gbp_endpoint (u8 * s, va_list * args);
 
 /**
- * Interface to source EPG DB - a per-interface vector
+ * GBP Endpoint Databases
  */
-typedef struct gbp_ep_by_itf_db_t_
-{
-  index_t *gte_vec;
-} gbp_ep_by_itf_db_t;
-
 typedef struct gbp_ep_by_ip_itf_db_t_
 {
-  clib_bihash_24_8_t gte_table;
-} gbp_ep_by_ip_itf_db_t;
-
-typedef struct gbp_ep_by_mac_itf_db_t_
-{
-  clib_bihash_16_8_t gte_table;
-} gbp_ep_by_mac_itf_db_t;
+  index_t *ged_by_sw_if_index;
+  clib_bihash_24_8_t ged_by_ip_rd;
+  clib_bihash_16_8_t ged_by_mac_bd;
+} gbp_ep_db_t;
 
 extern int gbp_endpoint_update (u32 sw_if_index,
                                const ip46_address_t * ip,
                                const mac_address_t * mac,
-                               epg_id_t epg_id, u32 * handle);
-extern void gbp_endpoint_delete (u32 handle);
+                               epg_id_t epg_id,
+                               gbp_endpoint_flags_t flags,
+                               const ip46_address_t * tun_src,
+                               const ip46_address_t * tun_dst, u32 * handle);
+extern void gbp_endpoint_delete (index_t gbpei);
 
-typedef walk_rc_t (*gbp_endpoint_cb_t) (gbp_endpoint_t * gbpe, void *ctx);
+typedef walk_rc_t (*gbp_endpoint_cb_t) (index_t gbpei, void *ctx);
 extern void gbp_endpoint_walk (gbp_endpoint_cb_t cb, void *ctx);
+extern void gbp_endpoint_scan (vlib_main_t * vm);
+extern f64 gbp_endpoint_scan_threshold (void);
+extern int gbp_endpoint_is_remote (const gbp_endpoint_t * ge);
 
+extern void gbp_endpoint_flush (u32 sw_if_index);
 
 /**
  * DP functions and databases
  */
-extern gbp_ep_by_itf_db_t gbp_ep_by_itf_db;
-extern gbp_ep_by_mac_itf_db_t gbp_ep_by_mac_itf_db;
-extern gbp_ep_by_ip_itf_db_t gbp_ep_by_ip_itf_db;
+extern gbp_ep_db_t gbp_ep_db;
 extern gbp_endpoint_t *gbp_endpoint_pool;
 
 /**
@@ -118,12 +159,104 @@ gbp_endpoint_get (index_t gbpei)
   return (pool_elt_at_index (gbp_endpoint_pool, gbpei));
 }
 
-always_inline gbp_endpoint_t *
-gbp_endpoint_get_itf (u32 sw_if_index)
+static_always_inline void
+gbp_endpoint_mk_key_mac (const u8 * mac,
+                        u32 bd_index, clib_bihash_kv_16_8_t * key)
 {
-  return (gbp_endpoint_get (gbp_ep_by_itf_db.gte_vec[sw_if_index]));
+  key->key[0] = ethernet_mac_address_u64 (mac);
+  key->key[1] = bd_index;
 }
 
+static_always_inline gbp_endpoint_t *
+gbp_endpoint_find_mac (const u8 * mac, u32 bd_index)
+{
+  clib_bihash_kv_16_8_t key, value;
+  int rv;
+
+  gbp_endpoint_mk_key_mac (mac, bd_index, &key);
+
+  rv = clib_bihash_search_16_8 (&gbp_ep_db.ged_by_mac_bd, &key, &value);
+
+  if (0 != rv)
+    return NULL;
+
+  return (gbp_endpoint_get (value.value));
+}
+
+static_always_inline void
+gbp_endpoint_mk_key_ip (const ip46_address_t * ip,
+                       u32 fib_index, clib_bihash_kv_24_8_t * key)
+{
+  key->key[0] = ip->as_u64[0];
+  key->key[1] = ip->as_u64[1];
+  key->key[2] = fib_index;
+}
+
+static_always_inline void
+gbp_endpoint_mk_key_ip4 (const ip4_address_t * ip,
+                        u32 fib_index, clib_bihash_kv_24_8_t * key)
+{
+  const ip46_address_t a = {
+    .ip4 = *ip,
+  };
+  gbp_endpoint_mk_key_ip (&a, fib_index, key);
+}
+
+static_always_inline gbp_endpoint_t *
+gbp_endpoint_find_ip4 (const ip4_address_t * ip, u32 fib_index)
+{
+  clib_bihash_kv_24_8_t key, value;
+  int rv;
+
+  gbp_endpoint_mk_key_ip4 (ip, fib_index, &key);
+
+  rv = clib_bihash_search_24_8 (&gbp_ep_db.ged_by_ip_rd, &key, &value);
+
+  if (0 != rv)
+    return NULL;
+
+  return (gbp_endpoint_get (value.value));
+}
+
+static_always_inline void
+gbp_endpoint_mk_key_ip6 (const ip6_address_t * ip,
+                        u32 fib_index, clib_bihash_kv_24_8_t * key)
+{
+  key->key[0] = ip->as_u64[0];
+  key->key[1] = ip->as_u64[1];
+  key->key[2] = fib_index;
+}
+
+static_always_inline gbp_endpoint_t *
+gbp_endpoint_find_ip6 (const ip6_address_t * ip, u32 fib_index)
+{
+  clib_bihash_kv_24_8_t key, value;
+  int rv;
+
+  gbp_endpoint_mk_key_ip6 (ip, fib_index, &key);
+
+  rv = clib_bihash_search_24_8 (&gbp_ep_db.ged_by_ip_rd, &key, &value);
+
+  if (0 != rv)
+    return NULL;
+
+  return (gbp_endpoint_get (value.value));
+}
+
+static_always_inline gbp_endpoint_t *
+gbp_endpoint_find_itf (u32 sw_if_index)
+{
+  index_t gei;
+
+  gei = gbp_ep_db.ged_by_sw_if_index[sw_if_index];
+
+  if (INDEX_INVALID != gei)
+    return (gbp_endpoint_get (gei));
+
+  return (NULL);
+}
+
+
 #endif
 
 /*
index 095c8fe..ee4af2c 100644 (file)
 
 #include <plugins/gbp/gbp_endpoint_group.h>
 #include <plugins/gbp/gbp_endpoint.h>
+#include <plugins/gbp/gbp_bridge_domain.h>
+#include <plugins/gbp/gbp_route_domain.h>
 
 #include <vnet/dpo/dvr_dpo.h>
 #include <vnet/fib/fib_table.h>
 #include <vnet/l2/l2_input.h>
-#include <vnet/l2/feat_bitmap.h>
 
 /**
  * Pool of GBP endpoint_groups
@@ -32,106 +33,218 @@ gbp_endpoint_group_t *gbp_endpoint_group_pool;
  * DB of endpoint_groups
  */
 gbp_endpoint_group_db_t gbp_endpoint_group_db;
+vlib_log_class_t gg_logger;
+
+#define GBP_EPG_DBG(...)                           \
+    vlib_log_debug (gg_logger, __VA_ARGS__);
 
 gbp_endpoint_group_t *
+gbp_endpoint_group_get (index_t i)
+{
+  return (pool_elt_at_index (gbp_endpoint_group_pool, i));
+}
+
+static void
+gbp_endpoint_group_lock (index_t i)
+{
+  gbp_endpoint_group_t *gg;
+
+  gg = gbp_endpoint_group_get (i);
+  gg->gg_locks++;
+}
+
+index_t
 gbp_endpoint_group_find (epg_id_t epg_id)
 {
   uword *p;
 
-  p = hash_get (gbp_endpoint_group_db.gepg_hash, epg_id);
+  p = hash_get (gbp_endpoint_group_db.gg_hash, epg_id);
 
   if (NULL != p)
-    return (pool_elt_at_index (gbp_endpoint_group_pool, p[0]));
+    return p[0];
 
-  return (NULL);
+  return (INDEX_INVALID);
+}
+
+index_t
+gbp_endpoint_group_find_and_lock (epg_id_t epg_id)
+{
+  uword *p;
+
+  p = hash_get (gbp_endpoint_group_db.gg_hash, epg_id);
+
+  if (NULL != p)
+    {
+      gbp_endpoint_group_lock (p[0]);
+      return p[0];
+    }
+  return (INDEX_INVALID);
 }
 
 int
-gbp_endpoint_group_add (epg_id_t epg_id,
-                       u32 bd_id,
-                       u32 ip4_table_id,
-                       u32 ip6_table_id, u32 uplink_sw_if_index)
+gbp_endpoint_group_add_and_lock (epg_id_t epg_id,
+                                u32 bd_id, u32 rd_id, u32 uplink_sw_if_index)
 {
-  gbp_endpoint_group_t *gepg;
+  gbp_endpoint_group_t *gg;
+  index_t ggi;
 
-  gepg = gbp_endpoint_group_find (epg_id);
+  ggi = gbp_endpoint_group_find (epg_id);
 
-  if (NULL == gepg)
+  if (INDEX_INVALID == ggi)
     {
+      gbp_bridge_domain_t *gb;
       fib_protocol_t fproto;
+      index_t gbi, grdi;
+
+      gbi = gbp_bridge_domain_find_and_lock (bd_id);
+
+      if (~0 == gbi)
+       return (VNET_API_ERROR_BD_NOT_MODIFIABLE);
 
-      pool_get (gbp_endpoint_group_pool, gepg);
-      clib_memset (gepg, 0, sizeof (*gepg));
+      grdi = gbp_route_domain_find_and_lock (rd_id);
 
-      gepg->gepg_id = epg_id;
-      gepg->gepg_bd = bd_id;
-      gepg->gepg_rd[FIB_PROTOCOL_IP4] = ip4_table_id;
-      gepg->gepg_rd[FIB_PROTOCOL_IP6] = ip6_table_id;
-      gepg->gepg_uplink_sw_if_index = uplink_sw_if_index;
+      if (~0 == grdi)
+       {
+         gbp_bridge_domain_unlock (gbi);
+         return (VNET_API_ERROR_NO_SUCH_FIB);
+       }
+
+      gb = gbp_bridge_domain_get (gbi);
+
+      pool_get_zero (gbp_endpoint_group_pool, gg);
+
+      gg->gg_id = epg_id;
+      gg->gg_rd = grdi;
+      gg->gg_gbd = gbi;
+      gg->gg_bd_index = gb->gb_bd_index;
+
+      gg->gg_uplink_sw_if_index = uplink_sw_if_index;
+      gg->gg_locks = 1;
 
       /*
        * an egress DVR dpo for internal subnets to use when sending
        * on the uplink interface
        */
-      FOR_EACH_FIB_IP_PROTOCOL (fproto)
-      {
-       gepg->gepg_fib_index[fproto] =
-         fib_table_find_or_create_and_lock (fproto,
-                                            gepg->gepg_rd[fproto],
-                                            FIB_SOURCE_PLUGIN_HI);
-
-       if (~0 == gepg->gepg_fib_index[fproto])
+      if (~0 != gg->gg_uplink_sw_if_index)
+       {
+         FOR_EACH_FIB_IP_PROTOCOL (fproto)
          {
-           return (VNET_API_ERROR_NO_SUCH_FIB);
+           dvr_dpo_add_or_lock (uplink_sw_if_index,
+                                fib_proto_to_dpo (fproto),
+                                &gg->gg_dpo[fproto]);
          }
 
-       dvr_dpo_add_or_lock (uplink_sw_if_index,
-                            fib_proto_to_dpo (fproto),
-                            &gepg->gepg_dpo[fproto]);
-      }
-
-      /*
-       * packets direct from the uplink have had policy applied
-       */
-      l2input_intf_bitmap_enable (gepg->gepg_uplink_sw_if_index,
-                                 L2INPUT_FEAT_GBP_NULL_CLASSIFY, 1);
+         /*
+          * Add the uplink to the BD
+          * packets direct from the uplink have had policy applied
+          */
+         set_int_l2_mode (vlib_get_main (), vnet_get_main (),
+                          MODE_L2_BRIDGE, gg->gg_uplink_sw_if_index,
+                          gg->gg_bd_index, L2_BD_PORT_TYPE_NORMAL, 0, 0);
+         l2input_intf_bitmap_enable (gg->gg_uplink_sw_if_index,
+                                     L2INPUT_FEAT_GBP_NULL_CLASSIFY, 1);
+       }
 
-      hash_set (gbp_endpoint_group_db.gepg_hash,
-               gepg->gepg_id, gepg - gbp_endpoint_group_pool);
+      hash_set (gbp_endpoint_group_db.gg_hash,
+               gg->gg_id, gg - gbp_endpoint_group_pool);
 
     }
+  else
+    {
+      gg = gbp_endpoint_group_get (ggi);
+      gg->gg_locks++;
+    }
+
+  GBP_EPG_DBG ("add: %U", format_gbp_endpoint_group, gg);
 
   return (0);
 }
 
 void
-gbp_endpoint_group_delete (epg_id_t epg_id)
+gbp_endpoint_group_unlock (index_t ggi)
 {
-  gbp_endpoint_group_t *gepg;
-  uword *p;
+  gbp_endpoint_group_t *gg;
 
-  p = hash_get (gbp_endpoint_group_db.gepg_hash, epg_id);
+  gg = gbp_endpoint_group_get (ggi);
 
-  if (NULL != p)
+  gg->gg_locks--;
+
+  if (0 == gg->gg_locks)
     {
       fib_protocol_t fproto;
 
-      gepg = pool_elt_at_index (gbp_endpoint_group_pool, p[0]);
+      gg = pool_elt_at_index (gbp_endpoint_group_pool, ggi);
 
-      l2input_intf_bitmap_enable (gepg->gepg_uplink_sw_if_index,
-                                 L2INPUT_FEAT_GBP_NULL_CLASSIFY, 0);
+      if (~0 != gg->gg_uplink_sw_if_index)
+       {
+         set_int_l2_mode (vlib_get_main (), vnet_get_main (),
+                          MODE_L3, gg->gg_uplink_sw_if_index,
+                          gg->gg_bd_index, L2_BD_PORT_TYPE_NORMAL, 0, 0);
 
+         l2input_intf_bitmap_enable (gg->gg_uplink_sw_if_index,
+                                     L2INPUT_FEAT_GBP_NULL_CLASSIFY, 0);
+       }
       FOR_EACH_FIB_IP_PROTOCOL (fproto)
       {
-       dpo_reset (&gepg->gepg_dpo[fproto]);
-       fib_table_unlock (gepg->gepg_fib_index[fproto],
-                         fproto, FIB_SOURCE_PLUGIN_HI);
+       dpo_reset (&gg->gg_dpo[fproto]);
       }
+      gbp_bridge_domain_unlock (gg->gg_gbd);
+      gbp_route_domain_unlock (gg->gg_rd);
+
+      hash_unset (gbp_endpoint_group_db.gg_hash, gg->gg_id);
 
-      hash_unset (gbp_endpoint_group_db.gepg_hash, epg_id);
+      pool_put (gbp_endpoint_group_pool, gg);
+    }
+}
+
+int
+gbp_endpoint_group_delete (epg_id_t epg_id)
+{
+  index_t ggi;
+
+  ggi = gbp_endpoint_group_find (epg_id);
 
-      pool_put (gbp_endpoint_group_pool, gepg);
+  if (INDEX_INVALID != ggi)
+    {
+      GBP_EPG_DBG ("del: %U", format_gbp_endpoint_group,
+                  gbp_endpoint_group_get (ggi));
+      gbp_endpoint_group_unlock (ggi);
+
+      return (0);
     }
+
+  return (VNET_API_ERROR_NO_SUCH_ENTRY);
+}
+
+u32
+gbp_endpoint_group_get_bd_id (const gbp_endpoint_group_t * gg)
+{
+  const gbp_bridge_domain_t *gb;
+
+  gb = gbp_bridge_domain_get (gg->gg_gbd);
+
+  return (gb->gb_bd_id);
+}
+
+index_t
+gbp_endpoint_group_get_fib_index (gbp_endpoint_group_t * gg,
+                                 fib_protocol_t fproto)
+{
+  const gbp_route_domain_t *grd;
+
+  grd = gbp_route_domain_get (gg->gg_rd);
+
+  return (grd->grd_fib_index[fproto]);
+}
+
+u32
+gbp_endpoint_group_get_bvi (gbp_endpoint_group_t * gg)
+{
+  const gbp_bridge_domain_t *gb;
+
+  gb = gbp_bridge_domain_get (gg->gg_gbd);
+
+  return (gb->gb_bvi_sw_if_index);
 }
 
 void
@@ -190,8 +303,8 @@ gbp_endpoint_group_cli (vlib_main_t * vm,
       if (~0 == rd_id)
        return clib_error_return (0, "route-domain must be specified");
 
-      gbp_endpoint_group_add (epg_id, bd_id, rd_id, rd_id,
-                             uplink_sw_if_index);
+      gbp_endpoint_group_add_and_lock (epg_id, bd_id, rd_id,
+                                      uplink_sw_if_index);
     }
   else
     gbp_endpoint_group_delete (epg_id);
@@ -213,19 +326,32 @@ VLIB_CLI_COMMAND (gbp_endpoint_group_cli_node, static) = {
   .function = gbp_endpoint_group_cli,
 };
 
-static int
-gbp_endpoint_group_show_one (gbp_endpoint_group_t *gepg, void *ctx)
+u8 *
+format_gbp_endpoint_group (u8 * s, va_list * args)
 {
+  gbp_endpoint_group_t *gg = va_arg (*args, gbp_endpoint_group_t*);
   vnet_main_t *vnm = vnet_get_main ();
+
+  if (NULL != gg)
+    s = format (s, "%d, bd:[%d,%d], rd:[%d] uplink:%U locks:%d",
+                gg->gg_id,
+                gbp_endpoint_group_get_bd_id(gg), gg->gg_bd_index,
+                gg->gg_rd,
+                format_vnet_sw_if_index_name, vnm, gg->gg_uplink_sw_if_index,
+                gg->gg_locks);
+  else
+    s = format (s, "NULL");
+
+  return (s);
+}
+
+static int
+gbp_endpoint_group_show_one (gbp_endpoint_group_t *gg, void *ctx)
+{
   vlib_main_t *vm;
 
   vm = ctx;
-  vlib_cli_output (vm, "  %d, bd:%d, ip4:%d ip6:%d uplink:%U",
-                   gepg->gepg_id,
-                   gepg->gepg_bd,
-                   gepg->gepg_rd[FIB_PROTOCOL_IP4],
-                   gepg->gepg_rd[FIB_PROTOCOL_IP6],
-                  format_vnet_sw_if_index_name, vnm, gepg->gepg_uplink_sw_if_index);
+  vlib_cli_output (vm, "  %U",format_gbp_endpoint_group, gg);
 
   return (1);
 }
@@ -256,6 +382,16 @@ VLIB_CLI_COMMAND (gbp_endpoint_group_show_node, static) = {
 };
 /* *INDENT-ON* */
 
+static clib_error_t *
+gbp_endpoint_group_init (vlib_main_t * vm)
+{
+  gg_logger = vlib_log_register_class ("gbp", "epg");
+
+  return (NULL);
+}
+
+VLIB_INIT_FUNCTION (gbp_endpoint_group_init);
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
index f71e5f5..7116a05 100644 (file)
@@ -28,37 +28,38 @@ typedef struct gpb_endpoint_group_t_
   /**
    * ID
    */
-  epg_id_t gepg_id;
+  epg_id_t gg_id;
 
   /**
    * Bridge-domain ID the EPG is in
    */
-  u32 gepg_bd;
+  index_t gg_gbd;
+  index_t gg_bd_index;
 
   /**
    * route-domain/IP-table ID the EPG is in
    */
-  u32 gepg_rd[FIB_PROTOCOL_IP_MAX];
-
-  /**
-   * resulting FIB indices
-   */
-  u32 gepg_fib_index[FIB_PROTOCOL_IP_MAX];
+  index_t gg_rd;
 
   /**
    * Is the EPG an external/NAT
    */
-  u8 gepg_is_ext;
+  u8 gg_is_ext;
 
   /**
    * the uplink interface dedicated to the EPG
    */
-  u32 gepg_uplink_sw_if_index;
+  u32 gg_uplink_sw_if_index;
 
   /**
    * The DPO used in the L3 path for forwarding internal subnets
    */
-  dpo_id_t gepg_dpo[FIB_PROTOCOL_IP_MAX];
+  dpo_id_t gg_dpo[FIB_PROTOCOL_IP_MAX];
+
+  /**
+   * Locks/references to this EPG
+   */
+  u32 gg_locks;
 } gbp_endpoint_group_t;
 
 /**
@@ -66,20 +67,30 @@ typedef struct gpb_endpoint_group_t_
  */
 typedef struct gbp_endpoint_group_db_t_
 {
-  uword *gepg_hash;
+  uword *gg_hash;
 } gbp_endpoint_group_db_t;
 
-extern int gbp_endpoint_group_add (epg_id_t epg_id,
-                                  u32 bd_id,
-                                  u32 ip4_table_id,
-                                  u32 ip6_table_id, u32 uplink_sw_if_index);
-extern void gbp_endpoint_group_delete (epg_id_t epg_id);
+extern int gbp_endpoint_group_add_and_lock (epg_id_t epg_id,
+                                           u32 bd_id,
+                                           u32 rd_id,
+                                           u32 uplink_sw_if_index);
+extern index_t gbp_endpoint_group_find_and_lock (epg_id_t epg_id);
+extern index_t gbp_endpoint_group_find (epg_id_t epg_id);
+extern int gbp_endpoint_group_delete (epg_id_t epg_id);
+extern void gbp_endpoint_group_unlock (index_t index);
+extern u32 gbp_endpoint_group_get_bd_id (const gbp_endpoint_group_t *);
+
+extern gbp_endpoint_group_t *gbp_endpoint_group_get (index_t i);
+extern index_t gbp_endpoint_group_get_fib_index (gbp_endpoint_group_t * gg,
+                                                fib_protocol_t fproto);
+extern u32 gbp_endpoint_group_get_bvi (gbp_endpoint_group_t * gg);
 
 typedef int (*gbp_endpoint_group_cb_t) (gbp_endpoint_group_t * gbpe,
                                        void *ctx);
 extern void gbp_endpoint_group_walk (gbp_endpoint_group_cb_t bgpe, void *ctx);
 
-extern gbp_endpoint_group_t *gbp_endpoint_group_find (epg_id_t epg_id);
+
+extern u8 *format_gbp_endpoint_group (u8 * s, va_list * args);
 
 /**
  * DP functions and databases
@@ -92,14 +103,14 @@ gbp_epg_itf_lookup (epg_id_t epg)
 {
   uword *p;
 
-  p = hash_get (gbp_endpoint_group_db.gepg_hash, epg);
+  p = hash_get (gbp_endpoint_group_db.gg_hash, epg);
 
   if (NULL != p)
     {
-      gbp_endpoint_group_t *gepg;
+      gbp_endpoint_group_t *gg;
 
-      gepg = pool_elt_at_index (gbp_endpoint_group_pool, p[0]);
-      return (gepg->gepg_uplink_sw_if_index);
+      gg = pool_elt_at_index (gbp_endpoint_group_pool, p[0]);
+      return (gg->gg_uplink_sw_if_index);
     }
   return (~0);
 }
@@ -109,14 +120,14 @@ gbp_epg_dpo_lookup (epg_id_t epg, fib_protocol_t fproto)
 {
   uword *p;
 
-  p = hash_get (gbp_endpoint_group_db.gepg_hash, epg);
+  p = hash_get (gbp_endpoint_group_db.gg_hash, epg);
 
   if (NULL != p)
     {
-      gbp_endpoint_group_t *gepg;
+      gbp_endpoint_group_t *gg;
 
-      gepg = pool_elt_at_index (gbp_endpoint_group_pool, p[0]);
-      return (&gepg->gepg_dpo[fproto]);
+      gg = pool_elt_at_index (gbp_endpoint_group_pool, p[0]);
+      return (&gg->gg_dpo[fproto]);
     }
   return (NULL);
 }
diff --git a/src/plugins/gbp/gbp_itf.c b/src/plugins/gbp/gbp_itf.c
new file mode 100644 (file)
index 0000000..39a1124
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * 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 <plugins/gbp/gbp_itf.h>
+
+/**
+ * Attributes and configurations attached to interfaces by GBP
+ */
+typedef struct gbp_itf_t_
+{
+  /**
+   * Number of references to this interface
+   */
+  u32 gi_locks;
+
+  u32 gi_sw_if_index;
+  u32 gi_bd_index;
+
+  /**
+   * L2/L3 Features configured by each user
+   */
+  u32 *gi_l2_input_fbs;
+  u32 gi_l2_input_fb;
+  u32 *gi_l2_output_fbs;
+  u32 gi_l2_output_fb;
+} gbp_itf_t;
+
+static gbp_itf_t *gbp_itfs;
+
+static gbp_itf_t *
+gbp_itf_get (index_t gii)
+{
+  vec_validate (gbp_itfs, gii);
+
+  return (&gbp_itfs[gii]);
+}
+
+static index_t
+gbp_itf_get_itf (u32 sw_if_index)
+{
+  return (sw_if_index);
+}
+
+index_t
+gbp_itf_add_and_lock (u32 sw_if_index, u32 bd_index)
+{
+  gbp_itf_t *gi;
+
+  gi = gbp_itf_get (gbp_itf_get_itf (sw_if_index));
+
+  if (0 == gi->gi_locks)
+    {
+      gi->gi_sw_if_index = sw_if_index;
+      gi->gi_bd_index = bd_index;
+
+      if (~0 != gi->gi_bd_index)
+       set_int_l2_mode (vlib_get_main (), vnet_get_main (),
+                        MODE_L2_BRIDGE, sw_if_index, bd_index,
+                        L2_BD_PORT_TYPE_NORMAL, 0, 0);
+
+    }
+
+  gi->gi_locks++;
+
+  return (sw_if_index);
+}
+
+void
+gbp_itf_unlock (index_t gii)
+{
+  gbp_itf_t *gi;
+
+  gi = gbp_itf_get (gii);
+  ASSERT (gi->gi_locks > 0);
+  gi->gi_locks--;
+
+  if (0 == gi->gi_locks)
+    {
+      if (~0 != gi->gi_bd_index)
+       set_int_l2_mode (vlib_get_main (), vnet_get_main (), MODE_L3,
+                        gi->gi_sw_if_index, 0, L2_BD_PORT_TYPE_NORMAL, 0, 0);
+      vec_free (gi->gi_l2_input_fbs);
+      vec_free (gi->gi_l2_output_fbs);
+
+      memset (gi, 0, sizeof (*gi));
+    }
+}
+
+void
+gbp_itf_set_l2_input_feature (index_t gii,
+                             index_t useri, l2input_feat_masks_t feats)
+{
+  u32 diff_fb, new_fb, *fb, feat;
+  gbp_itf_t *gi;
+
+  gi = gbp_itf_get (gii);
+
+  if (gi->gi_bd_index == ~0)
+    return;
+
+  vec_validate (gi->gi_l2_input_fbs, useri);
+  gi->gi_l2_input_fbs[useri] = feats;
+
+  new_fb = 0;
+  vec_foreach (fb, gi->gi_l2_input_fbs)
+  {
+    new_fb |= *fb;
+  }
+
+  /* add new features */
+  diff_fb = (gi->gi_l2_input_fb ^ new_fb) & new_fb;
+
+  /* *INDENT-OFF* */
+  foreach_set_bit (feat, diff_fb,
+  ({
+    l2input_intf_bitmap_enable (gi->gi_sw_if_index, (1 << feat), 1);
+  }));
+  /* *INDENT-ON* */
+
+  /* remove unneeded features */
+  diff_fb = (gi->gi_l2_input_fb ^ new_fb) & gi->gi_l2_input_fb;
+
+  /* *INDENT-OFF* */
+  foreach_set_bit (feat, diff_fb,
+  ({
+    l2input_intf_bitmap_enable (gi->gi_sw_if_index, (1 << feat), 0);
+  }));
+  /* *INDENT-ON* */
+
+  gi->gi_l2_input_fb = new_fb;
+}
+
+void
+gbp_itf_set_l2_output_feature (index_t gii,
+                              index_t useri, l2output_feat_masks_t feats)
+{
+  u32 diff_fb, new_fb, *fb, feat;
+  gbp_itf_t *gi;
+
+  gi = gbp_itf_get (gii);
+
+  if (gi->gi_bd_index == ~0)
+    return;
+
+  vec_validate (gi->gi_l2_output_fbs, useri);
+  gi->gi_l2_output_fbs[useri] = feats;
+
+  new_fb = 0;
+  vec_foreach (fb, gi->gi_l2_output_fbs)
+  {
+    new_fb |= *fb;
+  }
+
+  /* add new features */
+  diff_fb = (gi->gi_l2_output_fb ^ new_fb) & new_fb;
+
+  /* *INDENT-OFF* */
+  foreach_set_bit (feat, diff_fb,
+  ({
+    l2output_intf_bitmap_enable (gi->gi_sw_if_index, (1 << feat), 1);
+  }));
+  /* *INDENT-ON* */
+
+  /* remove unneeded features */
+  diff_fb = (gi->gi_l2_output_fb ^ new_fb) & gi->gi_l2_output_fb;
+
+  /* *INDENT-OFF* */
+  foreach_set_bit (feat, diff_fb,
+  ({
+    l2output_intf_bitmap_enable (gi->gi_sw_if_index, (1 << feat), 0);
+  }));
+  /* *INDENT-ON* */
+
+  gi->gi_l2_output_fb = new_fb;
+}
+
+u8 *
+format_gbp_itf (u8 * s, va_list * args)
+{
+  index_t gii = va_arg (*args, index_t);
+  gbp_itf_t *gi;
+
+  gi = gbp_itf_get (gii);
+
+  s = format (s, "%U locks:%d input-feats:%U output-feats:%U",
+             format_vnet_sw_if_index_name, vnet_get_main (),
+             gi->gi_sw_if_index, gi->gi_locks, format_l2_input_features,
+             gi->gi_l2_input_fb, format_l2_output_features,
+             gi->gi_l2_output_fb);
+
+  return (s);
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/gbp/gbp_itf.h b/src/plugins/gbp/gbp_itf.h
new file mode 100644 (file)
index 0000000..6ece7b1
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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 __GBP_INTERFACE_H__
+#define __GBP_INTERFACE_H__
+
+#include <vnet/l2/l2_input.h>
+#include <vnet/l2/l2_output.h>
+
+extern index_t gbp_itf_add_and_lock (u32 sw_if_index, u32 bd_index);
+extern void gbp_itf_unlock (index_t index);
+
+extern void gbp_itf_set_l2_input_feature (index_t gii,
+                                         index_t useri,
+                                         l2input_feat_masks_t feats);
+extern void gbp_itf_set_l2_output_feature (index_t gii,
+                                          index_t useri,
+                                          l2output_feat_masks_t feats);
+
+extern u8 *format_gbp_itf (u8 * s, va_list * args);
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/gbp/gbp_learn.c b/src/plugins/gbp/gbp_learn.c
new file mode 100644 (file)
index 0000000..9239779
--- /dev/null
@@ -0,0 +1,756 @@
+/*
+ * 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 <plugins/gbp/gbp.h>
+#include <plugins/gbp/gbp_learn.h>
+#include <plugins/gbp/gbp_bridge_domain.h>
+#include <vlibmemory/api.h>
+
+#include <vnet/util/throttle.h>
+#include <vnet/l2/l2_input.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/vxlan-gbp/vxlan_gbp_packet.h>
+
+/**
+ * Grouping of global data for the GBP source EPG classification feature
+ */
+typedef struct gbp_learn_main_t_
+{
+  /**
+   * Next nodes for L2 output features
+   */
+  u32 gl_l2_input_feat_next[32];
+
+  /**
+   * logger - VLIB log class
+   */
+  vlib_log_class_t gl_logger;
+
+  /**
+   * throttles for the DP leanring
+   */
+  throttle_t gl_l2_throttle;
+  throttle_t gl_l3_throttle;
+} gbp_learn_main_t;
+
+/**
+ * The maximum learning rate per-hashed EP
+ */
+#define GBP_ENDPOINT_HASH_LEARN_RATE (1e-2)
+
+static gbp_learn_main_t gbp_learn_main;
+
+#define GBP_LEARN_DBG(...)                                      \
+    vlib_log_debug (gbp_learn_main.gl_logger, __VA_ARGS__);
+
+#define foreach_gbp_learn                      \
+  _(DROP,    "drop")
+
+typedef enum
+{
+#define _(sym,str) GBP_LEARN_ERROR_##sym,
+  foreach_gbp_learn
+#undef _
+    GBP_LEARN_N_ERROR,
+} gbp_learn_error_t;
+
+static char *gbp_learn_error_strings[] = {
+#define _(sym,string) string,
+  foreach_gbp_learn
+#undef _
+};
+
+typedef enum
+{
+#define _(sym,str) GBP_LEARN_NEXT_##sym,
+  foreach_gbp_learn
+#undef _
+    GBP_LEARN_N_NEXT,
+} gbp_learn_next_t;
+
+typedef struct gbp_learn_l2_t_
+{
+  ip46_address_t ip;
+  mac_address_t mac;
+  u32 sw_if_index;
+  u32 bd_index;
+  epg_id_t epg;
+  ip46_address_t outer_src;
+  ip46_address_t outer_dst;
+} gbp_learn_l2_t;
+
+
+static void
+gbp_learn_l2_cp (const gbp_learn_l2_t * gl2)
+{
+  ip46_address_t *ips = NULL;
+
+  GBP_LEARN_DBG ("L2 EP: %U %U, %d",
+                format_mac_address_t, &gl2->mac,
+                format_ip46_address, &gl2->ip, IP46_TYPE_ANY, gl2->epg);
+
+  vec_add1 (ips, gl2->ip);
+
+  ASSERT (!ip46_address_is_zero (&gl2->outer_src));
+  ASSERT (!ip46_address_is_zero (&gl2->outer_dst));
+
+  /*
+   * flip the source and dst, since that's how it was received, this API
+   * takes how it's sent
+   */
+  gbp_endpoint_update (gl2->sw_if_index, ips,
+                      &gl2->mac, gl2->epg,
+                      (GBP_ENDPOINT_FLAG_LEARNT |
+                       GBP_ENDPOINT_FLAG_REMOTE),
+                      &gl2->outer_dst, &gl2->outer_src, NULL);
+}
+
+static void
+gbp_learn_l2_ip4_dp (const u8 * mac, const ip4_address_t * ip,
+                    u32 bd_index, u32 sw_if_index, epg_id_t epg,
+                    const ip4_address_t * outer_src,
+                    const ip4_address_t * outer_dst)
+{
+  gbp_learn_l2_t gl2 = {
+    .sw_if_index = sw_if_index,
+    .bd_index = bd_index,
+    .epg = epg,
+    .ip.ip4 = *ip,
+    .outer_src.ip4 = *outer_src,
+    .outer_dst.ip4 = *outer_dst,
+  };
+  mac_address_from_bytes (&gl2.mac, mac);
+
+  ASSERT (!ip46_address_is_zero (&gl2.outer_src));
+  ASSERT (!ip46_address_is_zero (&gl2.outer_dst));
+
+  vl_api_rpc_call_main_thread (gbp_learn_l2_cp, (u8 *) & gl2, sizeof (gl2));
+}
+
+static void
+gbp_learn_l2_ip6_dp (const u8 * mac, const ip6_address_t * ip,
+                    u32 bd_index, u32 sw_if_index, epg_id_t epg,
+                    const ip4_address_t * outer_src,
+                    const ip4_address_t * outer_dst)
+{
+  gbp_learn_l2_t gl2 = {
+    .sw_if_index = sw_if_index,
+    .bd_index = bd_index,
+    .epg = epg,
+    .ip.ip6 = *ip,
+    .outer_src.ip4 = *outer_src,
+    .outer_dst.ip4 = *outer_dst,
+  };
+  mac_address_from_bytes (&gl2.mac, mac);
+
+  vl_api_rpc_call_main_thread (gbp_learn_l2_cp, (u8 *) & gl2, sizeof (gl2));
+}
+
+static void
+gbp_learn_l2_dp (const u8 * mac, u32 bd_index, u32 sw_if_index,
+                epg_id_t epg,
+                const ip4_address_t * outer_src,
+                const ip4_address_t * outer_dst)
+{
+  gbp_learn_l2_t gl2 = {
+    .sw_if_index = sw_if_index,
+    .bd_index = bd_index,
+    .epg = epg,
+    .outer_src.ip4 = *outer_src,
+    .outer_dst.ip4 = *outer_dst,
+  };
+  mac_address_from_bytes (&gl2.mac, mac);
+
+  vl_api_rpc_call_main_thread (gbp_learn_l2_cp, (u8 *) & gl2, sizeof (gl2));
+}
+
+/**
+ * per-packet trace data
+ */
+typedef struct gbp_learn_l2_trace_t_
+{
+  /* per-pkt trace data */
+  mac_address_t mac;
+  u32 sw_if_index;
+  u32 new;
+  u32 throttled;
+  u32 epg;
+  u32 d_bit;
+} gbp_learn_l2_trace_t;
+
+always_inline void
+gbp_learn_get_outer (const ethernet_header_t * eh0,
+                    ip4_address_t * outer_src, ip4_address_t * outer_dst)
+{
+  ip4_header_t *ip0;
+  u8 *buff;
+
+  /* rewind back to the ivxlan header */
+  buff = (u8 *) eh0;
+  buff -= (sizeof (vxlan_gbp_header_t) +
+          sizeof (udp_header_t) + sizeof (ip4_header_t));
+
+  ip0 = (ip4_header_t *) buff;
+
+  *outer_src = ip0->src_address;
+  *outer_dst = ip0->dst_address;
+}
+
+static uword
+gbp_learn_l2 (vlib_main_t * vm,
+             vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+  u32 n_left_from, *from, *to_next, next_index, thread_index, seed;
+  gbp_learn_main_t *glm;
+  f64 time_now;
+
+  glm = &gbp_learn_main;
+  next_index = 0;
+  n_left_from = frame->n_vectors;
+  from = vlib_frame_vector_args (frame);
+  time_now = vlib_time_now (vm);
+  thread_index = vm->thread_index;
+
+  seed = throttle_seed (&glm->gl_l2_throttle, thread_index, time_now);
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         ip4_address_t outer_src, outer_dst;
+         u32 bi0, sw_if_index0, t0, epg0;
+         const ethernet_header_t *eh0;
+         gbp_learn_next_t next0;
+         gbp_endpoint_t *ge0;
+         vlib_buffer_t *b0;
+
+         next0 = GBP_LEARN_NEXT_DROP;
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+
+         b0 = vlib_get_buffer (vm, bi0);
+         sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+
+         eh0 = vlib_buffer_get_current (b0);
+         epg0 = vnet_buffer2 (b0)->gbp.src_epg;
+
+         next0 = vnet_l2_feature_next (b0, glm->gl_l2_input_feat_next,
+                                       L2INPUT_FEAT_GBP_LEARN);
+
+         ge0 = gbp_endpoint_find_mac (eh0->src_address,
+                                      vnet_buffer (b0)->l2.bd_index);
+
+         if (vnet_buffer2 (b0)->gbp.flags & VXLAN_GBP_GPFLAGS_D)
+           {
+             ge0 = NULL;
+             t0 = 1;
+             goto trace;
+           }
+
+         /*
+          * check for new EP or a moved EP
+          */
+         if (NULL == ge0 || ge0->ge_sw_if_index != sw_if_index0)
+
+           {
+             /*
+              * use the last 4 bytes of the mac address as the hash for the EP
+              */
+             t0 = throttle_check (&glm->gl_l2_throttle, thread_index,
+                                  *((u32 *) (eh0->src_address + 2)), seed);
+             if (!t0)
+               {
+                 gbp_learn_get_outer (eh0, &outer_src, &outer_dst);
+
+                 switch (clib_net_to_host_u16 (eh0->type))
+                   {
+                   case ETHERNET_TYPE_IP4:
+                     {
+                       const ip4_header_t *ip0;
+
+                       ip0 = (ip4_header_t *) (eh0 + 1);
+
+                       gbp_learn_l2_ip4_dp (eh0->src_address,
+                                            &ip0->src_address,
+                                            vnet_buffer (b0)->l2.bd_index,
+                                            sw_if_index0, epg0,
+                                            &outer_src, &outer_dst);
+
+                       break;
+                     }
+                   case ETHERNET_TYPE_IP6:
+                     {
+                       const ip6_header_t *ip0;
+
+                       ip0 = (ip6_header_t *) (eh0 + 1);
+
+                       gbp_learn_l2_ip6_dp (eh0->src_address,
+                                            &ip0->src_address,
+                                            vnet_buffer (b0)->l2.bd_index,
+                                            sw_if_index0, epg0,
+                                            &outer_src, &outer_dst);
+
+                       break;
+                     }
+                   default:
+                     gbp_learn_l2_dp (eh0->src_address,
+                                      vnet_buffer (b0)->l2.bd_index,
+                                      sw_if_index0, epg0,
+                                      &outer_src, &outer_dst);
+                     break;
+                   }
+               }
+           }
+         else
+           {
+             /*
+              * this update could happen simultaneoulsy from multiple workers
+              * but that's ok we are not interested in being very accurate.
+              */
+             t0 = 0;
+             ge0->ge_last_time = time_now;
+           }
+       trace:
+         if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
+           {
+             gbp_learn_l2_trace_t *t =
+               vlib_add_trace (vm, node, b0, sizeof (*t));
+             clib_memcpy (t->mac.bytes, eh0->src_address, 6);
+             t->new = (NULL == ge0);
+             t->throttled = t0;
+             t->sw_if_index = sw_if_index0;
+             t->epg = epg0;
+             t->d_bit = ! !(vnet_buffer2 (b0)->gbp.flags &
+                            VXLAN_GBP_GPFLAGS_D);
+           }
+
+         /* verify speculative enqueue, maybe switch current next frame */
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+                                          to_next, n_left_to_next,
+                                          bi0, next0);
+       }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  return frame->n_vectors;
+}
+
+/* packet trace format function */
+static u8 *
+format_gbp_learn_l2_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  gbp_learn_l2_trace_t *t = va_arg (*args, gbp_learn_l2_trace_t *);
+
+  s = format (s, "new:%d throttled:%d d-bit:%d mac:%U itf:%d epg:%d",
+             t->new, t->throttled, t->d_bit,
+             format_mac_address_t, &t->mac, t->sw_if_index, t->epg);
+
+  return s;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (gbp_learn_l2_node) = {
+  .function = gbp_learn_l2,
+  .name = "gbp-learn-l2",
+  .vector_size = sizeof (u32),
+  .format_trace = format_gbp_learn_l2_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = ARRAY_LEN(gbp_learn_error_strings),
+  .error_strings = gbp_learn_error_strings,
+
+  .n_next_nodes = GBP_LEARN_N_NEXT,
+
+  .next_nodes = {
+    [GBP_LEARN_NEXT_DROP] = "error-drop",
+  },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (gbp_learn_l2_node, gbp_learn_l2);
+/* *INDENT-ON* */
+
+typedef struct gbp_learn_l3_t_
+{
+  ip46_address_t ip;
+  u32 fib_index;
+  u32 sw_if_index;
+  epg_id_t epg;
+  ip46_address_t outer_src;
+  ip46_address_t outer_dst;
+} gbp_learn_l3_t;
+
+static void
+gbp_learn_l3_cp (const gbp_learn_l3_t * gl3)
+{
+  ip46_address_t *ips = NULL;
+
+  GBP_LEARN_DBG ("L3 EP: %U, %d", format_ip46_address, &gl3->ip,
+                IP46_TYPE_ANY, gl3->epg);
+
+  vec_add1 (ips, gl3->ip);
+
+  gbp_endpoint_update (gl3->sw_if_index, ips, NULL, gl3->epg,
+                      (GBP_ENDPOINT_FLAG_REMOTE |
+                       GBP_ENDPOINT_FLAG_LEARNT),
+                      &gl3->outer_dst, &gl3->outer_src, NULL);
+}
+
+static void
+gbp_learn_ip4_dp (const ip4_address_t * ip,
+                 u32 fib_index, u32 sw_if_index, epg_id_t epg,
+                 const ip4_address_t * outer_src,
+                 const ip4_address_t * outer_dst)
+{
+  /* *INDENT-OFF* */
+  gbp_learn_l3_t gl3 = {
+    .ip = {
+      .ip4 = *ip,
+    },
+    .sw_if_index = sw_if_index,
+    .fib_index = fib_index,
+    .epg = epg,
+    .outer_src.ip4 = *outer_src,
+    .outer_dst.ip4 = *outer_dst,
+  };
+  /* *INDENT-ON* */
+
+  vl_api_rpc_call_main_thread (gbp_learn_l3_cp, (u8 *) & gl3, sizeof (gl3));
+}
+
+static void
+gbp_learn_ip6_dp (const ip6_address_t * ip,
+                 u32 fib_index, u32 sw_if_index, epg_id_t epg,
+                 const ip4_address_t * outer_src,
+                 const ip4_address_t * outer_dst)
+{
+  /* *INDENT-OFF* */
+  gbp_learn_l3_t gl3 = {
+    .ip = {
+      .ip6 = *ip,
+    },
+    .sw_if_index = sw_if_index,
+    .fib_index = fib_index,
+    .epg = epg,
+    .outer_src.ip4 = *outer_src,
+    .outer_dst.ip4 = *outer_dst,
+  };
+  /* *INDENT-ON* */
+
+  vl_api_rpc_call_main_thread (gbp_learn_l3_cp, (u8 *) & gl3, sizeof (gl3));
+}
+
+/**
+ * per-packet trace data
+ */
+typedef struct gbp_learn_l3_trace_t_
+{
+  /* per-pkt trace data */
+  ip46_address_t ip;
+  u32 sw_if_index;
+  u32 new;
+  u32 throttled;
+  u32 epg;
+} gbp_learn_l3_trace_t;
+
+static uword
+gbp_learn_l3 (vlib_main_t * vm,
+             vlib_node_runtime_t * node, vlib_frame_t * frame,
+             fib_protocol_t fproto)
+{
+  u32 n_left_from, *from, *to_next, next_index, thread_index, seed;
+  gbp_learn_main_t *glm;
+  f64 time_now;
+
+  glm = &gbp_learn_main;
+  next_index = 0;
+  n_left_from = frame->n_vectors;
+  from = vlib_frame_vector_args (frame);
+  time_now = vlib_time_now (vm);
+  thread_index = vm->thread_index;
+
+  seed = throttle_seed (&glm->gl_l3_throttle, thread_index, time_now);
+
+  while (n_left_from > 0)
+    {
+      u32 n_left_to_next;
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         u32 bi0, sw_if_index0, t0, epg0, fib_index0;
+         CLIB_UNUSED (const ip4_header_t *) ip4_0;
+         CLIB_UNUSED (const ip6_header_t *) ip6_0;
+         ip4_address_t outer_src, outer_dst;
+         ethernet_header_t *eth0;
+         gbp_learn_next_t next0;
+         gbp_endpoint_t *ge0;
+         vlib_buffer_t *b0;
+
+         next0 = GBP_LEARN_NEXT_DROP;
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+
+         b0 = vlib_get_buffer (vm, bi0);
+         sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
+         epg0 = vnet_buffer2 (b0)->gbp.src_epg;
+         ip6_0 = NULL;
+         ip4_0 = NULL;
+
+         vnet_feature_next (&next0, b0);
+
+         if (vnet_buffer2 (b0)->gbp.flags & VXLAN_GBP_GPFLAGS_D)
+           {
+             t0 = 1;
+             ge0 = NULL;
+             goto trace;
+           }
+
+         fib_index0 = fib_table_get_index_for_sw_if_index (fproto,
+                                                           sw_if_index0);
+
+         if (FIB_PROTOCOL_IP6 == fproto)
+           {
+             ip6_0 = vlib_buffer_get_current (b0);
+             eth0 = (ethernet_header_t *) (((u8 *) ip6_0) - sizeof (*eth0));
+
+             gbp_learn_get_outer (eth0, &outer_src, &outer_dst);
+
+             ge0 = gbp_endpoint_find_ip6 (&ip6_0->src_address, fib_index0);
+
+             if (NULL == ge0)
+               {
+                 t0 = throttle_check (&glm->gl_l3_throttle,
+                                      thread_index,
+                                      ip6_address_hash_to_u32
+                                      (&ip6_0->src_address), seed);
+
+                 if (!t0)
+                   {
+                     gbp_learn_ip6_dp (&ip6_0->src_address,
+                                       fib_index0, sw_if_index0, epg0,
+                                       &outer_src, &outer_dst);
+                   }
+               }
+             else
+               {
+                 /*
+                  * this update could happen simultaneoulsy from multiple
+                  * workers but that's ok we are not interested in being
+                  * very accurate.
+                  */
+                 t0 = 0;
+                 ge0->ge_last_time = time_now;
+               }
+           }
+         else
+           {
+             ip4_0 = vlib_buffer_get_current (b0);
+             eth0 = (ethernet_header_t *) (((u8 *) ip4_0) - sizeof (*eth0));
+
+             gbp_learn_get_outer (eth0, &outer_src, &outer_dst);
+             ge0 = gbp_endpoint_find_ip4 (&ip4_0->src_address, fib_index0);
+
+             if (NULL == ge0)
+               {
+                 t0 = throttle_check (&glm->gl_l3_throttle, thread_index,
+                                      ip4_0->src_address.as_u32, seed);
+
+                 if (!t0)
+                   {
+                     gbp_learn_ip4_dp (&ip4_0->src_address,
+                                       fib_index0, sw_if_index0, epg0,
+                                       &outer_src, &outer_dst);
+                   }
+               }
+             else
+               {
+                 /*
+                  * this update could happen simultaneoulsy from multiple
+                  * workers but that's ok we are not interested in being
+                  * very accurate.
+                  */
+                 t0 = 0;
+                 ge0->ge_last_time = time_now;
+               }
+           }
+       trace:
+         if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
+           {
+             gbp_learn_l3_trace_t *t;
+
+             t = vlib_add_trace (vm, node, b0, sizeof (*t));
+             if (FIB_PROTOCOL_IP6 == fproto && ip6_0)
+               ip46_address_set_ip6 (&t->ip, &ip6_0->src_address);
+             if (FIB_PROTOCOL_IP4 == fproto && ip4_0)
+               ip46_address_set_ip4 (&t->ip, &ip4_0->src_address);
+             t->new = (NULL == ge0);
+             t->throttled = t0;
+             t->sw_if_index = sw_if_index0;
+             t->epg = epg0;
+           }
+
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+                                          to_next, n_left_to_next,
+                                          bi0, next0);
+       }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  return frame->n_vectors;
+}
+
+/* packet trace format function */
+static u8 *
+format_gbp_learn_l3_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  gbp_learn_l3_trace_t *t = va_arg (*args, gbp_learn_l3_trace_t *);
+
+  s = format (s, "new:%d throttled:%d ip:%U itf:%d epg:%d",
+             t->new, t->throttled,
+             format_ip46_address, &t->ip, IP46_TYPE_ANY, t->sw_if_index,
+             t->epg);
+
+  return s;
+}
+
+static uword
+gbp_learn_ip4 (vlib_main_t * vm,
+              vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+  return (gbp_learn_l3 (vm, node, frame, FIB_PROTOCOL_IP4));
+}
+
+static uword
+gbp_learn_ip6 (vlib_main_t * vm,
+              vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+  return (gbp_learn_l3 (vm, node, frame, FIB_PROTOCOL_IP6));
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (gbp_learn_ip4_node) = {
+  .function = gbp_learn_ip4,
+  .name = "gbp-learn-ip4",
+  .vector_size = sizeof (u32),
+  .format_trace = format_gbp_learn_l3_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (gbp_learn_ip4_node, gbp_learn_ip4);
+
+VNET_FEATURE_INIT (gbp_learn_ip4, static) =
+{
+  .arc_name = "ip4-unicast",
+  .node_name = "gbp-learn-ip4",
+};
+
+VLIB_REGISTER_NODE (gbp_learn_ip6_node) = {
+  .function = gbp_learn_ip6,
+  .name = "gbp-learn-ip6",
+  .vector_size = sizeof (u32),
+  .format_trace = format_gbp_learn_l3_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (gbp_learn_ip6_node, gbp_learn_ip6);
+
+VNET_FEATURE_INIT (gbp_learn_ip6, static) =
+{
+  .arc_name = "ip6-unicast",
+  .node_name = "gbp-learn-ip6",
+};
+
+/* *INDENT-ON* */
+
+void
+gbp_learn_enable (u32 sw_if_index, gbb_learn_mode_t mode)
+{
+  if (GBP_LEARN_MODE_L2 == mode)
+    l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_LEARN, 1);
+  else
+    {
+      vnet_feature_enable_disable ("ip4-unicast",
+                                  "gbp-learn-ip4", sw_if_index, 1, 0, 0);
+      vnet_feature_enable_disable ("ip6-unicast",
+                                  "gbp-learn-ip6", sw_if_index, 1, 0, 0);
+    }
+}
+
+void
+gbp_learn_disable (u32 sw_if_index, gbb_learn_mode_t mode)
+{
+  if (GBP_LEARN_MODE_L2 == mode)
+    l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_GBP_LEARN, 0);
+  else
+    {
+      vnet_feature_enable_disable ("ip4-unicast",
+                                  "gbp-learn-ip4", sw_if_index, 0, 0, 0);
+      vnet_feature_enable_disable ("ip6-unicast",
+                                  "gbp-learn-ip6", sw_if_index, 0, 0, 0);
+    }
+}
+
+static clib_error_t *
+gbp_learn_init (vlib_main_t * vm)
+{
+  gbp_learn_main_t *glm = &gbp_learn_main;
+  vlib_thread_main_t *tm = &vlib_thread_main;
+
+  /* Initialize the feature next-node indices */
+  feat_bitmap_init_next_nodes (vm,
+                              gbp_learn_l2_node.index,
+                              L2INPUT_N_FEAT,
+                              l2input_get_feat_names (),
+                              glm->gl_l2_input_feat_next);
+
+  throttle_init (&glm->gl_l2_throttle,
+                tm->n_vlib_mains, GBP_ENDPOINT_HASH_LEARN_RATE);
+
+  throttle_init (&glm->gl_l3_throttle,
+                tm->n_vlib_mains, GBP_ENDPOINT_HASH_LEARN_RATE);
+
+  glm->gl_logger = vlib_log_register_class ("gbp", "learn");
+
+  return 0;
+}
+
+VLIB_INIT_FUNCTION (gbp_learn_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/gbp/gbp_learn.h b/src/plugins/gbp/gbp_learn.h
new file mode 100644 (file)
index 0000000..836daf8
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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 __GBP_LEARN_H__
+#define __GBP_LEARN_H__
+
+#include <plugins/gbp/gbp.h>
+
+typedef enum gbp_learn_mode_t_
+{
+  GBP_LEARN_MODE_L2,
+  GBP_LEARN_MODE_L3,
+} gbb_learn_mode_t;
+
+extern void gbp_learn_enable (u32 sw_if_index, gbb_learn_mode_t mode);
+extern void gbp_learn_disable (u32 sw_if_index, gbb_learn_mode_t mode);
+
+extern void gbp_learn_set_inactive_threshold (u32 max_age);
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
index f57aa07..6d84a99 100644 (file)
@@ -15,6 +15,8 @@
 
 #include <plugins/gbp/gbp.h>
 
+#include <vnet/vxlan-gbp/vxlan_gbp_packet.h>
+
 /**
  * Grouping of global data for the GBP source EPG classification feature
  */
@@ -23,7 +25,7 @@ typedef struct gbp_policy_main_t_
   /**
    * Next nodes for L2 output features
    */
-  u32 l2_output_feat_next[32];
+  u32 l2_output_feat_next[2][32];
 } gbp_policy_main_t;
 
 static gbp_policy_main_t gbp_policy_main;
@@ -59,14 +61,16 @@ typedef enum
 typedef struct gbp_policy_trace_t_
 {
   /* per-pkt trace data */
-  epg_id_t src_epg;
-  epg_id_t dst_epg;
+  u32 src_epg;
+  u32 dst_epg;
   u32 acl_index;
+  u32 allowed;
 } gbp_policy_trace_t;
 
 static uword
-gbp_policy (vlib_main_t * vm,
-           vlib_node_runtime_t * node, vlib_frame_t * frame)
+gbp_policy_inline (vlib_main_t * vm,
+                  vlib_node_runtime_t * node,
+                  vlib_frame_t * frame, u8 is_port_based)
 {
   gbp_main_t *gm = &gbp_main;
   gbp_policy_main_t *gpm = &gbp_policy_main;
@@ -85,7 +89,8 @@ gbp_policy (vlib_main_t * vm,
 
       while (n_left_from > 0 && n_left_to_next > 0)
        {
-         const gbp_endpoint_t *gep0;
+         const ethernet_header_t *h0;
+         const gbp_endpoint_t *ge0;
          gbp_policy_next_t next0;
          gbp_contract_key_t key0;
          gbp_contract_value_t value0 = {
@@ -103,13 +108,39 @@ gbp_policy (vlib_main_t * vm,
          n_left_to_next -= 1;
 
          b0 = vlib_get_buffer (vm, bi0);
+         h0 = vlib_buffer_get_current (b0);
+         sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
 
+         /*
+          * If the A0bit is set then policy has already been applied
+          * and we skip enforcement here.
+          */
+         if (vnet_buffer2 (b0)->gbp.flags & VXLAN_GBP_GPFLAGS_A)
+           {
+             next0 = vnet_l2_feature_next (b0,
+                                           gpm->l2_output_feat_next
+                                           [is_port_based],
+                                           (is_port_based ?
+                                            L2OUTPUT_FEAT_GBP_POLICY_PORT :
+                                            L2OUTPUT_FEAT_GBP_POLICY_MAC));
+             key0.as_u32 = ~0;
+             goto trace;
+           }
          /*
           * determine the src and dst EPG
           */
-         sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
-         gep0 = gbp_endpoint_get_itf (sw_if_index0);
-         key0.gck_dst = gep0->ge_epg_id;
+         if (is_port_based)
+           ge0 = gbp_endpoint_find_itf (sw_if_index0);
+         else
+           ge0 = gbp_endpoint_find_mac (h0->dst_address,
+                                        vnet_buffer (b0)->l2.bd_index);
+
+         if (NULL != ge0)
+           key0.gck_dst = ge0->ge_epg_id;
+         else
+           /* If you cannot determine the destination EP then drop */
+           goto trace;
+
          key0.gck_src = vnet_buffer2 (b0)->gbp.src_epg;
 
          if (EPG_INVALID != key0.gck_src)
@@ -119,8 +150,14 @@ gbp_policy (vlib_main_t * vm,
                  /*
                   * intra-epg allowed
                   */
-                 next0 = vnet_l2_feature_next (b0, gpm->l2_output_feat_next,
-                                               L2OUTPUT_FEAT_GBP_POLICY);
+                 next0 =
+                   vnet_l2_feature_next (b0,
+                                         gpm->l2_output_feat_next
+                                         [is_port_based],
+                                         (is_port_based ?
+                                          L2OUTPUT_FEAT_GBP_POLICY_PORT :
+                                          L2OUTPUT_FEAT_GBP_POLICY_MAC));
+                 vnet_buffer2 (b0)->gbp.flags |= VXLAN_GBP_GPFLAGS_A;
                }
              else
                {
@@ -163,9 +200,19 @@ gbp_policy (vlib_main_t * vm,
                                                      &trace_bitmap0);
 
                      if (action0 > 0)
-                       next0 =
-                         vnet_l2_feature_next (b0, gpm->l2_output_feat_next,
-                                               L2OUTPUT_FEAT_GBP_POLICY);
+                       {
+                         vnet_buffer2 (b0)->gbp.flags |= VXLAN_GBP_GPFLAGS_A;
+
+                         next0 =
+                           vnet_l2_feature_next (b0,
+                                                 gpm->l2_output_feat_next
+                                                 [is_port_based],
+                                                 (is_port_based ?
+                                                  L2OUTPUT_FEAT_GBP_POLICY_PORT
+                                                  :
+                                                  L2OUTPUT_FEAT_GBP_POLICY_MAC));
+                         ;
+                       }
                    }
                }
            }
@@ -175,10 +222,15 @@ gbp_policy (vlib_main_t * vm,
               * the src EPG is not set when the packet arrives on an EPG
               * uplink interface and we do not need to apply policy
               */
-             next0 = vnet_l2_feature_next (b0, gpm->l2_output_feat_next,
-                                           L2OUTPUT_FEAT_GBP_POLICY);
+             next0 =
+               vnet_l2_feature_next (b0,
+                                     gpm->l2_output_feat_next[is_port_based],
+                                     (is_port_based ?
+                                      L2OUTPUT_FEAT_GBP_POLICY_PORT :
+                                      L2OUTPUT_FEAT_GBP_POLICY_MAC));
            }
 
+       trace:
          if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
            {
              gbp_policy_trace_t *t =
@@ -186,6 +238,7 @@ gbp_policy (vlib_main_t * vm,
              t->src_epg = key0.gck_src;
              t->dst_epg = key0.gck_dst;
              t->acl_index = value0.gc_acl_index;
+             t->allowed = (next0 != GBP_POLICY_NEXT_DENY);
            }
 
          /* verify speculative enqueue, maybe switch current next frame */
@@ -200,6 +253,20 @@ gbp_policy (vlib_main_t * vm,
   return frame->n_vectors;
 }
 
+static uword
+gbp_policy_port (vlib_main_t * vm,
+                vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+  return (gbp_policy_inline (vm, node, frame, 1));
+}
+
+static uword
+gbp_policy_mac (vlib_main_t * vm,
+               vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+  return (gbp_policy_inline (vm, node, frame, 0));
+}
+
 /* packet trace format function */
 static u8 *
 format_gbp_policy_trace (u8 * s, va_list * args)
@@ -209,16 +276,16 @@ format_gbp_policy_trace (u8 * s, va_list * args)
   gbp_policy_trace_t *t = va_arg (*args, gbp_policy_trace_t *);
 
   s =
-    format (s, "src:%d, dst:%d, acl:%d", t->src_epg, t->dst_epg,
-           t->acl_index);
+    format (s, "src:%d, dst:%d, acl:%d allowed:%d",
+           t->src_epg, t->dst_epg, t->acl_index, t->allowed);
 
   return s;
 }
 
 /* *INDENT-OFF* */
-VLIB_REGISTER_NODE (gbp_policy_node) = {
-  .function = gbp_policy,
-  .name = "gbp-policy",
+VLIB_REGISTER_NODE (gbp_policy_port_node) = {
+  .function = gbp_policy_port,
+  .name = "gbp-policy-port",
   .vector_size = sizeof (u32),
   .format_trace = format_gbp_policy_trace,
   .type = VLIB_NODE_TYPE_INTERNAL,
@@ -233,7 +300,26 @@ VLIB_REGISTER_NODE (gbp_policy_node) = {
   },
 };
 
-VLIB_NODE_FUNCTION_MULTIARCH (gbp_policy_node, gbp_policy);
+VLIB_NODE_FUNCTION_MULTIARCH (gbp_policy_port_node, gbp_policy_port);
+
+VLIB_REGISTER_NODE (gbp_policy_mac_node) = {
+  .function = gbp_policy_mac,
+  .name = "gbp-policy-mac",
+  .vector_size = sizeof (u32),
+  .format_trace = format_gbp_policy_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = ARRAY_LEN(gbp_policy_error_strings),
+  .error_strings = gbp_policy_error_strings,
+
+  .n_next_nodes = GBP_POLICY_N_NEXT,
+
+  .next_nodes = {
+    [GBP_POLICY_NEXT_DENY] = "error-drop",
+  },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (gbp_policy_mac_node, gbp_policy_mac);
 
 /* *INDENT-ON* */
 
@@ -245,10 +331,15 @@ gbp_policy_init (vlib_main_t * vm)
 
   /* Initialize the feature next-node indexes */
   feat_bitmap_init_next_nodes (vm,
-                              gbp_policy_node.index,
+                              gbp_policy_port_node.index,
+                              L2OUTPUT_N_FEAT,
+                              l2output_get_feat_names (),
+                              gpm->l2_output_feat_next[1]);
+  feat_bitmap_init_next_nodes (vm,
+                              gbp_policy_mac_node.index,
                               L2OUTPUT_N_FEAT,
                               l2output_get_feat_names (),
-                              gpm->l2_output_feat_next);
+                              gpm->l2_output_feat_next[0]);
 
   return error;
 }
index a2d9510..fd9dbce 100644 (file)
@@ -17,6 +17,8 @@
 #include <vnet/fib/ip4_fib.h>
 #include <vnet/fib/ip6_fib.h>
 #include <vnet/dpo/load_balance.h>
+#include <vnet/dpo/drop_dpo.h>
+#include <vnet/vxlan-gbp/vxlan_gbp_packet.h>
 
 #include <plugins/gbp/gbp.h>
 #include <plugins/gbp/gbp_policy_dpo.h>
@@ -49,7 +51,7 @@ gbp_policy_dpo_alloc (void)
 {
   gbp_policy_dpo_t *gpd;
 
-  pool_get (gbp_policy_dpo_pool, gpd);
+  pool_get_zero (gbp_policy_dpo_pool, gpd);
 
   return (gpd);
 }
@@ -110,19 +112,24 @@ gbp_policy_dpo_add_or_lock (dpo_proto_t dproto,
   dpo_id_t parent = DPO_INVALID;
 
   gpd = gbp_policy_dpo_alloc ();
-  clib_memset (gpd, 0, sizeof (*gpd));
 
   gpd->gpd_proto = dproto;
   gpd->gpd_sw_if_index = sw_if_index;
   gpd->gpd_epg = epg;
 
-  /*
-   * stack on the DVR DPO for the output interface
-   */
-  dvr_dpo_add_or_lock (sw_if_index, dproto, &parent);
+  if (~0 != sw_if_index)
+    {
+      /*
+       * stack on the DVR DPO for the output interface
+       */
+      dvr_dpo_add_or_lock (sw_if_index, dproto, &parent);
+    }
+  else
+    {
+      dpo_copy (&parent, drop_dpo_get (dproto));
+    }
 
   dpo_stack (gbp_policy_dpo_type, dproto, &gpd->gpd_dpo, &parent);
-
   dpo_set (dpo, gbp_policy_dpo_type, dproto, gbp_policy_dpo_get_index (gpd));
 }
 
@@ -144,11 +151,36 @@ format_gbp_policy_dpo (u8 * s, va_list * ap)
   return (s);
 }
 
+/**
+ * Interpose a policy DPO
+ */
+static void
+gbp_policy_dpo_interpose (const dpo_id_t * original,
+                         const dpo_id_t * parent, dpo_id_t * clone)
+{
+  gbp_policy_dpo_t *gpd, *gpd_clone;
+
+  gpd_clone = gbp_policy_dpo_alloc ();
+  gpd = gbp_policy_dpo_get (original->dpoi_index);
+
+  gpd_clone->gpd_proto = gpd->gpd_proto;
+  gpd_clone->gpd_epg = gpd->gpd_epg;
+  gpd_clone->gpd_sw_if_index = gpd->gpd_sw_if_index;
+
+  dpo_stack (gbp_policy_dpo_type,
+            gpd_clone->gpd_proto, &gpd_clone->gpd_dpo, parent);
+
+  dpo_set (clone,
+          gbp_policy_dpo_type,
+          gpd_clone->gpd_proto, gbp_policy_dpo_get_index (gpd_clone));
+}
+
 const static dpo_vft_t gbp_policy_dpo_vft = {
   .dv_lock = gbp_policy_dpo_lock,
   .dv_unlock = gbp_policy_dpo_unlock,
   .dv_format = format_gbp_policy_dpo,
   .dv_get_urpf = gbp_policy_dpo_get_urpf,
+  .dv_mk_interpose = gbp_policy_dpo_interpose,
 };
 
 /**
@@ -195,6 +227,7 @@ typedef struct gbp_policy_dpo_trace_t_
   u32 src_epg;
   u32 dst_epg;
   u32 acl_index;
+  u32 a_bit;
 } gbp_policy_dpo_trace_t;
 
 typedef enum
@@ -241,10 +274,18 @@ gbp_policy_dpo_inline (vlib_main_t * vm,
          next0 = GBP_POLICY_DROP;
 
          b0 = vlib_get_buffer (vm, bi0);
+
          gpd0 =
            gbp_policy_dpo_get_i (vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
          vnet_buffer (b0)->ip.adj_index[VLIB_TX] = gpd0->gpd_dpo.dpoi_index;
 
+         if (vnet_buffer2 (b0)->gbp.flags & VXLAN_GBP_GPFLAGS_A)
+           {
+             next0 = gpd0->gpd_dpo.dpoi_next_node;
+             key0.as_u32 = ~0;
+             goto trace;
+           }
+
          key0.gck_src = vnet_buffer2 (b0)->gbp.src_epg;
          key0.gck_dst = gpd0->gpd_epg;
 
@@ -256,6 +297,7 @@ gbp_policy_dpo_inline (vlib_main_t * vm,
                   * intra-epg allowed
                   */
                  next0 = gpd0->gpd_dpo.dpoi_next_node;
+                 vnet_buffer2 (b0)->gbp.flags |= VXLAN_GBP_GPFLAGS_A;
                }
              else
                {
@@ -287,7 +329,10 @@ gbp_policy_dpo_inline (vlib_main_t * vm,
                                                      &trace_bitmap0);
 
                      if (action0 > 0)
-                       next0 = gpd0->gpd_dpo.dpoi_next_node;
+                       {
+                         vnet_buffer2 (b0)->gbp.flags |= VXLAN_GBP_GPFLAGS_A;
+                         next0 = gpd0->gpd_dpo.dpoi_next_node;
+                       }
                    }
                }
            }
@@ -299,7 +344,7 @@ gbp_policy_dpo_inline (vlib_main_t * vm,
               */
              next0 = gpd0->gpd_dpo.dpoi_next_node;
            }
-
+       trace:
          if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
            {
              gbp_policy_dpo_trace_t *tr;
@@ -308,6 +353,7 @@ gbp_policy_dpo_inline (vlib_main_t * vm,
              tr->src_epg = key0.gck_src;
              tr->dst_epg = key0.gck_dst;
              tr->acl_index = value0.gc_acl_index;
+             tr->a_bit = vnet_buffer2 (b0)->gbp.flags & VXLAN_GBP_GPFLAGS_A;
            }
 
          vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
@@ -325,8 +371,8 @@ format_gbp_policy_dpo_trace (u8 * s, va_list * args)
   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
   gbp_policy_dpo_trace_t *t = va_arg (*args, gbp_policy_dpo_trace_t *);
 
-  s = format (s, " src-epg:%d dst-epg:%d acl-index:%d",
-             t->src_epg, t->dst_epg, t->acl_index);
+  s = format (s, " src-epg:%d dst-epg:%d acl-index:%d a-bit:%d",
+             t->src_epg, t->dst_epg, t->acl_index, t->a_bit);
 
   return s;
 }
index 95e8066..57ba408 100644 (file)
@@ -16,6 +16,7 @@
 #include <plugins/gbp/gbp_recirc.h>
 #include <plugins/gbp/gbp_endpoint_group.h>
 #include <plugins/gbp/gbp_endpoint.h>
+#include <plugins/gbp/gbp_itf.h>
 
 #include <vnet/dpo/dvr_dpo.h>
 #include <vnet/fib/fib_table.h>
@@ -30,6 +31,25 @@ gbp_recirc_t *gbp_recirc_pool;
  */
 index_t *gbp_recirc_db;
 
+/**
+ * logger
+ */
+vlib_log_class_t gr_logger;
+
+#define GBP_RECIRC_DBG(...)                           \
+    vlib_log_debug (gr_logger, __VA_ARGS__);
+
+u8 *
+format_gbp_recirc (u8 * s, va_list * args)
+{
+  gbp_recirc_t *gr = va_arg (*args, gbp_recirc_t *);
+  vnet_main_t *vnm = vnet_get_main ();
+
+  return format (s, "  %U, epg:%d, ext:%d",
+                format_vnet_sw_if_index_name, vnm,
+                gr->gr_sw_if_index, gr->gr_epg, gr->gr_is_ext);
+}
+
 int
 gbp_recirc_add (u32 sw_if_index, epg_id_t epg_id, u8 is_ext)
 {
@@ -42,8 +62,14 @@ gbp_recirc_add (u32 sw_if_index, epg_id_t epg_id, u8 is_ext)
 
   if (INDEX_INVALID == gri)
     {
-      gbp_endpoint_group_t *gepg;
+      gbp_endpoint_group_t *gg;
       fib_protocol_t fproto;
+      index_t ggi;
+
+      ggi = gbp_endpoint_group_find_and_lock (epg_id);
+
+      if (INDEX_INVALID == ggi)
+       return (VNET_API_ERROR_NO_SUCH_ENTRY);
 
       pool_get (gbp_recirc_pool, gr);
       clib_memset (gr, 0, sizeof (*gr));
@@ -62,16 +88,20 @@ gbp_recirc_add (u32 sw_if_index, epg_id_t epg_id, u8 is_ext)
       /*
        * cache the FIB indicies of the EPG
        */
-      gepg = gbp_endpoint_group_find (gr->gr_epg);
-
-      if (NULL == gepg)
-       return (VNET_API_ERROR_NO_SUCH_ENTRY);
+      gr->gr_epgi = ggi;
 
+      gg = gbp_endpoint_group_get (gr->gr_epgi);
       FOR_EACH_FIB_IP_PROTOCOL (fproto)
       {
-       gr->gr_fib_index[fproto] = gepg->gepg_fib_index[fproto];
+       gr->gr_fib_index[fproto] =
+         gbp_endpoint_group_get_fib_index (gg, fproto);
       }
 
+      /*
+       * bind to the bridge-domain of the EPG
+       */
+      gr->gr_itf = gbp_itf_add_and_lock (gr->gr_sw_if_index, gg->gg_bd_index);
+
       /*
        * Packets on the recirculation interface are subject to src-EPG
        * classification. Recirc interfaces are L2-emulation mode.
@@ -80,13 +110,19 @@ gbp_recirc_add (u32 sw_if_index, epg_id_t epg_id, u8 is_ext)
        */
       if (gr->gr_is_ext)
        {
+         mac_address_t mac;
          /*
           * recirc is for post-NAT translation packets going into
           * the external EPG, these are classified to the NAT EPG
           * based on its port
           */
+         mac_address_from_bytes (&mac,
+                                 vnet_sw_interface_get_hw_address
+                                 (vnet_get_main (), gr->gr_sw_if_index));
          gbp_endpoint_update (gr->gr_sw_if_index,
-                              NULL, NULL, gr->gr_epg, &gr->gr_ep);
+                              NULL, &mac, gr->gr_epg,
+                              GBP_ENDPOINT_FLAG_NONE,
+                              NULL, NULL, &gr->gr_ep);
          vnet_feature_enable_disable ("ip4-unicast",
                                       "ip4-gbp-src-classify",
                                       gr->gr_sw_if_index, 1, 0, 0);
@@ -111,7 +147,12 @@ gbp_recirc_add (u32 sw_if_index, epg_id_t epg_id, u8 is_ext)
 
       gbp_recirc_db[sw_if_index] = gri;
     }
+  else
+    {
+      gr = gbp_recirc_get (gri);
+    }
 
+  GBP_RECIRC_DBG ("add: %U", format_gbp_recirc, gr);
   return (0);
 }
 
@@ -127,6 +168,8 @@ gbp_recirc_delete (u32 sw_if_index)
     {
       gr = pool_elt_at_index (gbp_recirc_pool, gri);
 
+      GBP_RECIRC_DBG ("del: %U", format_gbp_recirc, gr);
+
       if (gr->gr_is_ext)
        {
          gbp_endpoint_delete (gr->gr_ep);
@@ -150,6 +193,9 @@ gbp_recirc_delete (u32 sw_if_index)
       ip4_sw_interface_enable_disable (gr->gr_sw_if_index, 0);
       ip6_sw_interface_enable_disable (gr->gr_sw_if_index, 0);
 
+      gbp_itf_unlock (gr->gr_itf);
+
+      gbp_endpoint_group_unlock (gr->gr_epgi);
       gbp_recirc_db[sw_if_index] = INDEX_INVALID;
       pool_put (gbp_recirc_pool, gr);
     }
@@ -158,12 +204,12 @@ gbp_recirc_delete (u32 sw_if_index)
 void
 gbp_recirc_walk (gbp_recirc_cb_t cb, void *ctx)
 {
-  gbp_recirc_t *gbpe;
+  gbp_recirc_t *ge;
 
   /* *INDENT-OFF* */
-  pool_foreach(gbpe, gbp_recirc_pool,
+  pool_foreach(ge, gbp_recirc_pool,
   {
-    if (!cb(gbpe, ctx))
+    if (!cb(ge, ctx))
       break;
   });
   /* *INDENT-ON* */
@@ -172,13 +218,7 @@ gbp_recirc_walk (gbp_recirc_cb_t cb, void *ctx)
 static int
 gbp_recirc_show_one (gbp_recirc_t * gr, void *ctx)
 {
-  vnet_main_t *vnm = vnet_get_main ();
-  vlib_main_t *vm;
-
-  vm = ctx;
-  vlib_cli_output (vm, "  %U, epg:%d, ext:%d",
-                  format_vnet_sw_if_index_name, vnm,
-                  gr->gr_sw_if_index, gr->gr_epg, gr->gr_is_ext);
+  vlib_cli_output (ctx, "  %U", format_gbp_recirc, gr);
 
   return (1);
 }
@@ -193,7 +233,6 @@ gbp_recirc_show (vlib_main_t * vm,
   return (NULL);
 }
 
-
 /*?
  * Show Group Based Policy Recircs and derived information
  *
@@ -209,6 +248,16 @@ VLIB_CLI_COMMAND (gbp_recirc_show_node, static) = {
 };
 /* *INDENT-ON* */
 
+static clib_error_t *
+gbp_recirc_init (vlib_main_t * vm)
+{
+  gr_logger = vlib_log_register_class ("gbp", "recirc");
+
+  return (NULL);
+}
+
+VLIB_INIT_FUNCTION (gbp_recirc_init);
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
index 148a5be..1d1a88a 100644 (file)
@@ -29,6 +29,11 @@ typedef struct gpb_recirc_t_
    */
   epg_id_t gr_epg;
 
+  /**
+   * The index of the EPG
+   */
+  index_t gr_epgi;
+
   /**
    * FIB indices the EPG is mapped to
    */
@@ -43,6 +48,7 @@ typedef struct gpb_recirc_t_
   /**
    */
   u32 gr_sw_if_index;
+  u32 gr_itf;
 
   /**
    * The endpoint created to represent the reric interface
@@ -62,7 +68,7 @@ extern void gbp_recirc_walk (gbp_recirc_cb_t bgpe, void *ctx);
 extern gbp_recirc_t *gbp_recirc_pool;
 extern index_t *gbp_recirc_db;
 
-always_inline const gbp_recirc_t *
+always_inline gbp_recirc_t *
 gbp_recirc_get (u32 sw_if_index)
 {
   return (pool_elt_at_index (gbp_recirc_pool, gbp_recirc_db[sw_if_index]));
diff --git a/src/plugins/gbp/gbp_route_domain.c b/src/plugins/gbp/gbp_route_domain.c
new file mode 100644 (file)
index 0000000..5518cc1
--- /dev/null
@@ -0,0 +1,413 @@
+/*
+ * 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 <plugins/gbp/gbp_route_domain.h>
+#include <plugins/gbp/gbp_endpoint.h>
+
+#include <vnet/dpo/dvr_dpo.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/ip/ip_neighbor.h>
+
+/**
+ * A fixed MAC address to use as the source MAC for packets L3 switched
+ * onto the routed uu-fwd interfaces.
+ * Magic values - origin lost to the mists of time...
+ */
+/* *INDENT-OFF* */
+const static mac_address_t GBP_ROUTED_SRC_MAC = {
+  .bytes = {
+    0x0, 0x22, 0xBD, 0xF8, 0x19, 0xFF,
+  }
+};
+
+const static mac_address_t GBP_ROUTED_DST_MAC = {
+  .bytes = {
+    00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+  }
+};
+/* *INDENT-ON* */
+
+/**
+ * Pool of GBP route_domains
+ */
+gbp_route_domain_t *gbp_route_domain_pool;
+
+/**
+ * DB of route_domains
+ */
+typedef struct gbp_route_domain_db_t
+{
+  uword *gbd_by_rd_id;
+} gbp_route_domain_db_t;
+
+static gbp_route_domain_db_t gbp_route_domain_db;
+
+/**
+ * logger
+ */
+vlib_log_class_t grd_logger;
+
+#define GBP_BD_DBG(...)                           \
+    vlib_log_debug (grd_logger, __VA_ARGS__);
+
+gbp_route_domain_t *
+gbp_route_domain_get (index_t i)
+{
+  return (pool_elt_at_index (gbp_route_domain_pool, i));
+}
+
+static void
+gbp_route_domain_lock (index_t i)
+{
+  gbp_route_domain_t *grd;
+
+  grd = gbp_route_domain_get (i);
+  grd->grd_locks++;
+}
+
+index_t
+gbp_route_domain_find (u32 rd_id)
+{
+  uword *p;
+
+  p = hash_get (gbp_route_domain_db.gbd_by_rd_id, rd_id);
+
+  if (NULL != p)
+    return p[0];
+
+  return (INDEX_INVALID);
+}
+
+index_t
+gbp_route_domain_find_and_lock (u32 rd_id)
+{
+  index_t grdi;
+
+  grdi = gbp_route_domain_find (rd_id);
+
+  if (INDEX_INVALID != grdi)
+    {
+      gbp_route_domain_lock (grdi);
+    }
+  return (grdi);
+}
+
+static void
+gbp_route_domain_db_add (gbp_route_domain_t * grd)
+{
+  index_t grdi = grd - gbp_route_domain_pool;
+
+  hash_set (gbp_route_domain_db.gbd_by_rd_id, grd->grd_id, grdi);
+}
+
+static void
+gbp_route_domain_db_remove (gbp_route_domain_t * grd)
+{
+  hash_unset (gbp_route_domain_db.gbd_by_rd_id, grd->grd_id);
+}
+
+int
+gbp_route_domain_add_and_lock (u32 rd_id,
+                              u32 ip4_table_id,
+                              u32 ip6_table_id,
+                              u32 ip4_uu_sw_if_index, u32 ip6_uu_sw_if_index)
+{
+  gbp_route_domain_t *grd;
+  index_t grdi;
+
+  grdi = gbp_route_domain_find (rd_id);
+
+  if (INDEX_INVALID == grdi)
+    {
+      fib_protocol_t fproto;
+
+      pool_get_zero (gbp_route_domain_pool, grd);
+
+      grd->grd_id = rd_id;
+      grd->grd_table_id[FIB_PROTOCOL_IP4] = ip4_table_id;
+      grd->grd_table_id[FIB_PROTOCOL_IP6] = ip6_table_id;
+      grd->grd_uu_sw_if_index[FIB_PROTOCOL_IP4] = ip4_uu_sw_if_index;
+      grd->grd_uu_sw_if_index[FIB_PROTOCOL_IP6] = ip6_uu_sw_if_index;
+
+      FOR_EACH_FIB_IP_PROTOCOL (fproto)
+      {
+       grd->grd_fib_index[fproto] =
+         fib_table_find_or_create_and_lock (fproto,
+                                            grd->grd_table_id[fproto],
+                                            FIB_SOURCE_PLUGIN_HI);
+
+       if (~0 != grd->grd_uu_sw_if_index[fproto])
+         {
+           ethernet_header_t *eth;
+           u8 *rewrite;
+
+           rewrite = NULL;
+           vec_validate (rewrite, sizeof (*eth) - 1);
+           eth = (ethernet_header_t *) rewrite;
+
+           eth->type = clib_host_to_net_u16 ((fproto == FIB_PROTOCOL_IP4 ?
+                                              ETHERNET_TYPE_IP4 :
+                                              ETHERNET_TYPE_IP6));
+
+           mac_address_to_bytes (gbp_route_domain_get_local_mac (),
+                                 eth->src_address);
+           mac_address_to_bytes (gbp_route_domain_get_remote_mac (),
+                                 eth->src_address);
+
+           /*
+            * create an adjacency out of the uu-fwd interfaces that will
+            * be used when adding subnet routes.
+            */
+           grd->grd_adj[fproto] =
+             adj_nbr_add_or_lock_w_rewrite (fproto,
+                                            fib_proto_to_link (fproto),
+                                            &ADJ_BCAST_ADDR,
+                                            grd->grd_uu_sw_if_index[fproto],
+                                            rewrite);
+         }
+       else
+         {
+           grd->grd_adj[fproto] = INDEX_INVALID;
+         }
+      }
+
+      gbp_route_domain_db_add (grd);
+    }
+  else
+    {
+      grd = gbp_route_domain_get (grdi);
+    }
+
+  grd->grd_locks++;
+  GBP_BD_DBG ("add: %U", format_gbp_route_domain, grd);
+
+  return (0);
+}
+
+void
+gbp_route_domain_unlock (index_t index)
+{
+  gbp_route_domain_t *grd;
+
+  grd = gbp_route_domain_get (index);
+
+  grd->grd_locks--;
+
+  if (0 == grd->grd_locks)
+    {
+      fib_protocol_t fproto;
+
+      GBP_BD_DBG ("destroy: %U", format_gbp_route_domain, grd);
+
+      FOR_EACH_FIB_IP_PROTOCOL (fproto)
+      {
+       fib_table_unlock (grd->grd_fib_index[fproto],
+                         fproto, FIB_SOURCE_PLUGIN_HI);
+       if (INDEX_INVALID != grd->grd_adj[fproto])
+         adj_unlock (grd->grd_adj[fproto]);
+      }
+
+      gbp_route_domain_db_remove (grd);
+
+      pool_put (gbp_route_domain_pool, grd);
+    }
+}
+
+int
+gbp_route_domain_delete (u32 rd_id)
+{
+  index_t grdi;
+
+  GBP_BD_DBG ("del: %d", rd_id);
+  grdi = gbp_route_domain_find (rd_id);
+
+  if (INDEX_INVALID != grdi)
+    {
+      GBP_BD_DBG ("del: %U", format_gbp_route_domain,
+                 gbp_route_domain_get (grdi));
+      gbp_route_domain_unlock (grdi);
+
+      return (0);
+    }
+
+  return (VNET_API_ERROR_NO_SUCH_ENTRY);
+}
+
+const mac_address_t *
+gbp_route_domain_get_local_mac (void)
+{
+  return (&GBP_ROUTED_SRC_MAC);
+}
+
+const mac_address_t *
+gbp_route_domain_get_remote_mac (void)
+{
+  return (&GBP_ROUTED_DST_MAC);
+}
+
+void
+gbp_route_domain_walk (gbp_route_domain_cb_t cb, void *ctx)
+{
+  gbp_route_domain_t *gbpe;
+
+  /* *INDENT-OFF* */
+  pool_foreach(gbpe, gbp_route_domain_pool,
+  {
+    if (!cb(gbpe, ctx))
+      break;
+  });
+  /* *INDENT-ON* */
+}
+
+static clib_error_t *
+gbp_route_domain_cli (vlib_main_t * vm,
+                     unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  vnet_main_t *vnm = vnet_get_main ();
+  u32 ip4_uu_sw_if_index = ~0;
+  u32 ip6_uu_sw_if_index = ~0;
+  u32 ip4_table_id = ~0;
+  u32 ip6_table_id = ~0;
+  u32 rd_id = ~0;
+  u8 add = 1;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "ip4-uu %U", unformat_vnet_sw_interface,
+                   vnm, &ip4_uu_sw_if_index))
+       ;
+      else if (unformat (input, "ip6-uu %U", unformat_vnet_sw_interface,
+                        vnm, &ip6_uu_sw_if_index))
+       ;
+      else if (unformat (input, "ip4-table-id %d", ip4_table_id))
+       ;
+      else if (unformat (input, "ip6-table-id %d", ip6_table_id))
+       ;
+      else if (unformat (input, "add"))
+       add = 1;
+      else if (unformat (input, "del"))
+       add = 0;
+      else if (unformat (input, "rd %d", &rd_id))
+       ;
+      else
+       break;
+    }
+
+  if (~0 == rd_id)
+    return clib_error_return (0, "RD-ID must be specified");
+
+  if (add)
+    {
+      if (~0 == ip4_table_id)
+       return clib_error_return (0, "IP4 table-ID must be specified");
+      if (~0 == ip6_table_id)
+       return clib_error_return (0, "IP6 table-ID must be specified");
+
+      gbp_route_domain_add_and_lock (rd_id, ip4_table_id,
+                                    ip6_table_id,
+                                    ip4_uu_sw_if_index, ip6_uu_sw_if_index);
+    }
+  else
+    gbp_route_domain_delete (rd_id);
+
+  return (NULL);
+}
+
+/*?
+ * Configure a GBP route-domain
+ *
+ * @cliexpar
+ * @cliexstart{set gbp route-domain [del] bd <ID> bvi <interface> uu-flood <interface>}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_route_domain_cli_node, static) = {
+  .path = "gbp route-domain",
+  .short_help = "gbp route-domain [del] epg bd <ID> bvi <interface> uu-flood <interface>",
+  .function = gbp_route_domain_cli,
+};
+
+u8 *
+format_gbp_route_domain (u8 * s, va_list * args)
+{
+  gbp_route_domain_t *grd = va_arg (*args, gbp_route_domain_t*);
+  vnet_main_t *vnm = vnet_get_main ();
+
+  if (NULL != grd)
+    s = format (s, "[%d] rd:%d ip4-uu:%U ip6-uu:%U locks:%d",
+                grd - gbp_route_domain_pool,
+                grd->grd_id,
+                format_vnet_sw_if_index_name, vnm, grd->grd_uu_sw_if_index[FIB_PROTOCOL_IP4],
+                format_vnet_sw_if_index_name, vnm, grd->grd_uu_sw_if_index[FIB_PROTOCOL_IP6],
+                grd->grd_locks);
+  else
+    s = format (s, "NULL");
+
+  return (s);
+}
+
+static int
+gbp_route_domain_show_one (gbp_route_domain_t *gb, void *ctx)
+{
+  vlib_main_t *vm;
+
+  vm = ctx;
+  vlib_cli_output (vm, "  %U",format_gbp_route_domain, gb);
+
+  return (1);
+}
+
+static clib_error_t *
+gbp_route_domain_show (vlib_main_t * vm,
+                  unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  vlib_cli_output (vm, "Route-Domains:");
+  gbp_route_domain_walk (gbp_route_domain_show_one, vm);
+
+  return (NULL);
+}
+
+/*?
+ * Show Group Based Policy Route_Domains and derived information
+ *
+ * @cliexpar
+ * @cliexstart{show gbp route_domain}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_route_domain_show_node, static) = {
+  .path = "show gbp route-domain",
+  .short_help = "show gbp route-domain\n",
+  .function = gbp_route_domain_show,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+gbp_route_domain_init (vlib_main_t * vm)
+{
+  grd_logger = vlib_log_register_class ("gbp", "rd");
+
+  return (NULL);
+}
+
+VLIB_INIT_FUNCTION (gbp_route_domain_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/gbp/gbp_route_domain.h b/src/plugins/gbp/gbp_route_domain.h
new file mode 100644 (file)
index 0000000..f7fc4a4
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * 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 __GBP_ROUTE_DOMAIN_H__
+#define __GBP_ROUTE_DOMAIN_H__
+
+#include <plugins/gbp/gbp_types.h>
+
+#include <vnet/fib/fib_types.h>
+#include <vnet/ethernet/mac_address.h>
+
+/**
+ * A route Domain Representation.
+ * This is a standard route-domain plus all the attributes it must
+ * have to supprt the GBP model.
+ */
+typedef struct gpb_route_domain_t_
+{
+  /**
+   * Route-domain ID
+   */
+  u32 grd_id;
+  u32 grd_fib_index[FIB_PROTOCOL_IP_MAX];
+  u32 grd_table_id[FIB_PROTOCOL_IP_MAX];
+
+  /**
+   * The RD's VNI interface on which packets from unkown endpoints
+   * arrive
+   */
+  u32 grd_vni_sw_if_index;
+
+  /**
+   * The interfaces on which to send packets to unnknown EPs
+   */
+  u32 grd_uu_sw_if_index[FIB_PROTOCOL_IP_MAX];
+
+  /**
+   * adjacencies on the UU interfaces.
+   */
+  u32 grd_adj[FIB_PROTOCOL_IP_MAX];
+
+  u32 grd_locks;
+} gbp_route_domain_t;
+
+extern int gbp_route_domain_add_and_lock (u32 rd_id,
+                                         u32 ip4_table_id,
+                                         u32 ip6_table_id,
+                                         u32 ip4_uu_sw_if_index,
+                                         u32 ip6_uu_sw_if_index);
+extern void gbp_route_domain_unlock (index_t grdi);
+extern index_t gbp_route_domain_find_and_lock (u32 rd_id);
+extern index_t gbp_route_domain_find (u32 rd_id);
+
+extern int gbp_route_domain_delete (u32 rd_id);
+extern gbp_route_domain_t *gbp_route_domain_get (index_t i);
+
+typedef int (*gbp_route_domain_cb_t) (gbp_route_domain_t * gb, void *ctx);
+extern void gbp_route_domain_walk (gbp_route_domain_cb_t bgpe, void *ctx);
+
+extern const mac_address_t *gbp_route_domain_get_local_mac (void);
+extern const mac_address_t *gbp_route_domain_get_remote_mac (void);
+
+extern u8 *format_gbp_route_domain (u8 * s, va_list * args);
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/gbp/gbp_scanner.c b/src/plugins/gbp/gbp_scanner.c
new file mode 100644 (file)
index 0000000..a2d0c9a
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * gbp.h : Group Based Policy
+ *
+ * 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 <plugins/gbp/gbp_scanner.h>
+#include <plugins/gbp/gbp_endpoint.h>
+#include <plugins/gbp/gbp_vxlan.h>
+
+vlib_log_class_t gs_logger;
+
+#define GBP_SCANNER_DBG(...)                                      \
+    vlib_log_debug (gs_logger, __VA_ARGS__);
+
+static uword
+gbp_scanner (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
+{
+  uword event_type, *event_data = 0;
+  bool enabled = 0, do_scan = 0;
+
+  while (1)
+    {
+      do_scan = 0;
+
+      if (enabled)
+       {
+         /* scan every 'inactive threshold' seconds */
+         vlib_process_wait_for_event_or_clock (vm,
+                                               gbp_endpoint_scan_threshold
+                                               ());
+       }
+      else
+       vlib_process_wait_for_event (vm);
+
+      event_type = vlib_process_get_events (vm, &event_data);
+      vec_reset_length (event_data);
+
+      switch (event_type)
+       {
+       case ~0:
+         /* timer expired */
+         do_scan = 1;
+         break;
+
+       case GBP_ENDPOINT_SCAN_START:
+         enabled = 1;
+         break;
+
+       case GBP_ENDPOINT_SCAN_STOP:
+         enabled = 0;
+         break;
+
+       default:
+         ASSERT (0);
+       }
+
+      if (do_scan)
+       {
+         GBP_SCANNER_DBG ("start");
+         gbp_endpoint_scan (vm);
+         GBP_SCANNER_DBG ("stop");
+       }
+    }
+  return 0;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (gbp_scanner_node) = {
+    .function = gbp_scanner,
+    .type = VLIB_NODE_TYPE_PROCESS,
+    .name = "gbp-scanner",
+};
+/* *INDENT-ON* */
+
+
+static clib_error_t *
+gbp_scanner_init (vlib_main_t * vm)
+{
+  gs_logger = vlib_log_register_class ("gbp", "scan");
+
+  return (NULL);
+}
+
+VLIB_INIT_FUNCTION (gbp_scanner_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/gbp/gbp_scanner.h b/src/plugins/gbp/gbp_scanner.h
new file mode 100644 (file)
index 0000000..070da38
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * 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 __GBP_SCANNER_H__
+#define __GBP_SCANNER_H__
+
+#include <vlib/vlib.h>
+
+typedef enum gbp_scan_event_t_
+{
+  GBP_ENDPOINT_SCAN_START,
+  GBP_ENDPOINT_SCAN_STOP,
+  GBP_VXLAN_SCAN_START,
+  GBP_VXLAN_SCAN_STOP,
+} gbp_scan_event_t;
+
+extern vlib_node_registration_t gbp_scanner_node;
+
+#endif
index b392511..d9d4299 100644 (file)
 #include <plugins/gbp/gbp.h>
 #include <plugins/gbp/gbp_fwd_dpo.h>
 #include <plugins/gbp/gbp_policy_dpo.h>
+#include <plugins/gbp/gbp_route_domain.h>
 
 #include <vnet/fib/fib_table.h>
 #include <vnet/dpo/load_balance.h>
 
+/**
+ * a key for the DB
+ */
+typedef struct gbp_subnet_key_t_
+{
+  fib_prefix_t gsk_pfx;
+  u32 gsk_fib_index;
+} gbp_subnet_key_t;
+
+/**
+ * Subnet
+ */
+typedef struct gbp_subnet_t_
+{
+  gbp_subnet_key_t *gs_key;
+  gbp_subnet_type_t gs_type;
+  index_t gs_rd;
+
+  union
+  {
+    struct
+    {
+      epg_id_t gs_epg;
+      u32 gs_sw_if_index;
+    } gs_stitched_external;
+  };
+} gbp_subnet_t;
+
+/**
+ * A DB of the subnets; key={pfx,fib-index}
+ */
+uword *gbp_subnet_db;
+
+/**
+ * pool of subnets
+ */
+gbp_subnet_t *gbp_subnet_pool;
+
+static index_t
+gbp_subnet_db_find (u32 fib_index, const fib_prefix_t * pfx)
+{
+  gbp_subnet_key_t key = {
+    .gsk_pfx = *pfx,
+    .gsk_fib_index = fib_index,
+  };
+  uword *p;
+
+  p = hash_get_mem (gbp_subnet_db, &key);
+
+  if (NULL != p)
+    return p[0];
+
+  return (INDEX_INVALID);
+}
+
+static void
+gbp_subnet_db_add (u32 fib_index, const fib_prefix_t * pfx, gbp_subnet_t * gs)
+{
+  gbp_subnet_key_t *key;
+
+  key = clib_mem_alloc (sizeof (*key));
+
+  clib_memcpy (&(key->gsk_pfx), pfx, sizeof (*pfx));
+  key->gsk_fib_index = fib_index;
+
+  hash_set_mem (gbp_subnet_db, key, (gs - gbp_subnet_pool));
+
+  gs->gs_key = key;
+}
+
+static void
+gbp_subnet_db_del (gbp_subnet_t * gs)
+{
+  hash_unset_mem (gbp_subnet_db, gs->gs_key);
+
+  clib_mem_free (gs->gs_key);
+  gs->gs_key = NULL;
+}
+
+
+static int
+gbp_subnet_transport_add (const gbp_subnet_t * gs)
+{
+  dpo_id_t gfd = DPO_INVALID;
+  gbp_route_domain_t *grd;
+  fib_protocol_t fproto;
+
+  fproto = gs->gs_key->gsk_pfx.fp_proto;
+  grd = gbp_route_domain_get (gs->gs_rd);
+
+  fib_table_entry_update_one_path (gs->gs_key->gsk_fib_index,
+                                  &gs->gs_key->gsk_pfx,
+                                  FIB_SOURCE_PLUGIN_HI,
+                                  FIB_ENTRY_FLAG_NONE,
+                                  fib_proto_to_dpo (fproto),
+                                  &ADJ_BCAST_ADDR,
+                                  grd->grd_uu_sw_if_index[fproto],
+                                  ~0, 1, NULL, FIB_ROUTE_PATH_FLAG_NONE);
+
+  dpo_reset (&gfd);
+
+  return (0);
+}
+
 static int
-gbp_internal_subnet_add (u32 fib_index, const fib_prefix_t * pfx)
+gbp_subnet_internal_add (const gbp_subnet_t * gs)
 {
   dpo_id_t gfd = DPO_INVALID;
 
-  gbp_fwd_dpo_add_or_lock (fib_proto_to_dpo (pfx->fp_proto), &gfd);
+  gbp_fwd_dpo_add_or_lock (fib_proto_to_dpo (gs->gs_key->gsk_pfx.fp_proto),
+                          &gfd);
 
-  fib_table_entry_special_dpo_update (fib_index,
-                                     pfx,
+  fib_table_entry_special_dpo_update (gs->gs_key->gsk_fib_index,
+                                     &gs->gs_key->gsk_pfx,
                                      FIB_SOURCE_PLUGIN_HI,
                                      FIB_ENTRY_FLAG_EXCLUSIVE, &gfd);
 
@@ -38,17 +144,19 @@ gbp_internal_subnet_add (u32 fib_index, const fib_prefix_t * pfx)
 }
 
 static int
-gbp_external_subnet_add (u32 fib_index,
-                        const fib_prefix_t * pfx,
-                        u32 sw_if_index, epg_id_t epg)
+gbp_subnet_external_add (gbp_subnet_t * gs, u32 sw_if_index, epg_id_t epg)
 {
   dpo_id_t gpd = DPO_INVALID;
 
-  gbp_policy_dpo_add_or_lock (fib_proto_to_dpo (pfx->fp_proto),
-                             epg, sw_if_index, &gpd);
+  gs->gs_stitched_external.gs_epg = epg;
+  gs->gs_stitched_external.gs_sw_if_index = sw_if_index;
+
+  gbp_policy_dpo_add_or_lock (fib_proto_to_dpo (gs->gs_key->gsk_pfx.fp_proto),
+                             gs->gs_stitched_external.gs_epg,
+                             gs->gs_stitched_external.gs_sw_if_index, &gpd);
 
-  fib_table_entry_special_dpo_update (fib_index,
-                                     pfx,
+  fib_table_entry_special_dpo_update (gs->gs_key->gsk_fib_index,
+                                     &gs->gs_key->gsk_pfx,
                                      FIB_SOURCE_PLUGIN_HI,
                                      (FIB_ENTRY_FLAG_EXCLUSIVE |
                                       FIB_ENTRY_FLAG_LOOSE_URPF_EXEMPT),
@@ -59,114 +167,255 @@ gbp_external_subnet_add (u32 fib_index,
   return (0);
 }
 
-static int
-gbp_subnet_del (u32 fib_index, const fib_prefix_t * pfx)
+int
+gbp_subnet_del (u32 rd_id, const fib_prefix_t * pfx)
 {
+  gbp_route_domain_t *grd;
+  index_t gsi, grdi;
+  gbp_subnet_t *gs;
+  u32 fib_index;
+
+  grdi = gbp_route_domain_find (rd_id);
+
+  if (~0 == grdi)
+    return (VNET_API_ERROR_NO_SUCH_FIB);
+
+  grd = gbp_route_domain_get (grdi);
+  fib_index = grd->grd_fib_index[pfx->fp_proto];
+
+  gsi = gbp_subnet_db_find (fib_index, pfx);
+
+  if (INDEX_INVALID == gsi)
+    return (VNET_API_ERROR_NO_SUCH_ENTRY);
+
+  gs = pool_elt_at_index (gbp_subnet_pool, gsi);
+
   fib_table_entry_delete (fib_index, pfx, FIB_SOURCE_PLUGIN_HI);
 
+  gbp_subnet_db_del (gs);
+  gbp_route_domain_unlock (gs->gs_rd);
+
+  pool_put (gbp_subnet_pool, gs);
+
   return (0);
 }
 
 int
-gbp_subnet_add_del (u32 table_id,
-                   const fib_prefix_t * pfx,
-                   u32 sw_if_index, epg_id_t epg, u8 is_add, u8 is_internal)
+gbp_subnet_add (u32 rd_id,
+               const fib_prefix_t * pfx,
+               gbp_subnet_type_t type, u32 sw_if_index, epg_id_t epg)
 {
+  gbp_route_domain_t *grd;
+  index_t grdi, gsi;
+  gbp_subnet_t *gs;
   u32 fib_index;
+  int rv;
 
-  fib_index = fib_table_find (pfx->fp_proto, table_id);
+  grdi = gbp_route_domain_find_and_lock (rd_id);
 
-  if (~0 == fib_index)
+  if (~0 == grdi)
     return (VNET_API_ERROR_NO_SUCH_FIB);
 
-  if (is_internal && is_add)
-    return (gbp_internal_subnet_add (fib_index, pfx));
-  else if (!is_internal && is_add)
-    return (gbp_external_subnet_add (fib_index, pfx, sw_if_index, epg));
+  grd = gbp_route_domain_get (grdi);
+  fib_index = grd->grd_fib_index[pfx->fp_proto];
 
-  return (gbp_subnet_del (fib_index, pfx));
-}
+  gsi = gbp_subnet_db_find (fib_index, pfx);
 
-typedef struct gbp_subnet_fib_table_walk_ctx_t_
-{
-  gbp_subnet_cb_t cb;
-  void *ctx;
-} gbp_subnet_fib_table_walk_ctx_t;
+  if (INDEX_INVALID != gsi)
+    return (VNET_API_ERROR_ENTRY_ALREADY_EXISTS);
 
-static fib_table_walk_rc_t
-gbp_subnet_fib_table_walk (fib_node_index_t fei, void *arg)
-{
-  gbp_subnet_fib_table_walk_ctx_t *ctx = arg;
-  const fib_prefix_t *pfx;
-  const dpo_id_t *dpo;
-  u32 table_id;
+  rv = -2;
+
+  pool_get (gbp_subnet_pool, gs);
 
-  pfx = fib_entry_get_prefix (fei);
-  table_id = fib_table_get_table_id (fib_entry_get_fib_index (fei),
-                                    pfx->fp_proto);
-  dpo = fib_entry_contribute_ip_forwarding (fei);
+  gs->gs_type = type;
+  gs->gs_rd = grdi;
+  gbp_subnet_db_add (fib_index, pfx, gs);
 
-  if (DPO_LOAD_BALANCE == dpo->dpoi_type)
+  switch (type)
     {
-      dpo = load_balance_get_bucket (dpo->dpoi_index, 0);
-
-      if (dpo->dpoi_type == gbp_policy_dpo_get_type ())
-       {
-         gbp_policy_dpo_t *gpd;
-
-         gpd = gbp_policy_dpo_get (dpo->dpoi_index);
-
-          /* *INDENT-OFF* */
-          ctx->cb (table_id, pfx,
-                   gpd->gpd_sw_if_index,
-                   gpd->gpd_epg,
-                   0,  // is_internal
-                   ctx->ctx);
-          /* *INDENT-ON* */
-       }
-      else if (dpo->dpoi_type == gbp_fwd_dpo_get_type ())
-       {
-          /* *INDENT-OFF* */
-          ctx->cb (table_id, pfx,
-                   ~0, // sw_if_index
-                   EPG_INVALID,  // epg
-                   1,   // is_internal
-                   ctx->ctx);
-          /* *INDENT-ON* */
-       }
+    case GBP_SUBNET_STITCHED_INTERNAL:
+      rv = gbp_subnet_internal_add (gs);
+      break;
+    case GBP_SUBNET_STITCHED_EXTERNAL:
+      rv = gbp_subnet_external_add (gs, sw_if_index, epg);
+      break;
+    case GBP_SUBNET_TRANSPORT:
+      rv = gbp_subnet_transport_add (gs);
+      break;
     }
 
-  return (FIB_TABLE_WALK_CONTINUE);
+  return (rv);
 }
 
 void
 gbp_subnet_walk (gbp_subnet_cb_t cb, void *ctx)
 {
-  fib_table_t *fib_table;
+  gbp_route_domain_t *grd;
+  gbp_subnet_t *gs;
+  u32 sw_if_index;
+  epg_id_t epg;
 
-  gbp_subnet_fib_table_walk_ctx_t wctx = {
-    .cb = cb,
-    .ctx = ctx,
-  };
+  epg = EPG_INVALID;
+  sw_if_index = ~0;
 
   /* *INDENT-OFF* */
-  pool_foreach (fib_table, ip4_main.fibs,
+  pool_foreach (gs, gbp_subnet_pool,
   ({
-    fib_table_walk(fib_table->ft_index,
-                   FIB_PROTOCOL_IP4,
-                   gbp_subnet_fib_table_walk,
-                   &wctx);
-  }));
-  pool_foreach (fib_table, ip6_main.fibs,
-  ({
-    fib_table_walk(fib_table->ft_index,
-                   FIB_PROTOCOL_IP6,
-                   gbp_subnet_fib_table_walk,
-                   &wctx);
+    grd = gbp_route_domain_get(gs->gs_rd);
+
+    switch (gs->gs_type)
+      {
+      case GBP_SUBNET_STITCHED_INTERNAL:
+      case GBP_SUBNET_TRANSPORT:
+        /* use defaults above */
+        break;
+      case GBP_SUBNET_STITCHED_EXTERNAL:
+        sw_if_index = gs->gs_stitched_external.gs_sw_if_index;
+        epg = gs->gs_stitched_external.gs_epg;
+        break;
+      }
+
+    if (WALK_STOP == cb (grd->grd_id, &gs->gs_key->gsk_pfx,
+                         gs->gs_type, epg, sw_if_index, ctx))
+      break;
   }));
   /* *INDENT-ON* */
 }
 
+typedef enum gsb_subnet_show_flags_t_
+{
+  GBP_SUBNET_SHOW_BRIEF,
+  GBP_SUBNET_SHOW_DETAILS,
+} gsb_subnet_show_flags_t;
+
+static u8 *
+format_gbp_subnet_type (u8 * s, va_list * args)
+{
+  gbp_subnet_type_t type = va_arg (*args, gbp_subnet_type_t);
+
+  switch (type)
+    {
+    case GBP_SUBNET_STITCHED_INTERNAL:
+      return (format (s, "stitched-internal"));
+    case GBP_SUBNET_STITCHED_EXTERNAL:
+      return (format (s, "stitched-external"));
+    case GBP_SUBNET_TRANSPORT:
+      return (format (s, "transport"));
+    }
+
+  return (format (s, "unknown"));
+}
+
+u8 *
+format_gbp_subnet (u8 * s, va_list * args)
+{
+  index_t gsi = va_arg (*args, index_t);
+  gsb_subnet_show_flags_t flags = va_arg (*args, gsb_subnet_show_flags_t);
+  gbp_subnet_t *gs;
+  u32 table_id;
+
+  gs = pool_elt_at_index (gbp_subnet_pool, gsi);
+
+  table_id = fib_table_get_table_id (gs->gs_key->gsk_fib_index,
+                                    gs->gs_key->gsk_pfx.fp_proto);
+
+  s = format (s, "[%d] tbl:%d %U %U", gsi, table_id,
+             format_fib_prefix, &gs->gs_key->gsk_pfx,
+             format_gbp_subnet_type, gs->gs_type);
+
+  switch (gs->gs_type)
+    {
+    case GBP_SUBNET_STITCHED_INTERNAL:
+    case GBP_SUBNET_TRANSPORT:
+      break;
+    case GBP_SUBNET_STITCHED_EXTERNAL:
+      s = format (s, " {epg:%d %U}", gs->gs_stitched_external.gs_epg,
+                 format_vnet_sw_if_index_name,
+                 vnet_get_main (), gs->gs_stitched_external.gs_sw_if_index);
+      break;
+    }
+
+  switch (flags)
+    {
+    case GBP_SUBNET_SHOW_DETAILS:
+      {
+       fib_node_index_t fei;
+
+       fei = fib_table_lookup_exact_match (gs->gs_key->gsk_fib_index,
+                                           &gs->gs_key->gsk_pfx);
+
+       s =
+         format (s, "\n  %U", format_fib_entry, fei,
+                 FIB_ENTRY_FORMAT_DETAIL);
+      }
+    case GBP_SUBNET_SHOW_BRIEF:
+      break;
+    }
+  return (s);
+}
+
+static clib_error_t *
+gbp_subnet_show (vlib_main_t * vm,
+                unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  u32 gsi;
+
+  gsi = INDEX_INVALID;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "%d", &gsi))
+       ;
+      else
+       break;
+    }
+
+  if (INDEX_INVALID != gsi)
+    {
+      vlib_cli_output (vm, "%U", format_gbp_subnet, gsi,
+                      GBP_SUBNET_SHOW_DETAILS);
+    }
+  else
+    {
+      /* *INDENT-OFF* */
+      pool_foreach_index(gsi, gbp_subnet_pool,
+      ({
+        vlib_cli_output (vm, "%U", format_gbp_subnet, gsi,
+                         GBP_SUBNET_SHOW_BRIEF);
+      }));
+      /* *INDENT-ON* */
+    }
+
+  return (NULL);
+}
+
+/*?
+ * Show Group Based Policy Subnets
+ *
+ * @cliexpar
+ * @cliexstart{show gbp subnet}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_subnet_show_node, static) = {
+  .path = "show gbp subnet",
+  .short_help = "show gbp subnet\n",
+  .function = gbp_subnet_show,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+gbp_subnet_init (vlib_main_t * vm)
+{
+  gbp_subnet_db = hash_create_mem (0,
+                                  sizeof (gbp_subnet_key_t), sizeof (u32));
+
+  return (NULL);
+}
+
+VLIB_INIT_FUNCTION (gbp_subnet_init);
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
index 24b4f3a..b6906de 100644 (file)
 
 #include <plugins/gbp/gbp_types.h>
 
-extern int gbp_subnet_add_del (u32 table_id,
-                              const fib_prefix_t * pfx,
-                              u32 sw_if_index,
-                              epg_id_t epg, u8 is_add, u8 is_internal);
+typedef enum gbp_subnet_type_t_
+{
+  GBP_SUBNET_TRANSPORT,
+  GBP_SUBNET_STITCHED_INTERNAL,
+  GBP_SUBNET_STITCHED_EXTERNAL,
+} gbp_subnet_type_t;
 
+extern int gbp_subnet_add (u32 rd_id,
+                          const fib_prefix_t * pfx,
+                          gbp_subnet_type_t type,
+                          u32 sw_if_index, epg_id_t epg);
+
+extern int gbp_subnet_del (u32 rd_id, const fib_prefix_t * pfx);
+
+typedef walk_rc_t (*gbp_subnet_cb_t) (u32 rd_id,
+                                     const fib_prefix_t * pfx,
+                                     gbp_subnet_type_t type,
+                                     u32 sw_if_index,
+                                     epg_id_t epg, void *ctx);
 
-typedef int (*gbp_subnet_cb_t) (u32 table_id,
-                               const fib_prefix_t * pfx,
-                               u32 sw_if_index,
-                               epg_id_t epg, u8 is_internal, void *ctx);
 extern void gbp_subnet_walk (gbp_subnet_cb_t cb, void *ctx);
 
 #endif
diff --git a/src/plugins/gbp/gbp_vxlan.c b/src/plugins/gbp/gbp_vxlan.c
new file mode 100644 (file)
index 0000000..b29fc11
--- /dev/null
@@ -0,0 +1,880 @@
+/*
+ * 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 <plugins/gbp/gbp_vxlan.h>
+#include <plugins/gbp/gbp_itf.h>
+#include <plugins/gbp/gbp_learn.h>
+#include <plugins/gbp/gbp_bridge_domain.h>
+#include <plugins/gbp/gbp_route_domain.h>
+
+#include <vnet/vxlan-gbp/vxlan_gbp.h>
+#include <vlibmemory/api.h>
+#include <vnet/fib/fib_table.h>
+
+/**
+ * A reference to a VXLAN-GBP tunnel created as a child/dependent tunnel
+ * of the tempplate GBP-VXLAN tunnel
+ */
+typedef struct vxlan_tunnel_ref_t_
+{
+  u32 vxr_sw_if_index;
+  index_t vxr_itf;
+  u32 vxr_locks;
+  index_t vxr_parent;
+  gbp_vxlan_tunnel_layer_t vxr_layer;
+} vxlan_tunnel_ref_t;
+
+/**
+ * DB of added tunnels
+ */
+uword *gv_db;
+
+/**
+ * Logger
+ */
+vlib_log_class_t gt_logger;
+
+/**
+ * Pool of template tunnels
+ */
+gbp_vxlan_tunnel_t *gbp_vxlan_tunnel_pool;
+
+/**
+ * Pool of child tunnels
+ */
+vxlan_tunnel_ref_t *vxlan_tunnel_ref_pool;
+
+/**
+ * DB of template interfaces by SW interface index
+ */
+index_t *gbp_vxlan_tunnel_db;
+
+/**
+ * DB of child interfaces by SW interface index
+ */
+index_t *vxlan_tunnel_ref_db;
+
+
+static char *gbp_vxlan_tunnel_layer_strings[] = {
+#define _(n,s) [GBP_VXLAN_TUN_##n] = s,
+  forecah_gbp_vxlan_tunnel_layer
+#undef _
+};
+
+#define GBP_VXLAN_TUN_DBG(...)                          \
+    vlib_log_debug (gt_logger, __VA_ARGS__);
+
+
+
+always_inline gbp_vxlan_tunnel_t *
+gbp_vxlan_tunnel_get (index_t gti)
+{
+  return (pool_elt_at_index (gbp_vxlan_tunnel_pool, gti));
+}
+
+static vxlan_tunnel_ref_t *
+vxlan_tunnel_ref_get (index_t vxri)
+{
+  return (pool_elt_at_index (vxlan_tunnel_ref_pool, vxri));
+}
+
+static u8 *
+format_vxlan_tunnel_ref (u8 * s, va_list * args)
+{
+  index_t vxri = va_arg (*args, u32);
+  vxlan_tunnel_ref_t *vxr;
+
+  vxr = vxlan_tunnel_ref_get (vxri);
+
+  s = format (s, "[%U locks:%d]", format_vnet_sw_if_index_name,
+             vnet_get_main (), vxr->vxr_sw_if_index, vxr->vxr_locks);
+
+  return (s);
+}
+
+static u32
+gdb_vxlan_dep_add (gbp_vxlan_tunnel_t * gt,
+                  u32 vni,
+                  const ip46_address_t * src, const ip46_address_t * dst)
+{
+  vnet_vxlan_gbp_tunnel_add_del_args_t args = {
+    .is_add = 1,
+    .is_ip6 = !ip46_address_is_ip4 (src),
+    .vni = vni,
+    .src = *src,
+    .dst = *dst,
+    .instance = ~0,
+    .mode = (GBP_VXLAN_TUN_L2 == gt->gt_layer ?
+            VXLAN_GBP_TUNNEL_MODE_L2 : VXLAN_GBP_TUNNEL_MODE_L3),
+  };
+  vxlan_tunnel_ref_t *vxr;
+  u32 sw_if_index;
+  index_t vxri;
+  int rv;
+
+  sw_if_index = ~0;
+  rv = vnet_vxlan_gbp_tunnel_add_del (&args, &sw_if_index);
+
+  if (VNET_API_ERROR_TUNNEL_EXIST == rv)
+    {
+      vxri = vxlan_tunnel_ref_db[sw_if_index];
+
+      vxr = vxlan_tunnel_ref_get (vxri);
+      vxr->vxr_locks++;
+    }
+  else if (0 == rv)
+    {
+      ASSERT (~0 != sw_if_index);
+      GBP_VXLAN_TUN_DBG ("add-dep:%U %U %U %d", format_vnet_sw_if_index_name,
+                        vnet_get_main (), sw_if_index,
+                        format_ip46_address, src, IP46_TYPE_ANY,
+                        format_ip46_address, dst, IP46_TYPE_ANY, vni);
+
+      pool_get_zero (vxlan_tunnel_ref_pool, vxr);
+
+      vxri = (vxr - vxlan_tunnel_ref_pool);
+      vxr->vxr_parent = gt - gbp_vxlan_tunnel_pool;
+      vxr->vxr_sw_if_index = sw_if_index;
+      vxr->vxr_locks = 1;
+      vxr->vxr_layer = gt->gt_layer;
+
+      /*
+       * store the child both on the parent's list and the global DB
+       */
+      vec_add1 (gt->gt_tuns, vxri);
+
+      vec_validate_init_empty (vxlan_tunnel_ref_db,
+                              vxr->vxr_sw_if_index, INDEX_INVALID);
+      vxlan_tunnel_ref_db[vxr->vxr_sw_if_index] = vxri;
+
+      if (GBP_VXLAN_TUN_L2 == vxr->vxr_layer)
+       {
+         vxr->vxr_itf = gbp_itf_add_and_lock (vxr->vxr_sw_if_index,
+                                              gt->gt_bd_index);
+
+         gbp_itf_set_l2_output_feature (vxr->vxr_itf, vxr->vxr_sw_if_index,
+                                        L2OUTPUT_FEAT_GBP_POLICY_MAC);
+         gbp_itf_set_l2_input_feature (vxr->vxr_itf, vxr->vxr_sw_if_index,
+                                       L2INPUT_FEAT_GBP_LEARN);
+       }
+      else
+       {
+         const gbp_route_domain_t *grd;
+         fib_protocol_t fproto;
+
+         grd = gbp_route_domain_get (gt->gt_grd);
+
+         FOR_EACH_FIB_IP_PROTOCOL (fproto)
+           ip_table_bind (fproto, vxr->vxr_sw_if_index,
+                          grd->grd_table_id[fproto], 1);
+
+         gbp_learn_enable (vxr->vxr_sw_if_index, GBP_LEARN_MODE_L3);
+       }
+    }
+
+  return (sw_if_index);
+}
+
+u32
+vxlan_gbp_tunnel_get_parent (u32 sw_if_index)
+{
+  ASSERT ((sw_if_index < vec_len (vxlan_tunnel_ref_db)) &&
+         (INDEX_INVALID != vxlan_tunnel_ref_db[sw_if_index]));
+
+  gbp_vxlan_tunnel_t *gt;
+  vxlan_tunnel_ref_t *vxr;
+
+  vxr = vxlan_tunnel_ref_get (vxlan_tunnel_ref_db[sw_if_index]);
+  gt = gbp_vxlan_tunnel_get (vxr->vxr_parent);
+
+  return (gt->gt_sw_if_index);
+}
+
+gbp_vxlan_tunnel_type_t
+gbp_vxlan_tunnel_get_type (u32 sw_if_index)
+{
+  if (sw_if_index < vec_len (vxlan_tunnel_ref_db) &&
+      INDEX_INVALID != vxlan_tunnel_ref_db[sw_if_index])
+    {
+      return (VXLAN_GBP_TUNNEL);
+    }
+  else if (sw_if_index < vec_len (gbp_vxlan_tunnel_db) &&
+          INDEX_INVALID != gbp_vxlan_tunnel_db[sw_if_index])
+    {
+      return (GBP_VXLAN_TEMPLATE_TUNNEL);
+    }
+
+  ASSERT (0);
+  return (GBP_VXLAN_TEMPLATE_TUNNEL);
+}
+
+u32
+gbp_vxlan_tunnel_clone_and_lock (u32 sw_if_index,
+                                const ip46_address_t * src,
+                                const ip46_address_t * dst)
+{
+  gbp_vxlan_tunnel_t *gt;
+  index_t gti;
+
+  gti = gbp_vxlan_tunnel_db[sw_if_index];
+
+  if (INDEX_INVALID == gti)
+    return (~0);
+
+  gt = pool_elt_at_index (gbp_vxlan_tunnel_pool, gti);
+
+  return (gdb_vxlan_dep_add (gt, gt->gt_vni, src, dst));
+}
+
+static void
+gdb_vxlan_dep_del (index_t vxri)
+{
+  vxlan_tunnel_ref_t *vxr;
+  gbp_vxlan_tunnel_t *gt;
+  u32 pos;
+
+  vxr = vxlan_tunnel_ref_get (vxri);
+  gt = gbp_vxlan_tunnel_get (vxr->vxr_parent);
+
+  GBP_VXLAN_TUN_DBG ("del-dep:%U", format_vxlan_tunnel_ref, vxri);
+
+  vxlan_tunnel_ref_db[vxr->vxr_sw_if_index] = INDEX_INVALID;
+  pos = vec_search (gt->gt_tuns, vxri);
+
+  ASSERT (~0 != pos);
+  vec_del1 (gt->gt_tuns, pos);
+
+  if (GBP_VXLAN_TUN_L2 == vxr->vxr_layer)
+    {
+      gbp_itf_set_l2_output_feature (vxr->vxr_itf, vxr->vxr_sw_if_index,
+                                    L2OUTPUT_FEAT_NONE);
+      gbp_itf_set_l2_input_feature (vxr->vxr_itf, vxr->vxr_sw_if_index,
+                                   L2INPUT_FEAT_NONE);
+      gbp_itf_unlock (vxr->vxr_itf);
+    }
+  else
+    {
+      fib_protocol_t fproto;
+
+      FOR_EACH_FIB_IP_PROTOCOL (fproto)
+       ip_table_bind (fproto, vxr->vxr_sw_if_index, 0, 0);
+    }
+
+  vnet_vxlan_gbp_tunnel_del (vxr->vxr_sw_if_index);
+
+  pool_put (vxlan_tunnel_ref_pool, vxr);
+}
+
+void
+vxlan_gbp_tunnel_unlock (u32 sw_if_index)
+{
+  vxlan_tunnel_ref_t *vxr;
+  index_t vxri;
+
+  vxri = vxlan_tunnel_ref_db[sw_if_index];
+
+  ASSERT (vxri != INDEX_INVALID);
+
+  vxr = vxlan_tunnel_ref_get (vxri);
+  vxr->vxr_locks--;
+
+  if (0 == vxr->vxr_locks)
+    {
+      gdb_vxlan_dep_del (vxri);
+    }
+}
+
+void
+vxlan_gbp_tunnel_lock (u32 sw_if_index)
+{
+  vxlan_tunnel_ref_t *vxr;
+  index_t vxri;
+
+  vxri = vxlan_tunnel_ref_db[sw_if_index];
+
+  ASSERT (vxri != INDEX_INVALID);
+
+  vxr = vxlan_tunnel_ref_get (vxri);
+  vxr->vxr_locks++;
+}
+
+#define foreach_gbp_vxlan_input_next         \
+  _(DROP, "error-drop")                      \
+  _(L2_INPUT, "l2-input")                    \
+  _(IP4_INPUT, "ip4-input")                  \
+  _(IP6_INPUT, "ip6-input")
+
+typedef enum
+{
+#define _(s,n) GBP_VXLAN_INPUT_NEXT_##s,
+  foreach_gbp_vxlan_input_next
+#undef _
+    GBP_VXLAN_INPUT_N_NEXT,
+} gbp_vxlan_input_next_t;
+
+#define foreach_gbp_vxlan_error              \
+  _(DECAPPED, "decapped")                    \
+  _(LEARNED, "learned")
+
+typedef enum
+{
+#define _(s,n) GBP_VXLAN_ERROR_##s,
+  foreach_gbp_vxlan_error
+#undef _
+    GBP_VXLAN_N_ERROR,
+} gbp_vxlan_input_error_t;
+
+static char *gbp_vxlan_error_strings[] = {
+#define _(n,s) s
+  foreach_gbp_vxlan_error
+#undef _
+};
+
+typedef struct gbp_vxlan_trace_t_
+{
+  u8 dropped;
+  u32 vni;
+  u32 sw_if_index;
+  u16 sclass;
+  u8 flags;
+} gbp_vxlan_trace_t;
+
+
+static uword
+gbp_vxlan_decap (vlib_main_t * vm,
+                vlib_node_runtime_t * node,
+                vlib_frame_t * from_frame, u8 is_ip4)
+{
+  u32 n_left_to_next, n_left_from, next_index, *to_next, *from;
+
+  next_index = 0;
+  from = vlib_frame_vector_args (from_frame);
+  n_left_from = from_frame->n_vectors;
+
+  while (n_left_from > 0)
+    {
+
+      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+
+      while (n_left_from > 0 && n_left_to_next > 0)
+       {
+         vxlan_gbp_header_t *vxlan_gbp0;
+         gbp_vxlan_input_next_t next0;
+         gbp_vxlan_tunnel_t *gt0;
+         vlib_buffer_t *b0;
+         u32 bi0, vni0;
+         uword *p;
+
+         bi0 = to_next[0] = from[0];
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+         next0 = GBP_VXLAN_INPUT_NEXT_DROP;
+
+         b0 = vlib_get_buffer (vm, bi0);
+         vxlan_gbp0 =
+           vlib_buffer_get_current (b0) - sizeof (vxlan_gbp_header_t);
+
+         vni0 = vxlan_gbp_get_vni (vxlan_gbp0);
+         p = hash_get (gv_db, vni0);
+
+         if (PREDICT_FALSE (NULL == p))
+           {
+             gt0 = NULL;
+             next0 = GBP_VXLAN_INPUT_NEXT_DROP;
+           }
+         else
+           {
+             gt0 = gbp_vxlan_tunnel_get (p[0]);
+
+             vnet_buffer (b0)->sw_if_index[VLIB_RX] = gt0->gt_sw_if_index;
+
+             if (GBP_VXLAN_TUN_L2 == gt0->gt_layer)
+               /*
+                * An L2 layer tunnel goes into the BD
+                */
+               next0 = GBP_VXLAN_INPUT_NEXT_L2_INPUT;
+             else
+               {
+                 /*
+                  * An L3 layer tunnel needs to strip the L2 header
+                  * an inject into the RD
+                  */
+                 ethernet_header_t *e0;
+                 u16 type0;
+
+                 e0 = vlib_buffer_get_current (b0);
+                 type0 = clib_net_to_host_u16 (e0->type);
+                 switch (type0)
+                   {
+                   case ETHERNET_TYPE_IP4:
+                     next0 = GBP_VXLAN_INPUT_NEXT_IP4_INPUT;
+                     break;
+                   case ETHERNET_TYPE_IP6:
+                     next0 = GBP_VXLAN_INPUT_NEXT_IP6_INPUT;
+                     break;
+                   default:
+                     goto trace;
+                   }
+                 vlib_buffer_advance (b0, sizeof (*e0));
+               }
+           }
+
+       trace:
+         if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
+           {
+             gbp_vxlan_trace_t *tr;
+
+             tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
+             tr->dropped = (next0 == GBP_VXLAN_INPUT_NEXT_DROP);
+             tr->vni = vni0;
+             tr->sw_if_index = (gt0 ? gt0->gt_sw_if_index : ~0);
+             tr->flags = vxlan_gbp_get_gpflags (vxlan_gbp0);
+             tr->sclass = vxlan_gbp_get_sclass (vxlan_gbp0);
+           }
+
+         vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
+                                          to_next, n_left_to_next,
+                                          bi0, next0);
+       }
+
+      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+    }
+
+  return from_frame->n_vectors;
+}
+
+static u8 *
+format_gbp_vxlan_rx_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  gbp_vxlan_trace_t *t = va_arg (*args, gbp_vxlan_trace_t *);
+
+  s = format (s, "vni:%d dropped:%d rx:%d sclass:%d flags:%U",
+             t->vni, t->dropped, t->sw_if_index,
+             t->sclass, format_vxlan_gbp_header_gpflags, t->flags);
+
+  return (s);
+}
+
+static uword
+gbp_vxlan4_decap (vlib_main_t * vm,
+                 vlib_node_runtime_t * node, vlib_frame_t * from_frame)
+{
+  return gbp_vxlan_decap (vm, node, from_frame, 1);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (gbp_vxlan4_input_node) =
+{
+  .function = gbp_vxlan4_decap,
+  .name = "gbp-vxlan4",
+  .vector_size = sizeof (u32),
+  .n_errors = GBP_VXLAN_N_ERROR,
+  .error_strings = gbp_vxlan_error_strings,
+  .n_next_nodes = GBP_VXLAN_INPUT_N_NEXT,
+  .format_trace = format_gbp_vxlan_rx_trace,
+  .next_nodes = {
+#define _(s,n) [GBP_VXLAN_INPUT_NEXT_##s] = n,
+    foreach_gbp_vxlan_input_next
+#undef _
+  },
+};
+VLIB_NODE_FUNCTION_MULTIARCH (gbp_vxlan4_input_node, gbp_vxlan4_decap)
+
+/* *INDENT-ON* */
+
+void
+gbp_vxlan_walk (gbp_vxlan_cb_t cb, void *ctx)
+{
+  gbp_vxlan_tunnel_t *gt;
+
+  /* *INDENT-OFF* */
+  pool_foreach (gt, gbp_vxlan_tunnel_pool,
+    ({
+      if (WALK_CONTINUE != cb(gt, ctx))
+        break;
+    }));
+  /* *INDENT-ON* */
+}
+
+static walk_rc_t
+gbp_vxlan_tunnel_show_one (gbp_vxlan_tunnel_t * gt, void *ctx)
+{
+  vlib_cli_output (ctx, "%U", format_gbp_vxlan_tunnel,
+                  gt - gbp_vxlan_tunnel_pool);
+
+  return (WALK_CONTINUE);
+}
+
+static u8 *
+format_gbp_vxlan_tunnel_name (u8 * s, va_list * args)
+{
+  u32 dev_instance = va_arg (*args, u32);
+
+  return format (s, "gbp-vxlan-%d", dev_instance);
+}
+
+u8 *
+format_gbp_vxlan_tunnel_layer (u8 * s, va_list * args)
+{
+  gbp_vxlan_tunnel_layer_t gl = va_arg (*args, gbp_vxlan_tunnel_layer_t);
+  s = format (s, "%s", gbp_vxlan_tunnel_layer_strings[gl]);
+
+  return (s);
+}
+
+u8 *
+format_gbp_vxlan_tunnel (u8 * s, va_list * args)
+{
+  u32 dev_instance = va_arg (*args, u32);
+  CLIB_UNUSED (int verbose) = va_arg (*args, int);
+  gbp_vxlan_tunnel_t *gt = gbp_vxlan_tunnel_get (dev_instance);
+  index_t *vxri;
+
+  s = format (s, "GBP VXLAN tunnel: hw:%d sw:%d vni:%d %U",
+             gt->gt_hw_if_index, gt->gt_sw_if_index, gt->gt_vni,
+             format_gbp_vxlan_tunnel_layer, gt->gt_layer);
+  if (GBP_VXLAN_TUN_L2 == gt->gt_layer)
+    s = format (s, " BD:%d bd-index:%d", gt->gt_bd_rd_id, gt->gt_bd_index);
+  else
+    s = format (s, " RD:%d fib-index:[%d,%d]",
+               gt->gt_bd_rd_id,
+               gt->gt_fib_index[FIB_PROTOCOL_IP4],
+               gt->gt_fib_index[FIB_PROTOCOL_IP6]);
+
+  s = format (s, " children:[");
+  vec_foreach (vxri, gt->gt_tuns)
+  {
+    s = format (s, "%U, ", format_vxlan_tunnel_ref, *vxri);
+  }
+  s = format (s, "]");
+
+  return s;
+}
+
+typedef struct gbp_vxlan_tx_trace_t_
+{
+  u32 vni;
+} gbp_vxlan_tx_trace_t;
+
+u8 *
+format_gbp_vxlan_tx_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  gbp_vxlan_tx_trace_t *t = va_arg (*args, gbp_vxlan_tx_trace_t *);
+
+  s = format (s, "GBP-VXLAN: vni:%d", t->vni);
+
+  return (s);
+}
+
+clib_error_t *
+gbp_vxlan_interface_admin_up_down (vnet_main_t * vnm,
+                                  u32 hw_if_index, u32 flags)
+{
+  vnet_hw_interface_t *hi;
+  u32 ti;
+
+  hi = vnet_get_hw_interface (vnm, hw_if_index);
+
+  if (NULL == gbp_vxlan_tunnel_db ||
+      hi->sw_if_index >= vec_len (gbp_vxlan_tunnel_db))
+    return (NULL);
+
+  ti = gbp_vxlan_tunnel_db[hi->sw_if_index];
+
+  if (~0 == ti)
+    /* not one of ours */
+    return (NULL);
+
+  if (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP)
+    vnet_hw_interface_set_flags (vnm, hw_if_index,
+                                VNET_HW_INTERFACE_FLAG_LINK_UP);
+  else
+    vnet_hw_interface_set_flags (vnm, hw_if_index, 0);
+
+  return (NULL);
+}
+
+static uword
+gbp_vxlan_interface_tx (vlib_main_t * vm,
+                       vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+  clib_warning ("you shouldn't be here, leaking buffers...");
+  return frame->n_vectors;
+}
+
+/* *INDENT-OFF* */
+VNET_DEVICE_CLASS (gbp_vxlan_device_class) = {
+  .name = "GBP VXLAN tunnel-template",
+  .format_device_name = format_gbp_vxlan_tunnel_name,
+  .format_device = format_gbp_vxlan_tunnel,
+  .format_tx_trace = format_gbp_vxlan_tx_trace,
+  .admin_up_down_function = gbp_vxlan_interface_admin_up_down,
+  .tx_function = gbp_vxlan_interface_tx,
+};
+
+VNET_HW_INTERFACE_CLASS (gbp_vxlan_hw_interface_class) = {
+  .name = "GBP-VXLAN",
+  .flags = VNET_HW_INTERFACE_CLASS_FLAG_P2P,
+};
+/* *INDENT-ON* */
+
+int
+gbp_vxlan_tunnel_add (u32 vni, gbp_vxlan_tunnel_layer_t layer,
+                     u32 bd_rd_id, u32 * sw_if_indexp)
+{
+  gbp_vxlan_tunnel_t *gt;
+  index_t gti;
+  uword *p;
+  int rv;
+
+  rv = 0;
+  p = hash_get (gv_db, vni);
+
+  GBP_VXLAN_TUN_DBG ("add: %d %d %d", vni, layer, bd_rd_id);
+
+  if (NULL == p)
+    {
+      vnet_sw_interface_t *si;
+      vnet_hw_interface_t *hi;
+      index_t gbi, grdi;
+      vnet_main_t *vnm;
+
+      gbi = grdi = INDEX_INVALID;
+
+      if (layer == GBP_VXLAN_TUN_L2)
+       {
+         gbi = gbp_bridge_domain_find_and_lock (bd_rd_id);
+
+         if (INDEX_INVALID == gbi)
+           {
+             return (VNET_API_ERROR_BD_NOT_MODIFIABLE);
+           }
+       }
+      else
+       {
+         grdi = gbp_route_domain_find_and_lock (bd_rd_id);
+
+         if (INDEX_INVALID == grdi)
+           {
+             return (VNET_API_ERROR_NO_SUCH_FIB);
+           }
+       }
+
+      vnm = vnet_get_main ();
+      pool_get (gbp_vxlan_tunnel_pool, gt);
+      gti = gt - gbp_vxlan_tunnel_pool;
+
+      gt->gt_vni = vni;
+      gt->gt_layer = layer;
+      gt->gt_bd_rd_id = bd_rd_id;
+      gt->gt_hw_if_index = vnet_register_interface (vnm,
+                                                   gbp_vxlan_device_class.index,
+                                                   gti,
+                                                   gbp_vxlan_hw_interface_class.index,
+                                                   gti);
+
+      hi = vnet_get_hw_interface (vnm, gt->gt_hw_if_index);
+
+      gt->gt_sw_if_index = hi->sw_if_index;
+
+      /* don't flood packets in a BD to these interfaces */
+      si = vnet_get_sw_interface (vnm, gt->gt_sw_if_index);
+      si->flood_class = VNET_FLOOD_CLASS_NO_FLOOD;
+
+      if (layer == GBP_VXLAN_TUN_L2)
+       {
+         gbp_bridge_domain_t *gb;
+
+         gb = gbp_bridge_domain_get (gbi);
+
+         gt->gt_gbd = gbi;
+         gt->gt_bd_index = gb->gb_bd_id;
+         gb->gb_vni_sw_if_index = gt->gt_sw_if_index;
+         /* set it up as a GBP interface */
+         gt->gt_itf = gbp_itf_add_and_lock (gt->gt_sw_if_index,
+                                            gt->gt_bd_index);
+         gbp_learn_enable (gt->gt_sw_if_index, GBP_LEARN_MODE_L2);
+       }
+      else
+       {
+         gbp_route_domain_t *grd;
+         fib_protocol_t fproto;
+
+         grd = gbp_route_domain_get (grdi);
+
+         gt->gt_grd = grdi;
+         grd->grd_vni_sw_if_index = gt->gt_sw_if_index;
+
+         gbp_learn_enable (gt->gt_sw_if_index, GBP_LEARN_MODE_L3);
+
+         ip4_sw_interface_enable_disable (gt->gt_sw_if_index, 1);
+         ip6_sw_interface_enable_disable (gt->gt_sw_if_index, 1);
+
+         FOR_EACH_FIB_IP_PROTOCOL (fproto)
+         {
+           gt->gt_fib_index[fproto] = grd->grd_fib_index[fproto];
+
+           ip_table_bind (fproto, gt->gt_sw_if_index,
+                          grd->grd_table_id[fproto], 1);
+         }
+       }
+
+      /*
+       * save the tunnel by VNI and by sw_if_index
+       */
+      hash_set (gv_db, vni, gti);
+
+      vec_validate (gbp_vxlan_tunnel_db, gt->gt_sw_if_index);
+      gbp_vxlan_tunnel_db[gt->gt_sw_if_index] = gti;
+
+      if (sw_if_indexp)
+       *sw_if_indexp = gt->gt_sw_if_index;
+
+      vxlan_gbp_register_udp_ports ();
+    }
+  else
+    {
+      gti = p[0];
+      rv = VNET_API_ERROR_IF_ALREADY_EXISTS;
+    }
+
+  GBP_VXLAN_TUN_DBG ("add: %U", format_gbp_vxlan_tunnel, gti);
+
+  return (rv);
+}
+
+int
+gbp_vxlan_tunnel_del (u32 vni)
+{
+  gbp_vxlan_tunnel_t *gt;
+  uword *p;
+
+  p = hash_get (gv_db, vni);
+
+  if (NULL != p)
+    {
+      vnet_main_t *vnm;
+
+      vnm = vnet_get_main ();
+      gt = gbp_vxlan_tunnel_get (p[0]);
+
+      vxlan_gbp_unregister_udp_ports ();
+
+      GBP_VXLAN_TUN_DBG ("del: %U", format_gbp_vxlan_tunnel,
+                        gt - gbp_vxlan_tunnel_pool);
+
+      gbp_endpoint_flush (gt->gt_sw_if_index);
+      ASSERT (0 == vec_len (gt->gt_tuns));
+      vec_free (gt->gt_tuns);
+
+      if (GBP_VXLAN_TUN_L2 == gt->gt_layer)
+       {
+         gbp_learn_disable (gt->gt_sw_if_index, GBP_LEARN_MODE_L2);
+         gbp_itf_unlock (gt->gt_itf);
+         gbp_bridge_domain_unlock (gt->gt_gbd);
+       }
+      else
+       {
+         fib_protocol_t fproto;
+
+         FOR_EACH_FIB_IP_PROTOCOL (fproto)
+           ip_table_bind (fproto, gt->gt_sw_if_index, 0, 0);
+
+         ip4_sw_interface_enable_disable (gt->gt_sw_if_index, 0);
+         ip6_sw_interface_enable_disable (gt->gt_sw_if_index, 0);
+
+         gbp_learn_disable (gt->gt_sw_if_index, GBP_LEARN_MODE_L3);
+         gbp_route_domain_unlock (gt->gt_grd);
+       }
+
+      vnet_sw_interface_set_flags (vnm, gt->gt_sw_if_index, 0);
+      vnet_delete_hw_interface (vnm, gt->gt_hw_if_index);
+
+      hash_unset (gv_db, vni);
+      gbp_vxlan_tunnel_db[gt->gt_sw_if_index] = INDEX_INVALID;
+
+      pool_put (gbp_vxlan_tunnel_pool, gt);
+    }
+  else
+    return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+  return (0);
+}
+
+static clib_error_t *
+gbp_vxlan_show (vlib_main_t * vm,
+               unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  gbp_vxlan_walk (gbp_vxlan_tunnel_show_one, vm);
+
+  return (NULL);
+}
+
+/*?
+ * Show Group Based Policy VXLAN tunnels
+ *
+ * @cliexpar
+ * @cliexstart{show gbp vxlan}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_vxlan_show_node, static) = {
+  .path = "show gbp vxlan",
+  .short_help = "show gbp vxlan\n",
+  .function = gbp_vxlan_show,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+gbp_vxlan_init (vlib_main_t * vm)
+{
+  u32 slot4;
+
+  /*
+   * insert ourselves into the VXLAN-GBP arc to collect the no-tunnel
+   * packets.
+   */
+  slot4 = vlib_node_add_next_with_slot (vm,
+                                       vxlan4_gbp_input_node.index,
+                                       gbp_vxlan4_input_node.index,
+                                       VXLAN_GBP_INPUT_NEXT_NO_TUNNEL);
+  ASSERT (slot4 == VXLAN_GBP_INPUT_NEXT_NO_TUNNEL);
+
+  /* slot6 = vlib_node_add_next_with_slot (vm, */
+  /*                                    vxlan6_gbp_input_node.index, */
+  /*                                    gbp_vxlan6_input_node.index, */
+  /*                                    VXLAN_GBP_INPUT_NEXT_NO_TUNNEL); */
+  /* ASSERT (slot6 == VXLAN_GBP_INPUT_NEXT_NO_TUNNEL); */
+
+  gt_logger = vlib_log_register_class ("gbp", "tun");
+
+  return (NULL);
+}
+
+VLIB_INIT_FUNCTION (gbp_vxlan_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/gbp/gbp_vxlan.h b/src/plugins/gbp/gbp_vxlan.h
new file mode 100644 (file)
index 0000000..7aa22e3
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * 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 __GBP_VXLAN_H__
+#define __GBP_VXLAN_H__
+
+#include <vnet/fib/fib_types.h>
+
+#define forecah_gbp_vxlan_tunnel_layer          \
+  _(L2, "l2")                                   \
+  _(L3, "l3")
+
+typedef enum gbp_vxlan_tunnel_layer_t_
+{
+#define _(s,n) GBP_VXLAN_TUN_##s,
+  forecah_gbp_vxlan_tunnel_layer
+#undef _
+} gbp_vxlan_tunnel_layer_t;
+
+/**
+ * GBP VXLAN (template) tunnel.
+ * A template tunnel has only a VNI, it does not have src,dst address.
+ * As such it cannot be used to send traffic. It is used in the RX path
+ * to RX vxlan-gbp packets that do not match an existing tunnel;
+ */
+typedef struct gbp_vxlan_tunnel_t_
+{
+  u32 gt_hw_if_index;
+  u32 gt_sw_if_index;
+  u32 gt_vni;
+
+  /**
+   * The BD or RD value (depending on the layer) that the tunnel is bound to
+   */
+  u32 gt_bd_rd_id;
+  gbp_vxlan_tunnel_layer_t gt_layer;
+
+  union
+  {
+    struct
+    {
+      /**
+       * BD index (if L2)
+       */
+      u32 gt_bd_index;
+      /**
+       * Reference to the GPB-BD
+       */
+      index_t gt_gbd;
+    };
+    struct
+    {
+      /**
+       * FIB inidices (if L3)
+       */
+      u32 gt_fib_index[FIB_PROTOCOL_IP_MAX];
+      /**
+       * References to the GBP-RD
+       */
+      index_t gt_grd;
+    };
+  };
+
+  /**
+   * gbp-itf config for this interface
+   */
+  index_t gt_itf;
+
+  /**
+   * list of child vxlan-gbp tunnels built from this template
+   */
+  index_t *gt_tuns;
+} gbp_vxlan_tunnel_t;
+
+/**
+ * The different types of interfaces that endpoints are learned on
+ */
+typedef enum gbp_vxlan_tunnel_type_t_
+{
+  /**
+   * This is the object type deifend above.
+   *  A template representation of a vxlan-gbp tunnel. from this tunnel
+   *  type, real vxlan-gbp tunnels are created (by cloning the VNI)
+   */
+  GBP_VXLAN_TEMPLATE_TUNNEL,
+
+  /**
+   * A real VXLAN-GBP tunnel (from vnet/vxlan-gbp/...)
+   */
+  VXLAN_GBP_TUNNEL,
+} gbp_vxlan_tunnel_type_t;
+
+extern int gbp_vxlan_tunnel_add (u32 vni, gbp_vxlan_tunnel_layer_t layer,
+                                u32 bd_rd_id, u32 * sw_if_indexp);
+extern int gbp_vxlan_tunnel_del (u32 vni);
+
+extern gbp_vxlan_tunnel_type_t gbp_vxlan_tunnel_get_type (u32 sw_if_index);
+
+extern u32 gbp_vxlan_tunnel_clone_and_lock (u32 parent_tunnel,
+                                           const ip46_address_t * src,
+                                           const ip46_address_t * dst);
+
+extern void vxlan_gbp_tunnel_lock (u32 sw_if_index);
+extern void vxlan_gbp_tunnel_unlock (u32 sw_if_index);
+extern u32 vxlan_gbp_tunnel_get_parent (u32 sw_if_index);
+
+typedef walk_rc_t (*gbp_vxlan_cb_t) (gbp_vxlan_tunnel_t * gt, void *ctx);
+extern void gbp_vxlan_walk (gbp_vxlan_cb_t cb, void *ctx);
+
+extern u8 *format_gbp_vxlan_tunnel (u8 * s, va_list * args);
+extern u8 *format_gbp_vxlan_tunnel_layer (u8 * s, va_list * args);
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
index a45f921..6d26b5a 100644 (file)
@@ -679,6 +679,7 @@ list(APPEND VNET_SOURCES
   vxlan-gbp/encap.c
   vxlan-gbp/vxlan_gbp_api.c
   vxlan-gbp/vxlan_gbp.c
+  vxlan-gbp/vxlan_gbp_packet.c
 )
 
 list(APPEND VNET_HEADERS
index 7b4390d..a249cb5 100644 (file)
@@ -37,6 +37,13 @@ mac_address_from_bytes (mac_address_t * mac, const u8 * bytes)
   clib_memcpy (mac->bytes, bytes, 6);
 }
 
+static_always_inline void
+mac_address_to_bytes (const mac_address_t * mac, u8 * bytes)
+{
+  /* zero out the last 2 bytes, then copy over only 6 */
+  clib_memcpy (bytes, mac->bytes, 6);
+}
+
 static_always_inline int
 mac_address_is_zero (const mac_address_t * mac)
 {
@@ -57,6 +64,12 @@ mac_address_from_u64 (u64 u, mac_address_t * mac)
   mac->bytes[5] = 0;
 }
 
+static_always_inline void
+mac_address_copy (mac_address_t * dst, const mac_address_t * src)
+{
+  mac_address_from_bytes (dst, src->bytes);
+}
+
 extern uword unformat_mac_address_t (unformat_input_t * input,
                                     va_list * args);
 extern u8 *format_mac_address_t (u8 * s, va_list * args);
index a3bfdc9..b7d9007 100644 (file)
@@ -271,6 +271,13 @@ vnet_sw_interface_is_api_valid (vnet_main_t * vnm, u32 sw_if_index)
     && vnet_sw_interface_is_api_visible (vnm, sw_if_index);
 }
 
+always_inline const u8 *
+vnet_sw_interface_get_hw_address (vnet_main_t * vnm, u32 sw_if_index)
+{
+  vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index);
+  return hw->hw_address;
+}
+
 always_inline uword
 vnet_hw_interface_get_flags (vnet_main_t * vnm, u32 hw_if_index)
 {
index 11b5276..3d1f806 100644 (file)
@@ -107,6 +107,7 @@ ip_prefix_decode (const vl_api_prefix_t * in, fib_prefix_t * out)
       break;
     }
   out->fp_len = in->address_length;
+  out->___fp___pad = 0;
   ip_address_decode (&in->address, &out->fp_addr);
 }
 
index 8b65bc3..7c71ea6 100644 (file)
@@ -483,7 +483,6 @@ autoreply define bd_ip_mac_add_del
   u32 context;
   u32 bd_id;
   u8 is_add;
-  u8 is_ipv6;
   vl_api_address_t ip;
   vl_api_mac_address_t mac;
 };
index f55e703..57fca57 100644 (file)
@@ -104,12 +104,13 @@ l2input_bd_config (u32 bd_index)
  _(FLOOD,         "l2-flood")                   \
  _(ARP_TERM,      "arp-term-l2bd")              \
  _(UU_FLOOD,      "l2-flood")                   \
- _(UU_FWD,        "l2-uu-fwd")                  \
  _(GBP_FWD,       "gbp-fwd")                    \
+ _(UU_FWD,        "l2-uu-fwd")                  \
  _(FWD,           "l2-fwd")                     \
  _(RW,            "l2-rw")                      \
  _(LEARN,         "l2-learn")                   \
  _(L2_EMULATION,  "l2-emulation")               \
+ _(GBP_LEARN,     "gbp-learn-l2")               \
  _(GBP_NULL_CLASSIFY, "gbp-null-classify")      \
  _(GBP_SRC_CLASSIFY,  "gbp-src-classify")       \
  _(VTR,           "l2-input-vtr")               \
index a6db776..33eeb8e 100644 (file)
@@ -81,7 +81,8 @@ extern vlib_node_registration_t l2output_node;
 #define foreach_l2output_feat \
  _(OUTPUT,            "interface-output")           \
  _(SPAN,              "span-l2-output")             \
- _(GBP_POLICY,        "gbp-policy")                 \
+ _(GBP_POLICY_PORT,   "gbp-policy-port")            \
+ _(GBP_POLICY_MAC,    "gbp-policy-mac")             \
  _(CFM,               "feature-bitmap-drop")        \
  _(QOS,               "feature-bitmap-drop")        \
  _(ACL,               "l2-output-acl")              \
index 1602e94..0d361a3 100644 (file)
@@ -29,6 +29,7 @@ typedef struct
   u32 error;
   u32 vni;
   u16 sclass;
+  u8 flags;
 } vxlan_gbp_rx_trace_t;
 
 static u8 *
@@ -44,8 +45,10 @@ format_vxlan_gbp_rx_trace (u8 * s, va_list * args)
                   t->vni);
   return format (s,
                 "VXLAN_GBP decap from vxlan_gbp_tunnel%d vni %d sclass %d"
-                " next %d error %d",
-                t->tunnel_index, t->vni, t->sclass, t->next_index, t->error);
+                " flags %U next %d error %d",
+                t->tunnel_index, t->vni, t->sclass,
+                format_vxlan_gbp_header_gpflags, t->flags,
+                t->next_index, t->error);
 }
 
 always_inline u32
@@ -161,10 +164,34 @@ vxlan6_gbp_find_tunnel (vxlan_gbp_main_t * vxm, last_tunnel_cache6 * cache,
   return t0;
 }
 
+always_inline vxlan_gbp_input_next_t
+vxlan_gbp_tunnel_get_next (const vxlan_gbp_tunnel_t * t, vlib_buffer_t * b0)
+{
+  if (VXLAN_GBP_TUNNEL_MODE_L2 == t->mode)
+    return (VXLAN_GBP_INPUT_NEXT_L2_INPUT);
+  else
+    {
+      ethernet_header_t *e0;
+      u16 type0;
+
+      e0 = vlib_buffer_get_current (b0);
+      vlib_buffer_advance (b0, sizeof (*e0));
+      type0 = clib_net_to_host_u16 (e0->type);
+      switch (type0)
+       {
+       case ETHERNET_TYPE_IP4:
+         return (VXLAN_GBP_INPUT_NEXT_IP4_INPUT);
+       case ETHERNET_TYPE_IP6:
+         return (VXLAN_GBP_INPUT_NEXT_IP6_INPUT);
+       }
+    }
+  return (VXLAN_GBP_INPUT_NEXT_DROP);
+}
+
 always_inline uword
 vxlan_gbp_input (vlib_main_t * vm,
                 vlib_node_runtime_t * node,
-                vlib_frame_t * from_frame, u32 is_ip4)
+                vlib_frame_t * from_frame, u8 is_ip4)
 {
   vxlan_gbp_main_t *vxm = &vxlan_gbp_main;
   vnet_main_t *vnm = vxm->vnet_main;
@@ -239,10 +266,6 @@ vxlan_gbp_input (vlib_main_t * vm,
              ip6_1 = cur1 - sizeof (udp_header_t) - sizeof (ip6_header_t);
            }
 
-         /* pop vxlan_gbp */
-         vlib_buffer_advance (b0, sizeof *vxlan_gbp0);
-         vlib_buffer_advance (b1, sizeof *vxlan_gbp1);
-
          u32 fi0 = buf_fib_index (b0, is_ip4);
          u32 fi1 = buf_fib_index (b1, is_ip4);
 
@@ -270,16 +293,19 @@ vxlan_gbp_input (vlib_main_t * vm,
          u32 len0 = vlib_buffer_length_in_chain (vm, b0);
          u32 len1 = vlib_buffer_length_in_chain (vm, b1);
 
-         u32 next0, next1;
+         vxlan_gbp_input_next_t next0, next1;
          u8 error0 = 0, error1 = 0;
          u8 flags0 = vxlan_gbp_get_flags (vxlan_gbp0);
          u8 flags1 = vxlan_gbp_get_flags (vxlan_gbp1);
+         /* Required to make the l2 tag push / pop code work on l2 subifs */
+         /* pop vxlan_gbp */
+         vlib_buffer_advance (b0, sizeof *vxlan_gbp0);
+         vlib_buffer_advance (b1, sizeof *vxlan_gbp1);
+
          /* Validate VXLAN_GBP tunnel encap-fib index against packet */
          if (PREDICT_FALSE
              (t0 == 0 || flags0 != (VXLAN_GBP_FLAGS_I | VXLAN_GBP_FLAGS_G)))
            {
-             next0 = VXLAN_GBP_INPUT_NEXT_DROP;
-
              if (t0 != 0
                  && flags0 != (VXLAN_GBP_FLAGS_I | VXLAN_GBP_FLAGS_G))
                {
@@ -287,22 +313,18 @@ vxlan_gbp_input (vlib_main_t * vm,
                  vlib_increment_combined_counter
                    (drop_counter, thread_index, stats_t0->sw_if_index, 1,
                     len0);
+                 next0 = VXLAN_GBP_INPUT_NEXT_DROP;
                }
              else
-               error0 = VXLAN_GBP_ERROR_NO_SUCH_TUNNEL;
+               {
+                 error0 = VXLAN_GBP_ERROR_NO_SUCH_TUNNEL;
+                 next0 = VXLAN_GBP_INPUT_NEXT_NO_TUNNEL;
+               }
              b0->error = node->errors[error0];
            }
          else
            {
-             next0 = t0->decap_next_index;
-             vnet_buffer2 (b0)->gbp.flags =
-               vxlan_gbp_get_gpflags (vxlan_gbp0);
-             vnet_buffer2 (b0)->gbp.src_epg =
-               vxlan_gbp_get_sclass (vxlan_gbp0);
-
-             /* Required to make the l2 tag push / pop code work on l2 subifs */
-             if (PREDICT_TRUE (next0 == VXLAN_GBP_INPUT_NEXT_L2_INPUT))
-               vnet_update_l2_len (b0);
+             next0 = vxlan_gbp_tunnel_get_next (t0, b0);
 
              /* Set packet input sw_if_index to unicast VXLAN tunnel for learning */
              vnet_buffer (b0)->sw_if_index[VLIB_RX] = t0->sw_if_index;
@@ -311,12 +333,13 @@ vxlan_gbp_input (vlib_main_t * vm,
              pkts_decapsulated++;
            }
 
-         /* Validate VXLAN_GBP tunnel encap-fib index against packet */
+         vnet_buffer2 (b0)->gbp.flags = vxlan_gbp_get_gpflags (vxlan_gbp0);
+         vnet_buffer2 (b0)->gbp.src_epg = vxlan_gbp_get_sclass (vxlan_gbp0);
+
+
          if (PREDICT_FALSE
              (t1 == 0 || flags1 != (VXLAN_GBP_FLAGS_I | VXLAN_GBP_FLAGS_G)))
            {
-             next1 = VXLAN_GBP_INPUT_NEXT_DROP;
-
              if (t1 != 0
                  && flags1 != (VXLAN_GBP_FLAGS_I | VXLAN_GBP_FLAGS_G))
                {
@@ -324,22 +347,18 @@ vxlan_gbp_input (vlib_main_t * vm,
                  vlib_increment_combined_counter
                    (drop_counter, thread_index, stats_t1->sw_if_index, 1,
                     len1);
+                 next1 = VXLAN_GBP_INPUT_NEXT_DROP;
                }
              else
-               error1 = VXLAN_GBP_ERROR_NO_SUCH_TUNNEL;
+               {
+                 error1 = VXLAN_GBP_ERROR_NO_SUCH_TUNNEL;
+                 next1 = VXLAN_GBP_INPUT_NEXT_NO_TUNNEL;
+               }
              b1->error = node->errors[error1];
            }
          else
            {
-             next1 = t1->decap_next_index;
-             vnet_buffer2 (b1)->gbp.flags =
-               vxlan_gbp_get_gpflags (vxlan_gbp1);
-             vnet_buffer2 (b1)->gbp.src_epg =
-               vxlan_gbp_get_sclass (vxlan_gbp1);
-
-             /* Required to make the l2 tag push / pop code work on l2 subifs */
-             if (PREDICT_TRUE (next1 == VXLAN_GBP_INPUT_NEXT_L2_INPUT))
-               vnet_update_l2_len (b1);
+             next1 = vxlan_gbp_tunnel_get_next (t1, b1);
 
              /* Set packet input sw_if_index to unicast VXLAN_GBP tunnel for learning */
              vnet_buffer (b1)->sw_if_index[VLIB_RX] = t1->sw_if_index;
@@ -349,6 +368,12 @@ vxlan_gbp_input (vlib_main_t * vm,
                (rx_counter, thread_index, stats_t1->sw_if_index, 1, len1);
            }
 
+         vnet_buffer2 (b1)->gbp.flags = vxlan_gbp_get_gpflags (vxlan_gbp1);
+         vnet_buffer2 (b1)->gbp.src_epg = vxlan_gbp_get_sclass (vxlan_gbp1);
+
+         vnet_update_l2_len (b0);
+         vnet_update_l2_len (b1);
+
          if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
            {
              vxlan_gbp_rx_trace_t *tr =
@@ -358,6 +383,7 @@ vxlan_gbp_input (vlib_main_t * vm,
              tr->tunnel_index = t0 == 0 ? ~0 : t0 - vxm->tunnels;
              tr->vni = vxlan_gbp_get_vni (vxlan_gbp0);
              tr->sclass = vxlan_gbp_get_sclass (vxlan_gbp0);
+             tr->flags = vxlan_gbp_get_gpflags (vxlan_gbp0);
            }
          if (PREDICT_FALSE (b1->flags & VLIB_BUFFER_IS_TRACED))
            {
@@ -368,6 +394,7 @@ vxlan_gbp_input (vlib_main_t * vm,
              tr->tunnel_index = t1 == 0 ? ~0 : t1 - vxm->tunnels;
              tr->vni = vxlan_gbp_get_vni (vxlan_gbp1);
              tr->sclass = vxlan_gbp_get_sclass (vxlan_gbp1);
+             tr->flags = vxlan_gbp_get_gpflags (vxlan_gbp0);
            }
 
          vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
@@ -395,9 +422,6 @@ vxlan_gbp_input (vlib_main_t * vm,
          else
            ip6_0 = cur0 - sizeof (udp_header_t) - sizeof (ip6_header_t);
 
-         /* pop (ip, udp, vxlan_gbp) */
-         vlib_buffer_advance (b0, sizeof (*vxlan_gbp0));
-
          u32 fi0 = buf_fib_index (b0, is_ip4);
 
          vxlan_gbp_tunnel_t *t0, *stats_t0 = 0;
@@ -412,15 +436,16 @@ vxlan_gbp_input (vlib_main_t * vm,
 
          uword len0 = vlib_buffer_length_in_chain (vm, b0);
 
-         u32 next0;
+         vxlan_gbp_input_next_t next0;
          u8 error0 = 0;
          u8 flags0 = vxlan_gbp_get_flags (vxlan_gbp0);
+
+         /* pop (ip, udp, vxlan_gbp) */
+         vlib_buffer_advance (b0, sizeof (*vxlan_gbp0));
          /* Validate VXLAN_GBP tunnel encap-fib index against packet */
          if (PREDICT_FALSE
              (t0 == 0 || flags0 != (VXLAN_GBP_FLAGS_I | VXLAN_GBP_FLAGS_G)))
            {
-             next0 = VXLAN_GBP_INPUT_NEXT_DROP;
-
              if (t0 != 0
                  && flags0 != (VXLAN_GBP_FLAGS_I | VXLAN_GBP_FLAGS_G))
                {
@@ -428,24 +453,18 @@ vxlan_gbp_input (vlib_main_t * vm,
                  vlib_increment_combined_counter
                    (drop_counter, thread_index, stats_t0->sw_if_index, 1,
                     len0);
+                 next0 = VXLAN_GBP_INPUT_NEXT_DROP;
                }
              else
-               error0 = VXLAN_GBP_ERROR_NO_SUCH_TUNNEL;
+               {
+                 error0 = VXLAN_GBP_ERROR_NO_SUCH_TUNNEL;
+                 next0 = VXLAN_GBP_INPUT_NEXT_NO_TUNNEL;
+               }
              b0->error = node->errors[error0];
            }
          else
            {
-             next0 = t0->decap_next_index;
-             vnet_buffer2 (b0)->gbp.flags =
-               vxlan_gbp_get_gpflags (vxlan_gbp0);
-             vnet_buffer2 (b0)->gbp.src_epg =
-               vxlan_gbp_get_sclass (vxlan_gbp0);
-
-
-             /* Required to make the l2 tag push / pop code work on l2 subifs */
-             if (PREDICT_TRUE (next0 == VXLAN_GBP_INPUT_NEXT_L2_INPUT))
-               vnet_update_l2_len (b0);
-
+             next0 = vxlan_gbp_tunnel_get_next (t0, b0);
              /* Set packet input sw_if_index to unicast VXLAN_GBP tunnel for learning */
              vnet_buffer (b0)->sw_if_index[VLIB_RX] = t0->sw_if_index;
              pkts_decapsulated++;
@@ -453,6 +472,11 @@ vxlan_gbp_input (vlib_main_t * vm,
              vlib_increment_combined_counter
                (rx_counter, thread_index, stats_t0->sw_if_index, 1, len0);
            }
+         vnet_buffer2 (b0)->gbp.flags = vxlan_gbp_get_gpflags (vxlan_gbp0);
+         vnet_buffer2 (b0)->gbp.src_epg = vxlan_gbp_get_sclass (vxlan_gbp0);
+
+         /* Required to make the l2 tag push / pop code work on l2 subifs */
+         vnet_update_l2_len (b0);
 
          if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
            {
@@ -463,6 +487,7 @@ vxlan_gbp_input (vlib_main_t * vm,
              tr->tunnel_index = t0 == 0 ? ~0 : t0 - vxm->tunnels;
              tr->vni = vxlan_gbp_get_vni (vxlan_gbp0);
              tr->sclass = vxlan_gbp_get_sclass (vxlan_gbp0);
+             tr->flags = vxlan_gbp_get_gpflags (vxlan_gbp0);
            }
          vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
                                           to_next, n_left_to_next,
index 6e41ec8..3e213dd 100644 (file)
@@ -23,7 +23,6 @@ import "vnet/ip/ip_types.api";
     @param dst - Destination IP address, can be multicast
     @param mcast_sw_if_index - Interface for multicast destination
     @param encap_table_id - Encap route table 
-    @param decap_next_index - Name of decap next graph node
     @param vni - The VXLAN Network Identifier, uint24
     @param sw_ifindex - Ignored in add message, set in details
 */
@@ -34,7 +33,6 @@ typedef vxlan_gbp_tunnel
   vl_api_address_t dst;
   u32 mcast_sw_if_index;
   u32 encap_table_id;
-  u32 decap_next_index;
   u32 vni;
   u32 sw_if_index;
 };
index ec4f923..691cc76 100644 (file)
 
 vxlan_gbp_main_t vxlan_gbp_main;
 
-static u8 *
-format_decap_next (u8 * s, va_list * args)
+u8 *
+format_vxlan_gbp_tunnel_mode (u8 * s, va_list * args)
 {
-  u32 next_index = va_arg (*args, u32);
+  vxlan_gbp_tunnel_mode_t mode = va_arg (*args, vxlan_gbp_tunnel_mode_t);
 
-  if (next_index == VXLAN_GBP_INPUT_NEXT_DROP)
-    return format (s, "drop");
-  else
-    return format (s, "index %d", next_index);
-  return s;
+  switch (mode)
+    {
+    case VXLAN_GBP_TUNNEL_MODE_L2:
+      s = format (s, "L2");
+      break;
+    case VXLAN_GBP_TUNNEL_MODE_L3:
+      s = format (s, "L3");
+      break;
+    }
+  return (s);
 }
 
 u8 *
@@ -51,17 +56,15 @@ format_vxlan_gbp_tunnel (u8 * s, va_list * args)
 
   s = format (s,
              "[%d] instance %d src %U dst %U vni %d fib-idx %d"
-             " sw-if-idx %d ",
+             " sw-if-idx %d mode %U ",
              t->dev_instance, t->user_instance,
              format_ip46_address, &t->src, IP46_TYPE_ANY,
              format_ip46_address, &t->dst, IP46_TYPE_ANY,
-             t->vni, t->encap_fib_index, t->sw_if_index);
+             t->vni, t->encap_fib_index, t->sw_if_index,
+             format_vxlan_gbp_tunnel_mode, t->mode);
 
   s = format (s, "encap-dpo-idx %d ", t->next_dpo.dpoi_index);
 
-  if (PREDICT_FALSE (t->decap_next_index != VXLAN_GBP_INPUT_NEXT_L2_INPUT))
-    s = format (s, "decap-next-%U ", format_decap_next, t->decap_next_index);
-
   if (PREDICT_FALSE (ip46_address_is_multicast (&t->dst)))
     s = format (s, "mcast-sw-if-idx %d ", t->mcast_sw_if_index);
 
@@ -210,9 +213,9 @@ const static fib_node_vft_t vxlan_gbp_vft = {
 
 #define foreach_copy_field                      \
 _(vni)                                          \
+_(mode)                                         \
 _(mcast_sw_if_index)                            \
 _(encap_fib_index)                              \
-_(decap_next_index)                             \
 _(src)                                          \
 _(dst)
 
@@ -267,18 +270,6 @@ vxlan_gbp_rewrite (vxlan_gbp_tunnel_t * t, bool is_ip6)
   vnet_rewrite_set_data (*t, &h, len);
 }
 
-static bool
-vxlan_gbp_decap_next_is_valid (vxlan_gbp_main_t * vxm, u32 is_ip6,
-                              u32 decap_next_index)
-{
-  vlib_main_t *vm = vxm->vlib_main;
-  u32 input_idx = (!is_ip6) ?
-    vxlan4_gbp_input_node.index : vxlan6_gbp_input_node.index;
-  vlib_node_runtime_t *r = vlib_node_get_runtime (vm, input_idx);
-
-  return decap_next_index < r->n_next_nodes;
-}
-
 static uword
 vtep_addr_ref (ip46_address_t * ip)
 {
@@ -434,14 +425,11 @@ int vnet_vxlan_gbp_tunnel_add_del
 
       /* adding a tunnel: tunnel must not already exist */
       if (p)
-       return VNET_API_ERROR_TUNNEL_EXIST;
-
-      /* if not set explicitly, default to l2 */
-      if (a->decap_next_index == ~0)
-       a->decap_next_index = VXLAN_GBP_INPUT_NEXT_L2_INPUT;
-      if (!vxlan_gbp_decap_next_is_valid (vxm, is_ip6, a->decap_next_index))
-       return VNET_API_ERROR_INVALID_DECAP_NEXT;
-
+       {
+         t = pool_elt_at_index (vxm->tunnels, *p);
+         *sw_if_indexp = t->sw_if_index;
+         return VNET_API_ERROR_TUNNEL_EXIST;
+       }
       pool_get_aligned (vxm->tunnels, t, CLIB_CACHE_LINE_BYTES);
       clib_memset (t, 0, sizeof (*t));
       dev_instance = t - vxm->tunnels;
@@ -505,6 +493,12 @@ int vnet_vxlan_gbp_tunnel_add_del
 
       t->sw_if_index = sw_if_index = hi->sw_if_index;
 
+      if (VXLAN_GBP_TUNNEL_MODE_L3 == t->mode)
+       {
+         ip4_sw_interface_enable_disable (t->sw_if_index, 1);
+         ip6_sw_interface_enable_disable (t->sw_if_index, 1);
+       }
+
       vec_validate_init_empty (vxm->tunnel_index_by_sw_if_index, sw_if_index,
                               ~0);
       vxm->tunnel_index_by_sw_if_index[sw_if_index] = dev_instance;
@@ -626,6 +620,12 @@ int vnet_vxlan_gbp_tunnel_add_del
       sw_if_index = t->sw_if_index;
       vnet_sw_interface_set_flags (vnm, sw_if_index, 0 /* down */ );
 
+      if (VXLAN_GBP_TUNNEL_MODE_L3 == t->mode)
+       {
+         ip4_sw_interface_enable_disable (t->sw_if_index, 0);
+         ip6_sw_interface_enable_disable (t->sw_if_index, 0);
+       }
+
       vxm->tunnel_index_by_sw_if_index[sw_if_index] = ~0;
 
       if (!is_ip6)
@@ -660,6 +660,36 @@ int vnet_vxlan_gbp_tunnel_add_del
   return 0;
 }
 
+int
+vnet_vxlan_gbp_tunnel_del (u32 sw_if_index)
+{
+  vxlan_gbp_main_t *vxm = &vxlan_gbp_main;
+  vxlan_gbp_tunnel_t *t = 0;
+  u32 ti;
+
+  if (sw_if_index >= vec_len (vxm->tunnel_index_by_sw_if_index))
+    return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+  ti = vxm->tunnel_index_by_sw_if_index[sw_if_index];
+  if (~0 != ti)
+    {
+      t = pool_elt_at_index (vxm->tunnels, ti);
+
+      vnet_vxlan_gbp_tunnel_add_del_args_t args = {
+       .is_add = 0,
+       .is_ip6 = !ip46_address_is_ip4 (&t->src),
+       .vni = t->vni,
+       .src = t->src,
+       .dst = t->dst,
+       .instance = ~0,
+      };
+
+      return (vnet_vxlan_gbp_tunnel_add_del (&args, NULL));
+    }
+
+  return VNET_API_ERROR_NO_SUCH_ENTRY;
+}
+
 static uword
 get_decap_next_for_node (u32 node_index, u32 ipv4_set)
 {
@@ -700,6 +730,7 @@ vxlan_gbp_tunnel_add_del_command_fn (vlib_main_t * vm,
   unformat_input_t _line_input, *line_input = &_line_input;
   ip46_address_t src = ip46_address_initializer, dst =
     ip46_address_initializer;
+  vxlan_gbp_tunnel_mode_t mode = VXLAN_GBP_TUNNEL_MODE_L2;
   u8 is_add = 1;
   u8 src_set = 0;
   u8 dst_set = 0;
index f9edcdc..66f0cff 100644 (file)
@@ -59,6 +59,14 @@ typedef clib_bihash_kv_16_8_t vxlan4_gbp_tunnel_key_t;
 */
 typedef clib_bihash_kv_24_8_t vxlan6_gbp_tunnel_key_t;
 
+typedef enum vxlan_gbp_tunnel_mode_t_
+{
+  VXLAN_GBP_TUNNEL_MODE_L2,
+  VXLAN_GBP_TUNNEL_MODE_L3,
+} vxlan_gbp_tunnel_mode_t;
+
+extern u8 *format_vxlan_gbp_tunnel_mode (u8 * s, va_list * args);
+
 typedef struct
 {
   /* Required for pool_get_aligned */
@@ -67,9 +75,6 @@ typedef struct
   /* FIB DPO for IP forwarding of VXLAN encap packet */
   dpo_id_t next_dpo;
 
-  /*  Group Policy ID */
-  u16 sclass;
-
   /* flags */
   u16 flags;
 
@@ -83,9 +88,6 @@ typedef struct
   /* mcast packet output intfc index (used only if dst is mcast) */
   u32 mcast_sw_if_index;
 
-  /* decap next index */
-  u32 decap_next_index;
-
   /* The FIB index for src/dst addresses */
   u32 encap_fib_index;
 
@@ -96,6 +98,12 @@ typedef struct
   /** Next node after VxLAN-GBP encap */
   uword encap_next_node;
 
+  /**
+   * Tunnel mode.
+   * L2 tunnels decap to L2 path, L3 tunnels to the L3 path
+   */
+  vxlan_gbp_tunnel_mode_t mode;
+
   /**
    * Linkage into the FIB object graph
    */
@@ -122,9 +130,12 @@ typedef struct
     vnet_declare_rewrite (VLIB_BUFFER_PRE_DATA_SIZE);
 } vxlan_gbp_tunnel_t;
 
-#define foreach_vxlan_gbp_input_next        \
-_(DROP, "error-drop")                   \
-_(L2_INPUT, "l2-input")
+#define foreach_vxlan_gbp_input_next         \
+  _(DROP, "error-drop")                      \
+  _(NO_TUNNEL, "error-punt")                 \
+  _(L2_INPUT, "l2-input")                    \
+  _(IP4_INPUT, "ip4-input")                  \
+  _(IP6_INPUT, "ip6-input")
 
 typedef enum
 {
@@ -142,6 +153,13 @@ typedef enum
   VXLAN_GBP_N_ERROR,
 } vxlan_gbp_input_error_t;
 
+/**
+ * Call back function packets that do not match a configured tunnel
+ */
+typedef vxlan_gbp_input_next_t (*vxlan_bgp_no_tunnel_t) (vlib_buffer_t * b,
+                                                        u32 thread_index,
+                                                        u8 is_ip6);
+
 typedef struct
 {
   /* vector of encap tunnel instances */
@@ -189,20 +207,22 @@ typedef struct
   u8 is_add;
   u8 is_ip6;
   u32 instance;
+  vxlan_gbp_tunnel_mode_t mode;
   ip46_address_t src, dst;
   u32 mcast_sw_if_index;
   u32 encap_fib_index;
-  u32 decap_next_index;
   u32 vni;
 } vnet_vxlan_gbp_tunnel_add_del_args_t;
 
 int vnet_vxlan_gbp_tunnel_add_del
   (vnet_vxlan_gbp_tunnel_add_del_args_t * a, u32 * sw_if_indexp);
+int vnet_vxlan_gbp_tunnel_del (u32 sw_if_indexp);
 
 void vnet_int_vxlan_gbp_bypass_mode (u32 sw_if_index, u8 is_ip6,
                                     u8 is_enable);
 
 u32 vnet_vxlan_gbp_get_tunnel_index (u32 sw_if_index);
+
 #endif /* included_vnet_vxlan_gbp_h */
 
 /*
index b7e6935..f5e97e5 100644 (file)
@@ -92,10 +92,10 @@ static void vl_api_vxlan_gbp_tunnel_add_del_t_handler
     .instance = ntohl (mp->tunnel.instance),
     .mcast_sw_if_index = ntohl (mp->tunnel.mcast_sw_if_index),
     .encap_fib_index = fib_index,
-    .decap_next_index = ntohl (mp->tunnel.decap_next_index),
     .vni = ntohl (mp->tunnel.vni),
     .dst = dst,
     .src = src,
+    .mode = VXLAN_GBP_TUNNEL_MODE_L2,
   };
 
   /* Check src & dst are different */
@@ -142,7 +142,6 @@ static void send_vxlan_gbp_tunnel_details
   rmp->tunnel.instance = htonl (t->user_instance);
   rmp->tunnel.mcast_sw_if_index = htonl (t->mcast_sw_if_index);
   rmp->tunnel.vni = htonl (t->vni);
-  rmp->tunnel.decap_next_index = htonl (t->decap_next_index);
   rmp->tunnel.sw_if_index = htonl (t->sw_if_index);
   rmp->context = context;
 
diff --git a/src/vnet/vxlan-gbp/vxlan_gbp_packet.c b/src/vnet/vxlan-gbp/vxlan_gbp_packet.c
new file mode 100644 (file)
index 0000000..01c7a19
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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 <vnet/vxlan-gbp/vxlan_gbp_packet.h>
+
+u8 *
+format_vxlan_gbp_header_flags (u8 * s, va_list * args)
+{
+  vxlan_gbp_flags_t flags = va_arg (*args, int);
+
+  if (VXLAN_GBP_FLAGS_NONE == flags)
+    {
+      s = format (s, "None");
+    }
+#define _(n,f) {                          \
+    if (VXLAN_GBP_FLAGS_##f & flags)      \
+      s = format (s, #f);                 \
+  }
+  foreach_vxlan_gbp_flags
+#undef _
+    return (s);
+}
+
+u8 *
+format_vxlan_gbp_header_gpflags (u8 * s, va_list * args)
+{
+  vxlan_gbp_gpflags_t flags = va_arg (*args, int);
+
+  if (VXLAN_GBP_GPFLAGS_NONE == flags)
+    {
+      s = format (s, "None");
+    }
+#define _(n,f) {                          \
+    if (VXLAN_GBP_GPFLAGS_##f & flags)    \
+      s = format (s, #f);                 \
+  }
+  foreach_vxlan_gbp_gpflags
+#undef _
+    return (s);
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
index e1674a0..33bccd6 100644 (file)
@@ -15,6 +15,8 @@
 #ifndef __included_vxlan_gbp_packet_h__
 #define __included_vxlan_gbp_packet_h__ 1
 
+#include <vlib/vlib.h>
+
 /*
  * From draft-smith-vxlan-group-policy-04.txt
  *
@@ -85,8 +87,17 @@ typedef struct
   u32 vni_reserved;
 } vxlan_gbp_header_t;
 
-#define VXLAN_GBP_FLAGS_G 0x80
-#define VXLAN_GBP_FLAGS_I 0x08
+#define foreach_vxlan_gbp_flags    \
+  _ (0x80, G)                      \
+  _ (0x08, I)
+
+typedef enum
+{
+  VXLAN_GBP_FLAGS_NONE = 0,
+#define _(n,f) VXLAN_GBP_FLAGS_##f = n,
+  foreach_vxlan_gbp_flags
+#undef _
+} __attribute__ ((packed)) vxlan_gbp_flags_t;
 
 #define foreach_vxlan_gbp_gpflags \
 _ (0x40, D)                       \
@@ -96,10 +107,11 @@ _ (0x08, A)
 
 typedef enum
 {
+  VXLAN_GBP_GPFLAGS_NONE = 0,
 #define _(n,f) VXLAN_GBP_GPFLAGS_##f = n,
   foreach_vxlan_gbp_gpflags
 #undef _
-} vxlan_gbp_gpflag_t;
+} __attribute__ ((packed)) vxlan_gbp_gpflags_t;
 
 static inline u32
 vxlan_gbp_get_vni (vxlan_gbp_header_t * h)
@@ -119,13 +131,13 @@ vxlan_gbp_get_sclass (vxlan_gbp_header_t * h)
   return sclass_host_byte_order;
 }
 
-static inline u8
+static inline vxlan_gbp_gpflags_t
 vxlan_gbp_get_gpflags (vxlan_gbp_header_t * h)
 {
   return h->gpflags;
 }
 
-static inline u8
+static inline vxlan_gbp_flags_t
 vxlan_gbp_get_flags (vxlan_gbp_header_t * h)
 {
   return h->flag_g_i;
@@ -139,6 +151,9 @@ vxlan_gbp_set_header (vxlan_gbp_header_t * h, u32 vni)
   h->flag_g_i = VXLAN_GBP_FLAGS_I | VXLAN_GBP_FLAGS_G;
 }
 
+extern u8 *format_vxlan_gbp_header_flags (u8 * s, va_list * args);
+extern u8 *format_vxlan_gbp_header_gpflags (u8 * s, va_list * args);
+
 #endif /* __included_vxlan_gbp_packet_h__ */
 
 /*
index fbe6c37..5bbd56a 100644 (file)
@@ -929,7 +929,35 @@ class VppTestCase(unittest.TestCase):
         input.add_stream(pkts)
         self.pg_enable_capture(self.pg_interfaces)
         self.pg_start()
-        rx = output.get_capture(len(pkts))
+        if isinstance(object, (list,)):
+            rx = []
+            for o in output:
+                rx.append(output.get_capture(len(pkts)))
+        else:
+            rx = output.get_capture(len(pkts))
+        return rx
+
+    def send_and_expect_only(self, input, pkts, output, timeout=None):
+        self.vapi.cli("clear trace")
+        input.add_stream(pkts)
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+        if isinstance(object, (list,)):
+            outputs = output
+            rx = []
+            for o in outputs:
+                rx.append(output.get_capture(len(pkts)))
+        else:
+            rx = output.get_capture(len(pkts))
+            outputs = [output]
+        if not timeout:
+            timeout = 1
+        for i in self.pg_interfaces:
+            if i not in outputs:
+                i.get_capture(0, timeout=timeout)
+                i.assert_nothing_captured()
+                timeout = 0.1
+
         return rx
 
 
index ef4bf70..a45b2f8 100644 (file)
@@ -5,11 +5,16 @@ import unittest
 from framework import VppTestCase, VppTestRunner
 from vpp_object import VppObject
 from vpp_neighbor import VppNeighbor
-from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable
+from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable, \
+    VppIpInterfaceAddress, VppIpInterfaceBind, find_route
+from vpp_l2 import VppBridgeDomain, VppBridgeDomainPort, \
+    VppBridgeDomainArpEntry, VppL2FibEntry, find_bridge_domain_port
+from vpp_vxlan_gbp_tunnel import *
 
 from vpp_ip import *
 from vpp_mac import *
 from vpp_papi_provider import L2_PORT_TYPE
+from vpp_papi import VppEnum
 
 from scapy.packet import Raw
 from scapy.layers.l2 import Ether, ARP
@@ -17,25 +22,43 @@ from scapy.layers.inet import IP, UDP
 from scapy.layers.inet6 import IPv6, ICMPv6ND_NS,  ICMPv6NDOptSrcLLAddr, \
     ICMPv6ND_NA
 from scapy.utils6 import in6_getnsma, in6_getnsmac
+from scapy.layers.vxlan import VXLAN
 
 from socket import AF_INET, AF_INET6
 from scapy.utils import inet_pton, inet_ntop
 from util import mactobinary
 
 
-def find_gbp_endpoint(test, sw_if_index, ip=None, mac=None):
-    vip = VppIpAddress(ip)
+def find_gbp_endpoint(test, sw_if_index=None, ip=None, mac=None):
+    if ip:
+        vip = VppIpAddress(ip)
+    if mac:
+        vmac = VppMacAddress(mac)
 
     eps = test.vapi.gbp_endpoint_dump()
+
     for ep in eps:
-        if ep.endpoint.sw_if_index != sw_if_index:
-            continue
-        for eip in ep.endpoint.ips:
-            if vip == eip:
+        if sw_if_index:
+            if ep.endpoint.sw_if_index != sw_if_index:
+                continue
+        if ip:
+            for eip in ep.endpoint.ips:
+                if vip == eip:
+                    return True
+        if mac:
+            if vmac == ep.endpoint.mac:
                 return True
     return False
 
 
+def find_gbp_vxlan(test, vni):
+    ts = test.vapi.gbp_vxlan_tunnel_dump()
+    for t in ts:
+        if t.tunnel.vni == vni:
+            return True
+    return False
+
+
 class VppGbpEndpoint(VppObject):
     """
     GBP Endpoint
@@ -43,7 +66,11 @@ class VppGbpEndpoint(VppObject):
 
     @property
     def bin_mac(self):
-        return mactobinary(self.itf.remote_mac)
+        return self.vmac.bytes
+
+    @property
+    def mac(self):
+        return self.vmac.address
 
     @property
     def mac(self):
@@ -73,7 +100,11 @@ class VppGbpEndpoint(VppObject):
     def fips(self):
         return [self.fip4, self.fip6]
 
-    def __init__(self, test, itf, epg, recirc, ip4, fip4, ip6, fip6):
+    def __init__(self, test, itf, epg, recirc, ip4, fip4, ip6, fip6,
+                 flags=0,
+                 tun_src="0.0.0.0",
+                 tun_dst="0.0.0.0",
+                 mac=True):
         self._test = test
         self.itf = itf
         self.epg = epg
@@ -84,14 +115,24 @@ class VppGbpEndpoint(VppObject):
         self._ip6 = VppIpAddress(ip6)
         self._fip6 = VppIpAddress(fip6)
 
-        self.vmac = VppMacAddress(self.itf.remote_mac)
+        if mac:
+            self.vmac = VppMacAddress(self.itf.remote_mac)
+        else:
+            self.vmac = VppMacAddress("00:00:00:00:00:00")
+
+        self.flags = flags
+        self.tun_src = VppIpAddress(tun_src)
+        self.tun_dst = VppIpAddress(tun_dst)
 
     def add_vpp_config(self):
         res = self._test.vapi.gbp_endpoint_add(
             self.itf.sw_if_index,
             [self.ip4.encode(), self.ip6.encode()],
             self.vmac.encode(),
-            self.epg.epg)
+            self.epg.epg,
+            self.flags,
+            self.tun_src.encode(),
+            self.tun_dst.encode())
         self.handle = res.handle
         self._test.registry.register(self, self._test.logger)
 
@@ -102,9 +143,10 @@ class VppGbpEndpoint(VppObject):
         return self.object_id()
 
     def object_id(self):
-        return "gbp-endpoint;[%d:%s:%d]" % (self.itf.sw_if_index,
-                                            self.ip4.address,
-                                            self.epg.epg)
+        return "gbp-endpoint:[%d==%d:%s:%d]" % (self.handle,
+                                                self.itf.sw_if_index,
+                                                self.ip4.address,
+                                                self.epg.epg)
 
     def query_vpp_config(self):
         return find_gbp_endpoint(self._test,
@@ -142,7 +184,7 @@ class VppGbpRecirc(VppObject):
         return self.object_id()
 
     def object_id(self):
-        return "gbp-recirc;[%d]" % (self.recirc.sw_if_index)
+        return "gbp-recirc:[%d]" % (self.recirc.sw_if_index)
 
     def query_vpp_config(self):
         rs = self._test.vapi.gbp_recirc_dump()
@@ -156,23 +198,21 @@ class VppGbpSubnet(VppObject):
     """
     GBP Subnet
     """
-
-    def __init__(self, test, table_id, address, address_len,
-                 is_internal=True,
-                 sw_if_index=None, epg=None):
+    def __init__(self, test, rd, address, address_len,
+                 type, sw_if_index=None, epg=None):
         self._test = test
-        self.table_id = table_id
+        self.rd_id = rd.rd_id
         self.prefix = VppIpPrefix(address, address_len)
-        self.is_internal = is_internal
+        self.type = type
         self.sw_if_index = sw_if_index
         self.epg = epg
 
     def add_vpp_config(self):
         self._test.vapi.gbp_subnet_add_del(
             1,
-            self.table_id,
-            self.is_internal,
+            self.rd_id,
             self.prefix.encode(),
+            self.type,
             sw_if_index=self.sw_if_index if self.sw_if_index else 0xffffffff,
             epg_id=self.epg if self.epg else 0xffff)
         self._test.registry.register(self, self._test.logger)
@@ -180,21 +220,21 @@ class VppGbpSubnet(VppObject):
     def remove_vpp_config(self):
         self._test.vapi.gbp_subnet_add_del(
             0,
-            self.table_id,
-            self.is_internal,
-            self.prefix.encode())
+            self.rd_id,
+            self.prefix.encode(),
+            self.type)
 
     def __str__(self):
         return self.object_id()
 
     def object_id(self):
-        return "gbp-subnet;[%d-%s]" % (self.table_id,
-                                       self.prefix)
+        return "gbp-subnet:[%d-%s]" % (self.rd_id, self.prefix)
 
     def query_vpp_config(self):
         ss = self._test.vapi.gbp_subnet_dump()
         for s in ss:
-            if s.subnet.table_id == self.table_id and \
+            if s.subnet.rd_id == self.rd_id and \
+               s.subnet.type == self.type and \
                s.subnet.prefix == self.prefix:
                 return True
         return False
@@ -217,29 +257,22 @@ class VppGbpEndpointGroup(VppObject):
         self.rd = rd
 
     def add_vpp_config(self):
-        self._test.vapi.gbp_endpoint_group_add_del(
-            1,
+        self._test.vapi.gbp_endpoint_group_add(
             self.epg,
-            self.bd,
-            self.rd,
-            self.rd,
-            self.uplink.sw_if_index)
+            self.bd.bd.bd_id,
+            self.rd.rd_id,
+            self.uplink.sw_if_index if self.uplink else INDEX_INVALID)
         self._test.registry.register(self, self._test.logger)
 
     def remove_vpp_config(self):
-        self._test.vapi.gbp_endpoint_group_add_del(
-            0,
-            self.epg,
-            self.bd,
-            self.rd,
-            self.rd,
-            self.uplink.sw_if_index)
+        self._test.vapi.gbp_endpoint_group_del(
+            self.epg)
 
     def __str__(self):
         return self.object_id()
 
     def object_id(self):
-        return "gbp-endpoint-group;[%d]" % (self.epg)
+        return "gbp-endpoint-group:[%d]" % (self.epg)
 
     def query_vpp_config(self):
         epgs = self._test.vapi.gbp_endpoint_group_dump()
@@ -249,6 +282,80 @@ class VppGbpEndpointGroup(VppObject):
         return False
 
 
+class VppGbpBridgeDomain(VppObject):
+    """
+    GBP Bridge Domain
+    """
+
+    def __init__(self, test, bd, bvi, uu_flood=None):
+        self._test = test
+        self.bvi = bvi
+        self.uu_flood = uu_flood
+        self.bd = bd
+
+    def add_vpp_config(self):
+        self._test.vapi.gbp_bridge_domain_add(
+            self.bd.bd_id,
+            self.bvi.sw_if_index,
+            self.uu_flood.sw_if_index if self.uu_flood else INDEX_INVALID)
+        self._test.registry.register(self, self._test.logger)
+
+    def remove_vpp_config(self):
+        self._test.vapi.gbp_bridge_domain_del(self.bd.bd_id)
+
+    def __str__(self):
+        return self.object_id()
+
+    def object_id(self):
+        return "gbp-bridge-domain:[%d]" % (self.bd.bd_id)
+
+    def query_vpp_config(self):
+        bds = self._test.vapi.gbp_bridge_domain_dump()
+        for bd in bds:
+            if bd.bd.bd_id == self.bd.bd_id:
+                return True
+        return False
+
+
+class VppGbpRouteDomain(VppObject):
+    """
+    GBP Route Domain
+    """
+
+    def __init__(self, test, rd_id, t4, t6, ip4_uu=None, ip6_uu=None):
+        self._test = test
+        self.rd_id = rd_id
+        self.t4 = t4
+        self.t6 = t6
+        self.ip4_uu = ip4_uu
+        self.ip6_uu = ip6_uu
+
+    def add_vpp_config(self):
+        self._test.vapi.gbp_route_domain_add(
+            self.rd_id,
+            self.t4.table_id,
+            self.t6.table_id,
+            self.ip4_uu.sw_if_index if self.ip4_uu else INDEX_INVALID,
+            self.ip6_uu.sw_if_index if self.ip6_uu else INDEX_INVALID)
+        self._test.registry.register(self, self._test.logger)
+
+    def remove_vpp_config(self):
+        self._test.vapi.gbp_route_domain_del(self.rd_id)
+
+    def __str__(self):
+        return self.object_id()
+
+    def object_id(self):
+        return "gbp-route-domain:[%d]" % (self.rd_id)
+
+    def query_vpp_config(self):
+        rds = self._test.vapi.gbp_route_domain_dump()
+        for rd in rds:
+            if rd.rd.rd_id == self.rd_id:
+                return True
+        return False
+
+
 class VppGbpContract(VppObject):
     """
     GBP Contract
@@ -279,7 +386,7 @@ class VppGbpContract(VppObject):
         return self.object_id()
 
     def object_id(self):
-        return "gbp-contract;[%d:%s:%d]" % (self.src_epg,
+        return "gbp-contract:[%d:%s:%d]" % (self.src_epg,
                                             self.dst_epg,
                                             self.acl_index)
 
@@ -292,6 +399,39 @@ class VppGbpContract(VppObject):
         return False
 
 
+class VppGbpVxlanTunnel(VppInterface):
+    """
+    GBP VXLAN tunnel
+    """
+
+    def __init__(self, test, vni, bd_rd_id, mode):
+        super(VppGbpVxlanTunnel, self).__init__(test)
+        self._test = test
+        self.vni = vni
+        self.bd_rd_id = bd_rd_id
+        self.mode = mode
+
+    def add_vpp_config(self):
+        r = self._test.vapi.gbp_vxlan_tunnel_add(
+            self.vni,
+            self.bd_rd_id,
+            self.mode)
+        self.set_sw_if_index(r.sw_if_index)
+        self._test.registry.register(self, self._test.logger)
+
+    def remove_vpp_config(self):
+        self._test.vapi.gbp_vxlan_tunnel_del(self.vni)
+
+    def __str__(self):
+        return self.object_id()
+
+    def object_id(self):
+        return "gbp-vxlan:%d" % (self.vni)
+
+    def query_vpp_config(self):
+        return find_gbp_vxlan(self._test, self.vni)
+
+
 class VppGbpAcl(VppObject):
     """
     GBP Acl
@@ -337,7 +477,7 @@ class VppGbpAcl(VppObject):
         return self.object_id()
 
     def object_id(self):
-        return "gbp-acl;[%d]" % (self.acl_index)
+        return "gbp-acl:[%d]" % (self.acl_index)
 
     def query_vpp_config(self):
         cs = self._test.vapi.acl_dump()
@@ -354,7 +494,7 @@ class TestGBP(VppTestCase):
         super(TestGBP, self).setUp()
 
         self.create_pg_interfaces(range(9))
-        self.create_loopback_interfaces(9)
+        self.create_loopback_interfaces(8)
 
         self.router_mac = VppMacAddress("00:11:22:33:44:55")
 
@@ -362,9 +502,6 @@ class TestGBP(VppTestCase):
             i.admin_up()
         for i in self.lo_interfaces:
             i.admin_up()
-            self.vapi.sw_interface_set_mac_address(
-                i.sw_if_index,
-                self.router_mac.bytes)
 
     def tearDown(self):
         for i in self.pg_interfaces:
@@ -465,42 +602,64 @@ class TestGBP(VppTestCase):
     def test_gbp(self):
         """ Group Based Policy """
 
-        nat_table = VppIpTable(self, 20)
-        nat_table.add_vpp_config()
-        nat_table = VppIpTable(self, 20, is_ip6=True)
-        nat_table.add_vpp_config()
-
         #
         # Bridge Domains
         #
-        self.vapi.bridge_domain_add_del(1, flood=1, uu_flood=1, forward=1,
-                                        learn=0, arp_term=1, is_add=1)
-        self.vapi.bridge_domain_add_del(2, flood=1, uu_flood=1, forward=1,
-                                        learn=0, arp_term=1, is_add=1)
-        self.vapi.bridge_domain_add_del(20, flood=1, uu_flood=1, forward=1,
-                                        learn=0, arp_term=1, is_add=1)
+        bd1 = VppBridgeDomain(self, 1)
+        bd2 = VppBridgeDomain(self, 2)
+        bd20 = VppBridgeDomain(self, 20)
+
+        bd1.add_vpp_config()
+        bd2.add_vpp_config()
+        bd20.add_vpp_config()
+
+        gbd1 = VppGbpBridgeDomain(self, bd1, self.loop0)
+        gbd2 = VppGbpBridgeDomain(self, bd2, self.loop1)
+        gbd20 = VppGbpBridgeDomain(self, bd20, self.loop2)
+
+        gbd1.add_vpp_config()
+        gbd2.add_vpp_config()
+        gbd20.add_vpp_config()
+
+        #
+        # Route Domains
+        #
+        gt4 = VppIpTable(self, 0)
+        gt4.add_vpp_config()
+        gt6 = VppIpTable(self, 0, is_ip6=True)
+        gt6.add_vpp_config()
+        nt4 = VppIpTable(self, 20)
+        nt4.add_vpp_config()
+        nt6 = VppIpTable(self, 20, is_ip6=True)
+        nt6.add_vpp_config()
+
+        rd0 = VppGbpRouteDomain(self, 0, gt4, gt6, None, None)
+        rd20 = VppGbpRouteDomain(self, 20, nt4, nt6, None, None)
+
+        rd0.add_vpp_config()
+        rd20.add_vpp_config()
 
         #
         # 3 EPGs, 2 of which share a BD.
         # 2 NAT EPGs, one for floating-IP subnets, the other for internet
         #
-        epgs = [VppGbpEndpointGroup(self, 220, 0, 1, self.pg4,
+        epgs = [VppGbpEndpointGroup(self, 220, rd0, gbd1, self.pg4,
                                     self.loop0,
                                     "10.0.0.128",
                                     "2001:10::128"),
-                VppGbpEndpointGroup(self, 221, 0, 1, self.pg5,
+                VppGbpEndpointGroup(self, 221, rd0, gbd1, self.pg5,
                                     self.loop0,
                                     "10.0.1.128",
                                     "2001:10:1::128"),
-                VppGbpEndpointGroup(self, 222, 0, 2, self.pg6,
+                VppGbpEndpointGroup(self, 222, rd0, gbd2, self.pg6,
                                     self.loop1,
                                     "10.0.2.128",
                                     "2001:10:2::128"),
-                VppGbpEndpointGroup(self, 333, 20, 20, self.pg7,
+                VppGbpEndpointGroup(self, 333, rd20, gbd20, self.pg7,
                                     self.loop2,
                                     "11.0.0.128",
                                     "3001::128"),
-                VppGbpEndpointGroup(self, 444, 20, 20, self.pg8,
+                VppGbpEndpointGroup(self, 444, rd20, gbd20, self.pg8,
                                     self.loop2,
                                     "11.0.0.129",
                                     "3001::129")]
@@ -513,7 +672,7 @@ class TestGBP(VppTestCase):
                    VppGbpRecirc(self, epgs[3],
                                 self.loop6, is_ext=True),
                    VppGbpRecirc(self, epgs[4],
-                                self.loop8, is_ext=True)]
+                                self.loop7, is_ext=True)]
 
         epg_nat = epgs[3]
         recirc_nat = recircs[3]
@@ -544,8 +703,11 @@ class TestGBP(VppTestCase):
         for epg in epgs:
             # IP config on the BVI interfaces
             if epg != epgs[1] and epg != epgs[4]:
-                epg.bvi.set_table_ip4(epg.rd)
-                epg.bvi.set_table_ip6(epg.rd)
+                VppIpInterfaceBind(self, epg.bvi, epg.rd.t4).add_vpp_config()
+                VppIpInterfaceBind(self, epg.bvi, epg.rd.t6).add_vpp_config()
+                self.vapi.sw_interface_set_mac_address(
+                    epg.bvi.sw_if_index,
+                    self.router_mac.bytes)
 
                 # The BVIs are NAT inside interfaces
                 self.vapi.nat44_interface_add_del_feature(epg.bvi.sw_if_index,
@@ -555,60 +717,37 @@ class TestGBP(VppTestCase):
                                                   is_inside=1,
                                                   is_add=1)
 
-            self.vapi.sw_interface_add_del_address(epg.bvi.sw_if_index,
-                                                   epg.bvi_ip4.bytes,
-                                                   32)
-            self.vapi.sw_interface_add_del_address(epg.bvi.sw_if_index,
-                                                   epg.bvi_ip6.bytes,
-                                                   128,
-                                                   is_ipv6=True)
+            if_ip4 = VppIpInterfaceAddress(self, epg.bvi, epg.bvi_ip4, 32)
+            if_ip6 = VppIpInterfaceAddress(self, epg.bvi, epg.bvi_ip6, 128)
+            if_ip4.add_vpp_config()
+            if_ip6.add_vpp_config()
 
-            # EPG uplink interfaces in the BD
-            epg.uplink.set_table_ip4(epg.rd)
-            epg.uplink.set_table_ip6(epg.rd)
-            self.vapi.sw_interface_set_l2_bridge(epg.uplink.sw_if_index,
-                                                 epg.bd)
+            # EPG uplink interfaces in the RD
+            VppIpInterfaceBind(self, epg.uplink, epg.rd.t4).add_vpp_config()
+            VppIpInterfaceBind(self, epg.uplink, epg.rd.t6).add_vpp_config()
 
             # add the BD ARP termination entry for BVI IP
-            self.vapi.bd_ip_mac_add_del(bd_id=epg.bd,
-                                        mac=self.router_mac.encode(),
-                                        ip=epg.bvi_ip4.encode(),
-                                        is_ipv6=0,
-                                        is_add=1)
-            self.vapi.bd_ip_mac_add_del(bd_id=epg.bd,
-                                        mac=self.router_mac.encode(),
-                                        ip=epg.bvi_ip6.encode(),
-                                        is_ipv6=1,
-                                        is_add=1)
-
-            # epg[1] shares the same BVI to epg[0]
-            if epg != epgs[1] and epg != epgs[4]:
-                # BVI in BD
-                self.vapi.sw_interface_set_l2_bridge(
-                    epg.bvi.sw_if_index,
-                    epg.bd,
-                    port_type=L2_PORT_TYPE.BVI)
-
-                # BVI L2 FIB entry
-                self.vapi.l2fib_add_del(self.router_mac.address,
-                                        epg.bd,
-                                        epg.bvi.sw_if_index,
-                                        is_add=1, bvi_mac=1)
+            epg.bd_arp_ip4 = VppBridgeDomainArpEntry(self, epg.bd.bd,
+                                                     self.router_mac.address,
+                                                     epg.bvi_ip4)
+            epg.bd_arp_ip6 = VppBridgeDomainArpEntry(self, epg.bd.bd,
+                                                     self.router_mac.address,
+                                                     epg.bvi_ip6)
+            epg.bd_arp_ip4.add_vpp_config()
+            epg.bd_arp_ip6.add_vpp_config()
 
             # EPG in VPP
             epg.add_vpp_config()
 
         for recirc in recircs:
             # EPG's ingress recirculation interface maps to its RD
-            recirc.recirc.set_table_ip4(recirc.epg.rd)
-            recirc.recirc.set_table_ip6(recirc.epg.rd)
+            VppIpInterfaceBind(self, recirc.recirc,
+                               recirc.epg.rd.t4).add_vpp_config()
+            VppIpInterfaceBind(self, recirc.recirc,
+                               recirc.epg.rd.t6).add_vpp_config()
 
-            # in the bridge to allow DVR. L2 emulation to punt to L3
-            self.vapi.sw_interface_set_l2_bridge(recirc.recirc.sw_if_index,
-                                                 recirc.epg.bd)
             self.vapi.sw_interface_set_l2_emulation(
                 recirc.recirc.sw_if_index)
-
             self.vapi.nat44_interface_add_del_feature(
                 recirc.recirc.sw_if_index,
                 is_inside=0,
@@ -620,8 +759,11 @@ class TestGBP(VppTestCase):
 
             recirc.add_vpp_config()
 
-        ep_routes = []
-        ep_arps = []
+        for recirc in recircs:
+            self.assertTrue(find_bridge_domain_port(self,
+                                                    recirc.epg.bd.bd.bd_id,
+                                                    recirc.recirc.sw_if_index))
+
         for ep in eps:
             self.pg_enable_capture(self.pg_interfaces)
             self.pg_start()
@@ -631,32 +773,6 @@ class TestGBP(VppTestCase):
             # the subnet is not attached.
             #
             for (ip, fip) in zip(ep.ips, ep.fips):
-                r = VppIpRoute(self, ip.address, ip.length,
-                               [VppRoutePath(ip.address,
-                                             ep.epg.bvi.sw_if_index,
-                                             proto=ip.dpo_proto)],
-                               is_ip6=ip.is_ip6)
-                r.add_vpp_config()
-                ep_routes.append(r)
-
-                #
-                # ARP entries for the endpoints
-                #
-                a = VppNeighbor(self,
-                                ep.epg.bvi.sw_if_index,
-                                ep.itf.remote_mac,
-                                ip.address,
-                                af=ip.af)
-                a.add_vpp_config()
-                ep_arps.append(a)
-
-                # add the BD ARP termination entry
-                self.vapi.bd_ip_mac_add_del(bd_id=ep.epg.bd,
-                                            mac=ep.vmac.encode(),
-                                            ip=ip.encode(),
-                                            is_ipv6=ip.is_ip6,
-                                            is_add=1)
-
                 # Add static mappings for each EP from the 10/8 to 11/8 network
                 if ip.af == AF_INET:
                     self.vapi.nat44_add_del_static_mapping(ip.bytes,
@@ -668,16 +784,6 @@ class TestGBP(VppTestCase):
                                                            fip.bytes,
                                                            vrf_id=0)
 
-            # add each EP itf to the its BD
-            self.vapi.sw_interface_set_l2_bridge(ep.itf.sw_if_index,
-                                                 ep.epg.bd)
-
-            # L2 FIB entry
-            self.vapi.l2fib_add_del(ep.mac,
-                                    ep.epg.bd,
-                                    ep.itf.sw_if_index,
-                                    is_add=1)
-
             # VPP EP create ...
             ep.add_vpp_config()
 
@@ -699,11 +805,8 @@ class TestGBP(VppTestCase):
 
             # add the BD ARP termination entry for floating IP
             for fip in ep.fips:
-                self.vapi.bd_ip_mac_add_del(bd_id=epg_nat.bd,
-                                            mac=ep.vmac.encode(),
-                                            ip=fip.encode(),
-                                            is_ipv6=fip.is_ip6,
-                                            is_add=1)
+                ba = VppBridgeDomainArpEntry(self, epg_nat.bd.bd, ep.mac, fip)
+                ba.add_vpp_config()
 
                 # floating IPs route via EPG recirc
                 r = VppIpRoute(self, fip.address, fip.length,
@@ -714,27 +817,31 @@ class TestGBP(VppTestCase):
                                table_id=20,
                                is_ip6=fip.is_ip6)
                 r.add_vpp_config()
-                ep_routes.append(r)
 
             # L2 FIB entries in the NAT EPG BD to bridge the packets from
             # the outside direct to the internal EPG
-            self.vapi.l2fib_add_del(ep.mac,
-                                    epg_nat.bd,
-                                    ep.recirc.recirc.sw_if_index,
-                                    is_add=1)
+            lf = VppL2FibEntry(self, epg_nat.bd.bd, ep.mac,
+                               ep.recirc.recirc, bvi_mac=0)
+            lf.add_vpp_config()
 
         #
-        # ARP packets for unknown IP are flooded
+        # ARP packets for unknown IP are sent to the EPG uplink
         #
         pkt_arp = (Ether(dst="ff:ff:ff:ff:ff:ff",
                          src=self.pg0.remote_mac) /
                    ARP(op="who-has",
                        hwdst="ff:ff:ff:ff:ff:ff",
                        hwsrc=self.pg0.remote_mac,
-                       pdst=epgs[0].bvi_ip4.address,
-                       psrc="10.0.0.88"))
+                       pdst="10.0.0.88",
+                       psrc="10.0.0.99"))
 
-        self.send_and_expect(self.pg0, [pkt_arp], self.pg0)
+        self.vapi.cli("clear trace")
+        self.pg0.add_stream(pkt_arp)
+
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+
+        rxd = epgs[0].uplink.get_capture(1)
 
         #
         # ARP/ND packets get a response
@@ -751,7 +858,8 @@ class TestGBP(VppTestCase):
 
         nsma = in6_getnsma(inet_pton(AF_INET6, eps[0].ip6.address))
         d = inet_ntop(AF_INET6, nsma)
-        pkt_nd = (Ether(dst=in6_getnsmac(nsma)) /
+        pkt_nd = (Ether(dst=in6_getnsmac(nsma),
+                        src=self.pg0.remote_mac) /
                   IPv6(dst=d, src=eps[0].ip6.address) /
                   ICMPv6ND_NS(tgt=epgs[0].bvi_ip6.address) /
                   ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac))
@@ -806,28 +914,40 @@ class TestGBP(VppTestCase):
         #
         # Add the subnet routes
         #
-        s41 = VppGbpSubnet(self, 0, "10.0.0.0", 24)
-        s42 = VppGbpSubnet(self, 0, "10.0.1.0", 24)
-        s43 = VppGbpSubnet(self, 0, "10.0.2.0", 24)
+        s41 = VppGbpSubnet(
+            self, rd0, "10.0.0.0", 24,
+            VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL)
+        s42 = VppGbpSubnet(
+            self, rd0, "10.0.1.0", 24,
+            VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL)
+        s43 = VppGbpSubnet(
+            self, rd0, "10.0.2.0", 24,
+            VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL)
+        s61 = VppGbpSubnet(
+            self, rd0, "2001:10::1", 64,
+            VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL)
+        s62 = VppGbpSubnet(
+            self, rd0, "2001:10:1::1", 64,
+            VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL)
+        s63 = VppGbpSubnet(
+            self, rd0, "2001:10:2::1", 64,
+            VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL)
         s41.add_vpp_config()
         s42.add_vpp_config()
         s43.add_vpp_config()
-        s61 = VppGbpSubnet(self, 0, "2001:10::1", 64)
-        s62 = VppGbpSubnet(self, 0, "2001:10:1::1", 64)
-        s63 = VppGbpSubnet(self, 0, "2001:10:2::1", 64)
         s61.add_vpp_config()
         s62.add_vpp_config()
         s63.add_vpp_config()
 
-        self.send_and_expect_bridged(self.pg0,
+        self.send_and_expect_bridged(eps[0].itf,
                                      pkt_intra_epg_220_ip4 * 65,
-                                     self.pg4)
-        self.send_and_expect_bridged(self.pg3,
+                                     eps[0].epg.uplink)
+        self.send_and_expect_bridged(eps[0].itf,
                                      pkt_inter_epg_222_ip4 * 65,
-                                     self.pg6)
-        self.send_and_expect_bridged6(self.pg3,
+                                     eps[0].epg.uplink)
+        self.send_and_expect_bridged6(eps[0].itf,
                                       pkt_inter_epg_222_ip6 * 65,
-                                      self.pg6)
+                                      eps[0].epg.uplink)
 
         self.logger.info(self.vapi.cli("sh ip fib 11.0.0.2"))
         self.logger.info(self.vapi.cli("sh gbp endpoint-group"))
@@ -838,6 +958,7 @@ class TestGBP(VppTestCase):
         self.logger.info(self.vapi.cli("sh int feat loop6"))
         self.logger.info(self.vapi.cli("sh vlib graph ip4-gbp-src-classify"))
         self.logger.info(self.vapi.cli("sh int feat loop3"))
+        self.logger.info(self.vapi.cli("sh int feat pg0"))
 
         #
         # Packet destined to unknown unicast is sent on the epg uplink ...
@@ -849,9 +970,9 @@ class TestGBP(VppTestCase):
                                        UDP(sport=1234, dport=1234) /
                                        Raw('\xa5' * 100))
 
-        self.send_and_expect_bridged(self.pg0,
+        self.send_and_expect_bridged(eps[0].itf,
                                      pkt_intra_epg_220_to_uplink * 65,
-                                     self.pg4)
+                                     eps[0].epg.uplink)
         # ... and nowhere else
         self.pg1.get_capture(0, timeout=0.1)
         self.pg1.assert_nothing_captured(remark="Flood onto other VMS")
@@ -863,9 +984,9 @@ class TestGBP(VppTestCase):
                                        UDP(sport=1234, dport=1234) /
                                        Raw('\xa5' * 100))
 
-        self.send_and_expect_bridged(self.pg2,
+        self.send_and_expect_bridged(eps[2].itf,
                                      pkt_intra_epg_221_to_uplink * 65,
-                                     self.pg5)
+                                     eps[2].epg.uplink)
 
         #
         # Packets from the uplink are forwarded in the absence of a contract
@@ -917,9 +1038,9 @@ class TestGBP(VppTestCase):
                                     UDP(sport=1234, dport=1234) /
                                     Raw('\xa5' * 100))
 
-        self.send_and_assert_no_replies(self.pg0,
+        self.send_and_assert_no_replies(eps[0].itf,
                                         pkt_inter_epg_220_to_221 * 65)
-        self.send_and_assert_no_replies(self.pg0,
+        self.send_and_assert_no_replies(eps[0].itf,
                                         pkt_inter_epg_220_to_222 * 65)
 
         #
@@ -932,10 +1053,10 @@ class TestGBP(VppTestCase):
         c1 = VppGbpContract(self, 220, 221, acl_index)
         c1.add_vpp_config()
 
-        self.send_and_expect_bridged(self.pg0,
+        self.send_and_expect_bridged(eps[0].itf,
                                      pkt_inter_epg_220_to_221 * 65,
-                                     self.pg2)
-        self.send_and_assert_no_replies(self.pg0,
+                                     eps[2].itf)
+        self.send_and_assert_no_replies(eps[0].itf,
                                         pkt_inter_epg_220_to_222 * 65)
 
         #
@@ -944,18 +1065,18 @@ class TestGBP(VppTestCase):
         c2 = VppGbpContract(self, 221, 220, acl_index)
         c2.add_vpp_config()
 
-        self.send_and_expect_bridged(self.pg0,
+        self.send_and_expect_bridged(eps[0].itf,
                                      pkt_inter_epg_220_to_221 * 65,
-                                     self.pg2)
-        self.send_and_expect_bridged(self.pg2,
+                                     eps[2].itf)
+        self.send_and_expect_bridged(eps[2].itf,
                                      pkt_inter_epg_221_to_220 * 65,
-                                     self.pg0)
+                                     eps[0].itf)
 
         #
         # check that inter group is still disabled for the groups
         # not in the contract.
         #
-        self.send_and_assert_no_replies(self.pg0,
+        self.send_and_assert_no_replies(eps[0].itf,
                                         pkt_inter_epg_220_to_222 * 65)
 
         #
@@ -966,9 +1087,9 @@ class TestGBP(VppTestCase):
 
         self.logger.info(self.vapi.cli("sh gbp contract"))
 
-        self.send_and_expect_routed(self.pg0,
+        self.send_and_expect_routed(eps[0].itf,
                                     pkt_inter_epg_220_to_222 * 65,
-                                    self.pg3,
+                                    eps[3].itf,
                                     self.router_mac.address)
 
         #
@@ -979,45 +1100,53 @@ class TestGBP(VppTestCase):
         c3.remove_vpp_config()
         acl.remove_vpp_config()
 
-        self.send_and_assert_no_replies(self.pg2,
+        self.send_and_assert_no_replies(eps[2].itf,
                                         pkt_inter_epg_221_to_220 * 65)
-        self.send_and_assert_no_replies(self.pg0,
+        self.send_and_assert_no_replies(eps[0].itf,
                                         pkt_inter_epg_220_to_221 * 65)
-        self.send_and_expect_bridged(self.pg0, pkt_intra_epg * 65, self.pg1)
+        self.send_and_expect_bridged(eps[0].itf,
+                                     pkt_intra_epg * 65,
+                                     eps[1].itf)
 
         #
         # EPs to the outside world
         #
 
         # in the EP's RD an external subnet via the NAT EPG's recirc
-        se1 = VppGbpSubnet(self, 0, "0.0.0.0", 0,
-                           is_internal=False,
-                           sw_if_index=recirc_nat.recirc.sw_if_index,
-                           epg=epg_nat.epg)
+        se1 = VppGbpSubnet(
+            self, rd0, "0.0.0.0", 0,
+            VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
+            sw_if_index=recirc_nat.recirc.sw_if_index,
+            epg=epg_nat.epg)
+        se2 = VppGbpSubnet(
+            self, rd0, "11.0.0.0", 8,
+            VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
+            sw_if_index=recirc_nat.recirc.sw_if_index,
+            epg=epg_nat.epg)
+        se16 = VppGbpSubnet(
+            self, rd0, "::", 0,
+            VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
+            sw_if_index=recirc_nat.recirc.sw_if_index,
+            epg=epg_nat.epg)
+        # in the NAT RD an external subnet via the NAT EPG's uplink
+        se3 = VppGbpSubnet(
+            self, rd20, "0.0.0.0", 0,
+            VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
+            sw_if_index=epg_nat.uplink.sw_if_index,
+            epg=epg_nat.epg)
+        se36 = VppGbpSubnet(
+            self, rd20, "::", 0,
+            VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
+            sw_if_index=epg_nat.uplink.sw_if_index,
+            epg=epg_nat.epg)
+        se4 = VppGbpSubnet(
+            self, rd20, "11.0.0.0", 8,
+            VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
+            sw_if_index=epg_nat.uplink.sw_if_index,
+            epg=epg_nat.epg)
         se1.add_vpp_config()
-        se2 = VppGbpSubnet(self, 0, "11.0.0.0", 8,
-                           is_internal=False,
-                           sw_if_index=recirc_nat.recirc.sw_if_index,
-                           epg=epg_nat.epg)
         se2.add_vpp_config()
-        se16 = VppGbpSubnet(self, 0, "::", 0,
-                            is_internal=False,
-                            sw_if_index=recirc_nat.recirc.sw_if_index,
-                            epg=epg_nat.epg)
         se16.add_vpp_config()
-        # in the NAT RD an external subnet via the NAT EPG's uplink
-        se3 = VppGbpSubnet(self, 20, "0.0.0.0", 0,
-                           is_internal=False,
-                           sw_if_index=epg_nat.uplink.sw_if_index,
-                           epg=epg_nat.epg)
-        se36 = VppGbpSubnet(self, 20, "::", 0,
-                            is_internal=False,
-                            sw_if_index=epg_nat.uplink.sw_if_index,
-                            epg=epg_nat.epg)
-        se4 = VppGbpSubnet(self, 20, "11.0.0.0", 8,
-                           is_internal=False,
-                           sw_if_index=epg_nat.uplink.sw_if_index,
-                           epg=epg_nat.epg)
         se3.add_vpp_config()
         se36.add_vpp_config()
         se4.add_vpp_config()
@@ -1039,7 +1168,7 @@ class TestGBP(VppTestCase):
                                        Raw('\xa5' * 100))
 
         # no policy yet
-        self.send_and_assert_no_replies(self.pg0,
+        self.send_and_assert_no_replies(eps[0].itf,
                                         pkt_inter_epg_220_to_global * 65)
 
         acl2 = VppGbpAcl(self)
@@ -1053,7 +1182,7 @@ class TestGBP(VppTestCase):
         c4 = VppGbpContract(self, 220, 333, acl_index2)
         c4.add_vpp_config()
 
-        self.send_and_expect_natted(self.pg0,
+        self.send_and_expect_natted(eps[0].itf,
                                     pkt_inter_epg_220_to_global * 65,
                                     self.pg7,
                                     eps[0].fip4.address)
@@ -1150,24 +1279,7 @@ class TestGBP(VppTestCase):
 
         for epg in epgs:
             # IP config on the BVI interfaces
-            self.vapi.sw_interface_add_del_address(epg.bvi.sw_if_index,
-                                                   epg.bvi_ip4.bytes,
-                                                   32,
-                                                   is_add=0)
-            self.vapi.sw_interface_add_del_address(epg.bvi.sw_if_index,
-                                                   epg.bvi_ip6.bytes,
-                                                   128,
-                                                   is_add=0,
-                                                   is_ipv6=True)
-            self.logger.info(self.vapi.cli("sh int addr"))
-
-            epg.uplink.set_table_ip4(0)
-            epg.uplink.set_table_ip6(0)
-
             if epg != epgs[0] and epg != epgs[3]:
-                epg.bvi.set_table_ip4(0)
-                epg.bvi.set_table_ip6(0)
-
                 self.vapi.nat44_interface_add_del_feature(epg.bvi.sw_if_index,
                                                           is_inside=1,
                                                           is_add=0)
@@ -1176,9 +1288,8 @@ class TestGBP(VppTestCase):
                                                   is_add=0)
 
         for recirc in recircs:
-            recirc.recirc.set_table_ip4(0)
-            recirc.recirc.set_table_ip6(0)
-
+            self.vapi.sw_interface_set_l2_emulation(
+                recirc.recirc.sw_if_index, enable=0)
             self.vapi.nat44_interface_add_del_feature(
                 recirc.recirc.sw_if_index,
                 is_inside=0,
@@ -1188,6 +1299,818 @@ class TestGBP(VppTestCase):
                 is_inside=0,
                 is_add=0)
 
+    def test_gbp_learn_l2(self):
+        """ GBP L2 Endpoint Learning """
+
+        learnt = [{'mac': '00:00:11:11:11:01',
+                   'ip': '10.0.0.1',
+                   'ip6': '2001:10::2'},
+                  {'mac': '00:00:11:11:11:02',
+                   'ip': '10.0.0.2',
+                   'ip6': '2001:10::3'}]
+
+        #
+        # lower the inactive threshold so these tests pass in a
+        # reasonable amount of time
+        #
+        self.vapi.gbp_endpoint_learn_set_inactive_threshold(1)
+
+        #
+        # IP tables
+        #
+        gt4 = VppIpTable(self, 1)
+        gt4.add_vpp_config()
+        gt6 = VppIpTable(self, 1, is_ip6=True)
+        gt6.add_vpp_config()
+
+        rd1 = VppGbpRouteDomain(self, 1, gt4, gt6)
+        rd1.add_vpp_config()
+
+        #
+        # Pg2 hosts the vxlan tunnel, hosts on pg2 to act as TEPs
+        # Pg3 hosts the IP4 UU-flood VXLAN tunnel
+        # Pg4 hosts the IP6 UU-flood VXLAN tunnel
+        #
+        self.pg2.config_ip4()
+        self.pg2.resolve_arp()
+        self.pg2.generate_remote_hosts(4)
+        self.pg2.configure_ipv4_neighbors()
+        self.pg3.config_ip4()
+        self.pg3.resolve_arp()
+        self.pg4.config_ip4()
+        self.pg4.resolve_arp()
+
+        #
+        # a GBP bridge domain with a BVI and a UU-flood interface
+        #
+        bd1 = VppBridgeDomain(self, 1)
+        bd1.add_vpp_config()
+        gbd1 = VppGbpBridgeDomain(self, bd1, self.loop0, self.pg3)
+        gbd1.add_vpp_config()
+
+        self.logger.info(self.vapi.cli("sh bridge 1 detail"))
+        self.logger.info(self.vapi.cli("sh gbp bridge"))
+
+        # ... and has a /32 applied
+        ip_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 32)
+        ip_addr.add_vpp_config()
+
+        #
+        # The Endpoint-group in which we are learning endpoints
+        #
+        epg_220 = VppGbpEndpointGroup(self, 220, rd1, gbd1,
+                                      None, self.loop0,
+                                      "10.0.0.128",
+                                      "2001:10::128")
+        epg_220.add_vpp_config()
+        epg_330 = VppGbpEndpointGroup(self, 330, rd1, gbd1,
+                                      None, self.loop1,
+                                      "10.0.1.128",
+                                      "2001:11::128")
+        epg_330.add_vpp_config()
+
+        #
+        # The VXLAN GBP tunnel is a bridge-port and has L2 endpoint
+        # leanring enabled
+        #
+        vx_tun_l2_1 = VppGbpVxlanTunnel(
+            self, 99, bd1.bd_id,
+            VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L2)
+        vx_tun_l2_1.add_vpp_config()
+
+        #
+        # A static endpoint that the learnt endpoints are trying to
+        # talk to
+        #
+        ep = VppGbpEndpoint(self, self.pg0,
+                            epg_220, None,
+                            "10.0.0.127", "11.0.0.127",
+                            "2001:10::1", "3001::1")
+        ep.add_vpp_config()
+
+        self.assertTrue(find_route(self, ep.ip4.address, 32, table_id=1))
+
+        # a packet with an sclass from an unknwon EPG
+        p = (Ether(src=self.pg2.remote_mac,
+                   dst=self.pg2.local_mac) /
+             IP(src=self.pg2.remote_hosts[0].ip4,
+                dst=self.pg2.local_ip4) /
+             UDP(sport=1234, dport=48879) /
+             VXLAN(vni=99, gpid=88, flags=0x88) /
+             Ether(src=learnt[0]["mac"], dst=ep.mac) /
+             IP(src=learnt[0]["ip"], dst=ep.ip4.address) /
+             UDP(sport=1234, dport=1234) /
+             Raw('\xa5' * 100))
+
+        self.send_and_assert_no_replies(self.pg2, p)
+
+        #
+        # we should not have learnt a new tunnel endpoint, since
+        # the EPG was not learnt.
+        #
+        self.assertEqual(INDEX_INVALID,
+                         find_vxlan_gbp_tunnel(self,
+                                               self.pg2.local_ip4,
+                                               self.pg2.remote_hosts[0].ip4,
+                                               99))
+
+        # epg is not learnt, becasue the EPG is unknwon
+        self.assertEqual(len(self.vapi.gbp_endpoint_dump()), 1)
+
+        for ii, l in enumerate(learnt):
+            # a packet with an sclass from a knwon EPG
+            # arriving on an unknown TEP
+            p = (Ether(src=self.pg2.remote_mac,
+                       dst=self.pg2.local_mac) /
+                 IP(src=self.pg2.remote_hosts[1].ip4,
+                    dst=self.pg2.local_ip4) /
+                 UDP(sport=1234, dport=48879) /
+                 VXLAN(vni=99, gpid=220, flags=0x88) /
+                 Ether(src=l['mac'], dst=ep.mac) /
+                 IP(src=l['ip'], dst=ep.ip4.address) /
+                 UDP(sport=1234, dport=1234) /
+                 Raw('\xa5' * 100))
+
+            rx = self.send_and_expect(self.pg2, [p], self.pg0)
+
+            # the new TEP
+            tep1_sw_if_index = find_vxlan_gbp_tunnel(
+                self,
+                self.pg2.local_ip4,
+                self.pg2.remote_hosts[1].ip4,
+                99)
+            self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
+
+            #
+            # the EP is learnt via the learnt TEP
+            # both from its MAC and its IP
+            #
+            self.assertTrue(find_gbp_endpoint(self,
+                                              vx_tun_l2_1.sw_if_index,
+                                              mac=l['mac']))
+            self.assertTrue(find_gbp_endpoint(self,
+                                              vx_tun_l2_1.sw_if_index,
+                                              ip=l['ip']))
+
+        self.logger.info(self.vapi.cli("show gbp endpoint"))
+        self.logger.info(self.vapi.cli("show gbp vxlan"))
+        self.logger.info(self.vapi.cli("show vxlan-gbp tunnel"))
+
+        #
+        # If we sleep for the threshold time, the learnt endpoints should
+        # age out
+        #
+        self.sleep(2)
+        for l in learnt:
+            self.assertFalse(find_gbp_endpoint(self,
+                                               tep1_sw_if_index,
+                                               mac=l['mac']))
+
+        self.logger.info(self.vapi.cli("show gbp endpoint"))
+        self.logger.info(self.vapi.cli("show gbp vxlan"))
+        self.logger.info(self.vapi.cli("show vxlan-gbp tunnel"))
+
+        #
+        # repeat. the do not learn bit is set so the EPs are not learnt
+        #
+        for l in learnt:
+            # a packet with an sclass from a knwon EPG
+            p = (Ether(src=self.pg2.remote_mac,
+                       dst=self.pg2.local_mac) /
+                 IP(src=self.pg2.remote_hosts[1].ip4,
+                    dst=self.pg2.local_ip4) /
+                 UDP(sport=1234, dport=48879) /
+                 VXLAN(vni=99, gpid=220, flags=0x88, gpflags="D") /
+                 Ether(src=l['mac'], dst=ep.mac) /
+                 IP(src=l['ip'], dst=ep.ip4.address) /
+                 UDP(sport=1234, dport=1234) /
+                 Raw('\xa5' * 100))
+
+            rx = self.send_and_expect(self.pg2, p*65, self.pg0)
+
+        for l in learnt:
+            self.assertFalse(find_gbp_endpoint(self,
+                                               vx_tun_l2_1.sw_if_index,
+                                               mac=l['mac']))
+
+        #
+        # repeat
+        #
+        for l in learnt:
+            # a packet with an sclass from a knwon EPG
+            p = (Ether(src=self.pg2.remote_mac,
+                       dst=self.pg2.local_mac) /
+                 IP(src=self.pg2.remote_hosts[1].ip4,
+                    dst=self.pg2.local_ip4) /
+                 UDP(sport=1234, dport=48879) /
+                 VXLAN(vni=99, gpid=220, flags=0x88) /
+                 Ether(src=l['mac'], dst=ep.mac) /
+                 IP(src=l['ip'], dst=ep.ip4.address) /
+                 UDP(sport=1234, dport=1234) /
+                 Raw('\xa5' * 100))
+
+            rx = self.send_and_expect(self.pg2, p*65, self.pg0)
+
+            self.assertTrue(find_gbp_endpoint(self,
+                                              vx_tun_l2_1.sw_if_index,
+                                              mac=l['mac']))
+
+        #
+        # Static EP replies to dynamics
+        #
+        self.logger.info(self.vapi.cli("sh l2fib bd_id 1"))
+        for l in learnt:
+            p = (Ether(src=ep.mac, dst=l['mac']) /
+                 IP(dst=l['ip'], src=ep.ip4.address) /
+                 UDP(sport=1234, dport=1234) /
+                 Raw('\xa5' * 100))
+
+            rxs = self.send_and_expect(self.pg0, p * 17, self.pg2)
+
+            for rx in rxs:
+                self.assertEqual(rx[IP].src, self.pg2.local_ip4)
+                self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4)
+                self.assertEqual(rx[UDP].dport, 48879)
+                # the UDP source port is a random value for hashing
+                self.assertEqual(rx[VXLAN].gpid, 220)
+                self.assertEqual(rx[VXLAN].vni, 99)
+                self.assertTrue(rx[VXLAN].flags.G)
+                self.assertTrue(rx[VXLAN].flags.Instance)
+                self.assertTrue(rx[VXLAN].gpflags.A)
+                self.assertFalse(rx[VXLAN].gpflags.D)
+
+        self.sleep(2)
+        for l in learnt:
+            self.assertFalse(find_gbp_endpoint(self,
+                                               vx_tun_l2_1.sw_if_index,
+                                               mac=l['mac']))
+
+        #
+        # repeat in the other EPG
+        # there's no contract between 220 and 330, but the A-bit is set
+        # so the packet is cleared for delivery
+        #
+        for l in learnt:
+            # a packet with an sclass from a knwon EPG
+            p = (Ether(src=self.pg2.remote_mac,
+                       dst=self.pg2.local_mac) /
+                 IP(src=self.pg2.remote_hosts[1].ip4,
+                    dst=self.pg2.local_ip4) /
+                 UDP(sport=1234, dport=48879) /
+                 VXLAN(vni=99, gpid=330, flags=0x88, gpflags='A') /
+                 Ether(src=l['mac'], dst=ep.mac) /
+                 IP(src=l['ip'], dst=ep.ip4.address) /
+                 UDP(sport=1234, dport=1234) /
+                 Raw('\xa5' * 100))
+
+            rx = self.send_and_expect(self.pg2, p*65, self.pg0)
+
+            self.assertTrue(find_gbp_endpoint(self,
+                                              vx_tun_l2_1.sw_if_index,
+                                              mac=l['mac']))
+
+        #
+        # static EP cannot reach the learnt EPs since there is no contract
+        #
+        self.logger.info(self.vapi.cli("show gbp endpoint"))
+        self.logger.info(self.vapi.cli("show l2fib all"))
+        for l in learnt:
+            p = (Ether(src=ep.mac, dst=l['mac']) /
+                 IP(dst=l['ip'], src=ep.ip4.address) /
+                 UDP(sport=1234, dport=1234) /
+                 Raw('\xa5' * 100))
+
+            self.send_and_assert_no_replies(self.pg0, [p], timeout=0.2)
+
+        #
+        # refresh the entries after the check for no replies above
+        #
+        for l in learnt:
+            # a packet with an sclass from a knwon EPG
+            p = (Ether(src=self.pg2.remote_mac,
+                       dst=self.pg2.local_mac) /
+                 IP(src=self.pg2.remote_hosts[1].ip4,
+                    dst=self.pg2.local_ip4) /
+                 UDP(sport=1234, dport=48879) /
+                 VXLAN(vni=99, gpid=330, flags=0x88, gpflags='A') /
+                 Ether(src=l['mac'], dst=ep.mac) /
+                 IP(src=l['ip'], dst=ep.ip4.address) /
+                 UDP(sport=1234, dport=1234) /
+                 Raw('\xa5' * 100))
+
+            rx = self.send_and_expect(self.pg2, p*65, self.pg0)
+
+            self.assertTrue(find_gbp_endpoint(self,
+                                              vx_tun_l2_1.sw_if_index,
+                                              mac=l['mac']))
+
+        #
+        # Add the contract so they can talk
+        #
+        acl = VppGbpAcl(self)
+        rule = acl.create_rule(permit_deny=1, proto=17)
+        rule2 = acl.create_rule(is_ipv6=1, permit_deny=1, proto=17)
+        acl_index = acl.add_vpp_config([rule, rule2])
+        c1 = VppGbpContract(self, 220, 330, acl_index)
+        c1.add_vpp_config()
+
+        for l in learnt:
+            p = (Ether(src=ep.mac, dst=l['mac']) /
+                 IP(dst=l['ip'], src=ep.ip4.address) /
+                 UDP(sport=1234, dport=1234) /
+                 Raw('\xa5' * 100))
+
+            self.send_and_expect(self.pg0, [p], self.pg2)
+
+        #
+        # send UU packets from the local EP
+        #
+        self.logger.info(self.vapi.cli("sh bridge 1 detail"))
+        self.logger.info(self.vapi.cli("sh gbp bridge"))
+        p_uu = (Ether(src=ep.mac, dst="00:11:11:11:11:11") /
+                IP(dst="10.0.0.133", src=ep.ip4.address) /
+                UDP(sport=1234, dport=1234) /
+                Raw('\xa5' * 100))
+        rxs = self.send_and_expect(ep.itf, [p_uu], gbd1.uu_flood)
+
+        #
+        # Add a mcast destination VXLAN-GBP tunnel for B&M traffic
+        #
+        tun_bm = VppVxlanGbpTunnel(self, self.pg4.local_ip4,
+                                   "239.1.1.1", 88,
+                                   mcast_itf=self.pg4)
+        tun_bm.add_vpp_config()
+        bp_bm = VppBridgeDomainPort(self, bd1, tun_bm,
+                                    port_type=L2_PORT_TYPE.NORMAL)
+        bp_bm.add_vpp_config()
+
+        self.logger.info(self.vapi.cli("sh bridge 1 detail"))
+
+        p_bm = (Ether(src=ep.mac, dst="ff:ff:ff:ff:ff:ff") /
+                IP(dst="10.0.0.133", src=ep.ip4.address) /
+                UDP(sport=1234, dport=1234) /
+                Raw('\xa5' * 100))
+        rxs = self.send_and_expect_only(ep.itf, [p_bm], tun_bm.mcast_itf)
+
+        #
+        # Check v6 Endpoints
+        #
+        for l in learnt:
+            # a packet with an sclass from a knwon EPG
+            p = (Ether(src=self.pg2.remote_mac,
+                       dst=self.pg2.local_mac) /
+                 IP(src=self.pg2.remote_hosts[1].ip4,
+                    dst=self.pg2.local_ip4) /
+                 UDP(sport=1234, dport=48879) /
+                 VXLAN(vni=99, gpid=330, flags=0x88, gpflags='A') /
+                 Ether(src=l['mac'], dst=ep.mac) /
+                 IPv6(src=l['ip6'], dst=ep.ip6.address) /
+                 UDP(sport=1234, dport=1234) /
+                 Raw('\xa5' * 100))
+
+            rx = self.send_and_expect(self.pg2, p*65, self.pg0)
+
+            self.assertTrue(find_gbp_endpoint(self,
+                                              vx_tun_l2_1.sw_if_index,
+                                              mac=l['mac']))
+
+        #
+        # L3 Endpoint Learning
+        #  - configured on the bridge's BVI
+        #
+
+        #
+        # clean up
+        #
+        self.sleep(2)
+        for l in learnt:
+            self.assertFalse(find_gbp_endpoint(self,
+                                               vx_tun_l2_1.sw_if_index,
+                                               mac=l['mac']))
+
+        self.pg2.unconfig_ip4()
+        self.pg3.unconfig_ip4()
+        self.pg4.unconfig_ip4()
+
+        self.logger.info(self.vapi.cli("sh int"))
+        self.logger.info(self.vapi.cli("sh gbp vxlan"))
+
+    def test_gbp_learn_l3(self):
+        """ GBP L3 Endpoint Learning """
+
+        routed_dst_mac = "00:0c:0c:0c:0c:0c"
+        routed_src_mac = "00:22:bd:f8:19:ff"
+
+        learnt = [{'mac': '00:00:11:11:11:02',
+                   'ip': '10.0.1.2',
+                   'ip6': '2001:10::2'},
+                  {'mac': '00:00:11:11:11:03',
+                   'ip': '10.0.1.3',
+                   'ip6': '2001:10::3'}]
+
+        #
+        # lower the inactive threshold so these tests pass in a
+        # reasonable amount of time
+        #
+        self.vapi.gbp_endpoint_learn_set_inactive_threshold(1)
+
+        #
+        # IP tables
+        #
+        t4 = VppIpTable(self, 1)
+        t4.add_vpp_config()
+        t6 = VppIpTable(self, 1, True)
+        t6.add_vpp_config()
+
+        tun_ip4_uu = VppVxlanGbpTunnel(self, self.pg4.local_ip4,
+                                       self.pg4.remote_ip4, 114)
+        tun_ip6_uu = VppVxlanGbpTunnel(self, self.pg4.local_ip4,
+                                       self.pg4.remote_ip4, 116)
+        tun_ip4_uu.add_vpp_config()
+        tun_ip6_uu.add_vpp_config()
+
+        rd1 = VppGbpRouteDomain(self, 2, t4, t6, tun_ip4_uu, tun_ip6_uu)
+        rd1.add_vpp_config()
+
+        self.loop0.set_mac(self.router_mac.address)
+
+        #
+        # Bind the BVI to the RD
+        #
+        VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config()
+        VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config()
+
+        #
+        # Pg2 hosts the vxlan tunnel
+        # hosts on pg2 to act as TEPs
+        # pg3 is BD uu-fwd
+        # pg4 is RD uu-fwd
+        #
+        self.pg2.config_ip4()
+        self.pg2.resolve_arp()
+        self.pg2.generate_remote_hosts(4)
+        self.pg2.configure_ipv4_neighbors()
+        self.pg3.config_ip4()
+        self.pg3.resolve_arp()
+        self.pg4.config_ip4()
+        self.pg4.resolve_arp()
+
+        #
+        # a GBP bridge domain with a BVI and a UU-flood interface
+        #
+        bd1 = VppBridgeDomain(self, 1)
+        bd1.add_vpp_config()
+        gbd1 = VppGbpBridgeDomain(self, bd1, self.loop0, self.pg3)
+        gbd1.add_vpp_config()
+
+        self.logger.info(self.vapi.cli("sh bridge 1 detail"))
+        self.logger.info(self.vapi.cli("sh gbp bridge"))
+        self.logger.info(self.vapi.cli("sh gbp route"))
+        self.logger.info(self.vapi.cli("show l2fib all"))
+
+        # ... and has a /32 and /128 applied
+        ip4_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 32)
+        ip4_addr.add_vpp_config()
+        ip6_addr = VppIpInterfaceAddress(self, gbd1.bvi, "2001:10::128", 128)
+        ip6_addr.add_vpp_config()
+
+        #
+        # The Endpoint-group in which we are learning endpoints
+        #
+        epg_220 = VppGbpEndpointGroup(self, 220, rd1, gbd1,
+                                      None, self.loop0,
+                                      "10.0.0.128",
+                                      "2001:10::128")
+        epg_220.add_vpp_config()
+
+        #
+        # The VXLAN GBP tunnel is a bridge-port and has L2 endpoint
+        # leanring enabled
+        #
+        vx_tun_l3 = VppGbpVxlanTunnel(
+            self, 101, rd1.rd_id,
+            VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3)
+        vx_tun_l3.add_vpp_config()
+
+        #
+        # A static endpoint that the learnt endpoints are trying to
+        # talk to
+        #
+        ep = VppGbpEndpoint(self, self.pg0,
+                            epg_220, None,
+                            "10.0.0.127", "11.0.0.127",
+                            "2001:10::1", "3001::1")
+        ep.add_vpp_config()
+
+        #
+        # learn some remote IPv4 EPs
+        #
+        for ii, l in enumerate(learnt):
+            # a packet with an sclass from a knwon EPG
+            # arriving on an unknown TEP
+            p = (Ether(src=self.pg2.remote_mac,
+                       dst=self.pg2.local_mac) /
+                 IP(src=self.pg2.remote_hosts[1].ip4,
+                    dst=self.pg2.local_ip4) /
+                 UDP(sport=1234, dport=48879) /
+                 VXLAN(vni=101, gpid=220, flags=0x88) /
+                 Ether(src=l['mac'], dst="00:00:00:11:11:11") /
+                 IP(src=l['ip'], dst=ep.ip4.address) /
+                 UDP(sport=1234, dport=1234) /
+                 Raw('\xa5' * 100))
+
+            rx = self.send_and_expect(self.pg2, [p], self.pg0)
+
+            # the new TEP
+            tep1_sw_if_index = find_vxlan_gbp_tunnel(
+                self,
+                self.pg2.local_ip4,
+                self.pg2.remote_hosts[1].ip4,
+                vx_tun_l3.vni)
+            self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
+
+            # endpoint learnt via the parent GBP-vxlan interface
+            self.assertTrue(find_gbp_endpoint(self,
+                                              vx_tun_l3._sw_if_index,
+                                              ip=l['ip']))
+
+        #
+        # Static IPv4 EP replies to learnt
+        #
+        for l in learnt:
+            p = (Ether(src=ep.mac, dst=self.loop0.local_mac) /
+                 IP(dst=l['ip'], src=ep.ip4.address) /
+                 UDP(sport=1234, dport=1234) /
+                 Raw('\xa5' * 100))
+
+            rxs = self.send_and_expect(self.pg0, p*1, self.pg2)
+
+            for rx in rxs:
+                self.assertEqual(rx[IP].src, self.pg2.local_ip4)
+                self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4)
+                self.assertEqual(rx[UDP].dport, 48879)
+                # the UDP source port is a random value for hashing
+                self.assertEqual(rx[VXLAN].gpid, 220)
+                self.assertEqual(rx[VXLAN].vni, 101)
+                self.assertTrue(rx[VXLAN].flags.G)
+                self.assertTrue(rx[VXLAN].flags.Instance)
+                self.assertTrue(rx[VXLAN].gpflags.A)
+                self.assertFalse(rx[VXLAN].gpflags.D)
+
+                inner = rx[VXLAN].payload
+
+                self.assertEqual(inner[Ether].src, routed_src_mac)
+                self.assertEqual(inner[Ether].dst, routed_dst_mac)
+                self.assertEqual(inner[IP].src, ep.ip4.address)
+                self.assertEqual(inner[IP].dst, l['ip'])
+
+        self.sleep(2)
+        for l in learnt:
+            self.assertFalse(find_gbp_endpoint(self,
+                                               tep1_sw_if_index,
+                                               ip=l['ip']))
+
+        #
+        # learn some remote IPv6 EPs
+        #
+        for ii, l in enumerate(learnt):
+            # a packet with an sclass from a knwon EPG
+            # arriving on an unknown TEP
+            p = (Ether(src=self.pg2.remote_mac,
+                       dst=self.pg2.local_mac) /
+                 IP(src=self.pg2.remote_hosts[1].ip4,
+                    dst=self.pg2.local_ip4) /
+                 UDP(sport=1234, dport=48879) /
+                 VXLAN(vni=101, gpid=220, flags=0x88) /
+                 Ether(src=l['mac'], dst="00:00:00:11:11:11") /
+                 IPv6(src=l['ip6'], dst=ep.ip6.address) /
+                 UDP(sport=1234, dport=1234) /
+                 Raw('\xa5' * 100))
+
+            rx = self.send_and_expect(self.pg2, [p], self.pg0)
+
+            # the new TEP
+            tep1_sw_if_index = find_vxlan_gbp_tunnel(
+                self,
+                self.pg2.local_ip4,
+                self.pg2.remote_hosts[1].ip4,
+                vx_tun_l3.vni)
+            self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
+
+            self.logger.info(self.vapi.cli("show gbp bridge"))
+            self.logger.info(self.vapi.cli("show vxlan-gbp tunnel"))
+            self.logger.info(self.vapi.cli("show gbp vxlan"))
+            self.logger.info(self.vapi.cli("show int addr"))
+
+            # endpoint learnt via the TEP
+            self.assertTrue(find_gbp_endpoint(self, ip=l['ip6']))
+
+        self.logger.info(self.vapi.cli("show gbp endpoint"))
+        self.logger.info(self.vapi.cli("show ip fib index 1 %s" % l['ip']))
+
+        #
+        # Static EP replies to learnt
+        #
+        for l in learnt:
+            p = (Ether(src=ep.mac, dst=self.loop0.local_mac) /
+                 IPv6(dst=l['ip6'], src=ep.ip6.address) /
+                 UDP(sport=1234, dport=1234) /
+                 Raw('\xa5' * 100))
+
+            rxs = self.send_and_expect(self.pg0, p*65, self.pg2)
+
+            for rx in rxs:
+                self.assertEqual(rx[IP].src, self.pg2.local_ip4)
+                self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4)
+                self.assertEqual(rx[UDP].dport, 48879)
+                # the UDP source port is a random value for hashing
+                self.assertEqual(rx[VXLAN].gpid, 220)
+                self.assertEqual(rx[VXLAN].vni, 101)
+                self.assertTrue(rx[VXLAN].flags.G)
+                self.assertTrue(rx[VXLAN].flags.Instance)
+                self.assertTrue(rx[VXLAN].gpflags.A)
+                self.assertFalse(rx[VXLAN].gpflags.D)
+
+                inner = rx[VXLAN].payload
+
+                self.assertEqual(inner[Ether].src, routed_src_mac)
+                self.assertEqual(inner[Ether].dst, routed_dst_mac)
+                self.assertEqual(inner[IPv6].src, ep.ip6.address)
+                self.assertEqual(inner[IPv6].dst, l['ip6'])
+
+        self.logger.info(self.vapi.cli("sh gbp endpoint"))
+        self.sleep(2)
+        for l in learnt:
+            self.assertFalse(find_gbp_endpoint(self,
+                                               tep1_sw_if_index,
+                                               ip=l['ip']))
+
+        #
+        # Static sends to unknown EP with no route
+        #
+        p = (Ether(src=ep.mac, dst=self.loop0.local_mac) /
+             IP(dst="10.0.0.99", src=ep.ip4.address) /
+             UDP(sport=1234, dport=1234) /
+             Raw('\xa5' * 100))
+
+        self.send_and_assert_no_replies(self.pg0, [p])
+
+        #
+        # Add a route to static EP's v4 and v6 subnet
+        #  packets should be send on the v4/v6 uu=fwd interface resp.
+        #
+        se_10_24 = VppGbpSubnet(
+            self, rd1, "10.0.0.0", 24,
+            VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_TRANSPORT)
+        se_10_24.add_vpp_config()
+
+        p = (Ether(src=ep.mac, dst=self.loop0.local_mac) /
+             IP(dst="10.0.0.99", src=ep.ip4.address) /
+             UDP(sport=1234, dport=1234) /
+             Raw('\xa5' * 100))
+
+        rxs = self.send_and_expect(self.pg0, [p], self.pg4)
+        for rx in rxs:
+            self.assertEqual(rx[IP].src, self.pg4.local_ip4)
+            self.assertEqual(rx[IP].dst, self.pg4.remote_ip4)
+            self.assertEqual(rx[UDP].dport, 48879)
+            # the UDP source port is a random value for hashing
+            self.assertEqual(rx[VXLAN].gpid, 220)
+            self.assertEqual(rx[VXLAN].vni, 114)
+            self.assertTrue(rx[VXLAN].flags.G)
+            self.assertTrue(rx[VXLAN].flags.Instance)
+            # policy is not applied to packets sent to the uu-fwd interfaces
+            self.assertFalse(rx[VXLAN].gpflags.A)
+            self.assertFalse(rx[VXLAN].gpflags.D)
+
+        #
+        # learn some remote IPv4 EPs
+        #
+        for ii, l in enumerate(learnt):
+            # a packet with an sclass from a knwon EPG
+            # arriving on an unknown TEP
+            p = (Ether(src=self.pg2.remote_mac,
+                       dst=self.pg2.local_mac) /
+                 IP(src=self.pg2.remote_hosts[1].ip4,
+                    dst=self.pg2.local_ip4) /
+                 UDP(sport=1234, dport=48879) /
+                 VXLAN(vni=101, gpid=220, flags=0x88) /
+                 Ether(src=l['mac'], dst="00:00:00:11:11:11") /
+                 IP(src=l['ip'], dst=ep.ip4.address) /
+                 UDP(sport=1234, dport=1234) /
+                 Raw('\xa5' * 100))
+
+            rx = self.send_and_expect(self.pg2, [p], self.pg0)
+
+            # the new TEP
+            tep1_sw_if_index = find_vxlan_gbp_tunnel(
+                self,
+                self.pg2.local_ip4,
+                self.pg2.remote_hosts[1].ip4,
+                vx_tun_l3.vni)
+            self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
+
+            # endpoint learnt via the parent GBP-vxlan interface
+            self.assertTrue(find_gbp_endpoint(self,
+                                              vx_tun_l3._sw_if_index,
+                                              ip=l['ip']))
+
+        #
+        # Add a remote endpoint from the API
+        #
+        rep_88 = VppGbpEndpoint(self, vx_tun_l3,
+                                epg_220, None,
+                                "10.0.0.88", "11.0.0.88",
+                                "2001:10::88", "3001::88",
+                                VppEnum.vl_api_gbp_endpoint_flags_t.REMOTE,
+                                self.pg2.local_ip4,
+                                self.pg2.remote_hosts[1].ip4,
+                                mac=None)
+        rep_88.add_vpp_config()
+
+        #
+        # Add a remote endpoint from the API that matches an existing one
+        #
+        rep_2 = VppGbpEndpoint(self, vx_tun_l3,
+                               epg_220, None,
+                               learnt[0]['ip'], "11.0.0.101",
+                               learnt[0]['ip6'], "3001::101",
+                               VppEnum.vl_api_gbp_endpoint_flags_t.REMOTE,
+                               self.pg2.local_ip4,
+                               self.pg2.remote_hosts[1].ip4,
+                               mac=None)
+        rep_2.add_vpp_config()
+
+        #
+        # Add a route to the leanred EP's v4 subnet
+        #  packets should be send on the v4/v6 uu=fwd interface resp.
+        #
+        se_10_1_24 = VppGbpSubnet(
+            self, rd1, "10.0.1.0", 24,
+            VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_TRANSPORT)
+        se_10_1_24.add_vpp_config()
+
+        self.logger.info(self.vapi.cli("show gbp endpoint"))
+
+        ips = ["10.0.0.88", learnt[0]['ip']]
+        for ip in ips:
+            p = (Ether(src=ep.mac, dst=self.loop0.local_mac) /
+                 IP(dst=ip, src=ep.ip4.address) /
+                 UDP(sport=1234, dport=1234) /
+                 Raw('\xa5' * 100))
+
+            rxs = self.send_and_expect(self.pg0, p*65, self.pg2)
+
+            for rx in rxs:
+                self.assertEqual(rx[IP].src, self.pg2.local_ip4)
+                self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4)
+                self.assertEqual(rx[UDP].dport, 48879)
+                # the UDP source port is a random value for hashing
+                self.assertEqual(rx[VXLAN].gpid, 220)
+                self.assertEqual(rx[VXLAN].vni, 101)
+                self.assertTrue(rx[VXLAN].flags.G)
+                self.assertTrue(rx[VXLAN].flags.Instance)
+                self.assertTrue(rx[VXLAN].gpflags.A)
+                self.assertFalse(rx[VXLAN].gpflags.D)
+
+                inner = rx[VXLAN].payload
+
+                self.assertEqual(inner[Ether].src, routed_src_mac)
+                self.assertEqual(inner[Ether].dst, routed_dst_mac)
+                self.assertEqual(inner[IP].src, ep.ip4.address)
+                self.assertEqual(inner[IP].dst, ip)
+
+        #
+        # remove the API remote EPs, they are now UU-fwd
+        #
+        rep_88.remove_vpp_config()
+        rep_2.remove_vpp_config()
+
+        self.logger.info(self.vapi.cli("show gbp endpoint"))
+
+        for ip in ips:
+            self.assertFalse(find_gbp_endpoint(self, ip=ip))
+
+            p = (Ether(src=ep.mac, dst=self.loop0.local_mac) /
+                 IP(dst=ip, src=ep.ip4.address) /
+                 UDP(sport=1234, dport=1234) /
+                 Raw('\xa5' * 100))
+
+            rxs = self.send_and_expect(self.pg0, [p], self.pg4)
+
+        #
+        # shutdown with learnt endpoint present
+        #
+        self.logger.info(self.vapi.cli("show gbp endpoint-group"))
+
+        #
+        # TODO
+        # remote endpoint becomes local
+        #
+        self.pg2.unconfig_ip4()
+        self.pg3.unconfig_ip4()
+        self.pg4.unconfig_ip4()
+
 
 if __name__ == '__main__':
     unittest.main(testRunner=VppTestRunner)
index 5a2694c..9f3ef53 100644 (file)
@@ -5,7 +5,7 @@ import socket
 
 from framework import VppTestCase, VppTestRunner
 from vpp_ip_route import VppIpRoute, VppRoutePath
-from vpp_papi_provider import L2_PORT_TYPE, BRIDGE_FLAGS
+from vpp_l2 import L2_PORT_TYPE, BRIDGE_FLAGS
 
 from scapy.packet import Raw
 from scapy.layers.l2 import Ether
index c7918fc..1fe6652 100644 (file)
@@ -454,3 +454,6 @@ class VppInterface(object):
                                "admin state")
         self.test.assert_equal(if_state.link_up_down, link_up_down,
                                "link state")
+
+    def __str__(self):
+        return self.name
index e92c919..6d22c16 100644 (file)
@@ -119,6 +119,10 @@ class VppIpAddress():
     def bytes(self):
         return self.addr.bytes
 
+    @property
+    def bytes(self):
+        return self.addr.bytes
+
     @property
     def address(self):
         return self.addr.address
@@ -168,10 +172,18 @@ class VppIpPrefix():
     def address(self):
         return self.addr.address
 
+    @property
+    def bytes(self):
+        return self.addr.bytes
+
     @property
     def length(self):
         return self.len
 
+    @property
+    def is_ip6(self):
+        return self.addr.is_ip6
+
     def __str__(self):
         return "%s/%d" % (self.address, self.length)
 
index 00a79f4..45609e8 100644 (file)
@@ -35,6 +35,13 @@ class MplsLspMode:
     UNIFORM = 1
 
 
+def ip_to_dpo_proto(addr):
+    if addr.version is 6:
+        return DpoProto.DPO_PROTO_IP6
+    else:
+        return DpoProto.DPO_PROTO_IP4
+
+
 def find_route(test, ip_addr, len, table_id=0, inet=AF_INET):
     if inet == AF_INET:
         s = 4
@@ -71,6 +78,23 @@ def find_mroute(test, grp_addr, src_addr, grp_addr_len,
     return False
 
 
+def fib_interface_ip_prefix(test, address, length, sw_if_index):
+    vp = VppIpPrefix(address, length)
+    addrs = test.vapi.ip_address_dump(sw_if_index, is_ipv6=vp.is_ip6)
+
+    if vp.is_ip6:
+        n = 16
+    else:
+        n = 4
+
+    for a in addrs:
+        if a.prefix_length == length and \
+           a.sw_if_index == sw_if_index and \
+           a.ip[:n] == vp.bytes:
+            return True
+    return False
+
+
 class VppIpTable(VppObject):
 
     def __init__(self,
@@ -95,6 +119,9 @@ class VppIpTable(VppObject):
             is_add=0)
 
     def query_vpp_config(self):
+        if self.table_id == 0:
+            # the default table always exists
+            return False
         # find the default route
         return find_route(self._test,
                           "::" if self.is_ip6 else "0.0.0.0",
@@ -111,6 +138,79 @@ class VppIpTable(VppObject):
                  self.table_id))
 
 
+class VppIpInterfaceAddress(VppObject):
+
+    def __init__(self, test, intf, addr, len):
+        self._test = test
+        self.intf = intf
+        self.prefix = VppIpPrefix(addr, len)
+
+    def add_vpp_config(self):
+        self._test.vapi.sw_interface_add_del_address(
+            self.intf.sw_if_index,
+            self.prefix.bytes,
+            self.prefix.length,
+            is_add=1,
+            is_ipv6=self.prefix.is_ip6)
+        self._test.registry.register(self, self._test.logger)
+
+    def remove_vpp_config(self):
+        self._test.vapi.sw_interface_add_del_address(
+            self.intf.sw_if_index,
+            self.prefix.bytes,
+            self.prefix.length,
+            is_add=0,
+            is_ipv6=self.prefix.is_ip6)
+
+    def query_vpp_config(self):
+        return fib_interface_ip_prefix(self._test,
+                                       self.prefix.address,
+                                       self.prefix.length,
+                                       self.intf.sw_if_index)
+
+    def __str__(self):
+        return self.object_id()
+
+    def object_id(self):
+        return "interface-ip-%s-%s" % (self.intf, self.prefix)
+
+
+class VppIpInterfaceBind(VppObject):
+
+    def __init__(self, test, intf, table):
+        self._test = test
+        self.intf = intf
+        self.table = table
+
+    def add_vpp_config(self):
+        if self.table.is_ip6:
+            self.intf.set_table_ip6(self.table.table_id)
+        else:
+            self.intf.set_table_ip4(self.table.table_id)
+        self._test.registry.register(self, self._test.logger)
+
+    def remove_vpp_config(self):
+        if 0 == self.table.table_id:
+            return
+        if self.table.is_ip6:
+            self.intf.set_table_ip6(0)
+        else:
+            self.intf.set_table_ip4(0)
+
+    def query_vpp_config(self):
+        if 0 == self.table.table_id:
+            return False
+        return self._test.vapi.sw_interface_get_table(
+            self.intf.sw_if_index,
+            self.table.is_ip6).vrf_id == self.table.table_id
+
+    def __str__(self):
+        return self.object_id()
+
+    def object_id(self):
+        return "interface-bind-%s-%s" % (self.intf, self.table)
+
+
 class VppMplsLabel(object):
     def __init__(self, value, mode=MplsLspMode.PIPE, ttl=64, exp=0):
         self.value = value
diff --git a/test/vpp_l2.py b/test/vpp_l2.py
new file mode 100644 (file)
index 0000000..a6b43ef
--- /dev/null
@@ -0,0 +1,221 @@
+"""
+  L2/BD Types
+
+"""
+
+from vpp_object import *
+from util import mactobinary
+from vpp_ip import VppIpAddress
+from vpp_mac import VppMacAddress
+from vpp_lo_interface import VppLoInterface
+
+
+class L2_PORT_TYPE:
+    NORMAL = 0
+    BVI = 1
+    UU_FWD = 2
+
+
+class BRIDGE_FLAGS:
+    NONE = 0
+    LEARN = 1
+    FWD = 2
+    FLOOD = 4
+    UU_FLOOD = 8
+    ARP_TERM = 16
+
+
+def find_bridge_domain(test, bd_id):
+    bds = test.vapi.bridge_domain_dump(bd_id)
+    return len(bds) == 1
+
+
+def find_bridge_domain_port(test, bd_id, sw_if_index):
+    bds = test.vapi.bridge_domain_dump(bd_id)
+    for bd in bds:
+        for p in bd.sw_if_details:
+            if p.sw_if_index == sw_if_index:
+                return True
+    return False
+
+
+def find_bridge_domain_arp_entry(test, bd_id, mac, ip):
+    vmac = VppMacAddress(mac)
+    vip = VppIpAddress(ip)
+
+    if vip.version == 4:
+        n = 4
+    else:
+        n = 16
+
+    arps = test.vapi.bd_ip_mac_dump(bd_id)
+    for arp in arps:
+        # do IP addr comparison too once .api is fixed...
+        if vmac.bytes == arp.mac_address and \
+           vip.bytes == arp.ip_address[:n]:
+            return True
+    return False
+
+
+def find_l2_fib_entry(test, bd_id, mac, sw_if_index):
+    vmac = VppMacAddress(mac)
+    lfs = test.vapi.l2_fib_table_dump(bd_id)
+    for lf in lfs:
+        if vmac.bytes == lf.mac and sw_if_index == lf.sw_if_index:
+            return True
+    return False
+
+
+class VppBridgeDomain(VppObject):
+
+    def __init__(self, test, bd_id,
+                 flood=1, uu_flood=1, forward=1,
+                 learn=1, arp_term=1):
+        self._test = test
+        self.bd_id = bd_id
+        self.flood = flood
+        self.uu_flood = uu_flood
+        self.forward = forward
+        self.learn = learn
+        self.arp_term = arp_term
+
+    def add_vpp_config(self):
+        self._test.vapi.bridge_domain_add_del(
+            self.bd_id,
+            is_add=1,
+            flood=self.flood,
+            uu_flood=self.uu_flood,
+            forward=self.forward,
+            learn=self.learn,
+            arp_term=self.arp_term)
+        self._test.registry.register(self, self._test.logger)
+
+    def remove_vpp_config(self):
+        self._test.vapi.bridge_domain_add_del(self.bd_id, is_add=0)
+
+    def query_vpp_config(self):
+        return find_bridge_domain(self._test, self.bd_id)
+
+    def __str__(self):
+        return self.object_id()
+
+    def object_id(self):
+        return "bridge-domain-%d" % (self.bd_id)
+
+
+class VppBridgeDomainPort(VppObject):
+
+    def __init__(self, test, bd, itf,
+                 port_type=L2_PORT_TYPE.NORMAL):
+        self._test = test
+        self.bd = bd
+        self.itf = itf
+        self.port_type = port_type
+
+    def add_vpp_config(self):
+        self._test.vapi.sw_interface_set_l2_bridge(
+            self.itf.sw_if_index,
+            self.bd.bd_id,
+            port_type=self.port_type,
+            enable=1)
+        self._test.registry.register(self, self._test.logger)
+
+    def remove_vpp_config(self):
+        self._test.vapi.sw_interface_set_l2_bridge(
+            self.itf.sw_if_index,
+            self.bd.bd_id,
+            port_type=self.port_type,
+            enable=0)
+
+    def query_vpp_config(self):
+        return find_bridge_domain_port(self._test,
+                                       self.bd.bd_id,
+                                       self.itf.sw_if_index)
+
+    def __str__(self):
+        return self.object_id()
+
+    def object_id(self):
+        return "BD-Port-%s-%s" % (self.bd, self.itf)
+
+
+class VppBridgeDomainArpEntry(VppObject):
+
+    def __init__(self, test, bd, mac, ip):
+        self._test = test
+        self.bd = bd
+        self.mac = VppMacAddress(mac)
+        self.ip = VppIpAddress(ip)
+
+    def add_vpp_config(self):
+        self._test.vapi.bd_ip_mac_add_del(
+            self.bd.bd_id,
+            self.mac.encode(),
+            self.ip.encode(),
+            is_add=1)
+        self._test.registry.register(self, self._test.logger)
+
+    def remove_vpp_config(self):
+        self._test.vapi.bd_ip_mac_add_del(
+            self.bd.bd_id,
+            self.mac.encode(),
+            self.ip.encode(),
+            is_add=0)
+
+    def query_vpp_config(self):
+        return find_bridge_domain_arp_entry(self._test,
+                                            self.bd.bd_id,
+                                            self.mac.address,
+                                            self.ip.address)
+
+    def __str__(self):
+        return self.object_id()
+
+    def object_id(self):
+        return "BD-Arp-Entry-%s-%s-%s" % (self.bd, self.mac, self.ip.address)
+
+
+class VppL2FibEntry(VppObject):
+
+    def __init__(self, test, bd, mac, itf,
+                 static_mac=0, filter_mac=0, bvi_mac=-1):
+        self._test = test
+        self.bd = bd
+        self.mac = VppMacAddress(mac)
+        self.itf = itf
+        self.static_mac = static_mac
+        self.filter_mac = filter_mac
+        if bvi_mac == -1:
+            self.bvi_mac = isinstance(self.itf, VppLoInterface)
+        else:
+            self.bvi_mac = bvi_mac
+
+    def add_vpp_config(self):
+        self._test.vapi.l2fib_add_del(
+            self.mac.address,
+            self.bd.bd_id,
+            self.itf.sw_if_index,
+            is_add=1,
+            static_mac=self.static_mac,
+            filter_mac=self.filter_mac,
+            bvi_mac=self.bvi_mac)
+        self._test.registry.register(self, self._test.logger)
+
+    def remove_vpp_config(self):
+        self._test.vapi.l2fib_add_del(
+            self.mac.address,
+            self.bd.bd_id,
+            self.itf.sw_if_index,
+            is_add=0)
+
+    def query_vpp_config(self):
+        return find_l2_fib_entry(self._test,
+                                 self.bd.bd_id,
+                                 self.mac.address,
+                                 self.itf.sw_if_index)
+
+    def __str__(self):
+        return self.object_id()
+
+    def object_id(self):
+        return "L2-Fib-Entry-%s-%s-%s" % (self.bd, self.mac, self.itf)
index c9ee11e..c0e6974 100644 (file)
@@ -22,3 +22,18 @@ class VppMacAddress():
     @property
     def address(self):
         return self.addr.address
+
+    def __str__(self):
+        return self.address
+
+    def __eq__(self, other):
+        if isinstance(other, self.__class__):
+            return self.address == other.addres
+        elif hasattr(other, "bytes"):
+            # vl_api_mac_addres_t
+            return self.bytes == other.bytes
+        else:
+            raise Exception("Comparing VppMacAddress:%s"
+                            "with unknown type: %s" %
+                            (self, other))
+        return False
index c219247..1adcc1b 100644 (file)
@@ -18,6 +18,7 @@ except:
 
 if do_import:
     from vpp_papi import VPP
+    from vpp_l2 import L2_PORT_TYPE
 
 # from vnet/vnet/mpls/mpls_types.h
 MPLS_IETF_MAX_LABEL = 0xfffff
@@ -43,21 +44,6 @@ class QOS_SOURCE:
     IP = 3
 
 
-class L2_PORT_TYPE:
-    NORMAL = 0
-    BVI = 1
-    UU_FWD = 2
-
-
-class BRIDGE_FLAGS:
-    NONE = 0
-    LEARN = 1
-    FWD = 2
-    FLOOD = 4
-    UU_FLOOD = 8
-    ARP_TERM = 16
-
-
 class UnexpectedApiReturnValueError(Exception):
     """ exception raised when the API return value is unexpected """
     pass
@@ -257,6 +243,17 @@ class VppPapiProvider(object):
                         {'sw_if_index': sw_if_index, 'is_ipv6': is_ipv6,
                          'vrf_id': table_id})
 
+    def sw_interface_get_table(self, sw_if_index, is_ipv6):
+        """ Get the IPvX Table-id for the Interface
+
+        :param sw_if_index:
+        :param is_ipv6:
+        :return table_id
+
+        """
+        return self.api(self.papi.sw_interface_get_table,
+                        {'sw_if_index': sw_if_index, 'is_ipv6': is_ipv6})
+
     def sw_interface_add_del_address(self, sw_if_index, addr, addr_len,
                                      is_ipv6=0, is_add=1, del_all=0):
         """
@@ -277,6 +274,11 @@ class VppPapiProvider(object):
                          'address_length': addr_len,
                          'address': addr})
 
+    def ip_address_dump(self, sw_if_index, is_ipv6=0):
+        return self.api(self.papi.ip_address_dump,
+                        {'sw_if_index': sw_if_index,
+                         'is_ipv6': is_ipv6})
+
     def sw_interface_set_unnumbered(self, sw_if_index, ip_sw_if_index,
                                     is_add=1):
         """ Set the Interface to be unnumbered
@@ -477,10 +479,13 @@ class VppPapiProvider(object):
         return self.api(self.papi.bd_ip_mac_add_del,
                         {'bd_id': bd_id,
                          'is_add': is_add,
-                         'is_ipv6': is_ipv6,
                          'ip': ip,
                          'mac': mac})
 
+    def bd_ip_mac_dump(self, bd_id):
+        return self.api(self.papi.bd_ip_mac_dump,
+                        {'bd_id': bd_id})
+
     def want_ip4_arp_events(self, enable_disable=1, address=0):
         return self.api(self.papi.want_ip4_arp_events,
                         {'enable_disable': enable_disable,
@@ -647,6 +652,11 @@ class VppPapiProvider(object):
         """
         return self.api(self.papi.l2fib_flush_all, {})
 
+    def l2_fib_table_dump(self, bd_id):
+        """ Dump the L2 FIB """
+        return self.api(self.papi.l2_fib_table_dump,
+                        {'bd_id': bd_id})
+
     def sw_interface_set_l2_bridge(self, sw_if_index, bd_id,
                                    shg=0, port_type=L2_PORT_TYPE.NORMAL,
                                    enable=1):
@@ -2771,7 +2781,6 @@ class VppPapiProvider(object):
             is_add=1,
             is_ipv6=0,
             encap_table_id=0,
-            decap_next_index=0xFFFFFFFF,
             vni=0,
             instance=0xFFFFFFFF):
         """
@@ -2794,7 +2803,6 @@ class VppPapiProvider(object):
                              'dst': dst,
                              'mcast_sw_if_index': mcast_sw_if_index,
                              'encap_table_id': encap_table_id,
-                             'decap_next_index': decap_next_index,
                              'vni': vni,
                              'instance': instance}})
 
@@ -3482,15 +3490,22 @@ class VppPapiProvider(object):
                          'enable_ip6': 1 if enable_ip6 else 0,
                          })
 
-    def gbp_endpoint_add(self, sw_if_index, ips, mac, epg):
+    def gbp_endpoint_add(self, sw_if_index, ips, mac, epg, flags,
+                         tun_src, tun_dst):
         """ GBP endpoint Add """
         return self.api(self.papi.gbp_endpoint_add,
                         {'endpoint': {
                             'sw_if_index': sw_if_index,
+                            'flags': 0,
                             'ips': ips,
                             'n_ips': len(ips),
                             'mac': mac,
-                            'epg_id': epg}})
+                            'epg_id': epg,
+                            'flags': flags,
+                            'tun': {
+                                'src': tun_src,
+                                'dst': tun_dst,
+                            }}})
 
     def gbp_endpoint_del(self, handle):
         """ GBP endpoint Del """
@@ -3501,24 +3516,73 @@ class VppPapiProvider(object):
         """ GBP endpoint Dump """
         return self.api(self.papi.gbp_endpoint_dump, {})
 
-    def gbp_endpoint_group_add_del(self, is_add, epg, bd,
-                                   ip4_rd,
-                                   ip6_rd,
-                                   uplink_sw_if_index):
-        """ GBP endpoint group Add/Del """
-        return self.api(self.papi.gbp_endpoint_group_add_del,
-                        {'is_add': is_add,
-                         'epg': {
+    def gbp_endpoint_group_add(self, epg, bd,
+                               rd, uplink_sw_if_index):
+        """ GBP endpoint group Add """
+        return self.api(self.papi.gbp_endpoint_group_add,
+                        {'epg':
+                         {
                              'uplink_sw_if_index': uplink_sw_if_index,
                              'bd_id': bd,
-                             'ip4_table_id': ip4_rd,
-                             'ip6_table_id': ip6_rd,
-                             'epg_id': epg}})
+                             'rd_id': rd,
+                             'epg_id': epg
+                         }})
+
+    def gbp_endpoint_group_del(self, epg):
+        """ GBP endpoint group Del """
+        return self.api(self.papi.gbp_endpoint_group_del,
+                        {'epg_id': epg})
 
     def gbp_endpoint_group_dump(self):
         """ GBP endpoint group Dump """
         return self.api(self.papi.gbp_endpoint_group_dump, {})
 
+    def gbp_bridge_domain_add(self, bd_id,
+                              bvi_sw_if_index,
+                              uu_fwd_sw_if_index):
+        """ GBP bridge-domain Add """
+        return self.api(self.papi.gbp_bridge_domain_add,
+                        {'bd':
+                         {
+                             'bvi_sw_if_index': bvi_sw_if_index,
+                             'uu_fwd_sw_if_index': uu_fwd_sw_if_index,
+                             'bd_id': bd_id
+                         }})
+
+    def gbp_bridge_domain_del(self, bd_id):
+        """ GBP bridge-domain Del """
+        return self.api(self.papi.gbp_bridge_domain_del,
+                        {'bd_id': bd_id})
+
+    def gbp_bridge_domain_dump(self):
+        """ GBP Bridge Domain Dump """
+        return self.api(self.papi.gbp_bridge_domain_dump, {})
+
+    def gbp_route_domain_add(self, rd_id,
+                             ip4_table_id,
+                             ip6_table_id,
+                             ip4_uu_sw_if_index,
+                             ip6_uu_sw_if_index):
+        """ GBP route-domain Add """
+        return self.api(self.papi.gbp_route_domain_add,
+                        {'rd':
+                         {
+                             'ip4_table_id': ip4_table_id,
+                             'ip6_table_id': ip6_table_id,
+                             'ip4_uu_sw_if_index': ip4_uu_sw_if_index,
+                             'ip6_uu_sw_if_index': ip6_uu_sw_if_index,
+                             'rd_id': rd_id
+                         }})
+
+    def gbp_route_domain_del(self, rd_id):
+        """ GBP route-domain Del """
+        return self.api(self.papi.gbp_route_domain_del,
+                        {'rd_id': rd_id})
+
+    def gbp_route_domain_dump(self):
+        """ GBP Route Domain Dump """
+        return self.api(self.papi.gbp_route_domain_dump, {})
+
     def gbp_recirc_add_del(self, is_add, sw_if_index, epg, is_ext):
         """ GBP recirc Add/Del """
         return self.api(self.papi.gbp_recirc_add_del,
@@ -3532,22 +3596,19 @@ class VppPapiProvider(object):
         """ GBP recirc Dump """
         return self.api(self.papi.gbp_recirc_dump, {})
 
-    def gbp_subnet_add_del(self, is_add, table_id,
-                           is_internal,
-                           prefix,
+    def gbp_subnet_add_del(self, is_add, rd_id,
+                           prefix, type,
                            sw_if_index=0xffffffff,
-                           epg_id=0xffff,
-                           is_ip6=False):
+                           epg_id=0xffff):
         """ GBP Subnet Add/Del """
         return self.api(self.papi.gbp_subnet_add_del,
                         {'is_add': is_add,
                          'subnet': {
-                             'is_internal': is_internal,
-                             'is_ip6': is_ip6,
+                             'type': type,
                              'sw_if_index': sw_if_index,
                              'epg_id': epg_id,
                              'prefix': prefix,
-                             'table_id': table_id}})
+                             'rd_id': rd_id}})
 
     def gbp_subnet_dump(self):
         """ GBP Subnet Dump """
@@ -3566,6 +3627,33 @@ class VppPapiProvider(object):
         """ GBP contract Dump """
         return self.api(self.papi.gbp_contract_dump, {})
 
+    def gbp_endpoint_learn_set_inactive_threshold(self, threshold):
+        """ GBP set inactive threshold """
+        return self.api(self.papi.gbp_endpoint_learn_set_inactive_threshold,
+                        {'threshold': threshold})
+
+    def gbp_vxlan_tunnel_add(self, vni, bd_rd_id, mode):
+        """ GBP VXLAN tunnel add """
+        return self.api(self.papi.gbp_vxlan_tunnel_add,
+                        {
+                            'tunnel': {
+                                'vni': vni,
+                                'mode': mode,
+                                'bd_rd_id': bd_rd_id
+                            }
+                        })
+
+    def gbp_vxlan_tunnel_del(self, vni):
+        """ GBP VXLAN tunnel del """
+        return self.api(self.papi.gbp_vxlan_tunnel_del,
+                        {
+                            'vni': vni,
+                        })
+
+    def gbp_vxlan_tunnel_dump(self):
+        """ GBP VXLAN tunnel add/del """
+        return self.api(self.papi.gbp_vxlan_tunnel_dump, {})
+
     def ipip_6rd_add_tunnel(self, ip6_table_id, ip6_prefix, ip6_prefix_len,
                             ip4_table_id, ip4_prefix, ip4_prefix_len, ip4_src,
                             security_check):
diff --git a/test/vpp_vxlan_gbp_tunnel.py b/test/vpp_vxlan_gbp_tunnel.py
new file mode 100644 (file)
index 0000000..805d4c5
--- /dev/null
@@ -0,0 +1,69 @@
+
+from vpp_interface import VppInterface
+from vpp_ip import VppIpAddress
+
+
+INDEX_INVALID = 0xffffffff
+
+
+def find_vxlan_gbp_tunnel(test, src, dst, vni):
+    vsrc = VppIpAddress(src)
+    vdst = VppIpAddress(dst)
+
+    ts = test.vapi.vxlan_gbp_tunnel_dump(INDEX_INVALID)
+    for t in ts:
+        if vsrc == t.tunnel.src and \
+           vdst == t.tunnel.dst and \
+           t.tunnel.vni == vni:
+            return t.tunnel.sw_if_index
+    return INDEX_INVALID
+
+
+class VppVxlanGbpTunnel(VppInterface):
+    """
+    VPP VXLAN GBP interface
+    """
+
+    def __init__(self, test, src, dst, vni, mcast_itf=None):
+        """ Create VXLAN-GBP Tunnel interface """
+        super(VppVxlanGbpTunnel, self).__init__(test)
+        self.src = VppIpAddress(src)
+        self.dst = VppIpAddress(dst)
+        self.vni = vni
+        self.mcast_itf = mcast_itf
+
+    def add_vpp_config(self):
+        mcast_sw_if_index = INDEX_INVALID
+        if (self.mcast_itf):
+            mcast_sw_if_index = self.mcast_itf.sw_if_index
+        reply = self.test.vapi.vxlan_gbp_tunnel_add_del(
+            self.src.encode(),
+            self.dst.encode(),
+            vni=self.vni,
+            mcast_sw_if_index=mcast_sw_if_index)
+        self.set_sw_if_index(reply.sw_if_index)
+        self._test.registry.register(self, self._test.logger)
+
+    def remove_vpp_config(self):
+        mcast_sw_if_index = INDEX_INVALID
+        if (self.mcast_itf):
+            mcast_sw_if_index = self.mcast_itf.sw_if_index
+        self.test.vapi.vxlan_gbp_tunnel_add_del(
+            self.src.encode(),
+            self.dst.encode(),
+            vni=self.vni,
+            is_add=0,
+            mcast_sw_if_index=mcast_sw_if_index)
+
+    def query_vpp_config(self):
+        return (INDEX_INVALID != find_vxlan_gbp_tunnel(self._test,
+                                                       self.src.address,
+                                                       self.dst.address,
+                                                       self.vni))
+
+    def __str__(self):
+        return self.object_id()
+
+    def object_id(self):
+        return "vxlan-gbp-%d-%d-%s-%s" % (self.sw_if_index, self.vni,
+                                          self.src, self.dst)