GBP: l3-out subnets 15/16215/7
authorNeale Ranns <nranns@cisco.com>
Wed, 21 Nov 2018 13:44:35 +0000 (05:44 -0800)
committerNeale Ranns <nranns@cisco.com>
Thu, 29 Nov 2018 12:36:30 +0000 (12:36 +0000)
Change-Id: Id4a20066fc5be716c61a497dfcb4d00dc1dbb28d
Signed-off-by: Neale Ranns <nranns@cisco.com>
31 files changed:
extras/vom/vom/CMakeLists.txt
extras/vom/vom/gbp_ext_itf.cpp [new file with mode: 0644]
extras/vom/vom/gbp_ext_itf.hpp [new file with mode: 0644]
extras/vom/vom/gbp_ext_itf_cmds.cpp [new file with mode: 0644]
extras/vom/vom/gbp_ext_itf_cmds.hpp [new file with mode: 0644]
extras/vom/vom/gbp_subnet.cpp
extras/vom/vom/gbp_subnet.hpp
src/plugins/gbp/CMakeLists.txt
src/plugins/gbp/gbp.api
src/plugins/gbp/gbp_api.c
src/plugins/gbp/gbp_bridge_domain.c
src/plugins/gbp/gbp_bridge_domain.h
src/plugins/gbp/gbp_classify.c
src/plugins/gbp/gbp_endpoint.c
src/plugins/gbp/gbp_endpoint.h
src/plugins/gbp/gbp_ext_itf.c [new file with mode: 0644]
src/plugins/gbp/gbp_ext_itf.h [new file with mode: 0644]
src/plugins/gbp/gbp_itf.c
src/plugins/gbp/gbp_policy_dpo.c
src/plugins/gbp/gbp_policy_dpo.h
src/plugins/gbp/gbp_recirc.c
src/plugins/gbp/gbp_recirc.h
src/plugins/gbp/gbp_route_domain.c
src/plugins/gbp/gbp_route_domain.h
src/plugins/gbp/gbp_scanner.c
src/plugins/gbp/gbp_scanner.h
src/plugins/gbp/gbp_subnet.c
src/plugins/gbp/gbp_subnet.h
src/vnet/l2/l2_input.h
test/test_gbp.py
test/vpp_papi_provider.py

index 7413df8..f68a740 100644 (file)
@@ -76,6 +76,8 @@ if(GBP_FILE)
     gbp_endpoint.cpp
     gbp_endpoint_group_cmds.cpp
     gbp_endpoint_group.cpp
+    gbp_ext_itf.cpp
+    gbp_ext_itf_cmds.cpp
     gbp_recirc_cmds.cpp
     gbp_recirc.cpp
     gbp_route_domain_cmds.cpp
@@ -196,6 +198,7 @@ if(GBP_FILE)
     gbp_bridge_domain.hpp
     gbp_endpoint.hpp
     gbp_endpoint_group.hpp
+    gbp_ext_itf.hpp
     gbp_recirc.hpp
     gbp_route_domain.hpp
     gbp_rule.hpp
diff --git a/extras/vom/vom/gbp_ext_itf.cpp b/extras/vom/vom/gbp_ext_itf.cpp
new file mode 100644 (file)
index 0000000..4b7302d
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/gbp_ext_itf.hpp"
+#include "vom/gbp_ext_itf_cmds.hpp"
+#include "vom/singular_db_funcs.hpp"
+
+namespace VOM {
+
+singular_db<gbp_ext_itf::key_t, gbp_ext_itf> gbp_ext_itf::m_db;
+
+gbp_ext_itf::event_handler gbp_ext_itf::m_evh;
+
+gbp_ext_itf::gbp_ext_itf(const interface& itf,
+                         const gbp_bridge_domain& gbd,
+                         const gbp_route_domain& grd)
+  : m_hw(false)
+  , m_itf(itf.singular())
+  , m_bd(gbd.singular())
+  , m_rd(grd.singular())
+{
+}
+
+gbp_ext_itf::gbp_ext_itf(const gbp_ext_itf& gbpe)
+  : m_hw(gbpe.m_hw)
+  , m_itf(gbpe.m_itf)
+  , m_bd(gbpe.m_bd)
+  , m_rd(gbpe.m_rd)
+{
+}
+
+gbp_ext_itf::~gbp_ext_itf()
+{
+  sweep();
+  m_db.release(key(), this);
+}
+
+const gbp_ext_itf::key_t
+gbp_ext_itf::key() const
+{
+  return (m_itf->key());
+}
+
+const handle_t&
+gbp_ext_itf::handle() const
+{
+  return m_itf->handle();
+}
+
+bool
+gbp_ext_itf::operator==(const gbp_ext_itf& gei) const
+{
+  return ((key() == gei.key()) && (m_itf == gei.m_itf) && (m_rd == gei.m_rd) &&
+          (m_bd == gei.m_bd));
+}
+
+void
+gbp_ext_itf::sweep()
+{
+  if (m_hw) {
+    HW::enqueue(new gbp_ext_itf_cmds::delete_cmd(m_hw, m_itf->handle()));
+  }
+  HW::write();
+}
+
+void
+gbp_ext_itf::replay()
+{
+  if (m_hw) {
+    HW::enqueue(new gbp_ext_itf_cmds::create_cmd(m_hw, m_itf->handle(),
+                                                 m_bd->id(), m_rd->id()));
+  }
+}
+
+std::string
+gbp_ext_itf::to_string() const
+{
+  std::ostringstream s;
+  s << "gbp-ext_itf:[" << m_itf->to_string() << ", " << m_bd->to_string()
+    << ", " << m_rd->to_string() << "]";
+
+  return (s.str());
+}
+
+void
+gbp_ext_itf::update(const gbp_ext_itf& r)
+{
+  if (rc_t::OK != m_hw.rc()) {
+    HW::enqueue(new gbp_ext_itf_cmds::create_cmd(m_hw, m_itf->handle(),
+                                                 m_bd->id(), m_rd->id()));
+  }
+}
+
+std::shared_ptr<gbp_ext_itf>
+gbp_ext_itf::find_or_add(const gbp_ext_itf& temp)
+{
+  return (m_db.find_or_add(temp.key(), temp));
+}
+
+std::shared_ptr<gbp_ext_itf>
+gbp_ext_itf::find(const key_t& k)
+{
+  return (m_db.find(k));
+}
+
+std::shared_ptr<gbp_ext_itf>
+gbp_ext_itf::singular() const
+{
+  return find_or_add(*this);
+}
+
+void
+gbp_ext_itf::dump(std::ostream& os)
+{
+  db_dump(m_db, os);
+}
+
+gbp_ext_itf::event_handler::event_handler()
+{
+  OM::register_listener(this);
+  inspect::register_handler({ "gbp-ext-itf" }, "GBP External-Itfs", this);
+}
+
+void
+gbp_ext_itf::event_handler::handle_replay()
+{
+  m_db.replay();
+}
+
+void
+gbp_ext_itf::event_handler::handle_populate(const client_db::key_t& key)
+{
+  std::shared_ptr<gbp_ext_itf_cmds::dump_cmd> cmd =
+    std::make_shared<gbp_ext_itf_cmds::dump_cmd>();
+
+  HW::enqueue(cmd);
+  HW::write();
+
+  for (auto& record : *cmd) {
+    auto& payload = record.get_payload();
+
+    std::shared_ptr<interface> itf =
+      interface::find(payload.ext_itf.sw_if_index);
+    std::shared_ptr<gbp_bridge_domain> gbd =
+      gbp_bridge_domain::find(payload.ext_itf.bd_id);
+    std::shared_ptr<gbp_route_domain> grd =
+      gbp_route_domain::find(payload.ext_itf.rd_id);
+
+    VOM_LOG(log_level_t::DEBUG) << "data: [" << payload.ext_itf.sw_if_index
+                                << ", " << payload.ext_itf.bd_id << ", "
+                                << payload.ext_itf.rd_id << "]";
+
+    if (itf && gbd && grd) {
+      gbp_ext_itf ext_itf(*itf, *gbd, *grd);
+      OM::commit(key, ext_itf);
+
+      VOM_LOG(log_level_t::DEBUG) << "read: " << ext_itf.to_string();
+    }
+  }
+}
+
+dependency_t
+gbp_ext_itf::event_handler::order() const
+{
+  return (dependency_t::BINDING);
+}
+
+void
+gbp_ext_itf::event_handler::show(std::ostream& os)
+{
+  db_dump(m_db, os);
+}
+} // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
diff --git a/extras/vom/vom/gbp_ext_itf.hpp b/extras/vom/vom/gbp_ext_itf.hpp
new file mode 100644 (file)
index 0000000..20d9b6e
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_GBP_EXT_ITF_H__
+#define __VOM_GBP_EXT_ITF_H__
+
+#include "vom/gbp_bridge_domain.hpp"
+#include "vom/gbp_route_domain.hpp"
+#include "vom/interface.hpp"
+#include "vom/singular_db.hpp"
+
+namespace VOM {
+/**
+ * A enternal interface for GBP
+ */
+class gbp_ext_itf : public object_base
+{
+public:
+  /**
+   * The key for a GBP ext_itf interface
+   */
+  typedef interface::key_t key_t;
+
+  /**
+   * Construct a GBP ext_itf
+   */
+  gbp_ext_itf(const interface& itf,
+              const gbp_bridge_domain& gbd,
+              const gbp_route_domain& grd);
+
+  /**
+   * Copy Construct
+   */
+  gbp_ext_itf(const gbp_ext_itf& r);
+
+  /**
+   * Destructor
+   */
+  ~gbp_ext_itf();
+
+  /**
+   * Return the object's key
+   */
+  const key_t key() const;
+
+  /**
+   * comparison operator
+   */
+  bool operator==(const gbp_ext_itf& bdae) const;
+
+  /**
+   * Return the matching 'singular instance'
+   */
+  std::shared_ptr<gbp_ext_itf> singular() const;
+
+  /**
+   * Find the instnace of the ext_itf interface in the OM
+   */
+  static std::shared_ptr<gbp_ext_itf> find(const key_t& k);
+
+  /**
+   * Dump all bridge_domain-doamin into the stream provided
+   */
+  static void dump(std::ostream& os);
+
+  /**
+   * replay the object to create it in hardware
+   */
+  void replay(void);
+
+  /**
+   * Convert to string for debugging
+   */
+  std::string to_string() const;
+
+  /**
+   * return the ext_itfulation interface's handle
+   */
+  const handle_t& handle() const;
+
+private:
+  /**
+   * Class definition for listeners to OM events
+   */
+  class event_handler : public OM::listener, public inspect::command_handler
+  {
+  public:
+    event_handler();
+    virtual ~event_handler() = default;
+
+    /**
+     * Handle a populate event
+     */
+    void handle_populate(const client_db::key_t& key);
+
+    /**
+     * Handle a replay event
+     */
+    void handle_replay();
+
+    /**
+     * Show the object in the Singular DB
+     */
+    void show(std::ostream& os);
+
+    /**
+     * Get the sortable Id of the listener
+     */
+    dependency_t order() const;
+  };
+
+  /**
+   * event_handler to register with OM
+   */
+  static event_handler m_evh;
+
+  /**
+   * Commit the acculmulated changes into VPP. i.e. to a 'HW" write.
+   */
+  void update(const gbp_ext_itf& obj);
+
+  /**
+   * Find or add the instnace of the bridge_domain domain in the OM
+   */
+  static std::shared_ptr<gbp_ext_itf> find_or_add(const gbp_ext_itf& 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_ext_itf>;
+
+  /**
+   * Sweep/reap the object if still stale
+   */
+  void sweep(void);
+
+  /**
+   * HW configuration for the result of creating the ext_itf
+   */
+  HW::item<bool> m_hw;
+
+  /**
+   * The interface the ext_itf is attached to.
+   */
+  std::shared_ptr<interface> m_itf;
+
+  /**
+   * The BD & RD the ext_itf is in
+   */
+  std::shared_ptr<gbp_bridge_domain> m_bd;
+  std::shared_ptr<gbp_route_domain> m_rd;
+
+  /**
+   * A map of all bridge_domains
+   */
+  static singular_db<key_t, gbp_ext_itf> m_db;
+};
+
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
diff --git a/extras/vom/vom/gbp_ext_itf_cmds.cpp b/extras/vom/vom/gbp_ext_itf_cmds.cpp
new file mode 100644 (file)
index 0000000..b2090ea
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vom/gbp_ext_itf_cmds.hpp"
+
+namespace VOM {
+namespace gbp_ext_itf_cmds {
+
+create_cmd::create_cmd(HW::item<bool>& item,
+                       const handle_t& itf,
+                       uint32_t bd_id,
+                       uint32_t rd_id)
+  : rpc_cmd(item)
+  , m_itf(itf)
+  , m_bd_id(bd_id)
+  , m_rd_id(rd_id)
+{
+}
+
+bool
+create_cmd::operator==(const create_cmd& other) const
+{
+  return ((m_itf == other.m_itf) && (m_bd_id == other.m_bd_id) &&
+          (m_rd_id == other.m_rd_id));
+}
+
+rc_t
+create_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), std::ref(*this));
+
+  auto& payload = req.get_request().get_payload();
+  payload.is_add = 1;
+  payload.ext_itf.sw_if_index = m_itf.value();
+  payload.ext_itf.bd_id = m_bd_id;
+  payload.ext_itf.rd_id = m_rd_id;
+
+  VAPI_CALL(req.execute());
+
+  return (wait());
+}
+
+std::string
+create_cmd::to_string() const
+{
+  std::ostringstream s;
+  s << "gbp-ext_itf-create: " << m_hw_item.to_string() << " itf:" << m_itf
+    << " bd-id:" << m_bd_id << " rd-id:" << m_rd_id;
+
+  return (s.str());
+}
+
+delete_cmd::delete_cmd(HW::item<bool>& item, const handle_t& itf)
+  : rpc_cmd(item)
+  , m_itf(itf)
+{
+}
+
+bool
+delete_cmd::operator==(const delete_cmd& other) const
+{
+  return (m_itf == other.m_itf);
+}
+
+rc_t
+delete_cmd::issue(connection& con)
+{
+  msg_t req(con.ctx(), std::ref(*this));
+
+  auto& payload = req.get_request().get_payload();
+  payload.is_add = 0;
+  payload.ext_itf.sw_if_index = m_itf.value();
+  payload.ext_itf.bd_id = ~0;
+  payload.ext_itf.rd_id = ~0;
+
+  VAPI_CALL(req.execute());
+
+  return (wait());
+}
+
+std::string
+delete_cmd::to_string() const
+{
+  std::ostringstream s;
+  s << "gbp-ext-itf-delete: " << m_hw_item.to_string() << " itf:" << m_itf;
+
+  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-ext-itf-dump");
+}
+
+}; // namespace gbp_ext_itf_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_ext_itf_cmds.hpp b/extras/vom/vom/gbp_ext_itf_cmds.hpp
new file mode 100644 (file)
index 0000000..51052b9
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __VOM_GBP_EXT_ITF_CMDS_H__
+#define __VOM_GBP_EXT_ITF_CMDS_H__
+
+#include "vom/dump_cmd.hpp"
+#include "vom/gbp_ext_itf.hpp"
+
+#include <vapi/gbp.api.vapi.hpp>
+
+namespace VOM {
+namespace gbp_ext_itf_cmds {
+
+/**
+* A command class that creates or updates the GBP ext_itf
+*/
+class create_cmd : public rpc_cmd<HW::item<bool>, vapi::Gbp_ext_itf_add_del>
+{
+public:
+  /**
+   * Constructor
+   */
+  create_cmd(HW::item<bool>& item,
+             const handle_t& itf,
+             uint32_t bd_id,
+             uint32_t rd_id);
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * Comparison operator - only used for UT
+   */
+  bool operator==(const create_cmd& i) const;
+
+private:
+  const handle_t m_itf;
+  const uint32_t m_bd_id;
+  const uint32_t m_rd_id;
+};
+
+/**
+ * A cmd class that deletes a GBP ext_itf
+ */
+class delete_cmd : public rpc_cmd<HW::item<bool>, vapi::Gbp_ext_itf_add_del>
+{
+public:
+  /**
+   * Constructor
+   */
+  delete_cmd(HW::item<bool>& item, const handle_t& itf);
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * Comparison operator - only used for UT
+   */
+  bool operator==(const delete_cmd& i) const;
+
+private:
+  const handle_t m_itf;
+};
+
+/**
+ * A cmd class that Dumps all the GBP ext_itfs
+ */
+class dump_cmd : public VOM::dump_cmd<vapi::Gbp_ext_itf_dump>
+{
+public:
+  /**
+   * Constructor
+   */
+  dump_cmd() = default;
+
+  /**
+   * Issue the command to VPP/HW
+   */
+  rc_t issue(connection& con);
+  /**
+   * convert to string format for debug purposes
+   */
+  std::string to_string() const;
+
+  /**
+   * Comparison operator - only used for UT
+   */
+  bool operator==(const dump_cmd& i) const;
+
+private:
+  /**
+   * HW reutrn code
+   */
+  HW::item<bool> item;
+};
+}; // namespace gbp_ext_itf_cmds
+}; // namespace VOM
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "mozilla")
+ * End:
+ */
+
+#endif
index 2221c61..8404dcd 100644 (file)
@@ -62,6 +62,18 @@ gbp_subnet::gbp_subnet(const gbp_route_domain& rd,
 {
 }
 
+gbp_subnet::gbp_subnet(const gbp_route_domain& rd,
+                       const route::prefix_t& prefix,
+                       const gbp_endpoint_group& epg)
+  : m_hw(false)
+  , m_rd(rd.singular())
+  , m_prefix(prefix)
+  , m_type(type_t::L3_OUT)
+  , m_recirc(nullptr)
+  , m_epg(epg.singular())
+{
+}
+
 gbp_subnet::gbp_subnet(const gbp_subnet& o)
   : m_hw(o.m_hw)
   , m_rd(o.m_rd)
@@ -215,6 +227,15 @@ gbp_subnet::event_handler::handle_populate(const client_db::key_t& key)
           VOM_LOG(log_level_t::DEBUG) << "read: " << gs.to_string();
           break;
         }
+        case GBP_API_SUBNET_L3_OUT: {
+          std::shared_ptr<gbp_endpoint_group> epg =
+            gbp_endpoint_group::find(payload.subnet.epg_id);
+
+          gbp_subnet gs(*rd, pfx, *epg);
+          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);
index e08f1a2..f18aa01 100644 (file)
@@ -53,6 +53,11 @@ public:
      */
     const static type_t TRANSPORT;
 
+    /**
+     * A transport subnet, sent via the RD's UU-fwd interface
+     */
+    const static type_t L3_OUT;
+
   private:
     type_t(int v, const std::string s);
   };
@@ -65,13 +70,20 @@ public:
              const type_t& type);
 
   /**
-   * Construct an external GBP subnet
+   * Construct an stitched external GBP subnet
    */
   gbp_subnet(const gbp_route_domain& rd,
              const route::prefix_t& prefix,
              const gbp_recirc& recirc,
              const gbp_endpoint_group& epg);
 
+  /**
+   * Construct an l3-out GBP subnet
+   */
+  gbp_subnet(const gbp_route_domain& rd,
+             const route::prefix_t& prefix,
+             const gbp_endpoint_group& epg);
+
   /**
    * Copy Construct
    */
index 377197a..4b51141 100644 (file)
@@ -19,6 +19,7 @@ add_vpp_plugin(gbp
   gbp_contract.c
   gbp_endpoint.c
   gbp_endpoint_group.c
+  gbp_ext_itf.c
   gbp_fwd.c
   gbp_fwd_dpo.c
   gbp_itf.c
index 6bdcc5d..bf7c167 100644 (file)
@@ -95,12 +95,13 @@ define gbp_route_domain_details
 
 enum gbp_endpoint_flags
 {
-  NONE = 0,
-  BOUNCE = 0x1,
-  REMOTE = 0x2,
-  LEARNT = 0x4,
+  GBP_API_ENDPOINT_FLAG_NONE = 0,
+  GBP_API_ENDPOINT_FLAG_BOUNCE = 0x1,
+  GBP_API_ENDPOINT_FLAG_REMOTE = 0x2,
+  GBP_API_ENDPOINT_FLAG_LEARNT = 0x4,
   /* hey Ole WTF */
-  REMOTE_LEARNT = 0x6,
+  GBP_API_ENDPOINT_FLAG_REMOTE_LEARNT = 0x6,
+  GBP_API_ENDPOINT_FLAG_EXTERNAL = 0x8,
 };
 
 typedef gbp_endpoint_tun
@@ -220,6 +221,7 @@ enum gbp_subnet_type
   GBP_API_SUBNET_TRANSPORT,
   GBP_API_SUBNET_STITCHED_INTERNAL,
   GBP_API_SUBNET_STITCHED_EXTERNAL,
+  GBP_API_SUBNET_L3_OUT,
 };
 
 typeonly define gbp_subnet
@@ -379,6 +381,33 @@ define gbp_vxlan_tunnel_details
   vl_api_gbp_vxlan_tunnel_t tunnel;
 };
 
+typeonly define gbp_ext_itf
+{
+  u32 sw_if_index;
+  u32 bd_id;
+  u32 rd_id;
+};
+
+autoreply define gbp_ext_itf_add_del
+{
+  u32 client_index;
+  u32 context;
+  u8  is_add;
+  vl_api_gbp_ext_itf_t ext_itf;
+};
+
+define gbp_ext_itf_dump
+{
+  u32 client_index;
+  u32 context;
+};
+
+define gbp_ext_itf_details
+{
+  u32 context;
+  vl_api_gbp_ext_itf_t ext_itf;
+};
+
 /*
  * Local Variables:
  * eval: (c-set-style "gnu")
index 4402ec1..5f87db8 100644 (file)
@@ -30,6 +30,7 @@
 #include <gbp/gbp_vxlan.h>
 #include <gbp/gbp_bridge_domain.h>
 #include <gbp/gbp_route_domain.h>
+#include <gbp/gbp_ext_itf.h>
 
 #include <vlibapi/api.h>
 #include <vlibmemory/api.h>
@@ -75,6 +76,8 @@
   _(GBP_ROUTE_DOMAIN_DUMP, gbp_route_domain_dump)           \
   _(GBP_RECIRC_ADD_DEL, gbp_recirc_add_del)                 \
   _(GBP_RECIRC_DUMP, gbp_recirc_dump)                       \
+  _(GBP_EXT_ITF_ADD_DEL, gbp_ext_itf_add_del)               \
+  _(GBP_EXT_ITF_DUMP, gbp_ext_itf_dump)                     \
   _(GBP_CONTRACT_ADD_DEL, gbp_contract_add_del)             \
   _(GBP_CONTRACT_DUMP, gbp_contract_dump)                   \
   _(GBP_ENDPOINT_LEARN_SET_INACTIVE_THRESHOLD, gbp_endpoint_learn_set_inactive_threshold) \
@@ -95,12 +98,14 @@ gbp_endpoint_flags_decode (vl_api_gbp_endpoint_flags_t v)
 
   v = ntohl (v);
 
-  if (v & BOUNCE)
+  if (v & GBP_API_ENDPOINT_FLAG_BOUNCE)
     f |= GBP_ENDPOINT_FLAG_BOUNCE;
-  if (v & REMOTE)
+  if (v & GBP_API_ENDPOINT_FLAG_REMOTE)
     f |= GBP_ENDPOINT_FLAG_REMOTE;
-  if (v & LEARNT)
+  if (v & GBP_API_ENDPOINT_FLAG_LEARNT)
     f |= GBP_ENDPOINT_FLAG_LEARNT;
+  if (v & GBP_API_ENDPOINT_FLAG_EXTERNAL)
+    f |= GBP_ENDPOINT_FLAG_EXTERNAL;
 
   return (f);
 }
@@ -112,11 +117,13 @@ gbp_endpoint_flags_encode (gbp_endpoint_flags_t f)
 
 
   if (f & GBP_ENDPOINT_FLAG_BOUNCE)
-    v |= BOUNCE;
+    v |= GBP_API_ENDPOINT_FLAG_BOUNCE;
   if (f & GBP_ENDPOINT_FLAG_REMOTE)
-    v |= REMOTE;
+    v |= GBP_API_ENDPOINT_FLAG_REMOTE;
   if (f & GBP_ENDPOINT_FLAG_LEARNT)
-    v |= LEARNT;
+    v |= GBP_API_ENDPOINT_FLAG_LEARNT;
+  if (f & GBP_ENDPOINT_FLAG_EXTERNAL)
+    v |= GBP_API_ENDPOINT_FLAG_EXTERNAL;
 
   v = htonl (v);
 
@@ -386,6 +393,9 @@ gub_subnet_type_from_api (vl_api_gbp_subnet_type_t a, gbp_subnet_type_t * t)
     case GBP_API_SUBNET_TRANSPORT:
       *t = GBP_SUBNET_TRANSPORT;
       return (0);
+    case GBP_API_SUBNET_L3_OUT:
+      *t = GBP_SUBNET_L3_OUT;
+      return (0);
     case GBP_API_SUBNET_STITCHED_INTERNAL:
       *t = GBP_SUBNET_STITCHED_INTERNAL;
       return (0);
@@ -440,6 +450,9 @@ gub_subnet_type_to_api (gbp_subnet_type_t t)
     case GBP_SUBNET_STITCHED_EXTERNAL:
       a = GBP_API_SUBNET_STITCHED_EXTERNAL;
       break;
+    case GBP_SUBNET_L3_OUT:
+      a = GBP_API_SUBNET_L3_OUT;
+      break;
     }
 
   a = clib_host_to_net_u32 (a);
@@ -642,7 +655,7 @@ vl_api_gbp_recirc_add_del_t_handler (vl_api_gbp_recirc_add_del_t * mp)
   REPLY_MACRO (VL_API_GBP_RECIRC_ADD_DEL_REPLY + GBP_MSG_BASE);
 }
 
-static int
+static walk_rc_t
 gbp_recirc_send_details (gbp_recirc_t * gr, void *args)
 {
   vl_api_gbp_recirc_details_t *mp;
@@ -651,7 +664,7 @@ gbp_recirc_send_details (gbp_recirc_t * gr, void *args)
   ctx = args;
   mp = vl_msg_api_alloc (sizeof (*mp));
   if (!mp)
-    return 1;
+    return (WALK_STOP);
 
   clib_memset (mp, 0, sizeof (*mp));
   mp->_vl_msg_id = ntohs (VL_API_GBP_RECIRC_DETAILS + GBP_MSG_BASE);
@@ -663,7 +676,7 @@ gbp_recirc_send_details (gbp_recirc_t * gr, void *args)
 
   vl_api_send_msg (ctx->reg, (u8 *) mp);
 
-  return (1);
+  return (WALK_CONTINUE);
 }
 
 static void
@@ -683,6 +696,70 @@ vl_api_gbp_recirc_dump_t_handler (vl_api_gbp_recirc_dump_t * mp)
   gbp_recirc_walk (gbp_recirc_send_details, &ctx);
 }
 
+static void
+vl_api_gbp_ext_itf_add_del_t_handler (vl_api_gbp_ext_itf_add_del_t * mp)
+{
+  vl_api_gbp_ext_itf_add_del_reply_t *rmp;
+  u32 sw_if_index;
+  int rv = 0;
+
+  sw_if_index = ntohl (mp->ext_itf.sw_if_index);
+  if (!vnet_sw_if_index_is_api_valid (sw_if_index))
+    goto bad_sw_if_index;
+
+  if (mp->is_add)
+    rv = gbp_ext_itf_add (sw_if_index,
+                         ntohl (mp->ext_itf.bd_id),
+                         ntohl (mp->ext_itf.rd_id));
+  else
+    rv = gbp_ext_itf_delete (sw_if_index);
+
+  BAD_SW_IF_INDEX_LABEL;
+
+  REPLY_MACRO (VL_API_GBP_EXT_ITF_ADD_DEL_REPLY + GBP_MSG_BASE);
+}
+
+static walk_rc_t
+gbp_ext_itf_send_details (gbp_ext_itf_t * gx, void *args)
+{
+  vl_api_gbp_ext_itf_details_t *mp;
+  gbp_walk_ctx_t *ctx;
+
+  ctx = args;
+  mp = vl_msg_api_alloc (sizeof (*mp));
+  if (!mp)
+    return (WALK_STOP);
+
+  clib_memset (mp, 0, sizeof (*mp));
+  mp->_vl_msg_id = ntohs (VL_API_GBP_EXT_ITF_DETAILS + GBP_MSG_BASE);
+  mp->context = ctx->context;
+
+  mp->ext_itf.bd_id = ntohl (gbp_bridge_domain_get_bd_id (gx->gx_bd));
+  mp->ext_itf.rd_id = ntohl (gbp_route_domain_get_rd_id (gx->gx_rd));
+  mp->ext_itf.sw_if_index = ntohl (gx->gx_itf);
+
+  vl_api_send_msg (ctx->reg, (u8 *) mp);
+
+  return (WALK_CONTINUE);
+}
+
+static void
+vl_api_gbp_ext_itf_dump_t_handler (vl_api_gbp_ext_itf_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_ext_itf_walk (gbp_ext_itf_send_details, &ctx);
+}
+
 static int
 gbp_contract_rule_action_deocde (vl_api_gbp_rule_action_t in,
                                 gbp_rule_action_t * out)
index 298819f..21ffe9c 100644 (file)
@@ -56,6 +56,16 @@ gbp_bridge_domain_lock (index_t i)
   gb->gb_locks++;
 }
 
+u32
+gbp_bridge_domain_get_bd_id (index_t gbdi)
+{
+  gbp_bridge_domain_t *gb;
+
+  gb = gbp_bridge_domain_get (gbdi);
+
+  return (gb->gb_bd_id);
+}
+
 static index_t
 gbp_bridge_domain_find (u32 bd_id)
 {
index cc03320..65f133c 100644 (file)
@@ -78,6 +78,7 @@ 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 index_t gbp_bridge_domain_index (const gbp_bridge_domain_t *);
+extern u32 gbp_bridge_domain_get_bd_id (index_t gbdi);
 
 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);
index 4846911..94044ae 100644 (file)
  */
 
 #include <plugins/gbp/gbp.h>
+#include <plugins/gbp/gbp_policy_dpo.h>
+#include <plugins/gbp/gbp_ext_itf.h>
+#include <vnet/fib/ip4_fib.h>
+#include <vnet/fib/ip6_fib.h>
+#include <vnet/dpo/load_balance.h>
 #include <vnet/l2/l2_input.h>
 #include <vnet/l2/feat_bitmap.h>
 #include <vnet/fib/fib_table.h>
@@ -25,9 +30,10 @@ typedef enum gbp_src_classify_type_t_
 {
   GBP_SRC_CLASSIFY_NULL,
   GBP_SRC_CLASSIFY_PORT,
+  GBP_SRC_CLASSIFY_LPM,
 } gbp_src_classify_type_t;
 
-#define GBP_SRC_N_CLASSIFY (GBP_SRC_CLASSIFY_PORT + 1)
+#define GBP_SRC_N_CLASSIFY (GBP_SRC_CLASSIFY_LPM + 1)
 
 /**
  * Grouping of global data for the GBP source EPG classification feature
@@ -297,6 +303,276 @@ VNET_FEATURE_INIT (gbp_ip6_src_classify_feat_node, static) =
   .runs_before = VNET_FEATURES ("nat66-out2in"),
 };
 
+/* *INDENT-ON* */
+
+typedef enum gbp_lpm_classify_next_t_
+{
+  GPB_LPM_CLASSIFY_DROP,
+} gbp_lpm_classify_next_t;
+
+always_inline dpo_proto_t
+ethertype_to_dpo_proto (const ethernet_header_t * eh0)
+{
+  u16 etype = clib_net_to_host_u16 (eh0->type);
+
+  switch (etype)
+    {
+    case ETHERNET_TYPE_IP4:
+      return (DPO_PROTO_IP4);
+    case ETHERNET_TYPE_IP6:
+      return (DPO_PROTO_IP6);
+    case ETHERNET_TYPE_VLAN:
+      {
+       ethernet_vlan_header_t *vh0;
+
+       vh0 = (ethernet_vlan_header_t *) (eh0 + 1);
+
+       switch (clib_net_to_host_u16 (vh0->type))
+         {
+         case ETHERNET_TYPE_IP4:
+           return (DPO_PROTO_IP4);
+         case ETHERNET_TYPE_IP6:
+           return (DPO_PROTO_IP6);
+         }
+      }
+    }
+
+  return (DPO_PROTO_NONE);
+}
+
+/*
+ * Determine the SRC EPG from a LPM
+ */
+always_inline uword
+gbp_lpm_classify_inline (vlib_main_t * vm,
+                        vlib_node_runtime_t * node,
+                        vlib_frame_t * frame,
+                        dpo_proto_t dproto, u8 is_recirc)
+{
+  gbp_src_classify_main_t *gscm = &gbp_src_classify_main;
+  u32 n_left_from, *from, *to_next;
+  u32 next_index;
+
+  next_index = 0;
+  n_left_from = frame->n_vectors;
+  from = vlib_frame_vector_args (frame);
+
+  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, fib_index0, lbi0;
+         gbp_lpm_classify_next_t next0;
+         const gbp_policy_dpo_t *gpd0;
+         const gbp_ext_itf_t *gx0;
+         const gbp_recirc_t *gr0;
+         const dpo_id_t *dpo0;
+         load_balance_t *lb0;
+         ip4_header_t *ip4_0;
+         ip6_header_t *ip6_0;
+         vlib_buffer_t *b0;
+         epg_id_t src_epg0;
+
+         bi0 = from[0];
+         to_next[0] = bi0;
+         from += 1;
+         to_next += 1;
+         n_left_from -= 1;
+         n_left_to_next -= 1;
+         next0 = GPB_LPM_CLASSIFY_DROP;
+
+         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 (DPO_PROTO_IP4 == dproto)
+           ip4_0 = vlib_buffer_get_current (b0);
+         else if (DPO_PROTO_IP4 == dproto)
+           ip6_0 = vlib_buffer_get_current (b0);
+         else if (DPO_PROTO_ETHERNET == dproto)
+           {
+             const ethernet_header_t *eh0;
+
+             eh0 = vlib_buffer_get_current (b0);
+
+             dproto = ethertype_to_dpo_proto (eh0);
+
+             switch (dproto)
+               {
+               case DPO_PROTO_IP4:
+                 ip4_0 = (vlib_buffer_get_current (b0) +
+                          vnet_buffer (b0)->l2.l2_len);
+                 break;
+               case DPO_PROTO_IP6:
+                 ip6_0 = (vlib_buffer_get_current (b0) +
+                          vnet_buffer (b0)->l2.l2_len);
+                 break;
+               default:
+                 /* not IP so no LPM classify possible */
+                 src_epg0 = EPG_INVALID;
+                 goto trace;
+               }
+           }
+
+         if (is_recirc)
+           {
+             gr0 = gbp_recirc_get (sw_if_index0);
+             fib_index0 = gr0->gr_fib_index[dproto];
+
+             vnet_feature_next (&next0, b0);
+           }
+         else
+           {
+             gx0 = gbp_ext_itf_get (sw_if_index0);
+             fib_index0 = gx0->gx_fib_index[dproto];
+
+             next0 = vnet_l2_feature_next
+               (b0, gscm->l2_input_feat_next[GBP_SRC_CLASSIFY_LPM],
+                L2INPUT_FEAT_GBP_LPM_CLASSIFY);
+           }
+
+         if (DPO_PROTO_IP4 == dproto)
+           {
+             lbi0 = ip4_fib_forwarding_lookup (fib_index0,
+                                               &ip4_0->src_address);
+           }
+         else if (DPO_PROTO_IP4 == dproto)
+           {
+             lbi0 = ip6_fib_table_fwding_lookup (&ip6_main, fib_index0,
+                                                 &ip6_0->src_address);
+           }
+         else
+           {
+             /* not IP so no LPM classify possible */
+             src_epg0 = EPG_INVALID;
+             goto trace;
+           }
+         lb0 = load_balance_get (lbi0);
+         dpo0 = load_balance_get_bucket_i (lb0, 0);
+
+         if (gbp_policy_dpo_type == dpo0->dpoi_type)
+           {
+             gpd0 = gbp_policy_dpo_get (dpo0->dpoi_index);
+             src_epg0 = gpd0->gpd_epg;
+           }
+         else
+           {
+             /* could not classify => drop */
+             src_epg0 = EPG_INVALID;
+             next0 = GPB_LPM_CLASSIFY_DROP;
+           }
+
+       trace:
+         vnet_buffer2 (b0)->gbp.src_epg = src_epg0;
+
+         if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
+           {
+             gbp_classify_trace_t *t =
+               vlib_add_trace (vm, node, b0, sizeof (*t));
+             t->src_epg = src_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;
+}
+
+static uword
+gbp_ip4_lpm_classify (vlib_main_t * vm,
+                     vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+  return (gbp_lpm_classify_inline (vm, node, frame, DPO_PROTO_IP4, 1));
+}
+
+static uword
+gbp_ip6_lpm_classify (vlib_main_t * vm,
+                     vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+  return (gbp_lpm_classify_inline (vm, node, frame, DPO_PROTO_IP6, 1));
+}
+
+static uword
+gbp_l2_lpm_classify (vlib_main_t * vm,
+                    vlib_node_runtime_t * node, vlib_frame_t * frame)
+{
+  return (gbp_lpm_classify_inline (vm, node, frame, DPO_PROTO_ETHERNET, 0));
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (gbp_ip4_lpm_classify_node) = {
+  .function = gbp_ip4_lpm_classify,
+  .name = "ip4-gbp-lpm-classify",
+  .vector_size = sizeof (u32),
+  .format_trace = format_gbp_classify_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = 0,
+  .n_next_nodes = 1,
+  .next_nodes = {
+    [GPB_LPM_CLASSIFY_DROP] = "ip4-drop"
+  },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (gbp_ip4_lpm_classify_node, gbp_ip4_lpm_classify);
+
+VLIB_REGISTER_NODE (gbp_ip6_lpm_classify_node) = {
+  .function = gbp_ip6_lpm_classify,
+  .name = "ip6-gbp-lpm-classify",
+  .vector_size = sizeof (u32),
+  .format_trace = format_gbp_classify_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = 0,
+  .n_next_nodes = 1,
+  .next_nodes = {
+    [GPB_LPM_CLASSIFY_DROP] = "ip6-drop"
+  },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (gbp_ip6_lpm_classify_node, gbp_ip6_lpm_classify);
+
+VLIB_REGISTER_NODE (gbp_l2_lpm_classify_node) = {
+  .function = gbp_l2_lpm_classify,
+  .name = "l2-gbp-lpm-classify",
+  .vector_size = sizeof (u32),
+  .format_trace = format_gbp_classify_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = 0,
+  .n_next_nodes = 1,
+  .next_nodes = {
+    [GPB_LPM_CLASSIFY_DROP] = "error-drop"
+  },
+};
+
+VLIB_NODE_FUNCTION_MULTIARCH (gbp_l2_lpm_classify_node, gbp_l2_lpm_classify);
+
+VNET_FEATURE_INIT (gbp_ip4_lpm_classify_feat_node, static) =
+{
+  .arc_name = "ip4-unicast",
+  .node_name = "ip4-gbp-lpm-classify",
+  .runs_before = VNET_FEATURES ("nat44-out2in"),
+};
+VNET_FEATURE_INIT (gbp_ip6_lpm_classify_feat_node, static) =
+{
+  .arc_name = "ip6-unicast",
+  .node_name = "ip6-gbp-lpm-classify",
+  .runs_before = VNET_FEATURES ("nat66-out2in"),
+};
+
+/* *INDENT-ON* */
+
 static clib_error_t *
 gbp_src_classify_init (vlib_main_t * vm)
 {
@@ -313,6 +589,11 @@ gbp_src_classify_init (vlib_main_t * vm)
                               L2INPUT_N_FEAT,
                               l2input_get_feat_names (),
                               em->l2_input_feat_next[GBP_SRC_CLASSIFY_PORT]);
+  feat_bitmap_init_next_nodes (vm,
+                              gbp_l2_lpm_classify_node.index,
+                              L2INPUT_N_FEAT,
+                              l2input_get_feat_names (),
+                              em->l2_input_feat_next[GBP_SRC_CLASSIFY_LPM]);
 
   return 0;
 }
index e9ced3f..0746b70 100644 (file)
@@ -92,7 +92,19 @@ format_gbp_endpoint_flags (u8 * s, va_list * args)
 int
 gbp_endpoint_is_remote (const gbp_endpoint_t * ge)
 {
-  return (ge->ge_fwd.gef_flags & GBP_ENDPOINT_FLAG_REMOTE);
+  return (! !(ge->ge_fwd.gef_flags & GBP_ENDPOINT_FLAG_REMOTE));
+}
+
+int
+gbp_endpoint_is_local (const gbp_endpoint_t * ge)
+{
+  return (!(ge->ge_fwd.gef_flags & GBP_ENDPOINT_FLAG_REMOTE));
+}
+
+int
+gbp_endpoint_is_external (const gbp_endpoint_t * ge)
+{
+  return (! !(ge->ge_fwd.gef_flags & GBP_ENDPOINT_FLAG_EXTERNAL));
 }
 
 static void
@@ -625,8 +637,12 @@ gbb_endpoint_fwd_recalc (gbp_endpoint_t * ge)
     {
       gbp_itf_set_l2_input_feature (gef->gef_itf, gei, L2INPUT_FEAT_GBP_FWD);
 
-      if (gbp_endpoint_is_remote (ge))
+      if (gbp_endpoint_is_remote (ge) || gbp_endpoint_is_external (ge))
        {
+         /*
+          * bridged packets to external endpoints should be classifed
+          * based on the EP's/BD's EPG
+          */
          gbp_itf_set_l2_output_feature (gef->gef_itf, gei,
                                         L2OUTPUT_FEAT_GBP_POLICY_MAC);
        }
@@ -711,6 +727,12 @@ gbb_endpoint_fwd_recalc (gbp_endpoint_t * ge)
                                        ip_sw_if_index, rewrite);
     vec_add1 (gef->gef_adjs, ai);
 
+    /*
+     * if the endpoint is external then routed packet to it must be
+     * classifed to the BD's EPG. but this will happen anyway with
+     * the GBP_MAC classification.
+     */
+
     if (NULL != gg)
       {
        if (gbp_endpoint_is_remote (ge))
@@ -728,6 +750,7 @@ gbb_endpoint_fwd_recalc (gbp_endpoint_t * ge)
                                             FIB_SOURCE_PLUGIN_HI,
                                             FIB_ENTRY_FLAG_INTERPOSE,
                                             &policy_dpo);
+           dpo_reset (&policy_dpo);
          }
 
        /*
@@ -735,7 +758,7 @@ gbb_endpoint_fwd_recalc (gbp_endpoint_t * ge)
         * 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)
+       if (gbp_endpoint_is_local (ge) && ~0 != gg->gg_uplink_sw_if_index)
          {
            gbp_endpoint_add_itf (gef->gef_itf, gei);
            if (FIB_PROTOCOL_IP4 == pfx->fp_proto)
@@ -750,13 +773,14 @@ gbb_endpoint_fwd_recalc (gbp_endpoint_t * ge)
       }
   }
 
-  if (!gbp_endpoint_is_remote (ge))
+  if (gbp_endpoint_is_local (ge) && !gbp_endpoint_is_external (ge))
     {
       /*
        * 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 if the group has an uplink
        * interface (on which the GBP-FWD feature would send UU traffic).
+       * External endpoints get classified based on an LPM match
        */
       l2input_feat_masks_t feats = L2INPUT_FEAT_GBP_SRC_CLASSIFY;
 
@@ -1444,6 +1468,10 @@ void
 gbp_learn_set_inactive_threshold (u32 threshold)
 {
   GBP_ENDPOINT_INACTIVE_TIME = threshold;
+
+  vlib_process_signal_event (vlib_get_main (),
+                            gbp_scanner_node.index,
+                            GBP_ENDPOINT_SCAN_SET_TIME, 0);
 }
 
 f64
index d56e91d..4b4c2e0 100644 (file)
@@ -32,8 +32,9 @@ 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_REMOTE,
+  GBP_ENDPOINT_ATTR_LEARNT,
+  GBP_ENDPOINT_ATTR_EXTERNAL,
   GBP_ENDPOINT_ATTR_LAST,
 } gbp_endpoint_attr_t;
 
@@ -43,12 +44,14 @@ typedef enum gbp_endpoint_flags_t_
   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_FLAG_EXTERNAL = (1 << GBP_ENDPOINT_ATTR_EXTERNAL),
 } gbp_endpoint_flags_t;
 
 #define GBP_ENDPOINT_ATTR_NAMES {                 \
     [GBP_ENDPOINT_ATTR_BOUNCE] = "bounce",        \
     [GBP_ENDPOINT_ATTR_REMOTE] = "remote",        \
     [GBP_ENDPOINT_ATTR_LEARNT] = "learnt",        \
+    [GBP_ENDPOINT_ATTR_EXTERNAL] = "external",    \
 }
 
 extern u8 *format_gbp_endpoint_flags (u8 * s, va_list * args);
@@ -235,6 +238,8 @@ 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 int gbp_endpoint_is_local (const gbp_endpoint_t * ge);
+extern int gbp_endpoint_is_external (const gbp_endpoint_t * ge);
 extern int gbp_endpoint_is_learnt (const gbp_endpoint_t * ge);
 
 
diff --git a/src/plugins/gbp/gbp_ext_itf.c b/src/plugins/gbp/gbp_ext_itf.c
new file mode 100644 (file)
index 0000000..57ff625
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * 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_ext_itf.h>
+#include <plugins/gbp/gbp_bridge_domain.h>
+#include <plugins/gbp/gbp_route_domain.h>
+#include <plugins/gbp/gbp_itf.h>
+
+/**
+ * Pool of GBP ext_itfs
+ */
+gbp_ext_itf_t *gbp_ext_itf_pool;
+
+/**
+ * external interface configs keyed by sw_if_index
+ */
+index_t *gbp_ext_itf_db;
+
+#define GBP_EXT_ITF_ID 0x00000080
+
+/**
+ * logger
+ */
+vlib_log_class_t gx_logger;
+
+#define GBP_EXT_ITF_DBG(...)                           \
+    vlib_log_debug (gx_logger, __VA_ARGS__);
+
+u8 *
+format_gbp_ext_itf (u8 * s, va_list * args)
+{
+  gbp_ext_itf_t *gx = va_arg (*args, gbp_ext_itf_t *);
+
+  return (format (s, "%U in %U",
+                 format_gbp_itf, gx->gx_itf,
+                 format_gbp_bridge_domain, gx->gx_bd));
+}
+
+int
+gbp_ext_itf_add (u32 sw_if_index, u32 bd_id, u32 rd_id)
+{
+  gbp_ext_itf_t *gx;
+  index_t gxi;
+
+  vec_validate_init_empty (gbp_ext_itf_db, sw_if_index, INDEX_INVALID);
+
+  gxi = gbp_ext_itf_db[sw_if_index];
+
+  if (INDEX_INVALID == gxi)
+    {
+      gbp_bridge_domain_t *gb;
+      gbp_route_domain_t *gr;
+      fib_protocol_t fproto;
+      index_t gbi, gri;
+
+      gbi = gbp_bridge_domain_find_and_lock (bd_id);
+
+      if (INDEX_INVALID == gbi)
+       return (VNET_API_ERROR_NO_SUCH_ENTRY);
+
+      gri = gbp_route_domain_find_and_lock (rd_id);
+
+      if (INDEX_INVALID == gri)
+       {
+         gbp_bridge_domain_unlock (gbi);
+         return (VNET_API_ERROR_NO_SUCH_ENTRY);
+       }
+
+      pool_get_zero (gbp_ext_itf_pool, gx);
+      gxi = gx - gbp_ext_itf_pool;
+
+      gb = gbp_bridge_domain_get (gbi);
+      gr = gbp_route_domain_get (gri);
+
+      gx->gx_bd = gbi;
+      gx->gx_rd = gri;
+
+      FOR_EACH_FIB_IP_PROTOCOL (fproto)
+      {
+       gx->gx_fib_index[fproto] =
+         gr->grd_fib_index[fib_proto_to_dpo (fproto)];
+      }
+
+      gx->gx_itf = gbp_itf_add_and_lock (sw_if_index, gb->gb_bd_index);
+      gbp_itf_set_l2_input_feature (gx->gx_itf, (gxi | GBP_EXT_ITF_ID),
+                                   L2INPUT_FEAT_GBP_LPM_CLASSIFY);
+
+      gbp_ext_itf_db[sw_if_index] = gxi;
+
+      GBP_EXT_ITF_DBG ("add: %U", format_gbp_ext_itf, gx);
+
+      return (0);
+    }
+
+  return (VNET_API_ERROR_ENTRY_ALREADY_EXISTS);
+}
+
+int
+gbp_ext_itf_delete (u32 sw_if_index)
+{
+  gbp_ext_itf_t *gx;
+  index_t gxi;
+
+  gxi = gbp_ext_itf_db[sw_if_index];
+
+  if (INDEX_INVALID != gxi)
+    {
+      gx = pool_elt_at_index (gbp_ext_itf_pool, gxi);
+
+      GBP_EXT_ITF_DBG ("del: %U", format_gbp_ext_itf, gx);
+
+      gbp_itf_set_l2_input_feature (gx->gx_itf,
+                                   (gxi | GBP_EXT_ITF_ID),
+                                   L2INPUT_FEAT_NONE);
+      gbp_itf_unlock (gx->gx_itf);
+
+      gbp_route_domain_unlock (gx->gx_rd);
+      gbp_bridge_domain_unlock (gx->gx_bd);
+
+      gbp_ext_itf_db[sw_if_index] = INDEX_INVALID;
+      pool_put (gbp_ext_itf_pool, gx);
+
+      return (0);
+    }
+  return (VNET_API_ERROR_NO_SUCH_ENTRY);
+}
+
+void
+gbp_ext_itf_walk (gbp_ext_itf_cb_t cb, void *ctx)
+{
+  gbp_ext_itf_t *ge;
+
+  /* *INDENT-OFF* */
+  pool_foreach(ge, gbp_ext_itf_pool,
+  {
+    if (!cb(ge, ctx))
+      break;
+  });
+  /* *INDENT-ON* */
+}
+
+static walk_rc_t
+gbp_ext_itf_show_one (gbp_ext_itf_t * gx, void *ctx)
+{
+  vlib_cli_output (ctx, "  %U", format_gbp_ext_itf, gx);
+
+  return (WALK_CONTINUE);
+}
+
+static clib_error_t *
+gbp_ext_itf_show (vlib_main_t * vm,
+                 unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  vlib_cli_output (vm, "External-Interfaces:");
+  gbp_ext_itf_walk (gbp_ext_itf_show_one, vm);
+
+  return (NULL);
+}
+
+/*?
+ * Show Group Based Policy external interface and derived information
+ *
+ * @cliexpar
+ * @cliexstart{show gbp ext-itf}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_ext_itf_show_node, static) = {
+  .path = "show gbp ext-itf",
+  .short_help = "show gbp ext-itf\n",
+  .function = gbp_ext_itf_show,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+gbp_ext_itf_init (vlib_main_t * vm)
+{
+  gx_logger = vlib_log_register_class ("gbp", "ext-itf");
+
+  return (NULL);
+}
+
+VLIB_INIT_FUNCTION (gbp_ext_itf_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/gbp/gbp_ext_itf.h b/src/plugins/gbp/gbp_ext_itf.h
new file mode 100644 (file)
index 0000000..dafcd08
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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_EXT_ITF_H__
+#define __GBP_EXT_ITF_H__
+
+#include <gbp/gbp.h>
+
+/**
+ * An external interface maps directly to an oflex L3ExternalInterface.
+ * The special characteristics of an external interface is the way the source
+ * EPG is determined for input packets which, like a recirc interface, is via
+ * a LPM.
+ */
+typedef struct gpb_ext_itf_t_
+{
+  /**
+   * The interface
+   */
+  u32 gx_itf;
+
+  /**
+   * The BD this external interface is a member of
+   */
+  index_t gx_bd;
+
+  /**
+   * The RD this external interface is a member of
+   */
+  index_t gx_rd;
+
+  /**
+   * cached FIB indices from the RD
+   */
+  u32 gx_fib_index[DPO_PROTO_NUM];
+
+} gbp_ext_itf_t;
+
+
+extern int gbp_ext_itf_add (u32 sw_if_index, u32 bd_id, u32 rd_id);
+extern int gbp_ext_itf_delete (u32 sw_if_index);
+
+extern u8 *format_gbp_ext_itf (u8 * s, va_list * args);
+
+typedef walk_rc_t (*gbp_ext_itf_cb_t) (gbp_ext_itf_t * gbpe, void *ctx);
+extern void gbp_ext_itf_walk (gbp_ext_itf_cb_t bgpe, void *ctx);
+
+
+/**
+ * Exposed types for the data-plane
+ */
+extern gbp_ext_itf_t *gbp_ext_itf_pool;
+extern index_t *gbp_ext_itf_db;
+
+always_inline gbp_ext_itf_t *
+gbp_ext_itf_get (u32 sw_if_index)
+{
+  return (pool_elt_at_index (gbp_ext_itf_pool, gbp_ext_itf_db[sw_if_index]));
+}
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
index e1afdfb..7cfe9da 100644 (file)
@@ -196,9 +196,9 @@ format_gbp_itf (u8 * s, va_list * args)
 
   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, 0,
-             gi->gi_l2_input_fb, format_l2_output_features, 0,
-             gi->gi_l2_output_fb);
+             gi->gi_sw_if_index, gi->gi_locks,
+             format_l2_input_features, gi->gi_l2_input_fb, 0,
+             format_l2_output_features, gi->gi_l2_output_fb, 0);
 
   return (s);
 }
index 7c53d1b..523e23c 100644 (file)
@@ -14,9 +14,6 @@
  */
 
 #include <vnet/dpo/dvr_dpo.h>
-#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>
 
 /**
  * DPO pool
  */
-static gbp_policy_dpo_t *gbp_policy_dpo_pool;
+gbp_policy_dpo_t *gbp_policy_dpo_pool;
 
 /**
  * DPO type registered for these GBP FWD
  */
-static dpo_type_t gbp_policy_dpo_type;
-
-static inline gbp_policy_dpo_t *
-gbp_policy_dpo_get_i (index_t index)
-{
-  return (pool_elt_at_index (gbp_policy_dpo_pool, index));
-}
-
-gbp_policy_dpo_t *
-gbp_policy_dpo_get (index_t index)
-{
-  return (gbp_policy_dpo_get_i (index));
-}
+dpo_type_t gbp_policy_dpo_type;
 
 static gbp_policy_dpo_t *
 gbp_policy_dpo_alloc (void)
 {
   gbp_policy_dpo_t *gpd;
 
-  pool_get_zero (gbp_policy_dpo_pool, gpd);
+  pool_get_aligned_zero (gbp_policy_dpo_pool, gpd, CLIB_CACHE_LINE_BYTES);
 
   return (gpd);
 }
@@ -61,7 +46,7 @@ gbp_policy_dpo_get_from_dpo (const dpo_id_t * dpo)
 {
   ASSERT (gbp_policy_dpo_type == dpo->dpoi_type);
 
-  return (gbp_policy_dpo_get_i (dpo->dpoi_index));
+  return (gbp_policy_dpo_get (dpo->dpoi_index));
 }
 
 static inline index_t
@@ -138,7 +123,7 @@ format_gbp_policy_dpo (u8 * s, va_list * ap)
 {
   index_t index = va_arg (*ap, index_t);
   u32 indent = va_arg (*ap, u32);
-  gbp_policy_dpo_t *gpd = gbp_policy_dpo_get_i (index);
+  gbp_policy_dpo_t *gpd = gbp_policy_dpo_get (index);
   vnet_main_t *vnm = vnet_get_main ();
 
   s = format (s, "gbp-policy-dpo: %U, epg:%d out:%U",
@@ -293,8 +278,7 @@ gbp_policy_dpo_inline (vlib_main_t * vm,
          b0 = vlib_get_buffer (vm, bi0);
 
          gc0 = NULL;
-         gpd0 =
-           gbp_policy_dpo_get_i (vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
+         gpd0 = gbp_policy_dpo_get (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)
@@ -451,191 +435,6 @@ VLIB_REGISTER_NODE (ip6_gbp_policy_dpo_node) = {
 
 VLIB_NODE_FUNCTION_MULTIARCH (ip4_gbp_policy_dpo_node, ip4_gbp_policy_dpo)
 VLIB_NODE_FUNCTION_MULTIARCH (ip6_gbp_policy_dpo_node, ip6_gbp_policy_dpo)
-/* *INDENT-ON* */
-
- /**
- * per-packet trace data
- */
-typedef struct gbp_classify_trace_t_
-{
-  /* per-pkt trace data */
-  epg_id_t src_epg;
-} gbp_classify_trace_t;
-
-typedef enum gbp_lpm_classify_next_t_
-{
-  GPB_LPM_CLASSIFY_DROP,
-} gbp_lpm_classify_next_t;
-
-/*
- * Determine the SRC EPG from a LPM
- */
-always_inline uword
-gbp_lpm_classify_inline (vlib_main_t * vm,
-                        vlib_node_runtime_t * node,
-                        vlib_frame_t * frame, fib_protocol_t fproto)
-{
-  u32 n_left_from, *from, *to_next;
-  u32 next_index;
-
-  next_index = 0;
-  n_left_from = frame->n_vectors;
-  from = vlib_frame_vector_args (frame);
-
-  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, fib_index0, lbi0;
-         gbp_lpm_classify_next_t next0;
-         const gbp_policy_dpo_t *gpd0;
-         const gbp_recirc_t *gr0;
-         const dpo_id_t *dpo0;
-         load_balance_t *lb0;
-         ip4_header_t *ip4_0;
-         ip6_header_t *ip6_0;
-         vlib_buffer_t *b0;
-         epg_id_t src_epg0;
-
-         bi0 = from[0];
-         to_next[0] = bi0;
-         from += 1;
-         to_next += 1;
-         n_left_from -= 1;
-         n_left_to_next -= 1;
-         next0 = GPB_LPM_CLASSIFY_DROP;
-
-         b0 = vlib_get_buffer (vm, bi0);
-
-         sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
-         gr0 = gbp_recirc_get (sw_if_index0);
-         fib_index0 = gr0->gr_fib_index[fproto];
-
-         if (FIB_PROTOCOL_IP4 == fproto)
-           {
-             ip4_0 = vlib_buffer_get_current (b0);
-             lbi0 = ip4_fib_forwarding_lookup (fib_index0,
-                                               &ip4_0->src_address);
-           }
-         else
-           {
-             ip6_0 = vlib_buffer_get_current (b0);
-             lbi0 = ip6_fib_table_fwding_lookup (&ip6_main, fib_index0,
-                                                 &ip6_0->src_address);
-           }
-
-         lb0 = load_balance_get (lbi0);
-         dpo0 = load_balance_get_bucket_i (lb0, 0);
-
-         if (gbp_policy_dpo_type == dpo0->dpoi_type)
-           {
-             gpd0 = gbp_policy_dpo_get_i (dpo0->dpoi_index);
-             src_epg0 = gpd0->gpd_epg;
-             vnet_feature_next (&next0, b0);
-           }
-         else
-           {
-             /* could not classify => drop */
-             src_epg0 = 0;
-           }
-
-         vnet_buffer2 (b0)->gbp.src_epg = src_epg0;
-
-         if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
-           {
-             gbp_classify_trace_t *t =
-               vlib_add_trace (vm, node, b0, sizeof (*t));
-             t->src_epg = src_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;
-}
-
-static uword
-gbp_ip4_lpm_classify (vlib_main_t * vm,
-                     vlib_node_runtime_t * node, vlib_frame_t * frame)
-{
-  return (gbp_lpm_classify_inline (vm, node, frame, FIB_PROTOCOL_IP4));
-}
-
-static uword
-gbp_ip6_lpm_classify (vlib_main_t * vm,
-                     vlib_node_runtime_t * node, vlib_frame_t * frame)
-{
-  return (gbp_lpm_classify_inline (vm, node, frame, FIB_PROTOCOL_IP6));
-}
-
- /* packet trace format function */
-static u8 *
-format_gbp_classify_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_classify_trace_t *t = va_arg (*args, gbp_classify_trace_t *);
-
-  s = format (s, "src-epg:%d", t->src_epg);
-
-  return s;
-}
-
-/* *INDENT-OFF* */
-VLIB_REGISTER_NODE (gbp_ip4_lpm_classify_node) = {
-  .function = gbp_ip4_lpm_classify,
-  .name = "ip4-gbp-lpm-classify",
-  .vector_size = sizeof (u32),
-  .format_trace = format_gbp_classify_trace,
-  .type = VLIB_NODE_TYPE_INTERNAL,
-
-  .n_errors = 0,
-  .n_next_nodes = 1,
-  .next_nodes = {
-    [GPB_LPM_CLASSIFY_DROP] = "ip4-drop"
-  },
-};
-
-VLIB_NODE_FUNCTION_MULTIARCH (gbp_ip4_lpm_classify_node, gbp_ip4_lpm_classify);
-
-VLIB_REGISTER_NODE (gbp_ip6_lpm_classify_node) = {
-  .function = gbp_ip6_lpm_classify,
-  .name = "ip6-gbp-lpm-classify",
-  .vector_size = sizeof (u32),
-  .format_trace = format_gbp_classify_trace,
-  .type = VLIB_NODE_TYPE_INTERNAL,
-
-  .n_errors = 0,
-  .n_next_nodes = 1,
-  .next_nodes = {
-    [GPB_LPM_CLASSIFY_DROP] = "ip6-drop"
-  },
-};
-
-VLIB_NODE_FUNCTION_MULTIARCH (gbp_ip6_lpm_classify_node, gbp_ip6_lpm_classify);
-
-VNET_FEATURE_INIT (gbp_ip4_lpm_classify_feat_node, static) =
-{
-  .arc_name = "ip4-unicast",
-  .node_name = "ip4-gbp-lpm-classify",
-  .runs_before = VNET_FEATURES ("nat44-out2in"),
-};
-VNET_FEATURE_INIT (gbp_ip6_lpm_classify_feat_node, static) =
-{
-  .arc_name = "ip6-unicast",
-  .node_name = "ip6-gbp-lpm-classify",
-  .runs_before = VNET_FEATURES ("nat66-out2in"),
-};
-
 /* *INDENT-ON* */
 
 /*
index 8ee086d..097205b 100644 (file)
@@ -25,6 +25,8 @@
  */
 typedef struct gbp_policy_dpo_t_
 {
+  CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
+
   /**
    * The protocol of packets using this DPO
    */
@@ -46,7 +48,7 @@ typedef struct gbp_policy_dpo_t_
   u16 gpd_locks;
 
   /**
-   * Stacked DPO on DVR of output interface
+   * Stacked DPO on DVR/ADJ of output interface
    */
   dpo_id_t gpd_dpo;
 } gbp_policy_dpo_t;
@@ -55,14 +57,24 @@ extern void gbp_policy_dpo_add_or_lock (dpo_proto_t dproto,
                                        epg_id_t epg,
                                        u32 sw_if_index, dpo_id_t * dpo);
 
-extern gbp_policy_dpo_t *gbp_policy_dpo_get (index_t index);
-
 extern dpo_type_t gbp_policy_dpo_get_type (void);
 
 extern vlib_node_registration_t ip4_gbp_policy_dpo_node;
 extern vlib_node_registration_t ip6_gbp_policy_dpo_node;
 extern vlib_node_registration_t gbp_policy_port_node;
 
+/**
+ * Types exposed for the Data-plane
+ */
+extern dpo_type_t gbp_policy_dpo_type;
+extern gbp_policy_dpo_t *gbp_policy_dpo_pool;
+
+always_inline gbp_policy_dpo_t *
+gbp_policy_dpo_get (index_t index)
+{
+  return (pool_elt_at_index (gbp_policy_dpo_pool, index));
+}
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
index 59588ea..d907be0 100644 (file)
@@ -21,6 +21,8 @@
 #include <vnet/dpo/dvr_dpo.h>
 #include <vnet/fib/fib_table.h>
 
+#include <vlib/unix/plugin.h>
+
 /**
  * Pool of GBP recircs
  */
@@ -36,6 +38,12 @@ index_t *gbp_recirc_db;
  */
 vlib_log_class_t gr_logger;
 
+/**
+ * L2 Emulation enable/disable symbols
+ */
+static void (*l2e_enable) (u32 sw_if_index);
+static void (*l2e_disable) (u32 sw_if_index);
+
 #define GBP_RECIRC_DBG(...)                           \
     vlib_log_debug (gr_logger, __VA_ARGS__);
 
@@ -72,8 +80,7 @@ gbp_recirc_add (u32 sw_if_index, epg_id_t epg_id, u8 is_ext)
        return (VNET_API_ERROR_NO_SUCH_ENTRY);
 
       gbp_endpoint_group_lock (ggi);
-      pool_get (gbp_recirc_pool, gr);
-      clib_memset (gr, 0, sizeof (*gr));
+      pool_get_zero (gbp_recirc_pool, gr);
       gri = gr - gbp_recirc_pool;
 
       gr->gr_epg = epg_id;
@@ -94,7 +101,7 @@ gbp_recirc_add (u32 sw_if_index, epg_id_t epg_id, u8 is_ext)
       gg = gbp_endpoint_group_get (gr->gr_epgi);
       FOR_EACH_FIB_IP_PROTOCOL (fproto)
       {
-       gr->gr_fib_index[fproto] =
+       gr->gr_fib_index[fib_proto_to_dpo (fproto)] =
          gbp_endpoint_group_get_fib_index (gg, fproto);
       }
 
@@ -103,6 +110,11 @@ gbp_recirc_add (u32 sw_if_index, epg_id_t epg_id, u8 is_ext)
        */
       gr->gr_itf = gbp_itf_add_and_lock (gr->gr_sw_if_index, gg->gg_bd_index);
 
+      /*
+       * set the interface into L2 emulation mode
+       */
+      l2e_enable (gr->gr_sw_if_index);
+
       /*
        * Packets on the recirculation interface are subject to src-EPG
        * classification. Recirc interfaces are L2-emulation mode.
@@ -195,6 +207,7 @@ 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);
+      l2e_disable (gr->gr_sw_if_index);
 
       gbp_itf_unlock (gr->gr_itf);
 
@@ -218,12 +231,12 @@ gbp_recirc_walk (gbp_recirc_cb_t cb, void *ctx)
   /* *INDENT-ON* */
 }
 
-static int
+static walk_rc_t
 gbp_recirc_show_one (gbp_recirc_t * gr, void *ctx)
 {
   vlib_cli_output (ctx, "  %U", format_gbp_recirc, gr);
 
-  return (1);
+  return (WALK_CONTINUE);
 }
 
 static clib_error_t *
@@ -256,6 +269,11 @@ gbp_recirc_init (vlib_main_t * vm)
 {
   gr_logger = vlib_log_register_class ("gbp", "recirc");
 
+  l2e_enable =
+    vlib_get_plugin_symbol ("l2e_plugin.so", "l2_emulation_enable");
+  l2e_disable =
+    vlib_get_plugin_symbol ("l2e_plugin.so", "l2_emulation_disable");
+
   return (NULL);
 }
 
index 1d1a88a..c577a5f 100644 (file)
 #include <vnet/fib/fib_types.h>
 
 /**
- * An Endpoint Group representation
+ * A GBP recirculation interface representation
+ *  Thes interfaces join Bridge domains that are internal to those that are
+ * NAT external, so the packets can be NAT translated and then undergo the
+ * whole policy process again.
  */
 typedef struct gpb_recirc_t_
 {
@@ -37,7 +40,7 @@ typedef struct gpb_recirc_t_
   /**
    * FIB indices the EPG is mapped to
    */
-  u32 gr_fib_index[FIB_PROTOCOL_IP_MAX];
+  u32 gr_fib_index[DPO_PROTO_NUM];
 
   /**
    * Is the interface for packets post-NAT translation (i.e. ext)
@@ -59,7 +62,7 @@ typedef struct gpb_recirc_t_
 extern int gbp_recirc_add (u32 sw_if_index, epg_id_t epg_id, u8 is_ext);
 extern void gbp_recirc_delete (u32 sw_if_index);
 
-typedef int (*gbp_recirc_cb_t) (gbp_recirc_t * gbpe, void *ctx);
+typedef walk_rc_t (*gbp_recirc_cb_t) (gbp_recirc_t * gbpe, void *ctx);
 extern void gbp_recirc_walk (gbp_recirc_cb_t bgpe, void *ctx);
 
 /**
index df3959e..67b6915 100644 (file)
@@ -231,6 +231,16 @@ gbp_route_domain_unlock (index_t index)
     }
 }
 
+u32
+gbp_route_domain_get_rd_id (index_t grdi)
+{
+  gbp_route_domain_t *grd;
+
+  grd = gbp_route_domain_get (grdi);
+
+  return (grd->grd_id);
+}
+
 int
 gbp_route_domain_delete (u32 rd_id)
 {
index ba5d5e4..b83d598 100644 (file)
@@ -66,6 +66,7 @@ extern index_t gbp_route_domain_index (const gbp_route_domain_t *);
 
 extern int gbp_route_domain_delete (u32 rd_id);
 extern gbp_route_domain_t *gbp_route_domain_get (index_t i);
+extern u32 gbp_route_domain_get_rd_id (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);
index a2d0c9a..90507a6 100644 (file)
 #include <plugins/gbp/gbp_endpoint.h>
 #include <plugins/gbp/gbp_vxlan.h>
 
+/**
+ * Scanner logger
+ */
 vlib_log_class_t gs_logger;
 
+/**
+ * Scanner state
+ */
+static bool gs_enabled;
+
 #define GBP_SCANNER_DBG(...)                                      \
     vlib_log_debug (gs_logger, __VA_ARGS__);
 
@@ -28,13 +36,13 @@ 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;
+  bool do_scan = 0;
 
   while (1)
     {
       do_scan = 0;
 
-      if (enabled)
+      if (gs_enabled)
        {
          /* scan every 'inactive threshold' seconds */
          vlib_process_wait_for_event_or_clock (vm,
@@ -55,11 +63,14 @@ gbp_scanner (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
          break;
 
        case GBP_ENDPOINT_SCAN_START:
-         enabled = 1;
+         gs_enabled = 1;
          break;
 
        case GBP_ENDPOINT_SCAN_STOP:
-         enabled = 0;
+         gs_enabled = 0;
+         break;
+
+       case GBP_ENDPOINT_SCAN_SET_TIME:
          break;
 
        default:
@@ -84,6 +95,30 @@ VLIB_REGISTER_NODE (gbp_scanner_node) = {
 };
 /* *INDENT-ON* */
 
+static clib_error_t *
+gbp_scanner_cli (vlib_main_t * vm,
+                unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+  vlib_cli_output (vm, "GBP-scanner: enabled:%d interval:%f",
+                  gs_enabled, gbp_endpoint_scan_threshold ());
+
+  return (NULL);
+}
+
+/*?
+ * Show GBP scanner
+ *
+ * @cliexpar
+ * @cliexstart{show gbp scanner}
+ * @cliexend
+ ?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (gbp_scanner_cli_node, static) = {
+  .path = "show gbp scanner",
+  .short_help = "show gbp scanner",
+  .function = gbp_scanner_cli,
+};
+/* *INDENT-ON* */
 
 static clib_error_t *
 gbp_scanner_init (vlib_main_t * vm)
index 070da38..1133167 100644 (file)
@@ -22,8 +22,7 @@ typedef enum gbp_scan_event_t_
 {
   GBP_ENDPOINT_SCAN_START,
   GBP_ENDPOINT_SCAN_STOP,
-  GBP_VXLAN_SCAN_START,
-  GBP_VXLAN_SCAN_STOP,
+  GBP_ENDPOINT_SCAN_SET_TIME,
 } gbp_scan_event_t;
 
 extern vlib_node_registration_t gbp_scanner_node;
index d9d4299..b0b6db8 100644 (file)
@@ -46,7 +46,13 @@ typedef struct gbp_subnet_t_
       epg_id_t gs_epg;
       u32 gs_sw_if_index;
     } gs_stitched_external;
+    struct
+    {
+      epg_id_t gs_epg;
+    } gs_l3_out;
   };
+
+  fib_node_index_t gs_fei;
 } gbp_subnet_t;
 
 /**
@@ -102,7 +108,7 @@ gbp_subnet_db_del (gbp_subnet_t * gs)
 
 
 static int
-gbp_subnet_transport_add (const gbp_subnet_t * gs)
+gbp_subnet_transport_add (gbp_subnet_t * gs)
 {
   dpo_id_t gfd = DPO_INVALID;
   gbp_route_domain_t *grd;
@@ -111,14 +117,15 @@ gbp_subnet_transport_add (const gbp_subnet_t * gs)
   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);
+  gs->gs_fei = 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);
 
@@ -126,17 +133,18 @@ gbp_subnet_transport_add (const gbp_subnet_t * gs)
 }
 
 static int
-gbp_subnet_internal_add (const gbp_subnet_t * gs)
+gbp_subnet_internal_add (gbp_subnet_t * gs)
 {
   dpo_id_t gfd = DPO_INVALID;
 
   gbp_fwd_dpo_add_or_lock (fib_proto_to_dpo (gs->gs_key->gsk_pfx.fp_proto),
                           &gfd);
 
-  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);
+  gs->gs_fei = 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);
 
   dpo_reset (&gfd);
 
@@ -155,12 +163,33 @@ gbp_subnet_external_add (gbp_subnet_t * gs, u32 sw_if_index, epg_id_t epg)
                              gs->gs_stitched_external.gs_epg,
                              gs->gs_stitched_external.gs_sw_if_index, &gpd);
 
-  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),
-                                     &gpd);
+  gs->gs_fei = 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),
+                                                  &gpd);
+
+  dpo_reset (&gpd);
+
+  return (0);
+}
+
+static int
+gbp_subnet_l3_out_add (gbp_subnet_t * gs, u32 sw_if_index, epg_id_t epg)
+{
+  dpo_id_t gpd = DPO_INVALID;
+
+  gs->gs_l3_out.gs_epg = epg;
+
+  gbp_policy_dpo_add_or_lock (fib_proto_to_dpo (gs->gs_key->gsk_pfx.fp_proto),
+                             gs->gs_l3_out.gs_epg, ~0, &gpd);
+
+  gs->gs_fei = fib_table_entry_special_dpo_add (gs->gs_key->gsk_fib_index,
+                                               &gs->gs_key->gsk_pfx,
+                                               FIB_SOURCE_SPECIAL,
+                                               FIB_ENTRY_FLAG_INTERPOSE,
+                                               &gpd);
 
   dpo_reset (&gpd);
 
@@ -190,7 +219,10 @@ gbp_subnet_del (u32 rd_id, const fib_prefix_t * pfx)
 
   gs = pool_elt_at_index (gbp_subnet_pool, gsi);
 
-  fib_table_entry_delete (fib_index, pfx, FIB_SOURCE_PLUGIN_HI);
+  if (GBP_SUBNET_L3_OUT == gs->gs_type)
+    fib_table_entry_delete (fib_index, pfx, FIB_SOURCE_SPECIAL);
+  else
+    fib_table_entry_delete (fib_index, pfx, FIB_SOURCE_PLUGIN_HI);
 
   gbp_subnet_db_del (gs);
   gbp_route_domain_unlock (gs->gs_rd);
@@ -243,6 +275,9 @@ gbp_subnet_add (u32 rd_id,
     case GBP_SUBNET_TRANSPORT:
       rv = gbp_subnet_transport_add (gs);
       break;
+    case GBP_SUBNET_L3_OUT:
+      rv = gbp_subnet_l3_out_add (gs, sw_if_index, epg);
+      break;
     }
 
   return (rv);
@@ -274,10 +309,13 @@ gbp_subnet_walk (gbp_subnet_cb_t cb, void *ctx)
         sw_if_index = gs->gs_stitched_external.gs_sw_if_index;
         epg = gs->gs_stitched_external.gs_epg;
         break;
+      case GBP_SUBNET_L3_OUT:
+        epg = gs->gs_l3_out.gs_epg;
+        break;
       }
 
     if (WALK_STOP == cb (grd->grd_id, &gs->gs_key->gsk_pfx,
-                         gs->gs_type, epg, sw_if_index, ctx))
+                         gs->gs_type, sw_if_index, epg, ctx))
       break;
   }));
   /* *INDENT-ON* */
@@ -302,6 +340,8 @@ format_gbp_subnet_type (u8 * s, va_list * args)
       return (format (s, "stitched-external"));
     case GBP_SUBNET_TRANSPORT:
       return (format (s, "transport"));
+    case GBP_SUBNET_L3_OUT:
+      return (format (s, "l3-out"));
     }
 
   return (format (s, "unknown"));
@@ -334,20 +374,17 @@ format_gbp_subnet (u8 * s, va_list * args)
                  format_vnet_sw_if_index_name,
                  vnet_get_main (), gs->gs_stitched_external.gs_sw_if_index);
       break;
+    case GBP_SUBNET_L3_OUT:
+      s = format (s, " {epg:%d}", gs->gs_l3_out.gs_epg);
+      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);
+       s = format (s, "\n  %U", format_fib_entry, gs->gs_fei,
+                   FIB_ENTRY_FORMAT_DETAIL);
       }
     case GBP_SUBNET_SHOW_BRIEF:
       break;
index b6906de..5fbd4b2 100644 (file)
@@ -23,6 +23,7 @@ typedef enum gbp_subnet_type_t_
   GBP_SUBNET_TRANSPORT,
   GBP_SUBNET_STITCHED_INTERNAL,
   GBP_SUBNET_STITCHED_EXTERNAL,
+  GBP_SUBNET_L3_OUT,
 } gbp_subnet_type_t;
 
 extern int gbp_subnet_add (u32 rd_id,
index 4a5cf6c..93da127 100644 (file)
@@ -113,6 +113,7 @@ l2input_bd_config (u32 bd_index)
  _(GBP_LEARN,     "gbp-learn-l2")               \
  _(GBP_NULL_CLASSIFY, "gbp-null-classify")      \
  _(GBP_SRC_CLASSIFY,  "gbp-src-classify")       \
+ _(GBP_LPM_CLASSIFY,  "l2-gbp-lpm-classify")    \
  _(VTR,           "l2-input-vtr")               \
  _(L2_IP_QOS_RECORD, "l2-ip-qos-record")        \
  _(VPATH,         "vpath-input-l2")             \
index 68bbfe4..4a59692 100644 (file)
@@ -196,6 +196,46 @@ class VppGbpRecirc(VppObject):
         return False
 
 
+class VppGbpExtItf(VppObject):
+    """
+    GBP ExtItfulation Interface
+    """
+
+    def __init__(self, test, itf, bd, rd):
+        self._test = test
+        self.itf = itf
+        self.bd = bd
+        self.rd = rd
+
+    def add_vpp_config(self):
+        self._test.vapi.gbp_ext_itf_add_del(
+            1,
+            self.itf.sw_if_index,
+            self.bd.bd_id,
+            self.rd.rd_id)
+        self._test.registry.register(self, self._test.logger)
+
+    def remove_vpp_config(self):
+        self._test.vapi.gbp_ext_itf_add_del(
+            0,
+            self.itf.sw_if_index,
+            self.bd.bd_id,
+            self.rd.rd_id)
+
+    def __str__(self):
+        return self.object_id()
+
+    def object_id(self):
+        return "gbp-ext-itf:[%d]" % (self.itf.sw_if_index)
+
+    def query_vpp_config(self):
+        rs = self._test.vapi.gbp_ext_itf_dump()
+        for r in rs:
+            if r.ext_itf.sw_if_index == self.itf.sw_if_index:
+                return True
+        return False
+
+
 class VppGbpSubnet(VppObject):
     """
     GBP Subnet
@@ -650,6 +690,8 @@ class TestGBP(VppTestCase):
     def test_gbp(self):
         """ Group Based Policy """
 
+        ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
+
         #
         # Bridge Domains
         #
@@ -794,8 +836,6 @@ class TestGBP(VppTestCase):
             VppIpInterfaceBind(self, recirc.recirc,
                                recirc.epg.rd.t6).add_vpp_config()
 
-            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,
@@ -1375,8 +1415,6 @@ class TestGBP(VppTestCase):
                                                   is_add=0)
 
         for recirc in recircs:
-            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,
@@ -1389,6 +1427,7 @@ class TestGBP(VppTestCase):
     def test_gbp_learn_l2(self):
         """ GBP L2 Endpoint Learning """
 
+        ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
         learnt = [{'mac': '00:00:11:11:11:01',
                    'ip': '10.0.0.1',
                    'ip6': '2001:10::2'},
@@ -1550,13 +1589,8 @@ class TestGBP(VppTestCase):
         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
         #
@@ -1791,6 +1825,7 @@ class TestGBP(VppTestCase):
     def test_gbp_learn_vlan_l2(self):
         """ GBP L2 Endpoint w/ VLANs"""
 
+        ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
         learnt = [{'mac': '00:00:11:11:11:01',
                    'ip': '10.0.0.1',
                    'ip6': '2001:10::2'},
@@ -1960,6 +1995,7 @@ class TestGBP(VppTestCase):
 
         self.vapi.cli("set logging class gbp debug")
 
+        ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
         routed_dst_mac = "00:0c:0c:0c:0c:0c"
         routed_src_mac = "00:22:bd:f8:19:ff"
 
@@ -2218,7 +2254,7 @@ class TestGBP(VppTestCase):
 
         #
         # Add a route to static EP's v4 and v6 subnet
-        #  packets should be send on the v4/v6 uu=fwd interface resp.
+        #  packets should be sent on the v4/v6 uu=fwd interface resp.
         #
         se_10_24 = VppGbpSubnet(
             self, rd1, "10.0.0.0", 24,
@@ -2283,7 +2319,7 @@ class TestGBP(VppTestCase):
                                 epg_220, None,
                                 "10.0.0.88", "11.0.0.88",
                                 "2001:10::88", "3001::88",
-                                VppEnum.vl_api_gbp_endpoint_flags_t.REMOTE,
+                                ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
                                 self.pg2.local_ip4,
                                 self.pg2.remote_hosts[1].ip4,
                                 mac=None)
@@ -2296,7 +2332,7 @@ class TestGBP(VppTestCase):
                                epg_220, None,
                                learnt[0]['ip'], "11.0.0.101",
                                learnt[0]['ip6'], "3001::101",
-                               VppEnum.vl_api_gbp_endpoint_flags_t.REMOTE,
+                               ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
                                self.pg2.local_ip4,
                                self.pg2.remote_hosts[1].ip4,
                                mac=None)
@@ -2407,6 +2443,7 @@ class TestGBP(VppTestCase):
 
         self.vapi.cli("set logging class gbp debug")
 
+        ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
         routed_dst_mac = "00:0c:0c:0c:0c:0c"
         routed_src_mac = "00:22:bd:f8:19:ff"
 
@@ -2960,6 +2997,304 @@ class TestGBP(VppTestCase):
             self.assertEqual(rx[IPv6].src, ep1.ip6.address)
             self.assertEqual(rx[IPv6].dst, "2001:10::88")
 
+        #
+        # cleanup
+        #
+        self.pg7.unconfig_ip4()
+
+    def test_gbp_l3_out(self):
+        """ GBP L3 Out """
+
+        ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
+        self.vapi.cli("set logging class gbp debug")
+
+        routed_dst_mac = "00:0c:0c:0c:0c:0c"
+        routed_src_mac = "00:22:bd:f8:19:ff"
+
+        #
+        # IP tables
+        #
+        t4 = VppIpTable(self, 1)
+        t4.add_vpp_config()
+        t6 = VppIpTable(self, 1, True)
+        t6.add_vpp_config()
+
+        rd1 = VppGbpRouteDomain(self, 2, t4, t6)
+        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()
+
+        #
+        # Pg7 hosts a BD's BUM
+        # Pg1 some other l3 interface
+        #
+        self.pg7.config_ip4()
+        self.pg7.resolve_arp()
+
+        #
+        # a GBP external bridge domains for the EPs
+        #
+        bd1 = VppBridgeDomain(self, 1)
+        bd1.add_vpp_config()
+        gbd1 = VppGbpBridgeDomain(self, bd1, self.loop0)
+        gbd1.add_vpp_config()
+
+        #
+        # The Endpoint-groups in which the external endpoints exist
+        #
+        epg_220 = VppGbpEndpointGroup(self, 220, rd1, gbd1,
+                                      None, gbd1.bvi,
+                                      "10.0.0.128",
+                                      "2001:10::128")
+        epg_220.add_vpp_config()
+
+        # the BVIs have the subnets applied ...
+        ip4_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 24)
+        ip4_addr.add_vpp_config()
+        ip6_addr = VppIpInterfaceAddress(self, gbd1.bvi, "2001:10::128", 64)
+        ip6_addr.add_vpp_config()
+
+        # ... which are L3-out subnets
+        l3o_1 = VppGbpSubnet(
+            self, rd1, "10.0.0.0", 24,
+            VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+            epg=200)
+        l3o_1.add_vpp_config()
+
+        #
+        # an external interface attached to the outside world and the
+        # external BD
+        #
+        vlan_100 = VppDot1QSubint(self, self.pg0, 100)
+        vlan_100.admin_up()
+        ext_itf = VppGbpExtItf(self, vlan_100, bd1, rd1)
+        ext_itf.add_vpp_config()
+
+        #
+        # a multicast vxlan-gbp tunnel for broadcast in the BD
+        #
+        tun_bm = VppVxlanGbpTunnel(self, self.pg7.local_ip4,
+                                   "239.1.1.1", 88,
+                                   mcast_itf=self.pg7)
+        tun_bm.add_vpp_config()
+        bp_bm = VppBridgeDomainPort(self, bd1, tun_bm,
+                                    port_type=L2_PORT_TYPE.NORMAL)
+        bp_bm.add_vpp_config()
+
+        #
+        # an unicast vxlan-gbp for inter-BD traffic
+        #
+        vx_tun_l3 = VppGbpVxlanTunnel(
+            self, 444, rd1.rd_id,
+            VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3)
+        vx_tun_l3.add_vpp_config()
+
+        #
+        # packets destined to unkown addresses in the BVI's subnet
+        # are ARP'd for
+        #
+        p4 = (Ether(src=self.pg0.remote_mac, dst=self.router_mac.address) /
+              Dot1Q(vlan=100) /
+              IP(src="10.0.0.1", dst="10.0.0.88") /
+              UDP(sport=1234, dport=1234) /
+              Raw('\xa5' * 100))
+        p6 = (Ether(src=self.pg0.remote_mac, dst=self.router_mac.address) /
+              Dot1Q(vlan=100) /
+              IPv6(src="2001:10::1", dst="2001:10::88") /
+              UDP(sport=1234, dport=1234) /
+              Raw('\xa5' * 100))
+
+        rxs = self.send_and_expect(self.pg0, p4 * 1, self.pg7)
+
+        for rx in rxs:
+            self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+            # self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+            self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+            self.assertEqual(rx[IP].dst, "239.1.1.1")
+            self.assertEqual(rx[VXLAN].vni, 88)
+            self.assertTrue(rx[VXLAN].flags.G)
+            self.assertTrue(rx[VXLAN].flags.Instance)
+            # policy is not applied since we don't know where it's going
+            self.assertFalse(rx[VXLAN].gpflags.A)
+            self.assertFalse(rx[VXLAN].gpflags.D)
+
+            inner = rx[VXLAN].payload
+
+            self.assertTrue(inner.haslayer(ARP))
+
+        #
+        # An external Endpoint
+        #
+        eep = VppGbpEndpoint(self, vlan_100,
+                             epg_220, None,
+                             "10.0.0.1", "11.0.0.1",
+                             "2001:10::1", "3001::1",
+                             ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL)
+        eep.add_vpp_config()
+
+        #
+        # A remote endpoint
+        #
+        rep = VppGbpEndpoint(self, vx_tun_l3,
+                             epg_220, None,
+                             "10.0.0.101", "11.0.0.101",
+                             "2001:10::101", "3001::101",
+                             ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
+                             self.pg7.local_ip4,
+                             self.pg7.remote_ip4,
+                             mac=None)
+        rep.add_vpp_config()
+
+        #
+        # remote to external
+        #
+        p = (Ether(src=self.pg7.remote_mac,
+                   dst=self.pg7.local_mac) /
+             IP(src=self.pg7.remote_ip4,
+                dst=self.pg7.local_ip4) /
+             UDP(sport=1234, dport=48879) /
+             VXLAN(vni=444, gpid=220, flags=0x88) /
+             Ether(src=self.pg0.remote_mac, dst=self.router_mac.address) /
+             IP(src="10.0.0.101", dst="10.0.0.1") /
+             UDP(sport=1234, dport=1234) /
+             Raw('\xa5' * 100))
+
+        rxs = self.send_and_expect(self.pg7, p * 1, self.pg0)
+
+        #
+        # A subnet reachable through the external EP
+        #
+        ip_220 = VppIpRoute(self, "10.220.0.0", 24,
+                            [VppRoutePath(eep.ip4.address,
+                                          eep.epg.bvi.sw_if_index)],
+                            table_id=t4.table_id)
+        ip_220.add_vpp_config()
+
+        l3o_220 = VppGbpSubnet(
+            self, rd1, "10.220.0.0", 24,
+            VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+            epg=220)
+        l3o_220.add_vpp_config()
+
+        p = (Ether(src=self.pg7.remote_mac,
+                   dst=self.pg7.local_mac) /
+             IP(src=self.pg7.remote_ip4,
+                dst=self.pg7.local_ip4) /
+             UDP(sport=1234, dport=48879) /
+             VXLAN(vni=444, gpid=220, flags=0x88) /
+             Ether(src=self.pg0.remote_mac, dst=self.router_mac.address) /
+             IP(src="10.0.0.101", dst="10.220.0.1") /
+             UDP(sport=1234, dport=1234) /
+             Raw('\xa5' * 100))
+
+        rxs = self.send_and_expect(self.pg7, p * 1, self.pg0)
+
+        #
+        # another external subnet, this time in a different EPG
+        #
+        ip_200 = VppIpRoute(self, "10.200.0.0", 24,
+                            [VppRoutePath(eep.ip4.address,
+                                          eep.epg.bvi.sw_if_index)],
+                            table_id=t4.table_id)
+        ip_200.add_vpp_config()
+
+        l3o_200 = VppGbpSubnet(
+            self, rd1, "10.200.0.0", 24,
+            VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+            epg=200)
+        l3o_200.add_vpp_config()
+
+        p = (Ether(src=self.pg7.remote_mac,
+                   dst=self.pg7.local_mac) /
+             IP(src=self.pg7.remote_ip4,
+                dst=self.pg7.local_ip4) /
+             UDP(sport=1234, dport=48879) /
+             VXLAN(vni=444, gpid=220, flags=0x88) /
+             Ether(src=self.pg0.remote_mac, dst=self.router_mac.address) /
+             IP(src="10.0.0.101", dst="10.200.0.1") /
+             UDP(sport=1234, dport=1234) /
+             Raw('\xa5' * 100))
+
+        #
+        # packets dropped due to lack of contract.
+        #
+        rxs = self.send_and_assert_no_replies(self.pg7, p * 1)
+
+        #
+        # from the the subnet in EPG 220 beyond the external to remote
+        #
+        p4 = (Ether(src=self.pg0.remote_mac, dst=self.router_mac.address) /
+              Dot1Q(vlan=100) /
+              IP(src="10.220.0.1", dst=rep.ip4.address) /
+              UDP(sport=1234, dport=1234) /
+              Raw('\xa5' * 100))
+
+        rxs = self.send_and_expect(self.pg0, p4 * 1, self.pg7)
+
+        for rx in rxs:
+            self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+            self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+            self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+            self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+            self.assertEqual(rx[VXLAN].vni, 444)
+            self.assertTrue(rx[VXLAN].flags.G)
+            self.assertTrue(rx[VXLAN].flags.Instance)
+            self.assertTrue(rx[VXLAN].gpflags.A)
+            self.assertFalse(rx[VXLAN].gpflags.D)
+
+        #
+        # from the the subnet in EPG 200 beyond the external to remote
+        # dropped due to no contract
+        #
+        p4 = (Ether(src=self.pg0.remote_mac, dst=self.router_mac.address) /
+              Dot1Q(vlan=100) /
+              IP(src="10.200.0.1", dst=rep.ip4.address) /
+              UDP(sport=1234, dport=1234) /
+              Raw('\xa5' * 100))
+
+        rxs = self.send_and_assert_no_replies(self.pg0, p4 * 1)
+
+        #
+        # add a contract
+        #
+        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, 200, 220, acl_index,
+            [VppGbpContractRule(
+                VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+                []),
+             VppGbpContractRule(
+                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+                 [])])
+        c1.add_vpp_config()
+
+        rxs = self.send_and_expect(self.pg0, p4 * 1, self.pg7)
+
+        for rx in rxs:
+            self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+            self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+            self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+            self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+            self.assertEqual(rx[VXLAN].vni, 444)
+            self.assertTrue(rx[VXLAN].flags.G)
+            self.assertTrue(rx[VXLAN].flags.Instance)
+            self.assertTrue(rx[VXLAN].gpflags.A)
+            self.assertFalse(rx[VXLAN].gpflags.D)
+
+        #
+        # cleanup
+        #
+        self.pg7.unconfig_ip4()
+
 
 if __name__ == '__main__':
     unittest.main(testRunner=VppTestRunner)
index 662732f..3707587 100644 (file)
@@ -3624,6 +3624,19 @@ class VppPapiProvider(object):
         """ GBP recirc Dump """
         return self.api(self.papi.gbp_recirc_dump, {})
 
+    def gbp_ext_itf_add_del(self, is_add, sw_if_index, bd_id, rd_id):
+        """ GBP recirc Add/Del """
+        return self.api(self.papi.gbp_ext_itf_add_del,
+                        {'is_add': is_add,
+                         'ext_itf': {
+                             'sw_if_index': sw_if_index,
+                             'bd_id': bd_id,
+                             'rd_id': rd_id}})
+
+    def gbp_ext_itf_dump(self):
+        """ GBP recirc Dump """
+        return self.api(self.papi.gbp_ext_itf_dump, {})
+
     def gbp_subnet_add_del(self, is_add, rd_id,
                            prefix, type,
                            sw_if_index=0xffffffff,